4
4
import com .amazonaws .auth .AWSStaticCredentialsProvider ;
5
5
import com .amazonaws .auth .BasicAWSCredentials ;
6
6
import com .amazonaws .client .builder .AwsClientBuilder ;
7
- import lombok .Getter ;
8
- import lombok .RequiredArgsConstructor ;
9
- import lombok .experimental .FieldDefaults ;
10
- import org .rnorth .ducttape .Preconditions ;
11
- import org .testcontainers .containers .GenericContainer ;
12
- import org .testcontainers .containers .wait .strategy .Wait ;
13
- import org .testcontainers .utility .DockerImageName ;
14
- import org .testcontainers .utility .TestcontainersConfiguration ;
15
-
16
7
import java .net .InetAddress ;
17
8
import java .net .URI ;
18
9
import java .net .URISyntaxException ;
21
12
import java .util .Arrays ;
22
13
import java .util .List ;
23
14
import java .util .stream .Collectors ;
15
+ import lombok .Getter ;
16
+ import lombok .RequiredArgsConstructor ;
17
+ import lombok .experimental .FieldDefaults ;
18
+ import lombok .extern .slf4j .Slf4j ;
19
+ import org .rnorth .ducttape .Preconditions ;
20
+ import org .testcontainers .containers .GenericContainer ;
21
+ import org .testcontainers .containers .wait .strategy .Wait ;
22
+ import org .testcontainers .utility .ComparableVersion ;
23
+ import org .testcontainers .utility .DockerImageName ;
24
+ import org .testcontainers .utility .TestcontainersConfiguration ;
24
25
25
26
/**
26
27
* <p>Container for Atlassian Labs Localstack, 'A fully functional local AWS cloud stack'.</p>
30
31
* {@link LocalStackContainer#getDefaultCredentialsProvider()}
31
32
* be used to obtain compatible endpoint configuration and credentials, respectively.</p>
32
33
*/
34
+ @ Slf4j
33
35
public class LocalStackContainer extends GenericContainer <LocalStackContainer > {
34
36
35
- public static final String VERSION = "0.10.8" ;
37
+ public static final String VERSION = "0.11.2" ;
38
+ static final int PORT = 4566 ;
36
39
private static final String HOSTNAME_EXTERNAL_ENV_VAR = "HOSTNAME_EXTERNAL" ;
37
-
38
40
private final List <Service > services = new ArrayList <>();
39
41
42
+ /**
43
+ * Whether or to assume that all APIs run on different ports (when <code>true</code>) or are
44
+ * exposed on a single port (<code>false</code>). From the Localstack README:
45
+ *
46
+ * <blockquote>Note: Starting with version 0.11.0, all APIs are exposed via a single edge
47
+ * service [...] The API-specific endpoints below are still left for backward-compatibility but
48
+ * may get removed in a future release - please reconfigure your client SDKs to start using the
49
+ * single edge endpoint URL!</blockquote>
50
+ * <p>
51
+ * Testcontainers will use the tag of the docker image to infer whether or not the used version
52
+ * of Localstack supports this feature.
53
+ */
54
+ private final boolean legacyMode ;
55
+
40
56
/**
41
57
* @deprecated use {@link LocalStackContainer(DockerImageName)} instead
42
58
*/
@@ -53,13 +69,41 @@ public LocalStackContainer(String version) {
53
69
this (DockerImageName .parse (TestcontainersConfiguration .getInstance ().getLocalStackImage () + ":" + version ));
54
70
}
55
71
72
+ /**
73
+ * @param dockerImageName image name to use for Localstack
74
+ */
56
75
public LocalStackContainer (final DockerImageName dockerImageName ) {
76
+ this (dockerImageName , shouldRunInLegacyMode (dockerImageName .getVersionPart ()));
77
+ }
78
+
79
+ /**
80
+ * @param dockerImageName image name to use for Localstack
81
+ * @param useLegacyMode if true, each AWS service is exposed on a different port
82
+ */
83
+ public LocalStackContainer (final DockerImageName dockerImageName , boolean useLegacyMode ) {
57
84
super (dockerImageName );
85
+ this .legacyMode = useLegacyMode ;
58
86
59
87
withFileSystemBind ("//var/run/docker.sock" , "/var/run/docker.sock" );
60
88
waitingFor (Wait .forLogMessage (".*Ready\\ .\n " , 1 ));
61
89
}
62
90
91
+ private static boolean shouldRunInLegacyMode (String version ) {
92
+ if (version .equals ("latest" )) {
93
+ return false ;
94
+ }
95
+
96
+ ComparableVersion comparableVersion = new ComparableVersion (version );
97
+ if (comparableVersion .isSemanticVersion ()) {
98
+ boolean versionRequiresLegacyMode = comparableVersion .isLessThan ("0.11" );
99
+ return versionRequiresLegacyMode ;
100
+ }
101
+
102
+ log .warn ("Version {} is not a semantic version, LocalStack will run in legacy mode." , version );
103
+ log .warn ("Consider using \" LocalStackContainer(DockerImageName dockerImageName, boolean legacyMode)\" constructor if you want to disable legacy mode." );
104
+ return true ;
105
+ }
106
+
63
107
@ Override
64
108
protected void configure () {
65
109
super .configure ();
@@ -81,9 +125,14 @@ protected void configure() {
81
125
}
82
126
logger ().info ("{} environment variable set to {} ({})" , HOSTNAME_EXTERNAL_ENV_VAR , getEnvMap ().get (HOSTNAME_EXTERNAL_ENV_VAR ), hostnameExternalReason );
83
127
84
- for (Service service : services ) {
85
- addExposedPort (service .getPort ());
86
- }
128
+ exposePorts ();
129
+ }
130
+
131
+ private void exposePorts () {
132
+ services .stream ()
133
+ .map (this ::getServicePort )
134
+ .distinct ()
135
+ .forEach (this ::addExposedPort );
87
136
}
88
137
89
138
/**
@@ -154,12 +203,16 @@ public URI getEndpointOverride(Service service) {
154
203
return new URI ("http://" +
155
204
ipAddress +
156
205
":" +
157
- getMappedPort (service . getPort ( )));
206
+ getMappedPort (getServicePort ( service )));
158
207
} catch (UnknownHostException | URISyntaxException e ) {
159
208
throw new IllegalStateException ("Cannot obtain endpoint URL" , e );
160
209
}
161
210
}
162
211
212
+ private int getServicePort (Service service ) {
213
+ return legacyMode ? service .port : PORT ;
214
+ }
215
+
163
216
/**
164
217
* Provides a {@link AWSCredentialsProvider} that is preconfigured to communicate with a given simulated service.
165
218
* The credentials provider should be set in the AWS Java SDK when building a client, e.g.:
@@ -271,5 +324,13 @@ public enum Service {
271
324
String localStackName ;
272
325
273
326
int port ;
327
+
328
+ @ Deprecated
329
+ /*
330
+ Since version 0.11, LocalStack exposes all services on a single (4566) port.
331
+ */
332
+ public int getPort () {
333
+ return port ;
334
+ }
274
335
}
275
336
}
0 commit comments