mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-04 10:42:23 +00:00
fix(previews): do not bind preview volumes to the production host device
A preview volume no longer gets driver_opts that name a host device, so it does not mount the same folder or disk as the production volume. tmpfs options are kept. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a0fa21b448
commit
7ec46faecb
2 files changed
+63
-7
No files matched your search
@@ -494,15 +494,23 @@ function composeRenamedVolumeDeclaration(mixed $declaration, string $name): arra
|
||||
* name-only declaration: Docker created it without the options, and Docker Compose would otherwise
|
||||
* ask to recreate it on every deployment.
|
||||
*
|
||||
* A preview volume does not get `driver_opts` that name a host device (for example a bind mount of a
|
||||
* host folder): the preview would otherwise mount the same folder or disk as the production volume.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
function composeRenamedVolumeDeclarationFor(mixed $declaration, string $name, ?LocalPersistentVolume $volume): array
|
||||
function composeRenamedVolumeDeclarationFor(mixed $declaration, string $name, ?LocalPersistentVolume $volume, bool $isPreview = false): array
|
||||
{
|
||||
if ($volume?->ignores_compose_driver_options) {
|
||||
return ['name' => $name];
|
||||
}
|
||||
|
||||
return composeRenamedVolumeDeclaration($declaration, $name);
|
||||
$renamed = composeRenamedVolumeDeclaration($declaration, $name);
|
||||
if ($isPreview && filled(data_get($renamed, 'driver_opts.device')) && data_get($renamed, 'driver_opts.type') !== 'tmpfs') {
|
||||
unset($renamed['driver_opts']);
|
||||
}
|
||||
|
||||
return $renamed;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1509,7 +1517,7 @@ function applicationParser(Application $resource, int $pull_request_id = 0, ?int
|
||||
'resource_type' => get_class($originalResource),
|
||||
]
|
||||
);
|
||||
$topLevel->get('volumes')->put($name, composeRenamedVolumeDeclarationFor($declaration, $name, $persistentVolume));
|
||||
$topLevel->get('volumes')->put($name, composeRenamedVolumeDeclarationFor($declaration, $name, $persistentVolume, $isPullRequest));
|
||||
}
|
||||
dispatch(new ServerFilesFromServerJob($originalResource));
|
||||
$volumesParsed->put($index, $volume);
|
||||
|
||||
@@ -90,6 +90,21 @@ volumes:
|
||||
app-data: {}
|
||||
YAML;
|
||||
|
||||
const DRIVER_OPTIONS_TMPFS_COMPOSE = <<<'YAML'
|
||||
services:
|
||||
web:
|
||||
image: nginx:alpine
|
||||
volumes:
|
||||
- 'opts:/data'
|
||||
volumes:
|
||||
opts:
|
||||
driver: local
|
||||
driver_opts:
|
||||
type: tmpfs
|
||||
device: tmpfs
|
||||
o: size=100m
|
||||
YAML;
|
||||
|
||||
beforeEach(function () {
|
||||
Server::flushIdentityMap();
|
||||
InstanceSettings::unguarded(fn () => InstanceSettings::firstOrCreate(['id' => 0]));
|
||||
@@ -172,6 +187,22 @@ function expectedDriverOptionsVolume(string $name): array
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* The declaration of a preview volume: the bind device of the production volume is not shared.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
function expectedPreviewDriverOptionsVolume(string $name): array
|
||||
{
|
||||
return [
|
||||
'driver' => 'local',
|
||||
'labels' => [
|
||||
'com.example.purpose' => 'host-folder',
|
||||
],
|
||||
'name' => $name,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, mixed> $volumes
|
||||
* @return list<string>
|
||||
@@ -208,7 +239,8 @@ describe('applicationParser', function () {
|
||||
'long syntax' => [DRIVER_OPTIONS_LONG_COMPOSE],
|
||||
]);
|
||||
|
||||
it('keeps driver options on the preview volume', function () {
|
||||
it('does not bind the preview volume to the host folder of the production volume', function () {
|
||||
// A preview volume with the same bind device would mount the production data.
|
||||
$application = driverOptionsApplication(DRIVER_OPTIONS_SHORT_COMPOSE);
|
||||
$uuid = $application->uuid;
|
||||
$preview = driverOptionsPreview($application);
|
||||
@@ -217,7 +249,21 @@ describe('applicationParser', function () {
|
||||
$name = addPreviewDeploymentSuffix("{$uuid}_opts", 42);
|
||||
|
||||
expect(driverOptionsVolumeSources($parsed['services']['web-pr-42']['volumes']))->toBe([$name])
|
||||
->and($parsed['volumes'][$name])->toBe(expectedDriverOptionsVolume($name));
|
||||
->and($parsed['volumes'][$name])->toBe(expectedPreviewDriverOptionsVolume($name));
|
||||
});
|
||||
|
||||
it('keeps tmpfs driver options on the preview volume', function () {
|
||||
$application = driverOptionsApplication(DRIVER_OPTIONS_TMPFS_COMPOSE);
|
||||
$preview = driverOptionsPreview($application);
|
||||
$name = addPreviewDeploymentSuffix("{$application->uuid}_opts", 42);
|
||||
|
||||
$parsed = applicationParser($application, 42, $preview->id)->toArray();
|
||||
|
||||
expect($parsed['volumes'][$name])->toBe([
|
||||
'driver' => 'local',
|
||||
'driver_opts' => ['type' => 'tmpfs', 'device' => 'tmpfs', 'o' => 'size=100m'],
|
||||
'name' => $name,
|
||||
]);
|
||||
});
|
||||
|
||||
it('replaces the name of the declaration with the renamed volume', function () {
|
||||
@@ -349,7 +395,9 @@ describe('volumes created before driver options were kept', function () {
|
||||
expect($parsed['volumes'][$name])->toBe(['name' => $name]);
|
||||
});
|
||||
|
||||
it('keeps driver options on a new preview volume of an existing application', function () {
|
||||
it('applies the preview driver options to a new preview volume of an existing application', function () {
|
||||
// The new preview volume is a new Docker volume, so the flag of the production volume does not apply;
|
||||
// the bind device of the production volume is still not shared with the preview.
|
||||
$application = driverOptionsApplication(DRIVER_OPTIONS_SHORT_COMPOSE);
|
||||
applicationParser($application);
|
||||
markVolumesAsCreatedBeforeDriverOptions();
|
||||
@@ -358,7 +406,7 @@ describe('volumes created before driver options were kept', function () {
|
||||
|
||||
$parsed = applicationParser($application->fresh(), 42, $preview->id)->toArray();
|
||||
|
||||
expect($parsed['volumes'][$name])->toBe(expectedDriverOptionsVolume($name));
|
||||
expect($parsed['volumes'][$name])->toBe(expectedPreviewDriverOptionsVolume($name));
|
||||
});
|
||||
|
||||
it('keeps the name-only declaration of an existing service volume', function () {
|
||||
|
||||
Reference in new issue
Block a user