@@ -55,47 +55,95 @@ func newAlreadyOwnedError(Object metav1.Object, Owner metav1.OwnerReference) *Al
55
55
// Since only one OwnerReference can be a controller, it returns an error if
56
56
// there is another OwnerReference with Controller flag set.
57
57
func SetControllerReference (owner , controlled metav1.Object , scheme * runtime.Scheme ) error {
58
+ // Validate the owner.
58
59
ro , ok := owner .(runtime.Object )
59
60
if ! ok {
60
61
return fmt .Errorf ("%T is not a runtime.Object, cannot call SetControllerReference" , owner )
61
62
}
62
-
63
- ownerNs := owner .GetNamespace ()
64
- if ownerNs != "" {
65
- objNs := controlled .GetNamespace ()
66
- if objNs == "" {
67
- return fmt .Errorf ("cluster-scoped resource must not have a namespace-scoped owner, owner's namespace %s" , ownerNs )
68
- }
69
- if ownerNs != objNs {
70
- return fmt .Errorf ("cross-namespace owner references are disallowed, owner's namespace %s, obj's namespace %s" , owner .GetNamespace (), controlled .GetNamespace ())
71
- }
63
+ if err := validateOwner (owner , controlled ); err != nil {
64
+ return err
72
65
}
73
66
67
+ // Create a new controller ref.
74
68
gvk , err := apiutil .GVKForObject (ro , scheme )
75
69
if err != nil {
76
70
return err
77
71
}
78
-
79
- // Create a new ref
80
72
ref := * metav1 .NewControllerRef (owner , schema.GroupVersionKind {Group : gvk .Group , Version : gvk .Version , Kind : gvk .Kind })
81
73
82
- existingRefs := controlled .GetOwnerReferences ()
83
- fi := - 1
84
- for i , r := range existingRefs {
85
- if referSameObject (ref , r ) {
86
- fi = i
87
- } else if r .Controller != nil && * r .Controller {
88
- return newAlreadyOwnedError (controlled , r )
89
- }
74
+ // Return early with an error if the object is already controlled.
75
+ if existing := metav1 .GetControllerOf (controlled ); existing != nil && ! referSameObject (* existing , ref ) {
76
+ return newAlreadyOwnedError (controlled , * existing )
90
77
}
91
- if fi == - 1 {
92
- existingRefs = append (existingRefs , ref )
78
+
79
+ // Update owner references and return.
80
+ upsertOwnerRef (ref , controlled )
81
+ return nil
82
+ }
83
+
84
+ // EnsureOwnerReference is a helper method to make sure the given object contains
85
+ // an object reference to the object provided.
86
+ // If a reference already exists, it'll be overwritten with the newly provided version.
87
+ func EnsureOwnerReference (owner , object metav1.Object , scheme * runtime.Scheme ) error {
88
+ // Validate the owner.
89
+ ro , ok := owner .(runtime.Object )
90
+ if ! ok {
91
+ return fmt .Errorf ("%T is not a runtime.Object, cannot call SetControllerReference" , owner )
92
+ }
93
+ if err := validateOwner (owner , object ); err != nil {
94
+ return err
95
+ }
96
+
97
+ // Create a new owner ref.
98
+ gvk , err := apiutil .GVKForObject (ro , scheme )
99
+ if err != nil {
100
+ return err
101
+ }
102
+ ref := metav1.OwnerReference {
103
+ APIVersion : gvk .GroupVersion ().String (),
104
+ Kind : gvk .Kind ,
105
+ UID : owner .GetUID (),
106
+ Name : owner .GetName (),
107
+ }
108
+
109
+ // Update owner references and return.
110
+ upsertOwnerRef (ref , object )
111
+ return nil
112
+
113
+ }
114
+
115
+ func upsertOwnerRef (ref metav1.OwnerReference , object metav1.Object ) {
116
+ owners := object .GetOwnerReferences ()
117
+ idx := indexOwnerRef (owners , ref )
118
+ if idx == - 1 {
119
+ owners = append (owners , ref )
93
120
} else {
94
- existingRefs [fi ] = ref
121
+ owners [idx ] = ref
122
+ }
123
+ object .SetOwnerReferences (owners )
124
+ }
125
+
126
+ // indexOwnerRef returns the index of the owner reference in the slice if found, or -1.
127
+ func indexOwnerRef (ownerReferences []metav1.OwnerReference , ref metav1.OwnerReference ) int {
128
+ for index , r := range ownerReferences {
129
+ if referSameObject (r , ref ) {
130
+ return index
131
+ }
95
132
}
133
+ return - 1
134
+ }
96
135
97
- // Update owner references
98
- controlled .SetOwnerReferences (existingRefs )
136
+ func validateOwner (owner , object metav1.Object ) error {
137
+ ownerNs := owner .GetNamespace ()
138
+ if ownerNs != "" {
139
+ objNs := object .GetNamespace ()
140
+ if objNs == "" {
141
+ return fmt .Errorf ("cluster-scoped resource must not have a namespace-scoped owner, owner's namespace %s" , ownerNs )
142
+ }
143
+ if ownerNs != objNs {
144
+ return fmt .Errorf ("cross-namespace owner references are disallowed, owner's namespace %s, obj's namespace %s" , owner .GetNamespace (), object .GetNamespace ())
145
+ }
146
+ }
99
147
return nil
100
148
}
101
149
0 commit comments