feat: add context

This commit is contained in:
franklin 2021-10-23 07:22:36 +02:00
parent a0c9da3468
commit a26e28c619
19 changed files with 164 additions and 124 deletions

View File

@ -1,6 +1,7 @@
package squadron
import (
"context"
"fmt"
"github.com/sirupsen/logrus"
@ -35,7 +36,7 @@ type Build struct {
// ------------------------------------------------------------------------------------------------
// Build ...
func (b *Build) Build() error {
func (b *Build) Build(ctx context.Context) error {
logrus.Infof("running docker build for %q", b.Context)
_, err := util.NewDockerCommand().Build(b.Context).
Arg("-t", fmt.Sprintf("%s:%s", b.Image, b.Tag)).
@ -48,14 +49,14 @@ func (b *Build) Build() error {
Arg("--target", b.Target).
Arg("--shm-size", b.ShmSize).
ListArg("--add-host", b.ExtraHosts).
Arg("--isolation", b.Isolation).Run()
Arg("--isolation", b.Isolation).Run(ctx)
return err
}
// Push ...
func (b *Build) Push() error {
func (b *Build) Push(ctx context.Context) error {
logrus.Infof("running docker push for %s:%s", b.Image, b.Tag)
_, err := util.NewDockerCommand().Push(b.Image, b.Tag)
_, err := util.NewDockerCommand().Push(ctx, b.Image, b.Tag)
return err
}

View File

@ -1,6 +1,7 @@
package squadron
import (
"context"
"fmt"
"io/ioutil"
"path"
@ -28,7 +29,7 @@ func (cd *ChartDependency) UnmarshalYAML(value *yaml.Node) error {
if err := value.Decode(&vString); err != nil {
return err
}
vBytes, err := executeFileTemplate(vString, nil, true)
vBytes, err := executeFileTemplate(context.Background(), vString, nil, true)
if err != nil {
return errors.Wrap(err, "failed to render chart string")
}

View File

@ -1,6 +1,8 @@
package actions
import (
"context"
"github.com/spf13/cobra"
"github.com/foomo/squadron"
@ -16,11 +18,11 @@ var buildCmd = &cobra.Command{
Example: " squadron build frontend backend",
Args: cobra.MinimumNArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
return build(args, cwd, flagFiles, flagPush)
return build(cmd.Context(), args, cwd, flagFiles, flagPush)
},
}
func build(args []string, cwd string, files []string, push bool) error {
func build(ctx context.Context, args []string, cwd string, files []string, push bool) error {
sq := squadron.New(cwd, "", files)
if err := sq.MergeConfigFiles(); err != nil {
@ -38,7 +40,7 @@ func build(args []string, cwd string, files []string, push bool) error {
}
}
if err := sq.RenderConfig(); err != nil {
if err := sq.RenderConfig(ctx); err != nil {
return err
}
@ -48,14 +50,14 @@ func build(args []string, cwd string, files []string, push bool) error {
}
for _, unit := range units {
if err := unit.Build(); err != nil {
if err := unit.Build(ctx); err != nil {
return err
}
}
if push {
for _, unit := range units {
if err := unit.Push(); err != nil {
if err := unit.Push(ctx); err != nil {
return err
}
}

View File

@ -1,6 +1,7 @@
package actions
import (
"context"
"fmt"
"github.com/spf13/cobra"
@ -18,11 +19,11 @@ var configCmd = &cobra.Command{
Example: " squadron config --file squadron.yaml --file squadron.override.yaml",
Args: cobra.MinimumNArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
return config(args, cwd, flagFiles, flagNoRender)
return config(cmd.Context(), args, cwd, flagFiles, flagNoRender)
},
}
func config(args []string, cwd string, files []string, noRender bool) error {
func config(ctx context.Context, args []string, cwd string, files []string, noRender bool) error {
sq := squadron.New(cwd, "", files)
if err := sq.MergeConfigFiles(); err != nil {
@ -41,7 +42,7 @@ func config(args []string, cwd string, files []string, noRender bool) error {
}
if !noRender {
if err := sq.RenderConfig(); err != nil {
if err := sq.RenderConfig(ctx); err != nil {
return err
}
}

View File

@ -1,6 +1,8 @@
package actions
import (
"context"
"github.com/spf13/cobra"
"github.com/foomo/squadron"
@ -16,11 +18,11 @@ var downCmd = &cobra.Command{
Example: " squadron down frontend backend --namespace demo",
Args: cobra.MinimumNArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
return down(args, cwd, flagNamespace, flagFiles)
return down(cmd.Context(), args, cwd, flagNamespace, flagFiles)
},
}
func down(args []string, cwd, namespace string, files []string) error {
func down(ctx context.Context, args []string, cwd, namespace string, files []string) error {
sq := squadron.New(cwd, namespace, files)
if err := sq.MergeConfigFiles(); err != nil {
@ -33,5 +35,5 @@ func down(args []string, cwd, namespace string, files []string) error {
return err
}
return sq.Down(units, helmArgs)
return sq.Down(ctx, units, helmArgs)
}

View File

@ -1,6 +1,8 @@
package actions
import (
"context"
"github.com/spf13/cobra"
"github.com/foomo/squadron"
@ -12,11 +14,11 @@ var generateCmd = &cobra.Command{
Example: " squadron generate fronted backend",
Args: cobra.MinimumNArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
return generate(args, cwd, flagFiles)
return generate(cmd.Context(), args, cwd, flagFiles)
},
}
func generate(args []string, cwd string, files []string) error {
func generate(ctx context.Context, args []string, cwd string, files []string) error {
sq := squadron.New(cwd, "", files)
if err := sq.MergeConfigFiles(); err != nil {
@ -34,9 +36,9 @@ func generate(args []string, cwd string, files []string) error {
}
}
if err := sq.RenderConfig(); err != nil {
if err := sq.RenderConfig(ctx); err != nil {
return err
}
return sq.Generate(sq.GetConfig().Units)
return sq.Generate(ctx, sq.GetConfig().Units)
}

View File

@ -1,6 +1,7 @@
package actions
import (
"context"
"fmt"
"github.com/spf13/cobra"
@ -14,11 +15,11 @@ var listCmd = &cobra.Command{
Example: " squadron list",
Args: cobra.MinimumNArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
return list(cwd, flagFiles)
return list(cmd.Context(), cwd, flagFiles)
},
}
func list(cwd string, files []string) error {
func list(ctx context.Context, cwd string, files []string) error {
sq := squadron.New(cwd, "", files)
if err := sq.MergeConfigFiles(); err != nil {

View File

@ -1,6 +1,8 @@
package actions
import (
"context"
"github.com/spf13/cobra"
"github.com/foomo/squadron"
@ -16,11 +18,11 @@ var pushCmd = &cobra.Command{
Short: "pushes the squadron or given units",
Example: " squadron push frontend backend --namespace demo --build",
RunE: func(cmd *cobra.Command, args []string) error {
return push(args, cwd, flagNamespace, flagBuild, flagFiles)
return push(cmd.Context(), args, cwd, flagNamespace, flagBuild, flagFiles)
},
}
func push(args []string, cwd, namespace string, build bool, files []string) error {
func push(ctx context.Context, args []string, cwd, namespace string, build bool, files []string) error {
sq := squadron.New(cwd, namespace, files)
if err := sq.MergeConfigFiles(); err != nil {
@ -38,7 +40,7 @@ func push(args []string, cwd, namespace string, build bool, files []string) erro
}
}
if err := sq.RenderConfig(); err != nil {
if err := sq.RenderConfig(ctx); err != nil {
return err
}
@ -49,14 +51,14 @@ func push(args []string, cwd, namespace string, build bool, files []string) erro
if build {
for _, unit := range units {
if err := unit.Build(); err != nil {
if err := unit.Build(ctx); err != nil {
return err
}
}
}
for _, unit := range units {
if err := unit.Push(); err != nil {
if err := unit.Push(ctx); err != nil {
return err
}
}

View File

@ -1,6 +1,8 @@
package actions
import (
"context"
"github.com/spf13/cobra"
"github.com/foomo/squadron"
@ -15,11 +17,11 @@ var statusCmd = &cobra.Command{
Short: "installs the squadron or given units",
Example: " squadron status frontend backend --namespace demo --build --push -- --dry-run",
RunE: func(cmd *cobra.Command, args []string) error {
return status(args, cwd, flagNamespace, flagFiles)
return status(cmd.Context(), args, cwd, flagNamespace, flagFiles)
},
}
func status(args []string, cwd, namespace string, files []string) error {
func status(ctx context.Context, args []string, cwd, namespace string, files []string) error {
sq := squadron.New(cwd, namespace, files)
if err := sq.MergeConfigFiles(); err != nil {
@ -39,7 +41,7 @@ func status(args []string, cwd, namespace string, files []string) error {
}
}
if err := sq.RenderConfig(); err != nil {
if err := sq.RenderConfig(ctx); err != nil {
return err
}
@ -48,5 +50,5 @@ func status(args []string, cwd, namespace string, files []string) error {
return err
}
return sq.Status(units, helmArgs)
return sq.Status(ctx, units, helmArgs)
}

View File

@ -1,6 +1,8 @@
package actions
import (
"context"
"github.com/spf13/cobra"
"github.com/foomo/squadron"
@ -16,11 +18,11 @@ var templateCmd = &cobra.Command{
Example: " squadron template frontend backend --namespace demo",
Args: cobra.MinimumNArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
return template(args, cwd, flagNamespace, flagFiles)
return template(cmd.Context(), args, cwd, flagNamespace, flagFiles)
},
}
func template(args []string, cwd, namespace string, files []string) error {
func template(ctx context.Context, args []string, cwd, namespace string, files []string) error {
sq := squadron.New(cwd, namespace, files)
if err := sq.MergeConfigFiles(); err != nil {
@ -40,7 +42,7 @@ func template(args []string, cwd, namespace string, files []string) error {
}
}
if err := sq.RenderConfig(); err != nil {
if err := sq.RenderConfig(ctx); err != nil {
return err
}
@ -49,9 +51,9 @@ func template(args []string, cwd, namespace string, files []string) error {
return err
}
if err := sq.Generate(sq.GetConfig().Units); err != nil {
if err := sq.Generate(ctx, sq.GetConfig().Units); err != nil {
return err
} else if err := sq.Template(units, helmArgs); err != nil {
} else if err := sq.Template(ctx, units, helmArgs); err != nil {
return err
}

View File

@ -1,6 +1,7 @@
package actions
import (
"context"
"fmt"
"os/user"
"strings"
@ -23,11 +24,11 @@ var upCmd = &cobra.Command{
Short: "installs the squadron or given units",
Example: " squadron up frontend backend --namespace demo --build --push -- --dry-run",
RunE: func(cmd *cobra.Command, args []string) error {
return up(args, cwd, flagNamespace, flagBuild, flagPush, flagDiff, flagFiles)
return up(cmd.Context(), args, cwd, flagNamespace, flagBuild, flagPush, flagDiff, flagFiles)
},
}
func up(args []string, cwd, namespace string, build, push, diff bool, files []string) error {
func up(ctx context.Context, args []string, cwd, namespace string, build, push, diff bool, files []string) error {
sq := squadron.New(cwd, namespace, files)
if err := sq.MergeConfigFiles(); err != nil {
@ -47,7 +48,7 @@ func up(args []string, cwd, namespace string, build, push, diff bool, files []st
}
}
if err := sq.RenderConfig(); err != nil {
if err := sq.RenderConfig(ctx); err != nil {
return err
}
@ -58,7 +59,7 @@ func up(args []string, cwd, namespace string, build, push, diff bool, files []st
if build {
for _, unit := range units {
if err := unit.Build(); err != nil {
if err := unit.Build(ctx); err != nil {
return err
}
}
@ -66,31 +67,31 @@ func up(args []string, cwd, namespace string, build, push, diff bool, files []st
if push {
for _, unit := range units {
if err := unit.Push(); err != nil {
if err := unit.Push(ctx); err != nil {
return err
}
}
}
if err := sq.Generate(units); err != nil {
if err := sq.Generate(ctx, units); err != nil {
return err
}
username := "unknown"
if value, err := util.NewCommand("git").Args("config", "user.name").Run(); err == nil {
if value, err := util.NewCommand("git").Args("config", "user.name").Run(ctx); err == nil {
username = strings.TrimSpace(value)
} else if value, err := user.Current(); err == nil {
username = strings.TrimSpace(value.Name)
}
commit := ""
if value, err := util.NewCommand("git").Args("rev-parse", "--short", "HEAD").Run(); err == nil {
if value, err := util.NewCommand("git").Args("rev-parse", "--short", "HEAD").Run(ctx); err == nil {
commit = strings.TrimSpace(value)
}
if !diff {
return sq.Up(units, helmArgs, username, version, commit)
} else if out, err := sq.Diff(units, helmArgs); err != nil {
return sq.Up(ctx, units, helmArgs, username, version, commit)
} else if out, err := sq.Diff(ctx, units, helmArgs); err != nil {
return err
} else {
fmt.Println(out)

View File

@ -2,6 +2,7 @@ package squadron
import (
"bytes"
"context"
"fmt"
"os"
"os/exec"
@ -129,7 +130,7 @@ func (sq *Squadron) FilterConfig(units []string) error {
return nil
}
func (sq *Squadron) RenderConfig() error {
func (sq *Squadron) RenderConfig(ctx context.Context) error {
var tv TemplateVars
var vars map[string]interface{}
if err := yaml.Unmarshal([]byte(sq.config), &vars); err != nil {
@ -146,7 +147,7 @@ func (sq *Squadron) RenderConfig() error {
tv.add("Squadron", value)
}
// execute without errors to get existing values
out, err := executeFileTemplate(sq.config, tv, false)
out, err := executeFileTemplate(ctx, sq.config, tv, false)
if err != nil {
return errors.Wrap(err, "failed to execute initial file template")
}
@ -164,7 +165,7 @@ func (sq *Squadron) RenderConfig() error {
replace(value)
tv.add("Squadron", value)
}
out, err = executeFileTemplate(sq.config, tv, true)
out, err = executeFileTemplate(ctx, sq.config, tv, true)
if err != nil {
return errors.Wrap(err, "failed to execute second file template")
}
@ -180,13 +181,13 @@ func (sq *Squadron) RenderConfig() error {
return nil
}
func (sq *Squadron) Generate(units map[string]*Unit) error {
func (sq *Squadron) Generate(ctx context.Context, units map[string]*Unit) error {
logrus.Infof("recreating chart output dir %q", sq.chartPath())
if err := sq.cleanupOutput(sq.chartPath()); err != nil {
return err
}
if sq.c.Unite {
return sq.generateUmbrellaChart(units)
return sq.generateUmbrellaChart(ctx, units)
}
for uName, u := range units {
logrus.Infof("generating %q value overrides file in %q", uName, sq.chartPath())
@ -197,23 +198,23 @@ func (sq *Squadron) Generate(units map[string]*Unit) error {
return nil
}
func (sq *Squadron) generateUmbrellaChart(units map[string]*Unit) error {
func (sq *Squadron) generateUmbrellaChart(ctx context.Context, units map[string]*Unit) error {
logrus.Infof("generating chart %q files in %q", sq.name, sq.chartPath())
if err := sq.generateChart(units, sq.chartPath(), sq.name, sq.c.Version); err != nil {
return err
}
logrus.Infof("running helm dependency update for chart: %v", sq.chartPath())
_, err := util.NewHelmCommand().UpdateDependency(sq.name, sq.chartPath())
_, err := util.NewHelmCommand().UpdateDependency(ctx, sq.chartPath())
return err
}
func (sq *Squadron) Package() error {
func (sq *Squadron) Package(ctx context.Context) error {
logrus.Infof("running helm package for chart: %v", sq.chartPath())
_, err := util.NewHelmCommand().Package(sq.name, sq.chartPath(), sq.basePath)
_, err := util.NewHelmCommand().Package(ctx, sq.chartPath(), sq.basePath)
return err
}
func (sq *Squadron) Down(units map[string]*Unit, helmArgs []string) error {
func (sq *Squadron) Down(ctx context.Context, units map[string]*Unit, helmArgs []string) error {
if sq.c.Unite {
logrus.Infof("running helm uninstall for: %s", sq.chartPath())
stdErr := bytes.NewBuffer([]byte{})
@ -222,7 +223,7 @@ func (sq *Squadron) Down(units map[string]*Unit, helmArgs []string) error {
Stdout(os.Stdout).
Args("--namespace", sq.namespace).
Args(helmArgs...).
Run(); err != nil &&
Run(ctx); err != nil &&
string(bytes.TrimSpace(stdErr.Bytes())) != fmt.Sprintf("Error: uninstall: Release not loaded: %s: release: not found", sq.name) {
return err
}
@ -237,7 +238,7 @@ func (sq *Squadron) Down(units map[string]*Unit, helmArgs []string) error {
Stdout(os.Stdout).
Args("--namespace", sq.namespace).
Args(helmArgs...).
Run(); err != nil &&
Run(ctx); err != nil &&
string(bytes.TrimSpace(stdErr.Bytes())) != fmt.Sprintf("Error: uninstall: Release not loaded: %s: release: not found", rName) {
return err
}
@ -246,14 +247,14 @@ func (sq *Squadron) Down(units map[string]*Unit, helmArgs []string) error {
return nil
}
func (sq *Squadron) Diff(units map[string]*Unit, helmArgs []string) (string, error) {
func (sq *Squadron) Diff(ctx context.Context, units map[string]*Unit, helmArgs []string) (string, error) {
if sq.c.Unite {
logrus.Infof("running helm diff for: %s", sq.chartPath())
manifest, err := exec.Command("helm", "get", "manifest", sq.name, "--namespace", sq.namespace).Output() //nolint:gosec
manifest, err := exec.CommandContext(ctx, "helm", "get", "manifest", sq.name, "--namespace", sq.namespace).Output() //nolint:gosec
if err != nil {
return "", err
}
template, err := exec.Command("helm", "upgrade", sq.name, sq.chartPath(), "--namespace", sq.namespace, "--dry-run").Output() //nolint:gosec
template, err := exec.CommandContext(ctx, "helm", "upgrade", sq.name, sq.chartPath(), "--namespace", sq.namespace, "--dry-run").Output() //nolint:gosec
if err != nil {
return "", err
}
@ -264,11 +265,11 @@ func (sq *Squadron) Diff(units map[string]*Unit, helmArgs []string) (string, err
// todo use release prefix on install: squadron name or --name
rName := fmt.Sprintf("%s-%s", sq.name, uName)
logrus.Infof("running helm diff for: %s", uName)
manifest, err := exec.Command("helm", "get", "manifest", rName, "--namespace", sq.namespace).CombinedOutput()
manifest, err := exec.CommandContext(ctx, "helm", "get", "manifest", rName, "--namespace", sq.namespace).CombinedOutput()
if err != nil && string(bytes.TrimSpace(manifest)) != errHelmReleaseNotFound {
return "", err
}
cmd := exec.Command("helm", "upgrade", rName, "--install", "--namespace", sq.namespace, "-f", path.Join(sq.chartPath(), uName+".yaml"), "--dry-run")
cmd := exec.CommandContext(ctx, "helm", "upgrade", rName, "--install", "--namespace", sq.namespace, "-f", path.Join(sq.chartPath(), uName+".yaml"), "--dry-run")
if strings.Contains(u.Chart.Repository, "file://") {
cmd.Args = append(cmd.Args, "/"+strings.TrimPrefix(u.Chart.Repository, "file://"))
} else {
@ -285,7 +286,7 @@ func (sq *Squadron) Diff(units map[string]*Unit, helmArgs []string) (string, err
return "", nil
}
func (sq *Squadron) Status(units map[string]*Unit, helmArgs []string) error {
func (sq *Squadron) Status(ctx context.Context, units map[string]*Unit, helmArgs []string) error {
stdOut := bytes.NewBuffer([]byte{})
if sq.c.Unite {
stdOut.WriteString("==== " + sq.name + strings.Repeat("=", 20-len(sq.name)) + "\n")
@ -296,7 +297,7 @@ func (sq *Squadron) Status(units map[string]*Unit, helmArgs []string) error {
Stdout(stdOut).
Args("--namespace", sq.namespace).
Args(helmArgs...).
Run(); err != nil &&
Run(ctx); err != nil &&
string(bytes.TrimSpace(stdErr.Bytes())) == errHelmReleaseNotFound {
stdOut.WriteString("NAME: " + sq.name + "\n")
stdOut.WriteString("STATUS: not installed\n")
@ -314,7 +315,7 @@ func (sq *Squadron) Status(units map[string]*Unit, helmArgs []string) error {
Stderr(stdErr).
Stdout(stdOut).
Args("--namespace", sq.namespace).
Args(helmArgs...).Run(); err != nil &&
Args(helmArgs...).Run(ctx); err != nil &&
string(bytes.TrimSpace(stdErr.Bytes())) == errHelmReleaseNotFound {
stdOut.WriteString("NAME: " + rName + "\n")
stdOut.WriteString("STATUS: not installed\n")
@ -326,8 +327,8 @@ func (sq *Squadron) Status(units map[string]*Unit, helmArgs []string) error {
return nil
}
func (sq *Squadron) Up(units map[string]*Unit, helmArgs []string, username, version, commit string) error {
description := fmt.Sprintf("\nManaged-By: Squadron %s\nDeployed-By: %s\nGit-Commit: %s", version, username, commit)
func (sq *Squadron) Up(ctx context.Context, units map[string]*Unit, helmArgs []string, username, version, commit string) error {
description := fmt.Sprintf("\nDeployed-By: %s\nManaged-By: Squadron %s\nGit-Commit: %s", version, username, commit)
if sq.c.Unite {
logrus.Infof("running helm upgrade for chart: %s", sq.chartPath())
@ -339,7 +340,7 @@ func (sq *Squadron) Up(units map[string]*Unit, helmArgs []string, username, vers
Args("--description", description).
Args("--install").
Args(helmArgs...).
Run()
Run(ctx)
return err
}
for uName, u := range units {
@ -355,7 +356,7 @@ func (sq *Squadron) Up(units map[string]*Unit, helmArgs []string, username, vers
// Args("dependency", "update").
// Cwd(strings.TrimPrefix(u.Chart.Repository, "file://")).
// Stdout(os.Stdout).
// Run(); err != nil {
// Run(ctx); err != nil {
// return err
// }
// }
@ -374,21 +375,21 @@ func (sq *Squadron) Up(units map[string]*Unit, helmArgs []string, username, vers
} else {
cmd.Args(u.Chart.Name, "--repo", u.Chart.Repository)
}
if _, err := cmd.Run(); err != nil {
if _, err := cmd.Run(ctx); err != nil {
return err
}
}
return nil
}
func (sq *Squadron) Template(units map[string]*Unit, helmArgs []string) error {
func (sq *Squadron) Template(ctx context.Context, units map[string]*Unit, helmArgs []string) error {
if sq.c.Unite {
logrus.Infof("running helm template for chart: %s", sq.chartPath())
_, err := util.NewHelmCommand().Args("template", sq.name, sq.chartPath()).
Stdout(os.Stdout).
Args("--namespace", sq.namespace).
Args(helmArgs...).
Run()
Run(ctx)
return err
}
for uName, u := range units {
@ -405,7 +406,7 @@ func (sq *Squadron) Template(units map[string]*Unit, helmArgs []string) error {
} else {
cmd.Args(u.Chart.Name, "--repo", u.Chart.Repository)
}
if _, err := cmd.Run(); err != nil {
if _, err := cmd.Run(ctx); err != nil {
return err
}
}

View File

@ -2,6 +2,7 @@ package squadron
import (
"bytes"
"context"
b64 "encoding/base64"
"fmt"
"io"
@ -29,14 +30,15 @@ func (tv *TemplateVars) add(name string, value interface{}) {
(*tv)[name] = value
}
func executeFileTemplate(text string, templateVars interface{}, errorOnMissing bool) ([]byte, error) {
func executeFileTemplate(ctx context.Context, text string, templateVars interface{}, errorOnMissing bool) ([]byte, error) {
templateFunctions := template.FuncMap{}
templateFunctions["env"] = env
templateFunctions["op"] = onePassword(templateVars, errorOnMissing)
templateFunctions["op"] = onePassword(ctx, templateVars, errorOnMissing)
templateFunctions["base64"] = base64
templateFunctions["default"] = defaultIndex
templateFunctions["default"] = defaultValue
templateFunctions["defaultIndex"] = defaultIndexValue
templateFunctions["indent"] = indent
templateFunctions["file"] = file(templateVars, errorOnMissing)
templateFunctions["file"] = file(ctx, templateVars, errorOnMissing)
templateFunctions["git"] = git
tpl, err := template.New("squadron").Delims("<%", "%>").Funcs(templateFunctions).Parse(text)
@ -61,13 +63,13 @@ func env(name string) (string, error) {
return value, nil
}
func file(templateVars interface{}, errorOnMissing bool) func(v string) (string, error) {
func file(ctx context.Context, templateVars interface{}, errorOnMissing bool) func(v string) (string, error) {
return func(v string) (string, error) {
if v == "" {
return "", nil
} else if fileBytes, err := ioutil.ReadFile(v); err != nil {
return "", errors.Wrap(err, "failed to read file")
} else if renderedBytes, err := executeFileTemplate(string(fileBytes), templateVars, errorOnMissing); err != nil {
} else if renderedBytes, err := executeFileTemplate(ctx, string(fileBytes), templateVars, errorOnMissing); err != nil {
return "", errors.Wrap(err, "failed to render file")
} else {
return string(bytes.TrimSpace(renderedBytes)), nil
@ -75,7 +77,14 @@ func file(templateVars interface{}, errorOnMissing bool) func(v string) (string,
}
}
func defaultIndex(v map[string]interface{}, index string, def interface{}) interface{} {
func defaultValue(value string, def interface{}) interface{} {
if value == "" {
return def
}
return value
}
func defaultIndexValue(v map[string]interface{}, index string, def interface{}) interface{} {
var ok bool
if _, ok = v[index]; ok {
return v[index]
@ -99,8 +108,8 @@ func replace(in interface{}) {
}
}
func git(action string) (string, error) {
cmd := exec.Command("git")
func git(ctx context.Context, action string) (string, error) {
cmd := exec.CommandContext(ctx, "git")
switch action {
case "tag":
@ -137,7 +146,7 @@ func render(name, text string, data interface{}, errorOnMissing bool) (string, e
return out.String(), nil
}
func onePassword(templateVars interface{}, errorOnMissing bool) func(account, uuid, field string) (string, error) {
func onePassword(ctx context.Context, templateVars interface{}, errorOnMissing bool) func(account, uuid, field string) (string, error) {
cache := map[string]string{}
return func(account, uuid, field string) (string, error) {
// validate command
@ -149,7 +158,7 @@ func onePassword(templateVars interface{}, errorOnMissing bool) func(account, uu
// validate session
if os.Getenv(fmt.Sprintf("OP_SESSION_%s", account)) == "" {
if err := onePasswordSignIn(account); err != nil {
if err := onePasswordSignIn(ctx, account); err != nil {
return "", err
}
}
@ -171,11 +180,11 @@ func onePassword(templateVars interface{}, errorOnMissing bool) func(account, uu
if value, ok := cache[cacheKey]; ok {
return value, nil
} else if res, err := onePasswordGet(uuid, field); err != nil && strings.Contains(res, "You are not currently signed in") {
} else if res, err := onePasswordGet(ctx, uuid, field); err != nil && strings.Contains(res, "You are not currently signed in") {
// retry with login
if err := onePasswordSignIn(account); err != nil {
if err := onePasswordSignIn(ctx, account); err != nil {
return "", err
} else if res, err = onePasswordGet(uuid, field); err != nil {
} else if res, err = onePasswordGet(ctx, uuid, field); err != nil {
return "", err
} else {
cache[cacheKey] = res
@ -190,16 +199,16 @@ func onePassword(templateVars interface{}, errorOnMissing bool) func(account, uu
}
}
func onePasswordGet(uuid, field string) (string, error) {
res, err := exec.Command("op", "--cache", "get", "item", uuid, "--fields", field).CombinedOutput()
func onePasswordGet(ctx context.Context, uuid, field string) (string, error) {
res, err := exec.CommandContext(ctx, "op", "--cache", "get", "item", uuid, "--fields", field).CombinedOutput()
return string(res), err
}
func onePasswordSignIn(account string) error {
func onePasswordSignIn(ctx context.Context, account string) error {
fmt.Println("Your templates includes a call to 1Password, please sign to retrieve your session token:")
// create command
cmd := exec.Command("op", "signin", account, "--raw")
cmd := exec.CommandContext(ctx, "op", "signin", account, "--raw")
// use multi writer to handle password prompt
var stdoutBuf bytes.Buffer

View File

@ -21,6 +21,8 @@ squadron:
ENV: <% env "SHELL" %>
GLOBAL: <% .Global.host %>
BASE64: <% base64 "1234567890" %>
DEFAULT_VALUE: <% "" | default "fallback" %>
DEFAULT_INDEX_VALUE: <% defaultIndex global "notexists" "fallback" %>
# ONE_PASSWORD: <% op "ACCOUNT_NAME" "UUID" "FIELD" %>
# ONE_PASSWORD: <% op "ACCOUNT_NAME" "Secret name" "FIELD" %>
# ONE_PASSWORD: <% op "ACCOUNT_NAME" "Secret name wit global {{ .Global.host }}" "FIELD" %>

12
unit.go
View File

@ -1,5 +1,9 @@
package squadron
import (
"context"
)
type Unit struct {
Chart ChartDependency `yaml:"chart,omitempty"`
Builds map[string]Build `yaml:"builds,omitempty"`
@ -11,9 +15,9 @@ type Unit struct {
// ------------------------------------------------------------------------------------------------
// Build ...
func (u *Unit) Build() error {
func (u *Unit) Build(ctx context.Context) error {
for _, build := range u.Builds {
if err := build.Build(); err != nil {
if err := build.Build(ctx); err != nil {
return err
}
}
@ -21,9 +25,9 @@ func (u *Unit) Build() error {
}
// Push ...
func (u *Unit) Push() error {
func (u *Unit) Push(ctx context.Context) error {
for _, build := range u.Builds {
if err := build.Push(); err != nil {
if err := build.Push(ctx); err != nil {
return err
}
}

View File

@ -2,6 +2,7 @@ package util
import (
"bytes"
"context"
"io"
"os"
"os/exec"
@ -129,8 +130,8 @@ func (c *Cmd) PostEnd(f func() error) *Cmd {
return c
}
func (c *Cmd) Run() (string, error) {
cmd := exec.Command(c.command[0], c.command[1:]...) //nolint:gosec
func (c *Cmd) Run(ctx context.Context) (string, error) {
cmd := exec.CommandContext(ctx, c.command[0], c.command[1:]...) //nolint:gosec
cmd.Env = append(os.Environ(), c.env...)
if c.cwd != "" {
cmd.Dir = c.cwd

View File

@ -1,6 +1,7 @@
package util
import (
"context"
"fmt"
"os"
)
@ -23,6 +24,6 @@ func (c *DockerCmd) Build(workDir string) *Cmd {
return c.Cwd(workDir).Args(args...)
}
func (c *DockerCmd) Push(image, tag string) (string, error) {
return c.Args("push", fmt.Sprintf("%s:%s", image, tag)).Run()
func (c *DockerCmd) Push(ctx context.Context, image, tag string) (string, error) {
return c.Args("push", fmt.Sprintf("%s:%s", image, tag)).Run(ctx)
}

View File

@ -1,5 +1,9 @@
package util
import (
"context"
)
type HelmCmd struct {
Cmd
}
@ -8,10 +12,10 @@ func NewHelmCommand() *HelmCmd {
return &HelmCmd{*NewCommand("helm")}
}
func (c HelmCmd) UpdateDependency(chart, chartPath string) (string, error) {
return c.Base().Args("dependency", "update", chartPath).Run()
func (c HelmCmd) UpdateDependency(ctx context.Context, chartPath string) (string, error) {
return c.Base().Args("dependency", "update", chartPath).Run(ctx)
}
func (c HelmCmd) Package(chart, chartPath, destPath string) (string, error) {
return c.Base().Args("package", chartPath, "--destination", destPath).Run()
func (c HelmCmd) Package(ctx context.Context, chartPath, destPath string) (string, error) {
return c.Base().Args("package", chartPath, "--destination", destPath).Run(ctx)
}

View File

@ -1,6 +1,7 @@
package util
import (
"context"
"encoding/json"
"fmt"
"os"
@ -26,13 +27,13 @@ func (c KubeCmd) WaitForRollout(deployment, timeout string) *Cmd {
"-w", "--timeout", timeout)
}
func (c KubeCmd) GetMostRecentPodBySelectors(selectors map[string]string) (string, error) {
func (c KubeCmd) GetMostRecentPodBySelectors(ctx context.Context, selectors map[string]string) (string, error) {
var selector []string //nolint:prealloc
for k, v := range selectors {
selector = append(selector, fmt.Sprintf("%v=%v", k, v))
}
out, err := c.Args("--selector", strings.Join(selector, ","),
"get", "pods", "--sort-by=.status.startTime", "-o", "name").Run()
"get", "pods", "--sort-by=.status.startTime", "-o", "name").Run(ctx)
if err != nil {
return "", err
}
@ -84,8 +85,8 @@ func (c KubeCmd) DeleteService(service string) *Cmd {
return c.Args("delete", "service", service)
}
func (c KubeCmd) GetDeployment(deployment string) (*v1.Deployment, error) {
out, err := c.Args("get", "deployment", deployment, "-o", "json").Run()
func (c KubeCmd) GetDeployment(ctx context.Context, deployment string) (*v1.Deployment, error) {
out, err := c.Args("get", "deployment", deployment, "-o", "json").Run(ctx)
if err != nil {
return nil, err
}
@ -96,30 +97,30 @@ func (c KubeCmd) GetDeployment(deployment string) (*v1.Deployment, error) {
return &d, nil
}
func (c KubeCmd) GetNamespaces() ([]string, error) {
out, err := c.Args("get", "namespace", "-o", "name").Run()
func (c KubeCmd) GetNamespaces(ctx context.Context) ([]string, error) {
out, err := c.Args("get", "namespace", "-o", "name").Run(ctx)
if err != nil {
return nil, err
}
return parseResources(out, "namespace/")
}
func (c KubeCmd) GetDeployments() ([]string, error) {
out, err := c.Args("get", "deployment", "-o", "name").Run()
func (c KubeCmd) GetDeployments(ctx context.Context) ([]string, error) {
out, err := c.Args("get", "deployment", "-o", "name").Run(ctx)
if err != nil {
return nil, err
}
return parseResources(out, "deployment.apps/")
}
func (c KubeCmd) GetPods(selectors map[string]string) ([]string, error) {
func (c KubeCmd) GetPods(ctx context.Context, selectors map[string]string) ([]string, error) {
var selector []string //nolint:prealloc
for k, v := range selectors {
selector = append(selector, fmt.Sprintf("%v=%v", k, v))
}
out, err := c.Args("--selector", strings.Join(selector, ","),
"get", "pods", "--sort-by=.status.startTime",
"-o", "name").Run()
"-o", "name").Run(ctx)
if err != nil {
return nil, err
}
@ -134,8 +135,8 @@ func (c KubeCmd) GetContainers(deployment v1.Deployment) []string {
return containers
}
func (c KubeCmd) GetPodsByLabels(labels []string) ([]string, error) {
out, err := c.Args("get", "pods", "-l", strings.Join(labels, ","), "-o", "name", "-A").Run()
func (c KubeCmd) GetPodsByLabels(ctx context.Context, labels []string) ([]string, error) {
out, err := c.Args("get", "pods", "-l", strings.Join(labels, ","), "-o", "name", "-A").Run(ctx)
if err != nil {
return nil, err
}
@ -146,27 +147,27 @@ func (c KubeCmd) RestartDeployment(deployment string) *Cmd {
return c.Args("rollout", "restart", fmt.Sprintf("deployment/%v", deployment))
}
func (c KubeCmd) CreateConfigMapFromFile(name, path string) (string, error) {
return c.Args("create", "configmap", name, "--from-file", path).Run()
func (c KubeCmd) CreateConfigMapFromFile(ctx context.Context, name, path string) (string, error) {
return c.Args("create", "configmap", name, "--from-file", path).Run(ctx)
}
func (c KubeCmd) CreateConfigMap(name string, keyMap map[string]string) (string, error) {
func (c KubeCmd) CreateConfigMap(ctx context.Context, name string, keyMap map[string]string) (string, error) {
c.Args("create", "configmap", name)
for key, value := range keyMap {
c.Args(fmt.Sprintf("--from-literal=%v=%v", key, value))
}
return c.Run()
return c.Run(ctx)
}
func (c KubeCmd) DeleteConfigMap(name string) (string, error) {
return c.Args("delete", "configmap", name).Run()
func (c KubeCmd) DeleteConfigMap(ctx context.Context, name string) (string, error) {
return c.Args("delete", "configmap", name).Run(ctx)
}
func (c KubeCmd) GetConfigMapKey(name, key string) (string, error) {
func (c KubeCmd) GetConfigMapKey(ctx context.Context, name, key string) (string, error) {
key = strings.ReplaceAll(key, ".", "\\.")
// jsonpath map key is not very fond of dots
out, err := c.Args("get", "configmap", name, "-o",
fmt.Sprintf("jsonpath={.data.%v}", key)).Run()
fmt.Sprintf("jsonpath={.data.%v}", key)).Run(ctx)
if err != nil {
return out, err
}