Skip to content
Draft
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
40 changes: 39 additions & 1 deletion internal/controller/gateway_mutator.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package controller

import (
"context"
"encoding/json"
"fmt"
"path/filepath"
"strings"
Expand Down Expand Up @@ -115,6 +116,16 @@ const (
// environment variables. The input delimiter is a semicolon (';') to allow
// values to contain commas without escaping.
// Example: "OTEL_SERVICE_NAME=ai-gateway;OTEL_TRACES_EXPORTER=otlp".
//
// Additionally, parse secretKeyRefs for handling secrets in ext_proc env vars:
// Examples:
//
// OTEL_EXPORTER_OTLP_HEADERS=secretKeyRef { "name": "SECRET_NAME", "key": "KEY" }
// OTEL_EXPORTER_OTLP_HEADERS=secretKeyRef {"name":"SECRET_NAME","key":"KEY"}
// OTEL_EXPORTER_OTLP_HEADERS=secretKeyRef{"name":"SECRET_NAME","key":"KEY"}
//
// then this will be parsed into an EnvVar whose ValueFrom.SecretKeyRef is set
// accordingly. Quotes around the name/key are required for valid json parsing.
func ParseExtraEnvVars(s string) ([]corev1.EnvVar, error) {
if s == "" {
return nil, nil
Expand All @@ -137,7 +148,34 @@ func ParseExtraEnvVars(s string) ([]corev1.EnvVar, error) {
if key == "" {
return nil, fmt.Errorf("empty env var name at position %d: %q", i+1, pair)
}
result = append(result, corev1.EnvVar{Name: key, Value: value})

// inline secretKeyRef dictionary parsing
if after, ok := strings.CutPrefix(strings.TrimSpace(value), "secretKeyRef"); ok {
jsonPart := after
var m map[string]string
if err := json.Unmarshal([]byte(jsonPart), &m); err != nil {
return nil, fmt.Errorf("failed to parse secretKeyRef json at position %d: %w", i+1, err)
}

secretName := strings.TrimSpace(m["name"])
secretKey := strings.TrimSpace(m["key"])
if secretName == "" || secretKey == "" {
return nil, fmt.Errorf("secretKeyRef missing name or key at position %d: %q :", i+1, value)
}

result = append(result, corev1.EnvVar{
Name: key,
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{Name: secretName},
Key: secretKey,
},
},
})
} else {
// Plain literal value -- preserve original spacing.
result = append(result, corev1.EnvVar{Name: key, Value: value})
}
}

if len(result) == 0 {
Expand Down
47 changes: 47 additions & 0 deletions internal/controller/gateway_mutator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,21 @@ func TestParseExtraEnvVars(t *testing.T) {
{Name: "OTEL_SERVICE_NAME", Value: "ai-gateway"},
},
},
{
name: "single secret ref",
input: "OTEL_SERVICE_NAME=secretKeyRef {\"name\":\"foo\",\"key\":\"bar\"}",
want: []corev1.EnvVar{
{
Name: "OTEL_SERVICE_NAME",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{Name: "foo"},
Key: "bar",
},
},
},
},
},
{
name: "multiple env vars",
input: "OTEL_SERVICE_NAME=ai-gateway;OTEL_TRACES_EXPORTER=otlp",
Expand All @@ -178,6 +193,23 @@ func TestParseExtraEnvVars(t *testing.T) {
{Name: "OTEL_TRACES_EXPORTER", Value: "otlp"},
},
},
{
name: "multiple env vars with a secretkeyref",
input: "OTEL_SERVICE_NAME=ai-gateway;OTEL_TRACES_EXPORTER=secretKeyRef {\"name\":\"foo\",\"key\":\"bar\"}",
want: []corev1.EnvVar{
{Name: "OTEL_SERVICE_NAME", Value: "ai-gateway"},
{
Name: "OTEL_TRACES_EXPORTER",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{Name: "foo"},
Key: "bar",
},
},
},
},
},

{
name: "env var with comma in value",
input: "OTEL_RESOURCE_ATTRIBUTES=service.name=gateway,service.version=1.0",
Expand Down Expand Up @@ -207,6 +239,21 @@ func TestParseExtraEnvVars(t *testing.T) {
{Name: "OTEL_SERVICE_NAME", Value: "ai-gateway"},
},
},
{
name: "trailing semicolon with secretkeyref",
input: "OTEL_SERVICE_NAME=secretKeyRef {\"name\":\"foo\",\"key\":\"bar\"};",
want: []corev1.EnvVar{
{
Name: "OTEL_SERVICE_NAME",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{Name: "foo"},
Key: "bar",
},
},
},
},
},
{
name: "spaces around values",
input: " OTEL_SERVICE_NAME = ai-gateway ; OTEL_TRACES_EXPORTER = otlp ",
Expand Down