mirror of
https://github.com/actions/actions-runner-controller.git
synced 2025-12-10 19:50:30 +00:00
Compare commits
222 Commits
v0.19.0
...
actions-ru
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7156ce040e | ||
|
|
0b9bef2c08 | ||
|
|
a5ed6bd263 | ||
|
|
921f547200 | ||
|
|
9079c5d85f | ||
|
|
a9aea0bd9c | ||
|
|
fcf4778bac | ||
|
|
eb0a4a9603 | ||
|
|
b6151ebb8d | ||
|
|
ba4bd7c0db | ||
|
|
5b92c412a4 | ||
|
|
e22d981d58 | ||
|
|
a7b39cc247 | ||
|
|
1e452358b4 | ||
|
|
92e133e007 | ||
|
|
d0d316252e | ||
|
|
b509eb4388 | ||
|
|
59437ef79f | ||
|
|
a51fb90cd2 | ||
|
|
eb53d238d1 | ||
|
|
7fdf9a6c67 | ||
|
|
6f591ee774 | ||
|
|
cc25dd7926 | ||
|
|
1b911749a6 | ||
|
|
b652a8f9ae | ||
|
|
069bf6a042 | ||
|
|
f09a974ac2 | ||
|
|
1f7e440030 | ||
|
|
9d5a562407 | ||
|
|
715e6a40f1 | ||
|
|
81b2c5ada9 | ||
|
|
9ae83dfff5 | ||
|
|
5e86881c30 | ||
|
|
1c75b20767 | ||
|
|
8a73560dbc | ||
|
|
01301d3ce8 | ||
|
|
02679ac1d8 | ||
|
|
1a6e5719c3 | ||
|
|
f72d871c5b | ||
|
|
ad48851dc9 | ||
|
|
c5950d75fa | ||
|
|
de1f48111a | ||
|
|
8a7720da77 | ||
|
|
608c56936e | ||
|
|
4ebec38208 | ||
|
|
0c34196d87 | ||
|
|
83c8a9809e | ||
|
|
c64000e11c | ||
|
|
9bb21aef1f | ||
|
|
7261d927fb | ||
|
|
91102c8088 | ||
|
|
6f51f560ba | ||
|
|
961f01baed | ||
|
|
d0642eeff1 | ||
|
|
473fe7f736 | ||
|
|
84b0c64d29 | ||
|
|
f0fccc020b | ||
|
|
2bd6d6342e | ||
|
|
ea2dbc2807 | ||
|
|
c718eaae4f | ||
|
|
67e39d719e | ||
|
|
bbd328a7cc | ||
|
|
8eb6c0f3f0 | ||
|
|
231c1f80e7 | ||
|
|
3c073c5e17 | ||
|
|
a1cfe3be36 | ||
|
|
898ad3c355 | ||
|
|
164a91b18f | ||
|
|
acb004f291 | ||
|
|
3de4e7e9c6 | ||
|
|
4a55fe563c | ||
|
|
23841642df | ||
|
|
7c4ac2ef44 | ||
|
|
550717020d | ||
|
|
9a5ae93cb7 | ||
|
|
f5175256c6 | ||
|
|
031b1848e0 | ||
|
|
47a17754fd | ||
|
|
85ddd0d137 | ||
|
|
eefb48ba3f | ||
|
|
62995fec5b | ||
|
|
7ee1d6bcdb | ||
|
|
b87e6e3966 | ||
|
|
88b8871830 | ||
|
|
2191617eb5 | ||
|
|
b305e38b17 | ||
|
|
b6c33cee32 | ||
|
|
46da4a6b6e | ||
|
|
f7e14e06e8 | ||
|
|
09e6b1839b | ||
|
|
0416a9272f | ||
|
|
c33578a041 | ||
|
|
49566aaebd | ||
|
|
f66e6a00fa | ||
|
|
431c1ed04a | ||
|
|
0d3de9ee2a | ||
|
|
79d63acded | ||
|
|
271a4dcd9d | ||
|
|
0401b2d786 | ||
|
|
43141cb751 | ||
|
|
b805cfada7 | ||
|
|
c4e97d600d | ||
|
|
fce7d6d2a7 | ||
|
|
5805e39e1f | ||
|
|
d36d47fe66 | ||
|
|
8657a34f32 | ||
|
|
b01e193aab | ||
|
|
0a3d2b686e | ||
|
|
2bc050a62d | ||
|
|
0725e72ae0 | ||
|
|
5f9fcaf016 | ||
|
|
e4e0b45933 | ||
|
|
2937173101 | ||
|
|
fccf29970b | ||
|
|
ea06001819 | ||
|
|
1bc1712519 | ||
|
|
24224613f3 | ||
|
|
3f331e9a39 | ||
|
|
67c7b7a228 | ||
|
|
2e325fa176 | ||
|
|
5e3f89bdc5 | ||
|
|
9f4f5ec951 | ||
|
|
1fafd0d139 | ||
|
|
24602ff3ee | ||
|
|
cf75d24def | ||
|
|
ac3721d0d5 | ||
|
|
594b086674 | ||
|
|
58d2591f09 | ||
|
|
1a75b4558b | ||
|
|
40c88eb490 | ||
|
|
fe64850d3d | ||
|
|
4320e0e5e1 | ||
|
|
4a61c2f3aa | ||
|
|
1eb135cace | ||
|
|
d918c91bea | ||
|
|
bf35c51440 | ||
|
|
b679a54196 | ||
|
|
5da808af96 | ||
|
|
e5b5ee6f1d | ||
|
|
cf3abcc7d6 | ||
|
|
cffc2585f9 | ||
|
|
01928863b9 | ||
|
|
a98729b08b | ||
|
|
ec0915ce7c | ||
|
|
d355f05ac0 | ||
|
|
6f27b4920e | ||
|
|
f8959f973f | ||
|
|
37955fa267 | ||
|
|
63fe89b7aa | ||
|
|
3f801af72a | ||
|
|
7008b0c257 | ||
|
|
d9df455781 | ||
|
|
7e42d3fa7c | ||
|
|
0593125d96 | ||
|
|
a815c37614 | ||
|
|
3539569fed | ||
|
|
fc131870aa | ||
|
|
382afa4450 | ||
|
|
5125dd7e77 | ||
|
|
2c711506ea | ||
|
|
dfa0f2eef4 | ||
|
|
180db37a9a | ||
|
|
424c33b11f | ||
|
|
34d9c6d4db | ||
|
|
167c5b4dc9 | ||
|
|
91c22ef964 | ||
|
|
5d292ee5ff | ||
|
|
5b4b65664c | ||
|
|
b6465c5d09 | ||
|
|
dc9f9b0bfb | ||
|
|
02e05bdafb | ||
|
|
a9421edd46 | ||
|
|
fb66b28569 | ||
|
|
fabead8c8e | ||
|
|
d528d18211 | ||
|
|
7e593a80ff | ||
|
|
27bdc780a3 | ||
|
|
3948406374 | ||
|
|
743e6d6202 | ||
|
|
29260549fa | ||
|
|
f17edd500b | ||
|
|
14564c7b8e | ||
|
|
7f2795b5d6 | ||
|
|
b27b6ea2a8 | ||
|
|
f858e2e432 | ||
|
|
6f130c2db5 | ||
|
|
dcea0f7f79 | ||
|
|
f19e7ea8a8 | ||
|
|
9437e164b4 | ||
|
|
82d1be7791 | ||
|
|
dbab1a5e92 | ||
|
|
e5a9d50cb6 | ||
|
|
67031acdc4 | ||
|
|
b1bfa8787f | ||
|
|
c78116b0f9 | ||
|
|
4ec57d3e39 | ||
|
|
79543add3f | ||
|
|
7722730dc0 | ||
|
|
044f4ad4ea | ||
|
|
20394be04d | ||
|
|
7a305d2892 | ||
|
|
927d6f03ce | ||
|
|
127a9aa7c4 | ||
|
|
2703fa75d6 | ||
|
|
605ec158f4 | ||
|
|
3b45d1b334 | ||
|
|
acb906164b | ||
|
|
98da4c2adb | ||
|
|
9e1c28fcff | ||
|
|
774db3fef4 | ||
|
|
8b90b0f0e3 | ||
|
|
a277489003 | ||
|
|
1084a37174 | ||
|
|
9e4dbf497c | ||
|
|
af0ca03752 | ||
|
|
37d9599dca | ||
|
|
08a676cfd4 | ||
|
|
f2e2060ff8 | ||
|
|
dc5f90025c | ||
|
|
8566a4f453 | ||
|
|
3366dc9a63 | ||
|
|
fa94799ec8 |
@@ -7,6 +7,7 @@ config
|
|||||||
charts
|
charts
|
||||||
.github
|
.github
|
||||||
.envrc
|
.envrc
|
||||||
|
.env
|
||||||
*.md
|
*.md
|
||||||
*.txt
|
*.txt
|
||||||
*.sh
|
*.sh
|
||||||
|
|||||||
52
.github/actions/setup-docker-environment/action.yaml
vendored
Normal file
52
.github/actions/setup-docker-environment/action.yaml
vendored
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
name: "Setup Docker"
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
username:
|
||||||
|
description: "Username"
|
||||||
|
required: true
|
||||||
|
password:
|
||||||
|
description: "Password"
|
||||||
|
required: true
|
||||||
|
ghcr_username:
|
||||||
|
description: "GHCR username. Usually set from the github.actor variable"
|
||||||
|
required: true
|
||||||
|
ghcr_password:
|
||||||
|
description: "GHCR password. Usually set from the secrets.GITHUB_TOKEN variable"
|
||||||
|
required: true
|
||||||
|
|
||||||
|
outputs:
|
||||||
|
sha_short:
|
||||||
|
description: "The short SHA used for image builds"
|
||||||
|
value: ${{ steps.vars.outputs.sha_short }}
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: "composite"
|
||||||
|
steps:
|
||||||
|
- name: Get Short SHA
|
||||||
|
id: vars
|
||||||
|
run: |
|
||||||
|
echo ::set-output name=sha_short::${GITHUB_SHA::7}
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v1
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v1
|
||||||
|
with:
|
||||||
|
version: latest
|
||||||
|
|
||||||
|
- name: Login to DockerHub
|
||||||
|
if: ${{ github.ref == 'master' && github.event.pull_request.merged == true }}
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
username: ${{ inputs.username }}
|
||||||
|
password: ${{ inputs.password }}
|
||||||
|
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
if: ${{ github.ref == 'master' && github.event.pull_request.merged == true }}
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ inputs.ghcr_username }}
|
||||||
|
password: ${{ inputs.ghcr_password }}
|
||||||
25
.github/lock.yml
vendored
Normal file
25
.github/lock.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# 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
|
||||||
23
.github/renovate.json5
vendored
Normal file
23
.github/renovate.json5
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"extends": ["config:base"],
|
||||||
|
"labels": ["dependencies"],
|
||||||
|
"packageRules": [
|
||||||
|
{
|
||||||
|
// automatically merge an update of runner
|
||||||
|
"matchPackageNames": ["actions/runner"],
|
||||||
|
"extractVersion": "^v(?<version>.*)$",
|
||||||
|
"automerge": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"regexManagers": [
|
||||||
|
{
|
||||||
|
// use https://github.com/actions/runner/releases
|
||||||
|
"fileMatch": [
|
||||||
|
".github/workflows/runners.yml"
|
||||||
|
],
|
||||||
|
"matchStrings": ["RUNNER_VERSION: +(?<currentValue>.*?)\\n"],
|
||||||
|
"depNameTemplate": "actions/runner",
|
||||||
|
"datasourceTemplate": "github-releases"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
3
.github/stale.yml
vendored
3
.github/stale.yml
vendored
@@ -18,8 +18,9 @@ exemptLabels:
|
|||||||
- refactor
|
- refactor
|
||||||
- documentation
|
- documentation
|
||||||
- chore
|
- chore
|
||||||
- needs-investigation
|
|
||||||
- bug
|
- bug
|
||||||
|
- dependencies
|
||||||
|
- needs-investigation
|
||||||
|
|
||||||
# Set to true to ignore issues in a project (defaults to false)
|
# Set to true to ignore issues in a project (defaults to false)
|
||||||
exemptProjects: false
|
exemptProjects: false
|
||||||
|
|||||||
123
.github/workflows/build-and-release-runners.yml
vendored
123
.github/workflows/build-and-release-runners.yml
vendored
@@ -1,123 +0,0 @@
|
|||||||
name: Build and Release Runners
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- '**'
|
|
||||||
paths:
|
|
||||||
- 'runner/**'
|
|
||||||
- .github/workflows/build-and-release-runners.yml
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
paths:
|
|
||||||
- runner/patched/*
|
|
||||||
- runner/Dockerfile
|
|
||||||
- runner/Dockerfile.ubuntu.1804
|
|
||||||
- runner/Dockerfile.dindrunner
|
|
||||||
- runner/entrypoint.sh
|
|
||||||
- .github/workflows/build-and-release-runners.yml
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
name: Build ${{ matrix.name }}-ubuntu-${{ matrix.os-version }}
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- name: actions-runner
|
|
||||||
os-version: 20.04
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
- name: actions-runner
|
|
||||||
os-version: 18.04
|
|
||||||
dockerfile: Dockerfile.ubuntu.1804
|
|
||||||
- name: actions-runner-dind
|
|
||||||
os-version: 20.04
|
|
||||||
dockerfile: Dockerfile.dindrunner
|
|
||||||
env:
|
|
||||||
RUNNER_VERSION: 2.278.0
|
|
||||||
DOCKER_VERSION: 19.03.12
|
|
||||||
DOCKERHUB_USERNAME: ${{ secrets.DOCKER_USER }}
|
|
||||||
steps:
|
|
||||||
- name: Set outputs
|
|
||||||
id: vars
|
|
||||||
run: echo ::set-output name=sha_short::${GITHUB_SHA::7}
|
|
||||||
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v1
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v1
|
|
||||||
with:
|
|
||||||
version: latest
|
|
||||||
|
|
||||||
- name: Login to DockerHub
|
|
||||||
uses: docker/login-action@v1
|
|
||||||
if: ${{ github.event_name == 'push' || github.event_name == 'release' }}
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKER_USER }}
|
|
||||||
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
|
|
||||||
|
|
||||||
- name: Build and Push Versioned Tags
|
|
||||||
uses: docker/build-push-action@v2
|
|
||||||
with:
|
|
||||||
context: ./runner
|
|
||||||
file: ./runner/${{ matrix.dockerfile }}
|
|
||||||
platforms: linux/amd64,linux/arm64
|
|
||||||
push: ${{ github.event_name != 'pull_request' }}
|
|
||||||
build-args: |
|
|
||||||
RUNNER_VERSION=${{ env.RUNNER_VERSION }}
|
|
||||||
DOCKER_VERSION=${{ env.DOCKER_VERSION }}
|
|
||||||
tags: |
|
|
||||||
${{ env.DOCKERHUB_USERNAME }}/${{ matrix.name }}:v${{ env.RUNNER_VERSION }}-ubuntu-${{ matrix.os-version }}
|
|
||||||
${{ env.DOCKERHUB_USERNAME }}/${{ matrix.name }}:v${{ env.RUNNER_VERSION }}-ubuntu-${{ matrix.os-version }}-${{ steps.vars.outputs.sha_short }}
|
|
||||||
|
|
||||||
latest-tags:
|
|
||||||
if: ${{ github.event_name == 'push' || github.event_name == 'release' }}
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
name: Build ${{ matrix.name }}-latest
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- name: actions-runner
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
- name: actions-runner-dind
|
|
||||||
dockerfile: Dockerfile.dindrunner
|
|
||||||
env:
|
|
||||||
RUNNER_VERSION: 2.277.1
|
|
||||||
DOCKER_VERSION: 19.03.12
|
|
||||||
DOCKERHUB_USERNAME: ${{ secrets.DOCKER_USER }}
|
|
||||||
steps:
|
|
||||||
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v1
|
|
||||||
|
|
||||||
- name: Set up Docker 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 }}
|
|
||||||
|
|
||||||
- name: Build and Push Latest Tag
|
|
||||||
uses: docker/build-push-action@v2
|
|
||||||
with:
|
|
||||||
context: ./runner
|
|
||||||
file: ./runner/${{ matrix.dockerfile }}
|
|
||||||
platforms: linux/amd64,linux/arm64
|
|
||||||
push: true
|
|
||||||
build-args: |
|
|
||||||
RUNNER_VERSION=${{ env.RUNNER_VERSION }}
|
|
||||||
DOCKER_VERSION=${{ env.DOCKER_VERSION }}
|
|
||||||
tags: |
|
|
||||||
${{ env.DOCKERHUB_USERNAME }}/${{ matrix.name }}:latest
|
|
||||||
14
.github/workflows/on-push-lint-charts.yml
vendored
14
.github/workflows/on-push-lint-charts.yml
vendored
@@ -4,18 +4,18 @@ on:
|
|||||||
push:
|
push:
|
||||||
paths:
|
paths:
|
||||||
- 'charts/**'
|
- 'charts/**'
|
||||||
|
- '.github/workflows/on-push-lint-charts.yml'
|
||||||
- '!charts/actions-runner-controller/docs/**'
|
- '!charts/actions-runner-controller/docs/**'
|
||||||
- '!charts/actions-runner-controller/*.md'
|
- '!**.md'
|
||||||
- '.github/**'
|
|
||||||
- '!.github/*.md'
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
env:
|
env:
|
||||||
KUBE_SCORE_VERSION: 1.10.0
|
KUBE_SCORE_VERSION: 1.10.0
|
||||||
HELM_VERSION: v3.4.1
|
HELM_VERSION: v3.8.0
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
lint-test:
|
lint-test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
name: Lint Chart
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
@@ -23,7 +23,7 @@ jobs:
|
|||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Set up Helm
|
- name: Set up Helm
|
||||||
uses: azure/setup-helm@v1
|
uses: azure/setup-helm@v2.0
|
||||||
with:
|
with:
|
||||||
version: ${{ env.HELM_VERSION }}
|
version: ${{ env.HELM_VERSION }}
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ jobs:
|
|||||||
python-version: 3.7
|
python-version: 3.7
|
||||||
|
|
||||||
- name: Set up chart-testing
|
- name: Set up chart-testing
|
||||||
uses: helm/chart-testing-action@v2.0.1
|
uses: helm/chart-testing-action@v2.2.0
|
||||||
|
|
||||||
- name: Run chart-testing (list-changed)
|
- name: Run chart-testing (list-changed)
|
||||||
id: list-changed
|
id: list-changed
|
||||||
@@ -63,7 +63,7 @@ jobs:
|
|||||||
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.0.0
|
uses: helm/kind-action@v1.2.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
|
||||||
|
|||||||
@@ -4,21 +4,23 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
- main # assume that the branch name may change in future
|
|
||||||
paths:
|
paths:
|
||||||
- 'charts/**'
|
- 'charts/**'
|
||||||
|
- '.github/workflows/on-push-master-publish-chart.yml'
|
||||||
- '!charts/actions-runner-controller/docs/**'
|
- '!charts/actions-runner-controller/docs/**'
|
||||||
- '.github/**'
|
|
||||||
- '!**.md'
|
- '!**.md'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
KUBE_SCORE_VERSION: 1.10.0
|
KUBE_SCORE_VERSION: 1.10.0
|
||||||
HELM_VERSION: v3.4.1
|
HELM_VERSION: v3.8.0
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
lint-chart:
|
lint-chart:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
name: Lint Chart
|
||||||
|
outputs:
|
||||||
|
publish-chart: ${{ steps.publish-chart-step.outputs.publish }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
@@ -26,7 +28,7 @@ jobs:
|
|||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Set up Helm
|
- name: Set up Helm
|
||||||
uses: azure/setup-helm@v1
|
uses: azure/setup-helm@v2.0
|
||||||
with:
|
with:
|
||||||
version: ${{ env.HELM_VERSION }}
|
version: ${{ env.HELM_VERSION }}
|
||||||
|
|
||||||
@@ -52,7 +54,7 @@ jobs:
|
|||||||
python-version: 3.7
|
python-version: 3.7
|
||||||
|
|
||||||
- name: Set up chart-testing
|
- name: Set up chart-testing
|
||||||
uses: helm/chart-testing-action@v2.0.1
|
uses: helm/chart-testing-action@v2.2.0
|
||||||
|
|
||||||
- name: Run chart-testing (list-changed)
|
- name: Run chart-testing (list-changed)
|
||||||
id: list-changed
|
id: list-changed
|
||||||
@@ -66,7 +68,7 @@ jobs:
|
|||||||
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.0.0
|
uses: helm/kind-action@v1.2.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
|
||||||
@@ -80,10 +82,25 @@ jobs:
|
|||||||
run: ct install --config charts/.ci/ct-config.yaml
|
run: ct install --config charts/.ci/ct-config.yaml
|
||||||
if: steps.list-changed.outputs.changed == 'true'
|
if: steps.list-changed.outputs.changed == 'true'
|
||||||
|
|
||||||
publish-chart:
|
# 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
|
||||||
|
id: publish-chart-step
|
||||||
|
run: |
|
||||||
|
CHART_TEXT=$(curl -fs https://raw.githubusercontent.com/actions-runner-controller/actions-runner-controller/master/charts/actions-runner-controller/Chart.yaml)
|
||||||
|
NEW_CHART_VERSION=$(echo "$CHART_TEXT" | grep version: | cut -d ' ' -f 2)
|
||||||
|
RELEASE_LIST=$(curl -fs https://api.github.com/repos/actions-runner-controller/actions-runner-controller/releases | jq .[].tag_name | grep actions-runner-controller | cut -d '"' -f 2 | cut -d '-' -f 4)
|
||||||
|
LATEST_RELEASED_CHART_VERSION=$(echo $RELEASE_LIST | cut -d ' ' -f 1)
|
||||||
|
echo "Chart version in master : $NEW_CHART_VERSION"
|
||||||
|
echo "Latest release chart version : $LATEST_RELEASED_CHART_VERSION"
|
||||||
|
if [[ $NEW_CHART_VERSION != $LATEST_RELEASED_CHART_VERSION ]]; then
|
||||||
|
echo "::set-output name=publish::true"
|
||||||
|
fi
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
publish-chart:
|
||||||
|
if: needs.lint-chart.outputs.publish-chart == 'true'
|
||||||
needs: lint-chart
|
needs: lint-chart
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Publish Chart
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
@@ -97,7 +114,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.1.0
|
uses: helm/chart-releaser-action@v1.3.0
|
||||||
env:
|
env:
|
||||||
CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||||
|
|
||||||
|
|||||||
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
@@ -1,3 +1,5 @@
|
|||||||
|
name: Publish Controller Image
|
||||||
|
|
||||||
on:
|
on:
|
||||||
release:
|
release:
|
||||||
types: [published]
|
types: [published]
|
||||||
@@ -16,6 +18,10 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: '^1.17.7'
|
||||||
|
|
||||||
- name: Install tools
|
- name: Install tools
|
||||||
run: |
|
run: |
|
||||||
curl -L -O https://github.com/kubernetes-sigs/kubebuilder/releases/download/v2.2.0/kubebuilder_2.2.0_linux_amd64.tar.gz
|
curl -L -O https://github.com/kubernetes-sigs/kubebuilder/releases/download/v2.2.0/kubebuilder_2.2.0_linux_amd64.tar.gz
|
||||||
|
|||||||
73
.github/workflows/runners.yml
vendored
Normal file
73
.github/workflows/runners.yml
vendored
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
name: Runners
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types:
|
||||||
|
- opened
|
||||||
|
- synchronize
|
||||||
|
- reopened
|
||||||
|
- closed
|
||||||
|
branches:
|
||||||
|
- 'master'
|
||||||
|
paths:
|
||||||
|
- 'runner/**'
|
||||||
|
- .github/workflows/runners.yml
|
||||||
|
- '!**.md'
|
||||||
|
|
||||||
|
env:
|
||||||
|
RUNNER_VERSION: 2.287.1
|
||||||
|
DOCKER_VERSION: 20.10.12
|
||||||
|
DOCKERHUB_USERNAME: summerwind
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
packages: write
|
||||||
|
contents: read
|
||||||
|
name: Build ${{ matrix.name }}-${{ matrix.os-name }}-${{ matrix.os-version }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- name: actions-runner
|
||||||
|
os-name: ubuntu
|
||||||
|
os-version: 20.04
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
- name: actions-runner-dind
|
||||||
|
os-name: ubuntu
|
||||||
|
os-version: 20.04
|
||||||
|
dockerfile: Dockerfile.dindrunner
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- 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 }}
|
||||||
|
|
||||||
|
- name: Build and Push Versioned Tags
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
context: ./runner
|
||||||
|
file: ./runner/${{ matrix.dockerfile }}
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
push: ${{ github.ref == 'master' && github.event.pull_request.merged == true }}
|
||||||
|
build-args: |
|
||||||
|
RUNNER_VERSION=${{ env.RUNNER_VERSION }}
|
||||||
|
DOCKER_VERSION=${{ env.DOCKER_VERSION }}
|
||||||
|
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 }}-${{ steps.vars.outputs.sha_short }}
|
||||||
|
${{ env.DOCKERHUB_USERNAME }}/${{ 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 }}-${{ steps.vars.outputs.sha_short }}
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
22
.github/workflows/test-entrypoint.yaml
vendored
Normal file
22
.github/workflows/test-entrypoint.yaml
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
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@v2
|
||||||
|
- name: Run unit tests for entrypoint.sh
|
||||||
|
run: |
|
||||||
|
cd test/entrypoint
|
||||||
|
bash entrypoint_unittest.sh
|
||||||
23
.github/workflows/test.yaml
vendored
23
.github/workflows/test.yaml
vendored
@@ -5,10 +5,15 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- 'runner/**'
|
- .github/workflows/runners.yml
|
||||||
- .github/workflows/build-and-release-runners.yml
|
- .github/workflows/on-push-lint-charts.yml
|
||||||
- '*.md'
|
- .github/workflows/on-push-master-publish-chart.yml
|
||||||
- '.gitignore'
|
- .github/workflows/release.yml
|
||||||
|
- .github/workflows/test-entrypoint.yml
|
||||||
|
- .github/workflows/wip.yml
|
||||||
|
- 'runner/**'
|
||||||
|
- '**.md'
|
||||||
|
- '.gitignore'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
@@ -17,11 +22,15 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: '^1.17.7'
|
||||||
|
- run: go version
|
||||||
- name: Install kubebuilder
|
- name: Install kubebuilder
|
||||||
run: |
|
run: |
|
||||||
curl -L -O https://github.com/kubernetes-sigs/kubebuilder/releases/download/v2.2.0/kubebuilder_2.2.0_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.2.0_linux_amd64.tar.gz
|
tar zxvf kubebuilder_2.3.2_linux_amd64.tar.gz
|
||||||
sudo mv kubebuilder_2.2.0_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
|
||||||
|
|||||||
9
.github/workflows/wip.yml
vendored
9
.github/workflows/wip.yml
vendored
@@ -1,8 +1,15 @@
|
|||||||
|
name: Publish Canary Image
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
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
|
||||||
- "runner/**"
|
- "runner/**"
|
||||||
- "**.md"
|
- "**.md"
|
||||||
- ".gitignore"
|
- ".gitignore"
|
||||||
@@ -10,7 +17,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: release-latest
|
name: Build and Publish Canary Image
|
||||||
env:
|
env:
|
||||||
DOCKERHUB_USERNAME: ${{ secrets.DOCKER_USER }}
|
DOCKERHUB_USERNAME: ${{ secrets.DOCKER_USER }}
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
9
.gitignore
vendored
9
.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
|
# Deploy Assets
|
||||||
release
|
release
|
||||||
|
|
||||||
# Binaries for programs and plugins
|
# Binaries for programs and plugins
|
||||||
@@ -15,17 +16,21 @@ bin
|
|||||||
*.out
|
*.out
|
||||||
|
|
||||||
# Kubernetes Generated files - skip generated files, except for vendored files
|
# Kubernetes Generated files - skip generated files, except for vendored files
|
||||||
|
|
||||||
!vendor/**/zz_generated.*
|
!vendor/**/zz_generated.*
|
||||||
|
|
||||||
# editor and IDE paraphernalia
|
# editor and IDE paraphernalia
|
||||||
|
.vscode
|
||||||
.idea
|
.idea
|
||||||
*.swp
|
*.swp
|
||||||
*.swo
|
*.swo
|
||||||
*~
|
*~
|
||||||
|
|
||||||
.envrc
|
.envrc
|
||||||
|
.env
|
||||||
|
.test.env
|
||||||
*.pem
|
*.pem
|
||||||
|
|
||||||
# OS
|
# OS
|
||||||
.DS_STORE
|
.DS_STORE
|
||||||
|
|
||||||
|
/test-assets
|
||||||
|
|||||||
159
CONTRIBUTING.md
159
CONTRIBUTING.md
@@ -1,8 +1,157 @@
|
|||||||
# Contributing
|
## Contributing
|
||||||
|
|
||||||
### Helm Version Bumps
|
### Testing Controller Built from a Pull Request
|
||||||
|
|
||||||
**Chart Version :** When bumping the chart version follow semantic versioning https://semver.org/<br />
|
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.
|
||||||
**App Version :** When bumping the app version you will also need to bump the chart version too. Again, follow semantic versioning when bumping the chart.
|
|
||||||
|
|
||||||
To determine if you need to bump the MAJOR, MINOR or PATCH versions you will need to review the changes between the previous app version and the new app version and / or ask for a maintainer to advise.
|
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).
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
### How to Contribute a Patch
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
#### Running an End to End Test
|
||||||
|
|
||||||
|
> **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
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 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.
|
||||||
|
|
||||||
|
To make your development cycle faster, use the below command to update deploy and update all the three:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
# Let assume we have all other envvars like DOCKER_USER, GITHUB_TOKEN already set,
|
||||||
|
# The below command will (re)build `actions-runner-controller:controller1` and `actions-runner:runner1`,
|
||||||
|
# load those into kind nodes, and then rerun kubectl or helm to install/upgrade the controller,
|
||||||
|
# and finally upgrade the runner deployment to use the new runner image.
|
||||||
|
#
|
||||||
|
# As helm 3 and kubectl is unable to recreate a pod when no tag change,
|
||||||
|
# you either need to bump VERSION and RUNNER_TAG on each run,
|
||||||
|
# or manually run `kubectl delete pod $POD` on respective pods for changes to actually take effect.
|
||||||
|
|
||||||
|
# Makefile
|
||||||
|
VERSION=controller1 \
|
||||||
|
RUNNER_TAG=runner1 \
|
||||||
|
make acceptance/pull acceptance/kind docker-build acceptance/load acceptance/deploy
|
||||||
|
```
|
||||||
|
|
||||||
|
If you've already deployed actions-runner-controller and only want to recreate pods to use the newer image, you can run:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
# Makefile
|
||||||
|
NAME=$DOCKER_USER/actions-runner-controller \
|
||||||
|
make docker-build acceptance/load && \
|
||||||
|
kubectl -n actions-runner-system delete po $(kubectl -n actions-runner-system get po -ojsonpath={.items[*].metadata.name})
|
||||||
|
```
|
||||||
|
|
||||||
|
Similarly, if you'd like to recreate runner pods with the newer runner image you can use the runner specific [Makefile](runner/Makefile) to build and / or push new runner images
|
||||||
|
|
||||||
|
```shell
|
||||||
|
# runner/Makefile
|
||||||
|
NAME=$DOCKER_USER/actions-runner make \
|
||||||
|
-C runner docker-{build,push}-ubuntu && \
|
||||||
|
(kubectl get po -ojsonpath={.items[*].metadata.name} | xargs -n1 kubectl delete po)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Developing the Runners
|
||||||
|
|
||||||
|
**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.
|
||||||
|
|
||||||
|
**Running Ginkgo Tests**
|
||||||
|
|
||||||
|
You can run the integration test suite that is written in Ginkgo with:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
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.
|
||||||
|
|
||||||
|
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
|
||||||
|
sudo mkdir -p /usr/local/kubebuilder/bin
|
||||||
|
make kube-apiserver etcd
|
||||||
|
sudo mv test-assets/{etcd,kube-apiserver} /usr/local/kubebuilder/bin/
|
||||||
|
go test -v -run TestAPIs github.com/actions-runner-controller/actions-runner-controller/controllers
|
||||||
|
```
|
||||||
|
|
||||||
|
To run Ginkgo tests selectively, set the pattern of target test names to `GINKGO_FOCUS`.
|
||||||
|
All the Ginkgo test that matches `GINKGO_FOCUS` will be run.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
GINKGO_FOCUS='[It] should create a new Runner resource from the specified template, add a another Runner on replicas increased, and removes all the replicas when set to 0' \
|
||||||
|
go test -v -run TestAPIs github.com/actions-runner-controller/actions-runner-controller/controllers
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Helm Version Bumps
|
||||||
|
|
||||||
|
In general we ask you not to bump the version in your PR, the maintainers in general manage the publishing of a new chart.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Build the manager binary
|
# Build the manager binary
|
||||||
FROM golang:1.15 as builder
|
FROM golang:1.17 as builder
|
||||||
|
|
||||||
ARG TARGETPLATFORM
|
ARG TARGETPLATFORM
|
||||||
|
|
||||||
|
|||||||
96
Makefile
96
Makefile
@@ -5,19 +5,23 @@ else
|
|||||||
endif
|
endif
|
||||||
DOCKER_USER ?= $(shell echo ${NAME} | cut -d / -f1)
|
DOCKER_USER ?= $(shell echo ${NAME} | cut -d / -f1)
|
||||||
VERSION ?= latest
|
VERSION ?= latest
|
||||||
|
TARGETPLATFORM ?= $(shell arch)
|
||||||
RUNNER_NAME ?= ${DOCKER_USER}/actions-runner
|
RUNNER_NAME ?= ${DOCKER_USER}/actions-runner
|
||||||
RUNNER_TAG ?= ${VERSION}
|
RUNNER_TAG ?= ${VERSION}
|
||||||
TEST_REPO ?= ${DOCKER_USER}/actions-runner-controller
|
TEST_REPO ?= ${DOCKER_USER}/actions-runner-controller
|
||||||
TEST_ORG ?=
|
TEST_ORG ?=
|
||||||
TEST_ORG_REPO ?=
|
TEST_ORG_REPO ?=
|
||||||
|
TEST_EPHEMERAL ?= false
|
||||||
SYNC_PERIOD ?= 5m
|
SYNC_PERIOD ?= 5m
|
||||||
|
USE_RUNNERSET ?=
|
||||||
# From https://github.com/VictoriaMetrics/operator/pull/44
|
RUNNER_FEATURE_FLAG_EPHEMERAL ?=
|
||||||
YAML_DROP=$(YQ) delete --inplace
|
KUBECONTEXT ?= kind-acceptance
|
||||||
YAML_DROP_PREFIX=spec.validation.openAPIV3Schema.properties.spec.properties
|
CLUSTER ?= acceptance
|
||||||
|
CERT_MANAGER_VERSION ?= v1.1.1
|
||||||
|
KUBE_RBAC_PROXY_VERSION ?= v0.11.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:trivialVersions=true"
|
CRD_OPTIONS ?= "crd:generateEmbeddedObjectMeta=true"
|
||||||
|
|
||||||
# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
|
# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
|
||||||
ifeq (,$(shell go env GOBIN))
|
ifeq (,$(shell go env GOBIN))
|
||||||
@@ -47,9 +51,11 @@ endif
|
|||||||
|
|
||||||
all: manager
|
all: manager
|
||||||
|
|
||||||
|
GO_TEST_ARGS ?= -short
|
||||||
|
|
||||||
# Run tests
|
# Run tests
|
||||||
test: generate fmt vet manifests
|
test: generate fmt vet manifests
|
||||||
go test ./... -coverprofile cover.out
|
go test $(GO_TEST_ARGS) ./... -coverprofile cover.out
|
||||||
|
|
||||||
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
|
||||||
@@ -80,10 +86,13 @@ deploy: manifests
|
|||||||
kustomize build config/default | kubectl apply -f -
|
kustomize build config/default | kubectl apply -f -
|
||||||
|
|
||||||
# Generate manifests e.g. CRD, RBAC etc.
|
# Generate manifests e.g. CRD, RBAC etc.
|
||||||
manifests: manifests-118 fix118 chart-crds
|
manifests: manifests-gen-crds chart-crds
|
||||||
|
|
||||||
manifests-118: controller-gen
|
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 \
|
||||||
|
$(YQ) write --inplace "$$YAMLFILE" spec.preserveUnknownFields false; \
|
||||||
|
done
|
||||||
|
|
||||||
chart-crds:
|
chart-crds:
|
||||||
cp config/crd/bases/*.yaml charts/actions-runner-controller/crds/
|
cp config/crd/bases/*.yaml charts/actions-runner-controller/crds/
|
||||||
@@ -96,30 +105,14 @@ fmt:
|
|||||||
vet:
|
vet:
|
||||||
go vet ./...
|
go vet ./...
|
||||||
|
|
||||||
# workaround for CRD issue with k8s 1.18 & controller-gen
|
|
||||||
# ref: https://github.com/kubernetes/kubernetes/issues/91395
|
|
||||||
fix118: yq
|
|
||||||
$(YAML_DROP) config/crd/bases/actions.summerwind.dev_runnerreplicasets.yaml $(YAML_DROP_PREFIX).template.properties.spec.properties.containers.items.properties
|
|
||||||
$(YAML_DROP) config/crd/bases/actions.summerwind.dev_runnerreplicasets.yaml $(YAML_DROP_PREFIX).template.properties.spec.properties.initContainers.items.properties
|
|
||||||
$(YAML_DROP) config/crd/bases/actions.summerwind.dev_runnerreplicasets.yaml $(YAML_DROP_PREFIX).template.properties.spec.properties.sidecarContainers.items.properties
|
|
||||||
$(YAML_DROP) config/crd/bases/actions.summerwind.dev_runnerreplicasets.yaml $(YAML_DROP_PREFIX).template.properties.spec.properties.ephemeralContainers.items.properties
|
|
||||||
$(YAML_DROP) config/crd/bases/actions.summerwind.dev_runnerdeployments.yaml $(YAML_DROP_PREFIX).template.properties.spec.properties.containers.items.properties
|
|
||||||
$(YAML_DROP) config/crd/bases/actions.summerwind.dev_runnerdeployments.yaml $(YAML_DROP_PREFIX).template.properties.spec.properties.initContainers.items.properties
|
|
||||||
$(YAML_DROP) config/crd/bases/actions.summerwind.dev_runnerdeployments.yaml $(YAML_DROP_PREFIX).template.properties.spec.properties.sidecarContainers.items.properties
|
|
||||||
$(YAML_DROP) config/crd/bases/actions.summerwind.dev_runnerdeployments.yaml $(YAML_DROP_PREFIX).template.properties.spec.properties.ephemeralContainers.items.properties
|
|
||||||
$(YAML_DROP) config/crd/bases/actions.summerwind.dev_runners.yaml $(YAML_DROP_PREFIX).containers.items.properties
|
|
||||||
$(YAML_DROP) config/crd/bases/actions.summerwind.dev_runners.yaml $(YAML_DROP_PREFIX).initContainers.items.properties
|
|
||||||
$(YAML_DROP) config/crd/bases/actions.summerwind.dev_runners.yaml $(YAML_DROP_PREFIX).sidecarContainers.items.properties
|
|
||||||
$(YAML_DROP) config/crd/bases/actions.summerwind.dev_runners.yaml $(YAML_DROP_PREFIX).ephemeralContainers.items.properties
|
|
||||||
|
|
||||||
# Generate code
|
# Generate code
|
||||||
generate: controller-gen
|
generate: controller-gen
|
||||||
$(CONTROLLER_GEN) object:headerFile=./hack/boilerplate.go.txt paths="./..."
|
$(CONTROLLER_GEN) object:headerFile=./hack/boilerplate.go.txt paths="./..."
|
||||||
|
|
||||||
# Build the docker image
|
# Build the docker image
|
||||||
docker-build:
|
docker-build:
|
||||||
docker build . -t ${NAME}:${VERSION}
|
docker build -t ${NAME}:${VERSION} .
|
||||||
docker build runner -t ${RUNNER_NAME}:${RUNNER_TAG} --build-arg TARGETPLATFORM=$(shell arch)
|
docker build -t ${RUNNER_NAME}:${RUNNER_TAG} --build-arg TARGETPLATFORM=${TARGETPLATFORM} runner
|
||||||
|
|
||||||
docker-buildx:
|
docker-buildx:
|
||||||
export DOCKER_CLI_EXPERIMENTAL=enabled
|
export DOCKER_CLI_EXPERIMENTAL=enabled
|
||||||
@@ -158,31 +151,31 @@ acceptance: release/clean acceptance/pull docker-build release
|
|||||||
acceptance/run: acceptance/kind acceptance/load acceptance/setup acceptance/deploy acceptance/tests acceptance/teardown
|
acceptance/run: acceptance/kind acceptance/load acceptance/setup acceptance/deploy acceptance/tests acceptance/teardown
|
||||||
|
|
||||||
acceptance/kind:
|
acceptance/kind:
|
||||||
kind create cluster --name acceptance --config acceptance/kind.yaml
|
kind create cluster --name ${CLUSTER} --config acceptance/kind.yaml
|
||||||
|
|
||||||
# Set TMPDIR to somewhere under $HOME when you use docker installed with Ubuntu snap
|
# Set TMPDIR to somewhere under $HOME when you use docker installed with Ubuntu snap
|
||||||
# Otherwise `load docker-image` fail while running `docker save`.
|
# Otherwise `load docker-image` fail while running `docker save`.
|
||||||
# See https://kind.sigs.k8s.io/docs/user/known-issues/#docker-installed-with-snap
|
# See https://kind.sigs.k8s.io/docs/user/known-issues/#docker-installed-with-snap
|
||||||
acceptance/load:
|
acceptance/load:
|
||||||
kind load docker-image ${NAME}:${VERSION} --name acceptance
|
kind load docker-image ${NAME}:${VERSION} --name ${CLUSTER}
|
||||||
kind load docker-image quay.io/brancz/kube-rbac-proxy:v0.10.0 --name acceptance
|
kind load docker-image quay.io/brancz/kube-rbac-proxy:$(KUBE_RBAC_PROXY_VERSION) --name ${CLUSTER}
|
||||||
kind load docker-image ${RUNNER_NAME}:${RUNNER_TAG} --name acceptance
|
kind load docker-image ${RUNNER_NAME}:${RUNNER_TAG} --name ${CLUSTER}
|
||||||
kind load docker-image docker:dind --name acceptance
|
kind load docker-image docker:dind --name ${CLUSTER}
|
||||||
kind load docker-image quay.io/jetstack/cert-manager-controller:v1.0.4 --name acceptance
|
kind load docker-image quay.io/jetstack/cert-manager-controller:$(CERT_MANAGER_VERSION) --name ${CLUSTER}
|
||||||
kind load docker-image quay.io/jetstack/cert-manager-cainjector:v1.0.4 --name acceptance
|
kind load docker-image quay.io/jetstack/cert-manager-cainjector:$(CERT_MANAGER_VERSION) --name ${CLUSTER}
|
||||||
kind load docker-image quay.io/jetstack/cert-manager-webhook:v1.0.4 --name acceptance
|
kind load docker-image quay.io/jetstack/cert-manager-webhook:$(CERT_MANAGER_VERSION) --name ${CLUSTER}
|
||||||
kubectl cluster-info --context kind-acceptance
|
kubectl cluster-info --context ${KUBECONTEXT}
|
||||||
|
|
||||||
# Pull the docker images for acceptance
|
# Pull the docker images for acceptance
|
||||||
acceptance/pull:
|
acceptance/pull:
|
||||||
docker pull quay.io/brancz/kube-rbac-proxy:v0.10.0
|
docker pull quay.io/brancz/kube-rbac-proxy:$(KUBE_RBAC_PROXY_VERSION)
|
||||||
docker pull docker:dind
|
docker pull docker:dind
|
||||||
docker pull quay.io/jetstack/cert-manager-controller:v1.0.4
|
docker pull quay.io/jetstack/cert-manager-controller:$(CERT_MANAGER_VERSION)
|
||||||
docker pull quay.io/jetstack/cert-manager-cainjector:v1.0.4
|
docker pull quay.io/jetstack/cert-manager-cainjector:$(CERT_MANAGER_VERSION)
|
||||||
docker pull quay.io/jetstack/cert-manager-webhook:v1.0.4
|
docker pull quay.io/jetstack/cert-manager-webhook:$(CERT_MANAGER_VERSION)
|
||||||
|
|
||||||
acceptance/setup:
|
acceptance/setup:
|
||||||
kubectl apply --validate=false -f https://github.com/jetstack/cert-manager/releases/download/v1.0.4/cert-manager.yaml #kubectl create namespace actions-runner-system
|
kubectl apply --validate=false -f https://github.com/jetstack/cert-manager/releases/download/$(CERT_MANAGER_VERSION)/cert-manager.yaml #kubectl create namespace actions-runner-system
|
||||||
kubectl -n cert-manager wait deploy/cert-manager-cainjector --for condition=available --timeout 90s
|
kubectl -n cert-manager wait deploy/cert-manager-cainjector --for condition=available --timeout 90s
|
||||||
kubectl -n cert-manager wait deploy/cert-manager-webhook --for condition=available --timeout 60s
|
kubectl -n cert-manager wait deploy/cert-manager-webhook --for condition=available --timeout 60s
|
||||||
kubectl -n cert-manager wait deploy/cert-manager --for condition=available --timeout 60s
|
kubectl -n cert-manager wait deploy/cert-manager --for condition=available --timeout 60s
|
||||||
@@ -191,22 +184,37 @@ acceptance/setup:
|
|||||||
sleep 5
|
sleep 5
|
||||||
|
|
||||||
acceptance/teardown:
|
acceptance/teardown:
|
||||||
kind delete cluster --name acceptance
|
kind delete cluster --name ${CLUSTER}
|
||||||
|
|
||||||
acceptance/deploy:
|
acceptance/deploy:
|
||||||
NAME=${NAME} DOCKER_USER=${DOCKER_USER} VERSION=${VERSION} RUNNER_NAME=${RUNNER_NAME} RUNNER_TAG=${RUNNER_TAG} TEST_REPO=${TEST_REPO} \
|
NAME=${NAME} DOCKER_USER=${DOCKER_USER} VERSION=${VERSION} RUNNER_NAME=${RUNNER_NAME} RUNNER_TAG=${RUNNER_TAG} TEST_REPO=${TEST_REPO} \
|
||||||
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} \
|
||||||
|
TEST_EPHEMERAL=${TEST_EPHEMERAL} \
|
||||||
|
RUNNER_FEATURE_FLAG_EPHEMERAL=${RUNNER_FEATURE_FLAG_EPHEMERAL} \
|
||||||
acceptance/deploy.sh
|
acceptance/deploy.sh
|
||||||
|
|
||||||
acceptance/tests:
|
acceptance/tests:
|
||||||
acceptance/checks.sh
|
acceptance/checks.sh
|
||||||
|
|
||||||
|
# We use -count=1 instead of `go clean -testcache`
|
||||||
|
# See https://terratest.gruntwork.io/docs/testing-best-practices/avoid-test-caching/
|
||||||
|
.PHONY: e2e
|
||||||
|
e2e:
|
||||||
|
go test -count=1 -v -timeout 600s -run '^TestE2E$$' ./test/e2e
|
||||||
|
|
||||||
# Upload release file to GitHub.
|
# Upload release file to GitHub.
|
||||||
github-release: release
|
github-release: release
|
||||||
ghr ${VERSION} release/
|
ghr ${VERSION} release/
|
||||||
|
|
||||||
# find or download controller-gen
|
# Find or download controller-gen
|
||||||
# download controller-gen if necessary
|
#
|
||||||
|
# Note that controller-gen newer than 0.4.1 is needed for https://github.com/kubernetes-sigs/controller-tools/issues/444#issuecomment-680168439
|
||||||
|
# Otherwise we get errors like the below:
|
||||||
|
# Error: failed to install CRD crds/actions.summerwind.dev_runnersets.yaml: CustomResourceDefinition.apiextensions.k8s.io "runnersets.actions.summerwind.dev" is invalid: [spec.validation.openAPIV3Schema.properties[spec].properties[template].properties[spec].properties[containers].items.properties[ports].items.properties[protocol].default: Required value: this property is in x-kubernetes-list-map-keys, so it must have a default or be a required property, spec.validation.openAPIV3Schema.properties[spec].properties[template].properties[spec].properties[initContainers].items.properties[ports].items.properties[protocol].default: Required value: this property is in x-kubernetes-list-map-keys, so it must have a default or be a required property]
|
||||||
|
#
|
||||||
|
# Note that controller-gen newer than 0.6.0 is needed due to https://github.com/kubernetes-sigs/controller-tools/issues/448
|
||||||
|
# Otherwise ObjectMeta embedded in Spec results in empty on the storage.
|
||||||
controller-gen:
|
controller-gen:
|
||||||
ifeq (, $(shell which controller-gen))
|
ifeq (, $(shell which controller-gen))
|
||||||
ifeq (, $(wildcard $(GOBIN)/controller-gen))
|
ifeq (, $(wildcard $(GOBIN)/controller-gen))
|
||||||
@@ -215,7 +223,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.3.0 ;\
|
go get 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
|
||||||
|
|||||||
2
PROJECT
2
PROJECT
@@ -1,5 +1,5 @@
|
|||||||
domain: summerwind.dev
|
domain: summerwind.dev
|
||||||
repo: github.com/summerwind/actions-runner-controller
|
repo: github.com/actions-runner-controller/actions-runner-controller
|
||||||
resources:
|
resources:
|
||||||
- group: actions
|
- group: actions
|
||||||
kind: Runner
|
kind: Runner
|
||||||
|
|||||||
100
TROUBLESHOOTING.md
Normal file
100
TROUBLESHOOTING.md
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
# Troubleshooting
|
||||||
|
|
||||||
|
* [Invalid header field value](#invalid-header-field-value)
|
||||||
|
* [Runner coming up before network available](#runner-coming-up-before-network-available)
|
||||||
|
* [Deployment fails on GKE due to webhooks](#deployment-fails-on-gke-due-to-webhooks)
|
||||||
|
|
||||||
|
## Invalid header field value
|
||||||
|
|
||||||
|
**Problem**
|
||||||
|
|
||||||
|
```json
|
||||||
|
2020-11-12T22:17:30.693Z ERROR controller-runtime.controller Reconciler error
|
||||||
|
{
|
||||||
|
"controller": "runner",
|
||||||
|
"request": "actions-runner-system/runner-deployment-dk7q8-dk5c9",
|
||||||
|
"error": "failed to create registration token: Post \"https://api.github.com/orgs/$YOUR_ORG_HERE/actions/runners/registration-token\": net/http: invalid header field value \"Bearer $YOUR_TOKEN_HERE\\n\" for key Authorization"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Solution**
|
||||||
|
|
||||||
|
Your base64'ed PAT token has a new line at the end, it needs to be created without a `\n` added, either:
|
||||||
|
* `echo -n $TOKEN | base64`
|
||||||
|
* Create the secret as described in the docs using the shell and documented flags
|
||||||
|
|
||||||
|
## Runner coming up before network available
|
||||||
|
|
||||||
|
**Problem**
|
||||||
|
|
||||||
|
If you're running your action runners on a service mesh like Istio, you might
|
||||||
|
have problems with runner configuration accompanied by logs like:
|
||||||
|
|
||||||
|
```
|
||||||
|
....
|
||||||
|
runner Starting Runner listener with startup type: service
|
||||||
|
runner Started listener process
|
||||||
|
runner An error occurred: Not configured
|
||||||
|
runner Runner listener exited with error code 2
|
||||||
|
runner Runner listener exit with retryable error, re-launch runner in 5 seconds.
|
||||||
|
....
|
||||||
|
```
|
||||||
|
|
||||||
|
This is because the `istio-proxy` has not completed configuring itself when the
|
||||||
|
configuration script tries to communicate with the network.
|
||||||
|
|
||||||
|
More broadly, there are many other circumstances where the runner pod coming up first can cause issues.
|
||||||
|
|
||||||
|
**Solution**<br />
|
||||||
|
|
||||||
|
> Added originally to help users with older istio instances.
|
||||||
|
> Newer Istio instances can use Istio's `holdApplicationUntilProxyStarts` attribute ([istio/istio#11130](https://github.com/istio/istio/issues/11130)) to avoid having to delay starting up the runner.
|
||||||
|
> Please read the discussion in [#592](https://github.com/actions-runner-controller/actions-runner-controller/pull/592) for more information.
|
||||||
|
|
||||||
|
You can add a delay to the runner's entrypoint script by setting the `STARTUP_DELAY_IN_SECONDS` environment variable for the runner pod. This will cause the script to sleep X seconds, this works with any runner kind.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: actions.summerwind.dev/v1alpha1
|
||||||
|
kind: RunnerDeployment
|
||||||
|
metadata:
|
||||||
|
name: example-runnerdeployment-with-sleep
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
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
|
||||||
|
value: "5"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Deployment fails on GKE due to webhooks
|
||||||
|
|
||||||
|
**Problem**
|
||||||
|
|
||||||
|
Due to GKEs firewall settings you may run into the following errors when trying to deploy runners on a private GKE cluster:
|
||||||
|
|
||||||
|
```
|
||||||
|
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:
|
||||||
|
context deadline exceeded
|
||||||
|
```
|
||||||
|
|
||||||
|
**Solution**<br />
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
```
|
||||||
|
# 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
|
||||||
|
```
|
||||||
@@ -1,32 +1,84 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
set -e
|
set +e
|
||||||
|
|
||||||
runner_name=
|
repo_runnerdeployment_passed="skipped"
|
||||||
|
repo_runnerset_passed="skipped"
|
||||||
|
|
||||||
while [ -z "${runner_name}" ]; do
|
echo "Checking if RunnerDeployment repo test is set"
|
||||||
echo Finding the runner... 1>&2
|
if [ "${TEST_REPO}" ] && [ ! "${USE_RUNNERSET}" ]; then
|
||||||
sleep 1
|
runner_name=
|
||||||
runner_name=$(kubectl get runner --output=jsonpath="{.items[*].metadata.name}")
|
count=0
|
||||||
done
|
while [ $count -le 30 ]; do
|
||||||
|
echo "Finding Runner ..."
|
||||||
|
runner_name=$(kubectl get runner --output=jsonpath="{.items[*].metadata.name}")
|
||||||
|
if [ "${runner_name}" ]; then
|
||||||
|
while [ $count -le 30 ]; do
|
||||||
|
runner_pod_name=
|
||||||
|
echo "Found Runner \""${runner_name}"\""
|
||||||
|
echo "Finding underlying pod ..."
|
||||||
|
runner_pod_name=$(kubectl get pod --output=jsonpath="{.items[*].metadata.name}" | grep ${runner_name})
|
||||||
|
if [ "${runner_pod_name}" ]; then
|
||||||
|
echo "Found underlying pod \""${runner_pod_name}"\""
|
||||||
|
echo "Waiting for pod \""${runner_pod_name}"\" to become ready..."
|
||||||
|
kubectl wait pod/${runner_pod_name} --for condition=ready --timeout 270s
|
||||||
|
break 2
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
let "count=count+1"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
let "count=count+1"
|
||||||
|
done
|
||||||
|
if [ $count -ge 30 ]; then
|
||||||
|
repo_runnerdeployment_passed=false
|
||||||
|
else
|
||||||
|
repo_runnerdeployment_passed=true
|
||||||
|
fi
|
||||||
|
echo "Checking if RunnerSet repo test is set"
|
||||||
|
elif [ "${TEST_REPO}" ] && [ "${USE_RUNNERSET}" ]; then
|
||||||
|
runnerset_name=
|
||||||
|
count=0
|
||||||
|
while [ $count -le 30 ]; do
|
||||||
|
echo "Finding RunnerSet ..."
|
||||||
|
runnerset_name=$(kubectl get runnerset --output=jsonpath="{.items[*].metadata.name}")
|
||||||
|
if [ "${runnerset_name}" ]; then
|
||||||
|
while [ $count -le 30 ]; do
|
||||||
|
runnerset_pod_name=
|
||||||
|
echo "Found RunnerSet \""${runnerset_name}"\""
|
||||||
|
echo "Finding underlying pod ..."
|
||||||
|
runnerset_pod_name=$(kubectl get pod --output=jsonpath="{.items[*].metadata.name}" | grep ${runnerset_name})
|
||||||
|
echo "BEFORE IF"
|
||||||
|
if [ "${runnerset_pod_name}" ]; then
|
||||||
|
echo "AFTER IF"
|
||||||
|
echo "Found underlying pod \""${runnerset_pod_name}"\""
|
||||||
|
echo "Waiting for pod \""${runnerset_pod_name}"\" to become ready..."
|
||||||
|
kubectl wait pod/${runnerset_pod_name} --for condition=ready --timeout 270s
|
||||||
|
break 2
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
let "count=count+1"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
let "count=count+1"
|
||||||
|
done
|
||||||
|
if [ $count -ge 30 ]; then
|
||||||
|
repo_runnerset_passed=false
|
||||||
|
else
|
||||||
|
repo_runnerset_passed=true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
echo Found runner ${runner_name}.
|
if [ ${repo_runnerset_passed} == true ] || [ ${repo_runnerset_passed} == "skipped" ] && \
|
||||||
|
[ ${repo_runnerdeployment_passed} == true ] || [ ${repo_runnerdeployment_passed} == "skipped" ]; then
|
||||||
# Wait a bit to make sure the runner pod is created before looking for it.
|
echo "INFO : All tests passed or skipped"
|
||||||
sleep 2
|
echo "RunnerSet Repo Test Status : ${repo_runnerset_passed}"
|
||||||
|
echo "RunnerDeployment Repo Test Status : ${repo_runnerdeployment_passed}"
|
||||||
pod_name=
|
else
|
||||||
|
echo "ERROR : Some tests failed"
|
||||||
while [ -z "${pod_name}" ]; do
|
echo "RunnerSet Repo Test Status : ${repo_runnerset_passed}"
|
||||||
echo Finding the runner pod... 1>&2
|
echo "RunnerDeployment Repo Test Status : ${repo_runnerdeployment_passed}"
|
||||||
sleep 1
|
exit 1
|
||||||
pod_name=$(kubectl get pod --output=jsonpath="{.items[*].metadata.name}" | grep ${runner_name})
|
fi
|
||||||
done
|
|
||||||
|
|
||||||
echo Found pod ${pod_name}.
|
|
||||||
|
|
||||||
echo Waiting for pod ${runner_name} to become ready... 1>&2
|
|
||||||
|
|
||||||
kubectl wait pod/${runner_name} --for condition=ready --timeout 270s
|
|
||||||
|
|
||||||
echo All tests passed. 1>&2
|
|
||||||
@@ -6,6 +6,8 @@ tpe=${ACCEPTANCE_TEST_SECRET_TYPE}
|
|||||||
|
|
||||||
VALUES_FILE=${VALUES_FILE:-$(dirname $0)/values.yaml}
|
VALUES_FILE=${VALUES_FILE:-$(dirname $0)/values.yaml}
|
||||||
|
|
||||||
|
kubectl delete secret controller-manager || :
|
||||||
|
|
||||||
if [ "${tpe}" == "token" ]; then
|
if [ "${tpe}" == "token" ]; then
|
||||||
if ! kubectl get secret controller-manager -n actions-runner-system >/dev/null; then
|
if ! kubectl get secret controller-manager -n actions-runner-system >/dev/null; then
|
||||||
kubectl create secret generic controller-manager \
|
kubectl create secret generic controller-manager \
|
||||||
@@ -23,6 +25,16 @@ else
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ -n "${WEBHOOK_GITHUB_TOKEN}" ]; then
|
||||||
|
kubectl -n actions-runner-system delete secret \
|
||||||
|
github-webhook-server || :
|
||||||
|
kubectl -n actions-runner-system create secret generic \
|
||||||
|
github-webhook-server \
|
||||||
|
--from-literal=github_token=${WEBHOOK_GITHUB_TOKEN:?WEBHOOK_GITHUB_TOKEN must not be empty}
|
||||||
|
else
|
||||||
|
echo 'Skipped deploying secret "github-webhook-server". Set WEBHOOK_GITHUB_TOKEN to deploy.' 1>&2
|
||||||
|
fi
|
||||||
|
|
||||||
tool=${ACCEPTANCE_TEST_DEPLOYMENT_TOOL}
|
tool=${ACCEPTANCE_TEST_DEPLOYMENT_TOOL}
|
||||||
|
|
||||||
if [ "${tool}" == "helm" ]; then
|
if [ "${tool}" == "helm" ]; then
|
||||||
@@ -35,6 +47,9 @@ if [ "${tool}" == "helm" ]; then
|
|||||||
--set image.repository=${NAME} \
|
--set image.repository=${NAME} \
|
||||||
--set image.tag=${VERSION} \
|
--set image.tag=${VERSION} \
|
||||||
-f ${VALUES_FILE}
|
-f ${VALUES_FILE}
|
||||||
|
# To prevent `CustomResourceDefinition.apiextensions.k8s.io "runners.actions.summerwind.dev" is invalid: metadata.annotations: Too long: must have at most 262144 bytes`
|
||||||
|
# errors
|
||||||
|
kubectl create -f charts/actions-runner-controller/crds || kubectl replace -f charts/actions-runner-controller/crds
|
||||||
kubectl -n actions-runner-system wait deploy/actions-runner-controller --for condition=available --timeout 60s
|
kubectl -n actions-runner-system wait deploy/actions-runner-controller --for condition=available --timeout 60s
|
||||||
else
|
else
|
||||||
kubectl apply \
|
kubectl apply \
|
||||||
@@ -46,21 +61,41 @@ fi
|
|||||||
# 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 [ -n "${TEST_REPO}" ]; then
|
||||||
cat acceptance/testdata/runnerdeploy.yaml | envsubst | kubectl apply -f -
|
if [ "${USE_RUNNERSET}" -ne "false" ]; then
|
||||||
cat acceptance/testdata/hra.yaml | envsubst | kubectl apply -f -
|
cat acceptance/testdata/repo.runnerset.yaml | envsubst | kubectl apply -f -
|
||||||
|
cat acceptance/testdata/repo.runnerset.hra.yaml | envsubst | kubectl apply -f -
|
||||||
|
else
|
||||||
|
echo 'Deploying runnerdeployment and hra. Set USE_RUNNERSET if you want to deploy runnerset instead.'
|
||||||
|
cat acceptance/testdata/repo.runnerdeploy.yaml | envsubst | kubectl apply -f -
|
||||||
|
cat acceptance/testdata/repo.hra.yaml | envsubst | kubectl apply -f -
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
echo 'Skipped deploying runnerdeployment and hra. Set TEST_REPO to "yourorg/yourrepo" to deploy.'
|
echo 'Skipped deploying runnerdeployment and hra. Set TEST_REPO to "yourorg/yourrepo" to deploy.'
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -n "${TEST_ORG}" ]; then
|
if [ -n "${TEST_ORG}" ]; then
|
||||||
cat acceptance/testdata/org.runnerdeploy.yaml | envsubst | kubectl apply -f -
|
cat acceptance/testdata/runnerdeploy.envsubst.yaml | TEST_ENTERPRISE= TEST_REPO= NAME=org-runnerdeploy envsubst | kubectl apply -f -
|
||||||
|
|
||||||
if [ -n "${TEST_ORG_REPO}" ]; then
|
if [ -n "${TEST_ORG_GROUP}" ]; then
|
||||||
cat acceptance/testdata/org.hra.yaml | envsubst | kubectl apply -f -
|
cat acceptance/testdata/runnerdeploy.envsubst.yaml | TEST_ENTERPRISE= TEST_REPO= TEST_GROUP=${TEST_ORG_GROUP} NAME=orggroup-runnerdeploy envsubst | kubectl apply -f -
|
||||||
else
|
else
|
||||||
echo 'Skipped deploying organizational hra. Set TEST_ORG_REPO to "yourorg/yourrepo" to deploy.'
|
echo 'Skipped deploying enterprise runnerdeployment. Set TEST_ORG_GROUP to deploy.'
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
echo 'Skipped deploying organizational runnerdeployment. Set TEST_ORG to deploy.'
|
echo 'Skipped deploying organizational runnerdeployment. Set TEST_ORG to deploy.'
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ -n "${TEST_ENTERPRISE}" ]; then
|
||||||
|
cat acceptance/testdata/runnerdeploy.envsubst.yaml | TEST_ORG= TEST_REPO= NAME=enterprise-runnerdeploy envsubst | kubectl apply -f -
|
||||||
|
|
||||||
|
if [ -n "${TEST_ENTERPRISE_GROUP}" ]; then
|
||||||
|
cat acceptance/testdata/runnerdeploy.envsubst.yaml | TEST_ORG= TEST_REPO= TEST_GROUP=${TEST_ENTERPRISE_GROUP} NAME=enterprisegroup-runnerdeploy envsubst | kubectl apply -f -
|
||||||
|
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
|
||||||
|
|||||||
1
acceptance/testdata/org.hra.yaml
vendored
1
acceptance/testdata/org.hra.yaml
vendored
@@ -24,6 +24,7 @@ spec:
|
|||||||
minReplicas: 0
|
minReplicas: 0
|
||||||
minReplicas: 0
|
minReplicas: 0
|
||||||
maxReplicas: 5
|
maxReplicas: 5
|
||||||
|
# Used to test that HRA is working for org runners
|
||||||
metrics:
|
metrics:
|
||||||
- type: PercentageRunnersBusy
|
- type: PercentageRunnersBusy
|
||||||
scaleUpThreshold: '0.75'
|
scaleUpThreshold: '0.75'
|
||||||
|
|||||||
7
acceptance/testdata/org.runnerdeploy.yaml
vendored
7
acceptance/testdata/org.runnerdeploy.yaml
vendored
@@ -14,6 +14,11 @@ spec:
|
|||||||
image: ${RUNNER_NAME}:${RUNNER_TAG}
|
image: ${RUNNER_NAME}:${RUNNER_TAG}
|
||||||
imagePullPolicy: IfNotPresent
|
imagePullPolicy: IfNotPresent
|
||||||
|
|
||||||
|
# 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
|
||||||
#
|
#
|
||||||
@@ -30,6 +35,8 @@ spec:
|
|||||||
# labels:
|
# labels:
|
||||||
# - "mylabel 1"
|
# - "mylabel 1"
|
||||||
# - "mylabel 2"
|
# - "mylabel 2"
|
||||||
|
labels:
|
||||||
|
- "${RUNNER_LABEL}"
|
||||||
|
|
||||||
#
|
#
|
||||||
# Non-standard working directory
|
# Non-standard working directory
|
||||||
|
|||||||
@@ -14,6 +14,11 @@ spec:
|
|||||||
image: ${RUNNER_NAME}:${RUNNER_TAG}
|
image: ${RUNNER_NAME}:${RUNNER_TAG}
|
||||||
imagePullPolicy: IfNotPresent
|
imagePullPolicy: IfNotPresent
|
||||||
|
|
||||||
|
# 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
|
||||||
#
|
#
|
||||||
@@ -30,6 +35,8 @@ spec:
|
|||||||
# labels:
|
# labels:
|
||||||
# - "mylabel 1"
|
# - "mylabel 1"
|
||||||
# - "mylabel 2"
|
# - "mylabel 2"
|
||||||
|
labels:
|
||||||
|
- "${RUNNER_LABEL}"
|
||||||
|
|
||||||
#
|
#
|
||||||
# Non-standard working directory
|
# Non-standard working directory
|
||||||
29
acceptance/testdata/repo.runnerset.hra.yaml
vendored
Normal file
29
acceptance/testdata/repo.runnerset.hra.yaml
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
apiVersion: actions.summerwind.dev/v1alpha1
|
||||||
|
kind: HorizontalRunnerAutoscaler
|
||||||
|
metadata:
|
||||||
|
name: example-runnerset
|
||||||
|
spec:
|
||||||
|
scaleTargetRef:
|
||||||
|
kind: RunnerSet
|
||||||
|
name: example-runnerset
|
||||||
|
scaleUpTriggers:
|
||||||
|
- githubEvent:
|
||||||
|
checkRun:
|
||||||
|
types: ["created"]
|
||||||
|
status: "queued"
|
||||||
|
amount: 1
|
||||||
|
duration: "1m"
|
||||||
|
# RunnerSet doesn't support scale from/to zero yet
|
||||||
|
minReplicas: 1
|
||||||
|
maxReplicas: 5
|
||||||
|
# This should be less than 600(seconds, the default) for faster testing
|
||||||
|
scaleDownDelaySecondsAfterScaleOut: 60
|
||||||
|
metrics:
|
||||||
|
- type: PercentageRunnersBusy
|
||||||
|
scaleUpThreshold: '0.75'
|
||||||
|
scaleDownThreshold: '0.3'
|
||||||
|
scaleUpFactor: '2'
|
||||||
|
scaleDownFactor: '0.5'
|
||||||
|
- type: TotalNumberOfQueuedAndInProgressWorkflowRuns
|
||||||
|
repositoryNames:
|
||||||
|
- ${TEST_REPO}
|
||||||
59
acceptance/testdata/repo.runnerset.yaml
vendored
Normal file
59
acceptance/testdata/repo.runnerset.yaml
vendored
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
apiVersion: actions.summerwind.dev/v1alpha1
|
||||||
|
kind: RunnerSet
|
||||||
|
metadata:
|
||||||
|
name: example-runnerset
|
||||||
|
spec:
|
||||||
|
# MANDATORY because it is based on StatefulSet: Results in a below error when omitted:
|
||||||
|
# missing required field "selector" in dev.summerwind.actions.v1alpha1.RunnerSet.spec
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: example-runnerset
|
||||||
|
|
||||||
|
# MANDATORY because it is based on StatefulSet: Results in a below error when omitted:
|
||||||
|
# missing required field "serviceName" in dev.summerwind.actions.v1alpha1.RunnerSet.spec]
|
||||||
|
serviceName: example-runnerset
|
||||||
|
|
||||||
|
#replicas: 1
|
||||||
|
|
||||||
|
# From my limited testing, `ephemeral: true` is more reliable.
|
||||||
|
# Seomtimes, updating already deployed runners from `ephemeral: false` to `ephemeral: true` seems to
|
||||||
|
# result in queued jobs hanging forever.
|
||||||
|
ephemeral: ${TEST_EPHEMERAL}
|
||||||
|
|
||||||
|
repository: ${TEST_REPO}
|
||||||
|
#
|
||||||
|
# Custom runner image
|
||||||
|
#
|
||||||
|
image: ${RUNNER_NAME}:${RUNNER_TAG}
|
||||||
|
#
|
||||||
|
# dockerd within runner container
|
||||||
|
#
|
||||||
|
## Replace `mumoshu/actions-runner-dind:dev` with your dind image
|
||||||
|
#dockerdWithinRunnerContainer: true
|
||||||
|
#
|
||||||
|
# Set the MTU used by dockerd-managed network interfaces (including docker-build-ubuntu)
|
||||||
|
#
|
||||||
|
#dockerMTU: 1450
|
||||||
|
#Runner group
|
||||||
|
# labels:
|
||||||
|
# - "mylabel 1"
|
||||||
|
# - "mylabel 2"
|
||||||
|
labels:
|
||||||
|
- "${RUNNER_LABEL}"
|
||||||
|
#
|
||||||
|
# Non-standard working directory
|
||||||
|
#
|
||||||
|
# workDir: "/"
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: example-runnerset
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: runner
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
env:
|
||||||
|
- name: RUNNER_FEATURE_FLAG_EPHEMERAL
|
||||||
|
value: "${RUNNER_FEATURE_FLAG_EPHEMERAL}"
|
||||||
|
#- name: docker
|
||||||
|
# #image: mumoshu/actions-runner-dind:dev
|
||||||
61
acceptance/testdata/runnerdeploy.envsubst.yaml
vendored
Normal file
61
acceptance/testdata/runnerdeploy.envsubst.yaml
vendored
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
apiVersion: actions.summerwind.dev/v1alpha1
|
||||||
|
kind: RunnerDeployment
|
||||||
|
metadata:
|
||||||
|
name: ${NAME}
|
||||||
|
spec:
|
||||||
|
# replicas: 1
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
enterprise: ${TEST_ENTERPRISE}
|
||||||
|
group: ${TEST_GROUP}
|
||||||
|
organization: ${TEST_ORG}
|
||||||
|
repository: ${TEST_REPO}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Custom runner image
|
||||||
|
#
|
||||||
|
image: ${RUNNER_NAME}:${RUNNER_TAG}
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
|
||||||
|
# Whether to pass --ephemeral (true) or --once (false, deprecated)
|
||||||
|
env:
|
||||||
|
- name: RUNNER_FEATURE_FLAG_EPHEMERAL
|
||||||
|
value: "${RUNNER_FEATURE_FLAG_EPHEMERAL}"
|
||||||
|
|
||||||
|
#
|
||||||
|
# dockerd within runner container
|
||||||
|
#
|
||||||
|
## Replace `mumoshu/actions-runner-dind:dev` with your dind image
|
||||||
|
#dockerdWithinRunnerContainer: true
|
||||||
|
#image: mumoshu/actions-runner-dind:dev
|
||||||
|
|
||||||
|
#
|
||||||
|
# Set the MTU used by dockerd-managed network interfaces (including docker-build-ubuntu)
|
||||||
|
#
|
||||||
|
#dockerMTU: 1450
|
||||||
|
|
||||||
|
#Runner group
|
||||||
|
# labels:
|
||||||
|
# - "mylabel 1"
|
||||||
|
# - "mylabel 2"
|
||||||
|
labels:
|
||||||
|
- "${RUNNER_LABEL}"
|
||||||
|
|
||||||
|
#
|
||||||
|
# Non-standard working directory
|
||||||
|
#
|
||||||
|
# workDir: "/"
|
||||||
|
---
|
||||||
|
apiVersion: actions.summerwind.dev/v1alpha1
|
||||||
|
kind: HorizontalRunnerAutoscaler
|
||||||
|
metadata:
|
||||||
|
name: ${NAME}
|
||||||
|
spec:
|
||||||
|
scaleTargetRef:
|
||||||
|
name: ${NAME}
|
||||||
|
scaleUpTriggers:
|
||||||
|
- githubEvent: {}
|
||||||
|
amount: 1
|
||||||
|
duration: "1m"
|
||||||
|
minReplicas: 0
|
||||||
|
maxReplicas: 10
|
||||||
@@ -1,12 +1,15 @@
|
|||||||
# Set actions-runner-controller settings for testing
|
# Set actions-runner-controller settings for testing
|
||||||
githubAPICacheDuration: 10s
|
githubAPICacheDuration: 10s
|
||||||
githubWebhookServer:
|
githubWebhookServer:
|
||||||
|
logLevel: debug
|
||||||
enabled: true
|
enabled: true
|
||||||
labels: {}
|
labels: {}
|
||||||
replicaCount: 1
|
replicaCount: 1
|
||||||
syncPeriod: 10m
|
syncPeriod: 10m
|
||||||
|
useRunnerGroupsVisibility: true
|
||||||
secret:
|
secret:
|
||||||
create: true
|
enabled: true
|
||||||
|
# create: true
|
||||||
name: "github-webhook-server"
|
name: "github-webhook-server"
|
||||||
### GitHub Webhook Configuration
|
### GitHub Webhook Configuration
|
||||||
#github_webhook_secret_token: ""
|
#github_webhook_secret_token: ""
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ type HorizontalRunnerAutoscalerSpec struct {
|
|||||||
// +optional
|
// +optional
|
||||||
MinReplicas *int `json:"minReplicas,omitempty"`
|
MinReplicas *int `json:"minReplicas,omitempty"`
|
||||||
|
|
||||||
// MinReplicas is the maximum number of replicas the deployment is allowed to scale
|
// MaxReplicas is the maximum number of replicas the deployment is allowed to scale
|
||||||
// +optional
|
// +optional
|
||||||
MaxReplicas *int `json:"maxReplicas,omitempty"`
|
MaxReplicas *int `json:"maxReplicas,omitempty"`
|
||||||
|
|
||||||
@@ -84,6 +84,10 @@ type CheckRunSpec struct {
|
|||||||
// Note that check_run name seem to equal to the job name you've defined in your actions workflow yaml file.
|
// Note that check_run name seem to equal to the job name you've defined in your actions workflow yaml file.
|
||||||
// So it is very likely that you can utilize this to trigger depending on the job.
|
// So it is very likely that you can utilize this to trigger depending on the job.
|
||||||
Names []string `json:"names,omitempty"`
|
Names []string `json:"names,omitempty"`
|
||||||
|
|
||||||
|
// Repositories is a list of GitHub repositories.
|
||||||
|
// Any check_run event whose repository matches one of repositories in the list can trigger autoscaling.
|
||||||
|
Repositories []string `json:"repositories,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://docs.github.com/en/actions/reference/events-that-trigger-workflows#pull_request
|
// https://docs.github.com/en/actions/reference/events-that-trigger-workflows#pull_request
|
||||||
@@ -106,6 +110,12 @@ type CapacityReservation struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ScaleTargetRef struct {
|
type ScaleTargetRef struct {
|
||||||
|
// Kind is the type of resource being referenced
|
||||||
|
// +optional
|
||||||
|
// +kubebuilder:validation:Enum=RunnerDeployment;RunnerSet
|
||||||
|
Kind string `json:"kind,omitempty"`
|
||||||
|
|
||||||
|
// Name is the name of resource being referenced
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,6 +227,7 @@ type CacheEntry struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// +kubebuilder:object:root=true
|
// +kubebuilder:object:root=true
|
||||||
|
// +kubebuilder:resource:shortName=hra
|
||||||
// +kubebuilder:subresource:status
|
// +kubebuilder:subresource:status
|
||||||
// +kubebuilder:printcolumn:JSONPath=".spec.minReplicas",name=Min,type=number
|
// +kubebuilder:printcolumn:JSONPath=".spec.minReplicas",name=Min,type=number
|
||||||
// +kubebuilder:printcolumn:JSONPath=".spec.maxReplicas",name=Max,type=number
|
// +kubebuilder:printcolumn:JSONPath=".spec.maxReplicas",name=Max,type=number
|
||||||
|
|||||||
@@ -27,6 +27,11 @@ import (
|
|||||||
|
|
||||||
// RunnerSpec defines the desired state of Runner
|
// RunnerSpec defines the desired state of Runner
|
||||||
type RunnerSpec struct {
|
type RunnerSpec struct {
|
||||||
|
RunnerConfig `json:",inline"`
|
||||||
|
RunnerPodSpec `json:",inline"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RunnerConfig struct {
|
||||||
// +optional
|
// +optional
|
||||||
// +kubebuilder:validation:Pattern=`^[^/]+$`
|
// +kubebuilder:validation:Pattern=`^[^/]+$`
|
||||||
Enterprise string `json:"enterprise,omitempty"`
|
Enterprise string `json:"enterprise,omitempty"`
|
||||||
@@ -48,53 +53,12 @@ type RunnerSpec struct {
|
|||||||
// +optional
|
// +optional
|
||||||
Ephemeral *bool `json:"ephemeral,omitempty"`
|
Ephemeral *bool `json:"ephemeral,omitempty"`
|
||||||
|
|
||||||
// +optional
|
|
||||||
Containers []corev1.Container `json:"containers,omitempty"`
|
|
||||||
// +optional
|
|
||||||
DockerdContainerResources corev1.ResourceRequirements `json:"dockerdContainerResources,omitempty"`
|
|
||||||
// +optional
|
|
||||||
DockerVolumeMounts []corev1.VolumeMount `json:"dockerVolumeMounts,omitempty"`
|
|
||||||
// +optional
|
|
||||||
Resources corev1.ResourceRequirements `json:"resources,omitempty"`
|
|
||||||
// +optional
|
|
||||||
VolumeMounts []corev1.VolumeMount `json:"volumeMounts,omitempty"`
|
|
||||||
// +optional
|
|
||||||
EnvFrom []corev1.EnvFromSource `json:"envFrom,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
// +optional
|
||||||
Image string `json:"image"`
|
Image string `json:"image"`
|
||||||
// +optional
|
|
||||||
ImagePullPolicy corev1.PullPolicy `json:"imagePullPolicy,omitempty"`
|
|
||||||
// +optional
|
|
||||||
Env []corev1.EnvVar `json:"env,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
Volumes []corev1.Volume `json:"volumes,omitempty"`
|
|
||||||
// +optional
|
// +optional
|
||||||
WorkDir string `json:"workDir,omitempty"`
|
WorkDir string `json:"workDir,omitempty"`
|
||||||
|
|
||||||
// +optional
|
|
||||||
InitContainers []corev1.Container `json:"initContainers,omitempty"`
|
|
||||||
// +optional
|
|
||||||
SidecarContainers []corev1.Container `json:"sidecarContainers,omitempty"`
|
|
||||||
// +optional
|
|
||||||
NodeSelector map[string]string `json:"nodeSelector,omitempty"`
|
|
||||||
// +optional
|
|
||||||
ServiceAccountName string `json:"serviceAccountName,omitempty"`
|
|
||||||
// +optional
|
|
||||||
AutomountServiceAccountToken *bool `json:"automountServiceAccountToken,omitempty"`
|
|
||||||
// +optional
|
|
||||||
SecurityContext *corev1.PodSecurityContext `json:"securityContext,omitempty"`
|
|
||||||
// +optional
|
|
||||||
ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets,omitempty"`
|
|
||||||
// +optional
|
|
||||||
Affinity *corev1.Affinity `json:"affinity,omitempty"`
|
|
||||||
// +optional
|
|
||||||
Tolerations []corev1.Toleration `json:"tolerations,omitempty"`
|
|
||||||
// +optional
|
|
||||||
EphemeralContainers []corev1.EphemeralContainer `json:"ephemeralContainers,omitempty"`
|
|
||||||
// +optional
|
|
||||||
TerminationGracePeriodSeconds *int64 `json:"terminationGracePeriodSeconds,omitempty"`
|
|
||||||
// +optional
|
// +optional
|
||||||
DockerdWithinRunnerContainer *bool `json:"dockerdWithinRunnerContainer,omitempty"`
|
DockerdWithinRunnerContainer *bool `json:"dockerdWithinRunnerContainer,omitempty"`
|
||||||
// +optional
|
// +optional
|
||||||
@@ -104,14 +68,92 @@ type RunnerSpec struct {
|
|||||||
// +optional
|
// +optional
|
||||||
DockerRegistryMirror *string `json:"dockerRegistryMirror,omitempty"`
|
DockerRegistryMirror *string `json:"dockerRegistryMirror,omitempty"`
|
||||||
// +optional
|
// +optional
|
||||||
HostAliases []corev1.HostAlias `json:"hostAliases,omitempty"`
|
|
||||||
// +optional
|
|
||||||
VolumeSizeLimit *resource.Quantity `json:"volumeSizeLimit,omitempty"`
|
VolumeSizeLimit *resource.Quantity `json:"volumeSizeLimit,omitempty"`
|
||||||
|
// +optional
|
||||||
|
VolumeStorageMedium *string `json:"volumeStorageMedium,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunnerPodSpec defines the desired pod spec fields of the runner pod
|
||||||
|
type RunnerPodSpec struct {
|
||||||
|
// +optional
|
||||||
|
DockerdContainerResources corev1.ResourceRequirements `json:"dockerdContainerResources,omitempty"`
|
||||||
|
|
||||||
|
// +optional
|
||||||
|
DockerVolumeMounts []corev1.VolumeMount `json:"dockerVolumeMounts,omitempty"`
|
||||||
|
|
||||||
|
// +optional
|
||||||
|
DockerEnv []corev1.EnvVar `json:"dockerEnv,omitempty"`
|
||||||
|
|
||||||
|
// +optional
|
||||||
|
Containers []corev1.Container `json:"containers,omitempty"`
|
||||||
|
|
||||||
|
// +optional
|
||||||
|
ImagePullPolicy corev1.PullPolicy `json:"imagePullPolicy,omitempty"`
|
||||||
|
|
||||||
|
// +optional
|
||||||
|
Env []corev1.EnvVar `json:"env,omitempty"`
|
||||||
|
|
||||||
|
// +optional
|
||||||
|
EnvFrom []corev1.EnvFromSource `json:"envFrom,omitempty"`
|
||||||
|
|
||||||
|
// +optional
|
||||||
|
Resources corev1.ResourceRequirements `json:"resources,omitempty"`
|
||||||
|
|
||||||
|
// +optional
|
||||||
|
VolumeMounts []corev1.VolumeMount `json:"volumeMounts,omitempty"`
|
||||||
|
|
||||||
|
// +optional
|
||||||
|
Volumes []corev1.Volume `json:"volumes,omitempty"`
|
||||||
|
|
||||||
|
// +optional
|
||||||
|
EnableServiceLinks *bool `json:"enableServiceLinks,omitempty"`
|
||||||
|
|
||||||
|
// +optional
|
||||||
|
InitContainers []corev1.Container `json:"initContainers,omitempty"`
|
||||||
|
|
||||||
|
// +optional
|
||||||
|
NodeSelector map[string]string `json:"nodeSelector,omitempty"`
|
||||||
|
|
||||||
|
// +optional
|
||||||
|
ServiceAccountName string `json:"serviceAccountName,omitempty"`
|
||||||
|
|
||||||
|
// +optional
|
||||||
|
AutomountServiceAccountToken *bool `json:"automountServiceAccountToken,omitempty"`
|
||||||
|
|
||||||
|
// +optional
|
||||||
|
SidecarContainers []corev1.Container `json:"sidecarContainers,omitempty"`
|
||||||
|
|
||||||
|
// +optional
|
||||||
|
SecurityContext *corev1.PodSecurityContext `json:"securityContext,omitempty"`
|
||||||
|
|
||||||
|
// +optional
|
||||||
|
ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets,omitempty"`
|
||||||
|
|
||||||
|
// +optional
|
||||||
|
Affinity *corev1.Affinity `json:"affinity,omitempty"`
|
||||||
|
|
||||||
|
// +optional
|
||||||
|
Tolerations []corev1.Toleration `json:"tolerations,omitempty"`
|
||||||
|
|
||||||
|
// +optional
|
||||||
|
TerminationGracePeriodSeconds *int64 `json:"terminationGracePeriodSeconds,omitempty"`
|
||||||
|
|
||||||
|
// +optional
|
||||||
|
EphemeralContainers []corev1.EphemeralContainer `json:"ephemeralContainers,omitempty"`
|
||||||
|
|
||||||
|
// +optional
|
||||||
|
HostAliases []corev1.HostAlias `json:"hostAliases,omitempty"`
|
||||||
|
|
||||||
|
// +optional
|
||||||
|
TopologySpreadConstraints []corev1.TopologySpreadConstraint `json:"topologySpreadConstraint,omitempty"`
|
||||||
|
|
||||||
// RuntimeClassName is the container runtime configuration that containers should run under.
|
// RuntimeClassName is the container runtime configuration that containers should run under.
|
||||||
// More info: https://kubernetes.io/docs/concepts/containers/runtime-class
|
// More info: https://kubernetes.io/docs/concepts/containers/runtime-class
|
||||||
// +optional
|
// +optional
|
||||||
RuntimeClassName *string `json:"runtimeClassName,omitempty"`
|
RuntimeClassName *string `json:"runtimeClassName,omitempty"`
|
||||||
|
|
||||||
|
// +optional
|
||||||
|
DnsConfig []corev1.PodDNSConfig `json:"dnsConfig,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateRepository validates repository field.
|
// ValidateRepository validates repository field.
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
ctrl "sigs.k8s.io/controller-runtime"
|
ctrl "sigs.k8s.io/controller-runtime"
|
||||||
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
|
logf "sigs.k8s.io/controller-runtime/pkg/log"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/webhook"
|
"sigs.k8s.io/controller-runtime/pkg/webhook"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ func (r *Runner) SetupWebhookWithManager(mgr ctrl.Manager) error {
|
|||||||
Complete()
|
Complete()
|
||||||
}
|
}
|
||||||
|
|
||||||
// +kubebuilder:webhook:path=/mutate-actions-summerwind-dev-v1alpha1-runner,verbs=create;update,mutating=true,failurePolicy=fail,groups=actions.summerwind.dev,resources=runners,versions=v1alpha1,name=mutate.runner.actions.summerwind.dev,sideEffects=None
|
// +kubebuilder:webhook:path=/mutate-actions-summerwind-dev-v1alpha1-runner,verbs=create;update,mutating=true,failurePolicy=fail,groups=actions.summerwind.dev,resources=runners,versions=v1alpha1,name=mutate.runner.actions.summerwind.dev,sideEffects=None,admissionReviewVersions=v1beta1
|
||||||
|
|
||||||
var _ webhook.Defaulter = &Runner{}
|
var _ webhook.Defaulter = &Runner{}
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ func (r *Runner) Default() {
|
|||||||
// Nothing to do.
|
// Nothing to do.
|
||||||
}
|
}
|
||||||
|
|
||||||
// +kubebuilder:webhook:path=/validate-actions-summerwind-dev-v1alpha1-runner,verbs=create;update,mutating=false,failurePolicy=fail,groups=actions.summerwind.dev,resources=runners,versions=v1alpha1,name=validate.runner.actions.summerwind.dev,sideEffects=None
|
// +kubebuilder:webhook:path=/validate-actions-summerwind-dev-v1alpha1-runner,verbs=create;update,mutating=false,failurePolicy=fail,groups=actions.summerwind.dev,resources=runners,versions=v1alpha1,name=validate.runner.actions.summerwind.dev,sideEffects=None,admissionReviewVersions=v1beta1
|
||||||
|
|
||||||
var _ webhook.Validator = &Runner{}
|
var _ webhook.Validator = &Runner{}
|
||||||
|
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ type RunnerDeploymentStatus struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// +kubebuilder:object:root=true
|
// +kubebuilder:object:root=true
|
||||||
|
// +kubebuilder:resource:shortName=rdeploy
|
||||||
// +kubebuilder:subresource:status
|
// +kubebuilder:subresource:status
|
||||||
// +kubebuilder:printcolumn:JSONPath=".spec.replicas",name=Desired,type=number
|
// +kubebuilder:printcolumn:JSONPath=".spec.replicas",name=Desired,type=number
|
||||||
// +kubebuilder:printcolumn:JSONPath=".status.replicas",name=Current,type=number
|
// +kubebuilder:printcolumn:JSONPath=".status.replicas",name=Current,type=number
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
ctrl "sigs.k8s.io/controller-runtime"
|
ctrl "sigs.k8s.io/controller-runtime"
|
||||||
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
|
logf "sigs.k8s.io/controller-runtime/pkg/log"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/webhook"
|
"sigs.k8s.io/controller-runtime/pkg/webhook"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ func (r *RunnerDeployment) SetupWebhookWithManager(mgr ctrl.Manager) error {
|
|||||||
Complete()
|
Complete()
|
||||||
}
|
}
|
||||||
|
|
||||||
// +kubebuilder:webhook:path=/mutate-actions-summerwind-dev-v1alpha1-runnerdeployment,verbs=create;update,mutating=true,failurePolicy=fail,groups=actions.summerwind.dev,resources=runnerdeployments,versions=v1alpha1,name=mutate.runnerdeployment.actions.summerwind.dev
|
// +kubebuilder:webhook:path=/mutate-actions-summerwind-dev-v1alpha1-runnerdeployment,verbs=create;update,mutating=true,failurePolicy=fail,groups=actions.summerwind.dev,resources=runnerdeployments,versions=v1alpha1,name=mutate.runnerdeployment.actions.summerwind.dev,sideEffects=None,admissionReviewVersions=v1beta1
|
||||||
|
|
||||||
var _ webhook.Defaulter = &RunnerDeployment{}
|
var _ webhook.Defaulter = &RunnerDeployment{}
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ func (r *RunnerDeployment) Default() {
|
|||||||
// Nothing to do.
|
// Nothing to do.
|
||||||
}
|
}
|
||||||
|
|
||||||
// +kubebuilder:webhook:path=/validate-actions-summerwind-dev-v1alpha1-runnerdeployment,verbs=create;update,mutating=false,failurePolicy=fail,groups=actions.summerwind.dev,resources=runnerdeployments,versions=v1alpha1,name=validate.runnerdeployment.actions.summerwind.dev
|
// +kubebuilder:webhook:path=/validate-actions-summerwind-dev-v1alpha1-runnerdeployment,verbs=create;update,mutating=false,failurePolicy=fail,groups=actions.summerwind.dev,resources=runnerdeployments,versions=v1alpha1,name=validate.runnerdeployment.actions.summerwind.dev,sideEffects=None,admissionReviewVersions=v1beta1
|
||||||
|
|
||||||
var _ webhook.Validator = &RunnerDeployment{}
|
var _ webhook.Validator = &RunnerDeployment{}
|
||||||
|
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ type RunnerTemplate struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// +kubebuilder:object:root=true
|
// +kubebuilder:object:root=true
|
||||||
|
// +kubebuilder:resource:shortName=rrs
|
||||||
// +kubebuilder:subresource:status
|
// +kubebuilder:subresource:status
|
||||||
// +kubebuilder:printcolumn:JSONPath=".spec.replicas",name=Desired,type=number
|
// +kubebuilder:printcolumn:JSONPath=".spec.replicas",name=Desired,type=number
|
||||||
// +kubebuilder:printcolumn:JSONPath=".status.replicas",name=Current,type=number
|
// +kubebuilder:printcolumn:JSONPath=".status.replicas",name=Current,type=number
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
ctrl "sigs.k8s.io/controller-runtime"
|
ctrl "sigs.k8s.io/controller-runtime"
|
||||||
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
|
logf "sigs.k8s.io/controller-runtime/pkg/log"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/webhook"
|
"sigs.k8s.io/controller-runtime/pkg/webhook"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ func (r *RunnerReplicaSet) SetupWebhookWithManager(mgr ctrl.Manager) error {
|
|||||||
Complete()
|
Complete()
|
||||||
}
|
}
|
||||||
|
|
||||||
// +kubebuilder:webhook:path=/mutate-actions-summerwind-dev-v1alpha1-runnerreplicaset,verbs=create;update,mutating=true,failurePolicy=fail,groups=actions.summerwind.dev,resources=runnerreplicasets,versions=v1alpha1,name=mutate.runnerreplicaset.actions.summerwind.dev,sideEffects=None
|
// +kubebuilder:webhook:path=/mutate-actions-summerwind-dev-v1alpha1-runnerreplicaset,verbs=create;update,mutating=true,failurePolicy=fail,groups=actions.summerwind.dev,resources=runnerreplicasets,versions=v1alpha1,name=mutate.runnerreplicaset.actions.summerwind.dev,sideEffects=None,admissionReviewVersions=v1beta1
|
||||||
|
|
||||||
var _ webhook.Defaulter = &RunnerReplicaSet{}
|
var _ webhook.Defaulter = &RunnerReplicaSet{}
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ func (r *RunnerReplicaSet) Default() {
|
|||||||
// Nothing to do.
|
// Nothing to do.
|
||||||
}
|
}
|
||||||
|
|
||||||
// +kubebuilder:webhook:path=/validate-actions-summerwind-dev-v1alpha1-runnerreplicaset,verbs=create;update,mutating=false,failurePolicy=fail,groups=actions.summerwind.dev,resources=runnerreplicasets,versions=v1alpha1,name=validate.runnerreplicaset.actions.summerwind.dev,sideEffects=None
|
// +kubebuilder:webhook:path=/validate-actions-summerwind-dev-v1alpha1-runnerreplicaset,verbs=create;update,mutating=false,failurePolicy=fail,groups=actions.summerwind.dev,resources=runnerreplicasets,versions=v1alpha1,name=validate.runnerreplicaset.actions.summerwind.dev,sideEffects=None,admissionReviewVersions=v1beta1
|
||||||
|
|
||||||
var _ webhook.Validator = &RunnerReplicaSet{}
|
var _ webhook.Validator = &RunnerReplicaSet{}
|
||||||
|
|
||||||
|
|||||||
88
api/v1alpha1/runnerset_types.go
Normal file
88
api/v1alpha1/runnerset_types.go
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 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 v1alpha1
|
||||||
|
|
||||||
|
import (
|
||||||
|
appsv1 "k8s.io/api/apps/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RunnerSetSpec defines the desired state of RunnerSet
|
||||||
|
type RunnerSetSpec struct {
|
||||||
|
RunnerConfig `json:",inline"`
|
||||||
|
|
||||||
|
appsv1.StatefulSetSpec `json:",inline"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RunnerSetStatus struct {
|
||||||
|
// See K8s deployment controller code for reference
|
||||||
|
// https://github.com/kubernetes/kubernetes/blob/ea0764452222146c47ec826977f49d7001b0ea8c/pkg/controller/deployment/sync.go#L487-L505
|
||||||
|
|
||||||
|
// AvailableReplicas is the total number of available runners which have been successfully registered to GitHub and still running.
|
||||||
|
// This corresponds to the sum of status.availableReplicas of all the runner replica sets.
|
||||||
|
// +optional
|
||||||
|
CurrentReplicas *int `json:"availableReplicas"`
|
||||||
|
|
||||||
|
// ReadyReplicas is the total number of available runners which have been successfully registered to GitHub and still running.
|
||||||
|
// This corresponds to the sum of status.readyReplicas of all the runner replica sets.
|
||||||
|
// +optional
|
||||||
|
ReadyReplicas *int `json:"readyReplicas"`
|
||||||
|
|
||||||
|
// ReadyReplicas is the total number of available runners which have been successfully registered to GitHub and still running.
|
||||||
|
// This corresponds to status.replicas of the runner replica set that has the desired template hash.
|
||||||
|
// +optional
|
||||||
|
UpdatedReplicas *int `json:"updatedReplicas"`
|
||||||
|
|
||||||
|
// DesiredReplicas is the total number of desired, non-terminated and latest pods to be set for the primary RunnerSet
|
||||||
|
// This doesn't include outdated pods while upgrading the deployment and replacing the runnerset.
|
||||||
|
// +optional
|
||||||
|
DesiredReplicas *int `json:"desiredReplicas"`
|
||||||
|
|
||||||
|
// Replicas is the total number of replicas
|
||||||
|
// +optional
|
||||||
|
Replicas *int `json:"replicas"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// +kubebuilder:object:root=true
|
||||||
|
// +kubebuilder:subresource:status
|
||||||
|
// +kubebuilder:printcolumn:JSONPath=".spec.replicas",name=Desired,type=number
|
||||||
|
// +kubebuilder:printcolumn:JSONPath=".status.replicas",name=Current,type=number
|
||||||
|
// +kubebuilder:printcolumn:JSONPath=".status.updatedReplicas",name=Up-To-Date,type=number
|
||||||
|
// +kubebuilder:printcolumn:JSONPath=".status.availableReplicas",name=Available,type=number
|
||||||
|
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
|
||||||
|
|
||||||
|
// RunnerSet is the Schema for the runnersets API
|
||||||
|
type RunnerSet struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||||
|
|
||||||
|
Spec RunnerSetSpec `json:"spec,omitempty"`
|
||||||
|
Status RunnerSetStatus `json:"status,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// +kubebuilder:object:root=true
|
||||||
|
|
||||||
|
// RunnerList contains a list of Runner
|
||||||
|
type RunnerSetList struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
metav1.ListMeta `json:"metadata,omitempty"`
|
||||||
|
Items []RunnerSet `json:"items"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
SchemeBuilder.Register(&RunnerSet{}, &RunnerSetList{})
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
//go:build !ignore_autogenerated
|
||||||
// +build !ignore_autogenerated
|
// +build !ignore_autogenerated
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -71,6 +72,11 @@ func (in *CheckRunSpec) DeepCopyInto(out *CheckRunSpec) {
|
|||||||
*out = make([]string, len(*in))
|
*out = make([]string, len(*in))
|
||||||
copy(*out, *in)
|
copy(*out, *in)
|
||||||
}
|
}
|
||||||
|
if in.Repositories != nil {
|
||||||
|
in, out := &in.Repositories, &out.Repositories
|
||||||
|
*out = make([]string, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CheckRunSpec.
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CheckRunSpec.
|
||||||
@@ -370,6 +376,61 @@ func (in *Runner) DeepCopyObject() runtime.Object {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *RunnerConfig) DeepCopyInto(out *RunnerConfig) {
|
||||||
|
*out = *in
|
||||||
|
if in.Labels != nil {
|
||||||
|
in, out := &in.Labels, &out.Labels
|
||||||
|
*out = make([]string, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
|
if in.Ephemeral != nil {
|
||||||
|
in, out := &in.Ephemeral, &out.Ephemeral
|
||||||
|
*out = new(bool)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
if in.DockerdWithinRunnerContainer != nil {
|
||||||
|
in, out := &in.DockerdWithinRunnerContainer, &out.DockerdWithinRunnerContainer
|
||||||
|
*out = new(bool)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
if in.DockerEnabled != nil {
|
||||||
|
in, out := &in.DockerEnabled, &out.DockerEnabled
|
||||||
|
*out = new(bool)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
if in.DockerMTU != nil {
|
||||||
|
in, out := &in.DockerMTU, &out.DockerMTU
|
||||||
|
*out = new(int64)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
if in.DockerRegistryMirror != nil {
|
||||||
|
in, out := &in.DockerRegistryMirror, &out.DockerRegistryMirror
|
||||||
|
*out = new(string)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
if in.VolumeSizeLimit != nil {
|
||||||
|
in, out := &in.VolumeSizeLimit, &out.VolumeSizeLimit
|
||||||
|
x := (*in).DeepCopy()
|
||||||
|
*out = &x
|
||||||
|
}
|
||||||
|
if in.VolumeStorageMedium != nil {
|
||||||
|
in, out := &in.VolumeStorageMedium, &out.VolumeStorageMedium
|
||||||
|
*out = new(string)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RunnerConfig.
|
||||||
|
func (in *RunnerConfig) DeepCopy() *RunnerConfig {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(RunnerConfig)
|
||||||
|
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 *RunnerDeployment) DeepCopyInto(out *RunnerDeployment) {
|
func (in *RunnerDeployment) DeepCopyInto(out *RunnerDeployment) {
|
||||||
*out = *in
|
*out = *in
|
||||||
@@ -527,6 +588,163 @@ func (in *RunnerList) DeepCopyObject() runtime.Object {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *RunnerPodSpec) DeepCopyInto(out *RunnerPodSpec) {
|
||||||
|
*out = *in
|
||||||
|
in.DockerdContainerResources.DeepCopyInto(&out.DockerdContainerResources)
|
||||||
|
if in.DockerVolumeMounts != nil {
|
||||||
|
in, out := &in.DockerVolumeMounts, &out.DockerVolumeMounts
|
||||||
|
*out = make([]v1.VolumeMount, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if in.DockerEnv != nil {
|
||||||
|
in, out := &in.DockerEnv, &out.DockerEnv
|
||||||
|
*out = make([]v1.EnvVar, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if in.Containers != nil {
|
||||||
|
in, out := &in.Containers, &out.Containers
|
||||||
|
*out = make([]v1.Container, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if in.Env != nil {
|
||||||
|
in, out := &in.Env, &out.Env
|
||||||
|
*out = make([]v1.EnvVar, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if in.EnvFrom != nil {
|
||||||
|
in, out := &in.EnvFrom, &out.EnvFrom
|
||||||
|
*out = make([]v1.EnvFromSource, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
in.Resources.DeepCopyInto(&out.Resources)
|
||||||
|
if in.VolumeMounts != nil {
|
||||||
|
in, out := &in.VolumeMounts, &out.VolumeMounts
|
||||||
|
*out = make([]v1.VolumeMount, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if in.Volumes != nil {
|
||||||
|
in, out := &in.Volumes, &out.Volumes
|
||||||
|
*out = make([]v1.Volume, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if in.EnableServiceLinks != nil {
|
||||||
|
in, out := &in.EnableServiceLinks, &out.EnableServiceLinks
|
||||||
|
*out = new(bool)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
if in.InitContainers != nil {
|
||||||
|
in, out := &in.InitContainers, &out.InitContainers
|
||||||
|
*out = make([]v1.Container, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if in.NodeSelector != nil {
|
||||||
|
in, out := &in.NodeSelector, &out.NodeSelector
|
||||||
|
*out = make(map[string]string, len(*in))
|
||||||
|
for key, val := range *in {
|
||||||
|
(*out)[key] = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if in.AutomountServiceAccountToken != nil {
|
||||||
|
in, out := &in.AutomountServiceAccountToken, &out.AutomountServiceAccountToken
|
||||||
|
*out = new(bool)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
if in.SidecarContainers != nil {
|
||||||
|
in, out := &in.SidecarContainers, &out.SidecarContainers
|
||||||
|
*out = make([]v1.Container, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if in.SecurityContext != nil {
|
||||||
|
in, out := &in.SecurityContext, &out.SecurityContext
|
||||||
|
*out = new(v1.PodSecurityContext)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
|
if in.ImagePullSecrets != nil {
|
||||||
|
in, out := &in.ImagePullSecrets, &out.ImagePullSecrets
|
||||||
|
*out = make([]v1.LocalObjectReference, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
|
if in.Affinity != nil {
|
||||||
|
in, out := &in.Affinity, &out.Affinity
|
||||||
|
*out = new(v1.Affinity)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
|
if in.Tolerations != nil {
|
||||||
|
in, out := &in.Tolerations, &out.Tolerations
|
||||||
|
*out = make([]v1.Toleration, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if in.TerminationGracePeriodSeconds != nil {
|
||||||
|
in, out := &in.TerminationGracePeriodSeconds, &out.TerminationGracePeriodSeconds
|
||||||
|
*out = new(int64)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
if in.EphemeralContainers != nil {
|
||||||
|
in, out := &in.EphemeralContainers, &out.EphemeralContainers
|
||||||
|
*out = make([]v1.EphemeralContainer, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if in.HostAliases != nil {
|
||||||
|
in, out := &in.HostAliases, &out.HostAliases
|
||||||
|
*out = make([]v1.HostAlias, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if in.TopologySpreadConstraints != nil {
|
||||||
|
in, out := &in.TopologySpreadConstraints, &out.TopologySpreadConstraints
|
||||||
|
*out = make([]v1.TopologySpreadConstraint, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if in.RuntimeClassName != nil {
|
||||||
|
in, out := &in.RuntimeClassName, &out.RuntimeClassName
|
||||||
|
*out = new(string)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
if in.DnsConfig != nil {
|
||||||
|
in, out := &in.DnsConfig, &out.DnsConfig
|
||||||
|
*out = make([]v1.PodDNSConfig, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RunnerPodSpec.
|
||||||
|
func (in *RunnerPodSpec) DeepCopy() *RunnerPodSpec {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(RunnerPodSpec)
|
||||||
|
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 *RunnerReplicaSet) DeepCopyInto(out *RunnerReplicaSet) {
|
func (in *RunnerReplicaSet) DeepCopyInto(out *RunnerReplicaSet) {
|
||||||
*out = *in
|
*out = *in
|
||||||
@@ -642,160 +860,127 @@ func (in *RunnerReplicaSetStatus) DeepCopy() *RunnerReplicaSetStatus {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *RunnerSet) DeepCopyInto(out *RunnerSet) {
|
||||||
|
*out = *in
|
||||||
|
out.TypeMeta = in.TypeMeta
|
||||||
|
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||||
|
in.Spec.DeepCopyInto(&out.Spec)
|
||||||
|
in.Status.DeepCopyInto(&out.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RunnerSet.
|
||||||
|
func (in *RunnerSet) DeepCopy() *RunnerSet {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(RunnerSet)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||||
|
func (in *RunnerSet) DeepCopyObject() runtime.Object {
|
||||||
|
if c := in.DeepCopy(); c != nil {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *RunnerSetList) DeepCopyInto(out *RunnerSetList) {
|
||||||
|
*out = *in
|
||||||
|
out.TypeMeta = in.TypeMeta
|
||||||
|
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||||
|
if in.Items != nil {
|
||||||
|
in, out := &in.Items, &out.Items
|
||||||
|
*out = make([]RunnerSet, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RunnerSetList.
|
||||||
|
func (in *RunnerSetList) DeepCopy() *RunnerSetList {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(RunnerSetList)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||||
|
func (in *RunnerSetList) DeepCopyObject() runtime.Object {
|
||||||
|
if c := in.DeepCopy(); c != nil {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *RunnerSetSpec) DeepCopyInto(out *RunnerSetSpec) {
|
||||||
|
*out = *in
|
||||||
|
in.RunnerConfig.DeepCopyInto(&out.RunnerConfig)
|
||||||
|
in.StatefulSetSpec.DeepCopyInto(&out.StatefulSetSpec)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RunnerSetSpec.
|
||||||
|
func (in *RunnerSetSpec) DeepCopy() *RunnerSetSpec {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(RunnerSetSpec)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *RunnerSetStatus) DeepCopyInto(out *RunnerSetStatus) {
|
||||||
|
*out = *in
|
||||||
|
if in.CurrentReplicas != nil {
|
||||||
|
in, out := &in.CurrentReplicas, &out.CurrentReplicas
|
||||||
|
*out = new(int)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
if in.ReadyReplicas != nil {
|
||||||
|
in, out := &in.ReadyReplicas, &out.ReadyReplicas
|
||||||
|
*out = new(int)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
if in.UpdatedReplicas != nil {
|
||||||
|
in, out := &in.UpdatedReplicas, &out.UpdatedReplicas
|
||||||
|
*out = new(int)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
if in.DesiredReplicas != nil {
|
||||||
|
in, out := &in.DesiredReplicas, &out.DesiredReplicas
|
||||||
|
*out = new(int)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
if in.Replicas != nil {
|
||||||
|
in, out := &in.Replicas, &out.Replicas
|
||||||
|
*out = new(int)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RunnerSetStatus.
|
||||||
|
func (in *RunnerSetStatus) DeepCopy() *RunnerSetStatus {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(RunnerSetStatus)
|
||||||
|
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 *RunnerSpec) DeepCopyInto(out *RunnerSpec) {
|
func (in *RunnerSpec) DeepCopyInto(out *RunnerSpec) {
|
||||||
*out = *in
|
*out = *in
|
||||||
if in.Labels != nil {
|
in.RunnerConfig.DeepCopyInto(&out.RunnerConfig)
|
||||||
in, out := &in.Labels, &out.Labels
|
in.RunnerPodSpec.DeepCopyInto(&out.RunnerPodSpec)
|
||||||
*out = make([]string, len(*in))
|
|
||||||
copy(*out, *in)
|
|
||||||
}
|
|
||||||
if in.Ephemeral != nil {
|
|
||||||
in, out := &in.Ephemeral, &out.Ephemeral
|
|
||||||
*out = new(bool)
|
|
||||||
**out = **in
|
|
||||||
}
|
|
||||||
if in.Containers != nil {
|
|
||||||
in, out := &in.Containers, &out.Containers
|
|
||||||
*out = make([]v1.Container, len(*in))
|
|
||||||
for i := range *in {
|
|
||||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
in.DockerdContainerResources.DeepCopyInto(&out.DockerdContainerResources)
|
|
||||||
if in.DockerVolumeMounts != nil {
|
|
||||||
in, out := &in.DockerVolumeMounts, &out.DockerVolumeMounts
|
|
||||||
*out = make([]v1.VolumeMount, len(*in))
|
|
||||||
for i := range *in {
|
|
||||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
in.Resources.DeepCopyInto(&out.Resources)
|
|
||||||
if in.VolumeMounts != nil {
|
|
||||||
in, out := &in.VolumeMounts, &out.VolumeMounts
|
|
||||||
*out = make([]v1.VolumeMount, len(*in))
|
|
||||||
for i := range *in {
|
|
||||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if in.EnvFrom != nil {
|
|
||||||
in, out := &in.EnvFrom, &out.EnvFrom
|
|
||||||
*out = make([]v1.EnvFromSource, len(*in))
|
|
||||||
for i := range *in {
|
|
||||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if in.Env != nil {
|
|
||||||
in, out := &in.Env, &out.Env
|
|
||||||
*out = make([]v1.EnvVar, len(*in))
|
|
||||||
for i := range *in {
|
|
||||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if in.Volumes != nil {
|
|
||||||
in, out := &in.Volumes, &out.Volumes
|
|
||||||
*out = make([]v1.Volume, len(*in))
|
|
||||||
for i := range *in {
|
|
||||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if in.InitContainers != nil {
|
|
||||||
in, out := &in.InitContainers, &out.InitContainers
|
|
||||||
*out = make([]v1.Container, len(*in))
|
|
||||||
for i := range *in {
|
|
||||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if in.SidecarContainers != nil {
|
|
||||||
in, out := &in.SidecarContainers, &out.SidecarContainers
|
|
||||||
*out = make([]v1.Container, len(*in))
|
|
||||||
for i := range *in {
|
|
||||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if in.NodeSelector != nil {
|
|
||||||
in, out := &in.NodeSelector, &out.NodeSelector
|
|
||||||
*out = make(map[string]string, len(*in))
|
|
||||||
for key, val := range *in {
|
|
||||||
(*out)[key] = val
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if in.AutomountServiceAccountToken != nil {
|
|
||||||
in, out := &in.AutomountServiceAccountToken, &out.AutomountServiceAccountToken
|
|
||||||
*out = new(bool)
|
|
||||||
**out = **in
|
|
||||||
}
|
|
||||||
if in.SecurityContext != nil {
|
|
||||||
in, out := &in.SecurityContext, &out.SecurityContext
|
|
||||||
*out = new(v1.PodSecurityContext)
|
|
||||||
(*in).DeepCopyInto(*out)
|
|
||||||
}
|
|
||||||
if in.ImagePullSecrets != nil {
|
|
||||||
in, out := &in.ImagePullSecrets, &out.ImagePullSecrets
|
|
||||||
*out = make([]v1.LocalObjectReference, len(*in))
|
|
||||||
copy(*out, *in)
|
|
||||||
}
|
|
||||||
if in.Affinity != nil {
|
|
||||||
in, out := &in.Affinity, &out.Affinity
|
|
||||||
*out = new(v1.Affinity)
|
|
||||||
(*in).DeepCopyInto(*out)
|
|
||||||
}
|
|
||||||
if in.Tolerations != nil {
|
|
||||||
in, out := &in.Tolerations, &out.Tolerations
|
|
||||||
*out = make([]v1.Toleration, len(*in))
|
|
||||||
for i := range *in {
|
|
||||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if in.EphemeralContainers != nil {
|
|
||||||
in, out := &in.EphemeralContainers, &out.EphemeralContainers
|
|
||||||
*out = make([]v1.EphemeralContainer, len(*in))
|
|
||||||
for i := range *in {
|
|
||||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if in.TerminationGracePeriodSeconds != nil {
|
|
||||||
in, out := &in.TerminationGracePeriodSeconds, &out.TerminationGracePeriodSeconds
|
|
||||||
*out = new(int64)
|
|
||||||
**out = **in
|
|
||||||
}
|
|
||||||
if in.DockerdWithinRunnerContainer != nil {
|
|
||||||
in, out := &in.DockerdWithinRunnerContainer, &out.DockerdWithinRunnerContainer
|
|
||||||
*out = new(bool)
|
|
||||||
**out = **in
|
|
||||||
}
|
|
||||||
if in.DockerEnabled != nil {
|
|
||||||
in, out := &in.DockerEnabled, &out.DockerEnabled
|
|
||||||
*out = new(bool)
|
|
||||||
**out = **in
|
|
||||||
}
|
|
||||||
if in.DockerMTU != nil {
|
|
||||||
in, out := &in.DockerMTU, &out.DockerMTU
|
|
||||||
*out = new(int64)
|
|
||||||
**out = **in
|
|
||||||
}
|
|
||||||
if in.DockerRegistryMirror != nil {
|
|
||||||
in, out := &in.DockerRegistryMirror, &out.DockerRegistryMirror
|
|
||||||
*out = new(string)
|
|
||||||
**out = **in
|
|
||||||
}
|
|
||||||
if in.HostAliases != nil {
|
|
||||||
in, out := &in.HostAliases, &out.HostAliases
|
|
||||||
*out = make([]v1.HostAlias, len(*in))
|
|
||||||
for i := range *in {
|
|
||||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if in.VolumeSizeLimit != nil {
|
|
||||||
in, out := &in.VolumeSizeLimit, &out.VolumeSizeLimit
|
|
||||||
x := (*in).DeepCopy()
|
|
||||||
*out = &x
|
|
||||||
}
|
|
||||||
if in.RuntimeClassName != nil {
|
|
||||||
in, out := &in.RuntimeClassName, &out.RuntimeClassName
|
|
||||||
*out = new(string)
|
|
||||||
**out = **in
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RunnerSpec.
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RunnerSpec.
|
||||||
|
|||||||
@@ -2,3 +2,4 @@
|
|||||||
lint-conf: charts/.ci/lint-config.yaml
|
lint-conf: charts/.ci/lint-config.yaml
|
||||||
chart-repos:
|
chart-repos:
|
||||||
- jetstack=https://charts.jetstack.io
|
- jetstack=https://charts.jetstack.io
|
||||||
|
check-version-increment: false # Disable checking that the chart version has been bumped
|
||||||
|
|||||||
@@ -15,20 +15,16 @@ 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.11.0
|
version: 0.16.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.18.2
|
appVersion: 0.21.1
|
||||||
|
|
||||||
home: https://github.com/summerwind/actions-runner-controller
|
home: https://github.com/actions-runner-controller/actions-runner-controller
|
||||||
|
|
||||||
sources:
|
sources:
|
||||||
- https://github.com/summerwind/actions-runner-controller
|
- https://github.com/actions-runner-controller/actions-runner-controller
|
||||||
|
|
||||||
maintainers:
|
maintainers:
|
||||||
- name: summerwind
|
- name: actions-runner-controller
|
||||||
email: contact@summerwind.jp
|
url: https://github.com/actions-runner-controller
|
||||||
url: https://github.com/summerwind
|
|
||||||
- name: funkypenguin
|
|
||||||
email: davidy@funkypenguin.co.nz
|
|
||||||
url: https://www.funkypenguin.co.nz
|
|
||||||
|
|||||||
@@ -4,34 +4,50 @@ All additional docs are kept in the `docs/` folder, this README is solely for do
|
|||||||
|
|
||||||
## Values
|
## Values
|
||||||
|
|
||||||
_The values are documented as of HEAD_
|
**_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_
|
> _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 |
|
| `syncPeriod` | Set the period in which the controler reconciles the desired runners count | 10m |
|
||||||
|
| `enableLeaderElection` | Enable election configuration | true |
|
||||||
|
| `leaderElectionId` | Set the election ID for the controller group | |
|
||||||
| `githubAPICacheDuration` | Set the cache period for API calls | |
|
| `githubAPICacheDuration` | Set the cache period for API calls | |
|
||||||
|
| `githubEnterpriseServerURL` | Set the URL for a self-hosted GitHub Enterprise Server | |
|
||||||
|
| `githubURL` | Override GitHub URL to be used for GitHub API calls | |
|
||||||
|
| `githubUploadURL` | Override GitHub Upload URL to be used for GitHub API calls | |
|
||||||
|
| `runnerGithubURL` | Override GitHub URL to be used by runners during registration | |
|
||||||
| `logLevel` | Set the log level of the controller container | |
|
| `logLevel` | Set the log level of the controller container | |
|
||||||
| `authSecret.create` | Deploy the controller auth secret | true |
|
| `additionalVolumes` | Set additional volumes to add to the manager container | |
|
||||||
|
| `additionalVolumeMounts` | Set additional volume mounts to add to the manager container | |
|
||||||
|
| `authSecret.create` | Deploy the controller auth secret | false |
|
||||||
| `authSecret.name` | Set the name of the auth secret | controller-manager |
|
| `authSecret.name` | Set the name of the auth secret | controller-manager |
|
||||||
|
| `authSecret.annotations` | Set annotations for the auth Secret | |
|
||||||
| `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_id` | The ID of your GitHub App. **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_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_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_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_token` | Your chosen GitHub PAT token. **This can't be set at the same time as the `authSecret.github_app_*`** | |
|
| `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_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_basicauth_password` | Password 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. | |
|
||||||
| `image.repository` | The "repository/image" of the controller container | summerwind/actions-runner-controller |
|
| `image.repository` | The "repository/image" of the controller container | summerwind/actions-runner-controller |
|
||||||
| `image.tag` | The tag of the controller container | |
|
| `image.tag` | The tag of the controller container | |
|
||||||
|
| `image.actionsRunnerRepositoryAndTag` | The "repository/image" of the actions runner container | summerwind/actions-runner:latest |
|
||||||
|
| `image.actionsRunnerImagePullSecrets` | Optional image pull secrets to be included in the runner pod's ImagePullSecrets | |
|
||||||
| `image.dindSidecarRepositoryAndTag` | The "repository/image" of the dind sidecar container | docker:dind |
|
| `image.dindSidecarRepositoryAndTag` | The "repository/image" of the dind sidecar container | docker:dind |
|
||||||
| `image.pullPolicy` | The pull policy of the controller image | IfNotPresent |
|
| `image.pullPolicy` | The pull policy of the controller image | IfNotPresent |
|
||||||
| `metrics.serviceMonitor` | Deploy serviceMonitor kind for for use with prometheus-operator CRDs | false |
|
| `metrics.serviceMonitor` | Deploy serviceMonitor kind for for use with prometheus-operator CRDs | false |
|
||||||
|
| `metrics.serviceAnnotations` | Set annotations for the provisioned metrics service resource | |
|
||||||
| `metrics.port` | Set port of metrics service | 8443 |
|
| `metrics.port` | Set port of metrics service | 8443 |
|
||||||
| `metrics.proxy.enabled` | Deploy kube-rbac-proxy container in controller pod | true |
|
| `metrics.proxy.enabled` | Deploy kube-rbac-proxy container in controller pod | true |
|
||||||
| `metrics.proxy.image.repository` | The "repository/image" of the kube-proxy container | quay.io/brancz/kube-rbac-proxy |
|
| `metrics.proxy.image.repository` | The "repository/image" of the kube-proxy container | quay.io/brancz/kube-rbac-proxy |
|
||||||
| `metrics.proxy.image.tag` | The tag of the kube-proxy image to use when pulling the container | v0.10.0 |
|
| `metrics.proxy.image.tag` | The tag of the kube-proxy image to use when pulling the container | v0.10.0 |
|
||||||
|
| `metrics.serviceMonitorLabels` | Set labels to apply to ServiceMonitor resources | |
|
||||||
| `imagePullSecrets` | Specifies the secret to be used when pulling the controller pod containers | |
|
| `imagePullSecrets` | Specifies the secret to be used when pulling the controller pod containers | |
|
||||||
| `fullNameOverride` | Override the full resource names | |
|
| `fullnameOverride` | Override the full resource names | |
|
||||||
| `nameOverride` | Override the resource name prefix | |
|
| `nameOverride` | Override the resource name prefix | |
|
||||||
| `serviceAccont.annotations` | Set annotations to the service account | |
|
| `serviceAccont.annotations` | Set annotations to the service account | |
|
||||||
| `serviceAccount.create` | Deploy the controller pod under a service account | true |
|
| `serviceAccount.create` | Deploy the controller pod under a service account | true |
|
||||||
@@ -40,26 +56,35 @@ _Default values are the defaults set in the charts values.yaml, some properties
|
|||||||
| `serviceAccount.name` | Set the name of the service account | |
|
| `serviceAccount.name` | Set the name of the service account | |
|
||||||
| `securityContext` | Set the security context for each container in the controller pod | |
|
| `securityContext` | Set the security context for each container in the controller pod | |
|
||||||
| `podSecurityContext` | Set the security context to controller pod | |
|
| `podSecurityContext` | Set the security context to controller pod | |
|
||||||
| `service.port` | Set controller service type | |
|
| `service.annotations` | Set annotations for the provisioned webhook service resource | |
|
||||||
| `service.type` | Set controller service ports | |
|
| `service.port` | Set controller service ports | |
|
||||||
|
| `service.type` | Set controller service type | |
|
||||||
| `topologySpreadConstraints` | Set the controller pod topologySpreadConstraints | |
|
| `topologySpreadConstraints` | Set the controller pod topologySpreadConstraints | |
|
||||||
| `nodeSelector` | Set the controller pod nodeSelector | |
|
| `nodeSelector` | Set the controller pod nodeSelector | |
|
||||||
| `resources` | Set the controller pod resources | |
|
| `resources` | Set the controller pod resources | |
|
||||||
| `affinity` | Set the controller pod affinity rules | |
|
| `affinity` | Set the controller pod affinity rules | |
|
||||||
|
| `podDisruptionBudget.enabled` | Enables a PDB to ensure HA of controller pods | false |
|
||||||
|
| `podDisruptionBudget.minAvailable` | Minimum number of pods that must be available after eviction | |
|
||||||
|
| `podDisruptionBudget.maxUnavailable` | Maximum number of pods that can be unavailable after eviction. Kubernetes 1.7+ required. | |
|
||||||
| `tolerations` | Set the controller pod tolerations | |
|
| `tolerations` | Set the controller pod tolerations | |
|
||||||
| `env` | Set environment variables for the controller container | |
|
| `env` | Set environment variables for the controller container | |
|
||||||
| `priorityClassName` | Set the controller pod priorityClassName | |
|
| `priorityClassName` | Set the controller pod priorityClassName | |
|
||||||
| `scope.watchNamespace` | Tells the controller which namespace to watch if `scope.singleNamespace` is true | |
|
| `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). |
|
||||||
| `scope.singleNamespace` | Limit the controller to watch a single namespace | false |
|
| `scope.singleNamespace` | Limit the controller to watch a single namespace | false |
|
||||||
|
| `certManagerEnabled` | Enable cert-manager. If disabled you must set admissionWebHooks.caBundle and create TLS secrets manually | true |
|
||||||
|
| `admissionWebHooks.caBundle` | Base64-encoded PEM bundle containing the CA that signed the webhook's serving certificate | |
|
||||||
| `githubWebhookServer.logLevel` | Set the log level of the githubWebhookServer container | |
|
| `githubWebhookServer.logLevel` | Set the log level of the githubWebhookServer container | |
|
||||||
| `githubWebhookServer.replicaCount` | Set the number of webhook server pods | 1 |
|
| `githubWebhookServer.replicaCount` | Set the number of webhook server pods | 1 |
|
||||||
|
| `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.syncPeriod` | Set the period in which the controller reconciles the resources | 10m |
|
||||||
| `githubWebhookServer.enabled` | Deploy the webhook server pod | false |
|
| `githubWebhookServer.enabled` | Deploy the webhook server pod | false |
|
||||||
| `githubWebhookServer.secret.create` | Deploy the webhook hook secret | true |
|
| `githubWebhookServer.secret.enabled` | Passes the webhook hook secret to the github-webhook-server | false |
|
||||||
|
| `githubWebhookServer.secret.create` | Deploy the webhook hook secret | false |
|
||||||
| `githubWebhookServer.secret.name` | Set the name of the webhook hook secret | github-webhook-server |
|
| `githubWebhookServer.secret.name` | Set the name of the webhook hook secret | github-webhook-server |
|
||||||
| `githubWebhookServer.secret.github_webhook_secret_token` | Set the webhook secret token value | |
|
| `githubWebhookServer.secret.github_webhook_secret_token` | Set the webhook secret token value | |
|
||||||
| `githubWebhookServer.imagePullSecrets` | Specifies the secret to be used when pulling the githubWebhookServer pod containers | |
|
| `githubWebhookServer.imagePullSecrets` | Specifies the secret to be used when pulling the githubWebhookServer pod containers | |
|
||||||
| `githubWebhookServer.nameOveride` | Override the resource name prefix | |
|
| `githubWebhookServer.nameOverride` | Override the resource name prefix | |
|
||||||
| `githubWebhookServer.fullNameOveride` | Override the full resource names | |
|
| `githubWebhookServer.fullnameOverride` | Override the full resource names | |
|
||||||
| `githubWebhookServer.serviceAccount.create` | Deploy the githubWebhookServer under a service account | true |
|
| `githubWebhookServer.serviceAccount.create` | Deploy the githubWebhookServer under a service account | true |
|
||||||
| `githubWebhookServer.serviceAccount.annotations` | Set annotations for the service account | |
|
| `githubWebhookServer.serviceAccount.annotations` | Set annotations for the service account | |
|
||||||
| `githubWebhookServer.serviceAccount.name` | Set the service account name | |
|
| `githubWebhookServer.serviceAccount.name` | Set the service account name | |
|
||||||
@@ -79,3 +104,7 @@ _Default values are the defaults set in the charts values.yaml, some properties
|
|||||||
| `githubWebhookServer.ingress.annotations` | Set annotations for the ingress kind | |
|
| `githubWebhookServer.ingress.annotations` | Set annotations for the ingress kind | |
|
||||||
| `githubWebhookServer.ingress.hosts` | Set hosts configuration for ingress | `[{"host": "chart-example.local", "paths": []}]` |
|
| `githubWebhookServer.ingress.hosts` | Set hosts configuration for ingress | `[{"host": "chart-example.local", "paths": []}]` |
|
||||||
| `githubWebhookServer.ingress.tls` | Set tls configuration for ingress | |
|
| `githubWebhookServer.ingress.tls` | Set tls configuration for ingress | |
|
||||||
|
| `githubWebhookServer.ingress.ingressClassName` | Set ingress class name | |
|
||||||
|
| `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. | |
|
||||||
|
|||||||
@@ -1,285 +1,239 @@
|
|||||||
|
apiVersion: apiextensions.k8s.io/v1
|
||||||
---
|
|
||||||
apiVersion: apiextensions.k8s.io/v1beta1
|
|
||||||
kind: CustomResourceDefinition
|
kind: CustomResourceDefinition
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
controller-gen.kubebuilder.io/version: v0.3.0
|
controller-gen.kubebuilder.io/version: v0.7.0
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
name: horizontalrunnerautoscalers.actions.summerwind.dev
|
name: horizontalrunnerautoscalers.actions.summerwind.dev
|
||||||
spec:
|
spec:
|
||||||
additionalPrinterColumns:
|
|
||||||
- JSONPath: .spec.minReplicas
|
|
||||||
name: Min
|
|
||||||
type: number
|
|
||||||
- JSONPath: .spec.maxReplicas
|
|
||||||
name: Max
|
|
||||||
type: number
|
|
||||||
- JSONPath: .status.desiredReplicas
|
|
||||||
name: Desired
|
|
||||||
type: number
|
|
||||||
- JSONPath: .status.scheduledOverridesSummary
|
|
||||||
name: Schedule
|
|
||||||
type: string
|
|
||||||
group: actions.summerwind.dev
|
group: actions.summerwind.dev
|
||||||
names:
|
names:
|
||||||
kind: HorizontalRunnerAutoscaler
|
kind: HorizontalRunnerAutoscaler
|
||||||
listKind: HorizontalRunnerAutoscalerList
|
listKind: HorizontalRunnerAutoscalerList
|
||||||
plural: horizontalrunnerautoscalers
|
plural: horizontalrunnerautoscalers
|
||||||
|
shortNames:
|
||||||
|
- hra
|
||||||
singular: horizontalrunnerautoscaler
|
singular: horizontalrunnerautoscaler
|
||||||
scope: Namespaced
|
scope: Namespaced
|
||||||
subresources:
|
versions:
|
||||||
status: {}
|
- additionalPrinterColumns:
|
||||||
validation:
|
- jsonPath: .spec.minReplicas
|
||||||
openAPIV3Schema:
|
name: Min
|
||||||
description: HorizontalRunnerAutoscaler is the Schema for the horizontalrunnerautoscaler
|
type: number
|
||||||
API
|
- jsonPath: .spec.maxReplicas
|
||||||
properties:
|
name: Max
|
||||||
apiVersion:
|
type: number
|
||||||
description: 'APIVersion defines the versioned schema of this representation
|
- jsonPath: .status.desiredReplicas
|
||||||
of an object. Servers should convert recognized schemas to the latest
|
name: Desired
|
||||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
type: number
|
||||||
|
- jsonPath: .status.scheduledOverridesSummary
|
||||||
|
name: Schedule
|
||||||
type: string
|
type: string
|
||||||
kind:
|
name: v1alpha1
|
||||||
description: 'Kind is a string value representing the REST resource this
|
schema:
|
||||||
object represents. Servers may infer this from the endpoint the client
|
openAPIV3Schema:
|
||||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
description: HorizontalRunnerAutoscaler is the Schema for the horizontalrunnerautoscaler API
|
||||||
type: string
|
|
||||||
metadata:
|
|
||||||
type: object
|
|
||||||
spec:
|
|
||||||
description: HorizontalRunnerAutoscalerSpec defines the desired state of
|
|
||||||
HorizontalRunnerAutoscaler
|
|
||||||
properties:
|
properties:
|
||||||
capacityReservations:
|
apiVersion:
|
||||||
items:
|
description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||||
description: CapacityReservation specifies the number of replicas
|
type: string
|
||||||
temporarily added to the scale target until ExpirationTime.
|
kind:
|
||||||
properties:
|
description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||||
expirationTime:
|
type: string
|
||||||
format: date-time
|
metadata:
|
||||||
type: string
|
|
||||||
name:
|
|
||||||
type: string
|
|
||||||
replicas:
|
|
||||||
type: integer
|
|
||||||
type: object
|
|
||||||
type: array
|
|
||||||
maxReplicas:
|
|
||||||
description: MinReplicas is the maximum number of replicas the deployment
|
|
||||||
is allowed to scale
|
|
||||||
type: integer
|
|
||||||
metrics:
|
|
||||||
description: Metrics is the collection of various metric targets to
|
|
||||||
calculate desired number of runners
|
|
||||||
items:
|
|
||||||
properties:
|
|
||||||
repositoryNames:
|
|
||||||
description: RepositoryNames is the list of repository names to
|
|
||||||
be used for calculating the metric. For example, a repository
|
|
||||||
name is the REPO part of `github.com/USER/REPO`.
|
|
||||||
items:
|
|
||||||
type: string
|
|
||||||
type: array
|
|
||||||
scaleDownAdjustment:
|
|
||||||
description: ScaleDownAdjustment is the number of runners removed
|
|
||||||
on scale-down. You can only specify either ScaleDownFactor or
|
|
||||||
ScaleDownAdjustment.
|
|
||||||
type: integer
|
|
||||||
scaleDownFactor:
|
|
||||||
description: ScaleDownFactor is the multiplicative factor applied
|
|
||||||
to the current number of runners used to determine how many
|
|
||||||
pods should be removed.
|
|
||||||
type: string
|
|
||||||
scaleDownThreshold:
|
|
||||||
description: ScaleDownThreshold is the percentage of busy runners
|
|
||||||
less than which will trigger the hpa to scale the runners down.
|
|
||||||
type: string
|
|
||||||
scaleUpAdjustment:
|
|
||||||
description: ScaleUpAdjustment is the number of runners added
|
|
||||||
on scale-up. You can only specify either ScaleUpFactor or ScaleUpAdjustment.
|
|
||||||
type: integer
|
|
||||||
scaleUpFactor:
|
|
||||||
description: ScaleUpFactor is the multiplicative factor applied
|
|
||||||
to the current number of runners used to determine how many
|
|
||||||
pods should be added.
|
|
||||||
type: string
|
|
||||||
scaleUpThreshold:
|
|
||||||
description: ScaleUpThreshold is the percentage of busy runners
|
|
||||||
greater than which will trigger the hpa to scale runners up.
|
|
||||||
type: string
|
|
||||||
type:
|
|
||||||
description: Type is the type of metric to be used for autoscaling.
|
|
||||||
The only supported Type is TotalNumberOfQueuedAndInProgressWorkflowRuns
|
|
||||||
type: string
|
|
||||||
type: object
|
|
||||||
type: array
|
|
||||||
minReplicas:
|
|
||||||
description: MinReplicas is the minimum number of replicas the deployment
|
|
||||||
is allowed to scale
|
|
||||||
type: integer
|
|
||||||
scaleDownDelaySecondsAfterScaleOut:
|
|
||||||
description: ScaleDownDelaySecondsAfterScaleUp is the approximate delay
|
|
||||||
for a scale down followed by a scale up Used to prevent flapping (down->up->down->...
|
|
||||||
loop)
|
|
||||||
type: integer
|
|
||||||
scaleTargetRef:
|
|
||||||
description: ScaleTargetRef sis the reference to scaled resource like
|
|
||||||
RunnerDeployment
|
|
||||||
properties:
|
|
||||||
name:
|
|
||||||
type: string
|
|
||||||
type: object
|
type: object
|
||||||
scaleUpTriggers:
|
spec:
|
||||||
description: "ScaleUpTriggers is an experimental feature to increase
|
description: HorizontalRunnerAutoscalerSpec defines the desired state of HorizontalRunnerAutoscaler
|
||||||
the desired replicas by 1 on each webhook requested received by the
|
properties:
|
||||||
webhookBasedAutoscaler. \n This feature requires you to also enable
|
capacityReservations:
|
||||||
and deploy the webhookBasedAutoscaler onto your cluster. \n Note that
|
items:
|
||||||
the added runners remain until the next sync period at least, and
|
description: CapacityReservation specifies the number of replicas temporarily added to the scale target until ExpirationTime.
|
||||||
they may or may not be used by GitHub Actions depending on the timing.
|
|
||||||
They are intended to be used to gain \"resource slack\" immediately
|
|
||||||
after you receive a webhook from GitHub, so that you can loosely expect
|
|
||||||
MinReplicas runners to be always available."
|
|
||||||
items:
|
|
||||||
properties:
|
|
||||||
amount:
|
|
||||||
type: integer
|
|
||||||
duration:
|
|
||||||
type: string
|
|
||||||
githubEvent:
|
|
||||||
properties:
|
properties:
|
||||||
checkRun:
|
expirationTime:
|
||||||
description: https://docs.github.com/en/actions/reference/events-that-trigger-workflows#check_run
|
|
||||||
properties:
|
|
||||||
names:
|
|
||||||
description: Names is a list of GitHub Actions glob patterns.
|
|
||||||
Any check_run event whose name matches one of patterns
|
|
||||||
in the list can trigger autoscaling. Note that check_run
|
|
||||||
name seem to equal to the job name you've defined in
|
|
||||||
your actions workflow yaml file. So it is very likely
|
|
||||||
that you can utilize this to trigger depending on the
|
|
||||||
job.
|
|
||||||
items:
|
|
||||||
type: string
|
|
||||||
type: array
|
|
||||||
status:
|
|
||||||
type: string
|
|
||||||
types:
|
|
||||||
items:
|
|
||||||
type: string
|
|
||||||
type: array
|
|
||||||
type: object
|
|
||||||
pullRequest:
|
|
||||||
description: https://docs.github.com/en/actions/reference/events-that-trigger-workflows#pull_request
|
|
||||||
properties:
|
|
||||||
branches:
|
|
||||||
items:
|
|
||||||
type: string
|
|
||||||
type: array
|
|
||||||
types:
|
|
||||||
items:
|
|
||||||
type: string
|
|
||||||
type: array
|
|
||||||
type: object
|
|
||||||
push:
|
|
||||||
description: PushSpec is the condition for triggering scale-up
|
|
||||||
on push event Also see https://docs.github.com/en/actions/reference/events-that-trigger-workflows#push
|
|
||||||
type: object
|
|
||||||
type: object
|
|
||||||
type: object
|
|
||||||
type: array
|
|
||||||
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.
|
|
||||||
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.
|
|
||||||
properties:
|
|
||||||
endTime:
|
|
||||||
description: EndTime is the time at which the first override ends.
|
|
||||||
format: date-time
|
|
||||||
type: string
|
|
||||||
minReplicas:
|
|
||||||
description: MinReplicas is the number of runners while overriding.
|
|
||||||
If omitted, it doesn't override minReplicas.
|
|
||||||
minimum: 0
|
|
||||||
nullable: true
|
|
||||||
type: integer
|
|
||||||
recurrenceRule:
|
|
||||||
properties:
|
|
||||||
frequency:
|
|
||||||
description: Frequency is the name of a predefined interval
|
|
||||||
of each recurrence. The valid values are "Daily", "Weekly",
|
|
||||||
"Monthly", and "Yearly". If empty, the corresponding override
|
|
||||||
happens only once.
|
|
||||||
enum:
|
|
||||||
- Daily
|
|
||||||
- Weekly
|
|
||||||
- Monthly
|
|
||||||
- Yearly
|
|
||||||
type: string
|
|
||||||
untilTime:
|
|
||||||
description: UntilTime is the time of the final recurrence.
|
|
||||||
If empty, the schedule recurs forever.
|
|
||||||
format: date-time
|
format: date-time
|
||||||
type: string
|
type: string
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
replicas:
|
||||||
|
type: integer
|
||||||
type: object
|
type: object
|
||||||
startTime:
|
type: array
|
||||||
description: StartTime is the time at which the first override
|
maxReplicas:
|
||||||
starts.
|
description: MaxReplicas is the maximum number of replicas the deployment is allowed to scale
|
||||||
format: date-time
|
type: integer
|
||||||
type: string
|
metrics:
|
||||||
required:
|
description: Metrics is the collection of various metric targets to calculate desired number of runners
|
||||||
- endTime
|
items:
|
||||||
- startTime
|
properties:
|
||||||
type: object
|
repositoryNames:
|
||||||
type: array
|
description: RepositoryNames is the list of repository names to be used for calculating the metric. For example, a repository name is the REPO part of `github.com/USER/REPO`.
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
scaleDownAdjustment:
|
||||||
|
description: ScaleDownAdjustment is the number of runners removed on scale-down. You can only specify either ScaleDownFactor or ScaleDownAdjustment.
|
||||||
|
type: integer
|
||||||
|
scaleDownFactor:
|
||||||
|
description: ScaleDownFactor is the multiplicative factor applied to the current number of runners used to determine how many pods should be removed.
|
||||||
|
type: string
|
||||||
|
scaleDownThreshold:
|
||||||
|
description: ScaleDownThreshold is the percentage of busy runners less than which will trigger the hpa to scale the runners down.
|
||||||
|
type: string
|
||||||
|
scaleUpAdjustment:
|
||||||
|
description: ScaleUpAdjustment is the number of runners added on scale-up. You can only specify either ScaleUpFactor or ScaleUpAdjustment.
|
||||||
|
type: integer
|
||||||
|
scaleUpFactor:
|
||||||
|
description: ScaleUpFactor is the multiplicative factor applied to the current number of runners used to determine how many pods should be added.
|
||||||
|
type: string
|
||||||
|
scaleUpThreshold:
|
||||||
|
description: ScaleUpThreshold is the percentage of busy runners greater than which will trigger the hpa to scale runners up.
|
||||||
|
type: string
|
||||||
|
type:
|
||||||
|
description: Type is the type of metric to be used for autoscaling. The only supported Type is TotalNumberOfQueuedAndInProgressWorkflowRuns
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
type: array
|
||||||
|
minReplicas:
|
||||||
|
description: MinReplicas is the minimum number of replicas the deployment is allowed to scale
|
||||||
|
type: integer
|
||||||
|
scaleDownDelaySecondsAfterScaleOut:
|
||||||
|
description: ScaleDownDelaySecondsAfterScaleUp is the approximate delay for a scale down followed by a scale up Used to prevent flapping (down->up->down->... loop)
|
||||||
|
type: integer
|
||||||
|
scaleTargetRef:
|
||||||
|
description: ScaleTargetRef sis the reference to scaled resource like RunnerDeployment
|
||||||
|
properties:
|
||||||
|
kind:
|
||||||
|
description: Kind is the type of resource being referenced
|
||||||
|
enum:
|
||||||
|
- RunnerDeployment
|
||||||
|
- RunnerSet
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
description: Name is the name of resource being referenced
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
scaleUpTriggers:
|
||||||
|
description: "ScaleUpTriggers is an experimental feature to increase the desired replicas by 1 on each webhook requested received by the webhookBasedAutoscaler. \n This feature requires you to also enable and deploy the webhookBasedAutoscaler onto your cluster. \n Note that the added runners remain until the next sync period at least, and they may or may not be used by GitHub Actions depending on the timing. They are intended to be used to gain \"resource slack\" immediately after you receive a webhook from GitHub, so that you can loosely expect MinReplicas runners to be always available."
|
||||||
|
items:
|
||||||
|
properties:
|
||||||
|
amount:
|
||||||
|
type: integer
|
||||||
|
duration:
|
||||||
|
type: string
|
||||||
|
githubEvent:
|
||||||
|
properties:
|
||||||
|
checkRun:
|
||||||
|
description: https://docs.github.com/en/actions/reference/events-that-trigger-workflows#check_run
|
||||||
|
properties:
|
||||||
|
names:
|
||||||
|
description: Names is a list of GitHub Actions glob patterns. Any check_run event whose name matches one of patterns in the list can trigger autoscaling. Note that check_run name seem to equal to the job name you've defined in your actions workflow yaml file. So it is very likely that you can utilize this to trigger depending on the job.
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
repositories:
|
||||||
|
description: Repositories is a list of GitHub repositories. Any check_run event whose repository matches one of repositories in the list can trigger autoscaling.
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
types:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
type: object
|
||||||
|
pullRequest:
|
||||||
|
description: https://docs.github.com/en/actions/reference/events-that-trigger-workflows#pull_request
|
||||||
|
properties:
|
||||||
|
branches:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
types:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
type: object
|
||||||
|
push:
|
||||||
|
description: PushSpec is the condition for triggering scale-up on push event Also see https://docs.github.com/en/actions/reference/events-that-trigger-workflows#push
|
||||||
|
type: object
|
||||||
|
type: object
|
||||||
|
type: object
|
||||||
|
type: array
|
||||||
|
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.
|
||||||
|
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.
|
||||||
|
properties:
|
||||||
|
endTime:
|
||||||
|
description: EndTime is the time at which the first override ends.
|
||||||
|
format: date-time
|
||||||
|
type: string
|
||||||
|
minReplicas:
|
||||||
|
description: MinReplicas is the number of runners while overriding. If omitted, it doesn't override minReplicas.
|
||||||
|
minimum: 0
|
||||||
|
nullable: true
|
||||||
|
type: integer
|
||||||
|
recurrenceRule:
|
||||||
|
properties:
|
||||||
|
frequency:
|
||||||
|
description: Frequency is the name of a predefined interval of each recurrence. The valid values are "Daily", "Weekly", "Monthly", and "Yearly". If empty, the corresponding override happens only once.
|
||||||
|
enum:
|
||||||
|
- Daily
|
||||||
|
- Weekly
|
||||||
|
- Monthly
|
||||||
|
- Yearly
|
||||||
|
type: string
|
||||||
|
untilTime:
|
||||||
|
description: UntilTime is the time of the final recurrence. If empty, the schedule recurs forever.
|
||||||
|
format: date-time
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
startTime:
|
||||||
|
description: StartTime is the time at which the first override starts.
|
||||||
|
format: date-time
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- endTime
|
||||||
|
- startTime
|
||||||
|
type: object
|
||||||
|
type: array
|
||||||
|
type: object
|
||||||
|
status:
|
||||||
|
properties:
|
||||||
|
cacheEntries:
|
||||||
|
items:
|
||||||
|
properties:
|
||||||
|
expirationTime:
|
||||||
|
format: date-time
|
||||||
|
type: string
|
||||||
|
key:
|
||||||
|
type: string
|
||||||
|
value:
|
||||||
|
type: integer
|
||||||
|
type: object
|
||||||
|
type: array
|
||||||
|
desiredReplicas:
|
||||||
|
description: DesiredReplicas is the total number of desired, non-terminated and latest pods to be set for the primary RunnerSet This doesn't include outdated pods while upgrading the deployment and replacing the runnerset.
|
||||||
|
type: integer
|
||||||
|
lastSuccessfulScaleOutTime:
|
||||||
|
format: date-time
|
||||||
|
nullable: true
|
||||||
|
type: string
|
||||||
|
observedGeneration:
|
||||||
|
description: ObservedGeneration is the most recent generation observed for the target. It corresponds to e.g. RunnerDeployment's generation, which is updated on mutation by the API Server.
|
||||||
|
format: int64
|
||||||
|
type: integer
|
||||||
|
scheduledOverridesSummary:
|
||||||
|
description: ScheduledOverridesSummary is the summary of active and upcoming scheduled overrides to be shown in e.g. a column of a `kubectl get hra` output for observability.
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
type: object
|
type: object
|
||||||
status:
|
served: true
|
||||||
properties:
|
storage: true
|
||||||
cacheEntries:
|
subresources:
|
||||||
items:
|
status: {}
|
||||||
properties:
|
preserveUnknownFields: false
|
||||||
expirationTime:
|
|
||||||
format: date-time
|
|
||||||
type: string
|
|
||||||
key:
|
|
||||||
type: string
|
|
||||||
value:
|
|
||||||
type: integer
|
|
||||||
type: object
|
|
||||||
type: array
|
|
||||||
desiredReplicas:
|
|
||||||
description: DesiredReplicas is the total number of desired, non-terminated
|
|
||||||
and latest pods to be set for the primary RunnerSet This doesn't include
|
|
||||||
outdated pods while upgrading the deployment and replacing the runnerset.
|
|
||||||
type: integer
|
|
||||||
lastSuccessfulScaleOutTime:
|
|
||||||
format: date-time
|
|
||||||
nullable: true
|
|
||||||
type: string
|
|
||||||
observedGeneration:
|
|
||||||
description: ObservedGeneration is the most recent generation observed
|
|
||||||
for the target. It corresponds to e.g. RunnerDeployment's generation,
|
|
||||||
which is updated on mutation by the API Server.
|
|
||||||
format: int64
|
|
||||||
type: integer
|
|
||||||
scheduledOverridesSummary:
|
|
||||||
description: ScheduledOverridesSummary is the summary of active and
|
|
||||||
upcoming scheduled overrides to be shown in e.g. a column of a `kubectl
|
|
||||||
get hra` output for observability.
|
|
||||||
type: string
|
|
||||||
type: object
|
|
||||||
type: object
|
|
||||||
version: v1alpha1
|
|
||||||
versions:
|
|
||||||
- name: v1alpha1
|
|
||||||
served: true
|
|
||||||
storage: true
|
|
||||||
status:
|
status:
|
||||||
acceptedNames:
|
acceptedNames:
|
||||||
kind: ""
|
kind: ""
|
||||||
|
|||||||
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
@@ -21,7 +21,8 @@ Due to the above you can't just do a `helm upgrade` to release the latest versio
|
|||||||
1. Upgrade CRDs
|
1. Upgrade CRDs
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
CHART_VERSION=0.11.0
|
# REMEMBER TO UPDATE THE CHART_VERSION TO RELEVANT CHART VERISON!!!!
|
||||||
|
CHART_VERSION=0.14.0
|
||||||
|
|
||||||
curl -L https://github.com/actions-runner-controller/actions-runner-controller/releases/download/actions-runner-controller-${CHART_VERSION}/actions-runner-controller-${CHART_VERSION}.tgz | tar zxv --strip 1 actions-runner-controller/crds
|
curl -L https://github.com/actions-runner-controller/actions-runner-controller/releases/download/actions-runner-controller-${CHART_VERSION}/actions-runner-controller-${CHART_VERSION}.tgz | tar zxv --strip 1 actions-runner-controller/crds
|
||||||
|
|
||||||
@@ -31,9 +32,10 @@ kubectl apply -f crds/
|
|||||||
2. Upgrade the Helm release
|
2. Upgrade the Helm release
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
helm upgrade --install \
|
# helm upgrade [RELEASE] [CHART] [flags]
|
||||||
--namespace actions-runner-system \
|
helm upgrade actions-runner-controller \
|
||||||
--version ${CHART_VERSION} \
|
|
||||||
actions-runner-controller/actions-runner-controller \
|
actions-runner-controller/actions-runner-controller \
|
||||||
actions-runner-controller
|
--install \
|
||||||
|
--namespace actions-runner-system \
|
||||||
|
--version ${CHART_VERSION}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -58,3 +58,7 @@ Create the name of the service account to use
|
|||||||
{{- define "actions-runner-controller-github-webhook-server.serviceMonitorName" -}}
|
{{- define "actions-runner-controller-github-webhook-server.serviceMonitorName" -}}
|
||||||
{{- include "actions-runner-controller-github-webhook-server.fullname" . | trunc 47 }}-service-monitor
|
{{- include "actions-runner-controller-github-webhook-server.fullname" . | trunc 47 }}-service-monitor
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
||||||
|
{{- define "actions-runner-controller-github-webhook-server.pdbName" -}}
|
||||||
|
{{- include "actions-runner-controller-github-webhook-server.fullname" . | trunc 59 }}-pdb
|
||||||
|
{{- end }}
|
||||||
@@ -68,6 +68,10 @@ Create the name of the service account to use
|
|||||||
{{- default (include "actions-runner-controller.fullname" .) .Values.authSecret.name -}}
|
{{- default (include "actions-runner-controller.fullname" .) .Values.authSecret.name -}}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
||||||
|
{{- define "actions-runner-controller.githubWebhookServerSecretName" -}}
|
||||||
|
{{- default (include "actions-runner-controller.fullname" .) .Values.githubWebhookServer.secret.name -}}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
{{- define "actions-runner-controller.leaderElectionRoleName" -}}
|
{{- define "actions-runner-controller.leaderElectionRoleName" -}}
|
||||||
{{- include "actions-runner-controller.fullname" . }}-leader-election
|
{{- include "actions-runner-controller.fullname" . }}-leader-election
|
||||||
{{- end }}
|
{{- end }}
|
||||||
@@ -107,3 +111,7 @@ Create the name of the service account to use
|
|||||||
{{- define "actions-runner-controller.servingCertName" -}}
|
{{- define "actions-runner-controller.servingCertName" -}}
|
||||||
{{- include "actions-runner-controller.fullname" . }}-serving-cert
|
{{- include "actions-runner-controller.fullname" . }}-serving-cert
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
||||||
|
{{- define "actions-runner-controller.pdbName" -}}
|
||||||
|
{{- include "actions-runner-controller.fullname" . | trunc 59 }}-pdb
|
||||||
|
{{- end }}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
{{- if .Values.certManagerEnabled }}
|
||||||
# The following manifests contain a self-signed issuer CR and a certificate CR.
|
# The following manifests contain a self-signed issuer CR and a certificate CR.
|
||||||
# More document can be found at https://docs.cert-manager.io
|
# More document can be found at https://docs.cert-manager.io
|
||||||
# WARNING: Targets CertManager 0.11 check https://docs.cert-manager.io/en/latest/tasks/upgrading/index.html for breaking changes
|
# WARNING: Targets CertManager 0.11 check https://docs.cert-manager.io/en/latest/tasks/upgrading/index.html for breaking changes
|
||||||
@@ -21,4 +22,5 @@ spec:
|
|||||||
issuerRef:
|
issuerRef:
|
||||||
kind: Issuer
|
kind: Issuer
|
||||||
name: {{ include "actions-runner-controller.selfsignedIssuerName" . }}
|
name: {{ include "actions-runner-controller.selfsignedIssuerName" . }}
|
||||||
secretName: webhook-server-cert # this secret will not be prefixed, since it's not managed by kustomize
|
secretName: {{ include "actions-runner-controller.servingCertName" . }}
|
||||||
|
{{- end }}
|
||||||
|
|||||||
@@ -7,4 +7,8 @@ data:
|
|||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
name: controller-manager
|
name: controller-manager
|
||||||
|
{{- if .Values.authSecret.annotations }}
|
||||||
|
annotations:
|
||||||
|
{{ toYaml .Values.authSecret.annotations | nindent 4 }}
|
||||||
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
@@ -5,6 +5,10 @@ metadata:
|
|||||||
{{- include "actions-runner-controller.labels" . | nindent 4 }}
|
{{- include "actions-runner-controller.labels" . | nindent 4 }}
|
||||||
name: {{ include "actions-runner-controller.metricsServiceName" . }}
|
name: {{ include "actions-runner-controller.metricsServiceName" . }}
|
||||||
namespace: {{ .Release.Namespace }}
|
namespace: {{ .Release.Namespace }}
|
||||||
|
{{- with .Values.metrics.serviceAnnotations }}
|
||||||
|
annotations:
|
||||||
|
{{- toYaml . | nindent 4 }}
|
||||||
|
{{- end }}
|
||||||
spec:
|
spec:
|
||||||
ports:
|
ports:
|
||||||
- name: metrics-port
|
- name: metrics-port
|
||||||
|
|||||||
@@ -4,11 +4,20 @@ kind: ServiceMonitor
|
|||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
{{- include "actions-runner-controller.labels" . | nindent 4 }}
|
{{- include "actions-runner-controller.labels" . | nindent 4 }}
|
||||||
|
{{- with .Values.metrics.serviceMonitorLabels }}
|
||||||
|
{{- toYaml . | nindent 4 }}
|
||||||
|
{{- end }}
|
||||||
name: {{ include "actions-runner-controller.serviceMonitorName" . }}
|
name: {{ include "actions-runner-controller.serviceMonitorName" . }}
|
||||||
spec:
|
spec:
|
||||||
endpoints:
|
endpoints:
|
||||||
- path: /metrics
|
- path: /metrics
|
||||||
port: metrics-port
|
port: metrics-port
|
||||||
|
{{- if .Values.metrics.proxy.enabled }}
|
||||||
|
bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
|
||||||
|
scheme: https
|
||||||
|
tlsConfig:
|
||||||
|
insecureSkipVerify: true
|
||||||
|
{{- end }}
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
{{- include "actions-runner-controller.selectorLabels" . | nindent 6 }}
|
{{- include "actions-runner-controller.selectorLabels" . | nindent 6 }}
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
{{- if .Values.podDisruptionBudget.enabled }}
|
||||||
|
apiVersion: policy/v1beta1
|
||||||
|
kind: PodDisruptionBudget
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
{{- include "actions-runner-controller.labels" . | nindent 4 }}
|
||||||
|
name: {{ include "actions-runner-controller.pdbName" . }}
|
||||||
|
namespace: {{ .Release.Namespace }}
|
||||||
|
spec:
|
||||||
|
{{- if .Values.podDisruptionBudget.minAvailable }}
|
||||||
|
minAvailable: {{ .Values.podDisruptionBudget.minAvailable }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Values.podDisruptionBudget.maxUnavailable }}
|
||||||
|
maxUnavailable: {{ .Values.podDisruptionBudget.maxUnavailable }}
|
||||||
|
{{- end }}
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
{{- include "actions-runner-controller.selectorLabels" . | nindent 6 }}
|
||||||
|
{{- end -}}
|
||||||
@@ -37,9 +37,21 @@ 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 }}"
|
||||||
|
{{- if .Values.enableLeaderElection }}
|
||||||
- "--enable-leader-election"
|
- "--enable-leader-election"
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Values.leaderElectionId }}
|
||||||
|
- "--leader-election-id={{ .Values.leaderElectionId }}"
|
||||||
|
{{- end }}
|
||||||
- "--sync-period={{ .Values.syncPeriod }}"
|
- "--sync-period={{ .Values.syncPeriod }}"
|
||||||
- "--docker-image={{ .Values.image.dindSidecarRepositoryAndTag }}"
|
- "--docker-image={{ .Values.image.dindSidecarRepositoryAndTag }}"
|
||||||
|
- "--runner-image={{ .Values.image.actionsRunnerRepositoryAndTag }}"
|
||||||
|
{{- range .Values.image.actionsRunnerImagePullSecrets }}
|
||||||
|
- "--runner-image-pull-secret={{ . }}"
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Values.dockerRegistryMirror }}
|
||||||
|
- "--docker-registry-mirror={{ .Values.dockerRegistryMirror }}"
|
||||||
|
{{- end }}
|
||||||
{{- 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 }}
|
||||||
@@ -49,9 +61,25 @@ spec:
|
|||||||
{{- if .Values.logLevel }}
|
{{- if .Values.logLevel }}
|
||||||
- "--log-level={{ .Values.logLevel }}"
|
- "--log-level={{ .Values.logLevel }}"
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
{{- if .Values.runnerGithubURL }}
|
||||||
|
- "--runner-github-url={{ .Values.runnerGithubURL }}"
|
||||||
|
{{- end }}
|
||||||
command:
|
command:
|
||||||
- "/manager"
|
- "/manager"
|
||||||
env:
|
env:
|
||||||
|
{{- if .Values.githubEnterpriseServerURL }}
|
||||||
|
- name: GITHUB_ENTERPRISE_URL
|
||||||
|
value: {{ .Values.githubEnterpriseServerURL }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Values.githubURL }}
|
||||||
|
- name: GITHUB_URL
|
||||||
|
value: {{ .Values.githubURL }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Values.githubUploadURL }}
|
||||||
|
- name: GITHUB_UPLOAD_URL
|
||||||
|
value: {{ .Values.githubUploadURL }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Values.authSecret.enabled }}
|
||||||
- name: GITHUB_TOKEN
|
- name: GITHUB_TOKEN
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
@@ -71,7 +99,23 @@ spec:
|
|||||||
name: {{ include "actions-runner-controller.secretName" . }}
|
name: {{ include "actions-runner-controller.secretName" . }}
|
||||||
optional: true
|
optional: true
|
||||||
- name: GITHUB_APP_PRIVATE_KEY
|
- name: GITHUB_APP_PRIVATE_KEY
|
||||||
value: /etc/actions-runner-controller/github_app_private_key
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
key: github_app_private_key
|
||||||
|
name: {{ include "actions-runner-controller.secretName" . }}
|
||||||
|
optional: true
|
||||||
|
{{- if .Values.authSecret.github_basicauth_username }}
|
||||||
|
- name: GITHUB_BASICAUTH_USERNAME
|
||||||
|
value: {{ .Values.authSecret.github_basicauth_username }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Values.authSecret.github_basicauth_password }}
|
||||||
|
- name: GITHUB_BASICAUTH_PASSWORD
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
key: github_basicauth_password
|
||||||
|
name: {{ include "actions-runner-controller.secretName" . }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
{{- range $key, $val := .Values.env }}
|
{{- range $key, $val := .Values.env }}
|
||||||
- name: {{ $key }}
|
- name: {{ $key }}
|
||||||
value: {{ $val | quote }}
|
value: {{ $val | quote }}
|
||||||
@@ -93,14 +137,19 @@ spec:
|
|||||||
securityContext:
|
securityContext:
|
||||||
{{- toYaml .Values.securityContext | nindent 12 }}
|
{{- toYaml .Values.securityContext | nindent 12 }}
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
|
{{- if .Values.authSecret.enabled }}
|
||||||
- mountPath: "/etc/actions-runner-controller"
|
- mountPath: "/etc/actions-runner-controller"
|
||||||
name: secret
|
name: secret
|
||||||
readOnly: true
|
readOnly: true
|
||||||
|
{{- end }}
|
||||||
- mountPath: /tmp
|
- mountPath: /tmp
|
||||||
name: tmp
|
name: tmp
|
||||||
- mountPath: /tmp/k8s-webhook-server/serving-certs
|
- mountPath: /tmp/k8s-webhook-server/serving-certs
|
||||||
name: cert
|
name: cert
|
||||||
readOnly: true
|
readOnly: true
|
||||||
|
{{- if .Values.additionalVolumeMounts }}
|
||||||
|
{{- toYaml .Values.additionalVolumeMounts | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
{{- if .Values.metrics.proxy.enabled }}
|
{{- if .Values.metrics.proxy.enabled }}
|
||||||
- args:
|
- args:
|
||||||
- "--secure-listen-address=0.0.0.0:{{ .Values.metrics.port }}"
|
- "--secure-listen-address=0.0.0.0:{{ .Values.metrics.port }}"
|
||||||
@@ -120,15 +169,20 @@ spec:
|
|||||||
{{- end }}
|
{{- end }}
|
||||||
terminationGracePeriodSeconds: 10
|
terminationGracePeriodSeconds: 10
|
||||||
volumes:
|
volumes:
|
||||||
|
{{- if .Values.authSecret.enabled }}
|
||||||
- name: secret
|
- name: secret
|
||||||
secret:
|
secret:
|
||||||
secretName: {{ include "actions-runner-controller.secretName" . }}
|
secretName: {{ include "actions-runner-controller.secretName" . }}
|
||||||
|
{{- end }}
|
||||||
- name: cert
|
- name: cert
|
||||||
secret:
|
secret:
|
||||||
defaultMode: 420
|
defaultMode: 420
|
||||||
secretName: webhook-server-cert
|
secretName: {{ include "actions-runner-controller.servingCertName" . }}
|
||||||
- name: tmp
|
- name: tmp
|
||||||
emptyDir: {}
|
emptyDir: {}
|
||||||
|
{{- if .Values.additionalVolumes }}
|
||||||
|
{{- toYaml .Values.additionalVolumes | nindent 6}}
|
||||||
|
{{- end }}
|
||||||
{{- with .Values.nodeSelector }}
|
{{- with .Values.nodeSelector }}
|
||||||
nodeSelector:
|
nodeSelector:
|
||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
|
|||||||
@@ -42,6 +42,12 @@ spec:
|
|||||||
{{- if .Values.githubWebhookServer.logLevel }}
|
{{- if .Values.githubWebhookServer.logLevel }}
|
||||||
- "--log-level={{ .Values.githubWebhookServer.logLevel }}"
|
- "--log-level={{ .Values.githubWebhookServer.logLevel }}"
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
{{- if .Values.scope.singleNamespace }}
|
||||||
|
- "--watch-namespace={{ default .Release.Namespace .Values.scope.watchNamespace }}"
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Values.runnerGithubURL }}
|
||||||
|
- "--runner-github-url={{ .Values.runnerGithubURL }}"
|
||||||
|
{{- end }}
|
||||||
command:
|
command:
|
||||||
- "/github-webhook-server"
|
- "/github-webhook-server"
|
||||||
env:
|
env:
|
||||||
@@ -51,6 +57,55 @@ spec:
|
|||||||
key: github_webhook_secret_token
|
key: github_webhook_secret_token
|
||||||
name: {{ include "actions-runner-controller-github-webhook-server.secretName" . }}
|
name: {{ include "actions-runner-controller-github-webhook-server.secretName" . }}
|
||||||
optional: true
|
optional: true
|
||||||
|
{{- if .Values.githubEnterpriseServerURL }}
|
||||||
|
- name: GITHUB_ENTERPRISE_URL
|
||||||
|
value: {{ .Values.githubEnterpriseServerURL }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Values.githubURL }}
|
||||||
|
- name: GITHUB_URL
|
||||||
|
value: {{ .Values.githubURL }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Values.githubUploadURL }}
|
||||||
|
- name: GITHUB_UPLOAD_URL
|
||||||
|
value: {{ .Values.githubUploadURL }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if and .Values.githubWebhookServer.useRunnerGroupsVisibility .Values.githubWebhookServer.secret.enabled }}
|
||||||
|
- name: GITHUB_TOKEN
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
key: github_token
|
||||||
|
name: {{ include "actions-runner-controller.githubWebhookServerSecretName" . }}
|
||||||
|
optional: true
|
||||||
|
- name: GITHUB_APP_ID
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
key: github_app_id
|
||||||
|
name: {{ include "actions-runner-controller.githubWebhookServerSecretName" . }}
|
||||||
|
optional: true
|
||||||
|
- name: GITHUB_APP_INSTALLATION_ID
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
key: github_app_installation_id
|
||||||
|
name: {{ include "actions-runner-controller.githubWebhookServerSecretName" . }}
|
||||||
|
optional: true
|
||||||
|
- name: GITHUB_APP_PRIVATE_KEY
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
key: github_app_private_key
|
||||||
|
name: {{ include "actions-runner-controller.githubWebhookServerSecretName" . }}
|
||||||
|
optional: true
|
||||||
|
{{- if .Values.authSecret.github_basicauth_username }}
|
||||||
|
- name: GITHUB_BASICAUTH_USERNAME
|
||||||
|
value: {{ .Values.authSecret.github_basicauth_username }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Values.authSecret.github_basicauth_password }}
|
||||||
|
- name: GITHUB_BASICAUTH_PASSWORD
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
key: github_basicauth_password
|
||||||
|
name: {{ include "actions-runner-controller.secretName" . }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
{{- range $key, $val := .Values.githubWebhookServer.env }}
|
{{- range $key, $val := .Values.githubWebhookServer.env }}
|
||||||
- name: {{ $key }}
|
- name: {{ $key }}
|
||||||
value: {{ $val | quote }}
|
value: {{ $val | quote }}
|
||||||
|
|||||||
@@ -1,9 +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 semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
|
{{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }}
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
{{- else if .Capabilities.APIVersions.Has "networking.k8s.io/v1beta1" }}
|
||||||
apiVersion: networking.k8s.io/v1beta1
|
apiVersion: networking.k8s.io/v1beta1
|
||||||
{{- else -}}
|
{{- else if .Capabilities.APIVersions.Has "extensions/v1beta1" }}
|
||||||
apiVersion: extensions/v1beta1
|
apiVersion: extensions/v1beta1
|
||||||
{{- end }}
|
{{- end }}
|
||||||
kind: Ingress
|
kind: Ingress
|
||||||
@@ -26,6 +28,9 @@ spec:
|
|||||||
secretName: {{ .secretName }}
|
secretName: {{ .secretName }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
{{- with .Values.githubWebhookServer.ingress.ingressClassName }}
|
||||||
|
ingressClassName: {{ . }}
|
||||||
|
{{- end }}
|
||||||
rules:
|
rules:
|
||||||
{{- range .Values.githubWebhookServer.ingress.hosts }}
|
{{- range .Values.githubWebhookServer.ingress.hosts }}
|
||||||
- host: {{ .host | quote }}
|
- host: {{ .host | quote }}
|
||||||
@@ -33,9 +38,19 @@ spec:
|
|||||||
paths:
|
paths:
|
||||||
{{- range .paths }}
|
{{- range .paths }}
|
||||||
- path: {{ .path }}
|
- path: {{ .path }}
|
||||||
|
{{- if $.Capabilities.APIVersions.Has "networking.k8s.io/v1" }}
|
||||||
|
pathType: {{ .pathType }}
|
||||||
|
{{- end }}
|
||||||
backend:
|
backend:
|
||||||
|
{{- if $.Capabilities.APIVersions.Has "networking.k8s.io/v1" }}
|
||||||
|
service:
|
||||||
|
name: {{ $fullName }}
|
||||||
|
port:
|
||||||
|
number: {{ $svcPort }}
|
||||||
|
{{- else }}
|
||||||
serviceName: {{ $fullName }}
|
serviceName: {{ $fullName }}
|
||||||
servicePort: {{ $svcPort }}
|
servicePort: {{ $svcPort }}
|
||||||
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
{{- if .Values.githubWebhookServer.podDisruptionBudget.enabled }}
|
||||||
|
apiVersion: policy/v1beta1
|
||||||
|
kind: PodDisruptionBudget
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
{{- include "actions-runner-controller.labels" . | nindent 4 }}
|
||||||
|
name: {{ include "actions-runner-controller-github-webhook-server.pdbName" . }}
|
||||||
|
namespace: {{ .Release.Namespace }}
|
||||||
|
spec:
|
||||||
|
{{- if .Values.githubWebhookServer.podDisruptionBudget.minAvailable }}
|
||||||
|
minAvailable: {{ .Values.githubWebhookServer.podDisruptionBudget.minAvailable }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Values.githubWebhookServer.podDisruptionBudget.maxUnavailable }}
|
||||||
|
maxUnavailable: {{ .Values.githubWebhookServer.podDisruptionBudget.maxUnavailable }}
|
||||||
|
{{- end }}
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
{{- include "actions-runner-controller-github-webhook-server.selectorLabels" . | nindent 6 }}
|
||||||
|
{{- end -}}
|
||||||
@@ -35,6 +35,14 @@ rules:
|
|||||||
- get
|
- get
|
||||||
- patch
|
- patch
|
||||||
- update
|
- update
|
||||||
|
- apiGroups:
|
||||||
|
- actions.summerwind.dev
|
||||||
|
resources:
|
||||||
|
- runnersets
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- actions.summerwind.dev
|
- actions.summerwind.dev
|
||||||
resources:
|
resources:
|
||||||
@@ -67,4 +75,16 @@ rules:
|
|||||||
- get
|
- get
|
||||||
- patch
|
- patch
|
||||||
- update
|
- update
|
||||||
|
- apiGroups:
|
||||||
|
- authentication.k8s.io
|
||||||
|
resources:
|
||||||
|
- tokenreviews
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- apiGroups:
|
||||||
|
- authorization.k8s.io
|
||||||
|
resources:
|
||||||
|
- subjectaccessreviews
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|||||||
@@ -6,6 +6,10 @@ metadata:
|
|||||||
namespace: {{ .Release.Namespace }}
|
namespace: {{ .Release.Namespace }}
|
||||||
labels:
|
labels:
|
||||||
{{- include "actions-runner-controller.labels" . | nindent 4 }}
|
{{- include "actions-runner-controller.labels" . | nindent 4 }}
|
||||||
|
{{- if .Values.githubWebhookServer.service.annotations }}
|
||||||
|
annotations:
|
||||||
|
{{ toYaml .Values.githubWebhookServer.service.annotations | nindent 4 }}
|
||||||
|
{{- end }}
|
||||||
spec:
|
spec:
|
||||||
type: {{ .Values.githubWebhookServer.service.type }}
|
type: {{ .Values.githubWebhookServer.service.type }}
|
||||||
ports:
|
ports:
|
||||||
|
|||||||
@@ -4,11 +4,20 @@ kind: ServiceMonitor
|
|||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
{{- include "actions-runner-controller.labels" . | nindent 4 }}
|
{{- include "actions-runner-controller.labels" . | nindent 4 }}
|
||||||
|
{{- with .Values.metrics.serviceMonitorLabels }}
|
||||||
|
{{- toYaml . | nindent 4 }}
|
||||||
|
{{- end }}
|
||||||
name: {{ include "actions-runner-controller-github-webhook-server.serviceMonitorName" . }}
|
name: {{ include "actions-runner-controller-github-webhook-server.serviceMonitorName" . }}
|
||||||
spec:
|
spec:
|
||||||
endpoints:
|
endpoints:
|
||||||
- path: /metrics
|
- path: /metrics
|
||||||
port: metrics-port
|
port: metrics-port
|
||||||
|
{{- if .Values.metrics.proxy.enabled }}
|
||||||
|
bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
|
||||||
|
scheme: https
|
||||||
|
tlsConfig:
|
||||||
|
insecureSkipVerify: true
|
||||||
|
{{- end }}
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
{{- include "actions-runner-controller-github-webhook-server.selectorLabels" . | nindent 6 }}
|
{{- include "actions-runner-controller-github-webhook-server.selectorLabels" . | nindent 6 }}
|
||||||
|
|||||||
@@ -132,6 +132,62 @@ rules:
|
|||||||
- get
|
- get
|
||||||
- patch
|
- patch
|
||||||
- update
|
- update
|
||||||
|
- apiGroups:
|
||||||
|
- actions.summerwind.dev
|
||||||
|
resources:
|
||||||
|
- runnersets
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- delete
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- actions.summerwind.dev
|
||||||
|
resources:
|
||||||
|
- runnersets/finalizers
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- delete
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- actions.summerwind.dev
|
||||||
|
resources:
|
||||||
|
- runnersets/status
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- apiGroups:
|
||||||
|
- "apps"
|
||||||
|
resources:
|
||||||
|
- statefulsets
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- delete
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- "apps"
|
||||||
|
resources:
|
||||||
|
- statefulsets/finalizers
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- delete
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- watch
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- ""
|
- ""
|
||||||
resources:
|
resources:
|
||||||
@@ -139,6 +195,15 @@ rules:
|
|||||||
verbs:
|
verbs:
|
||||||
- create
|
- create
|
||||||
- patch
|
- patch
|
||||||
|
- apiGroups:
|
||||||
|
- coordination.k8s.io
|
||||||
|
resources:
|
||||||
|
- leases
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- update
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- ""
|
- ""
|
||||||
resources:
|
resources:
|
||||||
|
|||||||
@@ -4,14 +4,20 @@ kind: Secret
|
|||||||
metadata:
|
metadata:
|
||||||
name: {{ include "actions-runner-controller.secretName" . }}
|
name: {{ include "actions-runner-controller.secretName" . }}
|
||||||
namespace: {{ .Release.Namespace }}
|
namespace: {{ .Release.Namespace }}
|
||||||
|
{{- if .Values.authSecret.annotations }}
|
||||||
|
annotations:
|
||||||
|
{{ toYaml .Values.authSecret.annotations | nindent 4 }}
|
||||||
|
{{- end }}
|
||||||
labels:
|
labels:
|
||||||
{{- include "actions-runner-controller.labels" . | nindent 4 }}
|
{{- include "actions-runner-controller.labels" . | nindent 4 }}
|
||||||
type: Opaque
|
type: Opaque
|
||||||
data:
|
data:
|
||||||
{{- if .Values.authSecret.github_app_id }}
|
{{- if .Values.authSecret.github_app_id }}
|
||||||
|
# Keep this as a string as strings integrate better with things like AWS Parameter Store, see PR #882 for an example
|
||||||
github_app_id: {{ .Values.authSecret.github_app_id | toString | b64enc }}
|
github_app_id: {{ .Values.authSecret.github_app_id | toString | b64enc }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.authSecret.github_app_installation_id }}
|
{{- if .Values.authSecret.github_app_installation_id }}
|
||||||
|
# Keep this as a string as strings integrate better with things like AWS Parameter Store, see PR #882 for an example
|
||||||
github_app_installation_id: {{ .Values.authSecret.github_app_installation_id | toString | b64enc }}
|
github_app_installation_id: {{ .Values.authSecret.github_app_installation_id | toString | b64enc }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.authSecret.github_app_private_key }}
|
{{- if .Values.authSecret.github_app_private_key }}
|
||||||
@@ -20,4 +26,7 @@ data:
|
|||||||
{{- if .Values.authSecret.github_token }}
|
{{- if .Values.authSecret.github_token }}
|
||||||
github_token: {{ .Values.authSecret.github_token | toString | b64enc }}
|
github_token: {{ .Values.authSecret.github_token | toString | b64enc }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
{{- if .Values.authSecret.github_basicauth_password }}
|
||||||
|
github_basicauth_password: {{ .Values.authSecret.github_basicauth_password | toString | b64enc }}
|
||||||
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|||||||
@@ -1,15 +1,21 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
apiVersion: admissionregistration.k8s.io/v1beta1
|
apiVersion: admissionregistration.k8s.io/v1
|
||||||
kind: MutatingWebhookConfiguration
|
kind: MutatingWebhookConfiguration
|
||||||
metadata:
|
metadata:
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
name: {{ include "actions-runner-controller.fullname" . }}-mutating-webhook-configuration
|
name: {{ include "actions-runner-controller.fullname" . }}-mutating-webhook-configuration
|
||||||
|
{{- if .Values.certManagerEnabled }}
|
||||||
annotations:
|
annotations:
|
||||||
cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/{{ include "actions-runner-controller.servingCertName" . }}
|
cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/{{ include "actions-runner-controller.servingCertName" . }}
|
||||||
|
{{- end }}
|
||||||
webhooks:
|
webhooks:
|
||||||
- clientConfig:
|
- admissionReviewVersions:
|
||||||
caBundle: Cg==
|
- v1beta1
|
||||||
|
clientConfig:
|
||||||
|
{{- if .Values.admissionWebHooks.caBundle }}
|
||||||
|
caBundle: {{ quote .Values.admissionWebHooks.caBundle }}
|
||||||
|
{{- end }}
|
||||||
service:
|
service:
|
||||||
name: {{ include "actions-runner-controller.webhookServiceName" . }}
|
name: {{ include "actions-runner-controller.webhookServiceName" . }}
|
||||||
namespace: {{ .Release.Namespace }}
|
namespace: {{ .Release.Namespace }}
|
||||||
@@ -27,8 +33,12 @@ webhooks:
|
|||||||
resources:
|
resources:
|
||||||
- runners
|
- runners
|
||||||
sideEffects: None
|
sideEffects: None
|
||||||
- clientConfig:
|
- admissionReviewVersions:
|
||||||
caBundle: Cg==
|
- v1beta1
|
||||||
|
clientConfig:
|
||||||
|
{{- if .Values.admissionWebHooks.caBundle }}
|
||||||
|
caBundle: {{ .Values.admissionWebHooks.caBundle }}
|
||||||
|
{{- end }}
|
||||||
service:
|
service:
|
||||||
name: {{ include "actions-runner-controller.webhookServiceName" . }}
|
name: {{ include "actions-runner-controller.webhookServiceName" . }}
|
||||||
namespace: {{ .Release.Namespace }}
|
namespace: {{ .Release.Namespace }}
|
||||||
@@ -46,8 +56,12 @@ webhooks:
|
|||||||
resources:
|
resources:
|
||||||
- runnerdeployments
|
- runnerdeployments
|
||||||
sideEffects: None
|
sideEffects: None
|
||||||
- clientConfig:
|
- admissionReviewVersions:
|
||||||
caBundle: Cg==
|
- v1beta1
|
||||||
|
clientConfig:
|
||||||
|
{{- if .Values.admissionWebHooks.caBundle }}
|
||||||
|
caBundle: {{ .Values.admissionWebHooks.caBundle }}
|
||||||
|
{{- end }}
|
||||||
service:
|
service:
|
||||||
name: {{ include "actions-runner-controller.webhookServiceName" . }}
|
name: {{ include "actions-runner-controller.webhookServiceName" . }}
|
||||||
namespace: {{ .Release.Namespace }}
|
namespace: {{ .Release.Namespace }}
|
||||||
@@ -65,17 +79,48 @@ webhooks:
|
|||||||
resources:
|
resources:
|
||||||
- runnerreplicasets
|
- runnerreplicasets
|
||||||
sideEffects: None
|
sideEffects: None
|
||||||
|
- admissionReviewVersions:
|
||||||
|
- v1beta1
|
||||||
|
clientConfig:
|
||||||
|
{{- if .Values.admissionWebHooks.caBundle }}
|
||||||
|
caBundle: {{ .Values.admissionWebHooks.caBundle }}
|
||||||
|
{{- end }}
|
||||||
|
service:
|
||||||
|
name: {{ include "actions-runner-controller.webhookServiceName" . }}
|
||||||
|
namespace: {{ .Release.Namespace }}
|
||||||
|
path: /mutate-runner-set-pod
|
||||||
|
failurePolicy: Fail
|
||||||
|
name: mutate-runner-pod.webhook.actions.summerwind.dev
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
apiVersions:
|
||||||
|
- v1
|
||||||
|
operations:
|
||||||
|
- CREATE
|
||||||
|
resources:
|
||||||
|
- pods
|
||||||
|
sideEffects: None
|
||||||
|
objectSelector:
|
||||||
|
matchLabels:
|
||||||
|
"actions-runner-controller/inject-registration-token": "true"
|
||||||
---
|
---
|
||||||
apiVersion: admissionregistration.k8s.io/v1beta1
|
apiVersion: admissionregistration.k8s.io/v1
|
||||||
kind: ValidatingWebhookConfiguration
|
kind: ValidatingWebhookConfiguration
|
||||||
metadata:
|
metadata:
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
name: {{ include "actions-runner-controller.fullname" . }}-validating-webhook-configuration
|
name: {{ include "actions-runner-controller.fullname" . }}-validating-webhook-configuration
|
||||||
|
{{- if .Values.certManagerEnabled }}
|
||||||
annotations:
|
annotations:
|
||||||
cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/{{ include "actions-runner-controller.servingCertName" . }}
|
cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/{{ include "actions-runner-controller.servingCertName" . }}
|
||||||
|
{{- end }}
|
||||||
webhooks:
|
webhooks:
|
||||||
- clientConfig:
|
- admissionReviewVersions:
|
||||||
caBundle: Cg==
|
- v1beta1
|
||||||
|
clientConfig:
|
||||||
|
{{- if .Values.admissionWebHooks.caBundle }}
|
||||||
|
caBundle: {{ .Values.admissionWebHooks.caBundle }}
|
||||||
|
{{- end }}
|
||||||
service:
|
service:
|
||||||
name: {{ include "actions-runner-controller.webhookServiceName" . }}
|
name: {{ include "actions-runner-controller.webhookServiceName" . }}
|
||||||
namespace: {{ .Release.Namespace }}
|
namespace: {{ .Release.Namespace }}
|
||||||
@@ -93,8 +138,12 @@ webhooks:
|
|||||||
resources:
|
resources:
|
||||||
- runners
|
- runners
|
||||||
sideEffects: None
|
sideEffects: None
|
||||||
- clientConfig:
|
- admissionReviewVersions:
|
||||||
caBundle: Cg==
|
- v1beta1
|
||||||
|
clientConfig:
|
||||||
|
{{- if .Values.admissionWebHooks.caBundle }}
|
||||||
|
caBundle: {{ .Values.admissionWebHooks.caBundle }}
|
||||||
|
{{- end }}
|
||||||
service:
|
service:
|
||||||
name: {{ include "actions-runner-controller.webhookServiceName" . }}
|
name: {{ include "actions-runner-controller.webhookServiceName" . }}
|
||||||
namespace: {{ .Release.Namespace }}
|
namespace: {{ .Release.Namespace }}
|
||||||
@@ -112,8 +161,12 @@ webhooks:
|
|||||||
resources:
|
resources:
|
||||||
- runnerdeployments
|
- runnerdeployments
|
||||||
sideEffects: None
|
sideEffects: None
|
||||||
- clientConfig:
|
- admissionReviewVersions:
|
||||||
caBundle: Cg==
|
- v1beta1
|
||||||
|
clientConfig:
|
||||||
|
{{- if .Values.admissionWebHooks.caBundle }}
|
||||||
|
caBundle: {{ .Values.admissionWebHooks.caBundle }}
|
||||||
|
{{- end }}
|
||||||
service:
|
service:
|
||||||
name: {{ include "actions-runner-controller.webhookServiceName" . }}
|
name: {{ include "actions-runner-controller.webhookServiceName" . }}
|
||||||
namespace: {{ .Release.Namespace }}
|
namespace: {{ .Release.Namespace }}
|
||||||
|
|||||||
@@ -5,6 +5,10 @@ metadata:
|
|||||||
namespace: {{ .Release.Namespace }}
|
namespace: {{ .Release.Namespace }}
|
||||||
labels:
|
labels:
|
||||||
{{- include "actions-runner-controller.labels" . | nindent 4 }}
|
{{- include "actions-runner-controller.labels" . | nindent 4 }}
|
||||||
|
{{- with .Values.service.annotations }}
|
||||||
|
annotations:
|
||||||
|
{{- toYaml . | nindent 4 }}
|
||||||
|
{{- end }}
|
||||||
spec:
|
spec:
|
||||||
type: {{ .Values.service.type }}
|
type: {{ .Values.service.type }}
|
||||||
ports:
|
ports:
|
||||||
|
|||||||
@@ -8,27 +8,57 @@ replicaCount: 1
|
|||||||
|
|
||||||
syncPeriod: 10m
|
syncPeriod: 10m
|
||||||
|
|
||||||
|
enableLeaderElection: true
|
||||||
|
# Specifies the controller id for leader election.
|
||||||
|
# Must be unique if more than one controller installed onto the same namespace.
|
||||||
|
#leaderElectionId: "actions-runner-controller"
|
||||||
|
|
||||||
# The controller tries its best not to repeat the duplicate GitHub API call
|
# The controller tries its best not to repeat the duplicate GitHub API call
|
||||||
# within this duration.
|
# within this duration.
|
||||||
# Defaults to syncPeriod - 10s.
|
# Defaults to syncPeriod - 10s.
|
||||||
#githubAPICacheDuration: 30s
|
#githubAPICacheDuration: 30s
|
||||||
|
|
||||||
|
# The URL of your GitHub Enterprise server, if you're using one.
|
||||||
|
#githubEnterpriseServerURL: https://github.example.com
|
||||||
|
|
||||||
|
# Override GitHub URLs in case of using proxy APIs
|
||||||
|
#githubURL: ""
|
||||||
|
#githubUploadURL: ""
|
||||||
|
#runnerGithubURL: ""
|
||||||
|
|
||||||
# Only 1 authentication method can be deployed at a time
|
# Only 1 authentication method can be deployed at a time
|
||||||
# Uncomment the configuration you are applying and fill in the details
|
# Uncomment the configuration you are applying and fill in the details
|
||||||
|
#
|
||||||
|
# If authSecret.enabled=true these values are inherited to actions-runner-controller's controller-manager container's env.
|
||||||
|
#
|
||||||
|
# Do set authSecret.enabled=false and set env if you want full control over
|
||||||
|
# the GitHub authn related envvars of the container.
|
||||||
|
# See https://github.com/actions-runner-controller/actions-runner-controller/pull/937 for more details.
|
||||||
authSecret:
|
authSecret:
|
||||||
create: true
|
enabled: true
|
||||||
|
create: false
|
||||||
name: "controller-manager"
|
name: "controller-manager"
|
||||||
|
annotations: {}
|
||||||
### GitHub Apps Configuration
|
### GitHub Apps Configuration
|
||||||
|
## NOTE: IDs MUST be strings, use quotes
|
||||||
#github_app_id: ""
|
#github_app_id: ""
|
||||||
#github_app_installation_id: ""
|
#github_app_installation_id: ""
|
||||||
#github_app_private_key: |
|
#github_app_private_key: |
|
||||||
### GitHub PAT Configuration
|
### GitHub PAT Configuration
|
||||||
#github_token: ""
|
#github_token: ""
|
||||||
|
### Basic auth for github API proxy
|
||||||
|
#github_basicauth_username: ""
|
||||||
|
#github_basicauth_password: ""
|
||||||
|
|
||||||
|
dockerRegistryMirror: ""
|
||||||
image:
|
image:
|
||||||
repository: summerwind/actions-runner-controller
|
repository: "summerwind/actions-runner-controller"
|
||||||
|
actionsRunnerRepositoryAndTag: "summerwind/actions-runner:latest"
|
||||||
dindSidecarRepositoryAndTag: "docker:dind"
|
dindSidecarRepositoryAndTag: "docker:dind"
|
||||||
pullPolicy: IfNotPresent
|
pullPolicy: IfNotPresent
|
||||||
|
# The default image-pull secrets name for self-hosted runner container.
|
||||||
|
# It's added to spec.ImagePullSecrets of self-hosted runner pods.
|
||||||
|
actionsRunnerImagePullSecrets: []
|
||||||
|
|
||||||
imagePullSecrets: []
|
imagePullSecrets: []
|
||||||
nameOverride: ""
|
nameOverride: ""
|
||||||
@@ -60,18 +90,23 @@ securityContext:
|
|||||||
# runAsNonRoot: true
|
# runAsNonRoot: true
|
||||||
# runAsUser: 1000
|
# runAsUser: 1000
|
||||||
|
|
||||||
|
# Webhook service resource
|
||||||
service:
|
service:
|
||||||
type: ClusterIP
|
type: ClusterIP
|
||||||
port: 443
|
port: 443
|
||||||
|
annotations: {}
|
||||||
|
|
||||||
|
# Metrics service resource
|
||||||
metrics:
|
metrics:
|
||||||
|
serviceAnnotations: {}
|
||||||
serviceMonitor: false
|
serviceMonitor: false
|
||||||
|
serviceMonitorLabels: {}
|
||||||
port: 8443
|
port: 8443
|
||||||
proxy:
|
proxy:
|
||||||
enabled: true
|
enabled: true
|
||||||
image:
|
image:
|
||||||
repository: quay.io/brancz/kube-rbac-proxy
|
repository: quay.io/brancz/kube-rbac-proxy
|
||||||
tag: v0.10.0
|
tag: v0.11.0
|
||||||
|
|
||||||
resources:
|
resources:
|
||||||
{}
|
{}
|
||||||
@@ -92,6 +127,12 @@ tolerations: []
|
|||||||
|
|
||||||
affinity: {}
|
affinity: {}
|
||||||
|
|
||||||
|
# Only one of minAvailable or maxUnavailable can be set
|
||||||
|
podDisruptionBudget:
|
||||||
|
enabled: false
|
||||||
|
# minAvailable: 1
|
||||||
|
# maxUnavailable: 3
|
||||||
|
|
||||||
# Leverage a PriorityClass to ensure your pods survive resource shortages
|
# Leverage a PriorityClass to ensure your pods survive resource shortages
|
||||||
# ref: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/
|
# ref: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/
|
||||||
# PriorityClass: system-cluster-critical
|
# PriorityClass: system-cluster-critical
|
||||||
@@ -103,6 +144,14 @@ env:
|
|||||||
# https_proxy: "proxy.com:8080"
|
# https_proxy: "proxy.com:8080"
|
||||||
# no_proxy: ""
|
# no_proxy: ""
|
||||||
|
|
||||||
|
## 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
|
||||||
|
## into the running container
|
||||||
|
additionalVolumes: []
|
||||||
|
|
||||||
|
## specify where the additional volumes are mounted in the manager container
|
||||||
|
additionalVolumeMounts: []
|
||||||
|
|
||||||
scope:
|
scope:
|
||||||
# If true, the controller will only watch custom resources in a single namespace
|
# If true, the controller will only watch custom resources in a single namespace
|
||||||
singleNamespace: false
|
singleNamespace: false
|
||||||
@@ -110,14 +159,23 @@ scope:
|
|||||||
# The default value is "", which means the namespace of the controller
|
# The default value is "", which means the namespace of the controller
|
||||||
watchNamespace: ""
|
watchNamespace: ""
|
||||||
|
|
||||||
|
certManagerEnabled: true
|
||||||
|
|
||||||
|
admissionWebHooks:
|
||||||
|
{}
|
||||||
|
#caBundle: "Ci0tLS0tQk...<base64-encoded PEM bundle containing the CA that signed the webhook's serving certificate>...tLS0K"
|
||||||
|
|
||||||
githubWebhookServer:
|
githubWebhookServer:
|
||||||
enabled: false
|
enabled: false
|
||||||
replicaCount: 1
|
replicaCount: 1
|
||||||
|
syncPeriod: 10m
|
||||||
|
useRunnerGroupsVisibility: false
|
||||||
secret:
|
secret:
|
||||||
create: true
|
enabled: false
|
||||||
|
create: false
|
||||||
name: "github-webhook-server"
|
name: "github-webhook-server"
|
||||||
### GitHub Webhook Configuration
|
### GitHub Webhook Configuration
|
||||||
#github_webhook_secret_token: ""
|
github_webhook_secret_token: ""
|
||||||
imagePullSecrets: []
|
imagePullSecrets: []
|
||||||
nameOverride: ""
|
nameOverride: ""
|
||||||
fullnameOverride: ""
|
fullnameOverride: ""
|
||||||
@@ -141,6 +199,7 @@ githubWebhookServer:
|
|||||||
priorityClassName: ""
|
priorityClassName: ""
|
||||||
service:
|
service:
|
||||||
type: ClusterIP
|
type: ClusterIP
|
||||||
|
annotations: {}
|
||||||
ports:
|
ports:
|
||||||
- port: 80
|
- port: 80
|
||||||
targetPort: http
|
targetPort: http
|
||||||
@@ -149,14 +208,22 @@ githubWebhookServer:
|
|||||||
#nodePort: someFixedPortForUseWithTerraformCdkCfnEtc
|
#nodePort: someFixedPortForUseWithTerraformCdkCfnEtc
|
||||||
ingress:
|
ingress:
|
||||||
enabled: false
|
enabled: false
|
||||||
annotations:
|
ingressClassName: ""
|
||||||
{}
|
annotations: {}
|
||||||
# kubernetes.io/ingress.class: nginx
|
# kubernetes.io/ingress.class: nginx
|
||||||
# kubernetes.io/tls-acme: "true"
|
# kubernetes.io/tls-acme: "true"
|
||||||
hosts:
|
hosts:
|
||||||
- host: chart-example.local
|
- host: chart-example.local
|
||||||
paths: []
|
paths: []
|
||||||
|
# - path: /*
|
||||||
|
# pathType: ImplementationSpecific
|
||||||
tls: []
|
tls: []
|
||||||
# - secretName: chart-example-tls
|
# - secretName: chart-example-tls
|
||||||
# hosts:
|
# hosts:
|
||||||
# - chart-example.local
|
# - chart-example.local
|
||||||
|
|
||||||
|
# Only one of minAvailable or maxUnavailable can be set
|
||||||
|
podDisruptionBudget:
|
||||||
|
enabled: false
|
||||||
|
# minAvailable: 1
|
||||||
|
# maxUnavailable: 3
|
||||||
|
|||||||
@@ -20,13 +20,16 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
actionsv1alpha1 "github.com/summerwind/actions-runner-controller/api/v1alpha1"
|
actionsv1alpha1 "github.com/actions-runner-controller/actions-runner-controller/api/v1alpha1"
|
||||||
"github.com/summerwind/actions-runner-controller/controllers"
|
"github.com/actions-runner-controller/actions-runner-controller/controllers"
|
||||||
|
"github.com/actions-runner-controller/actions-runner-controller/github"
|
||||||
|
"github.com/kelseyhightower/envconfig"
|
||||||
zaplib "go.uber.org/zap"
|
zaplib "go.uber.org/zap"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
|
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
|
||||||
@@ -48,6 +51,8 @@ const (
|
|||||||
logLevelInfo = "info"
|
logLevelInfo = "info"
|
||||||
logLevelWarn = "warn"
|
logLevelWarn = "warn"
|
||||||
logLevelError = "error"
|
logLevelError = "error"
|
||||||
|
|
||||||
|
webhookSecretTokenEnvName = "GITHUB_WEBHOOK_SECRET_TOKEN"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -65,16 +70,26 @@ func main() {
|
|||||||
metricsAddr string
|
metricsAddr string
|
||||||
|
|
||||||
// The secret token of the GitHub Webhook. See https://docs.github.com/en/developers/webhooks-and-events/securing-your-webhooks
|
// The secret token of the GitHub Webhook. See https://docs.github.com/en/developers/webhooks-and-events/securing-your-webhooks
|
||||||
webhookSecretToken string
|
webhookSecretToken string
|
||||||
|
webhookSecretTokenEnv string
|
||||||
|
|
||||||
watchNamespace string
|
watchNamespace string
|
||||||
|
|
||||||
enableLeaderElection bool
|
enableLeaderElection bool
|
||||||
syncPeriod time.Duration
|
syncPeriod time.Duration
|
||||||
logLevel string
|
logLevel string
|
||||||
|
|
||||||
|
ghClient *github.Client
|
||||||
)
|
)
|
||||||
|
|
||||||
webhookSecretToken = os.Getenv("GITHUB_WEBHOOK_SECRET_TOKEN")
|
var c github.Config
|
||||||
|
err = envconfig.Process("github", &c)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error: processing environment variables: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
webhookSecretTokenEnv = os.Getenv(webhookSecretTokenEnvName)
|
||||||
|
|
||||||
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.")
|
||||||
@@ -83,10 +98,26 @@ func main() {
|
|||||||
"Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager.")
|
"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.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", logLevelDebug, `The verbosity of the logging. Valid values are "debug", "info", "warn", "error". Defaults to "debug".`)
|
flag.StringVar(&logLevel, "log-level", logLevelDebug, `The verbosity of the logging. Valid values are "debug", "info", "warn", "error". Defaults to "debug".`)
|
||||||
|
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.Int64Var(&c.AppID, "github-app-id", c.AppID, "The application ID of GitHub App.")
|
||||||
|
flag.Int64Var(&c.AppInstallationID, "github-app-installation-id", c.AppInstallationID, "The installation ID of GitHub App.")
|
||||||
|
flag.StringVar(&c.AppPrivateKey, "github-app-private-key", c.AppPrivateKey, "The path of a private key file to authenticate as a GitHub App")
|
||||||
|
flag.StringVar(&c.URL, "github-url", c.URL, "GitHub URL to be used for GitHub API calls")
|
||||||
|
flag.StringVar(&c.UploadURL, "github-upload-url", c.UploadURL, "GitHub Upload URL to be used for GitHub API calls")
|
||||||
|
flag.StringVar(&c.BasicauthUsername, "github-basicauth-username", c.BasicauthUsername, "Username for GitHub basic auth to use instead of PAT or GitHub APP in case it's running behind a proxy API")
|
||||||
|
flag.StringVar(&c.BasicauthPassword, "github-basicauth-password", c.BasicauthPassword, "Password for GitHub basic auth to use instead of PAT or GitHub APP in case it's running behind a proxy API")
|
||||||
|
flag.StringVar(&c.RunnerGitHubURL, "runner-github-url", c.RunnerGitHubURL, "GitHub URL to be used by runners during registration")
|
||||||
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
if webhookSecretToken == "" && webhookSecretTokenEnv != "" {
|
||||||
|
setupLog.Info(fmt.Sprintf("Using the value from %s for -github-webhook-secret-token", webhookSecretTokenEnvName))
|
||||||
|
webhookSecretToken = webhookSecretTokenEnv
|
||||||
|
}
|
||||||
|
|
||||||
if webhookSecretToken == "" {
|
if webhookSecretToken == "" {
|
||||||
setupLog.Info("-webhook-secret-token is missing or empty. Create one following https://docs.github.com/en/developers/webhooks-and-events/securing-your-webhooks")
|
setupLog.Info(fmt.Sprintf("-github-webhook-secret-token and %s are missing or empty. Create one following https://docs.github.com/en/developers/webhooks-and-events/securing-your-webhooks and specify it via the flag or the envvar", webhookSecretTokenEnvName))
|
||||||
}
|
}
|
||||||
|
|
||||||
if watchNamespace == "" {
|
if watchNamespace == "" {
|
||||||
@@ -99,6 +130,8 @@ func main() {
|
|||||||
switch logLevel {
|
switch logLevel {
|
||||||
case logLevelDebug:
|
case logLevelDebug:
|
||||||
o.Development = true
|
o.Development = true
|
||||||
|
lvl := zaplib.NewAtomicLevelAt(-2) // maps to logr's V(2)
|
||||||
|
o.Level = &lvl
|
||||||
case logLevelInfo:
|
case logLevelInfo:
|
||||||
lvl := zaplib.NewAtomicLevelAt(zaplib.InfoLevel)
|
lvl := zaplib.NewAtomicLevelAt(zaplib.InfoLevel)
|
||||||
o.Level = &lvl
|
o.Level = &lvl
|
||||||
@@ -113,6 +146,22 @@ func main() {
|
|||||||
|
|
||||||
ctrl.SetLogger(logger)
|
ctrl.SetLogger(logger)
|
||||||
|
|
||||||
|
// In order to support runner groups with custom visibility (selected repositories), we need to perform some GitHub API calls.
|
||||||
|
// Let the user define if they want to opt-in supporting this option by providing the proper GitHub authentication parameters
|
||||||
|
// Without an opt-in, runner groups with custom visibility won't be supported to save API calls
|
||||||
|
// That is, all runner groups managed by ARC are assumed to be visible to any repositories,
|
||||||
|
// which is wrong when you have one or more non-default runner groups in your organization or enterprise.
|
||||||
|
if len(c.Token) > 0 || (c.AppID > 0 && c.AppInstallationID > 0 && c.AppPrivateKey != "") || (len(c.BasicauthUsername) > 0 && len(c.BasicauthPassword) > 0) {
|
||||||
|
ghClient, err = c.NewClient()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, "Error: Client creation failed.", err)
|
||||||
|
setupLog.Error(err, "unable to create controller", "controller", "Runner")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
|
||||||
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
|
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
|
||||||
Scheme: scheme,
|
Scheme: scheme,
|
||||||
SyncPeriod: &syncPeriod,
|
SyncPeriod: &syncPeriod,
|
||||||
@@ -127,16 +176,18 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
hraGitHubWebhook := &controllers.HorizontalRunnerAutoscalerGitHubWebhook{
|
hraGitHubWebhook := &controllers.HorizontalRunnerAutoscalerGitHubWebhook{
|
||||||
|
Name: "webhookbasedautoscaler",
|
||||||
Client: mgr.GetClient(),
|
Client: mgr.GetClient(),
|
||||||
Log: ctrl.Log.WithName("controllers").WithName("Runner"),
|
Log: ctrl.Log.WithName("controllers").WithName("webhookbasedautoscaler"),
|
||||||
Recorder: nil,
|
Recorder: nil,
|
||||||
Scheme: mgr.GetScheme(),
|
Scheme: mgr.GetScheme(),
|
||||||
SecretKeyBytes: []byte(webhookSecretToken),
|
SecretKeyBytes: []byte(webhookSecretToken),
|
||||||
Namespace: watchNamespace,
|
Namespace: watchNamespace,
|
||||||
|
GitHubClient: ghClient,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = hraGitHubWebhook.SetupWithManager(mgr); err != nil {
|
if err = hraGitHubWebhook.SetupWithManager(mgr); err != nil {
|
||||||
setupLog.Error(err, "unable to create controller", "controller", "Runner")
|
setupLog.Error(err, "unable to create controller", "controller", "webhookbasedautoscaler")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,7 +201,7 @@ func main() {
|
|||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
setupLog.Info("starting webhook server")
|
setupLog.Info("starting webhook server")
|
||||||
if err := mgr.Start(ctx.Done()); err != nil {
|
if err := mgr.Start(ctx); err != nil {
|
||||||
setupLog.Error(err, "problem running manager")
|
setupLog.Error(err, "problem running manager")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
@@ -183,7 +234,7 @@ func main() {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
<-ctrl.SetupSignalHandler()
|
<-ctrl.SetupSignalHandler().Done()
|
||||||
cancel()
|
cancel()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|||||||
@@ -1,285 +1,239 @@
|
|||||||
|
apiVersion: apiextensions.k8s.io/v1
|
||||||
---
|
|
||||||
apiVersion: apiextensions.k8s.io/v1beta1
|
|
||||||
kind: CustomResourceDefinition
|
kind: CustomResourceDefinition
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
controller-gen.kubebuilder.io/version: v0.3.0
|
controller-gen.kubebuilder.io/version: v0.7.0
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
name: horizontalrunnerautoscalers.actions.summerwind.dev
|
name: horizontalrunnerautoscalers.actions.summerwind.dev
|
||||||
spec:
|
spec:
|
||||||
additionalPrinterColumns:
|
|
||||||
- JSONPath: .spec.minReplicas
|
|
||||||
name: Min
|
|
||||||
type: number
|
|
||||||
- JSONPath: .spec.maxReplicas
|
|
||||||
name: Max
|
|
||||||
type: number
|
|
||||||
- JSONPath: .status.desiredReplicas
|
|
||||||
name: Desired
|
|
||||||
type: number
|
|
||||||
- JSONPath: .status.scheduledOverridesSummary
|
|
||||||
name: Schedule
|
|
||||||
type: string
|
|
||||||
group: actions.summerwind.dev
|
group: actions.summerwind.dev
|
||||||
names:
|
names:
|
||||||
kind: HorizontalRunnerAutoscaler
|
kind: HorizontalRunnerAutoscaler
|
||||||
listKind: HorizontalRunnerAutoscalerList
|
listKind: HorizontalRunnerAutoscalerList
|
||||||
plural: horizontalrunnerautoscalers
|
plural: horizontalrunnerautoscalers
|
||||||
|
shortNames:
|
||||||
|
- hra
|
||||||
singular: horizontalrunnerautoscaler
|
singular: horizontalrunnerautoscaler
|
||||||
scope: Namespaced
|
scope: Namespaced
|
||||||
subresources:
|
versions:
|
||||||
status: {}
|
- additionalPrinterColumns:
|
||||||
validation:
|
- jsonPath: .spec.minReplicas
|
||||||
openAPIV3Schema:
|
name: Min
|
||||||
description: HorizontalRunnerAutoscaler is the Schema for the horizontalrunnerautoscaler
|
type: number
|
||||||
API
|
- jsonPath: .spec.maxReplicas
|
||||||
properties:
|
name: Max
|
||||||
apiVersion:
|
type: number
|
||||||
description: 'APIVersion defines the versioned schema of this representation
|
- jsonPath: .status.desiredReplicas
|
||||||
of an object. Servers should convert recognized schemas to the latest
|
name: Desired
|
||||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
type: number
|
||||||
|
- jsonPath: .status.scheduledOverridesSummary
|
||||||
|
name: Schedule
|
||||||
type: string
|
type: string
|
||||||
kind:
|
name: v1alpha1
|
||||||
description: 'Kind is a string value representing the REST resource this
|
schema:
|
||||||
object represents. Servers may infer this from the endpoint the client
|
openAPIV3Schema:
|
||||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
description: HorizontalRunnerAutoscaler is the Schema for the horizontalrunnerautoscaler API
|
||||||
type: string
|
|
||||||
metadata:
|
|
||||||
type: object
|
|
||||||
spec:
|
|
||||||
description: HorizontalRunnerAutoscalerSpec defines the desired state of
|
|
||||||
HorizontalRunnerAutoscaler
|
|
||||||
properties:
|
properties:
|
||||||
capacityReservations:
|
apiVersion:
|
||||||
items:
|
description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||||
description: CapacityReservation specifies the number of replicas
|
type: string
|
||||||
temporarily added to the scale target until ExpirationTime.
|
kind:
|
||||||
properties:
|
description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||||
expirationTime:
|
type: string
|
||||||
format: date-time
|
metadata:
|
||||||
type: string
|
|
||||||
name:
|
|
||||||
type: string
|
|
||||||
replicas:
|
|
||||||
type: integer
|
|
||||||
type: object
|
|
||||||
type: array
|
|
||||||
maxReplicas:
|
|
||||||
description: MinReplicas is the maximum number of replicas the deployment
|
|
||||||
is allowed to scale
|
|
||||||
type: integer
|
|
||||||
metrics:
|
|
||||||
description: Metrics is the collection of various metric targets to
|
|
||||||
calculate desired number of runners
|
|
||||||
items:
|
|
||||||
properties:
|
|
||||||
repositoryNames:
|
|
||||||
description: RepositoryNames is the list of repository names to
|
|
||||||
be used for calculating the metric. For example, a repository
|
|
||||||
name is the REPO part of `github.com/USER/REPO`.
|
|
||||||
items:
|
|
||||||
type: string
|
|
||||||
type: array
|
|
||||||
scaleDownAdjustment:
|
|
||||||
description: ScaleDownAdjustment is the number of runners removed
|
|
||||||
on scale-down. You can only specify either ScaleDownFactor or
|
|
||||||
ScaleDownAdjustment.
|
|
||||||
type: integer
|
|
||||||
scaleDownFactor:
|
|
||||||
description: ScaleDownFactor is the multiplicative factor applied
|
|
||||||
to the current number of runners used to determine how many
|
|
||||||
pods should be removed.
|
|
||||||
type: string
|
|
||||||
scaleDownThreshold:
|
|
||||||
description: ScaleDownThreshold is the percentage of busy runners
|
|
||||||
less than which will trigger the hpa to scale the runners down.
|
|
||||||
type: string
|
|
||||||
scaleUpAdjustment:
|
|
||||||
description: ScaleUpAdjustment is the number of runners added
|
|
||||||
on scale-up. You can only specify either ScaleUpFactor or ScaleUpAdjustment.
|
|
||||||
type: integer
|
|
||||||
scaleUpFactor:
|
|
||||||
description: ScaleUpFactor is the multiplicative factor applied
|
|
||||||
to the current number of runners used to determine how many
|
|
||||||
pods should be added.
|
|
||||||
type: string
|
|
||||||
scaleUpThreshold:
|
|
||||||
description: ScaleUpThreshold is the percentage of busy runners
|
|
||||||
greater than which will trigger the hpa to scale runners up.
|
|
||||||
type: string
|
|
||||||
type:
|
|
||||||
description: Type is the type of metric to be used for autoscaling.
|
|
||||||
The only supported Type is TotalNumberOfQueuedAndInProgressWorkflowRuns
|
|
||||||
type: string
|
|
||||||
type: object
|
|
||||||
type: array
|
|
||||||
minReplicas:
|
|
||||||
description: MinReplicas is the minimum number of replicas the deployment
|
|
||||||
is allowed to scale
|
|
||||||
type: integer
|
|
||||||
scaleDownDelaySecondsAfterScaleOut:
|
|
||||||
description: ScaleDownDelaySecondsAfterScaleUp is the approximate delay
|
|
||||||
for a scale down followed by a scale up Used to prevent flapping (down->up->down->...
|
|
||||||
loop)
|
|
||||||
type: integer
|
|
||||||
scaleTargetRef:
|
|
||||||
description: ScaleTargetRef sis the reference to scaled resource like
|
|
||||||
RunnerDeployment
|
|
||||||
properties:
|
|
||||||
name:
|
|
||||||
type: string
|
|
||||||
type: object
|
type: object
|
||||||
scaleUpTriggers:
|
spec:
|
||||||
description: "ScaleUpTriggers is an experimental feature to increase
|
description: HorizontalRunnerAutoscalerSpec defines the desired state of HorizontalRunnerAutoscaler
|
||||||
the desired replicas by 1 on each webhook requested received by the
|
properties:
|
||||||
webhookBasedAutoscaler. \n This feature requires you to also enable
|
capacityReservations:
|
||||||
and deploy the webhookBasedAutoscaler onto your cluster. \n Note that
|
items:
|
||||||
the added runners remain until the next sync period at least, and
|
description: CapacityReservation specifies the number of replicas temporarily added to the scale target until ExpirationTime.
|
||||||
they may or may not be used by GitHub Actions depending on the timing.
|
|
||||||
They are intended to be used to gain \"resource slack\" immediately
|
|
||||||
after you receive a webhook from GitHub, so that you can loosely expect
|
|
||||||
MinReplicas runners to be always available."
|
|
||||||
items:
|
|
||||||
properties:
|
|
||||||
amount:
|
|
||||||
type: integer
|
|
||||||
duration:
|
|
||||||
type: string
|
|
||||||
githubEvent:
|
|
||||||
properties:
|
properties:
|
||||||
checkRun:
|
expirationTime:
|
||||||
description: https://docs.github.com/en/actions/reference/events-that-trigger-workflows#check_run
|
|
||||||
properties:
|
|
||||||
names:
|
|
||||||
description: Names is a list of GitHub Actions glob patterns.
|
|
||||||
Any check_run event whose name matches one of patterns
|
|
||||||
in the list can trigger autoscaling. Note that check_run
|
|
||||||
name seem to equal to the job name you've defined in
|
|
||||||
your actions workflow yaml file. So it is very likely
|
|
||||||
that you can utilize this to trigger depending on the
|
|
||||||
job.
|
|
||||||
items:
|
|
||||||
type: string
|
|
||||||
type: array
|
|
||||||
status:
|
|
||||||
type: string
|
|
||||||
types:
|
|
||||||
items:
|
|
||||||
type: string
|
|
||||||
type: array
|
|
||||||
type: object
|
|
||||||
pullRequest:
|
|
||||||
description: https://docs.github.com/en/actions/reference/events-that-trigger-workflows#pull_request
|
|
||||||
properties:
|
|
||||||
branches:
|
|
||||||
items:
|
|
||||||
type: string
|
|
||||||
type: array
|
|
||||||
types:
|
|
||||||
items:
|
|
||||||
type: string
|
|
||||||
type: array
|
|
||||||
type: object
|
|
||||||
push:
|
|
||||||
description: PushSpec is the condition for triggering scale-up
|
|
||||||
on push event Also see https://docs.github.com/en/actions/reference/events-that-trigger-workflows#push
|
|
||||||
type: object
|
|
||||||
type: object
|
|
||||||
type: object
|
|
||||||
type: array
|
|
||||||
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.
|
|
||||||
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.
|
|
||||||
properties:
|
|
||||||
endTime:
|
|
||||||
description: EndTime is the time at which the first override ends.
|
|
||||||
format: date-time
|
|
||||||
type: string
|
|
||||||
minReplicas:
|
|
||||||
description: MinReplicas is the number of runners while overriding.
|
|
||||||
If omitted, it doesn't override minReplicas.
|
|
||||||
minimum: 0
|
|
||||||
nullable: true
|
|
||||||
type: integer
|
|
||||||
recurrenceRule:
|
|
||||||
properties:
|
|
||||||
frequency:
|
|
||||||
description: Frequency is the name of a predefined interval
|
|
||||||
of each recurrence. The valid values are "Daily", "Weekly",
|
|
||||||
"Monthly", and "Yearly". If empty, the corresponding override
|
|
||||||
happens only once.
|
|
||||||
enum:
|
|
||||||
- Daily
|
|
||||||
- Weekly
|
|
||||||
- Monthly
|
|
||||||
- Yearly
|
|
||||||
type: string
|
|
||||||
untilTime:
|
|
||||||
description: UntilTime is the time of the final recurrence.
|
|
||||||
If empty, the schedule recurs forever.
|
|
||||||
format: date-time
|
format: date-time
|
||||||
type: string
|
type: string
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
replicas:
|
||||||
|
type: integer
|
||||||
type: object
|
type: object
|
||||||
startTime:
|
type: array
|
||||||
description: StartTime is the time at which the first override
|
maxReplicas:
|
||||||
starts.
|
description: MaxReplicas is the maximum number of replicas the deployment is allowed to scale
|
||||||
format: date-time
|
type: integer
|
||||||
type: string
|
metrics:
|
||||||
required:
|
description: Metrics is the collection of various metric targets to calculate desired number of runners
|
||||||
- endTime
|
items:
|
||||||
- startTime
|
properties:
|
||||||
type: object
|
repositoryNames:
|
||||||
type: array
|
description: RepositoryNames is the list of repository names to be used for calculating the metric. For example, a repository name is the REPO part of `github.com/USER/REPO`.
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
scaleDownAdjustment:
|
||||||
|
description: ScaleDownAdjustment is the number of runners removed on scale-down. You can only specify either ScaleDownFactor or ScaleDownAdjustment.
|
||||||
|
type: integer
|
||||||
|
scaleDownFactor:
|
||||||
|
description: ScaleDownFactor is the multiplicative factor applied to the current number of runners used to determine how many pods should be removed.
|
||||||
|
type: string
|
||||||
|
scaleDownThreshold:
|
||||||
|
description: ScaleDownThreshold is the percentage of busy runners less than which will trigger the hpa to scale the runners down.
|
||||||
|
type: string
|
||||||
|
scaleUpAdjustment:
|
||||||
|
description: ScaleUpAdjustment is the number of runners added on scale-up. You can only specify either ScaleUpFactor or ScaleUpAdjustment.
|
||||||
|
type: integer
|
||||||
|
scaleUpFactor:
|
||||||
|
description: ScaleUpFactor is the multiplicative factor applied to the current number of runners used to determine how many pods should be added.
|
||||||
|
type: string
|
||||||
|
scaleUpThreshold:
|
||||||
|
description: ScaleUpThreshold is the percentage of busy runners greater than which will trigger the hpa to scale runners up.
|
||||||
|
type: string
|
||||||
|
type:
|
||||||
|
description: Type is the type of metric to be used for autoscaling. The only supported Type is TotalNumberOfQueuedAndInProgressWorkflowRuns
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
type: array
|
||||||
|
minReplicas:
|
||||||
|
description: MinReplicas is the minimum number of replicas the deployment is allowed to scale
|
||||||
|
type: integer
|
||||||
|
scaleDownDelaySecondsAfterScaleOut:
|
||||||
|
description: ScaleDownDelaySecondsAfterScaleUp is the approximate delay for a scale down followed by a scale up Used to prevent flapping (down->up->down->... loop)
|
||||||
|
type: integer
|
||||||
|
scaleTargetRef:
|
||||||
|
description: ScaleTargetRef sis the reference to scaled resource like RunnerDeployment
|
||||||
|
properties:
|
||||||
|
kind:
|
||||||
|
description: Kind is the type of resource being referenced
|
||||||
|
enum:
|
||||||
|
- RunnerDeployment
|
||||||
|
- RunnerSet
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
description: Name is the name of resource being referenced
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
scaleUpTriggers:
|
||||||
|
description: "ScaleUpTriggers is an experimental feature to increase the desired replicas by 1 on each webhook requested received by the webhookBasedAutoscaler. \n This feature requires you to also enable and deploy the webhookBasedAutoscaler onto your cluster. \n Note that the added runners remain until the next sync period at least, and they may or may not be used by GitHub Actions depending on the timing. They are intended to be used to gain \"resource slack\" immediately after you receive a webhook from GitHub, so that you can loosely expect MinReplicas runners to be always available."
|
||||||
|
items:
|
||||||
|
properties:
|
||||||
|
amount:
|
||||||
|
type: integer
|
||||||
|
duration:
|
||||||
|
type: string
|
||||||
|
githubEvent:
|
||||||
|
properties:
|
||||||
|
checkRun:
|
||||||
|
description: https://docs.github.com/en/actions/reference/events-that-trigger-workflows#check_run
|
||||||
|
properties:
|
||||||
|
names:
|
||||||
|
description: Names is a list of GitHub Actions glob patterns. Any check_run event whose name matches one of patterns in the list can trigger autoscaling. Note that check_run name seem to equal to the job name you've defined in your actions workflow yaml file. So it is very likely that you can utilize this to trigger depending on the job.
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
repositories:
|
||||||
|
description: Repositories is a list of GitHub repositories. Any check_run event whose repository matches one of repositories in the list can trigger autoscaling.
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
types:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
type: object
|
||||||
|
pullRequest:
|
||||||
|
description: https://docs.github.com/en/actions/reference/events-that-trigger-workflows#pull_request
|
||||||
|
properties:
|
||||||
|
branches:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
types:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
type: object
|
||||||
|
push:
|
||||||
|
description: PushSpec is the condition for triggering scale-up on push event Also see https://docs.github.com/en/actions/reference/events-that-trigger-workflows#push
|
||||||
|
type: object
|
||||||
|
type: object
|
||||||
|
type: object
|
||||||
|
type: array
|
||||||
|
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.
|
||||||
|
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.
|
||||||
|
properties:
|
||||||
|
endTime:
|
||||||
|
description: EndTime is the time at which the first override ends.
|
||||||
|
format: date-time
|
||||||
|
type: string
|
||||||
|
minReplicas:
|
||||||
|
description: MinReplicas is the number of runners while overriding. If omitted, it doesn't override minReplicas.
|
||||||
|
minimum: 0
|
||||||
|
nullable: true
|
||||||
|
type: integer
|
||||||
|
recurrenceRule:
|
||||||
|
properties:
|
||||||
|
frequency:
|
||||||
|
description: Frequency is the name of a predefined interval of each recurrence. The valid values are "Daily", "Weekly", "Monthly", and "Yearly". If empty, the corresponding override happens only once.
|
||||||
|
enum:
|
||||||
|
- Daily
|
||||||
|
- Weekly
|
||||||
|
- Monthly
|
||||||
|
- Yearly
|
||||||
|
type: string
|
||||||
|
untilTime:
|
||||||
|
description: UntilTime is the time of the final recurrence. If empty, the schedule recurs forever.
|
||||||
|
format: date-time
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
startTime:
|
||||||
|
description: StartTime is the time at which the first override starts.
|
||||||
|
format: date-time
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- endTime
|
||||||
|
- startTime
|
||||||
|
type: object
|
||||||
|
type: array
|
||||||
|
type: object
|
||||||
|
status:
|
||||||
|
properties:
|
||||||
|
cacheEntries:
|
||||||
|
items:
|
||||||
|
properties:
|
||||||
|
expirationTime:
|
||||||
|
format: date-time
|
||||||
|
type: string
|
||||||
|
key:
|
||||||
|
type: string
|
||||||
|
value:
|
||||||
|
type: integer
|
||||||
|
type: object
|
||||||
|
type: array
|
||||||
|
desiredReplicas:
|
||||||
|
description: DesiredReplicas is the total number of desired, non-terminated and latest pods to be set for the primary RunnerSet This doesn't include outdated pods while upgrading the deployment and replacing the runnerset.
|
||||||
|
type: integer
|
||||||
|
lastSuccessfulScaleOutTime:
|
||||||
|
format: date-time
|
||||||
|
nullable: true
|
||||||
|
type: string
|
||||||
|
observedGeneration:
|
||||||
|
description: ObservedGeneration is the most recent generation observed for the target. It corresponds to e.g. RunnerDeployment's generation, which is updated on mutation by the API Server.
|
||||||
|
format: int64
|
||||||
|
type: integer
|
||||||
|
scheduledOverridesSummary:
|
||||||
|
description: ScheduledOverridesSummary is the summary of active and upcoming scheduled overrides to be shown in e.g. a column of a `kubectl get hra` output for observability.
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
type: object
|
type: object
|
||||||
status:
|
served: true
|
||||||
properties:
|
storage: true
|
||||||
cacheEntries:
|
subresources:
|
||||||
items:
|
status: {}
|
||||||
properties:
|
preserveUnknownFields: false
|
||||||
expirationTime:
|
|
||||||
format: date-time
|
|
||||||
type: string
|
|
||||||
key:
|
|
||||||
type: string
|
|
||||||
value:
|
|
||||||
type: integer
|
|
||||||
type: object
|
|
||||||
type: array
|
|
||||||
desiredReplicas:
|
|
||||||
description: DesiredReplicas is the total number of desired, non-terminated
|
|
||||||
and latest pods to be set for the primary RunnerSet This doesn't include
|
|
||||||
outdated pods while upgrading the deployment and replacing the runnerset.
|
|
||||||
type: integer
|
|
||||||
lastSuccessfulScaleOutTime:
|
|
||||||
format: date-time
|
|
||||||
nullable: true
|
|
||||||
type: string
|
|
||||||
observedGeneration:
|
|
||||||
description: ObservedGeneration is the most recent generation observed
|
|
||||||
for the target. It corresponds to e.g. RunnerDeployment's generation,
|
|
||||||
which is updated on mutation by the API Server.
|
|
||||||
format: int64
|
|
||||||
type: integer
|
|
||||||
scheduledOverridesSummary:
|
|
||||||
description: ScheduledOverridesSummary is the summary of active and
|
|
||||||
upcoming scheduled overrides to be shown in e.g. a column of a `kubectl
|
|
||||||
get hra` output for observability.
|
|
||||||
type: string
|
|
||||||
type: object
|
|
||||||
type: object
|
|
||||||
version: v1alpha1
|
|
||||||
versions:
|
|
||||||
- name: v1alpha1
|
|
||||||
served: true
|
|
||||||
storage: true
|
|
||||||
status:
|
status:
|
||||||
acceptedNames:
|
acceptedNames:
|
||||||
kind: ""
|
kind: ""
|
||||||
|
|||||||
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
4477
config/crd/bases/actions.summerwind.dev_runnersets.yaml
Normal file
4477
config/crd/bases/actions.summerwind.dev_runnersets.yaml
Normal file
File diff suppressed because it is too large
Load Diff
@@ -6,6 +6,7 @@ resources:
|
|||||||
- bases/actions.summerwind.dev_runnerreplicasets.yaml
|
- bases/actions.summerwind.dev_runnerreplicasets.yaml
|
||||||
- bases/actions.summerwind.dev_runnerdeployments.yaml
|
- bases/actions.summerwind.dev_runnerdeployments.yaml
|
||||||
- bases/actions.summerwind.dev_horizontalrunnerautoscalers.yaml
|
- bases/actions.summerwind.dev_horizontalrunnerautoscalers.yaml
|
||||||
|
- bases/actions.summerwind.dev_runnersets.yaml
|
||||||
# +kubebuilder:scaffold:crdkustomizeresource
|
# +kubebuilder:scaffold:crdkustomizeresource
|
||||||
|
|
||||||
patchesStrategicMerge:
|
patchesStrategicMerge:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# The following patch adds a directive for certmanager to inject CA into the CRD
|
# The following patch adds a directive for certmanager to inject CA into the CRD
|
||||||
# CRD conversion requires k8s 1.13 or later.
|
# CRD conversion requires k8s 1.13 or later.
|
||||||
apiVersion: apiextensions.k8s.io/v1beta1
|
apiVersion: apiextensions.k8s.io/v1
|
||||||
kind: CustomResourceDefinition
|
kind: CustomResourceDefinition
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
# The following patch enables conversion webhook for CRD
|
# The following patch enables conversion webhook for CRD
|
||||||
# CRD conversion requires k8s 1.13 or later.
|
# CRD conversion requires k8s 1.13 or later.
|
||||||
apiVersion: apiextensions.k8s.io/v1beta1
|
apiVersion: apiextensions.k8s.io/v1
|
||||||
kind: CustomResourceDefinition
|
kind: CustomResourceDefinition
|
||||||
metadata:
|
metadata:
|
||||||
name: runners.actions.summerwind.dev
|
name: runners.actions.summerwind.dev
|
||||||
spec:
|
spec:
|
||||||
conversion:
|
conversion:
|
||||||
strategy: Webhook
|
strategy: Webhook
|
||||||
webhookClientConfig:
|
webhook:
|
||||||
# this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank,
|
clientConfig:
|
||||||
# but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager)
|
# this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank,
|
||||||
caBundle: Cg==
|
# but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager)
|
||||||
service:
|
caBundle: Cg==
|
||||||
namespace: system
|
service:
|
||||||
name: webhook-service
|
namespace: system
|
||||||
path: /convert
|
name: webhook-service
|
||||||
|
path: /convert
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
# This patch add annotation to admission webhook config and
|
# This patch add annotation to admission webhook config and
|
||||||
# the variables $(CERTIFICATE_NAMESPACE) and $(CERTIFICATE_NAME) will be substituted by kustomize.
|
# the variables $(CERTIFICATE_NAMESPACE) and $(CERTIFICATE_NAME) will be substituted by kustomize.
|
||||||
apiVersion: admissionregistration.k8s.io/v1beta1
|
apiVersion: admissionregistration.k8s.io/v1
|
||||||
kind: MutatingWebhookConfiguration
|
kind: MutatingWebhookConfiguration
|
||||||
metadata:
|
metadata:
|
||||||
name: mutating-webhook-configuration
|
name: mutating-webhook-configuration
|
||||||
annotations:
|
annotations:
|
||||||
cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
|
cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
|
||||||
---
|
---
|
||||||
apiVersion: admissionregistration.k8s.io/v1beta1
|
apiVersion: admissionregistration.k8s.io/v1
|
||||||
kind: ValidatingWebhookConfiguration
|
kind: ValidatingWebhookConfiguration
|
||||||
metadata:
|
metadata:
|
||||||
name: validating-webhook-configuration
|
name: validating-webhook-configuration
|
||||||
|
|||||||
@@ -4,5 +4,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1
|
|||||||
kind: Kustomization
|
kind: Kustomization
|
||||||
images:
|
images:
|
||||||
- name: controller
|
- name: controller
|
||||||
newName: mumoshu/actions-runner-controller
|
newName: summerwind/actions-runner-controller
|
||||||
newTag: dev
|
newTag: latest
|
||||||
|
|||||||
@@ -134,6 +134,67 @@ rules:
|
|||||||
- get
|
- get
|
||||||
- patch
|
- patch
|
||||||
- update
|
- update
|
||||||
|
- apiGroups:
|
||||||
|
- actions.summerwind.dev
|
||||||
|
resources:
|
||||||
|
- runnersets
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- delete
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- actions.summerwind.dev
|
||||||
|
resources:
|
||||||
|
- runnersets/finalizers
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- delete
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- actions.summerwind.dev
|
||||||
|
resources:
|
||||||
|
- runnersets/status
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- apiGroups:
|
||||||
|
- apps
|
||||||
|
resources:
|
||||||
|
- statefulsets
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- delete
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- apps
|
||||||
|
resources:
|
||||||
|
- statefulsets/status
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- apiGroups:
|
||||||
|
- coordination.k8s.io
|
||||||
|
resources:
|
||||||
|
- leases
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- update
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- ""
|
- ""
|
||||||
resources:
|
resources:
|
||||||
|
|||||||
@@ -3,4 +3,4 @@ kind: Runner
|
|||||||
metadata:
|
metadata:
|
||||||
name: summerwind-actions-runner-controller
|
name: summerwind-actions-runner-controller
|
||||||
spec:
|
spec:
|
||||||
repository: summerwind/actions-runner-controller
|
repository: actions-runner-controller/actions-runner-controller
|
||||||
|
|||||||
@@ -6,4 +6,4 @@ spec:
|
|||||||
replicas: 2
|
replicas: 2
|
||||||
template:
|
template:
|
||||||
spec:
|
spec:
|
||||||
repository: summerwind/actions-runner-controller
|
repository: actions-runner-controller/actions-runner-controller
|
||||||
|
|||||||
@@ -6,4 +6,4 @@ spec:
|
|||||||
replicas: 2
|
replicas: 2
|
||||||
template:
|
template:
|
||||||
spec:
|
spec:
|
||||||
repository: summerwind/actions-runner-controller
|
repository: actions-runner-controller/actions-runner-controller
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
apiVersion: admissionregistration.k8s.io/v1beta1
|
apiVersion: admissionregistration.k8s.io/v1
|
||||||
kind: MutatingWebhookConfiguration
|
kind: MutatingWebhookConfiguration
|
||||||
metadata:
|
metadata:
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
name: mutating-webhook-configuration
|
name: mutating-webhook-configuration
|
||||||
webhooks:
|
webhooks:
|
||||||
- clientConfig:
|
- admissionReviewVersions:
|
||||||
caBundle: Cg==
|
- v1beta1
|
||||||
|
clientConfig:
|
||||||
service:
|
service:
|
||||||
name: webhook-service
|
name: webhook-service
|
||||||
namespace: system
|
namespace: system
|
||||||
@@ -25,8 +26,9 @@ webhooks:
|
|||||||
resources:
|
resources:
|
||||||
- runners
|
- runners
|
||||||
sideEffects: None
|
sideEffects: None
|
||||||
- clientConfig:
|
- admissionReviewVersions:
|
||||||
caBundle: Cg==
|
- v1beta1
|
||||||
|
clientConfig:
|
||||||
service:
|
service:
|
||||||
name: webhook-service
|
name: webhook-service
|
||||||
namespace: system
|
namespace: system
|
||||||
@@ -43,8 +45,10 @@ webhooks:
|
|||||||
- UPDATE
|
- UPDATE
|
||||||
resources:
|
resources:
|
||||||
- runnerdeployments
|
- runnerdeployments
|
||||||
- clientConfig:
|
sideEffects: None
|
||||||
caBundle: Cg==
|
- admissionReviewVersions:
|
||||||
|
- v1beta1
|
||||||
|
clientConfig:
|
||||||
service:
|
service:
|
||||||
name: webhook-service
|
name: webhook-service
|
||||||
namespace: system
|
namespace: system
|
||||||
@@ -62,16 +66,36 @@ webhooks:
|
|||||||
resources:
|
resources:
|
||||||
- runnerreplicasets
|
- runnerreplicasets
|
||||||
sideEffects: None
|
sideEffects: None
|
||||||
|
- admissionReviewVersions:
|
||||||
|
- v1beta1
|
||||||
|
clientConfig:
|
||||||
|
service:
|
||||||
|
name: webhook-service
|
||||||
|
namespace: system
|
||||||
|
path: /mutate-runner-set-pod
|
||||||
|
failurePolicy: Ignore
|
||||||
|
name: mutate-runner-pod.webhook.actions.summerwind.dev
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
apiVersions:
|
||||||
|
- v1
|
||||||
|
operations:
|
||||||
|
- CREATE
|
||||||
|
resources:
|
||||||
|
- pods
|
||||||
|
sideEffects: None
|
||||||
|
|
||||||
---
|
---
|
||||||
apiVersion: admissionregistration.k8s.io/v1beta1
|
apiVersion: admissionregistration.k8s.io/v1
|
||||||
kind: ValidatingWebhookConfiguration
|
kind: ValidatingWebhookConfiguration
|
||||||
metadata:
|
metadata:
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
name: validating-webhook-configuration
|
name: validating-webhook-configuration
|
||||||
webhooks:
|
webhooks:
|
||||||
- clientConfig:
|
- admissionReviewVersions:
|
||||||
caBundle: Cg==
|
- v1beta1
|
||||||
|
clientConfig:
|
||||||
service:
|
service:
|
||||||
name: webhook-service
|
name: webhook-service
|
||||||
namespace: system
|
namespace: system
|
||||||
@@ -89,8 +113,9 @@ webhooks:
|
|||||||
resources:
|
resources:
|
||||||
- runners
|
- runners
|
||||||
sideEffects: None
|
sideEffects: None
|
||||||
- clientConfig:
|
- admissionReviewVersions:
|
||||||
caBundle: Cg==
|
- v1beta1
|
||||||
|
clientConfig:
|
||||||
service:
|
service:
|
||||||
name: webhook-service
|
name: webhook-service
|
||||||
namespace: system
|
namespace: system
|
||||||
@@ -107,8 +132,10 @@ webhooks:
|
|||||||
- UPDATE
|
- UPDATE
|
||||||
resources:
|
resources:
|
||||||
- runnerdeployments
|
- runnerdeployments
|
||||||
- clientConfig:
|
sideEffects: None
|
||||||
caBundle: Cg==
|
- admissionReviewVersions:
|
||||||
|
- v1beta1
|
||||||
|
clientConfig:
|
||||||
service:
|
service:
|
||||||
name: webhook-service
|
name: webhook-service
|
||||||
namespace: system
|
namespace: system
|
||||||
|
|||||||
@@ -9,10 +9,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/summerwind/actions-runner-controller/api/v1alpha1"
|
"github.com/actions-runner-controller/actions-runner-controller/api/v1alpha1"
|
||||||
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
"github.com/google/go-github/v39/github"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -63,7 +61,7 @@ func (r *HorizontalRunnerAutoscalerReconciler) fetchSuggestedReplicasFromCache(h
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *HorizontalRunnerAutoscalerReconciler) suggestDesiredReplicas(rd v1alpha1.RunnerDeployment, hra v1alpha1.HorizontalRunnerAutoscaler) (*int, error) {
|
func (r *HorizontalRunnerAutoscalerReconciler) suggestDesiredReplicas(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 {
|
||||||
@@ -74,12 +72,12 @@ func (r *HorizontalRunnerAutoscalerReconciler) suggestDesiredReplicas(rd v1alpha
|
|||||||
numMetrics := len(metrics)
|
numMetrics := len(metrics)
|
||||||
if numMetrics == 0 {
|
if numMetrics == 0 {
|
||||||
if len(hra.Spec.ScaleUpTriggers) == 0 {
|
if len(hra.Spec.ScaleUpTriggers) == 0 {
|
||||||
return r.suggestReplicasByQueuedAndInProgressWorkflowRuns(rd, hra, nil)
|
return r.suggestReplicasByQueuedAndInProgressWorkflowRuns(st, hra, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
} else if numMetrics > 2 {
|
} else if numMetrics > 2 {
|
||||||
return nil, fmt.Errorf("Too many autoscaling metrics configured: It must be 0 to 2, but got %d", numMetrics)
|
return nil, fmt.Errorf("too many autoscaling metrics configured: It must be 0 to 2, but got %d", numMetrics)
|
||||||
}
|
}
|
||||||
|
|
||||||
primaryMetric := metrics[0]
|
primaryMetric := metrics[0]
|
||||||
@@ -92,11 +90,11 @@ func (r *HorizontalRunnerAutoscalerReconciler) suggestDesiredReplicas(rd v1alpha
|
|||||||
|
|
||||||
switch primaryMetricType {
|
switch primaryMetricType {
|
||||||
case v1alpha1.AutoscalingMetricTypeTotalNumberOfQueuedAndInProgressWorkflowRuns:
|
case v1alpha1.AutoscalingMetricTypeTotalNumberOfQueuedAndInProgressWorkflowRuns:
|
||||||
suggested, err = r.suggestReplicasByQueuedAndInProgressWorkflowRuns(rd, hra, &primaryMetric)
|
suggested, err = r.suggestReplicasByQueuedAndInProgressWorkflowRuns(st, hra, &primaryMetric)
|
||||||
case v1alpha1.AutoscalingMetricTypePercentageRunnersBusy:
|
case v1alpha1.AutoscalingMetricTypePercentageRunnersBusy:
|
||||||
suggested, err = r.suggestReplicasByPercentageRunnersBusy(rd, hra, primaryMetric)
|
suggested, err = r.suggestReplicasByPercentageRunnersBusy(st, hra, primaryMetric)
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("validting autoscaling metrics: unsupported metric type %q", primaryMetric)
|
return nil, fmt.Errorf("validating autoscaling metrics: unsupported metric type %q", primaryMetric)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -127,22 +125,22 @@ func (r *HorizontalRunnerAutoscalerReconciler) suggestDesiredReplicas(rd v1alpha
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return r.suggestReplicasByQueuedAndInProgressWorkflowRuns(rd, hra, &fallbackMetric)
|
return r.suggestReplicasByQueuedAndInProgressWorkflowRuns(st, hra, &fallbackMetric)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *HorizontalRunnerAutoscalerReconciler) suggestReplicasByQueuedAndInProgressWorkflowRuns(rd v1alpha1.RunnerDeployment, hra v1alpha1.HorizontalRunnerAutoscaler, metrics *v1alpha1.MetricSpec) (*int, error) {
|
func (r *HorizontalRunnerAutoscalerReconciler) suggestReplicasByQueuedAndInProgressWorkflowRuns(st scaleTarget, hra v1alpha1.HorizontalRunnerAutoscaler, metrics *v1alpha1.MetricSpec) (*int, error) {
|
||||||
|
|
||||||
var repos [][]string
|
var repos [][]string
|
||||||
repoID := rd.Spec.Template.Spec.Repository
|
repoID := st.repo
|
||||||
if repoID == "" {
|
if repoID == "" {
|
||||||
orgName := rd.Spec.Template.Spec.Organization
|
orgName := st.org
|
||||||
if orgName == "" {
|
if orgName == "" {
|
||||||
return nil, fmt.Errorf("asserting runner deployment spec to detect bug: spec.template.organization should not be empty on this code path")
|
return nil, fmt.Errorf("asserting runner deployment spec to detect bug: spec.template.organization should not be empty on this code path")
|
||||||
}
|
}
|
||||||
|
|
||||||
// In case it's an organizational runners deployment without any scaling metrics defined,
|
// In case it's an organizational runners deployment without any scaling metrics defined,
|
||||||
// we assume that the desired replicas should always be `minReplicas + capacityReservedThroughWebhook`.
|
// we assume that the desired replicas should always be `minReplicas + capacityReservedThroughWebhook`.
|
||||||
// See https://github.com/summerwind/actions-runner-controller/issues/377#issuecomment-793372693
|
// See https://github.com/actions-runner-controller/actions-runner-controller/issues/377#issuecomment-793372693
|
||||||
if metrics == nil {
|
if metrics == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@@ -167,14 +165,24 @@ func (r *HorizontalRunnerAutoscalerReconciler) suggestReplicasByQueuedAndInProgr
|
|||||||
fallback_cb()
|
fallback_cb()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
jobs, _, err := r.GitHubClient.Actions.ListWorkflowJobs(context.TODO(), user, repoName, runID, nil)
|
opt := github.ListWorkflowJobsOptions{ListOptions: github.ListOptions{PerPage: 50}}
|
||||||
if err != nil {
|
var allJobs []*github.WorkflowJob
|
||||||
r.Log.Error(err, "Error listing workflow jobs")
|
for {
|
||||||
fallback_cb()
|
jobs, resp, err := r.GitHubClient.Actions.ListWorkflowJobs(context.TODO(), user, repoName, runID, &opt)
|
||||||
} else if len(jobs.Jobs) == 0 {
|
if err != nil {
|
||||||
|
r.Log.Error(err, "Error listing workflow jobs")
|
||||||
|
return //err
|
||||||
|
}
|
||||||
|
allJobs = append(allJobs, jobs.Jobs...)
|
||||||
|
if resp.NextPage == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
opt.Page = resp.NextPage
|
||||||
|
}
|
||||||
|
if len(allJobs) == 0 {
|
||||||
fallback_cb()
|
fallback_cb()
|
||||||
} else {
|
} else {
|
||||||
for _, job := range jobs.Jobs {
|
for _, job := range allJobs {
|
||||||
switch job.GetStatus() {
|
switch job.GetStatus() {
|
||||||
case "completed":
|
case "completed":
|
||||||
// We add a case for `completed` so it is not counted in `unknown`.
|
// We add a case for `completed` so it is not counted in `unknown`.
|
||||||
@@ -230,14 +238,15 @@ func (r *HorizontalRunnerAutoscalerReconciler) suggestReplicasByQueuedAndInProgr
|
|||||||
"workflow_runs_queued", queued,
|
"workflow_runs_queued", queued,
|
||||||
"workflow_runs_unknown", unknown,
|
"workflow_runs_unknown", unknown,
|
||||||
"namespace", hra.Namespace,
|
"namespace", hra.Namespace,
|
||||||
"runner_deployment", rd.Name,
|
"kind", st.kind,
|
||||||
|
"name", st.st,
|
||||||
"horizontal_runner_autoscaler", hra.Name,
|
"horizontal_runner_autoscaler", hra.Name,
|
||||||
)
|
)
|
||||||
|
|
||||||
return &necessaryReplicas, nil
|
return &necessaryReplicas, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *HorizontalRunnerAutoscalerReconciler) suggestReplicasByPercentageRunnersBusy(rd v1alpha1.RunnerDeployment, hra v1alpha1.HorizontalRunnerAutoscaler, metrics v1alpha1.MetricSpec) (*int, error) {
|
func (r *HorizontalRunnerAutoscalerReconciler) suggestReplicasByPercentageRunnersBusy(st scaleTarget, hra v1alpha1.HorizontalRunnerAutoscaler, metrics v1alpha1.MetricSpec) (*int, error) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
scaleUpThreshold := defaultScaleUpThreshold
|
scaleUpThreshold := defaultScaleUpThreshold
|
||||||
scaleDownThreshold := defaultScaleDownThreshold
|
scaleDownThreshold := defaultScaleDownThreshold
|
||||||
@@ -294,41 +303,15 @@ func (r *HorizontalRunnerAutoscalerReconciler) suggestReplicasByPercentageRunner
|
|||||||
scaleDownFactor = sdf
|
scaleDownFactor = sdf
|
||||||
}
|
}
|
||||||
|
|
||||||
// return the list of runners in namespace. Horizontal Runner Autoscaler should only be responsible for scaling resources in its own ns.
|
runnerMap, err := st.getRunnerMap()
|
||||||
var runnerList v1alpha1.RunnerList
|
|
||||||
|
|
||||||
var opts []client.ListOption
|
|
||||||
|
|
||||||
opts = append(opts, client.InNamespace(rd.Namespace))
|
|
||||||
|
|
||||||
selector, err := metav1.LabelSelectorAsSelector(getSelector(&rd))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
opts = append(opts, client.MatchingLabelsSelector{Selector: selector})
|
|
||||||
|
|
||||||
r.Log.V(2).Info("Finding runners with selector", "ns", rd.Namespace)
|
|
||||||
|
|
||||||
if err := r.List(
|
|
||||||
ctx,
|
|
||||||
&runnerList,
|
|
||||||
opts...,
|
|
||||||
); err != nil {
|
|
||||||
if !kerrors.IsNotFound(err) {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
runnerMap := make(map[string]struct{})
|
|
||||||
for _, items := range runnerList.Items {
|
|
||||||
runnerMap[items.Name] = struct{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
enterprise = rd.Spec.Template.Spec.Enterprise
|
enterprise = st.enterprise
|
||||||
organization = rd.Spec.Template.Spec.Organization
|
organization = st.org
|
||||||
repository = rd.Spec.Template.Spec.Repository
|
repository = st.repo
|
||||||
)
|
)
|
||||||
|
|
||||||
// ListRunners will return all runners managed by GitHub - not restricted to ns
|
// ListRunners will return all runners managed by GitHub - not restricted to ns
|
||||||
@@ -343,7 +326,7 @@ func (r *HorizontalRunnerAutoscalerReconciler) suggestReplicasByPercentageRunner
|
|||||||
|
|
||||||
var desiredReplicasBefore int
|
var desiredReplicasBefore int
|
||||||
|
|
||||||
if v := rd.Spec.Replicas; v == nil {
|
if v := st.replicas; v == nil {
|
||||||
desiredReplicasBefore = 1
|
desiredReplicasBefore = 1
|
||||||
} else {
|
} else {
|
||||||
desiredReplicasBefore = *v
|
desiredReplicasBefore = *v
|
||||||
@@ -355,7 +338,7 @@ func (r *HorizontalRunnerAutoscalerReconciler) suggestReplicasByPercentageRunner
|
|||||||
numRunnersBusy int
|
numRunnersBusy int
|
||||||
)
|
)
|
||||||
|
|
||||||
numRunners = len(runnerList.Items)
|
numRunners = len(runnerMap)
|
||||||
|
|
||||||
for _, runner := range runners {
|
for _, runner := range runners {
|
||||||
if _, ok := runnerMap[*runner.Name]; ok {
|
if _, ok := runnerMap[*runner.Name]; ok {
|
||||||
@@ -382,7 +365,7 @@ func (r *HorizontalRunnerAutoscalerReconciler) suggestReplicasByPercentageRunner
|
|||||||
desiredReplicas = int(float64(desiredReplicasBefore) * scaleDownFactor)
|
desiredReplicas = int(float64(desiredReplicasBefore) * scaleDownFactor)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
desiredReplicas = *rd.Spec.Replicas
|
desiredReplicas = *st.replicas
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTES for operators:
|
// NOTES for operators:
|
||||||
@@ -398,7 +381,8 @@ func (r *HorizontalRunnerAutoscalerReconciler) suggestReplicasByPercentageRunner
|
|||||||
"num_runners_registered", numRunnersRegistered,
|
"num_runners_registered", numRunnersRegistered,
|
||||||
"num_runners_busy", numRunnersBusy,
|
"num_runners_busy", numRunnersBusy,
|
||||||
"namespace", hra.Namespace,
|
"namespace", hra.Namespace,
|
||||||
"runner_deployment", rd.Name,
|
"kind", st.kind,
|
||||||
|
"name", st.st,
|
||||||
"horizontal_runner_autoscaler", hra.Name,
|
"horizontal_runner_autoscaler", hra.Name,
|
||||||
"enterprise", enterprise,
|
"enterprise", enterprise,
|
||||||
"organization", organization,
|
"organization", organization,
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"net/url"
|
"net/url"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/summerwind/actions-runner-controller/api/v1alpha1"
|
"github.com/actions-runner-controller/actions-runner-controller/api/v1alpha1"
|
||||||
"github.com/summerwind/actions-runner-controller/github"
|
"github.com/actions-runner-controller/actions-runner-controller/github"
|
||||||
"github.com/summerwind/actions-runner-controller/github/fake"
|
"github.com/actions-runner-controller/actions-runner-controller/github/fake"
|
||||||
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"
|
||||||
@@ -203,7 +204,9 @@ func TestDetermineDesiredReplicas_RepositoryRunner(t *testing.T) {
|
|||||||
Spec: v1alpha1.RunnerDeploymentSpec{
|
Spec: v1alpha1.RunnerDeploymentSpec{
|
||||||
Template: v1alpha1.RunnerTemplate{
|
Template: v1alpha1.RunnerTemplate{
|
||||||
Spec: v1alpha1.RunnerSpec{
|
Spec: v1alpha1.RunnerSpec{
|
||||||
Repository: tc.repo,
|
RunnerConfig: v1alpha1.RunnerConfig{
|
||||||
|
Repository: tc.repo,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Replicas: tc.fixed,
|
Replicas: tc.fixed,
|
||||||
@@ -229,7 +232,9 @@ func TestDetermineDesiredReplicas_RepositoryRunner(t *testing.T) {
|
|||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
got, _, _, err := h.computeReplicasWithCache(log, metav1Now.Time, rd, hra, minReplicas)
|
st := h.scaleTargetFromRD(context.Background(), rd)
|
||||||
|
|
||||||
|
got, _, _, err := h.computeReplicasWithCache(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)
|
||||||
@@ -458,7 +463,9 @@ func TestDetermineDesiredReplicas_OrganizationalRunner(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Spec: v1alpha1.RunnerSpec{
|
Spec: v1alpha1.RunnerSpec{
|
||||||
Organization: tc.org,
|
RunnerConfig: v1alpha1.RunnerConfig{
|
||||||
|
Organization: tc.org,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Replicas: tc.fixed,
|
Replicas: tc.fixed,
|
||||||
@@ -493,7 +500,9 @@ func TestDetermineDesiredReplicas_OrganizationalRunner(t *testing.T) {
|
|||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
got, _, _, err := h.computeReplicasWithCache(log, metav1Now.Time, rd, hra, minReplicas)
|
st := h.scaleTargetFromRD(context.Background(), rd)
|
||||||
|
|
||||||
|
got, _, _, err := h.computeReplicasWithCache(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)
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package controllers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -29,17 +30,22 @@ import (
|
|||||||
"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/v33/github"
|
gogithub "github.com/google/go-github/v39/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"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
"github.com/summerwind/actions-runner-controller/api/v1alpha1"
|
"github.com/actions-runner-controller/actions-runner-controller/api/v1alpha1"
|
||||||
|
"github.com/actions-runner-controller/actions-runner-controller/github"
|
||||||
|
"github.com/actions-runner-controller/actions-runner-controller/simulator"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
scaleTargetKey = "scaleTarget"
|
scaleTargetKey = "scaleTarget"
|
||||||
|
|
||||||
|
keyPrefixEnterprise = "enterprises/"
|
||||||
|
keyRunnerGroup = "/group/"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HorizontalRunnerAutoscalerGitHubWebhook autoscales a HorizontalRunnerAutoscaler and the RunnerDeployment on each
|
// HorizontalRunnerAutoscalerGitHubWebhook autoscales a HorizontalRunnerAutoscaler and the RunnerDeployment on each
|
||||||
@@ -54,6 +60,9 @@ type HorizontalRunnerAutoscalerGitHubWebhook struct {
|
|||||||
// the administrator is generated and specified in GitHub Web UI.
|
// the administrator is generated and specified in GitHub Web UI.
|
||||||
SecretKeyBytes []byte
|
SecretKeyBytes []byte
|
||||||
|
|
||||||
|
// GitHub Client to discover runner groups assigned to a repository
|
||||||
|
GitHubClient *github.Client
|
||||||
|
|
||||||
// Namespace is the namespace to watch for HorizontalRunnerAutoscaler's to be
|
// Namespace is the namespace to watch for HorizontalRunnerAutoscaler's to be
|
||||||
// scaled on Webhook.
|
// scaled on Webhook.
|
||||||
// Set to empty for letting it watch for all namespaces.
|
// Set to empty for letting it watch for all namespaces.
|
||||||
@@ -61,7 +70,7 @@ type HorizontalRunnerAutoscalerGitHubWebhook struct {
|
|||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (autoscaler *HorizontalRunnerAutoscalerGitHubWebhook) Reconcile(request reconcile.Request) (reconcile.Result, error) {
|
func (autoscaler *HorizontalRunnerAutoscalerGitHubWebhook) Reconcile(_ context.Context, request reconcile.Request) (reconcile.Result, error) {
|
||||||
return ctrl.Result{}, nil
|
return ctrl.Result{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,7 +93,7 @@ func (autoscaler *HorizontalRunnerAutoscalerGitHubWebhook) Handle(w http.Respons
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
msg := err.Error()
|
msg := err.Error()
|
||||||
if written, err := w.Write([]byte(msg)); err != nil {
|
if written, err := w.Write([]byte(msg)); err != nil {
|
||||||
autoscaler.Log.Error(err, "failed writing http error response", "msg", msg, "written", written)
|
autoscaler.Log.V(1).Error(err, "failed writing http error response", "msg", msg, "written", written)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -98,6 +107,7 @@ func (autoscaler *HorizontalRunnerAutoscalerGitHubWebhook) Handle(w http.Respons
|
|||||||
|
|
||||||
// respond ok to GET / e.g. for health check
|
// respond ok to GET / e.g. for health check
|
||||||
if r.Method == http.MethodGet {
|
if r.Method == http.MethodGet {
|
||||||
|
ok = true
|
||||||
fmt.Fprintln(w, "webhook server is running")
|
fmt.Fprintln(w, "webhook server is running")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -141,6 +151,20 @@ func (autoscaler *HorizontalRunnerAutoscalerGitHubWebhook) Handle(w http.Respons
|
|||||||
"delivery", r.Header.Get("X-GitHub-Delivery"),
|
"delivery", r.Header.Get("X-GitHub-Delivery"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var enterpriseEvent struct {
|
||||||
|
Enterprise struct {
|
||||||
|
Slug string `json:"slug,omitempty"`
|
||||||
|
} `json:"enterprise,omitempty"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(payload, &enterpriseEvent); err != nil {
|
||||||
|
var s string
|
||||||
|
if payload != nil {
|
||||||
|
s = string(payload)
|
||||||
|
}
|
||||||
|
autoscaler.Log.Error(err, "could not parse webhook payload for extracting enterprise slug", "webhookType", webhookType, "payload", s)
|
||||||
|
}
|
||||||
|
enterpriseSlug := enterpriseEvent.Enterprise.Slug
|
||||||
|
|
||||||
switch e := event.(type) {
|
switch e := event.(type) {
|
||||||
case *gogithub.PushEvent:
|
case *gogithub.PushEvent:
|
||||||
target, err = autoscaler.getScaleUpTarget(
|
target, err = autoscaler.getScaleUpTarget(
|
||||||
@@ -149,6 +173,9 @@ func (autoscaler *HorizontalRunnerAutoscalerGitHubWebhook) Handle(w http.Respons
|
|||||||
e.Repo.GetName(),
|
e.Repo.GetName(),
|
||||||
e.Repo.Owner.GetLogin(),
|
e.Repo.Owner.GetLogin(),
|
||||||
e.Repo.Owner.GetType(),
|
e.Repo.Owner.GetType(),
|
||||||
|
// Most go-github Event types don't seem to contain Enteprirse(.Slug) fields
|
||||||
|
// we need, so we parse it by ourselves.
|
||||||
|
enterpriseSlug,
|
||||||
autoscaler.MatchPushEvent(e),
|
autoscaler.MatchPushEvent(e),
|
||||||
)
|
)
|
||||||
case *gogithub.PullRequestEvent:
|
case *gogithub.PullRequestEvent:
|
||||||
@@ -158,6 +185,9 @@ func (autoscaler *HorizontalRunnerAutoscalerGitHubWebhook) Handle(w http.Respons
|
|||||||
e.Repo.GetName(),
|
e.Repo.GetName(),
|
||||||
e.Repo.Owner.GetLogin(),
|
e.Repo.Owner.GetLogin(),
|
||||||
e.Repo.Owner.GetType(),
|
e.Repo.Owner.GetType(),
|
||||||
|
// Most go-github Event types don't seem to contain Enteprirse(.Slug) fields
|
||||||
|
// we need, so we parse it by ourselves.
|
||||||
|
enterpriseSlug,
|
||||||
autoscaler.MatchPullRequestEvent(e),
|
autoscaler.MatchPullRequestEvent(e),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -174,6 +204,9 @@ func (autoscaler *HorizontalRunnerAutoscalerGitHubWebhook) Handle(w http.Respons
|
|||||||
e.Repo.GetName(),
|
e.Repo.GetName(),
|
||||||
e.Repo.Owner.GetLogin(),
|
e.Repo.Owner.GetLogin(),
|
||||||
e.Repo.Owner.GetType(),
|
e.Repo.Owner.GetType(),
|
||||||
|
// Most go-github Event types don't seem to contain Enteprirse(.Slug) fields
|
||||||
|
// we need, so we parse it by ourselves.
|
||||||
|
enterpriseSlug,
|
||||||
autoscaler.MatchCheckRunEvent(e),
|
autoscaler.MatchCheckRunEvent(e),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -183,6 +216,53 @@ func (autoscaler *HorizontalRunnerAutoscalerGitHubWebhook) Handle(w http.Respons
|
|||||||
"action", e.GetAction(),
|
"action", e.GetAction(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
case *gogithub.WorkflowJobEvent:
|
||||||
|
if workflowJob := e.GetWorkflowJob(); workflowJob != nil {
|
||||||
|
log = log.WithValues(
|
||||||
|
"workflowJob.status", workflowJob.GetStatus(),
|
||||||
|
"workflowJob.labels", workflowJob.Labels,
|
||||||
|
"repository.name", e.Repo.GetName(),
|
||||||
|
"repository.owner.login", e.Repo.Owner.GetLogin(),
|
||||||
|
"repository.owner.type", e.Repo.Owner.GetType(),
|
||||||
|
"enterprise.slug", enterpriseSlug,
|
||||||
|
"action", e.GetAction(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
labels := e.WorkflowJob.Labels
|
||||||
|
|
||||||
|
switch action := e.GetAction(); action {
|
||||||
|
case "queued", "completed":
|
||||||
|
target, err = autoscaler.getJobScaleUpTargetForRepoOrOrg(
|
||||||
|
context.TODO(),
|
||||||
|
log,
|
||||||
|
e.Repo.GetName(),
|
||||||
|
e.Repo.Owner.GetLogin(),
|
||||||
|
e.Repo.Owner.GetType(),
|
||||||
|
enterpriseSlug,
|
||||||
|
labels,
|
||||||
|
)
|
||||||
|
|
||||||
|
if target != nil {
|
||||||
|
if e.GetAction() == "queued" {
|
||||||
|
target.Amount = 1
|
||||||
|
} else if e.GetAction() == "completed" {
|
||||||
|
// A nagative amount is processed in the tryScale func as a scale-down request,
|
||||||
|
// that erasese the oldest CapacityReservation with the same amount.
|
||||||
|
// If the first CapacityReservation was with Replicas=1, this negative scale target erases that,
|
||||||
|
// so that the resulting desired replicas decreases by 1.
|
||||||
|
target.Amount = -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
ok = true
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
|
||||||
|
log.V(2).Info("Received and ignored a workflow_job event as it triggers neither scale-up nor scale-down", "action", action)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
case *gogithub.PingEvent:
|
case *gogithub.PingEvent:
|
||||||
ok = true
|
ok = true
|
||||||
|
|
||||||
@@ -210,7 +290,7 @@ func (autoscaler *HorizontalRunnerAutoscalerGitHubWebhook) Handle(w http.Respons
|
|||||||
}
|
}
|
||||||
|
|
||||||
if target == nil {
|
if target == nil {
|
||||||
log.Info(
|
log.V(1).Info(
|
||||||
"Scale target not found. If this is unexpected, ensure that there is exactly one repository-wide or organizational runner deployment that matches this webhook event",
|
"Scale target not found. If this is unexpected, ensure that there is exactly one repository-wide or organizational runner deployment that matches this webhook event",
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -227,7 +307,7 @@ func (autoscaler *HorizontalRunnerAutoscalerGitHubWebhook) Handle(w http.Respons
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := autoscaler.tryScaleUp(context.TODO(), target); err != nil {
|
if err := autoscaler.tryScale(context.TODO(), target); err != nil {
|
||||||
log.Error(err, "could not scale up")
|
log.Error(err, "could not scale up")
|
||||||
|
|
||||||
return
|
return
|
||||||
@@ -237,7 +317,7 @@ func (autoscaler *HorizontalRunnerAutoscalerGitHubWebhook) Handle(w http.Respons
|
|||||||
|
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
|
|
||||||
msg := fmt.Sprintf("scaled %s by 1", target.Name)
|
msg := fmt.Sprintf("scaled %s by %d", target.Name, target.Amount)
|
||||||
|
|
||||||
autoscaler.Log.Info(msg)
|
autoscaler.Log.Info(msg)
|
||||||
|
|
||||||
@@ -352,7 +432,7 @@ func (autoscaler *HorizontalRunnerAutoscalerGitHubWebhook) getScaleTarget(ctx co
|
|||||||
"Found too many scale targets: "+
|
"Found too many scale targets: "+
|
||||||
"It must be exactly one to avoid ambiguity. "+
|
"It must be exactly one to avoid ambiguity. "+
|
||||||
"Either set Namespace for the webhook-based autoscaler to let it only find HRAs in the namespace, "+
|
"Either set Namespace for the webhook-based autoscaler to let it only find HRAs in the namespace, "+
|
||||||
"or update Repository or Organization fields in your RunnerDeployment resources to fix the ambiguity.",
|
"or update Repository, Organization, or Enterprise fields in your RunnerDeployment resources to fix the ambiguity.",
|
||||||
"scaleTargets", strings.Join(scaleTargetIDs, ","))
|
"scaleTargets", strings.Join(scaleTargetIDs, ","))
|
||||||
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
@@ -361,40 +441,320 @@ func (autoscaler *HorizontalRunnerAutoscalerGitHubWebhook) getScaleTarget(ctx co
|
|||||||
return &targets[0], nil
|
return &targets[0], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (autoscaler *HorizontalRunnerAutoscalerGitHubWebhook) getScaleUpTarget(ctx context.Context, log logr.Logger, repo, owner, ownerType string, f func(v1alpha1.ScaleUpTrigger) bool) (*ScaleTarget, error) {
|
func (autoscaler *HorizontalRunnerAutoscalerGitHubWebhook) getScaleUpTarget(ctx context.Context, log logr.Logger, repo, owner, ownerType, enterprise string, f func(v1alpha1.ScaleUpTrigger) bool) (*ScaleTarget, error) {
|
||||||
|
scaleTarget := func(value string) (*ScaleTarget, error) {
|
||||||
|
return autoscaler.getScaleTarget(ctx, value, f)
|
||||||
|
}
|
||||||
|
return autoscaler.getScaleUpTargetWithFunction(ctx, log, repo, owner, ownerType, enterprise, scaleTarget)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (autoscaler *HorizontalRunnerAutoscalerGitHubWebhook) getJobScaleUpTargetForRepoOrOrg(
|
||||||
|
ctx context.Context, log logr.Logger, repo, owner, ownerType, enterprise string, labels []string,
|
||||||
|
) (*ScaleTarget, error) {
|
||||||
|
|
||||||
|
scaleTarget := func(value string) (*ScaleTarget, error) {
|
||||||
|
return autoscaler.getJobScaleTarget(ctx, value, labels)
|
||||||
|
}
|
||||||
|
return autoscaler.getScaleUpTargetWithFunction(ctx, log, repo, owner, ownerType, enterprise, scaleTarget)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (autoscaler *HorizontalRunnerAutoscalerGitHubWebhook) getScaleUpTargetWithFunction(
|
||||||
|
ctx context.Context, log logr.Logger, repo, owner, ownerType, enterprise string, scaleTarget func(value string) (*ScaleTarget, error)) (*ScaleTarget, error) {
|
||||||
|
|
||||||
repositoryRunnerKey := owner + "/" + repo
|
repositoryRunnerKey := owner + "/" + repo
|
||||||
|
|
||||||
if target, err := autoscaler.getScaleTarget(ctx, repositoryRunnerKey, f); err != nil {
|
// Search for repository HRAs
|
||||||
log.Info("finding repository-wide runner", "repository", repositoryRunnerKey)
|
if target, err := scaleTarget(repositoryRunnerKey); err != nil {
|
||||||
|
log.Error(err, "finding repository-wide runner", "repository", repositoryRunnerKey)
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if target != nil {
|
} else if target != nil {
|
||||||
log.Info("scale up target is repository-wide runners", "repository", repo)
|
log.Info("job scale up target is repository-wide runners", "repository", repo)
|
||||||
return target, nil
|
return target, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if ownerType == "User" {
|
if ownerType == "User" {
|
||||||
log.V(1).Info("no repository runner found", "organization", owner)
|
log.V(1).Info("user repositories not supported", "owner", owner)
|
||||||
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if target, err := autoscaler.getScaleTarget(ctx, owner, f); err != nil {
|
// Find the potential runner groups first to avoid spending API queries needless. Once/if GitHub improves an
|
||||||
log.Info("finding organizational runner", "organization", owner)
|
// API to find related/linked runner groups from a specific repository this logic could be removed
|
||||||
|
managedRunnerGroups, err := autoscaler.getManagedRunnerGroupsFromHRAs(ctx, enterprise, owner)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err, "finding potential organization/enterprise runner groups from HRAs", "organization", owner)
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if target != nil {
|
}
|
||||||
log.Info("scale up target is organizational runners", "organization", owner)
|
if managedRunnerGroups.IsEmpty() {
|
||||||
return target, nil
|
log.V(1).Info("no repository/organizational/enterprise runner found",
|
||||||
} else {
|
|
||||||
log.V(1).Info("no repository runner or organizational runner found",
|
|
||||||
"repository", repositoryRunnerKey,
|
"repository", repositoryRunnerKey,
|
||||||
"organization", owner,
|
"organization", owner,
|
||||||
|
"enterprises", enterprise,
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
log.V(1).Info("Found some runner groups are managed by ARC", "groups", managedRunnerGroups)
|
||||||
|
}
|
||||||
|
|
||||||
|
var visibleGroups *simulator.VisibleRunnerGroups
|
||||||
|
if autoscaler.GitHubClient != nil {
|
||||||
|
simu := &simulator.Simulator{
|
||||||
|
Client: autoscaler.GitHubClient,
|
||||||
|
}
|
||||||
|
// 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
|
||||||
|
// where owner/repo has access to as well. The list will include default runner group also if it has access to
|
||||||
|
visibleGroups, err = simu.GetRunnerGroupsVisibleToRepository(ctx, owner, repositoryRunnerKey, managedRunnerGroups)
|
||||||
|
log.V(1).Info("Searching in runner groups", "groups", visibleGroups)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err, "Unable to find runner groups from repository", "organization", owner, "repository", repo)
|
||||||
|
return nil, fmt.Errorf("error while finding visible runner groups: %v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// For backwards compatibility if GitHub authentication is not configured, we assume all runner groups have
|
||||||
|
// visibility=all to honor the previous implementation, therefore any available enterprise/organization runner
|
||||||
|
// is a potential target for scaling. This will also avoid doing extra API calls caused by
|
||||||
|
// GitHubClient.GetRunnerGroupsVisibleToRepository in case users are not using custom visibility on their runner
|
||||||
|
// groups or they are using only default runner groups
|
||||||
|
visibleGroups = managedRunnerGroups
|
||||||
|
}
|
||||||
|
|
||||||
|
scaleTargetKey := func(rg simulator.RunnerGroup) string {
|
||||||
|
switch rg.Kind {
|
||||||
|
case simulator.Default:
|
||||||
|
switch rg.Scope {
|
||||||
|
case simulator.Organization:
|
||||||
|
return owner
|
||||||
|
case simulator.Enterprise:
|
||||||
|
return enterpriseKey(enterprise)
|
||||||
|
}
|
||||||
|
case simulator.Custom:
|
||||||
|
switch rg.Scope {
|
||||||
|
case simulator.Organization:
|
||||||
|
return organizationalRunnerGroupKey(owner, rg.Name)
|
||||||
|
case simulator.Enterprise:
|
||||||
|
return enterpriseRunnerGroupKey(enterprise, rg.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
log.V(1).Info("groups", "groups", visibleGroups)
|
||||||
|
|
||||||
|
var t *ScaleTarget
|
||||||
|
|
||||||
|
traverseErr := visibleGroups.Traverse(func(rg simulator.RunnerGroup) (bool, error) {
|
||||||
|
key := scaleTargetKey(rg)
|
||||||
|
|
||||||
|
target, err := scaleTarget(key)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err, "finding runner group", "enterprise", enterprise, "organization", owner, "repository", repo, "key", key)
|
||||||
|
return false, err
|
||||||
|
} else if target == nil {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
t = target
|
||||||
|
log.V(1).Info("job scale up target found", "enterprise", enterprise, "organization", owner, "repository", repo, "key", key)
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if traverseErr != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if t == nil {
|
||||||
|
log.V(1).Info("no repository/organizational/enterprise runner found",
|
||||||
|
"repository", repositoryRunnerKey,
|
||||||
|
"organization", owner,
|
||||||
|
"enterprise", enterprise,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (autoscaler *HorizontalRunnerAutoscalerGitHubWebhook) getManagedRunnerGroupsFromHRAs(ctx context.Context, enterprise, org string) (*simulator.VisibleRunnerGroups, error) {
|
||||||
|
groups := simulator.NewVisibleRunnerGroups()
|
||||||
|
ns := autoscaler.Namespace
|
||||||
|
|
||||||
|
var defaultListOpts []client.ListOption
|
||||||
|
if ns != "" {
|
||||||
|
defaultListOpts = append(defaultListOpts, client.InNamespace(ns))
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := append([]client.ListOption{}, defaultListOpts...)
|
||||||
|
if autoscaler.Namespace != "" {
|
||||||
|
opts = append(opts, client.InNamespace(autoscaler.Namespace))
|
||||||
|
}
|
||||||
|
|
||||||
|
var hraList v1alpha1.HorizontalRunnerAutoscalerList
|
||||||
|
if err := autoscaler.List(ctx, &hraList, opts...); err != nil {
|
||||||
|
return groups, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, hra := range hraList.Items {
|
||||||
|
var o, e, g string
|
||||||
|
|
||||||
|
kind := hra.Spec.ScaleTargetRef.Kind
|
||||||
|
switch kind {
|
||||||
|
case "RunnerSet":
|
||||||
|
var rs v1alpha1.RunnerSet
|
||||||
|
if err := autoscaler.Client.Get(context.Background(), types.NamespacedName{Namespace: hra.Namespace, Name: hra.Spec.ScaleTargetRef.Name}, &rs); err != nil {
|
||||||
|
return groups, err
|
||||||
|
}
|
||||||
|
o, e, g = rs.Spec.Organization, rs.Spec.Enterprise, rs.Spec.Group
|
||||||
|
case "RunnerDeployment", "":
|
||||||
|
var rd v1alpha1.RunnerDeployment
|
||||||
|
if err := autoscaler.Client.Get(context.Background(), types.NamespacedName{Namespace: hra.Namespace, Name: hra.Spec.ScaleTargetRef.Name}, &rd); err != nil {
|
||||||
|
return groups, err
|
||||||
|
}
|
||||||
|
o, e, g = rd.Spec.Template.Spec.Organization, rd.Spec.Template.Spec.Enterprise, rd.Spec.Template.Spec.Group
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported scale target kind: %v", kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
if g != "" && e == "" && o == "" {
|
||||||
|
autoscaler.Log.V(1).Info(
|
||||||
|
"invalid runner group config in scale target: spec.group must be set along with either spec.enterprise or spec.organization",
|
||||||
|
"scaleTargetKind", kind,
|
||||||
|
"group", g,
|
||||||
|
"enterprise", e,
|
||||||
|
"organization", o,
|
||||||
|
)
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if e != enterprise && o != org {
|
||||||
|
autoscaler.Log.V(1).Info(
|
||||||
|
"Skipped scale target irrelevant to event",
|
||||||
|
"eventOrganization", org,
|
||||||
|
"eventEnterprise", enterprise,
|
||||||
|
"scaleTargetKind", kind,
|
||||||
|
"scaleTargetGroup", g,
|
||||||
|
"scaleTargetEnterprise", e,
|
||||||
|
"scaleTargetOrganization", o,
|
||||||
|
)
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
rg := simulator.NewRunnerGroupFromProperties(e, o, g)
|
||||||
|
|
||||||
|
if err := groups.Add(rg); err != nil {
|
||||||
|
return groups, fmt.Errorf("failed adding visible group from HRA %s/%s: %w", hra.Namespace, hra.Name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return groups, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (autoscaler *HorizontalRunnerAutoscalerGitHubWebhook) getJobScaleTarget(ctx context.Context, name string, labels []string) (*ScaleTarget, error) {
|
||||||
|
hras, err := autoscaler.findHRAsByKey(ctx, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
autoscaler.Log.V(1).Info(fmt.Sprintf("Found %d HRAs by key", len(hras)), "key", name)
|
||||||
|
|
||||||
|
HRA:
|
||||||
|
for _, hra := range hras {
|
||||||
|
if !hra.ObjectMeta.DeletionTimestamp.IsZero() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(hra.Spec.ScaleUpTriggers) > 1 {
|
||||||
|
autoscaler.Log.V(1).Info("Skipping this HRA as it has too many ScaleUpTriggers to be used in workflow_job based scaling", "hra", hra.Name)
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var duration metav1.Duration
|
||||||
|
|
||||||
|
if len(hra.Spec.ScaleUpTriggers) > 0 {
|
||||||
|
duration = hra.Spec.ScaleUpTriggers[0].Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
if duration.Duration <= 0 {
|
||||||
|
// Try to release the reserved capacity after at least 10 minutes by default,
|
||||||
|
// we won't end up in the reserved capacity remained forever in case GitHub somehow stopped sending us "completed" workflow_job events.
|
||||||
|
// GitHub usually send us those but nothing is 100% guaranteed, e.g. in case of something went wrong on GitHub :)
|
||||||
|
// Probably we'd better make this configurable via custom resources in the future?
|
||||||
|
duration.Duration = 10 * time.Minute
|
||||||
|
}
|
||||||
|
|
||||||
|
switch hra.Spec.ScaleTargetRef.Kind {
|
||||||
|
case "RunnerSet":
|
||||||
|
var rs v1alpha1.RunnerSet
|
||||||
|
|
||||||
|
if err := autoscaler.Client.Get(context.Background(), types.NamespacedName{Namespace: hra.Namespace, Name: hra.Spec.ScaleTargetRef.Name}, &rs); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that the RunnerSet-managed runners have all the labels requested by the workflow_job.
|
||||||
|
for _, l := range labels {
|
||||||
|
var matched bool
|
||||||
|
|
||||||
|
// ignore "self-hosted" label as all instance here are self-hosted
|
||||||
|
if l == "self-hosted" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO labels related to OS and architecture needs to be explicitly declared or the current implementation will not be able to find them.
|
||||||
|
|
||||||
|
for _, l2 := range rs.Spec.Labels {
|
||||||
|
if l == l2 {
|
||||||
|
matched = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !matched {
|
||||||
|
continue HRA
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ScaleTarget{HorizontalRunnerAutoscaler: hra, ScaleUpTrigger: v1alpha1.ScaleUpTrigger{Duration: duration}}, nil
|
||||||
|
case "RunnerDeployment", "":
|
||||||
|
var rd v1alpha1.RunnerDeployment
|
||||||
|
|
||||||
|
if err := autoscaler.Client.Get(context.Background(), types.NamespacedName{Namespace: hra.Namespace, Name: hra.Spec.ScaleTargetRef.Name}, &rd); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that the RunnerDeployment-managed runners have all the labels requested by the workflow_job.
|
||||||
|
for _, l := range labels {
|
||||||
|
var matched bool
|
||||||
|
|
||||||
|
// ignore "self-hosted" label as all instance here are self-hosted
|
||||||
|
if l == "self-hosted" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO labels related to OS and architecture needs to be explicitly declared or the current implementation will not be able to find them.
|
||||||
|
|
||||||
|
for _, l2 := range rd.Spec.Template.Spec.Labels {
|
||||||
|
if l == l2 {
|
||||||
|
matched = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !matched {
|
||||||
|
continue HRA
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ScaleTarget{HorizontalRunnerAutoscaler: hra, ScaleUpTrigger: v1alpha1.ScaleUpTrigger{Duration: duration}}, nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported scaleTargetRef.kind: %v", hra.Spec.ScaleTargetRef.Kind)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (autoscaler *HorizontalRunnerAutoscalerGitHubWebhook) tryScaleUp(ctx context.Context, target *ScaleTarget) error {
|
func (autoscaler *HorizontalRunnerAutoscalerGitHubWebhook) tryScale(ctx context.Context, target *ScaleTarget) error {
|
||||||
if target == nil {
|
if target == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -403,16 +763,38 @@ func (autoscaler *HorizontalRunnerAutoscalerGitHubWebhook) tryScaleUp(ctx contex
|
|||||||
|
|
||||||
amount := 1
|
amount := 1
|
||||||
|
|
||||||
if target.ScaleUpTrigger.Amount > 0 {
|
if target.ScaleUpTrigger.Amount != 0 {
|
||||||
amount = target.ScaleUpTrigger.Amount
|
amount = target.ScaleUpTrigger.Amount
|
||||||
}
|
}
|
||||||
|
|
||||||
capacityReservations := getValidCapacityReservations(copy)
|
capacityReservations := getValidCapacityReservations(copy)
|
||||||
|
|
||||||
copy.Spec.CapacityReservations = append(capacityReservations, v1alpha1.CapacityReservation{
|
if amount > 0 {
|
||||||
ExpirationTime: metav1.Time{Time: time.Now().Add(target.ScaleUpTrigger.Duration.Duration)},
|
copy.Spec.CapacityReservations = append(capacityReservations, v1alpha1.CapacityReservation{
|
||||||
Replicas: amount,
|
ExpirationTime: metav1.Time{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
|
||||||
|
}
|
||||||
|
|
||||||
|
autoscaler.Log.Info(
|
||||||
|
"Patching hra for capacityReservations update",
|
||||||
|
"before", target.HorizontalRunnerAutoscaler.Spec.CapacityReservations,
|
||||||
|
"after", copy.Spec.CapacityReservations,
|
||||||
|
)
|
||||||
|
|
||||||
if err := autoscaler.Client.Patch(ctx, copy, client.MergeFrom(&target.HorizontalRunnerAutoscaler)); err != nil {
|
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 fmt.Errorf("patching horizontalrunnerautoscaler to add capacity reservation: %w", err)
|
||||||
@@ -443,20 +825,69 @@ func (autoscaler *HorizontalRunnerAutoscalerGitHubWebhook) SetupWithManager(mgr
|
|||||||
|
|
||||||
autoscaler.Recorder = mgr.GetEventRecorderFor(name)
|
autoscaler.Recorder = mgr.GetEventRecorderFor(name)
|
||||||
|
|
||||||
if err := mgr.GetFieldIndexer().IndexField(&v1alpha1.HorizontalRunnerAutoscaler{}, scaleTargetKey, func(rawObj runtime.Object) []string {
|
if err := mgr.GetFieldIndexer().IndexField(context.TODO(), &v1alpha1.HorizontalRunnerAutoscaler{}, scaleTargetKey, func(rawObj client.Object) []string {
|
||||||
hra := rawObj.(*v1alpha1.HorizontalRunnerAutoscaler)
|
hra := rawObj.(*v1alpha1.HorizontalRunnerAutoscaler)
|
||||||
|
|
||||||
if hra.Spec.ScaleTargetRef.Name == "" {
|
if hra.Spec.ScaleTargetRef.Name == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var rd v1alpha1.RunnerDeployment
|
switch hra.Spec.ScaleTargetRef.Kind {
|
||||||
|
case "", "RunnerDeployment":
|
||||||
|
var rd v1alpha1.RunnerDeployment
|
||||||
|
if err := autoscaler.Client.Get(context.Background(), types.NamespacedName{Namespace: hra.Namespace, Name: hra.Spec.ScaleTargetRef.Name}, &rd); err != nil {
|
||||||
|
autoscaler.Log.V(1).Info(fmt.Sprintf("RunnerDeployment not found with scale target ref name %s for hra %s", hra.Spec.ScaleTargetRef.Name, hra.Name))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if err := autoscaler.Client.Get(context.Background(), types.NamespacedName{Namespace: hra.Namespace, Name: hra.Spec.ScaleTargetRef.Name}, &rd); err != nil {
|
keys := []string{}
|
||||||
return nil
|
if rd.Spec.Template.Spec.Repository != "" {
|
||||||
|
keys = append(keys, rd.Spec.Template.Spec.Repository) // Repository runners
|
||||||
|
}
|
||||||
|
if rd.Spec.Template.Spec.Organization != "" {
|
||||||
|
if group := rd.Spec.Template.Spec.Group; group != "" {
|
||||||
|
keys = append(keys, organizationalRunnerGroupKey(rd.Spec.Template.Spec.Organization, rd.Spec.Template.Spec.Group)) // Organization runner groups
|
||||||
|
} else {
|
||||||
|
keys = append(keys, rd.Spec.Template.Spec.Organization) // Organization runners
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if enterprise := rd.Spec.Template.Spec.Enterprise; enterprise != "" {
|
||||||
|
if group := rd.Spec.Template.Spec.Group; group != "" {
|
||||||
|
keys = append(keys, enterpriseRunnerGroupKey(enterprise, rd.Spec.Template.Spec.Group)) // Enterprise runner groups
|
||||||
|
} else {
|
||||||
|
keys = append(keys, enterpriseKey(enterprise)) // Enterprise runners
|
||||||
|
}
|
||||||
|
}
|
||||||
|
autoscaler.Log.V(2).Info(fmt.Sprintf("HRA keys indexed for HRA %s: %v", hra.Name, keys))
|
||||||
|
return keys
|
||||||
|
case "RunnerSet":
|
||||||
|
var rs v1alpha1.RunnerSet
|
||||||
|
if err := autoscaler.Client.Get(context.Background(), types.NamespacedName{Namespace: hra.Namespace, Name: hra.Spec.ScaleTargetRef.Name}, &rs); err != nil {
|
||||||
|
autoscaler.Log.V(1).Info(fmt.Sprintf("RunnerSet not found with scale target ref name %s for hra %s", hra.Spec.ScaleTargetRef.Name, hra.Name))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
keys := []string{}
|
||||||
|
if rs.Spec.Repository != "" {
|
||||||
|
keys = append(keys, rs.Spec.Repository) // Repository runners
|
||||||
|
}
|
||||||
|
if rs.Spec.Organization != "" {
|
||||||
|
keys = append(keys, rs.Spec.Organization) // Organization runners
|
||||||
|
if group := rs.Spec.Group; group != "" {
|
||||||
|
keys = append(keys, organizationalRunnerGroupKey(rs.Spec.Organization, rs.Spec.Group)) // Organization runner groups
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if enterprise := rs.Spec.Enterprise; enterprise != "" {
|
||||||
|
keys = append(keys, enterpriseKey(enterprise)) // Enterprise runners
|
||||||
|
if group := rs.Spec.Group; group != "" {
|
||||||
|
keys = append(keys, enterpriseRunnerGroupKey(enterprise, rs.Spec.Group)) // Enterprise runner groups
|
||||||
|
}
|
||||||
|
}
|
||||||
|
autoscaler.Log.V(2).Info(fmt.Sprintf("HRA keys indexed for HRA %s: %v", hra.Name, keys))
|
||||||
|
return keys
|
||||||
}
|
}
|
||||||
|
|
||||||
return []string{rd.Spec.Template.Spec.Repository, rd.Spec.Template.Spec.Organization}
|
return nil
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -466,3 +897,15 @@ func (autoscaler *HorizontalRunnerAutoscalerGitHubWebhook) SetupWithManager(mgr
|
|||||||
Named(name).
|
Named(name).
|
||||||
Complete(autoscaler)
|
Complete(autoscaler)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func enterpriseKey(name string) string {
|
||||||
|
return keyPrefixEnterprise + name
|
||||||
|
}
|
||||||
|
|
||||||
|
func organizationalRunnerGroupKey(owner, group string) string {
|
||||||
|
return owner + keyRunnerGroup + group
|
||||||
|
}
|
||||||
|
|
||||||
|
func enterpriseRunnerGroupKey(enterprise, group string) string {
|
||||||
|
return keyPrefixEnterprise + enterprise + keyRunnerGroup + group
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/google/go-github/v33/github"
|
"github.com/actions-runner-controller/actions-runner-controller/api/v1alpha1"
|
||||||
"github.com/summerwind/actions-runner-controller/api/v1alpha1"
|
"github.com/actions-runner-controller/actions-runner-controller/pkg/actionsglob"
|
||||||
"github.com/summerwind/actions-runner-controller/pkg/actionsglob"
|
"github.com/google/go-github/v39/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 {
|
||||||
@@ -38,6 +38,16 @@ func (autoscaler *HorizontalRunnerAutoscalerGitHubWebhook) MatchCheckRunEvent(ev
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(scaleUpTrigger.GitHubEvent.CheckRun.Repositories) > 0 {
|
||||||
|
for _, repository := range scaleUpTrigger.GitHubEvent.CheckRun.Repositories {
|
||||||
|
if repository == *event.Repo.Name {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/google/go-github/v33/github"
|
"github.com/actions-runner-controller/actions-runner-controller/api/v1alpha1"
|
||||||
"github.com/summerwind/actions-runner-controller/api/v1alpha1"
|
"github.com/google/go-github/v39/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 {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/google/go-github/v33/github"
|
"github.com/actions-runner-controller/actions-runner-controller/api/v1alpha1"
|
||||||
"github.com/summerwind/actions-runner-controller/api/v1alpha1"
|
"github.com/google/go-github/v39/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 {
|
||||||
|
|||||||
@@ -4,21 +4,22 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/go-logr/logr"
|
|
||||||
"github.com/google/go-github/v33/github"
|
|
||||||
actionsv1alpha1 "github.com/summerwind/actions-runner-controller/api/v1alpha1"
|
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
|
||||||
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
actionsv1alpha1 "github.com/actions-runner-controller/actions-runner-controller/api/v1alpha1"
|
||||||
|
"github.com/go-logr/logr"
|
||||||
|
"github.com/google/go-github/v39/github"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -113,6 +114,284 @@ func TestWebhookPing(t *testing.T) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWebhookWorkflowJob(t *testing.T) {
|
||||||
|
setupTest := func() github.WorkflowJobEvent {
|
||||||
|
f, err := os.Open("testdata/org_webhook_workflow_job_payload.json")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("could not open the fixture: %s", err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
var e github.WorkflowJobEvent
|
||||||
|
if err := json.NewDecoder(f).Decode(&e); err != nil {
|
||||||
|
t.Fatalf("invalid json: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
t.Run("Successful", func(t *testing.T) {
|
||||||
|
e := setupTest()
|
||||||
|
hra := &actionsv1alpha1.HorizontalRunnerAutoscaler{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "test-name",
|
||||||
|
},
|
||||||
|
Spec: actionsv1alpha1.HorizontalRunnerAutoscalerSpec{
|
||||||
|
ScaleTargetRef: actionsv1alpha1.ScaleTargetRef{
|
||||||
|
Name: "test-name",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
rd := &actionsv1alpha1.RunnerDeployment{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "test-name",
|
||||||
|
},
|
||||||
|
Spec: actionsv1alpha1.RunnerDeploymentSpec{
|
||||||
|
Template: actionsv1alpha1.RunnerTemplate{
|
||||||
|
Spec: actionsv1alpha1.RunnerSpec{
|
||||||
|
RunnerConfig: actionsv1alpha1.RunnerConfig{
|
||||||
|
Organization: "MYORG",
|
||||||
|
Labels: []string{"label1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
initObjs := []runtime.Object{hra, rd}
|
||||||
|
|
||||||
|
testServerWithInitObjs(t,
|
||||||
|
"workflow_job",
|
||||||
|
&e,
|
||||||
|
200,
|
||||||
|
"scaled test-name by 1",
|
||||||
|
initObjs,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
t.Run("WrongLabels", func(t *testing.T) {
|
||||||
|
e := setupTest()
|
||||||
|
hra := &actionsv1alpha1.HorizontalRunnerAutoscaler{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "test-name",
|
||||||
|
},
|
||||||
|
Spec: actionsv1alpha1.HorizontalRunnerAutoscalerSpec{
|
||||||
|
ScaleTargetRef: actionsv1alpha1.ScaleTargetRef{
|
||||||
|
Name: "test-name",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
rd := &actionsv1alpha1.RunnerDeployment{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "test-name",
|
||||||
|
},
|
||||||
|
Spec: actionsv1alpha1.RunnerDeploymentSpec{
|
||||||
|
Template: actionsv1alpha1.RunnerTemplate{
|
||||||
|
Spec: actionsv1alpha1.RunnerSpec{
|
||||||
|
RunnerConfig: actionsv1alpha1.RunnerConfig{
|
||||||
|
Organization: "MYORG",
|
||||||
|
Labels: []string{"bad-label"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
initObjs := []runtime.Object{hra, rd}
|
||||||
|
|
||||||
|
testServerWithInitObjs(t,
|
||||||
|
"workflow_job",
|
||||||
|
&e,
|
||||||
|
200,
|
||||||
|
"no horizontalrunnerautoscaler to scale for this github event",
|
||||||
|
initObjs,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
// This test verifies that the old way of matching labels doesn't work anymore
|
||||||
|
t.Run("OldLabels", func(t *testing.T) {
|
||||||
|
e := setupTest()
|
||||||
|
hra := &actionsv1alpha1.HorizontalRunnerAutoscaler{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "test-name",
|
||||||
|
},
|
||||||
|
Spec: actionsv1alpha1.HorizontalRunnerAutoscalerSpec{
|
||||||
|
ScaleTargetRef: actionsv1alpha1.ScaleTargetRef{
|
||||||
|
Name: "test-name",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
rd := &actionsv1alpha1.RunnerDeployment{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "test-name",
|
||||||
|
},
|
||||||
|
Spec: actionsv1alpha1.RunnerDeploymentSpec{
|
||||||
|
Template: actionsv1alpha1.RunnerTemplate{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Labels: map[string]string{
|
||||||
|
"label1": "label1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Spec: actionsv1alpha1.RunnerSpec{
|
||||||
|
RunnerConfig: actionsv1alpha1.RunnerConfig{
|
||||||
|
Organization: "MYORG",
|
||||||
|
Labels: []string{"bad-label"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
initObjs := []runtime.Object{hra, rd}
|
||||||
|
|
||||||
|
testServerWithInitObjs(t,
|
||||||
|
"workflow_job",
|
||||||
|
&e,
|
||||||
|
200,
|
||||||
|
"no horizontalrunnerautoscaler to scale for this github event",
|
||||||
|
initObjs,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWebhookWorkflowJobWithSelfHostedLabel(t *testing.T) {
|
||||||
|
setupTest := func() github.WorkflowJobEvent {
|
||||||
|
f, err := os.Open("testdata/org_webhook_workflow_job_with_self_hosted_label_payload.json")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("could not open the fixture: %s", err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
var e github.WorkflowJobEvent
|
||||||
|
if err := json.NewDecoder(f).Decode(&e); err != nil {
|
||||||
|
t.Fatalf("invalid json: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
t.Run("Successful", func(t *testing.T) {
|
||||||
|
e := setupTest()
|
||||||
|
hra := &actionsv1alpha1.HorizontalRunnerAutoscaler{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "test-name",
|
||||||
|
},
|
||||||
|
Spec: actionsv1alpha1.HorizontalRunnerAutoscalerSpec{
|
||||||
|
ScaleTargetRef: actionsv1alpha1.ScaleTargetRef{
|
||||||
|
Name: "test-name",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
rd := &actionsv1alpha1.RunnerDeployment{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "test-name",
|
||||||
|
},
|
||||||
|
Spec: actionsv1alpha1.RunnerDeploymentSpec{
|
||||||
|
Template: actionsv1alpha1.RunnerTemplate{
|
||||||
|
Spec: actionsv1alpha1.RunnerSpec{
|
||||||
|
RunnerConfig: actionsv1alpha1.RunnerConfig{
|
||||||
|
Organization: "MYORG",
|
||||||
|
Labels: []string{"label1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
initObjs := []runtime.Object{hra, rd}
|
||||||
|
|
||||||
|
testServerWithInitObjs(t,
|
||||||
|
"workflow_job",
|
||||||
|
&e,
|
||||||
|
200,
|
||||||
|
"scaled test-name by 1",
|
||||||
|
initObjs,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
t.Run("WrongLabels", func(t *testing.T) {
|
||||||
|
e := setupTest()
|
||||||
|
hra := &actionsv1alpha1.HorizontalRunnerAutoscaler{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "test-name",
|
||||||
|
},
|
||||||
|
Spec: actionsv1alpha1.HorizontalRunnerAutoscalerSpec{
|
||||||
|
ScaleTargetRef: actionsv1alpha1.ScaleTargetRef{
|
||||||
|
Name: "test-name",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
rd := &actionsv1alpha1.RunnerDeployment{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "test-name",
|
||||||
|
},
|
||||||
|
Spec: actionsv1alpha1.RunnerDeploymentSpec{
|
||||||
|
Template: actionsv1alpha1.RunnerTemplate{
|
||||||
|
Spec: actionsv1alpha1.RunnerSpec{
|
||||||
|
RunnerConfig: actionsv1alpha1.RunnerConfig{
|
||||||
|
Organization: "MYORG",
|
||||||
|
Labels: []string{"bad-label"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
initObjs := []runtime.Object{hra, rd}
|
||||||
|
|
||||||
|
testServerWithInitObjs(t,
|
||||||
|
"workflow_job",
|
||||||
|
&e,
|
||||||
|
200,
|
||||||
|
"no horizontalrunnerautoscaler to scale for this github event",
|
||||||
|
initObjs,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
// This test verifies that the old way of matching labels doesn't work anymore
|
||||||
|
t.Run("OldLabels", func(t *testing.T) {
|
||||||
|
e := setupTest()
|
||||||
|
hra := &actionsv1alpha1.HorizontalRunnerAutoscaler{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "test-name",
|
||||||
|
},
|
||||||
|
Spec: actionsv1alpha1.HorizontalRunnerAutoscalerSpec{
|
||||||
|
ScaleTargetRef: actionsv1alpha1.ScaleTargetRef{
|
||||||
|
Name: "test-name",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
rd := &actionsv1alpha1.RunnerDeployment{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "test-name",
|
||||||
|
},
|
||||||
|
Spec: actionsv1alpha1.RunnerDeploymentSpec{
|
||||||
|
Template: actionsv1alpha1.RunnerTemplate{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Labels: map[string]string{
|
||||||
|
"label1": "label1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Spec: actionsv1alpha1.RunnerSpec{
|
||||||
|
RunnerConfig: actionsv1alpha1.RunnerConfig{
|
||||||
|
Organization: "MYORG",
|
||||||
|
Labels: []string{"bad-label"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
initObjs := []runtime.Object{hra, rd}
|
||||||
|
|
||||||
|
testServerWithInitObjs(t,
|
||||||
|
"workflow_job",
|
||||||
|
&e,
|
||||||
|
200,
|
||||||
|
"no horizontalrunnerautoscaler to scale for this github event",
|
||||||
|
initObjs,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetRequest(t *testing.T) {
|
func TestGetRequest(t *testing.T) {
|
||||||
hra := HorizontalRunnerAutoscalerGitHubWebhook{}
|
hra := HorizontalRunnerAutoscalerGitHubWebhook{}
|
||||||
request, _ := http.NewRequest(http.MethodGet, "/", nil)
|
request, _ := http.NewRequest(http.MethodGet, "/", nil)
|
||||||
@@ -166,23 +445,23 @@ func TestGetValidCapacityReservations(t *testing.T) {
|
|||||||
func installTestLogger(webhook *HorizontalRunnerAutoscalerGitHubWebhook) *bytes.Buffer {
|
func installTestLogger(webhook *HorizontalRunnerAutoscalerGitHubWebhook) *bytes.Buffer {
|
||||||
logs := &bytes.Buffer{}
|
logs := &bytes.Buffer{}
|
||||||
|
|
||||||
log := testLogger{
|
sink := &testLogSink{
|
||||||
name: "testlog",
|
name: "testlog",
|
||||||
writer: logs,
|
writer: logs,
|
||||||
}
|
}
|
||||||
|
|
||||||
webhook.Log = &log
|
log := logr.New(sink)
|
||||||
|
|
||||||
|
webhook.Log = log
|
||||||
|
|
||||||
return logs
|
return logs
|
||||||
}
|
}
|
||||||
|
|
||||||
func testServer(t *testing.T, eventType string, event interface{}, wantCode int, wantBody string) {
|
func testServerWithInitObjs(t *testing.T, eventType string, event interface{}, wantCode int, wantBody string, initObjs []runtime.Object) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
hraWebhook := &HorizontalRunnerAutoscalerGitHubWebhook{}
|
hraWebhook := &HorizontalRunnerAutoscalerGitHubWebhook{}
|
||||||
|
|
||||||
var initObjs []runtime.Object
|
|
||||||
|
|
||||||
client := fake.NewFakeClientWithScheme(sc, initObjs...)
|
client := fake.NewFakeClientWithScheme(sc, initObjs...)
|
||||||
|
|
||||||
logs := installTestLogger(hraWebhook)
|
logs := installTestLogger(hraWebhook)
|
||||||
@@ -226,6 +505,11 @@ func testServer(t *testing.T, eventType string, event interface{}, wantCode int,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testServer(t *testing.T, eventType string, event interface{}, wantCode int, wantBody string) {
|
||||||
|
var initObjs []runtime.Object
|
||||||
|
testServerWithInitObjs(t, eventType, event, wantCode, wantBody, initObjs)
|
||||||
|
}
|
||||||
|
|
||||||
func sendWebhook(server *httptest.Server, eventType string, event interface{}) (*http.Response, error) {
|
func sendWebhook(server *httptest.Server, eventType string, event interface{}) (*http.Response, error) {
|
||||||
jsonBuf := &bytes.Buffer{}
|
jsonBuf := &bytes.Buffer{}
|
||||||
enc := json.NewEncoder(jsonBuf)
|
enc := json.NewEncoder(jsonBuf)
|
||||||
@@ -255,18 +539,22 @@ func sendWebhook(server *httptest.Server, eventType string, event interface{}) (
|
|||||||
return http.DefaultClient.Do(req)
|
return http.DefaultClient.Do(req)
|
||||||
}
|
}
|
||||||
|
|
||||||
// testLogger is a sample logr.Logger that logs in-memory.
|
// testLogSink is a sample logr.Logger that logs in-memory.
|
||||||
// It's only for testing log outputs.
|
// It's only for testing log outputs.
|
||||||
type testLogger struct {
|
type testLogSink struct {
|
||||||
name string
|
name string
|
||||||
keyValues map[string]interface{}
|
keyValues map[string]interface{}
|
||||||
|
|
||||||
writer io.Writer
|
writer io.Writer
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ logr.Logger = &testLogger{}
|
var _ logr.LogSink = &testLogSink{}
|
||||||
|
|
||||||
func (l *testLogger) Info(msg string, kvs ...interface{}) {
|
func (l *testLogSink) Init(_ logr.RuntimeInfo) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *testLogSink) Info(_ int, msg string, kvs ...interface{}) {
|
||||||
fmt.Fprintf(l.writer, "%s] %s\t", l.name, msg)
|
fmt.Fprintf(l.writer, "%s] %s\t", l.name, msg)
|
||||||
for k, v := range l.keyValues {
|
for k, v := range l.keyValues {
|
||||||
fmt.Fprintf(l.writer, "%s=%+v ", k, v)
|
fmt.Fprintf(l.writer, "%s=%+v ", k, v)
|
||||||
@@ -277,28 +565,24 @@ func (l *testLogger) Info(msg string, kvs ...interface{}) {
|
|||||||
fmt.Fprintf(l.writer, "\n")
|
fmt.Fprintf(l.writer, "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (_ *testLogger) Enabled() bool {
|
func (_ *testLogSink) Enabled(level int) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *testLogger) Error(err error, msg string, kvs ...interface{}) {
|
func (l *testLogSink) Error(err error, msg string, kvs ...interface{}) {
|
||||||
kvs = append(kvs, "error", err)
|
kvs = append(kvs, "error", err)
|
||||||
l.Info(msg, kvs...)
|
l.Info(0, msg, kvs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *testLogger) V(_ int) logr.InfoLogger {
|
func (l *testLogSink) WithName(name string) logr.LogSink {
|
||||||
return l
|
return &testLogSink{
|
||||||
}
|
|
||||||
|
|
||||||
func (l *testLogger) WithName(name string) logr.Logger {
|
|
||||||
return &testLogger{
|
|
||||||
name: l.name + "." + name,
|
name: l.name + "." + name,
|
||||||
keyValues: l.keyValues,
|
keyValues: l.keyValues,
|
||||||
writer: l.writer,
|
writer: l.writer,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *testLogger) WithValues(kvs ...interface{}) logr.Logger {
|
func (l *testLogSink) WithValues(kvs ...interface{}) logr.LogSink {
|
||||||
newMap := make(map[string]interface{}, len(l.keyValues)+len(kvs)/2)
|
newMap := make(map[string]interface{}, len(l.keyValues)+len(kvs)/2)
|
||||||
for k, v := range l.keyValues {
|
for k, v := range l.keyValues {
|
||||||
newMap[k] = v
|
newMap[k] = v
|
||||||
@@ -306,7 +590,7 @@ func (l *testLogger) WithValues(kvs ...interface{}) logr.Logger {
|
|||||||
for i := 0; i < len(kvs); i += 2 {
|
for i := 0; i < len(kvs); i += 2 {
|
||||||
newMap[kvs[i].(string)] = kvs[i+1]
|
newMap[kvs[i].(string)] = kvs[i+1]
|
||||||
}
|
}
|
||||||
return &testLogger{
|
return &testLogSink{
|
||||||
name: l.name,
|
name: l.name,
|
||||||
keyValues: newMap,
|
keyValues: newMap,
|
||||||
writer: l.writer,
|
writer: l.writer,
|
||||||
|
|||||||
@@ -24,10 +24,11 @@ import (
|
|||||||
|
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
|
||||||
"github.com/summerwind/actions-runner-controller/github"
|
"github.com/actions-runner-controller/actions-runner-controller/github"
|
||||||
|
"github.com/go-logr/logr"
|
||||||
|
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
|
||||||
"github.com/go-logr/logr"
|
|
||||||
"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"
|
||||||
@@ -35,8 +36,8 @@ import (
|
|||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
"github.com/summerwind/actions-runner-controller/api/v1alpha1"
|
"github.com/actions-runner-controller/actions-runner-controller/api/v1alpha1"
|
||||||
"github.com/summerwind/actions-runner-controller/controllers/metrics"
|
"github.com/actions-runner-controller/actions-runner-controller/controllers/metrics"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -63,8 +64,7 @@ const defaultReplicas = 1
|
|||||||
// +kubebuilder:rbac:groups=actions.summerwind.dev,resources=horizontalrunnerautoscalers/status,verbs=get;update;patch
|
// +kubebuilder:rbac:groups=actions.summerwind.dev,resources=horizontalrunnerautoscalers/status,verbs=get;update;patch
|
||||||
// +kubebuilder:rbac:groups=core,resources=events,verbs=create;patch
|
// +kubebuilder:rbac:groups=core,resources=events,verbs=create;patch
|
||||||
|
|
||||||
func (r *HorizontalRunnerAutoscalerReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
|
func (r *HorizontalRunnerAutoscalerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||||
ctx := context.Background()
|
|
||||||
log := r.Log.WithValues("horizontalrunnerautoscaler", req.NamespacedName)
|
log := r.Log.WithValues("horizontalrunnerautoscaler", req.NamespacedName)
|
||||||
|
|
||||||
var hra v1alpha1.HorizontalRunnerAutoscaler
|
var hra v1alpha1.HorizontalRunnerAutoscaler
|
||||||
@@ -78,18 +78,181 @@ func (r *HorizontalRunnerAutoscalerReconciler) Reconcile(req ctrl.Request) (ctrl
|
|||||||
|
|
||||||
metrics.SetHorizontalRunnerAutoscalerSpec(hra.ObjectMeta, hra.Spec)
|
metrics.SetHorizontalRunnerAutoscalerSpec(hra.ObjectMeta, hra.Spec)
|
||||||
|
|
||||||
var rd v1alpha1.RunnerDeployment
|
kind := hra.Spec.ScaleTargetRef.Kind
|
||||||
if err := r.Get(ctx, types.NamespacedName{
|
|
||||||
Namespace: req.Namespace,
|
switch kind {
|
||||||
Name: hra.Spec.ScaleTargetRef.Name,
|
case "", "RunnerDeployment":
|
||||||
}, &rd); err != nil {
|
var rd v1alpha1.RunnerDeployment
|
||||||
return ctrl.Result{}, client.IgnoreNotFound(err)
|
if err := r.Get(ctx, types.NamespacedName{
|
||||||
|
Namespace: req.Namespace,
|
||||||
|
Name: hra.Spec.ScaleTargetRef.Name,
|
||||||
|
}, &rd); err != nil {
|
||||||
|
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !rd.ObjectMeta.DeletionTimestamp.IsZero() {
|
||||||
|
return ctrl.Result{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
st := r.scaleTargetFromRD(ctx, rd)
|
||||||
|
|
||||||
|
return r.reconcile(ctx, req, log, hra, st, func(newDesiredReplicas int) error {
|
||||||
|
currentDesiredReplicas := getIntOrDefault(rd.Spec.Replicas, defaultReplicas)
|
||||||
|
|
||||||
|
// Please add more conditions that we can in-place update the newest runnerreplicaset without disruption
|
||||||
|
if currentDesiredReplicas != newDesiredReplicas {
|
||||||
|
copy := rd.DeepCopy()
|
||||||
|
copy.Spec.Replicas = &newDesiredReplicas
|
||||||
|
|
||||||
|
if err := r.Client.Patch(ctx, copy, client.MergeFrom(&rd)); err != nil {
|
||||||
|
return fmt.Errorf("patching runnerdeployment to have %d replicas: %w", newDesiredReplicas, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
case "RunnerSet":
|
||||||
|
var rs v1alpha1.RunnerSet
|
||||||
|
if err := r.Get(ctx, types.NamespacedName{
|
||||||
|
Namespace: req.Namespace,
|
||||||
|
Name: hra.Spec.ScaleTargetRef.Name,
|
||||||
|
}, &rs); err != nil {
|
||||||
|
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !rs.ObjectMeta.DeletionTimestamp.IsZero() {
|
||||||
|
return ctrl.Result{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var replicas *int
|
||||||
|
|
||||||
|
if rs.Spec.Replicas != nil {
|
||||||
|
v := int(*rs.Spec.Replicas)
|
||||||
|
replicas = &v
|
||||||
|
}
|
||||||
|
|
||||||
|
st := scaleTarget{
|
||||||
|
st: rs.Name,
|
||||||
|
kind: "runnerset",
|
||||||
|
enterprise: rs.Spec.Enterprise,
|
||||||
|
org: rs.Spec.Organization,
|
||||||
|
repo: rs.Spec.Repository,
|
||||||
|
replicas: replicas,
|
||||||
|
getRunnerMap: func() (map[string]struct{}, error) {
|
||||||
|
// return the list of runners in namespace. Horizontal Runner Autoscaler should only be responsible for scaling resources in its own ns.
|
||||||
|
var runnerPodList corev1.PodList
|
||||||
|
|
||||||
|
var opts []client.ListOption
|
||||||
|
|
||||||
|
opts = append(opts, client.InNamespace(rs.Namespace))
|
||||||
|
|
||||||
|
selector, err := metav1.LabelSelectorAsSelector(rs.Spec.Selector)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
opts = append(opts, client.MatchingLabelsSelector{Selector: selector})
|
||||||
|
|
||||||
|
r.Log.V(2).Info("Finding runnerset's runner pods with selector", "ns", rs.Namespace)
|
||||||
|
|
||||||
|
if err := r.List(
|
||||||
|
ctx,
|
||||||
|
&runnerPodList,
|
||||||
|
opts...,
|
||||||
|
); err != nil {
|
||||||
|
if !kerrors.IsNotFound(err) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
runnerMap := make(map[string]struct{})
|
||||||
|
for _, items := range runnerPodList.Items {
|
||||||
|
runnerMap[items.Name] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return runnerMap, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.reconcile(ctx, req, log, hra, st, func(newDesiredReplicas int) error {
|
||||||
|
var replicas *int
|
||||||
|
if rs.Spec.Replicas != nil {
|
||||||
|
v := int(*rs.Spec.Replicas)
|
||||||
|
replicas = &v
|
||||||
|
}
|
||||||
|
currentDesiredReplicas := getIntOrDefault(replicas, defaultReplicas)
|
||||||
|
|
||||||
|
if currentDesiredReplicas != newDesiredReplicas {
|
||||||
|
copy := rs.DeepCopy()
|
||||||
|
v := int32(newDesiredReplicas)
|
||||||
|
copy.Spec.Replicas = &v
|
||||||
|
|
||||||
|
if err := r.Client.Patch(ctx, copy, client.MergeFrom(&rs)); err != nil {
|
||||||
|
return fmt.Errorf("patching runnerset to have %d replicas: %w", newDesiredReplicas, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if !rd.ObjectMeta.DeletionTimestamp.IsZero() {
|
log.Info(fmt.Sprintf("Unsupported scale target %s %s: kind %s is not supported. valid kinds are %s and %s", kind, hra.Spec.ScaleTargetRef.Name, kind, "RunnerDeployment", "RunnerSet"))
|
||||||
return ctrl.Result{}, nil
|
|
||||||
|
return ctrl.Result{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HorizontalRunnerAutoscalerReconciler) scaleTargetFromRD(ctx context.Context, rd v1alpha1.RunnerDeployment) scaleTarget {
|
||||||
|
st := scaleTarget{
|
||||||
|
st: rd.Name,
|
||||||
|
kind: "runnerdeployment",
|
||||||
|
enterprise: rd.Spec.Template.Spec.Enterprise,
|
||||||
|
org: rd.Spec.Template.Spec.Organization,
|
||||||
|
repo: rd.Spec.Template.Spec.Repository,
|
||||||
|
replicas: rd.Spec.Replicas,
|
||||||
|
getRunnerMap: func() (map[string]struct{}, error) {
|
||||||
|
// return the list of runners in namespace. Horizontal Runner Autoscaler should only be responsible for scaling resources in its own ns.
|
||||||
|
var runnerList v1alpha1.RunnerList
|
||||||
|
|
||||||
|
var opts []client.ListOption
|
||||||
|
|
||||||
|
opts = append(opts, client.InNamespace(rd.Namespace))
|
||||||
|
|
||||||
|
selector, err := metav1.LabelSelectorAsSelector(getSelector(&rd))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
opts = append(opts, client.MatchingLabelsSelector{Selector: selector})
|
||||||
|
|
||||||
|
r.Log.V(2).Info("Finding runners with selector", "ns", rd.Namespace)
|
||||||
|
|
||||||
|
if err := r.List(
|
||||||
|
ctx,
|
||||||
|
&runnerList,
|
||||||
|
opts...,
|
||||||
|
); err != nil {
|
||||||
|
if !kerrors.IsNotFound(err) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
runnerMap := make(map[string]struct{})
|
||||||
|
for _, items := range runnerList.Items {
|
||||||
|
runnerMap[items.Name] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return runnerMap, nil
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return st
|
||||||
|
}
|
||||||
|
|
||||||
|
type scaleTarget struct {
|
||||||
|
st, kind string
|
||||||
|
enterprise, repo, org string
|
||||||
|
replicas *int
|
||||||
|
|
||||||
|
getRunnerMap func() (map[string]struct{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HorizontalRunnerAutoscalerReconciler) reconcile(ctx context.Context, req ctrl.Request, log logr.Logger, hra v1alpha1.HorizontalRunnerAutoscaler, st scaleTarget, updatedDesiredReplicas func(int) error) (ctrl.Result, error) {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
minReplicas, active, upcoming, err := r.getMinReplicas(log, now, hra)
|
minReplicas, active, upcoming, err := r.getMinReplicas(log, now, hra)
|
||||||
@@ -99,7 +262,7 @@ func (r *HorizontalRunnerAutoscalerReconciler) Reconcile(req ctrl.Request) (ctrl
|
|||||||
return ctrl.Result{}, err
|
return ctrl.Result{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
newDesiredReplicas, computedReplicas, computedReplicasFromCache, err := r.computeReplicasWithCache(log, now, rd, hra, minReplicas)
|
newDesiredReplicas, computedReplicas, computedReplicasFromCache, err := r.computeReplicasWithCache(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())
|
||||||
|
|
||||||
@@ -108,16 +271,8 @@ func (r *HorizontalRunnerAutoscalerReconciler) Reconcile(req ctrl.Request) (ctrl
|
|||||||
return ctrl.Result{}, err
|
return ctrl.Result{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
currentDesiredReplicas := getIntOrDefault(rd.Spec.Replicas, defaultReplicas)
|
if err := updatedDesiredReplicas(newDesiredReplicas); err != nil {
|
||||||
|
return ctrl.Result{}, err
|
||||||
// Please add more conditions that we can in-place update the newest runnerreplicaset without disruption
|
|
||||||
if currentDesiredReplicas != newDesiredReplicas {
|
|
||||||
copy := rd.DeepCopy()
|
|
||||||
copy.Spec.Replicas = &newDesiredReplicas
|
|
||||||
|
|
||||||
if err := r.Client.Patch(ctx, copy, client.MergeFrom(&rd)); err != nil {
|
|
||||||
return ctrl.Result{}, fmt.Errorf("patching runnerdeployment to have %d replicas: %w", newDesiredReplicas, err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updated := hra.DeepCopy()
|
updated := hra.DeepCopy()
|
||||||
@@ -288,7 +443,7 @@ 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, rd v1alpha1.RunnerDeployment, hra v1alpha1.HorizontalRunnerAutoscaler, minReplicas int) (int, int, *int, error) {
|
func (r *HorizontalRunnerAutoscalerReconciler) computeReplicasWithCache(log logr.Logger, now time.Time, st scaleTarget, hra v1alpha1.HorizontalRunnerAutoscaler, minReplicas int) (int, int, *int, error) {
|
||||||
var suggestedReplicas int
|
var suggestedReplicas int
|
||||||
|
|
||||||
suggestedReplicasFromCache := r.fetchSuggestedReplicasFromCache(hra)
|
suggestedReplicasFromCache := r.fetchSuggestedReplicasFromCache(hra)
|
||||||
@@ -304,7 +459,7 @@ func (r *HorizontalRunnerAutoscalerReconciler) computeReplicasWithCache(log logr
|
|||||||
suggestedReplicas = *cached
|
suggestedReplicas = *cached
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
v, err := r.suggestDesiredReplicas(rd, hra)
|
v, err := r.suggestDesiredReplicas(st, hra)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, 0, nil, err
|
return 0, 0, nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/google/go-cmp/cmp"
|
|
||||||
actionsv1alpha1 "github.com/summerwind/actions-runner-controller/api/v1alpha1"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
actionsv1alpha1 "github.com/actions-runner-controller/actions-runner-controller/api/v1alpha1"
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetValidCacheEntries(t *testing.T) {
|
func TestGetValidCacheEntries(t *testing.T) {
|
||||||
|
|||||||
@@ -7,11 +7,10 @@ import (
|
|||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/go-github/v33/github"
|
github2 "github.com/actions-runner-controller/actions-runner-controller/github"
|
||||||
github2 "github.com/summerwind/actions-runner-controller/github"
|
"github.com/google/go-github/v39/github"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
|
||||||
|
|
||||||
"github.com/summerwind/actions-runner-controller/github/fake"
|
"github.com/actions-runner-controller/actions-runner-controller/github/fake"
|
||||||
|
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
@@ -24,7 +23,7 @@ import (
|
|||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
actionsv1alpha1 "github.com/summerwind/actions-runner-controller/api/v1alpha1"
|
actionsv1alpha1 "github.com/actions-runner-controller/actions-runner-controller/api/v1alpha1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type testEnvironment struct {
|
type testEnvironment struct {
|
||||||
@@ -52,8 +51,9 @@ var (
|
|||||||
// * starting all the reconcilers
|
// * starting all the reconcilers
|
||||||
// * stopping all the reconcilers after the test ends
|
// * stopping all the reconcilers after the test ends
|
||||||
// Call this function at the start of each of your tests.
|
// Call this function at the start of each of your tests.
|
||||||
func SetupIntegrationTest(ctx context.Context) *testEnvironment {
|
func SetupIntegrationTest(ctx2 context.Context) *testEnvironment {
|
||||||
var stopCh chan struct{}
|
var ctx context.Context
|
||||||
|
var cancel func()
|
||||||
ns := &corev1.Namespace{}
|
ns := &corev1.Namespace{}
|
||||||
|
|
||||||
env := &testEnvironment{
|
env := &testEnvironment{
|
||||||
@@ -63,7 +63,7 @@ func SetupIntegrationTest(ctx context.Context) *testEnvironment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
stopCh = make(chan struct{})
|
ctx, cancel = context.WithCancel(ctx2)
|
||||||
*ns = corev1.Namespace{
|
*ns = corev1.Namespace{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "testns-" + randStringRunes(5)},
|
ObjectMeta: metav1.ObjectMeta{Name: "testns-" + randStringRunes(5)},
|
||||||
}
|
}
|
||||||
@@ -166,13 +166,13 @@ func SetupIntegrationTest(ctx context.Context) *testEnvironment {
|
|||||||
go func() {
|
go func() {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
err := mgr.Start(stopCh)
|
err := mgr.Start(ctx)
|
||||||
Expect(err).NotTo(HaveOccurred(), "failed to start manager")
|
Expect(err).NotTo(HaveOccurred(), "failed to start manager")
|
||||||
}()
|
}()
|
||||||
})
|
})
|
||||||
|
|
||||||
AfterEach(func() {
|
AfterEach(func() {
|
||||||
close(stopCh)
|
defer cancel()
|
||||||
|
|
||||||
env.fakeGithubServer.Close()
|
env.fakeGithubServer.Close()
|
||||||
env.webhookServer.Close()
|
env.webhookServer.Close()
|
||||||
@@ -214,11 +214,15 @@ var _ = Context("INTEGRATION: Inside of a new namespace", func() {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Spec: actionsv1alpha1.RunnerSpec{
|
Spec: actionsv1alpha1.RunnerSpec{
|
||||||
Organization: "test",
|
RunnerConfig: actionsv1alpha1.RunnerConfig{
|
||||||
Image: "bar",
|
Organization: "test",
|
||||||
Group: "baz",
|
Image: "bar",
|
||||||
Env: []corev1.EnvVar{
|
Group: "baz",
|
||||||
{Name: "FOO", Value: "FOOVALUE"},
|
},
|
||||||
|
RunnerPodSpec: actionsv1alpha1.RunnerPodSpec{
|
||||||
|
Env: []corev1.EnvVar{
|
||||||
|
{Name: "FOO", Value: "FOOVALUE"},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -301,11 +305,15 @@ var _ = Context("INTEGRATION: Inside of a new namespace", func() {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Spec: actionsv1alpha1.RunnerSpec{
|
Spec: actionsv1alpha1.RunnerSpec{
|
||||||
Repository: "test/valid",
|
RunnerConfig: actionsv1alpha1.RunnerConfig{
|
||||||
Image: "bar",
|
Repository: "test/valid",
|
||||||
Group: "baz",
|
Image: "bar",
|
||||||
Env: []corev1.EnvVar{
|
Group: "baz",
|
||||||
{Name: "FOO", Value: "FOOVALUE"},
|
},
|
||||||
|
RunnerPodSpec: actionsv1alpha1.RunnerPodSpec{
|
||||||
|
Env: []corev1.EnvVar{
|
||||||
|
{Name: "FOO", Value: "FOOVALUE"},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -432,11 +440,15 @@ var _ = Context("INTEGRATION: Inside of a new namespace", func() {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Spec: actionsv1alpha1.RunnerSpec{
|
Spec: actionsv1alpha1.RunnerSpec{
|
||||||
Repository: "test/valid",
|
RunnerConfig: actionsv1alpha1.RunnerConfig{
|
||||||
Image: "bar",
|
Repository: "test/valid",
|
||||||
Group: "baz",
|
Image: "bar",
|
||||||
Env: []corev1.EnvVar{
|
Group: "baz",
|
||||||
{Name: "FOO", Value: "FOOVALUE"},
|
},
|
||||||
|
RunnerPodSpec: actionsv1alpha1.RunnerPodSpec{
|
||||||
|
Env: []corev1.EnvVar{
|
||||||
|
{Name: "FOO", Value: "FOOVALUE"},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -474,8 +486,9 @@ var _ = Context("INTEGRATION: Inside of a new namespace", func() {
|
|||||||
{
|
{
|
||||||
GitHubEvent: &actionsv1alpha1.GitHubEventScaleUpTriggerSpec{
|
GitHubEvent: &actionsv1alpha1.GitHubEventScaleUpTriggerSpec{
|
||||||
CheckRun: &actionsv1alpha1.CheckRunSpec{
|
CheckRun: &actionsv1alpha1.CheckRunSpec{
|
||||||
Types: []string{"created"},
|
Types: []string{"created"},
|
||||||
Status: "pending",
|
Status: "pending",
|
||||||
|
Repositories: []string{"valid", "foo", "bar"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Amount: 1,
|
Amount: 1,
|
||||||
@@ -505,12 +518,23 @@ var _ = Context("INTEGRATION: Inside of a new namespace", func() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Scale-up to 5 replicas on second check_run create webhook event
|
// Scale-up to 5 replicas on second check_run create webhook event
|
||||||
|
replicasAfterSecondWebhook := 5
|
||||||
{
|
{
|
||||||
env.SendOrgCheckRunEvent("test", "valid", "pending", "created")
|
env.SendOrgCheckRunEvent("test", "valid", "pending", "created")
|
||||||
ExpectRunnerSetsManagedReplicasCountEventuallyEquals(ctx, ns.Name, 5, "runners after second webhook event")
|
ExpectRunnerSetsManagedReplicasCountEventuallyEquals(ctx, ns.Name, replicasAfterSecondWebhook, "runners after second webhook event")
|
||||||
env.ExpectRegisteredNumberCountEventuallyEquals(5, "count of fake list runners")
|
env.ExpectRegisteredNumberCountEventuallyEquals(replicasAfterSecondWebhook, "count of fake list runners")
|
||||||
env.SyncRunnerRegistrations()
|
env.SyncRunnerRegistrations()
|
||||||
ExpectRunnerCountEventuallyEquals(ctx, ns.Name, 5)
|
ExpectRunnerCountEventuallyEquals(ctx, ns.Name, replicasAfterSecondWebhook)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not scale-up on third check_run create webhook event
|
||||||
|
// example repo is not in specified in actionsv1alpha1.CheckRunSpec.Repositories
|
||||||
|
{
|
||||||
|
env.SendOrgCheckRunEvent("test", "example", "pending", "created")
|
||||||
|
ExpectRunnerSetsManagedReplicasCountEventuallyEquals(ctx, ns.Name, replicasAfterSecondWebhook, "runners after third webhook event")
|
||||||
|
env.ExpectRegisteredNumberCountEventuallyEquals(replicasAfterSecondWebhook, "count of fake list runners")
|
||||||
|
env.SyncRunnerRegistrations()
|
||||||
|
ExpectRunnerCountEventuallyEquals(ctx, ns.Name, replicasAfterSecondWebhook)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -537,11 +561,15 @@ var _ = Context("INTEGRATION: Inside of a new namespace", func() {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Spec: actionsv1alpha1.RunnerSpec{
|
Spec: actionsv1alpha1.RunnerSpec{
|
||||||
Repository: "test/valid",
|
RunnerConfig: actionsv1alpha1.RunnerConfig{
|
||||||
Image: "bar",
|
Repository: "test/valid",
|
||||||
Group: "baz",
|
Image: "bar",
|
||||||
Env: []corev1.EnvVar{
|
Group: "baz",
|
||||||
{Name: "FOO", Value: "FOOVALUE"},
|
},
|
||||||
|
RunnerPodSpec: actionsv1alpha1.RunnerPodSpec{
|
||||||
|
Env: []corev1.EnvVar{
|
||||||
|
{Name: "FOO", Value: "FOOVALUE"},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -631,11 +659,15 @@ var _ = Context("INTEGRATION: Inside of a new namespace", func() {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Spec: actionsv1alpha1.RunnerSpec{
|
Spec: actionsv1alpha1.RunnerSpec{
|
||||||
Repository: "test/valid",
|
RunnerConfig: actionsv1alpha1.RunnerConfig{
|
||||||
Image: "bar",
|
Repository: "test/valid",
|
||||||
Group: "baz",
|
Image: "bar",
|
||||||
Env: []corev1.EnvVar{
|
Group: "baz",
|
||||||
{Name: "FOO", Value: "FOOVALUE"},
|
},
|
||||||
|
RunnerPodSpec: actionsv1alpha1.RunnerPodSpec{
|
||||||
|
Env: []corev1.EnvVar{
|
||||||
|
{Name: "FOO", Value: "FOOVALUE"},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -764,11 +796,15 @@ var _ = Context("INTEGRATION: Inside of a new namespace", func() {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Spec: actionsv1alpha1.RunnerSpec{
|
Spec: actionsv1alpha1.RunnerSpec{
|
||||||
Repository: "test/valid",
|
RunnerConfig: actionsv1alpha1.RunnerConfig{
|
||||||
Image: "bar",
|
Repository: "test/valid",
|
||||||
Group: "baz",
|
Image: "bar",
|
||||||
Env: []corev1.EnvVar{
|
Group: "baz",
|
||||||
{Name: "FOO", Value: "FOOVALUE"},
|
},
|
||||||
|
RunnerPodSpec: actionsv1alpha1.RunnerPodSpec{
|
||||||
|
Env: []corev1.EnvVar{
|
||||||
|
{Name: "FOO", Value: "FOOVALUE"},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -857,11 +893,15 @@ var _ = Context("INTEGRATION: Inside of a new namespace", func() {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Spec: actionsv1alpha1.RunnerSpec{
|
Spec: actionsv1alpha1.RunnerSpec{
|
||||||
Repository: "test/valid",
|
RunnerConfig: actionsv1alpha1.RunnerConfig{
|
||||||
Image: "bar",
|
Repository: "test/valid",
|
||||||
Group: "baz",
|
Image: "bar",
|
||||||
Env: []corev1.EnvVar{
|
Group: "baz",
|
||||||
{Name: "FOO", Value: "FOOVALUE"},
|
},
|
||||||
|
RunnerPodSpec: actionsv1alpha1.RunnerPodSpec{
|
||||||
|
Env: []corev1.EnvVar{
|
||||||
|
{Name: "FOO", Value: "FOOVALUE"},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -956,11 +996,15 @@ var _ = Context("INTEGRATION: Inside of a new namespace", func() {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Spec: actionsv1alpha1.RunnerSpec{
|
Spec: actionsv1alpha1.RunnerSpec{
|
||||||
Repository: "test/valid",
|
RunnerConfig: actionsv1alpha1.RunnerConfig{
|
||||||
Image: "bar",
|
Repository: "test/valid",
|
||||||
Group: "baz",
|
Image: "bar",
|
||||||
Env: []corev1.EnvVar{
|
Group: "baz",
|
||||||
{Name: "FOO", Value: "FOOVALUE"},
|
},
|
||||||
|
RunnerPodSpec: actionsv1alpha1.RunnerPodSpec{
|
||||||
|
Env: []corev1.EnvVar{
|
||||||
|
{Name: "FOO", Value: "FOOVALUE"},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -1033,6 +1077,169 @@ var _ = Context("INTEGRATION: Inside of a new namespace", func() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("should be able to scale visible organization runner group with default labels", func() {
|
||||||
|
name := "example-runnerdeploy"
|
||||||
|
|
||||||
|
{
|
||||||
|
rd := &actionsv1alpha1.RunnerDeployment{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: name,
|
||||||
|
Namespace: ns.Name,
|
||||||
|
},
|
||||||
|
Spec: actionsv1alpha1.RunnerDeploymentSpec{
|
||||||
|
Replicas: intPtr(1),
|
||||||
|
Selector: &metav1.LabelSelector{
|
||||||
|
MatchLabels: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Template: actionsv1alpha1.RunnerTemplate{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Labels: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Spec: actionsv1alpha1.RunnerSpec{
|
||||||
|
RunnerConfig: actionsv1alpha1.RunnerConfig{
|
||||||
|
Repository: "test/valid",
|
||||||
|
Image: "bar",
|
||||||
|
Group: "baz",
|
||||||
|
},
|
||||||
|
RunnerPodSpec: actionsv1alpha1.RunnerPodSpec{
|
||||||
|
Env: []corev1.EnvVar{
|
||||||
|
{Name: "FOO", Value: "FOOVALUE"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
ExpectCreate(ctx, rd, "test RunnerDeployment")
|
||||||
|
|
||||||
|
hra := &actionsv1alpha1.HorizontalRunnerAutoscaler{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: name,
|
||||||
|
Namespace: ns.Name,
|
||||||
|
},
|
||||||
|
Spec: actionsv1alpha1.HorizontalRunnerAutoscalerSpec{
|
||||||
|
ScaleTargetRef: actionsv1alpha1.ScaleTargetRef{
|
||||||
|
Name: name,
|
||||||
|
},
|
||||||
|
MinReplicas: intPtr(1),
|
||||||
|
MaxReplicas: intPtr(5),
|
||||||
|
ScaleDownDelaySecondsAfterScaleUp: intPtr(1),
|
||||||
|
ScaleUpTriggers: []actionsv1alpha1.ScaleUpTrigger{
|
||||||
|
{
|
||||||
|
GitHubEvent: &actionsv1alpha1.GitHubEventScaleUpTriggerSpec{},
|
||||||
|
Amount: 1,
|
||||||
|
Duration: metav1.Duration{Duration: time.Minute},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
ExpectCreate(ctx, hra, "test HorizontalRunnerAutoscaler")
|
||||||
|
|
||||||
|
ExpectRunnerSetsCountEventuallyEquals(ctx, ns.Name, 1)
|
||||||
|
ExpectRunnerSetsManagedReplicasCountEventuallyEquals(ctx, ns.Name, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
env.ExpectRegisteredNumberCountEventuallyEquals(1, "count of fake list runners")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scale-up to 2 replicas on first workflow_job webhook event
|
||||||
|
{
|
||||||
|
env.SendWorkflowJobEvent("test", "valid", "pending", "created", []string{"self-hosted"})
|
||||||
|
ExpectRunnerSetsCountEventuallyEquals(ctx, ns.Name, 1, "runner sets after webhook")
|
||||||
|
ExpectRunnerSetsManagedReplicasCountEventuallyEquals(ctx, ns.Name, 2, "runners after first webhook event")
|
||||||
|
env.ExpectRegisteredNumberCountEventuallyEquals(2, "count of fake list runners")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
It("should be able to scale visible organization runner group with custom labels", func() {
|
||||||
|
name := "example-runnerdeploy"
|
||||||
|
|
||||||
|
{
|
||||||
|
rd := &actionsv1alpha1.RunnerDeployment{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: name,
|
||||||
|
Namespace: ns.Name,
|
||||||
|
},
|
||||||
|
Spec: actionsv1alpha1.RunnerDeploymentSpec{
|
||||||
|
Replicas: intPtr(1),
|
||||||
|
Selector: &metav1.LabelSelector{
|
||||||
|
MatchLabels: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Template: actionsv1alpha1.RunnerTemplate{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Labels: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Spec: actionsv1alpha1.RunnerSpec{
|
||||||
|
RunnerConfig: actionsv1alpha1.RunnerConfig{
|
||||||
|
Repository: "test/valid",
|
||||||
|
Image: "bar",
|
||||||
|
Group: "baz",
|
||||||
|
Labels: []string{"custom-label"},
|
||||||
|
},
|
||||||
|
RunnerPodSpec: actionsv1alpha1.RunnerPodSpec{
|
||||||
|
Env: []corev1.EnvVar{
|
||||||
|
{Name: "FOO", Value: "FOOVALUE"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
ExpectCreate(ctx, rd, "test RunnerDeployment")
|
||||||
|
|
||||||
|
hra := &actionsv1alpha1.HorizontalRunnerAutoscaler{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: name,
|
||||||
|
Namespace: ns.Name,
|
||||||
|
},
|
||||||
|
Spec: actionsv1alpha1.HorizontalRunnerAutoscalerSpec{
|
||||||
|
ScaleTargetRef: actionsv1alpha1.ScaleTargetRef{
|
||||||
|
Name: name,
|
||||||
|
},
|
||||||
|
MinReplicas: intPtr(1),
|
||||||
|
MaxReplicas: intPtr(5),
|
||||||
|
ScaleDownDelaySecondsAfterScaleUp: intPtr(1),
|
||||||
|
ScaleUpTriggers: []actionsv1alpha1.ScaleUpTrigger{
|
||||||
|
{
|
||||||
|
GitHubEvent: &actionsv1alpha1.GitHubEventScaleUpTriggerSpec{},
|
||||||
|
Amount: 1,
|
||||||
|
Duration: metav1.Duration{Duration: time.Minute},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
ExpectCreate(ctx, hra, "test HorizontalRunnerAutoscaler")
|
||||||
|
|
||||||
|
ExpectRunnerSetsCountEventuallyEquals(ctx, ns.Name, 1)
|
||||||
|
ExpectRunnerSetsManagedReplicasCountEventuallyEquals(ctx, ns.Name, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
env.ExpectRegisteredNumberCountEventuallyEquals(1, "count of fake list runners")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scale-up to 2 replicas on first workflow_job webhook event
|
||||||
|
{
|
||||||
|
env.SendWorkflowJobEvent("test", "valid", "pending", "created", []string{"custom-label"})
|
||||||
|
ExpectRunnerSetsCountEventuallyEquals(ctx, ns.Name, 1, "runner sets after webhook")
|
||||||
|
ExpectRunnerSetsManagedReplicasCountEventuallyEquals(ctx, ns.Name, 2, "runners after first webhook event")
|
||||||
|
env.ExpectRegisteredNumberCountEventuallyEquals(2, "count of fake list runners")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -1164,6 +1371,28 @@ func (env *testEnvironment) SendUserCheckRunEvent(owner, repo, status, action st
|
|||||||
ExpectWithOffset(1, resp.StatusCode).To(Equal(200))
|
ExpectWithOffset(1, resp.StatusCode).To(Equal(200))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (env *testEnvironment) SendWorkflowJobEvent(owner, repo, status, action string, labels []string) {
|
||||||
|
resp, err := sendWebhook(env.webhookServer, "workflow_job", &github.WorkflowJobEvent{
|
||||||
|
Org: &github.Organization{
|
||||||
|
Name: github.String(owner),
|
||||||
|
},
|
||||||
|
WorkflowJob: &github.WorkflowJob{
|
||||||
|
Labels: labels,
|
||||||
|
},
|
||||||
|
Action: github.String("queued"),
|
||||||
|
Repo: &github.Repository{
|
||||||
|
Name: github.String(repo),
|
||||||
|
Owner: &github.User{
|
||||||
|
Login: github.String(owner),
|
||||||
|
Type: github.String("Organization"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
ExpectWithOffset(1, err).NotTo(HaveOccurred(), "failed to send check_run event")
|
||||||
|
|
||||||
|
ExpectWithOffset(1, resp.StatusCode).To(Equal(200))
|
||||||
|
}
|
||||||
func (env *testEnvironment) SyncRunnerRegistrations() {
|
func (env *testEnvironment) SyncRunnerRegistrations() {
|
||||||
var runnerList actionsv1alpha1.RunnerList
|
var runnerList actionsv1alpha1.RunnerList
|
||||||
|
|
||||||
@@ -1175,7 +1404,7 @@ func (env *testEnvironment) SyncRunnerRegistrations() {
|
|||||||
env.fakeRunnerList.Sync(runnerList.Items)
|
env.fakeRunnerList.Sync(runnerList.Items)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExpectCreate(ctx context.Context, rd runtime.Object, s string) {
|
func ExpectCreate(ctx context.Context, rd client.Object, s string) {
|
||||||
err := k8sClient.Create(ctx, rd)
|
err := k8sClient.Create(ctx, rd)
|
||||||
|
|
||||||
ExpectWithOffset(1, err).NotTo(HaveOccurred(), fmt.Sprintf("failed to create %s resource", s))
|
ExpectWithOffset(1, err).NotTo(HaveOccurred(), fmt.Sprintf("failed to create %s resource", s))
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package metrics
|
package metrics
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/actions-runner-controller/actions-runner-controller/api/v1alpha1"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/summerwind/actions-runner-controller/api/v1alpha1"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package metrics
|
package metrics
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/actions-runner-controller/actions-runner-controller/api/v1alpha1"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/summerwind/actions-runner-controller/api/v1alpha1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|||||||
37
controllers/metrics/runnerset.go
Normal file
37
controllers/metrics/runnerset.go
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package metrics
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/actions-runner-controller/actions-runner-controller/api/v1alpha1"
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
rsName = "runnerset"
|
||||||
|
rsNamespace = "namespace"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
runnerSetMetrics = []prometheus.Collector{
|
||||||
|
runnerSetReplicas,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
runnerSetReplicas = prometheus.NewGaugeVec(
|
||||||
|
prometheus.GaugeOpts{
|
||||||
|
Name: "runnerset_spec_replicas",
|
||||||
|
Help: "replicas of RunnerSet",
|
||||||
|
},
|
||||||
|
[]string{rsName, rsNamespace},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
func SetRunnerSet(rd v1alpha1.RunnerSet) {
|
||||||
|
labels := prometheus.Labels{
|
||||||
|
rsName: rd.Name,
|
||||||
|
rsNamespace: rd.Namespace,
|
||||||
|
}
|
||||||
|
if rd.Spec.Replicas != nil {
|
||||||
|
runnerSetReplicas.With(labels).Set(float64(*rd.Spec.Replicas))
|
||||||
|
}
|
||||||
|
}
|
||||||
132
controllers/pod_runner_token_injector.go
Normal file
132
controllers/pod_runner_token_injector.go
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/actions-runner-controller/actions-runner-controller/github"
|
||||||
|
"github.com/go-logr/logr"
|
||||||
|
"gomodules.xyz/jsonpatch/v2"
|
||||||
|
admissionv1 "k8s.io/api/admission/v1"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
"k8s.io/client-go/tools/record"
|
||||||
|
ctrl "sigs.k8s.io/controller-runtime"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
AnnotationKeyTokenExpirationDate = "actions-runner-controller/token-expires-at"
|
||||||
|
)
|
||||||
|
|
||||||
|
// +kubebuilder:webhook:path=/mutate-runner-set-pod,mutating=true,failurePolicy=ignore,groups="",resources=pods,verbs=create,versions=v1,name=mutate-runner-pod.webhook.actions.summerwind.dev,sideEffects=None,admissionReviewVersions=v1beta1
|
||||||
|
|
||||||
|
type PodRunnerTokenInjector struct {
|
||||||
|
client.Client
|
||||||
|
|
||||||
|
Name string
|
||||||
|
Log logr.Logger
|
||||||
|
Recorder record.EventRecorder
|
||||||
|
GitHubClient *github.Client
|
||||||
|
decoder *admission.Decoder
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *PodRunnerTokenInjector) Handle(ctx context.Context, req admission.Request) admission.Response {
|
||||||
|
var pod corev1.Pod
|
||||||
|
err := t.decoder.Decode(req, &pod)
|
||||||
|
if err != nil {
|
||||||
|
t.Log.Error(err, "Failed to decode request object")
|
||||||
|
return admission.Errored(http.StatusBadRequest, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if pod.Annotations == nil {
|
||||||
|
pod.Annotations = map[string]string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var runnerContainer *corev1.Container
|
||||||
|
|
||||||
|
for i := range pod.Spec.Containers {
|
||||||
|
c := pod.Spec.Containers[i]
|
||||||
|
|
||||||
|
if c.Name == "runner" {
|
||||||
|
runnerContainer = &c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if runnerContainer == nil {
|
||||||
|
return newEmptyResponse()
|
||||||
|
}
|
||||||
|
|
||||||
|
enterprise, okEnterprise := getEnv(runnerContainer, "RUNNER_ENTERPRISE")
|
||||||
|
repo, okRepo := getEnv(runnerContainer, "RUNNER_REPO")
|
||||||
|
org, okOrg := getEnv(runnerContainer, "RUNNER_ORG")
|
||||||
|
if !okRepo || !okOrg || !okEnterprise {
|
||||||
|
return newEmptyResponse()
|
||||||
|
}
|
||||||
|
|
||||||
|
rt, err := t.GitHubClient.GetRegistrationToken(context.Background(), enterprise, org, repo, pod.Name)
|
||||||
|
if err != nil {
|
||||||
|
t.Log.Error(err, "Failed to get new registration token")
|
||||||
|
return admission.Errored(http.StatusInternalServerError, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ts := rt.GetExpiresAt().Format(time.RFC3339)
|
||||||
|
|
||||||
|
updated := mutatePod(&pod, *rt.Token)
|
||||||
|
|
||||||
|
updated.Annotations[AnnotationKeyTokenExpirationDate] = ts
|
||||||
|
|
||||||
|
if pod.Spec.RestartPolicy != corev1.RestartPolicyOnFailure {
|
||||||
|
updated.Spec.RestartPolicy = corev1.RestartPolicyOnFailure
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, err := json.Marshal(updated)
|
||||||
|
if err != nil {
|
||||||
|
t.Log.Error(err, "Failed to encode new object")
|
||||||
|
return admission.Errored(http.StatusInternalServerError, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
res := admission.PatchResponseFromRaw(req.Object.Raw, buf)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func getEnv(container *corev1.Container, key string) (string, bool) {
|
||||||
|
for _, env := range container.Env {
|
||||||
|
if env.Name == key {
|
||||||
|
return env.Value, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *PodRunnerTokenInjector) InjectDecoder(d *admission.Decoder) error {
|
||||||
|
t.decoder = d
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newEmptyResponse() admission.Response {
|
||||||
|
pt := admissionv1.PatchTypeJSONPatch
|
||||||
|
return admission.Response{
|
||||||
|
Patches: []jsonpatch.Operation{},
|
||||||
|
AdmissionResponse: admissionv1.AdmissionResponse{
|
||||||
|
Allowed: true,
|
||||||
|
PatchType: &pt,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *PodRunnerTokenInjector) SetupWithManager(mgr ctrl.Manager) error {
|
||||||
|
name := "pod-runner-token-injector"
|
||||||
|
if r.Name != "" {
|
||||||
|
name = r.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Recorder = mgr.GetEventRecorderFor(name)
|
||||||
|
|
||||||
|
mgr.GetWebhookServer().Register("/mutate-runner-set-pod", &admission.Webhook{Handler: r})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
396
controllers/runner_pod_controller.go
Normal file
396
controllers/runner_pod_controller.go
Normal file
@@ -0,0 +1,396 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2020 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"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-logr/logr"
|
||||||
|
gogithub "github.com/google/go-github/v39/github"
|
||||||
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
|
|
||||||
|
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
"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"
|
||||||
|
|
||||||
|
"github.com/actions-runner-controller/actions-runner-controller/github"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RunnerPodReconciler reconciles a Runner object
|
||||||
|
type RunnerPodReconciler struct {
|
||||||
|
client.Client
|
||||||
|
Log logr.Logger
|
||||||
|
Recorder record.EventRecorder
|
||||||
|
Scheme *runtime.Scheme
|
||||||
|
GitHubClient *github.Client
|
||||||
|
Name string
|
||||||
|
RegistrationRecheckInterval time.Duration
|
||||||
|
RegistrationRecheckJitter time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// This names requires at least one slash to work.
|
||||||
|
// See https://github.com/google/knative-gcp/issues/378
|
||||||
|
runnerPodFinalizerName = "actions.summerwind.dev/runner-pod"
|
||||||
|
|
||||||
|
AnnotationKeyLastRegistrationCheckTime = "actions-runner-controller/last-registration-check-time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// +kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;watch;update;patch;delete
|
||||||
|
// +kubebuilder:rbac:groups=core,resources=events,verbs=create;patch
|
||||||
|
|
||||||
|
func (r *RunnerPodReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||||
|
log := r.Log.WithValues("runnerpod", req.NamespacedName)
|
||||||
|
|
||||||
|
var runnerPod corev1.Pod
|
||||||
|
if err := r.Get(ctx, req.NamespacedName, &runnerPod); err != nil {
|
||||||
|
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, isRunnerPod := runnerPod.Labels[LabelKeyRunnerSetName]
|
||||||
|
if !isRunnerPod {
|
||||||
|
return ctrl.Result{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var enterprise, org, repo string
|
||||||
|
|
||||||
|
envvars := runnerPod.Spec.Containers[0].Env
|
||||||
|
for _, e := range envvars {
|
||||||
|
switch e.Name {
|
||||||
|
case EnvVarEnterprise:
|
||||||
|
enterprise = e.Value
|
||||||
|
case EnvVarOrg:
|
||||||
|
org = e.Value
|
||||||
|
case EnvVarRepo:
|
||||||
|
repo = e.Value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if runnerPod.ObjectMeta.DeletionTimestamp.IsZero() {
|
||||||
|
finalizers, added := addFinalizer(runnerPod.ObjectMeta.Finalizers, runnerPodFinalizerName)
|
||||||
|
|
||||||
|
if added {
|
||||||
|
newRunner := runnerPod.DeepCopy()
|
||||||
|
newRunner.ObjectMeta.Finalizers = finalizers
|
||||||
|
|
||||||
|
if err := r.Patch(ctx, newRunner, client.MergeFrom(&runnerPod)); err != nil {
|
||||||
|
log.Error(err, "Failed to update runner")
|
||||||
|
return ctrl.Result{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctrl.Result{}, nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
finalizers, removed := removeFinalizer(runnerPod.ObjectMeta.Finalizers, runnerPodFinalizerName)
|
||||||
|
|
||||||
|
if removed {
|
||||||
|
ok, err := r.unregisterRunner(ctx, enterprise, org, repo, runnerPod.Name)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, &gogithub.RateLimitError{}) {
|
||||||
|
// We log the underlying error when we failed calling GitHub API to list or unregisters,
|
||||||
|
// or the runner is still busy.
|
||||||
|
log.Error(
|
||||||
|
err,
|
||||||
|
fmt.Sprintf(
|
||||||
|
"Failed to unregister runner due to GitHub API rate limits. Delaying retry for %s to avoid excessive GitHub API calls",
|
||||||
|
retryDelayOnGitHubAPIRateLimitError,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
return ctrl.Result{RequeueAfter: retryDelayOnGitHubAPIRateLimitError}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctrl.Result{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
log.V(1).Info("Runner no longer exists on GitHub")
|
||||||
|
}
|
||||||
|
|
||||||
|
newRunner := runnerPod.DeepCopy()
|
||||||
|
newRunner.ObjectMeta.Finalizers = finalizers
|
||||||
|
|
||||||
|
if err := r.Patch(ctx, newRunner, client.MergeFrom(&runnerPod)); err != nil {
|
||||||
|
log.Error(err, "Failed to update runner for finalizer removal")
|
||||||
|
return ctrl.Result{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Removed runner from GitHub", "repository", repo, "organization", org)
|
||||||
|
}
|
||||||
|
|
||||||
|
deletionTimeout := 1 * time.Minute
|
||||||
|
currentTime := time.Now()
|
||||||
|
deletionDidTimeout := currentTime.Sub(runnerPod.DeletionTimestamp.Add(deletionTimeout)) > 0
|
||||||
|
|
||||||
|
if deletionDidTimeout {
|
||||||
|
log.Info(
|
||||||
|
fmt.Sprintf("Failed to delete pod within %s. ", deletionTimeout)+
|
||||||
|
"This is typically the case when a Kubernetes node became unreachable "+
|
||||||
|
"and the kube controller started evicting nodes. Forcefully deleting the pod to not get stuck.",
|
||||||
|
"podDeletionTimestamp", runnerPod.DeletionTimestamp,
|
||||||
|
"currentTime", currentTime,
|
||||||
|
"configuredDeletionTimeout", deletionTimeout,
|
||||||
|
)
|
||||||
|
|
||||||
|
var force int64 = 0
|
||||||
|
// forcefully delete runner as we would otherwise get stuck if the node stays unreachable
|
||||||
|
if err := r.Delete(ctx, &runnerPod, &client.DeleteOptions{GracePeriodSeconds: &force}); err != nil {
|
||||||
|
// probably
|
||||||
|
if !kerrors.IsNotFound(err) {
|
||||||
|
log.Error(err, "Failed to forcefully delete pod resource ...")
|
||||||
|
return ctrl.Result{}, err
|
||||||
|
}
|
||||||
|
// forceful deletion finally succeeded
|
||||||
|
return ctrl.Result{Requeue: true}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Recorder.Event(&runnerPod, corev1.EventTypeNormal, "PodDeleted", fmt.Sprintf("Forcefully deleted pod '%s'", runnerPod.Name))
|
||||||
|
log.Info("Forcefully deleted runner pod", "repository", repo)
|
||||||
|
// give kube manager a little time to forcefully delete the stuck pod
|
||||||
|
return ctrl.Result{RequeueAfter: 3 * time.Second}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctrl.Result{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If pod has ended up succeeded we need to restart it
|
||||||
|
// Happens e.g. when dind is in runner and run completes
|
||||||
|
stopped := runnerPod.Status.Phase == corev1.PodSucceeded
|
||||||
|
|
||||||
|
if !stopped {
|
||||||
|
if runnerPod.Status.Phase == corev1.PodRunning {
|
||||||
|
for _, status := range runnerPod.Status.ContainerStatuses {
|
||||||
|
if status.Name != containerName {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if status.State.Terminated != nil && status.State.Terminated.ExitCode == 0 {
|
||||||
|
stopped = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
restart := stopped
|
||||||
|
|
||||||
|
var registrationRecheckDelay time.Duration
|
||||||
|
|
||||||
|
// all checks done below only decide whether a restart is needed
|
||||||
|
// if a restart was already decided before, there is no need for the checks
|
||||||
|
// saving API calls and scary log messages
|
||||||
|
if !restart {
|
||||||
|
registrationCheckInterval := time.Minute
|
||||||
|
if r.RegistrationRecheckInterval > 0 {
|
||||||
|
registrationCheckInterval = r.RegistrationRecheckInterval
|
||||||
|
}
|
||||||
|
|
||||||
|
lastCheckTimeStr := runnerPod.Annotations[AnnotationKeyLastRegistrationCheckTime]
|
||||||
|
|
||||||
|
var lastCheckTime *time.Time
|
||||||
|
|
||||||
|
if lastCheckTimeStr != "" {
|
||||||
|
t, err := time.Parse(time.RFC3339, lastCheckTimeStr)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err, "failed to parase last check time %q", lastCheckTimeStr)
|
||||||
|
return ctrl.Result{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
lastCheckTime = &t
|
||||||
|
}
|
||||||
|
|
||||||
|
// We want to call ListRunners GitHub Actions API only once per runner per minute.
|
||||||
|
// This if block, in conjunction with:
|
||||||
|
// return ctrl.Result{RequeueAfter: registrationRecheckDelay}, nil
|
||||||
|
// achieves that.
|
||||||
|
if lastCheckTime != nil {
|
||||||
|
nextCheckTime := lastCheckTime.Add(registrationCheckInterval)
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
// Requeue scheduled by RequeueAfter can happen a bit earlier (like dozens of milliseconds)
|
||||||
|
// so to avoid excessive, in-effective retry, we heuristically ignore the remaining delay in case it is
|
||||||
|
// shorter than 1s
|
||||||
|
requeueAfter := nextCheckTime.Sub(now) - time.Second
|
||||||
|
if requeueAfter > 0 {
|
||||||
|
log.Info(
|
||||||
|
fmt.Sprintf("Skipped registration check because it's deferred until %s. Retrying in %s at latest", nextCheckTime, requeueAfter),
|
||||||
|
"lastRegistrationCheckTime", lastCheckTime,
|
||||||
|
"registrationCheckInterval", registrationCheckInterval,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Without RequeueAfter, the controller may not retry on scheduled. Instead, it must wait until the
|
||||||
|
// next sync period passes, which can be too much later than nextCheckTime.
|
||||||
|
//
|
||||||
|
// We need to requeue on this reconcilation even though we have already scheduled the initial
|
||||||
|
// requeue previously with `return ctrl.Result{RequeueAfter: registrationRecheckDelay}, nil`.
|
||||||
|
// Apparently, the workqueue used by controller-runtime seems to deduplicate and resets the delay on
|
||||||
|
// other requeues- so the initial scheduled requeue may have been reset due to requeue on
|
||||||
|
// spec/status change.
|
||||||
|
return ctrl.Result{RequeueAfter: requeueAfter}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
notFound := false
|
||||||
|
offline := false
|
||||||
|
|
||||||
|
_, err := r.GitHubClient.IsRunnerBusy(ctx, enterprise, org, repo, runnerPod.Name)
|
||||||
|
|
||||||
|
currentTime := time.Now()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
var notFoundException *github.RunnerNotFound
|
||||||
|
var offlineException *github.RunnerOffline
|
||||||
|
if errors.As(err, ¬FoundException) {
|
||||||
|
notFound = true
|
||||||
|
} else if errors.As(err, &offlineException) {
|
||||||
|
offline = true
|
||||||
|
} else {
|
||||||
|
var e *gogithub.RateLimitError
|
||||||
|
if errors.As(err, &e) {
|
||||||
|
// We log the underlying error when we failed calling GitHub API to list or unregisters,
|
||||||
|
// or the runner is still busy.
|
||||||
|
log.Error(
|
||||||
|
err,
|
||||||
|
fmt.Sprintf(
|
||||||
|
"Failed to check if runner is busy due to Github API rate limit. Retrying in %s to avoid excessive GitHub API calls",
|
||||||
|
retryDelayOnGitHubAPIRateLimitError,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
return ctrl.Result{RequeueAfter: retryDelayOnGitHubAPIRateLimitError}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctrl.Result{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
registrationTimeout := 10 * time.Minute
|
||||||
|
durationAfterRegistrationTimeout := currentTime.Sub(runnerPod.CreationTimestamp.Add(registrationTimeout))
|
||||||
|
registrationDidTimeout := durationAfterRegistrationTimeout > 0
|
||||||
|
|
||||||
|
if notFound {
|
||||||
|
if registrationDidTimeout {
|
||||||
|
log.Info(
|
||||||
|
"Runner failed to register itself to GitHub in timely manner. "+
|
||||||
|
"Recreating the pod to see if it resolves the issue. "+
|
||||||
|
"CAUTION: If you see this a lot, you should investigate the root cause. "+
|
||||||
|
"See https://github.com/actions-runner-controller/actions-runner-controller/issues/288",
|
||||||
|
"podCreationTimestamp", runnerPod.CreationTimestamp,
|
||||||
|
"currentTime", currentTime,
|
||||||
|
"configuredRegistrationTimeout", registrationTimeout,
|
||||||
|
)
|
||||||
|
|
||||||
|
restart = true
|
||||||
|
} else {
|
||||||
|
log.V(1).Info(
|
||||||
|
"Runner pod exists but we failed to check if runner is busy. Apparently it still needs more time.",
|
||||||
|
"runnerName", runnerPod.Name,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else if offline {
|
||||||
|
if registrationDidTimeout {
|
||||||
|
log.Info(
|
||||||
|
"Already existing GitHub runner still appears offline . "+
|
||||||
|
"Recreating the pod to see if it resolves the issue. "+
|
||||||
|
"CAUTION: If you see this a lot, you should investigate the root cause. ",
|
||||||
|
"podCreationTimestamp", runnerPod.CreationTimestamp,
|
||||||
|
"currentTime", currentTime,
|
||||||
|
"configuredRegistrationTimeout", registrationTimeout,
|
||||||
|
)
|
||||||
|
|
||||||
|
restart = true
|
||||||
|
} else {
|
||||||
|
log.V(1).Info(
|
||||||
|
"Runner pod exists but the GitHub runner appears to be still offline. Waiting for runner to get online ...",
|
||||||
|
"runnerName", runnerPod.Name,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (notFound || offline) && !registrationDidTimeout {
|
||||||
|
registrationRecheckJitter := 10 * time.Second
|
||||||
|
if r.RegistrationRecheckJitter > 0 {
|
||||||
|
registrationRecheckJitter = r.RegistrationRecheckJitter
|
||||||
|
}
|
||||||
|
|
||||||
|
registrationRecheckDelay = registrationCheckInterval + wait.Jitter(registrationRecheckJitter, 0.1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't do anything if there's no need to restart the runner
|
||||||
|
if !restart {
|
||||||
|
// This guard enables us to update runner.Status.Phase to `Running` only after
|
||||||
|
// the runner is registered to GitHub.
|
||||||
|
if registrationRecheckDelay > 0 {
|
||||||
|
log.V(1).Info(fmt.Sprintf("Rechecking the runner registration in %s", registrationRecheckDelay))
|
||||||
|
|
||||||
|
updated := runnerPod.DeepCopy()
|
||||||
|
t := time.Now().Format(time.RFC3339)
|
||||||
|
updated.Annotations[AnnotationKeyLastRegistrationCheckTime] = t
|
||||||
|
|
||||||
|
if err := r.Patch(ctx, updated, client.MergeFrom(&runnerPod)); err != nil {
|
||||||
|
log.Error(err, "Failed to update runner pod annotation for LastRegistrationCheckTime")
|
||||||
|
return ctrl.Result{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctrl.Result{RequeueAfter: registrationRecheckDelay}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seeing this message, you can expect the runner to become `Running` soon.
|
||||||
|
log.Info(
|
||||||
|
"Runner appears to have registered and running.",
|
||||||
|
"podCreationTimestamp", runnerPod.CreationTimestamp,
|
||||||
|
)
|
||||||
|
|
||||||
|
return ctrl.Result{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete current pod if recreation is needed
|
||||||
|
if err := r.Delete(ctx, &runnerPod); err != nil {
|
||||||
|
log.Error(err, "Failed to delete pod resource")
|
||||||
|
return ctrl.Result{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Recorder.Event(&runnerPod, corev1.EventTypeNormal, "PodDeleted", fmt.Sprintf("Deleted pod '%s'", runnerPod.Name))
|
||||||
|
log.Info("Deleted runner pod", "name", runnerPod.Name)
|
||||||
|
|
||||||
|
return ctrl.Result{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RunnerPodReconciler) unregisterRunner(ctx context.Context, enterprise, org, repo, name string) (bool, error) {
|
||||||
|
return unregisterRunner(ctx, r.GitHubClient, enterprise, org, repo, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RunnerPodReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||||
|
name := "runnerpod-controller"
|
||||||
|
if r.Name != "" {
|
||||||
|
name = r.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Recorder = mgr.GetEventRecorderFor(name)
|
||||||
|
|
||||||
|
return ctrl.NewControllerManagedBy(mgr).
|
||||||
|
For(&corev1.Pod{}).
|
||||||
|
Named(name).
|
||||||
|
Complete(r)
|
||||||
|
}
|
||||||
@@ -37,8 +37,8 @@ import (
|
|||||||
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"
|
||||||
|
|
||||||
"github.com/summerwind/actions-runner-controller/api/v1alpha1"
|
"github.com/actions-runner-controller/actions-runner-controller/api/v1alpha1"
|
||||||
"github.com/summerwind/actions-runner-controller/controllers/metrics"
|
"github.com/actions-runner-controller/actions-runner-controller/controllers/metrics"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -65,8 +65,7 @@ type RunnerDeploymentReconciler struct {
|
|||||||
// +kubebuilder:rbac:groups=actions.summerwind.dev,resources=runnerreplicasets/status,verbs=get;update;patch
|
// +kubebuilder:rbac:groups=actions.summerwind.dev,resources=runnerreplicasets/status,verbs=get;update;patch
|
||||||
// +kubebuilder:rbac:groups=core,resources=events,verbs=create;patch
|
// +kubebuilder:rbac:groups=core,resources=events,verbs=create;patch
|
||||||
|
|
||||||
func (r *RunnerDeploymentReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
|
func (r *RunnerDeploymentReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||||
ctx := context.Background()
|
|
||||||
log := r.Log.WithValues("runnerdeployment", req.NamespacedName)
|
log := r.Log.WithValues("runnerdeployment", req.NamespacedName)
|
||||||
|
|
||||||
var rd v1alpha1.RunnerDeployment
|
var rd v1alpha1.RunnerDeployment
|
||||||
@@ -155,7 +154,7 @@ func (r *RunnerDeploymentReconciler) Reconcile(req ctrl.Request) (ctrl.Result, e
|
|||||||
// A selector update change doesn't trigger replicaset replacement,
|
// A selector update change doesn't trigger replicaset replacement,
|
||||||
// but we still need to update the existing replicaset with it.
|
// but we still need to update the existing replicaset with it.
|
||||||
// Otherwise selector-based runner query will never work on replicasets created before the controller v0.17.0
|
// Otherwise selector-based runner query will never work on replicasets created before the controller v0.17.0
|
||||||
// See https://github.com/summerwind/actions-runner-controller/pull/355#discussion_r585379259
|
// See https://github.com/actions-runner-controller/actions-runner-controller/pull/355#discussion_r585379259
|
||||||
if err := r.Client.Update(ctx, updateSet); err != nil {
|
if err := r.Client.Update(ctx, updateSet); err != nil {
|
||||||
log.Error(err, "Failed to update runnerreplicaset resource")
|
log.Error(err, "Failed to update runnerreplicaset resource")
|
||||||
|
|
||||||
@@ -439,7 +438,7 @@ func (r *RunnerDeploymentReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
|||||||
|
|
||||||
r.Recorder = mgr.GetEventRecorderFor(name)
|
r.Recorder = mgr.GetEventRecorderFor(name)
|
||||||
|
|
||||||
if err := mgr.GetFieldIndexer().IndexField(&v1alpha1.RunnerReplicaSet{}, runnerSetOwnerKey, func(rawObj runtime.Object) []string {
|
if err := mgr.GetFieldIndexer().IndexField(context.TODO(), &v1alpha1.RunnerReplicaSet{}, runnerSetOwnerKey, func(rawObj client.Object) []string {
|
||||||
runnerSet := rawObj.(*v1alpha1.RunnerReplicaSet)
|
runnerSet := rawObj.(*v1alpha1.RunnerReplicaSet)
|
||||||
owner := metav1.GetControllerOf(runnerSet)
|
owner := metav1.GetControllerOf(runnerSet)
|
||||||
if owner == nil {
|
if owner == nil {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user