diff --git a/charts/gha-runner-scale-set-experimental/templates/_helpers.tpl b/charts/gha-runner-scale-set-experimental/templates/_helpers.tpl index c065c5ee..7ddf2220 100644 --- a/charts/gha-runner-scale-set-experimental/templates/_helpers.tpl +++ b/charts/gha-runner-scale-set-experimental/templates/_helpers.tpl @@ -11,6 +11,19 @@ rendered as YAML booleans or numbers. {{- toYaml $out -}} {{- end }} +{{/* +Fail unless a value is absent or a mapping. Used to turn mis-typed metadata values into an +error that names the values path, instead of an opaque "range can't iterate over" further +down the render. +Expects a dict with "value" and "path". +*/}} +{{- define "assert-map" -}} +{{- $value := .value -}} +{{- if and (not (kindIs "invalid" $value)) (not (kindIs "map" $value)) -}} +{{- fail (printf "%s: must be a mapping, got %s" .path (kindOf $value)) -}} +{{- end -}} +{{- end }} + {{/* Validate a label or annotation key against the Kubernetes qualified name rules. Expects a dict with "key", "kind" (label|annotation) and "path" (the values path used in the error message). @@ -49,9 +62,11 @@ Expects a dict with "metadata" and "path". */}} {{- define "validate-metadata" -}} {{- $path := .path -}} +{{- include "assert-map" (dict "value" .metadata "path" $path) -}} {{- $metadata := .metadata | default dict -}} -{{- if kindIs "map" $metadata -}} -{{- range $key, $value := ((index $metadata "labels") | default dict) -}} +{{- $labels := index $metadata "labels" -}} +{{- include "assert-map" (dict "value" $labels "path" (printf "%s.labels" $path)) -}} +{{- range $key, $value := ($labels | default dict) -}} {{- include "validate-metadata-key" (dict "key" $key "kind" "label" "path" (printf "%s.labels" $path)) -}} {{- $rendered := printf "%v" $value -}} {{- if gt (len $rendered) 63 -}} @@ -61,25 +76,26 @@ Expects a dict with "metadata" and "path". {{- fail (printf "%s.labels: invalid value %q for label %q: a valid label value must be an empty string or consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character" $path $rendered $key) -}} {{- end -}} {{- end -}} -{{- range $key, $value := ((index $metadata "annotations") | default dict) -}} +{{- $annotations := index $metadata "annotations" -}} +{{- include "assert-map" (dict "value" $annotations "path" (printf "%s.annotations" $path)) -}} +{{- range $key, $value := ($annotations | default dict) -}} {{- include "validate-metadata-key" (dict "key" $key "kind" "annotation" "path" (printf "%s.annotations" $path)) -}} {{- end -}} -{{- end -}} {{- end }} {{/* Validate every label and annotation map the chart can render onto resources it manages. */}} {{- define "validate-all-metadata" -}} +{{- include "assert-map" (dict "value" .Values.resource "path" ".Values.resource") -}} {{- range $resource, $config := (.Values.resource | default dict) }} -{{- if kindIs "map" $config }} -{{- include "validate-metadata" (dict "metadata" (index $config "metadata") "path" (printf ".Values.resource.%s.metadata" $resource)) -}} -{{- end }} -{{- end }} -{{- $runnerPod := (index (.Values.runner | default dict) "pod") | default dict }} -{{- if kindIs "map" $runnerPod }} -{{- include "validate-metadata" (dict "metadata" (index $runnerPod "metadata") "path" ".Values.runner.pod.metadata") -}} +{{- include "assert-map" (dict "value" $config "path" (printf ".Values.resource.%s" $resource)) -}} +{{- include "validate-metadata" (dict "metadata" (index ($config | default dict) "metadata") "path" (printf ".Values.resource.%s.metadata" $resource)) -}} {{- end }} +{{- include "assert-map" (dict "value" .Values.runner "path" ".Values.runner") -}} +{{- $runnerPod := index (.Values.runner | default dict) "pod" -}} +{{- include "assert-map" (dict "value" $runnerPod "path" ".Values.runner.pod") -}} +{{- include "validate-metadata" (dict "metadata" (index ($runnerPod | default dict) "metadata") "path" ".Values.runner.pod.metadata") -}} {{- end }} {{/* diff --git a/charts/gha-runner-scale-set-experimental/templates/githubsecret.yaml b/charts/gha-runner-scale-set-experimental/templates/githubsecret.yaml index 0630891e..884b0bcb 100644 --- a/charts/gha-runner-scale-set-experimental/templates/githubsecret.yaml +++ b/charts/gha-runner-scale-set-experimental/templates/githubsecret.yaml @@ -1,3 +1,4 @@ +{{- include "validate-all-metadata" . }} {{- $usesKubernetesSecrets := or (not .Values.secretResolution) (eq .Values.secretResolution.type "kubernetes") -}} {{- if and (not $usesKubernetesSecrets) (empty .Values.auth.secretName) -}} diff --git a/charts/gha-runner-scale-set-experimental/templates/hook_extension.yaml b/charts/gha-runner-scale-set-experimental/templates/hook_extension.yaml index d9b4576a..b40e24c0 100644 --- a/charts/gha-runner-scale-set-experimental/templates/hook_extension.yaml +++ b/charts/gha-runner-scale-set-experimental/templates/hook_extension.yaml @@ -1,3 +1,4 @@ +{{- include "validate-all-metadata" . }} {{- $runner := (.Values.runner | default dict) -}} {{- $runnerMode := (index $runner "mode" | default "") -}} {{- $kubeMode := (index $runner "kubernetesMode" | default dict) -}} diff --git a/charts/gha-runner-scale-set-experimental/templates/kube_mode_role.yaml b/charts/gha-runner-scale-set-experimental/templates/kube_mode_role.yaml index acaef34f..168a0610 100644 --- a/charts/gha-runner-scale-set-experimental/templates/kube_mode_role.yaml +++ b/charts/gha-runner-scale-set-experimental/templates/kube_mode_role.yaml @@ -1,3 +1,4 @@ +{{- include "validate-all-metadata" . }} {{- $runner := (.Values.runner | default dict) -}} {{- $runnerMode := (index $runner "mode" | default "") -}} {{- $kubeMode := (index $runner "kubernetesMode" | default dict) -}} diff --git a/charts/gha-runner-scale-set-experimental/templates/kube_mode_role_binding.yaml b/charts/gha-runner-scale-set-experimental/templates/kube_mode_role_binding.yaml index c2ecc565..50334376 100644 --- a/charts/gha-runner-scale-set-experimental/templates/kube_mode_role_binding.yaml +++ b/charts/gha-runner-scale-set-experimental/templates/kube_mode_role_binding.yaml @@ -1,3 +1,4 @@ +{{- include "validate-all-metadata" . }} {{- $runner := (.Values.runner | default dict) -}} {{- $runnerMode := (index $runner "mode" | default "") -}} {{- $kubeMode := (index $runner "kubernetesMode" | default dict) -}} diff --git a/charts/gha-runner-scale-set-experimental/templates/kube_mode_serviceaccount.yaml b/charts/gha-runner-scale-set-experimental/templates/kube_mode_serviceaccount.yaml index 0e8b0b7a..1c9d65a1 100644 --- a/charts/gha-runner-scale-set-experimental/templates/kube_mode_serviceaccount.yaml +++ b/charts/gha-runner-scale-set-experimental/templates/kube_mode_serviceaccount.yaml @@ -1,3 +1,4 @@ +{{- include "validate-all-metadata" . }} {{- $runner := (.Values.runner | default dict) -}} {{- $runnerMode := (index $runner "mode" | default "") -}} {{- $kubeMode := (index $runner "kubernetesMode" | default dict) -}} diff --git a/charts/gha-runner-scale-set-experimental/templates/manager_role.yaml b/charts/gha-runner-scale-set-experimental/templates/manager_role.yaml index 2990ccc4..2ec8b3e7 100644 --- a/charts/gha-runner-scale-set-experimental/templates/manager_role.yaml +++ b/charts/gha-runner-scale-set-experimental/templates/manager_role.yaml @@ -1,3 +1,4 @@ +{{- include "validate-all-metadata" . }} apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: diff --git a/charts/gha-runner-scale-set-experimental/templates/manager_role_binding.yaml b/charts/gha-runner-scale-set-experimental/templates/manager_role_binding.yaml index 56094cec..9cf442ae 100644 --- a/charts/gha-runner-scale-set-experimental/templates/manager_role_binding.yaml +++ b/charts/gha-runner-scale-set-experimental/templates/manager_role_binding.yaml @@ -1,3 +1,4 @@ +{{- include "validate-all-metadata" . }} apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: diff --git a/charts/gha-runner-scale-set-experimental/templates/no_permission_serviceaccount.yaml b/charts/gha-runner-scale-set-experimental/templates/no_permission_serviceaccount.yaml index 6a2d53a4..1a5f99f4 100644 --- a/charts/gha-runner-scale-set-experimental/templates/no_permission_serviceaccount.yaml +++ b/charts/gha-runner-scale-set-experimental/templates/no_permission_serviceaccount.yaml @@ -1,3 +1,4 @@ +{{- include "validate-all-metadata" . }} {{- $runnerMode := (.Values.runner.mode | default "") -}} {{- if ne $runnerMode "kubernetes" -}} apiVersion: v1 diff --git a/charts/gha-runner-scale-set-experimental/tests/autoscaling_runner_set_metadata_validation_test.yaml b/charts/gha-runner-scale-set-experimental/tests/autoscaling_runner_set_metadata_validation_test.yaml index 47f7b237..97cfc980 100644 --- a/charts/gha-runner-scale-set-experimental/tests/autoscaling_runner_set_metadata_validation_test.yaml +++ b/charts/gha-runner-scale-set-experimental/tests/autoscaling_runner_set_metadata_validation_test.yaml @@ -110,3 +110,54 @@ tests: - equal: path: spec.ephemeralRunnerMetadata.annotations["enabled"] value: "true" + + - it: should fail when a metadata block is not a mapping + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + runner: + pod: + metadata: "oops" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - failedTemplate: + errorMessage: '.Values.runner.pod.metadata: must be a mapping, got string' + + - it: should fail when a labels block is not a mapping + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + resource: + all: + metadata: + labels: "oops" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - failedTemplate: + errorMessage: '.Values.resource.all.metadata.labels: must be a mapping, got string' + + - it: should fail when a resource entry is not a mapping + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + resource: + ephemeralRunner: "oops" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - failedTemplate: + errorMessage: '.Values.resource.ephemeralRunner: must be a mapping, got string' diff --git a/charts/gha-runner-scale-set/templates/_helpers.tpl b/charts/gha-runner-scale-set/templates/_helpers.tpl index dfc9fcac..3c1c395c 100644 --- a/charts/gha-runner-scale-set/templates/_helpers.tpl +++ b/charts/gha-runner-scale-set/templates/_helpers.tpl @@ -67,6 +67,19 @@ rendered as YAML booleans or numbers. {{- toYaml $out -}} {{- end }} +{{/* +Fail unless a value is absent or a mapping. Used to turn mis-typed metadata values into an +error that names the values path, instead of an opaque "range can't iterate over" further +down the render. +Expects a dict with "value" and "path". +*/}} +{{- define "gha-runner-scale-set.assertMap" -}} +{{- $value := .value -}} +{{- if and (not (kindIs "invalid" $value)) (not (kindIs "map" $value)) -}} +{{- fail (printf "%s: must be a mapping, got %s" .path (kindOf $value)) -}} +{{- end -}} +{{- end }} + {{/* Validate a label or annotation key against the Kubernetes qualified name rules. Expects a dict with "key", "kind" (label|annotation) and "path" (the values path used in the error message). @@ -104,6 +117,7 @@ Expects a dict with "labels" and "path". */}} {{- define "gha-runner-scale-set.validateLabels" -}} {{- $path := .path -}} +{{- include "gha-runner-scale-set.assertMap" (dict "value" .labels "path" $path) -}} {{- range $key, $value := (.labels | default dict) -}} {{- include "gha-runner-scale-set.validateMetadataKey" (dict "key" $key "kind" "label" "path" $path) -}} {{- $rendered := printf "%v" $value -}} @@ -122,6 +136,7 @@ Expects a dict with "annotations" and "path". */}} {{- define "gha-runner-scale-set.validateAnnotations" -}} {{- $path := .path -}} +{{- include "gha-runner-scale-set.assertMap" (dict "value" .annotations "path" $path) -}} {{- range $key, $value := (.annotations | default dict) -}} {{- include "gha-runner-scale-set.validateMetadataKey" (dict "key" $key "kind" "annotation" "path" $path) -}} {{- end -}} @@ -133,19 +148,20 @@ Validate every label and annotation map the chart can render onto resources it m {{- define "gha-runner-scale-set.validateMetadata" -}} {{- include "gha-runner-scale-set.validateLabels" (dict "labels" .Values.labels "path" ".Values.labels") -}} {{- include "gha-runner-scale-set.validateAnnotations" (dict "annotations" .Values.annotations "path" ".Values.annotations") -}} -{{- with .Values.template }} -{{- with .metadata }} -{{- include "gha-runner-scale-set.validateLabels" (dict "labels" .labels "path" ".Values.template.metadata.labels") -}} -{{- include "gha-runner-scale-set.validateAnnotations" (dict "annotations" .annotations "path" ".Values.template.metadata.annotations") -}} -{{- end }} -{{- end }} +{{- include "gha-runner-scale-set.assertMap" (dict "value" .Values.template "path" ".Values.template") -}} +{{- $templateMetadata := index (.Values.template | default dict) "metadata" -}} +{{- include "gha-runner-scale-set.assertMap" (dict "value" $templateMetadata "path" ".Values.template.metadata") -}} +{{- $templateMetadata = $templateMetadata | default dict -}} +{{- include "gha-runner-scale-set.validateLabels" (dict "labels" (index $templateMetadata "labels") "path" ".Values.template.metadata.labels") -}} +{{- include "gha-runner-scale-set.validateAnnotations" (dict "annotations" (index $templateMetadata "annotations") "path" ".Values.template.metadata.annotations") -}} +{{- include "gha-runner-scale-set.assertMap" (dict "value" .Values.resourceMeta "path" ".Values.resourceMeta") -}} {{- range $resource, $meta := (.Values.resourceMeta | default dict) }} -{{- if kindIs "map" $meta }} +{{- include "gha-runner-scale-set.assertMap" (dict "value" $meta "path" (printf ".Values.resourceMeta.%s" $resource)) -}} +{{- $meta = $meta | default dict -}} {{- include "gha-runner-scale-set.validateLabels" (dict "labels" (index $meta "labels") "path" (printf ".Values.resourceMeta.%s.labels" $resource)) -}} {{- include "gha-runner-scale-set.validateAnnotations" (dict "annotations" (index $meta "annotations") "path" (printf ".Values.resourceMeta.%s.annotations" $resource)) -}} {{- end }} {{- end }} -{{- end }} {{/* Render a ResourceMeta block for AutoscalingRunnerSet spec fields. diff --git a/charts/gha-runner-scale-set/templates/githubsecret.yaml b/charts/gha-runner-scale-set/templates/githubsecret.yaml index 170c2f33..5df122b0 100644 --- a/charts/gha-runner-scale-set/templates/githubsecret.yaml +++ b/charts/gha-runner-scale-set/templates/githubsecret.yaml @@ -1,3 +1,4 @@ +{{- include "gha-runner-scale-set.validateMetadata" . }} {{- if not (kindIs "string" .Values.githubConfigSecret) }} {{- $hasCustomResourceMeta := (and .Values.resourceMeta .Values.resourceMeta.githubConfigSecret) }} apiVersion: v1 diff --git a/charts/gha-runner-scale-set/templates/kube_mode_role.yaml b/charts/gha-runner-scale-set/templates/kube_mode_role.yaml index de59c69f..5a6cb7c4 100644 --- a/charts/gha-runner-scale-set/templates/kube_mode_role.yaml +++ b/charts/gha-runner-scale-set/templates/kube_mode_role.yaml @@ -1,3 +1,4 @@ +{{- include "gha-runner-scale-set.validateMetadata" . }} {{- $containerMode := .Values.containerMode }} {{- $hasCustomResourceMeta := (and .Values.resourceMeta .Values.resourceMeta.kubernetesModeRole) }} {{- if and (or (eq $containerMode.type "kubernetes") (eq $containerMode.type "kubernetes-novolume")) (not .Values.template.spec.serviceAccountName) }} diff --git a/charts/gha-runner-scale-set/templates/kube_mode_role_binding.yaml b/charts/gha-runner-scale-set/templates/kube_mode_role_binding.yaml index 4145b559..bf1c6cf0 100644 --- a/charts/gha-runner-scale-set/templates/kube_mode_role_binding.yaml +++ b/charts/gha-runner-scale-set/templates/kube_mode_role_binding.yaml @@ -1,3 +1,4 @@ +{{- include "gha-runner-scale-set.validateMetadata" . }} {{- $containerMode := .Values.containerMode }} {{- $hasCustomResourceMeta := (and .Values.resourceMeta .Values.resourceMeta.kubernetesModeRoleBinding) }} {{- if and (or (eq $containerMode.type "kubernetes") (eq $containerMode.type "kubernetes-novolume")) (not .Values.template.spec.serviceAccountName) }} diff --git a/charts/gha-runner-scale-set/templates/kube_mode_serviceaccount.yaml b/charts/gha-runner-scale-set/templates/kube_mode_serviceaccount.yaml index 44d977c5..d6255d3d 100644 --- a/charts/gha-runner-scale-set/templates/kube_mode_serviceaccount.yaml +++ b/charts/gha-runner-scale-set/templates/kube_mode_serviceaccount.yaml @@ -1,3 +1,4 @@ +{{- include "gha-runner-scale-set.validateMetadata" . }} {{- $containerMode := .Values.containerMode }} {{- $hasCustomResourceMeta := (and .Values.resourceMeta .Values.resourceMeta.kubernetesModeServiceAccount) }} {{- if and (or (eq $containerMode.type "kubernetes") (eq $containerMode.type "kubernetes-novolume")) (not .Values.template.spec.serviceAccountName) }} diff --git a/charts/gha-runner-scale-set/templates/manager_role.yaml b/charts/gha-runner-scale-set/templates/manager_role.yaml index 84c159c7..fdb41eea 100644 --- a/charts/gha-runner-scale-set/templates/manager_role.yaml +++ b/charts/gha-runner-scale-set/templates/manager_role.yaml @@ -1,3 +1,4 @@ +{{- include "gha-runner-scale-set.validateMetadata" . }} {{- $hasCustomResourceMeta := (and .Values.resourceMeta .Values.resourceMeta.managerRole) }} apiVersion: rbac.authorization.k8s.io/v1 kind: Role diff --git a/charts/gha-runner-scale-set/templates/manager_role_binding.yaml b/charts/gha-runner-scale-set/templates/manager_role_binding.yaml index 503ef119..c4296b32 100644 --- a/charts/gha-runner-scale-set/templates/manager_role_binding.yaml +++ b/charts/gha-runner-scale-set/templates/manager_role_binding.yaml @@ -1,3 +1,4 @@ +{{- include "gha-runner-scale-set.validateMetadata" . }} {{- $hasCustomResourceMeta := (and .Values.resourceMeta .Values.resourceMeta.managerRoleBinding) }} apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding diff --git a/charts/gha-runner-scale-set/templates/no_permission_serviceaccount.yaml b/charts/gha-runner-scale-set/templates/no_permission_serviceaccount.yaml index c7cdb1c5..a14f3531 100644 --- a/charts/gha-runner-scale-set/templates/no_permission_serviceaccount.yaml +++ b/charts/gha-runner-scale-set/templates/no_permission_serviceaccount.yaml @@ -1,3 +1,4 @@ +{{- include "gha-runner-scale-set.validateMetadata" . }} {{- $hasCustomResourceMeta := (and .Values.resourceMeta .Values.resourceMeta.noPermissionServiceAccount) }} {{- $containerMode := .Values.containerMode }} {{- if and (ne $containerMode.type "kubernetes") (ne $containerMode.type "kubernetes-novolume") (not .Values.template.spec.serviceAccountName) }} diff --git a/charts/gha-runner-scale-set/tests/template_test.go b/charts/gha-runner-scale-set/tests/template_test.go index a2bce81f..46f7470a 100644 --- a/charts/gha-runner-scale-set/tests/template_test.go +++ b/charts/gha-runner-scale-set/tests/template_test.go @@ -3256,3 +3256,110 @@ func TestTemplateRenderedAutoScalingRunnerSet_ValidMetadataIsAccepted(t *testing assert.Equal(t, "", autoscalingRunnerSet.Spec.Template.Labels["empty"]) assert.Equal(t, "any value is allowed: ✅", autoscalingRunnerSet.Spec.Template.Annotations["example.com/an"]) } + +// Kubernetes only accepts string label and annotation values. Values supplied as unquoted +// YAML scalars parse as bools/numbers, so the chart has to coerce them when rendering. +// SetValues always yields strings, so this has to come from a values file to be meaningful. +func TestTemplateRenderedAutoScalingRunnerSet_ScalarMetadataValuesAreRenderedAsStrings(t *testing.T) { + t.Parallel() + + helmChartPath, err := filepath.Abs("../../gha-runner-scale-set") + require.NoError(t, err) + + testValuesPath, err := filepath.Abs("../tests/values_scalar_metadata.yaml") + require.NoError(t, err) + + releaseName := "test-runners" + namespaceName := "test-" + strings.ToLower(random.UniqueID()) + + options := &helm.Options{ + Logger: logger.Discard, + ValuesFiles: []string{testValuesPath}, + KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), + } + + // UnmarshalK8SYaml fails outright if a value decodes as a bool or number rather than a + // string, so a successful decode is itself part of the assertion. + output := helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"}) + + var autoscalingRunnerSet v1alpha1.AutoscalingRunnerSet + helm.UnmarshalK8SYaml(t, output, &autoscalingRunnerSet) + + assert.Equal(t, "true", autoscalingRunnerSet.Labels["chart-bool"]) + assert.Equal(t, "1", autoscalingRunnerSet.Labels["chart-int"]) + assert.Equal(t, "false", autoscalingRunnerSet.Annotations["chart-bool-annotation"]) + assert.Equal(t, "1.5", autoscalingRunnerSet.Annotations["chart-float-annotation"]) + + assert.Equal(t, "true", autoscalingRunnerSet.Spec.Template.Labels["pod-bool"]) + assert.Equal(t, "42", autoscalingRunnerSet.Spec.Template.Labels["pod-int"]) + assert.Equal(t, "true", autoscalingRunnerSet.Spec.Template.Annotations["pod-bool-annotation"]) + assert.Equal(t, "7", autoscalingRunnerSet.Spec.Template.Annotations["pod-int-annotation"]) + + require.NotNil(t, autoscalingRunnerSet.Spec.EphemeralRunnerMetadata) + assert.Equal(t, "false", autoscalingRunnerSet.Spec.EphemeralRunnerMetadata.Labels["runner-bool"]) + assert.Equal(t, "3", autoscalingRunnerSet.Spec.EphemeralRunnerMetadata.Labels["runner-int"]) + assert.Equal(t, "9", autoscalingRunnerSet.Spec.EphemeralRunnerMetadata.Annotations["runner-int-annotation"]) + + output = helm.RenderTemplateContext(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/githubsecret.yaml"}) + + var githubSecret corev1.Secret + helm.UnmarshalK8SYaml(t, output, &githubSecret) + + assert.Equal(t, "5", githubSecret.Labels["secret-int"]) + assert.Equal(t, "true", githubSecret.Labels["chart-bool"]) + assert.Equal(t, "1.5", githubSecret.Annotations["chart-float-annotation"]) +} + +func TestTemplateRenderedAutoScalingRunnerSet_NonMapMetadataValidationError(t *testing.T) { + t.Parallel() + + helmChartPath, err := filepath.Abs("../../gha-runner-scale-set") + require.NoError(t, err) + + tt := map[string]struct { + key string + value string + expectedError string + }{ + "chart labels is not a map": { + key: "labels", + value: "oops", + expectedError: ".Values.labels: must be a mapping, got string", + }, + "runner pod labels is not a map": { + key: "template.metadata.labels", + value: "oops", + expectedError: ".Values.template.metadata.labels: must be a mapping, got string", + }, + "resourceMeta entry is not a map": { + key: "resourceMeta.githubConfigSecret", + value: "oops", + expectedError: ".Values.resourceMeta.githubConfigSecret: must be a mapping, got string", + }, + } + + for name, tc := range tt { + t.Run(name, func(t *testing.T) { + t.Parallel() + + releaseName := "test-runners" + namespaceName := "test-" + strings.ToLower(random.UniqueID()) + + options := &helm.Options{ + Logger: logger.Discard, + SetValues: map[string]string{ + "githubConfigUrl": "https://github.com/actions", + "githubConfigSecret.github_token": "gh_token12345", + "controllerServiceAccount.name": "arc", + "controllerServiceAccount.namespace": "arc-system", + tc.key: tc.value, + }, + KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), + } + + _, err := helm.RenderTemplateContextE(t, t.Context(), options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"}) + require.Error(t, err) + assert.ErrorContains(t, err, tc.expectedError) + }) + } +} diff --git a/charts/gha-runner-scale-set/tests/values_scalar_metadata.yaml b/charts/gha-runner-scale-set/tests/values_scalar_metadata.yaml new file mode 100644 index 00000000..c1898e78 --- /dev/null +++ b/charts/gha-runner-scale-set/tests/values_scalar_metadata.yaml @@ -0,0 +1,36 @@ +githubConfigUrl: https://github.com/actions +githubConfigSecret: + github_token: gh_token12345 +controllerServiceAccount: + name: arc + namespace: arc-system + +# Every value below is an unquoted YAML scalar, so it parses as a bool, int or float +# rather than a string. Kubernetes only accepts string label and annotation values, so +# the chart has to coerce these when rendering. +labels: + chart-bool: true + chart-int: 1 +annotations: + chart-bool-annotation: false + chart-float-annotation: 1.5 + +template: + metadata: + labels: + pod-bool: true + pod-int: 42 + annotations: + pod-bool-annotation: true + pod-int-annotation: 7 + +resourceMeta: + ephemeralRunner: + labels: + runner-bool: false + runner-int: 3 + annotations: + runner-int-annotation: 9 + githubConfigSecret: + labels: + secret-int: 5