132 lines
4.3 KiB
PHP
132 lines
4.3 KiB
PHP
<?php
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use App\Models\Instance;
|
|
use App\Models\ProvisioningRun;
|
|
use App\Provisioning\Jobs\AdvanceRunJob;
|
|
use Illuminate\Console\Command;
|
|
|
|
/**
|
|
* Traegt bestehenden Instanzen den Mailversand nach.
|
|
*
|
|
* Der Anlass: der Versand kam erst mit der Mitarbeiterverwaltung in die
|
|
* Pipeline. Jede vorher gebaute Instanz kann bis heute keine Mail verschicken
|
|
* — und die Einladung an einen Mitarbeiter geht von DORT aus, nicht von
|
|
* CluPilot. Ohne diesen Lauf bliebe die Verwaltung fuer alle Altkunden tot.
|
|
*
|
|
* Ein Befehl und kein Zeitplan, aus derselben Begruendung wie bei
|
|
* `clupilot:refresh-host-firewall`: das Loch ist endlich und schliesst sich
|
|
* endgueltig; ein naechtlicher Lauf wuerde am Tag, an dem der Schritt in der
|
|
* Pipeline still kaputtginge, fuer ihn einspringen und das Versagen
|
|
* verbergen; und eine Reparatur, die der Betreiber anstoesst, ist eine, deren
|
|
* Ausgabe er liest.
|
|
*/
|
|
class ConfigureInstanceMail extends Command
|
|
{
|
|
protected $signature = 'clupilot:configure-instance-mail
|
|
{--dry-run : auflisten, was liefe, und nichts anfassen}
|
|
{--instance= : nur diese eine Instanz, per uuid}';
|
|
|
|
protected $description = 'Traegt bestehenden Kundeninstanzen den Mailversand nach';
|
|
|
|
public function handle(): int
|
|
{
|
|
$dryRun = (bool) $this->option('dry-run');
|
|
$started = 0;
|
|
|
|
/** @var array<string, int> reason => count */
|
|
$skipped = [];
|
|
|
|
$query = Instance::query()->where('status', 'active');
|
|
|
|
if ($uuid = $this->option('instance')) {
|
|
$query->where('uuid', $uuid);
|
|
}
|
|
|
|
foreach ($query->with('host')->get() as $instance) {
|
|
$reason = $this->reasonToSkip($instance);
|
|
|
|
if ($reason !== null) {
|
|
$skipped[$reason] = ($skipped[$reason] ?? 0) + 1;
|
|
|
|
continue;
|
|
}
|
|
|
|
$started++;
|
|
|
|
if ($dryRun) {
|
|
$this->line("{$instance->uuid}: würde nachgetragen");
|
|
|
|
continue;
|
|
}
|
|
|
|
$run = ProvisioningRun::create([
|
|
'pipeline' => 'instance-mail',
|
|
'subject_type' => Instance::class,
|
|
'subject_id' => $instance->id,
|
|
'status' => ProvisioningRun::STATUS_PENDING,
|
|
'current_step' => 0,
|
|
'context' => [
|
|
'instance_id' => $instance->id,
|
|
'node' => $instance->host->node ?? 'pve',
|
|
'vmid' => (int) $instance->vmid,
|
|
],
|
|
]);
|
|
|
|
AdvanceRunJob::dispatch($run->uuid);
|
|
$this->info("{$instance->uuid}: Lauf {$run->id} gestartet");
|
|
$started++;
|
|
}
|
|
|
|
foreach ($skipped as $reason => $count) {
|
|
$this->line("übersprungen ({$reason}): {$count}");
|
|
}
|
|
|
|
$this->info($dryRun
|
|
? "Probelauf: {$started} Instanz(en) bekämen den Mailversand, ".array_sum($skipped).' übersprungen. Nichts wurde geändert.'
|
|
: "{$started} Lauf/Läufe gestartet, ".array_sum($skipped).' übersprungen.');
|
|
|
|
return self::SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* Warum diese Instanz uebergangen wird, oder null.
|
|
*
|
|
* Der Grund IST der Bericht: „2 uebergangen" allein ist keine Auskunft,
|
|
* mit der jemand etwas anfangen kann — „2 haben keinen Host" und „2 laufen
|
|
* bereits" sehen von hier aus gleich aus, sind aber völlig verschiedene
|
|
* Reparaturen.
|
|
*/
|
|
private function reasonToSkip(Instance $instance): ?string
|
|
{
|
|
if ($instance->host === null) {
|
|
return 'kein Host';
|
|
}
|
|
|
|
if (blank($instance->vmid)) {
|
|
return 'keine VMID';
|
|
}
|
|
|
|
if ($this->hasRunInFlight($instance)) {
|
|
return 'anderer Lauf aktiv';
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* JEDER Lauf gegen diese Instanz, absichtlich weit gefasst: das hier ist ein
|
|
* Reparatur-Durchgang. Er läuft wieder, und eine heute übergangene Instanz ist
|
|
* beim nächsten Aufruf dran. Nichts geht durch Warten verloren.
|
|
*/
|
|
private function hasRunInFlight(Instance $instance): bool
|
|
{
|
|
return ProvisioningRun::query()
|
|
->where('subject_type', Instance::class)
|
|
->where('subject_id', $instance->id)
|
|
->inFlight()
|
|
->exists();
|
|
}
|
|
}
|