-
Notifications
You must be signed in to change notification settings - Fork 35
CI: add e2e test for jumpstarter-telemetry #1027
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| # CI-only runtime image for the jumpstarter-telemetry binary. | ||
| FROM registry.access.redhat.com/ubi9/ubi-micro:9.8-1786321990@sha256:7e7f79ab747bf2b452e3043dd89f388e92be4c7fdcc8b815b58adf6c99c39c95 | ||
| WORKDIR / | ||
| COPY telemetry /telemetry | ||
| USER 65532:65532 | ||
| ENTRYPOINT ["/telemetry"] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -188,6 +188,13 @@ func (r *JumpstarterReconciler) Reconcile(ctx context.Context, req ctrl.Request) | |
| return ctrl.Result{}, err | ||
| } | ||
|
|
||
| // Ensure signing secrets exist before any Deployment that references them | ||
| // (CONTROLLER_KEY on controller/telemetry, ROUTER_KEY on router). | ||
| if err := r.reconcileSecrets(ctx, &jumpstarter); err != nil { | ||
| log.Error(err, "Failed to reconcile Secrets") | ||
| return ctrl.Result{}, err | ||
| } | ||
|
|
||
| // Build the desired controller ConfigMap once and compute its hash up front. | ||
| // The hash is embedded in the controller pod template annotation so that a config | ||
| // change (e.g. OIDC CA rotation) triggers a rolling restart without waiting for the | ||
|
|
@@ -242,18 +249,12 @@ func (r *JumpstarterReconciler) Reconcile(ctx context.Context, req ctrl.Request) | |
| return ctrl.Result{}, err | ||
| } | ||
|
|
||
| // Reconcile ConfigMaps (after deployments and services, before secrets) | ||
| // Reconcile ConfigMaps (after deployments and services) | ||
| if err := r.reconcileConfigMaps(ctx, &jumpstarter, desiredConfigMap); err != nil { | ||
| log.Error(err, "Failed to reconcile ConfigMaps") | ||
| return ctrl.Result{}, err | ||
| } | ||
|
|
||
| // Reconcile Secrets | ||
| if err := r.reconcileSecrets(ctx, &jumpstarter); err != nil { | ||
| log.Error(err, "Failed to reconcile Secrets") | ||
| return ctrl.Result{}, err | ||
| } | ||
|
|
||
| // Update status | ||
| if err := r.updateStatus(ctx, &jumpstarter); err != nil { | ||
| log.Error(err, "Failed to update status") | ||
|
|
@@ -1322,7 +1323,6 @@ func (r *JumpstarterReconciler) buildConfig(ctx context.Context, jumpstarter *op | |
| } | ||
|
|
||
| // Telemetry configuration. | ||
| // Certificate is intentionally omitted until the telemetry binary supports TLS serving. | ||
| if jumpstarter.Spec.Telemetry != nil && jumpstarter.Spec.Telemetry.Enabled { | ||
| t := jumpstarter.Spec.Telemetry | ||
| telemetryCfg := &config.Telemetry{ | ||
|
|
@@ -1332,6 +1332,15 @@ func (r *JumpstarterReconciler) buildConfig(ctx context.Context, jumpstarter *op | |
| if t.Logging.Filter.MinSeverity != "" { | ||
| telemetryCfg.Logging.Filter.MinSeverity = t.Logging.Filter.MinSeverity | ||
| } | ||
| if jumpstarter.Spec.CertManager.Enabled { | ||
| ca, err := r.resolveTelemetryCA(ctx, jumpstarter) | ||
| if err != nil { | ||
| return config.Config{}, fmt.Errorf("resolve telemetry CA: %w", err) | ||
| } | ||
| // Empty ca is valid for public external issuers (system trust). Self-signed | ||
| // mode returns an error from resolveTelemetryCA until the CA secret is ready. | ||
| telemetryCfg.Certificate = ca | ||
| } | ||
|
Comment on lines
+1335
to
+1343
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift 🔎 Supported by static analysis🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- applicable repository guidance ---'
find /tmp/coderabbit-repo-knowledge/jumpstarter-dev-jumpstarter-45622a71 -type f -name '*.md' -maxdepth 3 -print
printf '%s\n' '--- project structure ---'
cat project-structure.md
printf '%s\n' '--- target symbols ---'
rg -n -C 8 'resolveTelemetryCA|buildConfig|IssuerRef|CABundle|Telemetry|Certificate' controller/deploy/operator/internal/controller/jumpstarter/jumpstarter_controller.go
printf '%s\n' '--- directly bound config definitions ---'
rg -n -C 8 'type Config struct|type Telemetry|Certificate' controller/deploy/operator -g '*.go'Repository: jumpstarter-dev/jumpstarter Length of output: 3978 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- project structure guide ---'
find . /tmp/coderabbit-repo-knowledge/jumpstarter-dev-jumpstarter-45622a71 -name 'project-structure.md' -print
for f in \
/tmp/coderabbit-repo-knowledge/jumpstarter-dev-jumpstarter-45622a71/conventions/controller-deploy-operator-internal-controller-j.md \
/tmp/coderabbit-repo-knowledge/jumpstarter-dev-jumpstarter-45622a71/conventions/controller-deploy-operator.md \
/tmp/coderabbit-repo-knowledge/jumpstarter-dev-jumpstarter-45622a71/conventions/controller-deploy-operator-api-v1alpha1.md
do
printf '\n--- %s ---\n' "$f"
cat "$f"
done
printf '%s\n' '--- target file outline ---'
ast-grep outline controller/deploy/operator/internal/controller/jumpstarter/jumpstarter_controller.go
printf '%s\n' '--- telemetry and config references ---'
rg -n -C 12 'resolveTelemetryCA|buildConfig|IssuerRef|CABundle|Telemetry|Certificate' controller/deploy/operator/internal/controller/jumpstarter/jumpstarter_controller.go
printf '%s\n' '--- config definitions and telemetry consumers ---'
rg -n -C 10 'type Config struct|type Telemetry|Certificate' controller/deploy/operator -g '*.go'Repository: jumpstarter-dev/jumpstarter Length of output: 50384 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- project structure guide ---'
cat .claude/rules/project-structure.md
printf '%s\n' '--- buildConfig and resolver ---'
sed -n '1220,1450p' controller/deploy/operator/internal/controller/jumpstarter/jumpstarter_controller.go
printf '%s\n' '--- resolver definition and call sites ---'
rg -n -C 20 'func \(r \*JumpstarterReconciler\) resolveTelemetryCA|resolveTelemetryCA\(' controller/deploy/operator/internal/controller/jumpstarter -g '*.go'
printf '%s\n' '--- cert-manager server and issuer types ---'
sed -n '798,880p' controller/deploy/operator/api/v1alpha1/jumpstarter_types.go
printf '%s\n' '--- telemetry tests around CA resolution ---'
sed -n '540,730p' controller/deploy/operator/internal/controller/jumpstarter/telemetry_test.go
printf '%s\n' '--- internal telemetry config declaration and serialization tags ---'
rg -n -C 12 'type Telemetry struct|Certificate.*yaml|Certificate.*json' controller packages -g '*.go' 2>/dev/null || trueRepository: jumpstarter-dev/jumpstarter Length of output: 50385 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- config import ---'
sed -n '1,58p' controller/deploy/operator/internal/controller/jumpstarter/jumpstarter_controller.go
printf '%s\n' '--- telemetry config declarations ---'
rg -n -C 10 'type Telemetry struct|Certificate .*yaml|certificate.*omitempty' python controller protocol -g '*.py' -g '*.go' -g '*.yaml' -g '*.yml' | head -240
printf '%s\n' '--- telemetry certificate consumers ---'
rg -n -C 10 'telemetry.*certificate|Certificate.*telemetry|certificate.*Telemetry|TLS.*telemetry|telemetry.*TLS|ca.*bundle|ca_bundle' python controller -g '*.py' -g '*.go' | head -320Repository: jumpstarter-dev/jumpstarter Length of output: 47144 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- telemetry deployment construction ---'
sed -n '1,230p' controller/deploy/operator/internal/controller/jumpstarter/telemetry.go
printf '%s\n' '--- telemetry service TLS loading ---'
sed -n '55,85p' controller/internal/service/telemetry_service.go
sed -n '218,275p' controller/internal/service/telemetry_service.go
printf '%s\n' '--- shared TLS credential loader ---'
rg -n -C 18 'func LoadTLSCredentials|EXTERNAL_CERT_PEM|EXTERNAL_KEY_PEM' controller -g '*.go'
printf '%s\n' '--- telemetry endpoint client construction ---'
rg -n -C 15 'TelemetryEndpoint|telemetry.*Certificate|certificate.*telemetry|GetServiceEndpoints' controller python -g '*.go' -g '*.py' | head -360Repository: jumpstarter-dev/jumpstarter Length of output: 50384 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- telemetry Deployment template ---'
rg -n 'func createTelemetryDeployment|EXTERNAL_CERT_PEM|EXTERNAL_KEY_PEM|telemetryCertSuffix|Volumes:|VolumeMounts:|Env:' controller/deploy/operator/internal/controller/jumpstarter/telemetry.go
sed -n '250,390p' controller/deploy/operator/internal/controller/jumpstarter/telemetry.go
printf '%s\n' '--- controller endpoint advertisement ---'
rg -n -C 18 'GetServiceEndpoints|TelemetryEndpoints|telemetry.Certificate|cfg.Telemetry|Certificate:' controller/internal -g '*.go'
printf '%s\n' '--- exporter/client TLS handling for endpoint certificates ---'
rg -n -C 14 'Certificate.*Endpoint|Endpoint.*Certificate|x509.NewCertPool|AppendCertsFromPEM|RootCAs|WithTransportCredentials|credentials.NewTLS' controller/internal -g '*.go'Repository: jumpstarter-dev/jumpstarter Length of output: 50384 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- telemetry Certificate reconciliation ---'
rg -n -C 18 'Telemetry|telemetry.*Certificate|Certificate.*telemetry|telemetryCertSuffix|IssuerRef' controller/deploy/operator/internal/controller/jumpstarter/certificates.go controller/deploy/operator/internal/controller/jumpstarter/telemetry.go
printf '%s\n' '--- GetServiceEndpoints implementation ---'
rg -l 'GetServiceEndpoints' controller/internal --glob '*.go' | grep -v '_pb.go' | xargs -r -n1 sh -c 'echo "--- $0 ---"; rg -n -C 24 "GetServiceEndpoints|TelemetryEndpoints|TelemetryEndpoint" "$0"'
printf '%s\n' '--- client use of advertised telemetry certificate ---'
rg -l 'TelemetryEndpoint|telemetry_endpoints|certificate' python/packages controller/internal --glob '*.py' --glob '*.go' | grep -v -E '(_pb\\.go|protocol)' | head -80Repository: jumpstarter-dev/jumpstarter Length of output: 50384 Prevent telemetry TLS misconfiguration. When an external 🤖 Prompt for AI AgentsSource: Coding guidelines |
||
| cfg.Telemetry = telemetryCfg | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -273,6 +273,31 @@ func createTelemetryDeployment(jumpstarter *operatorv1alpha1.Jumpstarter) *appsv | |
| replicas = *t.Replicas | ||
| } | ||
|
|
||
| var tlsEnv []corev1.EnvVar | ||
| var volumeMounts []corev1.VolumeMount | ||
| var volumes []corev1.Volume | ||
| if jumpstarter.Spec.CertManager.Enabled { | ||
| tlsEnv = []corev1.EnvVar{ | ||
| {Name: "EXTERNAL_CERT_PEM", Value: "/tls/tls.crt"}, | ||
| {Name: "EXTERNAL_KEY_PEM", Value: "/tls/tls.key"}, | ||
| } | ||
| defaultMode := int32(420) | ||
| volumeMounts = []corev1.VolumeMount{{ | ||
| Name: "tls-certs", | ||
| MountPath: "/tls", | ||
| ReadOnly: true, | ||
| }} | ||
| volumes = []corev1.Volume{{ | ||
| Name: "tls-certs", | ||
| VolumeSource: corev1.VolumeSource{ | ||
| Secret: &corev1.SecretVolumeSource{ | ||
| SecretName: getTelemetryCertSecretName(jumpstarter), | ||
| DefaultMode: &defaultMode, | ||
| }, | ||
| }, | ||
| }} | ||
|
Comment on lines
+290
to
+298
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: #!/bin/bash
set -euo pipefail
ast-grep outline controller/internal/service/telemetry_service.go --items all
rg -n -C 8 'EXTERNAL_CERT_PEM|EXTERNAL_KEY_PEM|LoadTLSCredentials|LoadX509KeyPair|GetCertificate|GetConfigForClient|fsnotify' \
controller/internal/service/telemetry_service.go controller/internal
rg -n -C 8 'getControllerTLSSecretHash|TLSSecretHash|reconcileTelemetryDeployment' \
controller/deploy/operator/internal/controller/jumpstarterRepository: jumpstarter-dev/jumpstarter Length of output: 50383 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- repository guidance ---'
find /tmp/coderabbit-repo-knowledge/jumpstarter-dev-jumpstarter-45622a71 -maxdepth 2 -type f -name '*.md' -print
printf '%s\n' '--- project structure ---'
cat -n project-structure.md
printf '%s\n' '--- telemetry startup and TLS loading ---'
sed -n '210,315p' controller/internal/service/telemetry_service.go
sed -n '29,100p' controller/internal/service/tls_credentials.go
printf '%s\n' '--- telemetry Deployment construction and reconciliation ---'
sed -n '125,225p' controller/deploy/operator/internal/controller/jumpstarter/telemetry.go
sed -n '270,415p' controller/deploy/operator/internal/controller/jumpstarter/telemetry.go
printf '%s\n' '--- certificate reconciliation and existing hash pattern ---'
rg -n -C 12 'telemetryCertSuffix|Certificate|SecretHash|hash|checksum|cert-manager|CertificateRequest' \
controller/deploy/operator/internal/controller/jumpstarter/certificates.go \
controller/deploy/operator/internal/controller/jumpstarter/*.goRepository: jumpstarter-dev/jumpstarter Length of output: 3967 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- project structure locations ---'
find . -name 'project-structure.md' -print
printf '%s\n' '--- applicable repository conventions ---'
cat /tmp/coderabbit-repo-knowledge/jumpstarter-dev-jumpstarter-45622a71/conventions/repo-wide.md
cat /tmp/coderabbit-repo-knowledge/jumpstarter-dev-jumpstarter-45622a71/conventions/controller-deploy-operator-internal-controller-j.md
printf '%s\n' '--- telemetry startup and TLS loading ---'
sed -n '210,315p' controller/internal/service/telemetry_service.go
sed -n '29,100p' controller/internal/service/tls_credentials.go
printf '%s\n' '--- telemetry Deployment construction and reconciliation ---'
sed -n '125,225p' controller/deploy/operator/internal/controller/jumpstarter/telemetry.go
sed -n '270,415p' controller/deploy/operator/internal/controller/jumpstarter/telemetry.go
printf '%s\n' '--- certificate reconciliation and existing hash pattern ---'
rg -n -C 12 'telemetryCertSuffix|SecretHash|hash|checksum|cert-manager|CertificateRequest' \
controller/deploy/operator/internal/controller/jumpstarter/certificates.go \
controller/deploy/operator/internal/controller/jumpstarter/*.goRepository: jumpstarter-dev/jumpstarter Length of output: 50384 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- project structure ---'
cat -n .claude/rules/project-structure.md
printf '%s\n' '--- hash helpers and annotation builders ---'
rg -n -C 18 'func secretDataHash|func buildControllerPodAnnotations|func buildRouterPodAnnotations|tls-secret-sha256' \
controller/deploy/operator/internal/controller/jumpstarter
printf '%s\n' '--- telemetry deployment function boundaries ---'
rg -n 'func createTelemetryDeployment|func getTelemetryCertSecretName|func telemetryEndpointFor' \
controller/deploy/operator/internal/controller/jumpstarter/telemetry.go
sed -n '230,310p' controller/deploy/operator/internal/controller/jumpstarter/telemetry.go
printf '%s\n' '--- telemetry Certificate resource ---'
rg -n -C 20 'getTelemetryCertSecretName|telemetryCertSuffix|Certificate\{' \
controller/deploy/operator/internal/controller/jumpstarter/certificates.go \
controller/deploy/operator/internal/controller/jumpstarter/*.goRepository: jumpstarter-dev/jumpstarter Length of output: 50386 Restart telemetry pods after TLS Secret rotation.
🤖 Prompt for AI Agents |
||
| } | ||
|
|
||
| return &appsv1.Deployment{ | ||
| ObjectMeta: metav1.ObjectMeta{ | ||
| Name: fmt.Sprintf("%s-telemetry", jumpstarter.Name), | ||
|
|
@@ -310,7 +335,7 @@ func createTelemetryDeployment(jumpstarter *operatorv1alpha1.Jumpstarter) *appsv | |
| Args: []string{ | ||
| fmt.Sprintf("--grpc-bind=:%d", telemetryPort), | ||
| }, | ||
| Env: []corev1.EnvVar{ | ||
| Env: append([]corev1.EnvVar{ | ||
| { | ||
| Name: "CONTROLLER_KEY", | ||
| ValueFrom: &corev1.EnvVarSource{ | ||
|
|
@@ -322,7 +347,10 @@ func createTelemetryDeployment(jumpstarter *operatorv1alpha1.Jumpstarter) *appsv | |
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| // Advertised endpoint for self-signed SAN generation (must match controller ConfigMap). | ||
| {Name: "GRPC_TELEMETRY_ENDPOINT", Value: telemetryEndpointFor(jumpstarter.Namespace)}, | ||
| }, tlsEnv...), | ||
| VolumeMounts: volumeMounts, | ||
| Ports: []corev1.ContainerPort{ | ||
| { | ||
| ContainerPort: int32(telemetryPort), | ||
|
|
@@ -371,6 +399,7 @@ func createTelemetryDeployment(jumpstarter *operatorv1alpha1.Jumpstarter) *appsv | |
| Type: corev1.SeccompProfileTypeRuntimeDefault, | ||
| }, | ||
| }, | ||
| Volumes: volumes, | ||
| ServiceAccountName: jumpstarter.Name + telemetrySASuffix, | ||
| }, | ||
| }, | ||
|
|
@@ -425,14 +454,16 @@ func getTelemetryCertSecretName(js *operatorv1alpha1.Jumpstarter) string { | |
| } | ||
|
|
||
| // resolveTelemetryCA reads the CA certificate that exporters need to verify the | ||
| // telemetry TLS connection. For self-signed CA mode, the cert is in the CA secret; | ||
| // for external issuers, the user-provided caBundle is used. | ||
| // telemetry TLS connection. For self-signed CA mode, the cert is in the CA secret. | ||
| // For external issuers, the user-provided caBundle is preferred; when absent, ca.crt | ||
| // from the issued telemetry TLS secret is used if present. An empty return with no | ||
| // error means exporters should rely on the system trust store (public CA issuers). | ||
| func (r *JumpstarterReconciler) resolveTelemetryCA(ctx context.Context, jumpstarter *operatorv1alpha1.Jumpstarter) (string, error) { | ||
| if jumpstarter.Spec.CertManager.Server != nil && jumpstarter.Spec.CertManager.Server.IssuerRef != nil { | ||
| if len(jumpstarter.Spec.CertManager.Server.IssuerRef.CABundle) > 0 { | ||
| return string(jumpstarter.Spec.CertManager.Server.IssuerRef.CABundle), nil | ||
| } | ||
| return "", nil | ||
| return r.telemetryCAFromCertSecret(ctx, jumpstarter) | ||
| } | ||
|
|
||
| // Self-signed CA mode — read from the CA secret created by cert-manager | ||
|
|
@@ -447,6 +478,27 @@ func (r *JumpstarterReconciler) resolveTelemetryCA(ctx context.Context, jumpstar | |
| return "", fmt.Errorf("CA secret %s missing tls.crt", caSecretName) | ||
| } | ||
|
|
||
| // telemetryCAFromCertSecret returns ca.crt from the issued telemetry TLS secret, | ||
| // when cert-manager includes it. Missing secret or key is not an error: external | ||
| // issuers backed by public CAs may not need an explicit bundle in the controller config. | ||
| func (r *JumpstarterReconciler) telemetryCAFromCertSecret(ctx context.Context, jumpstarter *operatorv1alpha1.Jumpstarter) (string, error) { | ||
| secret := &corev1.Secret{} | ||
| err := r.Get(ctx, client.ObjectKey{ | ||
| Name: getTelemetryCertSecretName(jumpstarter), | ||
| Namespace: jumpstarter.Namespace, | ||
| }, secret) | ||
| if err != nil { | ||
| if errors.IsNotFound(err) { | ||
| return "", nil | ||
| } | ||
| return "", fmt.Errorf("telemetry TLS secret not found: %w", err) | ||
| } | ||
| if ca, ok := secret.Data["ca.crt"]; ok && len(ca) > 0 { | ||
| return string(ca), nil | ||
| } | ||
| return "", nil | ||
| } | ||
|
|
||
| // telemetryEndpointFor returns the in-cluster gRPC endpoint for the telemetry service. | ||
| func telemetryEndpointFor(namespace string) string { | ||
| return fmt.Sprintf("%s.%s.svc:%d", telemetryServiceName, namespace, telemetryPort) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Restore the prescribed reconciliation order.
reconcileSecretsnow runs before deployment reconciliation. The required sequence placesreconcileSecretsafterreconcileConfigMaps. Restore that sequence, or update the repository rule with an explicit exception.As per coding guidelines, “The reconcile loop must follow this order: fetch CR, apply runtime defaults, reconcile RBAC, reconcile Controller Deployment, reconcile Router Deployments, reconcile Services/networking, reconcile ConfigMaps, reconcile Secrets, update status.”
🤖 Prompt for AI Agents
Source: Coding guidelines