diff --git a/charts/gha-runner-scale-set-controller/templates/_helpers.tpl b/charts/gha-runner-scale-set-controller/templates/_helpers.tpl index ce3409fa..ebef4a9b 100644 --- a/charts/gha-runner-scale-set-controller/templates/_helpers.tpl +++ b/charts/gha-runner-scale-set-controller/templates/_helpers.tpl @@ -72,12 +72,20 @@ Create the name of the service account to use {{- end }} {{- end }} -{{- define "gha-runner-scale-set-controller.managerRoleName" -}} -{{- include "gha-runner-scale-set-controller.fullname" . }}-manager-role +{{- define "gha-runner-scale-set-controller.managerClusterRoleName" -}} +{{- include "gha-runner-scale-set-controller.fullname" . }}-manager-cluster-role {{- end }} -{{- define "gha-runner-scale-set-controller.managerRoleBinding" -}} -{{- include "gha-runner-scale-set-controller.fullname" . }}-manager-rolebinding +{{- define "gha-runner-scale-set-controller.managerClusterRoleBinding" -}} +{{- include "gha-runner-scale-set-controller.fullname" . }}-manager-cluster-rolebinding +{{- end }} + +{{- define "gha-runner-scale-set-controller.managerListenerRoleName" -}} +{{- include "gha-runner-scale-set-controller.fullname" . }}-manager-listener-role +{{- end }} + +{{- define "gha-runner-scale-set-controller.managerListenerRoleBinding" -}} +{{- include "gha-runner-scale-set-controller.fullname" . }}-manager-listener-rolebinding {{- end }} {{- define "gha-runner-scale-set-controller.leaderElectionRoleName" -}} diff --git a/charts/gha-runner-scale-set-controller/templates/deployment.yaml b/charts/gha-runner-scale-set-controller/templates/deployment.yaml index cae43f4e..f509aa67 100644 --- a/charts/gha-runner-scale-set-controller/templates/deployment.yaml +++ b/charts/gha-runner-scale-set-controller/templates/deployment.yaml @@ -5,6 +5,8 @@ metadata: namespace: {{ .Release.Namespace }} labels: {{- include "gha-runner-scale-set-controller.labels" . | nindent 4 }} + actions.github.com/controller-service-account-namespace: {{ .Release.Namespace }} + actions.github.com/controller-service-account-name: {{ include "gha-runner-scale-set-controller.serviceAccountName" . }} spec: replicas: {{ default 1 .Values.replicaCount }} selector: diff --git a/charts/gha-runner-scale-set-controller/templates/manager_role.yaml b/charts/gha-runner-scale-set-controller/templates/manager_cluster_role.yaml similarity index 79% rename from charts/gha-runner-scale-set-controller/templates/manager_role.yaml rename to charts/gha-runner-scale-set-controller/templates/manager_cluster_role.yaml index e69d599f..deb3c999 100644 --- a/charts/gha-runner-scale-set-controller/templates/manager_role.yaml +++ b/charts/gha-runner-scale-set-controller/templates/manager_cluster_role.yaml @@ -1,7 +1,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - name: {{ include "gha-runner-scale-set-controller.managerRoleName" . }} + name: {{ include "gha-runner-scale-set-controller.managerClusterRoleName" . }} rules: - apiGroups: - actions.github.com @@ -112,43 +112,13 @@ rules: resources: - pods verbs: - - create - - delete - - get - list - - patch - - update - watch -- apiGroups: - - "" - resources: - - pods/status - verbs: - - get -- apiGroups: - - "" - resources: - - secrets - verbs: - - create - - delete - - get - - update - apiGroups: - "" resources: - serviceaccounts verbs: - - create - - delete - - get - - list - - watch -- apiGroups: - - "" - resources: - - configmaps - verbs: - list - watch - apiGroups: @@ -156,10 +126,6 @@ rules: resources: - rolebindings verbs: - - create - - delete - - get - - update - list - watch - apiGroups: @@ -167,9 +133,5 @@ rules: resources: - roles verbs: - - create - - delete - - get - - update - list - watch diff --git a/charts/gha-runner-scale-set-controller/templates/manager_role_binding.yaml b/charts/gha-runner-scale-set-controller/templates/manager_cluster_role_binding.yaml similarity index 55% rename from charts/gha-runner-scale-set-controller/templates/manager_role_binding.yaml rename to charts/gha-runner-scale-set-controller/templates/manager_cluster_role_binding.yaml index 72549d6a..4ce8f9b8 100644 --- a/charts/gha-runner-scale-set-controller/templates/manager_role_binding.yaml +++ b/charts/gha-runner-scale-set-controller/templates/manager_cluster_role_binding.yaml @@ -1,11 +1,11 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: - name: {{ include "gha-runner-scale-set-controller.managerRoleBinding" . }} + name: {{ include "gha-runner-scale-set-controller.managerClusterRoleBinding" . }} roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole - name: {{ include "gha-runner-scale-set-controller.managerRoleName" . }} + name: {{ include "gha-runner-scale-set-controller.managerClusterRoleName" . }} subjects: - kind: ServiceAccount name: {{ include "gha-runner-scale-set-controller.serviceAccountName" . }} diff --git a/charts/gha-runner-scale-set-controller/templates/manager_listener_role.yaml b/charts/gha-runner-scale-set-controller/templates/manager_listener_role.yaml new file mode 100644 index 00000000..86a93777 --- /dev/null +++ b/charts/gha-runner-scale-set-controller/templates/manager_listener_role.yaml @@ -0,0 +1,40 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ include "gha-runner-scale-set-controller.managerListenerRoleName" . }} + namespace: {{ .Release.Namespace }} +rules: +- apiGroups: + - "" + resources: + - pods + verbs: + - create + - delete + - get +- apiGroups: + - "" + resources: + - pods/status + verbs: + - get +- apiGroups: + - "" + resources: + - secrets + verbs: + - create + - delete + - get + - patch + - update +- apiGroups: + - "" + resources: + - serviceaccounts + verbs: + - create + - delete + - get + - patch + - update \ No newline at end of file diff --git a/charts/gha-runner-scale-set-controller/templates/manager_listener_role_binding.yaml b/charts/gha-runner-scale-set-controller/templates/manager_listener_role_binding.yaml new file mode 100644 index 00000000..8a2f7f95 --- /dev/null +++ b/charts/gha-runner-scale-set-controller/templates/manager_listener_role_binding.yaml @@ -0,0 +1,13 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "gha-runner-scale-set-controller.managerListenerRoleBinding" . }} + namespace: {{ .Release.Namespace }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "gha-runner-scale-set-controller.managerListenerRoleName" . }} +subjects: +- kind: ServiceAccount + name: {{ include "gha-runner-scale-set-controller.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} \ No newline at end of file diff --git a/charts/gha-runner-scale-set-controller/tests/template_test.go b/charts/gha-runner-scale-set-controller/tests/template_test.go index fe4bf020..b954867e 100644 --- a/charts/gha-runner-scale-set-controller/tests/template_test.go +++ b/charts/gha-runner-scale-set-controller/tests/template_test.go @@ -147,7 +147,7 @@ func TestTemplate_NotCreateServiceAccount_ServiceAccountNotSet(t *testing.T) { assert.ErrorContains(t, err, "serviceAccount.name must be set if serviceAccount.create is false", "We should get an error because the default service account cannot be used") } -func TestTemplate_CreateManagerRole(t *testing.T) { +func TestTemplate_CreateManagerClusterRole(t *testing.T) { t.Parallel() // Path to the helm chart we will test @@ -162,17 +162,17 @@ func TestTemplate_CreateManagerRole(t *testing.T) { KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } - output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/manager_role.yaml"}) + output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/manager_cluster_role.yaml"}) - var managerRole rbacv1.ClusterRole - helm.UnmarshalK8SYaml(t, output, &managerRole) + var managerClusterRole rbacv1.ClusterRole + helm.UnmarshalK8SYaml(t, output, &managerClusterRole) - assert.Empty(t, managerRole.Namespace, "ClusterRole should not have a namespace") - assert.Equal(t, "test-arc-gha-runner-scale-set-controller-manager-role", managerRole.Name) - assert.Equal(t, 18, len(managerRole.Rules)) + assert.Empty(t, managerClusterRole.Namespace, "ClusterRole should not have a namespace") + assert.Equal(t, "test-arc-gha-runner-scale-set-controller-manager-cluster-role", managerClusterRole.Name) + assert.Equal(t, 15, len(managerClusterRole.Rules)) } -func TestTemplate_ManagerRoleBinding(t *testing.T) { +func TestTemplate_ManagerClusterRoleBinding(t *testing.T) { t.Parallel() // Path to the helm chart we will test @@ -189,16 +189,74 @@ func TestTemplate_ManagerRoleBinding(t *testing.T) { KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } - output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/manager_role_binding.yaml"}) + output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/manager_cluster_role_binding.yaml"}) - var managerRoleBinding rbacv1.ClusterRoleBinding - helm.UnmarshalK8SYaml(t, output, &managerRoleBinding) + var managerClusterRoleBinding rbacv1.ClusterRoleBinding + helm.UnmarshalK8SYaml(t, output, &managerClusterRoleBinding) - assert.Empty(t, managerRoleBinding.Namespace, "ClusterRoleBinding should not have a namespace") - assert.Equal(t, "test-arc-gha-runner-scale-set-controller-manager-rolebinding", managerRoleBinding.Name) - assert.Equal(t, "test-arc-gha-runner-scale-set-controller-manager-role", managerRoleBinding.RoleRef.Name) - assert.Equal(t, "test-arc-gha-runner-scale-set-controller", managerRoleBinding.Subjects[0].Name) - assert.Equal(t, namespaceName, managerRoleBinding.Subjects[0].Namespace) + assert.Empty(t, managerClusterRoleBinding.Namespace, "ClusterRoleBinding should not have a namespace") + assert.Equal(t, "test-arc-gha-runner-scale-set-controller-manager-cluster-rolebinding", managerClusterRoleBinding.Name) + assert.Equal(t, "test-arc-gha-runner-scale-set-controller-manager-cluster-role", managerClusterRoleBinding.RoleRef.Name) + assert.Equal(t, "test-arc-gha-runner-scale-set-controller", managerClusterRoleBinding.Subjects[0].Name) + assert.Equal(t, namespaceName, managerClusterRoleBinding.Subjects[0].Namespace) +} + +func TestTemplate_CreateManagerListenerRole(t *testing.T) { + t.Parallel() + + // Path to the helm chart we will test + helmChartPath, err := filepath.Abs("../../gha-runner-scale-set-controller") + require.NoError(t, err) + + releaseName := "test-arc" + namespaceName := "test-" + strings.ToLower(random.UniqueId()) + + options := &helm.Options{ + SetValues: map[string]string{}, + KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), + } + + output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/manager_listener_role.yaml"}) + + var managerListenerRole rbacv1.Role + helm.UnmarshalK8SYaml(t, output, &managerListenerRole) + + assert.Equal(t, namespaceName, managerListenerRole.Namespace, "Role should have a namespace") + assert.Equal(t, "test-arc-gha-runner-scale-set-controller-manager-listener-role", managerListenerRole.Name) + assert.Equal(t, 4, len(managerListenerRole.Rules)) + assert.Equal(t, "pods", managerListenerRole.Rules[0].Resources[0]) + assert.Equal(t, "pods/status", managerListenerRole.Rules[1].Resources[0]) + assert.Equal(t, "secrets", managerListenerRole.Rules[2].Resources[0]) + assert.Equal(t, "serviceaccounts", managerListenerRole.Rules[3].Resources[0]) +} + +func TestTemplate_ManagerListenerRoleBinding(t *testing.T) { + t.Parallel() + + // Path to the helm chart we will test + helmChartPath, err := filepath.Abs("../../gha-runner-scale-set-controller") + require.NoError(t, err) + + releaseName := "test-arc" + namespaceName := "test-" + strings.ToLower(random.UniqueId()) + + options := &helm.Options{ + SetValues: map[string]string{ + "serviceAccount.create": "true", + }, + KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), + } + + output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/manager_listener_role_binding.yaml"}) + + var managerListenerRoleBinding rbacv1.RoleBinding + helm.UnmarshalK8SYaml(t, output, &managerListenerRoleBinding) + + assert.Equal(t, namespaceName, managerListenerRoleBinding.Namespace, "RoleBinding should have a namespace") + assert.Equal(t, "test-arc-gha-runner-scale-set-controller-manager-listener-rolebinding", managerListenerRoleBinding.Name) + assert.Equal(t, "test-arc-gha-runner-scale-set-controller-manager-listener-role", managerListenerRoleBinding.RoleRef.Name) + assert.Equal(t, "test-arc-gha-runner-scale-set-controller", managerListenerRoleBinding.Subjects[0].Name) + assert.Equal(t, namespaceName, managerListenerRoleBinding.Subjects[0].Namespace) } func TestTemplate_ControllerDeployment_Defaults(t *testing.T) { @@ -237,6 +295,8 @@ func TestTemplate_ControllerDeployment_Defaults(t *testing.T) { assert.Equal(t, "test-arc", deployment.Labels["app.kubernetes.io/instance"]) assert.Equal(t, chart.AppVersion, deployment.Labels["app.kubernetes.io/version"]) assert.Equal(t, "Helm", deployment.Labels["app.kubernetes.io/managed-by"]) + assert.Equal(t, namespaceName, deployment.Labels["actions.github.com/controller-service-account-namespace"]) + assert.Equal(t, "test-arc-gha-runner-scale-set-controller", deployment.Labels["actions.github.com/controller-service-account-name"]) assert.Equal(t, int32(1), *deployment.Spec.Replicas) diff --git a/charts/gha-runner-scale-set/templates/_helpers.tpl b/charts/gha-runner-scale-set/templates/_helpers.tpl index 8511de07..208e42ea 100644 --- a/charts/gha-runner-scale-set/templates/_helpers.tpl +++ b/charts/gha-runner-scale-set/templates/_helpers.tpl @@ -458,3 +458,83 @@ volumeMounts: {{- end }} {{- end }} {{- end }} + +{{- define "gha-runner-scale-set.managerRoleName" -}} +{{- include "gha-runner-scale-set.fullname" . }}-manager-role +{{- end }} + +{{- define "gha-runner-scale-set.managerRoleBinding" -}} +{{- include "gha-runner-scale-set.fullname" . }}-manager-role-binding +{{- end }} + +{{- define "gha-runner-scale-set.managerServiceAccountName" -}} +{{- $searchControllerDeployment := 1 }} +{{- if .Values.controllerServiceAccount }} + {{- if .Values.controllerServiceAccount.name }} + {{- $searchControllerDeployment = 0 }} +{{- .Values.controllerServiceAccount.name }} + {{- end }} +{{- end }} +{{- if eq $searchControllerDeployment 1 }} + {{- $counter := 0 }} + {{- $controllerDeployment := dict }} + {{- $managerServiceAccountName := "" }} + {{- range $index, $deployment := (lookup "apps/v1" "Deployment" "" "").items }} + {{- range $key, $val := $deployment.metadata.labels }} + {{- if and (eq $key "app.kubernetes.io/part-of") (eq $val "gha-runner-scale-set-controller") }} + {{- $counter = add $counter 1 }} + {{- $controllerDeployment = $deployment }} + {{- end }} + {{- end }} + {{- end }} + {{- if lt $counter 1 }} + {{- fail "No gha-runner-scale-set-controller deployment found using label (app.kubernetes.io/part-of=gha-runner-scale-set-controller), consider setting controllerServiceAccount.name in values.yaml to be explicit if you think the discovery is wrong." }} + {{- end }} + {{- if gt $counter 1 }} + {{- fail "More than one gha-runner-scale-set-controller deployment found using label (app.kubernetes.io/part-of=gha-runner-scale-set-controller), consider setting controllerServiceAccount.name in values.yaml to be explicit if you think the discovery is wrong." }} + {{- end }} + {{- with $controllerDeployment.metadata }} + {{- $managerServiceAccountName = (get $controllerDeployment.metadata.labels "actions.github.com/controller-service-account-name") }} + {{- end }} + {{- if eq $managerServiceAccountName "" }} + {{- fail "No service account name found for gha-runner-scale-set-controller deployment using label (actions.github.com/controller-service-account-name), consider setting controllerServiceAccount.name in values.yaml to be explicit if you think the discovery is wrong." }} + {{- end }} +{{- $managerServiceAccountName }} +{{- end }} +{{- end }} + +{{- define "gha-runner-scale-set.managerServiceAccountNamespace" -}} +{{- $searchControllerDeployment := 1 }} +{{- if .Values.controllerServiceAccount }} + {{- if .Values.controllerServiceAccount.namespace }} + {{- $searchControllerDeployment = 0 }} +{{- .Values.controllerServiceAccount.namespace }} + {{- end }} +{{- end }} +{{- if eq $searchControllerDeployment 1 }} + {{- $counter := 0 }} + {{- $controllerDeployment := dict }} + {{- $managerServiceAccountNamespace := "" }} + {{- range $index, $deployment := (lookup "apps/v1" "Deployment" "" "").items }} + {{- range $key, $val := $deployment.metadata.labels }} + {{- if and (eq $key "app.kubernetes.io/part-of") (eq $val "gha-runner-scale-set-controller") }} + {{- $counter = add $counter 1 }} + {{- $controllerDeployment = $deployment }} + {{- end }} + {{- end }} + {{- end }} + {{- if lt $counter 1 }} + {{- fail "No gha-runner-scale-set-controller deployment found using label (app.kubernetes.io/part-of=gha-runner-scale-set-controller), consider setting controllerServiceAccount.name to be explicit if you think the discovery is wrong." }} + {{- end }} + {{- if gt $counter 1 }} + {{- fail "More than one gha-runner-scale-set-controller deployment found using label (app.kubernetes.io/part-of=gha-runner-scale-set-controller), consider setting controllerServiceAccount.name to be explicit if you think the discovery is wrong." }} + {{- end }} + {{- with $controllerDeployment.metadata }} + {{- $managerServiceAccountNamespace = (get $controllerDeployment.metadata.labels "actions.github.com/controller-service-account-namespace") }} + {{- end }} + {{- if eq $managerServiceAccountNamespace "" }} + {{- fail "No service account namespace found for gha-runner-scale-set-controller deployment using label (actions.github.com/controller-service-account-namespace), consider setting controllerServiceAccount.name to be explicit if you think the discovery is wrong." }} + {{- end }} +{{- $managerServiceAccountNamespace }} +{{- end }} +{{- end }} diff --git a/charts/gha-runner-scale-set/templates/manager_role.yaml b/charts/gha-runner-scale-set/templates/manager_role.yaml new file mode 100644 index 00000000..6f4cd9a6 --- /dev/null +++ b/charts/gha-runner-scale-set/templates/manager_role.yaml @@ -0,0 +1,59 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ include "gha-runner-scale-set.managerRoleName" . }} + namespace: {{ .Release.Namespace }} +rules: +- apiGroups: + - "" + resources: + - pods + verbs: + - create + - delete + - get +- apiGroups: + - "" + resources: + - pods/status + verbs: + - get +- apiGroups: + - "" + resources: + - secrets + verbs: + - create + - delete + - get + - list + - patch + - update +- apiGroups: + - rbac.authorization.k8s.io + resources: + - rolebindings + verbs: + - create + - delete + - get + - patch + - update +- apiGroups: + - rbac.authorization.k8s.io + resources: + - roles + verbs: + - create + - delete + - get + - patch + - update +{{- if .Values.githubServerTLS }} +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get +{{- end }} \ No newline at end of file diff --git a/charts/gha-runner-scale-set/templates/manager_role_binding.yaml b/charts/gha-runner-scale-set/templates/manager_role_binding.yaml new file mode 100644 index 00000000..ba38ab0f --- /dev/null +++ b/charts/gha-runner-scale-set/templates/manager_role_binding.yaml @@ -0,0 +1,13 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "gha-runner-scale-set.managerRoleBinding" . }} + namespace: {{ .Release.Namespace }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "gha-runner-scale-set.managerRoleName" . }} +subjects: +- kind: ServiceAccount + name: {{ include "gha-runner-scale-set.managerServiceAccountName" . | nindent 4 }} + namespace: {{ include "gha-runner-scale-set.managerServiceAccountNamespace" . | nindent 4 }} \ No newline at end of file diff --git a/charts/gha-runner-scale-set/tests/template_test.go b/charts/gha-runner-scale-set/tests/template_test.go index 864e63e8..24a08326 100644 --- a/charts/gha-runner-scale-set/tests/template_test.go +++ b/charts/gha-runner-scale-set/tests/template_test.go @@ -27,8 +27,10 @@ func TestTemplateRenderedGitHubSecretWithGitHubToken(t *testing.T) { options := &helm.Options{ SetValues: map[string]string{ - "githubConfigUrl": "https://github.com/actions", - "githubConfigSecret.github_token": "gh_token12345", + "githubConfigUrl": "https://github.com/actions", + "githubConfigSecret.github_token": "gh_token12345", + "controllerServiceAccount.name": "arc", + "controllerServiceAccount.namespace": "arc-system", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } @@ -60,6 +62,8 @@ func TestTemplateRenderedGitHubSecretWithGitHubApp(t *testing.T) { "githubConfigSecret.github_app_id": "10", "githubConfigSecret.github_app_installation_id": "100", "githubConfigSecret.github_app_private_key": "private_key", + "controllerServiceAccount.name": "arc", + "controllerServiceAccount.namespace": "arc-system", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } @@ -87,9 +91,11 @@ func TestTemplateRenderedGitHubSecretErrorWithMissingAuthInput(t *testing.T) { options := &helm.Options{ SetValues: map[string]string{ - "githubConfigUrl": "https://github.com/actions", - "githubConfigSecret.github_app_id": "", - "githubConfigSecret.github_token": "", + "githubConfigUrl": "https://github.com/actions", + "githubConfigSecret.github_app_id": "", + "githubConfigSecret.github_token": "", + "controllerServiceAccount.name": "arc", + "controllerServiceAccount.namespace": "arc-system", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } @@ -112,8 +118,10 @@ func TestTemplateRenderedGitHubSecretErrorWithMissingAppInput(t *testing.T) { options := &helm.Options{ SetValues: map[string]string{ - "githubConfigUrl": "https://github.com/actions", - "githubConfigSecret.github_app_id": "10", + "githubConfigUrl": "https://github.com/actions", + "githubConfigSecret.github_app_id": "10", + "controllerServiceAccount.name": "arc", + "controllerServiceAccount.namespace": "arc-system", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } @@ -136,8 +144,10 @@ func TestTemplateNotRenderedGitHubSecretWithPredefinedSecret(t *testing.T) { options := &helm.Options{ SetValues: map[string]string{ - "githubConfigUrl": "https://github.com/actions", - "githubConfigSecret": "pre-defined-secret", + "githubConfigUrl": "https://github.com/actions", + "githubConfigSecret": "pre-defined-secret", + "controllerServiceAccount.name": "arc", + "controllerServiceAccount.namespace": "arc-system", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } @@ -158,8 +168,10 @@ func TestTemplateRenderedSetServiceAccountToNoPermission(t *testing.T) { options := &helm.Options{ SetValues: map[string]string{ - "githubConfigUrl": "https://github.com/actions", - "githubConfigSecret.github_token": "gh_token12345", + "githubConfigUrl": "https://github.com/actions", + "githubConfigSecret.github_token": "gh_token12345", + "controllerServiceAccount.name": "arc", + "controllerServiceAccount.namespace": "arc-system", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } @@ -190,9 +202,11 @@ func TestTemplateRenderedSetServiceAccountToKubeMode(t *testing.T) { options := &helm.Options{ SetValues: map[string]string{ - "githubConfigUrl": "https://github.com/actions", - "githubConfigSecret.github_token": "gh_token12345", - "containerMode.type": "kubernetes", + "githubConfigUrl": "https://github.com/actions", + "githubConfigSecret.github_token": "gh_token12345", + "containerMode.type": "kubernetes", + "controllerServiceAccount.name": "arc", + "controllerServiceAccount.namespace": "arc-system", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } @@ -248,9 +262,11 @@ func TestTemplateRenderedUserProvideSetServiceAccount(t *testing.T) { options := &helm.Options{ SetValues: map[string]string{ - "githubConfigUrl": "https://github.com/actions", - "githubConfigSecret.github_token": "gh_token12345", - "template.spec.serviceAccountName": "test-service-account", + "githubConfigUrl": "https://github.com/actions", + "githubConfigSecret.github_token": "gh_token12345", + "template.spec.serviceAccountName": "test-service-account", + "controllerServiceAccount.name": "arc", + "controllerServiceAccount.namespace": "arc-system", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } @@ -277,8 +293,10 @@ func TestTemplateRenderedAutoScalingRunnerSet(t *testing.T) { options := &helm.Options{ SetValues: map[string]string{ - "githubConfigUrl": "https://github.com/actions", - "githubConfigSecret.github_token": "gh_token12345", + "githubConfigUrl": "https://github.com/actions", + "githubConfigSecret.github_token": "gh_token12345", + "controllerServiceAccount.name": "arc", + "controllerServiceAccount.namespace": "arc-system", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } @@ -322,9 +340,11 @@ func TestTemplateRenderedAutoScalingRunnerSet_RunnerScaleSetName(t *testing.T) { options := &helm.Options{ SetValues: map[string]string{ - "githubConfigUrl": "https://github.com/actions", - "githubConfigSecret.github_token": "gh_token12345", - "runnerScaleSetName": "test-runner-scale-set-name", + "githubConfigUrl": "https://github.com/actions", + "githubConfigSecret.github_token": "gh_token12345", + "runnerScaleSetName": "test-runner-scale-set-name", + "controllerServiceAccount.name": "arc", + "controllerServiceAccount.namespace": "arc-system", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } @@ -375,6 +395,8 @@ func TestTemplateRenderedAutoScalingRunnerSet_ProvideMetadata(t *testing.T) { "template.metadata.labels.test2": "test2", "template.metadata.annotations.test3": "test3", "template.metadata.annotations.test4": "test4", + "controllerServiceAccount.name": "arc", + "controllerServiceAccount.namespace": "arc-system", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } @@ -414,9 +436,11 @@ func TestTemplateRenderedAutoScalingRunnerSet_MaxRunnersValidationError(t *testi options := &helm.Options{ SetValues: map[string]string{ - "githubConfigUrl": "https://github.com/actions", - "githubConfigSecret.github_token": "gh_token12345", - "maxRunners": "-1", + "githubConfigUrl": "https://github.com/actions", + "githubConfigSecret.github_token": "gh_token12345", + "maxRunners": "-1", + "controllerServiceAccount.name": "arc", + "controllerServiceAccount.namespace": "arc-system", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } @@ -439,10 +463,12 @@ func TestTemplateRenderedAutoScalingRunnerSet_MinRunnersValidationError(t *testi options := &helm.Options{ SetValues: map[string]string{ - "githubConfigUrl": "https://github.com/actions", - "githubConfigSecret.github_token": "gh_token12345", - "maxRunners": "1", - "minRunners": "-1", + "githubConfigUrl": "https://github.com/actions", + "githubConfigSecret.github_token": "gh_token12345", + "maxRunners": "1", + "minRunners": "-1", + "controllerServiceAccount.name": "arc", + "controllerServiceAccount.namespace": "arc-system", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } @@ -465,10 +491,12 @@ func TestTemplateRenderedAutoScalingRunnerSet_MinMaxRunnersValidationError(t *te options := &helm.Options{ SetValues: map[string]string{ - "githubConfigUrl": "https://github.com/actions", - "githubConfigSecret.github_token": "gh_token12345", - "maxRunners": "0", - "minRunners": "1", + "githubConfigUrl": "https://github.com/actions", + "githubConfigSecret.github_token": "gh_token12345", + "maxRunners": "0", + "minRunners": "1", + "controllerServiceAccount.name": "arc", + "controllerServiceAccount.namespace": "arc-system", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } @@ -491,10 +519,12 @@ func TestTemplateRenderedAutoScalingRunnerSet_MinMaxRunnersValidationSameValue(t options := &helm.Options{ SetValues: map[string]string{ - "githubConfigUrl": "https://github.com/actions", - "githubConfigSecret.github_token": "gh_token12345", - "maxRunners": "0", - "minRunners": "0", + "githubConfigUrl": "https://github.com/actions", + "githubConfigSecret.github_token": "gh_token12345", + "maxRunners": "0", + "minRunners": "0", + "controllerServiceAccount.name": "arc", + "controllerServiceAccount.namespace": "arc-system", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } @@ -520,9 +550,11 @@ func TestTemplateRenderedAutoScalingRunnerSet_MinMaxRunnersValidation_OnlyMin(t options := &helm.Options{ SetValues: map[string]string{ - "githubConfigUrl": "https://github.com/actions", - "githubConfigSecret.github_token": "gh_token12345", - "minRunners": "5", + "githubConfigUrl": "https://github.com/actions", + "githubConfigSecret.github_token": "gh_token12345", + "minRunners": "5", + "controllerServiceAccount.name": "arc", + "controllerServiceAccount.namespace": "arc-system", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } @@ -548,9 +580,11 @@ func TestTemplateRenderedAutoScalingRunnerSet_MinMaxRunnersValidation_OnlyMax(t options := &helm.Options{ SetValues: map[string]string{ - "githubConfigUrl": "https://github.com/actions", - "githubConfigSecret.github_token": "gh_token12345", - "maxRunners": "5", + "githubConfigUrl": "https://github.com/actions", + "githubConfigSecret.github_token": "gh_token12345", + "maxRunners": "5", + "controllerServiceAccount.name": "arc", + "controllerServiceAccount.namespace": "arc-system", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } @@ -605,6 +639,10 @@ func TestTemplateRenderedAutoScalingRunnerSet_ExtraVolumes(t *testing.T) { namespaceName := "test-" + strings.ToLower(random.UniqueId()) options := &helm.Options{ + SetValues: map[string]string{ + "controllerServiceAccount.name": "arc", + "controllerServiceAccount.namespace": "arc-system", + }, ValuesFiles: []string{testValuesPath}, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } @@ -635,6 +673,10 @@ func TestTemplateRenderedAutoScalingRunnerSet_DinD_ExtraVolumes(t *testing.T) { namespaceName := "test-" + strings.ToLower(random.UniqueId()) options := &helm.Options{ + SetValues: map[string]string{ + "controllerServiceAccount.name": "arc", + "controllerServiceAccount.namespace": "arc-system", + }, ValuesFiles: []string{testValuesPath}, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } @@ -667,6 +709,10 @@ func TestTemplateRenderedAutoScalingRunnerSet_K8S_ExtraVolumes(t *testing.T) { namespaceName := "test-" + strings.ToLower(random.UniqueId()) options := &helm.Options{ + SetValues: map[string]string{ + "controllerServiceAccount.name": "arc", + "controllerServiceAccount.namespace": "arc-system", + }, ValuesFiles: []string{testValuesPath}, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } @@ -695,9 +741,11 @@ func TestTemplateRenderedAutoScalingRunnerSet_EnableDinD(t *testing.T) { options := &helm.Options{ SetValues: map[string]string{ - "githubConfigUrl": "https://github.com/actions", - "githubConfigSecret.github_token": "gh_token12345", - "containerMode.type": "dind", + "githubConfigUrl": "https://github.com/actions", + "githubConfigSecret.github_token": "gh_token12345", + "containerMode.type": "dind", + "controllerServiceAccount.name": "arc", + "controllerServiceAccount.namespace": "arc-system", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } @@ -784,9 +832,11 @@ func TestTemplateRenderedAutoScalingRunnerSet_EnableKubernetesMode(t *testing.T) options := &helm.Options{ SetValues: map[string]string{ - "githubConfigUrl": "https://github.com/actions", - "githubConfigSecret.github_token": "gh_token12345", - "containerMode.type": "kubernetes", + "githubConfigUrl": "https://github.com/actions", + "githubConfigSecret.github_token": "gh_token12345", + "containerMode.type": "kubernetes", + "controllerServiceAccount.name": "arc", + "controllerServiceAccount.namespace": "arc-system", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } @@ -839,8 +889,10 @@ func TestTemplateRenderedAutoScalingRunnerSet_UsePredefinedSecret(t *testing.T) options := &helm.Options{ SetValues: map[string]string{ - "githubConfigUrl": "https://github.com/actions", - "githubConfigSecret": "pre-defined-secrets", + "githubConfigUrl": "https://github.com/actions", + "githubConfigSecret": "pre-defined-secrets", + "controllerServiceAccount.name": "arc", + "controllerServiceAccount.namespace": "arc-system", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } @@ -871,8 +923,10 @@ func TestTemplateRenderedAutoScalingRunnerSet_ErrorOnEmptyPredefinedSecret(t *te options := &helm.Options{ SetValues: map[string]string{ - "githubConfigUrl": "https://github.com/actions", - "githubConfigSecret": "", + "githubConfigUrl": "https://github.com/actions", + "githubConfigSecret": "", + "controllerServiceAccount.name": "arc", + "controllerServiceAccount.namespace": "arc-system", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } @@ -895,13 +949,15 @@ func TestTemplateRenderedWithProxy(t *testing.T) { options := &helm.Options{ SetValues: map[string]string{ - "githubConfigUrl": "https://github.com/actions", - "githubConfigSecret": "pre-defined-secrets", - "proxy.http.url": "http://proxy.example.com", - "proxy.http.credentialSecretRef": "http-secret", - "proxy.https.url": "https://proxy.example.com", - "proxy.https.credentialSecretRef": "https-secret", - "proxy.noProxy": "{example.com,example.org}", + "githubConfigUrl": "https://github.com/actions", + "githubConfigSecret": "pre-defined-secrets", + "controllerServiceAccount.name": "arc", + "controllerServiceAccount.namespace": "arc-system", + "proxy.http.url": "http://proxy.example.com", + "proxy.http.credentialSecretRef": "http-secret", + "proxy.https.url": "https://proxy.example.com", + "proxy.https.credentialSecretRef": "https-secret", + "proxy.noProxy": "{example.com,example.org}", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } @@ -961,6 +1017,8 @@ func TestTemplateRenderedWithTLS(t *testing.T) { "githubServerTLS.certificateFrom.configMapKeyRef.name": "certs-configmap", "githubServerTLS.certificateFrom.configMapKeyRef.key": "cert.pem", "githubServerTLS.runnerMountPath": "/runner/mount/path", + "controllerServiceAccount.name": "arc", + "controllerServiceAccount.namespace": "arc-system", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } @@ -1018,6 +1076,8 @@ func TestTemplateRenderedWithTLS(t *testing.T) { "githubServerTLS.certificateFrom.configMapKeyRef.key": "cert.pem", "githubServerTLS.runnerMountPath": "/runner/mount/path/", "containerMode.type": "dind", + "controllerServiceAccount.name": "arc", + "controllerServiceAccount.namespace": "arc-system", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } @@ -1075,6 +1135,8 @@ func TestTemplateRenderedWithTLS(t *testing.T) { "githubServerTLS.certificateFrom.configMapKeyRef.key": "cert.pem", "githubServerTLS.runnerMountPath": "/runner/mount/path", "containerMode.type": "kubernetes", + "controllerServiceAccount.name": "arc", + "controllerServiceAccount.namespace": "arc-system", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } @@ -1132,6 +1194,8 @@ func TestTemplateRenderedWithTLS(t *testing.T) { "githubConfigSecret": "pre-defined-secrets", "githubServerTLS.certificateFrom.configMapKeyRef.name": "certs-configmap", "githubServerTLS.certificateFrom.configMapKeyRef.key": "cert.pem", + "controllerServiceAccount.name": "arc", + "controllerServiceAccount.namespace": "arc-system", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } @@ -1184,7 +1248,9 @@ func TestTemplateRenderedWithTLS(t *testing.T) { "githubConfigSecret": "pre-defined-secrets", "githubServerTLS.certificateFrom.configMapKeyRef.name": "certs-configmap", "githubServerTLS.certificateFrom.configMapKeyRef.key": "cert.pem", - "containerMode.type": "dind", + "containerMode.type": "dind", + "controllerServiceAccount.name": "arc", + "controllerServiceAccount.namespace": "arc-system", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } @@ -1237,7 +1303,9 @@ func TestTemplateRenderedWithTLS(t *testing.T) { "githubConfigSecret": "pre-defined-secrets", "githubServerTLS.certificateFrom.configMapKeyRef.name": "certs-configmap", "githubServerTLS.certificateFrom.configMapKeyRef.key": "cert.pem", - "containerMode.type": "kubernetes", + "containerMode.type": "kubernetes", + "controllerServiceAccount.name": "arc", + "controllerServiceAccount.namespace": "arc-system", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } @@ -1293,8 +1361,10 @@ func TestTemplateNamingConstraints(t *testing.T) { require.NoError(t, err) setValues := map[string]string{ - "githubConfigUrl": "https://github.com/actions", - "githubConfigSecret": "", + "githubConfigUrl": "https://github.com/actions", + "githubConfigSecret": "", + "controllerServiceAccount.name": "arc", + "controllerServiceAccount.namespace": "arc-system", } tt := map[string]struct { @@ -1339,8 +1409,10 @@ func TestTemplateRenderedGitHubConfigUrlEndsWIthSlash(t *testing.T) { options := &helm.Options{ SetValues: map[string]string{ - "githubConfigUrl": "https://github.com/actions/", - "githubConfigSecret.github_token": "gh_token12345", + "githubConfigUrl": "https://github.com/actions/", + "githubConfigSecret.github_token": "gh_token12345", + "controllerServiceAccount.name": "arc", + "controllerServiceAccount.namespace": "arc-system", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } @@ -1354,3 +1426,97 @@ func TestTemplateRenderedGitHubConfigUrlEndsWIthSlash(t *testing.T) { assert.Equal(t, "test-runners", ars.Name) assert.Equal(t, "https://github.com/actions", ars.Spec.GitHubConfigUrl) } + +func TestTemplate_CreateManagerRole(t *testing.T) { + t.Parallel() + + // Path to the helm chart we will test + helmChartPath, err := filepath.Abs("../../gha-runner-scale-set") + require.NoError(t, err) + + releaseName := "test-runners" + namespaceName := "test-" + strings.ToLower(random.UniqueId()) + + options := &helm.Options{ + SetValues: map[string]string{ + "githubConfigUrl": "https://github.com/actions", + "githubConfigSecret.github_token": "gh_token12345", + "controllerServiceAccount.name": "arc", + "controllerServiceAccount.namespace": "arc-system", + }, + KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), + } + + output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/manager_role.yaml"}) + + var managerRole rbacv1.Role + helm.UnmarshalK8SYaml(t, output, &managerRole) + + assert.Equal(t, namespaceName, managerRole.Namespace, "namespace should match the namespace of the Helm release") + assert.Equal(t, "test-runners-gha-runner-scale-set-manager-role", managerRole.Name) + assert.Equal(t, 5, len(managerRole.Rules)) +} + +func TestTemplate_CreateManagerRole_UseConfigMaps(t *testing.T) { + t.Parallel() + + // Path to the helm chart we will test + helmChartPath, err := filepath.Abs("../../gha-runner-scale-set") + require.NoError(t, err) + + releaseName := "test-runners" + namespaceName := "test-" + strings.ToLower(random.UniqueId()) + + options := &helm.Options{ + SetValues: map[string]string{ + "githubConfigUrl": "https://github.com/actions", + "githubConfigSecret.github_token": "gh_token12345", + "controllerServiceAccount.name": "arc", + "controllerServiceAccount.namespace": "arc-system", + "githubServerTLS.certificateFrom.configMapKeyRef.name": "test", + }, + KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), + } + + output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/manager_role.yaml"}) + + var managerRole rbacv1.Role + helm.UnmarshalK8SYaml(t, output, &managerRole) + + assert.Equal(t, namespaceName, managerRole.Namespace, "namespace should match the namespace of the Helm release") + assert.Equal(t, "test-runners-gha-runner-scale-set-manager-role", managerRole.Name) + assert.Equal(t, 6, len(managerRole.Rules)) + assert.Equal(t, "configmaps", managerRole.Rules[5].Resources[0]) +} + +func TestTemplate_CreateManagerRoleBinding(t *testing.T) { + t.Parallel() + + // Path to the helm chart we will test + helmChartPath, err := filepath.Abs("../../gha-runner-scale-set") + require.NoError(t, err) + + releaseName := "test-runners" + namespaceName := "test-" + strings.ToLower(random.UniqueId()) + + options := &helm.Options{ + SetValues: map[string]string{ + "githubConfigUrl": "https://github.com/actions", + "githubConfigSecret.github_token": "gh_token12345", + "controllerServiceAccount.name": "arc", + "controllerServiceAccount.namespace": "arc-system", + }, + KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), + } + + output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/manager_role_binding.yaml"}) + + var managerRoleBinding rbacv1.RoleBinding + helm.UnmarshalK8SYaml(t, output, &managerRoleBinding) + + assert.Equal(t, namespaceName, managerRoleBinding.Namespace, "namespace should match the namespace of the Helm release") + assert.Equal(t, "test-runners-gha-runner-scale-set-manager-role-binding", managerRoleBinding.Name) + assert.Equal(t, "test-runners-gha-runner-scale-set-manager-role", managerRoleBinding.RoleRef.Name) + assert.Equal(t, "arc", managerRoleBinding.Subjects[0].Name) + assert.Equal(t, "arc-system", managerRoleBinding.Subjects[0].Namespace) +} diff --git a/charts/gha-runner-scale-set/tests/values.yaml b/charts/gha-runner-scale-set/tests/values.yaml index fc42555e..425d25a4 100644 --- a/charts/gha-runner-scale-set/tests/values.yaml +++ b/charts/gha-runner-scale-set/tests/values.yaml @@ -2,4 +2,7 @@ githubConfigUrl: https://github.com/actions/actions-runner-controller githubConfigSecret: github_token: test maxRunners: 10 -minRunners: 5 \ No newline at end of file +minRunners: 5 +controllerServiceAccount: + name: "arc" + namespace: "arc-system" \ No newline at end of file diff --git a/charts/gha-runner-scale-set/values.yaml b/charts/gha-runner-scale-set/values.yaml index 87677fbc..97bb1aa7 100644 --- a/charts/gha-runner-scale-set/values.yaml +++ b/charts/gha-runner-scale-set/values.yaml @@ -161,3 +161,13 @@ containerMode: resources: requests: storage: 1Gi + +## Optional controller service account that needs to have required Role and RoleBinding +## to operate this gha-runner-scale-set installation. +## The helm chart will try to find the controller deployment and its service account at installation time. +## In case the helm chart can't find the right service account, you can explicitly pass in the following value +## to help it finish RoleBinding with the right service account. +## Note: if your controller is installed to only watch a single namespace, you have to pass these values explicitly. +# controllerServiceAccount: +# namespace: arc-system +# name: test-arc-gha-runner-scale-set-controller \ No newline at end of file