diff --git a/core/pom.xml b/core/pom.xml index 45f2ee64cf6..5618d213f6f 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -185,11 +185,6 @@ testng test - - com.github.tomakehurst - wiremock - test - diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/config/DefaultDriverOption.java b/core/src/main/java/com/datastax/oss/driver/api/core/config/DefaultDriverOption.java index dd60a2487fb..375d61a7982 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/config/DefaultDriverOption.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/config/DefaultDriverOption.java @@ -869,14 +869,6 @@ public enum DefaultDriverOption implements DriverOption { */ NETTY_DAEMON("advanced.netty.daemon"), - /** - * The location of the cloud secure bundle used to connect to DataStax Apache Cassandra as a - * service. - * - *

Value-type: {@link String} - */ - CLOUD_SECURE_CONNECT_BUNDLE("basic.cloud.secure-connect-bundle"), - /** * Whether the slow replica avoidance should be enabled in the default LBP. * diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/config/TypedDriverOption.java b/core/src/main/java/com/datastax/oss/driver/api/core/config/TypedDriverOption.java index af93e734ef1..7c9085270ed 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/config/TypedDriverOption.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/config/TypedDriverOption.java @@ -685,12 +685,6 @@ public String toString() { /** Whether the threads created by the driver should be daemon threads. */ public static final TypedDriverOption NETTY_DAEMON = new TypedDriverOption<>(DefaultDriverOption.NETTY_DAEMON, GenericType.BOOLEAN); - /** - * The location of the cloud secure bundle used to connect to DataStax Apache Cassandra as a - * service. - */ - public static final TypedDriverOption CLOUD_SECURE_CONNECT_BUNDLE = - new TypedDriverOption<>(DefaultDriverOption.CLOUD_SECURE_CONNECT_BUNDLE, GenericType.STRING); /** Whether the slow replica avoidance should be enabled in the default LBP. */ public static final TypedDriverOption LOAD_BALANCING_POLICY_SLOW_AVOIDANCE = new TypedDriverOption<>( diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/session/ProgrammaticArguments.java b/core/src/main/java/com/datastax/oss/driver/api/core/session/ProgrammaticArguments.java index 4db44655dc2..d70bf780593 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/session/ProgrammaticArguments.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/session/ProgrammaticArguments.java @@ -36,7 +36,6 @@ import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableMap; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; -import java.net.InetSocketAddress; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -68,7 +67,6 @@ public static Builder builder() { private final ClassLoader classLoader; private final AuthProvider authProvider; private final SslEngineFactory sslEngineFactory; - private final InetSocketAddress cloudProxyAddress; private final UUID startupClientId; private final String startupApplicationName; private final String startupApplicationVersion; @@ -88,7 +86,6 @@ private ProgrammaticArguments( @Nullable ClassLoader classLoader, @Nullable AuthProvider authProvider, @Nullable SslEngineFactory sslEngineFactory, - @Nullable InetSocketAddress cloudProxyAddress, @Nullable UUID startupClientId, @Nullable String startupApplicationName, @Nullable String startupApplicationVersion, @@ -107,7 +104,6 @@ private ProgrammaticArguments( this.classLoader = classLoader; this.authProvider = authProvider; this.sslEngineFactory = sslEngineFactory; - this.cloudProxyAddress = cloudProxyAddress; this.startupClientId = startupClientId; this.startupApplicationName = startupApplicationName; this.startupApplicationVersion = startupApplicationVersion; @@ -173,11 +169,6 @@ public SslEngineFactory getSslEngineFactory() { return sslEngineFactory; } - @Nullable - public InetSocketAddress getCloudProxyAddress() { - return cloudProxyAddress; - } - @Nullable public UUID getStartupClientId() { return startupClientId; @@ -223,7 +214,6 @@ public static class Builder { private ClassLoader classLoader; private AuthProvider authProvider; private SslEngineFactory sslEngineFactory; - private InetSocketAddress cloudProxyAddress; private UUID startupClientId; private String startupApplicationName; private String startupApplicationVersion; @@ -388,12 +378,6 @@ public Builder withClassLoader(@Nullable ClassLoader classLoader) { return this; } - @NonNull - public Builder withCloudProxyAddress(@Nullable InetSocketAddress cloudAddress) { - this.cloudProxyAddress = cloudAddress; - return this; - } - @NonNull public Builder withAuthProvider(@Nullable AuthProvider authProvider) { this.authProvider = authProvider; @@ -456,7 +440,6 @@ public ProgrammaticArguments build() { classLoader, authProvider, sslEngineFactory, - cloudProxyAddress, startupClientId, startupApplicationName, startupApplicationVersion, diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/session/SessionBuilder.java b/core/src/main/java/com/datastax/oss/driver/api/core/session/SessionBuilder.java index 8375f0ef30b..77c17fc7b41 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/session/SessionBuilder.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/session/SessionBuilder.java @@ -30,7 +30,6 @@ import com.datastax.oss.driver.api.core.auth.ProgrammaticPlainTextAuthProvider; import com.datastax.oss.driver.api.core.config.ClientRoutesConfig; import com.datastax.oss.driver.api.core.config.DefaultDriverOption; -import com.datastax.oss.driver.api.core.config.DriverConfig; import com.datastax.oss.driver.api.core.config.DriverConfigLoader; import com.datastax.oss.driver.api.core.config.DriverExecutionProfile; import com.datastax.oss.driver.api.core.context.DriverContext; @@ -48,24 +47,16 @@ import com.datastax.oss.driver.api.core.type.codec.registry.MutableCodecRegistry; import com.datastax.oss.driver.api.core.uuid.Uuids; import com.datastax.oss.driver.internal.core.ContactPoints; -import com.datastax.oss.driver.internal.core.config.cloud.CloudConfig; -import com.datastax.oss.driver.internal.core.config.cloud.CloudConfigFactory; import com.datastax.oss.driver.internal.core.config.typesafe.DefaultDriverConfigLoader; import com.datastax.oss.driver.internal.core.context.DefaultDriverContext; import com.datastax.oss.driver.internal.core.context.InternalDriverContext; import com.datastax.oss.driver.internal.core.metadata.DefaultEndPoint; import com.datastax.oss.driver.internal.core.session.DefaultSession; -import com.datastax.oss.driver.internal.core.tracker.W3CContextRequestIdGenerator; import com.datastax.oss.driver.internal.core.util.concurrent.BlockingOperation; import com.datastax.oss.driver.internal.core.util.concurrent.CompletableFutures; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; -import java.io.InputStream; import java.net.InetSocketAddress; -import java.net.MalformedURLException; -import java.net.URL; -import java.nio.file.Path; -import java.nio.file.Paths; import java.util.Collection; import java.util.Collections; import java.util.HashSet; @@ -73,7 +64,6 @@ import java.util.Map; import java.util.Set; import java.util.UUID; -import java.util.concurrent.Callable; import java.util.concurrent.CompletionStage; import java.util.function.Predicate; import javax.net.ssl.SSLContext; @@ -92,8 +82,6 @@ @NotThreadSafe public abstract class SessionBuilder { - public static final String ASTRA_PAYLOAD_KEY = "traceparent"; - private static final Logger LOG = LoggerFactory.getLogger(SessionBuilder.class); @SuppressWarnings("unchecked") @@ -102,11 +90,8 @@ public abstract class SessionBuilder { protected DriverConfigLoader configLoader; protected Set programmaticContactPoints = new HashSet<>(); protected CqlIdentifier keyspace; - protected Callable cloudConfigInputStream; protected ProgrammaticArguments.Builder programmaticArgumentsBuilder = ProgrammaticArguments.builder(); - private boolean programmaticSslFactory = false; - private boolean programmaticLocalDatacenter = false; /** * Sets the configuration loader to use. @@ -427,7 +412,6 @@ public SelfT withCredentials( */ @NonNull public SelfT withSslEngineFactory(@Nullable SslEngineFactory sslEngineFactory) { - this.programmaticSslFactory = true; this.programmaticArgumentsBuilder.withSslEngineFactory(sslEngineFactory); return self; } @@ -465,7 +449,6 @@ public SelfT withSslContext(@Nullable SSLContext sslContext) { * if you use a third-party implementation, refer to their documentation. */ public SelfT withLocalDatacenter(@NonNull String profileName, @NonNull String localDatacenter) { - this.programmaticLocalDatacenter = true; this.programmaticArgumentsBuilder.withLocalDatacenter(profileName, localDatacenter); return self; } @@ -647,31 +630,6 @@ public SelfT withClassLoader(@Nullable ClassLoader classLoader) { return self; } - /** - * Configures this SessionBuilder for Cloud deployments by retrieving connection information from - * the provided {@link Path}. - * - *

To connect to a Cloud database, you must first download the secure database bundle from the - * DataStax Astra console that contains the connection information, then instruct the driver to - * read its contents using either this method or one if its variants. - * - *

For more information, please refer to the DataStax Astra documentation. - * - * @param cloudConfigPath Path to the secure connect bundle zip file. - * @see #withCloudSecureConnectBundle(URL) - * @see #withCloudSecureConnectBundle(InputStream) - */ - @NonNull - public SelfT withCloudSecureConnectBundle(@NonNull Path cloudConfigPath) { - try { - URL cloudConfigUrl = cloudConfigPath.toAbsolutePath().normalize().toUri().toURL(); - this.cloudConfigInputStream = cloudConfigUrl::openStream; - } catch (MalformedURLException e) { - throw new IllegalArgumentException("Incorrect format of cloudConfigPath", e); - } - return self; - } - /** * Registers a CodecRegistry to use for the session. * @@ -684,72 +642,6 @@ public SelfT withCodecRegistry(@Nullable MutableCodecRegistry codecRegistry) { return self; } - /** - * Configures this SessionBuilder for Cloud deployments by retrieving connection information from - * the provided {@link URL}. - * - *

To connect to a Cloud database, you must first download the secure database bundle from the - * DataStax Astra console that contains the connection information, then instruct the driver to - * read its contents using either this method or one if its variants. - * - *

For more information, please refer to the DataStax Astra documentation. - * - * @param cloudConfigUrl URL to the secure connect bundle zip file. - * @see #withCloudSecureConnectBundle(Path) - * @see #withCloudSecureConnectBundle(InputStream) - */ - @NonNull - public SelfT withCloudSecureConnectBundle(@NonNull URL cloudConfigUrl) { - this.cloudConfigInputStream = cloudConfigUrl::openStream; - return self; - } - - /** - * Configures this SessionBuilder for Cloud deployments by retrieving connection information from - * the provided {@link InputStream}. - * - *

To connect to a Cloud database, you must first download the secure database bundle from the - * DataStax Astra console that contains the connection information, then instruct the driver to - * read its contents using either this method or one if its variants. - * - *

For more information, please refer to the DataStax Astra documentation. - * - *

Note that the provided stream will be consumed and closed when either {@link - * #build()} or {@link #buildAsync()} are called; attempting to reuse it afterwards will result in - * an error being thrown. - * - * @param cloudConfigInputStream A stream containing the secure connect bundle zip file. - * @see #withCloudSecureConnectBundle(Path) - * @see #withCloudSecureConnectBundle(URL) - */ - @NonNull - public SelfT withCloudSecureConnectBundle(@NonNull InputStream cloudConfigInputStream) { - this.cloudConfigInputStream = () -> cloudConfigInputStream; - return self; - } - - /** - * Configures this SessionBuilder to use the provided Cloud proxy endpoint. - * - *

Normally, this method should not be called directly; the normal and easiest way to configure - * the driver for Cloud deployments is through a {@linkplain #withCloudSecureConnectBundle(URL) - * secure connect bundle}. - * - *

Setting this option to any non-null address will make the driver use a special topology - * monitor tailored for Cloud deployments. This topology monitor assumes that the target cluster - * should be contacted through the proxy specified here, using SNI routing. - * - *

For more information, please refer to the DataStax Astra documentation. - * - * @param cloudProxyAddress The address of the Cloud proxy to use. - * @see Server Name Indication - */ - @NonNull - public SelfT withCloudProxyAddress(@Nullable InetSocketAddress cloudProxyAddress) { - this.programmaticArgumentsBuilder.withCloudProxyAddress(cloudProxyAddress); - return self; - } - /** * Configures this session to use client routes for cloud private-endpoint deployments. * @@ -910,52 +802,8 @@ protected final CompletionStage buildDefaultSessionAsync() { : defaultConfigLoader(programmaticArguments.getClassLoader()); DriverExecutionProfile defaultConfig = configLoader.getInitialConfig().getDefaultProfile(); - if (cloudConfigInputStream == null) { - String configUrlString = - defaultConfig.getString(DefaultDriverOption.CLOUD_SECURE_CONNECT_BUNDLE, null); - if (configUrlString != null) { - cloudConfigInputStream = () -> getURL(configUrlString).openStream(); - } - } List configContactPoints = defaultConfig.getStringList(DefaultDriverOption.CONTACT_POINTS, Collections.emptyList()); - if (cloudConfigInputStream != null) { - // override request id generator, unless user has already set it - if (programmaticArguments.getRequestIdGenerator() == null) { - programmaticArgumentsBuilder.withRequestIdGenerator( - new W3CContextRequestIdGenerator(ASTRA_PAYLOAD_KEY)); - LOG.debug( - "A secure connect bundle is provided, using W3CContextRequestIdGenerator as request ID generator."); - } - if (!programmaticContactPoints.isEmpty() || !configContactPoints.isEmpty()) { - LOG.info( - "Both a secure connect bundle and contact points were provided. These are mutually exclusive. The contact points from the secure bundle will have priority."); - // clear the contact points provided in the setting file and via addContactPoints - configContactPoints = Collections.emptyList(); - programmaticContactPoints = new HashSet<>(); - } - - if (programmaticSslFactory - || defaultConfig.isDefined(DefaultDriverOption.SSL_ENGINE_FACTORY_CLASS)) { - LOG.info( - "Both a secure connect bundle and SSL options were provided. They are mutually exclusive. The SSL options from the secure bundle will have priority."); - } - CloudConfig cloudConfig = - new CloudConfigFactory().createCloudConfig(cloudConfigInputStream.call()); - addContactEndPoints(cloudConfig.getEndPoints()); - - boolean localDataCenterDefined = - anyProfileHasDatacenterDefined(configLoader.getInitialConfig()); - if (programmaticLocalDatacenter || localDataCenterDefined) { - LOG.info( - "Both a secure connect bundle and a local datacenter were provided. They are mutually exclusive. The local datacenter from the secure bundle will have priority."); - programmaticArgumentsBuilder.clearDatacenters(); - } - withLocalDatacenter(cloudConfig.getLocalDatacenter()); - withSslEngineFactory(cloudConfig.getSslEngineFactory()); - withCloudProxyAddress(cloudConfig.getProxyAddress()); - programmaticArguments = programmaticArgumentsBuilder.build(); - } boolean resolveAddresses = defaultConfig.getBoolean(DefaultDriverOption.RESOLVE_CONTACT_POINTS, false); @@ -980,36 +828,6 @@ protected final CompletionStage buildDefaultSessionAsync() { } } - private boolean anyProfileHasDatacenterDefined(DriverConfig driverConfig) { - for (DriverExecutionProfile driverExecutionProfile : driverConfig.getProfiles().values()) { - if (driverExecutionProfile.isDefined(DefaultDriverOption.LOAD_BALANCING_LOCAL_DATACENTER)) { - return true; - } - } - return false; - } - - /** - * Returns URL based on the configUrl setting. If the configUrl has no protocol provided, the - * method will fallback to file:// protocol and return URL that has file protocol specified. - * - * @param configUrl url to config secure bundle - * @return URL with file protocol if there was not explicit protocol provided in the configUrl - * setting - */ - private URL getURL(String configUrl) throws MalformedURLException { - try { - return new URL(configUrl); - } catch (MalformedURLException e1) { - try { - return Paths.get(configUrl).toAbsolutePath().normalize().toUri().toURL(); - } catch (MalformedURLException e2) { - e2.addSuppressed(e1); - throw e2; - } - } - } - /** * This must return an instance of {@code InternalDriverContext} (it's not expressed * directly in the signature to avoid leaking that type through the protected API). diff --git a/core/src/main/java/com/datastax/oss/driver/internal/core/channel/ChannelFactory.java b/core/src/main/java/com/datastax/oss/driver/internal/core/channel/ChannelFactory.java index 35190afa3f4..75e81b2475e 100644 --- a/core/src/main/java/com/datastax/oss/driver/internal/core/channel/ChannelFactory.java +++ b/core/src/main/java/com/datastax/oss/driver/internal/core/channel/ChannelFactory.java @@ -23,11 +23,9 @@ */ package com.datastax.oss.driver.internal.core.channel; -import com.datastax.oss.driver.api.core.ConsistencyLevel; import com.datastax.oss.driver.api.core.ProtocolVersion; import com.datastax.oss.driver.api.core.UnsupportedProtocolVersionException; import com.datastax.oss.driver.api.core.config.DefaultDriverOption; -import com.datastax.oss.driver.api.core.config.DriverConfig; import com.datastax.oss.driver.api.core.config.DriverExecutionProfile; import com.datastax.oss.driver.api.core.context.DriverContext; import com.datastax.oss.driver.api.core.metadata.EndPoint; @@ -35,7 +33,6 @@ import com.datastax.oss.driver.api.core.metadata.NodeShardingInfo; import com.datastax.oss.driver.api.core.metrics.DefaultNodeMetric; import com.datastax.oss.driver.api.core.metrics.DefaultSessionMetric; -import com.datastax.oss.driver.internal.core.config.typesafe.TypesafeDriverConfig; import com.datastax.oss.driver.internal.core.context.InternalDriverContext; import com.datastax.oss.driver.internal.core.context.NettyOptions; import com.datastax.oss.driver.internal.core.metadata.DefaultNode; @@ -46,7 +43,6 @@ import com.datastax.oss.driver.internal.core.protocol.FrameEncoder; import com.datastax.oss.driver.shaded.guava.common.annotations.VisibleForTesting; import com.datastax.oss.driver.shaded.guava.common.base.Preconditions; -import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableMap; import com.datastax.oss.protocol.internal.ProtocolFeatures; import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; @@ -59,7 +55,6 @@ import java.net.ServerSocket; import java.net.SocketAddress; import java.util.List; -import java.util.Map; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; @@ -76,20 +71,8 @@ public class ChannelFactory { private static final Logger LOG = LoggerFactory.getLogger(ChannelFactory.class); - /** - * A value for {@link #productType} that indicates that we are connected to DataStax Cloud. This - * value matches the one defined at DSE DB server side at {@code ProductType.java}. - */ - private static final String DATASTAX_CLOUD_PRODUCT_TYPE = "DATASTAX_APOLLO"; - private static final AtomicBoolean LOGGED_ORPHAN_WARNING = new AtomicBoolean(); - /** - * A value for {@link #productType} that indicates that the server does not report any product - * type. - */ - private static final String UNKNOWN_PRODUCT_TYPE = "UNKNOWN"; - // The names of the handlers on the pipeline: public static final String SSL_HANDLER_NAME = "ssl"; public static final String INBOUND_TRAFFIC_METER_NAME = "inboundTrafficMeter"; @@ -132,14 +115,6 @@ public static int effectiveMaxOrphanRequests( private volatile String clusterName; - /** - * The value of the {@code PRODUCT_TYPE} option reported by the first channel we opened, in - * response to a {@code SUPPORTED} request. - * - *

If the server does not return that option, the value will be {@link #UNKNOWN_PRODUCT_TYPE}. - */ - @VisibleForTesting volatile String productType; - public ChannelFactory(InternalDriverContext context) { this.logPrefix = context.getSessionName(); this.context = context; @@ -296,24 +271,6 @@ private void connect( if (ChannelFactory.this.clusterName == null) { ChannelFactory.this.clusterName = driverChannel.getClusterName(); } - Map> supportedOptions = driverChannel.getOptions(); - if (ChannelFactory.this.productType == null && supportedOptions != null) { - List productTypes = supportedOptions.get("PRODUCT_TYPE"); - String productType = - productTypes != null && !productTypes.isEmpty() - ? productTypes.get(0) - : UNKNOWN_PRODUCT_TYPE; - ChannelFactory.this.productType = productType; - DriverConfig driverConfig = context.getConfig(); - if (driverConfig instanceof TypesafeDriverConfig - && productType.equals(DATASTAX_CLOUD_PRODUCT_TYPE)) { - ((TypesafeDriverConfig) driverConfig) - .overrideDefaults( - ImmutableMap.of( - DefaultDriverOption.REQUEST_CONSISTENCY, - ConsistencyLevel.LOCAL_QUORUM.name())); - } - } resultFuture.complete(driverChannel); } else { Throwable error = connectFuture.cause(); diff --git a/core/src/main/java/com/datastax/oss/driver/internal/core/config/cloud/CloudConfig.java b/core/src/main/java/com/datastax/oss/driver/internal/core/config/cloud/CloudConfig.java deleted file mode 100644 index 1a1076e9d78..00000000000 --- a/core/src/main/java/com/datastax/oss/driver/internal/core/config/cloud/CloudConfig.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 com.datastax.oss.driver.internal.core.config.cloud; - -import com.datastax.oss.driver.api.core.metadata.EndPoint; -import com.datastax.oss.driver.api.core.ssl.SslEngineFactory; -import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableList; -import edu.umd.cs.findbugs.annotations.NonNull; -import java.net.InetSocketAddress; -import java.util.List; -import net.jcip.annotations.ThreadSafe; - -@ThreadSafe -public class CloudConfig { - - private final InetSocketAddress proxyAddress; - private final List endPoints; - private final String localDatacenter; - private final SslEngineFactory sslEngineFactory; - - CloudConfig( - @NonNull InetSocketAddress proxyAddress, - @NonNull List endPoints, - @NonNull String localDatacenter, - @NonNull SslEngineFactory sslEngineFactory) { - this.proxyAddress = proxyAddress; - this.endPoints = ImmutableList.copyOf(endPoints); - this.localDatacenter = localDatacenter; - this.sslEngineFactory = sslEngineFactory; - } - - @NonNull - public InetSocketAddress getProxyAddress() { - return proxyAddress; - } - - @NonNull - public List getEndPoints() { - return endPoints; - } - - @NonNull - public String getLocalDatacenter() { - return localDatacenter; - } - - @NonNull - public SslEngineFactory getSslEngineFactory() { - return sslEngineFactory; - } -} diff --git a/core/src/main/java/com/datastax/oss/driver/internal/core/config/cloud/CloudConfigFactory.java b/core/src/main/java/com/datastax/oss/driver/internal/core/config/cloud/CloudConfigFactory.java deleted file mode 100644 index 817b3263d25..00000000000 --- a/core/src/main/java/com/datastax/oss/driver/internal/core/config/cloud/CloudConfigFactory.java +++ /dev/null @@ -1,296 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 com.datastax.oss.driver.internal.core.config.cloud; - -import com.datastax.oss.driver.api.core.metadata.EndPoint; -import com.datastax.oss.driver.internal.core.metadata.SniEndPoint; -import com.datastax.oss.driver.internal.core.ssl.SniSslEngineFactory; -import com.datastax.oss.driver.shaded.guava.common.io.ByteStreams; -import com.datastax.oss.driver.shaded.guava.common.net.HostAndPort; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import edu.umd.cs.findbugs.annotations.NonNull; -import java.io.BufferedReader; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.net.ConnectException; -import java.net.InetSocketAddress; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.UnknownHostException; -import java.nio.charset.StandardCharsets; -import java.security.GeneralSecurityException; -import java.security.KeyStore; -import java.security.SecureRandom; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManagerFactory; -import net.jcip.annotations.ThreadSafe; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -@ThreadSafe -public class CloudConfigFactory { - private static final Logger LOG = LoggerFactory.getLogger(CloudConfigFactory.class); - /** - * Creates a {@link CloudConfig} with information fetched from the specified Cloud configuration - * URL. - * - *

The target URL must point to a valid secure connect bundle archive in ZIP format. - * - * @param cloudConfigUrl the URL to fetch the Cloud configuration from; cannot be null. - * @throws IOException If the Cloud configuration cannot be read. - * @throws GeneralSecurityException If the Cloud SSL context cannot be created. - */ - @NonNull - public CloudConfig createCloudConfig(@NonNull URL cloudConfigUrl) - throws IOException, GeneralSecurityException { - Objects.requireNonNull(cloudConfigUrl, "cloudConfigUrl cannot be null"); - return createCloudConfig(cloudConfigUrl.openStream()); - } - - /** - * Creates a {@link CloudConfig} with information fetched from the specified {@link InputStream}. - * - *

The stream must contain a valid secure connect bundle archive in ZIP format. Note that the - * stream will be closed after a call to that method and cannot be used anymore. - * - * @param cloudConfig the stream to read the Cloud configuration from; cannot be null. - * @throws IOException If the Cloud configuration cannot be read. - * @throws GeneralSecurityException If the Cloud SSL context cannot be created. - */ - @NonNull - public CloudConfig createCloudConfig(@NonNull InputStream cloudConfig) - throws IOException, GeneralSecurityException { - Objects.requireNonNull(cloudConfig, "cloudConfig cannot be null"); - JsonNode configJson = null; - ByteArrayOutputStream keyStoreOutputStream = null; - ByteArrayOutputStream trustStoreOutputStream = null; - ObjectMapper mapper = new ObjectMapper().configure(JsonParser.Feature.AUTO_CLOSE_SOURCE, false); - try (ZipInputStream zipInputStream = new ZipInputStream(cloudConfig)) { - ZipEntry entry; - while ((entry = zipInputStream.getNextEntry()) != null) { - String fileName = entry.getName(); - switch (fileName) { - case "config.json": - configJson = mapper.readTree(zipInputStream); - break; - case "identity.jks": - keyStoreOutputStream = new ByteArrayOutputStream(); - ByteStreams.copy(zipInputStream, keyStoreOutputStream); - break; - case "trustStore.jks": - trustStoreOutputStream = new ByteArrayOutputStream(); - ByteStreams.copy(zipInputStream, trustStoreOutputStream); - break; - } - } - } - if (configJson == null) { - throw new IllegalStateException("Invalid bundle: missing file config.json"); - } - if (keyStoreOutputStream == null) { - throw new IllegalStateException("Invalid bundle: missing file identity.jks"); - } - if (trustStoreOutputStream == null) { - throw new IllegalStateException("Invalid bundle: missing file trustStore.jks"); - } - char[] keyStorePassword = getKeyStorePassword(configJson); - char[] trustStorePassword = getTrustStorePassword(configJson); - ByteArrayInputStream keyStoreInputStream = - new ByteArrayInputStream(keyStoreOutputStream.toByteArray()); - ByteArrayInputStream trustStoreInputStream = - new ByteArrayInputStream(trustStoreOutputStream.toByteArray()); - SSLContext sslContext = - createSslContext( - keyStoreInputStream, keyStorePassword, trustStoreInputStream, trustStorePassword); - URL metadataServiceUrl = getMetadataServiceUrl(configJson); - JsonNode proxyMetadataJson; - try (BufferedReader proxyMetadata = fetchProxyMetadata(metadataServiceUrl, sslContext)) { - proxyMetadataJson = mapper.readTree(proxyMetadata); - } - InetSocketAddress sniProxyAddress = getSniProxyAddress(proxyMetadataJson); - List endPoints = getEndPoints(proxyMetadataJson, sniProxyAddress); - String localDatacenter = getLocalDatacenter(proxyMetadataJson); - SniSslEngineFactory sslEngineFactory = new SniSslEngineFactory(sslContext); - validateIfBundleContainsUsernamePassword(configJson); - return new CloudConfig(sniProxyAddress, endPoints, localDatacenter, sslEngineFactory); - } - - @NonNull - protected char[] getKeyStorePassword(JsonNode configFile) { - if (configFile.has("keyStorePassword")) { - return configFile.get("keyStorePassword").asText().toCharArray(); - } else { - throw new IllegalStateException("Invalid config.json: missing field keyStorePassword"); - } - } - - @NonNull - protected char[] getTrustStorePassword(JsonNode configFile) { - if (configFile.has("trustStorePassword")) { - return configFile.get("trustStorePassword").asText().toCharArray(); - } else { - throw new IllegalStateException("Invalid config.json: missing field trustStorePassword"); - } - } - - @NonNull - protected URL getMetadataServiceUrl(JsonNode configFile) throws MalformedURLException { - if (configFile.has("host")) { - String metadataServiceHost = configFile.get("host").asText(); - if (configFile.has("port")) { - int metadataServicePort = configFile.get("port").asInt(); - return new URL("https", metadataServiceHost, metadataServicePort, "/metadata"); - } else { - throw new IllegalStateException("Invalid config.json: missing field port"); - } - } else { - throw new IllegalStateException("Invalid config.json: missing field host"); - } - } - - protected void validateIfBundleContainsUsernamePassword(JsonNode configFile) { - if (configFile.has("username") || configFile.has("password")) { - LOG.info( - "The bundle contains config.json with username and/or password. Providing it in the bundle is deprecated and ignored."); - } - } - - @NonNull - protected SSLContext createSslContext( - @NonNull ByteArrayInputStream keyStoreInputStream, - @NonNull char[] keyStorePassword, - @NonNull ByteArrayInputStream trustStoreInputStream, - @NonNull char[] trustStorePassword) - throws IOException, GeneralSecurityException { - KeyManagerFactory kmf = createKeyManagerFactory(keyStoreInputStream, keyStorePassword); - TrustManagerFactory tmf = createTrustManagerFactory(trustStoreInputStream, trustStorePassword); - SSLContext sslContext = SSLContext.getInstance("SSL"); - sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom()); - return sslContext; - } - - @NonNull - protected KeyManagerFactory createKeyManagerFactory( - @NonNull InputStream keyStoreInputStream, @NonNull char[] keyStorePassword) - throws IOException, GeneralSecurityException { - KeyStore ks = KeyStore.getInstance("JKS"); - ks.load(keyStoreInputStream, keyStorePassword); - KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); - kmf.init(ks, keyStorePassword); - Arrays.fill(keyStorePassword, (char) 0); - return kmf; - } - - @NonNull - protected TrustManagerFactory createTrustManagerFactory( - @NonNull InputStream trustStoreInputStream, @NonNull char[] trustStorePassword) - throws IOException, GeneralSecurityException { - KeyStore ts = KeyStore.getInstance("JKS"); - ts.load(trustStoreInputStream, trustStorePassword); - TrustManagerFactory tmf = - TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); - tmf.init(ts); - Arrays.fill(trustStorePassword, (char) 0); - return tmf; - } - - @NonNull - protected BufferedReader fetchProxyMetadata( - @NonNull URL metadataServiceUrl, @NonNull SSLContext sslContext) throws IOException { - try { - HttpsURLConnection connection = (HttpsURLConnection) metadataServiceUrl.openConnection(); - connection.setSSLSocketFactory(sslContext.getSocketFactory()); - connection.setRequestMethod("GET"); - return new BufferedReader( - new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8)); - } catch (ConnectException e) { - throw new IllegalStateException( - "Unable to connect to cloud metadata service. Please make sure your cluster is not parked or terminated", - e); - } catch (UnknownHostException e) { - throw new IllegalStateException( - "Unable to resolve host for cloud metadata service. Please make sure your cluster is not terminated", - e); - } - } - - @NonNull - protected String getLocalDatacenter(@NonNull JsonNode proxyMetadata) { - JsonNode contactInfo = getContactInfo(proxyMetadata); - if (contactInfo.has("local_dc")) { - return contactInfo.get("local_dc").asText(); - } else { - throw new IllegalStateException("Invalid proxy metadata: missing field local_dc"); - } - } - - @NonNull - protected InetSocketAddress getSniProxyAddress(@NonNull JsonNode proxyMetadata) { - JsonNode contactInfo = getContactInfo(proxyMetadata); - if (contactInfo.has("sni_proxy_address")) { - HostAndPort sniProxyHostAndPort = - HostAndPort.fromString(contactInfo.get("sni_proxy_address").asText()); - if (!sniProxyHostAndPort.hasPort()) { - throw new IllegalStateException( - "Invalid proxy metadata: missing port from field sni_proxy_address"); - } - return InetSocketAddress.createUnresolved( - sniProxyHostAndPort.getHost(), sniProxyHostAndPort.getPort()); - } else { - throw new IllegalStateException("Invalid proxy metadata: missing field sni_proxy_address"); - } - } - - @NonNull - protected List getEndPoints( - @NonNull JsonNode proxyMetadata, @NonNull InetSocketAddress sniProxyAddress) { - JsonNode contactInfo = getContactInfo(proxyMetadata); - if (contactInfo.has("contact_points")) { - List endPoints = new ArrayList<>(); - JsonNode hostIdsJson = contactInfo.get("contact_points"); - for (int i = 0; i < hostIdsJson.size(); i++) { - endPoints.add(new SniEndPoint(sniProxyAddress, hostIdsJson.get(i).asText())); - } - return endPoints; - } else { - throw new IllegalStateException("Invalid proxy metadata: missing field contact_points"); - } - } - - @NonNull - protected JsonNode getContactInfo(@NonNull JsonNode proxyMetadata) { - if (proxyMetadata.has("contact_info")) { - return proxyMetadata.get("contact_info"); - } else { - throw new IllegalStateException("Invalid proxy metadata: missing field contact_info"); - } - } -} diff --git a/core/src/main/java/com/datastax/oss/driver/internal/core/context/DefaultDriverConfigReporter.java b/core/src/main/java/com/datastax/oss/driver/internal/core/context/DefaultDriverConfigReporter.java index f48d686573c..84426d5fe56 100644 --- a/core/src/main/java/com/datastax/oss/driver/internal/core/context/DefaultDriverConfigReporter.java +++ b/core/src/main/java/com/datastax/oss/driver/internal/core/context/DefaultDriverConfigReporter.java @@ -41,7 +41,6 @@ import com.datastax.oss.driver.internal.core.specex.NoSpeculativeExecutionPolicy; import com.datastax.oss.driver.internal.core.ssl.DefaultSslEngineFactory; import com.datastax.oss.driver.internal.core.ssl.JdkSslHandlerFactory; -import com.datastax.oss.driver.internal.core.ssl.SniSslEngineFactory; import com.datastax.oss.driver.internal.core.ssl.SslHandlerFactory; import com.datastax.oss.driver.internal.core.time.AtomicTimestampGenerator; import com.datastax.oss.driver.internal.core.time.ServerSideTimestampGenerator; @@ -1137,10 +1136,6 @@ private static Optional hostnameValidation(@Nullable SslEngineFactory e } else if (engineFactory instanceof ProgrammaticSslEngineFactory) { return Optional.of( ((ProgrammaticSslEngineFactory) engineFactory).isHostnameValidationRequired()); - } else if (engineFactory instanceof SniSslEngineFactory) { - // No accessor to read: SniSslEngineFactory sets the "HTTPS" endpoint identification algorithm - // on every engine it builds, unconditionally. - return Optional.of(true); } return Optional.empty(); } diff --git a/core/src/main/java/com/datastax/oss/driver/internal/core/context/DefaultDriverContext.java b/core/src/main/java/com/datastax/oss/driver/internal/core/context/DefaultDriverContext.java index e6323b6b84c..58c61fb13d0 100644 --- a/core/src/main/java/com/datastax/oss/driver/internal/core/context/DefaultDriverContext.java +++ b/core/src/main/java/com/datastax/oss/driver/internal/core/context/DefaultDriverContext.java @@ -62,7 +62,6 @@ import com.datastax.oss.driver.internal.core.config.typesafe.TypesafeDriverConfig; import com.datastax.oss.driver.internal.core.control.ControlConnection; import com.datastax.oss.driver.internal.core.metadata.ClientRoutesTopologyMonitor; -import com.datastax.oss.driver.internal.core.metadata.CloudTopologyMonitor; import com.datastax.oss.driver.internal.core.metadata.DefaultTopologyMonitor; import com.datastax.oss.driver.internal.core.metadata.LoadBalancingPolicyWrapper; import com.datastax.oss.driver.internal.core.metadata.MetadataManager; @@ -114,7 +113,6 @@ import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; import io.netty.buffer.ByteBuf; -import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -254,7 +252,6 @@ public class DefaultDriverContext implements InternalDriverContext { private final Map> nodeFiltersFromBuilder; private final Map nodeDistanceEvaluatorsFromBuilder; private final ClassLoader classLoader; - private final InetSocketAddress cloudProxyAddress; private final ClientRoutesConfig clientRoutesConfigFromBuilder; private final LazyReference requestLogFormatterRef = new LazyReference<>("requestLogFormatter", this::buildRequestLogFormatter, cycleDetector); @@ -315,7 +312,6 @@ public DefaultDriverContext( this.nodeFiltersFromBuilder = nodeFilters; this.nodeDistanceEvaluatorsFromBuilder = programmaticArguments.getNodeDistanceEvaluators(); this.classLoader = programmaticArguments.getClassLoader(); - this.cloudProxyAddress = programmaticArguments.getCloudProxyAddress(); this.clientRoutesConfigFromBuilder = programmaticArguments.getClientRoutesConfig(); this.startupClientId = programmaticArguments.getStartupClientId(); this.startupApplicationName = programmaticArguments.getStartupApplicationName(); @@ -677,9 +673,6 @@ protected TopologyMonitor buildTopologyMonitor() { ClientRoutesConfig clientRoutesConfig = resolveClientRoutesConfig(); validateClientRoutesConfiguration(clientRoutesConfig); - if (cloudProxyAddress != null) { - return new CloudTopologyMonitor(this, cloudProxyAddress); - } if (clientRoutesConfig != null) { return new ClientRoutesTopologyMonitor(this, clientRoutesConfig); } @@ -690,12 +683,6 @@ private void validateClientRoutesConfiguration(ClientRoutesConfig clientRoutesCo if (clientRoutesConfig == null) { return; } - if (cloudProxyAddress != null) { - throw new IllegalStateException( - "Both a secure connect bundle and client routes configuration were provided. " - + "They are mutually exclusive. Please use either a secure connect bundle OR " - + "client routes configuration, but not both."); - } DriverExecutionProfile defaultProfile = getConfig().getDefaultProfile(); if (defaultProfile.isDefined(DefaultDriverOption.ADDRESS_TRANSLATOR_CLASS)) { String className = defaultProfile.getString(DefaultDriverOption.ADDRESS_TRANSLATOR_CLASS); diff --git a/core/src/main/java/com/datastax/oss/driver/internal/core/metadata/CloudTopologyMonitor.java b/core/src/main/java/com/datastax/oss/driver/internal/core/metadata/CloudTopologyMonitor.java deleted file mode 100644 index 021824a9b16..00000000000 --- a/core/src/main/java/com/datastax/oss/driver/internal/core/metadata/CloudTopologyMonitor.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 com.datastax.oss.driver.internal.core.metadata; - -import com.datastax.oss.driver.api.core.metadata.EndPoint; -import com.datastax.oss.driver.internal.core.adminrequest.AdminRow; -import com.datastax.oss.driver.internal.core.context.InternalDriverContext; -import edu.umd.cs.findbugs.annotations.NonNull; -import edu.umd.cs.findbugs.annotations.Nullable; -import java.net.InetSocketAddress; -import java.util.Objects; -import java.util.UUID; - -public class CloudTopologyMonitor extends DefaultTopologyMonitor { - - private final InetSocketAddress cloudProxyAddress; - - public CloudTopologyMonitor(InternalDriverContext context, InetSocketAddress cloudProxyAddress) { - super(context); - this.cloudProxyAddress = cloudProxyAddress; - } - - @NonNull - @Override - protected EndPoint buildNodeEndPoint( - @NonNull AdminRow row, - @Nullable InetSocketAddress broadcastRpcAddress, - @NonNull EndPoint localEndPoint) { - UUID hostId = Objects.requireNonNull(row.getUuid("host_id")); - return new SniEndPoint(cloudProxyAddress, hostId.toString()); - } -} diff --git a/core/src/main/java/com/datastax/oss/driver/internal/core/metadata/SniEndPoint.java b/core/src/main/java/com/datastax/oss/driver/internal/core/metadata/SniEndPoint.java deleted file mode 100644 index d1ab8eec98d..00000000000 --- a/core/src/main/java/com/datastax/oss/driver/internal/core/metadata/SniEndPoint.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 com.datastax.oss.driver.internal.core.metadata; - -import com.datastax.oss.driver.api.core.metadata.EndPoint; -import com.datastax.oss.driver.shaded.guava.common.primitives.UnsignedBytes; -import edu.umd.cs.findbugs.annotations.NonNull; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.UnknownHostException; -import java.util.Arrays; -import java.util.Comparator; -import java.util.Objects; -import java.util.concurrent.atomic.AtomicInteger; - -public class SniEndPoint implements EndPoint { - private static final AtomicInteger OFFSET = new AtomicInteger(); - - private final InetSocketAddress proxyAddress; - private final String serverName; - - /** - * @param proxyAddress the address of the proxy. If it is {@linkplain - * InetSocketAddress#isUnresolved() unresolved}, each call to {@link #resolve()} will - * re-resolve it, fetch all of its A-records, and if there are more than 1 pick one in a - * round-robin fashion. - * @param serverName the SNI server name. In the context of Cloud, this is the string - * representation of the host id. - */ - public SniEndPoint(InetSocketAddress proxyAddress, String serverName) { - this.proxyAddress = Objects.requireNonNull(proxyAddress, "SNI address cannot be null"); - this.serverName = Objects.requireNonNull(serverName, "SNI Server name cannot be null"); - } - - public String getServerName() { - return serverName; - } - - @NonNull - @Override - public InetSocketAddress resolve() { - try { - InetAddress[] aRecords = InetAddress.getAllByName(proxyAddress.getHostName()); - if (aRecords.length == 0) { - // Probably never happens, but the JDK docs don't explicitly say so - throw new IllegalArgumentException( - "Could not resolve proxy address " + proxyAddress.getHostName()); - } - // The order of the returned address is unspecified. Sort by IP to make sure we get a true - // round-robin - Arrays.sort(aRecords, IP_COMPARATOR); - int index = - (aRecords.length == 1) - ? 0 - : OFFSET.getAndUpdate(x -> x == Integer.MAX_VALUE ? 0 : x + 1) % aRecords.length; - return new InetSocketAddress(aRecords[index], proxyAddress.getPort()); - } catch (UnknownHostException e) { - throw new IllegalArgumentException( - "Could not resolve proxy address " + proxyAddress.getHostName(), e); - } - } - - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } else if (other instanceof SniEndPoint) { - SniEndPoint that = (SniEndPoint) other; - return this.proxyAddress.equals(that.proxyAddress) && this.serverName.equals(that.serverName); - } else { - return false; - } - } - - @Override - public int hashCode() { - return Objects.hash(proxyAddress, serverName); - } - - @Override - public String toString() { - // Note that this uses the original proxy address, so if there are multiple A-records it won't - // show which one was selected. If that turns out to be a problem for debugging, we might need - // to store the result of resolve() in Connection and log that instead of the endpoint. - return proxyAddress.toString() + ":" + serverName; - } - - @NonNull - @Override - public String asMetricPrefix() { - String hostString = proxyAddress.getHostString(); - if (hostString == null) { - throw new IllegalArgumentException( - "Could not extract a host string from provided proxy address " + proxyAddress); - } - return hostString.replace('.', '_') + ':' + proxyAddress.getPort() + '_' + serverName; - } - - @SuppressWarnings("UnnecessaryLambda") - private static final Comparator IP_COMPARATOR = - (InetAddress address1, InetAddress address2) -> - UnsignedBytes.lexicographicalComparator() - .compare(address1.getAddress(), address2.getAddress()); -} diff --git a/core/src/main/java/com/datastax/oss/driver/internal/core/ssl/SniSslEngineFactory.java b/core/src/main/java/com/datastax/oss/driver/internal/core/ssl/SniSslEngineFactory.java deleted file mode 100644 index 4d2cb69fbfc..00000000000 --- a/core/src/main/java/com/datastax/oss/driver/internal/core/ssl/SniSslEngineFactory.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 com.datastax.oss.driver.internal.core.ssl; - -import com.datastax.oss.driver.api.core.metadata.EndPoint; -import com.datastax.oss.driver.api.core.ssl.SslEngineFactory; -import com.datastax.oss.driver.internal.core.metadata.SniEndPoint; -import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableList; -import edu.umd.cs.findbugs.annotations.NonNull; -import java.net.InetSocketAddress; -import java.util.concurrent.CopyOnWriteArrayList; -import javax.net.ssl.SNIHostName; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLParameters; - -public class SniSslEngineFactory implements SslEngineFactory { - - // An offset that gets added to our "fake" ports (see below). We pick this value because it is the - // start of the ephemeral port range. - private static final int FAKE_PORT_OFFSET = 49152; - - private final SSLContext sslContext; - private final CopyOnWriteArrayList fakePorts = new CopyOnWriteArrayList<>(); - private final boolean allowDnsReverseLookupSan; - - public SniSslEngineFactory(SSLContext sslContext) { - this(sslContext, true); - } - - public SniSslEngineFactory(SSLContext sslContext, boolean allowDnsReverseLookupSan) { - this.sslContext = sslContext; - this.allowDnsReverseLookupSan = allowDnsReverseLookupSan; - } - - @NonNull - @Override - public SSLEngine newSslEngine(@NonNull EndPoint remoteEndpoint) { - if (!(remoteEndpoint instanceof SniEndPoint)) { - throw new IllegalArgumentException( - String.format( - "Configuration error: can only use %s with SNI end points", - this.getClass().getSimpleName())); - } - SniEndPoint sniEndPoint = (SniEndPoint) remoteEndpoint; - InetSocketAddress address = sniEndPoint.resolve(); - String sniServerName = sniEndPoint.getServerName(); - - // When hostname verification is enabled (with setEndpointIdentificationAlgorithm), the SSL - // engine will try to match the server's certificate against the SNI host name; if that doesn't - // work, it will fall back to the "advisory peer host" passed to createSSLEngine. - // - // In our case, the first check will never succeed because our SNI host name is not the DNS name - // (we use the Cassandra host_id instead). So we *must* set the advisory peer information. - // - // However if we use the address as-is, this leads to another issue: the advisory peer - // information is also used to cache SSL sessions internally. All of our nodes share the same - // proxy address, so the JDK tries to reuse SSL sessions across nodes. But it doesn't update the - // SNI host name every time, so it ends up opening connections to the wrong node. - // - // To avoid that, we create a unique "fake" port for every node. We still get session reuse for - // a given node, but not across nodes. This is safe because the advisory port is only used for - // session caching. - String peerHost = allowDnsReverseLookupSan ? address.getHostName() : address.getHostString(); - SSLEngine engine = sslContext.createSSLEngine(peerHost, getFakePort(sniServerName)); - engine.setUseClientMode(true); - SSLParameters parameters = engine.getSSLParameters(); - parameters.setServerNames(ImmutableList.of(new SNIHostName(sniServerName))); - parameters.setEndpointIdentificationAlgorithm("HTTPS"); - engine.setSSLParameters(parameters); - return engine; - } - - private int getFakePort(String sniServerName) { - fakePorts.addIfAbsent(sniServerName); - return FAKE_PORT_OFFSET + fakePorts.indexOf(sniServerName); - } - - @Override - public void close() { - // nothing to do - } -} diff --git a/core/src/main/resources/reference.conf b/core/src/main/resources/reference.conf index 590784b70c5..3850c495bcc 100644 --- a/core/src/main/resources/reference.conf +++ b/core/src/main/resources/reference.conf @@ -257,20 +257,6 @@ datastax-java-driver { # If this option is not defined, the driver defaults to true. slow-replica-avoidance = true } - basic.cloud { - # The location of the cloud secure bundle used to connect to DataStax Apache Cassandra as a - # service. - # This setting must be a valid URL. - # If the protocol is not specified, it is implicitly assumed to be the `file://` protocol, - # in which case the value is expected to be a valid path on the local filesystem. - # For example, `/a/path/to/bundle` will be interpreted as `file:/a/path/to/bunde`. - # If the protocol is provided explicitly, then the value will be used as is. - # - # Required: no - # Modifiable at runtime: no - # Overridable in a profile: no - // secure-connect-bundle = /location/of/secure/connect/bundle - } # DataStax Insights monitoring. basic.application { diff --git a/core/src/test/java/com/datastax/oss/driver/api/core/data/CqlVectorTest.java b/core/src/test/java/com/datastax/oss/driver/api/core/data/CqlVectorTest.java index 3e0872cb946..99986e3394d 100644 --- a/core/src/test/java/com/datastax/oss/driver/api/core/data/CqlVectorTest.java +++ b/core/src/test/java/com/datastax/oss/driver/api/core/data/CqlVectorTest.java @@ -37,8 +37,6 @@ import java.util.Iterator; import java.util.List; import java.util.stream.Collectors; -import org.apache.commons.codec.DecoderException; -import org.apache.commons.codec.binary.Hex; import org.assertj.core.util.Lists; import org.junit.Test; import org.junit.runner.RunWith; @@ -242,12 +240,12 @@ public int size() { } @Test - public void should_not_use_preallocate_serialized_size() throws DecoderException { + public void should_not_use_preallocate_serialized_size() { // serialized CqlVector(1.0f, 2.5f, 3.0f) with size field adjusted to Integer.MAX_VALUE byte[] suspiciousBytes = - Hex.decodeHex( - "aced000573720042636f6d2e64617461737461782e6f73732e6472697665722e6170692e636f72652e646174612e43716c566563746f722453657269616c697a6174696f6e50726f78790000000000000001030000787077047fffffff7372000f6a6176612e6c616e672e466c6f6174daedc9a2db3cf0ec02000146000576616c7565787200106a6176612e6c616e672e4e756d62657286ac951d0b94e08b02000078703f8000007371007e0002402000007371007e00024040000078" - .toCharArray()); + ByteUtils.getArray( + ByteUtils.fromHexString( + "0xaced000573720042636f6d2e64617461737461782e6f73732e6472697665722e6170692e636f72652e646174612e43716c566563746f722453657269616c697a6174696f6e50726f78790000000000000001030000787077047fffffff7372000f6a6176612e6c616e672e466c6f6174daedc9a2db3cf0ec02000146000576616c7565787200106a6176612e6c616e672e4e756d62657286ac951d0b94e08b02000078703f8000007371007e0002402000007371007e00024040000078")); try { new ObjectInputStream(new ByteArrayInputStream(suspiciousBytes)).readObject(); fail("Should not be able to deserialize bytes with incorrect size field"); diff --git a/core/src/test/java/com/datastax/oss/driver/internal/core/channel/ChannelFactoryClusterNameTest.java b/core/src/test/java/com/datastax/oss/driver/internal/core/channel/ChannelFactoryClusterNameTest.java index 4734a8ffdeb..166e79cdccb 100644 --- a/core/src/test/java/com/datastax/oss/driver/internal/core/channel/ChannelFactoryClusterNameTest.java +++ b/core/src/test/java/com/datastax/oss/driver/internal/core/channel/ChannelFactoryClusterNameTest.java @@ -86,6 +86,8 @@ public void should_check_cluster_name_for_next_connections() throws Throwable { null, DriverChannelOptions.DEFAULT, NoopNodeMetricUpdater.INSTANCE); + writeInboundFrame( + readOutboundFrame(), TestResponses.supportedResponse("mock_key", "mock_value")); writeInboundFrame(readOutboundFrame(), new Ready()); writeInboundFrame(readOutboundFrame(), TestResponses.clusterNameResponse("mockClusterName")); @@ -101,6 +103,8 @@ public void should_check_cluster_name_for_next_connections() throws Throwable { null, DriverChannelOptions.DEFAULT, NoopNodeMetricUpdater.INSTANCE); + writeInboundFrame( + readOutboundFrame(), TestResponses.supportedResponse("mock_key", "mock_value")); writeInboundFrame(readOutboundFrame(), new Ready()); writeInboundFrame(readOutboundFrame(), TestResponses.clusterNameResponse("wrongClusterName")); diff --git a/core/src/test/java/com/datastax/oss/driver/internal/core/channel/ChannelFactorySupportedOptionsTest.java b/core/src/test/java/com/datastax/oss/driver/internal/core/channel/ChannelFactorySupportedOptionsTest.java index 87407921619..20f63291d37 100644 --- a/core/src/test/java/com/datastax/oss/driver/internal/core/channel/ChannelFactorySupportedOptionsTest.java +++ b/core/src/test/java/com/datastax/oss/driver/internal/core/channel/ChannelFactorySupportedOptionsTest.java @@ -32,7 +32,7 @@ public class ChannelFactorySupportedOptionsTest extends ChannelFactoryTestBase { @Test - public void should_query_supported_options_on_first_channel() throws Throwable { + public void should_query_supported_options_on_every_channel() throws Throwable { // Given when(defaultProfile.isDefined(DefaultDriverOption.PROTOCOL_VERSION)).thenReturn(false); when(protocolVersionRegistry.highestNonBeta()).thenReturn(DefaultProtocolVersion.V4); @@ -65,12 +65,15 @@ public void should_query_supported_options_on_first_channel() throws Throwable { null, DriverChannelOptions.DEFAULT, NoopNodeMetricUpdater.INSTANCE); + writeInboundFrame( + readOutboundFrame(), TestResponses.supportedResponse("mock_key", "mock_value")); writeInboundFrame(readOutboundFrame(), new Ready()); writeInboundFrame(readOutboundFrame(), TestResponses.clusterNameResponse("mockClusterName")); // Then assertThatStage(channelFuture2).isSuccess(); DriverChannel channel2 = channelFuture2.toCompletableFuture().get(); - assertThat(channel2.getOptions()).isNull(); + assertThat(channel2.getOptions()).containsKey("mock_key"); + assertThat(channel2.getOptions().get("mock_key")).containsOnly("mock_value"); } } diff --git a/core/src/test/java/com/datastax/oss/driver/internal/core/channel/ChannelFactoryTestBase.java b/core/src/test/java/com/datastax/oss/driver/internal/core/channel/ChannelFactoryTestBase.java index ed6668a6c83..534c59236ec 100644 --- a/core/src/test/java/com/datastax/oss/driver/internal/core/channel/ChannelFactoryTestBase.java +++ b/core/src/test/java/com/datastax/oss/driver/internal/core/channel/ChannelFactoryTestBase.java @@ -272,7 +272,7 @@ protected void initChannel(Channel channel) throws Exception { endPoint, options, heartbeatHandler, - productType == null); + true); channel .pipeline() .addLast(ChannelFactory.INFLIGHT_HANDLER_NAME, inFlightHandler) diff --git a/core/src/test/java/com/datastax/oss/driver/internal/core/config/cloud/CloudConfigFactoryTest.java b/core/src/test/java/com/datastax/oss/driver/internal/core/config/cloud/CloudConfigFactoryTest.java deleted file mode 100644 index a0db82d298e..00000000000 --- a/core/src/test/java/com/datastax/oss/driver/internal/core/config/cloud/CloudConfigFactoryTest.java +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 com.datastax.oss.driver.internal.core.config.cloud; - -import static com.datastax.oss.driver.Assertions.assertThat; -import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; -import static com.github.tomakehurst.wiremock.client.WireMock.any; -import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; -import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; -import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; -import static org.assertj.core.api.Assertions.catchThrowable; - -import com.datastax.oss.driver.internal.core.ssl.SniSslEngineFactory; -import com.fasterxml.jackson.core.JsonParseException; -import com.github.tomakehurst.wiremock.common.JettySettings; -import com.github.tomakehurst.wiremock.core.Options; -import com.github.tomakehurst.wiremock.http.AdminRequestHandler; -import com.github.tomakehurst.wiremock.http.HttpServer; -import com.github.tomakehurst.wiremock.http.HttpServerFactory; -import com.github.tomakehurst.wiremock.http.StubRequestHandler; -import com.github.tomakehurst.wiremock.jetty9.JettyHttpServer; -import com.github.tomakehurst.wiremock.junit.WireMockRule; -import com.google.common.base.Joiner; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.URISyntaxException; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import org.eclipse.jetty.io.NetworkTrafficListener; -import org.eclipse.jetty.server.ConnectionFactory; -import org.eclipse.jetty.server.ServerConnector; -import org.eclipse.jetty.server.SslConnectionFactory; -import org.eclipse.jetty.util.ssl.SslContextFactory; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.junit.MockitoJUnitRunner; - -@RunWith(MockitoJUnitRunner.class) -public class CloudConfigFactoryTest { - - private static final String BUNDLE_PATH = "/config/cloud/creds.zip"; - - @Rule - public WireMockRule wireMockRule = - new WireMockRule( - wireMockConfig() - .httpsPort(30443) - .dynamicPort() - .httpServerFactory(new HttpsServerFactory()) - .needClientAuth(true) - .keystorePath(path("/config/cloud/identity.jks").toString()) - .keystorePassword("fakePasswordForTests") - .trustStorePath(path("/config/cloud/trustStore.jks").toString()) - .trustStorePassword("fakePasswordForTests2")); - - public CloudConfigFactoryTest() throws URISyntaxException {} - - @Test - public void should_load_config_from_local_filesystem() throws Exception { - // given - URL configFile = getClass().getResource(BUNDLE_PATH); - mockProxyMetadataService(jsonMetadata()); - // when - CloudConfigFactory cloudConfigFactory = new CloudConfigFactory(); - CloudConfig cloudConfig = cloudConfigFactory.createCloudConfig(configFile); - // then - assertCloudConfig(cloudConfig); - } - - @Test - public void should_load_config_from_external_location() throws Exception { - // given - mockHttpSecureBundle(secureBundle()); - mockProxyMetadataService(jsonMetadata()); - // when - URL configFile = new URL("http", "localhost", wireMockRule.port(), BUNDLE_PATH); - CloudConfigFactory cloudConfigFactory = new CloudConfigFactory(); - CloudConfig cloudConfig = cloudConfigFactory.createCloudConfig(configFile); - // then - assertCloudConfig(cloudConfig); - } - - @Test - public void should_throw_when_bundle_not_found() throws Exception { - // given - stubFor(any(urlEqualTo(BUNDLE_PATH)).willReturn(aResponse().withStatus(404))); - // when - URL configFile = new URL("http", "localhost", wireMockRule.port(), BUNDLE_PATH); - CloudConfigFactory cloudConfigFactory = new CloudConfigFactory(); - Throwable t = catchThrowable(() -> cloudConfigFactory.createCloudConfig(configFile)); - assertThat(t) - .isInstanceOf(FileNotFoundException.class) - .hasMessageContaining(configFile.toExternalForm()); - } - - @Test - public void should_throw_when_bundle_not_readable() throws Exception { - // given - mockHttpSecureBundle("not a zip file".getBytes(StandardCharsets.UTF_8)); - // when - URL configFile = new URL("http", "localhost", wireMockRule.port(), BUNDLE_PATH); - CloudConfigFactory cloudConfigFactory = new CloudConfigFactory(); - Throwable t = catchThrowable(() -> cloudConfigFactory.createCloudConfig(configFile)); - assertThat(t) - .isInstanceOf(IllegalStateException.class) - .hasMessageContaining("Invalid bundle: missing file config.json"); - } - - @Test - public void should_throw_when_metadata_not_found() throws Exception { - // given - mockHttpSecureBundle(secureBundle()); - stubFor(any(urlPathEqualTo("/metadata")).willReturn(aResponse().withStatus(404))); - // when - URL configFile = new URL("http", "localhost", wireMockRule.port(), BUNDLE_PATH); - CloudConfigFactory cloudConfigFactory = new CloudConfigFactory(); - Throwable t = catchThrowable(() -> cloudConfigFactory.createCloudConfig(configFile)); - assertThat(t).isInstanceOf(FileNotFoundException.class).hasMessageContaining("metadata"); - } - - @Test - public void should_throw_when_metadata_not_readable() throws Exception { - // given - mockHttpSecureBundle(secureBundle()); - mockProxyMetadataService("not a valid json payload"); - // when - URL configFile = new URL("http", "localhost", wireMockRule.port(), BUNDLE_PATH); - CloudConfigFactory cloudConfigFactory = new CloudConfigFactory(); - Throwable t = catchThrowable(() -> cloudConfigFactory.createCloudConfig(configFile)); - assertThat(t).isInstanceOf(JsonParseException.class).hasMessageContaining("Unrecognized token"); - } - - private void mockHttpSecureBundle(byte[] body) { - stubFor( - any(urlEqualTo(BUNDLE_PATH)) - .willReturn( - aResponse() - .withStatus(200) - .withHeader("Content-Type", "application/octet-stream") - .withBody(body))); - } - - private void mockProxyMetadataService(String jsonMetadata) { - stubFor( - any(urlPathEqualTo("/metadata")) - .willReturn( - aResponse() - .withStatus(200) - .withHeader("Content-Type", "application/json") - .withBody(jsonMetadata))); - } - - private byte[] secureBundle() throws IOException, URISyntaxException { - return Files.readAllBytes(path(BUNDLE_PATH)); - } - - private String jsonMetadata() throws IOException, URISyntaxException { - return Joiner.on('\n') - .join(Files.readAllLines(path("/config/cloud/metadata.json"), StandardCharsets.UTF_8)); - } - - private Path path(String resource) throws URISyntaxException { - return Paths.get(getClass().getResource(resource).toURI()); - } - - private void assertCloudConfig(CloudConfig config) { - InetSocketAddress expectedProxyAddress = InetSocketAddress.createUnresolved("localhost", 30002); - assertThat(config.getLocalDatacenter()).isEqualTo("dc1"); - assertThat(config.getProxyAddress()).isEqualTo(expectedProxyAddress); - assertThat(config.getEndPoints()).extracting("proxyAddress").containsOnly(expectedProxyAddress); - assertThat(config.getEndPoints()) - .extracting("serverName") - .containsExactly( - "4ac06655-f861-49f9-881e-3fee22e69b94", - "2af7c253-3394-4a0d-bfac-f1ad81b5154d", - "b17b6e2a-3f48-4d6a-81c1-20a0a1f3192a"); - assertThat(config.getSslEngineFactory()).isNotNull().isInstanceOf(SniSslEngineFactory.class); - } - - static { - javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier( - (hostname, sslSession) -> hostname.equals("localhost")); - } - - // see https://github.com/tomakehurst/wiremock/issues/874 - private static class HttpsServerFactory implements HttpServerFactory { - @Override - public HttpServer buildHttpServer( - Options options, - AdminRequestHandler adminRequestHandler, - StubRequestHandler stubRequestHandler) { - return new JettyHttpServer(options, adminRequestHandler, stubRequestHandler) { - @Override - protected ServerConnector createServerConnector( - String bindAddress, - JettySettings jettySettings, - int port, - NetworkTrafficListener listener, - ConnectionFactory... connectionFactories) { - if (port == options.httpsSettings().port()) { - SslConnectionFactory sslConnectionFactory = - (SslConnectionFactory) connectionFactories[0]; - SslContextFactory sslContextFactory = sslConnectionFactory.getSslContextFactory(); - sslContextFactory.setKeyStorePassword(options.httpsSettings().keyStorePassword()); - connectionFactories = - new ConnectionFactory[] {sslConnectionFactory, connectionFactories[1]}; - } - return super.createServerConnector( - bindAddress, jettySettings, port, listener, connectionFactories); - } - }; - } - } -} diff --git a/core/src/test/java/com/datastax/oss/driver/internal/core/context/ClientRoutesConfigFromFileTest.java b/core/src/test/java/com/datastax/oss/driver/internal/core/context/ClientRoutesConfigFromFileTest.java index 083302494aa..0c1c570ebee 100644 --- a/core/src/test/java/com/datastax/oss/driver/internal/core/context/ClientRoutesConfigFromFileTest.java +++ b/core/src/test/java/com/datastax/oss/driver/internal/core/context/ClientRoutesConfigFromFileTest.java @@ -27,7 +27,6 @@ import com.datastax.oss.driver.internal.core.config.typesafe.DefaultDriverConfigLoader; import com.datastax.oss.driver.internal.core.metadata.ClientRoutesTopologyMonitor; import com.typesafe.config.ConfigFactory; -import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.List; import org.junit.After; @@ -238,25 +237,6 @@ public void should_allow_client_routes_with_qualified_passthrough_address_transl assertThat(ctx.getTopologyMonitor()).isInstanceOf(ClientRoutesTopologyMonitor.class); } - @Test - public void should_throw_when_secure_connect_bundle_and_client_routes_both_configured() { - ProgrammaticArguments args = - ProgrammaticArguments.builder() - .withCloudProxyAddress(new InetSocketAddress("127.0.0.1", 9042)) - .build(); - DefaultDriverContext ctx = - contextFromHocon( - "advanced.client-routes.endpoints = [" - + " { connection-id = \"11111111-1111-1111-1111-111111111111\" }" - + "]", - args); - - assertThatThrownBy(ctx::getTopologyMonitor) - .isInstanceOf(IllegalStateException.class) - .hasMessageContaining("secure connect bundle") - .hasMessageContaining("client routes"); - } - @Test public void should_throw_when_client_routes_and_unloadable_address_translator_class() { DefaultDriverContext ctx = diff --git a/core/src/test/java/com/datastax/oss/driver/internal/core/context/DefaultDriverConfigReporterTest.java b/core/src/test/java/com/datastax/oss/driver/internal/core/context/DefaultDriverConfigReporterTest.java index 4122f82a399..5616734e178 100644 --- a/core/src/test/java/com/datastax/oss/driver/internal/core/context/DefaultDriverConfigReporterTest.java +++ b/core/src/test/java/com/datastax/oss/driver/internal/core/context/DefaultDriverConfigReporterTest.java @@ -51,7 +51,6 @@ import com.datastax.oss.driver.internal.core.specex.NoSpeculativeExecutionPolicy; import com.datastax.oss.driver.internal.core.ssl.DefaultSslEngineFactory; import com.datastax.oss.driver.internal.core.ssl.JdkSslHandlerFactory; -import com.datastax.oss.driver.internal.core.ssl.SniSslEngineFactory; import com.datastax.oss.driver.internal.core.ssl.SslHandlerFactory; import com.datastax.oss.driver.internal.core.time.AtomicTimestampGenerator; import com.datastax.oss.driver.internal.core.time.ServerSideTimestampGenerator; @@ -1534,7 +1533,6 @@ public void should_report_every_built_in_engine_factory() throws Exception { // configured one, to the whole option-to-field-to-report chain. assertThat(hostnameVerificationOf(new DefaultSslEngineFactory(policyConstructionContext()))) .isTrue(); - assertThat(hostnameVerificationOf(new SniSslEngineFactory(SSLContext.getDefault()))).isTrue(); assertThat(hostnameVerificationOf(new ProgrammaticSslEngineFactory(SSLContext.getDefault()))) .isFalse(); assertThat( diff --git a/core/src/test/java/com/datastax/oss/driver/internal/core/metadata/DefaultTopologyMonitorTest.java b/core/src/test/java/com/datastax/oss/driver/internal/core/metadata/DefaultTopologyMonitorTest.java index 892715a47c3..744438c7f71 100644 --- a/core/src/test/java/com/datastax/oss/driver/internal/core/metadata/DefaultTopologyMonitorTest.java +++ b/core/src/test/java/com/datastax/oss/driver/internal/core/metadata/DefaultTopologyMonitorTest.java @@ -52,10 +52,10 @@ import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableMap; import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableSet; import com.datastax.oss.driver.shaded.guava.common.collect.Maps; +import com.datastax.oss.driver.shaded.guava.common.collect.Streams; import com.datastax.oss.protocol.internal.Message; import com.datastax.oss.protocol.internal.ProtocolConstants; import com.datastax.oss.protocol.internal.response.Error; -import com.google.common.collect.Streams; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; diff --git a/core/src/test/java/com/datastax/oss/driver/internal/core/metadata/schema/TableMetadataTest.java b/core/src/test/java/com/datastax/oss/driver/internal/core/metadata/schema/TableMetadataTest.java index 03d63230992..252ebab38eb 100644 --- a/core/src/test/java/com/datastax/oss/driver/internal/core/metadata/schema/TableMetadataTest.java +++ b/core/src/test/java/com/datastax/oss/driver/internal/core/metadata/schema/TableMetadataTest.java @@ -23,9 +23,9 @@ import com.datastax.oss.driver.api.core.metadata.schema.TableMetadata; import com.datastax.oss.driver.internal.core.type.DefaultVectorType; import com.datastax.oss.driver.internal.core.type.PrimitiveType; +import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableList; import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableMap; import com.datastax.oss.protocol.internal.ProtocolConstants.DataType; -import com.google.common.collect.ImmutableList; import java.util.UUID; import org.junit.Test; diff --git a/core/src/test/java/com/datastax/oss/driver/internal/core/metrics/DefaultMetricIdTest.java b/core/src/test/java/com/datastax/oss/driver/internal/core/metrics/DefaultMetricIdTest.java index 339f9235dc2..8d7998c21f5 100644 --- a/core/src/test/java/com/datastax/oss/driver/internal/core/metrics/DefaultMetricIdTest.java +++ b/core/src/test/java/com/datastax/oss/driver/internal/core/metrics/DefaultMetricIdTest.java @@ -19,7 +19,7 @@ import static org.assertj.core.api.Assertions.assertThat; -import com.google.common.collect.ImmutableMap; +import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableMap; import org.junit.Test; public class DefaultMetricIdTest { diff --git a/core/src/test/java/com/datastax/oss/driver/internal/core/metrics/TaggingMetricIdGeneratorTest.java b/core/src/test/java/com/datastax/oss/driver/internal/core/metrics/TaggingMetricIdGeneratorTest.java index 809a7419ba4..cbc560ee35b 100644 --- a/core/src/test/java/com/datastax/oss/driver/internal/core/metrics/TaggingMetricIdGeneratorTest.java +++ b/core/src/test/java/com/datastax/oss/driver/internal/core/metrics/TaggingMetricIdGeneratorTest.java @@ -28,7 +28,7 @@ import com.datastax.oss.driver.api.core.metrics.DefaultNodeMetric; import com.datastax.oss.driver.api.core.metrics.DefaultSessionMetric; import com.datastax.oss.driver.internal.core.context.InternalDriverContext; -import com.google.common.collect.ImmutableMap; +import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableMap; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; diff --git a/core/src/test/java/com/datastax/oss/driver/internal/core/protocol/BytesToSegmentDecoderTest.java b/core/src/test/java/com/datastax/oss/driver/internal/core/protocol/BytesToSegmentDecoderTest.java index d151da309c1..c493ccc2080 100644 --- a/core/src/test/java/com/datastax/oss/driver/internal/core/protocol/BytesToSegmentDecoderTest.java +++ b/core/src/test/java/com/datastax/oss/driver/internal/core/protocol/BytesToSegmentDecoderTest.java @@ -21,10 +21,10 @@ import static org.assertj.core.api.Assertions.fail; import com.datastax.oss.driver.api.core.connection.CrcMismatchException; +import com.datastax.oss.driver.shaded.guava.common.base.Strings; import com.datastax.oss.protocol.internal.Compressor; import com.datastax.oss.protocol.internal.Segment; import com.datastax.oss.protocol.internal.SegmentCodec; -import com.google.common.base.Strings; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; diff --git a/core/src/test/resources/config/cloud/creds.zip b/core/src/test/resources/config/cloud/creds.zip deleted file mode 100644 index 3b5d1cb1cbd..00000000000 Binary files a/core/src/test/resources/config/cloud/creds.zip and /dev/null differ diff --git a/core/src/test/resources/config/cloud/identity.jks b/core/src/test/resources/config/cloud/identity.jks deleted file mode 100644 index bac5bbaa965..00000000000 Binary files a/core/src/test/resources/config/cloud/identity.jks and /dev/null differ diff --git a/core/src/test/resources/config/cloud/metadata.json b/core/src/test/resources/config/cloud/metadata.json deleted file mode 100644 index 35aa26f67f1..00000000000 --- a/core/src/test/resources/config/cloud/metadata.json +++ /dev/null @@ -1 +0,0 @@ -{"region":"local","contact_info":{"type":"sni_proxy","local_dc":"dc1","contact_points":["4ac06655-f861-49f9-881e-3fee22e69b94","2af7c253-3394-4a0d-bfac-f1ad81b5154d","b17b6e2a-3f48-4d6a-81c1-20a0a1f3192a"],"sni_proxy_address":"localhost:30002"}} diff --git a/core/src/test/resources/config/cloud/trustStore.jks b/core/src/test/resources/config/cloud/trustStore.jks deleted file mode 100644 index 8ee03f97da0..00000000000 Binary files a/core/src/test/resources/config/cloud/trustStore.jks and /dev/null differ diff --git a/examples/src/main/java/com/datastax/oss/driver/examples/astra/AstraReadCassandraVersion.java b/examples/src/main/java/com/datastax/oss/driver/examples/astra/AstraReadCassandraVersion.java deleted file mode 100644 index 7f4833c46d8..00000000000 --- a/examples/src/main/java/com/datastax/oss/driver/examples/astra/AstraReadCassandraVersion.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 com.datastax.oss.driver.examples.astra; - -import com.datastax.oss.driver.api.core.CqlSession; -import com.datastax.oss.driver.api.core.cql.ResultSet; -import com.datastax.oss.driver.api.core.cql.Row; -import java.nio.file.Paths; - -/** - * Connects to a DataStax Astra cluster and extracts basic information from it. - * - *

Preconditions: - * - *

- * - *

Side effects: none. - * - * @see - * Creating an Astra Database (GCP) - * @see - * Providing access to Astra databases (GCP) - * @see - * Obtaining Astra secure connect bundle (GCP) - * @see Java Driver online - * manual - */ -public class AstraReadCassandraVersion { - - public static void main(String[] args) { - - // The Session is what you use to execute queries. It is thread-safe and should be - // reused. - try (CqlSession session = - CqlSession.builder() - // Change the path here to the secure connect bundle location (see javadocs above) - .withCloudSecureConnectBundle(Paths.get("/path/to/secure-connect-database_name.zip")) - // Change the user_name and password here for the Astra instance - .withAuthCredentials("user_name", "fakePasswordForTests") - // Uncomment the next line to use a specific keyspace - // .withKeyspace("keyspace_name") - .build()) { - - // We use execute to send a query to Cassandra. This returns a ResultSet, which - // is essentially a collection of Row objects. - ResultSet rs = session.execute("select release_version from system.local WHERE key='local'"); - // Extract the first row (which is the only one in this case). - Row row = rs.one(); - - // Extract the value of the first (and only) column from the row. - assert row != null; - String releaseVersion = row.getString("release_version"); - System.out.printf("Cassandra version is: %s%n", releaseVersion); - } - // The try-with-resources block automatically close the session after we’re done with it. - // This step is important because it frees underlying resources (TCP connections, thread - // pools...). In a real application, you would typically do this at shutdown - // (for example, when undeploying your webapp). - } -} diff --git a/examples/src/main/java/com/datastax/oss/driver/examples/mapper/KillrVideoMapperExample.java b/examples/src/main/java/com/datastax/oss/driver/examples/mapper/KillrVideoMapperExample.java index 6284b16eac1..0f78821ee07 100644 --- a/examples/src/main/java/com/datastax/oss/driver/examples/mapper/KillrVideoMapperExample.java +++ b/examples/src/main/java/com/datastax/oss/driver/examples/mapper/KillrVideoMapperExample.java @@ -112,9 +112,8 @@ public static void main(String[] args) { Video video = new Video(); video.setUserid(user.getUserid()); - video.setName( - "Getting Started with DataStax Apache Cassandra as a Service on DataStax Astra"); - video.setLocation("https://www.youtube.com/watch?v=68xzKpcZURA"); + video.setName("Getting Started with ScyllaDB Cloud"); + video.setLocation("https://www.youtube.com/watch?v=jnWDMiuy9hM"); Set tags = new HashSet<>(); tags.add("apachecassandra"); tags.add("nosql"); @@ -146,8 +145,7 @@ public static void main(String[] args) { // Update the existing video: Video template = new Video(); template.setVideoid(video.getVideoid()); - template.setName( - "Getting Started with DataStax Apache Cassandra® as a Service on DataStax Astra"); + template.setName("Getting Started with ScyllaDB using the Java driver"); videoDao.update(template); // Reload the whole entity and check the fields video = videoDao.get(video.getVideoid()); diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 7f481242fce..6ce4fc728e9 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -178,11 +178,6 @@ api-ldap-codec-standalone test - - com.github.tomakehurst - wiremock - test - com.scylladb.oss.simulacron simulacron-native-server diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/api/core/cloud/CloudIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/api/core/cloud/CloudIT.java deleted file mode 100644 index 81a191779ba..00000000000 --- a/integration-tests/src/test/java/com/datastax/oss/driver/api/core/cloud/CloudIT.java +++ /dev/null @@ -1,500 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 com.datastax.oss.driver.api.core.cloud; - -import static com.datastax.oss.driver.internal.core.util.LoggerTest.setupTestLogger; -import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; -import static com.github.tomakehurst.wiremock.client.WireMock.any; -import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; -import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.timeout; -import static org.mockito.Mockito.verify; - -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.spi.ILoggingEvent; -import com.datastax.oss.driver.api.core.AllNodesFailedException; -import com.datastax.oss.driver.api.core.CqlSession; -import com.datastax.oss.driver.api.core.auth.AuthenticationException; -import com.datastax.oss.driver.api.core.config.DefaultDriverOption; -import com.datastax.oss.driver.api.core.config.DriverConfigLoader; -import com.datastax.oss.driver.api.core.cql.ResultSet; -import com.datastax.oss.driver.api.core.session.SessionBuilder; -import com.datastax.oss.driver.api.testinfra.session.SessionUtils; -import com.datastax.oss.driver.categories.IsolatedTests; -import com.datastax.oss.driver.internal.core.config.cloud.CloudConfigFactory; -import com.datastax.oss.driver.internal.core.ssl.DefaultSslEngineFactory; -import com.datastax.oss.driver.internal.core.util.LoggerTest; -import com.github.tomakehurst.wiremock.junit.WireMockRule; -import java.io.IOException; -import java.io.InputStream; -import java.net.InetSocketAddress; -import java.net.URL; -import java.nio.file.Files; -import java.nio.file.Path; -import java.security.NoSuchAlgorithmException; -import java.util.Collections; -import java.util.List; -import javax.net.ssl.SSLContext; -import org.junit.ClassRule; -import org.junit.Ignore; -import org.junit.Rule; -import org.junit.Test; -import org.junit.experimental.categories.Category; - -@Category(IsolatedTests.class) -@Ignore("Disabled because it is causing trouble in Jenkins CI") -public class CloudIT { - - private static final String BUNDLE_URL_PATH = "/certs/bundles/creds.zip"; - - @ClassRule public static SniProxyRule proxyRule = new SniProxyRule(); - - // Used only to host the secure connect bundle, for tests that require external URLs - @Rule - public WireMockRule wireMockRule = - new WireMockRule(wireMockConfig().dynamicPort().dynamicHttpsPort()); - - @Test - public void should_connect_to_proxy_using_path() { - ResultSet set; - Path bundle = proxyRule.getProxy().getDefaultBundlePath(); - try (CqlSession session = - CqlSession.builder() - .withAuthCredentials("cassandra", "cassandra") - .withCloudSecureConnectBundle(bundle) - .build()) { - set = session.execute("select * from system.local where key='local'"); - } - assertThat(set).isNotNull(); - } - - @Test - public void should_connect_and_log_info_that_config_json_with_username_password_was_provided() { - ResultSet set; - Path bundle = proxyRule.getProxy().getDefaultBundlePath(); - LoggerTest.LoggerSetup logger = setupTestLogger(CloudConfigFactory.class, Level.INFO); - - try (CqlSession session = - CqlSession.builder() - .withAuthCredentials("cassandra", "cassandra") - .withCloudSecureConnectBundle(bundle) - .build()) { - set = session.execute("select * from system.local where key='local'"); - verify(logger.appender, timeout(500).atLeast(1)) - .doAppend(logger.loggingEventCaptor.capture()); - assertThat( - logger.loggingEventCaptor.getAllValues().stream() - .map(ILoggingEvent::getFormattedMessage)) - .contains( - "The bundle contains config.json with username and/or password. Providing it in the bundle is deprecated and ignored."); - } - assertThat(set).isNotNull(); - } - - @Test - public void - should_fail_with_auth_error_when_connecting_using_bundle_with_username_password_in_config_json() { - Path bundle = proxyRule.getProxy().getDefaultBundlePath(); - - // fails with auth error because username/password from config.json is ignored - AllNodesFailedException exception = null; - try { - CqlSession.builder().withCloudSecureConnectBundle(bundle).build(); - } catch (AllNodesFailedException ex) { - exception = ex; - } - assertThat(exception).isNotNull(); - List errors = exception.getAllErrors().values().iterator().next(); - Throwable firstError = errors.get(0); - assertThat(firstError).isInstanceOf(AuthenticationException.class); - } - - @Test - public void should_connect_to_proxy_without_credentials() { - ResultSet set; - Path bundle = proxyRule.getProxy().getBundleWithoutCredentialsPath(); - try (CqlSession session = - CqlSession.builder() - .withCloudSecureConnectBundle(bundle) - .withAuthCredentials("cassandra", "cassandra") - .build()) { - set = session.execute("select * from system.local where key='local'"); - } - assertThat(set).isNotNull(); - } - - @Test - public void should_connect_to_proxy_using_non_normalized_path() { - Path bundle = proxyRule.getProxy().getBundlesRootPath().resolve("../bundles/creds-v1.zip"); - ResultSet set; - try (CqlSession session = - CqlSession.builder() - .withAuthCredentials("cassandra", "cassandra") - .withCloudSecureConnectBundle(bundle) - .build()) { - set = session.execute("select * from system.local where key='local'"); - } - assertThat(set).isNotNull(); - } - - @Test - public void should_connect_to_proxy_using_input_stream() throws IOException { - InputStream bundle = Files.newInputStream(proxyRule.getProxy().getDefaultBundlePath()); - ResultSet set; - try (CqlSession session = - CqlSession.builder() - .withAuthCredentials("cassandra", "cassandra") - .withCloudSecureConnectBundle(bundle) - .build()) { - set = session.execute("select * from system.local where key='local'"); - } - assertThat(set).isNotNull(); - } - - @Test - public void should_connect_to_proxy_using_URL() throws IOException { - // given - byte[] bundle = Files.readAllBytes(proxyRule.getProxy().getDefaultBundlePath()); - stubFor( - any(urlEqualTo(BUNDLE_URL_PATH)) - .willReturn( - aResponse() - .withStatus(200) - .withHeader("Content-Type", "application/octet-stream") - .withBody(bundle))); - URL bundleUrl = - new URL(String.format("http://localhost:%d%s", wireMockRule.port(), BUNDLE_URL_PATH)); - - // when - ResultSet set; - try (CqlSession session = - CqlSession.builder() - .withAuthCredentials("cassandra", "cassandra") - .withCloudSecureConnectBundle(bundleUrl) - .build()) { - - // then - set = session.execute("select * from system.local where key='local'"); - } - assertThat(set).isNotNull(); - } - - @Test - public void should_connect_to_proxy_using_absolute_path_provided_in_the_session_setting() { - // given - String bundle = proxyRule.getProxy().getDefaultBundlePath().toString(); - DriverConfigLoader loader = - DriverConfigLoader.programmaticBuilder() - .withString(DefaultDriverOption.CLOUD_SECURE_CONNECT_BUNDLE, bundle) - .build(); - // when - ResultSet set; - try (CqlSession session = - CqlSession.builder() - .withAuthCredentials("cassandra", "cassandra") - .withConfigLoader(loader) - .build()) { - - // then - set = session.execute("select * from system.local where key='local'"); - } - assertThat(set).isNotNull(); - } - - @Test - public void should_connect_to_proxy_using_non_normalized_path_provided_in_the_session_setting() { - // given - String bundle = - proxyRule.getProxy().getBundlesRootPath().resolve("../bundles/creds-v1.zip").toString(); - DriverConfigLoader loader = - DriverConfigLoader.programmaticBuilder() - .withString(DefaultDriverOption.CLOUD_SECURE_CONNECT_BUNDLE, bundle) - .build(); - // when - ResultSet set; - try (CqlSession session = - CqlSession.builder() - .withAuthCredentials("cassandra", "cassandra") - .withConfigLoader(loader) - .build()) { - - // then - set = session.execute("select * from system.local where key='local'"); - } - assertThat(set).isNotNull(); - } - - @Test - public void - should_connect_to_proxy_using_url_with_file_protocol_provided_in_the_session_setting() { - // given - String bundle = proxyRule.getProxy().getDefaultBundlePath().toString(); - DriverConfigLoader loader = - DriverConfigLoader.programmaticBuilder() - .withString(DefaultDriverOption.CLOUD_SECURE_CONNECT_BUNDLE, bundle) - .build(); - // when - ResultSet set; - try (CqlSession session = - CqlSession.builder() - .withAuthCredentials("cassandra", "cassandra") - .withConfigLoader(loader) - .build()) { - - // then - set = session.execute("select * from system.local where key='local'"); - } - assertThat(set).isNotNull(); - } - - @Test - public void should_connect_to_proxy_using_url_with_http_protocol_provided_in_the_session_setting() - throws IOException { - // given - byte[] bundle = Files.readAllBytes(proxyRule.getProxy().getDefaultBundlePath()); - stubFor( - any(urlEqualTo(BUNDLE_URL_PATH)) - .willReturn( - aResponse() - .withStatus(200) - .withHeader("Content-Type", "application/octet-stream") - .withBody(bundle))); - String bundleUrl = String.format("http://localhost:%d%s", wireMockRule.port(), BUNDLE_URL_PATH); - DriverConfigLoader loader = - DriverConfigLoader.programmaticBuilder() - .withString(DefaultDriverOption.CLOUD_SECURE_CONNECT_BUNDLE, bundleUrl) - .build(); - // when - ResultSet set; - try (CqlSession session = - CqlSession.builder() - .withAuthCredentials("cassandra", "cassandra") - .withConfigLoader(loader) - .build()) { - - // then - set = session.execute("select * from system.local where key='local'"); - } - assertThat(set).isNotNull(); - } - - @Test - public void - should_connect_and_log_info_when_contact_points_and_secure_bundle_used_programmatic() { - // given - LoggerTest.LoggerSetup logger = setupTestLogger(SessionBuilder.class, Level.INFO); - - Path bundle = proxyRule.getProxy().getBundleWithoutCredentialsPath(); - - try (CqlSession session = - CqlSession.builder() - .withCloudSecureConnectBundle(bundle) - .addContactPoint(new InetSocketAddress("127.0.0.1", 9042)) - .withAuthCredentials("cassandra", "cassandra") - .build(); ) { - - // when - ResultSet set = session.execute("select * from system.local where key='local'"); - // then - assertThat(set).isNotNull(); - verify(logger.appender, timeout(500).atLeast(1)) - .doAppend(logger.loggingEventCaptor.capture()); - assertThat( - logger.loggingEventCaptor.getAllValues().stream() - .map(ILoggingEvent::getFormattedMessage)) - .contains( - "Both a secure connect bundle and contact points were provided. These are mutually exclusive. The contact points from the secure bundle will have priority."); - - } finally { - logger.close(); - } - } - - @Test - public void should_connect_and_log_info_when_contact_points_and_secure_bundle_used_config() { - // given - LoggerTest.LoggerSetup logger = setupTestLogger(SessionBuilder.class, Level.INFO); - - DriverConfigLoader loader = - SessionUtils.configLoaderBuilder() - .withStringList( - DefaultDriverOption.CONTACT_POINTS, Collections.singletonList("localhost:9042")) - .build(); - - Path bundle = proxyRule.getProxy().getBundleWithoutCredentialsPath(); - - try (CqlSession session = - CqlSession.builder() - .withConfigLoader(loader) - .withCloudSecureConnectBundle(bundle) - .withAuthCredentials("cassandra", "cassandra") - .build(); ) { - - // when - ResultSet set = session.execute("select * from system.local where key='local'"); - // then - assertThat(set).isNotNull(); - verify(logger.appender, timeout(500).atLeast(1)) - .doAppend(logger.loggingEventCaptor.capture()); - assertThat( - logger.loggingEventCaptor.getAllValues().stream() - .map(ILoggingEvent::getFormattedMessage)) - .contains( - "Both a secure connect bundle and contact points were provided. These are mutually exclusive. The contact points from the secure bundle will have priority."); - - } finally { - logger.close(); - } - } - - @Test - public void should_connect_and_log_info_when_ssl_context_and_secure_bundle_used_programmatic() - throws NoSuchAlgorithmException { - // given - LoggerTest.LoggerSetup logger = setupTestLogger(SessionBuilder.class, Level.INFO); - - Path bundle = proxyRule.getProxy().getBundleWithoutCredentialsPath(); - - try (CqlSession session = - CqlSession.builder() - .withCloudSecureConnectBundle(bundle) - .withAuthCredentials("cassandra", "cassandra") - .withSslContext(SSLContext.getInstance("SSL")) - .build()) { - // when - ResultSet set = session.execute("select * from system.local where key='local'"); - // then - assertThat(set).isNotNull(); - verify(logger.appender, timeout(500).atLeast(1)) - .doAppend(logger.loggingEventCaptor.capture()); - assertThat( - logger.loggingEventCaptor.getAllValues().stream() - .map(ILoggingEvent::getFormattedMessage)) - .contains( - "Both a secure connect bundle and SSL options were provided. They are mutually exclusive. The SSL options from the secure bundle will have priority."); - } finally { - logger.close(); - } - } - - @Test - public void should_error_when_ssl_context_and_secure_bundle_used_config() - throws NoSuchAlgorithmException { - // given - LoggerTest.LoggerSetup logger = setupTestLogger(SessionBuilder.class, Level.INFO); - - DriverConfigLoader loader = - SessionUtils.configLoaderBuilder() - .withBoolean(DefaultDriverOption.RECONNECT_ON_INIT, true) - .withClass(DefaultDriverOption.SSL_ENGINE_FACTORY_CLASS, DefaultSslEngineFactory.class) - .build(); - - Path bundle = proxyRule.getProxy().getBundleWithoutCredentialsPath(); - - try (CqlSession session = - CqlSession.builder() - .withConfigLoader(loader) - .withCloudSecureConnectBundle(bundle) - .withAuthCredentials("cassandra", "cassandra") - .build()) { - // when - ResultSet set = session.execute("select * from system.local where key='local'"); - // then - assertThat(set).isNotNull(); - verify(logger.appender, timeout(500).atLeast(1)) - .doAppend(logger.loggingEventCaptor.capture()); - assertThat( - logger.loggingEventCaptor.getAllValues().stream() - .map(ILoggingEvent::getFormattedMessage)) - .contains( - "Both a secure connect bundle and SSL options were provided. They are mutually exclusive. The SSL options from the secure bundle will have priority."); - } finally { - logger.close(); - } - } - - @Test - public void - should_connect_and_log_info_when_local_data_center_and_secure_bundle_used_programmatic() { - // given - LoggerTest.LoggerSetup logger = setupTestLogger(SessionBuilder.class, Level.INFO); - - DriverConfigLoader loader = - SessionUtils.configLoaderBuilder() - .withString(DefaultDriverOption.LOAD_BALANCING_LOCAL_DATACENTER, "dc-ignore") - .build(); - - Path bundle = proxyRule.getProxy().getBundleWithoutCredentialsPath(); - - try (CqlSession session = - CqlSession.builder() - .withCloudSecureConnectBundle(bundle) - .withConfigLoader(loader) - .withAuthCredentials("cassandra", "cassandra") - .build(); ) { - - // when - ResultSet set = session.execute("select * from system.local where key='local'"); - // then - assertThat(set).isNotNull(); - verify(logger.appender, timeout(500).atLeast(1)) - .doAppend(logger.loggingEventCaptor.capture()); - assertThat( - logger.loggingEventCaptor.getAllValues().stream() - .map(ILoggingEvent::getFormattedMessage)) - .contains( - "Both a secure connect bundle and a local datacenter were provided. They are mutually exclusive. The local datacenter from the secure bundle will have priority."); - - } finally { - logger.close(); - } - } - - @Test - public void should_connect_and_log_info_when_local_data_center_and_secure_bundle_used_config() { - // given - LoggerTest.LoggerSetup logger = setupTestLogger(SessionBuilder.class, Level.INFO); - - Path bundle = proxyRule.getProxy().getBundleWithoutCredentialsPath(); - - try (CqlSession session = - CqlSession.builder() - .withCloudSecureConnectBundle(bundle) - .withLocalDatacenter("dc-ignored") - .withAuthCredentials("cassandra", "cassandra") - .build(); ) { - - // when - ResultSet set = session.execute("select * from system.local where key='local'"); - // then - assertThat(set).isNotNull(); - verify(logger.appender, timeout(500).atLeast(1)) - .doAppend(logger.loggingEventCaptor.capture()); - assertThat( - logger.loggingEventCaptor.getAllValues().stream() - .map(ILoggingEvent::getFormattedMessage)) - .contains( - "Both a secure connect bundle and a local datacenter were provided. They are mutually exclusive. The local datacenter from the secure bundle will have priority."); - - } finally { - logger.close(); - } - } -} diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/api/core/cloud/SniProxyRule.java b/integration-tests/src/test/java/com/datastax/oss/driver/api/core/cloud/SniProxyRule.java deleted file mode 100644 index fa009de78ae..00000000000 --- a/integration-tests/src/test/java/com/datastax/oss/driver/api/core/cloud/SniProxyRule.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 com.datastax.oss.driver.api.core.cloud; - -import org.junit.rules.ExternalResource; - -public class SniProxyRule extends ExternalResource { - - private final SniProxyServer proxy; - - public SniProxyRule() { - proxy = new SniProxyServer(); - } - - @Override - protected void before() { - proxy.startProxy(); - } - - @Override - protected void after() { - proxy.stopProxy(); - } - - public SniProxyServer getProxy() { - return proxy; - } -} diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/api/core/cloud/SniProxyServer.java b/integration-tests/src/test/java/com/datastax/oss/driver/api/core/cloud/SniProxyServer.java deleted file mode 100644 index 809354a7daf..00000000000 --- a/integration-tests/src/test/java/com/datastax/oss/driver/api/core/cloud/SniProxyServer.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 com.datastax.oss.driver.api.core.cloud; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.concurrent.TimeUnit; -import org.apache.commons.exec.CommandLine; -import org.apache.commons.exec.DefaultExecutor; -import org.apache.commons.exec.ExecuteStreamHandler; -import org.apache.commons.exec.ExecuteWatchdog; -import org.apache.commons.exec.Executor; -import org.apache.commons.exec.LogOutputStream; -import org.apache.commons.exec.PumpStreamHandler; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class SniProxyServer { - - private static final Logger LOG = LoggerFactory.getLogger(SniProxyServer.class); - - private final Path proxyPath; - private final Path bundlesRootPath; - private final Path defaultBundlePath; - private final Path bundleWithoutCredentialsPath; - private final Path bundleWithoutClientCertificatesPath; - private final Path bundleWithInvalidCAPath; - private final Path bundleWithUnreachableMetadataServicePath; - - private volatile boolean running = false; - - public SniProxyServer() { - this(Paths.get(System.getProperty("proxy.path", "./"))); - } - - public SniProxyServer(Path proxyPath) { - this.proxyPath = proxyPath.normalize().toAbsolutePath(); - bundlesRootPath = proxyPath.resolve("certs/bundles/"); - defaultBundlePath = bundlesRootPath.resolve("creds-v1.zip"); - bundleWithoutCredentialsPath = bundlesRootPath.resolve("creds-v1-wo-creds.zip"); - bundleWithoutClientCertificatesPath = bundlesRootPath.resolve("creds-v1-wo-cert.zip"); - bundleWithInvalidCAPath = bundlesRootPath.resolve("creds-v1-invalid-ca.zip"); - bundleWithUnreachableMetadataServicePath = bundlesRootPath.resolve("creds-v1-unreachable.zip"); - } - - public void startProxy() { - CommandLine run = CommandLine.parse(proxyPath + "/run.sh"); - execute(run); - running = true; - } - - public void stopProxy() { - if (running) { - CommandLine findImageId = - CommandLine.parse("docker ps -a -q --filter ancestor=single_endpoint"); - String id = execute(findImageId); - CommandLine stop = CommandLine.parse("docker kill " + id); - execute(stop); - running = false; - } - } - - /** @return The root folder of the SNI proxy server docker image. */ - public Path getProxyPath() { - return proxyPath; - } - - /** - * @return The root folder where secure connect bundles exposed by this SNI proxy for testing - * purposes can be found. - */ - public Path getBundlesRootPath() { - return bundlesRootPath; - } - - /** - * @return The default secure connect bundle. It contains credentials and all certificates - * required to connect. - */ - public Path getDefaultBundlePath() { - return defaultBundlePath; - } - - /** @return A secure connect bundle without credentials in config.json. */ - public Path getBundleWithoutCredentialsPath() { - return bundleWithoutCredentialsPath; - } - - /** @return A secure connect bundle without client certificates (no identity.jks). */ - public Path getBundleWithoutClientCertificatesPath() { - return bundleWithoutClientCertificatesPath; - } - - /** @return A secure connect bundle with an invalid Certificate Authority. */ - public Path getBundleWithInvalidCAPath() { - return bundleWithInvalidCAPath; - } - - /** @return A secure connect bundle with an invalid address for the Proxy Metadata Service. */ - public Path getBundleWithUnreachableMetadataServicePath() { - return bundleWithUnreachableMetadataServicePath; - } - - private String execute(CommandLine cli) { - LOG.debug("Executing: " + cli); - ExecuteWatchdog watchDog = new ExecuteWatchdog(TimeUnit.MINUTES.toMillis(10)); - ByteArrayOutputStream outStream = new ByteArrayOutputStream(); - try (LogOutputStream errStream = - new LogOutputStream() { - @Override - protected void processLine(String line, int logLevel) { - LOG.error("sniendpointerr> {}", line); - } - }) { - Executor executor = new DefaultExecutor(); - ExecuteStreamHandler streamHandler = new PumpStreamHandler(outStream, errStream); - executor.setStreamHandler(streamHandler); - executor.setWatchdog(watchDog); - executor.setWorkingDirectory(proxyPath.toFile()); - int retValue = executor.execute(cli); - if (retValue != 0) { - LOG.error("Non-zero exit code ({}) returned from executing ccm command: {}", retValue, cli); - } - return outStream.toString(); - } catch (IOException ex) { - if (watchDog.killedProcess()) { - throw new RuntimeException("The command '" + cli + "' was killed after 10 minutes"); - } else { - throw new RuntimeException("The command '" + cli + "' failed to execute", ex); - } - } - } -} diff --git a/manual/core/address_resolution/README.md b/manual/core/address_resolution/README.md index ae44feea3ea..543aeedbfee 100644 --- a/manual/core/address_resolution/README.md +++ b/manual/core/address_resolution/README.md @@ -130,9 +130,8 @@ Note that `OptionsMap`-based configuration does not support client routes — us API (`SessionBuilder.withClientRoutesConfig()`) instead, which can be combined with `OptionsMap` for all other driver options. -Client routes are **mutually exclusive** with: -- A custom `AddressTranslator` (if both are provided, an `IllegalStateException` is thrown) -- Cloud secure connect bundles (if both are provided, an `IllegalStateException` is thrown) +Client routes are **mutually exclusive** with a custom `AddressTranslator`; if both are provided, +an `IllegalStateException` is thrown. #### Quick start (programmatic) @@ -203,7 +202,7 @@ the JVM DNS cache TTL via the `networkaddress.cache.ttl` security property (e.g. - Requires ScyllaDB Enterprise ≥ 2026.1 with `system.client_routes` support (scylladb/scylladb#27323). Not yet available on ScyllaDB OSS. - Not supported on Apache Cassandra. -- Mutually exclusive with custom `AddressTranslator` and with cloud secure connect bundles. +- Mutually exclusive with a custom `AddressTranslator`. ### Fixed proxy hostname If your client applications access Cassandra through some kind of proxy (eg. with AWS PrivateLink when all Cassandra diff --git a/pom.xml b/pom.xml index 76caea447cc..cad28983488 100644 --- a/pom.xml +++ b/pom.xml @@ -487,11 +487,6 @@ api-ldap-codec-standalone 2.1.7 - - com.github.tomakehurst - wiremock - 2.25.0 - org.graalvm.sdk graal-sdk diff --git a/upgrade_guide/README.md b/upgrade_guide/README.md index 83963f94775..148550bb6da 100644 --- a/upgrade_guide/README.md +++ b/upgrade_guide/README.md @@ -19,6 +19,33 @@ under the License. ## Upgrade guide +### 4.19.2.2 + +#### DataStax Astra secure-connect-bundle support was removed + +This release intentionally removes the DataStax Astra secure-connect-bundle integration and its +public API. The following members are no longer available: + +- `SessionBuilder.withCloudSecureConnectBundle(Path)` +- `SessionBuilder.withCloudSecureConnectBundle(URL)` +- `SessionBuilder.withCloudSecureConnectBundle(InputStream)` +- `SessionBuilder.withCloudProxyAddress(InetSocketAddress)` +- `ProgrammaticArguments.getCloudProxyAddress()` +- `ProgrammaticArguments.Builder.withCloudProxyAddress(InetSocketAddress)` +- `SessionBuilder.ASTRA_PAYLOAD_KEY` +- `DefaultDriverOption.CLOUD_SECURE_CONNECT_BUNDLE` +- `TypedDriverOption.CLOUD_SECURE_CONNECT_BUNDLE` + +The old `datastax-java-driver.basic.cloud.secure-connect-bundle` configuration key is now an +unknown option and no longer supplies connection information. If that was the only connection +setting, the resulting configuration contains no contact points, so the driver falls back to its +default contact point, `127.0.0.1:9042`. + +This is an intentional binary- and source-compatibility break. Applications must replace secure +connect bundles with explicit `basic.contact-points` configuration or programmatic contact points. +For supported ScyllaDB private-endpoint deployments, use +[client routes](../manual/core/address_resolution/) together with an explicit contact point. + ### 4.19.2.1 #### The driver reports a session identifier, and its configuration, at connection time @@ -107,8 +134,8 @@ datastax-java-driver { Key points: -- **Mutually exclusive** with a custom `AddressTranslator` and with cloud secure connect bundles — - providing both throws `IllegalStateException` at session build time. +- **Mutually exclusive** with a custom `AddressTranslator` — providing both throws + `IllegalStateException` at session build time. - **Requires ScyllaDB Enterprise ≥ 2026.1** (scylladb/scylladb#27323). The feature is not available on ScyllaDB OSS or Apache Cassandra.