$value) {
if (preg_match("/^load-([a-z\-]*)$/", $arg, $matches)) {
$module = str_replace("-", "_", $matches[1]);
// find file : can only search under provision path
$files = file_scan_directory(dirname(__FILE__), "$module.module", array('.', '..', 'CVS'), 0, TRUE, 'name');
if (isset($files[$module])) {
provision_log("notice", t("Loading @module on request", array("@module" => $module)));
$loaded[$module]['name'] = $module;
$loaded[$module]['filename'] = $files[$module]->filename;
include_once($loaded[$module]['filename']);
}
$reset = TRUE;
}
}
if ($reset && is_array($loaded)) {
$existing = array();
$list = module_list(TRUE, FALSE);
foreach ($list as $module) {
$existing[$module]['name'] = $module;
$existing[$module]['filename'] = drupal_get_filename('module', $module);
drupal_load('module', $module);
}
$loaded = array_merge($existing, $loaded);
module_list(TRUE, FALSE, TRUE, $loaded);
// Force to regenerate the stored list of hook implementations.
module_implements('', TRUE, TRUE);
// As we have missed the bootstrap phase, we need to run
// our own provision_init hook to initialize modules.
module_invoke_all('provision_init');
}
}
/**
* Implementation of hook_init
*
* Provide defines for all the major paths and settings.
* These are settings that must not be modified during the running of the
* program, but are configurable.
*/
function provision_init() {
// Do not allow the program to be run as the root user. ever
$name = posix_getpwuid(posix_geteuid());
if ($name['name'] == 'root') {
provision_log('error', 'You are running the provision script as the root user. Exiting');
provision_set_error(PROVISION_FRAMEWORK_ERROR);
provision_output($url);
}
// Set up defines for platform
$docroot = provision_get_option(array("r", "root"), $_SERVER['PWD']);
$backend = provision_get_option(array('b', 'backend'), 0);
define("PROVISION_DRUSH_BACKEND", $backend);
define('PROVISION_DOMAIN', $_SERVER['HTTP_HOST']);
// Paths
$path = ($docroot) ? $docroot : $_SERVER['DOCUMENT_ROOT'];
define('PROVISION_DOCROOT_PATH', rtrim($path, '/'));
define('PROVISION_SITES_PATH', rtrim($path, '/') .'/sites');
define('PROVISION_DRUSH_PATH', './drush.php');
$parts = explode("/", rtrim($path, '/'));
array_pop($parts);
define('PROVISION_PARENT_PATH', rtrim(implode("/" , $parts), '/'));
define('PROVISION_BACKUP_PATH',
provision_get_option('backup_path', PROVISION_PARENT_PATH . '/backups'));
define('PROVISION_CONFIG_PATH',
provision_get_option('config_path', PROVISION_PARENT_PATH .'/config'));
define('PROVISION_VHOST_PATH', PROVISION_CONFIG_PATH .'/vhost.d');
// Commands
define('PROVISION_RESTART_CMD',
provision_get_option('restart_cmd', _provision_default_restart_cmd()));
// System account
$info = posix_getgrgid(posix_getgid());
define('PROVISION_WEB_GROUP',
provision_get_option('web_group', $info['name'] ));
define('PROVISION_SCRIPT_USER',
provision_get_option('script_user', get_current_user() ));
// Redirection urls
define('PROVISION_MASTER_URL',
provision_get_option('master_url', variable_get('install_url', $GLOBALS['base_url'])));
define('PROVISION_WEB_DISABLE_URL', PROVISION_MASTER_URL .'/provision/disabled');
define('PROVISION_WEB_MAINTENENCE_URL', PROVISION_MASTER_URL .'/provision/maintenance');
// Database
define('PROVISION_MASTER_DB',
provision_get_option('master_db', $GLOBALS['db_url']));
$db = parse_url(PROVISION_MASTER_DB);
define('PROVISION_DB_USER', $db['user']);
define('PROVISION_DB_PASSWD', $db['pass']);
define('PROVISION_DB_HOST', $db['host']);
// Drupal does not support multiple types of connections in the same session
preg_match("$^([a-z]*)://$", $GLOBALS['db_url'], $matches);
define('PROVISION_DB_TYPE', $matches[1]);
// Load requested additional modules
provision_load_from_args();
}
function _provision_default_restart_cmd() {
# try to detect the apache restart command
$command = '/usr/sbin/apachectl'; # a proper default for most of the world
foreach (array('/usr/local/sbin/apachectl', # freebsd
'/usr/sbin/apache2ctl', # debian + apache2
$command) as $test) {
if (is_executable($test)) {
$command = $test;
}
}
return "sudo $command graceful";
}
/**
* Generate a provision.settings.php file to configure provision
*/
function _provision_generate_config() {
provision_log('notice', t("Generating drushrc.php file"));
provision_path("chmod", PROVISION_DOCROOT_PATH . '/drushrc.php', 0600,
t('Changed permissions of drushrc.php to @confirm'),
t('Could not change permissions of drushrc.php to @confirm'));
provision_save_platform_data();
provision_path("chmod", PROVISION_DOCROOT_PATH . '/drushrc.php', 0400,
t('Changed permissions of drushrc.php to @confirm'),
t('Could not change permissions of drushrc.php to @confirm'));
return TRUE;
}
/**
* @defgroup provisionui Configure provisioning framework.
* @{
*/
/**
* Implementation of hook_menu().
*/
function provision_menu() {
$items['provision'] = array(
'title' => 'Configure your platform',
'description' => 'Configure your platform.',
'page callback' => 'provision_front',
'type' => MENU_CALLBACK,
'access callback' => TRUE,
);
$items['provision/disabled'] = array(
'title' => 'Site disabled',
'description' => 'Page showed when a site is disabled.',
'page callback' => 'provision_disabled_site',
'type' => MENU_CALLBACK,
'access callback' => 'user_access',
'access arguments' => array('access content'),
);
$items['provision/maintenance'] = array(
'title' => 'Site is undergoing maintenance.',
'description' => 'Page shown when a site is being restored or moved etc.',
'page callback' => 'provision_site_maintenance',
'type' => MENU_CALLBACK,
'access arguments' => array('access content'),
);
$items['provision/notfound'] = array(
'title' => 'Site not found.',
'description' => 'The site you have requested is not available.',
'page callback' => 'provision_site_notfound',
'type' => MENU_CALLBACK,
'access arguments' => array('access content'),
);
return $items;
}
function provision_front() {
if (variable_get('provision_setup', FALSE)) {
drupal_goto('provision/notfound');
}
$docroot = PROVISION_DOCROOT_PATH;
$uri = PROVISION_BASE_URL;
$drush_path = rtrim(drupal_get_path('module', 'drush'), '/') . '/drush.php';
$username = PROVISION_SCRIPT_USER;
$setup_cmd = << The provision setup command inspects your environment and creates an initial configuration file in If you are using this platform as the back end to an existing Hostmaster installation: Ensure that you are logged into the shell as %script_user, and then execute the following commands :Provision Setup
command');
$return .= t('drushrc.php
.
This configuration file stores important settings such as paths, binary locations and database credentials.
you need to run this command before adding the platform node to your hosting site, to allow the hosting site to communicate with this platform.@setup_cmd
This site was disabled by the site administrators.
"; } function provision_site_maintenance() { drupal_set_breadcrumb(array()); return "This site is being worked on presently. Check back later.
"; } function provision_site_notfound() { drupal_set_breadcrumb(array()); return "The site you have requested does not exist.
"; } /** * @} End of "defgroup provisionui" */ /** * @defgroup provisiondrush Command line interface for Provision. * @{ */ /** * Implementation of hook_drush_command(). */ function provision_drush_command() { $items['provision install'] = array( 'callback' => 'provision_install_cmd', 'arguments' => array('domain.com' => t('The domain of the site to install.')), 'description' => t('Provision a new site using the provided data.') ); $items['provision import'] = array( 'callback' => '_provision_import', 'arguments' => array('domain.com' => t('The domain of the site to import.')), 'description' => t('Turn an already running site into a provisioned site.') ); $items['provision backup'] = array( 'callback' => '_provision_backup', 'arguments' => array('domain.com' => t('The domain of the site to back up.')), 'optional arguments' => array('backup-file' => t('The file to save the backup to. This will be a gzipped tarball.')), 'description' => t('Generate a back up for the site.') ); $items['provision enable'] = array( 'callback' => '_provision_enable', 'arguments' => array('domain.com' => t('The domain of the site to enable (only if enabled).')), 'description' => 'Enable a disabled site.' ); $items['provision disable'] = array( 'callback' => '_provision_disable', 'arguments' => array('domain.com' => t('The domain of the site to disable (only if disabled).')), 'description' => 'Disable a site.' ); $items['provision verify'] = array( 'callback' => '_provision_verify', 'arguments' => array('domain.com' => t('The domain of the site to verify).')), 'description' => 'Verify that the provisioning framework is correctly installed.' ); $items['provision restore'] = array( 'callback' => '_provision_restore', 'description' => 'Restore the site to a previous backup. This will also generate a backup of the site as it was.', 'arguments' => array('domain.com' => t('The domain of the site to be restored'), 'site_backup.tar.gz' => t('The backup to restore the site to.')) ); $items['provision delete'] = array( 'callback' => '_provision_delete', 'description' => 'Delete a site.' ); $items['provision cron'] = array( 'callback' => '_provision_cron', 'description' => 'Run cron process for site.', 'arguments' => array('domain.com' => t('The domain of the site to be processed')) ); if (!function_exists('hosting_setup')) { $items['provision setup'] = array( 'callback' => '_provision_setup_cmd', 'description' => 'Initialize this platform to be able to create hosted sites.',); } /* // Not implemented yet. $items['provision deploy'] = array( 'callback' => '_provision_deploy', 'description' => 'Deploy a backup made on another provisioning platform on this one.' ); $items['provision rename'] = array( 'callback' => '_provision_rename', 'description' => 'Change the url of an existing site.' ); */ return $items; } /** * Drush callback function * * Installs a new site at $url. * It does so by calling hook_provision_pre_install(), hook_provision_install() and hook_provision_post_install(). * * @param url * The url of the site being installed. * @return * Returns provision_output on success or error. * Will exit with a PROVISION_SITE_INSTALLED error if the site already exists. * Will exit with a PROVISION_FRAMEWORK_ERROR if the command is incorrectly used. */ function provision_install_cmd($url) { global $args; $data = provision_get_site_data($url); if (!$args['commands'][2]) { print "Usage: drush.php provision install DOMAIN [OPTIONS]\n"; print "Install a new site for the domain DOMAIN.\n"; print "Example: drush.php provision install mydomain.com --site-db-host localhost\n"; provision_log("error", "Incorrect usage of the provisioning framework"); provision_set_error(PROVISION_FRAMEWORK_ERROR); provision_output($url, $data); } if (provision_invoke("install", $url, $data)) { provision_save_site_data($url, $data); } provision_output($url, $data); } /** * Generate a backup of the site using a site package. * * @param url * The url of the site being backed up. * @return * Output of provision_output() function. * Will exit with a PROVISION_SITE_NOT_FOUND error if the site does not exist. */ function _provision_backup($url) { if (!_provision_drupal_site_installed($url)) { provision_log("Error", "Site has not been installed yet."); provision_set_error(PROVISION_SITE_NOT_FOUND); provision_output($url, $data); } $data = provision_get_site_data($url); $args = func_get_args(); array_shift($args); $file = array_shift($args); _provision_backup_site($url, $data, $file); $data['installed'] = TRUE; provision_save_site_data($url, $data); provision_output($url, $data); } /** * Generate a backup tarbal for a site. */ function _provision_backup_site($url, &$data, $file = NULL) { // This is the actual drupal provisioning requirements. if (!is_dir(PROVISION_BACKUP_PATH)) { provision_log("Backup directory does not exist."); provision_set_error(PROVISION_PERM_ERROR); provision_output($url, $data); } if (is_file($file)) { provision_log("File specified already exists."); provision_set_error(PROVISION_PERM_ERROR); provision_output($url, $data); } $suggested = PROVISION_BACKUP_PATH ."/$url-". date("Y-m-d", mktime()) .".tar"; // Use format of mysite.com-2008-01-02, if already existing, add number. while (is_file($suggested .'.gz')) { $count++; $suggested = PROVISION_BACKUP_PATH ."/$url-". date("Y-m-d", mktime()) ."_$count.tar"; } $data['backup_file'] = (!empty($file)) ? ereg_replace('.gz$', '', $file) : $suggested; provision_invoke("backup", $url, $data); provision_shell_exec("gzip %s", $data['backup_file']); $data['backup_file'] = $data['backup_file'] .'.gz'; } /** * Import a running Drupal site into a provisioned site. * * This is accomplished by inspecting the settings.php file and generating a site.php file. * * @param url * The url of the site being synched. * @return * Output of provision_output() function. * Will exit with a PROVISION_SITE_NOT_FOUND error if the site does not exist. */ function _provision_import($url) { if (!$url) { print "Usage: drush.php provision import DOMAIN\n"; provision_set_error(PROVISION_FRAMEWORK_ERROR); provision_output($url, $data); } $data = provision_get_site_data($url); provision_invoke("import", $url, $data); provision_save_site_data($url, $data); provision_output($url, $data); } /** * Import a running Drupal site into a provisioned site. * * This is accomplished by inspecting the settings.php file and generating a site.php file. * * @param url * The url of the site being verified. * Optional, if no site is specified the platform will be verified * @return * Output of provision_output() function. * Will exit with a PROVISION_SITE_NOT_FOUND error if the site specified does not exist. */ function _provision_verify($url = NULL) { if (!$url) { // we are verifying a platform $data = array(); _provision_create_dir(PROVISION_CONFIG_PATH, t('Provision configuration'), 0755); _provision_create_dir(PROVISION_BACKUP_PATH, t('Web server configuration'), 0700); } else { // we are verifying a site if (!_provision_drupal_site_installed($url)) { provision_log("error", "Site has not been installed yet."); provision_set_error(PROVISION_SITE_NOT_FOUND); provision_output(); } $data = provision_get_site_data($url); } // We only save the settings if they validated correctly, not before. if (provision_invoke("verify", $url, $data)) { if (!$url) { _provision_generate_config(); } else { provision_save_site_data($url, $data); } } provision_output($url, $data); } /** * Restore command implementation * * This command when called will * 1. Make a backup of the current site, before modifications are made. * 2. Temporarily disable the site by causing apache to redirect to a help page. Restarting apache is required. * 3. Extract the backup that is being restored to to a temporary folder in the sites directory. * 4. Create a new database, belonging to the site's user, and switch it around with the current site's database. * 5. Import the old database and site.php details. * 6. Switch around the sites directory of the current site and the backup being restored. * 7. Regenerate configuration files. * 8. TODO: diagnostic to test that everything is ok? * 9. Remove the temporary redirect and restart apache so the previous site is available again. * 10. Remove the extranuous db and duplicate site directory. * * If at any time an error occurs, before step 9. It should reverse all the changes it has made, * and leave the current site directory and database in the right place, and remove all cruft that * was created by this process. */ function _provision_restore($url, $restore_file) { if (!($exists = _provision_drupal_site_installed($url))) { // this can probably be done more consistently with another // provision_path like function. provision_log("Error", "Site has not been installed yet."); provision_set_error(PROVISION_SITE_NOT_FOUND); } $exists &= provision_path("exists", $restore_file, TRUE, t("Restoring site from @path"), t("Could not find backup file @path"), PROVISION_FRAMEWORK_ERROR); if ($exists) { $data = provision_get_site_data($url); $data['restore_file'] = $restore_file; if (provision_invoke("restore", $url, $data)) { provision_save_site_data($url, $data); } } provision_output($url, $data); } function provision_provision_pre_restore($url, &$data) { _provision_backup_site($url, $data); // Backup site for posterity, before rolling back. provision_path("extract", $data['restore_file'], PROVISION_SITES_PATH ."/$url.restore", t('Successfully extracted the contents of @path to @confirm'), t('Failed to extract the contents of @path to @confirm'), PROVISION_PERM_ERROR, PROVISION_FRAMEWORK_ERROR); } function provision_provision_pre_restore_rollback($url, $data) { _provision_recursive_delete(PROVISION_SITES_PATH ."/$url.restore"); } function provision_provision_post_restore($url, $data) { _provision_recursive_delete(PROVISION_SITES_PATH ."/$url.restore"); } function _provision_disable($url) { if (!_provision_drupal_site_installed($url)) { provision_log("Error", "Site has not been installed yet."); provision_set_error(PROVISION_SITE_NOT_FOUND); provision_output($url, $data); } $data = provision_get_site_data($url); _provision_backup_site($url, $data); # Backup site for posterity, before disabling if (provision_invoke("disable", $url, $data)) { $data['enabled'] = FALSE; provision_save_site_data($url, $data); } provision_output($url, $data); } function _provision_enable($url) { if (!_provision_drupal_site_installed($url)) { provision_log("Error", "Site has not been installed yet."); provision_set_error(PROVISION_SITE_NOT_FOUND); provision_output($url, $data); } $data = provision_get_site_data($url); if (provision_invoke("enable", $url, $data)) { $data['enabled'] = TRUE; provision_save_site_data($url, $data); } provision_output($url, $data); } function _provision_delete($url) { $data = provision_get_site_data($url); $args = func_get_args(); array_shift($args); $file = array_shift($args); _provision_backup_site($url, $data, $file); provision_invoke("delete", $url, $data); provision_output($url, $data); } /** * Initial setup of platform * * Creates symlink to drush.php * Creates config path * Creates drushrc path * * This function is re-used by the hosting_setup command, as it is a superset of this functionality. */ function _provision_setup() { $success = TRUE; $drush_path = sprintf("%s/%s/drush.php", PROVISION_DOCROOT_PATH, drupal_get_path('module', 'drush')); $success &= provision_path('symlink', $drush_path, PROVISION_DOCROOT_PATH . '/drush.php', t('Created symlink for drush.php file'), t('Could not create symlink for drush.php'), PROVISION_FRAMEWORK_ERROR); $success &= _provision_generate_config(); return $success; } /** * Drush command wrapper for the setup of the platform */ function _provision_setup_cmd() { if (_provision_setup()) { variable_set('provision_setup', TRUE); } // @TODO use provision_output for this, but we need pretty print first. $logs = provision_get_log(); foreach ($logs as $log) { print "$log[message]\n"; } if (provision_get_error()) { print "\nThe command did not complete successfully, please fix the issues and re-run this script."; } } /** * Drush command to run cron */ function _provision_cron($url) { if (!_provision_drupal_site_installed($url)) { provision_log("Error", "Site has not been installed yet."); provision_set_error(PROVISION_SITE_NOT_FOUND); provision_output($url, $data); } $data = provision_get_site_data($url); _provision_drupal_switch_active_site($url); drupal_cron_run(); _provision_drupal_switch_active_site(); }