1/*
2Copyright 2015 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 unstructured
18
19import (
20	"bytes"
21	"errors"
22	"fmt"
23
24	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
25	"k8s.io/apimachinery/pkg/runtime"
26	"k8s.io/apimachinery/pkg/runtime/schema"
27	"k8s.io/apimachinery/pkg/types"
28	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
29)
30
31// Unstructured allows objects that do not have Golang structs registered to be manipulated
32// generically. This can be used to deal with the API objects from a plug-in. Unstructured
33// objects still have functioning TypeMeta features-- kind, version, etc.
34//
35// WARNING: This object has accessors for the v1 standard metadata. You *MUST NOT* use this
36// type if you are dealing with objects that are not in the server meta v1 schema.
37//
38// TODO: make the serialization part of this type distinct from the field accessors.
39// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
40// +k8s:deepcopy-gen=true
41type Unstructured struct {
42	// Object is a JSON compatible map with string, float, int, bool, []interface{}, or
43	// map[string]interface{}
44	// children.
45	Object map[string]interface{}
46}
47
48var _ metav1.Object = &Unstructured{}
49var _ runtime.Unstructured = &Unstructured{}
50var _ metav1.ListInterface = &Unstructured{}
51
52func (obj *Unstructured) GetObjectKind() schema.ObjectKind { return obj }
53
54func (obj *Unstructured) IsList() bool {
55	field, ok := obj.Object["items"]
56	if !ok {
57		return false
58	}
59	_, ok = field.([]interface{})
60	return ok
61}
62func (obj *Unstructured) ToList() (*UnstructuredList, error) {
63	if !obj.IsList() {
64		// return an empty list back
65		return &UnstructuredList{Object: obj.Object}, nil
66	}
67
68	ret := &UnstructuredList{}
69	ret.Object = obj.Object
70
71	err := obj.EachListItem(func(item runtime.Object) error {
72		castItem := item.(*Unstructured)
73		ret.Items = append(ret.Items, *castItem)
74		return nil
75	})
76	if err != nil {
77		return nil, err
78	}
79
80	return ret, nil
81}
82
83func (obj *Unstructured) EachListItem(fn func(runtime.Object) error) error {
84	field, ok := obj.Object["items"]
85	if !ok {
86		return errors.New("content is not a list")
87	}
88	items, ok := field.([]interface{})
89	if !ok {
90		return fmt.Errorf("content is not a list: %T", field)
91	}
92	for _, item := range items {
93		child, ok := item.(map[string]interface{})
94		if !ok {
95			return fmt.Errorf("items member is not an object: %T", child)
96		}
97		if err := fn(&Unstructured{Object: child}); err != nil {
98			return err
99		}
100	}
101	return nil
102}
103
104func (obj *Unstructured) UnstructuredContent() map[string]interface{} {
105	if obj.Object == nil {
106		return make(map[string]interface{})
107	}
108	return obj.Object
109}
110
111func (obj *Unstructured) SetUnstructuredContent(content map[string]interface{}) {
112	obj.Object = content
113}
114
115// MarshalJSON ensures that the unstructured object produces proper
116// JSON when passed to Go's standard JSON library.
117func (u *Unstructured) MarshalJSON() ([]byte, error) {
118	var buf bytes.Buffer
119	err := UnstructuredJSONScheme.Encode(u, &buf)
120	return buf.Bytes(), err
121}
122
123// UnmarshalJSON ensures that the unstructured object properly decodes
124// JSON when passed to Go's standard JSON library.
125func (u *Unstructured) UnmarshalJSON(b []byte) error {
126	_, _, err := UnstructuredJSONScheme.Decode(b, nil, u)
127	return err
128}
129
130// NewEmptyInstance returns a new instance of the concrete type containing only kind/apiVersion and no other data.
131// This should be called instead of reflect.New() for unstructured types because the go type alone does not preserve kind/apiVersion info.
132func (in *Unstructured) NewEmptyInstance() runtime.Unstructured {
133	out := new(Unstructured)
134	if in != nil {
135		out.GetObjectKind().SetGroupVersionKind(in.GetObjectKind().GroupVersionKind())
136	}
137	return out
138}
139
140func (in *Unstructured) DeepCopy() *Unstructured {
141	if in == nil {
142		return nil
143	}
144	out := new(Unstructured)
145	*out = *in
146	out.Object = runtime.DeepCopyJSON(in.Object)
147	return out
148}
149
150func (u *Unstructured) setNestedField(value interface{}, fields ...string) {
151	if u.Object == nil {
152		u.Object = make(map[string]interface{})
153	}
154	SetNestedField(u.Object, value, fields...)
155}
156
157func (u *Unstructured) setNestedStringSlice(value []string, fields ...string) {
158	if u.Object == nil {
159		u.Object = make(map[string]interface{})
160	}
161	SetNestedStringSlice(u.Object, value, fields...)
162}
163
164func (u *Unstructured) setNestedSlice(value []interface{}, fields ...string) {
165	if u.Object == nil {
166		u.Object = make(map[string]interface{})
167	}
168	SetNestedSlice(u.Object, value, fields...)
169}
170
171func (u *Unstructured) setNestedMap(value map[string]string, fields ...string) {
172	if u.Object == nil {
173		u.Object = make(map[string]interface{})
174	}
175	SetNestedStringMap(u.Object, value, fields...)
176}
177
178func (u *Unstructured) GetOwnerReferences() []metav1.OwnerReference {
179	field, found, err := NestedFieldNoCopy(u.Object, "metadata", "ownerReferences")
180	if !found || err != nil {
181		return nil
182	}
183	original, ok := field.([]interface{})
184	if !ok {
185		return nil
186	}
187	ret := make([]metav1.OwnerReference, 0, len(original))
188	for _, obj := range original {
189		o, ok := obj.(map[string]interface{})
190		if !ok {
191			// expected map[string]interface{}, got something else
192			return nil
193		}
194		ret = append(ret, extractOwnerReference(o))
195	}
196	return ret
197}
198
199func (u *Unstructured) SetOwnerReferences(references []metav1.OwnerReference) {
200	if references == nil {
201		RemoveNestedField(u.Object, "metadata", "ownerReferences")
202		return
203	}
204
205	newReferences := make([]interface{}, 0, len(references))
206	for _, reference := range references {
207		out, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&reference)
208		if err != nil {
209			utilruntime.HandleError(fmt.Errorf("unable to convert Owner Reference: %v", err))
210			continue
211		}
212		newReferences = append(newReferences, out)
213	}
214	u.setNestedField(newReferences, "metadata", "ownerReferences")
215}
216
217func (u *Unstructured) GetAPIVersion() string {
218	return getNestedString(u.Object, "apiVersion")
219}
220
221func (u *Unstructured) SetAPIVersion(version string) {
222	u.setNestedField(version, "apiVersion")
223}
224
225func (u *Unstructured) GetKind() string {
226	return getNestedString(u.Object, "kind")
227}
228
229func (u *Unstructured) SetKind(kind string) {
230	u.setNestedField(kind, "kind")
231}
232
233func (u *Unstructured) GetNamespace() string {
234	return getNestedString(u.Object, "metadata", "namespace")
235}
236
237func (u *Unstructured) SetNamespace(namespace string) {
238	if len(namespace) == 0 {
239		RemoveNestedField(u.Object, "metadata", "namespace")
240		return
241	}
242	u.setNestedField(namespace, "metadata", "namespace")
243}
244
245func (u *Unstructured) GetName() string {
246	return getNestedString(u.Object, "metadata", "name")
247}
248
249func (u *Unstructured) SetName(name string) {
250	if len(name) == 0 {
251		RemoveNestedField(u.Object, "metadata", "name")
252		return
253	}
254	u.setNestedField(name, "metadata", "name")
255}
256
257func (u *Unstructured) GetGenerateName() string {
258	return getNestedString(u.Object, "metadata", "generateName")
259}
260
261func (u *Unstructured) SetGenerateName(generateName string) {
262	if len(generateName) == 0 {
263		RemoveNestedField(u.Object, "metadata", "generateName")
264		return
265	}
266	u.setNestedField(generateName, "metadata", "generateName")
267}
268
269func (u *Unstructured) GetUID() types.UID {
270	return types.UID(getNestedString(u.Object, "metadata", "uid"))
271}
272
273func (u *Unstructured) SetUID(uid types.UID) {
274	if len(string(uid)) == 0 {
275		RemoveNestedField(u.Object, "metadata", "uid")
276		return
277	}
278	u.setNestedField(string(uid), "metadata", "uid")
279}
280
281func (u *Unstructured) GetResourceVersion() string {
282	return getNestedString(u.Object, "metadata", "resourceVersion")
283}
284
285func (u *Unstructured) SetResourceVersion(resourceVersion string) {
286	if len(resourceVersion) == 0 {
287		RemoveNestedField(u.Object, "metadata", "resourceVersion")
288		return
289	}
290	u.setNestedField(resourceVersion, "metadata", "resourceVersion")
291}
292
293func (u *Unstructured) GetGeneration() int64 {
294	val, found, err := NestedInt64(u.Object, "metadata", "generation")
295	if !found || err != nil {
296		return 0
297	}
298	return val
299}
300
301func (u *Unstructured) SetGeneration(generation int64) {
302	if generation == 0 {
303		RemoveNestedField(u.Object, "metadata", "generation")
304		return
305	}
306	u.setNestedField(generation, "metadata", "generation")
307}
308
309func (u *Unstructured) GetSelfLink() string {
310	return getNestedString(u.Object, "metadata", "selfLink")
311}
312
313func (u *Unstructured) SetSelfLink(selfLink string) {
314	if len(selfLink) == 0 {
315		RemoveNestedField(u.Object, "metadata", "selfLink")
316		return
317	}
318	u.setNestedField(selfLink, "metadata", "selfLink")
319}
320
321func (u *Unstructured) GetContinue() string {
322	return getNestedString(u.Object, "metadata", "continue")
323}
324
325func (u *Unstructured) SetContinue(c string) {
326	if len(c) == 0 {
327		RemoveNestedField(u.Object, "metadata", "continue")
328		return
329	}
330	u.setNestedField(c, "metadata", "continue")
331}
332
333func (u *Unstructured) GetRemainingItemCount() *int64 {
334	return getNestedInt64Pointer(u.Object, "metadata", "remainingItemCount")
335}
336
337func (u *Unstructured) SetRemainingItemCount(c *int64) {
338	if c == nil {
339		RemoveNestedField(u.Object, "metadata", "remainingItemCount")
340	} else {
341		u.setNestedField(*c, "metadata", "remainingItemCount")
342	}
343}
344
345func (u *Unstructured) GetCreationTimestamp() metav1.Time {
346	var timestamp metav1.Time
347	timestamp.UnmarshalQueryParameter(getNestedString(u.Object, "metadata", "creationTimestamp"))
348	return timestamp
349}
350
351func (u *Unstructured) SetCreationTimestamp(timestamp metav1.Time) {
352	ts, _ := timestamp.MarshalQueryParameter()
353	if len(ts) == 0 || timestamp.Time.IsZero() {
354		RemoveNestedField(u.Object, "metadata", "creationTimestamp")
355		return
356	}
357	u.setNestedField(ts, "metadata", "creationTimestamp")
358}
359
360func (u *Unstructured) GetDeletionTimestamp() *metav1.Time {
361	var timestamp metav1.Time
362	timestamp.UnmarshalQueryParameter(getNestedString(u.Object, "metadata", "deletionTimestamp"))
363	if timestamp.IsZero() {
364		return nil
365	}
366	return &timestamp
367}
368
369func (u *Unstructured) SetDeletionTimestamp(timestamp *metav1.Time) {
370	if timestamp == nil {
371		RemoveNestedField(u.Object, "metadata", "deletionTimestamp")
372		return
373	}
374	ts, _ := timestamp.MarshalQueryParameter()
375	u.setNestedField(ts, "metadata", "deletionTimestamp")
376}
377
378func (u *Unstructured) GetDeletionGracePeriodSeconds() *int64 {
379	val, found, err := NestedInt64(u.Object, "metadata", "deletionGracePeriodSeconds")
380	if !found || err != nil {
381		return nil
382	}
383	return &val
384}
385
386func (u *Unstructured) SetDeletionGracePeriodSeconds(deletionGracePeriodSeconds *int64) {
387	if deletionGracePeriodSeconds == nil {
388		RemoveNestedField(u.Object, "metadata", "deletionGracePeriodSeconds")
389		return
390	}
391	u.setNestedField(*deletionGracePeriodSeconds, "metadata", "deletionGracePeriodSeconds")
392}
393
394func (u *Unstructured) GetLabels() map[string]string {
395	m, _, _ := NestedStringMap(u.Object, "metadata", "labels")
396	return m
397}
398
399func (u *Unstructured) SetLabels(labels map[string]string) {
400	if labels == nil {
401		RemoveNestedField(u.Object, "metadata", "labels")
402		return
403	}
404	u.setNestedMap(labels, "metadata", "labels")
405}
406
407func (u *Unstructured) GetAnnotations() map[string]string {
408	m, _, _ := NestedStringMap(u.Object, "metadata", "annotations")
409	return m
410}
411
412func (u *Unstructured) SetAnnotations(annotations map[string]string) {
413	if annotations == nil {
414		RemoveNestedField(u.Object, "metadata", "annotations")
415		return
416	}
417	u.setNestedMap(annotations, "metadata", "annotations")
418}
419
420func (u *Unstructured) SetGroupVersionKind(gvk schema.GroupVersionKind) {
421	u.SetAPIVersion(gvk.GroupVersion().String())
422	u.SetKind(gvk.Kind)
423}
424
425func (u *Unstructured) GroupVersionKind() schema.GroupVersionKind {
426	gv, err := schema.ParseGroupVersion(u.GetAPIVersion())
427	if err != nil {
428		return schema.GroupVersionKind{}
429	}
430	gvk := gv.WithKind(u.GetKind())
431	return gvk
432}
433
434func (u *Unstructured) GetFinalizers() []string {
435	val, _, _ := NestedStringSlice(u.Object, "metadata", "finalizers")
436	return val
437}
438
439func (u *Unstructured) SetFinalizers(finalizers []string) {
440	if finalizers == nil {
441		RemoveNestedField(u.Object, "metadata", "finalizers")
442		return
443	}
444	u.setNestedStringSlice(finalizers, "metadata", "finalizers")
445}
446
447func (u *Unstructured) GetClusterName() string {
448	return getNestedString(u.Object, "metadata", "clusterName")
449}
450
451func (u *Unstructured) SetClusterName(clusterName string) {
452	if len(clusterName) == 0 {
453		RemoveNestedField(u.Object, "metadata", "clusterName")
454		return
455	}
456	u.setNestedField(clusterName, "metadata", "clusterName")
457}
458
459func (u *Unstructured) GetManagedFields() []metav1.ManagedFieldsEntry {
460	items, found, err := NestedSlice(u.Object, "metadata", "managedFields")
461	if !found || err != nil {
462		return nil
463	}
464	managedFields := []metav1.ManagedFieldsEntry{}
465	for _, item := range items {
466		m, ok := item.(map[string]interface{})
467		if !ok {
468			utilruntime.HandleError(fmt.Errorf("unable to retrieve managedFields for object, item %v is not a map", item))
469			return nil
470		}
471		out := metav1.ManagedFieldsEntry{}
472		if err := runtime.DefaultUnstructuredConverter.FromUnstructured(m, &out); err != nil {
473			utilruntime.HandleError(fmt.Errorf("unable to retrieve managedFields for object: %v", err))
474			return nil
475		}
476		managedFields = append(managedFields, out)
477	}
478	return managedFields
479}
480
481func (u *Unstructured) SetManagedFields(managedFields []metav1.ManagedFieldsEntry) {
482	if managedFields == nil {
483		RemoveNestedField(u.Object, "metadata", "managedFields")
484		return
485	}
486	items := []interface{}{}
487	for _, managedFieldsEntry := range managedFields {
488		out, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&managedFieldsEntry)
489		if err != nil {
490			utilruntime.HandleError(fmt.Errorf("unable to set managedFields for object: %v", err))
491			return
492		}
493		items = append(items, out)
494	}
495	u.setNestedSlice(items, "metadata", "managedFields")
496}
497