diff --git a/pkg/conversion/scheme.go b/pkg/conversion/scheme.go index fbb97aab23a..b5e73715100 100644 --- a/pkg/conversion/scheme.go +++ b/pkg/conversion/scheme.go @@ -211,6 +211,46 @@ func (s *Scheme) Convert(in, out interface{}) error { return s.converter.Convert(in, out, 0, s.generateConvertMeta(inVersion, outVersion)) } +// ConvertToVersion attempts to convert an input object to its matching Kind in another +// version within this scheme. Will return an error if the provided version does not +// contain the inKind (or a mapping by name defined with AddKnownTypeWithName). +func (s *Scheme) ConvertToVersion(in interface{}, outVersion string) (interface{}, error) { + t := reflect.TypeOf(in) + if t.Kind() != reflect.Ptr { + return nil, fmt.Errorf("only pointer types may be converted: %v", t) + } + t = t.Elem() + if t.Kind() != reflect.Struct { + return nil, fmt.Errorf("only pointers to struct types may be converted: %v", t) + } + + kinds, ok := s.typeToKind[t] + if !ok { + return nil, fmt.Errorf("%v cannot be converted into version %q", t, outVersion) + } + outKind := kinds[0] + + inVersion, _, err := s.ObjectVersionAndKind(in) + if err != nil { + return nil, err + } + + out, err := s.NewObject(outVersion, outKind) + if err != nil { + return nil, err + } + + if err := s.converter.Convert(in, out, 0, s.generateConvertMeta(inVersion, outVersion)); err != nil { + return nil, err + } + + if err := s.SetVersionAndKind(outVersion, outKind, out); err != nil { + return nil, err + } + + return out, nil +} + // generateConvertMeta constructs the meta value we pass to Convert. func (s *Scheme) generateConvertMeta(srcVersion, destVersion string) *Meta { return &Meta{ diff --git a/pkg/conversion/scheme_test.go b/pkg/conversion/scheme_test.go index 81000d390ea..31a99f74f05 100644 --- a/pkg/conversion/scheme_test.go +++ b/pkg/conversion/scheme_test.go @@ -240,6 +240,45 @@ func TestMultipleNames(t *testing.T) { } } +func TestKnownTypes(t *testing.T) { + s := GetTestScheme() + if len(s.KnownTypes("v2")) != 0 { + t.Errorf("should have no known types for v2") + } + + types := s.KnownTypes("v1") + for _, s := range []string{"TestType1", "TestType2", "TestType3", "ExternalInternalSame"} { + if _, ok := types[s]; !ok { + t.Errorf("missing type %q", s) + } + } +} + +func TestConvertToVersion(t *testing.T) { + s := GetTestScheme() + tt := &TestType1{A: "I'm not a pointer object"} + other, err := s.ConvertToVersion(tt, "v1") + if err != nil { + t.Fatalf("Failure: %v", err) + } + converted, ok := other.(*ExternalTestType1) + if !ok { + t.Fatalf("Got wrong type") + } + if tt.A != converted.A { + t.Fatalf("Failed to convert object correctly: %#v", converted) + } +} + +func TestConvertToVersionErr(t *testing.T) { + s := GetTestScheme() + tt := TestType1{A: "I'm not a pointer object"} + _, err := s.ConvertToVersion(tt, "v1") + if err == nil { + t.Fatalf("unexpected non-error") + } +} + func TestEncode_NonPtr(t *testing.T) { s := GetTestScheme() tt := TestType1{A: "I'm not a pointer object"} diff --git a/pkg/runtime/interfaces.go b/pkg/runtime/interfaces.go index c019518d601..01d0741c48e 100644 --- a/pkg/runtime/interfaces.go +++ b/pkg/runtime/interfaces.go @@ -33,6 +33,11 @@ type Codec interface { Encoder } +// ObjectConvertor converts an object to a different version. +type ObjectConvertor interface { + ConvertToVersion(in Object, outVersion string) (out Object, err error) +} + // ObjectTyper contains methods for extracting the APIVersion and Kind // of objects. type ObjectTyper interface { diff --git a/pkg/runtime/scheme.go b/pkg/runtime/scheme.go index cfd647919ca..1d9763396c4 100644 --- a/pkg/runtime/scheme.go +++ b/pkg/runtime/scheme.go @@ -17,6 +17,7 @@ limitations under the License. package runtime import ( + "fmt" "reflect" "github.com/GoogleCloudPlatform/kubernetes/pkg/conversion" @@ -223,6 +224,23 @@ func (s *Scheme) Convert(in, out interface{}) error { return s.raw.Convert(in, out) } +// ConvertToVersion attempts to convert an input object to its matching Kind in another +// version within this scheme. Will return an error if the provided version does not +// contain the inKind (or a mapping by name defined with AddKnownTypeWithName). Will also +// return an error if the conversion does not result in a valid Object being +// returned. +func (s *Scheme) ConvertToVersion(in Object, outVersion string) (Object, error) { + unknown, err := s.raw.ConvertToVersion(in, outVersion) + if err != nil { + return nil, err + } + obj, ok := unknown.(Object) + if !ok { + return nil, fmt.Errorf("the provided object cannot be converted to a runtime.Object: %#v", unknown) + } + return obj, nil +} + // EncodeToVersion turns the given api object into an appropriate JSON string. // Will return an error if the object doesn't have an embedded TypeMeta. // Obj may be a pointer to a struct, or a struct. If a struct, a copy