-
Notifications
You must be signed in to change notification settings - Fork 312
Implement Config Inversion with Default Strictness of Warning
#9539
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: mhlidd/test
Are you sure you want to change the base?
Changes from 16 commits
0969a7d
0eda863
d4cf4fb
76281ab
83f0f98
e9f9689
9625dd4
a2926f9
bcb6db6
31fc7b4
71e309f
c65460a
a4de955
cad575c
5fe0a88
4c924eb
3bbf486
8ba684e
cad4c24
11fc52b
63e5d5a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
package datadog.trace.config.inversion; | ||
|
||
import datadog.environment.EnvironmentVariables; | ||
import datadog.trace.api.telemetry.ConfigInversionMetricCollectorProvider; | ||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Locale; | ||
import java.util.Map; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
public class ConfigHelper { | ||
|
||
/** Config Inversion strictness policy for enforcement of undocumented environment variables */ | ||
public enum StrictnessPolicy { | ||
STRICT, | ||
WARNING, | ||
TEST; | ||
|
||
private String displayName; | ||
|
||
StrictnessPolicy() { | ||
this.displayName = name().toLowerCase(Locale.ROOT); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
if (displayName == null) { | ||
displayName = name().toLowerCase(Locale.ROOT); | ||
} | ||
return displayName; | ||
} | ||
} | ||
|
||
private static final Logger log = LoggerFactory.getLogger(ConfigHelper.class); | ||
|
||
private static final ConfigHelper INSTANCE = new ConfigHelper(); | ||
|
||
private StrictnessPolicy configInversionStrict = StrictnessPolicy.WARNING; | ||
|
||
// Cache for configs, init value is null | ||
private Map<String, String> configs; | ||
|
||
|
||
// Default to production source | ||
private SupportedConfigurationSource configSource = new SupportedConfigurationSource(); | ||
|
||
public static ConfigHelper get() { | ||
return INSTANCE; | ||
} | ||
|
||
public void setConfigInversionStrict(StrictnessPolicy configInversionStrict) { | ||
this.configInversionStrict = configInversionStrict; | ||
} | ||
|
||
public StrictnessPolicy configInversionStrictFlag() { | ||
return configInversionStrict; | ||
} | ||
|
||
// Used only for testing purposes | ||
void setConfigurationSource(SupportedConfigurationSource testSource) { | ||
configSource = testSource; | ||
} | ||
|
||
/** Resetting config cache. Useful for cleaning up after tests. */ | ||
void resetCache() { | ||
configs = null; | ||
} | ||
|
||
/** Reset all configuration data to the generated defaults. Useful for cleaning up after tests. */ | ||
void resetToDefaults() { | ||
configSource = new SupportedConfigurationSource(); | ||
this.configInversionStrict = StrictnessPolicy.WARNING; | ||
} | ||
|
||
public Map<String, String> getEnvironmentVariables() { | ||
if (configs != null) { | ||
return configs; | ||
} | ||
|
||
configs = new HashMap<>(); | ||
Map<String, String> env = EnvironmentVariables.getAll(); | ||
for (Map.Entry<String, String> entry : env.entrySet()) { | ||
String key = entry.getKey(); | ||
String value = entry.getValue(); | ||
String primaryEnv = configSource.primaryEnvFromAlias(key); | ||
if (key.startsWith("DD_") || key.startsWith("OTEL_") || null != primaryEnv) { | ||
if (configSource.supported(key)) { | ||
configs.put(key, value); | ||
// If this environment variable is the alias of another, and we haven't processed the | ||
// original environment variable yet, handle it here. | ||
} else if (null != primaryEnv && !configs.containsKey(primaryEnv)) { | ||
|
||
List<String> aliases = configSource.getAliases(primaryEnv); | ||
for (String alias : aliases) { | ||
if (env.containsKey(alias)) { | ||
configs.put(primaryEnv, env.get(alias)); | ||
break; | ||
} | ||
} | ||
} | ||
String envFromDeprecated; | ||
if ((envFromDeprecated = configSource.primaryEnvFromDeprecated(key)) != null) { | ||
mhlidd marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
String warning = | ||
"Environment variable " | ||
+ key | ||
+ " is deprecated. Please use " | ||
+ (primaryEnv != null ? primaryEnv : envFromDeprecated) | ||
+ " instead."; | ||
log.warn(warning); | ||
} | ||
} else { | ||
configs.put(key, value); | ||
} | ||
} | ||
return configs; | ||
} | ||
|
||
public String getEnvironmentVariable(String name) { | ||
if (configs != null && configs.containsKey(name)) { | ||
return configs.get(name); | ||
} | ||
|
||
if ((name.startsWith("DD_") || name.startsWith("OTEL_")) | ||
mhlidd marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
&& null != configSource.primaryEnvFromAlias(name) | ||
&& !configSource.supported(name)) { | ||
if (configInversionStrict != StrictnessPolicy.TEST) { | ||
ConfigInversionMetricCollectorProvider.get().setUndocumentedEnvVarMetric(name); | ||
} | ||
|
||
if (configInversionStrict == StrictnessPolicy.STRICT) { | ||
return null; // If strict mode is enabled, return null for unsupported configs | ||
} | ||
} | ||
|
||
String config = EnvironmentVariables.get(name); | ||
List<String> aliases; | ||
if (config == null && (aliases = configSource.getAliases(name)) != null) { | ||
for (String alias : aliases) { | ||
String aliasValue = EnvironmentVariables.get(alias); | ||
if (aliasValue != null) { | ||
return aliasValue; | ||
} | ||
} | ||
} | ||
return config; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package datadog.trace.config.inversion; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
/** | ||
* This class uses {@link #GeneratedSupportedConfigurations} for handling supported configurations | ||
* for Config Inversion Can be extended for testing with custom configuration data. | ||
*/ | ||
class SupportedConfigurationSource { | ||
|
||
/** @return Set of supported environment variable keys */ | ||
public boolean supported(String env) { | ||
return GeneratedSupportedConfigurations.SUPPORTED.contains(env); | ||
} | ||
|
||
/** @return List of aliases for an environment variable */ | ||
public List<String> getAliases(String env) { | ||
mhlidd marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return GeneratedSupportedConfigurations.ALIASES.getOrDefault(env, new ArrayList<>()); | ||
} | ||
|
||
/** @return Primary environment variable for a queried alias */ | ||
public String primaryEnvFromAlias(String alias) { | ||
return GeneratedSupportedConfigurations.ALIAS_MAPPING.getOrDefault(alias, null); | ||
} | ||
|
||
/** @return Map of deprecated configurations */ | ||
public String primaryEnvFromDeprecated(String deprecated) { | ||
return GeneratedSupportedConfigurations.DEPRECATED.getOrDefault(deprecated, null); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
package datadog.trace.config.inversion; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.junit.jupiter.api.Assertions.assertFalse; | ||
import static org.junit.jupiter.api.Assertions.assertNull; | ||
|
||
import datadog.trace.test.util.ControllableEnvironmentVariables; | ||
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
import java.util.HashMap; | ||
import java.util.HashSet; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Set; | ||
import org.junit.jupiter.api.AfterAll; | ||
import org.junit.jupiter.api.AfterEach; | ||
import org.junit.jupiter.api.BeforeAll; | ||
import org.junit.jupiter.api.Test; | ||
|
||
public class ConfigHelperTest { | ||
// Test environment variables | ||
private static final String TEST_DD_VAR = "DD_TEST_CONFIG"; | ||
private static final String TEST_DD_VAR_VAL = "test_dd_var"; | ||
private static final String TEST_OTEL_VAR = "OTEL_TEST_CONFIG"; | ||
private static final String TEST_OTEL_VAR_VAL = "test_otel_var"; | ||
private static final String TEST_REGULAR_VAR = "REGULAR_TEST_CONFIG"; | ||
private static final String TEST_REGULAR_VAR_VAL = "test_regular_var"; | ||
|
||
private static final String ALIAS_DD_VAR = "DD_TEST_CONFIG_ALIAS"; | ||
private static final String ALIAS_DD_VAL = "test_alias_val"; | ||
private static final String NON_DD_ALIAS_VAR = "TEST_CONFIG_ALIAS"; | ||
private static final String NON_DD_ALIAS_VAL = "test_alias_val_non_dd"; | ||
|
||
private static final String NEW_ALIAS_TARGET = "DD_NEW_ALIAS_TARGET"; | ||
private static final String NEW_ALIAS_KEY_1 = "DD_NEW_ALIAS_KEY_1"; | ||
private static final String NEW_ALIAS_KEY_2 = "DD_NEW_ALIAS_KEY_2"; | ||
|
||
private static ControllableEnvironmentVariables env; | ||
|
||
private static ConfigHelper.StrictnessPolicy strictness; | ||
private static TestSupportedConfigurationSource testSource; | ||
|
||
@BeforeAll | ||
static void setUp() { | ||
env = ControllableEnvironmentVariables.setup(); | ||
|
||
// Set up test configurations using SupportedConfigurationSource | ||
Set<String> testSupported = | ||
new HashSet<>(Arrays.asList(TEST_DD_VAR, TEST_OTEL_VAR, TEST_REGULAR_VAR)); | ||
|
||
Map<String, List<String>> testAliases = new HashMap<>(); | ||
testAliases.put(TEST_DD_VAR, Arrays.asList(ALIAS_DD_VAR, NON_DD_ALIAS_VAR)); | ||
testAliases.put(NEW_ALIAS_TARGET, Arrays.asList(NEW_ALIAS_KEY_1)); | ||
|
||
Map<String, String> testAliasMapping = new HashMap<>(); | ||
testAliasMapping.put(ALIAS_DD_VAR, TEST_DD_VAR); | ||
testAliasMapping.put(NON_DD_ALIAS_VAR, TEST_DD_VAR); | ||
testAliasMapping.put(NEW_ALIAS_KEY_2, NEW_ALIAS_TARGET); | ||
|
||
// Create and set test configuration source | ||
testSource = | ||
new TestSupportedConfigurationSource( | ||
testSupported, testAliases, testAliasMapping, new HashMap<>()); | ||
ConfigHelper.get().setConfigurationSource(testSource); | ||
strictness = ConfigHelper.get().configInversionStrictFlag(); | ||
ConfigHelper.get().setConfigInversionStrict(ConfigHelper.StrictnessPolicy.STRICT); | ||
} | ||
|
||
@AfterAll | ||
static void tearDown() { | ||
ConfigHelper.get().resetToDefaults(); | ||
ConfigHelper.get().setConfigInversionStrict(strictness); | ||
} | ||
|
||
@AfterEach | ||
void reset() { | ||
ConfigHelper.get().resetCache(); | ||
env.clear(); | ||
} | ||
|
||
@Test | ||
void testBasicConfigHelper() { | ||
env.set(TEST_DD_VAR, TEST_DD_VAR_VAL); | ||
env.set(TEST_OTEL_VAR, TEST_OTEL_VAR_VAL); | ||
env.set(TEST_REGULAR_VAR, TEST_REGULAR_VAR_VAL); | ||
|
||
assertEquals(TEST_DD_VAR_VAL, ConfigHelper.get().getEnvironmentVariable(TEST_DD_VAR)); | ||
assertEquals(TEST_OTEL_VAR_VAL, ConfigHelper.get().getEnvironmentVariable(TEST_OTEL_VAR)); | ||
assertEquals(TEST_REGULAR_VAR_VAL, ConfigHelper.get().getEnvironmentVariable(TEST_REGULAR_VAR)); | ||
|
||
Map<String, String> result = ConfigHelper.get().getEnvironmentVariables(); | ||
assertEquals(TEST_DD_VAR_VAL, result.get(TEST_DD_VAR)); | ||
assertEquals(TEST_OTEL_VAR_VAL, result.get(TEST_OTEL_VAR)); | ||
assertEquals(TEST_REGULAR_VAR_VAL, result.get(TEST_REGULAR_VAR)); | ||
} | ||
|
||
@Test | ||
void testAliasSupport() { | ||
env.set(ALIAS_DD_VAR, ALIAS_DD_VAL); | ||
|
||
assertEquals(ALIAS_DD_VAL, ConfigHelper.get().getEnvironmentVariable(TEST_DD_VAR)); | ||
Map<String, String> result = ConfigHelper.get().getEnvironmentVariables(); | ||
assertEquals(ALIAS_DD_VAL, result.get(TEST_DD_VAR)); | ||
assertFalse(result.containsKey(ALIAS_DD_VAR)); | ||
} | ||
|
||
@Test | ||
void testMainConfigPrecedence() { | ||
// When both main variable and alias are set, main should take precedence | ||
env.set(TEST_DD_VAR, TEST_DD_VAR_VAL); | ||
env.set(ALIAS_DD_VAR, ALIAS_DD_VAL); | ||
|
||
assertEquals(TEST_DD_VAR_VAL, ConfigHelper.get().getEnvironmentVariable(TEST_DD_VAR)); | ||
Map<String, String> result = ConfigHelper.get().getEnvironmentVariables(); | ||
assertEquals(TEST_DD_VAR_VAL, result.get(TEST_DD_VAR)); | ||
assertFalse(result.containsKey(ALIAS_DD_VAR)); | ||
} | ||
|
||
@Test | ||
void testNonDDAliases() { | ||
env.set(NON_DD_ALIAS_VAR, NON_DD_ALIAS_VAL); | ||
|
||
assertEquals(NON_DD_ALIAS_VAL, ConfigHelper.get().getEnvironmentVariable(TEST_DD_VAR)); | ||
Map<String, String> result = ConfigHelper.get().getEnvironmentVariables(); | ||
assertEquals(NON_DD_ALIAS_VAL, result.get(TEST_DD_VAR)); | ||
assertFalse(result.containsKey(NON_DD_ALIAS_VAR)); | ||
} | ||
|
||
@Test | ||
void testAliasesWithoutPresentAliases() { | ||
Map<String, String> result = ConfigHelper.get().getEnvironmentVariables(); | ||
assertFalse(result.containsKey(ALIAS_DD_VAR)); | ||
} | ||
|
||
@Test | ||
void testAliasWithEmptyList() { | ||
Map<String, List<String>> aliasMap = new HashMap<>(); | ||
aliasMap.put("EMPTY_ALIAS_CONFIG", new ArrayList<>()); | ||
|
||
ConfigHelper.get() | ||
.setConfigurationSource( | ||
new TestSupportedConfigurationSource( | ||
new HashSet<>(), aliasMap, new HashMap<>(), new HashMap<>())); | ||
|
||
assertNull(ConfigHelper.get().getEnvironmentVariable("EMPTY_ALIAS_CONFIG")); | ||
|
||
// Cleanup | ||
ConfigHelper.get().setConfigurationSource(testSource); | ||
} | ||
|
||
@Test | ||
void testAliasSkippedWhenBaseAlreadyPresent() { | ||
env.set(TEST_DD_VAR, TEST_DD_VAR_VAL); | ||
env.set(NON_DD_ALIAS_VAR, NON_DD_ALIAS_VAL); | ||
|
||
Map<String, String> result = ConfigHelper.get().getEnvironmentVariables(); | ||
assertEquals(TEST_DD_VAR_VAL, result.get(TEST_DD_VAR)); | ||
assertFalse(result.containsKey(NON_DD_ALIAS_VAR)); | ||
} | ||
|
||
@Test | ||
void testInconsistentAliasesAndAliasMapping() { | ||
env.set(NEW_ALIAS_KEY_2, "some_value"); | ||
|
||
Map<String, String> result = ConfigHelper.get().getEnvironmentVariables(); | ||
|
||
assertFalse(result.containsKey(NEW_ALIAS_KEY_2)); | ||
assertFalse(result.containsKey(NEW_ALIAS_TARGET)); | ||
} | ||
|
||
@Test | ||
void testUnsupportedEnvWarningNotInTestMode() { | ||
ConfigHelper.get().setConfigInversionStrict(ConfigHelper.StrictnessPolicy.TEST); | ||
|
||
env.set("DD_FAKE_VAR", "banana"); | ||
|
||
// Should allow unsupported variable in TEST mode | ||
assertEquals("banana", ConfigHelper.get().getEnvironmentVariable("DD_FAKE_VAR")); | ||
|
||
// Cleanup | ||
ConfigHelper.get().setConfigInversionStrict(ConfigHelper.StrictnessPolicy.STRICT); | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.