Skip to content

Commit 17a02ad

Browse files
committed
Warn on agentless authentication failures
1 parent f9e70e6 commit 17a02ad

3 files changed

Lines changed: 70 additions & 5 deletions

File tree

products/feature-flagging/feature-flagging-lib/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ dependencies {
2020
api(project(":communication"))
2121
implementation(project(":internal-api"))
2222
api(project(":products:feature-flagging:feature-flagging-bootstrap"))
23+
implementation(project(":utils:logging-utils"))
2324
api(project(":utils:queue-utils"))
2425

2526
compileOnly(project(":dd-trace-core")) // shading does not work with this one

products/feature-flagging/feature-flagging-lib/src/main/java/com/datadog/featureflag/AgentlessConfigurationSource.java

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import static datadog.trace.util.AgentThreadFactory.AgentThread.FEATURE_FLAG_CONFIGURATION_POLLER;
55

66
import datadog.communication.http.OkHttpUtils;
7+
import datadog.logging.RatelimitedLogger;
78
import datadog.trace.api.Config;
89
import datadog.trace.api.featureflag.FeatureFlaggingGateway;
910
import datadog.trace.api.featureflag.ufc.v1.ServerConfiguration;
@@ -38,6 +39,7 @@ final class AgentlessConfigurationSource implements ConfigurationSourceService {
3839
private static final String DATADOG_API_SERVER_DISTRIBUTION_PATH =
3940
"/api/v2/feature-flagging/config/server-distribution";
4041
private static final int MAX_ATTEMPTS = 3;
42+
private static final int MINUTES_BETWEEN_AUTH_WARNINGS = 5;
4143
private static final long FIRST_RETRY_MIN_MILLIS = 2_000;
4244
private static final long FIRST_RETRY_MAX_MILLIS = 10_000;
4345
private static final long SECOND_RETRY_MIN_MILLIS = 5_000;
@@ -51,6 +53,7 @@ final class AgentlessConfigurationSource implements ConfigurationSourceService {
5153
private final ScheduledExecutorService executor;
5254
private final RetrySleeper retrySleeper;
5355
private final DoubleSupplier jitter;
56+
private final RatelimitedLogger ratelimitedLogger;
5457
private final Object lifecycleLock = new Object();
5558
private final AtomicBoolean polling = new AtomicBoolean();
5659
private volatile boolean closed;
@@ -73,7 +76,8 @@ private AgentlessConfigurationSource(final Config config, final HttpUrl endpoint
7376
Executors.newSingleThreadScheduledExecutor(
7477
new AgentThreadFactory(FEATURE_FLAG_CONFIGURATION_POLLER)),
7578
TimeUnit.MILLISECONDS::sleep,
76-
() -> ThreadLocalRandom.current().nextDouble(1 - RETRY_JITTER, 1 + RETRY_JITTER));
79+
() -> ThreadLocalRandom.current().nextDouble(1 - RETRY_JITTER, 1 + RETRY_JITTER),
80+
new RatelimitedLogger(LOGGER, MINUTES_BETWEEN_AUTH_WARNINGS, TimeUnit.MINUTES));
7781
}
7882

7983
AgentlessConfigurationSource(
@@ -89,7 +93,8 @@ private AgentlessConfigurationSource(final Config config, final HttpUrl endpoint
8993
client,
9094
executor,
9195
TimeUnit.MILLISECONDS::sleep,
92-
() -> 1.0);
96+
() -> 1.0,
97+
new RatelimitedLogger(LOGGER, MINUTES_BETWEEN_AUTH_WARNINGS, TimeUnit.MINUTES));
9398
}
9499

95100
AgentlessConfigurationSource(
@@ -100,13 +105,34 @@ private AgentlessConfigurationSource(final Config config, final HttpUrl endpoint
100105
final ScheduledExecutorService executor,
101106
final RetrySleeper retrySleeper,
102107
final DoubleSupplier jitter) {
108+
this(
109+
endpoint,
110+
config,
111+
pollIntervalMillis,
112+
client,
113+
executor,
114+
retrySleeper,
115+
jitter,
116+
new RatelimitedLogger(LOGGER, MINUTES_BETWEEN_AUTH_WARNINGS, TimeUnit.MINUTES));
117+
}
118+
119+
AgentlessConfigurationSource(
120+
final HttpUrl endpoint,
121+
final Config config,
122+
final long pollIntervalMillis,
123+
final UfcHttpClient client,
124+
final ScheduledExecutorService executor,
125+
final RetrySleeper retrySleeper,
126+
final DoubleSupplier jitter,
127+
final RatelimitedLogger ratelimitedLogger) {
103128
this.endpoint = endpoint;
104129
this.config = config;
105130
this.pollIntervalMillis = pollIntervalMillis;
106131
this.client = client;
107132
this.executor = executor;
108133
this.retrySleeper = retrySleeper;
109134
this.jitter = jitter;
135+
this.ratelimitedLogger = ratelimitedLogger;
110136
}
111137

112138
@Override
@@ -207,9 +233,13 @@ private boolean apply(final UfcHttpResponse response) {
207233
return true;
208234
}
209235
if (response.status == HttpURLConnection.HTTP_UNAUTHORIZED
210-
|| response.status == HttpURLConnection.HTTP_FORBIDDEN
211-
|| response.status != HttpURLConnection.HTTP_OK
212-
|| response.body == null) {
236+
|| response.status == HttpURLConnection.HTTP_FORBIDDEN) {
237+
ratelimitedLogger.warn(
238+
"Feature Flagging agentless endpoint returned HTTP {}; verify DD_API_KEY is configured and valid",
239+
response.status);
240+
return false;
241+
}
242+
if (response.status != HttpURLConnection.HTTP_OK || response.body == null) {
213243
return false;
214244
}
215245
final ServerConfiguration configuration;

products/feature-flagging/feature-flagging-lib/src/test/java/com/datadog/featureflag/AgentlessConfigurationSourceTest.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import static org.mockito.Mockito.verifyNoInteractions;
1616

1717
import datadog.communication.http.OkHttpUtils;
18+
import datadog.logging.RatelimitedLogger;
1819
import datadog.trace.agent.test.server.http.JavaTestHttpServer;
1920
import datadog.trace.api.Config;
2021
import datadog.trace.api.featureflag.FeatureFlaggingGateway;
@@ -413,6 +414,39 @@ void rejectsForbiddenNonOkMissingBodyAndNullConfiguration() throws Exception {
413414
verifyNoInteractions(listener);
414415
}
415416

417+
@Test
418+
void warnsRateLimitedOnUnauthorizedAndForbidden() throws Exception {
419+
final RatelimitedLogger ratelimitedLogger = mock(RatelimitedLogger.class);
420+
final FakeClient client = new FakeClient(response(401, null, null), response(403, null, null));
421+
final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
422+
final AgentlessConfigurationSource service =
423+
new AgentlessConfigurationSource(
424+
HttpUrl.get("http://localhost" + CONFIG_PATH),
425+
config(),
426+
30_000,
427+
client,
428+
executor,
429+
delay -> {},
430+
() -> 1.0,
431+
ratelimitedLogger);
432+
433+
try {
434+
assertFalse(service.pollOnce());
435+
assertFalse(service.pollOnce());
436+
437+
verify(ratelimitedLogger)
438+
.warn(
439+
"Feature Flagging agentless endpoint returned HTTP {}; verify DD_API_KEY is configured and valid",
440+
HttpURLConnection.HTTP_UNAUTHORIZED);
441+
verify(ratelimitedLogger)
442+
.warn(
443+
"Feature Flagging agentless endpoint returned HTTP {}; verify DD_API_KEY is configured and valid",
444+
HttpURLConnection.HTTP_FORBIDDEN);
445+
} finally {
446+
service.close();
447+
}
448+
}
449+
416450
@Test
417451
void retriesTimeoutBeforeApplyingConfig() throws Exception {
418452
final FakeClient client =

0 commit comments

Comments
 (0)