Skip to content

Commit 2f4e08c

Browse files
authored
feat: Support for floci (awspring#1604)
1 parent d297172 commit 2f4e08c

15 files changed

Lines changed: 441 additions & 46 deletions

File tree

docs/src/main/asciidoc/docker-compose.adoc

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[#spring-cloud-aws-docker-compose]
22
== Docker Compose
33

4-
Spring Cloud AWS provides Docker Compose support for https://docs.localstack.cloud/references/docker-images/[LocalStack docker images] which simplifies local development of Spring Cloud AWS based projects.
4+
Spring Cloud AWS provides Docker Compose support for https://docs.localstack.cloud/references/docker-images/[LocalStack docker images] and https://floci.io/floci/getting-started/quick-start/[Floci docker images] which simplifies local development of Spring Cloud AWS based projects.
55

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

@@ -15,7 +15,7 @@ Maven coordinates, using <<index.adoc#bill-of-materials, Spring Cloud AWS BOM>>:
1515

1616
For more information about Spring Docker Compose support please refer to https://docs.spring.io/spring-boot/reference/features/dev-services.html#features.dev-services.docker-compose[official Spring documentation]
1717

18-
=== Example docker-compose.yaml file
18+
=== Localstack Example
1919

2020
[source,yaml]
2121
----
@@ -32,3 +32,19 @@ services:
3232

3333
`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY` and `AWS_DEFAULT_REGION` are required environment variables to ensure proper integration.
3434

35+
=== Floci Example
36+
37+
[source,yaml]
38+
----
39+
services:
40+
floci:
41+
image: hectorvent/floci
42+
environment:
43+
AWS_ACCESS_KEY_ID: noop
44+
AWS_SECRET_ACCESS_KEY: noop
45+
AWS_DEFAULT_REGION: eu-central-1
46+
ports:
47+
- "4566:4566"
48+
----
49+
50+
Floci works with any provided credentials, but the region is neccessary to ensure proper integration with Spring Cloud AWS.

spring-cloud-aws-docker-compose/src/main/java/io/awspring/cloud/docker/compose/AwsDockerComposeConnectionDetailsFactory.java

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,22 @@
2222
import org.springframework.boot.docker.compose.service.connection.DockerComposeConnectionSource;
2323

2424
/**
25-
* {@link DockerComposeConnectionDetailsFactory} to create {@link AwsConnectionDetails} for a {@code localstack}
26-
* service.
25+
* {@link DockerComposeConnectionDetailsFactory} to create {@link AwsConnectionDetails} for a {@code localstack} or
26+
* {@code floci} service.
2727
*
2828
* @author Dominik Kovács
29+
* @author Bastian Hellmann
2930
* @since 3.2.0
3031
*/
3132
class AwsDockerComposeConnectionDetailsFactory extends DockerComposeConnectionDetailsFactory<AwsConnectionDetails> {
3233

33-
private static final String[] LOCALSTACK_CONTAINER_NAMES = { "localstack/localstack", "localstack/localstack-pro" };
34+
private static final String[] AWS_EMULATOR_CONTAINER_NAMES = { "localstack/localstack", "localstack/localstack-pro",
35+
"floci/floci" };
3436

35-
private static final int LOCALSTACK_PORT = 4566;
37+
private static final int EMULATOR_PORT = 4566;
3638

3739
AwsDockerComposeConnectionDetailsFactory() {
38-
super(LOCALSTACK_CONTAINER_NAMES);
40+
super(AWS_EMULATOR_CONTAINER_NAMES);
3941
}
4042

4143
@Override
@@ -49,14 +51,14 @@ protected AwsConnectionDetails getDockerComposeConnectionDetails(DockerComposeCo
4951
private static final class AwsDockerComposeConnectionDetails extends DockerComposeConnectionDetails
5052
implements AwsConnectionDetails {
5153

52-
private final LocalStackEnvironment environment;
54+
private final AwsEmulatorEnvironment environment;
5355

5456
private final URI endpoint;
5557

5658
private AwsDockerComposeConnectionDetails(RunningService service) {
5759
super(service);
58-
this.environment = new LocalStackEnvironment(service.env());
59-
this.endpoint = URI.create("http://%s:%s".formatted(service.host(), service.ports().get(LOCALSTACK_PORT)));
60+
this.environment = new AwsEmulatorEnvironment(service.env());
61+
this.endpoint = URI.create("http://%s:%s".formatted(service.host(), service.ports().get(EMULATOR_PORT)));
6062
}
6163

6264
@Override

spring-cloud-aws-docker-compose/src/main/java/io/awspring/cloud/docker/compose/LocalStackEnvironment.java renamed to spring-cloud-aws-docker-compose/src/main/java/io/awspring/cloud/docker/compose/AwsEmulatorEnvironment.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,21 @@
1818
import java.util.Map;
1919

2020
/**
21-
* LocalStack environment details.
21+
* AWS emulator environment details.
2222
*
2323
* @author Dominik Kovács
24+
* @author Bastian Hellmann
2425
* @since 3.2.0
2526
*/
26-
class LocalStackEnvironment {
27+
class AwsEmulatorEnvironment {
2728

2829
private final String region;
2930

3031
private final String accessKey;
3132

3233
private final String secretKey;
3334

34-
LocalStackEnvironment(Map<String, String> env) {
35+
AwsEmulatorEnvironment(Map<String, String> env) {
3536
this.region = env.get("AWS_DEFAULT_REGION");
3637
this.accessKey = env.get("AWS_ACCESS_KEY_ID");
3738
this.secretKey = env.get("AWS_SECRET_ACCESS_KEY");

spring-cloud-aws-docker-compose/src/test/java/io/awspring/cloud/docker/compose/AwsDockerComposeConnectionDetailsFactoryTest.java

Lines changed: 84 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,39 +21,105 @@
2121
import java.io.IOException;
2222
import java.net.URI;
2323
import java.util.LinkedHashMap;
24-
import org.junit.jupiter.api.AfterAll;
24+
import org.junit.jupiter.api.AfterEach;
25+
import org.junit.jupiter.api.BeforeEach;
26+
import org.junit.jupiter.api.Nested;
2527
import org.junit.jupiter.api.Test;
2628
import org.springframework.boot.SpringApplication;
29+
import org.springframework.context.ConfigurableApplicationContext;
2730
import org.springframework.context.annotation.Configuration;
2831
import org.springframework.core.io.ClassPathResource;
2932
import org.springframework.core.io.Resource;
3033

3134
class AwsDockerComposeConnectionDetailsFactoryTest {
3235

3336
private final Resource dockerComposeResource = new ClassPathResource("docker-compose.yaml");
37+
private final Resource flociDockerComposeResource = new ClassPathResource("docker-compose-floci.yaml");
3438

35-
@AfterAll
36-
static void shutDown() {
37-
var shutdownHandlers = SpringApplication.getShutdownHandlers();
38-
((Runnable) shutdownHandlers).run();
39-
}
40-
41-
@Test
42-
void runCreatesConnectionDetailsThatCanAccessLocalStack() throws IOException {
39+
private ConfigurableApplicationContext runApplicationWith(Resource dockerComposeFile) throws IOException {
4340
var application = new SpringApplication(Config.class);
4441
var properties = new LinkedHashMap<String, Object>();
4542
properties.put("spring.docker.compose.skip.in-tests", "false");
46-
properties.put("spring.docker.compose.file", dockerComposeResource.getFile());
43+
properties.put("spring.docker.compose.file", dockerComposeFile.getFile());
4744
properties.put("spring.docker.compose.stop.command", "down");
4845
application.setDefaultProperties(properties);
49-
var connectionDetails = application.run().getBean(AwsConnectionDetails.class);
50-
51-
assertThat(connectionDetails.getAccessKey()).isEqualTo("noop");
52-
assertThat(connectionDetails.getSecretKey()).isEqualTo("noop");
53-
assertThat(connectionDetails.getRegion()).isEqualTo("eu-central-1");
54-
assertThat(connectionDetails.getEndpoint()).satisfiesAnyOf(
55-
endpoint -> assertThat(endpoint).isEqualTo(URI.create("http://localhost:4566")),
56-
endpoint -> assertThat(endpoint).isEqualTo(URI.create("http://127.0.0.1:4566")));
46+
return application.run();
47+
}
48+
49+
private void shutDownContext(ConfigurableApplicationContext context) {
50+
if (context != null) {
51+
context.close();
52+
}
53+
}
54+
55+
@Nested
56+
class LocalstackTests {
57+
58+
private ConfigurableApplicationContext applicationContext;
59+
private AwsConnectionDetails connectionDetails;
60+
61+
@BeforeEach
62+
void setUp() throws IOException {
63+
this.applicationContext = runApplicationWith(dockerComposeResource);
64+
this.connectionDetails = this.applicationContext.getBean(AwsConnectionDetails.class);
65+
}
66+
67+
@Test
68+
void createsAwsConnectionDetailsBean() {
69+
assertThat(connectionDetails).isNotNull();
70+
}
71+
72+
@Test
73+
void resolvesExpectedConnectionDetails() {
74+
assertThat(connectionDetails).isNotNull();
75+
76+
assertThat(connectionDetails.getAccessKey()).isEqualTo("noop");
77+
assertThat(connectionDetails.getSecretKey()).isEqualTo("noop");
78+
assertThat(connectionDetails.getRegion()).isEqualTo("eu-central-1");
79+
assertThat(connectionDetails.getEndpoint()).satisfiesAnyOf(
80+
endpoint -> assertThat(endpoint).isEqualTo(URI.create("http://localhost:4566")),
81+
endpoint -> assertThat(endpoint).isEqualTo(URI.create("http://127.0.0.1:4566")));
82+
}
83+
84+
@AfterEach
85+
void shutDown() {
86+
shutDownContext(this.applicationContext);
87+
this.applicationContext = null;
88+
}
89+
}
90+
91+
@Nested
92+
class FlociTests {
93+
94+
private ConfigurableApplicationContext applicationContext;
95+
private AwsConnectionDetails connectionDetails;
96+
97+
@BeforeEach
98+
void setUp() throws IOException {
99+
this.applicationContext = runApplicationWith(flociDockerComposeResource);
100+
this.connectionDetails = this.applicationContext.getBean(AwsConnectionDetails.class);
101+
}
102+
103+
@Test
104+
void createsAwsConnectionDetailsBean() {
105+
assertThat(connectionDetails).isNotNull();
106+
}
107+
108+
@Test
109+
void resolvesExpectedConnectionDetails() {
110+
assertThat(connectionDetails.getAccessKey()).isEqualTo("noop");
111+
assertThat(connectionDetails.getSecretKey()).isEqualTo("noop");
112+
assertThat(connectionDetails.getRegion()).isEqualTo("eu-central-1");
113+
assertThat(connectionDetails.getEndpoint()).satisfiesAnyOf(
114+
endpoint -> assertThat(endpoint).isEqualTo(URI.create("http://localhost:4567")),
115+
endpoint -> assertThat(endpoint).isEqualTo(URI.create("http://127.0.0.1:4567")));
116+
}
117+
118+
@AfterEach
119+
void shutDown() {
120+
shutDownContext(this.applicationContext);
121+
this.applicationContext = null;
122+
}
57123
}
58124

59125
@Configuration(proxyBeanMethods = false)

spring-cloud-aws-docker-compose/src/test/java/io/awspring/cloud/docker/compose/LocalStackEnvironmentTest.java renamed to spring-cloud-aws-docker-compose/src/test/java/io/awspring/cloud/docker/compose/AwsEmulatorEnvironmentTest.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,41 +21,41 @@
2121
import java.util.Map;
2222
import org.junit.jupiter.api.Test;
2323

24-
class LocalStackEnvironmentTest {
24+
class AwsEmulatorEnvironmentTest {
2525

2626
@Test
2727
void getRegionWhenRegionIsNotSet() {
28-
var environment = new LocalStackEnvironment(Collections.emptyMap());
28+
var environment = new AwsEmulatorEnvironment(Collections.emptyMap());
2929
assertThat(environment.getRegion()).isNull();
3030
}
3131

3232
@Test
3333
void getRegionWhenRegionIsSet() {
34-
var environment = new LocalStackEnvironment(Map.of("AWS_DEFAULT_REGION", "us-west-1"));
34+
var environment = new AwsEmulatorEnvironment(Map.of("AWS_DEFAULT_REGION", "us-west-1"));
3535
assertThat(environment.getRegion()).isEqualTo("us-west-1");
3636
}
3737

3838
@Test
3939
void getAccessKeyWhenAccessKeyIsNotSet() {
40-
var environment = new LocalStackEnvironment(Collections.emptyMap());
40+
var environment = new AwsEmulatorEnvironment(Collections.emptyMap());
4141
assertThat(environment.getAccessKey()).isNull();
4242
}
4343

4444
@Test
4545
void getAccessKeyWhenAccessKeyIsSet() {
46-
var environment = new LocalStackEnvironment(Map.of("AWS_ACCESS_KEY_ID", "access-key"));
46+
var environment = new AwsEmulatorEnvironment(Map.of("AWS_ACCESS_KEY_ID", "access-key"));
4747
assertThat(environment.getAccessKey()).isEqualTo("access-key");
4848
}
4949

5050
@Test
5151
void getSecretKeyWhenSecretKeyIsNotSet() {
52-
var environment = new LocalStackEnvironment(Collections.emptyMap());
52+
var environment = new AwsEmulatorEnvironment(Collections.emptyMap());
5353
assertThat(environment.getSecretKey()).isNull();
5454
}
5555

5656
@Test
5757
void getSecretKeyWhenSecretKeyIsSet() {
58-
var environment = new LocalStackEnvironment(Map.of("AWS_SECRET_ACCESS_KEY", "secret-key"));
58+
var environment = new AwsEmulatorEnvironment(Map.of("AWS_SECRET_ACCESS_KEY", "secret-key"));
5959
assertThat(environment.getSecretKey()).isEqualTo("secret-key");
6060
}
6161
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
services:
2+
floci:
3+
image: floci/floci:1.5.25
4+
environment:
5+
AWS_ACCESS_KEY_ID: noop
6+
AWS_SECRET_ACCESS_KEY: noop
7+
AWS_DEFAULT_REGION: eu-central-1
8+
ports:
9+
- "4567:4566"

spring-cloud-aws-samples/spring-cloud-aws-dynamodb-sample/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@
2828
<artifactId>spring-cloud-aws-testcontainers</artifactId>
2929
<scope>test</scope>
3030
</dependency>
31+
<dependency>
32+
<groupId>org.testcontainers</groupId>
33+
<artifactId>testcontainers-localstack</artifactId>
34+
<scope>test</scope>
35+
</dependency>
3136
</dependencies>
3237

3338
</project>

spring-cloud-aws-testcontainers/pom.xml

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,24 @@
3333
<artifactId>spring-boot-testcontainers</artifactId>
3434
</dependency>
3535
<dependency>
36-
<groupId>org.testcontainers</groupId>
37-
<artifactId>testcontainers-localstack</artifactId>
36+
<groupId>org.testcontainers</groupId>
37+
<artifactId>testcontainers-localstack</artifactId>
38+
<optional>true</optional>
39+
</dependency>
40+
<dependency>
41+
<groupId>io.floci</groupId>
42+
<artifactId>testcontainers-floci</artifactId>
43+
<optional>true</optional>
44+
<version>2.9.0</version>
3845
</dependency>
3946
<dependency>
4047
<groupId>org.springframework</groupId>
4148
<artifactId>spring-test</artifactId>
4249
<scope>test</scope>
4350
</dependency>
4451
<dependency>
45-
<groupId>org.testcontainers</groupId>
46-
<artifactId>testcontainers-junit-jupiter</artifactId>
52+
<groupId>org.testcontainers</groupId>
53+
<artifactId>testcontainers-junit-jupiter</artifactId>
4754
<scope>test</scope>
4855
</dependency>
4956
<dependency>

0 commit comments

Comments
 (0)