public function createHost($vmDetails, $provisioningVars = array())
{
// what are we doing?
$log = usingLog()->startAction('provision new VM');
// make sure we like the provided details
foreach (array('name', 'environment', 'osName', 'amiId', 'securityGroup') as $param) {
if (!isset($vmDetails->{$param})) {
throw new E5xx_ActionFailed(__METHOD__, "missing vmDetails['{$param}']");
}
}
// because EC2 is a shared resource, our VMs need namespacing
$vmDetails->ec2Name = $vmDetails->environment . '.' . $vmDetails->hostId;
// get our Ec2 client from the SDK
$client = fromAws()->getEc2Client();
// make sure the VM is stopped, if it is running
$log->addStep("stop EC2 VM '{$vmDetails->ec2Name}' if already running", function () use($vmDetails, $client) {
if (fromEc2Instance($vmDetails->hostId)->getInstanceisRunning()) {
// stop the host
usingEc2()->destroyVm($vmDetails->hostId);
}
});
// remove any existing hosts table entry
usingHostsTable()->removeHost($vmDetails->hostId);
// let's start the VM
$response = null;
try {
$log->addStep("create EC2 VM using AMI '{$vmDetails->amiId}'", function () use($client, $vmDetails, &$response) {
$response = $client->runInstances(array('ImageId' => $vmDetails->amiId, 'MinCount' => 1, 'MaxCount' => 1, 'KeyName' => $vmDetails->keyPairName, 'InstanceType' => $vmDetails->instanceType));
// we need to name this instance
if (isset($response['Instances'], $response['Instances'][0], $response['Instances'][0]['InstanceId'])) {
$client->createTags(array('Resources' => array($response['Instances'][0]['InstanceId']), 'Tags' => array(array('Key' => 'Name', 'Value' => $vmDetails->ec2Name))));
}
});
} catch (Exception $e) {
// something went wrong
$log->endAction("VM failed to provision :(");
throw new E5xx_ActionFailed(__METHOD__, $e->getMessage());
}
// we'll need this for future API calls
$instanceId = $response['Instances'][0]['InstanceId'];
try {
// now, we need to wait until this instance is running
$log->addStep("wait for EC2 VM '{$instanceId}' to finish booting", function () use($client, $vmDetails, $response, $instanceId) {
$client->waitUntilInstanceRunning(array('InstanceIds' => array($instanceId), 'waiter.interval' => 10, 'waiter.max_attempts' => 10));
// remember the instance data, to save us time in the future
$vmDetails->ec2Instance = $response['Instances'][0];
});
} catch (Exception $e) {
// something went wrong
$log->endAction("VM failed to start :(");
throw new E5xx_ActionFailed(__METHOD__, $e->getMessage());
}
// yes it did!!
//
// remember this vm, now that it is running
usingHostsTable()->addHost($vmDetails->hostId, $vmDetails);
// now, we need its IP address
$ipAddress = $this->determineIpAddress($vmDetails);
// store the IP address for future use
$vmDetails->ipAddress = $ipAddress;
// mark the box as provisioned
// we will use this in stopBox() to avoid destroying VMs that failed
// to provision
$vmDetails->provisioned = true;
// all done
$log->endAction("VM successfully started; IP address is {$ipAddress}");
}