blob: d1903394d7f2ea16ac9c412d5a34c8b495aad312 [file] [log] [blame]
Matteo Scandoloa4285862020-12-01 18:10:10 -08001/*
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}