3 final class DrydockManagementLeaseWorkflow
4 extends DrydockManagementWorkflow
{
6 protected function didConstruct() {
9 ->setSynopsis(pht('Lease a resource.'))
14 'param' => 'resource_type',
15 'help' => pht('Resource type.'),
20 'help' => pht('Set lease expiration time.'),
23 'name' => 'attributes',
26 'JSON file with lease attributes. Use "-" to read attributes '.
32 public function execute(PhutilArgumentParser
$args) {
33 $viewer = $this->getViewer();
35 $resource_type = $args->getArg('type');
36 if (!$resource_type) {
37 throw new PhutilArgumentUsageException(
39 'Specify a resource type with `%s`.',
43 $until = $args->getArg('until');
45 $until = strtotime($until);
47 throw new PhutilArgumentUsageException(
49 'Unable to parse argument to "%s".',
54 $attributes_file = $args->getArg('attributes');
55 if (strlen($attributes_file)) {
56 if ($attributes_file == '-') {
59 'Reading JSON attributes from stdin...');
60 $data = file_get_contents('php://stdin');
62 $data = Filesystem
::readFile($attributes_file);
65 $attributes = phutil_json_decode($data);
67 $attributes = array();
70 $lease = id(new DrydockLease())
71 ->setResourceType($resource_type);
73 $drydock_phid = id(new PhabricatorDrydockApplication())->getPHID();
74 $lease->setAuthorizingPHID($drydock_phid);
77 $lease->setAttributes($attributes);
80 // TODO: This is not hugely scalable, although this is a debugging workflow
81 // so maybe it's fine. Do we even need `bin/drydock lease` in the long run?
82 $all_blueprints = id(new DrydockBlueprintQuery())
85 $allowed_phids = mpull($all_blueprints, 'getPHID');
86 if (!$allowed_phids) {
89 'No blueprints exist which can plausibly allocate resources to '.
90 'satisfy the requested lease.'));
92 $lease->setAllowedBlueprintPHIDs($allowed_phids);
95 $lease->setUntil($until);
98 // If something fatals or the user interrupts the process (for example,
99 // with "^C"), release the lease. We'll cancel this below, if the lease
100 // actually activates.
101 $lease->setReleaseOnDestruction(true);
103 // TODO: This would probably be better handled with PhutilSignalRouter,
104 // but it currently doesn't route SIGINT. We're initializing it to setup
105 // SIGTERM handling and make eventual migration easier.
106 $router = PhutilSignalRouter
::getRouter();
107 pcntl_signal(SIGINT
, array($this, 'didReceiveInterrupt'));
109 $t_start = microtime(true);
110 $lease->queueForActivation();
113 "%s\n\n __%s__\n\n%s\n",
114 pht('Queued lease for activation:'),
115 PhabricatorEnv
::getProductionURI($lease->getURI()),
116 pht('Waiting for daemons to activate lease...'));
118 $this->waitUntilActive($lease);
120 // Now that we've survived activation and the lease is good, make it
122 $lease->setReleaseOnDestruction(false);
123 $t_end = microtime(true);
128 'Activation complete. This lease is permanent until manually '.
130 pht('$ ./bin/drydock release-lease --id %d', $lease->getID()),
132 'Lease activated in %sms.',
133 new PhutilNumber((int)(($t_end - $t_start) * 1000))));
138 public function didReceiveInterrupt($signo) {
139 // Doing this makes us run destructors, particularly the "release on
140 // destruction" trigger on the lease.
144 private function waitUntilActive(DrydockLease
$lease) {
145 $viewer = $this->getViewer();
148 $log_types = DrydockLogType
::getAllLogTypes();
151 while (!$is_active) {
154 $pager = id(new AphrontCursorPagerView())
155 ->setBeforeID($log_cursor);
157 // While we're waiting, show the user any logs which the daemons have
158 // generated to give them some clue about what's going on.
159 $logs = id(new DrydockLogQuery())
161 ->withLeasePHIDs(array($lease->getPHID()))
162 ->executeWithCursorPager($pager);
164 $logs = mpull($logs, null, 'getID');
166 $log_cursor = last_key($logs);
169 foreach ($logs as $log) {
170 $type_key = $log->getType();
171 if (isset($log_types[$type_key])) {
172 $type_object = id(clone $log_types[$type_key])
174 ->setViewer($viewer);
176 $log_data = $log->getData();
178 $type = $type_object->getLogTypeName();
179 $data = $type_object->renderLogForText($log_data);
181 $type = pht('Unknown ("%s")', $type_key);
191 $status = $lease->getStatus();
194 case DrydockLeaseStatus
::STATUS_ACTIVE
:
197 case DrydockLeaseStatus
::STATUS_RELEASED
:
198 throw new Exception(pht('Lease has already been released!'));
199 case DrydockLeaseStatus
::STATUS_DESTROYED
:
200 throw new Exception(pht('Lease has already been destroyed!'));
201 case DrydockLeaseStatus
::STATUS_BROKEN
:
202 throw new Exception(pht('Lease has been broken!'));
203 case DrydockLeaseStatus
::STATUS_PENDING
:
204 case DrydockLeaseStatus
::STATUS_ACQUIRED
:
209 'Lease has unknown status "%s".',