@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

authored by

James Rhodes and committed by
epriestley
7c3cb594 97937556

+198 -10
+1
scripts/drydock/drydock_control.php
··· 19 19 new DrydockManagementLeaseWorkflow(), 20 20 new DrydockManagementCloseWorkflow(), 21 21 new DrydockManagementReleaseWorkflow(), 22 + new DrydockManagementCreateResourceWorkflow(), 22 23 new PhutilHelpArgumentWorkflow(), 23 24 ); 24 25
+4
src/__phutil_library_map__.php
··· 641 641 'DrydockLogController' => 'applications/drydock/controller/DrydockLogController.php', 642 642 'DrydockLogQuery' => 'applications/drydock/query/DrydockLogQuery.php', 643 643 'DrydockManagementCloseWorkflow' => 'applications/drydock/management/DrydockManagementCloseWorkflow.php', 644 + 'DrydockManagementCreateResourceWorkflow' => 'applications/drydock/management/DrydockManagementCreateResourceWorkflow.php', 644 645 'DrydockManagementLeaseWorkflow' => 'applications/drydock/management/DrydockManagementLeaseWorkflow.php', 645 646 'DrydockManagementReleaseWorkflow' => 'applications/drydock/management/DrydockManagementReleaseWorkflow.php', 646 647 'DrydockManagementWaitForLeaseWorkflow' => 'applications/drydock/management/DrydockManagementWaitForLeaseWorkflow.php', 647 648 'DrydockManagementWorkflow' => 'applications/drydock/management/DrydockManagementWorkflow.php', 649 + 'DrydockPreallocatedHostBlueprint' => 'applications/drydock/blueprint/DrydockPreallocatedHostBlueprint.php', 648 650 'DrydockResource' => 'applications/drydock/storage/DrydockResource.php', 649 651 'DrydockResourceCloseController' => 'applications/drydock/controller/DrydockResourceCloseController.php', 650 652 'DrydockResourceListController' => 'applications/drydock/controller/DrydockResourceListController.php', ··· 2950 2952 'DrydockLogController' => 'DrydockController', 2951 2953 'DrydockLogQuery' => 'PhabricatorOffsetPagedQuery', 2952 2954 'DrydockManagementCloseWorkflow' => 'DrydockManagementWorkflow', 2955 + 'DrydockManagementCreateResourceWorkflow' => 'DrydockManagementWorkflow', 2953 2956 'DrydockManagementLeaseWorkflow' => 'DrydockManagementWorkflow', 2954 2957 'DrydockManagementReleaseWorkflow' => 'DrydockManagementWorkflow', 2955 2958 'DrydockManagementWaitForLeaseWorkflow' => 'DrydockManagementWorkflow', 2956 2959 'DrydockManagementWorkflow' => 'PhutilArgumentWorkflow', 2960 + 'DrydockPreallocatedHostBlueprint' => 'DrydockBlueprint', 2957 2961 'DrydockResource' => 'DrydockDAO', 2958 2962 'DrydockResourceCloseController' => 'DrydockController', 2959 2963 'DrydockResourceListController' => 'DrydockController',
+4 -3
src/applications/drydock/blueprint/DrydockLocalHostBlueprint.php
··· 3 3 final class DrydockLocalHostBlueprint extends DrydockBlueprint { 4 4 5 5 public function isEnabled() { 6 - // TODO: Figure this out. 7 - return true; 6 + return false; 8 7 } 9 8 10 9 public function canAllocateMoreResources(array $pool) { ··· 34 33 $resource = $this->newResourceTemplate('Host (localhost)'); 35 34 $resource->setStatus(DrydockResourceStatus::STATUS_OPEN); 36 35 $resource->setAttribute('path', $path); 36 + $resource->setAttribute('remote', "false"); 37 + $resource->setAttribute('preallocated', "false"); 37 38 $resource->save(); 38 39 39 40 return $resource; ··· 42 43 protected function canAllocateLease( 43 44 DrydockResource $resource, 44 45 DrydockLease $lease) { 45 - return true; 46 + return false; 46 47 } 47 48 48 49 protected function shouldAllocateLease(
+94
src/applications/drydock/blueprint/DrydockPreallocatedHostBlueprint.php
··· 1 + <?php 2 + 3 + final class DrydockPreallocatedHostBlueprint extends DrydockBlueprint { 4 + 5 + public function isEnabled() { 6 + return true; 7 + } 8 + 9 + public function canAllocateMoreResources(array $pool) { 10 + return false; 11 + } 12 + 13 + protected function executeAllocateResource(DrydockLease $lease) { 14 + throw new Exception("Preallocated hosts can't be dynamically allocated."); 15 + } 16 + 17 + protected function canAllocateLease( 18 + DrydockResource $resource, 19 + DrydockLease $lease) { 20 + return 21 + $lease->getAttribute('platform') === $resource->getAttribute('platform'); 22 + } 23 + 24 + protected function shouldAllocateLease( 25 + DrydockResource $resource, 26 + DrydockLease $lease, 27 + array $other_leases) { 28 + return true; 29 + } 30 + 31 + protected function executeAcquireLease( 32 + DrydockResource $resource, 33 + DrydockLease $lease) { 34 + 35 + // Similar to DrydockLocalHostBlueprint, we create a folder 36 + // on the remote host that the lease can use. 37 + 38 + $lease_id = $lease->getID(); 39 + 40 + // Can't use DIRECTORY_SEPERATOR here because that is relevant to 41 + // the platform we're currently running on, not the platform we are 42 + // remoting to. 43 + $separator = '/'; 44 + if ($lease->getAttribute('platform') === 'windows') { 45 + $separator = '\\'; 46 + } 47 + 48 + // Clean up the directory path a little. 49 + $base_path = rtrim($resource->getAttribute('path'), '/'); 50 + $base_path = rtrim($base_path, '\\'); 51 + $full_path = $base_path.$separator.$lease_id; 52 + 53 + $cmd = $lease->getInterface('command'); 54 + 55 + if ($lease->getAttribute('platform') !== 'windows') { 56 + $cmd->execx('mkdir %s', $full_path); 57 + } else { 58 + // Windows is terrible. The mkdir command doesn't even support putting 59 + // the path in quotes. IN QUOTES. ARGUHRGHUGHHGG!! Do some terribly 60 + // inaccurate sanity checking since we can't safely escape the path. 61 + if (preg_match('/^[A-Z]\\:\\\\[a-zA-Z0-9\\\\\\ ]/', $full_path) === 0) { 62 + throw new Exception( 63 + 'Unsafe path detected for Windows platform: "'.$full_path.'".'); 64 + } 65 + $cmd->execx('mkdir %C', $full_path); 66 + } 67 + 68 + $lease->setAttribute('path', $full_path); 69 + } 70 + 71 + public function getType() { 72 + return 'host'; 73 + } 74 + 75 + public function getInterface( 76 + DrydockResource $resource, 77 + DrydockLease $lease, 78 + $type) { 79 + 80 + switch ($type) { 81 + case 'command': 82 + return id(new DrydockSSHCommandInterface()) 83 + ->setConfiguration(array( 84 + 'host' => $resource->getAttribute('host'), 85 + 'port' => $resource->getAttribute('port'), 86 + 'user' => $resource->getAttribute('user'), 87 + 'ssh-keyfile' => $resource->getAttribute('ssh-keyfile'), 88 + 'platform' => $resource->getAttribute('platform'))); 89 + } 90 + 91 + throw new Exception("No interface of type '{$type}'."); 92 + } 93 + 94 + }
+29 -7
src/applications/drydock/interface/command/DrydockSSHCommandInterface.php
··· 4 4 5 5 public function getExecFuture($command) { 6 6 $argv = func_get_args(); 7 - $argv = $this->applyWorkingDirectoryToArgv($argv); 7 + 8 + // This assumes there's a UNIX shell living at the other 9 + // end of the connection, which isn't the case for Windows machines. 10 + if ($this->getConfig('platform') !== 'windows') { 11 + $argv = $this->applyWorkingDirectoryToArgv($argv); 12 + } 8 13 9 14 $full_command = call_user_func_array('csprintf', $argv); 10 15 16 + if ($this->getConfig('platform') === 'windows') { 17 + // On Windows platforms we need to execute cmd.exe explicitly since 18 + // most commands are not really executables. 19 + $full_command = 'C:\\Windows\\system32\\cmd.exe /C '.$full_command; 20 + } 21 + 11 22 // NOTE: The "-t -t" is for psuedo-tty allocation so we can "sudo" on some 12 23 // systems, but maybe more trouble than it's worth? 13 24 14 - return new ExecFuture( 15 - 'ssh -t -t -o StrictHostKeyChecking=no -i %s %s@%s -- %s', 16 - $this->getConfig('ssh-keyfile'), 17 - $this->getConfig('user'), 18 - $this->getConfig('host'), 19 - $full_command); 25 + $keyfile = $this->getConfig('ssh-keyfile'); 26 + if (!empty($keyfile)) { 27 + return new ExecFuture( 28 + 'ssh -t -t -o StrictHostKeyChecking=no -p %s -i %s %s@%s -- %s', 29 + $this->getConfig('port'), 30 + $this->getConfig('ssh-keyfile'), 31 + $this->getConfig('user'), 32 + $this->getConfig('host'), 33 + $full_command); 34 + } else { 35 + return new ExecFuture( 36 + 'ssh -t -t -o StrictHostKeyChecking=no -p %s %s@%s -- %s', 37 + $this->getConfig('port'), 38 + $this->getConfig('user'), 39 + $this->getConfig('host'), 40 + $full_command); 41 + } 20 42 } 21 43 22 44 }
+66
src/applications/drydock/management/DrydockManagementCreateResourceWorkflow.php
··· 1 + <?php 2 + 3 + final class DrydockManagementCreateResourceWorkflow 4 + extends DrydockManagementWorkflow { 5 + 6 + public function didConstruct() { 7 + $this 8 + ->setName('create-resource') 9 + ->setSynopsis('Create a resource manually.') 10 + ->setArguments( 11 + array( 12 + array( 13 + 'name' => 'name', 14 + 'param' => 'resource_name', 15 + 'help' => 'Resource name.', 16 + ), 17 + array( 18 + 'name' => 'blueprint', 19 + 'param' => 'blueprint_type', 20 + 'help' => 'Blueprint type.', 21 + ), 22 + array( 23 + 'name' => 'attributes', 24 + 'param' => 'name=value,...', 25 + 'help' => 'Resource attributes.', 26 + ), 27 + )); 28 + } 29 + 30 + public function execute(PhutilArgumentParser $args) { 31 + $console = PhutilConsole::getConsole(); 32 + 33 + $resource_name = $args->getArg('name'); 34 + if (!$resource_name) { 35 + throw new PhutilArgumentUsageException( 36 + "Specify a resource name with `--name`."); 37 + } 38 + 39 + $blueprint_type = $args->getArg('blueprint'); 40 + if (!$blueprint_type) { 41 + throw new PhutilArgumentUsageException( 42 + "Specify a blueprint type with `--blueprint`."); 43 + } 44 + 45 + $attributes = $args->getArg('attributes'); 46 + if ($attributes) { 47 + $options = new PhutilSimpleOptions(); 48 + $options->setCaseSensitive(true); 49 + $attributes = $options->parse($attributes); 50 + } 51 + 52 + $resource = new DrydockResource(); 53 + $resource->setBlueprintClass($blueprint_type); 54 + $resource->setType(id(new $blueprint_type())->getType()); 55 + $resource->setName($resource_name); 56 + $resource->setStatus(DrydockResourceStatus::STATUS_OPEN); 57 + if ($attributes) { 58 + $resource->setAttributes($attributes); 59 + } 60 + $resource->save(); 61 + 62 + $console->writeOut("Created Resource %s\n", $resource->getID()); 63 + return 0; 64 + } 65 + 66 + }