What happens
A volumeMounts entry added to any $-prefixed container in the hook extension template (ACTIONS_RUNNER_CONTAINER_HOOK_TEMPLATE) ends up on every container in the job pod, not just the one it was declared under. Same goes for a mount declared on $job leaking into the service containers.
This bit us when we needed to mount a shared volume into two containers at different paths — mounting it at the natural path in one container shadowed files that container needs, so we wanted it there only for the other container. The leak makes that impossible: the mount lands on every container.
Why
CONTAINER_VOLUMES is a module-level array, and every container gets that same array assigned by reference:
|
podContainer.volumeMounts = CONTAINER_VOLUMES |
Then mergeContainerWithOptions appends the extension's mounts with mergeLists, which mutates base in place:
|
base.volumeMounts = mergeLists(base.volumeMounts, volumeMounts) |
So the first container's merge pushes onto the shared array, and every later container starts from that already-polluted array. Net result: union of all containers' mounts on all containers.
Repro
Template:
spec:
containers:
- name: $job
volumeMounts:
- name: shared
mountPath: /a
- name: $mysvc
volumeMounts:
- name: shared
mountPath: /b
volumes:
- name: shared
emptyDir: {}
Both /a and /b show up in the job container and the service container.
Fix
Give each container its own copy, e.g. at prepare-job.ts#L287:
podContainer.volumeMounts = [...CONTAINER_VOLUMES]
(Env and ports go through the same mergeLists path but happen to start from per-container arrays, so this is the only one affected.)
Related
Seen on the kubernetes-novolume hook shipped in actions-runner:2.336.0; code above is current main.
What happens
A
volumeMountsentry added to any$-prefixed container in the hook extension template (ACTIONS_RUNNER_CONTAINER_HOOK_TEMPLATE) ends up on every container in the job pod, not just the one it was declared under. Same goes for a mount declared on$jobleaking into the service containers.This bit us when we needed to mount a shared volume into two containers at different paths — mounting it at the natural path in one container shadowed files that container needs, so we wanted it there only for the other container. The leak makes that impossible: the mount lands on every container.
Why
CONTAINER_VOLUMESis a module-level array, and every container gets that same array assigned by reference:runner-container-hooks/packages/k8s/src/hooks/prepare-job.ts
Line 287 in cf62bcc
Then
mergeContainerWithOptionsappends the extension's mounts withmergeLists, which mutatesbasein place:runner-container-hooks/packages/k8s/src/k8s/utils.ts
Line 196 in cf62bcc
runner-container-hooks/packages/k8s/src/k8s/utils.ts
Line 286 in cf62bcc
So the first container's merge pushes onto the shared array, and every later container starts from that already-polluted array. Net result: union of all containers' mounts on all containers.
Repro
Template:
Both
/aand/bshow up in the job container and the service container.Fix
Give each container its own copy, e.g. at prepare-job.ts#L287:
(Env and ports go through the same
mergeListspath but happen to start from per-container arrays, so this is the only one affected.)Related
CONTAINER_VOLUMESinto acontainerVolumes()function returning a fresh array, which would incidentally fix this.mergePodSpecWithOptionsmerge gap (initContainers), same general area.Seen on the
kubernetes-novolumehook shipped inactions-runner:2.336.0; code above is currentmain.