Implement basic resolver for actions client in resource builder

This commit is contained in:
Nikola Jokic 2025-01-14 17:33:32 +01:00
parent 27b292bdd3
commit 3e97c05e0f
No known key found for this signature in database
GPG Key ID: E4104494F9B8DDF6
12 changed files with 501 additions and 185 deletions

View File

@ -0,0 +1,248 @@
package actionsgithubcom
import (
"context"
"fmt"
"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/vault"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
)
type ActionsClientGetter interface {
GetActionsClientForAutoscalingRunnerSet(ctx context.Context, ars *v1alpha1.AutoscalingRunnerSet) (actions.ActionsService, error)
GetActionsClientForEphemeralRunnerSet(ctx context.Context, ers *v1alpha1.EphemeralRunnerSet) (actions.ActionsService, error)
GetActionsClientForEphemeralRunner(ctx context.Context, er *v1alpha1.EphemeralRunner) (actions.ActionsService, error)
}
var (
_ ActionsClientGetter = (*ActionsClientSecretResolver)(nil)
_ ActionsClientGetter = (*ActionsClientVaultResolver)(nil)
)
type ActionsClientSecretResolver struct {
client.Client
actions.MultiClient
}
func (r *ActionsClientSecretResolver) GetActionsClientForAutoscalingRunnerSet(ctx context.Context, ars *v1alpha1.AutoscalingRunnerSet) (actions.ActionsService, error) {
var configSecret corev1.Secret
if err := r.Get(ctx, types.NamespacedName{Namespace: ars.Namespace, Name: ars.Spec.GitHubConfigSecret}, &configSecret); err != nil {
return nil, fmt.Errorf("failed to find GitHub config secret: %w", err)
}
opts, err := r.actionsClientOptionsForAutoscalingRunnerSet(ctx, ars)
if err != nil {
return nil, fmt.Errorf("failed to get actions client options: %w", err)
}
return r.MultiClient.GetClientFromSecret(
ctx,
ars.Spec.GitHubConfigUrl,
ars.Namespace,
configSecret.Data,
opts...,
)
}
func (r *ActionsClientSecretResolver) actionsClientOptionsForAutoscalingRunnerSet(ctx context.Context, autoscalingRunnerSet *v1alpha1.AutoscalingRunnerSet) ([]actions.ClientOption, error) {
var options []actions.ClientOption
if autoscalingRunnerSet.Spec.Proxy != nil {
proxyFunc, err := autoscalingRunnerSet.Spec.Proxy.ProxyFunc(func(s string) (*corev1.Secret, error) {
var secret corev1.Secret
err := r.Get(ctx, types.NamespacedName{Namespace: autoscalingRunnerSet.Namespace, Name: s}, &secret)
if err != nil {
return nil, fmt.Errorf("failed to get proxy secret %s: %w", s, err)
}
return &secret, nil
})
if err != nil {
return nil, fmt.Errorf("failed to get proxy func: %w", err)
}
options = append(options, actions.WithProxy(proxyFunc))
}
tlsConfig := autoscalingRunnerSet.Spec.GitHubServerTLS
if tlsConfig != nil {
pool, err := tlsConfig.ToCertPool(func(name, key string) ([]byte, error) {
var configmap corev1.ConfigMap
err := r.Get(
ctx,
types.NamespacedName{
Namespace: autoscalingRunnerSet.Namespace,
Name: name,
},
&configmap,
)
if err != nil {
return nil, fmt.Errorf("failed to get configmap %s: %w", name, err)
}
return []byte(configmap.Data[key]), nil
})
if err != nil {
return nil, fmt.Errorf("failed to get tls config: %w", err)
}
options = append(options, actions.WithRootCAs(pool))
}
return options, nil
}
func (r *ActionsClientSecretResolver) GetActionsClientForEphemeralRunnerSet(ctx context.Context, rs *v1alpha1.EphemeralRunnerSet) (actions.ActionsService, error) {
secret := new(corev1.Secret)
if err := r.Get(ctx, types.NamespacedName{Namespace: rs.Namespace, Name: rs.Spec.EphemeralRunnerSpec.GitHubConfigSecret}, secret); err != nil {
return nil, fmt.Errorf("failed to get secret: %w", err)
}
opts, err := r.actionsClientOptionsForEphemeralRunnerSet(ctx, rs)
if err != nil {
return nil, fmt.Errorf("failed to get actions client options: %w", err)
}
return r.MultiClient.GetClientFromSecret(
ctx,
rs.Spec.EphemeralRunnerSpec.GitHubConfigUrl,
rs.Namespace,
secret.Data,
opts...,
)
}
func (r *ActionsClientSecretResolver) actionsClientOptionsForEphemeralRunnerSet(ctx context.Context, rs *v1alpha1.EphemeralRunnerSet) ([]actions.ClientOption, error) {
var opts []actions.ClientOption
if rs.Spec.EphemeralRunnerSpec.Proxy != nil {
proxyFunc, err := rs.Spec.EphemeralRunnerSpec.Proxy.ProxyFunc(func(s string) (*corev1.Secret, error) {
var secret corev1.Secret
err := r.Get(ctx, types.NamespacedName{Namespace: rs.Namespace, Name: s}, &secret)
if err != nil {
return nil, fmt.Errorf("failed to get secret %s: %w", s, err)
}
return &secret, nil
})
if err != nil {
return nil, fmt.Errorf("failed to get proxy func: %w", err)
}
opts = append(opts, actions.WithProxy(proxyFunc))
}
tlsConfig := rs.Spec.EphemeralRunnerSpec.GitHubServerTLS
if tlsConfig != nil {
pool, err := tlsConfig.ToCertPool(func(name, key string) ([]byte, error) {
var configmap corev1.ConfigMap
err := r.Get(
ctx,
types.NamespacedName{
Namespace: rs.Namespace,
Name: name,
},
&configmap,
)
if err != nil {
return nil, fmt.Errorf("failed to get configmap %s: %w", name, err)
}
return []byte(configmap.Data[key]), nil
})
if err != nil {
return nil, fmt.Errorf("failed to get tls config: %w", err)
}
opts = append(opts, actions.WithRootCAs(pool))
}
return opts, nil
}
func (r *ActionsClientSecretResolver) GetActionsClientForEphemeralRunner(ctx context.Context, runner *v1alpha1.EphemeralRunner) (actions.ActionsService, error) {
secret := new(corev1.Secret)
if err := r.Get(ctx, types.NamespacedName{Namespace: runner.Namespace, Name: runner.Spec.GitHubConfigSecret}, secret); err != nil {
return nil, fmt.Errorf("failed to get secret: %w", err)
}
opts, err := r.actionsClientOptionsForEphemeralRunner(ctx, runner)
if err != nil {
return nil, fmt.Errorf("failed to get actions client options: %w", err)
}
return r.MultiClient.GetClientFromSecret(
ctx,
runner.Spec.GitHubConfigUrl,
runner.Namespace,
secret.Data,
opts...,
)
}
func (r *ActionsClientSecretResolver) actionsClientOptionsForEphemeralRunner(ctx context.Context, runner *v1alpha1.EphemeralRunner) ([]actions.ClientOption, error) {
var opts []actions.ClientOption
if runner.Spec.Proxy != nil {
proxyFunc, err := runner.Spec.Proxy.ProxyFunc(func(s string) (*corev1.Secret, error) {
var secret corev1.Secret
err := r.Get(ctx, types.NamespacedName{Namespace: runner.Namespace, Name: s}, &secret)
if err != nil {
return nil, fmt.Errorf("failed to get proxy secret %s: %w", s, err)
}
return &secret, nil
})
if err != nil {
return nil, fmt.Errorf("failed to get proxy func: %w", err)
}
opts = append(opts, actions.WithProxy(proxyFunc))
}
tlsConfig := runner.Spec.GitHubServerTLS
if tlsConfig != nil {
pool, err := tlsConfig.ToCertPool(func(name, key string) ([]byte, error) {
var configmap corev1.ConfigMap
err := r.Get(
ctx,
types.NamespacedName{
Namespace: runner.Namespace,
Name: name,
},
&configmap,
)
if err != nil {
return nil, fmt.Errorf("failed to get configmap %s: %w", name, err)
}
return []byte(configmap.Data[key]), nil
})
if err != nil {
return nil, fmt.Errorf("failed to get tls config: %w", err)
}
opts = append(opts, actions.WithRootCAs(pool))
}
return opts, nil
}
type ActionsClientVaultResolver struct {
vault.Vault
actions.MultiClient
}
func (r *ActionsClientVaultResolver) GetActionsClientForAutoscalingRunnerSet(ctx context.Context, ars *v1alpha1.AutoscalingRunnerSet) (actions.ActionsService, error) {
panic("todo")
}
func (r *ActionsClientVaultResolver) GetActionsClientForEphemeralRunnerSet(ctx context.Context, ers *v1alpha1.EphemeralRunnerSet) (actions.ActionsService, error) {
panic("todo")
}
func (r *ActionsClientVaultResolver) GetActionsClientForEphemeralRunner(ctx context.Context, er *v1alpha1.EphemeralRunner) (actions.ActionsService, error) {
panic("todo")
}

View File

@ -402,7 +402,7 @@ func (r *AutoscalingRunnerSetReconciler) removeFinalizersFromDependentResources(
func (r *AutoscalingRunnerSetReconciler) createRunnerScaleSet(ctx context.Context, autoscalingRunnerSet *v1alpha1.AutoscalingRunnerSet, logger logr.Logger) (ctrl.Result, error) { func (r *AutoscalingRunnerSetReconciler) createRunnerScaleSet(ctx context.Context, autoscalingRunnerSet *v1alpha1.AutoscalingRunnerSet, logger logr.Logger) (ctrl.Result, error) {
logger.Info("Creating a new runner scale set") logger.Info("Creating a new runner scale set")
actionsClient, err := r.actionsClientFor(ctx, autoscalingRunnerSet) actionsClient, err := r.ActionsClientGetter.GetActionsClientForAutoscalingRunnerSet(ctx, autoscalingRunnerSet)
if len(autoscalingRunnerSet.Spec.RunnerScaleSetName) == 0 { if len(autoscalingRunnerSet.Spec.RunnerScaleSetName) == 0 {
autoscalingRunnerSet.Spec.RunnerScaleSetName = autoscalingRunnerSet.Name autoscalingRunnerSet.Spec.RunnerScaleSetName = autoscalingRunnerSet.Name
} }
@ -498,7 +498,7 @@ func (r *AutoscalingRunnerSetReconciler) updateRunnerScaleSetRunnerGroup(ctx con
return ctrl.Result{}, err return ctrl.Result{}, err
} }
actionsClient, err := r.actionsClientFor(ctx, autoscalingRunnerSet) actionsClient, err := r.ActionsClientGetter.GetActionsClientForAutoscalingRunnerSet(ctx, autoscalingRunnerSet)
if err != nil { if err != nil {
logger.Error(err, "Failed to initialize Actions service client for updating a existing runner scale set") logger.Error(err, "Failed to initialize Actions service client for updating a existing runner scale set")
return ctrl.Result{}, err return ctrl.Result{}, err
@ -546,7 +546,7 @@ func (r *AutoscalingRunnerSetReconciler) updateRunnerScaleSetName(ctx context.Co
return ctrl.Result{}, nil return ctrl.Result{}, nil
} }
actionsClient, err := r.actionsClientFor(ctx, autoscalingRunnerSet) actionsClient, err := r.ActionsClientGetter.GetActionsClientForAutoscalingRunnerSet(ctx, autoscalingRunnerSet)
if err != nil { if err != nil {
logger.Error(err, "Failed to initialize Actions service client for updating a existing runner scale set") logger.Error(err, "Failed to initialize Actions service client for updating a existing runner scale set")
return ctrl.Result{}, err return ctrl.Result{}, err
@ -597,7 +597,7 @@ func (r *AutoscalingRunnerSetReconciler) deleteRunnerScaleSet(ctx context.Contex
return nil return nil
} }
actionsClient, err := r.actionsClientFor(ctx, autoscalingRunnerSet) actionsClient, err := r.ActionsClientGetter.GetActionsClientForAutoscalingRunnerSet(ctx, autoscalingRunnerSet)
if err != nil { if err != nil {
logger.Error(err, "Failed to initialize Actions service client for updating a existing runner scale set") logger.Error(err, "Failed to initialize Actions service client for updating a existing runner scale set")
return err return err
@ -676,74 +676,6 @@ func (r *AutoscalingRunnerSetReconciler) listEphemeralRunnerSets(ctx context.Con
return &EphemeralRunnerSets{list: list}, nil return &EphemeralRunnerSets{list: list}, nil
} }
func (r *AutoscalingRunnerSetReconciler) actionsClientFor(ctx context.Context, autoscalingRunnerSet *v1alpha1.AutoscalingRunnerSet) (actions.ActionsService, error) {
var configSecret corev1.Secret
if err := r.Get(ctx, types.NamespacedName{Namespace: autoscalingRunnerSet.Namespace, Name: autoscalingRunnerSet.Spec.GitHubConfigSecret}, &configSecret); err != nil {
return nil, fmt.Errorf("failed to find GitHub config secret: %w", err)
}
opts, err := r.actionsClientOptionsFor(ctx, autoscalingRunnerSet)
if err != nil {
return nil, fmt.Errorf("failed to get actions client options: %w", err)
}
return r.ActionsClient.GetClientFromSecret(
ctx,
autoscalingRunnerSet.Spec.GitHubConfigUrl,
autoscalingRunnerSet.Namespace,
configSecret.Data,
opts...,
)
}
func (r *AutoscalingRunnerSetReconciler) actionsClientOptionsFor(ctx context.Context, autoscalingRunnerSet *v1alpha1.AutoscalingRunnerSet) ([]actions.ClientOption, error) {
var options []actions.ClientOption
if autoscalingRunnerSet.Spec.Proxy != nil {
proxyFunc, err := autoscalingRunnerSet.Spec.Proxy.ProxyFunc(func(s string) (*corev1.Secret, error) {
var secret corev1.Secret
err := r.Get(ctx, types.NamespacedName{Namespace: autoscalingRunnerSet.Namespace, Name: s}, &secret)
if err != nil {
return nil, fmt.Errorf("failed to get proxy secret %s: %w", s, err)
}
return &secret, nil
})
if err != nil {
return nil, fmt.Errorf("failed to get proxy func: %w", err)
}
options = append(options, actions.WithProxy(proxyFunc))
}
tlsConfig := autoscalingRunnerSet.Spec.GitHubServerTLS
if tlsConfig != nil {
pool, err := tlsConfig.ToCertPool(func(name, key string) ([]byte, error) {
var configmap corev1.ConfigMap
err := r.Get(
ctx,
types.NamespacedName{
Namespace: autoscalingRunnerSet.Namespace,
Name: name,
},
&configmap,
)
if err != nil {
return nil, fmt.Errorf("failed to get configmap %s: %w", name, err)
}
return []byte(configmap.Data[key]), nil
})
if err != nil {
return nil, fmt.Errorf("failed to get tls config: %w", err)
}
options = append(options, actions.WithRootCAs(pool))
}
return options, nil
}
// SetupWithManager sets up the controller with the Manager. // SetupWithManager sets up the controller with the Manager.
func (r *AutoscalingRunnerSetReconciler) SetupWithManager(mgr ctrl.Manager) error { func (r *AutoscalingRunnerSetReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr). return ctrl.NewControllerManagedBy(mgr).

View File

@ -70,7 +70,12 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() {
Log: logf.Log, Log: logf.Log,
ControllerNamespace: autoscalingNS.Name, ControllerNamespace: autoscalingNS.Name,
DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc", DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc",
ActionsClient: fake.NewMultiClient(), ResourceBuilder: ResourceBuilder{
ActionsClientGetter: &ActionsClientSecretResolver{
Client: k8sClient,
MultiClient: fake.NewMultiClient(),
},
},
} }
err := controller.SetupWithManager(mgr) err := controller.SetupWithManager(mgr)
Expect(err).NotTo(HaveOccurred(), "failed to setup controller") Expect(err).NotTo(HaveOccurred(), "failed to setup controller")
@ -677,13 +682,7 @@ 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)
controller := &AutoscalingRunnerSetReconciler{ multiClient := fake.NewMultiClient(
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Log: logf.Log,
ControllerNamespace: autoscalingNS.Name,
DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc",
ActionsClient: fake.NewMultiClient(
fake.WithDefaultClient( fake.WithDefaultClient(
fake.NewFakeClient( fake.NewFakeClient(
fake.WithUpdateRunnerScaleSet( fake.WithUpdateRunnerScaleSet(
@ -703,7 +702,20 @@ var _ = Describe("Test AutoScalingController updates", Ordered, func() {
), ),
nil, nil,
), ),
), )
controller := &AutoscalingRunnerSetReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Log: logf.Log,
ControllerNamespace: autoscalingNS.Name,
DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc",
ResourceBuilder: ResourceBuilder{
ActionsClientGetter: &ActionsClientSecretResolver{
Client: k8sClient,
MultiClient: multiClient,
},
},
} }
err := controller.SetupWithManager(mgr) err := controller.SetupWithManager(mgr)
Expect(err).NotTo(HaveOccurred(), "failed to setup controller") Expect(err).NotTo(HaveOccurred(), "failed to setup controller")
@ -818,7 +830,12 @@ var _ = Describe("Test AutoscalingController creation failures", Ordered, func()
Log: logf.Log, Log: logf.Log,
ControllerNamespace: autoscalingNS.Name, ControllerNamespace: autoscalingNS.Name,
DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc", DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc",
ActionsClient: fake.NewMultiClient(), ResourceBuilder: ResourceBuilder{
ActionsClientGetter: &ActionsClientSecretResolver{
Client: k8sClient,
MultiClient: fake.NewMultiClient(),
},
},
} }
err := controller.SetupWithManager(mgr) err := controller.SetupWithManager(mgr)
Expect(err).NotTo(HaveOccurred(), "failed to setup controller") Expect(err).NotTo(HaveOccurred(), "failed to setup controller")
@ -937,14 +954,19 @@ 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(),
Log: logf.Log, Log: logf.Log,
ControllerNamespace: autoscalingNS.Name, ControllerNamespace: autoscalingNS.Name,
DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc", DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc",
ActionsClient: actions.NewMultiClient(logr.Discard()), ResourceBuilder: ResourceBuilder{
ActionsClientGetter: &ActionsClientSecretResolver{
Client: k8sClient,
MultiClient: multiClient,
},
},
} }
err := controller.SetupWithManager(mgr) err := controller.SetupWithManager(mgr)
@ -1127,7 +1149,12 @@ var _ = Describe("Test client optional configuration", Ordered, func() {
Log: logf.Log, Log: logf.Log,
ControllerNamespace: autoscalingNS.Name, ControllerNamespace: autoscalingNS.Name,
DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc", DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc",
ActionsClient: fake.NewMultiClient(), ResourceBuilder: ResourceBuilder{
ActionsClientGetter: &ActionsClientSecretResolver{
Client: k8sClient,
MultiClient: fake.NewMultiClient(),
},
},
} }
err = controller.SetupWithManager(mgr) err = controller.SetupWithManager(mgr)
Expect(err).NotTo(HaveOccurred(), "failed to setup controller") Expect(err).NotTo(HaveOccurred(), "failed to setup controller")
@ -1136,7 +1163,10 @@ 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.ActionsClient = actions.NewMultiClient(logr.Discard()) controller.ResourceBuilder.ActionsClientGetter = &ActionsClientSecretResolver{
Client: k8sClient,
MultiClient: actions.NewMultiClient(logr.Discard()),
}
certsFolder := filepath.Join( certsFolder := filepath.Join(
"../../", "../../",
@ -1361,7 +1391,12 @@ var _ = Describe("Test external permissions cleanup", Ordered, func() {
Log: logf.Log, Log: logf.Log,
ControllerNamespace: autoscalingNS.Name, ControllerNamespace: autoscalingNS.Name,
DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc", DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc",
ActionsClient: fake.NewMultiClient(), ResourceBuilder: ResourceBuilder{
ActionsClientGetter: &ActionsClientSecretResolver{
Client: k8sClient,
MultiClient: fake.NewMultiClient(),
},
},
} }
err := controller.SetupWithManager(mgr) err := controller.SetupWithManager(mgr)
Expect(err).NotTo(HaveOccurred(), "failed to setup controller") Expect(err).NotTo(HaveOccurred(), "failed to setup controller")
@ -1519,7 +1554,12 @@ var _ = Describe("Test external permissions cleanup", Ordered, func() {
Log: logf.Log, Log: logf.Log,
ControllerNamespace: autoscalingNS.Name, ControllerNamespace: autoscalingNS.Name,
DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc", DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc",
ActionsClient: fake.NewMultiClient(), ResourceBuilder: ResourceBuilder{
ActionsClientGetter: &ActionsClientSecretResolver{
Client: k8sClient,
MultiClient: fake.NewMultiClient(),
},
},
} }
err := controller.SetupWithManager(mgr) err := controller.SetupWithManager(mgr)
Expect(err).NotTo(HaveOccurred(), "failed to setup controller") Expect(err).NotTo(HaveOccurred(), "failed to setup controller")
@ -1727,7 +1767,12 @@ var _ = Describe("Test resource version and build version mismatch", func() {
Log: logf.Log, Log: logf.Log,
ControllerNamespace: autoscalingNS.Name, ControllerNamespace: autoscalingNS.Name,
DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc", DefaultRunnerScaleSetListenerImage: "ghcr.io/actions/arc",
ActionsClient: fake.NewMultiClient(), ResourceBuilder: ResourceBuilder{
ActionsClientGetter: &ActionsClientSecretResolver{
Client: k8sClient,
MultiClient: fake.NewMultiClient(),
},
},
} }
err := controller.SetupWithManager(mgr) err := controller.SetupWithManager(mgr)
Expect(err).NotTo(HaveOccurred(), "failed to setup controller") Expect(err).NotTo(HaveOccurred(), "failed to setup controller")

View File

@ -331,7 +331,7 @@ func (r *EphemeralRunnerSetReconciler) cleanUpEphemeralRunners(ctx context.Conte
return false, nil return false, nil
} }
actionsClient, err := r.actionsClientFor(ctx, ephemeralRunnerSet) actionsClient, err := r.ActionsClientGetter.GetActionsClientForEphemeralRunnerSet(ctx, ephemeralRunnerSet)
if err != nil { if err != nil {
return false, err return false, err
} }
@ -439,7 +439,7 @@ func (r *EphemeralRunnerSetReconciler) deleteIdleEphemeralRunners(ctx context.Co
log.Info("No pending or running ephemeral runners running at this time for scale down") log.Info("No pending or running ephemeral runners running at this time for scale down")
return nil return nil
} }
actionsClient, err := r.actionsClientFor(ctx, ephemeralRunnerSet) actionsClient, err := r.ActionsClientGetter.GetActionsClientForEphemeralRunnerSet(ctx, ephemeralRunnerSet)
if err != nil { if err != nil {
return fmt.Errorf("failed to create actions client for ephemeral runner replica set: %w", err) return fmt.Errorf("failed to create actions client for ephemeral runner replica set: %w", err)
} }
@ -502,73 +502,6 @@ func (r *EphemeralRunnerSetReconciler) deleteEphemeralRunnerWithActionsClient(ct
return true, nil return true, nil
} }
func (r *EphemeralRunnerSetReconciler) actionsClientFor(ctx context.Context, rs *v1alpha1.EphemeralRunnerSet) (actions.ActionsService, error) {
secret := new(corev1.Secret)
if err := r.Get(ctx, types.NamespacedName{Namespace: rs.Namespace, Name: rs.Spec.EphemeralRunnerSpec.GitHubConfigSecret}, secret); err != nil {
return nil, fmt.Errorf("failed to get secret: %w", err)
}
opts, err := r.actionsClientOptionsFor(ctx, rs)
if err != nil {
return nil, fmt.Errorf("failed to get actions client options: %w", err)
}
return r.ActionsClient.GetClientFromSecret(
ctx,
rs.Spec.EphemeralRunnerSpec.GitHubConfigUrl,
rs.Namespace,
secret.Data,
opts...,
)
}
func (r *EphemeralRunnerSetReconciler) actionsClientOptionsFor(ctx context.Context, rs *v1alpha1.EphemeralRunnerSet) ([]actions.ClientOption, error) {
var opts []actions.ClientOption
if rs.Spec.EphemeralRunnerSpec.Proxy != nil {
proxyFunc, err := rs.Spec.EphemeralRunnerSpec.Proxy.ProxyFunc(func(s string) (*corev1.Secret, error) {
var secret corev1.Secret
err := r.Get(ctx, types.NamespacedName{Namespace: rs.Namespace, Name: s}, &secret)
if err != nil {
return nil, fmt.Errorf("failed to get secret %s: %w", s, err)
}
return &secret, nil
})
if err != nil {
return nil, fmt.Errorf("failed to get proxy func: %w", err)
}
opts = append(opts, actions.WithProxy(proxyFunc))
}
tlsConfig := rs.Spec.EphemeralRunnerSpec.GitHubServerTLS
if tlsConfig != nil {
pool, err := tlsConfig.ToCertPool(func(name, key string) ([]byte, error) {
var configmap corev1.ConfigMap
err := r.Get(
ctx,
types.NamespacedName{
Namespace: rs.Namespace,
Name: name,
},
&configmap,
)
if err != nil {
return nil, fmt.Errorf("failed to get configmap %s: %w", name, err)
}
return []byte(configmap.Data[key]), nil
})
if err != nil {
return nil, fmt.Errorf("failed to get tls config: %w", err)
}
opts = append(opts, actions.WithRootCAs(pool))
}
return opts, nil
}
// SetupWithManager sets up the controller with the Manager. // SetupWithManager sets up the controller with the Manager.
func (r *EphemeralRunnerSetReconciler) SetupWithManager(mgr ctrl.Manager) error { func (r *EphemeralRunnerSetReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr). return ctrl.NewControllerManagedBy(mgr).

View File

@ -57,7 +57,12 @@ var _ = Describe("Test EphemeralRunnerSet controller", func() {
Client: mgr.GetClient(), Client: mgr.GetClient(),
Scheme: mgr.GetScheme(), Scheme: mgr.GetScheme(),
Log: logf.Log, Log: logf.Log,
ActionsClient: fake.NewMultiClient(), ResourceBuilder: ResourceBuilder{
ActionsClientGetter: &ActionsClientSecretResolver{
Client: mgr.GetClient(),
MultiClient: fake.NewMultiClient(),
},
},
} }
err := controller.SetupWithManager(mgr) err := controller.SetupWithManager(mgr)
Expect(err).NotTo(HaveOccurred(), "failed to setup controller") Expect(err).NotTo(HaveOccurred(), "failed to setup controller")
@ -1107,7 +1112,12 @@ var _ = Describe("Test EphemeralRunnerSet controller with proxy settings", func(
Client: mgr.GetClient(), Client: mgr.GetClient(),
Scheme: mgr.GetScheme(), Scheme: mgr.GetScheme(),
Log: logf.Log, Log: logf.Log,
ActionsClient: actions.NewMultiClient(logr.Discard()), ResourceBuilder: ResourceBuilder{
ActionsClientGetter: &ActionsClientSecretResolver{
Client: mgr.GetClient(),
MultiClient: actions.NewMultiClient(logr.Discard()),
},
},
} }
err := controller.SetupWithManager(mgr) err := controller.SetupWithManager(mgr)
Expect(err).NotTo(HaveOccurred(), "failed to setup controller") Expect(err).NotTo(HaveOccurred(), "failed to setup controller")
@ -1406,7 +1416,12 @@ var _ = Describe("Test EphemeralRunnerSet controller with custom root CA", func(
Client: mgr.GetClient(), Client: mgr.GetClient(),
Scheme: mgr.GetScheme(), Scheme: mgr.GetScheme(),
Log: logf.Log, Log: logf.Log,
ActionsClient: actions.NewMultiClient(logr.Discard()), ResourceBuilder: ResourceBuilder{
ActionsClientGetter: &ActionsClientSecretResolver{
Client: mgr.GetClient(),
MultiClient: actions.NewMultiClient(logr.Discard()),
},
},
} }
err = controller.SetupWithManager(mgr) err = controller.SetupWithManager(mgr)
Expect(err).NotTo(HaveOccurred(), "failed to setup controller") Expect(err).NotTo(HaveOccurred(), "failed to setup controller")

View File

@ -72,6 +72,7 @@ func SetListenerEntrypoint(entrypoint string) {
type ResourceBuilder struct { type ResourceBuilder struct {
ExcludeLabelPropagationPrefixes []string ExcludeLabelPropagationPrefixes []string
ActionsClientGetter
} }
// boolPtr returns a pointer to a bool value // boolPtr returns a pointer to a bool value

9
go.mod
View File

@ -3,6 +3,9 @@ module github.com/actions/actions-runner-controller
go 1.24.3 go 1.24.3
require ( require (
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.12.0
github.com/bradleyfalzon/ghinstallation/v2 v2.14.0 github.com/bradleyfalzon/ghinstallation/v2 v2.14.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
@ -39,6 +42,9 @@ require (
require ( require (
filippo.io/edwards25519 v1.1.0 // indirect filippo.io/edwards25519 v1.1.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.1 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect
github.com/BurntSushi/toml v1.4.0 // indirect github.com/BurntSushi/toml v1.4.0 // indirect
github.com/ProtonMail/go-crypto v1.1.6 // indirect github.com/ProtonMail/go-crypto v1.1.6 // indirect
github.com/aws/aws-sdk-go-v2 v1.36.3 // indirect github.com/aws/aws-sdk-go-v2 v1.36.3 // indirect
@ -95,6 +101,7 @@ require (
github.com/go-sql-driver/mysql v1.9.0 // indirect github.com/go-sql-driver/mysql v1.9.0 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
github.com/golang/protobuf v1.5.4 // indirect github.com/golang/protobuf v1.5.4 // indirect
github.com/gonvenience/bunt v1.4.0 // indirect github.com/gonvenience/bunt v1.4.0 // indirect
github.com/gonvenience/idem v0.0.1 // indirect github.com/gonvenience/idem v0.0.1 // indirect
@ -121,6 +128,7 @@ require (
github.com/josharian/intern v1.0.0 // indirect github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.18.0 // indirect github.com/klauspost/compress v1.18.0 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mailru/easyjson v0.9.0 // indirect github.com/mailru/easyjson v0.9.0 // indirect
github.com/mattn/go-ciede2000 v0.0.0-20170301095244-782e8c62fec3 // indirect github.com/mattn/go-ciede2000 v0.0.0-20170301095244-782e8c62fec3 // indirect
@ -134,6 +142,7 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/pquerna/otp v1.4.0 // indirect github.com/pquerna/otp v1.4.0 // indirect

28
go.sum
View File

@ -1,5 +1,22 @@
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/Azure/azure-sdk-for-go v51.0.0+incompatible h1:p7blnyJSjJqf5jflHbSGhIhEpXIgIFmYZNg5uwqweso=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 h1:g0EZJwz7xkXQiZAI5xi9f3WWFYBlX1CPTrR+NDToRkQ=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0/go.mod h1:XCW7KnZet0Opnr7HccfUw1PLc4CjHqpcaxW8DHklNkQ=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 h1:B/dfvscEQtew9dVuoxqxrUKKv8Ih2f55PydknDamU+g=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0/go.mod h1:fiPSssYvltE08HJchL04dOy+RD4hgrjph0cwGGMntdI=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.0 h1:+m0M/LFxN43KvULkDNfdXOgrjtg6UYJPFBJyuEcRCAw=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.0/go.mod h1:PwOyop78lveYMRs6oCxjiVyBdyCgIYH6XHIVZO9/SFQ=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY=
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.12.0 h1:xnO4sFyG8UH2fElBkcqLTOZsAajvKfnSlgBBW8dXYjw=
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.12.0/go.mod h1:XD3DIOOVgBCO03OleB1fHjgktVRFxlT++KwKgIOewdM=
github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.1 h1:FbH3BbSb4bvGluTesZZ+ttN/MDsnMmQP36OSnDuSXqw=
github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.1/go.mod h1:9V2j0jn9jDEkCkv8w/bKTNppX/d0FVA1ud77xCIP4KA=
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM=
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE=
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU=
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw= github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw=
@ -97,6 +114,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU= github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/evanphx/json-patch v5.9.11+incompatible h1:ixHHqfcGvxhWkniF1tWxBHA0yb4Z+d1UQi45df52xW8= github.com/evanphx/json-patch v5.9.11+incompatible h1:ixHHqfcGvxhWkniF1tWxBHA0yb4Z+d1UQi45df52xW8=
@ -134,6 +153,8 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
@ -215,6 +236,8 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6 h1:IsMZxCuZqKuao2vNdfD82fjjgPLfyHLpR41Z88viRWs=
github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6/go.mod h1:3VeWNIJaW+O5xpRQbPp0Ybqu1vJd/pm7s2F473HRrkw=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
@ -269,6 +292,8 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.36.3 h1:hID7cr8t3Wp26+cYnfcjR6HpJ00fdogN6dqZ1t6IylU= github.com/onsi/gomega v1.36.3 h1:hID7cr8t3Wp26+cYnfcjR6HpJ00fdogN6dqZ1t6IylU=
github.com/onsi/gomega v1.36.3/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= github.com/onsi/gomega v1.36.3/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@ -284,6 +309,8 @@ github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/redis/go-redis/v9 v9.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4=
github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
@ -355,6 +382,7 @@ golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=

42
main.go
View File

@ -32,6 +32,8 @@ import (
"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/github/actions"
"github.com/actions/actions-runner-controller/logging" "github.com/actions/actions-runner-controller/logging"
"github.com/actions/actions-runner-controller/vault"
"github.com/actions/actions-runner-controller/vault/azurekeyvault"
"github.com/kelseyhightower/envconfig" "github.com/kelseyhightower/envconfig"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
@ -274,8 +276,18 @@ func main() {
log.WithName("actions-clients"), log.WithName("actions-clients"),
) )
actionsClientGetter, err := newActionsClientGetter(
mgr.GetClient(),
actionsMultiClient,
)
if err != nil {
log.Error(err, "unable to create actions client resolver")
os.Exit(1)
}
rb := actionsgithubcom.ResourceBuilder{ rb := actionsgithubcom.ResourceBuilder{
ExcludeLabelPropagationPrefixes: excludeLabelPropagationPrefixes, ExcludeLabelPropagationPrefixes: excludeLabelPropagationPrefixes,
ActionsClientGetter: actionsClientGetter,
} }
if err = (&actionsgithubcom.AutoscalingRunnerSetReconciler{ if err = (&actionsgithubcom.AutoscalingRunnerSetReconciler{
@ -308,7 +320,6 @@ func main() {
Client: mgr.GetClient(), Client: mgr.GetClient(),
Log: log.WithName("EphemeralRunnerSet").WithValues("version", build.Version), Log: log.WithName("EphemeralRunnerSet").WithValues("version", build.Version),
Scheme: mgr.GetScheme(), Scheme: mgr.GetScheme(),
ActionsClient: actionsMultiClient,
PublishMetrics: metricsAddr != "0", PublishMetrics: metricsAddr != "0",
ResourceBuilder: rb, ResourceBuilder: rb,
}).SetupWithManager(mgr); err != nil { }).SetupWithManager(mgr); err != nil {
@ -492,3 +503,32 @@ func (s *commaSeparatedStringSlice) Set(value string) error {
} }
return nil return nil
} }
func newActionsClientGetter(k8sClient client.Client, multiClient actions.MultiClient) (actionsgithubcom.ActionsClientGetter, error) {
vaultType := os.Getenv("CONTROLLER_MANAGER_VAULT_TYPE")
if vaultType == "" {
return &actionsgithubcom.ActionsClientSecretResolver{
Client: k8sClient,
MultiClient: multiClient,
}, nil
}
key := os.Getenv("CONTROLLER_MANAGER_VAULT_API_KEY")
var vault vault.Vault
switch vaultType {
case "azure":
v, err := azurekeyvault.New(azurekeyvault.Config{JWT: key})
if err != nil {
return nil, fmt.Errorf("failed to create Azure Key Vault client: %w", err)
}
vault = v
default:
return nil, fmt.Errorf("unsupported vault type: %q", vaultType)
}
return &actionsgithubcom.ActionsClientVaultResolver{
Vault: vault,
MultiClient: multiClient,
}, nil
}

View File

@ -0,0 +1,58 @@
package azurekeyvault
import (
"context"
"fmt"
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
"github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets"
)
type AzureKeyVault struct {
client *azsecrets.Client
}
type Config struct {
ClientID string
TenantID string
JWT string
URL string
}
func (c *Config) getAssertion(ctx context.Context) (string, error) {
return c.JWT, nil
}
func New(cfg Config) (*AzureKeyVault, error) {
cred, err := azidentity.NewClientAssertionCredential(
cfg.TenantID,
cfg.ClientID,
cfg.getAssertion,
&azidentity.ClientAssertionCredentialOptions{
ClientOptions: azcore.ClientOptions{},
},
)
if err != nil {
return nil, fmt.Errorf("failed to create client assertion credential: %w", err)
}
client, err := azsecrets.NewClient(cfg.URL, cred, nil)
if err != nil {
return nil, fmt.Errorf("failed to initialize keyvault client: %w", err)
}
return &AzureKeyVault{client: client}, nil
}
func (v *AzureKeyVault) GetSecret(ctx context.Context, name, version string) (string, error) {
secret, err := v.client.GetSecret(context.Background(), name, version, nil)
if err != nil {
return "", fmt.Errorf("failed to get secret: %w", err)
}
if secret.Value == nil {
return "", fmt.Errorf("secret value is nil")
}
return *secret.Value, nil
}

7
vault/vault.go Normal file
View File

@ -0,0 +1,7 @@
package vault
import "context"
type Vault interface {
GetSecret(ctx context.Context, name, version string) (string, error)
}