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