mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-12 15:53:46 -04:00
fix(api): harden service_name filter for application logs
- Match the Compose service label with format_docker_labels_to_json() and skip containers without labels. - Document service_name on the preview logs endpoint and remove the incorrect "ignored for non-Compose applications" claim. Every application runs as a Compose service, so a mismatch returns 404. - Add regression tests for service selection, the default container, and the exact-match 404. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
6023f4974e
commit
8bbab724e2
@@ -2481,7 +2481,7 @@ class ApplicationsController extends Controller
|
||||
new OA\Parameter(
|
||||
name: 'service_name',
|
||||
in: 'query',
|
||||
description: 'For Docker Compose applications, return logs only from the Compose service with this name. Returns 404 when no running container matches. Ignored for non-Compose applications.',
|
||||
description: 'Return logs only from the container of the Docker Compose service with this name. Returns 404 when no running container matches.',
|
||||
required: false,
|
||||
schema: new OA\Schema(type: 'string'),
|
||||
),
|
||||
@@ -2557,6 +2557,13 @@ class ApplicationsController extends Controller
|
||||
required: false,
|
||||
schema: new OA\Schema(type: 'boolean', default: false),
|
||||
),
|
||||
new OA\Parameter(
|
||||
name: 'service_name',
|
||||
in: 'query',
|
||||
description: 'Return logs only from the container of the Docker Compose service with this name. Returns 404 when no running container matches.',
|
||||
required: false,
|
||||
schema: new OA\Schema(type: 'string'),
|
||||
),
|
||||
],
|
||||
responses: [
|
||||
new OA\Response(response: 200, description: 'Preview runtime logs.', content: new OA\JsonContent(
|
||||
@@ -2615,10 +2622,7 @@ class ApplicationsController extends Controller
|
||||
$matchingContainer = $containers->first(function ($container) use ($serviceName) {
|
||||
$labels = data_get($container, 'Labels');
|
||||
|
||||
return $labels !== null && preg_match(
|
||||
'/(?:^|,)com\.docker\.compose\.service='.preg_quote($serviceName, '/').'(?:,|$)/',
|
||||
$labels
|
||||
) === 1;
|
||||
return filled($labels) && format_docker_labels_to_json($labels)->get('com.docker.compose.service') === $serviceName;
|
||||
});
|
||||
|
||||
if (! $matchingContainer) {
|
||||
|
||||
@@ -448,3 +448,55 @@ describe('PATCH /api/v1/applications/{uuid}/previews/{pull_request_id}', functio
|
||||
->assertJsonValidationErrors('docker_compose_domains');
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /api/v1/applications/{uuid}/logs service_name', function () {
|
||||
beforeEach(function () {
|
||||
$privateKey = PrivateKey::factory()->create(['team_id' => $this->team->id]);
|
||||
$this->server->update(['private_key_id' => $privateKey->id]);
|
||||
Process::fake(function ($process) {
|
||||
if (str_contains($process->command, 'docker ps -a')) {
|
||||
return Process::result(output: implode(PHP_EOL, [
|
||||
json_encode([
|
||||
'ID' => 'web-container',
|
||||
'Names' => "web-{$this->application->uuid}",
|
||||
'Labels' => "coolify.applicationId={$this->application->id},com.docker.compose.service=web",
|
||||
]),
|
||||
json_encode([
|
||||
'ID' => 'worker-container',
|
||||
'Names' => "worker-{$this->application->uuid}",
|
||||
'Labels' => "coolify.applicationId={$this->application->id},com.docker.compose.service=worker",
|
||||
]),
|
||||
]));
|
||||
}
|
||||
if (str_contains($process->command, 'docker inspect')) {
|
||||
return Process::result(output: json_encode(['State' => ['Status' => 'running']]));
|
||||
}
|
||||
if (str_contains($process->command, 'docker logs')) {
|
||||
return Process::result(output: str_contains($process->command, 'worker-container') ? 'worker log' : 'web log');
|
||||
}
|
||||
|
||||
return Process::result();
|
||||
});
|
||||
});
|
||||
|
||||
test('returns logs from the container of the requested service', function () {
|
||||
$this->withHeaders(previewAuthHeaders($this->bearerToken))
|
||||
->getJson("/api/v1/applications/{$this->application->uuid}/logs?service_name=worker")
|
||||
->assertOk()
|
||||
->assertJson(['logs' => 'worker log']);
|
||||
});
|
||||
|
||||
test('returns logs from the first container without service_name', function () {
|
||||
$this->withHeaders(previewAuthHeaders($this->bearerToken))
|
||||
->getJson("/api/v1/applications/{$this->application->uuid}/logs")
|
||||
->assertOk()
|
||||
->assertJson(['logs' => 'web log']);
|
||||
});
|
||||
|
||||
test('returns 404 when no container matches service_name exactly', function () {
|
||||
$this->withHeaders(previewAuthHeaders($this->bearerToken))
|
||||
->getJson("/api/v1/applications/{$this->application->uuid}/logs?service_name=work")
|
||||
->assertNotFound()
|
||||
->assertJson(['message' => "No running container found for service_name 'work'."]);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user