Skip to content

Commit

Permalink
Add --normalize-manifests flag to control manifest normalization beha…
Browse files Browse the repository at this point in the history
…vior
  • Loading branch information
tommasopozzetti committed May 28, 2021
1 parent bdb0ab0 commit cbb60e1
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 75 deletions.
26 changes: 14 additions & 12 deletions cmd/release.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@ import (
)

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

const releaseCmdLongUsage = `
Expand Down Expand Up @@ -79,6 +80,7 @@ func releaseCmd() *cobra.Command {
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.Flags().StringVar(&diff.output, "output", "diff", "Possible values: diff, simple, template. When set to \"template\", use the env var HELM_DIFF_TPL to specify the template.")
releaseCmd.Flags().BoolVar(&diff.normalizeManifests, "normalize-manifests", false, "normalize manifests before running diff to exclude style differences from the output")

releaseCmd.SuggestionsMinimumDistance = 1

Expand Down Expand Up @@ -115,8 +117,8 @@ func (d *release) differentiateHelm3() error {

if releaseChart1 == releaseChart2 {
seenAnyChanges := diff.Releases(
manifest.Parse(string(releaseResponse1), namespace, excludes...),
manifest.Parse(string(releaseResponse2), namespace, excludes...),
manifest.Parse(string(releaseResponse1), namespace, d.normalizeManifests, excludes...),
manifest.Parse(string(releaseResponse2), namespace, d.normalizeManifests, excludes...),
d.suppressedKinds,
d.showSecrets,
d.outputContext,
Expand Down Expand Up @@ -149,8 +151,8 @@ func (d *release) differentiate() error {

if releaseResponse1.Release.Chart.Metadata.Name == releaseResponse2.Release.Chart.Metadata.Name {
seenAnyChanges := diff.Releases(
manifest.ParseRelease(releaseResponse1.Release, d.includeTests),
manifest.ParseRelease(releaseResponse2.Release, d.includeTests),
manifest.ParseRelease(releaseResponse1.Release, d.includeTests, d.normalizeManifests),
manifest.ParseRelease(releaseResponse2.Release, d.includeTests, d.normalizeManifests),
d.suppressedKinds,
d.showSecrets,
d.outputContext,
Expand Down
36 changes: 19 additions & 17 deletions cmd/revision.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,16 @@ import (
)

type revision struct {
release string
client helm.Interface
detailedExitCode bool
suppressedKinds []string
revisions []string
outputContext int
includeTests bool
showSecrets bool
output string
release string
client helm.Interface
detailedExitCode bool
suppressedKinds []string
revisions []string
outputContext int
includeTests bool
showSecrets bool
output string
normalizeManifests bool
}

const revisionCmdLongUsage = `
Expand Down Expand Up @@ -89,6 +90,7 @@ func revisionCmd() *cobra.Command {
revisionCmd.Flags().IntVarP(&diff.outputContext, "context", "C", -1, "output NUM lines of context around changes")
revisionCmd.Flags().BoolVar(&diff.includeTests, "include-tests", false, "enable the diffing of the helm test hooks")
revisionCmd.Flags().StringVar(&diff.output, "output", "diff", "Possible values: diff, simple, template. When set to \"template\", use the env var HELM_DIFF_TPL to specify the template.")
revisionCmd.Flags().BoolVar(&diff.normalizeManifests, "normalize-manifests", false, "normalize manifests before running diff to exclude style differences from the output")

revisionCmd.SuggestionsMinimumDistance = 1

Expand Down Expand Up @@ -120,8 +122,8 @@ func (d *revision) differentiateHelm3() error {
}

diff.Manifests(
manifest.Parse(string(revisionResponse), namespace, excludes...),
manifest.Parse(string(releaseResponse), namespace, excludes...),
manifest.Parse(string(revisionResponse), namespace, d.normalizeManifests, excludes...),
manifest.Parse(string(releaseResponse), namespace, d.normalizeManifests, excludes...),
d.suppressedKinds,
d.showSecrets,
d.outputContext,
Expand All @@ -146,8 +148,8 @@ func (d *revision) differentiateHelm3() error {
}

seenAnyChanges := diff.Manifests(
manifest.Parse(string(revisionResponse1), namespace, excludes...),
manifest.Parse(string(revisionResponse2), namespace, excludes...),
manifest.Parse(string(revisionResponse1), namespace, d.normalizeManifests, excludes...),
manifest.Parse(string(revisionResponse2), namespace, d.normalizeManifests, excludes...),
d.suppressedKinds,
d.showSecrets,
d.outputContext,
Expand Down Expand Up @@ -185,8 +187,8 @@ func (d *revision) differentiate() error {
}

diff.Manifests(
manifest.ParseRelease(revisionResponse.Release, d.includeTests),
manifest.ParseRelease(releaseResponse.Release, d.includeTests),
manifest.ParseRelease(revisionResponse.Release, d.includeTests, d.normalizeManifests),
manifest.ParseRelease(releaseResponse.Release, d.includeTests, d.normalizeManifests),
d.suppressedKinds,
d.showSecrets,
d.outputContext,
Expand All @@ -211,8 +213,8 @@ func (d *revision) differentiate() error {
}

seenAnyChanges := diff.Manifests(
manifest.ParseRelease(revisionResponse1.Release, d.includeTests),
manifest.ParseRelease(revisionResponse2.Release, d.includeTests),
manifest.ParseRelease(revisionResponse1.Release, d.includeTests, d.normalizeManifests),
manifest.ParseRelease(revisionResponse2.Release, d.includeTests, d.normalizeManifests),
d.suppressedKinds,
d.showSecrets,
d.outputContext,
Expand Down
28 changes: 15 additions & 13 deletions cmd/rollback.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,16 @@ import (
)

type rollback struct {
release string
client helm.Interface
detailedExitCode bool
suppressedKinds []string
revisions []string
outputContext int
includeTests bool
showSecrets bool
output string
release string
client helm.Interface
detailedExitCode bool
suppressedKinds []string
revisions []string
outputContext int
includeTests bool
showSecrets bool
output string
normalizeManifests bool
}

const rollbackCmdLongUsage = `
Expand Down Expand Up @@ -81,6 +82,7 @@ func rollbackCmd() *cobra.Command {
rollbackCmd.Flags().IntVarP(&diff.outputContext, "context", "C", -1, "output NUM lines of context around changes")
rollbackCmd.Flags().BoolVar(&diff.includeTests, "include-tests", false, "enable the diffing of the helm test hooks")
rollbackCmd.Flags().StringVar(&diff.output, "output", "diff", "Possible values: diff, simple, template. When set to \"template\", use the env var HELM_DIFF_TPL to specify the template.")
rollbackCmd.Flags().BoolVar(&diff.normalizeManifests, "normalize-manifests", false, "normalize manifests before running diff to exclude style differences from the output")

rollbackCmd.SuggestionsMinimumDistance = 1

Expand Down Expand Up @@ -113,8 +115,8 @@ func (d *rollback) backcastHelm3() error {

// create a diff between the current manifest and the version of the manifest that a user is intended to rollback
seenAnyChanges := diff.Manifests(
manifest.Parse(string(releaseResponse), namespace, excludes...),
manifest.Parse(string(revisionResponse), namespace, excludes...),
manifest.Parse(string(releaseResponse), namespace, d.normalizeManifests, excludes...),
manifest.Parse(string(revisionResponse), namespace, d.normalizeManifests, excludes...),
d.suppressedKinds,
d.showSecrets,
d.outputContext,
Expand Down Expand Up @@ -149,8 +151,8 @@ func (d *rollback) backcast() error {

// create a diff between the current manifest and the version of the manifest that a user is intended to rollback
seenAnyChanges := diff.Manifests(
manifest.ParseRelease(releaseResponse.Release, d.includeTests),
manifest.ParseRelease(revisionResponse.Release, d.includeTests),
manifest.ParseRelease(releaseResponse.Release, d.includeTests, d.normalizeManifests),
manifest.ParseRelease(revisionResponse.Release, d.includeTests, d.normalizeManifests),
d.suppressedKinds,
d.showSecrets,
d.outputContext,
Expand Down
20 changes: 11 additions & 9 deletions cmd/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ type diffCmd struct {
postRenderer string
output string
install bool
normalizeManifests bool
}

func (d *diffCmd) isAllowUnreleased() bool {
Expand Down Expand Up @@ -117,6 +118,7 @@ func newChartCommand() *cobra.Command {
f.BoolVar(&diff.dryRun, "dry-run", false, "disables cluster access and show diff as if it was install. Implies --install, --reset-values, and --disable-validation")
f.StringVar(&diff.postRenderer, "post-renderer", "", "the path to an executable to be used for post rendering. If it exists in $PATH, the binary will be used, otherwise it will try to look for the executable at the given path")
f.StringVar(&diff.output, "output", "diff", "Possible values: diff, simple, json, template. When set to \"template\", use the env var HELM_DIFF_TPL to specify the template.")
f.BoolVar(&diff.normalizeManifests, "normalize-manifests", false, "normalize manifests before running diff to exclude style differences from the output")
if !isHelm3() {
f.StringVar(&diff.namespace, "namespace", "default", "namespace to assume the release to be installed into")
}
Expand Down Expand Up @@ -173,16 +175,16 @@ func (d *diffCmd) runHelm3() error {
releaseManifest = append(releaseManifest, hooks...)
}
if d.includeTests {
currentSpecs = manifest.Parse(string(releaseManifest), d.namespace)
currentSpecs = manifest.Parse(string(releaseManifest), d.namespace, d.normalizeManifests)
} else {
currentSpecs = manifest.Parse(string(releaseManifest), d.namespace, helm3TestHook, helm2TestSuccessHook)
currentSpecs = manifest.Parse(string(releaseManifest), d.namespace, d.normalizeManifests, helm3TestHook, helm2TestSuccessHook)
}
}
var newSpecs map[string]*manifest.MappingResult
if d.includeTests {
newSpecs = manifest.Parse(string(installManifest), d.namespace)
newSpecs = manifest.Parse(string(installManifest), d.namespace, d.normalizeManifests)
} else {
newSpecs = manifest.Parse(string(installManifest), d.namespace, helm3TestHook, helm2TestSuccessHook)
newSpecs = manifest.Parse(string(installManifest), d.namespace, d.normalizeManifests, helm3TestHook, helm2TestSuccessHook)
}
seenAnyChanges := diff.Manifests(currentSpecs, newSpecs, d.suppressedKinds, d.showSecrets, d.outputContext, d.output, os.Stdout)

Expand Down Expand Up @@ -247,7 +249,7 @@ func (d *diffCmd) run() error {
}

currentSpecs = make(map[string]*manifest.MappingResult)
newSpecs = manifest.Parse(installResponse.Release.Manifest, installResponse.Release.Namespace)
newSpecs = manifest.Parse(installResponse.Release.Manifest, installResponse.Release.Namespace, d.normalizeManifests)
} else {
upgradeResponse, err := d.client.UpdateRelease(
d.release,
Expand All @@ -262,11 +264,11 @@ func (d *diffCmd) run() error {
}

if d.noHooks {
currentSpecs = manifest.Parse(releaseResponse.Release.Manifest, releaseResponse.Release.Namespace)
newSpecs = manifest.Parse(upgradeResponse.Release.Manifest, upgradeResponse.Release.Namespace)
currentSpecs = manifest.Parse(releaseResponse.Release.Manifest, releaseResponse.Release.Namespace, d.normalizeManifests)
newSpecs = manifest.Parse(upgradeResponse.Release.Manifest, upgradeResponse.Release.Namespace, d.normalizeManifests)
} else {
currentSpecs = manifest.ParseRelease(releaseResponse.Release, d.includeTests)
newSpecs = manifest.ParseRelease(upgradeResponse.Release, d.includeTests)
currentSpecs = manifest.ParseRelease(releaseResponse.Release, d.includeTests, d.normalizeManifests)
newSpecs = manifest.ParseRelease(upgradeResponse.Release, d.includeTests, d.normalizeManifests)
}
}

Expand Down
35 changes: 20 additions & 15 deletions manifest/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func splitSpec(token string) (string, string) {
}

// ParseRelease parses release objects into MappingResult
func ParseRelease(release *release.Release, includeTests bool) map[string]*MappingResult {
func ParseRelease(release *release.Release, includeTests bool, normalizeManifests bool) map[string]*MappingResult {
manifest := release.Manifest
for _, hook := range release.Hooks {
if !includeTests && isTestHook(hook.Events) {
Expand All @@ -79,11 +79,11 @@ func ParseRelease(release *release.Release, includeTests bool) map[string]*Mappi
manifest += fmt.Sprintf("# Source: %s\n", hook.Path)
manifest += hook.Manifest
}
return Parse(manifest, release.Namespace)
return Parse(manifest, release.Namespace, normalizeManifests)
}

// Parse parses manifest strings into MappingResult
func Parse(manifest string, defaultNamespace string, excludedHooks ...string) map[string]*MappingResult {
func Parse(manifest string, defaultNamespace string, normalizeManifests bool, excludedHooks ...string) map[string]*MappingResult {
// Ensure we have a newline in front of the yaml seperator
scanner := bufio.NewScanner(strings.NewReader("\n" + manifest))
scanner.Split(scanYamlSpecs)
Expand All @@ -100,7 +100,7 @@ func Parse(manifest string, defaultNamespace string, excludedHooks ...string) ma
continue
}

parsed, err := parseContent(content, defaultNamespace, excludedHooks...)
parsed, err := parseContent(content, defaultNamespace, normalizeManifests, excludedHooks...)
if err != nil {
log.Fatalf("%v", err)
}
Expand All @@ -121,7 +121,7 @@ func Parse(manifest string, defaultNamespace string, excludedHooks ...string) ma
return result
}

func parseContent(content string, defaultNamespace string, excludedHooks ...string) ([]*MappingResult, error) {
func parseContent(content string, defaultNamespace string, normalizeManifests bool, excludedHooks ...string) ([]*MappingResult, error) {
var parsedMetadata metadata
if err := yaml.Unmarshal([]byte(content), &parsedMetadata); err != nil {
log.Fatalf("YAML unmarshal error: %s\nCan't unmarshal %s", err, content)
Expand All @@ -135,7 +135,7 @@ func parseContent(content string, defaultNamespace string, excludedHooks ...stri

if parsedMetadata.Kind == "List" {
type ListV1 struct {
Items []map[interface{}]interface{} `yaml:"items"`
Items []yaml.MapSlice `yaml:"items"`
}

var list ListV1
Expand All @@ -152,7 +152,7 @@ func parseContent(content string, defaultNamespace string, excludedHooks ...stri
log.Printf("YAML marshal error: %s\nCan't marshal %v", err, item)
}

subs, err := parseContent(string(subcontent), defaultNamespace, excludedHooks...)
subs, err := parseContent(string(subcontent), defaultNamespace, normalizeManifests, excludedHooks...)
if err != nil {
return nil, fmt.Errorf("Parsing YAML list item: %v", err)
}
Expand All @@ -163,15 +163,20 @@ func parseContent(content string, defaultNamespace string, excludedHooks ...stri
return result, nil
}

var object map[interface{}]interface{}
if err := yaml.Unmarshal([]byte(content), &object); err != nil {
log.Fatalf("YAML unmarshal error: %s\nCan't unmarshal %s", err, content)
}
normalizedContent, err := yaml.Marshal(object)
if err != nil {
log.Fatalf("YAML marshal error: %s\nCan't marshal %v", err, object)
if normalizeManifests {
// Unmarshal and marshal again content to normalize yaml structure
// This avoids style differences to show up as diffs but it can
// make the output different from the original template (since it is in normalized form)
var object map[interface{}]interface{}
if err := yaml.Unmarshal([]byte(content), &object); err != nil {
log.Fatalf("YAML unmarshal error: %s\nCan't unmarshal %s", err, content)
}
normalizedContent, err := yaml.Marshal(object)
if err != nil {
log.Fatalf("YAML marshal error: %s\nCan't marshal %v", err, object)
}
content = string(normalizedContent)
}
content = string(normalizedContent)

if isHook(parsedMetadata, excludedHooks...) {
return nil, nil
Expand Down
Loading

0 comments on commit cbb60e1

Please sign in to comment.