diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e235046..72f9cd0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -13,7 +13,7 @@ You'll need a runner compatible with hooks, a repository with container workflow - You'll need a runner compatible with hooks, a repository with container workflows to which you can register the runner and the hooks from this repository. - See [the runner contributing.md](../../github/CONTRIBUTING.MD) for how to get started with runner development. - Build your hook using `npm run build` -- Enable the hooks by setting `ACTIONS_RUNNER_CONTAINER_HOOK=./packages/{libraryname}/dist/index.js` file generated by [ncc](https://github.com/vercel/ncc) +- Enable the hooks by setting `ACTIONS_RUNNER_CONTAINER_HOOKS=./packages/{libraryname}/dist/index.js` file generated by [ncc](https://github.com/vercel/ncc) - Configure your self hosted runner against the a repository you have admin access - Run a workflow with a container job, for example ``` diff --git a/package.json b/package.json index 5defeb0..42e2922 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hooks", - "version": "0.1.3", + "version": "0.2.0", "description": "Three projects are included - k8s: a kubernetes hook implementation that spins up pods dynamically to run a job - docker: A hook implementation of the runner's docker implementation - A hook lib, which contains shared typescript definitions and utilities that the other packages consume", "main": "", "directories": { diff --git a/packages/docker/package-lock.json b/packages/docker/package-lock.json index 41d062d..262e4cd 100644 --- a/packages/docker/package-lock.json +++ b/packages/docker/package-lock.json @@ -9,7 +9,7 @@ "version": "0.1.0", "license": "MIT", "dependencies": { - "@actions/core": "^1.6.0", + "@actions/core": "^1.9.1", "@actions/exec": "^1.1.1", "hooklib": "file:../hooklib", "uuid": "^8.3.2" @@ -30,7 +30,7 @@ "version": "0.1.0", "license": "MIT", "dependencies": { - "@actions/core": "^1.6.0" + "@actions/core": "^1.9.1" }, "devDependencies": { "@types/node": "^17.0.23", @@ -43,11 +43,12 @@ } }, "node_modules/@actions/core": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.6.0.tgz", - "integrity": "sha512-NB1UAZomZlCV/LmJqkLhNTqtKfFXJZAUPcfl/zqG7EfsQdeUJtaWO98SGbuQ3pydJ3fHl2CvI/51OKYlCYYcaw==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.9.1.tgz", + "integrity": "sha512-5ad+U2YGrmmiw6du20AQW5XuWo7UKN2052FjSV7MX+Wfjf8sCqcsZe62NfgHys4QI4/Y+vQvLKYL8jWtA1ZBTA==", "dependencies": { - "@actions/http-client": "^1.0.11" + "@actions/http-client": "^2.0.1", + "uuid": "^8.3.2" } }, "node_modules/@actions/exec": { @@ -59,11 +60,11 @@ } }, "node_modules/@actions/http-client": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.11.tgz", - "integrity": "sha512-VRYHGQV1rqnROJqdMvGUbY/Kn8vriQe/F9HR2AlYHzmKuM/p3kjNuXhmdBfcVgsvRWTz5C5XW5xvndZrVBuAYg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz", + "integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==", "dependencies": { - "tunnel": "0.0.6" + "tunnel": "^0.0.6" } }, "node_modules/@actions/io": { @@ -5279,11 +5280,12 @@ }, "dependencies": { "@actions/core": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.6.0.tgz", - "integrity": "sha512-NB1UAZomZlCV/LmJqkLhNTqtKfFXJZAUPcfl/zqG7EfsQdeUJtaWO98SGbuQ3pydJ3fHl2CvI/51OKYlCYYcaw==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.9.1.tgz", + "integrity": "sha512-5ad+U2YGrmmiw6du20AQW5XuWo7UKN2052FjSV7MX+Wfjf8sCqcsZe62NfgHys4QI4/Y+vQvLKYL8jWtA1ZBTA==", "requires": { - "@actions/http-client": "^1.0.11" + "@actions/http-client": "^2.0.1", + "uuid": "^8.3.2" } }, "@actions/exec": { @@ -5295,11 +5297,11 @@ } }, "@actions/http-client": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.11.tgz", - "integrity": "sha512-VRYHGQV1rqnROJqdMvGUbY/Kn8vriQe/F9HR2AlYHzmKuM/p3kjNuXhmdBfcVgsvRWTz5C5XW5xvndZrVBuAYg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz", + "integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==", "requires": { - "tunnel": "0.0.6" + "tunnel": "^0.0.6" } }, "@actions/io": { @@ -7376,7 +7378,7 @@ "hooklib": { "version": "file:../hooklib", "requires": { - "@actions/core": "^1.6.0", + "@actions/core": "^1.9.1", "@types/node": "^17.0.23", "@typescript-eslint/parser": "^5.18.0", "@zeit/ncc": "^0.22.3", diff --git a/packages/docker/package.json b/packages/docker/package.json index 235cdea..087fd94 100644 --- a/packages/docker/package.json +++ b/packages/docker/package.json @@ -10,7 +10,7 @@ "author": "", "license": "MIT", "dependencies": { - "@actions/core": "^1.6.0", + "@actions/core": "^1.9.1", "@actions/exec": "^1.1.1", "hooklib": "file:../hooklib", "uuid": "^8.3.2" diff --git a/packages/docker/src/dockerCommands/container.ts b/packages/docker/src/dockerCommands/container.ts index 68c3f26..116115f 100644 --- a/packages/docker/src/dockerCommands/container.ts +++ b/packages/docker/src/dockerCommands/container.ts @@ -427,6 +427,9 @@ export async function containerRun( dockerArgs.push(args.image) if (args.entryPointArgs) { for (const entryPointArg of args.entryPointArgs) { + if (!entryPointArg) { + continue + } dockerArgs.push(entryPointArg) } } diff --git a/packages/docker/src/utils.ts b/packages/docker/src/utils.ts index fd974c5..4f2bdf7 100644 --- a/packages/docker/src/utils.ts +++ b/packages/docker/src/utils.ts @@ -16,6 +16,7 @@ export async function runDockerCommand( args: string[], options?: RunDockerCommandOptions ): Promise { + options = optionsWithDockerEnvs(options) const pipes = await exec.getExecOutput('docker', args, options) if (pipes.exitCode !== 0) { core.error(`Docker failed with exit code ${pipes.exitCode}`) @@ -24,6 +25,45 @@ export async function runDockerCommand( return Promise.resolve(pipes.stdout) } +export function optionsWithDockerEnvs( + options?: RunDockerCommandOptions +): RunDockerCommandOptions | undefined { + // From https://docs.docker.com/engine/reference/commandline/cli/#environment-variables + const dockerCliEnvs = new Set([ + 'DOCKER_API_VERSION', + 'DOCKER_CERT_PATH', + 'DOCKER_CONFIG', + 'DOCKER_CONTENT_TRUST_SERVER', + 'DOCKER_CONTENT_TRUST', + 'DOCKER_CONTEXT', + 'DOCKER_DEFAULT_PLATFORM', + 'DOCKER_HIDE_LEGACY_COMMANDS', + 'DOCKER_HOST', + 'DOCKER_STACK_ORCHESTRATOR', + 'DOCKER_TLS_VERIFY', + 'BUILDKIT_PROGRESS' + ]) + const dockerEnvs = {} + for (const key in process.env) { + if (dockerCliEnvs.has(key)) { + dockerEnvs[key] = process.env[key] + } + } + + const newOptions = { + workingDir: options?.workingDir, + input: options?.input, + env: options?.env || {} + } + + // Set docker envs or overwrite provided ones + for (const [key, value] of Object.entries(dockerEnvs)) { + newOptions.env[key] = value as string + } + + return newOptions +} + export function sanitize(val: string): string { if (!val || typeof val !== 'string') { return '' diff --git a/packages/docker/tests/utils-test.ts b/packages/docker/tests/utils-test.ts index ec6768d..6211b62 100644 --- a/packages/docker/tests/utils-test.ts +++ b/packages/docker/tests/utils-test.ts @@ -1,4 +1,4 @@ -import { sanitize } from '../src/utils' +import { optionsWithDockerEnvs, sanitize } from '../src/utils' describe('Utilities', () => { it('should return sanitized image name', () => { @@ -9,4 +9,41 @@ describe('Utilities', () => { const validStr = 'teststr8_one' expect(sanitize(validStr)).toBe(validStr) }) + + describe('with docker options', () => { + it('should augment options with docker environment variables', () => { + process.env.DOCKER_HOST = 'unix:///run/user/1001/docker.sock' + process.env.DOCKER_NOTEXIST = 'notexist' + + const optionDefinitions: any = [ + undefined, + {}, + { env: {} }, + { env: { DOCKER_HOST: 'unix://var/run/docker.sock' } } + ] + for (const opt of optionDefinitions) { + let options = optionsWithDockerEnvs(opt) + expect(options).toBeDefined() + expect(options?.env).toBeDefined() + expect(options?.env?.DOCKER_HOST).toBe(process.env.DOCKER_HOST) + expect(options?.env?.DOCKER_NOTEXIST).toBeUndefined() + } + }) + + it('should not overwrite other options', () => { + process.env.DOCKER_HOST = 'unix:///run/user/1001/docker.sock' + const opt = { + workingDir: 'test', + input: Buffer.from('test') + } + + const options = optionsWithDockerEnvs(opt) + expect(options).toBeDefined() + expect(options?.workingDir).toBe(opt.workingDir) + expect(options?.input).toBe(opt.input) + expect(options?.env).toStrictEqual({ + DOCKER_HOST: process.env.DOCKER_HOST + }) + }) + }) }) diff --git a/packages/hooklib/package-lock.json b/packages/hooklib/package-lock.json index ccb9091..f2a3e14 100644 --- a/packages/hooklib/package-lock.json +++ b/packages/hooklib/package-lock.json @@ -9,7 +9,7 @@ "version": "0.1.0", "license": "MIT", "dependencies": { - "@actions/core": "^1.6.0" + "@actions/core": "^1.9.1" }, "devDependencies": { "@types/node": "^17.0.23", @@ -22,19 +22,20 @@ } }, "node_modules/@actions/core": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.6.0.tgz", - "integrity": "sha512-NB1UAZomZlCV/LmJqkLhNTqtKfFXJZAUPcfl/zqG7EfsQdeUJtaWO98SGbuQ3pydJ3fHl2CvI/51OKYlCYYcaw==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.9.1.tgz", + "integrity": "sha512-5ad+U2YGrmmiw6du20AQW5XuWo7UKN2052FjSV7MX+Wfjf8sCqcsZe62NfgHys4QI4/Y+vQvLKYL8jWtA1ZBTA==", "dependencies": { - "@actions/http-client": "^1.0.11" + "@actions/http-client": "^2.0.1", + "uuid": "^8.3.2" } }, "node_modules/@actions/http-client": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.11.tgz", - "integrity": "sha512-VRYHGQV1rqnROJqdMvGUbY/Kn8vriQe/F9HR2AlYHzmKuM/p3kjNuXhmdBfcVgsvRWTz5C5XW5xvndZrVBuAYg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz", + "integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==", "dependencies": { - "tunnel": "0.0.6" + "tunnel": "^0.0.6" } }, "node_modules/@eslint/eslintrc": { @@ -2485,6 +2486,14 @@ "punycode": "^2.1.0" } }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/v8-compile-cache": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", @@ -2546,19 +2555,20 @@ }, "dependencies": { "@actions/core": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.6.0.tgz", - "integrity": "sha512-NB1UAZomZlCV/LmJqkLhNTqtKfFXJZAUPcfl/zqG7EfsQdeUJtaWO98SGbuQ3pydJ3fHl2CvI/51OKYlCYYcaw==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.9.1.tgz", + "integrity": "sha512-5ad+U2YGrmmiw6du20AQW5XuWo7UKN2052FjSV7MX+Wfjf8sCqcsZe62NfgHys4QI4/Y+vQvLKYL8jWtA1ZBTA==", "requires": { - "@actions/http-client": "^1.0.11" + "@actions/http-client": "^2.0.1", + "uuid": "^8.3.2" } }, "@actions/http-client": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.11.tgz", - "integrity": "sha512-VRYHGQV1rqnROJqdMvGUbY/Kn8vriQe/F9HR2AlYHzmKuM/p3kjNuXhmdBfcVgsvRWTz5C5XW5xvndZrVBuAYg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz", + "integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==", "requires": { - "tunnel": "0.0.6" + "tunnel": "^0.0.6" } }, "@eslint/eslintrc": { @@ -4300,6 +4310,11 @@ "punycode": "^2.1.0" } }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + }, "v8-compile-cache": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", diff --git a/packages/hooklib/package.json b/packages/hooklib/package.json index 393d569..0414ae2 100644 --- a/packages/hooklib/package.json +++ b/packages/hooklib/package.json @@ -23,6 +23,6 @@ "typescript": "^4.6.3" }, "dependencies": { - "@actions/core": "^1.6.0" + "@actions/core": "^1.9.1" } } diff --git a/packages/k8s/package-lock.json b/packages/k8s/package-lock.json index f8f65df..f91eda5 100644 --- a/packages/k8s/package-lock.json +++ b/packages/k8s/package-lock.json @@ -9,7 +9,7 @@ "version": "0.1.0", "license": "MIT", "dependencies": { - "@actions/core": "^1.6.0", + "@actions/core": "^1.9.1", "@actions/exec": "^1.1.1", "@actions/io": "^1.1.2", "@kubernetes/client-node": "^0.16.3", @@ -28,7 +28,7 @@ "version": "0.1.0", "license": "MIT", "dependencies": { - "@actions/core": "^1.6.0" + "@actions/core": "^1.9.1" }, "devDependencies": { "@types/node": "^17.0.23", @@ -41,11 +41,20 @@ } }, "node_modules/@actions/core": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.8.2.tgz", - "integrity": "sha512-FXcBL7nyik8K5ODeCKlxi+vts7torOkoDAKfeh61EAkAy1HAvwn9uVzZBY0f15YcQTcZZ2/iSGBFHEuioZWfDA==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.9.1.tgz", + "integrity": "sha512-5ad+U2YGrmmiw6du20AQW5XuWo7UKN2052FjSV7MX+Wfjf8sCqcsZe62NfgHys4QI4/Y+vQvLKYL8jWtA1ZBTA==", "dependencies": { - "@actions/http-client": "^2.0.1" + "@actions/http-client": "^2.0.1", + "uuid": "^8.3.2" + } + }, + "node_modules/@actions/core/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" } }, "node_modules/@actions/exec": { @@ -3428,9 +3437,9 @@ } }, "node_modules/jose": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/jose/-/jose-2.0.5.tgz", - "integrity": "sha512-BAiDNeDKTMgk4tvD0BbxJ8xHEHBZgpeRZ1zGPPsitSyMgjoMWiLGYAE7H7NpP5h0lPppQajQs871E8NHUrzVPA==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/jose/-/jose-2.0.6.tgz", + "integrity": "sha512-FVoPY7SflDodE4lknJmbAHSUjLCzE2H1F6MS0RYKMQ8SR+lNccpMf8R4eqkNYyyUjR5qZReOzZo5C5YiHOCjjg==", "dependencies": { "@panva/asn1.js": "^1.0.0" }, @@ -5145,11 +5154,19 @@ }, "dependencies": { "@actions/core": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.8.2.tgz", - "integrity": "sha512-FXcBL7nyik8K5ODeCKlxi+vts7torOkoDAKfeh61EAkAy1HAvwn9uVzZBY0f15YcQTcZZ2/iSGBFHEuioZWfDA==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.9.1.tgz", + "integrity": "sha512-5ad+U2YGrmmiw6du20AQW5XuWo7UKN2052FjSV7MX+Wfjf8sCqcsZe62NfgHys4QI4/Y+vQvLKYL8jWtA1ZBTA==", "requires": { - "@actions/http-client": "^2.0.1" + "@actions/http-client": "^2.0.1", + "uuid": "^8.3.2" + }, + "dependencies": { + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + } } }, "@actions/exec": { @@ -7074,7 +7091,7 @@ "hooklib": { "version": "file:../hooklib", "requires": { - "@actions/core": "^1.6.0", + "@actions/core": "^1.9.1", "@types/node": "^17.0.23", "@typescript-eslint/parser": "^5.18.0", "@zeit/ncc": "^0.22.3", @@ -7804,9 +7821,9 @@ } }, "jose": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/jose/-/jose-2.0.5.tgz", - "integrity": "sha512-BAiDNeDKTMgk4tvD0BbxJ8xHEHBZgpeRZ1zGPPsitSyMgjoMWiLGYAE7H7NpP5h0lPppQajQs871E8NHUrzVPA==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/jose/-/jose-2.0.6.tgz", + "integrity": "sha512-FVoPY7SflDodE4lknJmbAHSUjLCzE2H1F6MS0RYKMQ8SR+lNccpMf8R4eqkNYyyUjR5qZReOzZo5C5YiHOCjjg==", "requires": { "@panva/asn1.js": "^1.0.0" } diff --git a/packages/k8s/package.json b/packages/k8s/package.json index 901fa88..3d8299e 100644 --- a/packages/k8s/package.json +++ b/packages/k8s/package.json @@ -13,7 +13,7 @@ "author": "", "license": "MIT", "dependencies": { - "@actions/core": "^1.6.0", + "@actions/core": "^1.9.1", "@actions/exec": "^1.1.1", "@actions/io": "^1.1.2", "@kubernetes/client-node": "^0.16.3", diff --git a/packages/k8s/src/hooks/prepare-job.ts b/packages/k8s/src/hooks/prepare-job.ts index 488206d..7d79f96 100644 --- a/packages/k8s/src/hooks/prepare-job.ts +++ b/packages/k8s/src/hooks/prepare-job.ts @@ -158,7 +158,7 @@ function createPodSpec( name: string, jobContainer = false ): k8s.V1Container { - if (!container.entryPoint) { + if (!container.entryPoint && jobContainer) { container.entryPoint = DEFAULT_CONTAINER_ENTRY_POINT container.entryPointArgs = DEFAULT_CONTAINER_ENTRY_POINT_ARGS } diff --git a/packages/k8s/src/k8s/index.ts b/packages/k8s/src/k8s/index.ts index 389d159..cab7fae 100644 --- a/packages/k8s/src/k8s/index.ts +++ b/packages/k8s/src/k8s/index.ts @@ -556,27 +556,37 @@ class BackOffManager { export function containerPorts( container: ContainerInfo ): k8s.V1ContainerPort[] { - // 8080:8080/tcp - const portFormat = /(\d{1,5})(:(\d{1,5}))?(\/(tcp|udp))?/ - const ports: k8s.V1ContainerPort[] = [] for (const portDefinition of container.portMappings) { - const submatches = portFormat.exec(portDefinition) - if (!submatches) { - throw new Error( - `Port definition "${portDefinition}" is in incorrect format` - ) + const portProtoSplit = portDefinition.split('/') + if (portProtoSplit.length > 2) { + throw new Error(`Unexpected port format: ${portDefinition}`) } + const port = new k8s.V1ContainerPort() - port.hostPort = Number(submatches[1]) - if (submatches[3]) { - port.containerPort = Number(submatches[3]) + port.protocol = + portProtoSplit.length === 2 ? portProtoSplit[1].toUpperCase() : 'TCP' + + const portSplit = portProtoSplit[0].split(':') + if (portSplit.length > 2) { + throw new Error('ports should have at most one ":" separator') } - if (submatches[5]) { - port.protocol = submatches[5].toUpperCase() + + const parsePort = (p: string): number => { + const num = Number(p) + if (!Number.isInteger(num) || num < 1 || num > 65535) { + throw new Error(`invalid container port: ${p}`) + } + return num + } + + if (portSplit.length === 1) { + port.containerPort = parsePort(portSplit[0]) } else { - port.protocol = 'TCP' + port.hostPort = parsePort(portSplit[0]) + port.containerPort = parsePort(portSplit[1]) } + ports.push(port) } return ports diff --git a/packages/k8s/src/k8s/utils.ts b/packages/k8s/src/k8s/utils.ts index d3072b4..e2a23aa 100644 --- a/packages/k8s/src/k8s/utils.ts +++ b/packages/k8s/src/k8s/utils.ts @@ -22,16 +22,18 @@ export function containerVolumes( const workspacePath = process.env.GITHUB_WORKSPACE as string if (containerAction) { + const i = workspacePath.lastIndexOf('_work/') + const workspaceRelativePath = workspacePath.slice(i + '_work/'.length) mounts.push( { name: POD_VOLUME_NAME, mountPath: '/github/workspace', - subPath: workspacePath.substring(workspacePath.indexOf('work/') + 1) + subPath: workspaceRelativePath }, { name: POD_VOLUME_NAME, mountPath: '/github/file_commands', - subPath: workspacePath.substring(workspacePath.indexOf('work/') + 1) + subPath: '_temp/_runner_file_commands' } ) return mounts diff --git a/packages/k8s/tests/constants-test.ts b/packages/k8s/tests/constants-test.ts index 4fc1eb1..cd053fd 100644 --- a/packages/k8s/tests/constants-test.ts +++ b/packages/k8s/tests/constants-test.ts @@ -4,6 +4,7 @@ import { getSecretName, getStepPodName, getVolumeClaimName, + JOB_CONTAINER_NAME, MAX_POD_NAME_LENGTH, RunnerInstanceLabel, STEP_POD_NAME_SUFFIX_LENGTH @@ -170,4 +171,12 @@ describe('constants', () => { } }) }) + + describe('const values', () => { + it('should have constants set', () => { + expect(JOB_CONTAINER_NAME).toBeTruthy() + expect(MAX_POD_NAME_LENGTH).toBeGreaterThan(0) + expect(STEP_POD_NAME_SUFFIX_LENGTH).toBeGreaterThan(0) + }) + }) }) diff --git a/packages/k8s/tests/k8s-utils-test.ts b/packages/k8s/tests/k8s-utils-test.ts index cf30fda..4341375 100644 --- a/packages/k8s/tests/k8s-utils-test.ts +++ b/packages/k8s/tests/k8s-utils-test.ts @@ -1,5 +1,5 @@ import * as fs from 'fs' -import { POD_VOLUME_NAME } from '../src/k8s' +import { containerPorts, POD_VOLUME_NAME } from '../src/k8s' import { containerVolumes, writeEntryPointScript } from '../src/k8s/utils' import { TestHelper } from './test-setup' @@ -103,19 +103,22 @@ describe('k8s utils', () => { it('should have container action volumes', () => { let volumes = containerVolumes([], true, true) - expect( - volumes.find(e => e.mountPath === '/github/workspace') - ).toBeTruthy() - expect( - volumes.find(e => e.mountPath === '/github/file_commands') - ).toBeTruthy() + let workspace = volumes.find(e => e.mountPath === '/github/workspace') + let fileCommands = volumes.find( + e => e.mountPath === '/github/file_commands' + ) + expect(workspace).toBeTruthy() + expect(workspace?.subPath).toBe('repo/repo') + expect(fileCommands).toBeTruthy() + expect(fileCommands?.subPath).toBe('_temp/_runner_file_commands') + volumes = containerVolumes([], false, true) - expect( - volumes.find(e => e.mountPath === '/github/workspace') - ).toBeTruthy() - expect( - volumes.find(e => e.mountPath === '/github/file_commands') - ).toBeTruthy() + workspace = volumes.find(e => e.mountPath === '/github/workspace') + fileCommands = volumes.find(e => e.mountPath === '/github/file_commands') + expect(workspace).toBeTruthy() + expect(workspace?.subPath).toBe('repo/repo') + expect(fileCommands).toBeTruthy() + expect(fileCommands?.subPath).toBe('_temp/_runner_file_commands') }) it('should have externals, github home and github workflow mounts if job container', () => { @@ -149,5 +152,73 @@ describe('k8s utils', () => { volumes = containerVolumes([], false, false) expect(volumes.every(e => e.name === POD_VOLUME_NAME)).toBeTruthy() }) + + it('should parse container ports', () => { + const tt = [ + { + spec: '8080:80', + want: { + containerPort: 80, + hostPort: 8080, + protocol: 'TCP' + } + }, + { + spec: '8080:80/udp', + want: { + containerPort: 80, + hostPort: 8080, + protocol: 'UDP' + } + }, + { + spec: '8080/udp', + want: { + containerPort: 8080, + hostPort: undefined, + protocol: 'UDP' + } + }, + { + spec: '8080', + want: { + containerPort: 8080, + hostPort: undefined, + protocol: 'TCP' + } + } + ] + + for (const tc of tt) { + const got = containerPorts({ portMappings: [tc.spec] }) + for (const [key, value] of Object.entries(tc.want)) { + expect(got[0][key]).toBe(value) + } + } + }) + + it('should throw when ports are out of range (0, 65536)', () => { + expect(() => containerPorts({ portMappings: ['65536'] })).toThrow() + expect(() => containerPorts({ portMappings: ['0'] })).toThrow() + expect(() => containerPorts({ portMappings: ['65536/udp'] })).toThrow() + expect(() => containerPorts({ portMappings: ['0/udp'] })).toThrow() + expect(() => containerPorts({ portMappings: ['1:65536'] })).toThrow() + expect(() => containerPorts({ portMappings: ['65536:1'] })).toThrow() + expect(() => containerPorts({ portMappings: ['1:65536/tcp'] })).toThrow() + expect(() => containerPorts({ portMappings: ['65536:1/tcp'] })).toThrow() + expect(() => containerPorts({ portMappings: ['1:'] })).toThrow() + expect(() => containerPorts({ portMappings: [':1'] })).toThrow() + expect(() => containerPorts({ portMappings: ['1:/tcp'] })).toThrow() + expect(() => containerPorts({ portMappings: [':1/tcp'] })).toThrow() + }) + + it('should throw on multi ":" splits', () => { + expect(() => containerPorts({ portMappings: ['1:1:1'] })).toThrow() + }) + + it('should throw on multi "/" splits', () => { + expect(() => containerPorts({ portMappings: ['1:1/tcp/udp'] })).toThrow() + expect(() => containerPorts({ portMappings: ['1/tcp/udp'] })).toThrow() + }) }) }) diff --git a/releaseNotes.md b/releaseNotes.md index f49fb6a..7d8a4d3 100644 --- a/releaseNotes.md +++ b/releaseNotes.md @@ -1,6 +1,9 @@ ## Features +- Always use the Docker related ENVs from the host machine instead of ENVs from the runner job [#40] +- Use user defined entrypoints for service containers (instead of `tail -f /dev/null`) ## Bugs -- Fixed an issue where default private registry images did not pull correctly [#25] +- Fixed substring issue with /github/workspace and /github/file_commands [#35] +- Fixed issue related to setting hostPort and containerPort when formatting is not recognized by k8s default [#38] -## Misc \ No newline at end of file +