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(); } }