Commit 3332ffff authored by Adrian Rossouw's avatar Adrian Rossouw

Merge remote branch 'origin/master' into dev-restore

parents 111ad289 aed3d531
......@@ -185,7 +185,7 @@ class provisionService_db extends provisionService {
function generate_site_credentials() {
$creds = array();
// replace with service type
$db_type = drush_get_option('db_type', 'mysql');
$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;
......@@ -276,7 +276,7 @@ class provisionService_db_pdo extends provisionService_db {
function init() {
parent::init();
$this->dsn = sprintf("%s:host=%s", $this->PDO_type, $this->creds['host']);
$this->dsn = sprintf("%s:host=%s", $this->PDO_type, drush_is_local_host($this->creds['host']) ? 'localhost' : $this->creds['host']);
}
function connect() {
......
......@@ -9,7 +9,5 @@ function drush_db_pre_provision_install() {
}
function drush_db_pre_provision_install_rollback() {
if (!_provision_drupal_site_installed()) {
d()->service('db')->destroy_site_database();
}
d()->service('db')->destroy_site_database();
}
......@@ -127,4 +127,4 @@ Content of dispatch.sh::
export LD_LIBRARY_PATH
export PATH
php '/var/aegir/drush/drush.php' --php=/usr/local/bin/php hosting tasks --root='/var/aegir/hostmaster-HEAD' --uri='http://aegir.example.com'
php '/var/aegir/drush/drush.php' --php=/usr/local/bin/php '@hostmaster' hosting-dispatch
......@@ -176,6 +176,14 @@ file.
/etc/sudoers configuration::
aegir ALL=NOPASSWD: /usr/sbin/apache2ctl
Remote web servers
------------------
Any number of remote web servers may be configured. They need an aegir user and
Apache configuration as above, with the same user name and directory paths. SSH
public/private keys should be set up so hostmaster's Aegir user can access
remote web Aegir users with no passwords.
Database configuration
----------------------
......@@ -190,6 +198,9 @@ SQL commands::
Change 'XXXXXXXX' with a real password.
Any number of database servers may be used, replacing localhost with
hostmaster's hostname.
DNS Configuration
-----------------
......
......@@ -19,11 +19,11 @@ class provisionService_http extends provisionService {
function init() {
parent::init();
// System account
if ($this->server->name == '@server_localhost') {
if ($this->server->name == '@server_master') {
$this->server->setProperty('web_group', _provision_default_web_group());
}
else {
$this->server->web_group = d('@server_localhost')->web_group;
$this->server->web_group = d('@server_master')->web_group;
}
// Redirection urls
......
......@@ -19,8 +19,6 @@ function drush_provision_hostmaster_install($site = NULL) {
drush_backend_invoke('dl', array('drush_make-' . drush_get_option('drush_make_version', '6.x-2.0-beta6'), 'destination' => $aegir_root . '/.drush/'));
}
drush_print(dt("deploying hostmaster application"));
drush_backend_invoke('hostmaster-make', array($platform));
}
$data['master_db'] = drush_get_option('master_db');
......@@ -37,15 +35,18 @@ function drush_provision_hostmaster_install($site = NULL) {
$data['master_db'] = "$scheme://$user:$pass@$host";
}
$server = '@server_localhost';
// TODO: support creation of an external db server
$server = '@server_master';
drush_backend_invoke_args("provision-save", array($server), array(
'context_type' => 'server',
// files
// files
'remote_host' => php_uname('n'),
'aegir_root' => $aegir_root,
'script_user' => drush_get_option('script_user', 'aegir'),
'script_user' => drush_get_option('script_user', get_current_user()),
// apache
'http_service_type' => 'apache',
'web_group' => drush_get_option('web_group', _provision_default_web_group()),
'master_url' => "http://" . $site . "/",
// mysql
'db_service_type' => 'mysql',
'master_db' => $data['master_db'],
......@@ -62,12 +63,16 @@ function drush_provision_hostmaster_install($site = NULL) {
'server' => $server,
'web_server' => $server,
'root' => $platform,
'makefile' => $aegir_root . '/.drush/provision/aegir.make',
));
provision_backend_invoke($platform_name, 'provision-verify');
if (!drush_get_option('client_email')) {
$client_email = drush_prompt(dt("Admin user e-mail"), "webmaster@localhost");
}
else {
$client_email = drush_get_option('client_email');
}
$site_name = '@hostmaster';
drush_backend_invoke_args("provision-save", array($site_name), array(
......
......@@ -3,21 +3,10 @@
/**
* @file manual site migrate system
*/
function drush_provision_hostmaster_migrate_validate($site, $platform) {
// verify the target platform:
if (!is_dir($platform)) {
drush_print("The platform path you specified does not exist yet.");
drush_print("Downloading the hostmaster front end. (this might take a few minutes)");
drush_backend_invoke('make', array(dirname(__FILE__) . '/aegir.make', $platform));
if (drush_get_error()) {
drush_log("The hostmaster front end could not be downloaded, exiting.", "error");
}
}
$data = drush_get_merged_options();
// add a server record :
$server = '@server_localhost';
$server = '@server_master';
drush_backend_invoke_args("provision-save", array($server), array(
'context_type' => 'server',
'aegir_root' => dirname(rtrim($data['config_path'], '/')),
......@@ -30,8 +19,8 @@ function drush_provision_hostmaster_migrate_validate($site, $platform) {
// mysql
'db_service_type' => 'mysql',
'master_db' => $data['master_db'],
));
provision_backend_invoke($server, 'provision-verify');
drush_set_option('server_name', $server);
......@@ -51,6 +40,7 @@ function drush_provision_hostmaster_migrate_validate($site, $platform) {
'server' => $server,
'web_server' => $server,
'root' => $platform,
'makefile' => dirname(__FILE__) . '/aegir.make',
));
provision_backend_invoke($new_platform, 'provision-verify');
drush_set_option('new_platform', $new_platform);
......
......@@ -31,6 +31,14 @@ function drush_provision_drupal_pre_provision_install() {
_provision_drupal_create_directories();
}
/**
* If the install went south, and the site is not PROVISION_SITE_INSTALLED, clean up behind ourselves
*/
function drush_provision_drupal_pre_provision_install_rollback() {
_provision_recursive_delete( d()->site_path );
}
/**
* Install Drupal with the pre-configured settings, by calling an external script
*
......@@ -42,25 +50,18 @@ function drush_provision_drupal_provision_install() {
provision_prepare_environment();
_provision_drupal_create_settings_file();
drush_bootstrap(DRUSH_BOOTSTRAP_DRUPAL_SITE);
provision_save_site_data();
// call a backend task to do the actual installation.
$result = provision_backend_invoke(d()->name, "provision-install-backend");
// pass the login link back to the front end.
drush_set_option('login_link', $result['context']['login_link']);
drush_include_engine('drupal', 'install');
drush_bootstrap(DRUSH_BOOTSTRAP_DRUPAL_FULL);
drush_set_option('installed', TRUE, 'site');
_provision_drupal_maintain_aliases();
}
/**
* If the install went south, and the site is not PROVISION_SITE_INSTALLED, clean up behind ourselves
*/
function drush_provision_drupal_provision_install_rollback() {
if (d()->uri) {
if (!drush_get_option('installed', FALSE)) {
_provision_recursive_delete( d()->site_path );
}
} else {
drush_set_error('PROVISION_FRAMEWORK_ERROR', dt('no url defined in %function', array('%function' => __FUNCTION__)));
}
}
/**
* Finish the installation, regenerate the caches on the site so that
* any changes to things such as available modules/ themes can take affect.
......
......@@ -44,35 +44,6 @@ function provision_drupal_drush_engine_drupal() {
return $engines;
}
/**
* Initialize the platform / site
*
* This function is executed by drush_invoke, and is responsible
* for populating the $data context array
*/
function provision_drupal_drush_init() {
$command = drush_get_command();
$command = explode(" ", $command['command']);
if (preg_match("/^provision-/", $command[0])) {
_provision_context_init();
}
}
function _provision_context_init() {
static $is_run = false;
if ($is_run) {
return TRUE;
}
if ((d()->type === 'platform' || d()->type === 'site') && drush_bootstrap_validate(DRUSH_BOOTSTRAP_DRUPAL_ROOT)) {
// i don't think i should be bootstrapping here yet ... but i have no choice yet.
drush_bootstrap(DRUSH_BOOTSTRAP_DRUPAL_ROOT);
}
$is_run = TRUE;
}
/**
* Finalize the platform / site
......@@ -95,8 +66,8 @@ function provision_drupal_drush_exit() {
}
}
elseif (d()->type === 'platform') {
// Generate a drushrc.php for the platform unless it's being deleted
if (!preg_match("/^provision-delete/", $command[0])) {
// Don't generate the drushrc.php on provision-save/delete commands.
if (!preg_match("/^provision-(save|delete)/", $command[0])) {
provision_save_platform_data();
}
}
......@@ -115,6 +86,14 @@ function _provision_drupal_site_exists() {
return file_exists(d()->site_path . '/settings.php');
}
/**
* This command does the actual installation in it's own thread,
* so we can recover gracefully if things go really wrong.
*/
function drush_provision_drupal_provision_install_backend() {
drush_include_engine('drupal', 'install');
}
/**
* Sync the current Drupal platform and, if applicable, site. Call after
* finishing operations that affect the filesystem.
......
......@@ -21,11 +21,11 @@ function drush_provision_drupal_provision_verify_validate() {
/**
* implementation of provision_verify
*/
function drush_provision_drupal_provision_verify() {
function drush_provision_drupal_pre_provision_verify() {
if (d()->type === 'server') {
provision_file()->create_dir(d()->config_path, dt('Provision configuration'), 0711);
if (d()->name == '@server_localhost') {
if (d()->name == '@server_master') {
provision_file()->create_dir(d()->backup_path, dt('Backup'), 0700);
provision_file()->create_dir(d()->include_path, dt('Provision PHP configuration'), 0711);
if (!provision_file()->exists(d()->include_path . '/global.inc')->succeed('Global configuration file exists')->status()) {
......@@ -42,6 +42,20 @@ function drush_provision_drupal_provision_verify() {
}
}
elseif (d()->type === 'platform') {
// create a platform from a makefile, if it doesnt already exist and the makefile does.
if (!provision_file()->exists(d()->root)->status() && !empty(d()->makefile)) {
if (provision_file()->exists(d()->makefile)->status()) {
drush_log(dt("Platform path does not exists, fetching from drush makefile"));
drush_backend_invoke("make", array(d()->makefile, d()->root), array('root' => null, 'uri' => null));
if (drush_get_error()) {
return drush_set_error("DRUSH_MAKE_FAILED",
"Could not download platform using drush make. No platform present");
}
}
}
drush_bootstrap(DRUSH_BOOTSTRAP_DRUPAL_ROOT);
provision_file()->writable(d()->root . '/sites')
->succeed('Drupal sites directory @path is writable by the provisioning script')
->fail('Drupal sites directory @path is not writable by the provisioning script', 'PROVISION_SITES_DIR_NOT_WRITABLE');
......
......@@ -7,51 +7,101 @@
*/
class provisionConfig {
/**
* Template file, a PHP file which will have access to $this and variables
* as defined in $data.
*/
public $template = null;
/**
* Associate array of variables to make available to the template.
*/
public $data = array();
/**
* A provisionEnvironment object thie configuration relates to.
*
* @var provisionEnvironment
*/
public $owner = null;
/**
* If set, replaces file name in log messages.
*/
public $description = null;
protected $append = FALSE;
/**
* Octal Unix mode for permissons of the created file.
*/
protected $mode = NULL;
/**
* Unix group name for the created file.
*/
protected $group = NULL;
/**
* Forward $this->... to $this->owner->...
* object.
*/
function __get($name) {
if (isset($this->owner)) {
return $this->owner->$name;
}
}
/**
* Constructor, overriding not recommended.
*
* @param $owner
* An alias name for d(), the provisionEnvironment that this configuration
* is relevant to.
* @param $data
* An associative array to potentiall manipulate in process() and make
* available as variables to the template.
*/
function __construct($owner, $data = array()) {
if (is_null($this->template)) {
throw(exception);
}
$this->data = $this->parse();
if (sizeof($data)) {
$this->data = $data;
$this->data = array_merge($this->data, $data);
}
$this->owner = d($owner);
}
/**
* process the data before writing it
* Parse the existing config to load data
*
* this is called right before the file is written
* This is called in the constructor, and should take care of
* opening the file and parsing it into the data array.
*/
function parse() {
return array();
}
/**
* Process and add to $data before writing the configuration.
*
* this is a stub to be implemented by subclasses
* This is a stub to be implemented by subclasses.
*/
function process() {
return true;
}
/**
* the filename where the filename is read/written from/to
* The filename where the configuration is written.
*
* this is a stub to be implemented by subclasses
* This is a stub to be implemented by subclasses.
*/
function filename() {
return false;
}
/**
* Load template from filename().
*/
private function load_template() {
$reflect = new reflectionObject($this);
$base_dir = dirname($reflect->getFilename());
......@@ -66,7 +116,10 @@ class provisionConfig {
return false;
}
/**
* Render template, making variables available from $variables associative
* array.
*/
private function render_template($template, $variables) {
drush_errors_off();
extract($variables, EXTR_SKIP); // Extract the variables to a local namespace
......@@ -78,6 +131,16 @@ class provisionConfig {
return $contents; // Return the contents
}
/**
* Write out this configuration.
*
* 1. Make sure parent directory exists and is writable.
* 2. Load template with load_template().
* 3. Process $data with process().
* 4. Make existing file writable if necessary and possible.
* 5. Render template with $this and $data and write out to filename().
* 6. If $mode and/or $group are set, apply them for the new file.
*/
function write() {
$filename = $this->filename();
// Make directory structure if it does not exist.
......@@ -99,7 +162,7 @@ class provisionConfig {
->fail('Could not change permissions of @path to @perm');
}
provision_file()->file_put_contents($filename, $this->render_template($template, $this->data), $this->append ? FILE_APPEND : 0)
provision_file()->file_put_contents($filename, $this->render_template($template, $this->data))
->succeed('Generated config ' . (empty($this->description) ? $filename : $this->description), 'success');
// Change the permissions of the file if needed
......@@ -117,6 +180,9 @@ class provisionConfig {
}
}
/**
* Remove configuration file as specified by filename().
*/
function unlink() {
provision_file()->unlink($this->filename())->status();
}
......
......@@ -82,11 +82,16 @@ function provision_drush_command() {
'bootstrap' => DRUSH_BOOTSTRAP_DRUSH,
);
$items['provision-install'] = array(
'arguments' => array('domain.com' => dt('The domain of the site to install.')),
'description' => dt('Provision a new site using the provided data.'),
'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_ROOT
);
$items['provision-install-backend'] = array(
'description' => dt('Provision a new site using the provided data.'),
'hidden' => TRUE,
'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_SITE
);
$items['provision-import'] = array(
'arguments' => array('domain.com' => dt('The domain of the site to import.')),
'description' => dt('Turn an already running site into a provisioned site.'),
......@@ -176,14 +181,6 @@ function provision_drush_command() {
),
);
$items['hostmaster-make'] = array(
'description' => dt('Build a platform containing the Hostmaster user interface for provision.'),
'bootstrap' => DRUSH_BOOTSTRAP_DRUSH,
'arguments' => array(
'/path/to/platform' => dt('The path to create the platform in.')
),
);
$items['hostmaster-install'] = array(
'description' => dt('Install and verify the Hostmaster frontend.'),
'bootstrap' => DRUSH_BOOTSTRAP_DRUSH,
......@@ -216,10 +213,6 @@ function provision_drush_command() {
return $items;
}
function drush_provision_hostmaster_make($platform) {
drush_backend_invoke('make', array(dirname(__FILE__) . '/aegir.make', $platform));
}
function drush_provision_save($alias = null) {
if (drush_get_option('delete', false)) {
// remove an existing alias
......
<?php
/**
* Provision environment API
*
*
* @file Provision environment API
*/
/**
* Store and access environment objects by their alias name
* Store and access environment objects by alias name.
*
* @param $name
* A Drush alias name, including leading @.
* @param $_root_object
* Internal use only, set default object returned by d().
*
* @return
* provisionEnvironment object.
*/
function & d($name = NULL, $root_object = FALSE) {
function & d($name = NULL, $_root_object = FALSE) {
static $instances = null;
static $default_instance = '@self';
......@@ -21,7 +26,7 @@ function & d($name = NULL, $root_object = FALSE) {
return $instances[$default_instance];
}
else {
if ($root_object) {
if ($_root_object) {
$default_instance = $name;
}
......@@ -39,9 +44,8 @@ function & d($name = NULL, $root_object = FALSE) {
/**
* Simple access layer for drush_sitealias_get_record.
*
* Everytime sitealiases are fetched a lot of processing happens,
* but if the file doesnt exist yet there's a whole lot of unnecesary
* stuff happening.
* Everytime sitealiases are fetched a lot of processing happens, but if the
* file doesnt exist yet there's a whole lot of unnecesary stuff happening.
*
* We cache the result locally here.
*/
......@@ -56,7 +60,8 @@ function provision_sitealias_get_record($name) {
}
/**
* Create a new environment object and cache it in the d() static cache function.
* Create a new environment object and cache it in the d() static cache
* function.
*/
function provision_environment_factory($name) {
$classes = array('server' => 'provisionServer', 'platform' => 'provisionPlatform', 'site' => 'provisionSite');
......@@ -77,26 +82,42 @@ function provision_environment_factory($name) {
return new $classname($name);
}
/**
* Base environment class.
*
* Contains magic getter/setter functions
*/
class provisionEnvironment {
/**
* Name for saving aliases and referencing.
*/
public $name = null;
public $type = null;
/**
* 'server', 'platform', or 'site'.
*/
public $type = null;
/**
* Properties that will be persisted by provision-save. Access as object
* members, $envoronment->property_name. __get() and __set handle this. In
* init(), set defaults with setProperty().
*/
protected $properties = array();
/**
* Keeps track of properites that are names of provisionEnvironment objects.
* Set with is_oid().
*/
protected $oid_map = array();
protected $service_subs = array();
protected $parent_key = null;
protected $oid_map = array();
/**
* Retrieve value from $properties array if property does not exist in class proper.
* Retrieve value from $properties array if property does not exist in class
* proper. Properties that refer to provisionEnvironment objects will be run
* through d(), see is_oid().
*
* TODO: consider returning a reference to the value, so we can do things like:
* `$this->options['option'] = 'value'` and it will correctly set it in the
......@@ -143,7 +164,8 @@ class provisionEnvironment {
}
/**
* Remove the value from the properties array if the property does not exist in the class proper.
* Remove the value from the properties array if the property does not exist
* in the class proper.
*/
function __unset($name) {
if (isset($this->properties[$name])) {
......@@ -163,7 +185,7 @@ class provisionEnvironment {
}
/**
* Init stub function/
* Init stub function.
*/
function init() {
$this->setProperty('context_type', 'server');
......@@ -208,9 +230,17 @@ class provisionEnvironment {
/**
* Return a service object for the specific service type.
*
* This will return a specifically subscribed service object when
* one has been registered with service_subscribe, otherwise it will
* return the value specified by the property specified by $this->parent_key.
* This will return a specifically subscribed service object when one has
* been registered with service_subscribe, otherwise it will return the value
* specified by the property specified by $this->parent_key.
*
* @param $service
* Service type, such as 'http' or 'db'
* @param $name
* Override service owner with a context name as accepted by d().
*
* @return
* A provisionService object.
*/
function service($service, $name = null) {
if (isset($this->service_subs[$service])) {
......@@ -223,6 +253,16 @@ class provisionEnvironment {
return new provisionService_null($this->name);
}
}
/**
* Return environment-specific configuration options for help.
*
* @return
* array('--option' => 'description')
*/
static function option_documentation() {
return array();
}
}
/**
......@@ -232,13 +272,18 @@ class provisionEnvironment {
* objects for each of the available service types.
*/
class provisionServer extends provisionEnvironment {
/**
* Associative array of services for this server.
*
* @see provisionService
*/
protected $services = array();
static function option_documentation() {
$options = array(
'--remote_host' => 'server: host name; default localhost',
'--script_user' => 'server: OS user name; default current user',
'--aegir_root' => 'server: Aegir root; default /var/aegir',
'--aegir_root' => 'server: Aegir root; default ' . getenv('HOME'),
'--master_url' => 'server: Hostmaster URL',
);
foreach (drush_command_invoke_all('provision_services') as $service => $default) {
......@@ -263,13 +308,13 @@ class provisionServer extends provisionEnvironment {
$this->type = 'server';
$this->setProperty('remote_host', 'localhost');
if ($this->name == '@server_localhost') {
$this->setProperty('aegir_root', '/var/aegir');
if ($this->name == '@server_master') {
$this->setProperty('aegir_root', getenv('HOME'));
$this->setProperty('script_user', get_current_user());
}
else {
$this->aegir_root = d('@server_localhost')->aegir_root;
$this->script_user = d('@server_localhost')->script_user;
$this->aegir_root = d('@server_master')->aegir_root;
$this->script_user = d('@server_master')->script_user;
}
$this->backup_path = $this->aegir_root . '/backups';
......@@ -418,8 +463,8 @@ class provisionPlatform extends provisionEnvironment {
static function option_documentation() {
return array(
'--root' => 'platform: path to a Drupal installation',
'--server' => 'drush backend server; default @server_localhost',
'--web_server' => 'web server hosting the platform; default @server_localhost',
'--server' => 'drush backend server; default @server_master',
'--web_server' => 'web server hosting the platform; default @server_master',
);
}
......@@ -430,12 +475,14 @@ class provisionPlatform extends provisionEnvironment {
$this->type = 'platform';
$this->setProperty('root');
$this->setProperty('server', '@server_localhost');
$this->setProperty('server', '@server_master');
$this->is_oid('server');
$this->setProperty('web_server', '@server_localhost');
$this->setProperty('web_server', '@server_master');
$this->is_oid('web_server');
$this->service_subscribe('http', $this->web_server->name);
$this->setProperty('makefile', '');
}
function verify() {
......@@ -480,7 +527,7 @@ class provisionSite extends provisionEnvironment {
$this->setProperty('client_email');
$this->setProperty('aliases', array(), TRUE);
$this->setProperty('redirection', FALSE);
$this->setProperty('db_server', '@server_localhost');
$this->setProperty('db_server', '@server_master');
$this->is_oid('db_server');
$this->service_subscribe('db', $this->db_server->name);
......
......@@ -401,7 +401,7 @@ class provisionFileSystem extends provisionChainedState {
*
* @see file_put_contents()
*/
function file_put_contents($path, $data, $flags) {
function file_put_contents($path, $data, $flags = 0) {
$this->_clear_state();
$this->tokens = array('@path' => $path);
......
......@@ -247,7 +247,8 @@ function provision_backend_invoke($target, $command, $arguments = array(), $opti
/**