Skip to content

Commit 96c35e7

Browse files
Remove dependency on the runner's volume (#244)
* bump actions * experiment using init container to prepare working environment * rm script before continuing * fix * Update packages/k8s/src/hooks/run-script-step.ts Co-authored-by: Copilot <[email protected]> * leverage exec stat instead of printf * npm update * document the new constraint --------- Co-authored-by: DenisPalnitsky <[email protected]>
1 parent c67938c commit 96c35e7

17 files changed

+1839
-9044
lines changed

examples/extension.yaml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@ metadata:
44
labels:
55
labeled-by: "extension"
66
spec:
7-
securityContext:
8-
runAsUser: 1000
9-
runAsGroup: 3000
107
restartPolicy: Never
118
containers:
129
- name: $job # overwrites job container

package-lock.json

Lines changed: 496 additions & 3150 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/k8s/README.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,6 @@ rules:
2222
- apiGroups: [""]
2323
resources: ["pods/log"]
2424
verbs: ["get", "list", "watch",]
25-
- apiGroups: ["batch"]
26-
resources: ["jobs"]
27-
verbs: ["get", "list", "create", "delete"]
2825
- apiGroups: [""]
2926
resources: ["secrets"]
3027
verbs: ["get", "list", "create", "delete"]
@@ -43,3 +40,4 @@ rules:
4340
- Building container actions from a dockerfile is not supported at this time
4441
- Container actions will not have access to the services network or job container network
4542
- Docker [create options](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idcontaineroptions) are not supported
43+
- Container actions will have to specify the entrypoint, since the default entrypoint will be overridden to run the commands from the workflow.

packages/k8s/package-lock.json

Lines changed: 595 additions & 5198 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/k8s/package.json

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,18 @@
2020
"hooklib": "file:../hooklib",
2121
"js-yaml": "^4.1.0",
2222
"shlex": "^3.0.0",
23+
"tar-fs": "^3.1.0",
2324
"uuid": "^11.1.0"
2425
},
2526
"devDependencies": {
26-
"@babel/core": "^7.25.2",
27-
"@babel/preset-env": "^7.25.4",
27+
"@babel/core": "^7.28.3",
28+
"@babel/preset-env": "^7.28.3",
2829
"@types/jest": "^30.0.0",
29-
"@types/node": "^24.0.14",
30+
"@types/node": "^24.3.0",
3031
"@vercel/ncc": "^0.38.3",
31-
"babel-jest": "^30.0.4",
32-
"jest": "^30.0.4",
33-
"ts-jest": "^29.4.0",
34-
"typescript": "^5.8.3"
32+
"babel-jest": "^30.1.1",
33+
"jest": "^30.1.1",
34+
"ts-jest": "^29.4.1",
35+
"typescript": "^5.9.2"
3536
}
3637
}

packages/k8s/src/hooks/prepare-job.ts

Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import * as core from '@actions/core'
2-
import * as io from '@actions/io'
32
import * as k8s from '@kubernetes/client-node'
43
import {
54
JobContainerInfo,
@@ -8,26 +7,33 @@ import {
87
writeToResponseFile,
98
ServiceContainerInfo
109
} from 'hooklib'
11-
import path from 'path'
1210
import {
1311
containerPorts,
14-
createPod,
12+
createJobPod,
1513
isPodContainerAlpine,
1614
prunePods,
1715
waitForPodPhases,
18-
getPrepareJobTimeoutSeconds
16+
getPrepareJobTimeoutSeconds,
17+
execCpToPod,
18+
execPodStep
1919
} from '../k8s'
2020
import {
21-
containerVolumes,
21+
CONTAINER_VOLUMES,
2222
DEFAULT_CONTAINER_ENTRY_POINT,
2323
DEFAULT_CONTAINER_ENTRY_POINT_ARGS,
2424
generateContainerName,
2525
mergeContainerWithOptions,
2626
readExtensionFromFile,
2727
PodPhase,
28-
fixArgs
28+
fixArgs,
29+
prepareJobScript
2930
} from '../k8s/utils'
30-
import { CONTAINER_EXTENSION_PREFIX, JOB_CONTAINER_NAME } from './constants'
31+
import {
32+
CONTAINER_EXTENSION_PREFIX,
33+
getJobPodName,
34+
JOB_CONTAINER_NAME
35+
} from './constants'
36+
import { dirname } from 'path'
3137

3238
export async function prepareJob(
3339
args: PrepareJobArgs,
@@ -40,11 +46,9 @@ export async function prepareJob(
4046
await prunePods()
4147

4248
const extension = readExtensionFromFile()
43-
await copyExternalsToRoot()
4449

4550
let container: k8s.V1Container | undefined = undefined
4651
if (args.container?.image) {
47-
core.debug(`Using image '${args.container.image}' for job image`)
4852
container = createContainerSpec(
4953
args.container,
5054
JOB_CONTAINER_NAME,
@@ -56,7 +60,6 @@ export async function prepareJob(
5660
let services: k8s.V1Container[] = []
5761
if (args.services?.length) {
5862
services = args.services.map(service => {
59-
core.debug(`Adding service '${service.image}' to pod definition`)
6063
return createContainerSpec(
6164
service,
6265
generateContainerName(service.image),
@@ -72,7 +75,8 @@ export async function prepareJob(
7275

7376
let createdPod: k8s.V1Pod | undefined = undefined
7477
try {
75-
createdPod = await createPod(
78+
createdPod = await createJobPod(
79+
getJobPodName(),
7680
container,
7781
services,
7882
args.container.registry,
@@ -92,6 +96,13 @@ export async function prepareJob(
9296
`Job pod created, waiting for it to come online ${createdPod?.metadata?.name}`
9397
)
9498

99+
const runnerWorkspace = dirname(process.env.RUNNER_WORKSPACE as string)
100+
101+
let prepareScript: { containerPath: string; runnerPath: string } | undefined
102+
if (args.container?.userMountVolumes?.length) {
103+
prepareScript = prepareJobScript(args.container.userMountVolumes || [])
104+
}
105+
95106
try {
96107
await waitForPodPhases(
97108
createdPod.metadata.name,
@@ -104,6 +115,28 @@ export async function prepareJob(
104115
throw new Error(`pod failed to come online with error: ${err}`)
105116
}
106117

118+
await execCpToPod(createdPod.metadata.name, runnerWorkspace, '/__w')
119+
120+
if (prepareScript) {
121+
await execPodStep(
122+
['sh', '-e', prepareScript.containerPath],
123+
createdPod.metadata.name,
124+
JOB_CONTAINER_NAME
125+
)
126+
127+
const promises: Promise<void>[] = []
128+
for (const vol of args?.container?.userMountVolumes || []) {
129+
promises.push(
130+
execCpToPod(
131+
createdPod.metadata.name,
132+
vol.sourceVolumePath,
133+
vol.targetVolumePath
134+
)
135+
)
136+
}
137+
await Promise.all(promises)
138+
}
139+
107140
core.debug('Job pod is ready for traffic')
108141

109142
let isAlpine = false
@@ -127,7 +160,7 @@ function generateResponseFile(
127160
responseFile: string,
128161
args: PrepareJobArgs,
129162
appPod: k8s.V1Pod,
130-
isAlpine
163+
isAlpine: boolean
131164
): void {
132165
if (!appPod.metadata?.name) {
133166
throw new Error('app pod must have metadata.name specified')
@@ -184,17 +217,6 @@ function generateResponseFile(
184217
writeToResponseFile(responseFile, JSON.stringify(response))
185218
}
186219

187-
async function copyExternalsToRoot(): Promise<void> {
188-
const workspace = process.env['RUNNER_WORKSPACE']
189-
if (workspace) {
190-
await io.cp(
191-
path.join(workspace, '../../externals'),
192-
path.join(workspace, '../externals'),
193-
{ force: true, recursive: true, copySourceDirectory: false }
194-
)
195-
}
196-
}
197-
198220
export function createContainerSpec(
199221
container: JobContainerInfo | ServiceContainerInfo,
200222
name: string,
@@ -244,10 +266,7 @@ export function createContainerSpec(
244266
})
245267
}
246268

247-
podContainer.volumeMounts = containerVolumes(
248-
container.userMountVolumes,
249-
jobContainer
250-
)
269+
podContainer.volumeMounts = CONTAINER_VOLUMES
251270

252271
if (!extension) {
253272
return podContainer

0 commit comments

Comments
 (0)