Skip to content
......@@ -3,50 +3,109 @@
namespace Aegir\Provision;
use Aegir\Provision\Console\Config;
use Aegir\Provision\Commands\ExampleCommands;
use Aegir\Provision\Console\Config as ConsoleConfig;
use Consolidation\Config\Loader\ConfigProcessor;
use Aegir\Provision\Console\ConsoleOutput;
use Aegir\Provision\Robo\ProvisionCollectionBuilder;
use Aegir\Provision\Robo\ProvisionExecutor;
use Aegir\Provision\Robo\ProvisionTasks;
use Drupal\Console\Core\Style\DrupalStyle;
use League\Container\Container;
use League\Container\ContainerAwareInterface;
use League\Container\ContainerAwareTrait;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Psr\Log\LogLevel;
use Robo\Common\BuilderAwareTrait;
use Robo\Common\ConfigAwareTrait;
use Robo\Config\Config;
use Robo\Common\IO;
use Robo\Contract\BuilderAwareInterface;
use Robo\Contract\ConfigAwareInterface;
use Robo\Contract\IOAwareInterface;
use Robo\Robo;
use Robo\Runner as RoboRunner;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Logger\ConsoleLogger;
use Symfony\Component\Console\Output\OutputInterface;
class Provision {
/**
* Class Provision
*
* Uses BuilderAwareTrait to allow access to Robo Tasks:
*
* $this->getBuilder()->taskExec('ls -la')
* ->run()
*
* @package Aegir\Provision
*/
class Provision implements ConfigAwareInterface, ContainerAwareInterface, LoggerAwareInterface, IOAwareInterface, BuilderAwareInterface {
const APPLICATION_NAME = 'Aegir Provision';
const VERSION = '4.x-dev';
const REPOSITORY = 'aegir-project/provision';
use BuilderAwareTrait;
use ConfigAwareTrait;
use ContainerAwareTrait;
use LoggerAwareTrait;
use IO;
/**
* @var \Robo\Runner
*/
private $runner;
/**
* @var string[]
*/
private $commands = [];
/**
* @var \Aegir\Provision\Context[]
*/
private $contexts = [];
/**
* @var array[]
*/
private $context_files = [];
/**
* Provision constructor.
*
* @param \Aegir\Provision\Console\Config $config
* @param \Symfony\Component\Console\Input\InputInterface|null $input
* @param \Symfony\Component\Console\Output\OutputInterface|null $output
*/
public function __construct(
// Config $config,
Config $config,
InputInterface $input = NULL,
OutputInterface $output = NULL
) {
$logger = new ConsoleLogger($output);
// Prepare Console configuration and import it into Robo config.
$consoleConfig = new \Aegir\Provision\Console\Config();
$config = new Config();
$config->import($consoleConfig->all());
$this->setConfig($config);
$this
->setConfig($config)
;
// Create Application.
$application = new \Aegir\Provision\Application(self::APPLICATION_NAME, $config->get('version'));
$application->setConfig($consoleConfig);
$application = new Application(self::APPLICATION_NAME, self::VERSION);
$application
->setProvision($this)
->setLogger($logger)
;
$application->configureIO($input, $output);
$this->setInput($input);
$this->setOutput($output);
// Create and configure container.
$container = Robo::createDefaultContainer($input, $output, $application, $config);
// $this->setContainer($container);
// $container->add(MyCustomService::class);
$this->setContainer($container);
$this->configureContainer($container);
// Instantiate Robo Runner.
$this->runner = new RoboRunner([
ExampleCommands::class
......@@ -54,12 +113,270 @@ class Provision {
$this->runner->setContainer($container);
$this->runner->setSelfUpdateRepository(self::REPOSITORY);
$this->setBuilder($container->get('builder'));
$this->setLogger($container->get('logger'));
$this->loadAllContexts();
}
/**
* Lookup all context yml files and load into Context classes.
*/
private function loadAllContexts()
{
$folder = $this->getConfig()->get('config_path') . '/provision';
$finder = new \Symfony\Component\Finder\Finder();
$finder->files()->name("*.yml")->in($folder);
foreach ($finder as $file) {
$context_type = substr($file->getFilename(), 0, strpos($file->getFilename(), '.'));
$context_name = substr($file->getFilename(), strpos($file->getFilename(), '.') + 1, strlen($file->getFilename()) - strlen($context_type) - 5);
$this->context_files[$context_name] = [
'name' => $context_name,
'type' => $context_type,
'file' => $file,
];
}
// Load Context classes from files metadata.
foreach ($this->context_files as $context) {
$class = Context::getClassName($context['type']);
$this->contexts[$context['name']] = new $class($context['name'], $this);
}
}
/**
* Loads a single context from file into $this->contexts[$name].
*
* Used to load dependant contexts that might not be instantiated yet.
*
* @param $name
*/
public function loadContext($name) {
$class = Context::getClassName($this->context_files[$name]['type']);
$this->contexts[$this->context_files[$name]['name']] = new $class($this->context_files[$name]['name'], $this);
}
public function run(InputInterface $input, OutputInterface $output) {
$status_code = $this->runner->run($input, $output);
return $status_code;
}
/**
* Register the necessary classes for Provision.
*/
public function configureContainer(Container $container) {
// FROM https://github.com/acquia/blt :
// We create our own builder so that non-command classes are able to
// implement task methods, like taskExec(). Yes, there are now two builders
// in the container. "collectionBuilder" used for the actual command that
// was executed, and "builder" to be used with non-command classes.
$tasks = new ProvisionTasks();
$builder = new ProvisionCollectionBuilder($tasks);
$tasks->setBuilder($builder);
$container->add('builder', $builder);
$container->add('executor', ProvisionExecutor::class)
->withArgument('builder');
}
/**
* Temporary helper to allow public access to output.
*
* @return \Symfony\Component\Console\Output\OutputInterface
*/
public function getOutput()
{
return $this->output();
}
/**
* Temporary helper to allow public access to input.
*
* @return \Symfony\Component\Console\Input\InputInterface
*/
public function getInput()
{
return $this->input();
}
/**
* Gets Logger object.
* Returns the currently active Logger instance.
*
* @return \Psr\Log\LoggerInterface
*/
public function getLogger()
{
return $this->logger;
}
/**
* Provide access to DrupalStyle object.
*
* @return \Drupal\Console\Core\Style\DrupalStyle
*/
public function io()
{
if (!$this->io) {
$this->io = new DrupalStyle($this->input(), $this->output());
}
return $this->io;
}
/**
* Get a new Provision
* @return \Aegir\Provision\Provision
*/
static function getProvision() {
$input = new ArgvInput();
$output = new ConsoleOutput();
$config = new Config();
return new Provision($config, $input, $output);
}
/**
* Return all available contexts.
*
* @return array|Context
*/
public function getAllContexts($name = '') {
if ($name && isset($this->contexts[$name])) {
return $this->contexts[$name];
}
elseif ($name && !isset($this->contexts[$name])) {
return NULL;
}
else {
return $this->contexts;
}
}
/**
* Load all server contexts.
*
* @param null $service
* @return mixed
* @throws \Exception
*/
protected function getAllServers($service = NULL) {
$servers = [];
$context_files = $this->getAllContexts();
if (empty($context_files)) {
throw new \Exception('No server contexts found. Use `provision save` to create one.');
}
foreach ($context_files as $context) {
if ($context->type == 'server') {
$servers[$context->name] = $context;
}
}
return $servers;
}
/**
* Get a simple array of all contexts, for use in an options list.
* @return array
*/
public function getAllContextsOptions($type = NULL) {
$options = [];
foreach ($this->getAllContexts() as $name => $context) {
if ($type) {
if ($context->type == $type) {
$options[$name] = $context->name;
}
}
else {
$options[$name] = $context->type . ' ' . $context->name;
}
}
return $options;
}
/**
* Load the Aegir context with the specified name.
*
* @param $name
*
* @return array|\Aegir\Provision\Context
* @throws \Exception
*/
public function getContext($name) {
// Check if $name is empty.
if (empty($name)) {
throw new \Exception('Context name must not be empty.');
}
// If context exists but hasn't been loaded, load it.
if (empty($this->contexts[$name]) && !empty($this->context_files[$name])) {
$this->loadContext($name);
}
// Check if context still isn't found.
if (empty($this->contexts[$name])) {
throw new \Exception('Context not found with name: ' . $name);
}
return $this->contexts[$name];
}
/**
* Look for a context file being present. This is available before Context
* objects are bootstrapped.
*/
public function getContextFile($name) {
if (empty($name)) {
throw new \Exception('Context name must not be empty.');
}
if (empty($this->context_files[$name])) {
throw new \Exception('Context not found with name: ' . $name);
}
return$this->context_files[$name];
}
/**
* Get a simple array of all servers, optionally specifying the the service_type to filter by ("http", "db" etc.)
* @param string $service_type
* @return array
*/
public function getServerOptions($service_type = '') {
$servers = [];
foreach ($this->getAllServers() as $server) {
if ($service_type && !empty($server->config['services'][$service_type])) {
$servers[$server->name] = $server->name . ': ' . $server->config['services'][$service_type]['type'];
}
elseif ($service_type == '') {
$servers[$server->name] = $server->name . ': ' . $server->config['services'][$service_type]['type'];
}
}
return $servers;
}
/**
* Check that a context type's service requirements are provided by at least 1 server.
*
* @param $type
* @return array
*/
public function checkServiceRequirements($type) {
$class_name = Context::getClassName($type);
// @var $context Context
$service_requirements = $class_name::serviceRequirements();
$services = [];
foreach ($service_requirements as $service) {
try {
if (empty($this->getAllServers($service))) {
$services[$service] = 0;
}
else {
$services[$service] = 1;
}
} catch (\Exception $e) {
$services[$service] = 0;
}
}
return $services;
}
}
<?php
namespace Aegir\Provision\Robo;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Psr\Log\LoggerInterface;
use Psr\Log\LoggerTrait;
use Robo\Collection\CollectionBuilder;
use Robo\Collection\NestedCollectionInterface;
use Robo\Common\CommandArguments;
use Robo\Common\ExecCommand;
use Robo\Common\ExecTrait;
use Robo\Common\OutputAdapter;
use Robo\Common\TaskIO;
use Robo\Result;
use Robo\ResultData;
use Symfony\Component\Process\Process;
class ProvisionCollectionBuilder extends CollectionBuilder {
/**
* @var string|\Robo\Contract\CommandInterface
*/
protected $command;
/**
* @var \Robo\Contract\TaskInterface
*/
protected $currentTask;
}
\ No newline at end of file
<?php
namespace Aegir\Provision\Robo;
use GuzzleHttp\Client;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Robo\Collection\CollectionBuilder;
use Robo\Common\ConfigAwareTrait;
use Robo\Common\IO;
use Robo\Contract\VerbosityThresholdInterface;
use Robo\Robo;
use Robo\Contract\ConfigAwareInterface;
use Robo\Contract\IOAwareInterface;
use Symfony\Component\Process\Process;
/**
* A class for executing commands.
*
* This allows non-Robo-command classes to execute commands easily.
*/
class ProvisionExecutor implements ConfigAwareInterface, IOAwareInterface, LoggerAwareInterface {
use ConfigAwareTrait;
use IO;
use LoggerAwareTrait;
/**
* A copy of the Robo builder.
*
* @var \Aegir\Provision\Robo\ProvisionTasks
*/
protected $builder;
/**
* Executor constructor.
*
* @param \Robo\Collection\CollectionBuilder $builder
* This is a copy of the collection builder, required for calling various
* Robo tasks from non-command files.
*/
public function __construct(CollectionBuilder $builder) {
$this->builder = $builder;
}
/**
* Returns $this->builder.
*
* @return \Aegir\Provision\Robo\ProvisionTasks
* The builder.
*/
public function getBuilder() {
return $this->builder;
}
/**
* Wrapper for taskExec().
*
* @param string $command
* The command to execute.
*
* @return \Robo\Task\Base\Exec
* The task. You must call run() on this to execute it!
*/
public function taskExec($command) {
return $this->builder->taskExec($command);
}
// /**
// * Executes a drush command.
// *
// * @param string $command
// * The command to execute, without "drush" prefix.
// *
// * @return \Robo\Common\ProcessExecutor
// * The unexecuted process.
// */
// public function drush($command) {
// // @todo Set to silent if verbosity is less than very verbose.
// $bin = $this->getConfigValue('composer.bin');
// /** @var \Robo\Common\ProcessExecutor $process_executor */
// $drush_alias = $this->getConfigValue('drush.alias');
// if (!empty($drush_alias)) {
// $drush_alias = "@$drush_alias";
// }
//
// $command_string = "'$bin/drush' $drush_alias $command";
//
// if ($this->input()->hasOption('yes') && $this->input()->getOption('yes')) {
// $command_string .= ' -y';
// }
//
// $process_executor = Robo::process(new Process($command_string));
//
// return $process_executor->dir($this->getConfigValue('docroot'))
// ->interactive(FALSE)
// ->printOutput(TRUE)
// ->printMetadata(TRUE)
// ->setVerbosityThreshold(VerbosityThresholdInterface::VERBOSITY_VERY_VERBOSE);
// }
/**
* Executes a command.
*
* @param string $command
* The command.
*
* @return \Robo\Common\ProcessExecutor
* The unexecuted command.
*/
public function execute($command) {
/** @var \Robo\Common\ProcessExecutor $process_executor */
$process_executor = Robo::process(new Process($command));
return $process_executor->dir($this->getConfigValue('repo.root'))
->printOutput(FALSE)
->printMetadata(FALSE)
->interactive(FALSE);
}
/**
* Kills all system processes that are using a particular port.
*
* @param string $port
* The port number.
*/
public function killProcessByPort($port) {
$this->logger->info("Killing all processes on port '$port'...");
// This is allowed to fail.
// @todo Replace with standardized call to Symfony Process.
exec("command -v lsof && lsof -ti tcp:$port | xargs kill l 2>&1");
exec("pkill -f $port 2>&1");
}
/**
* Kills all system processes containing a particular string.
*
* @param string $name
* The name of the process.
*/
public function killProcessByName($name) {
$this->logger->info("Killing all processing containing string '$name'...");
// This is allowed to fail.
// @todo Replace with standardized call to Symfony Process.
exec("ps aux | grep -i $name | grep -v grep | awk '{print $2}' | xargs kill -9 2>&1");
// exec("ps aux | awk '/$name/ {print $2}' 2>&1 | xargs kill -9");.
}
/**
* Waits until a given URL responds with a non-50x response.
*
* This does have a maximum timeout, defined in wait().
*
* @param string $url
* The URL to wait for.
*/
public function waitForUrlAvailable($url) {
$this->wait([$this, 'checkUrl'], [$url], "Waiting for response from $url...");
}
/**
* Waits until a given callable returns TRUE.
*
* This does have a maximum timeout.
*
* @param callable $callable
* The method/function to wait for a TRUE response from.
* @param array $args
* Arguments to pass to $callable.
* @param string $message
* The message to display when this function is called.
*
* @return bool
* TRUE if callable returns TRUE.
*
* @throws \Exception
*/
public function wait(callable $callable, array $args, $message = '') {
$maxWait = 60 * 1000;
$checkEvery = 1 * 1000;
$start = microtime(TRUE) * 1000;
$end = $start + $maxWait;
if (!$message) {
$method_name = is_array($callable) ? $callable[1] : $callable;
$message = "Waiting for $method_name() to return true.";
}
// For some reason we can't reuse $start here.
while (microtime(TRUE) * 1000 < $end) {
$this->logger->info($message);
try {
if (call_user_func_array($callable, $args)) {
return TRUE;
}
}
catch (\Exception $e) {
$this->logger->error($e->getMessage());
}
usleep($checkEvery * 1000);
}
throw new BltException("Timed out.");
}
/**
* Checks a URL for a non-50x response.
*
* @param string $url
* The URL to check.
*
* @return bool
* TRUE if URL responded with a non-50x response.
*/
public function checkUrl($url) {
try {
$client = new Client();
$res = $client->request('GET', $url, [
'connection_timeout' => 2,
'timeout' => 2,
'exceptions' => FALSE,
]);
if ($res->getStatusCode() && substr($res->getStatusCode(), 0, 1) != '5') {
return TRUE;
}
else {
return FALSE;
}
}
catch (\Exception $e) {
}
return FALSE;
}
}
<?php
namespace Aegir\Provision\Robo;
use Consolidation\Config\ConfigInterface;
use League\Container\ContainerAwareInterface;
use League\Container\ContainerAwareTrait;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Robo\Collection\CollectionBuilder;
use Robo\Common\ConfigAwareTrait;
use Robo\Common\IO;
use Robo\Contract\BuilderAwareInterface;
use Robo\Contract\ConfigAwareInterface;
use Robo\Contract\IOAwareInterface;
use Robo\LoadAllTasks;
/**
* Base class for BLT Robo commands.
*/
class ProvisionTasks implements ConfigAwareInterface, LoggerAwareInterface, BuilderAwareInterface, IOAwareInterface, ContainerAwareInterface {
use ContainerAwareTrait;
use LoadAllTasks;
use ConfigAwareTrait;
// use InspectorAwareTrait;
use IO;
use LoggerAwareTrait;
}
......@@ -8,9 +8,20 @@
namespace Aegir\Provision;
class Service
use Aegir\Provision\Common\ContextAwareTrait;
use Aegir\Provision\Common\ProvisionAwareTrait;
use Psr\Log\LoggerAwareTrait;
use Robo\Common\BuilderAwareTrait;
use Robo\Common\OutputAdapter;
use Robo\Contract\BuilderAwareInterface;
class Service implements BuilderAwareInterface
{
use BuilderAwareTrait;
use ProvisionAwareTrait;
use ContextAwareTrait;
use LoggerAwareTrait;
public $type;
public $properties;
......@@ -23,11 +34,6 @@ class Service
*/
public $provider;
/**
* @var \Aegir\Provision\Application;
*/
public $application;
/**
* @var string
* The machine name of the service. ie. http, db
......@@ -40,12 +46,18 @@ class Service
*/
const SERVICE_NAME = 'Service Name';
function __construct($service_config, $provider_context)
function __construct($service_config, Context $provider_context)
{
$this->provider = $provider_context;
$this->application = $provider_context->application;
$this->setContext($provider_context);
$this->setProvision($provider_context->getProvision());
$this->setLogger($provider_context->getProvision()->getLogger());
$this->type = $service_config['type'];
$this->properties = $service_config['properties'];
if ($provider_context->getBuilder()) {
$this->setBuilder($provider_context->getBuilder());
}
}
/**
......@@ -70,30 +82,54 @@ class Service
return "\Aegir\Provision\Service\\{$service}Service";
}
}
/**
* React to the `provision verify` command.
*/
function verify()
{
return [
'configuration' => $this->writeConfigurations(),
'service' => $this->restartService(),
];
}
/**
* React to `provision verify` command when run on a subscriber, to verify the service's provider.
*
* This is used to allow skipping of the service restart.
* React to the verify command. Passes off to the method verifySite, verifyServer, verifyPlatform.
* @return mixed
*/
function verifyProvider()
{
return [
'configuration' => $this->writeConfigurations(),
];
public function verify() {
$method = 'verify' . ucfirst($this->getContext()->type);
$this->getProvision()->getLogger()->info("Running {method}", [
'method' => $method
]);
return $this::$method();
}
//
// /**
// * React to the `provision verify` command.
// */
// function verifySite()
// {
// return [
// 'configuration' => $this->writeConfigurations(),
// 'service' => $this->restartService(),
// ];
// }
//
// /**
// * React to the `provision verify` command.
// */
// function verifyPlatform()
// {
// return [
// 'configuration' => $this->writeConfigurations(),
// 'service' => $this->restartService(),
// ];
// }
//
// /**
// * React to `provision verify` command when run on a subscriber, to verify the service's provider.
// *
// * This is used to allow skipping of the service restart.
// */
// function verifyServer()
// {
// return [
// 'configuration' => $this->writeConfigurations(),
// ];
// }
/**
* Run the services "restart_command".
* @return bool
......@@ -103,14 +139,19 @@ class Service
return TRUE;
}
else {
try {
$this->application->console->exec($this->properties['restart_command'], 'Restarting service...');
$this->application->io->successLite('Service restarted.');
$task = $this->getProvision()->getBuilder()->taskExec($this->properties['restart_command'])
->silent(!$this->getProvision()->io()->isVerbose())
;
/** @var \Robo\Result $result */
$result = $task->run();
if ($result->wasSuccessful()) {
$this->getProvision()->io()->successLite('Service restarted.');
sleep(1);
return TRUE;
}
catch (\Exception $e) {
$this->application->io->errorLite('Unable to restart service: ' . $e->getMessage());
else {
$this->getProvision()->io()->errorLite('Unable to restart service:' . $result->getOutputData());
}
}
return FALSE;
......@@ -162,12 +203,12 @@ class Service
try {
$config = new $configuration_class($context, $this);
$config->write();
$context->application->io->successLite(
$context->getProvision()->io()->successLite(
'Wrote '.$config->description.' to '.$config->filename()
);
}
catch (\Exception $e) {
$context->application->io->errorLite(
$context->getProvision()->io()->errorLite(
'Unable to write '.$config->description.' to '.$config->filename() . ': ' . $e->getMessage()
);
$success = FALSE;
......@@ -265,4 +306,12 @@ class Service
// ];
}
/**
* @return \Aegir\Provision\Robo\ProvisionBuilder
*/
function getBuilder()
{
return $this->builder;
}
}
\ No newline at end of file
......@@ -34,7 +34,7 @@ class DbMysqlService extends DbService
if ($this->database_exists($test)) {
if (!$this->drop_database($test)) {
$this->provider->application->io->errorLite(strtr("Failed to drop database @dbname", ['@dbname' => $test]));
$this->provider->getProvision()->io()->errorLite(strtr("Failed to drop database @dbname", ['@dbname' => $test]));
}
return TRUE;
}
......
......@@ -12,6 +12,7 @@ namespace Aegir\Provision\Service;
use Aegir\Provision\Context\SiteContext;
use Aegir\Provision\Service;
use Aegir\Provision\ServiceInterface;
use Aegir\Provision\ServiceSubscription;
use Symfony\Component\Console\Output\OutputInterface;
......@@ -20,7 +21,7 @@ use Symfony\Component\Console\Output\OutputInterface;
*
* @package Aegir\Provision\Service
*/
class DbService extends Service
class DbService extends Service implements ServiceInterface
{
const SERVICE = 'db';
......@@ -89,7 +90,7 @@ class DbService extends Service
/**
* React to the `provision verify` command on Server contexts
*/
function verify() {
function verifyServer() {
$this->creds = array_map('urldecode', parse_url($this->properties['master_db']));
if (!isset($this->creds['port'])) {
......@@ -105,18 +106,18 @@ class DbService extends Service
try {
$this->connect();
$return = TRUE;
$this->provider->application->io->successLite('Successfully connected to database server!');
$this->provider->getProvision()->io()->successLite('Successfully connected to database server!');
if ($this->can_create_database()) {
$this->provider->application->io->successLite('Provision can create new databases.');
$this->provider->getProvision()->io()->successLite('Provision can create new databases.');
} else {
$this->provider->application->io->errorLite('Provision is unable to create databases.');
$this->provider->getProvision()->io()->errorLite('Provision is unable to create databases.');
$return = FALSE;
}
if ($this->can_grant_privileges()) {
$this->provider->application->io->successLite('Provision can grant privileges on database users.');
$this->provider->getProvision()->io()->successLite('Provision can grant privileges on database users.');
} else {
$this->provider->application->io->errorLite('Provision is unable to grant privileges on database users.');
$this->provider->getProvision()->io()->errorLite('Provision is unable to grant privileges on database users.');
$return = FALSE;
}
......@@ -125,7 +126,7 @@ class DbService extends Service
];
}
catch (\PDOException $e) {
$this->provider->application->io->errorLite($e->getMessage());
$this->provider->getProvision()->io()->errorLite($e->getMessage());
return [
'service' => FALSE
];
......@@ -135,11 +136,11 @@ class DbService extends Service
/**
* React to the `provision verify` command on subscriber contexts (sites and platforms)
*/
function verifySubscription(ServiceSubscription $serviceSubscription) {
$this->subscription = $serviceSubscription;
function verifySite() {
$this->subscription = $this->getContext()->getSubscription($this->type);
// Check for database
$this->create_site_database($serviceSubscription->context);
$this->create_site_database($this->getContext());
$this->creds_root = array_map('urldecode', parse_url($this->properties['master_db']));
......@@ -156,19 +157,23 @@ class DbService extends Service
try {
$this->connect();
$this->subscription->context->application->io->successLite('Successfully connected to database server.');
$this->subscription->context->getProvision()->io()->successLite('Successfully connected to database server.');
return [
'service' => TRUE
];
}
catch (\PDOException $e) {
$this->subscription->context->application->io->errorLite($e->getMessage());
$this->subscription->context->getProvision()->io()->errorLite($e->getMessage());
return [
'service' => FALSE
];
}
}
public function verifyPlatform() {
}
/**
* React to `provision verify` command when run on a subscriber, to verify the service's provider.
*
......@@ -215,11 +220,11 @@ class DbService extends Service
$query = preg_replace_callback($this::PROVISION_QUERY_REGEXP, array($this, 'query_callback'), $query);
try {
$this->provider->application->logger->notice("Running Query: {$query}");
$this->provider->getProvision()->getLogger()->info("Running Query: {$query}");
$result = $this->conn->query($query);
}
catch (\PDOException $e) {
$this->provider->application->io->errorLite($e->getMessage());
$this->provider->getProvision()->getLogger()->error($e->getMessage());
return FALSE;
}
......@@ -381,13 +386,13 @@ class DbService extends Service
}
if ($this->database_exists($db_name)) {
$this->application->io->successLite(strtr("Database '@name' already exists.", [
$this->getProvision()->io()->successLite(strtr("Database '@name' already exists.", [
'@name' => $db_name
]));
}
else {
$this->create_database($db_name);
$this->application->io->successLite(strtr("Created database '@name'.", [
$this->getProvision()->io()->successLite(strtr("Created database '@name'.", [
'@name' => $db_name,
]));
}
......@@ -398,7 +403,7 @@ class DbService extends Service
'@user' => $db_user
]));
}
$this->application->io->successLite(strtr("Granted privileges to user '@user@@host' for database '@name'.", [
$this->getProvision()->io()->successLite(strtr("Granted privileges to user '@user@@host' for database '@name'.", [
'@user' => $db_user,
'@host' => $db_grant_host,
'@name' => $db_name,
......@@ -408,7 +413,7 @@ class DbService extends Service
$status = $this->database_exists($db_name);
if ($status) {
$this->application->io->successLite(strtr("Database service configured for site @name.", [
$this->getProvision()->io()->successLite(strtr("Database service configured for site @name.", [
'@name' => $site->name,
]));
}
......
......@@ -21,7 +21,7 @@ class PlatformConfiguration extends Configuration {
function filename() {
$file = $this->context->name . '.conf';
return $this->context->application->getConfig()->get('config_path') . '/' . $this->service->provider->name . '/' . $this->service->getType() . '/platform.d/' . $file;
return $this->context->getProvision()->getConfig()->get('config_path') . '/' . $this->service->provider->name . '/' . $this->service->getType() . '/platform.d/' . $file;
}
function process()
......
......@@ -26,7 +26,7 @@ class ServerConfiguration extends Configuration {
function filename() {
if ($this->service->getType()) {
$file = $this->service->getType() . '.conf';
return $this->service->provider->application->getConfig()->get('config_path') . '/' . $this->service->provider->name . '/' . $file;
return $this->service->provider->getProvision()->getConfig()->get('config_path') . '/' . $this->service->provider->name . '/' . $file;
}
else {
return FALSE;
......@@ -35,7 +35,7 @@ class ServerConfiguration extends Configuration {
function process()
{
parent::process();
$app_dir = $this->context->application->getConfig()->get('config_path') . '/' . $this->context->name . '/' . $this->service->getType();
$app_dir = $this->context->getProvision()->getConfig()->get('config_path') . '/' . $this->context->name . '/' . $this->service->getType();
$this->data['http_port'] = $this->service->properties['http_port'];
$this->data['include_statement'] = '# INCLUDE STATEMENT';
$this->data['http_pred_path'] = "{$app_dir}/pre.d";
......
......@@ -23,7 +23,7 @@ class SiteConfiguration extends Configuration {
function filename() {
$file = $this->context->getProperty('uri') . '.conf';
return $this->context->application->getConfig()->get('config_path') . '/' . $this->service->provider->name . '/' . $this->service->getType() . '/vhost.d/' . $file;
return $this->context->getProvision()->getConfig()->get('config_path') . '/' . $this->service->provider->name . '/' . $this->service->getType() . '/vhost.d/' . $file;
}
function process() {
......
<?php
/**
* @file
* The Provision HttpApacheService class.
*
* @see \Provision_Service_http_apache
*/
namespace Aegir\Provision\Service\Http;
use Aegir\Provision\Robo\ProvisionCollectionBuilder;
use Aegir\Provision\Service\Http\Apache\Configuration\PlatformConfiguration;
use Aegir\Provision\Service\Http\Apache\Configuration\ServerConfiguration;
use Aegir\Provision\Service\Http\Apache\Configuration\SiteConfiguration;
use Aegir\Provision\Service\HttpService;
use Aegir\Provision\ServiceSubscription;
use Robo\Result;
use Aegir\Provision\Service\Http\Php\PhpServer;
use Symfony\Component\Process\Process;
/**
* Class HttpPhpService
*
* @package Aegir\Provision\Service\Http
*/
class HttpPhpService extends HttpService
{
const SERVICE_TYPE = 'php';
const SERVICE_TYPE_NAME = 'PHP Server';
public function verifyServer()
{
$host = $this->getContext()->getProperty('remote_host');
$port = $this->getProperty('http_port');
$this->getProvision()->getLogger()->info('Running server at {host}:{port}', [
'host' => $host,
'port' => $port,
]);
//
$this->getContext()->getBuilder()->build(PhpServer::class, ['port' => $port]);
// $server = new PhpServer($port);
/** @var PhpServer $server */
$server = $this->getContext()->getBuilder()->task(PhpServer::class, ['port' => $port]);
$server->host($host);
$server->dir(__DIR__ . '/Php/servertest');
$server->background();
$server->run();
//
$pid = $server->getProcess()->getPid();
$this->getProvision()->getLogger()->info('Server started at {host}:{port} running as process {pid}', [
'host' => $host,
'port' => $port,
'pid' => $pid,
]);
}
public function verifySite()
{
}
public function verifySubscription(ServiceSubscription $subscription)
{
print_r($subscription->context->getProperty('root'));
$this->application->getBuilder()->taskServer($this->getProperty('http_port'))
->dir('.');;
}
}
<?php
namespace Aegir\Provision\Service\Http\Php;
use Robo\Common\ExecCommand;
use Robo\Common\ExecTrait;
class PhpServer extends \Robo\Task\Development\PhpServer {
/**
* @var int
*/
protected $port;
/**
* @var string
*/
protected $host = '127.0.0.1';
/**
* @var string
*/
protected $command = 'php -S %s:%d ';
/**
* @param int $port
*/
public function __construct($port)
{
$this->port = $port;
if (strtolower(PHP_OS) === 'linux') {
$this->command = 'exec php -S %s:%d ';
}
}
/**
* @param string $host
*
* @return $this
*/
public function host($host)
{
$this->host = $host;
return $this;
}
/**
* @param string $path
*
* @return $this
*/
public function dir($path)
{
$this->command .= "-t $path";
return $this;
}
/**
* {@inheritdoc}
*/
public function getCommand()
{
return sprintf($this->command, $this->host, $this->port);
}
public function run() {
$this->executeCommand($this->getCommand());
}
public function getProcess() {
return $this->process;
}
public function task() {
}
}
\ No newline at end of file
<h1><?php print "It Works!"; ?></h1>
<p>
<?php print "This page is being generated by PHP web server." ?>
</p>
......@@ -11,6 +11,7 @@ namespace Aegir\Provision\Service;
//require_once DRUSH_BASE_PATH . '/commands/core/rsync.core.inc';
use Aegir\Provision\Service;
use Aegir\Provision\ServiceInterface;
use Aegir\Provision\ServiceSubscription;
use Consolidation\AnnotatedCommand\CommandFileDiscovery;
......@@ -19,7 +20,7 @@ use Consolidation\AnnotatedCommand\CommandFileDiscovery;
*
* @package Aegir\Provision\Service
*/
class HttpService extends Service {
class HttpService extends Service implements ServiceInterface {
const SERVICE = 'http';
const SERVICE_NAME = 'Web Server';
......@@ -57,7 +58,7 @@ class HttpService extends Service {
*
* This is used to allow skipping of the service restart.
*/
function verifyProvider()
function verifyServer()
{
return [
'configuration' => $this->writeConfigurations(),
......@@ -67,14 +68,18 @@ class HttpService extends Service {
/**
* React to the `provision verify` command on Server contexts
*/
function verifySubscription(ServiceSubscription $serviceSubscription) {
$this->subscription = $serviceSubscription;
function verifySite() {
$this->subscription = $this->getContext()->getSubscription();
return [
'configuration' => $this->writeConfigurations($serviceSubscription),
'configuration' => $this->writeConfigurations($this->subscription),
'service' => $this->restartService(),
];
}
//
function verifyPlatform() {
}
//
// /**
// * Support the ability to cloak the database credentials using environment variables.
// */
......
<?php
namespace Aegir\Provision;
interface ServiceInterface
{
/**
* Triggered on `provision verify` command on Site contexts.
*/
public function verifySite();
/**
* Triggered on `provision verify` command on Platform contexts.
*/
public function verifyPlatform();
/**
* Triggered on `provision verify` command on Site contexts.
*/
public function verifyServer();
}
\ No newline at end of file
......@@ -9,6 +9,9 @@
namespace Aegir\Provision;
use Aegir\Provision\Common\ContextAwareTrait;
use Aegir\Provision\Common\ProvisionAwareTrait;
class ServiceSubscription {
public $context;
......@@ -16,10 +19,19 @@ class ServiceSubscription {
public $server;
public $type;
public $properties = [];
use ProvisionAwareTrait;
use ContextAwareTrait;
function __construct($context, $server, $service_name) {
$this->context = $context;
$this->server = Application::getContext($server, $context->application);
function __construct(
Context $context,
$server,
$service_name
) {
$this->setContext($context);
$this->setProvision($context->getProvision());
$this->server = $this->getProvision()->getContext($server);
$this->service = $this->server->getService($service_name);
$this->type = $this->server->getService($service_name)->type;
......