github.com/aclisp/heapster@v0.19.2-0.20160613100040-51756f899a96/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/scheme.go (about) 1 /* 2 Copyright 2014 The Kubernetes Authors All rights reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package runtime 18 19 import ( 20 "fmt" 21 "net/url" 22 "reflect" 23 24 "k8s.io/kubernetes/pkg/api/unversioned" 25 "k8s.io/kubernetes/pkg/conversion" 26 ) 27 28 // Scheme defines methods for serializing and deserializing API objects, a type 29 // registry for converting group, version, and kind information to and from Go 30 // schemas, and mappings between Go schemas of different versions. A scheme is the 31 // foundation for a versioned API and versioned configuration over time. 32 // 33 // In a Scheme, a Type is a particular Go struct, a Version is a point-in-time 34 // identifier for a particular representation of that Type (typically backwards 35 // compatible), a Kind is the unique name for that Type within the Version, and a 36 // Group identifies a set of Versions, Kinds, and Types that evolve over time. An 37 // Unversioned Type is one that is not yet formally bound to a type and is promised 38 // to be backwards compatible (effectively a "v1" of a Type that does not expect 39 // to break in the future). 40 // 41 // Schemes are not expected to change at runtime and are only threadsafe after 42 // registration is complete. 43 type Scheme struct { 44 // versionMap allows one to figure out the go type of an object with 45 // the given version and name. 46 gvkToType map[unversioned.GroupVersionKind]reflect.Type 47 48 // typeToGroupVersion allows one to find metadata for a given go object. 49 // The reflect.Type we index by should *not* be a pointer. 50 typeToGVK map[reflect.Type][]unversioned.GroupVersionKind 51 52 // unversionedTypes are transformed without conversion in ConvertToVersion. 53 unversionedTypes map[reflect.Type]unversioned.GroupVersionKind 54 55 // unversionedKinds are the names of kinds that can be created in the context of any group 56 // or version 57 // TODO: resolve the status of unversioned types. 58 unversionedKinds map[string]reflect.Type 59 60 // Map from version and resource to the corresponding func to convert 61 // resource field labels in that version to internal version. 62 fieldLabelConversionFuncs map[string]map[string]FieldLabelConversionFunc 63 64 // converter stores all registered conversion functions. It also has 65 // default coverting behavior. 66 converter *conversion.Converter 67 68 // cloner stores all registered copy functions. It also has default 69 // deep copy behavior. 70 cloner *conversion.Cloner 71 } 72 73 // Function to convert a field selector to internal representation. 74 type FieldLabelConversionFunc func(label, value string) (internalLabel, internalValue string, err error) 75 76 // NewScheme creates a new Scheme. This scheme is pluggable by default. 77 func NewScheme() *Scheme { 78 s := &Scheme{ 79 gvkToType: map[unversioned.GroupVersionKind]reflect.Type{}, 80 typeToGVK: map[reflect.Type][]unversioned.GroupVersionKind{}, 81 unversionedTypes: map[reflect.Type]unversioned.GroupVersionKind{}, 82 unversionedKinds: map[string]reflect.Type{}, 83 cloner: conversion.NewCloner(), 84 fieldLabelConversionFuncs: map[string]map[string]FieldLabelConversionFunc{}, 85 } 86 s.converter = conversion.NewConverter(s.nameFunc) 87 88 s.AddConversionFuncs(DefaultEmbeddedConversions()...) 89 90 // Enable map[string][]string conversions by default 91 if err := s.AddConversionFuncs(DefaultStringConversions...); err != nil { 92 panic(err) 93 } 94 if err := s.RegisterInputDefaults(&map[string][]string{}, JSONKeyMapper, conversion.AllowDifferentFieldTypeNames|conversion.IgnoreMissingFields); err != nil { 95 panic(err) 96 } 97 if err := s.RegisterInputDefaults(&url.Values{}, JSONKeyMapper, conversion.AllowDifferentFieldTypeNames|conversion.IgnoreMissingFields); err != nil { 98 panic(err) 99 } 100 return s 101 } 102 103 // nameFunc returns the name of the type that we wish to use to determine when two types attempt 104 // a conversion. Defaults to the go name of the type if the type is not registered. 105 func (s *Scheme) nameFunc(t reflect.Type) string { 106 // find the preferred names for this type 107 gvks, ok := s.typeToGVK[t] 108 if !ok { 109 return t.Name() 110 } 111 112 for _, gvk := range gvks { 113 internalGV := gvk.GroupVersion() 114 internalGV.Version = "__internal" // this is hacky and maybe should be passed in 115 internalGVK := internalGV.WithKind(gvk.Kind) 116 117 if internalType, exists := s.gvkToType[internalGVK]; exists { 118 return s.typeToGVK[internalType][0].Kind 119 } 120 } 121 122 return gvks[0].Kind 123 } 124 125 // fromScope gets the input version, desired output version, and desired Scheme 126 // from a conversion.Scope. 127 func (s *Scheme) fromScope(scope conversion.Scope) *Scheme { 128 return s 129 } 130 131 // Converter allows access to the converter for the scheme 132 func (s *Scheme) Converter() *conversion.Converter { 133 return s.converter 134 } 135 136 // AddUnversionedTypes registers the provided types as "unversioned", which means that they follow special rules. 137 // Whenever an object of this type is serialized, it is serialized with the provided group version and is not 138 // converted. Thus unversioned objects are expected to remain backwards compatible forever, as if they were in an 139 // API group and version that would never be updated. 140 // 141 // TODO: there is discussion about removing unversioned and replacing it with objects that are manifest into 142 // every version with particular schemas. Resolve this method at that point. 143 func (s *Scheme) AddUnversionedTypes(version unversioned.GroupVersion, types ...Object) { 144 s.AddKnownTypes(version, types...) 145 for _, obj := range types { 146 t := reflect.TypeOf(obj).Elem() 147 gvk := version.WithKind(t.Name()) 148 s.unversionedTypes[t] = gvk 149 if _, ok := s.unversionedKinds[gvk.Kind]; ok { 150 panic(fmt.Sprintf("%v has already been registered as unversioned kind %q - kind name must be unique", reflect.TypeOf(t), gvk.Kind)) 151 } 152 s.unversionedKinds[gvk.Kind] = t 153 } 154 } 155 156 // AddKnownTypes registers all types passed in 'types' as being members of version 'version'. 157 // All objects passed to types should be pointers to structs. The name that go reports for 158 // the struct becomes the "kind" field when encoding. Version may not be empty - use the 159 // APIVersionInternal constant if you have a type that does not have a formal version. 160 func (s *Scheme) AddKnownTypes(gv unversioned.GroupVersion, types ...Object) { 161 if len(gv.Version) == 0 { 162 panic(fmt.Sprintf("version is required on all types: %s %v", gv, types[0])) 163 } 164 for _, obj := range types { 165 t := reflect.TypeOf(obj) 166 if t.Kind() != reflect.Ptr { 167 panic("All types must be pointers to structs.") 168 } 169 t = t.Elem() 170 if t.Kind() != reflect.Struct { 171 panic("All types must be pointers to structs.") 172 } 173 174 gvk := gv.WithKind(t.Name()) 175 s.gvkToType[gvk] = t 176 s.typeToGVK[t] = append(s.typeToGVK[t], gvk) 177 } 178 } 179 180 // AddKnownTypeWithName is like AddKnownTypes, but it lets you specify what this type should 181 // be encoded as. Useful for testing when you don't want to make multiple packages to define 182 // your structs. Version may not be empty - use the APIVersionInternal constant if you have a 183 // type that does not have a formal version. 184 func (s *Scheme) AddKnownTypeWithName(gvk unversioned.GroupVersionKind, obj Object) { 185 t := reflect.TypeOf(obj) 186 if len(gvk.Version) == 0 { 187 panic(fmt.Sprintf("version is required on all types: %s %v", gvk, t)) 188 } 189 if t.Kind() != reflect.Ptr { 190 panic("All types must be pointers to structs.") 191 } 192 t = t.Elem() 193 if t.Kind() != reflect.Struct { 194 panic("All types must be pointers to structs.") 195 } 196 197 s.gvkToType[gvk] = t 198 s.typeToGVK[t] = append(s.typeToGVK[t], gvk) 199 } 200 201 // KnownTypes returns the types known for the given version. 202 func (s *Scheme) KnownTypes(gv unversioned.GroupVersion) map[string]reflect.Type { 203 types := make(map[string]reflect.Type) 204 for gvk, t := range s.gvkToType { 205 if gv != gvk.GroupVersion() { 206 continue 207 } 208 209 types[gvk.Kind] = t 210 } 211 return types 212 } 213 214 // ObjectKind returns the group,version,kind of the go object, 215 // or an error if it's not a pointer or is unregistered. 216 func (s *Scheme) ObjectKind(obj Object) (unversioned.GroupVersionKind, error) { 217 gvks, err := s.ObjectKinds(obj) 218 if err != nil { 219 return unversioned.GroupVersionKind{}, err 220 } 221 return gvks[0], nil 222 } 223 224 // ObjectKinds returns all possible group,version,kind of the go object, 225 // or an error if it's not a pointer or is unregistered. 226 func (s *Scheme) ObjectKinds(obj Object) ([]unversioned.GroupVersionKind, error) { 227 v, err := conversion.EnforcePtr(obj) 228 if err != nil { 229 return nil, err 230 } 231 t := v.Type() 232 233 gvks, ok := s.typeToGVK[t] 234 if !ok { 235 return nil, ¬RegisteredErr{t: t} 236 } 237 238 return gvks, nil 239 } 240 241 // Recognizes returns true if the scheme is able to handle the provided group,version,kind 242 // of an object. 243 func (s *Scheme) Recognizes(gvk unversioned.GroupVersionKind) bool { 244 _, exists := s.gvkToType[gvk] 245 return exists 246 } 247 248 func (s *Scheme) IsUnversioned(obj Object) (bool, bool) { 249 v, err := conversion.EnforcePtr(obj) 250 if err != nil { 251 return false, false 252 } 253 t := v.Type() 254 255 if _, ok := s.typeToGVK[t]; !ok { 256 return false, false 257 } 258 _, ok := s.unversionedTypes[t] 259 return ok, true 260 } 261 262 // New returns a new API object of the given version and name, or an error if it hasn't 263 // been registered. The version and kind fields must be specified. 264 func (s *Scheme) New(kind unversioned.GroupVersionKind) (Object, error) { 265 if t, exists := s.gvkToType[kind]; exists { 266 return reflect.New(t).Interface().(Object), nil 267 } 268 269 if t, exists := s.unversionedKinds[kind.Kind]; exists { 270 return reflect.New(t).Interface().(Object), nil 271 } 272 return nil, ¬RegisteredErr{gvk: kind} 273 } 274 275 // AddGenericConversionFunc adds a function that accepts the ConversionFunc call pattern 276 // (for two conversion types) to the converter. These functions are checked first during 277 // a normal conversion, but are otherwise not called. Use AddConversionFuncs when registering 278 // typed conversions. 279 func (s *Scheme) AddGenericConversionFunc(fn conversion.GenericConversionFunc) { 280 s.converter.AddGenericConversionFunc(fn) 281 } 282 283 // Log sets a logger on the scheme. For test purposes only 284 func (s *Scheme) Log(l conversion.DebugLogger) { 285 s.converter.Debug = l 286 } 287 288 // AddIgnoredConversionType identifies a pair of types that should be skipped by 289 // conversion (because the data inside them is explicitly dropped during 290 // conversion). 291 func (s *Scheme) AddIgnoredConversionType(from, to interface{}) error { 292 return s.converter.RegisterIgnoredConversion(from, to) 293 } 294 295 // AddConversionFuncs adds functions to the list of conversion functions. The given 296 // functions should know how to convert between two of your API objects, or their 297 // sub-objects. We deduce how to call these functions from the types of their two 298 // parameters; see the comment for Converter.Register. 299 // 300 // Note that, if you need to copy sub-objects that didn't change, you can use the 301 // conversion.Scope object that will be passed to your conversion function. 302 // Additionally, all conversions started by Scheme will set the SrcVersion and 303 // DestVersion fields on the Meta object. Example: 304 // 305 // s.AddConversionFuncs( 306 // func(in *InternalObject, out *ExternalObject, scope conversion.Scope) error { 307 // // You can depend on Meta() being non-nil, and this being set to 308 // // the source version, e.g., "" 309 // s.Meta().SrcVersion 310 // // You can depend on this being set to the destination version, 311 // // e.g., "v1". 312 // s.Meta().DestVersion 313 // // Call scope.Convert to copy sub-fields. 314 // s.Convert(&in.SubFieldThatMoved, &out.NewLocation.NewName, 0) 315 // return nil 316 // }, 317 // ) 318 // 319 // (For more detail about conversion functions, see Converter.Register's comment.) 320 // 321 // Also note that the default behavior, if you don't add a conversion function, is to 322 // sanely copy fields that have the same names and same type names. It's OK if the 323 // destination type has extra fields, but it must not remove any. So you only need to 324 // add conversion functions for things with changed/removed fields. 325 func (s *Scheme) AddConversionFuncs(conversionFuncs ...interface{}) error { 326 for _, f := range conversionFuncs { 327 if err := s.converter.RegisterConversionFunc(f); err != nil { 328 return err 329 } 330 } 331 return nil 332 } 333 334 // Similar to AddConversionFuncs, but registers conversion functions that were 335 // automatically generated. 336 func (s *Scheme) AddGeneratedConversionFuncs(conversionFuncs ...interface{}) error { 337 for _, f := range conversionFuncs { 338 if err := s.converter.RegisterGeneratedConversionFunc(f); err != nil { 339 return err 340 } 341 } 342 return nil 343 } 344 345 // AddDeepCopyFuncs adds a function to the list of deep-copy functions. 346 // For the expected format of deep-copy function, see the comment for 347 // Copier.RegisterDeepCopyFunction. 348 func (s *Scheme) AddDeepCopyFuncs(deepCopyFuncs ...interface{}) error { 349 for _, f := range deepCopyFuncs { 350 if err := s.cloner.RegisterDeepCopyFunc(f); err != nil { 351 return err 352 } 353 } 354 return nil 355 } 356 357 // Similar to AddDeepCopyFuncs, but registers deep-copy functions that were 358 // automatically generated. 359 func (s *Scheme) AddGeneratedDeepCopyFuncs(deepCopyFuncs ...interface{}) error { 360 for _, f := range deepCopyFuncs { 361 if err := s.cloner.RegisterGeneratedDeepCopyFunc(f); err != nil { 362 return err 363 } 364 } 365 return nil 366 } 367 368 // AddFieldLabelConversionFunc adds a conversion function to convert field selectors 369 // of the given kind from the given version to internal version representation. 370 func (s *Scheme) AddFieldLabelConversionFunc(version, kind string, conversionFunc FieldLabelConversionFunc) error { 371 if s.fieldLabelConversionFuncs[version] == nil { 372 s.fieldLabelConversionFuncs[version] = map[string]FieldLabelConversionFunc{} 373 } 374 375 s.fieldLabelConversionFuncs[version][kind] = conversionFunc 376 return nil 377 } 378 379 // AddStructFieldConversion allows you to specify a mechanical copy for a moved 380 // or renamed struct field without writing an entire conversion function. See 381 // the comment in conversion.Converter.SetStructFieldCopy for parameter details. 382 // Call as many times as needed, even on the same fields. 383 func (s *Scheme) AddStructFieldConversion(srcFieldType interface{}, srcFieldName string, destFieldType interface{}, destFieldName string) error { 384 return s.converter.SetStructFieldCopy(srcFieldType, srcFieldName, destFieldType, destFieldName) 385 } 386 387 // RegisterInputDefaults sets the provided field mapping function and field matching 388 // as the defaults for the provided input type. The fn may be nil, in which case no 389 // mapping will happen by default. Use this method to register a mechanism for handling 390 // a specific input type in conversion, such as a map[string]string to structs. 391 func (s *Scheme) RegisterInputDefaults(in interface{}, fn conversion.FieldMappingFunc, defaultFlags conversion.FieldMatchingFlags) error { 392 return s.converter.RegisterInputDefaults(in, fn, defaultFlags) 393 } 394 395 // AddDefaultingFuncs adds functions to the list of default-value functions. 396 // Each of the given functions is responsible for applying default values 397 // when converting an instance of a versioned API object into an internal 398 // API object. These functions do not need to handle sub-objects. We deduce 399 // how to call these functions from the types of their two parameters. 400 // 401 // s.AddDefaultingFuncs( 402 // func(obj *v1.Pod) { 403 // if obj.OptionalField == "" { 404 // obj.OptionalField = "DefaultValue" 405 // } 406 // }, 407 // ) 408 func (s *Scheme) AddDefaultingFuncs(defaultingFuncs ...interface{}) error { 409 for _, f := range defaultingFuncs { 410 err := s.converter.RegisterDefaultingFunc(f) 411 if err != nil { 412 return err 413 } 414 } 415 return nil 416 } 417 418 // Copy does a deep copy of an API object. 419 func (s *Scheme) Copy(src Object) (Object, error) { 420 dst, err := s.DeepCopy(src) 421 if err != nil { 422 return nil, err 423 } 424 return dst.(Object), nil 425 } 426 427 // Performs a deep copy of the given object. 428 func (s *Scheme) DeepCopy(src interface{}) (interface{}, error) { 429 return s.cloner.DeepCopy(src) 430 } 431 432 // Convert will attempt to convert in into out. Both must be pointers. For easy 433 // testing of conversion functions. Returns an error if the conversion isn't 434 // possible. You can call this with types that haven't been registered (for example, 435 // a to test conversion of types that are nested within registered types), but in 436 // that case, the conversion.Scope object passed to your conversion functions won't 437 // have SrcVersion or DestVersion fields set correctly in Meta(). 438 func (s *Scheme) Convert(in, out interface{}) error { 439 inVersion := unversioned.GroupVersion{Group: "unknown", Version: "unknown"} 440 outVersion := unversioned.GroupVersion{Group: "unknown", Version: "unknown"} 441 if inObj, ok := in.(Object); ok { 442 if gvk, err := s.ObjectKind(inObj); err == nil { 443 inVersion = gvk.GroupVersion() 444 } 445 } 446 if outObj, ok := out.(Object); ok { 447 if gvk, err := s.ObjectKind(outObj); err == nil { 448 outVersion = gvk.GroupVersion() 449 } 450 } 451 flags, meta := s.generateConvertMeta(inVersion, outVersion, in) 452 if flags == 0 { 453 flags = conversion.AllowDifferentFieldTypeNames 454 } 455 return s.converter.Convert(in, out, flags, meta) 456 } 457 458 // Converts the given field label and value for an kind field selector from 459 // versioned representation to an unversioned one. 460 func (s *Scheme) ConvertFieldLabel(version, kind, label, value string) (string, string, error) { 461 if s.fieldLabelConversionFuncs[version] == nil { 462 return "", "", fmt.Errorf("No field label conversion function found for version: %s", version) 463 } 464 conversionFunc, ok := s.fieldLabelConversionFuncs[version][kind] 465 if !ok { 466 return "", "", fmt.Errorf("No field label conversion function found for version %s and kind %s", version, kind) 467 } 468 return conversionFunc(label, value) 469 } 470 471 // ConvertToVersion attempts to convert an input object to its matching Kind in another 472 // version within this scheme. Will return an error if the provided version does not 473 // contain the inKind (or a mapping by name defined with AddKnownTypeWithName). Will also 474 // return an error if the conversion does not result in a valid Object being 475 // returned. The serializer handles loading/serializing nested objects. 476 func (s *Scheme) ConvertToVersion(in Object, outVersion unversioned.GroupVersion) (Object, error) { 477 switch in.(type) { 478 case *Unknown, *Unstructured, *UnstructuredList: 479 old := in.GetObjectKind().GroupVersionKind() 480 defer in.GetObjectKind().SetGroupVersionKind(old) 481 setTargetVersion(in, s, outVersion) 482 return in, nil 483 } 484 t := reflect.TypeOf(in) 485 if t.Kind() != reflect.Ptr { 486 return nil, fmt.Errorf("only pointer types may be converted: %v", t) 487 } 488 489 t = t.Elem() 490 if t.Kind() != reflect.Struct { 491 return nil, fmt.Errorf("only pointers to struct types may be converted: %v", t) 492 } 493 494 var kind unversioned.GroupVersionKind 495 if unversionedKind, ok := s.unversionedTypes[t]; ok { 496 kind = unversionedKind 497 } else { 498 kinds, ok := s.typeToGVK[t] 499 if !ok || len(kinds) == 0 { 500 return nil, fmt.Errorf("%v is not a registered type and cannot be converted into version %q", t, outVersion) 501 } 502 kind = kinds[0] 503 } 504 505 outKind := outVersion.WithKind(kind.Kind) 506 507 inKind, err := s.ObjectKind(in) 508 if err != nil { 509 return nil, err 510 } 511 512 out, err := s.New(outKind) 513 if err != nil { 514 return nil, err 515 } 516 517 flags, meta := s.generateConvertMeta(inKind.GroupVersion(), outVersion, in) 518 if err := s.converter.Convert(in, out, flags, meta); err != nil { 519 return nil, err 520 } 521 522 setTargetVersion(out, s, outVersion) 523 return out, nil 524 } 525 526 // UnsafeConvertToVersion will convert in to the provided outVersion if such a conversion is possible, 527 // but does not guarantee the output object does not share fields with the input object. It attempts to be as 528 // efficient as possible when doing conversion. 529 func (s *Scheme) UnsafeConvertToVersion(in Object, outVersion unversioned.GroupVersion) (Object, error) { 530 switch t := in.(type) { 531 case *Unknown: 532 t.APIVersion = outVersion.String() 533 return t, nil 534 case *Unstructured: 535 t.SetAPIVersion(outVersion.String()) 536 return t, nil 537 case *UnstructuredList: 538 t.SetAPIVersion(outVersion.String()) 539 return t, nil 540 } 541 542 // determine the incoming kinds with as few allocations as possible. 543 t := reflect.TypeOf(in) 544 if t.Kind() != reflect.Ptr { 545 return nil, fmt.Errorf("only pointer types may be converted: %v", t) 546 } 547 t = t.Elem() 548 if t.Kind() != reflect.Struct { 549 return nil, fmt.Errorf("only pointers to struct types may be converted: %v", t) 550 } 551 kinds, ok := s.typeToGVK[t] 552 if !ok || len(kinds) == 0 { 553 return nil, fmt.Errorf("%v is not a registered type and cannot be converted into version %q", t, outVersion) 554 } 555 556 // if the Go type is also registered to the destination kind, no conversion is necessary 557 for i := range kinds { 558 if kinds[i].Version == outVersion.Version && kinds[i].Group == outVersion.Group { 559 setTargetKind(in, kinds[i]) 560 return in, nil 561 } 562 } 563 564 // type is unversioned, no conversion necessary 565 // it should be possible to avoid this allocation 566 if unversionedKind, ok := s.unversionedTypes[t]; ok { 567 kind := unversionedKind 568 outKind := outVersion.WithKind(kind.Kind) 569 setTargetKind(in, outKind) 570 return in, nil 571 } 572 573 // allocate a new object as the target using the target kind 574 // TODO: this should look in the target group version and find the first kind that matches, rather than the 575 // first kind registered in typeToGVK 576 kind := kinds[0] 577 kind.Version = outVersion.Version 578 kind.Group = outVersion.Group 579 out, err := s.New(kind) 580 if err != nil { 581 return nil, err 582 } 583 584 // TODO: try to avoid the allocations here - in fast paths we are not likely to need these flags or meta 585 flags, meta := s.converter.DefaultMeta(t) 586 if err := s.converter.Convert(in, out, flags, meta); err != nil { 587 return nil, err 588 } 589 590 setTargetKind(out, kind) 591 return out, nil 592 } 593 594 // generateConvertMeta constructs the meta value we pass to Convert. 595 func (s *Scheme) generateConvertMeta(srcGroupVersion, destGroupVersion unversioned.GroupVersion, in interface{}) (conversion.FieldMatchingFlags, *conversion.Meta) { 596 return s.converter.DefaultMeta(reflect.TypeOf(in)) 597 } 598 599 // setTargetVersion is deprecated and should be replaced by use of setTargetKind 600 func setTargetVersion(obj Object, raw *Scheme, gv unversioned.GroupVersion) { 601 if gv.Version == APIVersionInternal { 602 // internal is a special case 603 obj.GetObjectKind().SetGroupVersionKind(unversioned.GroupVersionKind{}) 604 return 605 } 606 gvk, _ := raw.ObjectKind(obj) 607 obj.GetObjectKind().SetGroupVersionKind(unversioned.GroupVersionKind{Group: gv.Group, Version: gv.Version, Kind: gvk.Kind}) 608 } 609 610 // setTargetKind sets the kind on an object, taking into account whether the target kind is the internal version. 611 func setTargetKind(obj Object, kind unversioned.GroupVersionKind) { 612 if kind.Version == APIVersionInternal { 613 // internal is a special case 614 // TODO: look at removing the need to special case this 615 obj.GetObjectKind().SetGroupVersionKind(unversioned.GroupVersionKind{}) 616 return 617 } 618 obj.GetObjectKind().SetGroupVersionKind(kind) 619 }