diff --git a/contrib/completions/bash/kubectl b/contrib/completions/bash/kubectl index df530d0577f..4132c1e6f75 100644 --- a/contrib/completions/bash/kubectl +++ b/contrib/completions/bash/kubectl @@ -364,6 +364,7 @@ _kubectl_delete() flags+=("--grace-period=") flags+=("--help") flags+=("-h") + flags+=("--ignore-not-found") flags+=("--selector=") two_word_flags+=("-l") @@ -587,6 +588,7 @@ _kubectl_stop() flags+=("--grace-period=") flags+=("--help") flags+=("-h") + flags+=("--ignore-not-found") flags+=("--selector=") two_word_flags+=("-l") diff --git a/docs/kubectl_delete.md b/docs/kubectl_delete.md index df5e54cbc68..c5e094de147 100644 --- a/docs/kubectl_delete.md +++ b/docs/kubectl_delete.md @@ -42,12 +42,13 @@ $ kubectl delete pods --all ### Options ``` - --all=false: [-all] to select all the specified resources + --all=false: [-all] to select all the specified resources. --cascade=true: If true, cascade the delete resources managed by this resource (e.g. Pods created by a ReplicationController). Default true. - -f, --filename=[]: Filename, directory, or URL to a file containing the resource to delete + -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 - -l, --selector="": Selector (label query) to filter on + --ignore-not-found=false: Treat "resource not found" as a successful delete. + -l, --selector="": Selector (label query) to filter on. ``` ### Options inherited from parent commands @@ -82,6 +83,6 @@ $ kubectl delete pods --all ### SEE ALSO * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-05-21 10:33:11.18056941 +0000 UTC +###### Auto generated by spf13/cobra at 2015-05-21 18:30:45.437003409 +0000 UTC [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/kubectl_delete.md?pixel)]() diff --git a/docs/kubectl_stop.md b/docs/kubectl_stop.md index dc9eff215da..e64cffd3f19 100644 --- a/docs/kubectl_stop.md +++ b/docs/kubectl_stop.md @@ -33,11 +33,12 @@ $ kubectl stop -f path/to/resources ### Options ``` - --all=false: [-all] to select all the specified resources - -f, --filename=[]: Filename, directory, or URL to file of resource(s) to be stopped + --all=false: [-all] to select all the specified resources. + -f, --filename=[]: Filename, directory, or URL to file of resource(s) to be stopped. --grace-period=-1: Period of time in seconds given to the resource to terminate gracefully. Ignored if negative. -h, --help=false: help for stop - -l, --selector="": Selector (label query) to filter on + --ignore-not-found=false: Treat "resource not found" as a successful stop. + -l, --selector="": Selector (label query) to filter on. ``` ### Options inherited from parent commands @@ -72,6 +73,6 @@ $ kubectl stop -f path/to/resources ### SEE ALSO * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-05-21 10:33:11.190996891 +0000 UTC +###### Auto generated by spf13/cobra at 2015-05-21 18:30:45.439945376 +0000 UTC [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/kubectl_stop.md?pixel)]() diff --git a/docs/man/man1/kubectl-delete.1 b/docs/man/man1/kubectl-delete.1 index bf955c71261..d3d9e282566 100644 --- a/docs/man/man1/kubectl-delete.1 +++ b/docs/man/man1/kubectl-delete.1 @@ -31,7 +31,7 @@ will be lost along with the rest of the resource. .SH OPTIONS .PP \fB\-\-all\fP=false - [\-all] to select all the specified resources + [\-all] to select all the specified resources. .PP \fB\-\-cascade\fP=true @@ -39,7 +39,7 @@ will be lost along with the rest of the resource. .PP \fB\-f\fP, \fB\-\-filename\fP=[] - Filename, directory, or URL to a file containing the resource to delete + Filename, directory, or URL to a file containing the resource to delete. .PP \fB\-\-grace\-period\fP=\-1 @@ -49,9 +49,13 @@ will be lost along with the rest of the resource. \fB\-h\fP, \fB\-\-help\fP=false help for delete +.PP +\fB\-\-ignore\-not\-found\fP=false + Treat "resource not found" as a successful delete. + .PP \fB\-l\fP, \fB\-\-selector\fP="" - Selector (label query) to filter on + Selector (label query) to filter on. .SH OPTIONS INHERITED FROM PARENT COMMANDS diff --git a/docs/man/man1/kubectl-stop.1 b/docs/man/man1/kubectl-stop.1 index ef394d17383..b09739cac69 100644 --- a/docs/man/man1/kubectl-stop.1 +++ b/docs/man/man1/kubectl-stop.1 @@ -23,11 +23,11 @@ If the resource is scalable it will be scaled to 0 before deletion. .SH OPTIONS .PP \fB\-\-all\fP=false - [\-all] to select all the specified resources + [\-all] to select all the specified resources. .PP \fB\-f\fP, \fB\-\-filename\fP=[] - Filename, directory, or URL to file of resource(s) to be stopped + Filename, directory, or URL to file of resource(s) to be stopped. .PP \fB\-\-grace\-period\fP=\-1 @@ -37,9 +37,13 @@ If the resource is scalable it will be scaled to 0 before deletion. \fB\-h\fP, \fB\-\-help\fP=false help for stop +.PP +\fB\-\-ignore\-not\-found\fP=false + Treat "resource not found" as a successful stop. + .PP \fB\-l\fP, \fB\-\-selector\fP="" - Selector (label query) to filter on + Selector (label query) to filter on. .SH OPTIONS INHERITED FROM PARENT COMMANDS diff --git a/hooks/pre-commit b/hooks/pre-commit index c9ed7693a8d..56a5cff2fbb 100755 --- a/hooks/pre-commit +++ b/hooks/pre-commit @@ -92,7 +92,7 @@ if ! hack/verify-gendocs.sh > /dev/null; then echo "${red}ERROR!" echo "Some docs are out of sync between CLI and markdown." echo "To regenerate docs, run:" - echo " hack/run-gendocs.sh > docs/kubectl.md" + echo " hack/run-gendocs.sh" exit_code=1 else echo "${green}OK" diff --git a/pkg/kubectl/cmd/delete.go b/pkg/kubectl/cmd/delete.go index e9a10e74976..b7ab12a3717 100644 --- a/pkg/kubectl/cmd/delete.go +++ b/pkg/kubectl/cmd/delete.go @@ -69,10 +69,11 @@ func NewCmdDelete(f *cmdutil.Factory, out io.Writer) *cobra.Command { cmdutil.CheckErr(err) }, } - usage := "Filename, directory, or URL to a file containing the resource to delete" + usage := "Filename, directory, or URL to a file containing the resource to delete." 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().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("cascade", true, "If true, cascade the delete 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.") return cmd @@ -98,20 +99,24 @@ func RunDelete(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str return err } + ignoreNotFound := cmdutil.GetFlagBool(cmd, "ignore-not-found") // By default use a reaper to delete all related resources. if cmdutil.GetFlagBool(cmd, "cascade") { - return ReapResult(r, f, out, cmdutil.GetFlagBool(cmd, "cascade"), cmdutil.GetFlagInt(cmd, "grace-period")) + return ReapResult(r, f, out, cmdutil.GetFlagBool(cmd, "cascade"), ignoreNotFound, cmdutil.GetFlagInt(cmd, "grace-period")) } - return DeleteResult(r, out) + return DeleteResult(r, out, ignoreNotFound) } -func ReapResult(r *resource.Result, f *cmdutil.Factory, out io.Writer, isDefaultDelete bool, gracePeriod int) error { +func ReapResult(r *resource.Result, f *cmdutil.Factory, out io.Writer, isDefaultDelete, ignoreNotFound bool, gracePeriod int) error { found := 0 - err := r.IgnoreErrors(errors.IsNotFound).Visit(func(info *resource.Info) error { + if ignoreNotFound { + r = r.IgnoreErrors(errors.IsNotFound) + } + err := r.Visit(func(info *resource.Info) error { found++ reaper, err := f.Reaper(info.Mapping) if err != nil { - // If the error is "not found" and the user didn't explicitly ask for stop. + // If there is no reaper for this resources and the user didn't explicitly ask for stop. if kubectl.IsNoSuchReaperError(err) && isDefaultDelete { return deleteResource(info, out) } @@ -136,9 +141,12 @@ func ReapResult(r *resource.Result, f *cmdutil.Factory, out io.Writer, isDefault return nil } -func DeleteResult(r *resource.Result, out io.Writer) error { +func DeleteResult(r *resource.Result, out io.Writer, ignoreNotFound bool) error { found := 0 - err := r.IgnoreErrors(errors.IsNotFound).Visit(func(info *resource.Info) error { + if ignoreNotFound { + r = r.IgnoreErrors(errors.IsNotFound) + } + err := r.Visit(func(info *resource.Info) error { found++ return deleteResource(info, out) }) diff --git a/pkg/kubectl/cmd/delete_test.go b/pkg/kubectl/cmd/delete_test.go index ef16e1c86b4..b1d3099cf81 100644 --- a/pkg/kubectl/cmd/delete_test.go +++ b/pkg/kubectl/cmd/delete_test.go @@ -18,13 +18,16 @@ package cmd import ( "bytes" + "fmt" "net/http" "strings" "testing" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/testapi" "github.com/GoogleCloudPlatform/kubernetes/pkg/client" + "github.com/GoogleCloudPlatform/kubernetes/pkg/util" ) func TestDeleteObjectByTuple(t *testing.T) { @@ -120,6 +123,34 @@ func TestDeleteObject(t *testing.T) { } } +func TestDeleteObjectNotFound(t *testing.T) { + f, tf, codec := NewAPIFactory() + 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/replicationcontrollers/redis-master" && m == "DELETE": + return &http.Response{StatusCode: 404, Body: stringBody("")}, 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("filename", "../../../examples/guestbook/redis-master-controller.json") + cmd.Flags().Set("cascade", "false") + filenames := cmd.Flags().Lookup("filename").Value.(*util.StringList) + err := RunDelete(f, buf, cmd, []string{}, *filenames) + if err == nil || !errors.IsNotFound(err) { + t.Errorf("unexpected error: expected NotFound, got %v", err) + } +} + func TestDeleteObjectIgnoreNotFound(t *testing.T) { f, tf, codec := NewAPIFactory() tf.Printer = &testPrinter{} @@ -141,6 +172,7 @@ func TestDeleteObjectIgnoreNotFound(t *testing.T) { cmd := NewCmdDelete(f, buf) cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.json") cmd.Flags().Set("cascade", "false") + cmd.Flags().Set("ignore-not-found", "true") cmd.Run(cmd, []string{}) if buf.String() != "" { @@ -181,7 +213,7 @@ func TestDeleteMultipleObject(t *testing.T) { } } -func TestDeleteMultipleObjectIgnoreMissing(t *testing.T) { +func TestDeleteMultipleObjectContinueOnMissing(t *testing.T) { _, svc, _ := testData() f, tf, codec := NewAPIFactory() @@ -207,7 +239,12 @@ func TestDeleteMultipleObjectIgnoreMissing(t *testing.T) { cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.json") cmd.Flags().Set("filename", "../../../examples/guestbook/frontend-service.json") cmd.Flags().Set("cascade", "false") - cmd.Run(cmd, []string{}) + filenames := cmd.Flags().Lookup("filename").Value.(*util.StringList) + fmt.Printf("filenames: %v\n", filenames) + err := RunDelete(f, buf, cmd, []string{}, *filenames) + if err == nil || !errors.IsNotFound(err) { + t.Errorf("unexpected error: expected NotFound, got %v", err) + } if buf.String() != "services/frontend\n" { t.Errorf("unexpected output: %s", buf.String()) diff --git a/pkg/kubectl/cmd/stop.go b/pkg/kubectl/cmd/stop.go index 2c39ec67c62..a821fde854d 100644 --- a/pkg/kubectl/cmd/stop.go +++ b/pkg/kubectl/cmd/stop.go @@ -57,10 +57,11 @@ func NewCmdStop(f *cmdutil.Factory, out io.Writer) *cobra.Command { cmdutil.CheckErr(RunStop(f, cmd, args, flags.Filenames, out)) }, } - usage := "Filename, directory, or URL to file of resource(s) to be stopped" + usage := "Filename, directory, or URL to file of resource(s) to be stopped." kubectl.AddJsonFilenameFlag(cmd, &flags.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().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 stop.") cmd.Flags().Int("grace-period", -1, "Period of time in seconds given to the resource to terminate gracefully. Ignored if negative.") return cmd } @@ -83,5 +84,5 @@ func RunStop(f *cmdutil.Factory, cmd *cobra.Command, args []string, filenames ut if r.Err() != nil { return r.Err() } - return ReapResult(r, f, out, false, cmdutil.GetFlagInt(cmd, "grace-period")) + return ReapResult(r, f, out, false, cmdutil.GetFlagBool(cmd, "ignore-not-found"), cmdutil.GetFlagInt(cmd, "grace-period")) }