Skip to content

Commit 0591ab8

Browse files
authored
Merge pull request #428 from buildkite/configurable-workspace-volume
Configurable workspace volume
2 parents b10e1f0 + ac81fb3 commit 0591ab8

File tree

7 files changed

+81
-10
lines changed

7 files changed

+81
-10
lines changed

README.md

+20
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,26 @@ Sidecar containers can be added to your job by specifying them under the top-lev
559559

560560
There is no guarantee that your sidecars will have started before your job, so using retries or a tool like [wait-for-it](https://github.com/vishnubob/wait-for-it) is a good idea to avoid flaky tests.
561561

562+
### The workspace volume
563+
564+
By default the workspace directory (`/workspace`) is mounted as an `emptyDir` ephemeral volume. Other volumes may be more desirable (e.g. a volume claim backed by an NVMe device).
565+
The default workspace volume can be set as stack configuration, e.g.
566+
567+
```yaml
568+
# values.yaml
569+
config:
570+
workspace-volume:
571+
name: workspace-2-the-reckoning
572+
ephemeral:
573+
volumeClaimTemplate:
574+
spec:
575+
accessModes: ["ReadWriteOnce"]
576+
storageClassName: my-special-storage-class
577+
resources:
578+
requests:
579+
storage: 1Gi
580+
```
581+
562582
### Extra volume mounts
563583

564584
In some situations, for example if you want to use [git mirrors](https://buildkite.com/docs/agent/v3#promoted-experiments-git-mirrors) you may want to attach extra volume mounts (in addition to the `/workspace` one) in all the pod containers.

charts/agent-stack-k8s/values.schema.json

+3
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,9 @@
262262
"title": "Causes the controller to prohibit the kubernetes plugin specified within jobs (pipeline YAML) - enabling this causes jobs with a kubernetes plugin to fail, preventing the pipeline YAML from having any influence over the podSpec",
263263
"examples": [true]
264264
},
265+
"workspaceVolume": {
266+
"$ref": "https://kubernetesjsonschema.dev/master/_definitions.json#/definitions/io.k8s.api.core.v1.Volume"
267+
},
265268
"agent-config": {
266269
"type": "object",
267270
"default": null,

cmd/controller/controller_test.go

+19-1
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,28 @@ func TestReadAndParseConfig(t *testing.T) {
3636
ClusterUUID: "beefcafe-abbe-baba-abba-deedcedecade",
3737
ProhibitKubernetesPlugin: true,
3838
GraphQLEndpoint: "http://graphql.buildkite.localhost/v1",
39+
40+
WorkspaceVolume: &corev1.Volume{
41+
Name: "workspace-2-the-reckoning",
42+
VolumeSource: corev1.VolumeSource{
43+
Ephemeral: &corev1.EphemeralVolumeSource{
44+
VolumeClaimTemplate: &corev1.PersistentVolumeClaimTemplate{
45+
Spec: corev1.PersistentVolumeClaimSpec{
46+
AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce},
47+
StorageClassName: ptr("my-special-storage-class"),
48+
Resources: corev1.VolumeResourceRequirements{
49+
Requests: corev1.ResourceList{
50+
"storage": resource.MustParse("1Gi"),
51+
},
52+
},
53+
},
54+
},
55+
},
56+
},
57+
},
3958
AgentConfig: &config.AgentConfig{
4059
Endpoint: ptr("http://agent.buildkite.localhost/v3"),
4160
},
42-
4361
DefaultCommandParams: &config.CommandParams{
4462
Interposer: config.InterposerVector,
4563
EnvFrom: []corev1.EnvFromSource{{

examples/config.yaml

+13
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,19 @@ tags:
2828
# preventing the pipeline YAML from having any influence over the podSpec
2929
prohibit-kubernetes-plugin: true
3030

31+
# The workspace volume can be overriden from its default (an emptyDir named
32+
# 'workspace').
33+
workspace-volume:
34+
name: workspace-2-the-reckoning
35+
ephemeral:
36+
volumeClaimTemplate:
37+
spec:
38+
accessModes: ["ReadWriteOnce"]
39+
storageClassName: my-special-storage-class
40+
resources:
41+
requests:
42+
storage: 1Gi
43+
3144
# Applies to all agents
3245
agent-config:
3346
# Setting a custom Agent REST API endpoint is usually only useful if you have

internal/controller/config/config.go

+4
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ type Config struct {
4949
ImagePullBackOffGracePeriod time.Duration `json:"image-pull-backoff-grace-period" validate:"omitempty"`
5050
JobCancelCheckerPollInterval time.Duration `json:"job-cancel-checker-poll-interval" validate:"omitempty"`
5151

52+
// WorkspaceVolume allows supplying a volume for /workspace. By default
53+
// an EmptyDir volume is created for it.
54+
WorkspaceVolume *corev1.Volume `json:"workspace-volume" validate:"omitempty"`
55+
5256
AgentConfig *AgentConfig `json:"agent-config" validate:"omitempty"`
5357
DefaultCheckoutParams *CheckoutParams `json:"default-checkout-params" validate:"omitempty"`
5458
DefaultCommandParams *CommandParams `json:"default-command-params" validate:"omitempty"`

internal/controller/controller.go

+1
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ func Run(
6666
AgentTokenSecretName: cfg.AgentTokenSecret,
6767
JobTTL: cfg.JobTTL,
6868
AdditionalRedactedVars: cfg.AdditionalRedactedVars,
69+
WorkspaceVolume: cfg.WorkspaceVolume,
6970
AgentConfig: cfg.AgentConfig,
7071
DefaultCheckoutParams: cfg.DefaultCheckoutParams,
7172
DefaultCommandParams: cfg.DefaultCommandParams,

internal/controller/scheduler/scheduler.go

+21-9
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ type Config struct {
4646
AgentTokenSecretName string
4747
JobTTL time.Duration
4848
AdditionalRedactedVars []string
49+
WorkspaceVolume *corev1.Volume
4950
AgentConfig *config.AgentConfig
5051
DefaultCheckoutParams *config.CheckoutParams
5152
DefaultCommandParams *config.CommandParams
@@ -286,7 +287,25 @@ func (w *worker) Build(podSpec *corev1.PodSpec, skipCheckout bool, inputs buildI
286287
Value: strings.Join(redactedVars, ","),
287288
})
288289

289-
volumeMounts := []corev1.VolumeMount{{Name: "workspace", MountPath: "/workspace"}}
290+
// workspaceVolume is shared among most containers, so set it up first.
291+
workspaceVolume := w.cfg.WorkspaceVolume
292+
if workspaceVolume == nil {
293+
// The default workspace volume is an empty dir volume.
294+
workspaceVolume = &corev1.Volume{
295+
Name: "workspace",
296+
VolumeSource: corev1.VolumeSource{
297+
EmptyDir: &corev1.EmptyDirVolumeSource{},
298+
},
299+
}
300+
}
301+
podSpec.Volumes = append(podSpec.Volumes, *workspaceVolume)
302+
303+
// Set up other volumes (hooks, plugins, keys).
304+
w.cfg.AgentConfig.ApplyVolumesTo(podSpec)
305+
306+
// Volume mounts shared among most containers: the workspace volume, and
307+
// any others supplied with ExtraVolumeMounts.
308+
volumeMounts := []corev1.VolumeMount{{Name: workspaceVolume.Name, MountPath: "/workspace"}}
290309
if inputs.k8sPlugin != nil {
291310
volumeMounts = append(volumeMounts, inputs.k8sPlugin.ExtraVolumeMounts...)
292311
}
@@ -614,14 +633,7 @@ func (w *worker) Build(podSpec *corev1.PodSpec, skipCheckout bool, inputs buildI
614633

615634
podSpec.InitContainers = append(initContainers, podSpec.InitContainers...)
616635

617-
podSpec.Volumes = append(podSpec.Volumes, corev1.Volume{
618-
Name: "workspace",
619-
VolumeSource: corev1.VolumeSource{
620-
EmptyDir: &corev1.EmptyDirVolumeSource{},
621-
},
622-
})
623-
w.cfg.AgentConfig.ApplyVolumesTo(podSpec)
624-
636+
// Only attempt the job once.
625637
podSpec.RestartPolicy = corev1.RestartPolicyNever
626638

627639
// Allow podSpec to be overridden by the agent configuration and the k8s plugin

0 commit comments

Comments
 (0)