@@ -20,9 +20,11 @@ import (
2020 "context"
2121 "fmt"
2222
23+ certmanagerv1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
2324 appsv1 "k8s.io/api/apps/v1"
2425 corev1 "k8s.io/api/core/v1"
2526 "k8s.io/apimachinery/pkg/api/errors"
27+ "k8s.io/apimachinery/pkg/api/meta"
2628 "k8s.io/apimachinery/pkg/api/resource"
2729 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2830 "k8s.io/apimachinery/pkg/util/intstr"
@@ -39,6 +41,7 @@ const (
3941 telemetryCertSuffix = "-telemetry-tls"
4042 telemetryServiceName = "jumpstarter-telemetry"
4143 telemetryComponentApp = "jumpstarter-telemetry"
44+ telemetrySASuffix = "-telemetry"
4245)
4346
4447// reconcileTelemetryDeploymentStage reconciles only the telemetry Deployment (and cleanup).
@@ -49,13 +52,48 @@ func (r *JumpstarterReconciler) reconcileTelemetryDeploymentStage(ctx context.Co
4952 return r .cleanupTelemetry (ctx , jumpstarter )
5053 }
5154
55+ if err := r .reconcileTelemetryServiceAccount (ctx , jumpstarter ); err != nil {
56+ return fmt .Errorf ("failed to reconcile telemetry service account: %w" , err )
57+ }
58+
5259 if err := r .reconcileTelemetryDeployment (ctx , jumpstarter ); err != nil {
5360 return fmt .Errorf ("failed to reconcile telemetry deployment: %w" , err )
5461 }
5562
5663 return nil
5764}
5865
66+ // reconcileTelemetryServiceAccount creates a dedicated, no-RBAC ServiceAccount for
67+ // the telemetry pod. The telemetry binary has no need for Kubernetes API access;
68+ // giving it the controller-manager SA would grant it far more privilege than required.
69+ func (r * JumpstarterReconciler ) reconcileTelemetryServiceAccount (ctx context.Context , jumpstarter * operatorv1alpha1.Jumpstarter ) error {
70+ log := logf .FromContext (ctx )
71+ saName := jumpstarter .Name + telemetrySASuffix
72+
73+ desired := & corev1.ServiceAccount {
74+ ObjectMeta : metav1.ObjectMeta {
75+ Name : saName ,
76+ Namespace : jumpstarter .Namespace ,
77+ Labels : telemetryLabels (jumpstarter ),
78+ },
79+ }
80+
81+ existing := & corev1.ServiceAccount {}
82+ existing .Name = saName
83+ existing .Namespace = jumpstarter .Namespace
84+
85+ op , err := controllerutil .CreateOrUpdate (ctx , r .Client , existing , func () error {
86+ existing .Labels = desired .Labels
87+ return controllerutil .SetControllerReference (jumpstarter , existing , r .Scheme )
88+ })
89+ if err != nil {
90+ return err
91+ }
92+
93+ log .V (1 ).Info ("Telemetry ServiceAccount reconciled" , "name" , saName , "operation" , op )
94+ return nil
95+ }
96+
5997// reconcileTelemetryServiceStage reconciles only the telemetry ClusterIP Service.
6098// It is called from the Services/networking stage of the reconcile loop.
6199func (r * JumpstarterReconciler ) reconcileTelemetryServiceStage (ctx context.Context , jumpstarter * operatorv1alpha1.Jumpstarter ) error {
@@ -73,7 +111,7 @@ func (r *JumpstarterReconciler) reconcileTelemetryServiceStage(ctx context.Conte
73111// reconcileTelemetryDeployment creates or updates the telemetry Deployment.
74112func (r * JumpstarterReconciler ) reconcileTelemetryDeployment (ctx context.Context , jumpstarter * operatorv1alpha1.Jumpstarter ) error {
75113 log := logf .FromContext (ctx )
76- desiredDeployment := r . createTelemetryDeployment (jumpstarter )
114+ desiredDeployment := createTelemetryDeployment (jumpstarter )
77115
78116 existingDeployment := & appsv1.Deployment {}
79117 existingDeployment .Name = desiredDeployment .Name
@@ -175,6 +213,7 @@ func (r *JumpstarterReconciler) reconcileTelemetryService(ctx context.Context, j
175213 }
176214
177215 existingService .Labels = desiredService .Labels
216+ existingService .Spec .Type = desiredService .Spec .Type
178217 existingService .Spec .Selector = desiredService .Spec .Selector
179218 existingService .Spec .Ports = desiredService .Spec .Ports
180219 return controllerutil .SetControllerReference (jumpstarter , existingService , r .Scheme )
@@ -192,7 +231,7 @@ func (r *JumpstarterReconciler) reconcileTelemetryService(ctx context.Context, j
192231}
193232
194233// createTelemetryDeployment builds the desired Deployment for the telemetry service.
195- func ( r * JumpstarterReconciler ) createTelemetryDeployment (jumpstarter * operatorv1alpha1.Jumpstarter ) * appsv1.Deployment {
234+ func createTelemetryDeployment (jumpstarter * operatorv1alpha1.Jumpstarter ) * appsv1.Deployment {
196235 t := jumpstarter .Spec .Telemetry
197236 labels := telemetryLabels (jumpstarter )
198237
@@ -201,51 +240,6 @@ func (r *JumpstarterReconciler) createTelemetryDeployment(jumpstarter *operatorv
201240 replicas = * t .Replicas
202241 }
203242
204- envVars := []corev1.EnvVar {
205- {
206- Name : "CONTROLLER_KEY" ,
207- ValueFrom : & corev1.EnvVarSource {
208- SecretKeyRef : & corev1.SecretKeySelector {
209- LocalObjectReference : corev1.LocalObjectReference {
210- Name : "jumpstarter-controller-secret" ,
211- },
212- Key : "key" ,
213- },
214- },
215- },
216- }
217-
218- var volumeMounts []corev1.VolumeMount
219- var volumes []corev1.Volume
220-
221- // Add TLS certificate mount when cert-manager is enabled
222- var tlsSecretName string
223- if jumpstarter .Spec .CertManager .Enabled {
224- tlsSecretName = GetTelemetryCertSecretName (jumpstarter )
225- }
226-
227- if tlsSecretName != "" {
228- envVars = append (envVars ,
229- corev1.EnvVar {Name : "EXTERNAL_CERT_PEM" , Value : "/tls/tls.crt" },
230- corev1.EnvVar {Name : "EXTERNAL_KEY_PEM" , Value : "/tls/tls.key" },
231- )
232- volumeMounts = append (volumeMounts , corev1.VolumeMount {
233- Name : "tls-certs" ,
234- MountPath : "/tls" ,
235- ReadOnly : true ,
236- })
237- defaultMode := int32 (420 )
238- volumes = append (volumes , corev1.Volume {
239- Name : "tls-certs" ,
240- VolumeSource : corev1.VolumeSource {
241- Secret : & corev1.SecretVolumeSource {
242- SecretName : tlsSecretName ,
243- DefaultMode : & defaultMode ,
244- },
245- },
246- })
247- }
248-
249243 return & appsv1.Deployment {
250244 ObjectMeta : metav1.ObjectMeta {
251245 Name : fmt .Sprintf ("%s-telemetry" , jumpstarter .Name ),
@@ -283,8 +277,6 @@ func (r *JumpstarterReconciler) createTelemetryDeployment(jumpstarter *operatorv
283277 Args : []string {
284278 fmt .Sprintf ("--grpc-bind=:%d" , telemetryPort ),
285279 },
286- Env : envVars ,
287- VolumeMounts : volumeMounts ,
288280 Ports : []corev1.ContainerPort {
289281 {
290282 ContainerPort : int32 (telemetryPort ),
@@ -327,23 +319,22 @@ func (r *JumpstarterReconciler) createTelemetryDeployment(jumpstarter *operatorv
327319 },
328320 },
329321 },
330- Volumes : volumes ,
331322 SecurityContext : & corev1.PodSecurityContext {
332323 RunAsNonRoot : boolPtr (true ),
333324 SeccompProfile : & corev1.SeccompProfile {
334325 Type : corev1 .SeccompProfileTypeRuntimeDefault ,
335326 },
336327 },
337- ServiceAccountName : fmt . Sprintf ( "%s-controller-manager" , jumpstarter .Name ) ,
328+ ServiceAccountName : jumpstarter .Name + telemetrySASuffix ,
338329 },
339330 },
340331 },
341332 }
342333}
343334
344335// cleanupTelemetry removes telemetry resources when telemetry is disabled.
345- // Owned resources (Deployment, Service) are deleted; the CR's garbage collection
346- // will handle removing any cert-manager Certificate .
336+ // GC cannot remove the cert-manager Certificate while the Jumpstarter CR still
337+ // exists (GC only fires when the owner is deleted), so it is deleted explicitly .
347338func (r * JumpstarterReconciler ) cleanupTelemetry (ctx context.Context , jumpstarter * operatorv1alpha1.Jumpstarter ) error {
348339 log := logf .FromContext (ctx )
349340
@@ -368,11 +359,31 @@ func (r *JumpstarterReconciler) cleanupTelemetry(ctx context.Context, jumpstarte
368359 log .Info ("Deleted telemetry service" , "name" , telemetryServiceName )
369360 }
370361
362+ certName := getTelemetryCertSecretName (jumpstarter )
363+ cert := & certmanagerv1.Certificate {}
364+ cert .Name = certName
365+ cert .Namespace = jumpstarter .Namespace
366+ if err := r .Delete (ctx , cert ); err != nil && ! errors .IsNotFound (err ) && ! meta .IsNoMatchError (err ) {
367+ return fmt .Errorf ("failed to delete telemetry certificate: %w" , err )
368+ } else if err == nil {
369+ log .Info ("Deleted telemetry certificate" , "name" , certName )
370+ }
371+
372+ saName := jumpstarter .Name + telemetrySASuffix
373+ sa := & corev1.ServiceAccount {}
374+ sa .Name = saName
375+ sa .Namespace = jumpstarter .Namespace
376+ if err := r .Delete (ctx , sa ); err != nil && ! errors .IsNotFound (err ) {
377+ return fmt .Errorf ("failed to delete telemetry service account: %w" , err )
378+ } else if err == nil {
379+ log .Info ("Deleted telemetry service account" , "name" , saName )
380+ }
381+
371382 return nil
372383}
373384
374- // GetTelemetryCertSecretName returns the name of the telemetry TLS secret.
375- func GetTelemetryCertSecretName (js * operatorv1alpha1.Jumpstarter ) string {
385+ // getTelemetryCertSecretName returns the name of the telemetry TLS secret.
386+ func getTelemetryCertSecretName (js * operatorv1alpha1.Jumpstarter ) string {
376387 return js .Name + telemetryCertSuffix
377388}
378389
0 commit comments