Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion ci/test/magento/deploy_brancher.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
]);

$productionStage = $configuration->addStage('test', 'banaan.store');
$productionStage->addBrancherServer('hndeployintegr8');
$productionStage->addBrancherServer('hndeployintegr8')
->setLabels(['gitref='.\getenv('GITHUB_SHA') ?: 'unknown']);

return $configuration;
23 changes: 23 additions & 0 deletions ci/test/run-brancher.sh
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,26 @@ $DP jq .brancher_hypernodes[0] deployment-report.json -r -e

# cleanup data
$DP hypernode-deploy cleanup -vvv

rm -f deployment-report.json

# Now do a test deploy again to deploy to a brancher node and clean it up by hnapi and labels matching
$DP hypernode-deploy deploy test -f /web/deploy.php -vvv

$DP ls -l
$DP test -f deployment-report.json
$DP jq . deployment-report.json
$DP jq .version deployment-report.json -r -e
$DP jq .stage deployment-report.json -r -e
$DP jq .hostnames[0] deployment-report.json -r -e
$DP jq .brancher_hypernodes[0] deployment-report.json -r -e

# Remove deployment report to make sure we can clean up using hnapi and labels matching
BRANCHER_INSTANCE=$($DP jq .brancher_hypernodes[0] deployment-report.json -r -e)
$DP rm -f deployment-report.json

# cleanup data
$DP hypernode-deploy cleanup test -vvv | tee cleanup.log

# Run tests on cleanup
grep "Stopping brancher Hypernode ${BRANCHER_INSTANCE}..." cleanup.log
12 changes: 6 additions & 6 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

36 changes: 35 additions & 1 deletion src/Brancher/BrancherHypernodeManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,40 @@ public function __construct(LoggerInterface $log)
$this->hypernodeClient = HypernodeClientFactory::create(getenv('HYPERNODE_API_TOKEN') ?: '');
}

/**
* Query brancher instances for given Hypernode and return the Brancher instance names.
*
* @param string $hypernode The parent hypernode to query the Brancher instances from
* @param string[] $labels Labels to match against, may be empty
* @return string[] The found Brancher instance names
*/
public function queryBrancherHypernodes(string $hypernode, array $labels = []): array
{
$result = [];

$hypernodes = $this->hypernodeClient->app->getList([
'parent' => $hypernode,
'type' => 'brancher',
'destroyed' => 'False',
]);
foreach ($hypernodes as $brancher) {
$match = true;

foreach ($labels as $label) {
if (!in_array($label, $brancher->labels)) {
$match = false;
break;
}
}

if ($match) {
$result[] = $brancher->name;
}
}

return $result;
}

/**
* Create brancher Hypernode instance for given Hypernode.
*
Expand Down Expand Up @@ -82,7 +116,7 @@ public function waitForAvailability(string $brancherHypernode, int $timeout = 90
} elseif ($timeElapsed < $allowedErrorWindow) {
// Sometimes we get an error where the logbook is not yet available, but it will be soon.
// We allow a small window for this to happen, and then we throw an exception.
sprintf(
printf(
'Got an expected exception during the allowed error window of HTTP code %d, waiting for %s to become available',
$e->getCode(),
$brancherHypernode
Expand Down
60 changes: 54 additions & 6 deletions src/Command/Cleanup.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,36 @@
namespace Hypernode\Deploy\Command;

use Hypernode\Deploy\Brancher\BrancherHypernodeManager;
use Hypernode\Deploy\ConfigurationLoader;
use Hypernode\Deploy\DeployerLoader;
use Hypernode\Deploy\Report\ReportLoader;
use Hypernode\DeployConfiguration\BrancherServer;
use Hypernode\DeployConfiguration\Configuration;
use Hypernode\DeployConfiguration\Server;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Throwable;

class Cleanup extends Command
{
private ReportLoader $reportLoader;
private DeployerLoader $deployerLoader;
private ConfigurationLoader $configurationLoader;
private BrancherHypernodeManager $brancherHypernodeManager;

public function __construct(ReportLoader $reportLoader, BrancherHypernodeManager $brancherHypernodeManager)
{
public function __construct(
ReportLoader $reportLoader,
DeployerLoader $deployerLoader,
ConfigurationLoader $configurationLoader,
BrancherHypernodeManager $brancherHypernodeManager
) {
parent::__construct();

$this->reportLoader = $reportLoader;
$this->deployerLoader = $deployerLoader;
$this->configurationLoader = $configurationLoader;
$this->brancherHypernodeManager = $brancherHypernodeManager;
}

Expand All @@ -31,6 +45,7 @@ protected function configure()
$this->setDescription(
'Clean up any acquired resources during the deployment, like brancher Hypernodes.'
);
$this->addArgument('stage', InputArgument::OPTIONAL, 'Stage to cleanup');
}

/**
Expand All @@ -40,13 +55,46 @@ protected function execute(InputInterface $input, OutputInterface $output)
{
$report = $this->reportLoader->loadReport();

if ($report === null) {
$output->writeln('No report found, skipping cleanup.');
return 0;
if ($report) {
$this->brancherHypernodeManager->cancel(...$report->getBrancherHypernodes());
}

$this->brancherHypernodeManager->cancel(...$report->getBrancherHypernodes());
/** @var string $stageName */
$stageName = $input->getArgument('stage');
if ($stageName) {
$this->deployerLoader->getOrCreateInstance($output);
$config = $this->configurationLoader->load($input->getOption('file') ?: 'deploy.php');
$this->cancelByStage($stageName, $config);
}

return 0;
}

/**
* Cancel brancher nodes by stage and their configured labels.
*
* @param string $stageName Stage to clean up
* @param Configuration $config Deployment configuration to read stages/servers from
* @return void
*/
private function cancelByStage(string $stageName, Configuration $config): void
{
foreach ($config->getStages() as $stage) {
if ($stage->getName() !== $stageName) {
continue;
}
foreach ($stage->getServers() as $server) {
if (!($server instanceof BrancherServer)) {
continue;
}
$labels = $server->getLabels();
$hypernode = $server->getOptions()[Server::OPTION_HN_PARENT_APP];
$brancherHypernodes = $this->brancherHypernodeManager->queryBrancherHypernodes(
$hypernode,
$labels
);
$this->brancherHypernodeManager->cancel(...$brancherHypernodes);
}
}
}
}
13 changes: 8 additions & 5 deletions src/Command/Deploy.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,7 @@

class Deploy extends Command
{
/**
* @var DeployRunner
*/
private $deployRunner;
private DeployRunner $deployRunner;
private ReportWriter $reportWriter;

public function __construct(DeployRunner $deployRunner, ReportWriter $reportWriter)
Expand All @@ -38,7 +35,13 @@ protected function configure()
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$result = $this->deployRunner->run($output, $input->getArgument('stage'), DeployRunner::TASK_DEPLOY, false, true);
$result = $this->deployRunner->run(
$output,
$input->getArgument('stage'),
DeployRunner::TASK_DEPLOY,
false,
true
);

if ($result === 0) {
$this->reportWriter->write($this->deployRunner->getDeploymentReport());
Expand Down
38 changes: 38 additions & 0 deletions src/ConfigurationLoader.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);

namespace Hypernode\Deploy;

use Deployer\Deployer;
use Deployer\Exception\Exception;
use Deployer\Exception\GracefulShutdownException;
use Hypernode\DeployConfiguration\Configuration;
use Throwable;

class ConfigurationLoader
{
/**
* @throws Exception
* @throws GracefulShutdownException
* @throws Throwable
*/
public function load(string $file): Configuration
{
if (!is_readable($file)) {
throw new \RuntimeException(sprintf('No %s file found in project root %s', $file, getcwd()));
}

$configuration = \call_user_func(function () use ($file) {
return require $file;
});

if (!$configuration instanceof Configuration) {
throw new \RuntimeException(
sprintf('%s/deploy.php did not return object of type %s', getcwd(), Configuration::class)
);
}

return $configuration;
}
}
Loading