Skip to content

Commit eef84ae

Browse files
authored
Implement metautils.IsControlledBy (#61)
1 parent 3a2218e commit eef84ae

File tree

2 files changed

+117
-0
lines changed

2 files changed

+117
-0
lines changed

metautils/metautils.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,15 @@
1616
package metautils
1717

1818
import (
19+
"fmt"
1920
"reflect"
2021
"strings"
2122

2223
"k8s.io/apimachinery/pkg/api/meta"
24+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2325
"k8s.io/apimachinery/pkg/runtime"
2426
"k8s.io/apimachinery/pkg/runtime/schema"
27+
"sigs.k8s.io/controller-runtime/pkg/client"
2528
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
2629
)
2730

@@ -68,3 +71,28 @@ func ListElementType(list runtime.Object) (reflect.Type, error) {
6871
v := reflect.ValueOf(itemsPtr)
6972
return v.Type().Elem().Elem(), nil
7073
}
74+
75+
// IsControlledBy checks if controlled is controlled by owner.
76+
// An object is considered to be controlled if there is a controller (via metav1.GetControllerOf) whose
77+
// GVK, name and UID match with the controller object.
78+
func IsControlledBy(scheme *runtime.Scheme, owner, controlled client.Object) (bool, error) {
79+
controller := metav1.GetControllerOf(controlled)
80+
if controller == nil {
81+
return false, nil
82+
}
83+
84+
gvk, err := apiutil.GVKForObject(owner, scheme)
85+
if err != nil {
86+
return false, fmt.Errorf("error getting object kinds of owner: %w", err)
87+
}
88+
89+
gv, err := schema.ParseGroupVersion(controller.APIVersion)
90+
if err != nil {
91+
return false, fmt.Errorf("could not parse controller api version: %w", err)
92+
}
93+
94+
return gvk.GroupVersion() == gv &&
95+
controller.Kind == gvk.Kind &&
96+
controller.Name == owner.GetName() &&
97+
controller.UID == owner.GetUID(), nil
98+
}

metautils/metautils_test.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,12 @@ import (
2222
. "github.com/onsi/gomega"
2323
appsv1 "k8s.io/api/apps/v1"
2424
corev1 "k8s.io/api/core/v1"
25+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2526
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2627
"k8s.io/apimachinery/pkg/runtime"
28+
"k8s.io/apimachinery/pkg/types"
2729
"k8s.io/client-go/kubernetes/scheme"
30+
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
2831
)
2932

3033
var _ = Describe("Metautils", func() {
@@ -81,4 +84,90 @@ var _ = Describe("Metautils", func() {
8184
)).To(HaveOccurred())
8285
})
8386
})
87+
88+
Describe("IsControlledBy", func() {
89+
It("should report true if the object is controlled by another", func() {
90+
By("making a controlling object")
91+
owner := &corev1.ConfigMap{
92+
ObjectMeta: metav1.ObjectMeta{
93+
Namespace: corev1.NamespaceDefault,
94+
Name: "owner",
95+
UID: types.UID("owner-uuid"),
96+
},
97+
}
98+
99+
By("making an object to be controlled")
100+
owned := &corev1.ConfigMap{
101+
ObjectMeta: metav1.ObjectMeta{
102+
Namespace: corev1.NamespaceDefault,
103+
Name: "owned",
104+
UID: types.UID("owned-uuid"),
105+
},
106+
}
107+
108+
By("setting the controller reference")
109+
Expect(controllerutil.SetControllerReference(owner, owned, scheme.Scheme)).To(Succeed())
110+
111+
By("asserting the object reports as controlled")
112+
Expect(IsControlledBy(scheme.Scheme, owner, owned)).To(BeTrue(), "object should be controlled by owner, object: %#v, owner: %#v", owned, owner)
113+
})
114+
115+
It("should report false if the object is not controlled by another", func() {
116+
By("making two regular objects")
117+
obj1 := &corev1.ConfigMap{
118+
ObjectMeta: metav1.ObjectMeta{
119+
Namespace: corev1.NamespaceDefault,
120+
Name: "obj1",
121+
UID: types.UID("obj1-uuid"),
122+
},
123+
}
124+
obj2 := &corev1.ConfigMap{
125+
ObjectMeta: metav1.ObjectMeta{
126+
Namespace: corev1.NamespaceDefault,
127+
Name: "obj2",
128+
UID: types.UID("obj2-uuid"),
129+
},
130+
}
131+
132+
By("asserting the object does not report as controlled")
133+
Expect(IsControlledBy(scheme.Scheme, obj1, obj2)).To(BeFalse(), "object should not be controlled, obj1: %#v, obj2: %#v", obj1, obj2)
134+
})
135+
136+
It("should error if it cannot determine the gvk of an object", func() {
137+
By("creating an object whose type is not registered in the default scheme")
138+
obj1 := &struct{ corev1.ConfigMap }{
139+
ConfigMap: corev1.ConfigMap{
140+
ObjectMeta: metav1.ObjectMeta{
141+
Namespace: corev1.NamespaceDefault,
142+
Name: "obj1",
143+
UID: types.UID("obj1-uuid"),
144+
},
145+
},
146+
}
147+
148+
By("making a controlling object")
149+
owner := &corev1.ConfigMap{
150+
ObjectMeta: metav1.ObjectMeta{
151+
Namespace: corev1.NamespaceDefault,
152+
Name: "owner",
153+
UID: types.UID("owner-uuid"),
154+
},
155+
}
156+
157+
By("making a regular object")
158+
owned := &corev1.ConfigMap{
159+
ObjectMeta: metav1.ObjectMeta{
160+
Namespace: corev1.NamespaceDefault,
161+
Name: "owned",
162+
UID: types.UID("owned-uuid"),
163+
},
164+
}
165+
166+
By("setting the controller for owned")
167+
Expect(controllerutil.SetControllerReference(owner, owned, scheme.Scheme)).To(Succeed())
168+
169+
_, err := IsControlledBy(scheme.Scheme, obj1, owned)
170+
Expect(err).To(HaveOccurred())
171+
})
172+
})
84173
})

0 commit comments

Comments
 (0)