Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
348 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package controller | ||
|
||
import ( | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
kapihelper "k8s.io/kubernetes/pkg/api/helper" | ||
) | ||
|
||
// EnsureOwnerRef adds the ownerref if needed. Removes ownerrefs with conflicting UIDs. | ||
// Returns true if the input is mutated. | ||
func EnsureOwnerRef(metadata metav1.Object, newOwnerRef metav1.OwnerReference) bool { | ||
foundButNotEqual := false | ||
for _, existingOwnerRef := range metadata.GetOwnerReferences() { | ||
if existingOwnerRef.APIVersion == newOwnerRef.APIVersion && | ||
existingOwnerRef.Kind == newOwnerRef.Kind && | ||
existingOwnerRef.Name == newOwnerRef.Name { | ||
|
||
// if we're completely the same, there's nothing to do | ||
if kapihelper.Semantic.DeepEqual(existingOwnerRef, newOwnerRef) { | ||
return false | ||
} | ||
|
||
foundButNotEqual = true | ||
break | ||
} | ||
} | ||
|
||
// if we weren't found, then we just need to add ourselves | ||
if !foundButNotEqual { | ||
metadata.SetOwnerReferences(append(metadata.GetOwnerReferences(), newOwnerRef)) | ||
return true | ||
} | ||
|
||
// if we need to remove an existing ownerRef, just do the easy thing and build it back from scratch | ||
newOwnerRefs := []metav1.OwnerReference{newOwnerRef} | ||
for i := range metadata.GetOwnerReferences() { | ||
existingOwnerRef := metadata.GetOwnerReferences()[i] | ||
if existingOwnerRef.APIVersion == newOwnerRef.APIVersion && | ||
existingOwnerRef.Kind == newOwnerRef.Kind && | ||
existingOwnerRef.Name == newOwnerRef.Name { | ||
continue | ||
} | ||
newOwnerRefs = append(newOwnerRefs, existingOwnerRef) | ||
} | ||
metadata.SetOwnerReferences(newOwnerRefs) | ||
return true | ||
} | ||
|
||
// HasOwnerRef checks to see if an object has a particular owner. It is not opinionated about | ||
// the bool fields | ||
func HasOwnerRef(metadata metav1.Object, needle metav1.OwnerReference) bool { | ||
for _, existingOwnerRef := range metadata.GetOwnerReferences() { | ||
if existingOwnerRef.APIVersion == needle.APIVersion && | ||
existingOwnerRef.Kind == needle.Kind && | ||
existingOwnerRef.Name == needle.Name && | ||
existingOwnerRef.UID == needle.UID { | ||
return true | ||
} | ||
} | ||
return false | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,221 @@ | ||
package controller | ||
|
||
import ( | ||
"testing" | ||
|
||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/apimachinery/pkg/types" | ||
kapihelper "k8s.io/kubernetes/pkg/api/helper" | ||
) | ||
|
||
func TestEnsureOwnerRef(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
obj *metav1.ObjectMeta | ||
newOwnerRef metav1.OwnerReference | ||
expectedOwners []metav1.OwnerReference | ||
expectedReturn bool | ||
}{ | ||
{ | ||
name: "empty", | ||
obj: &metav1.ObjectMeta{}, | ||
newOwnerRef: metav1.OwnerReference{ | ||
APIVersion: "v1", Kind: "Foo", Name: "the-name", UID: types.UID("uid"), | ||
}, | ||
expectedOwners: []metav1.OwnerReference{ | ||
{APIVersion: "v1", Kind: "Foo", Name: "the-name", UID: types.UID("uid")}, | ||
}, | ||
expectedReturn: true, | ||
}, | ||
{ | ||
name: "add", | ||
obj: &metav1.ObjectMeta{ | ||
OwnerReferences: []metav1.OwnerReference{ | ||
{APIVersion: "v1", Kind: "Foo", Name: "the-other", UID: types.UID("other-uid")}, | ||
}, | ||
}, | ||
newOwnerRef: metav1.OwnerReference{ | ||
APIVersion: "v1", Kind: "Foo", Name: "the-name", UID: types.UID("uid"), | ||
}, | ||
expectedOwners: []metav1.OwnerReference{ | ||
{APIVersion: "v1", Kind: "Foo", Name: "the-other", UID: types.UID("other-uid")}, | ||
{APIVersion: "v1", Kind: "Foo", Name: "the-name", UID: types.UID("uid")}, | ||
}, | ||
expectedReturn: true, | ||
}, | ||
{ | ||
name: "skip", | ||
obj: &metav1.ObjectMeta{ | ||
OwnerReferences: []metav1.OwnerReference{ | ||
{APIVersion: "v1", Kind: "Foo", Name: "the-name", UID: types.UID("uid")}, | ||
{APIVersion: "v1", Kind: "Foo", Name: "the-other", UID: types.UID("other-uid")}, | ||
}, | ||
}, | ||
newOwnerRef: metav1.OwnerReference{ | ||
APIVersion: "v1", Kind: "Foo", Name: "the-name", UID: types.UID("uid"), | ||
}, | ||
expectedOwners: []metav1.OwnerReference{ | ||
{APIVersion: "v1", Kind: "Foo", Name: "the-name", UID: types.UID("uid")}, | ||
{APIVersion: "v1", Kind: "Foo", Name: "the-other", UID: types.UID("other-uid")}, | ||
}, | ||
expectedReturn: false, | ||
}, | ||
{ | ||
name: "replace on uid", | ||
obj: &metav1.ObjectMeta{ | ||
OwnerReferences: []metav1.OwnerReference{ | ||
{APIVersion: "v1", Kind: "Foo", Name: "the-other", UID: types.UID("other-uid")}, | ||
{APIVersion: "v1", Kind: "Foo", Name: "the-name", UID: types.UID("bad-uid")}, | ||
}, | ||
}, | ||
newOwnerRef: metav1.OwnerReference{ | ||
APIVersion: "v1", Kind: "Foo", Name: "the-name", UID: types.UID("uid"), | ||
}, | ||
expectedOwners: []metav1.OwnerReference{ | ||
{APIVersion: "v1", Kind: "Foo", Name: "the-name", UID: types.UID("uid")}, | ||
{APIVersion: "v1", Kind: "Foo", Name: "the-other", UID: types.UID("other-uid")}, | ||
}, | ||
expectedReturn: true, | ||
}, | ||
{ | ||
name: "preserve controller", | ||
obj: &metav1.ObjectMeta{ | ||
OwnerReferences: []metav1.OwnerReference{ | ||
{APIVersion: "v1", Kind: "Foo", Name: "the-other", UID: types.UID("other-uid")}, | ||
{APIVersion: "v1", Kind: "Foo", Name: "the-name", UID: types.UID("uid")}, | ||
}, | ||
}, | ||
newOwnerRef: metav1.OwnerReference{ | ||
APIVersion: "v1", Kind: "Foo", Name: "the-name", UID: types.UID("uid"), Controller: boolPtr(true), | ||
}, | ||
expectedOwners: []metav1.OwnerReference{ | ||
{APIVersion: "v1", Kind: "Foo", Name: "the-name", UID: types.UID("uid"), Controller: boolPtr(true)}, | ||
{APIVersion: "v1", Kind: "Foo", Name: "the-other", UID: types.UID("other-uid")}, | ||
}, | ||
expectedReturn: true, | ||
}, | ||
{ | ||
name: "preserve block", | ||
obj: &metav1.ObjectMeta{ | ||
OwnerReferences: []metav1.OwnerReference{ | ||
{APIVersion: "v1", Kind: "Foo", Name: "the-other", UID: types.UID("other-uid")}, | ||
{APIVersion: "v1", Kind: "Foo", Name: "the-name", UID: types.UID("uid")}, | ||
}, | ||
}, | ||
newOwnerRef: metav1.OwnerReference{ | ||
APIVersion: "v1", Kind: "Foo", Name: "the-name", UID: types.UID("uid"), BlockOwnerDeletion: boolPtr(false), | ||
}, | ||
expectedOwners: []metav1.OwnerReference{ | ||
{APIVersion: "v1", Kind: "Foo", Name: "the-name", UID: types.UID("uid"), BlockOwnerDeletion: boolPtr(false)}, | ||
{APIVersion: "v1", Kind: "Foo", Name: "the-other", UID: types.UID("other-uid")}, | ||
}, | ||
expectedReturn: true, | ||
}, | ||
} | ||
|
||
for _, tc := range tests { | ||
t.Run(tc.name, func(t *testing.T) { | ||
actualReturn := EnsureOwnerRef(tc.obj, tc.newOwnerRef) | ||
if tc.expectedReturn != actualReturn { | ||
t.Errorf("expected %v, got %v", tc.expectedReturn, actualReturn) | ||
return | ||
} | ||
if !kapihelper.Semantic.DeepEqual(tc.expectedOwners, tc.obj.OwnerReferences) { | ||
t.Errorf("expected %v, got %v", tc.expectedOwners, tc.obj.OwnerReferences) | ||
return | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func boolPtr(in bool) *bool { | ||
return &in | ||
} | ||
|
||
func TestHasOwnerRef(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
haystack *metav1.ObjectMeta | ||
needle metav1.OwnerReference | ||
expected bool | ||
}{ | ||
{ | ||
name: "empty", | ||
haystack: &metav1.ObjectMeta{}, | ||
needle: metav1.OwnerReference{ | ||
APIVersion: "v1", Kind: "Foo", Name: "the-name", UID: types.UID("uid"), | ||
}, | ||
expected: false, | ||
}, | ||
{ | ||
name: "exact", | ||
haystack: &metav1.ObjectMeta{ | ||
OwnerReferences: []metav1.OwnerReference{{ | ||
APIVersion: "v1", Kind: "Foo", Name: "the-name", UID: types.UID("uid"), | ||
}}, | ||
}, | ||
needle: metav1.OwnerReference{ | ||
APIVersion: "v1", Kind: "Foo", Name: "the-name", UID: types.UID("uid"), | ||
}, | ||
expected: true, | ||
}, | ||
{ | ||
name: "not uid", | ||
haystack: &metav1.ObjectMeta{ | ||
OwnerReferences: []metav1.OwnerReference{{ | ||
APIVersion: "v1", Kind: "Foo", Name: "the-name", | ||
}}, | ||
}, | ||
needle: metav1.OwnerReference{ | ||
APIVersion: "v1", Kind: "Foo", Name: "the-name", UID: types.UID("uid"), | ||
}, | ||
expected: false, | ||
}, | ||
{ | ||
name: "ignored controller", | ||
haystack: &metav1.ObjectMeta{ | ||
OwnerReferences: []metav1.OwnerReference{{ | ||
APIVersion: "v1", Kind: "Foo", Name: "the-name", UID: types.UID("uid"), | ||
}}, | ||
}, | ||
needle: metav1.OwnerReference{ | ||
APIVersion: "v1", Kind: "Foo", Name: "the-name", UID: types.UID("uid"), Controller: boolPtr(true), | ||
}, | ||
expected: true, | ||
}, | ||
{ | ||
name: "ignored block", | ||
haystack: &metav1.ObjectMeta{ | ||
OwnerReferences: []metav1.OwnerReference{{ | ||
APIVersion: "v1", Kind: "Foo", Name: "the-name", UID: types.UID("uid"), | ||
}}, | ||
}, | ||
needle: metav1.OwnerReference{ | ||
APIVersion: "v1", Kind: "Foo", Name: "the-name", UID: types.UID("uid"), BlockOwnerDeletion: boolPtr(false), | ||
}, | ||
expected: true, | ||
}, | ||
{ | ||
name: "ignored both", | ||
haystack: &metav1.ObjectMeta{ | ||
OwnerReferences: []metav1.OwnerReference{{ | ||
APIVersion: "v1", Kind: "Foo", Name: "the-name", UID: types.UID("uid"), Controller: boolPtr(false), | ||
}}, | ||
}, | ||
needle: metav1.OwnerReference{ | ||
APIVersion: "v1", Kind: "Foo", Name: "the-name", UID: types.UID("uid"), BlockOwnerDeletion: boolPtr(false), | ||
}, | ||
expected: true, | ||
}, | ||
} | ||
|
||
for _, tc := range tests { | ||
t.Run(tc.name, func(t *testing.T) { | ||
actual := HasOwnerRef(tc.haystack, tc.needle) | ||
if tc.expected != actual { | ||
t.Errorf("expected %v, got %v", tc.expected, actual) | ||
return | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.