Skip to content

Commit 5ee58d7

Browse files
committed
fix: address code review comments for telemetry operator
Signed-off-by: Bella Khizgiyaev <bkhizgiy@redhat.com> Assisted-by: claude-4.6-opus
1 parent d9deed6 commit 5ee58d7

6 files changed

Lines changed: 252 additions & 179 deletions

File tree

controller/deploy/operator/internal/controller/jumpstarter/certificates.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ func (r *JumpstarterReconciler) reconcileRouterCertificate(ctx context.Context,
378378

379379
// reconcileTelemetryCertificate creates the TLS certificate for the telemetry service.
380380
func (r *JumpstarterReconciler) reconcileTelemetryCertificate(ctx context.Context, js *operatorv1alpha1.Jumpstarter, issuerRef cmmeta.ObjectReference) error {
381-
certName := GetTelemetryCertSecretName(js)
381+
certName := getTelemetryCertSecretName(js)
382382
includeInternalNames := !isExternalIssuer(js)
383383
dnsNames := r.collectTelemetryDNSNames(js, includeInternalNames)
384384
return r.reconcileServerCertificate(ctx, js, issuerRef, certName, "telemetry", dnsNames, nil)

controller/deploy/operator/internal/controller/jumpstarter/jumpstarter_controller.go

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -863,14 +863,6 @@ func (r *JumpstarterReconciler) createControllerDeployment(jumpstarter *operator
863863
},
864864
}
865865

866-
// Add telemetry endpoint env var when telemetry is enabled
867-
if jumpstarter.Spec.Telemetry != nil && jumpstarter.Spec.Telemetry.Enabled {
868-
envVars = append(envVars, corev1.EnvVar{
869-
Name: "GRPC_TELEMETRY_ENDPOINT",
870-
Value: telemetryEndpointFor(jumpstarter.Namespace),
871-
})
872-
}
873-
874866
var volumeMounts []corev1.VolumeMount
875867
var volumes []corev1.Volume
876868

@@ -1312,7 +1304,8 @@ func (r *JumpstarterReconciler) buildConfig(ctx context.Context, jumpstarter *op
13121304
Keys: jumpstarter.Spec.DeprecatedLabels.Keys,
13131305
}
13141306

1315-
// Telemetry configuration
1307+
// Telemetry configuration.
1308+
// Certificate is intentionally omitted until the telemetry binary supports TLS serving.
13161309
if jumpstarter.Spec.Telemetry != nil && jumpstarter.Spec.Telemetry.Enabled {
13171310
t := jumpstarter.Spec.Telemetry
13181311
telemetryCfg := &config.Telemetry{
@@ -1322,17 +1315,6 @@ func (r *JumpstarterReconciler) buildConfig(ctx context.Context, jumpstarter *op
13221315
if t.Logging.Filter.MinSeverity != "" {
13231316
telemetryCfg.Logging.Filter.MinSeverity = t.Logging.Filter.MinSeverity
13241317
}
1325-
// Resolve the CA certificate for TLS verification by exporters.
1326-
// When cert-manager is enabled in self-signed mode, read it from the CA secret.
1327-
// When an external issuer is used, use its CA bundle if provided.
1328-
if jumpstarter.Spec.CertManager.Enabled {
1329-
caCert, err := r.resolveTelemetryCA(ctx, jumpstarter)
1330-
if err != nil {
1331-
logf.FromContext(ctx).V(1).Info("Telemetry CA not yet available, will retry", "error", err)
1332-
} else if caCert != "" {
1333-
telemetryCfg.Certificate = caCert
1334-
}
1335-
}
13361318
cfg.Telemetry = telemetryCfg
13371319
}
13381320

controller/deploy/operator/internal/controller/jumpstarter/suite_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,9 @@ var _ = BeforeSuite(func() {
7070

7171
By("bootstrapping test environment")
7272
testEnv = &envtest.Environment{
73-
CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crd", "bases")},
73+
CRDDirectoryPaths: []string{
74+
filepath.Join("..", "..", "..", "config", "crd", "bases"),
75+
},
7476
ErrorIfCRDPathMissing: true,
7577
}
7678

controller/deploy/operator/internal/controller/jumpstarter/telemetry.go

Lines changed: 112 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -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,18 +52,53 @@ 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.
6199
func (r *JumpstarterReconciler) reconcileTelemetryServiceStage(ctx context.Context, jumpstarter *operatorv1alpha1.Jumpstarter) error {
62100
if jumpstarter.Spec.Telemetry == nil || !jumpstarter.Spec.Telemetry.Enabled {
63-
return nil
101+
return r.cleanupTelemetryService(ctx, jumpstarter)
64102
}
65103

66104
if err := r.reconcileTelemetryService(ctx, jumpstarter); err != nil {
@@ -70,10 +108,28 @@ func (r *JumpstarterReconciler) reconcileTelemetryServiceStage(ctx context.Conte
70108
return nil
71109
}
72110

111+
// cleanupTelemetryService removes only the telemetry Service when telemetry is disabled.
112+
// This provides symmetric cleanup so the Service isn't orphaned if the reconcile loop
113+
// order changes (e.g. services reconciled before deployments).
114+
func (r *JumpstarterReconciler) cleanupTelemetryService(ctx context.Context, jumpstarter *operatorv1alpha1.Jumpstarter) error {
115+
log := logf.FromContext(ctx)
116+
117+
svc := &corev1.Service{}
118+
svc.Name = telemetryServiceName
119+
svc.Namespace = jumpstarter.Namespace
120+
if err := r.Delete(ctx, svc); err != nil && !errors.IsNotFound(err) {
121+
return fmt.Errorf("failed to delete telemetry service: %w", err)
122+
} else if err == nil {
123+
log.Info("Deleted telemetry service", "name", telemetryServiceName)
124+
}
125+
126+
return nil
127+
}
128+
73129
// reconcileTelemetryDeployment creates or updates the telemetry Deployment.
74130
func (r *JumpstarterReconciler) reconcileTelemetryDeployment(ctx context.Context, jumpstarter *operatorv1alpha1.Jumpstarter) error {
75131
log := logf.FromContext(ctx)
76-
desiredDeployment := r.createTelemetryDeployment(jumpstarter)
132+
desiredDeployment := createTelemetryDeployment(jumpstarter)
77133

78134
existingDeployment := &appsv1.Deployment{}
79135
existingDeployment.Name = desiredDeployment.Name
@@ -119,8 +175,14 @@ func (r *JumpstarterReconciler) reconcileTelemetryDeployment(ctx context.Context
119175
return err
120176
}
121177

122-
log.Info("Telemetry deployment reconciled",
123-
"name", existingDeployment.Name, "namespace", existingDeployment.Namespace, "operation", op)
178+
switch op {
179+
case controllerutil.OperationResultNone:
180+
log.V(1).Info("Telemetry deployment is up to date",
181+
"name", existingDeployment.Name, "namespace", existingDeployment.Namespace)
182+
default:
183+
log.Info("Telemetry deployment reconciled",
184+
"name", existingDeployment.Name, "namespace", existingDeployment.Namespace, "operation", op)
185+
}
124186

125187
switch op {
126188
case controllerutil.OperationResultCreated:
@@ -166,15 +228,8 @@ func (r *JumpstarterReconciler) reconcileTelemetryService(ctx context.Context, j
166228
existingService.Namespace = desiredService.Namespace
167229

168230
op, err := controllerutil.CreateOrUpdate(ctx, r.Client, existingService, func() error {
169-
if existingService.CreationTimestamp.IsZero() {
170-
existingService.Labels = desiredService.Labels
171-
existingService.Spec.Type = desiredService.Spec.Type
172-
existingService.Spec.Selector = desiredService.Spec.Selector
173-
existingService.Spec.Ports = desiredService.Spec.Ports
174-
return controllerutil.SetControllerReference(jumpstarter, existingService, r.Scheme)
175-
}
176-
177231
existingService.Labels = desiredService.Labels
232+
existingService.Spec.Type = desiredService.Spec.Type
178233
existingService.Spec.Selector = desiredService.Spec.Selector
179234
existingService.Spec.Ports = desiredService.Spec.Ports
180235
return controllerutil.SetControllerReference(jumpstarter, existingService, r.Scheme)
@@ -185,14 +240,29 @@ func (r *JumpstarterReconciler) reconcileTelemetryService(ctx context.Context, j
185240
return err
186241
}
187242

188-
log.Info("Telemetry service reconciled",
189-
"name", existingService.Name, "namespace", existingService.Namespace, "operation", op)
243+
switch op {
244+
case controllerutil.OperationResultNone:
245+
log.V(1).Info("Telemetry service is up to date",
246+
"name", existingService.Name, "namespace", existingService.Namespace)
247+
case controllerutil.OperationResultCreated:
248+
log.Info("Telemetry service reconciled",
249+
"name", existingService.Name, "namespace", existingService.Namespace, "operation", op)
250+
r.emitEventf(jumpstarter, corev1.EventTypeNormal, "TelemetryServiceCreated",
251+
"Telemetry service created: name=%s namespace=%s",
252+
existingService.Name, existingService.Namespace)
253+
case controllerutil.OperationResultUpdated:
254+
log.Info("Telemetry service reconciled",
255+
"name", existingService.Name, "namespace", existingService.Namespace, "operation", op)
256+
r.emitEventf(jumpstarter, corev1.EventTypeNormal, "TelemetryServiceUpdated",
257+
"Telemetry service updated: name=%s namespace=%s",
258+
existingService.Name, existingService.Namespace)
259+
}
190260

191261
return nil
192262
}
193263

194264
// createTelemetryDeployment builds the desired Deployment for the telemetry service.
195-
func (r *JumpstarterReconciler) createTelemetryDeployment(jumpstarter *operatorv1alpha1.Jumpstarter) *appsv1.Deployment {
265+
func createTelemetryDeployment(jumpstarter *operatorv1alpha1.Jumpstarter) *appsv1.Deployment {
196266
t := jumpstarter.Spec.Telemetry
197267
labels := telemetryLabels(jumpstarter)
198268

@@ -201,51 +271,6 @@ func (r *JumpstarterReconciler) createTelemetryDeployment(jumpstarter *operatorv
201271
replicas = *t.Replicas
202272
}
203273

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-
249274
return &appsv1.Deployment{
250275
ObjectMeta: metav1.ObjectMeta{
251276
Name: fmt.Sprintf("%s-telemetry", jumpstarter.Name),
@@ -283,8 +308,6 @@ func (r *JumpstarterReconciler) createTelemetryDeployment(jumpstarter *operatorv
283308
Args: []string{
284309
fmt.Sprintf("--grpc-bind=:%d", telemetryPort),
285310
},
286-
Env: envVars,
287-
VolumeMounts: volumeMounts,
288311
Ports: []corev1.ContainerPort{
289312
{
290313
ContainerPort: int32(telemetryPort),
@@ -320,30 +343,29 @@ func (r *JumpstarterReconciler) createTelemetryDeployment(jumpstarter *operatorv
320343
TerminationMessagePath: "/dev/termination-log",
321344
TerminationMessagePolicy: corev1.TerminationMessageReadFile,
322345
SecurityContext: &corev1.SecurityContext{
323-
AllowPrivilegeEscalation: boolPtr(false),
346+
AllowPrivilegeEscalation: ptr.To(false),
324347
Capabilities: &corev1.Capabilities{
325348
Drop: []corev1.Capability{"ALL"},
326349
},
327350
},
328351
},
329352
},
330-
Volumes: volumes,
331353
SecurityContext: &corev1.PodSecurityContext{
332-
RunAsNonRoot: boolPtr(true),
354+
RunAsNonRoot: ptr.To(true),
333355
SeccompProfile: &corev1.SeccompProfile{
334356
Type: corev1.SeccompProfileTypeRuntimeDefault,
335357
},
336358
},
337-
ServiceAccountName: fmt.Sprintf("%s-controller-manager", jumpstarter.Name),
359+
ServiceAccountName: jumpstarter.Name + telemetrySASuffix,
338360
},
339361
},
340362
},
341363
}
342364
}
343365

344366
// 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.
367+
// GC cannot remove the cert-manager Certificate while the Jumpstarter CR still
368+
// exists (GC only fires when the owner is deleted), so it is deleted explicitly.
347369
func (r *JumpstarterReconciler) cleanupTelemetry(ctx context.Context, jumpstarter *operatorv1alpha1.Jumpstarter) error {
348370
log := logf.FromContext(ctx)
349371

@@ -368,11 +390,31 @@ func (r *JumpstarterReconciler) cleanupTelemetry(ctx context.Context, jumpstarte
368390
log.Info("Deleted telemetry service", "name", telemetryServiceName)
369391
}
370392

393+
certName := getTelemetryCertSecretName(jumpstarter)
394+
cert := &certmanagerv1.Certificate{}
395+
cert.Name = certName
396+
cert.Namespace = jumpstarter.Namespace
397+
if err := r.Delete(ctx, cert); err != nil && !errors.IsNotFound(err) && !meta.IsNoMatchError(err) {
398+
return fmt.Errorf("failed to delete telemetry certificate: %w", err)
399+
} else if err == nil {
400+
log.Info("Deleted telemetry certificate", "name", certName)
401+
}
402+
403+
saName := jumpstarter.Name + telemetrySASuffix
404+
sa := &corev1.ServiceAccount{}
405+
sa.Name = saName
406+
sa.Namespace = jumpstarter.Namespace
407+
if err := r.Delete(ctx, sa); err != nil && !errors.IsNotFound(err) {
408+
return fmt.Errorf("failed to delete telemetry service account: %w", err)
409+
} else if err == nil {
410+
log.Info("Deleted telemetry service account", "name", saName)
411+
}
412+
371413
return nil
372414
}
373415

374-
// GetTelemetryCertSecretName returns the name of the telemetry TLS secret.
375-
func GetTelemetryCertSecretName(js *operatorv1alpha1.Jumpstarter) string {
416+
// getTelemetryCertSecretName returns the name of the telemetry TLS secret.
417+
func getTelemetryCertSecretName(js *operatorv1alpha1.Jumpstarter) string {
376418
return js.Name + telemetryCertSuffix
377419
}
378420

0 commit comments

Comments
 (0)