Commit c17bbb17 authored by Jon Pugh's avatar Jon Pugh

Use annotated-command CommandFileDiscovery to lookup all service and service...

Use annotated-command CommandFileDiscovery to lookup all service and service type classes. Use this to ask the user what service they want to add to a server.
parent 62a092e3
......@@ -6,6 +6,7 @@
"type": "library",
"license": "GPL-2.0+",
"require": {
"consolidation/annotated-command": "~2",
"drupal/console-core": "1.0.2",
"psy/psysh": "^0.8.11",
"symfony/console": "^3.3",
......
......@@ -4,8 +4,108 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"content-hash": "835ca80eb26b4d7e438015632aaeaaa2",
"content-hash": "c8f06b1cc28f9638bdc92415ed78c328",
"packages": [
{
"name": "consolidation/annotated-command",
"version": "2.8.1",
"source": {
"type": "git",
"url": "https://github.com/consolidation/annotated-command.git",
"reference": "7f94009d732922d61408536f9228aca8f22e9135"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/consolidation/annotated-command/zipball/7f94009d732922d61408536f9228aca8f22e9135",
"reference": "7f94009d732922d61408536f9228aca8f22e9135",
"shasum": ""
},
"require": {
"consolidation/output-formatters": "^3.1.12",
"php": ">=5.4.0",
"psr/log": "^1",
"symfony/console": "^2.8|~3",
"symfony/event-dispatcher": "^2.5|^3",
"symfony/finder": "^2.5|^3"
},
"require-dev": {
"phpunit/phpunit": "^4.8",
"satooshi/php-coveralls": "^1.0",
"squizlabs/php_codesniffer": "^2.7"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.x-dev"
}
},
"autoload": {
"psr-4": {
"Consolidation\\AnnotatedCommand\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Greg Anderson",
"email": "greg.1.anderson@greenknowe.org"
}
],
"description": "Initialize Symfony Console commands from annotated command class methods.",
"time": "2017-10-17T01:48:51+00:00"
},
{
"name": "consolidation/output-formatters",
"version": "3.1.12",
"source": {
"type": "git",
"url": "https://github.com/consolidation/output-formatters.git",
"reference": "88ef346a1cefb92aab8b57a3214a6d5fc63f5d2a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/consolidation/output-formatters/zipball/88ef346a1cefb92aab8b57a3214a6d5fc63f5d2a",
"reference": "88ef346a1cefb92aab8b57a3214a6d5fc63f5d2a",
"shasum": ""
},
"require": {
"php": ">=5.4.0",
"symfony/console": "^2.8|~3",
"symfony/finder": "~2.5|~3.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8",
"satooshi/php-coveralls": "^1.0",
"squizlabs/php_codesniffer": "^2.7",
"victorjonsson/markdowndocs": "^1.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.x-dev"
}
},
"autoload": {
"psr-4": {
"Consolidation\\OutputFormatters\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Greg Anderson",
"email": "greg.1.anderson@greenknowe.org"
}
],
"description": "Format text by applying transformations provided by plug-in formatters.",
"time": "2017-10-12T19:38:03+00:00"
},
{
"name": "dflydev/dot-access-configuration",
"version": "v1.0.2",
......
......@@ -7,6 +7,7 @@ use Aegir\Provision\Context;
use Aegir\Provision\Context\PlatformContext;
use Aegir\Provision\Context\ServerContext;
use Aegir\Provision\Context\SiteContext;
use Consolidation\AnnotatedCommand\CommandFileDiscovery;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputDefinition;
......@@ -109,4 +110,22 @@ class ServicesCommand extends Command
$this->io->comment("List Services");
$this->context->showServices($this->io);
}
/**
* Add a new service to a server.
*/
protected function execute_add(InputInterface $input, OutputInterface $output)
{
// Ask which service.
$this->io->comment("List Services");
$service = $this->io->choice('Which service?', $this->context->getServiceOptions());
// Then ask which service type
$service_type = $this->io->choice('Which service type?', $this->context->getServiceTypeOptions($service));
$this->io->info("Adding $service service $service_type...");
}
}
......@@ -3,6 +3,7 @@
namespace Aegir\Provision\Context;
use Aegir\Provision\Context;
use Consolidation\AnnotatedCommand\CommandFileDiscovery;
use Drupal\Console\Core\Style\DrupalStyle;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
......@@ -56,6 +57,86 @@ class ServerContext extends Context implements ConfigurationInterface
}
/**
* Loads all available \Aegir\Provision\Service classes
*
* @return array
*/
protected function getAvailableServices($service = NULL) {
// Load all service classes
$classes = [];
$discovery = new CommandFileDiscovery();
$discovery->setSearchPattern('*Service.php');
$servicesFiles = $discovery->discover(__DIR__ .'/../Service', '\Aegir\Provision\Service');
foreach ($servicesFiles as $serviceClass) {
$classes[$serviceClass::SERVICE] = $serviceClass;
}
if ($service && isset($classes[$service])) {
return $classes[$service];
}
elseif ($service && !isset($classes[$service])) {
throw new \Exception("No service with name $service was found.");
}
else {
return $classes;
}
}
/**
* Lists all available services as a simple service => name array.
* @return array
*/
public function getServiceOptions() {
$options = [];
$services = $this->getAvailableServices();
foreach ($services as $service => $class) {
$options[$service] = $class::SERVICE_NAME;
}
return $options;
}
/**
* @return array
*/
protected function getAvailableServiceTypes($service, $service_type = NULL) {
// Load all service classes
$classes = [];
$discovery = new CommandFileDiscovery();
$discovery->setSearchPattern(ucfirst($service) . '*Service.php');
$serviceTypesFiles = $discovery->discover(__DIR__ .'/../Service/' . ucfirst($service), '\Aegir\Provision\Service\\' . ucfirst($service));
foreach ($serviceTypesFiles as $serviceTypeClass) {
$classes[$serviceTypeClass::SERVICE_TYPE] = $serviceTypeClass;
}
if ($service_type && isset($classes[$service_type])) {
return $classes[$service_type];
}
elseif ($service_type && !isset($classes[$service_type])) {
throw new \Exception("No service type with name $service_type was found.");
}
else {
return $classes;
}
}
/**
* Lists all available services as a simple service => name array.
* @return array
*/
public function getServiceTypeOptions($service) {
$options = [];
$service_types = $this->getAvailableServiceTypes($service);
foreach ($service_types as $service_type => $class) {
$options[$service_type] = $class::SERVICE_TYPE_NAME;
}
return $options;
}
/**
* Return all services for this context.
*
* @return array
......
......@@ -28,7 +28,19 @@ class Service {
*/
public $context;
protected $service = NULL;
/**
* @var string
* The machine name of the service. ie. http, db
*/
const SERVICE = 'service';
/**
* @var string
* A descriptive name of the service. ie. Web Server
*/
const SERVICE_NAME = 'Service Name';
protected $application_name = NULL;
protected $has_restart_cmd = FALSE;
......
<?php
/**
* @file
* The base Provision DbService class.
*
* @see \Provision_Service_db
*/
namespace Aegir\Provision\Service;
//require_once DRUSH_BASE_PATH . '/commands/core/rsync.core.inc';
use Aegir\Provision\Service;
/**
* Class DbService
*
* @package Aegir\Provision\Service
*/
class DbService extends Service {
const SERVICE = 'db';
const SERVICE_NAME = 'Database Server';
/**
* Register the db handler for sites, based on the db_server option.
*/
static function subscribe_site($context) {
$context->setProperty('db_server', '@server_master');
$context->is_oid('db_server');
$context->service_subscribe('db', $context->db_server->name);
}
static function option_documentation() {
return array(
'master_db' => 'server with db: Master database connection info, {type}://{user}:{password}@{host}',
'db_grant_all_hosts' => 'Grant access to site database users from any web host. If set to TRUE, any host will be allowed to connect to MySQL site databases on this server using the generated username and password. If set to FALSE, web hosts will be granted access by their detected IP address.',
);
}
function init_server() {
parent::init_server();
$this->server->setProperty('master_db');
$this->server->setProperty('db_grant_all_hosts', FALSE);
$this->server->setProperty('utf8mb4_is_supported', FALSE);
$this->creds = array_map('urldecode', parse_url($this->server->master_db));
return TRUE;
}
function save_server() {
// Check database 4 byte UTF-8 support and save it for later.
$this->server->utf8mb4_is_supported = $this->utf8mb4_is_supported();
}
/**
* Verifies database connection and commands
*/
function verify_server_cmd() {
if ($this->connect()) {
if ($this->can_create_database()) {
drush_log(dt('Provision can create new databases.'), 'success');
}
else {
drush_set_error('PROVISION_CREATE_DB_FAILED');
}
if ($this->can_grant_privileges()) {
drush_log(dt('Provision can grant privileges on database users.'), 'success');;
}
else {
drush_set_error('PROVISION_GRANT_DB_USER_FAILED');
}
if ($this->server->utf8mb4_is_supported) {
drush_log(dt('Provision can activate multi-byte UTF-8 support on Drupal 7 sites.'), 'success');
}
else {
drush_log(dt('Multi-byte UTF-8 for Drupal 7 is not supported on your system. See the <a href="@url">documentation on adding 4 byte UTF-8 support</a> for more information.', array('@url' => 'https://www.drupal.org/node/2754539')), 'warning');
}
} else {
drush_set_error('PROVISION_CONNECT_DB_FAILED');
}
}
/**
* Find a viable database name, based on the site's uri.
*/
function suggest_db_name() {
$uri = $this->context->uri;
$suggest_base = substr(str_replace(array('.', '-'), '' , preg_replace('/^www\./', '', $uri)), 0, 16);
if (!$this->database_exists($suggest_base)) {
return $suggest_base;
}
for ($i = 0; $i < 100; $i++) {
$option = sprintf("%s_%d", substr($suggest_base, 0, 15 - strlen( (string) $i) ), $i);
if (!$this->database_exists($option)) {
return $option;
}
}
drush_set_error('PROVISION_CREATE_DB_FAILED', dt("Could not find a free database names after 100 attempts"));
return false;
}
/**
* Generate a new mysql database and user account for the specified credentials
*/
function create_site_database($creds = array()) {
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", array('%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", array('@user' => $db_user)));
}
drush_log(dt("Granted privileges to %user@%client on %database", array('%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', array("@name" => $db_name)), 'success');
}
else {
drush_set_error('PROVISION_CREATE_DB_FAILED', dt("Could not create @name database", array("@name" => $db_name)));
}
return $status;
}
/**
* Remove the database and user account for the supplied credentials
*/
function destroy_site_database($creds = array()) {
if (!sizeof($creds)) {
$creds = $this->fetch_site_credentials();
}
extract($creds);
if ( $this->database_exists($db_name) ) {
drush_log(dt("Dropping database @dbname", array('@dbname' => $db_name)));
if (!$this->drop_database($db_name)) {
drush_log(dt("Failed to drop database @dbname", array('@dbname' => $db_name)), 'warning');
}
}
if ( $this->database_exists($db_name) ) {
drush_set_error('PROVISION_DROP_DB_FAILED');
return FALSE;
}
foreach ($this->grant_host_list() as $db_grant_host) {
drush_log(dt("Revoking privileges of %user@%client from %database", array('%user' => $db_user, '%client' => $db_grant_host, '%database' => $db_name)));
if (!$this->revoke($db_name, $db_user, $db_grant_host)) {
drush_log(dt("Failed to revoke user privileges"), 'warning');
}
}
}
function import_site_database($dump_file = null, $creds = array()) {
if (is_null($dump_file)) {
$dump_file = d()->site_path . '/database.sql';
}
if (!sizeof($creds)) {
$creds = $this->fetch_site_credentials();
}
$exists = provision_file()->exists($dump_file)
->succeed('Found database dump at @path.')
->fail('No database dump was found at @path.', 'PROVISION_DB_DUMP_NOT_FOUND')
->status();
if ($exists) {
$readable = provision_file()->readable($dump_file)
->succeed('Database dump at @path is readable')
->fail('The database dump at @path could not be read.', 'PROVISION_DB_DUMP_NOT_READABLE')
->status();
if ($readable) {
$this->import_dump($dump_file, $creds);
}
}
}
function generate_site_credentials() {
$creds = array();
// replace with service type
$db_type = drush_get_option('db_type', function_exists('mysqli_connect') ? 'mysqli' : 'mysql');
// As of Drupal 7 there is no more mysqli type
if (drush_drupal_major_version() >= 7) {
$db_type = ($db_type == 'mysqli') ? 'mysql' : $db_type;
}
//TODO - this should not be here at all
$creds['db_type'] = drush_set_option('db_type', $db_type, 'site');
$creds['db_host'] = drush_set_option('db_host', $this->server->remote_host, 'site');
$creds['db_port'] = drush_set_option('db_port', $this->server->db_port, 'site');
$creds['db_passwd'] = drush_set_option('db_passwd', provision_password(), 'site');
$creds['db_name'] = drush_set_option('db_name', $this->suggest_db_name(), 'site');
$creds['db_user'] = drush_set_option('db_user', $creds['db_name'], 'site');
return $creds;
}
function fetch_site_credentials() {
$creds = array();
$keys = array('db_type', 'db_port', 'db_user', 'db_name', 'db_host', 'db_passwd');
foreach ($keys as $key) {
$creds[$key] = drush_get_option($key, '', 'site');
}
return $creds;
}
function database_exists($name) {
return FALSE;
}
function drop_database($name) {
return FALSE;
}
function create_database($name) {
return FALSE;
}
function can_create_database() {
return FALSE;
}
function can_grant_privileges() {
return FALSE;
}
function grant($name, $username, $password, $host = '') {
return FALSE;
}
function revoke($name, $username, $host = '') {
return FALSE;
}
function import_dump($dump_file, $creds) {
return FALSE;
}
function generate_dump() {
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 array('%');
}
else {
return array_unique(array_map(array($this, 'grant_host'), $this->context->service('http')->grant_server_list()));
}
}
/**
* Return a hostname suitable for database grants from a server object.
*/
function grant_host(Provision_Context_server $server) {
return $server->remote_host;
}
/**
* Checks whether utf8mb4 support is available on the current database system.
*
* @return bool
*/
function utf8mb4_is_supported() {
// By default we assume that the database backend may not support 4 byte
// UTF-8.
return FALSE;
}
}
<?php
/**
* @file
* The Provision HttpApacheService class.
*
* @see \Provision_Service_http_apache
*/
namespace Aegir\Provision\Service\Http;
use Aegir\Provision\Service\HttpService;
/**
* Class HttpApacheService
*
* @package Aegir\Provision\Service\Http
*/
class HttpApacheService extends HttpService
{
const SERVICE_TYPE = 'apache';
const SERVICE_TYPE_NAME = 'Apache';
}
<?php
/**
* @file
* The Provision HttpNginxService class.
*
* @see \Provision_Service_http_Nginx
*/
namespace Aegir\Provision\Service\Http;
use Aegir\Provision\Service\HttpService;
/**
* Class HttpNginxService
*
* @package Aegir\Provision\Service\Http
*/
class HttpNginxService extends HttpService
{
const SERVICE_TYPE = 'nginx';
const SERVICE_TYPE_NAME = 'NGINX';
}
......@@ -11,6 +11,7 @@ namespace Aegir\Provision\Service;
//require_once DRUSH_BASE_PATH . '/commands/core/rsync.core.inc';
use Aegir\Provision\Service;
use Consolidation\AnnotatedCommand\CommandFileDiscovery;
/**
* Class HttpService
......@@ -18,7 +19,9 @@ use Aegir\Provision\Service;
* @package Aegir\Provision\Service
*/
class HttpService extends Service {
public $service = 'http';
const SERVICE = 'http';
const SERVICE_NAME = 'Web Server';
protected $ssl_enabled = FALSE;
/**
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment