Skip to content
This repository was archived by the owner on Apr 7, 2022. It is now read-only.

Commit 4cf1270

Browse files
author
epriestley
committed
In Harbormaster, make sure artifacts are destroyed even if a build is aborted
Summary: Ref T9252. Currently, Harbormaster and Drydock work like this in some cases: # Queue a lease for activation. # Then, a little later, save the lease PHID somewhere. # When the target/resource is destroyed, destroy the lease. However, something can happen between (1) and (2). In Drydock this window is very short and the "something" would have to be a lighting strike or something similar, but in Harbormaster we wait until the resource activates to do (2) so the window can be many minutes long. In particular, a user can use "Abort Build" during those many minutes. If they do, the target is destroyed but it doesn't yet have a record of the artifact, so the artifact isn't cleaned up. Make these things work like this instead: # Create a new lease and pre-generate a PHID for it. # Save that PHID as something that needs to be cleaned up. # Queue the lease for activation. # When the target/resource is destroyed, destroy the lease if it exists. This makes sure there's no step in the process where we might lose track of a lease/resource. Also, clean up and standardize some other stuff I hit. Test Plan: - Stopped daemons. - Restarted a build in Harbormaster. - Stepped through the build one stage at a time using `bin/worker execute ...`. - After the lease was queued, but before it activated, aborted the build. - Processed the Harbormaster side of things only. - Saw the lease get destroyed properly. Reviewers: chad, hach-que Reviewed By: hach-que Maniphest Tasks: T9252 Differential Revision: https://secure.phabricator.com/D14234
1 parent 0db86cc commit 4cf1270

11 files changed

+111
-55
lines changed

src/applications/drydock/blueprint/DrydockAlmanacServiceHostBlueprintImplementation.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,13 +262,14 @@ private function loadFreeBindings(DrydockBlueprint $blueprint) {
262262
array(
263263
DrydockResourceStatus::STATUS_PENDING,
264264
DrydockResourceStatus::STATUS_ACTIVE,
265+
DrydockResourceStatus::STATUS_BROKEN,
265266
DrydockResourceStatus::STATUS_RELEASED,
266267
))
267268
->execute();
268269

269270
$allocated_phids = array();
270271
foreach ($pool as $resource) {
271-
$allocated_phids[] = $resource->getAttribute('almanacDevicePHID');
272+
$allocated_phids[] = $resource->getAttribute('almanacBindingPHID');
272273
}
273274
$allocated_phids = array_fuse($allocated_phids);
274275

src/applications/drydock/blueprint/DrydockBlueprintImplementation.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ protected function newResourceTemplate(DrydockBlueprint $blueprint) {
288288
}
289289

290290
protected function newLease(DrydockBlueprint $blueprint) {
291-
return id(new DrydockLease());
291+
return DrydockLease::initializeNewLease();
292292
}
293293

294294
protected function requireActiveLease(DrydockLease $lease) {

src/applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,13 @@ public function allocateResource(
104104
$host_lease = $this->newLease($blueprint)
105105
->setResourceType('host')
106106
->setOwnerPHID($resource_phid)
107-
->setAttribute('workingcopy.resourcePHID', $resource_phid)
108-
->queueForActivation();
107+
->setAttribute('workingcopy.resourcePHID', $resource_phid);
108+
109+
$resource
110+
->setAttribute('host.leasePHID', $host_lease->getPHID())
111+
->save();
112+
113+
$host_lease->queueForActivation();
109114

110115
// TODO: Add some limits to the number of working copies we can have at
111116
// once?
@@ -121,7 +126,6 @@ public function allocateResource(
121126

122127
return $resource
123128
->setAttribute('repositories.map', $map)
124-
->setAttribute('host.leasePHID', $host_lease->getPHID())
125129
->allocateResource();
126130
}
127131

@@ -165,7 +169,13 @@ public function destroyResource(
165169
DrydockBlueprint $blueprint,
166170
DrydockResource $resource) {
167171

168-
$lease = $this->loadHostLease($resource);
172+
try {
173+
$lease = $this->loadHostLease($resource);
174+
} catch (Exception $ex) {
175+
// If we can't load the lease, assume we don't need to take any actions
176+
// to destroy it.
177+
return;
178+
}
169179

170180
// Destroy the lease on the host.
171181
$lease->releaseOnDestruction();

src/applications/drydock/logtype/DrydockLeaseWaitingForResourcesLogType.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public function renderLog(array $data) {
1919

2020
return pht(
2121
'Waiting for available resources from: %s.',
22-
$viewer->renderHandleList($blueprint_phids));
22+
$viewer->renderHandleList($blueprint_phids)->render());
2323
}
2424

2525
}

src/applications/drydock/storage/DrydockLease.php

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,16 @@ final class DrydockLease extends DrydockDAO
1919
private $activateWhenAcquired = false;
2020
private $slotLocks = array();
2121

22+
public static function initializeNewLease() {
23+
$lease = new DrydockLease();
24+
25+
// Pregenerate a PHID so that the caller can set something up to release
26+
// this lease before queueing it for activation.
27+
$lease->setPHID($lease->generatePHID());
28+
29+
return $lease;
30+
}
31+
2232
/**
2333
* Flag this lease to be released when its destructor is called. This is
2434
* mostly useful if you have a script which acquires, uses, and then releases
@@ -232,6 +242,7 @@ public function acquireOnResource(DrydockResource $resource) {
232242
}
233243

234244
$this->openTransaction();
245+
235246
try {
236247
DrydockSlotLock::acquireLocks($this->getPHID(), $this->slotLocks);
237248
$this->slotLocks = array();
@@ -247,16 +258,12 @@ public function acquireOnResource(DrydockResource $resource) {
247258
throw $ex;
248259
}
249260

250-
try {
251-
$this
252-
->setResourcePHID($resource->getPHID())
253-
->attachResource($resource)
254-
->setStatus($new_status)
255-
->save();
256-
} catch (Exception $ex) {
257-
$this->killTransaction();
258-
throw $ex;
259-
}
261+
$this
262+
->setResourcePHID($resource->getPHID())
263+
->attachResource($resource)
264+
->setStatus($new_status)
265+
->save();
266+
260267
$this->saveTransaction();
261268

262269
$this->isAcquired = true;
@@ -295,12 +302,24 @@ public function activateOnResource(DrydockResource $resource) {
295302

296303
$this->openTransaction();
297304

298-
$this
299-
->setStatus(DrydockLeaseStatus::STATUS_ACTIVE)
300-
->save();
301-
305+
try {
302306
DrydockSlotLock::acquireLocks($this->getPHID(), $this->slotLocks);
303307
$this->slotLocks = array();
308+
} catch (DrydockSlotLockException $ex) {
309+
$this->killTransaction();
310+
311+
$this->logEvent(
312+
DrydockSlotLockFailureLogType::LOGCONST,
313+
array(
314+
'locks' => $ex->getLockMap(),
315+
));
316+
317+
throw $ex;
318+
}
319+
320+
$this
321+
->setStatus(DrydockLeaseStatus::STATUS_ACTIVE)
322+
->save();
304323

305324
$this->saveTransaction();
306325

src/applications/drydock/storage/DrydockResource.php

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -113,13 +113,6 @@ public function needSlotLock($key) {
113113
}
114114

115115
public function allocateResource() {
116-
if ($this->getID()) {
117-
throw new Exception(
118-
pht(
119-
'Trying to allocate a resource which has already been persisted. '.
120-
'Only new resources may be allocated.'));
121-
}
122-
123116
// We expect resources to have a pregenerated PHID, as they should have
124117
// been created by a call to DrydockBlueprint->newResourceTemplate().
125118
if (!$this->getPHID()) {
@@ -155,9 +148,14 @@ public function allocateResource() {
155148
} catch (DrydockSlotLockException $ex) {
156149
$this->killTransaction();
157150

158-
// NOTE: We have to log this on the blueprint, as the resource is not
159-
// going to be saved so the PHID will vanish.
160-
$this->getBlueprint()->logEvent(
151+
if ($this->getID()) {
152+
$log_target = $this;
153+
} else {
154+
// If we don't have an ID, we have to log this on the blueprint, as the
155+
// resource is not going to be saved so the PHID will vanish.
156+
$log_target = $this->getBlueprint();
157+
}
158+
$log_target->logEvent(
161159
DrydockSlotLockFailureLogType::LOGCONST,
162160
array(
163161
'locks' => $ex->getLockMap(),
@@ -166,14 +164,9 @@ public function allocateResource() {
166164
throw $ex;
167165
}
168166

169-
try {
170-
$this
171-
->setStatus($new_status)
172-
->save();
173-
} catch (Exception $ex) {
174-
$this->killTransaction();
175-
throw $ex;
176-
}
167+
$this
168+
->setStatus($new_status)
169+
->save();
177170

178171
$this->saveTransaction();
179172

@@ -210,12 +203,24 @@ public function activateResource() {
210203

211204
$this->openTransaction();
212205

213-
$this
214-
->setStatus(DrydockResourceStatus::STATUS_ACTIVE)
215-
->save();
216-
206+
try {
217207
DrydockSlotLock::acquireLocks($this->getPHID(), $this->slotLocks);
218208
$this->slotLocks = array();
209+
} catch (DrydockSlotLockException $ex) {
210+
$this->killTransaction();
211+
212+
$this->logEvent(
213+
DrydockSlotLockFailureLogType::LOGCONST,
214+
array(
215+
'locks' => $ex->getLockMap(),
216+
));
217+
218+
throw $ex;
219+
}
220+
221+
$this
222+
->setStatus(DrydockResourceStatus::STATUS_ACTIVE)
223+
->save();
219224

220225
$this->saveTransaction();
221226

src/applications/drydock/storage/DrydockSlotLock.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ public static function acquireLocks($owner_phid, array $locks) {
149149
// time we should be able to figure out which locks are already held.
150150
$held = self::loadHeldLocks($locks);
151151
$held = mpull($held, 'getOwnerPHID', 'getLockKey');
152+
152153
throw new DrydockSlotLockException($held);
153154
}
154155
}

src/applications/harbormaster/artifact/HarbormasterDrydockLeaseArtifact.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ public function renderArtifactSummary(PhabricatorUser $viewer) {
2929
}
3030

3131
public function willCreateArtifact(PhabricatorUser $actor) {
32-
$this->loadArtifactLease($actor);
32+
// We don't load the lease here because it's expected that artifacts are
33+
// created before leases actually exist. This guarantees that the leases
34+
// will be cleaned up.
3335
}
3436

3537
public function loadArtifactLease(PhabricatorUser $viewer) {
@@ -51,7 +53,15 @@ public function loadArtifactLease(PhabricatorUser $viewer) {
5153
}
5254

5355
public function releaseArtifact(PhabricatorUser $actor) {
54-
$lease = $this->loadArtifactLease($actor);
56+
try {
57+
$lease = $this->loadArtifactLease($actor);
58+
} catch (Exception $ex) {
59+
// If we can't load the lease, treat it as already released. Artifacts
60+
// are generated before leases are queued, so it's possible to arrive
61+
// here under normal conditions.
62+
return;
63+
}
64+
5565
if (!$lease->canRelease()) {
5666
return;
5767
}

src/applications/harbormaster/step/HarbormasterLeaseWorkingCopyBuildStepImplementation.php

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public function execute(
4141
$working_copy_type = id(new DrydockWorkingCopyBlueprintImplementation())
4242
->getType();
4343

44-
$lease = id(new DrydockLease())
44+
$lease = DrydockLease::initializeNewLease()
4545
->setResourceType($working_copy_type)
4646
->setOwnerPHID($build_target->getPHID());
4747

@@ -54,6 +54,18 @@ public function execute(
5454
$lease->setAwakenTaskIDs(array($task_id));
5555
}
5656

57+
// TODO: Maybe add a method to mark artifacts like this as pending?
58+
59+
// Create the artifact now so that the lease is always disposed of, even
60+
// if this target is aborted.
61+
$build_target->createArtifact(
62+
$viewer,
63+
$settings['name'],
64+
HarbormasterWorkingCopyArtifact::ARTIFACTCONST,
65+
array(
66+
'drydockLeasePHID' => $lease->getPHID(),
67+
));
68+
5769
$lease->queueForActivation();
5870

5971
$build_target
@@ -73,14 +85,6 @@ public function execute(
7385
'Lease "%s" never activated.',
7486
$lease->getPHID()));
7587
}
76-
77-
$artifact = $build_target->createArtifact(
78-
$viewer,
79-
$settings['name'],
80-
HarbormasterWorkingCopyArtifact::ARTIFACTCONST,
81-
array(
82-
'drydockLeasePHID' => $lease->getPHID(),
83-
));
8488
}
8589

8690
public function getArtifactOutputs() {

src/infrastructure/daemon/workers/management/PhabricatorWorkerManagementExecuteWorkflow.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ public function execute(PhutilArgumentParser $args) {
4242
$task->getDataID());
4343
$task->setData($task_data->getData());
4444

45-
$console->writeOut(
45+
echo tsprintf(
46+
"%s\n",
4647
pht(
4748
'Executing task %d (%s)...',
4849
$task->getID(),

0 commit comments

Comments
 (0)