feat(core): You can validate compose files with docker compose config

fix(core): labels are now accepted with both compose styles
refactor: remove lots of ray's
This commit is contained in:
Andras Bacsai
2025-02-27 11:29:04 +01:00
parent 359806472b
commit 27e4882d57
29 changed files with 137 additions and 162 deletions

View File

@@ -208,7 +208,6 @@ class GetContainersStatus
$foundServices[] = "$service->id-$service->name"; $foundServices[] = "$service->id-$service->name";
$statusFromDb = $service->status; $statusFromDb = $service->status;
if ($statusFromDb !== $containerStatus) { if ($statusFromDb !== $containerStatus) {
// ray('Updating status: ' . $containerStatus);
$service->update(['status' => $containerStatus]); $service->update(['status' => $containerStatus]);
} else { } else {
$service->update(['last_online_at' => now()]); $service->update(['last_online_at' => now()]);

View File

@@ -1291,11 +1291,6 @@ class ApplicationsController extends Controller
$dockerCompose = base64_decode($request->docker_compose_raw); $dockerCompose = base64_decode($request->docker_compose_raw);
$dockerComposeRaw = Yaml::dump(Yaml::parse($dockerCompose), 10, 2, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK); $dockerComposeRaw = Yaml::dump(Yaml::parse($dockerCompose), 10, 2, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK);
// $isValid = validateComposeFile($dockerComposeRaw, $server_id);
// if ($isValid !== 'OK') {
// return $this->dispatch('error', "Invalid docker-compose file.\n$isValid");
// }
$service = new Service; $service = new Service;
removeUnnecessaryFieldsFromRequest($request); removeUnnecessaryFieldsFromRequest($request);
$service->fill($request->all()); $service->fill($request->all());

View File

@@ -202,7 +202,6 @@ class Gitea extends Controller
if ($found) { if ($found) {
$found->delete(); $found->delete();
$container_name = generateApplicationContainerName($application, $pull_request_id); $container_name = generateApplicationContainerName($application, $pull_request_id);
// ray('Stopping container: ' . $container_name);
instant_remote_process(["docker rm -f $container_name"], $application->destination->server); instant_remote_process(["docker rm -f $container_name"], $application->destination->server);
$return_payloads->push([ $return_payloads->push([
'application' => $application->name, 'application' => $application->name,

View File

@@ -208,7 +208,6 @@ class Github extends Controller
if ($found) { if ($found) {
$found->delete(); $found->delete();
$container_name = generateApplicationContainerName($application, $pull_request_id); $container_name = generateApplicationContainerName($application, $pull_request_id);
// ray('Stopping container: ' . $container_name);
instant_remote_process(["docker rm -f $container_name"], $application->destination->server); instant_remote_process(["docker rm -f $container_name"], $application->destination->server);
$return_payloads->push([ $return_payloads->push([
'application' => $application->name, 'application' => $application->name,

View File

@@ -227,7 +227,6 @@ class Gitlab extends Controller
if ($found) { if ($found) {
$found->delete(); $found->delete();
$container_name = generateApplicationContainerName($application, $pull_request_id); $container_name = generateApplicationContainerName($application, $pull_request_id);
// ray('Stopping container: ' . $container_name);
instant_remote_process(["docker rm -f $container_name"], $application->destination->server); instant_remote_process(["docker rm -f $container_name"], $application->destination->server);
$return_payloads->push([ $return_payloads->push([
'application' => $application->name, 'application' => $application->name,

View File

@@ -1207,7 +1207,6 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
if ($this->application->custom_healthcheck_found) { if ($this->application->custom_healthcheck_found) {
$this->application_deployment_queue->addLogEntry('Custom healthcheck found, skipping default healthcheck.'); $this->application_deployment_queue->addLogEntry('Custom healthcheck found, skipping default healthcheck.');
} }
// ray('New container name: ', $this->container_name);
if ($this->container_name) { if ($this->container_name) {
$counter = 1; $counter = 1;
$this->application_deployment_queue->addLogEntry('Waiting for healthcheck to pass on the new container.'); $this->application_deployment_queue->addLogEntry('Waiting for healthcheck to pass on the new container.');
@@ -1410,7 +1409,6 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
continue; continue;
} }
// ray('Deploying to additional destination: ', $server->name);
$deployment_uuid = new Cuid2; $deployment_uuid = new Cuid2;
queue_application_deployment( queue_application_deployment(
deployment_uuid: $deployment_uuid, deployment_uuid: $deployment_uuid,

View File

@@ -52,12 +52,6 @@ class DockerCompose extends Component
'dockerComposeRaw' => 'required', 'dockerComposeRaw' => 'required',
]); ]);
$this->dockerComposeRaw = Yaml::dump(Yaml::parse($this->dockerComposeRaw), 10, 2, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK); $this->dockerComposeRaw = Yaml::dump(Yaml::parse($this->dockerComposeRaw), 10, 2, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK);
$isValid = validateComposeFile($this->dockerComposeRaw, $server_id);
if ($isValid !== 'OK') {
return $this->dispatch('error', "Invalid docker-compose file.\n$isValid");
}
$project = Project::where('uuid', $this->parameters['project_uuid'])->first(); $project = Project::where('uuid', $this->parameters['project_uuid'])->first();
$environment = $project->load(['environments'])->environments->where('uuid', $this->parameters['environment_uuid'])->first(); $environment = $project->load(['environments'])->environments->where('uuid', $this->parameters['environment_uuid'])->first();

View File

@@ -39,6 +39,16 @@ class EditCompose extends Component
$this->service = Service::find($this->serviceId); $this->service = Service::find($this->serviceId);
} }
public function validateCompose()
{
$isValid = validateComposeFile($this->service->docker_compose_raw, $this->service->server_id);
if ($isValid !== 'OK') {
$this->dispatch('error', "Invalid docker-compose file.\n$isValid");
} else {
$this->dispatch('success', 'Docker compose is valid.');
}
}
public function saveEditedCompose() public function saveEditedCompose()
{ {
$this->dispatch('info', 'Saving new docker compose...'); $this->dispatch('info', 'Saving new docker compose...');

View File

@@ -63,7 +63,7 @@ class StackForm extends Component
public function saveCompose($raw) public function saveCompose($raw)
{ {
$this->service->docker_compose_raw = $raw; $this->service->docker_compose_raw = $raw;
$this->submit(notify: false); $this->submit(notify: true);
} }
public function instantSave() public function instantSave()
@@ -76,10 +76,6 @@ class StackForm extends Component
{ {
try { try {
$this->validate(); $this->validate();
$isValid = validateComposeFile($this->service->docker_compose_raw, $this->service->server->id);
if ($isValid !== 'OK') {
throw new \Exception("Invalid docker-compose file.\n$isValid");
}
$this->service->save(); $this->service->save();
$this->service->saveExtraFields($this->fields); $this->service->saveExtraFields($this->fields);
$this->service->parse(); $this->service->parse();

View File

@@ -29,8 +29,6 @@ class Webhooks extends Component
public function mount() public function mount()
{ {
// ray()->clearAll();
// ray()->showQueries();
$this->deploywebhook = generateDeployWebhook($this->resource); $this->deploywebhook = generateDeployWebhook($this->resource);
$this->githubManualWebhookSecret = data_get($this->resource, 'manual_webhook_secret_github'); $this->githubManualWebhookSecret = data_get($this->resource, 'manual_webhook_secret_github');

View File

@@ -105,7 +105,6 @@ class Deploy extends Component
$startTime = Carbon::now()->getTimestamp(); $startTime = Carbon::now()->getTimestamp();
while ($process->running()) { while ($process->running()) {
ray('running');
if (Carbon::now()->getTimestamp() - $startTime >= $timeout) { if (Carbon::now()->getTimestamp() - $startTime >= $timeout) {
$this->forceStopContainer($containerName); $this->forceStopContainer($containerName);
break; break;

View File

@@ -101,7 +101,6 @@ class Change extends Component
// ]); // ]);
// } // }
// ray($runners_by_repository);
// } // }
public function mount() public function mount()

View File

@@ -437,10 +437,6 @@ class Server extends BaseModel
"mkdir -p $dynamic_config_path", "mkdir -p $dynamic_config_path",
"echo '$base64' | base64 -d | tee $file > /dev/null", "echo '$base64' | base64 -d | tee $file > /dev/null",
], $this); ], $this);
if (config('app.env') === 'local') {
// ray($yaml);
}
} }
} elseif ($this->proxyType() === 'CADDY') { } elseif ($this->proxyType() === 'CADDY') {
$file = "$dynamic_config_path/coolify.caddy"; $file = "$dynamic_config_path/coolify.caddy";
@@ -970,10 +966,8 @@ $schema://$host {
} }
}); });
if ($supported->count() === 1) { if ($supported->count() === 1) {
// ray('supported');
return str($supported->first()); return str($supported->first());
} else { } else {
// ray('not supported');
return false; return false;
} }
} }

View File

@@ -4,9 +4,9 @@ namespace App\Traits;
use App\Notifications\Channels\DiscordChannel; use App\Notifications\Channels\DiscordChannel;
use App\Notifications\Channels\EmailChannel; use App\Notifications\Channels\EmailChannel;
use App\Notifications\Channels\PushoverChannel;
use App\Notifications\Channels\SlackChannel; use App\Notifications\Channels\SlackChannel;
use App\Notifications\Channels\TelegramChannel; use App\Notifications\Channels\TelegramChannel;
use App\Notifications\Channels\PushoverChannel;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
trait HasNotificationSettings trait HasNotificationSettings

View File

@@ -91,8 +91,6 @@ function next_queuable(string $server_id, string $application_id): bool
$server = Server::find($server_id); $server = Server::find($server_id);
$concurrent_builds = $server->settings->concurrent_builds; $concurrent_builds = $server->settings->concurrent_builds;
// ray("serverId:{$server->id}", "concurrentBuilds:{$concurrent_builds}", "deployments:{$deployments->count()}", "sameApplicationDeployments:{$same_application_deployments->count()}")->green();
if ($deployments->count() > $concurrent_builds) { if ($deployments->count() > $concurrent_builds) {
return false; return false;
} }

View File

@@ -569,7 +569,7 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
if ($shouldGenerateLabelsExactly) { if ($shouldGenerateLabelsExactly) {
switch ($application->destination->server->proxyType()) { switch ($application->destination->server->proxyType()) {
case ProxyTypes::TRAEFIK->value: case ProxyTypes::TRAEFIK->value:
$labels = $labels->merge(fqdnLabelsForTraefik( $proxyLabels = fqdnLabelsForTraefik(
uuid: $appUuid, uuid: $appUuid,
domains: $domains, domains: $domains,
onlyPort: $onlyPort, onlyPort: $onlyPort,
@@ -577,10 +577,11 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
is_gzip_enabled: $application->isGzipEnabled(), is_gzip_enabled: $application->isGzipEnabled(),
is_stripprefix_enabled: $application->isStripprefixEnabled(), is_stripprefix_enabled: $application->isStripprefixEnabled(),
redirect_direction: $application->redirect redirect_direction: $application->redirect
)); );
$labels = $labels->merge(convertToKeyValueCollection($proxyLabels));
break; break;
case ProxyTypes::CADDY->value: case ProxyTypes::CADDY->value:
$labels = $labels->merge(fqdnLabelsForCaddy( $proxyLabels = fqdnLabelsForCaddy(
network: $application->destination->network, network: $application->destination->network,
uuid: $appUuid, uuid: $appUuid,
domains: $domains, domains: $domains,
@@ -589,11 +590,12 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
is_gzip_enabled: $application->isGzipEnabled(), is_gzip_enabled: $application->isGzipEnabled(),
is_stripprefix_enabled: $application->isStripprefixEnabled(), is_stripprefix_enabled: $application->isStripprefixEnabled(),
redirect_direction: $application->redirect redirect_direction: $application->redirect
)); );
$labels = $labels->merge(convertToKeyValueCollection($proxyLabels));
break; break;
} }
} else { } else {
$labels = $labels->merge(fqdnLabelsForTraefik( $proxyLabels = fqdnLabelsForTraefik(
uuid: $appUuid, uuid: $appUuid,
domains: $domains, domains: $domains,
onlyPort: $onlyPort, onlyPort: $onlyPort,
@@ -601,8 +603,8 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
is_gzip_enabled: $application->isGzipEnabled(), is_gzip_enabled: $application->isGzipEnabled(),
is_stripprefix_enabled: $application->isStripprefixEnabled(), is_stripprefix_enabled: $application->isStripprefixEnabled(),
redirect_direction: $application->redirect redirect_direction: $application->redirect
)); );
$labels = $labels->merge(fqdnLabelsForCaddy( $proxyLabels = fqdnLabelsForCaddy(
network: $application->destination->network, network: $application->destination->network,
uuid: $appUuid, uuid: $appUuid,
domains: $domains, domains: $domains,
@@ -611,7 +613,8 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
is_gzip_enabled: $application->isGzipEnabled(), is_gzip_enabled: $application->isGzipEnabled(),
is_stripprefix_enabled: $application->isStripprefixEnabled(), is_stripprefix_enabled: $application->isStripprefixEnabled(),
redirect_direction: $application->redirect redirect_direction: $application->redirect
)); );
$labels = $labels->merge(convertToKeyValueCollection($proxyLabels));
} }
} }
} else { } else {
@@ -624,17 +627,18 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
if ($shouldGenerateLabelsExactly) { if ($shouldGenerateLabelsExactly) {
switch ($application->destination->server->proxyType()) { switch ($application->destination->server->proxyType()) {
case ProxyTypes::TRAEFIK->value: case ProxyTypes::TRAEFIK->value:
$labels = $labels->merge(fqdnLabelsForTraefik( $proxyLabels = fqdnLabelsForTraefik(
uuid: $appUuid, uuid: $appUuid,
domains: $domains, domains: $domains,
onlyPort: $onlyPort, onlyPort: $onlyPort,
is_force_https_enabled: $application->isForceHttpsEnabled(), is_force_https_enabled: $application->isForceHttpsEnabled(),
is_gzip_enabled: $application->isGzipEnabled(), is_gzip_enabled: $application->isGzipEnabled(),
is_stripprefix_enabled: $application->isStripprefixEnabled() is_stripprefix_enabled: $application->isStripprefixEnabled()
)); );
$labels = $labels->merge(convertToKeyValueCollection($proxyLabels));
break; break;
case ProxyTypes::CADDY->value: case ProxyTypes::CADDY->value:
$labels = $labels->merge(fqdnLabelsForCaddy( $proxyLabels = fqdnLabelsForCaddy(
network: $application->destination->network, network: $application->destination->network,
uuid: $appUuid, uuid: $appUuid,
domains: $domains, domains: $domains,
@@ -642,19 +646,20 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
is_force_https_enabled: $application->isForceHttpsEnabled(), is_force_https_enabled: $application->isForceHttpsEnabled(),
is_gzip_enabled: $application->isGzipEnabled(), is_gzip_enabled: $application->isGzipEnabled(),
is_stripprefix_enabled: $application->isStripprefixEnabled() is_stripprefix_enabled: $application->isStripprefixEnabled()
)); );
$labels = $labels->merge(convertToKeyValueCollection($proxyLabels));
break; break;
} }
} else { } else {
$labels = $labels->merge(fqdnLabelsForTraefik( $proxyLabels = fqdnLabelsForTraefik(
uuid: $appUuid, uuid: $appUuid,
domains: $domains, domains: $domains,
onlyPort: $onlyPort, onlyPort: $onlyPort,
is_force_https_enabled: $application->isForceHttpsEnabled(), is_force_https_enabled: $application->isForceHttpsEnabled(),
is_gzip_enabled: $application->isGzipEnabled(), is_gzip_enabled: $application->isGzipEnabled(),
is_stripprefix_enabled: $application->isStripprefixEnabled() is_stripprefix_enabled: $application->isStripprefixEnabled()
)); );
$labels = $labels->merge(fqdnLabelsForCaddy( $proxyLabels = fqdnLabelsForCaddy(
network: $application->destination->network, network: $application->destination->network,
uuid: $appUuid, uuid: $appUuid,
domains: $domains, domains: $domains,
@@ -662,7 +667,8 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
is_force_https_enabled: $application->isForceHttpsEnabled(), is_force_https_enabled: $application->isForceHttpsEnabled(),
is_gzip_enabled: $application->isGzipEnabled(), is_gzip_enabled: $application->isGzipEnabled(),
is_stripprefix_enabled: $application->isStripprefixEnabled() is_stripprefix_enabled: $application->isStripprefixEnabled()
)); );
$labels = $labels->merge(convertToKeyValueCollection($proxyLabels));
} }
} }
@@ -778,7 +784,6 @@ function convertDockerRunToCompose(?string $custom_docker_run_options = null)
} }
} }
} }
ray($payload);
$compose_options->put('deploy', [ $compose_options->put('deploy', [
'resources' => [ 'resources' => [
'reservations' => [ 'reservations' => [
@@ -829,26 +834,20 @@ function generateCustomDockerRunOptionsForDatabases($docker_run_options, $docker
function validateComposeFile(string $compose, int $server_id): string|Throwable function validateComposeFile(string $compose, int $server_id): string|Throwable
{ {
return 'OK';
try {
$uuid = Str::random(10); $uuid = Str::random(10);
$server = Server::findOrFail($server_id); try {
$server = Server::ownedByCurrentTeam()->findOrFail($server_id);
$base64_compose = base64_encode($compose); $base64_compose = base64_encode($compose);
$output = instant_remote_process([ instant_remote_process([
"echo {$base64_compose} | base64 -d | tee /tmp/{$uuid}.yml > /dev/null", "echo {$base64_compose} | base64 -d | tee /tmp/{$uuid}.yml > /dev/null",
"docker compose -f /tmp/{$uuid}.yml config", "chmod 600 /tmp/{$uuid}.yml",
"docker compose -f /tmp/{$uuid}.yml config --no-interpolate --no-path-resolution -q",
"rm /tmp/{$uuid}.yml",
], $server); ], $server);
ray($output);
return 'OK'; return 'OK';
} catch (\Throwable $e) { } catch (\Throwable $e) {
ray($e);
return $e->getMessage(); return $e->getMessage();
} finally {
instant_remote_process([
"rm /tmp/{$uuid}.yml",
], $server);
} }
} }

View File

@@ -800,7 +800,6 @@ function parseEnvVariable(Str|string $value)
} else { } else {
// SERVICE_BASE64_64_UMAMI // SERVICE_BASE64_64_UMAMI
$command = $value->after('SERVICE_')->beforeLast('_'); $command = $value->after('SERVICE_')->beforeLast('_');
ray($command);
} }
} }
} }
@@ -952,7 +951,6 @@ function validate_dns_entry(string $fqdn, Server $server)
$type = \PurplePixie\PhpDns\DNSTypes::NAME_A; $type = \PurplePixie\PhpDns\DNSTypes::NAME_A;
foreach ($dns_servers as $dns_server) { foreach ($dns_servers as $dns_server) {
try { try {
ray("Checking $host on $dns_server");
$query = new DNSQuery($dns_server); $query = new DNSQuery($dns_server);
$results = $query->query($host, $type); $results = $query->query($host, $type);
if ($results === false || $query->hasError()) { if ($results === false || $query->hasError()) {
@@ -961,13 +959,10 @@ function validate_dns_entry(string $fqdn, Server $server)
foreach ($results as $result) { foreach ($results as $result) {
if ($result->getType() == $type) { if ($result->getType() == $type) {
if (ip_match($result->getData(), $cloudflare_ips->toArray(), $match)) { if (ip_match($result->getData(), $cloudflare_ips->toArray(), $match)) {
ray("Found match in Cloudflare IPs: $match");
$found_matching_ip = true; $found_matching_ip = true;
break; break;
} }
if ($result->getData() === $ip) { if ($result->getData() === $ip) {
ray($host.' has IP address '.$result->getData());
ray($result->getString());
$found_matching_ip = true; $found_matching_ip = true;
break; break;
} }
@@ -977,7 +972,6 @@ function validate_dns_entry(string $fqdn, Server $server)
} catch (\Exception) { } catch (\Exception) {
} }
} }
ray("Found match: $found_matching_ip");
return $found_matching_ip; return $found_matching_ip;
} }
@@ -1331,7 +1325,6 @@ function parseServiceVolumes($serviceVolumes, $resource, $topLevelVolumes, $pull
$isDirectory = (bool) data_get($volume, 'isDirectory', null) || (bool) data_get($volume, 'is_directory', null); $isDirectory = (bool) data_get($volume, 'isDirectory', null) || (bool) data_get($volume, 'is_directory', null);
if ((is_null($isDirectory) || ! $isDirectory) && is_null($content)) { if ((is_null($isDirectory) || ! $isDirectory) && is_null($content)) {
// if isDirectory is not set (or false) & content is also not set, we assume it is a directory // if isDirectory is not set (or false) & content is also not set, we assume it is a directory
ray('setting isDirectory to true');
$isDirectory = true; $isDirectory = true;
} }
} }
@@ -1483,6 +1476,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
$serviceNetworks = collect(data_get($service, 'networks', [])); $serviceNetworks = collect(data_get($service, 'networks', []));
$serviceVariables = collect(data_get($service, 'environment', [])); $serviceVariables = collect(data_get($service, 'environment', []));
$serviceLabels = collect(data_get($service, 'labels', [])); $serviceLabels = collect(data_get($service, 'labels', []));
$serviceLabels = convertToKeyValueCollection($serviceLabels);
$hasHostNetworkMode = data_get($service, 'network_mode') === 'host' ? true : false; $hasHostNetworkMode = data_get($service, 'network_mode') === 'host' ? true : false;
if ($serviceLabels->count() > 0) { if ($serviceLabels->count() > 0) {
$removedLabels = collect([]); $removedLabels = collect([]);
@@ -1499,7 +1493,6 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
$serviceLabels->push("$removedLabelName=$removedLabel"); $serviceLabels->push("$removedLabelName=$removedLabel");
} }
} }
$containerName = "$serviceName-{$resource->uuid}"; $containerName = "$serviceName-{$resource->uuid}";
// Decide if the service is a database // Decide if the service is a database
@@ -1662,7 +1655,6 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
} }
if (is_null($isDirectory) && is_null($content)) { if (is_null($isDirectory) && is_null($content)) {
// if isDirectory is not set & content is also not set, we assume it is a directory // if isDirectory is not set & content is also not set, we assume it is a directory
ray('setting isDirectory to true');
$isDirectory = true; $isDirectory = true;
} }
} }
@@ -2013,7 +2005,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
if ($shouldGenerateLabelsExactly) { if ($shouldGenerateLabelsExactly) {
switch ($resource->server->proxyType()) { switch ($resource->server->proxyType()) {
case ProxyTypes::TRAEFIK->value: case ProxyTypes::TRAEFIK->value:
$serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik( $proxyLabels = fqdnLabelsForTraefik(
uuid: $resource->uuid, uuid: $resource->uuid,
domains: $fqdns, domains: $fqdns,
is_force_https_enabled: true, is_force_https_enabled: true,
@@ -2022,10 +2014,12 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
is_stripprefix_enabled: $savedService->isStripprefixEnabled(), is_stripprefix_enabled: $savedService->isStripprefixEnabled(),
service_name: $serviceName, service_name: $serviceName,
image: data_get($service, 'image') image: data_get($service, 'image')
)); );
$serviceLabels = $serviceLabels->merge(convertToKeyValueCollection($proxyLabels));
break; break;
case ProxyTypes::CADDY->value: case ProxyTypes::CADDY->value:
$serviceLabels = $serviceLabels->merge(fqdnLabelsForCaddy( $proxyLabels = fqdnLabelsForCaddy(
network: $resource->destination->network, network: $resource->destination->network,
uuid: $resource->uuid, uuid: $resource->uuid,
domains: $fqdns, domains: $fqdns,
@@ -2035,11 +2029,13 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
is_stripprefix_enabled: $savedService->isStripprefixEnabled(), is_stripprefix_enabled: $savedService->isStripprefixEnabled(),
service_name: $serviceName, service_name: $serviceName,
image: data_get($service, 'image') image: data_get($service, 'image')
)); );
$serviceLabels = $serviceLabels->merge(convertToKeyValueCollection($proxyLabels));
break; break;
} }
} else { } else {
$serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik( $proxyLabels = fqdnLabelsForTraefik(
uuid: $resource->uuid, uuid: $resource->uuid,
domains: $fqdns, domains: $fqdns,
is_force_https_enabled: true, is_force_https_enabled: true,
@@ -2048,8 +2044,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
is_stripprefix_enabled: $savedService->isStripprefixEnabled(), is_stripprefix_enabled: $savedService->isStripprefixEnabled(),
service_name: $serviceName, service_name: $serviceName,
image: data_get($service, 'image') image: data_get($service, 'image')
)); );
$serviceLabels = $serviceLabels->merge(fqdnLabelsForCaddy( $proxyLabels = fqdnLabelsForCaddy(
network: $resource->destination->network, network: $resource->destination->network,
uuid: $resource->uuid, uuid: $resource->uuid,
domains: $fqdns, domains: $fqdns,
@@ -2059,7 +2055,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
is_stripprefix_enabled: $savedService->isStripprefixEnabled(), is_stripprefix_enabled: $savedService->isStripprefixEnabled(),
service_name: $serviceName, service_name: $serviceName,
image: data_get($service, 'image') image: data_get($service, 'image')
)); );
$serviceLabels = $serviceLabels->merge(convertToKeyValueCollection($proxyLabels));
} }
} }
} }
@@ -2208,6 +2205,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
$serviceVariables = collect(data_get($service, 'environment', [])); $serviceVariables = collect(data_get($service, 'environment', []));
$serviceDependencies = collect(data_get($service, 'depends_on', [])); $serviceDependencies = collect(data_get($service, 'depends_on', []));
$serviceLabels = collect(data_get($service, 'labels', [])); $serviceLabels = collect(data_get($service, 'labels', []));
$serviceLabels = convertToKeyValueCollection($serviceLabels);
$serviceBuildVariables = collect(data_get($service, 'build.args', [])); $serviceBuildVariables = collect(data_get($service, 'build.args', []));
$serviceVariables = $serviceVariables->merge($serviceBuildVariables); $serviceVariables = $serviceVariables->merge($serviceBuildVariables);
if ($serviceLabels->count() > 0) { if ($serviceLabels->count() > 0) {
@@ -2529,9 +2527,6 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
} }
} }
} }
if ($collectedPorts->count() > 0) {
ray($collectedPorts->implode(','));
}
$definedNetworkExists = $topLevelNetworks->contains(function ($value, $_) use ($definedNetwork) { $definedNetworkExists = $topLevelNetworks->contains(function ($value, $_) use ($definedNetwork) {
return $value == $definedNetwork; return $value == $definedNetwork;
}); });
@@ -2784,8 +2779,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
if ($shouldGenerateLabelsExactly) { if ($shouldGenerateLabelsExactly) {
switch ($server->proxyType()) { switch ($server->proxyType()) {
case ProxyTypes::TRAEFIK->value: case ProxyTypes::TRAEFIK->value:
$serviceLabels = $serviceLabels->merge( $proxyLabels = fqdnLabelsForTraefik(
fqdnLabelsForTraefik(
uuid: $resource->uuid, uuid: $resource->uuid,
domains: $fqdns, domains: $fqdns,
serviceLabels: $serviceLabels, serviceLabels: $serviceLabels,
@@ -2794,12 +2788,12 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
is_force_https_enabled: $resource->isForceHttpsEnabled(), is_force_https_enabled: $resource->isForceHttpsEnabled(),
is_gzip_enabled: $resource->isGzipEnabled(), is_gzip_enabled: $resource->isGzipEnabled(),
is_stripprefix_enabled: $resource->isStripprefixEnabled(), is_stripprefix_enabled: $resource->isStripprefixEnabled(),
)
); );
$serviceLabels = $serviceLabels->merge(convertToKeyValueCollection($proxyLabels));
break; break;
case ProxyTypes::CADDY->value: case ProxyTypes::CADDY->value:
$serviceLabels = $serviceLabels->merge( $proxyLabels = fqdnLabelsForCaddy(
fqdnLabelsForCaddy(
network: $resource->destination->network, network: $resource->destination->network,
uuid: $resource->uuid, uuid: $resource->uuid,
domains: $fqdns, domains: $fqdns,
@@ -2808,13 +2802,13 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
is_force_https_enabled: $resource->isForceHttpsEnabled(), is_force_https_enabled: $resource->isForceHttpsEnabled(),
is_gzip_enabled: $resource->isGzipEnabled(), is_gzip_enabled: $resource->isGzipEnabled(),
is_stripprefix_enabled: $resource->isStripprefixEnabled(), is_stripprefix_enabled: $resource->isStripprefixEnabled(),
)
); );
$serviceLabels = $serviceLabels->merge(convertToKeyValueCollection($proxyLabels));
break; break;
} }
} else { } else {
$serviceLabels = $serviceLabels->merge( $proxyLabels = fqdnLabelsForTraefik(
fqdnLabelsForTraefik(
uuid: $resource->uuid, uuid: $resource->uuid,
domains: $fqdns, domains: $fqdns,
serviceLabels: $serviceLabels, serviceLabels: $serviceLabels,
@@ -2823,9 +2817,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
is_force_https_enabled: $resource->isForceHttpsEnabled(), is_force_https_enabled: $resource->isForceHttpsEnabled(),
is_gzip_enabled: $resource->isGzipEnabled(), is_gzip_enabled: $resource->isGzipEnabled(),
is_stripprefix_enabled: $resource->isStripprefixEnabled(), is_stripprefix_enabled: $resource->isStripprefixEnabled(),
)
); );
$serviceLabels = $serviceLabels->merge( $proxyLabels =
fqdnLabelsForCaddy( fqdnLabelsForCaddy(
network: $resource->destination->network, network: $resource->destination->network,
uuid: $resource->uuid, uuid: $resource->uuid,
@@ -2835,8 +2828,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
is_force_https_enabled: $resource->isForceHttpsEnabled(), is_force_https_enabled: $resource->isForceHttpsEnabled(),
is_gzip_enabled: $resource->isGzipEnabled(), is_gzip_enabled: $resource->isGzipEnabled(),
is_stripprefix_enabled: $resource->isStripprefixEnabled(), is_stripprefix_enabled: $resource->isStripprefixEnabled(),
)
); );
$serviceLabels = $serviceLabels->merge(convertToKeyValueCollection($proxyLabels));
} }
} }
} }
@@ -2851,7 +2844,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
pull_request_id: $pull_request_id, pull_request_id: $pull_request_id,
type: 'application' type: 'application'
); );
$serviceLabels = $serviceLabels->merge($defaultLabels); $serviceLabels = $serviceLabels->merge(convertToKeyValueCollection($defaultLabels));
if ($server->isLogDrainEnabled()) { if ($server->isLogDrainEnabled()) {
if ($resource instanceof Application && $resource->isLogDrainEnabled()) { if ($resource instanceof Application && $resource->isLogDrainEnabled()) {
@@ -2956,7 +2949,6 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
} }
$parsedServices = collect([]); $parsedServices = collect([]);
// ray()->clearAll();
$allMagicEnvironments = collect([]); $allMagicEnvironments = collect([]);
foreach ($services as $serviceName => $service) { foreach ($services as $serviceName => $service) {
@@ -3016,7 +3008,7 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
$environment = $environment->merge($buildArgs); $environment = $environment->merge($buildArgs);
// convert environment variables to one format // convert environment variables to one format
$environment = convertComposeEnvironmentToArray($environment); $environment = convertToKeyValueCollection($environment);
// Add Coolify defined environments // Add Coolify defined environments
$allEnvironments = $resource->environment_variables()->get(['key', 'value']); $allEnvironments = $resource->environment_variables()->get(['key', 'value']);
@@ -3192,12 +3184,13 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
$use_network_mode = data_get($service, 'network_mode') !== null; $use_network_mode = data_get($service, 'network_mode') !== null;
$depends_on = collect(data_get($service, 'depends_on', [])); $depends_on = collect(data_get($service, 'depends_on', []));
$labels = collect(data_get($service, 'labels', [])); $labels = collect(data_get($service, 'labels', []));
$labels = convertToKeyValueCollection($labels);
$environment = collect(data_get($service, 'environment', [])); $environment = collect(data_get($service, 'environment', []));
$ports = collect(data_get($service, 'ports', [])); $ports = collect(data_get($service, 'ports', []));
$buildArgs = collect(data_get($service, 'build.args', [])); $buildArgs = collect(data_get($service, 'build.args', []));
$environment = $environment->merge($buildArgs); $environment = $environment->merge($buildArgs);
$environment = convertComposeEnvironmentToArray($environment); $environment = convertToKeyValueCollection($environment);
$coolifyEnvironments = collect([]); $coolifyEnvironments = collect([]);
$isDatabase = isDatabaseImage(data_get_str($service, 'image')); $isDatabase = isDatabaseImage(data_get_str($service, 'image'));
@@ -3704,7 +3697,7 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
return $value; return $value;
}); });
} }
$serviceLabels = $labels->merge($defaultLabels); $serviceLabels = $labels->merge(convertToKeyValueCollection($defaultLabels));
if ($serviceLabels->count() > 0) { if ($serviceLabels->count() > 0) {
if ($isApplication) { if ($isApplication) {
$isContainerLabelEscapeEnabled = data_get($resource, 'settings.is_container_label_escape_enabled'); $isContainerLabelEscapeEnabled = data_get($resource, 'settings.is_container_label_escape_enabled');
@@ -3736,7 +3729,7 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
if ($shouldGenerateLabelsExactly) { if ($shouldGenerateLabelsExactly) {
switch ($server->proxyType()) { switch ($server->proxyType()) {
case ProxyTypes::TRAEFIK->value: case ProxyTypes::TRAEFIK->value:
$serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik( $proxyLabels = fqdnLabelsForTraefik(
uuid: $uuid, uuid: $uuid,
domains: $fqdns, domains: $fqdns,
is_force_https_enabled: true, is_force_https_enabled: true,
@@ -3745,10 +3738,11 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
is_stripprefix_enabled: $originalResource->isStripprefixEnabled(), is_stripprefix_enabled: $originalResource->isStripprefixEnabled(),
service_name: $serviceName, service_name: $serviceName,
image: $image image: $image
)); );
$serviceLabels = $serviceLabels->merge(convertToKeyValueCollection($proxyLabels));
break; break;
case ProxyTypes::CADDY->value: case ProxyTypes::CADDY->value:
$serviceLabels = $serviceLabels->merge(fqdnLabelsForCaddy( $proxyLabels = fqdnLabelsForCaddy(
network: $network, network: $network,
uuid: $uuid, uuid: $uuid,
domains: $fqdns, domains: $fqdns,
@@ -3759,11 +3753,13 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
service_name: $serviceName, service_name: $serviceName,
image: $image, image: $image,
predefinedPort: $predefinedPort predefinedPort: $predefinedPort
)); );
$serviceLabels = $serviceLabels->merge(convertToKeyValueCollection($proxyLabels));
break; break;
} }
} else { } else {
$serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik( $proxyLabels = fqdnLabelsForTraefik(
uuid: $uuid, uuid: $uuid,
domains: $fqdns, domains: $fqdns,
is_force_https_enabled: true, is_force_https_enabled: true,
@@ -3772,8 +3768,8 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
is_stripprefix_enabled: $originalResource->isStripprefixEnabled(), is_stripprefix_enabled: $originalResource->isStripprefixEnabled(),
service_name: $serviceName, service_name: $serviceName,
image: $image image: $image
)); );
$serviceLabels = $serviceLabels->merge(fqdnLabelsForCaddy( $proxyLabels = $proxyLabels->merge(fqdnLabelsForCaddy(
network: $network, network: $network,
uuid: $uuid, uuid: $uuid,
domains: $fqdns, domains: $fqdns,
@@ -3785,6 +3781,7 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
image: $image, image: $image,
predefinedPort: $predefinedPort predefinedPort: $predefinedPort
)); ));
$serviceLabels = $serviceLabels->merge(convertToKeyValueCollection($proxyLabels));
} }
} }
if ($isService) { if ($isService) {
@@ -3934,7 +3931,7 @@ function add_coolify_default_environment_variables(StandaloneRedis|StandalonePos
} }
} }
function convertComposeEnvironmentToArray($environment) function convertToKeyValueCollection($environment)
{ {
$convertedServiceVariables = collect([]); $convertedServiceVariables = collect([]);
if (isAssociativeArray($environment)) { if (isAssociativeArray($environment)) {

View File

@@ -1,4 +1,5 @@
<?php <?php
/** /**
* @see https://github.com/pionl/laravel-chunk-upload * @see https://github.com/pionl/laravel-chunk-upload
*/ */

View File

@@ -20,17 +20,15 @@
</x-forms.textarea> </x-forms.textarea>
</div> </div>
<div class="pt-2 flex gap-2"> <div class="pt-2 flex gap-2">
<div class="flex flex-col gap-2">
<x-forms.checkbox label="Escape special characters in labels?" <x-forms.checkbox label="Escape special characters in labels?"
helper="By default, $ (and other chars) is escaped. So if you write $ in the labels, it will be saved as $$.<br><br>If you want to use env variables inside the labels, turn this off." helper="By default, $ (and other chars) is escaped. So if you write $ in the labels, it will be saved as $$.<br><br>If you want to use env variables inside the labels, turn this off."
id="service.is_container_label_escape_enabled" instantSave></x-forms.checkbox> id="service.is_container_label_escape_enabled" instantSave></x-forms.checkbox>
<div class="flex-1"></div> <x-forms.checkbox label="Show Normal Textarea" x-model="showNormalTextarea"></x-forms.checkbox>
<div x-cloak x-show="raw">
<x-forms.button class="w-32" @click.prevent="showNormalTextarea = !showNormalTextarea">Switch
Textarea</x-forms.button>
</div> </div>
</div> </div>
<div class="flex justify-end w-full gap-2 pt-4"> <div class="flex w-full gap-2 pt-4">
<div class="flex items-end gap-2">
<div x-cloak x-show="raw"> <div x-cloak x-show="raw">
<x-forms.button class="w-64" @click.prevent="raw = !raw">Show Deployable Compose</x-forms.button> <x-forms.button class="w-64" @click.prevent="raw = !raw">Show Deployable Compose</x-forms.button>
</div> </div>
@@ -38,9 +36,11 @@
<x-forms.button class="w-64" @click.prevent="raw = !raw">Show Source <x-forms.button class="w-64" @click.prevent="raw = !raw">Show Source
Compose</x-forms.button> Compose</x-forms.button>
</div> </div>
</div>
<div class="flex-1"></div> <div class="flex-1"></div>
<x-forms.button class="w-64" wire:click.prevent='saveEditedCompose'> <x-forms.button class="w-28" wire:click.prevent='validateCompose'>
Validate
</x-forms.button>
<x-forms.button class="w-28" wire:click.prevent='saveEditedCompose'>
Save Save
</x-forms.button> </x-forms.button>
</div> </div>

View File

@@ -16,7 +16,7 @@ class LoginTest extends DuskTestCase
* *
* @throws Throwable * @throws Throwable
*/ */
public function testLogin() public function test_login()
{ {
$this->browse(callback: function (Browser $browser) { $this->browse(callback: function (Browser $browser) {
$browser->loginWithRootUser() $browser->loginWithRootUser()

View File

@@ -16,7 +16,7 @@ class ProjectAddNewTest extends DuskTestCase
* *
* @throws Throwable * @throws Throwable
*/ */
public function testLogin() public function test_login()
{ {
$this->browse(function (Browser $browser) { $this->browse(function (Browser $browser) {
$browser->loginWithRootUser() $browser->loginWithRootUser()

View File

@@ -16,7 +16,7 @@ class ProjectSearchTest extends DuskTestCase
* *
* @throws Throwable * @throws Throwable
*/ */
public function testLogin() public function test_login()
{ {
$this->browse(function (Browser $browser) { $this->browse(function (Browser $browser) {
$browser->loginWithRootUser() $browser->loginWithRootUser()

View File

@@ -16,7 +16,7 @@ class ProjectTest extends DuskTestCase
* *
* @throws Throwable * @throws Throwable
*/ */
public function testLogin() public function test_login()
{ {
$this->browse(function (Browser $browser) { $this->browse(function (Browser $browser) {
$browser->loginWithRootUser() $browser->loginWithRootUser()

View File

@@ -358,7 +358,7 @@ use Symfony\Component\Yaml\Yaml;
// expect($output)->toContain('Docker version'); // expect($output)->toContain('Docker version');
// }); // });
// test('ConvertComposeEnvironmentToArray', function () { // test('convertToKeyValueCollection', function () {
// ray()->clearAll(); // ray()->clearAll();
// $yaml = ' // $yaml = '
// services: // services:
@@ -374,9 +374,9 @@ use Symfony\Component\Yaml\Yaml;
// - POSTGRES_DB: activepieces // - POSTGRES_DB: activepieces
// '; // ';
// $parsedYaml = Yaml::parse($yaml); // $parsedYaml = Yaml::parse($yaml);
// $output = convertComposeEnvironmentToArray($parsedYaml['services']['activepieces']['environment']); // $output = convertToKeyValueCollection($parsedYaml['services']['activepieces']['environment']);
// $output2 = convertComposeEnvironmentToArray($parsedYaml['services']['activepieces2']['environment']); // $output2 = convertToKeyValueCollection($parsedYaml['services']['activepieces2']['environment']);
// $dboutput = convertComposeEnvironmentToArray($parsedYaml['services']['postgres']['environment']); // $dboutput = convertToKeyValueCollection($parsedYaml['services']['postgres']['environment']);
// ray($output); // ray($output);
// ray($output2); // ray($output2);
// ray($dboutput); // ray($dboutput);

View File

@@ -1,4 +1,5 @@
<?php <?php
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Test Case | Test Case