Skip to content

Commit 490a1a7

Browse files
committed
Fix docker compose environment parsing for bitnami images
Signed-off-by: He Zean <[email protected]>
1 parent cc49b30 commit 490a1a7

File tree

15 files changed

+199
-32
lines changed

15 files changed

+199
-32
lines changed

spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/postgres/PostgresJdbcDockerComposeConnectionDetailsFactoryIntegrationTests.java

+16-3
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,12 @@
3535
* @author Andy Wilkinson
3636
* @author Phillip Webb
3737
* @author Scott Frederick
38+
* @author He Zean
3839
*/
3940
class PostgresJdbcDockerComposeConnectionDetailsFactoryIntegrationTests {
4041

4142
@DockerComposeTest(composeFile = "postgres-compose.yaml", image = TestImage.POSTGRESQL)
42-
void runCreatesConnectionDetails(JdbcConnectionDetails connectionDetails) {
43+
void runCreatesConnectionDetails(JdbcConnectionDetails connectionDetails) throws ClassNotFoundException {
4344
assertConnectionDetails(connectionDetails);
4445
}
4546

@@ -53,10 +54,21 @@ void runCreatesConnectionDetailsThatCanAccessDatabaseWhenHostAuthMethodIsTrust(
5354
}
5455

5556
@DockerComposeTest(composeFile = "postgres-bitnami-compose.yaml", image = TestImage.BITNAMI_POSTGRESQL)
56-
void runWithBitnamiImageCreatesConnectionDetails(JdbcConnectionDetails connectionDetails) {
57+
void runWithBitnamiImageCreatesConnectionDetails(JdbcConnectionDetails connectionDetails)
58+
throws ClassNotFoundException {
5759
assertConnectionDetails(connectionDetails);
5860
}
5961

62+
@DockerComposeTest(composeFile = "postgres-bitnami-compose-empty-password.yaml",
63+
image = TestImage.BITNAMI_POSTGRESQL)
64+
void runWithBitnamiImageCreatesConnectionDetailsWithAllowEmptyPassword(JdbcConnectionDetails connectionDetails)
65+
throws ClassNotFoundException {
66+
assertThat(connectionDetails.getUsername()).isEqualTo("myuser");
67+
assertThat(connectionDetails.getPassword()).isEmpty();
68+
assertThat(connectionDetails.getJdbcUrl()).startsWith("jdbc:postgresql://").endsWith("/mydatabase");
69+
checkDatabaseAccess(connectionDetails);
70+
}
71+
6072
@DockerComposeTest(composeFile = "postgres-application-name-compose.yaml", image = TestImage.POSTGRESQL)
6173
void runCreatesConnectionDetailsApplicationName(JdbcConnectionDetails connectionDetails)
6274
throws ClassNotFoundException {
@@ -68,10 +80,11 @@ void runCreatesConnectionDetailsApplicationName(JdbcConnectionDetails connection
6880
.isEqualTo("spring boot");
6981
}
7082

71-
private void assertConnectionDetails(JdbcConnectionDetails connectionDetails) {
83+
private void assertConnectionDetails(JdbcConnectionDetails connectionDetails) throws ClassNotFoundException {
7284
assertThat(connectionDetails.getUsername()).isEqualTo("myuser");
7385
assertThat(connectionDetails.getPassword()).isEqualTo("secret");
7486
assertThat(connectionDetails.getJdbcUrl()).startsWith("jdbc:postgresql://").endsWith("/mydatabase");
87+
checkDatabaseAccess(connectionDetails);
7588
}
7689

7790
private void checkDatabaseAccess(JdbcConnectionDetails connectionDetails) throws ClassNotFoundException {

spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/postgres/PostgresR2dbcDockerComposeConnectionDetailsFactoryIntegrationTests.java

+13
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
* @author Andy Wilkinson
3838
* @author Phillip Webb
3939
* @author Scott Frederick
40+
* @author He Zean
4041
*/
4142
class PostgresR2dbcDockerComposeConnectionDetailsFactoryIntegrationTests {
4243

@@ -61,6 +62,17 @@ void runWithBitnamiImageCreatesConnectionDetails(R2dbcConnectionDetails connecti
6162
assertConnectionDetails(connectionDetails);
6263
}
6364

65+
@DockerComposeTest(composeFile = "postgres-bitnami-compose-empty-password.yaml",
66+
image = TestImage.BITNAMI_POSTGRESQL)
67+
void runWithBitnamiImageCreatesConnectionDetailsWithAllowEmptyPassword(R2dbcConnectionDetails connectionDetails) {
68+
ConnectionFactoryOptions connectionFactoryOptions = connectionDetails.getConnectionFactoryOptions();
69+
assertThat(connectionFactoryOptions.getRequiredValue(ConnectionFactoryOptions.USER)).isEqualTo("myuser");
70+
assertThat(connectionFactoryOptions.getValue(ConnectionFactoryOptions.PASSWORD)).isNull();
71+
assertThat(connectionFactoryOptions.getRequiredValue(ConnectionFactoryOptions.DATABASE))
72+
.isEqualTo("mydatabase");
73+
checkDatabaseAccess(connectionDetails);
74+
}
75+
6476
@DockerComposeTest(composeFile = "postgres-application-name-compose.yaml", image = TestImage.POSTGRESQL)
6577
void runCreatesConnectionDetailsApplicationName(R2dbcConnectionDetails connectionDetails) {
6678
assertConnectionDetails(connectionDetails);
@@ -78,6 +90,7 @@ private void assertConnectionDetails(R2dbcConnectionDetails connectionDetails) {
7890
assertThat(options.getRequiredValue(ConnectionFactoryOptions.USER)).isEqualTo("myuser");
7991
assertThat(options.getRequiredValue(ConnectionFactoryOptions.PASSWORD)).isEqualTo("secret");
8092
assertThat(options.getRequiredValue(ConnectionFactoryOptions.DRIVER)).isEqualTo("postgresql");
93+
checkDatabaseAccess(connectionDetails);
8194
}
8295

8396
private void checkDatabaseAccess(R2dbcConnectionDetails connectionDetails) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
services:
2+
database:
3+
image: '{imageName}'
4+
ports:
5+
- '5432'
6+
environment:
7+
- 'POSTGRESQL_USERNAME=myuser'
8+
- 'POSTGRESQL_DATABASE=mydatabase'
9+
- 'ALLOW_EMPTY_PASSWORD=yes'

spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/postgres/postgres-bitnami-compose.yaml

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ services:
44
ports:
55
- '5432'
66
environment:
7-
- 'POSTGRESQL_USER=myuser'
8-
- 'POSTGRESQL_DB=mydatabase'
7+
- 'POSTGRESQL_USERNAME=myuser'
8+
- 'POSTGRESQL_DATABASE=mydatabase'
99
- 'POSTGRESQL_PASSWORD=secret'

spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/clickhouse/ClickHouseEnvironment.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2024 the original author or authors.
2+
* Copyright 2012-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@
1818

1919
import java.util.Map;
2020

21+
import org.springframework.boot.docker.compose.service.util.BitnamiUtils;
2122
import org.springframework.util.Assert;
2223
import org.springframework.util.StringUtils;
2324

@@ -41,7 +42,7 @@ class ClickHouseEnvironment {
4142
}
4243

4344
private String extractPassword(Map<String, String> env) {
44-
boolean allowEmpty = Boolean.parseBoolean(env.getOrDefault("ALLOW_EMPTY_PASSWORD", Boolean.FALSE.toString()));
45+
boolean allowEmpty = BitnamiUtils.isBooleanYes(env.get("ALLOW_EMPTY_PASSWORD"));
4546
String password = env.get("CLICKHOUSE_PASSWORD");
4647
Assert.state(StringUtils.hasLength(password) || allowEmpty, "No ClickHouse password found");
4748
return (password != null) ? password : "";

spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/mariadb/MariaDbEnvironment.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2024 the original author or authors.
2+
* Copyright 2012-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@
1818

1919
import java.util.Map;
2020

21+
import org.springframework.boot.docker.compose.service.util.BitnamiUtils;
2122
import org.springframework.util.Assert;
2223
import org.springframework.util.StringUtils;
2324

@@ -53,7 +54,8 @@ private String extractPassword(Map<String, String> env) {
5354
Assert.state(!env.containsKey("MYSQL_RANDOM_ROOT_PASSWORD"), "MYSQL_RANDOM_ROOT_PASSWORD is not supported");
5455
Assert.state(!env.containsKey("MARIADB_ROOT_PASSWORD_HASH"), "MARIADB_ROOT_PASSWORD_HASH is not supported");
5556
boolean allowEmpty = env.containsKey("MARIADB_ALLOW_EMPTY_PASSWORD")
56-
|| env.containsKey("MYSQL_ALLOW_EMPTY_PASSWORD") || env.containsKey("ALLOW_EMPTY_PASSWORD");
57+
|| env.containsKey("MYSQL_ALLOW_EMPTY_PASSWORD")
58+
|| BitnamiUtils.isBooleanYes(env.get("ALLOW_EMPTY_PASSWORD"));
5759
String password = env.get("MARIADB_PASSWORD");
5860
password = (password != null) ? password : env.get("MYSQL_PASSWORD");
5961
password = (password != null) ? password : env.get("MARIADB_ROOT_PASSWORD");

spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/mysql/MySqlEnvironment.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2024 the original author or authors.
2+
* Copyright 2012-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@
1818

1919
import java.util.Map;
2020

21+
import org.springframework.boot.docker.compose.service.util.BitnamiUtils;
2122
import org.springframework.util.Assert;
2223
import org.springframework.util.StringUtils;
2324

@@ -45,7 +46,8 @@ class MySqlEnvironment {
4546

4647
private String extractPassword(Map<String, String> env) {
4748
Assert.state(!env.containsKey("MYSQL_RANDOM_ROOT_PASSWORD"), "MYSQL_RANDOM_ROOT_PASSWORD is not supported");
48-
boolean allowEmpty = env.containsKey("MYSQL_ALLOW_EMPTY_PASSWORD") || env.containsKey("ALLOW_EMPTY_PASSWORD");
49+
boolean allowEmpty = env.containsKey("MYSQL_ALLOW_EMPTY_PASSWORD")
50+
|| BitnamiUtils.isBooleanYes(env.get("ALLOW_EMPTY_PASSWORD"));
4951
String password = env.get("MYSQL_PASSWORD");
5052
password = (password != null) ? password : env.get("MYSQL_ROOT_PASSWORD");
5153
Assert.state(StringUtils.hasLength(password) || allowEmpty, "No MySQL password found");

spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/postgres/PostgresEnvironment.java

+14-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2024 the original author or authors.
2+
* Copyright 2012-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,7 +17,9 @@
1717
package org.springframework.boot.docker.compose.service.connection.postgres;
1818

1919
import java.util.Map;
20+
import java.util.Objects;
2021

22+
import org.springframework.boot.docker.compose.service.util.BitnamiUtils;
2123
import org.springframework.util.Assert;
2224
import org.springframework.util.StringUtils;
2325

@@ -29,6 +31,7 @@
2931
* @author Phillip Webb
3032
* @author Scott Frederick
3133
* @author Sidmar Theodoro
34+
* @author He Zean
3235
*/
3336
class PostgresEnvironment {
3437

@@ -39,18 +42,24 @@ class PostgresEnvironment {
3942
private final String database;
4043

4144
PostgresEnvironment(Map<String, String> env) {
42-
this.username = env.getOrDefault("POSTGRES_USER", env.getOrDefault("POSTGRESQL_USER", "postgres"));
45+
this.username = env.getOrDefault("POSTGRES_USER", env.getOrDefault("POSTGRESQL_USERNAME", "postgres"));
4346
this.password = extractPassword(env);
44-
this.database = env.getOrDefault("POSTGRES_DB", env.getOrDefault("POSTGRESQL_DB", this.username));
47+
this.database = env.getOrDefault("POSTGRES_DB", env.getOrDefault("POSTGRESQL_DATABASE", this.username));
4548
}
4649

4750
private String extractPassword(Map<String, String> env) {
4851
if (isUsingTrustHostAuthMethod(env)) {
4952
return null;
5053
}
5154
String password = env.getOrDefault("POSTGRES_PASSWORD", env.get("POSTGRESQL_PASSWORD"));
52-
Assert.state(StringUtils.hasLength(password), "PostgreSQL password must be provided");
53-
return password;
55+
Assert.state(isAllowingEmptyPassword(env) || StringUtils.hasLength(password),
56+
"PostgreSQL password must be provided");
57+
return Objects.requireNonNullElse(password, "");
58+
}
59+
60+
private boolean isAllowingEmptyPassword(Map<String, String> env) {
61+
String allowEmptyPassword = env.get("ALLOW_EMPTY_PASSWORD");
62+
return BitnamiUtils.isBooleanYes(allowEmptyPassword);
5463
}
5564

5665
private boolean isUsingTrustHostAuthMethod(Map<String, String> env) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright 2012-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.docker.compose.service.util;
18+
19+
import java.util.Locale;
20+
import java.util.Objects;
21+
22+
/**
23+
* Vendor specific utilities for Bitnami.
24+
*
25+
* @author He Zean
26+
* @since 3.4.1
27+
*/
28+
public abstract class BitnamiUtils {
29+
30+
/**
31+
* Checks if the provided argument is a boolean or is the string 'yes/true'.
32+
* Equivalent to the {@code is_boolean_yes} function in Bitnami's
33+
* {@code libvalidations.sh}.
34+
* @param value value to check
35+
* @return parsed boolean value
36+
*/
37+
public static boolean isBooleanYes(String value) {
38+
if (Objects.isNull(value)) {
39+
return false;
40+
}
41+
42+
value = value.toLowerCase(Locale.ROOT);
43+
return "1".equals(value) || "yes".equals(value) || "true".equals(value);
44+
}
45+
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright 2012-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
/**
18+
* Service utilities for Docker Compose.
19+
*/
20+
package org.springframework.boot.docker.compose.service.util;

spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/service/connection/clickhouse/ClickHouseEnvironmentTests.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2024 the original author or authors.
2+
* Copyright 2012-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -45,14 +45,14 @@ void getPasswordWhenHasPassword() {
4545

4646
@Test
4747
void getPasswordWhenHasNoPasswordAndAllowEmptyPassword() {
48-
ClickHouseEnvironment environment = new ClickHouseEnvironment(Map.of("ALLOW_EMPTY_PASSWORD", "true"));
48+
ClickHouseEnvironment environment = new ClickHouseEnvironment(Map.of("ALLOW_EMPTY_PASSWORD", "yes"));
4949
assertThat(environment.getPassword()).isEmpty();
5050
}
5151

5252
@Test
53-
void getPasswordWhenHasNoPasswordAndAllowEmptyPasswordIsFalse() {
53+
void getPasswordWhenHasNoPasswordAndAllowEmptyPasswordIsNo() {
5454
assertThatIllegalStateException()
55-
.isThrownBy(() -> new ClickHouseEnvironment(Map.of("ALLOW_EMPTY_PASSWORD", "false")))
55+
.isThrownBy(() -> new ClickHouseEnvironment(Map.of("ALLOW_EMPTY_PASSWORD", "no")))
5656
.withMessage("No ClickHouse password found");
5757
}
5858

spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/service/connection/mariadb/MariaDbEnvironmentTests.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2024 the original author or authors.
2+
* Copyright 2012-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -134,7 +134,7 @@ void getPasswordWhenHasMariadbPasswordAndMysqlRootPassword() {
134134
@Test
135135
void getPasswordWhenHasNoPasswordAndAllowEmptyPassword() {
136136
MariaDbEnvironment environment = new MariaDbEnvironment(
137-
Map.of("ALLOW_EMPTY_PASSWORD", "true", "MARIADB_DATABASE", "db"));
137+
Map.of("ALLOW_EMPTY_PASSWORD", "yes", "MARIADB_DATABASE", "db"));
138138
assertThat(environment.getPassword()).isEmpty();
139139
}
140140

spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/service/connection/mysql/MySqlEnvironmentTests.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2024 the original author or authors.
2+
* Copyright 2012-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -90,7 +90,7 @@ void getPasswordWhenHasNoPasswordAndMysqlAllowEmptyPassword() {
9090
@Test
9191
void getPasswordWhenHasNoPasswordAndAllowEmptyPassword() {
9292
MySqlEnvironment environment = new MySqlEnvironment(
93-
Map.of("ALLOW_EMPTY_PASSWORD", "true", "MYSQL_DATABASE", "db"));
93+
Map.of("ALLOW_EMPTY_PASSWORD", "yes", "MYSQL_DATABASE", "db"));
9494
assertThat(environment.getPassword()).isEmpty();
9595
}
9696

0 commit comments

Comments
 (0)