mirror of
https://github.com/actions/actions-runner-controller.git
synced 2025-12-11 12:06:57 +00:00
Compare commits
9 Commits
gha-runner
...
0.2.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3e37a29c21 | ||
|
|
bd9f32e354 | ||
|
|
babbfc77d5 | ||
|
|
322df79617 | ||
|
|
1c7c6639ed | ||
|
|
bcaac39a2e | ||
|
|
af625dd1cb | ||
|
|
44969659df | ||
|
|
a5f98dea75 |
43
.github/renovate.json5
vendored
43
.github/renovate.json5
vendored
@@ -1,43 +0,0 @@
|
|||||||
{
|
|
||||||
"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.yaml"
|
|
||||||
],
|
|
||||||
"matchStrings": ["RUNNER_VERSION: +(?<currentValue>.*?)\\n"],
|
|
||||||
"depNameTemplate": "actions/runner",
|
|
||||||
"datasourceTemplate": "github-releases"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fileMatch": [
|
|
||||||
"runner/Makefile",
|
|
||||||
"Makefile"
|
|
||||||
],
|
|
||||||
"matchStrings": ["RUNNER_VERSION \\?= +(?<currentValue>.*?)\\n"],
|
|
||||||
"depNameTemplate": "actions/runner",
|
|
||||||
"datasourceTemplate": "github-releases"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fileMatch": [
|
|
||||||
"runner/actions-runner.ubuntu-20.04.dockerfile",
|
|
||||||
"runner/actions-runner.ubuntu-22.04.dockerfile",
|
|
||||||
"runner/actions-runner-dind.ubuntu-20.04.dockerfile",
|
|
||||||
"runner/actions-runner-dind-rootless.ubuntu-20.04.dockerfile"
|
|
||||||
],
|
|
||||||
"matchStrings": ["RUNNER_VERSION=+(?<currentValue>.*?)\\n"],
|
|
||||||
"depNameTemplate": "actions/runner",
|
|
||||||
"datasourceTemplate": "github-releases"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
1
.github/workflows/publish-arc.yaml
vendored
1
.github/workflows/publish-arc.yaml
vendored
@@ -29,6 +29,7 @@ jobs:
|
|||||||
release-controller:
|
release-controller:
|
||||||
name: Release
|
name: Release
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ !startsWith(github.event.inputs.release_tag_name, 'gha-runner-scale-set-') }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|||||||
2
.github/workflows/release-runners.yaml
vendored
2
.github/workflows/release-runners.yaml
vendored
@@ -17,7 +17,7 @@ env:
|
|||||||
PUSH_TO_REGISTRIES: true
|
PUSH_TO_REGISTRIES: true
|
||||||
TARGET_ORG: actions-runner-controller
|
TARGET_ORG: actions-runner-controller
|
||||||
TARGET_WORKFLOW: release-runners.yaml
|
TARGET_WORKFLOW: release-runners.yaml
|
||||||
DOCKER_VERSION: 20.10.21
|
DOCKER_VERSION: 20.10.23
|
||||||
RUNNER_CONTAINER_HOOKS_VERSION: 0.2.0
|
RUNNER_CONTAINER_HOOKS_VERSION: 0.2.0
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|||||||
2
.github/workflows/update-runners.yaml
vendored
2
.github/workflows/update-runners.yaml
vendored
@@ -93,7 +93,7 @@ jobs:
|
|||||||
sed -i "s/$CURRENT_VERSION/$LATEST_VERSION/g" runner/Makefile
|
sed -i "s/$CURRENT_VERSION/$LATEST_VERSION/g" runner/Makefile
|
||||||
sed -i "s/$CURRENT_VERSION/$LATEST_VERSION/g" Makefile
|
sed -i "s/$CURRENT_VERSION/$LATEST_VERSION/g" Makefile
|
||||||
sed -i "s/$CURRENT_VERSION/$LATEST_VERSION/g" test/e2e/e2e_test.go
|
sed -i "s/$CURRENT_VERSION/$LATEST_VERSION/g" test/e2e/e2e_test.go
|
||||||
sed -i "s/$CURRENT_VERSION/$LATEST_VERSION/g" .github/workflows/e2e_test_linux_vm.yaml
|
sed -i "s/$CURRENT_VERSION/$LATEST_VERSION/g" .github/workflows/e2e-test-linux-vm.yaml
|
||||||
|
|
||||||
- name: Commit changes
|
- name: Commit changes
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
4
.github/workflows/validate-chart.yaml
vendored
4
.github/workflows/validate-chart.yaml
vendored
@@ -9,12 +9,16 @@ on:
|
|||||||
- '.github/workflows/validate-chart.yaml'
|
- '.github/workflows/validate-chart.yaml'
|
||||||
- '!charts/actions-runner-controller/docs/**'
|
- '!charts/actions-runner-controller/docs/**'
|
||||||
- '!**.md'
|
- '!**.md'
|
||||||
|
- '!charts/gha-runner-scale-set-controller/**'
|
||||||
|
- '!charts/gha-runner-scale-set/**'
|
||||||
push:
|
push:
|
||||||
paths:
|
paths:
|
||||||
- 'charts/**'
|
- 'charts/**'
|
||||||
- '.github/workflows/validate-chart.yaml'
|
- '.github/workflows/validate-chart.yaml'
|
||||||
- '!charts/actions-runner-controller/docs/**'
|
- '!charts/actions-runner-controller/docs/**'
|
||||||
- '!**.md'
|
- '!**.md'
|
||||||
|
- '!charts/gha-runner-scale-set-controller/**'
|
||||||
|
- '!charts/gha-runner-scale-set/**'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
env:
|
env:
|
||||||
KUBE_SCORE_VERSION: 1.10.0
|
KUBE_SCORE_VERSION: 1.10.0
|
||||||
|
|||||||
134
.github/workflows/validate-gha-chart.yaml
vendored
Normal file
134
.github/workflows/validate-gha-chart.yaml
vendored
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
name: Validate Helm Chart (gha-runner-scale-set-controller and gha-runner-scale-set)
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
paths:
|
||||||
|
- 'charts/**'
|
||||||
|
- '.github/workflows/validate-gha-chart.yaml'
|
||||||
|
- '!charts/actions-runner-controller/**'
|
||||||
|
- '!**.md'
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- 'charts/**'
|
||||||
|
- '.github/workflows/validate-gha-chart.yaml'
|
||||||
|
- '!charts/actions-runner-controller/**'
|
||||||
|
- '!**.md'
|
||||||
|
workflow_dispatch:
|
||||||
|
env:
|
||||||
|
KUBE_SCORE_VERSION: 1.16.1
|
||||||
|
HELM_VERSION: v3.8.0
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
validate-chart:
|
||||||
|
name: Lint Chart
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Set up Helm
|
||||||
|
# Using https://github.com/Azure/setup-helm/releases/tag/v3.5
|
||||||
|
uses: azure/setup-helm@5119fcb9089d432beecbf79bb2c7915207344b78
|
||||||
|
with:
|
||||||
|
version: ${{ env.HELM_VERSION }}
|
||||||
|
|
||||||
|
- name: Set up kube-score
|
||||||
|
run: |
|
||||||
|
wget https://github.com/zegl/kube-score/releases/download/v${{ env.KUBE_SCORE_VERSION }}/kube-score_${{ env.KUBE_SCORE_VERSION }}_linux_amd64 -O kube-score
|
||||||
|
chmod 755 kube-score
|
||||||
|
|
||||||
|
- name: Kube-score generated manifests
|
||||||
|
run: helm template --values charts/.ci/values-kube-score.yaml charts/* | ./kube-score score -
|
||||||
|
--ignore-test pod-networkpolicy
|
||||||
|
--ignore-test deployment-has-poddisruptionbudget
|
||||||
|
--ignore-test deployment-has-host-podantiaffinity
|
||||||
|
--ignore-test container-security-context
|
||||||
|
--ignore-test pod-probes
|
||||||
|
--ignore-test container-image-tag
|
||||||
|
--enable-optional-test container-security-context-privileged
|
||||||
|
--enable-optional-test container-security-context-readonlyrootfilesystem
|
||||||
|
|
||||||
|
# python is a requirement for the chart-testing action below (supports yamllint among other tests)
|
||||||
|
- uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: '3.7'
|
||||||
|
|
||||||
|
- name: Set up chart-testing
|
||||||
|
uses: helm/chart-testing-action@v2.3.1
|
||||||
|
|
||||||
|
- name: Set up latest version chart-testing
|
||||||
|
run: |
|
||||||
|
echo 'deb [trusted=yes] https://repo.goreleaser.com/apt/ /' | sudo tee /etc/apt/sources.list.d/goreleaser.list
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install goreleaser
|
||||||
|
git clone https://github.com/helm/chart-testing
|
||||||
|
cd chart-testing
|
||||||
|
unset CT_CONFIG_DIR
|
||||||
|
goreleaser build --clean --skip-validate
|
||||||
|
./dist/chart-testing_linux_amd64_v1/ct version
|
||||||
|
echo 'Adding ct directory to PATH...'
|
||||||
|
echo "$RUNNER_TEMP/chart-testing/dist/chart-testing_linux_amd64_v1" >> "$GITHUB_PATH"
|
||||||
|
echo 'Setting CT_CONFIG_DIR...'
|
||||||
|
echo "CT_CONFIG_DIR=$RUNNER_TEMP/chart-testing/etc" >> "$GITHUB_ENV"
|
||||||
|
working-directory: ${{ runner.temp }}
|
||||||
|
|
||||||
|
- name: Run chart-testing (list-changed)
|
||||||
|
id: list-changed
|
||||||
|
run: |
|
||||||
|
ct version
|
||||||
|
changed=$(ct list-changed --config charts/.ci/ct-config-gha.yaml)
|
||||||
|
if [[ -n "$changed" ]]; then
|
||||||
|
echo "::set-output name=changed::true"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Run chart-testing (lint)
|
||||||
|
run: |
|
||||||
|
ct lint --config charts/.ci/ct-config-gha.yaml
|
||||||
|
|
||||||
|
- name: Set up docker buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
if: steps.list-changed.outputs.changed == 'true'
|
||||||
|
with:
|
||||||
|
version: latest
|
||||||
|
|
||||||
|
- name: Build controller image
|
||||||
|
uses: docker/build-push-action@v3
|
||||||
|
if: steps.list-changed.outputs.changed == 'true'
|
||||||
|
with:
|
||||||
|
file: Dockerfile
|
||||||
|
platforms: linux/amd64
|
||||||
|
load: true
|
||||||
|
build-args: |
|
||||||
|
DOCKER_IMAGE_NAME=test-arc
|
||||||
|
VERSION=dev
|
||||||
|
tags: |
|
||||||
|
test-arc:dev
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
|
|
||||||
|
- name: Create kind cluster
|
||||||
|
uses: helm/kind-action@v1.4.0
|
||||||
|
if: steps.list-changed.outputs.changed == 'true'
|
||||||
|
with:
|
||||||
|
cluster_name: chart-testing
|
||||||
|
|
||||||
|
- name: Load image into cluster
|
||||||
|
if: steps.list-changed.outputs.changed == 'true'
|
||||||
|
run: |
|
||||||
|
export DOCKER_IMAGE_NAME=test-arc
|
||||||
|
export VERSION=dev
|
||||||
|
export IMG_RESULT=load
|
||||||
|
make docker-buildx
|
||||||
|
kind load docker-image test-arc:dev --name chart-testing
|
||||||
|
|
||||||
|
- name: Run chart-testing (install)
|
||||||
|
if: steps.list-changed.outputs.changed == 'true'
|
||||||
|
run: |
|
||||||
|
ct install --config charts/.ci/ct-config-gha.yaml
|
||||||
9
Makefile
9
Makefile
@@ -92,9 +92,14 @@ manager: generate fmt vet
|
|||||||
run: generate fmt vet manifests
|
run: generate fmt vet manifests
|
||||||
go run ./main.go
|
go run ./main.go
|
||||||
|
|
||||||
|
run-scaleset: generate fmt vet
|
||||||
|
CONTROLLER_MANAGER_POD_NAMESPACE=default \
|
||||||
|
CONTROLLER_MANAGER_CONTAINER_IMAGE="${DOCKER_IMAGE_NAME}:${VERSION}" \
|
||||||
|
go run ./main.go --auto-scaling-runner-set-only
|
||||||
|
|
||||||
# Install CRDs into a cluster
|
# Install CRDs into a cluster
|
||||||
install: manifests
|
install: manifests
|
||||||
kustomize build config/crd | kubectl apply -f -
|
kustomize build config/crd | kubectl apply --server-side -f -
|
||||||
|
|
||||||
# Uninstall CRDs from a cluster
|
# Uninstall CRDs from a cluster
|
||||||
uninstall: manifests
|
uninstall: manifests
|
||||||
@@ -103,7 +108,7 @@ uninstall: manifests
|
|||||||
# Deploy controller in the configured Kubernetes cluster in ~/.kube/config
|
# Deploy controller in the configured Kubernetes cluster in ~/.kube/config
|
||||||
deploy: manifests
|
deploy: manifests
|
||||||
cd config/manager && kustomize edit set image controller=${DOCKER_IMAGE_NAME}:${VERSION}
|
cd config/manager && kustomize edit set image controller=${DOCKER_IMAGE_NAME}:${VERSION}
|
||||||
kustomize build config/default | kubectl apply -f -
|
kustomize build config/default | kubectl apply --server-side -f -
|
||||||
|
|
||||||
# Generate manifests e.g. CRD, RBAC etc.
|
# Generate manifests e.g. CRD, RBAC etc.
|
||||||
manifests: manifests-gen-crds chart-crds
|
manifests: manifests-gen-crds chart-crds
|
||||||
|
|||||||
@@ -33,10 +33,14 @@ import (
|
|||||||
|
|
||||||
//+kubebuilder:object:root=true
|
//+kubebuilder:object:root=true
|
||||||
//+kubebuilder:subresource:status
|
//+kubebuilder:subresource:status
|
||||||
//+kubebuilder:printcolumn:JSONPath=".spec.minRunners",name=Minimum Runners,type=number
|
//+kubebuilder:printcolumn:JSONPath=".spec.minRunners",name=Minimum Runners,type=integer
|
||||||
//+kubebuilder:printcolumn:JSONPath=".spec.maxRunners",name=Maximum Runners,type=number
|
//+kubebuilder:printcolumn:JSONPath=".spec.maxRunners",name=Maximum Runners,type=integer
|
||||||
//+kubebuilder:printcolumn:JSONPath=".status.currentRunners",name=Current Runners,type=number
|
//+kubebuilder:printcolumn:JSONPath=".status.currentRunners",name=Current Runners,type=integer
|
||||||
//+kubebuilder:printcolumn:JSONPath=".status.state",name=State,type=string
|
//+kubebuilder:printcolumn:JSONPath=".status.state",name=State,type=string
|
||||||
|
//+kubebuilder:printcolumn:JSONPath=".status.pendingEphemeralRunners",name=Pending Runners,type=integer
|
||||||
|
//+kubebuilder:printcolumn:JSONPath=".status.runningEphemeralRunners",name=Running Runners,type=integer
|
||||||
|
//+kubebuilder:printcolumn:JSONPath=".status.finishedEphemeralRunners",name=Finished Runners,type=integer
|
||||||
|
//+kubebuilder:printcolumn:JSONPath=".status.deletingEphemeralRunners",name=Deleting Runners,type=integer
|
||||||
|
|
||||||
// AutoscalingRunnerSet is the Schema for the autoscalingrunnersets API
|
// AutoscalingRunnerSet is the Schema for the autoscalingrunnersets API
|
||||||
type AutoscalingRunnerSet struct {
|
type AutoscalingRunnerSet struct {
|
||||||
@@ -228,10 +232,19 @@ type ProxyServerConfig struct {
|
|||||||
// AutoscalingRunnerSetStatus defines the observed state of AutoscalingRunnerSet
|
// AutoscalingRunnerSetStatus defines the observed state of AutoscalingRunnerSet
|
||||||
type AutoscalingRunnerSetStatus struct {
|
type AutoscalingRunnerSetStatus struct {
|
||||||
// +optional
|
// +optional
|
||||||
CurrentRunners int `json:"currentRunners,omitempty"`
|
CurrentRunners int `json:"currentRunners"`
|
||||||
|
|
||||||
// +optional
|
// +optional
|
||||||
State string `json:"state,omitempty"`
|
State string `json:"state"`
|
||||||
|
|
||||||
|
// EphemeralRunner counts separated by the stage ephemeral runners are in, taken from the EphemeralRunnerSet
|
||||||
|
|
||||||
|
//+optional
|
||||||
|
PendingEphemeralRunners int `json:"pendingEphemeralRunners"`
|
||||||
|
// +optional
|
||||||
|
RunningEphemeralRunners int `json:"runningEphemeralRunners"`
|
||||||
|
// +optional
|
||||||
|
FailedEphemeralRunners int `json:"failedEphemeralRunners"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ars *AutoscalingRunnerSet) ListenerSpecHash() string {
|
func (ars *AutoscalingRunnerSet) ListenerSpecHash() string {
|
||||||
|
|||||||
@@ -31,13 +31,27 @@ type EphemeralRunnerSetSpec struct {
|
|||||||
// EphemeralRunnerSetStatus defines the observed state of EphemeralRunnerSet
|
// EphemeralRunnerSetStatus defines the observed state of EphemeralRunnerSet
|
||||||
type EphemeralRunnerSetStatus struct {
|
type EphemeralRunnerSetStatus struct {
|
||||||
// CurrentReplicas is the number of currently running EphemeralRunner resources being managed by this EphemeralRunnerSet.
|
// CurrentReplicas is the number of currently running EphemeralRunner resources being managed by this EphemeralRunnerSet.
|
||||||
CurrentReplicas int `json:"currentReplicas,omitempty"`
|
CurrentReplicas int `json:"currentReplicas"`
|
||||||
|
|
||||||
|
// EphemeralRunner counts separated by the stage ephemeral runners are in
|
||||||
|
|
||||||
|
// +optional
|
||||||
|
PendingEphemeralRunners int `json:"pendingEphemeralRunners"`
|
||||||
|
// +optional
|
||||||
|
RunningEphemeralRunners int `json:"runningEphemeralRunners"`
|
||||||
|
// +optional
|
||||||
|
FailedEphemeralRunners int `json:"failedEphemeralRunners"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// +kubebuilder:object:root=true
|
// +kubebuilder:object:root=true
|
||||||
// +kubebuilder:subresource:status
|
// +kubebuilder:subresource:status
|
||||||
// +kubebuilder:printcolumn:JSONPath=".spec.replicas",name="DesiredReplicas",type="integer"
|
// +kubebuilder:printcolumn:JSONPath=".spec.replicas",name="DesiredReplicas",type="integer"
|
||||||
// +kubebuilder:printcolumn:JSONPath=".status.currentReplicas", name="CurrentReplicas",type="integer"
|
// +kubebuilder:printcolumn:JSONPath=".status.currentReplicas", name="CurrentReplicas",type="integer"
|
||||||
|
//+kubebuilder:printcolumn:JSONPath=".status.pendingEphemeralRunners",name=Pending Runners,type=integer
|
||||||
|
//+kubebuilder:printcolumn:JSONPath=".status.runningEphemeralRunners",name=Running Runners,type=integer
|
||||||
|
//+kubebuilder:printcolumn:JSONPath=".status.finishedEphemeralRunners",name=Finished Runners,type=integer
|
||||||
|
//+kubebuilder:printcolumn:JSONPath=".status.deletingEphemeralRunners",name=Deleting Runners,type=integer
|
||||||
|
|
||||||
// EphemeralRunnerSet is the Schema for the ephemeralrunnersets API
|
// EphemeralRunnerSet is the Schema for the ephemeralrunnersets API
|
||||||
type EphemeralRunnerSet struct {
|
type EphemeralRunnerSet struct {
|
||||||
metav1.TypeMeta `json:",inline"`
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
|||||||
9
charts/.ci/ct-config-gha.yaml
Normal file
9
charts/.ci/ct-config-gha.yaml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# This file defines the config for "ct" (chart tester) used by the helm linting GitHub workflow
|
||||||
|
lint-conf: charts/.ci/lint-config.yaml
|
||||||
|
chart-repos:
|
||||||
|
- jetstack=https://charts.jetstack.io
|
||||||
|
check-version-increment: false # Disable checking that the chart version has been bumped
|
||||||
|
charts:
|
||||||
|
- charts/gha-runner-scale-set-controller
|
||||||
|
- charts/gha-runner-scale-set
|
||||||
|
skip-clean-up: true
|
||||||
@@ -5,5 +5,3 @@ chart-repos:
|
|||||||
check-version-increment: false # Disable checking that the chart version has been bumped
|
check-version-increment: false # Disable checking that the chart version has been bumped
|
||||||
charts:
|
charts:
|
||||||
- charts/actions-runner-controller
|
- charts/actions-runner-controller
|
||||||
- charts/gha-runner-scale-set-controller
|
|
||||||
- charts/gha-runner-scale-set
|
|
||||||
5
charts/gha-runner-scale-set-controller/ci/ci-values.yaml
Normal file
5
charts/gha-runner-scale-set-controller/ci/ci-values.yaml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Set the following to dummy values.
|
||||||
|
# This is only useful in CI
|
||||||
|
image:
|
||||||
|
repository: test-arc
|
||||||
|
tag: dev
|
||||||
@@ -17,16 +17,28 @@ spec:
|
|||||||
- additionalPrinterColumns:
|
- additionalPrinterColumns:
|
||||||
- jsonPath: .spec.minRunners
|
- jsonPath: .spec.minRunners
|
||||||
name: Minimum Runners
|
name: Minimum Runners
|
||||||
type: number
|
type: integer
|
||||||
- jsonPath: .spec.maxRunners
|
- jsonPath: .spec.maxRunners
|
||||||
name: Maximum Runners
|
name: Maximum Runners
|
||||||
type: number
|
type: integer
|
||||||
- jsonPath: .status.currentRunners
|
- jsonPath: .status.currentRunners
|
||||||
name: Current Runners
|
name: Current Runners
|
||||||
type: number
|
type: integer
|
||||||
- jsonPath: .status.state
|
- jsonPath: .status.state
|
||||||
name: State
|
name: State
|
||||||
type: string
|
type: string
|
||||||
|
- jsonPath: .status.pendingEphemeralRunners
|
||||||
|
name: Pending Runners
|
||||||
|
type: integer
|
||||||
|
- jsonPath: .status.runningEphemeralRunners
|
||||||
|
name: Running Runners
|
||||||
|
type: integer
|
||||||
|
- jsonPath: .status.finishedEphemeralRunners
|
||||||
|
name: Finished Runners
|
||||||
|
type: integer
|
||||||
|
- jsonPath: .status.deletingEphemeralRunners
|
||||||
|
name: Deleting Runners
|
||||||
|
type: integer
|
||||||
name: v1alpha1
|
name: v1alpha1
|
||||||
schema:
|
schema:
|
||||||
openAPIV3Schema:
|
openAPIV3Schema:
|
||||||
@@ -4306,6 +4318,12 @@ spec:
|
|||||||
properties:
|
properties:
|
||||||
currentRunners:
|
currentRunners:
|
||||||
type: integer
|
type: integer
|
||||||
|
failedEphemeralRunners:
|
||||||
|
type: integer
|
||||||
|
pendingEphemeralRunners:
|
||||||
|
type: integer
|
||||||
|
runningEphemeralRunners:
|
||||||
|
type: integer
|
||||||
state:
|
state:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
|
|||||||
@@ -21,6 +21,18 @@ spec:
|
|||||||
- jsonPath: .status.currentReplicas
|
- jsonPath: .status.currentReplicas
|
||||||
name: CurrentReplicas
|
name: CurrentReplicas
|
||||||
type: integer
|
type: integer
|
||||||
|
- jsonPath: .status.pendingEphemeralRunners
|
||||||
|
name: Pending Runners
|
||||||
|
type: integer
|
||||||
|
- jsonPath: .status.runningEphemeralRunners
|
||||||
|
name: Running Runners
|
||||||
|
type: integer
|
||||||
|
- jsonPath: .status.finishedEphemeralRunners
|
||||||
|
name: Finished Runners
|
||||||
|
type: integer
|
||||||
|
- jsonPath: .status.deletingEphemeralRunners
|
||||||
|
name: Deleting Runners
|
||||||
|
type: integer
|
||||||
name: v1alpha1
|
name: v1alpha1
|
||||||
schema:
|
schema:
|
||||||
openAPIV3Schema:
|
openAPIV3Schema:
|
||||||
@@ -4296,6 +4308,14 @@ spec:
|
|||||||
currentReplicas:
|
currentReplicas:
|
||||||
description: CurrentReplicas is the number of currently running EphemeralRunner resources being managed by this EphemeralRunnerSet.
|
description: CurrentReplicas is the number of currently running EphemeralRunner resources being managed by this EphemeralRunnerSet.
|
||||||
type: integer
|
type: integer
|
||||||
|
failedEphemeralRunners:
|
||||||
|
type: integer
|
||||||
|
pendingEphemeralRunners:
|
||||||
|
type: integer
|
||||||
|
runningEphemeralRunners:
|
||||||
|
type: integer
|
||||||
|
required:
|
||||||
|
- currentReplicas
|
||||||
type: object
|
type: object
|
||||||
type: object
|
type: object
|
||||||
served: true
|
served: true
|
||||||
|
|||||||
@@ -54,10 +54,8 @@ spec:
|
|||||||
command:
|
command:
|
||||||
- "/manager"
|
- "/manager"
|
||||||
env:
|
env:
|
||||||
- name: CONTROLLER_MANAGER_POD_NAME
|
- name: CONTROLLER_MANAGER_CONTAINER_IMAGE
|
||||||
valueFrom:
|
value: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
||||||
fieldRef:
|
|
||||||
fieldPath: metadata.name
|
|
||||||
- name: CONTROLLER_MANAGER_POD_NAMESPACE
|
- name: CONTROLLER_MANAGER_POD_NAMESPACE
|
||||||
valueFrom:
|
valueFrom:
|
||||||
fieldRef:
|
fieldRef:
|
||||||
|
|||||||
@@ -261,9 +261,11 @@ func TestTemplate_ControllerDeployment_Defaults(t *testing.T) {
|
|||||||
assert.Nil(t, deployment.Spec.Template.Spec.Affinity)
|
assert.Nil(t, deployment.Spec.Template.Spec.Affinity)
|
||||||
assert.Len(t, deployment.Spec.Template.Spec.Tolerations, 0)
|
assert.Len(t, deployment.Spec.Template.Spec.Tolerations, 0)
|
||||||
|
|
||||||
|
managerImage := "ghcr.io/actions/gha-runner-scale-set-controller:dev"
|
||||||
|
|
||||||
assert.Len(t, deployment.Spec.Template.Spec.Containers, 1)
|
assert.Len(t, deployment.Spec.Template.Spec.Containers, 1)
|
||||||
assert.Equal(t, "manager", deployment.Spec.Template.Spec.Containers[0].Name)
|
assert.Equal(t, "manager", deployment.Spec.Template.Spec.Containers[0].Name)
|
||||||
assert.Equal(t, "ghcr.io/actions/gha-runner-scale-set-controller:dev", deployment.Spec.Template.Spec.Containers[0].Image)
|
assert.Equal(t, managerImage, deployment.Spec.Template.Spec.Containers[0].Image)
|
||||||
assert.Equal(t, corev1.PullIfNotPresent, deployment.Spec.Template.Spec.Containers[0].ImagePullPolicy)
|
assert.Equal(t, corev1.PullIfNotPresent, deployment.Spec.Template.Spec.Containers[0].ImagePullPolicy)
|
||||||
|
|
||||||
assert.Len(t, deployment.Spec.Template.Spec.Containers[0].Command, 1)
|
assert.Len(t, deployment.Spec.Template.Spec.Containers[0].Command, 1)
|
||||||
@@ -274,8 +276,8 @@ func TestTemplate_ControllerDeployment_Defaults(t *testing.T) {
|
|||||||
assert.Equal(t, "--log-level=debug", deployment.Spec.Template.Spec.Containers[0].Args[1])
|
assert.Equal(t, "--log-level=debug", deployment.Spec.Template.Spec.Containers[0].Args[1])
|
||||||
|
|
||||||
assert.Len(t, deployment.Spec.Template.Spec.Containers[0].Env, 2)
|
assert.Len(t, deployment.Spec.Template.Spec.Containers[0].Env, 2)
|
||||||
assert.Equal(t, "CONTROLLER_MANAGER_POD_NAME", deployment.Spec.Template.Spec.Containers[0].Env[0].Name)
|
assert.Equal(t, "CONTROLLER_MANAGER_CONTAINER_IMAGE", deployment.Spec.Template.Spec.Containers[0].Env[0].Name)
|
||||||
assert.Equal(t, "metadata.name", deployment.Spec.Template.Spec.Containers[0].Env[0].ValueFrom.FieldRef.FieldPath)
|
assert.Equal(t, managerImage, deployment.Spec.Template.Spec.Containers[0].Env[0].Value)
|
||||||
|
|
||||||
assert.Equal(t, "CONTROLLER_MANAGER_POD_NAMESPACE", deployment.Spec.Template.Spec.Containers[0].Env[1].Name)
|
assert.Equal(t, "CONTROLLER_MANAGER_POD_NAMESPACE", deployment.Spec.Template.Spec.Containers[0].Env[1].Name)
|
||||||
assert.Equal(t, "metadata.namespace", deployment.Spec.Template.Spec.Containers[0].Env[1].ValueFrom.FieldRef.FieldPath)
|
assert.Equal(t, "metadata.namespace", deployment.Spec.Template.Spec.Containers[0].Env[1].ValueFrom.FieldRef.FieldPath)
|
||||||
@@ -375,9 +377,11 @@ func TestTemplate_ControllerDeployment_Customize(t *testing.T) {
|
|||||||
assert.Len(t, deployment.Spec.Template.Spec.Tolerations, 1)
|
assert.Len(t, deployment.Spec.Template.Spec.Tolerations, 1)
|
||||||
assert.Equal(t, "foo", deployment.Spec.Template.Spec.Tolerations[0].Key)
|
assert.Equal(t, "foo", deployment.Spec.Template.Spec.Tolerations[0].Key)
|
||||||
|
|
||||||
|
managerImage := "ghcr.io/actions/gha-runner-scale-set-controller:dev"
|
||||||
|
|
||||||
assert.Len(t, deployment.Spec.Template.Spec.Containers, 1)
|
assert.Len(t, deployment.Spec.Template.Spec.Containers, 1)
|
||||||
assert.Equal(t, "manager", deployment.Spec.Template.Spec.Containers[0].Name)
|
assert.Equal(t, "manager", deployment.Spec.Template.Spec.Containers[0].Name)
|
||||||
assert.Equal(t, "ghcr.io/actions/gha-runner-scale-set-controller:dev", deployment.Spec.Template.Spec.Containers[0].Image)
|
assert.Equal(t, managerImage, deployment.Spec.Template.Spec.Containers[0].Image)
|
||||||
assert.Equal(t, corev1.PullAlways, deployment.Spec.Template.Spec.Containers[0].ImagePullPolicy)
|
assert.Equal(t, corev1.PullAlways, deployment.Spec.Template.Spec.Containers[0].ImagePullPolicy)
|
||||||
|
|
||||||
assert.Len(t, deployment.Spec.Template.Spec.Containers[0].Command, 1)
|
assert.Len(t, deployment.Spec.Template.Spec.Containers[0].Command, 1)
|
||||||
@@ -389,8 +393,8 @@ func TestTemplate_ControllerDeployment_Customize(t *testing.T) {
|
|||||||
assert.Equal(t, "--log-level=debug", deployment.Spec.Template.Spec.Containers[0].Args[2])
|
assert.Equal(t, "--log-level=debug", deployment.Spec.Template.Spec.Containers[0].Args[2])
|
||||||
|
|
||||||
assert.Len(t, deployment.Spec.Template.Spec.Containers[0].Env, 2)
|
assert.Len(t, deployment.Spec.Template.Spec.Containers[0].Env, 2)
|
||||||
assert.Equal(t, "CONTROLLER_MANAGER_POD_NAME", deployment.Spec.Template.Spec.Containers[0].Env[0].Name)
|
assert.Equal(t, "CONTROLLER_MANAGER_CONTAINER_IMAGE", deployment.Spec.Template.Spec.Containers[0].Env[0].Name)
|
||||||
assert.Equal(t, "metadata.name", deployment.Spec.Template.Spec.Containers[0].Env[0].ValueFrom.FieldRef.FieldPath)
|
assert.Equal(t, managerImage, deployment.Spec.Template.Spec.Containers[0].Env[0].Value)
|
||||||
|
|
||||||
assert.Equal(t, "CONTROLLER_MANAGER_POD_NAMESPACE", deployment.Spec.Template.Spec.Containers[0].Env[1].Name)
|
assert.Equal(t, "CONTROLLER_MANAGER_POD_NAMESPACE", deployment.Spec.Template.Spec.Containers[0].Env[1].Name)
|
||||||
assert.Equal(t, "metadata.namespace", deployment.Spec.Template.Spec.Containers[0].Env[1].ValueFrom.FieldRef.FieldPath)
|
assert.Equal(t, "metadata.namespace", deployment.Spec.Template.Spec.Containers[0].Env[1].ValueFrom.FieldRef.FieldPath)
|
||||||
|
|||||||
@@ -17,16 +17,28 @@ spec:
|
|||||||
- additionalPrinterColumns:
|
- additionalPrinterColumns:
|
||||||
- jsonPath: .spec.minRunners
|
- jsonPath: .spec.minRunners
|
||||||
name: Minimum Runners
|
name: Minimum Runners
|
||||||
type: number
|
type: integer
|
||||||
- jsonPath: .spec.maxRunners
|
- jsonPath: .spec.maxRunners
|
||||||
name: Maximum Runners
|
name: Maximum Runners
|
||||||
type: number
|
type: integer
|
||||||
- jsonPath: .status.currentRunners
|
- jsonPath: .status.currentRunners
|
||||||
name: Current Runners
|
name: Current Runners
|
||||||
type: number
|
type: integer
|
||||||
- jsonPath: .status.state
|
- jsonPath: .status.state
|
||||||
name: State
|
name: State
|
||||||
type: string
|
type: string
|
||||||
|
- jsonPath: .status.pendingEphemeralRunners
|
||||||
|
name: Pending Runners
|
||||||
|
type: integer
|
||||||
|
- jsonPath: .status.runningEphemeralRunners
|
||||||
|
name: Running Runners
|
||||||
|
type: integer
|
||||||
|
- jsonPath: .status.finishedEphemeralRunners
|
||||||
|
name: Finished Runners
|
||||||
|
type: integer
|
||||||
|
- jsonPath: .status.deletingEphemeralRunners
|
||||||
|
name: Deleting Runners
|
||||||
|
type: integer
|
||||||
name: v1alpha1
|
name: v1alpha1
|
||||||
schema:
|
schema:
|
||||||
openAPIV3Schema:
|
openAPIV3Schema:
|
||||||
@@ -4306,6 +4318,12 @@ spec:
|
|||||||
properties:
|
properties:
|
||||||
currentRunners:
|
currentRunners:
|
||||||
type: integer
|
type: integer
|
||||||
|
failedEphemeralRunners:
|
||||||
|
type: integer
|
||||||
|
pendingEphemeralRunners:
|
||||||
|
type: integer
|
||||||
|
runningEphemeralRunners:
|
||||||
|
type: integer
|
||||||
state:
|
state:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
|
|||||||
@@ -21,6 +21,18 @@ spec:
|
|||||||
- jsonPath: .status.currentReplicas
|
- jsonPath: .status.currentReplicas
|
||||||
name: CurrentReplicas
|
name: CurrentReplicas
|
||||||
type: integer
|
type: integer
|
||||||
|
- jsonPath: .status.pendingEphemeralRunners
|
||||||
|
name: Pending Runners
|
||||||
|
type: integer
|
||||||
|
- jsonPath: .status.runningEphemeralRunners
|
||||||
|
name: Running Runners
|
||||||
|
type: integer
|
||||||
|
- jsonPath: .status.finishedEphemeralRunners
|
||||||
|
name: Finished Runners
|
||||||
|
type: integer
|
||||||
|
- jsonPath: .status.deletingEphemeralRunners
|
||||||
|
name: Deleting Runners
|
||||||
|
type: integer
|
||||||
name: v1alpha1
|
name: v1alpha1
|
||||||
schema:
|
schema:
|
||||||
openAPIV3Schema:
|
openAPIV3Schema:
|
||||||
@@ -4296,6 +4308,14 @@ spec:
|
|||||||
currentReplicas:
|
currentReplicas:
|
||||||
description: CurrentReplicas is the number of currently running EphemeralRunner resources being managed by this EphemeralRunnerSet.
|
description: CurrentReplicas is the number of currently running EphemeralRunner resources being managed by this EphemeralRunnerSet.
|
||||||
type: integer
|
type: integer
|
||||||
|
failedEphemeralRunners:
|
||||||
|
type: integer
|
||||||
|
pendingEphemeralRunners:
|
||||||
|
type: integer
|
||||||
|
runningEphemeralRunners:
|
||||||
|
type: integer
|
||||||
|
required:
|
||||||
|
- currentReplicas
|
||||||
type: object
|
type: object
|
||||||
type: object
|
type: object
|
||||||
served: true
|
served: true
|
||||||
|
|||||||
10
config/manager/env-replacement.yaml
Normal file
10
config/manager/env-replacement.yaml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
source:
|
||||||
|
kind: Deployment
|
||||||
|
name: controller-manager
|
||||||
|
fieldPath: spec.template.spec.containers.[name=manager].image
|
||||||
|
targets:
|
||||||
|
- select:
|
||||||
|
kind: Deployment
|
||||||
|
name: controller-manager
|
||||||
|
fieldPaths:
|
||||||
|
- spec.template.spec.containers.[name=manager].env.[name=CONTROLLER_MANAGER_CONTAINER_IMAGE].value
|
||||||
@@ -6,3 +6,6 @@ images:
|
|||||||
- name: controller
|
- name: controller
|
||||||
newName: summerwind/actions-runner-controller
|
newName: summerwind/actions-runner-controller
|
||||||
newTag: dev
|
newTag: dev
|
||||||
|
|
||||||
|
replacements:
|
||||||
|
- path: env-replacement.yaml
|
||||||
|
|||||||
@@ -50,10 +50,8 @@ spec:
|
|||||||
optional: true
|
optional: true
|
||||||
- name: GITHUB_APP_PRIVATE_KEY
|
- name: GITHUB_APP_PRIVATE_KEY
|
||||||
value: /etc/actions-runner-controller/github_app_private_key
|
value: /etc/actions-runner-controller/github_app_private_key
|
||||||
- name: CONTROLLER_MANAGER_POD_NAME
|
- name: CONTROLLER_MANAGER_CONTAINER_IMAGE
|
||||||
valueFrom:
|
value: CONTROLLER_MANAGER_CONTAINER_IMAGE
|
||||||
fieldRef:
|
|
||||||
fieldPath: metadata.name
|
|
||||||
- name: CONTROLLER_MANAGER_POD_NAMESPACE
|
- name: CONTROLLER_MANAGER_POD_NAMESPACE
|
||||||
valueFrom:
|
valueFrom:
|
||||||
fieldRef:
|
fieldRef:
|
||||||
|
|||||||
@@ -238,6 +238,9 @@ func (r *AutoscalingRunnerSetReconciler) Reconcile(ctx context.Context, req ctrl
|
|||||||
if latestRunnerSet.Status.CurrentReplicas != autoscalingRunnerSet.Status.CurrentRunners {
|
if latestRunnerSet.Status.CurrentReplicas != autoscalingRunnerSet.Status.CurrentRunners {
|
||||||
if err := patchSubResource(ctx, r.Status(), autoscalingRunnerSet, func(obj *v1alpha1.AutoscalingRunnerSet) {
|
if err := patchSubResource(ctx, r.Status(), autoscalingRunnerSet, func(obj *v1alpha1.AutoscalingRunnerSet) {
|
||||||
obj.Status.CurrentRunners = latestRunnerSet.Status.CurrentReplicas
|
obj.Status.CurrentRunners = latestRunnerSet.Status.CurrentReplicas
|
||||||
|
obj.Status.PendingEphemeralRunners = latestRunnerSet.Status.PendingEphemeralRunners
|
||||||
|
obj.Status.RunningEphemeralRunners = latestRunnerSet.Status.RunningEphemeralRunners
|
||||||
|
obj.Status.FailedEphemeralRunners = latestRunnerSet.Status.FailedEphemeralRunners
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
log.Error(err, "Failed to update autoscaling runner set status with current runner count")
|
log.Error(err, "Failed to update autoscaling runner set status with current runner count")
|
||||||
return ctrl.Result{}, err
|
return ctrl.Result{}, err
|
||||||
|
|||||||
@@ -157,23 +157,6 @@ var _ = Describe("Test AutoScalingRunnerSet controller", func() {
|
|||||||
err := k8sClient.List(ctx, runnerSetList, client.InNamespace(autoscalingRunnerSet.Namespace))
|
err := k8sClient.List(ctx, runnerSetList, client.InNamespace(autoscalingRunnerSet.Namespace))
|
||||||
Expect(err).NotTo(HaveOccurred(), "failed to list EphemeralRunnerSet")
|
Expect(err).NotTo(HaveOccurred(), "failed to list EphemeralRunnerSet")
|
||||||
Expect(len(runnerSetList.Items)).To(BeEquivalentTo(1), "Only one EphemeralRunnerSet should be created")
|
Expect(len(runnerSetList.Items)).To(BeEquivalentTo(1), "Only one EphemeralRunnerSet should be created")
|
||||||
runnerSet := runnerSetList.Items[0]
|
|
||||||
statusUpdate := runnerSet.DeepCopy()
|
|
||||||
statusUpdate.Status.CurrentReplicas = 100
|
|
||||||
err = k8sClient.Status().Patch(ctx, statusUpdate, client.MergeFrom(&runnerSet))
|
|
||||||
Expect(err).NotTo(HaveOccurred(), "failed to patch EphemeralRunnerSet status")
|
|
||||||
|
|
||||||
Eventually(
|
|
||||||
func() (int, error) {
|
|
||||||
updated := new(v1alpha1.AutoscalingRunnerSet)
|
|
||||||
err := k8sClient.Get(ctx, client.ObjectKey{Name: autoscalingRunnerSet.Name, Namespace: autoscalingRunnerSet.Namespace}, updated)
|
|
||||||
if err != nil {
|
|
||||||
return 0, fmt.Errorf("failed to get AutoScalingRunnerSet: %w", err)
|
|
||||||
}
|
|
||||||
return updated.Status.CurrentRunners, nil
|
|
||||||
},
|
|
||||||
autoscalingRunnerSetTestTimeout,
|
|
||||||
autoscalingRunnerSetTestInterval).Should(BeEquivalentTo(100), "AutoScalingRunnerSet status should be updated")
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -398,9 +381,75 @@ var _ = Describe("Test AutoScalingRunnerSet controller", func() {
|
|||||||
return updated.Annotations[runnerScaleSetRunnerGroupNameKey], nil
|
return updated.Annotations[runnerScaleSetRunnerGroupNameKey], nil
|
||||||
},
|
},
|
||||||
autoscalingRunnerSetTestTimeout,
|
autoscalingRunnerSetTestTimeout,
|
||||||
autoscalingRunnerSetTestInterval).Should(BeEquivalentTo("testgroup2"), "AutoScalingRunnerSet should have the runner group in its annotation")
|
autoscalingRunnerSetTestInterval,
|
||||||
|
).Should(BeEquivalentTo("testgroup2"), "AutoScalingRunnerSet should have the runner group in its annotation")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("Should update Status on EphemeralRunnerSet status Update", func() {
|
||||||
|
ars := new(v1alpha1.AutoscalingRunnerSet)
|
||||||
|
Eventually(
|
||||||
|
func() (bool, error) {
|
||||||
|
err := k8sClient.Get(
|
||||||
|
ctx,
|
||||||
|
client.ObjectKey{
|
||||||
|
Name: autoscalingRunnerSet.Name,
|
||||||
|
Namespace: autoscalingRunnerSet.Namespace,
|
||||||
|
},
|
||||||
|
ars,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
},
|
||||||
|
autoscalingRunnerSetTestTimeout,
|
||||||
|
autoscalingRunnerSetTestInterval,
|
||||||
|
).Should(BeTrue(), "AutoscalingRunnerSet should be created")
|
||||||
|
|
||||||
|
runnerSetList := new(v1alpha1.EphemeralRunnerSetList)
|
||||||
|
Eventually(func() (int, error) {
|
||||||
|
err := k8sClient.List(ctx, runnerSetList, client.InNamespace(ars.Namespace))
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return len(runnerSetList.Items), nil
|
||||||
|
},
|
||||||
|
autoscalingRunnerSetTestTimeout,
|
||||||
|
autoscalingRunnerSetTestInterval,
|
||||||
|
).Should(BeEquivalentTo(1), "Failed to fetch runner set list")
|
||||||
|
|
||||||
|
runnerSet := runnerSetList.Items[0]
|
||||||
|
statusUpdate := runnerSet.DeepCopy()
|
||||||
|
statusUpdate.Status.CurrentReplicas = 6
|
||||||
|
statusUpdate.Status.FailedEphemeralRunners = 1
|
||||||
|
statusUpdate.Status.RunningEphemeralRunners = 2
|
||||||
|
statusUpdate.Status.PendingEphemeralRunners = 3
|
||||||
|
|
||||||
|
desiredStatus := v1alpha1.AutoscalingRunnerSetStatus{
|
||||||
|
CurrentRunners: statusUpdate.Status.CurrentReplicas,
|
||||||
|
State: "",
|
||||||
|
PendingEphemeralRunners: statusUpdate.Status.PendingEphemeralRunners,
|
||||||
|
RunningEphemeralRunners: statusUpdate.Status.RunningEphemeralRunners,
|
||||||
|
FailedEphemeralRunners: statusUpdate.Status.FailedEphemeralRunners,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := k8sClient.Status().Patch(ctx, statusUpdate, client.MergeFrom(&runnerSet))
|
||||||
|
Expect(err).NotTo(HaveOccurred(), "Failed to patch runner set status")
|
||||||
|
|
||||||
|
Eventually(
|
||||||
|
func() (v1alpha1.AutoscalingRunnerSetStatus, error) {
|
||||||
|
updated := new(v1alpha1.AutoscalingRunnerSet)
|
||||||
|
err := k8sClient.Get(ctx, client.ObjectKey{Name: autoscalingRunnerSet.Name, Namespace: autoscalingRunnerSet.Namespace}, updated)
|
||||||
|
if err != nil {
|
||||||
|
return v1alpha1.AutoscalingRunnerSetStatus{}, fmt.Errorf("failed to get AutoScalingRunnerSet: %w", err)
|
||||||
|
}
|
||||||
|
return updated.Status, nil
|
||||||
|
},
|
||||||
|
autoscalingRunnerSetTestTimeout,
|
||||||
|
autoscalingRunnerSetTestInterval,
|
||||||
|
).Should(BeEquivalentTo(desiredStatus), "AutoScalingRunnerSet status should be updated")
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
var _ = Describe("Test AutoScalingController updates", func() {
|
var _ = Describe("Test AutoScalingController updates", func() {
|
||||||
|
|||||||
@@ -200,11 +200,18 @@ func (r *EphemeralRunnerSetReconciler) Reconcile(ctx context.Context, req ctrl.R
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
desiredStatus := v1alpha1.EphemeralRunnerSetStatus{
|
||||||
|
CurrentReplicas: total,
|
||||||
|
PendingEphemeralRunners: len(pendingEphemeralRunners),
|
||||||
|
RunningEphemeralRunners: len(runningEphemeralRunners),
|
||||||
|
FailedEphemeralRunners: len(failedEphemeralRunners),
|
||||||
|
}
|
||||||
|
|
||||||
// Update the status if needed.
|
// Update the status if needed.
|
||||||
if ephemeralRunnerSet.Status.CurrentReplicas != total {
|
if ephemeralRunnerSet.Status != desiredStatus {
|
||||||
log.Info("Updating status with current runners count", "count", total)
|
log.Info("Updating status with current runners count", "count", total)
|
||||||
if err := patchSubResource(ctx, r.Status(), ephemeralRunnerSet, func(obj *v1alpha1.EphemeralRunnerSet) {
|
if err := patchSubResource(ctx, r.Status(), ephemeralRunnerSet, func(obj *v1alpha1.EphemeralRunnerSet) {
|
||||||
obj.Status.CurrentReplicas = total
|
obj.Status = desiredStatus
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
log.Error(err, "Failed to update status with current runners count")
|
log.Error(err, "Failed to update status with current runners count")
|
||||||
return ctrl.Result{}, err
|
return ctrl.Result{}, err
|
||||||
|
|||||||
@@ -559,6 +559,181 @@ var _ = Describe("Test EphemeralRunnerSet controller", func() {
|
|||||||
ephemeralRunnerSetTestTimeout,
|
ephemeralRunnerSetTestTimeout,
|
||||||
ephemeralRunnerSetTestInterval).Should(BeEquivalentTo(0), "0 EphemeralRunner should be created")
|
ephemeralRunnerSetTestInterval).Should(BeEquivalentTo(0), "0 EphemeralRunner should be created")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("Should update status on Ephemeral Runner state changes", func() {
|
||||||
|
created := new(actionsv1alpha1.EphemeralRunnerSet)
|
||||||
|
Eventually(
|
||||||
|
func() error {
|
||||||
|
return k8sClient.Get(ctx, client.ObjectKey{Name: ephemeralRunnerSet.Name, Namespace: ephemeralRunnerSet.Namespace}, created)
|
||||||
|
},
|
||||||
|
ephemeralRunnerSetTestTimeout,
|
||||||
|
ephemeralRunnerSetTestInterval,
|
||||||
|
).Should(Succeed(), "EphemeralRunnerSet should be created")
|
||||||
|
|
||||||
|
// Scale up the EphemeralRunnerSet
|
||||||
|
updated := created.DeepCopy()
|
||||||
|
updated.Spec.Replicas = 3
|
||||||
|
err := k8sClient.Update(ctx, updated)
|
||||||
|
Expect(err).NotTo(HaveOccurred(), "failed to update EphemeralRunnerSet replica count")
|
||||||
|
|
||||||
|
runnerList := new(actionsv1alpha1.EphemeralRunnerList)
|
||||||
|
Eventually(
|
||||||
|
func() (bool, error) {
|
||||||
|
err := k8sClient.List(ctx, runnerList, client.InNamespace(ephemeralRunnerSet.Namespace))
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(runnerList.Items) != 3 {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var pendingOriginal *v1alpha1.EphemeralRunner
|
||||||
|
var runningOriginal *v1alpha1.EphemeralRunner
|
||||||
|
var failedOriginal *v1alpha1.EphemeralRunner
|
||||||
|
var empty []*v1alpha1.EphemeralRunner
|
||||||
|
for _, runner := range runnerList.Items {
|
||||||
|
switch runner.Status.RunnerId {
|
||||||
|
case 101:
|
||||||
|
pendingOriginal = runner.DeepCopy()
|
||||||
|
case 102:
|
||||||
|
runningOriginal = runner.DeepCopy()
|
||||||
|
case 103:
|
||||||
|
failedOriginal = runner.DeepCopy()
|
||||||
|
default:
|
||||||
|
empty = append(empty, runner.DeepCopy())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
refetch := false
|
||||||
|
if pendingOriginal == nil { // if NO pending
|
||||||
|
refetch = true
|
||||||
|
pendingOriginal = empty[0]
|
||||||
|
empty = empty[1:]
|
||||||
|
|
||||||
|
pending := pendingOriginal.DeepCopy()
|
||||||
|
pending.Status.RunnerId = 101
|
||||||
|
pending.Status.Phase = corev1.PodPending
|
||||||
|
|
||||||
|
err = k8sClient.Status().Patch(ctx, pending, client.MergeFrom(pendingOriginal))
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if runningOriginal == nil { // if NO running
|
||||||
|
refetch = true
|
||||||
|
runningOriginal = empty[0]
|
||||||
|
empty = empty[1:]
|
||||||
|
running := runningOriginal.DeepCopy()
|
||||||
|
running.Status.RunnerId = 102
|
||||||
|
running.Status.Phase = corev1.PodRunning
|
||||||
|
|
||||||
|
err = k8sClient.Status().Patch(ctx, running, client.MergeFrom(runningOriginal))
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if failedOriginal == nil { // if NO failed
|
||||||
|
refetch = true
|
||||||
|
failedOriginal = empty[0]
|
||||||
|
|
||||||
|
failed := pendingOriginal.DeepCopy()
|
||||||
|
failed.Status.RunnerId = 103
|
||||||
|
failed.Status.Phase = corev1.PodFailed
|
||||||
|
|
||||||
|
err = k8sClient.Status().Patch(ctx, failed, client.MergeFrom(failedOriginal))
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return !refetch, nil
|
||||||
|
},
|
||||||
|
ephemeralRunnerSetTestTimeout,
|
||||||
|
ephemeralRunnerSetTestInterval,
|
||||||
|
).Should(BeTrue(), "Failed to eventually update to one pending, one running and one failed")
|
||||||
|
|
||||||
|
desiredStatus := v1alpha1.EphemeralRunnerSetStatus{
|
||||||
|
CurrentReplicas: 3,
|
||||||
|
PendingEphemeralRunners: 1,
|
||||||
|
RunningEphemeralRunners: 1,
|
||||||
|
FailedEphemeralRunners: 1,
|
||||||
|
}
|
||||||
|
Eventually(
|
||||||
|
func() (v1alpha1.EphemeralRunnerSetStatus, error) {
|
||||||
|
updated := new(v1alpha1.EphemeralRunnerSet)
|
||||||
|
err := k8sClient.Get(ctx, client.ObjectKey{Name: ephemeralRunnerSet.Name, Namespace: ephemeralRunnerSet.Namespace}, updated)
|
||||||
|
if err != nil {
|
||||||
|
return v1alpha1.EphemeralRunnerSetStatus{}, err
|
||||||
|
}
|
||||||
|
return updated.Status, nil
|
||||||
|
},
|
||||||
|
ephemeralRunnerSetTestTimeout,
|
||||||
|
ephemeralRunnerSetTestInterval,
|
||||||
|
).Should(BeEquivalentTo(desiredStatus), "Status is not eventually updated to the desired one")
|
||||||
|
|
||||||
|
updated = new(v1alpha1.EphemeralRunnerSet)
|
||||||
|
err = k8sClient.Get(ctx, client.ObjectKey{Name: ephemeralRunnerSet.Name, Namespace: ephemeralRunnerSet.Namespace}, updated)
|
||||||
|
Expect(err).NotTo(HaveOccurred(), "Failed to fetch ephemeral runner set")
|
||||||
|
|
||||||
|
updatedOriginal := updated.DeepCopy()
|
||||||
|
updated.Spec.Replicas = 0
|
||||||
|
|
||||||
|
err = k8sClient.Patch(ctx, updated, client.MergeFrom(updatedOriginal))
|
||||||
|
Expect(err).NotTo(HaveOccurred(), "Failed to patch ephemeral runner set with 0 replicas")
|
||||||
|
|
||||||
|
Eventually(
|
||||||
|
func() (int, error) {
|
||||||
|
runnerList = new(actionsv1alpha1.EphemeralRunnerList)
|
||||||
|
err := k8sClient.List(ctx, runnerList, client.InNamespace(ephemeralRunnerSet.Namespace))
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
return len(runnerList.Items), nil
|
||||||
|
},
|
||||||
|
ephemeralRunnerSetTestTimeout,
|
||||||
|
ephemeralRunnerSetTestInterval,
|
||||||
|
).Should(BeEquivalentTo(1), "Failed to eventually scale down")
|
||||||
|
|
||||||
|
desiredStatus = v1alpha1.EphemeralRunnerSetStatus{
|
||||||
|
CurrentReplicas: 1,
|
||||||
|
PendingEphemeralRunners: 0,
|
||||||
|
RunningEphemeralRunners: 0,
|
||||||
|
FailedEphemeralRunners: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
Eventually(
|
||||||
|
func() (v1alpha1.EphemeralRunnerSetStatus, error) {
|
||||||
|
updated := new(v1alpha1.EphemeralRunnerSet)
|
||||||
|
err := k8sClient.Get(ctx, client.ObjectKey{Name: ephemeralRunnerSet.Name, Namespace: ephemeralRunnerSet.Namespace}, updated)
|
||||||
|
if err != nil {
|
||||||
|
return v1alpha1.EphemeralRunnerSetStatus{}, err
|
||||||
|
}
|
||||||
|
return updated.Status, nil
|
||||||
|
},
|
||||||
|
ephemeralRunnerSetTestTimeout,
|
||||||
|
ephemeralRunnerSetTestInterval,
|
||||||
|
).Should(BeEquivalentTo(desiredStatus), "Status is not eventually updated to the desired one")
|
||||||
|
|
||||||
|
err = k8sClient.Delete(ctx, &runnerList.Items[0])
|
||||||
|
Expect(err).To(BeNil(), "Failed to delete failed ephemeral runner")
|
||||||
|
|
||||||
|
desiredStatus = v1alpha1.EphemeralRunnerSetStatus{} // empty
|
||||||
|
Eventually(
|
||||||
|
func() (v1alpha1.EphemeralRunnerSetStatus, error) {
|
||||||
|
updated := new(v1alpha1.EphemeralRunnerSet)
|
||||||
|
err := k8sClient.Get(ctx, client.ObjectKey{Name: ephemeralRunnerSet.Name, Namespace: ephemeralRunnerSet.Namespace}, updated)
|
||||||
|
if err != nil {
|
||||||
|
return v1alpha1.EphemeralRunnerSetStatus{}, err
|
||||||
|
}
|
||||||
|
return updated.Status, nil
|
||||||
|
},
|
||||||
|
ephemeralRunnerSetTestTimeout,
|
||||||
|
ephemeralRunnerSetTestInterval,
|
||||||
|
).Should(BeEquivalentTo(desiredStatus), "Status is not eventually updated to the desired one")
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -821,12 +996,13 @@ var _ = Describe("Test EphemeralRunnerSet controller with proxy settings", func(
|
|||||||
err = k8sClient.Status().Patch(ctx, runner, client.MergeFrom(&runnerList.Items[0]))
|
err = k8sClient.Status().Patch(ctx, runner, client.MergeFrom(&runnerList.Items[0]))
|
||||||
Expect(err).NotTo(HaveOccurred(), "failed to update ephemeral runner status")
|
Expect(err).NotTo(HaveOccurred(), "failed to update ephemeral runner status")
|
||||||
|
|
||||||
updatedRunnerSet := new(actionsv1alpha1.EphemeralRunnerSet)
|
runnerSet := new(actionsv1alpha1.EphemeralRunnerSet)
|
||||||
err = k8sClient.Get(ctx, client.ObjectKey{Namespace: ephemeralRunnerSet.Namespace, Name: ephemeralRunnerSet.Name}, updatedRunnerSet)
|
err = k8sClient.Get(ctx, client.ObjectKey{Namespace: ephemeralRunnerSet.Namespace, Name: ephemeralRunnerSet.Name}, runnerSet)
|
||||||
Expect(err).NotTo(HaveOccurred(), "failed to get EphemeralRunnerSet")
|
Expect(err).NotTo(HaveOccurred(), "failed to get EphemeralRunnerSet")
|
||||||
|
|
||||||
|
updatedRunnerSet := runnerSet.DeepCopy()
|
||||||
updatedRunnerSet.Spec.Replicas = 0
|
updatedRunnerSet.Spec.Replicas = 0
|
||||||
err = k8sClient.Update(ctx, updatedRunnerSet)
|
err = k8sClient.Patch(ctx, updatedRunnerSet, client.MergeFrom(runnerSet))
|
||||||
Expect(err).NotTo(HaveOccurred(), "failed to update EphemeralRunnerSet")
|
Expect(err).NotTo(HaveOccurred(), "failed to update EphemeralRunnerSet")
|
||||||
|
|
||||||
Eventually(
|
Eventually(
|
||||||
|
|||||||
@@ -124,6 +124,31 @@ https://user-images.githubusercontent.com/568794/212668313-8946ddc5-60c1-461f-a7
|
|||||||
arc-runners arc-runner-set-rmrgw-runner-p9p5n 1/1 Running 0 21s
|
arc-runners arc-runner-set-rmrgw-runner-p9p5n 1/1 Running 0 21s
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Upgrade to newer versions
|
||||||
|
|
||||||
|
Upgrading actions-runner-controller requires a few extra steps because CRDs will not be automatically upgraded (this is a helm limitation).
|
||||||
|
|
||||||
|
1. Uninstall the autoscaling runner set first
|
||||||
|
|
||||||
|
```bash
|
||||||
|
INSTALLATION_NAME="arc-runner-set"
|
||||||
|
NAMESPACE="arc-runners"
|
||||||
|
helm uninstall "${INSTALLATION_NAME}" --namespace "${NAMESPACE}"
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Wait for all the pods to drain
|
||||||
|
|
||||||
|
1. Pull the new helm chart, unpack it and update the CRDs. When applying this step, don't forget to replace `<PATH>` with the path of the `gha-runner-scale-set-controller` helm chart:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
helm pull oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set-controller \
|
||||||
|
--version 0.3.0 \
|
||||||
|
--untar && \
|
||||||
|
kubectl replace -f <PATH>/gha-runner-scale-set-controller/crds/
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Reinstall actions-runner-controller using the steps from the previous section
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
### Check the logs
|
### Check the logs
|
||||||
|
|||||||
151
main.go
151
main.go
@@ -17,7 +17,6 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
@@ -33,9 +32,7 @@ import (
|
|||||||
"github.com/actions/actions-runner-controller/github/actions"
|
"github.com/actions/actions-runner-controller/github/actions"
|
||||||
"github.com/actions/actions-runner-controller/logging"
|
"github.com/actions/actions-runner-controller/logging"
|
||||||
"github.com/kelseyhightower/envconfig"
|
"github.com/kelseyhightower/envconfig"
|
||||||
corev1 "k8s.io/api/core/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
|
||||||
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
|
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
|
||||||
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
|
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
|
||||||
ctrl "sigs.k8s.io/controller-runtime"
|
ctrl "sigs.k8s.io/controller-runtime"
|
||||||
@@ -47,9 +44,7 @@ const (
|
|||||||
defaultDockerImage = "docker:dind"
|
defaultDockerImage = "docker:dind"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var scheme = runtime.NewScheme()
|
||||||
scheme = runtime.NewScheme()
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
_ = clientgoscheme.AddToScheme(scheme)
|
_ = clientgoscheme.AddToScheme(scheme)
|
||||||
@@ -68,6 +63,7 @@ func (i *stringSlice) Set(value string) error {
|
|||||||
*i = append(*i, value)
|
*i = append(*i, value)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
@@ -170,17 +166,69 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
multiClient := actionssummerwindnet.NewMultiGitHubClient(
|
if autoScalingRunnerSetOnly {
|
||||||
mgr.GetClient(),
|
managerImage := os.Getenv("CONTROLLER_MANAGER_CONTAINER_IMAGE")
|
||||||
ghClient,
|
if managerImage == "" {
|
||||||
)
|
log.Error(err, "unable to obtain listener image")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
managerNamespace := os.Getenv("CONTROLLER_MANAGER_POD_NAMESPACE")
|
||||||
|
if managerNamespace == "" {
|
||||||
|
log.Error(err, "unable to obtain manager pod namespace")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
actionsMultiClient := actions.NewMultiClient(
|
actionsMultiClient := actions.NewMultiClient(
|
||||||
"actions-runner-controller/"+build.Version,
|
"actions-runner-controller/"+build.Version,
|
||||||
log.WithName("actions-clients"),
|
log.WithName("actions-clients"),
|
||||||
)
|
)
|
||||||
|
|
||||||
if !autoScalingRunnerSetOnly {
|
if err = (&actionsgithubcom.AutoscalingRunnerSetReconciler{
|
||||||
|
Client: mgr.GetClient(),
|
||||||
|
Log: log.WithName("AutoscalingRunnerSet"),
|
||||||
|
Scheme: mgr.GetScheme(),
|
||||||
|
ControllerNamespace: managerNamespace,
|
||||||
|
DefaultRunnerScaleSetListenerImage: managerImage,
|
||||||
|
ActionsClient: actionsMultiClient,
|
||||||
|
DefaultRunnerScaleSetListenerImagePullSecrets: autoScalerImagePullSecrets,
|
||||||
|
}).SetupWithManager(mgr); err != nil {
|
||||||
|
log.Error(err, "unable to create controller", "controller", "AutoscalingRunnerSet")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = (&actionsgithubcom.EphemeralRunnerReconciler{
|
||||||
|
Client: mgr.GetClient(),
|
||||||
|
Log: log.WithName("EphemeralRunner"),
|
||||||
|
Scheme: mgr.GetScheme(),
|
||||||
|
ActionsClient: actionsMultiClient,
|
||||||
|
}).SetupWithManager(mgr); err != nil {
|
||||||
|
log.Error(err, "unable to create controller", "controller", "EphemeralRunner")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = (&actionsgithubcom.EphemeralRunnerSetReconciler{
|
||||||
|
Client: mgr.GetClient(),
|
||||||
|
Log: log.WithName("EphemeralRunnerSet"),
|
||||||
|
Scheme: mgr.GetScheme(),
|
||||||
|
ActionsClient: actionsMultiClient,
|
||||||
|
}).SetupWithManager(mgr); err != nil {
|
||||||
|
log.Error(err, "unable to create controller", "controller", "EphemeralRunnerSet")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
if err = (&actionsgithubcom.AutoscalingListenerReconciler{
|
||||||
|
Client: mgr.GetClient(),
|
||||||
|
Log: log.WithName("AutoscalingListener"),
|
||||||
|
Scheme: mgr.GetScheme(),
|
||||||
|
}).SetupWithManager(mgr); err != nil {
|
||||||
|
log.Error(err, "unable to create controller", "controller", "AutoscalingListener")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
multiClient := actionssummerwindnet.NewMultiGitHubClient(
|
||||||
|
mgr.GetClient(),
|
||||||
|
ghClient,
|
||||||
|
)
|
||||||
|
|
||||||
runnerReconciler := &actionssummerwindnet.RunnerReconciler{
|
runnerReconciler := &actionssummerwindnet.RunnerReconciler{
|
||||||
Client: mgr.GetClient(),
|
Client: mgr.GetClient(),
|
||||||
Log: log.WithName("runner"),
|
Log: log.WithName("runner"),
|
||||||
@@ -314,86 +362,6 @@ func main() {
|
|||||||
log.Error(err, "unable to create webhook", "webhook", "RunnerReplicaSet")
|
log.Error(err, "unable to create webhook", "webhook", "RunnerReplicaSet")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We use this environment avariable to turn on the ScaleSet related controllers.
|
|
||||||
// Otherwise ARC's legacy chart is unable to deploy a working ARC controller-manager pod,
|
|
||||||
// due to that the chart does not contain new actions.* CRDs while ARC requires those CRDs.
|
|
||||||
//
|
|
||||||
// We might have used a more explicitly named environment variable for this,
|
|
||||||
// e.g. "CONTROLLER_MANAGER_ENABLE_SCALE_SET" to explicitly enable the new controllers,
|
|
||||||
// or "CONTROLLER_MANAGER_DISABLE_SCALE_SET" to explicitly disable the new controllers.
|
|
||||||
// However, doing so would affect either private ARC testers or current ARC users
|
|
||||||
// who run ARC without those variabls.
|
|
||||||
mgrPodName := os.Getenv("CONTROLLER_MANAGER_POD_NAME")
|
|
||||||
if mgrPodName != "" {
|
|
||||||
mgrPodNamespace := os.Getenv("CONTROLLER_MANAGER_POD_NAMESPACE")
|
|
||||||
var mgrPod corev1.Pod
|
|
||||||
err = mgr.GetAPIReader().Get(context.Background(), types.NamespacedName{Namespace: mgrPodNamespace, Name: mgrPodName}, &mgrPod)
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err, fmt.Sprintf("unable to obtain manager pod: %s (%s)", mgrPodName, mgrPodNamespace))
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
var mgrContainer *corev1.Container
|
|
||||||
for _, container := range mgrPod.Spec.Containers {
|
|
||||||
if container.Name == "manager" {
|
|
||||||
mgrContainer = &container
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if mgrContainer != nil {
|
|
||||||
log.Info("Detected manager container", "image", mgrContainer.Image)
|
|
||||||
} else {
|
|
||||||
log.Error(err, "unable to obtain manager container image")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
if err = (&actionsgithubcom.AutoscalingRunnerSetReconciler{
|
|
||||||
Client: mgr.GetClient(),
|
|
||||||
Log: log.WithName("AutoscalingRunnerSet"),
|
|
||||||
Scheme: mgr.GetScheme(),
|
|
||||||
ControllerNamespace: mgrPodNamespace,
|
|
||||||
DefaultRunnerScaleSetListenerImage: mgrContainer.Image,
|
|
||||||
ActionsClient: actionsMultiClient,
|
|
||||||
DefaultRunnerScaleSetListenerImagePullSecrets: autoScalerImagePullSecrets,
|
|
||||||
}).SetupWithManager(mgr); err != nil {
|
|
||||||
log.Error(err, "unable to create controller", "controller", "AutoscalingRunnerSet")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = (&actionsgithubcom.EphemeralRunnerReconciler{
|
|
||||||
Client: mgr.GetClient(),
|
|
||||||
Log: log.WithName("EphemeralRunner"),
|
|
||||||
Scheme: mgr.GetScheme(),
|
|
||||||
ActionsClient: actionsMultiClient,
|
|
||||||
}).SetupWithManager(mgr); err != nil {
|
|
||||||
log.Error(err, "unable to create controller", "controller", "EphemeralRunner")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = (&actionsgithubcom.EphemeralRunnerSetReconciler{
|
|
||||||
Client: mgr.GetClient(),
|
|
||||||
Log: log.WithName("EphemeralRunnerSet"),
|
|
||||||
Scheme: mgr.GetScheme(),
|
|
||||||
ActionsClient: actionsMultiClient,
|
|
||||||
}).SetupWithManager(mgr); err != nil {
|
|
||||||
log.Error(err, "unable to create controller", "controller", "EphemeralRunnerSet")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
if err = (&actionsgithubcom.AutoscalingListenerReconciler{
|
|
||||||
Client: mgr.GetClient(),
|
|
||||||
Log: log.WithName("AutoscalingListener"),
|
|
||||||
Scheme: mgr.GetScheme(),
|
|
||||||
}).SetupWithManager(mgr); err != nil {
|
|
||||||
log.Error(err, "unable to create controller", "controller", "AutoscalingListener")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
// +kubebuilder:scaffold:builder
|
|
||||||
}
|
|
||||||
|
|
||||||
if !disableAdmissionWebhook && !autoScalingRunnerSetOnly {
|
|
||||||
injector := &actionssummerwindnet.PodRunnerTokenInjector{
|
injector := &actionssummerwindnet.PodRunnerTokenInjector{
|
||||||
Client: mgr.GetClient(),
|
Client: mgr.GetClient(),
|
||||||
GitHubClient: multiClient,
|
GitHubClient: multiClient,
|
||||||
@@ -404,6 +372,7 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
log.Info("starting manager")
|
log.Info("starting manager")
|
||||||
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
|
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
|
||||||
|
|||||||
@@ -79,14 +79,34 @@ func (reader *EventReader) ProcessWorkflowJobEvent(ctx context.Context, event in
|
|||||||
labels["repository_full_name"] = *n
|
labels["repository_full_name"] = *n
|
||||||
keysAndValues = append(keysAndValues, "repository_full_name", *n)
|
keysAndValues = append(keysAndValues, "repository_full_name", *n)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if e.Repo.Owner != nil {
|
||||||
|
if l := e.Repo.Owner.Login; l != nil {
|
||||||
|
labels["owner"] = *l
|
||||||
|
keysAndValues = append(keysAndValues, "owner", *l)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var org string
|
||||||
if e.Org != nil {
|
if e.Org != nil {
|
||||||
if n := e.Org.Name; n != nil {
|
if n := e.Org.Name; n != nil {
|
||||||
labels["organization"] = *e.Org.Name
|
org = *n
|
||||||
keysAndValues = append(keysAndValues, "organization", *n)
|
keysAndValues = append(keysAndValues, "organization", *n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
labels["organization"] = org
|
||||||
|
|
||||||
|
var wn string
|
||||||
|
if e.WorkflowJob != nil {
|
||||||
|
if n := e.WorkflowJob.WorkflowName; n != nil {
|
||||||
|
wn = *n
|
||||||
|
keysAndValues = append(keysAndValues, "workflow_name", *n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
labels["workflow_name"] = wn
|
||||||
|
|
||||||
|
log := reader.Log.WithValues(keysAndValues...)
|
||||||
|
|
||||||
// switch on job status
|
// switch on job status
|
||||||
switch action := e.GetAction(); action {
|
switch action := e.GetAction(); action {
|
||||||
@@ -102,14 +122,10 @@ func (reader *EventReader) ProcessWorkflowJobEvent(ctx context.Context, event in
|
|||||||
|
|
||||||
parseResult, err := reader.fetchAndParseWorkflowJobLogs(ctx, e)
|
parseResult, err := reader.fetchAndParseWorkflowJobLogs(ctx, e)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
reader.Log.Error(err, "reading workflow job log")
|
log.Error(err, "reading workflow job log")
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
reader.Log.WithValues("job_name", *e.WorkflowJob.Name, "job_id", fmt.Sprint(*e.WorkflowJob.ID), "repository", *e.Repo.Name, "repository_full_name", *e.Repo.FullName)
|
log.Info("reading workflow_job logs")
|
||||||
if len(*e.Org.Name) > 0 {
|
|
||||||
reader.Log.WithValues("organization", *e.Org.Name)
|
|
||||||
}
|
|
||||||
reader.Log.Info("reading workflow_job logs")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
githubWorkflowJobQueueDurationSeconds.With(labels).Observe(parseResult.QueueTime.Seconds())
|
githubWorkflowJobQueueDurationSeconds.With(labels).Observe(parseResult.QueueTime.Seconds())
|
||||||
@@ -122,10 +138,10 @@ func (reader *EventReader) ProcessWorkflowJobEvent(ctx context.Context, event in
|
|||||||
|
|
||||||
parseResult, err := reader.fetchAndParseWorkflowJobLogs(ctx, e)
|
parseResult, err := reader.fetchAndParseWorkflowJobLogs(ctx, e)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
reader.Log.Error(err, "reading workflow job log")
|
log.Error(err, "reading workflow job log")
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
reader.Log.Info("reading workflow_job logs", keysAndValues...)
|
log.Info("reading workflow_job logs", keysAndValues...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if *e.WorkflowJob.Conclusion == "failure" {
|
if *e.WorkflowJob.Conclusion == "failure" {
|
||||||
|
|||||||
@@ -71,14 +71,19 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func metricLabels(extras ...string) []string {
|
||||||
|
return append(append([]string{}, commonLabels...), extras...)
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
commonLabels = []string{"runs_on", "job_name", "organization", "repository", "repository_full_name", "owner", "workflow_name"}
|
||||||
githubWorkflowJobQueueDurationSeconds = prometheus.NewHistogramVec(
|
githubWorkflowJobQueueDurationSeconds = prometheus.NewHistogramVec(
|
||||||
prometheus.HistogramOpts{
|
prometheus.HistogramOpts{
|
||||||
Name: "github_workflow_job_queue_duration_seconds",
|
Name: "github_workflow_job_queue_duration_seconds",
|
||||||
Help: "Queue times for workflow jobs in seconds",
|
Help: "Queue times for workflow jobs in seconds",
|
||||||
Buckets: runtimeBuckets,
|
Buckets: runtimeBuckets,
|
||||||
},
|
},
|
||||||
[]string{"runs_on", "job_name"},
|
metricLabels(),
|
||||||
)
|
)
|
||||||
githubWorkflowJobRunDurationSeconds = prometheus.NewHistogramVec(
|
githubWorkflowJobRunDurationSeconds = prometheus.NewHistogramVec(
|
||||||
prometheus.HistogramOpts{
|
prometheus.HistogramOpts{
|
||||||
@@ -86,41 +91,41 @@ var (
|
|||||||
Help: "Run times for workflow jobs in seconds",
|
Help: "Run times for workflow jobs in seconds",
|
||||||
Buckets: runtimeBuckets,
|
Buckets: runtimeBuckets,
|
||||||
},
|
},
|
||||||
[]string{"runs_on", "job_name", "job_conclusion"},
|
metricLabels("job_conclusion"),
|
||||||
)
|
)
|
||||||
githubWorkflowJobConclusionsTotal = prometheus.NewCounterVec(
|
githubWorkflowJobConclusionsTotal = prometheus.NewCounterVec(
|
||||||
prometheus.CounterOpts{
|
prometheus.CounterOpts{
|
||||||
Name: "github_workflow_job_conclusions_total",
|
Name: "github_workflow_job_conclusions_total",
|
||||||
Help: "Conclusions for tracked workflow jobs",
|
Help: "Conclusions for tracked workflow jobs",
|
||||||
},
|
},
|
||||||
[]string{"runs_on", "job_name", "job_conclusion"},
|
metricLabels("job_conclusion"),
|
||||||
)
|
)
|
||||||
githubWorkflowJobsQueuedTotal = prometheus.NewCounterVec(
|
githubWorkflowJobsQueuedTotal = prometheus.NewCounterVec(
|
||||||
prometheus.CounterOpts{
|
prometheus.CounterOpts{
|
||||||
Name: "github_workflow_jobs_queued_total",
|
Name: "github_workflow_jobs_queued_total",
|
||||||
Help: "Total count of workflow jobs queued (events where job_status=queued)",
|
Help: "Total count of workflow jobs queued (events where job_status=queued)",
|
||||||
},
|
},
|
||||||
[]string{"runs_on", "job_name"},
|
metricLabels(),
|
||||||
)
|
)
|
||||||
githubWorkflowJobsStartedTotal = prometheus.NewCounterVec(
|
githubWorkflowJobsStartedTotal = prometheus.NewCounterVec(
|
||||||
prometheus.CounterOpts{
|
prometheus.CounterOpts{
|
||||||
Name: "github_workflow_jobs_started_total",
|
Name: "github_workflow_jobs_started_total",
|
||||||
Help: "Total count of workflow jobs started (events where job_status=in_progress)",
|
Help: "Total count of workflow jobs started (events where job_status=in_progress)",
|
||||||
},
|
},
|
||||||
[]string{"runs_on", "job_name"},
|
metricLabels(),
|
||||||
)
|
)
|
||||||
githubWorkflowJobsCompletedTotal = prometheus.NewCounterVec(
|
githubWorkflowJobsCompletedTotal = prometheus.NewCounterVec(
|
||||||
prometheus.CounterOpts{
|
prometheus.CounterOpts{
|
||||||
Name: "github_workflow_jobs_completed_total",
|
Name: "github_workflow_jobs_completed_total",
|
||||||
Help: "Total count of workflow jobs completed (events where job_status=completed)",
|
Help: "Total count of workflow jobs completed (events where job_status=completed)",
|
||||||
},
|
},
|
||||||
[]string{"runs_on", "job_name"},
|
metricLabels(),
|
||||||
)
|
)
|
||||||
githubWorkflowJobFailuresTotal = prometheus.NewCounterVec(
|
githubWorkflowJobFailuresTotal = prometheus.NewCounterVec(
|
||||||
prometheus.CounterOpts{
|
prometheus.CounterOpts{
|
||||||
Name: "github_workflow_job_failures_total",
|
Name: "github_workflow_job_failures_total",
|
||||||
Help: "Conclusions for tracked workflow runs",
|
Help: "Conclusions for tracked workflow runs",
|
||||||
},
|
},
|
||||||
[]string{"runs_on", "job_name", "failed_step", "exit_code"},
|
metricLabels("failed_step", "exit_code"),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ 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/v47/github"
|
gogithub "github.com/google/go-github/v50/github"
|
||||||
ctrl "sigs.k8s.io/controller-runtime"
|
ctrl "sigs.k8s.io/controller-runtime"
|
||||||
|
|
||||||
"github.com/actions/actions-runner-controller/github"
|
"github.com/actions/actions-runner-controller/github"
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ TARGETPLATFORM ?= $(shell arch)
|
|||||||
|
|
||||||
RUNNER_VERSION ?= 2.302.1
|
RUNNER_VERSION ?= 2.302.1
|
||||||
RUNNER_CONTAINER_HOOKS_VERSION ?= 0.2.0
|
RUNNER_CONTAINER_HOOKS_VERSION ?= 0.2.0
|
||||||
DOCKER_VERSION ?= 20.10.21
|
DOCKER_VERSION ?= 20.10.23
|
||||||
|
|
||||||
# default list of platforms for which multiarch image is built
|
# default list of platforms for which multiarch image is built
|
||||||
ifeq (${PLATFORMS}, )
|
ifeq (${PLATFORMS}, )
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ ARG RUNNER_VERSION
|
|||||||
ARG RUNNER_CONTAINER_HOOKS_VERSION=0.2.0
|
ARG RUNNER_CONTAINER_HOOKS_VERSION=0.2.0
|
||||||
# Docker and Docker Compose arguments
|
# Docker and Docker Compose arguments
|
||||||
ARG CHANNEL=stable
|
ARG CHANNEL=stable
|
||||||
ARG DOCKER_VERSION=20.10.18
|
ARG DOCKER_VERSION=20.10.23
|
||||||
ARG DOCKER_COMPOSE_VERSION=v2.16.0
|
ARG DOCKER_COMPOSE_VERSION=v2.16.0
|
||||||
ARG DUMB_INIT_VERSION=1.2.5
|
ARG DUMB_INIT_VERSION=1.2.5
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ ARG RUNNER_VERSION
|
|||||||
ARG RUNNER_CONTAINER_HOOKS_VERSION=0.2.0
|
ARG RUNNER_CONTAINER_HOOKS_VERSION=0.2.0
|
||||||
# Docker and Docker Compose arguments
|
# Docker and Docker Compose arguments
|
||||||
ARG CHANNEL=stable
|
ARG CHANNEL=stable
|
||||||
ARG DOCKER_VERSION=20.10.21
|
ARG DOCKER_VERSION=20.10.23
|
||||||
ARG DOCKER_COMPOSE_VERSION=v2.16.0
|
ARG DOCKER_COMPOSE_VERSION=v2.16.0
|
||||||
ARG DUMB_INIT_VERSION=1.2.5
|
ARG DUMB_INIT_VERSION=1.2.5
|
||||||
ARG RUNNER_USER_UID=1001
|
ARG RUNNER_USER_UID=1001
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ ARG RUNNER_VERSION
|
|||||||
ARG RUNNER_CONTAINER_HOOKS_VERSION=0.2.0
|
ARG RUNNER_CONTAINER_HOOKS_VERSION=0.2.0
|
||||||
# Docker and Docker Compose arguments
|
# Docker and Docker Compose arguments
|
||||||
ARG CHANNEL=stable
|
ARG CHANNEL=stable
|
||||||
ARG DOCKER_VERSION=20.10.18
|
ARG DOCKER_VERSION=20.10.23
|
||||||
ARG DOCKER_COMPOSE_VERSION=v2.16.0
|
ARG DOCKER_COMPOSE_VERSION=v2.16.0
|
||||||
ARG DUMB_INIT_VERSION=1.2.5
|
ARG DUMB_INIT_VERSION=1.2.5
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ ARG RUNNER_VERSION
|
|||||||
ARG RUNNER_CONTAINER_HOOKS_VERSION=0.2.0
|
ARG RUNNER_CONTAINER_HOOKS_VERSION=0.2.0
|
||||||
# Docker and Docker Compose arguments
|
# Docker and Docker Compose arguments
|
||||||
ARG CHANNEL=stable
|
ARG CHANNEL=stable
|
||||||
ARG DOCKER_VERSION=20.10.21
|
ARG DOCKER_VERSION=20.10.23
|
||||||
ARG DOCKER_COMPOSE_VERSION=v2.16.0
|
ARG DOCKER_COMPOSE_VERSION=v2.16.0
|
||||||
ARG DUMB_INIT_VERSION=1.2.5
|
ARG DUMB_INIT_VERSION=1.2.5
|
||||||
ARG RUNNER_USER_UID=1001
|
ARG RUNNER_USER_UID=1001
|
||||||
|
|||||||
Reference in New Issue
Block a user