Skip to content

Commit ca2dd7a

Browse files
authored
Modernize LocalStackContainer (#695)
1 parent ba62d9e commit ca2dd7a

File tree

1 file changed

+34
-52
lines changed

1 file changed

+34
-52
lines changed

modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java

Lines changed: 34 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,20 @@
44
import com.amazonaws.auth.AWSStaticCredentialsProvider;
55
import com.amazonaws.auth.BasicAWSCredentials;
66
import com.amazonaws.client.builder.AwsClientBuilder;
7-
import org.jetbrains.annotations.Nullable;
8-
import org.junit.rules.ExternalResource;
7+
import lombok.Getter;
8+
import lombok.RequiredArgsConstructor;
9+
import lombok.experimental.FieldDefaults;
910
import org.rnorth.ducttape.Preconditions;
1011
import org.testcontainers.containers.GenericContainer;
11-
import org.testcontainers.containers.wait.LogMessageWaitStrategy;
12+
import org.testcontainers.containers.wait.strategy.Wait;
1213

1314
import java.net.InetAddress;
1415
import java.net.UnknownHostException;
16+
import java.util.ArrayList;
1517
import java.util.Arrays;
18+
import java.util.List;
1619
import java.util.stream.Collectors;
1720

18-
import static org.testcontainers.containers.BindMode.READ_WRITE;
19-
2021
/**
2122
* <p>Container for Atlassian Labs Localstack, 'A fully functional local AWS cloud stack'.</p>
2223
* <p>{@link LocalStackContainer#withServices(Service...)} should be used to select which services
@@ -25,41 +26,34 @@
2526
* {@link LocalStackContainer#getDefaultCredentialsProvider()}
2627
* be used to obtain compatible endpoint configuration and credentials, respectively.</p>
2728
*/
28-
public class LocalStackContainer extends ExternalResource {
29-
30-
@Nullable private GenericContainer delegate;
31-
private Service[] services;
32-
33-
@Override
34-
protected void before() throws Throwable {
29+
public class LocalStackContainer extends GenericContainer<LocalStackContainer> {
3530

36-
Preconditions.check("services list must not be empty", services != null && services.length > 0);
31+
public static final String VERSION = "0.8.6";
3732

38-
final String servicesList = Arrays
39-
.stream(services)
40-
.map(Service::getLocalStackName)
41-
.collect(Collectors.joining(","));
33+
private final List<Service> services = new ArrayList<>();
4234

43-
final Integer[] portsList = Arrays
44-
.stream(services)
45-
.map(Service::getPort)
46-
.collect(Collectors.toSet()).toArray(new Integer[]{});
35+
public LocalStackContainer() {
36+
this(VERSION);
37+
}
4738

48-
delegate = new GenericContainer("localstack/localstack:0.8.5")
49-
.withExposedPorts(portsList)
50-
.withFileSystemBind("//var/run/docker.sock", "/var/run/docker.sock", READ_WRITE)
51-
.waitingFor(new LogMessageWaitStrategy().withRegEx(".*Ready\\.\n"))
52-
.withEnv("SERVICES", servicesList);
39+
public LocalStackContainer(String version) {
40+
super("localstack/localstack:" + version);
5341

54-
delegate.start();
42+
withFileSystemBind("//var/run/docker.sock", "/var/run/docker.sock");
43+
waitingFor(Wait.forLogMessage(".*Ready\\.\n", 1));
5544
}
5645

5746
@Override
58-
protected void after() {
47+
protected void configure() {
48+
super.configure();
49+
50+
Preconditions.check("services list must not be empty", !services.isEmpty());
5951

60-
Preconditions.check("delegate must have been created by before()", delegate != null);
52+
withEnv("SERVICES", services.stream().map(Service::getLocalStackName).collect(Collectors.joining(",")));
6153

62-
delegate.stop();
54+
for (Service service : services) {
55+
addExposedPort(service.getPort());
56+
}
6357
}
6458

6559
/**
@@ -68,8 +62,8 @@ protected void after() {
6862
* @return this container object
6963
*/
7064
public LocalStackContainer withServices(Service... services) {
71-
this.services = services;
72-
return this;
65+
this.services.addAll(Arrays.asList(services));
66+
return self();
7367
}
7468

7569
/**
@@ -85,19 +79,14 @@ public LocalStackContainer withServices(Service... services) {
8579
* @return an {@link AwsClientBuilder.EndpointConfiguration}
8680
*/
8781
public AwsClientBuilder.EndpointConfiguration getEndpointConfiguration(Service service) {
88-
89-
if (delegate == null) {
90-
throw new IllegalStateException("LocalStack has not been started yet!");
91-
}
92-
93-
final String address = delegate.getContainerIpAddress();
82+
final String address = getContainerIpAddress();
9483
String ipAddress = address;
9584
try {
9685
ipAddress = InetAddress.getByName(address).getHostAddress();
9786
} catch (UnknownHostException ignored) {
9887

9988
}
100-
ipAddress = ipAddress + ".xip.io";
89+
ipAddress = ipAddress + ".nip.io";
10190
while (true) {
10291
try {
10392
//noinspection ResultOfMethodCallIgnored
@@ -112,7 +101,7 @@ public AwsClientBuilder.EndpointConfiguration getEndpointConfiguration(Service s
112101
"http://" +
113102
ipAddress +
114103
":" +
115-
delegate.getMappedPort(service.getPort()), "us-east-1");
104+
getMappedPort(service.getPort()), "us-east-1");
116105
}
117106

118107
/**
@@ -130,6 +119,9 @@ public AWSCredentialsProvider getDefaultCredentialsProvider() {
130119
return new AWSStaticCredentialsProvider(new BasicAWSCredentials("accesskey", "secretkey"));
131120
}
132121

122+
@RequiredArgsConstructor
123+
@Getter
124+
@FieldDefaults(makeFinal = true)
133125
public enum Service {
134126
API_GATEWAY("apigateway", 4567),
135127
KINESIS("kinesis", 4568),
@@ -149,18 +141,8 @@ public enum Service {
149141
CLOUDFORMATION("cloudformation", 4581),
150142
CLOUDWATCH("cloudwatch", 4582);
151143

152-
private final String localStackName;
153-
private final int port;
154-
155-
Service(String localstackName, int port) {
156-
this.localStackName = localstackName;
157-
this.port = port;
158-
}
159-
160-
public String getLocalStackName() {
161-
return localStackName;
162-
}
144+
String localStackName;
163145

164-
public Integer getPort() { return port; }
146+
int port;
165147
}
166148
}

0 commit comments

Comments
 (0)