func roundTrip(t *testing.T, codec runtime.Codec, item runtime.Object) {
	printer := spew.ConfigState{DisableMethods: true}

	name := reflect.TypeOf(item).Elem().Name()
	data, err := codec.Encode(item)
	if err != nil {
		t.Errorf("%v: %v (%s)", name, err, printer.Sprintf("%#v", item))
		return
	}

	obj2, err := codec.Decode(data)
	if err != nil {
		t.Errorf("0: %v: %v\nCodec: %v\nData: %s\nSource: %#v", name, err, codec, string(data), printer.Sprintf("%#v", item))
		return
	}
	if !api.Semantic.DeepEqual(item, obj2) {
		t.Errorf("1: %v: diff: %v\nCodec: %v\nData: %s\nSource: %#v\nFinal: %#v", name, util.ObjectGoPrintDiff(item, obj2), codec, string(data), printer.Sprintf("%#v", item), printer.Sprintf("%#v", obj2))
		return
	}

	obj3 := reflect.New(reflect.TypeOf(item).Elem()).Interface().(runtime.Object)
	err = codec.DecodeInto(data, obj3)
	if err != nil {
		t.Errorf("2: %v: %v", name, err)
		return
	}
	if !api.Semantic.DeepEqual(item, obj3) {
		t.Errorf("3: %v: diff: %v\nCodec: %v", name, util.ObjectDiff(item, obj3), codec)
		return
	}
}
Exemple #2
0
func validateEvent(actualEvent *api.Event, expectedEvent *api.Event, t *testing.T) (*api.Event, error) {
	expectCompression := expectedEvent.Count > 1
	// Just check that the timestamp was set.
	if actualEvent.FirstTimestamp.IsZero() || actualEvent.LastTimestamp.IsZero() {
		t.Errorf("timestamp wasn't set: %#v", *actualEvent)
	}
	if actualEvent.FirstTimestamp.Equal(actualEvent.LastTimestamp) {
		if expectCompression {
			t.Errorf("FirstTimestamp (%q) and LastTimestamp (%q) must be equal to indicate only one occurrence of the event, but were different. Actual Event: %#v", actualEvent.FirstTimestamp, actualEvent.LastTimestamp, *actualEvent)
		}
	} else {
		if !expectCompression {
			t.Errorf("FirstTimestamp (%q) and LastTimestamp (%q) must be different to indicate event compression happened, but were the same. Actual Event: %#v", actualEvent.FirstTimestamp, actualEvent.LastTimestamp, *actualEvent)
		}
	}
	actualFirstTimestamp := actualEvent.FirstTimestamp
	actualLastTimestamp := actualEvent.LastTimestamp
	// Temp clear time stamps for comparison because actual values don't matter for comparison
	actualEvent.FirstTimestamp = expectedEvent.FirstTimestamp
	actualEvent.LastTimestamp = expectedEvent.LastTimestamp
	// Check that name has the right prefix.
	if n, en := actualEvent.Name, expectedEvent.Name; !strings.HasPrefix(n, en) {
		t.Errorf("Name '%v' does not contain prefix '%v'", n, en)
	}
	actualEvent.Name = expectedEvent.Name
	if e, a := expectedEvent, actualEvent; !reflect.DeepEqual(e, a) {
		t.Errorf("diff: %s", util.ObjectGoPrintDiff(e, a))
	}
	actualEvent.FirstTimestamp = actualFirstTimestamp
	actualEvent.LastTimestamp = actualLastTimestamp
	return actualEvent, nil
}
Exemple #3
0
func (r *subjectAccessTest) runTest(t *testing.T) {
	storage := NewREST(subjectaccessreview.NewRegistry(subjectaccessreview.NewREST(r.authorizer)))

	expectedResponse := &authorizationapi.SubjectAccessReviewResponse{
		Namespace: r.reviewRequest.Action.Namespace,
		Allowed:   r.authorizer.allowed,
		Reason:    r.authorizer.reason,
	}

	expectedAttributes := authorizer.ToDefaultAuthorizationAttributes(r.reviewRequest.Action)

	ctx := kapi.WithNamespace(kapi.NewContext(), r.reviewRequest.Action.Namespace)
	obj, err := storage.Create(ctx, r.reviewRequest)
	if err != nil && len(r.authorizer.err) == 0 {
		t.Fatalf("unexpected error: %v", err)
	}
	if len(r.authorizer.err) != 0 {
		if err == nil {
			t.Fatalf("unexpected non-error: %v", err)
		}
		if e, a := r.authorizer.err, err.Error(); e != a {
			t.Fatalf("expected %v, got %v", e, a)
		}

		return
	}

	switch obj.(type) {
	case *authorizationapi.SubjectAccessReviewResponse:
		if !reflect.DeepEqual(expectedResponse, obj) {
			t.Errorf("diff %v", util.ObjectGoPrintDiff(expectedResponse, obj))
		}
	case nil:
		if len(r.authorizer.err) == 0 {
			t.Fatal("unexpected nil object")
		}

	default:
		t.Errorf("Unexpected obj type: %v", obj)
	}

	if !reflect.DeepEqual(expectedAttributes, r.authorizer.actualAttributes) {
		t.Errorf("diff %v", util.ObjectGoPrintDiff(expectedAttributes, r.authorizer.actualAttributes))
	}
}
Exemple #4
0
func (r *subjectAccessTest) runTest(t *testing.T) {
	const namespace = "unittest"

	storage := REST{r.authorizer}

	expectedResponse := &authorizationapi.SubjectAccessReviewResponse{
		Namespace: namespace,
		Allowed:   r.authorizer.allowed,
		Reason:    r.authorizer.reason,
	}

	expectedAttributes := &authorizer.DefaultAuthorizationAttributes{
		Verb:     r.reviewRequest.Verb,
		Resource: r.reviewRequest.Resource,
	}

	ctx := kapi.WithNamespace(kapi.NewContext(), namespace)
	obj, err := storage.Create(ctx, r.reviewRequest)
	if err != nil && len(r.authorizer.err) == 0 {
		t.Fatalf("unexpected error: %v", err)
	}
	if err == nil && len(r.authorizer.err) != 0 {
		t.Fatalf("unexpected non-error: %v", err)
	}

	switch obj.(type) {
	case *authorizationapi.SubjectAccessReviewResponse:
		if !reflect.DeepEqual(expectedResponse, obj) {
			t.Errorf("diff %v", util.ObjectGoPrintDiff(expectedResponse, obj))
		}
	case nil:
		if len(r.authorizer.err) == 0 {
			t.Fatal("unexpected nil object")
		}

	default:
		t.Errorf("Unexpected obj type: %v", obj)
	}

	if !reflect.DeepEqual(expectedAttributes, r.authorizer.actualAttributes) {
		t.Errorf("diff %v", util.ObjectGoPrintDiff(expectedAttributes, r.authorizer.actualAttributes))
	}
}
func TestAPIServerDefaults(t *testing.T) {
	defaults := apiserveroptions.NewAPIServer()

	// This is a snapshot of the default config
	// If the default changes (new fields are added, or default values change), we want to know
	// Once we've reacted to the changes appropriately in BuildKubernetesMasterConfig(), update this expected default to match the new upstream defaults
	expectedDefaults := &apiserveroptions.APIServer{
		ServerRunOptions: &genericapiserver.ServerRunOptions{
			BindAddress:          net.ParseIP("0.0.0.0"),
			CertDirectory:        "/var/run/kubernetes",
			InsecureBindAddress:  net.ParseIP("127.0.0.1"),
			InsecurePort:         8080,
			LongRunningRequestRE: "(/|^)((watch|proxy)(/|$)|(logs?|portforward|exec|attach)/?$)",
			MaxRequestsInFlight:  400,
			SecurePort:           6443,
		},
		APIGroupPrefix:          "/apis",
		APIPrefix:               "/api",
		AdmissionControl:        "AlwaysAdmit",
		AuthorizationMode:       "AlwaysAllow",
		DeleteCollectionWorkers: 1,
		EnableLogsSupport:       true,
		EnableProfiling:         true,
		EnableWatchCache:        true,
		EtcdConfig: etcdstorage.EtcdConfig{
			Prefix: "/registry",
		},
		EventTTL:               1 * time.Hour,
		MasterCount:            1,
		MasterServiceNamespace: "default",
		MinRequestTimeout:      1800,
		RuntimeConfig:          util.ConfigurationMap{},
		StorageVersions:        registered.AllPreferredGroupVersions(),
		DefaultStorageVersions: registered.AllPreferredGroupVersions(),
		KubeletConfig: kubeletclient.KubeletClientConfig{
			Port:        10250,
			EnableHttps: true,
			HTTPTimeout: time.Duration(5) * time.Second,
		},
	}

	if !reflect.DeepEqual(defaults, expectedDefaults) {
		t.Logf("expected defaults, actual defaults: \n%s", util.ObjectGoPrintDiff(expectedDefaults, defaults))
		t.Errorf("Got different defaults than expected, adjust in BuildKubernetesMasterConfig and update expectedDefaults")
	}
}
Exemple #6
0
func TestEventUpdate(t *testing.T) {
	eventA := &api.Event{
		ObjectMeta:     api.ObjectMeta{Name: "foo", Namespace: api.NamespaceDefault},
		Reason:         "forTesting",
		InvolvedObject: api.ObjectReference{Name: "foo", Namespace: api.NamespaceDefault},
	}
	eventB := &api.Event{
		ObjectMeta:     api.ObjectMeta{Name: "foo", Namespace: api.NamespaceDefault},
		Reason:         "for testing again",
		InvolvedObject: api.ObjectReference{Name: "foo", Namespace: api.NamespaceDefault},
	}
	eventC := &api.Event{
		ObjectMeta:     api.ObjectMeta{Name: "foo", Namespace: api.NamespaceDefault, ResourceVersion: "1"},
		Reason:         "for testing again something else",
		InvolvedObject: api.ObjectReference{Name: "foo", Namespace: api.NamespaceDefault},
	}

	nodeWithEventA := tools.EtcdResponseWithError{
		R: &etcd.Response{
			Node: &etcd.Node{
				Value:         runtime.EncodeOrDie(testapi.Codec(), eventA),
				ModifiedIndex: 1,
				CreatedIndex:  1,
				TTL:           int64(testTTL),
			},
		},
		E: nil,
	}

	nodeWithEventB := tools.EtcdResponseWithError{
		R: &etcd.Response{
			Node: &etcd.Node{
				Value:         runtime.EncodeOrDie(testapi.Codec(), eventB),
				ModifiedIndex: 1,
				CreatedIndex:  1,
				TTL:           int64(testTTL),
			},
		},
		E: nil,
	}

	nodeWithEventC := tools.EtcdResponseWithError{
		R: &etcd.Response{
			Node: &etcd.Node{
				Value:         runtime.EncodeOrDie(testapi.Codec(), eventC),
				ModifiedIndex: 1,
				CreatedIndex:  1,
				TTL:           int64(testTTL),
			},
		},
		E: nil,
	}

	emptyNode := tools.EtcdResponseWithError{
		R: &etcd.Response{},
		E: tools.EtcdErrorNotFound,
	}

	ctx := api.NewDefaultContext()
	key := "foo"
	path, err := etcdgeneric.NamespaceKeyFunc(ctx, "/events", key)
	path = etcdtest.AddPrefix(path)
	if err != nil {
		t.Errorf("Unexpected error: %v", err)
	}

	table := map[string]struct {
		existing tools.EtcdResponseWithError
		expect   tools.EtcdResponseWithError
		toUpdate runtime.Object
		errOK    func(error) bool
	}{
		"doesNotExist": {
			existing: emptyNode,
			expect:   nodeWithEventA,
			toUpdate: eventA,
			errOK:    func(err error) bool { return err == nil },
		},
		"doesNotExist2": {
			existing: emptyNode,
			expect:   nodeWithEventB,
			toUpdate: eventB,
			errOK:    func(err error) bool { return err == nil },
		},
		"replaceExisting": {
			existing: nodeWithEventA,
			expect:   nodeWithEventC,
			toUpdate: eventC,
			errOK:    func(err error) bool { return err == nil },
		},
	}

	for name, item := range table {
		storage, fakeClient := newStorage(t)
		fakeClient.Data[path] = item.existing
		_, _, err := storage.Update(ctx, item.toUpdate)
		if !item.errOK(err) {
			t.Errorf("%v: unexpected error: %v", name, err)
		}

		// nullify fields set by infrastructure
		received := fakeClient.Data[path]
		var event api.Event
		if err := testapi.Codec().DecodeInto([]byte(received.R.Node.Value), &event); err != nil {
			t.Errorf("unexpected error: %v", err)
		}
		event.ObjectMeta.CreationTimestamp = util.Time{}
		event.ObjectMeta.UID = ""
		received.R.Node.Value = runtime.EncodeOrDie(testapi.Codec(), &event)

		if e, a := item.expect, received; !reflect.DeepEqual(e, a) {
			t.Errorf("%v:\n%s", name, util.ObjectGoPrintDiff(e, a))
		}
	}
}
func (tc *patchTestCase) Run(t *testing.T) {
	t.Logf("Starting test %s", tc.name)

	namespace := tc.startingPod.Namespace
	name := tc.startingPod.Name

	codec := testapi.Default.Codec()
	admit := tc.admit
	if admit == nil {
		admit = func(updatedObject runtime.Object) error {
			return nil
		}
	}

	testPatcher := &testPatcher{}
	testPatcher.startingPod = tc.startingPod
	testPatcher.updatePod = tc.updatePod

	ctx := api.NewDefaultContext()
	ctx = api.WithNamespace(ctx, namespace)

	namer := &testNamer{namespace, name}

	versionedObj, err := api.Scheme.ConvertToVersion(&api.Pod{}, "v1")
	if err != nil {
		t.Errorf("%s: unexpected error: %v", tc.name, err)
		return
	}

	for _, patchType := range []api.PatchType{api.JSONPatchType, api.MergePatchType, api.StrategicMergePatchType} {
		// TODO SUPPORT THIS!
		if patchType == api.JSONPatchType {
			continue
		}
		t.Logf("Working with patchType %v", patchType)

		originalObjJS, err := runtime.Encode(codec, tc.startingPod)
		if err != nil {
			t.Errorf("%s: unexpected error: %v", tc.name, err)
			return
		}
		changedJS, err := runtime.Encode(codec, tc.changedPod)
		if err != nil {
			t.Errorf("%s: unexpected error: %v", tc.name, err)
			return
		}

		patch := []byte{}
		switch patchType {
		case api.JSONPatchType:
			continue

		case api.StrategicMergePatchType:
			patch, err = strategicpatch.CreateStrategicMergePatch(originalObjJS, changedJS, versionedObj)
			if err != nil {
				t.Errorf("%s: unexpected error: %v", tc.name, err)
				return
			}

		case api.MergePatchType:
			patch, err = jsonpatch.CreateMergePatch(originalObjJS, changedJS)
			if err != nil {
				t.Errorf("%s: unexpected error: %v", tc.name, err)
				return
			}

		}

		resultObj, err := patchResource(ctx, admit, 1*time.Second, versionedObj, testPatcher, name, patchType, patch, namer, codec)
		if len(tc.expectedError) != 0 {
			if err == nil || err.Error() != tc.expectedError {
				t.Errorf("%s: expected error %v, but got %v", tc.name, tc.expectedError, err)
				return
			}
		} else {
			if err != nil {
				t.Errorf("%s: unexpected error: %v", tc.name, err)
				return
			}
		}

		if tc.expectedPod == nil {
			if resultObj != nil {
				t.Errorf("%s: unexpected result: %v", tc.name, resultObj)
			}
			return
		}

		resultPod := resultObj.(*api.Pod)

		// roundtrip to get defaulting
		expectedJS, err := runtime.Encode(codec, tc.expectedPod)
		if err != nil {
			t.Errorf("%s: unexpected error: %v", tc.name, err)
			return
		}
		expectedObj, err := runtime.Decode(codec, expectedJS)
		if err != nil {
			t.Errorf("%s: unexpected error: %v", tc.name, err)
			return
		}
		reallyExpectedPod := expectedObj.(*api.Pod)

		if !reflect.DeepEqual(*reallyExpectedPod, *resultPod) {
			t.Errorf("%s mismatch: %v\n", tc.name, util.ObjectGoPrintDiff(reallyExpectedPod, resultPod))
			return
		}
	}

}
Exemple #8
0
func TestControllerStart(t *testing.T) {
	two := int64(2)
	testCases := []struct {
		stream *api.ImageStream
		run    bool
	}{
		{
			stream: &api.ImageStream{
				ObjectMeta: kapi.ObjectMeta{
					Annotations: map[string]string{api.DockerImageRepositoryCheckAnnotation: unversioned.Now().UTC().Format(time.RFC3339)},
					Name:        "test",
					Namespace:   "other",
				},
			},
		},
		{
			stream: &api.ImageStream{
				ObjectMeta: kapi.ObjectMeta{
					Annotations: map[string]string{api.DockerImageRepositoryCheckAnnotation: unversioned.Now().UTC().Format(time.RFC3339)},
					Name:        "test",
					Namespace:   "other",
				},
				Spec: api.ImageStreamSpec{
					DockerImageRepository: "test/other",
				},
			},
		},
		{
			stream: &api.ImageStream{
				ObjectMeta: kapi.ObjectMeta{
					Annotations: map[string]string{api.DockerImageRepositoryCheckAnnotation: "a random error"},
					Name:        "test",
					Namespace:   "other",
				},
				Spec: api.ImageStreamSpec{
					DockerImageRepository: "test/other",
				},
			},
		},

		// references are ignored
		{
			stream: &api.ImageStream{
				ObjectMeta: kapi.ObjectMeta{Name: "test", Namespace: "other"},
				Spec: api.ImageStreamSpec{
					Tags: map[string]api.TagReference{
						"latest": {
							From:      &kapi.ObjectReference{Kind: "DockerImage", Name: "test/other:latest"},
							Reference: true,
						},
					},
				},
			},
		},
		{
			stream: &api.ImageStream{
				ObjectMeta: kapi.ObjectMeta{Name: "test", Namespace: "other"},
				Spec: api.ImageStreamSpec{
					Tags: map[string]api.TagReference{
						"latest": {
							From:      &kapi.ObjectReference{Kind: "AnotherImage", Name: "test/other:latest"},
							Reference: true,
						},
					},
				},
			},
		},

		// spec tag will be imported
		{
			run: true,
			stream: &api.ImageStream{
				ObjectMeta: kapi.ObjectMeta{Name: "test", Namespace: "other"},
				Spec: api.ImageStreamSpec{
					Tags: map[string]api.TagReference{
						"latest": {
							From: &kapi.ObjectReference{Kind: "DockerImage", Name: "test/other:latest"},
						},
					},
				},
			},
		},
		// spec tag with generation with no pending status will be imported
		{
			run: true,
			stream: &api.ImageStream{
				ObjectMeta: kapi.ObjectMeta{Name: "test", Namespace: "other"},
				Spec: api.ImageStreamSpec{
					Tags: map[string]api.TagReference{
						"latest": {
							From:       &kapi.ObjectReference{Kind: "DockerImage", Name: "test/other:latest"},
							Generation: &two,
						},
					},
				},
			},
		},
		// spec tag with generation with older status generation will be imported
		{
			run: true,
			stream: &api.ImageStream{
				ObjectMeta: kapi.ObjectMeta{Name: "test", Namespace: "other"},
				Spec: api.ImageStreamSpec{
					Tags: map[string]api.TagReference{
						"latest": {
							From:       &kapi.ObjectReference{Kind: "DockerImage", Name: "test/other:latest"},
							Generation: &two,
						},
					},
				},
				Status: api.ImageStreamStatus{
					Tags: map[string]api.TagEventList{"latest": {Items: []api.TagEvent{{Generation: 1}}}},
				},
			},
		},
		// spec tag with generation with status condition error and equal generation will not be imported
		{
			stream: &api.ImageStream{
				ObjectMeta: kapi.ObjectMeta{
					Annotations: map[string]string{api.DockerImageRepositoryCheckAnnotation: unversioned.Now().UTC().Format(time.RFC3339)},
					Name:        "test",
					Namespace:   "other",
				},
				Spec: api.ImageStreamSpec{
					Tags: map[string]api.TagReference{
						"latest": {
							From:       &kapi.ObjectReference{Kind: "DockerImage", Name: "test/other:latest"},
							Generation: &two,
						},
					},
				},
				Status: api.ImageStreamStatus{
					Tags: map[string]api.TagEventList{"latest": {Conditions: []api.TagEventCondition{
						{
							Type:       api.ImportSuccess,
							Status:     kapi.ConditionFalse,
							Generation: 2,
						},
					}}},
				},
			},
		},
		// spec tag with generation with status condition error and older generation will be imported
		{
			run: true,
			stream: &api.ImageStream{
				ObjectMeta: kapi.ObjectMeta{
					Annotations: map[string]string{api.DockerImageRepositoryCheckAnnotation: unversioned.Now().UTC().Format(time.RFC3339)},
					Name:        "test",
					Namespace:   "other",
				},
				Spec: api.ImageStreamSpec{
					Tags: map[string]api.TagReference{
						"latest": {
							From:       &kapi.ObjectReference{Kind: "DockerImage", Name: "test/other:latest"},
							Generation: &two,
						},
					},
				},
				Status: api.ImageStreamStatus{
					Tags: map[string]api.TagEventList{"latest": {Conditions: []api.TagEventCondition{
						{
							Type:       api.ImportSuccess,
							Status:     kapi.ConditionFalse,
							Generation: 1,
						},
					}}},
				},
			},
		},
		// spec tag with generation with older status generation will be imported
		{
			run: true,
			stream: &api.ImageStream{
				ObjectMeta: kapi.ObjectMeta{
					Annotations: map[string]string{api.DockerImageRepositoryCheckAnnotation: unversioned.Now().UTC().Format(time.RFC3339)},
					Name:        "test",
					Namespace:   "other",
				},
				Spec: api.ImageStreamSpec{
					Tags: map[string]api.TagReference{
						"latest": {
							From:       &kapi.ObjectReference{Kind: "DockerImage", Name: "test/other:latest"},
							Generation: &two,
						},
					},
				},
				Status: api.ImageStreamStatus{
					Tags: map[string]api.TagEventList{"latest": {Items: []api.TagEvent{{Generation: 1}}}},
				},
			},
		},
	}

	for i, test := range testCases {
		fake := &client.Fake{}
		c := ImportController{streams: fake}
		other, err := kapi.Scheme.DeepCopy(test.stream)
		if err != nil {
			t.Fatal(err)
		}

		if err := c.Next(test.stream); err != nil {
			t.Errorf("%d: unexpected error: %v", i, err)
		}
		if test.run {
			if len(fake.Actions()) == 0 {
				t.Errorf("%d: expected remote calls: %#v", i, fake)
			}
		} else {
			if !kapi.Semantic.DeepEqual(test.stream, other) {
				t.Errorf("%d: did not expect change to stream: %s", i, util.ObjectGoPrintDiff(test.stream, other))
			}
			if len(fake.Actions()) != 0 {
				t.Errorf("%d: did not expect remote calls", i)
			}
		}
	}
}
Exemple #9
0
func TestWebhook(t *testing.T) {
	serv := new(recorderService)
	s, err := NewTestServer(serv, serverCert, serverKey, caCert)
	if err != nil {
		t.Fatal(err)
	}
	defer s.Close()

	wh, err := newAuthorizer(s.URL, clientCert, clientKey, caCert)
	if err != nil {
		t.Fatal(err)
	}

	expTypeMeta := unversioned.TypeMeta{
		APIVersion: "authorization.k8s.io/v1beta1",
		Kind:       "SubjectAccessReview",
	}

	tests := []struct {
		attr authorizer.Attributes
		want v1beta1.SubjectAccessReview
	}{
		{
			attr: authorizer.AttributesRecord{User: &user.DefaultInfo{}},
			want: v1beta1.SubjectAccessReview{
				TypeMeta: expTypeMeta,
				Spec: v1beta1.SubjectAccessReviewSpec{
					NonResourceAttributes: &v1beta1.NonResourceAttributes{},
				},
			},
		},
		{
			attr: authorizer.AttributesRecord{User: &user.DefaultInfo{Name: "jane"}},
			want: v1beta1.SubjectAccessReview{
				TypeMeta: expTypeMeta,
				Spec: v1beta1.SubjectAccessReviewSpec{
					User: "******",
					NonResourceAttributes: &v1beta1.NonResourceAttributes{},
				},
			},
		},
		{
			attr: authorizer.AttributesRecord{
				User: &user.DefaultInfo{
					Name:   "jane",
					UID:    "1",
					Groups: []string{"group1", "group2"},
				},
				Verb:            "GET",
				Namespace:       "kittensandponies",
				APIGroup:        "group3",
				Resource:        "pods",
				ResourceRequest: true,
				Path:            "/foo",
			},
			want: v1beta1.SubjectAccessReview{
				TypeMeta: expTypeMeta,
				Spec: v1beta1.SubjectAccessReviewSpec{
					User:   "******",
					Groups: []string{"group1", "group2"},
					ResourceAttributes: &v1beta1.ResourceAttributes{
						Verb:      "GET",
						Namespace: "kittensandponies",
						Group:     "group3",
						Resource:  "pods",
					},
				},
			},
		},
	}

	for i, tt := range tests {
		if err := wh.Authorize(tt.attr); err != nil {
			t.Errorf("case %d: authorization failed: %v", i, err)
			continue
		}

		gotAttr, err := serv.Last()
		if err != nil {
			t.Errorf("case %d: failed to deserialize webhook request: %v", i, err)
			continue
		}
		if !reflect.DeepEqual(gotAttr, tt.want) {
			t.Errorf("case %d: got != want:\n%s", i, util.ObjectGoPrintDiff(gotAttr, tt.want))
		}
	}
}
func TestCMServerDefaults(t *testing.T) {
	defaults := cmapp.NewCMServer()

	// This is a snapshot of the default config
	// If the default changes (new fields are added, or default values change), we want to know
	// Once we've reacted to the changes appropriately in BuildKubernetesMasterConfig(), update this expected default to match the new upstream defaults
	expectedDefaults := &cmapp.CMServer{
		KubeControllerManagerConfiguration: componentconfig.KubeControllerManagerConfiguration{
			Port:                              10252, // disabled
			Address:                           "0.0.0.0",
			ConcurrentEndpointSyncs:           5,
			ConcurrentRCSyncs:                 5,
			ConcurrentRSSyncs:                 5,
			ConcurrentDaemonSetSyncs:          2,
			ConcurrentJobSyncs:                5,
			ConcurrentResourceQuotaSyncs:      5,
			ConcurrentDeploymentSyncs:         5,
			ConcurrentNamespaceSyncs:          2,
			LookupCacheSizeForRC:              4096,
			LookupCacheSizeForRS:              4096,
			LookupCacheSizeForDaemonSet:       1024,
			ServiceSyncPeriod:                 unversioned.Duration{Duration: 5 * time.Minute},
			NodeSyncPeriod:                    unversioned.Duration{Duration: 10 * time.Second},
			ResourceQuotaSyncPeriod:           unversioned.Duration{Duration: 5 * time.Minute},
			NamespaceSyncPeriod:               unversioned.Duration{Duration: 5 * time.Minute},
			PVClaimBinderSyncPeriod:           unversioned.Duration{Duration: 10 * time.Minute},
			HorizontalPodAutoscalerSyncPeriod: unversioned.Duration{Duration: 30 * time.Second},
			DeploymentControllerSyncPeriod:    unversioned.Duration{Duration: 30 * time.Second},
			MinResyncPeriod:                   unversioned.Duration{Duration: 12 * time.Hour},
			RegisterRetryCount:                10,
			PodEvictionTimeout:                unversioned.Duration{Duration: 5 * time.Minute},
			NodeMonitorGracePeriod:            unversioned.Duration{Duration: 40 * time.Second},
			NodeStartupGracePeriod:            unversioned.Duration{Duration: 60 * time.Second},
			NodeMonitorPeriod:                 unversioned.Duration{Duration: 5 * time.Second},
			ClusterName:                       "kubernetes",
			TerminatedPodGCThreshold:          12500,
			VolumeConfiguration: componentconfig.VolumeConfiguration{
				EnableHostPathProvisioning: false,
				PersistentVolumeRecyclerConfiguration: componentconfig.PersistentVolumeRecyclerConfiguration{
					MaximumRetry:             3,
					MinimumTimeoutNFS:        300,
					IncrementTimeoutNFS:      30,
					MinimumTimeoutHostPath:   60,
					IncrementTimeoutHostPath: 30,
				},
			},
			KubeAPIQPS:   20.0,
			KubeAPIBurst: 30,
			LeaderElection: componentconfig.LeaderElectionConfiguration{
				LeaderElect:   false,
				LeaseDuration: unversioned.Duration{Duration: 15 * time.Second},
				RenewDeadline: unversioned.Duration{Duration: 10 * time.Second},
				RetryPeriod:   unversioned.Duration{Duration: 2 * time.Second},
			},
		},
	}

	if !reflect.DeepEqual(defaults, expectedDefaults) {
		t.Logf("expected defaults, actual defaults: \n%s", util.ObjectGoPrintDiff(expectedDefaults, defaults))
		t.Errorf("Got different defaults than expected, adjust in BuildKubernetesMasterConfig and update expectedDefaults")
	}
}
func TestEventUpdate(t *testing.T) {
	eventA := &api.Event{
		ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: api.NamespaceDefault},
		Reason:     "forTesting",
	}
	eventB := &api.Event{
		ObjectMeta: api.ObjectMeta{Name: "bar", Namespace: api.NamespaceDefault},
		Reason:     "for testing again",
	}
	eventC := &api.Event{
		ObjectMeta: api.ObjectMeta{Name: "pan", Namespace: api.NamespaceDefault, ResourceVersion: "1"},
		Reason:     "for testing again something else",
	}

	nodeWithEventA := tools.EtcdResponseWithError{
		R: &etcd.Response{
			Node: &etcd.Node{
				Value:         runtime.EncodeOrDie(testapi.Codec(), eventA),
				ModifiedIndex: 1,
				CreatedIndex:  1,
				TTL:           int64(testTTL),
			},
		},
		E: nil,
	}

	nodeWithEventB := tools.EtcdResponseWithError{
		R: &etcd.Response{
			Node: &etcd.Node{
				Value:         runtime.EncodeOrDie(testapi.Codec(), eventB),
				ModifiedIndex: 1,
				CreatedIndex:  1,
				TTL:           int64(testTTL),
			},
		},
		E: nil,
	}

	nodeWithEventC := tools.EtcdResponseWithError{
		R: &etcd.Response{
			Node: &etcd.Node{
				Value:         runtime.EncodeOrDie(testapi.Codec(), eventC),
				ModifiedIndex: 1,
				CreatedIndex:  1,
				TTL:           int64(testTTL),
			},
		},
		E: nil,
	}

	emptyNode := tools.EtcdResponseWithError{
		R: &etcd.Response{},
		E: tools.EtcdErrorNotFound,
	}

	ctx := api.NewDefaultContext()
	key := "foo"
	path, err := etcdgeneric.NamespaceKeyFunc(ctx, "/events", key)
	path = etcdtest.AddPrefix(path)
	if err != nil {
		t.Errorf("Unexpected error: %v", err)
	}

	table := map[string]struct {
		existing tools.EtcdResponseWithError
		expect   tools.EtcdResponseWithError
		toUpdate runtime.Object
		errOK    func(error) bool
	}{
		"doesNotExist": {
			existing: emptyNode,
			expect:   nodeWithEventA,
			toUpdate: eventA,
			errOK:    func(err error) bool { return err == nil },
		},
		"doesNotExist2": {
			existing: emptyNode,
			expect:   nodeWithEventB,
			toUpdate: eventB,
			errOK:    func(err error) bool { return err == nil },
		},
		"replaceExisting": {
			existing: nodeWithEventA,
			expect:   nodeWithEventC,
			toUpdate: eventC,
			errOK:    func(err error) bool { return err == nil },
		},
	}

	for name, item := range table {
		fakeClient, registry := NewTestEventEtcdRegistry(t)
		fakeClient.Data[path] = item.existing
		err := registry.UpdateWithName(ctx, key, item.toUpdate)
		if !item.errOK(err) {
			t.Errorf("%v: unexpected error: %v", name, err)
		}

		if e, a := item.expect, fakeClient.Data[path]; !reflect.DeepEqual(e, a) {
			t.Errorf("%v:\n%s", name, util.ObjectGoPrintDiff(e, a))
		}
	}
}
// TestNestedParse tests that parsing the `go test` output in the test directory with a nested builder works as expected
func TestNestedParse(t *testing.T) {
	var testCases = []struct {
		name           string
		testFile       string
		rootSuiteNames []string
		expectedSuites *api.TestSuites
	}{
		{
			name:     "basic",
			testFile: "1.txt",
			expectedSuites: &api.TestSuites{
				Suites: []*api.TestSuite{
					{
						Name:     "package",
						NumTests: 2,
						Duration: 0.16,
						Children: []*api.TestSuite{
							{
								Name:     "package/name",
								NumTests: 2,
								Duration: 0.16,
								TestCases: []*api.TestCase{
									{
										Name:     "TestOne",
										Duration: 0.06,
									},
									{
										Name:     "TestTwo",
										Duration: 0.1,
									},
								},
							},
						},
					},
				},
			},
		},
		{
			name:           "basic with restricted root",
			testFile:       "1.txt",
			rootSuiteNames: []string{"package/name"},
			expectedSuites: &api.TestSuites{
				Suites: []*api.TestSuite{
					{
						Name:     "package/name",
						NumTests: 2,
						Duration: 0.16,
						TestCases: []*api.TestCase{
							{
								Name:     "TestOne",
								Duration: 0.06,
							},
							{
								Name:     "TestTwo",
								Duration: 0.1,
							},
						},
					},
				},
			},
		},
		{
			name:     "failure",
			testFile: "2.txt",
			expectedSuites: &api.TestSuites{
				Suites: []*api.TestSuite{
					{
						Name:      "package",
						NumTests:  2,
						NumFailed: 1,
						Duration:  0.15,
						Children: []*api.TestSuite{
							{
								Name:      "package/name",
								NumTests:  2,
								NumFailed: 1,
								Duration:  0.15,
								TestCases: []*api.TestCase{
									{
										Name:     "TestOne",
										Duration: 0.02,
										FailureOutput: &api.FailureOutput{
											Output: `=== RUN TestOne
--- FAIL: TestOne (0.02 seconds)
	file_test.go:11: Error message
	file_test.go:11: Longer
		error
		message.`,
										},
									},
									{
										Name:     "TestTwo",
										Duration: 0.13,
									},
								},
							},
						},
					},
				},
			},
		},
		{
			name:     "skip",
			testFile: "3.txt",
			expectedSuites: &api.TestSuites{
				Suites: []*api.TestSuite{
					{
						Name:       "package",
						NumTests:   2,
						NumSkipped: 1,
						Duration:   0.15,
						Children: []*api.TestSuite{
							{
								Name:       "package/name",
								NumTests:   2,
								NumSkipped: 1,
								Duration:   0.15,
								TestCases: []*api.TestCase{
									{
										Name:     "TestOne",
										Duration: 0.02,
										SkipMessage: &api.SkipMessage{
											Message: `=== RUN TestOne
--- SKIP: TestOne (0.02 seconds)
	file_test.go:11: Skip message`,
										},
									},
									{
										Name:     "TestTwo",
										Duration: 0.13,
									},
								},
							},
						},
					},
				},
			},
		},
		{
			name:     "go 1.4",
			testFile: "4.txt",
			expectedSuites: &api.TestSuites{
				Suites: []*api.TestSuite{
					{
						Name:     "package",
						NumTests: 2,
						Duration: 0.16,
						Children: []*api.TestSuite{
							{
								Name:     "package/name",
								NumTests: 2,
								Duration: 0.16,
								TestCases: []*api.TestCase{
									{
										Name:     "TestOne",
										Duration: 0.06,
									},
									{
										Name:     "TestTwo",
										Duration: 0.1,
									},
								},
							},
						},
					},
				},
			},
		},
		{
			name:     "multiple suites",
			testFile: "5.txt",
			expectedSuites: &api.TestSuites{
				Suites: []*api.TestSuite{
					{
						Name:      "package",
						NumTests:  4,
						NumFailed: 1,
						Duration:  0.31,
						Children: []*api.TestSuite{
							{
								Name:     "package/name1",
								NumTests: 2,
								Duration: 0.16,
								TestCases: []*api.TestCase{
									{
										Name:     "TestOne",
										Duration: 0.06,
									},
									{
										Name:     "TestTwo",
										Duration: 0.1,
									},
								},
							},
							{
								Name:      "package/name2",
								NumTests:  2,
								Duration:  0.15,
								NumFailed: 1,
								TestCases: []*api.TestCase{
									{
										Name:     "TestOne",
										Duration: 0.02,
										FailureOutput: &api.FailureOutput{
											Output: `=== RUN TestOne
--- FAIL: TestOne (0.02 seconds)
	file_test.go:11: Error message
	file_test.go:11: Longer
		error
		message.`,
										},
									},
									{
										Name:     "TestTwo",
										Duration: 0.13,
									},
								},
							},
						},
					},
				},
			},
		},
		{
			name:     "coverage statement",
			testFile: "6.txt",
			expectedSuites: &api.TestSuites{
				Suites: []*api.TestSuite{
					{
						Name:     "package",
						NumTests: 2,
						Duration: 0.16,
						Children: []*api.TestSuite{
							{
								Name:     "package/name",
								NumTests: 2,
								Duration: 0.16,
								Properties: []*api.TestSuiteProperty{
									{
										Name:  "coverage.statements.pct",
										Value: "13.37",
									},
								},
								TestCases: []*api.TestCase{
									{
										Name:     "TestOne",
										Duration: 0.06,
									},
									{
										Name:     "TestTwo",
										Duration: 0.1,
									},
								},
							},
						},
					},
				},
			},
		},
		{
			name:     "coverage statement in package result",
			testFile: "7.txt",
			expectedSuites: &api.TestSuites{
				Suites: []*api.TestSuite{
					{
						Name:     "package",
						NumTests: 2,
						Duration: 0.16,
						Children: []*api.TestSuite{
							{
								Name:     "package/name",
								NumTests: 2,
								Duration: 0.16,
								Properties: []*api.TestSuiteProperty{
									{
										Name:  "coverage.statements.pct",
										Value: "10.0",
									},
								},
								TestCases: []*api.TestCase{
									{
										Name:     "TestOne",
										Duration: 0.06,
									},
									{
										Name:     "TestTwo",
										Duration: 0.1,
									},
								},
							},
						},
					},
				},
			},
		},
		{
			name:     "go 1.5",
			testFile: "8.txt",
			expectedSuites: &api.TestSuites{
				Suites: []*api.TestSuite{
					{
						Name:     "package",
						NumTests: 2,
						Duration: 0.05,
						Children: []*api.TestSuite{
							{
								Name:     "package/name",
								NumTests: 2,
								Duration: 0.05,
								TestCases: []*api.TestCase{
									{
										Name:     "TestOne",
										Duration: 0.02,
									},
									{
										Name:     "TestTwo",
										Duration: 0.03,
									},
								},
							},
						},
					},
				},
			},
		},
		{
			name:     "nested ",
			testFile: "9.txt",
			expectedSuites: &api.TestSuites{
				Suites: []*api.TestSuite{
					{
						Name:       "package",
						NumTests:   6,
						NumFailed:  1,
						NumSkipped: 1,
						Duration:   0.4,
						Children: []*api.TestSuite{
							{
								Name:       "package/name",
								NumTests:   4,
								NumFailed:  1,
								NumSkipped: 1,
								Duration:   0.1,
								TestCases: []*api.TestCase{
									{
										Name:     "TestOne",
										Duration: 0.02,
									},
									{
										Name:     "TestTwo",
										Duration: 0.03,
									},
								},
								Children: []*api.TestSuite{

									{
										Name:       "package/name/nested",
										NumTests:   2,
										NumFailed:  1,
										NumSkipped: 1,
										Duration:   0.05,
										TestCases: []*api.TestCase{
											{
												Name:     "TestOne",
												Duration: 0.02,
												FailureOutput: &api.FailureOutput{
													Output: `=== RUN   TestOne
--- FAIL: TestOne (0.02 seconds)
	file_test.go:11: Error message
	file_test.go:11: Longer
		error
		message.`,
												},
											},
											{
												Name:     "TestTwo",
												Duration: 0.03,
												SkipMessage: &api.SkipMessage{
													Message: `=== RUN   TestTwo
--- SKIP: TestTwo (0.03 seconds)
	file_test.go:11: Skip message
PASS`, // we include this line greedily even though it does not belong to the test
												},
											},
										},
									},
								},
							},
							{
								Name:     "package/other",
								NumTests: 2,
								Duration: 0.3,
								Children: []*api.TestSuite{

									{
										Name:     "package/other/nested",
										NumTests: 2,
										Duration: 0.3,
										TestCases: []*api.TestCase{
											{
												Name:     "TestOne",
												Duration: 0.1,
											},
											{
												Name:     "TestTwo",
												Duration: 0.2,
											},
										},
									},
								},
							},
						},
					},
				},
			},
		},
		{
			name:     "test case timing doesn't add to test suite timing",
			testFile: "10.txt",
			expectedSuites: &api.TestSuites{
				Suites: []*api.TestSuite{
					{
						Name:     "package",
						NumTests: 2,
						Duration: 2.16,
						Children: []*api.TestSuite{
							{
								Name:     "package/name",
								NumTests: 2,
								Duration: 2.16,
								TestCases: []*api.TestCase{
									{
										Name:     "TestOne",
										Duration: 0.06,
									},
									{
										Name:     "TestTwo",
										Duration: 0.1,
									},
								},
							},
						},
					},
				},
			},
		},
	}

	for _, testCase := range testCases {
		parser := NewParser(nested.NewTestSuitesBuilder(testCase.rootSuiteNames))

		testFile := "./../../../test/gotest/testdata/" + testCase.testFile

		reader, err := os.Open(testFile)
		if err != nil {
			t.Errorf("%s: unexpected error opening file %q: %v", testCase.name, testFile, err)
			continue
		}
		testSuites, err := parser.Parse(bufio.NewScanner(reader))
		if err != nil {
			t.Errorf("%s: unexpected error parsing file: %v", testCase.name, err)
			continue
		}

		if !reflect.DeepEqual(testSuites, testCase.expectedSuites) {
			fmt.Println(util.ObjectGoPrintDiff(testSuites, testCase.expectedSuites))
			t.Errorf("%s: did not produce the correct test suites from file:\n\texpected:\n\t%v,\n\tgot\n\t%v", testCase.name, testCase.expectedSuites, testSuites)
		}
	}
}
func TestAnonymousConfig(t *testing.T) {
	f := fuzz.New().NilChance(0.0).NumElements(1, 1)
	f.Funcs(
		func(r *runtime.Codec, f fuzz.Continue) {},
		func(r *http.RoundTripper, f fuzz.Continue) {},
		func(fn *func(http.RoundTripper) http.RoundTripper, f fuzz.Continue) {},
	)
	for i := 0; i < 20; i++ {
		original := &restclient.Config{}
		f.Fuzz(original)
		actual := AnonymousClientConfig(original)
		expected := *original

		// this is the list of known security related fields, add to this list if a new field
		// is added to restclient.Config, update AnonymousClientConfig to preserve the field otherwise.
		expected.BearerToken = ""
		expected.Username = ""
		expected.Password = ""
		expected.TLSClientConfig.CertData = nil
		expected.TLSClientConfig.CertFile = ""
		expected.TLSClientConfig.KeyData = nil
		expected.TLSClientConfig.KeyFile = ""

		if !reflect.DeepEqual(actual, expected) {
			t.Fatalf("AnonymousClientConfig dropped unexpected fields, identify whether they are security related or not: %s", util.ObjectGoPrintDiff(expected, actual))
		}
	}
}
func roundTrip(t *testing.T, codec runtime.Codec, item runtime.Object) {
	printer := spew.ConfigState{DisableMethods: true}

	gvk, err := api.Scheme.ObjectKind(item)
	t.Logf("fully qualified kind for %v is %v with codec %v", reflect.TypeOf(item), gvk, codec)

	name := reflect.TypeOf(item).Elem().Name()
	data, err := runtime.Encode(codec, item)
	if err != nil {
		t.Errorf("%v: %v (%s)", name, err, printer.Sprintf("%#v", item))
		return
	}

	obj2, err := runtime.Decode(codec, data)
	if err != nil {
		t.Errorf("0: %v: %v\nCodec: %v\nData: %s\nSource: %#v", name, err, codec, string(data), printer.Sprintf("%#v", item))
		return
	}
	if !api.Semantic.DeepEqual(item, obj2) {
		t.Errorf("\n1: %v: diff: %v\nCodec: %v\nSource:\n\n%#v\n\nEncoded:\n\n%s\n\nFinal:\n\n%#v", name, util.ObjectGoPrintDiff(item, obj2), codec, printer.Sprintf("%#v", item), string(data), printer.Sprintf("%#v", obj2))
		return
	}

	obj3 := reflect.New(reflect.TypeOf(item).Elem()).Interface().(runtime.Object)
	err = runtime.DecodeInto(codec, data, obj3)
	if err != nil {
		t.Errorf("2: %v: %v", name, err)
		return
	}
	if !api.Semantic.DeepEqual(item, obj3) {
		t.Errorf("3: %v: diff: %v\nCodec: %v", name, util.ObjectDiff(item, obj3), codec)
		return
	}
}
func TestKubeletDefaults(t *testing.T) {
	defaults := kubeletoptions.NewKubeletServer()

	// This is a snapshot of the default config
	// If the default changes (new fields are added, or default values change), we want to know
	// Once we've reacted to the changes appropriately in BuildKubernetesNodeConfig(), update this expected default to match the new upstream defaults
	expectedDefaults := &kubeletoptions.KubeletServer{
		AuthPath:   util.NewStringFlag("/var/lib/kubelet/kubernetes_auth"),
		KubeConfig: util.NewStringFlag("/var/lib/kubelet/kubeconfig"),

		SystemReserved: util.ConfigurationMap{},
		KubeReserved:   util.ConfigurationMap{},
		KubeletConfiguration: componentconfig.KubeletConfiguration{
			Address:                     "0.0.0.0", // overridden
			AllowPrivileged:             false,     // overridden
			CAdvisorPort:                4194,      // disabled
			VolumeStatsAggPeriod:        unversioned.Duration{Duration: time.Minute},
			CertDirectory:               "/var/run/kubernetes",
			CgroupRoot:                  "",
			ClusterDNS:                  "", // overridden
			ClusterDomain:               "", // overridden
			ConfigureCBR0:               false,
			ContainerRuntime:            "docker",
			Containerized:               false, // overridden based on OPENSHIFT_CONTAINERIZED
			CPUCFSQuota:                 true,  // forced to true
			DockerExecHandlerName:       "native",
			EventBurst:                  10,
			EventRecordQPS:              5.0,
			EnableCustomMetrics:         false,
			EnableDebuggingHandlers:     true,
			EnableServer:                true,
			FileCheckFrequency:          unversioned.Duration{Duration: 20 * time.Second}, // overridden
			HealthzBindAddress:          "127.0.0.1",                                      // disabled
			HealthzPort:                 10248,                                            // disabled
			HostNetworkSources:          "*",                                              // overridden
			HostPIDSources:              "*",                                              // overridden
			HostIPCSources:              "*",                                              // overridden
			HTTPCheckFrequency:          unversioned.Duration{Duration: 20 * time.Second}, // disabled
			ImageMinimumGCAge:           unversioned.Duration{Duration: 2 * time.Minute},
			ImageGCHighThresholdPercent: 90,
			ImageGCLowThresholdPercent:  80,
			LowDiskSpaceThresholdMB:     256,
			MasterServiceNamespace:      "default",
			MaxContainerCount:           240,
			MaxPerPodContainerCount:     2,
			MaxOpenFiles:                1000000,
			MaxPods:                     110, // overridden
			MinimumGCAge:                unversioned.Duration{Duration: 1 * time.Minute},
			NetworkPluginDir:            "/usr/libexec/kubernetes/kubelet-plugins/net/exec/",
			NetworkPluginName:           "", // overridden
			NonMasqueradeCIDR:           "10.0.0.0/8",
			VolumePluginDir:             "/usr/libexec/kubernetes/kubelet-plugins/volume/exec/",
			NodeStatusUpdateFrequency:   unversioned.Duration{Duration: 10 * time.Second},
			NodeLabels:                  map[string]string{},
			OOMScoreAdj:                 -999,
			LockFilePath:                "",
			PodInfraContainerImage:      kubetypes.PodInfraContainerImage, // overridden
			Port:                           10250, // overridden
			ReadOnlyPort:                   10255, // disabled
			RegisterNode:                   true,
			RegisterSchedulable:            true,
			RegistryBurst:                  10,
			RegistryPullQPS:                5.0,
			KubeletCgroups:                 "",
			RktPath:                        "",
			RktStage1Image:                 "",
			RootDirectory:                  "/var/lib/kubelet", // overridden
			RuntimeCgroups:                 "",
			SerializeImagePulls:            true,
			StreamingConnectionIdleTimeout: unversioned.Duration{Duration: 4 * time.Hour},
			SyncFrequency:                  unversioned.Duration{Duration: 1 * time.Minute},
			SystemCgroups:                  "",
			TLSCertFile:                    "", // overridden to prevent cert generation
			TLSPrivateKeyFile:              "", // overridden to prevent cert generation
			ReconcileCIDR:                  true,
			KubeAPIQPS:                     5.0,
			KubeAPIBurst:                   10,
			ExperimentalFlannelOverlay:     false,
			OutOfDiskTransitionFrequency:   unversioned.Duration{Duration: 5 * time.Minute},
			HairpinMode:                    "promiscuous-bridge",
			BabysitDaemons:                 false,
		},
	}

	if !reflect.DeepEqual(defaults, expectedDefaults) {
		t.Logf("expected defaults, actual defaults: \n%s", util.ObjectGoPrintDiff(expectedDefaults, defaults))
		t.Errorf("Got different defaults than expected, adjust in BuildKubernetesNodeConfig and update expectedDefaults")
	}
}