@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Give Drydock resources a proper expiry mechanism

Summary:
Fixes T6569. This implements an expiry mechanism for Drydock resources which parallels the mechanism for leases.

A few things are missing that we'll probably need in the future:

- An "EXPIRES" command to update the expiration time. This would let resources be permanent while leased, then expire after, say, 24 hours without any leases.
- A callback like `shouldActuallyExpireRightNow()` for resources and leases that lets them decide not to expire at the last second.
- A callback like `didAcquireLease()` for resource blueprints, to parallel `didReleaseLease()`, letting them clear or extend their timer.

However, this stuff would mostly just let us tune behaviors, not really open up new capabilities.

Test Plan: Changed host resources to expire after 60 seconds, leased one, saw it vanish 60 seconds later.

Reviewers: hach-que, chad

Reviewed By: chad

Maniphest Tasks: T6569

Differential Revision: https://secure.phabricator.com/D14176

+142 -42
+2
resources/sql/autopatches/20150928.drydock.rexpire.1.sql
··· 1 + ALTER TABLE {$NAMESPACE}_drydock.drydock_resource 2 + ADD until INT UNSIGNED;
+4
src/applications/drydock/capability/DrydockDefaultViewCapability.php
··· 8 8 return pht('Default Blueprint View Policy'); 9 9 } 10 10 11 + public function shouldAllowPublicPolicySetting() { 12 + return true; 13 + } 14 + 11 15 }
+3 -8
src/applications/drydock/controller/DrydockConsoleController.php
··· 15 15 $nav->addFilter('blueprint', pht('Blueprints')); 16 16 $nav->addFilter('resource', pht('Resources')); 17 17 $nav->addFilter('lease', pht('Leases')); 18 - $nav->addFilter('log', pht('Logs')); 19 18 20 19 $nav->selectFilter(null); 21 20 ··· 31 30 $menu->addItem( 32 31 id(new PHUIObjectItemView()) 33 32 ->setHeader(pht('Blueprints')) 33 + ->setFontIcon('fa-map-o') 34 34 ->setHref($this->getApplicationURI('blueprint/')) 35 35 ->addAttribute( 36 36 pht( ··· 40 40 $menu->addItem( 41 41 id(new PHUIObjectItemView()) 42 42 ->setHeader(pht('Resources')) 43 + ->setFontIcon('fa-map') 43 44 ->setHref($this->getApplicationURI('resource/')) 44 45 ->addAttribute( 45 46 pht('View and manage resources Drydock has built, like hosts.'))); ··· 47 48 $menu->addItem( 48 49 id(new PHUIObjectItemView()) 49 50 ->setHeader(pht('Leases')) 51 + ->setFontIcon('fa-link') 50 52 ->setHref($this->getApplicationURI('lease/')) 51 53 ->addAttribute(pht('Manage leases on resources.'))); 52 - 53 - $menu->addItem( 54 - id(new PHUIObjectItemView()) 55 - ->setHeader(pht('Logs')) 56 - ->setHref($this->getApplicationURI('log/')) 57 - ->addAttribute(pht('View logs.'))); 58 - 59 54 60 55 $crumbs = $this->buildApplicationCrumbs(); 61 56 $crumbs->addTextCrumb(pht('Console'));
+8
src/applications/drydock/controller/DrydockResourceViewController.php
··· 116 116 pht('Status'), 117 117 $status); 118 118 119 + $until = $resource->getUntil(); 120 + if ($until) { 121 + $until_display = phabricator_datetime($until, $viewer); 122 + } else { 123 + $until_display = phutil_tag('em', array(), pht('Never')); 124 + } 125 + $view->addProperty(pht('Expires'), $until_display); 126 + 119 127 $view->addProperty( 120 128 pht('Resource Type'), 121 129 $resource->getType());
+9
src/applications/drydock/storage/DrydockLease.php
··· 295 295 } 296 296 } 297 297 298 + public function canUpdate() { 299 + switch ($this->getStatus()) { 300 + case DrydockLeaseStatus::STATUS_ACTIVE: 301 + return true; 302 + default: 303 + return false; 304 + } 305 + } 306 + 298 307 public function scheduleUpdate($epoch = null) { 299 308 PhabricatorWorker::scheduleTask( 300 309 'DrydockLeaseUpdateWorker',
+25 -1
src/applications/drydock/storage/DrydockResource.php
··· 7 7 protected $phid; 8 8 protected $blueprintPHID; 9 9 protected $status; 10 + protected $until; 10 11 11 12 protected $type; 12 13 protected $name; ··· 32 33 'ownerPHID' => 'phid?', 33 34 'status' => 'text32', 34 35 'type' => 'text64', 36 + 'until' => 'epoch?', 35 37 ), 36 38 self::CONFIG_KEY_SCHEMA => array( 37 39 'key_type' => array( ··· 126 128 127 129 $this->isAllocated = true; 128 130 131 + if ($new_status == DrydockResourceStatus::STATUS_ACTIVE) { 132 + $this->didActivate(); 133 + } 134 + 129 135 return $this; 130 136 } 131 137 ··· 164 170 165 171 $this->isActivated = true; 166 172 173 + $this->didActivate(); 174 + 167 175 return $this; 168 176 } 169 177 ··· 181 189 } 182 190 } 183 191 184 - public function scheduleUpdate() { 192 + public function scheduleUpdate($epoch = null) { 185 193 PhabricatorWorker::scheduleTask( 186 194 'DrydockResourceUpdateWorker', 187 195 array( 188 196 'resourcePHID' => $this->getPHID(), 197 + 'isExpireTask' => ($epoch !== null), 189 198 ), 190 199 array( 191 200 'objectPHID' => $this->getPHID(), 201 + 'delayUntil' => $epoch, 192 202 )); 193 203 } 194 204 ··· 208 218 209 219 if ($need_update) { 210 220 $this->scheduleUpdate(); 221 + } 222 + 223 + $expires = $this->getUntil(); 224 + if ($expires) { 225 + $this->scheduleUpdate($expires); 226 + } 227 + } 228 + 229 + public function canUpdate() { 230 + switch ($this->getStatus()) { 231 + case DrydockResourceStatus::STATUS_ACTIVE: 232 + return true; 233 + default: 234 + return false; 211 235 } 212 236 } 213 237
+4 -28
src/applications/drydock/worker/DrydockLeaseUpdateWorker.php
··· 23 23 } 24 24 25 25 private function updateLease(DrydockLease $lease) { 26 - if ($lease->getStatus() != DrydockLeaseStatus::STATUS_ACTIVE) { 26 + if (!$lease->canUpdate()) { 27 27 return; 28 28 } 29 29 30 - $viewer = $this->getViewer(); 31 - $drydock_phid = id(new PhabricatorDrydockApplication())->getPHID(); 32 - 33 - // Check if the lease has expired. If it is, we're going to send it a 34 - // release command. This command will be handled immediately below, it 35 - // just generates a command log and improves consistency. 36 - $now = PhabricatorTime::getNow(); 37 - $expires = $lease->getUntil(); 38 - if ($expires && ($expires <= $now)) { 39 - $command = DrydockCommand::initializeNewCommand($viewer) 40 - ->setTargetPHID($lease->getPHID()) 41 - ->setAuthorPHID($drydock_phid) 42 - ->setCommand(DrydockCommand::COMMAND_RELEASE) 43 - ->save(); 44 - } 30 + $this->checkLeaseExpiration($lease); 45 31 46 32 $commands = $this->loadCommands($lease->getPHID()); 47 33 foreach ($commands as $command) { 48 - if ($lease->getStatus() != DrydockLeaseStatus::STATUS_ACTIVE) { 49 - // Leases can't receive commands before they activate or after they 50 - // release. 34 + if (!$lease->canUpdate()) { 51 35 break; 52 36 } 53 37 ··· 58 42 ->save(); 59 43 } 60 44 61 - // If this is the task which will eventually release the lease after it 62 - // expires but it is still active, reschedule the task to run after the 63 - // lease expires. This can happen if the lease's expiration was pushed 64 - // forward. 65 - if ($lease->getStatus() == DrydockLeaseStatus::STATUS_ACTIVE) { 66 - if ($this->getTaskDataValue('isExpireTask') && $expires) { 67 - throw new PhabricatorWorkerYieldException($expires - $now); 68 - } 69 - } 45 + $this->yieldIfExpiringLease($lease); 70 46 } 71 47 72 48 private function processCommand(
+16 -5
src/applications/drydock/worker/DrydockResourceUpdateWorker.php
··· 11 11 $lock = PhabricatorGlobalLock::newLock($lock_key) 12 12 ->lock(1); 13 13 14 - $resource = $this->loadResource($resource_phid); 15 - $this->updateResource($resource); 14 + try { 15 + $resource = $this->loadResource($resource_phid); 16 + $this->updateResource($resource); 17 + } catch (Exception $ex) { 18 + $lock->unlock(); 19 + throw $ex; 20 + } 16 21 17 22 $lock->unlock(); 18 23 } 19 24 20 25 private function updateResource(DrydockResource $resource) { 26 + if (!$resource->canUpdate()) { 27 + return; 28 + } 29 + 30 + $this->checkResourceExpiration($resource); 31 + 21 32 $commands = $this->loadCommands($resource->getPHID()); 22 33 foreach ($commands as $command) { 23 - if ($resource->getStatus() != DrydockResourceStatus::STATUS_ACTIVE) { 24 - // Resources can't receive commands before they activate or after they 25 - // release. 34 + if (!$resource->canUpdate()) { 26 35 break; 27 36 } 28 37 ··· 32 41 ->setIsConsumed(true) 33 42 ->save(); 34 43 } 44 + 45 + $this->yieldIfExpiringResource($resource); 35 46 } 36 47 37 48 private function processCommand(
+64
src/applications/drydock/worker/DrydockWorker.php
··· 50 50 return $commands; 51 51 } 52 52 53 + protected function checkLeaseExpiration(DrydockLease $lease) { 54 + $this->checkObjectExpiration($lease); 55 + } 56 + 57 + protected function checkResourceExpiration(DrydockResource $resource) { 58 + $this->checkObjectExpiration($resource); 59 + } 60 + 61 + private function checkObjectExpiration($object) { 62 + // Check if the resource or lease has expired. If it has, we're going to 63 + // send it a release command. 64 + 65 + // This command is sent from within the update worker so it is handled 66 + // immediately, but doing this generates a log and improves consistency. 67 + 68 + $expires = $object->getUntil(); 69 + if (!$expires) { 70 + return; 71 + } 72 + 73 + $now = PhabricatorTime::getNow(); 74 + if ($expires > $now) { 75 + return; 76 + } 77 + 78 + $viewer = $this->getViewer(); 79 + $drydock_phid = id(new PhabricatorDrydockApplication())->getPHID(); 80 + 81 + $command = DrydockCommand::initializeNewCommand($viewer) 82 + ->setTargetPHID($object->getPHID()) 83 + ->setAuthorPHID($drydock_phid) 84 + ->setCommand(DrydockCommand::COMMAND_RELEASE) 85 + ->save(); 86 + } 87 + 88 + protected function yieldIfExpiringLease(DrydockLease $lease) { 89 + if (!$lease->canUpdate()) { 90 + return; 91 + } 92 + 93 + $this->yieldIfExpiring($lease->getUntil()); 94 + } 95 + 96 + protected function yieldIfExpiringResource(DrydockResource $resource) { 97 + if (!$resource->canUpdate()) { 98 + return; 99 + } 100 + 101 + $this->yieldIfExpiring($resource->getUntil()); 102 + } 103 + 104 + private function yieldIfExpiring($expires) { 105 + if (!$expires) { 106 + return; 107 + } 108 + 109 + if (!$this->getTaskDataValue('isExpireTask')) { 110 + return; 111 + } 112 + 113 + $now = PhabricatorTime::getNow(); 114 + throw new PhabricatorWorkerYieldException($expires - $now); 115 + } 116 + 53 117 }
+7
src/infrastructure/daemon/workers/management/PhabricatorWorkerManagementWorkflow.php
··· 39 39 } 40 40 } 41 41 42 + // When we lock tasks properly, this gets populated as a side effect. Just 43 + // fake it when doing manual CLI stuff. This makes sure CLI yields have 44 + // their expires times set properly. 45 + foreach ($tasks as $task) { 46 + $task->setServerTime(PhabricatorTime::getNow()); 47 + } 48 + 42 49 return $tasks; 43 50 } 44 51