package tests import ( "path/filepath" "strings" "testing" v1alpha1 "github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1" "github.com/gruntwork-io/terratest/modules/helm" "github.com/gruntwork-io/terratest/modules/k8s" "github.com/gruntwork-io/terratest/modules/random" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" ) func TestTemplateRenderedGitHubSecretWithGitHubToken(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", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/githubsecret.yaml"}) var githubSecret corev1.Secret helm.UnmarshalK8SYaml(t, output, &githubSecret) assert.Equal(t, namespaceName, githubSecret.Namespace) assert.Equal(t, "test-runners-gha-runner-scale-set-github-secret", githubSecret.Name) assert.Equal(t, "gh_token12345", string(githubSecret.Data["github_token"])) assert.Equal(t, "actions.github.com/secret-protection", githubSecret.Finalizers[0]) } func TestTemplateRenderedGitHubSecretWithGitHubApp(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_app_id": "10", "githubConfigSecret.github_app_installation_id": "100", "githubConfigSecret.github_app_private_key": "private_key", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/githubsecret.yaml"}) var githubSecret corev1.Secret helm.UnmarshalK8SYaml(t, output, &githubSecret) assert.Equal(t, namespaceName, githubSecret.Namespace) assert.Equal(t, "10", string(githubSecret.Data["github_app_id"])) assert.Equal(t, "100", string(githubSecret.Data["github_app_installation_id"])) assert.Equal(t, "private_key", string(githubSecret.Data["github_app_private_key"])) } func TestTemplateRenderedGitHubSecretErrorWithMissingAuthInput(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_app_id": "", "githubConfigSecret.github_token": "", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } _, err = helm.RenderTemplateE(t, options, helmChartPath, releaseName, []string{"templates/githubsecret.yaml"}) require.Error(t, err) assert.ErrorContains(t, err, "provide .Values.githubConfigSecret.github_token or .Values.githubConfigSecret.github_app_id") } func TestTemplateRenderedGitHubSecretErrorWithMissingAppInput(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_app_id": "10", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } _, err = helm.RenderTemplateE(t, options, helmChartPath, releaseName, []string{"templates/githubsecret.yaml"}) require.Error(t, err) assert.ErrorContains(t, err, "provide .Values.githubConfigSecret.github_app_installation_id and .Values.githubConfigSecret.github_app_private_key") } func TestTemplateNotRenderedGitHubSecretWithPredefinedSecret(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": "pre-defined-secret", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } _, err = helm.RenderTemplateE(t, options, helmChartPath, releaseName, []string{"templates/githubsecret.yaml"}) assert.ErrorContains(t, err, "could not find template templates/githubsecret.yaml in chart", "secret should not be rendered since a pre-defined secret is provided") } func TestTemplateRenderedSetServiceAccountToNoPermission(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", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/no_permission_serviceaccount.yaml"}) var serviceAccount corev1.ServiceAccount helm.UnmarshalK8SYaml(t, output, &serviceAccount) assert.Equal(t, namespaceName, serviceAccount.Namespace) assert.Equal(t, "test-runners-gha-runner-scale-set-no-permission-service-account", serviceAccount.Name) output = helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"}) var ars v1alpha1.AutoscalingRunnerSet helm.UnmarshalK8SYaml(t, output, &ars) assert.Equal(t, "test-runners-gha-runner-scale-set-no-permission-service-account", ars.Spec.Template.Spec.ServiceAccountName) } func TestTemplateRenderedSetServiceAccountToKubeMode(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", "containerMode.type": "kubernetes", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/kube_mode_serviceaccount.yaml"}) var serviceAccount corev1.ServiceAccount helm.UnmarshalK8SYaml(t, output, &serviceAccount) assert.Equal(t, namespaceName, serviceAccount.Namespace) assert.Equal(t, "test-runners-gha-runner-scale-set-kube-mode-service-account", serviceAccount.Name) output = helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/kube_mode_role.yaml"}) var role rbacv1.Role helm.UnmarshalK8SYaml(t, output, &role) assert.Equal(t, namespaceName, role.Namespace) assert.Equal(t, "test-runners-gha-runner-scale-set-kube-mode-role", role.Name) assert.Len(t, role.Rules, 5, "kube mode role should have 5 rules") assert.Equal(t, "pods", role.Rules[0].Resources[0]) assert.Equal(t, "pods/exec", role.Rules[1].Resources[0]) assert.Equal(t, "pods/log", role.Rules[2].Resources[0]) assert.Equal(t, "jobs", role.Rules[3].Resources[0]) assert.Equal(t, "secrets", role.Rules[4].Resources[0]) output = helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/kube_mode_role_binding.yaml"}) var roleBinding rbacv1.RoleBinding helm.UnmarshalK8SYaml(t, output, &roleBinding) assert.Equal(t, namespaceName, roleBinding.Namespace) assert.Equal(t, "test-runners-gha-runner-scale-set-kube-mode-role", roleBinding.Name) assert.Len(t, roleBinding.Subjects, 1) assert.Equal(t, "test-runners-gha-runner-scale-set-kube-mode-service-account", roleBinding.Subjects[0].Name) assert.Equal(t, namespaceName, roleBinding.Subjects[0].Namespace) assert.Equal(t, "test-runners-gha-runner-scale-set-kube-mode-role", roleBinding.RoleRef.Name) assert.Equal(t, "Role", roleBinding.RoleRef.Kind) output = helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"}) var ars v1alpha1.AutoscalingRunnerSet helm.UnmarshalK8SYaml(t, output, &ars) assert.Equal(t, "test-runners-gha-runner-scale-set-kube-mode-service-account", ars.Spec.Template.Spec.ServiceAccountName) } func TestTemplateRenderedUserProvideSetServiceAccount(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", "template.spec.serviceAccountName": "test-service-account", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } _, err = helm.RenderTemplateE(t, options, helmChartPath, releaseName, []string{"templates/no_permission_serviceaccount.yaml"}) assert.ErrorContains(t, err, "could not find template templates/no_permission_serviceaccount.yaml in chart", "no permission service account should not be rendered") output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"}) var ars v1alpha1.AutoscalingRunnerSet helm.UnmarshalK8SYaml(t, output, &ars) assert.Equal(t, "test-service-account", ars.Spec.Template.Spec.ServiceAccountName) } func TestTemplateRenderedAutoScalingRunnerSet(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", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"}) var ars v1alpha1.AutoscalingRunnerSet helm.UnmarshalK8SYaml(t, output, &ars) assert.Equal(t, namespaceName, ars.Namespace) assert.Equal(t, "test-runners", ars.Name) assert.Equal(t, "gha-runner-scale-set", ars.Labels["app.kubernetes.io/name"]) assert.Equal(t, "test-runners", ars.Labels["app.kubernetes.io/instance"]) assert.Equal(t, "https://github.com/actions", ars.Spec.GitHubConfigUrl) assert.Equal(t, "test-runners-gha-runner-scale-set-github-secret", ars.Spec.GitHubConfigSecret) assert.Empty(t, ars.Spec.RunnerGroup, "RunnerGroup should be empty") assert.Nil(t, ars.Spec.MinRunners, "MinRunners should be nil") assert.Nil(t, ars.Spec.MaxRunners, "MaxRunners should be nil") assert.Nil(t, ars.Spec.Proxy, "Proxy should be nil") assert.Nil(t, ars.Spec.GitHubServerTLS, "GitHubServerTLS should be nil") assert.NotNil(t, ars.Spec.Template.Spec, "Template.Spec should not be nil") assert.Len(t, ars.Spec.Template.Spec.Containers, 1, "Template.Spec should have 1 container") assert.Equal(t, "runner", ars.Spec.Template.Spec.Containers[0].Name) assert.Equal(t, "ghcr.io/actions/actions-runner:latest", ars.Spec.Template.Spec.Containers[0].Image) } func TestTemplateRenderedAutoScalingRunnerSet_ProvideMetadata(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", "template.metadata.labels.test1": "test1", "template.metadata.labels.test2": "test2", "template.metadata.annotations.test3": "test3", "template.metadata.annotations.test4": "test4", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"}) var ars v1alpha1.AutoscalingRunnerSet helm.UnmarshalK8SYaml(t, output, &ars) assert.Equal(t, namespaceName, ars.Namespace) assert.Equal(t, "test-runners", ars.Name) assert.NotNil(t, ars.Spec.Template.Labels, "Template.Spec.Labels should not be nil") assert.Equal(t, "test1", ars.Spec.Template.Labels["test1"], "Template.Spec.Labels should have test1") assert.Equal(t, "test2", ars.Spec.Template.Labels["test2"], "Template.Spec.Labels should have test2") assert.NotNil(t, ars.Spec.Template.Annotations, "Template.Spec.Annotations should not be nil") assert.Equal(t, "test3", ars.Spec.Template.Annotations["test3"], "Template.Spec.Annotations should have test3") assert.Equal(t, "test4", ars.Spec.Template.Annotations["test4"], "Template.Spec.Annotations should have test4") assert.NotNil(t, ars.Spec.Template.Spec, "Template.Spec should not be nil") assert.Len(t, ars.Spec.Template.Spec.Containers, 1, "Template.Spec should have 1 container") assert.Equal(t, "runner", ars.Spec.Template.Spec.Containers[0].Name) assert.Equal(t, "ghcr.io/actions/actions-runner:latest", ars.Spec.Template.Spec.Containers[0].Image) } func TestTemplateRenderedAutoScalingRunnerSet_MaxRunnersValidationError(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", "maxRunners": "-1", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } _, err = helm.RenderTemplateE(t, options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"}) require.Error(t, err) assert.ErrorContains(t, err, "maxRunners has to be greater or equal to 0") } func TestTemplateRenderedAutoScalingRunnerSet_MinRunnersValidationError(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", "maxRunners": "1", "minRunners": "-1", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } _, err = helm.RenderTemplateE(t, options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"}) require.Error(t, err) assert.ErrorContains(t, err, "minRunners has to be greater or equal to 0") } func TestTemplateRenderedAutoScalingRunnerSet_MinMaxRunnersValidationError(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", "maxRunners": "0", "minRunners": "1", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } _, err = helm.RenderTemplateE(t, options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"}) require.Error(t, err) assert.ErrorContains(t, err, "maxRunners has to be greater or equal to minRunners") } func TestTemplateRenderedAutoScalingRunnerSet_MinMaxRunnersValidationSameValue(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", "maxRunners": "0", "minRunners": "0", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"}) var ars v1alpha1.AutoscalingRunnerSet helm.UnmarshalK8SYaml(t, output, &ars) assert.Equal(t, 0, *ars.Spec.MinRunners, "MinRunners should be 0") assert.Equal(t, 0, *ars.Spec.MaxRunners, "MaxRunners should be 0") } func TestTemplateRenderedAutoScalingRunnerSet_MinMaxRunnersValidation_OnlyMin(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", "minRunners": "5", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"}) var ars v1alpha1.AutoscalingRunnerSet helm.UnmarshalK8SYaml(t, output, &ars) assert.Equal(t, 5, *ars.Spec.MinRunners, "MinRunners should be 5") assert.Nil(t, ars.Spec.MaxRunners, "MaxRunners should be nil") } func TestTemplateRenderedAutoScalingRunnerSet_MinMaxRunnersValidation_OnlyMax(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", "maxRunners": "5", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"}) var ars v1alpha1.AutoscalingRunnerSet helm.UnmarshalK8SYaml(t, output, &ars) assert.Equal(t, 5, *ars.Spec.MaxRunners, "MaxRunners should be 5") assert.Nil(t, ars.Spec.MinRunners, "MinRunners should be nil") } func TestTemplateRenderedAutoScalingRunnerSet_MinMaxRunners_FromValuesFile(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) testValuesPath, err := filepath.Abs("../tests/values.yaml") require.NoError(t, err) releaseName := "test-runners" namespaceName := "test-" + strings.ToLower(random.UniqueId()) options := &helm.Options{ ValuesFiles: []string{testValuesPath}, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"}) var ars v1alpha1.AutoscalingRunnerSet helm.UnmarshalK8SYaml(t, output, &ars) assert.Equal(t, 5, *ars.Spec.MinRunners, "MinRunners should be 5") assert.Equal(t, 10, *ars.Spec.MaxRunners, "MaxRunners should be 10") } func TestTemplateRenderedAutoScalingRunnerSet_EnableDinD(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", "containerMode.type": "dind", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"}) var ars v1alpha1.AutoscalingRunnerSet helm.UnmarshalK8SYaml(t, output, &ars) assert.Equal(t, namespaceName, ars.Namespace) assert.Equal(t, "test-runners", ars.Name) assert.Equal(t, "gha-runner-scale-set", ars.Labels["app.kubernetes.io/name"]) assert.Equal(t, "test-runners", ars.Labels["app.kubernetes.io/instance"]) assert.Equal(t, "https://github.com/actions", ars.Spec.GitHubConfigUrl) assert.Equal(t, "test-runners-gha-runner-scale-set-github-secret", ars.Spec.GitHubConfigSecret) assert.Empty(t, ars.Spec.RunnerGroup, "RunnerGroup should be empty") assert.Nil(t, ars.Spec.MinRunners, "MinRunners should be nil") assert.Nil(t, ars.Spec.MaxRunners, "MaxRunners should be nil") assert.Nil(t, ars.Spec.Proxy, "Proxy should be nil") assert.Nil(t, ars.Spec.GitHubServerTLS, "GitHubServerTLS should be nil") assert.NotNil(t, ars.Spec.Template.Spec, "Template.Spec should not be nil") assert.Len(t, ars.Spec.Template.Spec.InitContainers, 1, "Template.Spec should have 1 init container") assert.Equal(t, "init-dind-externals", ars.Spec.Template.Spec.InitContainers[0].Name) assert.Equal(t, "ghcr.io/actions/actions-runner:latest", ars.Spec.Template.Spec.InitContainers[0].Image) assert.Equal(t, "cp", ars.Spec.Template.Spec.InitContainers[0].Command[0]) assert.Equal(t, "-r -v /actions-runner/externals/. /actions-runner/tmpDir/", strings.Join(ars.Spec.Template.Spec.InitContainers[0].Args, " ")) assert.Len(t, ars.Spec.Template.Spec.Containers, 2, "Template.Spec should have 2 container") assert.Equal(t, "runner", ars.Spec.Template.Spec.Containers[0].Name) assert.Equal(t, "ghcr.io/actions/actions-runner:latest", ars.Spec.Template.Spec.Containers[0].Image) assert.Len(t, ars.Spec.Template.Spec.Containers[0].Env, 4, "The runner container should have 4 env vars, DOCKER_HOST, DOCKER_TLS_VERIFY, DOCKER_CERT_PATH and RUNNER_WAIT_FOR_DOCKER_IN_SECONDS") assert.Equal(t, "DOCKER_HOST", ars.Spec.Template.Spec.Containers[0].Env[0].Name) assert.Equal(t, "tcp://localhost:2376", ars.Spec.Template.Spec.Containers[0].Env[0].Value) assert.Equal(t, "DOCKER_TLS_VERIFY", ars.Spec.Template.Spec.Containers[0].Env[1].Name) assert.Equal(t, "1", ars.Spec.Template.Spec.Containers[0].Env[1].Value) assert.Equal(t, "DOCKER_CERT_PATH", ars.Spec.Template.Spec.Containers[0].Env[2].Name) assert.Equal(t, "/certs/client", ars.Spec.Template.Spec.Containers[0].Env[2].Value) assert.Equal(t, "RUNNER_WAIT_FOR_DOCKER_IN_SECONDS", ars.Spec.Template.Spec.Containers[0].Env[3].Name) assert.Equal(t, "120", ars.Spec.Template.Spec.Containers[0].Env[3].Value) assert.Len(t, ars.Spec.Template.Spec.Containers[0].VolumeMounts, 2, "The runner container should have 2 volume mounts, dind-cert and work") assert.Equal(t, "work", ars.Spec.Template.Spec.Containers[0].VolumeMounts[0].Name) assert.Equal(t, "/actions-runner/_work", ars.Spec.Template.Spec.Containers[0].VolumeMounts[0].MountPath) assert.False(t, ars.Spec.Template.Spec.Containers[0].VolumeMounts[0].ReadOnly) assert.Equal(t, "dind-cert", ars.Spec.Template.Spec.Containers[0].VolumeMounts[1].Name) assert.Equal(t, "/certs/client", ars.Spec.Template.Spec.Containers[0].VolumeMounts[1].MountPath) assert.True(t, ars.Spec.Template.Spec.Containers[0].VolumeMounts[1].ReadOnly) assert.Equal(t, "dind", ars.Spec.Template.Spec.Containers[1].Name) assert.Equal(t, "docker:dind", ars.Spec.Template.Spec.Containers[1].Image) assert.True(t, *ars.Spec.Template.Spec.Containers[1].SecurityContext.Privileged) assert.Len(t, ars.Spec.Template.Spec.Containers[1].VolumeMounts, 3, "The dind container should have 3 volume mounts, dind-cert, work and externals") assert.Equal(t, "work", ars.Spec.Template.Spec.Containers[1].VolumeMounts[0].Name) assert.Equal(t, "/actions-runner/_work", ars.Spec.Template.Spec.Containers[1].VolumeMounts[0].MountPath) assert.Equal(t, "dind-cert", ars.Spec.Template.Spec.Containers[1].VolumeMounts[1].Name) assert.Equal(t, "/certs/client", ars.Spec.Template.Spec.Containers[1].VolumeMounts[1].MountPath) assert.Equal(t, "dind-externals", ars.Spec.Template.Spec.Containers[1].VolumeMounts[2].Name) assert.Equal(t, "/actions-runner/externals", ars.Spec.Template.Spec.Containers[1].VolumeMounts[2].MountPath) } func TestTemplateRenderedAutoScalingRunnerSet_EnableKubernetesMode(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", "containerMode.type": "kubernetes", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"}) var ars v1alpha1.AutoscalingRunnerSet helm.UnmarshalK8SYaml(t, output, &ars) assert.Equal(t, namespaceName, ars.Namespace) assert.Equal(t, "test-runners", ars.Name) assert.Equal(t, "gha-runner-scale-set", ars.Labels["app.kubernetes.io/name"]) assert.Equal(t, "test-runners", ars.Labels["app.kubernetes.io/instance"]) assert.Equal(t, "https://github.com/actions", ars.Spec.GitHubConfigUrl) assert.Equal(t, "test-runners-gha-runner-scale-set-github-secret", ars.Spec.GitHubConfigSecret) assert.Empty(t, ars.Spec.RunnerGroup, "RunnerGroup should be empty") assert.Nil(t, ars.Spec.MinRunners, "MinRunners should be nil") assert.Nil(t, ars.Spec.MaxRunners, "MaxRunners should be nil") assert.Nil(t, ars.Spec.Proxy, "Proxy should be nil") assert.Nil(t, ars.Spec.GitHubServerTLS, "GitHubServerTLS should be nil") assert.NotNil(t, ars.Spec.Template.Spec, "Template.Spec should not be nil") assert.Len(t, ars.Spec.Template.Spec.Containers, 1, "Template.Spec should have 1 container") assert.Equal(t, "runner", ars.Spec.Template.Spec.Containers[0].Name) assert.Equal(t, "ghcr.io/actions/actions-runner:latest", ars.Spec.Template.Spec.Containers[0].Image) assert.Equal(t, "ACTIONS_RUNNER_CONTAINER_HOOKS", ars.Spec.Template.Spec.Containers[0].Env[0].Name) assert.Equal(t, "/actions-runner/k8s/index.js", ars.Spec.Template.Spec.Containers[0].Env[0].Value) assert.Equal(t, "ACTIONS_RUNNER_POD_NAME", ars.Spec.Template.Spec.Containers[0].Env[1].Name) assert.Equal(t, "ACTIONS_RUNNER_REQUIRE_JOB_CONTAINER", ars.Spec.Template.Spec.Containers[0].Env[2].Name) assert.Equal(t, "true", ars.Spec.Template.Spec.Containers[0].Env[2].Value) assert.Len(t, ars.Spec.Template.Spec.Volumes, 1, "Template.Spec should have 1 volume") assert.Equal(t, "work", ars.Spec.Template.Spec.Volumes[0].Name) assert.NotNil(t, ars.Spec.Template.Spec.Volumes[0].Ephemeral, "Template.Spec should have 1 ephemeral volume") } func TestTemplateRenderedAutoScalingRunnerSet_UsePredefinedSecret(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": "pre-defined-secrets", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"}) var ars v1alpha1.AutoscalingRunnerSet helm.UnmarshalK8SYaml(t, output, &ars) assert.Equal(t, namespaceName, ars.Namespace) assert.Equal(t, "test-runners", ars.Name) assert.Equal(t, "gha-runner-scale-set", ars.Labels["app.kubernetes.io/name"]) assert.Equal(t, "test-runners", ars.Labels["app.kubernetes.io/instance"]) assert.Equal(t, "https://github.com/actions", ars.Spec.GitHubConfigUrl) assert.Equal(t, "pre-defined-secrets", ars.Spec.GitHubConfigSecret) } func TestTemplateRenderedAutoScalingRunnerSet_ErrorOnEmptyPredefinedSecret(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": "", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } _, err = helm.RenderTemplateE(t, options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"}) require.Error(t, err) assert.ErrorContains(t, err, "Values.githubConfigSecret is required for setting auth with GitHub server") } func TestTemplateRenderedWithProxy(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": "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}", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"}) var ars v1alpha1.AutoscalingRunnerSet helm.UnmarshalK8SYaml(t, output, &ars) require.NotNil(t, ars.Spec.Proxy) require.NotNil(t, ars.Spec.Proxy.HTTP) assert.Equal(t, "http://proxy.example.com", ars.Spec.Proxy.HTTP.Url) assert.Equal(t, "http-secret", ars.Spec.Proxy.HTTP.CredentialSecretRef) require.NotNil(t, ars.Spec.Proxy.HTTPS) assert.Equal(t, "https://proxy.example.com", ars.Spec.Proxy.HTTPS.Url) assert.Equal(t, "https-secret", ars.Spec.Proxy.HTTPS.CredentialSecretRef) require.NotNil(t, ars.Spec.Proxy.NoProxy) require.Len(t, ars.Spec.Proxy.NoProxy, 2) assert.Contains(t, ars.Spec.Proxy.NoProxy, "example.com") assert.Contains(t, ars.Spec.Proxy.NoProxy, "example.org") }