// remove the file from our computer too $filename = '/tmp/testfile-' . $hostname . '.txt'; if (file_exists($filename)) { // tidy up unlink($filename); } } }); // ======================================================================== // // POSSIBLE ACTION(S) // // ------------------------------------------------------------------------ $story->addAction(function () { foreach (hostWithRole('upload_target') as $hostname) { fromHost($hostname)->downloadFile('testfile.txt', "/tmp/testfile-{$hostname}.txt"); } }); // ======================================================================== // // POST-TEST INSPECTION // // ------------------------------------------------------------------------ $story->addPostTestInspection(function () { // we should have a file for each host in the configuration foreach (hostWithRole('upload_target') as $hostname) { $filename = '/tmp/testfile-' . $hostname . '.txt'; if (!file_exists($filename)) { usingLog()->writeToLog("file not downloaded from host '{$hostname}'"); usingErrors()->throwException("file '{$filename}' not downloaded"); }
// use the checkpoint to share the name of our screen session $checkpoint = getCheckpoint(); $checkpoint->session = "storyplayer_test_session"; // make sure the session isn't running on the host foreach (hostWithRole('host_target') as $hostId) { $details = fromHost($hostId)->getScreenSessionDetails($checkpoint->session); if ($details) { usingHost($hostId)->stopProcess($details->pid); } } }); $story->addTestTeardown(function () { $checkpoint = getCheckpoint(); // if we've left the session running, go and kill it off foreach (hostWithRole('host_target') as $hostId) { $details = fromHost($hostId)->getScreenSessionDetails($checkpoint->session); if ($details) { usingHost($hostId)->stopProcess($details->pid); } } }); // ======================================================================== // // POSSIBLE ACTION(S) // // ------------------------------------------------------------------------ $story->addAction(function () { $checkpoint = getCheckpoint(); foreach (hostWithRole('host_target') as $hostId) { usingHost($hostId)->startInScreen($checkpoint->session, "top"); }
<?php // ======================================================================== // // STORY DETAILS // // ------------------------------------------------------------------------ $story = newStoryFor('Storyplayer')->inGroup('Modules')->called('HTTP: Can connect to self-signed SSL server'); $story->requiresStoryplayerVersion(2); // ======================================================================== // // POSSIBLE ACTION(S) // // ------------------------------------------------------------------------ $story->addAction(function () { foreach (hostWithRole('ssl_target') as $hostname) { $url = "https://" . fromHost($hostname)->getHostname(); fromHttp()->get($url); } }); // ======================================================================== // // POST-TEST INSPECTION // // ------------------------------------------------------------------------ $story->addPostTestInspection(function ($st) { // do nothing });
public function isConnectedToHost($hostId, $portNumber) { // what are we doing? $log = usingLog()->startAction("make sure ZMQ socket is connected to host '{$hostId}':{$portNumber}"); // build the address that we should be connected to $ipAddress = fromHost($hostId)->getIpAddress(); $zmqAddress = "tcp://{$ipAddress}:{$portNumber}"; // where are we connected to? $connections = fromZmqSocket($this->args[0])->getEndpoints(); // make sure we're connected assertsArray($connections['connect'])->containsValue($zmqAddress); // all done $log->endAction(); }
public function startVm($vmName) { // what are we doing? $log = usingLog()->startAction("start VM '{$vmName}'"); // get the VM details $vmDetails = fromHost($vmName)->getDetails(); // create our host adapter $host = HostLib::getHostAdapter($this->st, $vmDetails->type); // restart our virtual machine $host->startHost($vmDetails); // all done $log->endAction(); }
// // ------------------------------------------------------------------------ $story->addAction(function () { foreach (hostWithRole('upload_target') as $hostId) { // get the default user details for this test environment $hostUser = fromHost($hostId)->getStorySetting("user.username"); $hostGroup = fromHost($hostId)->getStorySetting("user.group"); // get the details for this file $details = fromHost($hostId)->getFileDetails('testfile.txt'); // make sure we have the details that we expect assertsObject($details)->isNotNull(); assertsObject($details)->hasAttribute("user"); assertsString($details->user)->equals($hostUser); assertsObject($details)->hasAttribute("group"); assertsString($details->group)->equals($hostGroup); } }); // ======================================================================== // // POST-TEST INSPECTION // // ------------------------------------------------------------------------ $story->addPostTestInspection(function () { foreach (hostWithRole('upload_target') as $hostId) { // get the default user details for this test environment $hostUser = fromHost($hostId)->getStorySetting("user.username"); $hostGroup = fromHost($hostId)->getStorySetting("user.group"); // check the details for this file expectsHost($hostId)->hasFileWithPermissions('testfile.txt', $hostUser, $hostGroup, 0644); } });
// // PRE-TEST INSPECTION // // ------------------------------------------------------------------------ // ======================================================================== // // POSSIBLE ACTION(S) // // ------------------------------------------------------------------------ $story->addAction(function () { // we're going to store the received message in here $checkpoint = getCheckpoint(); foreach (firstHostWithRole("zmq_target") as $hostId) { $context = usingZmqContext()->getZmqContext(); $inPort = fromHost($hostId)->getStorySetting("zmq.multi.inPort"); $outPort = fromHost($hostId)->getStorySetting("zmq.multi.outPort"); $inSocket = usingZmqContext($context)->connectToHost($hostId, $inPort, 'PUSH'); $outSocket = usingZmqContext($context)->connectToHost($hostId, $outPort, 'PULL'); expectsZmqSocket($inSocket)->canSendmultiNonBlocking($checkpoint->expectedMessage); $checkpoint->actualMessage = fromZmqSocket($outSocket)->recvMulti(); } }); // ======================================================================== // // POST-TEST INSPECTION // // ------------------------------------------------------------------------ $story->addPostTestInspection(function () { $checkpoint = getCheckpoint(); assertsObject($checkpoint)->hasAttribute("expectedMessage"); assertsObject($checkpoint)->hasAttribute("actualMessage");
/** * * @param stdClass $groupDef * @return void */ public function destroyHost($groupDef) { // what are we doing? $log = usingLog()->startAction("destroy VM(s)"); // stop all the VMs, one by one foreach ($groupDef->details->machines as $hostId => $machine) { // get the machine details $vmDetails = fromHostsTable()->getDetailsForHost($hostId); if ($vmDetails) { // is the VM actually running? if (fromHost($hostId)->getHostIsRunning()) { // delete the VM from disk // // this will also deregister the host from the // HostsTable and RolesTable usingVagrant()->destroyVm($hostId); } } } // all done $log->endAction(); }
// // TEST SETUP / TEARDOWN // // ------------------------------------------------------------------------ $story->addTestSetup(function () { // this is what we expect to retrieve from the test environment config $checkpoint = getCheckpoint(); $checkpoint->expected = "successfully retrieved this appSetting :)"; }); // ======================================================================== // // POSSIBLE ACTION(S) // // ------------------------------------------------------------------------ $story->addAction(function () { // do nothing }); // ======================================================================== // // POST-TEST INSPECTION // // ------------------------------------------------------------------------ $story->addPostTestInspection(function ($st) { // what value do we expect to retrieve? $expected = fromCheckpoint()->get('expected'); // make sure we have it foreach (hostWithRole('host_target') as $hostId) { $actual = fromHost($hostId)->getAppSetting('host.expected'); assertsString($actual)->equals($expected); } });
public function connectToHost($hostId, $port, $socketType, $sendHwm = 100, $recvHwm = 100) { // what are we doing? $log = usingLog()->startAction("connect() to ZMQ '{$socketType}' socket on host '{$hostId}':{$port}"); // do we have a supported socket? if (!isset($this->socketMap[$socketType])) { $msg = "unknown ZMQ socket type '{$socketType}'"; $log->endAction($msg); throw new E5xx_ActionFailed(__METHOD__, $msg); } // where are we connecting to? $ipAddress = fromHost($hostId)->getIpAddress(); // create the socket $socket = new ZMQSocket($this->args[0], $this->socketMap[$socketType]); if (!$socket) { $msg = "unable to create ZMQ socket"; $log->endAction($msg); throw new E5xx_ActionFailed(__METHOD__, $msg); } // set high-water marks now $socket->setSockOpt(ZMQ::SOCKOPT_SNDHWM, $sendHwm); $socket->setSockOpt(ZMQ::SOCKOPT_RCVHWM, $recvHwm); // make the connection // // NOTE: we use the 'force' parameter here to avoid Storyplayer // hanging if the remote end is not available $socket->connect("tcp://{$ipAddress}:{$port}", true); // all done $log->endAction(); return $socket; }
public function provisionHosts(ProvisioningDefinition $hosts, $provConf) { // what are we doing? $log = usingLog()->startAction("use Ansible to provision host(s)"); // get our ansible configuration $ansibleSettings = fromConfig()->getModuleSetting('ansible'); // our reverse list of roles => hosts $rolesToHosts = array(); // build up the list of roles foreach ($hosts as $hostId => $hostProps) { // what is the host's IP address? $ipAddress = fromHost($hostId)->getIpAddress(); // add the host to the required roles if (isset($hostProps->roles)) { foreach ($hostProps->roles as $role) { if (!isset($rolesToHosts[$role])) { $rolesToHosts[$role] = array(); } $rolesToHosts[$role][] = $ipAddress; } } } // at this point, we know which roles need applying to which hosts // // build up the inventory file $inventory = ""; foreach ($rolesToHosts as $role => $hostsForRole) { // add the role marker $inventory .= "[{$role}]" . PHP_EOL; // add the list of hosts foreach ($hostsForRole as $host) { $inventory .= $host . PHP_EOL; } // add an extra blank line for readability $inventory .= PHP_EOL; } // write out the inventory $inventoryFile = $this->writeInventoryFile($inventory); // where should we create the host_vars? $inventoryFolder = dirname($inventoryFile); // we set these in the foreach() loop $sshUsername = null; $sshKeyFile = null; // now we need to write out the host files foreach ($hosts as $hostId => $hostProps) { // what is the host's IP address? $ipAddress = fromHost($hostId)->getIpAddress(); $sshUsername = fromHost($hostId)->getSshUsername(); $sshKeyFile = fromHost($hostId)->getSshKeyFile(); // do we have any vars to write? if (!isset($hostProps->params) || $hostProps->params === null || is_array($hostProps) && count($hostProps) == 0) { // we'd better remove any host_vars file that exists, // in case what's there (if anything) is left over from // a different test run $this->removeHostVarsFile($inventoryFolder, $ipAddress); } else { // write the host vars file $this->writeHostVarsFile($inventoryFolder, $ipAddress, $hostProps->params); } } // build the command for Ansible $command = 'ansible-playbook -i "' . $inventoryFile . '"' . ' "--private-key=' . $sshKeyFile . '"' . ' "--user='******'"'; $command .= ' "' . $ansibleSettings->dir . DIRECTORY_SEPARATOR . $ansibleSettings->playbook . '"'; // let's run the command // // this looks like a hack, but it is the only way to work with Ansible // if there's an ansible.cfg in the root of the playbook :( $cwd = getcwd(); chdir($ansibleSettings->dir); $commandRunner = new CommandRunner(); $result = $commandRunner->runSilently($command); chdir($cwd); // what happened? if (!$result->didCommandSucceed()) { throw new E5xx_ActionFailed(__METHOD__, "provisioning failed"); } // all done $log->endAction(); }
/** * @param string $sessionName * @return bool */ public function getScreenIsRunning($sessionName) { // what are we doing? $log = usingLog()->startAction("check if screen session '{$sessionName}' is still running"); // get the details $sessionData = fromHost($this->args[0])->getScreenSessionDetails($sessionName); // all done if ($sessionData) { $log->endAction("still running"); return true; } else { $log->endAction("not running"); return false; } }
public function hasFolderWithPermissions($folder, $owner, $group, $mode) { // shorthand $octMode = decoct($mode); // what are we doing? $log = usingLog()->startAction("make sure folder '{$folder}' exists on host '{$this->args[0]}' with permissions '{$octMode}' owned by '{$owner}:{$group}'"); // make sure we have valid host details $hostDetails = $this->getHostDetails(); // get the file details $details = fromHost($hostDetails->hostId)->getFileDetails($folder); // validate the details if ($details === null) { throw new E5xx_ExpectFailed(__METHOD__, "'{$folder}' exists", "'{$folder}' does not exist"); } if ($details->type != 'dir') { throw new E5xx_ExpectFailed(__METHOD__, "'{$folder}' is a file", "'{$folder}' is type '{$details->type}'"); } if ($details->mode != $mode) { $theirOctMode = decoct($details->mode); throw new E5xx_ExpectFailed(__METHOD__, "'{$folder}' has permissions '{$octMode}'", "'{$folder}' has permissions '{$theirOctMode}'"); } if ($details->user != $owner || $details->group != $group) { throw new E5xx_ExpectFailed(__METHOD__, "'{$folder}' has ownership '{$owner}:{$group}'", "'{$folder}' has ownership '{$details->user}:{$details->group}'"); } // if we get here, then all is good $log->endAction(); }
public function provisionHosts(ProvisioningDefinition $hosts, $provConfig) { // what are we doing? $log = usingLog()->startAction("use dsbuild to provision host(s)"); // the params file that we are going to output $dsbuildParams = new BaseObject(); // build up the list of settings to write out foreach ($hosts as $hostId => $hostProps) { // what is the host's IP address? $ipAddress = fromHost($hostId)->getIpAddress(); $propName = $hostId . '_ipv4Address'; $dsbuildParams->{$propName} = $ipAddress; if (isset($hostProps->params)) { $dsbuildParams->mergeFrom($hostProps->params); } } // add in all the config settings that we know about $dsbuildParams->storyplayer_ipv4Address = fromConfig()->get('storyplayer.ipAddress'); $dsbuildParams->mergeFrom($this->flattenData($this->st->getActiveConfig()->getData(''))); // write them out $this->writeDsbuildParamsShellFile((array) $dsbuildParams); $this->writeDsbuildParamsYamlFile((array) $dsbuildParams); // at this point, we are ready to attempt provisioning // // provision each host in the order that they're listed foreach ($hosts as $hostId => $hostProps) { // which dsbuildfile are we going to run? $hostDir = fromHost($hostId)->getLocalFolder(); $dsbuildFilename = $this->getDsbuildFilename($hostDir, $provConfig, $hostId); if ($dsbuildFilename === null) { // there is no dsbuildfile at all to run $log->endAction("cannot find dsbuildfile to run :("); throw new E5xx_ActionFailed(__METHOD__, "no dsbuildfile to run"); } // at this point, we are ready to provision $commandRunner = new CommandRunner(); // copy the dsbuildparams files to the target machine using scp // NOTE: the "vagrant rsync" command seems not working with some Vagrant provisioners (e.g. OpenStack) $command = 'scp' . ' ' . $dsbuildParams->{'hosts_' . $hostId . '_scpOptions_0'} . ' ' . $dsbuildParams->{'hosts_' . $hostId . '_scpOptions_1'} . ' dsbuildparams.*' . ' ' . $dsbuildParams->{'hosts_' . $hostId . '_sshUsername'} . '@' . $dsbuildParams->{'hosts_' . $hostId . '_ipAddress'} . ':/vagrant/'; $result = $commandRunner->runSilently($command); if (!$result->didCommandSucceed()) { // try to rsync folders in case of scp fail $command = 'vagrant rsync ' . $hostId; $commandRunner->runSilently($command); } // provision $command = 'sudo bash /vagrant/' . $dsbuildFilename; $result = usingHost($hostId)->runCommand($command); // what happened? if (!$result->didCommandSucceed()) { throw new E5xx_ActionFailed(__METHOD__, "provisioning failed"); } } // all done $log->endAction(); }
// make sure the session is running on each host foreach (hostWithRole('host_target') as $hostId) { foreach ($checkpoint->sessions as $session) { $details = fromHost($hostId)->getScreenSessionDetails($session); if (!$details) { usingHost($hostId)->startInScreen($session, 'top'); } } } }); $story->addTestTeardown(function () { $checkpoint = getCheckpoint(); // if we've left the session running, go and kill it off foreach (hostWithRole('host_target') as $hostId) { foreach ($checkpoint->sessions as $session) { $details = fromHost($hostId)->getScreenSessionDetails($session); if ($details) { usingHost($hostId)->stopProcess($details->pid); } } } }); // ======================================================================== // // POSSIBLE ACTION(S) // // ------------------------------------------------------------------------ $story->addAction(function () { // make sure all the sessions are running first $checkpoint = getCheckpoint(); foreach (hostWithRole('host_target') as $hostId) {
public function disconnectFromHost($hostId, $port) { // what are we doing? $log = usingLog()->startAction("disconnect() from ZMQ tcp socket on host '{$hostId}':{$port}"); // where are we connecting to? $ipAddress = fromHost($hostId)->getIpAddress(); // we're reusing the existing socket $socket = $this->args[0]; // attempt the disconnection $socket->disconnect("tcp://{$ipAddress}:{$port}"); // all done $log->endAction(); }
// // PRE-TEST INSPECTION // // ------------------------------------------------------------------------ // ======================================================================== // // POSSIBLE ACTION(S) // // ------------------------------------------------------------------------ $story->addAction(function () { // we're going to store the received message in here $checkpoint = getCheckpoint(); foreach (firstHostWithRole("zmq_target") as $hostId) { $context = usingZmqContext()->getZmqContext(); $inPort = fromHost($hostId)->getStorySetting("zmq.single.inPort"); $outPort = fromHost($hostId)->getStorySetting("zmq.single.outPort"); $inSocket = usingZmqContext($context)->connectToHost($hostId, $inPort, 'PUSH'); $outSocket = usingZmqContext($context)->connectToHost($hostId, $outPort, 'PULL'); usingZmqSocket($inSocket)->send($checkpoint->expectedMessage); $checkpoint->actualMessage = fromZmqSocket($outSocket)->recv(); } }); // ======================================================================== // // POST-TEST INSPECTION // // ------------------------------------------------------------------------ $story->addPostTestInspection(function () { $checkpoint = getCheckpoint(); assertsObject($checkpoint)->hasAttribute("expectedMessage"); assertsObject($checkpoint)->hasAttribute("actualMessage");
public function stopProcess($pid, $grace = 5) { // what are we doing? $log = usingLog()->startAction("stop process '{$pid}' on host '{$this->args[0]}'"); // is the process running at all? if (!fromHost($this->args[0])->getPidIsRunning($pid)) { $log->endAction("process is not running"); return; } // yes it is, so stop it // send a TERM signal to the screen session $log->addStep("send SIGTERM to process '{$pid}'", function () use($pid) { if ($this->getIsLocalhost()) { posix_kill($pid, SIGTERM); } else { usingHost($this->args[0])->runCommand("kill {$pid}"); } }); // has this worked? $isStopped = $log->addStep("wait for process to terminate", function () use($pid, $grace, $log) { for ($i = 0; $i < $grace; $i++) { if (!fromHost($this->args[0])->getPidIsRunning($pid)) { return true; } // process still exists sleep(1); } return false; }); // did the process stop? if ($isStopped) { $log->endAction(); return; } $log->addStep("send SIGKILL to process '{$pid}'", function () use($pid) { if ($this->getIsLocalhost()) { posix_kill($pid, SIGKILL); } else { usingHost($this->args[0])->runCommand("kill -9 {$pid}"); } sleep(1); }); // success? if (fromHost($this->args[0])->getProcessIsRunning($pid)) { $log->endAction("process is still running :("); throw new E5xx_ActionFailed(__METHOD__); } // all done $log->endAction("process has finished"); }