Merge branch 'master' into feature/mtls-proxy-support
This commit is contained in:
commit
b1f916ee90
|
|
@ -103,6 +103,7 @@ jobs:
|
|||
- name: Build & push controller image
|
||||
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
build-args: VERSION=${{ inputs.release_tag_name }}
|
||||
|
|
|
|||
|
|
@ -65,82 +65,63 @@ jobs:
|
|||
echo "$changed" >> $GITHUB_OUTPUT
|
||||
echo "EOF" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Install helm-unittest
|
||||
if: |
|
||||
contains(steps.list-changed.outputs.changed_charts, 'charts/gha-runner-scale-set-controller-experimental') ||
|
||||
contains(steps.list-changed.outputs.changed_charts, 'charts/gha-runner-scale-set-experimental')
|
||||
run: |
|
||||
helm plugin install https://github.com/helm-unittest/helm-unittest.git
|
||||
|
||||
- name: Run helm-unittest (gha-runner-scale-set-controller-experimental)
|
||||
if: contains(steps.list-changed.outputs.changed_charts, 'charts/gha-runner-scale-set-controller-experimental')
|
||||
run: |
|
||||
helm unittest ./charts/gha-runner-scale-set-controller-experimental/
|
||||
|
||||
- name: Run helm-unittest (gha-runner-scale-set-experimental)
|
||||
if: contains(steps.list-changed.outputs.changed_charts, 'charts/gha-runner-scale-set-experimental')
|
||||
run: |
|
||||
helm unittest ./charts/gha-runner-scale-set-experimental/
|
||||
|
||||
- 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@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd
|
||||
if: steps.list-changed.outputs.changed == 'true'
|
||||
with:
|
||||
version: latest
|
||||
|
||||
- name: Build controller image
|
||||
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f
|
||||
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@ef37e7f390d99f746eb8b610417061a60e82a6cc
|
||||
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
|
||||
test-chart:
|
||||
name: Test Chart
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Helm
|
||||
uses: azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4
|
||||
with:
|
||||
version: ${{ env.HELM_VERSION }}
|
||||
|
||||
# python is a requirement for the chart-testing action below (supports yamllint among other tests)
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: "3.11"
|
||||
|
||||
- name: Set up chart-testing
|
||||
uses: helm/chart-testing-action@6ec842c01de15ebb84c8627d2744a0c2f2755c9f
|
||||
|
||||
- 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 "changed=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
echo "changed_charts<<EOF" >> $GITHUB_OUTPUT
|
||||
echo "$changed" >> $GITHUB_OUTPUT
|
||||
echo "EOF" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Install helm-unittest
|
||||
run: |
|
||||
helm plugin install https://github.com/helm-unittest/helm-unittest.git
|
||||
|
||||
- name: Run helm-unittest (gha-runner-scale-set-controller-experimental)
|
||||
run: |
|
||||
helm unittest ./charts/gha-runner-scale-set-controller-experimental/
|
||||
|
||||
- name: Run helm-unittest (gha-runner-scale-set-experimental)
|
||||
run: |
|
||||
helm unittest ./charts/gha-runner-scale-set-experimental/
|
||||
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version-file: "go.mod"
|
||||
cache: false
|
||||
|
||||
- name: Test gha-runner-scale-set
|
||||
run: go test ./charts/gha-runner-scale-set/...
|
||||
|
||||
- name: Test gha-runner-scale-set-controller
|
||||
run: go test ./charts/gha-runner-scale-set-controller/...
|
||||
- name: Test gha-runner-scale-set-experimental
|
||||
run: go test ./charts/gha-runner-scale-set-experimental/...
|
||||
- name: Test gha-runner-scale-set-controller-experimental
|
||||
run: go test ./charts/gha-runner-scale-set-controller-experimental/...
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
# Build the manager binary
|
||||
FROM --platform=$BUILDPLATFORM golang:1.26.2 AS builder
|
||||
FROM --platform=$BUILDPLATFORM golang:1.26.3 AS builder
|
||||
|
||||
WORKDIR /workspace
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ import (
|
|||
// +kubebuilder:printcolumn:JSONPath=".spec.minRunners",name=Minimum Runners,type=integer
|
||||
// +kubebuilder:printcolumn:JSONPath=".spec.maxRunners",name=Maximum Runners,type=integer
|
||||
// +kubebuilder:printcolumn:JSONPath=".status.currentRunners",name=Current Runners,type=integer
|
||||
// +kubebuilder:printcolumn:JSONPath=".status.phase",name=Phase,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
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ const EphemeralRunnerContainerName = "runner"
|
|||
// +kubebuilder:subresource:status
|
||||
// +kubebuilder:printcolumn:JSONPath=".spec.githubConfigUrl",name="GitHub Config URL",type=string
|
||||
// +kubebuilder:printcolumn:JSONPath=".status.runnerId",name=RunnerId,type=number
|
||||
// +kubebuilder:printcolumn:JSONPath=".status.phase",name=Phase,type=string
|
||||
// +kubebuilder:printcolumn:JSONPath=".status.phase",name=Status,type=string
|
||||
// +kubebuilder:printcolumn:JSONPath=".status.jobRepositoryName",name=JobRepository,type=string
|
||||
// +kubebuilder:printcolumn:JSONPath=".status.jobWorkflowRef",name=JobWorkflowRef,type=string
|
||||
// +kubebuilder:printcolumn:JSONPath=".status.workflowRunId",name=WorkflowRunId,type=number
|
||||
|
|
|
|||
|
|
@ -64,7 +64,6 @@ const (
|
|||
// +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
|
||||
// +kubebuilder:printcolumn:JSONPath=".status.phase",name=Phase,type=string
|
||||
|
||||
// EphemeralRunnerSet is the Schema for the ephemeralrunnersets API
|
||||
type EphemeralRunnerSet struct {
|
||||
|
|
|
|||
|
|
@ -8,4 +8,6 @@ check-version-increment: false # Disable checking that the chart version has bee
|
|||
charts:
|
||||
- charts/gha-runner-scale-set-controller
|
||||
- charts/gha-runner-scale-set
|
||||
- charts/gha-runner-scale-set-controller-experimental
|
||||
- charts/gha-runner-scale-set-experimental
|
||||
skip-clean-up: true
|
||||
|
|
|
|||
|
|
@ -15,13 +15,13 @@ type: application
|
|||
# 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.
|
||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||
version: "0.14.1"
|
||||
version: "0.14.2"
|
||||
|
||||
# This is the version number of the application being deployed. This version number should be
|
||||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||
# It is recommended to use it with quotes.
|
||||
appVersion: "0.14.1"
|
||||
appVersion: "0.14.2"
|
||||
|
||||
home: https://github.com/actions/actions-runner-controller
|
||||
|
||||
|
|
|
|||
|
|
@ -24,8 +24,8 @@ spec:
|
|||
- jsonPath: .status.currentRunners
|
||||
name: Current Runners
|
||||
type: integer
|
||||
- jsonPath: .status.phase
|
||||
name: Phase
|
||||
- jsonPath: .status.state
|
||||
name: State
|
||||
type: string
|
||||
- jsonPath: .status.pendingEphemeralRunners
|
||||
name: Pending Runners
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ spec:
|
|||
name: RunnerId
|
||||
type: number
|
||||
- jsonPath: .status.phase
|
||||
name: Phase
|
||||
name: Status
|
||||
type: string
|
||||
- jsonPath: .status.jobRepositoryName
|
||||
name: JobRepository
|
||||
|
|
|
|||
|
|
@ -33,9 +33,6 @@ spec:
|
|||
- jsonPath: .status.deletingEphemeralRunners
|
||||
name: Deleting Runners
|
||||
type: integer
|
||||
- jsonPath: .status.phase
|
||||
name: Phase
|
||||
type: string
|
||||
name: v1alpha1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
|
|
|
|||
|
|
@ -73,6 +73,11 @@ args:
|
|||
{{- with .Values.controller.manager.config.k8sClientRateLimiterBurst }}
|
||||
- "--k8s-client-rate-limiter-burst={{ . }}"
|
||||
{{- end }}
|
||||
{{- with .Values.controller.manager.config.rateLimiter }}
|
||||
{{- with .name }}
|
||||
- "--workqueue-rate-limiter={{ . }}"
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.controller.manager.config.healthProbeBindAddress }}
|
||||
- "--health-probe-bind-address={{ . }}"
|
||||
{{- end }}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,66 @@
|
|||
suite: "Controller Deployment rate limiter"
|
||||
templates:
|
||||
- deployment.yaml
|
||||
tests:
|
||||
- it: should omit workqueue-rate-limiter flag by default
|
||||
release:
|
||||
name: "test-arc"
|
||||
namespace: "test-ns"
|
||||
asserts:
|
||||
- notContains:
|
||||
path: spec.template.spec.containers[0].args
|
||||
content: "--workqueue-rate-limiter=bucket_rate_limiter"
|
||||
- notContains:
|
||||
path: spec.template.spec.containers[0].args
|
||||
content: "--workqueue-rate-limiter=typed_rate_limiter"
|
||||
|
||||
- it: should include workqueue-rate-limiter flag when bucket_rate_limiter is configured
|
||||
set:
|
||||
controller:
|
||||
manager:
|
||||
config:
|
||||
rateLimiter:
|
||||
name: "bucket_rate_limiter"
|
||||
release:
|
||||
name: "test-arc"
|
||||
namespace: "test-ns"
|
||||
asserts:
|
||||
- contains:
|
||||
path: spec.template.spec.containers[0].args
|
||||
content: "--workqueue-rate-limiter=bucket_rate_limiter"
|
||||
|
||||
- it: should include workqueue-rate-limiter flag when typed_rate_limiter is configured
|
||||
set:
|
||||
controller:
|
||||
manager:
|
||||
config:
|
||||
rateLimiter:
|
||||
name: "typed_rate_limiter"
|
||||
release:
|
||||
name: "test-arc"
|
||||
namespace: "test-ns"
|
||||
asserts:
|
||||
- contains:
|
||||
path: spec.template.spec.containers[0].args
|
||||
content: "--workqueue-rate-limiter=typed_rate_limiter"
|
||||
|
||||
- it: should render both config and extraArgs in deterministic order
|
||||
set:
|
||||
controller:
|
||||
manager:
|
||||
config:
|
||||
rateLimiter:
|
||||
name: "bucket_rate_limiter"
|
||||
container:
|
||||
extraArgs:
|
||||
- "--workqueue-rate-limiter=typed_rate_limiter"
|
||||
release:
|
||||
name: "test-arc"
|
||||
namespace: "test-ns"
|
||||
asserts:
|
||||
- contains:
|
||||
path: spec.template.spec.containers[0].args
|
||||
content: "--workqueue-rate-limiter=bucket_rate_limiter"
|
||||
- contains:
|
||||
path: spec.template.spec.containers[0].args
|
||||
content: "--workqueue-rate-limiter=typed_rate_limiter"
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
package tests
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gruntwork-io/terratest/modules/helm"
|
||||
"github.com/gruntwork-io/terratest/modules/k8s"
|
||||
"github.com/gruntwork-io/terratest/modules/logger"
|
||||
"github.com/gruntwork-io/terratest/modules/random"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gopkg.in/yaml.v2"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
)
|
||||
|
||||
type Chart struct {
|
||||
Version string `yaml:"version"`
|
||||
AppVersion string `yaml:"appVersion"`
|
||||
}
|
||||
|
||||
func TestTemplate_RenderedDeployment_UsesChartMetadataLabels(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
helmChartPath, err := filepath.Abs("../../gha-runner-scale-set-controller-experimental")
|
||||
require.NoError(t, err)
|
||||
|
||||
chartContent, err := os.ReadFile(filepath.Join(helmChartPath, "Chart.yaml"))
|
||||
require.NoError(t, err)
|
||||
|
||||
chart := new(Chart)
|
||||
err = yaml.Unmarshal(chartContent, chart)
|
||||
require.NoError(t, err)
|
||||
|
||||
releaseName := "test-arc"
|
||||
namespaceName := "test-" + strings.ToLower(random.UniqueId())
|
||||
|
||||
options := &helm.Options{
|
||||
Logger: logger.Discard,
|
||||
KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
|
||||
}
|
||||
|
||||
output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/deployment.yaml"})
|
||||
|
||||
var deployment appsv1.Deployment
|
||||
helm.UnmarshalK8SYaml(t, output, &deployment)
|
||||
|
||||
assert.Equal(t, "gha-rs-controller-"+chart.Version, deployment.Labels["helm.sh/chart"])
|
||||
assert.Equal(t, chart.AppVersion, deployment.Labels["app.kubernetes.io/version"])
|
||||
}
|
||||
|
|
@ -44,6 +44,13 @@ controller:
|
|||
k8sClientRateLimiterQPS: null
|
||||
k8sClientRateLimiterBurst: null
|
||||
|
||||
## Workqueue rate limiter configuration.
|
||||
## By default, controller-runtime uses a combined rate limiter with both a per-item
|
||||
## exponential backoff and an overall token bucket (10 QPS, 100 bucket size).
|
||||
## Valid names: "bucket_rate_limiter" (default), "typed_rate_limiter" (per-item only, no global token bucket).
|
||||
# rateLimiter:
|
||||
# name: "bucket_rate_limiter"
|
||||
|
||||
# The address the health probe endpoint binds to. Disabled if empty/null.
|
||||
# When set, liveness and readiness probes are added to the controller pod.
|
||||
# healthProbeBindAddress: ":8081"
|
||||
|
|
|
|||
|
|
@ -15,13 +15,13 @@ type: application
|
|||
# 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.
|
||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||
version: 0.14.1
|
||||
version: 0.14.2
|
||||
|
||||
# This is the version number of the application being deployed. This version number should be
|
||||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||
# It is recommended to use it with quotes.
|
||||
appVersion: "0.14.1"
|
||||
appVersion: "0.14.2"
|
||||
|
||||
home: https://github.com/actions/actions-runner-controller
|
||||
|
||||
|
|
|
|||
|
|
@ -24,8 +24,8 @@ spec:
|
|||
- jsonPath: .status.currentRunners
|
||||
name: Current Runners
|
||||
type: integer
|
||||
- jsonPath: .status.phase
|
||||
name: Phase
|
||||
- jsonPath: .status.state
|
||||
name: State
|
||||
type: string
|
||||
- jsonPath: .status.pendingEphemeralRunners
|
||||
name: Pending Runners
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ spec:
|
|||
name: RunnerId
|
||||
type: number
|
||||
- jsonPath: .status.phase
|
||||
name: Phase
|
||||
name: Status
|
||||
type: string
|
||||
- jsonPath: .status.jobRepositoryName
|
||||
name: JobRepository
|
||||
|
|
|
|||
|
|
@ -33,9 +33,6 @@ spec:
|
|||
- jsonPath: .status.deletingEphemeralRunners
|
||||
name: Deleting Runners
|
||||
type: integer
|
||||
- jsonPath: .status.phase
|
||||
name: Phase
|
||||
type: string
|
||||
name: v1alpha1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
|
|
|
|||
|
|
@ -15,13 +15,13 @@ type: application
|
|||
# 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.
|
||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||
version: "0.14.1"
|
||||
version: "0.14.2"
|
||||
|
||||
# This is the version number of the application being deployed. This version number should be
|
||||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||
# It is recommended to use it with quotes.
|
||||
appVersion: "0.14.1"
|
||||
appVersion: "0.14.2"
|
||||
|
||||
home: https://github.com/actions/actions-runner-controller
|
||||
|
||||
|
|
|
|||
|
|
@ -1,61 +0,0 @@
|
|||
package tests
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
v1alpha1 "github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1"
|
||||
"github.com/gruntwork-io/terratest/modules/helm"
|
||||
"github.com/gruntwork-io/terratest/modules/k8s"
|
||||
"github.com/gruntwork-io/terratest/modules/logger"
|
||||
"github.com/gruntwork-io/terratest/modules/random"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
type Chart struct {
|
||||
Version string `yaml:"version"`
|
||||
AppVersion string `yaml:"appVersion"`
|
||||
}
|
||||
|
||||
func TestTemplate_RenderedAutoscalingRunnerSet_UsesChartMetadataLabels(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
helmChartPath, err := filepath.Abs("../../gha-runner-scale-set-experimental")
|
||||
require.NoError(t, err)
|
||||
|
||||
chartContent, err := os.ReadFile(filepath.Join(helmChartPath, "Chart.yaml"))
|
||||
require.NoError(t, err)
|
||||
|
||||
chart := new(Chart)
|
||||
err = yaml.Unmarshal(chartContent, chart)
|
||||
require.NoError(t, err)
|
||||
|
||||
releaseName := "test-runners"
|
||||
namespaceName := "test-" + strings.ToLower(random.UniqueId())
|
||||
|
||||
options := &helm.Options{
|
||||
Logger: logger.Discard,
|
||||
SetValues: map[string]string{
|
||||
"scaleset.name": "test",
|
||||
"auth.url": "https://github.com/actions",
|
||||
"auth.githubToken": "gh_token12345",
|
||||
"controllerServiceAccount.name": "arc",
|
||||
"controllerServiceAccount.namespace": "arc-system",
|
||||
},
|
||||
KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
|
||||
}
|
||||
|
||||
output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/autoscalingrunnserset.yaml"})
|
||||
|
||||
var autoscalingRunnerSet v1alpha1.AutoscalingRunnerSet
|
||||
helm.UnmarshalK8SYaml(t, output, &autoscalingRunnerSet)
|
||||
|
||||
assert.Equal(t, "gha-rs-"+chart.Version, autoscalingRunnerSet.Labels["helm.sh/chart"])
|
||||
assert.Equal(t, chart.AppVersion, autoscalingRunnerSet.Labels["app.kubernetes.io/version"])
|
||||
assert.Equal(t, "gha-rs-"+chart.Version, autoscalingRunnerSet.Spec.Template.Labels["helm.sh/chart"])
|
||||
assert.Equal(t, chart.AppVersion, autoscalingRunnerSet.Spec.Template.Labels["app.kubernetes.io/version"])
|
||||
}
|
||||
|
|
@ -15,13 +15,13 @@ type: application
|
|||
# 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.
|
||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||
version: 0.14.1
|
||||
version: 0.14.2
|
||||
|
||||
# This is the version number of the application being deployed. This version number should be
|
||||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||
# It is recommended to use it with quotes.
|
||||
appVersion: "0.14.1"
|
||||
appVersion: "0.14.2"
|
||||
|
||||
home: https://github.com/actions/actions-runner-controller
|
||||
|
||||
|
|
|
|||
|
|
@ -24,8 +24,8 @@ spec:
|
|||
- jsonPath: .status.currentRunners
|
||||
name: Current Runners
|
||||
type: integer
|
||||
- jsonPath: .status.phase
|
||||
name: Phase
|
||||
- jsonPath: .status.state
|
||||
name: State
|
||||
type: string
|
||||
- jsonPath: .status.pendingEphemeralRunners
|
||||
name: Pending Runners
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ spec:
|
|||
name: RunnerId
|
||||
type: number
|
||||
- jsonPath: .status.phase
|
||||
name: Phase
|
||||
name: Status
|
||||
type: string
|
||||
- jsonPath: .status.jobRepositoryName
|
||||
name: JobRepository
|
||||
|
|
|
|||
|
|
@ -33,9 +33,6 @@ spec:
|
|||
- jsonPath: .status.deletingEphemeralRunners
|
||||
name: Deleting Runners
|
||||
type: integer
|
||||
- jsonPath: .status.phase
|
||||
name: Phase
|
||||
type: string
|
||||
name: v1alpha1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
|
|
|
|||
|
|
@ -138,14 +138,16 @@ func (r *AutoscalingRunnerSetReconciler) Reconcile(ctx context.Context, req ctrl
|
|||
|
||||
if !v1alpha1.IsVersionAllowed(autoscalingRunnerSet.Labels[LabelKeyKubernetesVersion], build.Version) {
|
||||
if err := r.Delete(ctx, autoscalingRunnerSet); err != nil {
|
||||
log.Error(err, "Failed to delete autoscaling runner set on version mismatch",
|
||||
log.Error(
|
||||
err, "Failed to delete autoscaling runner set on version mismatch",
|
||||
"buildVersion", build.Version,
|
||||
"autoscalingRunnerSetVersion", autoscalingRunnerSet.Labels[LabelKeyKubernetesVersion],
|
||||
)
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
log.Info("Autoscaling runner set version doesn't match the build version. Deleting the resource.",
|
||||
log.Info(
|
||||
"Autoscaling runner set version doesn't match the build version. Deleting the resource.",
|
||||
"buildVersion", build.Version,
|
||||
"autoscalingRunnerSetVersion", autoscalingRunnerSet.Labels[LabelKeyKubernetesVersion],
|
||||
)
|
||||
|
|
@ -270,12 +272,12 @@ func (r *AutoscalingRunnerSetReconciler) Reconcile(ctx context.Context, req ctrl
|
|||
// Our listener pod is out of date, so we need to delete it to get a new recreate.
|
||||
listenerValuesHashChanged := listener.Annotations[annotationKeyValuesHash] != autoscalingRunnerSet.Annotations[annotationKeyValuesHash]
|
||||
listenerSpecHashChanged := listener.Annotations[annotationKeyRunnerSpecHash] != autoscalingRunnerSet.ListenerSpecHash()
|
||||
if listenerFound && (listenerValuesHashChanged || listenerSpecHashChanged) {
|
||||
if listenerFound && (listenerValuesHashChanged ||
|
||||
listenerSpecHashChanged ||
|
||||
latestRunnerSet == nil ||
|
||||
listener.Spec.EphemeralRunnerSetName != latestRunnerSet.Name) {
|
||||
log.Info("RunnerScaleSetListener is out of date. Deleting it so that it is recreated", "name", listener.Name)
|
||||
if err := r.Delete(ctx, listener); err != nil {
|
||||
if kerrors.IsNotFound(err) {
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
log.Error(err, "Failed to delete AutoscalingListener resource")
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
|
@ -473,6 +475,7 @@ func (r *AutoscalingRunnerSetReconciler) removeFinalizersFromDependentResources(
|
|||
}
|
||||
|
||||
func (r *AutoscalingRunnerSetReconciler) createRunnerScaleSet(ctx context.Context, autoscalingRunnerSet *v1alpha1.AutoscalingRunnerSet, logger logr.Logger) (ctrl.Result, error) {
|
||||
original := autoscalingRunnerSet.DeepCopy()
|
||||
logger.Info("Creating a new runner scale set")
|
||||
actionsClient, err := r.GetActionsService(ctx, autoscalingRunnerSet)
|
||||
if len(autoscalingRunnerSet.Spec.RunnerScaleSetName) == 0 {
|
||||
|
|
@ -537,7 +540,8 @@ func (r *AutoscalingRunnerSetReconciler) createRunnerScaleSet(ctx context.Contex
|
|||
RunnerSetting: scaleset.RunnerSetting{
|
||||
DisableUpdate: true,
|
||||
},
|
||||
})
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
logger.Error(err, "Failed to create a new runner scale set on Actions service")
|
||||
return ctrl.Result{}, err
|
||||
|
|
@ -556,15 +560,16 @@ func (r *AutoscalingRunnerSetReconciler) createRunnerScaleSet(ctx context.Contex
|
|||
autoscalingRunnerSet.Labels = map[string]string{}
|
||||
}
|
||||
|
||||
autoscalingRunnerSet.Annotations[AnnotationKeyGitHubRunnerScaleSetName] = runnerScaleSet.Name
|
||||
autoscalingRunnerSet.Annotations[runnerScaleSetIDAnnotationKey] = strconv.Itoa(runnerScaleSet.ID)
|
||||
autoscalingRunnerSet.Annotations[AnnotationKeyGitHubRunnerGroupName] = runnerScaleSet.RunnerGroupName
|
||||
if err := applyGitHubURLLabels(autoscalingRunnerSet.Spec.GitHubConfigUrl, autoscalingRunnerSet.Labels); err != nil { // should never happen
|
||||
logger.Error(err, "Failed to apply GitHub URL labels")
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
logger.Info("Adding runner scale set ID, name and runner group name as an annotation and url labels")
|
||||
if err = patch(ctx, r.Client, autoscalingRunnerSet, func(obj *v1alpha1.AutoscalingRunnerSet) {
|
||||
obj.Annotations[AnnotationKeyGitHubRunnerScaleSetName] = runnerScaleSet.Name
|
||||
obj.Annotations[runnerScaleSetIDAnnotationKey] = strconv.Itoa(runnerScaleSet.ID)
|
||||
obj.Annotations[AnnotationKeyGitHubRunnerGroupName] = runnerScaleSet.RunnerGroupName
|
||||
if err := applyGitHubURLLabels(obj.Spec.GitHubConfigUrl, obj.Labels); err != nil { // should never happen
|
||||
logger.Error(err, "Failed to apply GitHub URL labels")
|
||||
}
|
||||
}); err != nil {
|
||||
if err = r.Patch(ctx, autoscalingRunnerSet, client.MergeFrom(original)); err != nil {
|
||||
logger.Error(err, "Failed to add runner scale set ID, name and runner group name as an annotation")
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -85,13 +85,19 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() {
|
|||
return &scaleset.RunnerGroup{ID: 1, Name: groupName}, nil
|
||||
}),
|
||||
scalefake.WithGetRunnerScaleSet(nil, nil),
|
||||
scalefake.WithCreateRunnerScaleSet(&scaleset.RunnerScaleSet{ID: 1, Name: "test-asrs", RunnerGroupID: 1, RunnerGroupName: "testgroup"}, nil),
|
||||
scalefake.WithCreateRunnerScaleSetFunc(func(ctx context.Context, rs *scaleset.RunnerScaleSet) (*scaleset.RunnerScaleSet, error) {
|
||||
// Return a RunnerScaleSet with name matching the requesting ARS
|
||||
runnerGroupMapLock.RLock()
|
||||
groupName := runnerGroupMap[rs.RunnerGroupID]
|
||||
runnerGroupMapLock.RUnlock()
|
||||
return &scaleset.RunnerScaleSet{ID: 1, Name: rs.Name, RunnerGroupID: rs.RunnerGroupID, RunnerGroupName: groupName}, nil
|
||||
}),
|
||||
scalefake.WithUpdateRunnerScaleSetFunc(func(ctx context.Context, scaleSetID int, rs *scaleset.RunnerScaleSet) (*scaleset.RunnerScaleSet, error) {
|
||||
// Return a RunnerScaleSet with the group name corresponding to the runner group ID
|
||||
runnerGroupMapLock.RLock()
|
||||
groupName := runnerGroupMap[rs.RunnerGroupID]
|
||||
runnerGroupMapLock.RUnlock()
|
||||
return &scaleset.RunnerScaleSet{ID: 1, Name: "test-asrs", RunnerGroupID: rs.RunnerGroupID, RunnerGroupName: groupName}, nil
|
||||
return &scaleset.RunnerScaleSet{ID: 1, Name: rs.Name, RunnerGroupID: rs.RunnerGroupID, RunnerGroupName: groupName}, nil
|
||||
}),
|
||||
scalefake.WithDeleteRunnerScaleSet(nil),
|
||||
),
|
||||
|
|
@ -153,7 +159,8 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() {
|
|||
return created.Finalizers[0], nil
|
||||
},
|
||||
autoscalingRunnerSetTestTimeout,
|
||||
autoscalingRunnerSetTestInterval).Should(BeEquivalentTo(autoscalingRunnerSetFinalizerName), "AutoScalingRunnerSet should have a finalizer")
|
||||
autoscalingRunnerSetTestInterval,
|
||||
).Should(BeEquivalentTo(autoscalingRunnerSetFinalizerName), "AutoScalingRunnerSet should have a finalizer")
|
||||
|
||||
// Check if runner scale set is created on service
|
||||
Eventually(
|
||||
|
|
@ -202,7 +209,8 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() {
|
|||
return fmt.Sprintf("%s/%s", created.Labels[LabelKeyGitHubOrganization], created.Labels[LabelKeyGitHubRepository]), nil
|
||||
},
|
||||
autoscalingRunnerSetTestTimeout,
|
||||
autoscalingRunnerSetTestInterval).Should(BeEquivalentTo("owner/repo"), "RunnerScaleSet should be created/fetched and update the AutoScalingRunnerSet's label")
|
||||
autoscalingRunnerSetTestInterval,
|
||||
).Should(BeEquivalentTo("owner/repo"), "RunnerScaleSet should be created/fetched and update the AutoScalingRunnerSet's label")
|
||||
|
||||
// Check if ephemeral runner set is created
|
||||
Eventually(
|
||||
|
|
@ -216,7 +224,8 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() {
|
|||
return len(runnerSetList.Items), nil
|
||||
},
|
||||
autoscalingRunnerSetTestTimeout,
|
||||
autoscalingRunnerSetTestInterval).Should(BeEquivalentTo(1), "Only one EphemeralRunnerSet should be created")
|
||||
autoscalingRunnerSetTestInterval,
|
||||
).Should(BeEquivalentTo(1), "Only one EphemeralRunnerSet should be created")
|
||||
|
||||
// Check if listener is created
|
||||
Eventually(
|
||||
|
|
@ -224,7 +233,8 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() {
|
|||
return k8sClient.Get(ctx, client.ObjectKey{Name: scaleSetListenerName(autoscalingRunnerSet), Namespace: autoscalingRunnerSet.Namespace}, new(v1alpha1.AutoscalingListener))
|
||||
},
|
||||
autoscalingRunnerSetTestTimeout,
|
||||
autoscalingRunnerSetTestInterval).Should(Succeed(), "Listener should be created")
|
||||
autoscalingRunnerSetTestInterval,
|
||||
).Should(Succeed(), "Listener should be created")
|
||||
|
||||
// Check if status is updated
|
||||
runnerSetList := new(v1alpha1.EphemeralRunnerSetList)
|
||||
|
|
@ -242,7 +252,8 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() {
|
|||
return k8sClient.Get(ctx, client.ObjectKey{Name: scaleSetListenerName(autoscalingRunnerSet), Namespace: autoscalingRunnerSet.Namespace}, new(v1alpha1.AutoscalingListener))
|
||||
},
|
||||
autoscalingRunnerSetTestTimeout,
|
||||
autoscalingRunnerSetTestInterval).Should(Succeed(), "Listener should be created")
|
||||
autoscalingRunnerSetTestInterval,
|
||||
).Should(Succeed(), "Listener should be created")
|
||||
|
||||
// Delete the AutoScalingRunnerSet
|
||||
err := k8sClient.Delete(ctx, autoscalingRunnerSet)
|
||||
|
|
@ -259,7 +270,8 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() {
|
|||
return fmt.Errorf("listener is not deleted")
|
||||
},
|
||||
autoscalingRunnerSetTestTimeout,
|
||||
autoscalingRunnerSetTestInterval).Should(Succeed(), "Listener should be deleted")
|
||||
autoscalingRunnerSetTestInterval,
|
||||
).Should(Succeed(), "Listener should be deleted")
|
||||
|
||||
// Check if all the EphemeralRunnerSet is deleted
|
||||
Eventually(
|
||||
|
|
@ -277,7 +289,8 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() {
|
|||
return nil
|
||||
},
|
||||
autoscalingRunnerSetTestTimeout,
|
||||
autoscalingRunnerSetTestInterval).Should(Succeed(), "All EphemeralRunnerSet should be deleted")
|
||||
autoscalingRunnerSetTestInterval,
|
||||
).Should(Succeed(), "All EphemeralRunnerSet should be deleted")
|
||||
|
||||
// Check if the AutoScalingRunnerSet is deleted
|
||||
Eventually(
|
||||
|
|
@ -290,10 +303,144 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() {
|
|||
return fmt.Errorf("AutoScalingRunnerSet is not deleted")
|
||||
},
|
||||
autoscalingRunnerSetTestTimeout,
|
||||
autoscalingRunnerSetTestInterval).Should(Succeed(), "AutoScalingRunnerSet should be deleted")
|
||||
autoscalingRunnerSetTestInterval,
|
||||
).Should(Succeed(), "AutoScalingRunnerSet should be deleted")
|
||||
})
|
||||
})
|
||||
|
||||
It("should not churn listener when already referencing latest ERS (no-op stability)", func() {
|
||||
min := 1
|
||||
max := 10
|
||||
testARSName := "test-asrs-no-churn"
|
||||
testARSNamespace := autoscalingNS.Name
|
||||
|
||||
testARS := &v1alpha1.AutoscalingRunnerSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: testARSName,
|
||||
Namespace: testARSNamespace,
|
||||
Labels: map[string]string{
|
||||
LabelKeyKubernetesVersion: buildVersion,
|
||||
},
|
||||
},
|
||||
Spec: v1alpha1.AutoscalingRunnerSetSpec{
|
||||
GitHubConfigUrl: "https://github.com/owner/repo",
|
||||
GitHubConfigSecret: configSecret.Name,
|
||||
MaxRunners: &max,
|
||||
MinRunners: &min,
|
||||
RunnerGroup: "testgroup",
|
||||
Template: corev1.PodTemplateSpec{
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Name: "runner",
|
||||
Image: "ghcr.io/actions/runner",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := k8sClient.Create(ctx, testARS)
|
||||
Expect(err).NotTo(HaveOccurred(), "failed to create test AutoScalingRunnerSet")
|
||||
|
||||
var latestERSName string
|
||||
Eventually(
|
||||
func() (string, error) {
|
||||
runnerSetList := new(v1alpha1.EphemeralRunnerSetList)
|
||||
err := k8sClient.List(ctx, runnerSetList, client.InNamespace(testARSNamespace))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Filter to only ERS owned by our test ARS
|
||||
var ownedByTestARS []v1alpha1.EphemeralRunnerSet
|
||||
for _, ers := range runnerSetList.Items {
|
||||
for _, owner := range ers.OwnerReferences {
|
||||
if owner.UID == testARS.UID {
|
||||
ownedByTestARS = append(ownedByTestARS, ers)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(ownedByTestARS) != 1 {
|
||||
return "", fmt.Errorf("expected 1 EphemeralRunnerSet owned by test ARS, got %d", len(ownedByTestARS))
|
||||
}
|
||||
|
||||
return ownedByTestARS[0].Name, nil
|
||||
},
|
||||
autoscalingRunnerSetTestTimeout,
|
||||
autoscalingRunnerSetTestInterval,
|
||||
).Should(Not(BeEmpty()), "ERS should be created")
|
||||
|
||||
// Capture the latest ERS name
|
||||
runnerSetList := new(v1alpha1.EphemeralRunnerSetList)
|
||||
err = k8sClient.List(ctx, runnerSetList, client.InNamespace(testARSNamespace))
|
||||
Expect(err).NotTo(HaveOccurred(), "failed to list EphemeralRunnerSet")
|
||||
|
||||
var ownedByTestARS []v1alpha1.EphemeralRunnerSet
|
||||
for _, ers := range runnerSetList.Items {
|
||||
for _, owner := range ers.OwnerReferences {
|
||||
if owner.UID == testARS.UID {
|
||||
ownedByTestARS = append(ownedByTestARS, ers)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
Expect(len(ownedByTestARS)).To(Equal(1), "should have exactly 1 EphemeralRunnerSet owned by test ARS")
|
||||
latestERSName = ownedByTestARS[0].Name
|
||||
|
||||
listener := new(v1alpha1.AutoscalingListener)
|
||||
Eventually(
|
||||
func() (string, error) {
|
||||
err := k8sClient.Get(ctx, client.ObjectKey{Name: scaleSetListenerName(testARS), Namespace: testARSNamespace}, listener)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return listener.Spec.EphemeralRunnerSetName, nil
|
||||
},
|
||||
autoscalingRunnerSetTestTimeout,
|
||||
autoscalingRunnerSetTestInterval,
|
||||
).Should(Equal(latestERSName), "listener should reference the latest ERS")
|
||||
|
||||
// Capture listener identity (UID and ResourceVersion)
|
||||
err = k8sClient.Get(ctx, client.ObjectKey{Name: scaleSetListenerName(testARS), Namespace: testARSNamespace}, listener)
|
||||
Expect(err).NotTo(HaveOccurred(), "failed to get listener")
|
||||
originalUID := listener.UID
|
||||
originalResourceVersion := listener.ResourceVersion
|
||||
|
||||
Consistently(
|
||||
func() (types.UID, error) {
|
||||
currentListener := new(v1alpha1.AutoscalingListener)
|
||||
err := k8sClient.Get(ctx, client.ObjectKey{Name: scaleSetListenerName(testARS), Namespace: testARSNamespace}, currentListener)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return currentListener.UID, nil
|
||||
},
|
||||
time.Second*5,
|
||||
autoscalingRunnerSetTestInterval,
|
||||
).Should(Equal(originalUID), "listener UID should remain unchanged (no recreation)")
|
||||
|
||||
Consistently(
|
||||
func() (string, error) {
|
||||
currentListener := new(v1alpha1.AutoscalingListener)
|
||||
err := k8sClient.Get(ctx, client.ObjectKey{Name: scaleSetListenerName(testARS), Namespace: testARSNamespace}, currentListener)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return currentListener.ResourceVersion, nil
|
||||
},
|
||||
time.Second*5,
|
||||
autoscalingRunnerSetTestInterval,
|
||||
).Should(Equal(originalResourceVersion), "listener ResourceVersion should remain unchanged (no updates)")
|
||||
|
||||
err = k8sClient.Get(ctx, client.ObjectKey{Name: scaleSetListenerName(testARS), Namespace: testARSNamespace}, listener)
|
||||
Expect(err).NotTo(HaveOccurred(), "failed to get listener")
|
||||
Expect(listener.Spec.EphemeralRunnerSetName).To(Equal(latestERSName), "listener should still reference latest ERS")
|
||||
})
|
||||
|
||||
Context("When updating a new AutoScalingRunnerSet", func() {
|
||||
It("It should re-create EphemeralRunnerSet and Listener as needed when updating AutoScalingRunnerSet", func() {
|
||||
// Wait till the listener is created
|
||||
|
|
@ -303,7 +450,8 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() {
|
|||
return k8sClient.Get(ctx, client.ObjectKey{Name: scaleSetListenerName(autoscalingRunnerSet), Namespace: autoscalingRunnerSet.Namespace}, listener)
|
||||
},
|
||||
autoscalingRunnerSetTestTimeout,
|
||||
autoscalingRunnerSetTestInterval).Should(Succeed(), "Listener should be created")
|
||||
autoscalingRunnerSetTestInterval,
|
||||
).Should(Succeed(), "Listener should be created")
|
||||
|
||||
runnerSetList := new(v1alpha1.EphemeralRunnerSetList)
|
||||
err := k8sClient.List(ctx, runnerSetList, client.InNamespace(autoscalingRunnerSet.Namespace))
|
||||
|
|
@ -339,7 +487,8 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() {
|
|||
return runnerSetList.Items[0].Annotations[annotationKeyRunnerSpecHash], nil
|
||||
},
|
||||
autoscalingRunnerSetTestTimeout,
|
||||
autoscalingRunnerSetTestInterval).ShouldNot(BeEquivalentTo(runnerSet.Annotations[annotationKeyRunnerSpecHash]), "New EphemeralRunnerSet should be created")
|
||||
autoscalingRunnerSetTestInterval,
|
||||
).ShouldNot(BeEquivalentTo(runnerSet.Annotations[annotationKeyRunnerSpecHash]), "New EphemeralRunnerSet should be created")
|
||||
|
||||
// We should create a new listener
|
||||
Eventually(
|
||||
|
|
@ -353,7 +502,8 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() {
|
|||
return listener.Spec.EphemeralRunnerSetName, nil
|
||||
},
|
||||
autoscalingRunnerSetTestTimeout,
|
||||
autoscalingRunnerSetTestInterval).ShouldNot(BeEquivalentTo(runnerSet.Name), "New Listener should be created")
|
||||
autoscalingRunnerSetTestInterval,
|
||||
).ShouldNot(BeEquivalentTo(runnerSet.Name), "New Listener should be created")
|
||||
|
||||
// Only update the Spec for the AutoScalingListener
|
||||
// This should trigger re-creation of the Listener only
|
||||
|
|
@ -389,7 +539,8 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() {
|
|||
return string(runnerSetList.Items[0].UID), nil
|
||||
},
|
||||
autoscalingRunnerSetTestTimeout,
|
||||
autoscalingRunnerSetTestInterval).Should(BeEquivalentTo(string(runnerSet.UID)), "New EphemeralRunnerSet should not be created")
|
||||
autoscalingRunnerSetTestInterval,
|
||||
).Should(BeEquivalentTo(string(runnerSet.UID)), "New EphemeralRunnerSet should not be created")
|
||||
|
||||
// We should only re-create a new listener
|
||||
Eventually(
|
||||
|
|
@ -403,7 +554,8 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() {
|
|||
return string(listener.UID), nil
|
||||
},
|
||||
autoscalingRunnerSetTestTimeout,
|
||||
autoscalingRunnerSetTestInterval).ShouldNot(BeEquivalentTo(string(listener.UID)), "New Listener should be created")
|
||||
autoscalingRunnerSetTestInterval,
|
||||
).ShouldNot(BeEquivalentTo(string(listener.UID)), "New Listener should be created")
|
||||
|
||||
// Only update the values hash for the autoscaling runner set
|
||||
// This should trigger re-creation of the Listener only
|
||||
|
|
@ -438,7 +590,8 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() {
|
|||
return string(runnerSetList.Items[0].UID), nil
|
||||
},
|
||||
autoscalingRunnerSetTestTimeout,
|
||||
autoscalingRunnerSetTestInterval).Should(BeEquivalentTo(string(runnerSet.UID)), "New EphemeralRunnerSet should not be created")
|
||||
autoscalingRunnerSetTestInterval,
|
||||
).Should(BeEquivalentTo(string(runnerSet.UID)), "New EphemeralRunnerSet should not be created")
|
||||
|
||||
// We should only re-create a new listener
|
||||
Eventually(
|
||||
|
|
@ -452,7 +605,8 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() {
|
|||
return string(listener.UID), nil
|
||||
},
|
||||
autoscalingRunnerSetTestTimeout,
|
||||
autoscalingRunnerSetTestInterval).ShouldNot(BeEquivalentTo(string(listener.UID)), "New Listener should be created")
|
||||
autoscalingRunnerSetTestInterval,
|
||||
).ShouldNot(BeEquivalentTo(string(listener.UID)), "New Listener should be created")
|
||||
})
|
||||
|
||||
It("It should update RunnerScaleSet's runner group on service when it changes", func() {
|
||||
|
|
@ -463,7 +617,8 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() {
|
|||
return k8sClient.Get(ctx, client.ObjectKey{Name: scaleSetListenerName(autoscalingRunnerSet), Namespace: autoscalingRunnerSet.Namespace}, new(v1alpha1.AutoscalingListener))
|
||||
},
|
||||
autoscalingRunnerSetTestTimeout,
|
||||
autoscalingRunnerSetTestInterval).Should(Succeed(), "Listener should be created")
|
||||
autoscalingRunnerSetTestInterval,
|
||||
).Should(Succeed(), "Listener should be created")
|
||||
|
||||
patched := autoscalingRunnerSet.DeepCopy()
|
||||
patched.Spec.RunnerGroup = "testgroup2"
|
||||
|
|
@ -485,7 +640,8 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() {
|
|||
return updated.Annotations[AnnotationKeyGitHubRunnerGroupName], nil
|
||||
},
|
||||
autoscalingRunnerSetTestTimeout,
|
||||
autoscalingRunnerSetTestInterval).Should(BeEquivalentTo("testgroup2"), "AutoScalingRunnerSet should have the new runner group in its annotation")
|
||||
autoscalingRunnerSetTestInterval,
|
||||
).Should(BeEquivalentTo("testgroup2"), "AutoScalingRunnerSet should have the new runner group in its annotation")
|
||||
|
||||
// delete the annotation and it should be re-added
|
||||
patched = autoscalingRunnerSet.DeepCopy()
|
||||
|
|
@ -642,13 +798,14 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() {
|
|||
).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
|
||||
},
|
||||
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")
|
||||
|
|
|
|||
|
|
@ -102,6 +102,13 @@ func WithSystemInfo(info scaleset.SystemInfo) ClientOption {
|
|||
}
|
||||
}
|
||||
|
||||
// WithCreateRunnerScaleSetFunc configures a function to handle CreateRunnerScaleSet calls dynamically
|
||||
func WithCreateRunnerScaleSetFunc(fn func(context.Context, *scaleset.RunnerScaleSet) (*scaleset.RunnerScaleSet, error)) ClientOption {
|
||||
return func(c *Client) {
|
||||
c.createRunnerScaleSetFunc = fn
|
||||
}
|
||||
}
|
||||
|
||||
// WithUpdateRunnerScaleSetFunc configures a function to handle UpdateRunnerScaleSet calls dynamically
|
||||
func WithUpdateRunnerScaleSetFunc(fn func(context.Context, int, *scaleset.RunnerScaleSet) (*scaleset.RunnerScaleSet, error)) ClientOption {
|
||||
return func(c *Client) {
|
||||
|
|
@ -112,6 +119,7 @@ func WithUpdateRunnerScaleSetFunc(fn func(context.Context, int, *scaleset.Runner
|
|||
// Client implements multiclient.Client interface for testing
|
||||
type Client struct {
|
||||
systemInfo scaleset.SystemInfo
|
||||
createRunnerScaleSetFunc func(context.Context, *scaleset.RunnerScaleSet) (*scaleset.RunnerScaleSet, error)
|
||||
updateRunnerScaleSetFunc func(context.Context, int, *scaleset.RunnerScaleSet) (*scaleset.RunnerScaleSet, error)
|
||||
|
||||
getRunnerScaleSetResult struct {
|
||||
|
|
@ -215,6 +223,9 @@ func (c *Client) GetRunnerScaleSetByID(ctx context.Context, runnerScaleSetID int
|
|||
}
|
||||
|
||||
func (c *Client) CreateRunnerScaleSet(ctx context.Context, runnerScaleSet *scaleset.RunnerScaleSet) (*scaleset.RunnerScaleSet, error) {
|
||||
if c.createRunnerScaleSetFunc != nil {
|
||||
return c.createRunnerScaleSetFunc(ctx, runnerScaleSet)
|
||||
}
|
||||
return c.createRunnerScaleSetResult.RunnerScaleSet, c.createRunnerScaleSetResult.err
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -43,6 +43,25 @@ You can follow [this troubleshooting guide](https://docs.github.com/en/actions/h
|
|||
|
||||
## Changelog
|
||||
|
||||
### 0.14.2
|
||||
|
||||
1. Fix orphan no-permission ServiceAccount in kubernetes-novolume mode [#4455](https://github.com/actions/actions-runner-controller/pull/4455)
|
||||
1. Updates: runner to v2.334.0 [#4467](https://github.com/actions/actions-runner-controller/pull/4467)
|
||||
1. Add option to disable workqueue bucket rate limiter [#4451](https://github.com/actions/actions-runner-controller/pull/4451)
|
||||
1. Add a flag for enabling pprof on the controller manager [#4449](https://github.com/actions/actions-runner-controller/pull/4449)
|
||||
1. Add health and readiness probes to controller manager [#4459](https://github.com/actions/actions-runner-controller/pull/4459)
|
||||
1. Fix empty GVK in OwnerReferences for modern controllers [#4475](https://github.com/actions/actions-runner-controller/pull/4475)
|
||||
1. Fix: Detect init container failure in EphemeralRunner controller [#4457](https://github.com/actions/actions-runner-controller/pull/4457)
|
||||
1. Bump the actions group with 3 updates [#4483](https://github.com/actions/actions-runner-controller/pull/4483)
|
||||
1. Render empty arrays for kubernetes-novolume volumes fields [#4461](https://github.com/actions/actions-runner-controller/pull/4461)
|
||||
1. Fix secret reconciliation updates for the listener pod [#4492](https://github.com/actions/actions-runner-controller/pull/4492)
|
||||
1. Fix job execution duration when runner assign time is not set [#4472](https://github.com/actions/actions-runner-controller/pull/4472)
|
||||
1. Update CODEOWNERS [#4495](https://github.com/actions/actions-runner-controller/pull/4495)
|
||||
1. Bump Go to 1.26.2 to fix critical security vulnerabilities [#4491](https://github.com/actions/actions-runner-controller/pull/4491)
|
||||
1. Fix helm chart validation workflow [#4479](https://github.com/actions/actions-runner-controller/pull/4479)
|
||||
1. Port rate limiter to experimental charts [#4478](https://github.com/actions/actions-runner-controller/pull/4478)
|
||||
1. Bump Go to 1.26.3 [#4504](https://github.com/actions/actions-runner-controller/pull/4504)
|
||||
|
||||
### 0.14.1
|
||||
|
||||
1. Fix null field for resource metadata fields in experimental chart [#4419](https://github.com/actions/actions-runner-controller/pull/4419)
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ func (c *GitHubConfig) GitHubAPIURL(path string) *url.URL {
|
|||
result.Host = fmt.Sprintf("api.%s", c.ConfigURL.Host)
|
||||
result.Path = ""
|
||||
|
||||
if strings.EqualFold("www.github.com", c.ConfigURL.Host) {
|
||||
if c.ConfigURL.Host == "www.github.com" {
|
||||
// re-routing www.github.com to api.github.com
|
||||
result.Host = "api.github.com"
|
||||
}
|
||||
|
|
@ -102,8 +102,8 @@ func isHostedGitHubURL(u *url.URL) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
return strings.EqualFold(u.Host, "github.com") ||
|
||||
strings.EqualFold(u.Host, "www.github.com") ||
|
||||
strings.EqualFold(u.Host, "github.localhost") ||
|
||||
return u.Host == "github.com" ||
|
||||
u.Host == "www.github.com" ||
|
||||
u.Host == "github.localhost" ||
|
||||
strings.HasSuffix(u.Host, ".ghe.com")
|
||||
}
|
||||
|
|
|
|||
4
go.mod
4
go.mod
|
|
@ -1,12 +1,12 @@
|
|||
module github.com/actions/actions-runner-controller
|
||||
|
||||
go 1.26.2
|
||||
go 1.26.3
|
||||
|
||||
require (
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets v1.4.0
|
||||
github.com/actions/scaleset v0.3.0
|
||||
github.com/actions/scaleset v0.4.0
|
||||
github.com/bradleyfalzon/ghinstallation/v2 v2.18.0
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
|
||||
github.com/evanphx/json-patch v5.9.11+incompatible
|
||||
|
|
|
|||
2
go.sum
2
go.sum
|
|
@ -27,6 +27,8 @@ github.com/actions-runner-controller/httpcache v0.2.0 h1:hCNvYuVPJ2xxYBymqBvH0hS
|
|||
github.com/actions-runner-controller/httpcache v0.2.0/go.mod h1:JLu9/2M/btPz1Zu/vTZ71XzukQHn2YeISPmJoM5exBI=
|
||||
github.com/actions/scaleset v0.3.0 h1:y5/ClYLJXFuGCikzILOOPhaCShAcL6K0mnUtjDKFxVw=
|
||||
github.com/actions/scaleset v0.3.0/go.mod h1:2L2I6rggFWV+zprDet6y7y7Vkm3HPudaup78eSc79Uo=
|
||||
github.com/actions/scaleset v0.4.0 h1:691GC2AkHb3ZGjfNvatboYoRS7CLr3+4VcZk/6w9IbM=
|
||||
github.com/actions/scaleset v0.4.0/go.mod h1:2L2I6rggFWV+zprDet6y7y7Vkm3HPudaup78eSc79Uo=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.3 h1:4kQ/fa22KjDt13QCy1+bYADvdgcxpfH18f0zP542kZA=
|
||||
|
|
|
|||
|
|
@ -783,26 +783,30 @@ func (e *env) installActionsRunnerController(t *testing.T, repo, tag, testID, ch
|
|||
}
|
||||
|
||||
if e.useApp {
|
||||
varEnv = append(varEnv,
|
||||
varEnv = append(
|
||||
varEnv,
|
||||
"ACCEPTANCE_TEST_SECRET_TYPE=app",
|
||||
"APP_ID="+e.appID,
|
||||
"APP_INSTALLATION_ID="+e.appInstallationID,
|
||||
"APP_PRIVATE_KEY_FILE="+e.appPrivateKeyFile,
|
||||
)
|
||||
} else {
|
||||
varEnv = append(varEnv,
|
||||
varEnv = append(
|
||||
varEnv,
|
||||
"ACCEPTANCE_TEST_SECRET_TYPE=token",
|
||||
"GITHUB_TOKEN="+e.githubToken,
|
||||
)
|
||||
}
|
||||
|
||||
if e.logFormat != "" {
|
||||
varEnv = append(varEnv,
|
||||
varEnv = append(
|
||||
varEnv,
|
||||
"LOG_FORMAT="+e.logFormat,
|
||||
)
|
||||
}
|
||||
|
||||
varEnv = append(varEnv,
|
||||
varEnv = append(
|
||||
varEnv,
|
||||
"GITHUB_WEBHOOK_SERVER_ENV_NAME="+c.GithubWebhookServerEnvName,
|
||||
"GITHUB_WEBHOOK_SERVER_ENV_VALUE="+c.GithubWebhookServerEnvValue,
|
||||
)
|
||||
|
|
@ -909,20 +913,24 @@ func (e *env) do(t *testing.T, op string, kind DeployKind, testID string, env ..
|
|||
}
|
||||
|
||||
if e.dockerdWithinRunnerContainer {
|
||||
varEnv = append(varEnv,
|
||||
varEnv = append(
|
||||
varEnv,
|
||||
"RUNNER_DOCKERD_WITHIN_RUNNER_CONTAINER=true",
|
||||
)
|
||||
if e.rootlessDocker {
|
||||
varEnv = append(varEnv,
|
||||
varEnv = append(
|
||||
varEnv,
|
||||
"RUNNER_NAME="+e.vars.runnerRootlessDindImageRepo,
|
||||
)
|
||||
} else {
|
||||
varEnv = append(varEnv,
|
||||
varEnv = append(
|
||||
varEnv,
|
||||
"RUNNER_NAME="+e.vars.runnerDindImageRepo,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
varEnv = append(varEnv,
|
||||
varEnv = append(
|
||||
varEnv,
|
||||
"RUNNER_DOCKERD_WITHIN_RUNNER_CONTAINER=false",
|
||||
"RUNNER_NAME="+e.vars.runnerImageRepo,
|
||||
)
|
||||
|
|
@ -1062,14 +1070,16 @@ func installActionsWorkflow(t *testing.T, testName, runnerLabel, testResultCMNam
|
|||
|
||||
if !kubernetesContainerMode {
|
||||
if kind == RunnerDeployments {
|
||||
steps = append(steps,
|
||||
steps = append(
|
||||
steps,
|
||||
testing.Step{
|
||||
Run: sudo + "mkdir -p \"${RUNNER_TOOL_CACHE}\" \"${HOME}/.cache\"",
|
||||
},
|
||||
)
|
||||
|
||||
if useSudo {
|
||||
steps = append(steps,
|
||||
steps = append(
|
||||
steps,
|
||||
testing.Step{
|
||||
// This might be the easiest way to handle permissions without use of securityContext
|
||||
// https://stackoverflow.com/questions/50156124/kubernetes-nfs-persistent-volumes-permission-denied#comment107483717_53186320
|
||||
|
|
@ -1080,7 +1090,8 @@ func installActionsWorkflow(t *testing.T, testName, runnerLabel, testResultCMNam
|
|||
}
|
||||
|
||||
if useSudo {
|
||||
steps = append(steps,
|
||||
steps = append(
|
||||
steps,
|
||||
testing.Step{
|
||||
// This might be the easiest way to handle permissions without use of securityContext
|
||||
// https://stackoverflow.com/questions/50156124/kubernetes-nfs-persistent-volumes-permission-denied#comment107483717_53186320
|
||||
|
|
@ -1102,18 +1113,20 @@ func installActionsWorkflow(t *testing.T, testName, runnerLabel, testResultCMNam
|
|||
)
|
||||
}
|
||||
|
||||
steps = append(steps,
|
||||
steps = append(
|
||||
steps,
|
||||
testing.Step{
|
||||
Uses: "actions/setup-go@v3",
|
||||
With: &testing.With{
|
||||
GoVersion: "1.26.1",
|
||||
GoVersion: "1.26.3",
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
// Ensure both the alias and the full command work after
|
||||
// https://github.com/actions/actions-runner-controller/pull/2326
|
||||
steps = append(steps,
|
||||
steps = append(
|
||||
steps,
|
||||
testing.Step{
|
||||
Run: "docker-compose version",
|
||||
},
|
||||
|
|
@ -1123,7 +1136,8 @@ func installActionsWorkflow(t *testing.T, testName, runnerLabel, testResultCMNam
|
|||
)
|
||||
}
|
||||
|
||||
steps = append(steps,
|
||||
steps = append(
|
||||
steps,
|
||||
testing.Step{
|
||||
Run: "go version",
|
||||
},
|
||||
|
|
@ -1166,19 +1180,21 @@ func installActionsWorkflow(t *testing.T, testName, runnerLabel, testResultCMNam
|
|||
if useCustomDockerContext {
|
||||
setupBuildXActionWith.Endpoint = "mycontext"
|
||||
|
||||
steps = append(steps, testing.Step{
|
||||
// https://github.com/docker/buildx/issues/413#issuecomment-710660155
|
||||
// To prevent setup-buildx-action from failing with:
|
||||
// error: could not create a builder instance with TLS data loaded from environment. Please use `docker context create <context-name>` to create a context for current environment and then create a builder instance with `docker buildx create <context-name>`
|
||||
Run: "docker context create mycontext",
|
||||
},
|
||||
steps = append(
|
||||
steps, testing.Step{
|
||||
// https://github.com/docker/buildx/issues/413#issuecomment-710660155
|
||||
// To prevent setup-buildx-action from failing with:
|
||||
// error: could not create a builder instance with TLS data loaded from environment. Please use `docker context create <context-name>` to create a context for current environment and then create a builder instance with `docker buildx create <context-name>`
|
||||
Run: "docker context create mycontext",
|
||||
},
|
||||
testing.Step{
|
||||
Run: "docker context use mycontext",
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
steps = append(steps,
|
||||
steps = append(
|
||||
steps,
|
||||
testing.Step{
|
||||
Name: "Set up Docker Buildx",
|
||||
Uses: "docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2",
|
||||
|
|
@ -1203,7 +1219,8 @@ func installActionsWorkflow(t *testing.T, testName, runnerLabel, testResultCMNam
|
|||
)
|
||||
|
||||
if useSudo {
|
||||
steps = append(steps,
|
||||
steps = append(
|
||||
steps,
|
||||
testing.Step{
|
||||
// https://github.com/docker/build-push-action/blob/master/docs/advanced/cache.md#local-cache
|
||||
// See https://github.com/moby/buildkit/issues/1896 for why this is needed
|
||||
|
|
@ -1218,7 +1235,8 @@ func installActionsWorkflow(t *testing.T, testName, runnerLabel, testResultCMNam
|
|||
|
||||
if useSudo {
|
||||
if kind == RunnerDeployments {
|
||||
steps = append(steps,
|
||||
steps = append(
|
||||
steps,
|
||||
testing.Step{
|
||||
// https://github.com/docker/build-push-action/blob/master/docs/advanced/cache.md#local-cache
|
||||
// See https://github.com/moby/buildkit/issues/1896 for why this is needed
|
||||
|
|
@ -1232,7 +1250,8 @@ func installActionsWorkflow(t *testing.T, testName, runnerLabel, testResultCMNam
|
|||
}
|
||||
}
|
||||
|
||||
steps = append(steps,
|
||||
steps = append(
|
||||
steps,
|
||||
testing.Step{
|
||||
Uses: "azure/setup-kubectl@3e0aec4d80787158d308d7b364cb1b702e7feb7f",
|
||||
With: &testing.With{
|
||||
|
|
|
|||
Loading…
Reference in New Issue