// AddUnversionedTypes registers the provided types as "unversioned", which means that they follow special rules. // Whenever an object of this type is serialized, it is serialized with the provided group version and is not // converted. Thus unversioned objects are expected to remain backwards compatible forever, as if they were in an // API group and version that would never be updated. // // TODO: there is discussion about removing unversioned and replacing it with objects that are manifest into // every version with particular schemas. Resolve this method at that point. func (s *Scheme) AddUnversionedTypes(version unversioned.GroupVersion, types ...Object) { s.AddKnownTypes(version, types...) for _, obj := range types { t := reflect.TypeOf(obj).Elem() gvk := version.WithKind(t.Name()) s.unversionedTypes[t] = gvk if _, ok := s.unversionedKinds[gvk.Kind]; ok { panic(fmt.Sprintf("%v has already been registered as unversioned kind %q - kind name must be unique", reflect.TypeOf(t), gvk.Kind)) } s.unversionedKinds[gvk.Kind] = t } }
func TestExternalToInternalMapping(t *testing.T) { internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"} scheme := runtime.NewScheme() scheme.AddKnownTypeWithName(internalGV.WithKind("OptionalExtensionType"), &InternalOptionalExtensionType{}) scheme.AddKnownTypeWithName(externalGV.WithKind("OptionalExtensionType"), &ExternalOptionalExtensionType{}) codec := serializer.NewCodecFactory(scheme).LegacyCodec(externalGV) table := []struct { obj runtime.Object encoded string }{ { &InternalOptionalExtensionType{Extension: nil}, `{"kind":"OptionalExtensionType","apiVersion":"` + externalGV.String() + `"}`, }, } for i, item := range table { gotDecoded, err := runtime.Decode(codec, []byte(item.encoded)) if err != nil { t.Errorf("unexpected error '%v' (%v)", err, item.encoded) } else if e, a := item.obj, gotDecoded; !reflect.DeepEqual(e, a) { t.Errorf("%d: unexpected objects:\n%s", i, diff.ObjectGoPrintSideBySide(e, a)) } } }
// 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. The serializer handles loading/serializing nested objects. func (s *Scheme) ConvertToVersion(in Object, outVersion unversioned.GroupVersion) (Object, error) { switch in.(type) { case *Unknown, *Unstructured, *UnstructuredList: old := in.GetObjectKind().GroupVersionKind() defer in.GetObjectKind().SetGroupVersionKind(old) setTargetVersion(in, s, outVersion) return in, nil } 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) } var kind unversioned.GroupVersionKind if unversionedKind, ok := s.unversionedTypes[t]; ok { kind = unversionedKind } else { kinds, ok := s.typeToGVK[t] if !ok || len(kinds) == 0 { return nil, fmt.Errorf("%v is not a registered type and cannot be converted into version %q", t, outVersion) } kind = kinds[0] } outKind := outVersion.WithKind(kind.Kind) inKinds, _, err := s.ObjectKinds(in) if err != nil { return nil, err } out, err := s.New(outKind) if err != nil { return nil, err } flags, meta := s.generateConvertMeta(inKinds[0].GroupVersion(), outVersion, in) if err := s.converter.Convert(in, out, flags, meta); err != nil { return nil, err } setTargetVersion(out, s, outVersion) return out, nil }
// AddKnownTypes registers all types passed in 'types' as being members of version 'version'. // All objects passed to types should be pointers to structs. The name that go reports for // the struct becomes the "kind" field when encoding. Version may not be empty - use the // APIVersionInternal constant if you have a type that does not have a formal version. func (s *Scheme) AddKnownTypes(gv unversioned.GroupVersion, types ...Object) { if len(gv.Version) == 0 { panic(fmt.Sprintf("version is required on all types: %s %v", gv, types[0])) } for _, obj := range types { t := reflect.TypeOf(obj) if t.Kind() != reflect.Ptr { panic("All types must be pointers to structs.") } t = t.Elem() if t.Kind() != reflect.Struct { panic("All types must be pointers to structs.") } gvk := gv.WithKind(t.Name()) s.gvkToType[gvk] = t s.typeToGVK[t] = append(s.typeToGVK[t], gvk) } }
func TestEncode(t *testing.T) { internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"} scheme := runtime.NewScheme() scheme.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{}) scheme.AddKnownTypeWithName(externalGV.WithKind("Simple"), &ExternalSimple{}) codec := serializer.NewCodecFactory(scheme).LegacyCodec(externalGV) test := &InternalSimple{ TestString: "I'm the same", } obj := runtime.Object(test) data, err := runtime.Encode(codec, obj) obj2, gvk, err2 := codec.Decode(data, nil, nil) if err != nil || err2 != nil { t.Fatalf("Failure: '%v' '%v'", err, err2) } if _, ok := obj2.(*InternalSimple); !ok { t.Fatalf("Got wrong type") } if !reflect.DeepEqual(obj2, test) { t.Errorf("Expected:\n %#v,\n Got:\n %#v", test, obj2) } if !reflect.DeepEqual(gvk, &unversioned.GroupVersionKind{Group: "test.group", Version: "testExternal", Kind: "Simple"}) { t.Errorf("unexpected gvk returned by decode: %#v", gvk) } }
// Returns a new Scheme set up with the test objects. func GetTestScheme() *runtime.Scheme { internalGV := unversioned.GroupVersion{Version: "__internal"} externalGV := unversioned.GroupVersion{Version: "v1"} s := runtime.NewScheme() // Ordinarily, we wouldn't add TestType2, but because this is a test and // both types are from the same package, we need to get it into the system // so that converter will match it with ExternalType2. s.AddKnownTypes(internalGV, &TestType1{}, &TestType2{}, &ExternalInternalSame{}) s.AddKnownTypes(externalGV, &ExternalInternalSame{}) s.AddKnownTypeWithName(externalGV.WithKind("TestType1"), &ExternalTestType1{}) s.AddKnownTypeWithName(externalGV.WithKind("TestType2"), &ExternalTestType2{}) s.AddKnownTypeWithName(internalGV.WithKind("TestType3"), &TestType1{}) s.AddKnownTypeWithName(externalGV.WithKind("TestType3"), &ExternalTestType1{}) return s }
func TestMetaValues(t *testing.T) { internalGV := unversioned.GroupVersion{Group: "test.group", Version: "__internal"} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "externalVersion"} s := runtime.NewScheme() s.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{}) s.AddKnownTypeWithName(externalGV.WithKind("Simple"), &ExternalSimple{}) internalToExternalCalls := 0 externalToInternalCalls := 0 // Register functions to verify that scope.Meta() gets set correctly. err := s.AddConversionFuncs( func(in *InternalSimple, out *ExternalSimple, scope conversion.Scope) error { t.Logf("internal -> external") scope.Convert(&in.TestString, &out.TestString, 0) internalToExternalCalls++ return nil }, func(in *ExternalSimple, out *InternalSimple, scope conversion.Scope) error { t.Logf("external -> internal") scope.Convert(&in.TestString, &out.TestString, 0) externalToInternalCalls++ return nil }, ) if err != nil { t.Fatalf("unexpected error: %v", err) } simple := &InternalSimple{ TestString: "foo", } s.Log(t) out, err := s.ConvertToVersion(simple, externalGV) if err != nil { t.Fatalf("unexpected error: %v", err) } internal, err := s.ConvertToVersion(out, internalGV) if err != nil { t.Fatalf("unexpected error: %v", err) } if e, a := simple, internal; !reflect.DeepEqual(e, a) { t.Errorf("Expected:\n %#v,\n Got:\n %#v", e, a) } if e, a := 1, internalToExternalCalls; e != a { t.Errorf("Expected %v, got %v", e, a) } if e, a := 1, externalToInternalCalls; e != a { t.Errorf("Expected %v, got %v", e, a) } }
// write renders a returned runtime.Object to the response as a stream or an encoded object. If the object // returned by the response implements rest.ResourceStreamer that interface will be used to render the // response. The Accept header and current API version will be passed in, and the output will be copied // directly to the response body. If content type is returned it is used, otherwise the content type will // be "application/octet-stream". All other objects are sent to standard JSON serialization. func write(statusCode int, gv unversioned.GroupVersion, s runtime.NegotiatedSerializer, object runtime.Object, w http.ResponseWriter, req *http.Request) { if stream, ok := object.(rest.ResourceStreamer); ok { out, flush, contentType, err := stream.InputStream(gv.String(), req.Header.Get("Accept")) if err != nil { errorNegotiated(err, s, gv, w, req) return } if out == nil { // No output provided - return StatusNoContent w.WriteHeader(http.StatusNoContent) return } defer out.Close() if wsstream.IsWebSocketRequest(req) { r := wsstream.NewReader(out, true) if err := r.Copy(w, req); err != nil { utilruntime.HandleError(fmt.Errorf("error encountered while streaming results via websocket: %v", err)) } return } if len(contentType) == 0 { contentType = "application/octet-stream" } w.Header().Set("Content-Type", contentType) w.WriteHeader(statusCode) writer := w.(io.Writer) if flush { writer = flushwriter.Wrap(w) } io.Copy(writer, out) return } writeNegotiated(s, gv, w, req, statusCode, object) }
// UnsafeConvertToVersion will convert in to the provided outVersion if such a conversion is possible, // but does not guarantee the output object does not share fields with the input object. It attempts to be as // efficient as possible when doing conversion. func (s *Scheme) UnsafeConvertToVersion(in Object, outVersion unversioned.GroupVersion) (Object, error) { switch t := in.(type) { case *Unknown: t.APIVersion = outVersion.String() return t, nil case *Unstructured: t.SetAPIVersion(outVersion.String()) return t, nil case *UnstructuredList: t.SetAPIVersion(outVersion.String()) return t, nil } // determine the incoming kinds with as few allocations as possible. 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.typeToGVK[t] if !ok || len(kinds) == 0 { return nil, fmt.Errorf("%v is not a registered type and cannot be converted into version %q", t, outVersion) } // if the Go type is also registered to the destination kind, no conversion is necessary for i := range kinds { if kinds[i].Version == outVersion.Version && kinds[i].Group == outVersion.Group { setTargetKind(in, kinds[i]) return in, nil } } // type is unversioned, no conversion necessary // it should be possible to avoid this allocation if unversionedKind, ok := s.unversionedTypes[t]; ok { kind := unversionedKind outKind := outVersion.WithKind(kind.Kind) setTargetKind(in, outKind) return in, nil } // allocate a new object as the target using the target kind // TODO: this should look in the target group version and find the first kind that matches, rather than the // first kind registered in typeToGVK kind := kinds[0] kind.Version = outVersion.Version kind.Group = outVersion.Group out, err := s.New(kind) if err != nil { return nil, err } // TODO: try to avoid the allocations here - in fast paths we are not likely to need these flags or meta flags, meta := s.converter.DefaultMeta(t) if err := s.converter.Convert(in, out, flags, meta); err != nil { return nil, err } setTargetKind(out, kind) return out, nil }
func TestScheme(t *testing.T) { internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"} scheme := runtime.NewScheme() scheme.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{}) scheme.AddKnownTypeWithName(externalGV.WithKind("Simple"), &ExternalSimple{}) // If set, would clear TypeMeta during conversion. //scheme.AddIgnoredConversionType(&TypeMeta{}, &TypeMeta{}) // test that scheme is an ObjectTyper var _ runtime.ObjectTyper = scheme internalToExternalCalls := 0 externalToInternalCalls := 0 // Register functions to verify that scope.Meta() gets set correctly. err := scheme.AddConversionFuncs( func(in *InternalSimple, out *ExternalSimple, scope conversion.Scope) error { scope.Convert(&in.TypeMeta, &out.TypeMeta, 0) scope.Convert(&in.TestString, &out.TestString, 0) internalToExternalCalls++ return nil }, func(in *ExternalSimple, out *InternalSimple, scope conversion.Scope) error { scope.Convert(&in.TypeMeta, &out.TypeMeta, 0) scope.Convert(&in.TestString, &out.TestString, 0) externalToInternalCalls++ return nil }, ) if err != nil { t.Fatalf("unexpected error: %v", err) } codecs := serializer.NewCodecFactory(scheme) codec := codecs.LegacyCodec(externalGV) jsonserializer, _ := codecs.SerializerForFileExtension("json") simple := &InternalSimple{ TestString: "foo", } // Test Encode, Decode, DecodeInto, and DecodeToVersion obj := runtime.Object(simple) data, err := runtime.Encode(codec, obj) if err != nil { t.Fatal(err) } obj2, err := runtime.Decode(codec, data) if err != nil { t.Fatal(err) } if _, ok := obj2.(*InternalSimple); !ok { t.Fatalf("Got wrong type") } if e, a := simple, obj2; !reflect.DeepEqual(e, a) { t.Errorf("Expected:\n %#v,\n Got:\n %#v", e, a) } obj3 := &InternalSimple{} if err := runtime.DecodeInto(codec, data, obj3); err != nil { t.Fatal(err) } // clearing TypeMeta is a function of the scheme, which we do not test here (ConvertToVersion // does not automatically clear TypeMeta anymore). simple.TypeMeta = runtime.TypeMeta{Kind: "Simple", APIVersion: externalGV.String()} if e, a := simple, obj3; !reflect.DeepEqual(e, a) { t.Errorf("Expected:\n %#v,\n Got:\n %#v", e, a) } obj4, err := runtime.Decode(jsonserializer, data) if err != nil { t.Fatal(err) } if _, ok := obj4.(*ExternalSimple); !ok { t.Fatalf("Got wrong type") } // Test Convert external := &ExternalSimple{} err = scheme.Convert(simple, external) if err != nil { t.Fatalf("Unexpected error: %v", err) } if e, a := simple.TestString, external.TestString; e != a { t.Errorf("Expected %v, got %v", e, a) } // Encode and Convert should each have caused an increment. if e, a := 2, internalToExternalCalls; e != a { t.Errorf("Expected %v, got %v", e, a) } // DecodeInto and Decode should each have caused an increment because of a conversion if e, a := 2, externalToInternalCalls; e != a { t.Errorf("Expected %v, got %v", e, a) } }
func TestUnversionedTypes(t *testing.T) { internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"} otherGV := unversioned.GroupVersion{Group: "group", Version: "other"} scheme := runtime.NewScheme() scheme.AddUnversionedTypes(externalGV, &InternalSimple{}) scheme.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{}) scheme.AddKnownTypeWithName(externalGV.WithKind("Simple"), &ExternalSimple{}) scheme.AddKnownTypeWithName(otherGV.WithKind("Simple"), &ExternalSimple{}) codec := serializer.NewCodecFactory(scheme).LegacyCodec(externalGV) if unv, ok := scheme.IsUnversioned(&InternalSimple{}); !unv || !ok { t.Fatalf("type not unversioned and in scheme: %t %t", unv, ok) } kinds, _, err := scheme.ObjectKinds(&InternalSimple{}) if err != nil { t.Fatal(err) } kind := kinds[0] if kind != externalGV.WithKind("InternalSimple") { t.Fatalf("unexpected: %#v", kind) } test := &InternalSimple{ TestString: "I'm the same", } obj := runtime.Object(test) data, err := runtime.Encode(codec, obj) if err != nil { t.Fatal(err) } obj2, gvk, err := codec.Decode(data, nil, nil) if err != nil { t.Fatal(err) } if _, ok := obj2.(*InternalSimple); !ok { t.Fatalf("Got wrong type") } if !reflect.DeepEqual(obj2, test) { t.Errorf("Expected:\n %#v,\n Got:\n %#v", test, obj2) } // object is serialized as an unversioned object (in the group and version it was defined in) if !reflect.DeepEqual(gvk, &unversioned.GroupVersionKind{Group: "test.group", Version: "testExternal", Kind: "InternalSimple"}) { t.Errorf("unexpected gvk returned by decode: %#v", gvk) } // when serialized to a different group, the object is kept in its preferred name codec = serializer.NewCodecFactory(scheme).LegacyCodec(otherGV) data, err = runtime.Encode(codec, obj) if err != nil { t.Fatal(err) } if string(data) != `{"apiVersion":"test.group/testExternal","kind":"InternalSimple","testString":"I'm the same"}`+"\n" { t.Errorf("unexpected data: %s", data) } }
func TestExtensionMapping(t *testing.T) { internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"} scheme := runtime.NewScheme() scheme.AddKnownTypeWithName(internalGV.WithKind("ExtensionType"), &InternalExtensionType{}) scheme.AddKnownTypeWithName(internalGV.WithKind("OptionalExtensionType"), &InternalOptionalExtensionType{}) scheme.AddKnownTypeWithName(externalGV.WithKind("ExtensionType"), &ExternalExtensionType{}) scheme.AddKnownTypeWithName(externalGV.WithKind("OptionalExtensionType"), &ExternalOptionalExtensionType{}) // register external first when the object is the same in both schemes, so ObjectVersionAndKind reports the // external version. scheme.AddKnownTypeWithName(externalGV.WithKind("A"), &ExtensionA{}) scheme.AddKnownTypeWithName(externalGV.WithKind("B"), &ExtensionB{}) scheme.AddKnownTypeWithName(internalGV.WithKind("A"), &ExtensionA{}) scheme.AddKnownTypeWithName(internalGV.WithKind("B"), &ExtensionB{}) codec := serializer.NewCodecFactory(scheme).LegacyCodec(externalGV) table := []struct { obj runtime.Object expected runtime.Object encoded string }{ { &InternalExtensionType{ Extension: runtime.NewEncodable(codec, &ExtensionA{TestString: "foo"}), }, &InternalExtensionType{ Extension: &runtime.Unknown{ Raw: []byte(`{"apiVersion":"test.group/testExternal","kind":"A","testString":"foo"}`), ContentType: runtime.ContentTypeJSON, }, }, // apiVersion is set in the serialized object for easier consumption by clients `{"apiVersion":"` + externalGV.String() + `","kind":"ExtensionType","extension":{"apiVersion":"test.group/testExternal","kind":"A","testString":"foo"}} `, }, { &InternalExtensionType{Extension: runtime.NewEncodable(codec, &ExtensionB{TestString: "bar"})}, &InternalExtensionType{ Extension: &runtime.Unknown{ Raw: []byte(`{"apiVersion":"test.group/testExternal","kind":"B","testString":"bar"}`), ContentType: runtime.ContentTypeJSON, }, }, // apiVersion is set in the serialized object for easier consumption by clients `{"apiVersion":"` + externalGV.String() + `","kind":"ExtensionType","extension":{"apiVersion":"test.group/testExternal","kind":"B","testString":"bar"}} `, }, { &InternalExtensionType{Extension: nil}, &InternalExtensionType{ Extension: nil, }, `{"apiVersion":"` + externalGV.String() + `","kind":"ExtensionType","extension":null} `, }, } for i, item := range table { gotEncoded, err := runtime.Encode(codec, item.obj) if err != nil { t.Errorf("unexpected error '%v' (%#v)", err, item.obj) } else if e, a := item.encoded, string(gotEncoded); e != a { t.Errorf("expected\n%#v\ngot\n%#v\n", e, a) } gotDecoded, err := runtime.Decode(codec, []byte(item.encoded)) if err != nil { t.Errorf("unexpected error '%v' (%v)", err, item.encoded) } else if e, a := item.expected, gotDecoded; !reflect.DeepEqual(e, a) { t.Errorf("%d: unexpected objects:\n%s", i, diff.ObjectGoPrintSideBySide(e, a)) } } }
// Decode attempts a decode of the object, then tries to convert it to the internal version. If into is provided and the decoding is // successful, the returned runtime.Object will be the value passed as into. Note that this may bypass conversion if you pass an // into that matches the serialized version. func (c *codec) Decode(data []byte, defaultGVK *unversioned.GroupVersionKind, into runtime.Object) (runtime.Object, *unversioned.GroupVersionKind, error) { versioned, isVersioned := into.(*runtime.VersionedObjects) if isVersioned { into = versioned.Last() } obj, gvk, err := c.decoder.Decode(data, defaultGVK, into) if err != nil { return nil, gvk, err } // if we specify a target, use generic conversion. if into != nil { if into == obj { if isVersioned { return versioned, gvk, nil } return into, gvk, nil } if err := c.convertor.Convert(obj, into); err != nil { return nil, gvk, err } if isVersioned { versioned.Objects = []runtime.Object{obj, into} return versioned, gvk, nil } return into, gvk, nil } // invoke a version conversion group := gvk.Group if defaultGVK != nil { group = defaultGVK.Group } var targetGV unversioned.GroupVersion if c.decodeVersion == nil { // convert to internal by default targetGV.Group = group targetGV.Version = runtime.APIVersionInternal } else { gv, ok := c.decodeVersion[group] if !ok { // unknown objects are left in their original version if isVersioned { versioned.Objects = []runtime.Object{obj} return versioned, gvk, nil } return obj, gvk, nil } targetGV = gv } if gvk.GroupVersion() == targetGV { if isVersioned { versioned.Objects = []runtime.Object{obj} return versioned, gvk, nil } return obj, gvk, nil } if isVersioned { // create a copy, because ConvertToVersion does not guarantee non-mutation of objects copied, err := c.copier.Copy(obj) if err != nil { copied = obj } versioned.Objects = []runtime.Object{copied} } // Convert if needed. out, err := c.convertor.ConvertToVersion(obj, targetGV) if err != nil { return nil, gvk, err } if isVersioned { versioned.Objects = append(versioned.Objects, out) return versioned, gvk, nil } return out, gvk, nil }
func TestStringMapConversion(t *testing.T) { internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "external"} scheme := runtime.NewScheme() scheme.Log(t) scheme.AddKnownTypeWithName(internalGV.WithKind("Complex"), &InternalComplex{}) scheme.AddKnownTypeWithName(externalGV.WithKind("Complex"), &ExternalComplex{}) testCases := map[string]struct { input map[string][]string errFn func(error) bool expected runtime.Object }{ "ignores omitempty": { input: map[string][]string{ "String": {"not_used"}, "string": {"value"}, "int": {"1"}, "Integer64": {"2"}, }, expected: &ExternalComplex{String: "value", Integer: 1}, }, "returns error on bad int": { input: map[string][]string{ "int": {"a"}, }, errFn: func(err error) bool { return err != nil }, expected: &ExternalComplex{}, }, "parses int64": { input: map[string][]string{ "Int64": {"-1"}, }, expected: &ExternalComplex{Int64: -1}, }, "returns error on bad int64": { input: map[string][]string{ "Int64": {"a"}, }, errFn: func(err error) bool { return err != nil }, expected: &ExternalComplex{}, }, "parses boolean true": { input: map[string][]string{ "bool": {"true"}, }, expected: &ExternalComplex{Bool: true}, }, "parses boolean any value": { input: map[string][]string{ "bool": {"foo"}, }, expected: &ExternalComplex{Bool: true}, }, "parses boolean false": { input: map[string][]string{ "bool": {"false"}, }, expected: &ExternalComplex{Bool: false}, }, "parses boolean empty value": { input: map[string][]string{ "bool": {""}, }, expected: &ExternalComplex{Bool: true}, }, "parses boolean no value": { input: map[string][]string{ "bool": {}, }, expected: &ExternalComplex{Bool: false}, }, } for k, tc := range testCases { out := &ExternalComplex{} if err := scheme.Convert(&tc.input, out); (tc.errFn == nil && err != nil) || (tc.errFn != nil && !tc.errFn(err)) { t.Errorf("%s: unexpected error: %v", k, err) continue } else if err != nil { continue } if !reflect.DeepEqual(out, tc.expected) { t.Errorf("%s: unexpected output: %#v", k, out) } } }
func TestDisabledVersion(t *testing.T) { g1v1 := unversioned.GroupVersion{Group: "group1", Version: "version1"} g1v2 := unversioned.GroupVersion{Group: "group1", Version: "version2"} g2v1 := unversioned.GroupVersion{Group: "group2", Version: "version1"} g3v1 := unversioned.GroupVersion{Group: "group3", Version: "version1"} resourceType := "the-resource" disabledResourceType := "the-disabled-resource" config := NewResourceConfig() config.DisableVersions(g1v1) config.EnableVersions(g1v2, g3v1) config.EnableResources(g1v1.WithResource(resourceType), g2v1.WithResource(resourceType)) config.DisableResources(g1v2.WithResource(disabledResourceType)) expectedEnabledResources := []unversioned.GroupVersionResource{ g1v2.WithResource(resourceType), g2v1.WithResource(resourceType), } expectedDisabledResources := []unversioned.GroupVersionResource{ g1v1.WithResource(resourceType), g1v1.WithResource(disabledResourceType), g1v2.WithResource(disabledResourceType), g2v1.WithResource(disabledResourceType), } for _, expectedResource := range expectedEnabledResources { if !config.ResourceEnabled(expectedResource) { t.Errorf("expected enabled for %v, from %v", expectedResource, config) } } for _, expectedResource := range expectedDisabledResources { if config.ResourceEnabled(expectedResource) { t.Errorf("expected disabled for %v, from %v", expectedResource, config) } } if e, a := false, config.AnyResourcesForVersionEnabled(g1v1); e != a { t.Errorf("expected %v, got %v", e, a) } if e, a := false, config.AllResourcesForVersionEnabled(g1v1); e != a { t.Errorf("expected %v, got %v", e, a) } if e, a := true, config.AnyResourcesForVersionEnabled(g1v2); e != a { t.Errorf("expected %v, got %v", e, a) } if e, a := false, config.AllResourcesForVersionEnabled(g1v2); e != a { t.Errorf("expected %v, got %v", e, a) } if e, a := true, config.AnyResourcesForVersionEnabled(g3v1); e != a { t.Errorf("expected %v, got %v", e, a) } if e, a := true, config.AllResourcesForVersionEnabled(g3v1); e != a { t.Errorf("expected %v, got %v", e, a) } expectedEnabledAnyVersionResources := []unversioned.GroupResource{ {Group: "group1", Resource: resourceType}, } expectedDisabledAnyResources := []unversioned.GroupResource{ {Group: "group1", Resource: disabledResourceType}, } for _, expectedResource := range expectedEnabledAnyVersionResources { if !config.AnyVersionOfResourceEnabled(expectedResource) { t.Errorf("expected enabled for %v, from %v", expectedResource, config) } } for _, expectedResource := range expectedDisabledAnyResources { if config.AnyVersionOfResourceEnabled(expectedResource) { t.Errorf("expected disabled for %v, from %v", expectedResource, config) } } }
// SupportedResourcesHandler returns a handler which will list the provided resources as available. func SupportedResourcesHandler(s runtime.NegotiatedSerializer, groupVersion unversioned.GroupVersion, apiResources []unversioned.APIResource) restful.RouteFunction { return func(req *restful.Request, resp *restful.Response) { writeNegotiated(s, unversioned.GroupVersion{}, resp.ResponseWriter, req.Request, http.StatusOK, &unversioned.APIResourceList{GroupVersion: groupVersion.String(), APIResources: apiResources}) } }