Skip to content

Commit fd9b9d2

Browse files
authored
feat(instance metadata service conection) : Added env variable, system and profile file property for AWS EC2 Metatdat Service timeout (#5735)
* feat(instance metadata service conection) : Added env variable and system property for AWS EC2 Metatdat Service timeout * removed system printhln * Updated ConnectionUtils to support mock of this class * Updated the test cases and made constructor private * Cannot make clas final since its used in mock * Handled PR comments * updated the changelogs * Hanled zoes comments * Moved IMDS service timeout to Ec2MetaDataConfigProvider class * Fix sonar quebe issues * Removing reference of internal class in Public class java docs
1 parent 0366920 commit fd9b9d2

File tree

27 files changed

+864
-186
lines changed

27 files changed

+864
-186
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"type": "feature",
3+
"category": "AWS SDK for Java v2",
4+
"contributor": "",
5+
"description": "Added `AWS_METADATA_SERVICE_TIMEOUT` environment variable, System property and `metadata_service_timeout` Profile property to configure connection and read timeouts (in seconds) for both InstanceProfileCredentialsProvider and IMDS client."
6+
}

core/auth/src/main/java/software/amazon/awssdk/auth/credentials/InstanceProfileCredentialsProvider.java

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
import software.amazon.awssdk.annotations.SdkPublicApi;
3232
import software.amazon.awssdk.annotations.SdkTestInternalApi;
3333
import software.amazon.awssdk.auth.credentials.internal.Ec2MetadataConfigProvider;
34-
import software.amazon.awssdk.auth.credentials.internal.Ec2MetadataDisableV1Resolver;
3534
import software.amazon.awssdk.auth.credentials.internal.HttpCredentialsLoader;
3635
import software.amazon.awssdk.auth.credentials.internal.HttpCredentialsLoader.LoadedCredentials;
3736
import software.amazon.awssdk.auth.credentials.internal.StaticResourcesEndpointProvider;
@@ -78,7 +77,6 @@ public final class InstanceProfileCredentialsProvider
7877
private final Clock clock;
7978
private final String endpoint;
8079
private final Ec2MetadataConfigProvider configProvider;
81-
private final Ec2MetadataDisableV1Resolver ec2MetadataDisableV1Resolver;
8280
private final HttpCredentialsLoader httpCredentialsLoader;
8381
private final CachedSupplier<AwsCredentials> credentialsCache;
8482

@@ -109,7 +107,6 @@ private InstanceProfileCredentialsProvider(BuilderImpl builder) {
109107
.profileFile(profileFile)
110108
.profileName(profileName)
111109
.build();
112-
this.ec2MetadataDisableV1Resolver = Ec2MetadataDisableV1Resolver.create(profileFile, profileName);
113110

114111
if (Boolean.TRUE.equals(builder.asyncCredentialUpdateEnabled)) {
115112
Validate.paramNotBlank(builder.asyncThreadName, "asyncThreadName");
@@ -211,9 +208,13 @@ private ResourcesEndpointProvider createEndpointProvider() {
211208
String token = getToken(imdsHostname);
212209
String[] securityCredentials = getSecurityCredentials(imdsHostname, token);
213210

214-
return new StaticResourcesEndpointProvider(URI.create(imdsHostname + SECURITY_CREDENTIALS_RESOURCE +
215-
securityCredentials[0]),
216-
getTokenHeaders(token));
211+
return StaticResourcesEndpointProvider.builder()
212+
.endpoint(URI.create(imdsHostname + SECURITY_CREDENTIALS_RESOURCE
213+
+ securityCredentials[0]))
214+
.headers(getTokenHeaders(token))
215+
.connectionTimeout(Duration.ofMillis(
216+
this.configProvider.serviceTimeout()))
217+
.build();
217218
}
218219

219220
private String getImdsEndpoint() {
@@ -226,8 +227,13 @@ private String getImdsEndpoint() {
226227

227228
private String getToken(String imdsHostname) {
228229
Map<String, String> tokenTtlHeaders = Collections.singletonMap(EC2_METADATA_TOKEN_TTL_HEADER, DEFAULT_TOKEN_TTL);
229-
ResourcesEndpointProvider tokenEndpoint = new StaticResourcesEndpointProvider(getTokenEndpoint(imdsHostname),
230-
tokenTtlHeaders);
230+
ResourcesEndpointProvider tokenEndpoint =
231+
StaticResourcesEndpointProvider.builder()
232+
.endpoint(getTokenEndpoint(imdsHostname))
233+
.headers(tokenTtlHeaders)
234+
.connectionTimeout(Duration.ofMillis(
235+
this.configProvider.serviceTimeout()))
236+
.build();
231237

232238
try {
233239
return HttpResourcesUtils.instance().readResource(tokenEndpoint, "PUT");
@@ -271,13 +277,16 @@ private String handleTokenErrorResponse(Exception e) {
271277
}
272278

273279
private boolean isInsecureFallbackDisabled() {
274-
return ec2MetadataDisableV1Resolver.resolve();
280+
return configProvider.isMetadataV1Disabled();
275281
}
276282

277283
private String[] getSecurityCredentials(String imdsHostname, String metadataToken) {
278284
ResourcesEndpointProvider securityCredentialsEndpoint =
279-
new StaticResourcesEndpointProvider(URI.create(imdsHostname + SECURITY_CREDENTIALS_RESOURCE),
280-
getTokenHeaders(metadataToken));
285+
StaticResourcesEndpointProvider.builder()
286+
.endpoint(URI.create(imdsHostname + SECURITY_CREDENTIALS_RESOURCE))
287+
.headers(getTokenHeaders(metadataToken))
288+
.connectionTimeout(Duration.ofMillis(this.configProvider.serviceTimeout()))
289+
.build();
281290

282291
String securityCredentialsList =
283292
invokeSafely(() -> HttpResourcesUtils.instance().readResource(securityCredentialsEndpoint));

core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/Ec2MetadataConfigProvider.java

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
package software.amazon.awssdk.auth.credentials.internal;
1717

18+
import java.time.Duration;
1819
import java.util.Optional;
1920
import java.util.function.Supplier;
2021
import software.amazon.awssdk.annotations.SdkInternalApi;
@@ -24,6 +25,8 @@
2425
import software.amazon.awssdk.profiles.ProfileFile;
2526
import software.amazon.awssdk.profiles.ProfileFileSystemSetting;
2627
import software.amazon.awssdk.profiles.ProfileProperty;
28+
import software.amazon.awssdk.utils.Lazy;
29+
import software.amazon.awssdk.utils.OptionalUtils;
2730

2831
@SdkInternalApi
2932
// TODO: Remove or consolidate this class with the one from the regions module.
@@ -41,9 +44,14 @@ public final class Ec2MetadataConfigProvider {
4144
private final Supplier<ProfileFile> profileFile;
4245
private final String profileName;
4346

47+
private final Lazy<Boolean> metadataV1Disabled;
48+
private final Lazy<Long> serviceTimeout;
49+
4450
private Ec2MetadataConfigProvider(Builder builder) {
4551
this.profileFile = builder.profileFile;
4652
this.profileName = builder.profileName;
53+
this.metadataV1Disabled = new Lazy<>(this::resolveMetadataV1Disabled);
54+
this.serviceTimeout = new Lazy<>(this::resolveServiceTimeout);
4755
}
4856

4957
public enum EndpointMode {
@@ -104,6 +112,84 @@ public String getEndpointOverride() {
104112
return configFileValue.orElse(null);
105113
}
106114

115+
/**
116+
* Resolves whether EC2 Metadata V1 is disabled.
117+
* @return true if EC2 Metadata V1 is disabled, false otherwise.
118+
*/
119+
public boolean isMetadataV1Disabled() {
120+
return metadataV1Disabled.getValue();
121+
}
122+
123+
/**
124+
* Resolves the EC2 Metadata Service Timeout in milliseconds.
125+
* @return the timeout value in milliseconds.
126+
*/
127+
public long serviceTimeout() {
128+
return serviceTimeout.getValue();
129+
}
130+
131+
// Internal resolution logic for Metadata V1 disabled
132+
private boolean resolveMetadataV1Disabled() {
133+
return OptionalUtils.firstPresent(
134+
fromSystemSettingsMetadataV1Disabled(),
135+
() -> fromProfileFileMetadataV1Disabled(profileFile, profileName)
136+
)
137+
.orElse(false);
138+
}
139+
140+
// Internal resolution logic for Service Timeout
141+
private long resolveServiceTimeout() {
142+
return OptionalUtils.firstPresent(
143+
fromSystemSettingsServiceTimeout(),
144+
() -> fromProfileFileServiceTimeout(profileFile, profileName)
145+
)
146+
.orElseGet(() -> parseTimeoutValue(SdkSystemSetting.AWS_METADATA_SERVICE_TIMEOUT.defaultValue()));
147+
}
148+
149+
// System settings resolution for Metadata V1 disabled
150+
private static Optional<Boolean> fromSystemSettingsMetadataV1Disabled() {
151+
return SdkSystemSetting.AWS_EC2_METADATA_V1_DISABLED.getBooleanValue();
152+
}
153+
154+
// Profile file resolution for Metadata V1 disabled
155+
private static Optional<Boolean> fromProfileFileMetadataV1Disabled(Supplier<ProfileFile> profileFile, String profileName) {
156+
return profileFile.get()
157+
.profile(profileName)
158+
.flatMap(p -> p.booleanProperty(ProfileProperty.EC2_METADATA_V1_DISABLED));
159+
}
160+
161+
// System settings resolution for Service Timeout
162+
private static Optional<Long> fromSystemSettingsServiceTimeout() {
163+
return SdkSystemSetting.AWS_METADATA_SERVICE_TIMEOUT.getNonDefaultStringValue()
164+
.map(Ec2MetadataConfigProvider::parseTimeoutValue);
165+
}
166+
167+
// Profile file resolution for Service Timeout
168+
private static Optional<Long> fromProfileFileServiceTimeout(Supplier<ProfileFile> profileFile, String profileName) {
169+
return profileFile.get()
170+
.profile(profileName)
171+
.flatMap(p -> p.property(ProfileProperty.METADATA_SERVICE_TIMEOUT))
172+
.map(Ec2MetadataConfigProvider::parseTimeoutValue);
173+
}
174+
175+
// Parses a timeout value from a string to milliseconds
176+
private static long parseTimeoutValue(String timeoutValue) {
177+
try {
178+
int timeoutSeconds = Integer.parseInt(timeoutValue);
179+
return Duration.ofSeconds(timeoutSeconds).toMillis();
180+
} catch (NumberFormatException e) {
181+
try {
182+
double timeoutSeconds = Double.parseDouble(timeoutValue);
183+
return Math.round(timeoutSeconds * 1000);
184+
} catch (NumberFormatException ignored) {
185+
throw new IllegalStateException(String.format(
186+
"Timeout value '%s' is not a valid integer or double.",
187+
timeoutValue
188+
));
189+
}
190+
}
191+
}
192+
107193
public static Builder builder() {
108194
return new Builder();
109195
}

core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/Ec2MetadataDisableV1Resolver.java

Lines changed: 0 additions & 63 deletions
This file was deleted.

core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/StaticResourcesEndpointProvider.java

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@
1717

1818
import java.io.IOException;
1919
import java.net.URI;
20+
import java.time.Duration;
2021
import java.util.Collections;
22+
import java.util.HashMap;
2123
import java.util.Map;
24+
import java.util.Optional;
2225
import software.amazon.awssdk.annotations.SdkInternalApi;
2326
import software.amazon.awssdk.regions.util.ResourcesEndpointProvider;
2427
import software.amazon.awssdk.utils.Validate;
@@ -27,14 +30,22 @@
2730
public final class StaticResourcesEndpointProvider implements ResourcesEndpointProvider {
2831
private final URI endpoint;
2932
private final Map<String, String> headers;
33+
private final Duration connectionTimeout;
3034

31-
public StaticResourcesEndpointProvider(URI endpoint,
32-
Map<String, String> additionalHeaders) {
35+
private StaticResourcesEndpointProvider(URI endpoint,
36+
Map<String, String> additionalHeaders,
37+
Duration customTimeout) {
3338
this.endpoint = Validate.paramNotNull(endpoint, "endpoint");
3439
this.headers = ResourcesEndpointProvider.super.headers();
3540
if (additionalHeaders != null) {
3641
this.headers.putAll(additionalHeaders);
3742
}
43+
this.connectionTimeout = customTimeout;
44+
}
45+
46+
@Override
47+
public Optional<Duration> connectionTimeout() {
48+
return Optional.ofNullable(connectionTimeout);
3849
}
3950

4051
@Override
@@ -46,4 +57,35 @@ public URI endpoint() throws IOException {
4657
public Map<String, String> headers() {
4758
return Collections.unmodifiableMap(headers);
4859
}
60+
61+
public static class Builder {
62+
private URI endpoint;
63+
private Map<String, String> additionalHeaders = new HashMap<>();
64+
private Duration customTimeout;
65+
66+
public Builder endpoint(URI endpoint) {
67+
this.endpoint = Validate.paramNotNull(endpoint, "endpoint");
68+
return this;
69+
}
70+
71+
public Builder headers(Map<String, String> headers) {
72+
if (headers != null) {
73+
this.additionalHeaders.putAll(headers);
74+
}
75+
return this;
76+
}
77+
78+
public Builder connectionTimeout(Duration timeout) {
79+
this.customTimeout = timeout;
80+
return this;
81+
}
82+
83+
public StaticResourcesEndpointProvider build() {
84+
return new StaticResourcesEndpointProvider(endpoint, additionalHeaders, customTimeout);
85+
}
86+
}
87+
88+
public static Builder builder() {
89+
return new Builder();
90+
}
4991
}

core/auth/src/test/java/software/amazon/awssdk/auth/credentials/HttpCredentialsLoaderTest.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,8 @@ private void stubForErrorResponse() {
144144

145145

146146
private ResourcesEndpointProvider testEndpointProvider() {
147-
return new StaticResourcesEndpointProvider(URI.create("http://localhost:" + mockServer.port() + CREDENTIALS_PATH),
148-
null);
147+
return StaticResourcesEndpointProvider.builder()
148+
.endpoint(URI.create("http://localhost:" + mockServer.port() + CREDENTIALS_PATH))
149+
.build();
149150
}
150151
}

0 commit comments

Comments
 (0)