Mailversand-Uebersprung wird am Lauf sichtbar, nicht nur im Log

Log::warning allein reicht nicht: bis der Mailserver steht, trifft dieser
Zweig auf JEDE einzelne Bestellung zu, und die einzige Rueckmeldung darf
nicht in einer Logdatei verschwinden, in die niemand schaut.
RegisterMonitoring macht sein Ueberspringen genau deshalb am Lauf sichtbar
(outcome: info) statt nur im Log — ConfigureInstanceMail bekommt jetzt
dieselbe Behandlung, das Log bleibt daneben fuer die Nachschau ausserhalb
der Konsole.

Zwei weitere Befunde aus der Pruefung behoben:
- Der Test "traegt jeden Wert einzeln in den Gast" prueft jetzt alle acht
  Schluessel aus GuestMailConfig::values() statt zwei — ein vergessener
  Schluessel faellt jetzt hier auf, nicht erst beim Kunden.
- `$config->problem() ?? 'mail_unavailable'` entfernt: im Zweig
  `! $config->available()` liefert problem() immer einen Grund, der
  Rueckfall konnte nie greifen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
claude/nice-moser-521659
nexxo 2026-08-03 20:19:01 +02:00
parent 3930cf12ae
commit b92176ee59
2 changed files with 56 additions and 14 deletions

View File

@ -28,13 +28,20 @@ use Illuminate\Support\Facades\Log;
* in der PFLICHT-Pipeline `customer`, und ein Kunde, der bezahlt hat, bekommt
* seine Cloud auch dann, wenn der Mailversand noch nicht eingerichtet ist
* genau wie RegisterMonitoring die Bereitstellung nie an einer nicht
* erreichbaren Ueberwachung scheitern laesst (siehe deren Kopfkommentar).
* Mailversand ist hier dieselbe Nebenfunktion: fehlt er, funktioniert die
* Cloud vollstaendig, nur Einladungen und Freigabe-Benachrichtigungen gehen
* nicht. Der Schritt geht in diesem Fall mit `advance()` weiter und
* protokolliert den Grund per `Log::warning`, damit der Betreiber es findet;
* nachgeholt wird es ueber `clupilot:configure-instance-mail` (Aufgabe 4),
* sobald Server und Postfach stehen.
* erreichbaren Ueberwachung scheitern laesst (siehe deren Kopfkommentar). Der
* Schritt geht in diesem Fall mit `advance()` weiter; nachgeholt wird es ueber
* `clupilot:configure-instance-mail` (Aufgabe 4), sobald Server und Postfach
* stehen.
*
* Die Rueckmeldung dazu geht an ZWEI Stellen, nicht nur einer genau wie bei
* RegisterMonitoring. Ein Lauf-Ereignis (`outcome: info`) haengt sich an den
* Lauf, den der Betreiber in der Konsole sieht; `Log::warning` bleibt daneben
* fuer die Nachschau ausserhalb der Konsole. Das Lauf-Ereignis ist hier nicht
* optional: solange der Mailserver noch nicht steht, tritt dieser Zweig bei
* JEDER einzelnen Bestellung ein, und die einzige Rueckmeldung darf dann
* nicht in einer Logdatei verschwinden, in die niemand schaut. Mailversand
* ist eine Nebenfunktion: fehlt er, funktioniert die Cloud vollstaendig, nur
* Einladungen und Freigabe-Benachrichtigungen gehen nicht.
*
* Nicht eingerichtet ist etwas anderes als kaputt: sobald Server und Postfach
* da sind, wird geschrieben, und ein Fehler beim Schreiben selbst (occ liefert
@ -90,11 +97,20 @@ class ConfigureInstanceMail extends CustomerStep
if (! $config->available()) {
// Nicht eingerichtet, nicht kaputt — siehe Kopfkommentar. Die Cloud
// wird trotzdem ausgeliefert; der Grund geht ins Log, nicht in
// einen gescheiterten Lauf.
// wird trotzdem ausgeliefert. available() === false heisst hier
// immer, dass problem() einen Grund liefert (siehe GuestMailConfig)
// — kein Rueckfall auf einen Fall, der nicht eintreten kann.
$grund = $config->problem();
$run->events()->create([
'step' => $this->key(),
'attempt' => $run->attempt,
'outcome' => 'info',
'message' => 'Mailversand uebersprungen (nicht eingerichtet): '.$grund,
]);
Log::warning('Mailversand fuer Instanz uebersprungen (nicht eingerichtet)', [
'instance' => $instance->uuid,
'reason' => $config->problem() ?? 'mail_unavailable',
'reason' => $grund,
]);
return StepResult::advance();

View File

@ -55,10 +55,16 @@ it('traegt jeden Wert einzeln in den Gast', function () {
$befehle = implode("\n", $pve->guestCommands);
expect($befehle)->toContain('config:system:set mail_smtphost --value=')
->and($befehle)->toContain('mail.clupilot.cloud')
->and($befehle)->toContain('config:system:set mail_domain --value=')
->and($befehle)->toContain('clupilot.cloud');
// Alle acht Werte aus GuestMailConfig::values(), nicht nur zwei — ein
// vergessener Schluessel soll hier auffallen, nicht erst beim Kunden.
expect($befehle)->toContain('config:system:set mail_smtpmode --value='.escapeshellarg('smtp'))
->and($befehle)->toContain('config:system:set mail_smtphost --value='.escapeshellarg('mail.clupilot.cloud'))
->and($befehle)->toContain('config:system:set mail_smtpport --value='.escapeshellarg('587'))
->and($befehle)->toContain('config:system:set mail_smtpsecure --value='.escapeshellarg('tls'))
->and($befehle)->toContain('config:system:set mail_smtpauth --value='.escapeshellarg('true'))
->and($befehle)->toContain('config:system:set mail_smtpname --value='.escapeshellarg('noreply@clupilot.cloud'))
->and($befehle)->toContain('config:system:set mail_from_address --value='.escapeshellarg('noreply'))
->and($befehle)->toContain('config:system:set mail_domain --value='.escapeshellarg('clupilot.cloud'));
});
it('maskiert das Passwort, sodass es keinen zweiten Befehl starten kann', function () {
@ -111,6 +117,26 @@ it('schreibt GAR NICHTS, wenn der Mailserver fehlt', function () {
);
});
it('haengt ein Lauf-Ereignis an, das der Betreiber in der Konsole sieht', function () {
// Log::warning allein reicht hier nicht: bis der Mailserver steht, trifft
// dieser Zweig auf JEDE Bestellung zu, und die einzige Rueckmeldung darf
// nicht in einer Logdatei verschwinden, in die niemand schaut —
// RegisterMonitoring macht sein Ueberspringen genauso am Lauf sichtbar.
Settings::set('mail.host', '');
$pve = new FakeProxmoxClient;
app()->instance(ProxmoxClient::class, $pve);
$run = laufMitInstanz(Instance::factory()->create());
app(ConfigureInstanceMail::class)->execute($run);
$ereignis = $run->events()->first();
expect($ereignis)->not->toBeNull()
->and($ereignis->step)->toBe('configure_instance_mail')
->and($ereignis->outcome)->toBe('info')
->and($ereignis->message)->toContain('no_server');
});
it('schreibt beim zweiten Lauf erneut, statt sich mit einem Merker zu sperren', function () {
versandbereitFuerSchritt();
$pve = new FakeProxmoxClient;