diff --git a/docs/kubectl.md b/docs/kubectl.md index a2659eee10d..593464f811c 100644 --- a/docs/kubectl.md +++ b/docs/kubectl.md @@ -394,7 +394,6 @@ Additional help topics: kubectl rollingupdate Perform a rolling update of the given ReplicationController kubectl resize Set a new size for a resizable resource (currently only Replication Controllers) kubectl run-container Run a particular image on the cluster. - kubectl stop Gracefully shutdown a resource Use "kubectl help [command]" for more information about that command. ``` @@ -934,46 +933,3 @@ Usage: ``` -#### stop -Gracefully shutdown a resource - -Attempts to shutdown and delete a resource that supports graceful termination. -If the resource is resizable it will be resized to 0 before deletion. - -Examples: - $ kubectl stop replicationcontroller foo - foo stopped - - -Usage: -``` - kubectl stop [flags] - - Available Flags: - --alsologtostderr=false: log to standard error as well as files - --api-version="": The API version to use when talking to the server - -a, --auth-path="": Path to the auth info file. If missing, prompt the user. Only used if using https. - --certificate-authority="": Path to a cert. file for the certificate authority. - --client-certificate="": Path to a client key file for TLS. - --client-key="": Path to a client key file for TLS. - --cluster="": The name of the kubeconfig cluster to use - --context="": The name of the kubeconfig context to use - --insecure-skip-tls-verify=false: If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure. - --kubeconfig="": Path to the kubeconfig file to use for CLI requests. - --log_backtrace_at=:0: when logging hits line file:N, emit a stack trace - --log_dir=: If non-empty, write log files in this directory - --log_flush_frequency=5s: Maximum number of seconds between log flushes - --logtostderr=true: log to standard error instead of files - --match-server-version=false: Require server version to match client version - -n, --namespace="": If present, the namespace scope for this CLI request. - --ns-path="/home/username/.kubernetes_ns": Path to the namespace info file that holds the namespace context to use for CLI requests. - -s, --server="": The address of the Kubernetes API server - --stderrthreshold=2: logs at or above this threshold go to stderr - --token="": Bearer token for authentication to the API server. - --user="": The name of the kubeconfig user to use - --v=0: log level for V logs - --validate=false: If true, use a schema to validate the input before sending it - --vmodule=: comma-separated list of pattern=N settings for file-filtered logging - -``` - diff --git a/pkg/kubectl/cmd/cmd.go b/pkg/kubectl/cmd/cmd.go index 2a94c6efc35..29c9bd904bc 100644 --- a/pkg/kubectl/cmd/cmd.go +++ b/pkg/kubectl/cmd/cmd.go @@ -64,8 +64,6 @@ type Factory struct { Printer func(cmd *cobra.Command, mapping *meta.RESTMapping, noHeaders bool) (kubectl.ResourcePrinter, error) // Returns a Resizer for changing the size of the specified RESTMapping type or an error Resizer func(cmd *cobra.Command, mapping *meta.RESTMapping) (kubectl.Resizer, error) - // Returns a Reaper for gracefully shutting down resources. - Reaper func(cmd *cobra.Command, mapping *meta.RESTMapping) (kubectl.Reaper, error) // Returns a schema that can validate objects stored on disk. Validator func(*cobra.Command) (validation.Schema, error) // Returns the default namespace to use in cases where no other namespace is specified @@ -133,14 +131,11 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { if err != nil { return nil, err } - return kubectl.ResizerFor(mapping.Kind, client) - }, - Reaper: func(cmd *cobra.Command, mapping *meta.RESTMapping) (kubectl.Reaper, error) { - client, err := clients.ClientForVersion(mapping.APIVersion) - if err != nil { - return nil, err + resizer, ok := kubectl.ResizerFor(mapping.Kind, client) + if !ok { + return nil, fmt.Errorf("no resizer has been implemented for %q", mapping.Kind) } - return kubectl.ReaperFor(mapping.Kind, client) + return resizer, nil }, Validator: func(cmd *cobra.Command) (validation.Schema, error) { if GetFlagBool(cmd, "validate") { @@ -211,7 +206,6 @@ Find more information at https://github.com/GoogleCloudPlatform/kubernetes.`, cmds.AddCommand(f.NewCmdResize(out)) cmds.AddCommand(f.NewCmdRunContainer(out)) - cmds.AddCommand(f.NewCmdStop(out)) return cmds } diff --git a/pkg/kubectl/cmd/stop.go b/pkg/kubectl/cmd/stop.go deleted file mode 100644 index 75431b0129a..00000000000 --- a/pkg/kubectl/cmd/stop.go +++ /dev/null @@ -1,55 +0,0 @@ -/* -Copyright 2014 Google Inc. All rights reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package cmd - -import ( - "fmt" - "io" - - "github.com/spf13/cobra" -) - -func (f *Factory) NewCmdStop(out io.Writer) *cobra.Command { - cmd := &cobra.Command{ - Use: "stop ", - Short: "Gracefully shutdown a resource", - Long: `Gracefully shutdown a resource - -Attempts to shutdown and delete a resource that supports graceful termination. -If the resource is resizable it will be resized to 0 before deletion. - -Examples: - $ kubectl stop replicationcontroller foo - foo stopped -`, - Run: func(cmd *cobra.Command, args []string) { - if len(args) != 2 { - usageError(cmd, " ") - } - mapper, _ := f.Object(cmd) - mapping, namespace, name := ResourceFromArgs(cmd, args, mapper) - - reaper, err := f.Reaper(cmd, mapping) - checkErr(err) - - s, err := reaper.Stop(namespace, name) - checkErr(err) - fmt.Fprintf(out, "%s\n", s) - }, - } - return cmd -} diff --git a/pkg/kubectl/resize.go b/pkg/kubectl/resize.go index ff3f866a631..b92d45d3d07 100644 --- a/pkg/kubectl/resize.go +++ b/pkg/kubectl/resize.go @@ -58,12 +58,12 @@ type Resizer interface { Resize(namespace, name string, preconditions *ResizePrecondition, newSize uint) (string, error) } -func ResizerFor(kind string, c client.Interface) (Resizer, error) { +func ResizerFor(kind string, c *client.Client) (Resizer, bool) { switch kind { case "ReplicationController": - return &ReplicationControllerResizer{c}, nil + return &ReplicationControllerResizer{c}, true } - return nil, fmt.Errorf("no resizer has been implemented for %q", kind) + return nil, false } type ReplicationControllerResizer struct { diff --git a/pkg/kubectl/rolling_updater_test.go b/pkg/kubectl/rolling_updater_test.go index aad03195f11..d1b56cb9551 100644 --- a/pkg/kubectl/rolling_updater_test.go +++ b/pkg/kubectl/rolling_updater_test.go @@ -26,18 +26,18 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/client" ) -type updaterFake struct { +type customFake struct { *client.Fake ctrl client.ReplicationControllerInterface } -func (c *updaterFake) ReplicationControllers(namespace string) client.ReplicationControllerInterface { +func (c *customFake) ReplicationControllers(namespace string) client.ReplicationControllerInterface { return c.ctrl } func fakeClientFor(namespace string, responses []fakeResponse) client.Interface { fake := client.Fake{} - return &updaterFake{ + return &customFake{ &fake, &fakeRc{ &client.FakeReplicationControllers{ diff --git a/pkg/kubectl/stop.go b/pkg/kubectl/stop.go deleted file mode 100644 index 9f3537c9174..00000000000 --- a/pkg/kubectl/stop.go +++ /dev/null @@ -1,110 +0,0 @@ -/* -Copyright 2014 Google Inc. All rights reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package kubectl - -import ( - "fmt" - "time" - - "github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta" - "github.com/GoogleCloudPlatform/kubernetes/pkg/client" - "github.com/GoogleCloudPlatform/kubernetes/pkg/util/wait" -) - -const ( - interval = time.Second * 3 - timeout = time.Minute * 5 -) - -// A Reaper handles terminating an object as gracefully as possible. -type Reaper interface { - Stop(namespace, name string) (string, error) -} - -func ReaperFor(kind string, c client.Interface) (Reaper, error) { - switch kind { - case "ReplicationController": - return &ReplicationControllerReaper{c, interval, timeout}, nil - case "Pod": - return &PodReaper{c}, nil - case "Service": - return &ServiceReaper{c}, nil - } - return nil, fmt.Errorf("no reaper has been implemented for %q", kind) -} - -type ReplicationControllerReaper struct { - client.Interface - pollInterval, timeout time.Duration -} -type PodReaper struct { - client.Interface -} -type ServiceReaper struct { - client.Interface -} - -type objInterface interface { - Delete(name string) error - Get(name string) (meta.Interface, error) -} - -func (reaper *ReplicationControllerReaper) Stop(namespace, name string) (string, error) { - rc := reaper.ReplicationControllers(namespace) - controller, err := rc.Get(name) - if err != nil { - return "", err - } - - controller.Spec.Replicas = 0 - // TODO: do retry on 409 errors here? - if _, err := rc.Update(controller); err != nil { - return "", err - } - if err := wait.Poll(reaper.pollInterval, reaper.timeout, - client.ControllerHasDesiredReplicas(reaper, controller)); err != nil { - return "", err - } - if err := rc.Delete(name); err != nil { - return "", err - } - return fmt.Sprintf("%s stopped", name), nil -} - -func (reaper *PodReaper) Stop(namespace, name string) (string, error) { - pods := reaper.Pods(namespace) - _, err := pods.Get(name) - if err != nil { - return "", err - } - if err := pods.Delete(name); err != nil { - return "", err - } - return fmt.Sprintf("%s stopped", name), nil -} - -func (reaper *ServiceReaper) Stop(namespace, name string) (string, error) { - services := reaper.Services(namespace) - _, err := services.Get(name) - if err != nil { - return "", err - } - if err := services.Delete(name); err != nil { - return "", err - } - return fmt.Sprintf("%s stopped", name), nil -} diff --git a/pkg/kubectl/stop_test.go b/pkg/kubectl/stop_test.go deleted file mode 100644 index 0d7c3f3a22b..00000000000 --- a/pkg/kubectl/stop_test.go +++ /dev/null @@ -1,168 +0,0 @@ -/* -Copyright 2014 Google Inc. All rights reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package kubectl - -import ( - "fmt" - "testing" - "time" - - "github.com/GoogleCloudPlatform/kubernetes/pkg/api" - "github.com/GoogleCloudPlatform/kubernetes/pkg/client" -) - -func TestReplicationControllerStop(t *testing.T) { - fake := &client.Fake{ - Ctrl: api.ReplicationController{ - Spec: api.ReplicationControllerSpec{ - Replicas: 0, - }, - }, - } - reaper := ReplicationControllerReaper{fake, time.Millisecond, time.Millisecond} - name := "foo" - s, err := reaper.Stop("default", name) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - expected := "foo stopped" - if s != expected { - t.Errorf("expected %s, got %s", expected, s) - } - if len(fake.Actions) != 4 { - t.Errorf("unexpected actions: %v, expected 4 actions (get, update, get, delete)", fake.Actions) - } - for i, action := range []string{"get", "update", "get", "delete"} { - if fake.Actions[i].Action != action+"-controller" { - t.Errorf("unexpected action: %v, expected %s-controller", fake.Actions[i], action) - } - } -} - -type noSuchPod struct { - *client.FakePods -} - -func (c *noSuchPod) Get(name string) (*api.Pod, error) { - return nil, fmt.Errorf("%s does not exist", name) -} - -type noDeleteService struct { - *client.FakeServices -} - -func (c *noDeleteService) Delete(service string) error { - return fmt.Errorf("I'm afraid I can't do that, Dave") -} - -type reaperFake struct { - *client.Fake - noSuchPod, noDeleteService bool -} - -func (c *reaperFake) Pods(namespace string) client.PodInterface { - pods := &client.FakePods{c.Fake, namespace} - if c.noSuchPod { - return &noSuchPod{pods} - } - return pods -} - -func (c *reaperFake) Services(namespace string) client.ServiceInterface { - services := &client.FakeServices{c.Fake, namespace} - if c.noDeleteService { - return &noDeleteService{services} - } - return services -} - -func TestSimpleStop(t *testing.T) { - tests := []struct { - fake *reaperFake - kind string - actions []string - expectError bool - test string - }{ - { - fake: &reaperFake{ - Fake: &client.Fake{}, - }, - kind: "Pod", - actions: []string{"get-pod", "delete-pod"}, - expectError: false, - test: "stop pod succeeds", - }, - { - fake: &reaperFake{ - Fake: &client.Fake{}, - }, - kind: "Service", - actions: []string{"get-service", "delete-service"}, - expectError: false, - test: "stop service succeeds", - }, - { - fake: &reaperFake{ - Fake: &client.Fake{}, - noSuchPod: true, - }, - kind: "Pod", - actions: []string{}, - expectError: true, - test: "stop pod fails, no pod", - }, - { - fake: &reaperFake{ - Fake: &client.Fake{}, - noDeleteService: true, - }, - kind: "Service", - actions: []string{"get-service"}, - expectError: true, - test: "stop service fails, can't delete", - }, - } - for _, test := range tests { - fake := test.fake - reaper, err := ReaperFor(test.kind, fake) - if err != nil { - t.Errorf("unexpected error: %v (%s)", err, test.test) - } - s, err := reaper.Stop("default", "foo") - if err != nil && !test.expectError { - t.Errorf("unexpected error: %v (%s)", err, test.test) - } - if err == nil { - if test.expectError { - t.Errorf("unexpected non-error: %v (%s)", err, test.test) - } - if s != "foo stopped" { - t.Errorf("unexpected return: %s (%s)", s, test.test) - } - } - if len(test.actions) != len(fake.Actions) { - t.Errorf("unexpected actions: %v; expected %v (%s)", fake.Actions, test.actions, test.test) - } - for i, action := range fake.Actions { - testAction := test.actions[i] - if action.Action != testAction { - t.Errorf("unexpected action: %v; expected %v (%s)", action, testAction, test.test) - } - } - } -}