| /* |
| 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. |
| */ |
| |
| package wait |
| |
| import ( |
| "io/ioutil" |
| "testing" |
| |
| "time" |
| |
| "strings" |
| |
| "github.com/davecgh/go-spew/spew" |
| |
| "k8s.io/apimachinery/pkg/api/meta" |
| metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
| "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" |
| "k8s.io/apimachinery/pkg/runtime" |
| "k8s.io/apimachinery/pkg/runtime/schema" |
| "k8s.io/apimachinery/pkg/types" |
| "k8s.io/apimachinery/pkg/util/wait" |
| "k8s.io/apimachinery/pkg/watch" |
| "k8s.io/cli-runtime/pkg/genericclioptions" |
| "k8s.io/cli-runtime/pkg/genericclioptions/printers" |
| "k8s.io/cli-runtime/pkg/genericclioptions/resource" |
| dynamicfakeclient "k8s.io/client-go/dynamic/fake" |
| clienttesting "k8s.io/client-go/testing" |
| ) |
| |
| func newUnstructured(apiVersion, kind, namespace, name string) *unstructured.Unstructured { |
| return &unstructured.Unstructured{ |
| Object: map[string]interface{}{ |
| "apiVersion": apiVersion, |
| "kind": kind, |
| "metadata": map[string]interface{}{ |
| "namespace": namespace, |
| "name": name, |
| "uid": "some-UID-value", |
| }, |
| }, |
| } |
| } |
| |
| func newUnstructuredStatus(status *metav1.Status) runtime.Unstructured { |
| obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(status) |
| if err != nil { |
| panic(err) |
| } |
| return &unstructured.Unstructured{ |
| Object: obj, |
| } |
| } |
| |
| func addCondition(in *unstructured.Unstructured, name, status string) *unstructured.Unstructured { |
| conditions, _, _ := unstructured.NestedSlice(in.Object, "status", "conditions") |
| conditions = append(conditions, map[string]interface{}{ |
| "type": name, |
| "status": status, |
| }) |
| unstructured.SetNestedSlice(in.Object, conditions, "status", "conditions") |
| return in |
| } |
| |
| func TestWaitForDeletion(t *testing.T) { |
| scheme := runtime.NewScheme() |
| |
| tests := []struct { |
| name string |
| infos []*resource.Info |
| fakeClient func() *dynamicfakeclient.FakeDynamicClient |
| timeout time.Duration |
| uidMap UIDMap |
| |
| expectedErr string |
| validateActions func(t *testing.T, actions []clienttesting.Action) |
| }{ |
| { |
| name: "missing on get", |
| infos: []*resource.Info{ |
| { |
| Mapping: &meta.RESTMapping{ |
| Resource: schema.GroupVersionResource{Group: "group", Version: "version", Resource: "theresource"}, |
| }, |
| Name: "name-foo", |
| Namespace: "ns-foo", |
| }, |
| }, |
| fakeClient: func() *dynamicfakeclient.FakeDynamicClient { |
| return dynamicfakeclient.NewSimpleDynamicClient(scheme) |
| }, |
| timeout: 10 * time.Second, |
| |
| validateActions: func(t *testing.T, actions []clienttesting.Action) { |
| if len(actions) != 1 { |
| t.Fatal(spew.Sdump(actions)) |
| } |
| if !actions[0].Matches("get", "theresource") || actions[0].(clienttesting.GetAction).GetName() != "name-foo" { |
| t.Error(spew.Sdump(actions)) |
| } |
| }, |
| }, |
| { |
| name: "handles no infos", |
| infos: []*resource.Info{}, |
| fakeClient: func() *dynamicfakeclient.FakeDynamicClient { |
| return dynamicfakeclient.NewSimpleDynamicClient(scheme) |
| }, |
| timeout: 10 * time.Second, |
| expectedErr: errNoMatchingResources.Error(), |
| |
| validateActions: func(t *testing.T, actions []clienttesting.Action) { |
| if len(actions) != 0 { |
| t.Fatal(spew.Sdump(actions)) |
| } |
| }, |
| }, |
| { |
| name: "uid conflict on get", |
| infos: []*resource.Info{ |
| { |
| Mapping: &meta.RESTMapping{ |
| Resource: schema.GroupVersionResource{Group: "group", Version: "version", Resource: "theresource"}, |
| }, |
| Name: "name-foo", |
| Namespace: "ns-foo", |
| }, |
| }, |
| fakeClient: func() *dynamicfakeclient.FakeDynamicClient { |
| fakeClient := dynamicfakeclient.NewSimpleDynamicClient(scheme) |
| fakeClient.PrependReactor("get", "theresource", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) { |
| return true, newUnstructured("group/version", "TheKind", "ns-foo", "name-foo"), nil |
| }) |
| count := 0 |
| fakeClient.PrependWatchReactor("theresource", func(action clienttesting.Action) (handled bool, ret watch.Interface, err error) { |
| if count == 0 { |
| count++ |
| fakeWatch := watch.NewRaceFreeFake() |
| go func() { |
| time.Sleep(100 * time.Millisecond) |
| fakeWatch.Stop() |
| }() |
| return true, fakeWatch, nil |
| } |
| fakeWatch := watch.NewRaceFreeFake() |
| return true, fakeWatch, nil |
| }) |
| return fakeClient |
| }, |
| timeout: 10 * time.Second, |
| uidMap: UIDMap{ |
| ResourceLocation{Namespace: "ns-foo", Name: "name-foo"}: types.UID("some-UID-value"), |
| ResourceLocation{GroupResource: schema.GroupResource{Group: "group", Resource: "theresource"}, Namespace: "ns-foo", Name: "name-foo"}: types.UID("some-nonmatching-UID-value"), |
| }, |
| |
| validateActions: func(t *testing.T, actions []clienttesting.Action) { |
| if len(actions) != 1 { |
| t.Fatal(spew.Sdump(actions)) |
| } |
| if !actions[0].Matches("get", "theresource") || actions[0].(clienttesting.GetAction).GetName() != "name-foo" { |
| t.Error(spew.Sdump(actions)) |
| } |
| }, |
| }, |
| { |
| name: "times out", |
| infos: []*resource.Info{ |
| { |
| Mapping: &meta.RESTMapping{ |
| Resource: schema.GroupVersionResource{Group: "group", Version: "version", Resource: "theresource"}, |
| }, |
| Name: "name-foo", |
| Namespace: "ns-foo", |
| }, |
| }, |
| fakeClient: func() *dynamicfakeclient.FakeDynamicClient { |
| fakeClient := dynamicfakeclient.NewSimpleDynamicClient(scheme) |
| fakeClient.PrependReactor("get", "theresource", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) { |
| return true, newUnstructured("group/version", "TheKind", "ns-foo", "name-foo"), nil |
| }) |
| return fakeClient |
| }, |
| timeout: 1 * time.Second, |
| |
| expectedErr: wait.ErrWaitTimeout.Error(), |
| validateActions: func(t *testing.T, actions []clienttesting.Action) { |
| if len(actions) != 2 { |
| t.Fatal(spew.Sdump(actions)) |
| } |
| if !actions[0].Matches("get", "theresource") || actions[0].(clienttesting.GetAction).GetName() != "name-foo" { |
| t.Error(spew.Sdump(actions)) |
| } |
| if !actions[1].Matches("watch", "theresource") { |
| t.Error(spew.Sdump(actions)) |
| } |
| }, |
| }, |
| { |
| name: "handles watch close out", |
| infos: []*resource.Info{ |
| { |
| Mapping: &meta.RESTMapping{ |
| Resource: schema.GroupVersionResource{Group: "group", Version: "version", Resource: "theresource"}, |
| }, |
| Name: "name-foo", |
| Namespace: "ns-foo", |
| }, |
| }, |
| fakeClient: func() *dynamicfakeclient.FakeDynamicClient { |
| fakeClient := dynamicfakeclient.NewSimpleDynamicClient(scheme) |
| fakeClient.PrependReactor("get", "theresource", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) { |
| return true, newUnstructured("group/version", "TheKind", "ns-foo", "name-foo"), nil |
| }) |
| count := 0 |
| fakeClient.PrependWatchReactor("theresource", func(action clienttesting.Action) (handled bool, ret watch.Interface, err error) { |
| if count == 0 { |
| count++ |
| fakeWatch := watch.NewRaceFreeFake() |
| go func() { |
| time.Sleep(100 * time.Millisecond) |
| fakeWatch.Stop() |
| }() |
| return true, fakeWatch, nil |
| } |
| fakeWatch := watch.NewRaceFreeFake() |
| return true, fakeWatch, nil |
| }) |
| return fakeClient |
| }, |
| timeout: 3 * time.Second, |
| |
| expectedErr: wait.ErrWaitTimeout.Error(), |
| validateActions: func(t *testing.T, actions []clienttesting.Action) { |
| if len(actions) != 4 { |
| t.Fatal(spew.Sdump(actions)) |
| } |
| if !actions[0].Matches("get", "theresource") || actions[0].(clienttesting.GetAction).GetName() != "name-foo" { |
| t.Error(spew.Sdump(actions)) |
| } |
| if !actions[1].Matches("watch", "theresource") { |
| t.Error(spew.Sdump(actions)) |
| } |
| if !actions[2].Matches("get", "theresource") || actions[2].(clienttesting.GetAction).GetName() != "name-foo" { |
| t.Error(spew.Sdump(actions)) |
| } |
| if !actions[3].Matches("watch", "theresource") { |
| t.Error(spew.Sdump(actions)) |
| } |
| }, |
| }, |
| { |
| name: "handles watch delete", |
| infos: []*resource.Info{ |
| { |
| Mapping: &meta.RESTMapping{ |
| Resource: schema.GroupVersionResource{Group: "group", Version: "version", Resource: "theresource"}, |
| }, |
| Name: "name-foo", |
| Namespace: "ns-foo", |
| }, |
| }, |
| fakeClient: func() *dynamicfakeclient.FakeDynamicClient { |
| fakeClient := dynamicfakeclient.NewSimpleDynamicClient(scheme) |
| fakeClient.PrependReactor("get", "theresource", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) { |
| return true, newUnstructured("group/version", "TheKind", "ns-foo", "name-foo"), nil |
| }) |
| fakeClient.PrependWatchReactor("theresource", 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")) |
| 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("get", "theresource") || actions[0].(clienttesting.GetAction).GetName() != "name-foo" { |
| t.Error(spew.Sdump(actions)) |
| } |
| if !actions[1].Matches("watch", "theresource") { |
| t.Error(spew.Sdump(actions)) |
| } |
| }, |
| }, |
| { |
| name: "ignores watch error", |
| infos: []*resource.Info{ |
| { |
| Mapping: &meta.RESTMapping{ |
| Resource: schema.GroupVersionResource{Group: "group", Version: "version", Resource: "theresource"}, |
| }, |
| Name: "name-foo", |
| Namespace: "ns-foo", |
| }, |
| }, |
| fakeClient: func() *dynamicfakeclient.FakeDynamicClient { |
| fakeClient := dynamicfakeclient.NewSimpleDynamicClient(scheme) |
| fakeClient.PrependReactor("get", "theresource", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) { |
| return true, newUnstructured("group/version", "TheKind", "ns-foo", "name-foo"), nil |
| }) |
| count := 0 |
| fakeClient.PrependWatchReactor("theresource", func(action clienttesting.Action) (handled bool, ret watch.Interface, err error) { |
| fakeWatch := watch.NewRaceFreeFake() |
| if count == 0 { |
| fakeWatch.Error(newUnstructuredStatus(&metav1.Status{ |
| TypeMeta: metav1.TypeMeta{Kind: "Status", APIVersion: "v1"}, |
| Status: "Failure", |
| Code: 500, |
| Message: "Bad", |
| })) |
| fakeWatch.Stop() |
| } else { |
| fakeWatch.Action(watch.Deleted, newUnstructured("group/version", "TheKind", "ns-foo", "name-foo")) |
| } |
| count++ |
| return true, fakeWatch, nil |
| }) |
| return fakeClient |
| }, |
| timeout: 10 * time.Second, |
| |
| validateActions: func(t *testing.T, actions []clienttesting.Action) { |
| if len(actions) != 4 { |
| t.Fatal(spew.Sdump(actions)) |
| } |
| if !actions[0].Matches("get", "theresource") || actions[0].(clienttesting.GetAction).GetName() != "name-foo" { |
| t.Error(spew.Sdump(actions)) |
| } |
| if !actions[1].Matches("watch", "theresource") { |
| t.Error(spew.Sdump(actions)) |
| } |
| if !actions[2].Matches("get", "theresource") || actions[2].(clienttesting.GetAction).GetName() != "name-foo" { |
| t.Error(spew.Sdump(actions)) |
| } |
| if !actions[3].Matches("watch", "theresource") { |
| t.Error(spew.Sdump(actions)) |
| } |
| }, |
| }, |
| } |
| |
| for _, test := range tests { |
| t.Run(test.name, func(t *testing.T) { |
| fakeClient := test.fakeClient() |
| o := &WaitOptions{ |
| ResourceFinder: genericclioptions.NewSimpleFakeResourceFinder(test.infos...), |
| UIDMap: test.uidMap, |
| DynamicClient: fakeClient, |
| Timeout: test.timeout, |
| |
| Printer: printers.NewDiscardingPrinter(), |
| ConditionFn: IsDeleted, |
| IOStreams: genericclioptions.NewTestIOStreamsDiscard(), |
| } |
| err := o.RunWait() |
| switch { |
| case err == nil && len(test.expectedErr) == 0: |
| case err != nil && len(test.expectedErr) == 0: |
| t.Fatal(err) |
| case err == nil && len(test.expectedErr) != 0: |
| t.Fatalf("missing: %q", test.expectedErr) |
| case err != nil && len(test.expectedErr) != 0: |
| if !strings.Contains(err.Error(), test.expectedErr) { |
| t.Fatalf("expected %q, got %q", test.expectedErr, err.Error()) |
| } |
| } |
| |
| test.validateActions(t, fakeClient.Actions()) |
| }) |
| } |
| } |
| |
| func TestWaitForCondition(t *testing.T) { |
| scheme := runtime.NewScheme() |
| |
| tests := []struct { |
| name string |
| infos []*resource.Info |
| fakeClient func() *dynamicfakeclient.FakeDynamicClient |
| timeout time.Duration |
| |
| expectedErr string |
| validateActions func(t *testing.T, actions []clienttesting.Action) |
| }{ |
| { |
| name: "present on get", |
| infos: []*resource.Info{ |
| { |
| Mapping: &meta.RESTMapping{ |
| Resource: schema.GroupVersionResource{Group: "group", Version: "version", Resource: "theresource"}, |
| }, |
| Name: "name-foo", |
| Namespace: "ns-foo", |
| }, |
| }, |
| fakeClient: func() *dynamicfakeclient.FakeDynamicClient { |
| fakeClient := dynamicfakeclient.NewSimpleDynamicClient(scheme) |
| fakeClient.PrependReactor("get", "theresource", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) { |
| return true, addCondition( |
| newUnstructured("group/version", "TheKind", "ns-foo", "name-foo"), |
| "the-condition", "status-value", |
| ), nil |
| }) |
| return fakeClient |
| }, |
| timeout: 10 * time.Second, |
| |
| validateActions: func(t *testing.T, actions []clienttesting.Action) { |
| if len(actions) != 1 { |
| t.Fatal(spew.Sdump(actions)) |
| } |
| if !actions[0].Matches("get", "theresource") || actions[0].(clienttesting.GetAction).GetName() != "name-foo" { |
| t.Error(spew.Sdump(actions)) |
| } |
| }, |
| }, |
| { |
| name: "handles no infos", |
| infos: []*resource.Info{}, |
| fakeClient: func() *dynamicfakeclient.FakeDynamicClient { |
| return dynamicfakeclient.NewSimpleDynamicClient(scheme) |
| }, |
| timeout: 10 * time.Second, |
| expectedErr: errNoMatchingResources.Error(), |
| |
| validateActions: func(t *testing.T, actions []clienttesting.Action) { |
| if len(actions) != 0 { |
| t.Fatal(spew.Sdump(actions)) |
| } |
| }, |
| }, |
| { |
| name: "handles empty object name", |
| infos: []*resource.Info{ |
| { |
| Mapping: &meta.RESTMapping{ |
| Resource: schema.GroupVersionResource{Group: "group", Version: "version", Resource: "theresource"}, |
| }, |
| Namespace: "ns-foo", |
| }, |
| }, |
| fakeClient: func() *dynamicfakeclient.FakeDynamicClient { |
| return dynamicfakeclient.NewSimpleDynamicClient(scheme) |
| }, |
| timeout: 10 * time.Second, |
| expectedErr: "resource name must be provided", |
| |
| validateActions: func(t *testing.T, actions []clienttesting.Action) { |
| if len(actions) != 0 { |
| t.Fatal(spew.Sdump(actions)) |
| } |
| }, |
| }, |
| { |
| name: "times out", |
| infos: []*resource.Info{ |
| { |
| Mapping: &meta.RESTMapping{ |
| Resource: schema.GroupVersionResource{Group: "group", Version: "version", Resource: "theresource"}, |
| }, |
| Name: "name-foo", |
| Namespace: "ns-foo", |
| }, |
| }, |
| fakeClient: func() *dynamicfakeclient.FakeDynamicClient { |
| fakeClient := dynamicfakeclient.NewSimpleDynamicClient(scheme) |
| fakeClient.PrependReactor("get", "theresource", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) { |
| return true, addCondition( |
| newUnstructured("group/version", "TheKind", "ns-foo", "name-foo"), |
| "some-other-condition", "status-value", |
| ), nil |
| }) |
| return fakeClient |
| }, |
| timeout: 1 * time.Second, |
| |
| expectedErr: wait.ErrWaitTimeout.Error(), |
| validateActions: func(t *testing.T, actions []clienttesting.Action) { |
| if len(actions) != 2 { |
| t.Fatal(spew.Sdump(actions)) |
| } |
| if !actions[0].Matches("get", "theresource") || actions[0].(clienttesting.GetAction).GetName() != "name-foo" { |
| t.Error(spew.Sdump(actions)) |
| } |
| if !actions[1].Matches("watch", "theresource") { |
| t.Error(spew.Sdump(actions)) |
| } |
| }, |
| }, |
| { |
| name: "handles watch close out", |
| infos: []*resource.Info{ |
| { |
| Mapping: &meta.RESTMapping{ |
| Resource: schema.GroupVersionResource{Group: "group", Version: "version", Resource: "theresource"}, |
| }, |
| Name: "name-foo", |
| Namespace: "ns-foo", |
| }, |
| }, |
| fakeClient: func() *dynamicfakeclient.FakeDynamicClient { |
| fakeClient := dynamicfakeclient.NewSimpleDynamicClient(scheme) |
| fakeClient.PrependReactor("get", "theresource", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) { |
| return true, newUnstructured("group/version", "TheKind", "ns-foo", "name-foo"), nil |
| }) |
| count := 0 |
| fakeClient.PrependWatchReactor("theresource", func(action clienttesting.Action) (handled bool, ret watch.Interface, err error) { |
| if count == 0 { |
| count++ |
| fakeWatch := watch.NewRaceFreeFake() |
| go func() { |
| time.Sleep(100 * time.Millisecond) |
| fakeWatch.Stop() |
| }() |
| return true, fakeWatch, nil |
| } |
| fakeWatch := watch.NewRaceFreeFake() |
| return true, fakeWatch, nil |
| }) |
| return fakeClient |
| }, |
| timeout: 3 * time.Second, |
| |
| expectedErr: wait.ErrWaitTimeout.Error(), |
| validateActions: func(t *testing.T, actions []clienttesting.Action) { |
| if len(actions) != 4 { |
| t.Fatal(spew.Sdump(actions)) |
| } |
| if !actions[0].Matches("get", "theresource") || actions[0].(clienttesting.GetAction).GetName() != "name-foo" { |
| t.Error(spew.Sdump(actions)) |
| } |
| if !actions[1].Matches("watch", "theresource") { |
| t.Error(spew.Sdump(actions)) |
| } |
| if !actions[2].Matches("get", "theresource") || actions[2].(clienttesting.GetAction).GetName() != "name-foo" { |
| t.Error(spew.Sdump(actions)) |
| } |
| if !actions[3].Matches("watch", "theresource") { |
| t.Error(spew.Sdump(actions)) |
| } |
| }, |
| }, |
| { |
| name: "handles watch condition change", |
| infos: []*resource.Info{ |
| { |
| Mapping: &meta.RESTMapping{ |
| Resource: schema.GroupVersionResource{Group: "group", Version: "version", Resource: "theresource"}, |
| }, |
| Name: "name-foo", |
| Namespace: "ns-foo", |
| }, |
| }, |
| fakeClient: func() *dynamicfakeclient.FakeDynamicClient { |
| fakeClient := dynamicfakeclient.NewSimpleDynamicClient(scheme) |
| fakeClient.PrependReactor("get", "theresource", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) { |
| return true, newUnstructured("group/version", "TheKind", "ns-foo", "name-foo"), nil |
| }) |
| fakeClient.PrependWatchReactor("theresource", func(action clienttesting.Action) (handled bool, ret watch.Interface, err error) { |
| fakeWatch := watch.NewRaceFreeFake() |
| fakeWatch.Action(watch.Modified, addCondition( |
| newUnstructured("group/version", "TheKind", "ns-foo", "name-foo"), |
| "the-condition", "status-value", |
| )) |
| 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("get", "theresource") || actions[0].(clienttesting.GetAction).GetName() != "name-foo" { |
| t.Error(spew.Sdump(actions)) |
| } |
| if !actions[1].Matches("watch", "theresource") { |
| t.Error(spew.Sdump(actions)) |
| } |
| }, |
| }, |
| { |
| name: "handles watch created", |
| infos: []*resource.Info{ |
| { |
| Mapping: &meta.RESTMapping{ |
| Resource: schema.GroupVersionResource{Group: "group", Version: "version", Resource: "theresource"}, |
| }, |
| Name: "name-foo", |
| Namespace: "ns-foo", |
| }, |
| }, |
| fakeClient: func() *dynamicfakeclient.FakeDynamicClient { |
| fakeClient := dynamicfakeclient.NewSimpleDynamicClient(scheme) |
| fakeClient.PrependWatchReactor("theresource", func(action clienttesting.Action) (handled bool, ret watch.Interface, err error) { |
| fakeWatch := watch.NewRaceFreeFake() |
| fakeWatch.Action(watch.Added, addCondition( |
| newUnstructured("group/version", "TheKind", "ns-foo", "name-foo"), |
| "the-condition", "status-value", |
| )) |
| 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("get", "theresource") || actions[0].(clienttesting.GetAction).GetName() != "name-foo" { |
| t.Error(spew.Sdump(actions)) |
| } |
| if !actions[1].Matches("watch", "theresource") { |
| t.Error(spew.Sdump(actions)) |
| } |
| }, |
| }, |
| { |
| name: "ignores watch error", |
| infos: []*resource.Info{ |
| { |
| Mapping: &meta.RESTMapping{ |
| Resource: schema.GroupVersionResource{Group: "group", Version: "version", Resource: "theresource"}, |
| }, |
| Name: "name-foo", |
| Namespace: "ns-foo", |
| }, |
| }, |
| fakeClient: func() *dynamicfakeclient.FakeDynamicClient { |
| fakeClient := dynamicfakeclient.NewSimpleDynamicClient(scheme) |
| fakeClient.PrependReactor("get", "theresource", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) { |
| return true, newUnstructured("group/version", "TheKind", "ns-foo", "name-foo"), nil |
| }) |
| count := 0 |
| fakeClient.PrependWatchReactor("theresource", func(action clienttesting.Action) (handled bool, ret watch.Interface, err error) { |
| fakeWatch := watch.NewRaceFreeFake() |
| if count == 0 { |
| fakeWatch.Error(newUnstructuredStatus(&metav1.Status{ |
| TypeMeta: metav1.TypeMeta{Kind: "Status", APIVersion: "v1"}, |
| Status: "Failure", |
| Code: 500, |
| Message: "Bad", |
| })) |
| fakeWatch.Stop() |
| } else { |
| fakeWatch.Action(watch.Modified, addCondition( |
| newUnstructured("group/version", "TheKind", "ns-foo", "name-foo"), |
| "the-condition", "status-value", |
| )) |
| } |
| count++ |
| return true, fakeWatch, nil |
| }) |
| return fakeClient |
| }, |
| timeout: 10 * time.Second, |
| |
| validateActions: func(t *testing.T, actions []clienttesting.Action) { |
| if len(actions) != 4 { |
| t.Fatal(spew.Sdump(actions)) |
| } |
| if !actions[0].Matches("get", "theresource") || actions[0].(clienttesting.GetAction).GetName() != "name-foo" { |
| t.Error(spew.Sdump(actions)) |
| } |
| if !actions[1].Matches("watch", "theresource") { |
| t.Error(spew.Sdump(actions)) |
| } |
| if !actions[2].Matches("get", "theresource") || actions[2].(clienttesting.GetAction).GetName() != "name-foo" { |
| t.Error(spew.Sdump(actions)) |
| } |
| if !actions[3].Matches("watch", "theresource") { |
| t.Error(spew.Sdump(actions)) |
| } |
| }, |
| }, |
| } |
| |
| for _, test := range tests { |
| t.Run(test.name, func(t *testing.T) { |
| fakeClient := test.fakeClient() |
| o := &WaitOptions{ |
| ResourceFinder: genericclioptions.NewSimpleFakeResourceFinder(test.infos...), |
| DynamicClient: fakeClient, |
| Timeout: test.timeout, |
| |
| Printer: printers.NewDiscardingPrinter(), |
| ConditionFn: ConditionalWait{conditionName: "the-condition", conditionStatus: "status-value", errOut: ioutil.Discard}.IsConditionMet, |
| IOStreams: genericclioptions.NewTestIOStreamsDiscard(), |
| } |
| err := o.RunWait() |
| switch { |
| case err == nil && len(test.expectedErr) == 0: |
| case err != nil && len(test.expectedErr) == 0: |
| t.Fatal(err) |
| case err == nil && len(test.expectedErr) != 0: |
| t.Fatalf("missing: %q", test.expectedErr) |
| case err != nil && len(test.expectedErr) != 0: |
| if !strings.Contains(err.Error(), test.expectedErr) { |
| t.Fatalf("expected %q, got %q", test.expectedErr, err.Error()) |
| } |
| } |
| |
| test.validateActions(t, fakeClient.Actions()) |
| }) |
| } |
| } |