Skip to content

Commit

Permalink
Add LocalstackAwsClientFactory to simplify creating AWS clients point…
Browse files Browse the repository at this point in the history
…ing to LocalStack. (#1106)

Fixes #1079
  • Loading branch information
maciejwalkowiak authored Mar 29, 2024
1 parent c826648 commit 7a4970d
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 2 deletions.
28 changes: 27 additions & 1 deletion docs/src/main/asciidoc/testing.adoc
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[#testing]
== Testing

Spring Cloud AWS provides https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#features.testing.testcontainers.service-connections[@ServiceConnection] for https://java.testcontainers.org/modules/localstack/[LocalStack Container] that simplifies using Testcontainers LocalStack module with Spring Cloud AWS based projects.
Spring Cloud AWS provides utilities for https://java.testcontainers.org/modules/localstack/[LocalStack Container] that simplify using Testcontainers LocalStack module with Spring Cloud AWS based projects.

Maven coordinates, using <<index.adoc#bill-of-materials, Spring Cloud AWS BOM>>:

Expand All @@ -13,6 +13,10 @@ Maven coordinates, using <<index.adoc#bill-of-materials, Spring Cloud AWS BOM>>:
</dependency>
----

== Service Connection

https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#features.testing.testcontainers.service-connections[@ServiceConnection] for https://java.testcontainers.org/modules/localstack/[LocalStack Container] simplifies configuring Spring Cloud AWS based project to point to LocalStack instead of real AWS.

Once Spring Cloud AWS detects in application test code a `LocalStackContainer` bean annotated with `@ServiceConnection`, it will automatically configure `region` and `credentials` to point to a LocalStack container.

[source,java]
Expand All @@ -25,3 +29,25 @@ LocalStackContainer localStackContainer() {
----

To understand in depth how service connection works, follow https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#features.testing.testcontainers.service-connections[Spring Boot Reference Guide] on this topic.

=== Using AWS Clients with LocalStack

Spring Cloud AWS provides `LocalstackAwsClientFactory` that simplifies creating AWS clients pointing to LocalStack, when there is a need to configure an AWS client outside of Spring application context:

[source,java]
----
@Testcontainers
class LocalstackAwsClientFactoryTest {
@Container
private LocalStackContainer localStackContainer = new LocalStackContainer(
DockerImageName.parse("localstack/localstack:3.2.0"));
@Test
void aTest() {
LocalstackAwsClientFactory factory = new LocalstackAwsClientFactory(localStackContainer);
S3Client s3Client = factory.create(S3Client.builder());
// ...
}
}
----
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public class AwsClientBuilderConfigurer {
private final AwsProperties awsProperties;
private final ClientOverrideConfiguration clientOverrideConfiguration;

AwsClientBuilderConfigurer(AwsCredentialsProvider credentialsProvider, AwsRegionProvider regionProvider,
public AwsClientBuilderConfigurer(AwsCredentialsProvider credentialsProvider, AwsRegionProvider regionProvider,
AwsProperties awsProperties) {
this.credentialsProvider = credentialsProvider;
this.regionProvider = regionProvider;
Expand Down
4 changes: 4 additions & 0 deletions spring-cloud-aws-testcontainers/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
<groupId>io.awspring.cloud</groupId>
<artifactId>spring-cloud-aws-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>io.awspring.cloud</groupId>
<artifactId>spring-cloud-aws-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-testcontainers</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* 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.testcontainers;

import software.amazon.awssdk.awscore.client.builder.AwsClientBuilder;

/**
* Provides convenient way to construct AWS SDK clients.
*
* @author Maciej Walkowiak
* @since 3.2.0
*/
public interface AwsClientFactory {
<CLIENT, BUILDER extends AwsClientBuilder<?, CLIENT>> CLIENT create(BUILDER builder);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* 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.testcontainers;

import io.awspring.cloud.autoconfigure.core.AwsClientBuilderConfigurer;
import io.awspring.cloud.autoconfigure.core.AwsProperties;
import io.awspring.cloud.core.region.StaticRegionProvider;
import org.testcontainers.containers.localstack.LocalStackContainer;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.awscore.client.builder.AwsClientBuilder;

/**
* {@link AwsClientFactory} implementation that creates containers pointing to LocalStack instance running through
* {@link LocalStackContainer}.
*
* @author Maciej Walkowiak
* @since 3.2.0
*/
public class LocalstackAwsClientFactory implements AwsClientFactory {
private final AwsClientBuilderConfigurer configurer;

public LocalstackAwsClientFactory(LocalStackContainer localstack) {
this.configurer = clientBuilderConfigurer(localstack);
}

@Override
public <CLIENT, BUILDER extends AwsClientBuilder<?, CLIENT>> CLIENT create(BUILDER builder) {
return configurer.configure(builder).build();
}

private AwsClientBuilderConfigurer clientBuilderConfigurer(LocalStackContainer localstack) {
AwsProperties properties = new AwsProperties();
properties.setEndpoint(localstack.getEndpoint());

StaticCredentialsProvider credentialsProvider = StaticCredentialsProvider
.create(AwsBasicCredentials.create(localstack.getAccessKey(), localstack.getSecretKey()));
StaticRegionProvider regionProvider = new StaticRegionProvider(localstack.getRegion());
return new AwsClientBuilderConfigurer(credentialsProvider, regionProvider, properties);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* 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.testcontainers;

import static org.assertj.core.api.Assertions.assertThat;

import org.junit.jupiter.api.Test;
import org.testcontainers.containers.localstack.LocalStackContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.testcontainers.utility.DockerImageName;
import software.amazon.awssdk.services.s3.S3Client;

/**
* Tests for {@link LocalstackAwsClientFactory}.
*
* @author Maciej Walkowiak
*/
@Testcontainers
class LocalstackAwsClientFactoryTest {

@Container
private LocalStackContainer localStackContainer = new LocalStackContainer(
DockerImageName.parse("localstack/localstack:3.2.0"));

@Test
void createsClient() {
var factory = new LocalstackAwsClientFactory(localStackContainer);
try (var s3Client = factory.create(S3Client.builder())) {
s3Client.createBucket(r -> r.bucket("my-bucket"));
assertThat(s3Client.listBuckets().buckets()).hasSize(1);
}
}
}

0 comments on commit 7a4970d

Please sign in to comment.