예제 #1
1
// roundTrip applies a single round-trip test to the given runtime object
// using the given codec.  The round-trip test ensures that an object can be
// deep-copied and converted from internal -> versioned -> internal without
// loss of data.
func roundTrip(t *testing.T, codec runtime.Codec, item runtime.Object) {
	printer := spew.ConfigState{DisableMethods: true}
	original := item

	// deep copy the original object
	copied, err := api.Scheme.DeepCopy(item)
	if err != nil {
		panic(fmt.Sprintf("unable to copy: %v", err))
	}
	item = copied.(runtime.Object)
	name := reflect.TypeOf(item).Elem().Name()

	// encode (serialize) the deep copy using the provided codec
	data, err := runtime.Encode(codec, item)
	if err != nil {
		if runtime.IsNotRegisteredError(err) {
			t.Logf("%v: not registered: %v (%s)", name, err, printer.Sprintf("%#v", item))
		} else {
			t.Errorf("%v: %v (%s)", name, err, printer.Sprintf("%#v", item))
		}
		return
	}

	// ensure that the deep copy is equal to the original; neither the deep
	// copy or conversion should alter the object
	if !api.Semantic.DeepEqual(original, item) {
		t.Errorf("0: %v: encode altered the object, diff: %v", name, diff.ObjectReflectDiff(original, item))
		return
	}

	// decode (deserialize) the encoded data back into an object
	obj2, err := runtime.Decode(codec, data)
	if err != nil {
		t.Errorf("0: %v: %v\nCodec: %#v\nData: %s\nSource: %#v", name, err, codec, dataAsString(data), printer.Sprintf("%#v", item))
		panic("failed")
	}

	// ensure that the object produced from decoding the encoded data is equal
	// to the original object
	if !api.Semantic.DeepEqual(original, obj2) {
		t.Errorf("\n1: %v: diff: %v\nCodec: %#v\nSource:\n\n%#v\n\nEncoded:\n\n%s\n\nFinal:\n\n%#v", name, diff.ObjectReflectDiff(item, obj2), codec, printer.Sprintf("%#v", item), dataAsString(data), printer.Sprintf("%#v", obj2))
		return
	}

	// decode the encoded data into a new object (instead of letting the codec
	// create a new object)
	obj3 := reflect.New(reflect.TypeOf(item).Elem()).Interface().(runtime.Object)
	if err := runtime.DecodeInto(codec, data, obj3); err != nil {
		t.Errorf("2: %v: %v", name, err)
		return
	}

	// ensure that the new runtime object is equal to the original after being
	// decoded into
	if !api.Semantic.DeepEqual(item, obj3) {
		t.Errorf("3: %v: diff: %v\nCodec: %#v", name, diff.ObjectReflectDiff(item, obj3), codec)
		return
	}
}
예제 #2
0
func TestDoRequestFailed(t *testing.T) {
	status := &metav1.Status{
		Code:    http.StatusNotFound,
		Status:  metav1.StatusFailure,
		Reason:  metav1.StatusReasonNotFound,
		Message: " \"\" not found",
		Details: &metav1.StatusDetails{},
	}
	expectedBody, _ := runtime.Encode(testapi.Default.Codec(), status)
	fakeHandler := utiltesting.FakeHandler{
		StatusCode:   404,
		ResponseBody: string(expectedBody),
		T:            t,
	}
	testServer := httptest.NewServer(&fakeHandler)
	defer testServer.Close()

	c, err := restClient(testServer)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	err = c.Get().Do().Error()
	if err == nil {
		t.Errorf("unexpected non-error")
	}
	ss, ok := err.(errors.APIStatus)
	if !ok {
		t.Errorf("unexpected error type %v", err)
	}
	actual := ss.Status()
	if !reflect.DeepEqual(status, &actual) {
		t.Errorf("Unexpected mis-match: %s", diff.ObjectReflectDiff(status, &actual))
	}
}
예제 #3
0
func TestDoRawRequestFailed(t *testing.T) {
	status := &metav1.Status{
		Code:    http.StatusNotFound,
		Status:  metav1.StatusFailure,
		Reason:  metav1.StatusReasonNotFound,
		Message: "the server could not find the requested resource",
		Details: &metav1.StatusDetails{
			Causes: []metav1.StatusCause{
				{Type: metav1.CauseTypeUnexpectedServerResponse, Message: "unknown"},
			},
		},
	}
	expectedBody, _ := runtime.Encode(testapi.Default.Codec(), status)
	fakeHandler := utiltesting.FakeHandler{
		StatusCode:   404,
		ResponseBody: string(expectedBody),
		T:            t,
	}
	testServer := httptest.NewServer(&fakeHandler)
	defer testServer.Close()

	c, err := restClient(testServer)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	body, err := c.Get().Do().Raw()

	if err == nil || body == nil {
		t.Errorf("unexpected non-error: %#v", body)
	}
	ss, ok := err.(errors.APIStatus)
	if !ok {
		t.Errorf("unexpected error type %v", err)
	}
	actual := ss.Status()
	if !reflect.DeepEqual(status, &actual) {
		t.Errorf("Unexpected mis-match: %s", diff.ObjectReflectDiff(status, &actual))
	}
}
예제 #4
0
func doDeepCopyTest(t *testing.T, kind schema.GroupVersionKind, f *fuzz.Fuzzer) {
	item, err := api.Scheme.New(kind)
	if err != nil {
		t.Fatalf("Could not create a %v: %s", kind, err)
	}
	f.Fuzz(item)
	itemCopy, err := api.Scheme.DeepCopy(item)
	if err != nil {
		t.Errorf("Could not deep copy a %v: %s", kind, err)
		return
	}

	if !reflect.DeepEqual(item, itemCopy) {
		t.Errorf("\nexpected: %#v\n\ngot:      %#v\n\ndiff:      %v", item, itemCopy, diff.ObjectReflectDiff(item, itemCopy))
	}

	prefuzzData := &bytes.Buffer{}
	if err := api.Codecs.LegacyCodec(kind.GroupVersion()).Encode(item, prefuzzData); err != nil {
		t.Errorf("Could not encode a %v: %s", kind, err)
		return
	}

	// Refuzz the copy, which should have no effect on the original
	f.Fuzz(itemCopy)

	postfuzzData := &bytes.Buffer{}
	if err := api.Codecs.LegacyCodec(kind.GroupVersion()).Encode(item, postfuzzData); err != nil {
		t.Errorf("Could not encode a %v: %s", kind, err)
		return
	}

	if bytes.Compare(prefuzzData.Bytes(), postfuzzData.Bytes()) != 0 {
		t.Log(diff.StringDiff(prefuzzData.String(), postfuzzData.String()))
		t.Errorf("Fuzzing copy modified original of %#v", kind)
		return
	}
}
예제 #5
0
// TODO: add a reflexive test that verifies that all SetDefaults functions are registered
func TestDefaulting(t *testing.T) {
	// these are the known types with defaulters - you must add to this list if you add a top level defaulter
	typesWithDefaulting := map[schema.GroupVersionKind]struct{}{
		{Group: "", Version: "v1", Kind: "ConfigMap"}:                                             {},
		{Group: "", Version: "v1", Kind: "ConfigMapList"}:                                         {},
		{Group: "", Version: "v1", Kind: "Endpoints"}:                                             {},
		{Group: "", Version: "v1", Kind: "EndpointsList"}:                                         {},
		{Group: "", Version: "v1", Kind: "Namespace"}:                                             {},
		{Group: "", Version: "v1", Kind: "NamespaceList"}:                                         {},
		{Group: "", Version: "v1", Kind: "Node"}:                                                  {},
		{Group: "", Version: "v1", Kind: "NodeList"}:                                              {},
		{Group: "", Version: "v1", Kind: "PersistentVolume"}:                                      {},
		{Group: "", Version: "v1", Kind: "PersistentVolumeList"}:                                  {},
		{Group: "", Version: "v1", Kind: "PersistentVolumeClaim"}:                                 {},
		{Group: "", Version: "v1", Kind: "PersistentVolumeClaimList"}:                             {},
		{Group: "", Version: "v1", Kind: "PodAttachOptions"}:                                      {},
		{Group: "", Version: "v1", Kind: "PodExecOptions"}:                                        {},
		{Group: "", Version: "v1", Kind: "Pod"}:                                                   {},
		{Group: "", Version: "v1", Kind: "PodList"}:                                               {},
		{Group: "", Version: "v1", Kind: "PodTemplate"}:                                           {},
		{Group: "", Version: "v1", Kind: "PodTemplateList"}:                                       {},
		{Group: "", Version: "v1", Kind: "ReplicationController"}:                                 {},
		{Group: "", Version: "v1", Kind: "ReplicationControllerList"}:                             {},
		{Group: "", Version: "v1", Kind: "Secret"}:                                                {},
		{Group: "", Version: "v1", Kind: "SecretList"}:                                            {},
		{Group: "", Version: "v1", Kind: "Service"}:                                               {},
		{Group: "", Version: "v1", Kind: "ServiceList"}:                                           {},
		{Group: "apps", Version: "v1beta1", Kind: "StatefulSet"}:                                  {},
		{Group: "apps", Version: "v1beta1", Kind: "StatefulSetList"}:                              {},
		{Group: "autoscaling", Version: "v1", Kind: "HorizontalPodAutoscaler"}:                    {},
		{Group: "autoscaling", Version: "v1", Kind: "HorizontalPodAutoscalerList"}:                {},
		{Group: "batch", Version: "v1", Kind: "Job"}:                                              {},
		{Group: "batch", Version: "v1", Kind: "JobList"}:                                          {},
		{Group: "batch", Version: "v2alpha1", Kind: "CronJob"}:                                    {},
		{Group: "batch", Version: "v2alpha1", Kind: "CronJobList"}:                                {},
		{Group: "batch", Version: "v2alpha1", Kind: "Job"}:                                        {},
		{Group: "batch", Version: "v2alpha1", Kind: "JobList"}:                                    {},
		{Group: "batch", Version: "v2alpha1", Kind: "JobTemplate"}:                                {},
		{Group: "batch", Version: "v2alpha1", Kind: "ScheduledJob"}:                               {},
		{Group: "batch", Version: "v2alpha1", Kind: "ScheduledJobList"}:                           {},
		{Group: "certificates.k8s.io", Version: "v1beta1", Kind: "CertificateSigningRequest"}:     {},
		{Group: "certificates.k8s.io", Version: "v1beta1", Kind: "CertificateSigningRequestList"}: {},
		{Group: "componentconfig", Version: "v1alpha1", Kind: "KubeProxyConfiguration"}:           {},
		{Group: "componentconfig", Version: "v1alpha1", Kind: "KubeSchedulerConfiguration"}:       {},
		{Group: "componentconfig", Version: "v1alpha1", Kind: "KubeletConfiguration"}:             {},
		{Group: "kubeadm.k8s.io", Version: "v1alpha1", Kind: "MasterConfiguration"}:               {},
		// This object contains only int fields which currently breaks the defaulting test because
		// it's pretty stupid. Once we add non integer fields, we should uncomment this.
		// {Group: "kubeadm.k8s.io", Version: "v1alpha1", Kind: "NodeConfiguration"}:                 {},
		{Group: "extensions", Version: "v1beta1", Kind: "DaemonSet"}:                              {},
		{Group: "extensions", Version: "v1beta1", Kind: "DaemonSetList"}:                          {},
		{Group: "extensions", Version: "v1beta1", Kind: "Deployment"}:                             {},
		{Group: "extensions", Version: "v1beta1", Kind: "DeploymentList"}:                         {},
		{Group: "extensions", Version: "v1beta1", Kind: "HorizontalPodAutoscaler"}:                {},
		{Group: "extensions", Version: "v1beta1", Kind: "HorizontalPodAutoscalerList"}:            {},
		{Group: "extensions", Version: "v1beta1", Kind: "ReplicaSet"}:                             {},
		{Group: "extensions", Version: "v1beta1", Kind: "ReplicaSetList"}:                         {},
		{Group: "rbac.authorization.k8s.io", Version: "v1alpha1", Kind: "ClusterRoleBinding"}:     {},
		{Group: "rbac.authorization.k8s.io", Version: "v1alpha1", Kind: "ClusterRoleBindingList"}: {},
		{Group: "rbac.authorization.k8s.io", Version: "v1alpha1", Kind: "RoleBinding"}:            {},
		{Group: "rbac.authorization.k8s.io", Version: "v1alpha1", Kind: "RoleBindingList"}:        {},
		{Group: "rbac.authorization.k8s.io", Version: "v1beta1", Kind: "ClusterRoleBinding"}:      {},
		{Group: "rbac.authorization.k8s.io", Version: "v1beta1", Kind: "ClusterRoleBindingList"}:  {},
		{Group: "rbac.authorization.k8s.io", Version: "v1beta1", Kind: "RoleBinding"}:             {},
		{Group: "rbac.authorization.k8s.io", Version: "v1beta1", Kind: "RoleBindingList"}:         {},
	}

	f := fuzz.New().NilChance(.5).NumElements(1, 1).RandSource(rand.NewSource(1))
	f.Funcs(
		func(s *runtime.RawExtension, c fuzz.Continue) {},
		func(s *metav1.LabelSelector, c fuzz.Continue) {
			c.FuzzNoCustom(s)
			s.MatchExpressions = nil // need to fuzz this specially
		},
		func(s *apiv1.ListOptions, c fuzz.Continue) {
			c.FuzzNoCustom(s)
			s.LabelSelector = "" // need to fuzz requirement strings specially
			s.FieldSelector = "" // need to fuzz requirement strings specially
		},
		func(s *extensionsv1beta1.ScaleStatus, c fuzz.Continue) {
			c.FuzzNoCustom(s)
			s.TargetSelector = "" // need to fuzz requirement strings specially
		},
	)

	scheme := api.Scheme
	var testTypes orderedGroupVersionKinds
	for gvk := range scheme.AllKnownTypes() {
		if gvk.Version == runtime.APIVersionInternal {
			continue
		}
		testTypes = append(testTypes, gvk)
	}
	sort.Sort(testTypes)

	for _, gvk := range testTypes {
		_, expectedChanged := typesWithDefaulting[gvk]
		iter := 0
		changedOnce := false
		for {
			if iter > *fuzzIters {
				if !expectedChanged || changedOnce {
					break
				}
				if iter > 300 {
					t.Errorf("expected %s to trigger defaulting due to fuzzing", gvk)
					break
				}
				// if we expected defaulting, continue looping until the fuzzer gives us one
				// at worst, we will timeout
			}
			iter++

			src, err := scheme.New(gvk)
			if err != nil {
				t.Fatal(err)
			}
			f.Fuzz(src)

			src.GetObjectKind().SetGroupVersionKind(schema.GroupVersionKind{})

			original, err := scheme.DeepCopy(src)
			if err != nil {
				t.Fatal(err)
			}

			// get internal
			withDefaults, _ := scheme.DeepCopy(src)
			scheme.Default(withDefaults.(runtime.Object))

			if !reflect.DeepEqual(original, withDefaults) {
				changedOnce = true
				if !expectedChanged {
					t.Errorf("{Group: \"%s\", Version: \"%s\", Kind: \"%s\"} did not expect defaults to be set - update expected or check defaulter registering: %s", gvk.Group, gvk.Version, gvk.Kind, diff.ObjectReflectDiff(original, withDefaults))
				}
			}
		}
	}
}
예제 #6
0
func TestConvertToVersion(t *testing.T) {
	testCases := []struct {
		scheme *runtime.Scheme
		in     runtime.Object
		gv     runtime.GroupVersioner
		same   bool
		out    runtime.Object
		errFn  func(error) bool
	}{
		// errors if the type is not registered in the scheme
		{
			scheme: GetTestScheme(),
			in:     &UnknownType{},
			errFn:  func(err error) bool { return err != nil && runtime.IsNotRegisteredError(err) },
		},
		// errors if the group versioner returns no target
		{
			scheme: GetTestScheme(),
			in:     &ExternalTestType1{A: "test"},
			gv:     testGroupVersioner{},
			errFn: func(err error) bool {
				return err != nil && strings.Contains(err.Error(), "is not suitable for converting")
			},
		},
		// converts to internal
		{
			scheme: GetTestScheme(),
			in:     &ExternalTestType1{A: "test"},
			gv:     schema.GroupVersion{Version: "__internal"},
			out:    &TestType1{A: "test"},
		},
		// prefers the best match
		{
			scheme: GetTestScheme(),
			in:     &ExternalTestType1{A: "test"},
			gv:     schema.GroupVersions{{Version: "__internal"}, {Version: "v1"}},
			out: &ExternalTestType1{
				MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType1"},
				A: "test",
			},
		},
		// unversioned type returned as-is
		{
			scheme: GetTestScheme(),
			in:     &UnversionedType{A: "test"},
			gv:     schema.GroupVersions{{Version: "v1"}},
			same:   true,
			out: &UnversionedType{
				MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "UnversionedType"},
				A: "test",
			},
		},
		// unversioned type returned when not included in the target types
		{
			scheme: GetTestScheme(),
			in:     &UnversionedType{A: "test"},
			gv:     schema.GroupVersions{{Group: "other", Version: "v2"}},
			same:   true,
			out: &UnversionedType{
				MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "UnversionedType"},
				A: "test",
			},
		},
		// detected as already being in the target version
		{
			scheme: GetTestScheme(),
			in:     &ExternalTestType1{A: "test"},
			gv:     schema.GroupVersions{{Version: "v1"}},
			same:   true,
			out: &ExternalTestType1{
				MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType1"},
				A: "test",
			},
		},
		// detected as already being in the first target version
		{
			scheme: GetTestScheme(),
			in:     &ExternalTestType1{A: "test"},
			gv:     schema.GroupVersions{{Version: "v1"}, {Version: "__internal"}},
			same:   true,
			out: &ExternalTestType1{
				MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType1"},
				A: "test",
			},
		},
		// detected as already being in the first target version
		{
			scheme: GetTestScheme(),
			in:     &ExternalTestType1{A: "test"},
			gv:     schema.GroupVersions{{Version: "v1"}, {Version: "__internal"}},
			same:   true,
			out: &ExternalTestType1{
				MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType1"},
				A: "test",
			},
		},
		// the external type is registered in multiple groups, versions, and kinds, and can be targeted to all of them (1/3): different kind
		{
			scheme: GetTestScheme(),
			in:     &ExternalTestType1{A: "test"},
			gv:     testGroupVersioner{ok: true, target: schema.GroupVersionKind{Kind: "TestType3", Version: "v1"}},
			same:   true,
			out: &ExternalTestType1{
				MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType3"},
				A: "test",
			},
		},
		// the external type is registered in multiple groups, versions, and kinds, and can be targeted to all of them (2/3): different gv
		{
			scheme: GetTestScheme(),
			in:     &ExternalTestType1{A: "test"},
			gv:     testGroupVersioner{ok: true, target: schema.GroupVersionKind{Kind: "TestType3", Group: "custom", Version: "v1"}},
			same:   true,
			out: &ExternalTestType1{
				MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "custom/v1", ObjectKind: "TestType3"},
				A: "test",
			},
		},
		// the external type is registered in multiple groups, versions, and kinds, and can be targeted to all of them (3/3): different gvk
		{
			scheme: GetTestScheme(),
			in:     &ExternalTestType1{A: "test"},
			gv:     testGroupVersioner{ok: true, target: schema.GroupVersionKind{Group: "custom", Version: "v1", Kind: "TestType5"}},
			same:   true,
			out: &ExternalTestType1{
				MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "custom/v1", ObjectKind: "TestType5"},
				A: "test",
			},
		},
		// multi group versioner recognizes multiple groups and forces the output to a particular version, copies because version differs
		{
			scheme: GetTestScheme(),
			in:     &ExternalTestType1{A: "test"},
			gv:     runtime.NewMultiGroupVersioner(schema.GroupVersion{Group: "other", Version: "v2"}, schema.GroupKind{Group: "custom", Kind: "TestType3"}, schema.GroupKind{Kind: "TestType1"}),
			out: &ExternalTestType1{
				MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "other/v2", ObjectKind: "TestType1"},
				A: "test",
			},
		},
		// multi group versioner recognizes multiple groups and forces the output to a particular version, copies because version differs
		{
			scheme: GetTestScheme(),
			in:     &ExternalTestType1{A: "test"},
			gv:     runtime.NewMultiGroupVersioner(schema.GroupVersion{Group: "other", Version: "v2"}, schema.GroupKind{Kind: "TestType1"}, schema.GroupKind{Group: "custom", Kind: "TestType3"}),
			out: &ExternalTestType1{
				MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "other/v2", ObjectKind: "TestType1"},
				A: "test",
			},
		},
		// multi group versioner is unable to find a match when kind AND group don't match (there is no TestType1 kind in group "other", and no kind "TestType5" in the default group)
		{
			scheme: GetTestScheme(),
			in:     &TestType1{A: "test"},
			gv:     runtime.NewMultiGroupVersioner(schema.GroupVersion{Group: "custom", Version: "v1"}, schema.GroupKind{Group: "other"}, schema.GroupKind{Kind: "TestType5"}),
			errFn: func(err error) bool {
				return err != nil && strings.Contains(err.Error(), "is not suitable for converting")
			},
		},
		// multi group versioner recognizes multiple groups and forces the output to a particular version, performs no copy
		{
			scheme: GetTestScheme(),
			in:     &ExternalTestType1{A: "test"},
			gv:     runtime.NewMultiGroupVersioner(schema.GroupVersion{Group: "", Version: "v1"}, schema.GroupKind{Group: "custom", Kind: "TestType3"}, schema.GroupKind{Kind: "TestType1"}),
			same:   true,
			out: &ExternalTestType1{
				MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType1"},
				A: "test",
			},
		},
		// multi group versioner recognizes multiple groups and forces the output to a particular version, performs no copy
		{
			scheme: GetTestScheme(),
			in:     &ExternalTestType1{A: "test"},
			gv:     runtime.NewMultiGroupVersioner(schema.GroupVersion{Group: "", Version: "v1"}, schema.GroupKind{Kind: "TestType1"}, schema.GroupKind{Group: "custom", Kind: "TestType3"}),
			same:   true,
			out: &ExternalTestType1{
				MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType1"},
				A: "test",
			},
		},
		// group versioner can choose a particular target kind for a given input when kind is the same across group versions
		{
			scheme: GetTestScheme(),
			in:     &TestType1{A: "test"},
			gv:     testGroupVersioner{ok: true, target: schema.GroupVersionKind{Version: "v1", Kind: "TestType3"}},
			out: &ExternalTestType1{
				MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType3"},
				A: "test",
			},
		},
		// group versioner can choose a different kind
		{
			scheme: GetTestScheme(),
			in:     &TestType1{A: "test"},
			gv:     testGroupVersioner{ok: true, target: schema.GroupVersionKind{Kind: "TestType5", Group: "custom", Version: "v1"}},
			out: &ExternalTestType1{
				MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "custom/v1", ObjectKind: "TestType5"},
				A: "test",
			},
		},
	}
	for i, test := range testCases {
		original, _ := test.scheme.DeepCopy(test.in)
		out, err := test.scheme.ConvertToVersion(test.in, test.gv)
		switch {
		case test.errFn != nil:
			if !test.errFn(err) {
				t.Errorf("%d: unexpected error: %v", i, err)
			}
			continue
		case err != nil:
			t.Errorf("%d: unexpected error: %v", i, err)
			continue
		}
		if out == test.in {
			t.Errorf("%d: ConvertToVersion should always copy out: %#v", i, out)
			continue
		}

		if test.same {
			if !reflect.DeepEqual(original, test.in) {
				t.Errorf("%d: unexpected mutation of input: %s", i, diff.ObjectReflectDiff(original, test.in))
				continue
			}
			if !reflect.DeepEqual(out, test.out) {
				t.Errorf("%d: unexpected out: %s", i, diff.ObjectReflectDiff(out, test.out))
				continue
			}
			unsafe, err := test.scheme.UnsafeConvertToVersion(test.in, test.gv)
			if err != nil {
				t.Errorf("%d: unexpected error: %v", i, err)
				continue
			}
			if !reflect.DeepEqual(unsafe, test.out) {
				t.Errorf("%d: unexpected unsafe: %s", i, diff.ObjectReflectDiff(unsafe, test.out))
				continue
			}
			if unsafe != test.in {
				t.Errorf("%d: UnsafeConvertToVersion should return same object: %#v", i, unsafe)
				continue
			}
			continue
		}
		if !reflect.DeepEqual(out, test.out) {
			t.Errorf("%d: unexpected out: %s", i, diff.ObjectReflectDiff(out, test.out))
			continue
		}
	}
}
예제 #7
0
func TestTransformUnstructuredError(t *testing.T) {
	testCases := []struct {
		Req *http.Request
		Res *http.Response

		Resource string
		Name     string

		ErrFn       func(error) bool
		Transformed error
	}{
		{
			Resource: "foo",
			Name:     "bar",
			Req: &http.Request{
				Method: "POST",
			},
			Res: &http.Response{
				StatusCode: http.StatusConflict,
				Body:       ioutil.NopCloser(bytes.NewReader(nil)),
			},
			ErrFn: apierrors.IsAlreadyExists,
		},
		{
			Resource: "foo",
			Name:     "bar",
			Req: &http.Request{
				Method: "PUT",
			},
			Res: &http.Response{
				StatusCode: http.StatusConflict,
				Body:       ioutil.NopCloser(bytes.NewReader(nil)),
			},
			ErrFn: apierrors.IsConflict,
		},
		{
			Resource: "foo",
			Name:     "bar",
			Req:      &http.Request{},
			Res: &http.Response{
				StatusCode: http.StatusNotFound,
				Body:       ioutil.NopCloser(bytes.NewReader(nil)),
			},
			ErrFn: apierrors.IsNotFound,
		},
		{
			Req: &http.Request{},
			Res: &http.Response{
				StatusCode: http.StatusBadRequest,
				Body:       ioutil.NopCloser(bytes.NewReader(nil)),
			},
			ErrFn: apierrors.IsBadRequest,
		},
		{
			// status in response overrides transformed result
			Req:   &http.Request{},
			Res:   &http.Response{StatusCode: http.StatusBadRequest, Body: ioutil.NopCloser(bytes.NewReader([]byte(`{"kind":"Status","apiVersion":"v1","status":"Failure","code":404}`)))},
			ErrFn: apierrors.IsBadRequest,
			Transformed: &apierrors.StatusError{
				ErrStatus: metav1.Status{Status: metav1.StatusFailure, Code: http.StatusNotFound},
			},
		},
		{
			// successful status is ignored
			Req:   &http.Request{},
			Res:   &http.Response{StatusCode: http.StatusBadRequest, Body: ioutil.NopCloser(bytes.NewReader([]byte(`{"kind":"Status","apiVersion":"v1","status":"Success","code":404}`)))},
			ErrFn: apierrors.IsBadRequest,
		},
		{
			// empty object does not change result
			Req:   &http.Request{},
			Res:   &http.Response{StatusCode: http.StatusBadRequest, Body: ioutil.NopCloser(bytes.NewReader([]byte(`{}`)))},
			ErrFn: apierrors.IsBadRequest,
		},
		{
			// we default apiVersion for backwards compatibility with old clients
			// TODO: potentially remove in 1.7
			Req:   &http.Request{},
			Res:   &http.Response{StatusCode: http.StatusBadRequest, Body: ioutil.NopCloser(bytes.NewReader([]byte(`{"kind":"Status","status":"Failure","code":404}`)))},
			ErrFn: apierrors.IsBadRequest,
			Transformed: &apierrors.StatusError{
				ErrStatus: metav1.Status{Status: metav1.StatusFailure, Code: http.StatusNotFound},
			},
		},
		{
			// we do not default kind
			Req:   &http.Request{},
			Res:   &http.Response{StatusCode: http.StatusBadRequest, Body: ioutil.NopCloser(bytes.NewReader([]byte(`{"status":"Failure","code":404}`)))},
			ErrFn: apierrors.IsBadRequest,
		},
	}

	for i, testCase := range testCases {
		r := &Request{
			content:      defaultContentConfig(),
			serializers:  defaultSerializers(),
			resourceName: testCase.Name,
			resource:     testCase.Resource,
		}
		result := r.transformResponse(testCase.Res, testCase.Req)
		err := result.err
		if !testCase.ErrFn(err) {
			t.Errorf("unexpected error: %v", err)
			continue
		}
		if !apierrors.IsUnexpectedServerError(err) {
			t.Errorf("%d: unexpected error type: %v", i, err)
		}
		if len(testCase.Name) != 0 && !strings.Contains(err.Error(), testCase.Name) {
			t.Errorf("unexpected error string: %s", err)
		}
		if len(testCase.Resource) != 0 && !strings.Contains(err.Error(), testCase.Resource) {
			t.Errorf("unexpected error string: %s", err)
		}

		// verify Error() properly transforms the error
		transformed := result.Error()
		expect := testCase.Transformed
		if expect == nil {
			expect = err
		}
		if !reflect.DeepEqual(expect, transformed) {
			t.Errorf("%d: unexpected Error(): %s", i, diff.ObjectReflectDiff(expect, transformed))
		}

		// verify result.Get properly transforms the error
		if _, err := result.Get(); !reflect.DeepEqual(expect, err) {
			t.Errorf("%d: unexpected error on Get(): %s", i, diff.ObjectReflectDiff(expect, err))
		}

		// verify result.Into properly handles the error
		if err := result.Into(&api.Pod{}); !reflect.DeepEqual(expect, err) {
			t.Errorf("%d: unexpected error on Into(): %s", i, diff.ObjectReflectDiff(expect, err))
		}

		// verify result.Raw leaves the error in the untransformed state
		if _, err := result.Raw(); !reflect.DeepEqual(result.err, err) {
			t.Errorf("%d: unexpected error on Raw(): %s", i, diff.ObjectReflectDiff(expect, err))
		}
	}
}
예제 #8
0
func doRoundTrip(t *testing.T, group testapi.TestGroup, kind string) {
	// We do fuzzing on the internal version of the object, and only then
	// convert to the external version. This is because custom fuzzing
	// function are only supported for internal objects.
	internalObj, err := api.Scheme.New(group.InternalGroupVersion().WithKind(kind))
	if err != nil {
		t.Fatalf("Couldn't create internal object %v: %v", kind, err)
	}
	seed := rand.Int63()
	apitesting.FuzzerFor(t, group.InternalGroupVersion(), rand.NewSource(seed)).
		// We are explicitly overwriting custom fuzzing functions, to ensure
		// that InitContainers and their statuses are not generated. This is
		// because in thise test we are simply doing json operations, in which
		// those disappear.
		Funcs(
			func(s *api.PodSpec, c fuzz.Continue) {
				c.FuzzNoCustom(s)
				s.InitContainers = nil
			},
			func(s *api.PodStatus, c fuzz.Continue) {
				c.FuzzNoCustom(s)
				s.InitContainerStatuses = nil
			},
		).Fuzz(internalObj)

	item, err := api.Scheme.New(group.GroupVersion().WithKind(kind))
	if err != nil {
		t.Fatalf("Couldn't create external object %v: %v", kind, err)
	}
	if err := api.Scheme.Convert(internalObj, item, nil); err != nil {
		t.Fatalf("Conversion for %v failed: %v", kind, err)
	}

	data, err := json.Marshal(item)
	if err != nil {
		t.Errorf("Error when marshaling object: %v", err)
		return
	}
	unstr := make(map[string]interface{})
	err = json.Unmarshal(data, &unstr)
	if err != nil {
		t.Errorf("Error when unmarshaling to unstructured: %v", err)
		return
	}

	data, err = json.Marshal(unstr)
	if err != nil {
		t.Errorf("Error when marshaling unstructured: %v", err)
		return
	}
	unmarshalledObj := reflect.New(reflect.TypeOf(item).Elem()).Interface()
	err = json.Unmarshal(data, &unmarshalledObj)
	if err != nil {
		t.Errorf("Error when unmarshaling to object: %v", err)
		return
	}
	if !api.Semantic.DeepEqual(item, unmarshalledObj) {
		t.Errorf("Object changed during JSON operations, diff: %v", diff.ObjectReflectDiff(item, unmarshalledObj))
		return
	}

	// TODO; Enable the following part of test once to/from unstructured
	// format conversions are implemented.
	/*
		newUnstr := make(map[string]interface{})
		err = unstructured.NewConverter().ToUnstructured(item, &newUnstr)
		if err != nil {
			t.Errorf("ToUnstructured failed: %v", err)
			return
		}

		newObj := reflect.New(reflect.TypeOf(item).Elem()).Interface().(runtime.Object)
		err = unstructured.NewConverter().FromUnstructured(newUnstr, newObj)
		if err != nil {
			t.Errorf("FromUnstructured failed: %v", err)
			return
		}

		if !api.Semantic.DeepEqual(item, newObj) {
			t.Errorf("Object changed, diff: %v", diff.ObjectReflectDiff(item, newObj))
		}
	*/
}