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 ×tamp 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 } 469 u.setNestedStringSlice(finalizers, "metadata", "finalizers") 470} 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} 483 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} 522