package cmd
import (
"errors"
"fmt"
"os"
"strconv"
"github.com/spf13/cobra"
"github.com/databus23/helm-diff/v3/diff"
"github.com/databus23/helm-diff/v3/manifest"
)
type rollback struct {
release string
kubeContext string
detailedExitCode bool
revisions []string
includeTests bool
normalizeManifests bool
diff.Options
}
const rollbackCmdLongUsage = `
This command compares the latest manifest 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 {
cmd.SilenceUsage = true
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
}
ProcessDiffOptions(cmd.Flags(), &diff.Options)
diff.release = args[0]
diff.revisions = args[1:]
return diff.backcastHelm3()
},
}
rollbackCmd.Flags().BoolVar(&diff.detailedExitCode, "detailed-exitcode", false, "return a non-zero exit code when there are changes")
rollbackCmd.Flags().BoolVar(&diff.includeTests, "include-tests", false, "enable the diffing of the helm test hooks")
rollbackCmd.Flags().BoolVar(&diff.normalizeManifests, "normalize-manifests", false, "normalize manifests before running diff to exclude style differences from the output")
rollbackCmd.Flags().StringVar(&diff.kubeContext, "kube-context", "", "name of the kubeconfig context to use")
AddDiffOptions(rollbackCmd.Flags(), &diff.Options)
rollbackCmd.SuggestionsMinimumDistance = 1
return rollbackCmd
}
func (d *rollback) backcastHelm3() error {
namespace := os.Getenv("HELM_NAMESPACE")
excludes := []string{manifest.Helm3TestHook, manifest.Helm2TestSuccessHook}
if d.includeTests {
excludes = []string{}
}
releaseResponse, err := getRelease(d.release, 0, namespace, d.kubeContext)
if err != nil {
return err
}
revision, _ := strconv.Atoi(d.revisions[0])
revisionResponse, err := getRelease(d.release, revision, namespace, d.kubeContext)
if err != nil {
return err
}
oldSpecs := manifest.Parse(releaseResponse, namespace, d.normalizeManifests, excludes...)
newSpecs := manifest.Parse(revisionResponse, namespace, d.normalizeManifests, excludes...)
releaseResponse = nil
revisionResponse = nil
seenAnyChanges := diff.Manifests(
oldSpecs,
newSpecs,
&d.Options,
os.Stdout)
if d.detailedExitCode && seenAnyChanges {
return Error{
error: errors.New("identified at least one change, exiting with non-zero exit code (detailed-exitcode parameter enabled)"),
Code: 2,
}
}
return nil
}