Skip to content

Commit

Permalink
757 - Integration test for Hot Reload of nodes
Browse files Browse the repository at this point in the history
  • Loading branch information
Paul Chandler committed Nov 7, 2024
1 parent 71f92c8 commit eca15d4
Show file tree
Hide file tree
Showing 5 changed files with 205 additions and 2 deletions.
5 changes: 5 additions & 0 deletions application/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,11 @@
<version>1.64</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>cassandra</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

</project>
24 changes: 24 additions & 0 deletions cassandra-test-image/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,30 @@
<artifactId>cassandra</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>


<dependency>
<groupId>com.ericsson.bss.cassandra.ecchronos</groupId>
<artifactId>application</artifactId>
</dependency>
<dependency>
<groupId>com.ericsson.bss.cassandra.ecchronos</groupId>
<artifactId>application</artifactId>
<version>1.0.0-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.12.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,26 @@

import com.datastax.oss.driver.api.core.CqlSession;
import com.datastax.oss.driver.api.core.CqlSessionBuilder;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.file.Path;
import java.nio.file.Paths;

import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.command.StartContainerCmd;
import com.github.dockerjava.api.command.StopContainerCmd;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.testcontainers.DockerClientFactory;
import org.testcontainers.containers.DockerComposeContainer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testcontainers.shaded.com.fasterxml.jackson.databind.node.BooleanNode;

public class AbstractCassandraCluster
{
private static DockerComposeContainer<?> composeContainer;
protected static DockerComposeContainer<?> composeContainer;
private static final Logger LOG = LoggerFactory.getLogger(AbstractCassandraCluster.class);
protected static String containerIP;
protected static CqlSession mySession;
Expand All @@ -41,7 +49,6 @@ public static void setup() throws InterruptedException
.resolve("cassandra-test-image/src/main/docker/docker-compose.yml");
composeContainer = new DockerComposeContainer<>(dockerComposePath.toFile());
composeContainer.start();

LOG.info("Waiting for the Cassandra cluster to finish starting up.");
Thread.sleep(50000);

Expand All @@ -64,5 +71,34 @@ public static void tearDownCluster()
}
composeContainer.stop();
}

protected void decommissionNode ( String node) throws IOException, InterruptedException
{
String stdout = composeContainer.getContainerByServiceName(node).get()
.execInContainer("nodetool", "-u", "cassandra", "-pw", "cassandra", "decommission").getStdout();
}

protected void startContainer ( String node)
{
DockerClient dockerClient = DockerClientFactory.instance().client();
String container = composeContainer
.getContainerByServiceName(node).get().getContainerId();

try (StartContainerCmd startCmd3 = dockerClient.startContainerCmd(container))
{
startCmd3.exec();
}
}

protected void stopContainer ( String node)
{
DockerClient dockerClient = DockerClientFactory.instance().client();
String container = composeContainer
.getContainerByServiceName(node).get().getContainerId();
try (StopContainerCmd stopCmd = dockerClient.stopContainerCmd(container))
{
stopCmd.exec();
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright 2024 Telefonaktiebolaget LM Ericsson
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cassandracluster;

import com.datastax.oss.driver.api.core.CqlSession;
import com.datastax.oss.driver.api.core.CqlSessionBuilder;
import com.ericsson.bss.cassandra.ecchronos.core.impl.repair.DefaultRepairConfigurationProvider;
import com.github.dockerjava.api.DockerClient;
import org.junit.BeforeClass;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testcontainers.DockerClientFactory;
import org.testcontainers.containers.DockerComposeContainer;

import java.net.InetSocketAddress;
import java.nio.file.Path;
import java.nio.file.Paths;

import static org.mockito.Mockito.*;

public class TestNodeAddition extends AbstractCassandraCluster
{
private static final Logger LOG = LoggerFactory.getLogger(TestNodeAddition.class);
@BeforeClass
public static void setup() throws InterruptedException {

Path dockerComposePath = Paths.get("")
.toAbsolutePath()
.getParent()
.resolve("cassandra-test-image/src/main/docker/docker-compose.yml");
composeContainer = new DockerComposeContainer<>(dockerComposePath.toFile());
composeContainer.withScaledService("cassandra-node-dc1-rack1-node2", 0 );
composeContainer.withScaledService("cassandra-node-dc2-rack1-node2", 0 );
composeContainer.withScaledService("cassandra-seed-dc1-rack1-node1", 1 );
composeContainer.withScaledService("cassandra-seed-dc2-rack1-node1", 0 );
composeContainer.start();
LOG.info("Waiting for the Cassandra cluster to finish starting up.");
Thread.sleep(50000);
}
@Test
public void testAdditionalNodesAddedToCluster() throws InterruptedException
{
DefaultRepairConfigurationProvider listener = mock(DefaultRepairConfigurationProvider.class);
containerIP = composeContainer.getContainerByServiceName("cassandra-seed-dc1-rack1-node1").get()
.getContainerInfo()
.getNetworkSettings().getNetworks().values().stream().findFirst().get().getIpAddress();
CqlSessionBuilder builder = CqlSession.builder()
.addContactPoint(new InetSocketAddress(containerIP, 9042))
.withLocalDatacenter("datacenter1")
.withAuthCredentials("cassandra", "cassandra")
.withNodeStateListener(listener);
mySession = builder.build();

// scale up new nodes
composeContainer.withScaledService("cassandra-node-dc1-rack1-node2", 1 );
composeContainer.withScaledService("cassandra-node-dc2-rack1-node2", 1 );
composeContainer.withScaledService("cassandra-seed-dc2-rack1-node1", 1 );
composeContainer.start();
LOG.info("Waiting for the new nodes to finish starting up.");
Thread.sleep(50000);

verify(listener, times(3)).onAdd(any());
verify(listener, times(0)).onRemove(any());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright 2024 Telefonaktiebolaget LM Ericsson
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cassandracluster;

import com.datastax.oss.driver.api.core.CqlSession;
import com.datastax.oss.driver.api.core.CqlSessionBuilder;
import com.ericsson.bss.cassandra.ecchronos.core.impl.repair.DefaultRepairConfigurationProvider;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.InetSocketAddress;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;

public class TestNodeRemoval extends AbstractCassandraCluster
{
private static final Logger LOG = LoggerFactory.getLogger(TestNodeRemoval.class);
@Test
public void testNodeDecommissionedFromCluster() throws InterruptedException
{
DefaultRepairConfigurationProvider listener = mock(DefaultRepairConfigurationProvider.class);
containerIP = composeContainer.getContainerByServiceName("cassandra-seed-dc1-rack1-node1").get()
.getContainerInfo()
.getNetworkSettings().getNetworks().values().stream().findFirst().get().getIpAddress();

CqlSessionBuilder builder = CqlSession.builder()
.addContactPoint(new InetSocketAddress(containerIP, 9042))
.withLocalDatacenter("datacenter1")
.withAuthCredentials("cassandra", "cassandra")
.withNodeStateListener(listener);
mySession = builder.build();

try
{
decommissionNode("cassandra-seed-dc1-rack1-node1");
}
catch (IOException e) {
throw new RuntimeException(e);
}
LOG.info("Waiting for node to be decommissioned.");
Thread.sleep(50000);

verify(listener, times(1)).onRemove(any());
}
}

0 comments on commit eca15d4

Please sign in to comment.