diff --git a/kmeta/labels.go b/kmeta/labels.go index e4cd1163eb..f9a72d8bf3 100644 --- a/kmeta/labels.go +++ b/kmeta/labels.go @@ -26,6 +26,13 @@ import ( // The methods in this file are used for managing subresources in cases where // a controller instantiates different resources for each version of itself. +// There are two sets of methods available here: +// * `*VersionLabel*`: these methods act on `metadata.resourceVersion` and +// create new labels for EVERY change to the resource (incl. `/status`). +// * `*GenerationLabel*`: these methods act on `metadata.generation` and +// create new labels for changes to the resource's "spec" (typically, but +// some K8s resources change `metadata.generation` for annotations as well +// e.g. Deployment). // // For example, if an A might instantiate N B's at version 1 and M B's at // version 2 then it can use MakeVersionLabels to decorate each subresource @@ -66,6 +73,37 @@ func MakeOldVersionLabelSelector(om metav1.ObjectMetaAccessor) labels.Selector { ) } +// MakeGenerationLabels constructs a set of labels to apply to subresources +// instantiated at this version of the parent resource, so that we can +// efficiently select them. +func MakeGenerationLabels(om metav1.ObjectMetaAccessor) labels.Set { + return map[string]string{ + "controller": string(om.GetObjectMeta().GetUID()), + "generation": genStr(om), + } +} + +// MakeGenerationLabelSelector constructs a selector for subresources +// instantiated at this version of the parent resource. This keys +// off of the labels populated by MakeGenerationLabels. +func MakeGenerationLabelSelector(om metav1.ObjectMetaAccessor) labels.Selector { + return labels.SelectorFromSet(MakeGenerationLabels(om)) +} + +// MakeOldGenerationLabelSelector constructs a selector for subresources +// instantiated at an older version of the parent resource. This keys +// off of the labels populated by MakeGenerationLabels. +func MakeOldGenerationLabelSelector(om metav1.ObjectMetaAccessor) labels.Selector { + return labels.NewSelector().Add( + mustNewRequirement("controller", selection.Equals, []string{string(om.GetObjectMeta().GetUID())}), + mustNewRequirement("generation", selection.NotEquals, []string{genStr(om)}), + ) +} + +func genStr(om metav1.ObjectMetaAccessor) string { + return fmt.Sprintf("%05d", om.GetObjectMeta().GetGeneration()) +} + // mustNewRequirement panics if there are any errors constructing our selectors. func mustNewRequirement(key string, op selection.Operator, vals []string) labels.Requirement { r, err := labels.NewRequirement(key, op, vals) diff --git a/kmeta/labels_test.go b/kmeta/labels_test.go index eff6961809..47c11c88ac 100644 --- a/kmeta/labels_test.go +++ b/kmeta/labels_test.go @@ -114,3 +114,96 @@ func TestMakeOldVersionLabelSelector(t *testing.T) { }) } } + +func TestMakeGenerationLabels(t *testing.T) { + tests := []struct { + name string + om metav1.ObjectMeta + s string + }{{ + name: "simple translation", + om: metav1.ObjectMeta{ + UID: "1234", + Generation: 5, + }, + s: "controller=1234,generation=00005", + }, { + name: "another simple translation", + om: metav1.ObjectMeta{ + UID: "abcd", + Generation: 5432, + }, + s: "controller=abcd,generation=05432", + }} + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + ls := MakeGenerationLabels(&test.om) + if want, got := test.s, ls.String(); got != want { + t.Errorf("MakeGenerationLabels() = %v, wanted %v", got, want) + } + }) + } +} + +func TestMakeGenerationLabelSelector(t *testing.T) { + tests := []struct { + name string + om metav1.ObjectMeta + s string + }{{ + name: "simple translation", + om: metav1.ObjectMeta{ + UID: "1234", + Generation: 5, + }, + s: "controller=1234,generation=00005", + }, { + name: "another simple translation", + om: metav1.ObjectMeta{ + UID: "abcd", + Generation: 5432, + }, + s: "controller=abcd,generation=05432", + }} + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + ls := MakeGenerationLabelSelector(&test.om) + if want, got := test.s, ls.String(); got != want { + t.Errorf("MakeGenerationLabelSelector() = %v, wanted %v", got, want) + } + }) + } +} + +func TestMakeOldGenerationLabelSelector(t *testing.T) { + tests := []struct { + name string + om metav1.ObjectMeta + s string + }{{ + name: "simple translation", + om: metav1.ObjectMeta{ + UID: "1234", + Generation: 5, + }, + s: "controller=1234,generation!=00005", + }, { + name: "another simple translation", + om: metav1.ObjectMeta{ + UID: "abcd", + Generation: 5432, + }, + s: "controller=abcd,generation!=05432", + }} + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + ls := MakeOldGenerationLabelSelector(&test.om) + if want, got := test.s, ls.String(); got != want { + t.Errorf("MakeOldGenerationLabelSelector() = %v, wanted %v", got, want) + } + }) + } +}