mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 11:50:44 +00:00
Merge pull request #86506 from dineshba/master
sts: rollout history will show the details of the sts if the revision is specfied
This commit is contained in:
commit
ef57a095d9
@ -95,6 +95,7 @@ go_test(
|
|||||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
"//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/types:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/util/diff: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/apimachinery/pkg/watch:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
|
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/testing:go_default_library",
|
"//staging/src/k8s.io/client-go/testing:go_default_library",
|
||||||
|
@ -183,6 +183,18 @@ func (h *DaemonSetHistoryViewer) ViewHistory(namespace, name string, revision in
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
return printHistory(history, revision, func(history *appsv1.ControllerRevision) (*corev1.PodTemplateSpec, error) {
|
||||||
|
dsOfHistory, err := applyDaemonSetHistory(ds, history)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &dsOfHistory.Spec.Template, err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// printHistory returns the podTemplate of the given revision if it is non-zero
|
||||||
|
// else returns the overall revisions
|
||||||
|
func printHistory(history []*appsv1.ControllerRevision, revision int64, getPodTemplate func(history *appsv1.ControllerRevision) (*corev1.PodTemplateSpec, error)) (string, error) {
|
||||||
historyInfo := make(map[int64]*appsv1.ControllerRevision)
|
historyInfo := make(map[int64]*appsv1.ControllerRevision)
|
||||||
for _, history := range history {
|
for _, history := range history {
|
||||||
// TODO: for now we assume revisions don't overlap, we may need to handle it
|
// TODO: for now we assume revisions don't overlap, we may need to handle it
|
||||||
@ -198,11 +210,11 @@ func (h *DaemonSetHistoryViewer) ViewHistory(namespace, name string, revision in
|
|||||||
if !ok {
|
if !ok {
|
||||||
return "", fmt.Errorf("unable to find the specified revision")
|
return "", fmt.Errorf("unable to find the specified revision")
|
||||||
}
|
}
|
||||||
dsOfHistory, err := applyDaemonSetHistory(ds, history)
|
podTemplate, err := getPodTemplate(history)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("unable to parse history %s", history.Name)
|
return "", fmt.Errorf("unable to parse history %s", history.Name)
|
||||||
}
|
}
|
||||||
return printTemplate(&dsOfHistory.Spec.Template)
|
return printTemplate(podTemplate)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print an overview of all Revisions
|
// Print an overview of all Revisions
|
||||||
@ -233,28 +245,17 @@ type StatefulSetHistoryViewer struct {
|
|||||||
|
|
||||||
// ViewHistory returns a list of the revision history of a statefulset
|
// ViewHistory returns a list of the revision history of a statefulset
|
||||||
// TODO: this should be a describer
|
// TODO: this should be a describer
|
||||||
// TODO: needs to implement detailed revision view
|
|
||||||
func (h *StatefulSetHistoryViewer) ViewHistory(namespace, name string, revision int64) (string, error) {
|
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 {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
return printHistory(history, revision, func(history *appsv1.ControllerRevision) (*corev1.PodTemplateSpec, error) {
|
||||||
if len(history) <= 0 {
|
stsOfHistory, err := applyStatefulSetHistory(sts, history)
|
||||||
return "No rollout history found.", nil
|
if err != nil {
|
||||||
}
|
return nil, err
|
||||||
revisions := make([]int64, 0, len(history))
|
|
||||||
for _, revision := range history {
|
|
||||||
revisions = append(revisions, revision.Revision)
|
|
||||||
}
|
|
||||||
sliceutil.SortInts64(revisions)
|
|
||||||
|
|
||||||
return tabbedString(func(out io.Writer) error {
|
|
||||||
fmt.Fprintf(out, "REVISION\n")
|
|
||||||
for _, r := range revisions {
|
|
||||||
fmt.Fprintf(out, "%d\n", r)
|
|
||||||
}
|
}
|
||||||
return nil
|
return &stsOfHistory.Spec.Template, err
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -365,6 +366,24 @@ func applyDaemonSetHistory(ds *appsv1.DaemonSet, history *appsv1.ControllerRevis
|
|||||||
return result, nil
|
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
|
// TODO: copied here until this becomes a describer
|
||||||
func tabbedString(f func(io.Writer) error) (string, error) {
|
func tabbedString(f func(io.Writer) error) (string, error) {
|
||||||
out := new(tabwriter.Writer)
|
out := new(tabwriter.Writer)
|
||||||
|
@ -27,6 +27,7 @@ import (
|
|||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
"k8s.io/apimachinery/pkg/util/json"
|
||||||
"k8s.io/client-go/kubernetes/fake"
|
"k8s.io/client-go/kubernetes/fake"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -53,60 +54,176 @@ func TestHistoryViewerFor(t *testing.T) {
|
|||||||
|
|
||||||
func TestViewHistory(t *testing.T) {
|
func TestViewHistory(t *testing.T) {
|
||||||
|
|
||||||
var (
|
t.Run("for statefulSet", func(t *testing.T) {
|
||||||
trueVar = true
|
var (
|
||||||
replicas = int32(1)
|
trueVar = true
|
||||||
|
replicas = int32(1)
|
||||||
|
|
||||||
podStub = corev1.PodTemplateSpec{
|
podStub = corev1.PodTemplateSpec{
|
||||||
ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"foo": "bar"}},
|
ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"foo": "bar"}},
|
||||||
Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "test", Image: "nginx"}}},
|
Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "test", Image: "nginx"}}},
|
||||||
|
}
|
||||||
|
|
||||||
|
ssStub = &appsv1.StatefulSet{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "moons",
|
||||||
|
Namespace: "default",
|
||||||
|
UID: "1993",
|
||||||
|
Labels: map[string]string{"foo": "bar"},
|
||||||
|
},
|
||||||
|
Spec: appsv1.StatefulSetSpec{Selector: &metav1.LabelSelector{MatchLabels: podStub.ObjectMeta.Labels}, Replicas: &replicas, Template: podStub},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
stsRawData, err := json.Marshal(ssStub)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error creating sts raw data: %v", err)
|
||||||
}
|
}
|
||||||
|
ssStub1 := &appsv1.ControllerRevision{
|
||||||
ssStub = &appsv1.StatefulSet{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: "moons",
|
|
||||||
Namespace: "default",
|
|
||||||
UID: "1993",
|
|
||||||
Labels: map[string]string{"foo": "bar"},
|
|
||||||
},
|
|
||||||
Spec: appsv1.StatefulSetSpec{Selector: &metav1.LabelSelector{MatchLabels: podStub.ObjectMeta.Labels}, Replicas: &replicas, Template: podStub},
|
|
||||||
}
|
|
||||||
|
|
||||||
ssStub1 = &appsv1.ControllerRevision{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "moons",
|
Name: "moons",
|
||||||
Namespace: "default",
|
Namespace: "default",
|
||||||
Labels: map[string]string{"foo": "bar"},
|
Labels: map[string]string{"foo": "bar"},
|
||||||
OwnerReferences: []metav1.OwnerReference{{"apps/v1", "StatefulSet", "moons", "1993", &trueVar, nil}},
|
OwnerReferences: []metav1.OwnerReference{{"apps/v1", "StatefulSet", "moons", "1993", &trueVar, nil}},
|
||||||
},
|
},
|
||||||
|
Data: runtime.RawExtension{Raw: stsRawData},
|
||||||
TypeMeta: metav1.TypeMeta{Kind: "StatefulSet", APIVersion: "apps/v1"},
|
TypeMeta: metav1.TypeMeta{Kind: "StatefulSet", APIVersion: "apps/v1"},
|
||||||
Revision: 1,
|
Revision: 1,
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
|
||||||
fakeClientSet := fake.NewSimpleClientset(ssStub)
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("create controllerRevisions error %v occurred ", err)
|
t.Fatalf("create controllerRevisions error %v occurred ", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var sts = &StatefulSetHistoryViewer{
|
var sts = &StatefulSetHistoryViewer{
|
||||||
fakeClientSet,
|
fakeClientSet,
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := sts.ViewHistory("default", "moons", 1)
|
t.Run("should show revisions list if the revision is not specified", func(t *testing.T) {
|
||||||
if err != nil {
|
result, err := sts.ViewHistory("default", "moons", 0)
|
||||||
t.Fatalf("error getting ViewHistory for a StatefulSets moons: %v", err)
|
if err != nil {
|
||||||
}
|
t.Fatalf("error getting ViewHistory for a StatefulSets moons: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
expected := `REVISION
|
expected := `REVISION CHANGE-CAUSE
|
||||||
1
|
1 <none>
|
||||||
`
|
`
|
||||||
|
|
||||||
if result != expected {
|
if result != expected {
|
||||||
t.Fatalf("unexpected output (%v was expected but got %v)", expected, result)
|
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: <none>
|
||||||
|
Host Port: <none>
|
||||||
|
Environment: <none>
|
||||||
|
Mounts: <none>
|
||||||
|
Volumes: <none>
|
||||||
|
`
|
||||||
|
|
||||||
|
if result != expected {
|
||||||
|
t.Fatalf("unexpected output (%v was expected but got %v)", expected, result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("for daemonSet", func(t *testing.T) {
|
||||||
|
var (
|
||||||
|
trueVar = true
|
||||||
|
podStub = corev1.PodTemplateSpec{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"foo": "bar"}},
|
||||||
|
Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "test", Image: "nginx"}}},
|
||||||
|
}
|
||||||
|
|
||||||
|
daemonSetStub = &appsv1.DaemonSet{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "moons",
|
||||||
|
Namespace: "default",
|
||||||
|
UID: "1993",
|
||||||
|
Labels: map[string]string{"foo": "bar"},
|
||||||
|
},
|
||||||
|
Spec: appsv1.DaemonSetSpec{Selector: &metav1.LabelSelector{MatchLabels: podStub.ObjectMeta.Labels}, Template: podStub},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
daemonSetRaw, err := json.Marshal(daemonSetStub)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error creating sts raw data: %v", err)
|
||||||
|
}
|
||||||
|
daemonSetControllerRevision := &appsv1.ControllerRevision{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "moons",
|
||||||
|
Namespace: "default",
|
||||||
|
Labels: map[string]string{"foo": "bar"},
|
||||||
|
OwnerReferences: []metav1.OwnerReference{{"apps/v1", "DaemonSet", "moons", "1993", &trueVar, nil}},
|
||||||
|
},
|
||||||
|
Data: runtime.RawExtension{Raw: daemonSetRaw},
|
||||||
|
TypeMeta: metav1.TypeMeta{Kind: "StatefulSet", APIVersion: "apps/v1"},
|
||||||
|
Revision: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
fakeClientSet := fake.NewSimpleClientset(daemonSetStub)
|
||||||
|
_, err = fakeClientSet.AppsV1().ControllerRevisions("default").Create(context.TODO(), daemonSetControllerRevision, metav1.CreateOptions{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("create controllerRevisions error %v occurred ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var daemonSetHistoryViewer = &DaemonSetHistoryViewer{
|
||||||
|
fakeClientSet,
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("should show revisions list if the revision is not specified", func(t *testing.T) {
|
||||||
|
result, err := daemonSetHistoryViewer.ViewHistory("default", "moons", 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error getting ViewHistory for a StatefulSets moons: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := `REVISION CHANGE-CAUSE
|
||||||
|
1 <none>
|
||||||
|
`
|
||||||
|
|
||||||
|
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 := daemonSetHistoryViewer.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: <none>
|
||||||
|
Host Port: <none>
|
||||||
|
Environment: <none>
|
||||||
|
Mounts: <none>
|
||||||
|
Volumes: <none>
|
||||||
|
`
|
||||||
|
|
||||||
|
if result != expected {
|
||||||
|
t.Fatalf("unexpected output (%v was expected but got %v)", expected, result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestApplyDaemonSetHistory(t *testing.T) {
|
func TestApplyDaemonSetHistory(t *testing.T) {
|
||||||
|
Loading…
Reference in New Issue
Block a user