Skip to content

Commit 0c341ae

Browse files
authored
Merge pull request #659 from buildkite/mingpipe-1664
PIPE-1664: job level image attribute take precedence over controller podSpecPatch
2 parents 5d7efdf + d10b689 commit 0c341ae

File tree

3 files changed

+130
-8
lines changed

3 files changed

+130
-8
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package scheduler
2+
3+
import v1 "k8s.io/api/core/v1"
4+
5+
func applyCustomImageIfPresent(podSpec *v1.PodSpec, inputs *buildInputs) *v1.PodSpec {
6+
7+
customImage := inputs.envMap["BUILDKITE_IMAGE"]
8+
if customImage == "" {
9+
return podSpec
10+
}
11+
12+
for i := range podSpec.Containers {
13+
c := &podSpec.Containers[i]
14+
if c.Name != CommandContainerName {
15+
continue
16+
}
17+
18+
c.Image = customImage
19+
}
20+
21+
return podSpec
22+
}

internal/controller/scheduler/scheduler.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ const (
4343
CopyAgentContainerName = "copy-agent"
4444
ImageCheckContainerNamePrefix = "imagecheck-"
4545
CheckoutContainerName = "checkout"
46+
CommandContainerName = "container-0"
4647
)
4748

4849
var errK8sPluginProhibited = errors.New("the kubernetes plugin is prohibited by this controller, but was configured on this job")
@@ -419,14 +420,10 @@ func (w *worker) Build(podSpec *corev1.PodSpec, skipCheckout bool, inputs buildI
419420
}
420421

421422
if len(podSpec.Containers) == 0 {
422-
image := w.cfg.Image
423-
if customImage := inputs.envMap["BUILDKITE_IMAGE"]; customImage != "" {
424-
image = inputs.envMap["BUILDKITE_IMAGE"]
425-
}
426-
// Create a default command container named "container-0".
423+
// Create a default command container
427424
c := corev1.Container{
428-
Name: "container-0",
429-
Image: image,
425+
Name: CommandContainerName,
426+
Image: w.cfg.Image,
430427
Command: commandContainerCommand,
431428
Args: commandContainerArgs,
432429
WorkingDir: "/workspace",
@@ -446,7 +443,7 @@ func (w *worker) Build(podSpec *corev1.PodSpec, skipCheckout bool, inputs buildI
446443
},
447444
{
448445
Name: "BUILDKITE_SOCKETS_PATH",
449-
Value: "/workspace/sockets/container-0",
446+
Value: "/workspace/sockets/" + CommandContainerName,
450447
},
451448
},
452449
}
@@ -682,6 +679,9 @@ func (w *worker) Build(podSpec *corev1.PodSpec, skipCheckout bool, inputs buildI
682679
w.logger.Debug("Applied podSpec patch from controller", zap.Any("patched", patched))
683680
}
684681

682+
// Support `image: ` syntax, this HAS TO happen between controller podSpec patch and plugin podSpec patch.
683+
podSpec = applyCustomImageIfPresent(podSpec, &inputs)
684+
685685
// If present, patch from the k8s plugin is applied second.
686686
if inputs.k8sPlugin != nil && inputs.k8sPlugin.PodSpecPatch != nil {
687687
patched, err := PatchPodSpec(podSpec, inputs.k8sPlugin.PodSpecPatch, w.cfg.DefaultCommandParams, inputs.k8sPlugin, w.cfg.AllowPodSpecPatchUnsafeCmdMod)

internal/controller/scheduler/scheduler_test.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,39 @@ func TestPatchPodSpec(t *testing.T) {
161161
},
162162
},
163163
},
164+
{
165+
name: "can patch image",
166+
podspec: &corev1.PodSpec{
167+
Containers: []corev1.Container{
168+
{
169+
Name: "container-0",
170+
Image: "alpine:a",
171+
Command: []string{
172+
"echo hello world",
173+
},
174+
},
175+
},
176+
},
177+
patch: &corev1.PodSpec{
178+
Containers: []corev1.Container{
179+
{
180+
Name: "container-0",
181+
Image: "alpine:b",
182+
},
183+
},
184+
},
185+
want: &corev1.PodSpec{
186+
Containers: []corev1.Container{
187+
{
188+
Name: "container-0",
189+
Image: "alpine:b",
190+
Command: []string{
191+
"echo hello world",
192+
},
193+
},
194+
},
195+
},
196+
},
164197
}
165198

166199
for _, test := range cases {
@@ -882,6 +915,73 @@ func TestProhibitKubernetesPlugin(t *testing.T) {
882915
require.Error(t, err)
883916
}
884917

918+
func TestCustomImageSyntax_pluginTakesTopPriority(t *testing.T) {
919+
t.Parallel()
920+
921+
pluginsYAML := `- github.com/buildkite-plugins/kubernetes-buildkite-plugin:
922+
podSpecPatch:
923+
containers:
924+
- name: container-0
925+
image: "x-image:plugin"`
926+
927+
pluginsJSON, err := yaml.YAMLToJSONStrict([]byte(pluginsYAML))
928+
require.NoError(t, err)
929+
930+
job := &api.AgentJob{
931+
ID: "abc",
932+
Env: map[string]string{
933+
"BUILDKITE_PLUGINS": string(pluginsJSON),
934+
"BUILDKITE_IMAGE": "x-image:job",
935+
},
936+
}
937+
sjob := &api.AgentScheduledJob{
938+
AgentQueryRules: []string{"queue=kubernetes"},
939+
}
940+
worker := New(zaptest.NewLogger(t), nil, nil, Config{
941+
Image: "buildkite/agent:latest",
942+
})
943+
inputs, err := worker.ParseJob(job, sjob)
944+
require.NoError(t, err)
945+
kjob, err := worker.Build(&corev1.PodSpec{}, false, inputs)
946+
require.NoError(t, err)
947+
948+
commandContainer := findContainer(t, kjob.Spec.Template.Spec.Containers, CommandContainerName)
949+
require.Equal(t, "x-image:plugin", commandContainer.Image)
950+
}
951+
952+
// Job level image syntax takes priority over controller setting
953+
func TestCustomImageSyntax_jobLevelImagePriority(t *testing.T) {
954+
t.Parallel()
955+
956+
job := &api.AgentJob{
957+
ID: "abc",
958+
Env: map[string]string{
959+
"BUILDKITE_IMAGE": "x-image:job",
960+
},
961+
}
962+
sjob := &api.AgentScheduledJob{
963+
AgentQueryRules: []string{"queue=kubernetes"},
964+
}
965+
worker := New(zaptest.NewLogger(t), nil, nil, Config{
966+
Image: "buildkite/agent:latest",
967+
PodSpecPatch: &corev1.PodSpec{
968+
Containers: []corev1.Container{
969+
{
970+
Name: "container-0",
971+
Image: "alpine:controller",
972+
},
973+
},
974+
},
975+
})
976+
inputs, err := worker.ParseJob(job, sjob)
977+
require.NoError(t, err)
978+
kjob, err := worker.Build(&corev1.PodSpec{}, false, inputs)
979+
require.NoError(t, err)
980+
981+
commandContainer := findContainer(t, kjob.Spec.Template.Spec.Containers, CommandContainerName)
982+
require.Equal(t, "x-image:job", commandContainer.Image)
983+
}
984+
885985
func TestImagePullPolicies(t *testing.T) {
886986
t.Parallel()
887987

0 commit comments

Comments
 (0)