From da5a6814a7f21190c017b3f2b2431c7671dc12e8 Mon Sep 17 00:00:00 2001 From: Peter Elmered Date: Sun, 21 Jul 2024 23:59:52 +0200 Subject: [PATCH] Add support for checking backups on filesystem disks --- src/Checks/Checks/BackupsCheck.php | 32 +++++++++----- src/Support/BackupFile.php | 34 ++++++++++++++ tests/Checks/BackupsCheckTest.php | 71 ++++++++++++++++++++++++++++++ 3 files changed, 127 insertions(+), 10 deletions(-) create mode 100644 src/Support/BackupFile.php diff --git a/src/Checks/Checks/BackupsCheck.php b/src/Checks/Checks/BackupsCheck.php index 04e363fc..0a9cffa9 100644 --- a/src/Checks/Checks/BackupsCheck.php +++ b/src/Checks/Checks/BackupsCheck.php @@ -3,16 +3,21 @@ namespace Spatie\Health\Checks\Checks; use Carbon\Carbon; +use Illuminate\Contracts\Filesystem\Filesystem; use Illuminate\Support\Collection; use Illuminate\Support\Facades\File; +use Illuminate\Support\Facades\Storage; use Spatie\Health\Checks\Check; use Spatie\Health\Checks\Result; +use Spatie\Health\Support\BackupFile; use Symfony\Component\HttpFoundation\File\File as SymfonyFile; class BackupsCheck extends Check { protected ?string $locatedAt = null; + protected ?Filesystem $disk = null; + protected ?Carbon $youngestShouldHaveBeenMadeBefore = null; protected ?Carbon $oldestShouldHaveBeenMadeAfter = null; @@ -30,6 +35,13 @@ public function locatedAt(string $globPath): self return $this; } + public function onDisk($disk) + { + $this->disk = Storage::disk($disk); + + return $this; + } + public function youngestBackShouldHaveBeenMadeBefore(Carbon $date): self { $this->youngestShouldHaveBeenMadeBefore = $date; @@ -61,7 +73,7 @@ public function numberOfBackups(?int $min = null, ?int $max = null): self public function run(): Result { - $files = collect(File::glob($this->locatedAt)); + $files = collect($this->disk ? $files = $this->disk->files($this->locatedAt) : File::glob($this->locatedAt)); if ($files->isEmpty()) { return Result::make()->failed('No backups found'); @@ -69,10 +81,10 @@ public function run(): Result $eligableBackups = $files ->map(function (string $path) { - return new SymfonyFile($path); + return new BackupFile($path, $this->disk); }) - ->filter(function (SymfonyFile $file) { - return $file->getSize() >= $this->minimumSizeInMegabytes * 1024 * 1024; + ->filter(function (BackupFile $file) { + return $file->size() >= $this->minimumSizeInMegabytes * 1024 * 1024; }); if ($eligableBackups->isEmpty()) { @@ -94,14 +106,14 @@ public function run(): Result if ($this->youngestShouldHaveBeenMadeBefore) { if ($this->youngestBackupIsToolOld($eligableBackups)) { return Result::make() - ->failed('Youngest backup was too old'); + ->failed('Youngest backup was too old'); } } if ($this->oldestShouldHaveBeenMadeAfter) { if ($this->oldestBackupIsTooYoung($eligableBackups)) { return Result::make() - ->failed('Oldest backup was too young'); + ->failed('Oldest backup was too young'); } } @@ -115,12 +127,12 @@ protected function youngestBackupIsToolOld(Collection $backups): bool { /** @var SymfonyFile|null $youngestBackup */ $youngestBackup = $backups - ->sortByDesc(fn (SymfonyFile $file) => $file->getMTime()) + ->sortByDesc(fn (BackupFile $file) => $file->lastModified()) ->first(); $threshold = $this->youngestShouldHaveBeenMadeBefore->getTimestamp(); - return $youngestBackup->getMTime() <= $threshold; + return $youngestBackup->lastModified() <= $threshold; } /** @@ -130,11 +142,11 @@ protected function oldestBackupIsTooYoung(Collection $backups): bool { /** @var SymfonyFile|null $oldestBackup */ $oldestBackup = $backups - ->sortBy(fn (SymfonyFile $file) => $file->getMTime()) + ->sortBy(fn (BackupFile $file) => $file->lastModified()) ->first(); $threshold = $this->oldestShouldHaveBeenMadeAfter->getTimestamp(); - return $oldestBackup->getMTime() >= $threshold; + return $oldestBackup->lastModified() >= $threshold; } } diff --git a/src/Support/BackupFile.php b/src/Support/BackupFile.php new file mode 100644 index 00000000..13f3b154 --- /dev/null +++ b/src/Support/BackupFile.php @@ -0,0 +1,34 @@ +file = new SymfonyFile($path); + } + } + + public function path(): string + { + return $this->path; + } + + public function size(): int + { + return $this->file ? $this->file->getSize() : $this->disk->size($this->path); + } + + public function lastModified(): int + { + return $this->file ? $this->file->getMTime() : $this->disk->lastModified($this->path); + } + +} diff --git a/tests/Checks/BackupsCheckTest.php b/tests/Checks/BackupsCheckTest.php index 16192dfa..a1f82dbd 100644 --- a/tests/Checks/BackupsCheckTest.php +++ b/tests/Checks/BackupsCheckTest.php @@ -1,5 +1,6 @@ run(); expect($result)->status->toBe(Status::failed()); }); + +it('will pass if the backup is at least than the given size when loaded from filesystem disk', function (int $sizeInMb) { + + Storage::fake('backups'); + + $tempFile = $this->temporaryDirectory->path('hey.zip'); + + shell_exec("truncate -s {$sizeInMb}M {$tempFile}"); + + Storage::disk('backups')->put('backups/hey.zip',file_get_contents($tempFile) ); + + $result = $this->backupsCheck + ->onDisk('backups') + ->locatedAt('backups') + ->atLeastSizeInMb(5) + ->run(); + + expect($result)->status->toBe(Status::ok()); +})->with([ + [5], + [6], +]); + +it('can check if the youngest backup is recent enough when loaded from filesystem disk', function () { + + Storage::fake('backups'); + Storage::disk('backups')->put('backups/hey.zip', 'content'); + + testTime()->addMinutes(4); + + $result = $this->backupsCheck + ->onDisk('backups') + ->locatedAt('backups') + ->youngestBackShouldHaveBeenMadeBefore(now()->subMinutes(5)->startOfMinute()) + ->run(); + + expect($result)->status->toBe(Status::ok()); + + testTime()->addMinutes(2); + + $result = $this->backupsCheck + ->locatedAt($this->temporaryDirectory->path('*.zip')) + ->youngestBackShouldHaveBeenMadeBefore(now()->subMinutes(5)) + ->run(); + expect($result)->status->toBe(Status::failed()); +}); + +it('can check if the oldest backup is old enough when loaded from filesystem disk', function () { + + Storage::fake('backups'); + Storage::disk('backups')->put('backups/hey.zip', 'content'); + + testTime()->addMinutes(4); + + $result = $this->backupsCheck + ->onDisk('backups') + ->locatedAt('backups') + ->oldestBackShouldHaveBeenMadeAfter(now()->subMinutes(5)) + ->run(); + + expect($result)->status->toBe(Status::failed()); + + testTime()->addMinutes(2); + + $result = $this->backupsCheck + ->locatedAt($this->temporaryDirectory->path('*.zip')) + ->oldestBackShouldHaveBeenMadeAfter(now()->subMinutes(5)) + ->run(); + expect($result)->status->toBe(Status::failed()); +});