From 89a91549abf7a6b15f8aeb17f269a786adc17918 Mon Sep 17 00:00:00 2001 From: Kyle Cronin Date: Fri, 24 Jan 2020 18:54:40 -0500 Subject: [PATCH] feat: Show scale down time for bluegreen replicasets (#370) Display time remaining until BlueGreen replicaset scale down deadline --- .../cmd/get/get_rollout.go | 4 ++ pkg/kubectl-argo-rollouts/cmd/get/get_test.go | 51 +++++++++++++++++++ pkg/kubectl-argo-rollouts/info/info.go | 7 +++ pkg/kubectl-argo-rollouts/info/info_test.go | 48 ++++++++++++----- .../info/replicaset_info.go | 37 +++++++++----- 5 files changed, 121 insertions(+), 26 deletions(-) diff --git a/pkg/kubectl-argo-rollouts/cmd/get/get_rollout.go b/pkg/kubectl-argo-rollouts/cmd/get/get_rollout.go index 83ddd94620..90c3664e5c 100644 --- a/pkg/kubectl-argo-rollouts/cmd/get/get_rollout.go +++ b/pkg/kubectl-argo-rollouts/cmd/get/get_rollout.go @@ -182,6 +182,10 @@ func (o *GetOptions) PrintReplicaSetInfo(w io.Writer, rsInfo info.ReplicaSetInfo infoCols = append(infoCols, o.colorize(info.InfoTagPreview)) name = o.colorizeStatus(name, info.InfoTagPreview) } + if rsInfo.ScaleDownDeadline != "" { + infoCols = append(infoCols, fmt.Sprintf("delay:%s", rsInfo.ScaleDownDelay())) + } + fmt.Fprintf(w, "%s%s %s\t%s\t%s %s\t%s\t%v\n", prefix, IconReplicaSet, name, "ReplicaSet", o.colorize(rsInfo.Icon), rsInfo.Status, rsInfo.Age(), strings.Join(infoCols, ",")) for i, podInfo := range rsInfo.Pods { isLast := i == len(rsInfo.Pods)-1 diff --git a/pkg/kubectl-argo-rollouts/cmd/get/get_test.go b/pkg/kubectl-argo-rollouts/cmd/get/get_test.go index 160f4b0647..a2d37eae30 100644 --- a/pkg/kubectl-argo-rollouts/cmd/get/get_test.go +++ b/pkg/kubectl-argo-rollouts/cmd/get/get_test.go @@ -5,6 +5,10 @@ import ( "fmt" "strings" "testing" + "time" + + "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/stretchr/testify/assert" "k8s.io/cli-runtime/pkg/genericclioptions" @@ -136,6 +140,53 @@ NAME KIND STATUS AGE INFO assertStdout(t, expectedOut, o.IOStreams) } +func TestGetBlueGreenRolloutScaleDownDelay(t *testing.T) { + rolloutObjs := testdata.NewBlueGreenRollout() + inFourHours := metav1.Now().Add(4 * time.Hour).Truncate(time.Second).UTC().Format(time.RFC3339) + rolloutObjs.ReplicaSets[2].Annotations[v1alpha1.DefaultReplicaSetScaleDownDeadlineAnnotationKey] = inFourHours + delete(rolloutObjs.ReplicaSets[2].Labels, v1alpha1.DefaultRolloutUniqueLabelKey) + + tf, o := options.NewFakeArgoRolloutsOptions(rolloutObjs.AllObjects()...) + o.RESTClientGetter = tf.WithNamespace(rolloutObjs.Rollouts[0].Namespace) + defer tf.Cleanup() + cmd := NewCmdGetRollout(o) + cmd.PersistentPreRunE = o.PersistentPreRunE + cmd.SetArgs([]string{rolloutObjs.Rollouts[0].Name, "--no-color"}) + err := cmd.Execute() + assert.NoError(t, err) + + expectedOut := strings.TrimPrefix(` +Name: bluegreen-demo +Namespace: jesse-test +Status: ॥ Paused +Strategy: BlueGreen +Images: argoproj/rollouts-demo:blue (active) + argoproj/rollouts-demo:green +Replicas: + Desired: 3 + Current: 6 + Updated: 3 + Ready: 6 + Available: 3 + +NAME KIND STATUS AGE INFO +⟳ bluegreen-demo Rollout ॥ Paused 7d +├──# revision:11 +│ └──⧉ bluegreen-demo-74b948fccb ReplicaSet ✔ Healthy 7d delay:3h59m +│ ├──□ bluegreen-demo-74b948fccb-5jz59 Pod ✔ Running 7d ready:1/1 +│ ├──□ bluegreen-demo-74b948fccb-mkhrl Pod ✔ Running 7d ready:1/1 +│ └──□ bluegreen-demo-74b948fccb-vvj2t Pod ✔ Running 7d ready:1/1 +├──# revision:10 +│ └──⧉ bluegreen-demo-6cbccd9f99 ReplicaSet ✔ Healthy 7d active +│ ├──□ bluegreen-demo-6cbccd9f99-gk78v Pod ✔ Running 7d ready:1/1 +│ ├──□ bluegreen-demo-6cbccd9f99-kxj8g Pod ✔ Running 7d ready:1/1 +│ └──□ bluegreen-demo-6cbccd9f99-t2d4f Pod ✔ Running 7d ready:1/1 +└──# revision:8 + └──⧉ bluegreen-demo-746d5fddf6 ReplicaSet • ScaledDown 7d +`, "\n") + assertStdout(t, expectedOut, o.IOStreams) +} + func TestGetCanaryRollout(t *testing.T) { rolloutObjs := testdata.NewCanaryRollout() diff --git a/pkg/kubectl-argo-rollouts/info/info.go b/pkg/kubectl-argo-rollouts/info/info.go index 2506d3576e..d5764b90b4 100644 --- a/pkg/kubectl-argo-rollouts/info/info.go +++ b/pkg/kubectl-argo-rollouts/info/info.go @@ -71,3 +71,10 @@ func parseExperimentTemplateName(annots map[string]string) string { } return "" } + +func parseScaleDownDeadline(annots map[string]string) string { + if annots != nil { + return annots[v1alpha1.DefaultReplicaSetScaleDownDeadlineAnnotationKey] + } + return "" +} diff --git a/pkg/kubectl-argo-rollouts/info/info_test.go b/pkg/kubectl-argo-rollouts/info/info_test.go index 997bb9f232..e80d84e78f 100644 --- a/pkg/kubectl-argo-rollouts/info/info_test.go +++ b/pkg/kubectl-argo-rollouts/info/info_test.go @@ -96,21 +96,41 @@ func TestCanaryRolloutInfo(t *testing.T) { } func TestBlueGreenRolloutInfo(t *testing.T) { - rolloutObjs := testdata.NewBlueGreenRollout() - roInfo := NewRolloutInfo(rolloutObjs.Rollouts[0], rolloutObjs.ReplicaSets, rolloutObjs.Pods, rolloutObjs.Experiments, rolloutObjs.AnalysisRuns) - assert.Equal(t, roInfo.Name, rolloutObjs.Rollouts[0].Name) - assert.Len(t, roInfo.Revisions(), 3) + { + rolloutObjs := testdata.NewBlueGreenRollout() + roInfo := NewRolloutInfo(rolloutObjs.Rollouts[0], rolloutObjs.ReplicaSets, rolloutObjs.Pods, rolloutObjs.Experiments, rolloutObjs.AnalysisRuns) + assert.Equal(t, roInfo.Name, rolloutObjs.Rollouts[0].Name) + assert.Len(t, roInfo.Revisions(), 3) - assert.Equal(t, roInfo.Images(), []ImageInfo{ - { - Image: "argoproj/rollouts-demo:blue", - Tags: []string{InfoTagActive}, - }, - { - Image: "argoproj/rollouts-demo:green", - Tags: []string{InfoTagPreview}, - }, - }) + assert.Len(t, roInfo.ReplicaSetsByRevision(11), 1) + assert.Len(t, roInfo.ReplicaSetsByRevision(10), 1) + assert.Len(t, roInfo.ReplicaSetsByRevision(8), 1) + + assert.Equal(t, roInfo.ReplicaSets[0].ScaleDownDeadline, "") + assert.Equal(t, roInfo.ReplicaSets[0].ScaleDownDelay(), "") + + assert.Equal(t, roInfo.Images(), []ImageInfo{ + { + Image: "argoproj/rollouts-demo:blue", + Tags: []string{InfoTagActive}, + }, + { + Image: "argoproj/rollouts-demo:green", + Tags: []string{InfoTagPreview}, + }, + }) + } + { + rolloutObjs := testdata.NewBlueGreenRollout() + inFourHours := metav1.Now().Add(4 * time.Hour).Truncate(time.Second).UTC().Format(time.RFC3339) + rolloutObjs.ReplicaSets[0].Annotations[v1alpha1.DefaultReplicaSetScaleDownDeadlineAnnotationKey] = inFourHours + delayedRs := rolloutObjs.ReplicaSets[0].ObjectMeta.UID + roInfo := NewRolloutInfo(rolloutObjs.Rollouts[0], rolloutObjs.ReplicaSets, rolloutObjs.Pods, rolloutObjs.Experiments, rolloutObjs.AnalysisRuns) + + assert.Equal(t, roInfo.ReplicaSets[1].UID, delayedRs) + assert.Equal(t, roInfo.ReplicaSets[1].ScaleDownDeadline, inFourHours) + assert.Equal(t, roInfo.ReplicaSets[1].ScaleDownDelay(), "3h59m") + } } func TestExperimentAnalysisRolloutInfo(t *testing.T) { diff --git a/pkg/kubectl-argo-rollouts/info/replicaset_info.go b/pkg/kubectl-argo-rollouts/info/replicaset_info.go index 3e0676057a..6f8fc3ac2d 100644 --- a/pkg/kubectl-argo-rollouts/info/replicaset_info.go +++ b/pkg/kubectl-argo-rollouts/info/replicaset_info.go @@ -2,28 +2,33 @@ package info import ( "sort" + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/duration" "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" ) type ReplicaSetInfo struct { Metadata - Status string - Icon string - Revision int - Stable bool - Canary bool - Active bool - Preview bool - Replicas int32 - Available int32 - Template string - Images []string - Pods []PodInfo + Status string + Icon string + Revision int + Stable bool + Canary bool + Active bool + Preview bool + Replicas int32 + Available int32 + Template string + ScaleDownDeadline string + Images []string + Pods []PodInfo } func getReplicaSetInfo(ownerUID types.UID, ro *v1alpha1.Rollout, allReplicaSets []*appsv1.ReplicaSet, allPods []*corev1.Pod) []ReplicaSetInfo { @@ -47,6 +52,7 @@ func getReplicaSetInfo(ownerUID types.UID, ro *v1alpha1.Rollout, allReplicaSets rsInfo.Icon = replicaSetIcon(rsInfo.Status) rsInfo.Revision = parseRevision(rs.ObjectMeta.Annotations) rsInfo.Template = parseExperimentTemplateName(rs.ObjectMeta.Annotations) + rsInfo.ScaleDownDeadline = parseScaleDownDeadline(rs.ObjectMeta.Annotations) if ro != nil { if ro.Spec.Strategy.Canary != nil && rs.Labels != nil { @@ -122,3 +128,10 @@ func getReplicaSetCondition(status appsv1.ReplicaSetStatus, condType appsv1.Repl } return nil } + +func (rs ReplicaSetInfo) ScaleDownDelay() string { + if deadline, err := time.Parse(time.RFC3339, rs.ScaleDownDeadline); err == nil { + return duration.HumanDuration(deadline.Sub(metav1.Now().Time)) + } + return "" +}