From 0d518cc4e9a7d3c43ec755268282502106f660bc Mon Sep 17 00:00:00 2001 From: David Turner Date: Thu, 27 Mar 2025 13:28:00 +0000 Subject: [PATCH 1/2] Improve `randomIdentifier` usage in AWS tests Adds prefixes to various randomly-generated values to make it easier to pin down where they're coming from in debugging sessions. Also forces the STS expiry time to be rendered in UTC. --- .../ec2/DiscoveryEc2InstanceProfileIT.java | 5 ++- .../fixture/aws/DynamicRegionSupplier.java | 35 +++++++++++++++++++ .../fixture/aws/sts/AwsStsHttpHandler.java | 6 ++-- .../fixture/aws/imds/Ec2ImdsHttpHandler.java | 5 +-- 4 files changed, 44 insertions(+), 7 deletions(-) create mode 100644 test/fixtures/aws-fixture-utils/src/main/java/fixture/aws/DynamicRegionSupplier.java diff --git a/plugins/discovery-ec2/src/javaRestTest/java/org/elasticsearch/discovery/ec2/DiscoveryEc2InstanceProfileIT.java b/plugins/discovery-ec2/src/javaRestTest/java/org/elasticsearch/discovery/ec2/DiscoveryEc2InstanceProfileIT.java index 78c3ca01e359c..fac52a657b04f 100644 --- a/plugins/discovery-ec2/src/javaRestTest/java/org/elasticsearch/discovery/ec2/DiscoveryEc2InstanceProfileIT.java +++ b/plugins/discovery-ec2/src/javaRestTest/java/org/elasticsearch/discovery/ec2/DiscoveryEc2InstanceProfileIT.java @@ -10,14 +10,13 @@ package org.elasticsearch.discovery.ec2; import fixture.aws.DynamicAwsCredentials; +import fixture.aws.DynamicRegionSupplier; import fixture.aws.ec2.AwsEc2HttpFixture; import fixture.aws.imds.Ec2ImdsHttpFixture; import fixture.aws.imds.Ec2ImdsServiceBuilder; import fixture.aws.imds.Ec2ImdsVersion; -import org.elasticsearch.common.util.LazyInitializable; import org.elasticsearch.discovery.DiscoveryModule; -import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.cluster.ElasticsearchCluster; import org.junit.ClassRule; import org.junit.rules.RuleChain; @@ -29,7 +28,7 @@ public class DiscoveryEc2InstanceProfileIT extends DiscoveryEc2ClusterFormationTestCase { // Lazy-initialized so we can generate it randomly, which is not possible in static context. - private static final Supplier regionSupplier = new LazyInitializable<>(ESTestCase::randomIdentifier)::getOrCompute; + private static final Supplier regionSupplier = new DynamicRegionSupplier(); private static final DynamicAwsCredentials dynamicCredentials = new DynamicAwsCredentials(regionSupplier, "ec2"); diff --git a/test/fixtures/aws-fixture-utils/src/main/java/fixture/aws/DynamicRegionSupplier.java b/test/fixtures/aws-fixture-utils/src/main/java/fixture/aws/DynamicRegionSupplier.java new file mode 100644 index 0000000000000..0f17507fe9700 --- /dev/null +++ b/test/fixtures/aws-fixture-utils/src/main/java/fixture/aws/DynamicRegionSupplier.java @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +package fixture.aws; + +import org.elasticsearch.test.ESTestCase; + +import java.util.Objects; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Supplier; + +/** + * Lazy supplier for a region name. We cannot use randomness like {@link ESTestCase#randomIdentifier()} when creating the test fixtures in + * the first place because this happens in static context, so instead we create one of these and defer the creation of the region name + * itself until the test actually starts running. + */ +public class DynamicRegionSupplier implements Supplier { + private final AtomicReference generatedRegion = new AtomicReference<>(); + + @Override + public String get() { + return Objects.requireNonNullElseGet(generatedRegion.get(), this::generateAndGet); + } + + private String generateAndGet() { + final var newRegion = "test-region-" + ESTestCase.randomIdentifier(); + return Objects.requireNonNullElse(generatedRegion.compareAndExchange(null, newRegion), newRegion); + } +} diff --git a/test/fixtures/aws-sts-fixture/src/main/java/fixture/aws/sts/AwsStsHttpHandler.java b/test/fixtures/aws-sts-fixture/src/main/java/fixture/aws/sts/AwsStsHttpHandler.java index ac3299f157485..8ffe2f2709440 100644 --- a/test/fixtures/aws-sts-fixture/src/main/java/fixture/aws/sts/AwsStsHttpHandler.java +++ b/test/fixtures/aws-sts-fixture/src/main/java/fixture/aws/sts/AwsStsHttpHandler.java @@ -18,6 +18,7 @@ import java.io.IOException; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; +import java.time.Clock; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.Arrays; @@ -73,7 +74,8 @@ public void handle(final HttpExchange exchange) throws IOException { exchange.close(); return; } - final var accessKey = randomIdentifier(); + // ATIA for a test key, similar to AKIA and ASIA used in real AWS credentials + final var accessKey = "ATIA_STS_" + randomIdentifier(); final var sessionToken = randomIdentifier(); newCredentialsConsumer.accept(accessKey, sessionToken); final byte[] response = String.format( @@ -104,7 +106,7 @@ public void handle(final HttpExchange exchange) throws IOException { ROLE_NAME, sessionToken, randomSecretKey(), - ZonedDateTime.now().plusDays(1L).format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ")), + ZonedDateTime.now(Clock.systemUTC()).plusDays(1L).format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ")), accessKey ).getBytes(StandardCharsets.UTF_8); exchange.getResponseHeaders().add("Content-Type", "text/xml; charset=UTF-8"); diff --git a/test/fixtures/ec2-imds-fixture/src/main/java/fixture/aws/imds/Ec2ImdsHttpHandler.java b/test/fixtures/ec2-imds-fixture/src/main/java/fixture/aws/imds/Ec2ImdsHttpHandler.java index 7606e9261dd3e..128591ab3a6ee 100644 --- a/test/fixtures/ec2-imds-fixture/src/main/java/fixture/aws/imds/Ec2ImdsHttpHandler.java +++ b/test/fixtures/ec2-imds-fixture/src/main/java/fixture/aws/imds/Ec2ImdsHttpHandler.java @@ -121,7 +121,7 @@ public void handle(final HttpExchange exchange) throws IOException { if ("GET".equals(requestMethod)) { if (path.equals(IMDS_SECURITY_CREDENTIALS_PATH) && dynamicProfileNames) { - final var profileName = randomIdentifier(); + final var profileName = "imds_profile_" + randomIdentifier(); validCredentialsEndpoints.add(IMDS_SECURITY_CREDENTIALS_PATH + profileName); sendStringResponse(exchange, profileName); return; @@ -133,7 +133,8 @@ public void handle(final HttpExchange exchange) throws IOException { sendStringResponse(exchange, Strings.toString(instanceIdentityDocument)); return; } else if (validCredentialsEndpoints.contains(path)) { - final String accessKey = randomIdentifier(); + // ATIA for a test key, similar to AKIA and ASIA used in real AWS credentials + final String accessKey = "ATIA_IMDS_" + randomIdentifier(); final String sessionToken = randomIdentifier(); newCredentialsConsumer.accept(accessKey, sessionToken); final byte[] response = Strings.format( From 5a7c058dad8be5d3a5d726d604d38475fce36eb0 Mon Sep 17 00:00:00 2001 From: David Turner Date: Thu, 27 Mar 2025 16:28:40 +0000 Subject: [PATCH 2/2] Different prefixes --- .../src/main/java/fixture/aws/DynamicRegionSupplier.java | 2 +- .../src/main/java/fixture/aws/sts/AwsStsHttpHandler.java | 3 +-- .../src/main/java/fixture/aws/imds/Ec2ImdsHttpHandler.java | 3 +-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/test/fixtures/aws-fixture-utils/src/main/java/fixture/aws/DynamicRegionSupplier.java b/test/fixtures/aws-fixture-utils/src/main/java/fixture/aws/DynamicRegionSupplier.java index 0f17507fe9700..28ce8b2b02b19 100644 --- a/test/fixtures/aws-fixture-utils/src/main/java/fixture/aws/DynamicRegionSupplier.java +++ b/test/fixtures/aws-fixture-utils/src/main/java/fixture/aws/DynamicRegionSupplier.java @@ -29,7 +29,7 @@ public String get() { } private String generateAndGet() { - final var newRegion = "test-region-" + ESTestCase.randomIdentifier(); + final var newRegion = "DynamicRegionSupplier-" + ESTestCase.randomIdentifier(); return Objects.requireNonNullElse(generatedRegion.compareAndExchange(null, newRegion), newRegion); } } diff --git a/test/fixtures/aws-sts-fixture/src/main/java/fixture/aws/sts/AwsStsHttpHandler.java b/test/fixtures/aws-sts-fixture/src/main/java/fixture/aws/sts/AwsStsHttpHandler.java index 8ffe2f2709440..1ed9b6888df8d 100644 --- a/test/fixtures/aws-sts-fixture/src/main/java/fixture/aws/sts/AwsStsHttpHandler.java +++ b/test/fixtures/aws-sts-fixture/src/main/java/fixture/aws/sts/AwsStsHttpHandler.java @@ -74,8 +74,7 @@ public void handle(final HttpExchange exchange) throws IOException { exchange.close(); return; } - // ATIA for a test key, similar to AKIA and ASIA used in real AWS credentials - final var accessKey = "ATIA_STS_" + randomIdentifier(); + final var accessKey = "test_key_STS_" + randomIdentifier(); final var sessionToken = randomIdentifier(); newCredentialsConsumer.accept(accessKey, sessionToken); final byte[] response = String.format( diff --git a/test/fixtures/ec2-imds-fixture/src/main/java/fixture/aws/imds/Ec2ImdsHttpHandler.java b/test/fixtures/ec2-imds-fixture/src/main/java/fixture/aws/imds/Ec2ImdsHttpHandler.java index 128591ab3a6ee..826f98bead497 100644 --- a/test/fixtures/ec2-imds-fixture/src/main/java/fixture/aws/imds/Ec2ImdsHttpHandler.java +++ b/test/fixtures/ec2-imds-fixture/src/main/java/fixture/aws/imds/Ec2ImdsHttpHandler.java @@ -133,8 +133,7 @@ public void handle(final HttpExchange exchange) throws IOException { sendStringResponse(exchange, Strings.toString(instanceIdentityDocument)); return; } else if (validCredentialsEndpoints.contains(path)) { - // ATIA for a test key, similar to AKIA and ASIA used in real AWS credentials - final String accessKey = "ATIA_IMDS_" + randomIdentifier(); + final String accessKey = "test_key_imds_" + randomIdentifier(); final String sessionToken = randomIdentifier(); newCredentialsConsumer.accept(accessKey, sessionToken); final byte[] response = Strings.format(