Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F15445805
D14126.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
15 KB
Referenced Files
None
Subscribers
None
D14126.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D14126: Allow AlmanacHost blueprints to build a meaningful CommandInterface
Attached
Detach File
Event Timeline
Log In to Comment