summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJon Pugh2018-03-26 20:45:59 (GMT)
committerJon Pugh2018-03-26 20:45:59 (GMT)
commit70754c7c40275899b0b3a492fc1a88770d931c36 (patch)
tree235287340fbbc0ca4dad375895247650c8b63696
parent3431be04c445f01c2dd40ffae06acaab95613910 (diff)
- Adding ability to specify context with "@alias" names, just like drush and provision 3.x.
- Use this new ability to pre-load the context to pre-load Robo Command files from services! Issue #26 - Add docker:command, docker:logs, and docker:shell commands to Contexts that are using Docker.
-rw-r--r--bin/provision-robo.php6
-rw-r--r--src/Provision/Application.php8
-rw-r--r--src/Provision/Command.php10
-rw-r--r--src/Provision/Command/SaveCommand.php22
-rw-r--r--src/Provision/Command/ServicesCommand.php32
-rw-r--r--src/Provision/Command/StatusCommand.php11
-rw-r--r--src/Provision/Command/VerifyCommand.php10
-rw-r--r--src/Provision/Console/ArgvInput.php37
-rw-r--r--src/Provision/Context.php53
-rw-r--r--src/Provision/Provision.php20
-rw-r--r--src/Provision/Service/Http/ApacheDocker/ApacheDockerCommands.php80
-rw-r--r--src/Provision/Service/Http/HttpApacheDockerService.php6
12 files changed, 221 insertions, 74 deletions
diff --git a/bin/provision-robo.php b/bin/provision-robo.php
index f131d00..a3a8049 100644
--- a/bin/provision-robo.php
+++ b/bin/provision-robo.php
@@ -22,7 +22,7 @@ use Aegir\Provision\Common\NotSetupException;
use Aegir\Provision\Console\ProvisionStyle;
use Robo\Common\TimeKeeper;
-use Symfony\Component\Console\Input\ArgvInput;
+use Aegir\Provision\Console\ArgvInput;
use Symfony\Component\Console\Exception\InvalidOptionException;
use Symfony\Component\Console\Exception\CommandNotFoundException;
@@ -68,10 +68,10 @@ try {
}
// Create the app.
- $app = new \Aegir\Provision\Provision($config, $input, $output);
+ $provision = new \Aegir\Provision\Provision($config, $input, $output);
// Run the app.
- $status_code = $app->run($input, $output);
+ $status_code = $provision->run($input, $output);
}
catch (Exception $e) {
diff --git a/src/Provision/Application.php b/src/Provision/Application.php
index 03391d1..19c302e 100644
--- a/src/Provision/Application.php
+++ b/src/Provision/Application.php
@@ -176,7 +176,7 @@ class Application extends BaseApplication
$exitCode = parent::doRunCommand($command, $input, $output);
return $exitCode;
}
-
+
/**
* {@inheritdoc}
*
@@ -187,9 +187,9 @@ class Application extends BaseApplication
$inputDefinition = parent::getDefaultInputDefinition();
$inputDefinition->addOption(
new InputOption(
- '--target',
- '-t',
- InputOption::VALUE_NONE,
+ '--context',
+ '-c',
+ InputOption::VALUE_OPTIONAL,
'The target context to act on.'
)
);
diff --git a/src/Provision/Command.php b/src/Provision/Command.php
index 8ddf5f6..505be42 100644
--- a/src/Provision/Command.php
+++ b/src/Provision/Command.php
@@ -28,6 +28,8 @@ abstract class Command extends BaseCommand
use ProvisionAwareTrait;
use LoggerAwareTrait;
+ const CONTEXT_REQUIRED = FALSE;
+
/**
* @var \Symfony\Component\Console\Input\InputInterface
*/
@@ -72,11 +74,11 @@ abstract class Command extends BaseCommand
$this->io = new ProvisionStyle($input, $output);
// Load active context if a command uses the argument.
- if ($this->input->hasArgument('context_name') && !empty($this->input->getArgument('context_name'))) {
+ if ($this->input->getOption('context') && !empty($this->input->getOption('context'))) {
try {
// Load context from context_name argument.
- $this->context_name = $this->input->getArgument('context_name');
+ $this->context_name = $this->input->getOption('context');
$this->context = $this->getProvision()->getContext($this->context_name);
}
catch (\Exception $e) {
@@ -94,9 +96,9 @@ abstract class Command extends BaseCommand
}
// If context_name is not specified, ask for it.
- elseif ($this->input->hasArgument('context_name') && $this->getDefinition()->getArgument('context_name')->isRequired() && empty($this->input->getArgument('context_name'))) {
+ elseif ($this::CONTEXT_REQUIRED && empty($this->input->getOption('context'))) {
$this->askForContext();
- $this->input->setArgument('context_name', $this->context_name);
+ $this->input->setOption('context', $this->context_name);
try {
$this->context = $this->getProvision()->getContext($this->context_name);
diff --git a/src/Provision/Command/SaveCommand.php b/src/Provision/Command/SaveCommand.php
index d3c1f86..ef1c5ac 100644
--- a/src/Provision/Command/SaveCommand.php
+++ b/src/Provision/Command/SaveCommand.php
@@ -27,7 +27,12 @@ use Symfony\Component\Console\Output\OutputInterface;
*/
class SaveCommand extends Command
{
-
+
+ /**
+ * This command needs a context.
+ */
+ const CONTEXT_REQUIRED = TRUE;
+
/**
* @var string
*/
@@ -61,11 +66,6 @@ class SaveCommand extends Command
protected function getCommandDefinition()
{
$inputDefinition = ServicesCommand::getCommandOptions();
- $inputDefinition[] = new InputArgument(
- 'context_name',
- InputArgument::REQUIRED,
- 'Context to save'
- );
$inputDefinition[] = new InputOption(
'context_type',
null,
@@ -135,7 +135,7 @@ class SaveCommand extends Command
protected function initialize(InputInterface $input, OutputInterface $output)
{
parent::initialize($input,$output);
- $this->context_name = $input->getArgument('context_name');
+ $this->context_name = $input->getOption('context');
$this->context_type = $input->getOption('context_type');
}
@@ -199,7 +199,7 @@ class SaveCommand extends Command
$options['type'] = $this->context_type;
$class = Context::getClassName($this->input->getOption('context_type'));
- $this->context = new $class($input->getArgument('context_name'), $this->getProvision(), $options);
+ $this->context = new $class($input->getOption('context'), $this->getProvision(), $options);
}
else {
$this->getProvision()->io()->helpBlock("Editing context {$this->context->name}...", ProvisionStyle::ICON_EDIT);
@@ -257,7 +257,7 @@ class SaveCommand extends Command
$this->io->warningLite('Context not saved.');
return;
}
-// $command = 'drush provision-save '.$input->getArgument('context_name');
+// $command = 'drush provision-save '.$input->getOption('context');
// $this->process($command);
// If editing a context, exit here.
@@ -437,7 +437,7 @@ class SaveCommand extends Command
}
$command = $this->getApplication()->find('services');
$arguments = [
- 'context_name' => $this->input->getArgument('context_name'),
+ 'context_name' => $this->input->getOption('context'),
'sub_command' => 'add',
];
while ($this->io->confirm('Add a service?')) {
@@ -468,7 +468,7 @@ class SaveCommand extends Command
$command = $this->getApplication()->find('services');
$arguments = [
- 'context_name' => $this->input->getArgument('context_name'),
+ 'context_name' => $this->input->getOption('context'),
'sub_command' => 'add',
'service' => $type,
];
diff --git a/src/Provision/Command/ServicesCommand.php b/src/Provision/Command/ServicesCommand.php
index 1e1b1f5..c777444 100644
--- a/src/Provision/Command/ServicesCommand.php
+++ b/src/Provision/Command/ServicesCommand.php
@@ -29,6 +29,11 @@ class ServicesCommand extends Command
{
/**
+ * This command needs a context.
+ */
+ const CONTEXT_REQUIRED = TRUE;
+
+ /**
* "list" (default), "add", "remove", or "configure"
* @var string
*/
@@ -45,7 +50,8 @@ class ServicesCommand extends Command
->setHelp(
'Use this command to add new services to servers, or to add service subscriptions to platforms and sites.'
)
- ->setDefinition($this->getCommandDefinition());
+ ->setDefinition($this->getCommandDefinition())
+ ;
}
/**
@@ -56,11 +62,7 @@ class ServicesCommand extends Command
protected function getCommandDefinition()
{
$inputDefinition = $this::getCommandOptions();
- $inputDefinition[] = new InputArgument(
- 'context_name',
- InputArgument::REQUIRED,
- 'Server to work on.'
- );
+
$inputDefinition[] = new InputArgument(
'sub_command',
InputArgument::OPTIONAL,
@@ -83,7 +85,6 @@ class ServicesCommand extends Command
InputOption::VALUE_OPTIONAL,
'The name of the service type to use.'
);
-
return new InputDefinition($inputDefinition);
}
@@ -130,14 +131,15 @@ class ServicesCommand extends Command
InputInterface $input,
OutputInterface $output
) {
-
- if ($input->getArgument('context_name') == 'add') {
- $this->sub_command = $input->getArgument('context_name');
- $input->setArgument('context_name', NULL);
- }
- else {
- $this->sub_command = $input->getArgument('sub_command');
- }
+//
+// if ($input->getOption('context_name') == 'add') {
+// $this->sub_command = $input->getArgument('context_name');
+// $input->setArgument('context_name', NULL);
+// }
+// else {
+// $this->sub_command = $input->getArgument('sub_command');
+// }
+ $this->sub_command = $input->getArgument('sub_command');
parent::initialize(
$input,
diff --git a/src/Provision/Command/StatusCommand.php b/src/Provision/Command/StatusCommand.php
index 9e1e5ac..d43e377 100644
--- a/src/Provision/Command/StatusCommand.php
+++ b/src/Provision/Command/StatusCommand.php
@@ -32,13 +32,6 @@ class StatusCommand extends Command
->setName('status')
->setDescription('Display system status.')
->setHelp('Lists helpful information about your system.')
- ->setDefinition([
- new InputArgument(
- 'context_name',
- InputArgument::OPTIONAL,
- 'Context to show info for.'
- )
- ])
;
}
@@ -49,14 +42,14 @@ class StatusCommand extends Command
{
$this->getProvision();
- if ($input->getArgument('context_name')) {
+ if ($input->getOption('context')) {
$rows = [['Configuration File', $this->context->config_path]];
foreach ($this->context->getProperties() as $name => $value) {
if (is_string($value)) {
$rows[] = [$name, $value];
}
}
- $this->io->table(['Provision Context:', $input->getArgument('context_name')], $rows);
+ $this->io->table(['Provision Context:', $input->getOption('context')], $rows);
// Display services.
$this->context->showServices($this->io);
diff --git a/src/Provision/Command/VerifyCommand.php b/src/Provision/Command/VerifyCommand.php
index 52a4b53..99fce94 100644
--- a/src/Provision/Command/VerifyCommand.php
+++ b/src/Provision/Command/VerifyCommand.php
@@ -26,6 +26,11 @@ class VerifyCommand extends Command
{
/**
+ * This command needs a context.
+ */
+ const CONTEXT_REQUIRED = TRUE;
+
+ /**
* {@inheritdoc}
*/
protected function configure()
@@ -47,11 +52,6 @@ class VerifyCommand extends Command
protected function getCommandDefinition()
{
$inputDefinition = [];
- $inputDefinition[] = new InputArgument(
- 'context_name',
- InputArgument::REQUIRED,
- 'Context to verify'
- );
return new InputDefinition($inputDefinition);
}
diff --git a/src/Provision/Console/ArgvInput.php b/src/Provision/Console/ArgvInput.php
new file mode 100644
index 0000000..72392ef
--- /dev/null
+++ b/src/Provision/Console/ArgvInput.php
@@ -0,0 +1,37 @@
+<?php
+
+namespace Aegir\Provision\Console;
+
+use \Symfony\Component\Console\Input\ArgvInput as ArgvInputBase;
+use \Symfony\Component\Console\Input\InputDefinition;
+
+class ArgvInput extends ArgvInputBase {
+
+ /**
+ * @var string The name of the active context, extracted from the first argument if it has "@" prefix.
+ */
+ public $activeContextName = NULL;
+
+ /**
+ * @param array|null $argv An array of parameters from the CLI (in the argv format)
+ * @param InputDefinition|null $definition A InputDefinition instance
+ */
+ public function __construct(array $argv = null, InputDefinition $definition = null)
+ {
+ // If @alias is used, swap it out with --context=
+ if (isset($argv[1]) && strpos($argv[1], '@') === 0) {
+ $context_name = ltrim($argv[1], '@');
+ $argv[1] = "--context={$context_name}";
+ $this->activeContextName = $context_name;
+ }
+ // If --context option is used, use that.
+ elseif ($argv_filtered = array_filter($argv, function ($key) {
+ return strpos($key, '--context') === 0;
+ })) {
+ $context_option = array_pop($argv_filtered);
+ $context_name = substr($context_option, strlen('--context='));
+ $this->activeContextName = $context_name;
+ }
+ parent::__construct($argv, $definition);
+ }
+} \ No newline at end of file
diff --git a/src/Provision/Context.php b/src/Provision/Context.php
index 9b43fab..10b5e54 100644
--- a/src/Provision/Context.php
+++ b/src/Provision/Context.php
@@ -53,7 +53,7 @@ class Context implements BuilderAwareInterface
*/
public $type = null;
const TYPE = null;
-
+
/**
* The role of this context, either 'subscriber' or 'provider'.
*
@@ -85,7 +85,7 @@ class Context implements BuilderAwareInterface
* @var \Symfony\Component\Filesystem\Filesystem
*/
public $fs;
-
+
/**
* Context constructor.
*
@@ -98,13 +98,13 @@ class Context implements BuilderAwareInterface
$options = [])
{
$this->name = $name;
-
+
$this->setProvision($provision);
$this->setBuilder($this->getProvision()->getBuilder());
-
+
$this->loadContextConfig($options);
$this->prepareServices();
-
+
$this->fs = new Filesystem();
}
@@ -143,14 +143,14 @@ class Context implements BuilderAwareInterface
}
}
}
-
+
$this->properties['type'] = $this->type;
$this->properties['name'] = $this->name;
-
+
$configs[] = $this->properties;
$this->config = $processor->processConfiguration($this, $configs);
-
+
} catch (\Exception $e) {
throw new InvalidOptionException(
strtr("There is an error with the configuration for !type '!name'. Check the file !file and try again. \n \nError: !message", [
@@ -392,7 +392,7 @@ class Context implements BuilderAwareInterface
->end()
->end();
}
-
+
// Load contextRequirements into config as ContextNodes.
foreach ($this->contextRequirements() as $property => $type) {
$root_node
@@ -405,14 +405,14 @@ class Context implements BuilderAwareInterface
->end()
->end();
}
-
+
if (method_exists($this, 'configTreeBuilder')) {
$this->configTreeBuilder($root_node);
}
return $tree_builder;
}
-
+
/**
* Prepare either services or service subscriptions config tree.
*/
@@ -457,15 +457,15 @@ class Context implements BuilderAwareInterface
* Output a list of all services for this context.
*/
public function showServices(DrupalStyle $io) {
-
+
$services = $this->isProvider()? $this->getServices(): $this->getSubscriptions();
if (!empty($services)) {
$rows = [];
-
+
$headers = $this->isProvider()?
['Services']:
['Service', 'Server', 'Type'];
-
+
foreach ($services as $name => $service) {
if ($this::ROLE == 'provider') {
$rows[] = [$name, $service->type];
@@ -535,11 +535,11 @@ class Context implements BuilderAwareInterface
*/
public function save()
{
-
+
// Create config folder if it does not exist.
$fs = new Filesystem();
$dumper = new Dumper();
-
+
try {
$fs->dumpFile($this->config_path, $dumper->dump($this->getProperties(), 10));
return true;
@@ -564,7 +564,7 @@ class Context implements BuilderAwareInterface
return false;
}
}
-
+
/**
* Retrieve the class name of a specific context type.
*
@@ -581,7 +581,7 @@ class Context implements BuilderAwareInterface
// public function verify() {
// return "Provision Context";
// }
-
+
/**
* Verify this context.
*
@@ -620,9 +620,9 @@ class Context implements BuilderAwareInterface
}
$tasks = [];
}
-
+
$result = $collection->run();
-
+
if ($result->wasSuccessful()) {
$this->getProvision()->io()->success('Verification Complete!');
}
@@ -793,7 +793,7 @@ class Context implements BuilderAwareInterface
public static function contextRequirements() {
return [];
}
-
+
/**
* Whether or not this context is a provider.
*
@@ -915,4 +915,15 @@ class Context implements BuilderAwareInterface
});
return $process->getExitCode();
}
+
+ /**
+ * return list of command classes.
+ */
+ function getServiceCommandClasses() {
+ $classes = [];
+ foreach ($this->servicesInvoke('getCommandClasses') as $class) {
+ $classes += $class;
+ }
+ return $classes;
+ }
}
diff --git a/src/Provision/Provision.php b/src/Provision/Provision.php
index 1b15997..3dd8883 100644
--- a/src/Provision/Provision.php
+++ b/src/Provision/Provision.php
@@ -8,6 +8,7 @@ use Aegir\Provision\Commands\ExampleCommands;
use Aegir\Provision\Console\ConsoleOutput;
use Aegir\Provision\Console\ProvisionStyle;
+use Aegir\Provision\Console\ArgvInput;
use Aegir\Provision\Context\ServerContext;
use Aegir\Provision\Robo\ProvisionCollectionBuilder;
use Aegir\Provision\Robo\ProvisionExecutor;
@@ -30,7 +31,6 @@ use Robo\Robo;
use Robo\Runner as RoboRunner;
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
use Symfony\Component\Console\Exception\InvalidOptionException;
-use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Logger\ConsoleLogger;
use Symfony\Component\Console\Output\OutputInterface;
@@ -88,6 +88,11 @@ class Provision implements ConfigAwareInterface, ContainerAwareInterface, Logger
* @var \Aegir\Provision\Context[]
*/
public $contexts = [];
+
+ /**
+ * @var \Aegir\Provision\Context The currently active context.
+ */
+ public $activeContext;
/**
* @var array[]
@@ -150,6 +155,10 @@ class Provision implements ConfigAwareInterface, ContainerAwareInterface, Logger
$this->console->setProvision($this);
$this->loadAllContexts();
+
+ if ($input->activeContextName) {
+ $this->activeContext = $this->getContext($input->activeContextName);
+ }
}
/**
@@ -208,7 +217,14 @@ class Provision implements ConfigAwareInterface, ContainerAwareInterface, Logger
}
public function run(InputInterface $input, OutputInterface $output) {
- $status_code = $this->runner->run($input, $output);
+
+ $commandClasses = [];
+
+ // Look up any robofiles in each service.
+ if ($this->activeContext && method_exists($this->activeContext, 'getServiceCommandClasses')) {
+ $commandClasses += $this->activeContext->getServiceCommandClasses();
+ }
+ $status_code = $this->runner->run($input, $output, $this->getApplication(), $commandClasses);
return $status_code;
}
diff --git a/src/Provision/Service/Http/ApacheDocker/ApacheDockerCommands.php b/src/Provision/Service/Http/ApacheDocker/ApacheDockerCommands.php
new file mode 100644
index 0000000..1cd1396
--- /dev/null
+++ b/src/Provision/Service/Http/ApacheDocker/ApacheDockerCommands.php
@@ -0,0 +1,80 @@
+<?php
+
+namespace Aegir\Provision\Service\Http\ApacheDocker;
+
+use Symfony\Component\Console\Input\ArgvInput;
+
+/**
+ * This is project's console commands configuration for Robo task runner.
+ *
+ * @see http://robo.li/
+ */
+class ApacheDockerCommands extends \Robo\Tasks
+{
+
+ /**
+ * @param $context_name The context name to act on.
+ * @param array $opts
+ *
+ * @throws \Psr\Container\ContainerExceptionInterface
+ * @throws \Psr\Container\NotFoundExceptionInterface
+ */
+ public function dockerCommand(
+ $docker_compose_command,
+ $opts = [
+ 'context' => 'The site or server to run the docker command in.',
+ 'docker-compose-options' => 'the options to pass to docker compose '
+ ]
+ ) {
+ /** @var \Aegir\Provision\Application $application */
+ $application = $this->getContainer()->get('application');
+
+ /** @var \Robo\Log\RoboLogger $logger */
+ $logger = $this->getContainer()->get('logger');
+
+ /** @var ArgvInput $input */
+ $input = $this->getContainer()->get('input');
+
+ $logger->info('Hi! This is a robo command.');
+
+ $process = new \Symfony\Component\Process\Process("docker-compose {$docker_compose_command}");
+
+ $wd = $this->getContainer()->get('application')->getProvision()->activeContext == 'server'?
+ $this->getContainer()->get('application')->getProvision()->activeContext->server_config_path:
+ $this->getContainer()->get('application')->getProvision()->activeContext->service('http')->provider->server_config_path;
+
+ $process->setWorkingDirectory($wd);
+ $process->setTty(TRUE);
+ $process->run();
+ }
+
+
+ /**
+ * Stream logs from the containers using docker-compose logs -f
+ */
+ public function dockerLogs() {
+ $process = new \Symfony\Component\Process\Process("docker-compose logs -f");
+
+ $wd = $this->getContainer()->get('application')->getProvision()->activeContext == 'server'?
+ $this->getContainer()->get('application')->getProvision()->activeContext->server_config_path:
+ $this->getContainer()->get('application')->getProvision()->activeContext->service('http')->provider->server_config_path;
+
+ $process->setWorkingDirectory($wd);
+ $process->setTty(TRUE);
+ $process->run(); }
+
+ /**
+ * Enter a bash shell in the web server container.
+ */
+ public function dockerShell() {
+ $process = new \Symfony\Component\Process\Process("docker-compose exec http bash");
+
+ $wd = $this->getContainer()->get('application')->getProvision()->activeContext == 'server'?
+ $this->getContainer()->get('application')->getProvision()->activeContext->server_config_path:
+ $this->getContainer()->get('application')->getProvision()->activeContext->service('http')->provider->server_config_path;
+
+ $process->setWorkingDirectory($wd);
+ $process->setTty(TRUE);
+ $process->run();
+ }
+} \ No newline at end of file
diff --git a/src/Provision/Service/Http/HttpApacheDockerService.php b/src/Provision/Service/Http/HttpApacheDockerService.php
index 6aa6f43..cc8b841 100644
--- a/src/Provision/Service/Http/HttpApacheDockerService.php
+++ b/src/Provision/Service/Http/HttpApacheDockerService.php
@@ -470,4 +470,10 @@ ENV;
$lines[] = " CustomLog /var/log/provision.log custom";
return implode("\n", $lines);
}
+
+ public function getCommandClasses() {
+ return [
+ \Aegir\Provision\Service\Http\ApacheDocker\ApacheDockerCommands::class
+ ];
+ }
}