From ff64eec58624ad70136ff527c078d4e6af51fcd8 Mon Sep 17 00:00:00 2001 From: Tim Carter Date: Wed, 29 Apr 2026 18:09:29 +1000 Subject: [PATCH] feat: standardize Kueue config and restore trace propagation Align Kueue Helm config with syft-generator. Move ResourceFlavor to sbomer-platform. Restore manual trace context propagation in TaskReconciler. --- .gitignore | 2 +- .../templates/deployment.yaml | 2 +- .../{clusterqueue.yaml => cluster-queue.yaml} | 15 ++--- .../{localqueue.yaml => local-queue.yaml} | 2 +- .../templates/kueue/resourceflavor.yaml | 9 --- helm/java-generator-chart/values.yaml | 21 +++--- .../generator/adapter/in/TaskReconciler.java | 33 ++++++--- .../core/service/TaskRunFactory.java | 5 +- .../generator/core/utility/TraceUtility.java | 67 +++++++++++++++++++ src/main/resources/application.properties | 2 +- 10 files changed, 116 insertions(+), 42 deletions(-) rename helm/java-generator-chart/templates/kueue/{clusterqueue.yaml => cluster-queue.yaml} (54%) rename helm/java-generator-chart/templates/kueue/{localqueue.yaml => local-queue.yaml} (88%) delete mode 100644 helm/java-generator-chart/templates/kueue/resourceflavor.yaml create mode 100644 src/main/java/org/jboss/sbomer/java/generator/core/utility/TraceUtility.java diff --git a/.gitignore b/.gitignore index faafe77..f997de2 100644 --- a/.gitignore +++ b/.gitignore @@ -23,7 +23,7 @@ .DS_Store -syft-generator.iml +java-generator.iml # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* diff --git a/helm/java-generator-chart/templates/deployment.yaml b/helm/java-generator-chart/templates/deployment.yaml index 0412653..4f5f1fa 100644 --- a/helm/java-generator-chart/templates/deployment.yaml +++ b/helm/java-generator-chart/templates/deployment.yaml @@ -58,7 +58,7 @@ spec: value: "service.name=sbomer-java-generator" # Kueue Configuration - {{- if .Values.kueue.includeKueue }} + {{- if .Values.kueue.enabled }} - name: SBOMER_GENERATOR_KUEUE_QUEUE_NAME value: {{ .Values.kueue.localQueue.name | quote }} {{- end }} diff --git a/helm/java-generator-chart/templates/kueue/clusterqueue.yaml b/helm/java-generator-chart/templates/kueue/cluster-queue.yaml similarity index 54% rename from helm/java-generator-chart/templates/kueue/clusterqueue.yaml rename to helm/java-generator-chart/templates/kueue/cluster-queue.yaml index 646dc24..2fbf746 100644 --- a/helm/java-generator-chart/templates/kueue/clusterqueue.yaml +++ b/helm/java-generator-chart/templates/kueue/cluster-queue.yaml @@ -1,4 +1,4 @@ -{{- if .Values.kueue.includeKueue -}} +{{- if .Values.kueue.enabled -}} apiVersion: kueue.x-k8s.io/v1beta1 kind: ClusterQueue metadata: @@ -8,18 +8,15 @@ metadata: spec: namespaceSelector: {} # Allow all namespaces resourceGroups: - - coveredResources: ["cpu", "memory"] + - coveredResources: ["cpu", "memory", "pods"] flavors: - name: {{ .Values.kueue.resourceFlavor.name }} resources: - # CPU quota for all concurrent tasks # Note: Kueue uses resource requests (not limits) for admission control - - name: "cpu" - nominalQuota: {{ .Values.kueue.clusterQueue.quotas.cpu | quote }} - # Memory quota for all concurrent tasks - # Note: Kueue uses resource requests (not limits) for admission control - - name: "memory" - nominalQuota: {{ .Values.kueue.clusterQueue.quotas.memory | quote }} + {{- range .Values.kueue.clusterQueue.resources }} + - name: {{ .name | quote }} + nominalQuota: {{ .nominalQuota }} + {{- end }} # Admission control configuration queueingStrategy: {{ .Values.kueue.clusterQueue.queueingStrategy }} {{- end }} diff --git a/helm/java-generator-chart/templates/kueue/localqueue.yaml b/helm/java-generator-chart/templates/kueue/local-queue.yaml similarity index 88% rename from helm/java-generator-chart/templates/kueue/localqueue.yaml rename to helm/java-generator-chart/templates/kueue/local-queue.yaml index da83adf..bba68b0 100644 --- a/helm/java-generator-chart/templates/kueue/localqueue.yaml +++ b/helm/java-generator-chart/templates/kueue/local-queue.yaml @@ -1,4 +1,4 @@ -{{- if .Values.kueue.includeKueue -}} +{{- if .Values.kueue.enabled -}} apiVersion: kueue.x-k8s.io/v1beta1 kind: LocalQueue metadata: diff --git a/helm/java-generator-chart/templates/kueue/resourceflavor.yaml b/helm/java-generator-chart/templates/kueue/resourceflavor.yaml deleted file mode 100644 index a4317bb..0000000 --- a/helm/java-generator-chart/templates/kueue/resourceflavor.yaml +++ /dev/null @@ -1,9 +0,0 @@ -{{- if .Values.kueue.includeKueue -}} -apiVersion: kueue.x-k8s.io/v1beta1 -kind: ResourceFlavor -metadata: - name: {{ .Values.kueue.resourceFlavor.name }} - labels: - {{- include "java-generator-chart.labels" . | nindent 4 }} -spec: {} -{{- end }} \ No newline at end of file diff --git a/helm/java-generator-chart/values.yaml b/helm/java-generator-chart/values.yaml index f94c542..169e8a3 100644 --- a/helm/java-generator-chart/values.yaml +++ b/helm/java-generator-chart/values.yaml @@ -72,30 +72,35 @@ config: # --- KUEUE CONFIG --- # Kueue is mandatory for queue management kueue: - includeKueue: true + enabled: true # LocalQueue configuration (namespace-scoped) localQueue: - name: "java-generator-queue" + name: java-generator-local-queue # ClusterQueue configuration (cluster-scoped) clusterQueue: - name: "java-generator-cluster-queue" + name: java-generator-cluster-queue queueingStrategy: BestEffortFIFO - quotas: + # Resource quotas for the cluster queue + # Kueue uses resource requests (not limits) for admission control + resources: # Total CPU quota across all task types # Calculated as: maxConcurrent * highest CPU request among task types # Example: 20 pods * 500m (cdxMavenPlugin/domino request) = 10000m - cpu: "10000m" + - name: cpu + nominalQuota: 10000m # Total memory quota across all task types # Calculated as: maxConcurrent * highest memory request among task types # Example: 20 pods * 1000Mi = 20Gi - memory: "20Gi" + - name: memory + nominalQuota: 20Gi # Maximum number of concurrent TaskRuns (across all types) - pods: 20 + - name: pods + nominalQuota: 20 # ResourceFlavor configuration resourceFlavor: - name: "default-flavor" + name: default-flavor # --- TEKTON TASK CONFIG --- task: diff --git a/src/main/java/org/jboss/sbomer/java/generator/adapter/in/TaskReconciler.java b/src/main/java/org/jboss/sbomer/java/generator/adapter/in/TaskReconciler.java index 0c2ad44..936313c 100644 --- a/src/main/java/org/jboss/sbomer/java/generator/adapter/in/TaskReconciler.java +++ b/src/main/java/org/jboss/sbomer/java/generator/adapter/in/TaskReconciler.java @@ -8,6 +8,7 @@ import org.jboss.sbomer.java.generator.core.port.api.GenerationOrchestrator; import org.jboss.sbomer.java.generator.core.port.spi.FailureNotifier; import org.jboss.sbomer.java.generator.core.utility.FailureUtility; +import org.jboss.sbomer.java.generator.core.utility.TraceUtility; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; @@ -19,7 +20,8 @@ import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.StatusCode; -import io.opentelemetry.instrumentation.annotations.WithSpan; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Scope; import jakarta.inject.Inject; import lombok.extern.slf4j.Slf4j; @@ -36,28 +38,39 @@ public class TaskReconciler implements Reconciler { @Inject ObjectMapper objectMapper; + @Inject + Tracer tracer; + private static final String REASON_OOM_KILLED = "OOMKilled"; private static final String GENERATION_ID_LABEL = "sbomer.jboss.org/generation-id"; private static final String RESULT_NAME_SBOM_URL = "sbom-url"; + private static final String TRACEPARENT_ANNOTATION = "sbomer.jboss.org/traceparent"; - @WithSpan @Override public UpdateControl reconcile(TaskRun taskRun, Context context) { String taskName = taskRun.getMetadata().getName(); String generationId = taskRun.getMetadata().getLabels().get(GENERATION_ID_LABEL); - // Extract status for logging and tracing + // Read trace context from TaskRun annotations + Map annotations = taskRun.getMetadata().getAnnotations(); + String traceParent = annotations != null ? annotations.get(TRACEPARENT_ANNOTATION) : null; + + // Extract status for span attributes and logging + String taskRunStatus = getConditionStatus(taskRun); String taskRunReason = getConditionReason(taskRun); - // Add span attributes for observability - Span.current().setAttribute("taskrun.name", taskName != null ? taskName : "unknown"); - Span.current().setAttribute("taskrun.reason", taskRunReason); - if (generationId != null) { - Span.current().setAttribute("generation.id", generationId); + // Create a child span under the original trace from the Kafka consumer + Span span = TraceUtility.childSpanBuilder(tracer, "TaskReconciler.reconcile", traceParent, generationId) + .setAttribute("taskrun.name", taskName != null ? taskName : "unknown") + .setAttribute("taskrun.status", taskRunStatus) + .setAttribute("taskrun.reason", taskRunReason) + .startSpan(); + try (Scope ignored = span.makeCurrent()) { + return doReconcile(taskRun, taskName, generationId, taskRunReason); + } finally { + span.end(); } - - return doReconcile(taskRun, taskName, generationId, taskRunReason); } private UpdateControl doReconcile(TaskRun taskRun, String taskName, String generationId, String statusReason) { diff --git a/src/main/java/org/jboss/sbomer/java/generator/core/service/TaskRunFactory.java b/src/main/java/org/jboss/sbomer/java/generator/core/service/TaskRunFactory.java index 2cab934..93853b6 100644 --- a/src/main/java/org/jboss/sbomer/java/generator/core/service/TaskRunFactory.java +++ b/src/main/java/org/jboss/sbomer/java/generator/core/service/TaskRunFactory.java @@ -34,10 +34,11 @@ public class TaskRunFactory { @ConfigProperty(name = "sbomer.generator.java.maven-settings-configmap", defaultValue = "java-generator-maven-settings") String mavenSettingsConfigMapName; - @ConfigProperty(name = "sbomer.generator.kueue.queue-name", defaultValue = "java-generator-queue") + @ConfigProperty(name = "sbomer.generator.kueue.queue-name", defaultValue = "java-generator-local-queue") String kueueQueueName; private static final String ANNOTATION_TRACEPARENT = "sbomer.jboss.org/traceparent"; + private static final String ANNOTATION_KUEUE_QUEUE = "kueue.x-k8s.io/queue-name"; public TaskRun createCdxMavenPluginTaskRun(GenerationTask generationTask) { return createBaseTaskRun(generationTask, cdxMavenPluginTaskName, "java-cdx-maven-gen-"); @@ -105,7 +106,7 @@ private TaskRun createBaseTaskRun(GenerationTask generationTask, String taskName labels.put(LABEL_GENERATION_ID, generationId); labels.put(LABEL_GENERATOR_TYPE, LABEL_GENERATOR_VALUE); labels.put("app.kubernetes.io/managed-by", "sbomer-java-generator"); - labels.put("kueue.x-k8s.io/queue-name", kueueQueueName); + labels.put(ANNOTATION_KUEUE_QUEUE, kueueQueueName); Map annotations = new HashMap<>(); if (generationTask.traceParent() != null) { diff --git a/src/main/java/org/jboss/sbomer/java/generator/core/utility/TraceUtility.java b/src/main/java/org/jboss/sbomer/java/generator/core/utility/TraceUtility.java new file mode 100644 index 0000000..5d05776 --- /dev/null +++ b/src/main/java/org/jboss/sbomer/java/generator/core/utility/TraceUtility.java @@ -0,0 +1,67 @@ +package org.jboss.sbomer.java.generator.core.utility; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanBuilder; +import io.opentelemetry.api.trace.SpanContext; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.api.trace.TraceFlags; +import io.opentelemetry.api.trace.TraceState; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Context; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class TraceUtility { + + private TraceUtility() {} + + /** + * Returns a SpanBuilder configured as the child of given traceparent. + * Callers can add their own attributes before calling startSpan(). + * + * @param tracer the OTel tracer. + * @param spanName the span operation name. + * @param traceParent the W3C traceparent header. + * @param generationId the generation ID to set as a span attribute. + * @return a configured {@link SpanBuilder}. + */ + public static SpanBuilder childSpanBuilder(Tracer tracer, String spanName, String traceParent, String generationId) { + SpanBuilder spanBuilder = tracer.spanBuilder(spanName) + .setSpanKind(SpanKind.INTERNAL) + .setAttribute("generation.id", generationId != null ? generationId : "unknown"); + SpanContext parentContext = parseTraceParent(traceParent); + if (parentContext != null && parentContext.isValid()) { + spanBuilder.setParent(Context.root().with(Span.wrap(parentContext))); + } + return spanBuilder; + } + + /** + * Parses W3C traceparent header into SpanContext. + * Expected format: 00--- + * + * @param traceParent the traceparent header value. + * @return the parsed SpanContext, or null if input is absent or malformed. + */ + public static SpanContext parseTraceParent(String traceParent) { + if (traceParent == null || traceParent.isEmpty()) { + return null; + } + String[] parts = traceParent.split("-"); + if (parts.length != 4) { + log.warn("Invalid traceparent header format: {}", traceParent); + return null; + } + try { + return SpanContext.createFromRemoteParent( + parts[1], // traceId + parts[2], // spanId + TraceFlags.fromHex(parts[3], 0), // traceFlags + TraceState.getDefault() + ); + } catch (Exception e) { + log.warn("Failed to parse traceparent header '{}': {}", traceParent, e.getMessage()); + return null; + } + } +} \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 37a4aea..7e5ccdd 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -33,7 +33,7 @@ sbomer.generator.java.gradle-init-configmap=java-generator-gradle-init sbomer.storage.url=http://localhost:8085 # Kueue queue name for TaskRun scheduling -sbomer.generator.kueue.queue-name=java-generator-queue +sbomer.generator.kueue.queue-name=java-generator-local-queue #======================================= # KAFKA - GLOBAL PRODUCER CONFIG