Ctrl K
ComunidadGuías…

app/Jobs/ScanHostPackagesJob.php

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
<?php

namespace App\Jobs;

use App\Actions\ImageScan\ScanHostPackages;
use App\Actions\ImageScan\ScanImage;
use App\Enums\ImageScanErrorCode;
use App\Events\ServerPatchCheckFinished;
use App\Models\Server;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
use Illuminate\Contracts\Queue\ShouldBeUniqueUntilProcessing;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Str;

/**
 * Scans the OS packages of a server. A busy image scan lock runs the job again later; after the last try it records
 * `LockBusy`. When it ends, the patches page reloads through `ServerPatchCheckFinished`.
 *
 * The unique lock ends when the job starts, so a scan requested during a running scan (for example, after a package
 * update) is queued and runs after it, instead of being dropped.
 */
class ScanHostPackagesJob implements ShouldBeEncrypted, ShouldBeUniqueUntilProcessing, ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    private const RETRY_DELAY_SECONDS = 300;

    public $tries = 6;

    public $maxExceptions = 1;

    /** A run never takes longer than the server scan lock lives, so the worker does not stop it before the cleanup. */
    public $timeout = ScanImage::MAX_LOCK_TTL;

    public $uniqueFor = ScanImage::MAX_LOCK_TTL;

    public function __construct(public Server $server)
    {
        // Self-hosted Horizon works through 'high' first, so scans there would delay deployments.
        $this->onQueue(isCloud() ? maintenance_queue() : 'default');
    }

    public function uniqueId(): string
    {
        return (string) $this->server->id;
    }

    public static function runningCacheKey(Server $server): string
    {
        return "server-host-scan-running:{$server->id}";
    }

    /**
     * Queues a scan and marks it as running, so the patches page shows the progress after a reload. The mark lasts
     * for the longest wait in the queue and the longest run. Each request writes a new token, so a scan that started
     * before this request cannot clear the marker of this request.
     */
    public static function dispatchManual(Server $server): void
    {
        Cache::put(self::runningCacheKey($server), (string) Str::uuid(), now()->addSeconds(2 * ScanImage::lockTtl($server)));
        self::dispatch($server);
    }

    public function handle(): void
    {
        $server = $this->server->fresh(['settings']);
        if (! $server instanceof Server) {
            return;
        }

        $request = Cache::get(self::runningCacheKey($server));

        if (! ScanHostPackages::run($server)) {
            if ($this->attempts() < $this->tries) {
                $this->release(self::RETRY_DELAY_SECONDS);

                return;
            }
            ScanHostPackages::recordFailure($server, ImageScanErrorCode::LockBusy);
        }

        // A request made during this scan queued a new scan, which clears the marker when it ends.
        if (Cache::get(self::runningCacheKey($server)) === $request) {
            Cache::forget(self::runningCacheKey($server));
        }
        ServerPatchCheckFinished::dispatch($server->team_id, $server->uuid);
    }

    public function failed(): void
    {
        Cache::forget(self::runningCacheKey($this->server));
    }
}