Add generated file
This PR adds generated files under pkg/client and vendor folder.
This commit is contained in:
324
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/wait/wait.go
generated
vendored
Normal file
324
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/wait/wait.go
generated
vendored
Normal file
@@ -0,0 +1,324 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package wait
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"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/util/wait"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/client-go/dynamic"
|
||||
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"
|
||||
)
|
||||
|
||||
// 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
|
||||
type WaitFlags struct {
|
||||
RESTClientGetter genericclioptions.RESTClientGetter
|
||||
PrintFlags *genericclioptions.PrintFlags
|
||||
ResourceBuilderFlags *genericclioptions.ResourceBuilderFlags
|
||||
|
||||
Timeout time.Duration
|
||||
ForCondition string
|
||||
|
||||
genericclioptions.IOStreams
|
||||
}
|
||||
|
||||
// NewWaitFlags returns a default WaitFlags
|
||||
func NewWaitFlags(restClientGetter genericclioptions.RESTClientGetter, streams genericclioptions.IOStreams) *WaitFlags {
|
||||
return &WaitFlags{
|
||||
RESTClientGetter: restClientGetter,
|
||||
PrintFlags: genericclioptions.NewPrintFlags("condition met"),
|
||||
ResourceBuilderFlags: genericclioptions.NewResourceBuilderFlags().
|
||||
WithLabelSelector("").
|
||||
WithAllNamespaces(false).
|
||||
WithLatest(),
|
||||
|
||||
Timeout: 30 * time.Second,
|
||||
|
||||
IOStreams: streams,
|
||||
}
|
||||
}
|
||||
|
||||
// NewCmdWait returns a cobra command for waiting
|
||||
func NewCmdWait(restClientGetter genericclioptions.RESTClientGetter, streams genericclioptions.IOStreams) *cobra.Command {
|
||||
flags := NewWaitFlags(restClientGetter, streams)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "wait resource.group/name [--for=delete|--for condition=available]",
|
||||
DisableFlagsInUseLine: true,
|
||||
Short: "Experimental: Wait for one condition on one or many resources",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
o, err := flags.ToOptions(args)
|
||||
cmdutil.CheckErr(err)
|
||||
err = o.RunWait()
|
||||
cmdutil.CheckErr(err)
|
||||
},
|
||||
SuggestFor: []string{"list", "ps"},
|
||||
}
|
||||
|
||||
flags.AddFlags(cmd)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// AddFlags registers flags for a cli
|
||||
func (flags *WaitFlags) AddFlags(cmd *cobra.Command) {
|
||||
flags.PrintFlags.AddFlags(cmd)
|
||||
flags.ResourceBuilderFlags.AddFlags(cmd.Flags())
|
||||
|
||||
cmd.Flags().DurationVar(&flags.Timeout, "timeout", flags.Timeout, "The length of time to wait before giving up. Zero means check once and don't wait, negative means wait for a week.")
|
||||
cmd.Flags().StringVar(&flags.ForCondition, "for", flags.ForCondition, "The condition to wait on: [delete|condition=condition-name].")
|
||||
}
|
||||
|
||||
// ToOptions converts from CLI inputs to runtime inputs
|
||||
func (flags *WaitFlags) ToOptions(args []string) (*WaitOptions, error) {
|
||||
printer, err := flags.PrintFlags.ToPrinter()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
builder := flags.ResourceBuilderFlags.ToBuilder(flags.RESTClientGetter, args)
|
||||
clientConfig, err := flags.RESTClientGetter.ToRESTConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dynamicClient, err := dynamic.NewForConfig(clientConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conditionFn, err := conditionFuncFor(flags.ForCondition)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
effectiveTimeout := flags.Timeout
|
||||
if effectiveTimeout < 0 {
|
||||
effectiveTimeout = 168 * time.Hour
|
||||
}
|
||||
|
||||
o := &WaitOptions{
|
||||
ResourceFinder: builder,
|
||||
DynamicClient: dynamicClient,
|
||||
Timeout: effectiveTimeout,
|
||||
|
||||
Printer: printer,
|
||||
ConditionFn: conditionFn,
|
||||
IOStreams: flags.IOStreams,
|
||||
}
|
||||
|
||||
return o, nil
|
||||
}
|
||||
|
||||
func conditionFuncFor(condition string) (ConditionFunc, error) {
|
||||
if strings.ToLower(condition) == "delete" {
|
||||
return IsDeleted, nil
|
||||
}
|
||||
if strings.HasPrefix(condition, "condition=") {
|
||||
conditionName := condition[len("condition="):]
|
||||
return ConditionalWait{
|
||||
conditionName: conditionName,
|
||||
// TODO allow specifying a false
|
||||
conditionStatus: "true",
|
||||
}.IsConditionMet, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unrecognized condition: %q", condition)
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
Printer printers.ResourcePrinter
|
||||
ConditionFn ConditionFunc
|
||||
genericclioptions.IOStreams
|
||||
}
|
||||
|
||||
// ConditionFunc is the interface for providing condition checks
|
||||
type ConditionFunc func(info *resource.Info, o *WaitOptions) (finalObject runtime.Object, done bool, err error)
|
||||
|
||||
// RunWait runs the waiting logic
|
||||
func (o *WaitOptions) RunWait() error {
|
||||
return o.ResourceFinder.Do().Visit(func(info *resource.Info, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
finalObject, success, err := o.ConditionFn(info, o)
|
||||
if success {
|
||||
o.Printer.PrintObj(finalObject, o.Out)
|
||||
return nil
|
||||
}
|
||||
if err == nil {
|
||||
return fmt.Errorf("%v unsatisified for unknown reason", finalObject)
|
||||
}
|
||||
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 {
|
||||
gottenObj, err := o.DynamicClient.Resource(info.Mapping.Resource).Namespace(info.Namespace).Get(info.Name, metav1.GetOptions{})
|
||||
if errors.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
|
||||
}
|
||||
|
||||
watchOptions := metav1.ListOptions{}
|
||||
watchOptions.FieldSelector = "metadata.name=" + info.Name
|
||||
watchOptions.ResourceVersion = gottenObj.GetResourceVersion()
|
||||
objWatch, err := o.DynamicClient.Resource(info.Mapping.Resource).Namespace(info.Namespace).Watch(watchOptions)
|
||||
if err != nil {
|
||||
return gottenObj, false, err
|
||||
}
|
||||
|
||||
timeout := endTime.Sub(time.Now())
|
||||
if timeout < 0 {
|
||||
// we're out of time
|
||||
return gottenObj, false, wait.ErrWaitTimeout
|
||||
}
|
||||
watchEvent, err := watch.Until(o.Timeout, objWatch, isDeleted)
|
||||
switch {
|
||||
case err == nil:
|
||||
return watchEvent.Object, true, nil
|
||||
case err == watch.ErrWatchClosed:
|
||||
continue
|
||||
case err == wait.ErrWaitTimeout:
|
||||
if watchEvent != nil {
|
||||
return watchEvent.Object, false, wait.ErrWaitTimeout
|
||||
}
|
||||
return gottenObj, false, wait.ErrWaitTimeout
|
||||
default:
|
||||
return gottenObj, false, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func isDeleted(event watch.Event) (bool, error) {
|
||||
return event.Type == watch.Deleted, nil
|
||||
}
|
||||
|
||||
// ConditionalWait hold information to check an API status condition
|
||||
type ConditionalWait struct {
|
||||
conditionName string
|
||||
conditionStatus string
|
||||
}
|
||||
|
||||
// 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 {
|
||||
resourceVersion := ""
|
||||
gottenObj, err := o.DynamicClient.Resource(info.Mapping.Resource).Namespace(info.Namespace).Get(info.Name, metav1.GetOptions{})
|
||||
switch {
|
||||
case errors.IsNotFound(err):
|
||||
resourceVersion = "0"
|
||||
case err != nil:
|
||||
return info.Object, false, err
|
||||
default:
|
||||
conditionMet, err := w.checkCondition(gottenObj)
|
||||
if conditionMet {
|
||||
return gottenObj, true, nil
|
||||
}
|
||||
if err != nil {
|
||||
return gottenObj, false, err
|
||||
}
|
||||
resourceVersion = gottenObj.GetResourceVersion()
|
||||
}
|
||||
|
||||
watchOptions := metav1.ListOptions{}
|
||||
watchOptions.FieldSelector = "metadata.name=" + info.Name
|
||||
watchOptions.ResourceVersion = resourceVersion
|
||||
objWatch, err := o.DynamicClient.Resource(info.Mapping.Resource).Namespace(info.Namespace).Watch(watchOptions)
|
||||
if err != nil {
|
||||
return gottenObj, false, err
|
||||
}
|
||||
|
||||
timeout := endTime.Sub(time.Now())
|
||||
if timeout < 0 {
|
||||
// we're out of time
|
||||
return gottenObj, false, wait.ErrWaitTimeout
|
||||
}
|
||||
watchEvent, err := watch.Until(o.Timeout, objWatch, w.isConditionMet)
|
||||
switch {
|
||||
case err == nil:
|
||||
return watchEvent.Object, true, nil
|
||||
case err == watch.ErrWatchClosed:
|
||||
continue
|
||||
case err == wait.ErrWaitTimeout:
|
||||
if watchEvent != nil {
|
||||
return watchEvent.Object, false, wait.ErrWaitTimeout
|
||||
}
|
||||
return gottenObj, false, wait.ErrWaitTimeout
|
||||
default:
|
||||
return gottenObj, false, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (w ConditionalWait) checkCondition(obj *unstructured.Unstructured) (bool, error) {
|
||||
conditions, found, err := unstructured.NestedSlice(obj.Object, "status", "conditions")
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if !found {
|
||||
return false, nil
|
||||
}
|
||||
for _, conditionUncast := range conditions {
|
||||
condition := conditionUncast.(map[string]interface{})
|
||||
name, found, err := unstructured.NestedString(condition, "type")
|
||||
if !found || err != nil || strings.ToLower(name) != strings.ToLower(w.conditionName) {
|
||||
continue
|
||||
}
|
||||
status, found, err := unstructured.NestedString(condition, "status")
|
||||
if !found || err != nil {
|
||||
continue
|
||||
}
|
||||
return strings.ToLower(status) == strings.ToLower(w.conditionStatus), nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (w ConditionalWait) isConditionMet(event watch.Event) (bool, error) {
|
||||
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
|
||||
}
|
||||
obj := event.Object.(*unstructured.Unstructured)
|
||||
return w.checkCondition(obj)
|
||||
}
|
Reference in New Issue
Block a user