Merge pull request #34443 from kargakis/add-revision-flag-in-rollout-status

Automatic merge from submit-queue

kubectl: add --revision flag in rollout status

Fixes https://github.com/kubernetes/kubernetes/issues/33185

@kubernetes/kubectl ptal
pull/6/head
Kubernetes Submit Queue 2016-10-10 07:32:26 -07:00 committed by GitHub
commit 5859bebf13
6 changed files with 46 additions and 16 deletions

View File

@ -2168,6 +2168,8 @@ __EOF__
kubectl rollout undo deployment nginx "${kube_flags[@]}"
# Check that the new replica set (nginx-618515232) has all old revisions stored in an annotation
kubectl get rs nginx-618515232 -o yaml | grep "deployment.kubernetes.io/revision-history: 1,3"
# Check that trying to watch the status of a superseded revision returns an error
! kubectl rollout status deployment/nginx --revision=3
# Clean up
kubectl delete deployment nginx "${kube_flags[@]}"

View File

@ -27,11 +27,13 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/annotations"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/extensions"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/controller"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util/errors"
"k8s.io/kubernetes/pkg/util/integer"
intstrutil "k8s.io/kubernetes/pkg/util/intstr"
@ -117,9 +119,13 @@ func LastRevision(allRSs []*extensions.ReplicaSet) int64 {
return secMax
}
// Revision returns the revision number of the input replica set
func Revision(rs *extensions.ReplicaSet) (int64, error) {
v, ok := rs.Annotations[RevisionAnnotation]
// Revision returns the revision number of the input object.
func Revision(obj runtime.Object) (int64, error) {
acc, err := meta.Accessor(obj)
if err != nil {
return 0, err
}
v, ok := acc.GetAnnotations()[RevisionAnnotation]
if !ok {
return 0, nil
}

View File

@ -68,6 +68,9 @@ func RunHistory(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, args []st
return cmdutil.UsageError(cmd, "Required resource not specified.")
}
revision := cmdutil.GetFlagInt64(cmd, "revision")
if revision < 0 {
return fmt.Errorf("revision must be a positive integer: %v", revision)
}
mapper, typer := f.Object()

View File

@ -32,11 +32,14 @@ import (
var (
status_long = dedent.Dedent(`
Show the status of the newest rollout.
Show the status of the rollout.
By default 'rollout status' will watch the status of the newest rollout
By default 'rollout status' will watch the status of the latest rollout
until it's done. If you don't want to wait for the rollout to finish then
you can use --watch=false.`)
you can use --watch=false. Note that if a new rollout starts in-between, then
'rollout status' will continue watching the latest revision. If you want to
pin to a specific revision and abort if it is rolled over by another revision,
use --revision=N where N is the revision you need to watch for.`)
status_example = dedent.Dedent(`
# Watch the rollout status of a deployment
kubectl rollout status deployment/nginx`)
@ -50,7 +53,7 @@ func NewCmdRolloutStatus(f *cmdutil.Factory, out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "status (TYPE NAME | TYPE/NAME) [flags]",
Short: "Show the status of newest rollout",
Short: "Show the status of the rollout",
Long: status_long,
Example: status_example,
Run: func(cmd *cobra.Command, args []string) {
@ -62,7 +65,8 @@ func NewCmdRolloutStatus(f *cmdutil.Factory, out io.Writer) *cobra.Command {
usage := "identifying the resource to get from a server."
cmdutil.AddFilenameOptionFlags(cmd, options, usage)
cmd.Flags().BoolP("watch", "w", true, "Watch the status of the newest rollout until it's done.")
cmd.Flags().BoolP("watch", "w", true, "Watch the status of the rollout until it's done.")
cmd.Flags().Int64("revision", 0, "Pin to a specific revision for showing its status. Defaults to 0 (last revision).")
return cmd
}
@ -114,8 +118,13 @@ func RunStatus(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, args []str
return err
}
revision := cmdutil.GetFlagInt64(cmd, "revision")
if revision < 0 {
return fmt.Errorf("revision must be a positive integer: %v", revision)
}
// check if deployment's has finished the rollout
status, done, err := statusViewer.Status(cmdNamespace, info.Name)
status, done, err := statusViewer.Status(cmdNamespace, info.Name, revision)
if err != nil {
return err
}
@ -140,7 +149,7 @@ func RunStatus(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, args []str
return intr.Run(func() error {
_, err := watch.Until(0, w, func(e watch.Event) (bool, error) {
// print deployment's status
status, done, err := statusViewer.Status(cmdNamespace, info.Name)
status, done, err := statusViewer.Status(cmdNamespace, info.Name, revision)
if err != nil {
return false, err
}

View File

@ -23,11 +23,12 @@ import (
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
extensionsclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/unversioned"
"k8s.io/kubernetes/pkg/controller/deployment/util"
)
// StatusViewer provides an interface for resources that provides rollout status.
// StatusViewer provides an interface for resources that have rollout status.
type StatusViewer interface {
Status(namespace, name string) (string, bool, error)
Status(namespace, name string, revision int64) (string, bool, error)
}
func StatusViewerFor(kind unversioned.GroupKind, c internalclientset.Interface) (StatusViewer, error) {
@ -43,11 +44,20 @@ type DeploymentStatusViewer struct {
}
// Status returns a message describing deployment status, and a bool value indicating if the status is considered done
func (s *DeploymentStatusViewer) Status(namespace, name string) (string, bool, error) {
func (s *DeploymentStatusViewer) Status(namespace, name string, revision int64) (string, bool, error) {
deployment, err := s.c.Deployments(namespace).Get(name)
if err != nil {
return "", false, err
}
if revision > 0 {
deploymentRev, err := util.Revision(deployment)
if err != nil {
return "", false, fmt.Errorf("cannot get the revision of deployment %q: %v", deployment.Name, err)
}
if revision != deploymentRev {
return "", false, fmt.Errorf("desired revision (%d) is different from the running revision (%d)", revision, deploymentRev)
}
}
if deployment.Generation <= deployment.Status.ObservedGeneration {
if deployment.Status.UpdatedReplicas < deployment.Spec.Replicas {
return fmt.Sprintf("Waiting for rollout to finish: %d out of %d new replicas have been updated...\n", deployment.Status.UpdatedReplicas, deployment.Spec.Replicas), false, nil
@ -58,7 +68,7 @@ func (s *DeploymentStatusViewer) Status(namespace, name string) (string, bool, e
if deployment.Status.AvailableReplicas < deployment.Status.UpdatedReplicas {
return fmt.Sprintf("Waiting for rollout to finish: %d of %d updated replicas are available...\n", deployment.Status.AvailableReplicas, deployment.Status.UpdatedReplicas), false, nil
}
return fmt.Sprintf("deployment %s successfully rolled out\n", name), true, nil
return fmt.Sprintf("deployment %q successfully rolled out\n", name), true, nil
}
return fmt.Sprintf("Waiting for deployment spec update to be observed...\n"), false, nil
}

View File

@ -85,7 +85,7 @@ func TestDeploymentStatusViewerStatus(t *testing.T) {
UnavailableReplicas: 0,
},
msg: "deployment foo successfully rolled out\n",
msg: "deployment \"foo\" successfully rolled out\n",
done: true,
},
{
@ -119,7 +119,7 @@ func TestDeploymentStatusViewerStatus(t *testing.T) {
}
client := fake.NewSimpleClientset(d).Extensions()
dsv := &DeploymentStatusViewer{c: client}
msg, done, err := dsv.Status("bar", "foo")
msg, done, err := dsv.Status("bar", "foo", 0)
if err != nil {
t.Fatalf("DeploymentStatusViewer.Status(): %v", err)
}