> "\$STUB_LOG" touch "\$STUB_STARTED" sleep "\${STUB_SLEEP:-1}" case "\$*" in *config*) printf '%s\\n' app redis ;; *"ps "*) printf '%s\\n' $running ;; *getent*) exit "\${STUB_REDIS_RC:-0}" ;; *storage/framework/down*) exit 1 ;; *" up "*) touch "\$STUB_UP_CALLED" ;; esac exit 0 SH); // Ein `git`, das sofort scheitert. Der Agent fängt das ab; dieser Test hat // keinen Grund, die Gegenstelle abzurufen. File::put($stub.'/git', "#!/bin/sh\nexit 1\n"); Process::run("chmod +x {$stub}/docker {$stub}/git"); return $stub; } afterEach(function () { File::deleteDirectory(storage_path('app/deploy')); }); it('lets the agent work while the watchdog is only looking', function () { // Der gemeldete Fall: nichts ist kaputt, der Wächter sieht nur nach — und // der Agent kommt trotzdem nicht an die Arbeit. $dir = deployDir(); $stub = dockerStub(); $result = Process::path(base_path()) ->timeout(90) ->env([ 'PATH' => $stub.':'.env('PATH', '/usr/local/bin:/usr/bin:/bin'), 'STUB_LOG' => $dir.'/.stub-log', 'STUB_STARTED' => $dir.'/.stub-started', 'STUB_UP_CALLED' => $dir.'/.stub-up-called', ]) ->run(<</dev/null 2>&1 & waechter=\$! # Nachweislich MITTEN im Lauf des Wächters, nicht davor und nicht # danach: erst wenn die Attrappe angefasst wurde, ist er drin. until [ -f "\$STUB_STARTED" ]; do sleep 0.05; done bash deploy/update-agent.sh >/dev/null 2>&1 || true kill \$waechter 2>/dev/null || true wait \$waechter 2>/dev/null || true BASH); expect($result->successful())->toBeTrue($result->errorOutput()); $alive = json_decode(File::get($dir.'/agent-alive.json'), true); expect($alive['state'])->toBe('running') ->and($alive['skips'])->toBe(0); }); it('keeps the watchdog away while an update holds the lock', function () { // Die andere Hälfte, und die teurere: der Wächter darf NICHT eingreifen, // während ein Update läuft. Er sieht hier einen fehlenden Dienst — also // etwas, das er sonst sofort richten würde — und muss die Hände trotzdem // stillhalten, weil die Sperre gehalten wird. $dir = deployDir(); $stub = dockerStub(running: 'redis'); $result = Process::path(base_path()) ->timeout(90) ->env([ 'PATH' => $stub.':'.env('PATH', '/usr/local/bin:/usr/bin:/bin'), 'STUB_LOG' => $dir.'/.stub-log', 'STUB_STARTED' => $dir.'/.stub-started', 'STUB_UP_CALLED' => $dir.'/.stub-up-called', ]) ->run(<< storage/app/deploy/.watchdog-out 2>&1 || true kill \$halter 2>/dev/null || true BASH); expect($result->successful())->toBeTrue($result->errorOutput()); expect(File::exists($dir.'/.stub-up-called')) ->toBeFalse('Der Waechter hat eingegriffen, obwohl die Sperre gehalten wurde.'); }); it('says in the journal that a run was skipped', function () { // Ein übersprungener Lauf sah im Journal aus wie ein erfolgreicher — der // Agent beendet sich sauber, systemd meldet Starting → Deactivated. Das // hat die Fehlersuche zweimal in die falsche Richtung geschickt. $dir = deployDir(); $result = Process::path(base_path()) ->timeout(60) ->run(<<<'BASH' flock storage/app/deploy/.agent.lock -c 'touch storage/app/deploy/.held; sleep 20' & halter=$! until [ -f storage/app/deploy/.held ]; do sleep 0.05; done bash deploy/update-agent.sh 2>&1 kill $halter 2>/dev/null || true BASH); // Die Zeile nennt den Halter mit: der Waechter fuer einen Lauf ist // Betrieb, derselbe Halter seit vierzig Laeufen ist es nicht — und genau // diese Unterscheidung war im Journal bisher nicht zu treffen. expect($result->output()) ->toContain('Uebersprungen') ->toContain('die Sperre haelt'); });