Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add @ServiceConnection support. #1075

Merged
merged 7 commits into from
Mar 18, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
<module>spring-cloud-aws-sqs</module>
<module>spring-cloud-aws-dynamodb</module>
<module>spring-cloud-aws-s3-parent</module>
<module>spring-cloud-aws-testcontainers</module>
<module>spring-cloud-aws-starters/spring-cloud-aws-starter</module>
<module>spring-cloud-aws-starters/spring-cloud-aws-starter-dynamodb</module>
<module>spring-cloud-aws-starters/spring-cloud-aws-starter-metrics</module>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,18 +48,25 @@ public class AwsClientBuilderConfigurer {
}

public <T extends AwsClientBuilder<?, ?>> T configure(T builder) {
return configure(builder, null, null);
return configure(builder, null, null, null);
}

public <T extends AwsClientBuilder<?, ?>> T configure(T builder, @Nullable AwsClientProperties clientProperties,
@Nullable AwsClientCustomizer<T> customizer) {
return configure(builder, clientProperties, null, customizer);
}

public <T extends AwsClientBuilder<?, ?>> T configure(T builder, @Nullable AwsClientProperties clientProperties,
@Nullable AwsConnectionDetails connectionDetails, @Nullable AwsClientCustomizer<T> customizer) {
Assert.notNull(builder, "builder is required");

builder.credentialsProvider(this.credentialsProvider).region(resolveRegion(clientProperties))
builder.credentialsProvider(this.credentialsProvider).region(resolveRegion(clientProperties, connectionDetails))
.overrideConfiguration(this.clientOverrideConfiguration);
Optional.ofNullable(this.awsProperties.getEndpoint()).ifPresent(builder::endpointOverride);
Optional.ofNullable(clientProperties).map(AwsClientProperties::getEndpoint)
.ifPresent(builder::endpointOverride);
Optional.ofNullable(connectionDetails).map(AwsConnectionDetails::getEndpoint)
.ifPresent(builder::endpointOverride);

Optional.ofNullable(this.awsProperties.getDefaultsMode()).ifPresent(builder::defaultsMode);
Optional.ofNullable(this.awsProperties.getFipsEnabled()).ifPresent(builder::fipsEnabled);
Expand All @@ -70,14 +77,21 @@ public class AwsClientBuilderConfigurer {
return builder;
}

public Region resolveRegion(@Nullable AwsClientProperties clientProperties) {
return resolveRegion(clientProperties, this.regionProvider);
public Region resolveRegion(@Nullable AwsClientProperties clientProperties,
@Nullable AwsConnectionDetails connectionDetails) {
return resolveRegion(clientProperties, connectionDetails, this.regionProvider);
}

public static Region resolveRegion(@Nullable AwsClientProperties clientProperties,
AwsRegionProvider regionProvider) {
return clientProperties != null && StringUtils.hasLength(clientProperties.getRegion())
? Region.of(clientProperties.getRegion())
: regionProvider.getRegion();
@Nullable AwsConnectionDetails connectionDetails, AwsRegionProvider regionProvider) {
if (connectionDetails != null && StringUtils.hasLength(connectionDetails.getRegion())) {
return Region.of(connectionDetails.getRegion());
}

if (clientProperties != null && StringUtils.hasLength(clientProperties.getRegion())) {
return Region.of(clientProperties.getRegion());
}

return regionProvider.getRegion();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright 2013-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.awspring.cloud.autoconfigure.core;

import java.net.URI;
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
import org.springframework.lang.Nullable;

/**
* Details required to establish a connection to a AWS.
*
* @author Maciej Walkowiak
* @since 3.2.0
*/
public interface AwsConnectionDetails extends ConnectionDetails {

/**
* AWS or Localstack endpoint.
*
* @return the AWS or Localstack endpoint or {@code null}
*/
@Nullable
default URI getEndpoint() {
return null;
}

/**
* AWS or Localstack region.
*
* @return the AWS or Localstack region or {@code null}
*/
@Nullable
default String getRegion() {
return null;
}

/**
* Credentials Access Key.
*
* @return the access key
*/
default String getAccessKey() {
return null;
}

/**
* Credentials Secret Key
*
* @return the secret key
*/
default String getSecretKey() {
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
Expand Down Expand Up @@ -59,21 +60,34 @@ public class CredentialsProviderAutoConfiguration {

private final CredentialsProperties properties;
private final AwsRegionProvider regionProvider;
private final ObjectProvider<AwsConnectionDetails> connectionDetails;

public CredentialsProviderAutoConfiguration(CredentialsProperties properties, AwsRegionProvider regionProvider) {
public CredentialsProviderAutoConfiguration(CredentialsProperties properties, AwsRegionProvider regionProvider,
ObjectProvider<AwsConnectionDetails> connectionDetails) {
this.properties = properties;
this.regionProvider = regionProvider;
this.connectionDetails = connectionDetails;
}

@Bean
public AwsCredentialsProvider credentialsProvider() {
return createCredentialsProvider(this.properties, this.regionProvider);
return createCredentialsProvider(this.properties, this.regionProvider, this.connectionDetails.getIfAvailable());
}

public static AwsCredentialsProvider createCredentialsProvider(CredentialsProperties properties,
AwsRegionProvider regionProvider) {
return createCredentialsProvider(properties, regionProvider, null);
}

public static AwsCredentialsProvider createCredentialsProvider(CredentialsProperties properties,
AwsRegionProvider regionProvider, @Nullable AwsConnectionDetails connectionDetails) {
final List<AwsCredentialsProvider> providers = new ArrayList<>();

if (connectionDetails != null && StringUtils.hasText(connectionDetails.getAccessKey())
&& StringUtils.hasText(connectionDetails.getSecretKey())) {
providers.add(createStaticCredentialsProvider(connectionDetails));
}

if (StringUtils.hasText(properties.getAccessKey()) && StringUtils.hasText(properties.getSecretKey())) {
providers.add(createStaticCredentialsProvider(properties));
}
Expand Down Expand Up @@ -114,6 +128,11 @@ private static StaticCredentialsProvider createStaticCredentialsProvider(Credent
.create(AwsBasicCredentials.create(properties.getAccessKey(), properties.getSecretKey()));
}

private static StaticCredentialsProvider createStaticCredentialsProvider(AwsConnectionDetails connectionDetails) {
return StaticCredentialsProvider
.create(AwsBasicCredentials.create(connectionDetails.getAccessKey(), connectionDetails.getSecretKey()));
}

private static ProfileCredentialsProvider createProfileCredentialProvider(Profile profile) {
ProfileFile profileFile;
if (profile.getPath() != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,15 @@

import io.awspring.cloud.autoconfigure.core.AwsClientBuilderConfigurer;
import io.awspring.cloud.autoconfigure.core.AwsClientCustomizer;
import io.awspring.cloud.autoconfigure.core.AwsConnectionDetails;
import io.awspring.cloud.autoconfigure.core.CredentialsProviderAutoConfiguration;
import io.awspring.cloud.autoconfigure.core.RegionProviderAutoConfiguration;
import io.awspring.cloud.dynamodb.*;
import io.awspring.cloud.dynamodb.DefaultDynamoDbTableNameResolver;
import io.awspring.cloud.dynamodb.DefaultDynamoDbTableSchemaResolver;
import io.awspring.cloud.dynamodb.DynamoDbOperations;
import io.awspring.cloud.dynamodb.DynamoDbTableNameResolver;
import io.awspring.cloud.dynamodb.DynamoDbTableSchemaResolver;
import io.awspring.cloud.dynamodb.DynamoDbTemplate;
import java.io.IOException;
import java.util.List;
import org.springframework.beans.factory.ObjectProvider;
Expand Down Expand Up @@ -57,6 +63,7 @@
@AutoConfigureAfter({ CredentialsProviderAutoConfiguration.class, RegionProviderAutoConfiguration.class })
@ConditionalOnProperty(name = "spring.cloud.aws.dynamodb.enabled", havingValue = "true", matchIfMissing = true)
public class DynamoDbAutoConfiguration {

@ConditionalOnProperty(name = "spring.cloud.aws.dynamodb.dax.url")
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = "software.amazon.dax.ClusterDaxClient")
Expand All @@ -65,7 +72,8 @@ static class DaxDynamoDbClient {
@ConditionalOnMissingBean
@Bean
public DynamoDbClient dynamoDbClient(DynamoDbProperties properties, AwsCredentialsProvider credentialsProvider,
AwsRegionProvider regionProvider) throws IOException {
AwsRegionProvider regionProvider, ObjectProvider<AwsConnectionDetails> connectionDetails)
throws IOException {
DaxProperties daxProperties = properties.getDax();

PropertyMapper propertyMapper = PropertyMapper.get();
Expand All @@ -90,8 +98,9 @@ public DynamoDbClient dynamoDbClient(DynamoDbProperties properties, AwsCredentia
propertyMapper.from(daxProperties.getSkipHostNameVerification()).whenNonNull()
.to(configuration::skipHostNameVerification);

configuration.region(AwsClientBuilderConfigurer.resolveRegion(properties, regionProvider))
.credentialsProvider(credentialsProvider).url(properties.getDax().getUrl());
configuration.region(AwsClientBuilderConfigurer.resolveRegion(properties,
connectionDetails.getIfAvailable(), regionProvider)).credentialsProvider(credentialsProvider)
.url(properties.getDax().getUrl());
return ClusterDaxClient.builder().overrideConfiguration(configuration.build()).build();
}

Expand All @@ -104,9 +113,10 @@ static class StandardDynamoDbClient {
@ConditionalOnMissingBean
@Bean
public DynamoDbClient dynamoDbClient(AwsClientBuilderConfigurer awsClientBuilderConfigurer,
ObjectProvider<AwsClientCustomizer<DynamoDbClientBuilder>> configurer, DynamoDbProperties properties) {
return awsClientBuilderConfigurer
.configure(DynamoDbClient.builder(), properties, configurer.getIfAvailable()).build();
ObjectProvider<AwsClientCustomizer<DynamoDbClientBuilder>> configurer,
ObjectProvider<AwsConnectionDetails> connectionDetails, DynamoDbProperties properties) {
return awsClientBuilderConfigurer.configure(DynamoDbClient.builder(), properties,
connectionDetails.getIfAvailable(), configurer.getIfAvailable()).build();
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import io.awspring.cloud.autoconfigure.core.AwsClientBuilderConfigurer;
import io.awspring.cloud.autoconfigure.core.AwsClientCustomizer;
import io.awspring.cloud.autoconfigure.core.AwsConnectionDetails;
import io.awspring.cloud.autoconfigure.core.CredentialsProviderAutoConfiguration;
import io.awspring.cloud.autoconfigure.core.RegionProviderAutoConfiguration;
import io.micrometer.cloudwatch2.CloudWatchConfig;
Expand Down Expand Up @@ -67,9 +68,10 @@ public CloudWatchMeterRegistry cloudWatchMeterRegistry(CloudWatchConfig config,
@ConditionalOnMissingBean
public CloudWatchAsyncClient cloudWatchAsyncClient(CloudWatchProperties properties,
AwsClientBuilderConfigurer awsClientBuilderConfigurer,
ObjectProvider<AwsClientCustomizer<CloudWatchAsyncClientBuilder>> configurer) {
return awsClientBuilderConfigurer
.configure(CloudWatchAsyncClient.builder(), properties, configurer.getIfAvailable()).build();
ObjectProvider<AwsClientCustomizer<CloudWatchAsyncClientBuilder>> configurer,
ObjectProvider<AwsConnectionDetails> connectionDetails) {
return awsClientBuilderConfigurer.configure(CloudWatchAsyncClient.builder(), properties,
connectionDetails.getIfAvailable(), configurer.getIfAvailable()).build();
}

@Bean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import io.awspring.cloud.autoconfigure.core.AwsClientBuilderConfigurer;
import io.awspring.cloud.autoconfigure.core.AwsClientCustomizer;
import io.awspring.cloud.autoconfigure.core.AwsConnectionDetails;
import io.awspring.cloud.autoconfigure.core.AwsProperties;
import io.awspring.cloud.autoconfigure.s3.properties.S3Properties;
import io.awspring.cloud.s3.InMemoryBufferingS3OutputStreamProvider;
Expand Down Expand Up @@ -70,9 +71,10 @@ public S3AutoConfiguration(S3Properties properties) {
@Bean
@ConditionalOnMissingBean
S3ClientBuilder s3ClientBuilder(AwsClientBuilderConfigurer awsClientBuilderConfigurer,
ObjectProvider<AwsClientCustomizer<S3ClientBuilder>> configurer) {
ObjectProvider<AwsClientCustomizer<S3ClientBuilder>> configurer,
ObjectProvider<AwsConnectionDetails> connectionDetails) {
S3ClientBuilder builder = awsClientBuilderConfigurer.configure(S3Client.builder(), this.properties,
configurer.getIfAvailable());
connectionDetails.getIfAvailable(), configurer.getIfAvailable());

Optional.ofNullable(this.properties.getCrossRegionEnabled()).ifPresent(builder::crossRegionAccessEnabled);

Expand All @@ -91,10 +93,11 @@ S3Template s3Template(S3Client s3Client, S3OutputStreamProvider s3OutputStreamPr
@Bean
@ConditionalOnMissingBean
S3Presigner s3Presigner(S3Properties properties, AwsProperties awsProperties,
AwsCredentialsProvider credentialsProvider, AwsRegionProvider regionProvider) {
AwsCredentialsProvider credentialsProvider, AwsRegionProvider regionProvider,
ObjectProvider<AwsConnectionDetails> connectionDetails) {
S3Presigner.Builder builder = S3Presigner.builder().serviceConfiguration(properties.toS3Configuration())
.credentialsProvider(credentialsProvider)
.region(AwsClientBuilderConfigurer.resolveRegion(properties, regionProvider));
.credentialsProvider(credentialsProvider).region(AwsClientBuilderConfigurer.resolveRegion(properties,
connectionDetails.getIfAvailable(), regionProvider));

if (properties.getEndpoint() != null) {
builder.endpointOverride(properties.getEndpoint());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@
package io.awspring.cloud.autoconfigure.s3;

import io.awspring.cloud.autoconfigure.core.AwsClientBuilderConfigurer;
import io.awspring.cloud.autoconfigure.core.AwsConnectionDetails;
import io.awspring.cloud.autoconfigure.core.AwsProperties;
import io.awspring.cloud.autoconfigure.s3.properties.S3CrtClientProperties;
import io.awspring.cloud.autoconfigure.s3.properties.S3Properties;
import java.util.Optional;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
Expand Down Expand Up @@ -61,9 +63,10 @@ public S3CrtAsyncClientAutoConfiguration(S3Properties properties, AwsProperties

@Bean
@ConditionalOnMissingBean
S3AsyncClient s3AsyncClient(AwsCredentialsProvider credentialsProvider) {
S3CrtAsyncClientBuilder builder = S3AsyncClient.crtBuilder().credentialsProvider(credentialsProvider)
.region(this.awsClientBuilderConfigurer.resolveRegion(this.properties));
S3AsyncClient s3AsyncClient(AwsCredentialsProvider credentialsProvider,
ObjectProvider<AwsConnectionDetails> connectionDetails) {
S3CrtAsyncClientBuilder builder = S3AsyncClient.crtBuilder().credentialsProvider(credentialsProvider).region(
this.awsClientBuilderConfigurer.resolveRegion(this.properties, connectionDetails.getIfAvailable()));
Optional.ofNullable(this.awsProperties.getEndpoint()).ifPresent(builder::endpointOverride);
Optional.ofNullable(this.properties.getEndpoint()).ifPresent(builder::endpointOverride);
Optional.ofNullable(this.properties.getCrossRegionEnabled()).ifPresent(builder::crossRegionAccessEnabled);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import io.awspring.cloud.autoconfigure.core.AwsClientBuilderConfigurer;
import io.awspring.cloud.autoconfigure.core.AwsClientCustomizer;
import io.awspring.cloud.autoconfigure.core.AwsConnectionDetails;
import io.awspring.cloud.autoconfigure.core.CredentialsProviderAutoConfiguration;
import io.awspring.cloud.autoconfigure.core.RegionProviderAutoConfiguration;
import io.awspring.cloud.ses.SimpleEmailServiceJavaMailSender;
Expand Down Expand Up @@ -54,9 +55,10 @@ public class SesAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public SesClient sesClient(SesProperties properties, AwsClientBuilderConfigurer awsClientBuilderConfigurer,
ObjectProvider<AwsClientCustomizer<SesClientBuilder>> configurer) {
return awsClientBuilderConfigurer.configure(SesClient.builder(), properties, configurer.getIfAvailable())
.build();
ObjectProvider<AwsClientCustomizer<SesClientBuilder>> configurer,
ObjectProvider<AwsConnectionDetails> connectionDetails) {
return awsClientBuilderConfigurer.configure(SesClient.builder(), properties, connectionDetails.getIfAvailable(),
configurer.getIfAvailable()).build();
}

@Bean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import io.awspring.cloud.autoconfigure.core.AwsClientBuilderConfigurer;
import io.awspring.cloud.autoconfigure.core.AwsClientCustomizer;
import io.awspring.cloud.autoconfigure.core.AwsConnectionDetails;
import io.awspring.cloud.autoconfigure.core.CredentialsProviderAutoConfiguration;
import io.awspring.cloud.autoconfigure.core.RegionProviderAutoConfiguration;
import io.awspring.cloud.sns.core.SnsOperations;
Expand Down Expand Up @@ -65,9 +66,10 @@ public class SnsAutoConfiguration {
@ConditionalOnMissingBean
@Bean
public SnsClient snsClient(SnsProperties properties, AwsClientBuilderConfigurer awsClientBuilderConfigurer,
ObjectProvider<AwsClientCustomizer<SnsClientBuilder>> configurer) {
return awsClientBuilderConfigurer.configure(SnsClient.builder(), properties, configurer.getIfAvailable())
.build();
ObjectProvider<AwsClientCustomizer<SnsClientBuilder>> configurer,
ObjectProvider<AwsConnectionDetails> connectionDetails) {
return awsClientBuilderConfigurer.configure(SnsClient.builder(), properties, connectionDetails.getIfAvailable(),
configurer.getIfAvailable()).build();
}

@ConditionalOnMissingBean(SnsOperations.class)
Expand Down
Loading
Loading