Page MenuHomePhabricator

D16566.id39876.diff
No OneTemporary

D16566.id39876.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
@@ -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

Mime Type
text/plain
Expires
Oct 15 2024, 6:02 PM (4 w, 2 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6713804
Default Alt Text
D16566.id39876.diff (12 KB)

Event Timeline