Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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*
Expand Down
2 changes: 1 addition & 1 deletion helm/java-generator-chart/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{{- if .Values.kueue.includeKueue -}}
{{- if .Values.kueue.enabled -}}
apiVersion: kueue.x-k8s.io/v1beta1
kind: ClusterQueue
metadata:
Expand All @@ -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 }}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{{- if .Values.kueue.includeKueue -}}
{{- if .Values.kueue.enabled -}}
apiVersion: kueue.x-k8s.io/v1beta1
kind: LocalQueue
metadata:
Expand Down
9 changes: 0 additions & 9 deletions helm/java-generator-chart/templates/kueue/resourceflavor.yaml

This file was deleted.

21 changes: 13 additions & 8 deletions helm/java-generator-chart/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

Expand All @@ -36,28 +38,39 @@ public class TaskReconciler implements Reconciler<TaskRun> {
@Inject
ObjectMapper objectMapper;

@Inject
Tracer tracer;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity, what is the difference in using Tracer vs using the @WithSpan?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question! https://github.com/sbomer-project/sbomer-engineering-hub/blob/main/docs/modules/ROOT/pages/observability-standards.adoc#kubernetes-resource-change

Basically if we just rely on @WithSpan we end up with an orphaned TaskReconciler root span because TaskReconciler is triggered by an async Kubernetes event - it has no knowledge of the parent span. It would end up disconnected to the generation request that triggered it and become part of a new trace.


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<TaskRun> reconcile(TaskRun taskRun, Context<TaskRun> 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<String, String> 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<TaskRun> doReconcile(TaskRun taskRun, String taskName, String generationId, String statusReason) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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-");
Expand Down Expand Up @@ -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<String, String> annotations = new HashMap<>();
if (generationTask.traceParent() != null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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-<traceId>-<spanId>-<traceFlags>
*
* @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;
}
}
}
2 changes: 1 addition & 1 deletion src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down