mirror of
https://github.com/coollabsio/coolify.git
synced 2026-09-25 17:01:49 -04:00
feat(sentinel): make sentinel mandatory on regular servers
Remove the enable/disable toggle from the server UI, logs page, and Sentinel API so is_sentinel_enabled is derived and read-only. Enable existing regular servers via migration, start Sentinel after validate- and-install, and drop the daily ServerManagerJob restart.
This commit is contained in:
@@ -9,6 +9,7 @@ use App\Models\Server;
|
||||
use App\Models\Team;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
class ScheduledJobDiagnostics extends Command
|
||||
@@ -203,7 +204,6 @@ class ScheduledJobDiagnostics extends Command
|
||||
}
|
||||
|
||||
$dedupKeys = [
|
||||
"sentinel-restart:{$server->id}" => '0 0 * * *',
|
||||
"server-patch-check:{$server->id}" => '0 0 * * 0',
|
||||
"server-check:{$server->id}" => isCloud() ? '*/5 * * * *' : '* * * * *',
|
||||
"server-storage-check:{$server->id}" => data_get($server->settings, 'server_disk_usage_check_frequency', '0 23 * * *'),
|
||||
@@ -235,7 +235,7 @@ class ScheduledJobDiagnostics extends Command
|
||||
$this->newLine();
|
||||
}
|
||||
|
||||
private function getServers(?string $serverFilter): \Illuminate\Support\Collection
|
||||
private function getServers(?string $serverFilter): Collection
|
||||
{
|
||||
$query = Server::with('settings')->where('ip', '!=', '1.2.3.4');
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ use OpenApi\Attributes as OA;
|
||||
class ServerSentinelController extends Controller
|
||||
{
|
||||
private const ALLOWED_FIELDS = [
|
||||
'is_sentinel_enabled',
|
||||
'is_metrics_enabled',
|
||||
'is_sentinel_debug_enabled',
|
||||
'sentinel_token',
|
||||
@@ -36,7 +35,7 @@ class ServerSentinelController extends Controller
|
||||
{
|
||||
$settings = $server->settings;
|
||||
$payload = [
|
||||
'is_sentinel_enabled' => (bool) $settings->is_sentinel_enabled,
|
||||
'is_sentinel_enabled' => $server->isSentinelEnabled(),
|
||||
'is_metrics_enabled' => (bool) $settings->is_metrics_enabled,
|
||||
'is_sentinel_debug_enabled' => (bool) $settings->is_sentinel_debug_enabled,
|
||||
'sentinel_metrics_refresh_rate_seconds' => (int) $settings->sentinel_metrics_refresh_rate_seconds,
|
||||
@@ -69,7 +68,7 @@ class ServerSentinelController extends Controller
|
||||
description: 'Sentinel settings.',
|
||||
content: new OA\JsonContent(
|
||||
properties: [
|
||||
new OA\Property(property: 'is_sentinel_enabled', type: 'boolean'),
|
||||
new OA\Property(property: 'is_sentinel_enabled', type: 'boolean', readOnly: true, description: 'Sentinel is mandatory on regular managed servers.'),
|
||||
new OA\Property(property: 'is_metrics_enabled', type: 'boolean'),
|
||||
new OA\Property(property: 'is_sentinel_debug_enabled', type: 'boolean'),
|
||||
new OA\Property(property: 'sentinel_token', type: 'string', description: 'Only present with read:sensitive.'),
|
||||
@@ -118,7 +117,6 @@ class ServerSentinelController extends Controller
|
||||
required: true,
|
||||
content: new OA\JsonContent(
|
||||
properties: [
|
||||
new OA\Property(property: 'is_sentinel_enabled', type: 'boolean'),
|
||||
new OA\Property(property: 'is_metrics_enabled', type: 'boolean'),
|
||||
new OA\Property(property: 'is_sentinel_debug_enabled', type: 'boolean'),
|
||||
new OA\Property(property: 'sentinel_token', type: 'string'),
|
||||
@@ -158,7 +156,6 @@ class ServerSentinelController extends Controller
|
||||
$this->authorize('update', $server);
|
||||
|
||||
$validator = customApiValidator($request->all(), [
|
||||
'is_sentinel_enabled' => 'boolean',
|
||||
'is_metrics_enabled' => 'boolean',
|
||||
'is_sentinel_debug_enabled' => 'boolean',
|
||||
'sentinel_token' => ['string', 'max:500', 'regex:/\A[a-zA-Z0-9._\-+=\/]+\z/'],
|
||||
@@ -189,29 +186,12 @@ class ServerSentinelController extends Controller
|
||||
}
|
||||
|
||||
$settings = $server->settings;
|
||||
$enablingSentinel = $request->has('is_sentinel_enabled')
|
||||
&& $request->boolean('is_sentinel_enabled')
|
||||
&& ! $settings->is_sentinel_enabled;
|
||||
|
||||
if ($enablingSentinel && $server->isBuildServer()) {
|
||||
return response()->json([
|
||||
'message' => 'Validation failed.',
|
||||
'errors' => ['is_sentinel_enabled' => ['Sentinel cannot be enabled on build servers.']],
|
||||
], 422);
|
||||
}
|
||||
|
||||
foreach (self::ALLOWED_FIELDS as $field) {
|
||||
if ($request->has($field)) {
|
||||
$settings->{$field} = $request->input($field);
|
||||
}
|
||||
}
|
||||
|
||||
// Disabling Sentinel also clears related toggles (matches Livewire toggleSentinel).
|
||||
if ($request->has('is_sentinel_enabled') && ! $request->boolean('is_sentinel_enabled')) {
|
||||
$settings->is_metrics_enabled = false;
|
||||
$settings->is_sentinel_debug_enabled = false;
|
||||
}
|
||||
|
||||
$settings->save();
|
||||
|
||||
auditLog('api.server.sentinel.updated', [
|
||||
|
||||
@@ -166,14 +166,6 @@ class ServerManagerJob implements ShouldBeEncrypted, ShouldQueue
|
||||
}
|
||||
}
|
||||
|
||||
$isSentinelEnabled = $server->isSentinelEnabled();
|
||||
$shouldRestartSentinel = $isSentinelEnabled && shouldRunCronNow('0 0 * * *', $serverTimezone, "sentinel-restart:{$server->id}", $this->executionTime);
|
||||
// Dispatch Sentinel restart if due (daily for Sentinel-enabled servers)
|
||||
|
||||
if ($shouldRestartSentinel) {
|
||||
CheckAndStartSentinelJob::dispatch($server);
|
||||
}
|
||||
|
||||
// Dispatch ServerStorageCheckJob if due (only when Sentinel is out of sync or disabled)
|
||||
// When Sentinel is active, PushServerUpdateJob handles storage checks with real-time data
|
||||
if ($sentinelOutOfSync) {
|
||||
@@ -195,7 +187,6 @@ class ServerManagerJob implements ShouldBeEncrypted, ShouldQueue
|
||||
ServerPatchCheckJob::dispatch($server);
|
||||
}
|
||||
|
||||
// Note: CheckAndStartSentinelJob is only dispatched daily (line above) for version updates.
|
||||
// Crash recovery is handled by sentinelOutOfSync → ServerCheckJob → CheckAndStartSentinelJob.
|
||||
}
|
||||
|
||||
|
||||
@@ -202,6 +202,9 @@ class ValidateAndInstallServerJob implements ShouldBeEncrypted, ShouldQueue
|
||||
// Broadcast events to update UI
|
||||
ServerValidated::dispatch($this->server->team_id, $this->server->uuid);
|
||||
ServerReachabilityChanged::dispatch($this->server);
|
||||
if ($this->server->isSentinelEnabled()) {
|
||||
CheckAndStartSentinelJob::dispatch($this->server);
|
||||
}
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('ValidateAndInstallServer: Exception occurred', [
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
namespace App\Livewire\Server;
|
||||
|
||||
use App\Actions\Server\StartSentinel;
|
||||
use App\Actions\Server\StopSentinel;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Livewire\Attributes\Validate;
|
||||
@@ -34,8 +32,6 @@ class Sentinel extends Component
|
||||
#[Validate(['nullable', 'url'])]
|
||||
public ?string $sentinelCustomUrl = null;
|
||||
|
||||
public bool $isSentinelEnabled;
|
||||
|
||||
public bool $isSentinelDebugEnabled;
|
||||
|
||||
public ?string $sentinelCustomDockerImage = null;
|
||||
@@ -64,7 +60,6 @@ class Sentinel extends Component
|
||||
$this->server->settings->sentinel_metrics_history_days = $this->sentinelMetricsHistoryDays;
|
||||
$this->server->settings->sentinel_push_interval_seconds = $this->sentinelPushIntervalSeconds;
|
||||
$this->server->settings->sentinel_custom_url = $this->sentinelCustomUrl;
|
||||
$this->server->settings->is_sentinel_enabled = $this->isSentinelEnabled;
|
||||
$this->server->settings->is_sentinel_debug_enabled = $this->isSentinelDebugEnabled;
|
||||
$this->server->settings->save();
|
||||
} else {
|
||||
@@ -74,7 +69,6 @@ class Sentinel extends Component
|
||||
$this->sentinelMetricsHistoryDays = $this->server->settings->sentinel_metrics_history_days;
|
||||
$this->sentinelPushIntervalSeconds = $this->server->settings->sentinel_push_interval_seconds;
|
||||
$this->sentinelCustomUrl = $this->server->settings->sentinel_custom_url;
|
||||
$this->isSentinelEnabled = $this->server->settings->is_sentinel_enabled;
|
||||
$this->isSentinelDebugEnabled = $this->server->settings->is_sentinel_debug_enabled;
|
||||
$this->sentinelUpdatedAt = $this->server->sentinel_updated_at;
|
||||
}
|
||||
@@ -103,33 +97,6 @@ class Sentinel extends Component
|
||||
}
|
||||
}
|
||||
|
||||
public function toggleSentinel(): void
|
||||
{
|
||||
try {
|
||||
$this->authorize('manageSentinel', $this->server);
|
||||
if (! $this->isSentinelEnabled) {
|
||||
if ($this->server->isBuildServer()) {
|
||||
$this->dispatch('error', 'Sentinel cannot be enabled on build servers.');
|
||||
|
||||
return;
|
||||
}
|
||||
$customImage = isDev() ? $this->sentinelCustomDockerImage : null;
|
||||
StartSentinel::run($this->server, true, null, $customImage);
|
||||
$this->sentinelCustomUrl = $this->server->settings->sentinel_custom_url;
|
||||
$this->isSentinelEnabled = true;
|
||||
} else {
|
||||
$this->isSentinelEnabled = false;
|
||||
$this->isMetricsEnabled = false;
|
||||
$this->isSentinelDebugEnabled = false;
|
||||
StopSentinel::dispatch($this->server);
|
||||
}
|
||||
$this->submit();
|
||||
$this->dispatch('refreshServerShow');
|
||||
} catch (\Throwable $e) {
|
||||
handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function regenerateSentinelToken()
|
||||
{
|
||||
try {
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Livewire\Server\Sentinel;
|
||||
|
||||
use App\Actions\Server\StartSentinel;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Illuminate\View\View;
|
||||
@@ -30,35 +29,6 @@ class Logs extends Component
|
||||
$this->authorize('viewSentinel', $this->server);
|
||||
}
|
||||
|
||||
public function enableSentinel(): void
|
||||
{
|
||||
$this->authorize('manageSentinel', $this->server);
|
||||
|
||||
try {
|
||||
$this->server->refresh();
|
||||
if ($this->server->isBuildServer()) {
|
||||
$this->dispatch('error', 'Sentinel cannot be enabled on build servers.');
|
||||
|
||||
return;
|
||||
}
|
||||
if ($this->server->isSwarm()) {
|
||||
$this->dispatch('error', 'Sentinel cannot be enabled on Swarm servers.');
|
||||
|
||||
return;
|
||||
}
|
||||
if ($this->server->isSentinelEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
StartSentinel::run($this->server, true);
|
||||
$this->server->refresh();
|
||||
$this->dispatch('refreshServerShow');
|
||||
$this->dispatch('success', 'Sentinel has been enabled.');
|
||||
} catch (\Throwable $e) {
|
||||
handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function render(): View
|
||||
{
|
||||
return view('livewire.server.sentinel.logs');
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Livewire\Server;
|
||||
|
||||
use App\Actions\Server\StartSentinel;
|
||||
use App\Actions\Server\StopSentinel;
|
||||
use App\Events\ServerReachabilityChanged;
|
||||
use App\Models\CloudProviderToken;
|
||||
@@ -67,8 +66,6 @@ class Show extends Component
|
||||
|
||||
public ?string $sentinelCustomUrl = null;
|
||||
|
||||
public bool $isSentinelEnabled;
|
||||
|
||||
public bool $isSentinelDebugEnabled;
|
||||
|
||||
public ?string $sentinelCustomDockerImage = null;
|
||||
@@ -161,7 +158,6 @@ class Show extends Component
|
||||
'sentinelMetricsHistoryDays' => 'required|integer|min:1',
|
||||
'sentinelPushIntervalSeconds' => 'required|integer|min:10',
|
||||
'sentinelCustomUrl' => 'nullable|url',
|
||||
'isSentinelEnabled' => 'required',
|
||||
'isSentinelDebugEnabled' => 'required',
|
||||
'serverTimezone' => 'required',
|
||||
];
|
||||
@@ -265,7 +261,6 @@ class Show extends Component
|
||||
$this->server->settings->sentinel_metrics_history_days = $this->sentinelMetricsHistoryDays;
|
||||
$this->server->settings->sentinel_push_interval_seconds = $this->sentinelPushIntervalSeconds;
|
||||
$this->server->settings->sentinel_custom_url = $this->sentinelCustomUrl;
|
||||
$this->server->settings->is_sentinel_enabled = $this->isSentinelEnabled;
|
||||
$this->server->settings->is_sentinel_debug_enabled = $this->isSentinelDebugEnabled;
|
||||
|
||||
if (! validate_timezone($this->serverTimezone)) {
|
||||
@@ -296,7 +291,6 @@ class Show extends Component
|
||||
$this->sentinelMetricsHistoryDays = $this->server->settings->sentinel_metrics_history_days;
|
||||
$this->sentinelPushIntervalSeconds = $this->server->settings->sentinel_push_interval_seconds;
|
||||
$this->sentinelCustomUrl = $this->server->settings->sentinel_custom_url;
|
||||
$this->isSentinelEnabled = $this->server->settings->is_sentinel_enabled;
|
||||
$this->isSentinelDebugEnabled = $this->server->settings->is_sentinel_debug_enabled;
|
||||
$this->sentinelUpdatedAt = $this->server->sentinel_updated_at;
|
||||
$this->serverTimezone = $this->server->settings->server_timezone;
|
||||
@@ -425,10 +419,10 @@ class Show extends Component
|
||||
|
||||
return;
|
||||
}
|
||||
if ($value === true && $this->isSentinelEnabled) {
|
||||
$this->isSentinelEnabled = false;
|
||||
if ($value === true && $this->server->isSentinelEnabled()) {
|
||||
$this->isMetricsEnabled = false;
|
||||
$this->isSentinelDebugEnabled = false;
|
||||
$this->server->settings->is_sentinel_enabled = false;
|
||||
StopSentinel::dispatch($this->server);
|
||||
$this->dispatch('info', 'Sentinel has been disabled as build servers cannot run Sentinel.');
|
||||
}
|
||||
@@ -440,30 +434,6 @@ class Show extends Component
|
||||
}
|
||||
}
|
||||
|
||||
public function updatedIsSentinelEnabled($value)
|
||||
{
|
||||
try {
|
||||
$this->authorize('manageSentinel', $this->server);
|
||||
if ($value === true) {
|
||||
if ($this->isBuildServer) {
|
||||
$this->isSentinelEnabled = false;
|
||||
$this->dispatch('error', 'Sentinel cannot be enabled on build servers.');
|
||||
|
||||
return;
|
||||
}
|
||||
$customImage = isDev() ? $this->sentinelCustomDockerImage : null;
|
||||
StartSentinel::run($this->server, true, null, $customImage);
|
||||
} else {
|
||||
$this->isMetricsEnabled = false;
|
||||
$this->isSentinelDebugEnabled = false;
|
||||
StopSentinel::dispatch($this->server);
|
||||
}
|
||||
$this->submit();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function regenerateSentinelToken()
|
||||
{
|
||||
try {
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace App\Livewire\Server;
|
||||
use App\Actions\Proxy\CheckProxy;
|
||||
use App\Actions\Proxy\StartProxy;
|
||||
use App\Events\ServerValidated;
|
||||
use App\Jobs\CheckAndStartSentinelJob;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Livewire\Component;
|
||||
@@ -275,6 +276,9 @@ class ValidateAndInstall extends Component
|
||||
$this->dispatch('refreshServerShow');
|
||||
$this->dispatch('refreshBoardingIndex');
|
||||
ServerValidated::dispatch($this->server->team_id, $this->server->uuid);
|
||||
if ($this->server->isSentinelEnabled()) {
|
||||
CheckAndStartSentinelJob::dispatch($this->server);
|
||||
}
|
||||
$this->dispatch('success', 'Server validated, proxy is starting in a moment.');
|
||||
$proxyShouldRun = CheckProxy::run($this->server, true);
|
||||
if (! $proxyShouldRun) {
|
||||
|
||||
@@ -972,17 +972,20 @@ $siteAddress {
|
||||
return Carbon::parse($this->sentinel_updated_at)->isAfter(now()->subSeconds($this->waitBeforeDoingSshCheck()));
|
||||
}
|
||||
|
||||
public function isSentinelEnabled()
|
||||
public function isSentinelEnabled(): bool
|
||||
{
|
||||
return ($this->isMetricsEnabled() || $this->isServerApiEnabled()) && ! $this->isBuildServer();
|
||||
return ! $this->isBuildServer()
|
||||
&& ! $this->isSwarm()
|
||||
&& ! $this->isForceDisabled()
|
||||
&& ! $this->isTransferredAway();
|
||||
}
|
||||
|
||||
public function isMetricsEnabled()
|
||||
public function isMetricsEnabled(): bool
|
||||
{
|
||||
return $this->settings->is_metrics_enabled;
|
||||
}
|
||||
|
||||
public function isServerApiEnabled()
|
||||
public function isServerApiEnabled(): bool
|
||||
{
|
||||
return $this->settings->is_sentinel_enabled;
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ class ServerTransferClaimer
|
||||
if ($rebindSentinel && $server->settings) {
|
||||
$server->settings->sentinel_custom_url = $instanceUrl;
|
||||
$server->settings->ensureValidSentinelToken();
|
||||
// Leave sentinel disabled until operator enables metrics; endpoint is ready.
|
||||
$server->settings->is_sentinel_enabled = true;
|
||||
$server->settings->save();
|
||||
$sentinelRebound = true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user