Skip to content
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

feat: Allow suppressing annotations and labels #468

Closed
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
4 changes: 3 additions & 1 deletion cmd/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ 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.StringArrayVar(&o.SuppressedKinds, "suppress", []string{}, "allows suppression of the values listed in the diff output")
f.StringArrayVar(&o.SuppressedAnnotations, "suppress-annotation", []string{}, "allows suppression of the specified annotations in the diff output")
f.StringArrayVar(&o.SuppressedLabels, "suppress-label", []string{}, "allows suppression of the specified labels in the diff output")
f.StringArrayVar(&o.SuppressedKinds, "suppress", []string{}, "allows suppression of the kinds listed in the diff output")
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.")
f.BoolVar(&o.StripTrailingCR, "strip-trailing-cr", false, "strip trailing carriage return on input")
Expand Down
79 changes: 73 additions & 6 deletions diff/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
"github.com/aryann/difflib"
"github.com/mgutz/ansi"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/serializer/json"
"k8s.io/apimachinery/pkg/util/yaml"
"k8s.io/client-go/kubernetes/scheme"
Expand All @@ -20,12 +22,14 @@ import (

// Options are all the options to be passed to generate a diff
type Options struct {
OutputFormat string
OutputContext int
StripTrailingCR bool
ShowSecrets bool
SuppressedKinds []string
FindRenames float32
OutputFormat string
OutputContext int
StripTrailingCR bool
ShowSecrets bool
SuppressedAnnotations []string
SuppressedLabels []string
SuppressedKinds []string
FindRenames float32
}

// Manifests diff on manifests
Expand Down Expand Up @@ -101,6 +105,10 @@ func contentSearch(report *Report, possiblyRemoved []string, oldIndex map[string
redactSecrets(oldContent, newContent)
}

if len(options.SuppressedAnnotations) > 0 || len(options.SuppressedLabels) > 0 {
suppressAnnotationsAndLabels(oldContent, newContent, options)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Presuming all diffMappingResults calls need to be preceded by the new function calls, can we instead put this code inside diffMappingResults for maintainability, changing the third parameter of it to the options instead of a bool? 🐱

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry for the late answer, I have running code also handling nested labels/annotations in pod templates, but I think it quickly gets too specialized, having to handle all cases one by one (CronJob, Job, Deployment, StatefulSet, etc.)

I think having a render script hook before diff could make sense, that would make it possible to run a custom script deleting/sanitizing required fields.

I know there are post-renderer flags passed to helm, but these are only applied to the target helm template command.

WDYT?

}

diff := diffMappingResults(oldContent, newContent, options.StripTrailingCR)
delta := actualChanges(diff)
if delta == 0 || len(diff) == 0 {
Expand Down Expand Up @@ -135,6 +143,10 @@ func doDiff(report *Report, key string, oldContent *manifest.MappingResult, newC
redactSecrets(oldContent, newContent)
}

if len(options.SuppressedAnnotations) > 0 || len(options.SuppressedLabels) > 0 {
suppressAnnotationsAndLabels(oldContent, newContent, options)
}

if oldContent == nil {
emptyMapping := &manifest.MappingResult{}
diffs := diffMappingResults(emptyMapping, newContent, options.StripTrailingCR)
Expand All @@ -151,6 +163,61 @@ func doDiff(report *Report, key string, oldContent *manifest.MappingResult, newC
}
}

func suppressAnnotationsAndLabels(old, new *manifest.MappingResult, options *Options) {
serializer := json.NewYAMLSerializer(json.DefaultMetaFactory, scheme.Scheme,
scheme.Scheme)

if old != nil {
var object *unstructured.Unstructured
if err := yaml.NewYAMLToJSONDecoder(bytes.NewBufferString(old.Content)).Decode(&object); err != nil {
old.Content = fmt.Sprintf("Error parsing old object: %s", err)
}

suppressObjectAnnotationsAndLabels(object, options)

var buf bytes.Buffer
if err := serializer.Encode(object, &buf); err != nil {
new.Content = fmt.Sprintf("Error encoding old object: %s", err)
}
old.Content = getComment(old.Content) + buf.String()
}
if new != nil {
var object *unstructured.Unstructured
if err := yaml.NewYAMLToJSONDecoder(bytes.NewBufferString(new.Content)).Decode(&object); err != nil {
new.Content = fmt.Sprintf("Error parsing new object: %s", err)
}

suppressObjectAnnotationsAndLabels(object, options)

var buf bytes.Buffer
if err := serializer.Encode(object, &buf); err != nil {
new.Content = fmt.Sprintf("Error encoding new object: %s", err)
}
new.Content = getComment(new.Content) + buf.String()
}
}

func suppressObjectAnnotationsAndLabels(object metav1.Object, options *Options) {
if len(options.SuppressedAnnotations) > 0 {
annotations := object.GetAnnotations()
for _, annotation := range options.SuppressedAnnotations {
if _, ok := annotations[annotation]; ok {
delete(annotations, annotation)
}
}
object.SetAnnotations(annotations)
}
if len(options.SuppressedLabels) > 0 {
labels := object.GetLabels()
for _, label := range options.SuppressedLabels {
if _, ok := labels[label]; ok {
delete(labels, label)
}
}
object.SetLabels(labels)
}
}

func redactSecrets(old, new *manifest.MappingResult) {
if (old != nil && old.Kind != "Secret") || (new != nil && new.Kind != "Secret") {
return
Expand Down