mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-22 23:02:42 +01:00
Drydock blueprint for preallocated remote hosts
Summary: This adds a Drydock blueprint for preallocated, remote hosts. This will be used by the Harbormaster interface to allow users to specify remote hosts that builds can be run on. This adds a `canAllocateResource` method to Drydock blueprints; it is used to detect whether a blueprint can allocate a resource for the given type and attributes. Test Plan: Ran: ``` bin/drydock lease --type host --attributes remote=true,preallocated=true,host=192.168.56.101,port=22,user=james,keyfile=,path=C:\\Build\\,platform=windows ``` and saw the "C:\Build\<id>" folder appear on the remote Windows machine. Viewed the lease and resource in Drydock as well. Reviewers: epriestley, #blessed_reviewers Reviewed By: epriestley CC: Korvin, epriestley, aran, jamesr Maniphest Tasks: T4111 Differential Revision: https://secure.phabricator.com/D7593
This commit is contained in:
parent
97937556ca
commit
7c3cb5948c
6 changed files with 198 additions and 10 deletions
|
@ -19,6 +19,7 @@ $workflows = array(
|
||||||
new DrydockManagementLeaseWorkflow(),
|
new DrydockManagementLeaseWorkflow(),
|
||||||
new DrydockManagementCloseWorkflow(),
|
new DrydockManagementCloseWorkflow(),
|
||||||
new DrydockManagementReleaseWorkflow(),
|
new DrydockManagementReleaseWorkflow(),
|
||||||
|
new DrydockManagementCreateResourceWorkflow(),
|
||||||
new PhutilHelpArgumentWorkflow(),
|
new PhutilHelpArgumentWorkflow(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -641,10 +641,12 @@ phutil_register_library_map(array(
|
||||||
'DrydockLogController' => 'applications/drydock/controller/DrydockLogController.php',
|
'DrydockLogController' => 'applications/drydock/controller/DrydockLogController.php',
|
||||||
'DrydockLogQuery' => 'applications/drydock/query/DrydockLogQuery.php',
|
'DrydockLogQuery' => 'applications/drydock/query/DrydockLogQuery.php',
|
||||||
'DrydockManagementCloseWorkflow' => 'applications/drydock/management/DrydockManagementCloseWorkflow.php',
|
'DrydockManagementCloseWorkflow' => 'applications/drydock/management/DrydockManagementCloseWorkflow.php',
|
||||||
|
'DrydockManagementCreateResourceWorkflow' => 'applications/drydock/management/DrydockManagementCreateResourceWorkflow.php',
|
||||||
'DrydockManagementLeaseWorkflow' => 'applications/drydock/management/DrydockManagementLeaseWorkflow.php',
|
'DrydockManagementLeaseWorkflow' => 'applications/drydock/management/DrydockManagementLeaseWorkflow.php',
|
||||||
'DrydockManagementReleaseWorkflow' => 'applications/drydock/management/DrydockManagementReleaseWorkflow.php',
|
'DrydockManagementReleaseWorkflow' => 'applications/drydock/management/DrydockManagementReleaseWorkflow.php',
|
||||||
'DrydockManagementWaitForLeaseWorkflow' => 'applications/drydock/management/DrydockManagementWaitForLeaseWorkflow.php',
|
'DrydockManagementWaitForLeaseWorkflow' => 'applications/drydock/management/DrydockManagementWaitForLeaseWorkflow.php',
|
||||||
'DrydockManagementWorkflow' => 'applications/drydock/management/DrydockManagementWorkflow.php',
|
'DrydockManagementWorkflow' => 'applications/drydock/management/DrydockManagementWorkflow.php',
|
||||||
|
'DrydockPreallocatedHostBlueprint' => 'applications/drydock/blueprint/DrydockPreallocatedHostBlueprint.php',
|
||||||
'DrydockResource' => 'applications/drydock/storage/DrydockResource.php',
|
'DrydockResource' => 'applications/drydock/storage/DrydockResource.php',
|
||||||
'DrydockResourceCloseController' => 'applications/drydock/controller/DrydockResourceCloseController.php',
|
'DrydockResourceCloseController' => 'applications/drydock/controller/DrydockResourceCloseController.php',
|
||||||
'DrydockResourceListController' => 'applications/drydock/controller/DrydockResourceListController.php',
|
'DrydockResourceListController' => 'applications/drydock/controller/DrydockResourceListController.php',
|
||||||
|
@ -2950,10 +2952,12 @@ phutil_register_library_map(array(
|
||||||
'DrydockLogController' => 'DrydockController',
|
'DrydockLogController' => 'DrydockController',
|
||||||
'DrydockLogQuery' => 'PhabricatorOffsetPagedQuery',
|
'DrydockLogQuery' => 'PhabricatorOffsetPagedQuery',
|
||||||
'DrydockManagementCloseWorkflow' => 'DrydockManagementWorkflow',
|
'DrydockManagementCloseWorkflow' => 'DrydockManagementWorkflow',
|
||||||
|
'DrydockManagementCreateResourceWorkflow' => 'DrydockManagementWorkflow',
|
||||||
'DrydockManagementLeaseWorkflow' => 'DrydockManagementWorkflow',
|
'DrydockManagementLeaseWorkflow' => 'DrydockManagementWorkflow',
|
||||||
'DrydockManagementReleaseWorkflow' => 'DrydockManagementWorkflow',
|
'DrydockManagementReleaseWorkflow' => 'DrydockManagementWorkflow',
|
||||||
'DrydockManagementWaitForLeaseWorkflow' => 'DrydockManagementWorkflow',
|
'DrydockManagementWaitForLeaseWorkflow' => 'DrydockManagementWorkflow',
|
||||||
'DrydockManagementWorkflow' => 'PhutilArgumentWorkflow',
|
'DrydockManagementWorkflow' => 'PhutilArgumentWorkflow',
|
||||||
|
'DrydockPreallocatedHostBlueprint' => 'DrydockBlueprint',
|
||||||
'DrydockResource' => 'DrydockDAO',
|
'DrydockResource' => 'DrydockDAO',
|
||||||
'DrydockResourceCloseController' => 'DrydockController',
|
'DrydockResourceCloseController' => 'DrydockController',
|
||||||
'DrydockResourceListController' => 'DrydockController',
|
'DrydockResourceListController' => 'DrydockController',
|
||||||
|
|
|
@ -3,8 +3,7 @@
|
||||||
final class DrydockLocalHostBlueprint extends DrydockBlueprint {
|
final class DrydockLocalHostBlueprint extends DrydockBlueprint {
|
||||||
|
|
||||||
public function isEnabled() {
|
public function isEnabled() {
|
||||||
// TODO: Figure this out.
|
return false;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function canAllocateMoreResources(array $pool) {
|
public function canAllocateMoreResources(array $pool) {
|
||||||
|
@ -34,6 +33,8 @@ final class DrydockLocalHostBlueprint extends DrydockBlueprint {
|
||||||
$resource = $this->newResourceTemplate('Host (localhost)');
|
$resource = $this->newResourceTemplate('Host (localhost)');
|
||||||
$resource->setStatus(DrydockResourceStatus::STATUS_OPEN);
|
$resource->setStatus(DrydockResourceStatus::STATUS_OPEN);
|
||||||
$resource->setAttribute('path', $path);
|
$resource->setAttribute('path', $path);
|
||||||
|
$resource->setAttribute('remote', "false");
|
||||||
|
$resource->setAttribute('preallocated', "false");
|
||||||
$resource->save();
|
$resource->save();
|
||||||
|
|
||||||
return $resource;
|
return $resource;
|
||||||
|
@ -42,7 +43,7 @@ final class DrydockLocalHostBlueprint extends DrydockBlueprint {
|
||||||
protected function canAllocateLease(
|
protected function canAllocateLease(
|
||||||
DrydockResource $resource,
|
DrydockResource $resource,
|
||||||
DrydockLease $lease) {
|
DrydockLease $lease) {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function shouldAllocateLease(
|
protected function shouldAllocateLease(
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class DrydockPreallocatedHostBlueprint extends DrydockBlueprint {
|
||||||
|
|
||||||
|
public function isEnabled() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function canAllocateMoreResources(array $pool) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function executeAllocateResource(DrydockLease $lease) {
|
||||||
|
throw new Exception("Preallocated hosts can't be dynamically allocated.");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function canAllocateLease(
|
||||||
|
DrydockResource $resource,
|
||||||
|
DrydockLease $lease) {
|
||||||
|
return
|
||||||
|
$lease->getAttribute('platform') === $resource->getAttribute('platform');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function shouldAllocateLease(
|
||||||
|
DrydockResource $resource,
|
||||||
|
DrydockLease $lease,
|
||||||
|
array $other_leases) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function executeAcquireLease(
|
||||||
|
DrydockResource $resource,
|
||||||
|
DrydockLease $lease) {
|
||||||
|
|
||||||
|
// Similar to DrydockLocalHostBlueprint, we create a folder
|
||||||
|
// on the remote host that the lease can use.
|
||||||
|
|
||||||
|
$lease_id = $lease->getID();
|
||||||
|
|
||||||
|
// Can't use DIRECTORY_SEPERATOR here because that is relevant to
|
||||||
|
// the platform we're currently running on, not the platform we are
|
||||||
|
// remoting to.
|
||||||
|
$separator = '/';
|
||||||
|
if ($lease->getAttribute('platform') === 'windows') {
|
||||||
|
$separator = '\\';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up the directory path a little.
|
||||||
|
$base_path = rtrim($resource->getAttribute('path'), '/');
|
||||||
|
$base_path = rtrim($base_path, '\\');
|
||||||
|
$full_path = $base_path.$separator.$lease_id;
|
||||||
|
|
||||||
|
$cmd = $lease->getInterface('command');
|
||||||
|
|
||||||
|
if ($lease->getAttribute('platform') !== 'windows') {
|
||||||
|
$cmd->execx('mkdir %s', $full_path);
|
||||||
|
} else {
|
||||||
|
// Windows is terrible. The mkdir command doesn't even support putting
|
||||||
|
// the path in quotes. IN QUOTES. ARGUHRGHUGHHGG!! Do some terribly
|
||||||
|
// inaccurate sanity checking since we can't safely escape the path.
|
||||||
|
if (preg_match('/^[A-Z]\\:\\\\[a-zA-Z0-9\\\\\\ ]/', $full_path) === 0) {
|
||||||
|
throw new Exception(
|
||||||
|
'Unsafe path detected for Windows platform: "'.$full_path.'".');
|
||||||
|
}
|
||||||
|
$cmd->execx('mkdir %C', $full_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
$lease->setAttribute('path', $full_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getType() {
|
||||||
|
return 'host';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getInterface(
|
||||||
|
DrydockResource $resource,
|
||||||
|
DrydockLease $lease,
|
||||||
|
$type) {
|
||||||
|
|
||||||
|
switch ($type) {
|
||||||
|
case 'command':
|
||||||
|
return id(new DrydockSSHCommandInterface())
|
||||||
|
->setConfiguration(array(
|
||||||
|
'host' => $resource->getAttribute('host'),
|
||||||
|
'port' => $resource->getAttribute('port'),
|
||||||
|
'user' => $resource->getAttribute('user'),
|
||||||
|
'ssh-keyfile' => $resource->getAttribute('ssh-keyfile'),
|
||||||
|
'platform' => $resource->getAttribute('platform')));
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception("No interface of type '{$type}'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -4,19 +4,41 @@ final class DrydockSSHCommandInterface extends DrydockCommandInterface {
|
||||||
|
|
||||||
public function getExecFuture($command) {
|
public function getExecFuture($command) {
|
||||||
$argv = func_get_args();
|
$argv = func_get_args();
|
||||||
|
|
||||||
|
// This assumes there's a UNIX shell living at the other
|
||||||
|
// end of the connection, which isn't the case for Windows machines.
|
||||||
|
if ($this->getConfig('platform') !== 'windows') {
|
||||||
$argv = $this->applyWorkingDirectoryToArgv($argv);
|
$argv = $this->applyWorkingDirectoryToArgv($argv);
|
||||||
|
}
|
||||||
|
|
||||||
$full_command = call_user_func_array('csprintf', $argv);
|
$full_command = call_user_func_array('csprintf', $argv);
|
||||||
|
|
||||||
|
if ($this->getConfig('platform') === 'windows') {
|
||||||
|
// On Windows platforms we need to execute cmd.exe explicitly since
|
||||||
|
// most commands are not really executables.
|
||||||
|
$full_command = 'C:\\Windows\\system32\\cmd.exe /C '.$full_command;
|
||||||
|
}
|
||||||
|
|
||||||
// NOTE: The "-t -t" is for psuedo-tty allocation so we can "sudo" on some
|
// NOTE: The "-t -t" is for psuedo-tty allocation so we can "sudo" on some
|
||||||
// systems, but maybe more trouble than it's worth?
|
// systems, but maybe more trouble than it's worth?
|
||||||
|
|
||||||
|
$keyfile = $this->getConfig('ssh-keyfile');
|
||||||
|
if (!empty($keyfile)) {
|
||||||
return new ExecFuture(
|
return new ExecFuture(
|
||||||
'ssh -t -t -o StrictHostKeyChecking=no -i %s %s@%s -- %s',
|
'ssh -t -t -o StrictHostKeyChecking=no -p %s -i %s %s@%s -- %s',
|
||||||
|
$this->getConfig('port'),
|
||||||
$this->getConfig('ssh-keyfile'),
|
$this->getConfig('ssh-keyfile'),
|
||||||
$this->getConfig('user'),
|
$this->getConfig('user'),
|
||||||
$this->getConfig('host'),
|
$this->getConfig('host'),
|
||||||
$full_command);
|
$full_command);
|
||||||
|
} else {
|
||||||
|
return new ExecFuture(
|
||||||
|
'ssh -t -t -o StrictHostKeyChecking=no -p %s %s@%s -- %s',
|
||||||
|
$this->getConfig('port'),
|
||||||
|
$this->getConfig('user'),
|
||||||
|
$this->getConfig('host'),
|
||||||
|
$full_command);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class DrydockManagementCreateResourceWorkflow
|
||||||
|
extends DrydockManagementWorkflow {
|
||||||
|
|
||||||
|
public function didConstruct() {
|
||||||
|
$this
|
||||||
|
->setName('create-resource')
|
||||||
|
->setSynopsis('Create a resource manually.')
|
||||||
|
->setArguments(
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'name' => 'name',
|
||||||
|
'param' => 'resource_name',
|
||||||
|
'help' => 'Resource name.',
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'name' => 'blueprint',
|
||||||
|
'param' => 'blueprint_type',
|
||||||
|
'help' => 'Blueprint type.',
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'name' => 'attributes',
|
||||||
|
'param' => 'name=value,...',
|
||||||
|
'help' => 'Resource attributes.',
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute(PhutilArgumentParser $args) {
|
||||||
|
$console = PhutilConsole::getConsole();
|
||||||
|
|
||||||
|
$resource_name = $args->getArg('name');
|
||||||
|
if (!$resource_name) {
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
"Specify a resource name with `--name`.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$blueprint_type = $args->getArg('blueprint');
|
||||||
|
if (!$blueprint_type) {
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
"Specify a blueprint type with `--blueprint`.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$attributes = $args->getArg('attributes');
|
||||||
|
if ($attributes) {
|
||||||
|
$options = new PhutilSimpleOptions();
|
||||||
|
$options->setCaseSensitive(true);
|
||||||
|
$attributes = $options->parse($attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
$resource = new DrydockResource();
|
||||||
|
$resource->setBlueprintClass($blueprint_type);
|
||||||
|
$resource->setType(id(new $blueprint_type())->getType());
|
||||||
|
$resource->setName($resource_name);
|
||||||
|
$resource->setStatus(DrydockResourceStatus::STATUS_OPEN);
|
||||||
|
if ($attributes) {
|
||||||
|
$resource->setAttributes($attributes);
|
||||||
|
}
|
||||||
|
$resource->save();
|
||||||
|
|
||||||
|
$console->writeOut("Created Resource %s\n", $resource->getID());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue