Allow delete multiple resources with the same name

pull/6/head
feihujiang 2015-08-19 16:33:02 +08:00
parent ebe50ea692
commit 943c57ff4e
6 changed files with 94 additions and 1 deletions

View File

@ -174,6 +174,9 @@ $ kubectl delete \-f ./pod.json
# Delete a pod based on the type and name in the JSON passed into stdin. # Delete a pod based on the type and name in the JSON passed into stdin.
$ cat pod.json | kubectl delete \-f \- $ cat pod.json | kubectl delete \-f \-
# Delete pods and services with same names "baz" and "foo"
$ kubectl delete pod,service baz foo
# Delete pods and services with label name=myLabel. # Delete pods and services with label name=myLabel.
$ kubectl delete pods,services \-l name=myLabel $ kubectl delete pods,services \-l name=myLabel

View File

@ -61,6 +61,9 @@ $ kubectl delete -f ./pod.json
# Delete a pod based on the type and name in the JSON passed into stdin. # Delete a pod based on the type and name in the JSON passed into stdin.
$ cat pod.json | kubectl delete -f - $ cat pod.json | kubectl delete -f -
# Delete pods and services with same names "baz" and "foo"
$ kubectl delete pod,service baz foo
# Delete pods and services with label name=myLabel. # Delete pods and services with label name=myLabel.
$ kubectl delete pods,services -l name=myLabel $ kubectl delete pods,services -l name=myLabel
@ -118,7 +121,7 @@ $ kubectl delete pods --all
* [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-08-20 22:01:12.476048394 +0000 UTC ###### Auto generated by spf13/cobra at 2015-08-21 06:18:47.444397685 +0000 UTC
<!-- BEGIN MUNGE: GENERATED_ANALYTICS --> <!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_delete.md?pixel)]() [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_delete.md?pixel)]()

View File

@ -47,6 +47,9 @@ $ kubectl delete -f ./pod.json
# Delete a pod based on the type and name in the JSON passed into stdin. # Delete a pod based on the type and name in the JSON passed into stdin.
$ cat pod.json | kubectl delete -f - $ cat pod.json | kubectl delete -f -
# Delete pods and services with same names "baz" and "foo"
$ kubectl delete pod,service baz foo
# Delete pods and services with label name=myLabel. # Delete pods and services with label name=myLabel.
$ kubectl delete pods,services -l name=myLabel $ kubectl delete pods,services -l name=myLabel

View File

@ -335,6 +335,41 @@ func TestDeleteMultipleObjectContinueOnMissing(t *testing.T) {
} }
} }
func TestDeleteMultipleResourcesWithTheSameName(t *testing.T) {
_, svc, rc := testData()
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/baz" && m == "DELETE":
return &http.Response{StatusCode: 200, Body: objBody(codec, &rc.Items[0])}, nil
case p == "/namespaces/test/replicationcontrollers/foo" && m == "DELETE":
return &http.Response{StatusCode: 200, Body: objBody(codec, &rc.Items[0])}, nil
case p == "/namespaces/test/services/baz" && m == "DELETE":
return &http.Response{StatusCode: 200, Body: objBody(codec, &svc.Items[0])}, nil
case p == "/namespaces/test/services/foo" && m == "DELETE":
return &http.Response{StatusCode: 200, Body: objBody(codec, &svc.Items[0])}, nil
default:
// Ensures no GET is performed when deleting by name
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("namespace", "test")
cmd.Flags().Set("cascade", "false")
cmd.Flags().Set("output", "name")
cmd.Run(cmd, []string{"replicationcontrollers,services", "baz", "foo"})
if buf.String() != "replicationcontroller/baz\nreplicationcontroller/foo\nservice/baz\nservice/foo\n" {
t.Errorf("unexpected output: %s", buf.String())
}
}
func TestDeleteDirectory(t *testing.T) { func TestDeleteDirectory(t *testing.T) {
_, svc, rc := testData() _, svc, rc := testData()

View File

@ -256,6 +256,23 @@ func (b *Builder) SelectAllParam(selectAll bool) *Builder {
// When two or more arguments are received, they must be a single type and resource name(s). // When two or more arguments are received, they must be a single type and resource name(s).
// The allowEmptySelector permits to select all the resources (via Everything func). // The allowEmptySelector permits to select all the resources (via Everything func).
func (b *Builder) ResourceTypeOrNameArgs(allowEmptySelector bool, args ...string) *Builder { func (b *Builder) ResourceTypeOrNameArgs(allowEmptySelector bool, args ...string) *Builder {
// convert multiple resources to resource tuples, a,b,c d as a transform to a/d b/d c/d
if len(args) >= 2 {
resources := []string{}
resources = append(resources, SplitResourceArgument(args[0])...)
if len(resources) > 1 {
names := []string{}
names = append(names, args[1:]...)
newArgs := []string{}
for _, resource := range resources {
for _, name := range names {
newArgs = append(newArgs, strings.Join([]string{resource, name}, "/"))
}
}
args = newArgs
}
}
if ok, err := hasCombinedTypeArgs(args); ok { if ok, err := hasCombinedTypeArgs(args); ok {
if err != nil { if err != nil {
b.errs = append(b.errs, err) b.errs = append(b.errs, err)

View File

@ -380,6 +380,38 @@ func TestResourceByName(t *testing.T) {
} }
} }
func TestMultipleResourceByTheSameName(t *testing.T) {
pods, svcs := testData()
b := NewBuilder(latest.RESTMapper, api.Scheme, fakeClientWith("", t, map[string]string{
"/namespaces/test/pods/foo": runtime.EncodeOrDie(latest.Codec, &pods.Items[0]),
"/namespaces/test/pods/baz": runtime.EncodeOrDie(latest.Codec, &pods.Items[1]),
"/namespaces/test/services/foo": runtime.EncodeOrDie(latest.Codec, &svcs.Items[0]),
"/namespaces/test/services/baz": runtime.EncodeOrDie(latest.Codec, &svcs.Items[0]),
})).
NamespaceParam("test")
test := &testVisitor{}
singular := false
if b.Do().Err() == nil {
t.Errorf("unexpected non-error")
}
b.ResourceTypeOrNameArgs(true, "pods,services", "foo", "baz")
err := b.Do().IntoSingular(&singular).Visit(test.Handle)
if err != nil || singular || len(test.Infos) != 4 {
t.Fatalf("unexpected response: %v %t %#v", err, singular, test.Infos)
}
if !api.Semantic.DeepDerivative([]runtime.Object{&pods.Items[0], &pods.Items[1], &svcs.Items[0], &svcs.Items[0]}, test.Objects()) {
t.Errorf("unexpected visited objects: %#v", test.Objects())
}
if _, err := b.Do().ResourceMapping(); err == nil {
t.Errorf("unexpected non-error")
}
}
func TestResourceByNameWithoutRequireObject(t *testing.T) { func TestResourceByNameWithoutRequireObject(t *testing.T) {
b := NewBuilder(latest.RESTMapper, api.Scheme, fakeClientWith("", t, map[string]string{})). b := NewBuilder(latest.RESTMapper, api.Scheme, fakeClientWith("", t, map[string]string{})).
NamespaceParam("test") NamespaceParam("test")