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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,7 @@
package actions package actions
import ( import (
"context"
"fmt" "fmt"
"os/user" "os/user"
"strings" "strings"
@ -23,11 +24,11 @@ var upCmd = &cobra.Command{
Short: "installs the squadron or given units", Short: "installs the squadron or given units",
Example: " squadron up frontend backend --namespace demo --build --push -- --dry-run", Example: " squadron up frontend backend --namespace demo --build --push -- --dry-run",
RunE: func(cmd *cobra.Command, args []string) error { 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) sq := squadron.New(cwd, namespace, files)
if err := sq.MergeConfigFiles(); err != nil { 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 return err
} }
@ -58,7 +59,7 @@ func up(args []string, cwd, namespace string, build, push, diff bool, files []st
if build { if build {
for _, unit := range units { for _, unit := range units {
if err := unit.Build(); err != nil { if err := unit.Build(ctx); err != nil {
return err return err
} }
} }
@ -66,31 +67,31 @@ func up(args []string, cwd, namespace string, build, push, diff bool, files []st
if push { if push {
for _, unit := range units { for _, unit := range units {
if err := unit.Push(); err != nil { if err := unit.Push(ctx); err != nil {
return err return err
} }
} }
} }
if err := sq.Generate(units); err != nil { if err := sq.Generate(ctx, units); err != nil {
return err return err
} }
username := "unknown" 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) username = strings.TrimSpace(value)
} else if value, err := user.Current(); err == nil { } else if value, err := user.Current(); err == nil {
username = strings.TrimSpace(value.Name) username = strings.TrimSpace(value.Name)
} }
commit := "" 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) commit = strings.TrimSpace(value)
} }
if !diff { if !diff {
return sq.Up(units, helmArgs, username, version, commit) return sq.Up(ctx, units, helmArgs, username, version, commit)
} else if out, err := sq.Diff(units, helmArgs); err != nil { } else if out, err := sq.Diff(ctx, units, helmArgs); err != nil {
return err return err
} else { } else {
fmt.Println(out) fmt.Println(out)

View File

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

View File

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

View File

@ -21,6 +21,8 @@ squadron:
ENV: <% env "SHELL" %> ENV: <% env "SHELL" %>
GLOBAL: <% .Global.host %> GLOBAL: <% .Global.host %>
BASE64: <% base64 "1234567890" %> 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" "UUID" "FIELD" %>
# ONE_PASSWORD: <% op "ACCOUNT_NAME" "Secret name" "FIELD" %> # ONE_PASSWORD: <% op "ACCOUNT_NAME" "Secret name" "FIELD" %>
# ONE_PASSWORD: <% op "ACCOUNT_NAME" "Secret name wit global {{ .Global.host }}" "FIELD" %> # ONE_PASSWORD: <% op "ACCOUNT_NAME" "Secret name wit global {{ .Global.host }}" "FIELD" %>

12
unit.go
View File

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

View File

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

View File

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

View File

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

View File

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