Create a kmeta package for libraries acting on ObjectMeta. (#67)

This bootstraps this package with a useful collection of label methods for managing sub-resources.  I'd initially written this as a way of managing versioned DaemonSet resources as part of WarmImage ([see here](62cad8045a/pkg/reconciler/warmimage/resources/meta.go (L28))).

I am upstreaming this here because I want to take advantage of it in Build for managing Image cache subresources of [Cluster]BuildTemplate resources.
This commit is contained in:
Matt Moore 2018-09-13 10:45:16 -07:00 committed by Knative Prow Robot
parent 8fc80deb20
commit 5f353e5de8
4 changed files with 216 additions and 0 deletions

5
kmeta/OWNERS Normal file
View File

@ -0,0 +1,5 @@
# The OWNERS file is used by prow to automatically merge approved PRs.
approvers:
- mattmoor
- jonjohnsonjr

19
kmeta/doc.go Normal file
View File

@ -0,0 +1,19 @@
/*
Copyright 2018 The Knative Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package kmeta provides Knative utilities for operating on Kubernetes
// resources' ObjectMeta.
package kmeta

76
kmeta/labels.go Normal file
View File

@ -0,0 +1,76 @@
/*
Copyright 2018 The Knative Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package kmeta
import (
"fmt"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/selection"
)
// The methods in this file are used for managing subresources in cases where
// a controller instantiates different resources for each version of itself.
//
// 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
// with the appropriate labels for the version at which it was instantiated.
//
// During reconciliation, MakeVersionLabelSelector can be used with the
// informer listers to access the appropriate subresources for the current
// version of the parent resource.
//
// Likewise during reconciliation, MakeOldVersionLabelSelector can be used
// with the API client's DeleteCollection method to clean up subresources
// for older versions of the resource.
// MakeVersionLabels 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 MakeVersionLabels(om metav1.ObjectMeta) labels.Set {
return map[string]string{
"controller": string(om.UID),
"version": om.ResourceVersion,
}
}
// MakeVersionLabelSelector constructs a selector for subresources
// instantiated at this version of the parent resource. This keys
// off of the labels populated by MakeVersionLabels.
func MakeVersionLabelSelector(om metav1.ObjectMeta) labels.Selector {
return labels.SelectorFromSet(MakeVersionLabels(om))
}
// MakeOldVersionLabelSelector constructs a selector for subresources
// instantiated at an older version of the parent resource. This keys
// off of the labels populated by MakeVersionLabels.
func MakeOldVersionLabelSelector(om metav1.ObjectMeta) labels.Selector {
return labels.NewSelector().Add(
mustNewRequirement("controller", selection.Equals, []string{string(om.UID)}),
mustNewRequirement("version", selection.NotEquals, []string{om.ResourceVersion}),
)
}
// 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)
if err != nil {
panic(fmt.Sprintf("mustNewRequirement(%v, %v, %v) = %v", key, op, vals, err))
}
return *r
}

116
kmeta/labels_test.go Normal file
View File

@ -0,0 +1,116 @@
/*
Copyright 2018 The Knative Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package kmeta
import (
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func TestMakeVersionLabels(t *testing.T) {
tests := []struct {
name string
om metav1.ObjectMeta
s string
}{{
name: "simple translation",
om: metav1.ObjectMeta{
UID: "1234",
ResourceVersion: "abcd",
},
s: "controller=1234,version=abcd",
}, {
name: "another simple translation",
om: metav1.ObjectMeta{
UID: "abcd",
ResourceVersion: "1234",
},
s: "controller=abcd,version=1234",
}}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
ls := MakeVersionLabels(test.om)
if want, got := test.s, ls.String(); got != want {
t.Errorf("MakeVersionLabels() = %v, wanted %v", got, want)
}
})
}
}
func TestMakeVersionLabelSelector(t *testing.T) {
tests := []struct {
name string
om metav1.ObjectMeta
s string
}{{
name: "simple translation",
om: metav1.ObjectMeta{
UID: "1234",
ResourceVersion: "abcd",
},
s: "controller=1234,version=abcd",
}, {
name: "another simple translation",
om: metav1.ObjectMeta{
UID: "abcd",
ResourceVersion: "1234",
},
s: "controller=abcd,version=1234",
}}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
ls := MakeVersionLabelSelector(test.om)
if want, got := test.s, ls.String(); got != want {
t.Errorf("MakeVersionLabelSelector() = %v, wanted %v", got, want)
}
})
}
}
func TestMakeOldVersionLabelSelector(t *testing.T) {
tests := []struct {
name string
om metav1.ObjectMeta
s string
}{{
name: "simple translation",
om: metav1.ObjectMeta{
UID: "1234",
ResourceVersion: "abcd",
},
s: "controller=1234,version!=abcd",
}, {
name: "another simple translation",
om: metav1.ObjectMeta{
UID: "abcd",
ResourceVersion: "1234",
},
s: "controller=abcd,version!=1234",
}}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
ls := MakeOldVersionLabelSelector(test.om)
if want, got := test.s, ls.String(); got != want {
t.Errorf("MakeOldVersionLabelSelector() = %v, wanted %v", got, want)
}
})
}
}