diff --git a/gateway-ha/pom.xml b/gateway-ha/pom.xml index be43afa2d..8ded75d59 100644 --- a/gateway-ha/pom.xml +++ b/gateway-ha/pom.xml @@ -78,6 +78,18 @@ classes + + com.h2database + h2 + 1.4.192 + + + + com.mysql + mysql-connector-j + 9.3.0 + + com.nimbusds nimbus-jose-jwt @@ -91,6 +103,12 @@ jdk11 + + com.oracle.database.jdbc + ojdbc11 + 23.8.0.25.04 + + com.squareup.okhttp3 okhttp @@ -268,23 +286,14 @@ - org.weakref - jmxutils - - - - com.mysql - mysql-connector-j - 9.3.0 - runtime + org.postgresql + postgresql + 42.7.5 - com.oracle.database.jdbc - ojdbc11-production - 23.8.0.25.04 - pom - runtime + org.weakref + jmxutils @@ -316,21 +325,7 @@ runtime - - org.postgresql - postgresql - 42.7.7 - runtime - - - - com.h2database - h2 - 1.4.192 - test - - com.squareup.okhttp3 mockwebserver diff --git a/gateway-ha/src/main/java/io/trino/gateway/ha/config/DataStoreConfiguration.java b/gateway-ha/src/main/java/io/trino/gateway/ha/config/DataStoreConfiguration.java index cd04c5335..a847ab1b1 100644 --- a/gateway-ha/src/main/java/io/trino/gateway/ha/config/DataStoreConfiguration.java +++ b/gateway-ha/src/main/java/io/trino/gateway/ha/config/DataStoreConfiguration.java @@ -13,6 +13,13 @@ */ package io.trino.gateway.ha.config; +import java.sql.Driver; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.Enumeration; + +import static java.util.Objects.requireNonNull; + public class DataStoreConfiguration { private String jdbcUrl; @@ -21,10 +28,15 @@ public class DataStoreConfiguration private String driver; private Integer queryHistoryHoursRetention = 4; private boolean runMigrationsEnabled = true; + private DataStoreType dataStoreType; + + // TODO: Refactor to decouple DataStoreConfiguration from a specific + // database implementation after adopting the Airlift configuration framework (https://github.com/trinodb/trino-gateway/issues/378) + private MySqlConfiguration mySqlConfiguration = new MySqlConfiguration(); public DataStoreConfiguration(String jdbcUrl, String user, String password, String driver, Integer queryHistoryHoursRetention, boolean runMigrationsEnabled) { - this.jdbcUrl = jdbcUrl; + this.jdbcUrl = requireNonNull(jdbcUrl, "jdbc must be set"); this.user = user; this.password = password; this.driver = driver; @@ -93,4 +105,45 @@ public void setRunMigrationsEnabled(boolean runMigrationsEnabled) { this.runMigrationsEnabled = runMigrationsEnabled; } + + public MySqlConfiguration getMySqlConfiguration() + { + return mySqlConfiguration; + } + + public void setMySqlConfiguration(MySqlConfiguration mySqlConfig) + { + this.mySqlConfiguration = mySqlConfig; + } + + public DataStoreType getDataStoreType() + { + if (dataStoreType != null) { + return dataStoreType; + } + String jdbcUrl = getJdbcUrl(); + try { + Enumeration drivers = DriverManager.getDrivers(); + while (drivers.hasMoreElements()) { + Driver driver = drivers.nextElement(); + if (driver.acceptsURL(jdbcUrl)) { + for (DataStoreType dataStoreType : DataStoreType.values()) { + if (dataStoreType.getDriverClass().isAssignableFrom(driver.getClass())) { + return dataStoreType; + } + } + break; + } + } + } + catch (SQLException e) { + throw new RuntimeException("Error enumerating JDBC drivers", e); + } + throw new IllegalStateException("Unable to infer DataStoreType for URL: " + jdbcUrl); + } + + public void setDataStoreType(DataStoreType backendType) + { + this.dataStoreType = backendType; + } } diff --git a/gateway-ha/src/main/java/io/trino/gateway/ha/config/DataStoreType.java b/gateway-ha/src/main/java/io/trino/gateway/ha/config/DataStoreType.java new file mode 100644 index 000000000..fdff99836 --- /dev/null +++ b/gateway-ha/src/main/java/io/trino/gateway/ha/config/DataStoreType.java @@ -0,0 +1,38 @@ +/* + * 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 io.trino.gateway.ha.config; + +import java.sql.Driver; + +public enum DataStoreType { + ORACLE(oracle.jdbc.driver.OracleDriver.class), + MYSQL(com.mysql.cj.jdbc.Driver.class), + POSTGRES(org.postgresql.Driver.class), + H2(org.h2.Driver.class); + + private final Class driverClass; + + DataStoreType(Class driverClass) + { + this.driverClass = driverClass; + } + + /** + * Returns the JDBC Driver class associated with this data store type. + */ + public Class getDriverClass() + { + return driverClass; + } +} diff --git a/gateway-ha/src/main/java/io/trino/gateway/ha/config/MySqlConfiguration.java b/gateway-ha/src/main/java/io/trino/gateway/ha/config/MySqlConfiguration.java new file mode 100644 index 000000000..5eb2d6aea --- /dev/null +++ b/gateway-ha/src/main/java/io/trino/gateway/ha/config/MySqlConfiguration.java @@ -0,0 +1,95 @@ +/* + * 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 io.trino.gateway.ha.config; + +import com.mysql.cj.conf.PropertyDefinitions.SslMode; + +/** + * Configuration for MySQL SSL (client cert and truststore settings). + */ +public class MySqlConfiguration +{ + private SslMode sslMode = SslMode.DISABLED; + private String clientCertificateKeyStoreUrl; + private String clientCertificateKeyStorePassword; + private String clientCertificateKeyStoreType; + private String trustCertificateKeyStoreUrl; + private String trustCertificateKeyStorePassword; + + public SslMode getSslMode() + { + return sslMode; + } + + public MySqlConfiguration setSslMode(SslMode sslMode) + { + this.sslMode = sslMode; + return this; + } + + public String getClientCertificateKeyStoreUrl() + { + return clientCertificateKeyStoreUrl; + } + + public MySqlConfiguration setClientCertificateKeyStoreUrl(String url) + { + this.clientCertificateKeyStoreUrl = url; + return this; + } + + public String getClientCertificateKeyStorePassword() + { + return clientCertificateKeyStorePassword; + } + + public MySqlConfiguration setClientCertificateKeyStorePassword(String password) + { + this.clientCertificateKeyStorePassword = password; + return this; + } + + public String getClientCertificateKeyStoreType() + { + return clientCertificateKeyStoreType; + } + + public MySqlConfiguration setClientCertificateKeyStoreType(String type) + { + this.clientCertificateKeyStoreType = type; + return this; + } + + public String getTrustCertificateKeyStoreUrl() + { + return trustCertificateKeyStoreUrl; + } + + public MySqlConfiguration setTrustCertificateKeyStoreUrl(String url) + { + this.trustCertificateKeyStoreUrl = url; + return this; + } + + public String getTrustCertificateKeyStorePassword() + { + return trustCertificateKeyStorePassword; + } + + public MySqlConfiguration setTrustCertificateKeyStorePassword(String password) + { + this.trustCertificateKeyStorePassword = password; + return this; + } +} diff --git a/gateway-ha/src/main/java/io/trino/gateway/ha/module/HaGatewayProviderModule.java b/gateway-ha/src/main/java/io/trino/gateway/ha/module/HaGatewayProviderModule.java index 1db9e149a..43ad3820d 100644 --- a/gateway-ha/src/main/java/io/trino/gateway/ha/module/HaGatewayProviderModule.java +++ b/gateway-ha/src/main/java/io/trino/gateway/ha/module/HaGatewayProviderModule.java @@ -39,6 +39,7 @@ import io.trino.gateway.ha.config.RoutingRulesConfiguration; import io.trino.gateway.ha.config.RulesExternalConfiguration; import io.trino.gateway.ha.config.UserConfiguration; +import io.trino.gateway.ha.persistence.DefaultJdbcPropertiesProvider; import io.trino.gateway.ha.persistence.JdbcConnectionManager; import io.trino.gateway.ha.router.BackendStateManager; import io.trino.gateway.ha.router.ForRouter; @@ -117,10 +118,10 @@ public HaGatewayProviderModule(HaGatewayConfiguration configuration) oAuth2GatewayCookieConfigurationPropertiesProvider.initialize(configuration.getOauth2GatewayCookieConfiguration()); Jdbi jdbi = Jdbi.create(configuration.getDataStore().getJdbcUrl(), configuration.getDataStore().getUser(), configuration.getDataStore().getPassword()); - JdbcConnectionManager connectionManager = new JdbcConnectionManager(jdbi, configuration.getDataStore()); + JdbcConnectionManager connectionManager = new JdbcConnectionManager(jdbi, configuration.getDataStore(), new DefaultJdbcPropertiesProvider()); resourceGroupsManager = new HaResourceGroupsManager(connectionManager); gatewayBackendManager = new HaGatewayManager(jdbi, configuration.getRouting()); - queryHistoryManager = new HaQueryHistoryManager(jdbi, configuration.getDataStore().getJdbcUrl().startsWith("jdbc:oracle")); + queryHistoryManager = new HaQueryHistoryManager(jdbi, configuration.getDataStore()); } private LbOAuthManager getOAuthManager(HaGatewayConfiguration configuration) diff --git a/gateway-ha/src/main/java/io/trino/gateway/ha/persistence/DefaultJdbcPropertiesProvider.java b/gateway-ha/src/main/java/io/trino/gateway/ha/persistence/DefaultJdbcPropertiesProvider.java new file mode 100644 index 000000000..39b984262 --- /dev/null +++ b/gateway-ha/src/main/java/io/trino/gateway/ha/persistence/DefaultJdbcPropertiesProvider.java @@ -0,0 +1,47 @@ +/* + * 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 io.trino.gateway.ha.persistence; + +import io.trino.gateway.ha.config.DataStoreConfiguration; + +import java.util.Properties; + +/** + * Default JDBC properties provider used as a fallback when no database-specific + * {@link JdbcPropertiesProvider} supports the given {@link DataStoreConfiguration}. + * + *

This provider simply sets the basic "user" and "password" properties + * and should always be the last provider in the list of available providers. + * + *

If a more specific provider (e.g., for MySQL, Oracle, etc.) supports the configuration, + * it should be preferred over this basic fallback. + */ +public class DefaultJdbcPropertiesProvider + implements JdbcPropertiesProvider +{ + @Override + public boolean supports(DataStoreConfiguration configuration) + { + return true; + } + + @Override + public Properties getProperties(DataStoreConfiguration configuration) + { + Properties properties = new Properties(); + properties.setProperty("user", configuration.getUser()); + properties.setProperty("password", configuration.getPassword()); + return properties; + } +} diff --git a/gateway-ha/src/main/java/io/trino/gateway/ha/persistence/JdbcConnectionManager.java b/gateway-ha/src/main/java/io/trino/gateway/ha/persistence/JdbcConnectionManager.java index 2506b3f2a..f1e20a02e 100644 --- a/gateway-ha/src/main/java/io/trino/gateway/ha/persistence/JdbcConnectionManager.java +++ b/gateway-ha/src/main/java/io/trino/gateway/ha/persistence/JdbcConnectionManager.java @@ -14,6 +14,8 @@ package io.trino.gateway.ha.persistence; import com.google.common.annotations.VisibleForTesting; +import com.google.inject.Inject; +import com.google.inject.Singleton; import io.airlift.log.Logger; import io.trino.gateway.ha.config.DataStoreConfiguration; import io.trino.gateway.ha.persistence.dao.QueryHistoryDao; @@ -24,27 +26,32 @@ import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Path; +import java.util.Properties; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import static java.util.Objects.requireNonNull; +@Singleton public class JdbcConnectionManager { private static final Logger log = Logger.get(JdbcConnectionManager.class); private final Jdbi jdbi; private final DataStoreConfiguration configuration; + private final JdbcPropertiesProvider jdbcPropertiesProvider; private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); - public JdbcConnectionManager(Jdbi jdbi, DataStoreConfiguration configuration) + @Inject + public JdbcConnectionManager(Jdbi jdbi, DataStoreConfiguration configuration, JdbcPropertiesProvider jdbcPropertiesProvider) { this.jdbi = requireNonNull(jdbi, "jdbi is null") .installPlugin(new SqlObjectPlugin()) .registerRowMapper(new RecordAndAnnotatedConstructorMapper()); this.configuration = configuration; + this.jdbcPropertiesProvider = requireNonNull(jdbcPropertiesProvider, "jdbcPropertiesProvider is null"); startCleanUps(); } @@ -58,12 +65,18 @@ public Jdbi getJdbi(@Nullable String routingGroupDatabase) if (routingGroupDatabase == null) { return jdbi; } + Properties properties = jdbcPropertiesProvider.getProperties(configuration); - return Jdbi.create(buildJdbcUrl(routingGroupDatabase), configuration.getUser(), configuration.getPassword()) + return Jdbi.create(buildJdbcUrl(routingGroupDatabase), properties) .installPlugin(new SqlObjectPlugin()) .registerRowMapper(new RecordAndAnnotatedConstructorMapper()); } + public DataStoreConfiguration getConfiguration() + { + return configuration; + } + @VisibleForTesting String buildJdbcUrl(@Nullable String routingGroupDatabase) { diff --git a/gateway-ha/src/main/java/io/trino/gateway/ha/persistence/JdbcPropertiesProvider.java b/gateway-ha/src/main/java/io/trino/gateway/ha/persistence/JdbcPropertiesProvider.java new file mode 100644 index 000000000..878f2d503 --- /dev/null +++ b/gateway-ha/src/main/java/io/trino/gateway/ha/persistence/JdbcPropertiesProvider.java @@ -0,0 +1,29 @@ +/* + * 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 io.trino.gateway.ha.persistence; + +import io.trino.gateway.ha.config.DataStoreConfiguration; + +import java.util.Properties; + +/** + * Given a DataStoreConfiguration, produce the proper JDBC driver properties. + * Different implementations can handle MySQL, Postgres, H2, etc. + */ +public interface JdbcPropertiesProvider +{ + boolean supports(DataStoreConfiguration config); + + Properties getProperties(DataStoreConfiguration config); +} diff --git a/gateway-ha/src/main/java/io/trino/gateway/ha/persistence/JdbcPropertiesProviderFactory.java b/gateway-ha/src/main/java/io/trino/gateway/ha/persistence/JdbcPropertiesProviderFactory.java new file mode 100644 index 000000000..aa2105383 --- /dev/null +++ b/gateway-ha/src/main/java/io/trino/gateway/ha/persistence/JdbcPropertiesProviderFactory.java @@ -0,0 +1,45 @@ +/* + * 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 io.trino.gateway.ha.persistence; + +import com.google.common.collect.ImmutableList; +import com.google.inject.Inject; +import com.google.inject.Singleton; +import io.trino.gateway.ha.config.DataStoreConfiguration; + +import java.util.List; + +import static java.lang.String.format; +import static java.util.Objects.requireNonNull; + +@Singleton +public class JdbcPropertiesProviderFactory +{ + private final List providers; + + @Inject + public JdbcPropertiesProviderFactory(List orderedProviders) + { + this.providers = ImmutableList.copyOf(requireNonNull(orderedProviders, "providers is null")); + } + + public JdbcPropertiesProvider forConfig(DataStoreConfiguration config) + { + return providers.stream() + .filter(provider -> provider.supports(config)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException( + format("No JdbcPropertiesProvider for driver: %s", config.getDriver()))); + } +} diff --git a/gateway-ha/src/main/java/io/trino/gateway/ha/persistence/MySqlJdbcPropertiesProvider.java b/gateway-ha/src/main/java/io/trino/gateway/ha/persistence/MySqlJdbcPropertiesProvider.java new file mode 100644 index 000000000..3710b5039 --- /dev/null +++ b/gateway-ha/src/main/java/io/trino/gateway/ha/persistence/MySqlJdbcPropertiesProvider.java @@ -0,0 +1,75 @@ +/* + * 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 io.trino.gateway.ha.persistence; + +import com.mysql.cj.conf.PropertyDefinitions.SslMode; +import com.mysql.cj.conf.PropertyKey; +import io.trino.gateway.ha.config.DataStoreConfiguration; +import io.trino.gateway.ha.config.MySqlConfiguration; + +import java.util.Locale; +import java.util.Properties; + +import static java.util.Objects.requireNonNull; + +public class MySqlJdbcPropertiesProvider + implements JdbcPropertiesProvider +{ + @Override + public boolean supports(DataStoreConfiguration configuration) + { + return configuration.getDriver() != null + && configuration.getJdbcUrl() != null + && configuration.getJdbcUrl().toLowerCase(Locale.ROOT).startsWith("jdbc:mysql"); + } + + @Override + public Properties getProperties(DataStoreConfiguration configuration) + { + Properties properties = new Properties(); + properties.setProperty("user", configuration.getUser()); + + MySqlConfiguration mySqlConfiguration = configuration.getMySqlConfiguration(); + properties.setProperty(PropertyKey.sslMode.getKeyName(), mySqlConfiguration.getSslMode().toString()); + + if (SslMode.VERIFY_CA.equals(mySqlConfiguration.getSslMode())) { + requireNonNull(mySqlConfiguration.getClientCertificateKeyStoreUrl(), + "clientCertificateKeyStoreUrl must be set when sslMode=VERIFY_CA"); + requireNonNull(mySqlConfiguration.getClientCertificateKeyStorePassword(), + "clientCertificateKeyStorePassword must be set when sslMode=VERIFY_CA"); + requireNonNull(mySqlConfiguration.getTrustCertificateKeyStoreUrl(), + "trustCertificateKeyStoreUrl must be set when sslMode=VERIFY_CA"); + requireNonNull(mySqlConfiguration.getTrustCertificateKeyStorePassword(), + "trustCertificateKeyStorePassword must be set when sslMode=VERIFY_CA"); + + properties.setProperty(PropertyKey.clientCertificateKeyStoreUrl.getKeyName(), + mySqlConfiguration.getClientCertificateKeyStoreUrl()); + properties.setProperty(PropertyKey.clientCertificateKeyStorePassword.getKeyName(), + mySqlConfiguration.getClientCertificateKeyStorePassword()); + if (mySqlConfiguration.getClientCertificateKeyStoreType() != null) { + properties.setProperty( + PropertyKey.clientCertificateKeyStoreType.getKeyName(), + mySqlConfiguration.getClientCertificateKeyStoreType()); + } + properties.setProperty(PropertyKey.trustCertificateKeyStoreUrl.getKeyName(), + mySqlConfiguration.getTrustCertificateKeyStoreUrl()); + properties.setProperty(PropertyKey.trustCertificateKeyStorePassword.getKeyName(), + mySqlConfiguration.getTrustCertificateKeyStorePassword()); + } + else { + properties.setProperty("password", configuration.getPassword()); + } + return properties; + } +} diff --git a/gateway-ha/src/main/java/io/trino/gateway/ha/router/HaGatewayManager.java b/gateway-ha/src/main/java/io/trino/gateway/ha/router/HaGatewayManager.java index db5ad7d0f..619d5c698 100644 --- a/gateway-ha/src/main/java/io/trino/gateway/ha/router/HaGatewayManager.java +++ b/gateway-ha/src/main/java/io/trino/gateway/ha/router/HaGatewayManager.java @@ -14,6 +14,7 @@ package io.trino.gateway.ha.router; import com.google.common.collect.ImmutableList; +import com.google.inject.Inject; import io.airlift.log.Logger; import io.trino.gateway.ha.config.ProxyBackendConfiguration; import io.trino.gateway.ha.config.RoutingConfiguration; @@ -36,6 +37,7 @@ public class HaGatewayManager private final GatewayBackendDao dao; private final String defaultRoutingGroup; + @Inject public HaGatewayManager(Jdbi jdbi, RoutingConfiguration routingConfiguration) { dao = requireNonNull(jdbi, "jdbi is null").onDemand(GatewayBackendDao.class); diff --git a/gateway-ha/src/main/java/io/trino/gateway/ha/router/HaQueryHistoryManager.java b/gateway-ha/src/main/java/io/trino/gateway/ha/router/HaQueryHistoryManager.java index 5a6dfad36..f14c8b40f 100644 --- a/gateway-ha/src/main/java/io/trino/gateway/ha/router/HaQueryHistoryManager.java +++ b/gateway-ha/src/main/java/io/trino/gateway/ha/router/HaQueryHistoryManager.java @@ -14,6 +14,9 @@ package io.trino.gateway.ha.router; import com.google.common.base.Strings; +import com.google.inject.Inject; +import io.trino.gateway.ha.config.DataStoreConfiguration; +import io.trino.gateway.ha.config.DataStoreType; import io.trino.gateway.ha.domain.TableData; import io.trino.gateway.ha.domain.request.QueryHistoryRequest; import io.trino.gateway.ha.domain.response.DistributionResponse; @@ -38,12 +41,14 @@ public class HaQueryHistoryManager private static final int FIRST_PAGE_NO = 1; private final QueryHistoryDao dao; - private final boolean isOracleBackend; + private final DataStoreType backend; - public HaQueryHistoryManager(Jdbi jdbi, boolean isOracleBackend) + @Inject + public HaQueryHistoryManager(Jdbi jdbi, DataStoreConfiguration configuration) { + requireNonNull(configuration, "DataStoreConfiguration is null"); dao = requireNonNull(jdbi, "jdbi is null").onDemand(QueryHistoryDao.class); - this.isOracleBackend = isOracleBackend; + this.backend = configuration.getDataStoreType(); } @Override @@ -70,10 +75,10 @@ public List fetchQueryHistory(Optional user) { List histories; if (user.isPresent()) { - histories = dao.findRecentQueriesByUserName(user.orElseThrow(), isOracleBackend); + histories = dao.findRecentQueriesByUserName(user.orElseThrow(), backend == DataStoreType.ORACLE); } else { - histories = dao.findRecentQueries(isOracleBackend); + histories = dao.findRecentQueries(backend == DataStoreType.ORACLE); } return upcast(histories); } diff --git a/gateway-ha/src/main/java/io/trino/gateway/ha/router/HaResourceGroupsManager.java b/gateway-ha/src/main/java/io/trino/gateway/ha/router/HaResourceGroupsManager.java index 462c17094..4d8c5d64a 100644 --- a/gateway-ha/src/main/java/io/trino/gateway/ha/router/HaResourceGroupsManager.java +++ b/gateway-ha/src/main/java/io/trino/gateway/ha/router/HaResourceGroupsManager.java @@ -14,6 +14,7 @@ package io.trino.gateway.ha.router; import com.google.common.collect.ImmutableList; +import com.google.inject.Inject; import io.trino.gateway.ha.persistence.JdbcConnectionManager; import io.trino.gateway.ha.persistence.dao.ExactMatchSourceSelectors; import io.trino.gateway.ha.persistence.dao.ExactMatchSourceSelectorsDao; @@ -36,6 +37,7 @@ public class HaResourceGroupsManager private final JdbcConnectionManager connectionManager; private final ExactMatchSourceSelectorsDao exactMatchSourceSelectorsDao; + @Inject public HaResourceGroupsManager(JdbcConnectionManager connectionManager) { this.connectionManager = connectionManager; diff --git a/gateway-ha/src/test/java/io/trino/gateway/ha/TestTrinoResource.java b/gateway-ha/src/test/java/io/trino/gateway/ha/TestTrinoResource.java index 99a00dcb6..b81a83a22 100644 --- a/gateway-ha/src/test/java/io/trino/gateway/ha/TestTrinoResource.java +++ b/gateway-ha/src/test/java/io/trino/gateway/ha/TestTrinoResource.java @@ -15,6 +15,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import io.trino.gateway.ha.config.DataStoreConfiguration; +import io.trino.gateway.ha.persistence.DefaultJdbcPropertiesProvider; import io.trino.gateway.ha.persistence.JdbcConnectionManager; import io.trino.gateway.ha.router.HaResourceGroupsManager; import okhttp3.MediaType; @@ -66,7 +67,7 @@ void setup() // Setup resource group manager DataStoreConfiguration db = new DataStoreConfiguration(postgresql.getJdbcUrl(), postgresql.getUsername(), postgresql.getPassword(), "org.postgresql.Driver", 4, true); Jdbi jdbi = Jdbi.create(postgresql.getJdbcUrl(), postgresql.getUsername(), postgresql.getPassword()); - connectionManager = new JdbcConnectionManager(jdbi, db); + connectionManager = new JdbcConnectionManager(jdbi, db, new DefaultJdbcPropertiesProvider()); resourceGroupManager = new HaResourceGroupsManager(connectionManager); // Start Trino Gateway so migrations are run to create tables before inserting test data diff --git a/gateway-ha/src/test/java/io/trino/gateway/ha/TestingJdbcConnectionManager.java b/gateway-ha/src/test/java/io/trino/gateway/ha/TestingJdbcConnectionManager.java index 1f60a0dfb..d0f0802a6 100644 --- a/gateway-ha/src/test/java/io/trino/gateway/ha/TestingJdbcConnectionManager.java +++ b/gateway-ha/src/test/java/io/trino/gateway/ha/TestingJdbcConnectionManager.java @@ -14,6 +14,7 @@ package io.trino.gateway.ha; import io.trino.gateway.ha.config.DataStoreConfiguration; +import io.trino.gateway.ha.persistence.DefaultJdbcPropertiesProvider; import io.trino.gateway.ha.persistence.JdbcConnectionManager; import org.jdbi.v3.core.Jdbi; import org.testcontainers.containers.JdbcDatabaseContainer; @@ -33,12 +34,12 @@ public static JdbcConnectionManager createTestingJdbcConnectionManager() HaGatewayTestUtils.seedRequiredData(tempH2DbDir.getAbsolutePath()); DataStoreConfiguration db = new DataStoreConfiguration(jdbcUrl, "sa", "sa", "org.h2.Driver", 4, false); Jdbi jdbi = Jdbi.create(jdbcUrl, "sa", "sa"); - return new JdbcConnectionManager(jdbi, db); + return new JdbcConnectionManager(jdbi, db, new DefaultJdbcPropertiesProvider()); } public static JdbcConnectionManager createTestingJdbcConnectionManager(JdbcDatabaseContainer container, DataStoreConfiguration config) { Jdbi jdbi = Jdbi.create(container.getJdbcUrl(), container.getUsername(), container.getPassword()); - return new JdbcConnectionManager(jdbi, config); + return new JdbcConnectionManager(jdbi, config, new DefaultJdbcPropertiesProvider()); } } diff --git a/gateway-ha/src/test/java/io/trino/gateway/ha/persistence/TestJdbcConnectionManager.java b/gateway-ha/src/test/java/io/trino/gateway/ha/persistence/TestJdbcConnectionManager.java index 8a3ff454c..f3be6e43a 100644 --- a/gateway-ha/src/test/java/io/trino/gateway/ha/persistence/TestJdbcConnectionManager.java +++ b/gateway-ha/src/test/java/io/trino/gateway/ha/persistence/TestJdbcConnectionManager.java @@ -108,7 +108,7 @@ void testBuildJdbcUrlWithNullJdbcUrlThrowsException() DataStoreConfiguration dataStoreConfiguration = Mockito.mock(DataStoreConfiguration.class); when(dataStoreConfiguration.getJdbcUrl()).thenReturn(null); - JdbcConnectionManager connectionManager = new JdbcConnectionManager(Jdbi.create("jdbc:h2:/mydb", "sa", "sa"), dataStoreConfiguration); + JdbcConnectionManager connectionManager = new JdbcConnectionManager(Jdbi.create("jdbc:h2:/mydb", "sa", "sa"), dataStoreConfiguration, new DefaultJdbcPropertiesProvider()); assertThatThrownBy(() -> connectionManager.buildJdbcUrl(null)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("JDBC URL cannot be null"); @@ -126,6 +126,6 @@ void testBuildJdbcUrlWithNoSlashThrowsException() private static JdbcConnectionManager createConnectionManager(String jdbcUrl) { DataStoreConfiguration db = new DataStoreConfiguration(jdbcUrl, "sa", "sa", "", 4, true); - return new JdbcConnectionManager(Jdbi.create(jdbcUrl, "sa", "sa"), db); + return new JdbcConnectionManager(Jdbi.create(jdbcUrl, "sa", "sa"), db, new DefaultJdbcPropertiesProvider()); } } diff --git a/gateway-ha/src/test/java/io/trino/gateway/ha/persistence/TestJdbcPropertiesProviderFactory.java b/gateway-ha/src/test/java/io/trino/gateway/ha/persistence/TestJdbcPropertiesProviderFactory.java new file mode 100644 index 000000000..d3399dc68 --- /dev/null +++ b/gateway-ha/src/test/java/io/trino/gateway/ha/persistence/TestJdbcPropertiesProviderFactory.java @@ -0,0 +1,121 @@ +/* + * 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 io.trino.gateway.ha.persistence; + +import com.mysql.cj.conf.PropertyDefinitions.SslMode; +import com.mysql.cj.conf.PropertyKey; +import io.trino.gateway.ha.config.DataStoreConfiguration; +import io.trino.gateway.ha.config.MySqlConfiguration; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.Properties; + +import static org.assertj.core.api.Assertions.assertThat; + +final class TestJdbcPropertiesProviderFactory +{ + private static JdbcPropertiesProviderFactory factoryFor() + { + List providers = Arrays.asList( + new MySqlJdbcPropertiesProvider(), + new DefaultJdbcPropertiesProvider()); + return new JdbcPropertiesProviderFactory(providers); + } + + private DataStoreConfiguration makeMySqlConfig(SslMode mode) + { + DataStoreConfiguration db = new DataStoreConfiguration(); + db.setDriver("com.mysql.Driver"); + db.setJdbcUrl("jdbc:mysql://host/db"); + db.setUser("root"); + db.setPassword("root123"); + MySqlConfiguration mySqlConfiguration = db.getMySqlConfiguration(); + mySqlConfiguration.setSslMode(mode); + return db; + } + + private DataStoreConfiguration makeH2Config() + { + DataStoreConfiguration db = new DataStoreConfiguration(); + db.setDriver("org.h2.Driver"); + db.setJdbcUrl("jdbc:h2:mem:test"); + db.setUser("sa"); + db.setPassword("sa"); + return db; + } + + @Test + void testBasicProviderWhenH2() + { + DataStoreConfiguration cfg = makeH2Config(); + JdbcPropertiesProviderFactory factory = factoryFor(); + DefaultJdbcPropertiesProvider provider = (DefaultJdbcPropertiesProvider) factory.forConfig(cfg); + assertThat(provider).isInstanceOf(DefaultJdbcPropertiesProvider.class); + + Properties properties = provider.getProperties(cfg); + assertThat(properties) + .hasSize(2) + .containsEntry("user", "sa") + .containsEntry("password", "sa"); + } + + @Test + void testMysqlProviderWhenSslDisabled() + { + DataStoreConfiguration cfg = makeMySqlConfig(SslMode.DISABLED); + JdbcPropertiesProviderFactory factory = factoryFor(); + JdbcPropertiesProvider provider = factory.forConfig(cfg); + assertThat(provider).isInstanceOf(MySqlJdbcPropertiesProvider.class); + + Properties properties = provider.getProperties(cfg); + assertThat(properties.getProperty("user")).isEqualTo("root"); + assertThat(properties.getProperty("password")).isEqualTo("root123"); + assertThat(properties.getProperty(PropertyKey.sslMode.getKeyName())) + .isEqualTo("DISABLED"); + + assertThat(properties).doesNotContainKeys( + PropertyKey.clientCertificateKeyStoreUrl.getKeyName(), + PropertyKey.clientCertificateKeyStorePassword.getKeyName(), + PropertyKey.clientCertificateKeyStoreType.getKeyName(), + PropertyKey.trustCertificateKeyStoreUrl.getKeyName(), + PropertyKey.trustCertificateKeyStorePassword.getKeyName()); + } + + @Test + void testMysqlProviderWhenVerifyCa() + { + DataStoreConfiguration cfg = makeMySqlConfig(SslMode.VERIFY_CA); + JdbcPropertiesProviderFactory factory = factoryFor(); + MySqlConfiguration mysqlConfig = cfg.getMySqlConfiguration(); + mysqlConfig.setClientCertificateKeyStoreUrl("file:/tmp/cli.p12") + .setClientCertificateKeyStorePassword("cpw") + .setClientCertificateKeyStoreType("PKCS12") + .setTrustCertificateKeyStoreUrl("file:/tmp/trs.p12") + .setTrustCertificateKeyStorePassword("tpw"); + + JdbcPropertiesProvider provider = factory.forConfig(cfg); + assertThat(provider).isInstanceOf(MySqlJdbcPropertiesProvider.class); + + Properties properties = provider.getProperties(cfg); + assertThat(properties.getProperty("user")).isEqualTo("root"); + assertThat(properties.getProperty(PropertyKey.sslMode.getKeyName())) + .isEqualTo("VERIFY_CA"); + assertThat(properties.getProperty(PropertyKey.clientCertificateKeyStoreUrl.getKeyName())) + .isEqualTo("file:/tmp/cli.p12"); + assertThat(properties.getProperty(PropertyKey.trustCertificateKeyStoreUrl.getKeyName())) + .isEqualTo("file:/tmp/trs.p12"); + } +} diff --git a/gateway-ha/src/test/java/io/trino/gateway/ha/router/BaseExternalUrlQueryHistoryTest.java b/gateway-ha/src/test/java/io/trino/gateway/ha/router/BaseExternalUrlQueryHistoryTest.java index d795b0194..45bdd7159 100644 --- a/gateway-ha/src/test/java/io/trino/gateway/ha/router/BaseExternalUrlQueryHistoryTest.java +++ b/gateway-ha/src/test/java/io/trino/gateway/ha/router/BaseExternalUrlQueryHistoryTest.java @@ -49,7 +49,7 @@ protected BaseExternalUrlQueryHistoryTest(JdbcDatabaseContainer container) true); FlywayMigration.migrate(config); JdbcConnectionManager jdbcConnectionManager = createTestingJdbcConnectionManager(container, config); - queryHistoryManager = new HaQueryHistoryManager(jdbcConnectionManager.getJdbi(), container.getJdbcUrl().startsWith("jdbc:oracle")); + queryHistoryManager = new HaQueryHistoryManager(jdbcConnectionManager.getJdbi(), config); } @AfterAll diff --git a/gateway-ha/src/test/java/io/trino/gateway/ha/router/BaseTestQueryHistoryManager.java b/gateway-ha/src/test/java/io/trino/gateway/ha/router/BaseTestQueryHistoryManager.java index 3f5de4bbc..777b6611f 100644 --- a/gateway-ha/src/test/java/io/trino/gateway/ha/router/BaseTestQueryHistoryManager.java +++ b/gateway-ha/src/test/java/io/trino/gateway/ha/router/BaseTestQueryHistoryManager.java @@ -50,7 +50,7 @@ void setUp() true); FlywayMigration.migrate(config); JdbcConnectionManager jdbcConnectionManager = createTestingJdbcConnectionManager(container, config); - queryHistoryManager = new HaQueryHistoryManager(jdbcConnectionManager.getJdbi(), container.getJdbcUrl().startsWith("jdbc:oracle")); + queryHistoryManager = new HaQueryHistoryManager(jdbcConnectionManager.getJdbi(), jdbcConnectionManager.getConfiguration()); } @AfterAll diff --git a/gateway-ha/src/test/java/io/trino/gateway/ha/router/TestRoutingManagerNotFound.java b/gateway-ha/src/test/java/io/trino/gateway/ha/router/TestRoutingManagerNotFound.java index 8f4154623..6d6d9ed0f 100644 --- a/gateway-ha/src/test/java/io/trino/gateway/ha/router/TestRoutingManagerNotFound.java +++ b/gateway-ha/src/test/java/io/trino/gateway/ha/router/TestRoutingManagerNotFound.java @@ -31,7 +31,7 @@ public TestRoutingManagerNotFound() routingConfiguration.setDefaultRoutingGroup("default"); GatewayBackendManager backendManager = new HaGatewayManager(connectionManager.getJdbi(), routingConfiguration); - QueryHistoryManager historyManager = new HaQueryHistoryManager(connectionManager.getJdbi(), false); + QueryHistoryManager historyManager = new HaQueryHistoryManager(connectionManager.getJdbi(), connectionManager.getConfiguration()); this.routingManager = new StochasticRoutingManager(backendManager, historyManager, routingConfiguration); } diff --git a/gateway-ha/src/test/java/io/trino/gateway/ha/router/TestSpecificDbResourceGroupsManager.java b/gateway-ha/src/test/java/io/trino/gateway/ha/router/TestSpecificDbResourceGroupsManager.java index f78b80377..10a4da47b 100644 --- a/gateway-ha/src/test/java/io/trino/gateway/ha/router/TestSpecificDbResourceGroupsManager.java +++ b/gateway-ha/src/test/java/io/trino/gateway/ha/router/TestSpecificDbResourceGroupsManager.java @@ -15,6 +15,7 @@ import io.trino.gateway.ha.HaGatewayTestUtils; import io.trino.gateway.ha.config.DataStoreConfiguration; +import io.trino.gateway.ha.persistence.DefaultJdbcPropertiesProvider; import io.trino.gateway.ha.persistence.JdbcConnectionManager; import org.jdbi.v3.core.Jdbi; import org.junit.jupiter.api.BeforeAll; @@ -48,7 +49,7 @@ void setUp() DataStoreConfiguration db = new DataStoreConfiguration(jdbcUrl, "sa", "sa", "org.h2.Driver", 4, false); Jdbi jdbi = Jdbi.create(jdbcUrl, "sa", "sa"); - JdbcConnectionManager connectionManager = new JdbcConnectionManager(jdbi, db); + JdbcConnectionManager connectionManager = new JdbcConnectionManager(jdbi, db, new DefaultJdbcPropertiesProvider()); super.resourceGroupManager = new HaResourceGroupsManager(connectionManager); } diff --git a/gateway-ha/src/test/java/io/trino/gateway/ha/router/TestStochasticRoutingManager.java b/gateway-ha/src/test/java/io/trino/gateway/ha/router/TestStochasticRoutingManager.java index 38f60ab22..b28723a54 100644 --- a/gateway-ha/src/test/java/io/trino/gateway/ha/router/TestStochasticRoutingManager.java +++ b/gateway-ha/src/test/java/io/trino/gateway/ha/router/TestStochasticRoutingManager.java @@ -38,7 +38,7 @@ void setUp() JdbcConnectionManager connectionManager = createTestingJdbcConnectionManager(); RoutingConfiguration routingConfiguration = new RoutingConfiguration(); backendManager = new HaGatewayManager(connectionManager.getJdbi(), routingConfiguration); - historyManager = new HaQueryHistoryManager(connectionManager.getJdbi(), false); + historyManager = new HaQueryHistoryManager(connectionManager.getJdbi(), connectionManager.getConfiguration()); haRoutingManager = new StochasticRoutingManager(backendManager, historyManager, routingConfiguration); }