mirror of
https://github.com/actions/actions-runner-controller.git
synced 2025-12-10 11:41:27 +00:00
Compare commits
294 Commits
actions-ru
...
actions-ru
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
332548093a | ||
|
|
b4e143dadc | ||
|
|
93c4dd856e | ||
|
|
93aea48c38 | ||
|
|
14b17cca73 | ||
|
|
5298c6ea29 | ||
|
|
8fa08d59b1 | ||
|
|
003c552c34 | ||
|
|
83370d7f95 | ||
|
|
7da9d0ae19 | ||
|
|
b56fa6a748 | ||
|
|
a22ee8a5f1 | ||
|
|
e1762ba746 | ||
|
|
710e2fbc3a | ||
|
|
433552770e | ||
|
|
ba2a32eef6 | ||
|
|
de0d7ad78c | ||
|
|
0382f3bbd5 | ||
|
|
b6640a033c | ||
|
|
998c028d90 | ||
|
|
f8dffab19d | ||
|
|
7ff5b7da8c | ||
|
|
6aaff4ecee | ||
|
|
437d0173b0 | ||
|
|
a389292478 | ||
|
|
6eadb03669 | ||
|
|
aa60021ab0 | ||
|
|
50e26bd2f6 | ||
|
|
2dd13b4a19 | ||
|
|
35af24cf03 | ||
|
|
ca96b66fbe | ||
|
|
4db5fbc7a1 | ||
|
|
add83bc7bc | ||
|
|
666bba784c | ||
|
|
0672ff0ff9 | ||
|
|
f2f22827a6 | ||
|
|
a3a801757d | ||
|
|
0c003f20d4 | ||
|
|
863760828a | ||
|
|
517fae4119 | ||
|
|
d9e1e64dc6 | ||
|
|
3c4ab2d479 | ||
|
|
3ca96557a6 | ||
|
|
5fd6ec4bc8 | ||
|
|
6863bdb208 | ||
|
|
f3fcb428ae | ||
|
|
41bae32a9f | ||
|
|
e4879e7ae4 | ||
|
|
e5bb130fda | ||
|
|
e7a21cfc53 | ||
|
|
8f54644b08 | ||
|
|
c56d6a6c85 | ||
|
|
a96c3e1102 | ||
|
|
d29de8d454 | ||
|
|
12c4d96250 | ||
|
|
46a13c0626 | ||
|
|
e32a8054d0 | ||
|
|
0deb6809b9 | ||
|
|
21af1ec19d | ||
|
|
dfadb86d66 | ||
|
|
c91e76f169 | ||
|
|
718232f8f4 | ||
|
|
c7f5f7d161 | ||
|
|
33bb6902bc | ||
|
|
aeb0601147 | ||
|
|
991c0b3211 | ||
|
|
71da6d5271 | ||
|
|
e4fd4bc99c | ||
|
|
d9a8dc7e84 | ||
|
|
795cf8b1de | ||
|
|
0615c2adb1 | ||
|
|
a918e56ece | ||
|
|
546b5251ed | ||
|
|
74dda4ea1b | ||
|
|
b29816290a | ||
|
|
921daff61b | ||
|
|
e233f7ad6a | ||
|
|
623c84fa52 | ||
|
|
d4fb6204cb | ||
|
|
f8e07c7fe4 | ||
|
|
f73713859c | ||
|
|
e0a7be253e | ||
|
|
915739b972 | ||
|
|
4925880e5e | ||
|
|
c143fd50b5 | ||
|
|
dbd668ae2d | ||
|
|
5c1be3265b | ||
|
|
ebcd838501 | ||
|
|
6ef276b239 | ||
|
|
f70f325f48 | ||
|
|
f7c336f9dd | ||
|
|
ae380f5987 | ||
|
|
4bf1c12a98 | ||
|
|
cb561d8db4 | ||
|
|
eaf6d2f2e2 | ||
|
|
5ae7ce16e0 | ||
|
|
bdcde44642 | ||
|
|
5116e3800e | ||
|
|
4e107a4e50 | ||
|
|
93238697d9 | ||
|
|
48f62b4c89 | ||
|
|
ea94b3cc5b | ||
|
|
0cac005ab2 | ||
|
|
55ca7bfdf5 | ||
|
|
ca97f39fcb | ||
|
|
f0c8c07428 | ||
|
|
e54edea918 | ||
|
|
e58f82bfce | ||
|
|
244e0dd987 | ||
|
|
02009cef17 | ||
|
|
2b5af62184 | ||
|
|
ec58ad19e0 | ||
|
|
cc9fe33ef5 | ||
|
|
4a5a85fd61 | ||
|
|
56b26fd751 | ||
|
|
36e95dad47 | ||
|
|
3724b46033 | ||
|
|
538e2783d7 | ||
|
|
72ca998266 | ||
|
|
d439ed5c81 | ||
|
|
58c2bdf2bb | ||
|
|
fe9164b025 | ||
|
|
06141b39b4 | ||
|
|
ac4c3fd365 | ||
|
|
dc29e31bcc | ||
|
|
784019f3d7 | ||
|
|
fc55477c1c | ||
|
|
3f78f71137 | ||
|
|
e511401e51 | ||
|
|
37aa1a0b8c | ||
|
|
bea0775bec | ||
|
|
79a494b2aa | ||
|
|
97404144eb | ||
|
|
b77489d098 | ||
|
|
4152afbd30 | ||
|
|
29f621e1c8 | ||
|
|
5651ba6ead | ||
|
|
759cc4b47f | ||
|
|
4ede0c18d0 | ||
|
|
9091d9b756 | ||
|
|
a09c2564d9 | ||
|
|
a555c90fd5 | ||
|
|
38644cf4e8 | ||
|
|
23f357db10 | ||
|
|
584745b67d | ||
|
|
df9592dc99 | ||
|
|
8071ac7066 | ||
|
|
3c33eca501 | ||
|
|
aa827474b2 | ||
|
|
c75c9f9226 | ||
|
|
c09a04ec01 | ||
|
|
618276e3d3 | ||
|
|
18dd89c884 | ||
|
|
98b17dc0a5 | ||
|
|
c658dcfa6d | ||
|
|
c4996d4bbd | ||
|
|
7a3fa4f362 | ||
|
|
1bfd743e69 | ||
|
|
734f3bd63a | ||
|
|
409dc4c114 | ||
|
|
4b9a6c6700 | ||
|
|
86e1a4a8f3 | ||
|
|
544d620bc3 | ||
|
|
1cfe1974c4 | ||
|
|
7e4b6ebd6d | ||
|
|
11cb9b7882 | ||
|
|
10b88bf070 | ||
|
|
8b619e7c6f | ||
|
|
fea1457f12 | ||
|
|
473295e3fc | ||
|
|
9f6f962fc7 | ||
|
|
2a475f25c7 | ||
|
|
dd9f25ea78 | ||
|
|
b8e4eee904 | ||
|
|
edbdef8d20 | ||
|
|
a190fa97bb | ||
|
|
bfc5ea4727 | ||
|
|
5a9e8545aa | ||
|
|
4446ba57e1 | ||
|
|
d62c8a4697 | ||
|
|
946d5b1fa7 | ||
|
|
da6b07660e | ||
|
|
e3deb0d752 | ||
|
|
82641e5036 | ||
|
|
2fe6adf5b7 | ||
|
|
736126b793 | ||
|
|
6abf5bbac8 | ||
|
|
dc4f116bda | ||
|
|
cda10fd243 | ||
|
|
b5d1a63bdf | ||
|
|
6f3e23973d | ||
|
|
a517c1ff66 | ||
|
|
9b28e633c1 | ||
|
|
8161136cbd | ||
|
|
a9ac5a1cbf | ||
|
|
d4f35cff4f | ||
|
|
f661249f07 | ||
|
|
73e430ce54 | ||
|
|
858ef8979d | ||
|
|
1ce0a183a6 | ||
|
|
63935d2053 | ||
|
|
fc63d6d26e | ||
|
|
5ea08411e6 | ||
|
|
067ed2e5ec | ||
|
|
d86bd2bcd7 | ||
|
|
ddd417f756 | ||
|
|
0386c0734c | ||
|
|
af96de6184 | ||
|
|
abb8615796 | ||
|
|
bc7a3cab1b | ||
|
|
e2c8163b8c | ||
|
|
84d16c1c12 | ||
|
|
071898c96b | ||
|
|
f24e2fa44e | ||
|
|
3c7d3d6b57 | ||
|
|
23f091d7fa | ||
|
|
667764e027 | ||
|
|
de693c4191 | ||
|
|
510fc9c834 | ||
|
|
7fd5e24961 | ||
|
|
9974b1a2b7 | ||
|
|
bd91b73fd9 | ||
|
|
a7ae910ee4 | ||
|
|
2733c36d0e | ||
|
|
0ef9a22cd4 | ||
|
|
933b0c7888 | ||
|
|
1b7ec33135 | ||
|
|
a62882d243 | ||
|
|
0cd13fe51d | ||
|
|
01c8dc237e | ||
|
|
7c4db63718 | ||
|
|
3d88b9630a | ||
|
|
1152e6b31d | ||
|
|
ac27df8301 | ||
|
|
9dd26168d6 | ||
|
|
18bfb28c0b | ||
|
|
84210e900b | ||
|
|
ef3313d147 | ||
|
|
c7eea169ad | ||
|
|
63be0223ad | ||
|
|
5bbea772f7 | ||
|
|
2aa3f1e142 | ||
|
|
3e988afc09 | ||
|
|
84210f3d2b | ||
|
|
536692181b | ||
|
|
23403172cb | ||
|
|
8a8ec43364 | ||
|
|
78c01fd31d | ||
|
|
bf45aa9f6b | ||
|
|
b5aa1750bb | ||
|
|
cdc9d20e7a | ||
|
|
8035d6d9f8 | ||
|
|
65f7ee92a6 | ||
|
|
fca8a538db | ||
|
|
95ddc77245 | ||
|
|
b5194fd75a | ||
|
|
adf69bbea0 | ||
|
|
b43ef70ac6 | ||
|
|
f1caebbaf0 | ||
|
|
ede28f5046 | ||
|
|
f08ab1490d | ||
|
|
772ca57056 | ||
|
|
51b13e3bab | ||
|
|
81017b130f | ||
|
|
bdbcf66569 | ||
|
|
0e15a78541 | ||
|
|
f85c3d06d9 | ||
|
|
51ba7d7160 | ||
|
|
759349de11 | ||
|
|
3014e98681 | ||
|
|
5f4be6a883 | ||
|
|
b98f470a70 | ||
|
|
e46b90f758 | ||
|
|
3a7e8c844b | ||
|
|
65a67ee61c | ||
|
|
215ba36fd1 | ||
|
|
27774b47bd | ||
|
|
fbde2b9a41 | ||
|
|
212098183a | ||
|
|
4a5097d8cf | ||
|
|
9c57d085f8 | ||
|
|
d6622f9369 | ||
|
|
3b67ee727f | ||
|
|
e6bddcd238 | ||
|
|
f60e57d789 | ||
|
|
3ca1152420 | ||
|
|
e94fa19843 | ||
|
|
99832d7104 | ||
|
|
289bcd8b64 | ||
|
|
5e8cba82c2 | ||
|
|
dabbc99c78 | ||
|
|
d01595cfbc | ||
|
|
c1e5829b03 | ||
|
|
800d6bd586 |
53
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
53
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -1,8 +1,18 @@
|
|||||||
name: Bug Report
|
name: Bug Report
|
||||||
description: File a bug report
|
description: File a bug report
|
||||||
title: "Bug"
|
title: "<Please write what didn't work for you here>"
|
||||||
labels: ["bug"]
|
labels: ["bug"]
|
||||||
body:
|
body:
|
||||||
|
- type: checkboxes
|
||||||
|
id: read-troubleshooting-guide
|
||||||
|
attributes:
|
||||||
|
label: Checks
|
||||||
|
description: Please check all the boxes below before submitting
|
||||||
|
options:
|
||||||
|
- label: I've already read https://github.com/actions-runner-controller/actions-runner-controller/blob/master/TROUBLESHOOTING.md and I'm sure my issue is not covered in the troubleshooting guide.
|
||||||
|
required: true
|
||||||
|
- label: I'm not using a custom entrypoint in my runner image
|
||||||
|
required: true
|
||||||
- type: input
|
- type: input
|
||||||
id: controller-version
|
id: controller-version
|
||||||
attributes:
|
attributes:
|
||||||
@@ -17,6 +27,12 @@ body:
|
|||||||
label: Helm Chart Version
|
label: Helm Chart Version
|
||||||
description: Run `helm list` and see what's shown under CHART VERSION. Any release tags prefixed with `actions-runner-controller-` are for chart releases
|
description: Run `helm list` and see what's shown under CHART VERSION. Any release tags prefixed with `actions-runner-controller-` are for chart releases
|
||||||
placeholder: ex. 0.11.0
|
placeholder: ex. 0.11.0
|
||||||
|
- type: input
|
||||||
|
id: cert-manager-version
|
||||||
|
attributes:
|
||||||
|
label: CertManager Version
|
||||||
|
description: Run `kubectl get po -o yaml $CERT_MANAGER_POD` and see the image tag, or run `helm list` and see what's shown under APP VERSION for your cert-manager Helm release.
|
||||||
|
placeholder: ex. 1.8
|
||||||
- type: dropdown
|
- type: dropdown
|
||||||
id: deployment-method
|
id: deployment-method
|
||||||
attributes:
|
attributes:
|
||||||
@@ -29,11 +45,22 @@ body:
|
|||||||
- Other
|
- Other
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: cert-manager
|
||||||
|
attributes:
|
||||||
|
label: cert-manager installation
|
||||||
|
description: Confirm that you've installed cert-manager correctly by answering a few questions
|
||||||
|
placeholder: |
|
||||||
|
- Did you follow https://github.com/actions-runner-controller/actions-runner-controller#installation? If not, describe the installation process so that we can reproduce your environment.
|
||||||
|
- Are you sure you've installed cert-manager from an official source?
|
||||||
|
(Note that we won't provide user support for cert-manager itself. Make sure cert-manager is fully working before testing ARC or reporting a bug
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
id: checks
|
id: checks
|
||||||
attributes:
|
attributes:
|
||||||
label: Checks
|
label: Checks
|
||||||
description: Please check the boxes below before submitting
|
description: Please check all the boxes below before submitting
|
||||||
options:
|
options:
|
||||||
- label: This isn't a question or user support case (For Q&A and community support, go to [Discussions](https://github.com/actions-runner-controller/actions-runner-controller/discussions). It might also be a good idea to contract with any of contributors and maintainers if your business is so critical and therefore you need priority support
|
- label: This isn't a question or user support case (For Q&A and community support, go to [Discussions](https://github.com/actions-runner-controller/actions-runner-controller/discussions). It might also be a good idea to contract with any of contributors and maintainers if your business is so critical and therefore you need priority support
|
||||||
required: true
|
required: true
|
||||||
@@ -41,7 +68,9 @@ body:
|
|||||||
required: true
|
required: true
|
||||||
- label: My actions-runner-controller version (v0.x.y) does support the feature
|
- label: My actions-runner-controller version (v0.x.y) does support the feature
|
||||||
required: true
|
required: true
|
||||||
- label: I've already upgraded ARC to the latest and it didn't fix the issue
|
- label: I've already upgraded ARC (including the CRDs, see charts/actions-runner-controller/docs/UPGRADING.md for details) to the latest and it didn't fix the issue
|
||||||
|
required: true
|
||||||
|
- label: I've migrated to the workflow job webhook event (if you using webhook driven scaling)
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: resource-definitions
|
id: resource-definitions
|
||||||
@@ -112,10 +141,12 @@ body:
|
|||||||
- type: textarea
|
- type: textarea
|
||||||
id: controller-logs
|
id: controller-logs
|
||||||
attributes:
|
attributes:
|
||||||
label: Controller Logs
|
label: Whole Controller Logs
|
||||||
description: "Include logs from `actions-runner-controller`'s controller-manager pod"
|
description: "NEVER EVER OMIT THIS! Include logs from `actions-runner-controller`'s controller-manager pod. Don't omit the parts you think irrelevant!"
|
||||||
render: shell
|
render: shell
|
||||||
placeholder: |
|
placeholder: |
|
||||||
|
PROVIDE THE LOGS VIA A GIST LINK (https://gist.github.com/), NOT DIRECTLY IN THIS TEXT AREA
|
||||||
|
|
||||||
To grab controller logs:
|
To grab controller logs:
|
||||||
|
|
||||||
# Set NS according to your setup
|
# Set NS according to your setup
|
||||||
@@ -125,17 +156,17 @@ body:
|
|||||||
kubectl -n $NS get po
|
kubectl -n $NS get po
|
||||||
|
|
||||||
kubectl -n $NS logs $POD_NAME > arc.log
|
kubectl -n $NS logs $POD_NAME > arc.log
|
||||||
|
|
||||||
Upload it to e.g. https://gist.github.com/ and paste the link to it here.
|
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: runner-pod-logs
|
id: runner-pod-logs
|
||||||
attributes:
|
attributes:
|
||||||
label: Runner Pod Logs
|
label: Whole Runner Pod Logs
|
||||||
description: "Include logs from runner pod(s)"
|
description: "Include logs from runner pod(s). Please don't omit the parts you think irrelevant!"
|
||||||
render: shell
|
render: shell
|
||||||
placeholder: |
|
placeholder: |
|
||||||
|
PROVIDE THE WHOLE LOGS VIA A GIST LINK (https://gist.github.com/), NOT DIRECTLY IN THIS TEXT AREA
|
||||||
|
|
||||||
To grab the runner pod logs:
|
To grab the runner pod logs:
|
||||||
|
|
||||||
# Set NS according to your setup. It should match your RunnerDeployment's metadata.namespace.
|
# Set NS according to your setup. It should match your RunnerDeployment's metadata.namespace.
|
||||||
@@ -146,8 +177,8 @@ body:
|
|||||||
|
|
||||||
kubectl -n $NS logs $POD_NAME -c runner > runnerpod_runner.log
|
kubectl -n $NS logs $POD_NAME -c runner > runnerpod_runner.log
|
||||||
kubectl -n $NS logs $POD_NAME -c docker > runnerpod_docker.log
|
kubectl -n $NS logs $POD_NAME -c docker > runnerpod_docker.log
|
||||||
|
|
||||||
Upload it to e.g. https://gist.github.com/ and paste the link to it here.
|
If any of the containers are getting terminated immediately, try adding `--previous` to the kubectl-logs command to obtain logs emitted before the termination.
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
|
|||||||
3
.github/ISSUE_TEMPLATE/config.yml
vendored
3
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,5 +1,4 @@
|
|||||||
# Blank issues are mainly for maintainers who are known to write complete issue descriptions without need to following a form
|
blank_issues_enabled: false
|
||||||
blank_issues_enabled: true
|
|
||||||
contact_links:
|
contact_links:
|
||||||
- name: Sponsor ARC Maintainers
|
- name: Sponsor ARC Maintainers
|
||||||
about: If your business relies on the continued maintainance of actions-runner-controller, please consider sponsoring the project and the maintainers.
|
about: If your business relies on the continued maintainance of actions-runner-controller, please consider sponsoring the project and the maintainers.
|
||||||
|
|||||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,19 +1,21 @@
|
|||||||
---
|
---
|
||||||
name: Feature request
|
name: Feature request
|
||||||
about: Suggest an idea for this project
|
about: Suggest an idea for this project
|
||||||
|
labels: enhancement
|
||||||
title: ''
|
title: ''
|
||||||
assignees: ''
|
assignees: ''
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Is your feature request related to a problem? Please describe.**
|
### What would you like added?
|
||||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
|
||||||
|
|
||||||
**Describe the solution you'd like**
|
*A clear and concise description of what you want to happen.*
|
||||||
A clear and concise description of what you want to happen.
|
|
||||||
|
|
||||||
**Describe alternatives you've considered**
|
Note: Feature requests to integrate vendor specific cloud tools (e.g. `awscli`, `gcloud-sdk`, `azure-cli`) will likely be rejected as the Runner image aims to be vendor agnostic.
|
||||||
A clear and concise description of any alternative solutions or features you've considered.
|
|
||||||
|
|
||||||
**Additional context**
|
### Why is this needed?
|
||||||
Add any other context or screenshots about the feature request here.
|
|
||||||
|
*A clear and concise description of any alternative solutions or features you've considered.*
|
||||||
|
|
||||||
|
### Additional context
|
||||||
|
|
||||||
|
*Add any other context or screenshots about the feature request here.*
|
||||||
|
|||||||
@@ -29,23 +29,23 @@ runs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v1
|
uses: docker/setup-qemu-action@v2
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v1
|
uses: docker/setup-buildx-action@v2
|
||||||
with:
|
with:
|
||||||
version: latest
|
version: latest
|
||||||
|
|
||||||
- name: Login to DockerHub
|
- name: Login to DockerHub
|
||||||
if: ${{ github.ref == 'master' && github.event.pull_request.merged == true }}
|
if: ${{ github.event_name == 'release' || github.event_name == 'push' && github.ref == 'refs/heads/master' }}
|
||||||
uses: docker/login-action@v1
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
username: ${{ inputs.username }}
|
username: ${{ inputs.username }}
|
||||||
password: ${{ inputs.password }}
|
password: ${{ inputs.password }}
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@v1
|
if: ${{ github.event_name == 'release' || github.event_name == 'push' && github.ref == 'refs/heads/master' }}
|
||||||
if: ${{ github.ref == 'master' && github.event.pull_request.merged == true }}
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ inputs.ghcr_username }}
|
username: ${{ inputs.ghcr_username }}
|
||||||
|
|||||||
25
.github/lock.yml
vendored
25
.github/lock.yml
vendored
@@ -1,25 +0,0 @@
|
|||||||
# Configuration for Lock Threads
|
|
||||||
# Repo: https://github.com/dessant/lock-threads-app
|
|
||||||
# App: https://github.com/apps/lock
|
|
||||||
|
|
||||||
# Number of days of inactivity before a closed issue or pull request is locked
|
|
||||||
daysUntilLock: 7
|
|
||||||
|
|
||||||
# Skip issues and pull requests created before a given timestamp. Timestamp must
|
|
||||||
# follow ISO 8601 (`YYYY-MM-DD`). Set to `false` to disable
|
|
||||||
skipCreatedBefore: false
|
|
||||||
|
|
||||||
# Issues and pull requests with these labels will be ignored. Set to `[]` to disable
|
|
||||||
exemptLabels: []
|
|
||||||
|
|
||||||
# Label to add before locking, such as `outdated`. Set to `false` to disable
|
|
||||||
lockLabel: false
|
|
||||||
|
|
||||||
# Comment to post before locking. Set to `false` to disable
|
|
||||||
lockComment: >
|
|
||||||
This thread has been automatically locked since there has not been
|
|
||||||
any recent activity after it was closed. Please open a new issue for
|
|
||||||
related bugs.
|
|
||||||
|
|
||||||
# Assign `resolved` as the reason for locking. Set to `false` to disable
|
|
||||||
setLockReason: true
|
|
||||||
7
.github/renovate.json5
vendored
7
.github/renovate.json5
vendored
@@ -13,7 +13,7 @@
|
|||||||
{
|
{
|
||||||
// use https://github.com/actions/runner/releases
|
// use https://github.com/actions/runner/releases
|
||||||
"fileMatch": [
|
"fileMatch": [
|
||||||
".github/workflows/runners.yml"
|
".github/workflows/runners.yaml"
|
||||||
],
|
],
|
||||||
"matchStrings": ["RUNNER_VERSION: +(?<currentValue>.*?)\\n"],
|
"matchStrings": ["RUNNER_VERSION: +(?<currentValue>.*?)\\n"],
|
||||||
"depNameTemplate": "actions/runner",
|
"depNameTemplate": "actions/runner",
|
||||||
@@ -30,8 +30,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fileMatch": [
|
"fileMatch": [
|
||||||
"runner/Dockerfile",
|
"runner/actions-runner.dockerfile",
|
||||||
"runner/Dockerfile.dindrunner"
|
"runner/actions-runner-dind.dockerfile",
|
||||||
|
"runner/actions-runner-dind-rootless.dockerfile"
|
||||||
],
|
],
|
||||||
"matchStrings": ["RUNNER_VERSION=+(?<currentValue>.*?)\\n"],
|
"matchStrings": ["RUNNER_VERSION=+(?<currentValue>.*?)\\n"],
|
||||||
"depNameTemplate": "actions/runner",
|
"depNameTemplate": "actions/runner",
|
||||||
|
|||||||
23
.github/workflows/golangci-lint.yaml
vendored
Normal file
23
.github/workflows/golangci-lint.yaml
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
name: golangci-lint
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: read
|
||||||
|
jobs:
|
||||||
|
golangci:
|
||||||
|
name: lint
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/setup-go@v3
|
||||||
|
with:
|
||||||
|
go-version: 1.19
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: golangci-lint
|
||||||
|
uses: golangci/golangci-lint-action@v3
|
||||||
|
with:
|
||||||
|
only-new-issues: true
|
||||||
|
version: v1.49.0
|
||||||
@@ -1,26 +1,28 @@
|
|||||||
name: Publish Controller Image
|
name: Publish ARC
|
||||||
|
|
||||||
on:
|
on:
|
||||||
release:
|
release:
|
||||||
types: [published]
|
types:
|
||||||
|
- published
|
||||||
|
|
||||||
|
# https://docs.github.com/en/rest/overview/permissions-required-for-github-apps
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
packages: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
release-controller:
|
||||||
runs-on: ubuntu-latest
|
|
||||||
name: Release
|
name: Release
|
||||||
|
runs-on: ubuntu-latest
|
||||||
env:
|
env:
|
||||||
DOCKERHUB_USERNAME: ${{ secrets.DOCKER_USER }}
|
DOCKERHUB_USERNAME: ${{ secrets.DOCKER_USER }}
|
||||||
steps:
|
steps:
|
||||||
- name: Set outputs
|
|
||||||
id: vars
|
|
||||||
run: echo ::set-output name=sha_short::${GITHUB_SHA::7}
|
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- uses: actions/setup-go@v3
|
- uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: '1.17.7'
|
go-version: '1.18.2'
|
||||||
|
|
||||||
- name: Install tools
|
- name: Install tools
|
||||||
run: |
|
run: |
|
||||||
@@ -39,31 +41,31 @@ jobs:
|
|||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: make github-release
|
run: |
|
||||||
|
make github-release
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Setup Docker Environment
|
||||||
uses: docker/setup-qemu-action@v1
|
id: vars
|
||||||
|
uses: ./.github/actions/setup-docker-environment
|
||||||
- name: Set up Docker Buildx
|
|
||||||
id: buildx
|
|
||||||
uses: docker/setup-buildx-action@v1
|
|
||||||
with:
|
with:
|
||||||
version: latest
|
username: ${{ env.DOCKERHUB_USERNAME }}
|
||||||
|
|
||||||
- name: Login to DockerHub
|
|
||||||
uses: docker/login-action@v1
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKER_USER }}
|
|
||||||
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
|
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
|
||||||
|
ghcr_username: ${{ github.actor }}
|
||||||
|
ghcr_password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Build and Push
|
- name: Build and Push
|
||||||
uses: docker/build-push-action@v2
|
uses: docker/build-push-action@v3
|
||||||
with:
|
with:
|
||||||
file: Dockerfile
|
file: Dockerfile
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
|
build-args: VERSION=${{ env.VERSION }}
|
||||||
push: true
|
push: true
|
||||||
tags: |
|
tags: |
|
||||||
${{ env.DOCKERHUB_USERNAME }}/actions-runner-controller:latest
|
${{ env.DOCKERHUB_USERNAME }}/actions-runner-controller:latest
|
||||||
${{ env.DOCKERHUB_USERNAME }}/actions-runner-controller:${{ env.VERSION }}
|
${{ env.DOCKERHUB_USERNAME }}/actions-runner-controller:${{ env.VERSION }}
|
||||||
${{ env.DOCKERHUB_USERNAME }}/actions-runner-controller:${{ env.VERSION }}-${{ steps.vars.outputs.sha_short }}
|
${{ env.DOCKERHUB_USERNAME }}/actions-runner-controller:${{ env.VERSION }}-${{ steps.vars.outputs.sha_short }}
|
||||||
|
ghcr.io/actions-runner-controller/actions-runner-controller:latest
|
||||||
|
ghcr.io/actions-runner-controller/actions-runner-controller:${{ env.VERSION }}
|
||||||
|
ghcr.io/actions-runner-controller/actions-runner-controller:${{ env.VERSION }}-${{ steps.vars.outputs.sha_short }}
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
59
.github/workflows/publish-canary.yaml
vendored
Normal file
59
.github/workflows/publish-canary.yaml
vendored
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
name: Publish Canary Image
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
paths-ignore:
|
||||||
|
- '**.md'
|
||||||
|
- '.github/ISSUE_TEMPLATE/**'
|
||||||
|
- '.github/workflows/validate-chart.yaml'
|
||||||
|
- '.github/workflows/publish-chart.yaml'
|
||||||
|
- '.github/workflows/publish-arc.yaml'
|
||||||
|
- '.github/workflows/runners.yaml'
|
||||||
|
- '.github/workflows/validate-entrypoint.yaml'
|
||||||
|
- '.github/renovate.*'
|
||||||
|
- 'runner/**'
|
||||||
|
- '.gitignore'
|
||||||
|
- 'PROJECT'
|
||||||
|
- 'LICENSE'
|
||||||
|
- 'Makefile'
|
||||||
|
|
||||||
|
# https://docs.github.com/en/rest/overview/permissions-required-for-github-apps
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
canary-build:
|
||||||
|
name: Build and Publish Canary Image
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
DOCKERHUB_USERNAME: ${{ secrets.DOCKER_USER }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Setup Docker Environment
|
||||||
|
id: vars
|
||||||
|
uses: ./.github/actions/setup-docker-environment
|
||||||
|
with:
|
||||||
|
username: ${{ env.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
|
||||||
|
ghcr_username: ${{ github.actor }}
|
||||||
|
ghcr_password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
# Considered unstable builds
|
||||||
|
# See Issue #285, PR #286, and PR #323 for more information
|
||||||
|
- name: Build and Push
|
||||||
|
uses: docker/build-push-action@v3
|
||||||
|
with:
|
||||||
|
file: Dockerfile
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
build-args: VERSION=canary-${{ github.sha }}
|
||||||
|
push: true
|
||||||
|
tags: |
|
||||||
|
${{ env.DOCKERHUB_USERNAME }}/actions-runner-controller:canary
|
||||||
|
ghcr.io/${{ github.repository }}:canary
|
||||||
|
cache-from: type=gha,scope=arc-canary
|
||||||
|
cache-to: type=gha,mode=max,scope=arc-canary
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
name: Publish helm chart
|
name: Publish Helm Chart
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -6,7 +6,7 @@ on:
|
|||||||
- master
|
- master
|
||||||
paths:
|
paths:
|
||||||
- 'charts/**'
|
- 'charts/**'
|
||||||
- '.github/workflows/on-push-master-publish-chart.yml'
|
- '.github/workflows/publish-chart.yaml'
|
||||||
- '!charts/actions-runner-controller/docs/**'
|
- '!charts/actions-runner-controller/docs/**'
|
||||||
- '!**.md'
|
- '!**.md'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
@@ -15,10 +15,13 @@ env:
|
|||||||
KUBE_SCORE_VERSION: 1.10.0
|
KUBE_SCORE_VERSION: 1.10.0
|
||||||
HELM_VERSION: v3.8.0
|
HELM_VERSION: v3.8.0
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
lint-chart:
|
lint-chart:
|
||||||
runs-on: ubuntu-latest
|
|
||||||
name: Lint Chart
|
name: Lint Chart
|
||||||
|
runs-on: ubuntu-latest
|
||||||
outputs:
|
outputs:
|
||||||
publish-chart: ${{ steps.publish-chart-step.outputs.publish }}
|
publish-chart: ${{ steps.publish-chart-step.outputs.publish }}
|
||||||
steps:
|
steps:
|
||||||
@@ -28,7 +31,7 @@ jobs:
|
|||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Set up Helm
|
- name: Set up Helm
|
||||||
uses: azure/setup-helm@v2.1
|
uses: azure/setup-helm@v3.3
|
||||||
with:
|
with:
|
||||||
version: ${{ env.HELM_VERSION }}
|
version: ${{ env.HELM_VERSION }}
|
||||||
|
|
||||||
@@ -49,12 +52,12 @@ jobs:
|
|||||||
--enable-optional-test container-security-context-readonlyrootfilesystem
|
--enable-optional-test container-security-context-readonlyrootfilesystem
|
||||||
|
|
||||||
# python is a requirement for the chart-testing action below (supports yamllint among other tests)
|
# python is a requirement for the chart-testing action below (supports yamllint among other tests)
|
||||||
- uses: actions/setup-python@v3
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: 3.7
|
python-version: '3.7'
|
||||||
|
|
||||||
- name: Set up chart-testing
|
- name: Set up chart-testing
|
||||||
uses: helm/chart-testing-action@v2.2.1
|
uses: helm/chart-testing-action@v2.3.1
|
||||||
|
|
||||||
- name: Run chart-testing (list-changed)
|
- name: Run chart-testing (list-changed)
|
||||||
id: list-changed
|
id: list-changed
|
||||||
@@ -65,22 +68,23 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Run chart-testing (lint)
|
- name: Run chart-testing (lint)
|
||||||
run: ct lint --config charts/.ci/ct-config.yaml
|
run: |
|
||||||
|
ct lint --config charts/.ci/ct-config.yaml
|
||||||
|
|
||||||
- name: Create kind cluster
|
- name: Create kind cluster
|
||||||
uses: helm/kind-action@v1.2.0
|
|
||||||
if: steps.list-changed.outputs.changed == 'true'
|
if: steps.list-changed.outputs.changed == 'true'
|
||||||
|
uses: helm/kind-action@v1.4.0
|
||||||
|
|
||||||
# We need cert-manager already installed in the cluster because we assume the CRDs exist
|
# We need cert-manager already installed in the cluster because we assume the CRDs exist
|
||||||
- name: Install cert-manager
|
- name: Install cert-manager
|
||||||
|
if: steps.list-changed.outputs.changed == 'true'
|
||||||
run: |
|
run: |
|
||||||
helm repo add jetstack https://charts.jetstack.io --force-update
|
helm repo add jetstack https://charts.jetstack.io --force-update
|
||||||
helm install cert-manager jetstack/cert-manager --set installCRDs=true --wait
|
helm install cert-manager jetstack/cert-manager --set installCRDs=true --wait
|
||||||
if: steps.list-changed.outputs.changed == 'true'
|
|
||||||
|
|
||||||
- name: Run chart-testing (install)
|
- name: Run chart-testing (install)
|
||||||
run: ct install --config charts/.ci/ct-config.yaml
|
|
||||||
if: steps.list-changed.outputs.changed == 'true'
|
if: steps.list-changed.outputs.changed == 'true'
|
||||||
|
run: ct install --config charts/.ci/ct-config.yaml
|
||||||
|
|
||||||
# WARNING: This relies on the latest release being inat the top of the JSON from GitHub and a clean chart.yaml
|
# WARNING: This relies on the latest release being inat the top of the JSON from GitHub and a clean chart.yaml
|
||||||
- name: Check if Chart Publish is Needed
|
- name: Check if Chart Publish is Needed
|
||||||
@@ -99,8 +103,11 @@ jobs:
|
|||||||
publish-chart:
|
publish-chart:
|
||||||
if: needs.lint-chart.outputs.publish-chart == 'true'
|
if: needs.lint-chart.outputs.publish-chart == 'true'
|
||||||
needs: lint-chart
|
needs: lint-chart
|
||||||
runs-on: ubuntu-latest
|
|
||||||
name: Publish Chart
|
name: Publish Chart
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write # for helm/chart-releaser-action to push chart release and create a release
|
||||||
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
@@ -114,7 +121,7 @@ jobs:
|
|||||||
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
|
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
|
||||||
|
|
||||||
- name: Run chart-releaser
|
- name: Run chart-releaser
|
||||||
uses: helm/chart-releaser-action@v1.4.0
|
uses: helm/chart-releaser-action@v1.4.1
|
||||||
env:
|
env:
|
||||||
CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||||
|
|
||||||
32
.github/workflows/run-codeql.yaml
vendored
Normal file
32
.github/workflows/run-codeql.yaml
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
name: Run CodeQL
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
schedule:
|
||||||
|
- cron: '30 1 * * 0'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
analyze:
|
||||||
|
name: Analyze
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
security-events: write
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Initialize CodeQL
|
||||||
|
uses: github/codeql-action/init@v2
|
||||||
|
with:
|
||||||
|
languages: go
|
||||||
|
|
||||||
|
- name: Autobuild
|
||||||
|
uses: github/codeql-action/autobuild@v2
|
||||||
|
|
||||||
|
- name: Perform CodeQL Analysis
|
||||||
|
uses: github/codeql-action/analyze@v2
|
||||||
29
.github/workflows/run-first-interaction.yaml
vendored
Normal file
29
.github/workflows/run-first-interaction.yaml
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
name: first-interaction
|
||||||
|
|
||||||
|
on:
|
||||||
|
issues:
|
||||||
|
types: [opened]
|
||||||
|
pull_request:
|
||||||
|
branches: [master]
|
||||||
|
types: [opened]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check_for_first_interaction:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: actions/first-interaction@main
|
||||||
|
with:
|
||||||
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
issue-message: |
|
||||||
|
Hello! Thank you for filing an issue.
|
||||||
|
|
||||||
|
The maintainers will triage your issue shortly.
|
||||||
|
|
||||||
|
In the meantime, please take a look at the [troubleshooting guide](https://github.com/actions-runner-controller/actions-runner-controller/blob/master/TROUBLESHOOTING.md) for bug reports.
|
||||||
|
|
||||||
|
If this is a feature request, please review our [contribution guidelines](https://github.com/actions-runner-controller/actions-runner-controller/blob/master/CONTRIBUTING.md).
|
||||||
|
pr-message: |
|
||||||
|
Hello! Thank you for your contribution.
|
||||||
|
|
||||||
|
Please review our [contribution guidelines](https://github.com/actions-runner-controller/actions-runner-controller/blob/master/CONTRIBUTING.md) to understand the project's testing and code conventions.
|
||||||
@@ -1,14 +1,20 @@
|
|||||||
name: 'Close stale issues and PRs'
|
name: Run Stale Bot
|
||||||
on:
|
on:
|
||||||
schedule:
|
schedule:
|
||||||
# 01:30 every day
|
|
||||||
- cron: '30 1 * * *'
|
- cron: '30 1 * * *'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
stale:
|
stale:
|
||||||
|
name: Run Stale
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
issues: write # for actions/stale to close stale issues
|
||||||
|
pull-requests: write # for actions/stale to close stale PRs
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@v5
|
- uses: actions/stale@v6
|
||||||
with:
|
with:
|
||||||
stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.'
|
stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.'
|
||||||
# turn off stale for both issues and PRs
|
# turn off stale for both issues and PRs
|
||||||
@@ -2,31 +2,41 @@ name: Runners
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
types:
|
types:
|
||||||
- opened
|
- opened
|
||||||
- synchronize
|
- synchronize
|
||||||
- reopened
|
- reopened
|
||||||
- closed
|
|
||||||
branches:
|
branches:
|
||||||
- 'master'
|
- 'master'
|
||||||
paths:
|
paths:
|
||||||
- 'runner/**'
|
- 'runner/**'
|
||||||
- '!runner/Makefile'
|
- '!runner/Makefile'
|
||||||
- .github/workflows/runners.yml
|
- '.github/workflows/runners.yaml'
|
||||||
|
- '!**.md'
|
||||||
|
# We must do a trigger on a push: instead of a types: closed so GitHub Secrets
|
||||||
|
# are available to the workflow run
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- 'master'
|
||||||
|
paths:
|
||||||
|
- 'runner/**'
|
||||||
|
- '!runner/Makefile'
|
||||||
|
- '.github/workflows/runners.yaml'
|
||||||
- '!**.md'
|
- '!**.md'
|
||||||
|
|
||||||
env:
|
env:
|
||||||
RUNNER_VERSION: 2.290.1
|
RUNNER_VERSION: 2.298.2
|
||||||
DOCKER_VERSION: 20.10.12
|
DOCKER_VERSION: 20.10.12
|
||||||
|
RUNNER_CONTAINER_HOOKS_VERSION: 0.1.2
|
||||||
DOCKERHUB_USERNAME: summerwind
|
DOCKERHUB_USERNAME: summerwind
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build-runners:
|
||||||
|
name: Build ${{ matrix.name }}-${{ matrix.os-name }}-${{ matrix.os-version }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
packages: write
|
packages: write
|
||||||
contents: read
|
contents: read
|
||||||
name: Build ${{ matrix.name }}-${{ matrix.os-name }}-${{ matrix.os-version }}
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@@ -34,11 +44,12 @@ jobs:
|
|||||||
- name: actions-runner
|
- name: actions-runner
|
||||||
os-name: ubuntu
|
os-name: ubuntu
|
||||||
os-version: 20.04
|
os-version: 20.04
|
||||||
dockerfile: Dockerfile
|
|
||||||
- name: actions-runner-dind
|
- name: actions-runner-dind
|
||||||
os-name: ubuntu
|
os-name: ubuntu
|
||||||
os-version: 20.04
|
os-version: 20.04
|
||||||
dockerfile: Dockerfile.dindrunner
|
- name: actions-runner-dind-rootless
|
||||||
|
os-name: ubuntu
|
||||||
|
os-version: 20.04
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
@@ -47,22 +58,23 @@ jobs:
|
|||||||
- name: Setup Docker Environment
|
- name: Setup Docker Environment
|
||||||
id: vars
|
id: vars
|
||||||
uses: ./.github/actions/setup-docker-environment
|
uses: ./.github/actions/setup-docker-environment
|
||||||
with:
|
with:
|
||||||
username: ${{ env.DOCKERHUB_USERNAME }}
|
username: ${{ env.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
|
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
|
||||||
ghcr_username: ${{ github.actor }}
|
ghcr_username: ${{ github.actor }}
|
||||||
ghcr_password: ${{ secrets.GITHUB_TOKEN }}
|
ghcr_password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Build and Push Versioned Tags
|
- name: Build and Push Versioned Tags
|
||||||
uses: docker/build-push-action@v2
|
uses: docker/build-push-action@v3
|
||||||
with:
|
with:
|
||||||
context: ./runner
|
context: ./runner
|
||||||
file: ./runner/${{ matrix.dockerfile }}
|
file: ./runner/${{ matrix.name }}.dockerfile
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
push: ${{ github.ref == 'master' && github.event.pull_request.merged == true }}
|
push: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
|
||||||
build-args: |
|
build-args: |
|
||||||
RUNNER_VERSION=${{ env.RUNNER_VERSION }}
|
RUNNER_VERSION=${{ env.RUNNER_VERSION }}
|
||||||
DOCKER_VERSION=${{ env.DOCKER_VERSION }}
|
DOCKER_VERSION=${{ env.DOCKER_VERSION }}
|
||||||
|
RUNNER_CONTAINER_HOOKS_VERSION=${{ env.RUNNER_CONTAINER_HOOKS_VERSION }}
|
||||||
tags: |
|
tags: |
|
||||||
${{ env.DOCKERHUB_USERNAME }}/${{ matrix.name }}:v${{ env.RUNNER_VERSION }}-${{ matrix.os-name }}-${{ matrix.os-version }}
|
${{ env.DOCKERHUB_USERNAME }}/${{ matrix.name }}:v${{ env.RUNNER_VERSION }}-${{ matrix.os-name }}-${{ matrix.os-version }}
|
||||||
${{ env.DOCKERHUB_USERNAME }}/${{ matrix.name }}:v${{ env.RUNNER_VERSION }}-${{ matrix.os-name }}-${{ matrix.os-version }}-${{ steps.vars.outputs.sha_short }}
|
${{ env.DOCKERHUB_USERNAME }}/${{ matrix.name }}:v${{ env.RUNNER_VERSION }}-${{ matrix.os-name }}-${{ matrix.os-version }}-${{ steps.vars.outputs.sha_short }}
|
||||||
@@ -70,5 +82,5 @@ jobs:
|
|||||||
ghcr.io/${{ github.repository }}/${{ matrix.name }}:latest
|
ghcr.io/${{ github.repository }}/${{ matrix.name }}:latest
|
||||||
ghcr.io/${{ github.repository }}/${{ matrix.name }}:v${{ env.RUNNER_VERSION }}-${{ matrix.os-name }}-${{ matrix.os-version }}
|
ghcr.io/${{ github.repository }}/${{ matrix.name }}:v${{ env.RUNNER_VERSION }}-${{ matrix.os-name }}-${{ matrix.os-version }}
|
||||||
ghcr.io/${{ github.repository }}/${{ matrix.name }}:v${{ env.RUNNER_VERSION }}-${{ matrix.os-name }}-${{ matrix.os-version }}-${{ steps.vars.outputs.sha_short }}
|
ghcr.io/${{ github.repository }}/${{ matrix.name }}:v${{ env.RUNNER_VERSION }}-${{ matrix.os-name }}-${{ matrix.os-version }}-${{ steps.vars.outputs.sha_short }}
|
||||||
cache-from: type=gha
|
cache-from: type=gha,scope=build-${{ matrix.name }}
|
||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max,scope=build-${{ matrix.name }}
|
||||||
21
.github/workflows/test-entrypoint.yaml
vendored
21
.github/workflows/test-entrypoint.yaml
vendored
@@ -1,21 +0,0 @@
|
|||||||
name: Unit tests for entrypoint
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- '**'
|
|
||||||
paths:
|
|
||||||
- 'runner/**'
|
|
||||||
- 'test/entrypoint/**'
|
|
||||||
- '!**.md'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
name: Test entrypoint
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
- name: Run unit tests for entrypoint.sh
|
|
||||||
run: |
|
|
||||||
make acceptance/runner/entrypoint
|
|
||||||
@@ -1,45 +1,59 @@
|
|||||||
name: CI
|
name: Validate ARC
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- .github/workflows/runners.yml
|
|
||||||
- .github/workflows/on-push-lint-charts.yml
|
|
||||||
- .github/workflows/on-push-master-publish-chart.yml
|
|
||||||
- .github/workflows/release.yml
|
|
||||||
- .github/workflows/test-entrypoint.yml
|
|
||||||
- .github/workflows/wip.yml
|
|
||||||
- 'runner/**'
|
|
||||||
- '**.md'
|
- '**.md'
|
||||||
|
- '.github/ISSUE_TEMPLATE/**'
|
||||||
|
- '.github/workflows/publish-canary.yaml'
|
||||||
|
- '.github/workflows/validate-chart.yaml'
|
||||||
|
- '.github/workflows/publish-chart.yaml'
|
||||||
|
- '.github/workflows/runners.yaml'
|
||||||
|
- '.github/workflows/publish-arc.yaml'
|
||||||
|
- '.github/workflows/validate-entrypoint.yaml'
|
||||||
|
- '.github/renovate.*'
|
||||||
|
- 'runner/**'
|
||||||
- '.gitignore'
|
- '.gitignore'
|
||||||
|
- 'PROJECT'
|
||||||
|
- 'LICENSE'
|
||||||
|
- 'Makefile'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test-controller:
|
||||||
|
name: Test ARC
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: Test
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
- uses: actions/setup-go@v3
|
|
||||||
|
- name: Set-up Go
|
||||||
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: '1.17.7'
|
go-version: '1.18.2'
|
||||||
check-latest: false
|
check-latest: false
|
||||||
- run: go version
|
|
||||||
- uses: actions/cache@v3
|
- uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
path: ~/go/pkg/mod
|
path: ~/go/pkg/mod
|
||||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-go-
|
${{ runner.os }}-go-
|
||||||
|
|
||||||
- name: Install kubebuilder
|
- name: Install kubebuilder
|
||||||
run: |
|
run: |
|
||||||
curl -L -O https://github.com/kubernetes-sigs/kubebuilder/releases/download/v2.3.2/kubebuilder_2.3.2_linux_amd64.tar.gz
|
curl -L -O https://github.com/kubernetes-sigs/kubebuilder/releases/download/v2.3.2/kubebuilder_2.3.2_linux_amd64.tar.gz
|
||||||
tar zxvf kubebuilder_2.3.2_linux_amd64.tar.gz
|
tar zxvf kubebuilder_2.3.2_linux_amd64.tar.gz
|
||||||
sudo mv kubebuilder_2.3.2_linux_amd64 /usr/local/kubebuilder
|
sudo mv kubebuilder_2.3.2_linux_amd64 /usr/local/kubebuilder
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: make test
|
run: |
|
||||||
|
make test
|
||||||
|
|
||||||
- name: Verify manifests are up-to-date
|
- name: Verify manifests are up-to-date
|
||||||
run: |
|
run: |
|
||||||
make manifests
|
make manifests
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
name: Lint and Test Charts
|
name: Validate Helm Chart
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
paths:
|
paths:
|
||||||
- 'charts/**'
|
- 'charts/**'
|
||||||
- '.github/workflows/on-push-lint-charts.yml'
|
- '.github/workflows/validate-chart.yaml'
|
||||||
- '!charts/actions-runner-controller/docs/**'
|
- '!charts/actions-runner-controller/docs/**'
|
||||||
- '!**.md'
|
- '!**.md'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
@@ -12,10 +12,13 @@ env:
|
|||||||
KUBE_SCORE_VERSION: 1.10.0
|
KUBE_SCORE_VERSION: 1.10.0
|
||||||
HELM_VERSION: v3.8.0
|
HELM_VERSION: v3.8.0
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
lint-test:
|
validate-chart:
|
||||||
runs-on: ubuntu-latest
|
|
||||||
name: Lint Chart
|
name: Lint Chart
|
||||||
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
@@ -23,7 +26,7 @@ jobs:
|
|||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Set up Helm
|
- name: Set up Helm
|
||||||
uses: azure/setup-helm@v2.1
|
uses: azure/setup-helm@v3.3
|
||||||
with:
|
with:
|
||||||
version: ${{ env.HELM_VERSION }}
|
version: ${{ env.HELM_VERSION }}
|
||||||
|
|
||||||
@@ -44,12 +47,12 @@ jobs:
|
|||||||
--enable-optional-test container-security-context-readonlyrootfilesystem
|
--enable-optional-test container-security-context-readonlyrootfilesystem
|
||||||
|
|
||||||
# python is a requirement for the chart-testing action below (supports yamllint among other tests)
|
# python is a requirement for the chart-testing action below (supports yamllint among other tests)
|
||||||
- uses: actions/setup-python@v3
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: 3.7
|
python-version: '3.7'
|
||||||
|
|
||||||
- name: Set up chart-testing
|
- name: Set up chart-testing
|
||||||
uses: helm/chart-testing-action@v2.2.1
|
uses: helm/chart-testing-action@v2.3.1
|
||||||
|
|
||||||
- name: Run chart-testing (list-changed)
|
- name: Run chart-testing (list-changed)
|
||||||
id: list-changed
|
id: list-changed
|
||||||
@@ -60,18 +63,20 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Run chart-testing (lint)
|
- name: Run chart-testing (lint)
|
||||||
run: ct lint --config charts/.ci/ct-config.yaml
|
run: |
|
||||||
|
ct lint --config charts/.ci/ct-config.yaml
|
||||||
|
|
||||||
- name: Create kind cluster
|
- name: Create kind cluster
|
||||||
uses: helm/kind-action@v1.2.0
|
uses: helm/kind-action@v1.4.0
|
||||||
if: steps.list-changed.outputs.changed == 'true'
|
if: steps.list-changed.outputs.changed == 'true'
|
||||||
|
|
||||||
# We need cert-manager already installed in the cluster because we assume the CRDs exist
|
# We need cert-manager already installed in the cluster because we assume the CRDs exist
|
||||||
- name: Install cert-manager
|
- name: Install cert-manager
|
||||||
|
if: steps.list-changed.outputs.changed == 'true'
|
||||||
run: |
|
run: |
|
||||||
helm repo add jetstack https://charts.jetstack.io --force-update
|
helm repo add jetstack https://charts.jetstack.io --force-update
|
||||||
helm install cert-manager jetstack/cert-manager --set installCRDs=true --wait
|
helm install cert-manager jetstack/cert-manager --set installCRDs=true --wait
|
||||||
if: steps.list-changed.outputs.changed == 'true'
|
|
||||||
|
|
||||||
- name: Run chart-testing (install)
|
- name: Run chart-testing (install)
|
||||||
run: ct install --config charts/.ci/ct-config.yaml
|
run: |
|
||||||
|
ct install --config charts/.ci/ct-config.yaml
|
||||||
45
.github/workflows/validate-runners.yaml
vendored
Normal file
45
.github/workflows/validate-runners.yaml
vendored
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
name: Validate Runners
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- '**'
|
||||||
|
paths:
|
||||||
|
- 'runner/**'
|
||||||
|
- 'test/entrypoint/**'
|
||||||
|
- '!**.md'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
shellcheck:
|
||||||
|
name: runner / shellcheck
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
- name: shellcheck
|
||||||
|
uses: reviewdog/action-shellcheck@v1
|
||||||
|
with:
|
||||||
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
path: "./runner"
|
||||||
|
pattern: |
|
||||||
|
*.sh
|
||||||
|
*.bash
|
||||||
|
update-status
|
||||||
|
# Make this consistent with `make shellsheck`
|
||||||
|
shellcheck_flags: "--shell bash --source-path runner"
|
||||||
|
exclude: "./.git/*"
|
||||||
|
check_all_files_with_shebangs: "false"
|
||||||
|
# Set this to "true" once we addressed all the shellcheck findings
|
||||||
|
fail_on_error: "false"
|
||||||
|
test-runner-entrypoint:
|
||||||
|
name: Test entrypoint
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: |
|
||||||
|
make acceptance/runner/entrypoint
|
||||||
51
.github/workflows/wip.yml
vendored
51
.github/workflows/wip.yml
vendored
@@ -1,51 +0,0 @@
|
|||||||
name: Publish Canary Image
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
paths-ignore:
|
|
||||||
- .github/workflows/runners.yml
|
|
||||||
- .github/workflows/on-push-lint-charts.yml
|
|
||||||
- .github/workflows/on-push-master-publish-chart.yml
|
|
||||||
- .github/workflows/release.yml
|
|
||||||
- .github/workflows/test-entrypoint.yml
|
|
||||||
- "runner/**"
|
|
||||||
- "**.md"
|
|
||||||
- ".gitignore"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
name: Build and Publish Canary Image
|
|
||||||
env:
|
|
||||||
DOCKERHUB_USERNAME: ${{ secrets.DOCKER_USER }}
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v1
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
id: buildx
|
|
||||||
uses: docker/setup-buildx-action@v1
|
|
||||||
with:
|
|
||||||
version: latest
|
|
||||||
|
|
||||||
- name: Login to DockerHub
|
|
||||||
uses: docker/login-action@v1
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKER_USER }}
|
|
||||||
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
|
|
||||||
|
|
||||||
# Considered unstable builds
|
|
||||||
# See Issue #285, PR #286, and PR #323 for more information
|
|
||||||
- name: Build and Push
|
|
||||||
uses: docker/build-push-action@v2
|
|
||||||
with:
|
|
||||||
file: Dockerfile
|
|
||||||
platforms: linux/amd64,linux/arm64
|
|
||||||
push: true
|
|
||||||
tags: |
|
|
||||||
${{ env.DOCKERHUB_USERNAME }}/actions-runner-controller:canary
|
|
||||||
17
.golangci.yaml
Normal file
17
.golangci.yaml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
run:
|
||||||
|
timeout: 3m
|
||||||
|
output:
|
||||||
|
format: github-actions
|
||||||
|
linters-settings:
|
||||||
|
errcheck:
|
||||||
|
exclude-functions:
|
||||||
|
- (net/http.ResponseWriter).Write
|
||||||
|
- (*net/http.Server).Shutdown
|
||||||
|
- (*github.com/actions-runner-controller/actions-runner-controller/simulator.VisibleRunnerGroups).Add
|
||||||
|
- (*github.com/actions-runner-controller/actions-runner-controller/testing.Kind).Stop
|
||||||
|
issues:
|
||||||
|
exclude-rules:
|
||||||
|
- path: controllers/suite_test.go
|
||||||
|
linters:
|
||||||
|
- staticcheck
|
||||||
|
text: "SA1019"
|
||||||
212
CONTRIBUTING.md
212
CONTRIBUTING.md
@@ -1,85 +1,53 @@
|
|||||||
## Contributing
|
# Contribution Guide
|
||||||
|
|
||||||
### Testing Controller Built from a Pull Request
|
- [Contribution Guide](#contribution-guide)
|
||||||
|
- [Welcome](#welcome)
|
||||||
|
- [Before contributing code](#before-contributing-code)
|
||||||
|
- [How to Contribute a Patch](#how-to-contribute-a-patch)
|
||||||
|
- [Developing the Controller](#developing-the-controller)
|
||||||
|
- [Developing the Runners](#developing-the-runners)
|
||||||
|
- [Tests](#tests)
|
||||||
|
- [Running Ginkgo Tests](#running-ginkgo-tests)
|
||||||
|
- [Running End to End Tests](#running-end-to-end-tests)
|
||||||
|
- [Rerunning a failed test](#rerunning-a-failed-test)
|
||||||
|
- [Testing in a non-kind cluster](#testing-in-a-non-kind-cluster)
|
||||||
|
- [Code conventions](#code-conventions)
|
||||||
|
- [Opening the Pull Request](#opening-the-pull-request)
|
||||||
|
- [Helm Version Changes](#helm-version-changes)
|
||||||
|
- [Testing Controller Built from a Pull Request](#testing-controller-built-from-a-pull-request)
|
||||||
|
|
||||||
We always appreciate your help in testing open pull requests by deploying custom builds of actions-runner-controller onto your own environment, so that we are extra sure we didn't break anything.
|
## Welcome
|
||||||
|
|
||||||
It is especially true when the pull request is about GitHub Enterprise, both GHEC and GHES, as [maintainers don't have GitHub Enterprise environments for testing](/README.md#github-enterprise-support).
|
This document is the single source of truth for how to contribute to the code base.
|
||||||
|
Feel free to browse the [open issues](https://github.com/actions-runner-controller/actions-runner-controller/issues) or file a new one, all feedback is welcome!
|
||||||
|
By reading this guide, we hope to give you all of the information you need to be able to pick up issues, contribute new features, and get your work
|
||||||
|
reviewed and merged.
|
||||||
|
|
||||||
The process would look like the below:
|
## Before contributing code
|
||||||
|
|
||||||
- Clone this repository locally
|
We welcome code patches, but to make sure things are well coordinated you should discuss any significant change before starting the work.
|
||||||
- Checkout the branch. If you use the `gh` command, run `gh pr checkout $PR_NUMBER`
|
The maintainers ask that you signal your intention to contribute to the project using the issue tracker.
|
||||||
- Run `NAME=$DOCKER_USER/actions-runner-controller VERSION=canary make docker-build docker-push` for a custom container image build
|
If there is an existing issue that you want to work on, please let us know so we can get it assigned to you.
|
||||||
- Update your actions-runner-controller's controller-manager deployment to use the new image, `$DOCKER_USER/actions-runner-controller:canary`
|
If you noticed a bug or want to add a new feature, there are issue templates you can fill out.
|
||||||
|
|
||||||
Please also note that you need to replace `$DOCKER_USER` with your own DockerHub account name.
|
When filing a feature request, the maintainers will review the change and give you a decision on whether we are willing to accept the feature into the project.
|
||||||
|
For significantly large and/or complex features, we may request that you write up an architectural decision record ([ADR](https://github.blog/2020-08-13-why-write-adrs/)) detailing the change.
|
||||||
|
Please use the [template](/adrs/0000-TEMPLATE.md) as guidance.
|
||||||
|
|
||||||
### How to Contribute a Patch
|
<!--
|
||||||
|
TODO: Add a pre-requisite section describing what developers should
|
||||||
|
install in order get started on ARC.
|
||||||
|
-->
|
||||||
|
|
||||||
Depending on what you are patching depends on how you should go about it. Below are some guides on how to test patches locally as well as develop the controller and runners.
|
## How to Contribute a Patch
|
||||||
|
|
||||||
When submitting a PR for a change please provide evidence that your change works as we still need to work on improving the CI of the project. Some resources are provided for helping achieve this, see this guide for details.
|
Depending on what you are patching depends on how you should go about it.
|
||||||
|
Below are some guides on how to test patches locally as well as develop the controller and runners.
|
||||||
|
|
||||||
#### Running an End to End Test
|
When submitting a PR for a change please provide evidence that your change works as we still need to work on improving the CI of the project.
|
||||||
|
Some resources are provided for helping achieve this, see this guide for details.
|
||||||
|
|
||||||
> **Notes for Ubuntu 20.04+ users**
|
### Developing the Controller
|
||||||
>
|
|
||||||
> If you're using Ubuntu 20.04 or greater, you might have installed `docker` with `snap`.
|
|
||||||
>
|
|
||||||
> If you want to stick with `snap`-provided `docker`, do not forget to set `TMPDIR` to
|
|
||||||
> somewhere under `$HOME`.
|
|
||||||
> Otherwise `kind load docker-image` fail while running `docker save`.
|
|
||||||
> See https://kind.sigs.k8s.io/docs/user/known-issues/#docker-installed-with-snap for more information.
|
|
||||||
|
|
||||||
To test your local changes against both PAT and App based authentication please run the `acceptance` make target with the authentication configuration details provided:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
# This sets `VERSION` envvar to some appropriate value
|
|
||||||
. hack/make-env.sh
|
|
||||||
|
|
||||||
DOCKER_USER=*** \
|
|
||||||
GITHUB_TOKEN=*** \
|
|
||||||
APP_ID=*** \
|
|
||||||
PRIVATE_KEY_FILE_PATH=path/to/pem/file \
|
|
||||||
INSTALLATION_ID=*** \
|
|
||||||
make acceptance
|
|
||||||
```
|
|
||||||
|
|
||||||
**Rerunning a failed test**
|
|
||||||
|
|
||||||
When one of tests run by `make acceptance` failed, you'd probably like to rerun only the failed one.
|
|
||||||
|
|
||||||
It can be done by `make acceptance/run` and by setting the combination of `ACCEPTANCE_TEST_DEPLOYMENT_TOOL=helm|kubectl` and `ACCEPTANCE_TEST_SECRET_TYPE=token|app` values that failed (note, you just need to set the corresponding authentication configuration in this circumstance)
|
|
||||||
|
|
||||||
In the example below, we rerun the test for the combination `ACCEPTANCE_TEST_DEPLOYMENT_TOOL=helm ACCEPTANCE_TEST_SECRET_TYPE=token` only:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
DOCKER_USER=*** \
|
|
||||||
GITHUB_TOKEN=*** \
|
|
||||||
ACCEPTANCE_TEST_DEPLOYMENT_TOOL=helm
|
|
||||||
ACCEPTANCE_TEST_SECRET_TYPE=token \
|
|
||||||
make acceptance/run
|
|
||||||
```
|
|
||||||
|
|
||||||
**Testing in a non-kind cluster**
|
|
||||||
|
|
||||||
If you prefer to test in a non-kind cluster, you can instead run:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
KUBECONFIG=path/to/kubeconfig \
|
|
||||||
DOCKER_USER=*** \
|
|
||||||
GITHUB_TOKEN=*** \
|
|
||||||
APP_ID=*** \
|
|
||||||
PRIVATE_KEY_FILE_PATH=path/to/pem/file \
|
|
||||||
INSTALLATION_ID=*** \
|
|
||||||
ACCEPTANCE_TEST_SECRET_TYPE=token \
|
|
||||||
make docker-build acceptance/setup \
|
|
||||||
acceptance/deploy \
|
|
||||||
acceptance/tests
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Developing the Controller
|
|
||||||
|
|
||||||
Rerunning the whole acceptance test suite from scratch on every little change to the controller, the runner, and the chart would be counter-productive.
|
Rerunning the whole acceptance test suite from scratch on every little change to the controller, the runner, and the chart would be counter-productive.
|
||||||
|
|
||||||
@@ -119,13 +87,14 @@ NAME=$DOCKER_USER/actions-runner make \
|
|||||||
(kubectl get po -ojsonpath={.items[*].metadata.name} | xargs -n1 kubectl delete po)
|
(kubectl get po -ojsonpath={.items[*].metadata.name} | xargs -n1 kubectl delete po)
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Developing the Runners
|
### Developing the Runners
|
||||||
|
|
||||||
**Tests**
|
#### Tests
|
||||||
|
|
||||||
A set of example pipelines (./acceptance/pipelines) are provided in this repository which you can use to validate your runners are working as expected. When raising a PR please run the relevant suites to prove your change hasn't broken anything.
|
A set of example pipelines (./acceptance/pipelines) are provided in this repository which you can use to validate your runners are working as expected.
|
||||||
|
When raising a PR please run the relevant suites to prove your change hasn't broken anything.
|
||||||
|
|
||||||
**Running Ginkgo Tests**
|
#### Running Ginkgo Tests
|
||||||
|
|
||||||
You can run the integration test suite that is written in Ginkgo with:
|
You can run the integration test suite that is written in Ginkgo with:
|
||||||
|
|
||||||
@@ -135,7 +104,8 @@ make test-with-deps
|
|||||||
|
|
||||||
This will firstly install a few binaries required to setup the integration test environment and then runs `go test` to start the Ginkgo test.
|
This will firstly install a few binaries required to setup the integration test environment and then runs `go test` to start the Ginkgo test.
|
||||||
|
|
||||||
If you don't want to use `make`, like when you're running tests from your IDE, install required binaries to `/usr/local/kubebuilder/bin`. That's the directory in which controller-runtime's `envtest` framework locates the binaries.
|
If you don't want to use `make`, like when you're running tests from your IDE, install required binaries to `/usr/local/kubebuilder/bin`.
|
||||||
|
That's the directory in which controller-runtime's `envtest` framework locates the binaries.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
sudo mkdir -p /usr/local/kubebuilder/bin
|
sudo mkdir -p /usr/local/kubebuilder/bin
|
||||||
@@ -152,6 +122,92 @@ GINKGO_FOCUS='[It] should create a new Runner resource from the specified templa
|
|||||||
go test -v -run TestAPIs github.com/actions-runner-controller/actions-runner-controller/controllers
|
go test -v -run TestAPIs github.com/actions-runner-controller/actions-runner-controller/controllers
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Helm Version Bumps
|
### Running End to End Tests
|
||||||
|
|
||||||
In general we ask you not to bump the version in your PR, the maintainers in general manage the publishing of a new chart.
|
> **Notes for Ubuntu 20.04+ users**
|
||||||
|
>
|
||||||
|
> If you're using Ubuntu 20.04 or greater, you might have installed `docker` with `snap`.
|
||||||
|
>
|
||||||
|
> If you want to stick with `snap`-provided `docker`, do not forget to set `TMPDIR` to somewhere under `$HOME`.
|
||||||
|
> Otherwise `kind load docker-image` fail while running `docker save`.
|
||||||
|
> See https://kind.sigs.k8s.io/docs/user/known-issues/#docker-installed-with-snap for more information.
|
||||||
|
|
||||||
|
To test your local changes against both PAT and App based authentication please run the `acceptance` make target with the authentication configuration details provided:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
# This sets `VERSION` envvar to some appropriate value
|
||||||
|
. hack/make-env.sh
|
||||||
|
|
||||||
|
DOCKER_USER=*** \
|
||||||
|
GITHUB_TOKEN=*** \
|
||||||
|
APP_ID=*** \
|
||||||
|
PRIVATE_KEY_FILE_PATH=path/to/pem/file \
|
||||||
|
INSTALLATION_ID=*** \
|
||||||
|
make acceptance
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Rerunning a failed test
|
||||||
|
|
||||||
|
When one of tests run by `make acceptance` failed, you'd probably like to rerun only the failed one.
|
||||||
|
|
||||||
|
It can be done by `make acceptance/run` and by setting the combination of `ACCEPTANCE_TEST_DEPLOYMENT_TOOL=helm|kubectl` and `ACCEPTANCE_TEST_SECRET_TYPE=token|app` values that failed (note, you just need to set the corresponding authentication configuration in this circumstance)
|
||||||
|
|
||||||
|
In the example below, we rerun the test for the combination `ACCEPTANCE_TEST_DEPLOYMENT_TOOL=helm ACCEPTANCE_TEST_SECRET_TYPE=token` only:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
DOCKER_USER=*** \
|
||||||
|
GITHUB_TOKEN=*** \
|
||||||
|
ACCEPTANCE_TEST_DEPLOYMENT_TOOL=helm \
|
||||||
|
ACCEPTANCE_TEST_SECRET_TYPE=token \
|
||||||
|
make acceptance/run
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Testing in a non-kind cluster
|
||||||
|
|
||||||
|
If you prefer to test in a non-kind cluster, you can instead run:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
KUBECONFIG=path/to/kubeconfig \
|
||||||
|
DOCKER_USER=*** \
|
||||||
|
GITHUB_TOKEN=*** \
|
||||||
|
APP_ID=*** \
|
||||||
|
PRIVATE_KEY_FILE_PATH=path/to/pem/file \
|
||||||
|
INSTALLATION_ID=*** \
|
||||||
|
ACCEPTANCE_TEST_SECRET_TYPE=token \
|
||||||
|
make docker-build acceptance/setup \
|
||||||
|
acceptance/deploy \
|
||||||
|
acceptance/tests
|
||||||
|
```
|
||||||
|
|
||||||
|
### Code conventions
|
||||||
|
|
||||||
|
Before shipping your PR, please check the following items to make sure CI passes.
|
||||||
|
|
||||||
|
- Run `go mod tidy` if you made changes to dependencies.
|
||||||
|
- Format the code using `gofmt`
|
||||||
|
- Run the `golangci-lint` tool locally.
|
||||||
|
- We recommend you use `make lint` to run the tool using a Docker container matching the CI version.
|
||||||
|
|
||||||
|
### Opening the Pull Request
|
||||||
|
|
||||||
|
Send PR, add issue number to description
|
||||||
|
|
||||||
|
## Helm Version Changes
|
||||||
|
|
||||||
|
In general we ask you not to bump the version in your PR.
|
||||||
|
The maintainers will manage releases and publishing new charts.
|
||||||
|
|
||||||
|
## Testing Controller Built from a Pull Request
|
||||||
|
|
||||||
|
We always appreciate your help in testing open pull requests by deploying custom builds of actions-runner-controller onto your own environment, so that we are extra sure we didn't break anything.
|
||||||
|
|
||||||
|
It is especially true when the pull request is about GitHub Enterprise, both GHEC and GHES, as [maintainers don't have GitHub Enterprise environments for testing](docs/detailed-docs.md#github-enterprise-support).
|
||||||
|
|
||||||
|
The process would look like the below:
|
||||||
|
|
||||||
|
- Clone this repository locally
|
||||||
|
- Checkout the branch. If you use the `gh` command, run `gh pr checkout $PR_NUMBER`
|
||||||
|
- Run `NAME=$DOCKER_USER/actions-runner-controller VERSION=canary make docker-build docker-push` for a custom container image build
|
||||||
|
- Update your actions-runner-controller's controller-manager deployment to use the new image, `$DOCKER_USER/actions-runner-controller:canary`
|
||||||
|
|
||||||
|
Please also note that you need to replace `$DOCKER_USER` with your own DockerHub account name.
|
||||||
13
Dockerfile
13
Dockerfile
@@ -1,11 +1,10 @@
|
|||||||
# Build the manager binary
|
# Build the manager binary
|
||||||
FROM --platform=$BUILDPLATFORM golang:1.17 as builder
|
FROM --platform=$BUILDPLATFORM golang:1.19.2 as builder
|
||||||
|
|
||||||
WORKDIR /workspace
|
WORKDIR /workspace
|
||||||
|
|
||||||
# Make it runnable on a distroless image/without libc
|
# Make it runnable on a distroless image/without libc
|
||||||
ENV CGO_ENABLED=0
|
ENV CGO_ENABLED=0
|
||||||
|
|
||||||
# Copy the Go Modules manifests
|
# Copy the Go Modules manifests
|
||||||
COPY go.mod go.sum ./
|
COPY go.mod go.sum ./
|
||||||
|
|
||||||
@@ -25,20 +24,20 @@ RUN go mod download
|
|||||||
# With the above commmand,
|
# With the above commmand,
|
||||||
# TARGETOS can be "linux", TARGETARCH can be "amd64", "arm64", and "arm", TARGETVARIANT can be "v7".
|
# TARGETOS can be "linux", TARGETARCH can be "amd64", "arm64", and "arm", TARGETVARIANT can be "v7".
|
||||||
|
|
||||||
ARG TARGETPLATFORM TARGETOS TARGETARCH TARGETVARIANT
|
ARG TARGETPLATFORM TARGETOS TARGETARCH TARGETVARIANT VERSION=dev
|
||||||
|
|
||||||
# We intentionally avoid `--mount=type=cache,mode=0777,target=/go/pkg/mod` in the `go mod download` and the `go build` runs
|
# We intentionally avoid `--mount=type=cache,mode=0777,target=/go/pkg/mod` in the `go mod download` and the `go build` runs
|
||||||
# to avoid https://github.com/moby/buildkit/issues/2334
|
# to avoid https://github.com/moby/buildkit/issues/2334
|
||||||
# We can use docker layer cache so the build is fast enogh anyway
|
# We can use docker layer cache so the build is fast enogh anyway
|
||||||
# We also use per-platform GOCACHE for the same reason.
|
# We also use per-platform GOCACHE for the same reason.
|
||||||
env GOCACHE /build/${TARGETPLATFORM}/root/.cache/go-build
|
ENV GOCACHE /build/${TARGETPLATFORM}/root/.cache/go-build
|
||||||
|
|
||||||
# Build
|
# Build
|
||||||
RUN --mount=target=. \
|
RUN --mount=target=. \
|
||||||
--mount=type=cache,mode=0777,target=${GOCACHE} \
|
--mount=type=cache,mode=0777,target=${GOCACHE} \
|
||||||
export GOOS=${TARGETOS} GOARCH=${TARGETARCH} GOARM=${TARGETVARIANT#v} && \
|
export GOOS=${TARGETOS} GOARCH=${TARGETARCH} GOARM=${TARGETVARIANT#v} && \
|
||||||
go build -o /out/manager main.go && \
|
go build -trimpath -ldflags="-s -w -X 'github.com/actions-runner-controller/actions-runner-controller/build.Version=${VERSION}'" -o /out/manager main.go && \
|
||||||
go build -o /out/github-webhook-server ./cmd/githubwebhookserver
|
go build -trimpath -ldflags="-s -w" -o /out/github-webhook-server ./cmd/githubwebhookserver
|
||||||
|
|
||||||
# Use distroless as minimal base image to package the manager binary
|
# Use distroless as minimal base image to package the manager binary
|
||||||
# Refer to https://github.com/GoogleContainerTools/distroless for more details
|
# Refer to https://github.com/GoogleContainerTools/distroless for more details
|
||||||
@@ -49,6 +48,6 @@ WORKDIR /
|
|||||||
COPY --from=builder /out/manager .
|
COPY --from=builder /out/manager .
|
||||||
COPY --from=builder /out/github-webhook-server .
|
COPY --from=builder /out/github-webhook-server .
|
||||||
|
|
||||||
USER nonroot:nonroot
|
USER 65532:65532
|
||||||
|
|
||||||
ENTRYPOINT ["/manager"]
|
ENTRYPOINT ["/manager"]
|
||||||
|
|||||||
44
Makefile
44
Makefile
@@ -4,8 +4,8 @@ else
|
|||||||
NAME ?= summerwind/actions-runner-controller
|
NAME ?= summerwind/actions-runner-controller
|
||||||
endif
|
endif
|
||||||
DOCKER_USER ?= $(shell echo ${NAME} | cut -d / -f1)
|
DOCKER_USER ?= $(shell echo ${NAME} | cut -d / -f1)
|
||||||
VERSION ?= latest
|
VERSION ?= dev
|
||||||
RUNNER_VERSION ?= 2.290.1
|
RUNNER_VERSION ?= 2.298.2
|
||||||
TARGETPLATFORM ?= $(shell arch)
|
TARGETPLATFORM ?= $(shell arch)
|
||||||
RUNNER_NAME ?= ${DOCKER_USER}/actions-runner
|
RUNNER_NAME ?= ${DOCKER_USER}/actions-runner
|
||||||
RUNNER_TAG ?= ${VERSION}
|
RUNNER_TAG ?= ${VERSION}
|
||||||
@@ -15,11 +15,11 @@ TEST_ORG_REPO ?=
|
|||||||
TEST_EPHEMERAL ?= false
|
TEST_EPHEMERAL ?= false
|
||||||
SYNC_PERIOD ?= 1m
|
SYNC_PERIOD ?= 1m
|
||||||
USE_RUNNERSET ?=
|
USE_RUNNERSET ?=
|
||||||
RUNNER_FEATURE_FLAG_EPHEMERAL ?=
|
|
||||||
KUBECONTEXT ?= kind-acceptance
|
KUBECONTEXT ?= kind-acceptance
|
||||||
CLUSTER ?= acceptance
|
CLUSTER ?= acceptance
|
||||||
CERT_MANAGER_VERSION ?= v1.1.1
|
CERT_MANAGER_VERSION ?= v1.1.1
|
||||||
KUBE_RBAC_PROXY_VERSION ?= v0.11.0
|
KUBE_RBAC_PROXY_VERSION ?= v0.11.0
|
||||||
|
SHELLCHECK_VERSION ?= 0.8.0
|
||||||
|
|
||||||
# Produce CRDs that work back to Kubernetes 1.11 (no version conversion)
|
# Produce CRDs that work back to Kubernetes 1.11 (no version conversion)
|
||||||
CRD_OPTIONS ?= "crd:generateEmbeddedObjectMeta=true"
|
CRD_OPTIONS ?= "crd:generateEmbeddedObjectMeta=true"
|
||||||
@@ -32,6 +32,7 @@ GOBIN=$(shell go env GOBIN)
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
TEST_ASSETS=$(PWD)/test-assets
|
TEST_ASSETS=$(PWD)/test-assets
|
||||||
|
TOOLS_PATH=$(PWD)/.tools
|
||||||
|
|
||||||
# default list of platforms for which multiarch image is built
|
# default list of platforms for which multiarch image is built
|
||||||
ifeq (${PLATFORMS}, )
|
ifeq (${PLATFORMS}, )
|
||||||
@@ -52,11 +53,15 @@ endif
|
|||||||
|
|
||||||
all: manager
|
all: manager
|
||||||
|
|
||||||
|
lint:
|
||||||
|
docker run --rm -v $(PWD):/app -w /app golangci/golangci-lint:v1.49.0 golangci-lint run
|
||||||
|
|
||||||
GO_TEST_ARGS ?= -short
|
GO_TEST_ARGS ?= -short
|
||||||
|
|
||||||
# Run tests
|
# Run tests
|
||||||
test: generate fmt vet manifests
|
test: generate fmt vet manifests shellcheck
|
||||||
go test $(GO_TEST_ARGS) ./... -coverprofile cover.out
|
go test $(GO_TEST_ARGS) ./... -coverprofile cover.out
|
||||||
|
go test -fuzz=Fuzz -fuzztime=10s -run=Fuzz* ./controllers
|
||||||
|
|
||||||
test-with-deps: kube-apiserver etcd kubectl
|
test-with-deps: kube-apiserver etcd kubectl
|
||||||
# See https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/envtest#pkg-constants
|
# See https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/envtest#pkg-constants
|
||||||
@@ -92,7 +97,7 @@ manifests: manifests-gen-crds chart-crds
|
|||||||
manifests-gen-crds: controller-gen yq
|
manifests-gen-crds: controller-gen yq
|
||||||
$(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases
|
$(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases
|
||||||
for YAMLFILE in config/crd/bases/actions*.yaml; do \
|
for YAMLFILE in config/crd/bases/actions*.yaml; do \
|
||||||
$(YQ) write --inplace "$$YAMLFILE" spec.preserveUnknownFields false; \
|
$(YQ) '.spec.preserveUnknownFields = false' --inplace "$$YAMLFILE" ; \
|
||||||
done
|
done
|
||||||
|
|
||||||
chart-crds:
|
chart-crds:
|
||||||
@@ -110,6 +115,10 @@ vet:
|
|||||||
generate: controller-gen
|
generate: controller-gen
|
||||||
$(CONTROLLER_GEN) object:headerFile=./hack/boilerplate.go.txt paths="./..."
|
$(CONTROLLER_GEN) object:headerFile=./hack/boilerplate.go.txt paths="./..."
|
||||||
|
|
||||||
|
# Run shellcheck on runner scripts
|
||||||
|
shellcheck: shellcheck-install
|
||||||
|
$(TOOLS_PATH)/shellcheck --shell bash --source-path runner runner/*.bash runner/*.sh
|
||||||
|
|
||||||
docker-buildx:
|
docker-buildx:
|
||||||
export DOCKER_CLI_EXPERIMENTAL=enabled ;\
|
export DOCKER_CLI_EXPERIMENTAL=enabled ;\
|
||||||
export DOCKER_BUILDKIT=1
|
export DOCKER_BUILDKIT=1
|
||||||
@@ -119,6 +128,7 @@ docker-buildx:
|
|||||||
docker buildx build --platform ${PLATFORMS} \
|
docker buildx build --platform ${PLATFORMS} \
|
||||||
--build-arg RUNNER_VERSION=${RUNNER_VERSION} \
|
--build-arg RUNNER_VERSION=${RUNNER_VERSION} \
|
||||||
--build-arg DOCKER_VERSION=${DOCKER_VERSION} \
|
--build-arg DOCKER_VERSION=${DOCKER_VERSION} \
|
||||||
|
--build-arg VERSION=${VERSION} \
|
||||||
-t "${NAME}:${VERSION}" \
|
-t "${NAME}:${VERSION}" \
|
||||||
-f Dockerfile \
|
-f Dockerfile \
|
||||||
. ${PUSH_ARG}
|
. ${PUSH_ARG}
|
||||||
@@ -188,7 +198,6 @@ acceptance/deploy:
|
|||||||
TEST_ORG=${TEST_ORG} TEST_ORG_REPO=${TEST_ORG_REPO} SYNC_PERIOD=${SYNC_PERIOD} \
|
TEST_ORG=${TEST_ORG} TEST_ORG_REPO=${TEST_ORG_REPO} SYNC_PERIOD=${SYNC_PERIOD} \
|
||||||
USE_RUNNERSET=${USE_RUNNERSET} \
|
USE_RUNNERSET=${USE_RUNNERSET} \
|
||||||
TEST_EPHEMERAL=${TEST_EPHEMERAL} \
|
TEST_EPHEMERAL=${TEST_EPHEMERAL} \
|
||||||
RUNNER_FEATURE_FLAG_EPHEMERAL=${RUNNER_FEATURE_FLAG_EPHEMERAL} \
|
|
||||||
acceptance/deploy.sh
|
acceptance/deploy.sh
|
||||||
|
|
||||||
acceptance/tests:
|
acceptance/tests:
|
||||||
@@ -223,7 +232,7 @@ ifeq (, $(wildcard $(GOBIN)/controller-gen))
|
|||||||
CONTROLLER_GEN_TMP_DIR=$$(mktemp -d) ;\
|
CONTROLLER_GEN_TMP_DIR=$$(mktemp -d) ;\
|
||||||
cd $$CONTROLLER_GEN_TMP_DIR ;\
|
cd $$CONTROLLER_GEN_TMP_DIR ;\
|
||||||
go mod init tmp ;\
|
go mod init tmp ;\
|
||||||
go get sigs.k8s.io/controller-tools/cmd/controller-gen@v0.7.0 ;\
|
go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.7.0 ;\
|
||||||
rm -rf $$CONTROLLER_GEN_TMP_DIR ;\
|
rm -rf $$CONTROLLER_GEN_TMP_DIR ;\
|
||||||
}
|
}
|
||||||
endif
|
endif
|
||||||
@@ -243,12 +252,31 @@ ifeq (, $(wildcard $(GOBIN)/yq))
|
|||||||
YQ_TMP_DIR=$$(mktemp -d) ;\
|
YQ_TMP_DIR=$$(mktemp -d) ;\
|
||||||
cd $$YQ_TMP_DIR ;\
|
cd $$YQ_TMP_DIR ;\
|
||||||
go mod init tmp ;\
|
go mod init tmp ;\
|
||||||
go get github.com/mikefarah/yq/v3@3.4.0 ;\
|
go install github.com/mikefarah/yq/v4@v4.25.3 ;\
|
||||||
rm -rf $$YQ_TMP_DIR ;\
|
rm -rf $$YQ_TMP_DIR ;\
|
||||||
}
|
}
|
||||||
endif
|
endif
|
||||||
YQ=$(GOBIN)/yq
|
YQ=$(GOBIN)/yq
|
||||||
|
|
||||||
|
# find or download shellcheck
|
||||||
|
# download shellcheck if necessary
|
||||||
|
shellcheck-install:
|
||||||
|
ifeq (, $(wildcard $(TOOLS_PATH)/shellcheck))
|
||||||
|
echo "Downloading shellcheck"
|
||||||
|
@{ \
|
||||||
|
set -e ;\
|
||||||
|
SHELLCHECK_TMP_DIR=$$(mktemp -d) ;\
|
||||||
|
cd $$SHELLCHECK_TMP_DIR ;\
|
||||||
|
curl -LO https://github.com/koalaman/shellcheck/releases/download/v$(SHELLCHECK_VERSION)/shellcheck-v$(SHELLCHECK_VERSION).linux.x86_64.tar.xz ;\
|
||||||
|
tar Jxvf shellcheck-v$(SHELLCHECK_VERSION).linux.x86_64.tar.xz ;\
|
||||||
|
cd $(CURDIR) ;\
|
||||||
|
mkdir -p $(TOOLS_PATH) ;\
|
||||||
|
mv $$SHELLCHECK_TMP_DIR/shellcheck-v$(SHELLCHECK_VERSION)/shellcheck $(TOOLS_PATH)/ ;\
|
||||||
|
rm -rf $$SHELLCHECK_TMP_DIR ;\
|
||||||
|
}
|
||||||
|
endif
|
||||||
|
SHELLCHECK=$(TOOLS_PATH)/shellcheck
|
||||||
|
|
||||||
OS_NAME := $(shell uname -s | tr A-Z a-z)
|
OS_NAME := $(shell uname -s | tr A-Z a-z)
|
||||||
|
|
||||||
# find or download etcd
|
# find or download etcd
|
||||||
|
|||||||
22
SECURITY.md
Normal file
22
SECURITY.md
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# Security Policy
|
||||||
|
|
||||||
|
## Sponsoring the project
|
||||||
|
|
||||||
|
This project is maintained by a small team of two and therefore lacks the resource to provide security fixes in a timely manner.
|
||||||
|
|
||||||
|
If you have important business(es) that relies on this project, please consider sponsoring the project so that the maintainer(s) can commit to providing such service.
|
||||||
|
|
||||||
|
Please refer to https://github.com/sponsors/actions-runner-controller for available tiers.
|
||||||
|
|
||||||
|
## Supported Versions
|
||||||
|
|
||||||
|
| Version | Supported |
|
||||||
|
| ------- | ------------------ |
|
||||||
|
| 0.23.0 | :white_check_mark: |
|
||||||
|
| < 0.23.0| :x: |
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
To report a security issue, please email ykuoka+arcsecurity(at)gmail.com with a description of the issue, the steps you took to create the issue, affected versions, and, if known, mitigations for the issue.
|
||||||
|
|
||||||
|
A maintainer will try to respond within 5 working days. If the issue is confirmed as a vulnerability, a Security Advisory will be opened. This project tries to follow a 90 day disclosure timeline.
|
||||||
@@ -1,10 +1,93 @@
|
|||||||
# Troubleshooting
|
# Troubleshooting
|
||||||
|
|
||||||
* [Invalid header field value](#invalid-header-field-value)
|
* [Tools](#tools)
|
||||||
* [Runner coming up before network available](#runner-coming-up-before-network-available)
|
* [Installation](#installation)
|
||||||
* [Deployment fails on GKE due to webhooks](#deployment-fails-on-gke-due-to-webhooks)
|
* [InternalError when calling webhook: context deadline exceeded](#internalerror-when-calling-webhook-context-deadline-exceeded)
|
||||||
|
* [Invalid header field value](#invalid-header-field-value)
|
||||||
|
* [Helm chart install failure: certificate signed by unknown authority](#helm-chart-install-failure-certificate-signed-by-unknown-authority)
|
||||||
|
* [Operations](#operations)
|
||||||
|
* [Stuck runner kind or backing pod](#stuck-runner-kind-or-backing-pod)
|
||||||
|
* [Delay in jobs being allocated to runners](#delay-in-jobs-being-allocated-to-runners)
|
||||||
|
* [Runner coming up before network available](#runner-coming-up-before-network-available)
|
||||||
|
* [Outgoing network action hangs indefinitely](#outgoing-network-action-hangs-indefinitely)
|
||||||
|
* [Unable to scale to zero with TotalNumberOfQueuedAndInProgressWorkflowRuns](#unable-to-scale-to-zero-with-totalnumberofqueuedandinprogressworkflowruns)
|
||||||
|
|
||||||
## Invalid header field value
|
## Tools
|
||||||
|
|
||||||
|
A list of tools which are helpful for troubleshooting
|
||||||
|
|
||||||
|
* https://github.com/rewanthtammana/kubectl-fields Kubernetes resources hierarchy parsing tool
|
||||||
|
* https://github.com/stern/stern Multi pod and container log tailing for Kubernetes
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Troubeshooting runbooks that relate to ARC installation problems
|
||||||
|
|
||||||
|
### InternalError when calling webhook: context deadline exceeded
|
||||||
|
|
||||||
|
**Problem**
|
||||||
|
|
||||||
|
This issue can come up for various reasons like leftovers from previous installations or not being able to access the K8s service's clusterIP associated with the admission webhook server (of ARC).
|
||||||
|
|
||||||
|
```
|
||||||
|
Internal error occurred: failed calling webhook "mutate.runnerdeployment.actions.summerwind.dev":
|
||||||
|
Post "https://actions-runner-controller-webhook.actions-runner-system.svc:443/mutate-actions-summerwind-dev-v1alpha1-runnerdeployment?timeout=10s": context deadline exceeded
|
||||||
|
```
|
||||||
|
|
||||||
|
**Solution**
|
||||||
|
|
||||||
|
First we will try the common solution of checking webhook leftovers from previous installations:
|
||||||
|
|
||||||
|
1. ```bash
|
||||||
|
kubectl get validatingwebhookconfiguration -A
|
||||||
|
kubectl get mutatingwebhookconfiguration -A
|
||||||
|
```
|
||||||
|
2. If you see any webhooks related to actions-runner-controller, delete them:
|
||||||
|
```bash
|
||||||
|
kubectl delete mutatingwebhookconfiguration actions-runner-controller-mutating-webhook-configuration
|
||||||
|
kubectl delete validatingwebhookconfiguration actions-runner-controller-validating-webhook-configuration
|
||||||
|
```
|
||||||
|
|
||||||
|
If that didn't work then probably your K8s control-plane is somehow unable to access the K8s service's clusterIP associated with the admission webhook server:
|
||||||
|
1. You're running apiserver as a binary and you didn't make service cluster IPs available to the host network.
|
||||||
|
2. You're running the apiserver in the pod but your pod network (i.e. CNI plugin installation and config) is not good so your pods(like kube-apiserver) in the K8s control-plane nodes can't access ARC's admission webhook server pod(s) in probably data-plane nodes.
|
||||||
|
|
||||||
|
|
||||||
|
Another reason could be due to GKEs firewall settings you may run into the following errors when trying to deploy runners on a private GKE cluster:
|
||||||
|
|
||||||
|
To fix this, you may either:
|
||||||
|
|
||||||
|
1. Configure the webhook to use another port, such as 443 or 10250, [each of
|
||||||
|
which allow traffic by default](https://cloud.google.com/kubernetes-engine/docs/how-to/private-clusters#add_firewall_rules).
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# With helm, you'd set `webhookPort` to the port number of your choice
|
||||||
|
# See https://github.com/actions-runner-controller/actions-runner-controller/pull/1410/files for more information
|
||||||
|
helm upgrade --install --namespace actions-runner-system --create-namespace \
|
||||||
|
--wait actions-runner-controller actions-runner-controller/actions-runner-controller \
|
||||||
|
--set webhookPort=10250
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Set up a firewall rule to allow the master node to connect to the default
|
||||||
|
webhook port. The exact way to do this may vary, but the following script
|
||||||
|
should point you in the right direction:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# 1) Retrieve the network tag automatically given to the worker nodes
|
||||||
|
# NOTE: this only works if you have only one cluster in your GCP project. You will have to manually inspect the result of this command to find the tag for the cluster you want to target
|
||||||
|
WORKER_NODES_TAG=$(gcloud compute instances list --format='text(tags.items[0])' --filter='metadata.kubelet-config:*' | grep tags | awk '{print $2}' | sort | uniq)
|
||||||
|
|
||||||
|
# 2) Take note of the VPC network in which you deployed your cluster
|
||||||
|
# NOTE this only works if you have only one network in which you deploy your clusters
|
||||||
|
NETWORK=$(gcloud compute instances list --format='text(networkInterfaces[0].network)' --filter='metadata.kubelet-config:*' | grep networks | awk -F'/' '{print $NF}' | sort | uniq)
|
||||||
|
|
||||||
|
# 3) Get the master source ip block
|
||||||
|
SOURCE=$(gcloud container clusters describe <cluster-name> --region <region> | grep masterIpv4CidrBlock| cut -d ':' -f 2 | tr -d ' ')
|
||||||
|
|
||||||
|
gcloud compute firewall-rules create k8s-cert-manager --source-ranges $SOURCE --target-tags $WORKER_NODES_TAG --allow TCP:9443 --network $NETWORK
|
||||||
|
```
|
||||||
|
|
||||||
|
### Invalid header field value
|
||||||
|
|
||||||
**Problem**
|
**Problem**
|
||||||
|
|
||||||
@@ -23,7 +106,88 @@ Your base64'ed PAT token has a new line at the end, it needs to be created witho
|
|||||||
* `echo -n $TOKEN | base64`
|
* `echo -n $TOKEN | base64`
|
||||||
* Create the secret as described in the docs using the shell and documented flags
|
* Create the secret as described in the docs using the shell and documented flags
|
||||||
|
|
||||||
## Runner coming up before network available
|
### Helm chart install failure: certificate signed by unknown authority
|
||||||
|
|
||||||
|
**Problem**
|
||||||
|
|
||||||
|
```
|
||||||
|
Error: UPGRADE FAILED: failed to create resource: Internal error occurred: failed calling webhook "webhook.cert-manager.io": failed to call webhook: Post "https://cert-manager-webhook.cert-manager.svc:443/mutate?timeout=10s": x509: certificate signed by unknown authority
|
||||||
|
```
|
||||||
|
|
||||||
|
Apparently, it's failing while `helm` is creating one of resources defined in the ARC chart and the cause was that cert-manager's webhook is not working correctly, due to the missing or the invalid CA certficate.
|
||||||
|
|
||||||
|
You'd try to tail logs from the `cert-manager-cainjector` and see it's failing with an error like:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ kubectl -n cert-manager logs cert-manager-cainjector-7cdbb9c945-g6bt4
|
||||||
|
I0703 03:31:55.159339 1 start.go:91] "starting" version="v1.1.1" revision="3ac7418070e22c87fae4b22603a6b952f797ae96"
|
||||||
|
I0703 03:31:55.615061 1 leaderelection.go:243] attempting to acquire leader lease kube-system/cert-manager-cainjector-leader-election...
|
||||||
|
I0703 03:32:10.738039 1 leaderelection.go:253] successfully acquired lease kube-system/cert-manager-cainjector-leader-election
|
||||||
|
I0703 03:32:10.739941 1 recorder.go:52] cert-manager/controller-runtime/manager/events "msg"="Normal" "message"="cert-manager-cainjector-7cdbb9c945-g6bt4_88e4bc70-eded-4343-a6fb-0ddd6434eb55 became leader" "object"={"kind":"ConfigMap","namespace":"kube-system","name":"cert-manager-cainjector-leader-election","uid":"942a021e-364c-461a-978c-f54a95723cdc","apiVersion":"v1","resourceVersion":"1576"} "reason"="LeaderElection"
|
||||||
|
E0703 03:32:11.192128 1 start.go:119] cert-manager/ca-injector "msg"="manager goroutine exited" "error"=null
|
||||||
|
I0703 03:32:12.339197 1 request.go:645] Throttling request took 1.047437675s, request: GET:https://10.96.0.1:443/apis/storage.k8s.io/v1beta1?timeout=32s
|
||||||
|
E0703 03:32:13.143790 1 start.go:151] cert-manager/ca-injector "msg"="Error registering certificate based controllers. Retrying after 5 seconds." "error"="no matches for kind \"MutatingWebhookConfiguration\" in version \"admissionregistration.k8s.io/v1beta1\""
|
||||||
|
Error: error registering secret controller: no matches for kind "MutatingWebhookConfiguration" in version "admissionregistration.k8s.io/v1beta1"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Solution**
|
||||||
|
|
||||||
|
Your cluster is based on a new enough Kubernetes of version 1.22 or greater which does not support the legacy `admissionregistration.k8s.io/v1beta1` API anymore, and your `cert-manager` is not up-to-date hence it's still trying to use the leagcy Kubernetes API.
|
||||||
|
|
||||||
|
In many cases, it's not an option to downgrade Kubernetes. So, just upgrade `cert-manager` to a more recent version that does have have the support for the specific Kubernetes version you're using.
|
||||||
|
|
||||||
|
See https://cert-manager.io/docs/installation/supported-releases/ for the list of available cert-manager versions.
|
||||||
|
|
||||||
|
## Operations
|
||||||
|
|
||||||
|
Troubeshooting runbooks that relate to ARC operational problems
|
||||||
|
|
||||||
|
### Stuck runner kind or backing pod
|
||||||
|
|
||||||
|
**Problem**
|
||||||
|
|
||||||
|
Sometimes either the runner kind (`kubectl get runners`) or it's underlying pod can get stuck in a terminating state for various reasons. You can get the kind unstuck by removing its finaliser using something like this:
|
||||||
|
|
||||||
|
**Solution**
|
||||||
|
|
||||||
|
Remove the finaliser from the relevent runner kind or pod
|
||||||
|
|
||||||
|
```
|
||||||
|
# Get all kind runners and remove the finalizer
|
||||||
|
$ kubectl get runners --no-headers | awk {'print $1'} | xargs kubectl patch runner --type merge -p '{"metadata":{"finalizers":null}}'
|
||||||
|
|
||||||
|
# Get all pods that are stuck terminating and remove the finalizer
|
||||||
|
$ kubectl -n get pods | grep Terminating | awk {'print $1'} | xargs kubectl patch pod -p '{"metadata":{"finalizers":null}}'
|
||||||
|
```
|
||||||
|
|
||||||
|
_Note the code assumes you have already selected the namespace your runners are in and that they
|
||||||
|
are in a namespace not shared with anything else_
|
||||||
|
|
||||||
|
### Delay in jobs being allocated to runners
|
||||||
|
|
||||||
|
**Problem**
|
||||||
|
|
||||||
|
ARC isn't involved in jobs actually getting allocated to a runner. ARC is responsible for orchestrating runners and the runner lifecycle. Why some people see large delays in job allocation is not clear however it has been confirmed https://github.com/actions-runner-controller/actions-runner-controller/issues/1387#issuecomment-1122593984 that this is caused from the self-update process somehow.
|
||||||
|
|
||||||
|
**Solution**
|
||||||
|
|
||||||
|
Disable the self-update process in your runner manifests
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: actions.summerwind.dev/v1alpha1
|
||||||
|
kind: RunnerDeployment
|
||||||
|
metadata:
|
||||||
|
name: example-runnerdeployment-with-sleep
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
...
|
||||||
|
env:
|
||||||
|
- name: DISABLE_RUNNER_UPDATE
|
||||||
|
value: "true"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Runner coming up before network available
|
||||||
|
|
||||||
**Problem**
|
**Problem**
|
||||||
|
|
||||||
@@ -61,40 +225,68 @@ metadata:
|
|||||||
spec:
|
spec:
|
||||||
template:
|
template:
|
||||||
spec:
|
spec:
|
||||||
|
...
|
||||||
env:
|
env:
|
||||||
# This runner's entrypoint script will have a 5 seconds delay
|
|
||||||
# as a first action within the entrypoint script
|
|
||||||
- name: STARTUP_DELAY_IN_SECONDS
|
- name: STARTUP_DELAY_IN_SECONDS
|
||||||
value: "5"
|
value: "5"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Deployment fails on GKE due to webhooks
|
## Outgoing network action hangs indefinitely
|
||||||
|
|
||||||
**Problem**
|
**Problem**
|
||||||
|
|
||||||
Due to GKEs firewall settings you may run into the following errors when trying to deploy runners on a private GKE cluster:
|
Some random outgoing network actions hangs indefinitely. This could be because your cluster does not give Docker the standard MTU of 1500, you can check this out by running `ip link` in a pod that encounters the problem and reading the outgoing interface's MTU value. If it is smaller than 1500, then try the following.
|
||||||
|
|
||||||
```
|
**Solution**
|
||||||
Internal error occurred: failed calling webhook "mutate.runner.actions.summerwind.dev":
|
|
||||||
Post https://webhook-service.actions-runner-system.svc:443/mutate-actions-summerwind-dev-v1alpha1-runner?timeout=10s:
|
Add a `dockerMTU` key in your runner's spec with the value you read on the outgoing interface. For instance:
|
||||||
context deadline exceeded
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: actions.summerwind.dev/v1alpha1
|
||||||
|
kind: RunnerDeployment
|
||||||
|
metadata:
|
||||||
|
name: github-runner
|
||||||
|
namespace: github-system
|
||||||
|
spec:
|
||||||
|
replicas: 6
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
dockerMTU: 1400
|
||||||
|
repository: $username/$repo
|
||||||
|
env: []
|
||||||
```
|
```
|
||||||
|
|
||||||
**Solution**<br />
|
If the issue still persists, you can set the `ARC_DOCKER_MTU_PROPAGATION` to propagate the host MTU to networks created
|
||||||
|
by the GitHub Runner. For instance:
|
||||||
To fix this, you need to set up a firewall rule to allow the master node to connect to the webhook port.
|
|
||||||
The exact way to do this may wary, but the following script should point you in the right direction:
|
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: actions.summerwind.dev/v1alpha1
|
||||||
|
kind: RunnerDeployment
|
||||||
|
metadata:
|
||||||
|
name: github-runner
|
||||||
|
namespace: github-system
|
||||||
|
spec:
|
||||||
|
replicas: 6
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
dockerMTU: 1400
|
||||||
|
repository: $username/$repo
|
||||||
|
env:
|
||||||
|
- name: ARC_DOCKER_MTU_PROPAGATION
|
||||||
|
value: "true"
|
||||||
```
|
```
|
||||||
# 1) Retrieve the network tag automatically given to the worker nodes
|
|
||||||
# NOTE: this only works if you have only one cluster in your GCP project. You will have to manually inspect the result of this command to find the tag for the cluster you want to target
|
|
||||||
WORKER_NODES_TAG=$(gcloud compute instances list --format='text(tags.items[0])' --filter='metadata.kubelet-config:*' | grep tags | awk '{print $2}' | sort | uniq)
|
|
||||||
|
|
||||||
# 2) Take note of the VPC network in which you deployed your cluster
|
You can read the discussion regarding this issue in
|
||||||
# NOTE this only works if you have only one network in which you deploy your clusters
|
(#1406)[https://github.com/actions-runner-controller/actions-runner-controller/issues/1046].
|
||||||
NETWORK=$(gcloud compute instances list --format='text(networkInterfaces[0].network)' --filter='metadata.kubelet-config:*' | grep networks | awk -F'/' '{print $NF}' | sort | uniq)
|
|
||||||
|
|
||||||
# 3) Get the master source ip block
|
## Unable to scale to zero with TotalNumberOfQueuedAndInProgressWorkflowRuns
|
||||||
SOURCE=$(gcloud container clusters describe <cluster-name> --region <region> | grep masterIpv4CidrBlock| cut -d ':' -f 2 | tr -d ' ')
|
|
||||||
gcloud compute firewall-rules create k8s-cert-manager --source-ranges $SOURCE --target-tags $WORKER_NODES_TAG --allow TCP:9443 --network $NETWORK
|
**Problem**
|
||||||
```
|
|
||||||
|
HRA doesn't scale the RunnerDeployment to zero, even though you did configure HRA correctly, to have a pull-based scaling metric `TotalNumberOfQueuedAndInProgressWorkflowRuns`, and set `minReplicas: 0`.
|
||||||
|
|
||||||
|
**Solution**
|
||||||
|
|
||||||
|
You very likely have some dangling workflow jobs stuck in `queued` or `in_progress` as seen in [#1057](https://github.com/actions-runner-controller/actions-runner-controller/issues/1057#issuecomment-1133439061).
|
||||||
|
|
||||||
|
Manually call [the "list workflow runs" API](https://docs.github.com/en/rest/actions/workflow-runs#list-workflow-runs-for-a-repository), and [remove the dangling workflow job(s)](https://docs.github.com/en/rest/actions/workflow-runs#delete-a-workflow-run).
|
||||||
|
|||||||
97
acceptance/argotunnel.sh
Executable file
97
acceptance/argotunnel.sh
Executable file
@@ -0,0 +1,97 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# See https://developers.cloudflare.com/cloudflare-one/tutorials/many-cfd-one-tunnel/
|
||||||
|
|
||||||
|
kubectl create ns tunnel || :
|
||||||
|
|
||||||
|
kubectl -n tunnel delete secret tunnel-credentials || :
|
||||||
|
|
||||||
|
kubectl -n tunnel create secret generic tunnel-credentials \
|
||||||
|
--from-file=credentials.json=$HOME/.cloudflared/${TUNNEL_ID}.json || :
|
||||||
|
|
||||||
|
cat <<MANIFEST | kubectl -n tunnel ${OP} -f -
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: cloudflared
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: cloudflared
|
||||||
|
replicas: 2 # You could also consider elastic scaling for this deployment
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: cloudflared
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: cloudflared
|
||||||
|
image: cloudflare/cloudflared:latest
|
||||||
|
args:
|
||||||
|
- tunnel
|
||||||
|
# Points cloudflared to the config file, which configures what
|
||||||
|
# cloudflared will actually do. This file is created by a ConfigMap
|
||||||
|
# below.
|
||||||
|
- --config
|
||||||
|
- /etc/cloudflared/config/config.yaml
|
||||||
|
- run
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
# Cloudflared has a /ready endpoint which returns 200 if and only if
|
||||||
|
# it has an active connection to the edge.
|
||||||
|
path: /ready
|
||||||
|
port: 2000
|
||||||
|
failureThreshold: 1
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 10
|
||||||
|
volumeMounts:
|
||||||
|
- name: config
|
||||||
|
mountPath: /etc/cloudflared/config
|
||||||
|
readOnly: true
|
||||||
|
# Each tunnel has an associated "credentials file" which authorizes machines
|
||||||
|
# to run the tunnel. cloudflared will read this file from its local filesystem,
|
||||||
|
# and it'll be stored in a k8s secret.
|
||||||
|
- name: creds
|
||||||
|
mountPath: /etc/cloudflared/creds
|
||||||
|
readOnly: true
|
||||||
|
volumes:
|
||||||
|
- name: creds
|
||||||
|
secret:
|
||||||
|
secretName: tunnel-credentials
|
||||||
|
# Create a config.yaml file from the ConfigMap below.
|
||||||
|
- name: config
|
||||||
|
configMap:
|
||||||
|
name: cloudflared
|
||||||
|
items:
|
||||||
|
- key: config.yaml
|
||||||
|
path: config.yaml
|
||||||
|
---
|
||||||
|
# This ConfigMap is just a way to define the cloudflared config.yaml file in k8s.
|
||||||
|
# It's useful to define it in k8s, rather than as a stand-alone .yaml file, because
|
||||||
|
# this lets you use various k8s templating solutions (e.g. Helm charts) to
|
||||||
|
# parameterize your config, instead of just using string literals.
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: cloudflared
|
||||||
|
data:
|
||||||
|
config.yaml: |
|
||||||
|
# Name of the tunnel you want to run
|
||||||
|
tunnel: ${TUNNEL_NAME}
|
||||||
|
credentials-file: /etc/cloudflared/creds/credentials.json
|
||||||
|
# Serves the metrics server under /metrics and the readiness server under /ready
|
||||||
|
metrics: 0.0.0.0:2000
|
||||||
|
# Autoupdates applied in a k8s pod will be lost when the pod is removed or restarted, so
|
||||||
|
# autoupdate doesn't make sense in Kubernetes. However, outside of Kubernetes, we strongly
|
||||||
|
# recommend using autoupdate.
|
||||||
|
no-autoupdate: true
|
||||||
|
ingress:
|
||||||
|
# The first rule proxies traffic to the httpbin sample Service defined in app.yaml
|
||||||
|
- hostname: ${TUNNEL_HOSTNAME}
|
||||||
|
service: http://actions-runner-controller-github-webhook-server.actions-runner-system:80
|
||||||
|
# This rule matches any traffic which didn't match a previous rule, and responds with HTTP 404.
|
||||||
|
- service: http_status:404
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
kubectl -n tunnel delete po -l app=cloudflared || :
|
||||||
@@ -41,8 +41,23 @@ TEST_ID=${TEST_ID:-default}
|
|||||||
|
|
||||||
if [ "${tool}" == "helm" ]; then
|
if [ "${tool}" == "helm" ]; then
|
||||||
set -v
|
set -v
|
||||||
|
|
||||||
|
CHART=${CHART:-charts/actions-runner-controller}
|
||||||
|
|
||||||
|
flags=()
|
||||||
|
if [ "${IMAGE_PULL_SECRET}" != "" ]; then
|
||||||
|
flags+=( --set imagePullSecrets[0].name=${IMAGE_PULL_SECRET})
|
||||||
|
flags+=( --set image.actionsRunnerImagePullSecrets[0].name=${IMAGE_PULL_SECRET})
|
||||||
|
flags+=( --set githubWebhookServer.imagePullSecrets[0].name=${IMAGE_PULL_SECRET})
|
||||||
|
fi
|
||||||
|
if [ "${CHART_VERSION}" != "" ]; then
|
||||||
|
flags+=( --version ${CHART_VERSION})
|
||||||
|
fi
|
||||||
|
|
||||||
|
set -vx
|
||||||
|
|
||||||
helm upgrade --install actions-runner-controller \
|
helm upgrade --install actions-runner-controller \
|
||||||
charts/actions-runner-controller \
|
${CHART} \
|
||||||
-n actions-runner-system \
|
-n actions-runner-system \
|
||||||
--create-namespace \
|
--create-namespace \
|
||||||
--set syncPeriod=${SYNC_PERIOD} \
|
--set syncPeriod=${SYNC_PERIOD} \
|
||||||
@@ -51,6 +66,7 @@ if [ "${tool}" == "helm" ]; then
|
|||||||
--set image.tag=${VERSION} \
|
--set image.tag=${VERSION} \
|
||||||
--set podAnnotations.test-id=${TEST_ID} \
|
--set podAnnotations.test-id=${TEST_ID} \
|
||||||
--set githubWebhookServer.podAnnotations.test-id=${TEST_ID} \
|
--set githubWebhookServer.podAnnotations.test-id=${TEST_ID} \
|
||||||
|
${flags[@]} --set image.imagePullPolicy=${IMAGE_PULL_POLICY} \
|
||||||
-f ${VALUES_FILE}
|
-f ${VALUES_FILE}
|
||||||
set +v
|
set +v
|
||||||
# To prevent `CustomResourceDefinition.apiextensions.k8s.io "runners.actions.summerwind.dev" is invalid: metadata.annotations: Too long: must have at most 262144 bytes`
|
# To prevent `CustomResourceDefinition.apiextensions.k8s.io "runners.actions.summerwind.dev" is invalid: metadata.annotations: Too long: must have at most 262144 bytes`
|
||||||
@@ -76,56 +92,3 @@ kubectl -n actions-runner-system wait deploy/actions-runner-controller --for con
|
|||||||
|
|
||||||
# Adhocly wait for some time until actions-runner-controller's admission webhook gets ready
|
# Adhocly wait for some time until actions-runner-controller's admission webhook gets ready
|
||||||
sleep 20
|
sleep 20
|
||||||
|
|
||||||
RUNNER_LABEL=${RUNNER_LABEL:-self-hosted}
|
|
||||||
|
|
||||||
if [ -n "${TEST_REPO}" ]; then
|
|
||||||
if [ "${USE_RUNNERSET}" != "false" ]; then
|
|
||||||
cat acceptance/testdata/runnerset.envsubst.yaml | TEST_ENTERPRISE= TEST_ORG= RUNNER_MIN_REPLICAS=${REPO_RUNNER_MIN_REPLICAS} NAME=repo-runnerset envsubst | kubectl apply -f -
|
|
||||||
else
|
|
||||||
echo 'Deploying runnerdeployment and hra. Set USE_RUNNERSET if you want to deploy runnerset instead.'
|
|
||||||
cat acceptance/testdata/runnerdeploy.envsubst.yaml | TEST_ENTERPRISE= TEST_ORG= RUNNER_MIN_REPLICAS=${REPO_RUNNER_MIN_REPLICAS} NAME=repo-runnerdeploy envsubst | kubectl apply -f -
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo 'Skipped deploying runnerdeployment and hra. Set TEST_REPO to "yourorg/yourrepo" to deploy.'
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "${TEST_ORG}" ]; then
|
|
||||||
if [ "${USE_RUNNERSET}" != "false" ]; then
|
|
||||||
cat acceptance/testdata/runnerset.envsubst.yaml | TEST_ENTERPRISE= TEST_REPO= RUNNER_MIN_REPLICAS=${ORG_RUNNER_MIN_REPLICAS} NAME=org-runnerset envsubst | kubectl apply -f -
|
|
||||||
else
|
|
||||||
cat acceptance/testdata/runnerdeploy.envsubst.yaml | TEST_ENTERPRISE= TEST_REPO= RUNNER_MIN_REPLICAS=${ORG_RUNNER_MIN_REPLICAS} NAME=org-runnerdeploy envsubst | kubectl apply -f -
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "${TEST_ORG_GROUP}" ]; then
|
|
||||||
if [ "${USE_RUNNERSET}" != "false" ]; then
|
|
||||||
cat acceptance/testdata/runnerset.envsubst.yaml | TEST_ENTERPRISE= TEST_REPO= RUNNER_MIN_REPLICAS=${ORG_RUNNER_MIN_REPLICAS} TEST_GROUP=${TEST_ORG_GROUP} NAME=orgroupg-runnerset envsubst | kubectl apply -f -
|
|
||||||
else
|
|
||||||
cat acceptance/testdata/runnerdeploy.envsubst.yaml | TEST_ENTERPRISE= TEST_REPO= RUNNER_MIN_REPLICAS=${ORG_RUNNER_MIN_REPLICAS} TEST_GROUP=${TEST_ORG_GROUP} NAME=orggroup-runnerdeploy envsubst | kubectl apply -f -
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo 'Skipped deploying enterprise runnerdeployment. Set TEST_ORG_GROUP to deploy.'
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo 'Skipped deploying organizational runnerdeployment. Set TEST_ORG to deploy.'
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "${TEST_ENTERPRISE}" ]; then
|
|
||||||
if [ "${USE_RUNNERSET}" != "false" ]; then
|
|
||||||
cat acceptance/testdata/runnerset.envsubst.yaml | TEST_ORG= TEST_REPO= RUNNER_MIN_REPLICAS=${ENTERPRISE_RUNNER_MIN_REPLICAS} NAME=enterprise-runnerset envsubst | kubectl apply -f -
|
|
||||||
else
|
|
||||||
cat acceptance/testdata/runnerdeploy.envsubst.yaml | TEST_ORG= TEST_REPO= RUNNER_MIN_REPLICAS=${ENTERPRISE_RUNNER_MIN_REPLICAS} NAME=enterprise-runnerdeploy envsubst | kubectl apply -f -
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "${TEST_ENTERPRISE_GROUP}" ]; then
|
|
||||||
if [ "${USE_RUNNERSET}" != "false" ]; then
|
|
||||||
cat acceptance/testdata/runnerset.envsubst.yaml | TEST_ORG= TEST_REPO= RUNNER_MIN_REPLICAS=${ENTERPRISE_RUNNER_MIN_REPLICAS} TEST_GROUP=${TEST_ENTERPRISE_GROUP} NAME=enterprisegroup-runnerset envsubst | kubectl apply -f -
|
|
||||||
else
|
|
||||||
cat acceptance/testdata/runnerdeploy.envsubst.yaml | TEST_ORG= TEST_REPO= RUNNER_MIN_REPLICAS=${ENTERPRISE_RUNNER_MIN_REPLICAS} TEST_GROUP=${TEST_ENTERPRISE_GROUP} NAME=enterprisegroup-runnerdeploy envsubst | kubectl apply -f -
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo 'Skipped deploying enterprise runnerdeployment. Set TEST_ENTERPRISE_GROUP to deploy.'
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo 'Skipped deploying enterprise runnerdeployment. Set TEST_ENTERPRISE to deploy.'
|
|
||||||
fi
|
|
||||||
|
|||||||
60
acceptance/deploy_runners.sh
Executable file
60
acceptance/deploy_runners.sh
Executable file
@@ -0,0 +1,60 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
OP=${OP:-apply}
|
||||||
|
|
||||||
|
RUNNER_LABEL=${RUNNER_LABEL:-self-hosted}
|
||||||
|
|
||||||
|
cat acceptance/testdata/kubernetes_container_mode.envsubst.yaml | NAMESPACE=${RUNNER_NAMESPACE} envsubst | kubectl apply -f -
|
||||||
|
|
||||||
|
if [ -n "${TEST_REPO}" ]; then
|
||||||
|
if [ "${USE_RUNNERSET}" != "false" ]; then
|
||||||
|
cat acceptance/testdata/runnerset.envsubst.yaml | TEST_ENTERPRISE= TEST_ORG= RUNNER_MIN_REPLICAS=${REPO_RUNNER_MIN_REPLICAS} NAME=repo-runnerset envsubst | kubectl ${OP} -f -
|
||||||
|
else
|
||||||
|
echo "Running ${OP} runnerdeployment and hra. Set USE_RUNNERSET if you want to deploy runnerset instead."
|
||||||
|
cat acceptance/testdata/runnerdeploy.envsubst.yaml | TEST_ENTERPRISE= TEST_ORG= RUNNER_MIN_REPLICAS=${REPO_RUNNER_MIN_REPLICAS} NAME=repo-runnerdeploy envsubst | kubectl ${OP} -f -
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Skipped ${OP} for runnerdeployment and hra. Set TEST_REPO to "yourorg/yourrepo" to deploy."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "${TEST_ORG}" ]; then
|
||||||
|
if [ "${USE_RUNNERSET}" != "false" ]; then
|
||||||
|
cat acceptance/testdata/runnerset.envsubst.yaml | TEST_ENTERPRISE= TEST_REPO= RUNNER_MIN_REPLICAS=${ORG_RUNNER_MIN_REPLICAS} NAME=org-runnerset envsubst | kubectl ${OP} -f -
|
||||||
|
else
|
||||||
|
cat acceptance/testdata/runnerdeploy.envsubst.yaml | TEST_ENTERPRISE= TEST_REPO= RUNNER_MIN_REPLICAS=${ORG_RUNNER_MIN_REPLICAS} NAME=org-runnerdeploy envsubst | kubectl ${OP} -f -
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "${TEST_ORG_GROUP}" ]; then
|
||||||
|
if [ "${USE_RUNNERSET}" != "false" ]; then
|
||||||
|
cat acceptance/testdata/runnerset.envsubst.yaml | TEST_ENTERPRISE= TEST_REPO= RUNNER_MIN_REPLICAS=${ORG_RUNNER_MIN_REPLICAS} TEST_GROUP=${TEST_ORG_GROUP} NAME=orggroup-runnerset envsubst | kubectl ${OP} -f -
|
||||||
|
else
|
||||||
|
cat acceptance/testdata/runnerdeploy.envsubst.yaml | TEST_ENTERPRISE= TEST_REPO= RUNNER_MIN_REPLICAS=${ORG_RUNNER_MIN_REPLICAS} TEST_GROUP=${TEST_ORG_GROUP} NAME=orggroup-runnerdeploy envsubst | kubectl ${OP} -f -
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Skipped ${OP} on enterprise runnerdeployment. Set TEST_ORG_GROUP to ${OP}."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Skipped ${OP} on organizational runnerdeployment. Set TEST_ORG to ${OP}."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "${TEST_ENTERPRISE}" ]; then
|
||||||
|
if [ "${USE_RUNNERSET}" != "false" ]; then
|
||||||
|
cat acceptance/testdata/runnerset.envsubst.yaml | TEST_ORG= TEST_REPO= RUNNER_MIN_REPLICAS=${ENTERPRISE_RUNNER_MIN_REPLICAS} NAME=enterprise-runnerset envsubst | kubectl ${OP} -f -
|
||||||
|
else
|
||||||
|
cat acceptance/testdata/runnerdeploy.envsubst.yaml | TEST_ORG= TEST_REPO= RUNNER_MIN_REPLICAS=${ENTERPRISE_RUNNER_MIN_REPLICAS} NAME=enterprise-runnerdeploy envsubst | kubectl ${OP} -f -
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "${TEST_ENTERPRISE_GROUP}" ]; then
|
||||||
|
if [ "${USE_RUNNERSET}" != "false" ]; then
|
||||||
|
cat acceptance/testdata/runnerset.envsubst.yaml | TEST_ORG= TEST_REPO= RUNNER_MIN_REPLICAS=${ENTERPRISE_RUNNER_MIN_REPLICAS} TEST_GROUP=${TEST_ENTERPRISE_GROUP} NAME=enterprisegroup-runnerset envsubst | kubectl ${OP} -f -
|
||||||
|
else
|
||||||
|
cat acceptance/testdata/runnerdeploy.envsubst.yaml | TEST_ORG= TEST_REPO= RUNNER_MIN_REPLICAS=${ENTERPRISE_RUNNER_MIN_REPLICAS} TEST_GROUP=${TEST_ENTERPRISE_GROUP} NAME=enterprisegroup-runnerdeploy envsubst | kubectl ${OP} -f -
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Skipped ${OP} on enterprise runnerdeployment. Set TEST_ENTERPRISE_GROUP to ${OP}."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Skipped ${OP} on enterprise runnerdeployment. Set TEST_ENTERPRISE to ${OP}."
|
||||||
|
fi
|
||||||
86
acceptance/testdata/kubernetes_container_mode.envsubst.yaml
vendored
Normal file
86
acceptance/testdata/kubernetes_container_mode.envsubst.yaml
vendored
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
# USAGE:
|
||||||
|
# cat acceptance/testdata/kubernetes_container_mode.envsubst.yaml | NAMESPACE=default envsubst | kubectl apply -f -
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
name: k8s-mode-runner
|
||||||
|
rules:
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["pods"]
|
||||||
|
verbs: ["get", "list", "create", "delete"]
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["pods/exec"]
|
||||||
|
verbs: ["get", "create"]
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["pods/log"]
|
||||||
|
verbs: ["get", "list", "watch",]
|
||||||
|
- apiGroups: ["batch"]
|
||||||
|
resources: ["jobs"]
|
||||||
|
verbs: ["get", "list", "create", "delete"]
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["secrets"]
|
||||||
|
verbs: ["get", "list", "create", "delete"]
|
||||||
|
# Needed to report test success by crating a cm from within workflow job step
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["configmaps"]
|
||||||
|
verbs: ["create", "delete"]
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
name: runner-status-updater
|
||||||
|
rules:
|
||||||
|
- apiGroups: ["actions.summerwind.dev"]
|
||||||
|
resources: ["runners/status"]
|
||||||
|
verbs: ["get", "update", "patch"]
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: ${RUNNER_SERVICE_ACCOUNT_NAME}
|
||||||
|
namespace: ${NAMESPACE}
|
||||||
|
---
|
||||||
|
# To verify it's working, try:
|
||||||
|
# kubectl auth can-i --as system:serviceaccount:default:runner get pod
|
||||||
|
# If incomplete, workflows and jobs would fail with an error message like:
|
||||||
|
# Error: Error: The Service account needs the following permissions [{"group":"","verbs":["get","list","create","delete"],"resource":"pods","subresource":""},{"group":"","verbs":["get","create"],"resource":"pods","subresource":"exec"},{"group":"","verbs":["get","list","watch"],"resource":"pods","subresource":"log"},{"group":"batch","verbs":["get","list","create","delete"],"resource":"jobs","subresource":""},{"group":"","verbs":["create","delete","get","list"],"resource":"secrets","subresource":""}] on the pod resource in the 'default' namespace. Please contact your self hosted runner administrator.
|
||||||
|
# Error: Process completed with exit code 1.
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
# This role binding allows "jane" to read pods in the "default" namespace.
|
||||||
|
# You need to already have a Role named "pod-reader" in that namespace.
|
||||||
|
kind: RoleBinding
|
||||||
|
metadata:
|
||||||
|
name: runner-k8s-mode-runner
|
||||||
|
namespace: ${NAMESPACE}
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: ${RUNNER_SERVICE_ACCOUNT_NAME}
|
||||||
|
namespace: ${NAMESPACE}
|
||||||
|
roleRef:
|
||||||
|
kind: ClusterRole
|
||||||
|
name: k8s-mode-runner
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: RoleBinding
|
||||||
|
metadata:
|
||||||
|
name: runner-runner-stat-supdater
|
||||||
|
namespace: ${NAMESPACE}
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: ${RUNNER_SERVICE_ACCOUNT_NAME}
|
||||||
|
namespace: ${NAMESPACE}
|
||||||
|
roleRef:
|
||||||
|
kind: ClusterRole
|
||||||
|
name: runner-status-updater
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
---
|
||||||
|
apiVersion: storage.k8s.io/v1
|
||||||
|
kind: StorageClass
|
||||||
|
metadata:
|
||||||
|
name: org-runnerdeploy-runner-work-dir
|
||||||
|
labels:
|
||||||
|
content: org-runnerdeploy-runner-work-dir
|
||||||
|
provisioner: rancher.io/local-path
|
||||||
|
reclaimPolicy: Delete
|
||||||
|
volumeBindingMode: WaitForFirstConsumer
|
||||||
38
acceptance/testdata/runnerdeploy.envsubst.yaml
vendored
38
acceptance/testdata/runnerdeploy.envsubst.yaml
vendored
@@ -1,3 +1,13 @@
|
|||||||
|
apiVersion: storage.k8s.io/v1
|
||||||
|
kind: StorageClass
|
||||||
|
metadata:
|
||||||
|
name: ${NAME}-runner-work-dir
|
||||||
|
labels:
|
||||||
|
content: ${NAME}-runner-work-dir
|
||||||
|
provisioner: rancher.io/local-path
|
||||||
|
reclaimPolicy: Delete
|
||||||
|
volumeBindingMode: WaitForFirstConsumer
|
||||||
|
---
|
||||||
apiVersion: actions.summerwind.dev/v1alpha1
|
apiVersion: actions.summerwind.dev/v1alpha1
|
||||||
kind: RunnerDeployment
|
kind: RunnerDeployment
|
||||||
metadata:
|
metadata:
|
||||||
@@ -19,11 +29,6 @@ spec:
|
|||||||
|
|
||||||
ephemeral: ${TEST_EPHEMERAL}
|
ephemeral: ${TEST_EPHEMERAL}
|
||||||
|
|
||||||
# Whether to pass --ephemeral (true) or --once (false, deprecated)
|
|
||||||
env:
|
|
||||||
- name: RUNNER_FEATURE_FLAG_EPHEMERAL
|
|
||||||
value: "${RUNNER_FEATURE_FLAG_EPHEMERAL}"
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# dockerd within runner container
|
# dockerd within runner container
|
||||||
#
|
#
|
||||||
@@ -44,10 +49,30 @@ spec:
|
|||||||
labels:
|
labels:
|
||||||
- "${RUNNER_LABEL}"
|
- "${RUNNER_LABEL}"
|
||||||
|
|
||||||
|
env:
|
||||||
|
- name: ROLLING_UPDATE_PHASE
|
||||||
|
value: "${ROLLING_UPDATE_PHASE}"
|
||||||
|
- name: ARC_DOCKER_MTU_PROPAGATION
|
||||||
|
value: "true"
|
||||||
|
|
||||||
|
dockerMTU: 1400
|
||||||
|
|
||||||
#
|
#
|
||||||
# Non-standard working directory
|
# Non-standard working directory
|
||||||
#
|
#
|
||||||
# workDir: "/"
|
# workDir: "/"
|
||||||
|
|
||||||
|
# # Uncomment the below to enable the kubernetes container mode
|
||||||
|
# # See https://github.com/actions-runner-controller/actions-runner-controller#runner-with-k8s-jobs
|
||||||
|
containerMode: ${RUNNER_CONTAINER_MODE}
|
||||||
|
workVolumeClaimTemplate:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
storageClassName: "${NAME}-runner-work-dir"
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 10Gi
|
||||||
|
serviceAccountName: ${RUNNER_SERVICE_ACCOUNT_NAME}
|
||||||
---
|
---
|
||||||
apiVersion: actions.summerwind.dev/v1alpha1
|
apiVersion: actions.summerwind.dev/v1alpha1
|
||||||
kind: HorizontalRunnerAutoscaler
|
kind: HorizontalRunnerAutoscaler
|
||||||
@@ -57,7 +82,8 @@ spec:
|
|||||||
scaleTargetRef:
|
scaleTargetRef:
|
||||||
name: ${NAME}
|
name: ${NAME}
|
||||||
scaleUpTriggers:
|
scaleUpTriggers:
|
||||||
- githubEvent: {}
|
- githubEvent:
|
||||||
|
workflowJob: {}
|
||||||
amount: 1
|
amount: 1
|
||||||
duration: "10m"
|
duration: "10m"
|
||||||
minReplicas: ${RUNNER_MIN_REPLICAS}
|
minReplicas: ${RUNNER_MIN_REPLICAS}
|
||||||
|
|||||||
190
acceptance/testdata/runnerset.envsubst.yaml
vendored
190
acceptance/testdata/runnerset.envsubst.yaml
vendored
@@ -1,3 +1,59 @@
|
|||||||
|
---
|
||||||
|
apiVersion: storage.k8s.io/v1
|
||||||
|
kind: StorageClass
|
||||||
|
metadata:
|
||||||
|
name: ${NAME}-runner-work-dir
|
||||||
|
labels:
|
||||||
|
content: ${NAME}-runner-work-dir
|
||||||
|
provisioner: rancher.io/local-path
|
||||||
|
reclaimPolicy: Delete
|
||||||
|
volumeBindingMode: WaitForFirstConsumer
|
||||||
|
---
|
||||||
|
apiVersion: storage.k8s.io/v1
|
||||||
|
kind: StorageClass
|
||||||
|
metadata:
|
||||||
|
name: ${NAME}
|
||||||
|
# In kind environments, the provider writes:
|
||||||
|
# /var/lib/docker/volumes/KIND_NODE_CONTAINER_VOL_ID/_data/local-path-provisioner/PV_NAME
|
||||||
|
# It can be hundreds of gigabytes depending on what you cache in the test workflow. Beware to not encounter `no space left on device` errors!
|
||||||
|
# If you did encounter no space errorrs try:
|
||||||
|
# docker system prune
|
||||||
|
# docker buildx prune #=> frees up /var/lib/docker/volumes/buildx_buildkit_container-builder0_state
|
||||||
|
# sudo rm -rf /var/lib/docker/volumes/KIND_NODE_CONTAINER_VOL_ID/_data/local-path-provisioner #=> frees up local-path-provisioner's data
|
||||||
|
provisioner: rancher.io/local-path
|
||||||
|
reclaimPolicy: Retain
|
||||||
|
volumeBindingMode: WaitForFirstConsumer
|
||||||
|
---
|
||||||
|
apiVersion: storage.k8s.io/v1
|
||||||
|
kind: StorageClass
|
||||||
|
metadata:
|
||||||
|
name: ${NAME}-var-lib-docker
|
||||||
|
labels:
|
||||||
|
content: ${NAME}-var-lib-docker
|
||||||
|
provisioner: rancher.io/local-path
|
||||||
|
reclaimPolicy: Retain
|
||||||
|
volumeBindingMode: WaitForFirstConsumer
|
||||||
|
---
|
||||||
|
apiVersion: storage.k8s.io/v1
|
||||||
|
kind: StorageClass
|
||||||
|
metadata:
|
||||||
|
name: ${NAME}-cache
|
||||||
|
labels:
|
||||||
|
content: ${NAME}-cache
|
||||||
|
provisioner: rancher.io/local-path
|
||||||
|
reclaimPolicy: Retain
|
||||||
|
volumeBindingMode: WaitForFirstConsumer
|
||||||
|
---
|
||||||
|
apiVersion: storage.k8s.io/v1
|
||||||
|
kind: StorageClass
|
||||||
|
metadata:
|
||||||
|
name: ${NAME}-runner-tool-cache
|
||||||
|
labels:
|
||||||
|
content: ${NAME}-runner-tool-cache
|
||||||
|
provisioner: rancher.io/local-path
|
||||||
|
reclaimPolicy: Retain
|
||||||
|
volumeBindingMode: WaitForFirstConsumer
|
||||||
|
---
|
||||||
apiVersion: actions.summerwind.dev/v1alpha1
|
apiVersion: actions.summerwind.dev/v1alpha1
|
||||||
kind: RunnerSet
|
kind: RunnerSet
|
||||||
metadata:
|
metadata:
|
||||||
@@ -56,14 +112,134 @@ spec:
|
|||||||
labels:
|
labels:
|
||||||
app: ${NAME}
|
app: ${NAME}
|
||||||
spec:
|
spec:
|
||||||
|
serviceAccountName: ${RUNNER_SERVICE_ACCOUNT_NAME}
|
||||||
containers:
|
containers:
|
||||||
- name: runner
|
- name: runner
|
||||||
imagePullPolicy: IfNotPresent
|
imagePullPolicy: IfNotPresent
|
||||||
env:
|
env:
|
||||||
- name: RUNNER_FEATURE_FLAG_EPHEMERAL
|
- name: RUNNER_FEATURE_FLAG_EPHEMERAL
|
||||||
value: "${RUNNER_FEATURE_FLAG_EPHEMERAL}"
|
value: "${RUNNER_FEATURE_FLAG_EPHEMERAL}"
|
||||||
#- name: docker
|
- name: GOMODCACHE
|
||||||
# #image: mumoshu/actions-runner-dind:dev
|
value: "/home/runner/.cache/go-mod"
|
||||||
|
- name: ROLLING_UPDATE_PHASE
|
||||||
|
value: "${ROLLING_UPDATE_PHASE}"
|
||||||
|
# PV-backed runner work dir
|
||||||
|
volumeMounts:
|
||||||
|
# Comment out the ephemeral work volume if you're going to test the kubernetes container mode
|
||||||
|
# The volume and mount with the same names will be created by workVolumeClaimTemplate and the kubernetes container mode support.
|
||||||
|
# - name: work
|
||||||
|
# mountPath: /runner/_work
|
||||||
|
# Cache docker image layers, in case dockerdWithinRunnerContainer=true
|
||||||
|
- name: var-lib-docker
|
||||||
|
mountPath: /var/lib/docker
|
||||||
|
# Cache go modules and builds
|
||||||
|
# - name: gocache
|
||||||
|
# # Run `goenv | grep GOCACHE` to verify the path is correct for your env
|
||||||
|
# mountPath: /home/runner/.cache/go-build
|
||||||
|
# - name: gomodcache
|
||||||
|
# # Run `goenv | grep GOMODCACHE` to verify the path is correct for your env
|
||||||
|
# # mountPath: /home/runner/go/pkg/mod
|
||||||
|
- name: cache
|
||||||
|
# go: could not create module cache: stat /home/runner/.cache/go-mod: permission denied
|
||||||
|
mountPath: "/home/runner/.cache"
|
||||||
|
- name: runner-tool-cache
|
||||||
|
# This corresponds to our runner image's default setting of RUNNER_TOOL_CACHE=/opt/hostedtoolcache.
|
||||||
|
#
|
||||||
|
# In case you customize the envvar in both runner and docker containers of the runner pod spec,
|
||||||
|
# You'd need to change this mountPath accordingly.
|
||||||
|
#
|
||||||
|
# The tool cache directory is defined in actions/toolkit's tool-cache module:
|
||||||
|
# https://github.com/actions/toolkit/blob/2f164000dcd42fb08287824a3bc3030dbed33687/packages/tool-cache/src/tool-cache.ts#L621-L638
|
||||||
|
#
|
||||||
|
# Many setup-* actions like setup-go utilizes the tool-cache module to download and cache installed binaries:
|
||||||
|
# https://github.com/actions/setup-go/blob/56a61c9834b4a4950dbbf4740af0b8a98c73b768/src/installer.ts#L144
|
||||||
|
mountPath: "/opt/hostedtoolcache"
|
||||||
|
# Valid only when dockerdWithinRunnerContainer=false
|
||||||
|
# - name: docker
|
||||||
|
# # PV-backed runner work dir
|
||||||
|
# volumeMounts:
|
||||||
|
# - name: work
|
||||||
|
# mountPath: /runner/_work
|
||||||
|
# # Cache docker image layers, in case dockerdWithinRunnerContainer=false
|
||||||
|
# - name: var-lib-docker
|
||||||
|
# mountPath: /var/lib/docker
|
||||||
|
# # image: mumoshu/actions-runner-dind:dev
|
||||||
|
|
||||||
|
# # For buildx cache
|
||||||
|
# - name: cache
|
||||||
|
# mountPath: "/home/runner/.cache"
|
||||||
|
# Comment out the ephemeral work volume if you're going to test the kubernetes container mode
|
||||||
|
# volumes:
|
||||||
|
# - name: work
|
||||||
|
# ephemeral:
|
||||||
|
# volumeClaimTemplate:
|
||||||
|
# spec:
|
||||||
|
# accessModes:
|
||||||
|
# - ReadWriteOnce
|
||||||
|
# storageClassName: "${NAME}-runner-work-dir"
|
||||||
|
# resources:
|
||||||
|
# requests:
|
||||||
|
# storage: 10Gi
|
||||||
|
volumeClaimTemplates:
|
||||||
|
- metadata:
|
||||||
|
name: vol1
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 10Mi
|
||||||
|
storageClassName: ${NAME}
|
||||||
|
## Dunno which provider supports auto-provisioning with selector.
|
||||||
|
## At least the rancher local path provider stopped with:
|
||||||
|
## waiting for a volume to be created, either by external provisioner "rancher.io/local-path" or manually created by system administrator
|
||||||
|
# selector:
|
||||||
|
# matchLabels:
|
||||||
|
# runnerset-volume-id: ${NAME}-vol1
|
||||||
|
- metadata:
|
||||||
|
name: vol2
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 10Mi
|
||||||
|
storageClassName: ${NAME}
|
||||||
|
# selector:
|
||||||
|
# matchLabels:
|
||||||
|
# runnerset-volume-id: ${NAME}-vol2
|
||||||
|
- metadata:
|
||||||
|
name: var-lib-docker
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 10Mi
|
||||||
|
storageClassName: ${NAME}-var-lib-docker
|
||||||
|
- metadata:
|
||||||
|
name: cache
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 10Mi
|
||||||
|
storageClassName: ${NAME}-cache
|
||||||
|
- metadata:
|
||||||
|
name: runner-tool-cache
|
||||||
|
# It turns out labels doesn't distinguish PVs across PVCs and the
|
||||||
|
# end result is PVs are reused by wrong PVCs.
|
||||||
|
# The correct way seems to be to differentiate storage class per pvc template.
|
||||||
|
# labels:
|
||||||
|
# id: runner-tool-cache
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 10Mi
|
||||||
|
storageClassName: ${NAME}-runner-tool-cache
|
||||||
---
|
---
|
||||||
apiVersion: actions.summerwind.dev/v1alpha1
|
apiVersion: actions.summerwind.dev/v1alpha1
|
||||||
kind: HorizontalRunnerAutoscaler
|
kind: HorizontalRunnerAutoscaler
|
||||||
@@ -74,9 +250,17 @@ spec:
|
|||||||
kind: RunnerSet
|
kind: RunnerSet
|
||||||
name: ${NAME}
|
name: ${NAME}
|
||||||
scaleUpTriggers:
|
scaleUpTriggers:
|
||||||
- githubEvent: {}
|
- githubEvent:
|
||||||
|
workflowJob: {}
|
||||||
amount: 1
|
amount: 1
|
||||||
duration: "10m"
|
duration: "10m"
|
||||||
minReplicas: ${RUNNER_MIN_REPLICAS}
|
minReplicas: ${RUNNER_MIN_REPLICAS}
|
||||||
maxReplicas: 10
|
maxReplicas: 10
|
||||||
scaleDownDelaySecondsAfterScaleOut: ${RUNNER_SCALE_DOWN_DELAY_SECONDS_AFTER_SCALE_OUT}
|
scaleDownDelaySecondsAfterScaleOut: ${RUNNER_SCALE_DOWN_DELAY_SECONDS_AFTER_SCALE_OUT}
|
||||||
|
# Comment out the whole metrics if you'd like to solely test webhook-based scaling
|
||||||
|
metrics:
|
||||||
|
- type: PercentageRunnersBusy
|
||||||
|
scaleUpThreshold: '0.75'
|
||||||
|
scaleDownThreshold: '0.25'
|
||||||
|
scaleUpFactor: '2'
|
||||||
|
scaleDownFactor: '0.5'
|
||||||
|
|||||||
@@ -1,6 +1,18 @@
|
|||||||
# Set actions-runner-controller settings for testing
|
# Set actions-runner-controller settings for testing
|
||||||
logLevel: "-4"
|
logLevel: "-4"
|
||||||
|
imagePullSecrets: []
|
||||||
|
image:
|
||||||
|
# This needs to be an empty array rather than a single-item array with empty name.
|
||||||
|
# Otherwise you end up with the following error on helm-upgrade:
|
||||||
|
# Error: UPGRADE FAILED: failed to create patch: map: map[] does not contain declared merge key: name && failed to create patch: map: map[] does not contain declared merge key: name
|
||||||
|
actionsRunnerImagePullSecrets: []
|
||||||
|
runner:
|
||||||
|
statusUpdateHook:
|
||||||
|
enabled: true
|
||||||
|
rbac:
|
||||||
|
allowGrantingKubernetesContainerModePermissions: true
|
||||||
githubWebhookServer:
|
githubWebhookServer:
|
||||||
|
imagePullSecrets: []
|
||||||
logLevel: "-4"
|
logLevel: "-4"
|
||||||
enabled: true
|
enabled: true
|
||||||
labels: {}
|
labels: {}
|
||||||
|
|||||||
18
adrs/0000-TEMPLATE.md
Normal file
18
adrs/0000-TEMPLATE.md
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Title
|
||||||
|
|
||||||
|
<!-- ADR titles should typically be imperative sentences. -->
|
||||||
|
|
||||||
|
**Status**: (Proposed|Accepted|Rejected|Superceded|Deprecated)
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
*What is the issue or background knowledge necessary for future readers
|
||||||
|
to understand why this ADR was written?*
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
**What** is the change being proposed? / **How** will it be implemented?*
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
*What becomes easier or more difficult to do because of this change?*
|
||||||
@@ -60,6 +60,9 @@ type HorizontalRunnerAutoscalerSpec struct {
|
|||||||
// The earlier a scheduled override is, the higher it is prioritized.
|
// The earlier a scheduled override is, the higher it is prioritized.
|
||||||
// +optional
|
// +optional
|
||||||
ScheduledOverrides []ScheduledOverride `json:"scheduledOverrides,omitempty"`
|
ScheduledOverrides []ScheduledOverride `json:"scheduledOverrides,omitempty"`
|
||||||
|
|
||||||
|
// +optional
|
||||||
|
GitHubAPICredentialsFrom *GitHubAPICredentialsFrom `json:"githubAPICredentialsFrom,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ScaleUpTrigger struct {
|
type ScaleUpTrigger struct {
|
||||||
@@ -130,7 +133,7 @@ type ScaleTargetRef struct {
|
|||||||
|
|
||||||
type MetricSpec struct {
|
type MetricSpec struct {
|
||||||
// Type is the type of metric to be used for autoscaling.
|
// Type is the type of metric to be used for autoscaling.
|
||||||
// The only supported Type is TotalNumberOfQueuedAndInProgressWorkflowRuns
|
// It can be TotalNumberOfQueuedAndInProgressWorkflowRuns or PercentageRunnersBusy.
|
||||||
Type string `json:"type,omitempty"`
|
Type string `json:"type,omitempty"`
|
||||||
|
|
||||||
// RepositoryNames is the list of repository names to be used for calculating the metric.
|
// RepositoryNames is the list of repository names to be used for calculating the metric.
|
||||||
@@ -170,7 +173,7 @@ type MetricSpec struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ScheduledOverride can be used to override a few fields of HorizontalRunnerAutoscalerSpec on schedule.
|
// ScheduledOverride can be used to override a few fields of HorizontalRunnerAutoscalerSpec on schedule.
|
||||||
// A schedule can optionally be recurring, so that the correspoding override happens every day, week, month, or year.
|
// A schedule can optionally be recurring, so that the corresponding override happens every day, week, month, or year.
|
||||||
type ScheduledOverride struct {
|
type ScheduledOverride struct {
|
||||||
// StartTime is the time at which the first override starts.
|
// StartTime is the time at which the first override starts.
|
||||||
StartTime metav1.Time `json:"startTime"`
|
StartTime metav1.Time `json:"startTime"`
|
||||||
|
|||||||
@@ -18,8 +18,10 @@ package v1alpha1
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/api/resource"
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
|
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
@@ -71,6 +73,19 @@ type RunnerConfig struct {
|
|||||||
VolumeSizeLimit *resource.Quantity `json:"volumeSizeLimit,omitempty"`
|
VolumeSizeLimit *resource.Quantity `json:"volumeSizeLimit,omitempty"`
|
||||||
// +optional
|
// +optional
|
||||||
VolumeStorageMedium *string `json:"volumeStorageMedium,omitempty"`
|
VolumeStorageMedium *string `json:"volumeStorageMedium,omitempty"`
|
||||||
|
|
||||||
|
// +optional
|
||||||
|
ContainerMode string `json:"containerMode,omitempty"`
|
||||||
|
|
||||||
|
GitHubAPICredentialsFrom *GitHubAPICredentialsFrom `json:"githubAPICredentialsFrom,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GitHubAPICredentialsFrom struct {
|
||||||
|
SecretRef SecretReference `json:"secretRef,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SecretReference struct {
|
||||||
|
Name string `json:"name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunnerPodSpec defines the desired pod spec fields of the runner pod
|
// RunnerPodSpec defines the desired pod spec fields of the runner pod
|
||||||
@@ -135,6 +150,9 @@ type RunnerPodSpec struct {
|
|||||||
// +optional
|
// +optional
|
||||||
Tolerations []corev1.Toleration `json:"tolerations,omitempty"`
|
Tolerations []corev1.Toleration `json:"tolerations,omitempty"`
|
||||||
|
|
||||||
|
// +optional
|
||||||
|
PriorityClassName string `json:"priorityClassName,omitempty"`
|
||||||
|
|
||||||
// +optional
|
// +optional
|
||||||
TerminationGracePeriodSeconds *int64 `json:"terminationGracePeriodSeconds,omitempty"`
|
TerminationGracePeriodSeconds *int64 `json:"terminationGracePeriodSeconds,omitempty"`
|
||||||
|
|
||||||
@@ -152,12 +170,37 @@ type RunnerPodSpec struct {
|
|||||||
// +optional
|
// +optional
|
||||||
RuntimeClassName *string `json:"runtimeClassName,omitempty"`
|
RuntimeClassName *string `json:"runtimeClassName,omitempty"`
|
||||||
|
|
||||||
|
// +optional
|
||||||
|
DnsPolicy corev1.DNSPolicy `json:"dnsPolicy,omitempty"`
|
||||||
|
|
||||||
// +optional
|
// +optional
|
||||||
DnsConfig *corev1.PodDNSConfig `json:"dnsConfig,omitempty"`
|
DnsConfig *corev1.PodDNSConfig `json:"dnsConfig,omitempty"`
|
||||||
|
|
||||||
|
// +optional
|
||||||
|
WorkVolumeClaimTemplate *WorkVolumeClaimTemplate `json:"workVolumeClaimTemplate,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rs *RunnerSpec) Validate(rootPath *field.Path) field.ErrorList {
|
||||||
|
var (
|
||||||
|
errList field.ErrorList
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
err = rs.validateRepository()
|
||||||
|
if err != nil {
|
||||||
|
errList = append(errList, field.Invalid(rootPath.Child("repository"), rs.Repository, err.Error()))
|
||||||
|
}
|
||||||
|
|
||||||
|
err = rs.validateWorkVolumeClaimTemplate()
|
||||||
|
if err != nil {
|
||||||
|
errList = append(errList, field.Invalid(rootPath.Child("workVolumeClaimTemplate"), rs.WorkVolumeClaimTemplate, err.Error()))
|
||||||
|
}
|
||||||
|
|
||||||
|
return errList
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateRepository validates repository field.
|
// ValidateRepository validates repository field.
|
||||||
func (rs *RunnerSpec) ValidateRepository() error {
|
func (rs *RunnerSpec) validateRepository() error {
|
||||||
// Enterprise, Organization and repository are both exclusive.
|
// Enterprise, Organization and repository are both exclusive.
|
||||||
foundCount := 0
|
foundCount := 0
|
||||||
if len(rs.Organization) > 0 {
|
if len(rs.Organization) > 0 {
|
||||||
@@ -179,6 +222,18 @@ func (rs *RunnerSpec) ValidateRepository() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rs *RunnerSpec) validateWorkVolumeClaimTemplate() error {
|
||||||
|
if rs.ContainerMode != "kubernetes" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if rs.WorkVolumeClaimTemplate == nil {
|
||||||
|
return errors.New("Spec.ContainerMode: kubernetes must have workVolumeClaimTemplate field specified")
|
||||||
|
}
|
||||||
|
|
||||||
|
return rs.WorkVolumeClaimTemplate.validate()
|
||||||
|
}
|
||||||
|
|
||||||
// RunnerStatus defines the observed state of Runner
|
// RunnerStatus defines the observed state of Runner
|
||||||
type RunnerStatus struct {
|
type RunnerStatus struct {
|
||||||
// Turns true only if the runner pod is ready.
|
// Turns true only if the runner pod is ready.
|
||||||
@@ -207,13 +262,60 @@ type RunnerStatusRegistration struct {
|
|||||||
ExpiresAt metav1.Time `json:"expiresAt"`
|
ExpiresAt metav1.Time `json:"expiresAt"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type WorkVolumeClaimTemplate struct {
|
||||||
|
StorageClassName string `json:"storageClassName"`
|
||||||
|
AccessModes []corev1.PersistentVolumeAccessMode `json:"accessModes"`
|
||||||
|
Resources corev1.ResourceRequirements `json:"resources"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WorkVolumeClaimTemplate) validate() error {
|
||||||
|
if w.AccessModes == nil || len(w.AccessModes) == 0 {
|
||||||
|
return errors.New("Access mode should have at least one mode specified")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, accessMode := range w.AccessModes {
|
||||||
|
switch accessMode {
|
||||||
|
case corev1.ReadWriteOnce, corev1.ReadWriteMany:
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("Access mode %v is not supported", accessMode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WorkVolumeClaimTemplate) V1Volume() corev1.Volume {
|
||||||
|
return corev1.Volume{
|
||||||
|
Name: "work",
|
||||||
|
VolumeSource: corev1.VolumeSource{
|
||||||
|
Ephemeral: &corev1.EphemeralVolumeSource{
|
||||||
|
VolumeClaimTemplate: &corev1.PersistentVolumeClaimTemplate{
|
||||||
|
Spec: corev1.PersistentVolumeClaimSpec{
|
||||||
|
AccessModes: w.AccessModes,
|
||||||
|
StorageClassName: &w.StorageClassName,
|
||||||
|
Resources: w.Resources,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WorkVolumeClaimTemplate) V1VolumeMount(mountPath string) corev1.VolumeMount {
|
||||||
|
return corev1.VolumeMount{
|
||||||
|
MountPath: mountPath,
|
||||||
|
Name: "work",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// +kubebuilder:object:root=true
|
// +kubebuilder:object:root=true
|
||||||
// +kubebuilder:subresource:status
|
// +kubebuilder:subresource:status
|
||||||
// +kubebuilder:printcolumn:JSONPath=".spec.enterprise",name=Enterprise,type=string
|
// +kubebuilder:printcolumn:JSONPath=".spec.enterprise",name=Enterprise,type=string
|
||||||
// +kubebuilder:printcolumn:JSONPath=".spec.organization",name=Organization,type=string
|
// +kubebuilder:printcolumn:JSONPath=".spec.organization",name=Organization,type=string
|
||||||
// +kubebuilder:printcolumn:JSONPath=".spec.repository",name=Repository,type=string
|
// +kubebuilder:printcolumn:JSONPath=".spec.repository",name=Repository,type=string
|
||||||
|
// +kubebuilder:printcolumn:JSONPath=".spec.group",name=Group,type=string
|
||||||
// +kubebuilder:printcolumn:JSONPath=".spec.labels",name=Labels,type=string
|
// +kubebuilder:printcolumn:JSONPath=".spec.labels",name=Labels,type=string
|
||||||
// +kubebuilder:printcolumn:JSONPath=".status.phase",name=Status,type=string
|
// +kubebuilder:printcolumn:JSONPath=".status.phase",name=Status,type=string
|
||||||
|
// +kubebuilder:printcolumn:JSONPath=".status.message",name=Message,type=string
|
||||||
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
|
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
|
||||||
|
|
||||||
// Runner is the Schema for the runners API
|
// Runner is the Schema for the runners API
|
||||||
@@ -235,11 +337,7 @@ func (r Runner) IsRegisterable() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
now := metav1.Now()
|
now := metav1.Now()
|
||||||
if r.Status.Registration.ExpiresAt.Before(&now) {
|
return !r.Status.Registration.ExpiresAt.Before(&now)
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// +kubebuilder:object:root=true
|
// +kubebuilder:object:root=true
|
||||||
|
|||||||
@@ -66,15 +66,7 @@ func (r *Runner) ValidateDelete() error {
|
|||||||
|
|
||||||
// Validate validates resource spec.
|
// Validate validates resource spec.
|
||||||
func (r *Runner) Validate() error {
|
func (r *Runner) Validate() error {
|
||||||
var (
|
errList := r.Spec.Validate(field.NewPath("spec"))
|
||||||
errList field.ErrorList
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
err = r.Spec.ValidateRepository()
|
|
||||||
if err != nil {
|
|
||||||
errList = append(errList, field.Invalid(field.NewPath("spec", "repository"), r.Spec.Repository, err.Error()))
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(errList) > 0 {
|
if len(errList) > 0 {
|
||||||
return apierrors.NewInvalid(r.GroupVersionKind().GroupKind(), r.Name, errList)
|
return apierrors.NewInvalid(r.GroupVersionKind().GroupKind(), r.Name, errList)
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ type RunnerDeploymentSpec struct {
|
|||||||
|
|
||||||
// EffectiveTime is the time the upstream controller requested to sync Replicas.
|
// EffectiveTime is the time the upstream controller requested to sync Replicas.
|
||||||
// It is usually populated by the webhook-based autoscaler via HRA.
|
// It is usually populated by the webhook-based autoscaler via HRA.
|
||||||
// The value is inherited to RunnerRepicaSet(s) and used to prevent ephemeral runners from unnecessarily recreated.
|
// The value is inherited to RunnerReplicaSet(s) and used to prevent ephemeral runners from unnecessarily recreated.
|
||||||
//
|
//
|
||||||
// +optional
|
// +optional
|
||||||
// +nullable
|
// +nullable
|
||||||
|
|||||||
@@ -66,15 +66,7 @@ func (r *RunnerDeployment) ValidateDelete() error {
|
|||||||
|
|
||||||
// Validate validates resource spec.
|
// Validate validates resource spec.
|
||||||
func (r *RunnerDeployment) Validate() error {
|
func (r *RunnerDeployment) Validate() error {
|
||||||
var (
|
errList := r.Spec.Template.Spec.Validate(field.NewPath("spec", "template", "spec"))
|
||||||
errList field.ErrorList
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
err = r.Spec.Template.Spec.ValidateRepository()
|
|
||||||
if err != nil {
|
|
||||||
errList = append(errList, field.Invalid(field.NewPath("spec", "template", "spec", "repository"), r.Spec.Template.Spec.Repository, err.Error()))
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(errList) > 0 {
|
if len(errList) > 0 {
|
||||||
return apierrors.NewInvalid(r.GroupVersionKind().GroupKind(), r.Name, errList)
|
return apierrors.NewInvalid(r.GroupVersionKind().GroupKind(), r.Name, errList)
|
||||||
|
|||||||
@@ -66,15 +66,7 @@ func (r *RunnerReplicaSet) ValidateDelete() error {
|
|||||||
|
|
||||||
// Validate validates resource spec.
|
// Validate validates resource spec.
|
||||||
func (r *RunnerReplicaSet) Validate() error {
|
func (r *RunnerReplicaSet) Validate() error {
|
||||||
var (
|
errList := r.Spec.Template.Spec.Validate(field.NewPath("spec", "template", "spec"))
|
||||||
errList field.ErrorList
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
err = r.Spec.Template.Spec.ValidateRepository()
|
|
||||||
if err != nil {
|
|
||||||
errList = append(errList, field.Invalid(field.NewPath("spec", "template", "spec", "repository"), r.Spec.Template.Spec.Repository, err.Error()))
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(errList) > 0 {
|
if len(errList) > 0 {
|
||||||
return apierrors.NewInvalid(r.GroupVersionKind().GroupKind(), r.Name, errList)
|
return apierrors.NewInvalid(r.GroupVersionKind().GroupKind(), r.Name, errList)
|
||||||
|
|||||||
@@ -33,6 +33,12 @@ type RunnerSetSpec struct {
|
|||||||
// +nullable
|
// +nullable
|
||||||
EffectiveTime *metav1.Time `json:"effectiveTime,omitempty"`
|
EffectiveTime *metav1.Time `json:"effectiveTime,omitempty"`
|
||||||
|
|
||||||
|
// +optional
|
||||||
|
ServiceAccountName string `json:"serviceAccountName,omitempty"`
|
||||||
|
|
||||||
|
// +optional
|
||||||
|
WorkVolumeClaimTemplate *WorkVolumeClaimTemplate `json:"workVolumeClaimTemplate,omitempty"`
|
||||||
|
|
||||||
appsv1.StatefulSetSpec `json:",inline"`
|
appsv1.StatefulSetSpec `json:",inline"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -90,6 +90,22 @@ func (in *CheckRunSpec) DeepCopy() *CheckRunSpec {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *GitHubAPICredentialsFrom) DeepCopyInto(out *GitHubAPICredentialsFrom) {
|
||||||
|
*out = *in
|
||||||
|
out.SecretRef = in.SecretRef
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GitHubAPICredentialsFrom.
|
||||||
|
func (in *GitHubAPICredentialsFrom) DeepCopy() *GitHubAPICredentialsFrom {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(GitHubAPICredentialsFrom)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *GitHubEventScaleUpTriggerSpec) DeepCopyInto(out *GitHubEventScaleUpTriggerSpec) {
|
func (in *GitHubEventScaleUpTriggerSpec) DeepCopyInto(out *GitHubEventScaleUpTriggerSpec) {
|
||||||
*out = *in
|
*out = *in
|
||||||
@@ -231,6 +247,11 @@ func (in *HorizontalRunnerAutoscalerSpec) DeepCopyInto(out *HorizontalRunnerAuto
|
|||||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if in.GitHubAPICredentialsFrom != nil {
|
||||||
|
in, out := &in.GitHubAPICredentialsFrom, &out.GitHubAPICredentialsFrom
|
||||||
|
*out = new(GitHubAPICredentialsFrom)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HorizontalRunnerAutoscalerSpec.
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HorizontalRunnerAutoscalerSpec.
|
||||||
@@ -425,6 +446,11 @@ func (in *RunnerConfig) DeepCopyInto(out *RunnerConfig) {
|
|||||||
*out = new(string)
|
*out = new(string)
|
||||||
**out = **in
|
**out = **in
|
||||||
}
|
}
|
||||||
|
if in.GitHubAPICredentialsFrom != nil {
|
||||||
|
in, out := &in.GitHubAPICredentialsFrom, &out.GitHubAPICredentialsFrom
|
||||||
|
*out = new(GitHubAPICredentialsFrom)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RunnerConfig.
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RunnerConfig.
|
||||||
@@ -741,6 +767,11 @@ func (in *RunnerPodSpec) DeepCopyInto(out *RunnerPodSpec) {
|
|||||||
*out = new(v1.PodDNSConfig)
|
*out = new(v1.PodDNSConfig)
|
||||||
(*in).DeepCopyInto(*out)
|
(*in).DeepCopyInto(*out)
|
||||||
}
|
}
|
||||||
|
if in.WorkVolumeClaimTemplate != nil {
|
||||||
|
in, out := &in.WorkVolumeClaimTemplate, &out.WorkVolumeClaimTemplate
|
||||||
|
*out = new(WorkVolumeClaimTemplate)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RunnerPodSpec.
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RunnerPodSpec.
|
||||||
@@ -939,6 +970,11 @@ func (in *RunnerSetSpec) DeepCopyInto(out *RunnerSetSpec) {
|
|||||||
in, out := &in.EffectiveTime, &out.EffectiveTime
|
in, out := &in.EffectiveTime, &out.EffectiveTime
|
||||||
*out = (*in).DeepCopy()
|
*out = (*in).DeepCopy()
|
||||||
}
|
}
|
||||||
|
if in.WorkVolumeClaimTemplate != nil {
|
||||||
|
in, out := &in.WorkVolumeClaimTemplate, &out.WorkVolumeClaimTemplate
|
||||||
|
*out = new(WorkVolumeClaimTemplate)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
in.StatefulSetSpec.DeepCopyInto(&out.StatefulSetSpec)
|
in.StatefulSetSpec.DeepCopyInto(&out.StatefulSetSpec)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1126,6 +1162,42 @@ func (in *ScheduledOverride) DeepCopy() *ScheduledOverride {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *SecretReference) DeepCopyInto(out *SecretReference) {
|
||||||
|
*out = *in
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretReference.
|
||||||
|
func (in *SecretReference) DeepCopy() *SecretReference {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(SecretReference)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *WorkVolumeClaimTemplate) DeepCopyInto(out *WorkVolumeClaimTemplate) {
|
||||||
|
*out = *in
|
||||||
|
if in.AccessModes != nil {
|
||||||
|
in, out := &in.AccessModes, &out.AccessModes
|
||||||
|
*out = make([]v1.PersistentVolumeAccessMode, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
|
in.Resources.DeepCopyInto(&out.Resources)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkVolumeClaimTemplate.
|
||||||
|
func (in *WorkVolumeClaimTemplate) DeepCopy() *WorkVolumeClaimTemplate {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(WorkVolumeClaimTemplate)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *WorkflowJobSpec) DeepCopyInto(out *WorkflowJobSpec) {
|
func (in *WorkflowJobSpec) DeepCopyInto(out *WorkflowJobSpec) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
|||||||
4
build/version.go
Normal file
4
build/version.go
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
package build
|
||||||
|
|
||||||
|
// This is overridden at build-time using go-build ldflags. dev is the fallback value
|
||||||
|
var Version = "NA"
|
||||||
@@ -15,10 +15,10 @@ type: application
|
|||||||
# This is the chart version. This version number should be incremented each time you make changes
|
# This is the chart version. This version number should be incremented each time you make changes
|
||||||
# to the chart and its templates, including the app version.
|
# to the chart and its templates, including the app version.
|
||||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||||
version: 0.18.0
|
version: 0.21.1
|
||||||
|
|
||||||
# Used as the default manager tag value when no tag property is provided in the values.yaml
|
# Used as the default manager tag value when no tag property is provided in the values.yaml
|
||||||
appVersion: 0.23.0
|
appVersion: 0.26.0
|
||||||
|
|
||||||
home: https://github.com/actions-runner-controller/actions-runner-controller
|
home: https://github.com/actions-runner-controller/actions-runner-controller
|
||||||
|
|
||||||
|
|||||||
@@ -8,103 +8,105 @@ All additional docs are kept in the `docs/` folder, this README is solely for do
|
|||||||
|
|
||||||
> _Default values are the defaults set in the charts `values.yaml`, some properties have default configurations in the code for when the property is omitted or invalid_
|
> _Default values are the defaults set in the charts `values.yaml`, some properties have default configurations in the code for when the property is omitted or invalid_
|
||||||
|
|
||||||
| Key | Description | Default |
|
| Key | Description | Default |
|
||||||
|----------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------|
|
|----------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------|
|
||||||
| `labels` | Set labels to apply to all resources in the chart | |
|
| `labels` | Set labels to apply to all resources in the chart | |
|
||||||
| `replicaCount` | Set the number of controller pods | 1 |
|
| `replicaCount` | Set the number of controller pods | 1 |
|
||||||
| `syncPeriod` | Set the period in which the controler reconciles the desired runners count | 10m |
|
| `webhookPort` | Set the containerPort for the webhook Pod | 9443 |
|
||||||
| `enableLeaderElection` | Enable election configuration | true |
|
| `syncPeriod` | Set the period in which the controller reconciles the desired runners count | 1m |
|
||||||
| `leaderElectionId` | Set the election ID for the controller group | |
|
| `enableLeaderElection` | Enable election configuration | true |
|
||||||
| `githubEnterpriseServerURL` | Set the URL for a self-hosted GitHub Enterprise Server | |
|
| `leaderElectionId` | Set the election ID for the controller group | |
|
||||||
| `githubURL` | Override GitHub URL to be used for GitHub API calls | |
|
| `githubEnterpriseServerURL` | Set the URL for a self-hosted GitHub Enterprise Server | |
|
||||||
| `githubUploadURL` | Override GitHub Upload URL to be used for GitHub API calls | |
|
| `githubURL` | Override GitHub URL to be used for GitHub API calls | |
|
||||||
| `runnerGithubURL` | Override GitHub URL to be used by runners during registration | |
|
| `githubUploadURL` | Override GitHub Upload URL to be used for GitHub API calls | |
|
||||||
| `logLevel` | Set the log level of the controller container | |
|
| `runnerGithubURL` | Override GitHub URL to be used by runners during registration | |
|
||||||
| `additionalVolumes` | Set additional volumes to add to the manager container | |
|
| `logLevel` | Set the log level of the controller container | |
|
||||||
| `additionalVolumeMounts` | Set additional volume mounts to add to the manager container | |
|
| `additionalVolumes` | Set additional volumes to add to the manager container | |
|
||||||
| `authSecret.create` | Deploy the controller auth secret | false |
|
| `additionalVolumeMounts` | Set additional volume mounts to add to the manager container | |
|
||||||
| `authSecret.name` | Set the name of the auth secret | controller-manager |
|
| `authSecret.create` | Deploy the controller auth secret | false |
|
||||||
| `authSecret.annotations` | Set annotations for the auth Secret | |
|
| `authSecret.name` | Set the name of the auth secret | controller-manager |
|
||||||
| `authSecret.github_app_id` | The ID of your GitHub App. **This can't be set at the same time as `authSecret.github_token`** | |
|
| `authSecret.annotations` | Set annotations for the auth Secret | |
|
||||||
| `authSecret.github_app_installation_id` | The ID of your GitHub App installation. **This can't be set at the same time as `authSecret.github_token`** | |
|
| `authSecret.github_app_id` | The ID of your GitHub App. **This can't be set at the same time as `authSecret.github_token`** | |
|
||||||
| `authSecret.github_app_private_key` | The multiline string of your GitHub App's private key. **This can't be set at the same time as `authSecret.github_token`** | |
|
| `authSecret.github_app_installation_id` | The ID of your GitHub App installation. **This can't be set at the same time as `authSecret.github_token`** | |
|
||||||
| `authSecret.github_token` | Your chosen GitHub PAT token. **This can't be set at the same time as the `authSecret.github_app_*`** | |
|
| `authSecret.github_app_private_key` | The multiline string of your GitHub App's private key. **This can't be set at the same time as `authSecret.github_token`** | |
|
||||||
| `authSecret.github_basicauth_username` | Username for GitHub basic auth to use instead of PAT or GitHub APP in case it's running behind a proxy API | |
|
| `authSecret.github_token` | Your chosen GitHub PAT token. **This can't be set at the same time as the `authSecret.github_app_*`** | |
|
||||||
| `authSecret.github_basicauth_password` | Password for GitHub basic auth to use instead of PAT or GitHub APP in case it's running behind a proxy API | |
|
| `authSecret.github_basicauth_username` | Username for GitHub basic auth to use instead of PAT or GitHub APP in case it's running behind a proxy API | |
|
||||||
| `dockerRegistryMirror` | The default Docker Registry Mirror used by runners. | |
|
| `authSecret.github_basicauth_password` | Password for GitHub basic auth to use instead of PAT or GitHub APP in case it's running behind a proxy API | |
|
||||||
| `hostNetwork` | The "hostNetwork" of the controller container | false |
|
| `dockerRegistryMirror` | The default Docker Registry Mirror used by runners. | |
|
||||||
| `image.repository` | The "repository/image" of the controller container | summerwind/actions-runner-controller |
|
| `hostNetwork` | The "hostNetwork" of the controller container | false |
|
||||||
| `image.tag` | The tag of the controller container | |
|
| `image.repository` | The "repository/image" of the controller container | summerwind/actions-runner-controller |
|
||||||
| `image.actionsRunnerRepositoryAndTag` | The "repository/image" of the actions runner container | summerwind/actions-runner:latest |
|
| `image.tag` | The tag of the controller container | |
|
||||||
| `image.actionsRunnerImagePullSecrets` | Optional image pull secrets to be included in the runner pod's ImagePullSecrets | |
|
| `image.actionsRunnerRepositoryAndTag` | The "repository/image" of the actions runner container | summerwind/actions-runner:latest |
|
||||||
| `image.dindSidecarRepositoryAndTag` | The "repository/image" of the dind sidecar container | docker:dind |
|
| `image.actionsRunnerImagePullSecrets` | Optional image pull secrets to be included in the runner pod's ImagePullSecrets | |
|
||||||
| `image.pullPolicy` | The pull policy of the controller image | IfNotPresent |
|
| `image.dindSidecarRepositoryAndTag` | The "repository/image" of the dind sidecar container | docker:dind |
|
||||||
| `metrics.serviceMonitor` | Deploy serviceMonitor kind for for use with prometheus-operator CRDs | false |
|
| `image.pullPolicy` | The pull policy of the controller image | IfNotPresent |
|
||||||
| `metrics.serviceAnnotations` | Set annotations for the provisioned metrics service resource | |
|
| `metrics.serviceMonitor` | Deploy serviceMonitor kind for for use with prometheus-operator CRDs | false |
|
||||||
| `metrics.port` | Set port of metrics service | 8443 |
|
| `metrics.serviceAnnotations` | Set annotations for the provisioned metrics service resource | |
|
||||||
| `metrics.proxy.enabled` | Deploy kube-rbac-proxy container in controller pod | true |
|
| `metrics.port` | Set port of metrics service | 8443 |
|
||||||
| `metrics.proxy.image.repository` | The "repository/image" of the kube-proxy container | quay.io/brancz/kube-rbac-proxy |
|
| `metrics.proxy.enabled` | Deploy kube-rbac-proxy container in controller pod | true |
|
||||||
| `metrics.proxy.image.tag` | The tag of the kube-proxy image to use when pulling the container | v0.10.0 |
|
| `metrics.proxy.image.repository` | The "repository/image" of the kube-proxy container | quay.io/brancz/kube-rbac-proxy |
|
||||||
| `metrics.serviceMonitorLabels` | Set labels to apply to ServiceMonitor resources | |
|
| `metrics.proxy.image.tag` | The tag of the kube-proxy image to use when pulling the container | v0.10.0 |
|
||||||
| `imagePullSecrets` | Specifies the secret to be used when pulling the controller pod containers | |
|
| `metrics.serviceMonitorLabels` | Set labels to apply to ServiceMonitor resources | |
|
||||||
| `fullnameOverride` | Override the full resource names | |
|
| `imagePullSecrets` | Specifies the secret to be used when pulling the controller pod containers | |
|
||||||
| `nameOverride` | Override the resource name prefix | |
|
| `fullnameOverride` | Override the full resource names | |
|
||||||
| `serviceAccount.annotations` | Set annotations to the service account | |
|
| `nameOverride` | Override the resource name prefix | |
|
||||||
| `serviceAccount.create` | Deploy the controller pod under a service account | true |
|
| `serviceAccount.annotations` | Set annotations to the service account | |
|
||||||
| `podAnnotations` | Set annotations for the controller pod | |
|
| `serviceAccount.create` | Deploy the controller pod under a service account | true |
|
||||||
| `podLabels` | Set labels for the controller pod | |
|
| `podAnnotations` | Set annotations for the controller pod | |
|
||||||
| `serviceAccount.name` | Set the name of the service account | |
|
| `podLabels` | Set labels for the controller pod | |
|
||||||
| `securityContext` | Set the security context for each container in the controller pod | |
|
| `serviceAccount.name` | Set the name of the service account | |
|
||||||
| `podSecurityContext` | Set the security context to controller pod | |
|
| `securityContext` | Set the security context for each container in the controller pod | |
|
||||||
| `service.annotations` | Set annotations for the provisioned webhook service resource | |
|
| `podSecurityContext` | Set the security context to controller pod | |
|
||||||
| `service.port` | Set controller service ports | |
|
| `service.annotations` | Set annotations for the provisioned webhook service resource | |
|
||||||
| `service.type` | Set controller service type | |
|
| `service.port` | Set controller service ports | |
|
||||||
| `topologySpreadConstraints` | Set the controller pod topologySpreadConstraints | |
|
| `service.type` | Set controller service type | |
|
||||||
| `nodeSelector` | Set the controller pod nodeSelector | |
|
| `topologySpreadConstraints` | Set the controller pod topologySpreadConstraints | |
|
||||||
| `resources` | Set the controller pod resources | |
|
| `nodeSelector` | Set the controller pod nodeSelector | |
|
||||||
| `affinity` | Set the controller pod affinity rules | |
|
| `resources` | Set the controller pod resources | |
|
||||||
| `podDisruptionBudget.enabled` | Enables a PDB to ensure HA of controller pods | false |
|
| `affinity` | Set the controller pod affinity rules | |
|
||||||
| `podDisruptionBudget.minAvailable` | Minimum number of pods that must be available after eviction | |
|
| `podDisruptionBudget.enabled` | Enables a PDB to ensure HA of controller pods | false |
|
||||||
| `podDisruptionBudget.maxUnavailable` | Maximum number of pods that can be unavailable after eviction. Kubernetes 1.7+ required. | |
|
| `podDisruptionBudget.minAvailable` | Minimum number of pods that must be available after eviction | |
|
||||||
| `tolerations` | Set the controller pod tolerations | |
|
| `podDisruptionBudget.maxUnavailable` | Maximum number of pods that can be unavailable after eviction. Kubernetes 1.7+ required. | |
|
||||||
| `env` | Set environment variables for the controller container | |
|
| `tolerations` | Set the controller pod tolerations | |
|
||||||
| `priorityClassName` | Set the controller pod priorityClassName | |
|
| `env` | Set environment variables for the controller container | |
|
||||||
| `scope.watchNamespace` | Tells the controller and the github webhook server which namespace to watch if `scope.singleNamespace` is true | `Release.Namespace` (the default namespace of the helm chart). |
|
| `priorityClassName` | Set the controller pod priorityClassName | |
|
||||||
| `scope.singleNamespace` | Limit the controller to watch a single namespace | false |
|
| `scope.watchNamespace` | Tells the controller and the github webhook server which namespace to watch if `scope.singleNamespace` is true | `Release.Namespace` (the default namespace of the helm chart). |
|
||||||
| `certManagerEnabled` | Enable cert-manager. If disabled you must set admissionWebHooks.caBundle and create TLS secrets manually | true |
|
| `scope.singleNamespace` | Limit the controller to watch a single namespace | false |
|
||||||
| `admissionWebHooks.caBundle` | Base64-encoded PEM bundle containing the CA that signed the webhook's serving certificate | |
|
| `certManagerEnabled` | Enable cert-manager. If disabled you must set admissionWebHooks.caBundle and create TLS secrets manually | true |
|
||||||
| `githubWebhookServer.logLevel` | Set the log level of the githubWebhookServer container | |
|
| `runner.statusUpdateHook.enabled` | Use custom RBAC for runners (role, role binding and service account), this will enable reporting runner statuses | false |
|
||||||
| `githubWebhookServer.replicaCount` | Set the number of webhook server pods | 1 |
|
| `admissionWebHooks.caBundle` | Base64-encoded PEM bundle containing the CA that signed the webhook's serving certificate | |
|
||||||
| `githubWebhookServer.useRunnerGroupsVisibility` | Enable supporting runner groups with custom visibility. This will incur in extra API calls and may blow up your budget. Currently, you also need to set `githubWebhookServer.secret.enabled` to enable this feature. | false |
|
| `githubWebhookServer.logLevel` | Set the log level of the githubWebhookServer container | |
|
||||||
| `githubWebhookServer.syncPeriod` | Set the period in which the controller reconciles the resources | 10m |
|
| `githubWebhookServer.replicaCount` | Set the number of webhook server pods | 1 |
|
||||||
| `githubWebhookServer.enabled` | Deploy the webhook server pod | false |
|
| `githubWebhookServer.useRunnerGroupsVisibility` | Enable supporting runner groups with custom visibility. This will incur in extra API calls and may blow up your budget. Currently, you also need to set `githubWebhookServer.secret.enabled` to enable this feature. | false |
|
||||||
| `githubWebhookServer.secret.enabled` | Passes the webhook hook secret to the github-webhook-server | false |
|
| `githubWebhookServer.enabled` | Deploy the webhook server pod | false |
|
||||||
| `githubWebhookServer.secret.create` | Deploy the webhook hook secret | false |
|
| `githubWebhookServer.queueLimit` | Set the queue size limit in the githubWebhookServer | |
|
||||||
| `githubWebhookServer.secret.name` | Set the name of the webhook hook secret | github-webhook-server |
|
| `githubWebhookServer.secret.enabled` | Passes the webhook hook secret to the github-webhook-server | false |
|
||||||
| `githubWebhookServer.secret.github_webhook_secret_token` | Set the webhook secret token value | |
|
| `githubWebhookServer.secret.create` | Deploy the webhook hook secret | false |
|
||||||
| `githubWebhookServer.imagePullSecrets` | Specifies the secret to be used when pulling the githubWebhookServer pod containers | |
|
| `githubWebhookServer.secret.name` | Set the name of the webhook hook secret | github-webhook-server |
|
||||||
| `githubWebhookServer.nameOverride` | Override the resource name prefix | |
|
| `githubWebhookServer.secret.github_webhook_secret_token` | Set the webhook secret token value | |
|
||||||
| `githubWebhookServer.fullnameOverride` | Override the full resource names | |
|
| `githubWebhookServer.imagePullSecrets` | Specifies the secret to be used when pulling the githubWebhookServer pod containers | |
|
||||||
| `githubWebhookServer.serviceAccount.create` | Deploy the githubWebhookServer under a service account | true |
|
| `githubWebhookServer.nameOverride` | Override the resource name prefix | |
|
||||||
| `githubWebhookServer.serviceAccount.annotations` | Set annotations for the service account | |
|
| `githubWebhookServer.fullnameOverride` | Override the full resource names | |
|
||||||
| `githubWebhookServer.serviceAccount.name` | Set the service account name | |
|
| `githubWebhookServer.serviceAccount.create` | Deploy the githubWebhookServer under a service account | true |
|
||||||
| `githubWebhookServer.podAnnotations` | Set annotations for the githubWebhookServer pod | |
|
| `githubWebhookServer.serviceAccount.annotations` | Set annotations for the service account | |
|
||||||
| `githubWebhookServer.podLabels` | Set labels for the githubWebhookServer pod | |
|
| `githubWebhookServer.serviceAccount.name` | Set the service account name | |
|
||||||
| `githubWebhookServer.podSecurityContext` | Set the security context to githubWebhookServer pod | |
|
| `githubWebhookServer.podAnnotations` | Set annotations for the githubWebhookServer pod | |
|
||||||
| `githubWebhookServer.securityContext` | Set the security context for each container in the githubWebhookServer pod | |
|
| `githubWebhookServer.podLabels` | Set labels for the githubWebhookServer pod | |
|
||||||
| `githubWebhookServer.resources` | Set the githubWebhookServer pod resources | |
|
| `githubWebhookServer.podSecurityContext` | Set the security context to githubWebhookServer pod | |
|
||||||
| `githubWebhookServer.topologySpreadConstraints` | Set the githubWebhookServer pod topologySpreadConstraints | |
|
| `githubWebhookServer.securityContext` | Set the security context for each container in the githubWebhookServer pod | |
|
||||||
| `githubWebhookServer.nodeSelector` | Set the githubWebhookServer pod nodeSelector | |
|
| `githubWebhookServer.resources` | Set the githubWebhookServer pod resources | |
|
||||||
| `githubWebhookServer.tolerations` | Set the githubWebhookServer pod tolerations | |
|
| `githubWebhookServer.topologySpreadConstraints` | Set the githubWebhookServer pod topologySpreadConstraints | |
|
||||||
| `githubWebhookServer.affinity` | Set the githubWebhookServer pod affinity rules | |
|
| `githubWebhookServer.nodeSelector` | Set the githubWebhookServer pod nodeSelector | |
|
||||||
| `githubWebhookServer.priorityClassName` | Set the githubWebhookServer pod priorityClassName | |
|
| `githubWebhookServer.tolerations` | Set the githubWebhookServer pod tolerations | |
|
||||||
| `githubWebhookServer.service.type` | Set githubWebhookServer service type | |
|
| `githubWebhookServer.affinity` | Set the githubWebhookServer pod affinity rules | |
|
||||||
| `githubWebhookServer.service.ports` | Set githubWebhookServer service ports | `[{"port":80, "targetPort:"http", "protocol":"TCP", "name":"http"}]` |
|
| `githubWebhookServer.priorityClassName` | Set the githubWebhookServer pod priorityClassName | |
|
||||||
| `githubWebhookServer.ingress.enabled` | Deploy an ingress kind for the githubWebhookServer | false |
|
| `githubWebhookServer.service.type` | Set githubWebhookServer service type | |
|
||||||
| `githubWebhookServer.ingress.annotations` | Set annotations for the ingress kind | |
|
| `githubWebhookServer.service.ports` | Set githubWebhookServer service ports | `[{"port":80, "targetPort:"http", "protocol":"TCP", "name":"http"}]` |
|
||||||
| `githubWebhookServer.ingress.hosts` | Set hosts configuration for ingress | `[{"host": "chart-example.local", "paths": []}]` |
|
| `githubWebhookServer.ingress.enabled` | Deploy an ingress kind for the githubWebhookServer | false |
|
||||||
| `githubWebhookServer.ingress.tls` | Set tls configuration for ingress | |
|
| `githubWebhookServer.ingress.annotations` | Set annotations for the ingress kind | |
|
||||||
| `githubWebhookServer.ingress.ingressClassName` | Set ingress class name | |
|
| `githubWebhookServer.ingress.hosts` | Set hosts configuration for ingress | `[{"host": "chart-example.local", "paths": []}]` |
|
||||||
| `githubWebhookServer.podDisruptionBudget.enabled` | Enables a PDB to ensure HA of githubwebhook pods | false |
|
| `githubWebhookServer.ingress.tls` | Set tls configuration for ingress | |
|
||||||
| `githubWebhookServer.podDisruptionBudget.minAvailable` | Minimum number of pods that must be available after eviction | |
|
| `githubWebhookServer.ingress.ingressClassName` | Set ingress class name | |
|
||||||
| `githubWebhookServer.podDisruptionBudget.maxUnavailable` | Maximum number of pods that can be unavailable after eviction. Kubernetes 1.7+ required. | |
|
| `githubWebhookServer.podDisruptionBudget.enabled` | Enables a PDB to ensure HA of githubwebhook pods | false |
|
||||||
|
| `githubWebhookServer.podDisruptionBudget.minAvailable` | Minimum number of pods that must be available after eviction | |
|
||||||
|
| `githubWebhookServer.podDisruptionBudget.maxUnavailable` | Maximum number of pods that can be unavailable after eviction. Kubernetes 1.7+ required. | |
|
||||||
|
|||||||
@@ -61,6 +61,16 @@ spec:
|
|||||||
type: integer
|
type: integer
|
||||||
type: object
|
type: object
|
||||||
type: array
|
type: array
|
||||||
|
githubAPICredentialsFrom:
|
||||||
|
properties:
|
||||||
|
secretRef:
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
type: object
|
||||||
|
type: object
|
||||||
maxReplicas:
|
maxReplicas:
|
||||||
description: MaxReplicas is the maximum number of replicas the deployment is allowed to scale
|
description: MaxReplicas is the maximum number of replicas the deployment is allowed to scale
|
||||||
type: integer
|
type: integer
|
||||||
@@ -92,7 +102,7 @@ spec:
|
|||||||
description: ScaleUpThreshold is the percentage of busy runners greater than which will trigger the hpa to scale runners up.
|
description: ScaleUpThreshold is the percentage of busy runners greater than which will trigger the hpa to scale runners up.
|
||||||
type: string
|
type: string
|
||||||
type:
|
type:
|
||||||
description: Type is the type of metric to be used for autoscaling. The only supported Type is TotalNumberOfQueuedAndInProgressWorkflowRuns
|
description: Type is the type of metric to be used for autoscaling. It can be TotalNumberOfQueuedAndInProgressWorkflowRuns or PercentageRunnersBusy.
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
type: array
|
type: array
|
||||||
@@ -170,7 +180,7 @@ spec:
|
|||||||
scheduledOverrides:
|
scheduledOverrides:
|
||||||
description: ScheduledOverrides is the list of ScheduledOverride. It can be used to override a few fields of HorizontalRunnerAutoscalerSpec on schedule. The earlier a scheduled override is, the higher it is prioritized.
|
description: ScheduledOverrides is the list of ScheduledOverride. It can be used to override a few fields of HorizontalRunnerAutoscalerSpec on schedule. The earlier a scheduled override is, the higher it is prioritized.
|
||||||
items:
|
items:
|
||||||
description: ScheduledOverride can be used to override a few fields of HorizontalRunnerAutoscalerSpec on schedule. A schedule can optionally be recurring, so that the correspoding override happens every day, week, month, or year.
|
description: ScheduledOverride can be used to override a few fields of HorizontalRunnerAutoscalerSpec on schedule. A schedule can optionally be recurring, so that the corresponding override happens every day, week, month, or year.
|
||||||
properties:
|
properties:
|
||||||
endTime:
|
endTime:
|
||||||
description: EndTime is the time at which the first override ends.
|
description: EndTime is the time at which the first override ends.
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -114,4 +114,4 @@ Create the name of the service account to use
|
|||||||
|
|
||||||
{{- define "actions-runner-controller.pdbName" -}}
|
{{- define "actions-runner-controller.pdbName" -}}
|
||||||
{{- include "actions-runner-controller.fullname" . | trunc 59 }}-pdb
|
{{- include "actions-runner-controller.fullname" . | trunc 59 }}-pdb
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ metadata:
|
|||||||
{{- toYaml . | nindent 4 }}
|
{{- toYaml . | nindent 4 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
name: {{ include "actions-runner-controller.serviceMonitorName" . }}
|
name: {{ include "actions-runner-controller.serviceMonitorName" . }}
|
||||||
|
namespace: {{ .Release.Namespace }}
|
||||||
spec:
|
spec:
|
||||||
endpoints:
|
endpoints:
|
||||||
- path: /metrics
|
- path: /metrics
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{{- if .Values.podDisruptionBudget.enabled }}
|
{{- if .Values.podDisruptionBudget.enabled }}
|
||||||
apiVersion: policy/v1beta1
|
apiVersion: policy/v1
|
||||||
kind: PodDisruptionBudget
|
kind: PodDisruptionBudget
|
||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ spec:
|
|||||||
{{- if .Values.leaderElectionId }}
|
{{- if .Values.leaderElectionId }}
|
||||||
- "--leader-election-id={{ .Values.leaderElectionId }}"
|
- "--leader-election-id={{ .Values.leaderElectionId }}"
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
- "--port={{ .Values.webhookPort }}"
|
||||||
- "--sync-period={{ .Values.syncPeriod }}"
|
- "--sync-period={{ .Values.syncPeriod }}"
|
||||||
- "--default-scale-down-delay={{ .Values.defaultScaleDownDelay }}"
|
- "--default-scale-down-delay={{ .Values.defaultScaleDownDelay }}"
|
||||||
- "--docker-image={{ .Values.image.dindSidecarRepositoryAndTag }}"
|
- "--docker-image={{ .Values.image.dindSidecarRepositoryAndTag }}"
|
||||||
@@ -57,15 +58,15 @@ spec:
|
|||||||
{{- if .Values.scope.singleNamespace }}
|
{{- if .Values.scope.singleNamespace }}
|
||||||
- "--watch-namespace={{ default .Release.Namespace .Values.scope.watchNamespace }}"
|
- "--watch-namespace={{ default .Release.Namespace .Values.scope.watchNamespace }}"
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.githubAPICacheDuration }}
|
|
||||||
- "--github-api-cache-duration={{ .Values.githubAPICacheDuration }}"
|
|
||||||
{{- end }}
|
|
||||||
{{- if .Values.logLevel }}
|
{{- if .Values.logLevel }}
|
||||||
- "--log-level={{ .Values.logLevel }}"
|
- "--log-level={{ .Values.logLevel }}"
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.runnerGithubURL }}
|
{{- if .Values.runnerGithubURL }}
|
||||||
- "--runner-github-url={{ .Values.runnerGithubURL }}"
|
- "--runner-github-url={{ .Values.runnerGithubURL }}"
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
{{- if .Values.runner.statusUpdateHook.enabled }}
|
||||||
|
- "--runner-status-update-hook"
|
||||||
|
{{- end }}
|
||||||
command:
|
command:
|
||||||
- "/manager"
|
- "/manager"
|
||||||
env:
|
env:
|
||||||
@@ -117,15 +118,19 @@ spec:
|
|||||||
name: {{ include "actions-runner-controller.secretName" . }}
|
name: {{ include "actions-runner-controller.secretName" . }}
|
||||||
optional: true
|
optional: true
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
{{- if kindIs "slice" .Values.env }}
|
||||||
|
{{- toYaml .Values.env | nindent 8 }}
|
||||||
|
{{- else }}
|
||||||
{{- range $key, $val := .Values.env }}
|
{{- range $key, $val := .Values.env }}
|
||||||
- name: {{ $key }}
|
- name: {{ $key }}
|
||||||
value: {{ $val | quote }}
|
value: {{ $val | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default (cat "v" .Chart.AppVersion | replace " " "") }}"
|
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default (cat "v" .Chart.AppVersion | replace " " "") }}"
|
||||||
name: manager
|
name: manager
|
||||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 9443
|
- containerPort: {{ .Values.webhookPort }}
|
||||||
name: webhook-server
|
name: webhook-server
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
{{- if not .Values.metrics.proxy.enabled }}
|
{{- if not .Values.metrics.proxy.enabled }}
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ spec:
|
|||||||
{{- $metricsHost := .Values.metrics.proxy.enabled | ternary "127.0.0.1" "0.0.0.0" }}
|
{{- $metricsHost := .Values.metrics.proxy.enabled | ternary "127.0.0.1" "0.0.0.0" }}
|
||||||
{{- $metricsPort := .Values.metrics.proxy.enabled | ternary "8080" .Values.metrics.port }}
|
{{- $metricsPort := .Values.metrics.proxy.enabled | ternary "8080" .Values.metrics.port }}
|
||||||
- "--metrics-addr={{ $metricsHost }}:{{ $metricsPort }}"
|
- "--metrics-addr={{ $metricsHost }}:{{ $metricsPort }}"
|
||||||
- "--sync-period={{ .Values.githubWebhookServer.syncPeriod }}"
|
|
||||||
{{- if .Values.githubWebhookServer.logLevel }}
|
{{- if .Values.githubWebhookServer.logLevel }}
|
||||||
- "--log-level={{ .Values.githubWebhookServer.logLevel }}"
|
- "--log-level={{ .Values.githubWebhookServer.logLevel }}"
|
||||||
{{- end }}
|
{{- end }}
|
||||||
@@ -49,6 +48,9 @@ spec:
|
|||||||
{{- if .Values.runnerGithubURL }}
|
{{- if .Values.runnerGithubURL }}
|
||||||
- "--runner-github-url={{ .Values.runnerGithubURL }}"
|
- "--runner-github-url={{ .Values.runnerGithubURL }}"
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
{{- if .Values.githubWebhookServer.queueLimit }}
|
||||||
|
- "--queue-limit={{ .Values.githubWebhookServer.queueLimit }}"
|
||||||
|
{{- end }}
|
||||||
command:
|
command:
|
||||||
- "/github-webhook-server"
|
- "/github-webhook-server"
|
||||||
env:
|
env:
|
||||||
|
|||||||
@@ -1,16 +1,11 @@
|
|||||||
{{- if .Values.githubWebhookServer.ingress.enabled -}}
|
{{- if .Values.githubWebhookServer.ingress.enabled -}}
|
||||||
{{- $fullName := include "actions-runner-controller-github-webhook-server.fullname" . -}}
|
{{- $fullName := include "actions-runner-controller-github-webhook-server.fullname" . -}}
|
||||||
{{- $svcPort := (index .Values.githubWebhookServer.service.ports 0).port -}}
|
{{- $svcPort := (index .Values.githubWebhookServer.service.ports 0).port -}}
|
||||||
{{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }}
|
|
||||||
apiVersion: networking.k8s.io/v1
|
apiVersion: networking.k8s.io/v1
|
||||||
{{- else if .Capabilities.APIVersions.Has "networking.k8s.io/v1beta1" }}
|
|
||||||
apiVersion: networking.k8s.io/v1beta1
|
|
||||||
{{- else if .Capabilities.APIVersions.Has "extensions/v1beta1" }}
|
|
||||||
apiVersion: extensions/v1beta1
|
|
||||||
{{- end }}
|
|
||||||
kind: Ingress
|
kind: Ingress
|
||||||
metadata:
|
metadata:
|
||||||
name: {{ $fullName }}
|
name: {{ $fullName }}
|
||||||
|
namespace: {{ .Release.Namespace }}
|
||||||
labels:
|
labels:
|
||||||
{{- include "actions-runner-controller.labels" . | nindent 4 }}
|
{{- include "actions-runner-controller.labels" . | nindent 4 }}
|
||||||
{{- with .Values.githubWebhookServer.ingress.annotations }}
|
{{- with .Values.githubWebhookServer.ingress.annotations }}
|
||||||
@@ -36,21 +31,17 @@ spec:
|
|||||||
- host: {{ .host | quote }}
|
- host: {{ .host | quote }}
|
||||||
http:
|
http:
|
||||||
paths:
|
paths:
|
||||||
|
{{- if .extraPaths }}
|
||||||
|
{{- toYaml .extraPaths | nindent 10 }}
|
||||||
|
{{- end }}
|
||||||
{{- range .paths }}
|
{{- range .paths }}
|
||||||
- path: {{ .path }}
|
- path: {{ .path }}
|
||||||
{{- if $.Capabilities.APIVersions.Has "networking.k8s.io/v1" }}
|
|
||||||
pathType: {{ .pathType }}
|
pathType: {{ .pathType }}
|
||||||
{{- end }}
|
|
||||||
backend:
|
backend:
|
||||||
{{- if $.Capabilities.APIVersions.Has "networking.k8s.io/v1" }}
|
|
||||||
service:
|
service:
|
||||||
name: {{ $fullName }}
|
name: {{ $fullName }}
|
||||||
port:
|
port:
|
||||||
number: {{ $svcPort }}
|
number: {{ $svcPort }}
|
||||||
{{- else }}
|
|
||||||
serviceName: {{ $fullName }}
|
|
||||||
servicePort: {{ $svcPort }}
|
|
||||||
{{- end }}
|
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{{- if .Values.githubWebhookServer.podDisruptionBudget.enabled }}
|
{{- if .Values.githubWebhookServer.podDisruptionBudget.enabled }}
|
||||||
apiVersion: policy/v1beta1
|
apiVersion: policy/v1
|
||||||
kind: PodDisruptionBudget
|
kind: PodDisruptionBudget
|
||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
|
|||||||
@@ -12,5 +12,17 @@ data:
|
|||||||
{{- if .Values.githubWebhookServer.secret.github_webhook_secret_token }}
|
{{- if .Values.githubWebhookServer.secret.github_webhook_secret_token }}
|
||||||
github_webhook_secret_token: {{ .Values.githubWebhookServer.secret.github_webhook_secret_token | toString | b64enc }}
|
github_webhook_secret_token: {{ .Values.githubWebhookServer.secret.github_webhook_secret_token | toString | b64enc }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
{{- if .Values.githubWebhookServer.secret.github_app_id }}
|
||||||
|
github_app_id: {{ .Values.githubWebhookServer.secret.github_app_id | toString | b64enc }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Values.githubWebhookServer.secret.github_app_installation_id }}
|
||||||
|
github_app_installation_id: {{ .Values.githubWebhookServer.secret.github_app_installation_id | toString | b64enc }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Values.githubWebhookServer.secret.github_app_private_key }}
|
||||||
|
github_app_private_key: {{ .Values.githubWebhookServer.secret.github_app_private_key | toString | b64enc }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Values.githubWebhookServer.secret.github_token }}
|
||||||
|
github_token: {{ .Values.githubWebhookServer.secret.github_token | toString | b64enc }}
|
||||||
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ metadata:
|
|||||||
{{- toYaml . | nindent 4 }}
|
{{- toYaml . | nindent 4 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
name: {{ include "actions-runner-controller-github-webhook-server.serviceMonitorName" . }}
|
name: {{ include "actions-runner-controller-github-webhook-server.serviceMonitorName" . }}
|
||||||
|
namespace: {{ .Release.Namespace }}
|
||||||
spec:
|
spec:
|
||||||
endpoints:
|
endpoints:
|
||||||
- path: /metrics
|
- path: /metrics
|
||||||
|
|||||||
@@ -195,6 +195,28 @@ rules:
|
|||||||
verbs:
|
verbs:
|
||||||
- create
|
- create
|
||||||
- patch
|
- patch
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- persistentvolumeclaims
|
||||||
|
verbs:
|
||||||
|
- delete
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- persistentvolumes
|
||||||
|
verbs:
|
||||||
|
- delete
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- watch
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- coordination.k8s.io
|
- coordination.k8s.io
|
||||||
resources:
|
resources:
|
||||||
@@ -228,3 +250,72 @@ rules:
|
|||||||
- patch
|
- patch
|
||||||
- update
|
- update
|
||||||
- watch
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- secrets
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
{{- if .Values.runner.statusUpdateHook.enabled }}
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- serviceaccounts
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- delete
|
||||||
|
- get
|
||||||
|
- apiGroups:
|
||||||
|
- rbac.authorization.k8s.io
|
||||||
|
resources:
|
||||||
|
- rolebindings
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- delete
|
||||||
|
- get
|
||||||
|
- apiGroups:
|
||||||
|
- rbac.authorization.k8s.io
|
||||||
|
resources:
|
||||||
|
- roles
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- delete
|
||||||
|
- get
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Values.rbac.allowGrantingKubernetesContainerModePermissions }}
|
||||||
|
{{/* These permissions are required by ARC to create RBAC resources for the runner pod to use the kubernetes container mode. */}}
|
||||||
|
{{/* See https://github.com/actions-runner-controller/actions-runner-controller/pull/1268/files#r917331632 */}}
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- pods/exec
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- get
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- pods/log
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- "batch"
|
||||||
|
resources:
|
||||||
|
- jobs
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- create
|
||||||
|
- delete
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- secrets
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- delete
|
||||||
|
{{- end }}
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
|
{{/*
|
||||||
|
We will use a self managed CA if one is not provided by cert-manager
|
||||||
|
*/}}
|
||||||
|
{{- $ca := genCA "actions-runner-ca" 3650 }}
|
||||||
|
{{- $cert := genSignedCert (printf "%s.%s.svc" (include "actions-runner-controller.webhookServiceName" .) .Release.Namespace) nil (list (printf "%s.%s.svc" (include "actions-runner-controller.webhookServiceName" .) .Release.Namespace)) 3650 $ca }}
|
||||||
---
|
---
|
||||||
apiVersion: admissionregistration.k8s.io/v1
|
apiVersion: admissionregistration.k8s.io/v1
|
||||||
kind: MutatingWebhookConfiguration
|
kind: MutatingWebhookConfiguration
|
||||||
@@ -20,6 +24,8 @@ webhooks:
|
|||||||
clientConfig:
|
clientConfig:
|
||||||
{{- if .Values.admissionWebHooks.caBundle }}
|
{{- if .Values.admissionWebHooks.caBundle }}
|
||||||
caBundle: {{ quote .Values.admissionWebHooks.caBundle }}
|
caBundle: {{ quote .Values.admissionWebHooks.caBundle }}
|
||||||
|
{{- else if not .Values.certManagerEnabled }}
|
||||||
|
caBundle: {{ $ca.Cert | b64enc | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
service:
|
service:
|
||||||
name: {{ include "actions-runner-controller.webhookServiceName" . }}
|
name: {{ include "actions-runner-controller.webhookServiceName" . }}
|
||||||
@@ -48,6 +54,8 @@ webhooks:
|
|||||||
clientConfig:
|
clientConfig:
|
||||||
{{- if .Values.admissionWebHooks.caBundle }}
|
{{- if .Values.admissionWebHooks.caBundle }}
|
||||||
caBundle: {{ .Values.admissionWebHooks.caBundle }}
|
caBundle: {{ .Values.admissionWebHooks.caBundle }}
|
||||||
|
{{- else if not .Values.certManagerEnabled }}
|
||||||
|
caBundle: {{ $ca.Cert | b64enc | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
service:
|
service:
|
||||||
name: {{ include "actions-runner-controller.webhookServiceName" . }}
|
name: {{ include "actions-runner-controller.webhookServiceName" . }}
|
||||||
@@ -76,6 +84,8 @@ webhooks:
|
|||||||
clientConfig:
|
clientConfig:
|
||||||
{{- if .Values.admissionWebHooks.caBundle }}
|
{{- if .Values.admissionWebHooks.caBundle }}
|
||||||
caBundle: {{ .Values.admissionWebHooks.caBundle }}
|
caBundle: {{ .Values.admissionWebHooks.caBundle }}
|
||||||
|
{{- else if not .Values.certManagerEnabled }}
|
||||||
|
caBundle: {{ $ca.Cert | b64enc | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
service:
|
service:
|
||||||
name: {{ include "actions-runner-controller.webhookServiceName" . }}
|
name: {{ include "actions-runner-controller.webhookServiceName" . }}
|
||||||
@@ -104,6 +114,8 @@ webhooks:
|
|||||||
clientConfig:
|
clientConfig:
|
||||||
{{- if .Values.admissionWebHooks.caBundle }}
|
{{- if .Values.admissionWebHooks.caBundle }}
|
||||||
caBundle: {{ .Values.admissionWebHooks.caBundle }}
|
caBundle: {{ .Values.admissionWebHooks.caBundle }}
|
||||||
|
{{- else if not .Values.certManagerEnabled }}
|
||||||
|
caBundle: {{ $ca.Cert | b64enc | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
service:
|
service:
|
||||||
name: {{ include "actions-runner-controller.webhookServiceName" . }}
|
name: {{ include "actions-runner-controller.webhookServiceName" . }}
|
||||||
@@ -145,6 +157,8 @@ webhooks:
|
|||||||
clientConfig:
|
clientConfig:
|
||||||
{{- if .Values.admissionWebHooks.caBundle }}
|
{{- if .Values.admissionWebHooks.caBundle }}
|
||||||
caBundle: {{ .Values.admissionWebHooks.caBundle }}
|
caBundle: {{ .Values.admissionWebHooks.caBundle }}
|
||||||
|
{{- else if not .Values.certManagerEnabled }}
|
||||||
|
caBundle: {{ $ca.Cert | b64enc | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
service:
|
service:
|
||||||
name: {{ include "actions-runner-controller.webhookServiceName" . }}
|
name: {{ include "actions-runner-controller.webhookServiceName" . }}
|
||||||
@@ -173,6 +187,8 @@ webhooks:
|
|||||||
clientConfig:
|
clientConfig:
|
||||||
{{- if .Values.admissionWebHooks.caBundle }}
|
{{- if .Values.admissionWebHooks.caBundle }}
|
||||||
caBundle: {{ .Values.admissionWebHooks.caBundle }}
|
caBundle: {{ .Values.admissionWebHooks.caBundle }}
|
||||||
|
{{- else if not .Values.certManagerEnabled }}
|
||||||
|
caBundle: {{ $ca.Cert | b64enc | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
service:
|
service:
|
||||||
name: {{ include "actions-runner-controller.webhookServiceName" . }}
|
name: {{ include "actions-runner-controller.webhookServiceName" . }}
|
||||||
@@ -201,6 +217,8 @@ webhooks:
|
|||||||
clientConfig:
|
clientConfig:
|
||||||
{{- if .Values.admissionWebHooks.caBundle }}
|
{{- if .Values.admissionWebHooks.caBundle }}
|
||||||
caBundle: {{ .Values.admissionWebHooks.caBundle }}
|
caBundle: {{ .Values.admissionWebHooks.caBundle }}
|
||||||
|
{{- else if not .Values.certManagerEnabled }}
|
||||||
|
caBundle: {{ $ca.Cert | b64enc | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
service:
|
service:
|
||||||
name: {{ include "actions-runner-controller.webhookServiceName" . }}
|
name: {{ include "actions-runner-controller.webhookServiceName" . }}
|
||||||
@@ -219,3 +237,18 @@ webhooks:
|
|||||||
resources:
|
resources:
|
||||||
- runnerreplicasets
|
- runnerreplicasets
|
||||||
sideEffects: None
|
sideEffects: None
|
||||||
|
{{ if not (or .Values.admissionWebHooks.caBundle .Values.certManagerEnabled) }}
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: {{ include "actions-runner-controller.servingCertName" . }}
|
||||||
|
namespace: {{ .Release.Namespace }}
|
||||||
|
labels:
|
||||||
|
{{- include "actions-runner-controller.labels" . | nindent 4 }}
|
||||||
|
type: kubernetes.io/tls
|
||||||
|
data:
|
||||||
|
tls.crt: {{ $cert.Cert | b64enc | quote }}
|
||||||
|
tls.key: {{ $cert.Key | b64enc | quote }}
|
||||||
|
ca.crt: {{ $ca.Cert | b64enc | quote }}
|
||||||
|
{{- end }}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ spec:
|
|||||||
type: {{ .Values.service.type }}
|
type: {{ .Values.service.type }}
|
||||||
ports:
|
ports:
|
||||||
- port: 443
|
- port: 443
|
||||||
targetPort: 9443
|
targetPort: {{ .Values.webhookPort }}
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
name: https
|
name: https
|
||||||
selector:
|
selector:
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ labels: {}
|
|||||||
|
|
||||||
replicaCount: 1
|
replicaCount: 1
|
||||||
|
|
||||||
|
webhookPort: 9443
|
||||||
syncPeriod: 1m
|
syncPeriod: 1m
|
||||||
defaultScaleDownDelay: 10m
|
defaultScaleDownDelay: 10m
|
||||||
|
|
||||||
@@ -14,12 +15,6 @@ enableLeaderElection: true
|
|||||||
# Must be unique if more than one controller installed onto the same namespace.
|
# Must be unique if more than one controller installed onto the same namespace.
|
||||||
#leaderElectionId: "actions-runner-controller"
|
#leaderElectionId: "actions-runner-controller"
|
||||||
|
|
||||||
# DEPRECATED: This has been removed as unnecessary in #1192
|
|
||||||
# The controller tries its best not to repeat the duplicate GitHub API call
|
|
||||||
# within this duration.
|
|
||||||
# Defaults to syncPeriod - 10s.
|
|
||||||
#githubAPICacheDuration: 30s
|
|
||||||
|
|
||||||
# The URL of your GitHub Enterprise server, if you're using one.
|
# The URL of your GitHub Enterprise server, if you're using one.
|
||||||
#githubEnterpriseServerURL: https://github.example.com
|
#githubEnterpriseServerURL: https://github.example.com
|
||||||
|
|
||||||
@@ -66,6 +61,18 @@ imagePullSecrets: []
|
|||||||
nameOverride: ""
|
nameOverride: ""
|
||||||
fullnameOverride: ""
|
fullnameOverride: ""
|
||||||
|
|
||||||
|
runner:
|
||||||
|
statusUpdateHook:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
rbac:
|
||||||
|
{}
|
||||||
|
# # This allows ARC to dynamically create a ServiceAccount and a Role for each Runner pod that uses "kubernetes" container mode,
|
||||||
|
# # by extending ARC's manager role to have the same permissions required by the pod runs the runner agent in "kubernetes" container mode.
|
||||||
|
# # Without this, Kubernetes blocks ARC to create the role to prevent a priviledge escalation.
|
||||||
|
# # See https://github.com/actions-runner-controller/actions-runner-controller/pull/1268/files#r917327010
|
||||||
|
# allowGrantingKubernetesContainerModePermissions: true
|
||||||
|
|
||||||
serviceAccount:
|
serviceAccount:
|
||||||
# Specifies whether a service account should be created
|
# Specifies whether a service account should be created
|
||||||
create: true
|
create: true
|
||||||
@@ -108,7 +115,7 @@ metrics:
|
|||||||
enabled: true
|
enabled: true
|
||||||
image:
|
image:
|
||||||
repository: quay.io/brancz/kube-rbac-proxy
|
repository: quay.io/brancz/kube-rbac-proxy
|
||||||
tag: v0.11.0
|
tag: v0.13.1
|
||||||
|
|
||||||
resources:
|
resources:
|
||||||
{}
|
{}
|
||||||
@@ -142,10 +149,20 @@ priorityClassName: ""
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
{}
|
{}
|
||||||
|
# specify additional environment variables for the controller pod.
|
||||||
|
# It's possible to specify either key vale pairs e.g.:
|
||||||
# http_proxy: "proxy.com:8080"
|
# http_proxy: "proxy.com:8080"
|
||||||
# https_proxy: "proxy.com:8080"
|
# https_proxy: "proxy.com:8080"
|
||||||
# no_proxy: ""
|
# no_proxy: ""
|
||||||
|
|
||||||
|
# or a list of complete environment variable definitions e.g.:
|
||||||
|
# - name: GITHUB_APP_INSTALLATION_ID
|
||||||
|
# valueFrom:
|
||||||
|
# secretKeyRef:
|
||||||
|
# key: some_key_in_the_secret
|
||||||
|
# name: some-secret-name
|
||||||
|
# optional: true
|
||||||
|
|
||||||
## specify additional volumes to mount in the manager container, this can be used
|
## specify additional volumes to mount in the manager container, this can be used
|
||||||
## to specify additional storage of material or to inject files from ConfigMaps
|
## to specify additional storage of material or to inject files from ConfigMaps
|
||||||
## into the running container
|
## into the running container
|
||||||
@@ -174,7 +191,6 @@ admissionWebHooks:
|
|||||||
githubWebhookServer:
|
githubWebhookServer:
|
||||||
enabled: false
|
enabled: false
|
||||||
replicaCount: 1
|
replicaCount: 1
|
||||||
syncPeriod: 10m
|
|
||||||
useRunnerGroupsVisibility: false
|
useRunnerGroupsVisibility: false
|
||||||
secret:
|
secret:
|
||||||
enabled: false
|
enabled: false
|
||||||
@@ -182,6 +198,13 @@ githubWebhookServer:
|
|||||||
name: "github-webhook-server"
|
name: "github-webhook-server"
|
||||||
### GitHub Webhook Configuration
|
### GitHub Webhook Configuration
|
||||||
github_webhook_secret_token: ""
|
github_webhook_secret_token: ""
|
||||||
|
### GitHub Apps Configuration
|
||||||
|
## NOTE: IDs MUST be strings, use quotes
|
||||||
|
#github_app_id: ""
|
||||||
|
#github_app_installation_id: ""
|
||||||
|
#github_app_private_key: |
|
||||||
|
### GitHub PAT Configuration
|
||||||
|
#github_token: ""
|
||||||
imagePullSecrets: []
|
imagePullSecrets: []
|
||||||
nameOverride: ""
|
nameOverride: ""
|
||||||
fullnameOverride: ""
|
fullnameOverride: ""
|
||||||
@@ -223,6 +246,20 @@ githubWebhookServer:
|
|||||||
paths: []
|
paths: []
|
||||||
# - path: /*
|
# - path: /*
|
||||||
# pathType: ImplementationSpecific
|
# pathType: ImplementationSpecific
|
||||||
|
# Extra paths that are not automatically connected to the server. This is useful when working with annotation based services.
|
||||||
|
extraPaths: []
|
||||||
|
# - path: /*
|
||||||
|
# backend:
|
||||||
|
# serviceName: ssl-redirect
|
||||||
|
# servicePort: use-annotation
|
||||||
|
## for Kubernetes >=1.19 (when "networking.k8s.io/v1" is used)
|
||||||
|
# - path: /*
|
||||||
|
# pathType: Prefix
|
||||||
|
# backend:
|
||||||
|
# service:
|
||||||
|
# name: ssl-redirect
|
||||||
|
# port:
|
||||||
|
# name: use-annotation
|
||||||
tls: []
|
tls: []
|
||||||
# - secretName: chart-example-tls
|
# - secretName: chart-example-tls
|
||||||
# hosts:
|
# hosts:
|
||||||
@@ -233,3 +270,4 @@ githubWebhookServer:
|
|||||||
enabled: false
|
enabled: false
|
||||||
# minAvailable: 1
|
# minAvailable: 1
|
||||||
# maxUnavailable: 3
|
# maxUnavailable: 3
|
||||||
|
# queueLimit: 100
|
||||||
|
|||||||
@@ -69,9 +69,8 @@ func main() {
|
|||||||
|
|
||||||
watchNamespace string
|
watchNamespace string
|
||||||
|
|
||||||
enableLeaderElection bool
|
logLevel string
|
||||||
syncPeriod time.Duration
|
queueLimit int
|
||||||
logLevel string
|
|
||||||
|
|
||||||
ghClient *github.Client
|
ghClient *github.Client
|
||||||
)
|
)
|
||||||
@@ -88,10 +87,8 @@ func main() {
|
|||||||
flag.StringVar(&webhookAddr, "webhook-addr", ":8000", "The address the metric endpoint binds to.")
|
flag.StringVar(&webhookAddr, "webhook-addr", ":8000", "The address the metric endpoint binds to.")
|
||||||
flag.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.")
|
flag.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.")
|
||||||
flag.StringVar(&watchNamespace, "watch-namespace", "", "The namespace to watch for HorizontalRunnerAutoscaler's to scale on Webhook. Set to empty for letting it watch for all namespaces.")
|
flag.StringVar(&watchNamespace, "watch-namespace", "", "The namespace to watch for HorizontalRunnerAutoscaler's to scale on Webhook. Set to empty for letting it watch for all namespaces.")
|
||||||
flag.BoolVar(&enableLeaderElection, "enable-leader-election", false,
|
|
||||||
"Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager.")
|
|
||||||
flag.DurationVar(&syncPeriod, "sync-period", 10*time.Minute, "Determines the minimum frequency at which K8s resources managed by this controller are reconciled. When you use autoscaling, set to a lower value like 10 minute, because this corresponds to the minimum time to react on demand change")
|
|
||||||
flag.StringVar(&logLevel, "log-level", logging.LogLevelDebug, `The verbosity of the logging. Valid values are "debug", "info", "warn", "error". Defaults to "debug".`)
|
flag.StringVar(&logLevel, "log-level", logging.LogLevelDebug, `The verbosity of the logging. Valid values are "debug", "info", "warn", "error". Defaults to "debug".`)
|
||||||
|
flag.IntVar(&queueLimit, "queue-limit", controllers.DefaultQueueLimit, `The maximum length of the scale operation queue. The scale opration is enqueued per every matching webhook event, and the server returns a 500 HTTP status when the queue was already full on enqueue attempt.`)
|
||||||
flag.StringVar(&webhookSecretToken, "github-webhook-secret-token", "", "The personal access token of GitHub.")
|
flag.StringVar(&webhookSecretToken, "github-webhook-secret-token", "", "The personal access token of GitHub.")
|
||||||
flag.StringVar(&c.Token, "github-token", c.Token, "The personal access token of GitHub.")
|
flag.StringVar(&c.Token, "github-token", c.Token, "The personal access token of GitHub.")
|
||||||
flag.Int64Var(&c.AppID, "github-app-id", c.AppID, "The application ID of GitHub App.")
|
flag.Int64Var(&c.AppID, "github-app-id", c.AppID, "The application ID of GitHub App.")
|
||||||
@@ -142,10 +139,10 @@ func main() {
|
|||||||
setupLog.Info("GitHub client is not initialized. Runner groups with custom visibility are not supported. If needed, please provide GitHub authentication. This will incur in extra GitHub API calls")
|
setupLog.Info("GitHub client is not initialized. Runner groups with custom visibility are not supported. If needed, please provide GitHub authentication. This will incur in extra GitHub API calls")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
syncPeriod := 10 * time.Minute
|
||||||
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
|
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
|
||||||
Scheme: scheme,
|
Scheme: scheme,
|
||||||
SyncPeriod: &syncPeriod,
|
SyncPeriod: &syncPeriod,
|
||||||
LeaderElection: enableLeaderElection,
|
|
||||||
Namespace: watchNamespace,
|
Namespace: watchNamespace,
|
||||||
MetricsBindAddress: metricsAddr,
|
MetricsBindAddress: metricsAddr,
|
||||||
Port: 9443,
|
Port: 9443,
|
||||||
@@ -164,6 +161,7 @@ func main() {
|
|||||||
SecretKeyBytes: []byte(webhookSecretToken),
|
SecretKeyBytes: []byte(webhookSecretToken),
|
||||||
Namespace: watchNamespace,
|
Namespace: watchNamespace,
|
||||||
GitHubClient: ghClient,
|
GitHubClient: ghClient,
|
||||||
|
QueueLimit: queueLimit,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = hraGitHubWebhook.SetupWithManager(mgr); err != nil {
|
if err = hraGitHubWebhook.SetupWithManager(mgr); err != nil {
|
||||||
|
|||||||
@@ -61,6 +61,16 @@ spec:
|
|||||||
type: integer
|
type: integer
|
||||||
type: object
|
type: object
|
||||||
type: array
|
type: array
|
||||||
|
githubAPICredentialsFrom:
|
||||||
|
properties:
|
||||||
|
secretRef:
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
type: object
|
||||||
|
type: object
|
||||||
maxReplicas:
|
maxReplicas:
|
||||||
description: MaxReplicas is the maximum number of replicas the deployment is allowed to scale
|
description: MaxReplicas is the maximum number of replicas the deployment is allowed to scale
|
||||||
type: integer
|
type: integer
|
||||||
@@ -92,7 +102,7 @@ spec:
|
|||||||
description: ScaleUpThreshold is the percentage of busy runners greater than which will trigger the hpa to scale runners up.
|
description: ScaleUpThreshold is the percentage of busy runners greater than which will trigger the hpa to scale runners up.
|
||||||
type: string
|
type: string
|
||||||
type:
|
type:
|
||||||
description: Type is the type of metric to be used for autoscaling. The only supported Type is TotalNumberOfQueuedAndInProgressWorkflowRuns
|
description: Type is the type of metric to be used for autoscaling. It can be TotalNumberOfQueuedAndInProgressWorkflowRuns or PercentageRunnersBusy.
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
type: array
|
type: array
|
||||||
@@ -170,7 +180,7 @@ spec:
|
|||||||
scheduledOverrides:
|
scheduledOverrides:
|
||||||
description: ScheduledOverrides is the list of ScheduledOverride. It can be used to override a few fields of HorizontalRunnerAutoscalerSpec on schedule. The earlier a scheduled override is, the higher it is prioritized.
|
description: ScheduledOverrides is the list of ScheduledOverride. It can be used to override a few fields of HorizontalRunnerAutoscalerSpec on schedule. The earlier a scheduled override is, the higher it is prioritized.
|
||||||
items:
|
items:
|
||||||
description: ScheduledOverride can be used to override a few fields of HorizontalRunnerAutoscalerSpec on schedule. A schedule can optionally be recurring, so that the correspoding override happens every day, week, month, or year.
|
description: ScheduledOverride can be used to override a few fields of HorizontalRunnerAutoscalerSpec on schedule. A schedule can optionally be recurring, so that the corresponding override happens every day, week, month, or year.
|
||||||
properties:
|
properties:
|
||||||
endTime:
|
endTime:
|
||||||
description: EndTime is the time at which the first override ends.
|
description: EndTime is the time at which the first override ends.
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -22,8 +22,6 @@ bases:
|
|||||||
- ../certmanager
|
- ../certmanager
|
||||||
# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'.
|
# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'.
|
||||||
#- ../prometheus
|
#- ../prometheus
|
||||||
# [GH_WEBHOOK_SERVER] To enable the GitHub webhook server, uncomment all sections with 'GH_WEBHOOK_SERVER'.
|
|
||||||
#- ../github-webhook-server
|
|
||||||
|
|
||||||
patchesStrategicMerge:
|
patchesStrategicMerge:
|
||||||
# Protect the /metrics endpoint by putting it behind auth.
|
# Protect the /metrics endpoint by putting it behind auth.
|
||||||
@@ -46,10 +44,6 @@ patchesStrategicMerge:
|
|||||||
# 'CERTMANAGER' needs to be enabled to use ca injection
|
# 'CERTMANAGER' needs to be enabled to use ca injection
|
||||||
- webhookcainjection_patch.yaml
|
- webhookcainjection_patch.yaml
|
||||||
|
|
||||||
# [GH_WEBHOOK_SERVER] To enable the GitHub webhook server, uncomment all sections with 'GH_WEBHOOK_SERVER'.
|
|
||||||
# Protect the GitHub webhook server metrics endpoint by putting it behind auth.
|
|
||||||
# - gh-webhook-server-auth-proxy-patch.yaml
|
|
||||||
|
|
||||||
# the following config is for teaching kustomize how to do var substitution
|
# the following config is for teaching kustomize how to do var substitution
|
||||||
vars:
|
vars:
|
||||||
# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix.
|
# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix.
|
||||||
|
|||||||
@@ -2,11 +2,14 @@ apiVersion: kustomize.config.k8s.io/v1beta1
|
|||||||
kind: Kustomization
|
kind: Kustomization
|
||||||
|
|
||||||
images:
|
images:
|
||||||
- name: controller
|
- name: controller
|
||||||
newName: summerwind/actions-runner-controller
|
newName: summerwind/actions-runner-controller
|
||||||
newTag: latest
|
newTag: latest
|
||||||
|
|
||||||
resources:
|
resources:
|
||||||
- deployment.yaml
|
- deployment.yaml
|
||||||
- rbac.yaml
|
- rbac.yaml
|
||||||
- service.yaml
|
- service.yaml
|
||||||
|
|
||||||
|
patchesStrategicMerge:
|
||||||
|
- gh-webhook-server-auth-proxy-patch.yaml
|
||||||
|
|||||||
@@ -202,6 +202,29 @@ rules:
|
|||||||
verbs:
|
verbs:
|
||||||
- create
|
- create
|
||||||
- patch
|
- patch
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- persistentvolumeclaims
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- delete
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- persistentvolumes
|
||||||
|
verbs:
|
||||||
|
- delete
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- watch
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- ""
|
- ""
|
||||||
resources:
|
resources:
|
||||||
@@ -226,3 +249,36 @@ rules:
|
|||||||
- patch
|
- patch
|
||||||
- update
|
- update
|
||||||
- watch
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- secrets
|
||||||
|
verbs:
|
||||||
|
- delete
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- serviceaccounts
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- delete
|
||||||
|
- get
|
||||||
|
- apiGroups:
|
||||||
|
- rbac.authorization.k8s.io
|
||||||
|
resources:
|
||||||
|
- rolebindings
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- delete
|
||||||
|
- get
|
||||||
|
- apiGroups:
|
||||||
|
- rbac.authorization.k8s.io
|
||||||
|
resources:
|
||||||
|
- roles
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- delete
|
||||||
|
- get
|
||||||
|
|||||||
6
contrib/README.md
Normal file
6
contrib/README.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
The `contrib` directory is the place for sharing various example code for deploying and operating `actions-runner-controller`.
|
||||||
|
|
||||||
|
Anything contained in this directory is provided as-is. The maintainers of `actions-runner-controller` is not yet commited to provide
|
||||||
|
full support for using, fixing, and enhancing it. However, they will do their best effort to collect feedbacks from early adopters and advanced users like you, and may eventually consider graduating any of the examples as an official addition to the project.
|
||||||
|
|
||||||
|
See https://github.com/actions-runner-controller/actions-runner-controller/pull/1375#issuecomment-1258816470 and https://github.com/actions-runner-controller/actions-runner-controller/pull/1559#issuecomment-1258827496 for more context.
|
||||||
25
contrib/examples/actions-runner/.helmignore
Normal file
25
contrib/examples/actions-runner/.helmignore
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# Patterns to ignore when building packages.
|
||||||
|
# This supports shell glob matching, relative path matching, and
|
||||||
|
# negation (prefixed with !). Only one pattern per line.
|
||||||
|
.DS_Store
|
||||||
|
# Common VCS dirs
|
||||||
|
.git/
|
||||||
|
.gitignore
|
||||||
|
.bzr/
|
||||||
|
.bzrignore
|
||||||
|
.hg/
|
||||||
|
.hgignore
|
||||||
|
.svn/
|
||||||
|
# Common backup files
|
||||||
|
*.swp
|
||||||
|
*.bak
|
||||||
|
*.tmp
|
||||||
|
*.orig
|
||||||
|
*~
|
||||||
|
# Various IDEs
|
||||||
|
.project
|
||||||
|
.idea/
|
||||||
|
*.tmproj
|
||||||
|
.vscode/
|
||||||
|
# Docs
|
||||||
|
docs/
|
||||||
10
contrib/examples/actions-runner/Chart.yaml
Normal file
10
contrib/examples/actions-runner/Chart.yaml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
apiVersion: v2
|
||||||
|
name: actions-runner
|
||||||
|
description: Helm Chart for Github Actions Runner
|
||||||
|
type: application
|
||||||
|
version: 0.0.1
|
||||||
|
appVersion: 2.290.1
|
||||||
|
|
||||||
|
home: https://github.com/actions-runner-controller/actions-runner-controller/tree/master/runner
|
||||||
|
sources:
|
||||||
|
- https://github.com/actions-runner-controller/actions-runner-controller/tree/master/runner
|
||||||
36
contrib/examples/actions-runner/README.md
Normal file
36
contrib/examples/actions-runner/README.md
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
## Docs
|
||||||
|
|
||||||
|
All additional docs are kept in the `docs/` folder, this README is solely for documenting the values.yaml keys and values
|
||||||
|
|
||||||
|
## Values
|
||||||
|
|
||||||
|
**_The values are documented as of HEAD, to review the configuration options for your chart version ensure you view this file at the relevent [tag](https://github.com/actions-runner-controller/actions-runner-controller/tags)_**
|
||||||
|
|
||||||
|
> _Default values are the defaults set in the charts values.yaml, some properties have default configurations in the code for when the property is omitted or invalid_
|
||||||
|
|
||||||
|
| Key | Description | Default |
|
||||||
|
|----------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------|
|
||||||
|
| `labels` | Set labels to apply to all resources in the chart | |
|
||||||
|
| `replicaCount` | Set the number of runner pods | 1 |
|
||||||
|
| `image.repository` | The "repository/image" of the runner container | summerwind/actions-runner |
|
||||||
|
| `image.tag` | The tag of the runner container | |
|
||||||
|
| `image.pullPolicy` | The pull policy of the runner image | IfNotPresent |
|
||||||
|
| `imagePullSecrets` | Specifies the secret to be used when pulling the runner pod containers | |
|
||||||
|
| `fullnameOverride` | Override the full resource names | |
|
||||||
|
| `nameOverride` | Override the resource name prefix | |
|
||||||
|
| `podAnnotations` | Set annotations for the runner pod | |
|
||||||
|
| `podLabels` | Set labels for the runner pod | |
|
||||||
|
| `podSecurityContext` | Set the security context to runner pod | |
|
||||||
|
| `nodeSelector` | Set the pod nodeSelector | |
|
||||||
|
| `affinity` | Set the runner pod affinity rules | |
|
||||||
|
| `tolerations` | Set the runner pod tolerations | |
|
||||||
|
| `env` | Set environment variables for the runner container | |
|
||||||
|
| `organization` | Github organization where runner will be registered | test |
|
||||||
|
| `repository` | Github repository where runner will be registered | |
|
||||||
|
| `runnerLabels` | Labels you want to add in your runner | test |
|
||||||
|
| `autoscaler.enabled` | Enable the HorizontalRunnerAutoscaler, if its enabled then replica count will not be used | true |
|
||||||
|
| `autoscaler.minReplicas` | Minimum no of replicas | 1 |
|
||||||
|
| `autoscaler.maxReplicas` | Maximum no of replicas | 5 |
|
||||||
|
| `autoscaler.scaleDownDelaySecondsAfterScaleOut` | [Anti-Flapping Configuration](https://github.com/actions-runner-controller/actions-runner-controller#anti-flapping-configuration) | 120 |
|
||||||
|
| `autoscaler.metrics` | [Pull driven scaling](https://github.com/actions-runner-controller/actions-runner-controller#pull-driven-scaling) | default |
|
||||||
|
| `autoscaler.scaleUpTriggers` | [Webhook driven scaling](https://github.com/actions-runner-controller/actions-runner-controller#webhook-driven-scaling) | |
|
||||||
54
contrib/examples/actions-runner/templates/_helpers.tpl
Normal file
54
contrib/examples/actions-runner/templates/_helpers.tpl
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
{{/*
|
||||||
|
Expand the name of the chart.
|
||||||
|
*/}}
|
||||||
|
{{- define "actions-runner.name" -}}
|
||||||
|
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Create a default fully qualified app name.
|
||||||
|
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
|
||||||
|
If release name contains chart name it will be used as a full name.
|
||||||
|
*/}}
|
||||||
|
{{- define "actions-runner.fullname" -}}
|
||||||
|
{{- if .Values.fullnameOverride }}
|
||||||
|
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
|
||||||
|
{{- else }}
|
||||||
|
{{- $name := default .Chart.Name .Values.nameOverride }}
|
||||||
|
{{- if contains $name .Release.Name }}
|
||||||
|
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
|
||||||
|
{{- else }}
|
||||||
|
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Create chart name and version as used by the chart label.
|
||||||
|
*/}}
|
||||||
|
{{- define "actions-runner.chart" -}}
|
||||||
|
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Common labels
|
||||||
|
*/}}
|
||||||
|
{{- define "actions-runner.labels" -}}
|
||||||
|
helm.sh/chart: {{ include "actions-runner.chart" . }}
|
||||||
|
{{ include "actions-runner.selectorLabels" . }}
|
||||||
|
{{- if .Chart.AppVersion }}
|
||||||
|
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
||||||
|
{{- end }}
|
||||||
|
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||||
|
{{- range $k, $v := .Values.labels }}
|
||||||
|
{{ $k }}: {{ $v }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Selector labels
|
||||||
|
*/}}
|
||||||
|
{{- define "actions-runner.selectorLabels" -}}
|
||||||
|
app.kubernetes.io/name: {{ include "actions-runner.name" . }}
|
||||||
|
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||||
|
{{- end }}
|
||||||
96
contrib/examples/actions-runner/templates/deployment.yaml
Normal file
96
contrib/examples/actions-runner/templates/deployment.yaml
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: List
|
||||||
|
items:
|
||||||
|
- apiVersion: actions.summerwind.dev/v1alpha1
|
||||||
|
kind: RunnerDeployment
|
||||||
|
metadata:
|
||||||
|
name: {{ include "actions-runner.fullname" . }}
|
||||||
|
namespace: {{ .Release.Namespace }}
|
||||||
|
labels:
|
||||||
|
{{- include "actions-runner.labels" . | nindent 6 }}
|
||||||
|
spec:
|
||||||
|
{{- if not .Values.autoscaler.enabled }}
|
||||||
|
replicas: {{ .Values.replicaCount }}
|
||||||
|
{{- end }}
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
{{- include "actions-runner.selectorLabels" . | nindent 8 }}
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
{{- with .Values.podAnnotations }}
|
||||||
|
annotations:
|
||||||
|
kubectl.kubernetes.io/default-logs-container: "runner"
|
||||||
|
{{- toYaml . | nindent 10 }}
|
||||||
|
{{- end }}
|
||||||
|
labels:
|
||||||
|
{{- include "actions-runner.selectorLabels" . | nindent 10 }}
|
||||||
|
{{- with .Values.podLabels }}
|
||||||
|
{{- toYaml . | nindent 10 }}
|
||||||
|
{{- end }}
|
||||||
|
spec:
|
||||||
|
{{- with .Values.imagePullSecrets }}
|
||||||
|
imagePullSecrets:
|
||||||
|
{{- toYaml . | nindent 10 }}
|
||||||
|
{{- end }}
|
||||||
|
securityContext:
|
||||||
|
{{- toYaml .Values.podSecurityContext | nindent 10 }}
|
||||||
|
{{- with .Values.priorityClassName }}
|
||||||
|
priorityClassName: "{{ . }}"
|
||||||
|
{{- end }}
|
||||||
|
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default (cat "v" .Chart.AppVersion | replace " " "") }}"
|
||||||
|
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||||
|
{{- if .Values.organization }}
|
||||||
|
organization: {{ .Values.organization }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Values.repository }}
|
||||||
|
repository: {{ .Values.repository }}
|
||||||
|
{{- end }}
|
||||||
|
group: {{ .Values.group | default "Default" }}
|
||||||
|
{{- with .Values.runnerLabels }}
|
||||||
|
labels:
|
||||||
|
{{- toYaml . | nindent 10 }}
|
||||||
|
{{- end }}
|
||||||
|
{{- with .Values.nodeSelector }}
|
||||||
|
nodeSelector:
|
||||||
|
{{- toYaml . | nindent 10 }}
|
||||||
|
{{- end }}
|
||||||
|
{{- with .Values.affinity }}
|
||||||
|
affinity:
|
||||||
|
{{- toYaml . | nindent 10 }}
|
||||||
|
{{- end }}
|
||||||
|
{{- with .Values.tolerations }}
|
||||||
|
tolerations:
|
||||||
|
{{- toYaml . | nindent 10 }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Values.env }}
|
||||||
|
env:
|
||||||
|
{{- range $key, $val := .Values.env }}
|
||||||
|
- name: {{ $key }}
|
||||||
|
value: {{ $val | quote }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Values.autoscaler.enabled }}
|
||||||
|
- apiVersion: actions.summerwind.dev/v1alpha1
|
||||||
|
kind: HorizontalRunnerAutoscaler
|
||||||
|
metadata:
|
||||||
|
name: {{ (list (include "actions-runner.fullname" .) "autoscaler" | join "-") }}
|
||||||
|
namespace: {{ .Release.Namespace }}
|
||||||
|
labels:
|
||||||
|
{{- include "actions-runner.labels" . | nindent 6 }}
|
||||||
|
spec:
|
||||||
|
{{- if .Values.autoscaler.scaleDownDelaySecondsAfterScaleOut }}
|
||||||
|
scaleDownDelaySecondsAfterScaleOut: {{ .Values.autoscaler.scaleDownDelaySecondsAfterScaleOut }}
|
||||||
|
{{- end }}
|
||||||
|
scaleTargetRef:
|
||||||
|
name: {{ include "actions-runner.fullname" . }}
|
||||||
|
minReplicas: {{ .Values.autoscaler.minReplicas }}
|
||||||
|
maxReplicas: {{ .Values.autoscaler.maxReplicas }}
|
||||||
|
{{- with .Values.autoscaler.scaleUpTriggers }}
|
||||||
|
scaleUpTriggers:
|
||||||
|
{{- toYaml . | nindent 4 }}
|
||||||
|
{{- end }}
|
||||||
|
{{- with .Values.autoscaler.metrics }}
|
||||||
|
metrics:
|
||||||
|
{{- toYaml . | nindent 4 }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
63
contrib/examples/actions-runner/values.yaml
Normal file
63
contrib/examples/actions-runner/values.yaml
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
image:
|
||||||
|
repository: summerwind/actions-runner
|
||||||
|
tag: v2.290.1-ubuntu-20.04
|
||||||
|
pullPolicy: IfNotPresent
|
||||||
|
|
||||||
|
# Create runner for an organization or a repository
|
||||||
|
# Set only one of the two either organization or repository
|
||||||
|
# By default, it creates runner under github organization test
|
||||||
|
organization: test
|
||||||
|
# repository: mumoshu/actions-runner-controller-ci
|
||||||
|
|
||||||
|
# Labels you want to add in your runner
|
||||||
|
runnerLabels:
|
||||||
|
- test
|
||||||
|
|
||||||
|
# If you enable Autoscaler, then it will not be used
|
||||||
|
replicaCount: 1
|
||||||
|
|
||||||
|
# The Runner Group that the runner(s) should be associated with.
|
||||||
|
# See https://docs.github.com/en/github-ae@latest/actions/hosting-your-own-runners/managing-access-to-self-hosted-runners-using-groups.
|
||||||
|
group: Default
|
||||||
|
|
||||||
|
autoscaler:
|
||||||
|
enabled: true
|
||||||
|
minReplicas: 1
|
||||||
|
maxReplicas: 5
|
||||||
|
scaleDownDelaySecondsAfterScaleOut: 120
|
||||||
|
# metrics (pull method) / scaleUpTriggers (push method)
|
||||||
|
# https://github.com/actions-runner-controller/actions-runner-controller#pull-driven-scaling
|
||||||
|
# https://github.com/actions-runner-controller/actions-runner-controller#webhook-driven-scaling
|
||||||
|
metrics:
|
||||||
|
- type: PercentageRunnersBusy
|
||||||
|
scaleUpThreshold: '0.75'
|
||||||
|
scaleDownThreshold: '0.25'
|
||||||
|
scaleUpFactor: '2'
|
||||||
|
scaleDownFactor: '0.5'
|
||||||
|
# scaleUpTriggers:
|
||||||
|
# - githubEvent: {}
|
||||||
|
# duration: "5m"
|
||||||
|
|
||||||
|
podAnnotations: {}
|
||||||
|
|
||||||
|
podLabels: {}
|
||||||
|
|
||||||
|
imagePullSecrets: []
|
||||||
|
|
||||||
|
podSecurityContext:
|
||||||
|
{}
|
||||||
|
# fsGroup: 2000
|
||||||
|
|
||||||
|
# Leverage a PriorityClass to ensure your pods survive resource shortages
|
||||||
|
# ref: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/
|
||||||
|
# PriorityClass: system-cluster-critical
|
||||||
|
priorityClassName: ""
|
||||||
|
|
||||||
|
nodeSelector: {}
|
||||||
|
|
||||||
|
tolerations: []
|
||||||
|
|
||||||
|
affinity: {}
|
||||||
|
|
||||||
|
env:
|
||||||
|
{}
|
||||||
60
contrib/examples/terraform/actions-runner-controller.tf
Normal file
60
contrib/examples/terraform/actions-runner-controller.tf
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
### Deploying with exposed github token
|
||||||
|
|
||||||
|
resource "kubernetes_namespace" "arc" {
|
||||||
|
metadata {
|
||||||
|
name = "actions-runner-system"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "helm_release" "actions-runner-controller" {
|
||||||
|
count = var.actions_runner_controller
|
||||||
|
name = "actions-runner-controller"
|
||||||
|
namespace = kubernetes_namespace.arc.metadata[0].name
|
||||||
|
create_namespace = true
|
||||||
|
chart = "actions-runner-controller"
|
||||||
|
repository = "https://actions-runner-controller.github.io/actions-runner-controller"
|
||||||
|
version = "v0.19.1"
|
||||||
|
values = [<<EOF
|
||||||
|
authSecret:
|
||||||
|
github_token: hdjasyd7das7d7asd78as87dasdas
|
||||||
|
create: true
|
||||||
|
EOF
|
||||||
|
]
|
||||||
|
depends_on = [resource.helm_release.cm]
|
||||||
|
}
|
||||||
|
|
||||||
|
#============================================================================================================================================
|
||||||
|
### Deploying with secret manager like AWS's
|
||||||
|
# make sure the name of the secret is the same as secret_id
|
||||||
|
|
||||||
|
data "aws_secretsmanager_secret_version" "creds" {
|
||||||
|
secret_id = "github/access_token"
|
||||||
|
}
|
||||||
|
locals {
|
||||||
|
github_creds = jsondecode(
|
||||||
|
data.aws_secretsmanager_secret_version.creds.secret_string
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "kubernetes_namespace" "arc" {
|
||||||
|
metadata {
|
||||||
|
name = "actions-runner-system"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "helm_release" "actions-runner-controller" {
|
||||||
|
count = var.actions_runner_controller
|
||||||
|
name = "actions-runner-controller"
|
||||||
|
namespace = kubernetes_namespace.arc.metadata[0].name
|
||||||
|
create_namespace = true
|
||||||
|
chart = "actions-runner-controller"
|
||||||
|
repository = "https://actions-runner-controller.github.io/actions-runner-controller"
|
||||||
|
version = "v0.19.1"
|
||||||
|
values = [<<EOF
|
||||||
|
authSecret:
|
||||||
|
github_token: ${local.github_creds.github_token}
|
||||||
|
create: true
|
||||||
|
EOF
|
||||||
|
]
|
||||||
|
depends_on = [resource.helm_release.cm]
|
||||||
|
}
|
||||||
27
contrib/examples/terraform/cert-manager.tf
Normal file
27
contrib/examples/terraform/cert-manager.tf
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# cert-manager must be deployed or included via the deployment process
|
||||||
|
|
||||||
|
resource "kubernetes_namespace" "cm" {
|
||||||
|
metadata {
|
||||||
|
name = "cert-manager"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "helm_release" "cm" {
|
||||||
|
count = var.actions_runner_controller
|
||||||
|
name = "cm"
|
||||||
|
namespace = kubernetes_namespace.cm.metadata[0].name
|
||||||
|
create_namespace = true
|
||||||
|
chart = "cert-manager"
|
||||||
|
repository = "https://charts.jetstack.io"
|
||||||
|
version = "v1.8.0"
|
||||||
|
values = [<<EOF
|
||||||
|
global:
|
||||||
|
podSecurityPolicy:
|
||||||
|
enabled: true
|
||||||
|
useAppArmor: true
|
||||||
|
prometheus:
|
||||||
|
enabled: false
|
||||||
|
installCRDs: true
|
||||||
|
EOF
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -9,7 +9,11 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/actions-runner-controller/actions-runner-controller/api/v1alpha1"
|
"github.com/actions-runner-controller/actions-runner-controller/api/v1alpha1"
|
||||||
"github.com/google/go-github/v39/github"
|
prometheus_metrics "github.com/actions-runner-controller/actions-runner-controller/controllers/metrics"
|
||||||
|
arcgithub "github.com/actions-runner-controller/actions-runner-controller/github"
|
||||||
|
"github.com/google/go-github/v47/github"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -19,7 +23,7 @@ const (
|
|||||||
defaultScaleDownFactor = 0.7
|
defaultScaleDownFactor = 0.7
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r *HorizontalRunnerAutoscalerReconciler) suggestDesiredReplicas(st scaleTarget, hra v1alpha1.HorizontalRunnerAutoscaler) (*int, error) {
|
func (r *HorizontalRunnerAutoscalerReconciler) suggestDesiredReplicas(ghc *arcgithub.Client, st scaleTarget, hra v1alpha1.HorizontalRunnerAutoscaler) (*int, error) {
|
||||||
if hra.Spec.MinReplicas == nil {
|
if hra.Spec.MinReplicas == nil {
|
||||||
return nil, fmt.Errorf("horizontalrunnerautoscaler %s/%s is missing minReplicas", hra.Namespace, hra.Name)
|
return nil, fmt.Errorf("horizontalrunnerautoscaler %s/%s is missing minReplicas", hra.Namespace, hra.Name)
|
||||||
} else if hra.Spec.MaxReplicas == nil {
|
} else if hra.Spec.MaxReplicas == nil {
|
||||||
@@ -46,9 +50,9 @@ func (r *HorizontalRunnerAutoscalerReconciler) suggestDesiredReplicas(st scaleTa
|
|||||||
|
|
||||||
switch primaryMetricType {
|
switch primaryMetricType {
|
||||||
case v1alpha1.AutoscalingMetricTypeTotalNumberOfQueuedAndInProgressWorkflowRuns:
|
case v1alpha1.AutoscalingMetricTypeTotalNumberOfQueuedAndInProgressWorkflowRuns:
|
||||||
suggested, err = r.suggestReplicasByQueuedAndInProgressWorkflowRuns(st, hra, &primaryMetric)
|
suggested, err = r.suggestReplicasByQueuedAndInProgressWorkflowRuns(ghc, st, hra, &primaryMetric)
|
||||||
case v1alpha1.AutoscalingMetricTypePercentageRunnersBusy:
|
case v1alpha1.AutoscalingMetricTypePercentageRunnersBusy:
|
||||||
suggested, err = r.suggestReplicasByPercentageRunnersBusy(st, hra, primaryMetric)
|
suggested, err = r.suggestReplicasByPercentageRunnersBusy(ghc, st, hra, primaryMetric)
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("validating autoscaling metrics: unsupported metric type %q", primaryMetric)
|
return nil, fmt.Errorf("validating autoscaling metrics: unsupported metric type %q", primaryMetric)
|
||||||
}
|
}
|
||||||
@@ -81,11 +85,10 @@ func (r *HorizontalRunnerAutoscalerReconciler) suggestDesiredReplicas(st scaleTa
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return r.suggestReplicasByQueuedAndInProgressWorkflowRuns(st, hra, &fallbackMetric)
|
return r.suggestReplicasByQueuedAndInProgressWorkflowRuns(ghc, st, hra, &fallbackMetric)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *HorizontalRunnerAutoscalerReconciler) suggestReplicasByQueuedAndInProgressWorkflowRuns(st scaleTarget, hra v1alpha1.HorizontalRunnerAutoscaler, metrics *v1alpha1.MetricSpec) (*int, error) {
|
func (r *HorizontalRunnerAutoscalerReconciler) suggestReplicasByQueuedAndInProgressWorkflowRuns(ghc *arcgithub.Client, st scaleTarget, hra v1alpha1.HorizontalRunnerAutoscaler, metrics *v1alpha1.MetricSpec) (*int, error) {
|
||||||
|
|
||||||
var repos [][]string
|
var repos [][]string
|
||||||
repoID := st.repo
|
repoID := st.repo
|
||||||
if repoID == "" {
|
if repoID == "" {
|
||||||
@@ -124,7 +127,7 @@ func (r *HorizontalRunnerAutoscalerReconciler) suggestReplicasByQueuedAndInProgr
|
|||||||
opt := github.ListWorkflowJobsOptions{ListOptions: github.ListOptions{PerPage: 50}}
|
opt := github.ListWorkflowJobsOptions{ListOptions: github.ListOptions{PerPage: 50}}
|
||||||
var allJobs []*github.WorkflowJob
|
var allJobs []*github.WorkflowJob
|
||||||
for {
|
for {
|
||||||
jobs, resp, err := r.GitHubClient.Actions.ListWorkflowJobs(context.TODO(), user, repoName, runID, &opt)
|
jobs, resp, err := ghc.Actions.ListWorkflowJobs(context.TODO(), user, repoName, runID, &opt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.Log.Error(err, "Error listing workflow jobs")
|
r.Log.Error(err, "Error listing workflow jobs")
|
||||||
return //err
|
return //err
|
||||||
@@ -182,7 +185,7 @@ func (r *HorizontalRunnerAutoscalerReconciler) suggestReplicasByQueuedAndInProgr
|
|||||||
|
|
||||||
for _, repo := range repos {
|
for _, repo := range repos {
|
||||||
user, repoName := repo[0], repo[1]
|
user, repoName := repo[0], repo[1]
|
||||||
workflowRuns, err := r.GitHubClient.ListRepositoryWorkflowRuns(context.TODO(), user, repoName)
|
workflowRuns, err := ghc.ListRepositoryWorkflowRuns(context.TODO(), user, repoName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -209,6 +212,20 @@ func (r *HorizontalRunnerAutoscalerReconciler) suggestReplicasByQueuedAndInProgr
|
|||||||
|
|
||||||
necessaryReplicas := queued + inProgress
|
necessaryReplicas := queued + inProgress
|
||||||
|
|
||||||
|
prometheus_metrics.SetHorizontalRunnerAutoscalerQueuedAndInProgressWorkflowRuns(
|
||||||
|
hra.ObjectMeta,
|
||||||
|
st.enterprise,
|
||||||
|
st.org,
|
||||||
|
st.repo,
|
||||||
|
st.kind,
|
||||||
|
st.st,
|
||||||
|
necessaryReplicas,
|
||||||
|
completed,
|
||||||
|
inProgress,
|
||||||
|
queued,
|
||||||
|
unknown,
|
||||||
|
)
|
||||||
|
|
||||||
r.Log.V(1).Info(
|
r.Log.V(1).Info(
|
||||||
fmt.Sprintf("Suggested desired replicas of %d by TotalNumberOfQueuedAndInProgressWorkflowRuns", necessaryReplicas),
|
fmt.Sprintf("Suggested desired replicas of %d by TotalNumberOfQueuedAndInProgressWorkflowRuns", necessaryReplicas),
|
||||||
"workflow_runs_completed", completed,
|
"workflow_runs_completed", completed,
|
||||||
@@ -224,7 +241,7 @@ func (r *HorizontalRunnerAutoscalerReconciler) suggestReplicasByQueuedAndInProgr
|
|||||||
return &necessaryReplicas, nil
|
return &necessaryReplicas, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *HorizontalRunnerAutoscalerReconciler) suggestReplicasByPercentageRunnersBusy(st scaleTarget, hra v1alpha1.HorizontalRunnerAutoscaler, metrics v1alpha1.MetricSpec) (*int, error) {
|
func (r *HorizontalRunnerAutoscalerReconciler) suggestReplicasByPercentageRunnersBusy(ghc *arcgithub.Client, st scaleTarget, hra v1alpha1.HorizontalRunnerAutoscaler, metrics v1alpha1.MetricSpec) (*int, error) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
scaleUpThreshold := defaultScaleUpThreshold
|
scaleUpThreshold := defaultScaleUpThreshold
|
||||||
scaleDownThreshold := defaultScaleDownThreshold
|
scaleDownThreshold := defaultScaleDownThreshold
|
||||||
@@ -293,7 +310,7 @@ func (r *HorizontalRunnerAutoscalerReconciler) suggestReplicasByPercentageRunner
|
|||||||
)
|
)
|
||||||
|
|
||||||
// ListRunners will return all runners managed by GitHub - not restricted to ns
|
// ListRunners will return all runners managed by GitHub - not restricted to ns
|
||||||
runners, err := r.GitHubClient.ListRunners(
|
runners, err := ghc.ListRunners(
|
||||||
ctx,
|
ctx,
|
||||||
enterprise,
|
enterprise,
|
||||||
organization,
|
organization,
|
||||||
@@ -314,22 +331,52 @@ func (r *HorizontalRunnerAutoscalerReconciler) suggestReplicasByPercentageRunner
|
|||||||
numRunners int
|
numRunners int
|
||||||
numRunnersRegistered int
|
numRunnersRegistered int
|
||||||
numRunnersBusy int
|
numRunnersBusy int
|
||||||
|
numTerminatingBusy int
|
||||||
)
|
)
|
||||||
|
|
||||||
numRunners = len(runnerMap)
|
numRunners = len(runnerMap)
|
||||||
|
|
||||||
|
busyTerminatingRunnerPods := map[string]struct{}{}
|
||||||
|
|
||||||
|
kindLabel := LabelKeyRunnerDeploymentName
|
||||||
|
if hra.Spec.ScaleTargetRef.Kind == "RunnerSet" {
|
||||||
|
kindLabel = LabelKeyRunnerSetName
|
||||||
|
}
|
||||||
|
|
||||||
|
var runnerPodList corev1.PodList
|
||||||
|
if err := r.Client.List(ctx, &runnerPodList, client.InNamespace(hra.Namespace), client.MatchingLabels(map[string]string{
|
||||||
|
kindLabel: hra.Spec.ScaleTargetRef.Name,
|
||||||
|
})); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range runnerPodList.Items {
|
||||||
|
if p.Annotations[AnnotationKeyUnregistrationFailureMessage] != "" {
|
||||||
|
busyTerminatingRunnerPods[p.Name] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for _, runner := range runners {
|
for _, runner := range runners {
|
||||||
if _, ok := runnerMap[*runner.Name]; ok {
|
if _, ok := runnerMap[*runner.Name]; ok {
|
||||||
numRunnersRegistered++
|
numRunnersRegistered++
|
||||||
|
|
||||||
if runner.GetBusy() {
|
if runner.GetBusy() {
|
||||||
numRunnersBusy++
|
numRunnersBusy++
|
||||||
|
} else if _, ok := busyTerminatingRunnerPods[*runner.Name]; ok {
|
||||||
|
numTerminatingBusy++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
delete(busyTerminatingRunnerPods, *runner.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remaining busyTerminatingRunnerPods are runners that were not on the ListRunners API response yet
|
||||||
|
for range busyTerminatingRunnerPods {
|
||||||
|
numTerminatingBusy++
|
||||||
|
}
|
||||||
|
|
||||||
var desiredReplicas int
|
var desiredReplicas int
|
||||||
fractionBusy := float64(numRunnersBusy) / float64(desiredReplicasBefore)
|
fractionBusy := float64(numRunnersBusy+numTerminatingBusy) / float64(desiredReplicasBefore)
|
||||||
if fractionBusy >= scaleUpThreshold {
|
if fractionBusy >= scaleUpThreshold {
|
||||||
if scaleUpAdjustment > 0 {
|
if scaleUpAdjustment > 0 {
|
||||||
desiredReplicas = desiredReplicasBefore + scaleUpAdjustment
|
desiredReplicas = desiredReplicasBefore + scaleUpAdjustment
|
||||||
@@ -350,6 +397,19 @@ func (r *HorizontalRunnerAutoscalerReconciler) suggestReplicasByPercentageRunner
|
|||||||
//
|
//
|
||||||
// - num_runners can be as twice as large as replicas_desired_before while
|
// - num_runners can be as twice as large as replicas_desired_before while
|
||||||
// the runnerdeployment controller is replacing RunnerReplicaSet for runner update.
|
// the runnerdeployment controller is replacing RunnerReplicaSet for runner update.
|
||||||
|
prometheus_metrics.SetHorizontalRunnerAutoscalerPercentageRunnersBusy(
|
||||||
|
hra.ObjectMeta,
|
||||||
|
st.enterprise,
|
||||||
|
st.org,
|
||||||
|
st.repo,
|
||||||
|
st.kind,
|
||||||
|
st.st,
|
||||||
|
desiredReplicas,
|
||||||
|
numRunners,
|
||||||
|
numRunnersRegistered,
|
||||||
|
numRunnersBusy,
|
||||||
|
numTerminatingBusy,
|
||||||
|
)
|
||||||
|
|
||||||
r.Log.V(1).Info(
|
r.Log.V(1).Info(
|
||||||
fmt.Sprintf("Suggested desired replicas of %d by PercentageRunnersBusy", desiredReplicas),
|
fmt.Sprintf("Suggested desired replicas of %d by PercentageRunnersBusy", desiredReplicas),
|
||||||
@@ -358,6 +418,7 @@ func (r *HorizontalRunnerAutoscalerReconciler) suggestReplicasByPercentageRunner
|
|||||||
"num_runners", numRunners,
|
"num_runners", numRunners,
|
||||||
"num_runners_registered", numRunnersRegistered,
|
"num_runners_registered", numRunnersRegistered,
|
||||||
"num_runners_busy", numRunnersBusy,
|
"num_runners_busy", numRunnersBusy,
|
||||||
|
"num_terminating_busy", numTerminatingBusy,
|
||||||
"namespace", hra.Namespace,
|
"namespace", hra.Namespace,
|
||||||
"kind", st.kind,
|
"kind", st.kind,
|
||||||
"name", st.st,
|
"name", st.st,
|
||||||
|
|||||||
@@ -330,7 +330,6 @@ func TestDetermineDesiredReplicas_RepositoryRunner(t *testing.T) {
|
|||||||
|
|
||||||
h := &HorizontalRunnerAutoscalerReconciler{
|
h := &HorizontalRunnerAutoscalerReconciler{
|
||||||
Log: log,
|
Log: log,
|
||||||
GitHubClient: client,
|
|
||||||
Scheme: scheme,
|
Scheme: scheme,
|
||||||
DefaultScaleDownDelay: DefaultScaleDownDelay,
|
DefaultScaleDownDelay: DefaultScaleDownDelay,
|
||||||
}
|
}
|
||||||
@@ -379,7 +378,7 @@ func TestDetermineDesiredReplicas_RepositoryRunner(t *testing.T) {
|
|||||||
|
|
||||||
st := h.scaleTargetFromRD(context.Background(), rd)
|
st := h.scaleTargetFromRD(context.Background(), rd)
|
||||||
|
|
||||||
got, err := h.computeReplicasWithCache(log, metav1Now.Time, st, hra, minReplicas)
|
got, err := h.computeReplicasWithCache(client, log, metav1Now.Time, st, hra, minReplicas)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if tc.err == "" {
|
if tc.err == "" {
|
||||||
t.Fatalf("unexpected error: expected none, got %v", err)
|
t.Fatalf("unexpected error: expected none, got %v", err)
|
||||||
@@ -720,7 +719,6 @@ func TestDetermineDesiredReplicas_OrganizationalRunner(t *testing.T) {
|
|||||||
h := &HorizontalRunnerAutoscalerReconciler{
|
h := &HorizontalRunnerAutoscalerReconciler{
|
||||||
Log: log,
|
Log: log,
|
||||||
Scheme: scheme,
|
Scheme: scheme,
|
||||||
GitHubClient: client,
|
|
||||||
DefaultScaleDownDelay: DefaultScaleDownDelay,
|
DefaultScaleDownDelay: DefaultScaleDownDelay,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -781,7 +779,7 @@ func TestDetermineDesiredReplicas_OrganizationalRunner(t *testing.T) {
|
|||||||
|
|
||||||
st := h.scaleTargetFromRD(context.Background(), rd)
|
st := h.scaleTargetFromRD(context.Background(), rd)
|
||||||
|
|
||||||
got, err := h.computeReplicasWithCache(log, metav1Now.Time, st, hra, minReplicas)
|
got, err := h.computeReplicasWithCache(client, log, metav1Now.Time, st, hra, minReplicas)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if tc.err == "" {
|
if tc.err == "" {
|
||||||
t.Fatalf("unexpected error: expected none, got %v", err)
|
t.Fatalf("unexpected error: expected none, got %v", err)
|
||||||
|
|||||||
@@ -4,17 +4,22 @@ import "time"
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
LabelKeyRunnerSetName = "runnerset-name"
|
LabelKeyRunnerSetName = "runnerset-name"
|
||||||
|
LabelKeyRunner = "actions-runner"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// This names requires at least one slash to work.
|
// This names requires at least one slash to work.
|
||||||
// See https://github.com/google/knative-gcp/issues/378
|
// See https://github.com/google/knative-gcp/issues/378
|
||||||
runnerPodFinalizerName = "actions.summerwind.dev/runner-pod"
|
runnerPodFinalizerName = "actions.summerwind.dev/runner-pod"
|
||||||
|
runnerLinkedResourcesFinalizerName = "actions.summerwind.dev/linked-resources"
|
||||||
|
|
||||||
annotationKeyPrefix = "actions-runner/"
|
annotationKeyPrefix = "actions-runner/"
|
||||||
|
|
||||||
AnnotationKeyLastRegistrationCheckTime = "actions-runner-controller/last-registration-check-time"
|
AnnotationKeyLastRegistrationCheckTime = "actions-runner-controller/last-registration-check-time"
|
||||||
|
|
||||||
|
// AnnotationKeyUnregistrationFailureMessage is the annotation that is added onto the pod once it failed to be unregistered from GitHub due to e.g. 422 error
|
||||||
|
AnnotationKeyUnregistrationFailureMessage = annotationKeyPrefix + "unregistration-failure-message"
|
||||||
|
|
||||||
// AnnotationKeyUnregistrationCompleteTimestamp is the annotation that is added onto the pod once the previously started unregistration process has been completed.
|
// AnnotationKeyUnregistrationCompleteTimestamp is the annotation that is added onto the pod once the previously started unregistration process has been completed.
|
||||||
AnnotationKeyUnregistrationCompleteTimestamp = annotationKeyPrefix + "unregistration-complete-timestamp"
|
AnnotationKeyUnregistrationCompleteTimestamp = annotationKeyPrefix + "unregistration-complete-timestamp"
|
||||||
|
|
||||||
@@ -47,8 +52,6 @@ const (
|
|||||||
// A pod that is timed out can be terminated if needed.
|
// A pod that is timed out can be terminated if needed.
|
||||||
registrationTimeout = 10 * time.Minute
|
registrationTimeout = 10 * time.Minute
|
||||||
|
|
||||||
defaultRegistrationCheckInterval = time.Minute
|
|
||||||
|
|
||||||
// DefaultRunnerPodRecreationDelayAfterWebhookScale is the delay until syncing the runners with the desired replicas
|
// DefaultRunnerPodRecreationDelayAfterWebhookScale is the delay until syncing the runners with the desired replicas
|
||||||
// after a webhook-based scale up.
|
// after a webhook-based scale up.
|
||||||
// This is used to prevent ARC from recreating completed runner pods that are deleted soon without being used at all.
|
// This is used to prevent ARC from recreating completed runner pods that are deleted soon without being used at all.
|
||||||
@@ -63,4 +66,7 @@ const (
|
|||||||
|
|
||||||
EnvVarRunnerName = "RUNNER_NAME"
|
EnvVarRunnerName = "RUNNER_NAME"
|
||||||
EnvVarRunnerToken = "RUNNER_TOKEN"
|
EnvVarRunnerToken = "RUNNER_TOKEN"
|
||||||
|
|
||||||
|
// defaultHookPath is path to the hook script used when the "containerMode: kubernetes" is specified
|
||||||
|
defaultRunnerHookPath = "/runner/k8s/index.js"
|
||||||
)
|
)
|
||||||
|
|||||||
206
controllers/horizontal_runner_autoscaler_batch_scale.go
Normal file
206
controllers/horizontal_runner_autoscaler_batch_scale.go
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/actions-runner-controller/actions-runner-controller/api/v1alpha1"
|
||||||
|
"github.com/go-logr/logr"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
type batchScaler struct {
|
||||||
|
Ctx context.Context
|
||||||
|
Client client.Client
|
||||||
|
Log logr.Logger
|
||||||
|
interval time.Duration
|
||||||
|
|
||||||
|
queue chan *ScaleTarget
|
||||||
|
workerStart sync.Once
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBatchScaler(ctx context.Context, client client.Client, log logr.Logger) *batchScaler {
|
||||||
|
return &batchScaler{
|
||||||
|
Ctx: ctx,
|
||||||
|
Client: client,
|
||||||
|
Log: log,
|
||||||
|
interval: 3 * time.Second,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type batchScaleOperation struct {
|
||||||
|
namespacedName types.NamespacedName
|
||||||
|
scaleOps []scaleOperation
|
||||||
|
}
|
||||||
|
|
||||||
|
type scaleOperation struct {
|
||||||
|
trigger v1alpha1.ScaleUpTrigger
|
||||||
|
log logr.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the scale target to the unbounded queue, blocking until the target is successfully added to the queue.
|
||||||
|
// All the targets in the queue are dequeued every 3 seconds, grouped by the HRA, and applied.
|
||||||
|
// In a happy path, batchScaler update each HRA only once, even though the HRA had two or more associated webhook events in the 3 seconds interval,
|
||||||
|
// which results in less K8s API calls and less HRA update conflicts in case your ARC installation receives a lot of webhook events
|
||||||
|
func (s *batchScaler) Add(st *ScaleTarget) {
|
||||||
|
if st == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.workerStart.Do(func() {
|
||||||
|
var expBackoff = []time.Duration{time.Second, 2 * time.Second, 4 * time.Second, 8 * time.Second, 16 * time.Second}
|
||||||
|
|
||||||
|
s.queue = make(chan *ScaleTarget)
|
||||||
|
|
||||||
|
log := s.Log
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
log.Info("Starting batch worker")
|
||||||
|
defer log.Info("Stopped batch worker")
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-s.Ctx.Done():
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
log.V(2).Info("Batch worker is dequeueing operations")
|
||||||
|
|
||||||
|
batches := map[types.NamespacedName]batchScaleOperation{}
|
||||||
|
after := time.After(s.interval)
|
||||||
|
var ops uint
|
||||||
|
|
||||||
|
batch:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-after:
|
||||||
|
break batch
|
||||||
|
case st := <-s.queue:
|
||||||
|
nsName := types.NamespacedName{
|
||||||
|
Namespace: st.HorizontalRunnerAutoscaler.Namespace,
|
||||||
|
Name: st.HorizontalRunnerAutoscaler.Name,
|
||||||
|
}
|
||||||
|
b, ok := batches[nsName]
|
||||||
|
if !ok {
|
||||||
|
b = batchScaleOperation{
|
||||||
|
namespacedName: nsName,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.scaleOps = append(b.scaleOps, scaleOperation{
|
||||||
|
log: *st.log,
|
||||||
|
trigger: st.ScaleUpTrigger,
|
||||||
|
})
|
||||||
|
batches[nsName] = b
|
||||||
|
ops++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.V(2).Info("Batch worker dequeued operations", "ops", ops, "batches", len(batches))
|
||||||
|
|
||||||
|
retry:
|
||||||
|
for i := 0; ; i++ {
|
||||||
|
failed := map[types.NamespacedName]batchScaleOperation{}
|
||||||
|
|
||||||
|
for nsName, b := range batches {
|
||||||
|
b := b
|
||||||
|
if err := s.batchScale(context.Background(), b); err != nil {
|
||||||
|
log.V(2).Info("Failed to scale due to error", "error", err)
|
||||||
|
failed[nsName] = b
|
||||||
|
} else {
|
||||||
|
log.V(2).Info("Successfully ran batch scale", "hra", b.namespacedName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(failed) == 0 {
|
||||||
|
break retry
|
||||||
|
}
|
||||||
|
|
||||||
|
batches = failed
|
||||||
|
|
||||||
|
delay := 16 * time.Second
|
||||||
|
if i < len(expBackoff) {
|
||||||
|
delay = expBackoff[i]
|
||||||
|
}
|
||||||
|
time.Sleep(delay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
})
|
||||||
|
|
||||||
|
s.queue <- st
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *batchScaler) batchScale(ctx context.Context, batch batchScaleOperation) error {
|
||||||
|
var hra v1alpha1.HorizontalRunnerAutoscaler
|
||||||
|
|
||||||
|
if err := s.Client.Get(ctx, batch.namespacedName, &hra); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
copy := hra.DeepCopy()
|
||||||
|
|
||||||
|
copy.Spec.CapacityReservations = getValidCapacityReservations(copy)
|
||||||
|
|
||||||
|
var added, completed int
|
||||||
|
|
||||||
|
for _, scale := range batch.scaleOps {
|
||||||
|
amount := 1
|
||||||
|
|
||||||
|
if scale.trigger.Amount != 0 {
|
||||||
|
amount = scale.trigger.Amount
|
||||||
|
}
|
||||||
|
|
||||||
|
scale.log.V(2).Info("Adding capacity reservation", "amount", amount)
|
||||||
|
|
||||||
|
if amount > 0 {
|
||||||
|
now := time.Now()
|
||||||
|
copy.Spec.CapacityReservations = append(copy.Spec.CapacityReservations, v1alpha1.CapacityReservation{
|
||||||
|
EffectiveTime: metav1.Time{Time: now},
|
||||||
|
ExpirationTime: metav1.Time{Time: now.Add(scale.trigger.Duration.Duration)},
|
||||||
|
Replicas: amount,
|
||||||
|
})
|
||||||
|
|
||||||
|
added += amount
|
||||||
|
} else if amount < 0 {
|
||||||
|
var reservations []v1alpha1.CapacityReservation
|
||||||
|
|
||||||
|
var found bool
|
||||||
|
|
||||||
|
for _, r := range copy.Spec.CapacityReservations {
|
||||||
|
if !found && r.Replicas+amount == 0 {
|
||||||
|
found = true
|
||||||
|
} else {
|
||||||
|
reservations = append(reservations, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
copy.Spec.CapacityReservations = reservations
|
||||||
|
|
||||||
|
completed += amount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
before := len(hra.Spec.CapacityReservations)
|
||||||
|
expired := before - len(copy.Spec.CapacityReservations)
|
||||||
|
after := len(copy.Spec.CapacityReservations)
|
||||||
|
|
||||||
|
s.Log.V(1).Info(
|
||||||
|
fmt.Sprintf("Updating hra %s for capacityReservations update", hra.Name),
|
||||||
|
"before", before,
|
||||||
|
"expired", expired,
|
||||||
|
"added", added,
|
||||||
|
"completed", completed,
|
||||||
|
"after", after,
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := s.Client.Update(ctx, copy); err != nil {
|
||||||
|
return fmt.Errorf("updating horizontalrunnerautoscaler to add capacity reservation: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -20,17 +20,17 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||||
|
|
||||||
"github.com/go-logr/logr"
|
"github.com/go-logr/logr"
|
||||||
gogithub "github.com/google/go-github/v39/github"
|
gogithub "github.com/google/go-github/v47/github"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/client-go/tools/record"
|
"k8s.io/client-go/tools/record"
|
||||||
ctrl "sigs.k8s.io/controller-runtime"
|
ctrl "sigs.k8s.io/controller-runtime"
|
||||||
@@ -46,6 +46,8 @@ const (
|
|||||||
|
|
||||||
keyPrefixEnterprise = "enterprises/"
|
keyPrefixEnterprise = "enterprises/"
|
||||||
keyRunnerGroup = "/group/"
|
keyRunnerGroup = "/group/"
|
||||||
|
|
||||||
|
DefaultQueueLimit = 100
|
||||||
)
|
)
|
||||||
|
|
||||||
// HorizontalRunnerAutoscalerGitHubWebhook autoscales a HorizontalRunnerAutoscaler and the RunnerDeployment on each
|
// HorizontalRunnerAutoscalerGitHubWebhook autoscales a HorizontalRunnerAutoscaler and the RunnerDeployment on each
|
||||||
@@ -68,6 +70,13 @@ type HorizontalRunnerAutoscalerGitHubWebhook struct {
|
|||||||
// Set to empty for letting it watch for all namespaces.
|
// Set to empty for letting it watch for all namespaces.
|
||||||
Namespace string
|
Namespace string
|
||||||
Name string
|
Name string
|
||||||
|
|
||||||
|
// QueueLimit is the maximum length of the bounded queue of scale targets and their associated operations
|
||||||
|
// A scale target is enqueued on each retrieval of each eligible webhook event, so that it is processed asynchronously.
|
||||||
|
QueueLimit int
|
||||||
|
|
||||||
|
worker *worker
|
||||||
|
workerInit sync.Once
|
||||||
}
|
}
|
||||||
|
|
||||||
func (autoscaler *HorizontalRunnerAutoscalerGitHubWebhook) Reconcile(_ context.Context, request reconcile.Request) (reconcile.Result, error) {
|
func (autoscaler *HorizontalRunnerAutoscalerGitHubWebhook) Reconcile(_ context.Context, request reconcile.Request) (reconcile.Result, error) {
|
||||||
@@ -122,7 +131,7 @@ func (autoscaler *HorizontalRunnerAutoscalerGitHubWebhook) Handle(w http.Respons
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
payload, err = ioutil.ReadAll(r.Body)
|
payload, err = io.ReadAll(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
autoscaler.Log.Error(err, "error reading request body")
|
autoscaler.Log.Error(err, "error reading request body")
|
||||||
|
|
||||||
@@ -312,9 +321,19 @@ func (autoscaler *HorizontalRunnerAutoscalerGitHubWebhook) Handle(w http.Respons
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := autoscaler.tryScale(context.TODO(), target); err != nil {
|
autoscaler.workerInit.Do(func() {
|
||||||
log.Error(err, "could not scale up")
|
batchScaler := newBatchScaler(context.Background(), autoscaler.Client, autoscaler.Log)
|
||||||
|
|
||||||
|
queueLimit := autoscaler.QueueLimit
|
||||||
|
if queueLimit == 0 {
|
||||||
|
queueLimit = DefaultQueueLimit
|
||||||
|
}
|
||||||
|
autoscaler.worker = newWorker(context.Background(), queueLimit, batchScaler.Add)
|
||||||
|
})
|
||||||
|
|
||||||
|
target.log = &log
|
||||||
|
if ok := autoscaler.worker.Add(target); !ok {
|
||||||
|
log.Error(err, "Could not scale up due to queue full")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -383,6 +402,8 @@ func matchTriggerConditionAgainstEvent(types []string, eventAction *string) bool
|
|||||||
type ScaleTarget struct {
|
type ScaleTarget struct {
|
||||||
v1alpha1.HorizontalRunnerAutoscaler
|
v1alpha1.HorizontalRunnerAutoscaler
|
||||||
v1alpha1.ScaleUpTrigger
|
v1alpha1.ScaleUpTrigger
|
||||||
|
|
||||||
|
log *logr.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func (autoscaler *HorizontalRunnerAutoscalerGitHubWebhook) searchScaleTargets(hras []v1alpha1.HorizontalRunnerAutoscaler, f func(v1alpha1.ScaleUpTrigger) bool) []ScaleTarget {
|
func (autoscaler *HorizontalRunnerAutoscalerGitHubWebhook) searchScaleTargets(hras []v1alpha1.HorizontalRunnerAutoscaler, f func(v1alpha1.ScaleUpTrigger) bool) []ScaleTarget {
|
||||||
@@ -501,6 +522,7 @@ func (autoscaler *HorizontalRunnerAutoscalerGitHubWebhook) getScaleUpTargetWithF
|
|||||||
if autoscaler.GitHubClient != nil {
|
if autoscaler.GitHubClient != nil {
|
||||||
simu := &simulator.Simulator{
|
simu := &simulator.Simulator{
|
||||||
Client: autoscaler.GitHubClient,
|
Client: autoscaler.GitHubClient,
|
||||||
|
Log: log,
|
||||||
}
|
}
|
||||||
// Get available organization runner groups and enterprise runner groups for a repository
|
// Get available organization runner groups and enterprise runner groups for a repository
|
||||||
// These are the sum of runner groups with repository access = All repositories and runner groups
|
// These are the sum of runner groups with repository access = All repositories and runner groups
|
||||||
@@ -770,63 +792,6 @@ HRA:
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (autoscaler *HorizontalRunnerAutoscalerGitHubWebhook) tryScale(ctx context.Context, target *ScaleTarget) error {
|
|
||||||
if target == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
copy := target.HorizontalRunnerAutoscaler.DeepCopy()
|
|
||||||
|
|
||||||
amount := 1
|
|
||||||
|
|
||||||
if target.ScaleUpTrigger.Amount != 0 {
|
|
||||||
amount = target.ScaleUpTrigger.Amount
|
|
||||||
}
|
|
||||||
|
|
||||||
capacityReservations := getValidCapacityReservations(copy)
|
|
||||||
|
|
||||||
if amount > 0 {
|
|
||||||
now := time.Now()
|
|
||||||
copy.Spec.CapacityReservations = append(capacityReservations, v1alpha1.CapacityReservation{
|
|
||||||
EffectiveTime: metav1.Time{Time: now},
|
|
||||||
ExpirationTime: metav1.Time{Time: now.Add(target.ScaleUpTrigger.Duration.Duration)},
|
|
||||||
Replicas: amount,
|
|
||||||
})
|
|
||||||
} else if amount < 0 {
|
|
||||||
var reservations []v1alpha1.CapacityReservation
|
|
||||||
|
|
||||||
var found bool
|
|
||||||
|
|
||||||
for _, r := range capacityReservations {
|
|
||||||
if !found && r.Replicas+amount == 0 {
|
|
||||||
found = true
|
|
||||||
} else {
|
|
||||||
reservations = append(reservations, r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
copy.Spec.CapacityReservations = reservations
|
|
||||||
}
|
|
||||||
|
|
||||||
before := len(target.HorizontalRunnerAutoscaler.Spec.CapacityReservations)
|
|
||||||
expired := before - len(capacityReservations)
|
|
||||||
after := len(copy.Spec.CapacityReservations)
|
|
||||||
|
|
||||||
autoscaler.Log.V(1).Info(
|
|
||||||
fmt.Sprintf("Patching hra %s for capacityReservations update", target.HorizontalRunnerAutoscaler.Name),
|
|
||||||
"before", before,
|
|
||||||
"expired", expired,
|
|
||||||
"amount", amount,
|
|
||||||
"after", after,
|
|
||||||
)
|
|
||||||
|
|
||||||
if err := autoscaler.Client.Patch(ctx, copy, client.MergeFrom(&target.HorizontalRunnerAutoscaler)); err != nil {
|
|
||||||
return fmt.Errorf("patching horizontalrunnerautoscaler to add capacity reservation: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getValidCapacityReservations(autoscaler *v1alpha1.HorizontalRunnerAutoscaler) []v1alpha1.CapacityReservation {
|
func getValidCapacityReservations(autoscaler *v1alpha1.HorizontalRunnerAutoscaler) []v1alpha1.CapacityReservation {
|
||||||
var capacityReservations []v1alpha1.CapacityReservation
|
var capacityReservations []v1alpha1.CapacityReservation
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package controllers
|
|||||||
import (
|
import (
|
||||||
"github.com/actions-runner-controller/actions-runner-controller/api/v1alpha1"
|
"github.com/actions-runner-controller/actions-runner-controller/api/v1alpha1"
|
||||||
"github.com/actions-runner-controller/actions-runner-controller/pkg/actionsglob"
|
"github.com/actions-runner-controller/actions-runner-controller/pkg/actionsglob"
|
||||||
"github.com/google/go-github/v39/github"
|
"github.com/google/go-github/v47/github"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (autoscaler *HorizontalRunnerAutoscalerGitHubWebhook) MatchCheckRunEvent(event *github.CheckRunEvent) func(scaleUpTrigger v1alpha1.ScaleUpTrigger) bool {
|
func (autoscaler *HorizontalRunnerAutoscalerGitHubWebhook) MatchCheckRunEvent(event *github.CheckRunEvent) func(scaleUpTrigger v1alpha1.ScaleUpTrigger) bool {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package controllers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/actions-runner-controller/actions-runner-controller/api/v1alpha1"
|
"github.com/actions-runner-controller/actions-runner-controller/api/v1alpha1"
|
||||||
"github.com/google/go-github/v39/github"
|
"github.com/google/go-github/v47/github"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (autoscaler *HorizontalRunnerAutoscalerGitHubWebhook) MatchPullRequestEvent(event *github.PullRequestEvent) func(scaleUpTrigger v1alpha1.ScaleUpTrigger) bool {
|
func (autoscaler *HorizontalRunnerAutoscalerGitHubWebhook) MatchPullRequestEvent(event *github.PullRequestEvent) func(scaleUpTrigger v1alpha1.ScaleUpTrigger) bool {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package controllers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/actions-runner-controller/actions-runner-controller/api/v1alpha1"
|
"github.com/actions-runner-controller/actions-runner-controller/api/v1alpha1"
|
||||||
"github.com/google/go-github/v39/github"
|
"github.com/google/go-github/v47/github"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (autoscaler *HorizontalRunnerAutoscalerGitHubWebhook) MatchPushEvent(event *github.PushEvent) func(scaleUpTrigger v1alpha1.ScaleUpTrigger) bool {
|
func (autoscaler *HorizontalRunnerAutoscalerGitHubWebhook) MatchPushEvent(event *github.PushEvent) func(scaleUpTrigger v1alpha1.ScaleUpTrigger) bool {
|
||||||
@@ -15,10 +15,6 @@ func (autoscaler *HorizontalRunnerAutoscalerGitHubWebhook) MatchPushEvent(event
|
|||||||
|
|
||||||
push := g.Push
|
push := g.Push
|
||||||
|
|
||||||
if push == nil {
|
return push != nil
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"net/url"
|
"net/url"
|
||||||
@@ -15,7 +14,7 @@ import (
|
|||||||
|
|
||||||
actionsv1alpha1 "github.com/actions-runner-controller/actions-runner-controller/api/v1alpha1"
|
actionsv1alpha1 "github.com/actions-runner-controller/actions-runner-controller/api/v1alpha1"
|
||||||
"github.com/go-logr/logr"
|
"github.com/go-logr/logr"
|
||||||
"github.com/google/go-github/v39/github"
|
"github.com/google/go-github/v47/github"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
|
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
|
||||||
@@ -504,7 +503,7 @@ func testServerWithInitObjs(t *testing.T, eventType string, event interface{}, w
|
|||||||
|
|
||||||
hraWebhook := &HorizontalRunnerAutoscalerGitHubWebhook{}
|
hraWebhook := &HorizontalRunnerAutoscalerGitHubWebhook{}
|
||||||
|
|
||||||
client := fake.NewFakeClientWithScheme(sc, initObjs...)
|
client := fake.NewClientBuilder().WithScheme(sc).WithRuntimeObjects(initObjs...).Build()
|
||||||
|
|
||||||
logs := installTestLogger(hraWebhook)
|
logs := installTestLogger(hraWebhook)
|
||||||
|
|
||||||
@@ -537,7 +536,7 @@ func testServerWithInitObjs(t *testing.T, eventType string, event interface{}, w
|
|||||||
t.Error("status:", resp.StatusCode)
|
t.Error("status:", resp.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
respBody, err := ioutil.ReadAll(resp.Body)
|
respBody, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -575,7 +574,7 @@ func sendWebhook(server *httptest.Server, eventType string, event interface{}) (
|
|||||||
"X-GitHub-Event": {eventType},
|
"X-GitHub-Event": {eventType},
|
||||||
"Content-Type": {"application/json"},
|
"Content-Type": {"application/json"},
|
||||||
},
|
},
|
||||||
Body: ioutil.NopCloser(bytes.NewBuffer(reqBody)),
|
Body: io.NopCloser(bytes.NewBuffer(reqBody)),
|
||||||
}
|
}
|
||||||
|
|
||||||
return http.DefaultClient.Do(req)
|
return http.DefaultClient.Do(req)
|
||||||
@@ -607,7 +606,7 @@ func (l *testLogSink) Info(_ int, msg string, kvs ...interface{}) {
|
|||||||
fmt.Fprintf(l.writer, "\n")
|
fmt.Fprintf(l.writer, "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (_ *testLogSink) Enabled(level int) bool {
|
func (*testLogSink) Enabled(level int) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
55
controllers/horizontal_runner_autoscaler_webhook_worker.go
Normal file
55
controllers/horizontal_runner_autoscaler_webhook_worker.go
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// worker is a worker that has a non-blocking bounded queue of scale targets, dequeues scale target and executes the scale operation one by one.
|
||||||
|
type worker struct {
|
||||||
|
scaleTargetQueue chan *ScaleTarget
|
||||||
|
work func(*ScaleTarget)
|
||||||
|
done chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newWorker(ctx context.Context, queueLimit int, work func(*ScaleTarget)) *worker {
|
||||||
|
w := &worker{
|
||||||
|
scaleTargetQueue: make(chan *ScaleTarget, queueLimit),
|
||||||
|
work: work,
|
||||||
|
done: make(chan struct{}),
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer close(w.done)
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case t := <-w.scaleTargetQueue:
|
||||||
|
work(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the scale target to the bounded queue, returning the result as a bool value. It returns true on successful enqueue, and returns false otherwise.
|
||||||
|
// When returned false, the queue is already full so the enqueue operation must be retried later.
|
||||||
|
// If the enqueue was triggered by an external source and there's no intermediate queue that we can use,
|
||||||
|
// you must instruct the source to resend the original request later.
|
||||||
|
// In case you're building a webhook server around this worker, this means that you must return a http error to the webhook server,
|
||||||
|
// so that (hopefully) the sender can resend the webhook event later, or at least the human operator can notice or be notified about the
|
||||||
|
// webhook develiery failure so that a manual retry can be done later.
|
||||||
|
func (w *worker) Add(st *ScaleTarget) bool {
|
||||||
|
select {
|
||||||
|
case w.scaleTargetQueue <- st:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *worker) Done() chan struct{} {
|
||||||
|
return w.done
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestWorker_Add(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
w := newWorker(ctx, 2, func(st *ScaleTarget) {})
|
||||||
|
require.True(t, w.Add(&ScaleTarget{}))
|
||||||
|
require.True(t, w.Add(&ScaleTarget{}))
|
||||||
|
require.False(t, w.Add(&ScaleTarget{}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWorker_Work(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
var count int
|
||||||
|
|
||||||
|
w := newWorker(ctx, 1, func(st *ScaleTarget) {
|
||||||
|
count++
|
||||||
|
cancel()
|
||||||
|
})
|
||||||
|
require.True(t, w.Add(&ScaleTarget{}))
|
||||||
|
require.False(t, w.Add(&ScaleTarget{}))
|
||||||
|
|
||||||
|
<-w.Done()
|
||||||
|
|
||||||
|
require.Equal(t, count, 1)
|
||||||
|
}
|
||||||
@@ -24,7 +24,6 @@ import (
|
|||||||
|
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
|
||||||
"github.com/actions-runner-controller/actions-runner-controller/github"
|
|
||||||
"github.com/go-logr/logr"
|
"github.com/go-logr/logr"
|
||||||
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
@@ -38,6 +37,7 @@ import (
|
|||||||
|
|
||||||
"github.com/actions-runner-controller/actions-runner-controller/api/v1alpha1"
|
"github.com/actions-runner-controller/actions-runner-controller/api/v1alpha1"
|
||||||
"github.com/actions-runner-controller/actions-runner-controller/controllers/metrics"
|
"github.com/actions-runner-controller/actions-runner-controller/controllers/metrics"
|
||||||
|
arcgithub "github.com/actions-runner-controller/actions-runner-controller/github"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -47,11 +47,10 @@ const (
|
|||||||
// HorizontalRunnerAutoscalerReconciler reconciles a HorizontalRunnerAutoscaler object
|
// HorizontalRunnerAutoscalerReconciler reconciles a HorizontalRunnerAutoscaler object
|
||||||
type HorizontalRunnerAutoscalerReconciler struct {
|
type HorizontalRunnerAutoscalerReconciler struct {
|
||||||
client.Client
|
client.Client
|
||||||
GitHubClient *github.Client
|
GitHubClient *MultiGitHubClient
|
||||||
Log logr.Logger
|
Log logr.Logger
|
||||||
Recorder record.EventRecorder
|
Recorder record.EventRecorder
|
||||||
Scheme *runtime.Scheme
|
Scheme *runtime.Scheme
|
||||||
CacheDuration time.Duration
|
|
||||||
DefaultScaleDownDelay time.Duration
|
DefaultScaleDownDelay time.Duration
|
||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
@@ -73,6 +72,8 @@ func (r *HorizontalRunnerAutoscalerReconciler) Reconcile(ctx context.Context, re
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !hra.ObjectMeta.DeletionTimestamp.IsZero() {
|
if !hra.ObjectMeta.DeletionTimestamp.IsZero() {
|
||||||
|
r.GitHubClient.DeinitForHRA(&hra)
|
||||||
|
|
||||||
return ctrl.Result{}, nil
|
return ctrl.Result{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -310,7 +311,12 @@ func (r *HorizontalRunnerAutoscalerReconciler) reconcile(ctx context.Context, re
|
|||||||
return ctrl.Result{}, err
|
return ctrl.Result{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
newDesiredReplicas, err := r.computeReplicasWithCache(log, now, st, hra, minReplicas)
|
ghc, err := r.GitHubClient.InitForHRA(context.Background(), &hra)
|
||||||
|
if err != nil {
|
||||||
|
return ctrl.Result{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
newDesiredReplicas, err := r.computeReplicasWithCache(ghc, log, now, st, hra, minReplicas)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.Recorder.Event(&hra, corev1.EventTypeNormal, "RunnerAutoscalingFailure", err.Error())
|
r.Recorder.Event(&hra, corev1.EventTypeNormal, "RunnerAutoscalingFailure", err.Error())
|
||||||
|
|
||||||
@@ -461,10 +467,10 @@ func (r *HorizontalRunnerAutoscalerReconciler) getMinReplicas(log logr.Logger, n
|
|||||||
return minReplicas, active, upcoming, nil
|
return minReplicas, active, upcoming, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *HorizontalRunnerAutoscalerReconciler) computeReplicasWithCache(log logr.Logger, now time.Time, st scaleTarget, hra v1alpha1.HorizontalRunnerAutoscaler, minReplicas int) (int, error) {
|
func (r *HorizontalRunnerAutoscalerReconciler) computeReplicasWithCache(ghc *arcgithub.Client, log logr.Logger, now time.Time, st scaleTarget, hra v1alpha1.HorizontalRunnerAutoscaler, minReplicas int) (int, error) {
|
||||||
var suggestedReplicas int
|
var suggestedReplicas int
|
||||||
|
|
||||||
v, err := r.suggestDesiredReplicas(st, hra)
|
v, err := r.suggestDesiredReplicas(ghc, st, hra)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
github2 "github.com/actions-runner-controller/actions-runner-controller/github"
|
github2 "github.com/actions-runner-controller/actions-runner-controller/github"
|
||||||
"github.com/google/go-github/v39/github"
|
"github.com/google/go-github/v47/github"
|
||||||
|
|
||||||
"github.com/actions-runner-controller/actions-runner-controller/github/fake"
|
"github.com/actions-runner-controller/actions-runner-controller/github/fake"
|
||||||
|
|
||||||
@@ -99,12 +99,14 @@ func SetupIntegrationTest(ctx2 context.Context) *testEnvironment {
|
|||||||
return fmt.Sprintf("%s%s", ns.Name, name)
|
return fmt.Sprintf("%s%s", ns.Name, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
multiClient := NewMultiGitHubClient(mgr.GetClient(), env.ghClient)
|
||||||
|
|
||||||
runnerController := &RunnerReconciler{
|
runnerController := &RunnerReconciler{
|
||||||
Client: mgr.GetClient(),
|
Client: mgr.GetClient(),
|
||||||
Scheme: scheme.Scheme,
|
Scheme: scheme.Scheme,
|
||||||
Log: logf.Log,
|
Log: logf.Log,
|
||||||
Recorder: mgr.GetEventRecorderFor("runnerreplicaset-controller"),
|
Recorder: mgr.GetEventRecorderFor("runnerreplicaset-controller"),
|
||||||
GitHubClient: env.ghClient,
|
GitHubClient: multiClient,
|
||||||
RunnerImage: "example/runner:test",
|
RunnerImage: "example/runner:test",
|
||||||
DockerImage: "example/docker:test",
|
DockerImage: "example/docker:test",
|
||||||
Name: controllerName("runner"),
|
Name: controllerName("runner"),
|
||||||
@@ -116,12 +118,11 @@ func SetupIntegrationTest(ctx2 context.Context) *testEnvironment {
|
|||||||
Expect(err).NotTo(HaveOccurred(), "failed to setup runner controller")
|
Expect(err).NotTo(HaveOccurred(), "failed to setup runner controller")
|
||||||
|
|
||||||
replicasetController := &RunnerReplicaSetReconciler{
|
replicasetController := &RunnerReplicaSetReconciler{
|
||||||
Client: mgr.GetClient(),
|
Client: mgr.GetClient(),
|
||||||
Scheme: scheme.Scheme,
|
Scheme: scheme.Scheme,
|
||||||
Log: logf.Log,
|
Log: logf.Log,
|
||||||
Recorder: mgr.GetEventRecorderFor("runnerreplicaset-controller"),
|
Recorder: mgr.GetEventRecorderFor("runnerreplicaset-controller"),
|
||||||
GitHubClient: env.ghClient,
|
Name: controllerName("runnerreplicaset"),
|
||||||
Name: controllerName("runnerreplicaset"),
|
|
||||||
}
|
}
|
||||||
err = replicasetController.SetupWithManager(mgr)
|
err = replicasetController.SetupWithManager(mgr)
|
||||||
Expect(err).NotTo(HaveOccurred(), "failed to setup runnerreplicaset controller")
|
Expect(err).NotTo(HaveOccurred(), "failed to setup runnerreplicaset controller")
|
||||||
@@ -137,13 +138,12 @@ func SetupIntegrationTest(ctx2 context.Context) *testEnvironment {
|
|||||||
Expect(err).NotTo(HaveOccurred(), "failed to setup runnerdeployment controller")
|
Expect(err).NotTo(HaveOccurred(), "failed to setup runnerdeployment controller")
|
||||||
|
|
||||||
autoscalerController := &HorizontalRunnerAutoscalerReconciler{
|
autoscalerController := &HorizontalRunnerAutoscalerReconciler{
|
||||||
Client: mgr.GetClient(),
|
Client: mgr.GetClient(),
|
||||||
Scheme: scheme.Scheme,
|
Scheme: scheme.Scheme,
|
||||||
Log: logf.Log,
|
Log: logf.Log,
|
||||||
GitHubClient: env.ghClient,
|
GitHubClient: multiClient,
|
||||||
Recorder: mgr.GetEventRecorderFor("horizontalrunnerautoscaler-controller"),
|
Recorder: mgr.GetEventRecorderFor("horizontalrunnerautoscaler-controller"),
|
||||||
CacheDuration: 1 * time.Second,
|
Name: controllerName("horizontalrunnerautoscaler"),
|
||||||
Name: controllerName("horizontalrunnerautoscaler"),
|
|
||||||
}
|
}
|
||||||
err = autoscalerController.SetupWithManager(mgr)
|
err = autoscalerController.SetupWithManager(mgr)
|
||||||
Expect(err).NotTo(HaveOccurred(), "failed to setup autoscaler controller")
|
Expect(err).NotTo(HaveOccurred(), "failed to setup autoscaler controller")
|
||||||
@@ -1367,7 +1367,7 @@ func (env *testEnvironment) ExpectRegisteredNumberCountEventuallyEquals(want int
|
|||||||
|
|
||||||
return len(rs)
|
return len(rs)
|
||||||
},
|
},
|
||||||
time.Second*5, time.Millisecond*500).Should(Equal(want), optionalDescriptions...)
|
time.Second*10, time.Millisecond*500).Should(Equal(want), optionalDescriptions...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (env *testEnvironment) SendOrgPullRequestEvent(org, repo, branch, action string) {
|
func (env *testEnvironment) SendOrgPullRequestEvent(org, repo, branch, action string) {
|
||||||
|
|||||||
@@ -7,8 +7,13 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
hraName = "horizontalrunnerautoscaler"
|
hraName = "horizontalrunnerautoscaler"
|
||||||
hraNamespace = "namespace"
|
hraNamespace = "namespace"
|
||||||
|
stEnterprise = "enterprise"
|
||||||
|
stOrganization = "organization"
|
||||||
|
stRepository = "repository"
|
||||||
|
stKind = "kind"
|
||||||
|
stName = "name"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -16,6 +21,16 @@ var (
|
|||||||
horizontalRunnerAutoscalerMinReplicas,
|
horizontalRunnerAutoscalerMinReplicas,
|
||||||
horizontalRunnerAutoscalerMaxReplicas,
|
horizontalRunnerAutoscalerMaxReplicas,
|
||||||
horizontalRunnerAutoscalerDesiredReplicas,
|
horizontalRunnerAutoscalerDesiredReplicas,
|
||||||
|
horizontalRunnerAutoscalerReplicasDesired,
|
||||||
|
horizontalRunnerAutoscalerRunners,
|
||||||
|
horizontalRunnerAutoscalerRunnersRegistered,
|
||||||
|
horizontalRunnerAutoscalerRunnersBusy,
|
||||||
|
horizontalRunnerAutoscalerTerminatingBusy,
|
||||||
|
horizontalRunnerAutoscalerNecessaryReplicas,
|
||||||
|
horizontalRunnerAutoscalerWorkflowRunsCompleted,
|
||||||
|
horizontalRunnerAutoscalerWorkflowRunsInProgress,
|
||||||
|
horizontalRunnerAutoscalerWorkflowRunsQueued,
|
||||||
|
horizontalRunnerAutoscalerWorkflowRunsUnknown,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -41,6 +56,78 @@ var (
|
|||||||
},
|
},
|
||||||
[]string{hraName, hraNamespace},
|
[]string{hraName, hraNamespace},
|
||||||
)
|
)
|
||||||
|
// PercentageRunnersBusy
|
||||||
|
horizontalRunnerAutoscalerReplicasDesired = prometheus.NewGaugeVec(
|
||||||
|
prometheus.GaugeOpts{
|
||||||
|
Name: "horizontalrunnerautoscaler_replicas_desired",
|
||||||
|
Help: "replicas_desired of PercentageRunnersBusy",
|
||||||
|
},
|
||||||
|
[]string{hraName, hraNamespace, stEnterprise, stOrganization, stRepository, stKind, stName},
|
||||||
|
)
|
||||||
|
horizontalRunnerAutoscalerRunners = prometheus.NewGaugeVec(
|
||||||
|
prometheus.GaugeOpts{
|
||||||
|
Name: "horizontalrunnerautoscaler_runners",
|
||||||
|
Help: "num_runners of PercentageRunnersBusy",
|
||||||
|
},
|
||||||
|
[]string{hraName, hraNamespace, stEnterprise, stOrganization, stRepository, stKind, stName},
|
||||||
|
)
|
||||||
|
horizontalRunnerAutoscalerRunnersRegistered = prometheus.NewGaugeVec(
|
||||||
|
prometheus.GaugeOpts{
|
||||||
|
Name: "horizontalrunnerautoscaler_runners_registered",
|
||||||
|
Help: "num_runners_registered of PercentageRunnersBusy",
|
||||||
|
},
|
||||||
|
[]string{hraName, hraNamespace, stEnterprise, stOrganization, stRepository, stKind, stName},
|
||||||
|
)
|
||||||
|
horizontalRunnerAutoscalerRunnersBusy = prometheus.NewGaugeVec(
|
||||||
|
prometheus.GaugeOpts{
|
||||||
|
Name: "horizontalrunnerautoscaler_runners_busy",
|
||||||
|
Help: "num_runners_busy of PercentageRunnersBusy",
|
||||||
|
},
|
||||||
|
[]string{hraName, hraNamespace, stEnterprise, stOrganization, stRepository, stKind, stName},
|
||||||
|
)
|
||||||
|
horizontalRunnerAutoscalerTerminatingBusy = prometheus.NewGaugeVec(
|
||||||
|
prometheus.GaugeOpts{
|
||||||
|
Name: "horizontalrunnerautoscaler_terminating_busy",
|
||||||
|
Help: "num_terminating_busy of PercentageRunnersBusy",
|
||||||
|
},
|
||||||
|
[]string{hraName, hraNamespace, stEnterprise, stOrganization, stRepository, stKind, stName},
|
||||||
|
)
|
||||||
|
// QueuedAndInProgressWorkflowRuns
|
||||||
|
horizontalRunnerAutoscalerNecessaryReplicas = prometheus.NewGaugeVec(
|
||||||
|
prometheus.GaugeOpts{
|
||||||
|
Name: "horizontalrunnerautoscaler_necessary_replicas",
|
||||||
|
Help: "necessary_replicas of QueuedAndInProgressWorkflowRuns",
|
||||||
|
},
|
||||||
|
[]string{hraName, hraNamespace, stEnterprise, stOrganization, stRepository, stKind, stName},
|
||||||
|
)
|
||||||
|
horizontalRunnerAutoscalerWorkflowRunsCompleted = prometheus.NewGaugeVec(
|
||||||
|
prometheus.GaugeOpts{
|
||||||
|
Name: "horizontalrunnerautoscaler_workflow_runs_completed",
|
||||||
|
Help: "workflow_runs_completed of QueuedAndInProgressWorkflowRuns",
|
||||||
|
},
|
||||||
|
[]string{hraName, hraNamespace, stEnterprise, stOrganization, stRepository, stKind, stName},
|
||||||
|
)
|
||||||
|
horizontalRunnerAutoscalerWorkflowRunsInProgress = prometheus.NewGaugeVec(
|
||||||
|
prometheus.GaugeOpts{
|
||||||
|
Name: "horizontalrunnerautoscaler_workflow_runs_in_progress",
|
||||||
|
Help: "workflow_runs_in_progress of QueuedAndInProgressWorkflowRuns",
|
||||||
|
},
|
||||||
|
[]string{hraName, hraNamespace, stEnterprise, stOrganization, stRepository, stKind, stName},
|
||||||
|
)
|
||||||
|
horizontalRunnerAutoscalerWorkflowRunsQueued = prometheus.NewGaugeVec(
|
||||||
|
prometheus.GaugeOpts{
|
||||||
|
Name: "horizontalrunnerautoscaler_workflow_runs_queued",
|
||||||
|
Help: "workflow_runs_queued of QueuedAndInProgressWorkflowRuns",
|
||||||
|
},
|
||||||
|
[]string{hraName, hraNamespace, stEnterprise, stOrganization, stRepository, stKind, stName},
|
||||||
|
)
|
||||||
|
horizontalRunnerAutoscalerWorkflowRunsUnknown = prometheus.NewGaugeVec(
|
||||||
|
prometheus.GaugeOpts{
|
||||||
|
Name: "horizontalrunnerautoscaler_workflow_runs_unknown",
|
||||||
|
Help: "workflow_runs_unknown of QueuedAndInProgressWorkflowRuns",
|
||||||
|
},
|
||||||
|
[]string{hraName, hraNamespace, stEnterprise, stOrganization, stRepository, stKind, stName},
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
func SetHorizontalRunnerAutoscalerSpec(o metav1.ObjectMeta, spec v1alpha1.HorizontalRunnerAutoscalerSpec) {
|
func SetHorizontalRunnerAutoscalerSpec(o metav1.ObjectMeta, spec v1alpha1.HorizontalRunnerAutoscalerSpec) {
|
||||||
@@ -65,3 +152,61 @@ func SetHorizontalRunnerAutoscalerStatus(o metav1.ObjectMeta, status v1alpha1.Ho
|
|||||||
horizontalRunnerAutoscalerDesiredReplicas.With(labels).Set(float64(*status.DesiredReplicas))
|
horizontalRunnerAutoscalerDesiredReplicas.With(labels).Set(float64(*status.DesiredReplicas))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SetHorizontalRunnerAutoscalerPercentageRunnersBusy(
|
||||||
|
o metav1.ObjectMeta,
|
||||||
|
enterprise string,
|
||||||
|
organization string,
|
||||||
|
repository string,
|
||||||
|
kind string,
|
||||||
|
name string,
|
||||||
|
desiredReplicas int,
|
||||||
|
numRunners int,
|
||||||
|
numRunnersRegistered int,
|
||||||
|
numRunnersBusy int,
|
||||||
|
numTerminatingBusy int,
|
||||||
|
) {
|
||||||
|
labels := prometheus.Labels{
|
||||||
|
hraName: o.Name,
|
||||||
|
hraNamespace: o.Namespace,
|
||||||
|
stEnterprise: enterprise,
|
||||||
|
stOrganization: organization,
|
||||||
|
stRepository: repository,
|
||||||
|
stKind: kind,
|
||||||
|
stName: name,
|
||||||
|
}
|
||||||
|
horizontalRunnerAutoscalerReplicasDesired.With(labels).Set(float64(desiredReplicas))
|
||||||
|
horizontalRunnerAutoscalerRunners.With(labels).Set(float64(numRunners))
|
||||||
|
horizontalRunnerAutoscalerRunnersRegistered.With(labels).Set(float64(numRunnersRegistered))
|
||||||
|
horizontalRunnerAutoscalerRunnersBusy.With(labels).Set(float64(numRunnersBusy))
|
||||||
|
horizontalRunnerAutoscalerTerminatingBusy.With(labels).Set(float64(numTerminatingBusy))
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetHorizontalRunnerAutoscalerQueuedAndInProgressWorkflowRuns(
|
||||||
|
o metav1.ObjectMeta,
|
||||||
|
enterprise string,
|
||||||
|
organization string,
|
||||||
|
repository string,
|
||||||
|
kind string,
|
||||||
|
name string,
|
||||||
|
necessaryReplicas int,
|
||||||
|
workflowRunsCompleted int,
|
||||||
|
workflowRunsInProgress int,
|
||||||
|
workflowRunsQueued int,
|
||||||
|
workflowRunsUnknown int,
|
||||||
|
) {
|
||||||
|
labels := prometheus.Labels{
|
||||||
|
hraName: o.Name,
|
||||||
|
hraNamespace: o.Namespace,
|
||||||
|
stEnterprise: enterprise,
|
||||||
|
stOrganization: organization,
|
||||||
|
stRepository: repository,
|
||||||
|
stKind: kind,
|
||||||
|
stName: name,
|
||||||
|
}
|
||||||
|
horizontalRunnerAutoscalerNecessaryReplicas.With(labels).Set(float64(necessaryReplicas))
|
||||||
|
horizontalRunnerAutoscalerWorkflowRunsCompleted.With(labels).Set(float64(workflowRunsCompleted))
|
||||||
|
horizontalRunnerAutoscalerWorkflowRunsInProgress.With(labels).Set(float64(workflowRunsInProgress))
|
||||||
|
horizontalRunnerAutoscalerWorkflowRunsQueued.With(labels).Set(float64(workflowRunsQueued))
|
||||||
|
horizontalRunnerAutoscalerWorkflowRunsUnknown.With(labels).Set(float64(workflowRunsUnknown))
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,12 +10,6 @@ const (
|
|||||||
rsNamespace = "namespace"
|
rsNamespace = "namespace"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
runnerSetMetrics = []prometheus.Collector{
|
|
||||||
runnerSetReplicas,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
runnerSetReplicas = prometheus.NewGaugeVec(
|
runnerSetReplicas = prometheus.NewGaugeVec(
|
||||||
prometheus.GaugeOpts{
|
prometheus.GaugeOpts{
|
||||||
|
|||||||
334
controllers/multi_githubclient.go
Normal file
334
controllers/multi_githubclient.go
Normal file
@@ -0,0 +1,334 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/sha1"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/actions-runner-controller/actions-runner-controller/api/v1alpha1"
|
||||||
|
"github.com/actions-runner-controller/actions-runner-controller/github"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// The api creds scret annotation is added by the runner controller or the runnerset controller according to runner.spec.githubAPICredentialsFrom.secretRef.name,
|
||||||
|
// so that the runner pod controller can share the same GitHub API credentials and the instance of the GitHub API client with the upstream controllers.
|
||||||
|
annotationKeyGitHubAPICredsSecret = annotationKeyPrefix + "github-api-creds-secret"
|
||||||
|
)
|
||||||
|
|
||||||
|
type runnerOwnerRef struct {
|
||||||
|
// kind is either StatefulSet or Runner, and populated via the owner reference in the runner pod controller or via the reconcilation target's kind in
|
||||||
|
// runnerset and runner controllers.
|
||||||
|
kind string
|
||||||
|
ns, name string
|
||||||
|
}
|
||||||
|
|
||||||
|
type secretRef struct {
|
||||||
|
ns, name string
|
||||||
|
}
|
||||||
|
|
||||||
|
// savedClient is the each cache entry that contains the client for the specific set of credentials,
|
||||||
|
// like a PAT or a pair of key and cert.
|
||||||
|
// the `hash` is a part of the savedClient not the key because we are going to keep only the client for the latest creds
|
||||||
|
// in case the operator updated the k8s secret containing the credentials.
|
||||||
|
type savedClient struct {
|
||||||
|
hash string
|
||||||
|
|
||||||
|
// refs is the map of all the objects that references this client, used for reference counting to gc
|
||||||
|
// the client if unneeded.
|
||||||
|
refs map[runnerOwnerRef]struct{}
|
||||||
|
|
||||||
|
*github.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
type resourceReader interface {
|
||||||
|
Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type MultiGitHubClient struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
|
||||||
|
client resourceReader
|
||||||
|
|
||||||
|
githubClient *github.Client
|
||||||
|
|
||||||
|
// The saved client is freed once all its dependents disappear, or the contents of the secret changed.
|
||||||
|
// We track dependents via a golang map embedded within the savedClient struct. Each dependent is checked on their respective Kubernetes finalizer,
|
||||||
|
// so that we won't miss any dependent's termination.
|
||||||
|
// The change is the secret is determined using the hash of its contents.
|
||||||
|
clients map[secretRef]savedClient
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMultiGitHubClient(client resourceReader, githubClient *github.Client) *MultiGitHubClient {
|
||||||
|
return &MultiGitHubClient{
|
||||||
|
client: client,
|
||||||
|
githubClient: githubClient,
|
||||||
|
clients: map[secretRef]savedClient{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init sets up and return the *github.Client for the object.
|
||||||
|
// In case the object (like RunnerDeployment) does not request a custom client, it returns the default client.
|
||||||
|
func (c *MultiGitHubClient) InitForRunnerPod(ctx context.Context, pod *corev1.Pod) (*github.Client, error) {
|
||||||
|
// These 3 default values are used only when the user created the pod directly, not via Runner, RunnerReplicaSet, RunnerDeploment, or RunnerSet resources.
|
||||||
|
ref := refFromRunnerPod(pod)
|
||||||
|
secretName := pod.Annotations[annotationKeyGitHubAPICredsSecret]
|
||||||
|
|
||||||
|
// kind can be any of Pod, Runner, RunnerReplicaSet, RunnerDeployment, or RunnerSet depending on which custom resource the user directly created.
|
||||||
|
return c.initClientWithSecretName(ctx, pod.Namespace, secretName, ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init sets up and return the *github.Client for the object.
|
||||||
|
// In case the object (like RunnerDeployment) does not request a custom client, it returns the default client.
|
||||||
|
func (c *MultiGitHubClient) InitForRunner(ctx context.Context, r *v1alpha1.Runner) (*github.Client, error) {
|
||||||
|
var secretName string
|
||||||
|
if r.Spec.GitHubAPICredentialsFrom != nil {
|
||||||
|
secretName = r.Spec.GitHubAPICredentialsFrom.SecretRef.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// These 3 default values are used only when the user created the runner resource directly, not via RunnerReplicaSet, RunnerDeploment, or RunnerSet resources.
|
||||||
|
ref := refFromRunner(r)
|
||||||
|
if ref.ns != r.Namespace {
|
||||||
|
return nil, fmt.Errorf("referencing github api creds secret from owner in another namespace is not supported yet")
|
||||||
|
}
|
||||||
|
|
||||||
|
// kind can be any of Runner, RunnerReplicaSet, or RunnerDeployment depending on which custom resource the user directly created.
|
||||||
|
return c.initClientWithSecretName(ctx, r.Namespace, secretName, ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init sets up and return the *github.Client for the object.
|
||||||
|
// In case the object (like RunnerDeployment) does not request a custom client, it returns the default client.
|
||||||
|
func (c *MultiGitHubClient) InitForRunnerSet(ctx context.Context, rs *v1alpha1.RunnerSet) (*github.Client, error) {
|
||||||
|
ref := refFromRunnerSet(rs)
|
||||||
|
|
||||||
|
var secretName string
|
||||||
|
if rs.Spec.GitHubAPICredentialsFrom != nil {
|
||||||
|
secretName = rs.Spec.GitHubAPICredentialsFrom.SecretRef.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.initClientWithSecretName(ctx, rs.Namespace, secretName, ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init sets up and return the *github.Client for the object.
|
||||||
|
// In case the object (like RunnerDeployment) does not request a custom client, it returns the default client.
|
||||||
|
func (c *MultiGitHubClient) InitForHRA(ctx context.Context, hra *v1alpha1.HorizontalRunnerAutoscaler) (*github.Client, error) {
|
||||||
|
ref := refFromHorizontalRunnerAutoscaler(hra)
|
||||||
|
|
||||||
|
var secretName string
|
||||||
|
if hra.Spec.GitHubAPICredentialsFrom != nil {
|
||||||
|
secretName = hra.Spec.GitHubAPICredentialsFrom.SecretRef.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.initClientWithSecretName(ctx, hra.Namespace, secretName, ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *MultiGitHubClient) DeinitForRunnerPod(p *corev1.Pod) {
|
||||||
|
secretName := p.Annotations[annotationKeyGitHubAPICredsSecret]
|
||||||
|
c.derefClient(p.Namespace, secretName, refFromRunnerPod(p))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *MultiGitHubClient) DeinitForRunner(r *v1alpha1.Runner) {
|
||||||
|
var secretName string
|
||||||
|
if r.Spec.GitHubAPICredentialsFrom != nil {
|
||||||
|
secretName = r.Spec.GitHubAPICredentialsFrom.SecretRef.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
c.derefClient(r.Namespace, secretName, refFromRunner(r))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *MultiGitHubClient) DeinitForRunnerSet(rs *v1alpha1.RunnerSet) {
|
||||||
|
var secretName string
|
||||||
|
if rs.Spec.GitHubAPICredentialsFrom != nil {
|
||||||
|
secretName = rs.Spec.GitHubAPICredentialsFrom.SecretRef.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
c.derefClient(rs.Namespace, secretName, refFromRunnerSet(rs))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *MultiGitHubClient) DeinitForHRA(hra *v1alpha1.HorizontalRunnerAutoscaler) {
|
||||||
|
var secretName string
|
||||||
|
if hra.Spec.GitHubAPICredentialsFrom != nil {
|
||||||
|
secretName = hra.Spec.GitHubAPICredentialsFrom.SecretRef.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
c.derefClient(hra.Namespace, secretName, refFromHorizontalRunnerAutoscaler(hra))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *MultiGitHubClient) initClientForSecret(secret *corev1.Secret, dependent *runnerOwnerRef) (*savedClient, error) {
|
||||||
|
secRef := secretRef{
|
||||||
|
ns: secret.Namespace,
|
||||||
|
name: secret.Name,
|
||||||
|
}
|
||||||
|
|
||||||
|
cliRef := c.clients[secRef]
|
||||||
|
|
||||||
|
var ks []string
|
||||||
|
|
||||||
|
for k := range secret.Data {
|
||||||
|
ks = append(ks, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.SliceStable(ks, func(i, j int) bool { return ks[i] < ks[j] })
|
||||||
|
|
||||||
|
hash := sha1.New()
|
||||||
|
for _, k := range ks {
|
||||||
|
hash.Write(secret.Data[k])
|
||||||
|
}
|
||||||
|
hashStr := hex.EncodeToString(hash.Sum(nil))
|
||||||
|
|
||||||
|
if cliRef.hash != hashStr {
|
||||||
|
delete(c.clients, secRef)
|
||||||
|
|
||||||
|
conf, err := secretDataToGitHubClientConfig(secret.Data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to the controller-wide setting if EnterpriseURL is not set and the original client is an enterprise client.
|
||||||
|
if conf.EnterpriseURL == "" && c.githubClient.IsEnterprise {
|
||||||
|
conf.EnterpriseURL = c.githubClient.GithubBaseURL
|
||||||
|
}
|
||||||
|
|
||||||
|
cli, err := conf.NewClient()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cliRef = savedClient{
|
||||||
|
hash: hashStr,
|
||||||
|
refs: map[runnerOwnerRef]struct{}{},
|
||||||
|
Client: cli,
|
||||||
|
}
|
||||||
|
|
||||||
|
c.clients[secRef] = cliRef
|
||||||
|
}
|
||||||
|
|
||||||
|
if dependent != nil {
|
||||||
|
c.clients[secRef].refs[*dependent] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &cliRef, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *MultiGitHubClient) initClientWithSecretName(ctx context.Context, ns, secretName string, runRef *runnerOwnerRef) (*github.Client, error) {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
|
||||||
|
if secretName == "" {
|
||||||
|
return c.githubClient, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
secRef := secretRef{
|
||||||
|
ns: ns,
|
||||||
|
name: secretName,
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := c.clients[secRef]; !ok {
|
||||||
|
c.clients[secRef] = savedClient{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var sec corev1.Secret
|
||||||
|
if err := c.client.Get(ctx, types.NamespacedName{Namespace: ns, Name: secretName}, &sec); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
savedClient, err := c.initClientForSecret(&sec, runRef)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return savedClient.Client, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *MultiGitHubClient) derefClient(ns, secretName string, dependent *runnerOwnerRef) {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
|
||||||
|
secRef := secretRef{
|
||||||
|
ns: ns,
|
||||||
|
name: secretName,
|
||||||
|
}
|
||||||
|
|
||||||
|
if dependent != nil {
|
||||||
|
delete(c.clients[secRef].refs, *dependent)
|
||||||
|
}
|
||||||
|
|
||||||
|
cliRef := c.clients[secRef]
|
||||||
|
|
||||||
|
if dependent == nil || len(cliRef.refs) == 0 {
|
||||||
|
delete(c.clients, secRef)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func secretDataToGitHubClientConfig(data map[string][]byte) (*github.Config, error) {
|
||||||
|
var (
|
||||||
|
conf github.Config
|
||||||
|
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
conf.URL = string(data["github_url"])
|
||||||
|
|
||||||
|
conf.UploadURL = string(data["github_upload_url"])
|
||||||
|
|
||||||
|
conf.EnterpriseURL = string(data["github_enterprise_url"])
|
||||||
|
|
||||||
|
conf.RunnerGitHubURL = string(data["github_runner_url"])
|
||||||
|
|
||||||
|
conf.Token = string(data["github_token"])
|
||||||
|
|
||||||
|
appID := string(data["github_app_id"])
|
||||||
|
|
||||||
|
conf.AppID, err = strconv.ParseInt(appID, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
instID := string(data["github_app_installation_id"])
|
||||||
|
|
||||||
|
conf.AppInstallationID, err = strconv.ParseInt(instID, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
conf.AppPrivateKey = string(data["github_app_private_key"])
|
||||||
|
|
||||||
|
return &conf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func refFromRunner(r *v1alpha1.Runner) *runnerOwnerRef {
|
||||||
|
return &runnerOwnerRef{
|
||||||
|
kind: r.Kind,
|
||||||
|
ns: r.Namespace,
|
||||||
|
name: r.Name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func refFromRunnerPod(po *corev1.Pod) *runnerOwnerRef {
|
||||||
|
return &runnerOwnerRef{
|
||||||
|
kind: po.Kind,
|
||||||
|
ns: po.Namespace,
|
||||||
|
name: po.Name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func refFromRunnerSet(rs *v1alpha1.RunnerSet) *runnerOwnerRef {
|
||||||
|
return &runnerOwnerRef{
|
||||||
|
kind: rs.Kind,
|
||||||
|
ns: rs.Namespace,
|
||||||
|
name: rs.Name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func refFromHorizontalRunnerAutoscaler(hra *v1alpha1.HorizontalRunnerAutoscaler) *runnerOwnerRef {
|
||||||
|
return &runnerOwnerRef{
|
||||||
|
kind: hra.Kind,
|
||||||
|
ns: hra.Namespace,
|
||||||
|
name: hra.Name,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,12 +7,45 @@ import (
|
|||||||
"github.com/actions-runner-controller/actions-runner-controller/github"
|
"github.com/actions-runner-controller/actions-runner-controller/github"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
|
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func newWorkGenericEphemeralVolume(t *testing.T, storageReq string) corev1.Volume {
|
||||||
|
GBs, err := resource.ParseQuantity(storageReq)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return corev1.Volume{
|
||||||
|
Name: "work",
|
||||||
|
VolumeSource: corev1.VolumeSource{
|
||||||
|
Ephemeral: &corev1.EphemeralVolumeSource{
|
||||||
|
VolumeClaimTemplate: &corev1.PersistentVolumeClaimTemplate{
|
||||||
|
Spec: corev1.PersistentVolumeClaimSpec{
|
||||||
|
AccessModes: []corev1.PersistentVolumeAccessMode{
|
||||||
|
corev1.ReadWriteOnce,
|
||||||
|
},
|
||||||
|
StorageClassName: strPtr("runner-work-dir"),
|
||||||
|
Resources: corev1.ResourceRequirements{
|
||||||
|
Requests: corev1.ResourceList{
|
||||||
|
corev1.ResourceStorage: GBs,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestNewRunnerPod(t *testing.T) {
|
func TestNewRunnerPod(t *testing.T) {
|
||||||
|
workGenericEphemeralVolume := newWorkGenericEphemeralVolume(t, "10Gi")
|
||||||
|
|
||||||
type testcase struct {
|
type testcase struct {
|
||||||
description string
|
description string
|
||||||
|
|
||||||
@@ -25,7 +58,7 @@ func TestNewRunnerPod(t *testing.T) {
|
|||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
"actions-runner-controller/inject-registration-token": "true",
|
"actions-runner-controller/inject-registration-token": "true",
|
||||||
"runnerset-name": "runner",
|
"actions-runner": "",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Spec: corev1.PodSpec{
|
Spec: corev1.PodSpec{
|
||||||
@@ -94,6 +127,10 @@ func TestNewRunnerPod(t *testing.T) {
|
|||||||
Name: "RUNNER_EPHEMERAL",
|
Name: "RUNNER_EPHEMERAL",
|
||||||
Value: "true",
|
Value: "true",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "RUNNER_STATUS_UPDATE_HOOK",
|
||||||
|
Value: "false",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Name: "DOCKER_HOST",
|
Name: "DOCKER_HOST",
|
||||||
Value: "tcp://localhost:2376",
|
Value: "tcp://localhost:2376",
|
||||||
@@ -106,10 +143,6 @@ func TestNewRunnerPod(t *testing.T) {
|
|||||||
Name: "DOCKER_CERT_PATH",
|
Name: "DOCKER_CERT_PATH",
|
||||||
Value: "/certs/client",
|
Value: "/certs/client",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
Name: "RUNNER_FEATURE_FLAG_EPHEMERAL",
|
|
||||||
Value: "true",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
VolumeMounts: []corev1.VolumeMount{
|
VolumeMounts: []corev1.VolumeMount{
|
||||||
{
|
{
|
||||||
@@ -159,7 +192,7 @@ func TestNewRunnerPod(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
RestartPolicy: corev1.RestartPolicyOnFailure,
|
RestartPolicy: corev1.RestartPolicyNever,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,7 +204,7 @@ func TestNewRunnerPod(t *testing.T) {
|
|||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
"actions-runner-controller/inject-registration-token": "true",
|
"actions-runner-controller/inject-registration-token": "true",
|
||||||
"runnerset-name": "runner",
|
"actions-runner": "",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Spec: corev1.PodSpec{
|
Spec: corev1.PodSpec{
|
||||||
@@ -229,8 +262,8 @@ func TestNewRunnerPod(t *testing.T) {
|
|||||||
Value: "true",
|
Value: "true",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "RUNNER_FEATURE_FLAG_EPHEMERAL",
|
Name: "RUNNER_STATUS_UPDATE_HOOK",
|
||||||
Value: "true",
|
Value: "false",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
VolumeMounts: []corev1.VolumeMount{
|
VolumeMounts: []corev1.VolumeMount{
|
||||||
@@ -245,7 +278,7 @@ func TestNewRunnerPod(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
RestartPolicy: corev1.RestartPolicyOnFailure,
|
RestartPolicy: corev1.RestartPolicyNever,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -253,7 +286,7 @@ func TestNewRunnerPod(t *testing.T) {
|
|||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
"actions-runner-controller/inject-registration-token": "true",
|
"actions-runner-controller/inject-registration-token": "true",
|
||||||
"runnerset-name": "runner",
|
"actions-runner": "",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Spec: corev1.PodSpec{
|
Spec: corev1.PodSpec{
|
||||||
@@ -311,8 +344,8 @@ func TestNewRunnerPod(t *testing.T) {
|
|||||||
Value: "true",
|
Value: "true",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "RUNNER_FEATURE_FLAG_EPHEMERAL",
|
Name: "RUNNER_STATUS_UPDATE_HOOK",
|
||||||
Value: "true",
|
Value: "false",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
VolumeMounts: []corev1.VolumeMount{
|
VolumeMounts: []corev1.VolumeMount{
|
||||||
@@ -327,7 +360,7 @@ func TestNewRunnerPod(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
RestartPolicy: corev1.RestartPolicyOnFailure,
|
RestartPolicy: corev1.RestartPolicyNever,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -400,8 +433,87 @@ func TestNewRunnerPod(t *testing.T) {
|
|||||||
DockerEnabled: boolPtr(false),
|
DockerEnabled: boolPtr(false),
|
||||||
},
|
},
|
||||||
want: newTestPod(dockerDisabled, func(p *corev1.Pod) {
|
want: newTestPod(dockerDisabled, func(p *corev1.Pod) {
|
||||||
// TODO
|
p.Spec.Containers[0].SecurityContext.Privileged = boolPtr(true)
|
||||||
// p.Spec.Containers[0].SecurityContext.Privileged = boolPtr(true)
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Mount generic ephemeral volume onto work (with explicit volumeMount)",
|
||||||
|
template: corev1.Pod{
|
||||||
|
Spec: corev1.PodSpec{
|
||||||
|
Containers: []corev1.Container{
|
||||||
|
{
|
||||||
|
Name: "runner",
|
||||||
|
VolumeMounts: []corev1.VolumeMount{
|
||||||
|
{
|
||||||
|
Name: "work",
|
||||||
|
MountPath: "/runner/_work",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Volumes: []corev1.Volume{
|
||||||
|
workGenericEphemeralVolume,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: newTestPod(base, func(p *corev1.Pod) {
|
||||||
|
p.Spec.Volumes = []corev1.Volume{
|
||||||
|
workGenericEphemeralVolume,
|
||||||
|
{
|
||||||
|
Name: "runner",
|
||||||
|
VolumeSource: corev1.VolumeSource{
|
||||||
|
EmptyDir: &corev1.EmptyDirVolumeSource{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "certs-client",
|
||||||
|
VolumeSource: corev1.VolumeSource{
|
||||||
|
EmptyDir: &corev1.EmptyDirVolumeSource{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
p.Spec.Containers[0].VolumeMounts = []corev1.VolumeMount{
|
||||||
|
{
|
||||||
|
Name: "work",
|
||||||
|
MountPath: "/runner/_work",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "runner",
|
||||||
|
MountPath: "/runner",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "certs-client",
|
||||||
|
MountPath: "/certs/client",
|
||||||
|
ReadOnly: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Mount generic ephemeral volume onto work (without explicit volumeMount)",
|
||||||
|
template: corev1.Pod{
|
||||||
|
Spec: corev1.PodSpec{
|
||||||
|
Volumes: []corev1.Volume{
|
||||||
|
workGenericEphemeralVolume,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: newTestPod(base, func(p *corev1.Pod) {
|
||||||
|
p.Spec.Volumes = []corev1.Volume{
|
||||||
|
workGenericEphemeralVolume,
|
||||||
|
{
|
||||||
|
Name: "runner",
|
||||||
|
VolumeSource: corev1.VolumeSource{
|
||||||
|
EmptyDir: &corev1.EmptyDirVolumeSource{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "certs-client",
|
||||||
|
VolumeSource: corev1.VolumeSource{
|
||||||
|
EmptyDir: &corev1.EmptyDirVolumeSource{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -417,14 +529,20 @@ func TestNewRunnerPod(t *testing.T) {
|
|||||||
for i := range testcases {
|
for i := range testcases {
|
||||||
tc := testcases[i]
|
tc := testcases[i]
|
||||||
t.Run(tc.description, func(t *testing.T) {
|
t.Run(tc.description, func(t *testing.T) {
|
||||||
got, err := newRunnerPod("runner", tc.template, tc.config, defaultRunnerImage, defaultRunnerImagePullSecrets, defaultDockerImage, defaultDockerRegistryMirror, githubBaseURL, false)
|
got, err := newRunnerPod(tc.template, tc.config, defaultRunnerImage, defaultRunnerImagePullSecrets, defaultDockerImage, defaultDockerRegistryMirror, githubBaseURL, false)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, tc.want, got)
|
require.Equal(t, tc.want, got)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func strPtr(s string) *string {
|
||||||
|
return &s
|
||||||
|
}
|
||||||
|
|
||||||
func TestNewRunnerPodFromRunnerController(t *testing.T) {
|
func TestNewRunnerPodFromRunnerController(t *testing.T) {
|
||||||
|
workGenericEphemeralVolume := newWorkGenericEphemeralVolume(t, "10Gi")
|
||||||
|
|
||||||
type testcase struct {
|
type testcase struct {
|
||||||
description string
|
description string
|
||||||
|
|
||||||
@@ -442,7 +560,7 @@ func TestNewRunnerPodFromRunnerController(t *testing.T) {
|
|||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
"actions-runner-controller/inject-registration-token": "true",
|
"actions-runner-controller/inject-registration-token": "true",
|
||||||
"pod-template-hash": "8857b86c7",
|
"pod-template-hash": "8857b86c7",
|
||||||
"runnerset-name": "runner",
|
"actions-runner": "",
|
||||||
},
|
},
|
||||||
OwnerReferences: []metav1.OwnerReference{
|
OwnerReferences: []metav1.OwnerReference{
|
||||||
{
|
{
|
||||||
@@ -520,6 +638,10 @@ func TestNewRunnerPodFromRunnerController(t *testing.T) {
|
|||||||
Name: "RUNNER_EPHEMERAL",
|
Name: "RUNNER_EPHEMERAL",
|
||||||
Value: "true",
|
Value: "true",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "RUNNER_STATUS_UPDATE_HOOK",
|
||||||
|
Value: "false",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Name: "DOCKER_HOST",
|
Name: "DOCKER_HOST",
|
||||||
Value: "tcp://localhost:2376",
|
Value: "tcp://localhost:2376",
|
||||||
@@ -532,10 +654,6 @@ func TestNewRunnerPodFromRunnerController(t *testing.T) {
|
|||||||
Name: "DOCKER_CERT_PATH",
|
Name: "DOCKER_CERT_PATH",
|
||||||
Value: "/certs/client",
|
Value: "/certs/client",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
Name: "RUNNER_FEATURE_FLAG_EPHEMERAL",
|
|
||||||
Value: "true",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
Name: "RUNNER_NAME",
|
Name: "RUNNER_NAME",
|
||||||
Value: "runner",
|
Value: "runner",
|
||||||
@@ -593,7 +711,7 @@ func TestNewRunnerPodFromRunnerController(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
RestartPolicy: corev1.RestartPolicyOnFailure,
|
RestartPolicy: corev1.RestartPolicyNever,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -603,7 +721,7 @@ func TestNewRunnerPodFromRunnerController(t *testing.T) {
|
|||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
"actions-runner-controller/inject-registration-token": "true",
|
"actions-runner-controller/inject-registration-token": "true",
|
||||||
"pod-template-hash": "8857b86c7",
|
"pod-template-hash": "8857b86c7",
|
||||||
"runnerset-name": "runner",
|
"actions-runner": "",
|
||||||
},
|
},
|
||||||
OwnerReferences: []metav1.OwnerReference{
|
OwnerReferences: []metav1.OwnerReference{
|
||||||
{
|
{
|
||||||
@@ -670,8 +788,8 @@ func TestNewRunnerPodFromRunnerController(t *testing.T) {
|
|||||||
Value: "true",
|
Value: "true",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "RUNNER_FEATURE_FLAG_EPHEMERAL",
|
Name: "RUNNER_STATUS_UPDATE_HOOK",
|
||||||
Value: "true",
|
Value: "false",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "RUNNER_NAME",
|
Name: "RUNNER_NAME",
|
||||||
@@ -694,7 +812,7 @@ func TestNewRunnerPodFromRunnerController(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
RestartPolicy: corev1.RestartPolicyOnFailure,
|
RestartPolicy: corev1.RestartPolicyNever,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -704,7 +822,7 @@ func TestNewRunnerPodFromRunnerController(t *testing.T) {
|
|||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
"actions-runner-controller/inject-registration-token": "true",
|
"actions-runner-controller/inject-registration-token": "true",
|
||||||
"pod-template-hash": "8857b86c7",
|
"pod-template-hash": "8857b86c7",
|
||||||
"runnerset-name": "runner",
|
"actions-runner": "",
|
||||||
},
|
},
|
||||||
OwnerReferences: []metav1.OwnerReference{
|
OwnerReferences: []metav1.OwnerReference{
|
||||||
{
|
{
|
||||||
@@ -771,8 +889,8 @@ func TestNewRunnerPodFromRunnerController(t *testing.T) {
|
|||||||
Value: "true",
|
Value: "true",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "RUNNER_FEATURE_FLAG_EPHEMERAL",
|
Name: "RUNNER_STATUS_UPDATE_HOOK",
|
||||||
Value: "true",
|
Value: "false",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "RUNNER_NAME",
|
Name: "RUNNER_NAME",
|
||||||
@@ -795,7 +913,7 @@ func TestNewRunnerPodFromRunnerController(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
RestartPolicy: corev1.RestartPolicyOnFailure,
|
RestartPolicy: corev1.RestartPolicyNever,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -904,7 +1022,97 @@ func TestNewRunnerPodFromRunnerController(t *testing.T) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
want: newTestPod(dockerDisabled, func(p *corev1.Pod) {
|
want: newTestPod(dockerDisabled, func(p *corev1.Pod) {
|
||||||
// p.Spec.Containers[0].SecurityContext.Privileged = boolPtr(true)
|
p.Spec.Containers[0].SecurityContext.Privileged = boolPtr(true)
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Mount generic ephemeral volume onto work (with explicit volumeMount)",
|
||||||
|
runner: arcv1alpha1.Runner{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "runner",
|
||||||
|
},
|
||||||
|
Spec: arcv1alpha1.RunnerSpec{
|
||||||
|
RunnerPodSpec: arcv1alpha1.RunnerPodSpec{
|
||||||
|
Containers: []corev1.Container{
|
||||||
|
{
|
||||||
|
Name: "runner",
|
||||||
|
VolumeMounts: []corev1.VolumeMount{
|
||||||
|
{
|
||||||
|
Name: "work",
|
||||||
|
MountPath: "/runner/_work",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Volumes: []corev1.Volume{
|
||||||
|
workGenericEphemeralVolume,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: newTestPod(base, func(p *corev1.Pod) {
|
||||||
|
p.Spec.Volumes = []corev1.Volume{
|
||||||
|
{
|
||||||
|
Name: "runner",
|
||||||
|
VolumeSource: corev1.VolumeSource{
|
||||||
|
EmptyDir: &corev1.EmptyDirVolumeSource{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "certs-client",
|
||||||
|
VolumeSource: corev1.VolumeSource{
|
||||||
|
EmptyDir: &corev1.EmptyDirVolumeSource{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
workGenericEphemeralVolume,
|
||||||
|
}
|
||||||
|
p.Spec.Containers[0].VolumeMounts = []corev1.VolumeMount{
|
||||||
|
{
|
||||||
|
Name: "work",
|
||||||
|
MountPath: "/runner/_work",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "runner",
|
||||||
|
MountPath: "/runner",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "certs-client",
|
||||||
|
MountPath: "/certs/client",
|
||||||
|
ReadOnly: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Mount generic ephemeral volume onto work (without explicit volumeMount)",
|
||||||
|
runner: arcv1alpha1.Runner{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "runner",
|
||||||
|
},
|
||||||
|
Spec: arcv1alpha1.RunnerSpec{
|
||||||
|
RunnerPodSpec: arcv1alpha1.RunnerPodSpec{
|
||||||
|
Volumes: []corev1.Volume{
|
||||||
|
workGenericEphemeralVolume,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: newTestPod(base, func(p *corev1.Pod) {
|
||||||
|
p.Spec.Volumes = []corev1.Volume{
|
||||||
|
{
|
||||||
|
Name: "runner",
|
||||||
|
VolumeSource: corev1.VolumeSource{
|
||||||
|
EmptyDir: &corev1.EmptyDirVolumeSource{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "certs-client",
|
||||||
|
VolumeSource: corev1.VolumeSource{
|
||||||
|
EmptyDir: &corev1.EmptyDirVolumeSource{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
workGenericEphemeralVolume,
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -923,13 +1131,20 @@ func TestNewRunnerPodFromRunnerController(t *testing.T) {
|
|||||||
|
|
||||||
for i := range testcases {
|
for i := range testcases {
|
||||||
tc := testcases[i]
|
tc := testcases[i]
|
||||||
|
|
||||||
|
rr := &testResourceReader{
|
||||||
|
objects: map[types.NamespacedName]client.Object{},
|
||||||
|
}
|
||||||
|
|
||||||
|
multiClient := NewMultiGitHubClient(rr, &github.Client{GithubBaseURL: githubBaseURL})
|
||||||
|
|
||||||
t.Run(tc.description, func(t *testing.T) {
|
t.Run(tc.description, func(t *testing.T) {
|
||||||
r := &RunnerReconciler{
|
r := &RunnerReconciler{
|
||||||
RunnerImage: defaultRunnerImage,
|
RunnerImage: defaultRunnerImage,
|
||||||
RunnerImagePullSecrets: defaultRunnerImagePullSecrets,
|
RunnerImagePullSecrets: defaultRunnerImagePullSecrets,
|
||||||
DockerImage: defaultDockerImage,
|
DockerImage: defaultDockerImage,
|
||||||
DockerRegistryMirror: defaultDockerRegistryMirror,
|
DockerRegistryMirror: defaultDockerRegistryMirror,
|
||||||
GitHubClient: &github.Client{GithubBaseURL: githubBaseURL},
|
GitHubClient: multiClient,
|
||||||
Scheme: scheme,
|
Scheme: scheme,
|
||||||
}
|
}
|
||||||
got, err := r.newPod(tc.runner)
|
got, err := r.newPod(tc.runner)
|
||||||
|
|||||||
74
controllers/persistent_volume_claim_controller.go
Normal file
74
controllers/persistent_volume_claim_controller.go
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2022 The actions-runner-controller authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/go-logr/logr"
|
||||||
|
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/client-go/tools/record"
|
||||||
|
ctrl "sigs.k8s.io/controller-runtime"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RunnerPersistentVolumeClaimReconciler reconciles a PersistentVolume object
|
||||||
|
type RunnerPersistentVolumeClaimReconciler struct {
|
||||||
|
client.Client
|
||||||
|
Log logr.Logger
|
||||||
|
Recorder record.EventRecorder
|
||||||
|
Scheme *runtime.Scheme
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
// +kubebuilder:rbac:groups=core,resources=persistentvolumeclaims,verbs=get;list;watch;update;patch;delete
|
||||||
|
// +kubebuilder:rbac:groups=core,resources=persistentvolumes,verbs=get;list;watch;update;patch;delete
|
||||||
|
// +kubebuilder:rbac:groups=core,resources=events,verbs=create;patch
|
||||||
|
|
||||||
|
func (r *RunnerPersistentVolumeClaimReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||||
|
log := r.Log.WithValues("pvc", req.NamespacedName)
|
||||||
|
|
||||||
|
var pvc corev1.PersistentVolumeClaim
|
||||||
|
if err := r.Get(ctx, req.NamespacedName, &pvc); err != nil {
|
||||||
|
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := syncPVC(ctx, r.Client, log, req.Namespace, &pvc)
|
||||||
|
|
||||||
|
if res == nil {
|
||||||
|
res = &ctrl.Result{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return *res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RunnerPersistentVolumeClaimReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||||
|
name := "runnerpersistentvolumeclaim-controller"
|
||||||
|
if r.Name != "" {
|
||||||
|
name = r.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Recorder = mgr.GetEventRecorderFor(name)
|
||||||
|
|
||||||
|
return ctrl.NewControllerManagedBy(mgr).
|
||||||
|
For(&corev1.PersistentVolumeClaim{}).
|
||||||
|
Named(name).
|
||||||
|
Complete(r)
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user