mirror of https://github.com/k3s-io/k3s
Merge pull request #72276 from vithati/users/vithati/kubectlissue503
Allow setting images for 'InitContainers' through kubectl set command.pull/564/head
commit
af31a202ea
|
@ -216,38 +216,20 @@ func (o *SetImageOptions) Run() error {
|
|||
allErrs := []error{}
|
||||
|
||||
patches := CalculatePatches(o.Infos, scheme.DefaultJSONEncoder(), func(obj runtime.Object) ([]byte, error) {
|
||||
transformed := false
|
||||
_, err := o.UpdatePodSpecForObject(obj, func(spec *v1.PodSpec) error {
|
||||
for name, image := range o.ContainerImages {
|
||||
var (
|
||||
containerFound bool
|
||||
err error
|
||||
resolved string
|
||||
)
|
||||
// Find the container to update, and update its image
|
||||
for i, c := range spec.Containers {
|
||||
if c.Name == name || name == "*" {
|
||||
containerFound = true
|
||||
if len(resolved) == 0 {
|
||||
if resolved, err = o.ResolveImage(image); err != nil {
|
||||
allErrs = append(allErrs, fmt.Errorf("error: unable to resolve image %q for container %q: %v", image, name, err))
|
||||
// Do not loop again if the image resolving failed for wildcard case as we
|
||||
// will report the same error again for the next container.
|
||||
if name == "*" {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
if spec.Containers[i].Image != resolved {
|
||||
spec.Containers[i].Image = resolved
|
||||
// Perform updates
|
||||
transformed = true
|
||||
}
|
||||
resolvedImageName, err := o.ResolveImage(image)
|
||||
if err != nil {
|
||||
allErrs = append(allErrs, fmt.Errorf("error: unable to resolve image %q for container %q: %v", image, name, err))
|
||||
if name == "*" {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
// Add a new container if not found
|
||||
if !containerFound {
|
||||
|
||||
initContainerFound := setImage(spec.InitContainers, name, resolvedImageName)
|
||||
containerFound := setImage(spec.Containers, name, resolvedImageName)
|
||||
if !containerFound && !initContainerFound {
|
||||
allErrs = append(allErrs, fmt.Errorf("error: unable to find container named %q", name))
|
||||
}
|
||||
}
|
||||
|
@ -256,9 +238,6 @@ func (o *SetImageOptions) Run() error {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !transformed {
|
||||
return nil, nil
|
||||
}
|
||||
// record this change (for rollout history)
|
||||
if err := o.Recorder.Record(obj); err != nil {
|
||||
klog.V(4).Infof("error recording current command: %v", err)
|
||||
|
@ -300,6 +279,18 @@ func (o *SetImageOptions) Run() error {
|
|||
return utilerrors.NewAggregate(allErrs)
|
||||
}
|
||||
|
||||
func setImage(containers []v1.Container, containerName string, image string) bool {
|
||||
containerFound := false
|
||||
// Find the container to update, and update its image
|
||||
for i, c := range containers {
|
||||
if c.Name == containerName || containerName == "*" {
|
||||
containerFound = true
|
||||
containers[i].Image = image
|
||||
}
|
||||
}
|
||||
return containerFound
|
||||
}
|
||||
|
||||
// getResourcesAndImages retrieves resources and container name:images pair from given args
|
||||
func getResourcesAndImages(args []string) (resources []string, containerImages map[string]string, err error) {
|
||||
pairType := "image"
|
||||
|
|
|
@ -221,6 +221,12 @@ func TestSetImageRemote(t *testing.T) {
|
|||
Image: "nginx",
|
||||
},
|
||||
},
|
||||
InitContainers: []corev1.Container{
|
||||
{
|
||||
Name: "busybox",
|
||||
Image: "busybox",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -242,6 +248,12 @@ func TestSetImageRemote(t *testing.T) {
|
|||
Image: "nginx",
|
||||
},
|
||||
},
|
||||
InitContainers: []corev1.Container{
|
||||
{
|
||||
Name: "busybox",
|
||||
Image: "busybox",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -263,6 +275,12 @@ func TestSetImageRemote(t *testing.T) {
|
|||
Image: "nginx",
|
||||
},
|
||||
},
|
||||
InitContainers: []corev1.Container{
|
||||
{
|
||||
Name: "busybox",
|
||||
Image: "busybox",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -284,6 +302,12 @@ func TestSetImageRemote(t *testing.T) {
|
|||
Image: "nginx",
|
||||
},
|
||||
},
|
||||
InitContainers: []corev1.Container{
|
||||
{
|
||||
Name: "busybox",
|
||||
Image: "busybox",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -305,6 +329,12 @@ func TestSetImageRemote(t *testing.T) {
|
|||
Image: "nginx",
|
||||
},
|
||||
},
|
||||
InitContainers: []corev1.Container{
|
||||
{
|
||||
Name: "busybox",
|
||||
Image: "busybox",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -326,6 +356,12 @@ func TestSetImageRemote(t *testing.T) {
|
|||
Image: "nginx",
|
||||
},
|
||||
},
|
||||
InitContainers: []corev1.Container{
|
||||
{
|
||||
Name: "busybox",
|
||||
Image: "busybox",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -347,6 +383,12 @@ func TestSetImageRemote(t *testing.T) {
|
|||
Image: "nginx",
|
||||
},
|
||||
},
|
||||
InitContainers: []corev1.Container{
|
||||
{
|
||||
Name: "busybox",
|
||||
Image: "busybox",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -368,6 +410,12 @@ func TestSetImageRemote(t *testing.T) {
|
|||
Image: "nginx",
|
||||
},
|
||||
},
|
||||
InitContainers: []corev1.Container{
|
||||
{
|
||||
Name: "busybox",
|
||||
Image: "busybox",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -389,6 +437,12 @@ func TestSetImageRemote(t *testing.T) {
|
|||
Image: "nginx",
|
||||
},
|
||||
},
|
||||
InitContainers: []corev1.Container{
|
||||
{
|
||||
Name: "busybox",
|
||||
Image: "busybox",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -410,6 +464,12 @@ func TestSetImageRemote(t *testing.T) {
|
|||
Image: "nginx",
|
||||
},
|
||||
},
|
||||
InitContainers: []corev1.Container{
|
||||
{
|
||||
Name: "busybox",
|
||||
Image: "busybox",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -431,6 +491,12 @@ func TestSetImageRemote(t *testing.T) {
|
|||
Image: "nginx",
|
||||
},
|
||||
},
|
||||
InitContainers: []corev1.Container{
|
||||
{
|
||||
Name: "busybox",
|
||||
Image: "busybox",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -452,6 +518,12 @@ func TestSetImageRemote(t *testing.T) {
|
|||
Image: "nginx",
|
||||
},
|
||||
},
|
||||
InitContainers: []corev1.Container{
|
||||
{
|
||||
Name: "busybox",
|
||||
Image: "busybox",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -473,6 +545,12 @@ func TestSetImageRemote(t *testing.T) {
|
|||
Image: "nginx",
|
||||
},
|
||||
},
|
||||
InitContainers: []corev1.Container{
|
||||
{
|
||||
Name: "busybox",
|
||||
Image: "busybox",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -494,6 +572,12 @@ func TestSetImageRemote(t *testing.T) {
|
|||
Image: "nginx",
|
||||
},
|
||||
},
|
||||
InitContainers: []corev1.Container{
|
||||
{
|
||||
Name: "busybox",
|
||||
Image: "busybox",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -515,6 +599,12 @@ func TestSetImageRemote(t *testing.T) {
|
|||
Image: "nginx",
|
||||
},
|
||||
},
|
||||
InitContainers: []corev1.Container{
|
||||
{
|
||||
Name: "busybox",
|
||||
Image: "busybox",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -572,3 +662,116 @@ func TestSetImageRemote(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetImageRemoteWithSpecificContainers(t *testing.T) {
|
||||
inputs := []struct {
|
||||
name string
|
||||
object runtime.Object
|
||||
groupVersion schema.GroupVersion
|
||||
path string
|
||||
args []string
|
||||
}{
|
||||
{
|
||||
name: "set container image only",
|
||||
object: &extensionsv1beta1.ReplicaSet{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||
Spec: extensionsv1beta1.ReplicaSetSpec{
|
||||
Template: corev1.PodTemplateSpec{
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Name: "nginx",
|
||||
Image: "nginx",
|
||||
},
|
||||
},
|
||||
InitContainers: []corev1.Container{
|
||||
{
|
||||
Name: "busybox",
|
||||
Image: "busybox",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
groupVersion: extensionsv1beta1.SchemeGroupVersion,
|
||||
path: "/namespaces/test/replicasets/nginx",
|
||||
args: []string{"replicaset", "nginx", "nginx=thingy"},
|
||||
},
|
||||
{
|
||||
name: "set initContainer image only",
|
||||
object: &appsv1beta2.ReplicaSet{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||
Spec: appsv1beta2.ReplicaSetSpec{
|
||||
Template: corev1.PodTemplateSpec{
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Name: "busybox",
|
||||
Image: "busybox",
|
||||
},
|
||||
},
|
||||
InitContainers: []corev1.Container{
|
||||
{
|
||||
Name: "nginx",
|
||||
Image: "nginx",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
groupVersion: appsv1beta2.SchemeGroupVersion,
|
||||
path: "/namespaces/test/replicasets/nginx",
|
||||
args: []string{"replicaset", "nginx", "nginx=thingy"},
|
||||
},
|
||||
}
|
||||
for _, input := range inputs {
|
||||
t.Run(input.name, func(t *testing.T) {
|
||||
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||
defer tf.Cleanup()
|
||||
|
||||
tf.Client = &fake.RESTClient{
|
||||
GroupVersion: input.groupVersion,
|
||||
NegotiatedSerializer: serializer.DirectCodecFactory{CodecFactory: scheme.Codecs},
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
case p == input.path && m == http.MethodGet:
|
||||
return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: objBody(input.object)}, nil
|
||||
case p == input.path && m == http.MethodPatch:
|
||||
stream, err := req.GetBody()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bytes, err := ioutil.ReadAll(stream)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
assert.Contains(t, string(bytes), `"image":"`+"thingy"+`","name":`+`"nginx"`, fmt.Sprintf("image not updated for %#v", input.object))
|
||||
assert.NotContains(t, string(bytes), `"image":"`+"thingy"+`","name":`+`"busybox"`, fmt.Sprintf("image updated for %#v", input.object))
|
||||
return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: objBody(input.object)}, nil
|
||||
default:
|
||||
t.Errorf("%s: unexpected request: %s %#v\n%#v", "image", req.Method, req.URL, req)
|
||||
return nil, fmt.Errorf("unexpected request")
|
||||
}
|
||||
}),
|
||||
}
|
||||
|
||||
outputFormat := "yaml"
|
||||
|
||||
streams := genericclioptions.NewTestIOStreamsDiscard()
|
||||
cmd := NewCmdImage(tf, streams)
|
||||
cmd.Flags().Set("output", outputFormat)
|
||||
opts := SetImageOptions{
|
||||
PrintFlags: genericclioptions.NewPrintFlags("").WithDefaultOutput(outputFormat).WithTypeSetter(scheme.Scheme),
|
||||
|
||||
Local: false,
|
||||
IOStreams: streams,
|
||||
}
|
||||
err := opts.Complete(tf, cmd, input.args)
|
||||
assert.NoError(t, err)
|
||||
err = opts.Run()
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue