commit
d7dfeaf988
@ -11,29 +11,34 @@ class StopApplication
|
||||
public function handle(Application $application)
|
||||
{
|
||||
$server = $application->destination->server;
|
||||
$containers = getCurrentApplicationContainerStatus($server, $application->id, 0);
|
||||
if ($containers->count() > 0) {
|
||||
foreach ($containers as $container) {
|
||||
$containerName = data_get($container, 'Names');
|
||||
if ($containerName) {
|
||||
instant_remote_process(
|
||||
["docker rm -f {$containerName}"],
|
||||
$server
|
||||
);
|
||||
if ($server->isSwarm()) {
|
||||
instant_remote_process(["docker stack rm {$application->uuid}" ], $server);
|
||||
} else {
|
||||
$containers = getCurrentApplicationContainerStatus($server, $application->id, 0);
|
||||
if ($containers->count() > 0) {
|
||||
foreach ($containers as $container) {
|
||||
$containerName = data_get($container, 'Names');
|
||||
if ($containerName) {
|
||||
instant_remote_process(
|
||||
["docker rm -f {$containerName}"],
|
||||
$server
|
||||
);
|
||||
}
|
||||
}
|
||||
// TODO: make notification for application
|
||||
// $application->environment->project->team->notify(new StatusChanged($application));
|
||||
}
|
||||
// TODO: make notification for application
|
||||
// $application->environment->project->team->notify(new StatusChanged($application));
|
||||
}
|
||||
// Delete Preview Deployments
|
||||
$previewDeployments = $application->previews;
|
||||
foreach ($previewDeployments as $previewDeployment) {
|
||||
$containers = getCurrentApplicationContainerStatus($server, $application->id, $previewDeployment->pull_request_id);
|
||||
foreach ($containers as $container) {
|
||||
$name = str_replace('/', '', $container['Names']);
|
||||
instant_remote_process(["docker rm -f $name"], $application->destination->server, throwError: false);
|
||||
// Delete Preview Deployments
|
||||
$previewDeployments = $application->previews;
|
||||
foreach ($previewDeployments as $previewDeployment) {
|
||||
$containers = getCurrentApplicationContainerStatus($server, $application->id, $previewDeployment->pull_request_id);
|
||||
foreach ($containers as $container) {
|
||||
$name = str_replace('/', '', $container['Names']);
|
||||
instant_remote_process(["docker rm -f $name"], $application->destination->server, throwError: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -17,35 +17,42 @@ public function handle(Server $server, $fromUI = false)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
$status = getContainerStatus($server, 'coolify-proxy');
|
||||
if ($status === 'running') {
|
||||
$server->proxy->set('status', 'running');
|
||||
if ($server->isSwarm()) {
|
||||
$status = getContainerStatus($server, 'coolify-proxy_traefik');
|
||||
$server->proxy->set('status', $status);
|
||||
$server->save();
|
||||
return false;
|
||||
}
|
||||
$ip = $server->ip;
|
||||
if ($server->id === 0) {
|
||||
$ip = 'host.docker.internal';
|
||||
}
|
||||
} else {
|
||||
$status = getContainerStatus($server, 'coolify-proxy');
|
||||
if ($status === 'running') {
|
||||
$server->proxy->set('status', 'running');
|
||||
$server->save();
|
||||
return false;
|
||||
}
|
||||
$ip = $server->ip;
|
||||
if ($server->id === 0) {
|
||||
$ip = 'host.docker.internal';
|
||||
}
|
||||
|
||||
$connection80 = @fsockopen($ip, '80');
|
||||
$connection443 = @fsockopen($ip, '443');
|
||||
$port80 = is_resource($connection80) && fclose($connection80);
|
||||
$port443 = is_resource($connection443) && fclose($connection443);
|
||||
if ($port80) {
|
||||
if ($fromUI) {
|
||||
throw new \Exception("Port 80 is in use.<br>You must stop the process using this port.<br>Docs: <a target='_blank' href='https://coolify.io/docs'>https://coolify.io/docs</a> <br> Discord: <a target='_blank' href='https://coollabs.io/discord'>https://coollabs.io/discord</a>");
|
||||
} else {
|
||||
return false;
|
||||
$connection80 = @fsockopen($ip, '80');
|
||||
$connection443 = @fsockopen($ip, '443');
|
||||
$port80 = is_resource($connection80) && fclose($connection80);
|
||||
$port443 = is_resource($connection443) && fclose($connection443);
|
||||
if ($port80) {
|
||||
if ($fromUI) {
|
||||
throw new \Exception("Port 80 is in use.<br>You must stop the process using this port.<br>Docs: <a target='_blank' href='https://coolify.io/docs'>https://coolify.io/docs</a> <br> Discord: <a target='_blank' href='https://coollabs.io/discord'>https://coollabs.io/discord</a>");
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($port443) {
|
||||
if ($fromUI) {
|
||||
throw new \Exception("Port 443 is in use.<br>You must stop the process using this port.<br>Docs: <a target='_blank' href='https://coolify.io/docs'>https://coolify.io/docs</a> <br> Discord: <a target='_blank' href='https://coollabs.io/discord'>https://coollabs.io/discord</a>");
|
||||
} else {
|
||||
return false;
|
||||
if ($port443) {
|
||||
if ($fromUI) {
|
||||
throw new \Exception("Port 443 is in use.<br>You must stop the process using this port.<br>Docs: <a target='_blank' href='https://coolify.io/docs'>https://coolify.io/docs</a> <br> Discord: <a target='_blank' href='https://coollabs.io/discord'>https://coollabs.io/discord</a>");
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ class StartProxy
|
||||
public function handle(Server $server, bool $async = true): string|Activity
|
||||
{
|
||||
try {
|
||||
|
||||
$proxyType = $server->proxyType();
|
||||
$commands = collect([]);
|
||||
$proxy_path = get_proxy_path();
|
||||
@ -24,18 +25,29 @@ public function handle(Server $server, bool $async = true): string|Activity
|
||||
$docker_compose_yml_base64 = base64_encode($configuration);
|
||||
$server->proxy->last_applied_settings = Str::of($docker_compose_yml_base64)->pipe('md5')->value;
|
||||
$server->save();
|
||||
$commands = $commands->merge([
|
||||
"mkdir -p $proxy_path && cd $proxy_path",
|
||||
"echo 'Creating required Docker Compose file.'",
|
||||
"echo 'Pulling docker image.'",
|
||||
'docker compose pull',
|
||||
"echo 'Stopping existing coolify-proxy.'",
|
||||
"docker compose down -v --remove-orphans > /dev/null 2>&1",
|
||||
"echo 'Starting coolify-proxy.'",
|
||||
'docker compose up -d --remove-orphans',
|
||||
"echo 'Proxy started successfully.'"
|
||||
]);
|
||||
$commands = $commands->merge(connectProxyToNetworks($server));
|
||||
if ($server->isSwarm()) {
|
||||
$commands = $commands->merge([
|
||||
"mkdir -p $proxy_path && cd $proxy_path",
|
||||
"echo 'Creating required Docker Compose file.'",
|
||||
"echo 'Starting coolify-proxy.'",
|
||||
"cd $proxy_path && docker stack deploy -c docker-compose.yml coolify-proxy",
|
||||
"echo 'Proxy started successfully.'"
|
||||
]);
|
||||
} else {
|
||||
$commands = $commands->merge([
|
||||
"mkdir -p $proxy_path && cd $proxy_path",
|
||||
"echo 'Creating required Docker Compose file.'",
|
||||
"echo 'Pulling docker image.'",
|
||||
'docker compose pull',
|
||||
"echo 'Stopping existing coolify-proxy.'",
|
||||
"docker compose down -v --remove-orphans > /dev/null 2>&1",
|
||||
"echo 'Starting coolify-proxy.'",
|
||||
'docker compose up -d --remove-orphans',
|
||||
"echo 'Proxy started successfully.'"
|
||||
]);
|
||||
$commands = $commands->merge(connectProxyToNetworks($server));
|
||||
}
|
||||
|
||||
if ($async) {
|
||||
$activity = remote_process($commands, $server);
|
||||
return $activity;
|
||||
@ -46,11 +58,9 @@ public function handle(Server $server, bool $async = true): string|Activity
|
||||
$server->save();
|
||||
return 'OK';
|
||||
}
|
||||
} catch(\Throwable $e) {
|
||||
} catch (\Throwable $e) {
|
||||
ray($e);
|
||||
throw $e;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -76,10 +76,20 @@ public function handle(Server $server)
|
||||
"echo 'Restarting Docker Engine...'",
|
||||
"systemctl enable docker >/dev/null 2>&1 || true",
|
||||
"systemctl restart docker",
|
||||
"echo 'Creating default Docker network (coolify)...'",
|
||||
"docker network create --attachable coolify >/dev/null 2>&1 || true",
|
||||
"echo 'Done!'"
|
||||
]);
|
||||
if ($server->isSwarm()) {
|
||||
$command = $command->merge([
|
||||
"docker network create --attachable --driver overlay coolify-overlay >/dev/null 2>&1 || true",
|
||||
]);
|
||||
} else {
|
||||
$command = $command->merge([
|
||||
"docker network create --attachable coolify >/dev/null 2>&1 || true",
|
||||
]);
|
||||
$command = $command->merge([
|
||||
"echo 'Done!'",
|
||||
]);
|
||||
}
|
||||
|
||||
return remote_process($command, $server);
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ public function handle()
|
||||
$this->alive();
|
||||
$cleanup = $this->option('cleanup');
|
||||
if ($cleanup) {
|
||||
echo "Running cleanup\n";
|
||||
$this->cleanup_stucked_resources();
|
||||
$this->cleanup_ssh();
|
||||
}
|
||||
@ -101,14 +102,14 @@ private function cleanup_stucked_resources()
|
||||
ray('Application without environment', $application->name);
|
||||
$application->delete();
|
||||
}
|
||||
if (!data_get($application, 'destination.server')) {
|
||||
ray('Application without server', $application->name);
|
||||
$application->delete();
|
||||
}
|
||||
if (!$application->destination()) {
|
||||
ray('Application without destination', $application->name);
|
||||
$application->delete();
|
||||
}
|
||||
if (!data_get($application, 'destination.server')) {
|
||||
ray('Application without server', $application->name);
|
||||
$application->delete();
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
echo "Error in application: {$e->getMessage()}\n";
|
||||
@ -120,14 +121,14 @@ private function cleanup_stucked_resources()
|
||||
ray('Postgresql without environment', $postgresql->name);
|
||||
$postgresql->delete();
|
||||
}
|
||||
if (!data_get($postgresql, 'destination.server')) {
|
||||
ray('Postgresql without server', $postgresql->name);
|
||||
$postgresql->delete();
|
||||
}
|
||||
if (!$postgresql->destination()) {
|
||||
ray('Postgresql without destination', $postgresql->name);
|
||||
$postgresql->delete();
|
||||
}
|
||||
if (!data_get($postgresql, 'destination.server')) {
|
||||
ray('Postgresql without server', $postgresql->name);
|
||||
$postgresql->delete();
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
echo "Error in postgresql: {$e->getMessage()}\n";
|
||||
@ -139,14 +140,14 @@ private function cleanup_stucked_resources()
|
||||
ray('Redis without environment', $redis->name);
|
||||
$redis->delete();
|
||||
}
|
||||
if (!data_get($redis, 'destination.server')) {
|
||||
ray('Redis without server', $redis->name);
|
||||
$redis->delete();
|
||||
}
|
||||
if (!$redis->destination()) {
|
||||
ray('Redis without destination', $redis->name);
|
||||
$redis->delete();
|
||||
}
|
||||
if (!data_get($redis, 'destination.server')) {
|
||||
ray('Redis without server', $redis->name);
|
||||
$redis->delete();
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
echo "Error in redis: {$e->getMessage()}\n";
|
||||
@ -159,14 +160,14 @@ private function cleanup_stucked_resources()
|
||||
ray('Mongodb without environment', $mongodb->name);
|
||||
$mongodb->delete();
|
||||
}
|
||||
if (!data_get($mongodb, 'destination.server')) {
|
||||
ray('Mongodb without server', $mongodb->name);
|
||||
$mongodb->delete();
|
||||
}
|
||||
if (!$mongodb->destination()) {
|
||||
ray('Mongodb without destination', $mongodb->name);
|
||||
$mongodb->delete();
|
||||
}
|
||||
if (!data_get($mongodb, 'destination.server')) {
|
||||
ray('Mongodb without server', $mongodb->name);
|
||||
$mongodb->delete();
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
echo "Error in mongodb: {$e->getMessage()}\n";
|
||||
@ -179,14 +180,14 @@ private function cleanup_stucked_resources()
|
||||
ray('Mysql without environment', $mysql->name);
|
||||
$mysql->delete();
|
||||
}
|
||||
if (!data_get($mysql, 'destination.server')) {
|
||||
ray('Mysql without server', $mysql->name);
|
||||
$mysql->delete();
|
||||
}
|
||||
if (!$mysql->destination()) {
|
||||
ray('Mysql without destination', $mysql->name);
|
||||
$mysql->delete();
|
||||
}
|
||||
if (!data_get($mysql, 'destination.server')) {
|
||||
ray('Mysql without server', $mysql->name);
|
||||
$mysql->delete();
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
echo "Error in mysql: {$e->getMessage()}\n";
|
||||
@ -199,14 +200,14 @@ private function cleanup_stucked_resources()
|
||||
ray('Mariadb without environment', $mariadb->name);
|
||||
$mariadb->delete();
|
||||
}
|
||||
if (!data_get($mariadb, 'destination.server')) {
|
||||
ray('Mariadb without server', $mariadb->name);
|
||||
$mariadb->delete();
|
||||
}
|
||||
if (!$mariadb->destination()) {
|
||||
ray('Mariadb without destination', $mariadb->name);
|
||||
$mariadb->delete();
|
||||
}
|
||||
if (!data_get($mariadb, 'destination.server')) {
|
||||
ray('Mariadb without server', $mariadb->name);
|
||||
$mariadb->delete();
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
echo "Error in mariadb: {$e->getMessage()}\n";
|
||||
@ -219,14 +220,14 @@ private function cleanup_stucked_resources()
|
||||
ray('Service without environment', $service->name);
|
||||
$service->delete();
|
||||
}
|
||||
if (!data_get($service, 'server')) {
|
||||
ray('Service without server', $service->name);
|
||||
$service->delete();
|
||||
}
|
||||
if (!$service->destination()) {
|
||||
ray('Service without destination', $service->name);
|
||||
$service->delete();
|
||||
}
|
||||
if (!data_get($service, 'server')) {
|
||||
ray('Service without server', $service->name);
|
||||
$service->delete();
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
echo "Error in service: {$e->getMessage()}\n";
|
||||
|
@ -31,6 +31,7 @@ class Index extends Component
|
||||
public ?string $remoteServerHost = null;
|
||||
public ?int $remoteServerPort = 22;
|
||||
public ?string $remoteServerUser = 'root';
|
||||
public bool $isSwarmManager = false;
|
||||
public ?Server $createdServer = null;
|
||||
|
||||
public Collection $projects;
|
||||
@ -182,7 +183,9 @@ public function saveServer()
|
||||
'private_key_id' => $this->createdPrivateKey->id,
|
||||
'team_id' => currentTeam()->id,
|
||||
]);
|
||||
$this->createdServer->save();
|
||||
$this->createdServer->settings->is_swarm_manager = $this->isSwarmManager;
|
||||
$this->createdServer->settings->save();
|
||||
$this->createdServer->addInitialNetwork();
|
||||
$this->validateServer();
|
||||
}
|
||||
public function validateServer()
|
||||
|
@ -17,14 +17,15 @@ class Form extends Component
|
||||
protected $listeners = ['serverRefresh'];
|
||||
|
||||
protected $rules = [
|
||||
'server.name' => 'required|min:6',
|
||||
'server.name' => 'required',
|
||||
'server.description' => 'nullable',
|
||||
'server.ip' => 'required',
|
||||
'server.user' => 'required',
|
||||
'server.port' => 'required',
|
||||
'server.settings.is_cloudflare_tunnel' => 'required',
|
||||
'server.settings.is_cloudflare_tunnel' => 'required|boolean',
|
||||
'server.settings.is_reachable' => 'required',
|
||||
'server.settings.is_part_of_swarm' => 'required',
|
||||
'server.settings.is_swarm_manager' => 'required|boolean',
|
||||
// 'server.settings.is_swarm_worker' => 'required|boolean',
|
||||
'wildcard_domain' => 'nullable|url',
|
||||
];
|
||||
protected $validationAttributes = [
|
||||
@ -34,8 +35,9 @@ class Form extends Component
|
||||
'server.user' => 'User',
|
||||
'server.port' => 'Port',
|
||||
'server.settings.is_cloudflare_tunnel' => 'Cloudflare Tunnel',
|
||||
'server.settings.is_reachable' => 'is reachable',
|
||||
'server.settings.is_part_of_swarm' => 'is part of swarm'
|
||||
'server.settings.is_reachable' => 'Is reachable',
|
||||
'server.settings.is_swarm_manager' => 'Swarm Manager',
|
||||
// 'server.settings.is_swarm_worker' => 'Swarm Worker',
|
||||
];
|
||||
|
||||
public function mount()
|
||||
@ -49,9 +51,14 @@ public function serverRefresh($install = true)
|
||||
}
|
||||
public function instantSave()
|
||||
{
|
||||
refresh_server_connection($this->server->privateKey);
|
||||
$this->validateServer();
|
||||
$this->server->settings->save();
|
||||
try {
|
||||
refresh_server_connection($this->server->privateKey);
|
||||
$this->validateServer(false);
|
||||
$this->server->settings->save();
|
||||
$this->emit('success', 'Server updated successfully.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function installDocker()
|
||||
{
|
||||
@ -100,6 +107,12 @@ public function validateServer($install = true)
|
||||
$install && $this->installDocker();
|
||||
return;
|
||||
}
|
||||
if ($this->server->isSwarm()) {
|
||||
$swarmInstalled = $this->server->validateDockerSwarm();
|
||||
if ($swarmInstalled) {
|
||||
$install && $this->emit('success', 'Docker Swarm is initiated.');
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
|
@ -21,7 +21,7 @@ class ByIp extends Component
|
||||
public string $ip;
|
||||
public string $user = 'root';
|
||||
public int $port = 22;
|
||||
public bool $is_part_of_swarm = false;
|
||||
public bool $is_swarm_manager = false;
|
||||
|
||||
protected $rules = [
|
||||
'name' => 'required|string',
|
||||
@ -29,6 +29,7 @@ class ByIp extends Component
|
||||
'ip' => 'required',
|
||||
'user' => 'required|string',
|
||||
'port' => 'required|integer',
|
||||
'is_swarm_manager' => 'required|boolean',
|
||||
];
|
||||
protected $validationAttributes = [
|
||||
'name' => 'Name',
|
||||
@ -36,6 +37,7 @@ class ByIp extends Component
|
||||
'ip' => 'IP Address/Domain',
|
||||
'user' => 'User',
|
||||
'port' => 'Port',
|
||||
'is_swarm_manager' => 'Swarm Manager',
|
||||
];
|
||||
|
||||
public function mount()
|
||||
@ -72,11 +74,11 @@ public function submit()
|
||||
'proxy' => [
|
||||
"type" => ProxyTypes::TRAEFIK_V2->value,
|
||||
"status" => ProxyStatus::EXITED->value,
|
||||
]
|
||||
|
||||
],
|
||||
]);
|
||||
$server->settings->is_part_of_swarm = $this->is_part_of_swarm;
|
||||
$server->settings->is_swarm_manager = $this->is_swarm_manager;
|
||||
$server->settings->save();
|
||||
$server->addInitialNetwork();
|
||||
return redirect()->route('server.show', $server->uuid);
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
|
@ -58,11 +58,25 @@ public function startProxy()
|
||||
|
||||
public function stop()
|
||||
{
|
||||
instant_remote_process([
|
||||
"docker rm -f coolify-proxy",
|
||||
], $this->server);
|
||||
$this->server->proxy->status = 'exited';
|
||||
$this->server->save();
|
||||
$this->emit('proxyStatusUpdated');
|
||||
try {
|
||||
if ($this->server->isSwarm()) {
|
||||
instant_remote_process([
|
||||
"docker service rm coolify-proxy_traefik",
|
||||
], $this->server);
|
||||
$this->server->proxy->status = 'exited';
|
||||
$this->server->save();
|
||||
$this->emit('proxyStatusUpdated');
|
||||
} else {
|
||||
instant_remote_process([
|
||||
"docker rm -f coolify-proxy",
|
||||
], $this->server);
|
||||
$this->server->proxy->status = 'exited';
|
||||
$this->server->save();
|
||||
$this->emit('proxyStatusUpdated');
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ class Status extends Component
|
||||
protected $listeners = ['proxyStatusUpdated', 'startProxyPolling'];
|
||||
public function startProxyPolling()
|
||||
{
|
||||
$this->polling = true;
|
||||
$this->checkProxy();
|
||||
}
|
||||
public function proxyStatusUpdated()
|
||||
{
|
||||
|
@ -156,25 +156,27 @@ public function handle(): void
|
||||
|
||||
// Generate custom host<->ip mapping
|
||||
$allContainers = instant_remote_process(["docker network inspect {$this->destination->network} -f '{{json .Containers}}' "], $this->server);
|
||||
$allContainers = format_docker_command_output_to_json($allContainers);
|
||||
$ips = collect([]);
|
||||
if (count($allContainers) > 0) {
|
||||
$allContainers = $allContainers[0];
|
||||
foreach ($allContainers as $container) {
|
||||
$containerName = data_get($container, 'Name');
|
||||
if ($containerName === 'coolify-proxy') {
|
||||
continue;
|
||||
}
|
||||
$containerIp = data_get($container, 'IPv4Address');
|
||||
if ($containerName && $containerIp) {
|
||||
$containerIp = str($containerIp)->before('/');
|
||||
$ips->put($containerName, $containerIp->value());
|
||||
if (!is_null($allContainers)) {
|
||||
$allContainers = format_docker_command_output_to_json($allContainers);
|
||||
$ips = collect([]);
|
||||
if (count($allContainers) > 0) {
|
||||
$allContainers = $allContainers[0];
|
||||
foreach ($allContainers as $container) {
|
||||
$containerName = data_get($container, 'Name');
|
||||
if ($containerName === 'coolify-proxy') {
|
||||
continue;
|
||||
}
|
||||
$containerIp = data_get($container, 'IPv4Address');
|
||||
if ($containerName && $containerIp) {
|
||||
$containerIp = str($containerIp)->before('/');
|
||||
$ips->put($containerName, $containerIp->value());
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->addHosts = $ips->map(function ($ip, $name) {
|
||||
return "--add-host $name:$ip";
|
||||
})->implode(' ');
|
||||
}
|
||||
$this->addHosts = $ips->map(function ($ip, $name) {
|
||||
return "--add-host $name:$ip";
|
||||
})->implode(' ');
|
||||
|
||||
if ($this->application->dockerfile_target_build) {
|
||||
$this->buildTarget = " --target {$this->application->dockerfile_target_build} ";
|
||||
@ -214,6 +216,17 @@ public function handle(): void
|
||||
}
|
||||
if ($this->application->docker_registry_image_name && $this->application->build_pack !== 'dockerimage') {
|
||||
$this->push_to_docker_registry();
|
||||
if ($this->server->isSwarm()) {
|
||||
$this->application_deployment_queue->addLogEntry("Creating / updating stack.");
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "cd {$this->workdir} && docker stack deploy --with-registry-auth -c docker-compose.yml {$this->application->uuid}")
|
||||
],
|
||||
[
|
||||
"echo 'Stack deployed. It may take a few minutes to fully available in your swarm.'"
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
$this->next(ApplicationDeploymentStatus::FINISHED->value);
|
||||
$this->application->isConfigurationChanged(true);
|
||||
@ -290,42 +303,6 @@ private function push_to_docker_registry()
|
||||
ray($e);
|
||||
}
|
||||
}
|
||||
// private function deploy_docker_compose()
|
||||
// {
|
||||
// $dockercompose_base64 = base64_encode($this->application->dockercompose);
|
||||
// $this->execute_remote_command(
|
||||
// [
|
||||
// "echo 'Starting deployment of {$this->application->name}.'"
|
||||
// ],
|
||||
// );
|
||||
// $this->prepare_builder_image();
|
||||
// $this->execute_remote_command(
|
||||
// [
|
||||
// executeInDocker($this->deployment_uuid, "echo '$dockercompose_base64' | base64 -d > $this->workdir/docker-compose.yaml")
|
||||
// ],
|
||||
// );
|
||||
// $this->build_image_name = Str::lower("{$this->customRepository}:build");
|
||||
// $this->production_image_name = Str::lower("{$this->application->uuid}:latest");
|
||||
// $this->save_environment_variables();
|
||||
// $containers = getCurrentApplicationContainerStatus($this->application->destination->server, $this->application->id);
|
||||
// ray($containers);
|
||||
// if ($containers->count() > 0) {
|
||||
// foreach ($containers as $container) {
|
||||
// $containerName = data_get($container, 'Names');
|
||||
// if ($containerName) {
|
||||
// instant_remote_process(
|
||||
// ["docker rm -f {$containerName}"],
|
||||
// $this->application->destination->server
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// $this->execute_remote_command(
|
||||
// ["echo -n 'Starting services (could take a while)...'"],
|
||||
// [executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up -d"), "hidden" => true],
|
||||
// );
|
||||
// }
|
||||
private function generate_image_names()
|
||||
{
|
||||
if ($this->application->dockerfile) {
|
||||
@ -402,7 +379,6 @@ private function save_environment_variables()
|
||||
$envs->push($env->key . '=' . $env->value);
|
||||
}
|
||||
}
|
||||
ray($envs);
|
||||
$envs_base64 = base64_encode($envs->implode("\n"));
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
@ -468,22 +444,31 @@ private function deploy_docker_compose_buildpack()
|
||||
$this->cleanup_git();
|
||||
$composeFile = $this->application->parseCompose(pull_request_id: $this->pull_request_id);
|
||||
$yaml = Yaml::dump($composeFile->toArray(), 10);
|
||||
ray($composeFile);
|
||||
ray($this->container_name);
|
||||
$this->docker_compose_base64 = base64_encode($yaml);
|
||||
$this->execute_remote_command([
|
||||
executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d > {$this->workdir}/docker-compose.yaml"), "hidden" => true
|
||||
executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d > {$this->workdir}{$this->docker_compose_location}"), "hidden" => true
|
||||
]);
|
||||
$this->save_environment_variables();
|
||||
$this->stop_running_container(force: true);
|
||||
|
||||
ray($this->pull_request_id);
|
||||
$networkId = $this->application->uuid;
|
||||
if ($this->pull_request_id !== 0) {
|
||||
$networkId = "{$this->application->uuid}-{$this->pull_request_id}";
|
||||
}
|
||||
$this->execute_remote_command([
|
||||
"docker network create --attachable '{$networkId}' >/dev/null || true", "hidden" => true, "ignore_errors" => true
|
||||
], [
|
||||
"docker network connect {$networkId} coolify-proxy || true", "hidden" => true, "ignore_errors" => true
|
||||
]);
|
||||
ray($networkId);
|
||||
if ($this->server->isSwarm()) {
|
||||
// TODO
|
||||
} else {
|
||||
$this->execute_remote_command([
|
||||
"docker network create --attachable '{$networkId}' >/dev/null || true", "hidden" => true, "ignore_errors" => true
|
||||
], [
|
||||
"docker network connect {$networkId} coolify-proxy || true", "hidden" => true, "ignore_errors" => true
|
||||
]);
|
||||
}
|
||||
|
||||
$this->start_by_compose_file();
|
||||
$this->application->loadComposeFile(isInit: false);
|
||||
}
|
||||
@ -570,74 +555,83 @@ private function deploy_static_buildpack()
|
||||
|
||||
private function rolling_update()
|
||||
{
|
||||
if (count($this->application->ports_mappings_array) > 0) {
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
"echo '\n----------------------------------------'",
|
||||
],
|
||||
["echo -n 'Application has ports mapped to the host system, rolling update is not supported.'"],
|
||||
);
|
||||
$this->stop_running_container(force: true);
|
||||
$this->start_by_compose_file();
|
||||
if ($this->server->isSwarm()) {
|
||||
// Skip this.
|
||||
} else {
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
"echo '\n----------------------------------------'",
|
||||
],
|
||||
["echo -n 'Rolling update started.'"],
|
||||
);
|
||||
$this->start_by_compose_file();
|
||||
$this->health_check();
|
||||
$this->stop_running_container();
|
||||
if (count($this->application->ports_mappings_array) > 0) {
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
"echo '\n----------------------------------------'",
|
||||
],
|
||||
["echo -n 'Application has ports mapped to the host system, rolling update is not supported.'"],
|
||||
);
|
||||
$this->stop_running_container(force: true);
|
||||
$this->start_by_compose_file();
|
||||
} else {
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
"echo '\n----------------------------------------'",
|
||||
],
|
||||
["echo -n 'Rolling update started.'"],
|
||||
);
|
||||
$this->start_by_compose_file();
|
||||
$this->health_check();
|
||||
$this->stop_running_container();
|
||||
$this->application_deployment_queue->addLogEntry("Rolling update completed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
private function health_check()
|
||||
{
|
||||
if ($this->application->isHealthcheckDisabled()) {
|
||||
$this->newVersionIsHealthy = true;
|
||||
return;
|
||||
}
|
||||
// ray('New container name: ', $this->container_name);
|
||||
if ($this->container_name) {
|
||||
$counter = 1;
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
"echo 'Waiting for healthcheck to pass on the new container.'"
|
||||
]
|
||||
);
|
||||
if ($this->full_healthcheck_url) {
|
||||
if ($this->server->isSwarm()) {
|
||||
// Implement healthcheck for swarm
|
||||
} else {
|
||||
if ($this->application->isHealthcheckDisabled()) {
|
||||
$this->newVersionIsHealthy = true;
|
||||
return;
|
||||
}
|
||||
// ray('New container name: ', $this->container_name);
|
||||
if ($this->container_name) {
|
||||
$counter = 1;
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
"echo 'Healthcheck URL (inside the container): {$this->full_healthcheck_url}'"
|
||||
"echo 'Waiting for healthcheck to pass on the new container.'"
|
||||
]
|
||||
);
|
||||
}
|
||||
while ($counter < $this->application->health_check_retries) {
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
"docker inspect --format='{{json .State.Health.Status}}' {$this->container_name}",
|
||||
"hidden" => true,
|
||||
"save" => "health_check"
|
||||
],
|
||||
|
||||
);
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
"echo 'Attempt {$counter} of {$this->application->health_check_retries} | Healthcheck status: {$this->saved_outputs->get('health_check')}'"
|
||||
],
|
||||
);
|
||||
if (Str::of($this->saved_outputs->get('health_check'))->contains('healthy')) {
|
||||
$this->newVersionIsHealthy = true;
|
||||
$this->application->update(['status' => 'running']);
|
||||
if ($this->full_healthcheck_url) {
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
"echo 'New container is healthy.'"
|
||||
"echo 'Healthcheck URL (inside the container): {$this->full_healthcheck_url}'"
|
||||
]
|
||||
);
|
||||
}
|
||||
while ($counter < $this->application->health_check_retries) {
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
"docker inspect --format='{{json .State.Health.Status}}' {$this->container_name}",
|
||||
"hidden" => true,
|
||||
"save" => "health_check"
|
||||
],
|
||||
|
||||
);
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
"echo 'Attempt {$counter} of {$this->application->health_check_retries} | Healthcheck status: {$this->saved_outputs->get('health_check')}'"
|
||||
],
|
||||
);
|
||||
break;
|
||||
if (Str::of($this->saved_outputs->get('health_check'))->contains('healthy')) {
|
||||
$this->newVersionIsHealthy = true;
|
||||
$this->application->update(['status' => 'running']);
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
"echo 'New container is healthy.'"
|
||||
],
|
||||
);
|
||||
break;
|
||||
}
|
||||
$counter++;
|
||||
sleep($this->application->health_check_interval);
|
||||
}
|
||||
$counter++;
|
||||
sleep($this->application->health_check_interval);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -844,26 +838,6 @@ private function generate_env_variables()
|
||||
$this->env_args = $this->env_args->implode(' ');
|
||||
}
|
||||
|
||||
private function modify_compose_file()
|
||||
{
|
||||
// ray("{$this->workdir}{$this->docker_compose_location}");
|
||||
$this->execute_remote_command([executeInDocker($this->deployment_uuid, "cat {$this->workdir}{$this->docker_compose_location}"), "hidden" => true, "save" => 'compose_file']);
|
||||
if ($this->saved_outputs->get('compose_file')) {
|
||||
$compose = $this->saved_outputs->get('compose_file');
|
||||
}
|
||||
try {
|
||||
$yaml = Yaml::parse($compose);
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception($e->getMessage());
|
||||
}
|
||||
$services = data_get($yaml, 'services');
|
||||
$topLevelNetworks = collect(data_get($yaml, 'networks', []));
|
||||
$definedNetwork = collect([$this->application->uuid]);
|
||||
|
||||
$services = collect($services)->map(function ($service, $serviceName) use ($topLevelNetworks, $definedNetwork) {
|
||||
$serviceNetworks = collect(data_get($service, 'networks', []));
|
||||
});
|
||||
}
|
||||
private function generate_compose_file()
|
||||
{
|
||||
$ports = $this->application->settings->is_static ? [80] : $this->application->ports_exposes_array;
|
||||
@ -884,21 +858,6 @@ private function generate_compose_file()
|
||||
}
|
||||
if ($this->pull_request_id !== 0) {
|
||||
$labels = collect(generateLabelsApplication($this->application, $this->preview));
|
||||
|
||||
// $newHostLabel = $newLabels->filter(function ($label) {
|
||||
// return str($label)->contains('Host');
|
||||
// });
|
||||
// $labels = $labels->reject(function ($label) {
|
||||
// return str($label)->contains('Host');
|
||||
// });
|
||||
// ray($labels,$newLabels);
|
||||
// $labels = $labels->map(function ($label) {
|
||||
// $pattern = '/([a-zA-Z0-9]+)-(\d+)-(http|https)/';
|
||||
// $replacement = "$1-pr-{$this->pull_request_id}-$2-$3";
|
||||
// $newLabel = preg_replace($pattern, $replacement, $label);
|
||||
// return $newLabel;
|
||||
// });
|
||||
// $labels = $labels->merge($newHostLabel);
|
||||
}
|
||||
$labels = $labels->merge(defaultLabels($this->application->id, $this->application->uuid, $this->pull_request_id))->toArray();
|
||||
$docker_compose = [
|
||||
@ -909,7 +868,6 @@ private function generate_compose_file()
|
||||
'container_name' => $this->container_name,
|
||||
'restart' => RESTART_MODE,
|
||||
'environment' => $environment_variables,
|
||||
'labels' => $labels,
|
||||
'expose' => $ports,
|
||||
'networks' => [
|
||||
$this->destination->network,
|
||||
@ -941,6 +899,48 @@ private function generate_compose_file()
|
||||
]
|
||||
]
|
||||
];
|
||||
if ($this->server->isSwarm()) {
|
||||
data_forget($docker_compose, 'services.' . $this->container_name . '.container_name');
|
||||
data_forget($docker_compose, 'services.' . $this->container_name . '.expose');
|
||||
data_forget($docker_compose, 'services.' . $this->container_name . '.restart');
|
||||
|
||||
data_forget($docker_compose, 'services.' . $this->container_name . '.mem_limit');
|
||||
data_forget($docker_compose, 'services.' . $this->container_name . '.memswap_limit');
|
||||
data_forget($docker_compose, 'services.' . $this->container_name . '.mem_swappiness');
|
||||
data_forget($docker_compose, 'services.' . $this->container_name . '.mem_reservation');
|
||||
data_forget($docker_compose, 'services.' . $this->container_name . '.cpus');
|
||||
data_forget($docker_compose, 'services.' . $this->container_name . '.cpuset');
|
||||
data_forget($docker_compose, 'services.' . $this->container_name . '.cpu_shares');
|
||||
|
||||
$docker_compose['services'][$this->container_name]['deploy'] = [
|
||||
'placement' => [
|
||||
'constraints' => [
|
||||
'node.role == worker'
|
||||
]
|
||||
],
|
||||
'mode' => 'replicated',
|
||||
'replicas' => 1,
|
||||
'update_config' => [
|
||||
'order' => 'start-first'
|
||||
],
|
||||
'rollback_config' => [
|
||||
'order' => 'start-first'
|
||||
],
|
||||
'labels' => $labels,
|
||||
'resources' => [
|
||||
'limits' => [
|
||||
'cpus' => $this->application->limits_cpus,
|
||||
'memory' => $this->application->limits_memory,
|
||||
],
|
||||
'reservations' => [
|
||||
'cpus' => $this->application->limits_cpus,
|
||||
'memory' => $this->application->limits_memory,
|
||||
]
|
||||
]
|
||||
];
|
||||
} else {
|
||||
$docker_compose['services'][$this->container_name]['labels'] = $labels;
|
||||
}
|
||||
if ($this->server->isLogDrainEnabled() && $this->application->isLogDrainEnabled()) {
|
||||
$docker_compose['services'][$this->container_name]['logging'] = [
|
||||
'driver' => 'fluentd',
|
||||
@ -988,6 +988,11 @@ private function generate_compose_file()
|
||||
// 'dockerfile' => $this->workdir . $this->dockerfile_location,
|
||||
// ];
|
||||
// }
|
||||
|
||||
$docker_compose['services'][$this->application->uuid] = $docker_compose['services'][$this->container_name];
|
||||
|
||||
data_forget($docker_compose, 'services.' . $this->container_name);
|
||||
|
||||
$this->docker_compose = Yaml::dump($docker_compose, 10);
|
||||
$this->docker_compose_base64 = base64_encode($this->docker_compose);
|
||||
$this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d > {$this->workdir}/docker-compose.yml"), "hidden" => true]);
|
||||
@ -1204,7 +1209,6 @@ private function stop_running_container(bool $force = false)
|
||||
[executeInDocker($this->deployment_uuid, "docker rm -f $containerName >/dev/null 2>&1"), "hidden" => true, "ignore_errors" => true],
|
||||
);
|
||||
});
|
||||
$this->application_deployment_queue->addLogEntry("Rolling update completed.");
|
||||
} else {
|
||||
$this->application_deployment_queue->addLogEntry("New container is not healthy, rolling back to the old container.");
|
||||
$this->execute_remote_command(
|
||||
@ -1226,6 +1230,7 @@ private function start_by_compose_file()
|
||||
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up --build -d"), "hidden" => true],
|
||||
);
|
||||
}
|
||||
$this->application_deployment_queue->addLogEntry("New container started.");
|
||||
}
|
||||
|
||||
private function generate_build_env_variables()
|
||||
@ -1289,9 +1294,13 @@ public function failed(Throwable $exception): void
|
||||
$this->execute_remote_command(
|
||||
["echo 'Oops something is not okay, are you okay? 😢'", 'type' => 'err'],
|
||||
["echo '{$exception->getMessage()}'", 'type' => 'err'],
|
||||
["echo -n 'Deployment failed. Removing the new version of your application.'", 'type' => 'err'],
|
||||
[executeInDocker($this->deployment_uuid, "docker rm -f $this->container_name >/dev/null 2>&1"), "hidden" => true, "ignore_errors" => true]
|
||||
);
|
||||
if ($this->application->build_pack !== 'dockercompose') {
|
||||
$this->execute_remote_command(
|
||||
["echo -n 'Deployment failed. Removing the new version of your application.'", 'type' => 'err'],
|
||||
[executeInDocker($this->deployment_uuid, "docker rm -f $this->container_name >/dev/null 2>&1"), "hidden" => true, "ignore_errors" => true]
|
||||
);
|
||||
}
|
||||
|
||||
$this->next(ApplicationDeploymentStatus::FAILED->value);
|
||||
}
|
||||
|
@ -42,8 +42,43 @@ public function handle()
|
||||
if (!$this->server->isServerReady()) {
|
||||
return;
|
||||
};
|
||||
$containers = instant_remote_process(["docker container inspect $(docker container ls -q) --format '{{json .}}'"], $this->server);
|
||||
if ($this->server->isSwarm()) {
|
||||
$containers = instant_remote_process(["docker service inspect $(docker service ls -q) --format '{{json .}}'"], $this->server, false);
|
||||
$containerReplicase = instant_remote_process(["docker service ls --format '{{json .}}'"], $this->server, false);
|
||||
} else {
|
||||
// Precheck for containers
|
||||
$containers = instant_remote_process(["docker container ls -q"], $this->server);
|
||||
if (!$containers) {
|
||||
return;
|
||||
}
|
||||
$containers = instant_remote_process(["docker container inspect $(docker container ls -q) --format '{{json .}}'"], $this->server, false);
|
||||
$containerReplicase = null;
|
||||
}
|
||||
if (is_null($containers)) {
|
||||
return;
|
||||
}
|
||||
$containers = format_docker_command_output_to_json($containers);
|
||||
if ($containerReplicase) {
|
||||
$containerReplicase = format_docker_command_output_to_json($containerReplicase);
|
||||
foreach ($containerReplicase as $containerReplica) {
|
||||
$name = data_get($containerReplica, 'Name');
|
||||
$containers = $containers->map(function ($container) use ($name, $containerReplica) {
|
||||
if (data_get($container, 'Spec.Name') === $name) {
|
||||
$replicas = data_get($containerReplica, 'Replicas');
|
||||
$running = str($replicas)->explode('/')[0];
|
||||
$total = str($replicas)->explode('/')[1];
|
||||
if ($running === $total) {
|
||||
data_set($container, 'State.Status', 'running');
|
||||
data_set($container, 'State.Health.Status', 'healthy');
|
||||
} else {
|
||||
data_set($container, 'State.Status', 'starting');
|
||||
data_set($container, 'State.Health.Status', 'unhealthy');
|
||||
}
|
||||
}
|
||||
return $container;
|
||||
});
|
||||
}
|
||||
}
|
||||
$applications = $this->server->applications();
|
||||
$databases = $this->server->databases();
|
||||
$services = $this->server->services()->get();
|
||||
@ -55,10 +90,16 @@ public function handle()
|
||||
$foundServices = [];
|
||||
|
||||
foreach ($containers as $container) {
|
||||
if ($this->server->isSwarm()) {
|
||||
$labels = data_get($container, 'Spec.Labels');
|
||||
$uuid = data_get($labels, 'coolify.name');
|
||||
} else {
|
||||
$labels = data_get($container, 'Config.Labels');
|
||||
$uuid = data_get($labels, 'com.docker.compose.service');
|
||||
}
|
||||
$containerStatus = data_get($container, 'State.Status');
|
||||
$containerHealth = data_get($container, 'State.Health.Status', 'unhealthy');
|
||||
$containerStatus = "$containerStatus ($containerHealth)";
|
||||
$labels = data_get($container, 'Config.Labels');
|
||||
$labels = Arr::undot(format_docker_labels_to_json($labels));
|
||||
$applicationId = data_get($labels, 'coolify.applicationId');
|
||||
if ($applicationId) {
|
||||
@ -90,7 +131,6 @@ public function handle()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$uuid = data_get($labels, 'com.docker.compose.service');
|
||||
if ($uuid) {
|
||||
$database = $databases->where('uuid', $uuid)->first();
|
||||
if ($database) {
|
||||
@ -245,7 +285,11 @@ public function handle()
|
||||
// Check if proxy is running
|
||||
$this->server->proxyType();
|
||||
$foundProxyContainer = $containers->filter(function ($value, $key) {
|
||||
return data_get($value, 'Name') === '/coolify-proxy';
|
||||
if ($this->server->isSwarm()) {
|
||||
return data_get($value, 'Spec.Name') === 'coolify-proxy_traefik';
|
||||
} else {
|
||||
return data_get($value, 'Name') === '/coolify-proxy';
|
||||
}
|
||||
})->first();
|
||||
if (!$foundProxyContainer) {
|
||||
try {
|
||||
|
@ -603,6 +603,7 @@ function parseCompose(int $pull_request_id = 0)
|
||||
{
|
||||
if ($this->docker_compose_raw) {
|
||||
$mainCompose = parseDockerComposeFile(resource: $this, isNew: false, pull_request_id: $pull_request_id);
|
||||
ray($this->docker_compose_pr_raw);
|
||||
if ($this->getMorphClass() === 'App\Models\Application' && $this->docker_compose_pr_raw) {
|
||||
parseDockerComposeFile(resource: $this, isNew: false, pull_request_id: $pull_request_id, is_pr: true);
|
||||
}
|
||||
@ -614,7 +615,7 @@ function parseCompose(int $pull_request_id = 0)
|
||||
function loadComposeFile($isInit = false)
|
||||
{
|
||||
$initialDockerComposeLocation = $this->docker_compose_location;
|
||||
$initialDockerComposePrLocation = $this->docker_compose_pr_location;
|
||||
// $initialDockerComposePrLocation = $this->docker_compose_pr_location;
|
||||
if ($this->build_pack === 'dockercompose') {
|
||||
if ($isInit && $this->docker_compose_raw) {
|
||||
return;
|
||||
@ -623,11 +624,11 @@ function loadComposeFile($isInit = false)
|
||||
['commands' => $cloneCommand] = $this->generateGitImportCommands(deployment_uuid: $uuid, only_checkout: true, exec_in_docker: false, custom_base_dir: '.');
|
||||
$workdir = rtrim($this->base_directory, '/');
|
||||
$composeFile = $this->docker_compose_location;
|
||||
$prComposeFile = $this->docker_compose_pr_location;
|
||||
$fileList = collect([".$composeFile"]);
|
||||
if ($composeFile !== $prComposeFile) {
|
||||
$fileList->push(".$prComposeFile");
|
||||
}
|
||||
// $prComposeFile = $this->docker_compose_pr_location;
|
||||
$fileList = collect([".$workdir$composeFile"]);
|
||||
// if ($composeFile !== $prComposeFile) {
|
||||
// $fileList->push(".$prComposeFile");
|
||||
// }
|
||||
$commands = collect([
|
||||
"mkdir -p /tmp/{$uuid} && cd /tmp/{$uuid}",
|
||||
$cloneCommand,
|
||||
@ -645,24 +646,24 @@ function loadComposeFile($isInit = false)
|
||||
$this->docker_compose_raw = $composeFileContent;
|
||||
$this->save();
|
||||
}
|
||||
if ($composeFile === $prComposeFile) {
|
||||
$this->docker_compose_pr_raw = $composeFileContent;
|
||||
$this->save();
|
||||
} else {
|
||||
$commands = collect([
|
||||
"cd /tmp/{$uuid}",
|
||||
"cat .$workdir$prComposeFile",
|
||||
]);
|
||||
$composePrFileContent = instant_remote_process($commands, $this->destination->server, false);
|
||||
if (!$composePrFileContent) {
|
||||
$this->docker_compose_pr_location = $initialDockerComposePrLocation;
|
||||
$this->save();
|
||||
throw new \Exception("Could not load compose file from $workdir$prComposeFile");
|
||||
} else {
|
||||
$this->docker_compose_pr_raw = $composePrFileContent;
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
// if ($composeFile === $prComposeFile) {
|
||||
// $this->docker_compose_pr_raw = $composeFileContent;
|
||||
// $this->save();
|
||||
// } else {
|
||||
// $commands = collect([
|
||||
// "cd /tmp/{$uuid}",
|
||||
// "cat .$workdir$prComposeFile",
|
||||
// ]);
|
||||
// $composePrFileContent = instant_remote_process($commands, $this->destination->server, false);
|
||||
// if (!$composePrFileContent) {
|
||||
// $this->docker_compose_pr_location = $initialDockerComposePrLocation;
|
||||
// $this->save();
|
||||
// throw new \Exception("Could not load compose file from $workdir$prComposeFile");
|
||||
// } else {
|
||||
// $this->docker_compose_pr_raw = $composePrFileContent;
|
||||
// $this->save();
|
||||
// }
|
||||
// }
|
||||
|
||||
$commands = collect([
|
||||
"rm -rf /tmp/{$uuid}",
|
||||
|
@ -2,8 +2,6 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Actions\Server\InstallLogDrain;
|
||||
use App\Actions\Server\InstallNewRelic;
|
||||
use App\Enums\ApplicationDeploymentStatus;
|
||||
use App\Enums\ProxyStatus;
|
||||
use App\Enums\ProxyTypes;
|
||||
@ -18,7 +16,7 @@
|
||||
use Spatie\SchemalessAttributes\Casts\SchemalessAttributes;
|
||||
use Spatie\SchemalessAttributes\SchemalessAttributesTrait;
|
||||
use Illuminate\Support\Str;
|
||||
use Stringable;
|
||||
use Illuminate\Support\Stringable;
|
||||
|
||||
class Server extends BaseModel
|
||||
{
|
||||
@ -37,25 +35,10 @@ protected static function booted()
|
||||
}
|
||||
$server->forceFill($payload);
|
||||
});
|
||||
|
||||
static::created(function ($server) {
|
||||
ServerSetting::create([
|
||||
'server_id' => $server->id,
|
||||
]);
|
||||
if ($server->id === 0) {
|
||||
StandaloneDocker::create([
|
||||
'id' => 0,
|
||||
'name' => 'coolify',
|
||||
'network' => 'coolify',
|
||||
'server_id' => $server->id,
|
||||
]);
|
||||
} else {
|
||||
StandaloneDocker::create([
|
||||
'name' => 'coolify',
|
||||
'network' => 'coolify',
|
||||
'server_id' => $server->id,
|
||||
]);
|
||||
}
|
||||
});
|
||||
static::deleting(function ($server) {
|
||||
$server->destinations()->each(function ($destination) {
|
||||
@ -84,7 +67,7 @@ static public function ownedByCurrentTeam(array $select = ['*'])
|
||||
{
|
||||
$teamId = currentTeam()->id;
|
||||
$selectArray = collect($select)->concat(['id']);
|
||||
return Server::whereTeamId($teamId)->with('settings')->select($selectArray->all())->orderBy('name');
|
||||
return Server::whereTeamId($teamId)->with('settings','swarmDockers','standaloneDockers')->select($selectArray->all())->orderBy('name');
|
||||
}
|
||||
|
||||
static public function isUsable()
|
||||
@ -103,7 +86,41 @@ public function settings()
|
||||
{
|
||||
return $this->hasOne(ServerSetting::class);
|
||||
}
|
||||
public function addInitialNetwork() {
|
||||
ray($this->id);
|
||||
|
||||
if ($this->id === 0) {
|
||||
if ($this->isSwarm()) {
|
||||
SwarmDocker::create([
|
||||
'id' => 0,
|
||||
'name' => 'coolify',
|
||||
'network' => 'coolify-overlay',
|
||||
'server_id' => $this->id,
|
||||
]);
|
||||
} else {
|
||||
StandaloneDocker::create([
|
||||
'id' => 0,
|
||||
'name' => 'coolify',
|
||||
'network' => 'coolify',
|
||||
'server_id' => $this->id,
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
if ($this->isSwarm()) {
|
||||
SwarmDocker::create([
|
||||
'name' => 'coolify-overlay',
|
||||
'network' => 'coolify-overlay',
|
||||
'server_id' => $this->id,
|
||||
]);
|
||||
} else {
|
||||
StandaloneDocker::create([
|
||||
'name' => 'coolify-overlay',
|
||||
'network' => 'coolify',
|
||||
'server_id' => $this->id,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
public function proxyType()
|
||||
{
|
||||
$proxyType = $this->proxy->get('type');
|
||||
@ -359,12 +376,16 @@ public function validateOS(): bool | Stringable
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public function isSwarm()
|
||||
{
|
||||
return data_get($this, 'settings.is_swarm_manager') || data_get($this, 'settings.is_swarm_worker');
|
||||
}
|
||||
public function validateConnection()
|
||||
{
|
||||
$server = Server::find($this->id);
|
||||
if ($this->skipServer()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$uptime = instant_remote_process(['uptime'], $this, false);
|
||||
if (!$uptime) {
|
||||
$this->settings()->update([
|
||||
@ -375,14 +396,14 @@ public function validateConnection()
|
||||
$this->settings()->update([
|
||||
'is_reachable' => true,
|
||||
]);
|
||||
$this->update([
|
||||
$server->update([
|
||||
'unreachable_count' => 0,
|
||||
]);
|
||||
}
|
||||
|
||||
if (data_get($this, 'unreachable_notification_sent') === true) {
|
||||
$this->team->notify(new Revived($this));
|
||||
$this->update(['unreachable_notification_sent' => false]);
|
||||
$server->update(['unreachable_notification_sent' => false]);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -400,7 +421,20 @@ public function validateDockerEngine($throwError = false)
|
||||
}
|
||||
$this->settings->is_usable = true;
|
||||
$this->settings->save();
|
||||
$this->validateCoolifyNetwork();
|
||||
$this->validateCoolifyNetwork(isSwarm: false);
|
||||
return true;
|
||||
}
|
||||
public function validateDockerSwarm()
|
||||
{
|
||||
$swarmStatus = instant_remote_process(["docker info|grep -i swarm"], $this, false);
|
||||
$swarmStatus = str($swarmStatus)->trim()->after(':')->trim();
|
||||
if ($swarmStatus === 'inactive') {
|
||||
throw new \Exception('Docker Swarm is not initiated. Please join the server to a swarm before continuing.');
|
||||
return false;
|
||||
}
|
||||
$this->settings->is_usable = true;
|
||||
$this->settings->save();
|
||||
$this->validateCoolifyNetwork(isSwarm: true);
|
||||
return true;
|
||||
}
|
||||
public function validateDockerEngineVersion()
|
||||
@ -417,9 +451,13 @@ public function validateDockerEngineVersion()
|
||||
$this->settings->save();
|
||||
return true;
|
||||
}
|
||||
public function validateCoolifyNetwork()
|
||||
public function validateCoolifyNetwork($isSwarm = false)
|
||||
{
|
||||
return instant_remote_process(["docker network create coolify --attachable >/dev/null 2>&1 || true"], $this, false);
|
||||
if ($isSwarm) {
|
||||
return instant_remote_process(["docker network create --attachable --driver overlay coolify-overlay >/dev/null 2>&1 || true"], $this, false);
|
||||
} else {
|
||||
return instant_remote_process(["docker network create coolify --attachable >/dev/null 2>&1 || true"], $this, false);
|
||||
}
|
||||
}
|
||||
public function executeRemoteCommand(Collection $commands, ?ApplicationDeploymentQueue $loggingModel = null)
|
||||
{
|
||||
|
@ -4,13 +4,57 @@
|
||||
|
||||
class SwarmDocker extends BaseModel
|
||||
{
|
||||
protected $guarded = [];
|
||||
|
||||
public function applications()
|
||||
{
|
||||
return $this->morphMany(Application::class, 'destination');
|
||||
}
|
||||
|
||||
public function postgresqls()
|
||||
{
|
||||
return $this->morphMany(StandalonePostgresql::class, 'destination');
|
||||
}
|
||||
|
||||
public function redis()
|
||||
{
|
||||
return $this->morphMany(StandaloneRedis::class, 'destination');
|
||||
}
|
||||
public function mongodbs()
|
||||
{
|
||||
return $this->morphMany(StandaloneMongodb::class, 'destination');
|
||||
}
|
||||
public function mysqls()
|
||||
{
|
||||
return $this->morphMany(StandaloneMysql::class, 'destination');
|
||||
}
|
||||
public function mariadbs()
|
||||
{
|
||||
return $this->morphMany(StandaloneMariadb::class, 'destination');
|
||||
}
|
||||
|
||||
public function server()
|
||||
{
|
||||
return $this->belongsTo(Server::class);
|
||||
}
|
||||
|
||||
public function services()
|
||||
{
|
||||
return $this->morphMany(Service::class, 'destination');
|
||||
}
|
||||
|
||||
public function databases()
|
||||
{
|
||||
$postgresqls = $this->postgresqls;
|
||||
$redis = $this->redis;
|
||||
$mongodbs = $this->mongodbs;
|
||||
$mysqls = $this->mysqls;
|
||||
$mariadbs = $this->mariadbs;
|
||||
return $postgresqls->concat($redis)->concat($mongodbs)->concat($mysqls)->concat($mariadbs);
|
||||
}
|
||||
|
||||
public function attachedTo()
|
||||
{
|
||||
return $this->applications?->count() > 0 || $this->databases()->count() > 0;
|
||||
}
|
||||
}
|
||||
|
@ -93,7 +93,11 @@ function executeInDocker(string $containerId, string $command)
|
||||
|
||||
function getContainerStatus(Server $server, string $container_id, bool $all_data = false, bool $throwError = false)
|
||||
{
|
||||
$container = instant_remote_process(["docker inspect --format '{{json .}}' {$container_id}"], $server, $throwError);
|
||||
if ($server->isSwarm()) {
|
||||
$container = instant_remote_process(["docker service ls --filter 'name={$container_id}' --format '{{json .}}' "], $server, $throwError);
|
||||
} else {
|
||||
$container = instant_remote_process(["docker inspect --format '{{json .}}' {$container_id}"], $server, $throwError);
|
||||
}
|
||||
if (!$container) {
|
||||
return 'exited';
|
||||
}
|
||||
@ -101,7 +105,19 @@ function getContainerStatus(Server $server, string $container_id, bool $all_data
|
||||
if ($all_data) {
|
||||
return $container[0];
|
||||
}
|
||||
return data_get($container[0], 'State.Status', 'exited');
|
||||
if ($server->isSwarm()) {
|
||||
$replicas = data_get($container[0], 'Replicas');
|
||||
$replicas = explode('/', $replicas);
|
||||
$active = (int)$replicas[0];
|
||||
$total = (int)$replicas[1];
|
||||
if ($active === $total) {
|
||||
return 'running';
|
||||
} else {
|
||||
return 'starting';
|
||||
}
|
||||
} else {
|
||||
return data_get($container[0], 'State.Status', 'exited');
|
||||
}
|
||||
}
|
||||
|
||||
function generateApplicationContainerName(Application $application, $pull_request_id = 0)
|
||||
@ -275,8 +291,11 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
|
||||
return $labels->all();
|
||||
}
|
||||
|
||||
function isDatabaseImage(string $image)
|
||||
function isDatabaseImage(?string $image = null)
|
||||
{
|
||||
if (is_null($image)) {
|
||||
return false;
|
||||
}
|
||||
$image = str($image);
|
||||
if ($image->contains(':')) {
|
||||
$image = str($image);
|
||||
|
@ -54,9 +54,15 @@ function connectProxyToNetworks(Server $server)
|
||||
function generate_default_proxy_configuration(Server $server)
|
||||
{
|
||||
$proxy_path = get_proxy_path();
|
||||
$networks = collect($server->standaloneDockers)->map(function ($docker) {
|
||||
return $docker['network'];
|
||||
})->unique();
|
||||
if ($server->isSwarm()) {
|
||||
$networks = collect($server->swarmDockers)->map(function ($docker) {
|
||||
return $docker['network'];
|
||||
})->unique();
|
||||
} else {
|
||||
$networks = collect($server->standaloneDockers)->map(function ($docker) {
|
||||
return $docker['network'];
|
||||
})->unique();
|
||||
}
|
||||
if ($networks->count() === 0) {
|
||||
$networks = collect(['coolify']);
|
||||
}
|
||||
@ -66,6 +72,16 @@ function generate_default_proxy_configuration(Server $server)
|
||||
"external" => true,
|
||||
];
|
||||
});
|
||||
$labels = [
|
||||
"traefik.enable=true",
|
||||
"traefik.http.routers.traefik.entrypoints=http",
|
||||
"traefik.http.routers.traefik.middlewares=traefik-basic-auth@file",
|
||||
"traefik.http.routers.traefik.service=api@internal",
|
||||
"traefik.http.services.traefik.loadbalancer.server.port=8080",
|
||||
// Global Middlewares
|
||||
"traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https",
|
||||
"traefik.http.middlewares.gzip.compress=true",
|
||||
];
|
||||
$config = [
|
||||
"version" => "3.8",
|
||||
"networks" => $array_of_networks->toArray(),
|
||||
@ -102,7 +118,6 @@ function generate_default_proxy_configuration(Server $server)
|
||||
"--entrypoints.https.address=:443",
|
||||
"--entrypoints.http.http.encodequerysemicolons=true",
|
||||
"--entrypoints.https.http.encodequerysemicolons=true",
|
||||
"--providers.docker=true",
|
||||
"--providers.docker.exposedbydefault=false",
|
||||
"--providers.file.directory=/traefik/dynamic/",
|
||||
"--providers.file.watch=true",
|
||||
@ -110,16 +125,7 @@ function generate_default_proxy_configuration(Server $server)
|
||||
"--certificatesresolvers.letsencrypt.acme.storage=/traefik/acme.json",
|
||||
"--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=http",
|
||||
],
|
||||
"labels" => [
|
||||
"traefik.enable=true",
|
||||
"traefik.http.routers.traefik.entrypoints=http",
|
||||
"traefik.http.routers.traefik.middlewares=traefik-basic-auth@file",
|
||||
"traefik.http.routers.traefik.service=api@internal",
|
||||
"traefik.http.services.traefik.loadbalancer.server.port=8080",
|
||||
// Global Middlewares
|
||||
"traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https",
|
||||
"traefik.http.middlewares.gzip.compress=true",
|
||||
],
|
||||
"labels" => $labels,
|
||||
],
|
||||
],
|
||||
];
|
||||
@ -128,7 +134,24 @@ function generate_default_proxy_configuration(Server $server)
|
||||
$config['services']['traefik']['command'][] = "--accesslog.filepath=/traefik/access.log";
|
||||
$config['services']['traefik']['command'][] = "--accesslog.bufferingsize=100";
|
||||
}
|
||||
$config = Yaml::dump($config, 4, 2);
|
||||
if ($server->isSwarm()) {
|
||||
data_forget($config, 'services.traefik.container_name');
|
||||
data_forget($config, 'services.traefik.restart');
|
||||
data_forget($config, 'services.traefik.labels');
|
||||
|
||||
$config['services']['traefik']['command'][] = "--providers.docker.swarmMode=true";
|
||||
$config['services']['traefik']['deploy'] = [
|
||||
"labels" => $labels,
|
||||
"placement" => [
|
||||
"constraints" => [
|
||||
"node.role==manager",
|
||||
],
|
||||
],
|
||||
];
|
||||
} else {
|
||||
$config['services']['traefik']['command'][] = "--providers.docker=true";
|
||||
}
|
||||
$config = Yaml::dump($config, 12, 2);
|
||||
SaveConfiguration::run($server, $config);
|
||||
return $config;
|
||||
}
|
||||
|
@ -123,6 +123,9 @@ function instant_remote_process(Collection|array $command, Server $server, $thro
|
||||
}
|
||||
return excludeCertainErrors($process->errorOutput(), $exitCode);
|
||||
}
|
||||
if ($output === 'null') {
|
||||
$output = null;
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
function excludeCertainErrors(string $errorOutput, ?int $exitCode = null)
|
||||
|
@ -863,7 +863,6 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
$key = Str::of($variableName);
|
||||
$value = Str::of($variable);
|
||||
}
|
||||
// TODO: here is the problem
|
||||
if ($key->startsWith('SERVICE_FQDN')) {
|
||||
if ($isNew || $savedService->fqdn === null) {
|
||||
$name = $key->after('SERVICE_FQDN_')->beforeLast('_')->lower();
|
||||
@ -1145,6 +1144,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
data_set($service, 'volumes', $serviceVolumes->toArray());
|
||||
}
|
||||
} else {
|
||||
// TODO
|
||||
}
|
||||
// Decide if the service is a database
|
||||
$isDatabase = isDatabaseImage(data_get_str($service, 'image'));
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
// The release version of your application
|
||||
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
|
||||
'release' => '4.0.0-beta.147',
|
||||
'release' => '4.0.0-beta.148',
|
||||
// When left empty or `null` the Laravel environment will be used
|
||||
'environment' => config('app.env'),
|
||||
|
||||
|
@ -1,3 +1,3 @@
|
||||
<?php
|
||||
|
||||
return '4.0.0-beta.147';
|
||||
return '4.0.0-beta.148';
|
||||
|
@ -0,0 +1,30 @@
|
||||
<?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('swarm_dockers', function (Blueprint $table) {
|
||||
$table->string('network');
|
||||
|
||||
$table->unique(['server_id', 'network']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('swarm_dockers', function (Blueprint $table) {
|
||||
$table->dropColumn('network');
|
||||
});
|
||||
}
|
||||
};
|
@ -0,0 +1,30 @@
|
||||
<?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('server_settings', function (Blueprint $table) {
|
||||
$table->renameColumn('is_part_of_swarm', 'is_swarm_manager');
|
||||
$table->boolean('is_swarm_worker')->default(false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('server_settings', function (Blueprint $table) {
|
||||
$table->renameColumn('is_swarm_manager', 'is_part_of_swarm');
|
||||
$table->dropColumn('is_swarm_worker');
|
||||
});
|
||||
}
|
||||
};
|
@ -13,9 +13,9 @@ class SwarmDockerSeeder extends Seeder
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
SwarmDocker::create([
|
||||
'name' => 'Swarm Docker 1',
|
||||
'server_id' => 1,
|
||||
]);
|
||||
// SwarmDocker::create([
|
||||
// 'name' => 'Swarm Docker 1',
|
||||
// 'server_id' => 1,
|
||||
// ]);
|
||||
}
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
#!/command/execlineb -P
|
||||
php /var/www/html/artisan app:init
|
||||
php /var/www/html/artisan app:init --cleanup
|
||||
|
@ -15,6 +15,8 @@
|
||||
<div class="flex-1"></div>
|
||||
@if ($application->build_pack === 'dockercompose' && is_null($application->docker_compose_raw))
|
||||
<div>Please load a Compose file.</div>
|
||||
@elseif ($application->destination->server->isSwarm() && str($application->docker_registry_image_name)->isEmpty())
|
||||
Swarm Deployments requires a Docker Image in a Registry.
|
||||
@else
|
||||
<x-applications.advanced :application="$application" />
|
||||
@if ($application->status !== 'exited')
|
||||
|
@ -131,16 +131,16 @@
|
||||
</form>
|
||||
</div>
|
||||
@if (!$serverReachable)
|
||||
This server is not reachable with the following public key.
|
||||
<br /> <br />
|
||||
Please make sure you have the correct public key in your ~/.ssh/authorized_keys file for user
|
||||
'root' or skip the boarding process and add a new private key manually to Coolify and to the
|
||||
server.
|
||||
<x-forms.input readonly id="serverPublicKey"></x-forms.input>
|
||||
<x-forms.button class="box" wire:target="validateServer"
|
||||
wire:click="validateServer">Check again
|
||||
</x-forms.button>
|
||||
@endif
|
||||
This server is not reachable with the following public key.
|
||||
<br /> <br />
|
||||
Please make sure you have the correct public key in your ~/.ssh/authorized_keys file for user
|
||||
'root' or skip the boarding process and add a new private key manually to Coolify and to the
|
||||
server.
|
||||
<x-forms.input readonly id="serverPublicKey"></x-forms.input>
|
||||
<x-forms.button class="box" wire:target="validateServer" wire:click="validateServer">Check
|
||||
again
|
||||
</x-forms.button>
|
||||
@endif
|
||||
</x-slot:actions>
|
||||
<x-slot:explanation>
|
||||
<p>Private Keys are used to connect to a remote server through a secure shell, called SSH.</p>
|
||||
@ -200,14 +200,17 @@
|
||||
label="Description" id="remoteServerDescription" />
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<x-forms.input required placeholder="127.0.0.1" label="IP Address"
|
||||
id="remoteServerHost" />
|
||||
<x-forms.input required placeholder="127.0.0.1" label="IP Address" id="remoteServerHost" />
|
||||
<x-forms.input required placeholder="Port number of your server. Default is 22."
|
||||
label="Port" id="remoteServerPort" />
|
||||
<x-forms.input required readonly
|
||||
placeholder="Username to connect to your server. Default is root." label="Username"
|
||||
id="remoteServerUser" />
|
||||
</div>
|
||||
{{-- <div class="w-64">
|
||||
<x-forms.checkbox type="checkbox" id="isSwarmManager"
|
||||
label="Is it a Swarm Manager?" />
|
||||
</div> --}}
|
||||
<x-forms.button type="submit">Check Connection</x-forms.button>
|
||||
</form>
|
||||
</x-slot:actions>
|
||||
@ -226,7 +229,7 @@
|
||||
</x-slot:question>
|
||||
<x-slot:actions>
|
||||
<x-forms.button class="justify-center box" wire:click="installDocker">
|
||||
Let's do it!</x-forms.button>
|
||||
Let's do it!</x-forms.button>
|
||||
@if ($dockerInstallationStarted)
|
||||
<x-forms.button class="justify-center box" wire:click="dockerInstalledOrSkipped">
|
||||
Validate Server & Continue</x-forms.button>
|
||||
@ -234,7 +237,10 @@
|
||||
</x-slot:actions>
|
||||
<x-slot:explanation>
|
||||
<p>This will install the latest Docker Engine on your server, configure a few things to be able
|
||||
to run optimal.<br><br>Minimum Docker Engine version is: 22<br><br>To manually install Docker Engine, check <a target="_blank" class="underline text-warning" href="https://coolify.io/docs/servers#install-docker-engine-manually">this documentation</a>.</p>
|
||||
to run optimal.<br><br>Minimum Docker Engine version is: 22<br><br>To manually install Docker
|
||||
Engine, check <a target="_blank" class="underline text-warning"
|
||||
href="https://coolify.io/docs/servers#install-docker-engine-manually">this
|
||||
documentation</a>.</p>
|
||||
</x-slot:explanation>
|
||||
</x-boarding-step>
|
||||
|
||||
|
@ -69,21 +69,39 @@
|
||||
@endif
|
||||
@if ($application->build_pack !== 'dockerimage' && $application->build_pack !== 'dockercompose')
|
||||
<h3>Docker Registry</h3>
|
||||
<div>Push the built image to a docker registry. More info <a class="underline"
|
||||
href="https://coolify.io/docs/docker-registries" target="_blank">here</a>.</div>
|
||||
@if ($application->destination->server->isSwarm())
|
||||
<div>Docker Swarm requires the image to be available in a registry. More info <a class="underline"
|
||||
href="https://coolify.io/docs/docker-registries" target="_blank">here</a>.</div>
|
||||
@else
|
||||
<div>Push the built image to a docker registry. More info <a class="underline"
|
||||
href="https://coolify.io/docs/docker-registries" target="_blank">here</a>.</div>
|
||||
@endif
|
||||
<div class="flex flex-col gap-2 xl:flex-row">
|
||||
@if ($application->build_pack === 'dockerimage')
|
||||
<x-forms.input id="application.docker_registry_image_name" label="Docker Image" />
|
||||
<x-forms.input id="application.docker_registry_image_tag" label="Docker Image Tag" />
|
||||
@if ($application->destination->server->isSwarm())
|
||||
<x-forms.input required id="application.docker_registry_image_name" label="Docker Image" />
|
||||
<x-forms.input id="application.docker_registry_image_tag" label="Docker Image Tag" />
|
||||
@else
|
||||
<x-forms.input id="application.docker_registry_image_name" label="Docker Image" />
|
||||
<x-forms.input id="application.docker_registry_image_tag" label="Docker Image Tag" />
|
||||
@endif
|
||||
@else
|
||||
<x-forms.input id="application.docker_registry_image_name"
|
||||
helper="Empty means it won't push the image to a docker registry."
|
||||
placeholder="Empty means it won't push the image to a docker registry."
|
||||
label="Docker Image" />
|
||||
<x-forms.input id="application.docker_registry_image_tag"
|
||||
placeholder="Empty means only push commit sha tag."
|
||||
helper="If set, it will tag the built image with this tag too. <br><br>Example: If you set it to 'latest', it will push the image with the commit sha tag + with the latest tag."
|
||||
label="Docker Image Tag" />
|
||||
@if ($application->destination->server->isSwarm())
|
||||
<x-forms.input id="application.docker_registry_image_name" required label="Docker Image" />
|
||||
<x-forms.input id="application.docker_registry_image_tag"
|
||||
helper="If set, it will tag the built image with this tag too. <br><br>Example: If you set it to 'latest', it will push the image with the commit sha tag + with the latest tag."
|
||||
label="Docker Image Tag" />
|
||||
@else
|
||||
<x-forms.input id="application.docker_registry_image_name"
|
||||
helper="Empty means it won't push the image to a docker registry."
|
||||
placeholder="Empty means it won't push the image to a docker registry."
|
||||
label="Docker Image" />
|
||||
<x-forms.input id="application.docker_registry_image_tag"
|
||||
placeholder="Empty means only push commit sha tag."
|
||||
helper="If set, it will tag the built image with this tag too. <br><br>Example: If you set it to 'latest', it will push the image with the commit sha tag + with the latest tag."
|
||||
label="Docker Image Tag" />
|
||||
@endif
|
||||
|
||||
@endif
|
||||
|
||||
</div>
|
||||
@ -140,8 +158,8 @@
|
||||
@endif
|
||||
@if ($application->build_pack === 'dockercompose')
|
||||
<x-forms.button wire:click="loadComposeFile">Reload Compose File</x-forms.button>
|
||||
<x-forms.textarea rows="10" readonly id="application.docker_compose" label="Docker Compose Content"
|
||||
helper="You need to modify the docker compose file." />
|
||||
<x-forms.textarea rows="10" readonly id="application.docker_compose"
|
||||
label="Docker Compose Content" helper="You need to modify the docker compose file." />
|
||||
{{-- <x-forms.textarea rows="10" readonly id="application.docker_compose_pr"
|
||||
label="Docker PR Compose Content" helper="You need to modify the docker compose file." /> --}}
|
||||
@endif
|
||||
|
@ -38,8 +38,7 @@
|
||||
<x-forms.input id="server.description" label="Description" />
|
||||
<x-forms.input placeholder="https://example.com" id="wildcard_domain" label="Wildcard Domain"
|
||||
helper="Wildcard domain for your applications. If you set this, you will get a random generated domain for your new applications.<br><span class='font-bold text-white'>Example:</span><br>In case you set:<span class='text-helper'>https://example.com</span> your applications will get:<br> <span class='text-helper'>https://randomId.example.com</span>" />
|
||||
{{-- <x-forms.checkbox disabled type="checkbox" id="server.settings.is_part_of_swarm"
|
||||
label="Is it part of a Swarm cluster?" /> --}}
|
||||
|
||||
</div>
|
||||
<div class="flex flex-col w-full gap-2 lg:flex-row">
|
||||
<x-forms.input id="server.ip" label="IP Address/Domain"
|
||||
@ -49,13 +48,17 @@
|
||||
<x-forms.input type="number" id="server.port" label="Port" required />
|
||||
</div>
|
||||
</div>
|
||||
@if (!$server->isLocalhost())
|
||||
<div class="w-64">
|
||||
<div class="w-64">
|
||||
@if (!$server->isLocalhost())
|
||||
<x-forms.checkbox instantSave
|
||||
helper="If you are using Cloudflare Tunnels, enable this. It will proxy all ssh requests to your server through Cloudflare.<span class='text-warning'>Coolify does not install/setup Cloudflare (cloudflared) on your server.</span>"
|
||||
id="server.settings.is_cloudflare_tunnel" label="Cloudflare Tunnel" />
|
||||
</div>
|
||||
@endif
|
||||
@endif
|
||||
{{-- <x-forms.checkbox instantSave type="checkbox" id="server.settings.is_swarm_manager"
|
||||
label="Is it a Swarm Manager?" /> --}}
|
||||
{{-- <x-forms.checkbox instantSave type="checkbox" id="server.settings.is_swarm_worker"
|
||||
label="Is it a Swarm Worker?" /> --}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if ($server->isFunctional())
|
||||
|
@ -3,7 +3,7 @@
|
||||
<x-limit-reached name="servers" />
|
||||
@else
|
||||
<h1>Create a new Server</h1>
|
||||
<div class="subtitle ">Servers are the main blocks of your infrastructure.</div>
|
||||
<div class="subtitle">Servers are the main blocks of your infrastructure.</div>
|
||||
<form class="flex flex-col gap-2" wire:submit.prevent='submit'>
|
||||
<div class="flex gap-2">
|
||||
<x-forms.input id="name" label="Name" required />
|
||||
@ -25,6 +25,10 @@
|
||||
@endif
|
||||
@endforeach
|
||||
</x-forms.select>
|
||||
{{-- <div class="w-64">
|
||||
<x-forms.checkbox type="checkbox" id="is_swarm_manager"
|
||||
label="Is it a Swarm Manager?" />
|
||||
</div> --}}
|
||||
<x-forms.button type="submit">
|
||||
Save New Server
|
||||
</x-forms.button>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<div>
|
||||
@if ($server->isFunctional())
|
||||
<div class="flex gap-2" @if ($polling) wire:poll.2000ms='checkProxy' @endif>
|
||||
<div class="flex gap-2" wire:poll.5000ms='checkProxy'>
|
||||
@if (data_get($server, 'proxy.status') === 'running')
|
||||
<x-status.running status="Proxy Running" />
|
||||
@elseif (data_get($server, 'proxy.status') === 'restarting')
|
||||
|
@ -16,6 +16,7 @@ curl -fsSL $CDN/.env.production -o /data/coolify/source/.env.production
|
||||
sort -u -t '=' -k 1,1 /data/coolify/source/.env /data/coolify/source/.env.production | sed '/^$/d' > /data/coolify/source/.env.temp && mv /data/coolify/source/.env.temp /data/coolify/source/.env
|
||||
|
||||
# Make sure coolify network exists
|
||||
docker network create coolify 2>/dev/null
|
||||
docker network create --attachable coolify 2>/dev/null
|
||||
# docker network create --attachable --driver=overlay coolify-overlay 2>/dev/null
|
||||
|
||||
docker run --pull always -v /data/coolify/source:/data/coolify/source -v /var/run/docker.sock:/var/run/docker.sock --rm ghcr.io/coollabsio/coolify-helper bash -c "LATEST_IMAGE=${1:-} docker compose --env-file /data/coolify/source/.env -f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.prod.yml up -d --pull always --remove-orphans --force-recreate"
|
||||
|
@ -4,7 +4,7 @@
|
||||
"version": "3.12.36"
|
||||
},
|
||||
"v4": {
|
||||
"version": "4.0.0-beta.147"
|
||||
"version": "4.0.0-beta.148"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user