Commit 83f57f6e authored by Adrian Rossouw's avatar Adrian Rossouw Committed by adrian

In progress commit. Remove dependency on date module. Lots of help additions....

In progress commit. Remove dependency on date module. Lots of help additions. Added verify step so the back end (mostly) configures itself. Switched templating to use php instead of tokens (needed loops in templates). Turned options to use underscore instead of dash for separation (now consistent). Lots of validation and logging, including strict permission checking.
parent 6d99b240
......@@ -81,7 +81,7 @@ function provision_invoke($hook, $url, &$data, $rollback = false) {
* @param extra
* An associative array containing additional data to be returned from the command. @see provision_stats_stats()
*/
function provision_output($url, $data, $extra = null) {
function provision_output($url = null, $data = array(), $extra = null) {
$return = $extra;
$return['site'] = $data;
$return['error_status'] = provision_get_error(); // error code being returned
......@@ -91,14 +91,15 @@ function provision_output($url, $data, $extra = null) {
print serialize($return);
}
else {
foreach (provision_get_log() as $entry) {
printf("%10s|%10s|%10%s", $entry['timestamp'], $entry['type'], $entry['severity'], $entry['message']);
}
if ($data) {
if ($return) {
if ($output = theme("provision_" . $data['action_type'] . "_output", $url, $return)) {
return $output;
} else {
/** TODO : return a cleanly formatted display of all the necessary information */
print_r($return);
}
}
}
exit(provision_get_error());
}
......@@ -245,22 +246,22 @@ function provision_get_site_data($url) {
global $args;
#TODO: Accept serialized string via unix pipe.
foreach ($args['options'] as $key => $value) {
if (preg_match("/^site-/", $key)) {
if (preg_match("/^site_/", $key)) {
$site_data[$key] = $value;
}
}
$site_data['site-url'] = $url;
$site_data['site-action-type'] = $args['commands'][1];
$docroot = variable_get('provision_root', '');
$site_data['site-document-root'] = ($docroot) ? $docroot . '/webroot' : $_SERVER['DOCUMENT_ROOT'];
$site_data['site-temporary-url'] = str_replace('.', '-', $url) . '.' . variable_get('provision_tempurl_base', $_SERVER['HTTP_HOST']);
$site_data['site-profile'] = ($site_data['site-profile']) ? $site_data['site-profile'] : variable_get('provision_default_profile', 'default');
$site_data['site-ip'] = variable_get('provision_apache_server_ip', '127.0.0.1');
$site_data['site-port'] = variable_get('provision_apache_server_ip', 80);
$site_data['site_url'] = $url;
$site_data['site_action_type'] = $args['commands'][1];
$docroot = drush_get_option("r", $_SERVER['PWD']);
$site_data['site_document_root'] = ($docroot) ? $docroot . '/webroot' : $_SERVER['DOCUMENT_ROOT'];
$site_data['site_profile'] = ($site_data['site_profile']) ? $site_data['site_profile'] : variable_get('provision_default_profile', 'default');
$site_data['site_ip'] = variable_get('provision_apache_server_ip', '127.0.0.1');
$site_data['site_port'] = variable_get('provision_apache_server_ip', 80);
if ($old_data = provision_load_site_data($url)) {
# Merge previously saved data with the new data. This way, old parameters overwrite new ones.
$site_data = array_merge(provision_load_site_data($url), $site_data);
}
return $site_data;
}
......@@ -303,9 +304,9 @@ function provision_save_site_data($url, $data) {
}
else {
fwrite($fp, "<?php\n");
$action = array('id' => $data['action-id'],
$action = array('id' => $data['action_id'],
'timestamp' => mktime(),
'action' => $data['site-action-type'],
'action' => $data['site_action_type'],
'success' => $data['success']);
$line = "\n\n\$actions[] = " . var_export($action, TRUE) . ';';
fwrite($fp, $line);
......@@ -342,16 +343,16 @@ function provision_save_site_data($url, $data) {
* A keyed array listing the substitution tokens. Elements should be
* in the form of: $list[$type][$token] = $description
*/
function provision_token_values($type, $object = null) {
global $args;
switch ($type) {
case 'site':
$values = array_merge(provision_get_site_data($args['commands'][2]), $object) ;
break;
}
return (array) $values;
#function provision_token_values($type, $object = null) {
# global $args;
# switch ($type) {
# case 'site':
# $values = array_merge(provision_get_site_data($args['commands'][2]), $object) ;
# break;
# }
# return (array) $values;
#}
}
/**
* Implementation of hook_token_list().
*
......@@ -365,18 +366,30 @@ function provision_token_values($type, $object = null) {
function provision_token_list($type = 'all') {
if ($type == 'site') {
/** TODO: Complete the token list to allow the front end to more easily edit the settings. */
$tokens['site']['site-url'] = t("The domain name used to access the site. This is defaulted to the value used on the command line.");
$tokens['site']['site-db-type'] = t("The type of database server used");
$tokens['site']['site-db-username'] = t("Username to access database for site");
$tokens['site']['site-db-password'] = t("Password to access database for site");
$tokens['site']['site-db-name'] = t("Database name for the site");
$tokens['site']['site-profile'] = t("Install profile of site");
$tokens['site']['site-action-type'] = t("What type of action has been used. Only used in conjuction with hosting front end");
$tokens['site']['site-action-type'] = t("What type of action has been used. Only used in conjuction with hosting front end");
$tokens['site']['site_url'] = t("The domain name used to access the site. This is defaulted to the value used on the command line.");
$tokens['site']['site_db_type'] = t("The type of database server used");
$tokens['site']['site_db_username'] = t("Username to access database for site");
$tokens['site']['site_db_password'] = t("Password to access database for site");
$tokens['site']['site_db_name'] = t("Database name for the site");
$tokens['site']['site_profile'] = t("Install profile of site");
$tokens['site']['site_action_type'] = t("What type of action has been used. Only used in conjuction with hosting front end");
$tokens['site']['site_action_type'] = t("What type of action has been used. Only used in conjuction with hosting front end");
}
return $tokens;
}
/**
* Generate the text for a config file using php
*/
function provision_render_config($template, $variables) {
extract($variables, EXTR_SKIP); // Extract the variables to a local namespace
ob_start(); // Start output buffering
eval("?>" . $template); // Generate content
$contents = ob_get_contents(); // Get the contents of the buffer
ob_end_clean(); // End buffering and discard
return $contents; // Return the contents
}
/**
* @} End of "defgroup provisiontokens".
*/
......@@ -388,11 +401,6 @@ function provision_token_list($type = 'all') {
*/
function provision_confirm_drush() {
#confirm that code is running through the command line.
if (php_isapi_name() != 'cli') {
drupal_set_message(t("This function should only be called via the command line Drush utility."));
return false;
}
return true;
}
......@@ -405,28 +413,32 @@ function _provision_backup_path() {
return variable_get('provision_backup_path', implode("/" , $parts) . '/backups');
}
/**
* Get the drushrc path of the Provision installation
* Get the base configuration path for the system
*/
function _provision_drushrc_path() {
$parts = explode("/", $_SERVER['DOCUMENT_ROOT']);
function _provision_config_path() {
$path = ($_SERVER['PWD']) ? $_SERVER['PWD'] : $_SERVER['DOCUMENT_ROOT'];
$parts = explode("/", $path);
array_pop($parts);
return variable_get('provision_drushrc_path', implode("/" , $parts) . '/platform.d');
return variable_get('provision_apache_config_path', implode("/" , $parts) . '/config') ;
}
/**
* Get the vhost path of the Provision installation
*/
function _provision_vhost_path() {
$parts = explode("/", $_SERVER['DOCUMENT_ROOT']);
array_pop($parts);
return variable_get('provision_vhost_path', implode("/" , $parts) . '/vhost.d');
return _provision_config_path() . '/vhost.d';
}
function _provision_path_can_be_created($path) {
/**
* Get the drushrc path of the Provision installation
*/
function _provision_drushrc_path() {
return _provision_config_path() . '/drush';
}
/**
* Wrapper around drush_shell_exec to provide sprintf functionality with some more safety.
*/
......@@ -456,11 +468,13 @@ function provision_set_active_db($new_db_url = null) {
#initialize static
if (!$old_db_url) {
$old_db_url = $db_url;
$db_url = array();
$db_url['default'] = $old_db_url;
}
if ($new_db_url) {
$db_url = $new_db_url;
db_set_active();
$db_url['new'] = $new_db_url;
db_set_active('new');
}
else {
$db_url = $old_db_url;
......@@ -486,3 +500,57 @@ function provision_get_group_name() {
$info = posix_getgrgid(posix_getgid());
return variable_get('provision_group', $info['name']);
}
/**
* Perform tests on directory.
*
* Perform test on path, and log error messages / codes on failure.
*/
function provision_check_path($path, $type, $test = true, $succeed_message = '', $fail_message = '', $error_codes = null) {
# The code style is a bit weird here, but it's a bit easier to read this way.
switch ($type) {
case "writeable" :
case "writable" : $value = is_writable($path);
break;
case "exists" : $value = file_exists($path);
break;
case "readable" : $value = is_readable($path);
break;
case "is_dir" : $value = is_dir($path);
break;
case "owner" : $value = fileowner($path);
break;
case "group" : $value = filegroup($path);
break;
case "mkdir" : $value = mkdir($path, 0770, true);
break;
case "chmod" : chmod($path, $test);
$value = sprintf("%o", fileperms($path));
break;
case "chown" : chown($path, $test);
$value = fileowner($path);
break;
case "chgrp" : chgrp($path, $test);
$value = filegroup($path);
break;
case "unlink" : $value = (file_exists($path)) ? unlink($path) : false;
break;
case "rmdir" : $value = (file_exists($path) && is_dir($path)) ? rmdir($path) : false;
break;
}
$status = ($value == $test) ? true : false;
if ($status) {
if ($succeed_message) {
provision_log("message", $succeed_message);
}
}
else {
if ($error_codes) {
provision_set_error($error_codes);
}
if ($fail_message) {
provision_log("error", $fail_message);
}
}
return $status;
}
\ No newline at end of file
......@@ -37,10 +37,50 @@
/** Include the provisioning API. */
include_once('provision.inc');
/**
* Implementation of hook_provision_service
*/
function provision_provision_service() {
return t("Basic configuration");
}
/**
* Implementation of hook_perm().
*/
function provision_perm() {
return array("administer provisioning");
}
/**
* @defgroup provisionui Configure provisioning framework.
* @{
*/
/**
* Implementation of hook_menu().
*/
function provision_menu($may_cache = true) {
if ($may_cache) {
$items[] = array(
'path' => 'admin/settings/provision',
'title' => t('Provisioning'),
'description' => t("Configure how new Drupal sites will be provisioned."),
'callback' => 'drupal_get_form',
'callback arguments' => array('provision_configure'),
'access' => user_access('administer provisioning'),
);
$items[] = array(
'path' => 'admin/help/provision/requirements',
'title' => t('Provisioning requirements'),
'description' => t("Information of how to configure the provisioning system."),
'callback' => 'provision_help_requirements',
'type' => MENU_CALLBACK
);
}
return $items;
}
/**
* Implementation of hook_help()
*/
......@@ -60,7 +100,9 @@ function provision_help($section) {
$output .= "<dl>";
foreach ($commands as $command => $info) {
if (preg_match('/^provision/', $command)) {
if (sizeof($info['arguments'])) {
$command .= ' ' . implode(' ', (array) key($info['arguments']));
}
if (sizeof($info['optional arguments'])) {
$command .= ' [' . implode('] [', (array) key($info['optional arguments'])) . ']';
}
......@@ -105,20 +147,23 @@ EOF;
username to "<code>@username</code>" and the group to "<code>@group</code>",
but you can change these in the <a href="@provision_link">provisioning section</a>.</p>', array("@username" => $username, "@group" => $group, "@provision_link" => url('admin/settings/provision'))) . '</li>';
$output .= '<li>' . t('<strong>Write access to a directory to store backups.</strong>
$output .= '<li>' . t('<p><strong>Write access to a directory to store backups.</strong>
The drush user needs to be able to maintain the backups repository to ensure that your site is backed up successfully.
It is incredibly important that this path is not accessible via the web server, so that no undesirables can get their
hands on your database. The recommended path is directly above your platform path, but it can be anywhere.<br />
hands on your database. The recommended path is directly above your platform path, but it can be anywhere.</p>
Based on your server configuration we have determined that your path should be <code>@backup_path</code>,
but you can change it change them in the <a href="@provision_link">provisioning section</a><br />
To create and configure this directory correctly, please enter the following commands :
<p>Based on your server configuration we have determined that your path should be <code>@backup_path</code>,
but you can change this in the <a href="@provision_link">provisioning section</a>.</p>
<strong>To configure: </strong> Please enter the following commands :
<pre>@mkdir_cmd</pre>',$mkdir_cmd) . '</li>';
$output .= "</ol>";
return $output;
}
}
/**
* Page callback with in depth requirement documentation
*/
function provision_help_requirements() {
$output .= t('<p>Unfortunately, due to the requirements of some of the functionality, significantly more access than is usually allowed on a shared hosting solution is required,
and as such, a virtual server or dedicated hosting system will be required to run this system.</p>');
......@@ -128,7 +173,7 @@ function provision_help_requirements() {
$name = module_invoke($module, 'provision_service');
$help = module_invoke($module, 'help', 'admin/help/provision#requirements');
if ($name && $help) {
$output .= '<h3>' . t($name) . '</h3>';
$output .= '<a href="requirements-$module"></a><h3>' . t($name) . '</h3>';
$output .= $help;
}
}
......@@ -136,42 +181,6 @@ function provision_help_requirements() {
return $output;
}
/**
* Implementation of hook_perm().
*/
function provision_perm() {
return array("administer provisioning");
}
/**
* @defgroup provisionui Configure provisioning framework.
* @{
*/
/**
* Implementation of hook_menu().
*/
function provision_menu($may_cache = true) {
if ($may_cache) {
$items[] = array(
'path' => 'admin/settings/provision',
'title' => t('Provisioning'),
'description' => t("Configure how new Drupal sites will be provisioned."),
'callback' => 'drupal_get_form',
'callback arguments' => array('provision_configure'),
'access' => user_access('administer provisioning'),
);
$items[] = array(
'path' => 'admin/help/provision/requirements',
'title' => t('Provisioning requirements'),
'description' => t("Information of how to configure the provisioning system."),
'callback' => 'provision_help_requirements',
'type' => MENU_CALLBACK
);
}
return $items;
}
/**
* Menu callback.
......@@ -197,15 +206,7 @@ function provision_configure() {
* Implementation of provision_configure
*/
function provision_provision_configure() {
$form['provision_tempurl_base'] = array(
'#type' => 'textfield',
'#required' => TRUE,
'#title' => t('Temporary URL base'),
'#description' => t('Each new domain that gets created gets created with a temporary url base that allows it to be immediately accessible.'),
'#default_value' => variable_get('provision_tempurl_base', $_SERVER['HTTP_HOST']),
'#size' => 40,
'#maxlength' => 255,
);
$form['provision_user'] = array(
'#type' => 'textfield',
......@@ -224,7 +225,15 @@ function provision_provision_configure() {
'#size' => 40,
'#maxlength' => 255,
);
$form['provision_backup_path'] = array(
'#type' => 'textfield',
'#required' => TRUE,
'#title' => t('Path to backup directory'),
'#description' => t('The file system location where backups will be stored.'),
'#default_value' => _provision_backup_path(),
'#size' => 40,
'#maxlength' => 255,
);
return $form;
}
......@@ -241,6 +250,7 @@ function provision_provision_configure() {
* 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.')),
......@@ -252,11 +262,13 @@ function provision_drush_command() {
'arguments' => array('domain.com' => t('The domain of the site to synch.')),
'description' => t('Regenerate all the config files for a site.')
);
$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.')),
......@@ -274,6 +286,12 @@ function provision_drush_command() {
'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 disable (only if disabled).')),
'description' => 'Verify that the provisioning framework is correctly installed.'
);
/*
// Not implemented yet.
$items['provision delete'] = array(
......@@ -322,7 +340,7 @@ function provision_install_cmd($url) {
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_set_log("error", "Incorrect usage of the provisioning framework");
provision_log("error", "Incorrect usage of the provisioning framework");
drupal_set_error(PROVISION_FRAMEWORK_ERROR);
provision_output($url, $data);
}
......@@ -389,17 +407,17 @@ function _provision_pre_install($url, &$data) {
* Boolean denoting whether the provision_invoke rolled back changes made.
*/
function _provision_install($url, &$data) {
#safety mechanism to ensure back end calls are not made via the front end.
if (!provision_confirm_drush()) return;
$rolled_back = provision_invoke("install", $url, $data);
# if (!$rolled_back) {
if (!$rolled_back) {
_provision_drupal_switch_active_site($url); # Change headers and db info, also backs up
_provision_drupal_force_load_modules($url);
_provision_drupal_install_schema($data['site-profile']);
_provision_drupal_install_schema($data['site_profile']);
_provision_drupal_force_load_modules();
_provision_drupal_switch_active_site(); # This *should* bring the site back to where we were before installing
# }
}
return $rolled_back;
}
......@@ -528,13 +546,58 @@ function _provision_import($url) {
include_once("sites/$url/settings.php");
$parts = parse_url($db_url);
$data['site-db-type'] = $parts['scheme'];
$data['site-db-user'] = $parts['user'];
$data['site-db-host'] = $parts['host'];
$data['site-db-passwd'] = $parts['pass'];
$data['site-db-name'] = substr($parts['path'], 1);
$data['site-profile'] = $installed_profile;
$data['site_db_type'] = $parts['scheme'];
$data['site_db_user'] = $parts['user'];
$data['site_db_host'] = $parts['host'];
$data['site_db_passwd'] = $parts['pass'];
$data['site_db_name'] = substr($parts['path'], 1);
$data['site_profile'] = $installed_profile;
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 synched.
* @return
* Output of provision_output() function.
* Will exit with a PROVISION_SITE_NOT_FOUND error if the site does not exist.
*/
function _provision_verify($url = '') {
$path = _provision_config_path();
$exists = provision_check_path($path, "exists", true ,
t("Provision configuration path exists."),
t("Provision configuration path does not exist."));
if (!$exists) {
$made = provision_check_path($path, "mkdir", true,
t("Provision configuration path has been created."),
t("Provision configuration could not be created."),
PROVISION_PERM_ERROR | PROVISION_FRAMEWORK_ERROR);
}
provision_check_path($path, "writable", true ,
t("Provision configuration path is writable."),
t("Provision configuration path is not writable."),
PROVISION_PERM_ERROR | PROVISION_FRAMEWORK_ERROR);
provision_check_path($path, "chown", provision_get_script_owner(),
t("Changed ownership of <code>%path</code>", array("%path" => $path)),
t("Could not change ownership <code>%path</code>", array("%path" => $path)),
PROVISION_PERM_ERROR | PROVISION_FRAMEWORK_ERROR );
provision_check_path($path, "chmod", 0700,
t("Changed permissions of <code>%path</code> to %perms", array("%path" => $path, '%perms' => 0700)),
t("Could not change ownership <code>%path</code> to %perms", array("%path" => $path, '%perms' => 0700)),
PROVISION_PERM_ERROR | PROVISION_FRAMEWORK_ERROR );
$data = array();
$rolled_back = provision_invoke("verify", $url, $data);
if ($url) {
provision_save_site_data($url, $data);
}
provision_output($url, $data);
}
......@@ -92,14 +92,15 @@ function provision_apache_provision_configure() {
'#rows' => 5,
);
$default_path = variable_get('provision_root', ereg_replace("/webroot$", "", $_SERVER['DOCUMENT_ROOT'])) . '/vhost.d';
$form['provision_apache_vhost_path'] = array(
$default_path = variable_get('provision_config_path', ereg_replace("/webroot$", "", $_SERVER['DOCUMENT_ROOT'])) . '/config';
$form['provision_apache_config_path'] = array(
'#type' => 'textfield',
'#title' => t('Path to the directory to store apache configuration files for hosted sites'),
'#title' => t('Path to the directory to store configuration files for hosted sites.'),
'#size' => 40,
'#default_value' => variable_get('provision_apache_vhost_path', $default_path),
'#default_value' => variable_get('provision_config_path', $default_path),
'#maxlength' => 255,
);
$form['provision_apache_restart_cmd'] = array(
'#type' => 'textfield',
'#title' => t('Apache restart command'),
......@@ -116,21 +117,7 @@ function provision_apache_provision_configure() {
* The default template provided for the virtual host configuration
*/
function _provision_apache_default_template() {
return <<<EOF
<VirtualHost *:80>
ServerAdmin [site-email]
DocumentRoot [site-document-root]
ServerName [site-url]
ServerAlias [site-temporary-url]
ServerAlias www.[site-url]
# Error handler for Drupal > 4.6.7
<Directory "[site-document-root]/sites/[site-url]/files">
SetHandler This_is_a_Drupal_security_line_do_not_remove
</Directory>
</VirtualHost>
EOF;
return file_get_contents(drupal_get_path('module', 'provision_apache') . "/templates/apache_vhost.tpl.php");
}
/**
......@@ -176,10 +163,7 @@ function provision_apache_provision_disable($url, &$data) {
/**
* Implementation of hook_provision_sync
*/
function provision_apache_provision_sync($url, &$data) {
#safety mechanism to ensure back end calls are not made via the front end.
if (!provision_confirm_drush()) return null;
function provision_apache_provision_synch($url, &$data) {
_provision_apache_create_vhost_config($url, $data);
_provision_apache_restart_apache();
}
......@@ -188,42 +172,30 @@ function provision_apache_provision_sync($url, &$data) {
* Delete virtual host file
*/
function _provision_apache_delete_vhost_config($url, $data) {
#safety mechanism to ensure back end calls are not made via the front end.
if (!provision_confirm_drush()) return null;
$vhost_path = variable_get('provision_apache_vhost_path', 'vhost.d');
if (file_exists()) {