/** * Runs upgrade process * * @return bool Returns true if all updates completed successfully, false otherwise */ public function run() { if (!self::checkPid()) { $this->console->warning("Cannot start a new process because another one has already been started."); return false; } register_shutdown_function('Scalr\\Upgrade\\UpgradeHandler::removePid'); //Loads updates $successful = $this->loadUpdates(); if (isset($this->opt->cmd) && $this->opt->cmd == self::CMD_RUN_SPECIFIC) { $pending = []; if (!isset($this->updates[$this->opt->uuid])) { $this->console->warning("Could not find specified update %s", $this->opt->uuid); exit(1); } $pending[] = $this->updates[$this->opt->uuid]; } else { $dt = new \DateTime($this->getLastDate(), new \DateTimeZone('UTC')); $pending = $this->updates->getPendingUpdates($dt->getTimestamp()); } if (count($pending) == 0) { $this->console->out('Scalr is up-to-date'); return $successful; } $this->console->success('Starting Scalr upgrade'); //Applies updates foreach ($pending as $update) { $update->console->interactive = $this->console->interactive; $successful = $this->applyUpdate($update) && $successful; } $this->console->success('Done'); return $successful; }
} if (isset($opt['n']) || isset($opt['new'])) { $template = UpgradeHandler::getPathToUpdates() . '/Template.php'; if (!is_readable($template)) { $console->error('Could not open template file for reading ' . $template); exit; } $released = gmdate('YmdHis'); $pathname = UpgradeHandler::getPathToUpdates() . '/Update' . $released . '.php'; $tpl = PhpTemplate::load($template, array('upd_released' => $released, 'upd_uuid' => \Scalr::GenerateUID())); if ($console->confirm("Are you sure you want to create a new upgrade class?")) { if (file_put_contents($pathname, $tpl) === false) { $console->error('Could not write to file "%s"', $pathname); exit; } $console->success('Upgrade class "%s" has been successfully created.', realpath($pathname)); } exit; } if (isset($opt['force'])) { UpgradeHandler::removePid(); } $upgrade = new UpgradeHandler($options); if (!$upgrade->run()) { exit(1); } $scalrInfo = Scalr::getContainer()->version(); try { Scalr::getDb()->Execute("\n REPLACE `scalr_hosts`\n SET `host` = ?, `version` = ?, `edition` = ?, `git_commit` = ?, `git_commit_added` = ?\n ", [php_uname("n"), $scalrInfo['version'], $scalrInfo['edition'], empty($scalrInfo['gitRevision']) ? null : $scalrInfo['gitRevision'], empty($scalrInfo['gitDate']) || ($gts = strtotime($scalrInfo['gitDate'])) === false ? null : date("Y-m-d H:i:s", $gts)]); } catch (Exception $e) { Scalr::logException($e);
/** * Applies update * * @param AbstractUpdate $upd Update to apply */ protected function applyUpdate(AbstractUpdate $upd) { if (!isset($this->attempts[$upd->getUuidHex()])) { $this->attempts[$upd->getUuidHex()] = 1; } else { $this->attempts[$upd->getUuidHex()]++; } if ($this->attempts[$upd->getUuidHex()] > self::MAX_ATTEMPTS) { throw new Exception\UpgradeException(sprintf('"%s" Failed due to infinity loop. Max number of attempts (%d) reached!', $upd->getName(), self::MAX_ATTEMPTS)); } if ($upd->getStatus() == AbstractUpgradeEntity::STATUS_OK) { //Upgrade file is updated. $upd->updateAppears(); //Compare checksum if ($upd->getEntity()->hash == $upd->getHash()) { if (isset($this->opt->cmd) && $this->opt->cmd == self::CMD_RUN_SPECIFIC && $this->opt->uuid == $upd->getUuidHex()) { $this->console->warning('Nothing to do. %s has complete status.', $upd->getName()); } return true; } else { //Update script has been changed and needs to be re-executed $upd->setStatus(AbstractUpgradeEntity::STATUS_PENDING); $upd->updateHash(); $upd->getEntity()->save(); } } $this->console->success('%s...', $upd->description ?: $upd->getName()); //Checks updates this upgrade depends upon if (!empty($upd->depends)) { foreach ($upd->depends as $uuid) { $uuidhex = AbstractUpdate::castUuid($uuid); if (!empty($this->updates[$uuidhex])) { $update = $this->updates[$uuidhex]; if ($update->getStatus() == AbstractUpgradeEntity::STATUS_OK) { //Relative update has already been successfully applied. continue; } } else { if (isset($this->stateBefore[$uuidhex])) { /* @var $upgradeEntity \Scalr\Upgrade\Entity\AbstractUpgradeEntity */ $upgradeEntity = $this->stateBefore[$uuidhex]; if ($updateEntity->status == AbstractUpgradeEntity::STATUS_OK) { //Relative update has already been applied continue; } else { //Relative update needs to be applied before dependant. $this->console->warning('"%s" has been declined as it depends on incomplete update "%s" which has status "%s". ' . 'Desired class "%s" does not exist in the expected folder.', $upd->getName(), $uuid, $upgradeEntity->getStatusName(), $upgradeEntity->getUpdateClassName()); return false; } } else { //Relative update has not been delivered yet. $this->console->warning('"%s" has been postponed as it depends on "%s" which has not been delivered yet.', $upd->getName(), $uuid); return false; } } if ($update->getStatus() == AbstractUpgradeEntity::STATUS_FAILED && isset($this->recurrences[$update->getUuidHex()])) { //Recurrence of the failed status. We don't need to report about it again. $this->console->warning('"%s" has been declined because of failure dependent update "%s".', $upd->getName(), $uuid); return false; } //Relative update has not been applied or it has incomplete status. //We need to apply it first. if ($this->applyUpdate($update) === false) { $this->console->warning('"%s" has been declined. Could not apply related update "%s".', $upd->getName(), $update->getName()); return false; } } } //Checks if update class implements SequenceInterface $stages = $upd instanceof SequenceInterface ? range(1, $upd->getNumberStages()) : array(1); $skip = 0; foreach ($stages as $stage) { //Checks if update is applied if ($upd->isApplied($stage)) { $upd->console->warning('Skips over the stage %d of update %s because it has already been applied.', $stage, $upd->getName()); $skip++; continue; } //Validates environment before applying if (!$upd->validateBefore($stage)) { $this->console->warning('Stage %d of update %s could not be applied because of invalid environment!', $stage, $upd->getName()); return false; } //Applies the update try { $upd->run($stage); } catch (\Exception $e) { //We should avoid repetition when another update depends on failed. $this->recurrences[$upd->getUuidHex()] = true; $upd->setStatus(AbstractUpgradeEntity::STATUS_FAILED); $upd->console->error('Stage %d of update %s failed! %s', $stage, $upd->getName(), $e->getMessage()); $upd->getEntity()->save(); $upd->getEntity()->createFailureMessage($upd->console->getLog()); return false; } } $this->console->success("%s - OK", $upd->description ?: $upd->getName()); $upd->setStatus(AbstractUpgradeEntity::STATUS_OK); $upd->updateHash(); $upd->updateApplied(); $upd->getEntity()->save(); return true; }