package tests import ( "fmt" "os" "path/filepath" "strings" "testing" "github.com/gruntwork-io/terratest/modules/helm" "github.com/gruntwork-io/terratest/modules/k8s" "github.com/gruntwork-io/terratest/modules/logger" "github.com/gruntwork-io/terratest/modules/random" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "gopkg.in/yaml.v2" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) type Chart struct { Version string `yaml:"version"` AppVersion string `yaml:"appVersion"` } func TestTemplate_CreateServiceAccount(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{ Logger: logger.Discard, SetValues: map[string]string{ "serviceAccount.create": "true", "serviceAccount.annotations.foo": "bar", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/serviceaccount.yaml"}) var serviceAccount corev1.ServiceAccount helm.UnmarshalK8SYaml(t, output, &serviceAccount) assert.Equal(t, namespaceName, serviceAccount.Namespace) assert.Equal(t, "test-arc-gha-rs-controller", serviceAccount.Name) assert.Equal(t, "bar", string(serviceAccount.Annotations["foo"])) } func TestTemplate_CreateServiceAccount_OverwriteName(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{ Logger: logger.Discard, SetValues: map[string]string{ "serviceAccount.create": "true", "serviceAccount.name": "overwritten-name", "serviceAccount.annotations.foo": "bar", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/serviceaccount.yaml"}) var serviceAccount corev1.ServiceAccount helm.UnmarshalK8SYaml(t, output, &serviceAccount) assert.Equal(t, namespaceName, serviceAccount.Namespace) assert.Equal(t, "overwritten-name", serviceAccount.Name) assert.Equal(t, "bar", string(serviceAccount.Annotations["foo"])) } func TestTemplate_CreateServiceAccount_CannotUseDefaultServiceAccount(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{ Logger: logger.Discard, SetValues: map[string]string{ "serviceAccount.create": "true", "serviceAccount.name": "default", "serviceAccount.annotations.foo": "bar", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } _, err = helm.RenderTemplateE(t, options, helmChartPath, releaseName, []string{"templates/serviceaccount.yaml"}) assert.ErrorContains(t, err, "serviceAccount.name cannot be set to 'default'", "We should get an error because the default service account cannot be used") } func TestTemplate_NotCreateServiceAccount(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{ Logger: logger.Discard, SetValues: map[string]string{ "serviceAccount.create": "false", "serviceAccount.name": "overwritten-name", "serviceAccount.annotations.foo": "bar", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } _, err = helm.RenderTemplateE(t, options, helmChartPath, releaseName, []string{"templates/serviceaccount.yaml"}) assert.ErrorContains(t, err, "could not find template templates/serviceaccount.yaml in chart", "We should get an error because the template should be skipped") } func TestTemplate_NotCreateServiceAccount_ServiceAccountNotSet(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{ Logger: logger.Discard, SetValues: map[string]string{ "serviceAccount.create": "false", "serviceAccount.annotations.foo": "bar", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } _, err = helm.RenderTemplateE(t, options, helmChartPath, releaseName, []string{"templates/deployment.yaml"}) 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_CreateManagerClusterRole(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{ Logger: logger.Discard, SetValues: map[string]string{}, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/manager_cluster_role.yaml"}) var managerClusterRole rbacv1.ClusterRole helm.UnmarshalK8SYaml(t, output, &managerClusterRole) assert.Empty(t, managerClusterRole.Namespace, "ClusterRole should not have a namespace") assert.Equal(t, "test-arc-gha-rs-controller", managerClusterRole.Name) assert.Equal(t, 16, len(managerClusterRole.Rules)) _, err = helm.RenderTemplateE(t, options, helmChartPath, releaseName, []string{"templates/manager_single_namespace_controller_role.yaml"}) assert.ErrorContains(t, err, "could not find template templates/manager_single_namespace_controller_role.yaml in chart", "We should get an error because the template should be skipped") _, err = helm.RenderTemplateE(t, options, helmChartPath, releaseName, []string{"templates/manager_single_namespace_watch_role.yaml"}) assert.ErrorContains(t, err, "could not find template templates/manager_single_namespace_watch_role.yaml in chart", "We should get an error because the template should be skipped") } func TestTemplate_ManagerClusterRoleBinding(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{ Logger: logger.Discard, SetValues: map[string]string{ "serviceAccount.create": "true", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/manager_cluster_role_binding.yaml"}) var managerClusterRoleBinding rbacv1.ClusterRoleBinding helm.UnmarshalK8SYaml(t, output, &managerClusterRoleBinding) assert.Empty(t, managerClusterRoleBinding.Namespace, "ClusterRoleBinding should not have a namespace") assert.Equal(t, "test-arc-gha-rs-controller", managerClusterRoleBinding.Name) assert.Equal(t, "test-arc-gha-rs-controller", managerClusterRoleBinding.RoleRef.Name) assert.Equal(t, "test-arc-gha-rs-controller", managerClusterRoleBinding.Subjects[0].Name) assert.Equal(t, namespaceName, managerClusterRoleBinding.Subjects[0].Namespace) _, err = helm.RenderTemplateE(t, options, helmChartPath, releaseName, []string{"templates/manager_single_namespace_controller_role_binding.yaml"}) assert.ErrorContains(t, err, "could not find template templates/manager_single_namespace_controller_role_binding.yaml in chart", "We should get an error because the template should be skipped") _, err = helm.RenderTemplateE(t, options, helmChartPath, releaseName, []string{"templates/manager_single_namespace_watch_role_binding.yaml"}) assert.ErrorContains(t, err, "could not find template templates/manager_single_namespace_watch_role_binding.yaml in chart", "We should get an error because the template should be skipped") } 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{ Logger: logger.Discard, 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-rs-controller-listener", 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{ Logger: logger.Discard, 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-rs-controller-listener", managerListenerRoleBinding.Name) assert.Equal(t, "test-arc-gha-rs-controller-listener", managerListenerRoleBinding.RoleRef.Name) assert.Equal(t, "test-arc-gha-rs-controller", managerListenerRoleBinding.Subjects[0].Name) assert.Equal(t, namespaceName, managerListenerRoleBinding.Subjects[0].Namespace) } func TestTemplate_ControllerDeployment_Defaults(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) chartContent, err := os.ReadFile(filepath.Join(helmChartPath, "Chart.yaml")) require.NoError(t, err) chart := new(Chart) err = yaml.Unmarshal(chartContent, chart) require.NoError(t, err) releaseName := "test-arc" namespaceName := "test-" + strings.ToLower(random.UniqueId()) options := &helm.Options{ Logger: logger.Discard, SetValues: map[string]string{ "image.tag": "dev", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/deployment.yaml"}) var deployment appsv1.Deployment helm.UnmarshalK8SYaml(t, output, &deployment) assert.Equal(t, namespaceName, deployment.Namespace) assert.Equal(t, "test-arc-gha-rs-controller", deployment.Name) assert.Equal(t, "gha-rs-controller-"+chart.Version, deployment.Labels["helm.sh/chart"]) assert.Equal(t, "gha-rs-controller", deployment.Labels["app.kubernetes.io/name"]) 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-rs-controller", deployment.Labels["actions.github.com/controller-service-account-name"]) assert.NotContains(t, deployment.Labels, "actions.github.com/controller-watch-single-namespace") assert.Equal(t, "gha-rs-controller", deployment.Labels["app.kubernetes.io/part-of"]) assert.Equal(t, int32(1), *deployment.Spec.Replicas) assert.Equal(t, "gha-rs-controller", deployment.Spec.Selector.MatchLabels["app.kubernetes.io/name"]) assert.Equal(t, "test-arc", deployment.Spec.Selector.MatchLabels["app.kubernetes.io/instance"]) assert.Equal(t, "gha-rs-controller", deployment.Spec.Template.Labels["app.kubernetes.io/name"]) assert.Equal(t, "test-arc", deployment.Spec.Template.Labels["app.kubernetes.io/instance"]) assert.Equal(t, "manager", deployment.Spec.Template.Annotations["kubectl.kubernetes.io/default-container"]) assert.Len(t, deployment.Spec.Template.Spec.ImagePullSecrets, 0) assert.Equal(t, "test-arc-gha-rs-controller", deployment.Spec.Template.Spec.ServiceAccountName) assert.Nil(t, deployment.Spec.Template.Spec.SecurityContext) assert.Empty(t, deployment.Spec.Template.Spec.PriorityClassName) assert.Equal(t, int64(10), *deployment.Spec.Template.Spec.TerminationGracePeriodSeconds) assert.Len(t, deployment.Spec.Template.Spec.Volumes, 1) assert.Equal(t, "tmp", deployment.Spec.Template.Spec.Volumes[0].Name) assert.NotNil(t, 10, deployment.Spec.Template.Spec.Volumes[0].EmptyDir) assert.Len(t, deployment.Spec.Template.Spec.NodeSelector, 0) assert.Nil(t, deployment.Spec.Template.Spec.Affinity) assert.Len(t, deployment.Spec.Template.Spec.TopologySpreadConstraints, 0) assert.Len(t, deployment.Spec.Template.Spec.Tolerations, 0) managerImage := "ghcr.io/actions/gha-runner-scale-set-controller:dev" assert.Len(t, deployment.Spec.Template.Spec.Containers, 1) assert.Equal(t, "manager", deployment.Spec.Template.Spec.Containers[0].Name) assert.Equal(t, managerImage, deployment.Spec.Template.Spec.Containers[0].Image) assert.Equal(t, corev1.PullIfNotPresent, deployment.Spec.Template.Spec.Containers[0].ImagePullPolicy) assert.Len(t, deployment.Spec.Template.Spec.Containers[0].Command, 1) assert.Equal(t, "/manager", deployment.Spec.Template.Spec.Containers[0].Command[0]) expectedArgs := []string{ "--auto-scaling-runner-set-only", "--log-level=debug", "--log-format=text", "--update-strategy=immediate", "--metrics-addr=0", "--listener-metrics-addr=0", "--listener-metrics-endpoint=", "--runner-max-concurrent-reconciles=2", } assert.ElementsMatch(t, expectedArgs, deployment.Spec.Template.Spec.Containers[0].Args) assert.Len(t, deployment.Spec.Template.Spec.Containers[0].Env, 2) assert.Equal(t, "CONTROLLER_MANAGER_CONTAINER_IMAGE", deployment.Spec.Template.Spec.Containers[0].Env[0].Name) assert.Equal(t, managerImage, deployment.Spec.Template.Spec.Containers[0].Env[0].Value) assert.Equal(t, "CONTROLLER_MANAGER_POD_NAMESPACE", deployment.Spec.Template.Spec.Containers[0].Env[1].Name) assert.Equal(t, "metadata.namespace", deployment.Spec.Template.Spec.Containers[0].Env[1].ValueFrom.FieldRef.FieldPath) assert.Empty(t, deployment.Spec.Template.Spec.Containers[0].Resources) assert.Nil(t, deployment.Spec.Template.Spec.Containers[0].SecurityContext) assert.Len(t, deployment.Spec.Template.Spec.Containers[0].VolumeMounts, 1) assert.Equal(t, "tmp", deployment.Spec.Template.Spec.Containers[0].VolumeMounts[0].Name) assert.Equal(t, "/tmp", deployment.Spec.Template.Spec.Containers[0].VolumeMounts[0].MountPath) } func TestTemplate_ControllerDeployment_Customize(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) chartContent, err := os.ReadFile(filepath.Join(helmChartPath, "Chart.yaml")) require.NoError(t, err) chart := new(Chart) err = yaml.Unmarshal(chartContent, chart) require.NoError(t, err) releaseName := "test-arc" namespaceName := "test-" + strings.ToLower(random.UniqueId()) options := &helm.Options{ Logger: logger.Discard, SetValues: map[string]string{ "labels.foo": "bar", "labels.github": "actions", "labels.team": "GitHub Team", "labels.teamMail": "team@github.com", "replicaCount": "1", "image.pullPolicy": "Always", "image.tag": "dev", "imagePullSecrets[0].name": "dockerhub", "nameOverride": "gha-rs-controller-override", "fullnameOverride": "gha-rs-controller-fullname-override", "env[0].name": "ENV_VAR_NAME_1", "env[0].value": "ENV_VAR_VALUE_1", "serviceAccount.name": "gha-rs-controller-sa", "podAnnotations.foo": "bar", "podSecurityContext.fsGroup": "1000", "securityContext.runAsUser": "1000", "securityContext.runAsNonRoot": "true", "resources.limits.cpu": "500m", "nodeSelector.foo": "bar", "tolerations[0].key": "foo", "affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].key": "foo", "affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].operator": "bar", "topologySpreadConstraints[0].labelSelector.matchLabels.foo": "bar", "topologySpreadConstraints[0].maxSkew": "1", "topologySpreadConstraints[0].topologyKey": "foo", "priorityClassName": "test-priority-class", "flags.updateStrategy": "eventual", "flags.logLevel": "info", "flags.logFormat": "json", "volumes[0].name": "customMount", "volumes[0].configMap.name": "my-configmap", "volumeMounts[0].name": "customMount", "volumeMounts[0].mountPath": "/my/mount/path", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/deployment.yaml"}) var deployment appsv1.Deployment helm.UnmarshalK8SYaml(t, output, &deployment) assert.Equal(t, namespaceName, deployment.Namespace) assert.Equal(t, "gha-rs-controller-fullname-override", deployment.Name) assert.Equal(t, "gha-rs-controller-"+chart.Version, deployment.Labels["helm.sh/chart"]) assert.Equal(t, "gha-rs-controller-override", deployment.Labels["app.kubernetes.io/name"]) 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, "gha-rs-controller", deployment.Labels["app.kubernetes.io/part-of"]) assert.Equal(t, "bar", deployment.Labels["foo"]) assert.Equal(t, "actions", deployment.Labels["github"]) assert.Equal(t, "GitHub Team", deployment.Labels["team"]) assert.Equal(t, "team@github.com", deployment.Labels["teamMail"]) assert.Equal(t, int32(1), *deployment.Spec.Replicas) assert.Equal(t, "gha-rs-controller-override", deployment.Spec.Selector.MatchLabels["app.kubernetes.io/name"]) assert.Equal(t, "test-arc", deployment.Spec.Selector.MatchLabels["app.kubernetes.io/instance"]) assert.Equal(t, "gha-rs-controller-override", deployment.Spec.Template.Labels["app.kubernetes.io/name"]) assert.Equal(t, "test-arc", deployment.Spec.Template.Labels["app.kubernetes.io/instance"]) assert.Equal(t, "bar", deployment.Spec.Template.Annotations["foo"]) assert.Equal(t, "manager", deployment.Spec.Template.Annotations["kubectl.kubernetes.io/default-container"]) assert.Equal(t, "ENV_VAR_NAME_1", deployment.Spec.Template.Spec.Containers[0].Env[2].Name) assert.Equal(t, "ENV_VAR_VALUE_1", deployment.Spec.Template.Spec.Containers[0].Env[2].Value) assert.Len(t, deployment.Spec.Template.Spec.ImagePullSecrets, 1) assert.Equal(t, "dockerhub", deployment.Spec.Template.Spec.ImagePullSecrets[0].Name) assert.Equal(t, "gha-rs-controller-sa", deployment.Spec.Template.Spec.ServiceAccountName) assert.Equal(t, int64(1000), *deployment.Spec.Template.Spec.SecurityContext.FSGroup) assert.Equal(t, "test-priority-class", deployment.Spec.Template.Spec.PriorityClassName) assert.Equal(t, int64(10), *deployment.Spec.Template.Spec.TerminationGracePeriodSeconds) assert.Len(t, deployment.Spec.Template.Spec.Volumes, 2) assert.Equal(t, "tmp", deployment.Spec.Template.Spec.Volumes[0].Name) assert.NotNil(t, deployment.Spec.Template.Spec.Volumes[0].EmptyDir) assert.Equal(t, "customMount", deployment.Spec.Template.Spec.Volumes[1].Name) assert.Equal(t, "my-configmap", deployment.Spec.Template.Spec.Volumes[1].ConfigMap.Name) assert.Len(t, deployment.Spec.Template.Spec.NodeSelector, 1) assert.Equal(t, "bar", deployment.Spec.Template.Spec.NodeSelector["foo"]) assert.NotNil(t, deployment.Spec.Template.Spec.Affinity.NodeAffinity) assert.Equal(t, "foo", deployment.Spec.Template.Spec.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms[0].MatchExpressions[0].Key) assert.Equal(t, "bar", string(deployment.Spec.Template.Spec.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms[0].MatchExpressions[0].Operator)) assert.Len(t, deployment.Spec.Template.Spec.TopologySpreadConstraints, 1) assert.Equal(t, "bar", deployment.Spec.Template.Spec.TopologySpreadConstraints[0].LabelSelector.MatchLabels["foo"]) assert.Equal(t, int32(1), deployment.Spec.Template.Spec.TopologySpreadConstraints[0].MaxSkew) assert.Equal(t, "foo", deployment.Spec.Template.Spec.TopologySpreadConstraints[0].TopologyKey) assert.Len(t, deployment.Spec.Template.Spec.Tolerations, 1) assert.Equal(t, "foo", deployment.Spec.Template.Spec.Tolerations[0].Key) managerImage := "ghcr.io/actions/gha-runner-scale-set-controller:dev" assert.Len(t, deployment.Spec.Template.Spec.Containers, 1) assert.Equal(t, "manager", deployment.Spec.Template.Spec.Containers[0].Name) assert.Equal(t, managerImage, deployment.Spec.Template.Spec.Containers[0].Image) assert.Equal(t, corev1.PullAlways, deployment.Spec.Template.Spec.Containers[0].ImagePullPolicy) assert.Len(t, deployment.Spec.Template.Spec.Containers[0].Command, 1) assert.Equal(t, "/manager", deployment.Spec.Template.Spec.Containers[0].Command[0]) expectArgs := []string{ "--auto-scaling-runner-set-only", "--auto-scaler-image-pull-secrets=dockerhub", "--log-level=info", "--log-format=json", "--update-strategy=eventual", "--listener-metrics-addr=0", "--listener-metrics-endpoint=", "--metrics-addr=0", "--runner-max-concurrent-reconciles=2", } assert.ElementsMatch(t, expectArgs, deployment.Spec.Template.Spec.Containers[0].Args) assert.Len(t, deployment.Spec.Template.Spec.Containers[0].Env, 3) assert.Equal(t, "CONTROLLER_MANAGER_CONTAINER_IMAGE", deployment.Spec.Template.Spec.Containers[0].Env[0].Name) assert.Equal(t, managerImage, deployment.Spec.Template.Spec.Containers[0].Env[0].Value) assert.Equal(t, "CONTROLLER_MANAGER_POD_NAMESPACE", deployment.Spec.Template.Spec.Containers[0].Env[1].Name) assert.Equal(t, "metadata.namespace", deployment.Spec.Template.Spec.Containers[0].Env[1].ValueFrom.FieldRef.FieldPath) assert.Equal(t, "ENV_VAR_NAME_1", deployment.Spec.Template.Spec.Containers[0].Env[2].Name) assert.Equal(t, "ENV_VAR_VALUE_1", deployment.Spec.Template.Spec.Containers[0].Env[2].Value) assert.Equal(t, "500m", deployment.Spec.Template.Spec.Containers[0].Resources.Limits.Cpu().String()) assert.True(t, *deployment.Spec.Template.Spec.Containers[0].SecurityContext.RunAsNonRoot) assert.Equal(t, int64(1000), *deployment.Spec.Template.Spec.Containers[0].SecurityContext.RunAsUser) assert.Len(t, deployment.Spec.Template.Spec.Containers[0].VolumeMounts, 2) assert.Equal(t, "tmp", deployment.Spec.Template.Spec.Containers[0].VolumeMounts[0].Name) assert.Equal(t, "/tmp", deployment.Spec.Template.Spec.Containers[0].VolumeMounts[0].MountPath) assert.Equal(t, "customMount", deployment.Spec.Template.Spec.Containers[0].VolumeMounts[1].Name) assert.Equal(t, "/my/mount/path", deployment.Spec.Template.Spec.Containers[0].VolumeMounts[1].MountPath) } func TestTemplate_EnableLeaderElectionRole(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{ Logger: logger.Discard, SetValues: map[string]string{ "replicaCount": "2", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/leader_election_role.yaml"}) var leaderRole rbacv1.Role helm.UnmarshalK8SYaml(t, output, &leaderRole) assert.Equal(t, "test-arc-gha-rs-controller-leader-election", leaderRole.Name) assert.Equal(t, namespaceName, leaderRole.Namespace) } func TestTemplate_EnableLeaderElectionRoleBinding(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{ Logger: logger.Discard, SetValues: map[string]string{ "replicaCount": "2", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/leader_election_role_binding.yaml"}) var leaderRoleBinding rbacv1.RoleBinding helm.UnmarshalK8SYaml(t, output, &leaderRoleBinding) assert.Equal(t, "test-arc-gha-rs-controller-leader-election", leaderRoleBinding.Name) assert.Equal(t, namespaceName, leaderRoleBinding.Namespace) assert.Equal(t, "test-arc-gha-rs-controller-leader-election", leaderRoleBinding.RoleRef.Name) assert.Equal(t, "test-arc-gha-rs-controller", leaderRoleBinding.Subjects[0].Name) } func TestTemplate_EnableLeaderElection(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{ Logger: logger.Discard, SetValues: map[string]string{ "replicaCount": "2", "image.tag": "dev", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/deployment.yaml"}) var deployment appsv1.Deployment helm.UnmarshalK8SYaml(t, output, &deployment) assert.Equal(t, namespaceName, deployment.Namespace) assert.Equal(t, "test-arc-gha-rs-controller", deployment.Name) assert.Equal(t, int32(2), *deployment.Spec.Replicas) assert.Len(t, deployment.Spec.Template.Spec.Containers, 1) assert.Equal(t, "manager", deployment.Spec.Template.Spec.Containers[0].Name) assert.Equal(t, "ghcr.io/actions/gha-runner-scale-set-controller:dev", deployment.Spec.Template.Spec.Containers[0].Image) assert.Equal(t, corev1.PullIfNotPresent, deployment.Spec.Template.Spec.Containers[0].ImagePullPolicy) assert.Len(t, deployment.Spec.Template.Spec.Containers[0].Command, 1) assert.Equal(t, "/manager", deployment.Spec.Template.Spec.Containers[0].Command[0]) expectedArgs := []string{ "--auto-scaling-runner-set-only", "--enable-leader-election", "--leader-election-id=test-arc-gha-rs-controller", "--log-level=debug", "--log-format=text", "--update-strategy=immediate", "--listener-metrics-addr=0", "--listener-metrics-endpoint=", "--metrics-addr=0", "--runner-max-concurrent-reconciles=2", } assert.ElementsMatch(t, expectedArgs, deployment.Spec.Template.Spec.Containers[0].Args) } func TestTemplate_ControllerDeployment_ForwardImagePullSecrets(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{ Logger: logger.Discard, SetValues: map[string]string{ "imagePullSecrets[0].name": "dockerhub", "imagePullSecrets[1].name": "ghcr", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/deployment.yaml"}) var deployment appsv1.Deployment helm.UnmarshalK8SYaml(t, output, &deployment) assert.Equal(t, namespaceName, deployment.Namespace) expectedArgs := []string{ "--auto-scaling-runner-set-only", "--auto-scaler-image-pull-secrets=dockerhub", "--auto-scaler-image-pull-secrets=ghcr", "--log-level=debug", "--log-format=text", "--update-strategy=immediate", "--listener-metrics-addr=0", "--listener-metrics-endpoint=", "--metrics-addr=0", "--runner-max-concurrent-reconciles=2", } assert.ElementsMatch(t, expectedArgs, deployment.Spec.Template.Spec.Containers[0].Args) } func TestTemplate_ControllerDeployment_WatchSingleNamespace(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) chartContent, err := os.ReadFile(filepath.Join(helmChartPath, "Chart.yaml")) require.NoError(t, err) chart := new(Chart) err = yaml.Unmarshal(chartContent, chart) require.NoError(t, err) releaseName := "test-arc" namespaceName := "test-" + strings.ToLower(random.UniqueId()) options := &helm.Options{ Logger: logger.Discard, SetValues: map[string]string{ "image.tag": "dev", "flags.watchSingleNamespace": "demo", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/deployment.yaml"}) var deployment appsv1.Deployment helm.UnmarshalK8SYaml(t, output, &deployment) assert.Equal(t, namespaceName, deployment.Namespace) assert.Equal(t, "test-arc-gha-rs-controller", deployment.Name) assert.Equal(t, "gha-rs-controller-"+chart.Version, deployment.Labels["helm.sh/chart"]) assert.Equal(t, "gha-rs-controller", deployment.Labels["app.kubernetes.io/name"]) 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-rs-controller", deployment.Labels["actions.github.com/controller-service-account-name"]) assert.Equal(t, "demo", deployment.Labels["actions.github.com/controller-watch-single-namespace"]) assert.Equal(t, int32(1), *deployment.Spec.Replicas) assert.Equal(t, "gha-rs-controller", deployment.Spec.Selector.MatchLabels["app.kubernetes.io/name"]) assert.Equal(t, "test-arc", deployment.Spec.Selector.MatchLabels["app.kubernetes.io/instance"]) assert.Equal(t, "gha-rs-controller", deployment.Spec.Template.Labels["app.kubernetes.io/name"]) assert.Equal(t, "test-arc", deployment.Spec.Template.Labels["app.kubernetes.io/instance"]) assert.Equal(t, "manager", deployment.Spec.Template.Annotations["kubectl.kubernetes.io/default-container"]) assert.Len(t, deployment.Spec.Template.Spec.ImagePullSecrets, 0) assert.Equal(t, "test-arc-gha-rs-controller", deployment.Spec.Template.Spec.ServiceAccountName) assert.Nil(t, deployment.Spec.Template.Spec.SecurityContext) assert.Empty(t, deployment.Spec.Template.Spec.PriorityClassName) assert.Equal(t, int64(10), *deployment.Spec.Template.Spec.TerminationGracePeriodSeconds) assert.Len(t, deployment.Spec.Template.Spec.Volumes, 1) assert.Equal(t, "tmp", deployment.Spec.Template.Spec.Volumes[0].Name) assert.NotNil(t, 10, deployment.Spec.Template.Spec.Volumes[0].EmptyDir) assert.Len(t, deployment.Spec.Template.Spec.NodeSelector, 0) assert.Nil(t, deployment.Spec.Template.Spec.Affinity) assert.Len(t, deployment.Spec.Template.Spec.TopologySpreadConstraints, 0) assert.Len(t, deployment.Spec.Template.Spec.Tolerations, 0) managerImage := "ghcr.io/actions/gha-runner-scale-set-controller:dev" assert.Len(t, deployment.Spec.Template.Spec.Containers, 1) assert.Equal(t, "manager", deployment.Spec.Template.Spec.Containers[0].Name) assert.Equal(t, "ghcr.io/actions/gha-runner-scale-set-controller:dev", deployment.Spec.Template.Spec.Containers[0].Image) assert.Equal(t, corev1.PullIfNotPresent, deployment.Spec.Template.Spec.Containers[0].ImagePullPolicy) assert.Len(t, deployment.Spec.Template.Spec.Containers[0].Command, 1) assert.Equal(t, "/manager", deployment.Spec.Template.Spec.Containers[0].Command[0]) expectedArgs := []string{ "--auto-scaling-runner-set-only", "--log-level=debug", "--log-format=text", "--watch-single-namespace=demo", "--update-strategy=immediate", "--listener-metrics-addr=0", "--listener-metrics-endpoint=", "--metrics-addr=0", "--runner-max-concurrent-reconciles=2", } assert.ElementsMatch(t, expectedArgs, deployment.Spec.Template.Spec.Containers[0].Args) assert.Len(t, deployment.Spec.Template.Spec.Containers[0].Env, 2) assert.Equal(t, "CONTROLLER_MANAGER_CONTAINER_IMAGE", deployment.Spec.Template.Spec.Containers[0].Env[0].Name) assert.Equal(t, managerImage, deployment.Spec.Template.Spec.Containers[0].Env[0].Value) assert.Equal(t, "CONTROLLER_MANAGER_POD_NAMESPACE", deployment.Spec.Template.Spec.Containers[0].Env[1].Name) assert.Equal(t, "metadata.namespace", deployment.Spec.Template.Spec.Containers[0].Env[1].ValueFrom.FieldRef.FieldPath) assert.Empty(t, deployment.Spec.Template.Spec.Containers[0].Resources) assert.Nil(t, deployment.Spec.Template.Spec.Containers[0].SecurityContext) assert.Len(t, deployment.Spec.Template.Spec.Containers[0].VolumeMounts, 1) assert.Equal(t, "tmp", deployment.Spec.Template.Spec.Containers[0].VolumeMounts[0].Name) assert.Equal(t, "/tmp", deployment.Spec.Template.Spec.Containers[0].VolumeMounts[0].MountPath) } func TestTemplate_ControllerContainerEnvironmentVariables(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{ Logger: logger.Discard, SetValues: map[string]string{ "env[0].Name": "ENV_VAR_NAME_1", "env[0].Value": "ENV_VAR_VALUE_1", "env[1].Name": "ENV_VAR_NAME_2", "env[1].ValueFrom.SecretKeyRef.Key": "ENV_VAR_NAME_2", "env[1].ValueFrom.SecretKeyRef.Name": "secret-name", "env[1].ValueFrom.SecretKeyRef.Optional": "true", "env[2].Name": "ENV_VAR_NAME_3", "env[2].Value": "", "env[3].Name": "ENV_VAR_NAME_4", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/deployment.yaml"}) var deployment appsv1.Deployment helm.UnmarshalK8SYaml(t, output, &deployment) assert.Equal(t, namespaceName, deployment.Namespace) assert.Equal(t, "test-arc-gha-rs-controller", deployment.Name) assert.Len(t, deployment.Spec.Template.Spec.Containers[0].Env, 6) assert.Equal(t, "ENV_VAR_NAME_1", deployment.Spec.Template.Spec.Containers[0].Env[2].Name) assert.Equal(t, "ENV_VAR_VALUE_1", deployment.Spec.Template.Spec.Containers[0].Env[2].Value) assert.Equal(t, "ENV_VAR_NAME_2", deployment.Spec.Template.Spec.Containers[0].Env[3].Name) assert.Equal(t, "secret-name", deployment.Spec.Template.Spec.Containers[0].Env[3].ValueFrom.SecretKeyRef.Name) assert.Equal(t, "ENV_VAR_NAME_2", deployment.Spec.Template.Spec.Containers[0].Env[3].ValueFrom.SecretKeyRef.Key) assert.True(t, *deployment.Spec.Template.Spec.Containers[0].Env[3].ValueFrom.SecretKeyRef.Optional) assert.Equal(t, "ENV_VAR_NAME_3", deployment.Spec.Template.Spec.Containers[0].Env[4].Name) assert.Empty(t, deployment.Spec.Template.Spec.Containers[0].Env[4].Value) assert.Equal(t, "ENV_VAR_NAME_4", deployment.Spec.Template.Spec.Containers[0].Env[5].Name) assert.Empty(t, deployment.Spec.Template.Spec.Containers[0].Env[5].ValueFrom) } func TestTemplate_WatchSingleNamespace_NotCreateManagerClusterRole(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{ Logger: logger.Discard, SetValues: map[string]string{ "flags.watchSingleNamespace": "demo", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } _, err = helm.RenderTemplateE(t, options, helmChartPath, releaseName, []string{"templates/manager_cluster_role.yaml"}) assert.ErrorContains(t, err, "could not find template templates/manager_cluster_role.yaml in chart", "We should get an error because the template should be skipped") } func TestTemplate_WatchSingleNamespace_NotManagerClusterRoleBinding(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{ Logger: logger.Discard, SetValues: map[string]string{ "serviceAccount.create": "true", "flags.watchSingleNamespace": "demo", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } _, err = helm.RenderTemplateE(t, options, helmChartPath, releaseName, []string{"templates/manager_cluster_role_binding.yaml"}) assert.ErrorContains(t, err, "could not find template templates/manager_cluster_role_binding.yaml in chart", "We should get an error because the template should be skipped") } func TestTemplate_CreateManagerSingleNamespaceRole(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{ Logger: logger.Discard, SetValues: map[string]string{ "flags.watchSingleNamespace": "demo", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/manager_single_namespace_controller_role.yaml"}) var managerSingleNamespaceControllerRole rbacv1.Role helm.UnmarshalK8SYaml(t, output, &managerSingleNamespaceControllerRole) assert.Equal(t, "test-arc-gha-rs-controller-single-namespace", managerSingleNamespaceControllerRole.Name) assert.Equal(t, namespaceName, managerSingleNamespaceControllerRole.Namespace) assert.Equal(t, 10, len(managerSingleNamespaceControllerRole.Rules)) output = helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/manager_single_namespace_watch_role.yaml"}) var managerSingleNamespaceWatchRole rbacv1.Role helm.UnmarshalK8SYaml(t, output, &managerSingleNamespaceWatchRole) assert.Equal(t, "test-arc-gha-rs-controller-single-namespace-watch", managerSingleNamespaceWatchRole.Name) assert.Equal(t, "demo", managerSingleNamespaceWatchRole.Namespace) assert.Equal(t, 14, len(managerSingleNamespaceWatchRole.Rules)) } func TestTemplate_ManagerSingleNamespaceRoleBinding(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{ Logger: logger.Discard, SetValues: map[string]string{ "flags.watchSingleNamespace": "demo", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/manager_single_namespace_controller_role_binding.yaml"}) var managerSingleNamespaceControllerRoleBinding rbacv1.RoleBinding helm.UnmarshalK8SYaml(t, output, &managerSingleNamespaceControllerRoleBinding) assert.Equal(t, "test-arc-gha-rs-controller-single-namespace", managerSingleNamespaceControllerRoleBinding.Name) assert.Equal(t, namespaceName, managerSingleNamespaceControllerRoleBinding.Namespace) assert.Equal(t, "test-arc-gha-rs-controller-single-namespace", managerSingleNamespaceControllerRoleBinding.RoleRef.Name) assert.Equal(t, "test-arc-gha-rs-controller", managerSingleNamespaceControllerRoleBinding.Subjects[0].Name) assert.Equal(t, namespaceName, managerSingleNamespaceControllerRoleBinding.Subjects[0].Namespace) output = helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/manager_single_namespace_watch_role_binding.yaml"}) var managerSingleNamespaceWatchRoleBinding rbacv1.RoleBinding helm.UnmarshalK8SYaml(t, output, &managerSingleNamespaceWatchRoleBinding) assert.Equal(t, "test-arc-gha-rs-controller-single-namespace-watch", managerSingleNamespaceWatchRoleBinding.Name) assert.Equal(t, "demo", managerSingleNamespaceWatchRoleBinding.Namespace) assert.Equal(t, "test-arc-gha-rs-controller-single-namespace-watch", managerSingleNamespaceWatchRoleBinding.RoleRef.Name) assert.Equal(t, "test-arc-gha-rs-controller", managerSingleNamespaceWatchRoleBinding.Subjects[0].Name) assert.Equal(t, namespaceName, managerSingleNamespaceWatchRoleBinding.Subjects[0].Namespace) } func TestControllerDeployment_MetricsPorts(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) chartContent, err := os.ReadFile(filepath.Join(helmChartPath, "Chart.yaml")) require.NoError(t, err) chart := new(Chart) err = yaml.Unmarshal(chartContent, chart) require.NoError(t, err) releaseName := "test-arc" namespaceName := "test-" + strings.ToLower(random.UniqueId()) options := &helm.Options{ Logger: logger.Discard, SetValues: map[string]string{ "image.tag": "dev", "metrics.controllerManagerAddr": ":8080", "metrics.listenerAddr": ":8081", "metrics.listenerEndpoint": "/metrics", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/deployment.yaml"}) var deployment appsv1.Deployment helm.UnmarshalK8SYaml(t, output, &deployment) require.Len(t, deployment.Spec.Template.Spec.Containers, 1, "Expected one container") container := deployment.Spec.Template.Spec.Containers[0] assert.Len(t, container.Ports, 1) port := container.Ports[0] assert.Equal(t, corev1.Protocol("TCP"), port.Protocol) assert.Equal(t, int32(8080), port.ContainerPort) metricsFlags := map[string]*struct { expect string frequency int }{ "--listener-metrics-addr": { expect: ":8081", }, "--listener-metrics-endpoint": { expect: "/metrics", }, "--metrics-addr": { expect: ":8080", }, } for _, cmd := range container.Args { s := strings.Split(cmd, "=") if len(s) != 2 { continue } flag, ok := metricsFlags[s[0]] if !ok { continue } flag.frequency++ assert.Equal(t, flag.expect, s[1]) } for key, value := range metricsFlags { assert.Equal(t, value.frequency, 1, fmt.Sprintf("frequency of %q is not 1", key)) } } func TestDeployment_excludeLabelPropagationPrefixes(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) chartContent, err := os.ReadFile(filepath.Join(helmChartPath, "Chart.yaml")) require.NoError(t, err) chart := new(Chart) err = yaml.Unmarshal(chartContent, chart) require.NoError(t, err) releaseName := "test-arc" namespaceName := "test-" + strings.ToLower(random.UniqueId()) options := &helm.Options{ Logger: logger.Discard, SetValues: map[string]string{ "flags.excludeLabelPropagationPrefixes[0]": "prefix.com/", "flags.excludeLabelPropagationPrefixes[1]": "complete.io/label", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/deployment.yaml"}) var deployment appsv1.Deployment helm.UnmarshalK8SYaml(t, output, &deployment) require.Len(t, deployment.Spec.Template.Spec.Containers, 1, "Expected one container") container := deployment.Spec.Template.Spec.Containers[0] assert.Contains(t, container.Args, "--exclude-label-propagation-prefix=prefix.com/") assert.Contains(t, container.Args, "--exclude-label-propagation-prefix=complete.io/label") } func TestNamespaceOverride(t *testing.T) { t.Parallel() chartPath := "../../gha-runner-scale-set-controller" releaseName := "test" releaseNamespace := "test-" + strings.ToLower(random.UniqueId()) namespaceOverride := "test-" + strings.ToLower(random.UniqueId()) tt := map[string]struct { file string options *helm.Options wantNamespace string }{ "deployment": { file: "deployment.yaml", options: &helm.Options{ Logger: logger.Discard, SetValues: map[string]string{ "namespaceOverride": namespaceOverride, }, KubectlOptions: k8s.NewKubectlOptions("", "", releaseNamespace), }, wantNamespace: namespaceOverride, }, "leader_election_role_binding": { file: "leader_election_role_binding.yaml", options: &helm.Options{ Logger: logger.Discard, SetValues: map[string]string{ "namespaceOverride": namespaceOverride, "replicaCount": "2", }, KubectlOptions: k8s.NewKubectlOptions("", "", releaseNamespace), }, wantNamespace: namespaceOverride, }, "leader_election_role": { file: "leader_election_role.yaml", options: &helm.Options{ Logger: logger.Discard, SetValues: map[string]string{ "namespaceOverride": namespaceOverride, "replicaCount": "2", }, KubectlOptions: k8s.NewKubectlOptions("", "", releaseNamespace), }, wantNamespace: namespaceOverride, }, "manager_listener_role_binding": { file: "manager_listener_role_binding.yaml", options: &helm.Options{ Logger: logger.Discard, SetValues: map[string]string{ "namespaceOverride": namespaceOverride, "replicaCount": "2", }, KubectlOptions: k8s.NewKubectlOptions("", "", releaseNamespace), }, wantNamespace: namespaceOverride, }, "manager_listener_role": { file: "manager_listener_role.yaml", options: &helm.Options{ Logger: logger.Discard, SetValues: map[string]string{ "namespaceOverride": namespaceOverride, "replicaCount": "2", }, KubectlOptions: k8s.NewKubectlOptions("", "", releaseNamespace), }, wantNamespace: namespaceOverride, }, "manager_single_namespace_controller_role": { file: "manager_single_namespace_controller_role.yaml", options: &helm.Options{ Logger: logger.Discard, SetValues: map[string]string{ "namespaceOverride": namespaceOverride, "flags.watchSingleNamespace": "true", }, KubectlOptions: k8s.NewKubectlOptions("", "", releaseNamespace), }, wantNamespace: namespaceOverride, }, "manager_single_namespace_controller_role_binding": { file: "manager_single_namespace_controller_role_binding.yaml", options: &helm.Options{ Logger: logger.Discard, SetValues: map[string]string{ "namespaceOverride": namespaceOverride, "flags.watchSingleNamespace": "true", }, KubectlOptions: k8s.NewKubectlOptions("", "", releaseNamespace), }, wantNamespace: namespaceOverride, }, "manager_single_namespace_watch_role": { file: "manager_single_namespace_watch_role.yaml", options: &helm.Options{ Logger: logger.Discard, SetValues: map[string]string{ "namespaceOverride": namespaceOverride, "flags.watchSingleNamespace": "target-ns", }, KubectlOptions: k8s.NewKubectlOptions("", "", releaseNamespace), }, wantNamespace: "target-ns", }, "manager_single_namespace_watch_role_binding": { file: "manager_single_namespace_watch_role_binding.yaml", options: &helm.Options{ Logger: logger.Discard, SetValues: map[string]string{ "namespaceOverride": namespaceOverride, "flags.watchSingleNamespace": "target-ns", }, KubectlOptions: k8s.NewKubectlOptions("", "", releaseNamespace), }, wantNamespace: "target-ns", }, } for name, tc := range tt { c := tc t.Run(name, func(t *testing.T) { t.Parallel() templateFile := filepath.Join("./templates", c.file) output, err := helm.RenderTemplateE(t, c.options, chartPath, releaseName, []string{templateFile}) if err != nil { t.Errorf("Error rendering template %s from chart %s: %s", c.file, chartPath, err) } type object struct { Metadata metav1.ObjectMeta } var renderedObject object helm.UnmarshalK8SYaml(t, output, &renderedObject) assert.Equal(t, tc.wantNamespace, renderedObject.Metadata.Namespace) }) } }