Skip to content
Merged
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
28 changes: 28 additions & 0 deletions metautils/metautils.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@
package metautils

import (
"fmt"
"reflect"
"strings"

"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
)

Expand Down Expand Up @@ -68,3 +71,28 @@ func ListElementType(list runtime.Object) (reflect.Type, error) {
v := reflect.ValueOf(itemsPtr)
return v.Type().Elem().Elem(), nil
}

// IsControlledBy checks if controlled is controlled by owner.
// An object is considered to be controlled if there is a controller (via metav1.GetControllerOf) whose
// GVK, name and UID match with the controller object.
func IsControlledBy(scheme *runtime.Scheme, owner, controlled client.Object) (bool, error) {
controller := metav1.GetControllerOf(controlled)
if controller == nil {
return false, nil
}

gvk, err := apiutil.GVKForObject(owner, scheme)
if err != nil {
return false, fmt.Errorf("error getting object kinds of owner: %w", err)
}

gv, err := schema.ParseGroupVersion(controller.APIVersion)
if err != nil {
return false, fmt.Errorf("could not parse controller api version: %w", err)
}

return gvk.GroupVersion() == gv &&
controller.Kind == gvk.Kind &&
controller.Name == owner.GetName() &&
controller.UID == owner.GetUID(), nil
}
89 changes: 89 additions & 0 deletions metautils/metautils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,12 @@ import (
. "github.com/onsi/gomega"
appsv1 "k8s.io/api/apps/v1"
corev1 "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"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/kubernetes/scheme"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)

var _ = Describe("Metautils", func() {
Expand Down Expand Up @@ -81,4 +84,90 @@ var _ = Describe("Metautils", func() {
)).To(HaveOccurred())
})
})

Describe("IsControlledBy", func() {
It("should report true if the object is controlled by another", func() {
By("making a controlling object")
owner := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Namespace: corev1.NamespaceDefault,
Name: "owner",
UID: types.UID("owner-uuid"),
},
}

By("making an object to be controlled")
owned := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Namespace: corev1.NamespaceDefault,
Name: "owned",
UID: types.UID("owned-uuid"),
},
}

By("setting the controller reference")
Expect(controllerutil.SetControllerReference(owner, owned, scheme.Scheme)).To(Succeed())

By("asserting the object reports as controlled")
Expect(IsControlledBy(scheme.Scheme, owner, owned)).To(BeTrue(), "object should be controlled by owner, object: %#v, owner: %#v", owned, owner)
})

It("should report false if the object is not controlled by another", func() {
By("making two regular objects")
obj1 := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Namespace: corev1.NamespaceDefault,
Name: "obj1",
UID: types.UID("obj1-uuid"),
},
}
obj2 := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Namespace: corev1.NamespaceDefault,
Name: "obj2",
UID: types.UID("obj2-uuid"),
},
}

By("asserting the object does not report as controlled")
Expect(IsControlledBy(scheme.Scheme, obj1, obj2)).To(BeFalse(), "object should not be controlled, obj1: %#v, obj2: %#v", obj1, obj2)
})

It("should error if it cannot determine the gvk of an object", func() {
By("creating an object whose type is not registered in the default scheme")
obj1 := &struct{ corev1.ConfigMap }{
ConfigMap: corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Namespace: corev1.NamespaceDefault,
Name: "obj1",
UID: types.UID("obj1-uuid"),
},
},
}

By("making a controlling object")
owner := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Namespace: corev1.NamespaceDefault,
Name: "owner",
UID: types.UID("owner-uuid"),
},
}

By("making a regular object")
owned := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Namespace: corev1.NamespaceDefault,
Name: "owned",
UID: types.UID("owned-uuid"),
},
}

By("setting the controller for owned")
Expect(controllerutil.SetControllerReference(owner, owned, scheme.Scheme)).To(Succeed())

_, err := IsControlledBy(scheme.Scheme, obj1, owned)
Expect(err).To(HaveOccurred())
})
})
})