diff --git a/bin/provision.php b/bin/provision.php index 39dbc026c0327990b956b0e0e9595e867ec15f5d..1a0f3aaa4fe92d872f05142c0ffc133a3250f83d 100644 --- a/bin/provision.php +++ b/bin/provision.php @@ -54,7 +54,7 @@ if ($debug){ } try { - $application = new Application(); + $application = new Application($input, $output); $application->run(); } catch (\Exception $e) { diff --git a/composer.json b/composer.json index c3f078dfb123b45896ce8e8302dc7862cf3400ba..2d63c44d9a0c66e6af6e6c9ae40aaa462467e277 100644 --- a/composer.json +++ b/composer.json @@ -8,6 +8,7 @@ "require": { "consolidation/annotated-command": "~2", "drupal/console-core": "1.0.2", + "psr/log": "^1.0", "psy/psysh": "^0.8.11", "symfony/console": "^3.3", "symfony/yaml": "^3.3" diff --git a/composer.lock b/composer.lock index 02c47ed0e2979ff6715e472212989c0cb93e5959..fe257338cfcd75d41345b3a63ac9f50d91986986 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "c8f06b1cc28f9638bdc92415ed78c328", + "content-hash": "25f4161ac19dde865bcef9fa9d341b03", "packages": [ { "name": "consolidation/annotated-command", diff --git a/src/Application.php b/src/Application.php index 3008df8b9a09cf4fc916ea588bba7a25ca3d4dd4..0d297b35a5321beb27006bb5cb0097a51aab2081 100644 --- a/src/Application.php +++ b/src/Application.php @@ -8,11 +8,14 @@ use Aegir\Provision\Command\ShellCommand; use Aegir\Provision\Command\StatusCommand; use Aegir\Provision\Command\VerifyCommand; use Aegir\Provision\Console\Config; +use Psr\Log\LoggerInterface; +use Psr\Log\LogLevel; use Symfony\Component\Console\Command\HelpCommand; use Symfony\Component\Console\Command\ListCommand; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Logger\ConsoleLogger; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Application as BaseApplication; @@ -49,10 +52,26 @@ class Application extends BaseApplication * @var string */ const DEFAULT_TIMEZONE = 'America/New_York'; - - public function __construct() + + /** + * @var LoggerInterface + */ + public $logger; + + /** + * Application constructor. + * + * @param \Symfony\Component\Console\Input\InputInterface $input + * @param \Symfony\Component\Console\Output\OutputInterface $output + * + * @throws \Exception + */ + public function __construct(InputInterface $input, OutputInterface $output) { - + $this->logger = new ConsoleLogger($output, + [LogLevel::INFO => OutputInterface::VERBOSITY_NORMAL] + ); + // If no timezone is set, set Default. if (empty(ini_get('date.timezone'))) { date_default_timezone_set($this::DEFAULT_TIMEZONE); @@ -105,7 +124,7 @@ class Application extends BaseApplication $commands[] = new ListCommand(); $commands[] = new SaveCommand(); $commands[] = new ServicesCommand(); - $commands[] = new ShellCommand(); +// $commands[] = new ShellCommand(); $commands[] = new StatusCommand(); $commands[] = new VerifyCommand(); @@ -137,16 +156,22 @@ class Application extends BaseApplication * * @return array */ - function getAllContexts() { + function getAllContexts($name = '') { $contexts = []; $finder = new \Symfony\Component\Finder\Finder(); - $finder->files()->name('*.yml')->in($this->config->get('config_path') . '/provision'); + $finder->files()->name('*' . $name . '.yml')->in($this->config->get('config_path') . '/provision'); foreach ($finder as $file) { list($context_type, $context_name) = explode('.', $file->getFilename()); $class = '\Aegir\Provision\Context\\' . ucfirst($context_type) . "Context"; - $contexts[$context_name] = new $class($context_name, $this->config->all()); + $contexts[$context_name] = new $class($context_name, $this->config->all(), $this); + } + + if ($name) { + return $contexts[$name]; + } + else { + return $contexts; } - return $contexts; } /** @@ -158,9 +183,50 @@ class Application extends BaseApplication * @throws \Exception */ function getContext($name) { - if (empty($this->getAllContexts()[$name])) { + if (empty($this->getAllContexts($name))) { throw new \Exception('Context not found with name: ' . $name); } - return $this->getAllContexts()[$name]; + return $this->getAllContexts($name); + } + + + /** + * Load all server contexts. + * + * @param null $service + * @return mixed + * @throws \Exception + */ + public function getAllServers($service = NULL) { + $servers = []; + $contexts = $this->getAllContexts(); + if (empty($contexts)) { + throw new \Exception('No contexts found. Use `provision save` to create one.'); + } + + foreach ($contexts as $name => &$context) { + if ($context->type == 'server') { + $servers[$name] = $context; + } + } + return $servers; + } + + /** + * 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; } } diff --git a/src/Command.php b/src/Command.php index 9ced4f1a492bd607899c82088b265647507c9a16..4cf83fcff693d187143b15c745acef480bdfdfe3 100644 --- a/src/Command.php +++ b/src/Command.php @@ -3,9 +3,11 @@ namespace Aegir\Provision; use Drupal\Console\Core\Style\DrupalStyle; +use Psr\Log\LogLevel; use Symfony\Component\Console\Command\Command as BaseCommand; use Drupal\Console\Core\Command\Shared\CommandTrait; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Logger\ConsoleLogger; use Symfony\Component\Console\Output\OutputInterface; /** diff --git a/src/Command/SaveCommand.php b/src/Command/SaveCommand.php index 0ba0aa33f395b085a6367b2ea02e960e82ca5260..bbff3242f0039921f48fde7174458ef476f894d0 100644 --- a/src/Command/SaveCommand.php +++ b/src/Command/SaveCommand.php @@ -68,7 +68,7 @@ class SaveCommand extends Command ); // Load all Aegir\Provision\Context and inject their options. - // @TODO: Figure out a way to do discovery to include all classes that inherit Aegir\Provision\Context + // @TODO: Use CommandFileDiscovery to include all classes that inherit Aegir\Provision\Context $contexts[] = SiteContext::option_documentation(); $contexts[] = PlatformContext::option_documentation(); $contexts[] = ServerContext::option_documentation(); @@ -113,7 +113,7 @@ class SaveCommand extends Command } $properties = $this->askForContextProperties(); $class = Context::getClassName($this->input->getOption('context_type')); - $this->context = new $class($input->getArgument('context_name'), $this->getApplication()->getConfig()->all(), $properties); + $this->context = new $class($input->getArgument('context_name'), $this->getApplication()->getConfig()->all(), $this->getApplication(), $properties); } // Delete context config. @@ -144,11 +144,6 @@ class SaveCommand extends Command $this->io->error("Unable to save configuration to {$this->context->config_path}. "); } } - - $output->writeln( - "Context Object: ".print_r($this->context,1) - ); - // $command = 'drush provision-save '.$input->getArgument('context_name'); // $this->process($command); } diff --git a/src/Command/ServicesCommand.php b/src/Command/ServicesCommand.php index 89baac15f2cefdb92f214301ff29e4b5ecd2d5ef..a0a369989effcb0771248242e3042aba7bf409cb 100644 --- a/src/Command/ServicesCommand.php +++ b/src/Command/ServicesCommand.php @@ -8,6 +8,7 @@ use Aegir\Provision\Context\PlatformContext; use Aegir\Provision\Context\ServerContext; use Aegir\Provision\Context\SiteContext; use Consolidation\AnnotatedCommand\CommandFileDiscovery; +use Symfony\Component\Console\Exception\LogicException; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputDefinition; @@ -81,9 +82,9 @@ class ServicesCommand extends Command $input, $output ); - if (isset($this->context->type) && $this->context->type != 'server') { - throw new \Exception('Context must be a server.'); - } +// if (isset($this->context->type) && $this->context->type != 'server') { +// throw new \Exception('Context must be a server.'); +// } $this->sub_command = $input->getArgument('sub_command'); } @@ -120,19 +121,50 @@ class ServicesCommand extends Command $this->io->comment("Add Services"); $service = $this->io->choice('Which service?', $this->context->getServiceOptions()); - // Then ask which service type - $service_type = $this->io->choice('Which service type?', $this->context->getServiceTypeOptions($service)); - // Then ask for all options. - $properties = $this->askForServiceProperties($service); + // If server, ask which service type. + if ($this->context->type == 'server') { + if (empty($this->context->getServiceTypeOptions($service))) { + throw new \Exception("There was no class found for service $service. Create one named \\Aegir\\Provision\\Service\\{$service}Service"); + } + + $service_type = $this->io->choice('Which service type?', $this->context->getServiceTypeOptions($service)); + + // Then ask for all options. + $properties = $this->askForServiceProperties($service); - $this->io->info("Adding $service service $service_type..."); + $this->io->info("Adding $service service $service_type..."); - try { - $this->context->config['services'][$service] = [ + $services_key = 'services'; + $service_info = [ 'type' => $service_type, - 'properties' => $properties, ]; + } + // All other context types are associating with servers that provide the service. + else { + if (empty($this->getApplication()->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."); + } + + $server = $this->io->choice('Which server?', $this->getApplication()->getServerOptions($service)); + + // Then ask for all options. + $server_context = $this->getApplication()->getContext($server); + $properties = $this->askForServiceProperties($service); + + $this->io->info("Using $service service from server $server..."); + + $services_key = 'service_subscriptions'; + $service_info = [ + 'server' => $server, + ]; + } + + try { + $this->context->config[$services_key][$service] = $service_info; + if (!empty($properties)) { + $this->context->config[$services_key][$service]['properties'] = $properties; + } $this->context->save(); $this->io->success('Service saved to Context!'); } @@ -149,8 +181,9 @@ class ServicesCommand extends Command private function askForServiceProperties($service) { $class = $this->context->getAvailableServices($service); + $method = "{$this->context->type}_options"; - $options = $class::option_documentation(); + $options = $class::{$method}(); $properties = []; foreach ($options as $name => $description) { // If option does not exist, ask for it. diff --git a/src/Context.php b/src/Context.php index 7237efbef911b65a5cb5797815d70158c14711d1..282d6d145e233159c4fdf73839b42b75d533aced 100644 --- a/src/Context.php +++ b/src/Context.php @@ -6,6 +6,9 @@ namespace Aegir\Provision; +use Consolidation\AnnotatedCommand\CommandFileDiscovery; +use Drupal\Console\Core\Style\DrupalStyle; +use Psr\Log\LoggerInterface; use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\Processor; use Symfony\Component\Filesystem\Exception\IOException; @@ -49,6 +52,22 @@ class Context */ protected $properties = []; + /** + * @var array + * A list of services associated with this context. + */ + protected $services = []; + + /** + * @var \Aegir\Provision\Application; + */ + public $application; + + /** + * @var LoggerInterface + */ + public $logger; + /** * Context constructor. * @@ -56,10 +75,12 @@ class Context * @param $console_config * @param array $options */ - function __construct($name, $console_config, $options = []) + function __construct($name, $console_config, Application $application, $options = []) { $this->name = $name; + $this->application = $application; $this->loadContextConfig($console_config, $options); + $this->prepareServices(); } /** @@ -99,13 +120,142 @@ class Context } } + /** + * Load Service classes from config into Context.. + */ + protected function prepareServices() { + if (isset($this->config['services'])) { + + foreach ($this->config['services'] as $service_name => $service) { + $service_name = ucfirst($service_name); + $service_type = ucfirst($service['type']); + $service_class = "\\Aegir\\Provision\\Service\\{$service_name}\\{$service_name}{$service_type}Service"; + $this->services[strtolower($service_name)] = new $service_class($service, $this); + } + } + elseif (isset($this->config['service_subscriptions'])) { + foreach ($this->config['service_subscriptions'] as $service_name => $service) { + $this->servers[$service_name] = $server = $this->application->getContext($service['server']); + $this->services[$service_name] = new ServiceSubscription($this, $server, $service_name); + } + } + else { + $this->services = []; + } + } + + /** + * Loads all available \Aegir\Provision\Service classes + * + * @return array + */ + public function getAvailableServices($service = '') { + + // Load all service classes + $classes = []; + $discovery = new CommandFileDiscovery(); + $discovery->setSearchPattern('*Service.php'); + $servicesFiles = $discovery->discover(__DIR__ .'/Service', '\Aegir\Provision\Service'); + foreach ($servicesFiles as $serviceClass) { + // If this is a server, show all services. If it is not, but service allows this type of context, load it. + if ($this->type == 'server' || in_array($this->type, $serviceClass::allowedContexts())) { + $classes[$serviceClass::SERVICE] = $serviceClass; + } + } + + if ($service && isset($classes[$service])) { + return $classes[$service]; + } + elseif ($service && !isset($classes[$service])) { + throw new \Exception("No service with name $service was found."); + } + else { + return $classes; + } + } + + /** + * Lists all available services as a simple service => name array. + * @return array + */ + public function getServiceOptions() { + $options = []; + $services = $this->getAvailableServices(); + foreach ($services as $service => $class) { + $options[$service] = $class::SERVICE_NAME; + } + return $options; + } + + /** + * @return array + */ + protected function getAvailableServiceTypes($service, $service_type = NULL) { + + // Load all service classes + $classes = []; + $discovery = new CommandFileDiscovery(); + $discovery->setSearchPattern(ucfirst($service) . '*Service.php'); + $serviceTypesFiles = $discovery->discover(__DIR__ .'/Service/' . ucfirst($service), '\Aegir\Provision\Service\\' . ucfirst($service)); + foreach ($serviceTypesFiles as $serviceTypeClass) { + $classes[$serviceTypeClass::SERVICE_TYPE] = $serviceTypeClass; + } + + if ($service_type && isset($classes[$service_type])) { + return $classes[$service_type]; + } + elseif ($service_type && !isset($classes[$service_type])) { + throw new \Exception("No service type with name $service_type was found."); + } + else { + return $classes; + } + } + + /** + * Lists all available services as a simple service => name array. + * @return array + */ + public function getServiceTypeOptions($service) { + $options = []; + $service_types = $this->getAvailableServiceTypes($service); + foreach ($service_types as $service_type => $class) { + $options[$service_type] = $class::SERVICE_TYPE_NAME; + } + return $options; + } + + + /** + * Return all services for this context. + * + * @return array + */ + public function getServices() { + return $this->services; + } + + /** + * Return all services for this context. + * + * @return array + */ + public function getService($type) { + if (isset($this->services[$type])) { + return $this->services[$type]; + } + else { + throw new \Exception("Service '$type' does not exist."); + } + } + /** * {@inheritdoc} */ public function getConfigTreeBuilder() { $tree_builder = new TreeBuilder(); - $root_node = $tree_builder->root('server'); + $root_node = $tree_builder->root($this->type); $root_node ->children() ->scalarNode('name') @@ -113,6 +263,28 @@ class Context ->end() ->end(); + // Load Services + if ($this->type == 'server') { + $services_key = 'services'; + $services_property = 'type'; + } + else { + $services_key = 'service_subscriptions'; + $services_property = 'server'; + } + + $root_node + ->children() + ->arrayNode($services_key) + ->prototype('array') + ->children() + ->scalarNode($services_property) + ->isRequired(true) + ->end() + ->append($this->addServiceProperties($services_key)) + ->end() + ->end(); + // @TODO: Figure out how we can let other classes add to Context properties. foreach ($this->option_documentation() as $name => $description) { $root_node @@ -130,6 +302,77 @@ class Context return $tree_builder; } + /** + * Append Service class options_documentation to config tree. + */ + public function addServiceProperties($property_name = 'services') + { + $builder = new TreeBuilder(); + $node = $builder->root('properties'); + + // Load config tree from Service type classes + if (!empty($this->getProperty($property_name)) && !empty($this->getProperty($property_name))) { + foreach ($this->getProperty($property_name) as $service => $info) { + + // If type is empty, it's because it's in the ServerContext + if (empty($info['type'])) { + $server = $this->application->getContext($info['server']); + $service_type = ucfirst($server->getService($service)->type); + } + else { + $service_type = ucfirst($info['type']); + } + $service = ucfirst($service); + $class = "\Aegir\Provision\Service\\{$service}\\{$service}{$service_type}Service"; + $method = "{$this->type}_options"; + + foreach ($class::{$method}() as $name => $description) { + $node + ->children() + ->scalarNode($name)->end() + ->end() + ->end(); + } + } + } + return $node; + } + + /** + * Output a list of all services for this context. + */ + public function showServices(DrupalStyle $io) { + if (!empty($this->getServices())) { + $is_server = $this->type == 'server'; + $rows = []; + + $headers = $is_server? + ['Services']: + ['Service', 'Server', 'Type']; + + foreach ($this->getServices() as $name => $service) { + if ($is_server) { + $rows[] = [$name, $service->type]; + } + else { + $rows[] = [ + $name, + $service->server->name, + $service->server->getService($name)->type + ]; + } + + // Show all properties. + if (!empty($service->properties )) { + foreach ($service->properties as $name => $value) { + $rows[] = [' ' . $name, $value]; + } + } + } + $io->table($headers, $rows); + } + } + /** * Return all properties for this context. * @@ -194,7 +437,23 @@ class Context return '\Aegir\Provision\Context\\' . ucfirst($type) . "Context"; } +// public function verify() { +// return "Provision Context"; +// } + + /** + * Verify this context. + * + * Running `provision verify CONTEXT` triggers this method. + * + * Collect all services for the context and run the verify() method on them + */ public function verify() { - return "Provision Context"; + + // Run verify method on all services. + foreach ($this->getServices() as $service) { + $service->verify(); + } } + } diff --git a/src/Context/PlatformContext.php b/src/Context/PlatformContext.php index e5f0f03079eb22c6f678c3b17d3c0e330fa047b5..d15abdb8da59d17bfffaf722326187a264b1c0a9 100644 --- a/src/Context/PlatformContext.php +++ b/src/Context/PlatformContext.php @@ -2,7 +2,9 @@ namespace Aegir\Provision\Context; +use Aegir\Provision\Application; use Aegir\Provision\Context; +use Aegir\Provision\Service\Http\Apache\Configuration\PlatformConfiguration; use Symfony\Component\Config\Definition\ConfigurationInterface; /** @@ -18,7 +20,35 @@ class PlatformContext extends Context implements ConfigurationInterface * @var string */ public $type = 'platform'; - + + /** + * @var \Aegir\Provision\Context\ServerContext; + */ + public $web_server; + + /** + * PlatformContext constructor. + * + * @param $name + * @param $console_config + * @param Application $application + * @param array $options + */ + function __construct($name, $console_config, Application $application, array $options = []) + { + parent::__construct($name, $console_config, $application, $options); + + // Load "web_server" context. + if (isset($this->config['web_server'])) { + $this->web_server = $application->getContext($this->config['web_server']); + $this->web_server->logger = $application->logger; + + } + else { + throw new \Exception('No web_server found.'); + } + } + static function option_documentation() { $options = [ @@ -31,4 +61,11 @@ class PlatformContext extends Context implements ConfigurationInterface return $options; } + +// @TODO: Remove. This should be handled by Services now. +// public function verify() { +// parent::verify(); +// $this->logger->info('Verifying Web Server...'); +// $this->web_server->verify(); +// } } diff --git a/src/Context/ServerContext.php b/src/Context/ServerContext.php index 61c5e676192899ca486b9a727ec7469f6b39ac50..07f375b1bc0151da145f9002937d3456e8a2ba8a 100644 --- a/src/Context/ServerContext.php +++ b/src/Context/ServerContext.php @@ -2,6 +2,7 @@ namespace Aegir\Provision\Context; +use Aegir\Provision\Application; use Aegir\Provision\Context; use Consolidation\AnnotatedCommand\CommandFileDiscovery; use Drupal\Console\Core\Style\DrupalStyle; @@ -23,42 +24,6 @@ class ServerContext extends Context implements ConfigurationInterface */ public $type = 'server'; - /** - * @var array - * A list of services needed for this context. - */ - protected $services = []; - - /** - * ServerContext constructor. - * - * @param $name - * @param $console_config - * @param array $options - */ - function __construct($name, $console_config, array $options = []) - { - parent::__construct($name, $console_config, $options); - if (isset($this->config['services'])) { - $this->prepareServices(); - } - else { - $this->services = []; - } - } - - /** - * Load Service classes from config into Context.. - */ - protected function prepareServices() { - foreach ($this->config['services'] as $service_name => $service) { - $service_name = ucfirst($service_name); - $service_type = ucfirst($service['type']); - $service_class = "\\Aegir\\Provision\\Service\\{$service_name}\\{$service_name}{$service_type}Service"; - $this->services[strtolower($service_name)] = new $service_class($service, $this); - } - } - static function option_documentation() { $options = [ @@ -71,166 +36,4 @@ class ServerContext extends Context implements ConfigurationInterface return $options; } - /** - * Loads all available \Aegir\Provision\Service classes - * - * @return array - */ - public function getAvailableServices($service = NULL) { - - // Load all service classes - $classes = []; - $discovery = new CommandFileDiscovery(); - $discovery->setSearchPattern('*Service.php'); - $servicesFiles = $discovery->discover(__DIR__ .'/../Service', '\Aegir\Provision\Service'); - - foreach ($servicesFiles as $serviceClass) { - $classes[$serviceClass::SERVICE] = $serviceClass; - } - - if ($service && isset($classes[$service])) { - return $classes[$service]; - } - elseif ($service && !isset($classes[$service])) { - throw new \Exception("No service with name $service was found."); - } - else { - return $classes; - } - } - - /** - * Lists all available services as a simple service => name array. - * @return array - */ - public function getServiceOptions() { - $options = []; - $services = $this->getAvailableServices(); - foreach ($services as $service => $class) { - $options[$service] = $class::SERVICE_NAME; - } - return $options; - } - - /** - * @return array - */ - protected function getAvailableServiceTypes($service, $service_type = NULL) { - - // Load all service classes - $classes = []; - $discovery = new CommandFileDiscovery(); - $discovery->setSearchPattern(ucfirst($service) . '*Service.php'); - $serviceTypesFiles = $discovery->discover(__DIR__ .'/../Service/' . ucfirst($service), '\Aegir\Provision\Service\\' . ucfirst($service)); - foreach ($serviceTypesFiles as $serviceTypeClass) { - $classes[$serviceTypeClass::SERVICE_TYPE] = $serviceTypeClass; - } - - if ($service_type && isset($classes[$service_type])) { - return $classes[$service_type]; - } - elseif ($service_type && !isset($classes[$service_type])) { - throw new \Exception("No service type with name $service_type was found."); - } - else { - return $classes; - } - } - - /** - * Lists all available services as a simple service => name array. - * @return array - */ - public function getServiceTypeOptions($service) { - $options = []; - $service_types = $this->getAvailableServiceTypes($service); - foreach ($service_types as $service_type => $class) { - $options[$service_type] = $class::SERVICE_TYPE_NAME; - } - return $options; - } - - - /** - * Return all services for this context. - * - * @return array - */ - public function getServices() { - return $this->services; - } - - /** - * Pass server specific config to Context configTreeBuilder. - */ - public function configTreeBuilder(&$root_node) - { - $root_node - ->children() - ->arrayNode('services') - ->prototype('array') - ->children() - ->scalarNode('type') - ->isRequired(true) - ->end() - ->append($this->addServiceProperties()) - ->end() - ->end(); - } - - /** - * Append Service class options_documentation to config tree. - */ - public function addServiceProperties() - { - $builder = new TreeBuilder(); - $node = $builder->root('properties'); - - // Load config tree from Service type classes - if (!empty($this->getProperty('services')) && !empty($this->getProperty('services'))) { - foreach ($this->getProperty('services') as $service => $info) { - $service = ucfirst($service); - $service_type = ucfirst($info['type']); - $class = "\Aegir\Provision\Service\\{$service}\\{$service}{$service_type}Service"; - foreach ($class::option_documentation() as $name => $description) { - $node - ->children() - ->scalarNode($name)->end() - ->end() - ->end(); - } - } - } - return $node; - } - - public function verify() { - - // Run verify method on all services. - foreach ($this->getServices() as $service) { - $service->verify(); - } - - return "Server Context Verified: " . $this->name; - } - - /** - * Output a list of all services for this service. - */ - public function showServices(DrupalStyle $io) { - if (!empty($this->getServices())) { - $rows = []; - foreach ($this->getServices() as $name => $service) { - $rows[] = [$name, $service->type]; - - // Show all properties. - if (!empty($service->properties )) { - foreach ($service->properties as $name => $value) { - $rows[] = [' ' . $name, $value]; - } - } - } - $io->table(['Services'], $rows); - } - } } diff --git a/src/Context/SiteContext.php b/src/Context/SiteContext.php index 2a0aec143efc8b241e795c59a94ad83ec00413d5..156e8e3d425e88982ce2570608be267dd2b13b39 100644 --- a/src/Context/SiteContext.php +++ b/src/Context/SiteContext.php @@ -2,6 +2,7 @@ namespace Aegir\Provision\Context; +use Aegir\Provision\Application; use Aegir\Provision\Context; use Symfony\Component\Config\Definition\ConfigurationInterface; @@ -19,6 +20,33 @@ class SiteContext extends Context implements ConfigurationInterface */ public $type = 'site'; + /** + * PlatformContext constructor. + * + * @param $name + * @param $console_config + * @param Application $application + * @param array $options + */ + function __construct($name, $console_config, Application $application, array $options = []) + { + parent::__construct($name, $console_config, $application, $options); + + // Load "db_server" context. +// if (isset($this->config['service_subscriptions']['db'])) { +// $this->db_server = $application->getContext($this->config['service_subscriptions']['db']['server']); +// } + + $this->logger = $application->logger; + + if (isset($this->config['platform'])) { + $this->platform = $application->getContext( + $this->config['platform'] + ); + } + + } + static function option_documentation() { return [ @@ -35,6 +63,16 @@ class SiteContext extends Context implements ConfigurationInterface ]; } + + public function verify() { + parent::verify(); + + // $this->db_server->service('db')->verify(); +// $this->platform->verify(); + +// return "Site Context Verified: " . $this->name; + } + // /** // * Write out this named context to an alias file. // */ diff --git a/src/Service.php b/src/Service.php index 29849bb9484523b2b285e9f2e29e10df7a7036af..f78055259263158f0a6b82f7dd2d3270883b3d54 100644 --- a/src/Service.php +++ b/src/Service.php @@ -27,6 +27,14 @@ class Service { function verify() { $this->writeConfigurations(); } + + /** + * List context types that are allowed to subscribe to this service. + * @return array + */ + static function allowedContexts() { + return []; + } /** * Write this service's configurations. @@ -35,9 +43,11 @@ class Service { if (empty($this->getConfigurations()[$this->context->type])) { return; } + $this->context->application->logger->info('CONTEXT ' . $this->context->type); foreach ($this->getConfigurations()[$this->context->type] as $configuration_class) { $config = new $configuration_class($this->context, $this); $config->write(); + $this->context->application->logger->info('Wrote ' . $config->description . ' to ' . $config->filename()); } } @@ -63,6 +73,37 @@ class Service { public function getName() { return $this::SERVICE; } + + /** + * Return a list of user configurable options that this service provides to Server Context objects. + */ + static function server_options() { + return []; +// return [ +// 'http_port' => 'The port which the web service is running on.', +// 'web_group' => 'server with http: OS group for permissions; working default will be attempted', +// ]; + } + + /** + * Return a list of user configurable options that this service provides to Platform Context objects. + */ + static function platform_options() { + return []; +// return [ +// 'platform_extra_config' => 'Extra lines of configuration to add to this platform.', +// ]; + } + + /** + * Return a list of user configurable options that this service provides to Site Context objects. + */ + static function site_options() { + return []; +// return [ +// 'site_mail' => 'The email address to use for the ServerAdmin configuration.', +// ]; + } /** * LEGACY @@ -400,16 +441,6 @@ class Service { // return TRUE; // } - /** - * Return service-specific configuration options for help. - * - * @return - * array('option' => 'description') - */ - static function option_documentation() { - return array(); - } - /** * Save symlink for this server from /var/aegir/config/APPLICATION_NAME.conf -> /var/aegir/config/SERVER/APPLICATION_NAME.conf * diff --git a/src/Service/DbService.php b/src/Service/DbService.php index 517ac87f6939a57ac0b486726429901c3dd05400..8988e7a2e6535111fff799e969483deaab900c9d 100644 --- a/src/Service/DbService.php +++ b/src/Service/DbService.php @@ -24,13 +24,42 @@ class DbService extends Service const SERVICE_NAME = 'Database Server'; - static function option_documentation() + /** + * Implements Service::server_options() + * + * @return array + */ + static function server_options() { return [ 'master_db' => 'server with db: Master database connection info, {type}://{user}:{password}@{host}', 'db_grant_all_hosts' => 'Grant access to site database users from any web host. If set to TRUE, any host will be allowed to connect to MySQL site databases on this server using the generated username and password. If set to FALSE, web hosts will be granted access by their detected IP address.', ]; } + + /** + * List context types that are allowed to subscribe to this service. + * @return array + */ + static function allowedContexts() { + return [ + 'site' + ]; + } + + /** + * Implements Service::server_options() + * + * @return array + */ + static function site_options() + { + return [ + 'db_name' => 'The name of the database. Default: Automatically generated.', + 'db_user' => 'The username used to access the database. Default: Automatically generated.', + 'db_password' => 'The password used to access the database. Default: Automatically generated.', + ]; + } /** * Register the db handler for sites, based on the db_server option. diff --git a/src/Service/Http/Apache/Configuration/PlatformConfiguration.php b/src/Service/Http/Apache/Configuration/PlatformConfiguration.php index 096fd061c71d3e2ccab5ee250756ec53e9d4ea77..3265de88bbff50b1cdc81866de93904bda8aaf16 100644 --- a/src/Service/Http/Apache/Configuration/PlatformConfiguration.php +++ b/src/Service/Http/Apache/Configuration/PlatformConfiguration.php @@ -16,16 +16,10 @@ class PlatformConfiguration extends Configuration { const SERVICE_TYPE = 'apache'; - public $template = 'server.tpl.php'; - public $description = 'web server configuration file'; + public $template = 'platform.tpl.php'; + public $description = 'platform configuration file'; function filename() { - if (isset($this->data['application_name'])) { - $file = $this->data['application_name'] . '.conf'; - return $this->data['server']->config_path . '/' . $file; - } - else { - return FALSE; - } + return $this->service->properties['http_platformd_path'] . '/' . ltrim($this->context->name, '@') . '.conf'; } } \ No newline at end of file diff --git a/src/Service/Http/Apache/Configuration/SiteConfiguration.php b/src/Service/Http/Apache/Configuration/SiteConfiguration.php index 3ac18a68240041ca16b2825815783c4c87768705..f7c23f5bd05ec164fa2043c85bf4fab0d339a28a 100644 --- a/src/Service/Http/Apache/Configuration/SiteConfiguration.php +++ b/src/Service/Http/Apache/Configuration/SiteConfiguration.php @@ -22,12 +22,18 @@ class SiteConfiguration extends Configuration { function filename() { - if (drush_get_option('provision_apache_conf_suffix', FALSE)) { - return $this->data['http_vhostd_path'] . '/' . $this->uri . '.conf'; - } - else { - return $this->data['http_vhostd_path'] . '/' . $this->uri; - } + $file = $this->uri . '.conf'; + +// return $this->service->properties['http_platformd_path'] . '/' . ltrim($this->context->name, '@') . '.conf'; + return $this->context->console_config['config_path'] . '/' . $this->context->name . '/' . $file; + +// return $this->context->config['config_path']; +// if (drush_get_option('provision_apache_conf_suffix', FALSE)) { +// return $this->data['http_vhostd_path'] . '/' . $this->uri . '.conf'; +// } +// else { +// return $this->data['http_vhostd_path'] . '/' . $this->uri; +// } } function process() { diff --git a/src/Service/HttpService.php b/src/Service/HttpService.php index 79c00cead451b6cfc07732889cf9bfb21d925a3c..af6ce4906d6c7865059745674b8240314e473488 100644 --- a/src/Service/HttpService.php +++ b/src/Service/HttpService.php @@ -24,16 +24,31 @@ class HttpService extends Service { protected $ssl_enabled = FALSE; - static function option_documentation() { - return array( + + /** + * Implements Service::server_options() + * + * @return array + */ + static function server_options() + { + return [ 'http_port' => 'The port which the web service is running on.', -// 'http_platformd_path' => 'The path to store platforms.', -// 'http_postd_path' => 'The path to store post configuration.', 'web_group' => 'server with http: OS group for permissions; working default will be attempted', 'web_disable_url' => 'server with http: URL disabled sites are redirected to; default {master_url}/hosting/disabled', 'web_maintenance_url' => 'server with http: URL maintenance sites are redirected to; default {master_url}/hosting/maintenance', 'restart_command' => 'The command to reload the web server configuration;' - ); + ]; + } + + /** + * List context types that are allowed to subscribe to this service. + * @return array + */ + static function allowedContexts() { + return [ + 'platform' + ]; } /** diff --git a/src/ServiceSubscription.php b/src/ServiceSubscription.php new file mode 100644 index 0000000000000000000000000000000000000000..bf20e29429015b5f67a61ef47a4ab8ca7ee8b06a --- /dev/null +++ b/src/ServiceSubscription.php @@ -0,0 +1,25 @@ +context = $context; + $this->server = $server; + $this->service = $server->getService($service_name); + $this->type = $server->getService($service_name)->type; + } +}