Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

kubectl: add --revision flag in rollout status #34443

Merged
merged 1 commit into from Oct 10, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions hack/make-rules/test-cmd.sh
Expand Up @@ -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[@]}"

Expand Down
12 changes: 9 additions & 3 deletions pkg/controller/deployment/util/deployment_util.go
Expand Up @@ -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"
Expand Down Expand Up @@ -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
}
Expand Down
3 changes: 3 additions & 0 deletions pkg/kubectl/cmd/rollout/rollout_history.go
Expand Up @@ -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()

Expand Down
23 changes: 16 additions & 7 deletions pkg/kubectl/cmd/rollout/rollout_status.go
Expand Up @@ -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`)
Expand All @@ -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) {
Expand All @@ -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
}

Expand Down Expand Up @@ -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
}
Expand All @@ -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
}
Expand Down
18 changes: 14 additions & 4 deletions pkg/kubectl/rollout_status.go
Expand Up @@ -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) {
Expand All @@ -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
Expand All @@ -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
}
4 changes: 2 additions & 2 deletions pkg/kubectl/rollout_status_test.go
Expand Up @@ -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,
},
{
Expand Down Expand Up @@ -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)
}
Expand Down