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));
}
$refuseReason = $upd->isRefused();
if (false !== $refuseReason) {
if ($this->opt->verbosity) {
$this->console->notice('%s is ignored. %s', $upd->getName(), (string) $refuseReason);
}
return true;
}
if ($upd->getStatus() == AbstractUpgradeEntity::STATUS_OK) {
//Upgrade file is updated.
$upd->updateAppears();
if (isset($this->opt->cmd) && $this->opt->cmd == self::CMD_RUN_SPECIFIC && $this->opt->uuid == $upd->getUuidHex()) {
//User has requested re-execution of update
$upd->setStatus(AbstractUpgradeEntity::STATUS_PENDING);
$upd->updateHash();
$upd->getEntity()->save();
} else {
//Compare checksum
if ($upd->getEntity()->hash == $upd->getHash()) {
//file modified time could be the issue
$upd->updateApplied();
$upd->getEntity()->save();
if (!empty($this->opt->verbosity) || isset($this->opt->cmd) && $this->opt->cmd == self::CMD_RUN_SPECIFIC && $this->opt->uuid == $upd->getUuidHex()) {
$this->console->warning('Ingnoring %s because of having complete status.', $upd->getName());
}
return true;
} else {
//We should ignore changes in the script and update hash
$upd->updateHash();
$upd->getEntity()->save();
return true;
}
}
}
$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 ($upgradeEntity->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->error('Error. Stage %d of update %s could not be applied because of invalid environment! validateBefore(%d) returned false.', $stage, $upd->getName(), $stage);
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('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;
}