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
12 changes: 10 additions & 2 deletions .github/workflows/ko-build-main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,18 @@ jobs:
- uses: ko-build/[email protected]
with:
version: v0.17.1
- run: |
- name: Build and publish indexer image
env:
VERSION: development
GIT_COMMIT: ${{ github.sha }}
run: |
BUILD_TIME=$(date -u '+%Y-%m-%d_%H:%M:%S')
export BUILD_TIME
GIT_COMMIT=${GIT_COMMIT:0:7}
export GIT_COMMIT
ko build github.com/linuxfoundation/lfx-v2-indexer-service/cmd/lfx-indexer \
-B \
--platform linux/amd64,linux/arm64 \
-t development \
-t ${{ github.sha }} \
-t development \
--sbom spdx
7 changes: 7 additions & 0 deletions .github/workflows/ko-build-tag.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,14 @@ jobs:
version: v0.17.1

- name: Build and publish indexer image
env:
VERSION: ${{ steps.prepare.outputs.app_version }}
GIT_COMMIT: ${{ github.sha }}
run: |
BUILD_TIME=$(date -u '+%Y-%m-%d_%H:%M:%S')
export BUILD_TIME
GIT_COMMIT=${GIT_COMMIT:0:7}
export GIT_COMMIT
ko build github.com/linuxfoundation/lfx-v2-indexer-service/cmd/lfx-indexer \
-B \
--platform linux/amd64,linux/arm64 \
Expand Down
9 changes: 9 additions & 0 deletions .ko.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Copyright The Linux Foundation and each contributor to LFX.
# SPDX-License-Identifier: MIT
builds:
- id: lfx-indexer
dir: ./cmd/lfx-indexer
ldflags:
- -X=main.Version={{.Env.VERSION}}
- -X=main.BuildTime={{.Env.BUILD_TIME}}
- -X=main.GitCommit={{.Env.GIT_COMMIT}}
53 changes: 53 additions & 0 deletions charts/lfx-v2-indexer-service/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,59 @@ spec:
value: {{ .Values.health.cacheDuration }}
- name: HEALTH_DETAILED_RESPONSE
value: {{ .Values.health.enableDetailedResponse | quote }}
{{- with .Values.app.extraEnv }}
{{- toYaml . | nindent 12 }}
{{- end }}
{{- $otelServiceName := .Values.app.otel.serviceName | toString | trim }}
{{- if ne $otelServiceName "" }}
- name: OTEL_SERVICE_NAME
value: {{ $otelServiceName | quote }}
{{- end }}
{{- $otelServiceVersion := .Values.app.otel.serviceVersion | toString | trim }}
{{- if ne $otelServiceVersion "" }}
- name: OTEL_SERVICE_VERSION
value: {{ $otelServiceVersion | quote }}
{{- end }}
{{- $otelEndpoint := .Values.app.otel.endpoint | toString | trim }}
{{- if ne $otelEndpoint "" }}
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: {{ $otelEndpoint | quote }}
{{- end }}
{{- $otelProtocol := .Values.app.otel.protocol | toString | trim }}
{{- if ne $otelProtocol "" }}
- name: OTEL_EXPORTER_OTLP_PROTOCOL
value: {{ $otelProtocol | quote }}
{{- end }}
{{- $otelInsecure := .Values.app.otel.insecure | toString | trim }}
{{- if ne $otelInsecure "" }}
- name: OTEL_EXPORTER_OTLP_INSECURE
value: {{ $otelInsecure | quote }}
{{- end }}
{{- $otelTracesExporter := .Values.app.otel.tracesExporter | toString | trim }}
{{- if ne $otelTracesExporter "" }}
- name: OTEL_TRACES_EXPORTER
value: {{ $otelTracesExporter | quote }}
{{- end }}
{{- $otelTracesSampleRatio := .Values.app.otel.tracesSampleRatio | toString | trim }}
{{- if ne $otelTracesSampleRatio "" }}
- name: OTEL_TRACES_SAMPLE_RATIO
value: {{ $otelTracesSampleRatio | quote }}
{{- end }}
{{- $otelMetricsExporter := .Values.app.otel.metricsExporter | toString | trim }}
{{- if ne $otelMetricsExporter "" }}
- name: OTEL_METRICS_EXPORTER
value: {{ $otelMetricsExporter | quote }}
{{- end }}
{{- $otelLogsExporter := .Values.app.otel.logsExporter | toString | trim }}
{{- if ne $otelLogsExporter "" }}
- name: OTEL_LOGS_EXPORTER
value: {{ $otelLogsExporter | quote }}
{{- end }}
{{- $otelPropagators := .Values.app.otel.propagators | toString | trim }}
{{- if ne $otelPropagators "" }}
- name: OTEL_PROPAGATORS
value: {{ $otelPropagators | quote }}
{{- end }}
Comment on lines +79 to +128
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

fd "values.yaml" charts/ --type f

Repository: linuxfoundation/lfx-v2-indexer-service

Length of output: 123


🏁 Script executed:

# Check the values.yaml in the specific chart directory
cat -n charts/lfx-v2-indexer-service/values.yaml | head -150

Repository: linuxfoundation/lfx-v2-indexer-service

Length of output: 5850


🏁 Script executed:

# Look for the otel configuration section in values.yaml
grep -n -A 30 "otel:" charts/lfx-v2-indexer-service/values.yaml

Repository: linuxfoundation/lfx-v2-indexer-service

Length of output: 1548


🏁 Script executed:

# Check if there are any guards already in deployment.yaml before line 79
sed -n '1,79p' charts/lfx-v2-indexer-service/templates/deployment.yaml | tail -20

Repository: linuxfoundation/lfx-v2-indexer-service

Length of output: 1019


Add a nil guard before accessing .Values.app.otel to prevent "<nil>" string injection.

While the default values.yaml defines the otel block, users who override values could potentially omit it. If .Values.app.otel becomes nil, accessing .otel.serviceName returns nil, which converts to the string literal "<nil>" via toString, bypassing the ne "" check and setting OTEL_SERVICE_NAME="<nil>".

Wrap the entire OTEL section in a nil guard:

+            {{- if .Values.app.otel }}
             {{- $otelServiceName := .Values.app.otel.serviceName | toString | trim }}
             {{- if ne $otelServiceName "" }}
             - name: OTEL_SERVICE_NAME
               value: {{ $otelServiceName | quote }}
             {{- end }}
             ...
             {{- end }}
+            {{- end }}

Alternatively, guard each access with default "" before toString:

{{- $otelServiceName := .Values.app.otel.serviceName | default "" | toString | trim }}

The outer guard is cleaner and more efficient.

🤖 Prompt for AI Agents
In `@charts/lfx-v2-indexer-service/templates/deployment.yaml` around lines 79 -
128, The OTEL env var block accesses .Values.app.otel fields directly which can
produce the literal "<nil>" when the otel map is absent; wrap the entire OTEL
section in a nil guard (e.g., `if .Values.app.otel`) so you only compute
$otelServiceName, $otelServiceVersion, $otelEndpoint, $otelProtocol,
$otelInsecure, $otelTracesExporter, $otelTracesSampleRatio,
$otelMetricsExporter, $otelLogsExporter, and $otelPropagators when
.Values.app.otel is non-nil, or alternatively apply `default ""` to each access
(e.g., use `default "" | toString | trim`) to ensure empty strings instead of
"<nil>" before the `ne ""` checks and env var creation.

ports:
- containerPort: {{ .Values.app.port }}
name: web
Expand Down
37 changes: 37 additions & 0 deletions charts/lfx-v2-indexer-service/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,43 @@ app:
readTimeout: "5s"
writeTimeout: "5s"
shutdownTimeout: "10s"
# extraEnv allows adding additional environment variables to the container.
# Supports both simple values and Kubernetes field references.
extraEnv: []
# otel is the configuration for OpenTelemetry tracing
otel:
# serviceName is the service name for OpenTelemetry resource identification
# (default: "lfx-v2-indexer-service")
serviceName: ""
# serviceVersion is the service version for OpenTelemetry resource identification
# (default: build-time Version variable)
serviceVersion: ""
Comment on lines +77 to +79
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

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

The values.yaml documentation claims the default for serviceVersion is "1.0.0", but the actual code in otel.go (line 90) doesn't provide any default value for serviceVersion - it uses whatever is in the environment variable or an empty string. This creates a discrepancy between the documented behavior and actual behavior. Either update the code to provide "1.0.0" as the default, or update the documentation to clarify that the default is an empty string.

Copilot uses AI. Check for mistakes.
# protocol specifies the OTLP protocol: "grpc" or "http"
# (default: "grpc")
protocol: "grpc"
# endpoint is the OTLP collector endpoint
# For gRPC: typically "host:4317", for HTTP: typically "host:4318"
endpoint: ""
# insecure disables TLS for the OTLP connection
# Set to "true" for in-cluster communication without TLS
insecure: "false"
# tracesExporter specifies the traces exporter: "otlp" or "none"
# (default: "none")
tracesExporter: "none"
# tracesSampleRatio specifies the sampling ratio for traces (0.0 to 1.0)
# A value of 1.0 means all traces are sampled, 0.5 means 50% are sampled
# (default: "1.0")
tracesSampleRatio: "1.0"
# metricsExporter specifies the metrics exporter: "otlp" or "none"
# (default: "none")
metricsExporter: "none"
# logsExporter specifies the logs exporter: "otlp" or "none"
# (default: "none")
logsExporter: "none"
# propagators specifies the propagators to use, comma-separated
# Supported values: "tracecontext", "baggage", "jaeger"
# (default: "tracecontext,baggage")
propagators: "tracecontext,baggage,jaeger"
Comment on lines +82 to +105
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

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

The default values in values.yaml use explicit values for fields that have documented defaults in the code. For example, protocol: "grpc", insecure: "false", tracesExporter: "none", etc. are set to their default values. This creates redundancy and potential for drift between the documented defaults in otel.go and the Helm chart defaults. Consider setting these to empty strings (like serviceName and endpoint) to allow the Go code defaults to take effect, or add clear comments indicating these values override code defaults.

Copilot uses AI. Check for mistakes.
Comment on lines +102 to +105
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

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

The default propagators value in values.yaml is "tracecontext,baggage,jaeger" but the code default in otel.go line 118 is "tracecontext,baggage" (without jaeger). This inconsistency means that deployments using default values.yaml will have different behavior than running the application with default environment variables. Consider aligning these defaults or documenting why they differ.

Copilot uses AI. Check for mistakes.

# image is the configuration for the container image
image:
Expand Down
32 changes: 32 additions & 0 deletions cmd/lfx-indexer/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,19 @@ import (

"github.com/linuxfoundation/lfx-v2-indexer-service/internal/container"
"github.com/linuxfoundation/lfx-v2-indexer-service/pkg/logging"
"github.com/linuxfoundation/lfx-v2-indexer-service/pkg/utils"
)

// Build-time variables set via ldflags
var (
Version = "dev"
BuildTime = "unknown"
GitCommit = "unknown"
)

// gracefulShutdownSeconds is the maximum time to wait for OpenTelemetry SDK to flush
const gracefulShutdownSeconds = 30

func main() {
// Parse CLI flags
flags := parseCLIFlags()
Expand All @@ -26,6 +37,27 @@ func main() {
// Handle early exits (help, config-check)
handleEarlyExits(flags, logger)

// Set up OpenTelemetry SDK.
// Command-line/environment OTEL_SERVICE_VERSION takes precedence over
// the build-time Version variable.
otelConfig := utils.OTelConfigFromEnv()
if otelConfig.ServiceVersion == "" {
otelConfig.ServiceVersion = Version
}
otelShutdown, err := utils.SetupOTelSDKWithConfig(context.Background(), otelConfig)
if err != nil {
logger.Error("error setting up OpenTelemetry SDK", "error", err)
os.Exit(1)
}
// Handle shutdown properly so nothing leaks.
defer func() {
ctx, cancel := context.WithTimeout(context.Background(), gracefulShutdownSeconds*time.Second)
defer cancel()
if shutdownErr := otelShutdown(ctx); shutdownErr != nil {
logger.Error("error shutting down OpenTelemetry SDK", "error", shutdownErr)
}
}()

// Log configuration with sources for transparency
logger.Info("Configuration loaded",
"port", flags.Port,
Expand Down
7 changes: 6 additions & 1 deletion cmd/lfx-indexer/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"time"

"github.com/linuxfoundation/lfx-v2-indexer-service/internal/container"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
)

// createHTTPServer creates and configures the HTTP server with health check routes
Expand All @@ -20,6 +21,10 @@ func createHTTPServer(container *container.Container, bind string) *http.Server
mux := http.NewServeMux()
container.HealthHandler.RegisterRoutes(mux)

// Wrap the handler with OpenTelemetry instrumentation
var handler http.Handler = mux
handler = otelhttp.NewHandler(handler, "indexer-service")

// Create HTTP server with CLI overrides
var addr string
if bind == "*" {
Expand All @@ -30,7 +35,7 @@ func createHTTPServer(container *container.Container, bind string) *http.Server

return &http.Server{
Addr: addr,
Handler: mux,
Handler: handler,
ReadTimeout: container.Config.Server.ReadTimeout,
WriteTimeout: container.Config.Server.WriteTimeout,
ReadHeaderTimeout: 3 * time.Second, // Security: prevent slowloris attacks
Expand Down
49 changes: 39 additions & 10 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,57 @@

module github.com/linuxfoundation/lfx-v2-indexer-service

go 1.24
go 1.24.0

require (
github.com/auth0/go-jwt-middleware/v2 v2.3.0
github.com/nats-io/nats.go v1.43.0
github.com/opensearch-project/opensearch-go/v2 v2.3.0
github.com/stretchr/testify v1.10.0
github.com/remychantenay/slog-otel v1.3.4
github.com/stretchr/testify v1.11.1
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0
go.opentelemetry.io/contrib/propagators/jaeger v1.40.0
go.opentelemetry.io/otel v1.40.0
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.16.0
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.16.0
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.40.0
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.40.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0
go.opentelemetry.io/otel/log v0.16.0
go.opentelemetry.io/otel/sdk v1.40.0
go.opentelemetry.io/otel/sdk/log v0.16.0
go.opentelemetry.io/otel/sdk/metric v1.40.0
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/nats-io/nkeys v0.4.11 // indirect
github.com/nats-io/nuid v1.0.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rogpeppe/go-internal v1.13.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/stretchr/objx v0.5.2 // indirect
golang.org/x/crypto v0.40.0 // indirect
golang.org/x/sync v0.11.0 // indirect
golang.org/x/sys v0.34.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 // indirect
go.opentelemetry.io/otel/metric v1.40.0 // indirect
go.opentelemetry.io/otel/trace v1.40.0 // indirect
go.opentelemetry.io/proto/otlp v1.9.0 // indirect
golang.org/x/crypto v0.47.0 // indirect
golang.org/x/net v0.49.0 // indirect
golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.40.0 // indirect
golang.org/x/text v0.33.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 // indirect
google.golang.org/grpc v1.78.0 // indirect
google.golang.org/protobuf v1.36.11 // indirect
gopkg.in/go-jose/go-jose.v2 v2.6.3 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Loading