diff --git a/bom/pom.xml b/bom/pom.xml index 1cd014286..b3767bdc5 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -147,6 +147,16 @@ quarkus-amazon-secretsmanager-deployment ${project.version} + + io.quarkiverse.amazonservices + quarkus-amazon-secretsmanager-config + ${project.version} + + + io.quarkiverse.amazonservices + quarkus-amazon-secretsmanager-config-deployment + ${project.version} + io.quarkiverse.amazonservices quarkus-amazon-devservices-ses diff --git a/common/deployment/src/main/java/io/quarkus/amazon/common/deployment/AbstractAmazonServiceProcessor.java b/common/deployment/src/main/java/io/quarkus/amazon/common/deployment/AbstractAmazonServiceProcessor.java index df2b48bb6..7f0fa3059 100644 --- a/common/deployment/src/main/java/io/quarkus/amazon/common/deployment/AbstractAmazonServiceProcessor.java +++ b/common/deployment/src/main/java/io/quarkus/amazon/common/deployment/AbstractAmazonServiceProcessor.java @@ -87,10 +87,10 @@ protected void setupExtension( //Discover all clients injections in order to determine if async or sync client is required for (RequireAmazonClientBuildItem clientRequirement : clientRequirements) { - if (clientRequirement.getSyncClassName().filter(syncClientName()::equals).isPresent()) { + if (clientRequirement.getSyncClassName().filter(p -> syncClientName().equals(p)).isPresent()) { syncClassName = Optional.of(syncClientName()); } - if (clientRequirement.getAsyncClassName().filter(asyncClientName()::equals).isPresent()) { + if (clientRequirement.getAsyncClassName().filter(p -> asyncClientName().equals(p)).isPresent()) { asyncClassName = Optional.of(asyncClientName()); } } @@ -220,7 +220,7 @@ protected void createClientBuilders( .scope(ApplicationScoped.class) .runtimeValue(syncClientBuilder) .done()); - clientSync.produce(new AmazonClientSyncResultBuildItem(configName)); + clientSync.produce(new AmazonClientSyncResultBuildItem(configName, syncClientBuilder)); } if (asyncClientBuilder != null) { asyncClientBuilder = recorder.configure(asyncClientBuilder, awsConfigRuntime, sdkConfigRuntime, @@ -230,7 +230,7 @@ protected void createClientBuilders( .scope(ApplicationScoped.class) .runtimeValue(asyncClientBuilder) .done()); - clientAsync.produce(new AmazonClientAsyncResultBuildItem(configName)); + clientAsync.produce(new AmazonClientAsyncResultBuildItem(configName, asyncClientBuilder)); } if (presignerBuilder != null) { presignerBuilder = recorder.configurePresigner(presignerBuilder, awsConfigRuntime, sdkConfigRuntime, diff --git a/common/deployment/src/main/java/io/quarkus/amazon/common/deployment/AmazonClientAsyncResultBuildItem.java b/common/deployment/src/main/java/io/quarkus/amazon/common/deployment/AmazonClientAsyncResultBuildItem.java index 1ebbdb8c8..923b752d2 100644 --- a/common/deployment/src/main/java/io/quarkus/amazon/common/deployment/AmazonClientAsyncResultBuildItem.java +++ b/common/deployment/src/main/java/io/quarkus/amazon/common/deployment/AmazonClientAsyncResultBuildItem.java @@ -1,6 +1,8 @@ package io.quarkus.amazon.common.deployment; import io.quarkus.builder.item.MultiBuildItem; +import io.quarkus.runtime.RuntimeValue; +import software.amazon.awssdk.awscore.client.builder.AwsClientBuilder; /* * Describes what async clients are provided by a given extension @@ -8,12 +10,18 @@ public final class AmazonClientAsyncResultBuildItem extends MultiBuildItem { private final String awsClientName; + private RuntimeValue asyncClientBuilder; - public AmazonClientAsyncResultBuildItem(String awsClientName) { + public AmazonClientAsyncResultBuildItem(String awsClientName, RuntimeValue asyncClientBuilder) { this.awsClientName = awsClientName; + this.asyncClientBuilder = asyncClientBuilder; } public String getAwsClientName() { return awsClientName; } + + public RuntimeValue getAsyncClientBuilder() { + return asyncClientBuilder; + } } diff --git a/common/deployment/src/main/java/io/quarkus/amazon/common/deployment/AmazonClientSyncResultBuildItem.java b/common/deployment/src/main/java/io/quarkus/amazon/common/deployment/AmazonClientSyncResultBuildItem.java index 2f2305f70..260564148 100644 --- a/common/deployment/src/main/java/io/quarkus/amazon/common/deployment/AmazonClientSyncResultBuildItem.java +++ b/common/deployment/src/main/java/io/quarkus/amazon/common/deployment/AmazonClientSyncResultBuildItem.java @@ -1,6 +1,8 @@ package io.quarkus.amazon.common.deployment; import io.quarkus.builder.item.MultiBuildItem; +import io.quarkus.runtime.RuntimeValue; +import software.amazon.awssdk.awscore.client.builder.AwsClientBuilder; /* * Describes what sync clients are provided by a given extension @@ -8,12 +10,18 @@ public final class AmazonClientSyncResultBuildItem extends MultiBuildItem { private final String awsClientName; + private RuntimeValue syncClientBuilder; - public AmazonClientSyncResultBuildItem(String awsClientName) { + public AmazonClientSyncResultBuildItem(String awsClientName, RuntimeValue syncClientBuilder) { this.awsClientName = awsClientName; + this.syncClientBuilder = syncClientBuilder; } public String getAwsClientName() { return awsClientName; } + + public RuntimeValue getSyncClientBuilder() { + return syncClientBuilder; + } } diff --git a/pom.xml b/pom.xml index f9913970d..652e56c9e 100644 --- a/pom.xml +++ b/pom.xml @@ -28,6 +28,7 @@ ssm sts secretsmanager + secretsmanager-config docs integration-tests diff --git a/secretsmanager-config/deployment/pom.xml b/secretsmanager-config/deployment/pom.xml new file mode 100644 index 000000000..270fc3298 --- /dev/null +++ b/secretsmanager-config/deployment/pom.xml @@ -0,0 +1,61 @@ + + + 4.0.0 + + + io.quarkiverse.amazonservices + quarkus-amazon-secretsmanager-config-parent + 999-SNAPSHOT + + + quarkus-amazon-secretsmanager-config-deployment + Quarkus - Amazon Services - Secrets Manager Config - Deployment + + + + io.quarkiverse.amazonservices + quarkus-amazon-common-deployment + + + io.quarkiverse.amazonservices + quarkus-amazon-common-deployment-spi + + + io.quarkiverse.amazonservices + quarkus-amazon-secretsmanager-config + + + software.amazon.awssdk + url-connection-client + + + + + io.quarkus + quarkus-junit5-internal + test + + + io.rest-assured + rest-assured + test + + + + + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${quarkus.version} + + + + + + + diff --git a/secretsmanager-config/deployment/src/main/java/io/quarkus/amazon/secretsmanager/config/deployment/SecretsManagerConfigDevServicesProcessor.java b/secretsmanager-config/deployment/src/main/java/io/quarkus/amazon/secretsmanager/config/deployment/SecretsManagerConfigDevServicesProcessor.java new file mode 100644 index 000000000..953f33561 --- /dev/null +++ b/secretsmanager-config/deployment/src/main/java/io/quarkus/amazon/secretsmanager/config/deployment/SecretsManagerConfigDevServicesProcessor.java @@ -0,0 +1,54 @@ +package io.quarkus.amazon.secretsmanager.config.deployment; + +import java.util.Map; + +import org.testcontainers.containers.localstack.LocalStackContainer; +import org.testcontainers.containers.localstack.LocalStackContainer.Service; + +import io.quarkus.amazon.common.deployment.spi.AbstractDevServicesLocalStackProcessor; +import io.quarkus.amazon.common.deployment.spi.DevServicesLocalStackProviderBuildItem; +import io.quarkus.amazon.common.runtime.DevServicesBuildTimeConfig; +import io.quarkus.amazon.secretsmanager.config.runtime.SecretsManagerConfigBuildTimeConfig; +import io.quarkus.amazon.secretsmanager.config.runtime.SecretsManagerDevServicesBuildTimeConfig; +import io.quarkus.deployment.annotations.BuildStep; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient; + +public class SecretsManagerConfigDevServicesProcessor extends AbstractDevServicesLocalStackProcessor { + + @BuildStep + DevServicesLocalStackProviderBuildItem setupSecretsManager(SecretsManagerConfigBuildTimeConfig clientBuildTimeConfig) { + return this.setup(Service.SECRETSMANAGER, clientBuildTimeConfig.devservices); + } + + @Override + protected void overrideDefaultConfig(Map defaultConfig) { + for (String key : defaultConfig.keySet().toArray(new String[0])) { + defaultConfig.put(key.replace(Service.SECRETSMANAGER.getName(), Service.SECRETSMANAGER.getName() + "-config"), + defaultConfig.remove(key)); + } + } + + @Override + protected void prepareLocalStack(DevServicesBuildTimeConfig clientBuildTimeConfig, + LocalStackContainer localstack) { + createSecrets(localstack, (SecretsManagerDevServicesBuildTimeConfig) clientBuildTimeConfig); + } + + public void createSecrets(LocalStackContainer localstack, SecretsManagerDevServicesBuildTimeConfig configuration) { + try (SecretsManagerClient client = SecretsManagerClient.builder() + .endpointOverride(localstack.getEndpointOverride(LocalStackContainer.Service.S3)) + .region(Region.of(localstack.getRegion())) + .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials + .create(localstack.getAccessKey(), localstack.getSecretKey()))) + .httpClientBuilder(UrlConnectionHttpClient.builder()) + .build()) { + configuration.secrets.forEach((key, value) -> { + client.createSecret(r -> r.name(key).secretString(value)); + }); + } + } +} diff --git a/secretsmanager-config/deployment/src/main/java/io/quarkus/amazon/secretsmanager/config/deployment/SecretsManagerConfigProcessor.java b/secretsmanager-config/deployment/src/main/java/io/quarkus/amazon/secretsmanager/config/deployment/SecretsManagerConfigProcessor.java new file mode 100644 index 000000000..0585eca63 --- /dev/null +++ b/secretsmanager-config/deployment/src/main/java/io/quarkus/amazon/secretsmanager/config/deployment/SecretsManagerConfigProcessor.java @@ -0,0 +1,136 @@ +package io.quarkus.amazon.secretsmanager.config.deployment; + +import java.util.List; +import java.util.Optional; + +import org.jboss.jandex.DotName; + +import io.quarkus.amazon.common.deployment.AbstractAmazonServiceProcessor; +import io.quarkus.amazon.common.deployment.AmazonClientBuildItem; +import io.quarkus.amazon.common.deployment.AmazonClientInterceptorsPathBuildItem; +import io.quarkus.amazon.common.deployment.AmazonClientSyncResultBuildItem; +import io.quarkus.amazon.common.deployment.AmazonClientSyncTransportBuildItem; +import io.quarkus.amazon.common.deployment.RequireAmazonClientBuildItem; +import io.quarkus.amazon.common.runtime.AmazonClientRecorder; +import io.quarkus.amazon.common.runtime.AmazonClientUrlConnectionTransportRecorder; +import io.quarkus.amazon.common.runtime.SyncHttpClientBuildTimeConfig; +import io.quarkus.amazon.common.runtime.SyncHttpClientBuildTimeConfig.SyncClientType; +import io.quarkus.amazon.secretsmanager.config.runtime.SecretsManagerConfigBuildTimeConfig; +import io.quarkus.amazon.secretsmanager.config.runtime.SecretsManagerConfigRecorder; +import io.quarkus.amazon.secretsmanager.config.runtime.SecretsManagerConfigSourceConfig; +import io.quarkus.arc.deployment.SyntheticBeanBuildItem; +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.annotations.ExecutionTime; +import io.quarkus.deployment.annotations.Record; +import io.quarkus.deployment.builditem.ExtensionSslNativeSupportBuildItem; +import io.quarkus.deployment.builditem.FeatureBuildItem; +import io.quarkus.deployment.builditem.RunTimeConfigurationSourceValueBuildItem; +import io.quarkus.runtime.RuntimeValue; +import software.amazon.awssdk.awscore.client.builder.AwsClientBuilder; +import software.amazon.awssdk.services.secretsmanager.SecretsManagerAsyncClientBuilder; +import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient; +import software.amazon.awssdk.services.secretsmanager.SecretsManagerClientBuilder; + +public class SecretsManagerConfigProcessor extends AbstractAmazonServiceProcessor { + + private static final String AMAZON_SECRETS_MANAGER_CONFIG = "amazon-secretsmanager-config"; + + SecretsManagerConfigBuildTimeConfig buildTimeConfig; + + @Override + protected String amazonServiceClientName() { + return AMAZON_SECRETS_MANAGER_CONFIG; + } + + @Override + protected String configName() { + return "secretsmanager-config"; + } + + @Override + protected DotName syncClientName() { + return DotName.createSimple(SecretsManagerClient.class.getName()); + } + + @Override + protected DotName asyncClientName() { + throw new UnsupportedOperationException(); + } + + @Override + protected String builtinInterceptorsPath() { + return "software/amazon/awssdk/services/secretsmanager/execution.interceptors"; + } + + @BuildStep + @Record(ExecutionTime.RUNTIME_INIT) + public RunTimeConfigurationSourceValueBuildItem configure(SecretsManagerConfigRecorder recorder, + SecretsManagerConfigSourceConfig config, SecretsManagerConfigBuildTimeConfig buildTimeConfig, + List syncClientBuilders) { + + String configName = configName(); + + RuntimeValue syncClientBuilder = (RuntimeValue) syncClientBuilders + .stream().filter(s -> configName.equals(s.getAwsClientName())).findFirst().get().getSyncClientBuilder(); + + return new RunTimeConfigurationSourceValueBuildItem( + recorder.configSources(config, buildTimeConfig, syncClientBuilder)); + } + + @BuildStep + void setup(List clientRequirements, + BuildProducer extensionSslNativeSupport, + BuildProducer feature, + BuildProducer interceptors, + BuildProducer clientProducer) { + + SyncHttpClientBuildTimeConfig syncClientConfig = new SyncHttpClientBuildTimeConfig(); + syncClientConfig.type = SyncClientType.URL; + + setupExtension(List.of(new RequireAmazonClientBuildItem(Optional.of(syncClientName()), Optional.empty())), + extensionSslNativeSupport, feature, interceptors, clientProducer, + buildTimeConfig.sdk, syncClientConfig); + } + + @BuildStep + @Record(ExecutionTime.RUNTIME_INIT) + void setupUrlConnectionSyncTransport(List amazonClients, SecretsManagerConfigRecorder recorder, + AmazonClientUrlConnectionTransportRecorder transportRecorder, + SecretsManagerConfigSourceConfig runtimeConfig, BuildProducer syncTransports) { + SyncHttpClientBuildTimeConfig syncClientConfig = new SyncHttpClientBuildTimeConfig(); + syncClientConfig.type = SyncClientType.URL; + + createUrlConnectionSyncTransportBuilder(amazonClients, + transportRecorder, + syncClientConfig, + recorder.getSyncConfig(runtimeConfig), + syncTransports); + } + + @BuildStep + @Record(ExecutionTime.RUNTIME_INIT) + void createClientBuilders(SecretsManagerConfigRecorder recorder, + AmazonClientRecorder commonRecorder, + SecretsManagerConfigSourceConfig runtimeConfig, + List syncTransports, + BuildProducer syntheticBeans, + BuildProducer clientSync) { + + createClientBuilders(commonRecorder, + recorder.getAwsConfig(runtimeConfig), + recorder.getSdkConfig(runtimeConfig), + buildTimeConfig.sdk, + syncTransports, + List.of(), + SecretsManagerClientBuilder.class, + (syncTransport) -> recorder.createSyncBuilder(runtimeConfig, syncTransport), + SecretsManagerAsyncClientBuilder.class, + null, + null, + null, + syntheticBeans, + clientSync, + null); + } +} diff --git a/secretsmanager-config/deployment/src/test/java/io/quarkus/amazon/secretsmanager/config/deployment/SecretsManagerConfigTest.java b/secretsmanager-config/deployment/src/test/java/io/quarkus/amazon/secretsmanager/config/deployment/SecretsManagerConfigTest.java new file mode 100644 index 000000000..e19b4286d --- /dev/null +++ b/secretsmanager-config/deployment/src/test/java/io/quarkus/amazon/secretsmanager/config/deployment/SecretsManagerConfigTest.java @@ -0,0 +1,24 @@ +package io.quarkus.amazon.secretsmanager.config.deployment; + +import org.eclipse.microprofile.config.inject.ConfigProperty; +import org.junit.Assert; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; + +public class SecretsManagerConfigTest { + + @ConfigProperty(name = "mysecretkey") + String secret; + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + .addAsResource("devservices.properties", "application.properties")); + + @Test + public void test() { + Assert.assertEquals("mysecretvalue", secret); + } +} diff --git a/secretsmanager-config/deployment/src/test/resources/devservices.properties b/secretsmanager-config/deployment/src/test/resources/devservices.properties new file mode 100644 index 000000000..d5bb7f297 --- /dev/null +++ b/secretsmanager-config/deployment/src/test/resources/devservices.properties @@ -0,0 +1 @@ +quarkus.secretsmanager-config.devservices.secrets.mysecretkey=mysecretvalue \ No newline at end of file diff --git a/secretsmanager-config/pom.xml b/secretsmanager-config/pom.xml new file mode 100644 index 000000000..c257385cf --- /dev/null +++ b/secretsmanager-config/pom.xml @@ -0,0 +1,21 @@ + + + 4.0.0 + + + io.quarkiverse.amazonservices + quarkus-amazon-services-build-parent + 999-SNAPSHOT + ../build-parent/pom.xml + + + quarkus-amazon-secretsmanager-config-parent + Quarkus - Amazon Services - Secrets Manager Config + pom + + + runtime + deployment + + + diff --git a/secretsmanager-config/runtime/pom.xml b/secretsmanager-config/runtime/pom.xml new file mode 100644 index 000000000..0f3a928e8 --- /dev/null +++ b/secretsmanager-config/runtime/pom.xml @@ -0,0 +1,56 @@ + + + 4.0.0 + + + io.quarkiverse.amazonservices + quarkus-amazon-secretsmanager-config-parent + 999-SNAPSHOT + + + quarkus-amazon-secretsmanager-config + Quarkus - Amazon Services - Secrets Manager Config - Runtime + Connect to Amazon Secrets Manager + + + + io.quarkiverse.amazonservices + quarkus-amazon-common + + + software.amazon.awssdk + secretsmanager + + + software.amazon.awssdk + netty-nio-client + + + software.amazon.awssdk + apache-client + + + + + + + + + io.quarkus + quarkus-extension-maven-plugin + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${quarkus.version} + + + + + + + diff --git a/secretsmanager-config/runtime/src/main/java/io/quarkus/amazon/secretsmanager/config/runtime/SecretsManagerConfigBuildTimeConfig.java b/secretsmanager-config/runtime/src/main/java/io/quarkus/amazon/secretsmanager/config/runtime/SecretsManagerConfigBuildTimeConfig.java new file mode 100644 index 000000000..24326b3ca --- /dev/null +++ b/secretsmanager-config/runtime/src/main/java/io/quarkus/amazon/secretsmanager/config/runtime/SecretsManagerConfigBuildTimeConfig.java @@ -0,0 +1,25 @@ +package io.quarkus.amazon.secretsmanager.config.runtime; + +import io.quarkus.amazon.common.runtime.SdkBuildTimeConfig; +import io.quarkus.runtime.annotations.ConfigItem; +import io.quarkus.runtime.annotations.ConfigPhase; +import io.quarkus.runtime.annotations.ConfigRoot; + +/** + * Amazon Secrets Manager Config build time configuration + */ +@ConfigRoot(name = "secretsmanager-config", phase = ConfigPhase.BUILD_AND_RUN_TIME_FIXED) +public class SecretsManagerConfigBuildTimeConfig { + + /** + * SDK client configurations for AWS Secrets Manager client + */ + @ConfigItem(name = ConfigItem.PARENT) + public SdkBuildTimeConfig sdk; + + /** + * Config for dev services + */ + @ConfigItem + public SecretsManagerDevServicesBuildTimeConfig devservices; +} diff --git a/secretsmanager-config/runtime/src/main/java/io/quarkus/amazon/secretsmanager/config/runtime/SecretsManagerConfigRecorder.java b/secretsmanager-config/runtime/src/main/java/io/quarkus/amazon/secretsmanager/config/runtime/SecretsManagerConfigRecorder.java new file mode 100644 index 000000000..bf04222eb --- /dev/null +++ b/secretsmanager-config/runtime/src/main/java/io/quarkus/amazon/secretsmanager/config/runtime/SecretsManagerConfigRecorder.java @@ -0,0 +1,47 @@ +package io.quarkus.amazon.secretsmanager.config.runtime; + +import org.eclipse.microprofile.config.spi.ConfigSourceProvider; + +import io.quarkus.amazon.common.runtime.AwsConfig; +import io.quarkus.amazon.common.runtime.SdkConfig; +import io.quarkus.amazon.common.runtime.SyncHttpClientConfig; +import io.quarkus.runtime.RuntimeValue; +import io.quarkus.runtime.annotations.Recorder; +import software.amazon.awssdk.awscore.client.builder.AwsClientBuilder; +import software.amazon.awssdk.http.SdkHttpClient; +import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient; +import software.amazon.awssdk.services.secretsmanager.SecretsManagerClientBuilder; + +@Recorder +public class SecretsManagerConfigRecorder { + + public RuntimeValue configSources(SecretsManagerConfigSourceConfig config, + SecretsManagerConfigBuildTimeConfig buildTimeConfig, + RuntimeValue syncClientBuilder) { + + return new RuntimeValue<>( + new SecretsManagerConfigSourceProvider((SecretsManagerClientBuilder) syncClientBuilder.getValue())); + } + + public RuntimeValue getSyncConfig(SecretsManagerConfigSourceConfig config) { + return new RuntimeValue<>(config.syncClient); + } + + public RuntimeValue getAwsConfig(SecretsManagerConfigSourceConfig config) { + return new RuntimeValue<>(config.aws); + } + + public RuntimeValue getSdkConfig(SecretsManagerConfigSourceConfig config) { + return new RuntimeValue<>(config.sdk); + } + + public RuntimeValue createSyncBuilder(SecretsManagerConfigSourceConfig config, + RuntimeValue transport) { + SecretsManagerClientBuilder builder = SecretsManagerClient.builder(); + if (transport != null) { + builder.httpClientBuilder(transport.getValue()); + } + return new RuntimeValue<>(builder); + } + +} diff --git a/secretsmanager-config/runtime/src/main/java/io/quarkus/amazon/secretsmanager/config/runtime/SecretsManagerConfigSourceConfig.java b/secretsmanager-config/runtime/src/main/java/io/quarkus/amazon/secretsmanager/config/runtime/SecretsManagerConfigSourceConfig.java new file mode 100644 index 000000000..956cabbc7 --- /dev/null +++ b/secretsmanager-config/runtime/src/main/java/io/quarkus/amazon/secretsmanager/config/runtime/SecretsManagerConfigSourceConfig.java @@ -0,0 +1,33 @@ +package io.quarkus.amazon.secretsmanager.config.runtime; + +import io.quarkus.amazon.common.runtime.AwsConfig; +import io.quarkus.amazon.common.runtime.SdkConfig; +import io.quarkus.amazon.common.runtime.SyncHttpClientConfig; +import io.quarkus.runtime.annotations.ConfigDocSection; +import io.quarkus.runtime.annotations.ConfigItem; +import io.quarkus.runtime.annotations.ConfigPhase; +import io.quarkus.runtime.annotations.ConfigRoot; + +@ConfigRoot(name = "secretsmanager-config", phase = ConfigPhase.BOOTSTRAP) +public class SecretsManagerConfigSourceConfig { + /** + * AWS SDK client configurations + */ + @ConfigItem(name = ConfigItem.PARENT) + @ConfigDocSection + public SdkConfig sdk; + + /** + * AWS services configurations + */ + @ConfigItem + @ConfigDocSection + public AwsConfig aws; + + /** + * Sync HTTP transport configurations + */ + @ConfigItem + @ConfigDocSection + public SyncHttpClientConfig syncClient; +} diff --git a/secretsmanager-config/runtime/src/main/java/io/quarkus/amazon/secretsmanager/config/runtime/SecretsManagerConfigSourceProvider.java b/secretsmanager-config/runtime/src/main/java/io/quarkus/amazon/secretsmanager/config/runtime/SecretsManagerConfigSourceProvider.java new file mode 100644 index 000000000..d61a67417 --- /dev/null +++ b/secretsmanager-config/runtime/src/main/java/io/quarkus/amazon/secretsmanager/config/runtime/SecretsManagerConfigSourceProvider.java @@ -0,0 +1,59 @@ +package io.quarkus.amazon.secretsmanager.config.runtime; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import jakarta.inject.Inject; + +import org.eclipse.microprofile.config.spi.ConfigSource; +import org.eclipse.microprofile.config.spi.ConfigSourceProvider; + +import io.smallrye.config.common.MapBackedConfigSource; +import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient; +import software.amazon.awssdk.services.secretsmanager.SecretsManagerClientBuilder; +import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueRequest; +import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueResponse; +import software.amazon.awssdk.services.secretsmanager.model.SecretListEntry; + +public class SecretsManagerConfigSourceProvider implements ConfigSourceProvider { + + @Inject + SecretsManagerClient secretsManagerClient; + + public SecretsManagerConfigSourceProvider(SecretsManagerClientBuilder syncClientBuilder) { + secretsManagerClient = syncClientBuilder.build(); + } + + @Override + public Iterable getConfigSources(ClassLoader forClassLoader) { + int ordinal = 299; // one less than env vars + List result = new ArrayList<>(); + // Retrieve all secrets using pagination and stream + Map secretsMap = secretsManagerClient.listSecretsPaginator().stream() + .flatMap(response -> response.secretList().stream()) + .collect(Collectors.toUnmodifiableMap(SecretListEntry::name, + secret -> getSecretValue(secretsManagerClient, secret.arn()))); + + result.add(new SecretsManagerConfigSource(secretsMap, ordinal)); + + return result; + } + + private static String getSecretValue(SecretsManagerClient secretsManager, String secretArn) { + + GetSecretValueRequest getSecretValueRequest = GetSecretValueRequest.builder() + .secretId(secretArn) + .build(); + GetSecretValueResponse getSecretValueResponse = secretsManager.getSecretValue(getSecretValueRequest); + return getSecretValueResponse.secretString(); + } + + private static final class SecretsManagerConfigSource extends MapBackedConfigSource { + + public SecretsManagerConfigSource(Map propertyMap, int ordinal) { + super("SecretsManagerConfigSource", propertyMap, ordinal); + } + } +} diff --git a/secretsmanager-config/runtime/src/main/java/io/quarkus/amazon/secretsmanager/config/runtime/SecretsManagerDevServicesBuildTimeConfig.java b/secretsmanager-config/runtime/src/main/java/io/quarkus/amazon/secretsmanager/config/runtime/SecretsManagerDevServicesBuildTimeConfig.java new file mode 100644 index 000000000..62525a030 --- /dev/null +++ b/secretsmanager-config/runtime/src/main/java/io/quarkus/amazon/secretsmanager/config/runtime/SecretsManagerDevServicesBuildTimeConfig.java @@ -0,0 +1,17 @@ +package io.quarkus.amazon.secretsmanager.config.runtime; + +import java.util.Map; + +import io.quarkus.amazon.common.runtime.DevServicesBuildTimeConfig; +import io.quarkus.runtime.annotations.ConfigGroup; +import io.quarkus.runtime.annotations.ConfigItem; + +@ConfigGroup +public class SecretsManagerDevServicesBuildTimeConfig extends DevServicesBuildTimeConfig { + + /** + * The secrets to create on startup. + */ + @ConfigItem + public Map secrets; +} diff --git a/secretsmanager-config/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/secretsmanager-config/runtime/src/main/resources/META-INF/quarkus-extension.yaml new file mode 100644 index 000000000..89e9190a3 --- /dev/null +++ b/secretsmanager-config/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -0,0 +1,15 @@ +--- +artifact: ${project.groupId}:${project.artifactId}:${project.version} +name: "Amazon Secrets Manager Config" +metadata: + keywords: + - "secretsmanager" + - "aws" + - "amazon" + - "config" + categories: + - "data" + guide: https://quarkiverse.github.io/quarkiverse-docs/quarkus-amazon-services/dev/amazon-secretsmanager.html + status: "preview" + config: + - "quarkus.secretsmanager-config." diff --git a/sns/deployment/pom.xml b/sns/deployment/pom.xml index d8d7fa24d..02f25960e 100644 --- a/sns/deployment/pom.xml +++ b/sns/deployment/pom.xml @@ -44,11 +44,6 @@ rest-assured test - - software.amazon.awssdk - netty-nio-client - test - software.amazon.awssdk url-connection-client