Example #1
0
 public function getProgramIsRunning($programName)
 {
     // what are we doing?
     $log = usingLog()->startAction("is program '{$programName}' running under supervisor on host '{$this->args[0]}'?");
     // get the host details
     $hostDetails = $this->getHostDetails();
     //run the supervisorctl command
     $result = usingHost($hostDetails->hostId)->runCommandAndIgnoreErrors("sudo supervisorctl status");
     // |egrep '^$programName' | awk '{print \\$2}'");
     // did the command succeed?
     if ($result->didCommandFail()) {
         $msg = "command failed with return code '{$result->returnCode}' and output '{$result->output}'";
         $log->endAction($msg);
         throw new E5xx_ActionFailed(__METHOD__);
     }
     // reduce the output down
     $lines = explode("\n", $result->output);
     $lines = FilterForMatchingRegex::against($lines, "/^{$programName} /");
     $lines = FilterColumns::from($lines, "1", ' ');
     if (empty($lines)) {
         $log->endAction("supervisor does not know about '{$programName}'");
         return false;
     }
     // what happened?
     if ($lines[0] == 'RUNNING') {
         $log->endAction('current status is RUNNING');
         return true;
     }
     // if we get here, then the program is not RUNNING, and we
     // treat that as a failure
     $log->endAction('current status is ' . $lines[0]);
     return false;
 }
Example #2
0
 public function stopProgram($programName)
 {
     // what are we doing?
     $log = usingLog()->startAction("stop program '{$programName}' on host '{$this->args[0]}'");
     // get the host details
     $hostDetails = $this->getHostDetails();
     // stop the program
     $result = usingHost($hostDetails->hostId)->runCommand("sudo supervisorctl stop '{$programName}'");
     // did the command succeed?
     if ($result->didCommandFail()) {
         throw new E5xx_ActionFailed(__METHOD__, "failed to start process '{$programName} (via supervisord)'");
     }
     // all done
     $log->endAction();
     return true;
 }
});
// ========================================================================
//
// POSSIBLE ACTION(S)
//
// ------------------------------------------------------------------------
$story->addAction(function () {
    // make sure all the sessions are running first
    $checkpoint = getCheckpoint();
    foreach (hostWithRole('host_target') as $hostId) {
        foreach ($checkpoint->sessions as $session) {
            expectsHost($hostId)->screenIsRunning($session);
        }
    }
    foreach (hostWithRole('host_target') as $hostId) {
        usingHost($hostId)->stopAllScreens();
    }
});
// ========================================================================
//
// POST-TEST INSPECTION
//
// ------------------------------------------------------------------------
$story->addPostTestInspection(function () {
    $checkpoint = getCheckpoint();
    foreach (hostWithRole('host_target') as $hostId) {
        foreach ($checkpoint->sessions as $session) {
            expectsHost($hostId)->screenIsNotRunning($session);
        }
    }
});
// ========================================================================
//
// STORY SETUP / TEAR-DOWN
//
// ------------------------------------------------------------------------
$story->addTestSetup(function () {
    // cleanup after ourselves
    foreach (hostWithRole('upload_target') as $hostname) {
        usingHost($hostname)->uploadFile(__DIR__ . '/testfile.txt', "testfile.txt");
    }
});
$story->addTestTeardown(function () {
    // cleanup after ourselves
    foreach (hostWithRole('upload_target') as $hostname) {
        // remove the file from the test environment
        usingHost($hostname)->runCommand("if [[ -e testfile.txt ]] ; then rm -f testfile.txt ; fi");
        // 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) {
    $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");
    }
});
// ========================================================================
//
// POST-TEST INSPECTION
//
// ------------------------------------------------------------------------
$story->addPostTestInspection(function () {
    $checkpoint = getCheckpoint();
    foreach (hostWithRole('host_target') as $hostId) {
        expectsHost($hostId)->screenIsRunning($checkpoint->session);
    }
});
Example #6
0
 /**
  *
  * @param  stdClass $groupDef
  * @param  array $provisioningVars
  * @return void
  */
 public function createHost($groupDef, $provisioningVars = array())
 {
     // what are we doing?
     $log = usingLog()->startAction('create new VM');
     // make sure we're happy with this group
     $this->checkGroupDefinition($groupDef);
     // where is the action?
     $baseFolder = $this->getVagrantDir($groupDef);
     // make sure we're happy with details about the machine
     foreach ($groupDef->details->machines as $hostId => $machine) {
         // TODO: it would be great to autodetect this one day
         if (!isset($machine->osName)) {
             throw new E5xx_ActionFailed(__METHOD__, "missing groupDef->details->machines['{$hostId}']->osName");
         }
         if (!isset($machine->roles)) {
             throw new E5xx_ActionFailed(__METHOD__, "missing groupDef->details->machines['{$hostId}']->roles");
         }
     }
     // make sure the VM is stopped, if it is running
     $log->addStep('stop vagrant VM in ' . $baseFolder . ' if already running', function () use($baseFolder) {
         $command = "vagrant destroy --force";
         $this->runCommandAgainstHostManager($baseFolder, $command);
     });
     // remove any existing hosts table entry
     foreach ($groupDef->details->machines as $hostId => $machine) {
         usingHostsTable()->removeHost($hostId);
         // remove any roles
         usingRolesTable()->removeHostFromAllRoles($hostId);
     }
     // work out which network interface to use
     $this->setVagrantBridgedInterface();
     // let's start the VM
     $command = "vagrant up";
     $result = $log->addStep('create vagrant VM(s) in ' . $baseFolder, function () use($baseFolder, $command) {
         return $this->runCommandAgainstHostManager($baseFolder, $command);
     });
     // did it work?
     if ($result->returnCode != 0) {
         $log->endAction("VM failed to start or provision :(");
         throw new E5xx_ActionFailed(__METHOD__);
     }
     // yes it did!!
     // store the details
     foreach ($groupDef->details->machines as $hostId => $machine) {
         // we want all the details from the config file
         $vmDetails = clone $machine;
         // this allows the story to perform actions against a single
         // machine if required
         $vmDetails->type = 'VagrantVm';
         // new in v2.x:
         //
         // when provisioning a folder of vagrant vms, we now use
         // the same name for the VM that vagrant uses
         $vmDetails->hostId = $hostId;
         // remember where the machine lives
         $vmDetails->dir = $baseFolder;
         // we need to remember how to SSH into the box
         $vmDetails->sshUsername = '******';
         $vmDetails->sshKeyFile = $this->determinePrivateKey($vmDetails);
         $vmDetails->sshOptions = ["-i '" . $vmDetails->sshKeyFile . "'", "-o StrictHostKeyChecking=no", "-o UserKnownHostsFile=/dev/null", "-o LogLevel=quiet"];
         $vmDetails->scpOptions = ["-i '" . $vmDetails->sshKeyFile . "'", "-o StrictHostKeyChecking=no"];
         // remember how to connect to the machine via the network
         $vmDetails->ipAddress = $this->determineIpAddress($vmDetails);
         $vmDetails->hostname = $this->determineHostname($vmDetails);
         // mark the box as provisioned
         // we will use this in stopBox() to avoid destroying VMs that failed
         // to provision
         $vmDetails->provisioned = true;
         // remember this vm, now that it is running
         usingHostsTable()->addHost($vmDetails->hostId, $vmDetails);
         foreach ($vmDetails->roles as $role) {
             usingRolesTable()->addHostToRole($vmDetails, $role);
         }
         // now, let's get this VM into our SSH known_hosts file, to avoid
         // prompting people when we try and provision this VM
         $log->addStep("get the VM into the SSH known_hosts file", function () use($vmDetails) {
             usingHost($vmDetails->hostId)->runCommand("ls");
         });
     }
     // all done
     $log->endAction();
 }
    $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)->stopInScreen($checkpoint->session);
    }
});
// ========================================================================
//
// POST-TEST INSPECTION
//
// ------------------------------------------------------------------------
$story->addPostTestInspection(function () {
    $checkpoint = getCheckpoint();
    foreach (hostWithRole('host_target') as $hostId) {
        expectsHost($hostId)->screenIsNotRunning($checkpoint->session);
    }
});
Example #8
0
 /**
  * @return array<object>
  */
 public function getAllScreenSessions()
 {
     // what are we doing?
     $log = usingLog()->startAction("get details about all screen sessions on host '{$this->args[0]}'");
     // are there any details?
     $cmd = "screen -ls";
     $result = usingHost($this->args[0])->runCommandAndIgnoreErrors($cmd);
     // NOTE:
     //
     // screen is not a well-behaved UNIX program, and its exit code
     // can be non-zero when everything is good
     if (empty($result->output)) {
         $msg = "unable to get list of screen sessions";
         $log->endAction($msg);
         return [];
     }
     // reduce the output down to a list of sessions
     $lines = explode("\n", $result->output);
     $lines = FilterForMatchingRegex::against($lines, "/[0-9]+.+\t/");
     if (empty($lines)) {
         $msg = "no screen processes running";
         $log->endAction($msg);
         return [];
     }
     $retval = [];
     foreach ($lines as $line) {
         $parts = explode('.', $line);
         $processDetails = new BaseObject();
         $processDetails->hostId = $this->args[0];
         $processDetails->type = 'screen';
         $processDetails->pid = trim($parts[0]);
         $processDetails->name = rtrim($parts[1]);
         $retval[] = $processDetails;
     }
     // all done
     $log->endAction("found " . count($retval) . " screen process(es)");
     // all done
     return $retval;
 }
//
// POSSIBLE ACTION(S)
//
// ------------------------------------------------------------------------
$story->addAction(function () {
    foreach (hostWithRole('upload_target') as $hostname) {
        usingHost($hostname)->uploadFile(__DIR__ . '/testfile.txt', "testfile.txt");
    }
});
// ========================================================================
//
// POST-TEST INSPECTION
//
// ------------------------------------------------------------------------
$story->addPostTestInspection(function () {
    foreach (hostWithRole('upload_target') as $hostname) {
        $result = usingHost($hostname)->runCommand('ls testfile.txt');
        $fileFound = false;
        $lines = explode("\n", $result->output);
        foreach ($lines as $line) {
            if ($line == "testfile.txt") {
                $fileFound = true;
            }
        }
        if (!$fileFound) {
            $msg = "file not found on host '{$hostname}'";
            usingLog()->writeToLog($msg);
            usingErrors()->throwException($msg);
        }
    }
});
Example #10
0
 /**
  *
  * @param  VagrantVmDetails $vmDetails
  * @param  array $provisioningVars
  * @return void
  */
 public function createHost($vmDetails, $provisioningVars = array())
 {
     // what are we doing?
     $log = usingLog()->startAction('provision new VM');
     // make sure we like the provided details
     foreach (array('name', 'osName', 'homeFolder') as $param) {
         if (!isset($vmDetails->{$param})) {
             throw new E5xx_ActionFailed(__METHOD__, "missing vmDetails['{$param}']");
         }
     }
     // make sure the folder exists
     $config = $this->st->getConfig();
     if (!isset($config->storyplayer->modules->vagrant)) {
         throw new E5xx_ActionFailed(__METHOD__, "'vagrant' section missing in your storyplayer.json config file");
     }
     if (!isset($config->storyplayer->modules->vagrant->dir)) {
         throw new E5xx_ActionFailed(__METHOD__, "'dir' setting missing from 'vagrant' section of your storyplayer.json config file");
     }
     $pathToHomeFolder = $config->storyplayer->modules->vagrant->dir . '/' . $vmDetails->homeFolder;
     if (!is_dir($pathToHomeFolder)) {
         throw new E5xx_ActionFailed(__METHOD__, "VM dir '{$pathToHomeFolder}' does not exist");
     }
     // remember where the Vagrantfile is
     $vmDetails->dir = $pathToHomeFolder;
     // make sure the VM is stopped, if it is running
     $log->addStep("stop vagrant VM in '{$pathToHomeFolder}' if already running", function () use($vmDetails) {
         $command = "vagrant destroy --force";
         $this->runCommandAgainstHostManager($vmDetails, $command);
     });
     // remove any existing hosts table entry
     usingHostsTable()->removeHost($vmDetails->hostId);
     // remove any roles
     usingRolesTable()->removeHostFromAllRoles($vmDetails->hostId);
     // let's start the VM
     $command = "vagrant up";
     $result = $log->addStep("create vagrant VM in '{$pathToHomeFolder}'", function () use($command, $vmDetails) {
         return $this->runCommandAgainstHostManager($vmDetails, $command);
     });
     // did it work?
     if ($result->returnCode !== 0) {
         $log->endAction("VM failed to start or provision :(");
         throw new E5xx_ActionFailed(__METHOD__);
     }
     // yes it did!!
     // now, we need to know how to contact this VM
     $vmDetails->ipAddress = $this->determineIpAddress($vmDetails);
     $vmDetails->hostname = $this->determineHostname($vmDetails);
     // mark the box as provisioned
     // we will use this in stopBox() to avoid destroying VMs that failed
     // to provision
     $vmDetails->provisioned = true;
     // remember this vm, now that it is running
     usingHostsTable()->addHost($vmDetails->hostId, $vmDetails);
     // now, let's get this VM into our SSH known_hosts file, to avoid
     // prompting people when we try and provision this VM
     $log->addStep("get the VM into the SSH known_hosts file", function () use($vmDetails) {
         usingHost($vmDetails->hostId)->runCommand("ls");
     });
     // all done
     $log->endAction("VM successfully started; IP address is {$vmDetails->ipAddress}");
 }
 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();
 }
Example #12
0
 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");
 }