Changeset View
Changeset View
Standalone View
Standalone View
src/applications/harbormaster/engine/HarbormasterBuildEngine.php
Show First 20 Lines • Show All 43 Lines • ▼ Show 20 Lines | public function setBuild(HarbormasterBuild $build) { | ||||
return $this; | return $this; | ||||
} | } | ||||
public function getBuild() { | public function getBuild() { | ||||
return $this->build; | return $this->build; | ||||
} | } | ||||
public function continueBuild() { | public function continueBuild() { | ||||
$viewer = $this->getViewer(); | |||||
$build = $this->getBuild(); | $build = $this->getBuild(); | ||||
$lock_key = 'harbormaster.build:'.$build->getID(); | $lock_key = 'harbormaster.build:'.$build->getID(); | ||||
$lock = PhabricatorGlobalLock::newLock($lock_key)->lock(15); | $lock = PhabricatorGlobalLock::newLock($lock_key)->lock(15); | ||||
$build->reload(); | $build->reload(); | ||||
$old_status = $build->getBuildStatus(); | $old_status = $build->getBuildStatus(); | ||||
try { | try { | ||||
$this->updateBuild($build); | $this->updateBuild($build); | ||||
} catch (Exception $ex) { | } catch (Exception $ex) { | ||||
// If any exception is raised, the build is marked as a failure and the | // If any exception is raised, the build is marked as a failure and the | ||||
// exception is re-thrown (this ensures we don't leave builds in an | // exception is re-thrown (this ensures we don't leave builds in an | ||||
// inconsistent state). | // inconsistent state). | ||||
$build->setBuildStatus(HarbormasterBuildStatus::STATUS_ERROR); | $build->setBuildStatus(HarbormasterBuildStatus::STATUS_ERROR); | ||||
$build->save(); | $build->save(); | ||||
$lock->unlock(); | $lock->unlock(); | ||||
$this->releaseAllArtifacts($build); | $build->releaseAllArtifacts($viewer); | ||||
throw $ex; | throw $ex; | ||||
} | } | ||||
$lock->unlock(); | $lock->unlock(); | ||||
// NOTE: We queue new targets after releasing the lock so that in-process | // NOTE: We queue new targets after releasing the lock so that in-process | ||||
// execution via `bin/harbormaster` does not reenter the locked region. | // execution via `bin/harbormaster` does not reenter the locked region. | ||||
Show All 14 Lines | public function continueBuild() { | ||||
if ($new_status != $old_status || $this->shouldForceBuildableUpdate()) { | if ($new_status != $old_status || $this->shouldForceBuildableUpdate()) { | ||||
$this->updateBuildable($build->getBuildable()); | $this->updateBuildable($build->getBuildable()); | ||||
} | } | ||||
$this->releaseQueuedArtifacts(); | $this->releaseQueuedArtifacts(); | ||||
// If we are no longer building for any reason, release all artifacts. | // If we are no longer building for any reason, release all artifacts. | ||||
if (!$build->isBuilding()) { | if (!$build->isBuilding()) { | ||||
$this->releaseAllArtifacts($build); | $build->releaseAllArtifacts($viewer); | ||||
} | } | ||||
} | } | ||||
private function updateBuild(HarbormasterBuild $build) { | private function updateBuild(HarbormasterBuild $build) { | ||||
if ($build->isAborting()) { | $viewer = $this->getViewer(); | ||||
$this->releaseAllArtifacts($build); | |||||
$build->setBuildStatus(HarbormasterBuildStatus::STATUS_ABORTED); | $content_source = PhabricatorContentSource::newForSource( | ||||
$build->save(); | PhabricatorDaemonContentSource::SOURCECONST); | ||||
$acting_phid = $viewer->getPHID(); | |||||
if (!$acting_phid) { | |||||
$acting_phid = id(new PhabricatorHarbormasterApplication())->getPHID(); | |||||
} | } | ||||
if (($build->getBuildStatus() == HarbormasterBuildStatus::STATUS_PENDING) || | $editor = $build->getApplicationTransactionEditor() | ||||
($build->isRestarting())) { | ->setActor($viewer) | ||||
$this->restartBuild($build); | ->setActingAsPHID($acting_phid) | ||||
$build->setBuildStatus(HarbormasterBuildStatus::STATUS_BUILDING); | ->setContentSource($content_source) | ||||
$build->save(); | ->setContinueOnNoEffect(true) | ||||
->setContinueOnMissingFields(true); | |||||
$xactions = array(); | |||||
$messages = $build->getUnprocessedMessagesForApply(); | |||||
foreach ($messages as $message) { | |||||
$message_type = $message->getType(); | |||||
$xactions[] = $build->getApplicationTransactionTemplate() | |||||
->setAuthorPHID($message->getAuthorPHID()) | |||||
->setTransactionType(HarbormasterBuildTransaction::TYPE_COMMAND) | |||||
->setNewValue($message_type); | |||||
} | } | ||||
if ($build->isResuming()) { | if (!$xactions) { | ||||
if ($build->isPending()) { | |||||
// TODO: This should be a transaction. | |||||
$build->restartBuild($viewer); | |||||
$build->setBuildStatus(HarbormasterBuildStatus::STATUS_BUILDING); | $build->setBuildStatus(HarbormasterBuildStatus::STATUS_BUILDING); | ||||
$build->save(); | $build->save(); | ||||
} | } | ||||
if ($build->isPausing() && !$build->isComplete()) { | |||||
$build->setBuildStatus(HarbormasterBuildStatus::STATUS_PAUSED); | |||||
$build->save(); | |||||
} | } | ||||
if ($xactions) { | |||||
$editor->applyTransactions($build, $xactions); | |||||
$build->markUnprocessedMessagesAsProcessed(); | $build->markUnprocessedMessagesAsProcessed(); | ||||
} | |||||
if ($build->getBuildStatus() == HarbormasterBuildStatus::STATUS_BUILDING) { | if ($build->getBuildStatus() == HarbormasterBuildStatus::STATUS_BUILDING) { | ||||
$this->updateBuildSteps($build); | $this->updateBuildSteps($build); | ||||
} | } | ||||
} | } | ||||
private function restartBuild(HarbormasterBuild $build) { | |||||
// We're restarting the build, so release all previous artifacts. | |||||
$this->releaseAllArtifacts($build); | |||||
// Increment the build generation counter on the build. | |||||
$build->setBuildGeneration($build->getBuildGeneration() + 1); | |||||
// Currently running targets should periodically check their build | |||||
// generation (which won't have changed) against the build's generation. | |||||
// If it is different, they will automatically stop what they're doing | |||||
// and abort. | |||||
// Previously we used to delete targets, logs and artifacts here. Instead, | |||||
// leave them around so users can view previous generations of this build. | |||||
} | |||||
private function updateBuildSteps(HarbormasterBuild $build) { | private function updateBuildSteps(HarbormasterBuild $build) { | ||||
$all_targets = id(new HarbormasterBuildTargetQuery()) | $all_targets = id(new HarbormasterBuildTargetQuery()) | ||||
->setViewer($this->getViewer()) | ->setViewer($this->getViewer()) | ||||
->withBuildPHIDs(array($build->getPHID())) | ->withBuildPHIDs(array($build->getPHID())) | ||||
->withBuildGenerations(array($build->getBuildGeneration())) | ->withBuildGenerations(array($build->getBuildGeneration())) | ||||
->execute(); | ->execute(); | ||||
$this->updateWaitingTargets($all_targets); | $this->updateWaitingTargets($all_targets); | ||||
▲ Show 20 Lines • Show All 429 Lines • ▼ Show 20 Lines | $harbormaster_phid = id(new PhabricatorHarbormasterApplication()) | ||||
->getPHID(); | ->getPHID(); | ||||
$engine | $engine | ||||
->setActingAsPHID($harbormaster_phid) | ->setActingAsPHID($harbormaster_phid) | ||||
->setContentSource($daemon_source) | ->setContentSource($daemon_source) | ||||
->publishBuildable($old, $new); | ->publishBuildable($old, $new); | ||||
} | } | ||||
private function releaseAllArtifacts(HarbormasterBuild $build) { | |||||
$targets = id(new HarbormasterBuildTargetQuery()) | |||||
->setViewer(PhabricatorUser::getOmnipotentUser()) | |||||
->withBuildPHIDs(array($build->getPHID())) | |||||
->withBuildGenerations(array($build->getBuildGeneration())) | |||||
->execute(); | |||||
if (count($targets) === 0) { | |||||
return; | |||||
} | |||||
$target_phids = mpull($targets, 'getPHID'); | |||||
$artifacts = id(new HarbormasterBuildArtifactQuery()) | |||||
->setViewer(PhabricatorUser::getOmnipotentUser()) | |||||
->withBuildTargetPHIDs($target_phids) | |||||
->withIsReleased(false) | |||||
->execute(); | |||||
foreach ($artifacts as $artifact) { | |||||
$artifact->releaseArtifact(); | |||||
} | |||||
} | |||||
private function releaseQueuedArtifacts() { | private function releaseQueuedArtifacts() { | ||||
foreach ($this->artifactReleaseQueue as $key => $artifact) { | foreach ($this->artifactReleaseQueue as $key => $artifact) { | ||||
$artifact->releaseArtifact(); | $artifact->releaseArtifact(); | ||||
unset($this->artifactReleaseQueue[$key]); | unset($this->artifactReleaseQueue[$key]); | ||||
} | } | ||||
} | } | ||||
} | } |