436 lines
12 KiB
PHP
436 lines
12 KiB
PHP
<?php
|
|
|
|
use Behat\Behat\Tester\Exception\PendingException;
|
|
use Behat\Behat\Context\Context;
|
|
use Behat\Behat\Context\SnippetAcceptingContext;
|
|
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
|
|
use Behat\Behat\Hook\Scope\AfterScenarioScope;
|
|
use Behat\Gherkin\Node\PyStringNode;
|
|
use Behat\Gherkin\Node\TableNode;
|
|
use Supervisor\Configuration\Parser\File as Parser;
|
|
use Supervisor\Configuration\Writer\File as Writer;
|
|
use Supervisor\Configuration\Section;
|
|
use Supervisor\Connector\XmlRpc;
|
|
use Supervisor\Supervisor;
|
|
use fXmlRpc\Client;
|
|
use fXmlRpc\Transport\Guzzle4Bridge;
|
|
use GuzzleHttp\Client as GuzzleClient;
|
|
|
|
/**
|
|
* Defines application features from the specific context.
|
|
*/
|
|
class FeatureContext implements Context, SnippetAcceptingContext
|
|
{
|
|
/**
|
|
* Initializes context.
|
|
*
|
|
* Every scenario gets its own context instance.
|
|
* You can also pass arbitrary arguments to the
|
|
* context constructor through behat.yml.
|
|
*/
|
|
public function __construct($bin = 'supervisord')
|
|
{
|
|
$this->bin = $bin;
|
|
}
|
|
|
|
/**
|
|
* @BeforeScenario
|
|
*/
|
|
public function setUpSupervisor(BeforeScenarioScope $scope)
|
|
{
|
|
$parser = new Parser(__DIR__.'/../../resources/supervisord.conf');
|
|
$this->configuration = $parser->parse();
|
|
|
|
$this->setUpConnector();
|
|
}
|
|
|
|
protected function setUpConnector()
|
|
{
|
|
$client = new Client(
|
|
'http://127.0.0.1:9001/RPC2',
|
|
new Guzzle4Bridge(new GuzzleClient(['defaults' => ['auth' => ['user', '123']]]))
|
|
);
|
|
|
|
$connector = new XmlRpc($client);
|
|
$this->supervisor = new Supervisor($connector);
|
|
}
|
|
|
|
/**
|
|
* @AfterScenario
|
|
*/
|
|
public function stopSupervisor(AfterScenarioScope $scope)
|
|
{
|
|
isset($this->process) and posix_kill($this->process, SIGKILL);
|
|
}
|
|
|
|
/**
|
|
* @Given I have Supervisor running
|
|
*/
|
|
public function iHaveSupervisorRunning()
|
|
{
|
|
$writer = new Writer($file = tempnam(sys_get_temp_dir(), 'supervisord_'));
|
|
$writer->write($this->configuration);
|
|
|
|
if ($this->supervisor->isConnected()) {
|
|
posix_kill($this->supervisor->getPID(), SIGKILL);
|
|
}
|
|
|
|
$command = sprintf('(%s --configuration %s > /dev/null 2>&1 & echo $!)&', $this->bin, $file);
|
|
exec($command, $op);
|
|
$this->process = (int)$op[0];
|
|
|
|
$c = 0;
|
|
while (!$this->supervisor->isConnected() and $c < 100) {
|
|
usleep(10000);
|
|
$c++;
|
|
}
|
|
|
|
if ($c >= 100) {
|
|
throw new \RuntimeException('Could not connect to supervisord');
|
|
}
|
|
|
|
if ($this->process !== $this->supervisor->getPID()) {
|
|
throw new \RuntimeException('Connected to supervisord with a different PID');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @When I ask for the API version
|
|
*/
|
|
public function iAskForTheApiVersion()
|
|
{
|
|
$this->version = $this->supervisor->getAPIVersion();
|
|
}
|
|
|
|
/**
|
|
* @Then I should get at least :ver version
|
|
*/
|
|
public function iShouldGetAtLeastVersion($ver)
|
|
{
|
|
if (version_compare($this->version, $ver) == -1) {
|
|
throw new \Exception(sprintf('Version "%s" does not match the minimum required "%s"', $this->version, $ver));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @When I ask for Supervisor version
|
|
*/
|
|
public function iAskForSupervisorVersion()
|
|
{
|
|
$this->version = $this->supervisor->getVersion();
|
|
}
|
|
|
|
/**
|
|
* @Given my Supervisor instance is called :identifier
|
|
*/
|
|
public function mySupervisorInstanceIsCalled($identifier)
|
|
{
|
|
$supervisord = $this->configuration->getSection('supervisord');
|
|
$supervisord->setProperty('identifier', $identifier);
|
|
}
|
|
|
|
/**
|
|
* @When I ask for Supervisor identification
|
|
*/
|
|
public function iAskForSupervisorIdentification()
|
|
{
|
|
$this->identifier = $this->supervisor->getIdentification();
|
|
}
|
|
|
|
/**
|
|
* @Then I should get :identifier as identifier
|
|
*/
|
|
public function iShouldGetAsIdentifier($identifier)
|
|
{
|
|
if ($this->identifier !== $identifier) {
|
|
throw new \Exception(sprintf('Identification "%s" does not match the required "%s"', $this->identifier, $identifier));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @When I ask for the state
|
|
*/
|
|
public function iAskForTheState()
|
|
{
|
|
$this->state = $this->supervisor->getState();
|
|
}
|
|
|
|
/**
|
|
* @Then I should get :code as statecode and :name as statename
|
|
*/
|
|
public function iShouldGetAsStatecodeAndAsStatename($code, $name)
|
|
{
|
|
if ($this->state['statecode'] != $code) {
|
|
throw new \Exception(sprintf('State code "%s" does not match the required "%s"', $this->state['statecode'], $code));
|
|
}
|
|
|
|
if ($this->state['statename'] !== $name) {
|
|
throw new \Exception(sprintf('Statename "%s" does not match the required "%s"', $this->state['statename'], $name));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @When I ask for the PID
|
|
*/
|
|
public function iAskForThePid()
|
|
{
|
|
$this->pid = $this->supervisor->getPID();
|
|
}
|
|
|
|
/**
|
|
* @Then I should get the real PID
|
|
*/
|
|
public function iShouldGetTheRealPid()
|
|
{
|
|
if ($this->process !== $this->pid) {
|
|
throw new \Exception(sprintf('PID "%s" does not match the real "%s"', $this->pid, $this->process));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @When I ask for the log
|
|
*/
|
|
public function iAskForTheLog()
|
|
{
|
|
$this->log = trim($this->supervisor->readLog(-(35 + strlen($this->process)), 0));
|
|
}
|
|
|
|
/**
|
|
* @Then I should get an INFO about supervisord started
|
|
*/
|
|
public function iShouldGetAnInfoAboutSupervisordStarted()
|
|
{
|
|
if ($this->log !== 'INFO supervisord started with pid '.$this->process) {
|
|
throw new \Exception(sprintf('The following log entry was expected: "%s", but we got this: "%s"', 'INFO supervisord started with pid '.$this->process, $this->log));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @When I try to call :action action
|
|
*/
|
|
public function iTryToCallAction($action)
|
|
{
|
|
$this->action = $action;
|
|
$this->response = call_user_func([$this->supervisor, $action]);
|
|
}
|
|
|
|
/**
|
|
* @When I check if the log is really empty
|
|
*/
|
|
public function iCheckIfTheLogIsReallyEmpty()
|
|
{
|
|
$this->log = trim($this->supervisor->readLog(-24, 0));
|
|
}
|
|
|
|
/**
|
|
* @Then I should get a success response
|
|
*/
|
|
public function iShouldGetASuccessResponse()
|
|
{
|
|
if ($this->response !== true) {
|
|
throw new \Exception(sprintf('Action "%s" was unsuccessful', $this->action));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @Then I should get a cleared log
|
|
*/
|
|
public function iShouldGetAClearedLog()
|
|
{
|
|
if ($this->log !== 'INFO reopening log file') {
|
|
throw new \Exception('Empty log cannot be confirmed');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @Then it should be stopped
|
|
*/
|
|
public function itShouldBeStopped()
|
|
{
|
|
if ($this->supervisor->isConnected() === true) {
|
|
throw new \Exception('Supervisor is still available');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @Then it should be running again
|
|
*/
|
|
public function itShouldBeRunningAgain()
|
|
{
|
|
if ($this->supervisor->isConnected() !== true) {
|
|
throw new \Exception('Supervisor is unavailable');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @Given I have a process called :process
|
|
*/
|
|
public function iHaveAProcessCalled($process)
|
|
{
|
|
$this->processName = $this->processes[] = $process;
|
|
|
|
$program = new Section\Program($process, [
|
|
'command' => exec('which '.$process),
|
|
]);
|
|
|
|
$this->configuration->addSection($program);
|
|
}
|
|
|
|
/**
|
|
* @When I wait for start
|
|
*/
|
|
public function iWaitForStart()
|
|
{
|
|
usleep(100000);
|
|
}
|
|
|
|
/**
|
|
* @When I get information about the processes
|
|
*/
|
|
public function iGetInformationAboutTheProcesses()
|
|
{
|
|
$processInfo = $this->supervisor->getAllProcessInfo();
|
|
$processNames = array_column($processInfo, 'name');
|
|
$this->processInfo = array_combine($processNames, $processInfo);
|
|
}
|
|
|
|
/**
|
|
* @Then I should see running
|
|
*/
|
|
public function iShouldSeeRunning()
|
|
{
|
|
foreach ($this->processes as $process) {
|
|
if (!isset($this->processInfo[$process]) or $this->processInfo[$process]['state'] < 10) {
|
|
throw new \Exception(sprintf('Process "%s" is not running', $process));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @Given autostart is disabled
|
|
*/
|
|
public function autostartIsDisabled()
|
|
{
|
|
$program = $this->configuration->getSection('program:'.$this->processName);
|
|
|
|
$program->setProperty('autostart', false);
|
|
}
|
|
|
|
/**
|
|
* @When I get information about the processes before action
|
|
*/
|
|
public function iGetInformationAboutTheProcessesBeforeAction()
|
|
{
|
|
$processInfo = $this->supervisor->getAllProcessInfo();
|
|
$processNames = array_column($processInfo, 'name');
|
|
$this->firstProcessInfo = array_combine($processNames, $processInfo);
|
|
}
|
|
|
|
/**
|
|
* @When I :action the process
|
|
*/
|
|
public function iTheProcess($action)
|
|
{
|
|
$this->action = $action.'Process';
|
|
$this->response = call_user_func([$this->supervisor, $this->action], $this->processName, false);
|
|
}
|
|
|
|
/**
|
|
* @Then I should see not running first
|
|
*/
|
|
public function iShouldSeeNotRunningFirst()
|
|
{
|
|
foreach ($this->processes as $process) {
|
|
if (!isset($this->firstProcessInfo[$process]) or $this->firstProcessInfo[$process]['state'] > 0) {
|
|
throw new \Exception(sprintf('Process "%s" is running', $process));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @When I :action the processes
|
|
*/
|
|
public function iTheProcesses($action)
|
|
{
|
|
$this->action = $action.'AllProcesses';
|
|
$this->response = call_user_func([$this->supervisor, $this->action], false);
|
|
}
|
|
|
|
/**
|
|
* @Then I should get a success response for all
|
|
*/
|
|
public function iShouldGetASuccessResponseForAll()
|
|
{
|
|
foreach ($this->response as $response) {
|
|
if ($response['description'] !== 'OK') {
|
|
throw new \Exception(sprintf('Action "%s" was unsuccessful', $this->action));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @Then I should see running first
|
|
*/
|
|
public function iShouldSeeRunningFirst()
|
|
{
|
|
foreach ($this->processes as $process) {
|
|
if (!isset($this->firstProcessInfo[$process]) or $this->firstProcessInfo[$process]['state'] < 10) {
|
|
throw new \Exception(sprintf('Process "%s" is not running before "%s"', $process, $this->action));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @Then I should see not running
|
|
*/
|
|
public function iShouldSeeNotRunning()
|
|
{
|
|
foreach ($this->processes as $process) {
|
|
if (!isset($this->processInfo[$process]) or $this->processInfo[$process]['state'] > 0) {
|
|
throw new \Exception(sprintf('Process "%s" is running', $process));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @Given it is part of group called :grp
|
|
*/
|
|
public function itIsPartOfGroupCalled($grp)
|
|
{
|
|
$this->groupName = $grp;
|
|
|
|
$program = $this->configuration->getSection('program:'.$this->processName);
|
|
$group = $this->configuration->getSection('group:'.$grp);
|
|
|
|
if (is_null($group)) {
|
|
$group = new Section\Group($grp, ['programs' => $this->processName]);
|
|
$this->configuration->addSection($group);
|
|
} else {
|
|
$programs = $group->getProperty('programs');
|
|
$programs[] = $this->processName;
|
|
$group->setProperty('programs', $programs);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @When I :action the processes in the group
|
|
*/
|
|
public function iTheProcessesInTheGroup($action)
|
|
{
|
|
$this->action = $action.'ProcessGroup';
|
|
$this->response = call_user_func([$this->supervisor, $this->action], $this->groupName, false);
|
|
}
|
|
|
|
/**
|
|
* @Then I should see them as part of the group
|
|
*/
|
|
public function iShouldSeeThemAsPartOfTheGroup()
|
|
{
|
|
foreach ($this->response as $response) {
|
|
if ($response['group'] !== $this->groupName) {
|
|
throw new \Exception(sprintf('Process "%s" is not part of the group "%s"', $response['name'], $this->groupName));
|
|
}
|
|
}
|
|
}
|
|
}
|