Skip to content

Commit a86e981

Browse files
committed
Adds support for reading AutoscalingRunnerSet githubConfigSecret from controller namespace
1 parent 1be410b commit a86e981

9 files changed

+193
-79
lines changed

charts/gha-runner-scale-set-controller/templates/deployment.yaml

+3
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ spec:
6868
{{- with .Values.flags.updateStrategy }}
6969
- "--update-strategy={{ . }}"
7070
{{- end }}
71+
{{- if .Values.flags.readGitHubConfigSecretFromControllerNamespace }}
72+
- "--read-github-config-secret-from-controller-namespace"
73+
{{- end }}
7174
{{- if .Values.metrics }}
7275
{{- with .Values.metrics }}
7376
- "--listener-metrics-addr={{ .listenerAddr }}"

charts/gha-runner-scale-set-controller/values.yaml

+6
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,12 @@ flags:
122122
## that you don't have any overprovisioning of runners.
123123
updateStrategy: "immediate"
124124

125+
## This flag determines whether the githubConfigSecret on the AutoscalingRunnerSet will be read from the
126+
## namespace that the controller is running in or the namespace that the AutoscalingRunnerSet resource is in.
127+
##
128+
## Defaults to false, so the secret will be read from the namespace that the runner lives in.
129+
readGitHubConfigSecretFromControllerNamespace: false
130+
125131
## Defines a list of prefixes that should not be propagated to internal resources.
126132
## This is useful when you have labels that are used for internal purposes and should not be propagated to internal resources.
127133
## See https://github.com/actions/actions-runner-controller/issues/3533 for more information.

controllers/actions.github.com/autoscalinglistener_controller.go

+12-3
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,9 @@ const (
4848
// AutoscalingListenerReconciler reconciles a AutoscalingListener object
4949
type AutoscalingListenerReconciler struct {
5050
client.Client
51-
Log logr.Logger
52-
Scheme *runtime.Scheme
51+
Log logr.Logger
52+
Scheme *runtime.Scheme
53+
ReadGitHubConfigSecretFromControllerNamespace bool
5354
// ListenerMetricsAddr is address that the metrics endpoint binds to.
5455
// If it is set to "0", the metrics server is not started.
5556
ListenerMetricsAddr string
@@ -130,7 +131,7 @@ func (r *AutoscalingListenerReconciler) Reconcile(ctx context.Context, req ctrl.
130131

131132
// Check if the GitHub config secret exists
132133
secret := new(corev1.Secret)
133-
if err := r.Get(ctx, types.NamespacedName{Namespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace, Name: autoscalingListener.Spec.GitHubConfigSecret}, secret); err != nil {
134+
if err := r.Get(ctx, types.NamespacedName{Namespace: r.deriveConfigSecretNamespace(autoscalingListener), Name: autoscalingListener.Spec.GitHubConfigSecret}, secret); err != nil {
134135
log.Error(err, "Failed to find GitHub config secret.",
135136
"namespace", autoscalingListener.Spec.AutoscalingRunnerSetNamespace,
136137
"name", autoscalingListener.Spec.GitHubConfigSecret)
@@ -275,6 +276,14 @@ func (r *AutoscalingListenerReconciler) Reconcile(ctx context.Context, req ctrl.
275276
return ctrl.Result{}, nil
276277
}
277278

279+
func (r *AutoscalingListenerReconciler) deriveConfigSecretNamespace(autoscalingListener *v1alpha1.AutoscalingListener) string {
280+
secretNamespace := autoscalingListener.Spec.AutoscalingRunnerSetNamespace
281+
if r.ReadGitHubConfigSecretFromControllerNamespace {
282+
secretNamespace = autoscalingListener.Namespace
283+
}
284+
return secretNamespace
285+
}
286+
278287
func (r *AutoscalingListenerReconciler) cleanupResources(ctx context.Context, autoscalingListener *v1alpha1.AutoscalingListener, logger logr.Logger) (done bool, err error) {
279288
logger.Info("Cleaning up the listener pod")
280289
listenerPod := new(corev1.Pod)

controllers/actions.github.com/autoscalingrunnerset_controller.go

+11-2
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ type AutoscalingRunnerSetReconciler struct {
7575
Log logr.Logger
7676
Scheme *runtime.Scheme
7777
ControllerNamespace string
78+
ReadGitHubConfigSecretFromControllerNamespace bool
7879
DefaultRunnerScaleSetListenerImage string
7980
DefaultRunnerScaleSetListenerImagePullSecrets []string
8081
UpdateStrategy UpdateStrategy
@@ -208,7 +209,7 @@ func (r *AutoscalingRunnerSetReconciler) Reconcile(ctx context.Context, req ctrl
208209
}
209210

210211
secret := new(corev1.Secret)
211-
if err := r.Get(ctx, types.NamespacedName{Namespace: autoscalingRunnerSet.Namespace, Name: autoscalingRunnerSet.Spec.GitHubConfigSecret}, secret); err != nil {
212+
if err := r.Get(ctx, types.NamespacedName{Namespace: r.deriveConfigSecretNamespace(autoscalingRunnerSet), Name: autoscalingRunnerSet.Spec.GitHubConfigSecret}, secret); err != nil {
212213
log.Error(err, "Failed to find GitHub config secret.",
213214
"namespace", autoscalingRunnerSet.Namespace,
214215
"name", autoscalingRunnerSet.Spec.GitHubConfigSecret)
@@ -678,7 +679,7 @@ func (r *AutoscalingRunnerSetReconciler) listEphemeralRunnerSets(ctx context.Con
678679

679680
func (r *AutoscalingRunnerSetReconciler) actionsClientFor(ctx context.Context, autoscalingRunnerSet *v1alpha1.AutoscalingRunnerSet) (actions.ActionsService, error) {
680681
var configSecret corev1.Secret
681-
if err := r.Get(ctx, types.NamespacedName{Namespace: autoscalingRunnerSet.Namespace, Name: autoscalingRunnerSet.Spec.GitHubConfigSecret}, &configSecret); err != nil {
682+
if err := r.Get(ctx, types.NamespacedName{Namespace: r.deriveConfigSecretNamespace(autoscalingRunnerSet), Name: autoscalingRunnerSet.Spec.GitHubConfigSecret}, &configSecret); err != nil {
682683
return nil, fmt.Errorf("failed to find GitHub config secret: %w", err)
683684
}
684685

@@ -696,6 +697,14 @@ func (r *AutoscalingRunnerSetReconciler) actionsClientFor(ctx context.Context, a
696697
)
697698
}
698699

700+
func (r *AutoscalingRunnerSetReconciler) deriveConfigSecretNamespace(autoscalingRunnerSet *v1alpha1.AutoscalingRunnerSet) string {
701+
secretNamespace := autoscalingRunnerSet.Namespace
702+
if r.ReadGitHubConfigSecretFromControllerNamespace {
703+
secretNamespace = r.ControllerNamespace
704+
}
705+
return secretNamespace
706+
}
707+
699708
func (r *AutoscalingRunnerSetReconciler) actionsClientOptionsFor(ctx context.Context, autoscalingRunnerSet *v1alpha1.AutoscalingRunnerSet) ([]actions.ClientOption, error) {
700709
var options []actions.ClientOption
701710

controllers/actions.github.com/autoscalingrunnerset_controller_test.go

+76-27
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() {
4444
var mgr ctrl.Manager
4545
var controller *AutoscalingRunnerSetReconciler
4646
var autoscalingNS *corev1.Namespace
47+
var controllerNS *corev1.Namespace
4748
var autoscalingRunnerSet *v1alpha1.AutoscalingRunnerSet
4849
var configSecret *corev1.Secret
4950

@@ -61,7 +62,11 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() {
6162

6263
BeforeEach(func() {
6364
ctx = context.Background()
64-
autoscalingNS, mgr = createNamespace(GinkgoT(), k8sClient)
65+
var namespaces []*corev1.Namespace
66+
namespaces, mgr = createNamespaces(GinkgoT(), 2)
67+
autoscalingNS = namespaces[0]
68+
controllerNS = namespaces[1]
69+
6570
configSecret = createDefaultSecret(GinkgoT(), k8sClient, autoscalingNS.Name)
6671

6772
controller = &AutoscalingRunnerSetReconciler{
@@ -77,32 +82,7 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() {
7782

7883
min := 1
7984
max := 10
80-
autoscalingRunnerSet = &v1alpha1.AutoscalingRunnerSet{
81-
ObjectMeta: metav1.ObjectMeta{
82-
Name: "test-asrs",
83-
Namespace: autoscalingNS.Name,
84-
Labels: map[string]string{
85-
LabelKeyKubernetesVersion: buildVersion,
86-
},
87-
},
88-
Spec: v1alpha1.AutoscalingRunnerSetSpec{
89-
GitHubConfigUrl: "https://github.com/owner/repo",
90-
GitHubConfigSecret: configSecret.Name,
91-
MaxRunners: &max,
92-
MinRunners: &min,
93-
RunnerGroup: "testgroup",
94-
Template: corev1.PodTemplateSpec{
95-
Spec: corev1.PodSpec{
96-
Containers: []corev1.Container{
97-
{
98-
Name: "runner",
99-
Image: "ghcr.io/actions/runner",
100-
},
101-
},
102-
},
103-
},
104-
},
105-
}
85+
autoscalingRunnerSet = createAutoscalingRunnerSet("test-asrs", autoscalingNS, buildVersion, configSecret.Name, min, max)
10686

10787
err = k8sClient.Create(ctx, autoscalingRunnerSet)
10888
Expect(err).NotTo(HaveOccurred(), "failed to create AutoScalingRunnerSet")
@@ -197,6 +177,46 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() {
197177
Expect(err).NotTo(HaveOccurred(), "failed to list EphemeralRunnerSet")
198178
Expect(len(runnerSetList.Items)).To(BeEquivalentTo(1), "Only one EphemeralRunnerSet should be created")
199179
})
180+
181+
It("It should read GitHub config secret from controller namespace if ReadGitHubConfigSecretFromControllerNamespace is enabled", func() {
182+
err := k8sClient.Delete(ctx, &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Name: configSecret.Name, Namespace: autoscalingNS.Name}})
183+
Expect(err).ToNot(HaveOccurred(), "failed to delete github-config-secret")
184+
185+
controller2 := controller
186+
controller2.ReadGitHubConfigSecretFromControllerNamespace = true
187+
controller2.ControllerNamespace = controllerNS.Name
188+
189+
err = controller2.SetupWithManager(mgr)
190+
Expect(err).NotTo(HaveOccurred(), "failed to setup controller")
191+
192+
controllerNamespaceConfigSecret := createDefaultSecret(GinkgoT(), k8sClient, controller2.ControllerNamespace)
193+
runner := createAutoscalingRunnerSet("secret-test-asrs", autoscalingNS, buildVersion, controllerNamespaceConfigSecret.Name, 0, 1)
194+
err = k8sClient.Create(ctx, runner)
195+
Expect(err).NotTo(HaveOccurred(), "failed to create AutoscalingRunnerSet")
196+
197+
created := new(v1alpha1.AutoscalingRunnerSet)
198+
// Check if runner scale set is created on service
199+
Eventually(
200+
func() (string, error) {
201+
err := k8sClient.Get(ctx, client.ObjectKey{Name: runner.Name, Namespace: autoscalingRunnerSet.Namespace}, created)
202+
if err != nil {
203+
return "", err
204+
}
205+
206+
if _, ok := created.Annotations[runnerScaleSetIdAnnotationKey]; !ok {
207+
return "", nil
208+
}
209+
210+
if _, ok := created.Annotations[AnnotationKeyGitHubRunnerGroupName]; !ok {
211+
return "", nil
212+
}
213+
214+
return fmt.Sprintf("%s_%s", created.Annotations[runnerScaleSetIdAnnotationKey], created.Annotations[AnnotationKeyGitHubRunnerGroupName]), nil
215+
},
216+
autoscalingRunnerSetTestTimeout,
217+
autoscalingRunnerSetTestInterval).Should(BeEquivalentTo("1_testgroup"), "RunnerScaleSet should be created/fetched and update the AutoScalingRunnerSet's annotation")
218+
219+
})
200220
})
201221

202222
Context("When deleting a new AutoScalingRunnerSet", func() {
@@ -1790,3 +1810,32 @@ var _ = Describe("Test resource version and build version mismatch", func() {
17901810
).Should(BeTrue())
17911811
})
17921812
})
1813+
1814+
func createAutoscalingRunnerSet(name string, ns *corev1.Namespace, buildVersion string, secretName string, min, max int) *v1alpha1.AutoscalingRunnerSet {
1815+
return &v1alpha1.AutoscalingRunnerSet{
1816+
ObjectMeta: metav1.ObjectMeta{
1817+
Name: name,
1818+
Namespace: ns.Name,
1819+
Labels: map[string]string{
1820+
LabelKeyKubernetesVersion: buildVersion,
1821+
},
1822+
},
1823+
Spec: v1alpha1.AutoscalingRunnerSetSpec{
1824+
GitHubConfigUrl: "https://github.com/owner/repo",
1825+
GitHubConfigSecret: secretName,
1826+
MaxRunners: &max,
1827+
MinRunners: &min,
1828+
RunnerGroup: "testgroup",
1829+
Template: corev1.PodTemplateSpec{
1830+
Spec: corev1.PodSpec{
1831+
Containers: []corev1.Container{
1832+
{
1833+
Name: "runner",
1834+
Image: "ghcr.io/actions/runner",
1835+
},
1836+
},
1837+
},
1838+
},
1839+
},
1840+
}
1841+
}

controllers/actions.github.com/ephemeralrunner_controller.go

+13-1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ type EphemeralRunnerReconciler struct {
5252
Log logr.Logger
5353
Scheme *runtime.Scheme
5454
ActionsClient actions.MultiClient
55+
56+
ControllerNamespace string
57+
ReadGitHubConfigSecretFromControllerNamespace bool
58+
5559
ResourceBuilder
5660
}
5761

@@ -592,6 +596,14 @@ func (r *EphemeralRunnerReconciler) updateStatusWithRunnerConfig(ctx context.Con
592596
return ctrl.Result{}, nil
593597
}
594598

599+
func (r *EphemeralRunnerReconciler) deriveConfigSecretNamespace(ephemeralRunner *v1alpha1.EphemeralRunner) string {
600+
secretNamespace := ephemeralRunner.Namespace
601+
if r.ReadGitHubConfigSecretFromControllerNamespace {
602+
secretNamespace = r.ControllerNamespace
603+
}
604+
return secretNamespace
605+
}
606+
595607
func (r *EphemeralRunnerReconciler) createPod(ctx context.Context, runner *v1alpha1.EphemeralRunner, secret *corev1.Secret, log logr.Logger) (ctrl.Result, error) {
596608
var envs []corev1.EnvVar
597609
if runner.Spec.ProxySecretRef != "" {
@@ -712,7 +724,7 @@ func (r *EphemeralRunnerReconciler) updateRunStatusFromPod(ctx context.Context,
712724

713725
func (r *EphemeralRunnerReconciler) actionsClientFor(ctx context.Context, runner *v1alpha1.EphemeralRunner) (actions.ActionsService, error) {
714726
secret := new(corev1.Secret)
715-
if err := r.Get(ctx, types.NamespacedName{Namespace: runner.Namespace, Name: runner.Spec.GitHubConfigSecret}, secret); err != nil {
727+
if err := r.Get(ctx, types.NamespacedName{Namespace: r.deriveConfigSecretNamespace(runner), Name: runner.Spec.GitHubConfigSecret}, secret); err != nil {
716728
return nil, fmt.Errorf("failed to get secret: %w", err)
717729
}
718730

controllers/actions.github.com/ephemeralrunnerset_controller.go

+12-2
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ type EphemeralRunnerSetReconciler struct {
5151
Scheme *runtime.Scheme
5252
ActionsClient actions.MultiClient
5353

54-
PublishMetrics bool
54+
PublishMetrics bool
55+
ControllerNamespace string
56+
ReadGitHubConfigSecretFromControllerNamespace bool
5557

5658
ResourceBuilder
5759
}
@@ -504,7 +506,7 @@ func (r *EphemeralRunnerSetReconciler) deleteEphemeralRunnerWithActionsClient(ct
504506

505507
func (r *EphemeralRunnerSetReconciler) actionsClientFor(ctx context.Context, rs *v1alpha1.EphemeralRunnerSet) (actions.ActionsService, error) {
506508
secret := new(corev1.Secret)
507-
if err := r.Get(ctx, types.NamespacedName{Namespace: rs.Namespace, Name: rs.Spec.EphemeralRunnerSpec.GitHubConfigSecret}, secret); err != nil {
509+
if err := r.Get(ctx, types.NamespacedName{Namespace: r.deriveConfigSecretNamespace(rs), Name: rs.Spec.EphemeralRunnerSpec.GitHubConfigSecret}, secret); err != nil {
508510
return nil, fmt.Errorf("failed to get secret: %w", err)
509511
}
510512

@@ -522,6 +524,14 @@ func (r *EphemeralRunnerSetReconciler) actionsClientFor(ctx context.Context, rs
522524
)
523525
}
524526

527+
func (r *EphemeralRunnerSetReconciler) deriveConfigSecretNamespace(rs *v1alpha1.EphemeralRunnerSet) string {
528+
secretNamespace := rs.Namespace
529+
if r.ReadGitHubConfigSecretFromControllerNamespace {
530+
secretNamespace = r.ControllerNamespace
531+
}
532+
return secretNamespace
533+
}
534+
525535
func (r *EphemeralRunnerSetReconciler) actionsClientOptionsFor(ctx context.Context, rs *v1alpha1.EphemeralRunnerSet) ([]actions.ClientOption, error) {
526536
var opts []actions.ClientOption
527537
if rs.Spec.EphemeralRunnerSpec.Proxy != nil {

controllers/actions.github.com/helpers_test.go

+24-16
Original file line numberDiff line numberDiff line change
@@ -35,29 +35,37 @@ func startManagers(t ginkgo.GinkgoTInterface, first manager.Manager, others ...m
3535
}
3636
}
3737

38-
func createNamespace(t ginkgo.GinkgoTInterface, client client.Client) (*corev1.Namespace, manager.Manager) {
39-
ns := &corev1.Namespace{
40-
ObjectMeta: metav1.ObjectMeta{Name: "testns-autoscaling" + RandStringRunes(5)},
41-
}
42-
43-
err := k8sClient.Create(context.Background(), ns)
44-
require.NoError(t, err)
38+
func createNamespaces(t ginkgo.GinkgoTInterface, numNamespaces int) ([]*corev1.Namespace, manager.Manager) {
39+
namespaces := make([]*corev1.Namespace, 0)
40+
defaultNamespaces := make(map[string]cache.Config)
41+
for range numNamespaces {
42+
ns := &corev1.Namespace{
43+
ObjectMeta: metav1.ObjectMeta{Name: "testns-autoscaling" + RandStringRunes(5)},
44+
}
45+
namespaces = append(namespaces, ns)
4546

46-
t.Cleanup(func() {
47-
err := k8sClient.Delete(context.Background(), ns)
47+
err := k8sClient.Create(context.Background(), ns)
4848
require.NoError(t, err)
49-
})
49+
50+
t.Cleanup(func() {
51+
err := k8sClient.Delete(context.Background(), ns)
52+
require.NoError(t, err)
53+
})
54+
55+
defaultNamespaces[ns.Name] = cache.Config{}
56+
}
5057

5158
mgr, err := ctrl.NewManager(cfg, ctrl.Options{
52-
Cache: cache.Options{
53-
DefaultNamespaces: map[string]cache.Config{
54-
ns.Name: {},
55-
},
56-
},
59+
Cache: cache.Options{DefaultNamespaces: defaultNamespaces},
5760
})
5861
require.NoError(t, err)
5962

60-
return ns, mgr
63+
return namespaces, mgr
64+
}
65+
66+
func createNamespace(t ginkgo.GinkgoTInterface, client client.Client) (*corev1.Namespace, manager.Manager) {
67+
namespaces, mgr := createNamespaces(t, 1)
68+
return namespaces[0], mgr
6169
}
6270

6371
func createDefaultSecret(t ginkgo.GinkgoTInterface, client client.Client, namespace string) *corev1.Secret {

0 commit comments

Comments
 (0)