mirror of
https://github.com/actions/runner-container-hooks.git
synced 2025-12-16 17:56:44 +00:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
64000d716a | ||
|
|
4ff4b552a6 | ||
|
|
4cdcf09c43 | ||
|
|
5107bb1d41 | ||
|
|
547ed30dc3 | ||
|
|
17fb66892c | ||
|
|
9319a8566a | ||
|
|
669ec6f706 | ||
|
|
aa658859f8 | ||
|
|
8b83223a2b | ||
|
|
586a052286 | ||
|
|
730509f702 | ||
|
|
3fc91e4132 |
20
.github/workflows/release.yaml
vendored
20
.github/workflows/release.yaml
vendored
@@ -12,6 +12,20 @@ jobs:
|
||||
name: Bootstrap the packages
|
||||
- run: npm run build-all
|
||||
name: Build packages
|
||||
- name: Zip up releases
|
||||
run: |
|
||||
zip -r -j actions-runner-hooks-docker-${{ steps.releaseNotes.outputs.version }}.zip packages/docker/dist
|
||||
zip -r -j actions-runner-hooks-k8s-${{ steps.releaseNotes.outputs.version }}.zip packages/k8s/dist
|
||||
- name: Calculate SHA
|
||||
id: sha
|
||||
shell: bash
|
||||
run: |
|
||||
sha_docker=$(sha256sum actions-runner-hooks-docker-${{ steps.releaseNotes.outputs.version }}.zip | awk '{print $1}')
|
||||
echo "Docker SHA: $sha_docker"
|
||||
echo "docker-sha=$sha_docker" >> $GITHUB_OUTPUT
|
||||
sha_k8s=$(sha256sum actions-runner-hooks-k8s-${{ steps.releaseNotes.outputs.version }}.zip | awk '{print $1}')
|
||||
echo "K8s SHA: $sha_k8s"
|
||||
echo "k8s-sha=$sha_k8s" >> $GITHUB_OUTPUT
|
||||
- uses: actions/github-script@v6
|
||||
id: releaseNotes
|
||||
with:
|
||||
@@ -20,13 +34,11 @@ jobs:
|
||||
const fs = require('fs');
|
||||
const hookVersion = require('./package.json').version
|
||||
var releaseNotes = fs.readFileSync('${{ github.workspace }}/releaseNotes.md', 'utf8').replace(/<HOOK_VERSION>/g, hookVersion)
|
||||
releaseNotes = releaseNotes.replace(/<DOCKER_SHA>/g, '${{ steps.sha.outputs.docker-sha }}')
|
||||
releaseNotes = releaseNotes.replace(/<K8S_SHA>/g, '${{ steps.sha.outputs.k8s-sha }}')
|
||||
console.log(releaseNotes)
|
||||
core.setOutput('version', hookVersion);
|
||||
core.setOutput('note', releaseNotes);
|
||||
- name: Zip up releases
|
||||
run: |
|
||||
zip -r -j actions-runner-hooks-docker-${{ steps.releaseNotes.outputs.version }}.zip packages/docker/dist
|
||||
zip -r -j actions-runner-hooks-k8s-${{ steps.releaseNotes.outputs.version }}.zip packages/k8s/dist
|
||||
- uses: actions/create-release@v1
|
||||
id: createRelease
|
||||
name: Create ${{ steps.releaseNotes.outputs.version }} Hook Release
|
||||
|
||||
32
docs/adrs/0096-hook-extensions.md
Normal file
32
docs/adrs/0096-hook-extensions.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# ADR 0096: Hook extensions
|
||||
|
||||
**Date:** 3 August 2023
|
||||
|
||||
**Status**: Proposed <!--Accepted|Rejected|Superceded|Deprecated-->
|
||||
|
||||
## Context
|
||||
|
||||
The current implementation of container hooks does not allow users to customize the pods created by the hook. While the implementation is designed to be used as is or as a starting point, building and maintaining a custom hook implementation just to specify additional fields is not a good user experience.
|
||||
|
||||
## Decision
|
||||
|
||||
We have decided to add hook extensions to the container hook implementation. This will allow users to customize the pods created by the hook by specifying additional fields. The hook extensions will be implemented in a way that is backwards-compatible with the existing hook implementation.
|
||||
|
||||
To allow customization, the runner executing the hook should have `ACTIONS_RUNNER_CONTAINER_HOOK_TEMPLATE` environment variable pointing to a yaml file on the runner system. The extension specified in that file will be applied both for job pods, and container steps.
|
||||
|
||||
If environment variable is set, but the file can't be read, the hook will fail, signaling incorrect configuration.
|
||||
|
||||
If the environment variable does not exist, the hook will apply the default spec.
|
||||
|
||||
In case the hook is able to read the extended spec, it will first create a default configuration, and then merged modified fields in the following way:
|
||||
|
||||
1. The `.metadata` fields that will be appended if they are not reserved are `labels` and `annotations`.
|
||||
2. The pod spec fields except for `containers` and `volumes` are applied from the template, possibly overwriting the field.
|
||||
3. The volumes are applied in form of appending additional volumes to the default volumes.
|
||||
4. The containers are merged based on the name assigned to them:
|
||||
1. If the name of the container *is not* "$job", the entire spec of the container will be added to the pod definition.
|
||||
2. If the name of the container *is* "$job", the `name` and the `image` fields are going to be ignored and the spec will be applied so that `env`, `volumeMounts`, `ports` are appended to the default container spec created by the hook, while the rest of the fields are going to be applied to the newly created container spec.
|
||||
|
||||
## Consequences
|
||||
|
||||
The addition of hook extensions will provide a better user experience for users who need to customize the pods created by the container hook. However, it will require additional effort to provide the template to the runner pod, and configure it properly.
|
||||
30
examples/extension.yaml
Normal file
30
examples/extension.yaml
Normal file
@@ -0,0 +1,30 @@
|
||||
metadata:
|
||||
annotations:
|
||||
annotated-by: "extension"
|
||||
labels:
|
||||
labeled-by: "extension"
|
||||
spec:
|
||||
securityContext:
|
||||
runAsUser: 1000
|
||||
runAsGroup: 3000
|
||||
restartPolicy: Never
|
||||
containers:
|
||||
- name: $job # overwirtes job container
|
||||
env:
|
||||
- name: ENV1
|
||||
value: "value1"
|
||||
imagePullPolicy: Always
|
||||
image: "busybox:1.28" # Ignored
|
||||
command:
|
||||
- sh
|
||||
args:
|
||||
- -c
|
||||
- sleep 50
|
||||
- name: side-car
|
||||
image: "ubuntu:latest" # required
|
||||
command:
|
||||
- sh
|
||||
args:
|
||||
- -c
|
||||
- sleep 60
|
||||
|
||||
16
package-lock.json
generated
16
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "hooks",
|
||||
"version": "0.3.2",
|
||||
"version": "0.4.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "hooks",
|
||||
"version": "0.3.2",
|
||||
"version": "0.4.0",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@types/jest": "^27.5.1",
|
||||
@@ -2625,9 +2625,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/word-wrap": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
|
||||
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz",
|
||||
"integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
@@ -4509,9 +4509,9 @@
|
||||
}
|
||||
},
|
||||
"word-wrap": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
|
||||
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz",
|
||||
"integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==",
|
||||
"dev": true
|
||||
},
|
||||
"wrappy": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "hooks",
|
||||
"version": "0.3.2",
|
||||
"version": "0.4.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": {
|
||||
|
||||
186
packages/docker/package-lock.json
generated
186
packages/docker/package-lock.json
generated
@@ -136,9 +136,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/core/node_modules/semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
@@ -186,9 +186,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-compilation-targets/node_modules/semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
@@ -3019,9 +3019,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/istanbul-lib-instrument/node_modules/semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
@@ -3848,12 +3848,15 @@
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "7.8.1",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.8.1.tgz",
|
||||
"integrity": "sha512-E1v547OCgJvbvevfjgK9sNKIVXO96NnsTsFPBlg4ZxjhsJSODoH9lk8Bm0OxvHNm6Vm5Yqkl/1fErDxhYL8Skg==",
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/make-dir": {
|
||||
@@ -3872,9 +3875,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/make-dir/node_modules/semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
@@ -4307,6 +4310,12 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/querystringify": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
|
||||
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/queue-microtask": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||
@@ -4355,6 +4364,12 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/requires-port": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/resolve": {
|
||||
"version": "1.22.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz",
|
||||
@@ -4485,18 +4500,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.3.6",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.6.tgz",
|
||||
"integrity": "sha512-HZWqcgwLsjaX1HBD31msI/rXktuIhS+lWvdE4kN9z+8IVT4Itc7vqU2WvYsyD6/sjYCt4dEKH/m1M3dwI9CC5w==",
|
||||
"version": "7.5.4",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
|
||||
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"lru-cache": "^7.4.0"
|
||||
"lru-cache": "^6.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10.0.0 || ^12.0.0 || ^14.0.0 || >=16.0.0"
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/shebang-command": {
|
||||
@@ -4770,14 +4785,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tough-cookie": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz",
|
||||
"integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==",
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz",
|
||||
"integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"psl": "^1.1.33",
|
||||
"punycode": "^2.1.1",
|
||||
"universalify": "^0.1.2"
|
||||
"universalify": "^0.2.0",
|
||||
"url-parse": "^1.5.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
@@ -5010,9 +5026,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/universalify": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
|
||||
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
|
||||
"integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 4.0.0"
|
||||
@@ -5028,6 +5044,16 @@
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/url-parse": {
|
||||
"version": "1.5.10",
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
|
||||
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"querystringify": "^2.1.1",
|
||||
"requires-port": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
@@ -5156,9 +5182,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/word-wrap": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
|
||||
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
|
||||
"integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
@@ -5241,6 +5267,12 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/yargs": {
|
||||
"version": "16.2.0",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
|
||||
@@ -5357,9 +5389,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
@@ -5396,9 +5428,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
@@ -7580,9 +7612,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
@@ -8230,10 +8262,13 @@
|
||||
"peer": true
|
||||
},
|
||||
"lru-cache": {
|
||||
"version": "7.8.1",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.8.1.tgz",
|
||||
"integrity": "sha512-E1v547OCgJvbvevfjgK9sNKIVXO96NnsTsFPBlg4ZxjhsJSODoH9lk8Bm0OxvHNm6Vm5Yqkl/1fErDxhYL8Skg==",
|
||||
"dev": true
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"yallist": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"make-dir": {
|
||||
"version": "3.1.0",
|
||||
@@ -8245,9 +8280,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
@@ -8581,6 +8616,12 @@
|
||||
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
|
||||
"dev": true
|
||||
},
|
||||
"querystringify": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
|
||||
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
|
||||
"dev": true
|
||||
},
|
||||
"queue-microtask": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||
@@ -8606,6 +8647,12 @@
|
||||
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
|
||||
"dev": true
|
||||
},
|
||||
"requires-port": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
|
||||
"dev": true
|
||||
},
|
||||
"resolve": {
|
||||
"version": "1.22.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz",
|
||||
@@ -8693,12 +8740,12 @@
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
"version": "7.3.6",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.6.tgz",
|
||||
"integrity": "sha512-HZWqcgwLsjaX1HBD31msI/rXktuIhS+lWvdE4kN9z+8IVT4Itc7vqU2WvYsyD6/sjYCt4dEKH/m1M3dwI9CC5w==",
|
||||
"version": "7.5.4",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
|
||||
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lru-cache": "^7.4.0"
|
||||
"lru-cache": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"shebang-command": {
|
||||
@@ -8908,14 +8955,15 @@
|
||||
}
|
||||
},
|
||||
"tough-cookie": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz",
|
||||
"integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==",
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz",
|
||||
"integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"psl": "^1.1.33",
|
||||
"punycode": "^2.1.1",
|
||||
"universalify": "^0.1.2"
|
||||
"universalify": "^0.2.0",
|
||||
"url-parse": "^1.5.3"
|
||||
}
|
||||
},
|
||||
"tr46": {
|
||||
@@ -9060,9 +9108,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"universalify": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
|
||||
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
|
||||
"integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
|
||||
"dev": true
|
||||
},
|
||||
"uri-js": {
|
||||
@@ -9075,6 +9123,16 @@
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"url-parse": {
|
||||
"version": "1.5.10",
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
|
||||
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"querystringify": "^2.1.1",
|
||||
"requires-port": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"uuid": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
@@ -9181,9 +9239,9 @@
|
||||
}
|
||||
},
|
||||
"word-wrap": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
|
||||
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
|
||||
"integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
|
||||
"dev": true
|
||||
},
|
||||
"wrap-ansi": {
|
||||
@@ -9240,6 +9298,12 @@
|
||||
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
|
||||
"dev": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"dev": true
|
||||
},
|
||||
"yargs": {
|
||||
"version": "16.2.0",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
|
||||
|
||||
@@ -91,11 +91,12 @@ export async function containerPull(
|
||||
image: string,
|
||||
configLocation: string
|
||||
): Promise<void> {
|
||||
const dockerArgs: string[] = ['pull']
|
||||
const dockerArgs: string[] = []
|
||||
if (configLocation) {
|
||||
dockerArgs.push('--config')
|
||||
dockerArgs.push(configLocation)
|
||||
}
|
||||
dockerArgs.push('pull')
|
||||
dockerArgs.push(image)
|
||||
for (let i = 0; i < 3; i++) {
|
||||
try {
|
||||
|
||||
@@ -40,7 +40,7 @@ export async function prepareJob(
|
||||
if (!container?.image) {
|
||||
core.info('No job container provided, skipping')
|
||||
} else {
|
||||
setupContainer(container)
|
||||
setupContainer(container, true)
|
||||
|
||||
const configLocation = await registryLogin(container.registry)
|
||||
try {
|
||||
@@ -174,9 +174,11 @@ function generateResponseFile(
|
||||
writeToResponseFile(responseFile, JSON.stringify(response))
|
||||
}
|
||||
|
||||
function setupContainer(container): void {
|
||||
container.entryPointArgs = [`-f`, `/dev/null`]
|
||||
container.entryPoint = 'tail'
|
||||
function setupContainer(container, jobContainer = false): void {
|
||||
if (!container.entryPoint && jobContainer) {
|
||||
container.entryPointArgs = [`-f`, `/dev/null`]
|
||||
container.entryPoint = 'tail'
|
||||
}
|
||||
}
|
||||
|
||||
function generateNetworkName(): string {
|
||||
|
||||
24
packages/hooklib/package-lock.json
generated
24
packages/hooklib/package-lock.json
generated
@@ -2215,9 +2215,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.3.7",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
|
||||
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
|
||||
"version": "7.5.4",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
|
||||
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"lru-cache": "^6.0.0"
|
||||
@@ -2532,9 +2532,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/word-wrap": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
|
||||
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz",
|
||||
"integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
@@ -4119,9 +4119,9 @@
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
"version": "7.3.7",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
|
||||
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
|
||||
"version": "7.5.4",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
|
||||
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lru-cache": "^6.0.0"
|
||||
@@ -4344,9 +4344,9 @@
|
||||
}
|
||||
},
|
||||
"word-wrap": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
|
||||
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz",
|
||||
"integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==",
|
||||
"dev": true
|
||||
},
|
||||
"wrappy": {
|
||||
|
||||
125
packages/k8s/package-lock.json
generated
125
packages/k8s/package-lock.json
generated
@@ -13,7 +13,8 @@
|
||||
"@actions/exec": "^1.1.1",
|
||||
"@actions/io": "^1.1.2",
|
||||
"@kubernetes/client-node": "^0.18.1",
|
||||
"hooklib": "file:../hooklib"
|
||||
"hooklib": "file:../hooklib",
|
||||
"js-yaml": "^4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^27.4.1",
|
||||
@@ -3081,9 +3082,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/jest-snapshot/node_modules/semver": {
|
||||
"version": "7.3.7",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
|
||||
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
|
||||
"version": "7.5.4",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
|
||||
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"lru-cache": "^6.0.0"
|
||||
@@ -3878,6 +3879,12 @@
|
||||
"node": ">=0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/querystringify": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
|
||||
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/react-is": {
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
||||
@@ -3949,6 +3956,12 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/requires-port": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/resolve": {
|
||||
"version": "1.22.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz",
|
||||
@@ -4038,9 +4051,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
@@ -4368,14 +4381,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tough-cookie": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz",
|
||||
"integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==",
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz",
|
||||
"integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"psl": "^1.1.33",
|
||||
"punycode": "^2.1.1",
|
||||
"universalify": "^0.1.2"
|
||||
"universalify": "^0.2.0",
|
||||
"url-parse": "^1.5.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
@@ -4437,9 +4451,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ts-jest/node_modules/semver": {
|
||||
"version": "7.3.7",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
|
||||
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
|
||||
"version": "7.5.4",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
|
||||
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"lru-cache": "^6.0.0"
|
||||
@@ -4541,9 +4555,9 @@
|
||||
"integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A=="
|
||||
},
|
||||
"node_modules/universalify": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
|
||||
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
|
||||
"integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 4.0.0"
|
||||
@@ -4557,6 +4571,16 @@
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/url-parse": {
|
||||
"version": "1.5.10",
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
|
||||
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"querystringify": "^2.1.1",
|
||||
"requires-port": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
|
||||
@@ -4686,9 +4710,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/word-wrap": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
|
||||
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz",
|
||||
"integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
@@ -7182,9 +7206,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"semver": {
|
||||
"version": "7.3.7",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
|
||||
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
|
||||
"version": "7.5.4",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
|
||||
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lru-cache": "^6.0.0"
|
||||
@@ -7786,6 +7810,12 @@
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz",
|
||||
"integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA=="
|
||||
},
|
||||
"querystringify": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
|
||||
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
|
||||
"dev": true
|
||||
},
|
||||
"react-is": {
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
||||
@@ -7846,6 +7876,12 @@
|
||||
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
|
||||
"dev": true
|
||||
},
|
||||
"requires-port": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
|
||||
"dev": true
|
||||
},
|
||||
"resolve": {
|
||||
"version": "1.22.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz",
|
||||
@@ -7911,9 +7947,9 @@
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"dev": true
|
||||
},
|
||||
"shebang-command": {
|
||||
@@ -8158,14 +8194,15 @@
|
||||
}
|
||||
},
|
||||
"tough-cookie": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz",
|
||||
"integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==",
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz",
|
||||
"integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"psl": "^1.1.33",
|
||||
"punycode": "^2.1.1",
|
||||
"universalify": "^0.1.2"
|
||||
"universalify": "^0.2.0",
|
||||
"url-parse": "^1.5.3"
|
||||
}
|
||||
},
|
||||
"tr46": {
|
||||
@@ -8194,9 +8231,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"semver": {
|
||||
"version": "7.3.7",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
|
||||
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
|
||||
"version": "7.5.4",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
|
||||
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lru-cache": "^6.0.0"
|
||||
@@ -8269,9 +8306,9 @@
|
||||
"integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A=="
|
||||
},
|
||||
"universalify": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
|
||||
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
|
||||
"integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
|
||||
"dev": true
|
||||
},
|
||||
"uri-js": {
|
||||
@@ -8282,6 +8319,16 @@
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"url-parse": {
|
||||
"version": "1.5.10",
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
|
||||
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"querystringify": "^2.1.1",
|
||||
"requires-port": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"uuid": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
|
||||
@@ -8385,9 +8432,9 @@
|
||||
}
|
||||
},
|
||||
"word-wrap": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
|
||||
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz",
|
||||
"integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==",
|
||||
"dev": true
|
||||
},
|
||||
"wrap-ansi": {
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
"@actions/exec": "^1.1.1",
|
||||
"@actions/io": "^1.1.2",
|
||||
"@kubernetes/client-node": "^0.18.1",
|
||||
"hooklib": "file:../hooklib"
|
||||
"hooklib": "file:../hooklib",
|
||||
"js-yaml": "^4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^27.4.1",
|
||||
|
||||
@@ -42,6 +42,7 @@ export function getSecretName(): string {
|
||||
export const MAX_POD_NAME_LENGTH = 63
|
||||
export const STEP_POD_NAME_SUFFIX_LENGTH = 8
|
||||
export const JOB_CONTAINER_NAME = 'job'
|
||||
export const JOB_CONTAINER_EXTENSION_NAME = '$job'
|
||||
|
||||
export class RunnerInstanceLabel {
|
||||
private podName: string
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import * as core from '@actions/core'
|
||||
import * as io from '@actions/io'
|
||||
import * as k8s from '@kubernetes/client-node'
|
||||
import { ContextPorts, prepareJobArgs, writeToResponseFile } from 'hooklib'
|
||||
import {
|
||||
JobContainerInfo,
|
||||
ContextPorts,
|
||||
PrepareJobArgs,
|
||||
writeToResponseFile
|
||||
} from 'hooklib'
|
||||
import path from 'path'
|
||||
import {
|
||||
containerPorts,
|
||||
@@ -15,12 +20,14 @@ import {
|
||||
DEFAULT_CONTAINER_ENTRY_POINT,
|
||||
DEFAULT_CONTAINER_ENTRY_POINT_ARGS,
|
||||
generateContainerName,
|
||||
mergeContainerWithOptions,
|
||||
readExtensionFromFile,
|
||||
PodPhase
|
||||
} from '../k8s/utils'
|
||||
import { JOB_CONTAINER_NAME } from './constants'
|
||||
import { JOB_CONTAINER_EXTENSION_NAME, JOB_CONTAINER_NAME } from './constants'
|
||||
|
||||
export async function prepareJob(
|
||||
args: prepareJobArgs,
|
||||
args: PrepareJobArgs,
|
||||
responseFile
|
||||
): Promise<void> {
|
||||
if (!args.container) {
|
||||
@@ -28,26 +35,46 @@ export async function prepareJob(
|
||||
}
|
||||
|
||||
await prunePods()
|
||||
|
||||
const extension = readExtensionFromFile()
|
||||
await copyExternalsToRoot()
|
||||
|
||||
let container: k8s.V1Container | undefined = undefined
|
||||
if (args.container?.image) {
|
||||
core.debug(`Using image '${args.container.image}' for job image`)
|
||||
container = createContainerSpec(args.container, JOB_CONTAINER_NAME, true)
|
||||
container = createContainerSpec(
|
||||
args.container,
|
||||
JOB_CONTAINER_NAME,
|
||||
true,
|
||||
extension
|
||||
)
|
||||
}
|
||||
|
||||
let services: k8s.V1Container[] = []
|
||||
if (args.services?.length) {
|
||||
services = args.services.map(service => {
|
||||
core.debug(`Adding service '${service.image}' to pod definition`)
|
||||
return createContainerSpec(service, generateContainerName(service.image))
|
||||
return createContainerSpec(
|
||||
service,
|
||||
generateContainerName(service.image),
|
||||
false,
|
||||
undefined
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
if (!container && !services?.length) {
|
||||
throw new Error('No containers exist, skipping hook invocation')
|
||||
}
|
||||
|
||||
let createdPod: k8s.V1Pod | undefined = undefined
|
||||
try {
|
||||
createdPod = await createPod(container, services, args.container.registry)
|
||||
createdPod = await createPod(
|
||||
container,
|
||||
services,
|
||||
args.container.registry,
|
||||
extension
|
||||
)
|
||||
} catch (err) {
|
||||
await prunePods()
|
||||
throw new Error(`failed to create job pod: ${err}`)
|
||||
@@ -153,9 +180,10 @@ async function copyExternalsToRoot(): Promise<void> {
|
||||
}
|
||||
|
||||
export function createContainerSpec(
|
||||
container,
|
||||
container: JobContainerInfo,
|
||||
name: string,
|
||||
jobContainer = false
|
||||
jobContainer = false,
|
||||
extension?: k8s.V1PodTemplateSpec
|
||||
): k8s.V1Container {
|
||||
if (!container.entryPoint && jobContainer) {
|
||||
container.entryPoint = DEFAULT_CONTAINER_ENTRY_POINT
|
||||
@@ -193,5 +221,17 @@ export function createContainerSpec(
|
||||
jobContainer
|
||||
)
|
||||
|
||||
if (!extension) {
|
||||
return podContainer
|
||||
}
|
||||
|
||||
const from = extension.spec?.containers?.find(
|
||||
c => c.name === JOB_CONTAINER_EXTENSION_NAME
|
||||
)
|
||||
|
||||
if (from) {
|
||||
mergeContainerWithOptions(podContainer, from)
|
||||
}
|
||||
|
||||
return podContainer
|
||||
}
|
||||
|
||||
@@ -12,12 +12,11 @@ import {
|
||||
} from '../k8s'
|
||||
import {
|
||||
containerVolumes,
|
||||
DEFAULT_CONTAINER_ENTRY_POINT,
|
||||
DEFAULT_CONTAINER_ENTRY_POINT_ARGS,
|
||||
PodPhase,
|
||||
writeEntryPointScript
|
||||
mergeContainerWithOptions,
|
||||
readExtensionFromFile
|
||||
} from '../k8s/utils'
|
||||
import { JOB_CONTAINER_NAME } from './constants'
|
||||
import { JOB_CONTAINER_EXTENSION_NAME, JOB_CONTAINER_NAME } from './constants'
|
||||
|
||||
export async function runContainerStep(
|
||||
stepContainer: RunContainerStepArgs
|
||||
@@ -31,10 +30,12 @@ export async function runContainerStep(
|
||||
secretName = await createSecretForEnvs(stepContainer.environmentVariables)
|
||||
}
|
||||
|
||||
core.debug(`Created secret ${secretName} for container job envs`)
|
||||
const container = createPodSpec(stepContainer, secretName)
|
||||
const extension = readExtensionFromFile()
|
||||
|
||||
const job = await createJob(container)
|
||||
core.debug(`Created secret ${secretName} for container job envs`)
|
||||
const container = createContainerSpec(stepContainer, secretName, extension)
|
||||
|
||||
const job = await createJob(container, extension)
|
||||
if (!job.metadata?.name) {
|
||||
throw new Error(
|
||||
`Expected job ${JSON.stringify(
|
||||
@@ -75,24 +76,21 @@ export async function runContainerStep(
|
||||
return Number(exitCode) || 1
|
||||
}
|
||||
|
||||
function createPodSpec(
|
||||
function createContainerSpec(
|
||||
container: RunContainerStepArgs,
|
||||
secretName?: string
|
||||
secretName?: string,
|
||||
extension?: k8s.V1PodTemplateSpec
|
||||
): k8s.V1Container {
|
||||
const podContainer = new k8s.V1Container()
|
||||
podContainer.name = JOB_CONTAINER_NAME
|
||||
podContainer.image = container.image
|
||||
|
||||
const { entryPoint, entryPointArgs } = container
|
||||
container.entryPoint = 'sh'
|
||||
|
||||
const { containerPath } = writeEntryPointScript(
|
||||
container.workingDirectory,
|
||||
entryPoint || DEFAULT_CONTAINER_ENTRY_POINT,
|
||||
entryPoint ? entryPointArgs || [] : DEFAULT_CONTAINER_ENTRY_POINT_ARGS
|
||||
)
|
||||
container.entryPointArgs = ['-e', containerPath]
|
||||
podContainer.command = [container.entryPoint, ...container.entryPointArgs]
|
||||
podContainer.workingDir = container.workingDirectory
|
||||
podContainer.command = container.entryPoint
|
||||
? [container.entryPoint]
|
||||
: undefined
|
||||
podContainer.args = container.entryPointArgs?.length
|
||||
? container.entryPointArgs
|
||||
: undefined
|
||||
|
||||
if (secretName) {
|
||||
podContainer.envFrom = [
|
||||
@@ -106,5 +104,16 @@ function createPodSpec(
|
||||
}
|
||||
podContainer.volumeMounts = containerVolumes(undefined, false, true)
|
||||
|
||||
if (!extension) {
|
||||
return podContainer
|
||||
}
|
||||
|
||||
const from = extension.spec?.containers?.find(
|
||||
c => c.name === JOB_CONTAINER_EXTENSION_NAME
|
||||
)
|
||||
if (from) {
|
||||
mergeContainerWithOptions(podContainer, from)
|
||||
}
|
||||
|
||||
return podContainer
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
getVolumeClaimName,
|
||||
RunnerInstanceLabel
|
||||
} from '../hooks/constants'
|
||||
import { PodPhase } from './utils'
|
||||
import { PodPhase, mergePodSpecWithOptions, mergeObjectMeta } from './utils'
|
||||
|
||||
const kc = new k8s.KubeConfig()
|
||||
|
||||
@@ -58,7 +58,8 @@ export const requiredPermissions = [
|
||||
export async function createPod(
|
||||
jobContainer?: k8s.V1Container,
|
||||
services?: k8s.V1Container[],
|
||||
registry?: Registry
|
||||
registry?: Registry,
|
||||
extension?: k8s.V1PodTemplateSpec
|
||||
): Promise<k8s.V1Pod> {
|
||||
const containers: k8s.V1Container[] = []
|
||||
if (jobContainer) {
|
||||
@@ -80,6 +81,7 @@ export async function createPod(
|
||||
appPod.metadata.labels = {
|
||||
[instanceLabel.key]: instanceLabel.value
|
||||
}
|
||||
appPod.metadata.annotations = {}
|
||||
|
||||
appPod.spec = new k8s.V1PodSpec()
|
||||
appPod.spec.containers = containers
|
||||
@@ -103,12 +105,21 @@ export async function createPod(
|
||||
appPod.spec.imagePullSecrets = [secretReference]
|
||||
}
|
||||
|
||||
if (extension?.metadata) {
|
||||
mergeObjectMeta(appPod, extension.metadata)
|
||||
}
|
||||
|
||||
if (extension?.spec) {
|
||||
mergePodSpecWithOptions(appPod.spec, extension.spec)
|
||||
}
|
||||
|
||||
const { body } = await k8sApi.createNamespacedPod(namespace(), appPod)
|
||||
return body
|
||||
}
|
||||
|
||||
export async function createJob(
|
||||
container: k8s.V1Container
|
||||
container: k8s.V1Container,
|
||||
extension?: k8s.V1PodTemplateSpec
|
||||
): Promise<k8s.V1Job> {
|
||||
const runnerInstanceLabel = new RunnerInstanceLabel()
|
||||
|
||||
@@ -118,6 +129,7 @@ export async function createJob(
|
||||
job.metadata = new k8s.V1ObjectMeta()
|
||||
job.metadata.name = getStepPodName()
|
||||
job.metadata.labels = { [runnerInstanceLabel.key]: runnerInstanceLabel.value }
|
||||
job.metadata.annotations = {}
|
||||
|
||||
job.spec = new k8s.V1JobSpec()
|
||||
job.spec.ttlSecondsAfterFinished = 300
|
||||
@@ -125,6 +137,9 @@ export async function createJob(
|
||||
job.spec.template = new k8s.V1PodTemplateSpec()
|
||||
|
||||
job.spec.template.spec = new k8s.V1PodSpec()
|
||||
job.spec.template.metadata = new k8s.V1ObjectMeta()
|
||||
job.spec.template.metadata.labels = {}
|
||||
job.spec.template.metadata.annotations = {}
|
||||
job.spec.template.spec.containers = [container]
|
||||
job.spec.template.spec.restartPolicy = 'Never'
|
||||
job.spec.template.spec.nodeName = await getCurrentNodeName()
|
||||
@@ -137,6 +152,17 @@ export async function createJob(
|
||||
}
|
||||
]
|
||||
|
||||
if (extension) {
|
||||
if (extension.metadata) {
|
||||
// apply metadata both to the job and the pod created by the job
|
||||
mergeObjectMeta(job, extension.metadata)
|
||||
mergeObjectMeta(job.spec.template, extension.metadata)
|
||||
}
|
||||
if (extension.spec) {
|
||||
mergePodSpecWithOptions(job.spec.template.spec, extension.spec)
|
||||
}
|
||||
}
|
||||
|
||||
const { body } = await k8sBatchV1Api.createNamespacedJob(namespace(), job)
|
||||
return body
|
||||
}
|
||||
@@ -473,6 +499,7 @@ async function getCurrentNodeName(): Promise<string> {
|
||||
}
|
||||
return nodeName
|
||||
}
|
||||
|
||||
export function namespace(): string {
|
||||
if (process.env['ACTIONS_RUNNER_KUBERNETES_NAMESPACE']) {
|
||||
return process.env['ACTIONS_RUNNER_KUBERNETES_NAMESPACE']
|
||||
@@ -554,3 +581,8 @@ export function containerPorts(
|
||||
}
|
||||
return ports
|
||||
}
|
||||
|
||||
export async function getPodByName(name): Promise<k8s.V1Pod> {
|
||||
const { body } = await k8sApi.readNamespacedPod(name, namespace())
|
||||
return body
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import * as k8s from '@kubernetes/client-node'
|
||||
import * as fs from 'fs'
|
||||
import * as yaml from 'js-yaml'
|
||||
import * as core from '@actions/core'
|
||||
import { Mount } from 'hooklib'
|
||||
import * as path from 'path'
|
||||
import { v1 as uuidv4 } from 'uuid'
|
||||
@@ -8,6 +10,8 @@ import { POD_VOLUME_NAME } from './index'
|
||||
export const DEFAULT_CONTAINER_ENTRY_POINT_ARGS = [`-f`, `/dev/null`]
|
||||
export const DEFAULT_CONTAINER_ENTRY_POINT = 'tail'
|
||||
|
||||
export const ENV_HOOK_TEMPLATE_PATH = 'ACTIONS_RUNNER_CONTAINER_HOOK_TEMPLATE'
|
||||
|
||||
export function containerVolumes(
|
||||
userMountVolumes: Mount[] = [],
|
||||
jobContainer = true,
|
||||
@@ -125,7 +129,8 @@ export function writeEntryPointScript(
|
||||
`"${key}=${value
|
||||
.replace(/\\/g, '\\\\')
|
||||
.replace(/"/g, '\\"')
|
||||
.replace(/\$/g, '\\$')}"`
|
||||
.replace(/\$/g, '\\$')
|
||||
.replace(/`/g, '\\`')}"`
|
||||
)
|
||||
}
|
||||
environmentPrefix = `env ${envBuffer.join(' ')} `
|
||||
@@ -158,6 +163,100 @@ export function generateContainerName(image: string): string {
|
||||
return name
|
||||
}
|
||||
|
||||
// Overwrite or append based on container options
|
||||
//
|
||||
// Keep in mind, envs and volumes could be passed as fields in container definition
|
||||
// so default volume mounts and envs are appended first, and then create options are used
|
||||
// to append more values
|
||||
//
|
||||
// Rest of the fields are just applied
|
||||
// For example, container.createOptions.container.image is going to overwrite container.image field
|
||||
export function mergeContainerWithOptions(
|
||||
base: k8s.V1Container,
|
||||
from: k8s.V1Container
|
||||
): void {
|
||||
for (const [key, value] of Object.entries(from)) {
|
||||
if (key === 'name') {
|
||||
core.warning("Skipping name override: name can't be overwritten")
|
||||
continue
|
||||
} else if (key === 'image') {
|
||||
core.warning("Skipping image override: image can't be overwritten")
|
||||
continue
|
||||
} else if (key === 'env') {
|
||||
const envs = value as k8s.V1EnvVar[]
|
||||
base.env = mergeLists(base.env, envs)
|
||||
} else if (key === 'volumeMounts' && value) {
|
||||
const volumeMounts = value as k8s.V1VolumeMount[]
|
||||
base.volumeMounts = mergeLists(base.volumeMounts, volumeMounts)
|
||||
} else if (key === 'ports' && value) {
|
||||
const ports = value as k8s.V1ContainerPort[]
|
||||
base.ports = mergeLists(base.ports, ports)
|
||||
} else {
|
||||
base[key] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function mergePodSpecWithOptions(
|
||||
base: k8s.V1PodSpec,
|
||||
from: k8s.V1PodSpec
|
||||
): void {
|
||||
for (const [key, value] of Object.entries(from)) {
|
||||
if (key === 'containers') {
|
||||
base.containers.push(
|
||||
...from.containers.filter(e => !e.name?.startsWith('$'))
|
||||
)
|
||||
} else if (key === 'volumes' && value) {
|
||||
const volumes = value as k8s.V1Volume[]
|
||||
base.volumes = mergeLists(base.volumes, volumes)
|
||||
} else {
|
||||
base[key] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function mergeObjectMeta(
|
||||
base: { metadata?: k8s.V1ObjectMeta },
|
||||
from: k8s.V1ObjectMeta
|
||||
): void {
|
||||
if (!base.metadata?.labels || !base.metadata?.annotations) {
|
||||
throw new Error(
|
||||
"Can't merge metadata: base.metadata or base.annotations field is undefined"
|
||||
)
|
||||
}
|
||||
if (from?.labels) {
|
||||
for (const [key, value] of Object.entries(from.labels)) {
|
||||
if (base.metadata?.labels?.[key]) {
|
||||
core.warning(`Label ${key} is already defined and will be overwritten`)
|
||||
}
|
||||
base.metadata.labels[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
if (from?.annotations) {
|
||||
for (const [key, value] of Object.entries(from.annotations)) {
|
||||
if (base.metadata?.annotations?.[key]) {
|
||||
core.warning(
|
||||
`Annotation ${key} is already defined and will be overwritten`
|
||||
)
|
||||
}
|
||||
base.metadata.annotations[key] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function readExtensionFromFile(): k8s.V1PodTemplateSpec | undefined {
|
||||
const filePath = process.env[ENV_HOOK_TEMPLATE_PATH]
|
||||
if (!filePath) {
|
||||
return undefined
|
||||
}
|
||||
const doc = yaml.load(fs.readFileSync(filePath, 'utf8'))
|
||||
if (!doc || typeof doc !== 'object') {
|
||||
throw new Error(`Failed to parse ${filePath}`)
|
||||
}
|
||||
return doc as k8s.V1PodTemplateSpec
|
||||
}
|
||||
|
||||
export enum PodPhase {
|
||||
PENDING = 'Pending',
|
||||
RUNNING = 'Running',
|
||||
@@ -166,3 +265,12 @@ export enum PodPhase {
|
||||
UNKNOWN = 'Unknown',
|
||||
COMPLETED = 'Completed'
|
||||
}
|
||||
|
||||
function mergeLists<T>(base?: T[], from?: T[]): T[] {
|
||||
const b: T[] = base || []
|
||||
if (!from?.length) {
|
||||
return b
|
||||
}
|
||||
b.push(...from)
|
||||
return b
|
||||
}
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
import * as fs from 'fs'
|
||||
import * as fs from 'fs'
|
||||
import { containerPorts, POD_VOLUME_NAME } from '../src/k8s'
|
||||
import {
|
||||
containerVolumes,
|
||||
generateContainerName,
|
||||
writeEntryPointScript
|
||||
writeEntryPointScript,
|
||||
mergePodSpecWithOptions,
|
||||
mergeContainerWithOptions,
|
||||
readExtensionFromFile,
|
||||
ENV_HOOK_TEMPLATE_PATH
|
||||
} from '../src/k8s/utils'
|
||||
import * as k8s from '@kubernetes/client-node'
|
||||
import { TestHelper } from './test-setup'
|
||||
|
||||
let testHelper: TestHelper
|
||||
@@ -328,4 +333,183 @@ describe('k8s utils', () => {
|
||||
expect(() => generateContainerName(':latest')).toThrow()
|
||||
})
|
||||
})
|
||||
|
||||
describe('read extension', () => {
|
||||
beforeEach(async () => {
|
||||
testHelper = new TestHelper()
|
||||
await testHelper.initialize()
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
await testHelper.cleanup()
|
||||
})
|
||||
|
||||
it('should throw if env variable is set but file does not exist', () => {
|
||||
process.env[ENV_HOOK_TEMPLATE_PATH] =
|
||||
'/path/that/does/not/exist/data.yaml'
|
||||
expect(() => readExtensionFromFile()).toThrow()
|
||||
})
|
||||
|
||||
it('should return undefined if env variable is not set', () => {
|
||||
delete process.env[ENV_HOOK_TEMPLATE_PATH]
|
||||
expect(readExtensionFromFile()).toBeUndefined()
|
||||
})
|
||||
|
||||
it('should throw if file is empty', () => {
|
||||
let filePath = testHelper.createFile('data.yaml')
|
||||
process.env[ENV_HOOK_TEMPLATE_PATH] = filePath
|
||||
expect(() => readExtensionFromFile()).toThrow()
|
||||
})
|
||||
|
||||
it('should throw if file is not valid yaml', () => {
|
||||
let filePath = testHelper.createFile('data.yaml')
|
||||
fs.writeFileSync(filePath, 'invalid yaml')
|
||||
process.env[ENV_HOOK_TEMPLATE_PATH] = filePath
|
||||
expect(() => readExtensionFromFile()).toThrow()
|
||||
})
|
||||
|
||||
it('should return object if file is valid', () => {
|
||||
let filePath = testHelper.createFile('data.yaml')
|
||||
fs.writeFileSync(
|
||||
filePath,
|
||||
`
|
||||
metadata:
|
||||
labels:
|
||||
label-name: label-value
|
||||
annotations:
|
||||
annotation-name: annotation-value
|
||||
spec:
|
||||
containers:
|
||||
- name: test
|
||||
image: node:14.16
|
||||
- name: job
|
||||
image: ubuntu:latest`
|
||||
)
|
||||
|
||||
process.env[ENV_HOOK_TEMPLATE_PATH] = filePath
|
||||
const extension = readExtensionFromFile()
|
||||
expect(extension).toBeDefined()
|
||||
})
|
||||
})
|
||||
|
||||
it('should merge container spec', () => {
|
||||
const base = {
|
||||
image: 'node:14.16',
|
||||
name: 'test',
|
||||
env: [
|
||||
{
|
||||
name: 'TEST',
|
||||
value: 'TEST'
|
||||
}
|
||||
],
|
||||
ports: [
|
||||
{
|
||||
containerPort: 8080,
|
||||
hostPort: 8080,
|
||||
protocol: 'TCP'
|
||||
}
|
||||
]
|
||||
} as k8s.V1Container
|
||||
|
||||
const from = {
|
||||
ports: [
|
||||
{
|
||||
containerPort: 9090,
|
||||
hostPort: 9090,
|
||||
protocol: 'TCP'
|
||||
}
|
||||
],
|
||||
env: [
|
||||
{
|
||||
name: 'TEST_TWO',
|
||||
value: 'TEST_TWO'
|
||||
}
|
||||
],
|
||||
image: 'ubuntu:latest',
|
||||
name: 'overwrite'
|
||||
} as k8s.V1Container
|
||||
|
||||
const expectContainer = {
|
||||
name: base.name,
|
||||
image: base.image,
|
||||
ports: [
|
||||
...(base.ports as k8s.V1ContainerPort[]),
|
||||
...(from.ports as k8s.V1ContainerPort[])
|
||||
],
|
||||
env: [...(base.env as k8s.V1EnvVar[]), ...(from.env as k8s.V1EnvVar[])]
|
||||
}
|
||||
|
||||
const expectJobContainer = JSON.parse(JSON.stringify(expectContainer))
|
||||
expectJobContainer.name = base.name
|
||||
mergeContainerWithOptions(base, from)
|
||||
expect(base).toStrictEqual(expectContainer)
|
||||
})
|
||||
|
||||
it('should merge pod spec', () => {
|
||||
const base = {
|
||||
containers: [
|
||||
{
|
||||
image: 'node:14.16',
|
||||
name: 'test',
|
||||
env: [
|
||||
{
|
||||
name: 'TEST',
|
||||
value: 'TEST'
|
||||
}
|
||||
],
|
||||
ports: [
|
||||
{
|
||||
containerPort: 8080,
|
||||
hostPort: 8080,
|
||||
protocol: 'TCP'
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
restartPolicy: 'Never'
|
||||
} as k8s.V1PodSpec
|
||||
|
||||
const from = {
|
||||
securityContext: {
|
||||
runAsUser: 1000,
|
||||
fsGroup: 2000
|
||||
},
|
||||
restartPolicy: 'Always',
|
||||
volumes: [
|
||||
{
|
||||
name: 'work',
|
||||
emptyDir: {}
|
||||
}
|
||||
],
|
||||
containers: [
|
||||
{
|
||||
image: 'ubuntu:latest',
|
||||
name: 'side-car',
|
||||
env: [
|
||||
{
|
||||
name: 'TEST',
|
||||
value: 'TEST'
|
||||
}
|
||||
],
|
||||
ports: [
|
||||
{
|
||||
containerPort: 8080,
|
||||
hostPort: 8080,
|
||||
protocol: 'TCP'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
} as k8s.V1PodSpec
|
||||
|
||||
const expected = JSON.parse(JSON.stringify(base))
|
||||
expected.securityContext = from.securityContext
|
||||
expected.restartPolicy = from.restartPolicy
|
||||
expected.volumes = from.volumes
|
||||
expected.containers.push(from.containers[0])
|
||||
|
||||
mergePodSpecWithOptions(base, from)
|
||||
|
||||
expect(base).toStrictEqual(expected)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -3,8 +3,15 @@ import * as path from 'path'
|
||||
import { cleanupJob } from '../src/hooks'
|
||||
import { createContainerSpec, prepareJob } from '../src/hooks/prepare-job'
|
||||
import { TestHelper } from './test-setup'
|
||||
import { generateContainerName } from '../src/k8s/utils'
|
||||
import {
|
||||
ENV_HOOK_TEMPLATE_PATH,
|
||||
generateContainerName,
|
||||
readExtensionFromFile
|
||||
} from '../src/k8s/utils'
|
||||
import { getPodByName } from '../src/k8s'
|
||||
import { V1Container } from '@kubernetes/client-node'
|
||||
import * as yaml from 'js-yaml'
|
||||
import { JOB_CONTAINER_NAME } from '../src/hooks/constants'
|
||||
|
||||
jest.useRealTimers()
|
||||
|
||||
@@ -83,6 +90,46 @@ describe('Prepare job', () => {
|
||||
expect(services[0].args).toBe(undefined)
|
||||
})
|
||||
|
||||
it('should run pod with extensions applied', async () => {
|
||||
process.env[ENV_HOOK_TEMPLATE_PATH] = path.join(
|
||||
__dirname,
|
||||
'../../../examples/extension.yaml'
|
||||
)
|
||||
|
||||
await expect(
|
||||
prepareJob(prepareJobData.args, prepareJobOutputFilePath)
|
||||
).resolves.not.toThrow()
|
||||
|
||||
delete process.env[ENV_HOOK_TEMPLATE_PATH]
|
||||
|
||||
const content = JSON.parse(
|
||||
fs.readFileSync(prepareJobOutputFilePath).toString()
|
||||
)
|
||||
|
||||
const got = await getPodByName(content.state.jobPod)
|
||||
|
||||
expect(got.metadata?.annotations?.['annotated-by']).toBe('extension')
|
||||
expect(got.metadata?.labels?.['labeled-by']).toBe('extension')
|
||||
expect(got.spec?.securityContext?.runAsUser).toBe(1000)
|
||||
expect(got.spec?.securityContext?.runAsGroup).toBe(3000)
|
||||
|
||||
// job container
|
||||
expect(got.spec?.containers[0].name).toBe(JOB_CONTAINER_NAME)
|
||||
expect(got.spec?.containers[0].image).toBe('node:14.16')
|
||||
expect(got.spec?.containers[0].command).toEqual(['sh'])
|
||||
expect(got.spec?.containers[0].args).toEqual(['-c', 'sleep 50'])
|
||||
|
||||
// service container
|
||||
expect(got.spec?.containers[1].image).toBe('redis')
|
||||
expect(got.spec?.containers[1].command).toBeFalsy()
|
||||
expect(got.spec?.containers[1].args).toBeFalsy()
|
||||
// side-car
|
||||
expect(got.spec?.containers[2].name).toBe('side-car')
|
||||
expect(got.spec?.containers[2].image).toBe('ubuntu:latest')
|
||||
expect(got.spec?.containers[2].command).toEqual(['sh'])
|
||||
expect(got.spec?.containers[2].args).toEqual(['-c', 'sleep 60'])
|
||||
})
|
||||
|
||||
test.each([undefined, null, []])(
|
||||
'should not throw exception when portMapping=%p',
|
||||
async pm => {
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { runContainerStep } from '../src/hooks'
|
||||
import { TestHelper } from './test-setup'
|
||||
import { ENV_HOOK_TEMPLATE_PATH } from '../src/k8s/utils'
|
||||
import * as fs from 'fs'
|
||||
import * as yaml from 'js-yaml'
|
||||
import { JOB_CONTAINER_EXTENSION_NAME } from '../src/hooks/constants'
|
||||
|
||||
jest.useRealTimers()
|
||||
|
||||
@@ -23,16 +27,52 @@ describe('Run container step', () => {
|
||||
expect(exitCode).toBe(0)
|
||||
})
|
||||
|
||||
it('should fail if the working directory does not exist', async () => {
|
||||
runContainerStepData.args.workingDirectory = '/foo/bar'
|
||||
await expect(runContainerStep(runContainerStepData.args)).rejects.toThrow()
|
||||
it('should run pod with extensions applied', async () => {
|
||||
const extension = {
|
||||
metadata: {
|
||||
annotations: {
|
||||
foo: 'bar'
|
||||
},
|
||||
labels: {
|
||||
bar: 'baz'
|
||||
}
|
||||
},
|
||||
spec: {
|
||||
containers: [
|
||||
{
|
||||
name: JOB_CONTAINER_EXTENSION_NAME,
|
||||
command: ['sh'],
|
||||
args: ['-c', 'echo test']
|
||||
},
|
||||
{
|
||||
name: 'side-container',
|
||||
image: 'ubuntu:latest',
|
||||
command: ['sh'],
|
||||
args: ['-c', 'echo test']
|
||||
}
|
||||
],
|
||||
restartPolicy: 'Never',
|
||||
securityContext: {
|
||||
runAsUser: 1000,
|
||||
runAsGroup: 3000
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let filePath = testHelper.createFile()
|
||||
fs.writeFileSync(filePath, yaml.dump(extension))
|
||||
process.env[ENV_HOOK_TEMPLATE_PATH] = filePath
|
||||
await expect(
|
||||
runContainerStep(runContainerStepData.args)
|
||||
).resolves.not.toThrow()
|
||||
delete process.env[ENV_HOOK_TEMPLATE_PATH]
|
||||
})
|
||||
|
||||
it('should shold have env variables available', async () => {
|
||||
runContainerStepData.args.entryPoint = 'bash'
|
||||
runContainerStepData.args.entryPointArgs = [
|
||||
'-c',
|
||||
"'if [[ -z $NODE_ENV ]]; then exit 1; fi'"
|
||||
'if [[ -z $NODE_ENV ]]; then exit 1; fi'
|
||||
]
|
||||
await expect(
|
||||
runContainerStep(runContainerStepData.args)
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
<!-- ## Features -->
|
||||
## Bugs
|
||||
|
||||
- Handle `$` symbols in environment variable names and values in k8s [#74]
|
||||
- Fix argument order for 'docker pull' [#85]
|
||||
- Do not overwrite entrypoint if it has already been set or if it is Service container [#83]
|
||||
- Throw if an entrypoint is not specified for container step [#77]
|
||||
- Include sha256 checksums in releaseNotes [#98]
|
||||
- Escape backtick in writeEntryPointScript [#101]
|
||||
- Implement yaml extensions overwriting the default pod/container spec [#75]
|
||||
|
||||
<!-- ## Misc -->
|
||||
|
||||
## SHA-256 Checksums
|
||||
|
||||
The SHA-256 checksums for the packages included in this build are shown below:
|
||||
|
||||
- actions-runner-hooks-docker-<HOOK_VERSION>.zip <DOCKER_SHA>
|
||||
- actions-runner-hooks-k8s-<HOOK_VERSION>.zip <K8S_SHA>
|
||||
|
||||
Reference in New Issue
Block a user