chore: prepare for PR

This commit is contained in:
Andras Bacsai 2026-02-15 13:46:08 +01:00
parent b40926e915
commit 1519666d4c
2 changed files with 92 additions and 7 deletions

View file

@ -112,12 +112,52 @@ public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|St
$dockercompose_base64 = base64_encode(Yaml::dump($docker_compose, 4, 2)); $dockercompose_base64 = base64_encode(Yaml::dump($docker_compose, 4, 2));
$nginxconf_base64 = base64_encode($nginxconf); $nginxconf_base64 = base64_encode($nginxconf);
instant_remote_process(["docker rm -f $proxyContainerName"], $server, false); instant_remote_process(["docker rm -f $proxyContainerName"], $server, false);
instant_remote_process([
"mkdir -p $configuration_dir", try {
"echo '{$nginxconf_base64}' | base64 -d | tee $configuration_dir/nginx.conf > /dev/null", instant_remote_process([
"echo '{$dockercompose_base64}' | base64 -d | tee $configuration_dir/docker-compose.yaml > /dev/null", "mkdir -p $configuration_dir",
"docker compose --project-directory {$configuration_dir} pull", "echo '{$nginxconf_base64}' | base64 -d | tee $configuration_dir/nginx.conf > /dev/null",
"docker compose --project-directory {$configuration_dir} up -d", "echo '{$dockercompose_base64}' | base64 -d | tee $configuration_dir/docker-compose.yaml > /dev/null",
], $server); "docker compose --project-directory {$configuration_dir} pull",
"docker compose --project-directory {$configuration_dir} up -d",
], $server);
} catch (\RuntimeException $e) {
if ($this->isNonTransientError($e->getMessage())) {
$database->update(['is_public' => false]);
$team = data_get($database, 'environment.project.team')
?? data_get($database, 'service.environment.project.team');
$team?->notify(
new \App\Notifications\Container\ContainerRestarted(
"TCP Proxy for {$database->name} database has been disabled due to error: {$e->getMessage()}",
$server,
)
);
ray("Database proxy for {$database->name} disabled due to non-transient error: {$e->getMessage()}");
return;
}
throw $e;
}
}
private function isNonTransientError(string $message): bool
{
$nonTransientPatterns = [
'port is already allocated',
'address already in use',
'Bind for',
];
foreach ($nonTransientPatterns as $pattern) {
if (str_contains($message, $pattern)) {
return true;
}
}
return false;
} }
} }

View file

@ -0,0 +1,45 @@
<?php
use App\Actions\Database\StartDatabaseProxy;
use App\Models\StandalonePostgresql;
use App\Models\Team;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Notification;
uses(RefreshDatabase::class);
beforeEach(function () {
Notification::fake();
});
test('database proxy is disabled on port already allocated error', function () {
$team = Team::factory()->create();
$database = StandalonePostgresql::factory()->create([
'team_id' => $team->id,
'is_public' => true,
'public_port' => 5432,
]);
expect($database->is_public)->toBeTrue();
$action = new StartDatabaseProxy;
// Use reflection to test the private method directly
$method = new ReflectionMethod($action, 'isNonTransientError');
expect($method->invoke($action, 'Bind for 0.0.0.0:5432 failed: port is already allocated'))->toBeTrue();
expect($method->invoke($action, 'address already in use'))->toBeTrue();
expect($method->invoke($action, 'some other error'))->toBeFalse();
});
test('isNonTransientError detects port conflict patterns', function () {
$action = new StartDatabaseProxy;
$method = new ReflectionMethod($action, 'isNonTransientError');
expect($method->invoke($action, 'Bind for 0.0.0.0:5432 failed: port is already allocated'))->toBeTrue()
->and($method->invoke($action, 'address already in use'))->toBeTrue()
->and($method->invoke($action, 'Bind for 0.0.0.0:3306 failed: port is already allocated'))->toBeTrue()
->and($method->invoke($action, 'network timeout'))->toBeFalse()
->and($method->invoke($action, 'connection refused'))->toBeFalse();
});