Code refactoring for bpa operator
[icn.git] / cmd / bpa-operator / vendor / sigs.k8s.io / controller-runtime / pkg / cache / internal / cache_reader.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 internal
18
19 import (
20         "context"
21         "fmt"
22         "reflect"
23
24         "k8s.io/apimachinery/pkg/api/errors"
25         apimeta "k8s.io/apimachinery/pkg/api/meta"
26         "k8s.io/apimachinery/pkg/fields"
27         "k8s.io/apimachinery/pkg/labels"
28         "k8s.io/apimachinery/pkg/runtime"
29         "k8s.io/apimachinery/pkg/runtime/schema"
30         "k8s.io/apimachinery/pkg/selection"
31         "k8s.io/client-go/tools/cache"
32         "sigs.k8s.io/controller-runtime/pkg/client"
33         "sigs.k8s.io/controller-runtime/pkg/internal/objectutil"
34 )
35
36 // CacheReader is a CacheReader
37 var _ client.Reader = &CacheReader{}
38
39 // CacheReader wraps a cache.Index to implement the client.CacheReader interface for a single type
40 type CacheReader struct {
41         // indexer is the underlying indexer wrapped by this cache.
42         indexer cache.Indexer
43
44         // groupVersionKind is the group-version-kind of the resource.
45         groupVersionKind schema.GroupVersionKind
46 }
47
48 // Get checks the indexer for the object and writes a copy of it if found
49 func (c *CacheReader) Get(_ context.Context, key client.ObjectKey, out runtime.Object) error {
50         storeKey := objectKeyToStoreKey(key)
51
52         // Lookup the object from the indexer cache
53         obj, exists, err := c.indexer.GetByKey(storeKey)
54         if err != nil {
55                 return err
56         }
57
58         // Not found, return an error
59         if !exists {
60                 // Resource gets transformed into Kind in the error anyway, so this is fine
61                 return errors.NewNotFound(schema.GroupResource{
62                         Group:    c.groupVersionKind.Group,
63                         Resource: c.groupVersionKind.Kind,
64                 }, key.Name)
65         }
66
67         // Verify the result is a runtime.Object
68         if _, isObj := obj.(runtime.Object); !isObj {
69                 // This should never happen
70                 return fmt.Errorf("cache contained %T, which is not an Object", obj)
71         }
72
73         // deep copy to avoid mutating cache
74         // TODO(directxman12): revisit the decision to always deepcopy
75         obj = obj.(runtime.Object).DeepCopyObject()
76
77         // Copy the value of the item in the cache to the returned value
78         // TODO(directxman12): this is a terrible hack, pls fix (we should have deepcopyinto)
79         outVal := reflect.ValueOf(out)
80         objVal := reflect.ValueOf(obj)
81         if !objVal.Type().AssignableTo(outVal.Type()) {
82                 return fmt.Errorf("cache had type %s, but %s was asked for", objVal.Type(), outVal.Type())
83         }
84         reflect.Indirect(outVal).Set(reflect.Indirect(objVal))
85         out.GetObjectKind().SetGroupVersionKind(c.groupVersionKind)
86
87         return nil
88 }
89
90 // List lists items out of the indexer and writes them to out
91 func (c *CacheReader) List(_ context.Context, opts *client.ListOptions, out runtime.Object) error {
92         var objs []interface{}
93         var err error
94
95         if opts != nil && opts.FieldSelector != nil {
96                 // TODO(directxman12): support more complicated field selectors by
97                 // combining multiple indicies, GetIndexers, etc
98                 field, val, requiresExact := requiresExactMatch(opts.FieldSelector)
99                 if !requiresExact {
100                         return fmt.Errorf("non-exact field matches are not supported by the cache")
101                 }
102                 // list all objects by the field selector.  If this is namespaced and we have one, ask for the
103                 // namespaced index key.  Otherwise, ask for the non-namespaced variant by using the fake "all namespaces"
104                 // namespace.
105                 objs, err = c.indexer.ByIndex(FieldIndexName(field), KeyToNamespacedKey(opts.Namespace, val))
106         } else if opts != nil && opts.Namespace != "" {
107                 objs, err = c.indexer.ByIndex(cache.NamespaceIndex, opts.Namespace)
108         } else {
109                 objs = c.indexer.List()
110         }
111         if err != nil {
112                 return err
113         }
114         var labelSel labels.Selector
115         if opts != nil && opts.LabelSelector != nil {
116                 labelSel = opts.LabelSelector
117         }
118
119         filteredItems, err := c.filterListItems(objs, labelSel)
120         if err != nil {
121                 return err
122         }
123
124         return apimeta.SetList(out, filteredItems)
125 }
126
127 func (c *CacheReader) filterListItems(objs []interface{}, labelSel labels.Selector) ([]runtime.Object, error) {
128         runtimeObjs := make([]runtime.Object, 0, len(objs))
129         for _, item := range objs {
130                 obj, isObj := item.(runtime.Object)
131                 if !isObj {
132                         return nil, fmt.Errorf("cache contained %T, which is not an Object", obj)
133                 }
134                 runtimeObjs = append(runtimeObjs, obj)
135         }
136
137         filteredItems, err := objectutil.FilterWithLabels(runtimeObjs, labelSel)
138         if err != nil {
139                 return nil, err
140         }
141
142         return filteredItems, nil
143 }
144
145 // objectKeyToStorageKey converts an object key to store key.
146 // It's akin to MetaNamespaceKeyFunc.  It's separate from
147 // String to allow keeping the key format easily in sync with
148 // MetaNamespaceKeyFunc.
149 func objectKeyToStoreKey(k client.ObjectKey) string {
150         if k.Namespace == "" {
151                 return k.Name
152         }
153         return k.Namespace + "/" + k.Name
154 }
155
156 // requiresExactMatch checks if the given field selector is of the form `k=v` or `k==v`.
157 func requiresExactMatch(sel fields.Selector) (field, val string, required bool) {
158         reqs := sel.Requirements()
159         if len(reqs) != 1 {
160                 return "", "", false
161         }
162         req := reqs[0]
163         if req.Operator != selection.Equals && req.Operator != selection.DoubleEquals {
164                 return "", "", false
165         }
166         return req.Field, req.Value, true
167 }
168
169 // FieldIndexName constructs the name of the index over the given field,
170 // for use with an indexer.
171 func FieldIndexName(field string) string {
172         return "field:" + field
173 }
174
175 // noNamespaceNamespace is used as the "namespace" when we want to list across all namespaces
176 const allNamespacesNamespace = "__all_namespaces"
177
178 // KeyToNamespacedKey prefixes the given index key with a namespace
179 // for use in field selector indexes.
180 func KeyToNamespacedKey(ns string, baseKey string) string {
181         if ns != "" {
182                 return ns + "/" + baseKey
183         }
184         return allNamespacesNamespace + "/" + baseKey
185 }