From b7d3595545ff048e85427492d4e81a9bca5dfa00 Mon Sep 17 00:00:00 2001 From: meyerg Date: Fri, 23 Feb 2024 10:38:57 -0600 Subject: [PATCH 1/6] Initial POC of a PostgreSqlReplicatedBindingsPropertiesProcessor --- ...PostgreSQLBindingsPropertiesProcessor.java | 89 +++++ ...PostgreSqlBindingsPropertiesProcessor.java | 84 +---- ...ReplicatedBindingsPropertiesProcessor.java | 160 +++++++++ ...icatedBindingsPropertiesProcessorTest.java | 329 ++++++++++++++++++ 4 files changed, 580 insertions(+), 82 deletions(-) create mode 100644 spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/AbstractPostgreSQLBindingsPropertiesProcessor.java create mode 100644 spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/PostgreSqlReplicatedBindingsPropertiesProcessor.java create mode 100644 spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/PostgreSqlReplicatedBindingsPropertiesProcessorTest.java diff --git a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/AbstractPostgreSQLBindingsPropertiesProcessor.java b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/AbstractPostgreSQLBindingsPropertiesProcessor.java new file mode 100644 index 0000000..73f20ec --- /dev/null +++ b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/AbstractPostgreSQLBindingsPropertiesProcessor.java @@ -0,0 +1,89 @@ +package org.springframework.cloud.bindings.boot; + +import java.nio.file.FileSystems; +import java.util.ArrayList; +import java.util.List; + +import org.springframework.cloud.bindings.Binding; + +public abstract class AbstractPostgreSQLBindingsPropertiesProcessor implements BindingsPropertiesProcessor { + + /** + * sslmode determines whether or with what priority a secure SSL TCP/IP connection will be negotiated with the server. + */ + public static final String SSL_MODE = "sslmode"; + /** + * sslrootcert specifies the name of a file containing SSL certificate authority (CA) certificate(s). + */ + public static final String SSL_ROOT_CERT = "sslrootcert"; + /** + * options Specifies command-line options to send to the server at connection start. + * CockroachDB uses this to pass in cluster routing id + */ + public static final String OPTIONS = "options"; + + /** + * Returns a concatenated list of options parameters defined in the bound file `options` in the format specified in + * PostgreSQL Doc. + *

+ * CockroachDB, which shares the same 'postgresql://' protocol as PostgreSQL, has customized options to meet its + * distributed database nature. + * Refer to Client Connection Parameters. + */ + protected String buildDbOptions(Binding binding) { + String options = binding.getSecret().getOrDefault(OPTIONS, ""); + String crdbOption = ""; + List dbOptions = new ArrayList<>(); + if (!options.equals("")) { + String[] allOpts = options.split("&"); + for (String o : allOpts) { + String[] keyval = o.split("="); + if (keyval.length != 2 || keyval[0].length() == 0 || keyval[1].length() == 0) { + continue; + } + if (keyval[0].equals("--cluster")) { + crdbOption = keyval[0] + "=" + keyval[1]; + } else { + dbOptions.add("-c " + keyval[0] + "=" + keyval[1]); + } + } + } + String combinedOptions = crdbOption; + if (dbOptions.size() > 0) { + String otherOpts = String.join(" ", dbOptions); + if (!combinedOptions.equals("")) { + combinedOptions = combinedOptions + " " + otherOpts; + } else { + combinedOptions = otherOpts; + } + } + if (!"".equals(combinedOptions)) { + combinedOptions = "options=" + combinedOptions; + } + return combinedOptions; + } + + /** + * Returns a concatenated string of all ssl parameters for enabling one-way TLS (PostgreSQL certifies itself) + * Refer to PostgreSQL Doc + */ + protected String buildSslModeParam(Binding binding) { + //process ssl params + //https://www.postgresql.org/docs/14/libpq-connect.html + String sslmode = binding.getSecret().getOrDefault(SSL_MODE, ""); + String sslRootCert = binding.getSecret().getOrDefault(SSL_ROOT_CERT, ""); + StringBuilder sslparam = new StringBuilder(); + if (!"".equals(sslmode)) { + sslparam.append(SSL_MODE).append("=").append(sslmode); + } + if (!"".equals(sslRootCert)) { + if (!"".equals(sslmode)) { + sslparam.append("&"); + } + sslparam.append(SSL_ROOT_CERT).append("=") + .append(binding.getPath()).append(FileSystems.getDefault().getSeparator()) + .append(sslRootCert); + } + return sslparam.toString(); + } +} diff --git a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/PostgreSqlBindingsPropertiesProcessor.java b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/PostgreSqlBindingsPropertiesProcessor.java index 4cdd783..6a5b8f5 100644 --- a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/PostgreSqlBindingsPropertiesProcessor.java +++ b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/PostgreSqlBindingsPropertiesProcessor.java @@ -20,9 +20,6 @@ import org.springframework.cloud.bindings.Bindings; import org.springframework.core.env.Environment; -import java.nio.file.FileSystems; -import java.util.ArrayList; -import java.util.List; import java.util.Map; import static org.springframework.cloud.bindings.boot.Guards.isTypeEnabled; @@ -32,25 +29,13 @@ * * @see JDBC URL Format */ -public final class PostgreSqlBindingsPropertiesProcessor implements BindingsPropertiesProcessor { +public final class PostgreSqlBindingsPropertiesProcessor extends AbstractPostgreSQLBindingsPropertiesProcessor { /** * The {@link Binding} type that this processor is interested in: {@value}. **/ public static final String TYPE = "postgresql"; - /** - * sslmode determines whether or with what priority a secure SSL TCP/IP connection will be negotiated with the server. - */ - public static final String SSL_MODE = "sslmode"; - /** - * sslrootcert specifies the name of a file containing SSL certificate authority (CA) certificate(s). - */ - public static final String SSL_ROOT_CERT = "sslrootcert"; - /** - * options Specifies command-line options to send to the server at connection start. - * CockroachDB uses this to pass in cluster routing id - */ - public static final String OPTIONS = "options"; + public static final String SPRING_DATASOURCE_URL = "spring.datasource.url"; public static final String SPRING_R2DBC_URL = "spring.r2dbc.url"; @@ -99,69 +84,4 @@ public void process(Environment environment, Bindings bindings, MapPostgreSQL Doc. - *

- * CockroachDB, which shares the same 'postgresql://' protocol as PostgreSQL, has customized options to meet its - * distributed database nature. - * Refer to Client Connection Parameters. - */ - private String buildDbOptions(Binding binding) { - String options = binding.getSecret().getOrDefault(OPTIONS, ""); - String crdbOption = ""; - List dbOptions = new ArrayList<>(); - if (!options.equals("")) { - String[] allOpts = options.split("&"); - for (String o : allOpts) { - String[] keyval = o.split("="); - if (keyval.length != 2 || keyval[0].length() == 0 || keyval[1].length() == 0) { - continue; - } - if (keyval[0].equals("--cluster")) { - crdbOption = keyval[0] + "=" + keyval[1]; - } else { - dbOptions.add("-c " + keyval[0] + "=" + keyval[1]); - } - } - } - String combinedOptions = crdbOption; - if (dbOptions.size() > 0) { - String otherOpts = String.join(" ", dbOptions); - if (!combinedOptions.equals("")) { - combinedOptions = combinedOptions + " " + otherOpts; - } else { - combinedOptions = otherOpts; - } - } - if (!"".equals(combinedOptions)) { - combinedOptions = "options=" + combinedOptions; - } - return combinedOptions; - } - - /** - * Returns a concatenated string of all ssl parameters for enabling one-way TLS (PostgreSQL certifies itself) - * Refer to PostgreSQL Doc - */ - private String buildSslModeParam(Binding binding) { - //process ssl params - //https://www.postgresql.org/docs/14/libpq-connect.html - String sslmode = binding.getSecret().getOrDefault(SSL_MODE, ""); - String sslRootCert = binding.getSecret().getOrDefault(SSL_ROOT_CERT, ""); - StringBuilder sslparam = new StringBuilder(); - if (!"".equals(sslmode)) { - sslparam.append(SSL_MODE).append("=").append(sslmode); - } - if (!"".equals(sslRootCert)) { - if (!"".equals(sslmode)) { - sslparam.append("&"); - } - sslparam.append(SSL_ROOT_CERT).append("=") - .append(binding.getPath()).append(FileSystems.getDefault().getSeparator()) - .append(sslRootCert); - } - return sslparam.toString(); - } } diff --git a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/PostgreSqlReplicatedBindingsPropertiesProcessor.java b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/PostgreSqlReplicatedBindingsPropertiesProcessor.java new file mode 100644 index 0000000..336faef --- /dev/null +++ b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/PostgreSqlReplicatedBindingsPropertiesProcessor.java @@ -0,0 +1,160 @@ +/* + * Copyright 2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.bindings.boot; + +import org.springframework.boot.context.event.ApplicationPreparedEvent; +import org.springframework.boot.logging.DeferredLog; +import org.springframework.cloud.bindings.Binding; +import org.springframework.cloud.bindings.Bindings; +import org.springframework.context.ApplicationListener; +import org.springframework.core.env.Environment; +import org.springframework.util.StringUtils; + +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +import static org.springframework.cloud.bindings.boot.Guards.isTypeEnabled; + +/** + * An implementation of {@link BindingsPropertiesProcessor} that detects {@link Binding}s of type: {@value TYPE}. + */ +public class PostgreSqlReplicatedBindingsPropertiesProcessor extends AbstractPostgreSQLBindingsPropertiesProcessor implements ApplicationListener { + + /** + * The {@link Binding} type that this processor is interested in: {@value}. + **/ + public static final String TYPE = "postgresql-replicated"; + + private static final DeferredLog LOG = new DeferredLog(); + + private static final String CORRELATION_FIELD = "correlation"; + + private static final String FUNCTION_FILED = "function"; + + private static final String RW_FUNCTION = "rw"; + + private static final String RO_FUNCTION = "ro"; + + private static final String JDBC_BASE_TEMPLATE = "spring.datasource.replicated[%d].%s"; + + private static final String R2DBC_BASE_TEMPLATE = "spring.r2dbc.replicated[%d].%s"; + + private static final String JDBC_PROPERTY_TEMPLATE = "spring.datasource.replicated[%d].%s.%s"; + + private static final String R2DBC_PROPERTY_TEMPLATE = "spring.r2dbc.replicated[%d].%s.%s"; + + @Override + public void process(Environment environment, Bindings bindings, Map properties) { + + if (!isTypeEnabled(environment, TYPE)) { + return; + } + + /* + * Group bindings together by correlation and filter out those that don't have a correlation + * or don't have a valid function + */ + final var bindingsCorrelations = bindings.filterBindings(TYPE) + .stream() + .filter(b -> { + if (!StringUtils.hasText(b.getSecret().get(CORRELATION_FIELD))) { + + LOG.warn(String.format("Replicate postgres binding %s is missing required correlation filed and will be ignored", b.getName())); + return false; + } + + final var fnc = b.getSecret().getOrDefault(FUNCTION_FILED, "").toLowerCase(); + if (fnc.compareTo(RW_FUNCTION) != 0 && fnc.compareTo(RO_FUNCTION) != 0) { + + LOG.warn(String.format("Replicate postgres binding %s does not have a valid function and will be ignored", b.getName())); + return false; + } + return true; + }) + .collect(Collectors.groupingBy(b -> b.getSecret().get(CORRELATION_FIELD))); + + final var cnt = new AtomicInteger(0); + + bindingsCorrelations.forEach((correlationName, binds) -> { + + final var idx = cnt.getAndIncrement(); + + binds.stream().forEach(binding -> { + final var fnc = binding.getSecret().get(FUNCTION_FILED).toLowerCase(); + + final var map = new MapMapper(binding.getSecret(), properties); + + map.from("correlation").to(String.format(JDBC_BASE_TEMPLATE, idx, "name")); + + map.from("password").to(formatJDBCTemplate(idx, fnc, "password")); + + map.from("host", "port", "database").to(formatJDBCTemplate(idx, fnc, "url"), + (host, port, database) -> String.format("jdbc:postgresql://%s:%s/%s", host, port, database)); + + String sslParam = buildSslModeParam(binding); + String sslModeOptions = buildDbOptions(binding); + if (!"".equals(sslParam) && !"".equals(sslModeOptions)) { + sslModeOptions = sslParam + "&" + sslModeOptions; + } else if (!"".equals(sslParam) ) { + sslModeOptions = sslParam; + } + + if (!"".equals(sslModeOptions)) { + properties.put(formatJDBCTemplate(idx, fnc, "url") , + properties.get(formatJDBCTemplate(idx, fnc, "url")) + "?" + sslModeOptions); + } + map.from("username").to(formatJDBCTemplate(idx, fnc, "username")); + + // jdbcURL takes precedence + map.from("jdbc-url").to(formatJDBCTemplate(idx, fnc, "url")); + + properties.put(formatJDBCTemplate(idx, fnc, "driver-class-name"), "org.postgresql.Driver"); + + //r2dbc properties + map.from("correlation").to(String.format(R2DBC_BASE_TEMPLATE, idx, "name")); + map.from("password").to(formatR2DBCTemplate(idx, fnc, "password")); + map.from("host", "port", "database").to(formatR2DBCTemplate(idx, fnc, "url"), + (host, port, database) -> String.format("r2dbc:postgresql://%s:%s/%s", host, port, database)); + if (!"".equals(sslModeOptions)) { + properties.put(formatR2DBCTemplate(idx, fnc, "url") , + properties.get(formatR2DBCTemplate(idx, fnc, "url")) + "?" + sslModeOptions); + } + map.from("username").to(formatR2DBCTemplate(idx, fnc, "username")); + + // r2dbcURL takes precedence + map.from("r2dbc-url").to(formatR2DBCTemplate(idx, fnc, "url")); + + }); + }); + } + + private String formatJDBCTemplate(int idx, String fnc, String field) + { + return String.format(JDBC_PROPERTY_TEMPLATE, idx, fnc, field); + } + + private String formatR2DBCTemplate(int idx, String fnc, String field) + { + return String.format(R2DBC_PROPERTY_TEMPLATE, idx, fnc, field); + } + + @Override + public void onApplicationEvent(ApplicationPreparedEvent event) { + LOG.replayTo(getClass()); + } +} diff --git a/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/PostgreSqlReplicatedBindingsPropertiesProcessorTest.java b/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/PostgreSqlReplicatedBindingsPropertiesProcessorTest.java new file mode 100644 index 0000000..462661f --- /dev/null +++ b/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/PostgreSqlReplicatedBindingsPropertiesProcessorTest.java @@ -0,0 +1,329 @@ +package org.springframework.cloud.bindings.boot; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.cloud.bindings.boot.PostgreSqlReplicatedBindingsPropertiesProcessor.TYPE; + +import java.nio.file.Paths; +import java.util.HashMap; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.cloud.bindings.Binding; +import org.springframework.cloud.bindings.Bindings; +import org.springframework.cloud.bindings.FluentMap; +import org.springframework.mock.env.MockEnvironment; + +@DisplayName("PostgreSQLReplicated BindingsPropertiesProcessor") +public class PostgreSqlReplicatedBindingsPropertiesProcessorTest { + + private final FluentMap rwsecret = new FluentMap() + .withEntry(Binding.TYPE, TYPE) + .withEntry("database", "testrw-database") + .withEntry("host", "testrw-host") + .withEntry("password", "testrw-password") + .withEntry("port", "testrw-port") + .withEntry("username", "testrw-username") + .withEntry("correlation", "test-correlation") + .withEntry("function", "rw"); + + private final FluentMap rosecret = new FluentMap() + .withEntry(Binding.TYPE, TYPE) + .withEntry("database", "testro-database") + .withEntry("host", "testro-host") + .withEntry("password", "testro-password") + .withEntry("port", "testro-port") + .withEntry("username", "testro-username") + .withEntry("correlation", "test-correlation") + .withEntry("function", "ro"); + + private final MockEnvironment environment = new MockEnvironment(); + + private final HashMap properties = new HashMap<>(); + + @Test + @DisplayName("composes jdbc url from host port and database") + void testJdbc() { + Bindings bindings = new Bindings( + new Binding("dbrw", Paths.get("dbrw-path"), rwsecret), + new Binding("dbro", Paths.get("dbro-path"), rosecret) + ); + new PostgreSqlReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); + assertThat(properties) + .containsEntry("spring.datasource.replicated[0].rw.driver-class-name", "org.postgresql.Driver") + .containsEntry("spring.datasource.replicated[0].rw.password", "testrw-password") + .containsEntry("spring.datasource.replicated[0].rw.url", "jdbc:postgresql://testrw-host:testrw-port/testrw-database") + .containsEntry("spring.datasource.replicated[0].rw.username", "testrw-username") + .containsEntry("spring.datasource.replicated[0].ro.driver-class-name", "org.postgresql.Driver") + .containsEntry("spring.datasource.replicated[0].ro.password", "testro-password") + .containsEntry("spring.datasource.replicated[0].ro.url", "jdbc:postgresql://testro-host:testro-port/testro-database") + .containsEntry("spring.datasource.replicated[0].ro.username", "testro-username") + .containsEntry("spring.datasource.replicated[0].name", "test-correlation"); + + } + + private final FluentMap rwsecret2 = new FluentMap() + .withEntry(Binding.TYPE, TYPE) + .withEntry("database", "test2rw-database") + .withEntry("host", "test2rw-host") + .withEntry("password", "test2rw-password") + .withEntry("port", "test2rw-port") + .withEntry("username", "test2rw-username") + .withEntry("correlation", "test2-correlation") + .withEntry("function", "rw"); + + private final FluentMap rosecret2 = new FluentMap() + .withEntry(Binding.TYPE, TYPE) + .withEntry("database", "test2ro-database") + .withEntry("host", "test2ro-host") + .withEntry("password", "test2ro-password") + .withEntry("port", "test2ro-port") + .withEntry("username", "test2ro-username") + .withEntry("correlation", "test2-correlation") + .withEntry("function", "ro"); + + + @Test + @DisplayName("composes multiple replications") + void testMultipleReplications() { + Bindings bindings = new Bindings( + new Binding("dbrw2", Paths.get("dbrw-path"), rwsecret2), + new Binding("dbro2", Paths.get("dbro-path"), rosecret2), + new Binding("dbrw", Paths.get("dbrw-path"), rwsecret), + new Binding("dbro", Paths.get("dbro-path"), rosecret) + ); + new PostgreSqlReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); + assertThat(properties) + .containsEntry("spring.datasource.replicated[0].rw.driver-class-name", "org.postgresql.Driver") + .containsEntry("spring.datasource.replicated[0].rw.password", "test2rw-password") + .containsEntry("spring.datasource.replicated[0].rw.url", "jdbc:postgresql://test2rw-host:test2rw-port/test2rw-database") + .containsEntry("spring.datasource.replicated[0].rw.username", "test2rw-username") + .containsEntry("spring.datasource.replicated[0].ro.driver-class-name", "org.postgresql.Driver") + .containsEntry("spring.datasource.replicated[0].ro.password", "test2ro-password") + .containsEntry("spring.datasource.replicated[0].ro.url", "jdbc:postgresql://test2ro-host:test2ro-port/test2ro-database") + .containsEntry("spring.datasource.replicated[0].ro.username", "test2ro-username") + .containsEntry("spring.datasource.replicated[0].name", "test2-correlation") + + .containsEntry("spring.datasource.replicated[1].rw.driver-class-name", "org.postgresql.Driver") + .containsEntry("spring.datasource.replicated[1].rw.password", "testrw-password") + .containsEntry("spring.datasource.replicated[1].rw.url", "jdbc:postgresql://testrw-host:testrw-port/testrw-database") + .containsEntry("spring.datasource.replicated[1].rw.username", "testrw-username") + .containsEntry("spring.datasource.replicated[1].ro.driver-class-name", "org.postgresql.Driver") + .containsEntry("spring.datasource.replicated[1].ro.password", "testro-password") + .containsEntry("spring.datasource.replicated[1].ro.url", "jdbc:postgresql://testro-host:testro-port/testro-database") + .containsEntry("spring.datasource.replicated[1].ro.username", "testro-username") + .containsEntry("spring.datasource.replicated[1].name", "test-correlation"); + + + + } + + @Test + @DisplayName("gives precedence to jdbc-url") + void testJdbcURL() { + Bindings bindings = new Bindings( + new Binding("dbrw", Paths.get("dbrw-path"), rwsecret.withEntry("jdbc-url", "testrw-jdbc-url")), + new Binding("dbro", Paths.get("dbro-path"), rosecret.withEntry("jdbc-url", "testro-jdbc-url")) + ); + new PostgreSqlReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); + assertThat(properties) + .containsEntry("spring.datasource.replicated[0].rw.driver-class-name", "org.postgresql.Driver") + .containsEntry("spring.datasource.replicated[0].rw.password", "testrw-password") + .containsEntry("spring.datasource.replicated[0].rw.url", "testrw-jdbc-url") + .containsEntry("spring.datasource.replicated[0].rw.username", "testrw-username") + .containsEntry("spring.datasource.replicated[0].ro.driver-class-name", "org.postgresql.Driver") + .containsEntry("spring.datasource.replicated[0].ro.password", "testro-password") + .containsEntry("spring.datasource.replicated[0].ro.url", "testro-jdbc-url") + .containsEntry("spring.datasource.replicated[0].ro.username", "testro-username") + .containsEntry("spring.datasource.replicated[0].name", "test-correlation"); + } + + @Test + @DisplayName("composes r2dbc url from host port and database") + void testR2dbc() { + Bindings bindings = new Bindings( + new Binding("dbrw", Paths.get("dbrw-path"), rwsecret), + new Binding("dbro", Paths.get("dbro-path"), rosecret) + ); + new PostgreSqlReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); + assertThat(properties) + .containsEntry("spring.r2dbc.replicated[0].rw.password", "testrw-password") + .containsEntry("spring.r2dbc.replicated[0].rw.url", "r2dbc:postgresql://testrw-host:testrw-port/testrw-database") + .containsEntry("spring.r2dbc.replicated[0].rw.username", "testrw-username") + .containsEntry("spring.r2dbc.replicated[0].ro.password", "testro-password") + .containsEntry("spring.r2dbc.replicated[0].ro.url", "r2dbc:postgresql://testro-host:testro-port/testro-database") + .containsEntry("spring.r2dbc.replicated[0].ro.username", "testro-username") + .containsEntry("spring.r2dbc.replicated[0].name", "test-correlation"); + } + + @Test + @DisplayName("gives precedence to r2dbc-url") + void testR2dbcURL() { + Bindings bindings = new Bindings( + new Binding("dbrw", Paths.get("dbrw-path"), rwsecret.withEntry("r2dbc-url", "testrw-r2dbc-url")), + new Binding("dbro", Paths.get("dbro-path"), rosecret.withEntry("r2dbc-url", "testro-r2dbc-url")) + ); + new PostgreSqlReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); + assertThat(properties) + .containsEntry("spring.r2dbc.replicated[0].rw.password", "testrw-password") + .containsEntry("spring.r2dbc.replicated[0].rw.url", "testrw-r2dbc-url") + .containsEntry("spring.r2dbc.replicated[0].rw.username", "testrw-username") + .containsEntry("spring.r2dbc.replicated[0].ro.password", "testro-password") + .containsEntry("spring.r2dbc.replicated[0].ro.url", "testro-r2dbc-url") + .containsEntry("spring.r2dbc.replicated[0].ro.username", "testro-username") + .containsEntry("spring.r2dbc.replicated[0].name", "test-correlation"); + } + + + @Test + @DisplayName("can be disabled") + void disabled() { + Bindings bindings = new Bindings( + new Binding("dbrw", Paths.get("dbrw-path"), rwsecret), + new Binding("dbro", Paths.get("dbro-path"), rosecret) + ); + environment.setProperty("org.springframework.cloud.bindings.boot.postgresql-replicated.enable", "false"); + + new PostgreSqlReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); + + assertThat(properties).isEmpty(); + } + + @Test + @DisplayName("composes jdbc url from host port and database with sslmode and crdb option") + void testJdbcWithSsl() { + Bindings bindings = new Bindings( + new Binding("dbrw", Paths.get("dbrw-path"), rwsecret.withEntry("sslmode", "verify-full") + .withEntry("sslrootcert", "root.cert") + .withEntry("options", "--cluster=routing-id&opt=val1")), + new Binding("dbro", Paths.get("dbro-path"), rosecret) + ); + new PostgreSqlReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); + assertThat(properties) + .containsEntry("spring.datasource.replicated[0].rw.driver-class-name", "org.postgresql.Driver") + .containsEntry("spring.datasource.replicated[0].rw.password", "testrw-password") + .containsEntry("spring.datasource.replicated[0].rw.url", "jdbc:postgresql://testrw-host:testrw-port/testrw-database?sslmode=verify-full&sslrootcert=dbrw-path/root.cert&options=--cluster=routing-id -c opt=val1") + .containsEntry("spring.datasource.replicated[0].rw.username", "testrw-username") + .containsEntry("spring.r2dbc.replicated[0].name", "test-correlation"); + } + + @Test + @DisplayName("composes jdbc url from host port and database with sslmode and crdb option") + void testJdbcWithInvalidCrdbOption() { + Bindings bindings = new Bindings( + new Binding("dbrw", Paths.get("dbrw-path"), rwsecret.withEntry("sslmode", "verify-full") + .withEntry("sslrootcert", "root.cert") + .withEntry("options", "-cluster=routing-id&opt=val1")), + new Binding("dbro", Paths.get("dbro-path"), rosecret) + ); + new PostgreSqlReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); + assertThat(properties) + .containsEntry("spring.datasource.replicated[0].rw.driver-class-name", "org.postgresql.Driver") + .containsEntry("spring.datasource.replicated[0].rw.password", "testrw-password") + .containsEntry("spring.datasource.replicated[0].rw.url", "jdbc:postgresql://testrw-host:testrw-port/testrw-database?sslmode=verify-full&sslrootcert=dbrw-path/root.cert&options=-c -cluster=routing-id -c opt=val1") + .containsEntry("spring.datasource.replicated[0].rw.username", "testrw-username") + .containsEntry("spring.r2dbc.replicated[0].name", "test-correlation"); + } + + @Test + @DisplayName("composes r2dbc url from host port and database with sslmode and crdb option") + void testR2dbcWithSsl() { + Bindings bindings = new Bindings( + new Binding("dbrw", Paths.get("dbrw-path"), rwsecret.withEntry("sslmode", "verify-full") + .withEntry("sslrootcert", "root.cert") + .withEntry("options", "--cluster=routing-id&opt=val1")), + new Binding("dbro", Paths.get("dbro-path"), rosecret) + ); + new PostgreSqlReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); + assertThat(properties) + .containsEntry("spring.r2dbc.replicated[0].rw.password", "testrw-password") + .containsEntry("spring.r2dbc.replicated[0].rw.url", "r2dbc:postgresql://testrw-host:testrw-port/testrw-database?sslmode=verify-full&sslrootcert=dbrw-path/root.cert&options=--cluster=routing-id -c opt=val1") + .containsEntry("spring.r2dbc.replicated[0].rw.username", "testrw-username") + .containsEntry("spring.r2dbc.replicated[0].name", "test-correlation"); + } + + @Test + @DisplayName("composes jdbc url from host port and database with sslmode disable") + void testJdbcWithSslDisable() { + Bindings bindings = new Bindings( + new Binding("dbrw", Paths.get("dbrw-path"), rwsecret.withEntry("sslmode", "disable")), + new Binding("dbro", Paths.get("dbro-path"), rosecret) + ); + new PostgreSqlReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); + assertThat(properties) + .containsEntry("spring.datasource.replicated[0].rw.driver-class-name", "org.postgresql.Driver") + .containsEntry("spring.datasource.replicated[0].rw.password", "testrw-password") + .containsEntry("spring.datasource.replicated[0].rw.url", "jdbc:postgresql://testrw-host:testrw-port/testrw-database?sslmode=disable") + .containsEntry("spring.datasource.replicated[0].rw.username", "testrw-username") + .containsEntry("spring.r2dbc.replicated[0].name", "test-correlation"); + } + + @Test + @DisplayName("composes jdbc url from host port and database with DB options") + void testJdbcWithDBoptions() { + Bindings bindings = new Bindings( + new Binding("dbrw", Paths.get("dbrw-path"), rwsecret.withEntry("options", "opt1=val1&opt2=val2")), + new Binding("dbro", Paths.get("dbro-path"), rosecret) + ); + new PostgreSqlReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); + assertThat(properties) + .containsEntry("spring.datasource.replicated[0].rw.driver-class-name", "org.postgresql.Driver") + .containsEntry("spring.datasource.replicated[0].rw.password", "testrw-password") + .containsEntry("spring.datasource.replicated[0].rw.url", "jdbc:postgresql://testrw-host:testrw-port/testrw-database?options=-c opt1=val1 -c opt2=val2") + .containsEntry("spring.datasource.replicated[0].rw.username", "testrw-username") + .containsEntry("spring.r2dbc.replicated[0].name", "test-correlation"); + } + + @Test + @DisplayName("composes jdbc url from host port and database with invalid DB options") + void testJdbcWithInvaildDBoptions() { + Bindings bindings = new Bindings( + new Binding("dbrw", Paths.get("dbrw-path"), rwsecret.withEntry("options", "opt1=val1&opt")), + new Binding("dbro", Paths.get("dbro-path"), rosecret) + ); + new PostgreSqlReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); + assertThat(properties) + .containsEntry("spring.datasource.replicated[0].rw.driver-class-name", "org.postgresql.Driver") + .containsEntry("spring.datasource.replicated[0].rw.password", "testrw-password") + .containsEntry("spring.datasource.replicated[0].rw.url", "jdbc:postgresql://testrw-host:testrw-port/testrw-database?options=-c opt1=val1") + .containsEntry("spring.datasource.replicated[0].rw.username", "testrw-username") + .containsEntry("spring.r2dbc.replicated[0].name", "test-correlation"); + } + + @Test + @DisplayName("composes jdbc url from host port and database with empty DB options") + void testJdbcWithEmptyDBoptions() { + Bindings bindings = new Bindings( + new Binding("dbrw", Paths.get("dbrw-path"), rwsecret.withEntry("options", "")), + new Binding("dbro", Paths.get("dbro-path"), rosecret) + ); + new PostgreSqlReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); + assertThat(properties) + .containsEntry("spring.datasource.replicated[0].rw.driver-class-name", "org.postgresql.Driver") + .containsEntry("spring.datasource.replicated[0].rw.password", "testrw-password") + .containsEntry("spring.datasource.replicated[0].rw.url", "jdbc:postgresql://testrw-host:testrw-port/testrw-database") + .containsEntry("spring.datasource.replicated[0].rw.username", "testrw-username") + .containsEntry("spring.r2dbc.replicated[0].name", "test-correlation"); + } + + @Test + @DisplayName("composes invalid replication due to no correlation") + void testJdbcNoCorrection() { + Bindings bindings = new Bindings( + new Binding("dbrw", Paths.get("dbrw-path"), rwsecret.withEntry("correlation", "")), + new Binding("dbro", Paths.get("dbro-path"), rosecret.withEntry("correlation", "")) + ); + new PostgreSqlReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); + assertThat(properties).isEmpty(); + } + + @Test + @DisplayName("composes invalid replication due to invalide fucntion") + void testJdbcInvalidFunction() { + Bindings bindings = new Bindings( + new Binding("dbrw", Paths.get("dbrw-path"), rwsecret.withEntry("function", "invalid")), + new Binding("dbro", Paths.get("dbro-path"), rosecret.withEntry("function", "invalid")) + ); + new PostgreSqlReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); + assertThat(properties).isEmpty(); + } +} From 43256ef792b9ab86404332878d1919afe54fce2b Mon Sep 17 00:00:00 2001 From: meyerg Date: Tue, 27 Feb 2024 14:40:14 -0600 Subject: [PATCH 2/6] Redesigning secrets for postgres-replicated. Going with a single secret design as well as only supporting a single logical "replicated" connection. --- ...PostgreSQLBindingsPropertiesProcessor.java | 21 +- ...ReplicatedBindingsPropertiesProcessor.java | 170 +++++------ ...icatedBindingsPropertiesProcessorTest.java | 287 ++++++------------ 3 files changed, 195 insertions(+), 283 deletions(-) diff --git a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/AbstractPostgreSQLBindingsPropertiesProcessor.java b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/AbstractPostgreSQLBindingsPropertiesProcessor.java index 73f20ec..4b19a72 100644 --- a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/AbstractPostgreSQLBindingsPropertiesProcessor.java +++ b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/AbstractPostgreSQLBindingsPropertiesProcessor.java @@ -31,7 +31,7 @@ public abstract class AbstractPostgreSQLBindingsPropertiesProcessor implements B * Refer to Client Connection Parameters. */ protected String buildDbOptions(Binding binding) { - String options = binding.getSecret().getOrDefault(OPTIONS, ""); + String options = binding.getSecret().getOrDefault(getDBOptionSecretField(), ""); String crdbOption = ""; List dbOptions = new ArrayList<>(); if (!options.equals("")) { @@ -70,8 +70,8 @@ protected String buildDbOptions(Binding binding) { protected String buildSslModeParam(Binding binding) { //process ssl params //https://www.postgresql.org/docs/14/libpq-connect.html - String sslmode = binding.getSecret().getOrDefault(SSL_MODE, ""); - String sslRootCert = binding.getSecret().getOrDefault(SSL_ROOT_CERT, ""); + String sslmode = binding.getSecret().getOrDefault(getSSLModeSecretField(), ""); + String sslRootCert = binding.getSecret().getOrDefault(getSSLRootCertSecretField(), ""); StringBuilder sslparam = new StringBuilder(); if (!"".equals(sslmode)) { sslparam.append(SSL_MODE).append("=").append(sslmode); @@ -86,4 +86,19 @@ protected String buildSslModeParam(Binding binding) { } return sslparam.toString(); } + + protected String getDBOptionSecretField() + { + return OPTIONS; + } + + protected String getSSLModeSecretField() + { + return SSL_MODE; + } + + protected String getSSLRootCertSecretField() + { + return SSL_ROOT_CERT; + } } diff --git a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/PostgreSqlReplicatedBindingsPropertiesProcessor.java b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/PostgreSqlReplicatedBindingsPropertiesProcessor.java index 336faef..c7d2d19 100644 --- a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/PostgreSqlReplicatedBindingsPropertiesProcessor.java +++ b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/PostgreSqlReplicatedBindingsPropertiesProcessor.java @@ -22,11 +22,8 @@ import org.springframework.cloud.bindings.Bindings; import org.springframework.context.ApplicationListener; import org.springframework.core.env.Environment; -import org.springframework.util.StringUtils; import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.Collectors; import static org.springframework.cloud.bindings.boot.Guards.isTypeEnabled; @@ -41,22 +38,20 @@ public class PostgreSqlReplicatedBindingsPropertiesProcessor extends AbstractPos public static final String TYPE = "postgresql-replicated"; private static final DeferredLog LOG = new DeferredLog(); - - private static final String CORRELATION_FIELD = "correlation"; - - private static final String FUNCTION_FILED = "function"; private static final String RW_FUNCTION = "rw"; private static final String RO_FUNCTION = "ro"; - private static final String JDBC_BASE_TEMPLATE = "spring.datasource.replicated[%d].%s"; + private static final String JDBC_BASE_TEMPLATE = "spring.datasource.replicated.%s"; - private static final String R2DBC_BASE_TEMPLATE = "spring.r2dbc.replicated[%d].%s"; + private static final String R2DBC_BASE_TEMPLATE = "spring.r2dbc.replicated.%s"; - private static final String JDBC_PROPERTY_TEMPLATE = "spring.datasource.replicated[%d].%s.%s"; + private static final String JDBC_PROPERTY_TEMPLATE = "spring.datasource.replicated.%s.%s"; - private static final String R2DBC_PROPERTY_TEMPLATE = "spring.r2dbc.replicated[%d].%s.%s"; + private static final String R2DBC_PROPERTY_TEMPLATE = "spring.r2dbc.replicated.%s.%s"; + + private String mode = RW_FUNCTION; @Override public void process(Environment environment, Bindings bindings, Map properties) { @@ -65,94 +60,91 @@ public void process(Environment environment, Bindings bindings, Map { - if (!StringUtils.hasText(b.getSecret().get(CORRELATION_FIELD))) { - - LOG.warn(String.format("Replicate postgres binding %s is missing required correlation filed and will be ignored", b.getName())); - return false; - } - - final var fnc = b.getSecret().getOrDefault(FUNCTION_FILED, "").toLowerCase(); - if (fnc.compareTo(RW_FUNCTION) != 0 && fnc.compareTo(RO_FUNCTION) != 0) { - - LOG.warn(String.format("Replicate postgres binding %s does not have a valid function and will be ignored", b.getName())); - return false; - } - return true; - }) - .collect(Collectors.groupingBy(b -> b.getSecret().get(CORRELATION_FIELD))); - - final var cnt = new AtomicInteger(0); - - bindingsCorrelations.forEach((correlationName, binds) -> { - - final var idx = cnt.getAndIncrement(); - - binds.stream().forEach(binding -> { - final var fnc = binding.getSecret().get(FUNCTION_FILED).toLowerCase(); - - final var map = new MapMapper(binding.getSecret(), properties); - - map.from("correlation").to(String.format(JDBC_BASE_TEMPLATE, idx, "name")); - - map.from("password").to(formatJDBCTemplate(idx, fnc, "password")); - - map.from("host", "port", "database").to(formatJDBCTemplate(idx, fnc, "url"), - (host, port, database) -> String.format("jdbc:postgresql://%s:%s/%s", host, port, database)); - - String sslParam = buildSslModeParam(binding); - String sslModeOptions = buildDbOptions(binding); - if (!"".equals(sslParam) && !"".equals(sslModeOptions)) { - sslModeOptions = sslParam + "&" + sslModeOptions; - } else if (!"".equals(sslParam) ) { - sslModeOptions = sslParam; - } - - if (!"".equals(sslModeOptions)) { - properties.put(formatJDBCTemplate(idx, fnc, "url") , - properties.get(formatJDBCTemplate(idx, fnc, "url")) + "?" + sslModeOptions); - } - map.from("username").to(formatJDBCTemplate(idx, fnc, "username")); - - // jdbcURL takes precedence - map.from("jdbc-url").to(formatJDBCTemplate(idx, fnc, "url")); - - properties.put(formatJDBCTemplate(idx, fnc, "driver-class-name"), "org.postgresql.Driver"); - - //r2dbc properties - map.from("correlation").to(String.format(R2DBC_BASE_TEMPLATE, idx, "name")); - map.from("password").to(formatR2DBCTemplate(idx, fnc, "password")); - map.from("host", "port", "database").to(formatR2DBCTemplate(idx, fnc, "url"), - (host, port, database) -> String.format("r2dbc:postgresql://%s:%s/%s", host, port, database)); - if (!"".equals(sslModeOptions)) { - properties.put(formatR2DBCTemplate(idx, fnc, "url") , - properties.get(formatR2DBCTemplate(idx, fnc, "url")) + "?" + sslModeOptions); - } - map.from("username").to(formatR2DBCTemplate(idx, fnc, "username")); - - // r2dbcURL takes precedence - map.from("r2dbc-url").to(formatR2DBCTemplate(idx, fnc, "url")); + bindings.filterBindings(TYPE).forEach(binding -> { + + properties.put(String.format(JDBC_BASE_TEMPLATE, "name"), binding.getName()); + + properties.put(String.format(R2DBC_BASE_TEMPLATE, "name"), binding.getName()); + + final var map = new MapMapper(binding.getSecret(), properties); + + mode = RW_FUNCTION; + buildProperties(map, properties, binding, RW_FUNCTION); + + mode = RO_FUNCTION; + buildProperties(map, properties, binding, RO_FUNCTION); - }); }); } - private String formatJDBCTemplate(int idx, String fnc, String field) + private void buildProperties(MapMapper map, Map properties, Binding binding, String fnc) { - return String.format(JDBC_PROPERTY_TEMPLATE, idx, fnc, field); + map.from(fnc + "-password").to(formatJDBCTemplate(fnc, "password")); + + map.from(fnc + "-host", fnc + "-port", fnc + "-database").to(formatJDBCTemplate(fnc, "url"), + (host, port, database) -> String.format("jdbc:postgresql://%s:%s/%s", host, port, database)); + + String sslParam = buildSslModeParam(binding); + String sslModeOptions = buildDbOptions(binding); + if (!"".equals(sslParam) && !"".equals(sslModeOptions)) { + sslModeOptions = sslParam + "&" + sslModeOptions; + } else if (!"".equals(sslParam) ) { + sslModeOptions = sslParam; + } + + if (!"".equals(sslModeOptions)) { + properties.put(formatJDBCTemplate(fnc, "url") , + properties.get(formatJDBCTemplate(fnc, "url")) + "?" + sslModeOptions); + } + map.from(fnc + "-username").to(formatJDBCTemplate(fnc, "username")); + + // jdbcURL takes precedence + map.from(fnc + "-jdbc-url").to(formatJDBCTemplate(fnc, "url")); + + properties.put(formatJDBCTemplate(fnc, "driver-class-name"), "org.postgresql.Driver"); + + //r2dbc properties + map.from(fnc + "-password").to(formatR2DBCTemplate(fnc, "password")); + map.from(fnc + "-host", fnc + "-port", fnc + "-database").to(formatR2DBCTemplate(fnc, "url"), + (host, port, database) -> String.format("r2dbc:postgresql://%s:%s/%s", host, port, database)); + if (!"".equals(sslModeOptions)) { + properties.put(formatR2DBCTemplate(fnc, "url") , + properties.get(formatR2DBCTemplate(fnc, "url")) + "?" + sslModeOptions); + } + map.from(fnc + "-username").to(formatR2DBCTemplate(fnc, "username")); + + // r2dbcURL takes precedence + map.from(fnc + "-r2dbc-url").to(formatR2DBCTemplate(fnc, "url")); + } + + private String formatJDBCTemplate(String fnc, String field) + { + return String.format(JDBC_PROPERTY_TEMPLATE, fnc, field); } - private String formatR2DBCTemplate(int idx, String fnc, String field) + private String formatR2DBCTemplate(String fnc, String field) { - return String.format(R2DBC_PROPERTY_TEMPLATE, idx, fnc, field); + return String.format(R2DBC_PROPERTY_TEMPLATE, fnc, field); } + @Override + protected String getDBOptionSecretField() + { + return String.format("%s-%s", mode , OPTIONS); + } + + @Override + protected String getSSLModeSecretField() + { + return String.format("%s-%s", mode , SSL_MODE); + } + + @Override + protected String getSSLRootCertSecretField() + { + return String.format("%s-%s", mode , SSL_ROOT_CERT); + } + @Override public void onApplicationEvent(ApplicationPreparedEvent event) { LOG.replayTo(getClass()); diff --git a/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/PostgreSqlReplicatedBindingsPropertiesProcessorTest.java b/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/PostgreSqlReplicatedBindingsPropertiesProcessorTest.java index 462661f..afce0ba 100644 --- a/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/PostgreSqlReplicatedBindingsPropertiesProcessorTest.java +++ b/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/PostgreSqlReplicatedBindingsPropertiesProcessorTest.java @@ -16,25 +16,18 @@ @DisplayName("PostgreSQLReplicated BindingsPropertiesProcessor") public class PostgreSqlReplicatedBindingsPropertiesProcessorTest { - private final FluentMap rwsecret = new FluentMap() + private final FluentMap secret = new FluentMap() .withEntry(Binding.TYPE, TYPE) - .withEntry("database", "testrw-database") - .withEntry("host", "testrw-host") - .withEntry("password", "testrw-password") - .withEntry("port", "testrw-port") - .withEntry("username", "testrw-username") - .withEntry("correlation", "test-correlation") - .withEntry("function", "rw"); - - private final FluentMap rosecret = new FluentMap() - .withEntry(Binding.TYPE, TYPE) - .withEntry("database", "testro-database") - .withEntry("host", "testro-host") - .withEntry("password", "testro-password") - .withEntry("port", "testro-port") - .withEntry("username", "testro-username") - .withEntry("correlation", "test-correlation") - .withEntry("function", "ro"); + .withEntry("rw-database", "testrw-database") + .withEntry("rw-host", "testrw-host") + .withEntry("rw-password", "testrw-password") + .withEntry("rw-port", "testrw-port") + .withEntry("rw-username", "testrw-username") + .withEntry("ro-database", "testro-database") + .withEntry("ro-host", "testro-host") + .withEntry("ro-password", "testro-password") + .withEntry("ro-port", "testro-port") + .withEntry("ro-username", "testro-username"); private final MockEnvironment environment = new MockEnvironment(); @@ -44,133 +37,74 @@ public class PostgreSqlReplicatedBindingsPropertiesProcessorTest { @DisplayName("composes jdbc url from host port and database") void testJdbc() { Bindings bindings = new Bindings( - new Binding("dbrw", Paths.get("dbrw-path"), rwsecret), - new Binding("dbro", Paths.get("dbro-path"), rosecret) + new Binding("dbrw", Paths.get("dbrw-path"), secret) ); new PostgreSqlReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); assertThat(properties) - .containsEntry("spring.datasource.replicated[0].rw.driver-class-name", "org.postgresql.Driver") - .containsEntry("spring.datasource.replicated[0].rw.password", "testrw-password") - .containsEntry("spring.datasource.replicated[0].rw.url", "jdbc:postgresql://testrw-host:testrw-port/testrw-database") - .containsEntry("spring.datasource.replicated[0].rw.username", "testrw-username") - .containsEntry("spring.datasource.replicated[0].ro.driver-class-name", "org.postgresql.Driver") - .containsEntry("spring.datasource.replicated[0].ro.password", "testro-password") - .containsEntry("spring.datasource.replicated[0].ro.url", "jdbc:postgresql://testro-host:testro-port/testro-database") - .containsEntry("spring.datasource.replicated[0].ro.username", "testro-username") - .containsEntry("spring.datasource.replicated[0].name", "test-correlation"); + .containsEntry("spring.datasource.replicated.rw.driver-class-name", "org.postgresql.Driver") + .containsEntry("spring.datasource.replicated.rw.password", "testrw-password") + .containsEntry("spring.datasource.replicated.rw.url", "jdbc:postgresql://testrw-host:testrw-port/testrw-database") + .containsEntry("spring.datasource.replicated.rw.username", "testrw-username") + .containsEntry("spring.datasource.replicated.ro.driver-class-name", "org.postgresql.Driver") + .containsEntry("spring.datasource.replicated.ro.password", "testro-password") + .containsEntry("spring.datasource.replicated.ro.url", "jdbc:postgresql://testro-host:testro-port/testro-database") + .containsEntry("spring.datasource.replicated.ro.username", "testro-username") + .containsEntry("spring.datasource.replicated.name", "dbrw"); } - private final FluentMap rwsecret2 = new FluentMap() - .withEntry(Binding.TYPE, TYPE) - .withEntry("database", "test2rw-database") - .withEntry("host", "test2rw-host") - .withEntry("password", "test2rw-password") - .withEntry("port", "test2rw-port") - .withEntry("username", "test2rw-username") - .withEntry("correlation", "test2-correlation") - .withEntry("function", "rw"); - private final FluentMap rosecret2 = new FluentMap() - .withEntry(Binding.TYPE, TYPE) - .withEntry("database", "test2ro-database") - .withEntry("host", "test2ro-host") - .withEntry("password", "test2ro-password") - .withEntry("port", "test2ro-port") - .withEntry("username", "test2ro-username") - .withEntry("correlation", "test2-correlation") - .withEntry("function", "ro"); - - - @Test - @DisplayName("composes multiple replications") - void testMultipleReplications() { - Bindings bindings = new Bindings( - new Binding("dbrw2", Paths.get("dbrw-path"), rwsecret2), - new Binding("dbro2", Paths.get("dbro-path"), rosecret2), - new Binding("dbrw", Paths.get("dbrw-path"), rwsecret), - new Binding("dbro", Paths.get("dbro-path"), rosecret) - ); - new PostgreSqlReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); - assertThat(properties) - .containsEntry("spring.datasource.replicated[0].rw.driver-class-name", "org.postgresql.Driver") - .containsEntry("spring.datasource.replicated[0].rw.password", "test2rw-password") - .containsEntry("spring.datasource.replicated[0].rw.url", "jdbc:postgresql://test2rw-host:test2rw-port/test2rw-database") - .containsEntry("spring.datasource.replicated[0].rw.username", "test2rw-username") - .containsEntry("spring.datasource.replicated[0].ro.driver-class-name", "org.postgresql.Driver") - .containsEntry("spring.datasource.replicated[0].ro.password", "test2ro-password") - .containsEntry("spring.datasource.replicated[0].ro.url", "jdbc:postgresql://test2ro-host:test2ro-port/test2ro-database") - .containsEntry("spring.datasource.replicated[0].ro.username", "test2ro-username") - .containsEntry("spring.datasource.replicated[0].name", "test2-correlation") - - .containsEntry("spring.datasource.replicated[1].rw.driver-class-name", "org.postgresql.Driver") - .containsEntry("spring.datasource.replicated[1].rw.password", "testrw-password") - .containsEntry("spring.datasource.replicated[1].rw.url", "jdbc:postgresql://testrw-host:testrw-port/testrw-database") - .containsEntry("spring.datasource.replicated[1].rw.username", "testrw-username") - .containsEntry("spring.datasource.replicated[1].ro.driver-class-name", "org.postgresql.Driver") - .containsEntry("spring.datasource.replicated[1].ro.password", "testro-password") - .containsEntry("spring.datasource.replicated[1].ro.url", "jdbc:postgresql://testro-host:testro-port/testro-database") - .containsEntry("spring.datasource.replicated[1].ro.username", "testro-username") - .containsEntry("spring.datasource.replicated[1].name", "test-correlation"); - - - - } - @Test @DisplayName("gives precedence to jdbc-url") void testJdbcURL() { Bindings bindings = new Bindings( - new Binding("dbrw", Paths.get("dbrw-path"), rwsecret.withEntry("jdbc-url", "testrw-jdbc-url")), - new Binding("dbro", Paths.get("dbro-path"), rosecret.withEntry("jdbc-url", "testro-jdbc-url")) + new Binding("dbrw", Paths.get("dbrw-path"), secret.withEntry("rw-jdbc-url", "testrw-jdbc-url").withEntry("ro-jdbc-url", "testro-jdbc-url")) ); new PostgreSqlReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); assertThat(properties) - .containsEntry("spring.datasource.replicated[0].rw.driver-class-name", "org.postgresql.Driver") - .containsEntry("spring.datasource.replicated[0].rw.password", "testrw-password") - .containsEntry("spring.datasource.replicated[0].rw.url", "testrw-jdbc-url") - .containsEntry("spring.datasource.replicated[0].rw.username", "testrw-username") - .containsEntry("spring.datasource.replicated[0].ro.driver-class-name", "org.postgresql.Driver") - .containsEntry("spring.datasource.replicated[0].ro.password", "testro-password") - .containsEntry("spring.datasource.replicated[0].ro.url", "testro-jdbc-url") - .containsEntry("spring.datasource.replicated[0].ro.username", "testro-username") - .containsEntry("spring.datasource.replicated[0].name", "test-correlation"); + .containsEntry("spring.datasource.replicated.rw.driver-class-name", "org.postgresql.Driver") + .containsEntry("spring.datasource.replicated.rw.password", "testrw-password") + .containsEntry("spring.datasource.replicated.rw.url", "testrw-jdbc-url") + .containsEntry("spring.datasource.replicated.rw.username", "testrw-username") + .containsEntry("spring.datasource.replicated.ro.driver-class-name", "org.postgresql.Driver") + .containsEntry("spring.datasource.replicated.ro.password", "testro-password") + .containsEntry("spring.datasource.replicated.ro.url", "testro-jdbc-url") + .containsEntry("spring.datasource.replicated.ro.username", "testro-username") + .containsEntry("spring.datasource.replicated.name", "dbrw"); } @Test @DisplayName("composes r2dbc url from host port and database") void testR2dbc() { Bindings bindings = new Bindings( - new Binding("dbrw", Paths.get("dbrw-path"), rwsecret), - new Binding("dbro", Paths.get("dbro-path"), rosecret) + new Binding("dbrw", Paths.get("dbrw-path"), secret) ); new PostgreSqlReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); assertThat(properties) - .containsEntry("spring.r2dbc.replicated[0].rw.password", "testrw-password") - .containsEntry("spring.r2dbc.replicated[0].rw.url", "r2dbc:postgresql://testrw-host:testrw-port/testrw-database") - .containsEntry("spring.r2dbc.replicated[0].rw.username", "testrw-username") - .containsEntry("spring.r2dbc.replicated[0].ro.password", "testro-password") - .containsEntry("spring.r2dbc.replicated[0].ro.url", "r2dbc:postgresql://testro-host:testro-port/testro-database") - .containsEntry("spring.r2dbc.replicated[0].ro.username", "testro-username") - .containsEntry("spring.r2dbc.replicated[0].name", "test-correlation"); + .containsEntry("spring.r2dbc.replicated.rw.password", "testrw-password") + .containsEntry("spring.r2dbc.replicated.rw.url", "r2dbc:postgresql://testrw-host:testrw-port/testrw-database") + .containsEntry("spring.r2dbc.replicated.rw.username", "testrw-username") + .containsEntry("spring.r2dbc.replicated.ro.password", "testro-password") + .containsEntry("spring.r2dbc.replicated.ro.url", "r2dbc:postgresql://testro-host:testro-port/testro-database") + .containsEntry("spring.r2dbc.replicated.ro.username", "testro-username") + .containsEntry("spring.r2dbc.replicated.name", "dbrw"); } @Test @DisplayName("gives precedence to r2dbc-url") void testR2dbcURL() { Bindings bindings = new Bindings( - new Binding("dbrw", Paths.get("dbrw-path"), rwsecret.withEntry("r2dbc-url", "testrw-r2dbc-url")), - new Binding("dbro", Paths.get("dbro-path"), rosecret.withEntry("r2dbc-url", "testro-r2dbc-url")) + new Binding("dbrw", Paths.get("dbrw-path"), secret.withEntry("rw-r2dbc-url", "testrw-r2dbc-url").withEntry("ro-r2dbc-url", "testro-r2dbc-url")) ); new PostgreSqlReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); assertThat(properties) - .containsEntry("spring.r2dbc.replicated[0].rw.password", "testrw-password") - .containsEntry("spring.r2dbc.replicated[0].rw.url", "testrw-r2dbc-url") - .containsEntry("spring.r2dbc.replicated[0].rw.username", "testrw-username") - .containsEntry("spring.r2dbc.replicated[0].ro.password", "testro-password") - .containsEntry("spring.r2dbc.replicated[0].ro.url", "testro-r2dbc-url") - .containsEntry("spring.r2dbc.replicated[0].ro.username", "testro-username") - .containsEntry("spring.r2dbc.replicated[0].name", "test-correlation"); + .containsEntry("spring.r2dbc.replicated.rw.password", "testrw-password") + .containsEntry("spring.r2dbc.replicated.rw.url", "testrw-r2dbc-url") + .containsEntry("spring.r2dbc.replicated.rw.username", "testrw-username") + .containsEntry("spring.r2dbc.replicated.ro.password", "testro-password") + .containsEntry("spring.r2dbc.replicated.ro.url", "testro-r2dbc-url") + .containsEntry("spring.r2dbc.replicated.ro.username", "testro-username") + .containsEntry("spring.r2dbc.replicated.name", "dbrw"); } @@ -178,8 +112,7 @@ void testR2dbcURL() { @DisplayName("can be disabled") void disabled() { Bindings bindings = new Bindings( - new Binding("dbrw", Paths.get("dbrw-path"), rwsecret), - new Binding("dbro", Paths.get("dbro-path"), rosecret) + new Binding("dbrw", Paths.get("dbrw-path"), secret) ); environment.setProperty("org.springframework.cloud.bindings.boot.postgresql-replicated.enable", "false"); @@ -192,138 +125,110 @@ void disabled() { @DisplayName("composes jdbc url from host port and database with sslmode and crdb option") void testJdbcWithSsl() { Bindings bindings = new Bindings( - new Binding("dbrw", Paths.get("dbrw-path"), rwsecret.withEntry("sslmode", "verify-full") - .withEntry("sslrootcert", "root.cert") - .withEntry("options", "--cluster=routing-id&opt=val1")), - new Binding("dbro", Paths.get("dbro-path"), rosecret) + new Binding("dbrw", Paths.get("dbrw-path"), secret.withEntry("rw-sslmode", "verify-full") + .withEntry("rw-sslrootcert", "root.cert") + .withEntry("rw-options", "--cluster=routing-id&opt=val1")) ); new PostgreSqlReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); assertThat(properties) - .containsEntry("spring.datasource.replicated[0].rw.driver-class-name", "org.postgresql.Driver") - .containsEntry("spring.datasource.replicated[0].rw.password", "testrw-password") - .containsEntry("spring.datasource.replicated[0].rw.url", "jdbc:postgresql://testrw-host:testrw-port/testrw-database?sslmode=verify-full&sslrootcert=dbrw-path/root.cert&options=--cluster=routing-id -c opt=val1") - .containsEntry("spring.datasource.replicated[0].rw.username", "testrw-username") - .containsEntry("spring.r2dbc.replicated[0].name", "test-correlation"); + .containsEntry("spring.datasource.replicated.rw.driver-class-name", "org.postgresql.Driver") + .containsEntry("spring.datasource.replicated.rw.password", "testrw-password") + .containsEntry("spring.datasource.replicated.rw.url", "jdbc:postgresql://testrw-host:testrw-port/testrw-database?sslmode=verify-full&sslrootcert=dbrw-path/root.cert&options=--cluster=routing-id -c opt=val1") + .containsEntry("spring.datasource.replicated.rw.username", "testrw-username") + .containsEntry("spring.r2dbc.replicated.name", "dbrw"); } @Test @DisplayName("composes jdbc url from host port and database with sslmode and crdb option") void testJdbcWithInvalidCrdbOption() { Bindings bindings = new Bindings( - new Binding("dbrw", Paths.get("dbrw-path"), rwsecret.withEntry("sslmode", "verify-full") - .withEntry("sslrootcert", "root.cert") - .withEntry("options", "-cluster=routing-id&opt=val1")), - new Binding("dbro", Paths.get("dbro-path"), rosecret) + new Binding("dbrw", Paths.get("dbrw-path"), secret.withEntry("rw-sslmode", "verify-full") + .withEntry("rw-sslrootcert", "root.cert") + .withEntry("rw-options", "-cluster=routing-id&opt=val1")) ); new PostgreSqlReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); assertThat(properties) - .containsEntry("spring.datasource.replicated[0].rw.driver-class-name", "org.postgresql.Driver") - .containsEntry("spring.datasource.replicated[0].rw.password", "testrw-password") - .containsEntry("spring.datasource.replicated[0].rw.url", "jdbc:postgresql://testrw-host:testrw-port/testrw-database?sslmode=verify-full&sslrootcert=dbrw-path/root.cert&options=-c -cluster=routing-id -c opt=val1") - .containsEntry("spring.datasource.replicated[0].rw.username", "testrw-username") - .containsEntry("spring.r2dbc.replicated[0].name", "test-correlation"); + .containsEntry("spring.datasource.replicated.rw.driver-class-name", "org.postgresql.Driver") + .containsEntry("spring.datasource.replicated.rw.password", "testrw-password") + .containsEntry("spring.datasource.replicated.rw.url", "jdbc:postgresql://testrw-host:testrw-port/testrw-database?sslmode=verify-full&sslrootcert=dbrw-path/root.cert&options=-c -cluster=routing-id -c opt=val1") + .containsEntry("spring.datasource.replicated.rw.username", "testrw-username") + .containsEntry("spring.r2dbc.replicated.name", "dbrw"); } @Test @DisplayName("composes r2dbc url from host port and database with sslmode and crdb option") void testR2dbcWithSsl() { Bindings bindings = new Bindings( - new Binding("dbrw", Paths.get("dbrw-path"), rwsecret.withEntry("sslmode", "verify-full") - .withEntry("sslrootcert", "root.cert") - .withEntry("options", "--cluster=routing-id&opt=val1")), - new Binding("dbro", Paths.get("dbro-path"), rosecret) + new Binding("dbrw", Paths.get("dbrw-path"), secret.withEntry("rw-sslmode", "verify-full") + .withEntry("rw-sslrootcert", "root.cert") + .withEntry("rw-options", "--cluster=routing-id&opt=val1")) ); new PostgreSqlReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); assertThat(properties) - .containsEntry("spring.r2dbc.replicated[0].rw.password", "testrw-password") - .containsEntry("spring.r2dbc.replicated[0].rw.url", "r2dbc:postgresql://testrw-host:testrw-port/testrw-database?sslmode=verify-full&sslrootcert=dbrw-path/root.cert&options=--cluster=routing-id -c opt=val1") - .containsEntry("spring.r2dbc.replicated[0].rw.username", "testrw-username") - .containsEntry("spring.r2dbc.replicated[0].name", "test-correlation"); + .containsEntry("spring.r2dbc.replicated.rw.password", "testrw-password") + .containsEntry("spring.r2dbc.replicated.rw.url", "r2dbc:postgresql://testrw-host:testrw-port/testrw-database?sslmode=verify-full&sslrootcert=dbrw-path/root.cert&options=--cluster=routing-id -c opt=val1") + .containsEntry("spring.r2dbc.replicated.rw.username", "testrw-username") + .containsEntry("spring.r2dbc.replicated.name", "dbrw"); } @Test @DisplayName("composes jdbc url from host port and database with sslmode disable") void testJdbcWithSslDisable() { Bindings bindings = new Bindings( - new Binding("dbrw", Paths.get("dbrw-path"), rwsecret.withEntry("sslmode", "disable")), - new Binding("dbro", Paths.get("dbro-path"), rosecret) + new Binding("dbrw", Paths.get("dbrw-path"), secret.withEntry("rw-sslmode", "disable")) ); new PostgreSqlReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); assertThat(properties) - .containsEntry("spring.datasource.replicated[0].rw.driver-class-name", "org.postgresql.Driver") - .containsEntry("spring.datasource.replicated[0].rw.password", "testrw-password") - .containsEntry("spring.datasource.replicated[0].rw.url", "jdbc:postgresql://testrw-host:testrw-port/testrw-database?sslmode=disable") - .containsEntry("spring.datasource.replicated[0].rw.username", "testrw-username") - .containsEntry("spring.r2dbc.replicated[0].name", "test-correlation"); + .containsEntry("spring.datasource.replicated.rw.driver-class-name", "org.postgresql.Driver") + .containsEntry("spring.datasource.replicated.rw.password", "testrw-password") + .containsEntry("spring.datasource.replicated.rw.url", "jdbc:postgresql://testrw-host:testrw-port/testrw-database?sslmode=disable") + .containsEntry("spring.datasource.replicated.rw.username", "testrw-username") + .containsEntry("spring.r2dbc.replicated.name", "dbrw"); } @Test @DisplayName("composes jdbc url from host port and database with DB options") void testJdbcWithDBoptions() { Bindings bindings = new Bindings( - new Binding("dbrw", Paths.get("dbrw-path"), rwsecret.withEntry("options", "opt1=val1&opt2=val2")), - new Binding("dbro", Paths.get("dbro-path"), rosecret) + new Binding("dbrw", Paths.get("dbrw-path"), secret.withEntry("rw-options", "opt1=val1&opt2=val2")) ); new PostgreSqlReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); assertThat(properties) - .containsEntry("spring.datasource.replicated[0].rw.driver-class-name", "org.postgresql.Driver") - .containsEntry("spring.datasource.replicated[0].rw.password", "testrw-password") - .containsEntry("spring.datasource.replicated[0].rw.url", "jdbc:postgresql://testrw-host:testrw-port/testrw-database?options=-c opt1=val1 -c opt2=val2") - .containsEntry("spring.datasource.replicated[0].rw.username", "testrw-username") - .containsEntry("spring.r2dbc.replicated[0].name", "test-correlation"); + .containsEntry("spring.datasource.replicated.rw.driver-class-name", "org.postgresql.Driver") + .containsEntry("spring.datasource.replicated.rw.password", "testrw-password") + .containsEntry("spring.datasource.replicated.rw.url", "jdbc:postgresql://testrw-host:testrw-port/testrw-database?options=-c opt1=val1 -c opt2=val2") + .containsEntry("spring.datasource.replicated.rw.username", "testrw-username") + .containsEntry("spring.r2dbc.replicated.name", "dbrw"); } @Test @DisplayName("composes jdbc url from host port and database with invalid DB options") void testJdbcWithInvaildDBoptions() { Bindings bindings = new Bindings( - new Binding("dbrw", Paths.get("dbrw-path"), rwsecret.withEntry("options", "opt1=val1&opt")), - new Binding("dbro", Paths.get("dbro-path"), rosecret) + new Binding("dbrw", Paths.get("dbrw-path"), secret.withEntry("rw-options", "opt1=val1&opt")) ); new PostgreSqlReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); assertThat(properties) - .containsEntry("spring.datasource.replicated[0].rw.driver-class-name", "org.postgresql.Driver") - .containsEntry("spring.datasource.replicated[0].rw.password", "testrw-password") - .containsEntry("spring.datasource.replicated[0].rw.url", "jdbc:postgresql://testrw-host:testrw-port/testrw-database?options=-c opt1=val1") - .containsEntry("spring.datasource.replicated[0].rw.username", "testrw-username") - .containsEntry("spring.r2dbc.replicated[0].name", "test-correlation"); + .containsEntry("spring.datasource.replicated.rw.driver-class-name", "org.postgresql.Driver") + .containsEntry("spring.datasource.replicated.rw.password", "testrw-password") + .containsEntry("spring.datasource.replicated.rw.url", "jdbc:postgresql://testrw-host:testrw-port/testrw-database?options=-c opt1=val1") + .containsEntry("spring.datasource.replicated.rw.username", "testrw-username") + .containsEntry("spring.r2dbc.replicated.name", "dbrw"); } @Test @DisplayName("composes jdbc url from host port and database with empty DB options") void testJdbcWithEmptyDBoptions() { Bindings bindings = new Bindings( - new Binding("dbrw", Paths.get("dbrw-path"), rwsecret.withEntry("options", "")), - new Binding("dbro", Paths.get("dbro-path"), rosecret) + new Binding("dbrw", Paths.get("dbrw-path"), secret.withEntry("rw-options", "")) ); new PostgreSqlReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); assertThat(properties) - .containsEntry("spring.datasource.replicated[0].rw.driver-class-name", "org.postgresql.Driver") - .containsEntry("spring.datasource.replicated[0].rw.password", "testrw-password") - .containsEntry("spring.datasource.replicated[0].rw.url", "jdbc:postgresql://testrw-host:testrw-port/testrw-database") - .containsEntry("spring.datasource.replicated[0].rw.username", "testrw-username") - .containsEntry("spring.r2dbc.replicated[0].name", "test-correlation"); - } - - @Test - @DisplayName("composes invalid replication due to no correlation") - void testJdbcNoCorrection() { - Bindings bindings = new Bindings( - new Binding("dbrw", Paths.get("dbrw-path"), rwsecret.withEntry("correlation", "")), - new Binding("dbro", Paths.get("dbro-path"), rosecret.withEntry("correlation", "")) - ); - new PostgreSqlReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); - assertThat(properties).isEmpty(); + .containsEntry("spring.datasource.replicated.rw.driver-class-name", "org.postgresql.Driver") + .containsEntry("spring.datasource.replicated.rw.password", "testrw-password") + .containsEntry("spring.datasource.replicated.rw.url", "jdbc:postgresql://testrw-host:testrw-port/testrw-database") + .containsEntry("spring.datasource.replicated.rw.username", "testrw-username") + .containsEntry("spring.r2dbc.replicated.name", "dbrw"); } - - @Test - @DisplayName("composes invalid replication due to invalide fucntion") - void testJdbcInvalidFunction() { - Bindings bindings = new Bindings( - new Binding("dbrw", Paths.get("dbrw-path"), rwsecret.withEntry("function", "invalid")), - new Binding("dbro", Paths.get("dbro-path"), rosecret.withEntry("function", "invalid")) - ); - new PostgreSqlReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); - assertThat(properties).isEmpty(); - } + } From ffc101b902d0f59dd223583971bb8e5364531ae4 Mon Sep 17 00:00:00 2001 From: meyerg Date: Wed, 28 Feb 2024 07:30:46 -0600 Subject: [PATCH 3/6] Creating replicated binding processors for remaining DB options. Created an abstract class for most replicated database processors as there is a common pattern between these processors. Added unit tests for new replicated binding processors. --- .../boot/AbstractReplicatedDataSource.java | 60 +++++++++ ...ReplicatedBindingsPropertiesProcessor.java | 73 +++++++++++ ...ReplicatedBindingsPropertiesProcessor.java | 100 +++++++++++++++ ...ReplicatedBindingsPropertiesProcessor.java | 58 +++++++++ ...ReplicatedBindingsPropertiesProcessor.java | 56 ++++++++ ...ReplicatedBindingsPropertiesProcessor.java | 56 ++++++++ ...icatedBindingsPropertiesProcessorTest.java | 120 ++++++++++++++++++ ...icatedBindingsPropertiesProcessorTest.java | 120 ++++++++++++++++++ ...icatedBindingsPropertiesProcessorTest.java | 119 +++++++++++++++++ ...icatedBindingsPropertiesProcessorTest.java | 120 ++++++++++++++++++ ...icatedBindingsPropertiesProcessorTest.java | 120 ++++++++++++++++++ 11 files changed, 1002 insertions(+) create mode 100644 spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/AbstractReplicatedDataSource.java create mode 100644 spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/Db2ReplicatedBindingsPropertiesProcessor.java create mode 100644 spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/MySqlReplicatedBindingsPropertiesProcessor.java create mode 100644 spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/OracleReplicatedBindingsPropertiesProcessor.java create mode 100644 spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/SapHanaReplicatedBindingsPropertiesProcessor.java create mode 100644 spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/SqlServerReplicatedBindingsPropertiesProcessor.java create mode 100644 spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/Db2ReplicatedBindingsPropertiesProcessorTest.java create mode 100644 spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/MySqlReplicatedBindingsPropertiesProcessorTest.java create mode 100644 spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/OracleReplicatedBindingsPropertiesProcessorTest.java create mode 100644 spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/SapHanaReplicatedBindingsPropertiesProcessorTest.java create mode 100644 spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/SqlServerReplicatedBindingsPropertiesProcessorTest.java diff --git a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/AbstractReplicatedDataSource.java b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/AbstractReplicatedDataSource.java new file mode 100644 index 0000000..dd836a5 --- /dev/null +++ b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/AbstractReplicatedDataSource.java @@ -0,0 +1,60 @@ +package org.springframework.cloud.bindings.boot; + +import static org.springframework.cloud.bindings.boot.Guards.isTypeEnabled; + +import java.util.Map; + +import org.springframework.cloud.bindings.Binding; +import org.springframework.cloud.bindings.Bindings; +import org.springframework.core.env.Environment; + +public abstract class AbstractReplicatedDataSource implements BindingsPropertiesProcessor { + + public static final String RW_FUNCTION = "rw"; + + public static final String RO_FUNCTION = "ro"; + + public static final String JDBC_BASE_TEMPLATE = "spring.datasource.replicated.%s"; + + public static final String R2DBC_BASE_TEMPLATE = "spring.r2dbc.replicated.%s"; + + public static final String JDBC_PROPERTY_TEMPLATE = "spring.datasource.replicated.%s.%s"; + + public static final String R2DBC_PROPERTY_TEMPLATE = "spring.r2dbc.replicated.%s.%s"; + + @Override + public void process(Environment environment, Bindings bindings, Map properties) { + if (!isTypeEnabled(environment, getType())) { + return; + } + + bindings.filterBindings(getType()).forEach(binding -> { + + properties.put(String.format(JDBC_BASE_TEMPLATE, "name"), binding.getName()); + + properties.put(String.format(R2DBC_BASE_TEMPLATE, "name"), binding.getName()); + + final var map = new MapMapper(binding.getSecret(), properties); + + buildProperties(map, properties, binding, RW_FUNCTION); + + buildProperties(map, properties, binding, RO_FUNCTION); + + }); + } + + protected String formatJDBCTemplate(String fnc, String field) + { + return String.format(JDBC_PROPERTY_TEMPLATE, fnc, field); + } + + protected String formatR2DBCTemplate(String fnc, String field) + { + return String.format(R2DBC_PROPERTY_TEMPLATE, fnc, field); + } + + protected abstract void buildProperties(MapMapper map, Map properties, Binding binding, String fnc); + + protected abstract String getType(); + +} diff --git a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/Db2ReplicatedBindingsPropertiesProcessor.java b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/Db2ReplicatedBindingsPropertiesProcessor.java new file mode 100644 index 0000000..8a289c6 --- /dev/null +++ b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/Db2ReplicatedBindingsPropertiesProcessor.java @@ -0,0 +1,73 @@ +/* + * Copyright 2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.bindings.boot; + +import java.util.Map; + +import org.springframework.boot.context.event.ApplicationPreparedEvent; +import org.springframework.boot.logging.DeferredLog; +import org.springframework.cloud.bindings.Binding; +import org.springframework.context.ApplicationListener; + +/** + * An implementation of {@link BindingsPropertiesProcessor} that detects {@link Binding}s of type: {@value TYPE}. + */ +public class Db2ReplicatedBindingsPropertiesProcessor extends AbstractReplicatedDataSource + implements ApplicationListener { + + /** + * The {@link Binding} type that this processor is interested in: {@value}. + **/ + public static final String TYPE = "db2-replicated"; + + private static final DeferredLog LOG = new DeferredLog(); + + @Override + protected void buildProperties(MapMapper map, Map properties, Binding binding, String fnc) + { + + //jdbc properties + map.from(fnc + "-username").to(formatJDBCTemplate(fnc, "username")); + map.from(fnc + "-password").to(formatJDBCTemplate(fnc, "password")); + map.from(fnc + "-host", fnc + "-port", fnc + "-database").to(formatJDBCTemplate(fnc, "url"), + (host, port, database) -> String.format("jdbc:db2://%s:%s/%s", host, port, database)); + + // jdbcURL takes precedence + map.from(fnc + "-jdbc-url").to(formatJDBCTemplate(fnc, "url")); + + properties.put(formatJDBCTemplate(fnc, "driver-class-name"), "com.ibm.db2.jcc.DB2Driver"); + + //r2dbc properties + map.from(fnc + "-username").to(formatR2DBCTemplate(fnc, "username")); + map.from(fnc + "-password").to(formatR2DBCTemplate(fnc, "password")); + map.from(fnc + "-host", fnc + "-port", fnc + "-database").to(formatR2DBCTemplate(fnc, "url"), + (host, port, database) -> String.format("r2dbc:db2://%s:%s/%s", host, port, database)); + + // r2dbcURL takes precedence + map.from(fnc + "-r2dbc-url").to(formatR2DBCTemplate(fnc, "url")); + } + + @Override + protected String getType() { + return TYPE; + } + + @Override + public void onApplicationEvent(ApplicationPreparedEvent event) { + LOG.replayTo(getClass()); + } +} diff --git a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/MySqlReplicatedBindingsPropertiesProcessor.java b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/MySqlReplicatedBindingsPropertiesProcessor.java new file mode 100644 index 0000000..b5eaf0d --- /dev/null +++ b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/MySqlReplicatedBindingsPropertiesProcessor.java @@ -0,0 +1,100 @@ +package org.springframework.cloud.bindings.boot; + +import java.util.Map; + +import org.springframework.boot.context.event.ApplicationPreparedEvent; +import org.springframework.boot.logging.DeferredLog; +import org.springframework.cloud.bindings.Binding; +import org.springframework.context.ApplicationListener; + +/** + * An implementation of {@link BindingsPropertiesProcessor} that detects {@link Binding}s of type: {@value TYPE}. + */ +public class MySqlReplicatedBindingsPropertiesProcessor extends AbstractReplicatedDataSource + implements ApplicationListener { + + /** + * The {@link Binding} type that this processor is interested in: {@value}. + **/ + public static final String TYPE = "mysql-replicated"; + + private static final DeferredLog LOG = new DeferredLog(); + + /** + * MySQL connection protocol constant. + */ + private static final String MYSQL_PROTOCOL = "mysql"; + + /** + * MariaDB connection protocol constant. + */ + private static final String MARIADB_PROTOCOL = "mariadb"; + + @Override + protected void buildProperties(MapMapper map, Map properties, Binding binding, String fnc) + { + + //jdbc properties + map.from(fnc + "-username").to(formatJDBCTemplate(fnc, "username")); + map.from(fnc + "-password").to(formatJDBCTemplate(fnc, "password")); + map.from(fnc + "-host", fnc + "-port", fnc + "-database").to(formatJDBCTemplate(fnc, "url"), + (host, port, database) -> String.format("jdbc:%s://%s:%s/%s", evalProtocol(), host, port, database)); + + // jdbcURL takes precedence + map.from(fnc + "-jdbc-url").to(formatJDBCTemplate(fnc, "url")); + + properties.put(formatJDBCTemplate(fnc, "driver-class-name"), "com.ibm.db2.jcc.DB2Driver"); + try { + Class.forName("org.mariadb.jdbc.Driver", false, getClass().getClassLoader()); + properties.put(formatJDBCTemplate(fnc, "driver-class-name"), "org.mariadb.jdbc.Driver"); + } catch (ClassNotFoundException e) { + try { + Class.forName("com.mysql.cj.jdbc.Driver", false, getClass().getClassLoader()); + properties.put(formatJDBCTemplate(fnc, "driver-class-name"), "com.mysql.cj.jdbc.Driver"); + } catch (ClassNotFoundException ignored) { + } + } + + + //r2dbc properties + map.from(fnc + "-username").to(formatR2DBCTemplate(fnc, "username")); + map.from(fnc + "-password").to(formatR2DBCTemplate(fnc, "password")); + map.from(fnc + "-host", fnc + "-port", fnc + "-database").to(formatR2DBCTemplate(fnc, "url"), + (host, port, database) -> String.format("r2dbc:%s://%s:%s/%s", evalProtocol(), host, port, database)); + + // r2dbcURL takes precedence + map.from(fnc + "-r2dbc-url").to(formatR2DBCTemplate(fnc, "url")); + } + + @Override + protected String getType() { + return TYPE; + } + + @Override + public void onApplicationEvent(ApplicationPreparedEvent event) { + LOG.replayTo(getClass()); + } + + private String evalProtocol() + { + // Default to "mysql" + String connectionProtocol = MYSQL_PROTOCOL; + + /* Starting with Spring Boot 2.7.0, the previous MySQL r2dbc driver is no longer supported and + * documentation suggests using the MariaDB R2DBC driver as an alternative. Some versions + * of the MariaDB R2DBC driver do not support "mysql" as part of the connection + * protocol; "mariadb" should be used instead when the MariaDB R2DBC driver class is on + * the classpath. + */ + + try { + Class.forName("org.mariadb.r2dbc.MariadbConnection"); + connectionProtocol = MARIADB_PROTOCOL; + } + catch (ClassNotFoundException ignored) { + } + + return connectionProtocol; + } +} diff --git a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/OracleReplicatedBindingsPropertiesProcessor.java b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/OracleReplicatedBindingsPropertiesProcessor.java new file mode 100644 index 0000000..860cfa8 --- /dev/null +++ b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/OracleReplicatedBindingsPropertiesProcessor.java @@ -0,0 +1,58 @@ +package org.springframework.cloud.bindings.boot; + +import java.util.Map; + +import org.springframework.boot.context.event.ApplicationPreparedEvent; +import org.springframework.boot.logging.DeferredLog; +import org.springframework.cloud.bindings.Binding; +import org.springframework.context.ApplicationListener; + +/** + * An implementation of {@link BindingsPropertiesProcessor} that detects {@link Binding}s of type: {@value TYPE}. + */ +public class OracleReplicatedBindingsPropertiesProcessor extends AbstractReplicatedDataSource + implements ApplicationListener { + + /** + * The {@link Binding} type that this processor is interested in: {@value}. + **/ + public static final String TYPE = "oracle-replicated"; + + private static final DeferredLog LOG = new DeferredLog(); + + @Override + protected void buildProperties(MapMapper map, Map properties, Binding binding, String fnc) + { + + //jdbc properties + map.from(fnc + "-username").to(formatJDBCTemplate(fnc, "username")); + map.from(fnc + "-password").to(formatJDBCTemplate(fnc, "password")); + map.from(fnc + "-host", fnc + "-port", fnc + "-database").to(formatJDBCTemplate(fnc, "url"), + (host, port, database) -> String.format("jdbc:oracle://%s:%s/%s", host, port, database)); + + // jdbcURL takes precedence + map.from(fnc + "-jdbc-url").to(formatJDBCTemplate(fnc, "url")); + + properties.put(formatJDBCTemplate(fnc, "driver-class-name"), "oracle.jdbc.OracleDriver"); + + //r2dbc properties + map.from(fnc + "-username").to(formatR2DBCTemplate(fnc, "username")); + map.from(fnc + "-password").to(formatR2DBCTemplate(fnc, "password")); + map.from(fnc + "-host", fnc + "-port", fnc + "-database").to(formatR2DBCTemplate(fnc, "url"), + (host, port, database) -> String.format("r2dbc:oracle://%s:%s/%s", host, port, database)); + + // r2dbcURL takes precedence + map.from(fnc + "-r2dbc-url").to(formatR2DBCTemplate(fnc, "url")); + } + + @Override + protected String getType() { + return TYPE; + } + + @Override + public void onApplicationEvent(ApplicationPreparedEvent event) { + LOG.replayTo(getClass()); + } + +} diff --git a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/SapHanaReplicatedBindingsPropertiesProcessor.java b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/SapHanaReplicatedBindingsPropertiesProcessor.java new file mode 100644 index 0000000..c5b9662 --- /dev/null +++ b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/SapHanaReplicatedBindingsPropertiesProcessor.java @@ -0,0 +1,56 @@ +package org.springframework.cloud.bindings.boot; + +import java.util.Map; + +import org.springframework.boot.context.event.ApplicationPreparedEvent; +import org.springframework.boot.logging.DeferredLog; +import org.springframework.cloud.bindings.Binding; +import org.springframework.context.ApplicationListener; + +/** + * An implementation of {@link BindingsPropertiesProcessor} that detects {@link Binding}s of type: {@value TYPE}. + */ +public class SapHanaReplicatedBindingsPropertiesProcessor extends AbstractReplicatedDataSource + implements ApplicationListener { + + /** + * The {@link Binding} type that this processor is interested in: {@value}. + **/ + public static final String TYPE = "hana-replicated"; + + private static final DeferredLog LOG = new DeferredLog(); + + @Override + protected void buildProperties(MapMapper map, Map properties, Binding binding, String fnc) + { + //jdbc properties + map.from(fnc + "-username").to(formatJDBCTemplate(fnc, "username")); + map.from(fnc + "-password").to(formatJDBCTemplate(fnc, "password")); + map.from(fnc + "-host", fnc + "-port", fnc + "-database").to(formatJDBCTemplate(fnc, "url"), + (host, port, database) -> String.format("jdbc:sap://%s:%s/%s", host, port, database)); + + // jdbcURL takes precedence + map.from(fnc + "-jdbc-url").to(formatJDBCTemplate(fnc, "url")); + + properties.put(formatJDBCTemplate(fnc, "driver-class-name"), "com.sap.db.jdbc.Driver"); + + //r2dbc properties + map.from(fnc + "-username").to(formatR2DBCTemplate(fnc, "username")); + map.from(fnc + "-password").to(formatR2DBCTemplate(fnc, "password")); + map.from(fnc + "-host", fnc + "-port", fnc + "-database").to(formatR2DBCTemplate(fnc, "url"), + (host, port, database) -> String.format("r2dbc:sap://%s:%s/%s", host, port, database)); + + // r2dbcURL takes precedence + map.from(fnc + "-r2dbc-url").to(formatR2DBCTemplate(fnc, "url")); + } + + @Override + protected String getType() { + return TYPE; + } + + @Override + public void onApplicationEvent(ApplicationPreparedEvent event) { + LOG.replayTo(getClass()); + } +} diff --git a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/SqlServerReplicatedBindingsPropertiesProcessor.java b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/SqlServerReplicatedBindingsPropertiesProcessor.java new file mode 100644 index 0000000..359713e --- /dev/null +++ b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/SqlServerReplicatedBindingsPropertiesProcessor.java @@ -0,0 +1,56 @@ +package org.springframework.cloud.bindings.boot; + +import java.util.Map; + +import org.springframework.boot.context.event.ApplicationPreparedEvent; +import org.springframework.boot.logging.DeferredLog; +import org.springframework.cloud.bindings.Binding; +import org.springframework.context.ApplicationListener; + +/** + * An implementation of {@link BindingsPropertiesProcessor} that detects {@link Binding}s of type: {@value TYPE}. + */ +public class SqlServerReplicatedBindingsPropertiesProcessor extends AbstractReplicatedDataSource + implements ApplicationListener { + + /** + * The {@link Binding} type that this processor is interested in: {@value}. + **/ + public static final String TYPE = "sqlserver-replicated"; + + private static final DeferredLog LOG = new DeferredLog(); + + @Override + protected void buildProperties(MapMapper map, Map properties, Binding binding, String fnc) + { + //jdbc properties + map.from(fnc + "-username").to(formatJDBCTemplate(fnc, "username")); + map.from(fnc + "-password").to(formatJDBCTemplate(fnc, "password")); + map.from(fnc + "-host", fnc + "-port", fnc + "-database").to(formatJDBCTemplate(fnc, "url"), + (host, port, database) -> String.format("jdbc:sqlserver://%s:%s/%s", host, port, database)); + + // jdbcURL takes precedence + map.from(fnc + "-jdbc-url").to(formatJDBCTemplate(fnc, "url")); + + properties.put(formatJDBCTemplate(fnc, "driver-class-name"), "com.microsoft.sqlserver.jdbc.SQLServerDriver"); + + //r2dbc properties + map.from(fnc + "-username").to(formatR2DBCTemplate(fnc, "username")); + map.from(fnc + "-password").to(formatR2DBCTemplate(fnc, "password")); + map.from(fnc + "-host", fnc + "-port", fnc + "-database").to(formatR2DBCTemplate(fnc, "url"), + (host, port, database) -> String.format("r2dbc:sqlserver://%s:%s/%s", host, port, database)); + + // r2dbcURL takes precedence + map.from(fnc + "-r2dbc-url").to(formatR2DBCTemplate(fnc, "url")); + } + + @Override + protected String getType() { + return TYPE; + } + + @Override + public void onApplicationEvent(ApplicationPreparedEvent event) { + LOG.replayTo(getClass()); + } +} diff --git a/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/Db2ReplicatedBindingsPropertiesProcessorTest.java b/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/Db2ReplicatedBindingsPropertiesProcessorTest.java new file mode 100644 index 0000000..86045a0 --- /dev/null +++ b/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/Db2ReplicatedBindingsPropertiesProcessorTest.java @@ -0,0 +1,120 @@ +package org.springframework.cloud.bindings.boot; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.cloud.bindings.boot.Db2ReplicatedBindingsPropertiesProcessor.TYPE; + +import java.nio.file.Paths; +import java.util.HashMap; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.cloud.bindings.Binding; +import org.springframework.cloud.bindings.Bindings; +import org.springframework.cloud.bindings.FluentMap; +import org.springframework.mock.env.MockEnvironment; + +@DisplayName("DB2Replicated BindingsPropertiesProcessor") +public class Db2ReplicatedBindingsPropertiesProcessorTest { + + private final FluentMap secret = new FluentMap() + .withEntry(Binding.TYPE, TYPE) + .withEntry("rw-database", "testrw-database") + .withEntry("rw-host", "testrw-host") + .withEntry("rw-password", "testrw-password") + .withEntry("rw-port", "testrw-port") + .withEntry("rw-username", "testrw-username") + .withEntry("ro-database", "testro-database") + .withEntry("ro-host", "testro-host") + .withEntry("ro-password", "testro-password") + .withEntry("ro-port", "testro-port") + .withEntry("ro-username", "testro-username"); + + private final MockEnvironment environment = new MockEnvironment(); + + private final HashMap properties = new HashMap<>(); + + @Test + @DisplayName("composes jdbc url from host port and database") + void testJdbc() { + Bindings bindings = new Bindings( + new Binding("dbrw", Paths.get("dbrw-path"), secret) + ); + new Db2ReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); + assertThat(properties) + .containsEntry("spring.datasource.replicated.rw.driver-class-name", "com.ibm.db2.jcc.DB2Driver") + .containsEntry("spring.datasource.replicated.rw.password", "testrw-password") + .containsEntry("spring.datasource.replicated.rw.url", "jdbc:db2://testrw-host:testrw-port/testrw-database") + .containsEntry("spring.datasource.replicated.rw.username", "testrw-username") + .containsEntry("spring.datasource.replicated.ro.driver-class-name", "com.ibm.db2.jcc.DB2Driver") + .containsEntry("spring.datasource.replicated.ro.password", "testro-password") + .containsEntry("spring.datasource.replicated.ro.url", "jdbc:db2://testro-host:testro-port/testro-database") + .containsEntry("spring.datasource.replicated.ro.username", "testro-username") + .containsEntry("spring.datasource.replicated.name", "dbrw"); + } + + @Test + @DisplayName("gives precedence to jdbc-url") + void testJdbcURL() { + Bindings bindings = new Bindings( + new Binding("dbrw", Paths.get("dbrw-path"), secret.withEntry("rw-jdbc-url", "testrw-jdbc-url").withEntry("ro-jdbc-url", "testro-jdbc-url")) + ); + new Db2ReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); + assertThat(properties) + .containsEntry("spring.datasource.replicated.rw.driver-class-name", "com.ibm.db2.jcc.DB2Driver") + .containsEntry("spring.datasource.replicated.rw.password", "testrw-password") + .containsEntry("spring.datasource.replicated.rw.url", "testrw-jdbc-url") + .containsEntry("spring.datasource.replicated.rw.username", "testrw-username") + .containsEntry("spring.datasource.replicated.ro.driver-class-name", "com.ibm.db2.jcc.DB2Driver") + .containsEntry("spring.datasource.replicated.ro.password", "testro-password") + .containsEntry("spring.datasource.replicated.ro.url", "testro-jdbc-url") + .containsEntry("spring.datasource.replicated.ro.username", "testro-username") + .containsEntry("spring.datasource.replicated.name", "dbrw"); + } + + @Test + @DisplayName("contributes r2dbc properties") + void testR2dbc() { + Bindings bindings = new Bindings( + new Binding("dbrw", Paths.get("dbrw-path"), secret) + ); + new Db2ReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); + assertThat(properties) + .containsEntry("spring.r2dbc.replicated.rw.password", "testrw-password") + .containsEntry("spring.r2dbc.replicated.rw.url", "r2dbc:db2://testrw-host:testrw-port/testrw-database") + .containsEntry("spring.r2dbc.replicated.rw.username", "testrw-username") + .containsEntry("spring.r2dbc.replicated.ro.password", "testro-password") + .containsEntry("spring.r2dbc.replicated.ro.url", "r2dbc:db2://testro-host:testro-port/testro-database") + .containsEntry("spring.r2dbc.replicated.ro.username", "testro-username") + .containsEntry("spring.r2dbc.replicated.name", "dbrw"); + } + + @Test + @DisplayName("gives precedence to r2dbc-url") + void testR2dbcURL() { + Bindings bindings = new Bindings( + new Binding("dbrw", Paths.get("dbrw-path"), secret.withEntry("rw-r2dbc-url", "testrw-r2dbc-url").withEntry("ro-r2dbc-url", "testro-r2dbc-url")) + ); + new Db2ReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); + assertThat(properties) + .containsEntry("spring.r2dbc.replicated.rw.password", "testrw-password") + .containsEntry("spring.r2dbc.replicated.rw.url", "testrw-r2dbc-url") + .containsEntry("spring.r2dbc.replicated.rw.username", "testrw-username") + .containsEntry("spring.r2dbc.replicated.ro.password", "testro-password") + .containsEntry("spring.r2dbc.replicated.ro.url", "testro-r2dbc-url") + .containsEntry("spring.r2dbc.replicated.ro.username", "testro-username") + .containsEntry("spring.r2dbc.replicated.name", "dbrw"); + } + + @Test + @DisplayName("can be disabled") + void disabled() { + Bindings bindings = new Bindings( + new Binding("dbrw", Paths.get("dbrw-path"), secret) + ); + environment.setProperty("org.springframework.cloud.bindings.boot.db2-replicated.enable", "false"); + + new Db2ReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); + + assertThat(properties).isEmpty(); + } +} diff --git a/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/MySqlReplicatedBindingsPropertiesProcessorTest.java b/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/MySqlReplicatedBindingsPropertiesProcessorTest.java new file mode 100644 index 0000000..698f5e2 --- /dev/null +++ b/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/MySqlReplicatedBindingsPropertiesProcessorTest.java @@ -0,0 +1,120 @@ +package org.springframework.cloud.bindings.boot; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.cloud.bindings.boot.MySqlReplicatedBindingsPropertiesProcessor.TYPE; + +import java.nio.file.Paths; +import java.util.HashMap; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.cloud.bindings.Binding; +import org.springframework.cloud.bindings.Bindings; +import org.springframework.cloud.bindings.FluentMap; +import org.springframework.mock.env.MockEnvironment; + +@DisplayName("MySQLReplicated BindingsPropertiesProcessor") +public class MySqlReplicatedBindingsPropertiesProcessorTest { + + private final FluentMap secret = new FluentMap() + .withEntry(Binding.TYPE, TYPE) + .withEntry("rw-database", "testrw-database") + .withEntry("rw-host", "testrw-host") + .withEntry("rw-password", "testrw-password") + .withEntry("rw-port", "testrw-port") + .withEntry("rw-username", "testrw-username") + .withEntry("ro-database", "testro-database") + .withEntry("ro-host", "testro-host") + .withEntry("ro-password", "testro-password") + .withEntry("ro-port", "testro-port") + .withEntry("ro-username", "testro-username"); + + private final MockEnvironment environment = new MockEnvironment(); + + private final HashMap properties = new HashMap<>(); + + @Test + @DisplayName("composes jdbc url from host port and database") + void testJdbc() { + Bindings bindings = new Bindings( + new Binding("dbrw", Paths.get("dbrw-path"), secret) + ); + new MySqlReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); + assertThat(properties) + .containsEntry("spring.datasource.replicated.rw.driver-class-name", "org.mariadb.jdbc.Driver") + .containsEntry("spring.datasource.replicated.rw.password", "testrw-password") + .containsEntry("spring.datasource.replicated.rw.url", "jdbc:mariadb://testrw-host:testrw-port/testrw-database") + .containsEntry("spring.datasource.replicated.rw.username", "testrw-username") + .containsEntry("spring.datasource.replicated.ro.driver-class-name", "org.mariadb.jdbc.Driver") + .containsEntry("spring.datasource.replicated.ro.password", "testro-password") + .containsEntry("spring.datasource.replicated.ro.url", "jdbc:mariadb://testro-host:testro-port/testro-database") + .containsEntry("spring.datasource.replicated.ro.username", "testro-username") + .containsEntry("spring.datasource.replicated.name", "dbrw"); + } + + @Test + @DisplayName("gives precedence to jdbc-url") + void testJdbcURL() { + Bindings bindings = new Bindings( + new Binding("dbrw", Paths.get("dbrw-path"), secret.withEntry("rw-jdbc-url", "testrw-jdbc-url").withEntry("ro-jdbc-url", "testro-jdbc-url")) + ); + new MySqlReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); + assertThat(properties) + .containsEntry("spring.datasource.replicated.rw.driver-class-name", "org.mariadb.jdbc.Driver") + .containsEntry("spring.datasource.replicated.rw.password", "testrw-password") + .containsEntry("spring.datasource.replicated.rw.url", "testrw-jdbc-url") + .containsEntry("spring.datasource.replicated.rw.username", "testrw-username") + .containsEntry("spring.datasource.replicated.ro.driver-class-name", "org.mariadb.jdbc.Driver") + .containsEntry("spring.datasource.replicated.ro.password", "testro-password") + .containsEntry("spring.datasource.replicated.ro.url", "testro-jdbc-url") + .containsEntry("spring.datasource.replicated.ro.username", "testro-username") + .containsEntry("spring.datasource.replicated.name", "dbrw"); + } + + @Test + @DisplayName("composes r2dbc url from host port and database") + void testR2dbc() { + Bindings bindings = new Bindings( + new Binding("dbrw", Paths.get("dbrw-path"), secret) + ); + new MySqlReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); + assertThat(properties) + .containsEntry("spring.r2dbc.replicated.rw.password", "testrw-password") + .containsEntry("spring.r2dbc.replicated.rw.url", "r2dbc:mariadb://testrw-host:testrw-port/testrw-database") + .containsEntry("spring.r2dbc.replicated.rw.username", "testrw-username") + .containsEntry("spring.r2dbc.replicated.ro.password", "testro-password") + .containsEntry("spring.r2dbc.replicated.ro.url", "r2dbc:mariadb://testro-host:testro-port/testro-database") + .containsEntry("spring.r2dbc.replicated.ro.username", "testro-username") + .containsEntry("spring.r2dbc.replicated.name", "dbrw"); + } + + @Test + @DisplayName("gives precedence to r2dbc-url") + void testR2dbcURL() { + Bindings bindings = new Bindings( + new Binding("dbrw", Paths.get("dbrw-path"), secret.withEntry("rw-r2dbc-url", "testrw-r2dbc-url").withEntry("ro-r2dbc-url", "testro-r2dbc-url")) + ); + new MySqlReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); + assertThat(properties) + .containsEntry("spring.r2dbc.replicated.rw.password", "testrw-password") + .containsEntry("spring.r2dbc.replicated.rw.url", "testrw-r2dbc-url") + .containsEntry("spring.r2dbc.replicated.rw.username", "testrw-username") + .containsEntry("spring.r2dbc.replicated.ro.password", "testro-password") + .containsEntry("spring.r2dbc.replicated.ro.url", "testro-r2dbc-url") + .containsEntry("spring.r2dbc.replicated.ro.username", "testro-username") + .containsEntry("spring.r2dbc.replicated.name", "dbrw"); + } + + @Test + @DisplayName("can be disabled") + void disabled() { + Bindings bindings = new Bindings( + new Binding("dbrw", Paths.get("dbrw-path"), secret) + ); + environment.setProperty("org.springframework.cloud.bindings.boot.mysql-replicated.enable", "false"); + + new MySqlReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); + + assertThat(properties).isEmpty(); + } +} diff --git a/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/OracleReplicatedBindingsPropertiesProcessorTest.java b/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/OracleReplicatedBindingsPropertiesProcessorTest.java new file mode 100644 index 0000000..f562587 --- /dev/null +++ b/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/OracleReplicatedBindingsPropertiesProcessorTest.java @@ -0,0 +1,119 @@ +package org.springframework.cloud.bindings.boot; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.cloud.bindings.boot.OracleReplicatedBindingsPropertiesProcessor.TYPE; + +import java.nio.file.Paths; +import java.util.HashMap; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.cloud.bindings.Binding; +import org.springframework.cloud.bindings.Bindings; +import org.springframework.cloud.bindings.FluentMap; +import org.springframework.mock.env.MockEnvironment; + +@DisplayName("OracleReplicated BindingsPropertiesProcessor") +public class OracleReplicatedBindingsPropertiesProcessorTest { + private final FluentMap secret = new FluentMap() + .withEntry(Binding.TYPE, TYPE) + .withEntry("rw-database", "testrw-database") + .withEntry("rw-host", "testrw-host") + .withEntry("rw-password", "testrw-password") + .withEntry("rw-port", "testrw-port") + .withEntry("rw-username", "testrw-username") + .withEntry("ro-database", "testro-database") + .withEntry("ro-host", "testro-host") + .withEntry("ro-password", "testro-password") + .withEntry("ro-port", "testro-port") + .withEntry("ro-username", "testro-username"); + + private final MockEnvironment environment = new MockEnvironment(); + + private final HashMap properties = new HashMap<>(); + + @Test + @DisplayName("composes jdbc url from host port and database") + void testJdbc() { + Bindings bindings = new Bindings( + new Binding("dbrw", Paths.get("dbrw-path"), secret) + ); + new OracleReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); + assertThat(properties) + .containsEntry("spring.datasource.replicated.rw.driver-class-name", "oracle.jdbc.OracleDriver") + .containsEntry("spring.datasource.replicated.rw.password", "testrw-password") + .containsEntry("spring.datasource.replicated.rw.url", "jdbc:oracle://testrw-host:testrw-port/testrw-database") + .containsEntry("spring.datasource.replicated.rw.username", "testrw-username") + .containsEntry("spring.datasource.replicated.ro.driver-class-name", "oracle.jdbc.OracleDriver") + .containsEntry("spring.datasource.replicated.ro.password", "testro-password") + .containsEntry("spring.datasource.replicated.ro.url", "jdbc:oracle://testro-host:testro-port/testro-database") + .containsEntry("spring.datasource.replicated.ro.username", "testro-username") + .containsEntry("spring.datasource.replicated.name", "dbrw"); + } + + @Test + @DisplayName("gives precedence to jdbc-url") + void testJdbcURL() { + Bindings bindings = new Bindings( + new Binding("dbrw", Paths.get("dbrw-path"), secret.withEntry("rw-jdbc-url", "testrw-jdbc-url").withEntry("ro-jdbc-url", "testro-jdbc-url")) + ); + new OracleReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); + assertThat(properties) + .containsEntry("spring.datasource.replicated.rw.driver-class-name", "oracle.jdbc.OracleDriver") + .containsEntry("spring.datasource.replicated.rw.password", "testrw-password") + .containsEntry("spring.datasource.replicated.rw.url", "testrw-jdbc-url") + .containsEntry("spring.datasource.replicated.rw.username", "testrw-username") + .containsEntry("spring.datasource.replicated.ro.driver-class-name", "oracle.jdbc.OracleDriver") + .containsEntry("spring.datasource.replicated.ro.password", "testro-password") + .containsEntry("spring.datasource.replicated.ro.url", "testro-jdbc-url") + .containsEntry("spring.datasource.replicated.ro.username", "testro-username") + .containsEntry("spring.datasource.replicated.name", "dbrw"); + } + + @Test + @DisplayName("composes r2dbc url from host port and database") + void testR2dbc() { + Bindings bindings = new Bindings( + new Binding("dbrw", Paths.get("dbrw-path"), secret) + ); + new OracleReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); + assertThat(properties) + .containsEntry("spring.r2dbc.replicated.rw.password", "testrw-password") + .containsEntry("spring.r2dbc.replicated.rw.url", "r2dbc:oracle://testrw-host:testrw-port/testrw-database") + .containsEntry("spring.r2dbc.replicated.rw.username", "testrw-username") + .containsEntry("spring.r2dbc.replicated.ro.password", "testro-password") + .containsEntry("spring.r2dbc.replicated.ro.url", "r2dbc:oracle://testro-host:testro-port/testro-database") + .containsEntry("spring.r2dbc.replicated.ro.username", "testro-username") + .containsEntry("spring.r2dbc.replicated.name", "dbrw"); + } + + @Test + @DisplayName("gives precedence to r2dbc-url") + void testR2dbcURL() { + Bindings bindings = new Bindings( + new Binding("dbrw", Paths.get("dbrw-path"), secret.withEntry("rw-r2dbc-url", "testrw-r2dbc-url").withEntry("ro-r2dbc-url", "testro-r2dbc-url")) + ); + new OracleReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); + assertThat(properties) + .containsEntry("spring.r2dbc.replicated.rw.password", "testrw-password") + .containsEntry("spring.r2dbc.replicated.rw.url", "testrw-r2dbc-url") + .containsEntry("spring.r2dbc.replicated.rw.username", "testrw-username") + .containsEntry("spring.r2dbc.replicated.ro.password", "testro-password") + .containsEntry("spring.r2dbc.replicated.ro.url", "testro-r2dbc-url") + .containsEntry("spring.r2dbc.replicated.ro.username", "testro-username") + .containsEntry("spring.r2dbc.replicated.name", "dbrw"); + } + + @Test + @DisplayName("can be disabled") + void disabled() { + Bindings bindings = new Bindings( + new Binding("dbrw", Paths.get("dbrw-path"), secret) + ); + environment.setProperty("org.springframework.cloud.bindings.boot.oracle-replicated.enable", "false"); + + new OracleReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); + + assertThat(properties).isEmpty(); + } +} diff --git a/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/SapHanaReplicatedBindingsPropertiesProcessorTest.java b/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/SapHanaReplicatedBindingsPropertiesProcessorTest.java new file mode 100644 index 0000000..a771752 --- /dev/null +++ b/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/SapHanaReplicatedBindingsPropertiesProcessorTest.java @@ -0,0 +1,120 @@ +package org.springframework.cloud.bindings.boot; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.cloud.bindings.boot.SapHanaReplicatedBindingsPropertiesProcessor.TYPE; + +import java.nio.file.Paths; +import java.util.HashMap; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.cloud.bindings.Binding; +import org.springframework.cloud.bindings.Bindings; +import org.springframework.cloud.bindings.FluentMap; +import org.springframework.mock.env.MockEnvironment; + +@DisplayName("SAP HanaRepicated BindingsPropertiesProcessor") +public class SapHanaReplicatedBindingsPropertiesProcessorTest { + + private final FluentMap secret = new FluentMap() + .withEntry(Binding.TYPE, TYPE) + .withEntry("rw-database", "testrw-database") + .withEntry("rw-host", "testrw-host") + .withEntry("rw-password", "testrw-password") + .withEntry("rw-port", "testrw-port") + .withEntry("rw-username", "testrw-username") + .withEntry("ro-database", "testro-database") + .withEntry("ro-host", "testro-host") + .withEntry("ro-password", "testro-password") + .withEntry("ro-port", "testro-port") + .withEntry("ro-username", "testro-username"); + + private final MockEnvironment environment = new MockEnvironment(); + + private final HashMap properties = new HashMap<>(); + + @Test + @DisplayName("composes jdbc url from host port and database") + void testJdbc() { + Bindings bindings = new Bindings( + new Binding("dbrw", Paths.get("dbrw-path"), secret) + ); + new SapHanaReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); + assertThat(properties) + .containsEntry("spring.datasource.replicated.rw.driver-class-name", "com.sap.db.jdbc.Driver") + .containsEntry("spring.datasource.replicated.rw.password", "testrw-password") + .containsEntry("spring.datasource.replicated.rw.url", "jdbc:sap://testrw-host:testrw-port/testrw-database") + .containsEntry("spring.datasource.replicated.rw.username", "testrw-username") + .containsEntry("spring.datasource.replicated.ro.driver-class-name", "com.sap.db.jdbc.Driver") + .containsEntry("spring.datasource.replicated.ro.password", "testro-password") + .containsEntry("spring.datasource.replicated.ro.url", "jdbc:sap://testro-host:testro-port/testro-database") + .containsEntry("spring.datasource.replicated.ro.username", "testro-username") + .containsEntry("spring.datasource.replicated.name", "dbrw"); + } + + @Test + @DisplayName("gives precedence to jdbc-url") + void testJdbcURL() { + Bindings bindings = new Bindings( + new Binding("dbrw", Paths.get("dbrw-path"), secret.withEntry("rw-jdbc-url", "testrw-jdbc-url").withEntry("ro-jdbc-url", "testro-jdbc-url")) + ); + new SapHanaReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); + assertThat(properties) + .containsEntry("spring.datasource.replicated.rw.driver-class-name", "com.sap.db.jdbc.Driver") + .containsEntry("spring.datasource.replicated.rw.password", "testrw-password") + .containsEntry("spring.datasource.replicated.rw.url", "testrw-jdbc-url") + .containsEntry("spring.datasource.replicated.rw.username", "testrw-username") + .containsEntry("spring.datasource.replicated.ro.driver-class-name", "com.sap.db.jdbc.Driver") + .containsEntry("spring.datasource.replicated.ro.password", "testro-password") + .containsEntry("spring.datasource.replicated.ro.url", "testro-jdbc-url") + .containsEntry("spring.datasource.replicated.ro.username", "testro-username") + .containsEntry("spring.datasource.replicated.name", "dbrw"); + } + + @Test + @DisplayName("composes r2dbc url from host port and database") + void testR2dbc() { + Bindings bindings = new Bindings( + new Binding("dbrw", Paths.get("dbrw-path"), secret) + ); + new SapHanaReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); + assertThat(properties) + .containsEntry("spring.r2dbc.replicated.rw.password", "testrw-password") + .containsEntry("spring.r2dbc.replicated.rw.url", "r2dbc:sap://testrw-host:testrw-port/testrw-database") + .containsEntry("spring.r2dbc.replicated.rw.username", "testrw-username") + .containsEntry("spring.r2dbc.replicated.ro.password", "testro-password") + .containsEntry("spring.r2dbc.replicated.ro.url", "r2dbc:sap://testro-host:testro-port/testro-database") + .containsEntry("spring.r2dbc.replicated.ro.username", "testro-username") + .containsEntry("spring.r2dbc.replicated.name", "dbrw"); + } + + @Test + @DisplayName("gives precedence to r2dbc-url") + void testR2dbcURL() { + Bindings bindings = new Bindings( + new Binding("dbrw", Paths.get("dbrw-path"), secret.withEntry("rw-r2dbc-url", "testrw-r2dbc-url").withEntry("ro-r2dbc-url", "testro-r2dbc-url")) + ); + new SapHanaReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); + assertThat(properties) + .containsEntry("spring.r2dbc.replicated.rw.password", "testrw-password") + .containsEntry("spring.r2dbc.replicated.rw.url", "testrw-r2dbc-url") + .containsEntry("spring.r2dbc.replicated.rw.username", "testrw-username") + .containsEntry("spring.r2dbc.replicated.ro.password", "testro-password") + .containsEntry("spring.r2dbc.replicated.ro.url", "testro-r2dbc-url") + .containsEntry("spring.r2dbc.replicated.ro.username", "testro-username") + .containsEntry("spring.r2dbc.replicated.name", "dbrw"); + } + + @Test + @DisplayName("can be disabled") + void disabled() { + Bindings bindings = new Bindings( + new Binding("dbrw", Paths.get("dbrw-path"), secret) + ); + environment.setProperty("org.springframework.cloud.bindings.boot.hana-replicated.enable", "false"); + + new SapHanaReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); + + assertThat(properties).isEmpty(); + } +} diff --git a/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/SqlServerReplicatedBindingsPropertiesProcessorTest.java b/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/SqlServerReplicatedBindingsPropertiesProcessorTest.java new file mode 100644 index 0000000..6f356e5 --- /dev/null +++ b/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/SqlServerReplicatedBindingsPropertiesProcessorTest.java @@ -0,0 +1,120 @@ +package org.springframework.cloud.bindings.boot; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.cloud.bindings.boot.SqlServerReplicatedBindingsPropertiesProcessor.TYPE; + +import java.nio.file.Paths; +import java.util.HashMap; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.cloud.bindings.Binding; +import org.springframework.cloud.bindings.Bindings; +import org.springframework.cloud.bindings.FluentMap; +import org.springframework.mock.env.MockEnvironment; + +@DisplayName("SQLServerReplicated BindingsPropertiesProcessor") +public class SqlServerReplicatedBindingsPropertiesProcessorTest { + + private final FluentMap secret = new FluentMap() + .withEntry(Binding.TYPE, TYPE) + .withEntry("rw-database", "testrw-database") + .withEntry("rw-host", "testrw-host") + .withEntry("rw-password", "testrw-password") + .withEntry("rw-port", "testrw-port") + .withEntry("rw-username", "testrw-username") + .withEntry("ro-database", "testro-database") + .withEntry("ro-host", "testro-host") + .withEntry("ro-password", "testro-password") + .withEntry("ro-port", "testro-port") + .withEntry("ro-username", "testro-username"); + + private final MockEnvironment environment = new MockEnvironment(); + + private final HashMap properties = new HashMap<>(); + + @Test + @DisplayName("composes jdbc url from host port and database") + void testJdbc() { + Bindings bindings = new Bindings( + new Binding("dbrw", Paths.get("dbrw-path"), secret) + ); + new SqlServerReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); + assertThat(properties) + .containsEntry("spring.datasource.replicated.rw.driver-class-name", "com.microsoft.sqlserver.jdbc.SQLServerDriver") + .containsEntry("spring.datasource.replicated.rw.password", "testrw-password") + .containsEntry("spring.datasource.replicated.rw.url", "jdbc:sqlserver://testrw-host:testrw-port/testrw-database") + .containsEntry("spring.datasource.replicated.rw.username", "testrw-username") + .containsEntry("spring.datasource.replicated.ro.driver-class-name", "com.microsoft.sqlserver.jdbc.SQLServerDriver") + .containsEntry("spring.datasource.replicated.ro.password", "testro-password") + .containsEntry("spring.datasource.replicated.ro.url", "jdbc:sqlserver://testro-host:testro-port/testro-database") + .containsEntry("spring.datasource.replicated.ro.username", "testro-username") + .containsEntry("spring.datasource.replicated.name", "dbrw"); + } + + @Test + @DisplayName("gives precedence to jdbc-url") + void testJdbcURL() { + Bindings bindings = new Bindings( + new Binding("dbrw", Paths.get("dbrw-path"), secret.withEntry("rw-jdbc-url", "testrw-jdbc-url").withEntry("ro-jdbc-url", "testro-jdbc-url")) + ); + new SqlServerReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); + assertThat(properties) + .containsEntry("spring.datasource.replicated.rw.driver-class-name", "com.microsoft.sqlserver.jdbc.SQLServerDriver") + .containsEntry("spring.datasource.replicated.rw.password", "testrw-password") + .containsEntry("spring.datasource.replicated.rw.url", "testrw-jdbc-url") + .containsEntry("spring.datasource.replicated.rw.username", "testrw-username") + .containsEntry("spring.datasource.replicated.ro.driver-class-name", "com.microsoft.sqlserver.jdbc.SQLServerDriver") + .containsEntry("spring.datasource.replicated.ro.password", "testro-password") + .containsEntry("spring.datasource.replicated.ro.url", "testro-jdbc-url") + .containsEntry("spring.datasource.replicated.ro.username", "testro-username") + .containsEntry("spring.datasource.replicated.name", "dbrw"); + } + + @Test + @DisplayName("composes r2dbc url from host port and database") + void testR2dbc() { + Bindings bindings = new Bindings( + new Binding("dbrw", Paths.get("dbrw-path"), secret) + ); + new SqlServerReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); + assertThat(properties) + .containsEntry("spring.r2dbc.replicated.rw.password", "testrw-password") + .containsEntry("spring.r2dbc.replicated.rw.url", "r2dbc:sqlserver://testrw-host:testrw-port/testrw-database") + .containsEntry("spring.r2dbc.replicated.rw.username", "testrw-username") + .containsEntry("spring.r2dbc.replicated.ro.password", "testro-password") + .containsEntry("spring.r2dbc.replicated.ro.url", "r2dbc:sqlserver://testro-host:testro-port/testro-database") + .containsEntry("spring.r2dbc.replicated.ro.username", "testro-username") + .containsEntry("spring.r2dbc.replicated.name", "dbrw"); + } + + @Test + @DisplayName("gives precedence to r2dbc-url") + void testR2dbcURL() { + Bindings bindings = new Bindings( + new Binding("dbrw", Paths.get("dbrw-path"), secret.withEntry("rw-r2dbc-url", "testrw-r2dbc-url").withEntry("ro-r2dbc-url", "testro-r2dbc-url")) + ); + new SqlServerReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); + assertThat(properties) + .containsEntry("spring.r2dbc.replicated.rw.password", "testrw-password") + .containsEntry("spring.r2dbc.replicated.rw.url", "testrw-r2dbc-url") + .containsEntry("spring.r2dbc.replicated.rw.username", "testrw-username") + .containsEntry("spring.r2dbc.replicated.ro.password", "testro-password") + .containsEntry("spring.r2dbc.replicated.ro.url", "testro-r2dbc-url") + .containsEntry("spring.r2dbc.replicated.ro.username", "testro-username") + .containsEntry("spring.r2dbc.replicated.name", "dbrw"); + } + + @Test + @DisplayName("can be disabled") + void disabled() { + Bindings bindings = new Bindings( + new Binding("dbrw", Paths.get("dbrw-path"), secret) + ); + environment.setProperty("org.springframework.cloud.bindings.boot.sqlserver-replicated.enable", "false"); + + new SqlServerReplicatedBindingsPropertiesProcessor().process(environment, bindings, properties); + + assertThat(properties).isEmpty(); + } +} From 18aa966cec63ea791a5014baad7a0117d876064d Mon Sep 17 00:00:00 2001 From: meyerg Date: Wed, 28 Feb 2024 08:56:20 -0600 Subject: [PATCH 4/6] Adding new processors to spring.factories file. Also updated Java doc and tagged all new processors as "final" --- ...PostgreSQLBindingsPropertiesProcessor.java | 9 +++++ .../boot/AbstractReplicatedDataSource.java | 38 +++++++++++++++++++ ...ReplicatedBindingsPropertiesProcessor.java | 2 +- ...ReplicatedBindingsPropertiesProcessor.java | 2 +- ...ReplicatedBindingsPropertiesProcessor.java | 2 +- ...ReplicatedBindingsPropertiesProcessor.java | 21 +++++++++- ...ReplicatedBindingsPropertiesProcessor.java | 2 +- ...ReplicatedBindingsPropertiesProcessor.java | 2 +- .../main/resources/META-INF/spring.factories | 12 ++++++ ...gSpecificEnvironmentPostProcessorTest.java | 2 +- 10 files changed, 85 insertions(+), 7 deletions(-) diff --git a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/AbstractPostgreSQLBindingsPropertiesProcessor.java b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/AbstractPostgreSQLBindingsPropertiesProcessor.java index 4b19a72..4c74a97 100644 --- a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/AbstractPostgreSQLBindingsPropertiesProcessor.java +++ b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/AbstractPostgreSQLBindingsPropertiesProcessor.java @@ -87,16 +87,25 @@ protected String buildSslModeParam(Binding binding) { return sslparam.toString(); } + /** + * name of the options field; override in concrete classes is field name is different + */ protected String getDBOptionSecretField() { return OPTIONS; } + /** + * name of the ssl mode field; override in concrete classes is field name is different + */ protected String getSSLModeSecretField() { return SSL_MODE; } + /** + * name of the ssl root cert field; override in concrete classes is field name is different + */ protected String getSSLRootCertSecretField() { return SSL_ROOT_CERT; diff --git a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/AbstractReplicatedDataSource.java b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/AbstractReplicatedDataSource.java index dd836a5..e379d18 100644 --- a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/AbstractReplicatedDataSource.java +++ b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/AbstractReplicatedDataSource.java @@ -8,18 +8,39 @@ import org.springframework.cloud.bindings.Bindings; import org.springframework.core.env.Environment; +/** + * Abstract class for replicated datasources processors that implement a common properties pattern. + */ public abstract class AbstractReplicatedDataSource implements BindingsPropertiesProcessor { + /** + * Read/write function field + */ public static final String RW_FUNCTION = "rw"; + /** + * Read only function field + */ public static final String RO_FUNCTION = "ro"; + /** + * Template for a base JDBC properties + */ public static final String JDBC_BASE_TEMPLATE = "spring.datasource.replicated.%s"; + /** + * Template for a base R2DBC properties + */ public static final String R2DBC_BASE_TEMPLATE = "spring.r2dbc.replicated.%s"; + /** + * Template for function based JDBC properties + */ public static final String JDBC_PROPERTY_TEMPLATE = "spring.datasource.replicated.%s.%s"; + /** + * Template for function based R2DBC properties + */ public static final String R2DBC_PROPERTY_TEMPLATE = "spring.r2dbc.replicated.%s.%s"; @Override @@ -43,18 +64,35 @@ public void process(Environment environment, Bindings bindings, Map properties, Binding binding, String fnc); + /** + * Gets the binding type of this processor. + * @return The binding type. + */ protected abstract String getType(); } diff --git a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/Db2ReplicatedBindingsPropertiesProcessor.java b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/Db2ReplicatedBindingsPropertiesProcessor.java index 8a289c6..293245c 100644 --- a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/Db2ReplicatedBindingsPropertiesProcessor.java +++ b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/Db2ReplicatedBindingsPropertiesProcessor.java @@ -26,7 +26,7 @@ /** * An implementation of {@link BindingsPropertiesProcessor} that detects {@link Binding}s of type: {@value TYPE}. */ -public class Db2ReplicatedBindingsPropertiesProcessor extends AbstractReplicatedDataSource +public final class Db2ReplicatedBindingsPropertiesProcessor extends AbstractReplicatedDataSource implements ApplicationListener { /** diff --git a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/MySqlReplicatedBindingsPropertiesProcessor.java b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/MySqlReplicatedBindingsPropertiesProcessor.java index b5eaf0d..7f34352 100644 --- a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/MySqlReplicatedBindingsPropertiesProcessor.java +++ b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/MySqlReplicatedBindingsPropertiesProcessor.java @@ -10,7 +10,7 @@ /** * An implementation of {@link BindingsPropertiesProcessor} that detects {@link Binding}s of type: {@value TYPE}. */ -public class MySqlReplicatedBindingsPropertiesProcessor extends AbstractReplicatedDataSource +public final class MySqlReplicatedBindingsPropertiesProcessor extends AbstractReplicatedDataSource implements ApplicationListener { /** diff --git a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/OracleReplicatedBindingsPropertiesProcessor.java b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/OracleReplicatedBindingsPropertiesProcessor.java index 860cfa8..b861f02 100644 --- a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/OracleReplicatedBindingsPropertiesProcessor.java +++ b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/OracleReplicatedBindingsPropertiesProcessor.java @@ -10,7 +10,7 @@ /** * An implementation of {@link BindingsPropertiesProcessor} that detects {@link Binding}s of type: {@value TYPE}. */ -public class OracleReplicatedBindingsPropertiesProcessor extends AbstractReplicatedDataSource +public final class OracleReplicatedBindingsPropertiesProcessor extends AbstractReplicatedDataSource implements ApplicationListener { /** diff --git a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/PostgreSqlReplicatedBindingsPropertiesProcessor.java b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/PostgreSqlReplicatedBindingsPropertiesProcessor.java index c7d2d19..d742045 100644 --- a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/PostgreSqlReplicatedBindingsPropertiesProcessor.java +++ b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/PostgreSqlReplicatedBindingsPropertiesProcessor.java @@ -30,7 +30,8 @@ /** * An implementation of {@link BindingsPropertiesProcessor} that detects {@link Binding}s of type: {@value TYPE}. */ -public class PostgreSqlReplicatedBindingsPropertiesProcessor extends AbstractPostgreSQLBindingsPropertiesProcessor implements ApplicationListener { +public final class PostgreSqlReplicatedBindingsPropertiesProcessor extends AbstractPostgreSQLBindingsPropertiesProcessor + implements BindingsPropertiesProcessor, ApplicationListener { /** * The {@link Binding} type that this processor is interested in: {@value}. @@ -39,16 +40,34 @@ public class PostgreSqlReplicatedBindingsPropertiesProcessor extends AbstractPos private static final DeferredLog LOG = new DeferredLog(); + /** + * Read/write function field + */ private static final String RW_FUNCTION = "rw"; + /** + * Read only function field + */ private static final String RO_FUNCTION = "ro"; + /** + * Template for a base JDBC properties + */ private static final String JDBC_BASE_TEMPLATE = "spring.datasource.replicated.%s"; + /** + * Template for a base R2DBC properties + */ private static final String R2DBC_BASE_TEMPLATE = "spring.r2dbc.replicated.%s"; + /** + * Template for function based JDBC properties + */ private static final String JDBC_PROPERTY_TEMPLATE = "spring.datasource.replicated.%s.%s"; + /** + * Template for function based R2DBC properties + */ private static final String R2DBC_PROPERTY_TEMPLATE = "spring.r2dbc.replicated.%s.%s"; private String mode = RW_FUNCTION; diff --git a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/SapHanaReplicatedBindingsPropertiesProcessor.java b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/SapHanaReplicatedBindingsPropertiesProcessor.java index c5b9662..8226dea 100644 --- a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/SapHanaReplicatedBindingsPropertiesProcessor.java +++ b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/SapHanaReplicatedBindingsPropertiesProcessor.java @@ -10,7 +10,7 @@ /** * An implementation of {@link BindingsPropertiesProcessor} that detects {@link Binding}s of type: {@value TYPE}. */ -public class SapHanaReplicatedBindingsPropertiesProcessor extends AbstractReplicatedDataSource +public final class SapHanaReplicatedBindingsPropertiesProcessor extends AbstractReplicatedDataSource implements ApplicationListener { /** diff --git a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/SqlServerReplicatedBindingsPropertiesProcessor.java b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/SqlServerReplicatedBindingsPropertiesProcessor.java index 359713e..7d45874 100644 --- a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/SqlServerReplicatedBindingsPropertiesProcessor.java +++ b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/SqlServerReplicatedBindingsPropertiesProcessor.java @@ -10,7 +10,7 @@ /** * An implementation of {@link BindingsPropertiesProcessor} that detects {@link Binding}s of type: {@value TYPE}. */ -public class SqlServerReplicatedBindingsPropertiesProcessor extends AbstractReplicatedDataSource +public final class SqlServerReplicatedBindingsPropertiesProcessor extends AbstractReplicatedDataSource implements ApplicationListener { /** diff --git a/spring-cloud-bindings/src/main/resources/META-INF/spring.factories b/spring-cloud-bindings/src/main/resources/META-INF/spring.factories index cde7fc3..1f60ac9 100644 --- a/spring-cloud-bindings/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-bindings/src/main/resources/META-INF/spring.factories @@ -1,7 +1,13 @@ org.springframework.context.ApplicationListener=\ org.springframework.cloud.bindings.boot.BindingFlattenedEnvironmentPostProcessor, \ org.springframework.cloud.bindings.boot.BindingSpecificEnvironmentPostProcessor, \ + org.springframework.cloud.bindings.boot.Db2ReplicatedBindingsPropertiesProcessor, \ + org.springframework.cloud.bindings.boot.MySqlReplicatedBindingsPropertiesProcessor, \ + org.springframework.cloud.bindings.boot.OracleReplicatedBindingsPropertiesProcessor, \ + org.springframework.cloud.bindings.boot.PostgreSqlReplicatedBindingsPropertiesProcessor, \ + org.springframework.cloud.bindings.boot.SapHanaReplicatedBindingsPropertiesProcessor, \ org.springframework.cloud.bindings.boot.SpringSecurityOAuth2BindingsPropertiesProcessor, \ + org.springframework.cloud.bindings.boot.SqlServerReplicatedBindingsPropertiesProcessor, \ org.springframework.cloud.bindings.boot.VaultBindingsPropertiesProcessor org.springframework.boot.env.EnvironmentPostProcessor=\ org.springframework.cloud.bindings.boot.BindingFlattenedEnvironmentPostProcessor, \ @@ -13,19 +19,25 @@ org.springframework.cloud.bindings.boot.BindingsPropertiesProcessor=\ org.springframework.cloud.bindings.boot.ConfigServerBindingsPropertiesProcessor, \ org.springframework.cloud.bindings.boot.CouchbaseBindingsPropertiesProcessor, \ org.springframework.cloud.bindings.boot.Db2BindingsPropertiesProcessor, \ + org.springframework.cloud.bindings.boot.Db2ReplicatedBindingsPropertiesProcessor, \ org.springframework.cloud.bindings.boot.ElasticsearchBindingsPropertiesProcessor, \ org.springframework.cloud.bindings.boot.EurekaBindingsPropertiesProcessor, \ org.springframework.cloud.bindings.boot.KafkaBindingsPropertiesProcessor, \ org.springframework.cloud.bindings.boot.LDAPBindingsPropertiesProcessor, \ org.springframework.cloud.bindings.boot.MongoDbBindingsPropertiesProcessor, \ org.springframework.cloud.bindings.boot.MySqlBindingsPropertiesProcessor, \ + org.springframework.cloud.bindings.boot.MySqlReplicatedBindingsPropertiesProcessor, \ org.springframework.cloud.bindings.boot.Neo4JBindingsPropertiesProcessor, \ org.springframework.cloud.bindings.boot.OracleBindingsPropertiesProcessor, \ + org.springframework.cloud.bindings.boot.OracleReplicatedBindingsPropertiesProcessor, \ org.springframework.cloud.bindings.boot.PostgreSqlBindingsPropertiesProcessor, \ + org.springframework.cloud.bindings.boot.PostgreSqlReplicatedBindingsPropertiesProcessor, \ org.springframework.cloud.bindings.boot.RabbitMqBindingsPropertiesProcessor, \ org.springframework.cloud.bindings.boot.RedisBindingsPropertiesProcessor, \ org.springframework.cloud.bindings.boot.SapHanaBindingsPropertiesProcessor, \ + org.springframework.cloud.bindings.boot.SapHanaReplicatedBindingsPropertiesProcessor, \ org.springframework.cloud.bindings.boot.SpringSecurityOAuth2BindingsPropertiesProcessor, \ org.springframework.cloud.bindings.boot.SqlServerBindingsPropertiesProcessor, \ + org.springframework.cloud.bindings.boot.SqlServerReplicatedBindingsPropertiesProcessor, \ org.springframework.cloud.bindings.boot.VaultBindingsPropertiesProcessor, \ org.springframework.cloud.bindings.boot.WavefrontBindingsPropertiesProcessor diff --git a/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/BindingSpecificEnvironmentPostProcessorTest.java b/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/BindingSpecificEnvironmentPostProcessorTest.java index 85a7d7b..4646db8 100644 --- a/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/BindingSpecificEnvironmentPostProcessorTest.java +++ b/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/BindingSpecificEnvironmentPostProcessorTest.java @@ -104,7 +104,7 @@ void order() { @Test @DisplayName("included implementations are registered") void includedImplementations() { - assertThat(new BindingSpecificEnvironmentPostProcessor().processors).hasSize(21); + assertThat(new BindingSpecificEnvironmentPostProcessor().processors).hasSize(27); } } From 7bebd9b1c5b3cdb8ac476b22b42be486e0a3abd6 Mon Sep 17 00:00:00 2001 From: meyerg Date: Thu, 29 Feb 2024 11:20:37 -0600 Subject: [PATCH 5/6] Adding documentation of replicated types to README. Adding missing license text to top of new files. --- README.md | 126 ++++++++++++++++++ ...PostgreSQLBindingsPropertiesProcessor.java | 16 +++ .../boot/AbstractReplicatedDataSource.java | 16 +++ ...ReplicatedBindingsPropertiesProcessor.java | 16 +++ ...ReplicatedBindingsPropertiesProcessor.java | 16 +++ ...ReplicatedBindingsPropertiesProcessor.java | 2 +- ...ReplicatedBindingsPropertiesProcessor.java | 16 +++ ...ReplicatedBindingsPropertiesProcessor.java | 16 +++ ...icatedBindingsPropertiesProcessorTest.java | 16 +++ ...icatedBindingsPropertiesProcessorTest.java | 16 +++ ...icatedBindingsPropertiesProcessorTest.java | 16 +++ ...icatedBindingsPropertiesProcessorTest.java | 16 +++ ...icatedBindingsPropertiesProcessorTest.java | 16 +++ ...icatedBindingsPropertiesProcessorTest.java | 16 +++ 14 files changed, 319 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4f1485e..6c94216 100644 --- a/README.md +++ b/README.md @@ -116,6 +116,26 @@ Disable Property: `org.springframework.cloud.bindings.boot.db2.enable` | `spring.r2dbc.password` | `{password}` | | `spring.r2dbc.username` | `{username}` | +### Replicated DB2 RDBMS +Type: `db2-replicated` +Disable Property: `org.springframework.cloud.bindings.boot.db2-replicated.enable` + +| Property | Value | +| ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | +| `spring.datasource.replicated.rw.driver-class-name` | `com.ibm.db2.jcc.DB2Driver` | +| `spring.datasource.replicated.rw.password` | `{rw-password}` | +| `spring.datasource.replicated.rw.url` | `{rw-jdbc-url}` or if not set then `jdbc:db2://{rw-host}:{rw-port}/{rw-database}` (you must have rw-host, rw-port and rw-database set or no mapping will occur) | +| `spring.datasource.replicated.rw.username` | `{rw-username}` | +| `spring.r2dbc.replicated.rw.url` | `{rw-r2dbc-url}` or if not set then `r2dbc:db2://{rw-host}:{rw-port}/{rw-database}` (you must have rw-host, rw-port and rw-database set or no mapping will occur) | +| `spring.r2dbc.replicated.rw.password` | `{rw-password}` | +| `spring.r2dbc.replicated.rw.username` | `{rw-username}` | +| `spring.datasource.replicated.ro.driver-class-name` | `com.ibm.db2.jcc.DB2Driver` | +| `spring.datasource.replicated.ro.password` | `{ro-password}` | +| `spring.datasource.replicated.ro.url` | `{ro-jdbc-url}` or if not set then `jdbc:db2://{ro-host}:{ro-port}/{ro-database}` (you must have ro-host, ro-port and ro-database set or no mapping will occur) | +| `spring.datasource.replicated.ro.username` | `{ro-username}` | +| `spring.r2dbc.replicated.ro.url` | `{ro-r2dbc-url}` or if not set then `r2dbc:db2://{ro-host}:{ro-port}/{ro-database}` (you must have ro-host, ro-port and ro- database set or no mapping will occur) | +| `spring.r2dbc.replicated.ro.password` | `{ro-password}` | +| `spring.r2dbc.replicated.ro.username` | `{ro-username}` | ### Elasticsearch Type: `elasticsearch` @@ -180,6 +200,27 @@ Disable Property: `org.springframework.cloud.bindings.boot.mysql.enable` | `spring.r2dbc.password` | `{password}` | | `spring.r2dbc.username` | `{username}` | +### Replicated MySQL RDBMS +Type: `mysql-replicated` +Disable Property: `org.springframework.cloud.bindings.boot.mysql-replicated.enable` + +| Property | Value | +| ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | +| `spring.datasource.replicated.rw.driver-class-name` | `org.mariadb.jdbc.Driver` or `com.mysql.cj.jdbc.Driver` depending on classpath | +| `spring.datasource.replicated.rw.password` | `{rw-password}` | +| `spring.datasource.replicated.rw.url` | `{rw-jdbc-url}` or if not set then `jdbc:mysql://{rw-host}:{rw-port}/{rw-database}` or `jdbc:mariadb://{rw-host}:{rw-port}/{rw-database}` (you must have rw-host, rw-port and rw-database set or no mapping will occur) | +| `spring.datasource.replicated.rw.username` | `{rw-username}` | +| `spring.r2dbc.replicated.rw.url` | `{rw-r2dbc-url}` or if not set then `r2dbc:mysql://{rw-host}:{rw-port}/{rw-database}` or `r2dbc:mariadb//{rw-host}:{rw-port}/{rw-database}` (you must have rw-host, rw-port and rw-database set or no mapping will occur) | +| `spring.r2dbc.replicated.rw.password` | `{rw-password}` | +| `spring.r2dbc.replicated.rw.username` | `{rw-username}` | +| `spring.datasource.replicated.ro.driver-class-name` | `org.mariadb.jdbc.Driver` or `com.mysql.cj.jdbc.Driver` depending on classpath | +| `spring.datasource.replicated.ro.password` | `{ro-password}` | +| `spring.datasource.replicated.ro.url` | `{ro-jdbc-url}` or if not set then `jdbc:mysql://{ro-host}:{ro-port}/{ro-database}` or `jdbc:mariadb://{ro-host}:{ro-port}/{ro-database}` (you must have ro-host, ro-port and ro-database set or no mapping will occur) | +| `spring.datasource.replicated.ro.username` | `{ro-username}` | +| `spring.r2dbc.replicated.ro.url` | `{ro-r2dbc-url}` or if not set then `r2dbc:mysql://{ro-host}:{ro-port}/{ro-database}` or `r2dbc:mariadb//{ro-host}:{ro-port}/{ro-database}` (you must have ro-host, ro-port and ro- database set or no mapping will occur) | +| `spring.r2dbc.replicated.ro.password` | `{ro-password}` | +| `spring.r2dbc.replicated.ro.username` | `{ro-username}` | + **Note:** Libraries on the classpath are examined for the purpose of evaluating the appropriate `jdbc` and `r2dbc` URLs. The existence of both MySQL and MariaDB libraries on the classpath is not supported and may lead to non-deterministic results. ### Neo4J @@ -208,6 +249,28 @@ Disable Property: `org.springframework.cloud.bindings.boot.oracle.enable` | `spring.r2dbc.password` | `{password}` | | `spring.r2dbc.username` | `{username}` | +### Replicated Oracle RDBMS +Type: `oracle-replicated` +Disable Property: `org.springframework.cloud.bindings.boot.oracle-replicated.enable` + +| Property | Value | +| ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | +| `spring.datasource.replicated.rw.driver-class-name` | `oracle.jdbc.OracleDriver` | +| `spring.datasource.replicated.rw.password` | `{rw-password}` | +| `spring.datasource.replicated.rw.url` | `{rw-jdbc-url}` or if not set then `jdbc:oracle://{rw-host}:{rw-port}/{rw-database}` (you must have rw-host, rw-port and rw-database set or no mapping will occur) | +| `spring.datasource.replicated.rw.username` | `{rw-username}` | +| `spring.r2dbc.replicated.rw.url` | `{rw-r2dbc-url}` or if not set then `r2dbc:oracle://{rw-host}:{rw-port}/{rw-database}` (you must have rw-host, rw-port and rw-database set or no mapping will occur) | +| `spring.r2dbc.replicated.rw.password` | `{rw-password}` | +| `spring.r2dbc.replicated.rw.username` | `{rw-username}` | +| `spring.datasource.replicated.ro.driver-class-name` | `oracle.jdbc.OracleDriver` | +| `spring.datasource.replicated.ro.password` | `{ro-password}` | +| `spring.datasource.replicated.ro.url` | `{ro-jdbc-url}` or if not set then `jdbc:oracle://{ro-host}:{ro-port}/{ro-database}` (you must have ro-host, ro-port and ro-database set or no mapping will occur) | +| `spring.datasource.replicated.ro.username` | `{ro-username}` | +| `spring.r2dbc.replicated.ro.url` | `{ro-r2dbc-url}` or if not set then `r2dbc:oracle://{ro-host}:{ro-port}/{ro-database}` (you must have ro-host, ro-port and ro- database set or no mapping will occur) | +| `spring.r2dbc.replicated.ro.password` | `{ro-password}` | +| `spring.r2dbc.replicated.ro.username` | `{ro-username}` | + + ### PostgreSQL RDBMS Type: `postgresql` Disable Property: `org.springframework.cloud.bindings.boot.postgresql.enable` @@ -222,6 +285,27 @@ Disable Property: `org.springframework.cloud.bindings.boot.postgresql.enable` | `spring.r2dbc.password` | `{password}` | | `spring.r2dbc.username` | `{username}` | +### Replicated PostgreSQL RDBMS +Type: `postgresql-replicated` +Disable Property: `org.springframework.cloud.bindings.boot.postgresql-replicated.enable` + +| Property | Value | +| ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | +| `spring.datasource.replicated.rw.driver-class-name` | `org.postgresql.Driver` | +| `spring.datasource.replicated.rw.password` | `{rw-password}` | +| `spring.datasource.replicated.rw.url` | `{rw-jdbc-url}` or if not set then `jdbc:postgresql://{rw-host}:{rw-port}/{rw-database}` (you must have rw-host, rw-port and rw-database set or no mapping will occur) | +| `spring.datasource.replicated.rw.username` | `{rw-username}` | +| `spring.r2dbc.replicated.rw.url` | `{rw-r2dbc-url}` or if not set then `r2dbc:postgresql://{rw-host}:{rw-port}/{rw-database}` (you must have rw-host, rw-port and rw-database set or no mapping will occur) | +| `spring.r2dbc.replicated.rw.password` | `{rw-password}` | +| `spring.r2dbc.replicated.rw.username` | `{rw-username}` | +| `spring.datasource.replicated.ro.driver-class-name` | `org.postgresql.Driver` | +| `spring.datasource.replicated.ro.password` | `{ro-password}` | +| `spring.datasource.replicated.ro.url` | `{ro-jdbc-url}` or if not set then `jdbc:postgresql://{ro-host}:{ro-port}/{ro-database}` (you must have ro-host, ro-port and ro-database set or no mapping will occur) | +| `spring.datasource.replicated.ro.username` | `{ro-username}` | +| `spring.r2dbc.replicated.ro.url` | `{ro-r2dbc-url}` or if not set then `r2dbc:postgresql://{ro-host}:{ro-port}/{ro-database}` (you must have ro-host, ro-port and ro- database set or no mapping will occur) | +| `spring.r2dbc.replicated.ro.password` | `{ro-password}` | +| `spring.r2dbc.replicated.ro.username` | `{ro-username}` | + ### RabbitMQ Type: `rabbitmq` Disable Property: `org.springframework.cloud.bindings.boot.rabbitmq.enable` @@ -269,6 +353,27 @@ Disable Property: `org.springframework.cloud.bindings.boot.hana.enable` | `spring.r2dbc.password` | `{password}` | | `spring.r2dbc.username` | `{username}` | +### Replicated SAP Hana RDBMS +Type: `hana-replicated` +Disable Property: `org.springframework.cloud.bindings.boot.hana-replicated.enable` + +| Property | Value | +| ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | +| `spring.datasource.replicated.rw.driver-class-name` | `com.sap.db.jdbc.Driver` | +| `spring.datasource.replicated.rw.password` | `{rw-password}` | +| `spring.datasource.replicated.rw.url` | `{rw-jdbc-url}` or if not set then `jdbc:sap://{rw-host}:{rw-port}/{rw-database}` (you must have rw-host, rw-port and rw-database set or no mapping will occur) | +| `spring.datasource.replicated.rw.username` | `{rw-username}` | +| `spring.r2dbc.replicated.rw.url` | `{rw-r2dbc-url}` or if not set then `r2dbc:sap://{rw-host}:{rw-port}/{rw-database}` (you must have rw-host, rw-port and rw-database set or no mapping will occur) | +| `spring.r2dbc.replicated.rw.password` | `{rw-password}` | +| `spring.r2dbc.replicated.rw.username` | `{rw-username}` | +| `spring.datasource.replicated.ro.driver-class-name` | `com.sap.db.jdbc.Driver` | +| `spring.datasource.replicated.ro.password` | `{ro-password}` | +| `spring.datasource.replicated.ro.url` | `{ro-jdbc-url}` or if not set then `jdbc:sap://{ro-host}:{ro-port}/{ro-database}` (you must have ro-host, ro-port and ro-database set or no mapping will occur) | +| `spring.datasource.replicated.ro.username` | `{ro-username}` | +| `spring.r2dbc.replicated.ro.url` | `{ro-r2dbc-url}` or if not set then `r2dbc:sap://{ro-host}:{ro-port}/{ro-database}` (you must have ro-host, ro-port and ro- database set or no mapping will occur) | +| `spring.r2dbc.replicated.ro.password` | `{ro-password}` | +| `spring.r2dbc.replicated.ro.username` | `{ro-username}` | + ## SCS Config Server Type: `config` Disable Property: `org.springframework.cloud.bindings.boot.config.enable` @@ -342,6 +447,27 @@ Disable Property: `org.springframework.cloud.bindings.boot.sqlserver.enable` | `spring.r2dbc.password` | `{password}` | | `spring.r2dbc.username` | `{username}` | +### Replicated SQLServer RDBMS +Type: `sqlserver-replicated` +Disable Property: `org.springframework.cloud.bindings.boot.sqlserver-replicated.enable` + +| Property | Value | +| ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | +| `spring.datasource.replicated.rw.driver-class-name` | `com.microsoft.sqlserver.jdbc.SQLServerDriver` | +| `spring.datasource.replicated.rw.password` | `{rw-password}` | +| `spring.datasource.replicated.rw.url` | `{rw-jdbc-url}` or if not set then `jdbc:sqlserver://{rw-host}:{rw-port}/{rw-database}` (you must have rw-host, rw-port and rw-database set or no mapping will occur) | +| `spring.datasource.replicated.rw.username` | `{rw-username}` | +| `spring.r2dbc.replicated.rw.url` | `{rw-r2dbc-url}` or if not set then `r2dbc:sqlserver://{rw-host}:{rw-port}/{rw-database}` (you must have rw-host, rw-port and rw-database set or no mapping will occur) | +| `spring.r2dbc.replicated.rw.password` | `{rw-password}` | +| `spring.r2dbc.replicated.rw.username` | `{rw-username}` | +| `spring.datasource.replicated.ro.driver-class-name` | `com.microsoft.sqlserver.jdbc.SQLServerDriver` | +| `spring.datasource.replicated.ro.password` | `{ro-password}` | +| `spring.datasource.replicated.ro.url` | `{ro-jdbc-url}` or if not set then `jdbc:sqlserver://{ro-host}:{ro-port}/{ro-database}` (you must have ro-host, ro-port and ro-database set or no mapping will occur) | +| `spring.datasource.replicated.ro.username` | `{ro-username}` | +| `spring.r2dbc.replicated.ro.url` | `{ro-r2dbc-url}` or if not set then `r2dbc:sqlserver://{ro-host}:{ro-port}/{ro-database}` (you must have ro-host, ro-port and ro- database set or no mapping will occur) | +| `spring.r2dbc.replicated.ro.password` | `{ro-password}` | +| `spring.r2dbc.replicated.ro.username` | `{ro-username}` | + ### Vault Type: `vault` diff --git a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/AbstractPostgreSQLBindingsPropertiesProcessor.java b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/AbstractPostgreSQLBindingsPropertiesProcessor.java index 4c74a97..9ec7ace 100644 --- a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/AbstractPostgreSQLBindingsPropertiesProcessor.java +++ b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/AbstractPostgreSQLBindingsPropertiesProcessor.java @@ -1,3 +1,19 @@ +/* + * Copyright 2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.cloud.bindings.boot; import java.nio.file.FileSystems; diff --git a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/AbstractReplicatedDataSource.java b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/AbstractReplicatedDataSource.java index e379d18..3de1ce8 100644 --- a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/AbstractReplicatedDataSource.java +++ b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/AbstractReplicatedDataSource.java @@ -1,3 +1,19 @@ +/* + * Copyright 2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.cloud.bindings.boot; import static org.springframework.cloud.bindings.boot.Guards.isTypeEnabled; diff --git a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/MySqlReplicatedBindingsPropertiesProcessor.java b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/MySqlReplicatedBindingsPropertiesProcessor.java index 7f34352..3937fe5 100644 --- a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/MySqlReplicatedBindingsPropertiesProcessor.java +++ b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/MySqlReplicatedBindingsPropertiesProcessor.java @@ -1,3 +1,19 @@ +/* + * Copyright 2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.cloud.bindings.boot; import java.util.Map; diff --git a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/OracleReplicatedBindingsPropertiesProcessor.java b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/OracleReplicatedBindingsPropertiesProcessor.java index b861f02..6f272d8 100644 --- a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/OracleReplicatedBindingsPropertiesProcessor.java +++ b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/OracleReplicatedBindingsPropertiesProcessor.java @@ -1,3 +1,19 @@ +/* + * Copyright 2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.cloud.bindings.boot; import java.util.Map; diff --git a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/PostgreSqlReplicatedBindingsPropertiesProcessor.java b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/PostgreSqlReplicatedBindingsPropertiesProcessor.java index d742045..eb9bb6a 100644 --- a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/PostgreSqlReplicatedBindingsPropertiesProcessor.java +++ b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/PostgreSqlReplicatedBindingsPropertiesProcessor.java @@ -11,7 +11,7 @@ * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and - * limitations under the License. + * limitations under the License. */ package org.springframework.cloud.bindings.boot; diff --git a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/SapHanaReplicatedBindingsPropertiesProcessor.java b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/SapHanaReplicatedBindingsPropertiesProcessor.java index 8226dea..ab49700 100644 --- a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/SapHanaReplicatedBindingsPropertiesProcessor.java +++ b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/SapHanaReplicatedBindingsPropertiesProcessor.java @@ -1,3 +1,19 @@ +/* + * Copyright 2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.cloud.bindings.boot; import java.util.Map; diff --git a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/SqlServerReplicatedBindingsPropertiesProcessor.java b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/SqlServerReplicatedBindingsPropertiesProcessor.java index 7d45874..53a95e7 100644 --- a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/SqlServerReplicatedBindingsPropertiesProcessor.java +++ b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/SqlServerReplicatedBindingsPropertiesProcessor.java @@ -1,3 +1,19 @@ +/* + * Copyright 2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.cloud.bindings.boot; import java.util.Map; diff --git a/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/Db2ReplicatedBindingsPropertiesProcessorTest.java b/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/Db2ReplicatedBindingsPropertiesProcessorTest.java index 86045a0..cd4b356 100644 --- a/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/Db2ReplicatedBindingsPropertiesProcessorTest.java +++ b/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/Db2ReplicatedBindingsPropertiesProcessorTest.java @@ -1,3 +1,19 @@ +/* + * Copyright 2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.cloud.bindings.boot; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/MySqlReplicatedBindingsPropertiesProcessorTest.java b/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/MySqlReplicatedBindingsPropertiesProcessorTest.java index 698f5e2..f86ab23 100644 --- a/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/MySqlReplicatedBindingsPropertiesProcessorTest.java +++ b/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/MySqlReplicatedBindingsPropertiesProcessorTest.java @@ -1,3 +1,19 @@ +/* + * Copyright 2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.cloud.bindings.boot; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/OracleReplicatedBindingsPropertiesProcessorTest.java b/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/OracleReplicatedBindingsPropertiesProcessorTest.java index f562587..856f7e5 100644 --- a/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/OracleReplicatedBindingsPropertiesProcessorTest.java +++ b/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/OracleReplicatedBindingsPropertiesProcessorTest.java @@ -1,3 +1,19 @@ +/* + * Copyright 2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.cloud.bindings.boot; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/PostgreSqlReplicatedBindingsPropertiesProcessorTest.java b/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/PostgreSqlReplicatedBindingsPropertiesProcessorTest.java index afce0ba..f3770a6 100644 --- a/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/PostgreSqlReplicatedBindingsPropertiesProcessorTest.java +++ b/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/PostgreSqlReplicatedBindingsPropertiesProcessorTest.java @@ -1,3 +1,19 @@ +/* + * Copyright 2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.cloud.bindings.boot; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/SapHanaReplicatedBindingsPropertiesProcessorTest.java b/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/SapHanaReplicatedBindingsPropertiesProcessorTest.java index a771752..439cef8 100644 --- a/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/SapHanaReplicatedBindingsPropertiesProcessorTest.java +++ b/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/SapHanaReplicatedBindingsPropertiesProcessorTest.java @@ -1,3 +1,19 @@ +/* + * Copyright 2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.cloud.bindings.boot; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/SqlServerReplicatedBindingsPropertiesProcessorTest.java b/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/SqlServerReplicatedBindingsPropertiesProcessorTest.java index 6f356e5..128128d 100644 --- a/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/SqlServerReplicatedBindingsPropertiesProcessorTest.java +++ b/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/SqlServerReplicatedBindingsPropertiesProcessorTest.java @@ -1,3 +1,19 @@ +/* + * Copyright 2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.cloud.bindings.boot; import static org.assertj.core.api.Assertions.assertThat; From 8153d4f5d7dbc15c06e8cb4e04e62fa214c9599f Mon Sep 17 00:00:00 2001 From: meyerg Date: Thu, 29 Feb 2024 11:25:12 -0600 Subject: [PATCH 6/6] Updating copywrite on new files from 2020 to 2024. --- .../boot/AbstractPostgreSQLBindingsPropertiesProcessor.java | 2 +- .../cloud/bindings/boot/AbstractReplicatedDataSource.java | 2 +- .../bindings/boot/Db2ReplicatedBindingsPropertiesProcessor.java | 2 +- .../boot/MySqlReplicatedBindingsPropertiesProcessor.java | 2 +- .../boot/OracleReplicatedBindingsPropertiesProcessor.java | 2 +- .../boot/PostgreSqlReplicatedBindingsPropertiesProcessor.java | 2 +- .../boot/SapHanaReplicatedBindingsPropertiesProcessor.java | 2 +- .../boot/SqlServerReplicatedBindingsPropertiesProcessor.java | 2 +- .../boot/Db2ReplicatedBindingsPropertiesProcessorTest.java | 2 +- .../boot/MySqlReplicatedBindingsPropertiesProcessorTest.java | 2 +- .../boot/OracleReplicatedBindingsPropertiesProcessorTest.java | 2 +- .../PostgreSqlReplicatedBindingsPropertiesProcessorTest.java | 2 +- .../boot/SapHanaReplicatedBindingsPropertiesProcessorTest.java | 2 +- .../SqlServerReplicatedBindingsPropertiesProcessorTest.java | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/AbstractPostgreSQLBindingsPropertiesProcessor.java b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/AbstractPostgreSQLBindingsPropertiesProcessor.java index 9ec7ace..77b3a86 100644 --- a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/AbstractPostgreSQLBindingsPropertiesProcessor.java +++ b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/AbstractPostgreSQLBindingsPropertiesProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 the original author or authors. + * Copyright 2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/AbstractReplicatedDataSource.java b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/AbstractReplicatedDataSource.java index 3de1ce8..4168eb7 100644 --- a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/AbstractReplicatedDataSource.java +++ b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/AbstractReplicatedDataSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 the original author or authors. + * Copyright 2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/Db2ReplicatedBindingsPropertiesProcessor.java b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/Db2ReplicatedBindingsPropertiesProcessor.java index 293245c..7580f73 100644 --- a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/Db2ReplicatedBindingsPropertiesProcessor.java +++ b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/Db2ReplicatedBindingsPropertiesProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 the original author or authors. + * Copyright 2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/MySqlReplicatedBindingsPropertiesProcessor.java b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/MySqlReplicatedBindingsPropertiesProcessor.java index 3937fe5..410ca1e 100644 --- a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/MySqlReplicatedBindingsPropertiesProcessor.java +++ b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/MySqlReplicatedBindingsPropertiesProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 the original author or authors. + * Copyright 2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/OracleReplicatedBindingsPropertiesProcessor.java b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/OracleReplicatedBindingsPropertiesProcessor.java index 6f272d8..c823963 100644 --- a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/OracleReplicatedBindingsPropertiesProcessor.java +++ b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/OracleReplicatedBindingsPropertiesProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 the original author or authors. + * Copyright 2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/PostgreSqlReplicatedBindingsPropertiesProcessor.java b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/PostgreSqlReplicatedBindingsPropertiesProcessor.java index eb9bb6a..fbf3b2e 100644 --- a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/PostgreSqlReplicatedBindingsPropertiesProcessor.java +++ b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/PostgreSqlReplicatedBindingsPropertiesProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 the original author or authors. + * Copyright 2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/SapHanaReplicatedBindingsPropertiesProcessor.java b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/SapHanaReplicatedBindingsPropertiesProcessor.java index ab49700..b2bce25 100644 --- a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/SapHanaReplicatedBindingsPropertiesProcessor.java +++ b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/SapHanaReplicatedBindingsPropertiesProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 the original author or authors. + * Copyright 2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/SqlServerReplicatedBindingsPropertiesProcessor.java b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/SqlServerReplicatedBindingsPropertiesProcessor.java index 53a95e7..d096bf8 100644 --- a/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/SqlServerReplicatedBindingsPropertiesProcessor.java +++ b/spring-cloud-bindings/src/main/java/org/springframework/cloud/bindings/boot/SqlServerReplicatedBindingsPropertiesProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 the original author or authors. + * Copyright 2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/Db2ReplicatedBindingsPropertiesProcessorTest.java b/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/Db2ReplicatedBindingsPropertiesProcessorTest.java index cd4b356..e830ac7 100644 --- a/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/Db2ReplicatedBindingsPropertiesProcessorTest.java +++ b/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/Db2ReplicatedBindingsPropertiesProcessorTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 the original author or authors. + * Copyright 2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/MySqlReplicatedBindingsPropertiesProcessorTest.java b/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/MySqlReplicatedBindingsPropertiesProcessorTest.java index f86ab23..8573231 100644 --- a/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/MySqlReplicatedBindingsPropertiesProcessorTest.java +++ b/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/MySqlReplicatedBindingsPropertiesProcessorTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 the original author or authors. + * Copyright 2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/OracleReplicatedBindingsPropertiesProcessorTest.java b/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/OracleReplicatedBindingsPropertiesProcessorTest.java index 856f7e5..ec9a3dc 100644 --- a/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/OracleReplicatedBindingsPropertiesProcessorTest.java +++ b/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/OracleReplicatedBindingsPropertiesProcessorTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 the original author or authors. + * Copyright 2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/PostgreSqlReplicatedBindingsPropertiesProcessorTest.java b/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/PostgreSqlReplicatedBindingsPropertiesProcessorTest.java index f3770a6..af71f84 100644 --- a/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/PostgreSqlReplicatedBindingsPropertiesProcessorTest.java +++ b/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/PostgreSqlReplicatedBindingsPropertiesProcessorTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 the original author or authors. + * Copyright 2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/SapHanaReplicatedBindingsPropertiesProcessorTest.java b/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/SapHanaReplicatedBindingsPropertiesProcessorTest.java index 439cef8..1c6e2ce 100644 --- a/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/SapHanaReplicatedBindingsPropertiesProcessorTest.java +++ b/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/SapHanaReplicatedBindingsPropertiesProcessorTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 the original author or authors. + * Copyright 2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/SqlServerReplicatedBindingsPropertiesProcessorTest.java b/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/SqlServerReplicatedBindingsPropertiesProcessorTest.java index 128128d..287a65c 100644 --- a/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/SqlServerReplicatedBindingsPropertiesProcessorTest.java +++ b/spring-cloud-bindings/src/test/java/org/springframework/cloud/bindings/boot/SqlServerReplicatedBindingsPropertiesProcessorTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 the original author or authors. + * Copyright 2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License.