From c7eaa72909ca64e4747872a36c719340a2431ad2 Mon Sep 17 00:00:00 2001 From: Jeremy Richards Date: Mon, 17 Nov 2025 11:08:40 -0700 Subject: [PATCH] Bootstrap Pods needs Humio ServiceAccount Add the HumioServiceAccount to the HumioBootstrapTokenConfig to provide the bootstrap pod with the same permissions the Humio nodepool pods have. This is necessary as the `default` ServiceAccount does not necessarily have permissions to access bucket storage, which can be a requirement for bootstrapping to complete successfully. Add unit tests to validate ServiceAccount is correctly added. Tests: In my environment, we had a workaround for this by adding the S3 bucket access to a node role. Removing that access broke the bootstrapper. Using this PR fixed the bootstrapper. Risk: Low - only mounts the ServiceAccount to one new location. There might be a race condition in when the ServiceAccount is created vs. when the bootstrapper Pod is created, but if there is, it resolves on its own without intervention. --- internal/controller/humiobootstraptoken_controller.go | 7 ++++--- internal/controller/humiobootstraptoken_defaults.go | 10 ++++++++++ internal/controller/humiocluster_controller.go | 9 +++++++++ .../humiobootstraptoken_controller_test.go | 4 ++++ 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/internal/controller/humiobootstraptoken_controller.go b/internal/controller/humiobootstraptoken_controller.go index 0778ad2a1..7f271ef2d 100644 --- a/internal/controller/humiobootstraptoken_controller.go +++ b/internal/controller/humiobootstraptoken_controller.go @@ -442,9 +442,10 @@ func (r *HumioBootstrapTokenReconciler) constructBootstrapPod(ctx context.Contex Namespace: bootstrapConfig.Namespace(), }, Spec: corev1.PodSpec{ - ImagePullSecrets: bootstrapConfig.imagePullSecrets(), - Affinity: bootstrapConfig.affinity(), - Tolerations: bootstrapConfig.tolerations(), + ServiceAccountName: bootstrapConfig.humioServiceAccountName(), + ImagePullSecrets: bootstrapConfig.imagePullSecrets(), + Affinity: bootstrapConfig.affinity(), + Tolerations: bootstrapConfig.tolerations(), Containers: []corev1.Container{ { Name: HumioContainerName, diff --git a/internal/controller/humiobootstraptoken_defaults.go b/internal/controller/humiobootstraptoken_defaults.go index 9fa6ceca2..3d506dddd 100644 --- a/internal/controller/humiobootstraptoken_defaults.go +++ b/internal/controller/humiobootstraptoken_defaults.go @@ -148,6 +148,16 @@ func (b *HumioBootstrapTokenConfig) resources() corev1.ResourceRequirements { } } +func (b *HumioBootstrapTokenConfig) humioServiceAccountName() string { + // If the HumioCluster spec has a service account name set, use that + if b.ManagedHumioCluster.Spec.HumioServiceAccountName != "" { + return b.ManagedHumioCluster.Spec.HumioServiceAccountName + } + // Otherwise, generate the default service account name based on cluster name + // This matches the default behavior when no node pool name is specified + return fmt.Sprintf("%s-%s", b.ManagedHumioCluster.Name, HumioServiceAccountNameSuffix) +} + func (b *HumioBootstrapTokenConfig) PodName() string { return fmt.Sprintf("%s-%s", b.BootstrapToken.Name, bootstrapTokenPodNameSuffix) } diff --git a/internal/controller/humiocluster_controller.go b/internal/controller/humiocluster_controller.go index 0435f517d..6fe1d100d 100644 --- a/internal/controller/humiocluster_controller.go +++ b/internal/controller/humiocluster_controller.go @@ -166,6 +166,15 @@ func (r *HumioClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request } } + // ensure service accounts exist for all node pools before creating bootstrap token + // the bootstrap pod needs to reference the service account, so it must exist first + for _, pool := range humioNodePools.Items { + if err := r.ensureHumioPodPermissions(ctx, hc, pool); err != nil { + return r.updateStatus(ctx, r.Status(), hc, statusOptions(). + withMessage(err.Error())) + } + } + // create HumioBootstrapToken and block until we have a hashed bootstrap token if result, err := r.ensureHumioClusterBootstrapToken(ctx, hc); result != emptyResult || err != nil { if err != nil { diff --git a/internal/controller/suite/bootstraptokens/humiobootstraptoken_controller_test.go b/internal/controller/suite/bootstraptokens/humiobootstraptoken_controller_test.go index 9646cbe51..23aee3c8e 100644 --- a/internal/controller/suite/bootstraptokens/humiobootstraptoken_controller_test.go +++ b/internal/controller/suite/bootstraptokens/humiobootstraptoken_controller_test.go @@ -128,6 +128,10 @@ var _ = Describe("HumioBootstrapToken Controller", func() { Expect(bootstrapTokenOneTimePod.Name).To(Equal(bootstrapTokenConfig.PodName())) + // Verify service account matches the Humio service account + expectedServiceAccountName := toCreateHumioCluster.Name + "-humio" + Expect(bootstrapTokenOneTimePod.Spec.ServiceAccountName).To(Equal(expectedServiceAccountName)) + // Verify node affinity matches Expect(bootstrapTokenOneTimePod.Spec.Affinity).ToNot(BeNil()) Expect(bootstrapTokenOneTimePod.Spec.Affinity.NodeAffinity).ToNot(BeNil())