diff --git a/internal/dao/dp.go b/internal/dao/dp.go index 4c6431fd..a732b6bf 100644 --- a/internal/dao/dp.go +++ b/internal/dao/dp.go @@ -229,7 +229,7 @@ func (d *Deployment) SetImages(ctx context.Context, path string, spec v1.PodSpec if !auth { return fmt.Errorf("user is not authorized to patch a deployment") } - jsonPatch, err := SetImageJsonPatch(spec) + jsonPatch, err := GetTemplateJsonPatch(spec) if err != nil { return err } diff --git a/internal/dao/ds.go b/internal/dao/ds.go index 38d94ba6..c5aae145 100644 --- a/internal/dao/ds.go +++ b/internal/dao/ds.go @@ -244,7 +244,7 @@ func (d *DaemonSet) SetImages(ctx context.Context, path string, spec v1.PodSpec) if !auth { return fmt.Errorf("user is not authorized to patch a daemonset") } - jsonPatch, err := SetImageJsonPatch(spec) + jsonPatch, err := GetTemplateJsonPatch(spec) if err != nil { return err } diff --git a/internal/dao/patch.go b/internal/dao/patch.go index 439f79eb..dc9bbf2a 100644 --- a/internal/dao/patch.go +++ b/internal/dao/patch.go @@ -10,10 +10,10 @@ type JsonPatch struct { } type Spec struct { - Template Template `json:"template"` + Template PodSpec `json:"template"` } -type Template struct { +type PodSpec struct { Spec ImagesSpec `json:"spec"` } @@ -30,23 +30,35 @@ type Element struct { } // Build a json patch string to update PodSpec images -func SetImageJsonPatch(spec v1.PodSpec) (string, error) { +func GetTemplateJsonPatch(spec v1.PodSpec) (string, error) { jsonPatch := JsonPatch{ Spec: Spec{ - Template: Template{ - Spec: ImagesSpec{ - SetElementOrderContainers: extractElements(spec.Containers, false), - Containers: extractElements(spec.Containers, true), - SetElementOrderInitContainers: extractElements(spec.InitContainers, false), - InitContainers: extractElements(spec.InitContainers, true), - }, - }, + Template: getPatchPodSpec(spec), }, } bytes, err := json.Marshal(jsonPatch) return string(bytes), err } +func GetJsonPatch(spec v1.PodSpec) (string, error) { + podSpec := getPatchPodSpec(spec) + bytes, err := json.Marshal(podSpec) + return string(bytes), err +} + +func getPatchPodSpec(spec v1.PodSpec) PodSpec { + podSpec := PodSpec{ + Spec: ImagesSpec{ + SetElementOrderContainers: extractElements(spec.Containers, false), + Containers: extractElements(spec.Containers, true), + SetElementOrderInitContainers: extractElements(spec.InitContainers, false), + InitContainers: extractElements(spec.InitContainers, true), + }, + } + + return podSpec +} + func extractElements(containers []v1.Container, withImage bool) []Element { elements := make([]Element, 0) for _, c := range containers { diff --git a/internal/dao/patch_test.go b/internal/dao/patch_test.go index 15c9183f..83428d9b 100644 --- a/internal/dao/patch_test.go +++ b/internal/dao/patch_test.go @@ -6,7 +6,7 @@ import ( "testing" ) -func TestSetImageJsonPatch(t *testing.T) { +func TestGetTemplateJsonPatch(t *testing.T) { type args struct { podSpec v1.PodSpec } @@ -30,9 +30,43 @@ func TestSetImageJsonPatch(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := SetImageJsonPatch(tt.args.podSpec) + got, err := GetTemplateJsonPatch(tt.args.podSpec) if (err != nil) != tt.wantErr { - t.Errorf("SetImageJsonPatch() error = %v, wantErr %v", err, tt.wantErr) + t.Errorf("GetTemplateJsonPatch() error = %v, wantErr %v", err, tt.wantErr) + return + } + require.JSONEq(t, tt.want, got, "Json strings should be equal") + }) + } +} + +func TestGetJsonPatch(t *testing.T) { + type args struct { + podSpec v1.PodSpec + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "simple", + args: args{ + podSpec: v1.PodSpec{ + InitContainers: []v1.Container{v1.Container{Image: "busybox:latest", Name: "init"}}, + Containers: []v1.Container{v1.Container{Image: "nginx:latest", Name: "nginx"}}, + }, + }, + want: `{"spec":{"$setElementOrder/containers":[{"name":"nginx"}],"$setElementOrder/initContainers":[{"name":"init"}],"containers":[{"image":"nginx:latest","name":"nginx"}],"initContainers":[{"image":"busybox:latest","name":"init"}]}}`, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := GetJsonPatch(tt.args.podSpec) + if (err != nil) != tt.wantErr { + t.Errorf("GetTemplateJsonPatch() error = %v, wantErr %v", err, tt.wantErr) return } require.JSONEq(t, tt.want, got, "Json strings should be equal") diff --git a/internal/dao/pod.go b/internal/dao/pod.go index f607341f..234804e2 100644 --- a/internal/dao/pod.go +++ b/internal/dao/pod.go @@ -18,15 +18,17 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" restclient "k8s.io/client-go/rest" mv1beta1 "k8s.io/metrics/pkg/apis/metrics/v1beta1" ) var ( - _ Accessor = (*Pod)(nil) - _ Nuker = (*Pod)(nil) - _ Loggable = (*Pod)(nil) - _ Controller = (*Pod)(nil) + _ Accessor = (*Pod)(nil) + _ Nuker = (*Pod)(nil) + _ Loggable = (*Pod)(nil) + _ Controller = (*Pod)(nil) + _ ContainsPodSpec = (*Pod)(nil) ) const ( @@ -455,3 +457,40 @@ func in(ll []string, s string) bool { } return false } + +func (p *Pod) GetPodSpec(path string) (*v1.PodSpec, error) { + pod, err := p.GetInstance(path) + if err != nil { + return nil, err + } + podSpec := pod.Spec + return &podSpec, nil +} + +func (p *Pod) SetImages(ctx context.Context, path string, spec v1.PodSpec) error { + ns, n := client.Namespaced(path) + auth, err := p.Client().CanI(ns, "v1/pod", []string{client.PatchVerb}) + if err != nil { + return err + } + if !auth { + return fmt.Errorf("user is not authorized to patch a deployment") + } + jsonPatch, err := GetJsonPatch(spec) + if err != nil { + return err + } + dial, err := p.Client().Dial() + if err != nil { + return err + } + log.Info().Msgf("jsonPatch : %v", jsonPatch) + _, err = dial.CoreV1().Pods(ns).Patch( + ctx, + n, + types.StrategicMergePatchType, + []byte(jsonPatch), + metav1.PatchOptions{}, + ) + return err +} diff --git a/internal/view/pod.go b/internal/view/pod.go index c8b12780..1b458cd5 100644 --- a/internal/view/pod.go +++ b/internal/view/pod.go @@ -29,7 +29,9 @@ type Pod struct { func NewPod(gvr client.GVR) ResourceViewer { p := Pod{} p.ResourceViewer = NewPortForwardExtender( - NewLogsExtender(NewBrowser(gvr), p.selectedContainer), + NewSetImageExtender( + NewLogsExtender(NewBrowser(gvr), p.selectedContainer), + ), ) p.SetBindKeysFn(p.bindKeys) p.GetTable().SetEnterFn(p.showContainers)