Moving to scaleset client for the controller (#4390)
This commit is contained in:
+14
-8
@@ -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:
|
packages:
|
||||||
github.com/actions/actions-runner-controller/github/actions:
|
github.com/actions/actions-runner-controller/github/actions:
|
||||||
config:
|
config:
|
||||||
@@ -8,13 +18,9 @@ packages:
|
|||||||
interfaces:
|
interfaces:
|
||||||
ActionsService:
|
ActionsService:
|
||||||
SessionService:
|
SessionService:
|
||||||
|
|
||||||
github.com/actions/actions-runner-controller/cmd/ghalistener/metrics:
|
github.com/actions/actions-runner-controller/cmd/ghalistener/metrics:
|
||||||
config:
|
config:
|
||||||
inpackage: true
|
all: true
|
||||||
dir: "{{.InterfaceDir}}"
|
github.com/actions/actions-runner-controller/controllers/actions.github.com:
|
||||||
filename: "mocks_test.go"
|
config:
|
||||||
pkgname: "metrics"
|
all: true
|
||||||
interfaces:
|
|
||||||
Recorder:
|
|
||||||
ServerExporter:
|
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ type EphemeralRunnerSpec struct {
|
|||||||
GitHubServerTLS *TLSConfig `json:"githubServerTLS,omitempty"`
|
GitHubServerTLS *TLSConfig `json:"githubServerTLS,omitempty"`
|
||||||
|
|
||||||
// +required
|
// +required
|
||||||
RunnerScaleSetId int `json:"runnerScaleSetId,omitempty"`
|
RunnerScaleSetID int `json:"runnerScaleSetId,omitempty"`
|
||||||
|
|
||||||
// +optional
|
// +optional
|
||||||
Proxy *ProxyConfig `json:"proxy,omitempty"`
|
Proxy *ProxyConfig `json:"proxy,omitempty"`
|
||||||
|
|||||||
@@ -9,11 +9,11 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1"
|
"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/apis/actions.github.com/v1alpha1/appconfig"
|
||||||
"github.com/actions/actions-runner-controller/build"
|
"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"
|
||||||
"github.com/actions/actions-runner-controller/vault/azurekeyvault"
|
"github.com/actions/actions-runner-controller/vault/azurekeyvault"
|
||||||
"github.com/actions/scaleset"
|
"github.com/actions/scaleset"
|
||||||
@@ -23,7 +23,7 @@ import (
|
|||||||
const appName = "ghalistener"
|
const appName = "ghalistener"
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
ConfigureUrl string `json:"configure_url"`
|
ConfigureURL string `json:"configure_url"`
|
||||||
VaultType vault.VaultType `json:"vault_type"`
|
VaultType vault.VaultType `json:"vault_type"`
|
||||||
VaultLookupKey string `json:"vault_lookup_key"`
|
VaultLookupKey string `json:"vault_lookup_key"`
|
||||||
// If the VaultType is set to "azure_key_vault", this field must be populated.
|
// 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.
|
// Validate checks the configuration for errors.
|
||||||
func (c *Config) Validate() error {
|
func (c *Config) Validate() error {
|
||||||
if len(c.ConfigureUrl) == 0 {
|
if len(c.ConfigureURL) == 0 {
|
||||||
return fmt.Errorf("GitHubConfigUrl is not provided")
|
return fmt.Errorf("GitHubConfigUrl is not provided")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,37 +137,7 @@ func (c *Config) Validate() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) Logger() (*slog.Logger, error) {
|
func (c *Config) Logger() (*slog.Logger, error) {
|
||||||
var lvl slog.Level
|
return logger.New(c.LogLevel, c.LogFormat)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) ActionsClient(logger *slog.Logger, clientOptions ...scaleset.HTTPOption) (*scaleset.Client, error) {
|
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 "":
|
case "":
|
||||||
c, err := scaleset.NewClientWithGitHubApp(
|
c, err := scaleset.NewClientWithGitHubApp(
|
||||||
scaleset.ClientWithGitHubAppConfig{
|
scaleset.ClientWithGitHubAppConfig{
|
||||||
GitHubConfigURL: c.ConfigureUrl,
|
GitHubConfigURL: c.ConfigureURL,
|
||||||
GitHubAppAuth: scaleset.GitHubAppAuth{
|
GitHubAppAuth: scaleset.GitHubAppAuth{
|
||||||
ClientID: c.AppConfig.AppID,
|
ClientID: c.AppConfig.AppID,
|
||||||
InstallationID: c.AppConfig.AppInstallationID,
|
InstallationID: c.AppConfig.AppInstallationID,
|
||||||
@@ -224,7 +194,7 @@ func (c *Config) ActionsClient(logger *slog.Logger, clientOptions ...scaleset.HT
|
|||||||
default:
|
default:
|
||||||
c, err := scaleset.NewClientWithPersonalAccessToken(
|
c, err := scaleset.NewClientWithPersonalAccessToken(
|
||||||
scaleset.NewClientWithPersonalAccessTokenConfig{
|
scaleset.NewClientWithPersonalAccessTokenConfig{
|
||||||
GitHubConfigURL: c.ConfigureUrl,
|
GitHubConfigURL: c.ConfigureURL,
|
||||||
PersonalAccessToken: c.Token,
|
PersonalAccessToken: c.Token,
|
||||||
SystemInfo: systemInfo,
|
SystemInfo: systemInfo,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ func TestCustomerServerRootCA(t *testing.T) {
|
|||||||
certsString = certsString + string(intermediate)
|
certsString = certsString + string(intermediate)
|
||||||
|
|
||||||
config := config.Config{
|
config := config.Config{
|
||||||
ConfigureUrl: server.ConfigURLForOrg("myorg"),
|
ConfigureURL: server.ConfigURLForOrg("myorg"),
|
||||||
ServerRootCA: certsString,
|
ServerRootCA: certsString,
|
||||||
AppConfig: &appconfig.AppConfig{
|
AppConfig: &appconfig.AppConfig{
|
||||||
Token: "token",
|
Token: "token",
|
||||||
@@ -85,7 +85,7 @@ func TestProxySettings(t *testing.T) {
|
|||||||
defer os.Setenv("http_proxy", prevProxy)
|
defer os.Setenv("http_proxy", prevProxy)
|
||||||
|
|
||||||
config := config.Config{
|
config := config.Config{
|
||||||
ConfigureUrl: "https://github.com/org/repo",
|
ConfigureURL: "https://github.com/org/repo",
|
||||||
AppConfig: &appconfig.AppConfig{
|
AppConfig: &appconfig.AppConfig{
|
||||||
Token: "token",
|
Token: "token",
|
||||||
},
|
},
|
||||||
@@ -103,7 +103,7 @@ func TestProxySettings(t *testing.T) {
|
|||||||
defer os.Setenv("https_proxy", prevProxy)
|
defer os.Setenv("https_proxy", prevProxy)
|
||||||
|
|
||||||
config := config.Config{
|
config := config.Config{
|
||||||
ConfigureUrl: "https://github.com/org/repo",
|
ConfigureURL: "https://github.com/org/repo",
|
||||||
AppConfig: &appconfig.AppConfig{
|
AppConfig: &appconfig.AppConfig{
|
||||||
Token: "token",
|
Token: "token",
|
||||||
},
|
},
|
||||||
@@ -124,7 +124,7 @@ func TestProxySettings(t *testing.T) {
|
|||||||
defer os.Setenv("no_proxy", prevNoProxy)
|
defer os.Setenv("no_proxy", prevNoProxy)
|
||||||
|
|
||||||
config := config.Config{
|
config := config.Config{
|
||||||
ConfigureUrl: "https://github.com/org/repo",
|
ConfigureURL: "https://github.com/org/repo",
|
||||||
AppConfig: &appconfig.AppConfig{
|
AppConfig: &appconfig.AppConfig{
|
||||||
Token: "token",
|
Token: "token",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
|
|
||||||
func TestConfigValidationMinMax(t *testing.T) {
|
func TestConfigValidationMinMax(t *testing.T) {
|
||||||
config := &Config{
|
config := &Config{
|
||||||
ConfigureUrl: "github.com/some_org/some_repo",
|
ConfigureURL: "github.com/some_org/some_repo",
|
||||||
EphemeralRunnerSetNamespace: "namespace",
|
EphemeralRunnerSetNamespace: "namespace",
|
||||||
EphemeralRunnerSetName: "deployment",
|
EphemeralRunnerSetName: "deployment",
|
||||||
RunnerScaleSetID: 1,
|
RunnerScaleSetID: 1,
|
||||||
@@ -26,7 +26,7 @@ func TestConfigValidationMinMax(t *testing.T) {
|
|||||||
|
|
||||||
func TestConfigValidationMissingToken(t *testing.T) {
|
func TestConfigValidationMissingToken(t *testing.T) {
|
||||||
config := &Config{
|
config := &Config{
|
||||||
ConfigureUrl: "github.com/some_org/some_repo",
|
ConfigureURL: "github.com/some_org/some_repo",
|
||||||
EphemeralRunnerSetNamespace: "namespace",
|
EphemeralRunnerSetNamespace: "namespace",
|
||||||
EphemeralRunnerSetName: "deployment",
|
EphemeralRunnerSetName: "deployment",
|
||||||
RunnerScaleSetID: 1,
|
RunnerScaleSetID: 1,
|
||||||
@@ -46,7 +46,7 @@ func TestConfigValidationAppKey(t *testing.T) {
|
|||||||
AppID: "1",
|
AppID: "1",
|
||||||
AppInstallationID: 10,
|
AppInstallationID: 10,
|
||||||
},
|
},
|
||||||
ConfigureUrl: "github.com/some_org/some_repo",
|
ConfigureURL: "github.com/some_org/some_repo",
|
||||||
EphemeralRunnerSetNamespace: "namespace",
|
EphemeralRunnerSetNamespace: "namespace",
|
||||||
EphemeralRunnerSetName: "deployment",
|
EphemeralRunnerSetName: "deployment",
|
||||||
RunnerScaleSetID: 1,
|
RunnerScaleSetID: 1,
|
||||||
@@ -63,7 +63,7 @@ func TestConfigValidationAppKey(t *testing.T) {
|
|||||||
AppID: "Iv23f8doAlphaNumer1c",
|
AppID: "Iv23f8doAlphaNumer1c",
|
||||||
AppInstallationID: 10,
|
AppInstallationID: 10,
|
||||||
},
|
},
|
||||||
ConfigureUrl: "github.com/some_org/some_repo",
|
ConfigureURL: "github.com/some_org/some_repo",
|
||||||
EphemeralRunnerSetNamespace: "namespace",
|
EphemeralRunnerSetNamespace: "namespace",
|
||||||
EphemeralRunnerSetName: "deployment",
|
EphemeralRunnerSetName: "deployment",
|
||||||
RunnerScaleSetID: 1,
|
RunnerScaleSetID: 1,
|
||||||
@@ -82,7 +82,7 @@ func TestConfigValidationOnlyOneTypeOfCredentials(t *testing.T) {
|
|||||||
AppPrivateKey: "asdf",
|
AppPrivateKey: "asdf",
|
||||||
Token: "asdf",
|
Token: "asdf",
|
||||||
},
|
},
|
||||||
ConfigureUrl: "github.com/some_org/some_repo",
|
ConfigureURL: "github.com/some_org/some_repo",
|
||||||
EphemeralRunnerSetNamespace: "namespace",
|
EphemeralRunnerSetNamespace: "namespace",
|
||||||
EphemeralRunnerSetName: "deployment",
|
EphemeralRunnerSetName: "deployment",
|
||||||
RunnerScaleSetID: 1,
|
RunnerScaleSetID: 1,
|
||||||
@@ -94,7 +94,7 @@ func TestConfigValidationOnlyOneTypeOfCredentials(t *testing.T) {
|
|||||||
|
|
||||||
func TestConfigValidation(t *testing.T) {
|
func TestConfigValidation(t *testing.T) {
|
||||||
config := &Config{
|
config := &Config{
|
||||||
ConfigureUrl: "https://github.com/actions",
|
ConfigureURL: "https://github.com/actions",
|
||||||
EphemeralRunnerSetNamespace: "namespace",
|
EphemeralRunnerSetNamespace: "namespace",
|
||||||
EphemeralRunnerSetName: "deployment",
|
EphemeralRunnerSetName: "deployment",
|
||||||
RunnerScaleSetID: 1,
|
RunnerScaleSetID: 1,
|
||||||
@@ -125,7 +125,7 @@ func TestConfigValidationConfigUrl(t *testing.T) {
|
|||||||
func TestConfigValidationWithVaultConfig(t *testing.T) {
|
func TestConfigValidationWithVaultConfig(t *testing.T) {
|
||||||
t.Run("valid", func(t *testing.T) {
|
t.Run("valid", func(t *testing.T) {
|
||||||
config := &Config{
|
config := &Config{
|
||||||
ConfigureUrl: "https://github.com/actions",
|
ConfigureURL: "https://github.com/actions",
|
||||||
EphemeralRunnerSetNamespace: "namespace",
|
EphemeralRunnerSetNamespace: "namespace",
|
||||||
EphemeralRunnerSetName: "deployment",
|
EphemeralRunnerSetName: "deployment",
|
||||||
RunnerScaleSetID: 1,
|
RunnerScaleSetID: 1,
|
||||||
@@ -140,7 +140,7 @@ func TestConfigValidationWithVaultConfig(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("invalid vault type", func(t *testing.T) {
|
t.Run("invalid vault type", func(t *testing.T) {
|
||||||
config := &Config{
|
config := &Config{
|
||||||
ConfigureUrl: "https://github.com/actions",
|
ConfigureURL: "https://github.com/actions",
|
||||||
EphemeralRunnerSetNamespace: "namespace",
|
EphemeralRunnerSetNamespace: "namespace",
|
||||||
EphemeralRunnerSetName: "deployment",
|
EphemeralRunnerSetName: "deployment",
|
||||||
RunnerScaleSetID: 1,
|
RunnerScaleSetID: 1,
|
||||||
@@ -155,7 +155,7 @@ func TestConfigValidationWithVaultConfig(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("vault type set without lookup key", func(t *testing.T) {
|
t.Run("vault type set without lookup key", func(t *testing.T) {
|
||||||
config := &Config{
|
config := &Config{
|
||||||
ConfigureUrl: "https://github.com/actions",
|
ConfigureURL: "https://github.com/actions",
|
||||||
EphemeralRunnerSetNamespace: "namespace",
|
EphemeralRunnerSetNamespace: "namespace",
|
||||||
EphemeralRunnerSetName: "deployment",
|
EphemeralRunnerSetName: "deployment",
|
||||||
RunnerScaleSetID: 1,
|
RunnerScaleSetID: 1,
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func run(ctx context.Context, config *config.Config) error {
|
func run(ctx context.Context, config *config.Config) error {
|
||||||
ghConfig, err := actions.ParseGitHubConfigFromURL(config.ConfigureUrl)
|
ghConfig, err := actions.ParseGitHubConfigFromURL(config.ConfigureURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to parse GitHub config from URL: %w", err)
|
return fmt.Errorf("failed to parse GitHub config from URL: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ import (
|
|||||||
logf "sigs.k8s.io/controller-runtime/pkg/log"
|
logf "sigs.k8s.io/controller-runtime/pkg/log"
|
||||||
|
|
||||||
ghalistenerconfig "github.com/actions/actions-runner-controller/cmd/ghalistener/config"
|
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/ginkgo/v2"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
@@ -43,7 +44,10 @@ var _ = Describe("Test AutoScalingListener controller", func() {
|
|||||||
autoscalingNS, mgr = createNamespace(GinkgoT(), k8sClient)
|
autoscalingNS, mgr = createNamespace(GinkgoT(), k8sClient)
|
||||||
configSecret = createDefaultSecret(GinkgoT(), k8sClient, autoscalingNS.Name)
|
configSecret = createDefaultSecret(GinkgoT(), k8sClient, autoscalingNS.Name)
|
||||||
|
|
||||||
secretResolver := NewSecretResolver(mgr.GetClient(), fake.NewMultiClient())
|
secretResolver := secretresolver.New(
|
||||||
|
mgr.GetClient(),
|
||||||
|
scalefake.NewMultiClient(),
|
||||||
|
)
|
||||||
|
|
||||||
rb := ResourceBuilder{
|
rb := ResourceBuilder{
|
||||||
SecretResolver: secretResolver,
|
SecretResolver: secretResolver,
|
||||||
@@ -459,7 +463,7 @@ var _ = Describe("Test AutoScalingListener customization", func() {
|
|||||||
autoscalingNS, mgr = createNamespace(GinkgoT(), k8sClient)
|
autoscalingNS, mgr = createNamespace(GinkgoT(), k8sClient)
|
||||||
configSecret = createDefaultSecret(GinkgoT(), k8sClient, autoscalingNS.Name)
|
configSecret = createDefaultSecret(GinkgoT(), k8sClient, autoscalingNS.Name)
|
||||||
|
|
||||||
secretResolver := NewSecretResolver(mgr.GetClient(), fake.NewMultiClient())
|
secretResolver := secretresolver.New(mgr.GetClient(), scalefake.NewMultiClient())
|
||||||
|
|
||||||
rb := ResourceBuilder{
|
rb := ResourceBuilder{
|
||||||
SecretResolver: secretResolver,
|
SecretResolver: secretResolver,
|
||||||
@@ -788,7 +792,7 @@ var _ = Describe("Test AutoScalingListener controller with proxy", func() {
|
|||||||
ctx = context.Background()
|
ctx = context.Background()
|
||||||
autoscalingNS, mgr = createNamespace(GinkgoT(), k8sClient)
|
autoscalingNS, mgr = createNamespace(GinkgoT(), k8sClient)
|
||||||
configSecret = createDefaultSecret(GinkgoT(), k8sClient, autoscalingNS.Name)
|
configSecret = createDefaultSecret(GinkgoT(), k8sClient, autoscalingNS.Name)
|
||||||
secretResolver := NewSecretResolver(mgr.GetClient(), fake.NewMultiClient())
|
secretResolver := secretresolver.New(mgr.GetClient(), scalefake.NewMultiClient())
|
||||||
|
|
||||||
rb := ResourceBuilder{
|
rb := ResourceBuilder{
|
||||||
SecretResolver: secretResolver,
|
SecretResolver: secretResolver,
|
||||||
@@ -991,7 +995,7 @@ var _ = Describe("Test AutoScalingListener controller with template modification
|
|||||||
autoscalingNS, mgr = createNamespace(GinkgoT(), k8sClient)
|
autoscalingNS, mgr = createNamespace(GinkgoT(), k8sClient)
|
||||||
configSecret = createDefaultSecret(GinkgoT(), k8sClient, autoscalingNS.Name)
|
configSecret = createDefaultSecret(GinkgoT(), k8sClient, autoscalingNS.Name)
|
||||||
|
|
||||||
secretResolver := NewSecretResolver(mgr.GetClient(), fake.NewMultiClient())
|
secretResolver := secretresolver.New(mgr.GetClient(), scalefake.NewMultiClient())
|
||||||
|
|
||||||
rb := ResourceBuilder{
|
rb := ResourceBuilder{
|
||||||
SecretResolver: secretResolver,
|
SecretResolver: secretResolver,
|
||||||
@@ -1094,7 +1098,7 @@ var _ = Describe("Test GitHub Server TLS configuration", func() {
|
|||||||
autoscalingNS, mgr = createNamespace(GinkgoT(), k8sClient)
|
autoscalingNS, mgr = createNamespace(GinkgoT(), k8sClient)
|
||||||
configSecret = createDefaultSecret(GinkgoT(), k8sClient, autoscalingNS.Name)
|
configSecret = createDefaultSecret(GinkgoT(), k8sClient, autoscalingNS.Name)
|
||||||
|
|
||||||
secretResolver := NewSecretResolver(mgr.GetClient(), fake.NewMultiClient())
|
secretResolver := secretresolver.New(mgr.GetClient(), scalefake.NewMultiClient())
|
||||||
|
|
||||||
rb := ResourceBuilder{
|
rb := ResourceBuilder{
|
||||||
SecretResolver: secretResolver,
|
SecretResolver: secretResolver,
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import (
|
|||||||
|
|
||||||
"github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1"
|
"github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1"
|
||||||
"github.com/actions/actions-runner-controller/build"
|
"github.com/actions/actions-runner-controller/build"
|
||||||
"github.com/actions/actions-runner-controller/github/actions"
|
"github.com/actions/scaleset"
|
||||||
"github.com/go-logr/logr"
|
"github.com/go-logr/logr"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
rbacv1 "k8s.io/api/rbac/v1"
|
rbacv1 "k8s.io/api/rbac/v1"
|
||||||
@@ -78,7 +78,6 @@ type AutoscalingRunnerSetReconciler struct {
|
|||||||
DefaultRunnerScaleSetListenerImage string
|
DefaultRunnerScaleSetListenerImage string
|
||||||
DefaultRunnerScaleSetListenerImagePullSecrets []string
|
DefaultRunnerScaleSetListenerImagePullSecrets []string
|
||||||
UpdateStrategy UpdateStrategy
|
UpdateStrategy UpdateStrategy
|
||||||
ActionsClient actions.MultiClient
|
|
||||||
ResourceBuilder
|
ResourceBuilder
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -427,17 +426,16 @@ func (r *AutoscalingRunnerSetReconciler) createRunnerScaleSet(ctx context.Contex
|
|||||||
if runnerScaleSet == nil {
|
if runnerScaleSet == nil {
|
||||||
runnerScaleSet, err = actionsClient.CreateRunnerScaleSet(
|
runnerScaleSet, err = actionsClient.CreateRunnerScaleSet(
|
||||||
ctx,
|
ctx,
|
||||||
&actions.RunnerScaleSet{
|
&scaleset.RunnerScaleSet{
|
||||||
Name: autoscalingRunnerSet.Spec.RunnerScaleSetName,
|
Name: autoscalingRunnerSet.Spec.RunnerScaleSetName,
|
||||||
RunnerGroupId: runnerGroupID,
|
RunnerGroupID: runnerGroupID,
|
||||||
Labels: []actions.Label{
|
Labels: []scaleset.Label{
|
||||||
{
|
{
|
||||||
Name: autoscalingRunnerSet.Spec.RunnerScaleSetName,
|
Name: autoscalingRunnerSet.Spec.RunnerScaleSetName,
|
||||||
Type: "System",
|
Type: "System",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
RunnerSetting: actions.RunnerSetting{
|
RunnerSetting: scaleset.RunnerSetting{
|
||||||
Ephemeral: true,
|
|
||||||
DisableUpdate: true,
|
DisableUpdate: true,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -447,15 +445,11 @@ func (r *AutoscalingRunnerSetReconciler) createRunnerScaleSet(ctx context.Contex
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
actionsClient.SetUserAgent(actions.UserAgentInfo{
|
info := actionsClient.SystemInfo()
|
||||||
Version: build.Version,
|
info.ScaleSetID = runnerScaleSet.ID
|
||||||
CommitSHA: build.CommitSHA,
|
actionsClient.SetSystemInfo(info)
|
||||||
ScaleSetID: runnerScaleSet.Id,
|
|
||||||
HasProxy: autoscalingRunnerSet.Spec.Proxy != nil,
|
|
||||||
Subsystem: "controller",
|
|
||||||
})
|
|
||||||
|
|
||||||
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 {
|
if autoscalingRunnerSet.Annotations == nil {
|
||||||
autoscalingRunnerSet.Annotations = map[string]string{}
|
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")
|
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) {
|
if err = patch(ctx, r.Client, autoscalingRunnerSet, func(obj *v1alpha1.AutoscalingRunnerSet) {
|
||||||
obj.Annotations[AnnotationKeyGitHubRunnerScaleSetName] = runnerScaleSet.Name
|
obj.Annotations[AnnotationKeyGitHubRunnerScaleSetName] = runnerScaleSet.Name
|
||||||
obj.Annotations[runnerScaleSetIDAnnotationKey] = strconv.Itoa(runnerScaleSet.Id)
|
obj.Annotations[runnerScaleSetIDAnnotationKey] = strconv.Itoa(runnerScaleSet.ID)
|
||||||
obj.Annotations[AnnotationKeyGitHubRunnerGroupName] = runnerScaleSet.RunnerGroupName
|
obj.Annotations[AnnotationKeyGitHubRunnerGroupName] = runnerScaleSet.RunnerGroupName
|
||||||
if err := applyGitHubURLLabels(obj.Spec.GitHubConfigUrl, obj.Labels); err != nil { // should never happen
|
if err := applyGitHubURLLabels(obj.Spec.GitHubConfigUrl, obj.Labels); err != nil { // should never happen
|
||||||
logger.Error(err, "Failed to apply GitHub URL labels")
|
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",
|
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,
|
"name", runnerScaleSet.Name,
|
||||||
"runnerGroupName", runnerScaleSet.RunnerGroupName)
|
"runnerGroupName", runnerScaleSet.RunnerGroupName)
|
||||||
return ctrl.Result{}, nil
|
return ctrl.Result{}, nil
|
||||||
@@ -507,7 +501,7 @@ func (r *AutoscalingRunnerSetReconciler) updateRunnerScaleSetRunnerGroup(ctx con
|
|||||||
runnerGroupID = int(runnerGroup.ID)
|
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 {
|
if err != nil {
|
||||||
logger.Error(err, "Failed to update runner scale set", "runnerScaleSetId", runnerScaleSetID)
|
logger.Error(err, "Failed to update runner scale set", "runnerScaleSetId", runnerScaleSetID)
|
||||||
return ctrl.Result{}, err
|
return ctrl.Result{}, err
|
||||||
@@ -544,7 +538,7 @@ func (r *AutoscalingRunnerSetReconciler) updateRunnerScaleSetName(ctx context.Co
|
|||||||
return ctrl.Result{}, err
|
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 {
|
if err != nil {
|
||||||
logger.Error(err, "Failed to update runner scale set", "runnerScaleSetId", runnerScaleSetID)
|
logger.Error(err, "Failed to update runner scale set", "runnerScaleSetId", runnerScaleSetID)
|
||||||
return ctrl.Result{}, err
|
return ctrl.Result{}, err
|
||||||
|
|||||||
@@ -3,13 +3,12 @@ package actionsgithubcom
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/base64"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
@@ -19,7 +18,6 @@ import (
|
|||||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||||
logf "sigs.k8s.io/controller-runtime/pkg/log"
|
logf "sigs.k8s.io/controller-runtime/pkg/log"
|
||||||
|
|
||||||
"github.com/go-logr/logr"
|
|
||||||
. "github.com/onsi/ginkgo/v2"
|
. "github.com/onsi/ginkgo/v2"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"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/apis/actions.github.com/v1alpha1"
|
||||||
"github.com/actions/actions-runner-controller/build"
|
"github.com/actions/actions-runner-controller/build"
|
||||||
"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/github/actions/fake"
|
scalefake "github.com/actions/actions-runner-controller/controllers/actions.github.com/multiclient/fake"
|
||||||
"github.com/actions/actions-runner-controller/github/actions/testserver"
|
"github.com/actions/actions-runner-controller/controllers/actions.github.com/secretresolver"
|
||||||
|
"github.com/actions/scaleset"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -63,6 +62,10 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() {
|
|||||||
autoscalingNS, mgr = createNamespace(GinkgoT(), k8sClient)
|
autoscalingNS, mgr = createNamespace(GinkgoT(), k8sClient)
|
||||||
configSecret = createDefaultSecret(GinkgoT(), k8sClient, autoscalingNS.Name)
|
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{
|
controller = &AutoscalingRunnerSetReconciler{
|
||||||
Client: mgr.GetClient(),
|
Client: mgr.GetClient(),
|
||||||
Scheme: mgr.GetScheme(),
|
Scheme: mgr.GetScheme(),
|
||||||
@@ -70,10 +73,30 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() {
|
|||||||
ControllerNamespace: autoscalingNS.Name,
|
ControllerNamespace: autoscalingNS.Name,
|
||||||
DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc",
|
DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc",
|
||||||
ResourceBuilder: ResourceBuilder{
|
ResourceBuilder: ResourceBuilder{
|
||||||
SecretResolver: &SecretResolver{
|
SecretResolver: secretresolver.New(mgr.GetClient(), scalefake.NewMultiClient(
|
||||||
k8sClient: k8sClient,
|
scalefake.WithClient(
|
||||||
multiClient: fake.NewMultiClient(),
|
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)
|
err := controller.SetupWithManager(mgr)
|
||||||
@@ -681,25 +704,34 @@ var _ = Describe("Test AutoScalingController updates", Ordered, func() {
|
|||||||
autoscalingNS, mgr = createNamespace(GinkgoT(), k8sClient)
|
autoscalingNS, mgr = createNamespace(GinkgoT(), k8sClient)
|
||||||
configSecret = createDefaultSecret(GinkgoT(), k8sClient, autoscalingNS.Name)
|
configSecret = createDefaultSecret(GinkgoT(), k8sClient, autoscalingNS.Name)
|
||||||
|
|
||||||
multiClient := fake.NewMultiClient(
|
multiClient := scalefake.NewMultiClient(
|
||||||
fake.WithDefaultClient(
|
scalefake.WithClient(
|
||||||
fake.NewFakeClient(
|
scalefake.NewClient(
|
||||||
fake.WithUpdateRunnerScaleSet(
|
scalefake.WithGenerateJitRunnerConfig(
|
||||||
&actions.RunnerScaleSet{
|
&scaleset.RunnerScaleSetJitRunnerConfig{
|
||||||
Id: 1,
|
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",
|
Name: "testset_update",
|
||||||
RunnerGroupId: 1,
|
RunnerGroupID: 1,
|
||||||
RunnerGroupName: "testgroup",
|
RunnerGroupName: "testgroup",
|
||||||
Labels: []actions.Label{{Type: "test", Name: "test"}},
|
Labels: []scaleset.Label{{Type: "test", Name: "test"}},
|
||||||
RunnerSetting: actions.RunnerSetting{},
|
RunnerSetting: scaleset.RunnerSetting{},
|
||||||
CreatedOn: time.Now(),
|
CreatedOn: time.Now(),
|
||||||
RunnerJitConfigUrl: "test.test.test",
|
RunnerJitConfigURL: "test.test.test",
|
||||||
Statistics: nil,
|
Statistics: nil,
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
nil,
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -710,10 +742,7 @@ var _ = Describe("Test AutoScalingController updates", Ordered, func() {
|
|||||||
ControllerNamespace: autoscalingNS.Name,
|
ControllerNamespace: autoscalingNS.Name,
|
||||||
DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc",
|
DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc",
|
||||||
ResourceBuilder: ResourceBuilder{
|
ResourceBuilder: ResourceBuilder{
|
||||||
SecretResolver: &SecretResolver{
|
SecretResolver: secretresolver.New(mgr.GetClient(), multiClient),
|
||||||
k8sClient: k8sClient,
|
|
||||||
multiClient: multiClient,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err := controller.SetupWithManager(mgr)
|
err := controller.SetupWithManager(mgr)
|
||||||
@@ -830,10 +859,7 @@ var _ = Describe("Test AutoscalingController creation failures", Ordered, func()
|
|||||||
ControllerNamespace: autoscalingNS.Name,
|
ControllerNamespace: autoscalingNS.Name,
|
||||||
DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc",
|
DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc",
|
||||||
ResourceBuilder: ResourceBuilder{
|
ResourceBuilder: ResourceBuilder{
|
||||||
SecretResolver: &SecretResolver{
|
SecretResolver: secretresolver.New(mgr.GetClient(), scalefake.NewMultiClient()),
|
||||||
k8sClient: k8sClient,
|
|
||||||
multiClient: fake.NewMultiClient(),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err := controller.SetupWithManager(mgr)
|
err := controller.SetupWithManager(mgr)
|
||||||
@@ -953,7 +979,6 @@ var _ = Describe("Test client optional configuration", Ordered, func() {
|
|||||||
ctx = context.Background()
|
ctx = context.Background()
|
||||||
autoscalingNS, mgr = createNamespace(GinkgoT(), k8sClient)
|
autoscalingNS, mgr = createNamespace(GinkgoT(), k8sClient)
|
||||||
configSecret = createDefaultSecret(GinkgoT(), k8sClient, autoscalingNS.Name)
|
configSecret = createDefaultSecret(GinkgoT(), k8sClient, autoscalingNS.Name)
|
||||||
multiClient := actions.NewMultiClient(logr.Discard())
|
|
||||||
controller = &AutoscalingRunnerSetReconciler{
|
controller = &AutoscalingRunnerSetReconciler{
|
||||||
Client: mgr.GetClient(),
|
Client: mgr.GetClient(),
|
||||||
Scheme: mgr.GetScheme(),
|
Scheme: mgr.GetScheme(),
|
||||||
@@ -961,10 +986,7 @@ var _ = Describe("Test client optional configuration", Ordered, func() {
|
|||||||
ControllerNamespace: autoscalingNS.Name,
|
ControllerNamespace: autoscalingNS.Name,
|
||||||
DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc",
|
DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc",
|
||||||
ResourceBuilder: ResourceBuilder{
|
ResourceBuilder: ResourceBuilder{
|
||||||
SecretResolver: &SecretResolver{
|
SecretResolver: secretresolver.New(mgr.GetClient(), multiclient.NewScaleset()),
|
||||||
k8sClient: k8sClient,
|
|
||||||
multiClient: multiClient,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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() {
|
It("should be able to make requests to a server using a proxy", func() {
|
||||||
serverSuccessfullyCalled := false
|
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
|
serverSuccessfullyCalled = true
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
}))
|
}))
|
||||||
|
defer proxy.Close()
|
||||||
|
|
||||||
min := 1
|
min := 1
|
||||||
max := 10
|
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() {
|
It("should be able to make requests to a server using a proxy with user info", func() {
|
||||||
serverSuccessfullyCalled := false
|
controller.ResourceBuilder.SecretResolver = secretresolver.New(k8sClient, scalefake.NewMultiClient(
|
||||||
proxy := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
scalefake.WithClient(
|
||||||
header := r.Header.Get("Proxy-Authorization")
|
scalefake.NewClient(
|
||||||
Expect(header).NotTo(BeEmpty())
|
scalefake.WithGetRunnerGroupByName(&scaleset.RunnerGroup{ID: 1, Name: "testgroup"}, nil),
|
||||||
|
scalefake.WithGetRunnerScaleSet(nil, nil),
|
||||||
header = strings.TrimPrefix(header, "Basic ")
|
scalefake.WithCreateRunnerScaleSet(&scaleset.RunnerScaleSet{ID: 1, Name: "test-asrs", RunnerGroupID: 1, RunnerGroupName: "testgroup"}, nil),
|
||||||
decoded, err := base64.StdEncoding.DecodeString(header)
|
scalefake.WithUpdateRunnerScaleSet(&scaleset.RunnerScaleSet{ID: 1, Name: "test-asrs", RunnerGroupID: 1, RunnerGroupName: "testgroup"}, nil),
|
||||||
Expect(err).NotTo(HaveOccurred())
|
scalefake.WithDeleteRunnerScaleSet(nil),
|
||||||
Expect(string(decoded)).To(Equal("test:password"))
|
),
|
||||||
|
),
|
||||||
serverSuccessfullyCalled = true
|
))
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
}))
|
|
||||||
GinkgoT().Cleanup(func() {
|
|
||||||
proxy.Close()
|
|
||||||
})
|
|
||||||
|
|
||||||
secretCredentials := &corev1.Secret{
|
secretCredentials := &corev1.Secret{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "proxy-credentials",
|
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)
|
err := k8sClient.Create(ctx, secretCredentials)
|
||||||
Expect(err).NotTo(HaveOccurred(), "failed to create secret credentials")
|
Expect(err).NotTo(HaveOccurred(), "failed to create secret credentials")
|
||||||
|
|
||||||
@@ -1078,7 +1100,7 @@ var _ = Describe("Test client optional configuration", Ordered, func() {
|
|||||||
RunnerGroup: "testgroup",
|
RunnerGroup: "testgroup",
|
||||||
Proxy: &v1alpha1.ProxyConfig{
|
Proxy: &v1alpha1.ProxyConfig{
|
||||||
HTTP: &v1alpha1.ProxyServerConfig{
|
HTTP: &v1alpha1.ProxyServerConfig{
|
||||||
Url: proxy.URL,
|
Url: "http://test:password@" + proxy.Listener.Addr().String(),
|
||||||
CredentialSecretRef: "proxy-credentials",
|
CredentialSecretRef: "proxy-credentials",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -1098,14 +1120,24 @@ var _ = Describe("Test client optional configuration", Ordered, func() {
|
|||||||
err = k8sClient.Create(ctx, autoscalingRunnerSet)
|
err = k8sClient.Create(ctx, autoscalingRunnerSet)
|
||||||
Expect(err).NotTo(HaveOccurred(), "failed to create 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(
|
Eventually(
|
||||||
func() (bool, error) {
|
func() (*v1alpha1.EphemeralRunnerSet, error) {
|
||||||
return serverSuccessfullyCalled, nil
|
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,
|
autoscalingRunnerSetTestTimeout,
|
||||||
1*time.Nanosecond,
|
autoscalingRunnerSetTestInterval,
|
||||||
).Should(BeTrue(), "server was not called")
|
).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,
|
ControllerNamespace: autoscalingNS.Name,
|
||||||
DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc",
|
DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc",
|
||||||
ResourceBuilder: ResourceBuilder{
|
ResourceBuilder: ResourceBuilder{
|
||||||
SecretResolver: &SecretResolver{
|
SecretResolver: secretresolver.New(mgr.GetClient(), scalefake.NewMultiClient(
|
||||||
k8sClient: k8sClient,
|
scalefake.WithClient(
|
||||||
multiClient: fake.NewMultiClient(),
|
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)
|
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() {
|
It("should be able to make requests to a server using root CAs", func() {
|
||||||
controller.SecretResolver = &SecretResolver{
|
controller.SecretResolver = secretresolver.New(k8sClient, multiclient.NewScaleset())
|
||||||
k8sClient: k8sClient,
|
|
||||||
multiClient: actions.NewMultiClient(logr.Discard()),
|
|
||||||
}
|
|
||||||
|
|
||||||
certsFolder := filepath.Join(
|
certsFolder := filepath.Join(
|
||||||
"../../",
|
"../../",
|
||||||
@@ -1177,7 +1213,7 @@ var _ = Describe("Test client optional configuration", Ordered, func() {
|
|||||||
keyPath := filepath.Join(certsFolder, "server.key")
|
keyPath := filepath.Join(certsFolder, "server.key")
|
||||||
|
|
||||||
serverSuccessfullyCalled := false
|
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
|
serverSuccessfullyCalled = true
|
||||||
w.WriteHeader(http.StatusOK)
|
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.TLS = &tls.Config{Certificates: []tls.Certificate{cert}}
|
||||||
server.StartTLS()
|
server.StartTLS()
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
min := 1
|
min := 1
|
||||||
max := 10
|
max := 10
|
||||||
@@ -1198,7 +1235,7 @@ var _ = Describe("Test client optional configuration", Ordered, func() {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Spec: v1alpha1.AutoscalingRunnerSetSpec{
|
Spec: v1alpha1.AutoscalingRunnerSetSpec{
|
||||||
GitHubConfigUrl: server.ConfigURLForOrg("my-org"),
|
GitHubConfigUrl: server.URL + "/my-org",
|
||||||
GitHubConfigSecret: configSecret.Name,
|
GitHubConfigSecret: configSecret.Name,
|
||||||
GitHubServerTLS: &v1alpha1.TLSConfig{
|
GitHubServerTLS: &v1alpha1.TLSConfig{
|
||||||
CertificateFrom: &v1alpha1.TLSCertificateSource{
|
CertificateFrom: &v1alpha1.TLSCertificateSource{
|
||||||
@@ -1391,10 +1428,7 @@ var _ = Describe("Test external permissions cleanup", Ordered, func() {
|
|||||||
ControllerNamespace: autoscalingNS.Name,
|
ControllerNamespace: autoscalingNS.Name,
|
||||||
DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc",
|
DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc",
|
||||||
ResourceBuilder: ResourceBuilder{
|
ResourceBuilder: ResourceBuilder{
|
||||||
SecretResolver: &SecretResolver{
|
SecretResolver: secretresolver.New(mgr.GetClient(), scalefake.NewMultiClient()),
|
||||||
k8sClient: k8sClient,
|
|
||||||
multiClient: fake.NewMultiClient(),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err := controller.SetupWithManager(mgr)
|
err := controller.SetupWithManager(mgr)
|
||||||
@@ -1554,10 +1588,7 @@ var _ = Describe("Test external permissions cleanup", Ordered, func() {
|
|||||||
ControllerNamespace: autoscalingNS.Name,
|
ControllerNamespace: autoscalingNS.Name,
|
||||||
DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc",
|
DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc",
|
||||||
ResourceBuilder: ResourceBuilder{
|
ResourceBuilder: ResourceBuilder{
|
||||||
SecretResolver: &SecretResolver{
|
SecretResolver: secretresolver.New(mgr.GetClient(), scalefake.NewMultiClient()),
|
||||||
k8sClient: k8sClient,
|
|
||||||
multiClient: fake.NewMultiClient(),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err := controller.SetupWithManager(mgr)
|
err := controller.SetupWithManager(mgr)
|
||||||
@@ -1767,10 +1798,7 @@ var _ = Describe("Test resource version and build version mismatch", func() {
|
|||||||
ControllerNamespace: autoscalingNS.Name,
|
ControllerNamespace: autoscalingNS.Name,
|
||||||
DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc",
|
DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc",
|
||||||
ResourceBuilder: ResourceBuilder{
|
ResourceBuilder: ResourceBuilder{
|
||||||
SecretResolver: &SecretResolver{
|
SecretResolver: secretresolver.New(mgr.GetClient(), scalefake.NewMultiClient()),
|
||||||
k8sClient: k8sClient,
|
|
||||||
multiClient: fake.NewMultiClient(),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err := controller.SetupWithManager(mgr)
|
err := controller.SetupWithManager(mgr)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
kclient "sigs.k8s.io/controller-runtime/pkg/client"
|
kclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
type object[T kclient.Object] interface {
|
type kubernetesObject[T kclient.Object] interface {
|
||||||
kclient.Object
|
kclient.Object
|
||||||
DeepCopy() T
|
DeepCopy() T
|
||||||
}
|
}
|
||||||
@@ -15,7 +15,7 @@ type patcher interface {
|
|||||||
Patch(ctx context.Context, obj kclient.Object, patch kclient.Patch, opts ...kclient.PatchOption) error
|
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()
|
original := obj.DeepCopy()
|
||||||
update(obj)
|
update(obj)
|
||||||
return client.Patch(ctx, obj, kclient.MergeFrom(original))
|
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
|
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()
|
original := obj.DeepCopy()
|
||||||
update(obj)
|
update(obj)
|
||||||
return client.Patch(ctx, obj, kclient.MergeFrom(original))
|
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/apis/actions.github.com/v1alpha1"
|
||||||
"github.com/actions/actions-runner-controller/github/actions"
|
"github.com/actions/actions-runner-controller/github/actions"
|
||||||
|
"github.com/actions/scaleset"
|
||||||
"github.com/go-logr/logr"
|
"github.com/go-logr/logr"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
@@ -599,7 +600,7 @@ func (r *EphemeralRunnerReconciler) deletePodAsFailed(ctx context.Context, ephem
|
|||||||
return nil
|
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
|
// Runner is not registered with the service. We need to register it first
|
||||||
log.Info("Creating ephemeral runner JIT config")
|
log.Info("Creating ephemeral runner JIT config")
|
||||||
actionsClient, err := r.GetActionsService(ctx, ephemeralRunner)
|
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)
|
return nil, fmt.Errorf("failed to get actions client for generating JIT config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
jitSettings := &actions.RunnerScaleSetJitRunnerSetting{
|
jitSettings := &scaleset.RunnerScaleSetJitRunnerSetting{
|
||||||
Name: ephemeralRunner.Name,
|
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
|
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
|
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)
|
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)
|
log.Info("Found the runner with the same name", "runnerId", existingRunner.ID, "runnerScaleSetId", existingRunner.RunnerScaleSetID)
|
||||||
if existingRunner.RunnerScaleSetId == ephemeralRunner.Spec.RunnerScaleSetId {
|
if existingRunner.RunnerScaleSetID == ephemeralRunner.Spec.RunnerScaleSetID {
|
||||||
log.Info("Removing the runner with the same name")
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to remove runner from the service: %w", err)
|
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",
|
log.Info("Created ephemeral runner pod",
|
||||||
"runnerScaleSetId", runner.Spec.RunnerScaleSetId,
|
"runnerScaleSetId", runner.Spec.RunnerScaleSetID,
|
||||||
"runnerName", runner.Status.RunnerName,
|
"runnerName", runner.Status.RunnerName,
|
||||||
"runnerId", runner.Status.RunnerId,
|
"runnerId", runner.Status.RunnerId,
|
||||||
"configUrl", runner.Spec.GitHubConfigUrl,
|
"configUrl", runner.Spec.GitHubConfigUrl,
|
||||||
@@ -740,7 +741,7 @@ func (r *EphemeralRunnerReconciler) createPod(ctx context.Context, runner *v1alp
|
|||||||
return ctrl.Result{}, nil
|
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")
|
log.Info("Creating new secret for ephemeral runner")
|
||||||
jitSecret := r.newEphemeralRunnerJitSecret(runner, jitConfig)
|
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/apis/actions.github.com/v1alpha1"
|
||||||
"github.com/actions/actions-runner-controller/github/actions"
|
"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/controllers/actions.github.com/multiclient"
|
||||||
"github.com/actions/actions-runner-controller/github/actions/testserver"
|
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/ginkgo/v2"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
@@ -43,7 +44,7 @@ func newExampleRunner(name, namespace, configSecretName string) *v1alpha1.Epheme
|
|||||||
Spec: v1alpha1.EphemeralRunnerSpec{
|
Spec: v1alpha1.EphemeralRunnerSpec{
|
||||||
GitHubConfigUrl: "https://github.com/owner/repo",
|
GitHubConfigUrl: "https://github.com/owner/repo",
|
||||||
GitHubConfigSecret: configSecretName,
|
GitHubConfigSecret: configSecretName,
|
||||||
RunnerScaleSetId: 1,
|
RunnerScaleSetID: 1,
|
||||||
PodTemplateSpec: corev1.PodTemplateSpec{
|
PodTemplateSpec: corev1.PodTemplateSpec{
|
||||||
Spec: corev1.PodSpec{
|
Spec: corev1.PodSpec{
|
||||||
Containers: []corev1.Container{
|
Containers: []corev1.Container{
|
||||||
@@ -111,10 +112,19 @@ var _ = Describe("EphemeralRunner", func() {
|
|||||||
Scheme: mgr.GetScheme(),
|
Scheme: mgr.GetScheme(),
|
||||||
Log: logf.Log,
|
Log: logf.Log,
|
||||||
ResourceBuilder: ResourceBuilder{
|
ResourceBuilder: ResourceBuilder{
|
||||||
SecretResolver: &SecretResolver{
|
SecretResolver: secretresolver.New(mgr.GetClient(), scalefake.NewMultiClient(
|
||||||
k8sClient: mgr.GetClient(),
|
scalefake.WithClient(
|
||||||
multiClient: fake.NewMultiClient(),
|
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(),
|
Scheme: mgr.GetScheme(),
|
||||||
Log: logf.Log,
|
Log: logf.Log,
|
||||||
ResourceBuilder: ResourceBuilder{
|
ResourceBuilder: ResourceBuilder{
|
||||||
SecretResolver: &SecretResolver{
|
SecretResolver: secretresolver.New(
|
||||||
k8sClient: mgr.GetClient(),
|
mgr.GetClient(),
|
||||||
multiClient: fake.NewMultiClient(
|
scalefake.NewMultiClient(
|
||||||
fake.WithDefaultClient(
|
scalefake.WithClient(
|
||||||
fake.NewFakeClient(
|
scalefake.NewClient(
|
||||||
fake.WithGetRunner(
|
scalefake.WithGetRunner(
|
||||||
nil,
|
nil,
|
||||||
&actions.ActionsError{
|
&actions.ActionsError{
|
||||||
StatusCode: http.StatusNotFound,
|
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)
|
err := controller.SetupWithManager(mgr)
|
||||||
@@ -1181,10 +1197,19 @@ var _ = Describe("EphemeralRunner", func() {
|
|||||||
Scheme: mgr.GetScheme(),
|
Scheme: mgr.GetScheme(),
|
||||||
Log: logf.Log,
|
Log: logf.Log,
|
||||||
ResourceBuilder: ResourceBuilder{
|
ResourceBuilder: ResourceBuilder{
|
||||||
SecretResolver: &SecretResolver{
|
SecretResolver: secretresolver.New(mgr.GetClient(), scalefake.NewMultiClient(
|
||||||
k8sClient: mgr.GetClient(),
|
scalefake.WithClient(
|
||||||
multiClient: fake.NewMultiClient(),
|
scalefake.NewClient(
|
||||||
},
|
scalefake.WithGenerateJitRunnerConfig(
|
||||||
|
&scaleset.RunnerScaleSetJitRunnerConfig{
|
||||||
|
Runner: &scaleset.RunnerReference{ID: 1, Name: "test-runner"},
|
||||||
|
EncodedJITConfig: "fake-jit-config",
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err := controller.SetupWithManager(mgr)
|
err := controller.SetupWithManager(mgr)
|
||||||
@@ -1196,10 +1221,10 @@ var _ = Describe("EphemeralRunner", func() {
|
|||||||
It("uses an actions client with proxy transport", func() {
|
It("uses an actions client with proxy transport", func() {
|
||||||
// Use an actual client
|
// Use an actual client
|
||||||
controller.ResourceBuilder = ResourceBuilder{
|
controller.ResourceBuilder = ResourceBuilder{
|
||||||
SecretResolver: &SecretResolver{
|
SecretResolver: secretresolver.New(
|
||||||
k8sClient: mgr.GetClient(),
|
mgr.GetClient(),
|
||||||
multiClient: actions.NewMultiClient(logr.Discard()),
|
multiclient.NewScaleset(),
|
||||||
},
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
proxySuccessfulllyCalled := false
|
proxySuccessfulllyCalled := false
|
||||||
@@ -1355,10 +1380,7 @@ var _ = Describe("EphemeralRunner", func() {
|
|||||||
Scheme: mgr.GetScheme(),
|
Scheme: mgr.GetScheme(),
|
||||||
Log: logf.Log,
|
Log: logf.Log,
|
||||||
ResourceBuilder: ResourceBuilder{
|
ResourceBuilder: ResourceBuilder{
|
||||||
SecretResolver: &SecretResolver{
|
SecretResolver: secretresolver.New(mgr.GetClient(), scalefake.NewMultiClient()),
|
||||||
k8sClient: mgr.GetClient(),
|
|
||||||
multiClient: fake.NewMultiClient(),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1379,7 +1401,7 @@ var _ = Describe("EphemeralRunner", func() {
|
|||||||
keyPath := filepath.Join(certsFolder, "server.key")
|
keyPath := filepath.Join(certsFolder, "server.key")
|
||||||
|
|
||||||
serverSuccessfullyCalled := false
|
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
|
serverSuccessfullyCalled = true
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
}))
|
}))
|
||||||
@@ -1388,17 +1410,18 @@ var _ = Describe("EphemeralRunner", func() {
|
|||||||
|
|
||||||
server.TLS = &tls.Config{Certificates: []tls.Certificate{cert}}
|
server.TLS = &tls.Config{Certificates: []tls.Certificate{cert}}
|
||||||
server.StartTLS()
|
server.StartTLS()
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
// Use an actual client
|
// Use an actual client
|
||||||
controller.ResourceBuilder = ResourceBuilder{
|
controller.ResourceBuilder = ResourceBuilder{
|
||||||
SecretResolver: &SecretResolver{
|
SecretResolver: secretresolver.New(
|
||||||
k8sClient: mgr.GetClient(),
|
mgr.GetClient(),
|
||||||
multiClient: actions.NewMultiClient(logr.Discard()),
|
multiclient.NewScaleset(),
|
||||||
},
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
ephemeralRunner := newExampleRunner("test-runner", autoScalingNS.Name, configSecret.Name)
|
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{
|
ephemeralRunner.Spec.GitHubServerTLS = &v1alpha1.TLSConfig{
|
||||||
CertificateFrom: &v1alpha1.TLSCertificateSource{
|
CertificateFrom: &v1alpha1.TLSCertificateSource{
|
||||||
ConfigMapKeyRef: &corev1.ConfigMapKeySelector{
|
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/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/metrics"
|
||||||
|
"github.com/actions/actions-runner-controller/controllers/actions.github.com/multiclient"
|
||||||
"github.com/actions/actions-runner-controller/github/actions"
|
"github.com/actions/actions-runner-controller/github/actions"
|
||||||
"github.com/go-logr/logr"
|
"github.com/go-logr/logr"
|
||||||
"go.uber.org/multierr"
|
"go.uber.org/multierr"
|
||||||
@@ -481,7 +482,7 @@ func (r *EphemeralRunnerSetReconciler) deleteIdleEphemeralRunners(ctx context.Co
|
|||||||
return multierr.Combine(errs...)
|
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 {
|
if err := actionsClient.RemoveRunner(ctx, int64(ephemeralRunner.Status.RunnerId)); err != nil {
|
||||||
actionsError := &actions.ActionsError{}
|
actionsError := &actions.ActionsError{}
|
||||||
if !errors.As(err, &actionsError) {
|
if !errors.As(err, &actionsError) {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package actionsgithubcom
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -19,16 +18,15 @@ import (
|
|||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
logf "sigs.k8s.io/controller-runtime/pkg/log"
|
logf "sigs.k8s.io/controller-runtime/pkg/log"
|
||||||
|
|
||||||
"github.com/go-logr/logr"
|
|
||||||
. "github.com/onsi/ginkgo/v2"
|
. "github.com/onsi/ginkgo/v2"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
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/apis/actions.github.com/v1alpha1"
|
||||||
"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/github/actions/fake"
|
fake "github.com/actions/actions-runner-controller/controllers/actions.github.com/multiclient/fake"
|
||||||
"github.com/actions/actions-runner-controller/github/actions/testserver"
|
"github.com/actions/actions-runner-controller/controllers/actions.github.com/secretresolver"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -57,10 +55,13 @@ var _ = Describe("Test EphemeralRunnerSet controller", func() {
|
|||||||
Scheme: mgr.GetScheme(),
|
Scheme: mgr.GetScheme(),
|
||||||
Log: logf.Log,
|
Log: logf.Log,
|
||||||
ResourceBuilder: ResourceBuilder{
|
ResourceBuilder: ResourceBuilder{
|
||||||
SecretResolver: &SecretResolver{
|
SecretResolver: secretresolver.New(mgr.GetClient(), fake.NewMultiClient(
|
||||||
k8sClient: mgr.GetClient(),
|
fake.WithClient(
|
||||||
multiClient: fake.NewMultiClient(),
|
fake.NewClient(
|
||||||
},
|
fake.WithRemoveRunner(nil),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err := controller.SetupWithManager(mgr)
|
err := controller.SetupWithManager(mgr)
|
||||||
@@ -75,7 +76,7 @@ var _ = Describe("Test EphemeralRunnerSet controller", func() {
|
|||||||
EphemeralRunnerSpec: v1alpha1.EphemeralRunnerSpec{
|
EphemeralRunnerSpec: v1alpha1.EphemeralRunnerSpec{
|
||||||
GitHubConfigUrl: "https://github.com/owner/repo",
|
GitHubConfigUrl: "https://github.com/owner/repo",
|
||||||
GitHubConfigSecret: configSecret.Name,
|
GitHubConfigSecret: configSecret.Name,
|
||||||
RunnerScaleSetId: 100,
|
RunnerScaleSetID: 100,
|
||||||
PodTemplateSpec: corev1.PodTemplateSpec{
|
PodTemplateSpec: corev1.PodTemplateSpec{
|
||||||
Spec: corev1.PodSpec{
|
Spec: corev1.PodSpec{
|
||||||
Containers: []corev1.Container{
|
Containers: []corev1.Container{
|
||||||
@@ -1103,10 +1104,7 @@ var _ = Describe("Test EphemeralRunnerSet controller with proxy settings", func(
|
|||||||
Scheme: mgr.GetScheme(),
|
Scheme: mgr.GetScheme(),
|
||||||
Log: logf.Log,
|
Log: logf.Log,
|
||||||
ResourceBuilder: ResourceBuilder{
|
ResourceBuilder: ResourceBuilder{
|
||||||
SecretResolver: &SecretResolver{
|
SecretResolver: secretresolver.New(mgr.GetClient(), multiclient.NewScaleset()),
|
||||||
k8sClient: mgr.GetClient(),
|
|
||||||
multiClient: actions.NewMultiClient(logr.Discard()),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err := controller.SetupWithManager(mgr)
|
err := controller.SetupWithManager(mgr)
|
||||||
@@ -1140,7 +1138,7 @@ var _ = Describe("Test EphemeralRunnerSet controller with proxy settings", func(
|
|||||||
EphemeralRunnerSpec: v1alpha1.EphemeralRunnerSpec{
|
EphemeralRunnerSpec: v1alpha1.EphemeralRunnerSpec{
|
||||||
GitHubConfigUrl: "http://example.com/owner/repo",
|
GitHubConfigUrl: "http://example.com/owner/repo",
|
||||||
GitHubConfigSecret: configSecret.Name,
|
GitHubConfigSecret: configSecret.Name,
|
||||||
RunnerScaleSetId: 100,
|
RunnerScaleSetID: 100,
|
||||||
Proxy: &v1alpha1.ProxyConfig{
|
Proxy: &v1alpha1.ProxyConfig{
|
||||||
HTTP: &v1alpha1.ProxyServerConfig{
|
HTTP: &v1alpha1.ProxyServerConfig{
|
||||||
Url: "http://proxy.example.com",
|
Url: "http://proxy.example.com",
|
||||||
@@ -1319,7 +1317,7 @@ var _ = Describe("Test EphemeralRunnerSet controller with proxy settings", func(
|
|||||||
EphemeralRunnerSpec: v1alpha1.EphemeralRunnerSpec{
|
EphemeralRunnerSpec: v1alpha1.EphemeralRunnerSpec{
|
||||||
GitHubConfigUrl: "http://example.com/owner/repo",
|
GitHubConfigUrl: "http://example.com/owner/repo",
|
||||||
GitHubConfigSecret: configSecret.Name,
|
GitHubConfigSecret: configSecret.Name,
|
||||||
RunnerScaleSetId: 100,
|
RunnerScaleSetID: 100,
|
||||||
Proxy: &v1alpha1.ProxyConfig{
|
Proxy: &v1alpha1.ProxyConfig{
|
||||||
HTTP: &v1alpha1.ProxyServerConfig{
|
HTTP: &v1alpha1.ProxyServerConfig{
|
||||||
Url: proxy.URL,
|
Url: proxy.URL,
|
||||||
@@ -1419,10 +1417,7 @@ var _ = Describe("Test EphemeralRunnerSet controller with custom root CA", func(
|
|||||||
Scheme: mgr.GetScheme(),
|
Scheme: mgr.GetScheme(),
|
||||||
Log: logf.Log,
|
Log: logf.Log,
|
||||||
ResourceBuilder: ResourceBuilder{
|
ResourceBuilder: ResourceBuilder{
|
||||||
SecretResolver: &SecretResolver{
|
SecretResolver: secretresolver.New(mgr.GetClient(), multiclient.NewScaleset()),
|
||||||
k8sClient: mgr.GetClient(),
|
|
||||||
multiClient: actions.NewMultiClient(logr.Discard()),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err = controller.SetupWithManager(mgr)
|
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() {
|
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{
|
ephemeralRunnerSet = &v1alpha1.EphemeralRunnerSet{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "test-asrs",
|
Name: "test-asrs",
|
||||||
@@ -1460,7 +1435,7 @@ var _ = Describe("Test EphemeralRunnerSet controller with custom root CA", func(
|
|||||||
Spec: v1alpha1.EphemeralRunnerSetSpec{
|
Spec: v1alpha1.EphemeralRunnerSetSpec{
|
||||||
Replicas: 1,
|
Replicas: 1,
|
||||||
EphemeralRunnerSpec: v1alpha1.EphemeralRunnerSpec{
|
EphemeralRunnerSpec: v1alpha1.EphemeralRunnerSpec{
|
||||||
GitHubConfigUrl: server.ConfigURLForOrg("my-org"),
|
GitHubConfigUrl: "https://github.example.com/api/v3",
|
||||||
GitHubConfigSecret: configSecret.Name,
|
GitHubConfigSecret: configSecret.Name,
|
||||||
GitHubServerTLS: &v1alpha1.TLSConfig{
|
GitHubServerTLS: &v1alpha1.TLSConfig{
|
||||||
CertificateFrom: &v1alpha1.TLSCertificateSource{
|
CertificateFrom: &v1alpha1.TLSCertificateSource{
|
||||||
@@ -1472,7 +1447,7 @@ var _ = Describe("Test EphemeralRunnerSet controller with custom root CA", func(
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
RunnerScaleSetId: 100,
|
RunnerScaleSetID: 100,
|
||||||
PodTemplateSpec: corev1.PodTemplateSpec{
|
PodTemplateSpec: corev1.PodTemplateSpec{
|
||||||
Spec: corev1.PodSpec{
|
Spec: corev1.PodSpec{
|
||||||
Containers: []corev1.Container{
|
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")
|
Expect(err).NotTo(HaveOccurred(), "failed to create EphemeralRunnerSet")
|
||||||
|
|
||||||
runnerList := new(v1alpha1.EphemeralRunnerList)
|
runnerList := new(v1alpha1.EphemeralRunnerList)
|
||||||
@@ -1503,32 +1478,10 @@ var _ = Describe("Test EphemeralRunnerSet controller with custom root CA", func(
|
|||||||
ephemeralRunnerSetTestInterval,
|
ephemeralRunnerSetTestInterval,
|
||||||
).Should(BeEquivalentTo(1), "failed to create ephemeral runner")
|
).Should(BeEquivalentTo(1), "failed to create ephemeral runner")
|
||||||
|
|
||||||
|
// Verify that the TLS configuration is properly propagated to the runner
|
||||||
runner := runnerList.Items[0].DeepCopy()
|
runner := runnerList.Items[0].DeepCopy()
|
||||||
Expect(runner.Spec.GitHubServerTLS).NotTo(BeNil(), "runner tls config should not be nil")
|
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")
|
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")
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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 (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"maps"
|
"maps"
|
||||||
@@ -14,10 +15,13 @@ import (
|
|||||||
"github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1/appconfig"
|
"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/build"
|
||||||
ghalistenerconfig "github.com/actions/actions-runner-controller/cmd/ghalistener/config"
|
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/github/actions"
|
||||||
"github.com/actions/actions-runner-controller/hash"
|
"github.com/actions/actions-runner-controller/hash"
|
||||||
"github.com/actions/actions-runner-controller/logging"
|
"github.com/actions/actions-runner-controller/logging"
|
||||||
"github.com/actions/actions-runner-controller/vault/azurekeyvault"
|
"github.com/actions/actions-runner-controller/vault/azurekeyvault"
|
||||||
|
"github.com/actions/scaleset"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
rbacv1 "k8s.io/api/rbac/v1"
|
rbacv1 "k8s.io/api/rbac/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
@@ -71,14 +75,14 @@ func SetListenerEntrypoint(entrypoint string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type ResourceBuilder struct {
|
type SecretResolver interface {
|
||||||
ExcludeLabelPropagationPrefixes []string
|
GetAppConfig(ctx context.Context, obj object.ActionsGitHubObject) (*appconfig.AppConfig, error)
|
||||||
*SecretResolver
|
GetActionsService(ctx context.Context, obj object.ActionsGitHubObject) (multiclient.Client, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// boolPtr returns a pointer to a bool value
|
type ResourceBuilder struct {
|
||||||
func boolPtr(v bool) *bool {
|
ExcludeLabelPropagationPrefixes []string
|
||||||
return &v
|
SecretResolver
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *ResourceBuilder) newAutoScalingListener(autoscalingRunnerSet *v1alpha1.AutoscalingRunnerSet, ephemeralRunnerSet *v1alpha1.EphemeralRunnerSet, namespace, image string, imagePullSecrets []corev1.LocalObjectReference) (*v1alpha1.AutoscalingListener, error) {
|
func (b *ResourceBuilder) newAutoScalingListener(autoscalingRunnerSet *v1alpha1.AutoscalingRunnerSet, ephemeralRunnerSet *v1alpha1.EphemeralRunnerSet, namespace, image string, imagePullSecrets []corev1.LocalObjectReference) (*v1alpha1.AutoscalingListener, error) {
|
||||||
@@ -183,7 +187,7 @@ func (b *ResourceBuilder) newScaleSetListenerConfig(autoscalingListener *v1alpha
|
|||||||
}
|
}
|
||||||
|
|
||||||
config := ghalistenerconfig.Config{
|
config := ghalistenerconfig.Config{
|
||||||
ConfigureUrl: autoscalingListener.Spec.GitHubConfigUrl,
|
ConfigureURL: autoscalingListener.Spec.GitHubConfigUrl,
|
||||||
EphemeralRunnerSetNamespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace,
|
EphemeralRunnerSetNamespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace,
|
||||||
EphemeralRunnerSetName: autoscalingListener.Spec.EphemeralRunnerSetName,
|
EphemeralRunnerSetName: autoscalingListener.Spec.EphemeralRunnerSetName,
|
||||||
MaxRunners: autoscalingListener.Spec.MaxRunners,
|
MaxRunners: autoscalingListener.Spec.MaxRunners,
|
||||||
@@ -319,8 +323,8 @@ func (b *ResourceBuilder) newScaleSetListenerPod(autoscalingListener *v1alpha1.A
|
|||||||
Kind: autoscalingListener.GetObjectKind().GroupVersionKind().Kind,
|
Kind: autoscalingListener.GetObjectKind().GroupVersionKind().Kind,
|
||||||
UID: autoscalingListener.GetUID(),
|
UID: autoscalingListener.GetUID(),
|
||||||
Name: autoscalingListener.GetName(),
|
Name: autoscalingListener.GetName(),
|
||||||
Controller: boolPtr(true),
|
Controller: new(true),
|
||||||
BlockOwnerDeletion: boolPtr(true),
|
BlockOwnerDeletion: new(true),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -591,15 +595,15 @@ func (b *ResourceBuilder) newEphemeralRunnerSet(autoscalingRunnerSet *v1alpha1.A
|
|||||||
Kind: autoscalingRunnerSet.GetObjectKind().GroupVersionKind().Kind,
|
Kind: autoscalingRunnerSet.GetObjectKind().GroupVersionKind().Kind,
|
||||||
UID: autoscalingRunnerSet.GetUID(),
|
UID: autoscalingRunnerSet.GetUID(),
|
||||||
Name: autoscalingRunnerSet.GetName(),
|
Name: autoscalingRunnerSet.GetName(),
|
||||||
Controller: boolPtr(true),
|
Controller: new(true),
|
||||||
BlockOwnerDeletion: boolPtr(true),
|
BlockOwnerDeletion: new(true),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Spec: v1alpha1.EphemeralRunnerSetSpec{
|
Spec: v1alpha1.EphemeralRunnerSetSpec{
|
||||||
Replicas: 0,
|
Replicas: 0,
|
||||||
EphemeralRunnerSpec: v1alpha1.EphemeralRunnerSpec{
|
EphemeralRunnerSpec: v1alpha1.EphemeralRunnerSpec{
|
||||||
RunnerScaleSetId: runnerScaleSetID,
|
RunnerScaleSetID: runnerScaleSetID,
|
||||||
GitHubConfigUrl: autoscalingRunnerSet.Spec.GitHubConfigUrl,
|
GitHubConfigUrl: autoscalingRunnerSet.Spec.GitHubConfigUrl,
|
||||||
GitHubConfigSecret: autoscalingRunnerSet.Spec.GitHubConfigSecret,
|
GitHubConfigSecret: autoscalingRunnerSet.Spec.GitHubConfigSecret,
|
||||||
Proxy: autoscalingRunnerSet.Spec.Proxy,
|
Proxy: autoscalingRunnerSet.Spec.Proxy,
|
||||||
@@ -645,8 +649,8 @@ func (b *ResourceBuilder) newEphemeralRunner(ephemeralRunnerSet *v1alpha1.Epheme
|
|||||||
Kind: ephemeralRunnerSet.GetObjectKind().GroupVersionKind().Kind,
|
Kind: ephemeralRunnerSet.GetObjectKind().GroupVersionKind().Kind,
|
||||||
UID: ephemeralRunnerSet.GetUID(),
|
UID: ephemeralRunnerSet.GetUID(),
|
||||||
Name: ephemeralRunnerSet.GetName(),
|
Name: ephemeralRunnerSet.GetName(),
|
||||||
Controller: boolPtr(true),
|
Controller: new(true),
|
||||||
BlockOwnerDeletion: boolPtr(true),
|
BlockOwnerDeletion: new(true),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -683,8 +687,8 @@ func (b *ResourceBuilder) newEphemeralRunnerPod(runner *v1alpha1.EphemeralRunner
|
|||||||
Kind: runner.GetObjectKind().GroupVersionKind().Kind,
|
Kind: runner.GetObjectKind().GroupVersionKind().Kind,
|
||||||
UID: runner.GetUID(),
|
UID: runner.GetUID(),
|
||||||
Name: runner.GetName(),
|
Name: runner.GetName(),
|
||||||
Controller: boolPtr(true),
|
Controller: new(true),
|
||||||
BlockOwnerDeletion: boolPtr(true),
|
BlockOwnerDeletion: new(true),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -722,7 +726,7 @@ func (b *ResourceBuilder) newEphemeralRunnerPod(runner *v1alpha1.EphemeralRunner
|
|||||||
return &newPod
|
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 (
|
var (
|
||||||
labels map[string]string
|
labels map[string]string
|
||||||
annotations map[string]string
|
annotations map[string]string
|
||||||
@@ -743,8 +747,8 @@ func (b *ResourceBuilder) newEphemeralRunnerJitSecret(ephemeralRunner *v1alpha1.
|
|||||||
Data: map[string][]byte{
|
Data: map[string][]byte{
|
||||||
jitTokenKey: []byte(jitConfig.EncodedJITConfig),
|
jitTokenKey: []byte(jitConfig.EncodedJITConfig),
|
||||||
"runnerName": []byte(jitConfig.Runner.Name),
|
"runnerName": []byte(jitConfig.Runner.Name),
|
||||||
"runnerId": []byte(strconv.Itoa(jitConfig.Runner.Id)),
|
"runnerId": []byte(strconv.Itoa(jitConfig.Runner.ID)),
|
||||||
"scaleSetId": []byte(strconv.Itoa(jitConfig.Runner.RunnerScaleSetId)),
|
"scaleSetId": []byte(strconv.Itoa(jitConfig.Runner.RunnerScaleSetID)),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1"
|
"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/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
@@ -176,11 +176,11 @@ func TestMetadataPropagation(t *testing.T) {
|
|||||||
assert.Equal(t, "ephemeral-runner-label", ephemeralRunner.Labels["test.com/ephemeral-runner-label"])
|
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"])
|
assert.Equal(t, "ephemeral-runner-annotation", ephemeralRunner.Annotations["test.com/ephemeral-runner-annotation"])
|
||||||
|
|
||||||
runnerSecret := b.newEphemeralRunnerJitSecret(ephemeralRunner, &actions.RunnerScaleSetJitRunnerConfig{
|
runnerSecret := b.newEphemeralRunnerJitSecret(ephemeralRunner, &scaleset.RunnerScaleSetJitRunnerConfig{
|
||||||
Runner: &actions.RunnerReference{
|
Runner: &scaleset.RunnerReference{
|
||||||
Id: 1,
|
ID: 1,
|
||||||
Name: "test",
|
Name: "test",
|
||||||
RunnerScaleSetId: 1,
|
RunnerScaleSetID: 1,
|
||||||
},
|
},
|
||||||
EncodedJITConfig: "",
|
EncodedJITConfig: "",
|
||||||
})
|
})
|
||||||
|
|||||||
+33
-32
@@ -1,16 +1,18 @@
|
|||||||
package actionsgithubcom
|
package secretresolver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/x509"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"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/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"
|
||||||
"github.com/actions/actions-runner-controller/vault/azurekeyvault"
|
"github.com/actions/actions-runner-controller/vault/azurekeyvault"
|
||||||
"golang.org/x/net/http/httpproxy"
|
"golang.org/x/net/http/httpproxy"
|
||||||
@@ -21,19 +23,27 @@ import (
|
|||||||
|
|
||||||
type SecretResolver struct {
|
type SecretResolver struct {
|
||||||
k8sClient client.Client
|
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 {
|
if k8sClient == nil {
|
||||||
panic("k8sClient must not be nil")
|
panic("k8sClient must not be nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
secretResolver := &SecretResolver{
|
secretResolver := &SecretResolver{
|
||||||
k8sClient: k8sClient,
|
k8sClient: k8sClient,
|
||||||
multiClient: multiClient,
|
multiClient: scalesetMultiClient,
|
||||||
|
logger: slog.New(slog.DiscardHandler),
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
@@ -43,17 +53,7 @@ func NewSecretResolver(k8sClient client.Client, multiClient actions.MultiClient,
|
|||||||
return secretResolver
|
return secretResolver
|
||||||
}
|
}
|
||||||
|
|
||||||
type ActionsGitHubObject interface {
|
func (sr *SecretResolver) GetAppConfig(ctx context.Context, obj object.ActionsGitHubObject) (*appconfig.AppConfig, error) {
|
||||||
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) {
|
|
||||||
resolver, err := sr.resolverForObject(ctx, obj)
|
resolver, err := sr.resolverForObject(ctx, obj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to get resolver for object: %v", err)
|
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
|
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)
|
resolver, err := sr.resolverForObject(ctx, obj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to get resolver for object: %v", err)
|
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)
|
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 {
|
if proxy := obj.GitHubProxy(); proxy != nil {
|
||||||
config := &httpproxy.Config{
|
config := &httpproxy.Config{
|
||||||
NoProxy: strings.Join(proxy.NoProxy, ","),
|
NoProxy: strings.Join(proxy.NoProxy, ","),
|
||||||
@@ -116,16 +116,14 @@ func (sr *SecretResolver) GetActionsService(ctx context.Context, obj ActionsGitH
|
|||||||
config.HTTPSProxy = u.String()
|
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)
|
return config.ProxyFunc()(req.URL)
|
||||||
}
|
}
|
||||||
|
|
||||||
clientOptions = append(clientOptions, actions.WithProxy(proxyFunc))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tlsConfig := obj.GitHubServerTLS()
|
var rootCAs *x509.CertPool
|
||||||
if tlsConfig != nil {
|
if tc := obj.GitHubServerTLS(); tc != nil {
|
||||||
pool, err := tlsConfig.ToCertPool(func(name, key string) ([]byte, error) {
|
pool, err := tc.ToCertPool(func(name, key string) ([]byte, error) {
|
||||||
var configmap corev1.ConfigMap
|
var configmap corev1.ConfigMap
|
||||||
err := sr.k8sClient.Get(
|
err := sr.k8sClient.Get(
|
||||||
ctx,
|
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)
|
return nil, fmt.Errorf("failed to get tls config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
clientOptions = append(clientOptions, actions.WithRootCAs(pool))
|
rootCAs = pool
|
||||||
}
|
}
|
||||||
|
|
||||||
return sr.multiClient.GetClientFor(
|
return sr.multiClient.GetClientFor(
|
||||||
ctx,
|
ctx,
|
||||||
obj.GitHubConfigUrl(),
|
&multiclient.ClientForOptions{
|
||||||
appConfig,
|
GithubConfigURL: obj.GitHubConfigUrl(),
|
||||||
obj.GetNamespace(),
|
AppConfig: *appConfig,
|
||||||
clientOptions...,
|
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()
|
vaultConfig := obj.VaultConfig()
|
||||||
if vaultConfig == nil || vaultConfig.Type == "" {
|
if vaultConfig == nil || vaultConfig.Type == "" {
|
||||||
return &k8sResolver{
|
return &k8sResolver{
|
||||||
@@ -6,7 +6,7 @@ require (
|
|||||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0
|
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/azidentity v1.13.1
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets v1.4.0
|
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/bradleyfalzon/ghinstallation/v2 v2.17.0
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
|
||||||
github.com/evanphx/json-patch v5.9.11+incompatible
|
github.com/evanphx/json-patch v5.9.11+incompatible
|
||||||
|
|||||||
@@ -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/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 h1:hCNvYuVPJ2xxYBymqBvH0hSiQpqz4PHF/LbU3XghGNI=
|
||||||
github.com/actions-runner-controller/httpcache v0.2.0/go.mod h1:JLu9/2M/btPz1Zu/vTZ71XzukQHn2YeISPmJoM5exBI=
|
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.2.0 h1:CKsDtTjOBCwjyT4ikwiMykMttzuKejimWRAvVr8xj9w=
|
||||||
github.com/actions/scaleset v0.1.1-0.20260218224657-feb84c6d04fb/go.mod h1:ncR5vzCCTUSyLgvclAtZ5dRBgF6qwA2nbTfTXmOJp84=
|
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 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
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=
|
github.com/aws/aws-sdk-go-v2 v1.39.2 h1:EJLg8IdbzgeD7xgvZ+I8M1e0fL0ptn/M47lianzth0I=
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
+9
-12
@@ -22,21 +22,18 @@ const (
|
|||||||
LogFormatJSON = "json"
|
LogFormatJSON = "json"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var LogOpts = zap.Options{
|
||||||
LogOpts = zap.Options{
|
TimeEncoder: zapcore.TimeEncoderOfLayout(time.RFC3339),
|
||||||
TimeEncoder: zapcore.TimeEncoderOfLayout(time.RFC3339),
|
Development: true,
|
||||||
Development: true,
|
EncoderConfigOptions: []zap.EncoderConfigOption{
|
||||||
EncoderConfigOptions: []zap.EncoderConfigOption{
|
func(ec *zapcore.EncoderConfig) {
|
||||||
func(ec *zapcore.EncoderConfig) {
|
ec.LevelKey = "severity"
|
||||||
ec.LevelKey = "severity"
|
ec.MessageKey = "message"
|
||||||
ec.MessageKey = "message"
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
)
|
}
|
||||||
|
|
||||||
func NewLogger(logLevel string, logFormat string) (logr.Logger, error) {
|
func NewLogger(logLevel string, logFormat string) (logr.Logger, error) {
|
||||||
|
|
||||||
if !validLogFormat(logFormat) {
|
if !validLogFormat(logFormat) {
|
||||||
return logr.Logger{}, errors.New("invalid log format specified")
|
return logr.Logger{}, errors.New("invalid log format specified")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,9 +28,11 @@ import (
|
|||||||
"github.com/actions/actions-runner-controller/build"
|
"github.com/actions/actions-runner-controller/build"
|
||||||
actionsgithubcom "github.com/actions/actions-runner-controller/controllers/actions.github.com"
|
actionsgithubcom "github.com/actions/actions-runner-controller/controllers/actions.github.com"
|
||||||
actionsgithubcommetrics "github.com/actions/actions-runner-controller/controllers/actions.github.com/metrics"
|
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"
|
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"
|
||||||
"github.com/actions/actions-runner-controller/github/actions"
|
"github.com/actions/actions-runner-controller/logger"
|
||||||
"github.com/actions/actions-runner-controller/logging"
|
"github.com/actions/actions-runner-controller/logging"
|
||||||
"github.com/kelseyhightower/envconfig"
|
"github.com/kelseyhightower/envconfig"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
@@ -85,7 +87,7 @@ func main() {
|
|||||||
enableLeaderElection bool
|
enableLeaderElection bool
|
||||||
disableAdmissionWebhook bool
|
disableAdmissionWebhook bool
|
||||||
updateStrategy string
|
updateStrategy string
|
||||||
leaderElectionId string
|
leaderElectionID string
|
||||||
port int
|
port int
|
||||||
syncPeriod time.Duration
|
syncPeriod time.Duration
|
||||||
|
|
||||||
@@ -121,7 +123,7 @@ func main() {
|
|||||||
flag.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.")
|
flag.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.")
|
||||||
flag.BoolVar(&enableLeaderElection, "enable-leader-election", false,
|
flag.BoolVar(&enableLeaderElection, "enable-leader-election", false,
|
||||||
"Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager.")
|
"Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager.")
|
||||||
flag.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.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.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.")
|
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,
|
WebhookServer: webhookServer,
|
||||||
LeaderElection: enableLeaderElection,
|
LeaderElection: enableLeaderElection,
|
||||||
LeaderElectionID: leaderElectionId,
|
LeaderElectionID: leaderElectionID,
|
||||||
Client: client.Options{
|
Client: client.Options{
|
||||||
Cache: &client.CacheOptions{
|
Cache: &client.CacheOptions{
|
||||||
DisableFor: []client.Object{
|
DisableFor: []client.Object{
|
||||||
@@ -270,13 +272,18 @@ func main() {
|
|||||||
actionsgithubcommetrics.RegisterMetrics()
|
actionsgithubcommetrics.RegisterMetrics()
|
||||||
}
|
}
|
||||||
|
|
||||||
actionsMultiClient := actions.NewMultiClient(
|
slogLogger, err := logger.New(logLevel, logFormat)
|
||||||
log.WithName("actions-clients"),
|
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(),
|
mgr.GetClient(),
|
||||||
actionsMultiClient,
|
scalesetMultiClient,
|
||||||
|
secretresolver.WithLogger(slogLogger),
|
||||||
)
|
)
|
||||||
|
|
||||||
rb := actionsgithubcom.ResourceBuilder{
|
rb := actionsgithubcom.ResourceBuilder{
|
||||||
@@ -292,7 +299,6 @@ func main() {
|
|||||||
Scheme: mgr.GetScheme(),
|
Scheme: mgr.GetScheme(),
|
||||||
ControllerNamespace: managerNamespace,
|
ControllerNamespace: managerNamespace,
|
||||||
DefaultRunnerScaleSetListenerImage: managerImage,
|
DefaultRunnerScaleSetListenerImage: managerImage,
|
||||||
ActionsClient: actionsMultiClient,
|
|
||||||
UpdateStrategy: actionsgithubcom.UpdateStrategy(updateStrategy),
|
UpdateStrategy: actionsgithubcom.UpdateStrategy(updateStrategy),
|
||||||
DefaultRunnerScaleSetListenerImagePullSecrets: autoScalerImagePullSecrets,
|
DefaultRunnerScaleSetListenerImagePullSecrets: autoScalerImagePullSecrets,
|
||||||
ResourceBuilder: rb,
|
ResourceBuilder: rb,
|
||||||
@@ -399,7 +405,7 @@ func main() {
|
|||||||
"default-docker-gid", runnerPodDefaults.DockerGID,
|
"default-docker-gid", runnerPodDefaults.DockerGID,
|
||||||
"common-runnner-labels", commonRunnerLabels,
|
"common-runnner-labels", commonRunnerLabels,
|
||||||
"leader-election-enabled", enableLeaderElection,
|
"leader-election-enabled", enableLeaderElection,
|
||||||
"leader-election-id", leaderElectionId,
|
"leader-election-id", leaderElectionID,
|
||||||
"watch-namespace", namespace,
|
"watch-namespace", namespace,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -489,7 +495,7 @@ func (s *commaSeparatedStringSlice) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *commaSeparatedStringSlice) Set(value string) error {
|
func (s *commaSeparatedStringSlice) Set(value string) error {
|
||||||
for _, v := range strings.Split(value, ",") {
|
for v := range strings.SplitSeq(value, ",") {
|
||||||
if v == "" {
|
if v == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user