This is an automated email from the ASF dual-hosted git repository.

stankiewicz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/beam.git


The following commit(s) were added to refs/heads/master by this push:
     new 7cc35440762 Resolve inline Secret placeholders in OpenTelemetry 
properties (#40579)
7cc35440762 is described below

commit 7cc354407622964509dada9612267dc5050e8fab
Author: RadosÅ‚aw Stankiewicz <[email protected]>
AuthorDate: Thu Oct 15 20:15:17 2026 +0200

    Resolve inline Secret placeholders in OpenTelemetry properties (#40579)
---
 .../apache/beam/sdk/options/SdkHarnessOptions.java | 46 ++++++++++++-
 .../beam/sdk/options/SdkHarnessOptionsTest.java    | 79 ++++++++++++++++++++++
 2 files changed, 122 insertions(+), 3 deletions(-)

diff --git 
a/sdks/java/core/src/main/java/org/apache/beam/sdk/options/SdkHarnessOptions.java
 
b/sdks/java/core/src/main/java/org/apache/beam/sdk/options/SdkHarnessOptions.java
index 0fcd770c1d1..bd1724688ea 100644
--- 
a/sdks/java/core/src/main/java/org/apache/beam/sdk/options/SdkHarnessOptions.java
+++ 
b/sdks/java/core/src/main/java/org/apache/beam/sdk/options/SdkHarnessOptions.java
@@ -34,7 +34,10 @@ import java.util.UUID;
 import java.util.logging.Level;
 import java.util.logging.LogManager;
 import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 import org.apache.beam.sdk.util.InstanceBuilder;
+import org.apache.beam.sdk.util.Secret;
 import 
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.annotations.VisibleForTesting;
 import 
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.collect.ImmutableMap;
 import org.checkerframework.checker.index.qual.NonNegative;
@@ -467,11 +470,19 @@ public interface SdkHarnessOptions extends 
PipelineOptions, MemoryMonitorOptions
 
   /**
    * The OpenTelemetry properties that will be appended to the set of system 
properties for SDK
-   * harness instances. Property names must be specified without the 'otel.' 
prefix.
+   * harness instances.
+   *
+   * <p>Property values may contain inline secret placeholders formatted as 
{@code
+   * ${type:<secret_type>;<secret_param>:<value>}} (see {@link 
Secret#parseSecretOption(String)}),
+   * which are resolved on the worker before setting system properties. For 
example: {@code
+   * 
otel.exporter.otlp.metrics.headers=api-key=${type:GcpSecret;version_name:projects/my-project/secrets/my-secret/versions/latest}}.
+   * Secret parameter values may contain {@code ':'} (such as ARNs or URLs), 
but cannot contain
+   * {@code ';'} or {@code '}'}.
    */
   @Description(
       "The OpenTelemetry properties that will be appended to the set of system 
properties for SDK "
-          + "harness instances. Property names must be specified without the 
'otel.' prefix.")
+          + "harness instances. Property values may reference secrets using "
+          + "${type:<secret_type>;<secret_param>:<value>} (parameter values 
cannot contain ';' or '}').")
   Map<String, String> getOpenTelemetryProperties();
 
   void setOpenTelemetryProperties(Map<String, String> value);
@@ -483,7 +494,7 @@ public interface SdkHarnessOptions extends PipelineOptions, 
MemoryMonitorOptions
       openTelemetryProperties.forEach(
           (k, v) -> {
             if (k != null && v != null) {
-              System.setProperty(k, v);
+              System.setProperty(k, resolveOpenTelemetryPropertyValue(v));
             }
           });
       if (System.getProperty("otel.service.instance.id") == null) {
@@ -499,6 +510,33 @@ public interface SdkHarnessOptions extends 
PipelineOptions, MemoryMonitorOptions
     }
   }
 
+  /**
+   * Resolves any inline {@code ${...}} secret placeholders in an 
OpenTelemetry property value using
+   * {@link Secret#parseSecretOption(String)}.
+   */
+  @VisibleForTesting
+  static String resolveOpenTelemetryPropertyValue(String value) {
+    Matcher matcher = 
GlobalOpenTelemetryFactory.SECRET_PLACEHOLDER_PATTERN.matcher(value);
+    if (!matcher.find()) {
+      return value;
+    }
+    // Use StringBuffer for Java 8 compatibility with 
Matcher#appendReplacement.
+    StringBuffer resolved = new StringBuffer();
+    do {
+      String secretOption = checkStateNotNull(matcher.group(1));
+      String secretValue = 
Secret.parseSecretOption(secretOption).getString(true);
+      if (secretValue == null) {
+        throw new IllegalStateException(
+            "Resolved secret value cannot be null for secret option: " + 
secretOption);
+      }
+      // Trim surrounding whitespace/newlines commonly stored in Secret 
Manager payloads and quote
+      // regex replacement metacharacters ('$' and '\') so they are not 
interpreted as group refs.
+      matcher.appendReplacement(resolved, 
Matcher.quoteReplacement(secretValue.trim()));
+    } while (matcher.find());
+    matcher.appendTail(resolved);
+    return resolved.toString();
+  }
+
   @JsonIgnore
   @Hidden
   @Default.InstanceFactory(GlobalOpenTelemetryFactory.class)
@@ -507,6 +545,8 @@ public interface SdkHarnessOptions extends PipelineOptions, 
MemoryMonitorOptions
   void setOpenTelemetry(OpenTelemetry value);
 
   class GlobalOpenTelemetryFactory implements 
DefaultValueFactory<OpenTelemetry> {
+    private static final Pattern SECRET_PLACEHOLDER_PATTERN = 
Pattern.compile("\\$\\{([^}]+)\\}");
+
     @Override
     public OpenTelemetry create(PipelineOptions options) {
       return GlobalOpenTelemetry.get();
diff --git 
a/sdks/java/core/src/test/java/org/apache/beam/sdk/options/SdkHarnessOptionsTest.java
 
b/sdks/java/core/src/test/java/org/apache/beam/sdk/options/SdkHarnessOptionsTest.java
index 80701773c5f..265189b3f15 100644
--- 
a/sdks/java/core/src/test/java/org/apache/beam/sdk/options/SdkHarnessOptionsTest.java
+++ 
b/sdks/java/core/src/test/java/org/apache/beam/sdk/options/SdkHarnessOptionsTest.java
@@ -19,20 +19,26 @@ package org.apache.beam.sdk.options;
 
 import static org.apache.beam.sdk.options.SdkHarnessOptions.LogLevel.WARN;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertThrows;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
 import 
org.apache.beam.sdk.options.SdkHarnessOptions.DefaultMaxCacheMemoryUsageMb;
 import 
org.apache.beam.sdk.options.SdkHarnessOptions.SdkHarnessLogLevelOverrides;
+import org.apache.beam.sdk.testing.RestoreSystemProperties;
 import org.apache.beam.sdk.util.common.ReflectHelpers;
 import 
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.collect.ImmutableMap;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
 /** Tests for {@link SdkHarnessOptions}. */
 @RunWith(JUnit4.class)
 public class SdkHarnessOptionsTest {
+  @Rule public TestRule restoreSystemProperties = new 
RestoreSystemProperties();
+
   private static final ObjectMapper MAPPER =
       new ObjectMapper()
           
.registerModules(ObjectMapper.findModules(ReflectHelpers.findClassLoader()));
@@ -112,4 +118,77 @@ public class SdkHarnessOptionsTest {
         testValue,
         MAPPER.writeValueAsString(MAPPER.readValue(testValue, 
SdkHarnessLogLevelOverrides.class)));
   }
+
+  @Test
+  public void testResolveOpenTelemetryPropertyValuePlain() {
+    assertEquals(
+        "https://otlp.example.com/v1/metrics";,
+        
SdkHarnessOptions.resolveOpenTelemetryPropertyValue("https://otlp.example.com/v1/metrics";));
+  }
+
+  @Test
+  public void testResolveOpenTelemetryPropertyValueWithSecretPlaceholder() {
+    assertEquals(
+        "api-key=my-secret-api-key,env=prod",
+        SdkHarnessOptions.resolveOpenTelemetryPropertyValue(
+            "api-key=${type:TestSecret;secret:my-secret-api-key},env=prod"));
+    assertEquals(
+        "api-key=my-secret-api-key,env=prod",
+        SdkHarnessOptions.resolveOpenTelemetryPropertyValue(
+            "api-key=${secret:my-secret-api-key;type:TestSecret},env=prod"));
+    assertEquals(
+        "endpoint=https://otlp.example.com:4318";,
+        SdkHarnessOptions.resolveOpenTelemetryPropertyValue(
+            
"endpoint=${type:TestSecret;secret:https://otlp.example.com:4318}";));
+  }
+
+  @Test
+  public void 
testResolveOpenTelemetryPropertyValueTrimsAndQuotesSpecialChars() {
+    assertEquals(
+        "api-key=sec$1\\path,token=tok2",
+        SdkHarnessOptions.resolveOpenTelemetryPropertyValue(
+            "api-key=${type:TestSecret;secret:  
sec$1\\path\n},token=${type:testsecret;secret:tok2\r\n}"));
+  }
+
+  @Test
+  public void testResolveOpenTelemetryPropertyValueWithInvalidSecretThrows() {
+    assertThrows(
+        IllegalArgumentException.class,
+        () ->
+            SdkHarnessOptions.resolveOpenTelemetryPropertyValue(
+                "api-key=${type:UnknownSecretType;secret:foo}"));
+    assertThrows(
+        IllegalArgumentException.class,
+        () -> 
SdkHarnessOptions.resolveOpenTelemetryPropertyValue("api-key=${secret:foo}"));
+  }
+
+  @Test
+  public void 
testConfigureOpenTelemetrySetsResolvedPropertiesAndDoesNotMutateOptions() {
+    System.clearProperty("otel.exporter.otlp.metrics.headers");
+    System.clearProperty("otel.service.instance.id");
+
+    SdkHarnessOptions options = 
PipelineOptionsFactory.as(SdkHarnessOptions.class);
+    String rawHeaderValue = "api-key=${type:TestSecret;secret:secret-api-key}";
+    options.setOpenTelemetryProperties(
+        ImmutableMap.of("otel.exporter.otlp.metrics.headers", rawHeaderValue));
+
+    SdkHarnessOptions.configureOpenTelemetry(options);
+
+    assertEquals(
+        "api-key=secret-api-key", 
System.getProperty("otel.exporter.otlp.metrics.headers"));
+    assertNotNull(System.getProperty("otel.service.instance.id"));
+    assertEquals(
+        rawHeaderValue,
+        
options.getOpenTelemetryProperties().get("otel.exporter.otlp.metrics.headers"));
+  }
+
+  @Test
+  public void testConfigureOpenTelemetryWhenEmptyDisablesAuthTargetSignals() {
+    System.clearProperty("google.otel.auth.target.signals");
+    SdkHarnessOptions options = 
PipelineOptionsFactory.as(SdkHarnessOptions.class);
+
+    SdkHarnessOptions.configureOpenTelemetry(options);
+
+    assertEquals("none", 
System.getProperty("google.otel.auth.target.signals"));
+  }
 }

Reply via email to