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) 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