Skip to content
<?php
namespace Aegir\Provision\Console;
/**
* Class ProvisionConfig
* @package Aegir\Provision\Console
*
* Many thanks to pantheon-systems/terminus.
*/
class ProvisionConfig extends \Robo\Config
{
/**
* @var string
*/
protected $constant_prefix = 'PROVISION_';
/**
* @var array
*/
protected $sources = [];
/**
* @var string
*/
protected $source_name = 'Unknown';
/**
* Set add all the values in the array to this Config object.
}
* @param array $array
*/
public function fromArray(array $array = [])
{
foreach ($array as $key => $val) {
$this->set($key, $val);
}
}
/**
* Convert the config to an array.
*
* @return array
*/
public function toArray()
{
$out = [];
foreach ($this->keys() as $key) {
$out[$key] = $this->get($key);
}
return $out;
}
/**
* Set a config value. Converts key from Provision constant (PROVISION_XXX) if needed.
*
* @param string $key
* @param mixed $value
*
* @return $this
*/
public function set($key, $value)
{
// Convert Provision constant name to internal key.
if ($this->keyIsConstant($key)) {
$key = $this->getKeyFromConstant($key);
}
return parent::set($key, $value);
}
/**
* Get a configuration value
*
* @param string $key Which config item to look up
* @param string|null $defaultOverride Override usual default value with a different default
*
* @return mixed
*/
public function get($key, $defaultOverride = null)
{
$value = parent::get($key, $defaultOverride);
// Replace placeholders.
if (is_string($value)) {
$value = $this->replacePlaceholders($value);
}
return $value;
}
/**
* Return all of the keys in the Config
* @return array
*/
public function keys()
{
return array_keys($this->export());
}
/**
* Override the values in this Config object with the given input Config
*
* @param \Aegir\Provision\Console\Config $in
*/
public function extend(ProvisionConfig $in)
{
foreach ($in->keys() as $key) {
$this->set($key, $in->get($key));
// Set the source of this variable to make tracking config easier.
$this->setSource($key, $in->getSource($key));
}
}
/**
* Get the name of the source for this configuration object.
*
* @return string
*/
public function getSourceName()
{
return $this->source_name;
}
/**
* Get a description of where this configuration came from.
*
* @param $key
* @return string
*/
public function getSource($key)
{
return isset($this->sources[$key]) ? $this->sources[$key] : $this->getSourceName();
}
/**
* Set the source for a given configuration item.
*
* @param $key
* @param $source
*/
protected function setSource($key, $source)
{
$this->sources[$key] = $source;
}
/**
* Reflects a key name given a PRovision constant name
*
* @param string $constant_name The name of a constant to get a key for
* @return string
*/
protected function getKeyFromConstant($constant_name)
{
$key = strtolower(str_replace($this->constant_prefix, '', $constant_name));
return $key;
}
/**
* Turn an internal key into a constant name
*
* @param string $key The key to get the constant name for.
* @return string
*/
public function getConstantFromKey($key)
{
$key = strtoupper($this->constant_prefix . $key);
return $key;
}
/**
* Determines if a key is a Provision constant name
*
* @param $key
* @return boolean
*/
protected function keyIsConstant($key)
{
return strpos($key, $this->constant_prefix) === 0;
}
/**
* Exchanges values in [[ ]] in the given string with constants
*
* @param string $string The string to perform replacements on
* @return string $string The modified string
*/
protected function replacePlaceholders($string)
{
$regex = '~\[\[(.*?)\]\]~';
preg_match_all($regex, $string, $matches);
if (!empty($matches)) {
foreach ($matches[1] as $id => $value) {
$replacement_key = $this->getKeyFromConstant(trim($value));
$replacement = $this->get($replacement_key);
if ($replacement) {
$string = str_replace($matches[0][$id], $replacement, $string);
}
}
}
$fixed_string = $this->fixDirectorySeparators($string);
return $fixed_string;
}
/**
* Ensures a directory exists
*
* @param string $name The name of the config var
* @param string $value The value of the named config var
* @return boolean|null
*/
public function ensureDirExists($name, $value)
{
if ((strpos($name, $this->constant_prefix) !== false) && (strpos($name, '_DIR') !== false) && ($value != '~')) {
try {
$dir_exists = (is_dir($value) || (!file_exists($value) && @mkdir($value, 0777, true)));
} catch (\Exception $e) {
return false;
}
return $dir_exists;
}
return null;
}
/**
* Ensures that directory paths work in any system
*
* @param string $path A path to set the directory separators for
* @return string
*/
public function fixDirectorySeparators($path)
{
return str_replace(['/', '\\',], DIRECTORY_SEPARATOR, $path);
}
/**
* @param mixed $source_name
*/
public function setSourceName($source_name)
{
$this->source_name = $source_name;
}
}
<?php
namespace Aegir\Provision\Console;
use Symfony\Component\Yaml\Yaml;
/**
* Class YamlConfig
* @package Aegir\Provision\Console
*
* Many thanks to pantheon-systems/terminus.
*/
class YamlConfig extends ProvisionConfig
{
/**
* YamlConfig constructor.
* @param string $yml_path The path to the yaml file.
*/
public function __construct($yml_path)
{
parent::__construct();
$this->setSourceName($yml_path);
$file_config = file_exists($yml_path) ? Yaml::parse(file_get_contents($yml_path)) : [];
if (!is_null($file_config)) {
$this->fromArray($file_config);
}
}
}
......@@ -6,12 +6,15 @@
namespace Aegir\Provision;
use Aegir\Provision\Common\ProvisionAwareTrait;
use Aegir\Provision\Console\Config;
use Consolidation\AnnotatedCommand\CommandFileDiscovery;
use Drupal\Console\Core\Style\DrupalStyle;
use Psr\Log\LoggerInterface;
use Robo\Common\BuilderAwareTrait;
use Robo\Contract\BuilderAwareInterface;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\Processor;
use Symfony\Component\Console\Exception\InvalidOptionException;
use Symfony\Component\Filesystem\Exception\IOException;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Yaml\Dumper;
......@@ -24,9 +27,12 @@ use Symfony\Component\Yaml\Yaml;
*
* @package Aegir\Provision
*/
class Context
class Context implements BuilderAwareInterface
{
use BuilderAwareTrait;
use ProvisionAwareTrait;
/**
* @var string
* Name for saving aliases and referencing.
......@@ -60,27 +66,23 @@ class Context
* init(), set defaults with setProperty().
*/
protected $properties = [];
/**
* @var \Aegir\Provision\Application;
*/
public $application;
/**
* @var LoggerInterface
*/
public $logger;
/**
* Context constructor.
*
* @param $name
* @param array $options
*/
function __construct($name, Application $application = NULL, $options = [])
function __construct(
$name,
Provision $provision,
$options = [])
{
$this->name = $name;
$this->application = $application;
$this->setProvision($provision);
$this->setBuilder($this->getProvision()->getBuilder());
$this->loadContextConfig($options);
$this->prepareServices();
}
......@@ -94,8 +96,8 @@ class Context
*/
private function loadContextConfig($options = []) {
if ($this->application) {
$this->config_path = $this->application->getConfig()->get('config_path') . '/provision/' . $this->type . '.' . $this->name . '.yml';
if ($this->getProvision()) {
$this->config_path = $this->getProvision()->getConfig()->get('config_path') . '/provision/' . $this->type . '.' . $this->name . '.yml';
}
else {
$config = new Config();
......@@ -115,14 +117,16 @@ class Context
$this->properties[$option] = $options[$option];
}
}
$this->properties['type'] = $this->type;
$this->properties['name'] = $this->name;
$configs[] = $this->properties;
$this->properties['context_type'] = $this->type;
$this->config = $processor->processConfiguration($this, $configs);
} 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", [
'!type' => $this->type,
'!name' => $this->name,
......@@ -269,6 +273,11 @@ class Context
->children()
->scalarNode('name')
->defaultValue($this->name)
->isRequired()
->end()
->scalarNode('type')
->defaultValue($this->type)
->isRequired()
->end()
->end();
......@@ -293,6 +302,7 @@ class Context
->node($property, 'context')
->isRequired()
->attribute('context_type', $type)
->attribute('provision', $this->getProvision())
->end()
->end();
}
......@@ -323,7 +333,7 @@ class Context
// If type is empty, it's because it's in the ServerContext
if (empty($info['type'])) {
$server = Application::getContext($info['server']);
$server = $this->getProvision()->getContext($info['server']);
$service_type = ucfirst($server->getService($service)->type);
}
else {
......@@ -466,28 +476,33 @@ class Context
*
* If this context is a Service Subscriber, the provider service will be verified first.
*/
public function verify() {
public function verifyCommand() {
$return_codes = [];
// Run verify method on all services.
foreach ($this->getServices() as $type => $service) {
$friendlyName = $service->getFriendlyName();
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}");
foreach ($service->verifyProvider() as $verify_part => $verify_success) {
$return_codes[] = $verify_success? 0: 1;
}
foreach ($this->getSubscription($type)->verify() as $type => $verify_success) {
$return_codes[] = $verify_success? 0: 1;
}
}
$return_codes[] = $service->verify() ? 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 (count(array_filter($return_codes))) {
......
......@@ -4,6 +4,7 @@ namespace Aegir\Provision\Context;
use Aegir\Provision\Application;
use Aegir\Provision\ContextSubscriber;
use Aegir\Provision\Provision;
use Symfony\Component\Config\Definition\ConfigurationInterface;
/**
......@@ -30,12 +31,15 @@ class PlatformContext extends ContextSubscriber implements ConfigurationInterfac
* PlatformContext constructor.
*
* @param $name
* @param Application $application
* @param Provision $provision
* @param array $options
*/
function __construct($name, Application $application = NULL, array $options = [])
{
parent::__construct($name, $application, $options);
function __construct(
$name,
Provision $provision = NULL,
$options = []
) {
parent::__construct($name, $provision, $options);
// Load "web_server" context.
// There is no need to validate for $this->properties['web_server'] because the config system does that.
......@@ -65,4 +69,22 @@ class PlatformContext extends ContextSubscriber implements ConfigurationInterfac
public static function serviceRequirements() {
return ['http'];
}
/**
* Output extra info before verifying.
*/
public function verify()
{
$this->getProvision()->io()->customLite($this->getProperty('root'), 'Root: ', '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();
}
}
......@@ -3,7 +3,9 @@
namespace Aegir\Provision\Context;
use Aegir\Provision\Application;
use Aegir\Provision\Context;
use Aegir\Provision\ContextSubscriber;
use Aegir\Provision\Provision;
use Symfony\Component\Config\Definition\ConfigurationInterface;
/**
......@@ -34,19 +36,23 @@ class SiteContext extends ContextSubscriber implements ConfigurationInterface
* @param Application $application
* @param array $options
*/
function __construct($name, Application $application = NULL, array $options = [])
{
parent::__construct($name, $application, $options);
function __construct(
$name,
Provision $provision = NULL,
$options = []
) {
parent::__construct($name, $provision, $options);
// Load "web_server" and "platform" contexts.
// 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->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.
$this->serviceSubscriptions['http'] = $this->platform->getSubscription('http');
$this->serviceSubscriptions['http']->context = $this;
}
......@@ -75,20 +81,16 @@ class SiteContext extends ContextSubscriber implements ConfigurationInterface
'platform' => 'platform'
];
}
/**
* Overrides ContextSubscriber::getSubscriptions() to add in the platform's subscriptions.
*
* @return array
* Output extra info before verifying.
*/
public function getSubscriptions() {
// Load this context's subscriptions.
$subscriptions = parent::getSubscriptions();
// Load the platform's subscriptions.
$subscriptions += $this->platform->getSubscriptions();
public function verify()
{
$this->getProvision()->io()->customLite($this->getProperty('uri'), 'Site URL: ', 'info');
$this->getProvision()->io()->customLite($this->platform->getProperty('root'), 'Root: ', 'info');
$this->getProvision()->io()->customLite($this->config_path, 'Configuration File: ', 'info');
return $subscriptions;
return parent::verify();
}
}
......@@ -93,6 +93,7 @@ class ContextSubscriber extends Context
->node('server', 'context')
->isRequired()
->attribute('context_type', 'server')
->attribute('provision', $this->getProvision())
->end()
->append($this->addServiceProperties('service_subscriptions'))
->end()
......
<?php
namespace Aegir\Provision;
use Aegir\Provision\Console\Config;
use Aegir\Provision\Commands\ExampleCommands;
use Aegir\Provision\Console\ConsoleOutput;
use Aegir\Provision\Robo\ProvisionCollectionBuilder;
use Aegir\Provision\Robo\ProvisionExecutor;
use Aegir\Provision\Robo\ProvisionTasks;
use Drupal\Console\Core\Style\DrupalStyle;
use League\Container\Container;
use League\Container\ContainerAwareInterface;
use League\Container\ContainerAwareTrait;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Psr\Log\LogLevel;
use Robo\Common\BuilderAwareTrait;
use Robo\Common\ConfigAwareTrait;
use Robo\Common\IO;
use Robo\Contract\BuilderAwareInterface;
use Robo\Contract\ConfigAwareInterface;
use Robo\Contract\IOAwareInterface;
use Robo\Robo;
use Robo\Runner as RoboRunner;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Logger\ConsoleLogger;
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 {
const APPLICATION_NAME = 'Aegir Provision';
const VERSION = '4.x-dev';
const REPOSITORY = 'aegir-project/provision';
use BuilderAwareTrait;
use ConfigAwareTrait;
use ContainerAwareTrait;
use LoggerAwareTrait;
use IO;
/**
* @var \Robo\Runner
*/
private $runner;
/**
* @var string[]
*/
private $commands = [];
/**
* @var \Aegir\Provision\Context[]
*/
private $contexts = [];
/**
* @var array[]
*/
private $context_files = [];
/**
* Provision constructor.
*
* @param \Aegir\Provision\Console\Config $config
* @param \Symfony\Component\Console\Input\InputInterface|null $input
* @param \Symfony\Component\Console\Output\OutputInterface|null $output
*/
public function __construct(
Config $config,
InputInterface $input = NULL,
OutputInterface $output = NULL
) {
$logger = new ConsoleLogger($output);
$this
->setConfig($config)
;
// Create Application.
$application = new Application(self::APPLICATION_NAME, self::VERSION);
$application
->setProvision($this)
->setLogger($logger)
;
$application->configureIO($input, $output);
$this->setInput($input);
$this->setOutput($output);
// Create and configure container.
$container = Robo::createDefaultContainer($input, $output, $application, $config);
$this->setContainer($container);
$this->configureContainer($container);
// Instantiate Robo Runner.
$this->runner = new RoboRunner([
ExampleCommands::class
]);
$this->runner->setContainer($container);
$this->runner->setSelfUpdateRepository(self::REPOSITORY);
$this->setBuilder($container->get('builder'));
$this->setLogger($container->get('logger'));
$this->loadAllContexts();
}
/**
* Lookup all context yml files and load into Context classes.
*/
private function loadAllContexts()
{
$folder = $this->getConfig()->get('config_path') . '/provision';
$finder = new \Symfony\Component\Finder\Finder();
$finder->files()->name("*.yml")->in($folder);
foreach ($finder as $file) {
$context_type = substr($file->getFilename(), 0, strpos($file->getFilename(), '.'));
$context_name = substr($file->getFilename(), strpos($file->getFilename(), '.') + 1, strlen($file->getFilename()) - strlen($context_type) - 5);
$this->context_files[$context_name] = [
'name' => $context_name,
'type' => $context_type,
'file' => $file,
];
}
// Load Context classes from files metadata.
foreach ($this->context_files as $context) {
$class = Context::getClassName($context['type']);
$this->contexts[$context['name']] = new $class($context['name'], $this);
}
}
/**
* Loads a single context from file into $this->contexts[$name].
*
* Used to load dependant contexts that might not be instantiated yet.
*
* @param $name
*/
public function loadContext($name) {
$class = Context::getClassName($this->context_files[$name]['type']);
$this->contexts[$this->context_files[$name]['name']] = new $class($this->context_files[$name]['name'], $this);
}
public function run(InputInterface $input, OutputInterface $output) {
$status_code = $this->runner->run($input, $output);
return $status_code;
}
/**
* Register the necessary classes for Provision.
*/
public function configureContainer(Container $container) {
// FROM https://github.com/acquia/blt :
// We create our own builder so that non-command classes are able to
// implement task methods, like taskExec(). Yes, there are now two builders
// in the container. "collectionBuilder" used for the actual command that
// was executed, and "builder" to be used with non-command classes.
$tasks = new ProvisionTasks();
$builder = new ProvisionCollectionBuilder($tasks);
$tasks->setBuilder($builder);
$container->add('builder', $builder);
$container->add('executor', ProvisionExecutor::class)
->withArgument('builder');
}
/**
* Temporary helper to allow public access to output.
*
* @return \Symfony\Component\Console\Output\OutputInterface
*/
public function getOutput()
{
return $this->output();
}
/**
* Temporary helper to allow public access to input.
*
* @return \Symfony\Component\Console\Input\InputInterface
*/
public function getInput()
{
return $this->input();
}
/**
* Gets Logger object.
* Returns the currently active Logger instance.
*
* @return \Psr\Log\LoggerInterface
*/
public function getLogger()
{
return $this->logger;
}
/**
* Provide access to DrupalStyle object.
*
* @return \Drupal\Console\Core\Style\DrupalStyle
*/
public function io()
{
if (!$this->io) {
$this->io = new DrupalStyle($this->input(), $this->output());
}
return $this->io;
}
/**
* Get a new Provision
* @return \Aegir\Provision\Provision
*/
static function getProvision() {
$input = new ArgvInput();
$output = new ConsoleOutput();
$config = new Config();
return new Provision($config, $input, $output);
}
/**
* Return all available contexts.
*
* @return array|Context
*/
public function getAllContexts($name = '') {
if ($name && isset($this->contexts[$name])) {
return $this->contexts[$name];
}
elseif ($name && !isset($this->contexts[$name])) {
return NULL;
}
else {
return $this->contexts;
}
}
/**
* Load all server contexts.
*
* @param null $service
* @return mixed
* @throws \Exception
*/
protected function getAllServers($service = NULL) {
$servers = [];
$context_files = $this->getAllContexts();
if (empty($context_files)) {
throw new \Exception('No server contexts found. Use `provision save` to create one.');
}
foreach ($context_files as $context) {
if ($context->type == 'server') {
$servers[$context->name] = $context;
}
}
return $servers;
}
/**
* Get a simple array of all contexts, for use in an options list.
* @return array
*/
public function getAllContextsOptions($type = NULL) {
$options = [];
foreach ($this->getAllContexts() as $name => $context) {
if ($type) {
if ($context->type == $type) {
$options[$name] = $context->name;
}
}
else {
$options[$name] = $context->type . ' ' . $context->name;
}
}
return $options;
}
/**
* Load the Aegir context with the specified name.
*
* @param $name
*
* @return array|\Aegir\Provision\Context
* @throws \Exception
*/
public function getContext($name) {
// Check if $name is empty.
if (empty($name)) {
throw new \Exception('Context name must not be empty.');
}
// If context exists but hasn't been loaded, load it.
if (empty($this->contexts[$name]) && !empty($this->context_files[$name])) {
$this->loadContext($name);
}
// Check if context still isn't found.
if (empty($this->contexts[$name])) {
throw new \Exception('Context not found with name: ' . $name);
}
return $this->contexts[$name];
}
/**
* Look for a context file being present. This is available before Context
* objects are bootstrapped.
*/
public function getContextFile($name) {
if (empty($name)) {
throw new \Exception('Context name must not be empty.');
}
if (empty($this->context_files[$name])) {
throw new \Exception('Context not found with name: ' . $name);
}
return$this->context_files[$name];
}
/**
* Get a simple array of all servers, optionally specifying the the service_type to filter by ("http", "db" etc.)
* @param string $service_type
* @return array
*/
public function getServerOptions($service_type = '') {
$servers = [];
foreach ($this->getAllServers() as $server) {
if ($service_type && !empty($server->config['services'][$service_type])) {
$servers[$server->name] = $server->name . ': ' . $server->config['services'][$service_type]['type'];
}
elseif ($service_type == '') {
$servers[$server->name] = $server->name . ': ' . $server->config['services'][$service_type]['type'];
}
}
return $servers;
}
/**
* Check that a context type's service requirements are provided by at least 1 server.
*
* @param $type
* @return array
*/
public function checkServiceRequirements($type) {
$class_name = Context::getClassName($type);
// @var $context Context
$service_requirements = $class_name::serviceRequirements();
$services = [];
foreach ($service_requirements as $service) {
try {
if (empty($this->getAllServers($service))) {
$services[$service] = 0;
}
else {
$services[$service] = 1;
}
} catch (\Exception $e) {
$services[$service] = 0;
}
}
return $services;
}
}
<?php
namespace Aegir\Provision\Robo;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Psr\Log\LoggerInterface;
use Psr\Log\LoggerTrait;
use Robo\Collection\CollectionBuilder;
use Robo\Collection\NestedCollectionInterface;
use Robo\Common\CommandArguments;
use Robo\Common\ExecCommand;
use Robo\Common\ExecTrait;
use Robo\Common\OutputAdapter;
use Robo\Common\TaskIO;
use Robo\Result;
use Robo\ResultData;
use Symfony\Component\Process\Process;
class ProvisionCollectionBuilder extends CollectionBuilder {
/**
* @var string|\Robo\Contract\CommandInterface
*/
protected $command;
/**
* @var \Robo\Contract\TaskInterface
*/
protected $currentTask;
}
\ No newline at end of file
<?php
namespace Aegir\Provision\Robo;
use GuzzleHttp\Client;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Robo\Collection\CollectionBuilder;
use Robo\Common\ConfigAwareTrait;
use Robo\Common\IO;
use Robo\Contract\VerbosityThresholdInterface;
use Robo\Robo;
use Robo\Contract\ConfigAwareInterface;
use Robo\Contract\IOAwareInterface;
use Symfony\Component\Process\Process;
/**
* A class for executing commands.
*
* This allows non-Robo-command classes to execute commands easily.
*/
class ProvisionExecutor implements ConfigAwareInterface, IOAwareInterface, LoggerAwareInterface {
use ConfigAwareTrait;
use IO;
use LoggerAwareTrait;
/**
* A copy of the Robo builder.
*
* @var \Aegir\Provision\Robo\ProvisionTasks
*/
protected $builder;
/**
* Executor constructor.
*
* @param \Robo\Collection\CollectionBuilder $builder
* This is a copy of the collection builder, required for calling various
* Robo tasks from non-command files.
*/
public function __construct(CollectionBuilder $builder) {
$this->builder = $builder;
}
/**
* Returns $this->builder.
*
* @return \Aegir\Provision\Robo\ProvisionTasks
* The builder.
*/
public function getBuilder() {
return $this->builder;
}
/**
* Wrapper for taskExec().
*
* @param string $command
* The command to execute.
*
* @return \Robo\Task\Base\Exec
* The task. You must call run() on this to execute it!
*/
public function taskExec($command) {
return $this->builder->taskExec($command);
}
// /**
// * Executes a drush command.
// *
// * @param string $command
// * The command to execute, without "drush" prefix.
// *
// * @return \Robo\Common\ProcessExecutor
// * The unexecuted process.
// */
// public function drush($command) {
// // @todo Set to silent if verbosity is less than very verbose.
// $bin = $this->getConfigValue('composer.bin');
// /** @var \Robo\Common\ProcessExecutor $process_executor */
// $drush_alias = $this->getConfigValue('drush.alias');
// if (!empty($drush_alias)) {
// $drush_alias = "@$drush_alias";
// }
//
// $command_string = "'$bin/drush' $drush_alias $command";
//
// if ($this->input()->hasOption('yes') && $this->input()->getOption('yes')) {
// $command_string .= ' -y';
// }
//
// $process_executor = Robo::process(new Process($command_string));
//
// return $process_executor->dir($this->getConfigValue('docroot'))
// ->interactive(FALSE)
// ->printOutput(TRUE)
// ->printMetadata(TRUE)
// ->setVerbosityThreshold(VerbosityThresholdInterface::VERBOSITY_VERY_VERBOSE);
// }
/**
* Executes a command.
*
* @param string $command
* The command.
*
* @return \Robo\Common\ProcessExecutor
* The unexecuted command.
*/
public function execute($command) {
/** @var \Robo\Common\ProcessExecutor $process_executor */
$process_executor = Robo::process(new Process($command));
return $process_executor->dir($this->getConfigValue('repo.root'))
->printOutput(FALSE)
->printMetadata(FALSE)
->interactive(FALSE);
}
/**
* Kills all system processes that are using a particular port.
*
* @param string $port
* The port number.
*/
public function killProcessByPort($port) {
$this->logger->info("Killing all processes on port '$port'...");
// This is allowed to fail.
// @todo Replace with standardized call to Symfony Process.
exec("command -v lsof && lsof -ti tcp:$port | xargs kill l 2>&1");
exec("pkill -f $port 2>&1");
}
/**
* Kills all system processes containing a particular string.
*
* @param string $name
* The name of the process.
*/
public function killProcessByName($name) {
$this->logger->info("Killing all processing containing string '$name'...");
// This is allowed to fail.
// @todo Replace with standardized call to Symfony Process.
exec("ps aux | grep -i $name | grep -v grep | awk '{print $2}' | xargs kill -9 2>&1");
// exec("ps aux | awk '/$name/ {print $2}' 2>&1 | xargs kill -9");.
}
/**
* Waits until a given URL responds with a non-50x response.
*
* This does have a maximum timeout, defined in wait().
*
* @param string $url
* The URL to wait for.
*/
public function waitForUrlAvailable($url) {
$this->wait([$this, 'checkUrl'], [$url], "Waiting for response from $url...");
}
/**
* Waits until a given callable returns TRUE.
*
* This does have a maximum timeout.
*
* @param callable $callable
* The method/function to wait for a TRUE response from.
* @param array $args
* Arguments to pass to $callable.
* @param string $message
* The message to display when this function is called.
*
* @return bool
* TRUE if callable returns TRUE.
*
* @throws \Exception
*/
public function wait(callable $callable, array $args, $message = '') {
$maxWait = 60 * 1000;
$checkEvery = 1 * 1000;
$start = microtime(TRUE) * 1000;
$end = $start + $maxWait;
if (!$message) {
$method_name = is_array($callable) ? $callable[1] : $callable;
$message = "Waiting for $method_name() to return true.";
}
// For some reason we can't reuse $start here.
while (microtime(TRUE) * 1000 < $end) {
$this->logger->info($message);
try {
if (call_user_func_array($callable, $args)) {
return TRUE;
}
}
catch (\Exception $e) {
$this->logger->error($e->getMessage());
}
usleep($checkEvery * 1000);
}
throw new BltException("Timed out.");
}
/**
* Checks a URL for a non-50x response.
*
* @param string $url
* The URL to check.
*
* @return bool
* TRUE if URL responded with a non-50x response.
*/
public function checkUrl($url) {
try {
$client = new Client();
$res = $client->request('GET', $url, [
'connection_timeout' => 2,
'timeout' => 2,
'exceptions' => FALSE,
]);
if ($res->getStatusCode() && substr($res->getStatusCode(), 0, 1) != '5') {
return TRUE;
}
else {
return FALSE;
}
}
catch (\Exception $e) {
}
return FALSE;
}
}
<?php
namespace Aegir\Provision\Robo;
use Consolidation\Config\ConfigInterface;
use League\Container\ContainerAwareInterface;
use League\Container\ContainerAwareTrait;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Robo\Collection\CollectionBuilder;
use Robo\Common\ConfigAwareTrait;
use Robo\Common\IO;
use Robo\Contract\BuilderAwareInterface;
use Robo\Contract\ConfigAwareInterface;
use Robo\Contract\IOAwareInterface;
use Robo\LoadAllTasks;
/**
* Base class for BLT Robo commands.
*/
class ProvisionTasks implements ConfigAwareInterface, LoggerAwareInterface, BuilderAwareInterface, IOAwareInterface, ContainerAwareInterface {
use ContainerAwareTrait;
use LoadAllTasks;
use ConfigAwareTrait;
// use InspectorAwareTrait;
use IO;
use LoggerAwareTrait;
}
......@@ -8,9 +8,20 @@
namespace Aegir\Provision;
class Service
use Aegir\Provision\Common\ContextAwareTrait;
use Aegir\Provision\Common\ProvisionAwareTrait;
use Psr\Log\LoggerAwareTrait;
use Robo\Common\BuilderAwareTrait;
use Robo\Common\OutputAdapter;
use Robo\Contract\BuilderAwareInterface;
class Service implements BuilderAwareInterface
{
use BuilderAwareTrait;
use ProvisionAwareTrait;
use ContextAwareTrait;
use LoggerAwareTrait;
public $type;
public $properties;
......@@ -23,11 +34,6 @@ class Service
*/
public $provider;
/**
* @var \Aegir\Provision\Application;
*/
public $application;
/**
* @var string
* The machine name of the service. ie. http, db
......@@ -40,12 +46,18 @@ class Service
*/
const SERVICE_NAME = 'Service Name';
function __construct($service_config, $provider_context)
function __construct($service_config, Context $provider_context)
{
$this->provider = $provider_context;
$this->application = $provider_context->application;
$this->setContext($provider_context);
$this->setProvision($provider_context->getProvision());
$this->setLogger($provider_context->getProvision()->getLogger());
$this->type = $service_config['type'];
$this->properties = $service_config['properties'];
if ($provider_context->getBuilder()) {
$this->setBuilder($provider_context->getBuilder());
}
}
/**
......@@ -70,30 +82,54 @@ class Service
return "\Aegir\Provision\Service\\{$service}Service";
}
}
/**
* React to the `provision verify` command.
*/
function verify()
{
return [
'configuration' => $this->writeConfigurations(),
'service' => $this->restartService(),
];
}
/**
* React to `provision verify` command when run on a subscriber, to verify the service's provider.
*
* This is used to allow skipping of the service restart.
* React to the verify command. Passes off to the method verifySite, verifyServer, verifyPlatform.
* @return mixed
*/
function verifyProvider()
{
return [
'configuration' => $this->writeConfigurations(),
];
public function verify() {
$method = 'verify' . ucfirst($this->getContext()->type);
$this->getProvision()->getLogger()->info("Running {method}", [
'method' => $method
]);
return $this::$method();
}
//
// /**
// * React to the `provision verify` command.
// */
// function verifySite()
// {
// return [
// 'configuration' => $this->writeConfigurations(),
// 'service' => $this->restartService(),
// ];
// }
//
// /**
// * React to the `provision verify` command.
// */
// function verifyPlatform()
// {
// return [
// 'configuration' => $this->writeConfigurations(),
// 'service' => $this->restartService(),
// ];
// }
//
// /**
// * React to `provision verify` command when run on a subscriber, to verify the service's provider.
// *
// * This is used to allow skipping of the service restart.
// */
// function verifyServer()
// {
// return [
// 'configuration' => $this->writeConfigurations(),
// ];
// }
/**
* Run the services "restart_command".
* @return bool
......@@ -103,13 +139,19 @@ class Service
return TRUE;
}
else {
try {
$output = $this->provider->shell_exec($this->properties['restart_command']);
$this->application->io->successLite('Service restarted.');
$task = $this->getProvision()->getBuilder()->taskExec($this->properties['restart_command'])
->silent(!$this->getProvision()->io()->isVerbose())
;
/** @var \Robo\Result $result */
$result = $task->run();
if ($result->wasSuccessful()) {
$this->getProvision()->io()->successLite('Service restarted.');
sleep(1);
return TRUE;
}
catch (\Exception $e) {
$this->application->io->errorLite('Unable to restart service: ' . $e->getMessage());
else {
$this->getProvision()->io()->errorLite('Unable to restart service:' . $result->getOutputData());
}
}
return FALSE;
......@@ -161,13 +203,13 @@ class Service
try {
$config = new $configuration_class($context, $this);
$config->write();
$context->application->io->successLite(
$context->getProvision()->io()->successLite(
'Wrote '.$config->description.' to '.$config->filename()
);
}
catch (\Exception $e) {
$context->application->io->errorLite(
'Unable to write '.$config->description.' to '.$config->filename()
$context->getProvision()->io()->errorLite(
'Unable to write '.$config->description.' to '.$config->filename() . ': ' . $e->getMessage()
);
$success = FALSE;
}
......@@ -202,6 +244,23 @@ class Service
{
return $this::SERVICE;
}
/**
* Get a specific property.
*
* @param $name
* @return mixed
* @throws \Exception
*/
public function getProperty($name) {
if (isset($this->properties[$name])) {
return $this->properties[$name];
}
else {
throw new \Exception("Property '$name' on service '$this->type' does not exist.");
}
}
/**
* Return the SERVICE_TYPE
......@@ -247,4 +306,12 @@ class Service
// ];
}
/**
* @return \Aegir\Provision\Robo\ProvisionBuilder
*/
function getBuilder()
{
return $this->builder;
}
}
\ No newline at end of file
......@@ -34,7 +34,7 @@ class DbMysqlService extends DbService
if ($this->database_exists($test)) {
if (!$this->drop_database($test)) {
$this->provider->application->io->errorLite(strtr("Failed to drop database @dbname", ['@dbname' => $test]));
$this->provider->getProvision()->io()->errorLite(strtr("Failed to drop database @dbname", ['@dbname' => $test]));
}
return TRUE;
}
......
......@@ -10,7 +10,9 @@ namespace Aegir\Provision\Service;
//require_once DRUSH_BASE_PATH . '/commands/core/rsync.core.inc';
use Aegir\Provision\Context\SiteContext;
use Aegir\Provision\Service;
use Aegir\Provision\ServiceInterface;
use Aegir\Provision\ServiceSubscription;
use Symfony\Component\Console\Output\OutputInterface;
......@@ -19,7 +21,7 @@ use Symfony\Component\Console\Output\OutputInterface;
*
* @package Aegir\Provision\Service
*/
class DbService extends Service
class DbService extends Service implements ServiceInterface
{
const SERVICE = 'db';
......@@ -88,7 +90,7 @@ class DbService extends Service
/**
* React to the `provision verify` command on Server contexts
*/
function verify() {
function verifyServer() {
$this->creds = array_map('urldecode', parse_url($this->properties['master_db']));
if (!isset($this->creds['port'])) {
......@@ -104,18 +106,18 @@ class DbService extends Service
try {
$this->connect();
$return = TRUE;
$this->provider->application->io->successLite('Successfully connected to database server!');
$this->provider->getProvision()->io()->successLite('Successfully connected to database server!');
if ($this->can_create_database()) {
$this->provider->application->io->successLite('Provision can create new databases.');
$this->provider->getProvision()->io()->successLite('Provision can create new databases.');
} else {
$this->provider->application->io->errorLite('Provision is unable to create databases.');
$this->provider->getProvision()->io()->errorLite('Provision is unable to create databases.');
$return = FALSE;
}
if ($this->can_grant_privileges()) {
$this->provider->application->io->successLite('Provision can grant privileges on database users.');
$this->provider->getProvision()->io()->successLite('Provision can grant privileges on database users.');
} else {
$this->provider->application->io->errorLite('Provision is unable to grant privileges on database users.');
$this->provider->getProvision()->io()->errorLite('Provision is unable to grant privileges on database users.');
$return = FALSE;
}
......@@ -124,7 +126,7 @@ class DbService extends Service
];
}
catch (\PDOException $e) {
$this->provider->application->io->errorLite($e->getMessage());
$this->provider->getProvision()->io()->errorLite($e->getMessage());
return [
'service' => FALSE
];
......@@ -134,8 +136,12 @@ class DbService extends Service
/**
* React to the `provision verify` command on subscriber contexts (sites and platforms)
*/
function verifySubscription(ServiceSubscription $serviceSubscription) {
$this->subscription = $serviceSubscription;
function verifySite() {
$this->subscription = $this->getContext()->getSubscription($this->type);
// Check for database
$this->create_site_database($this->getContext());
$this->creds_root = array_map('urldecode', parse_url($this->properties['master_db']));
// Use the credentials from the subscription properties.
......@@ -151,19 +157,35 @@ class DbService extends Service
try {
$this->connect();
$this->subscription->context->application->io->successLite('Successfully connected to database server!');
$this->subscription->context->getProvision()->io()->successLite('Successfully connected to database server.');
return [
'service' => TRUE
];
}
catch (\PDOException $e) {
$this->subscription->context->application->io->errorLite($e->getMessage());
$this->subscription->context->getProvision()->io()->errorLite($e->getMessage());
return [
'service' => FALSE
];
}
}
public function verifyPlatform() {
}
/**
* React to `provision verify` command when run on a subscriber, to verify the service's provider.
*
* This is used to verify the database server when a subscriber is verified.
*/
function verifyProvider()
{
return [
'service' => $this->verify(),
];
}
/**
* Attempt to connect to the database server using $this->creds
* @return \PDO
......@@ -198,11 +220,11 @@ class DbService extends Service
$query = preg_replace_callback($this::PROVISION_QUERY_REGEXP, array($this, 'query_callback'), $query);
try {
$this->provider->application->logger->notice("Running Query: {$query}");
$this->provider->getProvision()->getLogger()->info("Running Query: {$query}");
$result = $this->conn->query($query);
}
catch (\PDOException $e) {
$this->provider->application->io->errorLite($e->getMessage());
$this->provider->getProvision()->getLogger()->error($e->getMessage());
return FALSE;
}
......@@ -231,6 +253,15 @@ class DbService extends Service
}
}
/**
* Return the credentials array.
*
* @return array
*/
public function getCreds() {
return $this->creds;
}
//
// /**
......@@ -338,75 +369,62 @@ class DbService extends Service
//
// return false;
// }
//
// /**
// * Generate a new mysql database and user account for the specified
// * credentials
// */
// function create_site_database($creds = [])
// {
// if (!sizeof($creds)) {
// $creds = $this->generate_site_credentials();
// }
// extract($creds);
//
// if (drush_get_error() || !$this->can_create_database()) {
// drush_set_error('PROVISION_CREATE_DB_FAILED');
// drush_log("Database could not be created.", 'error');
//
// return false;
// }
//
// foreach ($this->grant_host_list() as $db_grant_host) {
// drush_log(
// dt(
// "Granting privileges to %user@%client on %database",
// [
// '%user' => $db_user,
// '%client' => $db_grant_host,
// '%database' => $db_name,
// ]
// )
// );
// if (!$this->grant($db_name, $db_user, $db_passwd, $db_grant_host)) {
// drush_set_error(
// 'PROVISION_CREATE_DB_FAILED',
// dt(
// "Could not create database user @user",
// ['@user' => $db_user]
// )
// );
// }
// drush_log(
// dt(
// "Granted privileges to %user@%client on %database",
// [
// '%user' => $db_user,
// '%client' => $db_grant_host,
// '%database' => $db_name,
// ]
// ),
// 'success'
// );
// }
//
// $this->create_database($db_name);
// $status = $this->database_exists($db_name);
//
// if ($status) {
// drush_log(
// dt('Created @name database', ["@name" => $db_name]),
// 'success'
// );
// } else {
// drush_set_error(
// 'PROVISION_CREATE_DB_FAILED',
// dt("Could not create @name database", ["@name" => $db_name])
// );
// }
//
// return $status;
// }
/**
* Generate a new mysql database and user account for the specified
* credentials
*/
function create_site_database(SiteContext $site)
{
$db_name = $site->getSubscription('db')->getProperty('db_name');
$db_user = $site->getSubscription('db')->getProperty('db_user');
$db_passwd = $site->getSubscription('db')->getProperty('db_password');
if (!$this->can_create_database()) {
throw new \Exception("Unable to create a database for the site {$site->name}");
}
if ($this->database_exists($db_name)) {
$this->getProvision()->io()->successLite(strtr("Database '@name' already exists.", [
'@name' => $db_name
]));
}
else {
$this->create_database($db_name);
$this->getProvision()->io()->successLite(strtr("Created database '@name'.", [
'@name' => $db_name,
]));
}
foreach ($this->grant_host_list() as $db_grant_host) {
if (!$this->grant($db_name, $db_user, $db_passwd, $db_grant_host)) {
throw new \Exception(strtr("Could not create database user @user", [
'@user' => $db_user
]));
}
$this->getProvision()->io()->successLite(strtr("Granted privileges to user '@user@@host' for database '@name'.", [
'@user' => $db_user,
'@host' => $db_grant_host,
'@name' => $db_name,
]));
}
$status = $this->database_exists($db_name);
if ($status) {
$this->getProvision()->io()->successLite(strtr("Database service configured for site @name.", [
'@name' => $site->name,
]));
}
else {
throw new \Exception(strtr("Could not create @name database", [
"@name" => $db_name
]));
}
return $status;
}
//
// /**
// * Remove the database and user account for the supplied credentials
......@@ -564,15 +582,15 @@ class DbService extends Service
// return false;
// }
//
// function can_create_database()
// {
// return false;
// }
//
// function can_grant_privileges()
// {
// return false;
// }
function can_create_database()
{
return false;
}
function can_grant_privileges()
{
return false;
}
//
// function grant($name, $username, $password, $host = '')
// {
......@@ -593,25 +611,30 @@ class DbService extends Service
// {
// return false;
// }
//
// /**
// * Return a list of hosts, as seen by the db server, which should be granted
// * access to the site database. If server property 'db_grant_all_hosts' is
// * TRUE, use the MySQL wildcard '%' instead of
// */
// function grant_host_list()
// {
// if ($this->server->db_grant_all_hosts) {
/**
* Return a list of hosts, as seen by the db server, which should be granted
* access to the site database. If server property 'db_grant_all_hosts' is
* TRUE, use the MySQL wildcard '%' instead of
*/
function grant_host_list()
{
// @TODO: Implement grant_server_list by injecting $service->subscription. Right now we don't have access to the site context inside a service class.
return ['%'];
// if ($this->getProperty('db_grant_all_hosts')) {
// return ['%'];
// } else {
//
// return array_unique(
// array_map(
// [$this, 'grant_host'],
// $this->context->service('http')->grant_server_list()
// $this->platform->service('http')->grant_server_list()
// )
// );
// }
// }
}
//
// /**
// * Return a hostname suitable for database grants from a server object.
......
......@@ -20,9 +20,8 @@ class PlatformConfiguration extends Configuration {
public $description = 'platform configuration file';
function filename() {
$path = $this->context->application->getConfig()->get('config_path') . '/' . $this->service->getType() . '/platform.d/';
return $path . $this->context->name . '.conf';
$file = $this->context->name . '.conf';
return $this->context->getProvision()->getConfig()->get('config_path') . '/' . $this->service->provider->name . '/' . $this->service->getType() . '/platform.d/' . $file;
}
function process()
......
......@@ -26,7 +26,7 @@ class ServerConfiguration extends Configuration {
function filename() {
if ($this->service->getType()) {
$file = $this->service->getType() . '.conf';
return $this->service->provider->application->getConfig()->get('config_path') . '/' . $this->service->provider->name . '/' . $file;
return $this->service->provider->getProvision()->getConfig()->get('config_path') . '/' . $this->service->provider->name . '/' . $file;
}
else {
return FALSE;
......@@ -35,7 +35,7 @@ class ServerConfiguration extends Configuration {
function process()
{
parent::process();
$app_dir = $this->context->application->getConfig()->get('config_path') . '/' . $this->service->getType();
$app_dir = $this->context->getProvision()->getConfig()->get('config_path') . '/' . $this->context->name . '/' . $this->service->getType();
$this->data['http_port'] = $this->service->properties['http_port'];
$this->data['include_statement'] = '# INCLUDE STATEMENT';
$this->data['http_pred_path'] = "{$app_dir}/pre.d";
......
......@@ -22,49 +22,57 @@ class SiteConfiguration extends Configuration {
function filename() {
$file = $this->uri . '.conf';
// return $this->service->properties['http_platformd_path'] . '/' . ltrim($this->context->name, '@') . '.conf';
return $this->context->application->getConfig()->get('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;
// }
$file = $this->context->getProperty('uri') . '.conf';
return $this->context->getProvision()->getConfig()->get('config_path') . '/' . $this->service->provider->name . '/' . $this->service->getType() . '/vhost.d/' . $file;
}
function process() {
parent::process();
if ($this->aliases && !is_array($this->aliases)) {
$this->aliases = explode(",", $this->aliases);
}
$this->aliases = array_filter($this->aliases, 'trim');
if ($this->drush_aliases && !is_array($this->drush_aliases)) {
$this->drush_aliases = explode(",", $this->drush_aliases);
}
$this->drush_aliases = array_filter($this->drush_aliases, 'trim');
if (!$this->site_enabled) {
$this->template = $this->disabled_template;
}
parent::process();
$this->data['http_port'] = $this->context->platform->getSubscription('http')->service->getProperty('http_port');
$this->data['root'] = $this->context->platform->getProperty('root');
$this->data['uri'] = $this->context->getProperty('uri');
$this->data['site_path'] = $this->data['root'] . '/sites/' . $this->data['uri'];
$app_dir = $this->context->application->getConfig()->get('config_path') . '/' . $this->service->getType();
$this->data['db_type'] = $this->context->getSubscription('db')->service->getType();
// $this->data['http_port'] = $this->service->properties['http_port'];
// $this->data['include_statement'] = '# INCLUDE STATEMENT';
// $this->data['http_pred_path'] = "{$app_dir}/pre.d";
// $this->data['http_postd_path'] = "{$app_dir}/post.d";
// $this->data['http_platformd_path'] = "{$app_dir}/platform.d";
// $this->data['extra_config'] = "";
// print_r($this->context->getSubscription('db'));
$this->data['http_vhostd_path'] = "{$app_dir}/vhost.d";
$this->data['db_name'] = $this->context->getSubscription('db')->getProperty('db_name');
$this->data['db_user'] = $this->context->getSubscription('db')->getProperty('db_user');
$this->data['db_passwd'] = $this->context->getSubscription('db')->getProperty('db_password');
$this->data['db_host'] = $this->context->getSubscription('db')->service->provider->getProperty('remote_host');
$this->data['db_port'] = $this->context->getSubscription('db')->service->getCreds()['port'];
$this->data['extra_config'] = '';
// if ($this->aliases && !is_array($this->aliases)) {
// $this->aliases = explode(",", $this->aliases);
// }
//
// $this->aliases = array_filter($this->aliases, 'trim');
//
// if ($this->drush_aliases && !is_array($this->drush_aliases)) {
// $this->drush_aliases = explode(",", $this->drush_aliases);
// }
//
// $this->drush_aliases = array_filter($this->drush_aliases, 'trim');
//
// if (!$this->site_enabled) {
// $this->template = $this->disabled_template;
// }
//
// $app_dir = $this->context->application->getConfig()->get('config_path') . '/' . $this->service->getType();
//
//// $this->data['http_port'] = $this->service->properties['http_port'];
//// $this->data['include_statement'] = '# INCLUDE STATEMENT';
//// $this->data['http_pred_path'] = "{$app_dir}/pre.d";
//// $this->data['http_postd_path'] = "{$app_dir}/post.d";
//// $this->data['http_platformd_path'] = "{$app_dir}/platform.d";
//// $this->data['extra_config'] = "";
//
// $this->data['http_vhostd_path'] = "{$app_dir}/vhost.d";
//
}
}
\ No newline at end of file
<VirtualHost *:<?php print $http_port; ?>>
<?php if ($this->site_mail) : ?>
ServerAdmin <?php print $this->site_mail; ?>
<?php if (isset($site_mail)) : ?>
ServerAdmin <?php print $site_mail; ?>
<?php endif;?>
<?php
$aegir_root = drush_get_option('aegir_root');
if (!$aegir_root && $server->aegir_root) {
$aegir_root = $server->aegir_root;
}
//$aegir_root = drush_get_option('aegir_root');
//if (!$aegir_root && $server->aegir_root) {
// $aegir_root = $server->aegir_root;
//}
?>
DocumentRoot <?php print $this->root; ?>
DocumentRoot <?php print $root; ?>
ServerName <?php print $this->uri; ?>
ServerName <?php print $uri; ?>
SetEnv db_type <?php print urlencode($db_type); ?>
......@@ -28,36 +28,36 @@ if (!$aegir_root && $server->aegir_root) {
<?php
if (sizeof($this->aliases)) {
foreach ($this->aliases as $alias) {
print " ServerAlias " . $alias . "\n";
}
}
//if (sizeof($this->aliases)) {
// foreach ($this->aliases as $alias) {
// print " ServerAlias " . $alias . "\n";
// }
//}
?>
<IfModule mod_rewrite.c>
RewriteEngine on
<?php
if ($this->redirection || $ssl_redirection) {
if ($ssl_redirection && !$this->redirection) {
print " # Redirect aliases in non-ssl to the same alias on ssl.\n";
print " RewriteRule ^/*(.*)$ https://%{HTTP_HOST}/$1 [NE,L,R=301]\n";
}
elseif ($ssl_redirection && $this->redirection) {
print " # Redirect all aliases + main uri to the main https uri.\n";
print " RewriteRule ^/*(.*)$ https://{$this->uri}/$1 [NE,L,R=301]\n";
}
elseif (!$ssl_redirection && $this->redirection) {
print " # Redirect all aliases to the main http url.\n";
print " RewriteCond %{HTTP_HOST} !^{$this->redirection}$ [NC]\n";
print " RewriteRule ^/*(.*)$ http://{$this->redirection}/$1 [NE,L,R=301]\n";
}
}
//if ($this->redirection || $ssl_redirection) {
//
// if ($ssl_redirection && !$this->redirection) {
// print " # Redirect aliases in non-ssl to the same alias on ssl.\n";
// print " RewriteRule ^/*(.*)$ https://%{HTTP_HOST}/$1 [NE,L,R=301]\n";
// }
// elseif ($ssl_redirection && $this->redirection) {
// print " # Redirect all aliases + main uri to the main https uri.\n";
// print " RewriteRule ^/*(.*)$ https://{$this->uri}/$1 [NE,L,R=301]\n";
// }
// elseif (!$ssl_redirection && $this->redirection) {
// print " # Redirect all aliases to the main http url.\n";
// print " RewriteCond %{HTTP_HOST} !^{$this->redirection}$ [NC]\n";
// print " RewriteRule ^/*(.*)$ http://{$this->redirection}/$1 [NE,L,R=301]\n";
// }
//}
?>
RewriteRule ^/files/(.*)$ /sites/<?php print $this->uri; ?>/files/$1 [L]
RewriteCond <?php print $this->site_path; ?>/files/robots.txt -f
RewriteRule ^/robots.txt /sites/<?php print $this->uri; ?>/files/robots.txt [L]
RewriteRule ^/files/(.*)$ /sites/<?php print $uri; ?>/files/$1 [L]
RewriteCond <?php print $site_path; ?>/files/robots.txt -f
RewriteRule ^/robots.txt /sites/<?php print $uri; ?>/files/robots.txt [L]
</IfModule>
<?php print $extra_config; ?>
......@@ -79,7 +79,7 @@ if ($this->redirection || $ssl_redirection) {
# Prevent direct reading of files in the private dir.
# This is for Drupal7 compatibility, which would normally drop
# a .htaccess in those directories, but we explicitly ignore those
<Directory "<?php print $this->site_path; ?>/private/" >
<Directory "<?php print $site_path; ?>/private/" >
<Files *>
SetHandler This_is_a_Drupal_security_line_do_not_remove
</Files>
......@@ -94,10 +94,10 @@ if ($this->redirection || $ssl_redirection) {
</Directory>
<?php
$if_subsite = $this->data['http_subdird_path'] . '/' . $this->uri;
if (provision_hosting_feature_enabled('subdirs') && provision_file()->exists($if_subsite)->status()) {
print " Include " . $if_subsite . "/*.conf\n";
}
//$if_subsite = $this->data['http_subdird_path'] . '/' . $this->uri;
//if (provision_hosting_feature_enabled('subdirs') && provision_file()->exists($if_subsite)->status()) {
// print " Include " . $if_subsite . "/*.conf\n";
//}
?>
</VirtualHost>
<?php
/**
* @file
* The Provision HttpApacheService class.
*
* @see \Provision_Service_http_apache
*/
namespace Aegir\Provision\Service\Http;
use Aegir\Provision\Robo\ProvisionCollectionBuilder;
use Aegir\Provision\Service\Http\Apache\Configuration\PlatformConfiguration;
use Aegir\Provision\Service\Http\Apache\Configuration\ServerConfiguration;
use Aegir\Provision\Service\Http\Apache\Configuration\SiteConfiguration;
use Aegir\Provision\Service\HttpService;
use Aegir\Provision\ServiceSubscription;
use Robo\Result;
use Aegir\Provision\Service\Http\Php\PhpServer;
use Symfony\Component\Process\Process;
/**
* Class HttpPhpService
*
* @package Aegir\Provision\Service\Http
*/
class HttpPhpService extends HttpService
{
const SERVICE_TYPE = 'php';
const SERVICE_TYPE_NAME = 'PHP Server';
public function verifyServer()
{
$host = $this->getContext()->getProperty('remote_host');
$port = $this->getProperty('http_port');
$this->getProvision()->getLogger()->info('Running server at {host}:{port}', [
'host' => $host,
'port' => $port,
]);
//
$this->getContext()->getBuilder()->build(PhpServer::class, ['port' => $port]);
// $server = new PhpServer($port);
/** @var PhpServer $server */
$server = $this->getContext()->getBuilder()->task(PhpServer::class, ['port' => $port]);
$server->host($host);
$server->dir(__DIR__ . '/Php/servertest');
$server->background();
$server->run();
//
$pid = $server->getProcess()->getPid();
$this->getProvision()->getLogger()->info('Server started at {host}:{port} running as process {pid}', [
'host' => $host,
'port' => $port,
'pid' => $pid,
]);
}
public function verifySite()
{
}
public function verifySubscription(ServiceSubscription $subscription)
{
print_r($subscription->context->getProperty('root'));
$this->application->getBuilder()->taskServer($this->getProperty('http_port'))
->dir('.');;
}
}
<?php
namespace Aegir\Provision\Service\Http\Php;
use Robo\Common\ExecCommand;
use Robo\Common\ExecTrait;
class PhpServer extends \Robo\Task\Development\PhpServer {
/**
* @var int
*/
protected $port;
/**
* @var string
*/
protected $host = '127.0.0.1';
/**
* @var string
*/
protected $command = 'php -S %s:%d ';
/**
* @param int $port
*/
public function __construct($port)
{
$this->port = $port;
if (strtolower(PHP_OS) === 'linux') {
$this->command = 'exec php -S %s:%d ';
}
}
/**
* @param string $host
*
* @return $this
*/
public function host($host)
{
$this->host = $host;
return $this;
}
/**
* @param string $path
*
* @return $this
*/
public function dir($path)
{
$this->command .= "-t $path";
return $this;
}
/**
* {@inheritdoc}
*/
public function getCommand()
{
return sprintf($this->command, $this->host, $this->port);
}
public function run() {
$this->executeCommand($this->getCommand());
}
public function getProcess() {
return $this->process;
}
public function task() {
}
}
\ No newline at end of file
<h1><?php print "It Works!"; ?></h1>
<p>
<?php print "This page is being generated by PHP web server." ?>
</p>