From 72467efb507f019ef9bc43eb668a4ae68dcdde66 Mon Sep 17 00:00:00 2001 From: aananthraj Date: Thu, 26 Apr 2018 22:54:48 +0530 Subject: [PATCH] adding revision and rollback subcommands --- cmd/revision.go | 118 ++++++++++++++++++++++++++++++++++++++++++++++++ cmd/rollback.go | 86 +++++++++++++++++++++++++++++++++++ cmd/root.go | 38 +++++++++++----- cmd/upgrade.go | 6 --- 4 files changed, 231 insertions(+), 17 deletions(-) create mode 100644 cmd/revision.go create mode 100644 cmd/rollback.go diff --git a/cmd/revision.go b/cmd/revision.go new file mode 100644 index 00000000..2e4fc597 --- /dev/null +++ b/cmd/revision.go @@ -0,0 +1,118 @@ +package cmd + +import ( + "errors" + "fmt" + "os" + "strconv" + + "github.com/databus23/helm-diff/diff" + "github.com/databus23/helm-diff/manifest" + "github.com/spf13/cobra" + "k8s.io/helm/pkg/helm" +) + +type revision struct { + release string + client helm.Interface + suppressedKinds []string + revisions []string +} + +const revisionCmdLongUsage = ` +This command compares the manifests details of a named release. + +It can be used to compare the manifests of + + - lastest REVISION with specified REVISION + $ helm diff revision [flags] RELEASE REVISION1 + Example: + $ helm diff revision my-release 2 + + - REVISION1 with REVISION2 + $ helm diff revision [flags] RELEASE REVISION1 REVISION2 + Example: + $ helm diff revision my-release 2 3 +` + +func revisionCmd() *cobra.Command { + diff := revision{} + revisionCmd := &cobra.Command{ + Use: "revision [flags] RELEASE REVISION1 [REVISION2]", + Short: "Shows diff between revision's manifests", + Long: revisionCmdLongUsage, + RunE: func(cmd *cobra.Command, args []string) error { + if v, _ := cmd.Flags().GetBool("version"); v { + fmt.Println(Version) + return nil + } + + switch { + case len(args) < 2: + return errors.New("Too few arguments to Command \"revision\".\nMinimum 2 arguments required: release name, revision") + case len(args) > 3: + return errors.New("Too many arguments to Command \"revision\".\nMaximum 3 arguments allowed: release name, revision1, revision2") + } + + if q, _ := cmd.Flags().GetBool("suppress-secrets"); q { + diff.suppressedKinds = append(diff.suppressedKinds, "Secret") + } + + diff.release = args[0] + diff.revisions = args[1:] + if diff.client == nil { + diff.client = helm.NewClient(helm.Host(os.Getenv("TILLER_HOST")), helm.ConnectTimeout(int64(30))) + } + return diff.differentiate() + }, + } + + revisionCmd.Flags().BoolP("suppress-secrets", "q", false, "suppress secrets in the output") + revisionCmd.Flags().StringArrayVar(&diff.suppressedKinds, "suppress", []string{}, "allows suppression of the values listed in the diff output") + revisionCmd.SuggestionsMinimumDistance = 1 + return revisionCmd +} + +func (d *revision) differentiate() error { + + switch len(d.revisions) { + case 1: + releaseResponse, err := d.client.ReleaseContent(d.release) + + if err != nil { + return prettyError(err) + } + + revision, _ := strconv.Atoi(d.revisions[0]) + revisionResponse, err := d.client.ReleaseContent(d.release, helm.ContentReleaseVersion(int32(revision))) + if err != nil { + return prettyError(err) + } + + diff.DiffManifests(manifest.Parse(revisionResponse.Release.Manifest), manifest.Parse(releaseResponse.Release.Manifest), d.suppressedKinds, os.Stdout) + + case 2: + revision1, _ := strconv.Atoi(d.revisions[0]) + revision2, _ := strconv.Atoi(d.revisions[1]) + if revision1 > revision2 { + revision1, revision2 = revision2, revision1 + } + + revisionResponse1, err := d.client.ReleaseContent(d.release, helm.ContentReleaseVersion(int32(revision1))) + if err != nil { + return prettyError(err) + } + + revisionResponse2, err := d.client.ReleaseContent(d.release, helm.ContentReleaseVersion(int32(revision2))) + if err != nil { + return prettyError(err) + } + + diff.DiffManifests(manifest.Parse(revisionResponse1.Release.Manifest), manifest.Parse(revisionResponse2.Release.Manifest), d.suppressedKinds, os.Stdout) + + default: + return errors.New("Invalid Arguments") + } + + return nil +} diff --git a/cmd/rollback.go b/cmd/rollback.go new file mode 100644 index 00000000..97a5b2a2 --- /dev/null +++ b/cmd/rollback.go @@ -0,0 +1,86 @@ +package cmd + +import ( + "fmt" + "os" + "strconv" + + "github.com/databus23/helm-diff/diff" + "github.com/databus23/helm-diff/manifest" + "github.com/spf13/cobra" + "k8s.io/helm/pkg/helm" +) + +type rollback struct { + release string + client helm.Interface + suppressedKinds []string + revisions []string +} + +const rollbackCmdLongUsage = ` +This command compares the laset manifests details of a named release +with specific revision values to rollback. + +It forecasts/visualizes changes, that a helm rollback could perform. +` + +func rollbackCmd() *cobra.Command { + diff := rollback{} + rollbackCmd := &cobra.Command{ + Use: "rollback [flags] [RELEASE] [REVISION]", + Short: "Show a diff explaining what a helm rollback could perform", + Long: rollbackCmdLongUsage, + Example: " helm diff rollback my-release 2", + RunE: func(cmd *cobra.Command, args []string) error { + if v, _ := cmd.Flags().GetBool("version"); v { + fmt.Println(Version) + return nil + } + + if err := checkArgsLength(len(args), "release name", "revision number"); err != nil { + return err + } + + if q, _ := cmd.Flags().GetBool("suppress-secrets"); q { + diff.suppressedKinds = append(diff.suppressedKinds, "Secret") + } + + diff.release = args[0] + diff.revisions = args[1:] + + if diff.client == nil { + diff.client = helm.NewClient(helm.Host(os.Getenv("TILLER_HOST")), helm.ConnectTimeout(int64(30))) + } + + return diff.backcast() + }, + } + + rollbackCmd.Flags().BoolP("suppress-secrets", "q", false, "suppress secrets in the output") + rollbackCmd.Flags().StringArrayVar(&diff.suppressedKinds, "suppress", []string{}, "allows suppression of the values listed in the diff output") + rollbackCmd.SuggestionsMinimumDistance = 1 + return rollbackCmd +} + +func (d *rollback) backcast() error { + + // get manifest of the latest release + releaseResponse, err := d.client.ReleaseContent(d.release) + + if err != nil { + return prettyError(err) + } + + // get manifest of the release to rollback + revision, _ := strconv.Atoi(d.revisions[0]) + revisionResponse, err := d.client.ReleaseContent(d.release, helm.ContentReleaseVersion(int32(revision))) + if err != nil { + return prettyError(err) + } + + // create a diff between the current manifest and the version of the manifest that a user is intended to rollback + diff.DiffManifests(manifest.Parse(releaseResponse.Release.Manifest), manifest.Parse(revisionResponse.Release.Manifest), d.suppressedKinds, os.Stdout) + + return nil +} diff --git a/cmd/root.go b/cmd/root.go index 32334fc9..0ed85349 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -1,25 +1,41 @@ package cmd -import "github.com/spf13/cobra" +import ( + "github.com/mgutz/ansi" + "github.com/spf13/cobra" +) func New() *cobra.Command { + chartCommand := newChartCommand() + cmd := &cobra.Command{ Use: "diff", Short: "Show manifest differences", + //Alias root command to chart subcommand + Args: chartCommand.Args, + // parse the flags and check for actions like suppress-secrets, no-colors + PersistentPreRun: func(cmd *cobra.Command, args []string) { + if nc, _ := cmd.Flags().GetBool("no-color"); nc { + ansi.DisableColors(true) + } + }, + RunE: func(cmd *cobra.Command, args []string) error { + cmd.Println(`Command "helm diff" is deprecated, use "helm diff upgrade" instead`) + return chartCommand.RunE(cmd, args) + }, } - chartCommand := newChartCommand() - cmd.AddCommand(newVersionCmd(), chartCommand) - //Alias root command to chart subcommand - cmd.Args = chartCommand.Args + // add no-color as global flag + cmd.PersistentFlags().Bool("no-color", false, "remove colors from the output") + // add flagset from chartCommand cmd.Flags().AddFlagSet(chartCommand.Flags()) - cmd.RunE = func(cmd *cobra.Command, args []string) error { - cmd.Println(`Command "helm diff" is deprecated, use "helm diff upgrade" instead`) - return chartCommand.RunE(cmd, args) - } + cmd.AddCommand(newVersionCmd(), chartCommand) + // add subcommands + cmd.AddCommand( + revisionCmd(), + rollbackCmd(), + ) cmd.SetHelpCommand(&cobra.Command{}) // Disable the help command - return cmd - } diff --git a/cmd/upgrade.go b/cmd/upgrade.go index 3488c71f..ca6d79f1 100644 --- a/cmd/upgrade.go +++ b/cmd/upgrade.go @@ -5,7 +5,6 @@ import ( "github.com/databus23/helm-diff/diff" "github.com/databus23/helm-diff/manifest" - "github.com/mgutz/ansi" "github.com/spf13/cobra" "k8s.io/helm/pkg/helm" ) @@ -47,10 +46,6 @@ func newChartCommand() *cobra.Command { diff.suppressedKinds = append(diff.suppressedKinds, "Secret") } - if nc, _ := cmd.Flags().GetBool("no-color"); nc { - ansi.DisableColors(true) - } - diff.release = args[0] diff.chart = args[1] if diff.client == nil { @@ -63,7 +58,6 @@ func newChartCommand() *cobra.Command { f := cmd.Flags() f.StringVar(&diff.chartVersion, "version", "", "specify the exact chart version to use. If this is not specified, the latest version is used") f.BoolP("suppress-secrets", "q", false, "suppress secrets in the output") - f.Bool("no-color", false, "remove colors from the output") f.VarP(&diff.valueFiles, "values", "f", "specify values in a YAML file (can specify multiple)") f.StringArrayVar(&diff.values, "set", []string{}, "set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)") f.BoolVar(&diff.reuseValues, "reuse-values", false, "reuse the last release's values and merge in any new values")