diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilityServices.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilityServices.java index 6fa99d27fd3..c2c4d0cccbe 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilityServices.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilityServices.java @@ -1,13 +1,8 @@ package datadog.trace.civisibility; -import com.squareup.moshi.JsonAdapter; -import com.squareup.moshi.Moshi; -import com.squareup.moshi.Types; import datadog.communication.BackendApi; import datadog.communication.BackendApiFactory; import datadog.communication.ddagent.SharedCommunicationObjects; -import datadog.communication.http.HttpRetryPolicy; -import datadog.communication.http.OkHttpUtils; import datadog.communication.util.IOUtils; import datadog.trace.api.Config; import datadog.trace.api.civisibility.telemetry.CiVisibilityCountMetric; @@ -15,6 +10,7 @@ import datadog.trace.api.civisibility.telemetry.tag.Command; import datadog.trace.api.git.GitInfoProvider; import datadog.trace.api.intake.Intake; +import datadog.trace.bootstrap.config.provider.civisibility.CiEnvironmentVariables; import datadog.trace.civisibility.ci.CIProviderInfoFactory; import datadog.trace.civisibility.ci.env.CiEnvironment; import datadog.trace.civisibility.ci.env.CiEnvironmentImpl; @@ -33,21 +29,22 @@ import datadog.trace.civisibility.source.ByteCodeLinesResolver; import datadog.trace.civisibility.source.CompilerAidedLinesResolver; import datadog.trace.civisibility.source.LinesResolver; -import datadog.trace.civisibility.source.index.*; +import datadog.trace.civisibility.source.index.CachingRepoIndexBuilderFactory; +import datadog.trace.civisibility.source.index.ConventionBasedResourceResolver; +import datadog.trace.civisibility.source.index.PackageResolver; +import datadog.trace.civisibility.source.index.PackageResolverImpl; +import datadog.trace.civisibility.source.index.RepoIndexFetcher; +import datadog.trace.civisibility.source.index.RepoIndexProvider; +import datadog.trace.civisibility.source.index.ResourceResolver; import datadog.trace.civisibility.utils.ShellCommandExecutor; import java.io.File; -import java.lang.reflect.Type; import java.net.InetSocketAddress; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Path; -import java.util.Collections; import java.util.Map; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import okhttp3.HttpUrl; -import okhttp3.OkHttpClient; -import okhttp3.Request; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -61,8 +58,6 @@ public class CiVisibilityServices { private static final String GIT_FOLDER_NAME = ".git"; - static final String DD_ENV_VARS_PROVIDER_KEY_HEADER = "DD-Env-Vars-Provider-Key"; - final ProcessHierarchy processHierarchy; final Config config; final CiVisibilityMetricCollector metricCollector; @@ -90,7 +85,7 @@ public class CiVisibilityServices { this.jvmInfoFactory = new CachingJvmInfoFactory(config, new JvmInfoFactoryImpl()); this.gitClientFactory = buildGitClientFactory(config, metricCollector); - this.environment = buildCiEnvironment(config, sco); + this.environment = buildCiEnvironment(); this.ciProviderInfoFactory = new CIProviderInfoFactory(config, environment); this.linesResolver = new BestEffortLinesResolver(new CompilerAidedLinesResolver(), new ByteCodeLinesResolver()); @@ -145,50 +140,13 @@ private static GitClient.Factory buildGitClientFactory( } @Nonnull - private static CiEnvironment buildCiEnvironment(Config config, SharedCommunicationObjects sco) { - CiEnvironment localEnvironment = CiEnvironmentImpl.local(); - String remoteEnvVarsProviderUrl = config.getCiVisibilityRemoteEnvVarsProviderUrl(); - if (remoteEnvVarsProviderUrl != null) { - String remoteEnvVarsProviderKey = config.getCiVisibilityRemoteEnvVarsProviderKey(); - CiEnvironment remoteEnvironment = - new CiEnvironmentImpl( - getRemoteEnvironment( - remoteEnvVarsProviderUrl, remoteEnvVarsProviderKey, sco.agentHttpClient)); - return new CompositeCiEnvironment(remoteEnvironment, localEnvironment); + private static CiEnvironment buildCiEnvironment() { + Map remoteEnvironment = CiEnvironmentVariables.getAll(); + if (remoteEnvironment != null) { + return new CompositeCiEnvironment( + new CiEnvironmentImpl(remoteEnvironment), CiEnvironmentImpl.local()); } else { - return localEnvironment; - } - } - - static Map getRemoteEnvironment(String url, String key, OkHttpClient httpClient) { - HttpRetryPolicy.Factory retryPolicyFactory = new HttpRetryPolicy.Factory(5, 100, 2.0, true); - - HttpUrl httpUrl = HttpUrl.get(url); - Request request = - new Request.Builder() - .url(httpUrl) - .header(DD_ENV_VARS_PROVIDER_KEY_HEADER, key) - .get() - .build(); - try (okhttp3.Response response = - OkHttpUtils.sendWithRetries(httpClient, retryPolicyFactory, request)) { - - if (response.isSuccessful()) { - Moshi moshi = new Moshi.Builder().build(); - Type type = Types.newParameterizedType(Map.class, String.class, String.class); - JsonAdapter> adapter = moshi.adapter(type); - return adapter.fromJson(response.body().source()); - } else { - logger.warn( - "Could not get remote CI environment (HTTP code {}) {}", - response.code(), - response.body() != null ? ": " + response.body().string() : ""); - return Collections.emptyMap(); - } - - } catch (Exception e) { - logger.warn("Could not get remote CI environment", e); - return Collections.emptyMap(); + return CiEnvironmentImpl.local(); } } diff --git a/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/CiVisibilityServicesTest.groovy b/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/CiVisibilityServicesTest.groovy deleted file mode 100644 index 8e7cbf71a5a..00000000000 --- a/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/CiVisibilityServicesTest.groovy +++ /dev/null @@ -1,33 +0,0 @@ -package datadog.trace.civisibility - -import datadog.trace.agent.test.server.http.TestHttpServer -import datadog.trace.agent.test.utils.OkHttpUtils -import spock.lang.Specification - -import static datadog.trace.agent.test.server.http.TestHttpServer.httpServer - -class CiVisibilityServicesTest extends Specification { - - def "test get remote environment"() { - given: - def key = "the-key" - - TestHttpServer remoteEnvironmentServer = httpServer { - handlers { - prefix("/") { - if (request.getHeader(CiVisibilityServices.DD_ENV_VARS_PROVIDER_KEY_HEADER) == key) { - response.status(200).send(""" { "a": 1, "b": "2" } """) - } else { - response.status(404).send() - } - } - } - } - - expect: - CiVisibilityServices.getRemoteEnvironment(remoteEnvironmentServer.address.toString(), key, OkHttpUtils.client()) == ["a": "1", "b": "2"] - - cleanup: - remoteEnvironmentServer.stop() - } -} diff --git a/dd-trace-api/src/main/java/datadog/trace/api/config/CiVisibilityConfig.java b/dd-trace-api/src/main/java/datadog/trace/api/config/CiVisibilityConfig.java index 31ee79215a4..457ca333b84 100644 --- a/dd-trace-api/src/main/java/datadog/trace/api/config/CiVisibilityConfig.java +++ b/dd-trace-api/src/main/java/datadog/trace/api/config/CiVisibilityConfig.java @@ -76,10 +76,6 @@ public final class CiVisibilityConfig { "civisibility.rum.flush.wait.millis"; public static final String CIVISIBILITY_AUTO_INSTRUMENTATION_PROVIDER = "civisibility.auto.instrumentation.provider"; - public static final String CIVISIBILITY_REMOTE_ENV_VARS_PROVIDER_URL = - "civisibility.remote.env.vars.provider.url"; - public static final String CIVISIBILITY_REMOTE_ENV_VARS_PROVIDER_KEY = - "civisibility.remote.env.vars.provider.key"; public static final String CIVISIBILITY_TEST_ORDER = "civisibility.test.order"; public static final String CIVISIBILITY_SCALATEST_FORK_MONITOR_ENABLED = "civisibility.scalatest.fork.monitor.enabled"; diff --git a/internal-api/src/main/java/datadog/trace/api/Config.java b/internal-api/src/main/java/datadog/trace/api/Config.java index ef0a02114da..a9728796c45 100644 --- a/internal-api/src/main/java/datadog/trace/api/Config.java +++ b/internal-api/src/main/java/datadog/trace/api/Config.java @@ -264,8 +264,6 @@ import static datadog.trace.api.config.CiVisibilityConfig.CIVISIBILITY_JVM_INFO_CACHE_SIZE; import static datadog.trace.api.config.CiVisibilityConfig.CIVISIBILITY_KNOWN_TESTS_REQUEST_ENABLED; import static datadog.trace.api.config.CiVisibilityConfig.CIVISIBILITY_MODULE_NAME; -import static datadog.trace.api.config.CiVisibilityConfig.CIVISIBILITY_REMOTE_ENV_VARS_PROVIDER_KEY; -import static datadog.trace.api.config.CiVisibilityConfig.CIVISIBILITY_REMOTE_ENV_VARS_PROVIDER_URL; import static datadog.trace.api.config.CiVisibilityConfig.CIVISIBILITY_REPO_INDEX_DUPLICATE_KEY_CHECK_ENABLED; import static datadog.trace.api.config.CiVisibilityConfig.CIVISIBILITY_REPO_INDEX_FOLLOW_SYMLINKS; import static datadog.trace.api.config.CiVisibilityConfig.CIVISIBILITY_RESOURCE_FOLDER_NAMES; @@ -1054,8 +1052,6 @@ public static String getHostName() { private final boolean ciVisibilityTelemetryEnabled; private final long ciVisibilityRumFlushWaitMillis; private final boolean ciVisibilityAutoInjected; - private final String ciVisibilityRemoteEnvVarsProviderUrl; - private final String ciVisibilityRemoteEnvVarsProviderKey; private final String ciVisibilityTestOrder; private final boolean ciVisibilityTestManagementEnabled; private final Integer ciVisibilityTestManagementAttemptToFixRetries; @@ -2363,10 +2359,6 @@ PROFILING_DATADOG_PROFILER_ENABLED, isDatadogProfilerSafeInCurrentEnvironment()) configProvider.getLong(CIVISIBILITY_RUM_FLUSH_WAIT_MILLIS, 500); ciVisibilityAutoInjected = Strings.isNotBlank(configProvider.getString(CIVISIBILITY_AUTO_INSTRUMENTATION_PROVIDER)); - ciVisibilityRemoteEnvVarsProviderUrl = - configProvider.getString(CIVISIBILITY_REMOTE_ENV_VARS_PROVIDER_URL); - ciVisibilityRemoteEnvVarsProviderKey = - configProvider.getString(CIVISIBILITY_REMOTE_ENV_VARS_PROVIDER_KEY); ciVisibilityTestOrder = configProvider.getString(CIVISIBILITY_TEST_ORDER); ciVisibilityTestManagementEnabled = configProvider.getBoolean(TEST_MANAGEMENT_ENABLED, true); ciVisibilityTestManagementAttemptToFixRetries = @@ -4039,14 +4031,6 @@ public boolean isCiVisibilityAutoInjected() { return ciVisibilityAutoInjected; } - public String getCiVisibilityRemoteEnvVarsProviderUrl() { - return ciVisibilityRemoteEnvVarsProviderUrl; - } - - public String getCiVisibilityRemoteEnvVarsProviderKey() { - return ciVisibilityRemoteEnvVarsProviderKey; - } - public String getCiVisibilityTestOrder() { return ciVisibilityTestOrder; } diff --git a/utils/config-utils/build.gradle.kts b/utils/config-utils/build.gradle.kts index 8faf1c5c6f3..7ef5a9b0e34 100644 --- a/utils/config-utils/build.gradle.kts +++ b/utils/config-utils/build.gradle.kts @@ -16,6 +16,7 @@ val excludedClassesCoverage by extra( // tested in internal-api "datadog.trace.api.telemetry.OtelEnvMetricCollectorProvider", "datadog.trace.api.telemetry.ConfigInversionMetricCollectorProvider", + "datadog.trace.bootstrap.config.provider.civisibility.CiEnvironmentVariables", "datadog.trace.bootstrap.config.provider.CapturedEnvironmentConfigSource", "datadog.trace.bootstrap.config.provider.ConfigConverter.ValueOfLookup", // tested in internal-api @@ -25,6 +26,7 @@ val excludedClassesCoverage by extra( "datadog.trace.bootstrap.config.provider.ConfigProvider.Singleton", "datadog.trace.bootstrap.config.provider.ConfigProvider.Source", "datadog.trace.bootstrap.config.provider.EnvironmentConfigSource", + "datadog.trace.bootstrap.config.provider.MapConfigSource", // tested in internal-api "datadog.trace.bootstrap.config.provider.OtelEnvironmentConfigSource", "datadog.trace.bootstrap.config.provider.stableconfig.Selector", @@ -58,4 +60,5 @@ dependencies { testImplementation(project(":utils:test-utils")) testImplementation("org.snakeyaml:snakeyaml-engine:2.9") + testImplementation("com.squareup.okhttp3:mockwebserver:${libs.versions.okhttp.legacy.get()}") } diff --git a/utils/config-utils/src/main/java/datadog/trace/bootstrap/config/provider/ConfigProvider.java b/utils/config-utils/src/main/java/datadog/trace/bootstrap/config/provider/ConfigProvider.java index ade045011db..0f25c548f5b 100644 --- a/utils/config-utils/src/main/java/datadog/trace/bootstrap/config/provider/ConfigProvider.java +++ b/utils/config-utils/src/main/java/datadog/trace/bootstrap/config/provider/ConfigProvider.java @@ -7,6 +7,8 @@ import datadog.environment.SystemProperties; import datadog.trace.api.ConfigCollector; import datadog.trace.api.ConfigOrigin; +import datadog.trace.bootstrap.config.provider.civisibility.CiEnvironmentVariables; +import datadog.trace.util.ConfigStrings; import de.thetaphi.forbiddenapis.SuppressForbidden; import java.io.File; import java.io.FileNotFoundException; @@ -20,6 +22,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Properties; import java.util.Set; import org.slf4j.Logger; @@ -495,24 +498,32 @@ public static ConfigProvider createDefault() { Properties configProperties = loadConfigurationFile( new ConfigProvider(new SystemPropertiesConfigSource(), new EnvironmentConfigSource())); - if (configProperties.isEmpty()) { - return new ConfigProvider( - new SystemPropertiesConfigSource(), - StableConfigSource.FLEET, - new EnvironmentConfigSource(), - new OtelEnvironmentConfigSource(), - StableConfigSource.LOCAL, - new CapturedEnvironmentConfigSource()); - } else { - return new ConfigProvider( - new SystemPropertiesConfigSource(), - StableConfigSource.FLEET, - new EnvironmentConfigSource(), - new PropertiesConfigSource(configProperties, true), - new OtelEnvironmentConfigSource(configProperties), - StableConfigSource.LOCAL, - new CapturedEnvironmentConfigSource()); - } + ConfigProvider.Source propertiesSource = + !configProperties.isEmpty() ? new PropertiesConfigSource(configProperties, true) : null; + + Map ciEnvironmentVariables = CiEnvironmentVariables.getAll(); + ConfigProvider.Source ciEnvironmentSource = + ciEnvironmentVariables != null + ? new MapConfigSource( + ciEnvironmentVariables, + ConfigStrings::propertyNameToEnvironmentVariableName, + ConfigOrigin.ENV) + : null; + + return new ConfigProvider( + filterNonNull( + new SystemPropertiesConfigSource(), + StableConfigSource.FLEET, + ciEnvironmentSource, + new EnvironmentConfigSource(), + propertiesSource, + new OtelEnvironmentConfigSource(), + StableConfigSource.LOCAL, + new CapturedEnvironmentConfigSource())); + } + + private static ConfigProvider.Source[] filterNonNull(ConfigProvider.Source... values) { + return Arrays.stream(values).filter(Objects::nonNull).toArray(Source[]::new); } public static ConfigProvider withoutCollector() { diff --git a/utils/config-utils/src/main/java/datadog/trace/bootstrap/config/provider/MapConfigSource.java b/utils/config-utils/src/main/java/datadog/trace/bootstrap/config/provider/MapConfigSource.java new file mode 100644 index 00000000000..00490b50819 --- /dev/null +++ b/utils/config-utils/src/main/java/datadog/trace/bootstrap/config/provider/MapConfigSource.java @@ -0,0 +1,31 @@ +package datadog.trace.bootstrap.config.provider; + +import datadog.trace.api.ConfigOrigin; +import java.util.Map; +import java.util.function.Function; + +final class MapConfigSource extends ConfigProvider.Source { + + private final Map properties; + private final Function keyTransformer; + private final ConfigOrigin origin; + + MapConfigSource( + Map properties, + Function keyTransformer, + ConfigOrigin origin) { + this.properties = properties; + this.keyTransformer = keyTransformer; + this.origin = origin; + } + + @Override + protected String get(String key) { + return properties.get(keyTransformer.apply(key)); + } + + @Override + public ConfigOrigin origin() { + return origin; + } +} diff --git a/utils/config-utils/src/main/java/datadog/trace/bootstrap/config/provider/civisibility/CiEnvironmentVariables.java b/utils/config-utils/src/main/java/datadog/trace/bootstrap/config/provider/civisibility/CiEnvironmentVariables.java new file mode 100644 index 00000000000..9c7ba095936 --- /dev/null +++ b/utils/config-utils/src/main/java/datadog/trace/bootstrap/config/provider/civisibility/CiEnvironmentVariables.java @@ -0,0 +1,159 @@ +package datadog.trace.bootstrap.config.provider.civisibility; + +import datadog.environment.EnvironmentVariables; +import datadog.trace.util.ConfigStrings; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.Callable; +import java.util.stream.Collectors; +import javax.annotation.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CiEnvironmentVariables { + + private static final Logger logger = LoggerFactory.getLogger(CiEnvironmentVariables.class); + + static final String CIVISIBILITY_REMOTE_ENV_VARS_PROVIDER_URL = + "dd.civisibility.remote.env.vars.provider.url"; + static final String CIVISIBILITY_REMOTE_ENV_VARS_PROVIDER_KEY = + "dd.civisibility.remote.env.vars.provider.key"; + + static final String DD_ENV_VARS_PROVIDER_KEY_HEADER = "DD-Env-Vars-Provider-Key"; + static final String ACCEPT_HEADER = "Accept"; + + private static final int CONNECT_TIMEOUT_MILLIS = 5000; + private static final int READ_TIMEOUT_MILLIS = 10000; + + private static final Map REMOTE_ENVIRONMENT; + + static { + String url = getConfigValue(CIVISIBILITY_REMOTE_ENV_VARS_PROVIDER_URL); + String key = getConfigValue(CIVISIBILITY_REMOTE_ENV_VARS_PROVIDER_KEY); + if (url != null && key != null) { + REMOTE_ENVIRONMENT = + getRemoteEnvironmentWithRetries(url, key, new RetryPolicy(500, 5, 2), null); + } else { + REMOTE_ENVIRONMENT = null; + } + } + + private static String getConfigValue(String propertyName) { + String propertyValue = System.getProperty(propertyName); + if (propertyValue != null) { + return propertyValue; + } + return EnvironmentVariables.get(ConfigStrings.toEnvVar(propertyName)); + } + + static Map getRemoteEnvironmentWithRetries( + String url, String key, RetryPolicy retryPolicy, Map fallbackValue) { + return doWithBackoffRetries(() -> getRemoteEnvironment(url, key), retryPolicy, fallbackValue); + } + + static final class RetryPolicy { + long delayMillis; + int maxAttempts; + double backoffFactor; + + public RetryPolicy(long delayMillis, int maxAttempts, double backoffFactor) { + this.delayMillis = delayMillis; + this.maxAttempts = maxAttempts; + this.backoffFactor = backoffFactor; + } + } + + private static T doWithBackoffRetries( + Callable action, RetryPolicy retryPolicy, T fallbackValue) { + long delayMillis = retryPolicy.delayMillis; + for (int i = 0; i < retryPolicy.maxAttempts; i++) { + if (Thread.currentThread().isInterrupted()) { + logger.warn("Interrupted while trying to read remote environment"); + return fallbackValue; + } + try { + return action.call(); + + } catch (Exception e) { + logger.warn("Error while trying to read remote environment", e); + sleep(delayMillis); + delayMillis = Math.round(delayMillis * retryPolicy.backoffFactor); + } + } + return fallbackValue; + } + + private static void sleep(long delayMillis) { + try { + Thread.sleep(delayMillis); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + private static Map getRemoteEnvironment(String url, String key) throws Exception { + HttpURLConnection conn = null; + try { + URL target = new URL(url); + conn = (HttpURLConnection) target.openConnection(); + conn.setRequestMethod("GET"); + conn.setRequestProperty(DD_ENV_VARS_PROVIDER_KEY_HEADER, key); + conn.setRequestProperty(ACCEPT_HEADER, "text/plain"); + conn.setConnectTimeout(CONNECT_TIMEOUT_MILLIS); + conn.setReadTimeout(READ_TIMEOUT_MILLIS); + + int code = conn.getResponseCode(); + if (code >= 200 && code < 300) { + Properties properties = new Properties(); + try (Reader r = new InputStreamReader(conn.getInputStream(), StandardCharsets.ISO_8859_1)) { + properties.load(r); + } + return asMap(properties); + + } else { + try (BufferedReader r = + new BufferedReader( + new InputStreamReader(conn.getErrorStream(), StandardCharsets.UTF_8))) { + String body = r.lines().collect(Collectors.joining("\n")); + throw new IOException( + String.format("Remote environment request failed (HTTP %d) %s", code, body)); + } + } + + } finally { + if (conn != null) { + conn.disconnect(); + } + } + } + + private static Map asMap(Properties properties) { + Map map = new HashMap<>(); + for (String name : properties.stringPropertyNames()) { + map.put(name, properties.getProperty(name)); + } + return map; + } + + @Nullable + public static String get(String name) { + if (REMOTE_ENVIRONMENT == null) { + return null; + } + return REMOTE_ENVIRONMENT.get(name); + } + + @Nullable + public static Map getAll() { + return REMOTE_ENVIRONMENT != null ? Collections.unmodifiableMap(REMOTE_ENVIRONMENT) : null; + } +} diff --git a/utils/config-utils/src/test/java/datadog/trace/bootstrap/config/provider/civisibility/CiEnvironmentVariablesTest.java b/utils/config-utils/src/test/java/datadog/trace/bootstrap/config/provider/civisibility/CiEnvironmentVariablesTest.java new file mode 100644 index 00000000000..8a913b0901b --- /dev/null +++ b/utils/config-utils/src/test/java/datadog/trace/bootstrap/config/provider/civisibility/CiEnvironmentVariablesTest.java @@ -0,0 +1,76 @@ +package datadog.trace.bootstrap.config.provider.civisibility; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; +import okhttp3.mockwebserver.Dispatcher; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import okhttp3.mockwebserver.RecordedRequest; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class CiEnvironmentVariablesTest { + + private static final String SECRET_KEY = "secret-key"; + + private static MockWebServer server; + + private static final AtomicInteger failedResponses = new AtomicInteger(0); + + @BeforeAll + public static void startServer() throws Exception { + server = new MockWebServer(); + server.setDispatcher( + new Dispatcher() { + @Override + public MockResponse dispatch(RecordedRequest req) { + if (failedResponses.getAndDecrement() > 0) { + return new MockResponse().setResponseCode(500); + } + if (SECRET_KEY.equals( + req.getHeader(CiEnvironmentVariables.DD_ENV_VARS_PROVIDER_KEY_HEADER))) { + return new MockResponse().setResponseCode(200).setBody("a=1\nb=2"); + } + return new MockResponse().setResponseCode(403); + } + }); + server.start(); + } + + @AfterAll + public static void stopServer() throws Exception { + server.shutdown(); + } + + @Test + void testGetEnvironment() { + failedResponses.set(1); // to test retries + + Map env = + CiEnvironmentVariables.getRemoteEnvironmentWithRetries( + server.url("/").toString(), + SECRET_KEY, + new CiEnvironmentVariables.RetryPolicy(2, 3, 2), + null); + assertEquals(2, env.size()); + assertEquals("1", env.get("a")); + assertEquals("2", env.get("b")); + } + + @Test + void testFailedGetEnvironment() { + failedResponses.set(3); + + Map env = + CiEnvironmentVariables.getRemoteEnvironmentWithRetries( + server.url("/").toString(), + SECRET_KEY, + new CiEnvironmentVariables.RetryPolicy(2, 3, 2), + null); + assertNull(env); + } +}