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