cmd/k8s-operator,k8s-operator: recorder custom service account

Allows configuring a custom service account for the recorder pods,
allowing the use of IRSA and other mechanisms for authing to write to
recorder buckets

Fixes #15875

Signed-off-by: Lee Briggs <lee@leebriggs.co.uk>
This commit is contained in:
Lee Briggs
2025-05-05 10:16:43 -07:00
parent b03a2a323b
commit 1a6255feb3
6 changed files with 50 additions and 14 deletions

View File

@@ -1557,6 +1557,13 @@ spec:
May also be set in PodSecurityContext. If set in both SecurityContext and May also be set in PodSecurityContext. If set in both SecurityContext and
PodSecurityContext, the value specified in SecurityContext takes precedence. PodSecurityContext, the value specified in SecurityContext takes precedence.
type: string type: string
serviceAccountName:
description: |-
The service account to use for the Recorder's StatefulSet. If not set,
the operator will create a service account with the same name as the
Recorder resource.
https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#service-account
type: string
tolerations: tolerations:
description: |- description: |-
Tolerations for Recorder Pods. By default, the operator does not apply Tolerations for Recorder Pods. By default, the operator does not apply

View File

@@ -4552,6 +4552,13 @@ spec:
type: string type: string
type: object type: object
type: object type: object
serviceAccountName:
description: |-
The service account to use for the Recorder's StatefulSet. If not set,
the operator will create a service account with the same name as the
Recorder resource.
https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#service-account
type: string
tolerations: tolerations:
description: |- description: |-
Tolerations for Recorder Pods. By default, the operator does not apply Tolerations for Recorder Pods. By default, the operator does not apply

View File

@@ -169,13 +169,16 @@ func (r *RecorderReconciler) maybeProvision(ctx context.Context, tsr *tsapi.Reco
}); err != nil { }); err != nil {
return fmt.Errorf("error creating state Secret: %w", err) return fmt.Errorf("error creating state Secret: %w", err)
} }
sa := tsrServiceAccount(tsr, r.tsNamespace) // Create the ServiceAccount only if the user hasn't specified a custom name
if _, err := createOrUpdate(ctx, r.Client, r.tsNamespace, sa, func(s *corev1.ServiceAccount) { if tsr.Spec.StatefulSet.Pod.ServiceAccountName == "" {
s.ObjectMeta.Labels = sa.ObjectMeta.Labels sa := tsrServiceAccount(tsr, r.tsNamespace)
s.ObjectMeta.Annotations = sa.ObjectMeta.Annotations if _, err := createOrUpdate(ctx, r.Client, r.tsNamespace, sa, func(s *corev1.ServiceAccount) {
s.ObjectMeta.OwnerReferences = sa.ObjectMeta.OwnerReferences s.ObjectMeta.Labels = sa.ObjectMeta.Labels
}); err != nil { s.ObjectMeta.Annotations = sa.ObjectMeta.Annotations
return fmt.Errorf("error creating ServiceAccount: %w", err) s.ObjectMeta.OwnerReferences = sa.ObjectMeta.OwnerReferences
}); err != nil {
return fmt.Errorf("error creating ServiceAccount: %w", err)
}
} }
role := tsrRole(tsr, r.tsNamespace) role := tsrRole(tsr, r.tsNamespace)
if _, err := createOrUpdate(ctx, r.Client, r.tsNamespace, role, func(r *rbacv1.Role) { if _, err := createOrUpdate(ctx, r.Client, r.tsNamespace, role, func(r *rbacv1.Role) {

View File

@@ -39,12 +39,18 @@ func tsrStatefulSet(tsr *tsapi.Recorder, namespace string) *appsv1.StatefulSet {
Annotations: tsr.Spec.StatefulSet.Pod.Annotations, Annotations: tsr.Spec.StatefulSet.Pod.Annotations,
}, },
Spec: corev1.PodSpec{ Spec: corev1.PodSpec{
ServiceAccountName: tsr.Name, ServiceAccountName: func() string {
Affinity: tsr.Spec.StatefulSet.Pod.Affinity, if tsr.Spec.StatefulSet.Pod.ServiceAccountName != "" {
SecurityContext: tsr.Spec.StatefulSet.Pod.SecurityContext, return tsr.Spec.StatefulSet.Pod.ServiceAccountName
ImagePullSecrets: tsr.Spec.StatefulSet.Pod.ImagePullSecrets, }
NodeSelector: tsr.Spec.StatefulSet.Pod.NodeSelector,
Tolerations: tsr.Spec.StatefulSet.Pod.Tolerations, return tsr.Name
}(),
Affinity: tsr.Spec.StatefulSet.Pod.Affinity,
SecurityContext: tsr.Spec.StatefulSet.Pod.SecurityContext,
ImagePullSecrets: tsr.Spec.StatefulSet.Pod.ImagePullSecrets,
NodeSelector: tsr.Spec.StatefulSet.Pod.NodeSelector,
Tolerations: tsr.Spec.StatefulSet.Pod.Tolerations,
Containers: []corev1.Container{ Containers: []corev1.Container{
{ {
Name: "recorder", Name: "recorder",
@@ -144,6 +150,11 @@ func tsrRole(tsr *tsapi.Recorder, namespace string) *rbacv1.Role {
} }
func tsrRoleBinding(tsr *tsapi.Recorder, namespace string) *rbacv1.RoleBinding { func tsrRoleBinding(tsr *tsapi.Recorder, namespace string) *rbacv1.RoleBinding {
saName := tsr.Spec.StatefulSet.Pod.ServiceAccountName
if saName == "" {
saName = tsr.Name
}
return &rbacv1.RoleBinding{ return &rbacv1.RoleBinding{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: tsr.Name, Name: tsr.Name,
@@ -154,7 +165,7 @@ func tsrRoleBinding(tsr *tsapi.Recorder, namespace string) *rbacv1.RoleBinding {
Subjects: []rbacv1.Subject{ Subjects: []rbacv1.Subject{
{ {
Kind: "ServiceAccount", Kind: "ServiceAccount",
Name: tsr.Name, Name: saName,
Namespace: namespace, Namespace: namespace,
}, },
}, },

View File

@@ -726,6 +726,7 @@ _Appears in:_
| `imagePullSecrets` _[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.3/#localobjectreference-v1-core) array_ | Image pull Secrets for Recorder Pods.<br />https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#PodSpec | | | | `imagePullSecrets` _[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.3/#localobjectreference-v1-core) array_ | Image pull Secrets for Recorder Pods.<br />https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#PodSpec | | |
| `nodeSelector` _object (keys:string, values:string)_ | Node selector rules for Recorder Pods. By default, the operator does<br />not apply any node selector rules.<br />https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#scheduling | | | | `nodeSelector` _object (keys:string, values:string)_ | Node selector rules for Recorder Pods. By default, the operator does<br />not apply any node selector rules.<br />https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#scheduling | | |
| `tolerations` _[Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.3/#toleration-v1-core) array_ | Tolerations for Recorder Pods. By default, the operator does not apply<br />any tolerations.<br />https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#scheduling | | | | `tolerations` _[Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.3/#toleration-v1-core) array_ | Tolerations for Recorder Pods. By default, the operator does not apply<br />any tolerations.<br />https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#scheduling | | |
| `serviceAccountName` _string_ | The service account to use for the Recorder's StatefulSet. If not set,<br />the operator will create a service account with the same name as the<br />Recorder resource.<br />https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#service-account | | |
#### RecorderSpec #### RecorderSpec

View File

@@ -142,6 +142,13 @@ type RecorderPod struct {
// https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#scheduling // https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#scheduling
// +optional // +optional
Tolerations []corev1.Toleration `json:"tolerations,omitempty"` Tolerations []corev1.Toleration `json:"tolerations,omitempty"`
// The service account to use for the Recorder's StatefulSet. If not set,
// the operator will create a service account with the same name as the
// Recorder resource.
// https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#service-account
// +optional
ServiceAccountName string `json:"serviceAccountName,omitempty"`
} }
type RecorderContainer struct { type RecorderContainer struct {