Skip to content
Merged
38 changes: 35 additions & 3 deletions examples/example.aliases.yml
Original file line number Diff line number Diff line change
Expand Up @@ -160,14 +160,16 @@
# if you set a 'remote-host' value, and your remote OS is Windows, if you
# do not set the 'OS' value, it will default to 'Linux' and could cause
# unintended consequences, particularly when running 'drush sql-sync'.
# - 'ssh-options': If the target requires special options, such as a non-
# - 'ssh': If the target requires special options, such as a non-
# standard port, alternative identity file, or alternative
# authentication method, ssh-options can contain a string of extra
# options that are used with the ssh command, eg "-p 100"
# authentication method, the `option` entry under the `ssh` item may
# contain a string of extra options that are used with the ssh command,
# e.g. "-p 100"
# - 'paths': An array of aliases for common rsync targets.
# Relative aliases are always taken from the Drupal root.
# - 'files': Path to 'files' directory. This will be looked up if not
# specified.
# - 'drush-script': Path to the remot Drush command.

Choose a reason for hiding this comment

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

Typo. :s/remot/remote/.

# - 'command': These options will only be set if the alias
# is used with the specified command. In the example below, the option
# `--no-dump` will be selected whenever the @stage alias
Expand All @@ -177,6 +179,36 @@
# - `drush sql-sync @live @stage`
# NOTE: Setting boolean options broke with Symfony 3. This will be fixed
# in a future release. See: https://github.com/drush-ops/drush/issues/2956
# - 'alias-parameters': These options will only be set if the alias is
# used as the specified parameter. `sql:sync` and `core:rsync` are the two
# core commands that use this entry. These commands both have `source`
# and `target` parameters.
Copy link
Member

Choose a reason for hiding this comment

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

can we add config-pull to this list?

#
# Complex example:
#
# @code
# # File: remote.alias.yml
# live:
# host: server.domain.com
# user: www-admin
# root: /other/path/to/drupal
# uri: http://example.com
# ssh:
# options: '-p 100'
# paths:
# drush-script: '/path/to/drush'
# command:
# site:
# install:
# options:
# admin-password: 'secret-secret'
# alias-parameters:
# target:
# core:
# rsnyc:
# options:
# excplude-paths: sites/default/files/private
# @endcode
#
# Altering aliases:
#
Expand Down
16 changes: 10 additions & 6 deletions src/Commands/sql/SqlSyncCommands.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,6 @@ public function sqlsync($source, $target, $options = ['no-dump' => false, 'no-sy
$backend_options = [];
$global_options = Drush::redispatchOptions() + ['strict' => 0];

if (Drush::simulate()) {
$backend_options['backend-simulate'] = true;
}

// Create target DB if needed.
if ($options['create-db']) {
$this->logger()->notice(dt('Starting to create database on target.'));
Expand All @@ -71,9 +67,11 @@ public function sqlsync($source, $target, $options = ['no-dump' => false, 'no-sy
);
if (!$options['no-dump']) {
$this->logger()->notice(dt('Starting to dump database on source.'));
$return = drush_invoke_process($source, 'sql-dump', array(), $dump_options, $backend_options);
$return = drush_invoke_process($sourceRecord, 'sql-dump', array(), $dump_options, $backend_options);
if ($return['error_status']) {
throw new \Exception(dt('sql-dump failed.'));
} elseif (Drush::simulate()) {
$source_dump_path = '/simulated/path/to/dump.tgz';
} else {
$source_dump_path = $return['object'];
if (!is_string($source_dump_path)) {
Expand Down Expand Up @@ -163,7 +161,10 @@ protected function injectAliasPathParameterOptions($input, $parameterName)
$aliasName = $input->getArgument($parameterName);

// Inject the source and target alias records into the alias config context.
$manager->get($aliasName)->injectIntoConfig($aliasConfigContext, $parameterName);
$aliasRecord = $manager->get($aliasName);
if (!empty($aliasRecord)) {
$manager->get($aliasName)->injectIntoConfig($aliasConfigContext, $parameterName);
}
}

/**
Expand Down Expand Up @@ -211,6 +212,9 @@ public function validate(CommandData $commandData)

public function databaseName(AliasRecord $record)
{
if ($record->isRemote() && preg_match('#\.simulated$#', $record->remoteHost())) {
return 'simulated_db';
}
$values = drush_invoke_process($record, "core-status", array(), array(), array('integrate' => false, 'override-simulated' => true));
if (is_array($values) && ($values['error_status'] == 0)) {
return $values['object']['db-name'];
Expand Down
6 changes: 5 additions & 1 deletion src/SiteAlias/AliasRecord.php
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,11 @@ protected function getParameterSpecificOptions($aliasData, $parameterName)
*/
public function legacyRecord()
{
return $this->exportConfig()->get('options', []);
$result = $this->exportConfig()->get('options', []);
if ($this->has('paths.drush-script')) {
$result['path-aliases']['%drush-script'] = $this->get('paths.drush-script');
}
return $result;
}

/**
Expand Down
28 changes: 28 additions & 0 deletions tests/CommandUnishTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,28 @@ abstract class CommandUnishTestCase extends UnishTestCase {
*/
protected $idleTimeout = 15;

/**
* Get command output and simiplify away things like full paths and extra
* whitespace.
*/
protected function getSimplifiedOutput()
{
$output = $this->getOutput();
// We do not care if Drush inserts a -t or not in the string. Depends on whether there is a tty.
$output = preg_replace('# -t #', ' ', $output);
// Remove double spaces from output to help protect test from false negatives if spacing changes subtlely
$output = preg_replace('# *#', ' ', $output);
// Debug flags may be added to command strings if we are in debug mode. Take those out so that tests in phpunit --debug mode work
$output = preg_replace('# --debug #', ' ', $output);
$output = preg_replace('# --verbose #', ' ', $output);
// Get rid of any full paths in the output
$output = str_replace(__DIR__, '__DIR__', $output);
$output = str_replace(self::getSandbox(), '__SANDBOX__', $output);
$output = str_replace(self::getSut(), '__SUT__', $output);

return $output;
}

/**
* Accessor for the last output, trimmed.
*
Expand Down Expand Up @@ -436,4 +458,10 @@ function drush_major_version() {
}
return (int)$major;
}

protected function assertOutputEquals($expected)
{
$output = $this->getSimplifiedOutput();
$this->assertEquals($expected, $output);
}
}
77 changes: 63 additions & 14 deletions tests/UnishTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,46 @@ function db_driver($db_url = NULL) {
return parse_url($db_url ?: self::getDbUrl(), PHP_URL_SCHEME);
}

/**
* Create some fixture sites that only have a 'settings.php' file
* with a database record.
*
* @param array $sites key=site_subder value=array of extra alias data
* @param string $aliasGroup Write aliases into a file named group.alias.yml
*/
function setUpSettings(array $sites, $aliasGroup = 'fixture') {
foreach ($sites as $subdir => $extra) {
$this->createSettings($subdir);
}
// Create basic site alias data with root and uri
$siteAliasData = $this->createAliasFileData(array_keys($sites), $aliasGroup);
// Add in caller-provided site alias data
$siteAliasData = array_merge_recursive($siteAliasData, $sites);
$this->writeSiteAliases($siteAliasData, $aliasGroup);
}

function createSettings($subdir) {
$settingsContents = <<<EOT
<?php

\$databases['default']['default'] = array (
'database' => 'unish_$subdir',
'username' => 'root',
'password' => '',
'prefix' => '',
'host' => '127.0.0.1',
'port' => '',
'namespace' => 'Drupal\\Core\\Database\\Driver\\mysql',
'driver' => 'mysql',
);
\$settings['install_profile'] = 'testing';
EOT;

$root = $this->webroot();
$settingsPath = "$root/sites/$subdir/settings.php";
self::mkdir(dirname($settingsPath));
file_put_contents($settingsPath, $settingsContents);
}
/**
* Assemble (and optionally install) one or more Drupal sites using a single codebase.
*
Expand All @@ -477,18 +517,33 @@ function setUpDrupal($num_sites = 1, $install = FALSE) {
copy($root . '/sites/example.sites.php', $root . '/sites/sites.php');
}

$siteData = $this->createAliasFile($sites_subdirs, 'unish');
self::$sites = [];
foreach ($siteData as $key => $data) {
self::$sites["unish.$key"] = $data;
}
}

function createAliasFileData($sites_subdirs, $aliasGroup = 'unish') {
$root = $this->webroot();
// Stash details about each site.
$sites = [];
foreach ($sites_subdirs as $subdir) {
self::$sites['unish.' . $subdir] = array(
$sites[$subdir] = array(
'root' => $root,
'uri' => $subdir,
'db_url' => $this->db_url($subdir),
);
}
return $sites;
}

function createAliasFile($sites_subdirs, $aliasGroup = 'unish') {
// Make an alias group for the sites.
$this->writeSiteAliases(self::$sites);
$sites = $this->createAliasFileData($sites_subdirs, $aliasGroup);
$this->writeSiteAliases($sites, $aliasGroup);

return self::$sites;
return $sites;
}

/**
Expand Down Expand Up @@ -525,22 +580,16 @@ function installDrupal($env = 'dev', $install = FALSE) {
*
* @param $sites
*/
function writeSiteAliases($sites) {
foreach ($sites as $name => $site) {
$groups[str_replace('unish.', '', $name)] = [
'root' => $site['root'],
'uri' => $site['uri']
];
}
$this->writeUnishConfig($groups);
function writeSiteAliases($sites, $aliasGroup = 'unish') {
$this->writeUnishConfig($sites, [], $aliasGroup);
}

function writeUnishConfig($unishAliases, $config = [])
function writeUnishConfig($unishAliases, $config = [], $aliasGroup = 'unish')
{
$etc = self::getSandbox() . '/etc/drush';
file_put_contents(Path::join($etc, 'unish.alias.yml'), Yaml::dump($unishAliases));
file_put_contents(Path::join($etc, $aliasGroup . '.alias.yml'), Yaml::dump($unishAliases, PHP_INT_MAX, 2));
$config['drush']['paths']['alias-path'][] = $etc;
file_put_contents(Path::join($etc, 'drush.yml'), Yaml::dump($config, 3));
file_put_contents(Path::join($etc, 'drush.yml'), Yaml::dump($config, PHP_INT_MAX, 2));
}

/**
Expand Down
16 changes: 0 additions & 16 deletions tests/rsyncTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,22 +86,6 @@ public function testRsyncPathAliases() {
$this->assertEquals($test_data, $actual);
}

/**
* Test to see if the output is what we expected.
*/
protected function assertOutputEquals($expected)
{
$output = $this->getOutput();
// We do not care if Drush inserts a -t or not in the string. Depends on whether there is a tty.
$output = preg_replace('# -t #', ' ', $output);
// Remove double spaces from output to help protect test from false negatives if spacing changes subtlely
$output = preg_replace('# *#', ' ', $output);
// Get rid of any full paths in the output
$output = str_replace(__DIR__, '__DIR__', $output);
$output = str_replace(self::getSandbox(), '__SANDBOX__', $output);
$this->assertEquals($expected, $output);
}

/**
* Test to see if rsync @site:%files calculates the %files path correctly.
* This tests the non-optimized code path. The optimized code path (direct
Expand Down
38 changes: 38 additions & 0 deletions tests/sqlSyncTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,44 @@
*/
class sqlSyncTest extends CommandUnishTestCase {

public function testSimulatedSqlSync() {
$fixtureSites = [
'remote' => [
'host' => 'server.isp.simulated',
'user' => 'www-admin',
'ssh' => [
'options' => '-o PasswordAuthentication=whatever'
],
'paths' => [
'drush-script' => '/path/to/drush',
],
],
'local' => [
],
];
$this->setUpSettings($fixtureSites, 'synctest');
$options = [
'simulate' => NULL,
'alias-path' => __DIR__ . '/resources/alias-fixtures',
];

// Test simulated simple rsync with two local sites
$this->drush('sql:sync', ['@synctest.remote', '@synctest.local'], $options, NULL, NULL, self::EXIT_SUCCESS, '2>&1');
$output = $this->getSimplifiedOutput();
$this->assertContains("Simulating backend invoke: ssh -o PasswordAuthentication=whatever [email protected] '/path/to/drush --backend=2 --strict=0 --alias-path=__DIR__/resources/alias-fixtures:__SANDBOX__/etc/drush --root=__SUT__/web --uri=remote sql-dump --no-ansi --gzip --result-file", $output);
$this->assertContains("Simulating backend invoke: __SUT__/vendor/drush/drush/drush --backend=2 --alias-path=__DIR__/resources/alias-fixtures:__SANDBOX__/etc/drush --uri=default core-rsync '@synctest.remote:/simulated/path/to/dump.tgz' '@synctest.local:__SANDBOX__/drush-tmp/dump.tgz' -- --remove-source-files", $output);
$this->assertContains("Simulating backend invoke: __SUT__/vendor/drush/drush/drush --backend=2 --strict=0 --alias-path=__DIR__/resources/alias-fixtures:__SANDBOX__/etc/drush --root=__SUT__/web --uri=local sql-query --no-ansi --file=__SANDBOX__/drush-tmp/dump.tgz --file-delete", $output);

// Test simulated backend invoke.
// Note that command-specific options are not processed for remote
// targets. The aliases are not interpreted at all until they recach
// the remote side, at which point they will be evaluated & any needed
// injection will be done.
$this->drush('sql:sync', ['@synctest.remote', '@synctest.local'], $options, 'user@server/path/to/drupal#sitename', NULL, self::EXIT_SUCCESS, '2>&1');
$output = $this->getSimplifiedOutput();
$this->assertContains("Simulating backend invoke: ssh -o PasswordAuthentication=whatever user@server '/path/to/drush --alias-path=__DIR__/resources/alias-fixtures:__SANDBOX__/etc/drush --root=/path/to/drupal --uri=sitename --no-ansi sql:sync '\''@synctest.remote'\'' '\''@synctest.local'\''", $output);
}

/**
* Covers the following responsibilities.
* - A user created on the source site is copied to the destination site.
Expand Down