Code refactoring for bpa operator
[icn.git] / cmd / bpa-operator / vendor / sigs.k8s.io / controller-runtime / pkg / handler / enqueue_owner.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 handler
18
19 import (
20         "fmt"
21
22         metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
23         "k8s.io/apimachinery/pkg/runtime"
24         "k8s.io/apimachinery/pkg/runtime/schema"
25         "k8s.io/apimachinery/pkg/types"
26         "k8s.io/client-go/util/workqueue"
27         "sigs.k8s.io/controller-runtime/pkg/event"
28         "sigs.k8s.io/controller-runtime/pkg/reconcile"
29         "sigs.k8s.io/controller-runtime/pkg/runtime/inject"
30         logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
31 )
32
33 var _ EventHandler = &EnqueueRequestForOwner{}
34
35 var log = logf.KBLog.WithName("eventhandler").WithName("EnqueueRequestForOwner")
36
37 // EnqueueRequestForOwner enqueues Requests for the Owners of an object.  E.g. the object that created
38 // the object that was the source of the Event.
39 //
40 // If a ReplicaSet creates Pods, users may reconcile the ReplicaSet in response to Pod Events using:
41 //
42 // - a source.Kind Source with Type of Pod.
43 //
44 // - a handler.EnqueueRequestForOwner EventHandler with an OwnerType of ReplicaSet and IsController set to true.
45 type EnqueueRequestForOwner struct {
46         // OwnerType is the type of the Owner object to look for in OwnerReferences.  Only Group and Kind are compared.
47         OwnerType runtime.Object
48
49         // IsController if set will only look at the first OwnerReference with Controller: true.
50         IsController bool
51
52         // groupKind is the cached Group and Kind from OwnerType
53         groupKind schema.GroupKind
54 }
55
56 // Create implements EventHandler
57 func (e *EnqueueRequestForOwner) Create(evt event.CreateEvent, q workqueue.RateLimitingInterface) {
58         for _, req := range e.getOwnerReconcileRequest(evt.Meta) {
59                 q.Add(req)
60         }
61 }
62
63 // Update implements EventHandler
64 func (e *EnqueueRequestForOwner) Update(evt event.UpdateEvent, q workqueue.RateLimitingInterface) {
65         for _, req := range e.getOwnerReconcileRequest(evt.MetaOld) {
66                 q.Add(req)
67         }
68         for _, req := range e.getOwnerReconcileRequest(evt.MetaNew) {
69                 q.Add(req)
70         }
71 }
72
73 // Delete implements EventHandler
74 func (e *EnqueueRequestForOwner) Delete(evt event.DeleteEvent, q workqueue.RateLimitingInterface) {
75         for _, req := range e.getOwnerReconcileRequest(evt.Meta) {
76                 q.Add(req)
77         }
78 }
79
80 // Generic implements EventHandler
81 func (e *EnqueueRequestForOwner) Generic(evt event.GenericEvent, q workqueue.RateLimitingInterface) {
82         for _, req := range e.getOwnerReconcileRequest(evt.Meta) {
83                 q.Add(req)
84         }
85 }
86
87 // parseOwnerTypeGroupKind parses the OwnerType into a Group and Kind and caches the result.  Returns false
88 // if the OwnerType could not be parsed using the scheme.
89 func (e *EnqueueRequestForOwner) parseOwnerTypeGroupKind(scheme *runtime.Scheme) error {
90         // Get the kinds of the type
91         kinds, _, err := scheme.ObjectKinds(e.OwnerType)
92         if err != nil {
93                 log.Error(err, "Could not get ObjectKinds for OwnerType", "owner type", fmt.Sprintf("%T", e.OwnerType))
94                 return err
95         }
96         // Expect only 1 kind.  If there is more than one kind this is probably an edge case such as ListOptions.
97         if len(kinds) != 1 {
98                 err := fmt.Errorf("Expected exactly 1 kind for OwnerType %T, but found %s kinds", e.OwnerType, kinds)
99                 log.Error(nil, "Expected exactly 1 kind for OwnerType", "owner type", fmt.Sprintf("%T", e.OwnerType), "kinds", kinds)
100                 return err
101
102         }
103         // Cache the Group and Kind for the OwnerType
104         e.groupKind = schema.GroupKind{Group: kinds[0].Group, Kind: kinds[0].Kind}
105         return nil
106 }
107
108 // getOwnerReconcileRequest looks at object and returns a slice of reconcile.Request to reconcile
109 // owners of object that match e.OwnerType.
110 func (e *EnqueueRequestForOwner) getOwnerReconcileRequest(object metav1.Object) []reconcile.Request {
111         // Iterate through the OwnerReferences looking for a match on Group and Kind against what was requested
112         // by the user
113         var result []reconcile.Request
114         for _, ref := range e.getOwnersReferences(object) {
115                 // Parse the Group out of the OwnerReference to compare it to what was parsed out of the requested OwnerType
116                 refGV, err := schema.ParseGroupVersion(ref.APIVersion)
117                 if err != nil {
118                         log.Error(err, "Could not parse OwnerReference APIVersion",
119                                 "api version", ref.APIVersion)
120                         return nil
121                 }
122
123                 // Compare the OwnerReference Group and Kind against the OwnerType Group and Kind specified by the user.
124                 // If the two match, create a Request for the objected referred to by
125                 // the OwnerReference.  Use the Name from the OwnerReference and the Namespace from the
126                 // object in the event.
127                 if ref.Kind == e.groupKind.Kind && refGV.Group == e.groupKind.Group {
128                         // Match found - add a Request for the object referred to in the OwnerReference
129                         result = append(result, reconcile.Request{NamespacedName: types.NamespacedName{
130                                 Namespace: object.GetNamespace(),
131                                 Name:      ref.Name,
132                         }})
133                 }
134         }
135
136         // Return the matches
137         return result
138 }
139
140 // getOwnersReferences returns the OwnerReferences for an object as specified by the EnqueueRequestForOwner
141 // - if IsController is true: only take the Controller OwnerReference (if found)
142 // - if IsController is false: take all OwnerReferences
143 func (e *EnqueueRequestForOwner) getOwnersReferences(object metav1.Object) []metav1.OwnerReference {
144         if object == nil {
145                 return nil
146         }
147
148         // If not filtered as Controller only, then use all the OwnerReferences
149         if !e.IsController {
150                 return object.GetOwnerReferences()
151         }
152         // If filtered to a Controller, only take the Controller OwnerReference
153         if ownerRef := metav1.GetControllerOf(object); ownerRef != nil {
154                 return []metav1.OwnerReference{*ownerRef}
155         }
156         // No Controller OwnerReference found
157         return nil
158 }
159
160 var _ inject.Scheme = &EnqueueRequestForOwner{}
161
162 // InjectScheme is called by the Controller to provide a singleton scheme to the EnqueueRequestForOwner.
163 func (e *EnqueueRequestForOwner) InjectScheme(s *runtime.Scheme) error {
164         return e.parseOwnerTypeGroupKind(s)
165 }