CluPilotCloud/app/Provisioning/Steps/Customer/RegisterMonitoring.php

79 lines
3.0 KiB
PHP

<?php
namespace App\Provisioning\Steps\Customer;
use App\Models\ProvisioningRun;
use App\Provisioning\StepResult;
use App\Services\Monitoring\MonitoringClient;
class RegisterMonitoring extends CustomerStep
{
public function __construct(private MonitoringClient $monitoring) {}
public function key(): string
{
return 'register_monitoring';
}
public function maxDuration(): int
{
return 120;
}
public function execute(ProvisioningRun $run): StepResult
{
if ($this->hasResource($run, 'monitoring_target_id')) {
return StepResult::advance();
}
$instance = $this->instance($run);
$url = 'https://'.$instance->subdomain.'.'.config('provisioning.dns.zone').'/status.php';
// Register with the monitoring service [E] before recording it.
// Monitoring is observability, not the product: a paid customer's cloud
// must not fail to be delivered because Kuma/the bridge is down. Retry a
// few times, then continue DEGRADED with a visible event — unless the
// operator declares monitoring mandatory.
try {
$targetId = $this->monitoring->registerTarget($instance->subdomain, $url);
} catch (\Throwable $e) {
if (config('provisioning.monitoring.required', false)) {
throw $e; // operator wants monitoring to gate delivery
}
// `attempt` is 0-based (this call IS attempt number attempt+1), so
// compare one-based: MONITORING_ATTEMPTS=2 really means two tries.
// Cap at the run's own retry budget — otherwise a large ATTEMPTS
// value would burn through max_attempts and fail the run, which is
// exactly what this degradation exists to prevent.
$configured = max(1, (int) config('provisioning.monitoring.attempts', 2));
$allowed = max(1, min($configured, (int) $run->max_attempts));
if ($run->attempt + 1 < $allowed) {
return StepResult::retry(30, 'monitoring unavailable: '.$e->getMessage());
}
// Give up on monitoring, keep the cloud. Recorded so it is visible in
// the admin console rather than silently missing.
$run->events()->create([
'step' => $this->key(),
'attempt' => $run->attempt,
'outcome' => 'info',
'message' => 'Monitoring übersprungen (Dienst nicht erreichbar): '.$e->getMessage(),
]);
return StepResult::advance();
}
// Local row BEFORE the breadcrumb (the short-circuit guard).
$instance->monitoringTargets()->firstOrCreate(
['external_id' => $targetId],
// 'unknown', not 'up': registering a check is not the same as
// passing one, and the sync job is what turns it into a verdict.
['url' => $url, 'status' => 'unknown'],
);
$this->recordResource($run, $instance->host, 'monitoring_target_id', $targetId);
return StepResult::advance();
}
}