From 2749dbdc9a1f1e6947278e70daed966749d80426 Mon Sep 17 00:00:00 2001 From: Dinesh Date: Sat, 21 Dec 2019 15:16:50 +0530 Subject: [PATCH] Describe sts on rollout history if the revision Signed-off-by: Dinesh --- .../kubectl/pkg/polymorphichelpers/BUILD | 1 + .../kubectl/pkg/polymorphichelpers/history.go | 44 ++++++++++-- .../pkg/polymorphichelpers/history_test.go | 69 +++++++++++++------ 3 files changed, 89 insertions(+), 25 deletions(-) diff --git a/staging/src/k8s.io/kubectl/pkg/polymorphichelpers/BUILD b/staging/src/k8s.io/kubectl/pkg/polymorphichelpers/BUILD index 37ff8eba97a..0b4d9f91a12 100644 --- a/staging/src/k8s.io/kubectl/pkg/polymorphichelpers/BUILD +++ b/staging/src/k8s.io/kubectl/pkg/polymorphichelpers/BUILD @@ -95,6 +95,7 @@ go_test( "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/types:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/util/json:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/watch:go_default_library", "//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library", "//staging/src/k8s.io/client-go/testing:go_default_library", diff --git a/staging/src/k8s.io/kubectl/pkg/polymorphichelpers/history.go b/staging/src/k8s.io/kubectl/pkg/polymorphichelpers/history.go index 0d00f4ab009..7baa1f0b38f 100644 --- a/staging/src/k8s.io/kubectl/pkg/polymorphichelpers/history.go +++ b/staging/src/k8s.io/kubectl/pkg/polymorphichelpers/history.go @@ -233,9 +233,8 @@ type StatefulSetHistoryViewer struct { // ViewHistory returns a list of the revision history of a statefulset // TODO: this should be a describer -// TODO: needs to implement detailed revision view func (h *StatefulSetHistoryViewer) ViewHistory(namespace, name string, revision int64) (string, error) { - _, history, err := statefulSetHistory(h.c.AppsV1(), namespace, name) + sts, history, err := statefulSetHistory(h.c.AppsV1(), namespace, name) if err != nil { return "", err } @@ -243,9 +242,26 @@ func (h *StatefulSetHistoryViewer) ViewHistory(namespace, name string, revision if len(history) <= 0 { return "No rollout history found.", nil } - revisions := make([]int64, 0, len(history)) - for _, revision := range history { - revisions = append(revisions, revision.Revision) + + historyInfo := make(map[int64]*appsv1.ControllerRevision) + for _, h := range history { + historyInfo[h.Revision] = h + } + if revision != 0 { + value, ok := historyInfo[revision] + if !ok { + return "", fmt.Errorf("unable to find the specified revision") + } + sts, err := applyStatefulSetHistory(sts, value) + if err != nil { + return "", fmt.Errorf("error unmarshelling sts: %w", err) + } + return printTemplate(&sts.Spec.Template) + } + + revisions := make([]int64, 0, len(historyInfo)) + for revision := range historyInfo { + revisions = append(revisions, revision) } sliceutil.SortInts64(revisions) @@ -365,6 +381,24 @@ func applyDaemonSetHistory(ds *appsv1.DaemonSet, history *appsv1.ControllerRevis return result, nil } +// applyStatefulSetHistory returns a specific revision of StatefulSet by applying the given history to a copy of the given StatefulSet +func applyStatefulSetHistory(sts *appsv1.StatefulSet, history *appsv1.ControllerRevision) (*appsv1.StatefulSet, error) { + stsBytes, err := json.Marshal(sts) + if err != nil { + return nil, err + } + patched, err := strategicpatch.StrategicMergePatch(stsBytes, history.Data.Raw, sts) + if err != nil { + return nil, err + } + result := &appsv1.StatefulSet{} + err = json.Unmarshal(patched, result) + if err != nil { + return nil, err + } + return result, nil +} + // TODO: copied here until this becomes a describer func tabbedString(f func(io.Writer) error) (string, error) { out := new(tabwriter.Writer) diff --git a/staging/src/k8s.io/kubectl/pkg/polymorphichelpers/history_test.go b/staging/src/k8s.io/kubectl/pkg/polymorphichelpers/history_test.go index 8b2af5704f9..ed9f5b97042 100644 --- a/staging/src/k8s.io/kubectl/pkg/polymorphichelpers/history_test.go +++ b/staging/src/k8s.io/kubectl/pkg/polymorphichelpers/history_test.go @@ -27,6 +27,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/json" "k8s.io/client-go/kubernetes/fake" ) @@ -71,21 +72,25 @@ func TestViewHistory(t *testing.T) { }, Spec: appsv1.StatefulSetSpec{Selector: &metav1.LabelSelector{MatchLabels: podStub.ObjectMeta.Labels}, Replicas: &replicas, Template: podStub}, } - - ssStub1 = &appsv1.ControllerRevision{ - ObjectMeta: metav1.ObjectMeta{ - Name: "moons", - Namespace: "default", - Labels: map[string]string{"foo": "bar"}, - OwnerReferences: []metav1.OwnerReference{{"apps/v1", "StatefulSet", "moons", "1993", &trueVar, nil}}, - }, - TypeMeta: metav1.TypeMeta{Kind: "StatefulSet", APIVersion: "apps/v1"}, - Revision: 1, - } ) + stsRawData, err := json.Marshal(ssStub) + if err != nil { + t.Fatalf("error creating sts raw data: %v", err) + } + ssStub1 := &appsv1.ControllerRevision{ + ObjectMeta: metav1.ObjectMeta{ + Name: "moons", + Namespace: "default", + Labels: map[string]string{"foo": "bar"}, + OwnerReferences: []metav1.OwnerReference{{"apps/v1", "StatefulSet", "moons", "1993", &trueVar, nil}}, + }, + Data: runtime.RawExtension{Raw: stsRawData}, + TypeMeta: metav1.TypeMeta{Kind: "StatefulSet", APIVersion: "apps/v1"}, + Revision: 1, + } fakeClientSet := fake.NewSimpleClientset(ssStub) - _, err := fakeClientSet.AppsV1().ControllerRevisions("default").Create(context.TODO(), ssStub1, metav1.CreateOptions{}) + _, err = fakeClientSet.AppsV1().ControllerRevisions("default").Create(context.TODO(), ssStub1, metav1.CreateOptions{}) if err != nil { t.Fatalf("create controllerRevisions error %v occurred ", err) } @@ -94,19 +99,43 @@ func TestViewHistory(t *testing.T) { fakeClientSet, } - result, err := sts.ViewHistory("default", "moons", 1) - if err != nil { - t.Fatalf("error getting ViewHistory for a StatefulSets moons: %v", err) - } + t.Run("should show revisions list if the revision is not specified", func(t *testing.T) { + result, err := sts.ViewHistory("default", "moons", 0) + if err != nil { + t.Fatalf("error getting ViewHistory for a StatefulSets moons: %v", err) + } - expected := `REVISION + expected := `REVISION 1 ` - if result != expected { - t.Fatalf("unexpected output (%v was expected but got %v)", expected, result) - } + if result != expected { + t.Fatalf("unexpected output (%v was expected but got %v)", expected, result) + } + }) + t.Run("should describe the revision if revision is specified", func(t *testing.T) { + result, err := sts.ViewHistory("default", "moons", 1) + if err != nil { + t.Fatalf("error getting ViewHistory for a StatefulSets moons: %v", err) + } + + expected := `Pod Template: + Labels: foo=bar + Containers: + test: + Image: nginx + Port: + Host Port: + Environment: + Mounts: + Volumes: +` + + if result != expected { + t.Fatalf("unexpected output (%v was expected but got %v)", expected, result) + } + }) } func TestApplyDaemonSetHistory(t *testing.T) {