Commit 3cf4d9b2 authored by Jeff Lowdermilk's avatar Jeff Lowdermilk

Delete fails on notfound errors.

If deleting multiple resources, command will continue on error and report not found resources at the end.
parent 0bea034e
...@@ -364,6 +364,7 @@ _kubectl_delete() ...@@ -364,6 +364,7 @@ _kubectl_delete()
flags+=("--grace-period=") flags+=("--grace-period=")
flags+=("--help") flags+=("--help")
flags+=("-h") flags+=("-h")
flags+=("--ignore-not-found")
flags+=("--selector=") flags+=("--selector=")
two_word_flags+=("-l") two_word_flags+=("-l")
...@@ -591,6 +592,7 @@ _kubectl_stop() ...@@ -591,6 +592,7 @@ _kubectl_stop()
flags+=("--grace-period=") flags+=("--grace-period=")
flags+=("--help") flags+=("--help")
flags+=("-h") flags+=("-h")
flags+=("--ignore-not-found")
flags+=("--selector=") flags+=("--selector=")
two_word_flags+=("-l") two_word_flags+=("-l")
......
...@@ -42,12 +42,13 @@ $ kubectl delete pods --all ...@@ -42,12 +42,13 @@ $ kubectl delete pods --all
### Options ### 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. --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. --grace-period=-1: Period of time in seconds given to the resource to terminate gracefully. Ignored if negative.
-h, --help=false: help for delete -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 ### Options inherited from parent commands
...@@ -82,6 +83,6 @@ $ kubectl delete pods --all ...@@ -82,6 +83,6 @@ $ kubectl delete pods --all
### SEE ALSO ### SEE ALSO
* [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager * [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)]() [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/kubectl_delete.md?pixel)]()
...@@ -33,11 +33,12 @@ $ kubectl stop -f path/to/resources ...@@ -33,11 +33,12 @@ $ kubectl stop -f path/to/resources
### Options ### Options
``` ```
--all=false: [-all] to select all the specified resources --all=false: [-all] to select all the specified resources.
-f, --filename=[]: Filename, directory, or URL to file of resource(s) to be stopped -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. --grace-period=-1: Period of time in seconds given to the resource to terminate gracefully. Ignored if negative.
-h, --help=false: help for stop -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 ### Options inherited from parent commands
...@@ -72,6 +73,6 @@ $ kubectl stop -f path/to/resources ...@@ -72,6 +73,6 @@ $ kubectl stop -f path/to/resources
### SEE ALSO ### SEE ALSO
* [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager * [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)]() [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/kubectl_stop.md?pixel)]()
...@@ -31,7 +31,7 @@ will be lost along with the rest of the resource. ...@@ -31,7 +31,7 @@ will be lost along with the rest of the resource.
.SH OPTIONS .SH OPTIONS
.PP .PP
\fB\-\-all\fP=false \fB\-\-all\fP=false
[\-all] to select all the specified resources [\-all] to select all the specified resources.
.PP .PP
\fB\-\-cascade\fP=true \fB\-\-cascade\fP=true
...@@ -39,7 +39,7 @@ will be lost along with the rest of the resource. ...@@ -39,7 +39,7 @@ will be lost along with the rest of the resource.
.PP .PP
\fB\-f\fP, \fB\-\-filename\fP=[] \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 .PP
\fB\-\-grace\-period\fP=\-1 \fB\-\-grace\-period\fP=\-1
...@@ -50,8 +50,12 @@ will be lost along with the rest of the resource. ...@@ -50,8 +50,12 @@ will be lost along with the rest of the resource.
help for delete help for delete
.PP .PP
\fB\-\-ignore\-not\-found\fP=false
Treat "resource not found" as a successful delete.
.PP
\fB\-l\fP, \fB\-\-selector\fP="" \fB\-l\fP, \fB\-\-selector\fP=""
Selector (label query) to filter on Selector (label query) to filter on.
.SH OPTIONS INHERITED FROM PARENT COMMANDS .SH OPTIONS INHERITED FROM PARENT COMMANDS
......
...@@ -23,11 +23,11 @@ If the resource is scalable it will be scaled to 0 before deletion. ...@@ -23,11 +23,11 @@ If the resource is scalable it will be scaled to 0 before deletion.
.SH OPTIONS .SH OPTIONS
.PP .PP
\fB\-\-all\fP=false \fB\-\-all\fP=false
[\-all] to select all the specified resources [\-all] to select all the specified resources.
.PP .PP
\fB\-f\fP, \fB\-\-filename\fP=[] \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 .PP
\fB\-\-grace\-period\fP=\-1 \fB\-\-grace\-period\fP=\-1
...@@ -38,8 +38,12 @@ If the resource is scalable it will be scaled to 0 before deletion. ...@@ -38,8 +38,12 @@ If the resource is scalable it will be scaled to 0 before deletion.
help for stop help for stop
.PP .PP
\fB\-\-ignore\-not\-found\fP=false
Treat "resource not found" as a successful stop.
.PP
\fB\-l\fP, \fB\-\-selector\fP="" \fB\-l\fP, \fB\-\-selector\fP=""
Selector (label query) to filter on Selector (label query) to filter on.
.SH OPTIONS INHERITED FROM PARENT COMMANDS .SH OPTIONS INHERITED FROM PARENT COMMANDS
......
...@@ -92,7 +92,7 @@ if ! hack/verify-gendocs.sh > /dev/null; then ...@@ -92,7 +92,7 @@ if ! hack/verify-gendocs.sh > /dev/null; then
echo "${red}ERROR!" echo "${red}ERROR!"
echo "Some docs are out of sync between CLI and markdown." echo "Some docs are out of sync between CLI and markdown."
echo "To regenerate docs, run:" echo "To regenerate docs, run:"
echo " hack/run-gendocs.sh > docs/kubectl.md" echo " hack/run-gendocs.sh"
exit_code=1 exit_code=1
else else
echo "${green}OK" echo "${green}OK"
......
...@@ -69,10 +69,11 @@ func NewCmdDelete(f *cmdutil.Factory, out io.Writer) *cobra.Command { ...@@ -69,10 +69,11 @@ func NewCmdDelete(f *cmdutil.Factory, out io.Writer) *cobra.Command {
cmdutil.CheckErr(err) 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) kubectl.AddJsonFilenameFlag(cmd, &filenames, usage)
cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on") 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("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().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.") cmd.Flags().Int("grace-period", -1, "Period of time in seconds given to the resource to terminate gracefully. Ignored if negative.")
return cmd return cmd
...@@ -98,20 +99,24 @@ func RunDelete(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str ...@@ -98,20 +99,24 @@ func RunDelete(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str
return err return err
} }
ignoreNotFound := cmdutil.GetFlagBool(cmd, "ignore-not-found")
// By default use a reaper to delete all related resources. // By default use a reaper to delete all related resources.
if cmdutil.GetFlagBool(cmd, "cascade") { 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 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++ found++
reaper, err := f.Reaper(info.Mapping) reaper, err := f.Reaper(info.Mapping)
if err != nil { 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 { if kubectl.IsNoSuchReaperError(err) && isDefaultDelete {
return deleteResource(info, out) return deleteResource(info, out)
} }
...@@ -136,9 +141,12 @@ func ReapResult(r *resource.Result, f *cmdutil.Factory, out io.Writer, isDefault ...@@ -136,9 +141,12 @@ func ReapResult(r *resource.Result, f *cmdutil.Factory, out io.Writer, isDefault
return nil return nil
} }
func DeleteResult(r *resource.Result, out io.Writer) error { func DeleteResult(r *resource.Result, out io.Writer, ignoreNotFound bool) error {
found := 0 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++ found++
return deleteResource(info, out) return deleteResource(info, out)
}) })
......
...@@ -18,13 +18,16 @@ package cmd ...@@ -18,13 +18,16 @@ package cmd
import ( import (
"bytes" "bytes"
"fmt"
"net/http" "net/http"
"strings" "strings"
"testing" "testing"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "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/api/testapi"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client" "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
) )
func TestDeleteObjectByTuple(t *testing.T) { func TestDeleteObjectByTuple(t *testing.T) {
...@@ -120,6 +123,34 @@ func TestDeleteObject(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) { func TestDeleteObjectIgnoreNotFound(t *testing.T) {
f, tf, codec := NewAPIFactory() f, tf, codec := NewAPIFactory()
tf.Printer = &testPrinter{} tf.Printer = &testPrinter{}
...@@ -141,6 +172,7 @@ func TestDeleteObjectIgnoreNotFound(t *testing.T) { ...@@ -141,6 +172,7 @@ func TestDeleteObjectIgnoreNotFound(t *testing.T) {
cmd := NewCmdDelete(f, buf) cmd := NewCmdDelete(f, buf)
cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.json") cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.json")
cmd.Flags().Set("cascade", "false") cmd.Flags().Set("cascade", "false")
cmd.Flags().Set("ignore-not-found", "true")
cmd.Run(cmd, []string{}) cmd.Run(cmd, []string{})
if buf.String() != "" { if buf.String() != "" {
...@@ -181,7 +213,7 @@ func TestDeleteMultipleObject(t *testing.T) { ...@@ -181,7 +213,7 @@ func TestDeleteMultipleObject(t *testing.T) {
} }
} }
func TestDeleteMultipleObjectIgnoreMissing(t *testing.T) { func TestDeleteMultipleObjectContinueOnMissing(t *testing.T) {
_, svc, _ := testData() _, svc, _ := testData()
f, tf, codec := NewAPIFactory() f, tf, codec := NewAPIFactory()
...@@ -207,7 +239,12 @@ func TestDeleteMultipleObjectIgnoreMissing(t *testing.T) { ...@@ -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/redis-master-controller.json")
cmd.Flags().Set("filename", "../../../examples/guestbook/frontend-service.json") cmd.Flags().Set("filename", "../../../examples/guestbook/frontend-service.json")
cmd.Flags().Set("cascade", "false") 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" { if buf.String() != "services/frontend\n" {
t.Errorf("unexpected output: %s", buf.String()) t.Errorf("unexpected output: %s", buf.String())
......
...@@ -57,10 +57,11 @@ func NewCmdStop(f *cmdutil.Factory, out io.Writer) *cobra.Command { ...@@ -57,10 +57,11 @@ func NewCmdStop(f *cmdutil.Factory, out io.Writer) *cobra.Command {
cmdutil.CheckErr(RunStop(f, cmd, args, flags.Filenames, out)) 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) kubectl.AddJsonFilenameFlag(cmd, &flags.Filenames, usage)
cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on") 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("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.") cmd.Flags().Int("grace-period", -1, "Period of time in seconds given to the resource to terminate gracefully. Ignored if negative.")
return cmd return cmd
} }
...@@ -83,5 +84,5 @@ func RunStop(f *cmdutil.Factory, cmd *cobra.Command, args []string, filenames ut ...@@ -83,5 +84,5 @@ func RunStop(f *cmdutil.Factory, cmd *cobra.Command, args []string, filenames ut
if r.Err() != nil { if r.Err() != nil {
return r.Err() 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"))
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment