diff --git a/src/applications/harbormaster/constants/HarbormasterBuildStatus.php b/src/applications/harbormaster/constants/HarbormasterBuildStatus.php --- a/src/applications/harbormaster/constants/HarbormasterBuildStatus.php +++ b/src/applications/harbormaster/constants/HarbormasterBuildStatus.php @@ -12,6 +12,11 @@ const STATUS_PAUSED = 'paused'; const STATUS_DEADLOCKED = 'deadlocked'; + const PENDING_PAUSING = 'x-pausing'; + const PENDING_RESUMING = 'x-resuming'; + const PENDING_RESTARTING = 'x-restarting'; + const PENDING_ABORTING = 'x-aborting'; + private $key; private $properties; @@ -48,6 +53,10 @@ return $this->getProperty('isComplete'); } + public function isPending() { + return $this->getProperty('isPending'); + } + public function isPassed() { return ($this->key === self::STATUS_PASSED); } @@ -56,6 +65,33 @@ return ($this->key === self::STATUS_FAILED); } + public function isAborting() { + return ($this->key === self::PENDING_ABORTING); + } + + public function isRestarting() { + return ($this->key === self::PENDING_RESTARTING); + } + + public function isResuming() { + return ($this->key === self::PENDING_RESUMING); + } + + public function isPausing() { + return ($this->key === self::PENDING_PAUSING); + } + + public function getIconIcon() { + return $this->getProperty('icon'); + } + + public function getIconColor() { + return $this->getProperty('color'); + } + + public function getName() { + return $this->getProperty('name'); + } /** * Get a human readable name for a build status constant. @@ -134,6 +170,7 @@ 'color.ansi' => 'yellow', 'isBuilding' => false, 'isComplete' => false, + 'isPending' => false, ), self::STATUS_PENDING => array( 'name' => pht('Pending'), @@ -142,6 +179,7 @@ 'color.ansi' => 'yellow', 'isBuilding' => true, 'isComplete' => false, + 'isPending' => false, ), self::STATUS_BUILDING => array( 'name' => pht('Building'), @@ -150,6 +188,7 @@ 'color.ansi' => 'yellow', 'isBuilding' => true, 'isComplete' => false, + 'isPending' => false, ), self::STATUS_PASSED => array( 'name' => pht('Passed'), @@ -158,6 +197,7 @@ 'color.ansi' => 'green', 'isBuilding' => false, 'isComplete' => true, + 'isPending' => false, ), self::STATUS_FAILED => array( 'name' => pht('Failed'), @@ -166,6 +206,7 @@ 'color.ansi' => 'red', 'isBuilding' => false, 'isComplete' => true, + 'isPending' => false, ), self::STATUS_ABORTED => array( 'name' => pht('Aborted'), @@ -174,6 +215,7 @@ 'color.ansi' => 'red', 'isBuilding' => false, 'isComplete' => true, + 'isPending' => false, ), self::STATUS_ERROR => array( 'name' => pht('Unexpected Error'), @@ -182,6 +224,7 @@ 'color.ansi' => 'red', 'isBuilding' => false, 'isComplete' => true, + 'isPending' => false, ), self::STATUS_PAUSED => array( 'name' => pht('Paused'), @@ -190,6 +233,7 @@ 'color.ansi' => 'yellow', 'isBuilding' => false, 'isComplete' => false, + 'isPending' => false, ), self::STATUS_DEADLOCKED => array( 'name' => pht('Deadlocked'), @@ -198,6 +242,43 @@ 'color.ansi' => 'red', 'isBuilding' => false, 'isComplete' => true, + 'isPending' => false, + ), + self::PENDING_PAUSING => array( + 'name' => pht('Pausing'), + 'icon' => 'fa-exclamation-triangle', + 'color' => 'red', + 'color.ansi' => 'red', + 'isBuilding' => false, + 'isComplete' => false, + 'isPending' => true, + ), + self::PENDING_RESUMING => array( + 'name' => pht('Resuming'), + 'icon' => 'fa-exclamation-triangle', + 'color' => 'red', + 'color.ansi' => 'red', + 'isBuilding' => false, + 'isComplete' => false, + 'isPending' => true, + ), + self::PENDING_RESTARTING => array( + 'name' => pht('Restarting'), + 'icon' => 'fa-exclamation-triangle', + 'color' => 'red', + 'color.ansi' => 'red', + 'isBuilding' => false, + 'isComplete' => false, + 'isPending' => true, + ), + self::PENDING_ABORTING => array( + 'name' => pht('Aborting'), + 'icon' => 'fa-exclamation-triangle', + 'color' => 'red', + 'color.ansi' => 'red', + 'isBuilding' => false, + 'isComplete' => false, + 'isPending' => true, ), ); } diff --git a/src/applications/harbormaster/controller/HarbormasterBuildViewController.php b/src/applications/harbormaster/controller/HarbormasterBuildViewController.php --- a/src/applications/harbormaster/controller/HarbormasterBuildViewController.php +++ b/src/applications/harbormaster/controller/HarbormasterBuildViewController.php @@ -32,21 +32,13 @@ ->setPolicyObject($build) ->setHeaderIcon('fa-cubes'); - $is_restarting = $build->isRestarting(); - - if ($is_restarting) { - $page_header->setStatus( - 'fa-exclamation-triangle', 'red', pht('Restarting')); - } else if ($build->isPausing()) { - $page_header->setStatus( - 'fa-exclamation-triangle', 'red', pht('Pausing')); - } else if ($build->isResuming()) { - $page_header->setStatus( - 'fa-exclamation-triangle', 'red', pht('Resuming')); - } else if ($build->isAborting()) { - $page_header->setStatus( - 'fa-exclamation-triangle', 'red', pht('Aborting')); - } + $status = $build->getBuildPendingStatusObject(); + + $status_icon = $status->getIconIcon(); + $status_color = $status->getIconColor(); + $status_name = $status->getName(); + + $page_header->setStatus($status_icon, $status_color, $status_name); $max_generation = (int)$build->getBuildGeneration(); if ($max_generation === 0) { @@ -55,7 +47,7 @@ $min_generation = 1; } - if ($is_restarting) { + if ($build->isRestarting()) { $max_generation = $max_generation + 1; } @@ -624,10 +616,6 @@ pht('Build Plan'), $handles[$build->getBuildPlanPHID()]->renderLink()); - $properties->addProperty( - pht('Status'), - $this->getStatus($build)); - return id(new PHUIObjectBoxView()) ->setHeaderText(pht('Properties')) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) @@ -681,31 +669,6 @@ ->setTable($table); } - - private function getStatus(HarbormasterBuild $build) { - $status_view = new PHUIStatusListView(); - - $item = new PHUIStatusItemView(); - - if ($build->isPausing()) { - $status_name = pht('Pausing'); - $icon = PHUIStatusItemView::ICON_RIGHT; - $color = 'dark'; - } else { - $status = $build->getBuildStatus(); - $status_name = - HarbormasterBuildStatus::getBuildStatusName($status); - $icon = HarbormasterBuildStatus::getBuildStatusIcon($status); - $color = HarbormasterBuildStatus::getBuildStatusColor($status); - } - - $item->setTarget($status_name); - $item->setIcon($icon, $color); - $status_view->addItem($item); - - return $status_view; - } - private function buildMessages(array $messages) { $viewer = $this->getRequest()->getUser(); diff --git a/src/applications/harbormaster/storage/build/HarbormasterBuild.php b/src/applications/harbormaster/storage/build/HarbormasterBuild.php --- a/src/applications/harbormaster/storage/build/HarbormasterBuild.php +++ b/src/applications/harbormaster/storage/build/HarbormasterBuild.php @@ -212,6 +212,64 @@ return "/harbormaster/build/{$id}/"; } + public function getBuildPendingStatusObject() { + + // NOTE: If a build has multiple unprocessed messages, we'll ignore + // messages that are obsoleted by a later or stronger message. + // + // For example, if a build has both "pause" and "abort" messages in queue, + // we just ignore the "pause" message and perform an "abort", since pausing + // first wouldn't affect the final state, so we can just skip it. + // + // Likewise, if a build has both "restart" and "abort" messages, the most + // recent message is controlling: we'll take whichever action a command + // was most recently issued for. + + $is_restarting = false; + $is_aborting = false; + $is_pausing = false; + $is_resuming = false; + + foreach ($this->getUnprocessedMessages() as $message_object) { + $message_type = $message_object->getType(); + switch ($message_type) { + case HarbormasterBuildCommand::COMMAND_RESTART: + $is_restarting = true; + $is_aborting = false; + break; + case HarbormasterBuildCommand::COMMAND_ABORT: + $is_aborting = true; + $is_restarting = false; + break; + case HarbormasterBuildCommand::COMMAND_PAUSE: + $is_pausing = true; + $is_resuming = false; + break; + case HarbormasterBuildCommand::COMMAND_RESUME: + $is_resuming = true; + $is_pausing = false; + break; + } + } + + $pending_status = null; + if ($is_restarting) { + $pending_status = HarbormasterBuildStatus::PENDING_RESTARTING; + } else if ($is_aborting) { + $pending_status = HarbormasterBuildStatus::PENDING_ABORTING; + } else if ($is_pausing) { + $pending_status = HarbormasterBuildStatus::PENDING_PAUSING; + } else if ($is_resuming) { + $pending_status = HarbormasterBuildStatus::PENDING_RESUMING; + } + + if ($pending_status !== null) { + return HarbormasterBuildStatus::newBuildStatusObject($pending_status); + } + + return $this->getBuildStatusObject(); + } + protected function getBuildStatusObject() { $status_key = $this->getBuildStatus(); return HarbormasterBuildStatus::newBuildStatusObject($status_key); @@ -309,7 +367,9 @@ return !$this->isComplete() && !$this->isPaused() && - !$this->isPausing(); + !$this->isPausing() && + !$this->isRestarting() && + !$this->isAborting(); } public function canAbortBuild() { @@ -317,7 +377,9 @@ return false; } - return !$this->isComplete(); + return + !$this->isComplete() && + !$this->isAborting(); } public function canResumeBuild() { @@ -325,78 +387,27 @@ return false; } - return $this->isPaused() && - !$this->isResuming(); + return + $this->isPaused() && + !$this->isResuming() && + !$this->isRestarting() && + !$this->isAborting(); } public function isPausing() { - $is_pausing = false; - foreach ($this->getUnprocessedMessages() as $message_object) { - $message_type = $message_object->getType(); - switch ($message_type) { - case HarbormasterBuildCommand::COMMAND_PAUSE: - $is_pausing = true; - break; - case HarbormasterBuildCommand::COMMAND_RESUME: - case HarbormasterBuildCommand::COMMAND_RESTART: - $is_pausing = false; - break; - case HarbormasterBuildCommand::COMMAND_ABORT: - $is_pausing = true; - break; - } - } - - return $is_pausing; + return $this->getBuildPendingStatusObject()->isPausing(); } public function isResuming() { - $is_resuming = false; - foreach ($this->getUnprocessedMessages() as $message_object) { - $message_type = $message_object->getType(); - switch ($message_type) { - case HarbormasterBuildCommand::COMMAND_RESTART: - case HarbormasterBuildCommand::COMMAND_RESUME: - $is_resuming = true; - break; - case HarbormasterBuildCommand::COMMAND_PAUSE: - $is_resuming = false; - break; - case HarbormasterBuildCommand::COMMAND_ABORT: - $is_resuming = false; - break; - } - } - - return $is_resuming; + return $this->getBuildPendingStatusObject()->isResuming(); } public function isRestarting() { - $is_restarting = false; - foreach ($this->getUnprocessedMessages() as $message_object) { - $message_type = $message_object->getType(); - switch ($message_type) { - case HarbormasterBuildCommand::COMMAND_RESTART: - $is_restarting = true; - break; - } - } - - return $is_restarting; + return $this->getBuildPendingStatusObject()->isRestarting(); } public function isAborting() { - $is_aborting = false; - foreach ($this->getUnprocessedMessages() as $message_object) { - $message_type = $message_object->getType(); - switch ($message_type) { - case HarbormasterBuildCommand::COMMAND_ABORT: - $is_aborting = true; - break; - } - } - - return $is_aborting; + return $this->getBuildPendingStatusObject()->isAborting(); } public function markUnprocessedMessagesAsProcessed() {