mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 03:41:45 +00:00
Move kubectl wait to informers with a cache to avoid hanging due to objects disappearing from the cluster (#108086)
* move to using informers for getObjAndCheckCondition * move to using informers for IsDeleted * update tests to handle new informer methodology * set minimum timeout to 1s because informer can't handle less for caching reasons * fix null return for deletes issue
This commit is contained in:
parent
4f97d6c942
commit
171431ca2c
@ -40,11 +40,13 @@ 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"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -317,70 +319,79 @@ 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)
|
if len(info.Name) == 0 {
|
||||||
for {
|
return info.Object, false, fmt.Errorf("resource name must be provided")
|
||||||
if len(info.Name) == 0 {
|
}
|
||||||
return info.Object, false, fmt.Errorf("resource name must be provided")
|
|
||||||
}
|
|
||||||
|
|
||||||
nameSelector := fields.OneTermEqualSelector("metadata.name", info.Name).String()
|
gottenObj, initObjGetErr := o.DynamicClient.Resource(info.Mapping.Resource).Namespace(info.Namespace).Get(context.Background(), info.Name, metav1.GetOptions{})
|
||||||
|
if apierrors.IsNotFound(initObjGetErr) {
|
||||||
// List with a name field selector to get the current resourceVersion to watch from (not the object's resourceVersion)
|
return info.Object, true, nil
|
||||||
gottenObjList, err := o.DynamicClient.Resource(info.Mapping.Resource).Namespace(info.Namespace).List(context.TODO(), metav1.ListOptions{FieldSelector: nameSelector})
|
}
|
||||||
if apierrors.IsNotFound(err) {
|
if initObjGetErr != nil {
|
||||||
return info.Object, true, nil
|
// TODO this could do something slightly fancier if we wish
|
||||||
}
|
return info.Object, false, initObjGetErr
|
||||||
if err != nil {
|
}
|
||||||
// TODO this could do something slightly fancier if we wish
|
resourceLocation := ResourceLocation{
|
||||||
return info.Object, false, err
|
GroupResource: info.Mapping.Resource.GroupResource(),
|
||||||
}
|
Namespace: gottenObj.GetNamespace(),
|
||||||
if len(gottenObjList.Items) != 1 {
|
Name: gottenObj.GetName(),
|
||||||
return info.Object, true, nil
|
}
|
||||||
}
|
if uid, ok := o.UIDMap[resourceLocation]; ok {
|
||||||
gottenObj := &gottenObjList.Items[0]
|
if gottenObj.GetUID() != uid {
|
||||||
resourceLocation := ResourceLocation{
|
return gottenObj, true, nil
|
||||||
GroupResource: info.Mapping.Resource.GroupResource(),
|
|
||||||
Namespace: gottenObj.GetNamespace(),
|
|
||||||
Name: gottenObj.GetName(),
|
|
||||||
}
|
|
||||||
if uid, ok := o.UIDMap[resourceLocation]; ok {
|
|
||||||
if gottenObj.GetUID() != uid {
|
|
||||||
return gottenObj, true, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
watchOptions := metav1.ListOptions{}
|
|
||||||
watchOptions.FieldSelector = nameSelector
|
|
||||||
watchOptions.ResourceVersion = gottenObjList.GetResourceVersion()
|
|
||||||
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)
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
endTime := time.Now().Add(o.Timeout)
|
||||||
|
timeout := time.Until(endTime)
|
||||||
|
errWaitTimeoutWithName := extendErrWaitTimeout(wait.ErrWaitTimeout, info)
|
||||||
|
if timeout < 0 {
|
||||||
|
// 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 {
|
||||||
|
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, true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait has helper methods for handling watches, including error handling.
|
// Wait has helper methods for handling watches, including error handling.
|
||||||
@ -410,68 +421,67 @@ 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)
|
if len(info.Name) == 0 {
|
||||||
for {
|
return info.Object, false, fmt.Errorf("resource name must be provided")
|
||||||
if len(info.Name) == 0 {
|
|
||||||
return info.Object, false, fmt.Errorf("resource name must be provided")
|
|
||||||
}
|
|
||||||
|
|
||||||
nameSelector := fields.OneTermEqualSelector("metadata.name", info.Name).String()
|
|
||||||
|
|
||||||
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)
|
|
||||||
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, watchtools.ConditionFunc(condMet))
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
endTime := time.Now().Add(o.Timeout)
|
||||||
|
timeout := time.Until(endTime)
|
||||||
|
errWaitTimeoutWithName := extendErrWaitTimeout(wait.ErrWaitTimeout, info)
|
||||||
|
if timeout < 0 {
|
||||||
|
// we're out of time
|
||||||
|
return info.Object, false, errWaitTimeoutWithName
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := watchtools.ContextWithOptionalTimeout(context.Background(), o.Timeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
mapping := info.ResourceMapping() // used to pass back meaningful errors if object disappears
|
||||||
|
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 {
|
||||||
|
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
|
||||||
|
@ -276,8 +276,9 @@ 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" {
|
|
||||||
t.Error(spew.Sdump(actions))
|
if !actions[0].Matches("get", "theresource") || actions[0].(clienttesting.GetAction).GetName() != "name-foo" {
|
||||||
|
t.Error(spew.Sdump(actions[0]))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -318,7 +319,7 @@ func TestWaitForDeletion(t *testing.T) {
|
|||||||
count++
|
count++
|
||||||
fakeWatch := watch.NewRaceFreeFake()
|
fakeWatch := watch.NewRaceFreeFake()
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(1 * time.Second)
|
||||||
fakeWatch.Stop()
|
fakeWatch.Stop()
|
||||||
}()
|
}()
|
||||||
return true, fakeWatch, nil
|
return true, fakeWatch, nil
|
||||||
@ -338,7 +339,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("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))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -356,6 +357,9 @@ 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
|
||||||
})
|
})
|
||||||
@ -365,13 +369,16 @@ 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) != 2 {
|
if len(actions) != 3 {
|
||||||
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))
|
||||||
}
|
}
|
||||||
if !actions[1].Matches("watch", "theresource") {
|
if !actions[1].Matches("list", "theresource") || actions[1].(clienttesting.ListAction).GetListRestrictions().Fields.String() != "metadata.name=name-foo" {
|
||||||
|
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))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -389,6 +396,13 @@ 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")
|
||||||
@ -402,7 +416,7 @@ func TestWaitForDeletion(t *testing.T) {
|
|||||||
count++
|
count++
|
||||||
fakeWatch := watch.NewRaceFreeFake()
|
fakeWatch := watch.NewRaceFreeFake()
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(1 * time.Second)
|
||||||
fakeWatch.Stop()
|
fakeWatch.Stop()
|
||||||
}()
|
}()
|
||||||
return true, fakeWatch, nil
|
return true, fakeWatch, nil
|
||||||
@ -419,17 +433,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("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]))
|
||||||
}
|
}
|
||||||
if !actions[1].Matches("watch", "theresource") || actions[1].(clienttesting.WatchAction).GetWatchRestrictions().ResourceVersion != "234" {
|
if !actions[1].Matches("list", "theresource") || actions[1].(clienttesting.ListAction).GetListRestrictions().Fields.String() != "metadata.name=name-foo" {
|
||||||
t.Error(spew.Sdump(actions))
|
t.Error(spew.Sdump(actions[1]))
|
||||||
}
|
}
|
||||||
if !actions[2].Matches("list", "theresource") || actions[2].(clienttesting.ListAction).GetListRestrictions().Fields.String() != "metadata.name=name-foo" {
|
if !actions[2].Matches("watch", "theresource") || actions[2].(clienttesting.WatchAction).GetWatchRestrictions().ResourceVersion != "234" {
|
||||||
t.Error(spew.Sdump(actions))
|
t.Error(spew.Sdump(actions[2]))
|
||||||
}
|
}
|
||||||
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))
|
t.Error(spew.Sdump(actions[3]))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -459,13 +473,10 @@ 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) != 2 {
|
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))
|
|
||||||
}
|
|
||||||
if !actions[1].Matches("watch", "theresource") {
|
|
||||||
t.Error(spew.Sdump(actions))
|
t.Error(spew.Sdump(actions))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -511,14 +522,26 @@ 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) != 2 {
|
if len(actions) != 6 {
|
||||||
t.Fatal(spew.Sdump(actions))
|
t.Fatal(spew.Sdump(actions))
|
||||||
}
|
}
|
||||||
if !actions[0].Matches("list", "theresource-1") {
|
if !actions[0].Matches("get", "theresource-1") || actions[0].(clienttesting.GetAction).GetName() != "name-foo-1" {
|
||||||
t.Error(spew.Sdump(actions))
|
t.Error(spew.Sdump(actions[0]))
|
||||||
}
|
}
|
||||||
if !actions[1].Matches("list", "theresource-2") {
|
if !actions[1].Matches("list", "theresource-1") || actions[1].(clienttesting.ListAction).GetListRestrictions().Fields.String() != "metadata.name=name-foo-1" {
|
||||||
t.Error(spew.Sdump(actions))
|
t.Error(spew.Sdump(actions[1]))
|
||||||
|
}
|
||||||
|
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]))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -560,19 +583,10 @@ 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) != 4 {
|
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))
|
|
||||||
}
|
|
||||||
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))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -649,10 +663,14 @@ 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) != 1 {
|
if len(actions) != 2 {
|
||||||
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") {
|
||||||
|
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))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -717,7 +735,7 @@ func TestWaitForCondition(t *testing.T) {
|
|||||||
},
|
},
|
||||||
timeout: 1 * time.Second,
|
timeout: 1 * time.Second,
|
||||||
|
|
||||||
expectedErr: "timed out waiting for the condition on theresource/name-foo",
|
expectedErr: `theresource.group "name-foo" not found`,
|
||||||
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))
|
||||||
@ -756,7 +774,7 @@ func TestWaitForCondition(t *testing.T) {
|
|||||||
count++
|
count++
|
||||||
fakeWatch := watch.NewRaceFreeFake()
|
fakeWatch := watch.NewRaceFreeFake()
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(1 * time.Second)
|
||||||
fakeWatch.Stop()
|
fakeWatch.Stop()
|
||||||
}()
|
}()
|
||||||
return true, fakeWatch, nil
|
return true, fakeWatch, nil
|
||||||
@ -770,7 +788,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) != 4 {
|
if len(actions) != 3 {
|
||||||
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" {
|
||||||
@ -779,10 +797,7 @@ 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("list", "theresource") || actions[2].(clienttesting.ListAction).GetListRestrictions().Fields.String() != "metadata.name=name-foo" {
|
if !actions[2].Matches("watch", "theresource") || actions[2].(clienttesting.WatchAction).GetWatchRestrictions().ResourceVersion != "234" {
|
||||||
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))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -945,7 +960,7 @@ func TestWaitForCondition(t *testing.T) {
|
|||||||
},
|
},
|
||||||
timeout: 1 * time.Second,
|
timeout: 1 * time.Second,
|
||||||
|
|
||||||
expectedErr: "timed out waiting for the condition on theresource/name-foo",
|
expectedErr: `theresource.group "name-foo" not found`,
|
||||||
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))
|
||||||
@ -1022,7 +1037,7 @@ func TestWaitForCondition(t *testing.T) {
|
|||||||
},
|
},
|
||||||
timeout: 1 * time.Second,
|
timeout: 1 * time.Second,
|
||||||
|
|
||||||
expectedErr: "timed out waiting for the condition on theresource/name-foo",
|
expectedErr: `theresource.group "name-foo" not found`,
|
||||||
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))
|
||||||
@ -1305,7 +1320,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.Millisecond,
|
Timeout: 1 * time.Second,
|
||||||
|
|
||||||
Printer: printers.NewDiscardingPrinter(),
|
Printer: printers.NewDiscardingPrinter(),
|
||||||
ConditionFn: JSONPathWait{
|
ConditionFn: JSONPathWait{
|
||||||
@ -1376,10 +1391,12 @@ 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) != 1 {
|
if len(actions) != 2 {
|
||||||
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" &&
|
||||||
|
actions[1].(clienttesting.WatchAction).GetWatchRestrictions().Fields.String() != "metadata.name=foo-b6699dcfb-rnv7t" {
|
||||||
t.Error(spew.Sdump(actions))
|
t.Error(spew.Sdump(actions))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1441,7 +1458,7 @@ func TestWaitForJSONPathCondition(t *testing.T) {
|
|||||||
},
|
},
|
||||||
timeout: 1 * time.Second,
|
timeout: 1 * time.Second,
|
||||||
|
|
||||||
expectedErr: "timed out waiting for the condition on theresource/foo-b6699dcfb-rnv7t",
|
expectedErr: `theresource.group "foo-b6699dcfb-rnv7t" not found`,
|
||||||
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))
|
||||||
@ -1480,7 +1497,7 @@ func TestWaitForJSONPathCondition(t *testing.T) {
|
|||||||
count++
|
count++
|
||||||
fakeWatch := watch.NewRaceFreeFake()
|
fakeWatch := watch.NewRaceFreeFake()
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(1 * time.Second)
|
||||||
fakeWatch.Stop()
|
fakeWatch.Stop()
|
||||||
}()
|
}()
|
||||||
return true, fakeWatch, nil
|
return true, fakeWatch, nil
|
||||||
@ -1496,7 +1513,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) != 4 {
|
if len(actions) != 3 {
|
||||||
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" {
|
||||||
@ -1505,10 +1522,7 @@ 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("list", "theresource") || actions[2].(clienttesting.ListAction).GetListRestrictions().Fields.String() != "metadata.name=foo-b6699dcfb-rnv7t" {
|
if !actions[2].Matches("watch", "theresource") || actions[2].(clienttesting.WatchAction).GetWatchRestrictions().ResourceVersion != "234" {
|
||||||
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))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1606,7 +1620,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", "ns-foo", "name-foo")), nil
|
return true, newUnstructuredList(newUnstructured("group/version", "TheKind", "default", "foo-b6699dcfb-rnv7t")), 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) {
|
||||||
@ -1633,20 +1647,14 @@ 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) != 4 {
|
if len(actions) != 2 {
|
||||||
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))
|
t.Error(spew.Sdump(actions[0]))
|
||||||
}
|
}
|
||||||
if !actions[1].Matches("watch", "theresource") {
|
if !actions[1].Matches("watch", "theresource") || actions[1].(clienttesting.WatchAction).GetWatchRestrictions().Fields.String() != "metadata.name=foo-b6699dcfb-rnv7t" {
|
||||||
t.Error(spew.Sdump(actions))
|
t.Error(spew.Sdump(actions[1]))
|
||||||
}
|
|
||||||
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))
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user