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[@]}" kubectl rollout undo deployment nginx "${kube_flags[@]}"
# Check that the new replica set (nginx-618515232) has all old revisions stored in an annotation # 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" 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 # Clean up
kubectl delete deployment nginx "${kube_flags[@]}" kubectl delete deployment nginx "${kube_flags[@]}"

View File

@ -27,11 +27,13 @@ import (
"k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/annotations" "k8s.io/kubernetes/pkg/api/annotations"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/apis/extensions"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/controller" "k8s.io/kubernetes/pkg/controller"
"k8s.io/kubernetes/pkg/labels" "k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util/errors" "k8s.io/kubernetes/pkg/util/errors"
"k8s.io/kubernetes/pkg/util/integer" "k8s.io/kubernetes/pkg/util/integer"
intstrutil "k8s.io/kubernetes/pkg/util/intstr" intstrutil "k8s.io/kubernetes/pkg/util/intstr"
@ -117,9 +119,13 @@ func LastRevision(allRSs []*extensions.ReplicaSet) int64 {
return secMax return secMax
} }
// Revision returns the revision number of the input replica set // Revision returns the revision number of the input object.
func Revision(rs *extensions.ReplicaSet) (int64, error) { func Revision(obj runtime.Object) (int64, error) {
v, ok := rs.Annotations[RevisionAnnotation] acc, err := meta.Accessor(obj)
if err != nil {
return 0, err
}
v, ok := acc.GetAnnotations()[RevisionAnnotation]
if !ok { if !ok {
return 0, nil 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.") return cmdutil.UsageError(cmd, "Required resource not specified.")
} }
revision := cmdutil.GetFlagInt64(cmd, "revision") revision := cmdutil.GetFlagInt64(cmd, "revision")
if revision < 0 {
return fmt.Errorf("revision must be a positive integer: %v", revision)
}
mapper, typer := f.Object() mapper, typer := f.Object()

View File

@ -32,11 +32,14 @@ import (
var ( var (
status_long = dedent.Dedent(` 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 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(` status_example = dedent.Dedent(`
# Watch the rollout status of a deployment # Watch the rollout status of a deployment
kubectl rollout status deployment/nginx`) kubectl rollout status deployment/nginx`)
@ -50,7 +53,7 @@ func NewCmdRolloutStatus(f *cmdutil.Factory, out io.Writer) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "status (TYPE NAME | TYPE/NAME) [flags]", Use: "status (TYPE NAME | TYPE/NAME) [flags]",
Short: "Show the status of newest rollout", Short: "Show the status of the rollout",
Long: status_long, Long: status_long,
Example: status_example, Example: status_example,
Run: func(cmd *cobra.Command, args []string) { 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." usage := "identifying the resource to get from a server."
cmdutil.AddFilenameOptionFlags(cmd, options, usage) 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 return cmd
} }
@ -114,8 +118,13 @@ func RunStatus(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, args []str
return err 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 // 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 { if err != nil {
return err return err
} }
@ -140,7 +149,7 @@ func RunStatus(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, args []str
return intr.Run(func() error { return intr.Run(func() error {
_, err := watch.Until(0, w, func(e watch.Event) (bool, error) { _, err := watch.Until(0, w, func(e watch.Event) (bool, error) {
// print deployment's status // print deployment's status
status, done, err := statusViewer.Status(cmdNamespace, info.Name) status, done, err := statusViewer.Status(cmdNamespace, info.Name, revision)
if err != nil { if err != nil {
return false, err return false, err
} }

View File

@ -23,11 +23,12 @@ import (
"k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
extensionsclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/unversioned" 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 { 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) { 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 // 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) deployment, err := s.c.Deployments(namespace).Get(name)
if err != nil { if err != nil {
return "", false, err 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.Generation <= deployment.Status.ObservedGeneration {
if deployment.Status.UpdatedReplicas < deployment.Spec.Replicas { 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 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 { 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("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 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, UnavailableReplicas: 0,
}, },
msg: "deployment foo successfully rolled out\n", msg: "deployment \"foo\" successfully rolled out\n",
done: true, done: true,
}, },
{ {
@ -119,7 +119,7 @@ func TestDeploymentStatusViewerStatus(t *testing.T) {
} }
client := fake.NewSimpleClientset(d).Extensions() client := fake.NewSimpleClientset(d).Extensions()
dsv := &DeploymentStatusViewer{c: client} dsv := &DeploymentStatusViewer{c: client}
msg, done, err := dsv.Status("bar", "foo") msg, done, err := dsv.Status("bar", "foo", 0)
if err != nil { if err != nil {
t.Fatalf("DeploymentStatusViewer.Status(): %v", err) t.Fatalf("DeploymentStatusViewer.Status(): %v", err)
} }