-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
MSSQLServerContainer.java
162 lines (128 loc) · 5.33 KB
/
MSSQLServerContainer.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
package org.testcontainers.containers;
import org.testcontainers.utility.DockerImageName;
import org.testcontainers.utility.LicenseAcceptance;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Stream;
/**
* @author Stefan Hufschmidt
*/
public class MSSQLServerContainer<SELF extends MSSQLServerContainer<SELF>> extends JdbcDatabaseContainer<SELF> {
private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("mcr.microsoft.com/mssql/server");
@Deprecated
public static final String DEFAULT_TAG = "2017-CU12";
public static final String NAME = "sqlserver";
public static final String IMAGE = DEFAULT_IMAGE_NAME.getUnversionedPart();
public static final Integer MS_SQL_SERVER_PORT = 1433;
static final String DEFAULT_USER = "SA";
static final String DEFAULT_PASSWORD = "A_Str0ng_Required_Password";
private String password = DEFAULT_PASSWORD;
private static final int DEFAULT_STARTUP_TIMEOUT_SECONDS = 240;
private static final int DEFAULT_CONNECT_TIMEOUT_SECONDS = 240;
private static final Pattern[] PASSWORD_CATEGORY_VALIDATION_PATTERNS = new Pattern[] {
Pattern.compile("[A-Z]+"),
Pattern.compile("[a-z]+"),
Pattern.compile("[0-9]+"),
Pattern.compile("[^a-zA-Z0-9]+", Pattern.CASE_INSENSITIVE),
};
/**
* @deprecated use {@link MSSQLServerContainer(DockerImageName)} instead
*/
@Deprecated
public MSSQLServerContainer() {
this(DEFAULT_IMAGE_NAME.withTag(DEFAULT_TAG));
}
public MSSQLServerContainer(final String dockerImageName) {
this(DockerImageName.parse(dockerImageName));
}
public MSSQLServerContainer(final DockerImageName dockerImageName) {
super(dockerImageName);
dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME);
withStartupTimeoutSeconds(DEFAULT_STARTUP_TIMEOUT_SECONDS);
withConnectTimeoutSeconds(DEFAULT_CONNECT_TIMEOUT_SECONDS);
addExposedPort(MS_SQL_SERVER_PORT);
}
@Override
public Set<Integer> getLivenessCheckPortNumbers() {
return super.getLivenessCheckPortNumbers();
}
@Override
protected void configure() {
// If license was not accepted programatically, check if it was accepted via resource file
if (!getEnvMap().containsKey("ACCEPT_EULA")) {
LicenseAcceptance.assertLicenseAccepted(this.getDockerImageName());
acceptLicense();
}
addEnv("SA_PASSWORD", password);
}
/**
* Accepts the license for the SQLServer container by setting the ACCEPT_EULA=Y
* variable as described at <a href="https://hub.docker.com/_/microsoft-mssql-server">https://hub.docker.com/_/microsoft-mssql-server</a>
*/
public SELF acceptLicense() {
addEnv("ACCEPT_EULA", "Y");
return self();
}
@Override
public String getDriverClassName() {
return "com.microsoft.sqlserver.jdbc.SQLServerDriver";
}
@Override
protected String constructUrlForConnection(String queryString) {
// The JDBC driver of MS SQL Server enables encryption by default for versions > 10.1.0.
// We need to disable it by default to be able to use the container without having to pass extra params.
// See https://github.com/microsoft/mssql-jdbc/releases/tag/v10.1.0
if (urlParameters.keySet().stream().map(String::toLowerCase).noneMatch("encrypt"::equals)) {
urlParameters.put("encrypt", "false");
}
return super.constructUrlForConnection(queryString);
}
@Override
public String getJdbcUrl() {
String additionalUrlParams = constructUrlParameters(";", ";");
return "jdbc:sqlserver://" + getHost() + ":" + getMappedPort(MS_SQL_SERVER_PORT) + additionalUrlParams;
}
@Override
public String getUsername() {
return DEFAULT_USER;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getTestQueryString() {
return "SELECT 1";
}
@Override
public SELF withPassword(final String password) {
checkPasswordStrength(password);
this.password = password;
return self();
}
private void checkPasswordStrength(String password) {
if (password == null) {
throw new IllegalArgumentException("Null password is not allowed");
}
if (password.length() < 8) {
throw new IllegalArgumentException("Password should be at least 8 characters long");
}
if (password.length() > 128) {
throw new IllegalArgumentException("Password can be up to 128 characters long");
}
long satisfiedCategories = Stream
.of(PASSWORD_CATEGORY_VALIDATION_PATTERNS)
.filter(p -> p.matcher(password).find())
.count();
if (satisfiedCategories < 3) {
throw new IllegalArgumentException(
"Password must contain characters from three of the following four categories:\n" +
" - Latin uppercase letters (A through Z)\n" +
" - Latin lowercase letters (a through z)\n" +
" - Base 10 digits (0 through 9)\n" +
" - Non-alphanumeric characters such as: exclamation point (!), dollar sign ($), number sign (#), " +
"or percent (%)."
);
}
}
}