Page MenuHomePhabricator

D14126.diff
No OneTemporary

D14126.diff

diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -838,7 +838,6 @@
'DrydockLeaseSearchEngine' => 'applications/drydock/query/DrydockLeaseSearchEngine.php',
'DrydockLeaseStatus' => 'applications/drydock/constants/DrydockLeaseStatus.php',
'DrydockLeaseViewController' => 'applications/drydock/controller/DrydockLeaseViewController.php',
- 'DrydockLocalCommandInterface' => 'applications/drydock/interface/command/DrydockLocalCommandInterface.php',
'DrydockLog' => 'applications/drydock/storage/DrydockLog.php',
'DrydockLogController' => 'applications/drydock/controller/DrydockLogController.php',
'DrydockLogListController' => 'applications/drydock/controller/DrydockLogListController.php',
@@ -846,6 +845,7 @@
'DrydockLogQuery' => 'applications/drydock/query/DrydockLogQuery.php',
'DrydockLogSearchEngine' => 'applications/drydock/query/DrydockLogSearchEngine.php',
'DrydockManagementCloseWorkflow' => 'applications/drydock/management/DrydockManagementCloseWorkflow.php',
+ 'DrydockManagementCommandWorkflow' => 'applications/drydock/management/DrydockManagementCommandWorkflow.php',
'DrydockManagementLeaseWorkflow' => 'applications/drydock/management/DrydockManagementLeaseWorkflow.php',
'DrydockManagementReleaseWorkflow' => 'applications/drydock/management/DrydockManagementReleaseWorkflow.php',
'DrydockManagementWorkflow' => 'applications/drydock/management/DrydockManagementWorkflow.php',
@@ -4555,7 +4555,6 @@
'DrydockLeaseSearchEngine' => 'PhabricatorApplicationSearchEngine',
'DrydockLeaseStatus' => 'DrydockConstants',
'DrydockLeaseViewController' => 'DrydockLeaseController',
- 'DrydockLocalCommandInterface' => 'DrydockCommandInterface',
'DrydockLog' => array(
'DrydockDAO',
'PhabricatorPolicyInterface',
@@ -4566,6 +4565,7 @@
'DrydockLogQuery' => 'DrydockQuery',
'DrydockLogSearchEngine' => 'PhabricatorApplicationSearchEngine',
'DrydockManagementCloseWorkflow' => 'DrydockManagementWorkflow',
+ 'DrydockManagementCommandWorkflow' => 'DrydockManagementWorkflow',
'DrydockManagementLeaseWorkflow' => 'DrydockManagementWorkflow',
'DrydockManagementReleaseWorkflow' => 'DrydockManagementWorkflow',
'DrydockManagementWorkflow' => 'PhabricatorManagementWorkflow',
diff --git a/src/applications/drydock/blueprint/DrydockAlmanacServiceHostBlueprintImplementation.php b/src/applications/drydock/blueprint/DrydockAlmanacServiceHostBlueprintImplementation.php
--- a/src/applications/drydock/blueprint/DrydockAlmanacServiceHostBlueprintImplementation.php
+++ b/src/applications/drydock/blueprint/DrydockAlmanacServiceHostBlueprintImplementation.php
@@ -119,11 +119,37 @@
}
public function getInterface(
+ DrydockBlueprint $blueprint,
DrydockResource $resource,
DrydockLease $lease,
$type) {
- // TODO: Actually do stuff here, this needs work and currently makes this
- // entire exercise pointless.
+
+ $viewer = PhabricatorUser::getOmnipotentUser();
+
+ switch ($type) {
+ case DrydockCommandInterface::INTERFACE_TYPE:
+ $credential_phid = $blueprint->getFieldValue('credentialPHID');
+ $binding_phid = $resource->getAttribute('almanacBindingPHID');
+
+ $binding = id(new AlmanacBindingQuery())
+ ->setViewer($viewer)
+ ->withPHIDs(array($binding_phid))
+ ->executeOne();
+ if (!$binding) {
+ // TODO: This is probably a permanent failure, destroy this resource?
+ throw new Exception(
+ pht(
+ 'Unable to load binding "%s" to create command interface.',
+ $binding_phid));
+ }
+
+ $interface = $binding->getInterface();
+
+ return id(new DrydockSSHCommandInterface())
+ ->setConfig('credentialPHID', $credential_phid)
+ ->setConfig('host', $interface->getAddress())
+ ->setConfig('port', $interface->getPort());
+ }
}
public function getFieldSpecifications() {
diff --git a/src/applications/drydock/blueprint/DrydockBlueprintImplementation.php b/src/applications/drydock/blueprint/DrydockBlueprintImplementation.php
--- a/src/applications/drydock/blueprint/DrydockBlueprintImplementation.php
+++ b/src/applications/drydock/blueprint/DrydockBlueprintImplementation.php
@@ -1,61 +1,23 @@
<?php
/**
- * @task lease Lease Acquisition
- * @task resource Resource Allocation
- * @task log Logging
+ * @task lease Lease Acquisition
+ * @task resource Resource Allocation
+ * @task interface Resource Interfaces
+ * @task log Logging
*/
abstract class DrydockBlueprintImplementation extends Phobject {
private $activeResource;
private $activeLease;
- private $instance;
abstract public function getType();
- abstract public function getInterface(
- DrydockResource $resource,
- DrydockLease $lease,
- $type);
abstract public function isEnabled();
abstract public function getBlueprintName();
abstract public function getDescription();
- public function getBlueprintClass() {
- return get_class($this);
- }
-
- protected function loadLease($lease_id) {
- // TODO: Get rid of this?
- $query = id(new DrydockLeaseQuery())
- ->setViewer(PhabricatorUser::getOmnipotentUser())
- ->withIDs(array($lease_id))
- ->execute();
-
- $lease = idx($query, $lease_id);
-
- if (!$lease) {
- throw new Exception(pht("No such lease '%d'!", $lease_id));
- }
-
- return $lease;
- }
-
- protected function getInstance() {
- if (!$this->instance) {
- throw new Exception(
- pht('Attach the blueprint instance to the implementation.'));
- }
-
- return $this->instance;
- }
-
- public function attachInstance(DrydockBlueprint $instance) {
- $this->instance = $instance;
- return $this;
- }
-
public function getFieldSpecifications() {
return array();
}
@@ -105,6 +67,7 @@
DrydockResource $resource,
DrydockLease $lease);
+
final public function releaseLease(
DrydockBlueprint $blueprint,
DrydockResource $resource,
@@ -236,6 +199,16 @@
DrydockLease $lease);
+/* -( Resource Interfaces )------------------------------------------------ */
+
+
+ abstract public function getInterface(
+ DrydockBlueprint $blueprint,
+ DrydockResource $resource,
+ DrydockLease $lease,
+ $type);
+
+
/* -( Logging )------------------------------------------------------------ */
@@ -308,7 +281,7 @@
$this->log(
pht(
"Blueprint '%s': Created New Template",
- $this->getBlueprintClass()));
+ get_class($this)));
return $resource;
}
diff --git a/src/applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php b/src/applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php
--- a/src/applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php
+++ b/src/applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php
@@ -122,18 +122,11 @@
}
public function getInterface(
+ DrydockBlueprint $blueprint,
DrydockResource $resource,
DrydockLease $lease,
$type) {
-
- switch ($type) {
- case 'command':
- return $this
- ->loadLease($resource->getAttribute('lease.host'))
- ->getInterface($type);
- }
-
- throw new Exception(pht("No interface of type '%s'.", $type));
+ // TODO: This blueprint doesn't work at all.
}
}
diff --git a/src/applications/drydock/interface/DrydockInterface.php b/src/applications/drydock/interface/DrydockInterface.php
--- a/src/applications/drydock/interface/DrydockInterface.php
+++ b/src/applications/drydock/interface/DrydockInterface.php
@@ -2,12 +2,12 @@
abstract class DrydockInterface extends Phobject {
- private $config;
+ private $config = array();
abstract public function getInterfaceType();
- final public function setConfiguration(array $config) {
- $this->config = $config;
+ final public function setConfig($key, $value) {
+ $this->config[$key] = $value;
return $this;
}
diff --git a/src/applications/drydock/interface/command/DrydockCommandInterface.php b/src/applications/drydock/interface/command/DrydockCommandInterface.php
--- a/src/applications/drydock/interface/command/DrydockCommandInterface.php
+++ b/src/applications/drydock/interface/command/DrydockCommandInterface.php
@@ -2,6 +2,8 @@
abstract class DrydockCommandInterface extends DrydockInterface {
+ const INTERFACE_TYPE = 'command';
+
private $workingDirectory;
public function setWorkingDirectory($working_directory) {
@@ -14,7 +16,7 @@
}
final public function getInterfaceType() {
- return 'command';
+ return self::INTERFACE_TYPE;
}
final public function exec($command) {
@@ -38,7 +40,7 @@
protected function applyWorkingDirectoryToArgv(array $argv) {
if ($this->getWorkingDirectory() !== null) {
$cmd = $argv[0];
- $cmd = "(cd %s; {$cmd})";
+ $cmd = "(cd %s && {$cmd})";
$argv = array_merge(
array($cmd),
array($this->getWorkingDirectory()),
diff --git a/src/applications/drydock/interface/command/DrydockLocalCommandInterface.php b/src/applications/drydock/interface/command/DrydockLocalCommandInterface.php
deleted file mode 100644
--- a/src/applications/drydock/interface/command/DrydockLocalCommandInterface.php
+++ /dev/null
@@ -1,12 +0,0 @@
-<?php
-
-final class DrydockLocalCommandInterface extends DrydockCommandInterface {
-
- public function getExecFuture($command) {
- $argv = func_get_args();
- $argv = $this->applyWorkingDirectoryToArgv($argv);
-
- return newv('ExecFuture', $argv);
- }
-
-}
diff --git a/src/applications/drydock/interface/command/DrydockSSHCommandInterface.php b/src/applications/drydock/interface/command/DrydockSSHCommandInterface.php
--- a/src/applications/drydock/interface/command/DrydockSSHCommandInterface.php
+++ b/src/applications/drydock/interface/command/DrydockSSHCommandInterface.php
@@ -2,35 +2,19 @@
final class DrydockSSHCommandInterface extends DrydockCommandInterface {
- private $passphraseSSHKey;
+ private $credential;
private $connectTimeout;
- private function openCredentialsIfNotOpen() {
- if ($this->passphraseSSHKey !== null) {
- return;
- }
-
- $credential = id(new PassphraseCredentialQuery())
- ->setViewer(PhabricatorUser::getOmnipotentUser())
- ->withIDs(array($this->getConfig('credential')))
- ->needSecrets(true)
- ->executeOne();
-
- if ($credential === null) {
- throw new Exception(
- pht(
- 'There is no credential with ID %d.',
- $this->getConfig('credential')));
- }
+ private function loadCredential() {
+ if ($this->credential === null) {
+ $credential_phid = $this->getConfig('credentialPHID');
- if ($credential->getProvidesType() !==
- PassphraseSSHPrivateKeyCredentialType::PROVIDES_TYPE) {
- throw new Exception(pht('Only private key credentials are supported.'));
+ $this->credential = PassphraseSSHKey::loadFromPHID(
+ $credential_phid,
+ PhabricatorUser::getOmnipotentUser());
}
- $this->passphraseSSHKey = PassphraseSSHKey::loadFromPHID(
- $credential->getPHID(),
- PhabricatorUser::getOmnipotentUser());
+ return $this->credential;
}
public function setConnectTimeout($timeout) {
@@ -39,30 +23,36 @@
}
public function getExecFuture($command) {
- $this->openCredentialsIfNotOpen();
+ $credential = $this->loadCredential();
$argv = func_get_args();
$argv = $this->applyWorkingDirectoryToArgv($argv);
$full_command = call_user_func_array('csprintf', $argv);
- $command_timeout = '';
- if ($this->connectTimeout !== null) {
- $command_timeout = csprintf(
- '-o %s',
- 'ConnectTimeout='.$this->connectTimeout);
+ $flags = array();
+ $flags[] = '-o';
+ $flags[] = 'LogLevel=quiet';
+
+ $flags[] = '-o';
+ $flags[] = 'StrictHostKeyChecking=no';
+
+ $flags[] = '-o';
+ $flags[] = 'UserKnownHostsFile=/dev/null';
+
+ $flags[] = '-o';
+ $flags[] = 'BatchMode=yes';
+
+ if ($this->connectTimeout) {
+ $flags[] = '-o';
+ $flags[] = 'ConnectTimeout='.$this->connectTimeout;
}
return new ExecFuture(
- 'ssh '.
- '-o LogLevel=quiet '.
- '-o StrictHostKeyChecking=no '.
- '-o UserKnownHostsFile=/dev/null '.
- '-o BatchMode=yes '.
- '%C -p %s -i %P %P@%s -- %s',
- $command_timeout,
+ 'ssh %Ls -l %P -p %s -i %P %s -- %s',
+ $flags,
+ $credential->getUsernameEnvelope(),
$this->getConfig('port'),
- $this->passphraseSSHKey->getKeyfileEnvelope(),
- $this->passphraseSSHKey->getUsernameEnvelope(),
+ $credential->getKeyfileEnvelope(),
$this->getConfig('host'),
$full_command);
}
diff --git a/src/applications/drydock/management/DrydockManagementCommandWorkflow.php b/src/applications/drydock/management/DrydockManagementCommandWorkflow.php
new file mode 100644
--- /dev/null
+++ b/src/applications/drydock/management/DrydockManagementCommandWorkflow.php
@@ -0,0 +1,66 @@
+<?php
+
+final class DrydockManagementCommandWorkflow
+ extends DrydockManagementWorkflow {
+
+ protected function didConstruct() {
+ $this
+ ->setName('command')
+ ->setSynopsis(pht('Run a command on a leased resource.'))
+ ->setArguments(
+ array(
+ array(
+ 'name' => 'lease',
+ 'param' => 'id',
+ 'help' => pht('Lease ID.'),
+ ),
+ array(
+ 'name' => 'argv',
+ 'wildcard' => true,
+ 'help' => pht('Command to execute.'),
+ ),
+ ));
+ }
+
+ public function execute(PhutilArgumentParser $args) {
+ $lease_id = $args->getArg('lease');
+ if (!$lease_id) {
+ throw new PhutilArgumentUsageException(
+ pht(
+ 'Use %s to specify a lease.',
+ '--lease'));
+ }
+
+ $argv = $args->getArg('argv');
+ if (!$argv) {
+ throw new PhutilArgumentUsageException(
+ pht(
+ 'Specify a command to run.'));
+ }
+
+ $lease = id(new DrydockLeaseQuery())
+ ->setViewer($this->getViewer())
+ ->withIDs(array($lease_id))
+ ->executeOne();
+ if (!$lease) {
+ throw new Exception(
+ pht(
+ 'Unable to load lease with ID "%s"!',
+ $lease_id));
+ }
+
+ // TODO: Check lease state, etc.
+
+ $interface = $lease->getInterface(DrydockCommandInterface::INTERFACE_TYPE);
+
+ list($stdout, $stderr) = call_user_func_array(
+ array($interface, 'execx'),
+ array('%Ls', $argv));
+
+ fprintf(STDOUT, $stdout);
+ fprintf(STDERR, $stderr);
+
+ return 0;
+ }
+
+}
diff --git a/src/applications/drydock/storage/DrydockBlueprint.php b/src/applications/drydock/storage/DrydockBlueprint.php
--- a/src/applications/drydock/storage/DrydockBlueprint.php
+++ b/src/applications/drydock/storage/DrydockBlueprint.php
@@ -173,6 +173,24 @@
return $this;
}
+ public function getInterface(
+ DrydockResource $resource,
+ DrydockLease $lease,
+ $type) {
+
+ $interface = $this->getImplementation()
+ ->getInterface($this, $resource, $lease, $type);
+
+ if (!$interface) {
+ throw new Exception(
+ pht(
+ 'Unable to build resource interface of type "%s".',
+ $type));
+ }
+
+ return $interface;
+ }
+
/* -( PhabricatorApplicationTransactionInterface )------------------------- */

File Metadata

Mime Type
text/plain
Expires
Fri, Mar 28, 4:08 PM (1 w, 9 h ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7725521
Default Alt Text
D14126.diff (15 KB)

Event Timeline