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 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 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 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) }() }
// NewCLI initialize the upstream E2E framework and set the namespace to match // with the project name. Note that this function does not initialize the project // role bindings for the namespace. func NewCLI(project, adminConfigPath string) *CLI { client := &CLI{} client.kubeFramework = e2e.InitializeFramework(project, client.SetupProject) client.outputDir = os.TempDir() client.username = "******" if len(adminConfigPath) == 0 { FatalErr(fmt.Errorf("You must set the KUBECONFIG variable to admin kubeconfig.")) } client.adminConfigPath = adminConfigPath kcmdutil.BehaviorOnFatal(func(msg string) { panic(msg) }) return client }
func initTestErrorHandler(t *testing.T) { cmdutil.BehaviorOnFatal(func(str string) { t.Errorf("Error running command: %s", str) }) }
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 initTestErrorHandler(t *testing.T) { cmdutil.BehaviorOnFatal(func(str string, code int) { t.Errorf("Error running command (exit code %d): %s", code, str) }) }
func TestInitFederation(t *testing.T) { cmdErrMsg := "" dnsProvider := "" cmdutil.BehaviorOnFatal(func(str string, code int) { cmdErrMsg = str }) fakeKubeFiles, err := kubefedtesting.FakeKubeconfigFiles() if err != nil { t.Fatalf("unexpected error: %v", err) } defer kubefedtesting.RemoveFakeKubeconfigFiles(fakeKubeFiles) testCases := []struct { federation string kubeconfigGlobal string kubeconfigExplicit string dnsZoneName string lbIP string image string expectedErr string dnsProvider string }{ { federation: "union", kubeconfigGlobal: fakeKubeFiles[0], kubeconfigExplicit: "", dnsZoneName: "example.test.", lbIP: "10.20.30.40", image: "example.test/foo:bar", expectedErr: "", dnsProvider: "test-dns-provider", }, { federation: "union", kubeconfigGlobal: fakeKubeFiles[0], kubeconfigExplicit: "", dnsZoneName: "example.test.", lbIP: "10.20.30.40", image: "example.test/foo:bar", expectedErr: "", dnsProvider: "", //test for default value of dns provider }, } for i, tc := range testCases { cmdErrMsg = "" dnsProvider = "" buf := bytes.NewBuffer([]byte{}) if "" != tc.dnsProvider { dnsProvider = tc.dnsProvider } else { dnsProvider = "google-clouddns" //default value of dns-provider } hostFactory, err := fakeInitHostFactory(tc.federation, util.DefaultFederationSystemNamespace, tc.lbIP, tc.dnsZoneName, tc.image, dnsProvider) if err != nil { t.Fatalf("[%d] unexpected error: %v", i, err) } adminConfig, err := kubefedtesting.NewFakeAdminConfig(hostFactory, tc.kubeconfigGlobal) if err != nil { t.Fatalf("[%d] unexpected error: %v", i, err) } cmd := NewCmdInit(buf, adminConfig) cmd.Flags().Set("kubeconfig", tc.kubeconfigExplicit) cmd.Flags().Set("host-cluster-context", "substrate") cmd.Flags().Set("dns-zone-name", tc.dnsZoneName) cmd.Flags().Set("image", tc.image) if "" != tc.dnsProvider { cmd.Flags().Set("dns-provider", tc.dnsProvider) } cmd.Run(cmd, []string{tc.federation}) if tc.expectedErr == "" { // uses the name from the federation, not the response // Actual data passed are tested in the fake secret and cluster // REST clients. want := fmt.Sprintf("Federation API server is running at: %s\n", tc.lbIP) if got := buf.String(); got != want { t.Errorf("[%d] unexpected output: got: %s, want: %s", i, got, want) if cmdErrMsg != "" { t.Errorf("[%d] unexpected error message: %s", i, cmdErrMsg) } } } else { if cmdErrMsg != tc.expectedErr { t.Errorf("[%d] expected error: %s, got: %s, output: %s", i, tc.expectedErr, cmdErrMsg, buf.String()) } } testKubeconfigUpdate(t, tc.federation, tc.lbIP, tc.kubeconfigGlobal, tc.kubeconfigExplicit) } }
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) } } } }
func TestJoinFederation(t *testing.T) { cmdErrMsg := "" cmdutil.BehaviorOnFatal(func(str string, code int) { cmdErrMsg = str }) fakeKubeFiles, err := kubefedtesting.FakeKubeconfigFiles() if err != nil { t.Fatalf("unexpected error: %v", err) } defer kubefedtesting.RemoveFakeKubeconfigFiles(fakeKubeFiles) testCases := []struct { cluster string clusterCtx string secret string server string token string kubeconfigGlobal string kubeconfigExplicit string expectedServer string expectedErr string }{ { cluster: "syndicate", clusterCtx: "", secret: "", server: "https://10.20.30.40", token: "badge", kubeconfigGlobal: fakeKubeFiles[0], kubeconfigExplicit: "", expectedServer: "https://10.20.30.40", expectedErr: "", }, { cluster: "ally", clusterCtx: "", secret: "", server: "ally256.example.com:80", token: "souvenir", kubeconfigGlobal: fakeKubeFiles[0], kubeconfigExplicit: fakeKubeFiles[1], expectedServer: "https://ally256.example.com:80", expectedErr: "", }, { cluster: "confederate", clusterCtx: "", secret: "", server: "10.8.8.8", token: "totem", kubeconfigGlobal: fakeKubeFiles[1], kubeconfigExplicit: fakeKubeFiles[2], expectedServer: "https://10.8.8.8", expectedErr: "", }, { cluster: "associate", clusterCtx: "confederate", secret: "confidential", server: "10.8.8.8", token: "totem", kubeconfigGlobal: fakeKubeFiles[1], kubeconfigExplicit: fakeKubeFiles[2], expectedServer: "https://10.8.8.8", expectedErr: "", }, { cluster: "affiliate", clusterCtx: "", secret: "", server: "https://10.20.30.40", token: "badge", kubeconfigGlobal: fakeKubeFiles[0], kubeconfigExplicit: "", expectedServer: "https://10.20.30.40", expectedErr: fmt.Sprintf("error: cluster context %q not found", "affiliate"), }, } for i, tc := range testCases { cmdErrMsg = "" f := testJoinFederationFactory(tc.cluster, tc.secret, tc.expectedServer) buf := bytes.NewBuffer([]byte{}) hostFactory, err := fakeJoinHostFactory(tc.cluster, tc.clusterCtx, tc.secret, tc.server, tc.token) if err != nil { t.Fatalf("[%d] unexpected error: %v", i, err) } adminConfig, err := kubefedtesting.NewFakeAdminConfig(hostFactory, tc.kubeconfigGlobal) if err != nil { t.Fatalf("[%d] unexpected error: %v", i, err) } cmd := NewCmdJoin(f, buf, adminConfig) cmd.Flags().Set("kubeconfig", tc.kubeconfigExplicit) cmd.Flags().Set("host-cluster-context", "substrate") if tc.clusterCtx != "" { cmd.Flags().Set("cluster-context", tc.clusterCtx) } if tc.secret != "" { cmd.Flags().Set("secret-name", tc.secret) } cmd.Run(cmd, []string{tc.cluster}) if tc.expectedErr == "" { // uses the name from the cluster, not the response // Actual data passed are tested in the fake secret and cluster // REST clients. if msg := buf.String(); msg != fmt.Sprintf("cluster %q created\n", tc.cluster) { t.Errorf("[%d] unexpected output: %s", i, msg) if cmdErrMsg != "" { t.Errorf("[%d] unexpected error message: %s", i, cmdErrMsg) } } } else { if cmdErrMsg != tc.expectedErr { t.Errorf("[%d] expected error: %s, got: %s, output: %s", i, tc.expectedErr, cmdErrMsg, buf.String()) } } } }
func TestUnjoinFederation(t *testing.T) { cmdErrMsg := "" cmdutil.BehaviorOnFatal(func(str string, code int) { cmdErrMsg = str }) fakeKubeFiles, err := kubefedtesting.FakeKubeconfigFiles() if err != nil { t.Fatalf("unexpected error: %v", err) } defer kubefedtesting.RemoveFakeKubeconfigFiles(fakeKubeFiles) testCases := []struct { cluster string wantCluster string wantSecret string kubeconfigGlobal string kubeconfigExplicit string expectedServer string expectedErr string }{ // Tests that the contexts and credentials are read from the // global, default kubeconfig and the correct cluster resource // is deregisterd. { cluster: "syndicate", wantCluster: "syndicate", wantSecret: "", kubeconfigGlobal: fakeKubeFiles[0], kubeconfigExplicit: "", expectedServer: "https://10.20.30.40", expectedErr: "", }, // Tests that the contexts and credentials are read from the // explicit kubeconfig file specified and the correct cluster // resource is deregisterd. kubeconfig contains a single // cluster and context. { cluster: "ally", wantCluster: "ally", wantSecret: "", kubeconfigGlobal: fakeKubeFiles[0], kubeconfigExplicit: fakeKubeFiles[1], expectedServer: "http://ally256.example.com:80", expectedErr: "", }, // Tests that the contexts and credentials are read from the // explicit kubeconfig file specified and the correct cluster // resource is deregisterd. kubeconfig consists of multiple // clusters and contexts. { cluster: "confederate", wantCluster: "confederate", wantSecret: "", kubeconfigGlobal: fakeKubeFiles[1], kubeconfigExplicit: fakeKubeFiles[2], expectedServer: "https://10.8.8.8", expectedErr: "", }, // Negative test to ensure that we get the right warning // when the specified cluster to deregister is not found. { cluster: "noexist", wantCluster: "affiliate", wantSecret: "", kubeconfigGlobal: fakeKubeFiles[0], kubeconfigExplicit: "", expectedServer: "https://10.20.30.40", expectedErr: fmt.Sprintf("WARNING: cluster %q not found in federation, so its credentials' secret couldn't be deleted", "affiliate"), }, // Negative test to ensure that we get the right warning // when the specified cluster's credentials secret is not // found. { cluster: "affiliate", wantCluster: "affiliate", wantSecret: "noexist", kubeconfigGlobal: fakeKubeFiles[0], kubeconfigExplicit: "", expectedServer: "https://10.20.30.40", expectedErr: fmt.Sprintf("WARNING: secret %q not found in the host cluster, so it couldn't be deleted", "noexist"), }, } for i, tc := range testCases { cmdErrMsg = "" f := testUnjoinFederationFactory(tc.cluster, tc.expectedServer, tc.wantSecret) buf := bytes.NewBuffer([]byte{}) errBuf := bytes.NewBuffer([]byte{}) hostFactory := fakeUnjoinHostFactory(tc.cluster) adminConfig, err := kubefedtesting.NewFakeAdminConfig(hostFactory, tc.kubeconfigGlobal) if err != nil { t.Fatalf("[%d] unexpected error: %v", i, err) } cmd := NewCmdUnjoin(f, buf, errBuf, adminConfig) cmd.Flags().Set("kubeconfig", tc.kubeconfigExplicit) cmd.Flags().Set("host", "substrate") cmd.Run(cmd, []string{tc.wantCluster}) if tc.expectedErr == "" { // uses the name from the cluster, not the response // Actual data passed are tested in the fake secret and cluster // REST clients. if msg := buf.String(); msg != fmt.Sprintf("Successfully removed cluster %q from federation\n", tc.cluster) { t.Errorf("[%d] unexpected output: %s", i, msg) if cmdErrMsg != "" { t.Errorf("[%d] unexpected error message: %s", i, cmdErrMsg) } } } else { if errMsg := errBuf.String(); errMsg != tc.expectedErr { t.Errorf("[%d] expected warning: %s, got: %s, output: %s", i, tc.expectedErr, errMsg, buf.String()) } } } }