feat(set_image): add set image feature for pod

mine
Antoine Meausoone 2020-08-20 13:58:06 +02:00
parent a892c0a0aa
commit b00c77f477
6 changed files with 108 additions and 21 deletions

View File

@ -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
}

View File

@ -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
}

View File

@ -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 {

View File

@ -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")

View File

@ -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
}

View File

@ -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)