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

Modernize LocalStackContainer #695

Merged
merged 1 commit into from
May 16, 2018
Merged
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,20 @@
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import org.jetbrains.annotations.Nullable;
import org.junit.rules.ExternalResource;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import org.rnorth.ducttape.Preconditions;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.LogMessageWaitStrategy;
import org.testcontainers.containers.wait.strategy.Wait;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import static org.testcontainers.containers.BindMode.READ_WRITE;

/**
* <p>Container for Atlassian Labs Localstack, 'A fully functional local AWS cloud stack'.</p>
* <p>{@link LocalStackContainer#withServices(Service...)} should be used to select which services
Expand All @@ -25,41 +26,34 @@
* {@link LocalStackContainer#getDefaultCredentialsProvider()}
* be used to obtain compatible endpoint configuration and credentials, respectively.</p>
*/
public class LocalStackContainer extends ExternalResource {

@Nullable private GenericContainer delegate;
private Service[] services;

@Override
protected void before() throws Throwable {
public class LocalStackContainer extends GenericContainer<LocalStackContainer> {

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

final String servicesList = Arrays
.stream(services)
.map(Service::getLocalStackName)
.collect(Collectors.joining(","));
private final List<Service> services = new ArrayList<>();

final Integer[] portsList = Arrays
.stream(services)
.map(Service::getPort)
.collect(Collectors.toSet()).toArray(new Integer[]{});
public LocalStackContainer() {
this(VERSION);
}

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

delegate.start();
withFileSystemBind("//var/run/docker.sock", "/var/run/docker.sock");
waitingFor(Wait.forLogMessage(".*Ready\\.\n", 1));
}

@Override
protected void after() {
protected void configure() {
super.configure();

Preconditions.check("services list must not be empty", !services.isEmpty());

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

delegate.stop();
for (Service service : services) {
addExposedPort(service.getPort());
}
}

/**
Expand All @@ -68,8 +62,8 @@ protected void after() {
* @return this container object
*/
public LocalStackContainer withServices(Service... services) {
this.services = services;
return this;
this.services.addAll(Arrays.asList(services));
return self();
}

/**
Expand All @@ -85,19 +79,14 @@ public LocalStackContainer withServices(Service... services) {
* @return an {@link AwsClientBuilder.EndpointConfiguration}
*/
public AwsClientBuilder.EndpointConfiguration getEndpointConfiguration(Service service) {

if (delegate == null) {
throw new IllegalStateException("LocalStack has not been started yet!");
}

final String address = delegate.getContainerIpAddress();
final String address = getContainerIpAddress();
String ipAddress = address;
try {
ipAddress = InetAddress.getByName(address).getHostAddress();
} catch (UnknownHostException ignored) {

}
ipAddress = ipAddress + ".xip.io";
ipAddress = ipAddress + ".nip.io";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick, but I assume this is not an IP address anymore after concatenating the String 😛

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I blame the original author ;)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI I changed to nip.io because xip.io is unreliable (and caused some CI failures)

while (true) {
try {
//noinspection ResultOfMethodCallIgnored
Expand All @@ -112,7 +101,7 @@ public AwsClientBuilder.EndpointConfiguration getEndpointConfiguration(Service s
"http://" +
ipAddress +
":" +
delegate.getMappedPort(service.getPort()), "us-east-1");
getMappedPort(service.getPort()), "us-east-1");
}

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

@RequiredArgsConstructor
@Getter
@FieldDefaults(makeFinal = true)
public enum Service {
API_GATEWAY("apigateway", 4567),
KINESIS("kinesis", 4568),
Expand All @@ -149,18 +141,8 @@ public enum Service {
CLOUDFORMATION("cloudformation", 4581),
CLOUDWATCH("cloudwatch", 4582);

private final String localStackName;
private final int port;

Service(String localstackName, int port) {
this.localStackName = localstackName;
this.port = port;
}

public String getLocalStackName() {
return localStackName;
}
String localStackName;

public Integer getPort() { return port; }
int port;
}
}