mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-14 10:53:34 +00:00
Fresh dep ensure
This commit is contained in:
48
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/wait/BUILD
generated
vendored
48
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/wait/BUILD
generated
vendored
@ -7,17 +7,21 @@ go_library(
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/kubectl/cmd/util:go_default_library",
|
||||
"//pkg/kubectl/genericclioptions:go_default_library",
|
||||
"//pkg/kubectl/genericclioptions/printers:go_default_library",
|
||||
"//pkg/kubectl/genericclioptions/resource:go_default_library",
|
||||
"//pkg/kubectl/util/templates:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/watch:go_default_library",
|
||||
"//staging/src/k8s.io/cli-runtime/pkg/genericclioptions:go_default_library",
|
||||
"//staging/src/k8s.io/cli-runtime/pkg/genericclioptions/printers:go_default_library",
|
||||
"//staging/src/k8s.io/cli-runtime/pkg/genericclioptions/resource:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/dynamic:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/tools/watch:go_default_library",
|
||||
"//vendor/github.com/spf13/cobra:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
|
||||
"//vendor/k8s.io/client-go/dynamic:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@ -40,17 +44,19 @@ go_test(
|
||||
srcs = ["wait_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//pkg/kubectl/genericclioptions:go_default_library",
|
||||
"//pkg/kubectl/genericclioptions/printers:go_default_library",
|
||||
"//pkg/kubectl/genericclioptions/resource:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/watch:go_default_library",
|
||||
"//staging/src/k8s.io/cli-runtime/pkg/genericclioptions:go_default_library",
|
||||
"//staging/src/k8s.io/cli-runtime/pkg/genericclioptions/printers:go_default_library",
|
||||
"//staging/src/k8s.io/cli-runtime/pkg/genericclioptions/resource:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/dynamic/fake:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/testing:go_default_library",
|
||||
"//vendor/github.com/davecgh/go-spew/spew:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
|
||||
"//vendor/k8s.io/client-go/dynamic/fake:go_default_library",
|
||||
"//vendor/k8s.io/client-go/testing:go_default_library",
|
||||
],
|
||||
)
|
||||
|
152
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/wait/wait.go
generated
vendored
152
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/wait/wait.go
generated
vendored
@ -17,25 +17,57 @@ limitations under the License.
|
||||
package wait
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
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"
|
||||
"k8s.io/client-go/dynamic"
|
||||
watchtools "k8s.io/client-go/tools/watch"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions/printers"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/templates"
|
||||
)
|
||||
|
||||
var (
|
||||
wait_long = templates.LongDesc(`
|
||||
Experimental: Wait for a specific condition on one or many resources.
|
||||
|
||||
The command takes multiple resources and waits until the specified condition
|
||||
is seen in the Status field of every given resource.
|
||||
|
||||
Alternatively, the command can wait for the given set of resources to be deleted
|
||||
by providing the "delete" keyword as the value to the --for flag.
|
||||
|
||||
A successful message will be printed to stdout indicating when the specified
|
||||
condition has been met. One can use -o option to change to output destination.`)
|
||||
|
||||
wait_example = templates.Examples(`
|
||||
# Wait for the pod "busybox1" to contain the status condition of type "Ready".
|
||||
kubectl wait --for=condition=Ready pod/busybox1
|
||||
|
||||
# Wait for the pod "busybox1" to be deleted, with a timeout of 60s, after having issued the "delete" command.
|
||||
kubectl delete pod/busybox1
|
||||
kubectl wait --for=delete pod/busybox1 --timeout=60s`)
|
||||
)
|
||||
|
||||
// errNoMatchingResources is returned when there is no resources matching a query.
|
||||
var errNoMatchingResources = errors.New("no matching resources found")
|
||||
|
||||
// WaitFlags directly reflect the information that CLI is gathering via flags. They will be converted to Options, which
|
||||
// reflect the runtime requirements for the command. This structure reduces the transformation to wiring and makes
|
||||
// the logic itself easy to unit test
|
||||
@ -71,9 +103,11 @@ func NewCmdWait(restClientGetter genericclioptions.RESTClientGetter, streams gen
|
||||
flags := NewWaitFlags(restClientGetter, streams)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "wait resource.group/name [--for=delete|--for condition=available]",
|
||||
Use: "wait resource.group/name [--for=delete|--for condition=available]",
|
||||
DisableFlagsInUseLine: true,
|
||||
Short: "Experimental: Wait for one condition on one or many resources",
|
||||
Short: "Experimental: Wait for a specific condition on one or many resources.",
|
||||
Long: wait_long,
|
||||
Example: wait_example,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
o, err := flags.ToOptions(args)
|
||||
cmdutil.CheckErr(err)
|
||||
@ -112,7 +146,7 @@ func (flags *WaitFlags) ToOptions(args []string) (*WaitOptions, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conditionFn, err := conditionFuncFor(flags.ForCondition)
|
||||
conditionFn, err := conditionFuncFor(flags.ForCondition, flags.ErrOut)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -135,28 +169,45 @@ func (flags *WaitFlags) ToOptions(args []string) (*WaitOptions, error) {
|
||||
return o, nil
|
||||
}
|
||||
|
||||
func conditionFuncFor(condition string) (ConditionFunc, error) {
|
||||
func conditionFuncFor(condition string, errOut io.Writer) (ConditionFunc, error) {
|
||||
if strings.ToLower(condition) == "delete" {
|
||||
return IsDeleted, nil
|
||||
}
|
||||
if strings.HasPrefix(condition, "condition=") {
|
||||
conditionName := condition[len("condition="):]
|
||||
conditionValue := "true"
|
||||
if equalsIndex := strings.Index(conditionName, "="); equalsIndex != -1 {
|
||||
conditionValue = conditionName[equalsIndex+1:]
|
||||
conditionName = conditionName[0:equalsIndex]
|
||||
}
|
||||
|
||||
return ConditionalWait{
|
||||
conditionName: conditionName,
|
||||
// TODO allow specifying a false
|
||||
conditionStatus: "true",
|
||||
conditionName: conditionName,
|
||||
conditionStatus: conditionValue,
|
||||
errOut: errOut,
|
||||
}.IsConditionMet, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unrecognized condition: %q", condition)
|
||||
}
|
||||
|
||||
type ResourceLocation struct {
|
||||
GroupResource schema.GroupResource
|
||||
Namespace string
|
||||
Name string
|
||||
}
|
||||
|
||||
type UIDMap map[ResourceLocation]types.UID
|
||||
|
||||
// WaitOptions is a set of options that allows you to wait. This is the object reflects the runtime needs of a wait
|
||||
// command, making the logic itself easy to unit test with our existing mocks.
|
||||
type WaitOptions struct {
|
||||
ResourceFinder genericclioptions.ResourceFinder
|
||||
DynamicClient dynamic.Interface
|
||||
Timeout time.Duration
|
||||
// UIDMap maps a resource location to a UID. It is optional, but ConditionFuncs may choose to use it to make the result
|
||||
// more reliable. For instance, delete can look for UID consistency during delegated calls.
|
||||
UIDMap UIDMap
|
||||
DynamicClient dynamic.Interface
|
||||
Timeout time.Duration
|
||||
|
||||
Printer printers.ResourcePrinter
|
||||
ConditionFn ConditionFunc
|
||||
@ -168,11 +219,13 @@ type ConditionFunc func(info *resource.Info, o *WaitOptions) (finalObject runtim
|
||||
|
||||
// RunWait runs the waiting logic
|
||||
func (o *WaitOptions) RunWait() error {
|
||||
return o.ResourceFinder.Do().Visit(func(info *resource.Info, err error) error {
|
||||
visitCount := 0
|
||||
err := o.ResourceFinder.Do().Visit(func(info *resource.Info, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
visitCount++
|
||||
finalObject, success, err := o.ConditionFn(info, o)
|
||||
if success {
|
||||
o.Printer.PrintObj(finalObject, o.Out)
|
||||
@ -183,20 +236,40 @@ func (o *WaitOptions) RunWait() error {
|
||||
}
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if visitCount == 0 {
|
||||
return errNoMatchingResources
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// IsDeleted is a condition func for waiting for something to be deleted
|
||||
func IsDeleted(info *resource.Info, o *WaitOptions) (runtime.Object, bool, error) {
|
||||
endTime := time.Now().Add(o.Timeout)
|
||||
for {
|
||||
if len(info.Name) == 0 {
|
||||
return info.Object, false, fmt.Errorf("resource name must be provided")
|
||||
}
|
||||
gottenObj, err := o.DynamicClient.Resource(info.Mapping.Resource).Namespace(info.Namespace).Get(info.Name, metav1.GetOptions{})
|
||||
if errors.IsNotFound(err) {
|
||||
if apierrors.IsNotFound(err) {
|
||||
return info.Object, true, nil
|
||||
}
|
||||
if err != nil {
|
||||
// TODO this could do something slightly fancier if we wish
|
||||
return info.Object, false, err
|
||||
}
|
||||
resourceLocation := ResourceLocation{
|
||||
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 = "metadata.name=" + info.Name
|
||||
@ -211,11 +284,14 @@ func IsDeleted(info *resource.Info, o *WaitOptions) (runtime.Object, bool, error
|
||||
// we're out of time
|
||||
return gottenObj, false, wait.ErrWaitTimeout
|
||||
}
|
||||
watchEvent, err := watch.Until(o.Timeout, objWatch, isDeleted)
|
||||
|
||||
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 == watch.ErrWatchClosed:
|
||||
case err == watchtools.ErrWatchClosed:
|
||||
continue
|
||||
case err == wait.ErrWaitTimeout:
|
||||
if watchEvent != nil {
|
||||
@ -228,24 +304,46 @@ func IsDeleted(info *resource.Info, o *WaitOptions) (runtime.Object, bool, error
|
||||
}
|
||||
}
|
||||
|
||||
func isDeleted(event watch.Event) (bool, error) {
|
||||
return event.Type == watch.Deleted, nil
|
||||
// Wait has helper methods for handling watches, including error handling.
|
||||
type Wait struct {
|
||||
errOut io.Writer
|
||||
}
|
||||
|
||||
// IsDeleted returns true if the object is deleted. It prints any errors it encounters.
|
||||
func (w Wait) IsDeleted(event watch.Event) (bool, error) {
|
||||
switch event.Type {
|
||||
case watch.Error:
|
||||
// keep waiting in the event we see an error - we expect the watch to be closed by
|
||||
// the server if the error is unrecoverable.
|
||||
err := apierrors.FromObject(event.Object)
|
||||
fmt.Fprintf(w.errOut, "error: An error occurred while waiting for the object to be deleted: %v", err)
|
||||
return false, nil
|
||||
case watch.Deleted:
|
||||
return true, nil
|
||||
default:
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
// ConditionalWait hold information to check an API status condition
|
||||
type ConditionalWait struct {
|
||||
conditionName string
|
||||
conditionStatus string
|
||||
// errOut is written to if an error occurs
|
||||
errOut io.Writer
|
||||
}
|
||||
|
||||
// IsConditionMet is a conditionfunc for waiting on an API condition to be met
|
||||
func (w ConditionalWait) IsConditionMet(info *resource.Info, o *WaitOptions) (runtime.Object, bool, error) {
|
||||
endTime := time.Now().Add(o.Timeout)
|
||||
for {
|
||||
if len(info.Name) == 0 {
|
||||
return info.Object, false, fmt.Errorf("resource name must be provided")
|
||||
}
|
||||
resourceVersion := ""
|
||||
gottenObj, err := o.DynamicClient.Resource(info.Mapping.Resource).Namespace(info.Namespace).Get(info.Name, metav1.GetOptions{})
|
||||
switch {
|
||||
case errors.IsNotFound(err):
|
||||
case apierrors.IsNotFound(err):
|
||||
resourceVersion = "0"
|
||||
case err != nil:
|
||||
return info.Object, false, err
|
||||
@ -273,11 +371,14 @@ func (w ConditionalWait) IsConditionMet(info *resource.Info, o *WaitOptions) (ru
|
||||
// we're out of time
|
||||
return gottenObj, false, wait.ErrWaitTimeout
|
||||
}
|
||||
watchEvent, err := watch.Until(o.Timeout, objWatch, w.isConditionMet)
|
||||
|
||||
ctx, cancel := watchtools.ContextWithOptionalTimeout(context.Background(), o.Timeout)
|
||||
watchEvent, err := watchtools.UntilWithoutRetry(ctx, objWatch, w.isConditionMet)
|
||||
cancel()
|
||||
switch {
|
||||
case err == nil:
|
||||
return watchEvent.Object, true, nil
|
||||
case err == watch.ErrWatchClosed:
|
||||
case err == watchtools.ErrWatchClosed:
|
||||
continue
|
||||
case err == wait.ErrWaitTimeout:
|
||||
if watchEvent != nil {
|
||||
@ -315,6 +416,13 @@ func (w ConditionalWait) checkCondition(obj *unstructured.Unstructured) (bool, e
|
||||
}
|
||||
|
||||
func (w ConditionalWait) isConditionMet(event watch.Event) (bool, error) {
|
||||
if event.Type == watch.Error {
|
||||
// keep waiting in the event we see an error - we expect the watch to be closed by
|
||||
// the server
|
||||
err := apierrors.FromObject(event.Object)
|
||||
fmt.Fprintf(w.errOut, "error: An error occurred while waiting for the condition to be satisfied: %v", err)
|
||||
return false, nil
|
||||
}
|
||||
if event.Type == watch.Deleted {
|
||||
// this will chain back out, result in another get and an return false back up the chain
|
||||
return false, nil
|
||||
|
356
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/wait/wait_test.go
generated
vendored
356
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/wait/wait_test.go
generated
vendored
@ -17,6 +17,7 @@ limitations under the License.
|
||||
package wait
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"time"
|
||||
@ -26,16 +27,18 @@ import (
|
||||
"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"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions/printers"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource"
|
||||
)
|
||||
|
||||
func newUnstructured(apiVersion, kind, namespace, name string) *unstructured.Unstructured {
|
||||
@ -46,11 +49,22 @@ func newUnstructured(apiVersion, kind, namespace, name string) *unstructured.Uns
|
||||
"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{}{
|
||||
@ -66,21 +80,24 @@ func TestWaitForDeletion(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
info *resource.Info
|
||||
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",
|
||||
info: &resource.Info{
|
||||
Mapping: &meta.RESTMapping{
|
||||
Resource: schema.GroupVersionResource{Group: "group", Version: "version", Resource: "theresource"},
|
||||
infos: []*resource.Info{
|
||||
{
|
||||
Mapping: &meta.RESTMapping{
|
||||
Resource: schema.GroupVersionResource{Group: "group", Version: "version", Resource: "theresource"},
|
||||
},
|
||||
Name: "name-foo",
|
||||
Namespace: "ns-foo",
|
||||
},
|
||||
Name: "name-foo",
|
||||
Namespace: "ns-foo",
|
||||
},
|
||||
fakeClient: func() *dynamicfakeclient.FakeDynamicClient {
|
||||
return dynamicfakeclient.NewSimpleDynamicClient(scheme)
|
||||
@ -97,13 +114,77 @@ func TestWaitForDeletion(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "times out",
|
||||
info: &resource.Info{
|
||||
Mapping: &meta.RESTMapping{
|
||||
Resource: schema.GroupVersionResource{Group: "group", Version: "version", Resource: "theresource"},
|
||||
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",
|
||||
},
|
||||
Name: "name-foo",
|
||||
Namespace: "ns-foo",
|
||||
},
|
||||
fakeClient: func() *dynamicfakeclient.FakeDynamicClient {
|
||||
fakeClient := dynamicfakeclient.NewSimpleDynamicClient(scheme)
|
||||
@ -129,12 +210,14 @@ func TestWaitForDeletion(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "handles watch close out",
|
||||
info: &resource.Info{
|
||||
Mapping: &meta.RESTMapping{
|
||||
Resource: schema.GroupVersionResource{Group: "group", Version: "version", Resource: "theresource"},
|
||||
infos: []*resource.Info{
|
||||
{
|
||||
Mapping: &meta.RESTMapping{
|
||||
Resource: schema.GroupVersionResource{Group: "group", Version: "version", Resource: "theresource"},
|
||||
},
|
||||
Name: "name-foo",
|
||||
Namespace: "ns-foo",
|
||||
},
|
||||
Name: "name-foo",
|
||||
Namespace: "ns-foo",
|
||||
},
|
||||
fakeClient: func() *dynamicfakeclient.FakeDynamicClient {
|
||||
fakeClient := dynamicfakeclient.NewSimpleDynamicClient(scheme)
|
||||
@ -180,12 +263,14 @@ func TestWaitForDeletion(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "handles watch delete",
|
||||
info: &resource.Info{
|
||||
Mapping: &meta.RESTMapping{
|
||||
Resource: schema.GroupVersionResource{Group: "group", Version: "version", Resource: "theresource"},
|
||||
infos: []*resource.Info{
|
||||
{
|
||||
Mapping: &meta.RESTMapping{
|
||||
Resource: schema.GroupVersionResource{Group: "group", Version: "version", Resource: "theresource"},
|
||||
},
|
||||
Name: "name-foo",
|
||||
Namespace: "ns-foo",
|
||||
},
|
||||
Name: "name-foo",
|
||||
Namespace: "ns-foo",
|
||||
},
|
||||
fakeClient: func() *dynamicfakeclient.FakeDynamicClient {
|
||||
fakeClient := dynamicfakeclient.NewSimpleDynamicClient(scheme)
|
||||
@ -213,13 +298,69 @@ func TestWaitForDeletion(t *testing.T) {
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
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.info),
|
||||
ResourceFinder: genericclioptions.NewSimpleFakeResourceFinder(test.infos...),
|
||||
UIDMap: test.uidMap,
|
||||
DynamicClient: fakeClient,
|
||||
Timeout: test.timeout,
|
||||
|
||||
@ -250,7 +391,7 @@ func TestWaitForCondition(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
info *resource.Info
|
||||
infos []*resource.Info
|
||||
fakeClient func() *dynamicfakeclient.FakeDynamicClient
|
||||
timeout time.Duration
|
||||
|
||||
@ -259,12 +400,14 @@ func TestWaitForCondition(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "present on get",
|
||||
info: &resource.Info{
|
||||
Mapping: &meta.RESTMapping{
|
||||
Resource: schema.GroupVersionResource{Group: "group", Version: "version", Resource: "theresource"},
|
||||
infos: []*resource.Info{
|
||||
{
|
||||
Mapping: &meta.RESTMapping{
|
||||
Resource: schema.GroupVersionResource{Group: "group", Version: "version", Resource: "theresource"},
|
||||
},
|
||||
Name: "name-foo",
|
||||
Namespace: "ns-foo",
|
||||
},
|
||||
Name: "name-foo",
|
||||
Namespace: "ns-foo",
|
||||
},
|
||||
fakeClient: func() *dynamicfakeclient.FakeDynamicClient {
|
||||
fakeClient := dynamicfakeclient.NewSimpleDynamicClient(scheme)
|
||||
@ -288,13 +431,52 @@ func TestWaitForCondition(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "times out",
|
||||
info: &resource.Info{
|
||||
Mapping: &meta.RESTMapping{
|
||||
Resource: schema.GroupVersionResource{Group: "group", Version: "version", Resource: "theresource"},
|
||||
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",
|
||||
},
|
||||
Name: "name-foo",
|
||||
Namespace: "ns-foo",
|
||||
},
|
||||
fakeClient: func() *dynamicfakeclient.FakeDynamicClient {
|
||||
fakeClient := dynamicfakeclient.NewSimpleDynamicClient(scheme)
|
||||
@ -323,12 +505,14 @@ func TestWaitForCondition(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "handles watch close out",
|
||||
info: &resource.Info{
|
||||
Mapping: &meta.RESTMapping{
|
||||
Resource: schema.GroupVersionResource{Group: "group", Version: "version", Resource: "theresource"},
|
||||
infos: []*resource.Info{
|
||||
{
|
||||
Mapping: &meta.RESTMapping{
|
||||
Resource: schema.GroupVersionResource{Group: "group", Version: "version", Resource: "theresource"},
|
||||
},
|
||||
Name: "name-foo",
|
||||
Namespace: "ns-foo",
|
||||
},
|
||||
Name: "name-foo",
|
||||
Namespace: "ns-foo",
|
||||
},
|
||||
fakeClient: func() *dynamicfakeclient.FakeDynamicClient {
|
||||
fakeClient := dynamicfakeclient.NewSimpleDynamicClient(scheme)
|
||||
@ -374,12 +558,14 @@ func TestWaitForCondition(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "handles watch condition change",
|
||||
info: &resource.Info{
|
||||
Mapping: &meta.RESTMapping{
|
||||
Resource: schema.GroupVersionResource{Group: "group", Version: "version", Resource: "theresource"},
|
||||
infos: []*resource.Info{
|
||||
{
|
||||
Mapping: &meta.RESTMapping{
|
||||
Resource: schema.GroupVersionResource{Group: "group", Version: "version", Resource: "theresource"},
|
||||
},
|
||||
Name: "name-foo",
|
||||
Namespace: "ns-foo",
|
||||
},
|
||||
Name: "name-foo",
|
||||
Namespace: "ns-foo",
|
||||
},
|
||||
fakeClient: func() *dynamicfakeclient.FakeDynamicClient {
|
||||
fakeClient := dynamicfakeclient.NewSimpleDynamicClient(scheme)
|
||||
@ -412,12 +598,14 @@ func TestWaitForCondition(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "handles watch created",
|
||||
info: &resource.Info{
|
||||
Mapping: &meta.RESTMapping{
|
||||
Resource: schema.GroupVersionResource{Group: "group", Version: "version", Resource: "theresource"},
|
||||
infos: []*resource.Info{
|
||||
{
|
||||
Mapping: &meta.RESTMapping{
|
||||
Resource: schema.GroupVersionResource{Group: "group", Version: "version", Resource: "theresource"},
|
||||
},
|
||||
Name: "name-foo",
|
||||
Namespace: "ns-foo",
|
||||
},
|
||||
Name: "name-foo",
|
||||
Namespace: "ns-foo",
|
||||
},
|
||||
fakeClient: func() *dynamicfakeclient.FakeDynamicClient {
|
||||
fakeClient := dynamicfakeclient.NewSimpleDynamicClient(scheme)
|
||||
@ -445,18 +633,76 @@ func TestWaitForCondition(t *testing.T) {
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
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.info),
|
||||
ResourceFinder: genericclioptions.NewSimpleFakeResourceFinder(test.infos...),
|
||||
DynamicClient: fakeClient,
|
||||
Timeout: test.timeout,
|
||||
|
||||
Printer: printers.NewDiscardingPrinter(),
|
||||
ConditionFn: ConditionalWait{conditionName: "the-condition", conditionStatus: "status-value"}.IsConditionMet,
|
||||
ConditionFn: ConditionalWait{conditionName: "the-condition", conditionStatus: "status-value", errOut: ioutil.Discard}.IsConditionMet,
|
||||
IOStreams: genericclioptions.NewTestIOStreamsDiscard(),
|
||||
}
|
||||
err := o.RunWait()
|
||||
|
Reference in New Issue
Block a user