icingaweb2-module-director/library/Director/Deployment/ConditionalDeployment.php

191 lines
5.2 KiB
PHP

<?php
namespace Icinga\Module\Director\Deployment;
use Icinga\Exception\IcingaException;
use Icinga\Module\Director\Core\CoreApi;
use Icinga\Module\Director\Db;
use Icinga\Module\Director\IcingaConfig\IcingaConfig;
use Icinga\Module\Director\Objects\DirectorDeploymentLog;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Psr\Log\NullLogger;
class ConditionalDeployment implements LoggerAwareInterface
{
use LoggerAwareTrait;
/** @var Db */
protected $db;
/** @var CoreApi */
protected $api;
/** @var ?DeploymentGracePeriod */
protected $gracePeriod = null;
protected $force = false;
protected $hasBeenForced = false;
/** @var ?string */
protected $noDeploymentReason = null;
public function __construct(Db $connection, CoreApi $api = null)
{
$this->setLogger(new NullLogger());
$this->db = $connection;
if ($api === null) {
$this->api = $connection->getDeploymentEndpoint()->api();
} else {
$this->api = $api;
}
$this->refresh();
}
/**
* @param IcingaConfig $config
* @return ?DirectorDeploymentLog
*/
public function deploy(IcingaConfig $config)
{
$this->hasBeenForced = false;
if ($this->shouldDeploy($config)) {
return $this->reallyDeploy($config);
} elseif ($this->force) {
$deployment = $this->reallyDeploy($config);
$this->hasBeenForced = true;
return $deployment;
}
return null;
}
/**
* @param bool $force
* @return $this
*/
public function force($force = true)
{
$this->force = $force;
return $this;
}
public function setGracePeriod(DeploymentGracePeriod $gracePeriod)
{
$this->gracePeriod = $gracePeriod;
return $this;
}
public function refresh()
{
$this->api->collectLogFiles($this->db);
$this->api->wipeInactiveStages($this->db);
}
public function waitForStartupAfterDeploy(DirectorDeploymentLog $deploymentLog, $timeout)
{
$startTime = time();
while ((time() - $startTime) <= $timeout) {
$deploymentFromDB = DirectorDeploymentLog::load($deploymentLog->getId(), $this->db);
$stageCollected = $deploymentFromDB->get('stage_collected');
if ($stageCollected === null) {
usleep(500000);
continue;
}
if ($stageCollected === 'n') {
return 'stage has not been collected (Icinga "lost" the deployment)';
}
if ($deploymentFromDB->get('startup_succeeded') === 'y') {
return true;
}
return 'deployment failed during startup (usually a Configuration Error)';
}
return 'deployment timed out (while waiting for an Icinga restart)';
}
/**
* @return string|null
*/
public function getNoDeploymentReason()
{
return $this->noDeploymentReason;
}
public function hasBeenForced()
{
return $this->hasBeenForced;
}
protected function shouldDeploy(IcingaConfig $config)
{
$this->noDeploymentReason = null;
if ($this->hasNeverDeployed()) {
return true;
}
if ($this->isWithinGracePeriod()) {
$this->noDeploymentReason = 'Grace period is active';
return false;
}
if ($this->deployedConfigMatches($config)) {
$this->noDeploymentReason = 'Config matches last deployed one';
return false;
}
if ($this->getActiveChecksum() === $config->getHexChecksum()) {
$this->noDeploymentReason = 'Config matches active stage';
return false;
}
return true;
}
protected function hasNeverDeployed()
{
return !DirectorDeploymentLog::hasDeployments($this->db);
}
protected function isWithinGracePeriod()
{
return $this->gracePeriod && $this->gracePeriod->isActive();
}
protected function deployedConfigMatches(IcingaConfig $config)
{
if ($deployment = DirectorDeploymentLog::optionalLatest($this->db)) {
return $deployment->getConfigHexChecksum() === $config->getHexChecksum();
}
return false;
}
protected function getActiveChecksum()
{
return DirectorDeploymentLog::getConfigChecksumForStageName(
$this->db,
$this->api->getActiveStageName()
);
}
/**
* @param IcingaConfig $config
* @return bool|DirectorDeploymentLog
* @throws IcingaException
* @throws \Icinga\Module\Director\Exception\DuplicateKeyException
*/
protected function reallyDeploy(IcingaConfig $config)
{
$checksum = $config->getHexChecksum();
$this->logger->info(sprintf('Director ConfigJob ready to deploy "%s"', $checksum));
if ($deployment = $this->api->dumpConfig($config, $this->db)) {
$this->logger->notice(sprintf('Director ConfigJob deployed config "%s"', $checksum));
return $deployment;
} else {
throw new IcingaException('Failed to deploy config "%s"', $checksum);
}
}
}