blob: 0ba18d45d8dff102e63a70fa9b08276dbb6340ab [file] [log] [blame]
Zack Williamse940c7a2019-08-21 14:25:39 -07001/*
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{}
David Bainbridge86971522019-09-26 22:09:39 +000050var _ metav1.ListInterface = &Unstructured{}
Zack Williamse940c7a2019-08-21 14:25:39 -070051
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
David Bainbridge86971522019-09-26 22:09:39 +0000130// 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
Zack Williamse940c7a2019-08-21 14:25:39 -0700140func (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
David Bainbridge86971522019-09-26 22:09:39 +0000157func (u *Unstructured) setNestedStringSlice(value []string, fields ...string) {
Zack Williamse940c7a2019-08-21 14:25:39 -0700158 if u.Object == nil {
159 u.Object = make(map[string]interface{})
160 }
161 SetNestedStringSlice(u.Object, value, fields...)
162}
163
David Bainbridge86971522019-09-26 22:09:39 +0000164func (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
Zack Williamse940c7a2019-08-21 14:25:39 -0700171func (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
David Bainbridge86971522019-09-26 22:09:39 +0000333func (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
Zack Williamse940c7a2019-08-21 14:25:39 -0700345func (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) GetInitializers() *metav1.Initializers {
435 m, found, err := nestedMapNoCopy(u.Object, "metadata", "initializers")
436 if !found || err != nil {
437 return nil
438 }
439 out := &metav1.Initializers{}
440 if err := runtime.DefaultUnstructuredConverter.FromUnstructured(m, out); err != nil {
441 utilruntime.HandleError(fmt.Errorf("unable to retrieve initializers for object: %v", err))
442 return nil
443 }
444 return out
445}
446
447func (u *Unstructured) SetInitializers(initializers *metav1.Initializers) {
448 if initializers == nil {
449 RemoveNestedField(u.Object, "metadata", "initializers")
450 return
451 }
452 out, err := runtime.DefaultUnstructuredConverter.ToUnstructured(initializers)
453 if err != nil {
454 utilruntime.HandleError(fmt.Errorf("unable to retrieve initializers for object: %v", err))
455 }
456 u.setNestedField(out, "metadata", "initializers")
457}
458
459func (u *Unstructured) GetFinalizers() []string {
460 val, _, _ := NestedStringSlice(u.Object, "metadata", "finalizers")
461 return val
462}
463
464func (u *Unstructured) SetFinalizers(finalizers []string) {
465 if finalizers == nil {
466 RemoveNestedField(u.Object, "metadata", "finalizers")
467 return
468 }
David Bainbridge86971522019-09-26 22:09:39 +0000469 u.setNestedStringSlice(finalizers, "metadata", "finalizers")
Zack Williamse940c7a2019-08-21 14:25:39 -0700470}
471
472func (u *Unstructured) GetClusterName() string {
473 return getNestedString(u.Object, "metadata", "clusterName")
474}
475
476func (u *Unstructured) SetClusterName(clusterName string) {
477 if len(clusterName) == 0 {
478 RemoveNestedField(u.Object, "metadata", "clusterName")
479 return
480 }
481 u.setNestedField(clusterName, "metadata", "clusterName")
482}
David Bainbridge86971522019-09-26 22:09:39 +0000483
484func (u *Unstructured) GetManagedFields() []metav1.ManagedFieldsEntry {
485 items, found, err := NestedSlice(u.Object, "metadata", "managedFields")
486 if !found || err != nil {
487 return nil
488 }
489 managedFields := []metav1.ManagedFieldsEntry{}
490 for _, item := range items {
491 m, ok := item.(map[string]interface{})
492 if !ok {
493 utilruntime.HandleError(fmt.Errorf("unable to retrieve managedFields for object, item %v is not a map", item))
494 return nil
495 }
496 out := metav1.ManagedFieldsEntry{}
497 if err := runtime.DefaultUnstructuredConverter.FromUnstructured(m, &out); err != nil {
498 utilruntime.HandleError(fmt.Errorf("unable to retrieve managedFields for object: %v", err))
499 return nil
500 }
501 managedFields = append(managedFields, out)
502 }
503 return managedFields
504}
505
506func (u *Unstructured) SetManagedFields(managedFields []metav1.ManagedFieldsEntry) {
507 if managedFields == nil {
508 RemoveNestedField(u.Object, "metadata", "managedFields")
509 return
510 }
511 items := []interface{}{}
512 for _, managedFieldsEntry := range managedFields {
513 out, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&managedFieldsEntry)
514 if err != nil {
515 utilruntime.HandleError(fmt.Errorf("unable to set managedFields for object: %v", err))
516 return
517 }
518 items = append(items, out)
519 }
520 u.setNestedSlice(items, "metadata", "managedFields")
521}