Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementing CCM Class #99

Open
wants to merge 1 commit into
base: v1.3.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file modified docs/generate_doc.sh
100755 → 100644
Empty file.
Empty file modified docs/generate_doc_yaml.sh
100755 → 100644
Empty file.
Empty file modified scripts/compile-php.sh
100755 → 100644
Empty file.
Empty file modified scripts/gen_stub.php
100755 → 100644
Empty file.
Empty file modified scripts/run-docker-tests.sh
100755 → 100644
Empty file.
Empty file modified scripts/run-scylladb-ssl.sh
100755 → 100644
Empty file.
Empty file modified scripts/run-tests.sh
100755 → 100644
Empty file.
348 changes: 348 additions & 0 deletions tests/Support/CCM.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,348 @@
<?php

declare(strict_types=1);

namespace Cassandra\Tests\Support;

class CCM
{
public const DEFAULT_CLUSTER_PREFIX = "php-driver";
public const PROCESS_TIMEOUT_IN_SECONDS = 480;

public function __construct(
private readonly bool $isSilent = false,
private readonly string $clusterPrefix = self::DEFAULT_CLUSTER_PREFIX,
)
{
// $this->version = $version;
// $this->isSilent = $isSilent;
// $this->clusterPrefix = $clusterPrefix;
// $this->process = new Process(null);
// $this->cluster = null;
// $this->session = null;
// $this->ssl = false;
// $this->clientAuth = false;
// $this->dataCenterOneNodes = 0;
// $this->dataCenterTwoNodes = 0;
// $this->process->setTimeout(self::PROCESS_TIMEOUT_IN_SECONDS);
}
public function setupSchema($schema, $dropExistingKeyspaces = true)
{
if ($dropExistingKeyspaces) {
$system_keyspaces = "system_schema.keyspaces";

$keyspaces = $this->session->execute(new SimpleStatement("SELECT keyspace_name FROM $system_keyspaces"));

foreach ($keyspaces as $row) {
$keyspace = $row['keyspace_name'];

if ($this->startsWith("system", $keyspace)) {
continue;
}

if (!$this->isSilent) {
echo "DROP KEYSPACE " . $keyspace . "\n";
}
$this->session->execute(new SimpleStatement("DROP KEYSPACE $keyspace"));
}
}

foreach (explode(";\n", $schema) as $cql) {
$cql = trim($cql);

if (empty($cql)) {
continue;
}

if (!$this->isSilent) {
echo $cql . "\n";
}
$this->session->execute(new SimpleStatement($cql));
}
}

public function start()
{
$this->run('start', '--wait-other-notice', '--wait-for-binary-proto');
$builder = Cassandra::cluster()
->withPersistentSessions(false)
->withContactPoints('127.0.0.1');

if ($this->ssl || $this->clientAuth) {
$sslOptions = Cassandra::ssl()
->withTrustedCerts(realpath(__DIR__ . '/ssl/cassandra.pem'))
->withVerifyFlags(Cassandra::VERIFY_PEER_CERT)
->withClientCert(realpath(__DIR__ . '/ssl/driver.pem'))
->withPrivateKey(realpath(__DIR__ . '/ssl/driver.key'), 'php-driver')
->build();
$builder->withSSL($sslOptions);
}

for ($retries = 1; $retries <= 10; $retries++) {
try {
$this->cluster = $builder->build();
$this->session = $this->cluster->connect();
break;
} catch (Cassandra\Exception\RuntimeException $e) {
unset($this->session);
unset($this->cluster);
sleep($retries * 0.4);
}
}

if (!isset($this->session)) {
throw new RuntimeException("Unable to initialize a Session, check cassandra logs");
}
}

public function stop()
{
unset($this->session);
unset($this->cluster);
$this->run('stop');
}

private function getClusters()
{
$active = '';
$clusters = array();
foreach (explode(PHP_EOL, $this->run('list')) as $cluster) {
$clusterName = trim(substr($cluster, 2, strlen($cluster) - 2));

// Determine if this cluster is the active cluster
if ($this->isActive($cluster)) {
$active = $clusterName;
}

// Add the cluster to the list
if (!empty($clusterName)) {
$clusters[] = $clusterName;
}
}

return array('active' => $active, 'list' => $clusters);
}

private function internalSetup($dataCenterOneNodes, $dataCenterTwoNodes)
{
$this->dataCenterOneNodes = $dataCenterOneNodes;
$this->dataCenterTwoNodes = $dataCenterTwoNodes;

$clusters = $this->getClusters();
$clusterName = $this->clusterPrefix . '_'
. str_replace(".", "-", $this->version) . '_'
. $dataCenterOneNodes . '-' . $dataCenterTwoNodes;

if ($this->ssl) {
$clusterName .= "_ssl";
}

if ($this->clientAuth) {
$clusterName .= "_client_auth";
}

if ($clusters['active'] != $clusterName) {
// Ensure any active cluster is stopped
if (!empty($clusters['active'])) {
$this->stop();
}

// Determine if a cluster should be created or re-used
if (in_array($clusterName, $clusters['list'])) {
$this->run('switch', $clusterName);
} else {
$this->run('create', '-v', 'binary:' . $this->version, '-b', $clusterName);

$params = array(
'updateconf', '--rt', '1000', 'read_request_timeout_in_ms: 1000',
'write_request_timeout_in_ms: 1000', 'request_timeout_in_ms: 1000',
'phi_convict_threshold: 16', 'hinted_handoff_enabled: false',
'dynamic_snitch_update_interval_in_ms: 1000',
);

if (substr($this->version, 0, 4) == '1.2.') {
$params[] = 'reduce_cache_sizes_at: 0';
$params[] = 'reduce_cache_capacity_to: 0';
$params[] = 'flush_largest_memtables_at: 0';
$params[] = 'index_interval: 512';
} else {
$params[] = 'cas_contention_timeout_in_ms: 10000';
$params[] = 'file_cache_size_in_mb: 0';
}

$params[] = 'native_transport_max_threads: 1';
$params[] = 'rpc_min_threads: 1';
$params[] = 'rpc_max_threads: 1';
$params[] = 'concurrent_reads: 2';
$params[] = 'concurrent_writes: 2';
$params[] = 'concurrent_compactors: 1';
$params[] = 'compaction_throughput_mb_per_sec: 0';

if (strcmp($this->version, '2.1') < 0) {
$params[] = 'in_memory_compaction_limit_in_mb: 1';
}

if (version_compare($this->version, "2.2.0", ">=")) {
$this->run('updateconf', 'enable_user_defined_functions: true');
}

if (version_compare($this->version, "3.0.0", ">=")) {
$this->run('updateconf', 'enable_scripted_user_defined_functions: true');
}


$params[] = 'key_cache_size_in_mb: 0';
$params[] = 'key_cache_save_period: 0';
$params[] = 'memtable_flush_writers: 1';
$params[] = 'max_hints_delivery_threads: 1';

call_user_func_array(array($this, 'run'), $params);
$this->run('populate', '-n', $dataCenterOneNodes.':'.$dataCenterTwoNodes, '-i', '127.0.0.');
}
}
}

public function setup($dataCenterOneNodes, $dataCenterTwoNodes) {
$this->ssl = false;
$this->clientAuth = false;
$this->internalSetup($dataCenterOneNodes, $dataCenterTwoNodes);
}

public function setupSSL()
{
if (!$this->ssl) {
$this->ssl = true;
$this->internalSetup(1, 0);
$this->stop();
$this->run('updateconf',
'client_encryption_options.enabled: true',
'client_encryption_options.keystore: ' . realpath(__DIR__ . '/ssl/.keystore'),
'client_encryption_options.keystore_password: php-driver'
);
}
}

public function setupClientVerification()
{
if (!$this->clientAuth) {
$this->clientAuth = true;
$this->internalSetup(1, 0);
$this->stop();
$this->run('updateconf',
'client_encryption_options.enabled: true',
'client_encryption_options.keystore: ' . realpath(__DIR__ . '/ssl/.keystore'),
'client_encryption_options.keystore_password: php-driver',
'client_encryption_options.require_client_auth: true',
'client_encryption_options.truststore: ' . realpath(__DIR__ . '/ssl/.truststore'),
'client_encryption_options.truststore_password: php-driver'
);
}
}

public function setupUserDefinedFunctions()
{
$this->ssl = false;
$this->clientAuth = false;
$this->internalSetup(1, 0);
if (version_compare($this->version, "2.2.0", ">=")) {
$this->run('updateconf', 'enable_user_defined_functions: true');
}
if (version_compare($this->version, "3.0.0", ">=")) {
$this->run('updateconf', 'enable_scripted_user_defined_functions: true');
}
}

public function enableTracing($isEnabled)
{
$nodes = $this->dataCenterOneNodes + $this->dataCenterTwoNodes;
for ($node = 1; $node <= $nodes; ++$node) {
$this->run('node'.$node, 'nodetool', 'settraceprobability', ((bool) $isEnabled) ? 1 : 0);
}
}

public function pauseNode($nodes)
{
foreach (array($nodes) as $node) {
$this->run('node'.$node, 'pause');
}
}

public function resumeNode($nodes)
{
foreach (array($nodes) as $node) {
$this->run('node'.$node, 'resume');
}
}

public function startNode($nodes)
{
foreach (array($nodes) as $node) {
$this->run('node'.$node, 'start');
}
sleep(5); //TODO: Mechanism required to ensure node is up
}

public function stopNode($nodes)
{
foreach (array($nodes) as $node) {
$this->run('node'.$node, 'stop');
}
sleep(5); //TODO: Mechanism required to ensure node is down
}

private function isActive($clusterName)
{
return $this->startsWith(' *', $clusterName);
}

private function startsWith($prefix, $string)
{
return substr($string, 0, strlen($prefix)) === $prefix;
}

private function run()
{
$args = func_get_args();
foreach ($args as $i => $arg) {
$args[$i] = escapeshellarg($arg);
}

$command = sprintf('ccm %s', implode(' ', $args));
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' || strtoupper(substr(PHP_OS, 0, 6)) === 'CYGWIN') {
$keepWindowsContext = '';
if ($args[0] != "\"start\"") {
$keepWindowsContext = '/B ';
}
$command = 'START "PHP Driver - CCM" ' . $keepWindowsContext . '/MIN /WAIT ' . $command;
}
$this->process->setCommandLine($command);

if (!$this->isSilent) {
echo 'ccm > ' . $command . "\n";
}
$this->process->mustRun(function ($type, $buffer) {
if (!$this->isSilent) {
echo 'ccm > ' . $buffer;
}
});

return $this->process->getOutput();
}

public function removeCluster($cluster)
{
return $this->run('remove', $cluster);
}

public function removeAllClusters($is_all = false)
{
$clusters = $this->getClusters();
foreach ($clusters['list'] as $cluster) {
// Determine if the cluster should be deleted
if (!$is_all && substr(strtolower($cluster), 0, strlen($this->clusterPrefix)) != $this->clusterPrefix) {
continue;
}
$this->removeCluster($cluster);
}
}
}
2 changes: 1 addition & 1 deletion tests/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
]
},
"require": {
"php": "^8.1",
"php": "^8.3",
"ext-cassandra": "^1.3.9-dev",
"symfony/process": "^6.2",
"nesbot/carbon": "^2.66"
Expand Down