diff --git a/.github/workflows/bench_run.yml b/.github/workflows/bench_run.yaml similarity index 100% rename from .github/workflows/bench_run.yml rename to .github/workflows/bench_run.yaml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yaml similarity index 100% rename from .github/workflows/build.yml rename to .github/workflows/build.yaml diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yaml similarity index 100% rename from .github/workflows/build_and_test.yml rename to .github/workflows/build_and_test.yaml diff --git a/.github/workflows/create_comment.yml b/.github/workflows/create_comment.yaml similarity index 100% rename from .github/workflows/create_comment.yml rename to .github/workflows/create_comment.yaml diff --git a/.github/workflows/license_check.yml b/.github/workflows/license_check.yaml similarity index 100% rename from .github/workflows/license_check.yml rename to .github/workflows/license_check.yaml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yaml similarity index 100% rename from .github/workflows/release.yml rename to .github/workflows/release.yaml diff --git a/docs/src/examples/cassandra-cluster-shotover-sidecar.md b/docs/src/examples/cassandra-cluster-shotover-sidecar.md index 37e6301a6..595d28a81 100644 --- a/docs/src/examples/cassandra-cluster-shotover-sidecar.md +++ b/docs/src/examples/cassandra-cluster-shotover-sidecar.md @@ -12,10 +12,10 @@ In this example, we will be connecting to a Cassandra cluster that has the follo ### Rewriting the peer ports -Shotover will be deployed as a sidecar to each node in the Cassandra cluster, listening on `9043`. Use the following [docker-compose.yml](https://raw.githubusercontent.com/shotover/shotover-proxy/cassandra-docs/shotover-proxy/example-configs-docker/cassandra-peers-rewrite/docker-compose.yml) to run the Cassandra cluster and Shotover sidecars. In this example we want to ensure that all our traffic to Cassandra goes through Shotover. +Shotover will be deployed as a sidecar to each node in the Cassandra cluster, listening on `9043`. Use the following [docker-compose.yaml](https://raw.githubusercontent.com/shotover/shotover-proxy/cassandra-docs/shotover-proxy/example-configs-docker/cassandra-peers-rewrite/docker-compose.yaml) to run the Cassandra cluster and Shotover sidecars. In this example we want to ensure that all our traffic to Cassandra goes through Shotover. ```console -curl -L https://raw.githubusercontent.com/shotover/shotover-proxy/main/shotover-proxy/example-configs-docker/cassandra-peers-rewrite/docker-compose.yml --output docker-compose.yml +curl -L https://raw.githubusercontent.com/shotover/shotover-proxy/main/shotover-proxy/example-configs-docker/cassandra-peers-rewrite/docker-compose.yaml --output docker-compose.yaml ``` Below we can see an example of a Cassandra node and it's Shotover sidecar, notice that they are running on the same network address (`172.16.1.2`) and the present directory is being mounted to allow Shotover to access the config and topology files. diff --git a/docs/src/examples/redis-clustering-aware.md b/docs/src/examples/redis-clustering-aware.md index 4353fb292..7e00787c0 100644 --- a/docs/src/examples/redis-clustering-aware.md +++ b/docs/src/examples/redis-clustering-aware.md @@ -13,10 +13,10 @@ In this example, we will be connecting to a Redis cluster that has the following * `172.16.1.6:6379` * `172.16.1.7:6379` -Shotover will be deployed as a sidecar to each node in the Redis cluster, listening on `6380`. Use the following [docker-compose.yml](https://github.com/shotover/shotover-proxy/blob/main/shotover-proxy/example-configs-docker/redis-cluster-ports-rewrite/docker-compose.yml) to run the Redis cluster and Shotover sidecars. +Shotover will be deployed as a sidecar to each node in the Redis cluster, listening on `6380`. Use the following [docker-compose.yaml](https://github.com/shotover/shotover-proxy/blob/main/shotover-proxy/example-configs-docker/redis-cluster-ports-rewrite/docker-compose.yaml) to run the Redis cluster and Shotover sidecars. ```console -curl -L https://raw.githubusercontent.com/shotover/shotover-proxy/main/shotover-proxy/example-configs-docker/redis-cluster-ports-rewrite/docker-compose.yml --output docker-compose.yml +curl -L https://raw.githubusercontent.com/shotover/shotover-proxy/main/shotover-proxy/example-configs-docker/redis-cluster-ports-rewrite/docker-compose.yaml --output docker-compose.yaml ``` Below we can see an example of a Redis node and it's Shotover sidecar. Notice they are running on the same network address (`172.16.1.2`) and the present directory is being mounted to allow Shotover to access the config and topology files. diff --git a/docs/src/examples/redis-clustering-unaware.md b/docs/src/examples/redis-clustering-unaware.md index 1428196ef..62d55a121 100644 --- a/docs/src/examples/redis-clustering-unaware.md +++ b/docs/src/examples/redis-clustering-unaware.md @@ -6,14 +6,14 @@ The following guide shows you how to configure Shotover Proxy to support transpa First you need to setup a Redis cluster for Shotover to connect to. -The easiest way to do this is with this example [docker-compose.yml](https://github.com/shotover/shotover-proxy/blob/main/shotover-proxy/example-configs-docker/redis-cluster-hiding/docker-compose.yml) -You should first inspect the `docker-compose.yml` to understand what the cluster looks like and how its exposed to the network. +The easiest way to do this is with this example [docker-compose.yaml](https://github.com/shotover/shotover-proxy/blob/main/shotover-proxy/example-configs-docker/redis-cluster-hiding/docker-compose.yaml) +You should first inspect the `docker-compose.yaml` to understand what the cluster looks like and how its exposed to the network. Then run: ```bash -curl -L https://raw.githubusercontent.com/shotover/shotover-proxy/main/shotover-proxy/example-configs/redis-cluster-hiding/docker-compose.yml --output docker-compose.yml -docker-compose -f docker-compose.yml up +curl -L https://raw.githubusercontent.com/shotover/shotover-proxy/main/shotover-proxy/example-configs/redis-cluster-hiding/docker-compose.yaml --output docker-compose.yaml +docker-compose -f docker-compose.yaml up ``` When you are finished with the containers ctrl + c will shut them down. @@ -29,7 +29,7 @@ Modify your `topology.yaml` file like this: {{#include ../../../shotover-proxy/example-configs-docker/redis-cluster-hiding/topology.yaml}} ``` -If you didnt use the standard `docker-compose.yml` setup then you will need to change `first_contact_points` to point to the Redis instances you used. +If you didnt use the standard `docker-compose.yaml` setup then you will need to change `first_contact_points` to point to the Redis instances you used. ## Testing diff --git a/shotover-proxy/benches/benches/cassandra.rs b/shotover-proxy/benches/benches/cassandra.rs index 850971dd5..958aaa28e 100644 --- a/shotover-proxy/benches/benches/cassandra.rs +++ b/shotover-proxy/benches/benches/cassandra.rs @@ -28,7 +28,7 @@ fn cassandra(c: &mut Criterion) { let resources = new_lazy_shared(|| { BenchResources::new( "example-configs/cassandra-protect-local/topology.yaml", - "example-configs/cassandra-protect-local/docker-compose.yml", + "example-configs/cassandra-protect-local/docker-compose.yaml", ) }); for query in &queries { @@ -53,7 +53,7 @@ fn cassandra(c: &mut Criterion) { let resources = new_lazy_shared(|| { BenchResources::new( "example-configs/cassandra-redis-cache/topology.yaml", - "example-configs/cassandra-redis-cache/docker-compose.yml", + "example-configs/cassandra-redis-cache/docker-compose.yaml", ) }); // Benches the case where the message does not meet the criteria for caching @@ -79,7 +79,7 @@ fn cassandra(c: &mut Criterion) { let resources = new_lazy_shared(|| { BenchResources::new( "example-configs/cassandra-passthrough/topology.yaml", - "example-configs/cassandra-passthrough/docker-compose.yml", + "example-configs/cassandra-passthrough/docker-compose.yaml", ) }); for query in &queries { @@ -105,7 +105,7 @@ fn cassandra(c: &mut Criterion) { let resources = new_lazy_shared(|| { BenchResources::new( "tests/test-configs/cassandra-passthrough-parse-request/topology.yaml", - "tests/test-configs/cassandra-passthrough-parse-request/docker-compose.yml", + "tests/test-configs/cassandra-passthrough-parse-request/docker-compose.yaml", ) }); for query in &queries { @@ -131,7 +131,7 @@ fn cassandra(c: &mut Criterion) { let resources = new_lazy_shared(|| { BenchResources::new( "tests/test-configs/cassandra-passthrough-parse-response/topology.yaml", - "tests/test-configs/cassandra-passthrough-parse-response/docker-compose.yml", + "tests/test-configs/cassandra-passthrough-parse-response/docker-compose.yaml", ) }); for query in &queries { @@ -156,7 +156,7 @@ fn cassandra(c: &mut Criterion) { let resources = new_lazy_shared(|| { BenchResources::new_tls( "example-configs/cassandra-tls/topology.yaml", - "example-configs/cassandra-tls/docker-compose.yml", + "example-configs/cassandra-tls/docker-compose.yaml", ) }); for query in &queries { @@ -188,7 +188,7 @@ fn cassandra(c: &mut Criterion) { let resources = new_lazy_shared(|| { let resources = BenchResources::new( "example-configs/cassandra-protect-local/topology.yaml", - "example-configs/cassandra-protect-local/docker-compose.yml", + "example-configs/cassandra-protect-local/docker-compose.yaml", ); resources @@ -235,7 +235,7 @@ fn cassandra(c: &mut Criterion) { let resources = new_lazy_shared(|| { BenchResources::new( "example-configs/cassandra-request-throttling/topology.yaml", - "example-configs/cassandra-request-throttling/docker-compose.yml", + "example-configs/cassandra-request-throttling/docker-compose.yaml", ) }); for query in &queries { diff --git a/shotover-proxy/benches/benches/redis.rs b/shotover-proxy/benches/benches/redis.rs index 7023e6744..304ffa68b 100644 --- a/shotover-proxy/benches/benches/redis.rs +++ b/shotover-proxy/benches/benches/redis.rs @@ -31,7 +31,7 @@ fn redis(c: &mut Criterion) { let resources = new_lazy_shared(|| { BenchResources::new( "example-configs/redis-multi/topology.yaml", - "example-configs/redis-multi/docker-compose.yml", + "example-configs/redis-multi/docker-compose.yaml", ) }); for query in &queries { @@ -54,7 +54,7 @@ fn redis(c: &mut Criterion) { let resources = new_lazy_shared(|| { BenchResources::new( "example-configs/redis-cluster-hiding/topology.yaml", - "example-configs/redis-cluster-hiding/docker-compose.yml", + "example-configs/redis-cluster-hiding/docker-compose.yaml", ) }); for query in &queries { @@ -77,7 +77,7 @@ fn redis(c: &mut Criterion) { let resources = new_lazy_shared(|| { BenchResources::new( "example-configs/redis-passthrough/topology.yaml", - "example-configs/redis-passthrough/docker-compose.yml", + "example-configs/redis-passthrough/docker-compose.yaml", ) }); for query in &queries { @@ -105,7 +105,7 @@ fn redis(c: &mut Criterion) { )); BenchResources::new( "example-configs/redis-tls/topology.yaml", - "example-configs/redis-tls/docker-compose.yml", + "example-configs/redis-tls/docker-compose.yaml", ) }, move |b, state| { @@ -127,7 +127,7 @@ fn redis(c: &mut Criterion) { )); BenchResources::new( "example-configs/redis-cluster-tls/topology.yaml", - "example-configs/redis-cluster-tls/docker-compose.yml", + "example-configs/redis-cluster-tls/docker-compose.yaml", ) }, move |b, state| { diff --git a/shotover-proxy/example-configs-docker/cassandra-peers-rewrite/docker-compose.yml b/shotover-proxy/example-configs-docker/cassandra-peers-rewrite/docker-compose.yaml similarity index 100% rename from shotover-proxy/example-configs-docker/cassandra-peers-rewrite/docker-compose.yml rename to shotover-proxy/example-configs-docker/cassandra-peers-rewrite/docker-compose.yaml diff --git a/shotover-proxy/example-configs-docker/redis-cluster-ports-rewrite/docker-compose.yml b/shotover-proxy/example-configs-docker/redis-cluster-ports-rewrite/docker-compose.yaml similarity index 100% rename from shotover-proxy/example-configs-docker/redis-cluster-ports-rewrite/docker-compose.yml rename to shotover-proxy/example-configs-docker/redis-cluster-ports-rewrite/docker-compose.yaml diff --git a/shotover-proxy/example-configs-docker/redis-cluster/docker-compose.yml b/shotover-proxy/example-configs-docker/redis-cluster/docker-compose.yaml similarity index 100% rename from shotover-proxy/example-configs-docker/redis-cluster/docker-compose.yml rename to shotover-proxy/example-configs-docker/redis-cluster/docker-compose.yaml diff --git a/shotover-proxy/example-configs/cassandra-cluster-multi-rack/docker-compose.yml b/shotover-proxy/example-configs/cassandra-cluster-multi-rack/docker-compose.yaml similarity index 100% rename from shotover-proxy/example-configs/cassandra-cluster-multi-rack/docker-compose.yml rename to shotover-proxy/example-configs/cassandra-cluster-multi-rack/docker-compose.yaml diff --git a/shotover-proxy/example-configs/cassandra-cluster-tls/docker-compose.yml b/shotover-proxy/example-configs/cassandra-cluster-tls/docker-compose.yaml similarity index 100% rename from shotover-proxy/example-configs/cassandra-cluster-tls/docker-compose.yml rename to shotover-proxy/example-configs/cassandra-cluster-tls/docker-compose.yaml diff --git a/shotover-proxy/example-configs/cassandra-cluster/docker-compose-cassandra-v3.yml b/shotover-proxy/example-configs/cassandra-cluster/docker-compose-cassandra-v3.yaml similarity index 100% rename from shotover-proxy/example-configs/cassandra-cluster/docker-compose-cassandra-v3.yml rename to shotover-proxy/example-configs/cassandra-cluster/docker-compose-cassandra-v3.yaml diff --git a/shotover-proxy/example-configs/cassandra-cluster/docker-compose-cassandra-v4.yml b/shotover-proxy/example-configs/cassandra-cluster/docker-compose-cassandra-v4.yaml similarity index 100% rename from shotover-proxy/example-configs/cassandra-cluster/docker-compose-cassandra-v4.yml rename to shotover-proxy/example-configs/cassandra-cluster/docker-compose-cassandra-v4.yaml diff --git a/shotover-proxy/example-configs/cassandra-passthrough/docker-compose.yml b/shotover-proxy/example-configs/cassandra-passthrough/docker-compose.yaml similarity index 100% rename from shotover-proxy/example-configs/cassandra-passthrough/docker-compose.yml rename to shotover-proxy/example-configs/cassandra-passthrough/docker-compose.yaml diff --git a/shotover-proxy/example-configs/cassandra-protect-aws/docker-compose.yml b/shotover-proxy/example-configs/cassandra-protect-aws/docker-compose.yaml similarity index 100% rename from shotover-proxy/example-configs/cassandra-protect-aws/docker-compose.yml rename to shotover-proxy/example-configs/cassandra-protect-aws/docker-compose.yaml diff --git a/shotover-proxy/example-configs/cassandra-protect-local/docker-compose.yml b/shotover-proxy/example-configs/cassandra-protect-local/docker-compose.yaml similarity index 100% rename from shotover-proxy/example-configs/cassandra-protect-local/docker-compose.yml rename to shotover-proxy/example-configs/cassandra-protect-local/docker-compose.yaml diff --git a/shotover-proxy/example-configs/cassandra-redis-cache/docker-compose.yml b/shotover-proxy/example-configs/cassandra-redis-cache/docker-compose.yaml similarity index 100% rename from shotover-proxy/example-configs/cassandra-redis-cache/docker-compose.yml rename to shotover-proxy/example-configs/cassandra-redis-cache/docker-compose.yaml diff --git a/shotover-proxy/example-configs/cassandra-request-throttling/docker-compose.yml b/shotover-proxy/example-configs/cassandra-request-throttling/docker-compose.yaml similarity index 100% rename from shotover-proxy/example-configs/cassandra-request-throttling/docker-compose.yml rename to shotover-proxy/example-configs/cassandra-request-throttling/docker-compose.yaml diff --git a/shotover-proxy/example-configs/cassandra-tls/docker-compose.yml b/shotover-proxy/example-configs/cassandra-tls/docker-compose.yaml similarity index 100% rename from shotover-proxy/example-configs/cassandra-tls/docker-compose.yml rename to shotover-proxy/example-configs/cassandra-tls/docker-compose.yaml diff --git a/shotover-proxy/example-configs/redis-cluster-dr/docker-compose.yml b/shotover-proxy/example-configs/redis-cluster-dr/docker-compose.yaml similarity index 100% rename from shotover-proxy/example-configs/redis-cluster-dr/docker-compose.yml rename to shotover-proxy/example-configs/redis-cluster-dr/docker-compose.yaml diff --git a/shotover-proxy/example-configs/redis-cluster-handling/docker-compose.yml b/shotover-proxy/example-configs/redis-cluster-handling/docker-compose.yaml similarity index 100% rename from shotover-proxy/example-configs/redis-cluster-handling/docker-compose.yml rename to shotover-proxy/example-configs/redis-cluster-handling/docker-compose.yaml diff --git a/shotover-proxy/example-configs/redis-cluster-hiding/docker-compose.yml b/shotover-proxy/example-configs/redis-cluster-hiding/docker-compose.yaml similarity index 100% rename from shotover-proxy/example-configs/redis-cluster-hiding/docker-compose.yml rename to shotover-proxy/example-configs/redis-cluster-hiding/docker-compose.yaml diff --git a/shotover-proxy/example-configs/redis-cluster-tls/docker-compose-with-key.yml b/shotover-proxy/example-configs/redis-cluster-tls/docker-compose-with-key.yaml similarity index 100% rename from shotover-proxy/example-configs/redis-cluster-tls/docker-compose-with-key.yml rename to shotover-proxy/example-configs/redis-cluster-tls/docker-compose-with-key.yaml diff --git a/shotover-proxy/example-configs/redis-cluster-tls/docker-compose.yml b/shotover-proxy/example-configs/redis-cluster-tls/docker-compose.yaml similarity index 100% rename from shotover-proxy/example-configs/redis-cluster-tls/docker-compose.yml rename to shotover-proxy/example-configs/redis-cluster-tls/docker-compose.yaml diff --git a/shotover-proxy/example-configs/redis-multi/docker-compose.yml b/shotover-proxy/example-configs/redis-multi/docker-compose.yaml similarity index 100% rename from shotover-proxy/example-configs/redis-multi/docker-compose.yml rename to shotover-proxy/example-configs/redis-multi/docker-compose.yaml diff --git a/shotover-proxy/example-configs/redis-passthrough/docker-compose.yml b/shotover-proxy/example-configs/redis-passthrough/docker-compose.yaml similarity index 100% rename from shotover-proxy/example-configs/redis-passthrough/docker-compose.yml rename to shotover-proxy/example-configs/redis-passthrough/docker-compose.yaml diff --git a/shotover-proxy/example-configs/redis-tls/docker-compose.yml b/shotover-proxy/example-configs/redis-tls/docker-compose.yaml similarity index 100% rename from shotover-proxy/example-configs/redis-tls/docker-compose.yml rename to shotover-proxy/example-configs/redis-tls/docker-compose.yaml diff --git a/shotover-proxy/examples/cassandra_bench.rs b/shotover-proxy/examples/cassandra_bench.rs index 9403cff9b..8aeda10b9 100644 --- a/shotover-proxy/examples/cassandra_bench.rs +++ b/shotover-proxy/examples/cassandra_bench.rs @@ -30,7 +30,7 @@ fn main() { let latte = Latte::new(args.rate); { - let _compose = DockerCompose::new(&format!("{}/docker-compose.yml", args.config_dir)); + let _compose = DockerCompose::new(&format!("{}/docker-compose.yaml", args.config_dir)); // Uses ShotoverProcess instead of ShotoverManager for a more accurate benchmark let shotover_manager = ShotoverProcess::new(&format!("{}/topology.yaml", args.config_dir)); diff --git a/shotover-proxy/src/transforms/cassandra/sink_cluster/mod.rs b/shotover-proxy/src/transforms/cassandra/sink_cluster/mod.rs index d3e1b2e60..e89ea2d71 100644 --- a/shotover-proxy/src/transforms/cassandra/sink_cluster/mod.rs +++ b/shotover-proxy/src/transforms/cassandra/sink_cluster/mod.rs @@ -214,6 +214,22 @@ impl CassandraSinkCluster { async fn send_message(&mut self, mut messages: Messages) -> ChainResponse { if self.nodes_rx.has_changed()? { self.pool.update_nodes(&mut self.nodes_rx); + + // recreate the control connection if it is down + if let Some(address) = self.control_connection_address { + if !self + .pool + .nodes() + .iter() + .any(|x| x.address == address && x.is_up) + { + let address = self + .pool + .get_round_robin_node_in_dc_rack(&self.local_shotover_node.rack) + .address; + self.create_control_connection(address).await?; + } + } } let tables_to_rewrite: Vec = messages @@ -251,9 +267,7 @@ impl CassandraSinkCluster { .address }; - self.control_connection = - Some(self.connection_factory.new_connection(random_point).await?); - self.control_connection_address = Some(random_point); + self.create_control_connection(random_point).await?; } if !self.init_handshake_complete { @@ -500,16 +514,11 @@ impl CassandraSinkCluster { // If we have to populate the local_nodes at this point then that means the control connection // may not have been made against a node in the configured data_center/rack. // Therefore we need to recreate the control connection to ensure that it is in the configured data_center/rack. - let random_address = self + let address = self .pool .get_round_robin_node_in_dc_rack(&self.local_shotover_node.rack) .address; - self.control_connection = Some( - self.connection_factory - .new_connection(random_address) - .await?, - ); - self.control_connection_address = Some(random_address); + self.create_control_connection(address).await?; } tracing::info!( "Control connection finalized against node at: {:?}", @@ -519,6 +528,13 @@ impl CassandraSinkCluster { Ok(()) } + async fn create_control_connection(&mut self, address: SocketAddr) -> Result<()> { + self.control_connection = Some(self.connection_factory.new_connection(address).await?); + self.control_connection_address = Some(address); + + Ok(()) + } + fn get_rewrite_table(&self, request: &mut Message, index: usize) -> Option { if let Some(Frame::Cassandra(cassandra)) = request.frame() { // No need to handle Batch as selects can only occur on Query diff --git a/shotover-proxy/src/transforms/cassandra/sink_cluster/routing_key.rs b/shotover-proxy/src/transforms/cassandra/sink_cluster/routing_key.rs index 542123b41..602123020 100644 --- a/shotover-proxy/src/transforms/cassandra/sink_cluster/routing_key.rs +++ b/shotover-proxy/src/transforms/cassandra/sink_cluster/routing_key.rs @@ -30,7 +30,10 @@ fn serialize_routing_key_with_indexes( 0 => None, 1 => values .get(pk_indexes[0] as usize) - .map(|value| value.serialize_to_vec(version)), + .and_then(|value| match value { + Value::Some(value) => Some(value.serialize_to_vec(version)), + _ => None, + }), _ => { let mut buf = vec![]; if pk_indexes diff --git a/shotover-proxy/tests/cassandra_int_tests/cluster_single_rack_v4.rs b/shotover-proxy/tests/cassandra_int_tests/cluster_single_rack_v4.rs index 934684d2a..4ed9f7c6b 100644 --- a/shotover-proxy/tests/cassandra_int_tests/cluster_single_rack_v4.rs +++ b/shotover-proxy/tests/cassandra_int_tests/cluster_single_rack_v4.rs @@ -255,18 +255,13 @@ pub async fn test_node_going_down( shotover_manager: ShotoverManager, driver: CassandraDriver, ) { - { - let mut connection_shotover = CassandraConnection::new("127.0.0.1", 9042, driver).await; - connection_shotover - .enable_schema_awaiter("172.16.1.2:9044", None) - .await; - // Use Replication 2 in case it ends up on the node that we kill - run_query(&connection_shotover, "CREATE KEYSPACE cluster_single_rack_node_going_down WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 2 };").await; - run_query(&connection_shotover, "CREATE TABLE cluster_single_rack_node_going_down.test_table (pk varchar PRIMARY KEY, col1 int, col2 boolean);").await; - connection_shotover.await_schema_agreement().await; - - // TODO: hold onto this connection and use it to test how we handle preexisting connections before the node is stopped - } + let mut connection_shotover = CassandraConnection::new("127.0.0.1", 9042, driver).await; + connection_shotover + .enable_schema_awaiter("172.16.1.2:9044", None) + .await; + // Use Replication 2 in case it ends up on the node that we kill + run_query(&connection_shotover, "CREATE KEYSPACE cluster_single_rack_node_going_down WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 2 };").await; + run_query(&connection_shotover, "CREATE TABLE cluster_single_rack_node_going_down.test_table (pk varchar PRIMARY KEY, col1 int, col2 boolean);").await; { let event_connection_direct = @@ -320,14 +315,20 @@ pub async fn test_node_going_down( .await .expect_err("CassandraSinkCluster must filter out this event"); - // test that shotover handles preexisting connections after node goes down - // TODO: test_connection_handles_node_down(&old_connection).await; + let new_connection = CassandraConnection::new("127.0.0.1", 9042, driver).await; + + // setup data to read + run_query(&new_connection, "INSERT INTO cluster_single_rack_node_going_down.test_table (pk, col1, col2) VALUES ('pk1', 42, true);").await; + run_query(&new_connection, "INSERT INTO cluster_single_rack_node_going_down.test_table (pk, col1, col2) VALUES ('pk2', 413, false);").await; // test that shotover handles new connections after node goes down - let new_connection = CassandraConnection::new("127.0.0.1", 9042, driver).await; test_connection_handles_node_down(&new_connection).await; + + // test that shotover handles preexisting connections after node goes down + test_connection_handles_node_down(&connection_shotover).await; } + std::mem::drop(connection_shotover); // Purposefully dispose of these as we left the underlying cassandra cluster in a non-recoverable state std::mem::drop(shotover_manager); std::mem::drop(compose); @@ -337,10 +338,6 @@ async fn test_connection_handles_node_down(connection: &CassandraConnection) { // test a query that hits the control node and performs rewriting test_rewrite_system_local(connection).await; - // test queries that get routed across all nodes and performs reading and writing - run_query(connection, "INSERT INTO cluster_single_rack_node_going_down.test_table (pk, col1, col2) VALUES ('pk1', 42, true);").await; - run_query(connection, "INSERT INTO cluster_single_rack_node_going_down.test_table (pk, col1, col2) VALUES ('pk2', 413, false);").await; - // run this a few times to make sure we arent getting lucky with the routing for _ in 0..10 { assert_query_result( diff --git a/shotover-proxy/tests/cassandra_int_tests/mod.rs b/shotover-proxy/tests/cassandra_int_tests/mod.rs index c65373f57..50be5b10d 100644 --- a/shotover-proxy/tests/cassandra_int_tests/mod.rs +++ b/shotover-proxy/tests/cassandra_int_tests/mod.rs @@ -38,6 +38,8 @@ mod prepared_statements; #[cfg(feature = "cassandra-cpp-driver-tests")] #[cfg(feature = "alpha-transforms")] mod protect; +#[cfg(feature = "cassandra-cpp-driver-tests")] +mod routing; mod table; mod udt; @@ -64,7 +66,7 @@ where #[tokio::test(flavor = "multi_thread")] #[serial] async fn test_passthrough(#[case] driver: CassandraDriver) { - let _compose = DockerCompose::new("example-configs/cassandra-passthrough/docker-compose.yml"); + let _compose = DockerCompose::new("example-configs/cassandra-passthrough/docker-compose.yaml"); let _shotover_manager = ShotoverManager::from_topology_file("example-configs/cassandra-passthrough/topology.yaml"); @@ -81,7 +83,7 @@ async fn test_passthrough(#[case] driver: CassandraDriver) { #[tokio::test(flavor = "multi_thread")] #[serial] async fn test_passthrough_encode(#[case] driver: CassandraDriver) { - let _compose = DockerCompose::new("example-configs/cassandra-passthrough/docker-compose.yml"); + let _compose = DockerCompose::new("example-configs/cassandra-passthrough/docker-compose.yaml"); let _shotover_manager = ShotoverManager::from_topology_file( "example-configs/cassandra-passthrough/topology-encode.yaml", @@ -100,7 +102,7 @@ async fn test_passthrough_encode(#[case] driver: CassandraDriver) { #[serial] async fn test_source_tls_and_single_tls(#[case] driver: CassandraDriver) { test_helpers::cert::generate_cassandra_test_certs(); - let _compose = DockerCompose::new("example-configs/cassandra-tls/docker-compose.yml"); + let _compose = DockerCompose::new("example-configs/cassandra-tls/docker-compose.yaml"); let _shotover_manager = ShotoverManager::from_topology_file("example-configs/cassandra-tls/topology.yaml"); @@ -132,7 +134,7 @@ async fn test_source_tls_and_single_tls(#[case] driver: CassandraDriver) { #[serial] async fn test_cluster_single_rack_v3(#[case] driver: CassandraDriver) { let _compose = - DockerCompose::new("example-configs/cassandra-cluster/docker-compose-cassandra-v3.yml"); + DockerCompose::new("example-configs/cassandra-cluster/docker-compose-cassandra-v3.yaml"); { let _shotover_manager = ShotoverManager::from_topology_file( @@ -148,6 +150,7 @@ async fn test_cluster_single_rack_v3(#[case] driver: CassandraDriver) { }; standard_test_suite(&connection, driver).await; cluster_single_rack_v3::test_dummy_peers(&connection().await).await; + routing::test("127.0.0.1", 9042, "172.16.1.2", 9042).await; //Check for bugs in cross connection state native_types::test(&connection().await).await; @@ -164,7 +167,7 @@ async fn test_cluster_single_rack_v3(#[case] driver: CassandraDriver) { #[serial] async fn test_cluster_single_rack_v4(#[case] driver: CassandraDriver) { let compose = - DockerCompose::new("example-configs/cassandra-cluster/docker-compose-cassandra-v4.yml"); + DockerCompose::new("example-configs/cassandra-cluster/docker-compose-cassandra-v4.yaml"); let connection = || async { let mut connection = CassandraConnection::new("127.0.0.1", 9042, driver).await; @@ -181,6 +184,7 @@ async fn test_cluster_single_rack_v4(#[case] driver: CassandraDriver) { standard_test_suite(&connection, driver).await; cluster_single_rack_v4::test(&connection().await).await; + routing::test("127.0.0.1", 9042, "172.16.1.2", 9044).await; //Check for bugs in cross connection state let mut connection2 = CassandraConnection::new("127.0.0.1", 9042, driver).await; connection2 @@ -212,7 +216,7 @@ async fn test_cluster_single_rack_v4(#[case] driver: CassandraDriver) { #[serial] async fn test_cluster_multi_rack(#[case] driver: CassandraDriver) { let _compose = - DockerCompose::new("example-configs/cassandra-cluster-multi-rack/docker-compose.yml"); + DockerCompose::new("example-configs/cassandra-cluster-multi-rack/docker-compose.yaml"); { let _shotover_manager_rack1 = ShotoverManager::from_topology_file_without_observability( @@ -252,7 +256,7 @@ async fn test_source_tls_and_cluster_tls(#[case] driver: CassandraDriver) { test_helpers::cert::generate_cassandra_test_certs(); let ca_cert = "example-configs/docker-images/cassandra-tls-4.0.6/certs/localhost_CA.crt"; - let _compose = DockerCompose::new("example-configs/cassandra-cluster-tls/docker-compose.yml"); + let _compose = DockerCompose::new("example-configs/cassandra-cluster-tls/docker-compose.yaml"); { let _shotover_manager = ShotoverManager::from_topology_file( "example-configs/cassandra-cluster-tls/topology.yaml", @@ -295,7 +299,7 @@ async fn test_cassandra_redis_cache(#[case] driver: CassandraDriver) { let recorder = DebuggingRecorder::new(); let snapshotter = recorder.snapshotter(); recorder.install().unwrap(); - let _compose = DockerCompose::new("example-configs/cassandra-redis-cache/docker-compose.yml"); + let _compose = DockerCompose::new("example-configs/cassandra-redis-cache/docker-compose.yaml"); let shotover_manager = ShotoverManager::from_topology_file_without_observability( "example-configs/cassandra-redis-cache/topology.yaml", @@ -323,7 +327,8 @@ async fn test_cassandra_redis_cache(#[case] driver: CassandraDriver) { #[tokio::test(flavor = "multi_thread")] #[serial] async fn test_cassandra_protect_transform_local(#[case] driver: CassandraDriver) { - let _compose = DockerCompose::new("example-configs/cassandra-protect-local/docker-compose.yml"); + let _compose = + DockerCompose::new("example-configs/cassandra-protect-local/docker-compose.yaml"); let _shotover_manager = ShotoverManager::from_topology_file( "example-configs/cassandra-protect-local/topology.yaml", @@ -344,7 +349,7 @@ async fn test_cassandra_protect_transform_local(#[case] driver: CassandraDriver) #[tokio::test(flavor = "multi_thread")] #[serial] async fn test_cassandra_protect_transform_aws(#[case] driver: CassandraDriver) { - let _compose = DockerCompose::new("example-configs/cassandra-protect-aws/docker-compose.yml"); + let _compose = DockerCompose::new("example-configs/cassandra-protect-aws/docker-compose.yaml"); let _compose_aws = DockerCompose::new_moto(); let _shotover_manager = @@ -491,7 +496,7 @@ async fn test_cassandra_peers_rewrite_cassandra3(#[case] driver: CassandraDriver #[serial] async fn test_cassandra_request_throttling(#[case] driver: CassandraDriver) { let _docker_compose = - DockerCompose::new("example-configs/cassandra-passthrough/docker-compose.yml"); + DockerCompose::new("example-configs/cassandra-passthrough/docker-compose.yaml"); let _shotover_manager = ShotoverManager::from_topology_file("tests/test-configs/cassandra-request-throttling.yaml"); @@ -586,7 +591,7 @@ async fn test_cassandra_request_throttling(#[case] driver: CassandraDriver) { #[serial] async fn test_events_keyspace(#[case] driver: CassandraDriver) { let _docker_compose = - DockerCompose::new("example-configs/cassandra-passthrough/docker-compose.yml"); + DockerCompose::new("example-configs/cassandra-passthrough/docker-compose.yaml"); let _shotover_manager = ShotoverManager::from_topology_file("example-configs/cassandra-passthrough/topology.yaml"); diff --git a/shotover-proxy/tests/cassandra_int_tests/prepared_statements.rs b/shotover-proxy/tests/cassandra_int_tests/prepared_statements.rs index 25996441f..48d070177 100644 --- a/shotover-proxy/tests/cassandra_int_tests/prepared_statements.rs +++ b/shotover-proxy/tests/cassandra_int_tests/prepared_statements.rs @@ -80,28 +80,26 @@ async fn select_cross_connection( async fn use_statement(session: &CassandraConnection) { // Create prepared command with the correct keyspace run_query(session, "USE test_prepare_statements;").await; - let _prepared = session + let prepared = session .prepare("INSERT INTO table_1 (id) VALUES (?);") .await; // change the keyspace to be incorrect run_query(session, "USE test_prepare_statements_empty;").await; - // TODO: Somehow executing the query fails when run through shotover but succeeds when run directly against cassandra - // We should investigate and fix the issue in shotover // observe query completing against the original keyspace without errors - // assert_eq!( - // session.execute_prepared(&prepared, 358), - // Vec::>::new() - // ); - - // // observe that the query succeeded despite the keyspace being incorrect at the time. - // assert_query_result( - // session, - // "SELECT id FROM test_prepare_statements.table_1 WHERE id = 358;", - // &[&[ResultValue::Int(358)]], - // ) - // .await; + assert_eq!( + session.execute_prepared(&prepared, 358).await, + Vec::>::new() + ); + + // observe that the query succeeded despite the keyspace being incorrect at the time. + assert_query_result( + session, + "SELECT id FROM test_prepare_statements.table_1 WHERE id = 358;", + &[&[ResultValue::Int(358)]], + ) + .await; } pub async fn test(session: &CassandraConnection, connection_creator: impl Fn() -> Fut) diff --git a/shotover-proxy/tests/cassandra_int_tests/routing.rs b/shotover-proxy/tests/cassandra_int_tests/routing.rs new file mode 100644 index 000000000..5e2870692 --- /dev/null +++ b/shotover-proxy/tests/cassandra_int_tests/routing.rs @@ -0,0 +1,93 @@ +use crate::helpers::cassandra::{run_query, CassandraConnection, CassandraDriver}; + +pub async fn create_keyspace(connection: &mut CassandraConnection) { + let create_ks: &'static str = "CREATE KEYSPACE IF NOT EXISTS test_routing_ks WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 };"; + run_query(connection, create_ks).await; +} + +pub async fn create_table(connection: &mut CassandraConnection) { + let create_table_cql = + "CREATE TABLE IF NOT EXISTS test_routing_ks.my_test_table (key int PRIMARY KEY, name text);"; + run_query(connection, create_table_cql).await; +} + +pub async fn test( + shotover_contact_point: &str, + shotover_port: u16, + cassandra_contact_point: &str, + cassandra_port: u16, +) { + let mut shotover = CassandraConnection::new( + shotover_contact_point, + shotover_port, + CassandraDriver::Scylla, + ) + .await; + shotover + .enable_schema_awaiter( + &format!("{}:{}", cassandra_contact_point, cassandra_port), + None, + ) + .await; + let cassandra = CassandraConnection::new( + cassandra_contact_point, + cassandra_port, + CassandraDriver::Scylla, + ) + .await; + + create_keyspace(&mut shotover).await; + create_table(&mut shotover).await; + + let insert_cql = "INSERT INTO test_routing_ks.my_test_table (key, name) VALUES (?, 'my_name')"; + let prepared_insert = shotover.prepare(insert_cql).await; + + let select_cql = "SELECT name FROM test_routing_ks.my_test_table WHERE key = ?;"; + let prepared_select = shotover.prepare(select_cql).await; + + let update_cql = "UPDATE test_routing_ks.my_test_table SET name = 'not_my_name' WHERE key = ?"; + let prepared_update = cassandra.prepare(update_cql).await; + + let delete_cql = "DELETE FROM test_routing_ks.my_test_table WHERE key = ?;"; + let prepared_delete = cassandra.prepare(delete_cql).await; + + for key in 0..10 { + let shotover_hit = shotover + .execute_prepared_coordinator_node(&prepared_insert, key) + .await; + let cassandra_hit = cassandra + .execute_prepared_coordinator_node(&prepared_insert, key) + .await; + assert_eq!(shotover_hit, cassandra_hit); + } + + for key in 0..10 { + let shotover_hit = shotover + .execute_prepared_coordinator_node(&prepared_select, key) + .await; + let cassandra_hit = cassandra + .execute_prepared_coordinator_node(&prepared_select, key) + .await; + assert_eq!(shotover_hit, cassandra_hit); + } + + for key in 0..10 { + let shotover_hit = shotover + .execute_prepared_coordinator_node(&prepared_update, key) + .await; + let cassandra_hit = cassandra + .execute_prepared_coordinator_node(&prepared_update, key) + .await; + assert_eq!(shotover_hit, cassandra_hit); + } + + for key in 0..10 { + let shotover_hit = shotover + .execute_prepared_coordinator_node(&prepared_delete, key) + .await; + let cassandra_hit = cassandra + .execute_prepared_coordinator_node(&prepared_delete, key) + .await; + assert_eq!(shotover_hit, cassandra_hit); + } +} diff --git a/shotover-proxy/tests/examples/mod.rs b/shotover-proxy/tests/examples/mod.rs index c3e735312..017f2b360 100644 --- a/shotover-proxy/tests/examples/mod.rs +++ b/shotover-proxy/tests/examples/mod.rs @@ -9,7 +9,7 @@ use test_helpers::docker_compose::DockerCompose; #[serial] async fn test_cassandra_rewrite_peers_example() { let _docker_compose = - DockerCompose::new("example-configs-docker/cassandra-peers-rewrite/docker-compose.yml"); + DockerCompose::new("example-configs-docker/cassandra-peers-rewrite/docker-compose.yaml"); let connection = CassandraConnection::new("172.16.1.2", 9043, CassandraDriver::Datastax).await; diff --git a/shotover-proxy/tests/helpers/cassandra.rs b/shotover-proxy/tests/helpers/cassandra.rs index fbee55a11..4b6c1e631 100644 --- a/shotover-proxy/tests/helpers/cassandra.rs +++ b/shotover-proxy/tests/helpers/cassandra.rs @@ -1,10 +1,11 @@ #[cfg(feature = "cassandra-cpp-driver-tests")] use cassandra_cpp::{ stmt, Batch, BatchType, CassErrorCode, CassResult, Cluster, Error, ErrorKind, - PreparedStatement, Session as DatastaxSession, Ssl, Value, ValueType, + PreparedStatement as PreparedStatementCpp, Session as DatastaxSession, Ssl, Value, ValueType, }; #[cfg(feature = "cassandra-cpp-driver-tests")] use cassandra_protocol::frame::message_error::ErrorType; +use cassandra_protocol::types::IntoRustByIndex; use cassandra_protocol::{ frame::message_error::ErrorBody, types::cassandra_type::{wrapper_fn, CassandraType}, @@ -26,21 +27,25 @@ use cdrs_tokio::{ }; use openssl::ssl::{SslContext, SslMethod}; use ordered_float::OrderedFloat; +use scylla::frame::response::result::CqlValue; +use scylla::prepared_statement::PreparedStatement as PreparedStatementScylla; use scylla::{Session as SessionScylla, SessionBuilder as SessionBuilderScylla}; #[cfg(feature = "cassandra-cpp-driver-tests")] use std::fs::read_to_string; +use std::net::IpAddr; use std::sync::Arc; #[derive(Debug)] pub enum PreparedQuery { #[cfg(feature = "cassandra-cpp-driver-tests")] - Datastax(PreparedStatement), + Datastax(PreparedStatementCpp), CdrsTokio(CdrsTokioPreparedQuery), + Scylla(PreparedStatementScylla), } impl PreparedQuery { #[cfg(feature = "cassandra-cpp-driver-tests")] - fn as_datastax(&self) -> &PreparedStatement { + fn as_datastax(&self) -> &PreparedStatementCpp { match self { PreparedQuery::Datastax(p) => p, _ => panic!("Not PreparedQuery::Datastax"), @@ -50,10 +55,16 @@ impl PreparedQuery { fn as_cdrs(&self) -> &CdrsTokioPreparedQuery { match self { PreparedQuery::CdrsTokio(p) => p, - #[cfg(feature = "cassandra-cpp-driver-tests")] _ => panic!("Not PreparedQuery::CdrsTokio"), } } + + fn as_scylla(&self) -> &PreparedStatementScylla { + match self { + PreparedQuery::Scylla(s) => s, + _ => panic!("Not PreparedQuery::Scylla"), + } + } } #[cfg(feature = "cassandra-cpp-driver-tests")] @@ -74,6 +85,7 @@ pub enum CassandraDriver { #[cfg(feature = "cassandra-cpp-driver-tests")] Datastax, CdrsTokio, + Scylla, } type CdrsTokioSessionInstance = CdrsTokioSession< @@ -92,6 +104,10 @@ pub enum CassandraConnection { session: CdrsTokioSessionInstance, schema_awaiter: Option, }, + Scylla { + session: SessionScylla, + schema_awaiter: Option, + }, } impl CassandraConnection { @@ -104,6 +120,7 @@ impl CassandraConnection { match driver { #[cfg(feature = "cassandra-cpp-driver-tests")] CassandraDriver::Datastax => { + cassandra_cpp::set_log_logger(); let mut cluster = Cluster::default(); cluster.set_contact_points(contact_points).unwrap(); cluster.set_credentials("cassandra", "cassandra").unwrap(); @@ -147,6 +164,24 @@ impl CassandraConnection { schema_awaiter: None, } } + CassandraDriver::Scylla => { + let session = SessionBuilderScylla::new() + .known_nodes( + &contact_points + .split(',') + .map(|contact_point| format!("{contact_point}:{port}")) + .collect::>(), + ) + .user("cassandra", "cassandra") + .build() + .await + .unwrap(); + + CassandraConnection::Scylla { + session, + schema_awaiter: None, + } + } } } @@ -154,7 +189,6 @@ impl CassandraConnection { pub fn as_cdrs(&self) -> &CdrsTokioSessionInstance { match self { Self::CdrsTokio { session, .. } => session, - #[cfg(feature = "cassandra-cpp-driver-tests")] _ => panic!("Not CdrsTokio"), } } @@ -200,6 +234,7 @@ impl CassandraConnection { } // TODO actually implement TLS for cdrs-tokio CassandraDriver::CdrsTokio => todo!(), + CassandraDriver::Scylla => todo!(), } } @@ -215,6 +250,7 @@ impl CassandraConnection { #[cfg(feature = "cassandra-cpp-driver-tests")] Self::Datastax { schema_awaiter, .. } => schema_awaiter, Self::CdrsTokio { schema_awaiter, .. } => schema_awaiter, + Self::Scylla { schema_awaiter, .. } => schema_awaiter, }; *schema_awaiter = Some( @@ -233,6 +269,7 @@ impl CassandraConnection { #[cfg(feature = "cassandra-cpp-driver-tests")] Self::Datastax { schema_awaiter, .. } => schema_awaiter, Self::CdrsTokio { schema_awaiter, .. } => schema_awaiter, + Self::Scylla { schema_awaiter, .. } => schema_awaiter, }; if let Some(schema_awaiter) = schema_awaiter { schema_awaiter.await_schema_agreement().await.unwrap(); @@ -257,6 +294,21 @@ impl CassandraConnection { let response = session.query(query).await.unwrap(); Self::process_cdrs_response(response) } + Self::Scylla { session, .. } => { + let rows = session.query(query, ()).await.unwrap().rows; + match rows { + Some(rows) => rows + .into_iter() + .map(|x| { + x.columns + .into_iter() + .map(|col| ResultValue::new_from_scylla(col.unwrap())) + .collect() + }) + .collect(), + None => vec![], + } + } }; let query = query.to_uppercase(); @@ -278,6 +330,7 @@ impl CassandraConnection { session.execute(&statement).await } Self::CdrsTokio { .. } => todo!(), + Self::Scylla { .. } => todo!(), } } @@ -303,6 +356,7 @@ impl CassandraConnection { _ => todo!(), } } + Self::Scylla { .. } => todo!(), } } @@ -326,6 +380,75 @@ impl CassandraConnection { let query = session.prepare(query).await.unwrap(); PreparedQuery::CdrsTokio(query) } + Self::Scylla { session, .. } => { + let mut prepared = session.prepare(query).await.unwrap(); + prepared.set_tracing(true); + PreparedQuery::Scylla(prepared) + } + } + } + + #[allow(dead_code)] + pub async fn execute_prepared_coordinator_node( + &self, + prepared_query: &PreparedQuery, + key: i32, + ) -> IpAddr { + match self { + #[cfg(feature = "cassandra-cpp-driver-tests")] + Self::Datastax { .. } => { + todo!(); + } + Self::CdrsTokio { session, .. } => { + let statement = prepared_query.as_cdrs(); + let query_params = QueryParamsBuilder::new() + .with_values(query_values!(key)) + .build(); + + let params = StatementParams { + query_params, + is_idempotent: false, + keyspace: None, + token: None, + routing_key: None, + tracing: true, + warnings: false, + speculative_execution_policy: None, + retry_policy: None, + beta_protocol: false, + }; + + let response = session.exec_with_params(statement, ¶ms).await.unwrap(); + + let tracing_id = response.tracing_id.unwrap(); + tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; // let cassandra finish writing to the tracing table + let row = session + .query(format!( + "SELECT coordinator FROM system_traces.sessions WHERE session_id = {}", + tracing_id + )) + .await + .unwrap() + .response_body() + .unwrap() + .into_rows() + .unwrap(); + + row[0].get_by_index(0).unwrap().unwrap() + } + Self::Scylla { session, .. } => { + let statement = prepared_query.as_scylla(); + let response = session.execute(statement, (key,)).await.unwrap(); + let tracing_id = response.tracing_id.unwrap(); + + tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; + session + .get_tracing_info(&tracing_id) + .await + .unwrap() + .coordinator + .unwrap() + } } } @@ -374,6 +497,7 @@ impl CassandraConnection { Self::process_cdrs_response(response) } + Self::Scylla { .. } => todo!(), } } @@ -408,6 +532,7 @@ impl CassandraConnection { session.batch(batch).await.unwrap(); } + Self::Scylla { .. } => todo!(), } } @@ -428,6 +553,7 @@ impl CassandraConnection { } } Self::CdrsTokio { .. } => todo!(), + Self::Scylla { .. } => todo!(), } } @@ -666,6 +792,55 @@ impl ResultValue { CassandraType::Null => ResultValue::Null, } } + + pub fn new_from_scylla(value: CqlValue) -> Self { + match value { + CqlValue::Ascii(ascii) => Self::Ascii(ascii), + CqlValue::BigInt(big_int) => Self::BigInt(big_int), + CqlValue::Blob(blob) => Self::Blob(blob), + CqlValue::Boolean(b) => Self::Boolean(b), + CqlValue::Counter(_counter) => todo!(), + CqlValue::Decimal(_decimal) => todo!(), + CqlValue::Float(float) => Self::Float(float.into()), + CqlValue::Int(int) => Self::Int(int), + CqlValue::Timestamp(_timestamp) => todo!(), + CqlValue::Uuid(uuid) => Self::Uuid(uuid), + CqlValue::Varint(_var_int) => todo!(), + CqlValue::Timeuuid(timeuuid) => Self::TimeUuid(timeuuid), + CqlValue::Inet(ip) => Self::Inet(ip.to_string()), + CqlValue::Date(_date) => todo!(), + CqlValue::Time(_time) => todo!(), + CqlValue::SmallInt(small_int) => Self::SmallInt(small_int), + CqlValue::TinyInt(tiny_int) => Self::TinyInt(tiny_int), + CqlValue::Duration(_duration) => todo!(), + CqlValue::Double(double) => Self::Double(double.into()), + CqlValue::Text(text) => Self::Varchar(text), + CqlValue::Empty => Self::Null, + CqlValue::List(mut list) => { + Self::List(list.drain(..).map(ResultValue::new_from_scylla).collect()) + } + CqlValue::Set(mut set) => { + Self::Set(set.drain(..).map(ResultValue::new_from_scylla).collect()) + } + CqlValue::Map(mut map) => Self::Map( + map.drain(..) + .map(|(k, v)| { + ( + ResultValue::new_from_scylla(k), + ResultValue::new_from_scylla(v), + ) + }) + .collect(), + ), + CqlValue::Tuple(mut tuple) => Self::Tuple( + tuple + .drain(..) + .map(|element| ResultValue::new_from_scylla(element.unwrap())) + .collect(), + ), + CqlValue::UserDefinedType { .. } => todo!(), + } + } } /// Execute a `query` against the `session` and assert that the result rows match `expected_rows` diff --git a/shotover-proxy/tests/redis_int_tests/mod.rs b/shotover-proxy/tests/redis_int_tests/mod.rs index 9c9a31a92..ab6c6fc10 100644 --- a/shotover-proxy/tests/redis_int_tests/mod.rs +++ b/shotover-proxy/tests/redis_int_tests/mod.rs @@ -15,7 +15,7 @@ pub mod basic_driver_tests; #[tokio::test(flavor = "multi_thread")] #[serial] async fn test_passthrough() { - let _compose = DockerCompose::new("example-configs/redis-passthrough/docker-compose.yml"); + let _compose = DockerCompose::new("example-configs/redis-passthrough/docker-compose.yaml"); let shotover_manager = ShotoverManager::from_topology_file("example-configs/redis-passthrough/topology.yaml"); let mut connection = shotover_manager.redis_connection_async(6379).await; @@ -44,7 +44,7 @@ async fn test_cluster_tls() { test_helpers::cert::generate_redis_test_certs(Path::new("example-configs/redis-tls/certs")); { - let _compose = DockerCompose::new("example-configs/redis-cluster-tls/docker-compose.yml"); + let _compose = DockerCompose::new("example-configs/redis-cluster-tls/docker-compose.yaml"); let shotover_manager = ShotoverManager::from_topology_file("example-configs/redis-cluster-tls/topology.yaml"); @@ -58,7 +58,7 @@ async fn test_cluster_tls() { // Quick test to verify it works with private key { let _compose = - DockerCompose::new("example-configs/redis-cluster-tls/docker-compose-with-key.yml"); + DockerCompose::new("example-configs/redis-cluster-tls/docker-compose-with-key.yaml"); let shotover_manager = ShotoverManager::from_topology_file( "example-configs/redis-cluster-tls/topology-with-key.yaml", ); @@ -73,7 +73,7 @@ async fn test_cluster_tls() { async fn test_source_tls_and_single_tls() { test_helpers::cert::generate_redis_test_certs(Path::new("example-configs/redis-tls/certs")); - let _compose = DockerCompose::new("example-configs/redis-tls/docker-compose.yml"); + let _compose = DockerCompose::new("example-configs/redis-tls/docker-compose.yaml"); let shotover_manager = ShotoverManager::from_topology_file("example-configs/redis-tls/topology.yaml"); @@ -100,7 +100,7 @@ async fn test_source_tls_and_single_tls() { #[serial] async fn test_cluster_ports_rewrite() { let _compose = - DockerCompose::new("tests/test-configs/redis-cluster-ports-rewrite/docker-compose.yml"); + DockerCompose::new("tests/test-configs/redis-cluster-ports-rewrite/docker-compose.yaml"); let shotover_manager = ShotoverManager::from_topology_file( "tests/test-configs/redis-cluster-ports-rewrite/topology.yaml", ); @@ -118,7 +118,7 @@ async fn test_cluster_ports_rewrite() { #[tokio::test(flavor = "multi_thread")] #[serial] async fn test_redis_multi() { - let _compose = DockerCompose::new("example-configs/redis-multi/docker-compose.yml"); + let _compose = DockerCompose::new("example-configs/redis-multi/docker-compose.yaml"); let shotover_manager = ShotoverManager::from_topology_file("example-configs/redis-multi/topology.yaml"); let mut connection = shotover_manager.redis_connection_async(6379).await; @@ -131,7 +131,7 @@ async fn test_redis_multi() { #[tokio::test(flavor = "multi_thread")] #[serial] async fn test_cluster_auth_redis() { - let _compose = DockerCompose::new("tests/test-configs/redis-cluster-auth/docker-compose.yml"); + let _compose = DockerCompose::new("tests/test-configs/redis-cluster-auth/docker-compose.yaml"); let shotover_manager = ShotoverManager::from_topology_file("tests/test-configs/redis-cluster-auth/topology.yaml"); let mut connection = shotover_manager.redis_connection_async(6379).await; @@ -143,7 +143,7 @@ async fn test_cluster_auth_redis() { #[tokio::test(flavor = "multi_thread")] #[serial] async fn test_cluster_hiding_redis() { - let _compose = DockerCompose::new("example-configs/redis-cluster-hiding/docker-compose.yml"); + let _compose = DockerCompose::new("example-configs/redis-cluster-hiding/docker-compose.yaml"); let shotover_manager = ShotoverManager::from_topology_file("example-configs/redis-cluster-hiding/topology.yaml"); @@ -159,7 +159,7 @@ async fn test_cluster_hiding_redis() { #[tokio::test(flavor = "multi_thread")] #[serial] async fn test_cluster_handling_redis() { - let _compose = DockerCompose::new("example-configs/redis-cluster-handling/docker-compose.yml"); + let _compose = DockerCompose::new("example-configs/redis-cluster-handling/docker-compose.yaml"); let shotover_manager = ShotoverManager::from_topology_file("example-configs/redis-cluster-handling/topology.yaml"); @@ -176,7 +176,7 @@ async fn test_cluster_handling_redis() { #[tokio::test(flavor = "multi_thread")] #[serial] async fn test_cluster_dr_redis() { - let _compose = DockerCompose::new("example-configs/redis-cluster-dr/docker-compose.yml"); + let _compose = DockerCompose::new("example-configs/redis-cluster-dr/docker-compose.yaml"); let nodes = vec![ "redis://127.0.0.1:2120/", diff --git a/shotover-proxy/tests/scripts/cassandra-cluster.sh b/shotover-proxy/tests/scripts/cassandra-cluster.sh index 59901d787..923d46d08 100644 --- a/shotover-proxy/tests/scripts/cassandra-cluster.sh +++ b/shotover-proxy/tests/scripts/cassandra-cluster.sh @@ -8,9 +8,9 @@ SCRIPT_DIR="$(cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" # and returns that instead. So if our tests fails, our CI system will still pick it up! function defer { - docker-compose -f $SCRIPT_DIR/../../example-configs/cassandra-cluster/docker-compose.yml down + docker-compose -f $SCRIPT_DIR/../../example-configs/cassandra-cluster/docker-compose.yaml down } trap defer EXIT -docker-compose -f $SCRIPT_DIR/../../example-configs/cassandra-cluster/docker-compose.yml up +docker-compose -f $SCRIPT_DIR/../../example-configs/cassandra-cluster/docker-compose.yaml up diff --git a/shotover-proxy/tests/scripts/cassandra.sh b/shotover-proxy/tests/scripts/cassandra.sh index d523d5893..c6e196ac3 100755 --- a/shotover-proxy/tests/scripts/cassandra.sh +++ b/shotover-proxy/tests/scripts/cassandra.sh @@ -8,12 +8,12 @@ SCRIPT_DIR="$(cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" # and returns that instead. So if our tests fails, our CI system will still pick it up! function defer { - docker-compose -f $SCRIPT_DIR/../../example-configs/cassandra-standalone/docker-compose.yml down + docker-compose -f $SCRIPT_DIR/../../example-configs/cassandra-standalone/docker-compose.yaml down } trap defer EXIT -docker-compose -f $SCRIPT_DIR/../../example-configs/cassandra-standalone/docker-compose.yml up +docker-compose -f $SCRIPT_DIR/../../example-configs/cassandra-standalone/docker-compose.yaml up #cargo run -- --topology-file $SCRIPT_DIR/../../example-configs/redis-cluster/topology.yaml --config-file $SCRIPT_DIR/../../config/config.yaml diff --git a/shotover-proxy/tests/scripts/flame_profile.sh b/shotover-proxy/tests/scripts/flame_profile.sh index 230db71dd..f0a00522d 100755 --- a/shotover-proxy/tests/scripts/flame_profile.sh +++ b/shotover-proxy/tests/scripts/flame_profile.sh @@ -10,7 +10,7 @@ SCRIPT_DIR="$(cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" #echo -1 | sudo tee /proc/sys/kernel/perf_event_paranoid -docker-compose -f $SCRIPT_DIR/../../example-configs/redis-cluster/docker-compose.yml up -d +docker-compose -f $SCRIPT_DIR/../../example-configs/redis-cluster/docker-compose.yaml up -d echo "Getting ready to run proxy" sleep 5 echo "Running shotover" @@ -20,4 +20,4 @@ timeout 30 cargo flamegraph --bin shotover-proxy -- --topology-file example-conf sleep 10 timeout 20 ~/Downloads/redis-5.0.8/src/redis-benchmark -t set,get -l -q -docker-compose -f $SCRIPT_DIR/../../example-configs/redis-cluster/docker-compose.yml down +docker-compose -f $SCRIPT_DIR/../../example-configs/redis-cluster/docker-compose.yaml down diff --git a/shotover-proxy/tests/scripts/redis-dr.sh b/shotover-proxy/tests/scripts/redis-dr.sh index 9020c4c7a..c72648b06 100755 --- a/shotover-proxy/tests/scripts/redis-dr.sh +++ b/shotover-proxy/tests/scripts/redis-dr.sh @@ -8,11 +8,11 @@ SCRIPT_DIR="$(cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" # and returns that instead. So if our tests fails, our CI system will still pick it up! function defer { - docker-compose -f $SCRIPT_DIR/../../example-configs/redis-cluster/docker-compose.yml down + docker-compose -f $SCRIPT_DIR/../../example-configs/redis-cluster/docker-compose.yaml down } trap defer EXIT -docker-compose -f $SCRIPT_DIR/../../example-configs/redis-cluster-dr/docker-compose.yml up +docker-compose -f $SCRIPT_DIR/../../example-configs/redis-cluster-dr/docker-compose.yaml up #cargo run -- --topology-file $SCRIPT_DIR/../../example-configs/redis-cluster/topology.yaml --config-file $SCRIPT_DIR/../../config/config.yaml diff --git a/shotover-proxy/tests/scripts/redis.sh b/shotover-proxy/tests/scripts/redis.sh index 43a48e18a..4642ec006 100755 --- a/shotover-proxy/tests/scripts/redis.sh +++ b/shotover-proxy/tests/scripts/redis.sh @@ -8,12 +8,12 @@ SCRIPT_DIR="$(cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" # and returns that instead. So if our tests fails, our CI system will still pick it up! function defer { - docker-compose -f $SCRIPT_DIR/../../example-configs/redis-cluster/docker-compose.yml down + docker-compose -f $SCRIPT_DIR/../../example-configs/redis-cluster/docker-compose.yaml down } trap defer EXIT -docker-compose -f $SCRIPT_DIR/../../example-configs/redis-cluster/docker-compose.yml up +docker-compose -f $SCRIPT_DIR/../../example-configs/redis-cluster/docker-compose.yaml up #cargo run -- --topology-file $SCRIPT_DIR/../../example-configs/redis-cluster/topology.yaml --config-file $SCRIPT_DIR/../../config/config.yaml diff --git a/shotover-proxy/tests/scripts/soak_setup.sh b/shotover-proxy/tests/scripts/soak_setup.sh index 3a93ea969..3a22a828a 100755 --- a/shotover-proxy/tests/scripts/soak_setup.sh +++ b/shotover-proxy/tests/scripts/soak_setup.sh @@ -7,12 +7,12 @@ SCRIPT_DIR="$(cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" # and returns that instead. So if our tests fails, our CI system will still pick it up! function defer { - docker-compose -f $SCRIPT_DIR/../../example-configs/redis-cluster/docker-compose.yml down + docker-compose -f $SCRIPT_DIR/../../example-configs/redis-cluster/docker-compose.yaml down } trap defer EXIT -docker-compose -f $SCRIPT_DIR/../../example-configs/redis-cluster/docker-compose.yml up -d +docker-compose -f $SCRIPT_DIR/../../example-configs/redis-cluster/docker-compose.yaml up -d echo "Getting ready to run proxy" sleep 5 echo "Running shotover" diff --git a/shotover-proxy/tests/scripts/teardown.sh b/shotover-proxy/tests/scripts/teardown.sh index 957a6ae40..c656570ae 100755 --- a/shotover-proxy/tests/scripts/teardown.sh +++ b/shotover-proxy/tests/scripts/teardown.sh @@ -2,7 +2,7 @@ SCRIPT_DIR="$(cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" -find $SCRIPT_DIR/../../example-configs/ -name 'docker-compose.yml' -exec docker-compose -f {} rm -f -s \; +find $SCRIPT_DIR/../../example-configs/ -name 'docker-compose.yaml' -exec docker-compose -f {} rm -f -s \; yes | docker volume prune yes | docker network prune diff --git a/shotover-proxy/tests/test-configs/cassandra-passthrough-parse-request/docker-compose.yml b/shotover-proxy/tests/test-configs/cassandra-passthrough-parse-request/docker-compose.yaml similarity index 100% rename from shotover-proxy/tests/test-configs/cassandra-passthrough-parse-request/docker-compose.yml rename to shotover-proxy/tests/test-configs/cassandra-passthrough-parse-request/docker-compose.yaml diff --git a/shotover-proxy/tests/test-configs/cassandra-passthrough-parse-response/docker-compose.yml b/shotover-proxy/tests/test-configs/cassandra-passthrough-parse-response/docker-compose.yaml similarity index 100% rename from shotover-proxy/tests/test-configs/cassandra-passthrough-parse-response/docker-compose.yml rename to shotover-proxy/tests/test-configs/cassandra-passthrough-parse-response/docker-compose.yaml diff --git a/shotover-proxy/tests/test-configs/redis-cluster-auth/docker-compose.yml b/shotover-proxy/tests/test-configs/redis-cluster-auth/docker-compose.yaml similarity index 100% rename from shotover-proxy/tests/test-configs/redis-cluster-auth/docker-compose.yml rename to shotover-proxy/tests/test-configs/redis-cluster-auth/docker-compose.yaml diff --git a/shotover-proxy/tests/test-configs/redis-cluster-ports-rewrite/docker-compose.yml b/shotover-proxy/tests/test-configs/redis-cluster-ports-rewrite/docker-compose.yaml similarity index 100% rename from shotover-proxy/tests/test-configs/redis-cluster-ports-rewrite/docker-compose.yml rename to shotover-proxy/tests/test-configs/redis-cluster-ports-rewrite/docker-compose.yaml diff --git a/shotover-proxy/tests/transforms/docker-compose-moto.yml b/shotover-proxy/tests/transforms/docker-compose-moto.yaml similarity index 100% rename from shotover-proxy/tests/transforms/docker-compose-moto.yml rename to shotover-proxy/tests/transforms/docker-compose-moto.yaml diff --git a/shotover-proxy/tests/transforms/tee.rs b/shotover-proxy/tests/transforms/tee.rs index c6e2e94d9..29852796c 100644 --- a/shotover-proxy/tests/transforms/tee.rs +++ b/shotover-proxy/tests/transforms/tee.rs @@ -78,7 +78,7 @@ async fn test_fail_with_mismatch() { #[tokio::test(flavor = "multi_thread")] #[serial] async fn test_subchain_matches() { - let _compose = DockerCompose::new("example-configs/redis-passthrough/docker-compose.yml"); + let _compose = DockerCompose::new("example-configs/redis-passthrough/docker-compose.yaml"); let shotover_manager = ShotoverManager::from_topology_file("tests/test-configs/tee/subchain.yaml"); @@ -112,7 +112,7 @@ async fn test_subchain_matches() { #[tokio::test(flavor = "multi_thread")] #[serial] async fn test_subchain_with_mismatch() { - let _compose = DockerCompose::new("example-configs/redis-passthrough/docker-compose.yml"); + let _compose = DockerCompose::new("example-configs/redis-passthrough/docker-compose.yaml"); let shotover_manager = ShotoverManager::from_topology_file("tests/test-configs/tee/subchain_with_mismatch.yaml"); diff --git a/test-helpers/src/docker_compose.rs b/test-helpers/src/docker_compose.rs index 1f964267c..80dc623e0 100644 --- a/test-helpers/src/docker_compose.rs +++ b/test-helpers/src/docker_compose.rs @@ -87,7 +87,7 @@ impl DockerCompose { env::set_var("AWS_ACCESS_KEY_ID", "dummy-access-key"); env::set_var("AWS_SECRET_ACCESS_KEY", "dummy-access-key-secret"); - DockerCompose::new("tests/transforms/docker-compose-moto.yml") + DockerCompose::new("tests/transforms/docker-compose-moto.yaml") } /// Stops the container with the provided service name @@ -119,46 +119,46 @@ impl DockerCompose { fn wait_for_containers_to_startup(&self) { match self.file_path.as_ref() { - "tests/transforms/docker-compose-moto.yml" => { + "tests/transforms/docker-compose-moto.yaml" => { self.wait_for_log(r#"Press CTRL\+C to quit"#, 1, 110) } - "example-configs/redis-passthrough/docker-compose.yml" - | "example-configs/redis-tls/docker-compose.yml" => { + "example-configs/redis-passthrough/docker-compose.yaml" + | "example-configs/redis-tls/docker-compose.yaml" => { self.wait_for_log("Ready to accept connections", 1, 110) } - "example-configs/redis-multi/docker-compose.yml" => { + "example-configs/redis-multi/docker-compose.yaml" => { self.wait_for_log("Ready to accept connections", 3, 110) } - "tests/test-configs/redis-cluster-ports-rewrite/docker-compose.yml" - | "tests/test-configs/redis-cluster-auth/docker-compose.yml" - | "example-configs/redis-cluster-handling/docker-compose.yml" - | "example-configs/redis-cluster-hiding/docker-compose.yml" - | "example-configs/redis-cluster-tls/docker-compose.yml" - | "example-configs/redis-cluster-tls/docker-compose-with-key.yml" => { + "tests/test-configs/redis-cluster-ports-rewrite/docker-compose.yaml" + | "tests/test-configs/redis-cluster-auth/docker-compose.yaml" + | "example-configs/redis-cluster-handling/docker-compose.yaml" + | "example-configs/redis-cluster-hiding/docker-compose.yaml" + | "example-configs/redis-cluster-tls/docker-compose.yaml" + | "example-configs/redis-cluster-tls/docker-compose-with-key.yaml" => { self.wait_for_log("Cluster state changed", 6, 110) } - "example-configs/redis-cluster-dr/docker-compose.yml" => { + "example-configs/redis-cluster-dr/docker-compose.yaml" => { self.wait_for_log("Cluster state changed", 12, 110) } - "example-configs/cassandra-passthrough/docker-compose.yml" - | "example-configs/cassandra-tls/docker-compose.yml" - | "example-configs/cassandra-redis-cache/docker-compose.yml" - | "example-configs/cassandra-protect-local/docker-compose.yml" - | "example-configs/cassandra-protect-aws/docker-compose.yml" - | "example-configs/cassandra-request-throttling/docker-compose.yml" - | "tests/test-configs/cassandra-passthrough-parse-request/docker-compose.yml" - | "tests/test-configs/cassandra-passthrough-parse-response/docker-compose.yml" => { + "example-configs/cassandra-passthrough/docker-compose.yaml" + | "example-configs/cassandra-tls/docker-compose.yaml" + | "example-configs/cassandra-redis-cache/docker-compose.yaml" + | "example-configs/cassandra-protect-local/docker-compose.yaml" + | "example-configs/cassandra-protect-aws/docker-compose.yaml" + | "example-configs/cassandra-request-throttling/docker-compose.yaml" + | "tests/test-configs/cassandra-passthrough-parse-request/docker-compose.yaml" + | "tests/test-configs/cassandra-passthrough-parse-response/docker-compose.yaml" => { self.wait_for_log("Startup complete", 1, 110) } "tests/test-configs/cassandra-peers-rewrite/docker-compose-4.0-cassandra.yaml" | "tests/test-configs/cassandra-peers-rewrite/docker-compose-3.11-cassandra.yaml" => { self.wait_for_log("Startup complete", 2, 110) } - "example-configs-docker/cassandra-peers-rewrite/docker-compose.yml" - | "example-configs/cassandra-cluster/docker-compose-cassandra-v4.yml" - | "example-configs/cassandra-cluster/docker-compose-cassandra-v3.yml" - | "example-configs/cassandra-cluster-multi-rack/docker-compose.yml" - | "example-configs/cassandra-cluster-tls/docker-compose.yml" => { + "example-configs-docker/cassandra-peers-rewrite/docker-compose.yaml" + | "example-configs/cassandra-cluster/docker-compose-cassandra-v4.yaml" + | "example-configs/cassandra-cluster/docker-compose-cassandra-v3.yaml" + | "example-configs/cassandra-cluster-multi-rack/docker-compose.yaml" + | "example-configs/cassandra-cluster-tls/docker-compose.yaml" => { self.wait_for_log("Startup complete", 3, 180) } path => unimplemented!(