77 lines
2.7 KiB
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]);
|
|
}
|
|
}
|