Skip to content

Commit

Permalink
Add public functions to add/remove OwnerReferences without the refere…
Browse files Browse the repository at this point in the history
…nced object
  • Loading branch information
keeganwitt committed Oct 19, 2024
1 parent 685db56 commit cbf5989
Show file tree
Hide file tree
Showing 2 changed files with 237 additions and 0 deletions.
30 changes: 30 additions & 0 deletions pkg/controller/controllerutil/controllerutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,36 @@ func RemoveOwnerReference(owner, object metav1.Object, scheme *runtime.Scheme) e
return nil
}

// AppendOwnerReference is a helper method to make sure the given object contains a provided owner reference.
// This allows you to declare that owner has a dependency on the object without specifying it as a controller.
// If a reference already exists, it'll be overwritten with the newly provided version.
func AppendOwnerReference(owner metav1.OwnerReference, object metav1.Object) {
upsertOwnerRef(owner, object)
}

// DropOwnerReference is a helper method to make sure the given object removes a provided owner reference.
// This allows you to remove the owner to establish a new owner of the object in a subsequent call.
func DropOwnerReference(owner metav1.OwnerReference, object metav1.Object) error {
ownerRefs := object.GetOwnerReferences()
index := indexOwnerRef(ownerRefs, metav1.OwnerReference{
APIVersion: owner.APIVersion,
Name: owner.Name,
Kind: owner.Kind,
})

if index == -1 {
return fmt.Errorf("%T does not have an controller reference for %T", object, owner)
}

if ownerRefs[index].Controller == nil || !*ownerRefs[index].Controller {
return fmt.Errorf("%T owner is not the controller reference for %T", owner, object)
}

ownerRefs = append(ownerRefs[:index], ownerRefs[index+1:]...)
object.SetOwnerReferences(ownerRefs)
return nil
}

// HasControllerReference returns true if the object
// has an owner ref with controller equal to true
func HasControllerReference(object metav1.Object) bool {
Expand Down
207 changes: 207 additions & 0 deletions pkg/controller/controllerutil/controllerutil_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,213 @@ var _ = Describe("Controllerutil", func() {
})
})

var _ = Describe("Controllerutil", func() {
Describe("AppendOwnerReference", func() {
It("should set ownerRef on an empty list", func() {
rs := &appsv1.ReplicaSet{}
dep := metav1.OwnerReference{
APIVersion: "v1",
Kind: "Deployment",
Name: "foo",
UID: "foo-uid",
}
controllerutil.AppendOwnerReference(dep, rs)
Expect(rs.OwnerReferences).To(ConsistOf(metav1.OwnerReference{
Name: "foo",
Kind: "Deployment",
APIVersion: "extensions/v1beta1",
UID: "foo-uid",
}))
})

It("should not duplicate owner references", func() {
rs := &appsv1.ReplicaSet{
ObjectMeta: metav1.ObjectMeta{
OwnerReferences: []metav1.OwnerReference{
{
Name: "foo",
Kind: "Deployment",
APIVersion: "extensions/v1beta1",
UID: "foo-uid",
},
},
},
}
dep := metav1.OwnerReference{
APIVersion: "v1",
Kind: "Deployment",
Name: "foo",
UID: "foo-uid",
}

controllerutil.AppendOwnerReference(dep, rs)
Expect(rs.OwnerReferences).To(ConsistOf(metav1.OwnerReference{
Name: "foo",
Kind: "Deployment",
APIVersion: "extensions/v1beta1",
UID: "foo-uid",
}))
})

It("should update the reference", func() {
rs := &appsv1.ReplicaSet{
ObjectMeta: metav1.ObjectMeta{
OwnerReferences: []metav1.OwnerReference{
{
Name: "foo",
Kind: "Deployment",
APIVersion: "extensions/v1alpha1",
UID: "foo-uid-1",
},
},
},
}
dep := metav1.OwnerReference{
APIVersion: "v1",
Kind: "Deployment",
Name: "foo",
UID: "foo-uid-2",
}

controllerutil.AppendOwnerReference(dep, rs)
Expect(rs.OwnerReferences).To(ConsistOf(metav1.OwnerReference{
Name: "foo",
Kind: "Deployment",
APIVersion: "extensions/v1beta1",
UID: "foo-uid-2",
}))
})
It("should remove the owner reference", func() {
rs := &appsv1.ReplicaSet{
ObjectMeta: metav1.ObjectMeta{
OwnerReferences: []metav1.OwnerReference{
{
Name: "foo",
Kind: "Deployment",
APIVersion: "extensions/v1alpha1",
UID: "foo-uid-1",
},
},
},
}
dep := metav1.OwnerReference{
APIVersion: "v1",
Kind: "Deployment",
Name: "foo",
UID: "foo-uid-2",
}

controllerutil.AppendOwnerReference(dep, rs)
Expect(rs.OwnerReferences).To(ConsistOf(metav1.OwnerReference{
Name: "foo",
Kind: "Deployment",
APIVersion: "extensions/v1beta1",
UID: "foo-uid-2",
}))
Expect(controllerutil.DropOwnerReference(dep, rs)).ToNot(HaveOccurred())
Expect(rs.GetOwnerReferences()).To(BeEmpty())
})
It("should error when trying to remove the reference that doesn't exist", func() {
rs := &appsv1.ReplicaSet{
ObjectMeta: metav1.ObjectMeta{},
}
dep := metav1.OwnerReference{
APIVersion: "v1",
Kind: "Deployment",
Name: "foo",
UID: "foo-uid-2",
}
Expect(controllerutil.DropOwnerReference(dep, rs)).To(HaveOccurred())
Expect(rs.GetOwnerReferences()).To(BeEmpty())
})
It("should error when trying to remove the reference that doesn't abide by the scheme", func() {
rs := &appsv1.ReplicaSet{
ObjectMeta: metav1.ObjectMeta{},
}
dep := metav1.OwnerReference{
APIVersion: "v1",
Kind: "Deployment",
Name: "foo",
UID: "foo-uid-2",
}
controllerutil.AppendOwnerReference(dep, rs)
Expect(controllerutil.DropOwnerReference(dep, rs)).To(HaveOccurred())
Expect(rs.GetOwnerReferences()).To(HaveLen(1))
})
It("should error when trying to remove the owner when setting the owner as a non runtime.Object", func() {
rs := &appsv1.ReplicaSet{
ObjectMeta: metav1.ObjectMeta{},
}
dep := metav1.OwnerReference{
APIVersion: "v1",
Kind: "Deployment",
Name: "foo",
UID: "foo-uid-2",
}
controllerutil.AppendOwnerReference(dep, rs)
Expect(controllerutil.DropOwnerReference(dep, rs)).To(HaveOccurred())
Expect(rs.GetOwnerReferences()).To(HaveLen(1))
})

It("should error when trying to remove an owner that doesn't exist", func() {
rs := &appsv1.ReplicaSet{
ObjectMeta: metav1.ObjectMeta{},
}
dep := metav1.OwnerReference{
APIVersion: "v1",
Kind: "Deployment",
Name: "foo",
UID: "foo-uid-2",
}
dep2 := metav1.OwnerReference{
APIVersion: "v1",
Kind: "Deployment",
Name: "foo",
UID: "foo-uid-3",
}
controllerutil.AppendOwnerReference(dep, rs)
Expect(controllerutil.DropOwnerReference(dep2, rs)).To(HaveOccurred())
Expect(rs.GetOwnerReferences()).To(HaveLen(1))
})

It("should error when DropOwnerReference owner's controller is set to false", func() {
rs := &appsv1.ReplicaSet{
ObjectMeta: metav1.ObjectMeta{},
}
dep := metav1.OwnerReference{
APIVersion: "v1",
Kind: "Deployment",
Name: "foo",
UID: "foo-uid-2",
}
controllerutil.AppendOwnerReference(dep, rs)
Expect(controllerutil.DropOwnerReference(dep, rs)).To(HaveOccurred())
})

It("should error when DropOwnerReference passed in owner is not the owner", func() {
rs := &appsv1.ReplicaSet{
ObjectMeta: metav1.ObjectMeta{},
}
dep := metav1.OwnerReference{
APIVersion: "v1",
Kind: "Deployment",
Name: "foo",
UID: "foo-uid-2",
}
dep2 := metav1.OwnerReference{
APIVersion: "v1",
Kind: "Deployment",
Name: "foo-2",
UID: "foo-uid-42",
}
controllerutil.AppendOwnerReference(dep, rs)
controllerutil.AppendOwnerReference(dep2, rs)
Expect(controllerutil.DropOwnerReference(dep2, rs)).To(HaveOccurred())
Expect(rs.GetOwnerReferences()).To(HaveLen(2))
})
})
})

Describe("SetControllerReference", func() {
It("should set the OwnerReference if it can find the group version kind", func() {
rs := &appsv1.ReplicaSet{}
Expand Down

0 comments on commit cbf5989

Please sign in to comment.