Skip to content

Instantly share code, notes, and snippets.

@grasmash
Last active August 19, 2016 21:08
Show Gist options
  • Save grasmash/069c744b20cd01388e03 to your computer and use it in GitHub Desktop.
Save grasmash/069c744b20cd01388e03 to your computer and use it in GitHub Desktop.
Symfony-based VM tasks
<?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