diff --git a/.github/workflows/e2e-test-linux-vm.yaml b/.github/workflows/e2e-test-linux-vm.yaml index 9a64405c..a054149b 100644 --- a/.github/workflows/e2e-test-linux-vm.yaml +++ b/.github/workflows/e2e-test-linux-vm.yaml @@ -79,20 +79,20 @@ jobs: echo "ARC_NAME=$ARC_NAME" >> $GITHUB_OUTPUT count=0 while true; do - POD_NAME=$(kubectl get pods -n arc-systems -l auto-scaling-runner-set-name=$ARC_NAME -o name) + POD_NAME=$(kubectl get pods -n arc-systems -l actions.github.com/scale-set-name=$ARC_NAME -o name) if [ -n "$POD_NAME" ]; then echo "Pod found: $POD_NAME" break fi if [ "$count" -ge 10 ]; then - echo "Timeout waiting for listener pod with label auto-scaling-runner-set-name=$ARC_NAME" + echo "Timeout waiting for listener pod with label actions.github.com/scale-set-name=$ARC_NAME" exit 1 fi sleep 1 done - kubectl wait --timeout=30s --for=condition=ready pod -n arc-systems -l auto-scaling-runner-set-name=$ARC_NAME + kubectl wait --timeout=30s --for=condition=ready pod -n arc-systems -l actions.github.com/scale-set-name=$ARC_NAME kubectl get pod -n arc-systems - + - name: Test ARC E2E uses: ./.github/actions/execute-assert-arc-e2e timeout-minutes: 10 @@ -167,18 +167,18 @@ jobs: echo "ARC_NAME=$ARC_NAME" >> $GITHUB_OUTPUT count=0 while true; do - POD_NAME=$(kubectl get pods -n arc-systems -l auto-scaling-runner-set-name=$ARC_NAME -o name) + POD_NAME=$(kubectl get pods -n arc-systems -l actions.github.com/scale-set-name=$ARC_NAME -o name) if [ -n "$POD_NAME" ]; then echo "Pod found: $POD_NAME" break fi if [ "$count" -ge 10 ]; then - echo "Timeout waiting for listener pod with label auto-scaling-runner-set-name=$ARC_NAME" + echo "Timeout waiting for listener pod with label actions.github.com/scale-set-name=$ARC_NAME" exit 1 fi sleep 1 done - kubectl wait --timeout=30s --for=condition=ready pod -n arc-systems -l auto-scaling-runner-set-name=$ARC_NAME + kubectl wait --timeout=30s --for=condition=ready pod -n arc-systems -l actions.github.com/scale-set-name=$ARC_NAME kubectl get pod -n arc-systems - name: Test ARC E2E @@ -254,18 +254,18 @@ jobs: echo "ARC_NAME=$ARC_NAME" >> $GITHUB_OUTPUT count=0 while true; do - POD_NAME=$(kubectl get pods -n arc-systems -l auto-scaling-runner-set-name=$ARC_NAME -o name) + POD_NAME=$(kubectl get pods -n arc-systems -l actions.github.com/scale-set-name=$ARC_NAME -o name) if [ -n "$POD_NAME" ]; then echo "Pod found: $POD_NAME" break fi if [ "$count" -ge 10 ]; then - echo "Timeout waiting for listener pod with label auto-scaling-runner-set-name=$ARC_NAME" + echo "Timeout waiting for listener pod with label actions.github.com/scale-set-name=$ARC_NAME" exit 1 fi sleep 1 done - kubectl wait --timeout=30s --for=condition=ready pod -n arc-systems -l auto-scaling-runner-set-name=$ARC_NAME + kubectl wait --timeout=30s --for=condition=ready pod -n arc-systems -l actions.github.com/scale-set-name=$ARC_NAME kubectl get pod -n arc-systems - name: Test ARC E2E @@ -350,18 +350,18 @@ jobs: echo "ARC_NAME=$ARC_NAME" >> $GITHUB_OUTPUT count=0 while true; do - POD_NAME=$(kubectl get pods -n arc-systems -l auto-scaling-runner-set-name=$ARC_NAME -o name) + POD_NAME=$(kubectl get pods -n arc-systems -l actions.github.com/scale-set-name=$ARC_NAME -o name) if [ -n "$POD_NAME" ]; then echo "Pod found: $POD_NAME" break fi if [ "$count" -ge 10 ]; then - echo "Timeout waiting for listener pod with label auto-scaling-runner-set-name=$ARC_NAME" + echo "Timeout waiting for listener pod with label actions.github.com/scale-set-name=$ARC_NAME" exit 1 fi sleep 1 done - kubectl wait --timeout=30s --for=condition=ready pod -n arc-systems -l auto-scaling-runner-set-name=$ARC_NAME + kubectl wait --timeout=30s --for=condition=ready pod -n arc-systems -l actions.github.com/scale-set-name=$ARC_NAME kubectl get pod -n arc-systems - name: Test ARC E2E @@ -448,18 +448,18 @@ jobs: echo "ARC_NAME=$ARC_NAME" >> $GITHUB_OUTPUT count=0 while true; do - POD_NAME=$(kubectl get pods -n arc-systems -l auto-scaling-runner-set-name=$ARC_NAME -o name) + POD_NAME=$(kubectl get pods -n arc-systems -l actions.github.com/scale-set-name=$ARC_NAME -o name) if [ -n "$POD_NAME" ]; then echo "Pod found: $POD_NAME" break fi if [ "$count" -ge 10 ]; then - echo "Timeout waiting for listener pod with label auto-scaling-runner-set-name=$ARC_NAME" + echo "Timeout waiting for listener pod with label actions.github.com/scale-set-name=$ARC_NAME" exit 1 fi sleep 1 done - kubectl wait --timeout=30s --for=condition=ready pod -n arc-systems -l auto-scaling-runner-set-name=$ARC_NAME + kubectl wait --timeout=30s --for=condition=ready pod -n arc-systems -l actions.github.com/scale-set-name=$ARC_NAME kubectl get pod -n arc-systems - name: Test ARC E2E @@ -540,18 +540,18 @@ jobs: echo "ARC_NAME=$ARC_NAME" >> $GITHUB_OUTPUT count=0 while true; do - POD_NAME=$(kubectl get pods -n arc-systems -l auto-scaling-runner-set-name=$ARC_NAME -o name) + POD_NAME=$(kubectl get pods -n arc-systems -l actions.github.com/scale-set-name=$ARC_NAME -o name) if [ -n "$POD_NAME" ]; then echo "Pod found: $POD_NAME" break fi if [ "$count" -ge 10 ]; then - echo "Timeout waiting for listener pod with label auto-scaling-runner-set-name=$ARC_NAME" + echo "Timeout waiting for listener pod with label actions.github.com/scale-set-name=$ARC_NAME" exit 1 fi sleep 1 done - kubectl wait --timeout=30s --for=condition=ready pod -n arc-systems -l auto-scaling-runner-set-name=$ARC_NAME + kubectl wait --timeout=30s --for=condition=ready pod -n arc-systems -l actions.github.com/scale-set-name=$ARC_NAME kubectl get pod -n arc-systems - name: Test ARC E2E diff --git a/charts/gha-runner-scale-set/templates/_helpers.tpl b/charts/gha-runner-scale-set/templates/_helpers.tpl index babaa4aa..45e77945 100644 --- a/charts/gha-runner-scale-set/templates/_helpers.tpl +++ b/charts/gha-runner-scale-set/templates/_helpers.tpl @@ -40,6 +40,7 @@ helm.sh/chart: {{ include "gha-runner-scale-set.chart" . }} app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} {{- end }} app.kubernetes.io/managed-by: {{ .Release.Service }} +app.kubernetes.io/part-of: gha-runner-scale-set {{- end }} {{/* diff --git a/charts/gha-runner-scale-set/templates/autoscalingrunnerset.yaml b/charts/gha-runner-scale-set/templates/autoscalingrunnerset.yaml index 526ad120..455a7d0b 100644 --- a/charts/gha-runner-scale-set/templates/autoscalingrunnerset.yaml +++ b/charts/gha-runner-scale-set/templates/autoscalingrunnerset.yaml @@ -10,6 +10,7 @@ metadata: name: {{ .Release.Name }} namespace: {{ .Release.Namespace }} labels: + app.kubernetes.io/component: "autoscaling-runner-set" {{- include "gha-runner-scale-set.labels" . | nindent 4 }} spec: githubConfigUrl: {{ required ".Values.githubConfigUrl is required" (trimSuffix "/" .Values.githubConfigUrl) }} diff --git a/charts/gha-runner-scale-set/tests/template_test.go b/charts/gha-runner-scale-set/tests/template_test.go index c5960ad3..9c3692ee 100644 --- a/charts/gha-runner-scale-set/tests/template_test.go +++ b/charts/gha-runner-scale-set/tests/template_test.go @@ -311,6 +311,10 @@ func TestTemplateRenderedAutoScalingRunnerSet(t *testing.T) { assert.Equal(t, "gha-runner-scale-set", ars.Labels["app.kubernetes.io/name"]) assert.Equal(t, "test-runners", ars.Labels["app.kubernetes.io/instance"]) + assert.Equal(t, "gha-runner-scale-set", ars.Labels["app.kubernetes.io/part-of"]) + assert.Equal(t, "autoscaling-runner-set", ars.Labels["app.kubernetes.io/component"]) + assert.NotEmpty(t, ars.Labels["app.kubernetes.io/version"]) + assert.Equal(t, "https://github.com/actions", ars.Spec.GitHubConfigUrl) assert.Equal(t, "test-runners-gha-runner-scale-set-github-secret", ars.Spec.GitHubConfigSecret) diff --git a/controllers/actions.github.com/autoscalinglistener_controller.go b/controllers/actions.github.com/autoscalinglistener_controller.go index dec92334..5509946c 100644 --- a/controllers/actions.github.com/autoscalinglistener_controller.go +++ b/controllers/actions.github.com/autoscalinglistener_controller.go @@ -523,8 +523,8 @@ func (r *AutoscalingListenerReconciler) createProxySecret(ctx context.Context, a Name: proxyListenerSecretName(autoscalingListener), Namespace: autoscalingListener.Namespace, Labels: map[string]string{ - "auto-scaling-runner-set-namespace": autoscalingListener.Spec.AutoscalingRunnerSetNamespace, - "auto-scaling-runner-set-name": autoscalingListener.Spec.AutoscalingRunnerSetName, + LabelKeyGitHubScaleSetNamespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace, + LabelKeyGitHubScaleSetName: autoscalingListener.Spec.AutoscalingRunnerSetName, }, }, Data: data, diff --git a/controllers/actions.github.com/autoscalingrunnerset_controller.go b/controllers/actions.github.com/autoscalingrunnerset_controller.go index b833e57d..43c13823 100644 --- a/controllers/actions.github.com/autoscalingrunnerset_controller.go +++ b/controllers/actions.github.com/autoscalingrunnerset_controller.go @@ -45,9 +45,8 @@ const ( autoscalingRunnerSetOwnerKey = ".metadata.controller" LabelKeyRunnerSpecHash = "runner-spec-hash" autoscalingRunnerSetFinalizerName = "autoscalingrunnerset.actions.github.com/finalizer" - runnerScaleSetIdKey = "runner-scale-set-id" - runnerScaleSetNameKey = "runner-scale-set-name" - runnerScaleSetRunnerGroupNameKey = "runner-scale-set-runner-group-name" + runnerScaleSetIdAnnotationKey = "runner-scale-set-id" + runnerScaleSetNameAnnotationKey = "runner-scale-set-name" ) // AutoscalingRunnerSetReconciler reconciles a AutoscalingRunnerSet object @@ -140,7 +139,7 @@ func (r *AutoscalingRunnerSetReconciler) Reconcile(ctx context.Context, req ctrl return ctrl.Result{}, nil } - scaleSetIdRaw, ok := autoscalingRunnerSet.Annotations[runnerScaleSetIdKey] + scaleSetIdRaw, ok := autoscalingRunnerSet.Annotations[runnerScaleSetIdAnnotationKey] if !ok { // Need to create a new runner scale set on Actions service log.Info("Runner scale set id annotation does not exist. Creating a new runner scale set.") @@ -154,14 +153,14 @@ func (r *AutoscalingRunnerSetReconciler) Reconcile(ctx context.Context, req ctrl } // Make sure the runner group of the scale set is up to date - currentRunnerGroupName, ok := autoscalingRunnerSet.Annotations[runnerScaleSetRunnerGroupNameKey] + currentRunnerGroupName, ok := autoscalingRunnerSet.Annotations[AnnotationKeyGitHubRunnerGroupName] if !ok || (len(autoscalingRunnerSet.Spec.RunnerGroup) > 0 && !strings.EqualFold(currentRunnerGroupName, autoscalingRunnerSet.Spec.RunnerGroup)) { log.Info("AutoScalingRunnerSet runner group changed. Updating the runner scale set.") return r.updateRunnerScaleSetRunnerGroup(ctx, autoscalingRunnerSet, log) } // Make sure the runner scale set name is up to date - currentRunnerScaleSetName, ok := autoscalingRunnerSet.Annotations[runnerScaleSetNameKey] + currentRunnerScaleSetName, ok := autoscalingRunnerSet.Annotations[runnerScaleSetNameAnnotationKey] if !ok || (len(autoscalingRunnerSet.Spec.RunnerScaleSetName) > 0 && !strings.EqualFold(currentRunnerScaleSetName, autoscalingRunnerSet.Spec.RunnerScaleSetName)) { log.Info("AutoScalingRunnerSet runner scale set name changed. Updating the runner scale set.") return r.updateRunnerScaleSetName(ctx, autoscalingRunnerSet, log) @@ -365,12 +364,18 @@ func (r *AutoscalingRunnerSetReconciler) createRunnerScaleSet(ctx context.Contex if autoscalingRunnerSet.Annotations == nil { autoscalingRunnerSet.Annotations = map[string]string{} } + if autoscalingRunnerSet.Labels == nil { + autoscalingRunnerSet.Labels = map[string]string{} + } - logger.Info("Adding runner scale set ID, name and runner group name as an annotation") + logger.Info("Adding runner scale set ID, name and runner group name as an annotation and url labels") if err = patch(ctx, r.Client, autoscalingRunnerSet, func(obj *v1alpha1.AutoscalingRunnerSet) { - obj.Annotations[runnerScaleSetNameKey] = runnerScaleSet.Name - obj.Annotations[runnerScaleSetIdKey] = strconv.Itoa(runnerScaleSet.Id) - obj.Annotations[runnerScaleSetRunnerGroupNameKey] = runnerScaleSet.RunnerGroupName + obj.Annotations[runnerScaleSetNameAnnotationKey] = runnerScaleSet.Name + obj.Annotations[runnerScaleSetIdAnnotationKey] = strconv.Itoa(runnerScaleSet.Id) + obj.Annotations[AnnotationKeyGitHubRunnerGroupName] = runnerScaleSet.RunnerGroupName + if err := applyGitHubURLLabels(obj.Spec.GitHubConfigUrl, obj.Labels); err != nil { // should never happen + logger.Error(err, "Failed to apply GitHub URL labels") + } }); err != nil { logger.Error(err, "Failed to add runner scale set ID, name and runner group name as an annotation") return ctrl.Result{}, err @@ -384,7 +389,7 @@ func (r *AutoscalingRunnerSetReconciler) createRunnerScaleSet(ctx context.Contex } func (r *AutoscalingRunnerSetReconciler) updateRunnerScaleSetRunnerGroup(ctx context.Context, autoscalingRunnerSet *v1alpha1.AutoscalingRunnerSet, logger logr.Logger) (ctrl.Result, error) { - runnerScaleSetId, err := strconv.Atoi(autoscalingRunnerSet.Annotations[runnerScaleSetIdKey]) + runnerScaleSetId, err := strconv.Atoi(autoscalingRunnerSet.Annotations[runnerScaleSetIdAnnotationKey]) if err != nil { logger.Error(err, "Failed to parse runner scale set ID") return ctrl.Result{}, err @@ -415,7 +420,7 @@ func (r *AutoscalingRunnerSetReconciler) updateRunnerScaleSetRunnerGroup(ctx con logger.Info("Updating runner scale set runner group name as an annotation") if err := patch(ctx, r.Client, autoscalingRunnerSet, func(obj *v1alpha1.AutoscalingRunnerSet) { - obj.Annotations[runnerScaleSetRunnerGroupNameKey] = updatedRunnerScaleSet.RunnerGroupName + obj.Annotations[AnnotationKeyGitHubRunnerGroupName] = updatedRunnerScaleSet.RunnerGroupName }); err != nil { logger.Error(err, "Failed to update runner group name annotation") return ctrl.Result{}, err @@ -426,7 +431,7 @@ func (r *AutoscalingRunnerSetReconciler) updateRunnerScaleSetRunnerGroup(ctx con } func (r *AutoscalingRunnerSetReconciler) updateRunnerScaleSetName(ctx context.Context, autoscalingRunnerSet *v1alpha1.AutoscalingRunnerSet, logger logr.Logger) (ctrl.Result, error) { - runnerScaleSetId, err := strconv.Atoi(autoscalingRunnerSet.Annotations[runnerScaleSetIdKey]) + runnerScaleSetId, err := strconv.Atoi(autoscalingRunnerSet.Annotations[runnerScaleSetIdAnnotationKey]) if err != nil { logger.Error(err, "Failed to parse runner scale set ID") return ctrl.Result{}, err @@ -451,7 +456,7 @@ func (r *AutoscalingRunnerSetReconciler) updateRunnerScaleSetName(ctx context.Co logger.Info("Updating runner scale set name as an annotation") if err := patch(ctx, r.Client, autoscalingRunnerSet, func(obj *v1alpha1.AutoscalingRunnerSet) { - obj.Annotations[runnerScaleSetNameKey] = updatedRunnerScaleSet.Name + obj.Annotations[runnerScaleSetNameAnnotationKey] = updatedRunnerScaleSet.Name }); err != nil { logger.Error(err, "Failed to update runner scale set name annotation") return ctrl.Result{}, err @@ -463,7 +468,7 @@ func (r *AutoscalingRunnerSetReconciler) updateRunnerScaleSetName(ctx context.Co func (r *AutoscalingRunnerSetReconciler) deleteRunnerScaleSet(ctx context.Context, autoscalingRunnerSet *v1alpha1.AutoscalingRunnerSet, logger logr.Logger) error { logger.Info("Deleting the runner scale set from Actions service") - runnerScaleSetId, err := strconv.Atoi(autoscalingRunnerSet.Annotations[runnerScaleSetIdKey]) + runnerScaleSetId, err := strconv.Atoi(autoscalingRunnerSet.Annotations[runnerScaleSetIdAnnotationKey]) if err != nil { // If the annotation is not set correctly, or if it does not exist, we are going to get stuck in a loop trying to parse the scale set id. // If the configuration is invalid (secret does not exist for example), we never get to the point to create runner set. But then, manual cleanup diff --git a/controllers/actions.github.com/autoscalingrunnerset_controller_test.go b/controllers/actions.github.com/autoscalingrunnerset_controller_test.go index 2a5fd780..2fd0e61b 100644 --- a/controllers/actions.github.com/autoscalingrunnerset_controller_test.go +++ b/controllers/actions.github.com/autoscalingrunnerset_controller_test.go @@ -117,19 +117,39 @@ var _ = Describe("Test AutoScalingRunnerSet controller", func() { return "", err } - if _, ok := created.Annotations[runnerScaleSetIdKey]; !ok { + if _, ok := created.Annotations[runnerScaleSetIdAnnotationKey]; !ok { return "", nil } - if _, ok := created.Annotations[runnerScaleSetRunnerGroupNameKey]; !ok { + if _, ok := created.Annotations[AnnotationKeyGitHubRunnerGroupName]; !ok { return "", nil } - return fmt.Sprintf("%s_%s", created.Annotations[runnerScaleSetIdKey], created.Annotations[runnerScaleSetRunnerGroupNameKey]), nil + return fmt.Sprintf("%s_%s", created.Annotations[runnerScaleSetIdAnnotationKey], created.Annotations[AnnotationKeyGitHubRunnerGroupName]), nil }, autoscalingRunnerSetTestTimeout, autoscalingRunnerSetTestInterval).Should(BeEquivalentTo("1_testgroup"), "RunnerScaleSet should be created/fetched and update the AutoScalingRunnerSet's annotation") + Eventually( + func() (string, error) { + err := k8sClient.Get(ctx, client.ObjectKey{Name: autoscalingRunnerSet.Name, Namespace: autoscalingRunnerSet.Namespace}, created) + if err != nil { + return "", err + } + + if _, ok := created.Labels[LabelKeyGitHubOrganization]; !ok { + return "", nil + } + + if _, ok := created.Labels[LabelKeyGitHubRepository]; !ok { + return "", nil + } + + return fmt.Sprintf("%s/%s", created.Labels[LabelKeyGitHubOrganization], created.Labels[LabelKeyGitHubRepository]), nil + }, + autoscalingRunnerSetTestTimeout, + autoscalingRunnerSetTestInterval).Should(BeEquivalentTo("owner/repo"), "RunnerScaleSet should be created/fetched and update the AutoScalingRunnerSet's label") + // Check if ephemeral runner set is created Eventually( func() (int, error) { @@ -351,18 +371,18 @@ var _ = Describe("Test AutoScalingRunnerSet controller", func() { return "", err } - if _, ok := updated.Annotations[runnerScaleSetRunnerGroupNameKey]; !ok { + if _, ok := updated.Annotations[AnnotationKeyGitHubRunnerGroupName]; !ok { return "", nil } - return updated.Annotations[runnerScaleSetRunnerGroupNameKey], nil + return updated.Annotations[AnnotationKeyGitHubRunnerGroupName], nil }, autoscalingRunnerSetTestTimeout, autoscalingRunnerSetTestInterval).Should(BeEquivalentTo("testgroup2"), "AutoScalingRunnerSet should have the new runner group in its annotation") // delete the annotation and it should be re-added patched = autoscalingRunnerSet.DeepCopy() - delete(patched.Annotations, runnerScaleSetRunnerGroupNameKey) + delete(patched.Annotations, AnnotationKeyGitHubRunnerGroupName) err = k8sClient.Patch(ctx, patched, client.MergeFrom(autoscalingRunnerSet)) Expect(err).NotTo(HaveOccurred(), "failed to patch AutoScalingRunnerSet") @@ -374,11 +394,11 @@ var _ = Describe("Test AutoScalingRunnerSet controller", func() { return "", err } - if _, ok := updated.Annotations[runnerScaleSetRunnerGroupNameKey]; !ok { + if _, ok := updated.Annotations[AnnotationKeyGitHubRunnerGroupName]; !ok { return "", nil } - return updated.Annotations[runnerScaleSetRunnerGroupNameKey], nil + return updated.Annotations[AnnotationKeyGitHubRunnerGroupName], nil }, autoscalingRunnerSetTestTimeout, autoscalingRunnerSetTestInterval, @@ -539,7 +559,7 @@ var _ = Describe("Test AutoScalingController updates", func() { return "", err } - if val, ok := ars.Annotations[runnerScaleSetNameKey]; ok { + if val, ok := ars.Annotations[runnerScaleSetNameAnnotationKey]; ok { return val, nil } @@ -562,7 +582,7 @@ var _ = Describe("Test AutoScalingController updates", func() { return "", err } - if val, ok := ars.Annotations[runnerScaleSetNameKey]; ok { + if val, ok := ars.Annotations[runnerScaleSetNameAnnotationKey]; ok { return val, nil } diff --git a/controllers/actions.github.com/ephemeralrunnerset_controller.go b/controllers/actions.github.com/ephemeralrunnerset_controller.go index 27a8a227..6a90ec78 100644 --- a/controllers/actions.github.com/ephemeralrunnerset_controller.go +++ b/controllers/actions.github.com/ephemeralrunnerset_controller.go @@ -356,10 +356,9 @@ func (r *EphemeralRunnerSetReconciler) createProxySecret(ctx context.Context, ep ObjectMeta: metav1.ObjectMeta{ Name: proxyEphemeralRunnerSetSecretName(ephemeralRunnerSet), Namespace: ephemeralRunnerSet.Namespace, - Labels: map[string]string{ - // TODO: figure out autoScalingRunnerSet name and set it as a label for this secret - // "auto-scaling-runner-set-namespace": ephemeralRunnerSet.Namespace, - // "auto-scaling-runner-set-name": ephemeralRunnerSet.Name, + Labels: map[string]string{ + LabelKeyGitHubScaleSetName: ephemeralRunnerSet.Labels[LabelKeyGitHubScaleSetName], + LabelKeyGitHubScaleSetNamespace: ephemeralRunnerSet.Labels[LabelKeyGitHubScaleSetNamespace], }, }, Data: proxySecretData, diff --git a/controllers/actions.github.com/resourcebuilder.go b/controllers/actions.github.com/resourcebuilder.go index dd555289..4ca6abc6 100644 --- a/controllers/actions.github.com/resourcebuilder.go +++ b/controllers/actions.github.com/resourcebuilder.go @@ -8,6 +8,7 @@ import ( "github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1" "github.com/actions/actions-runner-controller/build" + "github.com/actions/actions-runner-controller/github/actions" "github.com/actions/actions-runner-controller/hash" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" @@ -19,12 +20,42 @@ const ( jitTokenKey = "jitToken" ) -// labels applied to resources +// Labels applied to resources const ( - LabelKeyAutoScaleRunnerSetName = "auto-scaling-runner-set-name" - LabelKeyAutoScaleRunnerSetNamespace = "auto-scaling-runner-set-namespace" + // Kubernetes labels + LabelKeyKubernetesPartOf = "app.kubernetes.io/part-of" + LabelKeyKubernetesComponent = "app.kubernetes.io/component" + LabelKeyKubernetesVersion = "app.kubernetes.io/version" + + // Github labels + LabelKeyGitHubScaleSetName = "actions.github.com/scale-set-name" + LabelKeyGitHubScaleSetNamespace = "actions.github.com/scale-set-namespace" + LabelKeyGitHubEnterprise = "actions.github.com/enterprise" + LabelKeyGitHubOrganization = "actions.github.com/organization" + LabelKeyGitHubRepository = "actions.github.com/repository" ) +const AnnotationKeyGitHubRunnerGroupName = "actions.github.com/runner-group-name" + +// Labels applied to listener roles +const ( + labelKeyListenerName = "auto-scaling-listener-name" + labelKeyListenerNamespace = "auto-scaling-listener-namespace" +) + +var commonLabelKeys = [...]string{ + LabelKeyKubernetesPartOf, + LabelKeyKubernetesComponent, + LabelKeyKubernetesVersion, + LabelKeyGitHubScaleSetName, + LabelKeyGitHubScaleSetNamespace, + LabelKeyGitHubEnterprise, + LabelKeyGitHubOrganization, + LabelKeyGitHubRepository, +} + +const labelValueKubernetesPartOf = "gha-runner-scale-set" + type resourceBuilder struct{} func (b *resourceBuilder) newScaleSetListenerPod(autoscalingListener *v1alpha1.AutoscalingListener, serviceAccount *corev1.ServiceAccount, secret *corev1.Secret, envs ...corev1.EnvVar) *corev1.Pod { @@ -129,6 +160,11 @@ func (b *resourceBuilder) newScaleSetListenerPod(autoscalingListener *v1alpha1.A RestartPolicy: corev1.RestartPolicyNever, } + labels := make(map[string]string, len(autoscalingListener.Labels)) + for key, val := range autoscalingListener.Labels { + labels[key] = val + } + newRunnerScaleSetListenerPod := &corev1.Pod{ TypeMeta: metav1.TypeMeta{ Kind: "Pod", @@ -137,10 +173,7 @@ func (b *resourceBuilder) newScaleSetListenerPod(autoscalingListener *v1alpha1.A ObjectMeta: metav1.ObjectMeta{ Name: autoscalingListener.Name, Namespace: autoscalingListener.Namespace, - Labels: map[string]string{ - LabelKeyAutoScaleRunnerSetNamespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace, - LabelKeyAutoScaleRunnerSetName: autoscalingListener.Spec.AutoscalingRunnerSetName, - }, + Labels: labels, }, Spec: podSpec, } @@ -149,14 +182,28 @@ func (b *resourceBuilder) newScaleSetListenerPod(autoscalingListener *v1alpha1.A } func (b *resourceBuilder) newEphemeralRunnerSet(autoscalingRunnerSet *v1alpha1.AutoscalingRunnerSet) (*v1alpha1.EphemeralRunnerSet, error) { - runnerScaleSetId, err := strconv.Atoi(autoscalingRunnerSet.Annotations[runnerScaleSetIdKey]) + runnerScaleSetId, err := strconv.Atoi(autoscalingRunnerSet.Annotations[runnerScaleSetIdAnnotationKey]) if err != nil { return nil, err } runnerSpecHash := autoscalingRunnerSet.RunnerSetSpecHash() - newLabels := map[string]string{} - newLabels[LabelKeyRunnerSpecHash] = runnerSpecHash + newLabels := map[string]string{ + LabelKeyRunnerSpecHash: runnerSpecHash, + LabelKeyKubernetesPartOf: labelValueKubernetesPartOf, + LabelKeyKubernetesComponent: "runner-set", + LabelKeyKubernetesVersion: autoscalingRunnerSet.Labels[LabelKeyKubernetesVersion], + LabelKeyGitHubScaleSetName: autoscalingRunnerSet.Name, + LabelKeyGitHubScaleSetNamespace: autoscalingRunnerSet.Namespace, + } + + if err := applyGitHubURLLabels(autoscalingRunnerSet.Spec.GitHubConfigUrl, newLabels); err != nil { + return nil, fmt.Errorf("failed to apply GitHub URL labels: %v", err) + } + + newAnnotations := map[string]string{ + AnnotationKeyGitHubRunnerGroupName: autoscalingRunnerSet.Annotations[AnnotationKeyGitHubRunnerGroupName], + } newEphemeralRunnerSet := &v1alpha1.EphemeralRunnerSet{ TypeMeta: metav1.TypeMeta{}, @@ -164,6 +211,7 @@ func (b *resourceBuilder) newEphemeralRunnerSet(autoscalingRunnerSet *v1alpha1.A GenerateName: autoscalingRunnerSet.ObjectMeta.Name + "-", Namespace: autoscalingRunnerSet.ObjectMeta.Namespace, Labels: newLabels, + Annotations: newAnnotations, }, Spec: v1alpha1.EphemeralRunnerSetSpec{ Replicas: 0, @@ -187,8 +235,8 @@ func (b *resourceBuilder) newScaleSetListenerServiceAccount(autoscalingListener Name: scaleSetListenerServiceAccountName(autoscalingListener), Namespace: autoscalingListener.Namespace, Labels: map[string]string{ - LabelKeyAutoScaleRunnerSetNamespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace, - LabelKeyAutoScaleRunnerSetName: autoscalingListener.Spec.AutoscalingRunnerSetName, + LabelKeyGitHubScaleSetNamespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace, + LabelKeyGitHubScaleSetName: autoscalingListener.Spec.AutoscalingRunnerSetName, }, }, } @@ -202,11 +250,11 @@ func (b *resourceBuilder) newScaleSetListenerRole(autoscalingListener *v1alpha1. Name: scaleSetListenerRoleName(autoscalingListener), Namespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace, Labels: map[string]string{ - LabelKeyAutoScaleRunnerSetNamespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace, - LabelKeyAutoScaleRunnerSetName: autoscalingListener.Spec.AutoscalingRunnerSetName, - "auto-scaling-listener-namespace": autoscalingListener.Namespace, - "auto-scaling-listener-name": autoscalingListener.Name, - "role-policy-rules-hash": rulesHash, + LabelKeyGitHubScaleSetNamespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace, + LabelKeyGitHubScaleSetName: autoscalingListener.Spec.AutoscalingRunnerSetName, + labelKeyListenerNamespace: autoscalingListener.Namespace, + labelKeyListenerName: autoscalingListener.Name, + "role-policy-rules-hash": rulesHash, }, }, Rules: rules, @@ -236,12 +284,12 @@ func (b *resourceBuilder) newScaleSetListenerRoleBinding(autoscalingListener *v1 Name: scaleSetListenerRoleName(autoscalingListener), Namespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace, Labels: map[string]string{ - LabelKeyAutoScaleRunnerSetNamespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace, - LabelKeyAutoScaleRunnerSetName: autoscalingListener.Spec.AutoscalingRunnerSetName, - "auto-scaling-listener-namespace": autoscalingListener.Namespace, - "auto-scaling-listener-name": autoscalingListener.Name, - "role-binding-role-ref-hash": roleRefHash, - "role-binding-subject-hash": subjectHash, + LabelKeyGitHubScaleSetNamespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace, + LabelKeyGitHubScaleSetName: autoscalingListener.Spec.AutoscalingRunnerSetName, + labelKeyListenerNamespace: autoscalingListener.Namespace, + labelKeyListenerName: autoscalingListener.Name, + "role-binding-role-ref-hash": roleRefHash, + "role-binding-subject-hash": subjectHash, }, }, RoleRef: roleRef, @@ -259,9 +307,9 @@ func (b *resourceBuilder) newScaleSetListenerSecretMirror(autoscalingListener *v Name: scaleSetListenerSecretMirrorName(autoscalingListener), Namespace: autoscalingListener.Namespace, Labels: map[string]string{ - LabelKeyAutoScaleRunnerSetNamespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace, - LabelKeyAutoScaleRunnerSetName: autoscalingListener.Spec.AutoscalingRunnerSetName, - "secret-data-hash": dataHash, + LabelKeyGitHubScaleSetNamespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace, + LabelKeyGitHubScaleSetName: autoscalingListener.Spec.AutoscalingRunnerSetName, + "secret-data-hash": dataHash, }, }, Data: secret.DeepCopy().Data, @@ -271,7 +319,7 @@ func (b *resourceBuilder) newScaleSetListenerSecretMirror(autoscalingListener *v } 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[runnerScaleSetIdKey]) + runnerScaleSetId, err := strconv.Atoi(autoscalingRunnerSet.Annotations[runnerScaleSetIdAnnotationKey]) if err != nil { return nil, err } @@ -285,14 +333,25 @@ func (b *resourceBuilder) newAutoScalingListener(autoscalingRunnerSet *v1alpha1. effectiveMinRunners = *autoscalingRunnerSet.Spec.MinRunners } + githubConfig, err := actions.ParseGitHubConfigFromURL(autoscalingRunnerSet.Spec.GitHubConfigUrl) + if err != nil { + return nil, fmt.Errorf("failed to parse github config from url: %v", err) + } + autoscalingListener := &v1alpha1.AutoscalingListener{ ObjectMeta: metav1.ObjectMeta{ Name: scaleSetListenerName(autoscalingRunnerSet), Namespace: namespace, Labels: map[string]string{ - LabelKeyAutoScaleRunnerSetNamespace: autoscalingRunnerSet.Namespace, - LabelKeyAutoScaleRunnerSetName: autoscalingRunnerSet.Name, - LabelKeyRunnerSpecHash: autoscalingRunnerSet.ListenerSpecHash(), + LabelKeyGitHubScaleSetNamespace: autoscalingRunnerSet.Namespace, + LabelKeyGitHubScaleSetName: autoscalingRunnerSet.Name, + LabelKeyKubernetesPartOf: labelValueKubernetesPartOf, + LabelKeyKubernetesComponent: "runner-scale-set-listener", + LabelKeyKubernetesVersion: autoscalingRunnerSet.Labels[LabelKeyKubernetesVersion], + LabelKeyGitHubEnterprise: githubConfig.Enterprise, + LabelKeyGitHubOrganization: githubConfig.Organization, + LabelKeyGitHubRepository: githubConfig.Repository, + LabelKeyRunnerSpecHash: autoscalingRunnerSet.ListenerSpecHash(), }, }, Spec: v1alpha1.AutoscalingListenerSpec{ @@ -315,11 +374,30 @@ func (b *resourceBuilder) newAutoScalingListener(autoscalingRunnerSet *v1alpha1. } func (b *resourceBuilder) newEphemeralRunner(ephemeralRunnerSet *v1alpha1.EphemeralRunnerSet) *v1alpha1.EphemeralRunner { + labels := make(map[string]string) + for _, key := range commonLabelKeys { + switch key { + case LabelKeyKubernetesComponent: + labels[key] = "runner" + default: + v, ok := ephemeralRunnerSet.Labels[key] + if !ok { + continue + } + labels[key] = v + } + } + annotations := make(map[string]string) + for key, val := range ephemeralRunnerSet.Annotations { + annotations[key] = val + } return &v1alpha1.EphemeralRunner{ TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{ GenerateName: ephemeralRunnerSet.Name + "-runner-", Namespace: ephemeralRunnerSet.Namespace, + Labels: labels, + Annotations: annotations, }, Spec: ephemeralRunnerSet.Spec.EphemeralRunnerSpec, } @@ -337,6 +415,7 @@ func (b *resourceBuilder) newEphemeralRunnerPod(ctx context.Context, runner *v1a for k, v := range runner.Spec.PodTemplateSpec.Labels { labels[k] = v } + labels["actions-ephemeral-runner"] = string(corev1.ConditionTrue) for k, v := range runner.ObjectMeta.Annotations { annotations[k] = v @@ -352,8 +431,6 @@ func (b *resourceBuilder) newEphemeralRunnerPod(ctx context.Context, runner *v1a runner.Status.RunnerJITConfig, ) - labels["actions-ephemeral-runner"] = string(corev1.ConditionTrue) - objectMeta := metav1.ObjectMeta{ Name: runner.ObjectMeta.Name, Namespace: runner.ObjectMeta.Namespace, @@ -469,3 +546,22 @@ func rulesForListenerRole(resourceNames []string) []rbacv1.PolicyRule { }, } } + +func applyGitHubURLLabels(url string, labels map[string]string) error { + githubConfig, err := actions.ParseGitHubConfigFromURL(url) + if err != nil { + return fmt.Errorf("failed to parse github config from url: %v", err) + } + + if len(githubConfig.Enterprise) > 0 { + labels[LabelKeyGitHubEnterprise] = githubConfig.Enterprise + } + if len(githubConfig.Organization) > 0 { + labels[LabelKeyGitHubOrganization] = githubConfig.Organization + } + if len(githubConfig.Repository) > 0 { + labels[LabelKeyGitHubRepository] = githubConfig.Repository + } + + return nil +} diff --git a/controllers/actions.github.com/resourcebuilder_test.go b/controllers/actions.github.com/resourcebuilder_test.go new file mode 100644 index 00000000..e41d7980 --- /dev/null +++ b/controllers/actions.github.com/resourcebuilder_test.go @@ -0,0 +1,93 @@ +package actionsgithubcom + +import ( + "context" + "testing" + + "github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestLabelPropagation(t *testing.T) { + autoscalingRunnerSet := v1alpha1.AutoscalingRunnerSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-scale-set", + Namespace: "test-ns", + Labels: map[string]string{ + LabelKeyKubernetesPartOf: labelValueKubernetesPartOf, + LabelKeyKubernetesVersion: "0.2.0", + }, + Annotations: map[string]string{ + runnerScaleSetIdAnnotationKey: "1", + AnnotationKeyGitHubRunnerGroupName: "test-group", + }, + }, + Spec: v1alpha1.AutoscalingRunnerSetSpec{ + GitHubConfigUrl: "https://github.com/org/repo", + }, + } + + var b resourceBuilder + ephemeralRunnerSet, err := b.newEphemeralRunnerSet(&autoscalingRunnerSet) + require.NoError(t, err) + assert.Equal(t, labelValueKubernetesPartOf, ephemeralRunnerSet.Labels[LabelKeyKubernetesPartOf]) + assert.Equal(t, "runner-set", ephemeralRunnerSet.Labels[LabelKeyKubernetesComponent]) + assert.Equal(t, autoscalingRunnerSet.Labels[LabelKeyKubernetesVersion], ephemeralRunnerSet.Labels[LabelKeyKubernetesVersion]) + assert.NotEmpty(t, ephemeralRunnerSet.Labels[LabelKeyRunnerSpecHash]) + assert.Equal(t, autoscalingRunnerSet.Name, ephemeralRunnerSet.Labels[LabelKeyGitHubScaleSetName]) + assert.Equal(t, autoscalingRunnerSet.Namespace, ephemeralRunnerSet.Labels[LabelKeyGitHubScaleSetNamespace]) + assert.Equal(t, "", ephemeralRunnerSet.Labels[LabelKeyGitHubEnterprise]) + assert.Equal(t, "org", ephemeralRunnerSet.Labels[LabelKeyGitHubOrganization]) + assert.Equal(t, "repo", ephemeralRunnerSet.Labels[LabelKeyGitHubRepository]) + assert.Equal(t, autoscalingRunnerSet.Annotations[AnnotationKeyGitHubRunnerGroupName], ephemeralRunnerSet.Annotations[AnnotationKeyGitHubRunnerGroupName]) + + listener, err := b.newAutoScalingListener(&autoscalingRunnerSet, ephemeralRunnerSet, autoscalingRunnerSet.Namespace, "test:latest", nil) + require.NoError(t, err) + assert.Equal(t, labelValueKubernetesPartOf, listener.Labels[LabelKeyKubernetesPartOf]) + assert.Equal(t, "runner-scale-set-listener", listener.Labels[LabelKeyKubernetesComponent]) + assert.Equal(t, autoscalingRunnerSet.Labels[LabelKeyKubernetesVersion], listener.Labels[LabelKeyKubernetesVersion]) + assert.NotEmpty(t, ephemeralRunnerSet.Labels[LabelKeyRunnerSpecHash]) + assert.Equal(t, autoscalingRunnerSet.Name, listener.Labels[LabelKeyGitHubScaleSetName]) + assert.Equal(t, autoscalingRunnerSet.Namespace, listener.Labels[LabelKeyGitHubScaleSetNamespace]) + assert.Equal(t, "", listener.Labels[LabelKeyGitHubEnterprise]) + assert.Equal(t, "org", listener.Labels[LabelKeyGitHubOrganization]) + assert.Equal(t, "repo", listener.Labels[LabelKeyGitHubRepository]) + + listenerServiceAccount := &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + } + listenerSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + } + listenerPod := b.newScaleSetListenerPod(listener, listenerServiceAccount, listenerSecret) + assert.Equal(t, listenerPod.Labels, listener.Labels) + + ephemeralRunner := b.newEphemeralRunner(ephemeralRunnerSet) + require.NoError(t, err) + + for _, key := range commonLabelKeys { + if key == LabelKeyKubernetesComponent { + continue + } + assert.Equal(t, ephemeralRunnerSet.Labels[key], ephemeralRunner.Labels[key]) + } + assert.Equal(t, "runner", ephemeralRunner.Labels[LabelKeyKubernetesComponent]) + assert.Equal(t, autoscalingRunnerSet.Annotations[AnnotationKeyGitHubRunnerGroupName], ephemeralRunner.Annotations[AnnotationKeyGitHubRunnerGroupName]) + + runnerSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + } + pod := b.newEphemeralRunnerPod(context.TODO(), ephemeralRunner, runnerSecret) + for key := range ephemeralRunner.Labels { + assert.Equal(t, ephemeralRunner.Labels[key], pod.Labels[key]) + } +} diff --git a/docs/preview/gha-runner-scale-set-controller/README.md b/docs/preview/gha-runner-scale-set-controller/README.md index 31ed62cd..3c6f5e50 100644 --- a/docs/preview/gha-runner-scale-set-controller/README.md +++ b/docs/preview/gha-runner-scale-set-controller/README.md @@ -160,7 +160,7 @@ kubectl logs -n "${NAMESPACE}" -l app.kubernetes.io/name=gha-runner-scale-set-co ```bash # Runner set listener logs -kubectl logs -n "${NAMESPACE}" -l auto-scaling-runner-set-namespace=arc-systems -l auto-scaling-runner-set-name=arc-runner-set +kubectl logs -n "${NAMESPACE}" -l actions.github.com/scale-set-namespace=arc-systems -l actions.github.com/scale-set-name=arc-runner-set ``` ### Naming error: `Name must have up to characters`