Revert "Move kubectl wait to informers with a cache to avoid hanging due to objects disappearing from the cluster (#108086)"

This reverts commit 171431ca2c.
This commit is contained in:
Marcus Puckett 2022-07-01 13:45:18 -07:00 committed by GitHub
parent be5d7aa60f
commit a6bd1e6ac9
2 changed files with 175 additions and 193 deletions

View File

@ -40,13 +40,11 @@ import (
"k8s.io/cli-runtime/pkg/printers" "k8s.io/cli-runtime/pkg/printers"
"k8s.io/cli-runtime/pkg/resource" "k8s.io/cli-runtime/pkg/resource"
"k8s.io/client-go/dynamic" "k8s.io/client-go/dynamic"
"k8s.io/client-go/tools/cache"
watchtools "k8s.io/client-go/tools/watch" watchtools "k8s.io/client-go/tools/watch"
"k8s.io/client-go/util/jsonpath" "k8s.io/client-go/util/jsonpath"
cmdget "k8s.io/kubectl/pkg/cmd/get" cmdget "k8s.io/kubectl/pkg/cmd/get"
cmdutil "k8s.io/kubectl/pkg/cmd/util" cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/interrupt"
"k8s.io/kubectl/pkg/util/templates" "k8s.io/kubectl/pkg/util/templates"
) )
@ -319,18 +317,27 @@ func (o *WaitOptions) RunWait() error {
// IsDeleted is a condition func for waiting for something to be deleted // IsDeleted is a condition func for waiting for something to be deleted
func IsDeleted(info *resource.Info, o *WaitOptions) (runtime.Object, bool, error) { func IsDeleted(info *resource.Info, o *WaitOptions) (runtime.Object, bool, error) {
endTime := time.Now().Add(o.Timeout)
for {
if len(info.Name) == 0 { if len(info.Name) == 0 {
return info.Object, false, fmt.Errorf("resource name must be provided") return info.Object, false, fmt.Errorf("resource name must be provided")
} }
gottenObj, initObjGetErr := o.DynamicClient.Resource(info.Mapping.Resource).Namespace(info.Namespace).Get(context.Background(), info.Name, metav1.GetOptions{}) nameSelector := fields.OneTermEqualSelector("metadata.name", info.Name).String()
if apierrors.IsNotFound(initObjGetErr) {
// List with a name field selector to get the current resourceVersion to watch from (not the object's resourceVersion)
gottenObjList, err := o.DynamicClient.Resource(info.Mapping.Resource).Namespace(info.Namespace).List(context.TODO(), metav1.ListOptions{FieldSelector: nameSelector})
if apierrors.IsNotFound(err) {
return info.Object, true, nil return info.Object, true, nil
} }
if initObjGetErr != nil { if err != nil {
// TODO this could do something slightly fancier if we wish // TODO this could do something slightly fancier if we wish
return info.Object, false, initObjGetErr return info.Object, false, err
} }
if len(gottenObjList.Items) != 1 {
return info.Object, true, nil
}
gottenObj := &gottenObjList.Items[0]
resourceLocation := ResourceLocation{ resourceLocation := ResourceLocation{
GroupResource: info.Mapping.Resource.GroupResource(), GroupResource: info.Mapping.Resource.GroupResource(),
Namespace: gottenObj.GetNamespace(), Namespace: gottenObj.GetNamespace(),
@ -342,56 +349,38 @@ func IsDeleted(info *resource.Info, o *WaitOptions) (runtime.Object, bool, error
} }
} }
endTime := time.Now().Add(o.Timeout) watchOptions := metav1.ListOptions{}
timeout := time.Until(endTime) watchOptions.FieldSelector = nameSelector
errWaitTimeoutWithName := extendErrWaitTimeout(wait.ErrWaitTimeout, info) watchOptions.ResourceVersion = gottenObjList.GetResourceVersion()
if timeout < 0 { objWatch, err := o.DynamicClient.Resource(info.Mapping.Resource).Namespace(info.Namespace).Watch(context.TODO(), watchOptions)
// we're out of time
return info.Object, false, errWaitTimeoutWithName
}
ctx, cancel := watchtools.ContextWithOptionalTimeout(context.Background(), o.Timeout)
defer cancel()
fieldSelector := fields.OneTermEqualSelector("metadata.name", info.Name).String()
lw := &cache.ListWatch{
ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
options.FieldSelector = fieldSelector
return o.DynamicClient.Resource(info.Mapping.Resource).Namespace(info.Namespace).List(context.TODO(), options)
},
WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
options.FieldSelector = fieldSelector
return o.DynamicClient.Resource(info.Mapping.Resource).Namespace(info.Namespace).Watch(context.TODO(), options)
},
}
// this function is used to refresh the cache to prevent timeout waits on resources that have disappeared
preconditionFunc := func(store cache.Store) (bool, error) {
_, exists, err := store.Get(&metav1.ObjectMeta{Namespace: info.Namespace, Name: info.Name})
if err != nil { if err != nil {
return true, err
}
if !exists {
// since we're looking for it to disappear we just return here if it no longer exists
return true, nil
}
return false, nil
}
intr := interrupt.New(nil, cancel)
err := intr.Run(func() error {
_, err := watchtools.UntilWithSync(ctx, lw, &unstructured.Unstructured{}, preconditionFunc, Wait{errOut: o.ErrOut}.IsDeleted)
return err
})
if err != nil {
if err == wait.ErrWaitTimeout {
return gottenObj, false, errWaitTimeoutWithName
}
return gottenObj, false, err return gottenObj, false, err
} }
return gottenObj, true, nil timeout := endTime.Sub(time.Now())
errWaitTimeoutWithName := extendErrWaitTimeout(wait.ErrWaitTimeout, info)
if timeout < 0 {
// we're out of time
return gottenObj, false, errWaitTimeoutWithName
}
ctx, cancel := watchtools.ContextWithOptionalTimeout(context.Background(), o.Timeout)
watchEvent, err := watchtools.UntilWithoutRetry(ctx, objWatch, Wait{errOut: o.ErrOut}.IsDeleted)
cancel()
switch {
case err == nil:
return watchEvent.Object, true, nil
case err == watchtools.ErrWatchClosed:
continue
case err == wait.ErrWaitTimeout:
if watchEvent != nil {
return watchEvent.Object, false, errWaitTimeoutWithName
}
return gottenObj, false, errWaitTimeoutWithName
default:
return gottenObj, false, err
}
}
} }
// Wait has helper methods for handling watches, including error handling. // Wait has helper methods for handling watches, including error handling.
@ -421,67 +410,68 @@ type checkCondFunc func(obj *unstructured.Unstructured) (bool, error)
// getObjAndCheckCondition will make a List query to the API server to get the object and check if the condition is met using check function. // getObjAndCheckCondition will make a List query to the API server to get the object and check if the condition is met using check function.
// If the condition is not met, it will make a Watch query to the server and pass in the condMet function // If the condition is not met, it will make a Watch query to the server and pass in the condMet function
func getObjAndCheckCondition(info *resource.Info, o *WaitOptions, condMet isCondMetFunc, check checkCondFunc) (runtime.Object, bool, error) { func getObjAndCheckCondition(info *resource.Info, o *WaitOptions, condMet isCondMetFunc, check checkCondFunc) (runtime.Object, bool, error) {
endTime := time.Now().Add(o.Timeout)
for {
if len(info.Name) == 0 { if len(info.Name) == 0 {
return info.Object, false, fmt.Errorf("resource name must be provided") return info.Object, false, fmt.Errorf("resource name must be provided")
} }
endTime := time.Now().Add(o.Timeout) nameSelector := fields.OneTermEqualSelector("metadata.name", info.Name).String()
timeout := time.Until(endTime)
var gottenObj *unstructured.Unstructured
// List with a name field selector to get the current resourceVersion to watch from (not the object's resourceVersion)
gottenObjList, err := o.DynamicClient.Resource(info.Mapping.Resource).Namespace(info.Namespace).List(context.TODO(), metav1.ListOptions{FieldSelector: nameSelector})
resourceVersion := ""
switch {
case err != nil:
return info.Object, false, err
case len(gottenObjList.Items) != 1:
resourceVersion = gottenObjList.GetResourceVersion()
default:
gottenObj = &gottenObjList.Items[0]
conditionMet, err := check(gottenObj)
if conditionMet {
return gottenObj, true, nil
}
if err != nil {
return gottenObj, false, err
}
resourceVersion = gottenObjList.GetResourceVersion()
}
watchOptions := metav1.ListOptions{}
watchOptions.FieldSelector = nameSelector
watchOptions.ResourceVersion = resourceVersion
objWatch, err := o.DynamicClient.Resource(info.Mapping.Resource).Namespace(info.Namespace).Watch(context.TODO(), watchOptions)
if err != nil {
return gottenObj, false, err
}
timeout := endTime.Sub(time.Now())
errWaitTimeoutWithName := extendErrWaitTimeout(wait.ErrWaitTimeout, info) errWaitTimeoutWithName := extendErrWaitTimeout(wait.ErrWaitTimeout, info)
if timeout < 0 { if timeout < 0 {
// we're out of time // we're out of time
return info.Object, false, errWaitTimeoutWithName return gottenObj, false, errWaitTimeoutWithName
} }
ctx, cancel := watchtools.ContextWithOptionalTimeout(context.Background(), o.Timeout) ctx, cancel := watchtools.ContextWithOptionalTimeout(context.Background(), o.Timeout)
defer cancel() watchEvent, err := watchtools.UntilWithoutRetry(ctx, objWatch, watchtools.ConditionFunc(condMet))
cancel()
mapping := info.ResourceMapping() // used to pass back meaningful errors if object disappears switch {
fieldSelector := fields.OneTermEqualSelector("metadata.name", info.Name).String() case err == nil:
lw := &cache.ListWatch{ return watchEvent.Object, true, nil
ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { case err == watchtools.ErrWatchClosed:
options.FieldSelector = fieldSelector continue
return o.DynamicClient.Resource(info.Mapping.Resource).Namespace(info.Namespace).List(context.TODO(), options) case err == wait.ErrWaitTimeout:
}, if watchEvent != nil {
WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { return watchEvent.Object, false, errWaitTimeoutWithName
options.FieldSelector = fieldSelector
return o.DynamicClient.Resource(info.Mapping.Resource).Namespace(info.Namespace).Watch(context.TODO(), options)
},
} }
return gottenObj, false, errWaitTimeoutWithName
// this function is used to refresh the cache to prevent timeout waits on resources that have disappeared default:
preconditionFunc := func(store cache.Store) (bool, error) { return gottenObj, false, err
_, exists, err := store.Get(&metav1.ObjectMeta{Namespace: info.Namespace, Name: info.Name})
if err != nil {
return true, err
} }
if !exists {
return true, apierrors.NewNotFound(mapping.Resource.GroupResource(), info.Name)
} }
return false, nil
}
var result runtime.Object
intr := interrupt.New(nil, cancel)
err := intr.Run(func() error {
ev, err := watchtools.UntilWithSync(ctx, lw, &unstructured.Unstructured{}, preconditionFunc, watchtools.ConditionFunc(condMet))
if ev != nil {
result = ev.Object
}
if err == context.DeadlineExceeded {
return errWaitTimeoutWithName
}
return err
})
if err != nil {
if err == wait.ErrWaitTimeout {
return result, false, errWaitTimeoutWithName
}
return result, false, err
}
return result, true, nil
} }
// ConditionalWait hold information to check an API status condition // ConditionalWait hold information to check an API status condition

View File

@ -276,9 +276,8 @@ func TestWaitForDeletion(t *testing.T) {
if len(actions) != 1 { if len(actions) != 1 {
t.Fatal(spew.Sdump(actions)) t.Fatal(spew.Sdump(actions))
} }
if !actions[0].Matches("list", "theresource") || actions[0].(clienttesting.ListAction).GetListRestrictions().Fields.String() != "metadata.name=name-foo" {
if !actions[0].Matches("get", "theresource") || actions[0].(clienttesting.GetAction).GetName() != "name-foo" { t.Error(spew.Sdump(actions))
t.Error(spew.Sdump(actions[0]))
} }
}, },
}, },
@ -319,7 +318,7 @@ func TestWaitForDeletion(t *testing.T) {
count++ count++
fakeWatch := watch.NewRaceFreeFake() fakeWatch := watch.NewRaceFreeFake()
go func() { go func() {
time.Sleep(1 * time.Second) time.Sleep(100 * time.Millisecond)
fakeWatch.Stop() fakeWatch.Stop()
}() }()
return true, fakeWatch, nil return true, fakeWatch, nil
@ -339,7 +338,7 @@ func TestWaitForDeletion(t *testing.T) {
if len(actions) != 1 { if len(actions) != 1 {
t.Fatal(spew.Sdump(actions)) t.Fatal(spew.Sdump(actions))
} }
if !actions[0].Matches("get", "theresource") || actions[0].(clienttesting.GetAction).GetName() != "name-foo" { if !actions[0].Matches("list", "theresource") || actions[0].(clienttesting.ListAction).GetListRestrictions().Fields.String() != "metadata.name=name-foo" {
t.Error(spew.Sdump(actions)) t.Error(spew.Sdump(actions))
} }
}, },
@ -357,9 +356,6 @@ func TestWaitForDeletion(t *testing.T) {
}, },
fakeClient: func() *dynamicfakeclient.FakeDynamicClient { fakeClient: func() *dynamicfakeclient.FakeDynamicClient {
fakeClient := dynamicfakeclient.NewSimpleDynamicClientWithCustomListKinds(scheme, listMapping) fakeClient := dynamicfakeclient.NewSimpleDynamicClientWithCustomListKinds(scheme, listMapping)
fakeClient.PrependReactor("get", "theresource", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) {
return true, newUnstructuredList(newUnstructured("group/version", "TheKind", "ns-foo", "name-foo")), nil
})
fakeClient.PrependReactor("list", "theresource", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) { fakeClient.PrependReactor("list", "theresource", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) {
return true, newUnstructuredList(newUnstructured("group/version", "TheKind", "ns-foo", "name-foo")), nil return true, newUnstructuredList(newUnstructured("group/version", "TheKind", "ns-foo", "name-foo")), nil
}) })
@ -369,16 +365,13 @@ func TestWaitForDeletion(t *testing.T) {
expectedErr: "timed out waiting for the condition on theresource/name-foo", expectedErr: "timed out waiting for the condition on theresource/name-foo",
validateActions: func(t *testing.T, actions []clienttesting.Action) { validateActions: func(t *testing.T, actions []clienttesting.Action) {
if len(actions) != 3 { if len(actions) != 2 {
t.Fatal(spew.Sdump(actions)) t.Fatal(spew.Sdump(actions))
} }
if !actions[0].Matches("get", "theresource") || actions[0].(clienttesting.GetAction).GetName() != "name-foo" { if !actions[0].Matches("list", "theresource") || actions[0].(clienttesting.ListAction).GetListRestrictions().Fields.String() != "metadata.name=name-foo" {
t.Error(spew.Sdump(actions)) t.Error(spew.Sdump(actions))
} }
if !actions[1].Matches("list", "theresource") || actions[1].(clienttesting.ListAction).GetListRestrictions().Fields.String() != "metadata.name=name-foo" { if !actions[1].Matches("watch", "theresource") {
t.Error(spew.Sdump(actions))
}
if !actions[2].Matches("watch", "theresource") || actions[2].(clienttesting.WatchAction).GetWatchRestrictions().Fields.String() != "metadata.name=name-foo" {
t.Error(spew.Sdump(actions)) t.Error(spew.Sdump(actions))
} }
}, },
@ -396,13 +389,6 @@ func TestWaitForDeletion(t *testing.T) {
}, },
fakeClient: func() *dynamicfakeclient.FakeDynamicClient { fakeClient: func() *dynamicfakeclient.FakeDynamicClient {
fakeClient := dynamicfakeclient.NewSimpleDynamicClientWithCustomListKinds(scheme, listMapping) fakeClient := dynamicfakeclient.NewSimpleDynamicClientWithCustomListKinds(scheme, listMapping)
fakeClient.PrependReactor("get", "theresource", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) {
unstructuredObj := newUnstructured("group/version", "TheKind", "ns-foo", "name-foo")
unstructuredObj.SetResourceVersion("123")
unstructuredList := newUnstructuredList(unstructuredObj)
unstructuredList.SetResourceVersion("234")
return true, unstructuredList, nil
})
fakeClient.PrependReactor("list", "theresource", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) { fakeClient.PrependReactor("list", "theresource", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) {
unstructuredObj := newUnstructured("group/version", "TheKind", "ns-foo", "name-foo") unstructuredObj := newUnstructured("group/version", "TheKind", "ns-foo", "name-foo")
unstructuredObj.SetResourceVersion("123") unstructuredObj.SetResourceVersion("123")
@ -416,7 +402,7 @@ func TestWaitForDeletion(t *testing.T) {
count++ count++
fakeWatch := watch.NewRaceFreeFake() fakeWatch := watch.NewRaceFreeFake()
go func() { go func() {
time.Sleep(1 * time.Second) time.Sleep(100 * time.Millisecond)
fakeWatch.Stop() fakeWatch.Stop()
}() }()
return true, fakeWatch, nil return true, fakeWatch, nil
@ -433,17 +419,17 @@ func TestWaitForDeletion(t *testing.T) {
if len(actions) != 4 { if len(actions) != 4 {
t.Fatal(spew.Sdump(actions)) t.Fatal(spew.Sdump(actions))
} }
if !actions[0].Matches("get", "theresource") || actions[0].(clienttesting.GetAction).GetName() != "name-foo" { if !actions[0].Matches("list", "theresource") || actions[0].(clienttesting.ListAction).GetListRestrictions().Fields.String() != "metadata.name=name-foo" {
t.Error(spew.Sdump(actions[0])) t.Error(spew.Sdump(actions))
} }
if !actions[1].Matches("list", "theresource") || actions[1].(clienttesting.ListAction).GetListRestrictions().Fields.String() != "metadata.name=name-foo" { if !actions[1].Matches("watch", "theresource") || actions[1].(clienttesting.WatchAction).GetWatchRestrictions().ResourceVersion != "234" {
t.Error(spew.Sdump(actions[1])) t.Error(spew.Sdump(actions))
} }
if !actions[2].Matches("watch", "theresource") || actions[2].(clienttesting.WatchAction).GetWatchRestrictions().ResourceVersion != "234" { if !actions[2].Matches("list", "theresource") || actions[2].(clienttesting.ListAction).GetListRestrictions().Fields.String() != "metadata.name=name-foo" {
t.Error(spew.Sdump(actions[2])) t.Error(spew.Sdump(actions))
} }
if !actions[3].Matches("watch", "theresource") || actions[3].(clienttesting.WatchAction).GetWatchRestrictions().ResourceVersion != "234" { if !actions[3].Matches("watch", "theresource") || actions[3].(clienttesting.WatchAction).GetWatchRestrictions().ResourceVersion != "234" {
t.Error(spew.Sdump(actions[3])) t.Error(spew.Sdump(actions))
} }
}, },
}, },
@ -473,10 +459,13 @@ func TestWaitForDeletion(t *testing.T) {
timeout: 10 * time.Second, timeout: 10 * time.Second,
validateActions: func(t *testing.T, actions []clienttesting.Action) { validateActions: func(t *testing.T, actions []clienttesting.Action) {
if len(actions) != 1 { if len(actions) != 2 {
t.Fatal(spew.Sdump(actions)) t.Fatal(spew.Sdump(actions))
} }
if !actions[0].Matches("get", "theresource") || actions[0].(clienttesting.GetAction).GetName() != "name-foo" { if !actions[0].Matches("list", "theresource") || actions[0].(clienttesting.ListAction).GetListRestrictions().Fields.String() != "metadata.name=name-foo" {
t.Error(spew.Sdump(actions))
}
if !actions[1].Matches("watch", "theresource") {
t.Error(spew.Sdump(actions)) t.Error(spew.Sdump(actions))
} }
}, },
@ -522,26 +511,14 @@ func TestWaitForDeletion(t *testing.T) {
timeout: 10 * time.Second, timeout: 10 * time.Second,
validateActions: func(t *testing.T, actions []clienttesting.Action) { validateActions: func(t *testing.T, actions []clienttesting.Action) {
if len(actions) != 6 { if len(actions) != 2 {
t.Fatal(spew.Sdump(actions)) t.Fatal(spew.Sdump(actions))
} }
if !actions[0].Matches("get", "theresource-1") || actions[0].(clienttesting.GetAction).GetName() != "name-foo-1" { if !actions[0].Matches("list", "theresource-1") {
t.Error(spew.Sdump(actions[0])) t.Error(spew.Sdump(actions))
} }
if !actions[1].Matches("list", "theresource-1") || actions[1].(clienttesting.ListAction).GetListRestrictions().Fields.String() != "metadata.name=name-foo-1" { if !actions[1].Matches("list", "theresource-2") {
t.Error(spew.Sdump(actions[1])) t.Error(spew.Sdump(actions))
}
if !actions[2].Matches("watch", "theresource-1") || actions[2].(clienttesting.WatchAction).GetWatchRestrictions().Fields.String() != "metadata.name=name-foo-1" {
t.Error(spew.Sdump(actions[2]))
}
if !actions[3].Matches("get", "theresource-2") || actions[3].(clienttesting.GetAction).GetName() != "name-foo-2" {
t.Error(spew.Sdump(actions[3]))
}
if !actions[4].Matches("list", "theresource-2") || actions[4].(clienttesting.ListAction).GetListRestrictions().Fields.String() != "metadata.name=name-foo-2" {
t.Error(spew.Sdump(actions[4]))
}
if !actions[5].Matches("watch", "theresource-2") || actions[5].(clienttesting.WatchAction).GetWatchRestrictions().Fields.String() != "metadata.name=name-foo-2" {
t.Error(spew.Sdump(actions[5]))
} }
}, },
}, },
@ -583,10 +560,19 @@ func TestWaitForDeletion(t *testing.T) {
timeout: 10 * time.Second, timeout: 10 * time.Second,
validateActions: func(t *testing.T, actions []clienttesting.Action) { validateActions: func(t *testing.T, actions []clienttesting.Action) {
if len(actions) != 1 { if len(actions) != 4 {
t.Fatal(spew.Sdump(actions)) t.Fatal(spew.Sdump(actions))
} }
if !actions[0].Matches("get", "theresource") || actions[0].(clienttesting.GetAction).GetName() != "name-foo" { if !actions[0].Matches("list", "theresource") || actions[0].(clienttesting.ListAction).GetListRestrictions().Fields.String() != "metadata.name=name-foo" {
t.Error(spew.Sdump(actions))
}
if !actions[1].Matches("watch", "theresource") {
t.Error(spew.Sdump(actions))
}
if !actions[2].Matches("list", "theresource") || actions[2].(clienttesting.ListAction).GetListRestrictions().Fields.String() != "metadata.name=name-foo" {
t.Error(spew.Sdump(actions))
}
if !actions[3].Matches("watch", "theresource") {
t.Error(spew.Sdump(actions)) t.Error(spew.Sdump(actions))
} }
}, },
@ -663,14 +649,10 @@ func TestWaitForCondition(t *testing.T) {
timeout: 10 * time.Second, timeout: 10 * time.Second,
validateActions: func(t *testing.T, actions []clienttesting.Action) { validateActions: func(t *testing.T, actions []clienttesting.Action) {
if len(actions) != 2 { if len(actions) != 1 {
t.Fatal(spew.Sdump(actions)) t.Fatal(spew.Sdump(actions))
} }
if !actions[0].Matches("list", "theresource") { if !actions[0].Matches("list", "theresource") || actions[0].(clienttesting.ListAction).GetListRestrictions().Fields.String() != "metadata.name=name-foo" {
t.Error(spew.Sdump(actions))
} else if actions[0].(clienttesting.ListAction).GetListRestrictions().Fields.String() != "metadata.name=name-foo" {
t.Error(spew.Sdump(actions))
} else if actions[1].(clienttesting.WatchAction).GetWatchRestrictions().Fields.String() != "metadata.name=name-foo" {
t.Error(spew.Sdump(actions)) t.Error(spew.Sdump(actions))
} }
}, },
@ -735,7 +717,7 @@ func TestWaitForCondition(t *testing.T) {
}, },
timeout: 1 * time.Second, timeout: 1 * time.Second,
expectedErr: `theresource.group "name-foo" not found`, expectedErr: "timed out waiting for the condition on theresource/name-foo",
validateActions: func(t *testing.T, actions []clienttesting.Action) { validateActions: func(t *testing.T, actions []clienttesting.Action) {
if len(actions) != 2 { if len(actions) != 2 {
t.Fatal(spew.Sdump(actions)) t.Fatal(spew.Sdump(actions))
@ -774,7 +756,7 @@ func TestWaitForCondition(t *testing.T) {
count++ count++
fakeWatch := watch.NewRaceFreeFake() fakeWatch := watch.NewRaceFreeFake()
go func() { go func() {
time.Sleep(1 * time.Second) time.Sleep(100 * time.Millisecond)
fakeWatch.Stop() fakeWatch.Stop()
}() }()
return true, fakeWatch, nil return true, fakeWatch, nil
@ -788,7 +770,7 @@ func TestWaitForCondition(t *testing.T) {
expectedErr: "timed out waiting for the condition on theresource/name-foo", expectedErr: "timed out waiting for the condition on theresource/name-foo",
validateActions: func(t *testing.T, actions []clienttesting.Action) { validateActions: func(t *testing.T, actions []clienttesting.Action) {
if len(actions) != 3 { if len(actions) != 4 {
t.Fatal(spew.Sdump(actions)) t.Fatal(spew.Sdump(actions))
} }
if !actions[0].Matches("list", "theresource") || actions[0].(clienttesting.ListAction).GetListRestrictions().Fields.String() != "metadata.name=name-foo" { if !actions[0].Matches("list", "theresource") || actions[0].(clienttesting.ListAction).GetListRestrictions().Fields.String() != "metadata.name=name-foo" {
@ -797,7 +779,10 @@ func TestWaitForCondition(t *testing.T) {
if !actions[1].Matches("watch", "theresource") || actions[1].(clienttesting.WatchAction).GetWatchRestrictions().ResourceVersion != "234" { if !actions[1].Matches("watch", "theresource") || actions[1].(clienttesting.WatchAction).GetWatchRestrictions().ResourceVersion != "234" {
t.Error(spew.Sdump(actions)) t.Error(spew.Sdump(actions))
} }
if !actions[2].Matches("watch", "theresource") || actions[2].(clienttesting.WatchAction).GetWatchRestrictions().ResourceVersion != "234" { if !actions[2].Matches("list", "theresource") || actions[2].(clienttesting.ListAction).GetListRestrictions().Fields.String() != "metadata.name=name-foo" {
t.Error(spew.Sdump(actions))
}
if !actions[3].Matches("watch", "theresource") || actions[3].(clienttesting.WatchAction).GetWatchRestrictions().ResourceVersion != "234" {
t.Error(spew.Sdump(actions)) t.Error(spew.Sdump(actions))
} }
}, },
@ -960,7 +945,7 @@ func TestWaitForCondition(t *testing.T) {
}, },
timeout: 1 * time.Second, timeout: 1 * time.Second,
expectedErr: `theresource.group "name-foo" not found`, expectedErr: "timed out waiting for the condition on theresource/name-foo",
validateActions: func(t *testing.T, actions []clienttesting.Action) { validateActions: func(t *testing.T, actions []clienttesting.Action) {
if len(actions) != 2 { if len(actions) != 2 {
t.Fatal(spew.Sdump(actions)) t.Fatal(spew.Sdump(actions))
@ -1037,7 +1022,7 @@ func TestWaitForCondition(t *testing.T) {
}, },
timeout: 1 * time.Second, timeout: 1 * time.Second,
expectedErr: `theresource.group "name-foo" not found`, expectedErr: "timed out waiting for the condition on theresource/name-foo",
validateActions: func(t *testing.T, actions []clienttesting.Action) { validateActions: func(t *testing.T, actions []clienttesting.Action) {
if len(actions) != 2 { if len(actions) != 2 {
t.Fatal(spew.Sdump(actions)) t.Fatal(spew.Sdump(actions))
@ -1320,7 +1305,7 @@ func TestWaitForDifferentJSONPathExpression(t *testing.T) {
o := &WaitOptions{ o := &WaitOptions{
ResourceFinder: genericclioptions.NewSimpleFakeResourceFinder(infos...), ResourceFinder: genericclioptions.NewSimpleFakeResourceFinder(infos...),
DynamicClient: fakeClient, DynamicClient: fakeClient,
Timeout: 1 * time.Second, Timeout: 1 * time.Millisecond,
Printer: printers.NewDiscardingPrinter(), Printer: printers.NewDiscardingPrinter(),
ConditionFn: JSONPathWait{ ConditionFn: JSONPathWait{
@ -1391,12 +1376,10 @@ func TestWaitForJSONPathCondition(t *testing.T) {
expectedErr: None, expectedErr: None,
validateActions: func(t *testing.T, actions []clienttesting.Action) { validateActions: func(t *testing.T, actions []clienttesting.Action) {
if len(actions) != 2 { if len(actions) != 1 {
t.Fatal(spew.Sdump(actions)) t.Fatal(spew.Sdump(actions))
} }
if !actions[0].Matches("list", "theresource") || if !actions[0].Matches("list", "theresource") || actions[0].(clienttesting.ListAction).GetListRestrictions().Fields.String() != "metadata.name=foo-b6699dcfb-rnv7t" {
actions[0].(clienttesting.ListAction).GetListRestrictions().Fields.String() != "metadata.name=foo-b6699dcfb-rnv7t" &&
actions[1].(clienttesting.WatchAction).GetWatchRestrictions().Fields.String() != "metadata.name=foo-b6699dcfb-rnv7t" {
t.Error(spew.Sdump(actions)) t.Error(spew.Sdump(actions))
} }
}, },
@ -1458,7 +1441,7 @@ func TestWaitForJSONPathCondition(t *testing.T) {
}, },
timeout: 1 * time.Second, timeout: 1 * time.Second,
expectedErr: `theresource.group "foo-b6699dcfb-rnv7t" not found`, expectedErr: "timed out waiting for the condition on theresource/foo-b6699dcfb-rnv7t",
validateActions: func(t *testing.T, actions []clienttesting.Action) { validateActions: func(t *testing.T, actions []clienttesting.Action) {
if len(actions) != 2 { if len(actions) != 2 {
t.Fatal(spew.Sdump(actions)) t.Fatal(spew.Sdump(actions))
@ -1497,7 +1480,7 @@ func TestWaitForJSONPathCondition(t *testing.T) {
count++ count++
fakeWatch := watch.NewRaceFreeFake() fakeWatch := watch.NewRaceFreeFake()
go func() { go func() {
time.Sleep(1 * time.Second) time.Sleep(100 * time.Millisecond)
fakeWatch.Stop() fakeWatch.Stop()
}() }()
return true, fakeWatch, nil return true, fakeWatch, nil
@ -1513,7 +1496,7 @@ func TestWaitForJSONPathCondition(t *testing.T) {
expectedErr: "timed out waiting for the condition on theresource/foo-b6699dcfb-rnv7t", expectedErr: "timed out waiting for the condition on theresource/foo-b6699dcfb-rnv7t",
validateActions: func(t *testing.T, actions []clienttesting.Action) { validateActions: func(t *testing.T, actions []clienttesting.Action) {
if len(actions) != 3 { if len(actions) != 4 {
t.Fatal(spew.Sdump(actions)) t.Fatal(spew.Sdump(actions))
} }
if !actions[0].Matches("list", "theresource") || actions[0].(clienttesting.ListAction).GetListRestrictions().Fields.String() != "metadata.name=foo-b6699dcfb-rnv7t" { if !actions[0].Matches("list", "theresource") || actions[0].(clienttesting.ListAction).GetListRestrictions().Fields.String() != "metadata.name=foo-b6699dcfb-rnv7t" {
@ -1522,7 +1505,10 @@ func TestWaitForJSONPathCondition(t *testing.T) {
if !actions[1].Matches("watch", "theresource") || actions[1].(clienttesting.WatchAction).GetWatchRestrictions().ResourceVersion != "234" { if !actions[1].Matches("watch", "theresource") || actions[1].(clienttesting.WatchAction).GetWatchRestrictions().ResourceVersion != "234" {
t.Error(spew.Sdump(actions)) t.Error(spew.Sdump(actions))
} }
if !actions[2].Matches("watch", "theresource") || actions[2].(clienttesting.WatchAction).GetWatchRestrictions().ResourceVersion != "234" { if !actions[2].Matches("list", "theresource") || actions[2].(clienttesting.ListAction).GetListRestrictions().Fields.String() != "metadata.name=foo-b6699dcfb-rnv7t" {
t.Error(spew.Sdump(actions))
}
if !actions[3].Matches("watch", "theresource") || actions[3].(clienttesting.WatchAction).GetWatchRestrictions().ResourceVersion != "234" {
t.Error(spew.Sdump(actions)) t.Error(spew.Sdump(actions))
} }
}, },
@ -1620,7 +1606,7 @@ func TestWaitForJSONPathCondition(t *testing.T) {
fakeClient: func() *dynamicfakeclient.FakeDynamicClient { fakeClient: func() *dynamicfakeclient.FakeDynamicClient {
fakeClient := dynamicfakeclient.NewSimpleDynamicClientWithCustomListKinds(scheme, listMapping) fakeClient := dynamicfakeclient.NewSimpleDynamicClientWithCustomListKinds(scheme, listMapping)
fakeClient.PrependReactor("list", "theresource", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) { fakeClient.PrependReactor("list", "theresource", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) {
return true, newUnstructuredList(newUnstructured("group/version", "TheKind", "default", "foo-b6699dcfb-rnv7t")), nil return true, newUnstructuredList(newUnstructured("group/version", "TheKind", "ns-foo", "name-foo")), nil
}) })
count := 0 count := 0
fakeClient.PrependWatchReactor("theresource", func(action clienttesting.Action) (handled bool, ret watch.Interface, err error) { fakeClient.PrependWatchReactor("theresource", func(action clienttesting.Action) (handled bool, ret watch.Interface, err error) {
@ -1647,14 +1633,20 @@ func TestWaitForJSONPathCondition(t *testing.T) {
expectedErr: None, expectedErr: None,
validateActions: func(t *testing.T, actions []clienttesting.Action) { validateActions: func(t *testing.T, actions []clienttesting.Action) {
if len(actions) != 2 { if len(actions) != 4 {
t.Fatal(spew.Sdump(actions)) t.Fatal(spew.Sdump(actions))
} }
if !actions[0].Matches("list", "theresource") || actions[0].(clienttesting.ListAction).GetListRestrictions().Fields.String() != "metadata.name=foo-b6699dcfb-rnv7t" { if !actions[0].Matches("list", "theresource") || actions[0].(clienttesting.ListAction).GetListRestrictions().Fields.String() != "metadata.name=foo-b6699dcfb-rnv7t" {
t.Error(spew.Sdump(actions[0])) t.Error(spew.Sdump(actions))
} }
if !actions[1].Matches("watch", "theresource") || actions[1].(clienttesting.WatchAction).GetWatchRestrictions().Fields.String() != "metadata.name=foo-b6699dcfb-rnv7t" { if !actions[1].Matches("watch", "theresource") {
t.Error(spew.Sdump(actions[1])) t.Error(spew.Sdump(actions))
}
if !actions[2].Matches("list", "theresource") || actions[2].(clienttesting.ListAction).GetListRestrictions().Fields.String() != "metadata.name=foo-b6699dcfb-rnv7t" {
t.Error(spew.Sdump(actions))
}
if !actions[3].Matches("watch", "theresource") {
t.Error(spew.Sdump(actions))
} }
}, },
}, },