parser parser
This commit is contained in:
@@ -1628,8 +1628,7 @@ class Application extends BaseModel
|
|||||||
{
|
{
|
||||||
if ($this->compose_parsing_version === '3') {
|
if ($this->compose_parsing_version === '3') {
|
||||||
return $this->newParser($pull_request_id, $preview_id);
|
return $this->newParser($pull_request_id, $preview_id);
|
||||||
} else
|
} elseif ($this->docker_compose_raw) {
|
||||||
if ($this->docker_compose_raw) {
|
|
||||||
return parseDockerComposeFile(resource: $this, isNew: false, pull_request_id: $pull_request_id, preview_id: $preview_id);
|
return parseDockerComposeFile(resource: $this, isNew: false, pull_request_id: $pull_request_id, preview_id: $preview_id);
|
||||||
} else {
|
} else {
|
||||||
return collect([]);
|
return collect([]);
|
||||||
|
@@ -2,11 +2,14 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Enums\ProxyTypes;
|
||||||
|
use App\Jobs\ServerFilesFromServerJob;
|
||||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
use OpenApi\Attributes as OA;
|
use OpenApi\Attributes as OA;
|
||||||
use Spatie\Url\Url;
|
use Spatie\Url\Url;
|
||||||
use Symfony\Component\Yaml\Yaml;
|
use Symfony\Component\Yaml\Yaml;
|
||||||
@@ -43,6 +46,14 @@ class Service extends BaseModel
|
|||||||
|
|
||||||
protected $appends = ['server_status'];
|
protected $appends = ['server_status'];
|
||||||
|
|
||||||
|
protected static function booted()
|
||||||
|
{
|
||||||
|
static::created(function ($service) {
|
||||||
|
$service->compose_parsing_version = '2';
|
||||||
|
$service->save();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public function isConfigurationChanged(bool $save = false)
|
public function isConfigurationChanged(bool $save = false)
|
||||||
{
|
{
|
||||||
$domains = $this->applications()->get()->pluck('fqdn')->sort()->toArray();
|
$domains = $this->applications()->get()->pluck('fqdn')->sort()->toArray();
|
||||||
@@ -981,9 +992,544 @@ class Service extends BaseModel
|
|||||||
instant_remote_process($commands, $this->server);
|
instant_remote_process($commands, $this->server);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function newParser()
|
||||||
|
{
|
||||||
|
return newParser($this);
|
||||||
|
|
||||||
|
$uuid = data_get($this, 'uuid');
|
||||||
|
$server = data_get($this, 'destination.server');
|
||||||
|
$compose = data_get($this, 'docker_compose_raw');
|
||||||
|
try {
|
||||||
|
$yaml = Yaml::parse($compose);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$allServices = get_service_templates();
|
||||||
|
$services = data_get($yaml, 'services', collect([]));
|
||||||
|
$topLevel = collect([
|
||||||
|
'volumes' => collect(data_get($yaml, 'volumes', [])),
|
||||||
|
'networks' => collect(data_get($yaml, 'networks', [])),
|
||||||
|
'configs' => collect(data_get($yaml, 'configs', [])),
|
||||||
|
'secrets' => collect(data_get($yaml, 'secrets', [])),
|
||||||
|
]);
|
||||||
|
// If there are predefined volumes, make sure they are not null
|
||||||
|
if ($topLevel->get('volumes')->count() > 0) {
|
||||||
|
$temp = collect([]);
|
||||||
|
foreach ($topLevel['volumes'] as $volumeName => $volume) {
|
||||||
|
if (is_null($volume)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$temp->put($volumeName, $volume);
|
||||||
|
}
|
||||||
|
$topLevel['volumes'] = $temp;
|
||||||
|
}
|
||||||
|
// Get the base docker network
|
||||||
|
$baseNetwork = collect([$uuid]);
|
||||||
|
$parsedServices = collect([]);
|
||||||
|
|
||||||
|
// Let's loop through the services
|
||||||
|
foreach ($services as $serviceName => $service) {
|
||||||
|
if ($serviceName === 'registry') {
|
||||||
|
$tempServiceName = 'docker-registry';
|
||||||
|
} else {
|
||||||
|
$tempServiceName = $serviceName;
|
||||||
|
}
|
||||||
|
if (str(data_get($service, 'image'))->contains('glitchtip')) {
|
||||||
|
$tempServiceName = 'glitchtip';
|
||||||
|
}
|
||||||
|
if ($serviceName === 'supabase-kong') {
|
||||||
|
$tempServiceName = 'supabase';
|
||||||
|
}
|
||||||
|
$serviceDefinition = data_get($allServices, $tempServiceName);
|
||||||
|
$predefinedPort = data_get($serviceDefinition, 'port');
|
||||||
|
if ($serviceName === 'plausible') {
|
||||||
|
$predefinedPort = '8000';
|
||||||
|
}
|
||||||
|
$image = data_get_str($service, 'image');
|
||||||
|
$restart = data_get_str($service, 'restart', RESTART_MODE);
|
||||||
|
$logging = data_get($service, 'logging');
|
||||||
|
|
||||||
|
if ($server->isLogDrainEnabled() && $this->isLogDrainEnabled()) {
|
||||||
|
$logging = [
|
||||||
|
'driver' => 'fluentd',
|
||||||
|
'options' => [
|
||||||
|
'fluentd-address' => 'tcp://127.0.0.1:24224',
|
||||||
|
'fluentd-async' => 'true',
|
||||||
|
'fluentd-sub-second-precision' => 'true',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$volumes = collect(data_get($service, 'volumes', []));
|
||||||
|
$networks = collect(data_get($service, 'networks', []));
|
||||||
|
$labels = collect(data_get($service, 'labels', []));
|
||||||
|
$environment = collect(data_get($service, 'environment', []));
|
||||||
|
$buildArgs = collect(data_get($service, 'build.args', []));
|
||||||
|
$environment = $environment->merge($buildArgs);
|
||||||
|
$hasHostNetworkMode = data_get($service, 'network_mode') === 'host' ? true : false;
|
||||||
|
|
||||||
|
$containerName = "$serviceName-{$this->uuid}";
|
||||||
|
$isDatabase = isDatabaseImage(data_get_str($service, 'image'));
|
||||||
|
$volumesParsed = collect([]);
|
||||||
|
|
||||||
|
if ($isDatabase) {
|
||||||
|
$savedService = ServiceDatabase::firstOrCreate([
|
||||||
|
'name' => $serviceName,
|
||||||
|
'image' => $image,
|
||||||
|
'service_id' => $this->id,
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$savedService = ServiceApplication::firstOrCreate([
|
||||||
|
'name' => $serviceName,
|
||||||
|
'image' => $image,
|
||||||
|
'service_id' => $this->id,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
$fileStorages = $savedService->fileStorages();
|
||||||
|
|
||||||
|
// Check if image changed
|
||||||
|
if ($savedService->image !== $image) {
|
||||||
|
$savedService->image = $image;
|
||||||
|
$savedService->save();
|
||||||
|
}
|
||||||
|
if ($volumes->count() > 0) {
|
||||||
|
foreach ($volumes as $index => $volume) {
|
||||||
|
$type = null;
|
||||||
|
$source = null;
|
||||||
|
$target = null;
|
||||||
|
$content = null;
|
||||||
|
$isDirectory = false;
|
||||||
|
if (is_string($volume)) {
|
||||||
|
$source = str($volume)->before(':');
|
||||||
|
$target = str($volume)->after(':')->beforeLast(':');
|
||||||
|
$foundConfig = $fileStorages->whereMountPath($target)->first();
|
||||||
|
if (sourceIsLocal($source)) {
|
||||||
|
$type = str('bind');
|
||||||
|
if ($foundConfig) {
|
||||||
|
$contentNotNull_temp = data_get($foundConfig, 'content');
|
||||||
|
if ($contentNotNull_temp) {
|
||||||
|
$content = $contentNotNull_temp;
|
||||||
|
}
|
||||||
|
$isDirectory = data_get($foundConfig, 'is_directory');
|
||||||
|
} else {
|
||||||
|
// By default, we cannot determine if the bind is a directory or not, so we set it to directory
|
||||||
|
$isDirectory = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$type = str('volume');
|
||||||
|
}
|
||||||
|
} elseif (is_array($volume)) {
|
||||||
|
$type = data_get_str($volume, 'type');
|
||||||
|
$source = data_get_str($volume, 'source');
|
||||||
|
$target = data_get_str($volume, 'target');
|
||||||
|
$content = data_get($volume, 'content');
|
||||||
|
$isDirectory = (bool) data_get($volume, 'isDirectory', null) || (bool) data_get($volume, 'is_directory', null);
|
||||||
|
|
||||||
|
$foundConfig = $fileStorages->whereMountPath($target)->first();
|
||||||
|
if ($foundConfig) {
|
||||||
|
$contentNotNull_temp = data_get($foundConfig, 'content');
|
||||||
|
if ($contentNotNull_temp) {
|
||||||
|
$content = $contentNotNull_temp;
|
||||||
|
}
|
||||||
|
$isDirectory = data_get($foundConfig, 'is_directory');
|
||||||
|
} else {
|
||||||
|
// if isDirectory is not set (or false) & content is also not set, we assume it is a directory
|
||||||
|
if ((is_null($isDirectory) || ! $isDirectory) && is_null($content)) {
|
||||||
|
$isDirectory = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($type->value() === 'bind') {
|
||||||
|
if ($source->value() === '/var/run/docker.sock') {
|
||||||
|
return $volume;
|
||||||
|
}
|
||||||
|
if ($source->value() === '/tmp' || $source->value() === '/tmp/') {
|
||||||
|
return $volume;
|
||||||
|
}
|
||||||
|
$mainDirectory = str(base_configuration_dir().'/applications/'.$uuid);
|
||||||
|
$source = replaceLocalSource($source, $mainDirectory);
|
||||||
|
|
||||||
|
LocalFileVolume::updateOrCreate(
|
||||||
|
[
|
||||||
|
'mount_path' => $target,
|
||||||
|
'resource_id' => $savedService->id,
|
||||||
|
'resource_type' => get_class($savedService),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'fs_path' => $source,
|
||||||
|
'mount_path' => $target,
|
||||||
|
'content' => $content,
|
||||||
|
'is_directory' => $isDirectory,
|
||||||
|
'resource_id' => $savedService->id,
|
||||||
|
'resource_type' => get_class($savedService),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
$volume = "$source:$target";
|
||||||
|
} elseif ($type->value() === 'volume') {
|
||||||
|
if ($topLevel->get('volumes')->has($source->value())) {
|
||||||
|
$temp = $topLevel->get('volumes')->get($source->value());
|
||||||
|
if (data_get($temp, 'driver_opts.type') === 'cifs') {
|
||||||
|
return $volume;
|
||||||
|
}
|
||||||
|
if (data_get($temp, 'driver_opts.type') === 'nfs') {
|
||||||
|
return $volume;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$slugWithoutUuid = Str::slug($source, '-');
|
||||||
|
$name = "{$uuid}_{$slugWithoutUuid}";
|
||||||
|
if (is_string($volume)) {
|
||||||
|
$source = str($volume)->before(':');
|
||||||
|
$target = str($volume)->after(':')->beforeLast(':');
|
||||||
|
$source = $name;
|
||||||
|
$volume = "$source:$target";
|
||||||
|
} elseif (is_array($volume)) {
|
||||||
|
data_set($volume, 'source', $name);
|
||||||
|
}
|
||||||
|
$topLevel->get('volumes')->put($name, [
|
||||||
|
'name' => $name,
|
||||||
|
]);
|
||||||
|
|
||||||
|
LocalPersistentVolume::updateOrCreate(
|
||||||
|
[
|
||||||
|
'mount_path' => $target,
|
||||||
|
'resource_id' => $savedService->id,
|
||||||
|
'resource_type' => get_class($savedService),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => $name,
|
||||||
|
'mount_path' => $target,
|
||||||
|
'resource_id' => $savedService->id,
|
||||||
|
'resource_type' => get_class($savedService),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
dispatch(new ServerFilesFromServerJob($savedService));
|
||||||
|
$volumesParsed->put($index, $volume);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($topLevel->get('networks')?->count() > 0) {
|
||||||
|
foreach ($topLevel->get('networks') as $networkName => $network) {
|
||||||
|
if ($networkName === 'default') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// ignore aliases
|
||||||
|
if ($network['aliases'] ?? false) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$networkExists = $networks->contains(function ($value, $key) use ($networkName) {
|
||||||
|
return $value == $networkName || $key == $networkName;
|
||||||
|
});
|
||||||
|
if (! $networkExists) {
|
||||||
|
$networks->put($networkName, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$baseNetworkExists = $networks->contains(function ($value, $_) use ($baseNetwork) {
|
||||||
|
return $value == $baseNetwork;
|
||||||
|
});
|
||||||
|
if (! $baseNetworkExists) {
|
||||||
|
foreach ($baseNetwork as $network) {
|
||||||
|
$topLevel->get('networks')->put($network, [
|
||||||
|
'name' => $network,
|
||||||
|
'external' => true,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$networks_temp = collect();
|
||||||
|
|
||||||
|
foreach ($networks as $key => $network) {
|
||||||
|
if (gettype($network) === 'string') {
|
||||||
|
// networks:
|
||||||
|
// - appwrite
|
||||||
|
$networks_temp->put($network, null);
|
||||||
|
} elseif (gettype($network) === 'array') {
|
||||||
|
// networks:
|
||||||
|
// default:
|
||||||
|
// ipv4_address: 192.168.203.254
|
||||||
|
$networks_temp->put($key, $network);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($baseNetwork as $key => $network) {
|
||||||
|
$networks_temp->put($network, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert
|
||||||
|
// - SESSION_SECRET: 123 to - SESSION_SECRET=123
|
||||||
|
$convertedServiceVariables = collect([]);
|
||||||
|
foreach ($environment as $variableName => $variable) {
|
||||||
|
if (is_numeric($variableName)) {
|
||||||
|
if (is_array($variable)) {
|
||||||
|
$key = str(collect($variable)->keys()->first());
|
||||||
|
$value = str(collect($variable)->values()->first());
|
||||||
|
$variable = "$key=$value";
|
||||||
|
$convertedServiceVariables->put($variableName, $variable);
|
||||||
|
} elseif (is_string($variable)) {
|
||||||
|
$convertedServiceVariables->put($variableName, $variable);
|
||||||
|
}
|
||||||
|
} elseif (is_string($variableName)) {
|
||||||
|
$convertedServiceVariables->put($variableName, $variable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$environment = $convertedServiceVariables;
|
||||||
|
|
||||||
|
// filter magic environments
|
||||||
|
$magicEnvironments = $environment->filter(function ($value, $key) {
|
||||||
|
return str($key)->startsWith('SERVICE_FQDN') || str($key)->startsWith('SERVICE_URL') || str($value)->startsWith('SERVICE_FQDN') || str($value)->startsWith('SERVICE_URL');
|
||||||
|
});
|
||||||
|
if ($magicEnvironments->count() > 0) {
|
||||||
|
foreach ($magicEnvironments as $key => $value) {
|
||||||
|
$key = str($key);
|
||||||
|
$value = str($value);
|
||||||
|
$command = $key->after('SERVICE_')->beforeLast('_');
|
||||||
|
if ($command->value() === 'FQDN') {
|
||||||
|
$fqdn = generateFqdn($server, "{$savedService->name}-{$uuid}");
|
||||||
|
if ($value && get_class($value) === 'Illuminate\Support\Stringable' && $value->startsWith('/')) {
|
||||||
|
$path = $value->value();
|
||||||
|
$value = "$fqdn$path";
|
||||||
|
} else {
|
||||||
|
$value = $fqdn;
|
||||||
|
}
|
||||||
|
} elseif ($command->value() === 'URL') {
|
||||||
|
$fqdn = generateFqdn($server, "{$savedService->name}-{$uuid}");
|
||||||
|
$value = str($fqdn)->replace('http://', '')->replace('https://', '')->replace('www.', '');
|
||||||
|
}
|
||||||
|
if (! $isDatabase && ! $this->environment_variables()->where('key', $key)->where('service_id', $this->id)->first()) {
|
||||||
|
$savedService->fqdn = $value;
|
||||||
|
$savedService->save();
|
||||||
|
}
|
||||||
|
$this->environment_variables()->where('key', $key)->where('service_id', $this->id)->firstOrCreate([
|
||||||
|
'key' => $key,
|
||||||
|
'service_id' => $this->id,
|
||||||
|
], [
|
||||||
|
'value' => $value,
|
||||||
|
'is_build_time' => false,
|
||||||
|
'is_preview' => false,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($environment as $key => $value) {
|
||||||
|
if (is_numeric($key)) {
|
||||||
|
if (is_array($value)) {
|
||||||
|
// - SESSION_SECRET: 123
|
||||||
|
// - SESSION_SECRET:
|
||||||
|
$key = str(collect($value)->keys()->first());
|
||||||
|
$value = str(collect($value)->values()->first());
|
||||||
|
} else {
|
||||||
|
$variable = str($value);
|
||||||
|
if ($variable->contains('=')) {
|
||||||
|
// - SESSION_SECRET=123
|
||||||
|
// - SESSION_SECRET=
|
||||||
|
$key = $variable->before('=');
|
||||||
|
$value = $variable->after('=');
|
||||||
|
} else {
|
||||||
|
// - SESSION_SECRET
|
||||||
|
$key = $variable;
|
||||||
|
$value = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// SESSION_SECRET: 123
|
||||||
|
// SESSION_SECRET:
|
||||||
|
$key = str($key);
|
||||||
|
$value = str($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto generate FQDN and URL
|
||||||
|
// environment:
|
||||||
|
// - SERVICE_FQDN_UMAMI=/umami
|
||||||
|
// - FQDN=$SERVICE_FQDN_UMAMI
|
||||||
|
// - URL=$SERVICE_URL_UMAMI
|
||||||
|
// - TEST=${TEST:-initial}
|
||||||
|
// - HARDCODED=stuff
|
||||||
|
|
||||||
|
if ($value->startsWith('$')) {
|
||||||
|
$value = str(replaceVariables($value));
|
||||||
|
if ($value->startsWith('SERVICE_')) {
|
||||||
|
// $value = SERVICE_FQDN_UMAMI
|
||||||
|
$command = $value->after('SERVICE_')->beforeLast('_');
|
||||||
|
if ($command->value() === 'FQDN') {
|
||||||
|
if ($magicEnvironments->has($value->value())) {
|
||||||
|
$found = $magicEnvironments->get($value->value());
|
||||||
|
if ($found) {
|
||||||
|
$found = $this->environment_variables()->where('key', $value->value())->where('service_id', $this->id)->first();
|
||||||
|
if ($found) {
|
||||||
|
$value = $found->value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$fqdn = generateFqdn($server, "{$savedService->name}-{$uuid}");
|
||||||
|
if ($value && get_class($value) === 'Illuminate\Support\Stringable' && $value->startsWith('/')) {
|
||||||
|
$path = $value->value();
|
||||||
|
$value = "$fqdn$path";
|
||||||
|
} else {
|
||||||
|
$value = $fqdn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} elseif ($command->value() === 'URL') {
|
||||||
|
if ($magicEnvironments->has($value->value())) {
|
||||||
|
$found = $magicEnvironments->get($value->value());
|
||||||
|
if ($found) {
|
||||||
|
$found = $this->environment_variables()->where('key', $value->value())->where('service_id', $this->id)->first();
|
||||||
|
if ($found) {
|
||||||
|
$value = str($found->value)->replace('http://', '')->replace('https://', '')->replace('www.', '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$fqdn = generateFqdn($server, "{$savedService->name}-{$uuid}");
|
||||||
|
$value = str($fqdn)->replace('http://', '')->replace('https://', '')->replace('www.', '');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$value = generateEnvValue($command, $this);
|
||||||
|
}
|
||||||
|
$this->environment_variables()->where('key', $key)->where('service_id', $this->id)->firstOrCreate([
|
||||||
|
'key' => $key,
|
||||||
|
'service_id' => $this->id,
|
||||||
|
], [
|
||||||
|
'value' => $value,
|
||||||
|
'is_build_time' => false,
|
||||||
|
'is_preview' => false,
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
if ($value->contains(':-')) {
|
||||||
|
$key = $value->before(':');
|
||||||
|
$value = $value->after(':-');
|
||||||
|
} elseif ($value->contains('-')) {
|
||||||
|
$key = $value->before('-');
|
||||||
|
$value = $value->after('-');
|
||||||
|
} elseif ($value->contains(':?')) {
|
||||||
|
$key = $value->before(':');
|
||||||
|
$value = $value->after(':?');
|
||||||
|
} elseif ($value->contains('?')) {
|
||||||
|
$key = $value->before('?');
|
||||||
|
$value = $value->after('?');
|
||||||
|
} else {
|
||||||
|
$key = $value;
|
||||||
|
$value = null;
|
||||||
|
}
|
||||||
|
$this->environment_variables()->where('key', $key)->where('service_id', $this->id)->firstOrCreate([
|
||||||
|
'key' => $key,
|
||||||
|
'service_id' => $this->id,
|
||||||
|
], [
|
||||||
|
'value' => $value,
|
||||||
|
'is_build_time' => false,
|
||||||
|
'is_preview' => false,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->environment_variables->where('key', 'COOLIFY_CONTAINER_NAME')->isEmpty()) {
|
||||||
|
$environment->put('COOLIFY_CONTAINER_NAME', $containerName);
|
||||||
|
}
|
||||||
|
// Remove SERVICE_FQDN and SERVICE_URL from environment
|
||||||
|
$environment = $environment->filter(function ($value, $key) {
|
||||||
|
return ! str($key)->startsWith('SERVICE_FQDN') && ! str($key)->startsWith('SERVICE_URL');
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
if ($savedService->serviceType()) {
|
||||||
|
$fqdns = generateServiceSpecificFqdns($savedService);
|
||||||
|
} else {
|
||||||
|
$fqdns = collect(data_get($savedService, 'fqdns'))->filter();
|
||||||
|
}
|
||||||
|
$defaultLabels = defaultLabels($this->id, $containerName, type: 'service', subType: $isDatabase ? 'database' : 'application', subId: $savedService->id);
|
||||||
|
$serviceLabels = $labels->merge($defaultLabels);
|
||||||
|
if (! $isDatabase && $fqdns->count() > 0) {
|
||||||
|
if ($fqdns) {
|
||||||
|
$shouldGenerateLabelsExactly = $this->server->settings->generate_exact_labels;
|
||||||
|
if ($shouldGenerateLabelsExactly) {
|
||||||
|
switch ($this->server->proxyType()) {
|
||||||
|
case ProxyTypes::TRAEFIK->value:
|
||||||
|
$serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik(
|
||||||
|
uuid: $this->uuid,
|
||||||
|
domains: $fqdns,
|
||||||
|
is_force_https_enabled: true,
|
||||||
|
serviceLabels: $serviceLabels,
|
||||||
|
is_gzip_enabled: $savedService->isGzipEnabled(),
|
||||||
|
is_stripprefix_enabled: $savedService->isStripprefixEnabled(),
|
||||||
|
service_name: $serviceName,
|
||||||
|
image: data_get($service, 'image')
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
case ProxyTypes::CADDY->value:
|
||||||
|
$serviceLabels = $serviceLabels->merge(fqdnLabelsForCaddy(
|
||||||
|
network: $this->destination->network,
|
||||||
|
uuid: $this->uuid,
|
||||||
|
domains: $fqdns,
|
||||||
|
is_force_https_enabled: true,
|
||||||
|
serviceLabels: $serviceLabels,
|
||||||
|
is_gzip_enabled: $savedService->isGzipEnabled(),
|
||||||
|
is_stripprefix_enabled: $savedService->isStripprefixEnabled(),
|
||||||
|
service_name: $serviceName,
|
||||||
|
image: data_get($service, 'image')
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik(
|
||||||
|
uuid: $this->uuid,
|
||||||
|
domains: $fqdns,
|
||||||
|
is_force_https_enabled: true,
|
||||||
|
serviceLabels: $serviceLabels,
|
||||||
|
is_gzip_enabled: $savedService->isGzipEnabled(),
|
||||||
|
is_stripprefix_enabled: $savedService->isStripprefixEnabled(),
|
||||||
|
service_name: $serviceName,
|
||||||
|
image: data_get($service, 'image')
|
||||||
|
));
|
||||||
|
$serviceLabels = $serviceLabels->merge(fqdnLabelsForCaddy(
|
||||||
|
network: $this->destination->network,
|
||||||
|
uuid: $this->uuid,
|
||||||
|
domains: $fqdns,
|
||||||
|
is_force_https_enabled: true,
|
||||||
|
serviceLabels: $serviceLabels,
|
||||||
|
is_gzip_enabled: $savedService->isGzipEnabled(),
|
||||||
|
is_stripprefix_enabled: $savedService->isStripprefixEnabled(),
|
||||||
|
service_name: $serviceName,
|
||||||
|
image: data_get($service, 'image')
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$payload = collect($service)->merge([
|
||||||
|
'restart' => $restart->value(),
|
||||||
|
'container_name' => $containerName,
|
||||||
|
'volumes' => $volumesParsed,
|
||||||
|
'networks' => $networks_temp,
|
||||||
|
'labels' => $serviceLabels,
|
||||||
|
'environment' => $environment,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($logging) {
|
||||||
|
$payload['logging'] = $logging;
|
||||||
|
}
|
||||||
|
|
||||||
|
$parsedServices->put($serviceName, $payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
$topLevel->put('services', $parsedServices);
|
||||||
|
$customOrder = ['services', 'volumes', 'networks', 'configs', 'secrets'];
|
||||||
|
|
||||||
|
$topLevel = $topLevel->sortBy(function ($value, $key) use ($customOrder) {
|
||||||
|
return array_search($key, $customOrder);
|
||||||
|
});
|
||||||
|
$this->docker_compose = Yaml::dump(convertToArray($topLevel), 10, 2);
|
||||||
|
data_forget($this, 'environment_variables');
|
||||||
|
data_forget($this, 'environment_variables_preview');
|
||||||
|
$this->save();
|
||||||
|
|
||||||
|
return $topLevel;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public function parse(bool $isNew = false): Collection
|
public function parse(bool $isNew = false): Collection
|
||||||
{
|
{
|
||||||
return parseDockerComposeFile($this, $isNew);
|
if ($this->compose_parsing_version === '3') {
|
||||||
|
return $this->newParser();
|
||||||
|
} elseif ($this->docker_compose_raw) {
|
||||||
|
return parseDockerComposeFile($this, $isNew);
|
||||||
|
} else {
|
||||||
|
return collect([]);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function networks()
|
public function networks()
|
||||||
|
@@ -478,7 +478,7 @@ function data_get_str($data, $key, $default = null): Stringable
|
|||||||
return str($str);
|
return str($str);
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateFqdn(Server $server, string $random)
|
function generateFqdn(Server $server, string $random): string
|
||||||
{
|
{
|
||||||
$wildcard = data_get($server, 'settings.wildcard_domain');
|
$wildcard = data_get($server, 'settings.wildcard_domain');
|
||||||
if (is_null($wildcard) || $wildcard === '') {
|
if (is_null($wildcard) || $wildcard === '') {
|
||||||
@@ -1746,6 +1746,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
|||||||
} elseif (is_string($variable)) {
|
} elseif (is_string($variable)) {
|
||||||
$convertedServiceVariables->put($variableName, $variable);
|
$convertedServiceVariables->put($variableName, $variable);
|
||||||
}
|
}
|
||||||
|
} elseif (is_string($variableName)) {
|
||||||
|
$convertedServiceVariables->put($variableName, $variable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$serviceVariables = $convertedServiceVariables;
|
$serviceVariables = $convertedServiceVariables;
|
||||||
@@ -1988,6 +1990,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
|||||||
} else {
|
} else {
|
||||||
$fqdns = collect(data_get($savedService, 'fqdns'))->filter();
|
$fqdns = collect(data_get($savedService, 'fqdns'))->filter();
|
||||||
}
|
}
|
||||||
|
ray($savedService);
|
||||||
$defaultLabels = defaultLabels($resource->id, $containerName, type: 'service', subType: $isDatabase ? 'database' : 'application', subId: $savedService->id);
|
$defaultLabels = defaultLabels($resource->id, $containerName, type: 'service', subType: $isDatabase ? 'database' : 'application', subId: $savedService->id);
|
||||||
$serviceLabels = $serviceLabels->merge($defaultLabels);
|
$serviceLabels = $serviceLabels->merge($defaultLabels);
|
||||||
if (! $isDatabase && $fqdns->count() > 0) {
|
if (! $isDatabase && $fqdns->count() > 0) {
|
||||||
@@ -2860,3 +2863,479 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
|||||||
return collect($finalServices);
|
return collect($finalServices);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function newParser(Application|Service $resource, int $pull_request_id = 0)
|
||||||
|
{
|
||||||
|
$isApplication = $resource instanceof Application;
|
||||||
|
$isService = $resource instanceof Service;
|
||||||
|
|
||||||
|
$uuid = data_get($resource, 'uuid');
|
||||||
|
$compose = data_get($resource, 'docker_compose_raw');
|
||||||
|
|
||||||
|
if ($isApplication) {
|
||||||
|
$nameOfId = 'application_id';
|
||||||
|
$pullRequestId = $pull_request_id;
|
||||||
|
$isPullRequest = $pullRequestId == 0 ? false : true;
|
||||||
|
$server = data_get($resource, 'destination.server');
|
||||||
|
$fileStorages = $resource->fileStorages();
|
||||||
|
} elseif ($isService) {
|
||||||
|
$nameOfId = 'service_id';
|
||||||
|
$server = data_get($resource, 'server');
|
||||||
|
$allServices = get_service_templates();
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$yaml = Yaml::parse($compose);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$services = data_get($yaml, 'services', collect([]));
|
||||||
|
$topLevel = collect([
|
||||||
|
'volumes' => collect(data_get($yaml, 'volumes', [])),
|
||||||
|
'networks' => collect(data_get($yaml, 'networks', [])),
|
||||||
|
'configs' => collect(data_get($yaml, 'configs', [])),
|
||||||
|
'secrets' => collect(data_get($yaml, 'secrets', [])),
|
||||||
|
]);
|
||||||
|
// If there are predefined volumes, make sure they are not null
|
||||||
|
if ($topLevel->get('volumes')->count() > 0) {
|
||||||
|
$temp = collect([]);
|
||||||
|
foreach ($topLevel['volumes'] as $volumeName => $volume) {
|
||||||
|
if (is_null($volume)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$temp->put($volumeName, $volume);
|
||||||
|
}
|
||||||
|
$topLevel['volumes'] = $temp;
|
||||||
|
}
|
||||||
|
// Get the base docker network
|
||||||
|
$baseNetwork = collect([$uuid]);
|
||||||
|
if ($isApplication && $isPullRequest) {
|
||||||
|
$baseNetwork = collect(["{$uuid}-{$pullRequestId}"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$parsedServices = collect([]);
|
||||||
|
foreach ($services as $serviceName => $service) {
|
||||||
|
$image = data_get_str($service, 'image');
|
||||||
|
$restart = data_get_str($service, 'restart', RESTART_MODE);
|
||||||
|
$logging = data_get($service, 'logging');
|
||||||
|
|
||||||
|
if ($server->isLogDrainEnabled() && $resource->isLogDrainEnabled()) {
|
||||||
|
$logging = [
|
||||||
|
'driver' => 'fluentd',
|
||||||
|
'options' => [
|
||||||
|
'fluentd-address' => 'tcp://127.0.0.1:24224',
|
||||||
|
'fluentd-async' => 'true',
|
||||||
|
'fluentd-sub-second-precision' => 'true',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
$volumes = collect(data_get($service, 'volumes', []));
|
||||||
|
$networks = collect(data_get($service, 'networks', []));
|
||||||
|
$depends_on = collect(data_get($service, 'depends_on', []));
|
||||||
|
$labels = collect(data_get($service, 'labels', []));
|
||||||
|
$environment = collect(data_get($service, 'environment', []));
|
||||||
|
$buildArgs = collect(data_get($service, 'build.args', []));
|
||||||
|
$environment = $environment->merge($buildArgs);
|
||||||
|
$isDatabase = isDatabaseImage(data_get_str($service, 'image'));
|
||||||
|
$volumesParsed = collect([]);
|
||||||
|
|
||||||
|
if ($isApplication) {
|
||||||
|
$baseName = generateApplicationContainerName(
|
||||||
|
application: $resource,
|
||||||
|
pull_request_id: $pullRequestId
|
||||||
|
);
|
||||||
|
$containerName = "$serviceName-$baseName";
|
||||||
|
} elseif ($isService) {
|
||||||
|
$containerName = "$serviceName-{$resource->uuid}";
|
||||||
|
|
||||||
|
if ($serviceName === 'registry') {
|
||||||
|
$tempServiceName = 'docker-registry';
|
||||||
|
} else {
|
||||||
|
$tempServiceName = $serviceName;
|
||||||
|
}
|
||||||
|
if (str(data_get($service, 'image'))->contains('glitchtip')) {
|
||||||
|
$tempServiceName = 'glitchtip';
|
||||||
|
}
|
||||||
|
if ($serviceName === 'supabase-kong') {
|
||||||
|
$tempServiceName = 'supabase';
|
||||||
|
}
|
||||||
|
$serviceDefinition = data_get($allServices, $tempServiceName);
|
||||||
|
$predefinedPort = data_get($serviceDefinition, 'port');
|
||||||
|
if ($serviceName === 'plausible') {
|
||||||
|
$predefinedPort = '8000';
|
||||||
|
}
|
||||||
|
if ($isDatabase) {
|
||||||
|
$savedService = ServiceDatabase::firstOrCreate([
|
||||||
|
'name' => $serviceName,
|
||||||
|
'image' => $image,
|
||||||
|
'service_id' => $resource->id,
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$savedService = ServiceApplication::firstOrCreate([
|
||||||
|
'name' => $serviceName,
|
||||||
|
'image' => $image,
|
||||||
|
'service_id' => $resource->id,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
$fileStorages = $savedService->fileStorages();
|
||||||
|
if ($savedService->image !== $image) {
|
||||||
|
$savedService->image = $image;
|
||||||
|
$savedService->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($volumes->count() > 0) {
|
||||||
|
foreach ($volumes as $index => $volume) {
|
||||||
|
$type = null;
|
||||||
|
$source = null;
|
||||||
|
$target = null;
|
||||||
|
$content = null;
|
||||||
|
$isDirectory = false;
|
||||||
|
if (is_string($volume)) {
|
||||||
|
$source = str($volume)->before(':');
|
||||||
|
$target = str($volume)->after(':')->beforeLast(':');
|
||||||
|
$foundConfig = $fileStorages->whereMountPath($target)->first();
|
||||||
|
if (sourceIsLocal($source)) {
|
||||||
|
$type = str('bind');
|
||||||
|
if ($foundConfig) {
|
||||||
|
$contentNotNull_temp = data_get($foundConfig, 'content');
|
||||||
|
if ($contentNotNull_temp) {
|
||||||
|
$content = $contentNotNull_temp;
|
||||||
|
}
|
||||||
|
$isDirectory = data_get($foundConfig, 'is_directory');
|
||||||
|
} else {
|
||||||
|
// By default, we cannot determine if the bind is a directory or not, so we set it to directory
|
||||||
|
$isDirectory = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$type = str('volume');
|
||||||
|
}
|
||||||
|
} elseif (is_array($volume)) {
|
||||||
|
$type = data_get_str($volume, 'type');
|
||||||
|
$source = data_get_str($volume, 'source');
|
||||||
|
$target = data_get_str($volume, 'target');
|
||||||
|
$content = data_get($volume, 'content');
|
||||||
|
$isDirectory = (bool) data_get($volume, 'isDirectory', null) || (bool) data_get($volume, 'is_directory', null);
|
||||||
|
|
||||||
|
$foundConfig = $fileStorages->whereMountPath($target)->first();
|
||||||
|
if ($foundConfig) {
|
||||||
|
$contentNotNull_temp = data_get($foundConfig, 'content');
|
||||||
|
if ($contentNotNull_temp) {
|
||||||
|
$content = $contentNotNull_temp;
|
||||||
|
}
|
||||||
|
$isDirectory = data_get($foundConfig, 'is_directory');
|
||||||
|
} else {
|
||||||
|
// if isDirectory is not set (or false) & content is also not set, we assume it is a directory
|
||||||
|
if ((is_null($isDirectory) || ! $isDirectory) && is_null($content)) {
|
||||||
|
$isDirectory = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($type->value() === 'bind') {
|
||||||
|
if ($source->value() === '/var/run/docker.sock') {
|
||||||
|
return $volume;
|
||||||
|
}
|
||||||
|
if ($source->value() === '/tmp' || $source->value() === '/tmp/') {
|
||||||
|
return $volume;
|
||||||
|
}
|
||||||
|
$mainDirectory = str(base_configuration_dir().'/applications/'.$uuid);
|
||||||
|
$source = replaceLocalSource($source, $mainDirectory);
|
||||||
|
|
||||||
|
LocalFileVolume::updateOrCreate(
|
||||||
|
[
|
||||||
|
'mount_path' => $target,
|
||||||
|
'resource_id' => $savedService->id,
|
||||||
|
'resource_type' => get_class($savedService),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'fs_path' => $source,
|
||||||
|
'mount_path' => $target,
|
||||||
|
'content' => $content,
|
||||||
|
'is_directory' => $isDirectory,
|
||||||
|
'resource_id' => $savedService->id,
|
||||||
|
'resource_type' => get_class($savedService),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
$volume = "$source:$target";
|
||||||
|
} elseif ($type->value() === 'volume') {
|
||||||
|
if ($topLevel->get('volumes')->has($source->value())) {
|
||||||
|
$temp = $topLevel->get('volumes')->get($source->value());
|
||||||
|
if (data_get($temp, 'driver_opts.type') === 'cifs') {
|
||||||
|
return $volume;
|
||||||
|
}
|
||||||
|
if (data_get($temp, 'driver_opts.type') === 'nfs') {
|
||||||
|
return $volume;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$slugWithoutUuid = Str::slug($source, '-');
|
||||||
|
$name = "{$uuid}_{$slugWithoutUuid}";
|
||||||
|
if (is_string($volume)) {
|
||||||
|
$source = str($volume)->before(':');
|
||||||
|
$target = str($volume)->after(':')->beforeLast(':');
|
||||||
|
$source = $name;
|
||||||
|
$volume = "$source:$target";
|
||||||
|
} elseif (is_array($volume)) {
|
||||||
|
data_set($volume, 'source', $name);
|
||||||
|
}
|
||||||
|
$topLevel->get('volumes')->put($name, [
|
||||||
|
'name' => $name,
|
||||||
|
]);
|
||||||
|
|
||||||
|
LocalPersistentVolume::updateOrCreate(
|
||||||
|
[
|
||||||
|
'mount_path' => $target,
|
||||||
|
'resource_id' => $savedService->id,
|
||||||
|
'resource_type' => get_class($savedService),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => $name,
|
||||||
|
'mount_path' => $target,
|
||||||
|
'resource_id' => $savedService->id,
|
||||||
|
'resource_type' => get_class($savedService),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
dispatch(new ServerFilesFromServerJob($savedService));
|
||||||
|
$volumesParsed->put($index, $volume);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($topLevel->get('networks')?->count() > 0) {
|
||||||
|
foreach ($topLevel->get('networks') as $networkName => $network) {
|
||||||
|
if ($networkName === 'default') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// ignore aliases
|
||||||
|
if ($network['aliases'] ?? false) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$networkExists = $networks->contains(function ($value, $key) use ($networkName) {
|
||||||
|
return $value == $networkName || $key == $networkName;
|
||||||
|
});
|
||||||
|
if (! $networkExists) {
|
||||||
|
$networks->put($networkName, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$baseNetworkExists = $networks->contains(function ($value, $_) use ($baseNetwork) {
|
||||||
|
return $value == $baseNetwork;
|
||||||
|
});
|
||||||
|
if (! $baseNetworkExists) {
|
||||||
|
foreach ($baseNetwork as $network) {
|
||||||
|
$topLevel->get('networks')->put($network, [
|
||||||
|
'name' => $network,
|
||||||
|
'external' => true,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$networks_temp = collect();
|
||||||
|
|
||||||
|
foreach ($networks as $key => $network) {
|
||||||
|
if (gettype($network) === 'string') {
|
||||||
|
// networks:
|
||||||
|
// - appwrite
|
||||||
|
$networks_temp->put($network, null);
|
||||||
|
} elseif (gettype($network) === 'array') {
|
||||||
|
// networks:
|
||||||
|
// default:
|
||||||
|
// ipv4_address: 192.168.203.254
|
||||||
|
$networks_temp->put($key, $network);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($baseNetwork as $key => $network) {
|
||||||
|
$networks_temp->put($network, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($isApplication) {
|
||||||
|
if (data_get($resource, 'settings.connect_to_docker_network')) {
|
||||||
|
$network = $resource->destination->network;
|
||||||
|
$networks_temp->put($network, null);
|
||||||
|
$topLevel->get('networks')->put($network, [
|
||||||
|
'name' => $network,
|
||||||
|
'external' => true,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// convert environment variables a different format
|
||||||
|
$convertedServiceVariables = collect([]);
|
||||||
|
foreach ($environment as $variableName => $variable) {
|
||||||
|
if (is_numeric($variableName)) {
|
||||||
|
if (is_array($variable)) {
|
||||||
|
$key = str(collect($variable)->keys()->first());
|
||||||
|
$value = str(collect($variable)->values()->first());
|
||||||
|
$variable = "$key=$value";
|
||||||
|
$convertedServiceVariables->put($variableName, $variable);
|
||||||
|
} elseif (is_string($variable)) {
|
||||||
|
if (is_numeric($variableName)) {
|
||||||
|
$convertedServiceVariables->put($variable, null);
|
||||||
|
} else {
|
||||||
|
$convertedServiceVariables->put($variableName, $variable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} elseif (is_string($variableName)) {
|
||||||
|
$convertedServiceVariables->put($variableName, $variable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$environment = $convertedServiceVariables;
|
||||||
|
|
||||||
|
// filter magic environments
|
||||||
|
$magicEnvironments = $environment->filter(function ($value, $key) {
|
||||||
|
$value = str(replaceVariables(str($value)));
|
||||||
|
|
||||||
|
return str($key)->startsWith('SERVICE_') || str($value)->startsWith('SERVICE_');
|
||||||
|
});
|
||||||
|
ray($magicEnvironments);
|
||||||
|
// TODO: go through all the magic environments and handle all kinds of cases, FQDN, URL, PASSWORD, USER, etc.
|
||||||
|
if ($magicEnvironments->count() > 0) {
|
||||||
|
foreach ($magicEnvironments as $key => $value) {
|
||||||
|
$key = str($key);
|
||||||
|
$value = str($value);
|
||||||
|
$command = $key->after('SERVICE_')->before('_');
|
||||||
|
if ($command->value() === 'FQDN') {
|
||||||
|
if ($isApplication) {
|
||||||
|
$fqdn = generateFqdn($server, "{$resource->name}-{$uuid}");
|
||||||
|
|
||||||
|
} elseif ($isService) {
|
||||||
|
$fqdn = generateFqdn($server, "{$savedService->name}-{$uuid}");
|
||||||
|
}
|
||||||
|
if ($value && get_class($value) === 'Illuminate\Support\Stringable' && $value->startsWith('/')) {
|
||||||
|
$path = $value->value();
|
||||||
|
$value = "$fqdn$path";
|
||||||
|
} else {
|
||||||
|
$value = $fqdn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$resource->environment_variables()->where('key', $key)->where($nameOfId, $resource->id)->firstOrCreate([
|
||||||
|
'key' => $key,
|
||||||
|
$nameOfId => $resource->id,
|
||||||
|
], [
|
||||||
|
'value' => $value,
|
||||||
|
'is_build_time' => false,
|
||||||
|
'is_preview' => false,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($environment as $key => $value) {
|
||||||
|
if (is_numeric($key)) {
|
||||||
|
if (is_array($value)) {
|
||||||
|
// - SESSION_SECRET: 123
|
||||||
|
// - SESSION_SECRET:
|
||||||
|
$key = str(collect($value)->keys()->first());
|
||||||
|
$value = str(collect($value)->values()->first());
|
||||||
|
} else {
|
||||||
|
$variable = str($value);
|
||||||
|
if ($variable->contains('=')) {
|
||||||
|
// - SESSION_SECRET=123
|
||||||
|
// - SESSION_SECRET=
|
||||||
|
$key = $variable->before('=');
|
||||||
|
$value = $variable->after('=');
|
||||||
|
} else {
|
||||||
|
// - SESSION_SECRET
|
||||||
|
$key = $variable;
|
||||||
|
$value = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// SESSION_SECRET: 123
|
||||||
|
// SESSION_SECRET:
|
||||||
|
$key = str($key);
|
||||||
|
$value = str($value);
|
||||||
|
}
|
||||||
|
// Auto generate FQDN and URL
|
||||||
|
// environment:
|
||||||
|
// - SERVICE_FQDN_UMAMI=/umami
|
||||||
|
// - FQDN=$SERVICE_FQDN_UMAMI
|
||||||
|
// - URL=$SERVICE_URL_UMAMI
|
||||||
|
// - TEST=${TEST:-initial}
|
||||||
|
// - HARDCODED=stuff
|
||||||
|
if ($value->startsWith('$')) {
|
||||||
|
$value = str(replaceVariables($value));
|
||||||
|
if ($value->startsWith('SERVICE_')) {
|
||||||
|
$command = $value->after('SERVICE_')->before('_');
|
||||||
|
if ($command->value() === 'FQDN') {
|
||||||
|
if ($magicEnvironments->has($value->value())) {
|
||||||
|
$found = $magicEnvironments->get($value->value());
|
||||||
|
if ($found) {
|
||||||
|
$found = $resource->environment_variables()->where('key', $value->value())->where($nameOfId, $resource->id)->first();
|
||||||
|
if ($found) {
|
||||||
|
$value = $found->value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ($isApplication) {
|
||||||
|
$fqdn = generateFqdn($server, "{$resource->name}-{$uuid}");
|
||||||
|
|
||||||
|
} elseif ($isService) {
|
||||||
|
$fqdn = generateFqdn($server, "{$savedService->name}-{$uuid}");
|
||||||
|
}
|
||||||
|
if ($value && get_class($value) === 'Illuminate\Support\Stringable' && $value->startsWith('/')) {
|
||||||
|
$path = $value->value();
|
||||||
|
$value = "$fqdn$path";
|
||||||
|
} else {
|
||||||
|
$value = $fqdn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} elseif ($command->value() === 'URL') {
|
||||||
|
if ($magicEnvironments->has($value->value())) {
|
||||||
|
$found = $magicEnvironments->get($value->value());
|
||||||
|
if ($found) {
|
||||||
|
$found = $resource->environment_variables()->where('key', $value->value())->where($nameOfId, $resource->id)->first();
|
||||||
|
if ($found) {
|
||||||
|
$value = $found->value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ($isApplication) {
|
||||||
|
$fqdn = generateFqdn($server, "{$resource->name}-{$uuid}");
|
||||||
|
} elseif ($isService) {
|
||||||
|
$fqdn = generateFqdn($server, "{$savedService->name}-{$uuid}");
|
||||||
|
}
|
||||||
|
$value = str($fqdn)->replace('http://', '')->replace('https://', '')->replace('www.', '');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$value = generateEnvValue($command, $resource);
|
||||||
|
}
|
||||||
|
$resource->environment_variables()->where('key', $key)->where($nameOfId, $resource->id)->firstOrCreate([
|
||||||
|
'key' => $key,
|
||||||
|
$nameOfId => $resource->id,
|
||||||
|
], [
|
||||||
|
'value' => $value,
|
||||||
|
'is_build_time' => false,
|
||||||
|
'is_preview' => false,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$payload = collect($service)->merge([
|
||||||
|
'restart' => $restart->value(),
|
||||||
|
'container_name' => $containerName,
|
||||||
|
'volumes' => $volumesParsed,
|
||||||
|
'networks' => $networks_temp,
|
||||||
|
'labels' => $labels,
|
||||||
|
'environment' => $environment,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($logging) {
|
||||||
|
$payload['logging'] = $logging;
|
||||||
|
}
|
||||||
|
if ($depends_on->count() > 0) {
|
||||||
|
$payload['depends_on'] = $depends_on;
|
||||||
|
}
|
||||||
|
if ($isApplication && $isPullRequest) {
|
||||||
|
$serviceName = "{$serviceName}-pr-{$pullRequestId}";
|
||||||
|
}
|
||||||
|
$parsedServices->put($serviceName, $payload);
|
||||||
|
}
|
||||||
|
$topLevel->put('services', $parsedServices);
|
||||||
|
$customOrder = ['services', 'volumes', 'networks', 'configs', 'secrets'];
|
||||||
|
|
||||||
|
$topLevel = $topLevel->sortBy(function ($value, $key) use ($customOrder) {
|
||||||
|
return array_search($key, $customOrder);
|
||||||
|
});
|
||||||
|
$resource->docker_compose = Yaml::dump(convertToArray($topLevel), 10, 2);
|
||||||
|
data_forget($resource, 'environment_variables');
|
||||||
|
data_forget($resource, 'environment_variables_preview');
|
||||||
|
$resource->save();
|
||||||
|
|
||||||
|
return $topLevel;
|
||||||
|
}
|
||||||
|
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('services', function (Blueprint $table) {
|
||||||
|
$table->string('compose_parsing_version')->default('2');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('services', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('compose_parsing_version');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
@@ -2,6 +2,7 @@
|
|||||||
# slogan: Open source no-code business automation.
|
# slogan: Open source no-code business automation.
|
||||||
# tags: workflow, automation, no code, open source
|
# tags: workflow, automation, no code, open source
|
||||||
# logo: svgs/activepieces.png
|
# logo: svgs/activepieces.png
|
||||||
|
# port: 80
|
||||||
|
|
||||||
services:
|
services:
|
||||||
activepieces:
|
activepieces:
|
||||||
|
File diff suppressed because one or more lines are too long
@@ -3,14 +3,15 @@
|
|||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Models\GithubApp;
|
use App\Models\GithubApp;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
|
use App\Models\Service;
|
||||||
use App\Models\StandaloneDocker;
|
use App\Models\StandaloneDocker;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
use Symfony\Component\Yaml\Yaml;
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
use Visus\Cuid2\Cuid2;
|
||||||
|
|
||||||
ray()->clearAll();
|
ray()->clearAll();
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
$this->composeFile = [
|
$this->applicationComposeFile = [
|
||||||
'version' => '3.8',
|
'version' => '3.8',
|
||||||
'services' => [
|
'services' => [
|
||||||
'app' => [
|
'app' => [
|
||||||
@@ -64,8 +65,8 @@ beforeEach(function () {
|
|||||||
],
|
],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
$this->composeFileString = Yaml::dump($this->composeFile, 10, 2);
|
$this->applicationComposeFileString = Yaml::dump($this->applicationComposeFile, 10, 2);
|
||||||
$this->jsonComposeFile = json_encode($this->composeFile, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR);
|
$this->jsonapplicationComposeFile = json_encode($this->applicationComposeFile, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR);
|
||||||
|
|
||||||
$this->application = Application::create([
|
$this->application = Application::create([
|
||||||
'name' => 'Application for tests',
|
'name' => 'Application for tests',
|
||||||
@@ -75,7 +76,7 @@ beforeEach(function () {
|
|||||||
'git_branch' => 'main',
|
'git_branch' => 'main',
|
||||||
'base_directory' => '/docker-compose-test',
|
'base_directory' => '/docker-compose-test',
|
||||||
'docker_compose_location' => 'docker-compose.yml',
|
'docker_compose_location' => 'docker-compose.yml',
|
||||||
'docker_compose_raw' => $this->composeFileString,
|
'docker_compose_raw' => $this->applicationComposeFileString,
|
||||||
'build_pack' => 'dockercompose',
|
'build_pack' => 'dockercompose',
|
||||||
'ports_exposes' => '3000',
|
'ports_exposes' => '3000',
|
||||||
'environment_id' => 1,
|
'environment_id' => 1,
|
||||||
@@ -84,181 +85,306 @@ beforeEach(function () {
|
|||||||
'source_id' => 1,
|
'source_id' => 1,
|
||||||
'source_type' => GithubApp::class,
|
'source_type' => GithubApp::class,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$this->serviceComposeFile = [
|
||||||
|
'services' => [
|
||||||
|
'activepieces' => [
|
||||||
|
'image' => 'ghcr.io/activepieces/activepieces:latest',
|
||||||
|
'environment' => [
|
||||||
|
'SERVICE_FQDN_ACTIVEPIECES_80' => '/app',
|
||||||
|
'AP_API_KEY' => '$SERVICE_PASSWORD_64_APIKEY',
|
||||||
|
'AP_ENCRYPTION_KEY' => '$SERVICE_PASSWORD_ENCRYPTIONKEY',
|
||||||
|
'AP_ENGINE_EXECUTABLE_PATH' => 'dist/packages/engine/main.js',
|
||||||
|
'AP_ENVIRONMENT' => 'prod',
|
||||||
|
'AP_EXECUTION_MODE' => 'UNSANDBOXED',
|
||||||
|
'AP_FRONTEND_URL' => '$SERVICE_FQDN_ACTIVEPIECES',
|
||||||
|
'AP_JWT_SECRET' => '$SERVICE_PASSWORD_64_JWT',
|
||||||
|
'AP_POSTGRES_DATABASE' => 'activepieces',
|
||||||
|
'AP_POSTGRES_HOST' => 'postgres',
|
||||||
|
'AP_POSTGRES_PASSWORD' => '$SERVICE_PASSWORD_POSTGRES',
|
||||||
|
'AP_POSTGRES_PORT' => '5432',
|
||||||
|
'AP_POSTGRES_USERNAME' => '$SERVICE_USER_POSTGRES',
|
||||||
|
'AP_REDIS_HOST' => 'redis',
|
||||||
|
'AP_REDIS_PORT' => '6379',
|
||||||
|
'AP_SANDBOX_RUN_TIME_SECONDS' => '600',
|
||||||
|
'AP_TELEMETRY_ENABLED' => 'true',
|
||||||
|
'AP_TEMPLATES_SOURCE_URL' => 'https://cloud.activepieces.com/api/v1/flow-templates',
|
||||||
|
'AP_TRIGGER_DEFAULT_POLL_INTERVAL' => '5',
|
||||||
|
'AP_WEBHOOK_TIMEOUT_SECONDS' => '30',
|
||||||
|
'AP_TEST' => '${AP_TEST:-test}',
|
||||||
|
|
||||||
|
],
|
||||||
|
'depends_on' => [
|
||||||
|
'postgres' => [
|
||||||
|
'condition' => 'service_healthy',
|
||||||
|
],
|
||||||
|
'redis' => [
|
||||||
|
'condition' => 'service_started',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'healthcheck' => [
|
||||||
|
'test' => [
|
||||||
|
'CMD',
|
||||||
|
'curl',
|
||||||
|
'-f',
|
||||||
|
'http://127.0.0.1:80',
|
||||||
|
],
|
||||||
|
'interval' => '5s',
|
||||||
|
'timeout' => '20s',
|
||||||
|
'retries' => 10,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'postgres' => [
|
||||||
|
'image' => 'postgres:latest',
|
||||||
|
'environment' => [
|
||||||
|
'POSTGRES_DB' => 'activepieces',
|
||||||
|
'POSTGRES_PASSWORD' => '$SERVICE_PASSWORD_POSTGRES',
|
||||||
|
'POSTGRES_USER' => '$SERVICE_USER_POSTGRES',
|
||||||
|
],
|
||||||
|
'volumes' => [
|
||||||
|
'dbdata:/var/lib/postgresql/data',
|
||||||
|
],
|
||||||
|
'healthcheck' => [
|
||||||
|
'test' => [
|
||||||
|
'CMD-SHELL',
|
||||||
|
'pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}',
|
||||||
|
],
|
||||||
|
'interval' => '5s',
|
||||||
|
'timeout' => '20s',
|
||||||
|
'retries' => 10,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'redis' => [
|
||||||
|
'image' => 'redis:latest',
|
||||||
|
'volumes' => [
|
||||||
|
'redis_data:/data',
|
||||||
|
],
|
||||||
|
'healthcheck' => [
|
||||||
|
'test' => [
|
||||||
|
'CMD',
|
||||||
|
'redis-cli',
|
||||||
|
'ping',
|
||||||
|
],
|
||||||
|
'interval' => '5s',
|
||||||
|
'timeout' => '20s',
|
||||||
|
'retries' => 10,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
$this->serviceComposeFileString = Yaml::dump($this->serviceComposeFile, 10, 2);
|
||||||
|
$this->jsonServiceComposeFile = json_encode($this->serviceComposeFile, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR);
|
||||||
|
|
||||||
|
$this->service = Service::create([
|
||||||
|
'name' => 'Service for tests',
|
||||||
|
'uuid' => (string) new Cuid2(),
|
||||||
|
'docker_compose_raw' => $this->serviceComposeFileString,
|
||||||
|
'environment_id' => 1,
|
||||||
|
'server_id' => 0,
|
||||||
|
'destination_id' => 0,
|
||||||
|
'destination_type' => StandaloneDocker::class,
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function () {
|
afterEach(function () {
|
||||||
$this->application->forceDelete();
|
$this->application->forceDelete();
|
||||||
|
$this->service->forceDelete();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('ComposeParse', function () {
|
// test('ApplicationComposeParse', function () {
|
||||||
expect($this->jsonComposeFile)->toBeJson()->ray();
|
// expect($this->jsonapplicationComposeFile)->toBeJson()->ray();
|
||||||
|
|
||||||
$output = $this->application->newParser();
|
// $output = $this->application->newParser();
|
||||||
$outputOld = $this->application->oldParser();
|
// $outputOld = $this->application->oldParser();
|
||||||
|
// expect($output)->toBeInstanceOf(Collection::class);
|
||||||
|
// expect($outputOld)->toBeInstanceOf(Collection::class);
|
||||||
|
|
||||||
|
// $services = $output->get('services');
|
||||||
|
// $servicesCount = count($this->applicationComposeFile['services']);
|
||||||
|
// expect($services)->toHaveCount($servicesCount);
|
||||||
|
|
||||||
|
// $app = $services->get('app');
|
||||||
|
// expect($app)->not->toBeNull();
|
||||||
|
|
||||||
|
// $db = $services->get('db');
|
||||||
|
// expect($db)->not->toBeNull();
|
||||||
|
|
||||||
|
// $appDependsOn = $app->get('depends_on');
|
||||||
|
// expect($appDependsOn)->toContain('db');
|
||||||
|
|
||||||
|
// $dbDependsOn = $db->get('depends_on');
|
||||||
|
|
||||||
|
// expect($dbDependsOn->keys()->first())->toContain('app');
|
||||||
|
// expect(data_get($dbDependsOn, 'app.condition'))->toBe('service_healthy');
|
||||||
|
|
||||||
|
// $environment = $app->get('environment');
|
||||||
|
// expect($environment)->not->toBeNull();
|
||||||
|
|
||||||
|
// $coolifyBranch = $environment->get('COOLIFY_BRANCH');
|
||||||
|
// expect($coolifyBranch)->toBe('main');
|
||||||
|
|
||||||
|
// $coolifyContainerName = $environment->get('COOLIFY_CONTAINER_NAME');
|
||||||
|
// expect($coolifyContainerName)->toMatch('/app-[a-z0-9]{24}-[0-9]{12}/');
|
||||||
|
|
||||||
|
// $volumes = $app->get('volumes');
|
||||||
|
// // /etc/nginx
|
||||||
|
// $fileMount = $volumes->get(0);
|
||||||
|
// $applicationConfigurationDir = application_configuration_dir();
|
||||||
|
// expect($fileMount)->toBe("{$applicationConfigurationDir}/{$this->application->uuid}/nginx:/etc/nginx");
|
||||||
|
|
||||||
|
// // data:/var/www/html
|
||||||
|
// $volumeMount = $volumes->get(1);
|
||||||
|
// expect($volumeMount)->toBe("{$this->application->uuid}_data:/var/www/html");
|
||||||
|
|
||||||
|
// $containerName = $app->get('container_name');
|
||||||
|
// expect($containerName)->toMatch('/app-[a-z0-9]{24}-[0-9]{12}/');
|
||||||
|
|
||||||
|
// $labels = $app->get('labels');
|
||||||
|
// expect($labels)->not->toBeNull();
|
||||||
|
// expect($labels)->toContain('coolify.managed=true');
|
||||||
|
// expect($labels)->toContain('coolify.pullRequestId=0');
|
||||||
|
|
||||||
|
// $topLevelVolumes = $output->get('volumes');
|
||||||
|
// expect($topLevelVolumes)->not->toBeNull();
|
||||||
|
// $firstVolume = $topLevelVolumes->first();
|
||||||
|
// expect(data_get($firstVolume, 'name'))->toBe("{$this->application->uuid}_data");
|
||||||
|
|
||||||
|
// $topLevelNetworks = $output->get('networks');
|
||||||
|
// expect($topLevelNetworks)->not->toBeNull();
|
||||||
|
// $defaultNetwork = data_get($topLevelNetworks, 'default');
|
||||||
|
// expect($defaultNetwork)->not->toBeNull();
|
||||||
|
// expect(data_get($defaultNetwork, 'name'))->toBe('something');
|
||||||
|
// expect(data_get($defaultNetwork, 'external'))->toBe(true);
|
||||||
|
|
||||||
|
// $noinetNetwork = data_get($topLevelNetworks, 'noinet');
|
||||||
|
// expect($noinetNetwork)->not->toBeNull();
|
||||||
|
// expect(data_get($noinetNetwork, 'driver'))->toBe('bridge');
|
||||||
|
// expect(data_get($noinetNetwork, 'internal'))->toBe(true);
|
||||||
|
|
||||||
|
// $serviceNetwork = data_get($topLevelNetworks, "{$this->application->uuid}");
|
||||||
|
// expect($serviceNetwork)->not->toBeNull();
|
||||||
|
// expect(data_get($serviceNetwork, 'name'))->toBe("{$this->application->uuid}");
|
||||||
|
// expect(data_get($serviceNetwork, 'external'))->toBe(true);
|
||||||
|
|
||||||
|
// });
|
||||||
|
|
||||||
|
// test('ApplicationComposeParsePreviewDeployment', function () {
|
||||||
|
// $pullRequestId = 1;
|
||||||
|
// $previewId = 77;
|
||||||
|
// expect($this->jsonapplicationComposeFile)->toBeJson()->ray();
|
||||||
|
|
||||||
|
// $output = $this->application->newParser(pull_request_id: $pullRequestId, preview_id: $previewId);
|
||||||
|
// $outputOld = $this->application->oldParser();
|
||||||
|
// expect($output)->toBeInstanceOf(Collection::class);
|
||||||
|
// expect($outputOld)->toBeInstanceOf(Collection::class);
|
||||||
|
|
||||||
|
// ray(Yaml::dump($output->toArray(), 10, 2));
|
||||||
|
// $services = $output->get('services');
|
||||||
|
// $servicesCount = count($this->applicationComposeFile['services']);
|
||||||
|
// expect($services)->toHaveCount($servicesCount);
|
||||||
|
|
||||||
|
// $appNull = $services->get('app');
|
||||||
|
// expect($appNull)->toBeNull();
|
||||||
|
|
||||||
|
// $dbNull = $services->get('db');
|
||||||
|
// expect($dbNull)->toBeNull();
|
||||||
|
|
||||||
|
// $app = $services->get("app-pr-{$pullRequestId}");
|
||||||
|
// expect($app)->not->toBeNull();
|
||||||
|
|
||||||
|
// $db = $services->get("db-pr-{$pullRequestId}");
|
||||||
|
// expect($db)->not->toBeNull();
|
||||||
|
|
||||||
|
// $appDependsOn = $app->get('depends_on');
|
||||||
|
// expect($appDependsOn)->toContain('db-pr-'.$pullRequestId);
|
||||||
|
|
||||||
|
// $dbDependsOn = $db->get('depends_on');
|
||||||
|
|
||||||
|
// expect($dbDependsOn->keys()->first())->toContain('app-pr-'.$pullRequestId);
|
||||||
|
// expect(data_get($dbDependsOn, 'app-pr-'.$pullRequestId.'.condition'))->toBe('service_healthy');
|
||||||
|
|
||||||
|
// $environment = $app->get('environment');
|
||||||
|
// expect($environment)->not->toBeNull();
|
||||||
|
|
||||||
|
// $coolifyBranch = $environment->get('COOLIFY_BRANCH');
|
||||||
|
// expect($coolifyBranch)->toBe("pull/{$pullRequestId}/head");
|
||||||
|
|
||||||
|
// $coolifyContainerName = $environment->get('COOLIFY_CONTAINER_NAME');
|
||||||
|
// expect($coolifyContainerName)->toMatch("/app-[a-z0-9]{24}-pr-{$pullRequestId}/");
|
||||||
|
|
||||||
|
// $volumes = $app->get('volumes');
|
||||||
|
// // /etc/nginx
|
||||||
|
// $fileMount = $volumes->get(0);
|
||||||
|
// $applicationConfigurationDir = application_configuration_dir();
|
||||||
|
// expect($fileMount)->toBe("{$applicationConfigurationDir}/{$this->application->uuid}/nginx-pr-{$pullRequestId}:/etc/nginx");
|
||||||
|
|
||||||
|
// // data:/var/www/html
|
||||||
|
// $volumeMount = $volumes->get(1);
|
||||||
|
// expect($volumeMount)->toBe("{$this->application->uuid}_data-pr-{$pullRequestId}:/var/www/html");
|
||||||
|
|
||||||
|
// $containerName = $app->get('container_name');
|
||||||
|
// expect($containerName)->toMatch("/app-[a-z0-9]{24}-pr-{$pullRequestId}/");
|
||||||
|
|
||||||
|
// $labels = $app->get('labels');
|
||||||
|
// expect($labels)->not->toBeNull();
|
||||||
|
// expect($labels)->toContain('coolify.managed=true');
|
||||||
|
// expect($labels)->toContain("coolify.pullRequestId={$pullRequestId}");
|
||||||
|
|
||||||
|
// $topLevelVolumes = $output->get('volumes');
|
||||||
|
// expect($topLevelVolumes)->not->toBeNull();
|
||||||
|
// $firstVolume = $topLevelVolumes->first();
|
||||||
|
// expect(data_get($firstVolume, 'name'))->toBe("{$this->application->uuid}_data-pr-{$pullRequestId}");
|
||||||
|
|
||||||
|
// $topLevelNetworks = $output->get('networks');
|
||||||
|
// expect($topLevelNetworks)->not->toBeNull();
|
||||||
|
// $defaultNetwork = data_get($topLevelNetworks, 'default');
|
||||||
|
// expect($defaultNetwork)->not->toBeNull();
|
||||||
|
// expect(data_get($defaultNetwork, 'name'))->toBe('something');
|
||||||
|
// expect(data_get($defaultNetwork, 'external'))->toBe(true);
|
||||||
|
|
||||||
|
// $noinetNetwork = data_get($topLevelNetworks, 'noinet');
|
||||||
|
// expect($noinetNetwork)->not->toBeNull();
|
||||||
|
// expect(data_get($noinetNetwork, 'driver'))->toBe('bridge');
|
||||||
|
// expect(data_get($noinetNetwork, 'internal'))->toBe(true);
|
||||||
|
|
||||||
|
// $serviceNetwork = data_get($topLevelNetworks, "{$this->application->uuid}-{$pullRequestId}");
|
||||||
|
// expect($serviceNetwork)->not->toBeNull();
|
||||||
|
// expect(data_get($serviceNetwork, 'name'))->toBe("{$this->application->uuid}-{$pullRequestId}");
|
||||||
|
// expect(data_get($serviceNetwork, 'external'))->toBe(true);
|
||||||
|
|
||||||
|
// });
|
||||||
|
|
||||||
|
test('ServiceComposeParseNew', function () {
|
||||||
|
ray()->clearAll();
|
||||||
|
$output = $this->service->newParser();
|
||||||
|
// ray('New parser');
|
||||||
|
// ray(data_get($output, 'services.activepieces.environment')->toArray());
|
||||||
|
ray($this->service->environment_variables->pluck('value', 'key')->toArray());
|
||||||
|
// foreach ($this->service->applications as $application) {
|
||||||
|
// ray($application->persistentStorages->pluck('mount_path', 'name')->toArray());
|
||||||
|
// }
|
||||||
|
// foreach ($this->service->databases as $database) {
|
||||||
|
// ray($database->persistentStorages->pluck('mount_path', 'name')->toArray());
|
||||||
|
// }
|
||||||
expect($output)->toBeInstanceOf(Collection::class);
|
expect($output)->toBeInstanceOf(Collection::class);
|
||||||
expect($outputOld)->toBeInstanceOf(Collection::class);
|
|
||||||
|
|
||||||
ray(Yaml::dump($output->toArray(), 10, 2));
|
|
||||||
$services = $output->get('services');
|
|
||||||
$servicesCount = count($this->composeFile['services']);
|
|
||||||
expect($services)->toHaveCount($servicesCount);
|
|
||||||
|
|
||||||
$app = $services->get("app");
|
|
||||||
expect($app)->not->toBeNull();
|
|
||||||
|
|
||||||
$db = $services->get("db");
|
|
||||||
expect($db)->not->toBeNull();
|
|
||||||
|
|
||||||
$appDependsOn = $app->get('depends_on');
|
|
||||||
expect($appDependsOn)->toContain('db');
|
|
||||||
|
|
||||||
$dbDependsOn = $db->get('depends_on');
|
|
||||||
|
|
||||||
expect($dbDependsOn->keys()->first())->toContain('app');
|
|
||||||
expect(data_get($dbDependsOn, 'app.condition'))->toBe('service_healthy');
|
|
||||||
|
|
||||||
|
|
||||||
$environment = $app->get('environment');
|
|
||||||
expect($environment)->not->toBeNull();
|
|
||||||
|
|
||||||
$coolifyBranch = $environment->get('COOLIFY_BRANCH');
|
|
||||||
expect($coolifyBranch)->toBe("main");
|
|
||||||
|
|
||||||
$coolifyContainerName = $environment->get('COOLIFY_CONTAINER_NAME');
|
|
||||||
expect($coolifyContainerName)->toMatch("/app-[a-z0-9]{24}-[0-9]{12}/");
|
|
||||||
|
|
||||||
$volumes = $app->get('volumes');
|
|
||||||
// /etc/nginx
|
|
||||||
$fileMount = $volumes->get(0);
|
|
||||||
$applicationConfigurationDir = application_configuration_dir();
|
|
||||||
expect($fileMount)->toBe("{$applicationConfigurationDir}/{$this->application->uuid}/nginx:/etc/nginx");
|
|
||||||
|
|
||||||
// data:/var/www/html
|
|
||||||
$volumeMount = $volumes->get(1);
|
|
||||||
expect($volumeMount)->toBe("{$this->application->uuid}_data:/var/www/html");
|
|
||||||
|
|
||||||
$containerName = $app->get('container_name');
|
|
||||||
expect($containerName)->toMatch("/app-[a-z0-9]{24}-[0-9]{12}/");
|
|
||||||
|
|
||||||
$labels = $app->get('labels');
|
|
||||||
expect($labels)->not->toBeNull();
|
|
||||||
expect($labels)->toContain('coolify.managed=true');
|
|
||||||
expect($labels)->toContain("coolify.pullRequestId=0");
|
|
||||||
|
|
||||||
$topLevelVolumes = $output->get('volumes');
|
|
||||||
expect($topLevelVolumes)->not->toBeNull();
|
|
||||||
$firstVolume = $topLevelVolumes->first();
|
|
||||||
expect(data_get($firstVolume, 'name'))->toBe("{$this->application->uuid}_data");
|
|
||||||
|
|
||||||
$topLevelNetworks = $output->get('networks');
|
|
||||||
expect($topLevelNetworks)->not->toBeNull();
|
|
||||||
$defaultNetwork = data_get($topLevelNetworks, 'default');
|
|
||||||
expect($defaultNetwork)->not->toBeNull();
|
|
||||||
expect(data_get($defaultNetwork, 'name'))->toBe('something');
|
|
||||||
expect(data_get($defaultNetwork, 'external'))->toBe(true);
|
|
||||||
|
|
||||||
$noinetNetwork = data_get($topLevelNetworks, 'noinet');
|
|
||||||
expect($noinetNetwork)->not->toBeNull();
|
|
||||||
expect(data_get($noinetNetwork, 'driver'))->toBe('bridge');
|
|
||||||
expect(data_get($noinetNetwork, 'internal'))->toBe(true);
|
|
||||||
|
|
||||||
$serviceNetwork = data_get($topLevelNetworks, "{$this->application->uuid}");
|
|
||||||
expect($serviceNetwork)->not->toBeNull();
|
|
||||||
expect(data_get($serviceNetwork, 'name'))->toBe("{$this->application->uuid}");
|
|
||||||
expect(data_get($serviceNetwork, 'external'))->toBe(true);
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// test('ServiceComposeParseOld', function () {
|
||||||
|
// $output = parseDockerComposeFile($this->service);
|
||||||
|
// ray('Old parser');
|
||||||
|
// ray($output->toArray());
|
||||||
|
// ray($this->service->environment_variables->pluck('value', 'key')->toArray());
|
||||||
|
// foreach ($this->service->applications as $application) {
|
||||||
|
// ray($application->persistentStorages->pluck('mount_path', 'name')->toArray());
|
||||||
|
// }
|
||||||
|
// foreach ($this->service->databases as $database) {
|
||||||
|
// ray($database->persistentStorages->pluck('mount_path', 'name')->toArray());
|
||||||
|
// }
|
||||||
|
// expect($output)->toBeInstanceOf(Collection::class);
|
||||||
|
// });
|
||||||
|
|
||||||
test('ComposeParsePreviewDeployment', function () {
|
// test('DockerBinaryAvailableOnLocalhost', function () {
|
||||||
$pullRequestId = 1;
|
// $server = Server::find(0);
|
||||||
$previewId = 77;
|
// $output = instant_remote_process(['docker --version'], $server);
|
||||||
expect($this->jsonComposeFile)->toBeJson()->ray();
|
// expect($output)->toContain('Docker version');
|
||||||
|
// });
|
||||||
$output = $this->application->newParser(pull_request_id: $pullRequestId, preview_id: $previewId);
|
|
||||||
$outputOld = $this->application->oldParser();
|
|
||||||
expect($output)->toBeInstanceOf(Collection::class);
|
|
||||||
expect($outputOld)->toBeInstanceOf(Collection::class);
|
|
||||||
|
|
||||||
ray(Yaml::dump($output->toArray(), 10, 2));
|
|
||||||
$services = $output->get('services');
|
|
||||||
$servicesCount = count($this->composeFile['services']);
|
|
||||||
expect($services)->toHaveCount($servicesCount);
|
|
||||||
|
|
||||||
$appNull = $services->get('app');
|
|
||||||
expect($appNull)->toBeNull();
|
|
||||||
|
|
||||||
$dbNull = $services->get('db');
|
|
||||||
expect($dbNull)->toBeNull();
|
|
||||||
|
|
||||||
$app = $services->get("app-pr-{$pullRequestId}");
|
|
||||||
expect($app)->not->toBeNull();
|
|
||||||
|
|
||||||
$db = $services->get("db-pr-{$pullRequestId}");
|
|
||||||
expect($db)->not->toBeNull();
|
|
||||||
|
|
||||||
$appDependsOn = $app->get('depends_on');
|
|
||||||
expect($appDependsOn)->toContain('db-pr-'.$pullRequestId);
|
|
||||||
|
|
||||||
$dbDependsOn = $db->get('depends_on');
|
|
||||||
|
|
||||||
expect($dbDependsOn->keys()->first())->toContain('app-pr-'.$pullRequestId);
|
|
||||||
expect(data_get($dbDependsOn, 'app-pr-'.$pullRequestId.'.condition'))->toBe('service_healthy');
|
|
||||||
|
|
||||||
|
|
||||||
$environment = $app->get('environment');
|
|
||||||
expect($environment)->not->toBeNull();
|
|
||||||
|
|
||||||
$coolifyBranch = $environment->get('COOLIFY_BRANCH');
|
|
||||||
expect($coolifyBranch)->toBe("pull/{$pullRequestId}/head");
|
|
||||||
|
|
||||||
$coolifyContainerName = $environment->get('COOLIFY_CONTAINER_NAME');
|
|
||||||
expect($coolifyContainerName)->toMatch("/app-[a-z0-9]{24}-pr-{$pullRequestId}/");
|
|
||||||
|
|
||||||
$volumes = $app->get('volumes');
|
|
||||||
// /etc/nginx
|
|
||||||
$fileMount = $volumes->get(0);
|
|
||||||
$applicationConfigurationDir = application_configuration_dir();
|
|
||||||
expect($fileMount)->toBe("{$applicationConfigurationDir}/{$this->application->uuid}/nginx-pr-{$pullRequestId}:/etc/nginx");
|
|
||||||
|
|
||||||
// data:/var/www/html
|
|
||||||
$volumeMount = $volumes->get(1);
|
|
||||||
expect($volumeMount)->toBe("{$this->application->uuid}_data-pr-{$pullRequestId}:/var/www/html");
|
|
||||||
|
|
||||||
$containerName = $app->get('container_name');
|
|
||||||
expect($containerName)->toMatch("/app-[a-z0-9]{24}-pr-{$pullRequestId}/");
|
|
||||||
|
|
||||||
$labels = $app->get('labels');
|
|
||||||
expect($labels)->not->toBeNull();
|
|
||||||
expect($labels)->toContain('coolify.managed=true');
|
|
||||||
expect($labels)->toContain("coolify.pullRequestId={$pullRequestId}");
|
|
||||||
|
|
||||||
$topLevelVolumes = $output->get('volumes');
|
|
||||||
expect($topLevelVolumes)->not->toBeNull();
|
|
||||||
$firstVolume = $topLevelVolumes->first();
|
|
||||||
expect(data_get($firstVolume, 'name'))->toBe("{$this->application->uuid}_data-pr-{$pullRequestId}");
|
|
||||||
|
|
||||||
$topLevelNetworks = $output->get('networks');
|
|
||||||
expect($topLevelNetworks)->not->toBeNull();
|
|
||||||
$defaultNetwork = data_get($topLevelNetworks, 'default');
|
|
||||||
expect($defaultNetwork)->not->toBeNull();
|
|
||||||
expect(data_get($defaultNetwork, 'name'))->toBe('something');
|
|
||||||
expect(data_get($defaultNetwork, 'external'))->toBe(true);
|
|
||||||
|
|
||||||
$noinetNetwork = data_get($topLevelNetworks, 'noinet');
|
|
||||||
expect($noinetNetwork)->not->toBeNull();
|
|
||||||
expect(data_get($noinetNetwork, 'driver'))->toBe('bridge');
|
|
||||||
expect(data_get($noinetNetwork, 'internal'))->toBe(true);
|
|
||||||
|
|
||||||
$serviceNetwork = data_get($topLevelNetworks, "{$this->application->uuid}-{$pullRequestId}");
|
|
||||||
expect($serviceNetwork)->not->toBeNull();
|
|
||||||
expect(data_get($serviceNetwork, 'name'))->toBe("{$this->application->uuid}-{$pullRequestId}");
|
|
||||||
expect(data_get($serviceNetwork, 'external'))->toBe(true);
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
test('DockerBinaryAvailableOnLocalhost', function () {
|
|
||||||
$server = Server::find(0);
|
|
||||||
$output = instant_remote_process(['docker --version'], $server);
|
|
||||||
expect($output)->toContain('Docker version');
|
|
||||||
});
|
|
||||||
|
Reference in New Issue
Block a user