diff --git a/docs/man/man1/kubectl-delete.1 b/docs/man/man1/kubectl-delete.1 index 98681c763d0..b44526ddc44 100644 --- a/docs/man/man1/kubectl-delete.1 +++ b/docs/man/man1/kubectl-delete.1 @@ -51,7 +51,7 @@ will be lost along with the rest of the resource. .PP \fB\-\-ignore\-not\-found\fP=false - Treat "resource not found" as a successful delete. + Treat "resource not found" as a successful delete. Defaults to "true" when \-\-all is specified. .PP \fB\-l\fP, \fB\-\-selector\fP="" diff --git a/docs/user-guide/kubectl/kubectl_delete.md b/docs/user-guide/kubectl/kubectl_delete.md index 85b0ac53c5a..5f245718f64 100644 --- a/docs/user-guide/kubectl/kubectl_delete.md +++ b/docs/user-guide/kubectl/kubectl_delete.md @@ -80,7 +80,7 @@ $ kubectl delete pods --all -f, --filename=[]: Filename, directory, or URL to a file containing the resource to delete. --grace-period=-1: Period of time in seconds given to the resource to terminate gracefully. Ignored if negative. -h, --help=false: help for delete - --ignore-not-found=false: Treat "resource not found" as a successful delete. + --ignore-not-found=false: Treat "resource not found" as a successful delete. Defaults to "true" when --all is specified. -l, --selector="": Selector (label query) to filter on. --timeout=0: The length of time to wait before giving up on a delete, zero means determine a timeout from the size of the object ``` @@ -118,7 +118,7 @@ $ kubectl delete pods --all * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-07-16 05:13:00.190175769 +0000 UTC +###### Auto generated by spf13/cobra at 2015-07-23 17:43:06.942148224 +0000 UTC diff --git a/pkg/kubectl/cmd/delete.go b/pkg/kubectl/cmd/delete.go index aa1c3a4208c..dd3d7db4325 100644 --- a/pkg/kubectl/cmd/delete.go +++ b/pkg/kubectl/cmd/delete.go @@ -74,7 +74,7 @@ func NewCmdDelete(f *cmdutil.Factory, out io.Writer) *cobra.Command { kubectl.AddJsonFilenameFlag(cmd, &filenames, usage) cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on.") cmd.Flags().Bool("all", false, "[-all] to select all the specified resources.") - cmd.Flags().Bool("ignore-not-found", false, "Treat \"resource not found\" as a successful delete.") + cmd.Flags().Bool("ignore-not-found", false, "Treat \"resource not found\" as a successful delete. Defaults to \"true\" when --all is specified.") cmd.Flags().Bool("cascade", true, "If true, cascade the deletion of the resources managed by this resource (e.g. Pods created by a ReplicationController). Default true.") cmd.Flags().Int("grace-period", -1, "Period of time in seconds given to the resource to terminate gracefully. Ignored if negative.") cmd.Flags().Duration("timeout", 0, "The length of time to wait before giving up on a delete, zero means determine a timeout from the size of the object") @@ -86,13 +86,14 @@ func RunDelete(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str if err != nil { return err } + deleteAll := cmdutil.GetFlagBool(cmd, "all") mapper, typer := f.Object() r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). FilenameParam(enforceNamespace, filenames...). SelectorParam(cmdutil.GetFlagString(cmd, "selector")). - SelectAllParam(cmdutil.GetFlagBool(cmd, "all")). + SelectAllParam(deleteAll). ResourceTypeOrNameArgs(false, args...).RequireObject(false). Flatten(). Do() @@ -102,6 +103,18 @@ func RunDelete(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str } ignoreNotFound := cmdutil.GetFlagBool(cmd, "ignore-not-found") + if deleteAll { + f := cmd.Flags().Lookup("ignore-not-found") + // The flag should never be missing + if f == nil { + return fmt.Errorf("missing --ignore-not-found flag") + } + // If the user didn't explicitly set the option, default to ignoring NotFound errors when used with --all + if !f.Changed { + ignoreNotFound = true + } + } + // By default use a reaper to delete all related resources. if cmdutil.GetFlagBool(cmd, "cascade") { return ReapResult(r, f, out, cmdutil.GetFlagBool(cmd, "cascade"), ignoreNotFound, cmdutil.GetFlagDuration(cmd, "timeout"), cmdutil.GetFlagInt(cmd, "grace-period")) diff --git a/pkg/kubectl/cmd/delete_test.go b/pkg/kubectl/cmd/delete_test.go index 0fff9c7a302..742554b5c19 100644 --- a/pkg/kubectl/cmd/delete_test.go +++ b/pkg/kubectl/cmd/delete_test.go @@ -179,6 +179,86 @@ func TestDeleteObjectIgnoreNotFound(t *testing.T) { } } +func TestDeleteAllNotFound(t *testing.T) { + _, svc, _ := testData() + + f, tf, codec := NewAPIFactory() + + // Add an item to the list which will result in a 404 on delete + svc.Items = append(svc.Items, api.Service{ObjectMeta: api.ObjectMeta{Name: "foo"}}) + notFoundError := &errors.NewNotFound("Service", "foo").(*errors.StatusError).ErrStatus + + tf.Printer = &testPrinter{} + tf.Client = &client.FakeRESTClient{ + Codec: codec, + Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) { + switch p, m := req.URL.Path, req.Method; { + case p == "/namespaces/test/services" && m == "GET": + return &http.Response{StatusCode: 200, Body: objBody(codec, svc)}, nil + case p == "/namespaces/test/services/foo" && m == "DELETE": + return &http.Response{StatusCode: 404, Body: objBody(codec, notFoundError)}, nil + case p == "/namespaces/test/services/baz" && m == "DELETE": + return &http.Response{StatusCode: 200, Body: objBody(codec, &svc.Items[0])}, nil + default: + t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) + return nil, nil + } + }), + } + tf.Namespace = "test" + buf := bytes.NewBuffer([]byte{}) + + cmd := NewCmdDelete(f, buf) + cmd.Flags().Set("all", "true") + cmd.Flags().Set("cascade", "false") + // Make sure we can explicitly choose to fail on NotFound errors, even with --all + cmd.Flags().Set("ignore-not-found", "false") + + err := RunDelete(f, buf, cmd, []string{"services"}, nil) + if err == nil || !errors.IsNotFound(err) { + t.Errorf("unexpected error: expected NotFound, got %v", err) + } +} + +func TestDeleteAllIgnoreNotFound(t *testing.T) { + _, svc, _ := testData() + + f, tf, codec := NewAPIFactory() + + // Add an item to the list which will result in a 404 on delete + svc.Items = append(svc.Items, api.Service{ObjectMeta: api.ObjectMeta{Name: "foo"}}) + notFoundError := &errors.NewNotFound("Service", "foo").(*errors.StatusError).ErrStatus + + tf.Printer = &testPrinter{} + tf.Client = &client.FakeRESTClient{ + Codec: codec, + Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) { + switch p, m := req.URL.Path, req.Method; { + case p == "/namespaces/test/services" && m == "GET": + return &http.Response{StatusCode: 200, Body: objBody(codec, svc)}, nil + case p == "/namespaces/test/services/foo" && m == "DELETE": + return &http.Response{StatusCode: 404, Body: objBody(codec, notFoundError)}, nil + case p == "/namespaces/test/services/baz" && m == "DELETE": + return &http.Response{StatusCode: 200, Body: objBody(codec, &svc.Items[0])}, nil + default: + t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) + return nil, nil + } + }), + } + tf.Namespace = "test" + buf := bytes.NewBuffer([]byte{}) + + cmd := NewCmdDelete(f, buf) + cmd.Flags().Set("all", "true") + cmd.Flags().Set("cascade", "false") + cmd.Run(cmd, []string{"services"}) + + if buf.String() != "services/baz\n" { + t.Errorf("unexpected output: %s", buf.String()) + } +} + func TestDeleteMultipleObject(t *testing.T) { _, svc, rc := testData()