diff --git a/charts/gha-runner-scale-set-controller/templates/deployment.yaml b/charts/gha-runner-scale-set-controller/templates/deployment.yaml index e0dd8297..66b9a4b5 100644 --- a/charts/gha-runner-scale-set-controller/templates/deployment.yaml +++ b/charts/gha-runner-scale-set-controller/templates/deployment.yaml @@ -79,6 +79,9 @@ spec: - "--listener-metrics-endpoint=" - "--metrics-addr=0" {{- end }} + {{- range .Values.flags.excludeLabelPropagationPrefixes }} + - "--exclude-label-propagation-prefix={{ . }}" + {{- end }} command: - "/manager" {{- with .Values.metrics }} diff --git a/charts/gha-runner-scale-set-controller/tests/template_test.go b/charts/gha-runner-scale-set-controller/tests/template_test.go index 125ef0b0..afee95a2 100644 --- a/charts/gha-runner-scale-set-controller/tests/template_test.go +++ b/charts/gha-runner-scale-set-controller/tests/template_test.go @@ -1035,3 +1035,41 @@ func TestControllerDeployment_MetricsPorts(t *testing.T) { 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") +} diff --git a/charts/gha-runner-scale-set-controller/values.yaml b/charts/gha-runner-scale-set-controller/values.yaml index efd39958..8e74317e 100644 --- a/charts/gha-runner-scale-set-controller/values.yaml +++ b/charts/gha-runner-scale-set-controller/values.yaml @@ -121,3 +121,12 @@ flags: ## This can lead to a longer time to apply the change but it will ensure ## that you don't have any overprovisioning of runners. updateStrategy: "immediate" + + ## Defines a list of prefixes that should not be propagated to internal resources. + ## This is useful when you have labels that are used for internal purposes and should not be propagated to internal resources. + ## See https://github.com/actions/actions-runner-controller/issues/3533 for more information. + ## + ## By default, all labels are propagated to internal resources + ## Labels that match prefix specified in the list are excluded from propagation. + # excludeLabelPropagationPrefixes: + # - "argocd.argoproj.io/instance" diff --git a/controllers/actions.github.com/autoscalinglistener_controller.go b/controllers/actions.github.com/autoscalinglistener_controller.go index f35c85e9..b97b8424 100644 --- a/controllers/actions.github.com/autoscalinglistener_controller.go +++ b/controllers/actions.github.com/autoscalinglistener_controller.go @@ -55,7 +55,7 @@ type AutoscalingListenerReconciler struct { ListenerMetricsAddr string ListenerMetricsEndpoint string - resourceBuilder resourceBuilder + ResourceBuilder } // +kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;watch;create;update;patch;delete @@ -373,7 +373,7 @@ func (r *AutoscalingListenerReconciler) cleanupResources(ctx context.Context, au } func (r *AutoscalingListenerReconciler) createServiceAccountForListener(ctx context.Context, autoscalingListener *v1alpha1.AutoscalingListener, logger logr.Logger) (ctrl.Result, error) { - newServiceAccount := r.resourceBuilder.newScaleSetListenerServiceAccount(autoscalingListener) + newServiceAccount := r.ResourceBuilder.newScaleSetListenerServiceAccount(autoscalingListener) if err := ctrl.SetControllerReference(autoscalingListener, newServiceAccount, r.Scheme); err != nil { return ctrl.Result{}, err @@ -458,7 +458,7 @@ func (r *AutoscalingListenerReconciler) createListenerPod(ctx context.Context, a logger.Info("Creating listener config secret") - podConfig, err := r.resourceBuilder.newScaleSetListenerConfig(autoscalingListener, secret, metricsConfig, cert) + podConfig, err := r.ResourceBuilder.newScaleSetListenerConfig(autoscalingListener, secret, metricsConfig, cert) if err != nil { logger.Error(err, "Failed to build listener config secret") return ctrl.Result{}, err @@ -477,7 +477,7 @@ func (r *AutoscalingListenerReconciler) createListenerPod(ctx context.Context, a return ctrl.Result{Requeue: true}, nil } - newPod, err := r.resourceBuilder.newScaleSetListenerPod(autoscalingListener, &podConfig, serviceAccount, secret, metricsConfig, envs...) + newPod, err := r.ResourceBuilder.newScaleSetListenerPod(autoscalingListener, &podConfig, serviceAccount, secret, metricsConfig, envs...) if err != nil { logger.Error(err, "Failed to build listener pod") return ctrl.Result{}, err @@ -537,7 +537,7 @@ func (r *AutoscalingListenerReconciler) certificate(ctx context.Context, autosca } func (r *AutoscalingListenerReconciler) createSecretsForListener(ctx context.Context, autoscalingListener *v1alpha1.AutoscalingListener, secret *corev1.Secret, logger logr.Logger) (ctrl.Result, error) { - newListenerSecret := r.resourceBuilder.newScaleSetListenerSecretMirror(autoscalingListener, secret) + newListenerSecret := r.ResourceBuilder.newScaleSetListenerSecretMirror(autoscalingListener, secret) if err := ctrl.SetControllerReference(autoscalingListener, newListenerSecret, r.Scheme); err != nil { return ctrl.Result{}, err @@ -609,7 +609,7 @@ func (r *AutoscalingListenerReconciler) updateSecretsForListener(ctx context.Con } func (r *AutoscalingListenerReconciler) createRoleForListener(ctx context.Context, autoscalingListener *v1alpha1.AutoscalingListener, logger logr.Logger) (ctrl.Result, error) { - newRole := r.resourceBuilder.newScaleSetListenerRole(autoscalingListener) + newRole := r.ResourceBuilder.newScaleSetListenerRole(autoscalingListener) logger.Info("Creating listener role", "namespace", newRole.Namespace, "name", newRole.Name, "rules", newRole.Rules) if err := r.Create(ctx, newRole); err != nil { @@ -637,7 +637,7 @@ func (r *AutoscalingListenerReconciler) updateRoleForListener(ctx context.Contex } func (r *AutoscalingListenerReconciler) createRoleBindingForListener(ctx context.Context, autoscalingListener *v1alpha1.AutoscalingListener, listenerRole *rbacv1.Role, serviceAccount *corev1.ServiceAccount, logger logr.Logger) (ctrl.Result, error) { - newRoleBinding := r.resourceBuilder.newScaleSetListenerRoleBinding(autoscalingListener, listenerRole, serviceAccount) + newRoleBinding := r.ResourceBuilder.newScaleSetListenerRoleBinding(autoscalingListener, listenerRole, serviceAccount) logger.Info("Creating listener role binding", "namespace", newRoleBinding.Namespace, diff --git a/controllers/actions.github.com/autoscalingrunnerset_controller.go b/controllers/actions.github.com/autoscalingrunnerset_controller.go index 7030c7ba..6746df3d 100644 --- a/controllers/actions.github.com/autoscalingrunnerset_controller.go +++ b/controllers/actions.github.com/autoscalingrunnerset_controller.go @@ -79,8 +79,7 @@ type AutoscalingRunnerSetReconciler struct { DefaultRunnerScaleSetListenerImagePullSecrets []string UpdateStrategy UpdateStrategy ActionsClient actions.MultiClient - - resourceBuilder resourceBuilder + ResourceBuilder } // +kubebuilder:rbac:groups=actions.github.com,resources=autoscalingrunnersets,verbs=get;list;watch;create;update;patch;delete @@ -623,7 +622,7 @@ func (r *AutoscalingRunnerSetReconciler) deleteRunnerScaleSet(ctx context.Contex } func (r *AutoscalingRunnerSetReconciler) createEphemeralRunnerSet(ctx context.Context, autoscalingRunnerSet *v1alpha1.AutoscalingRunnerSet, log logr.Logger) (ctrl.Result, error) { - desiredRunnerSet, err := r.resourceBuilder.newEphemeralRunnerSet(autoscalingRunnerSet) + desiredRunnerSet, err := r.ResourceBuilder.newEphemeralRunnerSet(autoscalingRunnerSet) if err != nil { log.Error(err, "Could not create EphemeralRunnerSet") return ctrl.Result{}, err @@ -652,7 +651,7 @@ func (r *AutoscalingRunnerSetReconciler) createAutoScalingListenerForRunnerSet(c }) } - autoscalingListener, err := r.resourceBuilder.newAutoScalingListener(autoscalingRunnerSet, ephemeralRunnerSet, r.ControllerNamespace, r.DefaultRunnerScaleSetListenerImage, imagePullSecrets) + autoscalingListener, err := r.ResourceBuilder.newAutoScalingListener(autoscalingRunnerSet, ephemeralRunnerSet, r.ControllerNamespace, r.DefaultRunnerScaleSetListenerImage, imagePullSecrets) if err != nil { log.Error(err, "Could not create AutoscalingListener spec") return ctrl.Result{}, err diff --git a/controllers/actions.github.com/ephemeralrunner_controller.go b/controllers/actions.github.com/ephemeralrunner_controller.go index eeb30c28..36ea1146 100644 --- a/controllers/actions.github.com/ephemeralrunner_controller.go +++ b/controllers/actions.github.com/ephemeralrunner_controller.go @@ -49,10 +49,10 @@ const ( // EphemeralRunnerReconciler reconciles a EphemeralRunner object type EphemeralRunnerReconciler struct { client.Client - Log logr.Logger - Scheme *runtime.Scheme - ActionsClient actions.MultiClient - resourceBuilder resourceBuilder + Log logr.Logger + Scheme *runtime.Scheme + ActionsClient actions.MultiClient + ResourceBuilder } // +kubebuilder:rbac:groups=actions.github.com,resources=ephemeralrunners,verbs=get;list;watch;create;update;patch;delete @@ -642,7 +642,7 @@ func (r *EphemeralRunnerReconciler) createPod(ctx context.Context, runner *v1alp } log.Info("Creating new pod for ephemeral runner") - newPod := r.resourceBuilder.newEphemeralRunnerPod(ctx, runner, secret, envs...) + newPod := r.ResourceBuilder.newEphemeralRunnerPod(ctx, runner, secret, envs...) if err := ctrl.SetControllerReference(runner, newPod, r.Scheme); err != nil { log.Error(err, "Failed to set controller reference to a new pod") @@ -667,7 +667,7 @@ func (r *EphemeralRunnerReconciler) createPod(ctx context.Context, runner *v1alp func (r *EphemeralRunnerReconciler) createSecret(ctx context.Context, runner *v1alpha1.EphemeralRunner, log logr.Logger) (ctrl.Result, error) { log.Info("Creating new secret for ephemeral runner") - jitSecret := r.resourceBuilder.newEphemeralRunnerJitSecret(runner) + jitSecret := r.ResourceBuilder.newEphemeralRunnerJitSecret(runner) if err := ctrl.SetControllerReference(runner, jitSecret, r.Scheme); err != nil { return ctrl.Result{}, fmt.Errorf("failed to set controller reference: %v", err) diff --git a/controllers/actions.github.com/ephemeralrunnerset_controller.go b/controllers/actions.github.com/ephemeralrunnerset_controller.go index c5d166a5..c1c2523e 100644 --- a/controllers/actions.github.com/ephemeralrunnerset_controller.go +++ b/controllers/actions.github.com/ephemeralrunnerset_controller.go @@ -53,7 +53,7 @@ type EphemeralRunnerSetReconciler struct { PublishMetrics bool - resourceBuilder resourceBuilder + ResourceBuilder } //+kubebuilder:rbac:groups=actions.github.com,resources=ephemeralrunnersets,verbs=get;list;watch;create;update;patch;delete @@ -360,7 +360,7 @@ func (r *EphemeralRunnerSetReconciler) createEphemeralRunners(ctx context.Contex // Track multiple errors at once and return the bundle. errs := make([]error, 0) for i := 0; i < count; i++ { - ephemeralRunner := r.resourceBuilder.newEphemeralRunner(runnerSet) + ephemeralRunner := r.ResourceBuilder.newEphemeralRunner(runnerSet) if runnerSet.Spec.EphemeralRunnerSpec.Proxy != nil { ephemeralRunner.Spec.ProxySecretRef = proxyEphemeralRunnerSetSecretName(runnerSet) } diff --git a/controllers/actions.github.com/resourcebuilder.go b/controllers/actions.github.com/resourcebuilder.go index 49bdcac0..57fd7257 100644 --- a/controllers/actions.github.com/resourcebuilder.go +++ b/controllers/actions.github.com/resourcebuilder.go @@ -8,6 +8,7 @@ import ( "math" "net" "strconv" + "strings" "github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1" "github.com/actions/actions-runner-controller/build" @@ -68,9 +69,11 @@ func SetListenerEntrypoint(entrypoint string) { } } -type resourceBuilder struct{} +type ResourceBuilder struct { + ExcludeLabelPropagationPrefixes []string +} -func (b *resourceBuilder) newAutoScalingListener(autoscalingRunnerSet *v1alpha1.AutoscalingRunnerSet, ephemeralRunnerSet *v1alpha1.EphemeralRunnerSet, namespace, image string, imagePullSecrets []corev1.LocalObjectReference) (*v1alpha1.AutoscalingListener, error) { +func (b *ResourceBuilder) newAutoScalingListener(autoscalingRunnerSet *v1alpha1.AutoscalingRunnerSet, ephemeralRunnerSet *v1alpha1.EphemeralRunnerSet, namespace, image string, imagePullSecrets []corev1.LocalObjectReference) (*v1alpha1.AutoscalingListener, error) { runnerScaleSetId, err := strconv.Atoi(autoscalingRunnerSet.Annotations[runnerScaleSetIdAnnotationKey]) if err != nil { return nil, err @@ -85,7 +88,7 @@ func (b *resourceBuilder) newAutoScalingListener(autoscalingRunnerSet *v1alpha1. effectiveMinRunners = *autoscalingRunnerSet.Spec.MinRunners } - labels := mergeLabels(autoscalingRunnerSet.Labels, map[string]string{ + labels := b.mergeLabels(autoscalingRunnerSet.Labels, map[string]string{ LabelKeyGitHubScaleSetNamespace: autoscalingRunnerSet.Namespace, LabelKeyGitHubScaleSetName: autoscalingRunnerSet.Name, LabelKeyKubernetesPartOf: labelValueKubernetesPartOf, @@ -150,7 +153,7 @@ func (lm *listenerMetricsServerConfig) containerPort() (corev1.ContainerPort, er }, nil } -func (b *resourceBuilder) newScaleSetListenerConfig(autoscalingListener *v1alpha1.AutoscalingListener, secret *corev1.Secret, metricsConfig *listenerMetricsServerConfig, cert string) (*corev1.Secret, error) { +func (b *ResourceBuilder) newScaleSetListenerConfig(autoscalingListener *v1alpha1.AutoscalingListener, secret *corev1.Secret, metricsConfig *listenerMetricsServerConfig, cert string) (*corev1.Secret, error) { var ( metricsAddr = "" metricsEndpoint = "" @@ -213,7 +216,7 @@ func (b *resourceBuilder) newScaleSetListenerConfig(autoscalingListener *v1alpha }, nil } -func (b *resourceBuilder) newScaleSetListenerPod(autoscalingListener *v1alpha1.AutoscalingListener, podConfig *corev1.Secret, serviceAccount *corev1.ServiceAccount, secret *corev1.Secret, metricsConfig *listenerMetricsServerConfig, envs ...corev1.EnvVar) (*corev1.Pod, error) { +func (b *ResourceBuilder) newScaleSetListenerPod(autoscalingListener *v1alpha1.AutoscalingListener, podConfig *corev1.Secret, serviceAccount *corev1.ServiceAccount, secret *corev1.Secret, metricsConfig *listenerMetricsServerConfig, envs ...corev1.EnvVar) (*corev1.Pod, error) { listenerEnv := []corev1.EnvVar{ { Name: "LISTENER_CONFIG_PATH", @@ -406,12 +409,12 @@ func mergeListenerContainer(base, from *corev1.Container) { base.TTY = from.TTY } -func (b *resourceBuilder) newScaleSetListenerServiceAccount(autoscalingListener *v1alpha1.AutoscalingListener) *corev1.ServiceAccount { +func (b *ResourceBuilder) newScaleSetListenerServiceAccount(autoscalingListener *v1alpha1.AutoscalingListener) *corev1.ServiceAccount { return &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ Name: scaleSetListenerServiceAccountName(autoscalingListener), Namespace: autoscalingListener.Namespace, - Labels: mergeLabels(autoscalingListener.Labels, map[string]string{ + Labels: b.mergeLabels(autoscalingListener.Labels, map[string]string{ LabelKeyGitHubScaleSetNamespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace, LabelKeyGitHubScaleSetName: autoscalingListener.Spec.AutoscalingRunnerSetName, }), @@ -419,14 +422,14 @@ func (b *resourceBuilder) newScaleSetListenerServiceAccount(autoscalingListener } } -func (b *resourceBuilder) newScaleSetListenerRole(autoscalingListener *v1alpha1.AutoscalingListener) *rbacv1.Role { +func (b *ResourceBuilder) newScaleSetListenerRole(autoscalingListener *v1alpha1.AutoscalingListener) *rbacv1.Role { rules := rulesForListenerRole([]string{autoscalingListener.Spec.EphemeralRunnerSetName}) rulesHash := hash.ComputeTemplateHash(&rules) newRole := &rbacv1.Role{ ObjectMeta: metav1.ObjectMeta{ Name: scaleSetListenerRoleName(autoscalingListener), Namespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace, - Labels: mergeLabels(autoscalingListener.Labels, map[string]string{ + Labels: b.mergeLabels(autoscalingListener.Labels, map[string]string{ LabelKeyGitHubScaleSetNamespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace, LabelKeyGitHubScaleSetName: autoscalingListener.Spec.AutoscalingRunnerSetName, labelKeyListenerNamespace: autoscalingListener.Namespace, @@ -440,7 +443,7 @@ func (b *resourceBuilder) newScaleSetListenerRole(autoscalingListener *v1alpha1. return newRole } -func (b *resourceBuilder) newScaleSetListenerRoleBinding(autoscalingListener *v1alpha1.AutoscalingListener, listenerRole *rbacv1.Role, serviceAccount *corev1.ServiceAccount) *rbacv1.RoleBinding { +func (b *ResourceBuilder) newScaleSetListenerRoleBinding(autoscalingListener *v1alpha1.AutoscalingListener, listenerRole *rbacv1.Role, serviceAccount *corev1.ServiceAccount) *rbacv1.RoleBinding { roleRef := rbacv1.RoleRef{ Kind: "Role", Name: listenerRole.Name, @@ -460,7 +463,7 @@ func (b *resourceBuilder) newScaleSetListenerRoleBinding(autoscalingListener *v1 ObjectMeta: metav1.ObjectMeta{ Name: scaleSetListenerRoleName(autoscalingListener), Namespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace, - Labels: mergeLabels(autoscalingListener.Labels, map[string]string{ + Labels: b.mergeLabels(autoscalingListener.Labels, map[string]string{ LabelKeyGitHubScaleSetNamespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace, LabelKeyGitHubScaleSetName: autoscalingListener.Spec.AutoscalingRunnerSetName, labelKeyListenerNamespace: autoscalingListener.Namespace, @@ -476,14 +479,14 @@ func (b *resourceBuilder) newScaleSetListenerRoleBinding(autoscalingListener *v1 return newRoleBinding } -func (b *resourceBuilder) newScaleSetListenerSecretMirror(autoscalingListener *v1alpha1.AutoscalingListener, secret *corev1.Secret) *corev1.Secret { +func (b *ResourceBuilder) newScaleSetListenerSecretMirror(autoscalingListener *v1alpha1.AutoscalingListener, secret *corev1.Secret) *corev1.Secret { dataHash := hash.ComputeTemplateHash(&secret.Data) newListenerSecret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: scaleSetListenerSecretMirrorName(autoscalingListener), Namespace: autoscalingListener.Namespace, - Labels: mergeLabels(autoscalingListener.Labels, map[string]string{ + Labels: b.mergeLabels(autoscalingListener.Labels, map[string]string{ LabelKeyGitHubScaleSetNamespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace, LabelKeyGitHubScaleSetName: autoscalingListener.Spec.AutoscalingRunnerSetName, "secret-data-hash": dataHash, @@ -495,14 +498,14 @@ func (b *resourceBuilder) newScaleSetListenerSecretMirror(autoscalingListener *v return newListenerSecret } -func (b *resourceBuilder) newEphemeralRunnerSet(autoscalingRunnerSet *v1alpha1.AutoscalingRunnerSet) (*v1alpha1.EphemeralRunnerSet, error) { +func (b *ResourceBuilder) newEphemeralRunnerSet(autoscalingRunnerSet *v1alpha1.AutoscalingRunnerSet) (*v1alpha1.EphemeralRunnerSet, error) { runnerScaleSetId, err := strconv.Atoi(autoscalingRunnerSet.Annotations[runnerScaleSetIdAnnotationKey]) if err != nil { return nil, err } runnerSpecHash := autoscalingRunnerSet.RunnerSetSpecHash() - labels := mergeLabels(autoscalingRunnerSet.Labels, map[string]string{ + labels := b.mergeLabels(autoscalingRunnerSet.Labels, map[string]string{ LabelKeyKubernetesPartOf: labelValueKubernetesPartOf, LabelKeyKubernetesComponent: "runner-set", LabelKeyKubernetesVersion: autoscalingRunnerSet.Labels[LabelKeyKubernetesVersion], @@ -515,7 +518,6 @@ func (b *resourceBuilder) newEphemeralRunnerSet(autoscalingRunnerSet *v1alpha1.A } newAnnotations := map[string]string{ - AnnotationKeyGitHubRunnerGroupName: autoscalingRunnerSet.Annotations[AnnotationKeyGitHubRunnerGroupName], AnnotationKeyGitHubRunnerScaleSetName: autoscalingRunnerSet.Annotations[AnnotationKeyGitHubRunnerScaleSetName], annotationKeyRunnerSpecHash: runnerSpecHash, @@ -545,7 +547,7 @@ func (b *resourceBuilder) newEphemeralRunnerSet(autoscalingRunnerSet *v1alpha1.A return newEphemeralRunnerSet, nil } -func (b *resourceBuilder) newEphemeralRunner(ephemeralRunnerSet *v1alpha1.EphemeralRunnerSet) *v1alpha1.EphemeralRunner { +func (b *ResourceBuilder) newEphemeralRunner(ephemeralRunnerSet *v1alpha1.EphemeralRunnerSet) *v1alpha1.EphemeralRunner { labels := make(map[string]string) for k, v := range ephemeralRunnerSet.Labels { if k == LabelKeyKubernetesComponent { @@ -572,7 +574,7 @@ func (b *resourceBuilder) newEphemeralRunner(ephemeralRunnerSet *v1alpha1.Epheme } } -func (b *resourceBuilder) newEphemeralRunnerPod(ctx context.Context, runner *v1alpha1.EphemeralRunner, secret *corev1.Secret, envs ...corev1.EnvVar) *corev1.Pod { +func (b *ResourceBuilder) newEphemeralRunnerPod(ctx context.Context, runner *v1alpha1.EphemeralRunner, secret *corev1.Secret, envs ...corev1.EnvVar) *corev1.Pod { var newPod corev1.Pod labels := map[string]string{} @@ -640,7 +642,7 @@ func (b *resourceBuilder) newEphemeralRunnerPod(ctx context.Context, runner *v1a return &newPod } -func (b *resourceBuilder) newEphemeralRunnerJitSecret(ephemeralRunner *v1alpha1.EphemeralRunner) *corev1.Secret { +func (b *ResourceBuilder) newEphemeralRunnerJitSecret(ephemeralRunner *v1alpha1.EphemeralRunner) *corev1.Secret { return &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: ephemeralRunner.Name, @@ -748,14 +750,26 @@ func trimLabelValue(val string) string { return val } -func mergeLabels(base, overwrite map[string]string) map[string]string { - mergedLabels := map[string]string{} +func (b *ResourceBuilder) mergeLabels(base, overwrite map[string]string) map[string]string { + mergedLabels := make(map[string]string, len(base)) +base: for k, v := range base { + for _, prefix := range b.ExcludeLabelPropagationPrefixes { + if strings.HasPrefix(k, prefix) { + continue base + } + } mergedLabels[k] = v } +overwrite: for k, v := range overwrite { + for _, prefix := range b.ExcludeLabelPropagationPrefixes { + if strings.HasPrefix(k, prefix) { + continue overwrite + } + } mergedLabels[k] = v } diff --git a/controllers/actions.github.com/resourcebuilder_test.go b/controllers/actions.github.com/resourcebuilder_test.go index 52ab19b4..b914f02d 100644 --- a/controllers/actions.github.com/resourcebuilder_test.go +++ b/controllers/actions.github.com/resourcebuilder_test.go @@ -19,9 +19,13 @@ func TestLabelPropagation(t *testing.T) { Name: "test-scale-set", Namespace: "test-ns", Labels: map[string]string{ - LabelKeyKubernetesPartOf: labelValueKubernetesPartOf, - LabelKeyKubernetesVersion: "0.2.0", - "arbitrary-label": "random-value", + LabelKeyKubernetesPartOf: labelValueKubernetesPartOf, + LabelKeyKubernetesVersion: "0.2.0", + "arbitrary-label": "random-value", + "example.com/label": "example-value", + "example.com/example": "example-value", + "directly.excluded.org/label": "excluded-value", + "directly.excluded.org/arbitrary": "not-excluded-value", }, Annotations: map[string]string{ runnerScaleSetIdAnnotationKey: "1", @@ -34,7 +38,12 @@ func TestLabelPropagation(t *testing.T) { }, } - var b resourceBuilder + b := ResourceBuilder{ + ExcludeLabelPropagationPrefixes: []string{ + "example.com/", + "directly.excluded.org/label", + }, + } ephemeralRunnerSet, err := b.newEphemeralRunnerSet(&autoscalingRunnerSet) require.NoError(t, err) assert.Equal(t, labelValueKubernetesPartOf, ephemeralRunnerSet.Labels[LabelKeyKubernetesPartOf]) @@ -63,6 +72,11 @@ func TestLabelPropagation(t *testing.T) { assert.Equal(t, "repo", listener.Labels[LabelKeyGitHubRepository]) assert.Equal(t, autoscalingRunnerSet.Labels["arbitrary-label"], listener.Labels["arbitrary-label"]) + assert.NotContains(t, listener.Labels, "example.com/label") + assert.NotContains(t, listener.Labels, "example.com/example") + assert.NotContains(t, listener.Labels, "directly.excluded.org/label") + assert.Equal(t, "not-excluded-value", listener.Labels["directly.excluded.org/arbitrary"]) + listenerServiceAccount := &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ Name: "test", @@ -128,7 +142,7 @@ func TestGitHubURLTrimLabelValues(t *testing.T) { GitHubConfigUrl: fmt.Sprintf("https://github.com/%s/%s", organization, repository), } - var b resourceBuilder + var b ResourceBuilder ephemeralRunnerSet, err := b.newEphemeralRunnerSet(autoscalingRunnerSet) require.NoError(t, err) assert.Len(t, ephemeralRunnerSet.Labels[LabelKeyGitHubEnterprise], 0) @@ -152,7 +166,7 @@ func TestGitHubURLTrimLabelValues(t *testing.T) { GitHubConfigUrl: fmt.Sprintf("https://github.com/enterprises/%s", enterprise), } - var b resourceBuilder + var b ResourceBuilder ephemeralRunnerSet, err := b.newEphemeralRunnerSet(autoscalingRunnerSet) require.NoError(t, err) assert.Len(t, ephemeralRunnerSet.Labels[LabelKeyGitHubEnterprise], 63) diff --git a/main.go b/main.go index 3392377c..d7edea6c 100644 --- a/main.go +++ b/main.go @@ -94,10 +94,11 @@ func main() { runnerImagePullSecrets stringSlice runnerPodDefaults actionssummerwindnet.RunnerPodDefaults - namespace string - logLevel string - logFormat string - watchSingleNamespace string + namespace string + logLevel string + logFormat string + watchSingleNamespace string + excludeLabelPropagationPrefixes stringSlice autoScalerImagePullSecrets stringSlice @@ -138,6 +139,7 @@ func main() { flag.Var(&commonRunnerLabels, "common-runner-labels", "Runner labels in the K1=V1,K2=V2,... format that are inherited all the runners created by the controller. See https://github.com/actions/actions-runner-controller/issues/321 for more information") flag.StringVar(&namespace, "watch-namespace", "", "The namespace to watch for custom resources. Set to empty for letting it watch for all namespaces.") flag.StringVar(&watchSingleNamespace, "watch-single-namespace", "", "Restrict to watch for custom resources in a single namespace.") + flag.Var(&excludeLabelPropagationPrefixes, "exclude-label-propagation-prefix", "The list of prefixes that should be excluded from label propagation") flag.StringVar(&logLevel, "log-level", logging.LogLevelDebug, `The verbosity of the logging. Valid values are "debug", "info", "warn", "error". Defaults to "debug".`) flag.StringVar(&logFormat, "log-format", "text", `The log format. Valid options are "text" and "json". Defaults to "text"`) flag.BoolVar(&autoScalingRunnerSetOnly, "auto-scaling-runner-set-only", false, "Make controller only reconcile AutoRunnerScaleSet object.") @@ -258,6 +260,10 @@ func main() { log.WithName("actions-clients"), ) + rb := actionsgithubcom.ResourceBuilder{ + ExcludeLabelPropagationPrefixes: excludeLabelPropagationPrefixes, + } + if err = (&actionsgithubcom.AutoscalingRunnerSetReconciler{ Client: mgr.GetClient(), Log: log.WithName("AutoscalingRunnerSet").WithValues("version", build.Version), @@ -267,27 +273,30 @@ func main() { ActionsClient: actionsMultiClient, UpdateStrategy: actionsgithubcom.UpdateStrategy(updateStrategy), DefaultRunnerScaleSetListenerImagePullSecrets: autoScalerImagePullSecrets, + ResourceBuilder: rb, }).SetupWithManager(mgr); err != nil { log.Error(err, "unable to create controller", "controller", "AutoscalingRunnerSet") os.Exit(1) } if err = (&actionsgithubcom.EphemeralRunnerReconciler{ - Client: mgr.GetClient(), - Log: log.WithName("EphemeralRunner").WithValues("version", build.Version), - Scheme: mgr.GetScheme(), - ActionsClient: actionsMultiClient, + Client: mgr.GetClient(), + Log: log.WithName("EphemeralRunner").WithValues("version", build.Version), + Scheme: mgr.GetScheme(), + ActionsClient: actionsMultiClient, + ResourceBuilder: rb, }).SetupWithManager(mgr); err != nil { log.Error(err, "unable to create controller", "controller", "EphemeralRunner") os.Exit(1) } if err = (&actionsgithubcom.EphemeralRunnerSetReconciler{ - Client: mgr.GetClient(), - Log: log.WithName("EphemeralRunnerSet").WithValues("version", build.Version), - Scheme: mgr.GetScheme(), - ActionsClient: actionsMultiClient, - PublishMetrics: metricsAddr != "0", + Client: mgr.GetClient(), + Log: log.WithName("EphemeralRunnerSet").WithValues("version", build.Version), + Scheme: mgr.GetScheme(), + ActionsClient: actionsMultiClient, + PublishMetrics: metricsAddr != "0", + ResourceBuilder: rb, }).SetupWithManager(mgr); err != nil { log.Error(err, "unable to create controller", "controller", "EphemeralRunnerSet") os.Exit(1) @@ -299,6 +308,7 @@ func main() { Scheme: mgr.GetScheme(), ListenerMetricsAddr: listenerMetricsAddr, ListenerMetricsEndpoint: listenerMetricsEndpoint, + ResourceBuilder: rb, }).SetupWithManager(mgr); err != nil { log.Error(err, "unable to create controller", "controller", "AutoscalingListener") os.Exit(1)