Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F14047927
D16566.id.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
12 KB
Referenced Files
None
Subscribers
None
D16566.id.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
@@ -215,6 +215,7 @@
'ArcanistLandWorkflow' => 'workflow/ArcanistLandWorkflow.php',
'ArcanistLanguageConstructParenthesesXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistLanguageConstructParenthesesXHPASTLinterRule.php',
'ArcanistLanguageConstructParenthesesXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistLanguageConstructParenthesesXHPASTLinterRuleTestCase.php',
+ 'ArcanistLeaseWorkflow' => 'workflow/ArcanistLeaseWorkflow.php',
'ArcanistLesscLinter' => 'lint/linter/ArcanistLesscLinter.php',
'ArcanistLesscLinterTestCase' => 'lint/linter/__tests__/ArcanistLesscLinterTestCase.php',
'ArcanistLiberateWorkflow' => 'workflow/ArcanistLiberateWorkflow.php',
@@ -306,6 +307,7 @@
'ArcanistPyLintLinterTestCase' => 'lint/linter/__tests__/ArcanistPyLintLinterTestCase.php',
'ArcanistRaggedClassTreeEdgeXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistRaggedClassTreeEdgeXHPASTLinterRule.php',
'ArcanistRaggedClassTreeEdgeXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistRaggedClassTreeEdgeXHPASTLinterRuleTestCase.php',
+ 'ArcanistReleaseWorkflow' => 'workflow/ArcanistReleaseWorkflow.php',
'ArcanistRepositoryAPI' => 'repository/api/ArcanistRepositoryAPI.php',
'ArcanistRepositoryAPIMiscTestCase' => 'repository/api/__tests__/ArcanistRepositoryAPIMiscTestCase.php',
'ArcanistRepositoryAPIStateTestCase' => 'repository/api/__tests__/ArcanistRepositoryAPIStateTestCase.php',
@@ -629,6 +631,7 @@
'ArcanistLandWorkflow' => 'ArcanistWorkflow',
'ArcanistLanguageConstructParenthesesXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistLanguageConstructParenthesesXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
+ 'ArcanistLeaseWorkflow' => 'ArcanistWorkflow',
'ArcanistLesscLinter' => 'ArcanistExternalLinter',
'ArcanistLesscLinterTestCase' => 'ArcanistExternalLinterTestCase',
'ArcanistLiberateWorkflow' => 'ArcanistWorkflow',
@@ -720,6 +723,7 @@
'ArcanistPyLintLinterTestCase' => 'ArcanistExternalLinterTestCase',
'ArcanistRaggedClassTreeEdgeXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistRaggedClassTreeEdgeXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
+ 'ArcanistReleaseWorkflow' => 'ArcanistWorkflow',
'ArcanistRepositoryAPI' => 'Phobject',
'ArcanistRepositoryAPIMiscTestCase' => 'PhutilTestCase',
'ArcanistRepositoryAPIStateTestCase' => 'PhutilTestCase',
diff --git a/src/workflow/ArcanistLeaseWorkflow.php b/src/workflow/ArcanistLeaseWorkflow.php
new file mode 100644
--- /dev/null
+++ b/src/workflow/ArcanistLeaseWorkflow.php
@@ -0,0 +1,214 @@
+<?php
+
+final class ArcanistLeaseWorkflow extends ArcanistWorkflow {
+
+ public function getWorkflowName() {
+ return 'lease';
+ }
+
+ public function getCommandSynopses() {
+ return phutil_console_format(<<<EOTEXT
+ **lease** [__options__]
+EOTEXT
+ );
+ }
+
+ public function getCommandHelp() {
+ return phutil_console_format(<<<EOTEXT
+
+ Lease resources from Drydock.
+
+ When given no arguments, this workflow will show you any blueprints
+ which you have requested authorization to use, and your authorization
+ state for each one.
+
+ When given a blueprint ID, this workflow will attempt to create a
+ lease using the specified blueprint. If you are not yet authorized
+ to use the blueprint, you will be prompted to request authorization.
+
+ Lease attributes should be supplied as a path to a file containing
+ JSON, or you can specify **-** and pipe JSON to the workflow over
+ stdin. See documentation for the conduit method 'drydock.createlease'
+ for more information about what attributes you can supply.
+
+ Successfully created leases may not be immediately active after the
+ workflow exits. You can instruct this workflow to block, waiting
+ until the lease is active, by supplying the **--wait** argument.
+EOTEXT
+ );
+ }
+
+ public function getArguments() {
+ return array(
+ 'blueprint' => array(
+ 'help' => pht('ID of the blueprint to use when acquiring a lease.'),
+ 'param' => 'id',
+ ),
+ 'wait' => array(
+ 'help' => pht('Block until the lease is active.'),
+ ),
+ '*' => 'attributes',
+ );
+ }
+
+ public function requiresAuthentication() {
+ return true;
+ }
+
+ public function run() {
+ if (!$this->getPassedArguments()) {
+ $this->listAuthorizations();
+ return 0;
+ }
+
+ $attributes = $this->getArgument('attributes');
+ if (count($attributes)) {
+ $file = head($attributes);
+ if ($file === '-') {
+ $json = file_get_contents('php://stdin');
+ } else {
+ $json = Filesystem::readFile($file);
+ }
+ $attributes = phutil_json_decode($json);
+ }
+
+ $blueprint_id = (int)$this->getArgument('blueprint');
+ if (!$blueprint_id) {
+ throw new ArcanistUsageException('You must provide a blueprint ID.');
+ }
+ $blueprint = $this->getConduit()->callMethodSynchronous(
+ 'drydock.blueprint.search',
+ array(
+ 'constraints' => array(
+ 'ids' => array($blueprint_id),
+ ),
+ ));
+ $blueprint = $blueprint['data'][0];
+ try {
+ $lease = $this->getConduit()->callMethodSynchronous(
+ 'drydock.createlease',
+ array(
+ 'blueprintPHID' => $blueprint['phid'],
+ 'leaseAttributes' => $attributes,
+ ));
+ } catch (ConduitClientException $e) {
+ if ($e->getErrorCode() === 'ERR_NOT_AUTHORIZED') {
+ if (phutil_console_confirm(pht(
+ 'You are not authorized to use this blueprint, request '.
+ 'authorization?'))) {
+ $this->requestAuthorization($blueprint['phid']);
+ return 1;
+ }
+ }
+ throw $e;
+ }
+
+ echo phutil_console_format(
+ "%s\n **%s** __%s__\n\n",
+ pht('Lease queued for activation.'),
+ pht('Lease URI:'),
+ rtrim($this->getConfigFromAnySource('phabricator.uri'), '/').'/'.
+ 'drydock/lease/'.$lease['id'].'/');
+
+ if ($this->getArgument('wait')) {
+ $this->waitUntilActive($lease['phid']);
+ }
+
+ return 0;
+ }
+
+ private function waitUntilActive($lease_phid) {
+ $lease = $this->getConduit()->callMethodSynchronous(
+ 'drydock.lease.search',
+ array(
+ 'constraints' => array(
+ 'phids' => array($lease_phid),
+ ),
+ ));
+ $lease_status = idxv($lease, array('data', 0, 'fields', 'status', 'value'));
+ switch ($lease_status) {
+ case 'pending':
+ case 'acquired':
+ sleep(1);
+ $this->waitUntilActive($lease_phid);
+ break;
+ case 'active':
+ return;
+ case 'released':
+ case 'broken':
+ case 'destroyed':
+ throw new Exception(pht('Lease failed to activate.'));
+ }
+ }
+
+ private function requestAuthorization($blueprint_phid) {
+ $console = PhutilConsole::getConsole();
+ $authorization = $this->getConduit()->callMethodSynchronous(
+ 'drydock.requestauthorization',
+ array(
+ 'blueprintPHID' => $blueprint_phid,
+ ));
+ echo phutil_console_format(
+ "%s\n **%s** __%s__\n\n",
+ pht('Authorization request issued.'),
+ pht('Request URI:'),
+ rtrim($this->getConfigFromAnySource('phabricator.uri'), '/').'/'.
+ 'drydock/authorization/'.$authorization['id'].'/');
+ }
+
+ private function listAuthorizations() {
+ $console = PhutilConsole::getConsole();
+ $console->writeOut("<bg:blue> %s </bg>\n", pht('BLUEPRINT AUTHORIZATIONS'));
+ $conduit = $this->getConduit();
+ $authorizations = $conduit->callMethodSynchronous(
+ 'drydock.authorization.search',
+ array(
+ 'constraints' => array(
+ 'objectPHIDs' => array($this->getUserPHID()),
+ ),
+ ));
+ $authorizations = idx($authorizations, 'data');
+
+ if (!$authorizations) {
+ $console->writeOut("\t%s\n", pht('No authorizations found.'));
+ }
+
+ $authorization_fields = ipull($authorizations, 'fields');
+ $blueprint_phids = ipull($authorization_fields, 'blueprintPHID');
+
+ $blueprints = $conduit->callMethodSynchronous(
+ 'drydock.blueprint.search',
+ array(
+ 'constraints' => array(
+ 'phids' => $blueprint_phids,
+ ),
+ ));
+ $blueprints = idx($blueprints, 'data');
+ $blueprints = ipull($blueprints, null, 'phid');
+
+ $table = (new PhutilConsoleTable())
+ ->addColumn('id', array('title' => 'ID'))
+ ->addColumn('name', array('title' => 'Name'))
+ ->addColumn('type', array('title' => 'Type'))
+ ->addColumn('authorization', array('title' => 'Authorization'))
+ ->setBorders(true);
+
+ foreach ($authorizations as $authorization) {
+ $blueprint_phid = idxv($authorization, array('fields', 'blueprintPHID'));
+ $blueprint = idx($blueprints, $blueprint_phid);
+ $table->addRow(array(
+ 'id' => idx($blueprint, 'id'),
+ 'name' => idxv($blueprint, array('fields', 'name')),
+ 'type' => idxv($blueprint, array('fields', 'type')),
+ 'authorization' => idxv($authorization, array(
+ 'fields',
+ 'blueprintAuthorizationState',
+ 'name',
+ )),
+ ));
+ }
+
+ $table->draw();
+ }
+
+}
diff --git a/src/workflow/ArcanistReleaseWorkflow.php b/src/workflow/ArcanistReleaseWorkflow.php
new file mode 100644
--- /dev/null
+++ b/src/workflow/ArcanistReleaseWorkflow.php
@@ -0,0 +1,108 @@
+<?php
+
+final class ArcanistReleaseWorkflow extends ArcanistWorkflow {
+
+ public function getWorkflowName() {
+ return 'release';
+ }
+
+ public function getCommandSynopses() {
+ return phutil_console_format(<<<EOTEXT
+ **release** [__id__]
+EOTEXT
+ );
+ }
+
+ public function getCommandHelp() {
+ return phutil_console_format(<<<EOTEXT
+
+ Release Drydock leases when you are done with them.
+EOTEXT
+ );
+ }
+
+ public function getArguments() {
+ return array(
+ '*' => 'lease',
+ );
+ }
+
+ public function requiresAuthentication() {
+ return true;
+ }
+
+ public function run() {
+ if (!$this->getPassedArguments()) {
+ $this->listLeases();
+ return 0;
+ }
+
+ $lease_id = (int)head($this->getArgument('lease'));
+ if (!$lease_id) {
+ throw new ArcanistUsageException('You must provide a lease ID.');
+ }
+
+ $lease = $this->getConduit()->callMethodSynchronous(
+ 'drydock.lease.search',
+ array(
+ 'constraints' => array(
+ 'ids' => array($lease_id),
+ ),
+ ));
+ $lease = $lease['data'][0];
+
+ $this->getConduit()->callMethodSynchronous('drydock.destroylease', array(
+ 'leasePHID' => $lease['phid'],
+ ));
+
+ echo phutil_console_format(
+ "%s\n **%s** __%s__\n\n",
+ pht('Lease scheduled for release.'),
+ pht('Lease URI:'),
+ rtrim($this->getConfigFromAnySource('phabricator.uri'), '/').'/'.
+ 'drydock/lease/'.$lease['id'].'/');
+
+ return 0;
+ }
+
+ protected function listLeases() {
+ $console = PhutilConsole::getConsole();
+ $console->writeOut("<bg:blue> %s </bg>\n", pht('LIVE LEASES'));
+ $conduit = $this->getConduit();
+ $leases = $conduit->callMethodSynchronous('drydock.lease.search', array(
+ 'constraints' => array(
+ 'statuses' => array(
+ 'pending',
+ 'acquired',
+ 'active',
+ ),
+ 'ownerPHIDs' => array(
+ $this->getUserPHID(),
+ ),
+ ),
+ ));
+ $leases = idx($leases, 'data', array());
+
+ if (!$leases) {
+ $console->writeOut("\t%s\n", pht('No leases found.'));
+ return;
+ }
+
+ $table = (new PhutilConsoleTable())
+ ->addColumn('id', array('title' => 'ID'))
+ ->addColumn('status', array('title' => 'Status'))
+ ->addColumn('type', array('title' => 'Type'))
+ ->setBorders(true);
+
+ foreach ($leases as $lease) {
+ $table->addRow(array(
+ 'id' => idx($lease, 'id'),
+ 'status' => idxv($lease, array('fields', 'status', 'name')),
+ 'type' => idxv($lease, array('fields', 'resourceType')),
+ ));
+ }
+
+ $table->draw();
+ }
+
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Nov 15, 5:50 AM (8 h, 50 m ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6713804
Default Alt Text
D16566.id.diff (12 KB)
Attached To
Mode
D16566: Manage Drydock leases from arcanist
Attached
Detach File
Event Timeline
Log In to Comment