mirror of
https://github.com/actions/actions-runner-controller.git
synced 2026-03-15 22:02:48 +08:00
Compare commits
4 Commits
dependabot
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f99c6eda0b | ||
|
|
1d9f626c53 | ||
|
|
1f3e5b9027 | ||
|
|
cd5b93d1bc |
2
.github/workflows/go.yaml
vendored
2
.github/workflows/go.yaml
vendored
@@ -51,7 +51,7 @@ jobs:
|
||||
uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20
|
||||
with:
|
||||
only-new-issues: true
|
||||
version: v2.5.0
|
||||
version: v2.11.2
|
||||
|
||||
generate:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
all: false
|
||||
dir: "{{.InterfaceDir}}"
|
||||
filename: mocks_test.go
|
||||
force-file-write: true
|
||||
formatter: goimports
|
||||
log-level: info
|
||||
structname: "{{.Mock}}{{.InterfaceName}}"
|
||||
pkgname: "{{.SrcPackageName}}"
|
||||
recursive: false
|
||||
template: testify
|
||||
packages:
|
||||
github.com/actions/actions-runner-controller/github/actions:
|
||||
config:
|
||||
@@ -8,13 +18,9 @@ packages:
|
||||
interfaces:
|
||||
ActionsService:
|
||||
SessionService:
|
||||
|
||||
github.com/actions/actions-runner-controller/cmd/ghalistener/metrics:
|
||||
config:
|
||||
inpackage: true
|
||||
dir: "{{.InterfaceDir}}"
|
||||
filename: "mocks_test.go"
|
||||
pkgname: "metrics"
|
||||
interfaces:
|
||||
Recorder:
|
||||
ServerExporter:
|
||||
all: true
|
||||
github.com/actions/actions-runner-controller/controllers/actions.github.com:
|
||||
config:
|
||||
all: true
|
||||
|
||||
16
Dockerfile
16
Dockerfile
@@ -1,5 +1,5 @@
|
||||
# Build the manager binary
|
||||
FROM --platform=$BUILDPLATFORM golang:1.25.3 AS builder
|
||||
FROM --platform=$BUILDPLATFORM golang:1.26.1 AS builder
|
||||
|
||||
WORKDIR /workspace
|
||||
|
||||
@@ -34,13 +34,13 @@ ENV GOCACHE="/build/${TARGETPLATFORM}/root/.cache/go-build"
|
||||
|
||||
# Build
|
||||
RUN --mount=target=. \
|
||||
--mount=type=cache,mode=0777,target=${GOCACHE} \
|
||||
export GOOS=${TARGETOS} GOARCH=${TARGETARCH} GOARM=${TARGETVARIANT#v} && \
|
||||
go build -trimpath -ldflags="-s -w -X 'github.com/actions/actions-runner-controller/build.Version=${VERSION}' -X 'github.com/actions/actions-runner-controller/build.CommitSHA=${COMMIT_SHA}'" -o /out/manager main.go && \
|
||||
go build -trimpath -ldflags="-s -w -X 'github.com/actions/actions-runner-controller/build.Version=${VERSION}' -X 'github.com/actions/actions-runner-controller/build.CommitSHA=${COMMIT_SHA}'" -o /out/ghalistener ./cmd/ghalistener && \
|
||||
go build -trimpath -ldflags="-s -w" -o /out/github-webhook-server ./cmd/githubwebhookserver && \
|
||||
go build -trimpath -ldflags="-s -w" -o /out/actions-metrics-server ./cmd/actionsmetricsserver && \
|
||||
go build -trimpath -ldflags="-s -w" -o /out/sleep ./cmd/sleep
|
||||
--mount=type=cache,mode=0777,target=${GOCACHE} \
|
||||
export GOOS=${TARGETOS} GOARCH=${TARGETARCH} GOARM=${TARGETVARIANT#v} && \
|
||||
go build -trimpath -ldflags="-s -w -X 'github.com/actions/actions-runner-controller/build.Version=${VERSION}' -X 'github.com/actions/actions-runner-controller/build.CommitSHA=${COMMIT_SHA}'" -o /out/manager main.go && \
|
||||
go build -trimpath -ldflags="-s -w -X 'github.com/actions/actions-runner-controller/build.Version=${VERSION}' -X 'github.com/actions/actions-runner-controller/build.CommitSHA=${COMMIT_SHA}'" -o /out/ghalistener ./cmd/ghalistener && \
|
||||
go build -trimpath -ldflags="-s -w" -o /out/github-webhook-server ./cmd/githubwebhookserver && \
|
||||
go build -trimpath -ldflags="-s -w" -o /out/actions-metrics-server ./cmd/actionsmetricsserver && \
|
||||
go build -trimpath -ldflags="-s -w" -o /out/sleep ./cmd/sleep
|
||||
|
||||
# Use distroless as minimal base image to package the manager binary
|
||||
# Refer to https://github.com/GoogleContainerTools/distroless for more details
|
||||
|
||||
2
Makefile
2
Makefile
@@ -62,7 +62,7 @@ endif
|
||||
all: manager
|
||||
|
||||
lint:
|
||||
docker run --rm -v $(PWD):/app -w /app golangci/golangci-lint:v2.5.0 golangci-lint run
|
||||
docker run --rm -v $(PWD):/app -w /app golangci/golangci-lint:v2.11.2 golangci-lint run
|
||||
|
||||
GO_TEST_ARGS ?= -short
|
||||
|
||||
|
||||
@@ -69,6 +69,18 @@ type AutoscalingListenerSpec struct {
|
||||
|
||||
// +optional
|
||||
Template *corev1.PodTemplateSpec `json:"template,omitempty"`
|
||||
|
||||
// +optional
|
||||
ConfigSecretMetadata *ResourceMeta `json:"configSecretMetadata,omitempty"`
|
||||
|
||||
// +optional
|
||||
ServiceAccountMetadata *ResourceMeta `json:"serviceAccountMetadata,omitempty"`
|
||||
|
||||
// +optional
|
||||
RoleMetadata *ResourceMeta `json:"roleMetadata,omitempty"`
|
||||
|
||||
// +optional
|
||||
RoleBindingMetadata *ResourceMeta `json:"roleBindingMetadata,omitempty"`
|
||||
}
|
||||
|
||||
// AutoscalingListenerStatus defines the observed state of AutoscalingListener
|
||||
|
||||
@@ -78,12 +78,36 @@ type AutoscalingRunnerSetSpec struct {
|
||||
// Required
|
||||
Template corev1.PodTemplateSpec `json:"template,omitempty"`
|
||||
|
||||
// +optional
|
||||
AutoscalingListenerMetadata *ResourceMeta `json:"autoscalingListener,omitempty"`
|
||||
|
||||
// +optional
|
||||
ListenerMetrics *MetricsConfig `json:"listenerMetrics,omitempty"`
|
||||
|
||||
// +optional
|
||||
ListenerTemplate *corev1.PodTemplateSpec `json:"listenerTemplate,omitempty"`
|
||||
|
||||
// +optional
|
||||
ListenerServiceAccountMetadata *ResourceMeta `json:"listenerServiceAccountMetadata,omitempty"`
|
||||
|
||||
// +optional
|
||||
ListenerRoleMetadata *ResourceMeta `json:"listenerRoleMetadata,omitempty"`
|
||||
|
||||
// +optional
|
||||
ListenerRoleBindingMetadata *ResourceMeta `json:"listenerRoleBindingMetadata,omitempty"`
|
||||
|
||||
// +optional
|
||||
ListenerConfigSecretMetadata *ResourceMeta `json:"listenerConfigSecretMetadata,omitempty"`
|
||||
|
||||
// +optional
|
||||
EphemeralRunnerSetMetadata *ResourceMeta `json:"ephemeralRunnerSetMetadata,omitempty"`
|
||||
|
||||
// +optional
|
||||
EphemeralRunnerMetadata *ResourceMeta `json:"ephemeralRunnerMetadata,omitempty"`
|
||||
|
||||
// +optional
|
||||
EphemeralRunnerConfigSecretMetadata *ResourceMeta `json:"ephemeralRunnerConfigSecretMetadata,omitempty"`
|
||||
|
||||
// +optional
|
||||
// +kubebuilder:validation:Minimum:=0
|
||||
MaxRunners *int `json:"maxRunners,omitempty"`
|
||||
|
||||
9
apis/actions.github.com/v1alpha1/common.go
Normal file
9
apis/actions.github.com/v1alpha1/common.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package v1alpha1
|
||||
|
||||
// ResourceMeta carries metadata common to all internal resources
|
||||
type ResourceMeta struct {
|
||||
// +optional
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
// +optional
|
||||
Annotations map[string]string `json:"annotations,omitempty"`
|
||||
}
|
||||
@@ -111,7 +111,7 @@ type EphemeralRunnerSpec struct {
|
||||
GitHubServerTLS *TLSConfig `json:"githubServerTLS,omitempty"`
|
||||
|
||||
// +required
|
||||
RunnerScaleSetId int `json:"runnerScaleSetId,omitempty"`
|
||||
RunnerScaleSetID int `json:"runnerScaleSetId,omitempty"`
|
||||
|
||||
// +optional
|
||||
Proxy *ProxyConfig `json:"proxy,omitempty"`
|
||||
@@ -122,6 +122,9 @@ type EphemeralRunnerSpec struct {
|
||||
// +optional
|
||||
VaultConfig *VaultConfig `json:"vaultConfig,omitempty"`
|
||||
|
||||
// +optional
|
||||
EphemeralRunnerConfigSecretMetadata *ResourceMeta `json:"ephemeralRunnerConfigSecretMetadata,omitempty"`
|
||||
|
||||
corev1.PodTemplateSpec `json:",inline"`
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@ type EphemeralRunnerSetSpec struct {
|
||||
PatchID int `json:"patchID"`
|
||||
// EphemeralRunnerSpec is the spec of the ephemeral runner
|
||||
EphemeralRunnerSpec EphemeralRunnerSpec `json:"ephemeralRunnerSpec,omitempty"`
|
||||
// +optional
|
||||
EphemeralRunnerMetadata *ResourceMeta `json:"ephemeralRunnerMetadata,omitempty"`
|
||||
}
|
||||
|
||||
// EphemeralRunnerSetStatus defines the observed state of EphemeralRunnerSet
|
||||
|
||||
@@ -118,6 +118,26 @@ func (in *AutoscalingListenerSpec) DeepCopyInto(out *AutoscalingListenerSpec) {
|
||||
*out = new(v1.PodTemplateSpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.ConfigSecretMetadata != nil {
|
||||
in, out := &in.ConfigSecretMetadata, &out.ConfigSecretMetadata
|
||||
*out = new(ResourceMeta)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.ServiceAccountMetadata != nil {
|
||||
in, out := &in.ServiceAccountMetadata, &out.ServiceAccountMetadata
|
||||
*out = new(ResourceMeta)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.RoleMetadata != nil {
|
||||
in, out := &in.RoleMetadata, &out.RoleMetadata
|
||||
*out = new(ResourceMeta)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.RoleBindingMetadata != nil {
|
||||
in, out := &in.RoleBindingMetadata, &out.RoleBindingMetadata
|
||||
*out = new(ResourceMeta)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutoscalingListenerSpec.
|
||||
@@ -223,6 +243,11 @@ func (in *AutoscalingRunnerSetSpec) DeepCopyInto(out *AutoscalingRunnerSetSpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
in.Template.DeepCopyInto(&out.Template)
|
||||
if in.AutoscalingListenerMetadata != nil {
|
||||
in, out := &in.AutoscalingListenerMetadata, &out.AutoscalingListenerMetadata
|
||||
*out = new(ResourceMeta)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.ListenerMetrics != nil {
|
||||
in, out := &in.ListenerMetrics, &out.ListenerMetrics
|
||||
*out = new(MetricsConfig)
|
||||
@@ -233,6 +258,41 @@ func (in *AutoscalingRunnerSetSpec) DeepCopyInto(out *AutoscalingRunnerSetSpec)
|
||||
*out = new(v1.PodTemplateSpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.ListenerServiceAccountMetadata != nil {
|
||||
in, out := &in.ListenerServiceAccountMetadata, &out.ListenerServiceAccountMetadata
|
||||
*out = new(ResourceMeta)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.ListenerRoleMetadata != nil {
|
||||
in, out := &in.ListenerRoleMetadata, &out.ListenerRoleMetadata
|
||||
*out = new(ResourceMeta)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.ListenerRoleBindingMetadata != nil {
|
||||
in, out := &in.ListenerRoleBindingMetadata, &out.ListenerRoleBindingMetadata
|
||||
*out = new(ResourceMeta)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.ListenerConfigSecretMetadata != nil {
|
||||
in, out := &in.ListenerConfigSecretMetadata, &out.ListenerConfigSecretMetadata
|
||||
*out = new(ResourceMeta)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.EphemeralRunnerSetMetadata != nil {
|
||||
in, out := &in.EphemeralRunnerSetMetadata, &out.EphemeralRunnerSetMetadata
|
||||
*out = new(ResourceMeta)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.EphemeralRunnerMetadata != nil {
|
||||
in, out := &in.EphemeralRunnerMetadata, &out.EphemeralRunnerMetadata
|
||||
*out = new(ResourceMeta)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.EphemeralRunnerConfigSecretMetadata != nil {
|
||||
in, out := &in.EphemeralRunnerConfigSecretMetadata, &out.EphemeralRunnerConfigSecretMetadata
|
||||
*out = new(ResourceMeta)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.MaxRunners != nil {
|
||||
in, out := &in.MaxRunners, &out.MaxRunners
|
||||
*out = new(int)
|
||||
@@ -427,6 +487,11 @@ func (in *EphemeralRunnerSetList) DeepCopyObject() runtime.Object {
|
||||
func (in *EphemeralRunnerSetSpec) DeepCopyInto(out *EphemeralRunnerSetSpec) {
|
||||
*out = *in
|
||||
in.EphemeralRunnerSpec.DeepCopyInto(&out.EphemeralRunnerSpec)
|
||||
if in.EphemeralRunnerMetadata != nil {
|
||||
in, out := &in.EphemeralRunnerMetadata, &out.EphemeralRunnerMetadata
|
||||
*out = new(ResourceMeta)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EphemeralRunnerSetSpec.
|
||||
@@ -472,6 +537,11 @@ func (in *EphemeralRunnerSpec) DeepCopyInto(out *EphemeralRunnerSpec) {
|
||||
*out = new(VaultConfig)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.EphemeralRunnerConfigSecretMetadata != nil {
|
||||
in, out := &in.EphemeralRunnerConfigSecretMetadata, &out.EphemeralRunnerConfigSecretMetadata
|
||||
*out = new(ResourceMeta)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
in.PodTemplateSpec.DeepCopyInto(&out.PodTemplateSpec)
|
||||
}
|
||||
|
||||
@@ -660,6 +730,35 @@ func (in *ProxyServerConfig) DeepCopy() *ProxyServerConfig {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ResourceMeta) DeepCopyInto(out *ResourceMeta) {
|
||||
*out = *in
|
||||
if in.Labels != nil {
|
||||
in, out := &in.Labels, &out.Labels
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
if in.Annotations != nil {
|
||||
in, out := &in.Annotations, &out.Annotations
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceMeta.
|
||||
func (in *ResourceMeta) DeepCopy() *ResourceMeta {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ResourceMeta)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TLSCertificateSource) DeepCopyInto(out *TLSCertificateSource) {
|
||||
*out = *in
|
||||
|
||||
@@ -56,6 +56,19 @@ spec:
|
||||
autoscalingRunnerSetNamespace:
|
||||
description: Required
|
||||
type: string
|
||||
configSecretMetadata:
|
||||
description: ResourceMeta carries metadata common to all internal
|
||||
resources
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
ephemeralRunnerSetName:
|
||||
description: Required
|
||||
type: string
|
||||
@@ -196,9 +209,48 @@ spec:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
roleBindingMetadata:
|
||||
description: ResourceMeta carries metadata common to all internal
|
||||
resources
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
roleMetadata:
|
||||
description: ResourceMeta carries metadata common to all internal
|
||||
resources
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
runnerScaleSetId:
|
||||
description: Required
|
||||
type: integer
|
||||
serviceAccountMetadata:
|
||||
description: ResourceMeta carries metadata common to all internal
|
||||
resources
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
template:
|
||||
description: PodTemplateSpec describes the data a pod should have
|
||||
when created from a template
|
||||
|
||||
@@ -64,6 +64,54 @@ spec:
|
||||
spec:
|
||||
description: AutoscalingRunnerSetSpec defines the desired state of AutoscalingRunnerSet
|
||||
properties:
|
||||
autoscalingListener:
|
||||
description: ResourceMeta carries metadata common to all internal resources
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
ephemeralRunnerConfigSecretMetadata:
|
||||
description: ResourceMeta carries metadata common to all internal resources
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
ephemeralRunnerMetadata:
|
||||
description: ResourceMeta carries metadata common to all internal resources
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
ephemeralRunnerSetMetadata:
|
||||
description: ResourceMeta carries metadata common to all internal resources
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
githubConfigSecret:
|
||||
description: Required
|
||||
type: string
|
||||
@@ -99,6 +147,18 @@ spec:
|
||||
x-kubernetes-map-type: atomic
|
||||
type: object
|
||||
type: object
|
||||
listenerConfigSecretMetadata:
|
||||
description: ResourceMeta carries metadata common to all internal resources
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
listenerMetrics:
|
||||
description: MetricsConfig holds configuration parameters for each metric type
|
||||
properties:
|
||||
@@ -143,6 +203,42 @@ spec:
|
||||
type: object
|
||||
type: object
|
||||
type: object
|
||||
listenerRoleBindingMetadata:
|
||||
description: ResourceMeta carries metadata common to all internal resources
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
listenerRoleMetadata:
|
||||
description: ResourceMeta carries metadata common to all internal resources
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
listenerServiceAccountMetadata:
|
||||
description: ResourceMeta carries metadata common to all internal resources
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
listenerTemplate:
|
||||
description: PodTemplateSpec describes the data a pod should have when created from a template
|
||||
properties:
|
||||
|
||||
@@ -70,6 +70,18 @@ spec:
|
||||
spec:
|
||||
description: EphemeralRunnerSpec defines the desired state of EphemeralRunner
|
||||
properties:
|
||||
ephemeralRunnerConfigSecretMetadata:
|
||||
description: ResourceMeta carries metadata common to all internal resources
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
githubConfigSecret:
|
||||
type: string
|
||||
githubConfigUrl:
|
||||
|
||||
@@ -58,9 +58,33 @@ spec:
|
||||
spec:
|
||||
description: EphemeralRunnerSetSpec defines the desired state of EphemeralRunnerSet
|
||||
properties:
|
||||
ephemeralRunnerMetadata:
|
||||
description: ResourceMeta carries metadata common to all internal resources
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
ephemeralRunnerSpec:
|
||||
description: EphemeralRunnerSpec is the spec of the ephemeral runner
|
||||
properties:
|
||||
ephemeralRunnerConfigSecretMetadata:
|
||||
description: ResourceMeta carries metadata common to all internal resources
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
githubConfigSecret:
|
||||
type: string
|
||||
githubConfigUrl:
|
||||
|
||||
@@ -9,11 +9,11 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1"
|
||||
"github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1/appconfig"
|
||||
"github.com/actions/actions-runner-controller/build"
|
||||
"github.com/actions/actions-runner-controller/logger"
|
||||
"github.com/actions/actions-runner-controller/vault"
|
||||
"github.com/actions/actions-runner-controller/vault/azurekeyvault"
|
||||
"github.com/actions/scaleset"
|
||||
@@ -23,7 +23,7 @@ import (
|
||||
const appName = "ghalistener"
|
||||
|
||||
type Config struct {
|
||||
ConfigureUrl string `json:"configure_url"`
|
||||
ConfigureURL string `json:"configure_url"`
|
||||
VaultType vault.VaultType `json:"vault_type"`
|
||||
VaultLookupKey string `json:"vault_lookup_key"`
|
||||
// If the VaultType is set to "azure_key_vault", this field must be populated.
|
||||
@@ -102,7 +102,7 @@ func Read(ctx context.Context, configPath string) (*Config, error) {
|
||||
|
||||
// Validate checks the configuration for errors.
|
||||
func (c *Config) Validate() error {
|
||||
if len(c.ConfigureUrl) == 0 {
|
||||
if len(c.ConfigureURL) == 0 {
|
||||
return fmt.Errorf("GitHubConfigUrl is not provided")
|
||||
}
|
||||
|
||||
@@ -137,37 +137,7 @@ func (c *Config) Validate() error {
|
||||
}
|
||||
|
||||
func (c *Config) Logger() (*slog.Logger, error) {
|
||||
var lvl slog.Level
|
||||
switch strings.ToLower(c.LogLevel) {
|
||||
case "debug":
|
||||
lvl = slog.LevelDebug
|
||||
case "info":
|
||||
lvl = slog.LevelInfo
|
||||
case "warn":
|
||||
lvl = slog.LevelWarn
|
||||
case "error":
|
||||
lvl = slog.LevelError
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid log level: %s", c.LogLevel)
|
||||
}
|
||||
|
||||
var logger *slog.Logger
|
||||
switch c.LogFormat {
|
||||
case "json":
|
||||
logger = slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
|
||||
AddSource: true,
|
||||
Level: lvl,
|
||||
}))
|
||||
case "text":
|
||||
logger = slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
|
||||
AddSource: true,
|
||||
Level: lvl,
|
||||
}))
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid log format: %s", c.LogFormat)
|
||||
}
|
||||
|
||||
return logger.With("app", appName), nil
|
||||
return logger.New(c.LogLevel, c.LogFormat)
|
||||
}
|
||||
|
||||
func (c *Config) ActionsClient(logger *slog.Logger, clientOptions ...scaleset.HTTPOption) (*scaleset.Client, error) {
|
||||
@@ -207,7 +177,7 @@ func (c *Config) ActionsClient(logger *slog.Logger, clientOptions ...scaleset.HT
|
||||
case "":
|
||||
c, err := scaleset.NewClientWithGitHubApp(
|
||||
scaleset.ClientWithGitHubAppConfig{
|
||||
GitHubConfigURL: c.ConfigureUrl,
|
||||
GitHubConfigURL: c.ConfigureURL,
|
||||
GitHubAppAuth: scaleset.GitHubAppAuth{
|
||||
ClientID: c.AppConfig.AppID,
|
||||
InstallationID: c.AppConfig.AppInstallationID,
|
||||
@@ -224,7 +194,7 @@ func (c *Config) ActionsClient(logger *slog.Logger, clientOptions ...scaleset.HT
|
||||
default:
|
||||
c, err := scaleset.NewClientWithPersonalAccessToken(
|
||||
scaleset.NewClientWithPersonalAccessTokenConfig{
|
||||
GitHubConfigURL: c.ConfigureUrl,
|
||||
GitHubConfigURL: c.ConfigureURL,
|
||||
PersonalAccessToken: c.Token,
|
||||
SystemInfo: systemInfo,
|
||||
},
|
||||
|
||||
@@ -54,7 +54,7 @@ func TestCustomerServerRootCA(t *testing.T) {
|
||||
certsString = certsString + string(intermediate)
|
||||
|
||||
config := config.Config{
|
||||
ConfigureUrl: server.ConfigURLForOrg("myorg"),
|
||||
ConfigureURL: server.ConfigURLForOrg("myorg"),
|
||||
ServerRootCA: certsString,
|
||||
AppConfig: &appconfig.AppConfig{
|
||||
Token: "token",
|
||||
@@ -85,7 +85,7 @@ func TestProxySettings(t *testing.T) {
|
||||
defer os.Setenv("http_proxy", prevProxy)
|
||||
|
||||
config := config.Config{
|
||||
ConfigureUrl: "https://github.com/org/repo",
|
||||
ConfigureURL: "https://github.com/org/repo",
|
||||
AppConfig: &appconfig.AppConfig{
|
||||
Token: "token",
|
||||
},
|
||||
@@ -103,7 +103,7 @@ func TestProxySettings(t *testing.T) {
|
||||
defer os.Setenv("https_proxy", prevProxy)
|
||||
|
||||
config := config.Config{
|
||||
ConfigureUrl: "https://github.com/org/repo",
|
||||
ConfigureURL: "https://github.com/org/repo",
|
||||
AppConfig: &appconfig.AppConfig{
|
||||
Token: "token",
|
||||
},
|
||||
@@ -124,7 +124,7 @@ func TestProxySettings(t *testing.T) {
|
||||
defer os.Setenv("no_proxy", prevNoProxy)
|
||||
|
||||
config := config.Config{
|
||||
ConfigureUrl: "https://github.com/org/repo",
|
||||
ConfigureURL: "https://github.com/org/repo",
|
||||
AppConfig: &appconfig.AppConfig{
|
||||
Token: "token",
|
||||
},
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
func TestConfigValidationMinMax(t *testing.T) {
|
||||
config := &Config{
|
||||
ConfigureUrl: "github.com/some_org/some_repo",
|
||||
ConfigureURL: "github.com/some_org/some_repo",
|
||||
EphemeralRunnerSetNamespace: "namespace",
|
||||
EphemeralRunnerSetName: "deployment",
|
||||
RunnerScaleSetID: 1,
|
||||
@@ -26,7 +26,7 @@ func TestConfigValidationMinMax(t *testing.T) {
|
||||
|
||||
func TestConfigValidationMissingToken(t *testing.T) {
|
||||
config := &Config{
|
||||
ConfigureUrl: "github.com/some_org/some_repo",
|
||||
ConfigureURL: "github.com/some_org/some_repo",
|
||||
EphemeralRunnerSetNamespace: "namespace",
|
||||
EphemeralRunnerSetName: "deployment",
|
||||
RunnerScaleSetID: 1,
|
||||
@@ -46,7 +46,7 @@ func TestConfigValidationAppKey(t *testing.T) {
|
||||
AppID: "1",
|
||||
AppInstallationID: 10,
|
||||
},
|
||||
ConfigureUrl: "github.com/some_org/some_repo",
|
||||
ConfigureURL: "github.com/some_org/some_repo",
|
||||
EphemeralRunnerSetNamespace: "namespace",
|
||||
EphemeralRunnerSetName: "deployment",
|
||||
RunnerScaleSetID: 1,
|
||||
@@ -63,7 +63,7 @@ func TestConfigValidationAppKey(t *testing.T) {
|
||||
AppID: "Iv23f8doAlphaNumer1c",
|
||||
AppInstallationID: 10,
|
||||
},
|
||||
ConfigureUrl: "github.com/some_org/some_repo",
|
||||
ConfigureURL: "github.com/some_org/some_repo",
|
||||
EphemeralRunnerSetNamespace: "namespace",
|
||||
EphemeralRunnerSetName: "deployment",
|
||||
RunnerScaleSetID: 1,
|
||||
@@ -82,7 +82,7 @@ func TestConfigValidationOnlyOneTypeOfCredentials(t *testing.T) {
|
||||
AppPrivateKey: "asdf",
|
||||
Token: "asdf",
|
||||
},
|
||||
ConfigureUrl: "github.com/some_org/some_repo",
|
||||
ConfigureURL: "github.com/some_org/some_repo",
|
||||
EphemeralRunnerSetNamespace: "namespace",
|
||||
EphemeralRunnerSetName: "deployment",
|
||||
RunnerScaleSetID: 1,
|
||||
@@ -94,7 +94,7 @@ func TestConfigValidationOnlyOneTypeOfCredentials(t *testing.T) {
|
||||
|
||||
func TestConfigValidation(t *testing.T) {
|
||||
config := &Config{
|
||||
ConfigureUrl: "https://github.com/actions",
|
||||
ConfigureURL: "https://github.com/actions",
|
||||
EphemeralRunnerSetNamespace: "namespace",
|
||||
EphemeralRunnerSetName: "deployment",
|
||||
RunnerScaleSetID: 1,
|
||||
@@ -125,7 +125,7 @@ func TestConfigValidationConfigUrl(t *testing.T) {
|
||||
func TestConfigValidationWithVaultConfig(t *testing.T) {
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
config := &Config{
|
||||
ConfigureUrl: "https://github.com/actions",
|
||||
ConfigureURL: "https://github.com/actions",
|
||||
EphemeralRunnerSetNamespace: "namespace",
|
||||
EphemeralRunnerSetName: "deployment",
|
||||
RunnerScaleSetID: 1,
|
||||
@@ -140,7 +140,7 @@ func TestConfigValidationWithVaultConfig(t *testing.T) {
|
||||
|
||||
t.Run("invalid vault type", func(t *testing.T) {
|
||||
config := &Config{
|
||||
ConfigureUrl: "https://github.com/actions",
|
||||
ConfigureURL: "https://github.com/actions",
|
||||
EphemeralRunnerSetNamespace: "namespace",
|
||||
EphemeralRunnerSetName: "deployment",
|
||||
RunnerScaleSetID: 1,
|
||||
@@ -155,7 +155,7 @@ func TestConfigValidationWithVaultConfig(t *testing.T) {
|
||||
|
||||
t.Run("vault type set without lookup key", func(t *testing.T) {
|
||||
config := &Config{
|
||||
ConfigureUrl: "https://github.com/actions",
|
||||
ConfigureURL: "https://github.com/actions",
|
||||
EphemeralRunnerSetNamespace: "namespace",
|
||||
EphemeralRunnerSetName: "deployment",
|
||||
RunnerScaleSetID: 1,
|
||||
|
||||
@@ -40,7 +40,7 @@ func main() {
|
||||
}
|
||||
|
||||
func run(ctx context.Context, config *config.Config) error {
|
||||
ghConfig, err := actions.ParseGitHubConfigFromURL(config.ConfigureUrl)
|
||||
ghConfig, err := actions.ParseGitHubConfigFromURL(config.ConfigureURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse GitHub config from URL: %w", err)
|
||||
}
|
||||
|
||||
@@ -56,6 +56,19 @@ spec:
|
||||
autoscalingRunnerSetNamespace:
|
||||
description: Required
|
||||
type: string
|
||||
configSecretMetadata:
|
||||
description: ResourceMeta carries metadata common to all internal
|
||||
resources
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
ephemeralRunnerSetName:
|
||||
description: Required
|
||||
type: string
|
||||
@@ -196,9 +209,48 @@ spec:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
roleBindingMetadata:
|
||||
description: ResourceMeta carries metadata common to all internal
|
||||
resources
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
roleMetadata:
|
||||
description: ResourceMeta carries metadata common to all internal
|
||||
resources
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
runnerScaleSetId:
|
||||
description: Required
|
||||
type: integer
|
||||
serviceAccountMetadata:
|
||||
description: ResourceMeta carries metadata common to all internal
|
||||
resources
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
template:
|
||||
description: PodTemplateSpec describes the data a pod should have
|
||||
when created from a template
|
||||
|
||||
@@ -64,6 +64,54 @@ spec:
|
||||
spec:
|
||||
description: AutoscalingRunnerSetSpec defines the desired state of AutoscalingRunnerSet
|
||||
properties:
|
||||
autoscalingListener:
|
||||
description: ResourceMeta carries metadata common to all internal resources
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
ephemeralRunnerConfigSecretMetadata:
|
||||
description: ResourceMeta carries metadata common to all internal resources
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
ephemeralRunnerMetadata:
|
||||
description: ResourceMeta carries metadata common to all internal resources
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
ephemeralRunnerSetMetadata:
|
||||
description: ResourceMeta carries metadata common to all internal resources
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
githubConfigSecret:
|
||||
description: Required
|
||||
type: string
|
||||
@@ -99,6 +147,18 @@ spec:
|
||||
x-kubernetes-map-type: atomic
|
||||
type: object
|
||||
type: object
|
||||
listenerConfigSecretMetadata:
|
||||
description: ResourceMeta carries metadata common to all internal resources
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
listenerMetrics:
|
||||
description: MetricsConfig holds configuration parameters for each metric type
|
||||
properties:
|
||||
@@ -143,6 +203,42 @@ spec:
|
||||
type: object
|
||||
type: object
|
||||
type: object
|
||||
listenerRoleBindingMetadata:
|
||||
description: ResourceMeta carries metadata common to all internal resources
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
listenerRoleMetadata:
|
||||
description: ResourceMeta carries metadata common to all internal resources
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
listenerServiceAccountMetadata:
|
||||
description: ResourceMeta carries metadata common to all internal resources
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
listenerTemplate:
|
||||
description: PodTemplateSpec describes the data a pod should have when created from a template
|
||||
properties:
|
||||
|
||||
@@ -70,6 +70,18 @@ spec:
|
||||
spec:
|
||||
description: EphemeralRunnerSpec defines the desired state of EphemeralRunner
|
||||
properties:
|
||||
ephemeralRunnerConfigSecretMetadata:
|
||||
description: ResourceMeta carries metadata common to all internal resources
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
githubConfigSecret:
|
||||
type: string
|
||||
githubConfigUrl:
|
||||
|
||||
@@ -58,9 +58,33 @@ spec:
|
||||
spec:
|
||||
description: EphemeralRunnerSetSpec defines the desired state of EphemeralRunnerSet
|
||||
properties:
|
||||
ephemeralRunnerMetadata:
|
||||
description: ResourceMeta carries metadata common to all internal resources
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
ephemeralRunnerSpec:
|
||||
description: EphemeralRunnerSpec is the spec of the ephemeral runner
|
||||
properties:
|
||||
ephemeralRunnerConfigSecretMetadata:
|
||||
description: ResourceMeta carries metadata common to all internal resources
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
githubConfigSecret:
|
||||
type: string
|
||||
githubConfigUrl:
|
||||
|
||||
@@ -15,7 +15,8 @@ import (
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/log"
|
||||
|
||||
ghalistenerconfig "github.com/actions/actions-runner-controller/cmd/ghalistener/config"
|
||||
"github.com/actions/actions-runner-controller/github/actions/fake"
|
||||
scalefake "github.com/actions/actions-runner-controller/controllers/actions.github.com/multiclient/fake"
|
||||
"github.com/actions/actions-runner-controller/controllers/actions.github.com/secretresolver"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
@@ -43,7 +44,10 @@ var _ = Describe("Test AutoScalingListener controller", func() {
|
||||
autoscalingNS, mgr = createNamespace(GinkgoT(), k8sClient)
|
||||
configSecret = createDefaultSecret(GinkgoT(), k8sClient, autoscalingNS.Name)
|
||||
|
||||
secretResolver := NewSecretResolver(mgr.GetClient(), fake.NewMultiClient())
|
||||
secretResolver := secretresolver.New(
|
||||
mgr.GetClient(),
|
||||
scalefake.NewMultiClient(),
|
||||
)
|
||||
|
||||
rb := ResourceBuilder{
|
||||
SecretResolver: secretResolver,
|
||||
@@ -459,7 +463,7 @@ var _ = Describe("Test AutoScalingListener customization", func() {
|
||||
autoscalingNS, mgr = createNamespace(GinkgoT(), k8sClient)
|
||||
configSecret = createDefaultSecret(GinkgoT(), k8sClient, autoscalingNS.Name)
|
||||
|
||||
secretResolver := NewSecretResolver(mgr.GetClient(), fake.NewMultiClient())
|
||||
secretResolver := secretresolver.New(mgr.GetClient(), scalefake.NewMultiClient())
|
||||
|
||||
rb := ResourceBuilder{
|
||||
SecretResolver: secretResolver,
|
||||
@@ -788,7 +792,7 @@ var _ = Describe("Test AutoScalingListener controller with proxy", func() {
|
||||
ctx = context.Background()
|
||||
autoscalingNS, mgr = createNamespace(GinkgoT(), k8sClient)
|
||||
configSecret = createDefaultSecret(GinkgoT(), k8sClient, autoscalingNS.Name)
|
||||
secretResolver := NewSecretResolver(mgr.GetClient(), fake.NewMultiClient())
|
||||
secretResolver := secretresolver.New(mgr.GetClient(), scalefake.NewMultiClient())
|
||||
|
||||
rb := ResourceBuilder{
|
||||
SecretResolver: secretResolver,
|
||||
@@ -991,7 +995,7 @@ var _ = Describe("Test AutoScalingListener controller with template modification
|
||||
autoscalingNS, mgr = createNamespace(GinkgoT(), k8sClient)
|
||||
configSecret = createDefaultSecret(GinkgoT(), k8sClient, autoscalingNS.Name)
|
||||
|
||||
secretResolver := NewSecretResolver(mgr.GetClient(), fake.NewMultiClient())
|
||||
secretResolver := secretresolver.New(mgr.GetClient(), scalefake.NewMultiClient())
|
||||
|
||||
rb := ResourceBuilder{
|
||||
SecretResolver: secretResolver,
|
||||
@@ -1094,7 +1098,7 @@ var _ = Describe("Test GitHub Server TLS configuration", func() {
|
||||
autoscalingNS, mgr = createNamespace(GinkgoT(), k8sClient)
|
||||
configSecret = createDefaultSecret(GinkgoT(), k8sClient, autoscalingNS.Name)
|
||||
|
||||
secretResolver := NewSecretResolver(mgr.GetClient(), fake.NewMultiClient())
|
||||
secretResolver := secretresolver.New(mgr.GetClient(), scalefake.NewMultiClient())
|
||||
|
||||
rb := ResourceBuilder{
|
||||
SecretResolver: secretResolver,
|
||||
|
||||
@@ -25,7 +25,7 @@ import (
|
||||
|
||||
"github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1"
|
||||
"github.com/actions/actions-runner-controller/build"
|
||||
"github.com/actions/actions-runner-controller/github/actions"
|
||||
"github.com/actions/scaleset"
|
||||
"github.com/go-logr/logr"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
@@ -78,7 +78,6 @@ type AutoscalingRunnerSetReconciler struct {
|
||||
DefaultRunnerScaleSetListenerImage string
|
||||
DefaultRunnerScaleSetListenerImagePullSecrets []string
|
||||
UpdateStrategy UpdateStrategy
|
||||
ActionsClient actions.MultiClient
|
||||
ResourceBuilder
|
||||
}
|
||||
|
||||
@@ -427,17 +426,16 @@ func (r *AutoscalingRunnerSetReconciler) createRunnerScaleSet(ctx context.Contex
|
||||
if runnerScaleSet == nil {
|
||||
runnerScaleSet, err = actionsClient.CreateRunnerScaleSet(
|
||||
ctx,
|
||||
&actions.RunnerScaleSet{
|
||||
&scaleset.RunnerScaleSet{
|
||||
Name: autoscalingRunnerSet.Spec.RunnerScaleSetName,
|
||||
RunnerGroupId: runnerGroupID,
|
||||
Labels: []actions.Label{
|
||||
RunnerGroupID: runnerGroupID,
|
||||
Labels: []scaleset.Label{
|
||||
{
|
||||
Name: autoscalingRunnerSet.Spec.RunnerScaleSetName,
|
||||
Type: "System",
|
||||
},
|
||||
},
|
||||
RunnerSetting: actions.RunnerSetting{
|
||||
Ephemeral: true,
|
||||
RunnerSetting: scaleset.RunnerSetting{
|
||||
DisableUpdate: true,
|
||||
},
|
||||
})
|
||||
@@ -447,15 +445,11 @@ func (r *AutoscalingRunnerSetReconciler) createRunnerScaleSet(ctx context.Contex
|
||||
}
|
||||
}
|
||||
|
||||
actionsClient.SetUserAgent(actions.UserAgentInfo{
|
||||
Version: build.Version,
|
||||
CommitSHA: build.CommitSHA,
|
||||
ScaleSetID: runnerScaleSet.Id,
|
||||
HasProxy: autoscalingRunnerSet.Spec.Proxy != nil,
|
||||
Subsystem: "controller",
|
||||
})
|
||||
info := actionsClient.SystemInfo()
|
||||
info.ScaleSetID = runnerScaleSet.ID
|
||||
actionsClient.SetSystemInfo(info)
|
||||
|
||||
logger.Info("Created/Reused a runner scale set", "id", runnerScaleSet.Id, "runnerGroupName", runnerScaleSet.RunnerGroupName)
|
||||
logger.Info("Created/Reused a runner scale set", "id", runnerScaleSet.ID, "runnerGroupName", runnerScaleSet.RunnerGroupName)
|
||||
if autoscalingRunnerSet.Annotations == nil {
|
||||
autoscalingRunnerSet.Annotations = map[string]string{}
|
||||
}
|
||||
@@ -466,7 +460,7 @@ func (r *AutoscalingRunnerSetReconciler) createRunnerScaleSet(ctx context.Contex
|
||||
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[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")
|
||||
@@ -477,7 +471,7 @@ func (r *AutoscalingRunnerSetReconciler) createRunnerScaleSet(ctx context.Contex
|
||||
}
|
||||
|
||||
logger.Info("Updated with runner scale set ID, name and runner group name as an annotation",
|
||||
"id", runnerScaleSet.Id,
|
||||
"id", runnerScaleSet.ID,
|
||||
"name", runnerScaleSet.Name,
|
||||
"runnerGroupName", runnerScaleSet.RunnerGroupName)
|
||||
return ctrl.Result{}, nil
|
||||
@@ -507,7 +501,7 @@ func (r *AutoscalingRunnerSetReconciler) updateRunnerScaleSetRunnerGroup(ctx con
|
||||
runnerGroupID = int(runnerGroup.ID)
|
||||
}
|
||||
|
||||
updatedRunnerScaleSet, err := actionsClient.UpdateRunnerScaleSet(ctx, runnerScaleSetID, &actions.RunnerScaleSet{RunnerGroupId: runnerGroupID})
|
||||
updatedRunnerScaleSet, err := actionsClient.UpdateRunnerScaleSet(ctx, runnerScaleSetID, &scaleset.RunnerScaleSet{RunnerGroupID: runnerGroupID})
|
||||
if err != nil {
|
||||
logger.Error(err, "Failed to update runner scale set", "runnerScaleSetId", runnerScaleSetID)
|
||||
return ctrl.Result{}, err
|
||||
@@ -544,7 +538,7 @@ func (r *AutoscalingRunnerSetReconciler) updateRunnerScaleSetName(ctx context.Co
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
updatedRunnerScaleSet, err := actionsClient.UpdateRunnerScaleSet(ctx, runnerScaleSetID, &actions.RunnerScaleSet{Name: autoscalingRunnerSet.Spec.RunnerScaleSetName})
|
||||
updatedRunnerScaleSet, err := actionsClient.UpdateRunnerScaleSet(ctx, runnerScaleSetID, &scaleset.RunnerScaleSet{Name: autoscalingRunnerSet.Spec.RunnerScaleSetName})
|
||||
if err != nil {
|
||||
logger.Error(err, "Failed to update runner scale set", "runnerScaleSetId", runnerScaleSetID)
|
||||
return ctrl.Result{}, err
|
||||
|
||||
@@ -3,13 +3,12 @@ package actionsgithubcom
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
@@ -19,7 +18,6 @@ import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/log"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
@@ -28,9 +26,10 @@ import (
|
||||
|
||||
"github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1"
|
||||
"github.com/actions/actions-runner-controller/build"
|
||||
"github.com/actions/actions-runner-controller/github/actions"
|
||||
"github.com/actions/actions-runner-controller/github/actions/fake"
|
||||
"github.com/actions/actions-runner-controller/github/actions/testserver"
|
||||
"github.com/actions/actions-runner-controller/controllers/actions.github.com/multiclient"
|
||||
scalefake "github.com/actions/actions-runner-controller/controllers/actions.github.com/multiclient/fake"
|
||||
"github.com/actions/actions-runner-controller/controllers/actions.github.com/secretresolver"
|
||||
"github.com/actions/scaleset"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -63,6 +62,10 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() {
|
||||
autoscalingNS, mgr = createNamespace(GinkgoT(), k8sClient)
|
||||
configSecret = createDefaultSecret(GinkgoT(), k8sClient, autoscalingNS.Name)
|
||||
|
||||
// Track runner group mappings for dynamic responses
|
||||
runnerGroupMap := map[int]string{1: "testgroup"} // ID -> Name mapping
|
||||
runnerGroupMapLock := &sync.RWMutex{} // Thread-safe access
|
||||
|
||||
controller = &AutoscalingRunnerSetReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
@@ -70,10 +73,30 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() {
|
||||
ControllerNamespace: autoscalingNS.Name,
|
||||
DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc",
|
||||
ResourceBuilder: ResourceBuilder{
|
||||
SecretResolver: &SecretResolver{
|
||||
k8sClient: k8sClient,
|
||||
multiClient: fake.NewMultiClient(),
|
||||
},
|
||||
SecretResolver: secretresolver.New(mgr.GetClient(), scalefake.NewMultiClient(
|
||||
scalefake.WithClient(
|
||||
scalefake.NewClient(
|
||||
scalefake.WithGetRunnerGroupByNameFunc(func(ctx context.Context, groupName string) (*scaleset.RunnerGroup, error) {
|
||||
// Support both "testgroup" and "testgroup2"
|
||||
// Update the mapping when a new group is requested
|
||||
runnerGroupMapLock.Lock()
|
||||
runnerGroupMap[1] = groupName
|
||||
runnerGroupMapLock.Unlock()
|
||||
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.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
|
||||
}),
|
||||
scalefake.WithDeleteRunnerScaleSet(nil),
|
||||
),
|
||||
),
|
||||
)),
|
||||
},
|
||||
}
|
||||
err := controller.SetupWithManager(mgr)
|
||||
@@ -681,25 +704,34 @@ var _ = Describe("Test AutoScalingController updates", Ordered, func() {
|
||||
autoscalingNS, mgr = createNamespace(GinkgoT(), k8sClient)
|
||||
configSecret = createDefaultSecret(GinkgoT(), k8sClient, autoscalingNS.Name)
|
||||
|
||||
multiClient := fake.NewMultiClient(
|
||||
fake.WithDefaultClient(
|
||||
fake.NewFakeClient(
|
||||
fake.WithUpdateRunnerScaleSet(
|
||||
&actions.RunnerScaleSet{
|
||||
Id: 1,
|
||||
multiClient := scalefake.NewMultiClient(
|
||||
scalefake.WithClient(
|
||||
scalefake.NewClient(
|
||||
scalefake.WithGenerateJitRunnerConfig(
|
||||
&scaleset.RunnerScaleSetJitRunnerConfig{
|
||||
Runner: &scaleset.RunnerReference{ID: 1, Name: "test-runner"},
|
||||
EncodedJITConfig: "fake-jit-config",
|
||||
},
|
||||
nil,
|
||||
),
|
||||
scalefake.WithGetRunnerGroupByName(&scaleset.RunnerGroup{ID: 1, Name: "testgroup"}, nil),
|
||||
scalefake.WithGetRunnerScaleSet(nil, nil),
|
||||
scalefake.WithCreateRunnerScaleSet(&scaleset.RunnerScaleSet{ID: 1, Name: "testset", RunnerGroupID: 1, RunnerGroupName: "testgroup"}, nil),
|
||||
scalefake.WithUpdateRunnerScaleSet(
|
||||
&scaleset.RunnerScaleSet{
|
||||
ID: 1,
|
||||
Name: "testset_update",
|
||||
RunnerGroupId: 1,
|
||||
RunnerGroupID: 1,
|
||||
RunnerGroupName: "testgroup",
|
||||
Labels: []actions.Label{{Type: "test", Name: "test"}},
|
||||
RunnerSetting: actions.RunnerSetting{},
|
||||
Labels: []scaleset.Label{{Type: "test", Name: "test"}},
|
||||
RunnerSetting: scaleset.RunnerSetting{},
|
||||
CreatedOn: time.Now(),
|
||||
RunnerJitConfigUrl: "test.test.test",
|
||||
RunnerJitConfigURL: "test.test.test",
|
||||
Statistics: nil,
|
||||
},
|
||||
nil,
|
||||
),
|
||||
),
|
||||
nil,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -710,10 +742,7 @@ var _ = Describe("Test AutoScalingController updates", Ordered, func() {
|
||||
ControllerNamespace: autoscalingNS.Name,
|
||||
DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc",
|
||||
ResourceBuilder: ResourceBuilder{
|
||||
SecretResolver: &SecretResolver{
|
||||
k8sClient: k8sClient,
|
||||
multiClient: multiClient,
|
||||
},
|
||||
SecretResolver: secretresolver.New(mgr.GetClient(), multiClient),
|
||||
},
|
||||
}
|
||||
err := controller.SetupWithManager(mgr)
|
||||
@@ -830,10 +859,7 @@ var _ = Describe("Test AutoscalingController creation failures", Ordered, func()
|
||||
ControllerNamespace: autoscalingNS.Name,
|
||||
DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc",
|
||||
ResourceBuilder: ResourceBuilder{
|
||||
SecretResolver: &SecretResolver{
|
||||
k8sClient: k8sClient,
|
||||
multiClient: fake.NewMultiClient(),
|
||||
},
|
||||
SecretResolver: secretresolver.New(mgr.GetClient(), scalefake.NewMultiClient()),
|
||||
},
|
||||
}
|
||||
err := controller.SetupWithManager(mgr)
|
||||
@@ -953,7 +979,6 @@ var _ = Describe("Test client optional configuration", Ordered, func() {
|
||||
ctx = context.Background()
|
||||
autoscalingNS, mgr = createNamespace(GinkgoT(), k8sClient)
|
||||
configSecret = createDefaultSecret(GinkgoT(), k8sClient, autoscalingNS.Name)
|
||||
multiClient := actions.NewMultiClient(logr.Discard())
|
||||
controller = &AutoscalingRunnerSetReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
@@ -961,10 +986,7 @@ var _ = Describe("Test client optional configuration", Ordered, func() {
|
||||
ControllerNamespace: autoscalingNS.Name,
|
||||
DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc",
|
||||
ResourceBuilder: ResourceBuilder{
|
||||
SecretResolver: &SecretResolver{
|
||||
k8sClient: k8sClient,
|
||||
multiClient: multiClient,
|
||||
},
|
||||
SecretResolver: secretresolver.New(mgr.GetClient(), multiclient.NewScaleset()),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -976,10 +998,11 @@ var _ = Describe("Test client optional configuration", Ordered, func() {
|
||||
|
||||
It("should be able to make requests to a server using a proxy", func() {
|
||||
serverSuccessfullyCalled := false
|
||||
proxy := testserver.New(GinkgoT(), http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
proxy := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
serverSuccessfullyCalled = true
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
defer proxy.Close()
|
||||
|
||||
min := 1
|
||||
max := 10
|
||||
@@ -1029,23 +1052,17 @@ var _ = Describe("Test client optional configuration", Ordered, func() {
|
||||
})
|
||||
|
||||
It("should be able to make requests to a server using a proxy with user info", func() {
|
||||
serverSuccessfullyCalled := false
|
||||
proxy := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
header := r.Header.Get("Proxy-Authorization")
|
||||
Expect(header).NotTo(BeEmpty())
|
||||
|
||||
header = strings.TrimPrefix(header, "Basic ")
|
||||
decoded, err := base64.StdEncoding.DecodeString(header)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(string(decoded)).To(Equal("test:password"))
|
||||
|
||||
serverSuccessfullyCalled = true
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
GinkgoT().Cleanup(func() {
|
||||
proxy.Close()
|
||||
})
|
||||
|
||||
controller.ResourceBuilder.SecretResolver = secretresolver.New(k8sClient, scalefake.NewMultiClient(
|
||||
scalefake.WithClient(
|
||||
scalefake.NewClient(
|
||||
scalefake.WithGetRunnerGroupByName(&scaleset.RunnerGroup{ID: 1, Name: "testgroup"}, nil),
|
||||
scalefake.WithGetRunnerScaleSet(nil, nil),
|
||||
scalefake.WithCreateRunnerScaleSet(&scaleset.RunnerScaleSet{ID: 1, Name: "test-asrs", RunnerGroupID: 1, RunnerGroupName: "testgroup"}, nil),
|
||||
scalefake.WithUpdateRunnerScaleSet(&scaleset.RunnerScaleSet{ID: 1, Name: "test-asrs", RunnerGroupID: 1, RunnerGroupName: "testgroup"}, nil),
|
||||
scalefake.WithDeleteRunnerScaleSet(nil),
|
||||
),
|
||||
),
|
||||
))
|
||||
secretCredentials := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "proxy-credentials",
|
||||
@@ -1057,6 +1074,11 @@ var _ = Describe("Test client optional configuration", Ordered, func() {
|
||||
},
|
||||
}
|
||||
|
||||
proxy := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
defer proxy.Close()
|
||||
|
||||
err := k8sClient.Create(ctx, secretCredentials)
|
||||
Expect(err).NotTo(HaveOccurred(), "failed to create secret credentials")
|
||||
|
||||
@@ -1078,7 +1100,7 @@ var _ = Describe("Test client optional configuration", Ordered, func() {
|
||||
RunnerGroup: "testgroup",
|
||||
Proxy: &v1alpha1.ProxyConfig{
|
||||
HTTP: &v1alpha1.ProxyServerConfig{
|
||||
Url: proxy.URL,
|
||||
Url: "http://test:password@" + proxy.Listener.Addr().String(),
|
||||
CredentialSecretRef: "proxy-credentials",
|
||||
},
|
||||
},
|
||||
@@ -1098,14 +1120,24 @@ var _ = Describe("Test client optional configuration", Ordered, func() {
|
||||
err = k8sClient.Create(ctx, autoscalingRunnerSet)
|
||||
Expect(err).NotTo(HaveOccurred(), "failed to create AutoScalingRunnerSet")
|
||||
|
||||
// wait for server to be called
|
||||
// Verify proxy config with credentials is propagated to EphemeralRunnerSet
|
||||
Eventually(
|
||||
func() (bool, error) {
|
||||
return serverSuccessfullyCalled, nil
|
||||
func() (*v1alpha1.EphemeralRunnerSet, error) {
|
||||
runnerSetList := new(v1alpha1.EphemeralRunnerSetList)
|
||||
err := k8sClient.List(ctx, runnerSetList, client.InNamespace(autoscalingNS.Name))
|
||||
if err != nil || len(runnerSetList.Items) == 0 {
|
||||
return nil, err
|
||||
}
|
||||
return &runnerSetList.Items[0], nil
|
||||
},
|
||||
autoscalingRunnerSetTestTimeout,
|
||||
1*time.Nanosecond,
|
||||
).Should(BeTrue(), "server was not called")
|
||||
autoscalingRunnerSetTestInterval,
|
||||
).Should(WithTransform(func(ers *v1alpha1.EphemeralRunnerSet) *v1alpha1.ProxyConfig {
|
||||
if ers != nil {
|
||||
return ers.Spec.EphemeralRunnerSpec.Proxy
|
||||
}
|
||||
return nil
|
||||
}, Not(BeNil())), "EphemeralRunnerSet should have proxy configuration with credentials")
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1149,10 +1181,17 @@ var _ = Describe("Test client optional configuration", Ordered, func() {
|
||||
ControllerNamespace: autoscalingNS.Name,
|
||||
DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc",
|
||||
ResourceBuilder: ResourceBuilder{
|
||||
SecretResolver: &SecretResolver{
|
||||
k8sClient: k8sClient,
|
||||
multiClient: fake.NewMultiClient(),
|
||||
},
|
||||
SecretResolver: secretresolver.New(mgr.GetClient(), scalefake.NewMultiClient(
|
||||
scalefake.WithClient(
|
||||
scalefake.NewClient(
|
||||
scalefake.WithGetRunnerGroupByName(&scaleset.RunnerGroup{ID: 1, Name: "testgroup"}, nil),
|
||||
scalefake.WithGetRunnerScaleSet(nil, nil),
|
||||
scalefake.WithCreateRunnerScaleSet(&scaleset.RunnerScaleSet{ID: 1, Name: "test-asrs", RunnerGroupID: 1, RunnerGroupName: "testgroup"}, nil),
|
||||
scalefake.WithUpdateRunnerScaleSet(&scaleset.RunnerScaleSet{ID: 1, Name: "test-asrs", RunnerGroupID: 1, RunnerGroupName: "testgroup"}, nil),
|
||||
scalefake.WithDeleteRunnerScaleSet(nil),
|
||||
),
|
||||
),
|
||||
)),
|
||||
},
|
||||
}
|
||||
err = controller.SetupWithManager(mgr)
|
||||
@@ -1162,10 +1201,7 @@ var _ = Describe("Test client optional configuration", Ordered, func() {
|
||||
})
|
||||
|
||||
It("should be able to make requests to a server using root CAs", func() {
|
||||
controller.SecretResolver = &SecretResolver{
|
||||
k8sClient: k8sClient,
|
||||
multiClient: actions.NewMultiClient(logr.Discard()),
|
||||
}
|
||||
controller.SecretResolver = secretresolver.New(k8sClient, multiclient.NewScaleset())
|
||||
|
||||
certsFolder := filepath.Join(
|
||||
"../../",
|
||||
@@ -1177,7 +1213,7 @@ var _ = Describe("Test client optional configuration", Ordered, func() {
|
||||
keyPath := filepath.Join(certsFolder, "server.key")
|
||||
|
||||
serverSuccessfullyCalled := false
|
||||
server := testserver.NewUnstarted(GinkgoT(), http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
server := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
serverSuccessfullyCalled = true
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
@@ -1186,6 +1222,7 @@ var _ = Describe("Test client optional configuration", Ordered, func() {
|
||||
|
||||
server.TLS = &tls.Config{Certificates: []tls.Certificate{cert}}
|
||||
server.StartTLS()
|
||||
defer server.Close()
|
||||
|
||||
min := 1
|
||||
max := 10
|
||||
@@ -1198,7 +1235,7 @@ var _ = Describe("Test client optional configuration", Ordered, func() {
|
||||
},
|
||||
},
|
||||
Spec: v1alpha1.AutoscalingRunnerSetSpec{
|
||||
GitHubConfigUrl: server.ConfigURLForOrg("my-org"),
|
||||
GitHubConfigUrl: server.URL + "/my-org",
|
||||
GitHubConfigSecret: configSecret.Name,
|
||||
GitHubServerTLS: &v1alpha1.TLSConfig{
|
||||
CertificateFrom: &v1alpha1.TLSCertificateSource{
|
||||
@@ -1391,10 +1428,7 @@ var _ = Describe("Test external permissions cleanup", Ordered, func() {
|
||||
ControllerNamespace: autoscalingNS.Name,
|
||||
DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc",
|
||||
ResourceBuilder: ResourceBuilder{
|
||||
SecretResolver: &SecretResolver{
|
||||
k8sClient: k8sClient,
|
||||
multiClient: fake.NewMultiClient(),
|
||||
},
|
||||
SecretResolver: secretresolver.New(mgr.GetClient(), scalefake.NewMultiClient()),
|
||||
},
|
||||
}
|
||||
err := controller.SetupWithManager(mgr)
|
||||
@@ -1554,10 +1588,7 @@ var _ = Describe("Test external permissions cleanup", Ordered, func() {
|
||||
ControllerNamespace: autoscalingNS.Name,
|
||||
DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc",
|
||||
ResourceBuilder: ResourceBuilder{
|
||||
SecretResolver: &SecretResolver{
|
||||
k8sClient: k8sClient,
|
||||
multiClient: fake.NewMultiClient(),
|
||||
},
|
||||
SecretResolver: secretresolver.New(mgr.GetClient(), scalefake.NewMultiClient()),
|
||||
},
|
||||
}
|
||||
err := controller.SetupWithManager(mgr)
|
||||
@@ -1767,10 +1798,7 @@ var _ = Describe("Test resource version and build version mismatch", func() {
|
||||
ControllerNamespace: autoscalingNS.Name,
|
||||
DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc",
|
||||
ResourceBuilder: ResourceBuilder{
|
||||
SecretResolver: &SecretResolver{
|
||||
k8sClient: k8sClient,
|
||||
multiClient: fake.NewMultiClient(),
|
||||
},
|
||||
SecretResolver: secretresolver.New(mgr.GetClient(), scalefake.NewMultiClient()),
|
||||
},
|
||||
}
|
||||
err := controller.SetupWithManager(mgr)
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
kclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
type object[T kclient.Object] interface {
|
||||
type kubernetesObject[T kclient.Object] interface {
|
||||
kclient.Object
|
||||
DeepCopy() T
|
||||
}
|
||||
@@ -15,7 +15,7 @@ type patcher interface {
|
||||
Patch(ctx context.Context, obj kclient.Object, patch kclient.Patch, opts ...kclient.PatchOption) error
|
||||
}
|
||||
|
||||
func patch[T object[T]](ctx context.Context, client patcher, obj T, update func(obj T)) error {
|
||||
func patch[T kubernetesObject[T]](ctx context.Context, client patcher, obj T, update func(obj T)) error {
|
||||
original := obj.DeepCopy()
|
||||
update(obj)
|
||||
return client.Patch(ctx, obj, kclient.MergeFrom(original))
|
||||
@@ -25,7 +25,7 @@ type subResourcePatcher interface {
|
||||
Patch(ctx context.Context, obj kclient.Object, patch kclient.Patch, opts ...kclient.SubResourcePatchOption) error
|
||||
}
|
||||
|
||||
func patchSubResource[T object[T]](ctx context.Context, client subResourcePatcher, obj T, update func(obj T)) error {
|
||||
func patchSubResource[T kubernetesObject[T]](ctx context.Context, client subResourcePatcher, obj T, update func(obj T)) error {
|
||||
original := obj.DeepCopy()
|
||||
update(obj)
|
||||
return client.Patch(ctx, obj, kclient.MergeFrom(original))
|
||||
|
||||
@@ -27,6 +27,7 @@ import (
|
||||
|
||||
"github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1"
|
||||
"github.com/actions/actions-runner-controller/github/actions"
|
||||
"github.com/actions/scaleset"
|
||||
"github.com/go-logr/logr"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
@@ -599,7 +600,7 @@ func (r *EphemeralRunnerReconciler) deletePodAsFailed(ctx context.Context, ephem
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *EphemeralRunnerReconciler) createRunnerJitConfig(ctx context.Context, ephemeralRunner *v1alpha1.EphemeralRunner, log logr.Logger) (*actions.RunnerScaleSetJitRunnerConfig, error) {
|
||||
func (r *EphemeralRunnerReconciler) createRunnerJitConfig(ctx context.Context, ephemeralRunner *v1alpha1.EphemeralRunner, log logr.Logger) (*scaleset.RunnerScaleSetJitRunnerConfig, error) {
|
||||
// Runner is not registered with the service. We need to register it first
|
||||
log.Info("Creating ephemeral runner JIT config")
|
||||
actionsClient, err := r.GetActionsService(ctx, ephemeralRunner)
|
||||
@@ -607,7 +608,7 @@ func (r *EphemeralRunnerReconciler) createRunnerJitConfig(ctx context.Context, e
|
||||
return nil, fmt.Errorf("failed to get actions client for generating JIT config: %w", err)
|
||||
}
|
||||
|
||||
jitSettings := &actions.RunnerScaleSetJitRunnerSetting{
|
||||
jitSettings := &scaleset.RunnerScaleSetJitRunnerSetting{
|
||||
Name: ephemeralRunner.Name,
|
||||
}
|
||||
|
||||
@@ -618,9 +619,9 @@ func (r *EphemeralRunnerReconciler) createRunnerJitConfig(ctx context.Context, e
|
||||
}
|
||||
}
|
||||
|
||||
jitConfig, err := actionsClient.GenerateJitRunnerConfig(ctx, jitSettings, ephemeralRunner.Spec.RunnerScaleSetId)
|
||||
jitConfig, err := actionsClient.GenerateJitRunnerConfig(ctx, jitSettings, ephemeralRunner.Spec.RunnerScaleSetID)
|
||||
if err == nil { // if NO error
|
||||
log.Info("Created ephemeral runner JIT config", "runnerId", jitConfig.Runner.Id)
|
||||
log.Info("Created ephemeral runner JIT config", "runnerId", jitConfig.Runner.ID)
|
||||
return jitConfig, nil
|
||||
}
|
||||
|
||||
@@ -652,10 +653,10 @@ func (r *EphemeralRunnerReconciler) createRunnerJitConfig(ctx context.Context, e
|
||||
return nil, fmt.Errorf("%w: runner existed, retry configuration", retryableError)
|
||||
}
|
||||
|
||||
log.Info("Found the runner with the same name", "runnerId", existingRunner.Id, "runnerScaleSetId", existingRunner.RunnerScaleSetId)
|
||||
if existingRunner.RunnerScaleSetId == ephemeralRunner.Spec.RunnerScaleSetId {
|
||||
log.Info("Found the runner with the same name", "runnerId", existingRunner.ID, "runnerScaleSetId", existingRunner.RunnerScaleSetID)
|
||||
if existingRunner.RunnerScaleSetID == ephemeralRunner.Spec.RunnerScaleSetID {
|
||||
log.Info("Removing the runner with the same name")
|
||||
err := actionsClient.RemoveRunner(ctx, int64(existingRunner.Id))
|
||||
err := actionsClient.RemoveRunner(ctx, int64(existingRunner.ID))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to remove runner from the service: %w", err)
|
||||
}
|
||||
@@ -731,7 +732,7 @@ func (r *EphemeralRunnerReconciler) createPod(ctx context.Context, runner *v1alp
|
||||
}
|
||||
|
||||
log.Info("Created ephemeral runner pod",
|
||||
"runnerScaleSetId", runner.Spec.RunnerScaleSetId,
|
||||
"runnerScaleSetId", runner.Spec.RunnerScaleSetID,
|
||||
"runnerName", runner.Status.RunnerName,
|
||||
"runnerId", runner.Status.RunnerId,
|
||||
"configUrl", runner.Spec.GitHubConfigUrl,
|
||||
@@ -740,7 +741,7 @@ func (r *EphemeralRunnerReconciler) createPod(ctx context.Context, runner *v1alp
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
func (r *EphemeralRunnerReconciler) createSecret(ctx context.Context, runner *v1alpha1.EphemeralRunner, jitConfig *actions.RunnerScaleSetJitRunnerConfig, log logr.Logger) (*corev1.Secret, error) {
|
||||
func (r *EphemeralRunnerReconciler) createSecret(ctx context.Context, runner *v1alpha1.EphemeralRunner, jitConfig *scaleset.RunnerScaleSetJitRunnerConfig, log logr.Logger) (*corev1.Secret, error) {
|
||||
log.Info("Creating new secret for ephemeral runner")
|
||||
jitSecret := r.newEphemeralRunnerJitSecret(runner, jitConfig)
|
||||
|
||||
|
||||
@@ -14,10 +14,11 @@ import (
|
||||
|
||||
"github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1"
|
||||
"github.com/actions/actions-runner-controller/github/actions"
|
||||
"github.com/go-logr/logr"
|
||||
|
||||
"github.com/actions/actions-runner-controller/github/actions/fake"
|
||||
"github.com/actions/actions-runner-controller/github/actions/testserver"
|
||||
"github.com/actions/actions-runner-controller/controllers/actions.github.com/multiclient"
|
||||
scalefake "github.com/actions/actions-runner-controller/controllers/actions.github.com/multiclient/fake"
|
||||
"github.com/actions/actions-runner-controller/controllers/actions.github.com/secretresolver"
|
||||
"github.com/actions/scaleset"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
@@ -43,7 +44,7 @@ func newExampleRunner(name, namespace, configSecretName string) *v1alpha1.Epheme
|
||||
Spec: v1alpha1.EphemeralRunnerSpec{
|
||||
GitHubConfigUrl: "https://github.com/owner/repo",
|
||||
GitHubConfigSecret: configSecretName,
|
||||
RunnerScaleSetId: 1,
|
||||
RunnerScaleSetID: 1,
|
||||
PodTemplateSpec: corev1.PodTemplateSpec{
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: []corev1.Container{
|
||||
@@ -111,10 +112,19 @@ var _ = Describe("EphemeralRunner", func() {
|
||||
Scheme: mgr.GetScheme(),
|
||||
Log: logf.Log,
|
||||
ResourceBuilder: ResourceBuilder{
|
||||
SecretResolver: &SecretResolver{
|
||||
k8sClient: mgr.GetClient(),
|
||||
multiClient: fake.NewMultiClient(),
|
||||
},
|
||||
SecretResolver: secretresolver.New(mgr.GetClient(), scalefake.NewMultiClient(
|
||||
scalefake.WithClient(
|
||||
scalefake.NewClient(
|
||||
scalefake.WithGenerateJitRunnerConfig(
|
||||
&scaleset.RunnerScaleSetJitRunnerConfig{
|
||||
Runner: &scaleset.RunnerReference{ID: 1, Name: "test-runner"},
|
||||
EncodedJITConfig: "fake-jit-config",
|
||||
},
|
||||
nil,
|
||||
),
|
||||
),
|
||||
),
|
||||
)),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1096,12 +1106,12 @@ var _ = Describe("EphemeralRunner", func() {
|
||||
Scheme: mgr.GetScheme(),
|
||||
Log: logf.Log,
|
||||
ResourceBuilder: ResourceBuilder{
|
||||
SecretResolver: &SecretResolver{
|
||||
k8sClient: mgr.GetClient(),
|
||||
multiClient: fake.NewMultiClient(
|
||||
fake.WithDefaultClient(
|
||||
fake.NewFakeClient(
|
||||
fake.WithGetRunner(
|
||||
SecretResolver: secretresolver.New(
|
||||
mgr.GetClient(),
|
||||
scalefake.NewMultiClient(
|
||||
scalefake.WithClient(
|
||||
scalefake.NewClient(
|
||||
scalefake.WithGetRunner(
|
||||
nil,
|
||||
&actions.ActionsError{
|
||||
StatusCode: http.StatusNotFound,
|
||||
@@ -1110,11 +1120,17 @@ var _ = Describe("EphemeralRunner", func() {
|
||||
},
|
||||
},
|
||||
),
|
||||
scalefake.WithGenerateJitRunnerConfig(
|
||||
&scaleset.RunnerScaleSetJitRunnerConfig{
|
||||
Runner: &scaleset.RunnerReference{ID: 1, Name: "test-runner"},
|
||||
EncodedJITConfig: "fake-jit-config",
|
||||
},
|
||||
nil,
|
||||
),
|
||||
),
|
||||
nil,
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
},
|
||||
}
|
||||
err := controller.SetupWithManager(mgr)
|
||||
@@ -1181,10 +1197,19 @@ var _ = Describe("EphemeralRunner", func() {
|
||||
Scheme: mgr.GetScheme(),
|
||||
Log: logf.Log,
|
||||
ResourceBuilder: ResourceBuilder{
|
||||
SecretResolver: &SecretResolver{
|
||||
k8sClient: mgr.GetClient(),
|
||||
multiClient: fake.NewMultiClient(),
|
||||
},
|
||||
SecretResolver: secretresolver.New(mgr.GetClient(), scalefake.NewMultiClient(
|
||||
scalefake.WithClient(
|
||||
scalefake.NewClient(
|
||||
scalefake.WithGenerateJitRunnerConfig(
|
||||
&scaleset.RunnerScaleSetJitRunnerConfig{
|
||||
Runner: &scaleset.RunnerReference{ID: 1, Name: "test-runner"},
|
||||
EncodedJITConfig: "fake-jit-config",
|
||||
},
|
||||
nil,
|
||||
),
|
||||
),
|
||||
),
|
||||
)),
|
||||
},
|
||||
}
|
||||
err := controller.SetupWithManager(mgr)
|
||||
@@ -1196,10 +1221,10 @@ var _ = Describe("EphemeralRunner", func() {
|
||||
It("uses an actions client with proxy transport", func() {
|
||||
// Use an actual client
|
||||
controller.ResourceBuilder = ResourceBuilder{
|
||||
SecretResolver: &SecretResolver{
|
||||
k8sClient: mgr.GetClient(),
|
||||
multiClient: actions.NewMultiClient(logr.Discard()),
|
||||
},
|
||||
SecretResolver: secretresolver.New(
|
||||
mgr.GetClient(),
|
||||
multiclient.NewScaleset(),
|
||||
),
|
||||
}
|
||||
|
||||
proxySuccessfulllyCalled := false
|
||||
@@ -1355,10 +1380,7 @@ var _ = Describe("EphemeralRunner", func() {
|
||||
Scheme: mgr.GetScheme(),
|
||||
Log: logf.Log,
|
||||
ResourceBuilder: ResourceBuilder{
|
||||
SecretResolver: &SecretResolver{
|
||||
k8sClient: mgr.GetClient(),
|
||||
multiClient: fake.NewMultiClient(),
|
||||
},
|
||||
SecretResolver: secretresolver.New(mgr.GetClient(), scalefake.NewMultiClient()),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1379,7 +1401,7 @@ var _ = Describe("EphemeralRunner", func() {
|
||||
keyPath := filepath.Join(certsFolder, "server.key")
|
||||
|
||||
serverSuccessfullyCalled := false
|
||||
server := testserver.NewUnstarted(GinkgoT(), http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
server := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
serverSuccessfullyCalled = true
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
@@ -1388,17 +1410,18 @@ var _ = Describe("EphemeralRunner", func() {
|
||||
|
||||
server.TLS = &tls.Config{Certificates: []tls.Certificate{cert}}
|
||||
server.StartTLS()
|
||||
defer server.Close()
|
||||
|
||||
// Use an actual client
|
||||
controller.ResourceBuilder = ResourceBuilder{
|
||||
SecretResolver: &SecretResolver{
|
||||
k8sClient: mgr.GetClient(),
|
||||
multiClient: actions.NewMultiClient(logr.Discard()),
|
||||
},
|
||||
SecretResolver: secretresolver.New(
|
||||
mgr.GetClient(),
|
||||
multiclient.NewScaleset(),
|
||||
),
|
||||
}
|
||||
|
||||
ephemeralRunner := newExampleRunner("test-runner", autoScalingNS.Name, configSecret.Name)
|
||||
ephemeralRunner.Spec.GitHubConfigUrl = server.ConfigURLForOrg("my-org")
|
||||
ephemeralRunner.Spec.GitHubConfigUrl = server.URL + "/my-org"
|
||||
ephemeralRunner.Spec.GitHubServerTLS = &v1alpha1.TLSConfig{
|
||||
CertificateFrom: &v1alpha1.TLSCertificateSource{
|
||||
ConfigMapKeyRef: &corev1.ConfigMapKeySelector{
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
|
||||
"github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1"
|
||||
"github.com/actions/actions-runner-controller/controllers/actions.github.com/metrics"
|
||||
"github.com/actions/actions-runner-controller/controllers/actions.github.com/multiclient"
|
||||
"github.com/actions/actions-runner-controller/github/actions"
|
||||
"github.com/go-logr/logr"
|
||||
"go.uber.org/multierr"
|
||||
@@ -481,7 +482,7 @@ func (r *EphemeralRunnerSetReconciler) deleteIdleEphemeralRunners(ctx context.Co
|
||||
return multierr.Combine(errs...)
|
||||
}
|
||||
|
||||
func (r *EphemeralRunnerSetReconciler) deleteEphemeralRunnerWithActionsClient(ctx context.Context, ephemeralRunner *v1alpha1.EphemeralRunner, actionsClient actions.ActionsService, log logr.Logger) (bool, error) {
|
||||
func (r *EphemeralRunnerSetReconciler) deleteEphemeralRunnerWithActionsClient(ctx context.Context, ephemeralRunner *v1alpha1.EphemeralRunner, actionsClient multiclient.Client, log logr.Logger) (bool, error) {
|
||||
if err := actionsClient.RemoveRunner(ctx, int64(ephemeralRunner.Status.RunnerId)); err != nil {
|
||||
actionsError := &actions.ActionsError{}
|
||||
if !errors.As(err, &actionsError) {
|
||||
|
||||
@@ -2,7 +2,6 @@ package actionsgithubcom
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/http"
|
||||
@@ -19,16 +18,15 @@ import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/log"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/stretchr/testify/require"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1"
|
||||
"github.com/actions/actions-runner-controller/github/actions"
|
||||
"github.com/actions/actions-runner-controller/github/actions/fake"
|
||||
"github.com/actions/actions-runner-controller/github/actions/testserver"
|
||||
"github.com/actions/actions-runner-controller/controllers/actions.github.com/multiclient"
|
||||
fake "github.com/actions/actions-runner-controller/controllers/actions.github.com/multiclient/fake"
|
||||
"github.com/actions/actions-runner-controller/controllers/actions.github.com/secretresolver"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -57,10 +55,13 @@ var _ = Describe("Test EphemeralRunnerSet controller", func() {
|
||||
Scheme: mgr.GetScheme(),
|
||||
Log: logf.Log,
|
||||
ResourceBuilder: ResourceBuilder{
|
||||
SecretResolver: &SecretResolver{
|
||||
k8sClient: mgr.GetClient(),
|
||||
multiClient: fake.NewMultiClient(),
|
||||
},
|
||||
SecretResolver: secretresolver.New(mgr.GetClient(), fake.NewMultiClient(
|
||||
fake.WithClient(
|
||||
fake.NewClient(
|
||||
fake.WithRemoveRunner(nil),
|
||||
),
|
||||
),
|
||||
)),
|
||||
},
|
||||
}
|
||||
err := controller.SetupWithManager(mgr)
|
||||
@@ -75,7 +76,7 @@ var _ = Describe("Test EphemeralRunnerSet controller", func() {
|
||||
EphemeralRunnerSpec: v1alpha1.EphemeralRunnerSpec{
|
||||
GitHubConfigUrl: "https://github.com/owner/repo",
|
||||
GitHubConfigSecret: configSecret.Name,
|
||||
RunnerScaleSetId: 100,
|
||||
RunnerScaleSetID: 100,
|
||||
PodTemplateSpec: corev1.PodTemplateSpec{
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: []corev1.Container{
|
||||
@@ -1103,10 +1104,7 @@ var _ = Describe("Test EphemeralRunnerSet controller with proxy settings", func(
|
||||
Scheme: mgr.GetScheme(),
|
||||
Log: logf.Log,
|
||||
ResourceBuilder: ResourceBuilder{
|
||||
SecretResolver: &SecretResolver{
|
||||
k8sClient: mgr.GetClient(),
|
||||
multiClient: actions.NewMultiClient(logr.Discard()),
|
||||
},
|
||||
SecretResolver: secretresolver.New(mgr.GetClient(), multiclient.NewScaleset()),
|
||||
},
|
||||
}
|
||||
err := controller.SetupWithManager(mgr)
|
||||
@@ -1140,7 +1138,7 @@ var _ = Describe("Test EphemeralRunnerSet controller with proxy settings", func(
|
||||
EphemeralRunnerSpec: v1alpha1.EphemeralRunnerSpec{
|
||||
GitHubConfigUrl: "http://example.com/owner/repo",
|
||||
GitHubConfigSecret: configSecret.Name,
|
||||
RunnerScaleSetId: 100,
|
||||
RunnerScaleSetID: 100,
|
||||
Proxy: &v1alpha1.ProxyConfig{
|
||||
HTTP: &v1alpha1.ProxyServerConfig{
|
||||
Url: "http://proxy.example.com",
|
||||
@@ -1319,7 +1317,7 @@ var _ = Describe("Test EphemeralRunnerSet controller with proxy settings", func(
|
||||
EphemeralRunnerSpec: v1alpha1.EphemeralRunnerSpec{
|
||||
GitHubConfigUrl: "http://example.com/owner/repo",
|
||||
GitHubConfigSecret: configSecret.Name,
|
||||
RunnerScaleSetId: 100,
|
||||
RunnerScaleSetID: 100,
|
||||
Proxy: &v1alpha1.ProxyConfig{
|
||||
HTTP: &v1alpha1.ProxyServerConfig{
|
||||
Url: proxy.URL,
|
||||
@@ -1419,10 +1417,7 @@ var _ = Describe("Test EphemeralRunnerSet controller with custom root CA", func(
|
||||
Scheme: mgr.GetScheme(),
|
||||
Log: logf.Log,
|
||||
ResourceBuilder: ResourceBuilder{
|
||||
SecretResolver: &SecretResolver{
|
||||
k8sClient: mgr.GetClient(),
|
||||
multiClient: actions.NewMultiClient(logr.Discard()),
|
||||
},
|
||||
SecretResolver: secretresolver.New(mgr.GetClient(), multiclient.NewScaleset()),
|
||||
},
|
||||
}
|
||||
err = controller.SetupWithManager(mgr)
|
||||
@@ -1432,26 +1427,6 @@ var _ = Describe("Test EphemeralRunnerSet controller with custom root CA", func(
|
||||
})
|
||||
|
||||
It("should be able to make requests to a server using root CAs", func() {
|
||||
certsFolder := filepath.Join(
|
||||
"../../",
|
||||
"github",
|
||||
"actions",
|
||||
"testdata",
|
||||
)
|
||||
certPath := filepath.Join(certsFolder, "server.crt")
|
||||
keyPath := filepath.Join(certsFolder, "server.key")
|
||||
|
||||
serverSuccessfullyCalled := false
|
||||
server := testserver.NewUnstarted(GinkgoT(), http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
serverSuccessfullyCalled = true
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
cert, err := tls.LoadX509KeyPair(certPath, keyPath)
|
||||
Expect(err).NotTo(HaveOccurred(), "failed to load server cert")
|
||||
|
||||
server.TLS = &tls.Config{Certificates: []tls.Certificate{cert}}
|
||||
server.StartTLS()
|
||||
|
||||
ephemeralRunnerSet = &v1alpha1.EphemeralRunnerSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-asrs",
|
||||
@@ -1460,7 +1435,7 @@ var _ = Describe("Test EphemeralRunnerSet controller with custom root CA", func(
|
||||
Spec: v1alpha1.EphemeralRunnerSetSpec{
|
||||
Replicas: 1,
|
||||
EphemeralRunnerSpec: v1alpha1.EphemeralRunnerSpec{
|
||||
GitHubConfigUrl: server.ConfigURLForOrg("my-org"),
|
||||
GitHubConfigUrl: "https://github.example.com/api/v3",
|
||||
GitHubConfigSecret: configSecret.Name,
|
||||
GitHubServerTLS: &v1alpha1.TLSConfig{
|
||||
CertificateFrom: &v1alpha1.TLSCertificateSource{
|
||||
@@ -1472,7 +1447,7 @@ var _ = Describe("Test EphemeralRunnerSet controller with custom root CA", func(
|
||||
},
|
||||
},
|
||||
},
|
||||
RunnerScaleSetId: 100,
|
||||
RunnerScaleSetID: 100,
|
||||
PodTemplateSpec: corev1.PodTemplateSpec{
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: []corev1.Container{
|
||||
@@ -1487,7 +1462,7 @@ var _ = Describe("Test EphemeralRunnerSet controller with custom root CA", func(
|
||||
},
|
||||
}
|
||||
|
||||
err = k8sClient.Create(ctx, ephemeralRunnerSet)
|
||||
err := k8sClient.Create(ctx, ephemeralRunnerSet)
|
||||
Expect(err).NotTo(HaveOccurred(), "failed to create EphemeralRunnerSet")
|
||||
|
||||
runnerList := new(v1alpha1.EphemeralRunnerList)
|
||||
@@ -1503,32 +1478,10 @@ var _ = Describe("Test EphemeralRunnerSet controller with custom root CA", func(
|
||||
ephemeralRunnerSetTestInterval,
|
||||
).Should(BeEquivalentTo(1), "failed to create ephemeral runner")
|
||||
|
||||
// Verify that the TLS configuration is properly propagated to the runner
|
||||
runner := runnerList.Items[0].DeepCopy()
|
||||
Expect(runner.Spec.GitHubServerTLS).NotTo(BeNil(), "runner tls config should not be nil")
|
||||
Expect(runner.Spec.GitHubServerTLS).To(BeEquivalentTo(ephemeralRunnerSet.Spec.EphemeralRunnerSpec.GitHubServerTLS), "runner tls config should be correct")
|
||||
|
||||
runner.Status.Phase = corev1.PodRunning
|
||||
runner.Status.RunnerId = 100
|
||||
err = k8sClient.Status().Patch(ctx, runner, client.MergeFrom(&runnerList.Items[0]))
|
||||
Expect(err).NotTo(HaveOccurred(), "failed to update ephemeral runner status")
|
||||
|
||||
currentRunnerSet := new(v1alpha1.EphemeralRunnerSet)
|
||||
err = k8sClient.Get(ctx, client.ObjectKey{Namespace: ephemeralRunnerSet.Namespace, Name: ephemeralRunnerSet.Name}, currentRunnerSet)
|
||||
Expect(err).NotTo(HaveOccurred(), "failed to get EphemeralRunnerSet")
|
||||
|
||||
updatedRunnerSet := currentRunnerSet.DeepCopy()
|
||||
updatedRunnerSet.Spec.Replicas = 0
|
||||
err = k8sClient.Patch(ctx, updatedRunnerSet, client.MergeFrom(currentRunnerSet))
|
||||
Expect(err).NotTo(HaveOccurred(), "failed to update EphemeralRunnerSet")
|
||||
|
||||
// wait for server to be called
|
||||
Eventually(
|
||||
func() bool {
|
||||
return serverSuccessfullyCalled
|
||||
},
|
||||
autoscalingRunnerSetTestTimeout,
|
||||
1*time.Nanosecond,
|
||||
).Should(BeTrue(), "server was not called")
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
1831
controllers/actions.github.com/mocks_test.go
Normal file
1831
controllers/actions.github.com/mocks_test.go
Normal file
File diff suppressed because it is too large
Load Diff
230
controllers/actions.github.com/multiclient/fake/client.go
Normal file
230
controllers/actions.github.com/multiclient/fake/client.go
Normal file
@@ -0,0 +1,230 @@
|
||||
package fake
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/actions/actions-runner-controller/controllers/actions.github.com/multiclient"
|
||||
"github.com/actions/scaleset"
|
||||
)
|
||||
|
||||
// ClientOption is a functional option for configuring a fake Client
|
||||
type ClientOption func(*Client)
|
||||
|
||||
// WithGetRunnerScaleSet configures the result of GetRunnerScaleSet
|
||||
func WithGetRunnerScaleSet(result *scaleset.RunnerScaleSet, err error) ClientOption {
|
||||
return func(c *Client) {
|
||||
c.getRunnerScaleSetResult.RunnerScaleSet = result
|
||||
c.getRunnerScaleSetResult.err = err
|
||||
}
|
||||
}
|
||||
|
||||
// WithGetRunnerScaleSetByID configures the result of GetRunnerScaleSetByID
|
||||
func WithGetRunnerScaleSetByID(result *scaleset.RunnerScaleSet, err error) ClientOption {
|
||||
return func(c *Client) {
|
||||
c.getRunnerScaleSetByIDResult.RunnerScaleSet = result
|
||||
c.getRunnerScaleSetByIDResult.err = err
|
||||
}
|
||||
}
|
||||
|
||||
// WithGetRunnerGroupByName configures the result of GetRunnerGroupByName
|
||||
func WithGetRunnerGroupByName(result *scaleset.RunnerGroup, err error) ClientOption {
|
||||
return func(c *Client) {
|
||||
c.getRunnerGroupByNameResult.RunnerGroup = result
|
||||
c.getRunnerGroupByNameResult.err = err
|
||||
}
|
||||
}
|
||||
|
||||
// WithGetRunnerGroupByNameFunc configures a function to handle GetRunnerGroupByName calls dynamically
|
||||
func WithGetRunnerGroupByNameFunc(fn func(context.Context, string) (*scaleset.RunnerGroup, error)) ClientOption {
|
||||
return func(c *Client) {
|
||||
c.getRunnerGroupByNameFunc = fn
|
||||
}
|
||||
}
|
||||
|
||||
// WithCreateRunnerScaleSet configures the result of CreateRunnerScaleSet
|
||||
func WithCreateRunnerScaleSet(result *scaleset.RunnerScaleSet, err error) ClientOption {
|
||||
return func(c *Client) {
|
||||
c.createRunnerScaleSetResult.RunnerScaleSet = result
|
||||
c.createRunnerScaleSetResult.err = err
|
||||
}
|
||||
}
|
||||
|
||||
// WithUpdateRunnerScaleSet configures the result of UpdateRunnerScaleSet
|
||||
func WithUpdateRunnerScaleSet(result *scaleset.RunnerScaleSet, err error) ClientOption {
|
||||
return func(c *Client) {
|
||||
c.updateRunnerScaleSetResult.RunnerScaleSet = result
|
||||
c.updateRunnerScaleSetResult.err = err
|
||||
}
|
||||
}
|
||||
|
||||
// WithDeleteRunnerScaleSet configures the result of DeleteRunnerScaleSet
|
||||
func WithDeleteRunnerScaleSet(err error) ClientOption {
|
||||
return func(c *Client) {
|
||||
c.deleteRunnerScaleSetResult.err = err
|
||||
}
|
||||
}
|
||||
|
||||
// WithRemoveRunner configures the result of RemoveRunner
|
||||
func WithRemoveRunner(err error) ClientOption {
|
||||
return func(c *Client) {
|
||||
c.removeRunnerResult.err = err
|
||||
}
|
||||
}
|
||||
|
||||
// WithGenerateJitRunnerConfig configures the result of GenerateJitRunnerConfig
|
||||
func WithGenerateJitRunnerConfig(result *scaleset.RunnerScaleSetJitRunnerConfig, err error) ClientOption {
|
||||
return func(c *Client) {
|
||||
c.generateJitRunnerConfigResult.RunnerScaleSetJitRunnerConfig = result
|
||||
c.generateJitRunnerConfigResult.err = err
|
||||
}
|
||||
}
|
||||
|
||||
// WithGetRunnerByName configures the result of GetRunnerByName
|
||||
func WithGetRunnerByName(result *scaleset.RunnerReference, err error) ClientOption {
|
||||
return func(c *Client) {
|
||||
c.getRunnerByNameResult.RunnerReference = result
|
||||
c.getRunnerByNameResult.err = err
|
||||
}
|
||||
}
|
||||
|
||||
// WithGetRunner configures the result of GetRunner
|
||||
func WithGetRunner(result *scaleset.RunnerReference, err error) ClientOption {
|
||||
return func(c *Client) {
|
||||
c.getRunnerResult.RunnerReference = result
|
||||
c.getRunnerResult.err = err
|
||||
}
|
||||
}
|
||||
|
||||
// WithSystemInfo configures the SystemInfo
|
||||
func WithSystemInfo(info scaleset.SystemInfo) ClientOption {
|
||||
return func(c *Client) {
|
||||
c.systemInfo = info
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
c.updateRunnerScaleSetFunc = fn
|
||||
}
|
||||
}
|
||||
|
||||
// Client implements multiclient.Client interface for testing
|
||||
type Client struct {
|
||||
systemInfo scaleset.SystemInfo
|
||||
updateRunnerScaleSetFunc func(context.Context, int, *scaleset.RunnerScaleSet) (*scaleset.RunnerScaleSet, error)
|
||||
|
||||
getRunnerScaleSetResult struct {
|
||||
*scaleset.RunnerScaleSet
|
||||
err error
|
||||
}
|
||||
getRunnerScaleSetByIDResult struct {
|
||||
*scaleset.RunnerScaleSet
|
||||
err error
|
||||
}
|
||||
getRunnerGroupByNameResult struct {
|
||||
*scaleset.RunnerGroup
|
||||
err error
|
||||
}
|
||||
getRunnerGroupByNameFunc func(context.Context, string) (*scaleset.RunnerGroup, error)
|
||||
createRunnerScaleSetResult struct {
|
||||
*scaleset.RunnerScaleSet
|
||||
err error
|
||||
}
|
||||
updateRunnerScaleSetResult struct {
|
||||
*scaleset.RunnerScaleSet
|
||||
err error
|
||||
}
|
||||
deleteRunnerScaleSetResult struct {
|
||||
err error
|
||||
}
|
||||
removeRunnerResult struct {
|
||||
err error
|
||||
}
|
||||
generateJitRunnerConfigResult struct {
|
||||
*scaleset.RunnerScaleSetJitRunnerConfig
|
||||
err error
|
||||
}
|
||||
getRunnerByNameResult struct {
|
||||
*scaleset.RunnerReference
|
||||
err error
|
||||
}
|
||||
getRunnerResult struct {
|
||||
*scaleset.RunnerReference
|
||||
err error
|
||||
}
|
||||
messageSessionClientResult struct {
|
||||
*scaleset.MessageSessionClient
|
||||
err error
|
||||
}
|
||||
}
|
||||
|
||||
// Compile-time interface check
|
||||
var _ multiclient.Client = (*Client)(nil)
|
||||
|
||||
// NewClient creates a new fake Client with the given options
|
||||
func NewClient(opts ...ClientOption) *Client {
|
||||
c := &Client{}
|
||||
for _, opt := range opts {
|
||||
opt(c)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Client) SetSystemInfo(info scaleset.SystemInfo) {
|
||||
c.systemInfo = info
|
||||
}
|
||||
|
||||
func (c *Client) SystemInfo() scaleset.SystemInfo {
|
||||
return c.systemInfo
|
||||
}
|
||||
|
||||
func (c *Client) MessageSessionClient(ctx context.Context, runnerScaleSetID int, owner string, options ...scaleset.HTTPOption) (*scaleset.MessageSessionClient, error) {
|
||||
return c.messageSessionClientResult.MessageSessionClient, c.messageSessionClientResult.err
|
||||
}
|
||||
|
||||
func (c *Client) GenerateJitRunnerConfig(ctx context.Context, jitRunnerSetting *scaleset.RunnerScaleSetJitRunnerSetting, scaleSetID int) (*scaleset.RunnerScaleSetJitRunnerConfig, error) {
|
||||
return c.generateJitRunnerConfigResult.RunnerScaleSetJitRunnerConfig, c.generateJitRunnerConfigResult.err
|
||||
}
|
||||
|
||||
func (c *Client) GetRunner(ctx context.Context, runnerID int) (*scaleset.RunnerReference, error) {
|
||||
return c.getRunnerResult.RunnerReference, c.getRunnerResult.err
|
||||
}
|
||||
|
||||
func (c *Client) GetRunnerByName(ctx context.Context, runnerName string) (*scaleset.RunnerReference, error) {
|
||||
return c.getRunnerByNameResult.RunnerReference, c.getRunnerByNameResult.err
|
||||
}
|
||||
|
||||
func (c *Client) RemoveRunner(ctx context.Context, runnerID int64) error {
|
||||
return c.removeRunnerResult.err
|
||||
}
|
||||
|
||||
func (c *Client) GetRunnerGroupByName(ctx context.Context, runnerGroup string) (*scaleset.RunnerGroup, error) {
|
||||
if c.getRunnerGroupByNameFunc != nil {
|
||||
return c.getRunnerGroupByNameFunc(ctx, runnerGroup)
|
||||
}
|
||||
return c.getRunnerGroupByNameResult.RunnerGroup, c.getRunnerGroupByNameResult.err
|
||||
}
|
||||
|
||||
func (c *Client) GetRunnerScaleSet(ctx context.Context, runnerGroupID int, runnerScaleSetName string) (*scaleset.RunnerScaleSet, error) {
|
||||
return c.getRunnerScaleSetResult.RunnerScaleSet, c.getRunnerScaleSetResult.err
|
||||
}
|
||||
|
||||
func (c *Client) GetRunnerScaleSetByID(ctx context.Context, runnerScaleSetID int) (*scaleset.RunnerScaleSet, error) {
|
||||
return c.getRunnerScaleSetByIDResult.RunnerScaleSet, c.getRunnerScaleSetByIDResult.err
|
||||
}
|
||||
|
||||
func (c *Client) CreateRunnerScaleSet(ctx context.Context, runnerScaleSet *scaleset.RunnerScaleSet) (*scaleset.RunnerScaleSet, error) {
|
||||
return c.createRunnerScaleSetResult.RunnerScaleSet, c.createRunnerScaleSetResult.err
|
||||
}
|
||||
|
||||
func (c *Client) UpdateRunnerScaleSet(ctx context.Context, runnerScaleSetID int, runnerScaleSet *scaleset.RunnerScaleSet) (*scaleset.RunnerScaleSet, error) {
|
||||
if c.updateRunnerScaleSetFunc != nil {
|
||||
return c.updateRunnerScaleSetFunc(ctx, runnerScaleSetID, runnerScaleSet)
|
||||
}
|
||||
return c.updateRunnerScaleSetResult.RunnerScaleSet, c.updateRunnerScaleSetResult.err
|
||||
}
|
||||
|
||||
func (c *Client) DeleteRunnerScaleSet(ctx context.Context, runnerScaleSetID int) error {
|
||||
return c.deleteRunnerScaleSetResult.err
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package fake
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/actions/actions-runner-controller/controllers/actions.github.com/multiclient"
|
||||
)
|
||||
|
||||
// MultiClientOption is a functional option for configuring a fake MultiClient
|
||||
type MultiClientOption func(*MultiClient)
|
||||
|
||||
// WithClient configures the client that GetClientFor will return
|
||||
func WithClient(c multiclient.Client) MultiClientOption {
|
||||
return func(mc *MultiClient) {
|
||||
mc.client = c
|
||||
}
|
||||
}
|
||||
|
||||
// WithGetClientForError configures an error that GetClientFor will return
|
||||
func WithGetClientForError(err error) MultiClientOption {
|
||||
return func(mc *MultiClient) {
|
||||
mc.getClientForErr = err
|
||||
}
|
||||
}
|
||||
|
||||
// MultiClient implements multiclient.MultiClient interface for testing
|
||||
type MultiClient struct {
|
||||
client multiclient.Client
|
||||
getClientForErr error
|
||||
}
|
||||
|
||||
// Compile-time interface check
|
||||
var _ multiclient.MultiClient = (*MultiClient)(nil)
|
||||
|
||||
// NewMultiClient creates a new fake MultiClient with the given options
|
||||
func NewMultiClient(opts ...MultiClientOption) *MultiClient {
|
||||
mc := &MultiClient{}
|
||||
for _, opt := range opts {
|
||||
opt(mc)
|
||||
}
|
||||
// Default behavior: if no client configured, return a default NewClient()
|
||||
if mc.client == nil {
|
||||
mc.client = NewClient()
|
||||
}
|
||||
return mc
|
||||
}
|
||||
|
||||
func (mc *MultiClient) GetClientFor(ctx context.Context, opts *multiclient.ClientForOptions) (multiclient.Client, error) {
|
||||
if mc.getClientForErr != nil {
|
||||
return nil, mc.getClientForErr
|
||||
}
|
||||
return mc.client, nil
|
||||
}
|
||||
177
controllers/actions.github.com/multiclient/multi_client.go
Normal file
177
controllers/actions.github.com/multiclient/multi_client.go
Normal file
@@ -0,0 +1,177 @@
|
||||
package multiclient
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1/appconfig"
|
||||
"github.com/actions/actions-runner-controller/build"
|
||||
"github.com/actions/actions-runner-controller/github/actions"
|
||||
"github.com/actions/scaleset"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type MultiClient interface {
|
||||
GetClientFor(ctx context.Context, opts *ClientForOptions) (Client, error)
|
||||
}
|
||||
|
||||
type Scaleset struct {
|
||||
mu sync.Mutex
|
||||
clients map[string]*multiClientEntry
|
||||
}
|
||||
|
||||
type multiClientEntry struct {
|
||||
client *scaleset.Client
|
||||
rootCAs *x509.CertPool
|
||||
}
|
||||
|
||||
func NewScaleset() *Scaleset {
|
||||
return &Scaleset{
|
||||
clients: make(map[string]*multiClientEntry),
|
||||
}
|
||||
}
|
||||
|
||||
type Client interface {
|
||||
SetSystemInfo(info scaleset.SystemInfo)
|
||||
SystemInfo() scaleset.SystemInfo
|
||||
|
||||
MessageSessionClient(ctx context.Context, runnerScaleSetID int, owner string, options ...scaleset.HTTPOption) (*scaleset.MessageSessionClient, error)
|
||||
|
||||
GenerateJitRunnerConfig(ctx context.Context, jitRunnerSetting *scaleset.RunnerScaleSetJitRunnerSetting, scaleSetID int) (*scaleset.RunnerScaleSetJitRunnerConfig, error)
|
||||
|
||||
GetRunner(ctx context.Context, runnerID int) (*scaleset.RunnerReference, error)
|
||||
GetRunnerByName(ctx context.Context, runnerName string) (*scaleset.RunnerReference, error)
|
||||
RemoveRunner(ctx context.Context, runnerID int64) error
|
||||
|
||||
GetRunnerGroupByName(ctx context.Context, runnerGroup string) (*scaleset.RunnerGroup, error)
|
||||
|
||||
GetRunnerScaleSet(ctx context.Context, runnerGroupID int, runnerScaleSetName string) (*scaleset.RunnerScaleSet, error)
|
||||
GetRunnerScaleSetByID(ctx context.Context, runnerScaleSetID int) (*scaleset.RunnerScaleSet, error)
|
||||
|
||||
CreateRunnerScaleSet(ctx context.Context, runnerScaleSet *scaleset.RunnerScaleSet) (*scaleset.RunnerScaleSet, error)
|
||||
UpdateRunnerScaleSet(ctx context.Context, runnerScaleSetID int, runnerScaleSet *scaleset.RunnerScaleSet) (*scaleset.RunnerScaleSet, error)
|
||||
DeleteRunnerScaleSet(ctx context.Context, runnerScaleSetID int) error
|
||||
}
|
||||
|
||||
func (m *Scaleset) GetClientFor(ctx context.Context, opts *ClientForOptions) (Client, error) {
|
||||
identifier, err := opts.identifier()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate client identifier: %w", err)
|
||||
}
|
||||
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
entry, ok := m.clients[identifier]
|
||||
if ok && entry.rootCAs.Equal(opts.RootCAs) {
|
||||
return entry.client, nil
|
||||
}
|
||||
|
||||
client, err := opts.newClient()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create new client: %w", err)
|
||||
}
|
||||
|
||||
m.clients[identifier] = &multiClientEntry{
|
||||
client: client,
|
||||
rootCAs: opts.RootCAs,
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
type ClientForOptions struct {
|
||||
GithubConfigURL string
|
||||
AppConfig appconfig.AppConfig
|
||||
Namespace string
|
||||
RootCAs *x509.CertPool
|
||||
ProxyFunc func(*http.Request) (*url.URL, error)
|
||||
}
|
||||
|
||||
func (o *ClientForOptions) identifier() (string, error) {
|
||||
if err := o.AppConfig.Validate(); err != nil {
|
||||
return "", fmt.Errorf("failed to validate app config: %w", err)
|
||||
}
|
||||
if _, err := actions.ParseGitHubConfigFromURL(o.GithubConfigURL); err != nil {
|
||||
return "", fmt.Errorf("failed to parse GitHub config URL: %w", err)
|
||||
}
|
||||
if o.Namespace == "" {
|
||||
return "", fmt.Errorf("namespace is required to generate client identifier")
|
||||
}
|
||||
identifier := fmt.Sprintf("configURL:%q,namespace:%q,proxy:%t", o.GithubConfigURL, o.Namespace, o.ProxyFunc != nil)
|
||||
|
||||
if o.AppConfig.Token != "" {
|
||||
identifier += fmt.Sprintf(",token:%q,", o.AppConfig.Token)
|
||||
} else {
|
||||
identifier += fmt.Sprintf(
|
||||
",appID:%q,installationID:%q,key:%q",
|
||||
o.AppConfig.AppID,
|
||||
strconv.FormatInt(o.AppConfig.AppInstallationID, 10),
|
||||
o.AppConfig.AppPrivateKey,
|
||||
)
|
||||
}
|
||||
|
||||
if o.RootCAs != nil {
|
||||
// ignoring because this cert pool is intended not to come from SystemCertPool
|
||||
// nolint:staticcheck
|
||||
identifier += fmt.Sprintf(",rootCAs:%q", o.RootCAs.Subjects())
|
||||
}
|
||||
|
||||
return uuid.NewHash(sha256.New(), uuid.NameSpaceOID, []byte(identifier), 6).String(), nil
|
||||
}
|
||||
|
||||
func (o *ClientForOptions) newClient() (*scaleset.Client, error) {
|
||||
systemInfo := scaleset.SystemInfo{
|
||||
System: "actions-runner-controller",
|
||||
Version: build.Version,
|
||||
CommitSHA: build.CommitSHA,
|
||||
ScaleSetID: 0, // by default, scale set is 0 (not created yet)
|
||||
Subsystem: "gha-scale-set-controller",
|
||||
}
|
||||
|
||||
var options []scaleset.HTTPOption
|
||||
if o.RootCAs != nil {
|
||||
options = append(options, scaleset.WithRootCAs(o.RootCAs))
|
||||
}
|
||||
if o.ProxyFunc != nil {
|
||||
options = append(options, scaleset.WithProxy(o.ProxyFunc))
|
||||
}
|
||||
|
||||
if o.AppConfig.Token != "" {
|
||||
c, err := scaleset.NewClientWithPersonalAccessToken(
|
||||
scaleset.NewClientWithPersonalAccessTokenConfig{
|
||||
GitHubConfigURL: o.GithubConfigURL,
|
||||
PersonalAccessToken: o.AppConfig.Token,
|
||||
SystemInfo: systemInfo,
|
||||
},
|
||||
options...,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to instantiate client with personal access token auth: %w", err)
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
c, err := scaleset.NewClientWithGitHubApp(
|
||||
scaleset.ClientWithGitHubAppConfig{
|
||||
GitHubConfigURL: o.GithubConfigURL,
|
||||
GitHubAppAuth: scaleset.GitHubAppAuth{
|
||||
ClientID: o.AppConfig.AppID,
|
||||
InstallationID: o.AppConfig.AppInstallationID,
|
||||
PrivateKey: o.AppConfig.AppPrivateKey,
|
||||
},
|
||||
SystemInfo: systemInfo,
|
||||
},
|
||||
options...,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to instantiate client with GitHub App auth: %w", err)
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
16
controllers/actions.github.com/object/object.go
Normal file
16
controllers/actions.github.com/object/object.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package object
|
||||
|
||||
import (
|
||||
"github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
type ActionsGitHubObject interface {
|
||||
client.Object
|
||||
GitHubConfigUrl() string
|
||||
GitHubConfigSecret() string
|
||||
GitHubProxy() *v1alpha1.ProxyConfig
|
||||
GitHubServerTLS() *v1alpha1.TLSConfig
|
||||
VaultConfig() *v1alpha1.VaultConfig
|
||||
VaultProxy() *v1alpha1.ProxyConfig
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package actionsgithubcom
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"maps"
|
||||
@@ -14,10 +15,13 @@ import (
|
||||
"github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1/appconfig"
|
||||
"github.com/actions/actions-runner-controller/build"
|
||||
ghalistenerconfig "github.com/actions/actions-runner-controller/cmd/ghalistener/config"
|
||||
"github.com/actions/actions-runner-controller/controllers/actions.github.com/multiclient"
|
||||
"github.com/actions/actions-runner-controller/controllers/actions.github.com/object"
|
||||
"github.com/actions/actions-runner-controller/github/actions"
|
||||
"github.com/actions/actions-runner-controller/hash"
|
||||
"github.com/actions/actions-runner-controller/logging"
|
||||
"github.com/actions/actions-runner-controller/vault/azurekeyvault"
|
||||
"github.com/actions/scaleset"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -71,14 +75,14 @@ func SetListenerEntrypoint(entrypoint string) {
|
||||
}
|
||||
}
|
||||
|
||||
type ResourceBuilder struct {
|
||||
ExcludeLabelPropagationPrefixes []string
|
||||
*SecretResolver
|
||||
type SecretResolver interface {
|
||||
GetAppConfig(ctx context.Context, obj object.ActionsGitHubObject) (*appconfig.AppConfig, error)
|
||||
GetActionsService(ctx context.Context, obj object.ActionsGitHubObject) (multiclient.Client, error)
|
||||
}
|
||||
|
||||
// boolPtr returns a pointer to a bool value
|
||||
func boolPtr(v bool) *bool {
|
||||
return &v
|
||||
type ResourceBuilder struct {
|
||||
ExcludeLabelPropagationPrefixes []string
|
||||
SecretResolver
|
||||
}
|
||||
|
||||
func (b *ResourceBuilder) newAutoScalingListener(autoscalingRunnerSet *v1alpha1.AutoscalingRunnerSet, ephemeralRunnerSet *v1alpha1.EphemeralRunnerSet, namespace, image string, imagePullSecrets []corev1.LocalObjectReference) (*v1alpha1.AutoscalingListener, error) {
|
||||
@@ -96,7 +100,7 @@ func (b *ResourceBuilder) newAutoScalingListener(autoscalingRunnerSet *v1alpha1.
|
||||
effectiveMinRunners = *autoscalingRunnerSet.Spec.MinRunners
|
||||
}
|
||||
|
||||
labels := b.mergeLabels(autoscalingRunnerSet.Labels, map[string]string{
|
||||
labels := b.filterAndMergeLabels(autoscalingRunnerSet.Labels, map[string]string{
|
||||
LabelKeyGitHubScaleSetNamespace: autoscalingRunnerSet.Namespace,
|
||||
LabelKeyGitHubScaleSetName: autoscalingRunnerSet.Name,
|
||||
LabelKeyKubernetesPartOf: labelValueKubernetesPartOf,
|
||||
@@ -104,13 +108,18 @@ func (b *ResourceBuilder) newAutoScalingListener(autoscalingRunnerSet *v1alpha1.
|
||||
LabelKeyKubernetesVersion: autoscalingRunnerSet.Labels[LabelKeyKubernetesVersion],
|
||||
})
|
||||
|
||||
if err := applyGitHubURLLabels(autoscalingRunnerSet.Spec.GitHubConfigUrl, labels); err != nil {
|
||||
return nil, fmt.Errorf("failed to apply GitHub URL labels: %v", err)
|
||||
}
|
||||
|
||||
annotations := map[string]string{
|
||||
annotationKeyRunnerSpecHash: autoscalingRunnerSet.ListenerSpecHash(),
|
||||
annotationKeyValuesHash: autoscalingRunnerSet.Annotations[annotationKeyValuesHash],
|
||||
}
|
||||
|
||||
if err := applyGitHubURLLabels(autoscalingRunnerSet.Spec.GitHubConfigUrl, labels); err != nil {
|
||||
return nil, fmt.Errorf("failed to apply GitHub URL labels: %v", err)
|
||||
if autoscalingRunnerSet.Spec.AutoscalingListenerMetadata != nil {
|
||||
labels = b.filterAndMergeLabels(autoscalingRunnerSet.Spec.AutoscalingListenerMetadata.Labels, labels)
|
||||
annotations = b.mergeAnnotations(autoscalingRunnerSet.Spec.AutoscalingListenerMetadata.Annotations, annotations)
|
||||
}
|
||||
|
||||
autoscalingListener := &v1alpha1.AutoscalingListener{
|
||||
@@ -136,6 +145,10 @@ func (b *ResourceBuilder) newAutoScalingListener(autoscalingRunnerSet *v1alpha1.
|
||||
GitHubServerTLS: autoscalingRunnerSet.Spec.GitHubServerTLS,
|
||||
Metrics: autoscalingRunnerSet.Spec.ListenerMetrics,
|
||||
Template: autoscalingRunnerSet.Spec.ListenerTemplate,
|
||||
ServiceAccountMetadata: autoscalingRunnerSet.Spec.ListenerServiceAccountMetadata,
|
||||
RoleMetadata: autoscalingRunnerSet.Spec.ListenerRoleMetadata,
|
||||
RoleBindingMetadata: autoscalingRunnerSet.Spec.ListenerRoleBindingMetadata,
|
||||
ConfigSecretMetadata: autoscalingRunnerSet.Spec.ListenerConfigSecretMetadata,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -174,7 +187,7 @@ func (b *ResourceBuilder) newScaleSetListenerConfig(autoscalingListener *v1alpha
|
||||
}
|
||||
|
||||
config := ghalistenerconfig.Config{
|
||||
ConfigureUrl: autoscalingListener.Spec.GitHubConfigUrl,
|
||||
ConfigureURL: autoscalingListener.Spec.GitHubConfigUrl,
|
||||
EphemeralRunnerSetNamespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace,
|
||||
EphemeralRunnerSetName: autoscalingListener.Spec.EphemeralRunnerSetName,
|
||||
MaxRunners: autoscalingListener.Spec.MaxRunners,
|
||||
@@ -212,10 +225,22 @@ func (b *ResourceBuilder) newScaleSetListenerConfig(autoscalingListener *v1alpha
|
||||
return nil, fmt.Errorf("failed to encode config: %w", err)
|
||||
}
|
||||
|
||||
var labels map[string]string
|
||||
if autoscalingListener.Spec.ConfigSecretMetadata != nil && len(autoscalingListener.Spec.ConfigSecretMetadata.Labels) > 0 {
|
||||
labels = b.filterAndMergeLabels(autoscalingListener.Spec.ConfigSecretMetadata.Labels, nil)
|
||||
}
|
||||
|
||||
var annotations map[string]string
|
||||
if autoscalingListener.Spec.ConfigSecretMetadata != nil && len(autoscalingListener.Spec.ConfigSecretMetadata.Annotations) > 0 {
|
||||
annotations = autoscalingListener.Spec.ConfigSecretMetadata.Annotations
|
||||
}
|
||||
|
||||
return &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: scaleSetListenerConfigName(autoscalingListener),
|
||||
Namespace: autoscalingListener.Namespace,
|
||||
Name: scaleSetListenerConfigName(autoscalingListener),
|
||||
Namespace: autoscalingListener.Namespace,
|
||||
Labels: labels,
|
||||
Annotations: annotations,
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"config.json": buf.Bytes(),
|
||||
@@ -298,8 +323,8 @@ func (b *ResourceBuilder) newScaleSetListenerPod(autoscalingListener *v1alpha1.A
|
||||
Kind: autoscalingListener.GetObjectKind().GroupVersionKind().Kind,
|
||||
UID: autoscalingListener.GetUID(),
|
||||
Name: autoscalingListener.GetName(),
|
||||
Controller: boolPtr(true),
|
||||
BlockOwnerDeletion: boolPtr(true),
|
||||
Controller: new(true),
|
||||
BlockOwnerDeletion: new(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -431,32 +456,49 @@ func mergeListenerContainer(base, from *corev1.Container) {
|
||||
}
|
||||
|
||||
func (b *ResourceBuilder) newScaleSetListenerServiceAccount(autoscalingListener *v1alpha1.AutoscalingListener) *corev1.ServiceAccount {
|
||||
return &corev1.ServiceAccount{
|
||||
base := &corev1.ServiceAccount{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: autoscalingListener.Name,
|
||||
Namespace: autoscalingListener.Namespace,
|
||||
Labels: b.mergeLabels(autoscalingListener.Labels, map[string]string{
|
||||
Labels: b.filterAndMergeLabels(autoscalingListener.Labels, map[string]string{
|
||||
LabelKeyGitHubScaleSetNamespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace,
|
||||
LabelKeyGitHubScaleSetName: autoscalingListener.Spec.AutoscalingRunnerSetName,
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
if autoscalingListener.Spec.ServiceAccountMetadata != nil {
|
||||
base.Labels = b.filterAndMergeLabels(autoscalingListener.Spec.ServiceAccountMetadata.Labels, base.Labels)
|
||||
base.Annotations = b.mergeAnnotations(autoscalingListener.Spec.ServiceAccountMetadata.Annotations, base.Annotations)
|
||||
}
|
||||
|
||||
return base
|
||||
}
|
||||
|
||||
func (b *ResourceBuilder) newScaleSetListenerRole(autoscalingListener *v1alpha1.AutoscalingListener) *rbacv1.Role {
|
||||
rules := rulesForListenerRole([]string{autoscalingListener.Spec.EphemeralRunnerSetName})
|
||||
rulesHash := hash.ComputeTemplateHash(&rules)
|
||||
|
||||
labels := b.filterAndMergeLabels(autoscalingListener.Labels, map[string]string{
|
||||
LabelKeyGitHubScaleSetNamespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace,
|
||||
LabelKeyGitHubScaleSetName: autoscalingListener.Spec.AutoscalingRunnerSetName,
|
||||
labelKeyListenerNamespace: autoscalingListener.Namespace,
|
||||
labelKeyListenerName: autoscalingListener.Name,
|
||||
"role-policy-rules-hash": rulesHash,
|
||||
})
|
||||
|
||||
var annotations map[string]string
|
||||
if autoscalingListener.Spec.RoleMetadata != nil {
|
||||
labels = b.filterAndMergeLabels(autoscalingListener.Spec.RoleMetadata.Labels, labels)
|
||||
annotations = b.mergeAnnotations(autoscalingListener.Spec.RoleMetadata.Annotations, nil)
|
||||
}
|
||||
|
||||
newRole := &rbacv1.Role{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: autoscalingListener.Name,
|
||||
Namespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace,
|
||||
Labels: b.mergeLabels(autoscalingListener.Labels, map[string]string{
|
||||
LabelKeyGitHubScaleSetNamespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace,
|
||||
LabelKeyGitHubScaleSetName: autoscalingListener.Spec.AutoscalingRunnerSetName,
|
||||
labelKeyListenerNamespace: autoscalingListener.Namespace,
|
||||
labelKeyListenerName: autoscalingListener.Name,
|
||||
"role-policy-rules-hash": rulesHash,
|
||||
}),
|
||||
Name: autoscalingListener.Name,
|
||||
Namespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace,
|
||||
Labels: labels,
|
||||
Annotations: annotations,
|
||||
},
|
||||
Rules: rules,
|
||||
}
|
||||
@@ -480,18 +522,28 @@ func (b *ResourceBuilder) newScaleSetListenerRoleBinding(autoscalingListener *v1
|
||||
}
|
||||
subjectHash := hash.ComputeTemplateHash(&subjects)
|
||||
|
||||
labels := b.filterAndMergeLabels(autoscalingListener.Labels, map[string]string{
|
||||
LabelKeyGitHubScaleSetNamespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace,
|
||||
LabelKeyGitHubScaleSetName: autoscalingListener.Spec.AutoscalingRunnerSetName,
|
||||
labelKeyListenerNamespace: autoscalingListener.Namespace,
|
||||
labelKeyListenerName: autoscalingListener.Name,
|
||||
"role-binding-role-ref-hash": roleRefHash,
|
||||
"role-binding-subject-hash": subjectHash,
|
||||
})
|
||||
|
||||
var annotations map[string]string
|
||||
|
||||
if autoscalingListener.Spec.RoleBindingMetadata != nil {
|
||||
labels = b.filterAndMergeLabels(autoscalingListener.Spec.RoleBindingMetadata.Labels, labels)
|
||||
annotations = autoscalingListener.Spec.RoleBindingMetadata.Annotations
|
||||
}
|
||||
|
||||
newRoleBinding := &rbacv1.RoleBinding{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: autoscalingListener.Name,
|
||||
Namespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace,
|
||||
Labels: b.mergeLabels(autoscalingListener.Labels, map[string]string{
|
||||
LabelKeyGitHubScaleSetNamespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace,
|
||||
LabelKeyGitHubScaleSetName: autoscalingListener.Spec.AutoscalingRunnerSetName,
|
||||
labelKeyListenerNamespace: autoscalingListener.Namespace,
|
||||
labelKeyListenerName: autoscalingListener.Name,
|
||||
"role-binding-role-ref-hash": roleRefHash,
|
||||
"role-binding-subject-hash": subjectHash,
|
||||
}),
|
||||
Name: autoscalingListener.Name,
|
||||
Namespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace,
|
||||
Labels: labels,
|
||||
Annotations: annotations,
|
||||
},
|
||||
RoleRef: roleRef,
|
||||
Subjects: subjects,
|
||||
@@ -507,7 +559,7 @@ func (b *ResourceBuilder) newEphemeralRunnerSet(autoscalingRunnerSet *v1alpha1.A
|
||||
}
|
||||
runnerSpecHash := autoscalingRunnerSet.RunnerSetSpecHash()
|
||||
|
||||
labels := b.mergeLabels(autoscalingRunnerSet.Labels, map[string]string{
|
||||
labels := b.filterAndMergeLabels(autoscalingRunnerSet.Labels, map[string]string{
|
||||
LabelKeyKubernetesPartOf: labelValueKubernetesPartOf,
|
||||
LabelKeyKubernetesComponent: "runner-set",
|
||||
LabelKeyKubernetesVersion: autoscalingRunnerSet.Labels[LabelKeyKubernetesVersion],
|
||||
@@ -519,41 +571,48 @@ func (b *ResourceBuilder) newEphemeralRunnerSet(autoscalingRunnerSet *v1alpha1.A
|
||||
return nil, fmt.Errorf("failed to apply GitHub URL labels: %v", err)
|
||||
}
|
||||
|
||||
newAnnotations := map[string]string{
|
||||
annotations := map[string]string{
|
||||
AnnotationKeyGitHubRunnerGroupName: autoscalingRunnerSet.Annotations[AnnotationKeyGitHubRunnerGroupName],
|
||||
AnnotationKeyGitHubRunnerScaleSetName: autoscalingRunnerSet.Annotations[AnnotationKeyGitHubRunnerScaleSetName],
|
||||
annotationKeyRunnerSpecHash: runnerSpecHash,
|
||||
}
|
||||
|
||||
if autoscalingRunnerSet.Spec.EphemeralRunnerSetMetadata != nil {
|
||||
labels = b.filterAndMergeLabels(autoscalingRunnerSet.Spec.EphemeralRunnerSetMetadata.Labels, labels)
|
||||
annotations = b.mergeAnnotations(autoscalingRunnerSet.Spec.EphemeralRunnerSetMetadata.Annotations, annotations)
|
||||
}
|
||||
|
||||
newEphemeralRunnerSet := &v1alpha1.EphemeralRunnerSet{
|
||||
TypeMeta: metav1.TypeMeta{},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
GenerateName: autoscalingRunnerSet.Name + "-",
|
||||
Namespace: autoscalingRunnerSet.Namespace,
|
||||
Labels: labels,
|
||||
Annotations: newAnnotations,
|
||||
Annotations: annotations,
|
||||
OwnerReferences: []metav1.OwnerReference{
|
||||
{
|
||||
APIVersion: autoscalingRunnerSet.GetObjectKind().GroupVersionKind().GroupVersion().String(),
|
||||
Kind: autoscalingRunnerSet.GetObjectKind().GroupVersionKind().Kind,
|
||||
UID: autoscalingRunnerSet.GetUID(),
|
||||
Name: autoscalingRunnerSet.GetName(),
|
||||
Controller: boolPtr(true),
|
||||
BlockOwnerDeletion: boolPtr(true),
|
||||
Controller: new(true),
|
||||
BlockOwnerDeletion: new(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
Spec: v1alpha1.EphemeralRunnerSetSpec{
|
||||
Replicas: 0,
|
||||
EphemeralRunnerSpec: v1alpha1.EphemeralRunnerSpec{
|
||||
RunnerScaleSetId: runnerScaleSetID,
|
||||
GitHubConfigUrl: autoscalingRunnerSet.Spec.GitHubConfigUrl,
|
||||
GitHubConfigSecret: autoscalingRunnerSet.Spec.GitHubConfigSecret,
|
||||
Proxy: autoscalingRunnerSet.Spec.Proxy,
|
||||
GitHubServerTLS: autoscalingRunnerSet.Spec.GitHubServerTLS,
|
||||
PodTemplateSpec: autoscalingRunnerSet.Spec.Template,
|
||||
VaultConfig: autoscalingRunnerSet.VaultConfig(),
|
||||
RunnerScaleSetID: runnerScaleSetID,
|
||||
GitHubConfigUrl: autoscalingRunnerSet.Spec.GitHubConfigUrl,
|
||||
GitHubConfigSecret: autoscalingRunnerSet.Spec.GitHubConfigSecret,
|
||||
Proxy: autoscalingRunnerSet.Spec.Proxy,
|
||||
GitHubServerTLS: autoscalingRunnerSet.Spec.GitHubServerTLS,
|
||||
PodTemplateSpec: autoscalingRunnerSet.Spec.Template,
|
||||
VaultConfig: autoscalingRunnerSet.VaultConfig(),
|
||||
EphemeralRunnerConfigSecretMetadata: autoscalingRunnerSet.Spec.EphemeralRunnerConfigSecretMetadata,
|
||||
},
|
||||
EphemeralRunnerMetadata: autoscalingRunnerSet.Spec.EphemeralRunnerMetadata,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -568,6 +627,12 @@ func (b *ResourceBuilder) newEphemeralRunner(ephemeralRunnerSet *v1alpha1.Epheme
|
||||
annotations := make(map[string]string, len(ephemeralRunnerSet.Annotations)+1)
|
||||
maps.Copy(annotations, ephemeralRunnerSet.Annotations)
|
||||
annotations[AnnotationKeyPatchID] = strconv.Itoa(ephemeralRunnerSet.Spec.PatchID)
|
||||
|
||||
if ephemeralRunnerSet.Spec.EphemeralRunnerMetadata != nil {
|
||||
labels = b.filterAndMergeLabels(ephemeralRunnerSet.Spec.EphemeralRunnerMetadata.Labels, labels)
|
||||
annotations = b.mergeAnnotations(ephemeralRunnerSet.Spec.EphemeralRunnerMetadata.Annotations, annotations)
|
||||
}
|
||||
|
||||
return &v1alpha1.EphemeralRunner{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
GenerateName: ephemeralRunnerSet.Name + "-runner-",
|
||||
@@ -584,8 +649,8 @@ func (b *ResourceBuilder) newEphemeralRunner(ephemeralRunnerSet *v1alpha1.Epheme
|
||||
Kind: ephemeralRunnerSet.GetObjectKind().GroupVersionKind().Kind,
|
||||
UID: ephemeralRunnerSet.GetUID(),
|
||||
Name: ephemeralRunnerSet.GetName(),
|
||||
Controller: boolPtr(true),
|
||||
BlockOwnerDeletion: boolPtr(true),
|
||||
Controller: new(true),
|
||||
BlockOwnerDeletion: new(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -622,8 +687,8 @@ func (b *ResourceBuilder) newEphemeralRunnerPod(runner *v1alpha1.EphemeralRunner
|
||||
Kind: runner.GetObjectKind().GroupVersionKind().Kind,
|
||||
UID: runner.GetUID(),
|
||||
Name: runner.GetName(),
|
||||
Controller: boolPtr(true),
|
||||
BlockOwnerDeletion: boolPtr(true),
|
||||
Controller: new(true),
|
||||
BlockOwnerDeletion: new(true),
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -661,17 +726,29 @@ func (b *ResourceBuilder) newEphemeralRunnerPod(runner *v1alpha1.EphemeralRunner
|
||||
return &newPod
|
||||
}
|
||||
|
||||
func (b *ResourceBuilder) newEphemeralRunnerJitSecret(ephemeralRunner *v1alpha1.EphemeralRunner, jitConfig *actions.RunnerScaleSetJitRunnerConfig) *corev1.Secret {
|
||||
func (b *ResourceBuilder) newEphemeralRunnerJitSecret(ephemeralRunner *v1alpha1.EphemeralRunner, jitConfig *scaleset.RunnerScaleSetJitRunnerConfig) *corev1.Secret {
|
||||
var (
|
||||
labels map[string]string
|
||||
annotations map[string]string
|
||||
)
|
||||
|
||||
if ephemeralRunner.Spec.EphemeralRunnerConfigSecretMetadata != nil {
|
||||
labels = b.filterAndMergeLabels(ephemeralRunner.Spec.EphemeralRunnerConfigSecretMetadata.Labels, nil)
|
||||
annotations = ephemeralRunner.Spec.EphemeralRunnerConfigSecretMetadata.Annotations
|
||||
}
|
||||
|
||||
return &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: ephemeralRunner.Name,
|
||||
Namespace: ephemeralRunner.Namespace,
|
||||
Name: ephemeralRunner.Name,
|
||||
Namespace: ephemeralRunner.Namespace,
|
||||
Labels: labels,
|
||||
Annotations: annotations,
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
jitTokenKey: []byte(jitConfig.EncodedJITConfig),
|
||||
"runnerName": []byte(jitConfig.Runner.Name),
|
||||
"runnerId": []byte(strconv.Itoa(jitConfig.Runner.Id)),
|
||||
"scaleSetId": []byte(strconv.Itoa(jitConfig.Runner.RunnerScaleSetId)),
|
||||
"runnerId": []byte(strconv.Itoa(jitConfig.Runner.ID)),
|
||||
"scaleSetId": []byte(strconv.Itoa(jitConfig.Runner.RunnerScaleSetID)),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -756,9 +833,12 @@ func trimLabelValue(val string) string {
|
||||
return strings.Trim(val, "-_.")
|
||||
}
|
||||
|
||||
func (b *ResourceBuilder) mergeLabels(base, overwrite map[string]string) map[string]string {
|
||||
mergedLabels := make(map[string]string, len(base))
|
||||
func (b *ResourceBuilder) filterAndMergeLabels(base, overwrite map[string]string) map[string]string {
|
||||
if base == nil && overwrite == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
mergedLabels := make(map[string]string, len(base))
|
||||
base:
|
||||
for k, v := range base {
|
||||
for _, prefix := range b.ExcludeLabelPropagationPrefixes {
|
||||
@@ -781,3 +861,12 @@ overwrite:
|
||||
|
||||
return mergedLabels
|
||||
}
|
||||
|
||||
func (b *ResourceBuilder) mergeAnnotations(base, overwrite map[string]string) map[string]string {
|
||||
if base == nil && overwrite == nil {
|
||||
return nil
|
||||
}
|
||||
base = maps.Clone(base)
|
||||
maps.Copy(base, overwrite)
|
||||
return base
|
||||
}
|
||||
|
||||
@@ -6,13 +6,14 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1"
|
||||
"github.com/actions/scaleset"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestLabelPropagation(t *testing.T) {
|
||||
func TestMetadataPropagation(t *testing.T) {
|
||||
autoscalingRunnerSet := v1alpha1.AutoscalingRunnerSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-scale-set",
|
||||
@@ -34,6 +35,70 @@ func TestLabelPropagation(t *testing.T) {
|
||||
},
|
||||
Spec: v1alpha1.AutoscalingRunnerSetSpec{
|
||||
GitHubConfigUrl: "https://github.com/org/repo",
|
||||
AutoscalingListenerMetadata: &v1alpha1.ResourceMeta{
|
||||
Labels: map[string]string{
|
||||
"test.com/autoscaling-listener-label": "autoscaling-listener-label",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"test.com/autoscaling-listener-annotation": "autoscaling-listener-annotation",
|
||||
},
|
||||
},
|
||||
ListenerServiceAccountMetadata: &v1alpha1.ResourceMeta{
|
||||
Labels: map[string]string{
|
||||
"test.com/listener-service-account-label": "listener-service-account-label",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"test.com/listener-service-account-annotation": "listener-service-account-annotation",
|
||||
},
|
||||
},
|
||||
ListenerRoleMetadata: &v1alpha1.ResourceMeta{
|
||||
Labels: map[string]string{
|
||||
"test.com/listener-role-label": "listener-role-label",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"test.com/listener-role-annotation": "listener-role-annotation",
|
||||
},
|
||||
},
|
||||
ListenerRoleBindingMetadata: &v1alpha1.ResourceMeta{
|
||||
Labels: map[string]string{
|
||||
"test.com/listener-role-binding-label": "listener-role-binding-label",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"test.com/listener-role-binding-annotation": "listener-role-binding-annotation",
|
||||
},
|
||||
},
|
||||
ListenerConfigSecretMetadata: &v1alpha1.ResourceMeta{
|
||||
Labels: map[string]string{
|
||||
"test.com/listener-config-secret-label": "listener-config-secret-label",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"test.com/listener-config-secret-annotation": "listener-config-secret-annotation",
|
||||
},
|
||||
},
|
||||
EphemeralRunnerSetMetadata: &v1alpha1.ResourceMeta{
|
||||
Labels: map[string]string{
|
||||
"test.com/ephemeral-runner-set-label": "ephemeral-runner-set-label",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"test.com/ephemeral-runner-set-annotation": "ephemeral-runner-set-annotation",
|
||||
},
|
||||
},
|
||||
EphemeralRunnerMetadata: &v1alpha1.ResourceMeta{
|
||||
Labels: map[string]string{
|
||||
"test.com/ephemeral-runner-label": "ephemeral-runner-label",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"test.com/ephemeral-runner-annotation": "ephemeral-runner-annotation",
|
||||
},
|
||||
},
|
||||
EphemeralRunnerConfigSecretMetadata: &v1alpha1.ResourceMeta{
|
||||
Labels: map[string]string{
|
||||
"test.com/ephemeral-runner-config-secret-label": "ephemeral-runner-config-secret-label",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"test.com/ephemeral-runner-config-secret-annotation": "ephemeral-runner-config-secret-annotation",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -57,6 +122,8 @@ func TestLabelPropagation(t *testing.T) {
|
||||
assert.Equal(t, autoscalingRunnerSet.Annotations[AnnotationKeyGitHubRunnerGroupName], ephemeralRunnerSet.Annotations[AnnotationKeyGitHubRunnerGroupName])
|
||||
assert.Equal(t, autoscalingRunnerSet.Annotations[AnnotationKeyGitHubRunnerScaleSetName], ephemeralRunnerSet.Annotations[AnnotationKeyGitHubRunnerScaleSetName])
|
||||
assert.Equal(t, autoscalingRunnerSet.Labels["arbitrary-label"], ephemeralRunnerSet.Labels["arbitrary-label"])
|
||||
assert.Equal(t, "ephemeral-runner-set-label", ephemeralRunnerSet.Labels["test.com/ephemeral-runner-set-label"])
|
||||
assert.Equal(t, "ephemeral-runner-set-annotation", ephemeralRunnerSet.Annotations["test.com/ephemeral-runner-set-annotation"])
|
||||
|
||||
listener, err := b.newAutoScalingListener(&autoscalingRunnerSet, ephemeralRunnerSet, autoscalingRunnerSet.Namespace, "test:latest", nil)
|
||||
require.NoError(t, err)
|
||||
@@ -70,17 +137,26 @@ func TestLabelPropagation(t *testing.T) {
|
||||
assert.Equal(t, "org", listener.Labels[LabelKeyGitHubOrganization])
|
||||
assert.Equal(t, "repo", listener.Labels[LabelKeyGitHubRepository])
|
||||
assert.Equal(t, autoscalingRunnerSet.Labels["arbitrary-label"], listener.Labels["arbitrary-label"])
|
||||
assert.Equal(t, "autoscaling-listener-label", listener.Labels["test.com/autoscaling-listener-label"])
|
||||
assert.Equal(t, "autoscaling-listener-annotation", listener.Annotations["test.com/autoscaling-listener-annotation"])
|
||||
|
||||
assert.NotContains(t, listener.Labels, "example.com/label")
|
||||
assert.NotContains(t, listener.Labels, "example.com/example")
|
||||
assert.NotContains(t, listener.Labels, "directly.excluded.org/label")
|
||||
assert.Equal(t, "not-excluded-value", listener.Labels["directly.excluded.org/arbitrary"])
|
||||
|
||||
listenerServiceAccount := &corev1.ServiceAccount{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test",
|
||||
},
|
||||
}
|
||||
listenerServiceAccount := b.newScaleSetListenerServiceAccount(listener)
|
||||
assert.Equal(t, "listener-service-account-label", listenerServiceAccount.Labels["test.com/listener-service-account-label"])
|
||||
assert.Equal(t, "listener-service-account-annotation", listenerServiceAccount.Annotations["test.com/listener-service-account-annotation"])
|
||||
|
||||
listenerRole := b.newScaleSetListenerRole(listener)
|
||||
assert.Equal(t, "listener-role-label", listenerRole.Labels["test.com/listener-role-label"])
|
||||
assert.Equal(t, "listener-role-annotation", listenerRole.Annotations["test.com/listener-role-annotation"])
|
||||
|
||||
listenerRoleBinding := b.newScaleSetListenerRoleBinding(listener, listenerRole, listenerServiceAccount)
|
||||
assert.Equal(t, "listener-role-binding-label", listenerRoleBinding.Labels["test.com/listener-role-binding-label"])
|
||||
assert.Equal(t, "listener-role-binding-annotation", listenerRoleBinding.Annotations["test.com/listener-role-binding-annotation"])
|
||||
|
||||
listenerPod, err := b.newScaleSetListenerPod(listener, &corev1.Secret{}, listenerServiceAccount, nil)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, listenerPod.Labels, listener.Labels)
|
||||
@@ -97,12 +173,20 @@ func TestLabelPropagation(t *testing.T) {
|
||||
assert.Equal(t, "runner", ephemeralRunner.Labels[LabelKeyKubernetesComponent])
|
||||
assert.Equal(t, autoscalingRunnerSet.Annotations[AnnotationKeyGitHubRunnerGroupName], ephemeralRunner.Annotations[AnnotationKeyGitHubRunnerGroupName])
|
||||
assert.Equal(t, autoscalingRunnerSet.Annotations[AnnotationKeyGitHubRunnerScaleSetName], ephemeralRunnerSet.Annotations[AnnotationKeyGitHubRunnerScaleSetName])
|
||||
assert.Equal(t, "ephemeral-runner-label", ephemeralRunner.Labels["test.com/ephemeral-runner-label"])
|
||||
assert.Equal(t, "ephemeral-runner-annotation", ephemeralRunner.Annotations["test.com/ephemeral-runner-annotation"])
|
||||
|
||||
runnerSecret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test",
|
||||
runnerSecret := b.newEphemeralRunnerJitSecret(ephemeralRunner, &scaleset.RunnerScaleSetJitRunnerConfig{
|
||||
Runner: &scaleset.RunnerReference{
|
||||
ID: 1,
|
||||
Name: "test",
|
||||
RunnerScaleSetID: 1,
|
||||
},
|
||||
}
|
||||
EncodedJITConfig: "",
|
||||
})
|
||||
assert.Equal(t, "ephemeral-runner-config-secret-label", runnerSecret.Labels["test.com/ephemeral-runner-config-secret-label"])
|
||||
assert.Equal(t, "ephemeral-runner-config-secret-annotation", runnerSecret.Annotations["test.com/ephemeral-runner-config-secret-annotation"])
|
||||
|
||||
pod := b.newEphemeralRunnerPod(ephemeralRunner, runnerSecret)
|
||||
for key := range ephemeralRunner.Labels {
|
||||
assert.Equal(t, ephemeralRunner.Labels[key], pod.Labels[key])
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
package actionsgithubcom
|
||||
package secretresolver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1"
|
||||
"github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1/appconfig"
|
||||
"github.com/actions/actions-runner-controller/github/actions"
|
||||
"github.com/actions/actions-runner-controller/controllers/actions.github.com/multiclient"
|
||||
"github.com/actions/actions-runner-controller/controllers/actions.github.com/object"
|
||||
"github.com/actions/actions-runner-controller/vault"
|
||||
"github.com/actions/actions-runner-controller/vault/azurekeyvault"
|
||||
"golang.org/x/net/http/httpproxy"
|
||||
@@ -21,19 +23,27 @@ import (
|
||||
|
||||
type SecretResolver struct {
|
||||
k8sClient client.Client
|
||||
multiClient actions.MultiClient
|
||||
multiClient multiclient.MultiClient
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
type SecretResolverOption func(*SecretResolver)
|
||||
type Option func(*SecretResolver)
|
||||
|
||||
func NewSecretResolver(k8sClient client.Client, multiClient actions.MultiClient, opts ...SecretResolverOption) *SecretResolver {
|
||||
func WithLogger(logger *slog.Logger) Option {
|
||||
return func(sr *SecretResolver) {
|
||||
sr.logger = logger
|
||||
}
|
||||
}
|
||||
|
||||
func New(k8sClient client.Client, scalesetMultiClient multiclient.MultiClient, opts ...Option) *SecretResolver {
|
||||
if k8sClient == nil {
|
||||
panic("k8sClient must not be nil")
|
||||
}
|
||||
|
||||
secretResolver := &SecretResolver{
|
||||
k8sClient: k8sClient,
|
||||
multiClient: multiClient,
|
||||
multiClient: scalesetMultiClient,
|
||||
logger: slog.New(slog.DiscardHandler),
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
@@ -43,17 +53,7 @@ func NewSecretResolver(k8sClient client.Client, multiClient actions.MultiClient,
|
||||
return secretResolver
|
||||
}
|
||||
|
||||
type ActionsGitHubObject interface {
|
||||
client.Object
|
||||
GitHubConfigUrl() string
|
||||
GitHubConfigSecret() string
|
||||
GitHubProxy() *v1alpha1.ProxyConfig
|
||||
GitHubServerTLS() *v1alpha1.TLSConfig
|
||||
VaultConfig() *v1alpha1.VaultConfig
|
||||
VaultProxy() *v1alpha1.ProxyConfig
|
||||
}
|
||||
|
||||
func (sr *SecretResolver) GetAppConfig(ctx context.Context, obj ActionsGitHubObject) (*appconfig.AppConfig, error) {
|
||||
func (sr *SecretResolver) GetAppConfig(ctx context.Context, obj object.ActionsGitHubObject) (*appconfig.AppConfig, error) {
|
||||
resolver, err := sr.resolverForObject(ctx, obj)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get resolver for object: %v", err)
|
||||
@@ -67,7 +67,7 @@ func (sr *SecretResolver) GetAppConfig(ctx context.Context, obj ActionsGitHubObj
|
||||
return appConfig, nil
|
||||
}
|
||||
|
||||
func (sr *SecretResolver) GetActionsService(ctx context.Context, obj ActionsGitHubObject) (actions.ActionsService, error) {
|
||||
func (sr *SecretResolver) GetActionsService(ctx context.Context, obj object.ActionsGitHubObject) (multiclient.Client, error) {
|
||||
resolver, err := sr.resolverForObject(ctx, obj)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get resolver for object: %v", err)
|
||||
@@ -78,7 +78,7 @@ func (sr *SecretResolver) GetActionsService(ctx context.Context, obj ActionsGitH
|
||||
return nil, fmt.Errorf("failed to resolve app config: %v", err)
|
||||
}
|
||||
|
||||
var clientOptions []actions.ClientOption
|
||||
var proxyFunc func(req *http.Request) (*url.URL, error)
|
||||
if proxy := obj.GitHubProxy(); proxy != nil {
|
||||
config := &httpproxy.Config{
|
||||
NoProxy: strings.Join(proxy.NoProxy, ","),
|
||||
@@ -116,16 +116,14 @@ func (sr *SecretResolver) GetActionsService(ctx context.Context, obj ActionsGitH
|
||||
config.HTTPSProxy = u.String()
|
||||
}
|
||||
|
||||
proxyFunc := func(req *http.Request) (*url.URL, error) {
|
||||
proxyFunc = func(req *http.Request) (*url.URL, error) {
|
||||
return config.ProxyFunc()(req.URL)
|
||||
}
|
||||
|
||||
clientOptions = append(clientOptions, actions.WithProxy(proxyFunc))
|
||||
}
|
||||
|
||||
tlsConfig := obj.GitHubServerTLS()
|
||||
if tlsConfig != nil {
|
||||
pool, err := tlsConfig.ToCertPool(func(name, key string) ([]byte, error) {
|
||||
var rootCAs *x509.CertPool
|
||||
if tc := obj.GitHubServerTLS(); tc != nil {
|
||||
pool, err := tc.ToCertPool(func(name, key string) ([]byte, error) {
|
||||
var configmap corev1.ConfigMap
|
||||
err := sr.k8sClient.Get(
|
||||
ctx,
|
||||
@@ -145,19 +143,22 @@ func (sr *SecretResolver) GetActionsService(ctx context.Context, obj ActionsGitH
|
||||
return nil, fmt.Errorf("failed to get tls config: %w", err)
|
||||
}
|
||||
|
||||
clientOptions = append(clientOptions, actions.WithRootCAs(pool))
|
||||
rootCAs = pool
|
||||
}
|
||||
|
||||
return sr.multiClient.GetClientFor(
|
||||
ctx,
|
||||
obj.GitHubConfigUrl(),
|
||||
appConfig,
|
||||
obj.GetNamespace(),
|
||||
clientOptions...,
|
||||
&multiclient.ClientForOptions{
|
||||
GithubConfigURL: obj.GitHubConfigUrl(),
|
||||
AppConfig: *appConfig,
|
||||
Namespace: obj.GetNamespace(),
|
||||
RootCAs: rootCAs,
|
||||
ProxyFunc: proxyFunc,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (sr *SecretResolver) resolverForObject(ctx context.Context, obj ActionsGitHubObject) (resolver, error) {
|
||||
func (sr *SecretResolver) resolverForObject(ctx context.Context, obj object.ActionsGitHubObject) (resolver, error) {
|
||||
vaultConfig := obj.VaultConfig()
|
||||
if vaultConfig == nil || vaultConfig.Type == "" {
|
||||
return &k8sResolver{
|
||||
@@ -54,7 +54,7 @@ func (r *HorizontalRunnerAutoscalerReconciler) suggestDesiredReplicas(ghc *arcgi
|
||||
case v1alpha1.AutoscalingMetricTypePercentageRunnersBusy:
|
||||
suggested, err = r.suggestReplicasByPercentageRunnersBusy(ghc, st, hra, primaryMetric)
|
||||
default:
|
||||
return nil, fmt.Errorf("validating autoscaling metrics: unsupported metric type %q", primaryMetric)
|
||||
return nil, fmt.Errorf("validating autoscaling metrics: unsupported metric type %q", primaryMetric.Type)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -256,7 +256,7 @@ func (c *Client) Identifier() string {
|
||||
identifier += fmt.Sprintf(
|
||||
"appID:%q,installationID:%q,key:%q",
|
||||
c.creds.AppCreds.AppID,
|
||||
c.creds.AppCreds.AppInstallationID,
|
||||
strconv.FormatInt(c.creds.AppCreds.AppInstallationID, 10),
|
||||
c.creds.AppCreds.AppPrivateKey,
|
||||
)
|
||||
}
|
||||
|
||||
4
go.mod
4
go.mod
@@ -1,12 +1,12 @@
|
||||
module github.com/actions/actions-runner-controller
|
||||
|
||||
go 1.25.3
|
||||
go 1.26.1
|
||||
|
||||
require (
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.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.1.1-0.20260218224657-feb84c6d04fb
|
||||
github.com/actions/scaleset v0.2.0
|
||||
github.com/bradleyfalzon/ghinstallation/v2 v2.17.0
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
|
||||
github.com/evanphx/json-patch v5.9.11+incompatible
|
||||
|
||||
4
go.sum
4
go.sum
@@ -25,8 +25,8 @@ github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBi
|
||||
github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE=
|
||||
github.com/actions-runner-controller/httpcache v0.2.0 h1:hCNvYuVPJ2xxYBymqBvH0hSiQpqz4PHF/LbU3XghGNI=
|
||||
github.com/actions-runner-controller/httpcache v0.2.0/go.mod h1:JLu9/2M/btPz1Zu/vTZ71XzukQHn2YeISPmJoM5exBI=
|
||||
github.com/actions/scaleset v0.1.1-0.20260218224657-feb84c6d04fb h1:9jQ9/kHm00UTvZf5MiQcZgIVounynwFEhh0wCV3Ts00=
|
||||
github.com/actions/scaleset v0.1.1-0.20260218224657-feb84c6d04fb/go.mod h1:ncR5vzCCTUSyLgvclAtZ5dRBgF6qwA2nbTfTXmOJp84=
|
||||
github.com/actions/scaleset v0.2.0 h1:CKsDtTjOBCwjyT4ikwiMykMttzuKejimWRAvVr8xj9w=
|
||||
github.com/actions/scaleset v0.2.0/go.mod h1:ncR5vzCCTUSyLgvclAtZ5dRBgF6qwA2nbTfTXmOJp84=
|
||||
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.39.2 h1:EJLg8IdbzgeD7xgvZ+I8M1e0fL0ptn/M47lianzth0I=
|
||||
|
||||
40
logger/logger.go
Normal file
40
logger/logger.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// New creates new slog.Logger based on the format
|
||||
func New(logLevel string, logFormat string) (*slog.Logger, error) {
|
||||
var lvl slog.Level
|
||||
switch strings.ToLower(logLevel) {
|
||||
case "debug":
|
||||
lvl = slog.LevelDebug
|
||||
case "info":
|
||||
lvl = slog.LevelInfo
|
||||
case "warn":
|
||||
lvl = slog.LevelWarn
|
||||
case "error":
|
||||
lvl = slog.LevelError
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid log level: %s", logLevel)
|
||||
}
|
||||
|
||||
switch logFormat {
|
||||
case "json":
|
||||
return slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
|
||||
AddSource: true,
|
||||
Level: lvl,
|
||||
})), nil
|
||||
case "text":
|
||||
return slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
|
||||
AddSource: true,
|
||||
Level: lvl,
|
||||
})), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid log format: %s", logFormat)
|
||||
}
|
||||
}
|
||||
@@ -22,21 +22,18 @@ const (
|
||||
LogFormatJSON = "json"
|
||||
)
|
||||
|
||||
var (
|
||||
LogOpts = zap.Options{
|
||||
TimeEncoder: zapcore.TimeEncoderOfLayout(time.RFC3339),
|
||||
Development: true,
|
||||
EncoderConfigOptions: []zap.EncoderConfigOption{
|
||||
func(ec *zapcore.EncoderConfig) {
|
||||
ec.LevelKey = "severity"
|
||||
ec.MessageKey = "message"
|
||||
},
|
||||
var LogOpts = zap.Options{
|
||||
TimeEncoder: zapcore.TimeEncoderOfLayout(time.RFC3339),
|
||||
Development: true,
|
||||
EncoderConfigOptions: []zap.EncoderConfigOption{
|
||||
func(ec *zapcore.EncoderConfig) {
|
||||
ec.LevelKey = "severity"
|
||||
ec.MessageKey = "message"
|
||||
},
|
||||
}
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
func NewLogger(logLevel string, logFormat string) (logr.Logger, error) {
|
||||
|
||||
if !validLogFormat(logFormat) {
|
||||
return logr.Logger{}, errors.New("invalid log format specified")
|
||||
}
|
||||
|
||||
30
main.go
30
main.go
@@ -28,9 +28,11 @@ import (
|
||||
"github.com/actions/actions-runner-controller/build"
|
||||
actionsgithubcom "github.com/actions/actions-runner-controller/controllers/actions.github.com"
|
||||
actionsgithubcommetrics "github.com/actions/actions-runner-controller/controllers/actions.github.com/metrics"
|
||||
"github.com/actions/actions-runner-controller/controllers/actions.github.com/multiclient"
|
||||
"github.com/actions/actions-runner-controller/controllers/actions.github.com/secretresolver"
|
||||
actionssummerwindnet "github.com/actions/actions-runner-controller/controllers/actions.summerwind.net"
|
||||
"github.com/actions/actions-runner-controller/github"
|
||||
"github.com/actions/actions-runner-controller/github/actions"
|
||||
"github.com/actions/actions-runner-controller/logger"
|
||||
"github.com/actions/actions-runner-controller/logging"
|
||||
"github.com/kelseyhightower/envconfig"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
@@ -85,7 +87,7 @@ func main() {
|
||||
enableLeaderElection bool
|
||||
disableAdmissionWebhook bool
|
||||
updateStrategy string
|
||||
leaderElectionId string
|
||||
leaderElectionID string
|
||||
port int
|
||||
syncPeriod time.Duration
|
||||
|
||||
@@ -121,7 +123,7 @@ func main() {
|
||||
flag.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.")
|
||||
flag.BoolVar(&enableLeaderElection, "enable-leader-election", false,
|
||||
"Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager.")
|
||||
flag.StringVar(&leaderElectionId, "leader-election-id", "actions-runner-controller", "Controller id for leader election.")
|
||||
flag.StringVar(&leaderElectionID, "leader-election-id", "actions-runner-controller", "Controller id for leader election.")
|
||||
flag.StringVar(&runnerPodDefaults.RunnerImage, "runner-image", defaultRunnerImage, "The image name of self-hosted runner container to use by default if one isn't defined in yaml.")
|
||||
flag.StringVar(&runnerPodDefaults.DockerImage, "docker-image", defaultDockerImage, "The image name of docker sidecar container to use by default if one isn't defined in yaml.")
|
||||
flag.StringVar(&runnerPodDefaults.DockerGID, "docker-gid", defaultDockerGID, "The default GID of docker group in the docker sidecar container. Use 1001 for dockerd sidecars of Ubuntu 20.04 runners 121 for Ubuntu 22.04 and 24.04.")
|
||||
@@ -239,7 +241,7 @@ func main() {
|
||||
},
|
||||
WebhookServer: webhookServer,
|
||||
LeaderElection: enableLeaderElection,
|
||||
LeaderElectionID: leaderElectionId,
|
||||
LeaderElectionID: leaderElectionID,
|
||||
Client: client.Options{
|
||||
Cache: &client.CacheOptions{
|
||||
DisableFor: []client.Object{
|
||||
@@ -270,13 +272,18 @@ func main() {
|
||||
actionsgithubcommetrics.RegisterMetrics()
|
||||
}
|
||||
|
||||
actionsMultiClient := actions.NewMultiClient(
|
||||
log.WithName("actions-clients"),
|
||||
)
|
||||
slogLogger, err := logger.New(logLevel, logFormat)
|
||||
if err != nil {
|
||||
log.Error(err, "unable to create logger for secret resolver")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
secretResolver := actionsgithubcom.NewSecretResolver(
|
||||
scalesetMultiClient := multiclient.NewScaleset()
|
||||
|
||||
secretResolver := secretresolver.New(
|
||||
mgr.GetClient(),
|
||||
actionsMultiClient,
|
||||
scalesetMultiClient,
|
||||
secretresolver.WithLogger(slogLogger),
|
||||
)
|
||||
|
||||
rb := actionsgithubcom.ResourceBuilder{
|
||||
@@ -292,7 +299,6 @@ func main() {
|
||||
Scheme: mgr.GetScheme(),
|
||||
ControllerNamespace: managerNamespace,
|
||||
DefaultRunnerScaleSetListenerImage: managerImage,
|
||||
ActionsClient: actionsMultiClient,
|
||||
UpdateStrategy: actionsgithubcom.UpdateStrategy(updateStrategy),
|
||||
DefaultRunnerScaleSetListenerImagePullSecrets: autoScalerImagePullSecrets,
|
||||
ResourceBuilder: rb,
|
||||
@@ -399,7 +405,7 @@ func main() {
|
||||
"default-docker-gid", runnerPodDefaults.DockerGID,
|
||||
"common-runnner-labels", commonRunnerLabels,
|
||||
"leader-election-enabled", enableLeaderElection,
|
||||
"leader-election-id", leaderElectionId,
|
||||
"leader-election-id", leaderElectionID,
|
||||
"watch-namespace", namespace,
|
||||
)
|
||||
|
||||
@@ -489,7 +495,7 @@ func (s *commaSeparatedStringSlice) String() string {
|
||||
}
|
||||
|
||||
func (s *commaSeparatedStringSlice) Set(value string) error {
|
||||
for _, v := range strings.Split(value, ",") {
|
||||
for v := range strings.SplitSeq(value, ",") {
|
||||
if v == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -1106,7 +1106,7 @@ func installActionsWorkflow(t *testing.T, testName, runnerLabel, testResultCMNam
|
||||
testing.Step{
|
||||
Uses: "actions/setup-go@v3",
|
||||
With: &testing.With{
|
||||
GoVersion: "1.25.1",
|
||||
GoVersion: "1.26.1",
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user