Correct a parameter order swap in "diffusion.historyquery" for Mercurial
[phabricator.git] / src / applications / drydock / management / DrydockManagementLeaseWorkflow.php
blob08f33c6b5f8a18f6616c068dcbe3002503f7a997
1 <?php
3 final class DrydockManagementLeaseWorkflow
4 extends DrydockManagementWorkflow {
6 protected function didConstruct() {
7 $this
8 ->setName('lease')
9 ->setSynopsis(pht('Lease a resource.'))
10 ->setArguments(
11 array(
12 array(
13 'name' => 'type',
14 'param' => 'resource_type',
15 'help' => pht('Resource type.'),
17 array(
18 'name' => 'until',
19 'param' => 'time',
20 'help' => pht('Set lease expiration time.'),
22 array(
23 'name' => 'attributes',
24 'param' => 'file',
25 'help' => pht(
26 'JSON file with lease attributes. Use "-" to read attributes '.
27 'from stdin.'),
29 ));
32 public function execute(PhutilArgumentParser $args) {
33 $viewer = $this->getViewer();
35 $resource_type = $args->getArg('type');
36 if (!$resource_type) {
37 throw new PhutilArgumentUsageException(
38 pht(
39 'Specify a resource type with `%s`.',
40 '--type'));
43 $until = $args->getArg('until');
44 if (strlen($until)) {
45 $until = strtotime($until);
46 if ($until <= 0) {
47 throw new PhutilArgumentUsageException(
48 pht(
49 'Unable to parse argument to "%s".',
50 '--until'));
54 $attributes_file = $args->getArg('attributes');
55 if (strlen($attributes_file)) {
56 if ($attributes_file == '-') {
57 echo tsprintf(
58 "%s\n",
59 'Reading JSON attributes from stdin...');
60 $data = file_get_contents('php://stdin');
61 } else {
62 $data = Filesystem::readFile($attributes_file);
65 $attributes = phutil_json_decode($data);
66 } else {
67 $attributes = array();
70 $lease = id(new DrydockLease())
71 ->setResourceType($resource_type);
73 $drydock_phid = id(new PhabricatorDrydockApplication())->getPHID();
74 $lease->setAuthorizingPHID($drydock_phid);
76 if ($attributes) {
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())
83 ->setViewer($viewer)
84 ->execute();
85 $allowed_phids = mpull($all_blueprints, 'getPHID');
86 if (!$allowed_phids) {
87 throw new Exception(
88 pht(
89 'No blueprints exist which can plausibly allocate resources to '.
90 'satisfy the requested lease.'));
92 $lease->setAllowedBlueprintPHIDs($allowed_phids);
94 if ($until) {
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();
112 echo tsprintf(
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
121 // durable.
122 $lease->setReleaseOnDestruction(false);
123 $t_end = microtime(true);
125 echo tsprintf(
126 "%s\n\n %s\n\n%s\n",
127 pht(
128 'Activation complete. This lease is permanent until manually '.
129 'released with:'),
130 pht('$ ./bin/drydock release-lease --id %d', $lease->getID()),
131 pht(
132 'Lease activated in %sms.',
133 new PhutilNumber((int)(($t_end - $t_start) * 1000))));
135 return 0;
138 public function didReceiveInterrupt($signo) {
139 // Doing this makes us run destructors, particularly the "release on
140 // destruction" trigger on the lease.
141 exit(128 + $signo);
144 private function waitUntilActive(DrydockLease $lease) {
145 $viewer = $this->getViewer();
147 $log_cursor = 0;
148 $log_types = DrydockLogType::getAllLogTypes();
150 $is_active = false;
151 while (!$is_active) {
152 $lease->reload();
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())
160 ->setViewer($viewer)
161 ->withLeasePHIDs(array($lease->getPHID()))
162 ->executeWithCursorPager($pager);
163 if ($logs) {
164 $logs = mpull($logs, null, 'getID');
165 ksort($logs);
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])
173 ->setLog($log)
174 ->setViewer($viewer);
176 $log_data = $log->getData();
178 $type = $type_object->getLogTypeName();
179 $data = $type_object->renderLogForText($log_data);
180 } else {
181 $type = pht('Unknown ("%s")', $type_key);
182 $data = null;
185 echo tsprintf(
186 "<%s> %B\n",
187 $type,
188 $data);
191 $status = $lease->getStatus();
193 switch ($status) {
194 case DrydockLeaseStatus::STATUS_ACTIVE:
195 $is_active = true;
196 break;
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:
205 break;
206 default:
207 throw new Exception(
208 pht(
209 'Lease has unknown status "%s".',
210 $status));
213 if ($is_active) {
214 break;
215 } else {
216 sleep(1);