Skip to content

Commit

Permalink
Merge pull request #66 from ByteInternet/cleanup_by_labels
Browse files Browse the repository at this point in the history
  • Loading branch information
tdgroot authored Dec 5, 2022
2 parents 823819a + 37f6d78 commit c9d7444
Show file tree
Hide file tree
Showing 9 changed files with 220 additions and 122 deletions.
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

0 comments on commit c9d7444

Please sign in to comment.