From 3b4ce90a5a9fd22676fb8d9818b244221758f8b4 Mon Sep 17 00:00:00 2001 From: Yusuke KUOKA Date: Thu, 26 Jul 2018 00:16:32 +0900 Subject: [PATCH] feat: Optional detailed exitcodes for `helmfile diff` Adds the `--detailed-exitcode` to the `helmfile diff` command to return `1` on failure, and `2` when no error but diff is seen. This feature requires the latest `helm-diff` containing https://github.com/databus23/helm-diff/pull/78, and `helm` containing https://github.com/helm/helm/pull/4367. This is verified to work by manually running commands like the followings: ```bash ./helmfile --helm-binary helm211dev -f ./examples/helmfile.d diff --detailed-exitcode; echo $? ./helmfile --helm-binary helm211dev -f ./examples/helmfile.d diff; echo $? ``` Note that, in above example commands, `helm211dev` is a custom `helm` binary that is built from helm's master branch containing [the necessary enhancement to allow propagate non-zero plugin exit code](https://github.com/helm/helm/pull/4367). --- main.go | 20 ++++++++++++++++---- state/state.go | 6 +++++- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/main.go b/main.go index 6e3009b4..76656811 100644 --- a/main.go +++ b/main.go @@ -15,6 +15,7 @@ import ( "github.com/urfave/cli" "go.uber.org/zap" "go.uber.org/zap/zapcore" + "os/exec" ) const ( @@ -166,6 +167,10 @@ func main() { Name: "sync-repos", Usage: "enable a repo sync prior to diffing", }, + cli.BoolFlag{ + Name: "detailed-exitcode", + Usage: "return a non-zero exit code when there are changes", + }, cli.IntFlag{ Name: "concurrency", Value: 0, @@ -190,8 +195,9 @@ func main() { values := c.StringSlice("values") workers := c.Int("concurrency") + detailedExitCode := c.Bool("detailed-exitcode") - return state.DiffReleases(helm, values, workers) + return state.DiffReleases(helm, values, workers, detailedExitCode) }) }, }, @@ -367,8 +373,7 @@ func main() { err := app.Run(os.Args) if err != nil { - log.Printf("err: %s", err.Error()) - os.Exit(1) + log.Panicf("[bug] this code path shouldn't be arrived: helmfile is expected to exit from within the `cleanup` func in main.go: %v", err) } } @@ -517,7 +522,14 @@ func clean(state *state.HelmState, errs []error) error { for _, err := range errs { fmt.Printf("err: %s\n", err.Error()) } - os.Exit(1) + switch e := errs[0].(type) { + case *exec.ExitError: + // Propagate any non-zero exit status from the external command like `helm` that is failed under the hood + status := e.Sys().(syscall.WaitStatus) + os.Exit(status.ExitStatus()) + default: + os.Exit(1) + } } return nil } diff --git a/state/state.go b/state/state.go index dc9708a4..c23f57e1 100644 --- a/state/state.go +++ b/state/state.go @@ -254,7 +254,7 @@ func (state *HelmState) SyncReleases(helm helmexec.Interface, additionalValues [ } // DiffReleases wrapper for executing helm diff on the releases -func (state *HelmState) DiffReleases(helm helmexec.Interface, additionalValues []string, workerLimit int) []error { +func (state *HelmState) DiffReleases(helm helmexec.Interface, additionalValues []string, workerLimit int, detailedExitCode bool) []error { var wgRelease sync.WaitGroup var wgError sync.WaitGroup errs := []error{} @@ -289,6 +289,10 @@ func (state *HelmState) DiffReleases(helm helmexec.Interface, additionalValues [ flags = append(flags, "--values", valfile) } + if detailedExitCode { + flags = append(flags, "--detailed-exitcode") + } + if len(errs) == 0 { if err := helm.DiffRelease(release.Name, normalizeChart(state.BaseChartPath, release.Chart), flags...); err != nil { errs = append(errs, err)