Skip to content
Commits on Source (24)
...@@ -7,9 +7,11 @@ use Aegir\Provision\Command\ServicesCommand; ...@@ -7,9 +7,11 @@ use Aegir\Provision\Command\ServicesCommand;
use Aegir\Provision\Command\ShellCommand; use Aegir\Provision\Command\ShellCommand;
use Aegir\Provision\Command\StatusCommand; use Aegir\Provision\Command\StatusCommand;
use Aegir\Provision\Command\VerifyCommand; use Aegir\Provision\Command\VerifyCommand;
use Aegir\Provision\Common\ProvisionAwareTrait;
use Aegir\Provision\Console\Config; use Aegir\Provision\Console\Config;
use Aegir\Provision\Console\ConsoleOutput; use Aegir\Provision\Console\ConsoleOutput;
use Drupal\Console\Core\Style\DrupalStyle; use Drupal\Console\Core\Style\DrupalStyle;
use Psr\Log\LoggerAwareTrait;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel; use Psr\Log\LogLevel;
use Symfony\Component\Console\Command\HelpCommand; use Symfony\Component\Console\Command\HelpCommand;
...@@ -20,6 +22,7 @@ use Symfony\Component\Console\Input\InputDefinition; ...@@ -20,6 +22,7 @@ use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Logger\ConsoleLogger; use Symfony\Component\Console\Logger\ConsoleLogger;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Application as BaseApplication; use Symfony\Component\Console\Application as BaseApplication;
use Symfony\Component\Console\Command\Command as BaseCommand;
//use Symfony\Component\DependencyInjection\ContainerInterface; //use Symfony\Component\DependencyInjection\ContainerInterface;
//use Drupal\Console\Annotations\DrupalCommandAnnotationReader; //use Drupal\Console\Annotations\DrupalCommandAnnotationReader;
...@@ -43,21 +46,9 @@ class Application extends BaseApplication ...@@ -43,21 +46,9 @@ class Application extends BaseApplication
* @var string * @var string
*/ */
const DEFAULT_TIMEZONE = 'America/New_York'; const DEFAULT_TIMEZONE = 'America/New_York';
/** use ProvisionAwareTrait;
* @var LoggerInterface use LoggerAwareTrait;
*/
public $logger;
/**
* @var DrupalStyle
*/
public $io;
/**
* @var \Aegir\Provision\Provision
*/
public $provision;
/** /**
* @var ConsoleOutput * @var ConsoleOutput
...@@ -72,53 +63,25 @@ class Application extends BaseApplication ...@@ -72,53 +63,25 @@ class Application extends BaseApplication
* *
* @throws \Exception * @throws \Exception
*/ */
public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN', Provision $provision = NULL) public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN')
{ {
// If no timezone is set, set Default. // If no timezone is set, set Default.
if (empty(ini_get('date.timezone'))) { if (empty(ini_get('date.timezone'))) {
date_default_timezone_set($this::DEFAULT_TIMEZONE); date_default_timezone_set($this::DEFAULT_TIMEZONE);
} }
//
// // Load Configs
// try {
// $this->config = new Config();
// }
// catch (\Exception $e) {
// throw new \Exception($e->getMessage());
// }
$this->provision = $provision;
parent::__construct($name, $version);
}
/**
* Prepare input and output arguments. Use this to extend the Application object so that $input and $output is fully populated.
*
* @param \Symfony\Component\Console\Input\InputInterface $input
* @param \Symfony\Component\Console\Output\OutputInterface $output
*/
public function configureIO(InputInterface $input, OutputInterface $output) {
parent::configureIO($input, $output);
$this->io = new DrupalStyle($input, $output);
$this->input = $input;
$this->output = $output;
$this->logger = new ConsoleLogger($output, parent::__construct($name, $version);
[LogLevel::INFO => OutputInterface::VERBOSITY_NORMAL]
);
} }
/** /**
* Getter for Configuration. * Make configureIO public so we can run it before ->run()
* *
* @return \Aegir\Provision\Console\ProvisionConfig * @param InputInterface $input
* Configuration object. * @param OutputInterface $output
*/ */
public function getConfig() public function configureIO(InputInterface $input, OutputInterface $output)
{ {
return $this->provision->getConfig(); parent::configureIO($input, $output);
} }
/** /**
...@@ -136,7 +99,29 @@ class Application extends BaseApplication ...@@ -136,7 +99,29 @@ class Application extends BaseApplication
return $commands; return $commands;
} }
/**
* Interrupts Command execution to add services like provision and logger.
*
* @param \Symfony\Component\Console\Command\Command $command
* @param \Symfony\Component\Console\Input\InputInterface $input
* @param \Symfony\Component\Console\Output\OutputInterface $output
*
* @return int
*/
protected function doRunCommand( BaseCommand $command, InputInterface $input, OutputInterface $output)
{
// Only setProvision if the command is using the trait.
if (method_exists($command, 'setProvision')) {
$command
->setProvision($this->getProvision())
->setLogger($this->logger)
;
}
$exitCode = parent::doRunCommand($command, $input, $output);
return $exitCode;
}
/** /**
* {@inheritdoc} * {@inheritdoc}
* *
...@@ -156,145 +141,4 @@ class Application extends BaseApplication ...@@ -156,145 +141,4 @@ class Application extends BaseApplication
return $inputDefinition; return $inputDefinition;
} }
/**
* Load all contexts into Context objects.
*
* @return array
*/
static function getAllContexts($name = '', $application = NULL) {
$contexts = [];
$config = new Config();
$context_files = [];
$finder = new \Symfony\Component\Finder\Finder();
$finder->files()->name('*' . $name . '.yml')->in($config->get('config_path') . '/provision');
foreach ($finder as $file) {
list($context_type, $context_name) = explode('.', $file->getFilename());
$context_files[$context_name] = [
'name' => $context_name,
'type' => $context_type,
'file' => $file,
];
}
foreach ($context_files as $context) {
$class = Context::getClassName($context['type']);
$contexts[$context['name']] = new $class($context['name'], $application);
}
if ($name && isset($contexts[$name])) {
return $contexts[$name];
}
elseif ($name && !isset($contexts[$name])) {
return NULL;
}
else {
return $contexts;
}
}
/**
* Load all server contexts.
*
* @param null $service
* @return mixed
* @throws \Exception
*/
static public function getAllServers($service = NULL) {
$servers = [];
$context_files = self::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 \Aegir\Provision\Context
* @throws \Exception
*/
static public function getContext($name, Application $application = NULL) {
if (empty($name)) {
throw new \Exception('Context name must not be empty.');
}
if (empty(Application::getAllContexts($name, $application))) {
throw new \Exception('Context not found with name: ' . $name);
}
return Application::getAllContexts($name, $application);
}
/**
* 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
*/
static 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(Application::getAllServers($service))) {
$services[$service] = 0;
}
else {
$services[$service] = 1;
}
} catch (\Exception $e) {
$services[$service] = 0;
}
}
return $services;
}
} }
...@@ -2,7 +2,10 @@ ...@@ -2,7 +2,10 @@
namespace Aegir\Provision; namespace Aegir\Provision;
use Aegir\Provision\Common\ProvisionAwareTrait;
use Drupal\Console\Core\Style\DrupalStyle; use Drupal\Console\Core\Style\DrupalStyle;
use Psr\Log\LoggerAwareTrait;
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel; use Psr\Log\LogLevel;
use Symfony\Component\Console\Command\Command as BaseCommand; use Symfony\Component\Console\Command\Command as BaseCommand;
use Drupal\Console\Core\Command\Shared\CommandTrait; use Drupal\Console\Core\Command\Shared\CommandTrait;
...@@ -19,7 +22,8 @@ abstract class Command extends BaseCommand ...@@ -19,7 +22,8 @@ abstract class Command extends BaseCommand
{ {
use CommandTrait; use CommandTrait;
use ProvisionAwareTrait;
use LoggerAwareTrait;
/** /**
* @var \Symfony\Component\Console\Input\InputInterface * @var \Symfony\Component\Console\Input\InputInterface
...@@ -50,7 +54,7 @@ abstract class Command extends BaseCommand ...@@ -50,7 +54,7 @@ abstract class Command extends BaseCommand
* @var string * @var string
*/ */
public $context_name; public $context_name;
/** /**
* @param InputInterface $input An InputInterface instance * @param InputInterface $input An InputInterface instance
* @param OutputInterface $output An OutputInterface instance * @param OutputInterface $output An OutputInterface instance
...@@ -70,7 +74,7 @@ abstract class Command extends BaseCommand ...@@ -70,7 +74,7 @@ abstract class Command extends BaseCommand
try { try {
// Load context from context_name argument. // Load context from context_name argument.
$this->context_name = $this->input->getArgument('context_name'); $this->context_name = $this->input->getArgument('context_name');
$this->context = Application::getContext($this->context_name, $this->getApplication()); $this->context = $this->getProvision()->getContext($this->context_name);
} }
catch (\Exception $e) { catch (\Exception $e) {
...@@ -92,7 +96,7 @@ abstract class Command extends BaseCommand ...@@ -92,7 +96,7 @@ abstract class Command extends BaseCommand
$this->input->setArgument('context_name', $this->context_name); $this->input->setArgument('context_name', $this->context_name);
try { try {
$this->context = Application::getContext($this->context_name, $this->getApplication()); $this->context = $this->getProvision()->getContext($this->context_name);
} }
catch (\Exception $e) { catch (\Exception $e) {
$this->context = NULL; $this->context = NULL;
...@@ -104,11 +108,11 @@ abstract class Command extends BaseCommand ...@@ -104,11 +108,11 @@ abstract class Command extends BaseCommand
* Show a list of Contexts to the user for them to choose from. * Show a list of Contexts to the user for them to choose from.
*/ */
public function askForContext($question = 'Choose a context') { public function askForContext($question = 'Choose a context') {
if (empty($this->getApplication()->getAllContextsOptions())) { if (empty($this->getProvision()->getAllContextsOptions())) {
throw new \Exception('No contexts available! use <comment>provision save</comment> to create one.'); throw new \Exception('No contexts available! use <comment>provision save</comment> to create one.');
} }
$this->context_name = $this->io->choice($question, $this->getApplication()->getAllContextsOptions()); $this->context_name = $this->io->choice($question, $this->getProvision()->getAllContextsOptions());
} }
/** /**
......
...@@ -8,6 +8,7 @@ use Aegir\Provision\Context; ...@@ -8,6 +8,7 @@ use Aegir\Provision\Context;
use Aegir\Provision\Context\PlatformContext; use Aegir\Provision\Context\PlatformContext;
use Aegir\Provision\Context\ServerContext; use Aegir\Provision\Context\ServerContext;
use Aegir\Provision\Context\SiteContext; use Aegir\Provision\Context\SiteContext;
use Aegir\Provision\Provision;
use Aegir\Provision\Service; use Aegir\Provision\Service;
use Symfony\Component\Console\Exception\InvalidOptionException; use Symfony\Component\Console\Exception\InvalidOptionException;
use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Input\ArrayInput;
...@@ -152,7 +153,7 @@ class SaveCommand extends Command ...@@ -152,7 +153,7 @@ class SaveCommand extends Command
// Check for context type service requirements. // Check for context type service requirements.
$exit = FALSE; $exit = FALSE;
$this->io->comment("Checking service requirements for context type {$context_type}..."); $this->io->comment("Checking service requirements for context type {$context_type}...");
$reqs = Application::checkServiceRequirements($context_type); $reqs = $this->getProvision()->checkServiceRequirements($context_type);
foreach ($reqs as $service => $available) { foreach ($reqs as $service => $available) {
if ($available) { if ($available) {
$this->io->successLite("Service $service: Available"); $this->io->successLite("Service $service: Available");
...@@ -169,9 +170,12 @@ class SaveCommand extends Command ...@@ -169,9 +170,12 @@ class SaveCommand extends Command
} }
$properties = $this->askForContextProperties(); $options = $this->askForContextProperties();
$options['name'] = $this->context_name;
$options['type'] = $this->context_type;
$class = Context::getClassName($this->input->getOption('context_type')); $class = Context::getClassName($this->input->getOption('context_type'));
$this->context = new $class($input->getArgument('context_name'), $this->getApplication(), $properties); $this->context = new $class($input->getArgument('context_name'), $this->getProvision(), $options);
} }
// Delete context config. // Delete context config.
...@@ -227,7 +231,7 @@ class SaveCommand extends Command ...@@ -227,7 +231,7 @@ class SaveCommand extends Command
*/ */
public function askForContext($question = 'Choose a context') public function askForContext($question = 'Choose a context')
{ {
$options = $this->getApplication()->getAllContextsOptions(); $options = $this->getProvision()->getAllContextsOptions();
// If there are options, add "new" to the list. // If there are options, add "new" to the list.
if (count($options)) { if (count($options)) {
...@@ -305,7 +309,7 @@ class SaveCommand extends Command ...@@ -305,7 +309,7 @@ class SaveCommand extends Command
// $context_name = $this->io->ask($all_services[$type]); // $context_name = $this->io->ask($all_services[$type]);
// } // }
// $context = Application::getContext($context_name); // $context = Provision::getContext($context_name);
$this->io->info("Adding required service $type..."); $this->io->info("Adding required service $type...");
...@@ -348,7 +352,7 @@ class SaveCommand extends Command ...@@ -348,7 +352,7 @@ class SaveCommand extends Command
$contexts[$property] = $this->input->getOption($property); $contexts[$property] = $this->input->getOption($property);
try { try {
$context = Application::getContext($contexts[$property]); $context = $this->getProvision()->getContext($contexts[$property]);
} }
catch (\Exception $e) { catch (\Exception $e) {
throw new \Exception("Context set by option --{$property} does not exist."); throw new \Exception("Context set by option --{$property} does not exist.");
...@@ -359,7 +363,7 @@ class SaveCommand extends Command ...@@ -359,7 +363,7 @@ class SaveCommand extends Command
} }
} }
else { else {
$contexts[$property] = $this->io->choice("Select $property context", $this->getApplication()->getAllContextsOptions($context_type)); $contexts[$property] = $this->io->choice("Select $property context", $this->getProvision()->getAllContextsOptions($context_type));
} }
} }
return $contexts; return $contexts;
......
...@@ -219,16 +219,16 @@ class ServicesCommand extends Command ...@@ -219,16 +219,16 @@ class ServicesCommand extends Command
} }
// All other context types are associating with servers that provide the service. // All other context types are associating with servers that provide the service.
else { else {
if (empty($this->getApplication()->getServerOptions($service))) { if (empty($this->getProvision()->getServerOptions($service))) {
throw new \Exception("No servers providing $service service were found. Create one with `provision save` or use `provision services` to add to an existing server."); throw new \Exception("No servers providing $service service were found. Create one with `provision save` or use `provision services` to add to an existing server.");
} }
$server = $this->input->getArgument('server')? $server = $this->input->getArgument('server')?
$this->input->getArgument('server'): $this->input->getArgument('server'):
$this->io->choice('Which server?', $this->getApplication()->getServerOptions($service)); $this->io->choice('Which server?', $this->getProvision()->getServerOptions($service));
// Then ask for all options. // Then ask for all options.
$server_context = $this->getApplication()->getContext($server); $server_context = $this->getProvision()->getContext($server);
$properties = $this->askForServiceProperties($service); $properties = $this->askForServiceProperties($service);
$this->io->info("Using $service service from server $server..."); $this->io->info("Using $service service from server $server...");
......
...@@ -43,6 +43,7 @@ class StatusCommand extends Command ...@@ -43,6 +43,7 @@ class StatusCommand extends Command
*/ */
protected function execute(InputInterface $input, OutputInterface $output) protected function execute(InputInterface $input, OutputInterface $output)
{ {
$this->getProvision();
if ($input->getArgument('context_name')) { if ($input->getArgument('context_name')) {
$rows = [['Configuration File', $this->context->config_path]]; $rows = [['Configuration File', $this->context->config_path]];
...@@ -59,7 +60,7 @@ class StatusCommand extends Command ...@@ -59,7 +60,7 @@ class StatusCommand extends Command
else { else {
$headers = ['Provision CLI Configuration']; $headers = ['Provision CLI Configuration'];
$rows = []; $rows = [];
$config = $this->getApplication()->getConfig()->toArray(); $config = $this->getProvision()->getConfig()->toArray();
unset($config['options']); unset($config['options']);
foreach ($config as $key => $value) { foreach ($config as $key => $value) {
$rows[] = [$key, $value]; $rows[] = [$key, $value];
...@@ -68,14 +69,14 @@ class StatusCommand extends Command ...@@ -68,14 +69,14 @@ class StatusCommand extends Command
// Lookup all contexts // Lookup all contexts
$rows = []; $rows = [];
foreach ($this->getApplication()->getAllContexts() as $context) { foreach ($this->getProvision()->getAllContexts() as $context) {
$rows[] = [$context->name, $context->type]; $rows[] = [$context->name, $context->type];
} }
$headers = ['Contexts']; $headers = ['Contexts'];
$this->io->table($headers, $rows); $this->io->table($headers, $rows);
// Offer to output a context status. // Offer to output a context status.
$options = $this->getApplication()->getAllContextsOptions(); $options = $this->getProvision()->getAllContextsOptions();
$options['none'] = 'none'; $options['none'] = 'none';
$context = $this->io->choiceNoList('Get status for', $options, 'none'); $context = $this->io->choiceNoList('Get status for', $options, 'none');
......
...@@ -60,6 +60,11 @@ class VerifyCommand extends Command ...@@ -60,6 +60,11 @@ class VerifyCommand extends Command
*/ */
protected function execute(InputInterface $input, OutputInterface $output) protected function execute(InputInterface $input, OutputInterface $output)
{ {
if (empty($this->context)) {
throw new \Exception("You must specify a context to verify.");
}
$this->io->title(strtr("Verify %type: %name", [ $this->io->title(strtr("Verify %type: %name", [
'%name' => $this->context_name, '%name' => $this->context_name,
'%type' => $this->context->type, '%type' => $this->context->type,
...@@ -75,8 +80,7 @@ class VerifyCommand extends Command ...@@ -75,8 +80,7 @@ class VerifyCommand extends Command
} }
*/ */
$message = $this->context->verify(); $message = $this->context->verifyCommand();
$this->io->comment($message);
} }
} }
<?php
namespace Aegir\Provision\Common;
use Aegir\Provision\Context;
trait ContextAwareTrait
{
/**
* @var Context
*/
protected $context = NULL;
/**
* @param Context $context
*
* @return $this
*/
public function setContext(Context $context = NULL)
{
$this->context = $context;
return $this;
}
/**
* @return Context
*/
public function getContext()
{
return $this->context;
}
}
<?php
namespace Aegir\Provision\Common;
use Aegir\Provision\Context;
use Symfony\Component\Process\Process;
use Twig\Node\Expression\Unary\NegUnary;
trait ProcessAwareTrait
{
/**
* @var Process
*/
protected $process = NULL;
// /**
// * @var string
// */
// protected $command;
/**
* @param Process $process
*
* @return $this
*/
protected function setProcess(Process $process = NULL)
{
$this->process = $process;
return $this;
}
/**
* @return Process
*/
public function getProcess()
{
if (is_null($this->process)) {
$this->process = new Process($this->command);
}
return $this->process;
}
/**
* @param $command
*
* @return $this
*/
public function setCommand($command) {
$this->command = $command;
return $this;
}
/**
* @return string
*/
public function getCommand() {
return $this->command;
}
public function execute() {
$this->process->run();
}
}
<?php
namespace Aegir\Provision\Common;
use Aegir\Provision\Provision;
trait ProvisionAwareTrait
{
/**
* @var Provision
*/
protected $provision = NULL;
/**
* @param Provision $provision
*
* @return $this
*/
public function setProvision(Provision $provision = NULL)
{
$this->provision = $provision;
return $this;
}
/**
* @return Provision
*/
public function getProvision()
{
if (is_null($this->provision)) {
return Provision::getProvision();
}
return $this->provision;
}
}
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
namespace Aegir\Provision\ConfigDefinition; namespace Aegir\Provision\ConfigDefinition;
use Aegir\Provision\Application; use Aegir\Provision\Common\ProvisionAwareTrait;
use Symfony\Component\Config\Definition\Builder\ScalarNodeDefinition; use Symfony\Component\Config\Definition\Builder\ScalarNodeDefinition;
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
...@@ -28,6 +28,8 @@ use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; ...@@ -28,6 +28,8 @@ use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
*/ */
class ContextNodeDefinition extends ScalarNodeDefinition class ContextNodeDefinition extends ScalarNodeDefinition
{ {
use ProvisionAwareTrait;
protected function createNode() protected function createNode()
{ {
/** /**
...@@ -50,12 +52,13 @@ class ContextNodeDefinition extends ScalarNodeDefinition ...@@ -50,12 +52,13 @@ class ContextNodeDefinition extends ScalarNodeDefinition
*/ */
public function validateContext($value) public function validateContext($value)
{ {
$this->setProvision($this->getNode()->getAttribute('provision'));
// No need to do anything else. // No need to do anything else.
// If there is no context named $value, getContext() throws an exception for us. // If there is no context named $value, getContext() throws an exception for us.
Application::getContext($value);
// If context_type is specified, Validate that the desired context is the right type. // If context_type is specified, Validate that the desired context is the right type.
if ($this->getNode()->getAttribute('context_type') && Application::getContext($value)->type != $this->getNode()->getAttribute('context_type')) { if ($this->getNode()->getAttribute('context_type') && $this->getProvision()->getContext($value)->type != $this->getNode()->getAttribute('context_type')) {
throw new InvalidConfigurationException(strtr('The context specified for !name must be type !type.', [ throw new InvalidConfigurationException(strtr('The context specified for !name must be type !type.', [
'!name' => $this->name, '!name' => $this->name,
'!type' => $this->getNode()->getAttribute('context_type'), '!type' => $this->getNode()->getAttribute('context_type'),
...@@ -69,8 +72,8 @@ class ContextNodeDefinition extends ScalarNodeDefinition ...@@ -69,8 +72,8 @@ class ContextNodeDefinition extends ScalarNodeDefinition
$this->getNode()->getAttribute('service_requirement'): $this->getNode()->getAttribute('service_requirement'):
$path[2] $path[2]
; ;
Application::getContext($value)->getService($service); $this->getProvision()->getContext($value)->getService($service);
} }
return $value; return $value;
} }
......
...@@ -6,14 +6,15 @@ ...@@ -6,14 +6,15 @@
namespace Aegir\Provision; namespace Aegir\Provision;
use Aegir\Provision\Common\ProvisionAwareTrait;
use Aegir\Provision\Console\Config; use Aegir\Provision\Console\Config;
use Consolidation\AnnotatedCommand\CommandFileDiscovery; use Consolidation\AnnotatedCommand\CommandFileDiscovery;
use Drupal\Console\Core\Style\DrupalStyle; use Drupal\Console\Core\Style\DrupalStyle;
use Psr\Log\LoggerInterface;
use Robo\Common\BuilderAwareTrait; use Robo\Common\BuilderAwareTrait;
use Robo\Contract\BuilderAwareInterface; use Robo\Contract\BuilderAwareInterface;
use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\Processor; use Symfony\Component\Config\Definition\Processor;
use Symfony\Component\Console\Exception\InvalidOptionException;
use Symfony\Component\Filesystem\Exception\IOException; use Symfony\Component\Filesystem\Exception\IOException;
use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Yaml\Dumper; use Symfony\Component\Yaml\Dumper;
...@@ -30,6 +31,7 @@ class Context implements BuilderAwareInterface ...@@ -30,6 +31,7 @@ class Context implements BuilderAwareInterface
{ {
use BuilderAwareTrait; use BuilderAwareTrait;
use ProvisionAwareTrait;
/** /**
* @var string * @var string
...@@ -64,33 +66,25 @@ class Context implements BuilderAwareInterface ...@@ -64,33 +66,25 @@ class Context implements BuilderAwareInterface
* init(), set defaults with setProperty(). * init(), set defaults with setProperty().
*/ */
protected $properties = []; protected $properties = [];
/**
* @var \Aegir\Provision\Application;
*/
public $application;
/**
* @var LoggerInterface
*/
public $logger;
/** /**
* Context constructor. * Context constructor.
* *
* @param $name * @param $name
* @param array $options * @param array $options
*/ */
function __construct($name, Application $application = NULL, $options = []) function __construct(
$name,
Provision $provision,
$options = [])
{ {
$this->name = $name; $this->name = $name;
$this->application = $application;
$this->setProvision($provision);
$this->setBuilder($this->getProvision()->getBuilder());
$this->loadContextConfig($options); $this->loadContextConfig($options);
$this->prepareServices(); $this->prepareServices();
if ($this->application) {
$this->setBuilder($this->application->provision->getBuilder());
}
} }
/** /**
...@@ -102,8 +96,8 @@ class Context implements BuilderAwareInterface ...@@ -102,8 +96,8 @@ class Context implements BuilderAwareInterface
*/ */
private function loadContextConfig($options = []) { private function loadContextConfig($options = []) {
if ($this->application) { if ($this->getProvision()) {
$this->config_path = $this->application->getConfig()->get('config_path') . '/provision/' . $this->type . '.' . $this->name . '.yml'; $this->config_path = $this->getProvision()->getConfig()->get('config_path') . '/provision/' . $this->type . '.' . $this->name . '.yml';
} }
else { else {
$config = new Config(); $config = new Config();
...@@ -123,14 +117,16 @@ class Context implements BuilderAwareInterface ...@@ -123,14 +117,16 @@ class Context implements BuilderAwareInterface
$this->properties[$option] = $options[$option]; $this->properties[$option] = $options[$option];
} }
} }
$this->properties['type'] = $this->type;
$this->properties['name'] = $this->name;
$configs[] = $this->properties; $configs[] = $this->properties;
$this->properties['context_type'] = $this->type;
$this->config = $processor->processConfiguration($this, $configs); $this->config = $processor->processConfiguration($this, $configs);
} catch (\Exception $e) { } catch (\Exception $e) {
throw new \Exception( throw new InvalidOptionException(
strtr("There is an error with the configuration for !type '!name'. Check the file !file and try again. \n \nError: !message", [ strtr("There is an error with the configuration for !type '!name'. Check the file !file and try again. \n \nError: !message", [
'!type' => $this->type, '!type' => $this->type,
'!name' => $this->name, '!name' => $this->name,
...@@ -277,6 +273,11 @@ class Context implements BuilderAwareInterface ...@@ -277,6 +273,11 @@ class Context implements BuilderAwareInterface
->children() ->children()
->scalarNode('name') ->scalarNode('name')
->defaultValue($this->name) ->defaultValue($this->name)
->isRequired()
->end()
->scalarNode('type')
->defaultValue($this->type)
->isRequired()
->end() ->end()
->end(); ->end();
...@@ -301,6 +302,7 @@ class Context implements BuilderAwareInterface ...@@ -301,6 +302,7 @@ class Context implements BuilderAwareInterface
->node($property, 'context') ->node($property, 'context')
->isRequired() ->isRequired()
->attribute('context_type', $type) ->attribute('context_type', $type)
->attribute('provision', $this->getProvision())
->end() ->end()
->end(); ->end();
} }
...@@ -331,7 +333,7 @@ class Context implements BuilderAwareInterface ...@@ -331,7 +333,7 @@ class Context implements BuilderAwareInterface
// If type is empty, it's because it's in the ServerContext // If type is empty, it's because it's in the ServerContext
if (empty($info['type'])) { if (empty($info['type'])) {
$server = Application::getContext($info['server']); $server = $this->getProvision()->getContext($info['server']);
$service_type = ucfirst($server->getService($service)->type); $service_type = ucfirst($server->getService($service)->type);
} }
else { else {
...@@ -474,32 +476,33 @@ class Context implements BuilderAwareInterface ...@@ -474,32 +476,33 @@ class Context implements BuilderAwareInterface
* *
* If this context is a Service Subscriber, the provider service will be verified first. * If this context is a Service Subscriber, the provider service will be verified first.
*/ */
public function verify() { public function verifyCommand() {
$return_codes = []; $return_codes = [];
// Run verify method on all services. // Run verify method on all services.
foreach ($this->getServices() as $type => $service) { foreach ($this->getServices() as $type => $service) {
$friendlyName = $service->getFriendlyName(); $return_codes[] = $service->verify() ? 0 : 1;
if ($this->isProvider()) {
$this->application->io->section("Verify service: {$friendlyName}");
foreach ($service->verify() as $type => $verify_success) {
$return_codes[] = $verify_success? 0: 1;
}
}
else {
$this->application->io->section("Verify service: {$friendlyName} on {$service->provider->name}");
// First verify the service provider.
foreach ($service->verifyProvider() as $verify_part => $verify_success) {
$return_codes[] = $verify_success? 0: 1;
}
// Then run "verify" on the subscriptions.
foreach ($this->getSubscription($type)->verify() as $type => $verify_success) {
$return_codes[] = $verify_success? 0: 1;
}
}
} }
//
// if ($this->isProvider()) {
// $this->getProvision()->io()->section("Verify service: {$friendlyName}");
// foreach ($service->verify() as $type => $verify_success) {
// $return_codes[] = $verify_success? 0: 1;
// }
// }
// else {
// $this->getProvision()->io()->section("Verify service: {$friendlyName} on {$service->provider->name}");
//
// // First verify the service provider.
// foreach ($service->verifyProvider() as $verify_part => $verify_success) {
// $return_codes[] = $verify_success? 0: 1;
// }
//
// // Then run "verify" on the subscriptions.
// foreach ($this->getSubscription($type)->verify() as $type => $verify_success) {
// $return_codes[] = $verify_success? 0: 1;
// }
// }
// }
// If any service verify failed, exit with a non-zero code. // If any service verify failed, exit with a non-zero code.
if (count(array_filter($return_codes))) { if (count(array_filter($return_codes))) {
......
...@@ -4,6 +4,7 @@ namespace Aegir\Provision\Context; ...@@ -4,6 +4,7 @@ namespace Aegir\Provision\Context;
use Aegir\Provision\Application; use Aegir\Provision\Application;
use Aegir\Provision\ContextSubscriber; use Aegir\Provision\ContextSubscriber;
use Aegir\Provision\Provision;
use Symfony\Component\Config\Definition\ConfigurationInterface; use Symfony\Component\Config\Definition\ConfigurationInterface;
/** /**
...@@ -30,12 +31,15 @@ class PlatformContext extends ContextSubscriber implements ConfigurationInterfac ...@@ -30,12 +31,15 @@ class PlatformContext extends ContextSubscriber implements ConfigurationInterfac
* PlatformContext constructor. * PlatformContext constructor.
* *
* @param $name * @param $name
* @param Application $application * @param Provision $provision
* @param array $options * @param array $options
*/ */
function __construct($name, Application $application = NULL, array $options = []) function __construct(
{ $name,
parent::__construct($name, $application, $options); Provision $provision = NULL,
$options = []
) {
parent::__construct($name, $provision, $options);
// Load "web_server" context. // Load "web_server" context.
// There is no need to validate for $this->properties['web_server'] because the config system does that. // There is no need to validate for $this->properties['web_server'] because the config system does that.
...@@ -71,8 +75,15 @@ class PlatformContext extends ContextSubscriber implements ConfigurationInterfac ...@@ -71,8 +75,15 @@ class PlatformContext extends ContextSubscriber implements ConfigurationInterfac
*/ */
public function verify() public function verify()
{ {
$this->application->io->customLite($this->getProperty('root'), 'Root: ', 'info'); $this->getProvision()->io()->customLite($this->getProperty('root'), 'Root: ', 'info');
$this->application->io->customLite($this->config_path, 'Configuration File: ', 'info'); $this->getProvision()->io()->customLite($this->config_path, 'Configuration File: ', 'info');
// This is how you can use Robo Tasks in a platform verification call.
// The "silent" method actually works here.
// It only partially works in Service::restartServices()!
$this->getBuilder()->taskExec('env')
->silent(!$this->getProvision()->io()->isVerbose())
->run();
return parent::verify(); return parent::verify();
} }
......
...@@ -3,7 +3,9 @@ ...@@ -3,7 +3,9 @@
namespace Aegir\Provision\Context; namespace Aegir\Provision\Context;
use Aegir\Provision\Application; use Aegir\Provision\Application;
use Aegir\Provision\Context;
use Aegir\Provision\ContextSubscriber; use Aegir\Provision\ContextSubscriber;
use Aegir\Provision\Provision;
use Symfony\Component\Config\Definition\ConfigurationInterface; use Symfony\Component\Config\Definition\ConfigurationInterface;
/** /**
...@@ -34,17 +36,20 @@ class SiteContext extends ContextSubscriber implements ConfigurationInterface ...@@ -34,17 +36,20 @@ class SiteContext extends ContextSubscriber implements ConfigurationInterface
* @param Application $application * @param Application $application
* @param array $options * @param array $options
*/ */
function __construct($name, Application $application = NULL, array $options = []) function __construct(
{ $name,
parent::__construct($name, $application, $options); Provision $provision = NULL,
$options = []
) {
parent::__construct($name, $provision, $options);
// Load "web_server" and "platform" contexts. // Load "web_server" and "platform" contexts.
// There is no need to check if the property exists because the config system does that. // There is no need to check if the property exists because the config system does that.
// $this->db_server = $application->getContext($this->properties['db_server']); // $this->db_server = $application->getContext($this->properties['db_server']);
$this->platform = Application::getContext($this->properties['platform'], $application); // Load platform context... @TODO: Automatically do this for required contexts?
$this->platform = $this->getProvision()->getContext($this->properties['platform']);
// Add platform http service subscription. // Add platform http service subscription.
$this->serviceSubscriptions['http'] = $this->platform->getSubscription('http'); $this->serviceSubscriptions['http'] = $this->platform->getSubscription('http');
$this->serviceSubscriptions['http']->context = $this; $this->serviceSubscriptions['http']->context = $this;
...@@ -82,9 +87,9 @@ class SiteContext extends ContextSubscriber implements ConfigurationInterface ...@@ -82,9 +87,9 @@ class SiteContext extends ContextSubscriber implements ConfigurationInterface
*/ */
public function verify() public function verify()
{ {
$this->application->io->customLite($this->getProperty('uri'), 'Site URL: ', 'info'); $this->getProvision()->io()->customLite($this->getProperty('uri'), 'Site URL: ', 'info');
$this->application->io->customLite($this->platform->getProperty('root'), 'Root: ', 'info'); $this->getProvision()->io()->customLite($this->platform->getProperty('root'), 'Root: ', 'info');
$this->application->io->customLite($this->config_path, 'Configuration File: ', 'info'); $this->getProvision()->io()->customLite($this->config_path, 'Configuration File: ', 'info');
return parent::verify(); return parent::verify();
} }
......
...@@ -93,6 +93,7 @@ class ContextSubscriber extends Context ...@@ -93,6 +93,7 @@ class ContextSubscriber extends Context
->node('server', 'context') ->node('server', 'context')
->isRequired() ->isRequired()
->attribute('context_type', 'server') ->attribute('context_type', 'server')
->attribute('provision', $this->getProvision())
->end() ->end()
->append($this->addServiceProperties('service_subscriptions')) ->append($this->addServiceProperties('service_subscriptions'))
->end() ->end()
......
...@@ -6,14 +6,17 @@ namespace Aegir\Provision; ...@@ -6,14 +6,17 @@ namespace Aegir\Provision;
use Aegir\Provision\Console\Config; use Aegir\Provision\Console\Config;
use Aegir\Provision\Commands\ExampleCommands; use Aegir\Provision\Commands\ExampleCommands;
use Aegir\Provision\Console\ConsoleOutput;
use Aegir\Provision\Robo\ProvisionCollectionBuilder;
use Aegir\Provision\Robo\ProvisionExecutor; use Aegir\Provision\Robo\ProvisionExecutor;
use Aegir\Provision\Robo\ProvisionTasks; use Aegir\Provision\Robo\ProvisionTasks;
use Drupal\Console\Core\Style\DrupalStyle;
use League\Container\Container; use League\Container\Container;
use League\Container\ContainerAwareInterface; use League\Container\ContainerAwareInterface;
use League\Container\ContainerAwareTrait; use League\Container\ContainerAwareTrait;
use Psr\Log\LoggerAwareInterface; use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait; use Psr\Log\LoggerAwareTrait;
use Robo\Collection\CollectionBuilder; use Psr\Log\LogLevel;
use Robo\Common\BuilderAwareTrait; use Robo\Common\BuilderAwareTrait;
use Robo\Common\ConfigAwareTrait; use Robo\Common\ConfigAwareTrait;
use Robo\Common\IO; use Robo\Common\IO;
...@@ -22,9 +25,21 @@ use Robo\Contract\ConfigAwareInterface; ...@@ -22,9 +25,21 @@ use Robo\Contract\ConfigAwareInterface;
use Robo\Contract\IOAwareInterface; use Robo\Contract\IOAwareInterface;
use Robo\Robo; use Robo\Robo;
use Robo\Runner as RoboRunner; use Robo\Runner as RoboRunner;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Logger\ConsoleLogger;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
/**
* 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 { class Provision implements ConfigAwareInterface, ContainerAwareInterface, LoggerAwareInterface, IOAwareInterface, BuilderAwareInterface {
const APPLICATION_NAME = 'Aegir Provision'; const APPLICATION_NAME = 'Aegir Provision';
...@@ -47,6 +62,16 @@ class Provision implements ConfigAwareInterface, ContainerAwareInterface, Logger ...@@ -47,6 +62,16 @@ class Provision implements ConfigAwareInterface, ContainerAwareInterface, Logger
*/ */
private $commands = []; private $commands = [];
/**
* @var \Aegir\Provision\Context[]
*/
private $contexts = [];
/**
* @var array[]
*/
private $context_files = [];
/** /**
* Provision constructor. * Provision constructor.
* *
...@@ -59,19 +84,28 @@ class Provision implements ConfigAwareInterface, ContainerAwareInterface, Logger ...@@ -59,19 +84,28 @@ class Provision implements ConfigAwareInterface, ContainerAwareInterface, Logger
InputInterface $input = NULL, InputInterface $input = NULL,
OutputInterface $output = NULL OutputInterface $output = NULL
) { ) {
$this->setConfig($config);
$this->setInput($input); $logger = new ConsoleLogger($output);
$this->setOutput($output);
$this
->setConfig($config)
;
// Create Application. // Create Application.
$application = new Application(self::APPLICATION_NAME, self::VERSION, $this); $application = new Application(self::APPLICATION_NAME, self::VERSION);
// $application->setConfig($consoleConfig); $application
->setProvision($this)
->setLogger($logger)
;
$application->configureIO($input, $output);
$this->setInput($input);
$this->setOutput($output);
// Create and configure container. // Create and configure container.
$container = Robo::createDefaultContainer($input, $output, $application, $config); $container = Robo::createDefaultContainer($input, $output, $application, $config);
$this->setContainer($container); $this->setContainer($container);
$this->configureContainer($container); $this->configureContainer($container);
// Instantiate Robo Runner. // Instantiate Robo Runner.
$this->runner = new RoboRunner([ $this->runner = new RoboRunner([
ExampleCommands::class ExampleCommands::class
...@@ -81,16 +115,57 @@ class Provision implements ConfigAwareInterface, ContainerAwareInterface, Logger ...@@ -81,16 +115,57 @@ class Provision implements ConfigAwareInterface, ContainerAwareInterface, Logger
$this->runner->setSelfUpdateRepository(self::REPOSITORY); $this->runner->setSelfUpdateRepository(self::REPOSITORY);
$this->setBuilder($container->get('builder')); $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) { public function run(InputInterface $input, OutputInterface $output) {
$status_code = $this->runner->run($input, $output); $status_code = $this->runner->run($input, $output);
return $status_code; return $status_code;
} }
/** /**
* Register the necessary classes for BLT. * Register the necessary classes for Provision.
*/ */
public function configureContainer(Container $container) { public function configureContainer(Container $container) {
...@@ -100,7 +175,7 @@ class Provision implements ConfigAwareInterface, ContainerAwareInterface, Logger ...@@ -100,7 +175,7 @@ class Provision implements ConfigAwareInterface, ContainerAwareInterface, Logger
// in the container. "collectionBuilder" used for the actual command that // in the container. "collectionBuilder" used for the actual command that
// was executed, and "builder" to be used with non-command classes. // was executed, and "builder" to be used with non-command classes.
$tasks = new ProvisionTasks(); $tasks = new ProvisionTasks();
$builder = new CollectionBuilder($tasks); $builder = new ProvisionCollectionBuilder($tasks);
$tasks->setBuilder($builder); $tasks->setBuilder($builder);
$container->add('builder', $builder); $container->add('builder', $builder);
$container->add('executor', ProvisionExecutor::class) $container->add('executor', ProvisionExecutor::class)
...@@ -116,4 +191,192 @@ class Provision implements ConfigAwareInterface, ContainerAwareInterface, Logger ...@@ -116,4 +191,192 @@ class Provision implements ConfigAwareInterface, ContainerAwareInterface, Logger
{ {
return $this->output(); 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
...@@ -8,9 +8,20 @@ ...@@ -8,9 +8,20 @@
namespace Aegir\Provision; 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 $type;
public $properties; public $properties;
...@@ -23,11 +34,6 @@ class Service ...@@ -23,11 +34,6 @@ class Service
*/ */
public $provider; public $provider;
/**
* @var \Aegir\Provision\Application;
*/
public $application;
/** /**
* @var string * @var string
* The machine name of the service. ie. http, db * The machine name of the service. ie. http, db
...@@ -40,12 +46,18 @@ class Service ...@@ -40,12 +46,18 @@ class Service
*/ */
const SERVICE_NAME = 'Service Name'; const SERVICE_NAME = 'Service Name';
function __construct($service_config, $provider_context) function __construct($service_config, Context $provider_context)
{ {
$this->provider = $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->type = $service_config['type'];
$this->properties = $service_config['properties']; $this->properties = $service_config['properties'];
if ($provider_context->getBuilder()) {
$this->setBuilder($provider_context->getBuilder());
}
} }
/** /**
...@@ -70,30 +82,54 @@ class Service ...@@ -70,30 +82,54 @@ class Service
return "\Aegir\Provision\Service\\{$service}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. * React to the verify command. Passes off to the method verifySite, verifyServer, verifyPlatform.
* * @return mixed
* This is used to allow skipping of the service restart.
*/ */
function verifyProvider() public function verify() {
{ $method = 'verify' . ucfirst($this->getContext()->type);
return [ $this->getProvision()->getLogger()->info("Running {method}", [
'configuration' => $this->writeConfigurations(), '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". * Run the services "restart_command".
* @return bool * @return bool
...@@ -103,14 +139,19 @@ class Service ...@@ -103,14 +139,19 @@ class Service
return TRUE; return TRUE;
} }
else { else {
try { $task = $this->getProvision()->getBuilder()->taskExec($this->properties['restart_command'])
$this->application->provision->getOutput()->exec($this->properties['restart_command'], 'Restarting service...'); ->silent(!$this->getProvision()->io()->isVerbose())
$this->application->io->successLite('Service restarted.'); ;
/** @var \Robo\Result $result */
$result = $task->run();
if ($result->wasSuccessful()) {
$this->getProvision()->io()->successLite('Service restarted.');
sleep(1); sleep(1);
return TRUE; return TRUE;
} }
catch (\Exception $e) { else {
$this->application->io->errorLite('Unable to restart service: ' . $e->getMessage()); $this->getProvision()->io()->errorLite('Unable to restart service:' . $result->getOutputData());
} }
} }
return FALSE; return FALSE;
...@@ -162,12 +203,12 @@ class Service ...@@ -162,12 +203,12 @@ class Service
try { try {
$config = new $configuration_class($context, $this); $config = new $configuration_class($context, $this);
$config->write(); $config->write();
$context->application->io->successLite( $context->getProvision()->io()->successLite(
'Wrote '.$config->description.' to '.$config->filename() 'Wrote '.$config->description.' to '.$config->filename()
); );
} }
catch (\Exception $e) { catch (\Exception $e) {
$context->application->io->errorLite( $context->getProvision()->io()->errorLite(
'Unable to write '.$config->description.' to '.$config->filename() . ': ' . $e->getMessage() 'Unable to write '.$config->description.' to '.$config->filename() . ': ' . $e->getMessage()
); );
$success = FALSE; $success = FALSE;
...@@ -265,4 +306,12 @@ class Service ...@@ -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 ...@@ -34,7 +34,7 @@ class DbMysqlService extends DbService
if ($this->database_exists($test)) { if ($this->database_exists($test)) {
if (!$this->drop_database($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; return TRUE;
} }
......
...@@ -12,6 +12,7 @@ namespace Aegir\Provision\Service; ...@@ -12,6 +12,7 @@ namespace Aegir\Provision\Service;
use Aegir\Provision\Context\SiteContext; use Aegir\Provision\Context\SiteContext;
use Aegir\Provision\Service; use Aegir\Provision\Service;
use Aegir\Provision\ServiceInterface;
use Aegir\Provision\ServiceSubscription; use Aegir\Provision\ServiceSubscription;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
...@@ -20,7 +21,7 @@ use Symfony\Component\Console\Output\OutputInterface; ...@@ -20,7 +21,7 @@ use Symfony\Component\Console\Output\OutputInterface;
* *
* @package Aegir\Provision\Service * @package Aegir\Provision\Service
*/ */
class DbService extends Service class DbService extends Service implements ServiceInterface
{ {
const SERVICE = 'db'; const SERVICE = 'db';
...@@ -89,7 +90,7 @@ class DbService extends Service ...@@ -89,7 +90,7 @@ class DbService extends Service
/** /**
* React to the `provision verify` command on Server contexts * React to the `provision verify` command on Server contexts
*/ */
function verify() { function verifyServer() {
$this->creds = array_map('urldecode', parse_url($this->properties['master_db'])); $this->creds = array_map('urldecode', parse_url($this->properties['master_db']));
if (!isset($this->creds['port'])) { if (!isset($this->creds['port'])) {
...@@ -105,18 +106,18 @@ class DbService extends Service ...@@ -105,18 +106,18 @@ class DbService extends Service
try { try {
$this->connect(); $this->connect();
$return = TRUE; $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()) { 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 { } 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; $return = FALSE;
} }
if ($this->can_grant_privileges()) { 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 { } 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; $return = FALSE;
} }
...@@ -125,7 +126,7 @@ class DbService extends Service ...@@ -125,7 +126,7 @@ class DbService extends Service
]; ];
} }
catch (\PDOException $e) { catch (\PDOException $e) {
$this->provider->application->io->errorLite($e->getMessage()); $this->provider->getProvision()->io()->errorLite($e->getMessage());
return [ return [
'service' => FALSE 'service' => FALSE
]; ];
...@@ -135,11 +136,11 @@ class DbService extends Service ...@@ -135,11 +136,11 @@ class DbService extends Service
/** /**
* React to the `provision verify` command on subscriber contexts (sites and platforms) * React to the `provision verify` command on subscriber contexts (sites and platforms)
*/ */
function verifySubscription(ServiceSubscription $serviceSubscription) { function verifySite() {
$this->subscription = $serviceSubscription; $this->subscription = $this->getContext()->getSubscription($this->type);
// Check for database // 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'])); $this->creds_root = array_map('urldecode', parse_url($this->properties['master_db']));
...@@ -156,19 +157,23 @@ class DbService extends Service ...@@ -156,19 +157,23 @@ class DbService extends Service
try { try {
$this->connect(); $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 [ return [
'service' => TRUE 'service' => TRUE
]; ];
} }
catch (\PDOException $e) { catch (\PDOException $e) {
$this->subscription->context->application->io->errorLite($e->getMessage()); $this->subscription->context->getProvision()->io()->errorLite($e->getMessage());
return [ return [
'service' => FALSE 'service' => FALSE
]; ];
} }
} }
public function verifyPlatform() {
}
/** /**
* React to `provision verify` command when run on a subscriber, to verify the service's provider. * React to `provision verify` command when run on a subscriber, to verify the service's provider.
* *
...@@ -215,11 +220,11 @@ class DbService extends Service ...@@ -215,11 +220,11 @@ class DbService extends Service
$query = preg_replace_callback($this::PROVISION_QUERY_REGEXP, array($this, 'query_callback'), $query); $query = preg_replace_callback($this::PROVISION_QUERY_REGEXP, array($this, 'query_callback'), $query);
try { try {
$this->provider->application->logger->notice("Running Query: {$query}"); $this->provider->getProvision()->getLogger()->info("Running Query: {$query}");
$result = $this->conn->query($query); $result = $this->conn->query($query);
} }
catch (\PDOException $e) { catch (\PDOException $e) {
$this->provider->application->io->errorLite($e->getMessage()); $this->provider->getProvision()->getLogger()->error($e->getMessage());
return FALSE; return FALSE;
} }
...@@ -381,13 +386,13 @@ class DbService extends Service ...@@ -381,13 +386,13 @@ class DbService extends Service
} }
if ($this->database_exists($db_name)) { 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 '@name' => $db_name
])); ]));
} }
else { else {
$this->create_database($db_name); $this->create_database($db_name);
$this->application->io->successLite(strtr("Created database '@name'.", [ $this->getProvision()->io()->successLite(strtr("Created database '@name'.", [
'@name' => $db_name, '@name' => $db_name,
])); ]));
} }
...@@ -398,7 +403,7 @@ class DbService extends Service ...@@ -398,7 +403,7 @@ class DbService extends Service
'@user' => $db_user '@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, '@user' => $db_user,
'@host' => $db_grant_host, '@host' => $db_grant_host,
'@name' => $db_name, '@name' => $db_name,
...@@ -408,7 +413,7 @@ class DbService extends Service ...@@ -408,7 +413,7 @@ class DbService extends Service
$status = $this->database_exists($db_name); $status = $this->database_exists($db_name);
if ($status) { 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, '@name' => $site->name,
])); ]));
} }
......
...@@ -21,7 +21,7 @@ class PlatformConfiguration extends Configuration { ...@@ -21,7 +21,7 @@ class PlatformConfiguration extends Configuration {
function filename() { function filename() {
$file = $this->context->name . '.conf'; $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() function process()
......