...

Source file src/k8s.io/kubectl/pkg/cmd/rollout/rollout_status.go

Documentation: k8s.io/kubectl/pkg/cmd/rollout

     1  /*
     2  Copyright 2016 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package rollout
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"time"
    23  
    24  	"github.com/spf13/cobra"
    25  
    26  	"k8s.io/apimachinery/pkg/api/meta"
    27  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    29  	"k8s.io/apimachinery/pkg/fields"
    30  	"k8s.io/apimachinery/pkg/runtime"
    31  	"k8s.io/apimachinery/pkg/watch"
    32  	"k8s.io/cli-runtime/pkg/genericclioptions"
    33  	"k8s.io/cli-runtime/pkg/genericiooptions"
    34  	"k8s.io/cli-runtime/pkg/resource"
    35  	"k8s.io/client-go/dynamic"
    36  	"k8s.io/client-go/tools/cache"
    37  	watchtools "k8s.io/client-go/tools/watch"
    38  	cmdutil "k8s.io/kubectl/pkg/cmd/util"
    39  	"k8s.io/kubectl/pkg/polymorphichelpers"
    40  	"k8s.io/kubectl/pkg/scheme"
    41  	"k8s.io/kubectl/pkg/util/completion"
    42  	"k8s.io/kubectl/pkg/util/i18n"
    43  	"k8s.io/kubectl/pkg/util/interrupt"
    44  	"k8s.io/kubectl/pkg/util/templates"
    45  )
    46  
    47  var (
    48  	statusLong = templates.LongDesc(i18n.T(`
    49  		Show the status of the rollout.
    50  
    51  		By default 'rollout status' will watch the status of the latest rollout
    52  		until it's done. If you don't want to wait for the rollout to finish then
    53  		you can use --watch=false. Note that if a new rollout starts in-between, then
    54  		'rollout status' will continue watching the latest revision. If you want to
    55  		pin to a specific revision and abort if it is rolled over by another revision,
    56  		use --revision=N where N is the revision you need to watch for.`))
    57  
    58  	statusExample = templates.Examples(`
    59  		# Watch the rollout status of a deployment
    60  		kubectl rollout status deployment/nginx`)
    61  )
    62  
    63  // RolloutStatusOptions holds the command-line options for 'rollout status' sub command
    64  type RolloutStatusOptions struct {
    65  	PrintFlags *genericclioptions.PrintFlags
    66  
    67  	Namespace        string
    68  	EnforceNamespace bool
    69  	BuilderArgs      []string
    70  	LabelSelector    string
    71  
    72  	Watch    bool
    73  	Revision int64
    74  	Timeout  time.Duration
    75  
    76  	StatusViewerFn func(*meta.RESTMapping) (polymorphichelpers.StatusViewer, error)
    77  	Builder        func() *resource.Builder
    78  	DynamicClient  dynamic.Interface
    79  
    80  	FilenameOptions *resource.FilenameOptions
    81  	genericiooptions.IOStreams
    82  }
    83  
    84  // NewRolloutStatusOptions returns an initialized RolloutStatusOptions instance
    85  func NewRolloutStatusOptions(streams genericiooptions.IOStreams) *RolloutStatusOptions {
    86  	return &RolloutStatusOptions{
    87  		PrintFlags:      genericclioptions.NewPrintFlags("").WithTypeSetter(scheme.Scheme),
    88  		FilenameOptions: &resource.FilenameOptions{},
    89  		IOStreams:       streams,
    90  		Watch:           true,
    91  		Timeout:         0,
    92  	}
    93  }
    94  
    95  // NewCmdRolloutStatus returns a Command instance for the 'rollout status' sub command
    96  func NewCmdRolloutStatus(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command {
    97  	o := NewRolloutStatusOptions(streams)
    98  
    99  	validArgs := []string{"deployment", "daemonset", "statefulset"}
   100  
   101  	cmd := &cobra.Command{
   102  		Use:                   "status (TYPE NAME | TYPE/NAME) [flags]",
   103  		DisableFlagsInUseLine: true,
   104  		Short:                 i18n.T("Show the status of the rollout"),
   105  		Long:                  statusLong,
   106  		Example:               statusExample,
   107  		ValidArgsFunction:     completion.SpecifiedResourceTypeAndNameNoRepeatCompletionFunc(f, validArgs),
   108  		Run: func(cmd *cobra.Command, args []string) {
   109  			cmdutil.CheckErr(o.Complete(f, args))
   110  			cmdutil.CheckErr(o.Validate())
   111  			cmdutil.CheckErr(o.Run())
   112  		},
   113  	}
   114  
   115  	usage := "identifying the resource to get from a server."
   116  	cmdutil.AddFilenameOptionFlags(cmd, o.FilenameOptions, usage)
   117  	cmd.Flags().BoolVarP(&o.Watch, "watch", "w", o.Watch, "Watch the status of the rollout until it's done.")
   118  	cmd.Flags().Int64Var(&o.Revision, "revision", o.Revision, "Pin to a specific revision for showing its status. Defaults to 0 (last revision).")
   119  	cmd.Flags().DurationVar(&o.Timeout, "timeout", o.Timeout, "The length of time to wait before ending watch, zero means never. Any other values should contain a corresponding time unit (e.g. 1s, 2m, 3h).")
   120  	cmdutil.AddLabelSelectorFlagVar(cmd, &o.LabelSelector)
   121  
   122  	return cmd
   123  }
   124  
   125  // Complete completes all the required options
   126  func (o *RolloutStatusOptions) Complete(f cmdutil.Factory, args []string) error {
   127  	o.Builder = f.NewBuilder
   128  
   129  	var err error
   130  	o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace()
   131  	if err != nil {
   132  		return err
   133  	}
   134  
   135  	o.BuilderArgs = args
   136  	o.StatusViewerFn = polymorphichelpers.StatusViewerFn
   137  
   138  	o.DynamicClient, err = f.DynamicClient()
   139  	if err != nil {
   140  		return err
   141  	}
   142  
   143  	return nil
   144  }
   145  
   146  // Validate makes sure all the provided values for command-line options are valid
   147  func (o *RolloutStatusOptions) Validate() error {
   148  	if len(o.BuilderArgs) == 0 && cmdutil.IsFilenameSliceEmpty(o.FilenameOptions.Filenames, o.FilenameOptions.Kustomize) {
   149  		return fmt.Errorf("required resource not specified")
   150  	}
   151  
   152  	if o.Revision < 0 {
   153  		return fmt.Errorf("revision must be a positive integer: %v", o.Revision)
   154  	}
   155  
   156  	return nil
   157  }
   158  
   159  // Run performs the execution of 'rollout status' sub command
   160  func (o *RolloutStatusOptions) Run() error {
   161  	r := o.Builder().
   162  		WithScheme(scheme.Scheme, scheme.Scheme.PrioritizedVersionsAllGroups()...).
   163  		NamespaceParam(o.Namespace).DefaultNamespace().
   164  		LabelSelectorParam(o.LabelSelector).
   165  		FilenameParam(o.EnforceNamespace, o.FilenameOptions).
   166  		ResourceTypeOrNameArgs(true, o.BuilderArgs...).
   167  		ContinueOnError().
   168  		Latest().
   169  		Flatten().
   170  		Do()
   171  
   172  	err := r.Err()
   173  	if err != nil {
   174  		return err
   175  	}
   176  
   177  	resourceFound := false
   178  	err = r.Visit(func(info *resource.Info, _ error) error {
   179  		resourceFound = true
   180  		mapping := info.ResourceMapping()
   181  		statusViewer, err := o.StatusViewerFn(mapping)
   182  		if err != nil {
   183  			return err
   184  		}
   185  
   186  		fieldSelector := fields.OneTermEqualSelector("metadata.name", info.Name).String()
   187  		lw := &cache.ListWatch{
   188  			ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
   189  				options.FieldSelector = fieldSelector
   190  				return o.DynamicClient.Resource(info.Mapping.Resource).Namespace(info.Namespace).List(context.TODO(), options)
   191  			},
   192  			WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
   193  				options.FieldSelector = fieldSelector
   194  				return o.DynamicClient.Resource(info.Mapping.Resource).Namespace(info.Namespace).Watch(context.TODO(), options)
   195  			},
   196  		}
   197  
   198  		// if the rollout isn't done yet, keep watching deployment status
   199  		ctx, cancel := watchtools.ContextWithOptionalTimeout(context.Background(), o.Timeout)
   200  		intr := interrupt.New(nil, cancel)
   201  		return intr.Run(func() error {
   202  			_, err = watchtools.UntilWithSync(ctx, lw, &unstructured.Unstructured{}, nil, func(e watch.Event) (bool, error) {
   203  				switch t := e.Type; t {
   204  				case watch.Added, watch.Modified:
   205  					status, done, err := statusViewer.Status(e.Object.(runtime.Unstructured), o.Revision)
   206  					if err != nil {
   207  						return false, err
   208  					}
   209  					fmt.Fprintf(o.Out, "%s", status)
   210  					// Quit waiting if the rollout is done
   211  					if done {
   212  						return true, nil
   213  					}
   214  
   215  					shouldWatch := o.Watch
   216  					if !shouldWatch {
   217  						return true, nil
   218  					}
   219  
   220  					return false, nil
   221  
   222  				case watch.Deleted:
   223  					// We need to abort to avoid cases of recreation and not to silently watch the wrong (new) object
   224  					return true, fmt.Errorf("object has been deleted")
   225  
   226  				default:
   227  					return true, fmt.Errorf("internal error: unexpected event %#v", e)
   228  				}
   229  			})
   230  			return err
   231  		})
   232  	})
   233  
   234  	if err != nil {
   235  		return err
   236  	}
   237  
   238  	if !resourceFound {
   239  		fmt.Fprintf(o.ErrOut, "No resources found in %s namespace.\n", o.Namespace)
   240  	}
   241  
   242  	return nil
   243  }
   244  

View as plain text