Remove BPA from Makefile
[icn.git] / cmd / bpa-operator / vendor / sigs.k8s.io / controller-runtime / pkg / cache / informer_cache.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 cache
18
19 import (
20         "context"
21         "fmt"
22         "reflect"
23         "strings"
24
25         apimeta "k8s.io/apimachinery/pkg/api/meta"
26         "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
27         "k8s.io/apimachinery/pkg/runtime"
28         "k8s.io/apimachinery/pkg/runtime/schema"
29         "k8s.io/client-go/tools/cache"
30         "sigs.k8s.io/controller-runtime/pkg/cache/internal"
31         "sigs.k8s.io/controller-runtime/pkg/client"
32         "sigs.k8s.io/controller-runtime/pkg/client/apiutil"
33 )
34
35 var (
36         _ Informers     = &informerCache{}
37         _ client.Reader = &informerCache{}
38         _ Cache         = &informerCache{}
39 )
40
41 // informerCache is a Kubernetes Object cache populated from InformersMap.  informerCache wraps an InformersMap.
42 type informerCache struct {
43         *internal.InformersMap
44 }
45
46 // Get implements Reader
47 func (ip *informerCache) Get(ctx context.Context, key client.ObjectKey, out runtime.Object) error {
48         gvk, err := apiutil.GVKForObject(out, ip.Scheme)
49         if err != nil {
50                 return err
51         }
52
53         cache, err := ip.InformersMap.Get(gvk, out)
54         if err != nil {
55                 return err
56         }
57         return cache.Reader.Get(ctx, key, out)
58 }
59
60 // List implements Reader
61 func (ip *informerCache) List(ctx context.Context, opts *client.ListOptions, out runtime.Object) error {
62         gvk, err := apiutil.GVKForObject(out, ip.Scheme)
63         if err != nil {
64                 return err
65         }
66
67         if !strings.HasSuffix(gvk.Kind, "List") {
68                 return fmt.Errorf("non-list type %T (kind %q) passed as output", out, gvk)
69         }
70         // we need the non-list GVK, so chop off the "List" from the end of the kind
71         gvk.Kind = gvk.Kind[:len(gvk.Kind)-4]
72         _, isUnstructured := out.(*unstructured.UnstructuredList)
73         var cacheTypeObj runtime.Object
74         if isUnstructured {
75                 u := &unstructured.Unstructured{}
76                 u.SetGroupVersionKind(gvk)
77                 cacheTypeObj = u
78         } else {
79                 itemsPtr, err := apimeta.GetItemsPtr(out)
80                 if err != nil {
81                         return nil
82                 }
83                 // http://knowyourmeme.com/memes/this-is-fine
84                 elemType := reflect.Indirect(reflect.ValueOf(itemsPtr)).Type().Elem()
85                 cacheTypeValue := reflect.Zero(reflect.PtrTo(elemType))
86                 var ok bool
87                 cacheTypeObj, ok = cacheTypeValue.Interface().(runtime.Object)
88                 if !ok {
89                         return fmt.Errorf("cannot get cache for %T, its element %T is not a runtime.Object", out, cacheTypeValue.Interface())
90                 }
91         }
92
93         cache, err := ip.InformersMap.Get(gvk, cacheTypeObj)
94         if err != nil {
95                 return err
96         }
97
98         return cache.Reader.List(ctx, opts, out)
99 }
100
101 // GetInformerForKind returns the informer for the GroupVersionKind
102 func (ip *informerCache) GetInformerForKind(gvk schema.GroupVersionKind) (cache.SharedIndexInformer, error) {
103         // Map the gvk to an object
104         obj, err := ip.Scheme.New(gvk)
105         if err != nil {
106                 return nil, err
107         }
108         i, err := ip.InformersMap.Get(gvk, obj)
109         if err != nil {
110                 return nil, err
111         }
112         return i.Informer, err
113 }
114
115 // GetInformer returns the informer for the obj
116 func (ip *informerCache) GetInformer(obj runtime.Object) (cache.SharedIndexInformer, error) {
117         gvk, err := apiutil.GVKForObject(obj, ip.Scheme)
118         if err != nil {
119                 return nil, err
120         }
121         i, err := ip.InformersMap.Get(gvk, obj)
122         if err != nil {
123                 return nil, err
124         }
125         return i.Informer, err
126 }
127
128 // IndexField adds an indexer to the underlying cache, using extraction function to get
129 // value(s) from the given field.  This index can then be used by passing a field selector
130 // to List. For one-to-one compatibility with "normal" field selectors, only return one value.
131 // The values may be anything.  They will automatically be prefixed with the namespace of the
132 // given object, if present.  The objects passed are guaranteed to be objects of the correct type.
133 func (ip *informerCache) IndexField(obj runtime.Object, field string, extractValue client.IndexerFunc) error {
134         informer, err := ip.GetInformer(obj)
135         if err != nil {
136                 return err
137         }
138         return indexByField(informer.GetIndexer(), field, extractValue)
139 }
140
141 func indexByField(indexer cache.Indexer, field string, extractor client.IndexerFunc) error {
142         indexFunc := func(objRaw interface{}) ([]string, error) {
143                 // TODO(directxman12): check if this is the correct type?
144                 obj, isObj := objRaw.(runtime.Object)
145                 if !isObj {
146                         return nil, fmt.Errorf("object of type %T is not an Object", objRaw)
147                 }
148                 meta, err := apimeta.Accessor(obj)
149                 if err != nil {
150                         return nil, err
151                 }
152                 ns := meta.GetNamespace()
153
154                 rawVals := extractor(obj)
155                 var vals []string
156                 if ns == "" {
157                         // if we're not doubling the keys for the namespaced case, just re-use what was returned to us
158                         vals = rawVals
159                 } else {
160                         // if we need to add non-namespaced versions too, double the length
161                         vals = make([]string, len(rawVals)*2)
162                 }
163                 for i, rawVal := range rawVals {
164                         // save a namespaced variant, so that we can ask
165                         // "what are all the object matching a given index *in a given namespace*"
166                         vals[i] = internal.KeyToNamespacedKey(ns, rawVal)
167                         if ns != "" {
168                                 // if we have a namespace, also inject a special index key for listing
169                                 // regardless of the object namespace
170                                 vals[i+len(rawVals)] = internal.KeyToNamespacedKey("", rawVal)
171                         }
172                 }
173
174                 return vals, nil
175         }
176
177         return indexer.AddIndexers(cache.Indexers{internal.FieldIndexName(field): indexFunc})
178 }