Skip to content

Commit

Permalink
Merge pull request #25657 from tsegismont/service_binding_reactive_sq…
Browse files Browse the repository at this point in the history
…l_clients

K8S service binding support for Reactive SQL Clients
  • Loading branch information
geoand authored May 20, 2022
2 parents d6b5389 + af60d0b commit 8022fe5
Show file tree
Hide file tree
Showing 42 changed files with 758 additions and 154 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import java.util.List;
import java.util.Optional;

import io.quarkus.kubernetes.service.binding.runtime.JdbcDatasourceUtil;
import io.quarkus.kubernetes.service.binding.runtime.DatasourceServiceBindingConfigSourceFactory;
import io.quarkus.kubernetes.service.binding.runtime.ServiceBinding;
import io.quarkus.kubernetes.service.binding.runtime.ServiceBindingConfigSource;
import io.quarkus.kubernetes.service.binding.runtime.ServiceBindingConverter;
Expand All @@ -12,6 +12,7 @@ public class DB2ServiceBindingConverter implements ServiceBindingConverter {

@Override
public Optional<ServiceBindingConfigSource> convert(List<ServiceBinding> serviceBindings) {
return JdbcDatasourceUtil.convert(serviceBindings, "db2");
return ServiceBinding.singleMatchingByType("db2", serviceBindings)
.map(new DatasourceServiceBindingConfigSourceFactory.Jdbc());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import java.util.List;
import java.util.Optional;

import io.quarkus.kubernetes.service.binding.runtime.JdbcDatasourceUtil;
import io.quarkus.kubernetes.service.binding.runtime.DatasourceServiceBindingConfigSourceFactory;
import io.quarkus.kubernetes.service.binding.runtime.ServiceBinding;
import io.quarkus.kubernetes.service.binding.runtime.ServiceBindingConfigSource;
import io.quarkus.kubernetes.service.binding.runtime.ServiceBindingConverter;
Expand All @@ -12,6 +12,15 @@ public class MariaDBServiceBindingConverter implements ServiceBindingConverter {

@Override
public Optional<ServiceBindingConfigSource> convert(List<ServiceBinding> serviceBindings) {
return JdbcDatasourceUtil.convert(serviceBindings, "mysql", "mariadb");
return ServiceBinding.singleMatchingByType("mysql", serviceBindings)
.map(new MariaDBDatasourceServiceBindingConfigSourceFactory());
}

private static class MariaDBDatasourceServiceBindingConfigSourceFactory
extends DatasourceServiceBindingConfigSourceFactory.Jdbc {
@Override
protected String formatUrl(String urlFormat, String type, String host, String database, String portPart) {
return super.formatUrl(urlFormat, "mariadb", host, database, portPart);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import java.util.List;
import java.util.Optional;

import io.quarkus.kubernetes.service.binding.runtime.JdbcDatasourceUtil;
import io.quarkus.kubernetes.service.binding.runtime.DatasourceServiceBindingConfigSourceFactory;
import io.quarkus.kubernetes.service.binding.runtime.ServiceBinding;
import io.quarkus.kubernetes.service.binding.runtime.ServiceBindingConfigSource;
import io.quarkus.kubernetes.service.binding.runtime.ServiceBindingConverter;
Expand All @@ -12,6 +12,7 @@ public class MsSQLServiceBindingConverter implements ServiceBindingConverter {

@Override
public Optional<ServiceBindingConfigSource> convert(List<ServiceBinding> serviceBindings) {
return JdbcDatasourceUtil.convert(serviceBindings, "sqlserver");
return ServiceBinding.singleMatchingByType("sqlserver", serviceBindings)
.map(new DatasourceServiceBindingConfigSourceFactory.Jdbc());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import java.util.List;
import java.util.Optional;

import io.quarkus.kubernetes.service.binding.runtime.JdbcDatasourceUtil;
import io.quarkus.kubernetes.service.binding.runtime.DatasourceServiceBindingConfigSourceFactory;
import io.quarkus.kubernetes.service.binding.runtime.ServiceBinding;
import io.quarkus.kubernetes.service.binding.runtime.ServiceBindingConfigSource;
import io.quarkus.kubernetes.service.binding.runtime.ServiceBindingConverter;
Expand All @@ -12,6 +12,7 @@ public class MySQLServiceBindingConverter implements ServiceBindingConverter {

@Override
public Optional<ServiceBindingConfigSource> convert(List<ServiceBinding> serviceBindings) {
return JdbcDatasourceUtil.convert(serviceBindings, "mysql");
return ServiceBinding.singleMatchingByType("mysql", serviceBindings)
.map(new DatasourceServiceBindingConfigSourceFactory.Jdbc());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import java.util.List;
import java.util.Optional;

import io.quarkus.kubernetes.service.binding.runtime.JdbcDatasourceUtil;
import io.quarkus.kubernetes.service.binding.runtime.DatasourceServiceBindingConfigSourceFactory;
import io.quarkus.kubernetes.service.binding.runtime.ServiceBinding;
import io.quarkus.kubernetes.service.binding.runtime.ServiceBindingConfigSource;
import io.quarkus.kubernetes.service.binding.runtime.ServiceBindingConverter;
Expand All @@ -12,7 +12,8 @@ public class OracleServiceBindingConverter implements ServiceBindingConverter {

@Override
public Optional<ServiceBindingConfigSource> convert(List<ServiceBinding> serviceBindings) {
return JdbcDatasourceUtil.convert(serviceBindings, "oracle");
return ServiceBinding.singleMatchingByType("oracle", serviceBindings)
.map(new DatasourceServiceBindingConfigSourceFactory.Jdbc());
}

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package io.quarkus.jdbc.postgresql.runtime;

import static io.quarkus.kubernetes.service.binding.runtime.JdbcDatasourceUtil.QUARKUS_DATASOURCE_JDBC_URL;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
Expand All @@ -11,7 +9,7 @@
import java.util.Map;
import java.util.Optional;

import io.quarkus.kubernetes.service.binding.runtime.JdbcDatasourceUtil;
import io.quarkus.kubernetes.service.binding.runtime.DatasourceServiceBindingConfigSourceFactory;
import io.quarkus.kubernetes.service.binding.runtime.ServiceBinding;
import io.quarkus.kubernetes.service.binding.runtime.ServiceBindingConfigSource;
import io.quarkus.kubernetes.service.binding.runtime.ServiceBindingConverter;
Expand All @@ -25,83 +23,89 @@ public class PostgreSQLServiceBindingConverter implements ServiceBindingConverte

@Override
public Optional<ServiceBindingConfigSource> convert(List<ServiceBinding> serviceBindings) {
return ServiceBinding.singleMatchingByType(BINDING_TYPE, serviceBindings)
.map(new PostgreSQLDatasourceServiceBindingConfigSourceFactory());
}

Optional<ServiceBinding> matchingByType = ServiceBinding.singleMatchingByType(BINDING_TYPE, serviceBindings);
if (!matchingByType.isPresent()) {
return Optional.empty();
}
ServiceBinding binding = matchingByType.get();

Map<String, String> properties = JdbcDatasourceUtil.getServiceBindingProperties(binding, BINDING_TYPE);
//process ssl params
//https://www.postgresql.org/docs/14/libpq-connect.html
StringBuilder sslparam = new StringBuilder();
String sslmode = binding.getProperties().getOrDefault(SSL_MODE, "");
String sslRootCert = binding.getProperties().getOrDefault(SSL_ROOT_CERT, "");
if (!"".equals(sslmode)) {
sslparam.append(SSL_MODE).append("=").append(sslmode);
}
if (!"".equals(sslRootCert)) {
private static class PostgreSQLDatasourceServiceBindingConfigSourceFactory
extends DatasourceServiceBindingConfigSourceFactory.Jdbc {

@Override
protected String formatUrl(String urlFormat, String type, String host, String database, String portPart) {
String result = super.formatUrl(urlFormat, type, host, database, portPart);

Map<String, String> sbProps = serviceBinding.getProperties();

//process ssl params
//https://www.postgresql.org/docs/14/libpq-connect.html
StringBuilder sslparam = new StringBuilder();
String sslmode = sbProps.getOrDefault(SSL_MODE, "");
String sslRootCert = sbProps.getOrDefault(SSL_ROOT_CERT, "");
if (!"".equals(sslmode)) {
sslparam.append("&");
sslparam.append(SSL_MODE).append("=").append(sslmode);
}
if (!"".equals(sslRootCert)) {
if (!"".equals(sslmode)) {
sslparam.append("&");
}
sslparam.append(SSL_ROOT_CERT).append("=")
.append(serviceBinding.getBindingDirectory()).append(FileSystems.getDefault().getSeparator())
.append(sslRootCert);
}
sslparam.append(SSL_ROOT_CERT).append("=")
.append(binding.getBindingDirectory()).append(FileSystems.getDefault().getSeparator())
.append(sslRootCert);
}

//cockroachdb cloud uses options parameter to pass in the cluster routing-id
//https://www.cockroachlabs.com/docs/v21.2/connection-parameters#additional-connection-parameters
String options = binding.getProperties().getOrDefault(OPTIONS, "");
String crdbOption = "";
List<String> postgreOptions = 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;
//cockroachdb cloud uses options parameter to pass in the cluster routing-id
//https://www.cockroachlabs.com/docs/v21.2/connection-parameters#additional-connection-parameters
String options = sbProps.getOrDefault(OPTIONS, "");
String crdbOption = "";
List<String> postgreOptions = 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 {
postgreOptions.add("-c " + keyval[0] + "=" + keyval[1]);
}
}
if (keyval[0].equals("--cluster")) {
crdbOption = keyval[0] + "=" + keyval[1];
}

String combinedOptions = crdbOption;
if (postgreOptions.size() > 0) {
String otherOpts = String.join(" ", postgreOptions);
if (!combinedOptions.equals("")) {
combinedOptions = combinedOptions + " " + otherOpts;
} else {
postgreOptions.add("-c " + keyval[0] + "=" + keyval[1]);
combinedOptions = otherOpts;
}
}
}

String combinedOptions = crdbOption;
if (postgreOptions.size() > 0) {
String otherOpts = String.join(" ", postgreOptions);
if (!combinedOptions.equals("")) {
combinedOptions = combinedOptions + " " + otherOpts;
} else {
combinedOptions = otherOpts;
try {
combinedOptions = combinedOptions.length() > 0 ? OPTIONS + "=" + encode(combinedOptions).replace("+", "%20")
: "";
} catch (UnsupportedEncodingException e) {
throw new IllegalArgumentException("failed to encode options params" + options, e);
}
}

try {
combinedOptions = combinedOptions.length() > 0 ? OPTIONS + "=" + encode(combinedOptions).replace("+", "%20") : "";
} catch (UnsupportedEncodingException e) {
throw new IllegalArgumentException("failed to encode options params" + options, e);
}
if (sslparam.length() > 0 && !combinedOptions.equals("")) {
combinedOptions = sslparam + "&" + combinedOptions;
} else if (sslparam.length() > 0) {
combinedOptions = sslparam.toString();
}

if (sslparam.length() > 0 && !combinedOptions.equals("")) {
combinedOptions = sslparam + "&" + combinedOptions;
} else if (sslparam.length() > 0) {
combinedOptions = sslparam.toString();
}
if (!"".equals(combinedOptions)) {
//append sslmode and options to the URL
result += "?" + combinedOptions;
}

if (!"".equals(combinedOptions)) {
//append sslmode and options to the URL
properties.put(QUARKUS_DATASOURCE_JDBC_URL,
properties.get(QUARKUS_DATASOURCE_JDBC_URL) + "?" + combinedOptions);
return result;
}

return Optional.of(new ServiceBindingConfigSource(BINDING_TYPE + "-k8s-service-binding-source", properties));
}

private String encode(String str) throws UnsupportedEncodingException {
return URLEncoder.encode(str, StandardCharsets.UTF_8.toString());
private String encode(String str) throws UnsupportedEncodingException {
return URLEncoder.encode(str, StandardCharsets.UTF_8.toString());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package io.quarkus.kubernetes.service.binding.runtime;

import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

import org.jboss.logging.Logger;

public abstract class DatasourceServiceBindingConfigSourceFactory
implements Function<ServiceBinding, ServiceBindingConfigSource> {

private static final Logger log = Logger.getLogger(DatasourceServiceBindingConfigSourceFactory.class);

private final String configSourceNamePrefix;
private final String urlPropertyName;
private final String urlFormat;
protected ServiceBinding serviceBinding;

private DatasourceServiceBindingConfigSourceFactory(String configSourceNamePrefix, String urlPropertyName,
String urlFormat) {
this.configSourceNamePrefix = configSourceNamePrefix;
this.urlPropertyName = urlPropertyName;
this.urlFormat = urlFormat;
}

@Override
public final ServiceBindingConfigSource apply(ServiceBinding serviceBinding) {
this.serviceBinding = serviceBinding;
String name = configSourceNamePrefix + "-" + serviceBinding.getType() + "-k8s-service-binding-source";
return new ServiceBindingConfigSource(name, getServiceBindingProperties());
}

private Map<String, String> getServiceBindingProperties() {
Map<String, String> properties = new HashMap<>();
Map<String, String> bindingProperties = serviceBinding.getProperties();

String username = bindingProperties.get("username");
if (username != null) {
properties.put("quarkus.datasource.username", username);
} else {
log.debugf("Property 'username' was not found for datasource of type %s", serviceBinding.getType());
}
String password = bindingProperties.get("password");
if (password != null) {
properties.put("quarkus.datasource.password", password);
} else {
log.debugf("Property 'password' was not found for datasource of type %s", serviceBinding.getType());
}

String host = bindingProperties.get("host");
String port = bindingProperties.get("port");
String database = bindingProperties.get("database");
if ((host != null) && (database != null)) {
String portPart = "";
if (port != null) {
portPart = ":" + port;
}
properties.put(urlPropertyName, formatUrl(urlFormat, serviceBinding.getType(), host, database, portPart));
} else {
log.debugf("One or more of 'host' or 'database' properties were not found for datasource of type %s",
serviceBinding.getType());
}

return properties;
}

protected String formatUrl(String urlFormat, String type, String host, String database, String portPart) {
return String.format(urlFormat, type, host, portPart, database);
}

public static class Jdbc extends DatasourceServiceBindingConfigSourceFactory {
public Jdbc() {
super("jdbc", "quarkus.datasource.jdbc.url", "jdbc:%s://%s%s/%s");
}
}

public static class Reactive extends DatasourceServiceBindingConfigSourceFactory {
public Reactive() {
super("reactive", "quarkus.datasource.reactive.url", "%s://%s%s/%s");
}
}
}
Loading

0 comments on commit 8022fe5

Please sign in to comment.