diff --git a/pkg/skaffold/actions/k8sjob/task.go b/pkg/skaffold/actions/k8sjob/task.go index ea4a03342b4..42c63e66a31 100644 --- a/pkg/skaffold/actions/k8sjob/task.go +++ b/pkg/skaffold/actions/k8sjob/task.go @@ -87,7 +87,17 @@ func (t Task) Exec(ctx context.Context, out io.Writer) error { } c := t.getContainerToDeploy() - t.setManifestValues(c) + var original corev1.Container + for _, ec := range t.jobManifest.Spec.Template.Spec.Containers { + if ec.Name == c.Name { + original = ec + break + } + } + patchToK8sContainer(c, &original) + + t.jobManifest.Spec.Template.Spec.Containers = []corev1.Container{original} + t.jobManifest.ObjectMeta.Name = t.Name() if err := k8sjobutil.ForceJobDelete(ctx, t.jobManifest.Name, jm, t.kubectl); err != nil { return errors.Wrap(err, fmt.Sprintf("preparing job %v for execution", t.jobManifest.Name)) @@ -110,6 +120,20 @@ func (t Task) Exec(ctx context.Context, out io.Writer) error { return err } +func patchToK8sContainer(container corev1.Container, dst *corev1.Container) { + dst.Name = container.Name + dst.Image = container.Image + dst.Command = container.Command + dst.Args = container.Args + + for _, e := range container.Env { + dst.Env = append(dst.Env, corev1.EnvVar{ + Name: e.Name, + Value: e.Value, + }) + } +} + func (t Task) Cleanup(ctx context.Context, out io.Writer) error { jm, err := t.jobsManager() if err != nil { @@ -145,11 +169,6 @@ func (t Task) getK8SEnvVars(envVars []latest.VerifyEnvVar) (k8sEnvVar []corev1.E return } -func (t *Task) setManifestValues(c corev1.Container) { - t.jobManifest.Spec.Template.Spec.Containers = []corev1.Container{c} - t.jobManifest.ObjectMeta.Name = t.Name() -} - func (t Task) deployJob(ctx context.Context, jobManifest batchv1.Job, jobsManager typesbatchv1.JobInterface) error { return k8sjobutil.WithRetryablePoll(ctx, func(ctx context.Context) error { _, err := jobsManager.Create(ctx, &jobManifest, v1.CreateOptions{}) diff --git a/pkg/skaffold/actions/k8sjob/task_test.go b/pkg/skaffold/actions/k8sjob/task_test.go new file mode 100644 index 00000000000..b664b49198a --- /dev/null +++ b/pkg/skaffold/actions/k8sjob/task_test.go @@ -0,0 +1,133 @@ +/* +Copyright 2024 The Skaffold Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package k8sjob + +import ( + "testing" + + corev1 "k8s.io/api/core/v1" + + "github.com/GoogleContainerTools/skaffold/v2/testutil" +) + +func TestPatchToK8sContainer(t *testing.T) { + tests := []struct { + description string + actionContainer corev1.Container + k8sContainer corev1.Container + expected corev1.Container + }{ + { + description: "update all fields", + actionContainer: corev1.Container{ + Image: "my-image:latest", + Command: []string{"/bin/bash"}, + Args: []string{"-c", "echo hello world"}, + Name: "my-container", + Env: []corev1.EnvVar{ + {Name: "FOO", Value: "BAR"}, + }, + }, + k8sContainer: corev1.Container{}, + expected: corev1.Container{ + Image: "my-image:latest", + Command: []string{"/bin/bash"}, + Args: []string{"-c", "echo hello world"}, + Name: "my-container", + Env: []corev1.EnvVar{ + {Name: "FOO", Value: "BAR"}, + }, + }, + }, + { + description: "update image", + actionContainer: corev1.Container{ + Image: "my-new-image:latest", + }, + k8sContainer: corev1.Container{ + Image: "my-image:latest", + }, + expected: corev1.Container{ + Image: "my-new-image:latest", + }, + }, + { + description: "update command", + actionContainer: corev1.Container{ + Command: []string{"/bin/ls"}, + }, + k8sContainer: corev1.Container{ + Command: []string{"/bin/bash"}, + }, + expected: corev1.Container{ + Command: []string{"/bin/ls"}, + }, + }, + { + description: "update args", + actionContainer: corev1.Container{ + Args: []string{"-l"}, + }, + k8sContainer: corev1.Container{ + Args: []string{"-c", "echo hello world"}, + }, + expected: corev1.Container{ + Args: []string{"-l"}, + }, + }, + { + description: "update name", + actionContainer: corev1.Container{ + Name: "my-new-container", + }, + k8sContainer: corev1.Container{ + Name: "my-container", + }, + expected: corev1.Container{ + Name: "my-new-container", + }, + }, + { + description: "update env", + actionContainer: corev1.Container{ + Env: []corev1.EnvVar{ + {Name: "FOO", Value: "BARR"}, + {Name: "BAZ", Value: "QUX"}, + }, + }, + k8sContainer: corev1.Container{ + Env: []corev1.EnvVar{ + {Name: "FOO", Value: "BAR"}, + }, + }, + // Duplicate name should be ok, as the last writer should win. + expected: corev1.Container{ + Env: []corev1.EnvVar{ + {Name: "FOO", Value: "BAR"}, + {Name: "FOO", Value: "BARR"}, + {Name: "BAZ", Value: "QUX"}, + }, + }, + }, + } + for _, test := range tests { + testutil.Run(t, test.description, func(t *testutil.T) { + patchToK8sContainer(test.actionContainer, &test.k8sContainer) + t.CheckDeepEqual(test.expected, test.k8sContainer) + }) + } +}