Skip to content

Commit

Permalink
Add support for checking backups on filesystem disks
Browse files Browse the repository at this point in the history
  • Loading branch information
pelmered committed Jul 21, 2024
1 parent b44e807 commit da5a681
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 10 deletions.
32 changes: 22 additions & 10 deletions src/Checks/Checks/BackupsCheck.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -61,18 +73,18 @@ 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');
}

$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()) {
Expand All @@ -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');
}
}

Expand All @@ -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;
}

/**
Expand All @@ -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;
}
}
34 changes: 34 additions & 0 deletions src/Support/BackupFile.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php
namespace Spatie\Health\Support;

use Illuminate\Contracts\Filesystem\Filesystem;
use Symfony\Component\HttpFoundation\File\File as SymfonyFile;

class BackupFile {
protected ?SymfonyFile $file = null;

public function __construct(
protected string $path,
protected ?Filesystem $disk = null,
) {
if (!$disk) {
$this->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);
}

}
71 changes: 71 additions & 0 deletions tests/Checks/BackupsCheckTest.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?php

use Illuminate\Support\Facades\Storage;
use Spatie\Health\Checks\Checks\BackupsCheck;
use Spatie\Health\Enums\Status;
use Spatie\Health\Facades\Health;
Expand Down Expand Up @@ -150,3 +151,73 @@
->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());
});

0 comments on commit da5a681

Please sign in to comment.