Modified BPA controller and fixed bugs
[icn.git] / cmd / bpa-operator / vendor / sigs.k8s.io / controller-runtime / pkg / controller / controllerutil / controllerutil.go
1 /*
2 Copyright 2018 The Kubernetes Authors.
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8     http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 */
16
17 package controllerutil
18
19 import (
20         "context"
21         "fmt"
22         "reflect"
23
24         "k8s.io/apimachinery/pkg/api/errors"
25         "k8s.io/apimachinery/pkg/apis/meta/v1"
26         "k8s.io/apimachinery/pkg/runtime"
27         "k8s.io/apimachinery/pkg/runtime/schema"
28         "sigs.k8s.io/controller-runtime/pkg/client"
29         "sigs.k8s.io/controller-runtime/pkg/client/apiutil"
30 )
31
32 // AlreadyOwnedError is an error returned if the object you are trying to assign
33 // a controller reference is already owned by another controller Object is the
34 // subject and Owner is the reference for the current owner
35 type AlreadyOwnedError struct {
36         Object v1.Object
37         Owner  v1.OwnerReference
38 }
39
40 func (e *AlreadyOwnedError) Error() string {
41         return fmt.Sprintf("Object %s/%s is already owned by another %s controller %s", e.Object.GetNamespace(), e.Object.GetName(), e.Owner.Kind, e.Owner.Name)
42 }
43
44 func newAlreadyOwnedError(Object v1.Object, Owner v1.OwnerReference) *AlreadyOwnedError {
45         return &AlreadyOwnedError{
46                 Object: Object,
47                 Owner:  Owner,
48         }
49 }
50
51 // SetControllerReference sets owner as a Controller OwnerReference on owned.
52 // This is used for garbage collection of the owned object and for
53 // reconciling the owner object on changes to owned (with a Watch + EnqueueRequestForOwner).
54 // Since only one OwnerReference can be a controller, it returns an error if
55 // there is another OwnerReference with Controller flag set.
56 func SetControllerReference(owner, object v1.Object, scheme *runtime.Scheme) error {
57         ro, ok := owner.(runtime.Object)
58         if !ok {
59                 return fmt.Errorf("is not a %T a runtime.Object, cannot call SetControllerReference", owner)
60         }
61
62         gvk, err := apiutil.GVKForObject(ro, scheme)
63         if err != nil {
64                 return err
65         }
66
67         // Create a new ref
68         ref := *v1.NewControllerRef(owner, schema.GroupVersionKind{Group: gvk.Group, Version: gvk.Version, Kind: gvk.Kind})
69
70         existingRefs := object.GetOwnerReferences()
71         fi := -1
72         for i, r := range existingRefs {
73                 if referSameObject(ref, r) {
74                         fi = i
75                 } else if r.Controller != nil && *r.Controller {
76                         return newAlreadyOwnedError(object, r)
77                 }
78         }
79         if fi == -1 {
80                 existingRefs = append(existingRefs, ref)
81         } else {
82                 existingRefs[fi] = ref
83         }
84
85         // Update owner references
86         object.SetOwnerReferences(existingRefs)
87         return nil
88 }
89
90 // Returns true if a and b point to the same object
91 func referSameObject(a, b v1.OwnerReference) bool {
92         aGV, err := schema.ParseGroupVersion(a.APIVersion)
93         if err != nil {
94                 return false
95         }
96
97         bGV, err := schema.ParseGroupVersion(b.APIVersion)
98         if err != nil {
99                 return false
100         }
101
102         return aGV == bGV && a.Kind == b.Kind && a.Name == b.Name
103 }
104
105 // OperationResult is the action result of a CreateOrUpdate call
106 type OperationResult string
107
108 const ( // They should complete the sentence "Deployment default/foo has been ..."
109         // OperationResultNone means that the resource has not been changed
110         OperationResultNone OperationResult = "unchanged"
111         // OperationResultCreated means that a new resource is created
112         OperationResultCreated OperationResult = "created"
113         // OperationResultUpdated means that an existing resource is updated
114         OperationResultUpdated OperationResult = "updated"
115 )
116
117 // CreateOrUpdate creates or updates the given object obj in the Kubernetes
118 // cluster. The object's desired state should be reconciled with the existing
119 // state using the passed in ReconcileFn. obj must be a struct pointer so that
120 // obj can be updated with the content returned by the Server.
121 //
122 // It returns the executed operation and an error.
123 func CreateOrUpdate(ctx context.Context, c client.Client, obj runtime.Object, f MutateFn) (OperationResult, error) {
124         // op is the operation we are going to attempt
125         op := OperationResultNone
126
127         // get the existing object meta
128         metaObj, ok := obj.(v1.Object)
129         if !ok {
130                 return OperationResultNone, fmt.Errorf("%T does not implement metav1.Object interface", obj)
131         }
132
133         // retrieve the existing object
134         key := client.ObjectKey{
135                 Name:      metaObj.GetName(),
136                 Namespace: metaObj.GetNamespace(),
137         }
138         err := c.Get(ctx, key, obj)
139
140         // reconcile the existing object
141         existing := obj.DeepCopyObject()
142         existingObjMeta := existing.(v1.Object)
143         existingObjMeta.SetName(metaObj.GetName())
144         existingObjMeta.SetNamespace(metaObj.GetNamespace())
145
146         if e := f(obj); e != nil {
147                 return OperationResultNone, e
148         }
149
150         if metaObj.GetName() != existingObjMeta.GetName() {
151                 return OperationResultNone, fmt.Errorf("ReconcileFn cannot mutate objects name")
152         }
153
154         if metaObj.GetNamespace() != existingObjMeta.GetNamespace() {
155                 return OperationResultNone, fmt.Errorf("ReconcileFn cannot mutate objects namespace")
156         }
157
158         if errors.IsNotFound(err) {
159                 err = c.Create(ctx, obj)
160                 op = OperationResultCreated
161         } else if err == nil {
162                 if reflect.DeepEqual(existing, obj) {
163                         return OperationResultNone, nil
164                 }
165                 err = c.Update(ctx, obj)
166                 op = OperationResultUpdated
167         } else {
168                 return OperationResultNone, err
169         }
170
171         if err != nil {
172                 op = OperationResultNone
173         }
174         return op, err
175 }
176
177 // MutateFn is a function which mutates the existing object into it's desired state.
178 type MutateFn func(existing runtime.Object) error