Skip to content

feature: diffing between different releases #131

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 7, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# Helm Diff Plugin
[![Go Report Card](https://goreportcard.com/badge/github.com/databus23/helm-diff)](https://goreportcard.com/report/github.com/databus23/helm-diff)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/databus23/helm-diff/blob/master/LICENSE)

This is a Helm plugin giving your a preview of what a `helm upgrade` would change.
It basically generates a diff between the latest deployed version of a release
Expand Down Expand Up @@ -33,6 +35,7 @@ Usage:
diff [command]

Available Commands:
release Shows diff between release's manifests
revision Shows diff between revision's manifests
rollback Show a diff explaining what a helm rollback could perform
upgrade Show a diff explaining what a helm upgrade would change.
Expand Down Expand Up @@ -89,6 +92,41 @@ Global Flags:
--no-color remove colors from the output
```

### release:

```
$ helm diff release -h

This command compares the manifests details of a different releases created from the same chart

It can be used to compare the manifests of

- release1 with release2
$ helm diff release [flags] release1 release2
Example:
$ helm diff release my-prod my-stage

Usage:
diff release [flags] RELEASE release1 [release2]

Flags:
-C, --context int output NUM lines of context around changes (default -1)
-h, --help help for release
--home string location of your Helm config. Overrides $HELM_HOME (default "/home/aananth/.helm")
--include-tests enable the diffing of the helm test hooks
--suppress stringArray allows suppression of the values listed in the diff output
-q, --suppress-secrets suppress secrets in the output
--tls enable TLS for request
--tls-ca-cert string path to TLS CA certificate file (default "$HELM_HOME/ca.pem")
--tls-cert string path to TLS certificate file (default "$HELM_HOME/cert.pem")
--tls-hostname string the server name used to verify the hostname on the returned certificates from the server
--tls-key string path to TLS key file (default "$HELM_HOME/key.pem")
--tls-verify enable TLS for request and verify remote

Global Flags:
--no-color remove colors from the output
```

### revision:

```
Expand Down
111 changes: 111 additions & 0 deletions cmd/release.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package cmd

import (
"errors"
"fmt"
"os"

"github.com/spf13/cobra"
"k8s.io/helm/pkg/helm"

"github.com/databus23/helm-diff/diff"
"github.com/databus23/helm-diff/manifest"
)

type release struct {
client helm.Interface
detailedExitCode bool
suppressedKinds []string
releases []string
outputContext int
includeTests bool
}

const releaseCmdLongUsage = `
This command compares the manifests details of a different releases created from the same chart

It can be used to compare the manifests of

- release1 with release2
$ helm diff release [flags] release1 release2
Example:
$ helm diff release my-prod my-stage
`

func releaseCmd() *cobra.Command {
diff := release{}
releaseCmd := &cobra.Command{
Use: "release [flags] RELEASE release1 [release2]",
Short: "Shows diff between release's manifests",
Long: releaseCmdLongUsage,
PreRun: func(*cobra.Command, []string) {
expandTLSPaths()
},
RunE: func(cmd *cobra.Command, args []string) error {
// Suppress the command usage on error. See #77 for more info
cmd.SilenceUsage = true

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 \"release\".\nMinimum 2 arguments required: release name-1, release name-2")
}

if q, _ := cmd.Flags().GetBool("suppress-secrets"); q {
diff.suppressedKinds = append(diff.suppressedKinds, "Secret")
}

diff.releases = args[0:]
if diff.client == nil {
diff.client = createHelmClient()
}
return diff.differentiate()
},
}

releaseCmd.Flags().BoolP("suppress-secrets", "q", false, "suppress secrets in the output")
releaseCmd.Flags().StringArrayVar(&diff.suppressedKinds, "suppress", []string{}, "allows suppression of the values listed in the diff output")
releaseCmd.Flags().IntVarP(&diff.outputContext, "context", "C", -1, "output NUM lines of context around changes")
releaseCmd.Flags().BoolVar(&diff.includeTests, "include-tests", false, "enable the diffing of the helm test hooks")
releaseCmd.SuggestionsMinimumDistance = 1

addCommonCmdOptions(releaseCmd.Flags())

return releaseCmd
}

func (d *release) differentiate() error {

releaseResponse1, err := d.client.ReleaseContent(d.releases[0])
if err != nil {
return prettyError(err)
}

releaseResponse2, err := d.client.ReleaseContent(d.releases[1])
if err != nil {
return prettyError(err)
}

if releaseResponse1.Release.Chart.Metadata.Name == releaseResponse2.Release.Chart.Metadata.Name {
seenAnyChanges := diff.DiffReleases(
manifest.ParseRelease(releaseResponse1.Release, d.includeTests),
manifest.ParseRelease(releaseResponse2.Release, d.includeTests),
d.suppressedKinds,
d.outputContext,
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,
}
}
} else {
fmt.Printf("Error : Incomparable Releases \n Unable to compare releases from two different charts \"%s\", \"%s\". \n try helm diff release --help to know more \n", releaseResponse1.Release.Chart.Metadata.Name, releaseResponse2.Release.Chart.Metadata.Name)
}
return nil
}
1 change: 1 addition & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ func New() *cobra.Command {
cmd.AddCommand(
revisionCmd(),
rollbackCmd(),
releaseCmd(),
)
cmd.SetHelpCommand(&cobra.Command{}) // Disable the help command
return cmd
Expand Down
39 changes: 39 additions & 0 deletions diff/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"io"
"math"
"sort"
"strings"

"github.com/aryann/difflib"
Expand Down Expand Up @@ -51,6 +52,13 @@ func DiffManifests(oldIndex, newIndex map[string]*manifest.MappingResult, suppre
return seenAnyChanges
}

// DiffReleases reindex the content based on the template names and pass it to DiffManifests
func DiffReleases(oldIndex, newIndex map[string]*manifest.MappingResult, suppressedKinds []string, context int, to io.Writer) bool {
oldIndex = reIndexForRelease(oldIndex)
newIndex = reIndexForRelease(newIndex)
return DiffManifests(oldIndex, newIndex, suppressedKinds, context, to)
}

func diffMappingResults(oldContent *manifest.MappingResult, newContent *manifest.MappingResult) []difflib.DiffRecord {
return diffStrings(oldContent.Content, newContent.Content)
}
Expand Down Expand Up @@ -137,3 +145,34 @@ func calculateDistances(diffs []difflib.DiffRecord) map[int]int {

return distances
}

// reIndexForRelease based on template names
func reIndexForRelease(index map[string]*manifest.MappingResult) map[string]*manifest.MappingResult {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very very nit but I think it's fine to say reindex rather than reIndex 😃


// sort the index to iterate map in the same order
var keys []string
for key := range index {
keys = append(keys, key)
}
sort.Strings(keys)

// holds number of object in a single file
count := make(map[string]int)

newIndex := make(map[string]*manifest.MappingResult)

for key := range keys {

str := strings.Replace(strings.Split(index[keys[key]].Content, "\n")[0], "# Source: ", "", 1)

if _, ok := newIndex[str]; ok {
count[str]++
str += fmt.Sprintf(" %d", count[str])
newIndex[str] = index[keys[key]]
} else {
newIndex[str] = index[keys[key]]
count[str]++
}
}
return newIndex
}
22 changes: 11 additions & 11 deletions glide.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.