blob: 781469ec2657e6ee4161b899bd7285a47a5d7a78 [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{}
50
51func (obj *Unstructured) GetObjectKind() schema.ObjectKind { return obj }
52
53func (obj *Unstructured) IsList() bool {
54 field, ok := obj.Object["items"]
55 if !ok {
56 return false
57 }
58 _, ok = field.([]interface{})
59 return ok
60}
61func (obj *Unstructured) ToList() (*UnstructuredList, error) {
62 if !obj.IsList() {
63 // return an empty list back
64 return &UnstructuredList{Object: obj.Object}, nil
65 }
66
67 ret := &UnstructuredList{}
68 ret.Object = obj.Object
69
70 err := obj.EachListItem(func(item runtime.Object) error {
71 castItem := item.(*Unstructured)
72 ret.Items = append(ret.Items, *castItem)
73 return nil
74 })
75 if err != nil {
76 return nil, err
77 }
78
79 return ret, nil
80}
81
82func (obj *Unstructured) EachListItem(fn func(runtime.Object) error) error {
83 field, ok := obj.Object["items"]
84 if !ok {
85 return errors.New("content is not a list")
86 }
87 items, ok := field.([]interface{})
88 if !ok {
89 return fmt.Errorf("content is not a list: %T", field)
90 }
91 for _, item := range items {
92 child, ok := item.(map[string]interface{})
93 if !ok {
94 return fmt.Errorf("items member is not an object: %T", child)
95 }
96 if err := fn(&Unstructured{Object: child}); err != nil {
97 return err
98 }
99 }
100 return nil
101}
102
103func (obj *Unstructured) UnstructuredContent() map[string]interface{} {
104 if obj.Object == nil {
105 return make(map[string]interface{})
106 }
107 return obj.Object
108}
109
110func (obj *Unstructured) SetUnstructuredContent(content map[string]interface{}) {
111 obj.Object = content
112}
113
114// MarshalJSON ensures that the unstructured object produces proper
115// JSON when passed to Go's standard JSON library.
116func (u *Unstructured) MarshalJSON() ([]byte, error) {
117 var buf bytes.Buffer
118 err := UnstructuredJSONScheme.Encode(u, &buf)
119 return buf.Bytes(), err
120}
121
122// UnmarshalJSON ensures that the unstructured object properly decodes
123// JSON when passed to Go's standard JSON library.
124func (u *Unstructured) UnmarshalJSON(b []byte) error {
125 _, _, err := UnstructuredJSONScheme.Decode(b, nil, u)
126 return err
127}
128
129func (in *Unstructured) DeepCopy() *Unstructured {
130 if in == nil {
131 return nil
132 }
133 out := new(Unstructured)
134 *out = *in
135 out.Object = runtime.DeepCopyJSON(in.Object)
136 return out
137}
138
139func (u *Unstructured) setNestedField(value interface{}, fields ...string) {
140 if u.Object == nil {
141 u.Object = make(map[string]interface{})
142 }
143 SetNestedField(u.Object, value, fields...)
144}
145
146func (u *Unstructured) setNestedSlice(value []string, fields ...string) {
147 if u.Object == nil {
148 u.Object = make(map[string]interface{})
149 }
150 SetNestedStringSlice(u.Object, value, fields...)
151}
152
153func (u *Unstructured) setNestedMap(value map[string]string, fields ...string) {
154 if u.Object == nil {
155 u.Object = make(map[string]interface{})
156 }
157 SetNestedStringMap(u.Object, value, fields...)
158}
159
160func (u *Unstructured) GetOwnerReferences() []metav1.OwnerReference {
161 field, found, err := NestedFieldNoCopy(u.Object, "metadata", "ownerReferences")
162 if !found || err != nil {
163 return nil
164 }
165 original, ok := field.([]interface{})
166 if !ok {
167 return nil
168 }
169 ret := make([]metav1.OwnerReference, 0, len(original))
170 for _, obj := range original {
171 o, ok := obj.(map[string]interface{})
172 if !ok {
173 // expected map[string]interface{}, got something else
174 return nil
175 }
176 ret = append(ret, extractOwnerReference(o))
177 }
178 return ret
179}
180
181func (u *Unstructured) SetOwnerReferences(references []metav1.OwnerReference) {
182 if references == nil {
183 RemoveNestedField(u.Object, "metadata", "ownerReferences")
184 return
185 }
186
187 newReferences := make([]interface{}, 0, len(references))
188 for _, reference := range references {
189 out, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&reference)
190 if err != nil {
191 utilruntime.HandleError(fmt.Errorf("unable to convert Owner Reference: %v", err))
192 continue
193 }
194 newReferences = append(newReferences, out)
195 }
196 u.setNestedField(newReferences, "metadata", "ownerReferences")
197}
198
199func (u *Unstructured) GetAPIVersion() string {
200 return getNestedString(u.Object, "apiVersion")
201}
202
203func (u *Unstructured) SetAPIVersion(version string) {
204 u.setNestedField(version, "apiVersion")
205}
206
207func (u *Unstructured) GetKind() string {
208 return getNestedString(u.Object, "kind")
209}
210
211func (u *Unstructured) SetKind(kind string) {
212 u.setNestedField(kind, "kind")
213}
214
215func (u *Unstructured) GetNamespace() string {
216 return getNestedString(u.Object, "metadata", "namespace")
217}
218
219func (u *Unstructured) SetNamespace(namespace string) {
220 if len(namespace) == 0 {
221 RemoveNestedField(u.Object, "metadata", "namespace")
222 return
223 }
224 u.setNestedField(namespace, "metadata", "namespace")
225}
226
227func (u *Unstructured) GetName() string {
228 return getNestedString(u.Object, "metadata", "name")
229}
230
231func (u *Unstructured) SetName(name string) {
232 if len(name) == 0 {
233 RemoveNestedField(u.Object, "metadata", "name")
234 return
235 }
236 u.setNestedField(name, "metadata", "name")
237}
238
239func (u *Unstructured) GetGenerateName() string {
240 return getNestedString(u.Object, "metadata", "generateName")
241}
242
243func (u *Unstructured) SetGenerateName(generateName string) {
244 if len(generateName) == 0 {
245 RemoveNestedField(u.Object, "metadata", "generateName")
246 return
247 }
248 u.setNestedField(generateName, "metadata", "generateName")
249}
250
251func (u *Unstructured) GetUID() types.UID {
252 return types.UID(getNestedString(u.Object, "metadata", "uid"))
253}
254
255func (u *Unstructured) SetUID(uid types.UID) {
256 if len(string(uid)) == 0 {
257 RemoveNestedField(u.Object, "metadata", "uid")
258 return
259 }
260 u.setNestedField(string(uid), "metadata", "uid")
261}
262
263func (u *Unstructured) GetResourceVersion() string {
264 return getNestedString(u.Object, "metadata", "resourceVersion")
265}
266
267func (u *Unstructured) SetResourceVersion(resourceVersion string) {
268 if len(resourceVersion) == 0 {
269 RemoveNestedField(u.Object, "metadata", "resourceVersion")
270 return
271 }
272 u.setNestedField(resourceVersion, "metadata", "resourceVersion")
273}
274
275func (u *Unstructured) GetGeneration() int64 {
276 val, found, err := NestedInt64(u.Object, "metadata", "generation")
277 if !found || err != nil {
278 return 0
279 }
280 return val
281}
282
283func (u *Unstructured) SetGeneration(generation int64) {
284 if generation == 0 {
285 RemoveNestedField(u.Object, "metadata", "generation")
286 return
287 }
288 u.setNestedField(generation, "metadata", "generation")
289}
290
291func (u *Unstructured) GetSelfLink() string {
292 return getNestedString(u.Object, "metadata", "selfLink")
293}
294
295func (u *Unstructured) SetSelfLink(selfLink string) {
296 if len(selfLink) == 0 {
297 RemoveNestedField(u.Object, "metadata", "selfLink")
298 return
299 }
300 u.setNestedField(selfLink, "metadata", "selfLink")
301}
302
303func (u *Unstructured) GetContinue() string {
304 return getNestedString(u.Object, "metadata", "continue")
305}
306
307func (u *Unstructured) SetContinue(c string) {
308 if len(c) == 0 {
309 RemoveNestedField(u.Object, "metadata", "continue")
310 return
311 }
312 u.setNestedField(c, "metadata", "continue")
313}
314
315func (u *Unstructured) GetCreationTimestamp() metav1.Time {
316 var timestamp metav1.Time
317 timestamp.UnmarshalQueryParameter(getNestedString(u.Object, "metadata", "creationTimestamp"))
318 return timestamp
319}
320
321func (u *Unstructured) SetCreationTimestamp(timestamp metav1.Time) {
322 ts, _ := timestamp.MarshalQueryParameter()
323 if len(ts) == 0 || timestamp.Time.IsZero() {
324 RemoveNestedField(u.Object, "metadata", "creationTimestamp")
325 return
326 }
327 u.setNestedField(ts, "metadata", "creationTimestamp")
328}
329
330func (u *Unstructured) GetDeletionTimestamp() *metav1.Time {
331 var timestamp metav1.Time
332 timestamp.UnmarshalQueryParameter(getNestedString(u.Object, "metadata", "deletionTimestamp"))
333 if timestamp.IsZero() {
334 return nil
335 }
336 return &timestamp
337}
338
339func (u *Unstructured) SetDeletionTimestamp(timestamp *metav1.Time) {
340 if timestamp == nil {
341 RemoveNestedField(u.Object, "metadata", "deletionTimestamp")
342 return
343 }
344 ts, _ := timestamp.MarshalQueryParameter()
345 u.setNestedField(ts, "metadata", "deletionTimestamp")
346}
347
348func (u *Unstructured) GetDeletionGracePeriodSeconds() *int64 {
349 val, found, err := NestedInt64(u.Object, "metadata", "deletionGracePeriodSeconds")
350 if !found || err != nil {
351 return nil
352 }
353 return &val
354}
355
356func (u *Unstructured) SetDeletionGracePeriodSeconds(deletionGracePeriodSeconds *int64) {
357 if deletionGracePeriodSeconds == nil {
358 RemoveNestedField(u.Object, "metadata", "deletionGracePeriodSeconds")
359 return
360 }
361 u.setNestedField(*deletionGracePeriodSeconds, "metadata", "deletionGracePeriodSeconds")
362}
363
364func (u *Unstructured) GetLabels() map[string]string {
365 m, _, _ := NestedStringMap(u.Object, "metadata", "labels")
366 return m
367}
368
369func (u *Unstructured) SetLabels(labels map[string]string) {
370 if labels == nil {
371 RemoveNestedField(u.Object, "metadata", "labels")
372 return
373 }
374 u.setNestedMap(labels, "metadata", "labels")
375}
376
377func (u *Unstructured) GetAnnotations() map[string]string {
378 m, _, _ := NestedStringMap(u.Object, "metadata", "annotations")
379 return m
380}
381
382func (u *Unstructured) SetAnnotations(annotations map[string]string) {
383 if annotations == nil {
384 RemoveNestedField(u.Object, "metadata", "annotations")
385 return
386 }
387 u.setNestedMap(annotations, "metadata", "annotations")
388}
389
390func (u *Unstructured) SetGroupVersionKind(gvk schema.GroupVersionKind) {
391 u.SetAPIVersion(gvk.GroupVersion().String())
392 u.SetKind(gvk.Kind)
393}
394
395func (u *Unstructured) GroupVersionKind() schema.GroupVersionKind {
396 gv, err := schema.ParseGroupVersion(u.GetAPIVersion())
397 if err != nil {
398 return schema.GroupVersionKind{}
399 }
400 gvk := gv.WithKind(u.GetKind())
401 return gvk
402}
403
404func (u *Unstructured) GetInitializers() *metav1.Initializers {
405 m, found, err := nestedMapNoCopy(u.Object, "metadata", "initializers")
406 if !found || err != nil {
407 return nil
408 }
409 out := &metav1.Initializers{}
410 if err := runtime.DefaultUnstructuredConverter.FromUnstructured(m, out); err != nil {
411 utilruntime.HandleError(fmt.Errorf("unable to retrieve initializers for object: %v", err))
412 return nil
413 }
414 return out
415}
416
417func (u *Unstructured) SetInitializers(initializers *metav1.Initializers) {
418 if initializers == nil {
419 RemoveNestedField(u.Object, "metadata", "initializers")
420 return
421 }
422 out, err := runtime.DefaultUnstructuredConverter.ToUnstructured(initializers)
423 if err != nil {
424 utilruntime.HandleError(fmt.Errorf("unable to retrieve initializers for object: %v", err))
425 }
426 u.setNestedField(out, "metadata", "initializers")
427}
428
429func (u *Unstructured) GetFinalizers() []string {
430 val, _, _ := NestedStringSlice(u.Object, "metadata", "finalizers")
431 return val
432}
433
434func (u *Unstructured) SetFinalizers(finalizers []string) {
435 if finalizers == nil {
436 RemoveNestedField(u.Object, "metadata", "finalizers")
437 return
438 }
439 u.setNestedSlice(finalizers, "metadata", "finalizers")
440}
441
442func (u *Unstructured) GetClusterName() string {
443 return getNestedString(u.Object, "metadata", "clusterName")
444}
445
446func (u *Unstructured) SetClusterName(clusterName string) {
447 if len(clusterName) == 0 {
448 RemoveNestedField(u.Object, "metadata", "clusterName")
449 return
450 }
451 u.setNestedField(clusterName, "metadata", "clusterName")
452}