...
 
Commits (169)
......@@ -6,6 +6,9 @@ GRTAGS
GSYMS
GTAGS
# Documentation
docs
# Development
.idea
......
......@@ -40,11 +40,12 @@ before_install:
- 'echo "web_user: docker" >> ~/.provision.yml'
- 'echo "web_user_uid: 999" >> ~/.provision.yml'
- 'echo "config_path: /home/travis/config" >> ~/.provision.yml'
- 'echo "contexts_path: /home/travis/config/contexts" >> ~/.provision.yml'
install:
- composer install
- sudo ln -s $PWD/bin/provision /usr/local/bin/provision
- mkdir /home/travis/config
- mkdir /home/travis/config/contexts -p
- ls -la
script:
......@@ -54,28 +55,28 @@ script:
- provision status -n
# Add server context.
- provision save server_master -n
- provision save --context=server_master -n
--context_type=server
--remote_host=provision.local.computer
--aegir_root=/var/aegir
--script_user=aegir
# Add services to server_master context.
- provision services server_master add http -n
- provision @server_master services add http -n
--service_type=apacheDocker
--http_port=80
--web_group=www-data
--restart_command="sudo apache2ctl graceful"
- provision services server_master add db -n
- provision @server_master services add db -n
--service_type=mysqlDocker
--master_db="mysql://root:root@db:3306"
--db_grant_all_hosts=1
- provision services server_master
- provision @server_master services
# Add platform context
- provision save platform_hostmaster -n
- provision save --context=platform_hostmaster -n
--context_type=platform
--root=/home/travis/hostmaster
--server_http=server_master
......@@ -84,10 +85,10 @@ script:
# add http service to platform.
# @TODO: This should already be done thanks to --server_http!!!
- provision services platform_hostmaster add http server_master
- provision @platform_hostmaster services add http server_master
# Add site context
- provision save hostmaster -n
- provision save --context=hostmaster -n
--context_type=site
--platform=platform_hostmaster
--server_db=server_master
......@@ -99,7 +100,7 @@ script:
--db_password=drupal
# Add a site context without a platform!
- provision save eight.local.computer -n
- provision save --context=eight.local.computer -n
--context_type=site
--server_db=server_master
--server_http=server_master
......@@ -113,28 +114,28 @@ script:
--makefile=https://raw.githubusercontent.com/aegir-project/provision/7.x-3.x/provision-tests/makes/drupal8.make
- provision status -n
- provision status server_master
- provision status platform_hostmaster
- provision status hostmaster
- provision status eight.local.computer
- provision @server_master status
- provision @platform_hostmaster status
- provision @hostmaster status
- provision @eight.local.computer status
- provision verify server_master
- provision @server_master verify
# Docker creates the host path when docker run happens, in server verify.
# Platform verify uses drush make, which won't run if folder exists.
# @TODO: Figure out how to deal with this within provision.
- sudo rm -rf /home/travis/hostmaster
- provision verify platform_hostmaster
- provision @platform_hostmaster verify
- ls -la /home/travis/hostmaster
- provision verify hostmaster
- provision @hostmaster verify
- sudo rm -rf /home/travis/eight
- provision verify eight.local.computer
- provision @eight.local.computer verify
- provision verify server_master
- provision @server_master verify
- docker ps
- docker logs servermaster_http_1
......
# Provision CLI
# Introduction
![](/assets/server-verify.png)
![](assets/server-verify.png)
Provision is a command-line interface for quickly launching websites on any computer for development, testing, or production.
Provision is awesome because it's designed to work with any system and because it's written in PHP making it familiar to web developers.
Provision is stable because it comes from a long history of automating Drupal hosting within the [Aegir Project](https://www.aegirproject.org/).
Provision is stable because it comes from a long history of automating Drupal hosting within the [Aegir Project](https://www.aegirproject.org/).
The 4.x version is a total rewrite, based on [Symfony Console](https://symfony.com/doc/3.4/console.html) and [Robo](https://robo.li/) components. We are working to make Provision a useful stand-alone command for managing any kind of web app.
......@@ -20,9 +20,9 @@ You add your site's URLs and path to your source code using `provision save`and
## Development
Provision 4.x is a work in progress. If you want open source Drupal hosting now, please see [the Aegir Project](https://www.aegirproject.org/).
Provision 4.x is a work in progress. If you want open source Drupal hosting now, please see [DevShop](https://www.getdevshop.com/) and [the Aegir Project](https://www.aegirproject.org/).
Currently being developed on GitHub: [github.com/aegir-project/provision](https://github.com/aegir-project/provision).
Currently being developed on GitHub: [github.com/provision4/provision](https://github.com/provision4/provision).
Documentation is still in progress, currently available at [aegir.gitbooks.io/provision](https://aegir.gitbooks.io/provision/).
......@@ -67,7 +67,3 @@ More documentation on the new Provision is coming soon. Thanks for your patience
--Jon
##
# Summary
* [Introduction](README.md)
* [Developing Provision](docs/developing-provision.md)
* [Customizing Provision](docs/customizing-provision.md)
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="96mm"
height="96mm"
viewBox="0 0 340.15748 340.15748"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="logo.svg"
inkscape:export-filename="/Users/jon/Projects/aegir4/provision/assets/logo.png"
inkscape:export-xdpi="300"
inkscape:export-ydpi="300">
<defs
id="defs4">
<linearGradient
inkscape:collect="always"
id="linearGradient5617">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop5619" />
<stop
style="stop-color:#ffffff;stop-opacity:0;"
offset="1"
id="stop5621" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5617"
id="linearGradient5627"
x1="303.82538"
y1="343.66541"
x2="456.00577"
y2="343.66541"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(2.6015015,11.692185)" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.0866842"
inkscape:cx="186.05456"
inkscape:cy="160.08285"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:window-width="1366"
inkscape:window-height="698"
inkscape:window-x="0"
inkscape:window-y="1"
inkscape:window-maximized="1" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-72.39051,-136.1473)">
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:900;font-stretch:normal;font-size:135.48083496px;line-height:125%;font-family:'.SF NS Display Condensed';-inkscape-font-specification:'.SF NS Display Condensed, Heavy';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:0;stroke:#3a3a3a;stroke-width:6.455;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="164.60326"
y="300.63254"
id="text4136"
sodipodi:linespacing="125%"
transform="matrix(1.0310589,0,-0.22940975,0.96987669,0,0)"><tspan
sodipodi:role="line"
x="164.60326"
y="300.63254"
id="tspan4146"
style="stroke-width:6.455;stroke-miterlimit:4;stroke-dasharray:none;stroke-linejoin:round;stroke:#3a3a3a;stroke-opacity:1">Pro</tspan></text>
<text
transform="matrix(1.0310589,0,-0.22940975,0.96987669,0,0)"
sodipodi:linespacing="125%"
id="text4171"
y="445.43045"
x="302.11938"
style="font-style:normal;font-variant:normal;font-weight:900;font-stretch:normal;font-size:255.67454529px;line-height:125%;font-family:'.SF NS Display Condensed';-inkscape-font-specification:'.SF NS Display Condensed, Heavy';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:#404040;stroke-width:10.835;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
xml:space="preserve"><tspan
id="tspan4173"
y="445.43045"
x="302.11938"
sodipodi:role="line"
style="fill:#ffffff;fill-opacity:1;stroke-width:10.835;stroke-miterlimit:4;stroke-dasharray:none;stroke-linejoin:round;stroke:#404040;stroke-opacity:1">4</tspan></text>
</g>
</svg>
{
"name": "aegir/provision",
"description": "The CLI for the Aegir Hosting System.",
"name": "provision4/cli",
"description": "The Website Provisioning CLI",
"keywords": ["Hosting", "Drupal", "Aegir"],
"homepage": "http://aegirproject.org/",
"type": "library",
"homepage": "https://github.com/provision4/provision",
"type": "project",
"license": "GPL-2.0+",
"require": {
"consolidation/Robo": "^1.1",
"consolidation/Robo": "dev-block-output",
"consolidation/annotated-command": "~2",
"drupal/console-core": "1.0.2",
"drush/drush": "8.x",
......@@ -15,19 +15,17 @@
"symfony/console": "^3.2",
"symfony/yaml": "^3.2"
},
"repositories": [
{
"type": "vcs",
"url": "git@github.com:provision4/Robo.git"
}
],
"bin": ["bin/provision"],
"authors": [
{
"name": "Provision Contributors",
"homepage": "https://github.com/aegir-project/provision/graphs/contributors"
},
{
"name": "Hosting Contributors",
"homepage": "https://github.com/aegir-project/hosting/graphs/contributors"
},
{
"name": "Hostmaster Contributors",
"homepage": "https://github.com/aegir-project/hostmaster/graphs/contributors"
"homepage": "https://github.com/provision4/provision/graphs/contributors"
},
{
"name": "Jon Pugh",
......@@ -36,12 +34,15 @@
}
],
"support": {
"issues": "https://www.drupal.org/project/issues/provision?categories=All",
"docs": "https://docs.aegirproject.org/"
"issues": "https://github.com/provision4/provision/issues",
"docs": "https://aegir.gitbooks.io/provision/"
},
"config": {
"bin-dir": "bin/",
"sort-packages": true
"sort-packages": true,
"platform": {
"php": "5.5.9"
}
},
"minimum-stability": "dev",
"prefer-stable": true,
......
This diff is collapsed.
......@@ -53,9 +53,11 @@ ENV COMPOSER_VERSION 1b137f8bf6db3e79a38a5bc45324414a6b1f9df2
RUN echo "$RUN_PREFIX Installing Composer version $COMPOSER_VERSION"
RUN wget https://raw.githubusercontent.com/composer/getcomposer.org/$COMPOSER_VERSION/web/installer -O - -q | php -- --quiet
RUN mv composer.phar /usr/local/bin/composer
RUN chmod +x /usr/local/bin/composer
ENV DRUSH_VERSION 8.1.16
RUN wget https://github.com/drush-ops/drush/releases/download/8.1.16/drush.phar -O - -q > /usr/local/bin/drush
RUN chmod +x /usr/local/bin/drush
USER $PROVISION_BASE_USER_NAME
......
......@@ -20,6 +20,11 @@ ARG IMAGE_NAME=http
ARG IMAGE_TAG=php7
FROM provision4/$IMAGE_NAME:$IMAGE_TAG
USER root
RUN echo "$RUN_PREFIX Creating set-user-ids script..."
COPY set-user-ids.sh /usr/local/bin/set-user-ids
RUN chmod +x /usr/local/bin/set-user-ids
ARG PROVISION_USER_UID=1000
ENV PROVISION_USER_UID ${PROVISION_USER_UID:-1001}
ARG PROVISION_WEB_UID=1001
......
......@@ -11,12 +11,17 @@ USER_NAME=$1
USER_UID=$2
WEB_UID=$3
echo "$PREFIX Changing user '$USER_NAME' UID/GID to '$USER_UID'...
echo "$PREFIX Recreating user '$USER_NAME' UID/GID to '$USER_UID'...
"
usermod -u $USER_UID $USER_NAME
groupmod -g $USER_UID $USER_NAME
userdel $USER_NAME
chown $USER_UID:$USER_UID /var/$USER_NAME -R
addgroup --gid $USER_UID $USER_NAME
useradd --no-log-init --uid $USER_UID --gid $USER_UID --system --home-dir /var/$USER_NAME $USER_NAME
echo "$PREFIX Changing user 'www-data' UID/GID to '$WEB_UID'...
"
usermod -u $WEB_UID www-data
groupmod -g $WEB_UID www-data
userdel www-data
addgroup --gid $WEB_UID www-data
useradd --no-log-init --uid $WEB_UID --gid $WEB_UID --system www-data
# Summary
* [Introduction](README.md)
* [Developing Provision](/docs/developing-provision.md "How to contribute to Provision.")
# Customizing Provision
## Docker Services
When using Provision's Docker services, a docker-compose.yml file is automatically generated, using pre-built images for Database & Web servers.
Each server has a "config path" where all server configuration is stored, such as apache config. Check `provision status` for the server config path. The server config folder is filled with files. Most files are generated automaticaly by provision verify. You can create the files labelled \(Optional\) below to customize the behavior of this server's stack.
```
~/.config/provision/$SERVER_NAME
/.env # Generated on provision verify. Includes the COMPOSE_FILE variable to include all found docker-compose yml files.
/.env-custom # (Optional) Included in .env when it is generated. See https://docs.docker.com/compose/reference/envvars/ for available environment variables.
/docker-compose-provision.yml # Generated on provision verify
/docker-compose*.yml # (Optional) Additional files named docker-compose*.yml are detected and written to .env, so any calls to docker-compose in this directory load all files.
/.provision.yml # (Optional) YML file with hooks to run on verify.
/mysql.cnf # (Optional) MySQL configuration can be put into this file.***
/php.ini # (Optional) Custom PHP configuration.****
/php-cli.ini # (Optional) Custom PHP configuration for CLI only.
/Dockerfile.http # (Optional) Custom dockerfile for the http service.*****
/Dockerfile.db # (Optional) Custom dockerfile to use the db service.
/apacheDocker.conf # Generated on provision verify
/apacheDocker
/platform.d # Generated Platform apache configs.
/pre.d # Custom Apache configs can be put in here.
/post.d # Custom Apache configs can be put in here.
/vhost.d # Generated Site virtualhost configs.
/platform.d
```
\*\* The docker-compose command supports automatic merging of docker-compose.yml files, by passing multiple `-f` options. Provision detects if this file is present and automatically adds this for you when you run `provision verify`
\*\*\* mysql.cnf file must follow the right format:
```
[mysqld]
max_allowed_packet = 32M
```
\*\*\*\* The `php.ini` file (if present) is mapped to `/etc/php/7.0/apache2/conf.d/99-provision.ini` and the `php-cli.ini` file is mapped to `/etc/php/7.0/cli/conf.d/99-provision.ini`. Make sure it is also in the right format:
```ini
memory_limit=512M
```
\*\*\*\*\* The http container requires a few specific things to work. You should use `FROM provision4/http` or `FROM provision4/http:php7` as your base image. See the [Dockerfile.user](dockerfiles/Dockerfile.user) file as an example Dockerfile. Copy to ~/.config/provision/$SERVER_NAME/Dockerfile.http, then run `provision verify $SERVER_NAME` to build. If you wish to provide an entirely different http dockerfile, look at [Dockerfile.http.php7](dockerfiles/Dockerfile.http.php7) to learn the requirements. As long as your image has the web server configuration links and the correct sudoers file, Provision shopuld be able to use it.
#### Remember...
After modifying any optional configuration files, run `provision verify` on your server to enable the changes.
## Hooks Files
### .provision.yml
You can put a file called `.provision.yml` in the root of a site, or in the server config folder.
Currently only "pre-verify" hooks are possible.
The format should be like so:
```yml
hooks:
verify:
pre: |
echo "Do something here before verifying anything."
echo "You can use the 'env' command to see what environment variables are available."
echo "Commands are run on the host. If you need to run a command inside a container, use something like:"
echo "docker-compose exec http $COMMAND"
env
echo "Increasing PHP's memory_limit..."
echo "memory_limit=512M" > php.ini
```
There are a few environment variables you might find helpful:
- `PROVISION_CONTEXT` - The name of the server context providing HTTP service.
- `PROVISION_CONTEXT_CONFIG_FILE` - The path on the host to this context's YML configuration file.
- `PROVISION_CONTEXT_SERVER_HTTP` - The name of the server context providing HTTP service.
- `PROVISION_CONTEXT_SERVER_HTTP_CONFIG_PATH` - Full path on the host to the HTTP server's configuration. This is the folder the `docker-compose.yml` file is in. (If this is a server context, the hooks are run in this folder.)
# Developing Provision
One of our missions is to be _Easy to Develop._
This is not just for the core team, but also so others can come in and customize the system to their needs without much difficulty.
## Code
The source code for provision is available at [github.com/aegir-project/provision](https://github.com/aegir-project/provision). The main branch is `4.x`.
The important files and folders are:
* composer.json - Defines the project and the dependencies.
* bin/provision - Executable. Run this to use provision.
* src/ - All the new classes.
* vendor/ - The new vendor directory. All composer libraries get installed here when you run `composer install`.
* .travis.yml - Automated tests for our new branch.
* README.md - The main documentation file.
NOTE: The entire codebase pre-4.x is still in the repository, so \_everything else \_is legacy code.
## Classes
### [class Provision](https://github.com/aegir-project/provision/blob/4.x/src/Provision.php)
Implements:
* ConfigAwareInterface: `$provision->getConfig()`to load the CLI config, optionally loaded from ~/.provision.yml
* ContainerAwareInterface: Uses [container.thephpleague.com](http://container.thephpleague.com) for dependency injection.
* LoggerAwareInterface: `$provision->getLogger()->info()` to `$provision->getLogger()->debug(),` easy PSR logging.
* BuilderAwareInterface: Access to the Robo Builder.
* and more... API docs coming soon.
### [class Context](https://github.com/aegir-project/provision/blob/4.x/src/Context.php)
A Context represents an object we are tracking, either a Server, Platform, or Site.
Each context type has different properties, defined in the `option_documentation()` method.
`$context->save()` will save the context properties into a YML file. Run provision status to reveal the path to a context's YML file.
`$context->verifyCommand() `is triggered when the `provision verify` command is run
### [class ServerContext](https://github.com/aegir-project/provision/blob/4.x/src/Context/ServerContext.php)
ServerContext "provide" services, while all others "subscribe" to them.
Use `ServerContext::shell_exec()` to easily run commands in the Server's config directory, while hiding output, throwing exception with error messages, and showing output when running with `-v`.
###
......@@ -2,12 +2,17 @@
namespace Aegir\Provision;
use Aegir\Provision\Command\CdCommand;
use Aegir\Provision\Command\EditCommand;
use Aegir\Provision\Command\SaveCommand;
use Aegir\Provision\Command\ServicesCommand;
use Aegir\Provision\Command\SetupCommand;
use Aegir\Provision\Command\ShellCommand;
use Aegir\Provision\Command\StatusCommand;
use Aegir\Provision\Command\Ui\CreateUiCommand;
use Aegir\Provision\Command\Ui\UiCreateCommand;
use Aegir\Provision\Command\VerifyCommand;
use Aegir\Provision\Command\InstallCommand;
use Aegir\Provision\Common\ProvisionAwareTrait;
use Aegir\Provision\Console\Config;
use Aegir\Provision\Console\ConsoleOutput;
......@@ -143,14 +148,18 @@ class Application extends BaseApplication
*/
protected function getDefaultCommands()
{
$commands[] = new CdCommand();
$commands[] = new HelpCommand();
$commands[] = new ListCommand();
$commands[] = new SaveCommand();
$commands[] = new EditCommand();
$commands[] = new SetupCommand();
$commands[] = new ServicesCommand();
// $commands[] = new ShellCommand();
$commands[] = new ShellCommand();
$commands[] = new StatusCommand();
$commands[] = new VerifyCommand();
$commands[] = new InstallCommand();
$commands[] = new UiCreateCommand();
return $commands;
}
......
......@@ -28,8 +28,17 @@ abstract class Command extends BaseCommand
use ProvisionAwareTrait;
use LoggerAwareTrait;
/**
* Set if this command requires a context. If so provision will automatically ask for which one if not specified..
*/
const CONTEXT_REQUIRED = FALSE;
/**
* Set if this command is only for certain context types.
*/
const CONTEXT_REQUIRED_TYPES = [];
const CONTEXT_REQUIRED_QUESTION = 'Which context';
/**
* @var \Symfony\Component\Console\Input\InputInterface
*/
......@@ -77,7 +86,7 @@ abstract class Command extends BaseCommand
if ($this->input->getOption('context') && !empty($this->input->getOption('context'))) {
try {
// Load context from context_name argument.
// Load context from context_name option.
$this->context_name = $this->input->getOption('context');
$this->context = $this->getProvision()->getContext($this->context_name);
}
......@@ -85,19 +94,22 @@ abstract class Command extends BaseCommand
// If no context with the specified name is found:
// if this is "save" command and option for --delete is used, throw exception: context must exist to delete.
if ($this->getName() == 'save' && $input->getOption('delete')) {
if ($this->getName() == 'context:save' && $input->getOption('delete')) {
throw new \Exception("No context named {$this->context_name}. Unable to delete.");
}
// If this is any other command, context is required.
elseif ($this->getName() != 'save') {
elseif ($this->getName() != 'context:save') {
throw new InvalidArgumentException($e->getMessage());
}
}
}
// If context_name is not specified, ask for it.
elseif ($this::CONTEXT_REQUIRED && empty($this->input->getOption('context'))) {
$this->askForContext();
elseif (($this::CONTEXT_REQUIRED && empty($this->input->getOption('context')))
|| ($this->getName() == 'save' && empty($this->input->getOption('context')))
) {
$this->askForContext($this::CONTEXT_REQUIRED_QUESTION, $this::CONTEXT_REQUIRED_TYPES);
$this->input->setOption('context', $this->context_name);
try {
......@@ -112,12 +124,12 @@ abstract class Command extends BaseCommand
/**
* Show a list of Contexts to the user for them to choose from.
*/
public function askForContext($question = 'Choose a context') {
public function askForContext($question = self::CONTEXT_REQUIRED_QUESTION, $context_types = array()) {
if (empty($this->getProvision()->getAllContextsOptions())) {
throw new \Exception('No contexts available! use `provision save` to create one.');
}
$this->context_name = $this->io->choice($question, $this->getProvision()->getAllContextsOptions());
$this->context_name = $this->io->choice($question, $this->getProvision()->getAllContextsOptions($context_types));
}
/**
......
<?php
namespace Aegir\Provision\Command;
use Aegir\Provision\Command;
use Aegir\Provision\Provision;
use Psy\Shell;
use Psy\Configuration;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Class CdCommand
*
* @package Aegir\Provision\Command
*/
class CdCommand extends Command
{
const CONTEXT_REQUIRED = TRUE;
/**
* {@inheritdoc}
*/
protected function configure()
{
$this
->setName('cd')
->setDescription('Change into the directory of this site, server, or platform.')
->setHelp('If the chosen context is a site or platform, the `cd` command will put you into the directory of that sites source code. If the chosen context is a server, you will be put into the server config folder.')
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$process = new \Symfony\Component\Process\Process("bash");
$process->setTty(true);
if ($this->context->type == 'site') {
$dir = $this->context->getProperty('root');
}
elseif ($this->context->type == 'server') {
$dir = $this->context->getProperty('server_config_path');
}
if (getenv('SHELL')) {
$shell = getenv('SHELL');
}
else {
$shell = 'bash';
}
$process->setCommandLine("cd $dir && $shell");
$process->setEnv($_SERVER);
$messages[] = "Opening $shell shell in $dir...";
$this->io->commentBlock($messages);
$process->run();
}
}
<?php
namespace Aegir\Provision\Command;
use Aegir\Provision\Application;
use Aegir\Provision\Command;
use Aegir\Provision\Console\ProvisionStyle;
use Aegir\Provision\Context;
use Aegir\Provision\Context\PlatformContext;
use Aegir\Provision\Context\ServerContext;
use Aegir\Provision\Context\SiteContext;
use Aegir\Provision\Property;
use Aegir\Provision\Provision;
use Aegir\Provision\Service;
use Symfony\Component\Console\Exception\InvalidOptionException;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Class EditCommand
*
* @package Aegir\Provision\Command
*/
class EditCommand extends Command
{
const CONTEXT_REQUIRED = TRUE;
/**
* {@inheritdoc}
*/
protected function configure()
{
$this
->setName('context:edit')
->setAliases(['edit'])
->setDescription('Edit a context file')
->setHelp(
'Use this command to interactively setup a new site, platform or server (known as "contexts"). Metadata is saved to .yml files in the "config_path" folder. Once you have create a context, use the `provision status` command to view the list of added contexts.'
);
}
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->context->process_exec('${VISUAL-${EDITOR-vi}} ' . $this->context->config_path);
}
}
<?php
namespace Aegir\Provision\Command;
use Aegir\Provision\Command;
use Aegir\Provision\Context;
use Aegir\Provision\Context\PlatformContext;
use Aegir\Provision\Context\ServerContext;
use Aegir\Provision\Context\SiteContext;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Class VerifyCommand
*
* Replacement for drush provision-verify command
*
* @package Aegir\Provision\Command
* @see provision.drush.inc
* @see drush_provision_verify()
*/
class InstallCommand extends Command
{
/**
* This command needs a context.
*/
const CONTEXT_REQUIRED = TRUE;
const CONTEXT_REQUIRED_TYPES = ['site'];
const CONTEXT_REQUIRED_QUESTION = 'Install which site';
/**
* {@inheritdoc}
*/
protected function configure()
{
$this
->setName('install')
->setDescription('Run the install process for a site.')
->setHelp(
'Run this command to prepare the site or web app, installing database tables and preparing folders, for example.'
)
->setDefinition($this->getCommandDefinition());
}
/**
* Generate the list of options derived from ProvisionContextType classes.
*
* @return \Symfony\Component\Console\Input\InputDefinition
*/
protected function getCommandDefinition()
{
$inputDefinition = [];
$inputDefinition[] = new InputOption(
'option',
null,
InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL,
"Pass install options to the sites install command."
);
$inputDefinition[] = new InputOption(
'skip-verify',
null,
InputOption::VALUE_NONE,
"By default, the 'provision verify' command is run before the install process starts. Pass --skip-verify to skip it."
);
return new InputDefinition($inputDefinition);
}
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->io->title(strtr("Verify %type: %name", [
'%name' => $this->context_name,
'%type' => $this->context->type,
]));
/**
* The provision-verify command function looks like:
*
*
function drush_provision_verify() {
provision_backend_invoke(d()->name, 'provision-save');
d()->command_invoke('verify');
}
*/
$this->context->runSteps('install');
}
}
......@@ -27,12 +27,6 @@ use Symfony\Component\Console\Output\OutputInterface;
*/
class SaveCommand extends Command
{
/**
* This command needs a context.
*/
const CONTEXT_REQUIRED = TRUE;
/**
* @var string
*/
......@@ -49,11 +43,11 @@ class SaveCommand extends Command
protected function configure()
{
$this
->setName('save')
->setAliases(['add'])
->setDescription('Save Provision Context.')
->setName('context:save')
->setAliases(['add', 'save'])
->setDescription('Create or update a site, platform, or server.')
->setHelp(
'Saves a ProvisionContext object to file. Currently just passes to "drush provision-save".'
'Use this command to interactively setup a new site, platform or server (known as "contexts"). Metadata is saved to .yml files in the "config_path" folder. Once you have create a context, use the `provision status` command to view the list of added contexts.'
)
->setDefinition($this->getCommandDefinition());
}
......@@ -70,8 +64,7 @@ class SaveCommand extends Command
'context_type',
null,
InputOption::VALUE_OPTIONAL,
'server, platform, or site',
'site'
'server, platform, or site'
);
$inputDefinition[] = new InputOption(
'delete',
......@@ -151,14 +144,55 @@ class SaveCommand extends Command
$this->newContext = TRUE;
// If context_type is still empty, throw an exception. Happens if using -n
// If context_type is not specified,..
if (empty($context_type)) {
throw new \Exception('Option --context_type must be specified.');
// If user is interactive...
if ($input->isInteractive()){
// If name is not set, ask for it.
if (empty($this->context_name)) {
while (empty($this->context_name)) {
$this->context_name = $this->io->ask('Context Name:', '', function ($value) {
if (empty($value)) {
$this->io->error('You must specify a context name.');
}
else {
return $value;
}
});
}
$this->input->setOption('context', $this->context_name);
}
// If there are no contexts at all, default to "server".
$contexts = $this->getProvision()->getAllContexts();
if (count($contexts) == 0) {
// @TODO: This is the onboarding moment for CLI users.
$context_type = 'server';
$this->io->note('No contexts exist yet. First one must be a server.');
}
// If there are other contexts, ask what type the user wants to create.
else {
$context_type = $this->io->choice('What do you want to create?', Context::getContextTypeOptions());
}
// Save option to $input.
$input->setOption('context_type', $context_type);
}
else {
// Not interactive: --context_type is required.
throw new \Exception('Option --context_type must be specified.');
}
}
else {
$this->input->setOption('context_type', $context_type);
if (empty($this->context_name)) {
throw new \Exception('No context name set. You must use the --context option when running non-interactively.');
}
}
// Handle invalid context_type.
if (!class_exists(Context::getClassName($context_type))) {
$types = Context::getContextTypeOptions();
......@@ -276,7 +310,7 @@ class SaveCommand extends Command
// Offer to verify. (only if --verify option was specified or is interactive and confirmation is made.
if ($this->input->getOption('verify') || ($this->input->isInteractive() && $this->io->confirm('Would you like to run `provision verify` on this ' . $this->input->getOption('context_type') . '?'))) {
$command = $this->getApplication()->find('verify');
$arguments['context_name'] = $this->context_name;
$arguments['--context'] = $this->context_name;
$input = new ArrayInput($arguments);
exit($command->run($input, $this->output));
}
......@@ -321,7 +355,7 @@ class SaveCommand extends Command
* Override to add options
* @param string $question
*/
public function askForContext($question = 'Choose a context')
public function askForContext($question = 'Choose a context', $context_types = array())
{
$options = $this->getProvision()->getAllContextsOptions();
......@@ -334,19 +368,6 @@ class SaveCommand extends Command
if (empty($this->input->getOption('context_type'))) {
$type_options = Context::getContextTypeOptions();
// Check for platforms. If none. don't allow sites.
$platform_exists = FALSE;
foreach ($options as $name => $type_and_name) {
if (strpos($type_and_name, 'platform') === 0) {
$platform_exists = TRUE;
}
}
if (!$platform_exists) {
unset($type_options['site']);
$this->io->block("You cannot add a site until you have at least one platform.");
}
$context_type = $this->io->choice('Context Type?', $type_options);
}
else {
......@@ -437,7 +458,7 @@ class SaveCommand extends Command
}
$command = $this->getApplication()->find('services');
$arguments = [
'context_name' => $this->input->getOption('context'),
'--context' => $this->input->getOption('context'),
'sub_command' => 'add',
];
while ($this->io->confirm('Add a service?')) {
......@@ -468,7 +489,7 @@ class SaveCommand extends Command
$command = $this->getApplication()->find('services');
$arguments = [
'context_name' => $this->input->getOption('context'),
'--context' => $this->input->getOption('context'),
'sub_command' => 'add',
'service' => $type,
];
......
......@@ -99,21 +99,25 @@ class ServicesCommand extends Command
// Load all service options
$options = Context::getServiceOptions();
// For each service type...
foreach ($options as $service => $service_name) {
$class = Service::getClassName($service);
// Load option_documentation() into input options.
foreach (Context::getContextTypeOptions() as $type => $type_name) {
$method = "{$type}_options";
foreach ($class::$method() as $option => $description) {
$description = "$type_name $service service: $description";
$inputDefinition[] = new InputOption($option, NULL, InputOption::VALUE_OPTIONAL, $description);
// Load every available service type.
foreach (Context::getServiceTypeOptions($service) as $service_type => $service_name) {
$class = Service::getClassName($service, $service_type);
Provision::getProvision()->getLogger()->debug("Loading options from $class $service_type");
// Load option_documentation() into input options.
foreach (Context::getContextTypeOptions() as $type => $type_name) {
$method = "{$type}_options";
foreach ($class::$method() as $option => $description) {
$description = "$type_name $service $service_name service: $description";
$inputDefinition[] = new InputOption($option, NULL, InputOption::VALUE_OPTIONAL, $description);
}
}
}
}
return $inputDefinition;
......@@ -141,6 +145,11 @@ class ServicesCommand extends Command
// }
$this->sub_command = $input->getArgument('sub_command');
// Ensure '@context' names can be used: Trim the "@"
if ($input->getArgument('server')) {
$input->setArgument('server', ltrim($input->getArgument('server'), '@'));
}
parent::initialize(
$input,
$output
......@@ -216,7 +225,7 @@ class ServicesCommand extends Command
}
if ($this->context->hasService($service)) {
$this->getProvision()->io()->helpBlock("Editing service {$service} provded by server '{$this->context->name}'...", ProvisionStyle::ICON_EDIT);
$this->getProvision()->io()->helpBlock("Editing service {$service} provided by server '{$this->context->name}'...", ProvisionStyle::ICON_EDIT);
}
// Then ask for all options.
......@@ -283,12 +292,12 @@ class ServicesCommand extends Command
$property = Provision::newProperty($property);
}
if ($this->context->hasService($service) && $this->context->getService($service)->getProperty($name)) {
if ($this->context->hasService($service) && $this->context->getService($service)->hasProperty($name) && $this->context->getService($service)->getProperty($name)) {
$property->default = $this->context->getService($service)->getProperty($name);
}
// If option does not exist, ask for it.
if (!empty($this->input->getOption($name))) {
if ($this->input->hasOption($name) && !empty($this->input->getOption($name))) {
$properties[$name] = $this->input->getOption($name);
$this->io->comment("Using option {$name}={$properties[$name]}");
}
......
......@@ -193,7 +193,7 @@ YML;
$command = $this->getApplication()->find('save');
$parameters = $_SERVER['argv'];
$parameters['--context_type'] = 'server';
$parameters['context_name'] = 'server_master';
$parameters['--context'] = 'server_master';
$input = new ArrayInput($parameters);
$exit_code = $command->run($input, $this->output);
......
......@@ -3,6 +3,8 @@
namespace Aegir\Provision\Command;
use Aegir\Provision\Command;
use Aegir\Provision\Provision;
use Aegir\Provision\Service\DockerServiceInterface;
use Psy\Shell;
use Psy\Configuration;
use Symfony\Component\Console\Input\InputInterface;
......@@ -15,6 +17,7 @@ use Symfony\Component\Console\Output\OutputInterface;
*/
class ShellCommand extends Command
{
const CONTEXT_REQUIRED = TRUE;
/**
* {@inheritdoc}
......@@ -32,8 +35,51 @@ class ShellCommand extends Command
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$config = new Configuration;
$shell = new Shell($config);
$shell->run();
$messages = [];
$process = new \Symfony\Component\Process\Process("bash");
$process->setTty(TRUE);
$dir = $this->context->getWorkingDir();
if ($this->context->type == 'site') {
$ps1 = Provision::APPLICATION_FUN_NAME . ' \[\e[33m\]' . $this->context_name . '\[\e[m\] \w \[\e[36;40m\]\\$\[\e[m\] ';
$process->setWorkingDirectory($dir);
$process->setCommandLine("cd $dir && PS1='$ps1' bash");
//@TODO: Allow services to set environment variables for both shell command and virtualhost config.
$env = $_SERVER;
$env['db_type'] = $this->context->getSubscription('db')->service->getType();
$env['db_name'] = $this->context->getSubscription('db')->getProperty('db_name');
$env['db_user'] = $this->context->getSubscription('db')->getProperty('db_user');
$env['db_passwd'] = $this->context->getSubscription('db')->getProperty('db_password');
// @TODO: We shouldn't always rely on what remote_host says.
$env['db_host'] = $this->context->getSubscription('db')->service->provider->getProperty('remote_host');
$env['db_port'] = $this->context->getSubscription('db')->service->getCreds()['port'];
// If bin dir is found, add to path
if (file_exists($this->context->getProperty('root') . '/bin')) {
$env['PATH'] .= ':' . $this->context->getProperty('root') . '/bin';
}
if (file_exists($this->context->getProperty('root') . '/vendor/bin')) {
$env['PATH'] .= ':' . $this->context->getProperty('root') . '/vendor/bin';
}
$process->setEnv($env);
}
if ($this->context->hasService('http') && $this->context->getService('http') instanceof DockerServiceInterface) {
$process->setWorkingDirectory($this->context->service('http')->provider->getWorkingDir());
$process->setCommandLine("docker-compose exec http bash");
}
$messages[] = "Opening bash shell in " . $dir;
$messages[] = "Running the command: " . $process->getCommandLine();
$messages[] = 'The commands composer, drupal, drush and more are available.';
$messages[] = 'Type "exit" to leave.';
$this->io->commentBlock($messages);
$process->run();
}
}
......@@ -32,6 +32,7 @@ class StatusCommand extends Command
->setName('status')
->setDescription('Display system status.')
->setHelp('Lists helpful information about your system.')
->setAliases(array('ls'))
;
}
......@@ -97,7 +98,7 @@ class StatusCommand extends Command
$context = $this->io->choiceNoList('Get status for', $options, 'select a context');
if ($context != 'select a context') {
$command = $this->getApplication()->find('status');
$arguments['context_name'] = $context;
$arguments['--context'] = $context;
$input = new ArrayInput($arguments);
exit($command->run($input, $this->output));
}
......
This diff is collapsed.
......@@ -80,7 +80,7 @@ class VerifyCommand extends Command
}
*/
$message = $this->context->verifyCommand();
$this->context->runSteps('verify');
}
}
......@@ -16,7 +16,7 @@ class ArgvInput extends ArgvInputBase {
* @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)
public function __construct(array $argv = [], InputDefinition $definition = null)
{
// If @alias is used, swap it out with --context=
if (isset($argv[1]) && strpos($argv[1], '@') === 0) {
......@@ -26,11 +26,13 @@ class ArgvInput extends ArgvInputBase {
}
// If --context option is used, use that.
elseif ($argv_filtered = array_filter($argv, function ($key) {
return strpos($key, '--context') === 0;
return strpos($key, '--context=') === 0;
})) {
$context_option = array_pop($argv_filtered);
$context_name = substr($context_option, strlen('--context='));
$this->activeContextName = $context_name;
// Allow --context=@server_master.
$this->activeContextName = ltrim($context_name, '@');
}
parent::__construct($argv, $definition);
}
......
......@@ -5,11 +5,11 @@ namespace Aegir\Provision\Console;
use Aegir\Provision\Common\NotSetupException;
use Aegir\Provision\Common\ProvisionAwareTrait;
use Aegir\Provision\Provision;
use Aegir\Provision\Console\ArgvInput;
use Drupal\Console\Core\Style\DrupalStyle;
use Robo\Common\IO;
use Symfony\Component\Config\Definition\Exception\Exception;
use Symfony\Component\Console\Exception\InvalidOptionException;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Filesystem\Exception\IOExceptionInterface;
......@@ -22,6 +22,7 @@ use Symfony\Component\Filesystem\Exception\IOExceptionInterface;
class Config extends ProvisionConfig
{
const CONFIG_FILENAME = '.provision.yml';
const COMPOSER_INSTALL_DEFAULT = 'composer install --no-interaction';
use IO;
use ProvisionAwareTrait;
......@@ -116,12 +117,12 @@ class Config extends ProvisionConfig
// Check for missing everything. Tell the user to run the setup command.
// @TODO: Run the setup command here instead. I poked and prodded but could not get it to work. Config is instantiated before Application
if (
!file_exists($this->get('config_path')) &&
!file_exists($this->get('console_config_file'))
) {
throw new NotSetupException();
}
// if (
// !file_exists($this->get('config_path')) &&
// !file_exists($this->get('console_config_file'))
// ) {
// throw new NotSetupException();
// }
// Check for paths that need to be writable.
......@@ -139,7 +140,7 @@ class Config extends ProvisionConfig
}
}
if ($errors) {
throw new NotSetupException(implode("\n\n", $errors));
throw new Exception(implode("\n\n", $errors));
}
// Ensure that script_user is the user.
......@@ -151,21 +152,22 @@ class Config extends ProvisionConfig
// @TODO: Ensure that web_user exists. Right now all that matters is web_user_uid
// Ensure that script user is a member of web user group.
if (!$this->isUserInWebGroup($this->get('web_user_uid'))) {
$this->io()->warningLite("Your user is not in the web group.");
$this->io()->helpBlock([
"To add your user to the web user group, run one of the following commands:",
"",
" mac: sudo dseditgroup -o edit -a {$this->get('script_user')} -t user {$this->get('web_user')}",
" linux: sudo usermod -aG {$this->get('web_user')} {$this->get('script_user')}",
]);
throw new InvalidOptionException(
"The current user ({$this->get('script_user')}) is not in the group '{$this->get('web_user')}' [{$this->get('web_user_uid')}]. Please add your user '{$this->config->get('script_user')}' to the group '{$this->get('web_user')}' or change the web_user or web_user_uid values in the file {{$this->get('console_config_file')}}.}"
);
}
// @TODO: Only invoke this if running on a server using local web server.
// // Ensure that script user is a member of web user group.
// if (!$this->isUserInWebGroup($this->get('web_user_uid'))) {
//
// $this->io()->warningLite("Your user is not in the web group.");
// $this->io()->helpBlock([
// "To add your user to the web user group, run one of the following commands:",
// "",
// " mac: sudo dseditgroup -o edit -a {$this->get('script_user')} -t user {$this->get('web_user')}",
// " linux: sudo usermod -aG {$this->get('web_user')} {$this->get('script_user')}",
// ]);
//
// throw new InvalidOptionException(
// "The current user ({$this->get('script_user')}) is not in the group '{$this->get('web_user')}' [{$this->get('web_user_uid')}]. Please add your user '{$this->config->get('script_user')}' to the group '{$this->get('web_user')}' or change the web_user or web_user_uid values in the file {{$this->get('console_config_file')}}.}"
// );
// }
}
......
......@@ -5,7 +5,6 @@ namespace Aegir\Provision\Console;
use Aegir\Provision\Provision;
use Drupal\Console\Core\Style\DrupalStyle;
use Robo\Common\InputAwareTrait;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Component\Console\Output\OutputInterface;
......@@ -30,6 +29,8 @@ class ProvisionStyle extends DrupalStyle {
const ICON_FAILED = '🔥';
const ICON_COMMAND = '$';
const ICON_BULLET = '➤';
const ICON_FOLDER = '📂';
const ICON_FILE = '🗎';
public function __construct(InputInterface $input, OutputInterface $output)
{
......
This diff is collapsed.
......@@ -23,6 +23,11 @@ class PlatformContext extends ServiceSubscriber implements ConfigurationInterfac
public $type = 'platform';
const TYPE = 'platform';
/**
* @TODO: Move to PlatformType class for PHP once we have it.
*/
const COMPOSER_INSTALL_DEFAULT = 'composer install --no-interaction';
/**
* @var \Aegir\Provision\Context\ServerContext;
*/
......@@ -193,6 +198,13 @@ class PlatformContext extends ServiceSubscriber implements ConfigurationInterfac
->description('platform: Relative path to the "document root" in your source code. Leave blank if docroot is the root.')
->required(FALSE)
,
'composer_install_command' =>
Provision::newProperty()
->description('The command to run immediately after cloning or pulling new code. If this is a production site, you would should add "--no-dev".')
->forceAsk()
->defaultValue(self::COMPOSER_INSTALL_DEFAULT)
->required(FALSE)
,
];
return $options;
......@@ -223,17 +235,17 @@ class PlatformContext extends ServiceSubscriber implements ConfigurationInterfac
$this->getProvision()->io()->newLine();
$tasks = [];
$steps = [];
// If platform files don't exist, but has git url or makefile, build now.
if ($this->getProperty('git_url')) {
if ($this->fs->exists($this->getProperty('root'))) {
$tasks['platform.files'] = $this->getProvision()->newTask()
$steps['platform.files'] = Provision::newStep()
->success('Cloning git repository... Files already exist.');
}
else {
$tasks['platform.git'] = $this->getProvision()->newTask()
$steps['platform.git'] = Provision::newStep()
->start('Cloning git repository...')
->execute(function () {
if (!$this->fs->exists($this->getProperty('root'))) {
......@@ -259,7 +271,7 @@ class PlatformContext extends ServiceSubscriber implements ConfigurationInterfac
if ($this->getProperty('makefile')) {
if ($this->fs->exists($this->getProperty('document_root_full'))) {
$tasks['platform.files'] = $this->getProvision()->newTask()
$steps['platform.files'] = Provision::newStep()
->start('Building platform from makefile... Files already exist.');
}
else {
......@@ -267,7 +279,7 @@ class PlatformContext extends ServiceSubscriber implements ConfigurationInterfac
if (!Provision::fs()->isAbsolutePath($makefile)) {
$makefile = $this->getProperty('root') . DIRECTORY_SEPARATOR . $makefile;
}
$tasks['platform.make'] = $this->getProvision()->newTask()
$steps['platform.make'] = Provision::newStep()
->start('Building platform from makefile...')
->execute(function () use ($makefile) {
$drush = realpath(__DIR__ . '/../../../bin/drush');
......@@ -289,13 +301,18 @@ class PlatformContext extends ServiceSubscriber implements ConfigurationInterfac
}
// If files already exist, say so.
$tasks['platform.found'] = $this->getProvision()->newTask()
$steps['platform.found'] = Provision::newStep()
->start('Checking root path for files...')
->execute(function () {
return $this->fs->exists($this->getProperty('root'))? 0: 1;
if ($this->fs->exists($this->getProperty('root')) ) {
return 0;
}
else {
throw new \Exception('The path specified as "root" does not exist. Please put your code in the folder or change the "root" property in the context: ' . $this->getProperty('root'));
}
});
return $tasks;
return $steps;
// return parent::verify();
}
......
......@@ -6,7 +6,6 @@ use Aegir\Provision\Console\Config;
use Aegir\Provision\ServiceProvider;
use Aegir\Provision\Property;
use Aegir\Provision\Provision;
use Aegir\Provision\Service\DockerServiceInterface;
use Psr\Log\LogLevel;
use Robo\ResultData;
use Symfony\Component\Config\Definition\ConfigurationInterface;
......@@ -66,6 +65,11 @@ class ServerContext extends ServiceProvider implements ConfigurationInterface
static function option_documentation()
{
return [
'context_class' => Provision::newProperty()
->description('The name of the class to load for this context.')
->hidden()
->defaultValue(self::getClassName(self::TYPE))
,
'remote_host' =>
Provision::newProperty()
->description('server: host name')
......@@ -79,6 +83,21 @@ class ServerContext extends ServiceProvider implements ConfigurationInterface
}
return $remote_host;
}),
'ip_addresses' =>
Provision::newProperty()
->description('server: IP Addresses')
->required(FALSE)
->validate(function($ip_addresses) {
$ips = explode(',', $ip_addresses