func TestEmbedNoCADisallowed(t *testing.T) { expectedConfig := newRedFederalCowHammerConfig() test := configCommandTest{ args: []string{"set-cluster", "another-cluster", "--" + clientcmd.FlagEmbedCerts + "=true"}, startingConfig: newRedFederalCowHammerConfig(), expectedConfig: expectedConfig, } func() { defer func() { // Restore cmdutil behavior. cmdutil.DefaultBehaviorOnFatal() }() // Check exit code. cmdutil.BehaviorOnFatal(func(e string, code int) { if code != 1 { t.Errorf("The exit code is %d, expected 1", code) } expectedOutputs := []string{"--certificate-authority", "embed"} test.checkOutput(e, expectedOutputs, t) }) test.run(t) }() }
func TestCAAndInsecureDisallowed(t *testing.T) { test := configCommandTest{ args: []string{"set-cluster", "another-cluster", "--" + clientcmd.FlagCAFile + "=cafile", "--" + clientcmd.FlagInsecure + "=true"}, startingConfig: newRedFederalCowHammerConfig(), expectedConfig: newRedFederalCowHammerConfig(), } func() { defer func() { // Restore cmdutil behavior. cmdutil.DefaultBehaviorOnFatal() }() // Check exit code. cmdutil.BehaviorOnFatal(func(e string, code int) { if code != 1 { t.Errorf("The exit code is %d, expected 1", code) } expectedOutputs := []string{"certificate", "insecure"} test.checkOutput(e, expectedOutputs, t) }) test.run(t) }() }
func TestSetBytesBad(t *testing.T) { startingConfig := newRedFederalCowHammerConfig() startingConfig.Clusters["another-cluster"] = clientcmdapi.NewCluster() test := configCommandTest{ args: []string{"set", "clusters.another-cluster.certificate-authority-data", "cadata"}, startingConfig: startingConfig, expectedConfig: startingConfig, } func() { defer func() { // Restore cmdutil behavior. cmdutil.DefaultBehaviorOnFatal() }() // Check exit code. cmdutil.BehaviorOnFatal(func(e string, code int) { if code != 1 { t.Errorf("The exit code is %d, expected 1", code) } }) test.run(t) }() }
func TestTokenAndBasicDisallowed(t *testing.T) { expectedConfig := newRedFederalCowHammerConfig() test := configCommandTest{ args: []string{"set-credentials", "another-user", "--" + clientcmd.FlagUsername + "=myuser", "--" + clientcmd.FlagBearerToken + "=token"}, startingConfig: newRedFederalCowHammerConfig(), expectedConfig: expectedConfig, } func() { defer func() { // Restore cmdutil behavior. cmdutil.DefaultBehaviorOnFatal() }() // Check exit code. cmdutil.BehaviorOnFatal(func(e string, code int) { if code != 1 { t.Errorf("The exit code is %d, expected 1", code) } expectedOutputs := []string{"--token", "--username"} test.checkOutput(e, expectedOutputs, t) }) test.run(t) }() }
func TestSetNonExistentContext(t *testing.T) { expectedConfig := newRedFederalCowHammerConfig() test := configCommandTest{ args: []string{"use-context", "non-existent-config"}, startingConfig: expectedConfig, expectedConfig: expectedConfig, } func() { defer func() { // Restore cmdutil behavior. cmdutil.DefaultBehaviorOnFatal() }() // Check exit code. cmdutil.BehaviorOnFatal(func(e string, code int) { if code != 1 { t.Errorf("The exit code is %d, expected 1", code) } expectedOutputs := []string{`no context exists with the name: "non-existent-config"`} test.checkOutput(e, expectedOutputs, t) }) test.run(t) }() }
func TestCordon(t *testing.T) { tests := []struct { description string node *api.Node expected *api.Node cmd func(*cmdutil.Factory, io.Writer) *cobra.Command arg string expectFatal bool }{ { description: "node/node syntax", node: cordoned_node, expected: node, cmd: NewCmdUncordon, arg: "node/node", expectFatal: false, }, { description: "uncordon for real", node: cordoned_node, expected: node, cmd: NewCmdUncordon, arg: "node", expectFatal: false, }, { description: "uncordon does nothing", node: node, expected: node, cmd: NewCmdUncordon, arg: "node", expectFatal: false, }, { description: "cordon does nothing", node: cordoned_node, expected: cordoned_node, cmd: NewCmdCordon, arg: "node", expectFatal: false, }, { description: "cordon for real", node: node, expected: cordoned_node, cmd: NewCmdCordon, arg: "node", expectFatal: false, }, { description: "cordon missing node", node: node, expected: node, cmd: NewCmdCordon, arg: "bar", expectFatal: true, }, { description: "uncordon missing node", node: node, expected: node, cmd: NewCmdUncordon, arg: "bar", expectFatal: true, }, } for _, test := range tests { f, tf, codec := NewAPIFactory() new_node := &api.Node{} updated := false tf.Client = &fake.RESTClient{ Codec: codec, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { m := &MyReq{req} switch { case m.isFor("GET", "/nodes/node"): return &http.Response{StatusCode: 200, Body: objBody(codec, test.node)}, nil case m.isFor("GET", "/nodes/bar"): return &http.Response{StatusCode: 404, Body: stringBody("nope")}, nil case m.isFor("PUT", "/nodes/node"): data, err := ioutil.ReadAll(req.Body) if err != nil { t.Fatalf("%s: unexpected error: %v", test.description, err) } defer req.Body.Close() if err := runtime.DecodeInto(codec, data, new_node); err != nil { t.Fatalf("%s: unexpected error: %v", test.description, err) } if !reflect.DeepEqual(test.expected.Spec, new_node.Spec) { t.Fatalf("%s: expected:\n%v\nsaw:\n%v\n", test.description, test.expected.Spec, new_node.Spec) } updated = true return &http.Response{StatusCode: 200, Body: objBody(codec, new_node)}, nil default: t.Fatalf("%s: unexpected request: %v %#v\n%#v", test.description, req.Method, req.URL, req) return nil, nil } }), } tf.ClientConfig = &client.Config{ContentConfig: client.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}} buf := bytes.NewBuffer([]byte{}) cmd := test.cmd(f, buf) saw_fatal := false func() { defer func() { // Recover from the panic below. _ = recover() // Restore cmdutil behavior cmdutil.DefaultBehaviorOnFatal() }() cmdutil.BehaviorOnFatal(func(e string) { saw_fatal = true; panic(e) }) cmd.SetArgs([]string{test.arg}) cmd.Execute() }() if test.expectFatal { if !saw_fatal { t.Fatalf("%s: unexpected non-error", test.description) } if updated { t.Fatalf("%s: unexpcted update", test.description) } } if !test.expectFatal && saw_fatal { t.Fatalf("%s: unexpected error", test.description) } if !reflect.DeepEqual(test.expected.Spec, test.node.Spec) && !updated { t.Fatalf("%s: node never updated", test.description) } } }
func TestDrain(t *testing.T) { labels := make(map[string]string) labels["my_key"] = "my_value" rc := api.ReplicationController{ ObjectMeta: api.ObjectMeta{ Name: "rc", Namespace: "default", CreationTimestamp: unversioned.Time{time.Now()}, Labels: labels, SelfLink: testapi.Default.SelfLink("replicationcontrollers", "rc"), }, Spec: api.ReplicationControllerSpec{ Selector: labels, }, } rc_anno := make(map[string]string) rc_anno[controller.CreatedByAnnotation] = refJson(t, &rc) replicated_pod := api.Pod{ ObjectMeta: api.ObjectMeta{ Name: "bar", Namespace: "default", CreationTimestamp: unversioned.Time{time.Now()}, Labels: labels, Annotations: rc_anno, }, Spec: api.PodSpec{ NodeName: "node", }, } ds := extensions.DaemonSet{ ObjectMeta: api.ObjectMeta{ Name: "ds", Namespace: "default", CreationTimestamp: unversioned.Time{time.Now()}, SelfLink: "/apis/extensions/v1beta1/namespaces/default/daemonsets/ds", }, Spec: extensions.DaemonSetSpec{ Selector: &extensions.LabelSelector{MatchLabels: labels}, }, } ds_anno := make(map[string]string) ds_anno[controller.CreatedByAnnotation] = refJson(t, &ds) ds_pod := api.Pod{ ObjectMeta: api.ObjectMeta{ Name: "bar", Namespace: "default", CreationTimestamp: unversioned.Time{time.Now()}, Labels: labels, Annotations: ds_anno, }, Spec: api.PodSpec{ NodeName: "node", }, } job := extensions.Job{ ObjectMeta: api.ObjectMeta{ Name: "job", Namespace: "default", CreationTimestamp: unversioned.Time{time.Now()}, SelfLink: "/apis/extensions/v1beta1/namespaces/default/jobs/job", }, Spec: extensions.JobSpec{ Selector: &extensions.LabelSelector{MatchLabels: labels}, }, } job_pod := api.Pod{ ObjectMeta: api.ObjectMeta{ Name: "bar", Namespace: "default", CreationTimestamp: unversioned.Time{time.Now()}, Labels: labels, Annotations: map[string]string{controller.CreatedByAnnotation: refJson(t, &job)}, }, } naked_pod := api.Pod{ ObjectMeta: api.ObjectMeta{ Name: "bar", Namespace: "default", CreationTimestamp: unversioned.Time{time.Now()}, Labels: labels, }, Spec: api.PodSpec{ NodeName: "node", }, } tests := []struct { description string node *api.Node expected *api.Node pods []api.Pod rcs []api.ReplicationController args []string expectFatal bool expectDelete bool }{ { description: "RC-managed pod", node: node, expected: cordoned_node, pods: []api.Pod{replicated_pod}, rcs: []api.ReplicationController{rc}, args: []string{"node"}, expectFatal: false, expectDelete: true, }, { description: "DS-managed pod", node: node, expected: cordoned_node, pods: []api.Pod{ds_pod}, rcs: []api.ReplicationController{rc}, args: []string{"node"}, expectFatal: false, expectDelete: true, }, { description: "Job-managed pod", node: node, expected: cordoned_node, pods: []api.Pod{job_pod}, rcs: []api.ReplicationController{rc}, args: []string{"node"}, expectFatal: false, expectDelete: true, }, { description: "naked pod", node: node, expected: cordoned_node, pods: []api.Pod{naked_pod}, rcs: []api.ReplicationController{}, args: []string{"node"}, expectFatal: true, expectDelete: false, }, { description: "naked pod with --force", node: node, expected: cordoned_node, pods: []api.Pod{naked_pod}, rcs: []api.ReplicationController{}, args: []string{"node", "--force"}, expectFatal: false, expectDelete: true, }, { description: "empty node", node: node, expected: cordoned_node, pods: []api.Pod{}, rcs: []api.ReplicationController{rc}, args: []string{"node"}, expectFatal: false, expectDelete: false, }, } for _, test := range tests { new_node := &api.Node{} deleted := false f, tf, codec := NewAPIFactory() tf.Client = &fake.RESTClient{ Codec: codec, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { m := &MyReq{req} switch { case m.isFor("GET", "/nodes/node"): return &http.Response{StatusCode: 200, Body: objBody(codec, test.node)}, nil case m.isFor("GET", "/namespaces/default/replicationcontrollers/rc"): return &http.Response{StatusCode: 200, Body: objBody(codec, &test.rcs[0])}, nil case m.isFor("GET", "/namespaces/default/daemonsets/ds"): return &http.Response{StatusCode: 200, Body: objBody(testapi.Extensions.Codec(), &ds)}, nil case m.isFor("GET", "/namespaces/default/jobs/job"): return &http.Response{StatusCode: 200, Body: objBody(testapi.Extensions.Codec(), &job)}, nil case m.isFor("GET", "/pods"): values, err := url.ParseQuery(req.URL.RawQuery) if err != nil { t.Fatalf("%s: unexpected error: %v", test.description, err) } get_params := make(url.Values) get_params["fieldSelector"] = []string{"spec.nodeName=node"} if !reflect.DeepEqual(get_params, values) { t.Fatalf("%s: expected:\n%v\nsaw:\n%v\n", test.description, get_params, values) } return &http.Response{StatusCode: 200, Body: objBody(codec, &api.PodList{Items: test.pods})}, nil case m.isFor("GET", "/replicationcontrollers"): return &http.Response{StatusCode: 200, Body: objBody(codec, &api.ReplicationControllerList{Items: test.rcs})}, nil case m.isFor("PUT", "/nodes/node"): data, err := ioutil.ReadAll(req.Body) if err != nil { t.Fatalf("%s: unexpected error: %v", test.description, err) } defer req.Body.Close() if err := runtime.DecodeInto(codec, data, new_node); err != nil { t.Fatalf("%s: unexpected error: %v", test.description, err) } if !reflect.DeepEqual(test.expected.Spec, new_node.Spec) { t.Fatalf("%s: expected:\n%v\nsaw:\n%v\n", test.description, test.expected.Spec, new_node.Spec) } return &http.Response{StatusCode: 200, Body: objBody(codec, new_node)}, nil case m.isFor("DELETE", "/namespaces/default/pods/bar"): deleted = true return &http.Response{StatusCode: 204, Body: objBody(codec, &test.pods[0])}, nil default: t.Fatalf("%s: unexpected request: %v %#v\n%#v", test.description, req.Method, req.URL, req) return nil, nil } }), } tf.ClientConfig = &client.Config{ContentConfig: client.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}} buf := bytes.NewBuffer([]byte{}) cmd := NewCmdDrain(f, buf) saw_fatal := false func() { defer func() { // Recover from the panic below. _ = recover() // Restore cmdutil behavior cmdutil.DefaultBehaviorOnFatal() }() cmdutil.BehaviorOnFatal(func(e string) { saw_fatal = true; panic(e) }) cmd.SetArgs(test.args) cmd.Execute() }() if test.expectFatal { if !saw_fatal { t.Fatalf("%s: unexpected non-error", test.description) } } if test.expectDelete { if !deleted { t.Fatalf("%s: pod never deleted", test.description) } } if !test.expectDelete { if deleted { t.Fatalf("%s: unexpected delete", test.description) } } } }
func TestDrain(t *testing.T) { labels := make(map[string]string) labels["my_key"] = "my_value" rc := api.ReplicationController{ ObjectMeta: api.ObjectMeta{ Name: "rc", Namespace: "default", CreationTimestamp: unversioned.Time{Time: time.Now()}, Labels: labels, SelfLink: testapi.Default.SelfLink("replicationcontrollers", "rc"), }, Spec: api.ReplicationControllerSpec{ Selector: labels, }, } rc_anno := make(map[string]string) rc_anno[api.CreatedByAnnotation] = refJson(t, &rc) rc_pod := api.Pod{ ObjectMeta: api.ObjectMeta{ Name: "bar", Namespace: "default", CreationTimestamp: unversioned.Time{Time: time.Now()}, Labels: labels, Annotations: rc_anno, }, Spec: api.PodSpec{ NodeName: "node", }, } ds := extensions.DaemonSet{ ObjectMeta: api.ObjectMeta{ Name: "ds", Namespace: "default", CreationTimestamp: unversioned.Time{Time: time.Now()}, SelfLink: "/apis/extensions/v1beta1/namespaces/default/daemonsets/ds", }, Spec: extensions.DaemonSetSpec{ Selector: &unversioned.LabelSelector{MatchLabels: labels}, }, } ds_anno := make(map[string]string) ds_anno[api.CreatedByAnnotation] = refJson(t, &ds) ds_pod := api.Pod{ ObjectMeta: api.ObjectMeta{ Name: "bar", Namespace: "default", CreationTimestamp: unversioned.Time{Time: time.Now()}, Labels: labels, Annotations: ds_anno, }, Spec: api.PodSpec{ NodeName: "node", }, } job := batch.Job{ ObjectMeta: api.ObjectMeta{ Name: "job", Namespace: "default", CreationTimestamp: unversioned.Time{Time: time.Now()}, SelfLink: "/apis/extensions/v1beta1/namespaces/default/jobs/job", }, Spec: batch.JobSpec{ Selector: &unversioned.LabelSelector{MatchLabels: labels}, }, } job_pod := api.Pod{ ObjectMeta: api.ObjectMeta{ Name: "bar", Namespace: "default", CreationTimestamp: unversioned.Time{Time: time.Now()}, Labels: labels, Annotations: map[string]string{api.CreatedByAnnotation: refJson(t, &job)}, }, } rs := extensions.ReplicaSet{ ObjectMeta: api.ObjectMeta{ Name: "rs", Namespace: "default", CreationTimestamp: unversioned.Time{Time: time.Now()}, Labels: labels, SelfLink: testapi.Default.SelfLink("replicasets", "rs"), }, Spec: extensions.ReplicaSetSpec{ Selector: &unversioned.LabelSelector{MatchLabels: labels}, }, } rs_anno := make(map[string]string) rs_anno[api.CreatedByAnnotation] = refJson(t, &rs) rs_pod := api.Pod{ ObjectMeta: api.ObjectMeta{ Name: "bar", Namespace: "default", CreationTimestamp: unversioned.Time{Time: time.Now()}, Labels: labels, Annotations: rs_anno, }, Spec: api.PodSpec{ NodeName: "node", }, } naked_pod := api.Pod{ ObjectMeta: api.ObjectMeta{ Name: "bar", Namespace: "default", CreationTimestamp: unversioned.Time{Time: time.Now()}, Labels: labels, }, Spec: api.PodSpec{ NodeName: "node", }, } emptydir_pod := api.Pod{ ObjectMeta: api.ObjectMeta{ Name: "bar", Namespace: "default", CreationTimestamp: unversioned.Time{Time: time.Now()}, Labels: labels, }, Spec: api.PodSpec{ NodeName: "node", Volumes: []api.Volume{ { Name: "scratch", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{Medium: ""}}, }, }, }, } tests := []struct { description string node *api.Node expected *api.Node pods []api.Pod rcs []api.ReplicationController replicaSets []extensions.ReplicaSet args []string expectFatal bool expectDelete bool }{ { description: "RC-managed pod", node: node, expected: cordoned_node, pods: []api.Pod{rc_pod}, rcs: []api.ReplicationController{rc}, args: []string{"node"}, expectFatal: false, expectDelete: true, }, { description: "DS-managed pod", node: node, expected: cordoned_node, pods: []api.Pod{ds_pod}, rcs: []api.ReplicationController{rc}, args: []string{"node"}, expectFatal: true, expectDelete: false, }, { description: "DS-managed pod with --ignore-daemonsets", node: node, expected: cordoned_node, pods: []api.Pod{ds_pod}, rcs: []api.ReplicationController{rc}, args: []string{"node", "--ignore-daemonsets"}, expectFatal: false, expectDelete: false, }, { description: "Job-managed pod", node: node, expected: cordoned_node, pods: []api.Pod{job_pod}, rcs: []api.ReplicationController{rc}, args: []string{"node"}, expectFatal: false, expectDelete: true, }, { description: "RS-managed pod", node: node, expected: cordoned_node, pods: []api.Pod{rs_pod}, replicaSets: []extensions.ReplicaSet{rs}, args: []string{"node"}, expectFatal: false, expectDelete: true, }, { description: "naked pod", node: node, expected: cordoned_node, pods: []api.Pod{naked_pod}, rcs: []api.ReplicationController{}, args: []string{"node"}, expectFatal: true, expectDelete: false, }, { description: "naked pod with --force", node: node, expected: cordoned_node, pods: []api.Pod{naked_pod}, rcs: []api.ReplicationController{}, args: []string{"node", "--force"}, expectFatal: false, expectDelete: true, }, { description: "pod with EmptyDir", node: node, expected: cordoned_node, pods: []api.Pod{emptydir_pod}, args: []string{"node", "--force"}, expectFatal: true, expectDelete: false, }, { description: "pod with EmptyDir and --delete-local-data", node: node, expected: cordoned_node, pods: []api.Pod{emptydir_pod}, args: []string{"node", "--force", "--delete-local-data=true"}, expectFatal: false, expectDelete: true, }, { description: "empty node", node: node, expected: cordoned_node, pods: []api.Pod{}, rcs: []api.ReplicationController{rc}, args: []string{"node"}, expectFatal: false, expectDelete: false, }, } testEviction := false for i := 0; i < 2; i++ { testEviction = !testEviction var currMethod string if testEviction { currMethod = EvictionMethod } else { currMethod = DeleteMethod } for _, test := range tests { new_node := &api.Node{} deleted := false evicted := false f, tf, codec, ns := cmdtesting.NewAPIFactory() tf.Client = &fake.RESTClient{ NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { m := &MyReq{req} switch { case req.Method == "GET" && req.URL.Path == "/api": apiVersions := unversioned.APIVersions{ Versions: []string{"v1"}, } return genResponseWithJsonEncodedBody(apiVersions) case req.Method == "GET" && req.URL.Path == "/apis": groupList := unversioned.APIGroupList{ Groups: []unversioned.APIGroup{ { Name: "policy", PreferredVersion: unversioned.GroupVersionForDiscovery{ GroupVersion: "policy/v1beta1", }, }, }, } return genResponseWithJsonEncodedBody(groupList) case req.Method == "GET" && req.URL.Path == "/api/v1": resourceList := unversioned.APIResourceList{ GroupVersion: "v1", } if testEviction { resourceList.APIResources = []unversioned.APIResource{ { Name: EvictionSubresource, Kind: EvictionKind, }, } } return genResponseWithJsonEncodedBody(resourceList) case m.isFor("GET", "/nodes/node"): return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, test.node)}, nil case m.isFor("GET", "/namespaces/default/replicationcontrollers/rc"): return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &test.rcs[0])}, nil case m.isFor("GET", "/namespaces/default/daemonsets/ds"): return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(testapi.Extensions.Codec(), &ds)}, nil case m.isFor("GET", "/namespaces/default/jobs/job"): return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(testapi.Extensions.Codec(), &job)}, nil case m.isFor("GET", "/namespaces/default/replicasets/rs"): return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(testapi.Extensions.Codec(), &test.replicaSets[0])}, nil case m.isFor("GET", "/namespaces/default/pods/bar"): return &http.Response{StatusCode: 404, Header: defaultHeader(), Body: objBody(codec, &api.Pod{})}, nil case m.isFor("GET", "/pods"): values, err := url.ParseQuery(req.URL.RawQuery) if err != nil { t.Fatalf("%s: unexpected error: %v", test.description, err) } get_params := make(url.Values) get_params["fieldSelector"] = []string{"spec.nodeName=node"} if !reflect.DeepEqual(get_params, values) { t.Fatalf("%s: expected:\n%v\nsaw:\n%v\n", test.description, get_params, values) } return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &api.PodList{Items: test.pods})}, nil case m.isFor("GET", "/replicationcontrollers"): return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &api.ReplicationControllerList{Items: test.rcs})}, nil case m.isFor("PUT", "/nodes/node"): data, err := ioutil.ReadAll(req.Body) if err != nil { t.Fatalf("%s: unexpected error: %v", test.description, err) } defer req.Body.Close() if err := runtime.DecodeInto(codec, data, new_node); err != nil { t.Fatalf("%s: unexpected error: %v", test.description, err) } if !reflect.DeepEqual(test.expected.Spec, new_node.Spec) { t.Fatalf("%s: expected:\n%v\nsaw:\n%v\n", test.description, test.expected.Spec, new_node.Spec) } return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, new_node)}, nil case m.isFor("DELETE", "/namespaces/default/pods/bar"): deleted = true return &http.Response{StatusCode: 204, Header: defaultHeader(), Body: objBody(codec, &test.pods[0])}, nil case m.isFor("POST", "/namespaces/default/pods/bar/eviction"): evicted = true return &http.Response{StatusCode: 201, Header: defaultHeader(), Body: policyObjBody(&policy.Eviction{})}, nil default: t.Fatalf("%s: unexpected request: %v %#v\n%#v", test.description, req.Method, req.URL, req) return nil, nil } }), } tf.ClientConfig = defaultClientConfig() buf := bytes.NewBuffer([]byte{}) errBuf := bytes.NewBuffer([]byte{}) cmd := NewCmdDrain(f, buf, errBuf) saw_fatal := false func() { defer func() { // Recover from the panic below. _ = recover() // Restore cmdutil behavior cmdutil.DefaultBehaviorOnFatal() }() cmdutil.BehaviorOnFatal(func(e string, code int) { saw_fatal = true; panic(e) }) cmd.SetArgs(test.args) cmd.Execute() }() if test.expectFatal { if !saw_fatal { t.Fatalf("%s: unexpected non-error when using %s", test.description, currMethod) } } if test.expectDelete { // Test Delete if !testEviction && !deleted { t.Fatalf("%s: pod never deleted", test.description) } // Test Eviction if testEviction && !evicted { t.Fatalf("%s: pod never evicted", test.description) } } if !test.expectDelete { if deleted { t.Fatalf("%s: unexpected delete when using %s", test.description, currMethod) } } } } }
func TestTaint(t *testing.T) { tests := []struct { description string oldTaints []api.Taint newTaints []api.Taint args []string expectFatal bool expectTaint bool }{ // success cases { description: "taints a node with effect NoSchedule", newTaints: []api.Taint{{ Key: "foo", Value: "bar", Effect: "NoSchedule", }}, args: []string{"node", "node-name", "foo=bar:NoSchedule"}, expectFatal: false, expectTaint: true, }, { description: "taints a node with effect PreferNoSchedule", newTaints: []api.Taint{{ Key: "foo", Value: "bar", Effect: "PreferNoSchedule", }}, args: []string{"node", "node-name", "foo=bar:PreferNoSchedule"}, expectFatal: false, expectTaint: true, }, { description: "update an existing taint on the node, change the value from bar to barz", oldTaints: []api.Taint{{ Key: "foo", Value: "bar", Effect: "NoSchedule", }}, newTaints: []api.Taint{{ Key: "foo", Value: "barz", Effect: "NoSchedule", }}, args: []string{"node", "node-name", "foo=barz:NoSchedule", "--overwrite"}, expectFatal: false, expectTaint: true, }, { description: "taints a node with two taints", newTaints: []api.Taint{{ Key: "dedicated", Value: "namespaceA", Effect: "NoSchedule", }, { Key: "foo", Value: "bar", Effect: "PreferNoSchedule", }}, args: []string{"node", "node-name", "dedicated=namespaceA:NoSchedule", "foo=bar:PreferNoSchedule"}, expectFatal: false, expectTaint: true, }, { description: "node has two taints with the same key but different effect, remove one of them by indicating exact key and effect", oldTaints: []api.Taint{{ Key: "dedicated", Value: "namespaceA", Effect: "NoSchedule", }, { Key: "dedicated", Value: "namespaceA", Effect: "PreferNoSchedule", }}, newTaints: []api.Taint{{ Key: "dedicated", Value: "namespaceA", Effect: "PreferNoSchedule", }}, args: []string{"node", "node-name", "dedicated:NoSchedule-"}, expectFatal: false, expectTaint: true, }, { description: "node has two taints with the same key but different effect, remove all of them with wildcard", oldTaints: []api.Taint{{ Key: "dedicated", Value: "namespaceA", Effect: "NoSchedule", }, { Key: "dedicated", Value: "namespaceA", Effect: "PreferNoSchedule", }}, newTaints: []api.Taint{}, args: []string{"node", "node-name", "dedicated-"}, expectFatal: false, expectTaint: true, }, { description: "node has two taints, update one of them and remove the other", oldTaints: []api.Taint{{ Key: "dedicated", Value: "namespaceA", Effect: "NoSchedule", }, { Key: "foo", Value: "bar", Effect: "PreferNoSchedule", }}, newTaints: []api.Taint{{ Key: "foo", Value: "barz", Effect: "PreferNoSchedule", }}, args: []string{"node", "node-name", "dedicated:NoSchedule-", "foo=barz:PreferNoSchedule", "--overwrite"}, expectFatal: false, expectTaint: true, }, // error cases { description: "invalid taint key", args: []string{"node", "node-name", "nospecialchars^@=banana:NoSchedule"}, expectFatal: true, expectTaint: false, }, { description: "invalid taint effect", args: []string{"node", "node-name", "foo=bar:NoExcute"}, expectFatal: true, expectTaint: false, }, { description: "duplicated taints with the same key and effect should be rejected", args: []string{"node", "node-name", "foo=bar:NoExcute", "foo=barz:NoExcute"}, expectFatal: true, expectTaint: false, }, { description: "can't update existing taint on the node, since 'overwrite' flag is not set", oldTaints: []api.Taint{{ Key: "foo", Value: "bar", Effect: "NoSchedule", }}, newTaints: []api.Taint{{ Key: "foo", Value: "bar", Effect: "NoSchedule", }}, args: []string{"node", "node-name", "foo=bar:NoSchedule"}, expectFatal: true, expectTaint: false, }, } for _, test := range tests { oldNode, expectNewNode := generateNodeAndTaintedNode(test.oldTaints, test.newTaints) new_node := &api.Node{} tainted := false f, tf, codec, ns := NewAPIFactory() tf.Client = &fake.RESTClient{ NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { m := &MyReq{req} switch { case m.isFor("GET", "/nodes/node-name"): return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, oldNode)}, nil case m.isFor("PATCH", "/nodes/node-name"), m.isFor("PUT", "/nodes/node-name"): tainted = true data, err := ioutil.ReadAll(req.Body) if err != nil { t.Fatalf("%s: unexpected error: %v", test.description, err) } defer req.Body.Close() if err := runtime.DecodeInto(codec, data, new_node); err != nil { t.Fatalf("%s: unexpected error: %v", test.description, err) } if !AnnotationsHaveEqualTaints(expectNewNode.Annotations, new_node.Annotations) { t.Fatalf("%s: expected:\n%v\nsaw:\n%v\n", test.description, expectNewNode.Annotations, new_node.Annotations) } return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, new_node)}, nil default: t.Fatalf("%s: unexpected request: %v %#v\n%#v", test.description, req.Method, req.URL, req) return nil, nil } }), } tf.ClientConfig = defaultClientConfig() buf := bytes.NewBuffer([]byte{}) cmd := NewCmdTaint(f, buf) saw_fatal := false func() { defer func() { // Recover from the panic below. _ = recover() // Restore cmdutil behavior cmdutil.DefaultBehaviorOnFatal() }() cmdutil.BehaviorOnFatal(func(e string, code int) { saw_fatal = true; panic(e) }) cmd.SetArgs(test.args) cmd.Execute() }() if test.expectFatal { if !saw_fatal { t.Fatalf("%s: unexpected non-error", test.description) } } if test.expectTaint { if !tainted { t.Fatalf("%s: node not tainted", test.description) } } if !test.expectTaint { if tainted { t.Fatalf("%s: unexpected taint", test.description) } } } }