Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,28 @@

package io.opentelemetry.contrib.dynamic.policy.registry;

import io.opentelemetry.contrib.dynamic.policy.registry.json.JsonPolicyInitConfigReader;
import io.opentelemetry.contrib.dynamic.policy.registry.yaml.YamlPolicyInitConfigReader;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nullable;

/** Top-level registry initialization model containing per-source mapping config. */
public final class PolicyInitConfig {
static final String POLICY_INIT_CONFIG_PROPERTY_JSON =
"otel.java.experimental.telemetry.policy.init.json";
static final String POLICY_INIT_CONFIG_PROPERTY_YAML =
"otel.java.experimental.telemetry.policy.init.yaml";
private static final Logger logger = Logger.getLogger(PolicyInitConfig.class.getName());

private final List<PolicySourceConfig> sources;

Expand All @@ -28,6 +43,52 @@ public List<PolicySourceConfig> getSources() {
return sources;
}

/**
* Reads policy-init configuration based on config properties.
*
* <p>YAML takes precedence over JSON when both are present. If both are present, and the YAML
* file is invalid, the JSON file is still ignored. If the file parsed is invalid, a warning is
* logged and null is returned.
*
* @param config OpenTelemetry config properties
* @return parsed init config, or null when no init-config path is configured or the file is
* invalid
* @throws NullPointerException if config is null
*/
@Nullable
public static PolicyInitConfig readFromConfigProperties(ConfigProperties config) {
Objects.requireNonNull(config, "config cannot be null");
String mappingPathYaml = config.getString(POLICY_INIT_CONFIG_PROPERTY_YAML);
if (mappingPathYaml == null || mappingPathYaml.trim().isEmpty()) {
String mappingPathJson = config.getString(POLICY_INIT_CONFIG_PROPERTY_JSON);
if (mappingPathJson == null || mappingPathJson.trim().isEmpty()) {
return null;
} else {
try (InputStream in = Files.newInputStream(Paths.get(mappingPathJson.trim()))) {
return JsonPolicyInitConfigReader.read(in);
} catch (IOException | RuntimeException e) {
logger.log(
Level.WARNING,
"Failed to load telemetry policy init config from {0}",
mappingPathJson.trim());
logger.log(Level.WARNING, "Policy init config read failed", e);
return null;
}
}
} else {
try (InputStream in = Files.newInputStream(Paths.get(mappingPathYaml.trim()))) {
return YamlPolicyInitConfigReader.read(in);
} catch (IOException | RuntimeException e) {
logger.log(
Level.WARNING,
"Failed to load telemetry policy init config from {0}",
mappingPathYaml.trim());
logger.log(Level.WARNING, "Policy init config read failed", e);
return null;
}
}
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.contrib.dynamic.policy.registry;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import io.opentelemetry.contrib.dynamic.policy.source.SourceFormat;
import io.opentelemetry.contrib.dynamic.policy.source.SourceKind;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

class PolicyInitConfigTest {

@TempDir Path tempDir;

@Test
void readFromConfigPropertiesReturnsNullWhenBothPathsMissing() {
ConfigProperties config = mock(ConfigProperties.class);
when(config.getString(PolicyInitConfig.POLICY_INIT_CONFIG_PROPERTY_YAML)).thenReturn(null);
when(config.getString(PolicyInitConfig.POLICY_INIT_CONFIG_PROPERTY_JSON)).thenReturn(null);

assertThat(PolicyInitConfig.readFromConfigProperties(config)).isNull();
}

@Test
void readFromConfigPropertiesPrefersYamlWhenBothPathsPresent() throws Exception {
Path jsonPath = tempDir.resolve("policy-init.json");
Files.write(jsonPath, jsonWithLocation("from-json").getBytes(StandardCharsets.UTF_8));
Path yamlPath = tempDir.resolve("policy-init.yaml");
Files.write(yamlPath, yamlWithLocation("from-yaml").getBytes(StandardCharsets.UTF_8));

ConfigProperties config = mock(ConfigProperties.class);
when(config.getString(PolicyInitConfig.POLICY_INIT_CONFIG_PROPERTY_YAML))
.thenReturn(yamlPath.toString());
when(config.getString(PolicyInitConfig.POLICY_INIT_CONFIG_PROPERTY_JSON))
.thenReturn(jsonPath.toString());

PolicyInitConfig initConfig = PolicyInitConfig.readFromConfigProperties(config);

assertThat(initConfig).isNotNull();
assertThat(initConfig.getSources()).hasSize(1);
assertThat(initConfig.getSources().get(0).getLocation()).isEqualTo("from-yaml");
}

@Test
void readFromConfigPropertiesFallsBackToJsonWhenYamlBlank() throws Exception {
Path jsonPath = tempDir.resolve("policy-init.json");
Files.write(jsonPath, jsonWithLocation("from-json").getBytes(StandardCharsets.UTF_8));

ConfigProperties config = mock(ConfigProperties.class);
when(config.getString(PolicyInitConfig.POLICY_INIT_CONFIG_PROPERTY_YAML)).thenReturn(" ");
when(config.getString(PolicyInitConfig.POLICY_INIT_CONFIG_PROPERTY_JSON))
.thenReturn(jsonPath.toString());

PolicyInitConfig initConfig = PolicyInitConfig.readFromConfigProperties(config);

assertThat(initConfig).isNotNull();
assertThat(initConfig.getSources()).hasSize(1);
assertThat(initConfig.getSources().get(0).getLocation()).isEqualTo("from-json");
}

@Test
void readFromConfigPropertiesReadsJsonWhenYamlPathMissing() throws Exception {
Path configPath = tempDir.resolve("policy-init.json");
Files.write(configPath, minimalJsonConfig().getBytes(StandardCharsets.UTF_8));

PolicyInitConfig config = configFromPaths(null, configPath.toString());

assertThat(config.getSources()).hasSize(1);
PolicySourceConfig source = config.getSources().get(0);
assertThat(source.getKind()).isEqualTo(SourceKind.OPAMP);
assertThat(source.getFormat()).isEqualTo(SourceFormat.JSONKEYVALUE);
assertThat(source.getLocation()).isEqualTo("vendor");
assertThat(source.getMappings()).hasSize(1);
assertThat(source.getMappings().get(0).getSourceKey()).isEqualTo("sampling_rate");
assertThat(source.getMappings().get(0).getPolicyType()).isEqualTo("trace_sampling_rate_policy");
}

@Test
void readFromConfigPropertiesReadsYamlWhenYamlPathProvided() throws Exception {
Path configPath = tempDir.resolve("policy-init.yaml");
Files.write(configPath, minimalYamlConfig().getBytes(StandardCharsets.UTF_8));

PolicyInitConfig config = configFromPaths(configPath.toString(), null);

assertThat(config.getSources()).hasSize(1);
PolicySourceConfig source = config.getSources().get(0);
assertThat(source.getKind()).isEqualTo(SourceKind.OPAMP);
assertThat(source.getFormat()).isEqualTo(SourceFormat.JSONKEYVALUE);
assertThat(source.getMappings()).hasSize(1);
}

@Test
void readFromConfigPropertiesReturnsNullOnFileReadFailure() {
Path missing = tempDir.resolve("missing-policy-init.json");

assertThat(configFromPaths(null, missing.toString())).isNull();
}

@Test
void readFromConfigPropertiesReturnsNullOnParseFailure() throws Exception {
Path badJson = tempDir.resolve("bad-policy-init.json");
Files.write(badJson, "{}".getBytes(StandardCharsets.UTF_8));

assertThat(configFromPaths(null, badJson.toString())).isNull();
}

private static PolicyInitConfig configFromPaths(String yamlPath, String jsonPath) {
ConfigProperties config = mock(ConfigProperties.class);
when(config.getString(PolicyInitConfig.POLICY_INIT_CONFIG_PROPERTY_YAML)).thenReturn(yamlPath);
when(config.getString(PolicyInitConfig.POLICY_INIT_CONFIG_PROPERTY_JSON)).thenReturn(jsonPath);
return PolicyInitConfig.readFromConfigProperties(config);
}

private static String minimalJsonConfig() {
return "{\"sources\":[{\"kind\":\"opamp\",\"format\":\"jsonkeyvalue\",\"location\":\"vendor\","
+ "\"mappings\":[{\"sourceKey\":\"sampling_rate\",\"policyType\":\"trace_sampling_rate_policy\"}]}]}";
}

private static String jsonWithLocation(String location) {
return "{\"sources\":[{\"kind\":\"opamp\",\"format\":\"jsonkeyvalue\",\"location\":\""
+ location
+ "\",\"mappings\":[{\"sourceKey\":\"sampling_rate\",\"policyType\":\"trace_sampling_rate_policy\"}]}]}";
}

private static String minimalYamlConfig() {
return "sources:\n"
+ " - kind: opamp\n"
+ " format: jsonkeyvalue\n"
+ " location: vendor\n"
+ " mappings:\n"
+ " - sourceKey: sampling_rate\n"
+ " policyType: trace_sampling_rate_policy\n";
}

private static String yamlWithLocation(String location) {
return "sources:\n"
+ " - kind: opamp\n"
+ " format: jsonkeyvalue\n"
+ " location: "
+ location
+ "\n"
+ " mappings:\n"
+ " - sourceKey: sampling_rate\n"
+ " policyType: trace_sampling_rate_policy\n";
}
}
Loading