1/*
2Copyright 2014 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package meta
18
19import (
20	"fmt"
21	"reflect"
22
23	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
24	"k8s.io/apimachinery/pkg/conversion"
25	"k8s.io/apimachinery/pkg/runtime"
26	"k8s.io/apimachinery/pkg/runtime/schema"
27	"k8s.io/apimachinery/pkg/types"
28	"k8s.io/klog/v2"
29)
30
31// errNotList is returned when an object implements the Object style interfaces but not the List style
32// interfaces.
33var errNotList = fmt.Errorf("object does not implement the List interfaces")
34
35var errNotCommon = fmt.Errorf("object does not implement the common interface for accessing the SelfLink")
36
37// CommonAccessor returns a Common interface for the provided object or an error if the object does
38// not provide List.
39func CommonAccessor(obj interface{}) (metav1.Common, error) {
40	switch t := obj.(type) {
41	case List:
42		return t, nil
43	case ListMetaAccessor:
44		if m := t.GetListMeta(); m != nil {
45			return m, nil
46		}
47		return nil, errNotCommon
48	case metav1.ListMetaAccessor:
49		if m := t.GetListMeta(); m != nil {
50			return m, nil
51		}
52		return nil, errNotCommon
53	case metav1.Object:
54		return t, nil
55	case metav1.ObjectMetaAccessor:
56		if m := t.GetObjectMeta(); m != nil {
57			return m, nil
58		}
59		return nil, errNotCommon
60	default:
61		return nil, errNotCommon
62	}
63}
64
65// ListAccessor returns a List interface for the provided object or an error if the object does
66// not provide List.
67// IMPORTANT: Objects are NOT a superset of lists. Do not use this check to determine whether an
68// object *is* a List.
69func ListAccessor(obj interface{}) (List, error) {
70	switch t := obj.(type) {
71	case List:
72		return t, nil
73	case ListMetaAccessor:
74		if m := t.GetListMeta(); m != nil {
75			return m, nil
76		}
77		return nil, errNotList
78	case metav1.ListMetaAccessor:
79		if m := t.GetListMeta(); m != nil {
80			return m, nil
81		}
82		return nil, errNotList
83	default:
84		return nil, errNotList
85	}
86}
87
88// errNotObject is returned when an object implements the List style interfaces but not the Object style
89// interfaces.
90var errNotObject = fmt.Errorf("object does not implement the Object interfaces")
91
92// Accessor takes an arbitrary object pointer and returns meta.Interface.
93// obj must be a pointer to an API type. An error is returned if the minimum
94// required fields are missing. Fields that are not required return the default
95// value and are a no-op if set.
96func Accessor(obj interface{}) (metav1.Object, error) {
97	switch t := obj.(type) {
98	case metav1.Object:
99		return t, nil
100	case metav1.ObjectMetaAccessor:
101		if m := t.GetObjectMeta(); m != nil {
102			return m, nil
103		}
104		return nil, errNotObject
105	default:
106		return nil, errNotObject
107	}
108}
109
110// AsPartialObjectMetadata takes the metav1 interface and returns a partial object.
111// TODO: consider making this solely a conversion action.
112func AsPartialObjectMetadata(m metav1.Object) *metav1.PartialObjectMetadata {
113	switch t := m.(type) {
114	case *metav1.ObjectMeta:
115		return &metav1.PartialObjectMetadata{ObjectMeta: *t}
116	default:
117		return &metav1.PartialObjectMetadata{
118			ObjectMeta: metav1.ObjectMeta{
119				Name:                       m.GetName(),
120				GenerateName:               m.GetGenerateName(),
121				Namespace:                  m.GetNamespace(),
122				SelfLink:                   m.GetSelfLink(),
123				UID:                        m.GetUID(),
124				ResourceVersion:            m.GetResourceVersion(),
125				Generation:                 m.GetGeneration(),
126				CreationTimestamp:          m.GetCreationTimestamp(),
127				DeletionTimestamp:          m.GetDeletionTimestamp(),
128				DeletionGracePeriodSeconds: m.GetDeletionGracePeriodSeconds(),
129				Labels:                     m.GetLabels(),
130				Annotations:                m.GetAnnotations(),
131				OwnerReferences:            m.GetOwnerReferences(),
132				Finalizers:                 m.GetFinalizers(),
133				ClusterName:                m.GetClusterName(),
134				ManagedFields:              m.GetManagedFields(),
135			},
136		}
137	}
138}
139
140// TypeAccessor returns an interface that allows retrieving and modifying the APIVersion
141// and Kind of an in-memory internal object.
142// TODO: this interface is used to test code that does not have ObjectMeta or ListMeta
143// in round tripping (objects which can use apiVersion/kind, but do not fit the Kube
144// api conventions).
145func TypeAccessor(obj interface{}) (Type, error) {
146	if typed, ok := obj.(runtime.Object); ok {
147		return objectAccessor{typed}, nil
148	}
149	v, err := conversion.EnforcePtr(obj)
150	if err != nil {
151		return nil, err
152	}
153	t := v.Type()
154	if v.Kind() != reflect.Struct {
155		return nil, fmt.Errorf("expected struct, but got %v: %v (%#v)", v.Kind(), t, v.Interface())
156	}
157
158	typeMeta := v.FieldByName("TypeMeta")
159	if !typeMeta.IsValid() {
160		return nil, fmt.Errorf("struct %v lacks embedded TypeMeta type", t)
161	}
162	a := &genericAccessor{}
163	if err := extractFromTypeMeta(typeMeta, a); err != nil {
164		return nil, fmt.Errorf("unable to find type fields on %#v: %v", typeMeta, err)
165	}
166	return a, nil
167}
168
169type objectAccessor struct {
170	runtime.Object
171}
172
173func (obj objectAccessor) GetKind() string {
174	return obj.GetObjectKind().GroupVersionKind().Kind
175}
176
177func (obj objectAccessor) SetKind(kind string) {
178	gvk := obj.GetObjectKind().GroupVersionKind()
179	gvk.Kind = kind
180	obj.GetObjectKind().SetGroupVersionKind(gvk)
181}
182
183func (obj objectAccessor) GetAPIVersion() string {
184	return obj.GetObjectKind().GroupVersionKind().GroupVersion().String()
185}
186
187func (obj objectAccessor) SetAPIVersion(version string) {
188	gvk := obj.GetObjectKind().GroupVersionKind()
189	gv, err := schema.ParseGroupVersion(version)
190	if err != nil {
191		gv = schema.GroupVersion{Version: version}
192	}
193	gvk.Group, gvk.Version = gv.Group, gv.Version
194	obj.GetObjectKind().SetGroupVersionKind(gvk)
195}
196
197// NewAccessor returns a MetadataAccessor that can retrieve
198// or manipulate resource version on objects derived from core API
199// metadata concepts.
200func NewAccessor() MetadataAccessor {
201	return resourceAccessor{}
202}
203
204// resourceAccessor implements ResourceVersioner and SelfLinker.
205type resourceAccessor struct{}
206
207func (resourceAccessor) Kind(obj runtime.Object) (string, error) {
208	return objectAccessor{obj}.GetKind(), nil
209}
210
211func (resourceAccessor) SetKind(obj runtime.Object, kind string) error {
212	objectAccessor{obj}.SetKind(kind)
213	return nil
214}
215
216func (resourceAccessor) APIVersion(obj runtime.Object) (string, error) {
217	return objectAccessor{obj}.GetAPIVersion(), nil
218}
219
220func (resourceAccessor) SetAPIVersion(obj runtime.Object, version string) error {
221	objectAccessor{obj}.SetAPIVersion(version)
222	return nil
223}
224
225func (resourceAccessor) Namespace(obj runtime.Object) (string, error) {
226	accessor, err := Accessor(obj)
227	if err != nil {
228		return "", err
229	}
230	return accessor.GetNamespace(), nil
231}
232
233func (resourceAccessor) SetNamespace(obj runtime.Object, namespace string) error {
234	accessor, err := Accessor(obj)
235	if err != nil {
236		return err
237	}
238	accessor.SetNamespace(namespace)
239	return nil
240}
241
242func (resourceAccessor) Name(obj runtime.Object) (string, error) {
243	accessor, err := Accessor(obj)
244	if err != nil {
245		return "", err
246	}
247	return accessor.GetName(), nil
248}
249
250func (resourceAccessor) SetName(obj runtime.Object, name string) error {
251	accessor, err := Accessor(obj)
252	if err != nil {
253		return err
254	}
255	accessor.SetName(name)
256	return nil
257}
258
259func (resourceAccessor) GenerateName(obj runtime.Object) (string, error) {
260	accessor, err := Accessor(obj)
261	if err != nil {
262		return "", err
263	}
264	return accessor.GetGenerateName(), nil
265}
266
267func (resourceAccessor) SetGenerateName(obj runtime.Object, name string) error {
268	accessor, err := Accessor(obj)
269	if err != nil {
270		return err
271	}
272	accessor.SetGenerateName(name)
273	return nil
274}
275
276func (resourceAccessor) UID(obj runtime.Object) (types.UID, error) {
277	accessor, err := Accessor(obj)
278	if err != nil {
279		return "", err
280	}
281	return accessor.GetUID(), nil
282}
283
284func (resourceAccessor) SetUID(obj runtime.Object, uid types.UID) error {
285	accessor, err := Accessor(obj)
286	if err != nil {
287		return err
288	}
289	accessor.SetUID(uid)
290	return nil
291}
292
293func (resourceAccessor) SelfLink(obj runtime.Object) (string, error) {
294	accessor, err := CommonAccessor(obj)
295	if err != nil {
296		return "", err
297	}
298	return accessor.GetSelfLink(), nil
299}
300
301func (resourceAccessor) SetSelfLink(obj runtime.Object, selfLink string) error {
302	accessor, err := CommonAccessor(obj)
303	if err != nil {
304		return err
305	}
306	accessor.SetSelfLink(selfLink)
307	return nil
308}
309
310func (resourceAccessor) Labels(obj runtime.Object) (map[string]string, error) {
311	accessor, err := Accessor(obj)
312	if err != nil {
313		return nil, err
314	}
315	return accessor.GetLabels(), nil
316}
317
318func (resourceAccessor) SetLabels(obj runtime.Object, labels map[string]string) error {
319	accessor, err := Accessor(obj)
320	if err != nil {
321		return err
322	}
323	accessor.SetLabels(labels)
324	return nil
325}
326
327func (resourceAccessor) Annotations(obj runtime.Object) (map[string]string, error) {
328	accessor, err := Accessor(obj)
329	if err != nil {
330		return nil, err
331	}
332	return accessor.GetAnnotations(), nil
333}
334
335func (resourceAccessor) SetAnnotations(obj runtime.Object, annotations map[string]string) error {
336	accessor, err := Accessor(obj)
337	if err != nil {
338		return err
339	}
340	accessor.SetAnnotations(annotations)
341	return nil
342}
343
344func (resourceAccessor) ResourceVersion(obj runtime.Object) (string, error) {
345	accessor, err := CommonAccessor(obj)
346	if err != nil {
347		return "", err
348	}
349	return accessor.GetResourceVersion(), nil
350}
351
352func (resourceAccessor) SetResourceVersion(obj runtime.Object, version string) error {
353	accessor, err := CommonAccessor(obj)
354	if err != nil {
355		return err
356	}
357	accessor.SetResourceVersion(version)
358	return nil
359}
360
361func (resourceAccessor) Continue(obj runtime.Object) (string, error) {
362	accessor, err := ListAccessor(obj)
363	if err != nil {
364		return "", err
365	}
366	return accessor.GetContinue(), nil
367}
368
369func (resourceAccessor) SetContinue(obj runtime.Object, version string) error {
370	accessor, err := ListAccessor(obj)
371	if err != nil {
372		return err
373	}
374	accessor.SetContinue(version)
375	return nil
376}
377
378// extractFromOwnerReference extracts v to o. v is the OwnerReferences field of an object.
379func extractFromOwnerReference(v reflect.Value, o *metav1.OwnerReference) error {
380	if err := runtime.Field(v, "APIVersion", &o.APIVersion); err != nil {
381		return err
382	}
383	if err := runtime.Field(v, "Kind", &o.Kind); err != nil {
384		return err
385	}
386	if err := runtime.Field(v, "Name", &o.Name); err != nil {
387		return err
388	}
389	if err := runtime.Field(v, "UID", &o.UID); err != nil {
390		return err
391	}
392	var controllerPtr *bool
393	if err := runtime.Field(v, "Controller", &controllerPtr); err != nil {
394		return err
395	}
396	if controllerPtr != nil {
397		controller := *controllerPtr
398		o.Controller = &controller
399	}
400	var blockOwnerDeletionPtr *bool
401	if err := runtime.Field(v, "BlockOwnerDeletion", &blockOwnerDeletionPtr); err != nil {
402		return err
403	}
404	if blockOwnerDeletionPtr != nil {
405		block := *blockOwnerDeletionPtr
406		o.BlockOwnerDeletion = &block
407	}
408	return nil
409}
410
411// setOwnerReference sets v to o. v is the OwnerReferences field of an object.
412func setOwnerReference(v reflect.Value, o *metav1.OwnerReference) error {
413	if err := runtime.SetField(o.APIVersion, v, "APIVersion"); err != nil {
414		return err
415	}
416	if err := runtime.SetField(o.Kind, v, "Kind"); err != nil {
417		return err
418	}
419	if err := runtime.SetField(o.Name, v, "Name"); err != nil {
420		return err
421	}
422	if err := runtime.SetField(o.UID, v, "UID"); err != nil {
423		return err
424	}
425	if o.Controller != nil {
426		controller := *(o.Controller)
427		if err := runtime.SetField(&controller, v, "Controller"); err != nil {
428			return err
429		}
430	}
431	if o.BlockOwnerDeletion != nil {
432		block := *(o.BlockOwnerDeletion)
433		if err := runtime.SetField(&block, v, "BlockOwnerDeletion"); err != nil {
434			return err
435		}
436	}
437	return nil
438}
439
440// genericAccessor contains pointers to strings that can modify an arbitrary
441// struct and implements the Accessor interface.
442type genericAccessor struct {
443	namespace         *string
444	name              *string
445	generateName      *string
446	uid               *types.UID
447	apiVersion        *string
448	kind              *string
449	resourceVersion   *string
450	selfLink          *string
451	creationTimestamp *metav1.Time
452	deletionTimestamp **metav1.Time
453	labels            *map[string]string
454	annotations       *map[string]string
455	ownerReferences   reflect.Value
456	finalizers        *[]string
457}
458
459func (a genericAccessor) GetNamespace() string {
460	if a.namespace == nil {
461		return ""
462	}
463	return *a.namespace
464}
465
466func (a genericAccessor) SetNamespace(namespace string) {
467	if a.namespace == nil {
468		return
469	}
470	*a.namespace = namespace
471}
472
473func (a genericAccessor) GetName() string {
474	if a.name == nil {
475		return ""
476	}
477	return *a.name
478}
479
480func (a genericAccessor) SetName(name string) {
481	if a.name == nil {
482		return
483	}
484	*a.name = name
485}
486
487func (a genericAccessor) GetGenerateName() string {
488	if a.generateName == nil {
489		return ""
490	}
491	return *a.generateName
492}
493
494func (a genericAccessor) SetGenerateName(generateName string) {
495	if a.generateName == nil {
496		return
497	}
498	*a.generateName = generateName
499}
500
501func (a genericAccessor) GetUID() types.UID {
502	if a.uid == nil {
503		return ""
504	}
505	return *a.uid
506}
507
508func (a genericAccessor) SetUID(uid types.UID) {
509	if a.uid == nil {
510		return
511	}
512	*a.uid = uid
513}
514
515func (a genericAccessor) GetAPIVersion() string {
516	return *a.apiVersion
517}
518
519func (a genericAccessor) SetAPIVersion(version string) {
520	*a.apiVersion = version
521}
522
523func (a genericAccessor) GetKind() string {
524	return *a.kind
525}
526
527func (a genericAccessor) SetKind(kind string) {
528	*a.kind = kind
529}
530
531func (a genericAccessor) GetResourceVersion() string {
532	return *a.resourceVersion
533}
534
535func (a genericAccessor) SetResourceVersion(version string) {
536	*a.resourceVersion = version
537}
538
539func (a genericAccessor) GetSelfLink() string {
540	return *a.selfLink
541}
542
543func (a genericAccessor) SetSelfLink(selfLink string) {
544	*a.selfLink = selfLink
545}
546
547func (a genericAccessor) GetCreationTimestamp() metav1.Time {
548	return *a.creationTimestamp
549}
550
551func (a genericAccessor) SetCreationTimestamp(timestamp metav1.Time) {
552	*a.creationTimestamp = timestamp
553}
554
555func (a genericAccessor) GetDeletionTimestamp() *metav1.Time {
556	return *a.deletionTimestamp
557}
558
559func (a genericAccessor) SetDeletionTimestamp(timestamp *metav1.Time) {
560	*a.deletionTimestamp = timestamp
561}
562
563func (a genericAccessor) GetLabels() map[string]string {
564	if a.labels == nil {
565		return nil
566	}
567	return *a.labels
568}
569
570func (a genericAccessor) SetLabels(labels map[string]string) {
571	*a.labels = labels
572}
573
574func (a genericAccessor) GetAnnotations() map[string]string {
575	if a.annotations == nil {
576		return nil
577	}
578	return *a.annotations
579}
580
581func (a genericAccessor) SetAnnotations(annotations map[string]string) {
582	if a.annotations == nil {
583		emptyAnnotations := make(map[string]string)
584		a.annotations = &emptyAnnotations
585	}
586	*a.annotations = annotations
587}
588
589func (a genericAccessor) GetFinalizers() []string {
590	if a.finalizers == nil {
591		return nil
592	}
593	return *a.finalizers
594}
595
596func (a genericAccessor) SetFinalizers(finalizers []string) {
597	*a.finalizers = finalizers
598}
599
600func (a genericAccessor) GetOwnerReferences() []metav1.OwnerReference {
601	var ret []metav1.OwnerReference
602	s := a.ownerReferences
603	if s.Kind() != reflect.Ptr || s.Elem().Kind() != reflect.Slice {
604		klog.Errorf("expect %v to be a pointer to slice", s)
605		return ret
606	}
607	s = s.Elem()
608	// Set the capacity to one element greater to avoid copy if the caller later append an element.
609	ret = make([]metav1.OwnerReference, s.Len(), s.Len()+1)
610	for i := 0; i < s.Len(); i++ {
611		if err := extractFromOwnerReference(s.Index(i), &ret[i]); err != nil {
612			klog.Errorf("extractFromOwnerReference failed: %v", err)
613			return ret
614		}
615	}
616	return ret
617}
618
619func (a genericAccessor) SetOwnerReferences(references []metav1.OwnerReference) {
620	s := a.ownerReferences
621	if s.Kind() != reflect.Ptr || s.Elem().Kind() != reflect.Slice {
622		klog.Errorf("expect %v to be a pointer to slice", s)
623	}
624	s = s.Elem()
625	newReferences := reflect.MakeSlice(s.Type(), len(references), len(references))
626	for i := 0; i < len(references); i++ {
627		if err := setOwnerReference(newReferences.Index(i), &references[i]); err != nil {
628			klog.Errorf("setOwnerReference failed: %v", err)
629			return
630		}
631	}
632	s.Set(newReferences)
633}
634
635// extractFromTypeMeta extracts pointers to version and kind fields from an object
636func extractFromTypeMeta(v reflect.Value, a *genericAccessor) error {
637	if err := runtime.FieldPtr(v, "APIVersion", &a.apiVersion); err != nil {
638		return err
639	}
640	if err := runtime.FieldPtr(v, "Kind", &a.kind); err != nil {
641		return err
642	}
643	return nil
644}
645