CluPilotCloud/app/Provisioning/PipelineRegistry.php

77 lines
2.7 KiB
PHP

<?php
namespace App\Provisioning;
use App\Provisioning\Contracts\ProvisioningStep;
use InvalidArgumentException;
/**
* Maps a pipeline name (host | customer) to its ordered list of step classes.
*/
class PipelineRegistry
{
/** @param array<string, array<int, class-string<ProvisioningStep>>> $pipelines */
public function __construct(private array $pipelines) {}
/** @return array<int, class-string<ProvisioningStep>> */
public function stepsFor(string $pipeline): array
{
if (! isset($this->pipelines[$pipeline])) {
throw new InvalidArgumentException("Unknown pipeline [{$pipeline}].");
}
return $this->pipelines[$pipeline];
}
public function count(string $pipeline): int
{
return count($this->stepsFor($pipeline));
}
/**
* Will a run of `$pipeline` standing at `$fromStep` still carry out every
* step of `$work`?
*
* The question a caller about to start a `$work` run has to ask about the run
* already in flight, and the reason it is asked step by step rather than by
* pipeline name: `plan-change` contains the whole of `address` and the whole
* of `storage`, `quota` contains only the last third of `storage`, and
* `restart` contains neither. A name comparison would have to be kept in step
* with config/provisioning.php by hand; this reads the same lists the runner
* executes.
*
* EVERY step, not any: the three steps of a storage change are ordered
* because a quota Nextcloud enforces over a filesystem that never grew is a
* promise of space the machine does not have. A run that would apply the new
* quota and nothing else does not cover the work — it delivers half of it,
* which is worse than delivering none.
*
* Steps at `$fromStep` itself count as still to come. The step there is
* either about to run or running, and every step reads the machine's state
* when it gets there rather than when the run was created.
*/
public function stillCovers(string $pipeline, int $fromStep, string $work): bool
{
$remaining = array_slice($this->stepsFor($pipeline), max($fromStep, 0));
foreach ($this->stepsFor($work) as $step) {
if (! in_array($step, $remaining, true)) {
return false;
}
}
return true;
}
public function resolve(string $pipeline, int $index): ProvisioningStep
{
$steps = $this->stepsFor($pipeline);
if (! isset($steps[$index])) {
throw new InvalidArgumentException("No step at index {$index} for pipeline [{$pipeline}].");
}
return app($steps[$index]);
}
}