actions / actions/runner-container-hooks

k8s: extension volumeMounts leak onto all containers (shared CONTAINER_VOLUMES array)

Open Beginner friendly
#442 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
153
Forks
112
Avg merge
6m
Merged PRs (30d)
1

Description

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:

https://github.com/actions/runner-container-hooks/blob/cf62bccba0d59addaf08a115f96ebcd81fb499d8/packages/k8s/src/hooks/prepare-job.ts#L287

Then mergeContainerWithOptions appends the extension's mounts with mergeLists, which mutates base in place:

https://github.com/actions/runner-container-hooks/blob/cf62bccba0d59addaf08a115f96ebcd81fb499d8/packages/k8s/src/k8s/utils.ts#L196

https://github.com/actions/runner-container-hooks/blob/cf62bccba0d59addaf08a115f96ebcd81fb499d8/packages/k8s/src/k8s/utils.ts#L286

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
  • #399 refactors CONTAINER_VOLUMES into a containerVolumes() function returning a fresh array, which would incidentally fix this.
  • #335 is another mergePodSpecWithOptions merge gap (initContainers), same general area.

Seen on the kubernetes-novolume hook shipped in actions-runner:2.336.0; code above is current main.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in packages/k8s/src/hooks/prepare-job.ts around line 287 and inspect how podContainer.volumeMounts is initialized. Read mergeContainerWithOptions and mergeLists in packages/k8s/src/k8s/utils.ts, then verify the YAML reproduction: each container should retain only its declared mounts, without changing the behavior of environment variables or ports.

Written by the indexing model from the issue text.

Assessment

Tech stack
kubernetes, typescript
Domain
devops, infrastructure
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
88/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.