Last active
August 19, 2016 21:08
-
-
Save grasmash/069c744b20cd01388e03 to your computer and use it in GitHub Desktop.
Symfony-based VM tasks
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Download Drupal VM for the project. | |
* | |
* This is accomplished through a `git clone` to the 'box' directory. The | |
* '.git' directory is then removed from 'box'. | |
* | |
* @param InputInterface $input | |
* @param OutputInterface $output | |
*/ | |
protected function addVm(InputInterface $input, OutputInterface $output) { | |
$output->writeln('<info>Cloning Drupal VM from GitHub...</info>'); | |
// Add Drupal VM Vagrant box repository and then remove the .git files. | |
$vm_dir = 'box'; | |
$this->remove("{$this->newProjectDirectory}/$vm_dir"); | |
// We are intentionally pinning to a specific release for stability. | |
$this->git( | |
'clone', array( | |
'1.9.5', | |
"[email protected]:geerlingguy/drupal-vm.git", | |
"{$this->newProjectDirectory}/$vm_dir", | |
), | |
array('branch' => NULL) | |
); | |
$this->remove("{$this->newProjectDirectory}/$vm_dir/.git"); | |
$this->configureVm($input, $output); | |
$this->bootstrapVm($input, $output); | |
} | |
/** | |
* Configure Drupal VM for the project. | |
* | |
* @param InputInterface $input | |
* @param OutputInterface $output | |
*/ | |
protected function configureVm(InputInterface $input, OutputInterface $output) { | |
$vm_dir = 'box'; | |
// Load the example configuration file included with Drupal VM. | |
$parser = new Parser(); | |
$vm_config = $parser->parse(file_get_contents("{$this->newProjectDirectory}/$vm_dir/example.config.yml")); | |
// Add the scripts directory to synced folders list. | |
$vm_config['vagrant_synced_folders'][] = array( | |
'local_path' => "{$this->newProjectDirectory}/scripts", | |
'destination' => '/scripts', | |
'id' => 'project_template_scripts', | |
'type' => 'nfs', | |
); | |
// Add the tests directory to the synced folders list. | |
$vm_config['vagrant_synced_folders'][] = array( | |
'local_path' => "{$this->newProjectDirectory}/tests", | |
'destination' => '/tests', | |
'id' => 'project_template_tests', | |
'type' => 'nfs', | |
); | |
// Use the docroot as the site's primary synced folder. | |
$mount_point = "/var/www/{$this->config['project']['acquia_subname']}"; | |
$vm_config['vagrant_synced_folders'][0]['local_path'] = "{$this->newProjectDirectory}/docroot"; | |
$vm_config['vagrant_synced_folders'][0]['destination'] = $mount_point; | |
// Specify that no CRON tasks are setup. | |
$vm_config['drupalvm_cron_jobs'] = array( | |
array( | |
// Provide CRON a name. | |
'name' => 'Local Drupal CRON', | |
// A duration between CRON tasks. | |
'minute' => '*/60', | |
// The CRON job to execute. | |
'job' => 'drush -r {{ drupal_core_path }} core-cron', | |
) | |
); | |
// Use the projects key to separate multiple DrupalVM instances. | |
$vm_config['vagrant_machine_name'] = $this->config['project']['acquia_subname']; | |
// Map the desired IP address for the project. | |
$vm_config['vagrant_ip'] = $this->config['vm']['vagrant_ip']; | |
// Mimic Acquia Cloud configuration. | |
$vm_config['vagrant_box'] = 'geerlingguy/ubuntu1204'; | |
$vm_config['php_version'] = '5.5'; | |
$vm_config['solr_version'] = '4.5.1'; | |
// Specify the VM extras you wish you install for this project. | |
$vm_config['installed_extras'] = $this->config['vm']['installed_extras']; | |
// Specify project specific global Composer packages. | |
$vm_config['composer_global_packages'] = $this->config['vm']['composer_global_packages']; | |
// Update domain configuration. | |
$local_url = parse_url($this->config['project']['local_url']); | |
$vm_config['vagrant_hostname'] = $local_url['host']; | |
$vm_config['drupal_domain'] = $local_url['host']; | |
$vm_config['drupal_site_name'] = $this->config['project']['human_name']; | |
$vm_config['drupal_core_path'] = $mount_point; | |
$vm_config['drupal_major_version'] = $this->config['vm']['drupal_major_version']; | |
// Set apache vhosts by project, not to DrupalVM default. | |
$vm_config['apache_vhosts'][1]['servername'] = 'xhprof.' . $local_url['host']; | |
$vm_config['apache_vhosts'][2]['servername'] = 'pimpmylog.' . $local_url['host']; | |
// Update the path to make file. | |
$make_file = $this->config['project']['make_file']; | |
$vm_config['drush_makefile_path'] = '/scripts/' . $make_file; | |
// Remove makefile extension. | |
$vm_config['drupal_install_profile'] = $this->config['project']['install_profile']; | |
// Update other important settings. | |
$vm_config['drupal_enable_modules'] = []; | |
$vm_config['extra_apt_packages'] = []; | |
// Do not execute subsequent drush make within the VM since files are in docroot. | |
$vm_config['build_makefile'] = FALSE; | |
$vm_config['install_site'] = TRUE; | |
// Set the installed version of drush | |
$vm_config['drush_version'] = $this->config['vm']['drush_version']; | |
// Write adjusted config.yml to disk. | |
$this->fs->dumpFile("{$this->newProjectDirectory}/$vm_dir/config.yml", Yaml::dump($vm_config, 4, 2)); | |
$output->writeln("<info>Drupal VM was installed to `{$this->config['project']['acquia_subname']}/box`.</info>"); | |
} | |
/** | |
* Check the dependencies needed for the VM to load on the host machine. | |
* | |
* @param InputInterface $input | |
* @param OutputInterface $output | |
*/ | |
protected function checkVmDependencies(InputInterface $input, OutputInterface $output) { | |
// We need to do a full audit before returning. As such, we should check for errors more broadly. | |
$has_errors = FALSE; | |
$output->writeln('<info>Checking the Drupal VM dependencies...</info>'); | |
// Check for virtualbox version 4.3.x. | |
$output->writeln('<info>Checking for virtualbox</info>'); | |
$result = strtolower($this->customCommand('VBoxManage', '-v', array())); | |
if ($result == '-bash: vboxmanage: command not found') { | |
$output->writeln('<error>Unmet dependency, please install virtualbox 4.3.x</error>'); | |
return; | |
} | |
else { | |
$parsed_version = explode(".", $result); | |
// Check major and minor version. | |
if ($parsed_version[0] != '4' and $parsed_version[1] != '3') { | |
$output->writeln('<comment>Unmet dependency, please upgrade virtualbox to version 4.3.x</comment>'); | |
$has_errors = TRUE; | |
} | |
else { | |
$output->writeln('<info>Virtualbox version is currently supported.</info>'); | |
} | |
} | |
// Check for vagrant 1.7.2 or higher. | |
$output->writeln('<info>Checking for vagrant</info>'); | |
$result = strtolower($this->customCommand('vagrant', '-v')); | |
if ($result == '-bash: vagrant: command not found') { | |
$output->writeln('<error>Unmet dependency, please install vagrant 1.7.2 or higher</error>'); | |
$has_errors = TRUE; | |
} | |
else { | |
$parsed_version = explode(' ', $result); | |
$parsed_version = explode(".", $parsed_version[1]); | |
// Check major and minor version. | |
if ($parsed_version[0] != '1' and $parsed_version[1] != '7' and intval($parsed_version[2]) > 2) { | |
$output->writeln('<error>Unmet dependency, please upgrade vagrant to version 1.7.2 or higher</error>'); | |
$has_errors = TRUE; | |
} | |
else { | |
$output->writeln('<comment>Vagrant version is currently supported.</comment>'); | |
} | |
} | |
// Check for ansible version 1.9.2 or higher. | |
$result = strtolower($this->customCommand('ansible', '--version')); | |
if ($result == '-bash: ansible: command not found') { | |
$output->writeln('<error>Unmet dependency, please install ansible 1.9.2 or higher. To install, run `sudo pip install ansible`.</error>'); | |
$has_errors = TRUE; | |
} | |
else { | |
$parsed_version = explode(' ', $result); | |
$parsed_version = explode(".", $parsed_version[1]); | |
// Check major and minor version. | |
if ($parsed_version[0] != '1' and $parsed_version[1] != '9' and intval($parsed_version[2]) > 2) { | |
$output->writeln('<error>Unmet dependency, please install ansible 1.9.2 or higher. To upgrade, run `sudo pip install ansible -U`.</error>'); | |
$has_errors = TRUE; | |
} | |
else { | |
$output->writeln('<comment>Ansible version is currently supported.</comment>'); | |
} | |
} | |
// Check for duplicate machine names and duplicate IP within VirtualBox. | |
$output->writeln('<info>Checking for duplicant virtualbox host names and IPs</info>'); | |
$result = $this->executeProcess('vboxmanage list vms', FALSE); | |
$existing_hosts = explode("\n", strtolower($result)); | |
$local_url = parse_url($this->config['project']['local_url']); | |
foreach ($existing_hosts as $existing_host) { | |
if ($existing_host) { | |
$host_name = explode(" ", $existing_host); | |
$host_name = str_replace('"', '', $host_name[0]); | |
// Check host. | |
if (strtolower($host_name) == strtolower($local_url['host'])) { | |
$output->writeln('<error>You already have a virtual machine host with the name ' . $host_name . '.</error>'); | |
$output->writeln('<comment>You will not be able to run these virtual machines concurrently. Please update the local_url configuration to use something unique.</comment>'); | |
$has_errors = TRUE; | |
} | |
// Load the IP(s) that are associated to the existing VMs | |
$result = $this->executeProcess('vboxmanage guestproperty enumerate ' . $host_name, FALSE); | |
if ($result) { | |
$host_ips = explode("\n", strtolower($result)); | |
foreach ($host_ips as $host_ip) { | |
//$output->writeln('<comment>Checking \'' . $host_ip . '\'</comment>'); | |
if ($host_ip and strpos($host_ip, '/v4/ip')) { | |
// Key/values are comma-separated. | |
$ip_address = explode(", ", $host_ip); | |
// The IP address value is stored in the second value in the form of 'value: xxx.yyy.zzz.qqq'. | |
// Split the key from the value. | |
$ip_address = explode(": ", $ip_address[1]); | |
// Grab the value itself. | |
$ip_address = $ip_address[1]; | |
// Check it against the specified IP. | |
if ($ip_address == $this->config['vm']['vagrant_ip']) { | |
$output->writeln('<error>The ' . $host_name . ' virtual machine host already has the IP ' . $ip_address . '.</error>'); | |
$output->writeln('<comment>You will not be able to run these virtual machines concurrently. Please update the vm > vagrant_ip configuration to use something unique.</comment>'); | |
$has_errors = TRUE; | |
} | |
} | |
} | |
} | |
} | |
} | |
// Print out message to the user to review the output generated above. | |
if ($has_errors) { | |
$output->writeln('<error>Errors were generated during the installation process. Please review the errors and manually bootstrap the VM.</error>'); | |
$output->writeln("<info>To set up the Drupal VM, follow the Quick Start Guide at http://www.drupalvm.com</info>"); | |
} | |
// Return whether or not there were issues found that would impede the VM. | |
return $has_errors; | |
} | |
/** | |
* Load the VM on the host machine. | |
* | |
* @param InputInterface $input | |
* @param OutputInterface $output | |
*/ | |
protected function bootstrapVm(InputInterface $input, OutputInterface $output) { | |
if (!empty($this->config['vm']['bootstrap']) and $this->config['vm']['bootstrap']) { | |
// Check VM dependencies. | |
$has_errors = $this->checkVmDependencies($input, $output); | |
// If all dependencies are met, load the VM. | |
if (!$has_errors) { | |
$output->writeln('<info>Bootstrapping Drupal VM...</info>'); | |
// Load ansible reqs. | |
if (!empty($this->config['vm']['rebuild_requirements']) and $this->config['vm']['rebuild_requirements']) { | |
$output->writeln('<info>Loading ansible requirements. NOTE - you will be prompted to enter your sudo password</info>'); | |
$role_file = $this->newProjectDirectory . '/box/provisioning/requirements.txt'; | |
$result = strtolower($this->customCommand('sudo', 'ansible-galaxy', array('install --force'), array('role-file' => $role_file))); | |
} | |
// Load host manager. | |
$output->writeln('<info>Loading host manager</info>'); | |
$result = strtolower($this->customCommand('vagrant', 'plugin install', array('vagrant-hostsupdater'))); | |
// Run Vagrant up from VM dir. | |
$output->writeln('<info>Bootstrapping VM</info>'); | |
$result = strtolower($this->customCommand('(cd ' . $this->newProjectDirectory . '/box && vagrant up )', '')); | |
} | |
} | |
/** | |
* @param string $command | |
* The binary to call. E.g., 'git'. | |
* @param array $arguments | |
* An array of arguments. E.g., 'pull origin/master'. | |
* @param array $options | |
* An array of options in one of the following formats: | |
* array('bare' => NULL, 'tags' => 'smoke') will translate into | |
* `--bare --tags=smoke` | |
* @return mixed | |
* Returns the command output. | |
* | |
* @todo Replace this with a real implementation of Symfony command class. | |
*/ | |
protected function customCommand($binary, $command, array $arguments = array(), array $options = array()) { | |
$arguments = implode(' ', $arguments); | |
$string_options = ''; | |
foreach ($options as $name => $value) { | |
if (is_null($value)) { | |
$string_options .= ' --' . $name; | |
} | |
else { | |
$string_options .= ' --' . $name . '=' . $value; | |
} | |
} | |
$command = "$binary {$command} {$string_options} {$arguments}"; | |
return $this->executeProcess($command); | |
} | |
/** | |
* Executes a command process directly. | |
* | |
* @param string $command | |
* The command to run. | |
* | |
* @return string | |
* The command output. | |
*/ | |
protected function executeProcess($command) { | |
$process = new Process($command); | |
$process->setTimeout(3600); | |
$process->run( | |
function ($type, $buffer) { | |
print $buffer; | |
} | |
); | |
if (!$process->isSuccessful()) { | |
throw new \RuntimeException($process->getErrorOutput()); | |
} | |
return $process->getOutput(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment