diff --git a/README.md b/README.md index 08a5db31..79be8cb2 100644 --- a/README.md +++ b/README.md @@ -51,18 +51,18 @@ The Helm Diff Plugin * Shows a diff explaining what a helm upgrade would change: This fetches the currently deployed version of a release - and compares it to a local chart plus values. This can be + and compares it to a local chart plus values. This can be used to visualize what changes a helm upgrade will perform. -* Shows a diff explaining what had changed between the two revisions: +* Shows a diff explaining what had changed between two revisions: This fetches previously deployed versions of a release - and compares them. This can be used to visualize what changes + and compares them. This can be used to visualize what changes were made during revision change. * Shows a diff explaining what a helm rollback would change: This fetches the currently deployed version of a release - and compares it to the previously deployed version of the release that you - want to rollback. This can be used to visualize what changes a + and compares it to the previously deployed version of the release, that you + want to rollback. This can be used to visualize what changes a helm rollback will perform. Usage: @@ -78,46 +78,52 @@ Available Commands: version Show version of the helm diff plugin Flags: - --allow-unreleased enables diffing of releases that are not yet deployed via Helm - -a, --api-versions stringArray Kubernetes api versions used for Capabilities.APIVersions - --color color output. You can control the value for this flag via HELM_DIFF_COLOR=[true|false]. If both --no-color and --color are unspecified, coloring enabled only when the stdout is a term and TERM is not "dumb" - -C, --context int output NUM lines of context around changes (default -1) - --detailed-exitcode return a non-zero exit code when there are changes - --devel use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored. - --disable-openapi-validation disables rendered templates validation against the Kubernetes OpenAPI Schema - --disable-validation disables rendered templates validation against the Kubernetes cluster you are currently pointing to. This is the same validation performed on an install - --dry-run disables cluster access and show diff as if it was install. Implies --install, --reset-values, and --disable-validation - --enable-dns enable DNS lookups when rendering templates - --skip-schema-validation disables rendered templates validation against the Kubernetes OpenAPI Schema - -D, --find-renames float32 Enable rename detection if set to any value greater than 0. If specified, the value denotes the maximum fraction of changed content as lines added + removed compared to total lines in a diff for considering it a rename. Only objects of the same Kind are attempted to be matched - -h, --help help for diff - --include-crds include CRDs in the diffing - --include-tests enable the diffing of the helm test hooks - --install enables diffing of releases that are not yet deployed via Helm (equivalent to --allow-unreleased, added to match "helm upgrade --install" command - --kube-version string Kubernetes version used for Capabilities.KubeVersion - --kubeconfig string This flag is ignored, to allow passing of this top level flag to helm - --no-color remove colors from the output. If both --no-color and --color are unspecified, coloring enabled only when the stdout is a term and TERM is not "dumb" - --no-hooks disable diffing of hooks - --normalize-manifests normalize manifests before running diff to exclude style differences from the output - --output string Possible values: diff, simple, template, dyff. When set to "template", use the env var HELM_DIFF_TPL to specify the template. (default "diff") - --post-renderer string 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 - --post-renderer-args stringArray an argument to the post-renderer (can specify multiple) - --repo string specify the chart repository url to locate the requested chart - --reset-values reset the values to the ones built into the chart and merge in any new values - --reuse-values reuse the last release's values and merge in any new values. If '--reset-values' is specified, this is ignored - --reset-then-reuse-values reset the values to the ones built into the chart, apply the last release's values and merge in any new values. If '--reset-values' or '--reuse-values' is specified, this is ignored - --set stringArray set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2) - --set-file stringArray set values from respective files specified via the command line (can specify multiple or separate values with commas: key1=path1,key2=path2) - --set-string stringArray set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2) - --show-secrets do not redact secret values in the output - --strip-trailing-cr strip trailing carriage return on input - --suppress stringArray allows suppression of the kinds listed in the diff output (can specify multiple, like '--suppress Deployment --suppress Service') - -q, --suppress-secrets suppress secrets in the output - --three-way-merge use three-way-merge to compute patch and generate diff output - -f, --values valueFiles specify values in a YAML file (can specify multiple) (default []) - --version string specify the exact chart version to use. If this is not specified, the latest version is used - -Additional help topics: + --allow-unreleased enables diffing of releases that are not yet deployed via Helm + -a, --api-versions stringArray Kubernetes api versions used for Capabilities.APIVersions + --color color output. You can control the value for this flag via HELM_DIFF_COLOR=[true|false]. If both --no-color and --color are unspecified, coloring enabled only when the stdout is a term and TERM is not "dumb" + -C, --context int output NUM lines of context around changes (default -1) + --detailed-exitcode return a non-zero exit code when there are changes + --devel use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored. + --disable-openapi-validation disables rendered templates validation against the Kubernetes OpenAPI Schema + --disable-validation disables rendered templates validation against the Kubernetes cluster you are currently pointing to. This is the same validation performed on an install + --dry-run string[="client"] --dry-run, --dry-run=client, or --dry-run=true disables cluster access and show diff as if it was install. Implies --install, --reset-values, and --disable-validation. --dry-run=server enables the cluster access with helm-get and the lookup template function. + --enable-dns enable DNS lookups when rendering templates + -D, --find-renames float32 Enable rename detection if set to any value greater than 0. If specified, the value denotes the maximum fraction of changed content as lines added + removed compared to total lines in a diff for considering it a rename. Only objects of the same Kind are attempted to be matched + -h, --help help for diff + --include-crds include CRDs in the diffing + --include-tests enable the diffing of the helm test hooks + --insecure-skip-tls-verify skip tls certificate checks for the chart download + --install enables diffing of releases that are not yet deployed via Helm (equivalent to --allow-unreleased, added to match "helm upgrade --install" command + --kube-version string Kubernetes version used for Capabilities.KubeVersion + --kubeconfig string This flag is ignored, to allow passing of this top level flag to helm + --no-color remove colors from the output. If both --no-color and --color are unspecified, coloring enabled only when the stdout is a term and TERM is not "dumb" + --no-hooks disable diffing of hooks + --normalize-manifests normalize manifests before running diff to exclude style differences from the output + --output string Possible values: diff, simple, template, dyff. When set to "template", use the env var HELM_DIFF_TPL to specify the template. (default "diff") + --post-renderer string 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 + --post-renderer-args stringArray an argument to the post-renderer (can specify multiple) + --repo string specify the chart repository url to locate the requested chart + --reset-then-reuse-values reset the values to the ones built into the chart, apply the last release's values and merge in any new values. If '--reset-values' or '--reuse-values' is specified, this is ignored + --reset-values reset the values to the ones built into the chart and merge in any new values + --reuse-values reuse the last release's values and merge in any new values. If '--reset-values' is specified, this is ignored + --set stringArray set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2) + --set-file stringArray set values from respective files specified via the command line (can specify multiple or separate values with commas: key1=path1,key2=path2) + --set-json stringArray set JSON values on the command line (can specify multiple or separate values with commas: key1=jsonval1,key2=jsonval2) + --set-literal stringArray set STRING literal values on the command line + --set-string stringArray set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2) + --show-secrets do not redact secret values in the output + --show-secrets-decoded decode secret values in the output + --skip-schema-validation skip validation of the rendered manifests against the Kubernetes OpenAPI schema + --strip-trailing-cr strip trailing carriage return on input + --suppress stringArray allows suppression of the kinds listed in the diff output (can specify multiple, like '--suppress Deployment --suppress Service') + --suppress-output-line-regex stringArray a regex to suppress diff output lines that match + -q, --suppress-secrets suppress secrets in the output + --take-ownership if set, upgrade will ignore the check for helm annotations and take ownership of the existing resources + --three-way-merge use three-way-merge to compute patch and generate diff output + -f, --values valueFiles specify values in a YAML file (can specify multiple) (default []) + --version string specify the exact chart version to use. If this is not specified, the latest version is used + +Additional help topcis: diff Use "diff [command] --help" for more information about a command. @@ -172,48 +178,54 @@ Examples: # Read the flag usage below for more information on --normalize-manifests. HELM_DIFF_NORMALIZE_MANIFESTS=true helm diff upgrade my-release datadog/datadog - # Set HELM_DIFF_OUTPUT_CONTEXT=n to configure the output context to n lines. - # This is equivalent to specifying the --context flag. - # Read the flag usage below for more information on --context. - HELM_DIFF_OUTPUT_CONTEXT=5 helm diff upgrade my-release datadog/datadog +# Set HELM_DIFF_OUTPUT_CONTEXT=n to configure the output context to n lines. +# This is equivalent to specifying the --context flag. +# Read the flag usage below for more information on --context. +HELM_DIFF_OUTPUT_CONTEXT=5 helm diff upgrade my-release datadog/datadog Flags: - --allow-unreleased enables diffing of releases that are not yet deployed via Helm - -a, --api-versions stringArray Kubernetes api versions used for Capabilities.APIVersions - -C, --context int output NUM lines of context around changes (default -1) - --detailed-exitcode return a non-zero exit code when there are changes - --devel use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored. - --disable-openapi-validation disables rendered templates validation against the Kubernetes OpenAPI Schema - --disable-validation disables rendered templates validation against the Kubernetes cluster you are currently pointing to. This is the same validation performed on an install - --dry-run disables cluster access and show diff as if it was install. Implies --install, --reset-values, and --disable-validation - --enable-dns enable DNS lookups when rendering templates - --skip-schema-validation skip validation of rendered templates against the Kubernetes OpenAPI Schema - -D, --find-renames float32 Enable rename detection if set to any value greater than 0. If specified, the value denotes the maximum fraction of changed content as lines added + removed compared to total lines in a diff for considering it a rename. Only objects of the same Kind are attempted to be matched - -h, --help help for upgrade - --include-crds include CRDs in the diffing - --include-tests enable the diffing of the helm test hooks - --install enables diffing of releases that are not yet deployed via Helm (equivalent to --allow-unreleased, added to match "helm upgrade --install" command - --kube-version string Kubernetes version used for Capabilities.KubeVersion - --kubeconfig string This flag is ignored, to allow passing of this top level flag to helm - --no-hooks disable diffing of hooks - --normalize-manifests normalize manifests before running diff to exclude style differences from the output - --output string Possible values: diff, simple, template, dyff. When set to "template", use the env var HELM_DIFF_TPL to specify the template. (default "diff") - --post-renderer string 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 - --post-renderer-args stringArray an argument to the post-renderer (can specify multiple) - --repo string specify the chart repository url to locate the requested chart - --reset-values reset the values to the ones built into the chart and merge in any new values - --reuse-values reuse the last release's values and merge in any new values. If '--reset-values' is specified, this is ignored - --reset-then-reuse-values reset the values to the ones built into the chart, apply the last release's values and merge in any new values. If '--reset-values' or '--reuse-values' is specified, this is ignored - --set stringArray set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2) - --set-file stringArray set values from respective files specified via the command line (can specify multiple or separate values with commas: key1=path1,key2=path2) - --set-string stringArray set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2) - --show-secrets do not redact secret values in the output - --strip-trailing-cr strip trailing carriage return on input - --suppress stringArray allows suppression of the values listed in the diff output - -q, --suppress-secrets suppress secrets in the output - --three-way-merge use three-way-merge to compute patch and generate diff output - -f, --values valueFiles specify values in a YAML file (can specify multiple) (default []) - --version string specify the exact chart version to use. If this is not specified, the latest version is used + --allow-unreleased enables diffing of releases that are not yet deployed via Helm + -a, --api-versions stringArray Kubernetes api versions used for Capabilities.APIVersions + -C, --context int output NUM lines of context around changes (default -1) + --detailed-exitcode return a non-zero exit code when there are changes + --devel use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored. + --disable-openapi-validation disables rendered templates validation against the Kubernetes OpenAPI Schema + --disable-validation disables rendered templates validation against the Kubernetes cluster you are currently pointing to. This is the same validation performed on an install + --dry-run string[="client"] --dry-run, --dry-run=client, or --dry-run=true disables cluster access and show diff as if it was install. Implies --install, --reset-values, and --disable-validation. --dry-run=server enables the cluster access with helm-get and the lookup template function. + --enable-dns enable DNS lookups when rendering templates + -D, --find-renames float32 Enable rename detection if set to any value greater than 0. If specified, the value denotes the maximum fraction of changed content as lines added + removed compared to total lines in a diff for considering it a rename. Only objects of the same Kind are attempted to be matched + -h, --help help for upgrade + --include-crds include CRDs in the diffing + --include-tests enable the diffing of the helm test hooks + --insecure-skip-tls-verify skip tls certificate checks for the chart download + --install enables diffing of releases that are not yet deployed via Helm (equivalent to --allow-unreleased, added to match "helm upgrade --install" command + --kube-version string Kubernetes version used for Capabilities.KubeVersion + --kubeconfig string This flag is ignored, to allow passing of this top level flag to helm + --no-hooks disable diffing of hooks + --normalize-manifests normalize manifests before running diff to exclude style differences from the output + --output string Possible values: diff, simple, template, dyff. When set to "template", use the env var HELM_DIFF_TPL to specify the template. (default "diff") + --post-renderer string 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 + --post-renderer-args stringArray an argument to the post-renderer (can specify multiple) + --repo string specify the chart repository url to locate the requested chart + --reset-then-reuse-values reset the values to the ones built into the chart, apply the last release's values and merge in any new values. If '--reset-values' or '--reuse-values' is specified, this is ignored + --reset-values reset the values to the ones built into the chart and merge in any new values + --reuse-values reuse the last release's values and merge in any new values. If '--reset-values' is specified, this is ignored + --set stringArray set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2) + --set-file stringArray set values from respective files specified via the command line (can specify multiple or separate values with commas: key1=path1,key2=path2) + --set-json stringArray set JSON values on the command line (can specify multiple or separate values with commas: key1=jsonval1,key2=jsonval2) + --set-literal stringArray set STRING literal values on the command line + --set-string stringArray set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2) + --show-secrets do not redact secret values in the output + --show-secrets-decoded decode secret values in the output + --skip-schema-validation skip validation of the rendered manifests against the Kubernetes OpenAPI schema + --strip-trailing-cr strip trailing carriage return on input + --suppress stringArray allows suppression of the kinds listed in the diff output (can specify multiple, like '--suppress Deployment --suppress Service') + --suppress-output-line-regex stringArray a regex to suppress diff output lines that match + -q, --suppress-secrets suppress secrets in the output + --take-ownership if set, upgrade will ignore the check for helm annotations and take ownership of the existing resources + --three-way-merge use three-way-merge to compute patch and generate diff output + -f, --values valueFiles specify values in a YAML file (can specify multiple) (default []) + --version string specify the exact chart version to use. If this is not specified, the latest version is used Global Flags: --color color output. You can control the value for this flag via HELM_DIFF_COLOR=[true|false]. If both --no-color and --color are unspecified, coloring enabled only when the stdout is a term and TERM is not "dumb" @@ -225,30 +237,33 @@ Global Flags: ``` $ helm diff release -h -This command compares the manifests details of a different releases created from the same chart +This command compares the manifests details of a different releases created from the same chart. +The release name may be specified using namespace/release syntax. It can be used to compare the manifests of - release1 with release2 - $ helm diff release [flags] release1 release2 + $ helm diff release [flags] release1 release2 Example: - $ helm diff release my-prod my-stage + $ helm diff release my-prod my-stage + $ helm diff release prod/my-prod stage/my-stage Usage: diff release [flags] RELEASE release1 [release2] Flags: - -C, --context int output NUM lines of context around changes (default -1) - --detailed-exitcode return a non-zero exit code when there are changes - -D, --find-renames float32 Enable rename detection if set to any value greater than 0. If specified, the value denotes the maximum fraction of changed content as lines added + removed compared to total lines in a diff for considering it a rename. Only objects of the same Kind are attempted to be matched - -h, --help help for release - --include-tests enable the diffing of the helm test hooks - --normalize-manifests normalize manifests before running diff to exclude style differences from the output - --output string Possible values: diff, simple, template, dyff. When set to "template", use the env var HELM_DIFF_TPL to specify the template. (default "diff") - --show-secrets do not redact secret values in the output - --strip-trailing-cr strip trailing carriage return on input - --suppress stringArray allows suppression of the values listed in the diff output - -q, --suppress-secrets suppress secrets in the output + -C, --context int output NUM lines of context around changes (default -1) + --detailed-exitcode return a non-zero exit code when there are changes + -D, --find-renames float32 Enable rename detection if set to any value greater than 0. If specified, the value denotes the maximum fraction of changed content as lines added + removed compared to total lines in a diff for considering it a rename. Only objects of the same Kind are attempted to be matched + -h, --help help for release + --include-tests enable the diffing of the helm test hooks + --normalize-manifests normalize manifests before running diff to exclude style differences from the output + --output string Possible values: diff, simple, template, dyff. When set to "template", use the env var HELM_DIFF_TPL to specify the template. (default "diff") + --show-secrets do not redact secret values in the output + --strip-trailing-cr strip trailing carriage return on input + --suppress stringArray allows suppression of the kinds listed in the diff output (can specify multiple, like '--suppress Deployment --suppress Service') + --suppress-output-line-regex stringArray a regex to suppress diff output lines that match + -q, --suppress-secrets suppress secrets in the output Global Flags: --color color output. You can control the value for this flag via HELM_DIFF_COLOR=[true|false]. If both --no-color and --color are unspecified, coloring enabled only when the stdout is a term and TERM is not "dumb" @@ -265,30 +280,33 @@ This command compares the manifests details of a named release. It can be used to compare the manifests of - latest REVISION with specified REVISION - $ helm diff revision [flags] RELEASE REVISION1 + $ helm diff revision [flags] RELEASE REVISION1 Example: - $ helm diff revision my-release 2 + $ helm diff revision my-release 2 - REVISION1 with REVISION2 - $ helm diff revision [flags] RELEASE REVISION1 REVISION2 + $ helm diff revision [flags] RELEASE REVISION1 REVISION2 Example: - $ helm diff revision my-release 2 3 + $ helm diff revision my-release 2 3 Usage: diff revision [flags] RELEASE REVISION1 [REVISION2] Flags: - -C, --context int output NUM lines of context around changes (default -1) - --detailed-exitcode return a non-zero exit code when there are changes - -D, --find-renames float32 Enable rename detection if set to any value greater than 0. If specified, the value denotes the maximum fraction of changed content as lines added + removed compared to total lines in a diff for considering it a rename. Only objects of the same Kind are attempted to be matched - -h, --help help for revision - --include-tests enable the diffing of the helm test hooks - --normalize-manifests normalize manifests before running diff to exclude style differences from the output - --output string Possible values: diff, simple, template, dyff. When set to "template", use the env var HELM_DIFF_TPL to specify the template. (default "diff") - --show-secrets do not redact secret values in the output - --strip-trailing-cr strip trailing carriage return on input - --suppress stringArray allows suppression of the values listed in the diff output - -q, --suppress-secrets suppress secrets in the output + -C, --context int output NUM lines of context around changes (default -1) + --show-secrets-decoded decode secret values in the output + --detailed-exitcode return a non-zero exit code when there are changes + -D, --find-renames float32 Enable rename detection if set to any value greater than 0. If specified, the value denotes the maximum fraction of changed content as lines added + removed compared to total lines in a diff for considering it a rename. Only objects of the same Kind are attempted to be matched + -h, --help help for revision + --include-tests enable the diffing of the helm test hooks + --normalize-manifests normalize manifests before running diff to exclude style differences from the output + --output string Possible values: diff, simple, template, dyff. When set to "template", use the env var HELM_DIFF_TPL to specify the template. (default "diff") + --show-secrets do not redact secret values in the output + --show-secrets-decoded decode secret values in the output + --strip-trailing-cr strip trailing carriage return on input + --suppress stringArray allows suppression of the kinds listed in the diff output (can specify multiple, like '--suppress Deployment --suppress Service') + --suppress-output-line-regex stringArray a regex to suppress diff output lines that match + -q, --suppress-secrets suppress secrets in the output Global Flags: --color color output. You can control the value for this flag via HELM_DIFF_COLOR=[true|false]. If both --no-color and --color are unspecified, coloring enabled only when the stdout is a term and TERM is not "dumb" @@ -312,17 +330,19 @@ Examples: helm diff rollback my-release 2 Flags: - -C, --context int output NUM lines of context around changes (default -1) - --detailed-exitcode return a non-zero exit code when there are changes - -D, --find-renames float32 Enable rename detection if set to any value greater than 0. If specified, the value denotes the maximum fraction of changed content as lines added + removed compared to total lines in a diff for considering it a rename. Only objects of the same Kind are attempted to be matched - -h, --help help for rollback - --include-tests enable the diffing of the helm test hooks - --normalize-manifests normalize manifests before running diff to exclude style differences from the output - --output string Possible values: diff, simple, template, dyff. When set to "template", use the env var HELM_DIFF_TPL to specify the template. (default "diff") - --show-secrets do not redact secret values in the output - --strip-trailing-cr strip trailing carriage return on input - --suppress stringArray allows suppression of the values listed in the diff output - -q, --suppress-secrets suppress secrets in the output + -C, --context int output NUM lines of context around changes (default -1) + --detailed-exitcode return a non-zero exit code when there are changes + -D, --find-renames float32 Enable rename detection if set to any value greater than 0. If specified, the value denotes the maximum fraction of changed content as lines added + removed compared to total lines in a diff for considering it a rename. Only objects of the same Kind are attempted to be matched + -h, --help help for rollback + --include-tests enable the diffing of the helm test hooks + --normalize-manifests normalize manifests before running diff to exclude style differences from the output + --output string Possible values: diff, simple, template, dyff. When set to "template", use the env var HELM_DIFF_TPL to specify the template. (default "diff") + --show-secrets do not redact secret values in the output + --show-secrets-decoded decode secret values in the output + --strip-trailing-cr strip trailing carriage return on input + --suppress stringArray allows suppression of the kinds listed in the diff output (can specify multiple, like '--suppress Deployment --suppress Service') + --suppress-output-line-regex stringArray a regex to suppress diff output lines that match + -q, --suppress-secrets suppress secrets in the output Global Flags: --color color output. You can control the value for this flag via HELM_DIFF_COLOR=[true|false]. If both --no-color and --color are unspecified, coloring enabled only when the stdout is a term and TERM is not "dumb" diff --git a/cmd/options.go b/cmd/options.go index 57e16fb1..502b73d7 100644 --- a/cmd/options.go +++ b/cmd/options.go @@ -10,6 +10,7 @@ import ( func AddDiffOptions(f *pflag.FlagSet, o *diff.Options) { f.BoolP("suppress-secrets", "q", false, "suppress secrets in the output") f.BoolVar(&o.ShowSecrets, "show-secrets", false, "do not redact secret values in the output") + f.BoolVar(&o.ShowSecretsDecoded, "show-secrets-decoded", false, "decode secret values in the output") f.StringArrayVar(&o.SuppressedKinds, "suppress", []string{}, "allows suppression of the kinds listed in the diff output (can specify multiple, like '--suppress Deployment --suppress Service')") f.IntVarP(&o.OutputContext, "context", "C", -1, "output NUM lines of context around changes") f.StringVar(&o.OutputFormat, "output", "diff", "Possible values: diff, simple, template, dyff. When set to \"template\", use the env var HELM_DIFF_TPL to specify the template.") diff --git a/diff/diff.go b/diff/diff.go index 1a6d6bc6..0934e8eb 100644 --- a/diff/diff.go +++ b/diff/diff.go @@ -25,6 +25,7 @@ type Options struct { OutputContext int StripTrailingCR bool ShowSecrets bool + ShowSecretsDecoded bool SuppressedKinds []string FindRenames float32 SuppressedOutputLineRegex []string @@ -179,7 +180,10 @@ func contentSearch(report *Report, possiblyRemoved []string, oldIndex map[string continue } - if !options.ShowSecrets { + switch { + case options.ShowSecretsDecoded: + decodeSecrets(oldContent, newContent) + case !options.ShowSecrets: redactSecrets(oldContent, newContent) } @@ -212,8 +216,10 @@ func doDiff(report *Report, key string, oldContent *manifest.MappingResult, newC if oldContent != nil && newContent != nil && oldContent.Content == newContent.Content { return } - - if !options.ShowSecrets { + switch { + case options.ShowSecretsDecoded: + decodeSecrets(oldContent, newContent) + case !options.ShowSecrets: redactSecrets(oldContent, newContent) } @@ -233,7 +239,9 @@ func doDiff(report *Report, key string, oldContent *manifest.MappingResult, newC } } +// redactSecrets redacts secrets from the diff output. func redactSecrets(old, new *manifest.MappingResult) { + var oldSecretDecodeErr, newSecretDecodeErr error if (old != nil && old.Kind != "Secret") || (new != nil && new.Kind != "Secret") { return } @@ -242,33 +250,37 @@ func redactSecrets(old, new *manifest.MappingResult) { var oldSecret, newSecret v1.Secret if old != nil { - if err := yaml.NewYAMLToJSONDecoder(bytes.NewBufferString(old.Content)).Decode(&oldSecret); err != nil { - old.Content = fmt.Sprintf("Error parsing old secret: %s", err) - } - //if we have a Secret containing `stringData`, apply the same - //transformation that the apiserver would do with it (this protects - //stringData keys from being overwritten down below) - if len(oldSecret.StringData) > 0 && oldSecret.Data == nil { - oldSecret.Data = make(map[string][]byte, len(oldSecret.StringData)) - } - for k, v := range oldSecret.StringData { - oldSecret.Data[k] = []byte(v) + oldSecretDecodeErr = yaml.NewYAMLToJSONDecoder(bytes.NewBufferString(old.Content)).Decode(&oldSecret) + if oldSecretDecodeErr != nil { + old.Content = fmt.Sprintf("Error parsing old secret: %s", oldSecretDecodeErr) + } else { + //if we have a Secret containing `stringData`, apply the same + //transformation that the apiserver would do with it (this protects + //stringData keys from being overwritten down below) + if len(oldSecret.StringData) > 0 && oldSecret.Data == nil { + oldSecret.Data = make(map[string][]byte, len(oldSecret.StringData)) + } + for k, v := range oldSecret.StringData { + oldSecret.Data[k] = []byte(v) + } } } if new != nil { - if err := yaml.NewYAMLToJSONDecoder(bytes.NewBufferString(new.Content)).Decode(&newSecret); err != nil { - new.Content = fmt.Sprintf("Error parsing new secret: %s", err) - } - //same as above - if len(newSecret.StringData) > 0 && newSecret.Data == nil { - newSecret.Data = make(map[string][]byte, len(newSecret.StringData)) - } - for k, v := range newSecret.StringData { - newSecret.Data[k] = []byte(v) + newSecretDecodeErr = yaml.NewYAMLToJSONDecoder(bytes.NewBufferString(new.Content)).Decode(&newSecret) + if newSecretDecodeErr != nil { + new.Content = fmt.Sprintf("Error parsing new secret: %s", newSecretDecodeErr) + } else { + //same as above + if len(newSecret.StringData) > 0 && newSecret.Data == nil { + newSecret.Data = make(map[string][]byte, len(newSecret.StringData)) + } + for k, v := range newSecret.StringData { + newSecret.Data[k] = []byte(v) + } } } - if old != nil { + if old != nil && oldSecretDecodeErr == nil { oldSecret.StringData = make(map[string]string, len(oldSecret.Data)) for k, v := range oldSecret.Data { if new != nil && bytes.Equal(v, newSecret.Data[k]) { @@ -278,7 +290,7 @@ func redactSecrets(old, new *manifest.MappingResult) { } } } - if new != nil { + if new != nil && newSecretDecodeErr == nil { newSecret.StringData = make(map[string]string, len(newSecret.Data)) for k, v := range newSecret.Data { if old != nil && bytes.Equal(v, oldSecret.Data[k]) { @@ -290,21 +302,81 @@ func redactSecrets(old, new *manifest.MappingResult) { } // remove Data field now that we are using StringData for serialization - var buf bytes.Buffer - if old != nil { + if old != nil && oldSecretDecodeErr == nil { + oldSecretBuf := bytes.NewBuffer(nil) oldSecret.Data = nil - if err := serializer.Encode(&oldSecret, &buf); err != nil { + if err := serializer.Encode(&oldSecret, oldSecretBuf); err != nil { new.Content = fmt.Sprintf("Error encoding new secret: %s", err) } - old.Content = getComment(old.Content) + strings.Replace(strings.Replace(buf.String(), "stringData", "data", 1), " creationTimestamp: null\n", "", 1) - buf.Reset() //reuse buffer for new secret + old.Content = getComment(old.Content) + strings.Replace(strings.Replace(oldSecretBuf.String(), "stringData", "data", 1), " creationTimestamp: null\n", "", 1) + oldSecretBuf.Reset() + } + if new != nil && newSecretDecodeErr == nil { + newSecretBuf := bytes.NewBuffer(nil) + newSecret.Data = nil + if err := serializer.Encode(&newSecret, newSecretBuf); err != nil { + new.Content = fmt.Sprintf("Error encoding new secret: %s", err) + } + new.Content = getComment(new.Content) + strings.Replace(strings.Replace(newSecretBuf.String(), "stringData", "data", 1), " creationTimestamp: null\n", "", 1) + newSecretBuf.Reset() + } +} + +// decodeSecrets decodes secrets from the diff output. +func decodeSecrets(old, new *manifest.MappingResult) { + var oldSecretDecodeErr, newSecretDecodeErr error + if (old != nil && old.Kind != "Secret") || (new != nil && new.Kind != "Secret") { + return + } + serializer := json.NewYAMLSerializer(json.DefaultMetaFactory, scheme.Scheme, + scheme.Scheme) + var oldSecret, newSecret v1.Secret + + if old != nil { + oldSecretDecodeErr = yaml.NewYAMLToJSONDecoder(bytes.NewBufferString(old.Content)).Decode(&oldSecret) + if oldSecretDecodeErr != nil { + old.Content = fmt.Sprintf("Error parsing old secret: %s", oldSecretDecodeErr) + } else { + if len(oldSecret.Data) > 0 && oldSecret.StringData == nil { + oldSecret.StringData = make(map[string]string, len(oldSecret.Data)) + } + for k, v := range oldSecret.Data { + oldSecret.StringData[k] = string(v) + } + } } if new != nil { + newSecretDecodeErr = yaml.NewYAMLToJSONDecoder(bytes.NewBufferString(new.Content)).Decode(&newSecret) + if newSecretDecodeErr != nil { + new.Content = fmt.Sprintf("Error parsing new secret: %s", newSecretDecodeErr) + } else { + if len(newSecret.Data) > 0 && newSecret.StringData == nil { + newSecret.StringData = make(map[string]string, len(newSecret.StringData)) + } + for k, v := range newSecret.Data { + newSecret.StringData[k] = string(v) + } + } + } + + // remove Data field now that we are using StringData for serialization + if old != nil && oldSecretDecodeErr == nil { + oldSecretBuf := bytes.NewBuffer(nil) + oldSecret.Data = nil + if err := serializer.Encode(&oldSecret, oldSecretBuf); err != nil { + new.Content = fmt.Sprintf("Error encoding new secret: %s", err) + } + old.Content = getComment(old.Content) + strings.Replace(oldSecretBuf.String(), " creationTimestamp: null\n", "", 1) + oldSecretBuf.Reset() + } + if new != nil && newSecretDecodeErr == nil { + newSecretBuf := bytes.NewBuffer(nil) newSecret.Data = nil - if err := serializer.Encode(&newSecret, &buf); err != nil { + if err := serializer.Encode(&newSecret, newSecretBuf); err != nil { new.Content = fmt.Sprintf("Error encoding new secret: %s", err) } - new.Content = getComment(new.Content) + strings.Replace(strings.Replace(buf.String(), "stringData", "data", 1), " creationTimestamp: null\n", "", 1) + new.Content = getComment(new.Content) + strings.Replace(newSecretBuf.String(), " creationTimestamp: null\n", "", 1) + newSecretBuf.Reset() } } diff --git a/diff/diff_test.go b/diff/diff_test.go index f2c832e7..bbafa8a2 100644 --- a/diff/diff_test.go +++ b/diff/diff_test.go @@ -277,7 +277,7 @@ annotations: t.Run("OnChange", func(t *testing.T) { var buf1 bytes.Buffer - diffOptions := Options{"diff", 10, false, true, []string{}, 0.0, []string{}} + diffOptions := Options{"diff", 10, false, true, false, []string{}, 0.0, []string{}} if changesSeen := Manifests(specBeta, specRelease, &diffOptions, &buf1); !changesSeen { t.Error("Unexpected return value from Manifests: Expected the return value to be `true` to indicate that it has seen any change(s), but was `false`") @@ -296,7 +296,7 @@ annotations: t.Run("OnChangeWithSuppress", func(t *testing.T) { var buf1 bytes.Buffer - diffOptions := Options{"diff", 10, false, true, []string{}, 0.0, []string{"apiVersion"}} + diffOptions := Options{"diff", 10, false, true, false, []string{}, 0.0, []string{"apiVersion"}} if changesSeen := Manifests(specBeta, specReleaseSpec, &diffOptions, &buf1); !changesSeen { t.Error("Unexpected return value from Manifests: Expected the return value to be `true` to indicate that it has seen any change(s), but was `false`") @@ -315,7 +315,7 @@ annotations: t.Run("OnChangeWithSuppressAll", func(t *testing.T) { var buf1 bytes.Buffer - diffOptions := Options{"diff", 10, false, true, []string{}, 0.0, []string{"apiVersion"}} + diffOptions := Options{"diff", 10, false, true, false, []string{}, 0.0, []string{"apiVersion"}} if changesSeen := Manifests(specBeta, specRelease, &diffOptions, &buf1); !changesSeen { t.Error("Unexpected return value from Manifests: Expected the return value to be `true` to indicate that it has seen any change(s), but was `false`") @@ -327,7 +327,7 @@ annotations: t.Run("OnChangeRename", func(t *testing.T) { var buf1 bytes.Buffer - diffOptions := Options{"diff", 10, false, true, []string{}, 0.5, []string{}} + diffOptions := Options{"diff", 10, false, true, false, []string{}, 0.5, []string{}} if changesSeen := Manifests(specReleaseSpec, specReleaseRenamed, &diffOptions, &buf1); !changesSeen { t.Error("Unexpected return value from Manifests: Expected the return value to be `true` to indicate that it has seen any change(s), but was `false`") @@ -348,7 +348,7 @@ annotations: t.Run("OnChangeRenameAndUpdate", func(t *testing.T) { var buf1 bytes.Buffer - diffOptions := Options{"diff", 10, false, true, []string{}, 0.5, []string{}} + diffOptions := Options{"diff", 10, false, true, false, []string{}, 0.5, []string{}} if changesSeen := Manifests(specReleaseSpec, specReleaseRenamedAndUpdated, &diffOptions, &buf1); !changesSeen { t.Error("Unexpected return value from Manifests: Expected the return value to be `true` to indicate that it has seen any change(s), but was `false`") @@ -370,7 +370,7 @@ annotations: t.Run("OnChangeRenameAndAdded", func(t *testing.T) { var buf1 bytes.Buffer - diffOptions := Options{"diff", 10, false, true, []string{}, 0.5, []string{}} + diffOptions := Options{"diff", 10, false, true, false, []string{}, 0.5, []string{}} if changesSeen := Manifests(specReleaseSpec, specReleaseRenamedAndAdded, &diffOptions, &buf1); !changesSeen { t.Error("Unexpected return value from Manifests: Expected the return value to be `true` to indicate that it has seen any change(s), but was `false`") @@ -394,7 +394,7 @@ annotations: t.Run("OnChangeRenameAndAddedWithPartialSuppress", func(t *testing.T) { var buf1 bytes.Buffer - diffOptions := Options{"diff", 10, false, true, []string{}, 0.5, []string{"app: "}} + diffOptions := Options{"diff", 10, false, true, false, []string{}, 0.5, []string{"app: "}} if changesSeen := Manifests(specReleaseSpec, specReleaseRenamedAndAdded, &diffOptions, &buf1); !changesSeen { t.Error("Unexpected return value from Manifests: Expected the return value to be `true` to indicate that it has seen any change(s), but was `false`") @@ -417,7 +417,7 @@ annotations: t.Run("OnChangeRenameAndRemoved", func(t *testing.T) { var buf1 bytes.Buffer - diffOptions := Options{"diff", 10, false, true, []string{}, 0.5, []string{}} + diffOptions := Options{"diff", 10, false, true, false, []string{}, 0.5, []string{}} if changesSeen := Manifests(specReleaseRenamedAndAdded, specReleaseSpec, &diffOptions, &buf1); !changesSeen { t.Error("Unexpected return value from Manifests: Expected the return value to be `true` to indicate that it has seen any change(s), but was `false`") @@ -441,7 +441,7 @@ annotations: t.Run("OnChangeRenameAndRemovedWithPartialSuppress", func(t *testing.T) { var buf1 bytes.Buffer - diffOptions := Options{"diff", 10, false, true, []string{}, 0.5, []string{"app: "}} + diffOptions := Options{"diff", 10, false, true, false, []string{}, 0.5, []string{"app: "}} if changesSeen := Manifests(specReleaseRenamedAndAdded, specReleaseSpec, &diffOptions, &buf1); !changesSeen { t.Error("Unexpected return value from Manifests: Expected the return value to be `true` to indicate that it has seen any change(s), but was `false`") @@ -464,7 +464,7 @@ annotations: t.Run("OnNoChange", func(t *testing.T) { var buf2 bytes.Buffer - diffOptions := Options{"diff", 10, false, true, []string{}, 0.0, []string{}} + diffOptions := Options{"diff", 10, false, true, false, []string{}, 0.0, []string{}} if changesSeen := Manifests(specRelease, specRelease, &diffOptions, &buf2); changesSeen { t.Error("Unexpected return value from Manifests: Expected the return value to be `false` to indicate that it has NOT seen any change(s), but was `true`") @@ -475,7 +475,7 @@ annotations: t.Run("OnChangeRemoved", func(t *testing.T) { var buf1 bytes.Buffer - diffOptions := Options{"diff", 10, false, true, []string{}, 0.5, []string{}} + diffOptions := Options{"diff", 10, false, true, false, []string{}, 0.5, []string{}} if changesSeen := Manifests(specRelease, nil, &diffOptions, &buf1); !changesSeen { t.Error("Unexpected return value from Manifests: Expected the return value to be `true` to indicate that it has seen any change(s), but was `false`") @@ -493,7 +493,7 @@ annotations: t.Run("OnChangeRemovedWithResourcePolicyKeep", func(t *testing.T) { var buf2 bytes.Buffer - diffOptions := Options{"diff", 10, false, true, []string{}, 0.0, []string{}} + diffOptions := Options{"diff", 10, false, true, false, []string{}, 0.0, []string{}} if changesSeen := Manifests(specReleaseKeep, nil, &diffOptions, &buf2); changesSeen { t.Error("Unexpected return value from Manifests: Expected the return value to be `false` to indicate that it has NOT seen any change(s), but was `true`") @@ -504,7 +504,7 @@ annotations: t.Run("OnChangeSimple", func(t *testing.T) { var buf1 bytes.Buffer - diffOptions := Options{"simple", 10, false, true, []string{}, 0.0, []string{}} + diffOptions := Options{"simple", 10, false, true, false, []string{}, 0.0, []string{}} if changesSeen := Manifests(specBeta, specRelease, &diffOptions, &buf1); !changesSeen { t.Error("Unexpected return value from Manifests: Expected the return value to be `true` to indicate that it has seen any change(s), but was `false`") @@ -517,7 +517,7 @@ Plan: 0 to add, 1 to change, 0 to destroy, 0 to change ownership. t.Run("OnNoChangeSimple", func(t *testing.T) { var buf2 bytes.Buffer - diffOptions := Options{"simple", 10, false, true, []string{}, 0.0, []string{}} + diffOptions := Options{"simple", 10, false, true, false, []string{}, 0.0, []string{}} if changesSeen := Manifests(specRelease, specRelease, &diffOptions, &buf2); changesSeen { t.Error("Unexpected return value from Manifests: Expected the return value to be `false` to indicate that it has NOT seen any change(s), but was `true`") } @@ -527,7 +527,7 @@ Plan: 0 to add, 1 to change, 0 to destroy, 0 to change ownership. t.Run("OnChangeTemplate", func(t *testing.T) { var buf1 bytes.Buffer - diffOptions := Options{"template", 10, false, true, []string{}, 0.0, []string{}} + diffOptions := Options{"template", 10, false, true, false, []string{}, 0.0, []string{}} if changesSeen := Manifests(specBeta, specRelease, &diffOptions, &buf1); !changesSeen { t.Error("Unexpected return value from Manifests: Expected the return value to be `true` to indicate that it has seen any change(s), but was `false`") @@ -545,7 +545,7 @@ Plan: 0 to add, 1 to change, 0 to destroy, 0 to change ownership. t.Run("OnChangeJSON", func(t *testing.T) { var buf1 bytes.Buffer - diffOptions := Options{"json", 10, false, true, []string{}, 0.0, []string{}} + diffOptions := Options{"json", 10, false, true, false, []string{}, 0.0, []string{}} if changesSeen := Manifests(specBeta, specRelease, &diffOptions, &buf1); !changesSeen { t.Error("Unexpected return value from Manifests: Expected the return value to be `true` to indicate that it has seen any change(s), but was `false`") @@ -563,7 +563,7 @@ Plan: 0 to add, 1 to change, 0 to destroy, 0 to change ownership. t.Run("OnNoChangeTemplate", func(t *testing.T) { var buf2 bytes.Buffer - diffOptions := Options{"template", 10, false, true, []string{}, 0.0, []string{}} + diffOptions := Options{"template", 10, false, true, false, []string{}, 0.0, []string{}} if changesSeen := Manifests(specRelease, specRelease, &diffOptions, &buf2); changesSeen { t.Error("Unexpected return value from Manifests: Expected the return value to be `false` to indicate that it has NOT seen any change(s), but was `true`") @@ -575,7 +575,7 @@ Plan: 0 to add, 1 to change, 0 to destroy, 0 to change ownership. t.Run("OnChangeCustomTemplate", func(t *testing.T) { var buf1 bytes.Buffer os.Setenv("HELM_DIFF_TPL", "testdata/customTemplate.tpl") - diffOptions := Options{"template", 10, false, true, []string{}, 0.0, []string{}} + diffOptions := Options{"template", 10, false, true, false, []string{}, 0.0, []string{}} if changesSeen := Manifests(specBeta, specRelease, &diffOptions, &buf1); !changesSeen { t.Error("Unexpected return value from Manifests: Expected the return value to be `false` to indicate that it has NOT seen any change(s), but was `true`") @@ -658,7 +658,7 @@ stringData: t.Run("OnChangeSecretWithByteData", func(t *testing.T) { var buf1 bytes.Buffer - diffOptions := Options{"diff", 10, false, false, []string{}, 0.5, []string{}} //NOTE: ShowSecrets = false + diffOptions := Options{"diff", 10, false, false, false, []string{}, 0.5, []string{}} //NOTE: ShowSecrets = false if changesSeen := Manifests(specSecretWithByteData, specSecretWithByteDataChanged, &diffOptions, &buf1); !changesSeen { t.Error("Unexpected return value from Manifests: Expected the return value to be `true` to indicate that it has seen any change(s), but was `false`") @@ -683,7 +683,7 @@ stringData: t.Run("OnChangeSecretWithStringData", func(t *testing.T) { var buf1 bytes.Buffer - diffOptions := Options{"diff", 10, false, false, []string{}, 0.5, []string{}} //NOTE: ShowSecrets = false + diffOptions := Options{"diff", 10, false, false, false, []string{}, 0.5, []string{}} //NOTE: ShowSecrets = false if changesSeen := Manifests(specSecretWithStringData, specSecretWithStringDataChanged, &diffOptions, &buf1); !changesSeen { t.Error("Unexpected return value from Manifests: Expected the return value to be `true` to indicate that it has seen any change(s), but was `false`") @@ -807,7 +807,7 @@ data: t.Run("OnChangeOwnershipWithoutSpecChange", func(t *testing.T) { var buf1 bytes.Buffer - diffOptions := Options{"diff", 10, false, true, []string{}, 0.5, []string{}} //NOTE: ShowSecrets = false + diffOptions := Options{"diff", 10, false, true, false, []string{}, 0.5, []string{}} //NOTE: ShowSecrets = false newOwnedReleases := map[string]OwnershipDiff{ "default, foobar, ConfigMap (v1)": { @@ -827,7 +827,7 @@ data: t.Run("OnChangeOwnershipWithSpecChange", func(t *testing.T) { var buf1 bytes.Buffer - diffOptions := Options{"diff", 10, false, true, []string{}, 0.5, []string{}} //NOTE: ShowSecrets = false + diffOptions := Options{"diff", 10, false, true, false, []string{}, 0.5, []string{}} //NOTE: ShowSecrets = false specNew := map[string]*manifest.MappingResult{ "default, foobar, ConfigMap (v1)": { @@ -869,3 +869,284 @@ default, foobar, ConfigMap (v1) has changed: `, buf1.String()) }) } +func TestDecodeSecrets(t *testing.T) { + ansi.DisableColors(true) + + t.Run("decodeSecrets with valid base64 data", func(t *testing.T) { + old := &manifest.MappingResult{ + Name: "default, foo, Secret (v1)", + Kind: "Secret", + Content: ` +apiVersion: v1 +kind: Secret +metadata: + name: foo +type: Opaque +data: + key1: dmFsdWUx + key2: dmFsdWUy +`, + } + new := &manifest.MappingResult{ + Name: "default, foo, Secret (v1)", + Kind: "Secret", + Content: ` +apiVersion: v1 +kind: Secret +metadata: + name: foo +type: Opaque +data: + key1: bmV3dmFsdWUx + key2: dmFsdWUy +`, + } + decodeSecrets(old, new) + require.Contains(t, old.Content, "key1: value1") + require.Contains(t, old.Content, "key2: value2") + require.Contains(t, new.Content, "key1: newvalue1") + require.Contains(t, new.Content, "key2: value2") + }) + + t.Run("decodeSecrets with stringData", func(t *testing.T) { + old := &manifest.MappingResult{ + Name: "default, foo, Secret (v1)", + Kind: "Secret", + Content: ` +apiVersion: v1 +kind: Secret +metadata: + name: foo +type: Opaque +stringData: + key1: value1 + key2: value2 +`, + } + new := &manifest.MappingResult{ + Name: "default, foo, Secret (v1)", + Kind: "Secret", + Content: ` +apiVersion: v1 +kind: Secret +metadata: + name: foo +type: Opaque +stringData: + key1: value1changed + key2: value2 +`, + } + decodeSecrets(old, new) + require.Contains(t, old.Content, "key1: value1") + require.Contains(t, old.Content, "key2: value2") + require.Contains(t, new.Content, "key1: value1changed") + require.Contains(t, new.Content, "key2: value2") + }) + + t.Run("decodeSecrets with invalid base64", func(t *testing.T) { + old := &manifest.MappingResult{ + Name: "default, foo, Secret (v1)", + Kind: "Secret", + Content: ` +apiVersion: v1 +kind: Secret +metadata: + name: foo +type: Opaque +data: + key1: invalidbase64 +`, + } + new := &manifest.MappingResult{ + Name: "default, foo, Secret (v1)", + Kind: "Secret", + Content: ` +apiVersion: v1 +kind: Secret +metadata: + name: foo +type: Opaque +data: + key1: dmFsdWUx +`, + } + decodeSecrets(old, new) + require.Contains(t, old.Content, "Error parsing old secret") + require.Contains(t, new.Content, "key1: value1") + }) + + t.Run("decodeSecrets with non-Secret kind", func(t *testing.T) { + old := &manifest.MappingResult{ + Name: "default, foo, ConfigMap (v1)", + Kind: "ConfigMap", + Content: "apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: foo\n", + } + new := &manifest.MappingResult{ + Name: "default, foo, ConfigMap (v1)", + Kind: "ConfigMap", + Content: "apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: foo\n", + } + origOld := old.Content + origNew := new.Content + decodeSecrets(old, new) + require.Equal(t, origOld, old.Content) + require.Equal(t, origNew, new.Content) + }) + + t.Run("decodeSecrets with nil arguments", func(t *testing.T) { + // Should not panic or change anything + decodeSecrets(nil, nil) + }) +} + +func TestRedactSecrets(t *testing.T) { + ansi.DisableColors(true) + + t.Run("redactSecrets with valid base64 data", func(t *testing.T) { + old := &manifest.MappingResult{ + Name: "default, foo, Secret (v1)", + Kind: "Secret", + Content: ` +apiVersion: v1 +kind: Secret +metadata: + name: foo +type: Opaque +data: + key1: dmFsdWUx + key2: dmFsdWUy +`, + } + new := &manifest.MappingResult{ + Name: "default, foo, Secret (v1)", + Kind: "Secret", + Content: ` +apiVersion: v1 +kind: Secret +metadata: + name: foo +type: Opaque +data: + key1: bmV3dmFsdWUx + key2: dmFsdWUy +`, + } + redactSecrets(old, new) + require.Contains(t, old.Content, "key1: '-------- # (6 bytes)'") + require.Contains(t, old.Content, "key2: 'REDACTED # (6 bytes)'") + require.Contains(t, new.Content, "key1: '++++++++ # (9 bytes)'") + require.Contains(t, new.Content, "key2: 'REDACTED # (6 bytes)'") + }) + + t.Run("redactSecrets with stringData", func(t *testing.T) { + old := &manifest.MappingResult{ + Name: "default, foo, Secret (v1)", + Kind: "Secret", + Content: ` +apiVersion: v1 +kind: Secret +metadata: + name: foo +type: Opaque +stringData: + key1: value1 + key2: value2 +`, + } + new := &manifest.MappingResult{ + Name: "default, foo, Secret (v1)", + Kind: "Secret", + Content: ` +apiVersion: v1 +kind: Secret +metadata: + name: foo +type: Opaque +stringData: + key1: value1changed + key2: value2 +`, + } + redactSecrets(old, new) + require.Contains(t, old.Content, "key1: '-------- # (6 bytes)'") + require.Contains(t, old.Content, "key2: 'REDACTED # (6 bytes)'") + require.Contains(t, new.Content, "key1: '++++++++ # (13 bytes)'") + require.Contains(t, new.Content, "key2: 'REDACTED # (6 bytes)'") + }) + + t.Run("redactSecrets with nil arguments", func(t *testing.T) { + // Should not panic or change anything + redactSecrets(nil, nil) + }) + + t.Run("redactSecrets with non-Secret kind", func(t *testing.T) { + old := &manifest.MappingResult{ + Name: "default, foo, ConfigMap (v1)", + Kind: "ConfigMap", + Content: "apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: foo\n", + } + new := &manifest.MappingResult{ + Name: "default, foo, ConfigMap (v1)", + Kind: "ConfigMap", + Content: "apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: foo\n", + } + origOld := old.Content + origNew := new.Content + redactSecrets(old, new) + require.Equal(t, origOld, old.Content) + require.Equal(t, origNew, new.Content) + }) + + t.Run("redactSecrets with invalid YAML", func(t *testing.T) { + old := &manifest.MappingResult{ + Name: "default, foo, Secret (v1)", + Kind: "Secret", + Content: "invalid: yaml: :::", + } + new := &manifest.MappingResult{ + Name: "default, foo, Secret (v1)", + Kind: "Secret", + Content: "invalid: yaml: :::", + } + redactSecrets(old, new) + require.Contains(t, old.Content, "Error parsing old secret") + require.Contains(t, new.Content, "Error parsing new secret") + }) + + t.Run("redactSecrets with only old secret", func(t *testing.T) { + old := &manifest.MappingResult{ + Name: "default, foo, Secret (v1)", + Kind: "Secret", + Content: ` +apiVersion: v1 +kind: Secret +metadata: + name: foo +type: Opaque +data: + key1: dmFsdWUx +`, + } + redactSecrets(old, nil) + require.Contains(t, old.Content, "key1: '-------- # (6 bytes)'") + }) + + t.Run("redactSecrets with only new secret", func(t *testing.T) { + new := &manifest.MappingResult{ + Name: "default, foo, Secret (v1)", + Kind: "Secret", + Content: ` +apiVersion: v1 +kind: Secret +metadata: + name: foo +type: Opaque +data: + key1: dmFsdWUx +`, + } + redactSecrets(nil, new) + require.Contains(t, new.Content, "key1: '++++++++ # (6 bytes)'") + }) +}