k8s.io/client-go@v0.31.1/metadata/fake/simple.go (about) 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 fake 18 19 import ( 20 "context" 21 "fmt" 22 "strings" 23 24 "k8s.io/apimachinery/pkg/api/meta" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 "k8s.io/apimachinery/pkg/labels" 27 "k8s.io/apimachinery/pkg/runtime" 28 "k8s.io/apimachinery/pkg/runtime/schema" 29 "k8s.io/apimachinery/pkg/runtime/serializer" 30 "k8s.io/apimachinery/pkg/types" 31 "k8s.io/apimachinery/pkg/watch" 32 "k8s.io/client-go/metadata" 33 "k8s.io/client-go/testing" 34 ) 35 36 // MetadataClient assists in creating fake objects for use when testing, since metadata.Getter 37 // does not expose create 38 type MetadataClient interface { 39 metadata.Getter 40 CreateFake(obj *metav1.PartialObjectMetadata, opts metav1.CreateOptions, subresources ...string) (*metav1.PartialObjectMetadata, error) 41 UpdateFake(obj *metav1.PartialObjectMetadata, opts metav1.UpdateOptions, subresources ...string) (*metav1.PartialObjectMetadata, error) 42 } 43 44 // NewTestScheme creates a unique Scheme for each test. 45 func NewTestScheme() *runtime.Scheme { 46 return runtime.NewScheme() 47 } 48 49 // NewSimpleMetadataClient creates a new client that will use the provided scheme and respond with the 50 // provided objects when requests are made. It will track actions made to the client which can be checked 51 // with GetActions(). 52 func NewSimpleMetadataClient(scheme *runtime.Scheme, objects ...runtime.Object) *FakeMetadataClient { 53 gvkFakeList := schema.GroupVersionKind{Group: "fake-metadata-client-group", Version: "v1", Kind: "List"} 54 if !scheme.Recognizes(gvkFakeList) { 55 // In order to use List with this client, you have to have the v1.List registered in your scheme, since this is a test 56 // type we modify the input scheme 57 scheme.AddKnownTypeWithName(gvkFakeList, &metav1.List{}) 58 } 59 60 codecs := serializer.NewCodecFactory(scheme) 61 o := testing.NewObjectTracker(scheme, codecs.UniversalDeserializer()) 62 for _, obj := range objects { 63 if err := o.Add(obj); err != nil { 64 panic(err) 65 } 66 } 67 68 cs := &FakeMetadataClient{scheme: scheme, tracker: o} 69 cs.AddReactor("*", "*", testing.ObjectReaction(o)) 70 cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) { 71 gvr := action.GetResource() 72 ns := action.GetNamespace() 73 watch, err := o.Watch(gvr, ns) 74 if err != nil { 75 return false, nil, err 76 } 77 return true, watch, nil 78 }) 79 80 return cs 81 } 82 83 // FakeMetadataClient implements clientset.Interface. Meant to be embedded into a 84 // struct to get a default implementation. This makes faking out just the method 85 // you want to test easier. 86 type FakeMetadataClient struct { 87 testing.Fake 88 scheme *runtime.Scheme 89 tracker testing.ObjectTracker 90 } 91 92 type metadataResourceClient struct { 93 client *FakeMetadataClient 94 namespace string 95 resource schema.GroupVersionResource 96 } 97 98 var ( 99 _ metadata.Interface = &FakeMetadataClient{} 100 _ testing.FakeClient = &FakeMetadataClient{} 101 ) 102 103 func (c *FakeMetadataClient) Tracker() testing.ObjectTracker { 104 return c.tracker 105 } 106 107 // Resource returns an interface for accessing the provided resource. 108 func (c *FakeMetadataClient) Resource(resource schema.GroupVersionResource) metadata.Getter { 109 return &metadataResourceClient{client: c, resource: resource} 110 } 111 112 // Namespace returns an interface for accessing the current resource in the specified 113 // namespace. 114 func (c *metadataResourceClient) Namespace(ns string) metadata.ResourceInterface { 115 ret := *c 116 ret.namespace = ns 117 return &ret 118 } 119 120 // CreateFake records the object creation and processes it via the reactor. 121 func (c *metadataResourceClient) CreateFake(obj *metav1.PartialObjectMetadata, opts metav1.CreateOptions, subresources ...string) (*metav1.PartialObjectMetadata, error) { 122 var uncastRet runtime.Object 123 var err error 124 switch { 125 case len(c.namespace) == 0 && len(subresources) == 0: 126 uncastRet, err = c.client.Fake. 127 Invokes(testing.NewRootCreateAction(c.resource, obj), obj) 128 129 case len(c.namespace) == 0 && len(subresources) > 0: 130 var accessor metav1.Object // avoid shadowing err 131 accessor, err = meta.Accessor(obj) 132 if err != nil { 133 return nil, err 134 } 135 name := accessor.GetName() 136 uncastRet, err = c.client.Fake. 137 Invokes(testing.NewRootCreateSubresourceAction(c.resource, name, strings.Join(subresources, "/"), obj), obj) 138 139 case len(c.namespace) > 0 && len(subresources) == 0: 140 uncastRet, err = c.client.Fake. 141 Invokes(testing.NewCreateAction(c.resource, c.namespace, obj), obj) 142 143 case len(c.namespace) > 0 && len(subresources) > 0: 144 var accessor metav1.Object // avoid shadowing err 145 accessor, err = meta.Accessor(obj) 146 if err != nil { 147 return nil, err 148 } 149 name := accessor.GetName() 150 uncastRet, err = c.client.Fake. 151 Invokes(testing.NewCreateSubresourceAction(c.resource, name, strings.Join(subresources, "/"), c.namespace, obj), obj) 152 153 } 154 155 if err != nil { 156 return nil, err 157 } 158 if uncastRet == nil { 159 return nil, err 160 } 161 ret, ok := uncastRet.(*metav1.PartialObjectMetadata) 162 if !ok { 163 return nil, fmt.Errorf("unexpected return value type %T", uncastRet) 164 } 165 return ret, err 166 } 167 168 // UpdateFake records the object update and processes it via the reactor. 169 func (c *metadataResourceClient) UpdateFake(obj *metav1.PartialObjectMetadata, opts metav1.UpdateOptions, subresources ...string) (*metav1.PartialObjectMetadata, error) { 170 var uncastRet runtime.Object 171 var err error 172 switch { 173 case len(c.namespace) == 0 && len(subresources) == 0: 174 uncastRet, err = c.client.Fake. 175 Invokes(testing.NewRootUpdateAction(c.resource, obj), obj) 176 177 case len(c.namespace) == 0 && len(subresources) > 0: 178 uncastRet, err = c.client.Fake. 179 Invokes(testing.NewRootUpdateSubresourceAction(c.resource, strings.Join(subresources, "/"), obj), obj) 180 181 case len(c.namespace) > 0 && len(subresources) == 0: 182 uncastRet, err = c.client.Fake. 183 Invokes(testing.NewUpdateAction(c.resource, c.namespace, obj), obj) 184 185 case len(c.namespace) > 0 && len(subresources) > 0: 186 uncastRet, err = c.client.Fake. 187 Invokes(testing.NewUpdateSubresourceAction(c.resource, strings.Join(subresources, "/"), c.namespace, obj), obj) 188 189 } 190 191 if err != nil { 192 return nil, err 193 } 194 if uncastRet == nil { 195 return nil, err 196 } 197 ret, ok := uncastRet.(*metav1.PartialObjectMetadata) 198 if !ok { 199 return nil, fmt.Errorf("unexpected return value type %T", uncastRet) 200 } 201 return ret, err 202 } 203 204 // UpdateStatus records the object status update and processes it via the reactor. 205 func (c *metadataResourceClient) UpdateStatus(obj *metav1.PartialObjectMetadata, opts metav1.UpdateOptions) (*metav1.PartialObjectMetadata, error) { 206 var uncastRet runtime.Object 207 var err error 208 switch { 209 case len(c.namespace) == 0: 210 uncastRet, err = c.client.Fake. 211 Invokes(testing.NewRootUpdateSubresourceAction(c.resource, "status", obj), obj) 212 213 case len(c.namespace) > 0: 214 uncastRet, err = c.client.Fake. 215 Invokes(testing.NewUpdateSubresourceAction(c.resource, "status", c.namespace, obj), obj) 216 217 } 218 219 if err != nil { 220 return nil, err 221 } 222 if uncastRet == nil { 223 return nil, err 224 } 225 ret, ok := uncastRet.(*metav1.PartialObjectMetadata) 226 if !ok { 227 return nil, fmt.Errorf("unexpected return value type %T", uncastRet) 228 } 229 return ret, err 230 } 231 232 // Delete records the object deletion and processes it via the reactor. 233 func (c *metadataResourceClient) Delete(ctx context.Context, name string, opts metav1.DeleteOptions, subresources ...string) error { 234 var err error 235 switch { 236 case len(c.namespace) == 0 && len(subresources) == 0: 237 _, err = c.client.Fake. 238 Invokes(testing.NewRootDeleteAction(c.resource, name), &metav1.Status{Status: "metadata delete fail"}) 239 240 case len(c.namespace) == 0 && len(subresources) > 0: 241 _, err = c.client.Fake. 242 Invokes(testing.NewRootDeleteSubresourceAction(c.resource, strings.Join(subresources, "/"), name), &metav1.Status{Status: "metadata delete fail"}) 243 244 case len(c.namespace) > 0 && len(subresources) == 0: 245 _, err = c.client.Fake. 246 Invokes(testing.NewDeleteAction(c.resource, c.namespace, name), &metav1.Status{Status: "metadata delete fail"}) 247 248 case len(c.namespace) > 0 && len(subresources) > 0: 249 _, err = c.client.Fake. 250 Invokes(testing.NewDeleteSubresourceAction(c.resource, strings.Join(subresources, "/"), c.namespace, name), &metav1.Status{Status: "metadata delete fail"}) 251 } 252 253 return err 254 } 255 256 // DeleteCollection records the object collection deletion and processes it via the reactor. 257 func (c *metadataResourceClient) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOptions metav1.ListOptions) error { 258 var err error 259 switch { 260 case len(c.namespace) == 0: 261 action := testing.NewRootDeleteCollectionAction(c.resource, listOptions) 262 _, err = c.client.Fake.Invokes(action, &metav1.Status{Status: "metadata deletecollection fail"}) 263 264 case len(c.namespace) > 0: 265 action := testing.NewDeleteCollectionAction(c.resource, c.namespace, listOptions) 266 _, err = c.client.Fake.Invokes(action, &metav1.Status{Status: "metadata deletecollection fail"}) 267 268 } 269 270 return err 271 } 272 273 // Get records the object retrieval and processes it via the reactor. 274 func (c *metadataResourceClient) Get(ctx context.Context, name string, opts metav1.GetOptions, subresources ...string) (*metav1.PartialObjectMetadata, error) { 275 var uncastRet runtime.Object 276 var err error 277 switch { 278 case len(c.namespace) == 0 && len(subresources) == 0: 279 uncastRet, err = c.client.Fake. 280 Invokes(testing.NewRootGetAction(c.resource, name), &metav1.Status{Status: "metadata get fail"}) 281 282 case len(c.namespace) == 0 && len(subresources) > 0: 283 uncastRet, err = c.client.Fake. 284 Invokes(testing.NewRootGetSubresourceAction(c.resource, strings.Join(subresources, "/"), name), &metav1.Status{Status: "metadata get fail"}) 285 286 case len(c.namespace) > 0 && len(subresources) == 0: 287 uncastRet, err = c.client.Fake. 288 Invokes(testing.NewGetAction(c.resource, c.namespace, name), &metav1.Status{Status: "metadata get fail"}) 289 290 case len(c.namespace) > 0 && len(subresources) > 0: 291 uncastRet, err = c.client.Fake. 292 Invokes(testing.NewGetSubresourceAction(c.resource, c.namespace, strings.Join(subresources, "/"), name), &metav1.Status{Status: "metadata get fail"}) 293 } 294 295 if err != nil { 296 return nil, err 297 } 298 if uncastRet == nil { 299 return nil, err 300 } 301 ret, ok := uncastRet.(*metav1.PartialObjectMetadata) 302 if !ok { 303 return nil, fmt.Errorf("unexpected return value type %T", uncastRet) 304 } 305 return ret, err 306 } 307 308 // List records the object deletion and processes it via the reactor. 309 func (c *metadataResourceClient) List(ctx context.Context, opts metav1.ListOptions) (*metav1.PartialObjectMetadataList, error) { 310 var obj runtime.Object 311 var err error 312 switch { 313 case len(c.namespace) == 0: 314 obj, err = c.client.Fake. 315 Invokes(testing.NewRootListAction(c.resource, schema.GroupVersionKind{Group: "fake-metadata-client-group", Version: "v1", Kind: "" /*List is appended by the tracker automatically*/}, opts), &metav1.Status{Status: "metadata list fail"}) 316 317 case len(c.namespace) > 0: 318 obj, err = c.client.Fake. 319 Invokes(testing.NewListAction(c.resource, schema.GroupVersionKind{Group: "fake-metadata-client-group", Version: "v1", Kind: "" /*List is appended by the tracker automatically*/}, c.namespace, opts), &metav1.Status{Status: "metadata list fail"}) 320 321 } 322 323 if obj == nil { 324 return nil, err 325 } 326 327 label, _, _ := testing.ExtractFromListOptions(opts) 328 if label == nil { 329 label = labels.Everything() 330 } 331 332 inputList, ok := obj.(*metav1.List) 333 if !ok { 334 return nil, fmt.Errorf("incoming object is incorrect type %T", obj) 335 } 336 337 list := &metav1.PartialObjectMetadataList{ 338 ListMeta: inputList.ListMeta, 339 } 340 for i := range inputList.Items { 341 item, ok := inputList.Items[i].Object.(*metav1.PartialObjectMetadata) 342 if !ok { 343 return nil, fmt.Errorf("item %d in list %T is %T", i, inputList, inputList.Items[i].Object) 344 } 345 metadata, err := meta.Accessor(item) 346 if err != nil { 347 return nil, err 348 } 349 if label.Matches(labels.Set(metadata.GetLabels())) { 350 list.Items = append(list.Items, *item) 351 } 352 } 353 return list, nil 354 } 355 356 func (c *metadataResourceClient) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) { 357 switch { 358 case len(c.namespace) == 0: 359 return c.client.Fake. 360 InvokesWatch(testing.NewRootWatchAction(c.resource, opts)) 361 362 case len(c.namespace) > 0: 363 return c.client.Fake. 364 InvokesWatch(testing.NewWatchAction(c.resource, c.namespace, opts)) 365 366 } 367 368 panic("math broke") 369 } 370 371 // Patch records the object patch and processes it via the reactor. 372 func (c *metadataResourceClient) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (*metav1.PartialObjectMetadata, error) { 373 var uncastRet runtime.Object 374 var err error 375 switch { 376 case len(c.namespace) == 0 && len(subresources) == 0: 377 uncastRet, err = c.client.Fake. 378 Invokes(testing.NewRootPatchAction(c.resource, name, pt, data), &metav1.Status{Status: "metadata patch fail"}) 379 380 case len(c.namespace) == 0 && len(subresources) > 0: 381 uncastRet, err = c.client.Fake. 382 Invokes(testing.NewRootPatchSubresourceAction(c.resource, name, pt, data, subresources...), &metav1.Status{Status: "metadata patch fail"}) 383 384 case len(c.namespace) > 0 && len(subresources) == 0: 385 uncastRet, err = c.client.Fake. 386 Invokes(testing.NewPatchAction(c.resource, c.namespace, name, pt, data), &metav1.Status{Status: "metadata patch fail"}) 387 388 case len(c.namespace) > 0 && len(subresources) > 0: 389 uncastRet, err = c.client.Fake. 390 Invokes(testing.NewPatchSubresourceAction(c.resource, c.namespace, name, pt, data, subresources...), &metav1.Status{Status: "metadata patch fail"}) 391 392 } 393 394 if err != nil { 395 return nil, err 396 } 397 if uncastRet == nil { 398 return nil, err 399 } 400 ret, ok := uncastRet.(*metav1.PartialObjectMetadata) 401 if !ok { 402 return nil, fmt.Errorf("unexpected return value type %T", uncastRet) 403 } 404 return ret, err 405 }