diff --git a/.changes/next-release/bugfix-AWSSDKforJavav2-6d1d0aa.json b/.changes/next-release/bugfix-AWSSDKforJavav2-6d1d0aa.json new file mode 100644 index 000000000000..96242bdc232b --- /dev/null +++ b/.changes/next-release/bugfix-AWSSDKforJavav2-6d1d0aa.json @@ -0,0 +1,6 @@ +{ + "category": "AWS SDK for Java v2", + "contributor": "", + "type": "bugfix", + "description": "Keep precedence of options when passed to ProfileFileSupplier.aggregate" +} diff --git a/core/profiles/src/main/java/software/amazon/awssdk/profiles/ProfileFileSupplier.java b/core/profiles/src/main/java/software/amazon/awssdk/profiles/ProfileFileSupplier.java index 1521fdcfbc2d..4dec2883c814 100644 --- a/core/profiles/src/main/java/software/amazon/awssdk/profiles/ProfileFileSupplier.java +++ b/core/profiles/src/main/java/software/amazon/awssdk/profiles/ProfileFileSupplier.java @@ -16,9 +16,11 @@ package software.amazon.awssdk.profiles; import java.nio.file.Path; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Supplier; import software.amazon.awssdk.annotations.SdkPublicApi; @@ -119,7 +121,8 @@ static ProfileFileSupplier aggregate(ProfileFileSupplier... suppliers) { return new ProfileFileSupplier() { final AtomicReference currentAggregateProfileFile = new AtomicReference<>(); - final ConcurrentHashMap, ProfileFile> currentValuesBySupplier = new ConcurrentHashMap<>(); + final Map, ProfileFile> currentValuesBySupplier + = Collections.synchronizedMap(new LinkedHashMap<>()); @Override public ProfileFile get() { diff --git a/core/profiles/src/test/java/software/amazon/awssdk/profiles/ProfileFileSupplierTest.java b/core/profiles/src/test/java/software/amazon/awssdk/profiles/ProfileFileSupplierTest.java index e51bc53101f0..50e4c80ecc58 100644 --- a/core/profiles/src/test/java/software/amazon/awssdk/profiles/ProfileFileSupplierTest.java +++ b/core/profiles/src/test/java/software/amazon/awssdk/profiles/ProfileFileSupplierTest.java @@ -410,6 +410,56 @@ void aggregate_supplierReturnsSameInstanceMultipleTimesAggregatingProfileFileSup assertThat(suppliedProfileFiles).isEqualTo(distinctAggregateProfileFiles); } + @Test + void aggregate_duplicateOptionsGivenFixedProfileFirst_preservesPrecedence() { + ProfileFile configFile1 = configFile("profile default", Pair.of("aws_access_key_id", "config-key")); + Path credentialsFilePath = generateTestCredentialsFile("defaultAccessKey", "defaultSecretAccessKey"); + + ProfileFileSupplier supplier = ProfileFileSupplier.aggregate( + ProfileFileSupplier.fixedProfileFile(configFile1), + ProfileFileSupplier.reloadWhenModified(credentialsFilePath, ProfileFile.Type.CREDENTIALS)); + + ProfileFile profileFile = supplier.get(); + String accessKeyId = profileFile.profile("default").get().property("aws_access_key_id").get(); + + assertThat(accessKeyId).isEqualTo("config-key"); + + generateTestCredentialsFile("defaultAccessKey2", "defaultSecretAccessKey2"); + + profileFile = supplier.get(); + accessKeyId = profileFile.profile("default").get().property("aws_access_key_id").get(); + + assertThat(accessKeyId).isEqualTo("config-key"); + } + + @Test + void aggregate_duplicateOptionsGivenReloadingProfileFirst_preservesPrecedence() { + AdjustableClock clock = new AdjustableClock(); + + ProfileFile configFile1 = configFile("profile default", Pair.of("aws_access_key_id", "config-key")); + Path credentialsFilePath = generateTestCredentialsFile("defaultAccessKey", "defaultSecretAccessKey"); + + ProfileFileSupplier supplier = ProfileFileSupplier.aggregate( + builderWithClock(clock) + .reloadWhenModified(credentialsFilePath, ProfileFile.Type.CREDENTIALS) + .build(), + ProfileFileSupplier.fixedProfileFile(configFile1)); + + ProfileFile profileFile = supplier.get(); + String accessKeyId = profileFile.profile("default").get().property("aws_access_key_id").get(); + + assertThat(accessKeyId).isEqualTo("defaultAccessKey"); + + generateTestCredentialsFile("defaultAccessKey2", "defaultSecretAccessKey2"); + + clock.tickForward(Duration.ofMillis(1_000)); + + profileFile = supplier.get(); + accessKeyId = profileFile.profile("default").get().property("aws_access_key_id").get(); + + assertThat(accessKeyId).isEqualTo("defaultAccessKey2"); + } + @Test void fixedProfileFile_nullProfileFile_returnsNonNullSupplier() { ProfileFile file = null;