Compare commits

...

1 Commits

Author SHA1 Message Date
Nikola Jokic
1d9f626c53 Allow users to apply labels and annotations to internal resources (#4400) 2026-03-12 10:32:54 +01:00
16 changed files with 736 additions and 50 deletions

View File

@@ -69,6 +69,18 @@ type AutoscalingListenerSpec struct {
// +optional
Template *corev1.PodTemplateSpec `json:"template,omitempty"`
// +optional
ConfigSecretMetadata *ResourceMeta `json:"configSecretMetadata,omitempty"`
// +optional
ServiceAccountMetadata *ResourceMeta `json:"serviceAccountMetadata,omitempty"`
// +optional
RoleMetadata *ResourceMeta `json:"roleMetadata,omitempty"`
// +optional
RoleBindingMetadata *ResourceMeta `json:"roleBindingMetadata,omitempty"`
}
// AutoscalingListenerStatus defines the observed state of AutoscalingListener

View File

@@ -78,12 +78,36 @@ type AutoscalingRunnerSetSpec struct {
// Required
Template corev1.PodTemplateSpec `json:"template,omitempty"`
// +optional
AutoscalingListenerMetadata *ResourceMeta `json:"autoscalingListener,omitempty"`
// +optional
ListenerMetrics *MetricsConfig `json:"listenerMetrics,omitempty"`
// +optional
ListenerTemplate *corev1.PodTemplateSpec `json:"listenerTemplate,omitempty"`
// +optional
ListenerServiceAccountMetadata *ResourceMeta `json:"listenerServiceAccountMetadata,omitempty"`
// +optional
ListenerRoleMetadata *ResourceMeta `json:"listenerRoleMetadata,omitempty"`
// +optional
ListenerRoleBindingMetadata *ResourceMeta `json:"listenerRoleBindingMetadata,omitempty"`
// +optional
ListenerConfigSecretMetadata *ResourceMeta `json:"listenerConfigSecretMetadata,omitempty"`
// +optional
EphemeralRunnerSetMetadata *ResourceMeta `json:"ephemeralRunnerSetMetadata,omitempty"`
// +optional
EphemeralRunnerMetadata *ResourceMeta `json:"ephemeralRunnerMetadata,omitempty"`
// +optional
EphemeralRunnerConfigSecretMetadata *ResourceMeta `json:"ephemeralRunnerConfigSecretMetadata,omitempty"`
// +optional
// +kubebuilder:validation:Minimum:=0
MaxRunners *int `json:"maxRunners,omitempty"`

View File

@@ -0,0 +1,9 @@
package v1alpha1
// ResourceMeta carries metadata common to all internal resources
type ResourceMeta struct {
// +optional
Labels map[string]string `json:"labels,omitempty"`
// +optional
Annotations map[string]string `json:"annotations,omitempty"`
}

View File

@@ -122,6 +122,9 @@ type EphemeralRunnerSpec struct {
// +optional
VaultConfig *VaultConfig `json:"vaultConfig,omitempty"`
// +optional
EphemeralRunnerConfigSecretMetadata *ResourceMeta `json:"ephemeralRunnerConfigSecretMetadata,omitempty"`
corev1.PodTemplateSpec `json:",inline"`
}

View File

@@ -28,6 +28,8 @@ type EphemeralRunnerSetSpec struct {
PatchID int `json:"patchID"`
// EphemeralRunnerSpec is the spec of the ephemeral runner
EphemeralRunnerSpec EphemeralRunnerSpec `json:"ephemeralRunnerSpec,omitempty"`
// +optional
EphemeralRunnerMetadata *ResourceMeta `json:"ephemeralRunnerMetadata,omitempty"`
}
// EphemeralRunnerSetStatus defines the observed state of EphemeralRunnerSet

View File

@@ -118,6 +118,26 @@ func (in *AutoscalingListenerSpec) DeepCopyInto(out *AutoscalingListenerSpec) {
*out = new(v1.PodTemplateSpec)
(*in).DeepCopyInto(*out)
}
if in.ConfigSecretMetadata != nil {
in, out := &in.ConfigSecretMetadata, &out.ConfigSecretMetadata
*out = new(ResourceMeta)
(*in).DeepCopyInto(*out)
}
if in.ServiceAccountMetadata != nil {
in, out := &in.ServiceAccountMetadata, &out.ServiceAccountMetadata
*out = new(ResourceMeta)
(*in).DeepCopyInto(*out)
}
if in.RoleMetadata != nil {
in, out := &in.RoleMetadata, &out.RoleMetadata
*out = new(ResourceMeta)
(*in).DeepCopyInto(*out)
}
if in.RoleBindingMetadata != nil {
in, out := &in.RoleBindingMetadata, &out.RoleBindingMetadata
*out = new(ResourceMeta)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutoscalingListenerSpec.
@@ -223,6 +243,11 @@ func (in *AutoscalingRunnerSetSpec) DeepCopyInto(out *AutoscalingRunnerSetSpec)
(*in).DeepCopyInto(*out)
}
in.Template.DeepCopyInto(&out.Template)
if in.AutoscalingListenerMetadata != nil {
in, out := &in.AutoscalingListenerMetadata, &out.AutoscalingListenerMetadata
*out = new(ResourceMeta)
(*in).DeepCopyInto(*out)
}
if in.ListenerMetrics != nil {
in, out := &in.ListenerMetrics, &out.ListenerMetrics
*out = new(MetricsConfig)
@@ -233,6 +258,41 @@ func (in *AutoscalingRunnerSetSpec) DeepCopyInto(out *AutoscalingRunnerSetSpec)
*out = new(v1.PodTemplateSpec)
(*in).DeepCopyInto(*out)
}
if in.ListenerServiceAccountMetadata != nil {
in, out := &in.ListenerServiceAccountMetadata, &out.ListenerServiceAccountMetadata
*out = new(ResourceMeta)
(*in).DeepCopyInto(*out)
}
if in.ListenerRoleMetadata != nil {
in, out := &in.ListenerRoleMetadata, &out.ListenerRoleMetadata
*out = new(ResourceMeta)
(*in).DeepCopyInto(*out)
}
if in.ListenerRoleBindingMetadata != nil {
in, out := &in.ListenerRoleBindingMetadata, &out.ListenerRoleBindingMetadata
*out = new(ResourceMeta)
(*in).DeepCopyInto(*out)
}
if in.ListenerConfigSecretMetadata != nil {
in, out := &in.ListenerConfigSecretMetadata, &out.ListenerConfigSecretMetadata
*out = new(ResourceMeta)
(*in).DeepCopyInto(*out)
}
if in.EphemeralRunnerSetMetadata != nil {
in, out := &in.EphemeralRunnerSetMetadata, &out.EphemeralRunnerSetMetadata
*out = new(ResourceMeta)
(*in).DeepCopyInto(*out)
}
if in.EphemeralRunnerMetadata != nil {
in, out := &in.EphemeralRunnerMetadata, &out.EphemeralRunnerMetadata
*out = new(ResourceMeta)
(*in).DeepCopyInto(*out)
}
if in.EphemeralRunnerConfigSecretMetadata != nil {
in, out := &in.EphemeralRunnerConfigSecretMetadata, &out.EphemeralRunnerConfigSecretMetadata
*out = new(ResourceMeta)
(*in).DeepCopyInto(*out)
}
if in.MaxRunners != nil {
in, out := &in.MaxRunners, &out.MaxRunners
*out = new(int)
@@ -427,6 +487,11 @@ func (in *EphemeralRunnerSetList) DeepCopyObject() runtime.Object {
func (in *EphemeralRunnerSetSpec) DeepCopyInto(out *EphemeralRunnerSetSpec) {
*out = *in
in.EphemeralRunnerSpec.DeepCopyInto(&out.EphemeralRunnerSpec)
if in.EphemeralRunnerMetadata != nil {
in, out := &in.EphemeralRunnerMetadata, &out.EphemeralRunnerMetadata
*out = new(ResourceMeta)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EphemeralRunnerSetSpec.
@@ -472,6 +537,11 @@ func (in *EphemeralRunnerSpec) DeepCopyInto(out *EphemeralRunnerSpec) {
*out = new(VaultConfig)
(*in).DeepCopyInto(*out)
}
if in.EphemeralRunnerConfigSecretMetadata != nil {
in, out := &in.EphemeralRunnerConfigSecretMetadata, &out.EphemeralRunnerConfigSecretMetadata
*out = new(ResourceMeta)
(*in).DeepCopyInto(*out)
}
in.PodTemplateSpec.DeepCopyInto(&out.PodTemplateSpec)
}
@@ -660,6 +730,35 @@ func (in *ProxyServerConfig) DeepCopy() *ProxyServerConfig {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ResourceMeta) DeepCopyInto(out *ResourceMeta) {
*out = *in
if in.Labels != nil {
in, out := &in.Labels, &out.Labels
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.Annotations != nil {
in, out := &in.Annotations, &out.Annotations
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceMeta.
func (in *ResourceMeta) DeepCopy() *ResourceMeta {
if in == nil {
return nil
}
out := new(ResourceMeta)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TLSCertificateSource) DeepCopyInto(out *TLSCertificateSource) {
*out = *in

View File

@@ -56,6 +56,19 @@ spec:
autoscalingRunnerSetNamespace:
description: Required
type: string
configSecretMetadata:
description: ResourceMeta carries metadata common to all internal
resources
properties:
annotations:
additionalProperties:
type: string
type: object
labels:
additionalProperties:
type: string
type: object
type: object
ephemeralRunnerSetName:
description: Required
type: string
@@ -196,9 +209,48 @@ spec:
type: string
type: array
type: object
roleBindingMetadata:
description: ResourceMeta carries metadata common to all internal
resources
properties:
annotations:
additionalProperties:
type: string
type: object
labels:
additionalProperties:
type: string
type: object
type: object
roleMetadata:
description: ResourceMeta carries metadata common to all internal
resources
properties:
annotations:
additionalProperties:
type: string
type: object
labels:
additionalProperties:
type: string
type: object
type: object
runnerScaleSetId:
description: Required
type: integer
serviceAccountMetadata:
description: ResourceMeta carries metadata common to all internal
resources
properties:
annotations:
additionalProperties:
type: string
type: object
labels:
additionalProperties:
type: string
type: object
type: object
template:
description: PodTemplateSpec describes the data a pod should have
when created from a template

View File

@@ -64,6 +64,54 @@ spec:
spec:
description: AutoscalingRunnerSetSpec defines the desired state of AutoscalingRunnerSet
properties:
autoscalingListener:
description: ResourceMeta carries metadata common to all internal resources
properties:
annotations:
additionalProperties:
type: string
type: object
labels:
additionalProperties:
type: string
type: object
type: object
ephemeralRunnerConfigSecretMetadata:
description: ResourceMeta carries metadata common to all internal resources
properties:
annotations:
additionalProperties:
type: string
type: object
labels:
additionalProperties:
type: string
type: object
type: object
ephemeralRunnerMetadata:
description: ResourceMeta carries metadata common to all internal resources
properties:
annotations:
additionalProperties:
type: string
type: object
labels:
additionalProperties:
type: string
type: object
type: object
ephemeralRunnerSetMetadata:
description: ResourceMeta carries metadata common to all internal resources
properties:
annotations:
additionalProperties:
type: string
type: object
labels:
additionalProperties:
type: string
type: object
type: object
githubConfigSecret:
description: Required
type: string
@@ -99,6 +147,18 @@ spec:
x-kubernetes-map-type: atomic
type: object
type: object
listenerConfigSecretMetadata:
description: ResourceMeta carries metadata common to all internal resources
properties:
annotations:
additionalProperties:
type: string
type: object
labels:
additionalProperties:
type: string
type: object
type: object
listenerMetrics:
description: MetricsConfig holds configuration parameters for each metric type
properties:
@@ -143,6 +203,42 @@ spec:
type: object
type: object
type: object
listenerRoleBindingMetadata:
description: ResourceMeta carries metadata common to all internal resources
properties:
annotations:
additionalProperties:
type: string
type: object
labels:
additionalProperties:
type: string
type: object
type: object
listenerRoleMetadata:
description: ResourceMeta carries metadata common to all internal resources
properties:
annotations:
additionalProperties:
type: string
type: object
labels:
additionalProperties:
type: string
type: object
type: object
listenerServiceAccountMetadata:
description: ResourceMeta carries metadata common to all internal resources
properties:
annotations:
additionalProperties:
type: string
type: object
labels:
additionalProperties:
type: string
type: object
type: object
listenerTemplate:
description: PodTemplateSpec describes the data a pod should have when created from a template
properties:

View File

@@ -70,6 +70,18 @@ spec:
spec:
description: EphemeralRunnerSpec defines the desired state of EphemeralRunner
properties:
ephemeralRunnerConfigSecretMetadata:
description: ResourceMeta carries metadata common to all internal resources
properties:
annotations:
additionalProperties:
type: string
type: object
labels:
additionalProperties:
type: string
type: object
type: object
githubConfigSecret:
type: string
githubConfigUrl:

View File

@@ -58,9 +58,33 @@ spec:
spec:
description: EphemeralRunnerSetSpec defines the desired state of EphemeralRunnerSet
properties:
ephemeralRunnerMetadata:
description: ResourceMeta carries metadata common to all internal resources
properties:
annotations:
additionalProperties:
type: string
type: object
labels:
additionalProperties:
type: string
type: object
type: object
ephemeralRunnerSpec:
description: EphemeralRunnerSpec is the spec of the ephemeral runner
properties:
ephemeralRunnerConfigSecretMetadata:
description: ResourceMeta carries metadata common to all internal resources
properties:
annotations:
additionalProperties:
type: string
type: object
labels:
additionalProperties:
type: string
type: object
type: object
githubConfigSecret:
type: string
githubConfigUrl:

View File

@@ -56,6 +56,19 @@ spec:
autoscalingRunnerSetNamespace:
description: Required
type: string
configSecretMetadata:
description: ResourceMeta carries metadata common to all internal
resources
properties:
annotations:
additionalProperties:
type: string
type: object
labels:
additionalProperties:
type: string
type: object
type: object
ephemeralRunnerSetName:
description: Required
type: string
@@ -196,9 +209,48 @@ spec:
type: string
type: array
type: object
roleBindingMetadata:
description: ResourceMeta carries metadata common to all internal
resources
properties:
annotations:
additionalProperties:
type: string
type: object
labels:
additionalProperties:
type: string
type: object
type: object
roleMetadata:
description: ResourceMeta carries metadata common to all internal
resources
properties:
annotations:
additionalProperties:
type: string
type: object
labels:
additionalProperties:
type: string
type: object
type: object
runnerScaleSetId:
description: Required
type: integer
serviceAccountMetadata:
description: ResourceMeta carries metadata common to all internal
resources
properties:
annotations:
additionalProperties:
type: string
type: object
labels:
additionalProperties:
type: string
type: object
type: object
template:
description: PodTemplateSpec describes the data a pod should have
when created from a template

View File

@@ -64,6 +64,54 @@ spec:
spec:
description: AutoscalingRunnerSetSpec defines the desired state of AutoscalingRunnerSet
properties:
autoscalingListener:
description: ResourceMeta carries metadata common to all internal resources
properties:
annotations:
additionalProperties:
type: string
type: object
labels:
additionalProperties:
type: string
type: object
type: object
ephemeralRunnerConfigSecretMetadata:
description: ResourceMeta carries metadata common to all internal resources
properties:
annotations:
additionalProperties:
type: string
type: object
labels:
additionalProperties:
type: string
type: object
type: object
ephemeralRunnerMetadata:
description: ResourceMeta carries metadata common to all internal resources
properties:
annotations:
additionalProperties:
type: string
type: object
labels:
additionalProperties:
type: string
type: object
type: object
ephemeralRunnerSetMetadata:
description: ResourceMeta carries metadata common to all internal resources
properties:
annotations:
additionalProperties:
type: string
type: object
labels:
additionalProperties:
type: string
type: object
type: object
githubConfigSecret:
description: Required
type: string
@@ -99,6 +147,18 @@ spec:
x-kubernetes-map-type: atomic
type: object
type: object
listenerConfigSecretMetadata:
description: ResourceMeta carries metadata common to all internal resources
properties:
annotations:
additionalProperties:
type: string
type: object
labels:
additionalProperties:
type: string
type: object
type: object
listenerMetrics:
description: MetricsConfig holds configuration parameters for each metric type
properties:
@@ -143,6 +203,42 @@ spec:
type: object
type: object
type: object
listenerRoleBindingMetadata:
description: ResourceMeta carries metadata common to all internal resources
properties:
annotations:
additionalProperties:
type: string
type: object
labels:
additionalProperties:
type: string
type: object
type: object
listenerRoleMetadata:
description: ResourceMeta carries metadata common to all internal resources
properties:
annotations:
additionalProperties:
type: string
type: object
labels:
additionalProperties:
type: string
type: object
type: object
listenerServiceAccountMetadata:
description: ResourceMeta carries metadata common to all internal resources
properties:
annotations:
additionalProperties:
type: string
type: object
labels:
additionalProperties:
type: string
type: object
type: object
listenerTemplate:
description: PodTemplateSpec describes the data a pod should have when created from a template
properties:

View File

@@ -70,6 +70,18 @@ spec:
spec:
description: EphemeralRunnerSpec defines the desired state of EphemeralRunner
properties:
ephemeralRunnerConfigSecretMetadata:
description: ResourceMeta carries metadata common to all internal resources
properties:
annotations:
additionalProperties:
type: string
type: object
labels:
additionalProperties:
type: string
type: object
type: object
githubConfigSecret:
type: string
githubConfigUrl:

View File

@@ -58,9 +58,33 @@ spec:
spec:
description: EphemeralRunnerSetSpec defines the desired state of EphemeralRunnerSet
properties:
ephemeralRunnerMetadata:
description: ResourceMeta carries metadata common to all internal resources
properties:
annotations:
additionalProperties:
type: string
type: object
labels:
additionalProperties:
type: string
type: object
type: object
ephemeralRunnerSpec:
description: EphemeralRunnerSpec is the spec of the ephemeral runner
properties:
ephemeralRunnerConfigSecretMetadata:
description: ResourceMeta carries metadata common to all internal resources
properties:
annotations:
additionalProperties:
type: string
type: object
labels:
additionalProperties:
type: string
type: object
type: object
githubConfigSecret:
type: string
githubConfigUrl:

View File

@@ -96,7 +96,7 @@ func (b *ResourceBuilder) newAutoScalingListener(autoscalingRunnerSet *v1alpha1.
effectiveMinRunners = *autoscalingRunnerSet.Spec.MinRunners
}
labels := b.mergeLabels(autoscalingRunnerSet.Labels, map[string]string{
labels := b.filterAndMergeLabels(autoscalingRunnerSet.Labels, map[string]string{
LabelKeyGitHubScaleSetNamespace: autoscalingRunnerSet.Namespace,
LabelKeyGitHubScaleSetName: autoscalingRunnerSet.Name,
LabelKeyKubernetesPartOf: labelValueKubernetesPartOf,
@@ -104,13 +104,18 @@ func (b *ResourceBuilder) newAutoScalingListener(autoscalingRunnerSet *v1alpha1.
LabelKeyKubernetesVersion: autoscalingRunnerSet.Labels[LabelKeyKubernetesVersion],
})
if err := applyGitHubURLLabels(autoscalingRunnerSet.Spec.GitHubConfigUrl, labels); err != nil {
return nil, fmt.Errorf("failed to apply GitHub URL labels: %v", err)
}
annotations := map[string]string{
annotationKeyRunnerSpecHash: autoscalingRunnerSet.ListenerSpecHash(),
annotationKeyValuesHash: autoscalingRunnerSet.Annotations[annotationKeyValuesHash],
}
if err := applyGitHubURLLabels(autoscalingRunnerSet.Spec.GitHubConfigUrl, labels); err != nil {
return nil, fmt.Errorf("failed to apply GitHub URL labels: %v", err)
if autoscalingRunnerSet.Spec.AutoscalingListenerMetadata != nil {
labels = b.filterAndMergeLabels(autoscalingRunnerSet.Spec.AutoscalingListenerMetadata.Labels, labels)
annotations = b.mergeAnnotations(autoscalingRunnerSet.Spec.AutoscalingListenerMetadata.Annotations, annotations)
}
autoscalingListener := &v1alpha1.AutoscalingListener{
@@ -136,6 +141,10 @@ func (b *ResourceBuilder) newAutoScalingListener(autoscalingRunnerSet *v1alpha1.
GitHubServerTLS: autoscalingRunnerSet.Spec.GitHubServerTLS,
Metrics: autoscalingRunnerSet.Spec.ListenerMetrics,
Template: autoscalingRunnerSet.Spec.ListenerTemplate,
ServiceAccountMetadata: autoscalingRunnerSet.Spec.ListenerServiceAccountMetadata,
RoleMetadata: autoscalingRunnerSet.Spec.ListenerRoleMetadata,
RoleBindingMetadata: autoscalingRunnerSet.Spec.ListenerRoleBindingMetadata,
ConfigSecretMetadata: autoscalingRunnerSet.Spec.ListenerConfigSecretMetadata,
},
}
@@ -212,10 +221,22 @@ func (b *ResourceBuilder) newScaleSetListenerConfig(autoscalingListener *v1alpha
return nil, fmt.Errorf("failed to encode config: %w", err)
}
var labels map[string]string
if autoscalingListener.Spec.ConfigSecretMetadata != nil && len(autoscalingListener.Spec.ConfigSecretMetadata.Labels) > 0 {
labels = b.filterAndMergeLabels(autoscalingListener.Spec.ConfigSecretMetadata.Labels, nil)
}
var annotations map[string]string
if autoscalingListener.Spec.ConfigSecretMetadata != nil && len(autoscalingListener.Spec.ConfigSecretMetadata.Annotations) > 0 {
annotations = autoscalingListener.Spec.ConfigSecretMetadata.Annotations
}
return &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: scaleSetListenerConfigName(autoscalingListener),
Namespace: autoscalingListener.Namespace,
Name: scaleSetListenerConfigName(autoscalingListener),
Namespace: autoscalingListener.Namespace,
Labels: labels,
Annotations: annotations,
},
Data: map[string][]byte{
"config.json": buf.Bytes(),
@@ -431,32 +452,49 @@ func mergeListenerContainer(base, from *corev1.Container) {
}
func (b *ResourceBuilder) newScaleSetListenerServiceAccount(autoscalingListener *v1alpha1.AutoscalingListener) *corev1.ServiceAccount {
return &corev1.ServiceAccount{
base := &corev1.ServiceAccount{
ObjectMeta: metav1.ObjectMeta{
Name: autoscalingListener.Name,
Namespace: autoscalingListener.Namespace,
Labels: b.mergeLabels(autoscalingListener.Labels, map[string]string{
Labels: b.filterAndMergeLabels(autoscalingListener.Labels, map[string]string{
LabelKeyGitHubScaleSetNamespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace,
LabelKeyGitHubScaleSetName: autoscalingListener.Spec.AutoscalingRunnerSetName,
}),
},
}
if autoscalingListener.Spec.ServiceAccountMetadata != nil {
base.Labels = b.filterAndMergeLabels(autoscalingListener.Spec.ServiceAccountMetadata.Labels, base.Labels)
base.Annotations = b.mergeAnnotations(autoscalingListener.Spec.ServiceAccountMetadata.Annotations, base.Annotations)
}
return base
}
func (b *ResourceBuilder) newScaleSetListenerRole(autoscalingListener *v1alpha1.AutoscalingListener) *rbacv1.Role {
rules := rulesForListenerRole([]string{autoscalingListener.Spec.EphemeralRunnerSetName})
rulesHash := hash.ComputeTemplateHash(&rules)
labels := b.filterAndMergeLabels(autoscalingListener.Labels, map[string]string{
LabelKeyGitHubScaleSetNamespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace,
LabelKeyGitHubScaleSetName: autoscalingListener.Spec.AutoscalingRunnerSetName,
labelKeyListenerNamespace: autoscalingListener.Namespace,
labelKeyListenerName: autoscalingListener.Name,
"role-policy-rules-hash": rulesHash,
})
var annotations map[string]string
if autoscalingListener.Spec.RoleMetadata != nil {
labels = b.filterAndMergeLabels(autoscalingListener.Spec.RoleMetadata.Labels, labels)
annotations = b.mergeAnnotations(autoscalingListener.Spec.RoleMetadata.Annotations, nil)
}
newRole := &rbacv1.Role{
ObjectMeta: metav1.ObjectMeta{
Name: autoscalingListener.Name,
Namespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace,
Labels: b.mergeLabels(autoscalingListener.Labels, map[string]string{
LabelKeyGitHubScaleSetNamespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace,
LabelKeyGitHubScaleSetName: autoscalingListener.Spec.AutoscalingRunnerSetName,
labelKeyListenerNamespace: autoscalingListener.Namespace,
labelKeyListenerName: autoscalingListener.Name,
"role-policy-rules-hash": rulesHash,
}),
Name: autoscalingListener.Name,
Namespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace,
Labels: labels,
Annotations: annotations,
},
Rules: rules,
}
@@ -480,18 +518,28 @@ func (b *ResourceBuilder) newScaleSetListenerRoleBinding(autoscalingListener *v1
}
subjectHash := hash.ComputeTemplateHash(&subjects)
labels := b.filterAndMergeLabels(autoscalingListener.Labels, map[string]string{
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,
})
var annotations map[string]string
if autoscalingListener.Spec.RoleBindingMetadata != nil {
labels = b.filterAndMergeLabels(autoscalingListener.Spec.RoleBindingMetadata.Labels, labels)
annotations = autoscalingListener.Spec.RoleBindingMetadata.Annotations
}
newRoleBinding := &rbacv1.RoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: autoscalingListener.Name,
Namespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace,
Labels: b.mergeLabels(autoscalingListener.Labels, map[string]string{
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,
}),
Name: autoscalingListener.Name,
Namespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace,
Labels: labels,
Annotations: annotations,
},
RoleRef: roleRef,
Subjects: subjects,
@@ -507,7 +555,7 @@ func (b *ResourceBuilder) newEphemeralRunnerSet(autoscalingRunnerSet *v1alpha1.A
}
runnerSpecHash := autoscalingRunnerSet.RunnerSetSpecHash()
labels := b.mergeLabels(autoscalingRunnerSet.Labels, map[string]string{
labels := b.filterAndMergeLabels(autoscalingRunnerSet.Labels, map[string]string{
LabelKeyKubernetesPartOf: labelValueKubernetesPartOf,
LabelKeyKubernetesComponent: "runner-set",
LabelKeyKubernetesVersion: autoscalingRunnerSet.Labels[LabelKeyKubernetesVersion],
@@ -519,19 +567,24 @@ func (b *ResourceBuilder) newEphemeralRunnerSet(autoscalingRunnerSet *v1alpha1.A
return nil, fmt.Errorf("failed to apply GitHub URL labels: %v", err)
}
newAnnotations := map[string]string{
annotations := map[string]string{
AnnotationKeyGitHubRunnerGroupName: autoscalingRunnerSet.Annotations[AnnotationKeyGitHubRunnerGroupName],
AnnotationKeyGitHubRunnerScaleSetName: autoscalingRunnerSet.Annotations[AnnotationKeyGitHubRunnerScaleSetName],
annotationKeyRunnerSpecHash: runnerSpecHash,
}
if autoscalingRunnerSet.Spec.EphemeralRunnerSetMetadata != nil {
labels = b.filterAndMergeLabels(autoscalingRunnerSet.Spec.EphemeralRunnerSetMetadata.Labels, labels)
annotations = b.mergeAnnotations(autoscalingRunnerSet.Spec.EphemeralRunnerSetMetadata.Annotations, annotations)
}
newEphemeralRunnerSet := &v1alpha1.EphemeralRunnerSet{
TypeMeta: metav1.TypeMeta{},
ObjectMeta: metav1.ObjectMeta{
GenerateName: autoscalingRunnerSet.Name + "-",
Namespace: autoscalingRunnerSet.Namespace,
Labels: labels,
Annotations: newAnnotations,
Annotations: annotations,
OwnerReferences: []metav1.OwnerReference{
{
APIVersion: autoscalingRunnerSet.GetObjectKind().GroupVersionKind().GroupVersion().String(),
@@ -546,14 +599,16 @@ func (b *ResourceBuilder) newEphemeralRunnerSet(autoscalingRunnerSet *v1alpha1.A
Spec: v1alpha1.EphemeralRunnerSetSpec{
Replicas: 0,
EphemeralRunnerSpec: v1alpha1.EphemeralRunnerSpec{
RunnerScaleSetId: runnerScaleSetID,
GitHubConfigUrl: autoscalingRunnerSet.Spec.GitHubConfigUrl,
GitHubConfigSecret: autoscalingRunnerSet.Spec.GitHubConfigSecret,
Proxy: autoscalingRunnerSet.Spec.Proxy,
GitHubServerTLS: autoscalingRunnerSet.Spec.GitHubServerTLS,
PodTemplateSpec: autoscalingRunnerSet.Spec.Template,
VaultConfig: autoscalingRunnerSet.VaultConfig(),
RunnerScaleSetId: runnerScaleSetID,
GitHubConfigUrl: autoscalingRunnerSet.Spec.GitHubConfigUrl,
GitHubConfigSecret: autoscalingRunnerSet.Spec.GitHubConfigSecret,
Proxy: autoscalingRunnerSet.Spec.Proxy,
GitHubServerTLS: autoscalingRunnerSet.Spec.GitHubServerTLS,
PodTemplateSpec: autoscalingRunnerSet.Spec.Template,
VaultConfig: autoscalingRunnerSet.VaultConfig(),
EphemeralRunnerConfigSecretMetadata: autoscalingRunnerSet.Spec.EphemeralRunnerConfigSecretMetadata,
},
EphemeralRunnerMetadata: autoscalingRunnerSet.Spec.EphemeralRunnerMetadata,
},
}
@@ -568,6 +623,12 @@ func (b *ResourceBuilder) newEphemeralRunner(ephemeralRunnerSet *v1alpha1.Epheme
annotations := make(map[string]string, len(ephemeralRunnerSet.Annotations)+1)
maps.Copy(annotations, ephemeralRunnerSet.Annotations)
annotations[AnnotationKeyPatchID] = strconv.Itoa(ephemeralRunnerSet.Spec.PatchID)
if ephemeralRunnerSet.Spec.EphemeralRunnerMetadata != nil {
labels = b.filterAndMergeLabels(ephemeralRunnerSet.Spec.EphemeralRunnerMetadata.Labels, labels)
annotations = b.mergeAnnotations(ephemeralRunnerSet.Spec.EphemeralRunnerMetadata.Annotations, annotations)
}
return &v1alpha1.EphemeralRunner{
ObjectMeta: metav1.ObjectMeta{
GenerateName: ephemeralRunnerSet.Name + "-runner-",
@@ -662,10 +723,22 @@ func (b *ResourceBuilder) newEphemeralRunnerPod(runner *v1alpha1.EphemeralRunner
}
func (b *ResourceBuilder) newEphemeralRunnerJitSecret(ephemeralRunner *v1alpha1.EphemeralRunner, jitConfig *actions.RunnerScaleSetJitRunnerConfig) *corev1.Secret {
var (
labels map[string]string
annotations map[string]string
)
if ephemeralRunner.Spec.EphemeralRunnerConfigSecretMetadata != nil {
labels = b.filterAndMergeLabels(ephemeralRunner.Spec.EphemeralRunnerConfigSecretMetadata.Labels, nil)
annotations = ephemeralRunner.Spec.EphemeralRunnerConfigSecretMetadata.Annotations
}
return &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: ephemeralRunner.Name,
Namespace: ephemeralRunner.Namespace,
Name: ephemeralRunner.Name,
Namespace: ephemeralRunner.Namespace,
Labels: labels,
Annotations: annotations,
},
Data: map[string][]byte{
jitTokenKey: []byte(jitConfig.EncodedJITConfig),
@@ -756,9 +829,12 @@ func trimLabelValue(val string) string {
return strings.Trim(val, "-_.")
}
func (b *ResourceBuilder) mergeLabels(base, overwrite map[string]string) map[string]string {
mergedLabels := make(map[string]string, len(base))
func (b *ResourceBuilder) filterAndMergeLabels(base, overwrite map[string]string) map[string]string {
if base == nil && overwrite == nil {
return nil
}
mergedLabels := make(map[string]string, len(base))
base:
for k, v := range base {
for _, prefix := range b.ExcludeLabelPropagationPrefixes {
@@ -781,3 +857,12 @@ overwrite:
return mergedLabels
}
func (b *ResourceBuilder) mergeAnnotations(base, overwrite map[string]string) map[string]string {
if base == nil && overwrite == nil {
return nil
}
base = maps.Clone(base)
maps.Copy(base, overwrite)
return base
}

View File

@@ -6,13 +6,14 @@ import (
"testing"
"github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1"
"github.com/actions/actions-runner-controller/github/actions"
"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) {
func TestMetadataPropagation(t *testing.T) {
autoscalingRunnerSet := v1alpha1.AutoscalingRunnerSet{
ObjectMeta: metav1.ObjectMeta{
Name: "test-scale-set",
@@ -34,6 +35,70 @@ func TestLabelPropagation(t *testing.T) {
},
Spec: v1alpha1.AutoscalingRunnerSetSpec{
GitHubConfigUrl: "https://github.com/org/repo",
AutoscalingListenerMetadata: &v1alpha1.ResourceMeta{
Labels: map[string]string{
"test.com/autoscaling-listener-label": "autoscaling-listener-label",
},
Annotations: map[string]string{
"test.com/autoscaling-listener-annotation": "autoscaling-listener-annotation",
},
},
ListenerServiceAccountMetadata: &v1alpha1.ResourceMeta{
Labels: map[string]string{
"test.com/listener-service-account-label": "listener-service-account-label",
},
Annotations: map[string]string{
"test.com/listener-service-account-annotation": "listener-service-account-annotation",
},
},
ListenerRoleMetadata: &v1alpha1.ResourceMeta{
Labels: map[string]string{
"test.com/listener-role-label": "listener-role-label",
},
Annotations: map[string]string{
"test.com/listener-role-annotation": "listener-role-annotation",
},
},
ListenerRoleBindingMetadata: &v1alpha1.ResourceMeta{
Labels: map[string]string{
"test.com/listener-role-binding-label": "listener-role-binding-label",
},
Annotations: map[string]string{
"test.com/listener-role-binding-annotation": "listener-role-binding-annotation",
},
},
ListenerConfigSecretMetadata: &v1alpha1.ResourceMeta{
Labels: map[string]string{
"test.com/listener-config-secret-label": "listener-config-secret-label",
},
Annotations: map[string]string{
"test.com/listener-config-secret-annotation": "listener-config-secret-annotation",
},
},
EphemeralRunnerSetMetadata: &v1alpha1.ResourceMeta{
Labels: map[string]string{
"test.com/ephemeral-runner-set-label": "ephemeral-runner-set-label",
},
Annotations: map[string]string{
"test.com/ephemeral-runner-set-annotation": "ephemeral-runner-set-annotation",
},
},
EphemeralRunnerMetadata: &v1alpha1.ResourceMeta{
Labels: map[string]string{
"test.com/ephemeral-runner-label": "ephemeral-runner-label",
},
Annotations: map[string]string{
"test.com/ephemeral-runner-annotation": "ephemeral-runner-annotation",
},
},
EphemeralRunnerConfigSecretMetadata: &v1alpha1.ResourceMeta{
Labels: map[string]string{
"test.com/ephemeral-runner-config-secret-label": "ephemeral-runner-config-secret-label",
},
Annotations: map[string]string{
"test.com/ephemeral-runner-config-secret-annotation": "ephemeral-runner-config-secret-annotation",
},
},
},
}
@@ -57,6 +122,8 @@ func TestLabelPropagation(t *testing.T) {
assert.Equal(t, autoscalingRunnerSet.Annotations[AnnotationKeyGitHubRunnerGroupName], ephemeralRunnerSet.Annotations[AnnotationKeyGitHubRunnerGroupName])
assert.Equal(t, autoscalingRunnerSet.Annotations[AnnotationKeyGitHubRunnerScaleSetName], ephemeralRunnerSet.Annotations[AnnotationKeyGitHubRunnerScaleSetName])
assert.Equal(t, autoscalingRunnerSet.Labels["arbitrary-label"], ephemeralRunnerSet.Labels["arbitrary-label"])
assert.Equal(t, "ephemeral-runner-set-label", ephemeralRunnerSet.Labels["test.com/ephemeral-runner-set-label"])
assert.Equal(t, "ephemeral-runner-set-annotation", ephemeralRunnerSet.Annotations["test.com/ephemeral-runner-set-annotation"])
listener, err := b.newAutoScalingListener(&autoscalingRunnerSet, ephemeralRunnerSet, autoscalingRunnerSet.Namespace, "test:latest", nil)
require.NoError(t, err)
@@ -70,17 +137,26 @@ func TestLabelPropagation(t *testing.T) {
assert.Equal(t, "org", listener.Labels[LabelKeyGitHubOrganization])
assert.Equal(t, "repo", listener.Labels[LabelKeyGitHubRepository])
assert.Equal(t, autoscalingRunnerSet.Labels["arbitrary-label"], listener.Labels["arbitrary-label"])
assert.Equal(t, "autoscaling-listener-label", listener.Labels["test.com/autoscaling-listener-label"])
assert.Equal(t, "autoscaling-listener-annotation", listener.Annotations["test.com/autoscaling-listener-annotation"])
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",
},
}
listenerServiceAccount := b.newScaleSetListenerServiceAccount(listener)
assert.Equal(t, "listener-service-account-label", listenerServiceAccount.Labels["test.com/listener-service-account-label"])
assert.Equal(t, "listener-service-account-annotation", listenerServiceAccount.Annotations["test.com/listener-service-account-annotation"])
listenerRole := b.newScaleSetListenerRole(listener)
assert.Equal(t, "listener-role-label", listenerRole.Labels["test.com/listener-role-label"])
assert.Equal(t, "listener-role-annotation", listenerRole.Annotations["test.com/listener-role-annotation"])
listenerRoleBinding := b.newScaleSetListenerRoleBinding(listener, listenerRole, listenerServiceAccount)
assert.Equal(t, "listener-role-binding-label", listenerRoleBinding.Labels["test.com/listener-role-binding-label"])
assert.Equal(t, "listener-role-binding-annotation", listenerRoleBinding.Annotations["test.com/listener-role-binding-annotation"])
listenerPod, err := b.newScaleSetListenerPod(listener, &corev1.Secret{}, listenerServiceAccount, nil)
require.NoError(t, err)
assert.Equal(t, listenerPod.Labels, listener.Labels)
@@ -97,12 +173,20 @@ func TestLabelPropagation(t *testing.T) {
assert.Equal(t, "runner", ephemeralRunner.Labels[LabelKeyKubernetesComponent])
assert.Equal(t, autoscalingRunnerSet.Annotations[AnnotationKeyGitHubRunnerGroupName], ephemeralRunner.Annotations[AnnotationKeyGitHubRunnerGroupName])
assert.Equal(t, autoscalingRunnerSet.Annotations[AnnotationKeyGitHubRunnerScaleSetName], ephemeralRunnerSet.Annotations[AnnotationKeyGitHubRunnerScaleSetName])
assert.Equal(t, "ephemeral-runner-label", ephemeralRunner.Labels["test.com/ephemeral-runner-label"])
assert.Equal(t, "ephemeral-runner-annotation", ephemeralRunner.Annotations["test.com/ephemeral-runner-annotation"])
runnerSecret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "test",
runnerSecret := b.newEphemeralRunnerJitSecret(ephemeralRunner, &actions.RunnerScaleSetJitRunnerConfig{
Runner: &actions.RunnerReference{
Id: 1,
Name: "test",
RunnerScaleSetId: 1,
},
}
EncodedJITConfig: "",
})
assert.Equal(t, "ephemeral-runner-config-secret-label", runnerSecret.Labels["test.com/ephemeral-runner-config-secret-label"])
assert.Equal(t, "ephemeral-runner-config-secret-annotation", runnerSecret.Annotations["test.com/ephemeral-runner-config-secret-annotation"])
pod := b.newEphemeralRunnerPod(ephemeralRunner, runnerSecret)
for key := range ephemeralRunner.Labels {
assert.Equal(t, ephemeralRunner.Labels[key], pod.Labels[key])