diff --git a/pkg/kubectl/cmd/wait/wait.go b/pkg/kubectl/cmd/wait/wait.go index 1fd91f2ca9..4dd46fc5dd 100644 --- a/pkg/kubectl/cmd/wait/wait.go +++ b/pkg/kubectl/cmd/wait/wait.go @@ -90,6 +90,8 @@ func NewWaitFlags(restClientGetter genericclioptions.RESTClientGetter, streams g PrintFlags: genericclioptions.NewPrintFlags("condition met"), ResourceBuilderFlags: genericclioptions.NewResourceBuilderFlags(). WithLabelSelector(""). + WithFieldSelector(""). + WithAll(false). WithAllNamespaces(false). WithAll(false). WithLatest(), @@ -105,11 +107,12 @@ func NewCmdWait(restClientGetter genericclioptions.RESTClientGetter, streams gen flags := NewWaitFlags(restClientGetter, streams) cmd := &cobra.Command{ - Use: "wait resource.group/name [--for=delete|--for condition=available]", + Use: "wait ([-f FILENAME] | resource.group/resource.name | resource.group [(-l label | --all)]) [--for=delete|--for condition=available]", + Short: "Experimental: Wait for a specific condition on one or many resources.", + Long: waitLong, + Example: waitExample, + DisableFlagsInUseLine: true, - Short: "Experimental: Wait for a specific condition on one or many resources.", - Long: waitLong, - Example: waitExample, Run: func(cmd *cobra.Command, args []string) { o, err := flags.ToOptions(args) cmdutil.CheckErr(err) diff --git a/pkg/kubectl/cmd/wait/wait_test.go b/pkg/kubectl/cmd/wait/wait_test.go index 3f14f9d537..92d224b3a5 100644 --- a/pkg/kubectl/cmd/wait/wait_test.go +++ b/pkg/kubectl/cmd/wait/wait_test.go @@ -310,6 +310,58 @@ func TestWaitForDeletion(t *testing.T) { } }, }, + { + name: "handles watch delete multiple", + infos: []*resource.Info{ + { + Mapping: &meta.RESTMapping{ + Resource: schema.GroupVersionResource{Group: "group", Version: "version", Resource: "theresource-1"}, + }, + Name: "name-foo-1", + Namespace: "ns-foo", + }, + { + Mapping: &meta.RESTMapping{ + Resource: schema.GroupVersionResource{Group: "group", Version: "version", Resource: "theresource-2"}, + }, + Name: "name-foo-2", + Namespace: "ns-foo", + }, + }, + fakeClient: func() *dynamicfakeclient.FakeDynamicClient { + fakeClient := dynamicfakeclient.NewSimpleDynamicClient(scheme) + fakeClient.PrependReactor("get", "theresource-1", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) { + return true, newUnstructured("group/version", "TheKind", "ns-foo", "name-foo-1"), nil + }) + fakeClient.PrependReactor("get", "theresource-2", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) { + return true, newUnstructured("group/version", "TheKind", "ns-foo", "name-foo-2"), nil + }) + fakeClient.PrependWatchReactor("theresource-1", func(action clienttesting.Action) (handled bool, ret watch.Interface, err error) { + fakeWatch := watch.NewRaceFreeFake() + fakeWatch.Action(watch.Deleted, newUnstructured("group/version", "TheKind", "ns-foo", "name-foo-1")) + return true, fakeWatch, nil + }) + fakeClient.PrependWatchReactor("theresource-2", func(action clienttesting.Action) (handled bool, ret watch.Interface, err error) { + fakeWatch := watch.NewRaceFreeFake() + fakeWatch.Action(watch.Deleted, newUnstructured("group/version", "TheKind", "ns-foo", "name-foo-2")) + return true, fakeWatch, nil + }) + return fakeClient + }, + timeout: 10 * time.Second, + + validateActions: func(t *testing.T, actions []clienttesting.Action) { + if len(actions) != 2 { + t.Fatal(spew.Sdump(actions)) + } + if !actions[0].Matches("list", "theresource-1") { + t.Error(spew.Sdump(actions)) + } + if !actions[1].Matches("list", "theresource-2") { + t.Error(spew.Sdump(actions)) + } + }, + }, { name: "ignores watch error", infos: []*resource.Info{ diff --git a/test/cmd/legacy-script.sh b/test/cmd/legacy-script.sh index ff32d9f208..73eef56e29 100755 --- a/test/cmd/legacy-script.sh +++ b/test/cmd/legacy-script.sh @@ -51,6 +51,7 @@ source "${KUBE_ROOT}/test/cmd/save-config.sh" source "${KUBE_ROOT}/test/cmd/storage.sh" source "${KUBE_ROOT}/test/cmd/template-output.sh" source "${KUBE_ROOT}/test/cmd/version.sh" +source "${KUBE_ROOT}/test/cmd/wait.sh" ETCD_HOST=${ETCD_HOST:-127.0.0.1} @@ -832,6 +833,12 @@ runTests() { ################# record_command run_impersonation_tests + #################### + # kubectl wait # + #################### + + record_command run_wait_tests + kube::test::clear_all if [[ -n "${foundError}" ]]; then diff --git a/test/cmd/wait.sh b/test/cmd/wait.sh new file mode 100644 index 0000000000..73d4603fe2 --- /dev/null +++ b/test/cmd/wait.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash + +# Copyright 2018 The Kubernetes Authors. +# +# 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. + +set -o errexit +set -o nounset +set -o pipefail + +run_wait_tests() { + set -o nounset + set -o errexit + + kube::log::status "Testing kubectl wait" + + create_and_use_new_namespace + + ### Wait for deletion using --all flag + + # create test data + kubectl create deployment test-1 --image=busybox + kubectl create deployment test-2 --image=busybox + + # Post-Condition: deployments exists + kube::test::get_object_assert "deployments" "{{range .items}}{{.metadata.name}},{{end}}" 'test-1,test-2,' + + # Delete all deployments async to kubectl wait + ( sleep 2 && kubectl delete deployment --all ) & + + # Command: Wait for all deployments to be deleted + output_message=$(kubectl wait deployment --for=delete --all) + + # Post-Condition: Wait was successful + kube::test::if_has_string "${output_message}" 'test-1 condition met' + kube::test::if_has_string "${output_message}" 'test-2 condition met' + + set +o nounset + set +o errexit +} \ No newline at end of file