Skip to content

Commit c20f260

Browse files
codeniomumoshu
authored andcommitted
diffing between different releases (#131)
- Add diff release subcommand to compare different releases - Update subcommands usage in README - (Extra!) Adds go report card badge and license badge to README Resolves #130
1 parent f0cbdb2 commit c20f260

File tree

5 files changed

+200
-11
lines changed

5 files changed

+200
-11
lines changed

README.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
# Helm Diff Plugin
2+
[![Go Report Card](https://goreportcard.com/badge/github.com/databus23/helm-diff)](https://goreportcard.com/report/github.com/databus23/helm-diff)
3+
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/databus23/helm-diff/blob/master/LICENSE)
24

35
This is a Helm plugin giving your a preview of what a `helm upgrade` would change.
46
It basically generates a diff between the latest deployed version of a release
@@ -33,6 +35,7 @@ Usage:
3335
diff [command]
3436
3537
Available Commands:
38+
release Shows diff between release's manifests
3639
revision Shows diff between revision's manifests
3740
rollback Show a diff explaining what a helm rollback could perform
3841
upgrade Show a diff explaining what a helm upgrade would change.
@@ -89,6 +92,41 @@ Global Flags:
8992
--no-color remove colors from the output
9093
```
9194

95+
### release:
96+
97+
```
98+
$ helm diff release -h
99+
100+
This command compares the manifests details of a different releases created from the same chart
101+
102+
It can be used to compare the manifests of
103+
104+
- release1 with release2
105+
$ helm diff release [flags] release1 release2
106+
Example:
107+
$ helm diff release my-prod my-stage
108+
109+
Usage:
110+
diff release [flags] RELEASE release1 [release2]
111+
112+
Flags:
113+
-C, --context int output NUM lines of context around changes (default -1)
114+
-h, --help help for release
115+
--home string location of your Helm config. Overrides $HELM_HOME (default "/home/aananth/.helm")
116+
--include-tests enable the diffing of the helm test hooks
117+
--suppress stringArray allows suppression of the values listed in the diff output
118+
-q, --suppress-secrets suppress secrets in the output
119+
--tls enable TLS for request
120+
--tls-ca-cert string path to TLS CA certificate file (default "$HELM_HOME/ca.pem")
121+
--tls-cert string path to TLS certificate file (default "$HELM_HOME/cert.pem")
122+
--tls-hostname string the server name used to verify the hostname on the returned certificates from the server
123+
--tls-key string path to TLS key file (default "$HELM_HOME/key.pem")
124+
--tls-verify enable TLS for request and verify remote
125+
126+
Global Flags:
127+
--no-color remove colors from the output
128+
```
129+
92130
### revision:
93131

94132
```

cmd/release.go

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package cmd
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"os"
7+
8+
"github.com/spf13/cobra"
9+
"k8s.io/helm/pkg/helm"
10+
11+
"github.com/databus23/helm-diff/diff"
12+
"github.com/databus23/helm-diff/manifest"
13+
)
14+
15+
type release struct {
16+
client helm.Interface
17+
detailedExitCode bool
18+
suppressedKinds []string
19+
releases []string
20+
outputContext int
21+
includeTests bool
22+
}
23+
24+
const releaseCmdLongUsage = `
25+
This command compares the manifests details of a different releases created from the same chart
26+
27+
It can be used to compare the manifests of
28+
29+
- release1 with release2
30+
$ helm diff release [flags] release1 release2
31+
Example:
32+
$ helm diff release my-prod my-stage
33+
`
34+
35+
func releaseCmd() *cobra.Command {
36+
diff := release{}
37+
releaseCmd := &cobra.Command{
38+
Use: "release [flags] RELEASE release1 [release2]",
39+
Short: "Shows diff between release's manifests",
40+
Long: releaseCmdLongUsage,
41+
PreRun: func(*cobra.Command, []string) {
42+
expandTLSPaths()
43+
},
44+
RunE: func(cmd *cobra.Command, args []string) error {
45+
// Suppress the command usage on error. See #77 for more info
46+
cmd.SilenceUsage = true
47+
48+
if v, _ := cmd.Flags().GetBool("version"); v {
49+
fmt.Println(Version)
50+
return nil
51+
}
52+
53+
switch {
54+
case len(args) < 2:
55+
return errors.New("Too few arguments to Command \"release\".\nMinimum 2 arguments required: release name-1, release name-2")
56+
}
57+
58+
if q, _ := cmd.Flags().GetBool("suppress-secrets"); q {
59+
diff.suppressedKinds = append(diff.suppressedKinds, "Secret")
60+
}
61+
62+
diff.releases = args[0:]
63+
if diff.client == nil {
64+
diff.client = createHelmClient()
65+
}
66+
return diff.differentiate()
67+
},
68+
}
69+
70+
releaseCmd.Flags().BoolP("suppress-secrets", "q", false, "suppress secrets in the output")
71+
releaseCmd.Flags().StringArrayVar(&diff.suppressedKinds, "suppress", []string{}, "allows suppression of the values listed in the diff output")
72+
releaseCmd.Flags().IntVarP(&diff.outputContext, "context", "C", -1, "output NUM lines of context around changes")
73+
releaseCmd.Flags().BoolVar(&diff.includeTests, "include-tests", false, "enable the diffing of the helm test hooks")
74+
releaseCmd.SuggestionsMinimumDistance = 1
75+
76+
addCommonCmdOptions(releaseCmd.Flags())
77+
78+
return releaseCmd
79+
}
80+
81+
func (d *release) differentiate() error {
82+
83+
releaseResponse1, err := d.client.ReleaseContent(d.releases[0])
84+
if err != nil {
85+
return prettyError(err)
86+
}
87+
88+
releaseResponse2, err := d.client.ReleaseContent(d.releases[1])
89+
if err != nil {
90+
return prettyError(err)
91+
}
92+
93+
if releaseResponse1.Release.Chart.Metadata.Name == releaseResponse2.Release.Chart.Metadata.Name {
94+
seenAnyChanges := diff.DiffReleases(
95+
manifest.ParseRelease(releaseResponse1.Release, d.includeTests),
96+
manifest.ParseRelease(releaseResponse2.Release, d.includeTests),
97+
d.suppressedKinds,
98+
d.outputContext,
99+
os.Stdout)
100+
101+
if d.detailedExitCode && seenAnyChanges {
102+
return Error{
103+
error: errors.New("identified at least one change, exiting with non-zero exit code (detailed-exitcode parameter enabled)"),
104+
Code: 2,
105+
}
106+
}
107+
} else {
108+
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)
109+
}
110+
return nil
111+
}

cmd/root.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ func New() *cobra.Command {
5656
cmd.AddCommand(
5757
revisionCmd(),
5858
rollbackCmd(),
59+
releaseCmd(),
5960
)
6061
cmd.SetHelpCommand(&cobra.Command{}) // Disable the help command
6162
return cmd

diff/diff.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"io"
66
"math"
7+
"sort"
78
"strings"
89

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

55+
// DiffReleases reindex the content based on the template names and pass it to DiffManifests
56+
func DiffReleases(oldIndex, newIndex map[string]*manifest.MappingResult, suppressedKinds []string, context int, to io.Writer) bool {
57+
oldIndex = reIndexForRelease(oldIndex)
58+
newIndex = reIndexForRelease(newIndex)
59+
return DiffManifests(oldIndex, newIndex, suppressedKinds, context, to)
60+
}
61+
5462
func diffMappingResults(oldContent *manifest.MappingResult, newContent *manifest.MappingResult) []difflib.DiffRecord {
5563
return diffStrings(oldContent.Content, newContent.Content)
5664
}
@@ -137,3 +145,34 @@ func calculateDistances(diffs []difflib.DiffRecord) map[int]int {
137145

138146
return distances
139147
}
148+
149+
// reIndexForRelease based on template names
150+
func reIndexForRelease(index map[string]*manifest.MappingResult) map[string]*manifest.MappingResult {
151+
152+
// sort the index to iterate map in the same order
153+
var keys []string
154+
for key := range index {
155+
keys = append(keys, key)
156+
}
157+
sort.Strings(keys)
158+
159+
// holds number of object in a single file
160+
count := make(map[string]int)
161+
162+
newIndex := make(map[string]*manifest.MappingResult)
163+
164+
for key := range keys {
165+
166+
str := strings.Replace(strings.Split(index[keys[key]].Content, "\n")[0], "# Source: ", "", 1)
167+
168+
if _, ok := newIndex[str]; ok {
169+
count[str]++
170+
str += fmt.Sprintf(" %d", count[str])
171+
newIndex[str] = index[keys[key]]
172+
} else {
173+
newIndex[str] = index[keys[key]]
174+
count[str]++
175+
}
176+
}
177+
return newIndex
178+
}

glide.lock

Lines changed: 11 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)