Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deploy / Update Hook Enhancements and Bugfixes for Drush 9 #3006

Closed
wants to merge 12 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"doctrine/common": "^2.5",
"doctrine/inflector": "~1.1.0",
"drupal/coder": "^8.2.11",
"drush/drush": "^9.3.0",
"drush/drush": "dev-master#e30df96a53e51b8abb278cf1c25cd29ee30af63c as 9.3.0",
"grasmash/drupal-security-warning": "^1.0.0",
"grasmash/yaml-cli": "^1.0.0",
"grasmash/yaml-expander": "^1.2.0",
Expand Down
29 changes: 29 additions & 0 deletions scripts/blt/cache.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

/**
* @file
* Creates a unique temporary cache directory given a site, env, and uri.
* This is used for every blt command invocation on site install and update.
*/

$site = $argv[1];
$env = $argv[2];
$uri = $argv[3];


if (empty($argv[3])) {
echo "Error: Not enough arguments. Site, environment, and uri are required.\n";
exit(1);
}

// Create a temporary cache directory for this drush process only.
$cache_directory = sprintf('/mnt/tmp/%s.%s/drush_tmp_cache/%s', $site, $env, md5($uri));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is this functionally different from the existing code? It looks like this PR is assigning a cache directory based on site URI, whereas the existing site is assigning it based on db_role. Shouldn't that basically do the same thing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

passing the directory as a cli prefix ensures that the child processes spawned from the current shell inherit the value. Exporting the value in the bash shell could still result in another process changing or overriding what should be a unique value. The db_role alone isnt enough to isolate the calls to cache rebuild in particular because on ACSF the live and update environments share a database, so env differentiates the two. It is more significant that that the cache directly is in /mnt as opposed to /tmp and that this directory is also created and deleted to avoid permissions issues and race conditions

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My understanding is that using export makes the variable available to all child / sub-processes. Ref: https://stackoverflow.com/questions/1158091/defining-a-variable-with-or-without-export

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the issue is that the value can then be modified by exec or other subprocesses of the blt and respective drush commands which does not prevent cache collisions on environments with multiple web servers. if you have suggestions on how to better solve this I am happy to refactor.

shell_exec(sprintf('mkdir -p %s', escapeshellarg($cache_directory)));

if (!file_exists($cache_directory)) {
syslog(LOG_ERR, sprintf('Drush updates could not be executed, as the required cache directory [%s] is missing.', $cache_directory));
die('Missing or corrupted drush cache for this process.');
}
else {
echo "$cache_directory";
}
15 changes: 9 additions & 6 deletions scripts/factory-hooks/db-update/db-update.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/bin/bash
##!/bin/bash
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra hash here I had to comment out in order to get bash to run this.

#
# Factory Hook: db-update
#
Expand Down Expand Up @@ -26,13 +26,16 @@ blt="/var/www/html/$site.$env/vendor/acquia/blt/bin/blt"
uri=`/usr/bin/env php /mnt/www/html/$site.$env/hooks/acquia/uri.php $site $env $db_role`

# Print a statement to the cloud log.
echo "$site.$target_env: Running BLT deploy tasks on $uri domain in $env environment on the $site subscription."
echo "Running BLT deploy tasks on $uri domain in $env environment on the $site subscription."

IFS='.' read -a name <<< "${uri}"

# Set Drush cache to local ephemeral storage to avoid race conditions. This is
# done on a per site basis to completely avoid race conditions.
# Create and set Drush cache to local ephemeral storage to avoid race conditions. This is
# done on a per site process basis to completely avoid race conditions.
# @see https://github.com/acquia/blt/pull/2922
export DRUSH_PATHS_CACHE_DIRECTORY=/tmp/.drush/${db_role}

$blt drupal:update --environment=$env --site=${name[0]} --define drush.uri=$domain --verbose --yes
cacheDir=`/usr/bin/env php /mnt/www/html/$site.$env/vendor/acquia/blt/scripts/blt/cache.php $site $env $uri`

DRUSH_PATHS_CACHE_DIRECTORY=$cacheDir $blt drupal:update --environment=$env --site=${name[0]} --define drush.uri=$domain --verbose --yes --no-interaction


97 changes: 86 additions & 11 deletions scripts/factory-hooks/post-install/post-install.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,95 @@
* in your subscription. Unlike most API-based hooks, this hook does not
* take arguments, but instead executes the PHP code it is provided.
*
* This is used so that an ACSF site install will match a local BLT site
* install. After a local site install, the update functions are run.
* This is used so that an ACSF site install is identical to the local BLT site
* install, with the environment, site, and uri CLI runtime arguments overriding
* all other configuration.
*
*/

$site = $_ENV['AH_SITE_GROUP'];
$env = $_ENV['AH_SITE_ENVIRONMENT'];
$target_env = $site . $env;
use Drush\Drush;
use Drupal\Component\FileCache\FileCacheFactory;
use Drupal\Core\Database\Database;
use Drupal\Core\Site\Settings;


// Acquia hosting site / environment names
$site = getenv('AH_SITE_GROUP');
$env = getenv('AH_SITE_ENVIRONMENT');
$uri = FALSE;

// ACSF Database Role
if (!empty($GLOBALS['gardens_site_settings']['conf']['acsf_db_name'])) {
$db_role = $GLOBALS['gardens_site_settings']['conf']['acsf_db_name'];
}

$docroot = sprintf('/var/www/html/%s.%s/docroot', $site, $env);

// BLT executable
$blt = sprintf('/var/www/html/%s.%s/vendor/bin/blt', $site, $env);

global $acsf_site_name;

/**
* Exit on error.
*
* @param string $message
* A message to write to sdderr.
*/
function error($message) {
fwrite(STDERR, $message);
exit(1);
}

fwrite(STDERR, sprintf("Running updates on: site: %s; env: %s; db_role: %s; name: %s;\n", $site, $env, $db_role, $acsf_site_name));

include_once $docroot . '/sites/g/sites.inc';
$sites_json = gardens_site_data_load_file();
if (!$sites_json) {
error('The ACSF site registry could not be loaded from the server.');
}

foreach ($sites_json['sites'] as $site_domain => $site_info) {
if ($site_info['conf']['acsf_db_name'] === $db_role && !empty($site_info['flags']['preferred_domain'])) {
$uri = $site_domain;
fwrite(STDERR, "Site domain: $uri;\n");
break;
}
}
if (!$uri) {
error('Could not find the preferred domain that belongs to the site.');
}

$docroot = sprintf('/var/www/html/%s.%s/docroot', $site, $env);

// Create a temporary cache directory for this drush process.
// @TODO: use the cache.php helper script.
$cache_directory = sprintf('/mnt/tmp/%s.%s/drush_tmp_cache/%s', $site, $env, md5($uri));
shell_exec(sprintf('mkdir -p %s', escapeshellarg($cache_directory)));

// Execute the updates
$command = sprintf(
'DRUSH_PATHS_CACHE_DIRECTORY=%s %s drupal:update --environment=%s --site=%s --define drush.uri=%s --verbose --yes --no-interaction',
escapeshellarg($cache_directory),
escapeshellarg($blt),
escapeshellarg($env),
escapeshellarg($acsf_site_name),
escapeshellarg($uri)
);
fwrite(STDERR, "Executing: $command with cache dir $cache_directory;\n");

$result = 0;
$output = array();
exec($command, $output, $result);
print join("\n", $output);

// Clean up the drush cache directory.
shell_exec(sprintf('rm -rf %s', escapeshellarg($cache_directory)));

if ($result) {
fwrite(STDERR, "Command execution returned status code: $result!\n");
exit($result);
}

// The public domain name of the website.
// Run updates against requested domain rather than acsf primary domain.
$domain = $_SERVER['HTTP_HOST'];

$domain_fragments = explode('.', $_SERVER['HTTP_HOST']);
$site_name = array_shift($domain_fragments);

exec("/mnt/www/html/$site.$env/vendor/acquia/blt/bin/blt drupal:update --environment=$env --site=$site_name --define drush.uri=$domain --verbose --yes");
72 changes: 47 additions & 25 deletions settings/blt.settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,57 @@
* Setup BLT utility variables, include required files.
*/

use Acquia\Blt\Robo\Config\ConfigInitializer;
use Acquia\Blt\Robo\Config\ConfigInitializer;
use Drupal\Component\Utility\Bytes;
use Drupal\Core\DrupalKernel;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;

/**
* Host detection.
*/
if (!empty($_SERVER['HTTP_X_FORWARDED_HOST'])) {
$forwarded_host = $_SERVER['HTTP_X_FORWARDED_HOST'];
}
elseif (!empty($_SERVER['HTTP_HOST'])) {
$forwarded_host = $_SERVER['HTTP_HOST'];
/*******************************************************************************
* Host forwarding.
******************************************************************************/

// Drupal 8 aliasing/importing.
// Must be declared in global scope.
$http_host = getenv('HTTP_HOST');
$request_method = getenv('REQUEST_METHOD');
$request_uri = getenv('REQUEST_URI');
$http_x_request_id = getenv('HTTP_X_REQUEST_ID');

// Tell Drupal whether the client arrived via HTTPS. Ensure the
// request is coming from our load balancers by checking the IP address.
if (getenv('HTTP_X_FORWARDED_PROTO') == 'https'
&& getenv('REMOTE_ADDR')
&& in_array(getenv('REMOTE_ADDR'), $trusted_reverse_proxy_ips)) {
$_ENV['HTTPS'] = 'on';
$_SERVER['HTTPS'] = 'on';
putenv('HTTPS=on');
}
else {
$forwarded_host = NULL;
$x_ips = getenv('HTTP_X_FORWARDED_FOR') ? explode(',', getenv('HTTP_X_FORWARDED_FOR')) : array();
$x_ips = array_map('trim', $x_ips);

// Add REMOTE_ADDR to the X-Forwarded-For in case it's an internal AWS address.
if (getenv('REMOTE_ADDR')) {
$x_ips[] = getenv('REMOTE_ADDR');
}

$server_protocol = empty($_SERVER['HTTPS']) ? 'http' : 'https';
$forwarded_protocol = !empty($_ENV['HTTP_X_FORWARDED_PROTO']) ? $_ENV['HTTP_X_FORWARDED_PROTO'] : $server_protocol;
// Check firstly for the bal and then check for an internal IP immediately.
$settings['reverse_proxy_addresses'] = array();
if ($ip = array_pop($x_ips)) {
if (in_array($ip, $trusted_reverse_proxy_ips)) {
if (!in_array($ip, $settings['reverse_proxy_addresses'])) {
$settings['reverse_proxy_addresses'][] = $ip;
}
// We have a reverse proxy so turn the setting on.
$settings['reverse_proxy'] = TRUE;

// Get the next IP to test if it is internal.
$ip = array_pop($x_ips);
if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE)) {
if (!in_array($ip, $settings['reverse_proxy_addresses'])) {
$settings['reverse_proxy_addresses'][] = $ip;
}
}
}
}

/*******************************************************************************
* Environment detection.
Expand Down Expand Up @@ -92,12 +121,7 @@
* Site directory detection.
*/
if (!isset($site_path)) {
try {
$site_path = DrupalKernel::findSitePath(Request::createFromGlobals());
}
catch (BadRequestHttpException $e) {
$site_path = 'sites/default';
}
$site_path = \Drupal::service('site.path');
}
$site_dir = str_replace('sites/', '', $site_path);

Expand All @@ -116,7 +140,7 @@

// The hostname must match the pattern local.[sitename].com, where
// [sitename] is a value in the multisites array.
$domain_fragments = explode('.', $_SERVER['HTTP_HOST']);
$domain_fragments = explode('.', $http_host);
$name = array_slice($domain_fragments, 1);
$acsf_sites = $blt_config->get('multisites');
if (in_array($name, $acsf_sites)) {
Expand All @@ -135,8 +159,6 @@
******************************************************************************/

if ($is_ah_env) {
// Tempoary fix for CL-21595.
$_SERVER['PWD'] = DRUPAL_ROOT;
$group_settings_file = "/var/www/site-php/$ah_group/$ah_group-settings.inc";
$site_settings_file = "/var/www/site-php/$ah_group/$site_dir-settings.inc";
if (!$is_acsf_env && file_exists($group_settings_file)) {
Expand Down
5 changes: 0 additions & 5 deletions src/Robo/Commands/Setup/ConfigCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,6 @@ public function import() {

$task = $this->taskDrush()
->stopOnFail()
// Sometimes drush forgets where to find its aliases.
->drush("cc")->arg('drush')
// Rebuild caches in case service definitions have changed.
// @see https://www.drupal.org/node/2826466
->drush("cache-rebuild")
// Execute db updates.
// This must happen before features are imported or configuration is
// imported. For instance, if you add a dependency on a new extension to
Expand Down
23 changes: 23 additions & 0 deletions src/Update/Updates.php
Original file line number Diff line number Diff line change
Expand Up @@ -606,4 +606,27 @@ public function update_9001001() {
$this->updater->getOutput()->writeln("");
}

/**
* 9.1.2.
*
* @Update(
* version = "9001002",
* description = "Factory Hooks Drush 9 bug fixes and enhancements."
* )
*/
public function update_9001002() {
if (file_exists($this->updater->getRepoRoot() . '/factory-hooks')) {
$messages[] = "This update will update the files in your existing factory hooks directory.";
$messages[] = "Review the resulting files and ensure that any customizations have been re-added.";
$this->updater->executeCommand("./vendor/bin/blt recipes:acsf:init:hooks");
}
else {
$messages = ["Updated BLT. Review and commit any changes to project files."];
}
$formattedBlock = $this->updater->getFormatter()->formatBlock($messages, 'ice');
$this->updater->getOutput()->writeln("");
$this->updater->getOutput()->writeln($formattedBlock);
$this->updater->getOutput()->writeln("");
}

}
2 changes: 2 additions & 0 deletions template/drush/drush.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ drush:
include:
- '${env.home}/.drush'
- /usr/share/drush/commands
# Override file-based cache default of $HOME/.drush.
cache-directory: /tmp/.drush