This commit is contained in:
Andras Bacsai 2023-05-24 15:25:08 +02:00
parent a0306f3951
commit 167379b0e0
23 changed files with 118 additions and 125 deletions

View File

@ -100,7 +100,7 @@ protected function getCommand(): string
$port = $this->activity->getExtraProperty('port');
$command = $this->activity->getExtraProperty('command');
return generateSshCommand($private_key_location, $server_ip, $user, $port, $command);
return generate_ssh_command($private_key_location, $server_ip, $user, $port, $command);
}
protected function handleOutput(string $type, string $output)

View File

@ -11,7 +11,7 @@ class CheckProxySettingsInSync
public function __invoke(Server $server, bool $reset = false)
{
$proxy_path = config('coolify.proxy_config_path');
$output = instantRemoteProcess([
$output = instant_remote_process([
"cat $proxy_path/docker-compose.yml",
], $server, false);
if (is_null($output) || $reset) {
@ -23,7 +23,7 @@ public function __invoke(Server $server, bool $reset = false)
$server->extra_attributes->last_saved_proxy_settings = Str::of($docker_compose_yml_base64)->pipe('md5')->value;
$server->save();
if (is_null($output) || $reset) {
instantRemoteProcess([
instant_remote_process([
"mkdir -p $proxy_path",
"echo '$docker_compose_yml_base64' | base64 -d > $proxy_path/docker-compose.yml",
], $server);

View File

@ -25,7 +25,7 @@ public function __invoke(Server $server): Activity
return "docker network ls --format '{{.Name}}' | grep '^$network$' >/dev/null 2>&1 || docker network create --attachable $network > /dev/null 2>&1";
});
$configuration = instantRemoteProcess([
$configuration = instant_remote_process([
"cat $proxy_path/docker-compose.yml",
], $server, false);
if (is_null($configuration)) {
@ -40,7 +40,7 @@ public function __invoke(Server $server): Activity
$env_file_base64 = base64_encode(
$this->getEnvContents()
);
$activity = remoteProcess([
$activity = remote_process([
...$create_networks_command,
"echo 'Docker networks created...'",
"mkdir -p $proxy_path",

View File

@ -10,7 +10,7 @@ class InstallDocker
public function __invoke(Server $server)
{
$config = base64_encode('{ "live-restore": true }');
$activity = remoteProcess([
$activity = remote_process([
"echo Installing Docker...",
"curl https://releases.rancher.com/install-docker/23.0.sh | sh",
"echo Configuring Docker...",

View File

@ -25,8 +25,8 @@ public function delete()
if ($this->destination->attachedTo()) {
return $this->emit('error', 'You must delete all resources before deleting this destination.');
}
instantRemoteProcess(["docker network disconnect {$this->destination->network} coolify-proxy"], $this->destination->server, throwError: false);
instantRemoteProcess(['docker network rm -f ' . $this->destination->network], $this->destination->server);
instant_remote_process(["docker network disconnect {$this->destination->network} coolify-proxy"], $this->destination->server, throwError: false);
instant_remote_process(['docker network rm -f ' . $this->destination->network], $this->destination->server);
}
$this->destination->delete();
return redirect()->route('dashboard');

View File

@ -45,8 +45,8 @@ public function submit()
return;
}
$server = Server::find($this->server_id);
instantRemoteProcess(['docker network create --attachable ' . $this->network], $server, throwError: false);
instantRemoteProcess(["docker network connect $this->network coolify-proxy"], $server, throwError: false);
instant_remote_process(['docker network create --attachable ' . $this->network], $server, throwError: false);
instant_remote_process(["docker network connect $this->network coolify-proxy"], $server, throwError: false);
$docker = ModelsStandaloneDocker::create([
'name' => $this->name,

View File

@ -15,10 +15,10 @@ public function upgrade()
if (!$server) {
return;
}
instantRemoteProcess([
instant_remote_process([
"sleep 2"
], $server);
remoteProcess([
remote_process([
"sleep 10"
], $server, ActivityTypes::INLINE->value);
$this->emit('updateInitiated');
@ -31,18 +31,18 @@ public function upgrade()
return;
}
instantRemoteProcess([
instant_remote_process([
"curl -fsSL $cdn/docker-compose.yml -o /data/coolify/source/docker-compose.yml",
"curl -fsSL $cdn/docker-compose.prod.yml -o /data/coolify/source/docker-compose.prod.yml",
"curl -fsSL $cdn/.env.production -o /data/coolify/source/.env.production",
"curl -fsSL $cdn/upgrade.sh -o /data/coolify/source/upgrade.sh",
], $server);
instantRemoteProcess([
instant_remote_process([
"docker compose -f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.prod.yml pull",
], $server);
remoteProcess([
remote_process([
"bash /data/coolify/source/upgrade.sh $latestVersion"
], $server, ActivityTypes::INLINE->value);

View File

@ -18,7 +18,7 @@ public function delete()
{
$destination = $this->application->destination->getMorphClass()::where('id', $this->application->destination->id)->first();
instantRemoteProcess(["docker rm -f {$this->application->uuid}"], $destination->server);
instant_remote_process(["docker rm -f {$this->application->uuid}"], $destination->server);
$this->application->delete();
return redirect()->route('project.resources', [
'project_uuid' => $this->parameters['project_uuid'],

View File

@ -42,13 +42,13 @@ public function loadImages()
{
try {
$image = $this->application->uuid;
$output = instantRemoteProcess([
$output = instant_remote_process([
"docker inspect --format='{{.Config.Image}}' {$this->application->uuid}",
], $this->application->destination->server, throwError: false);
$current_tag = Str::of($output)->trim()->explode(":");
$this->current = data_get($current_tag, 1);
$output = instantRemoteProcess([
$output = instant_remote_process([
"docker images --format '{{.Repository}}#{{.Tag}}#{{.CreatedAt}}'",
], $this->application->destination->server);
$this->images = Str::of($output)->trim()->explode("\n")->filter(function ($item) use ($image) {

View File

@ -26,7 +26,7 @@ public function runCommand()
{
try {
$this->validate();
$activity = remoteProcess([$this->command], Server::where('uuid', $this->server)->first(), ActivityTypes::INLINE->value);
$activity = remote_process([$this->command], Server::where('uuid', $this->server)->first(), ActivityTypes::INLINE->value);
$this->emit('newMonitorActivity', $activity->id);
} catch (\Exception $e) {
return general_error_handler($e);

View File

@ -36,7 +36,7 @@ public function installDocker()
public function validateServer()
{
try {
$this->uptime = instantRemoteProcess(['uptime'], $this->server, false);
$this->uptime = instant_remote_process(['uptime'], $this->server, false);
if (!$this->uptime) {
$this->uptime = 'Server not reachable.';
throw new \Exception('Server not reachable.');
@ -47,11 +47,11 @@ public function validateServer()
$this->emit('serverValidated');
}
}
$this->dockerVersion = instantRemoteProcess(['docker version|head -2|grep -i version'], $this->server, false);
$this->dockerVersion = instant_remote_process(['docker version|head -2|grep -i version'], $this->server, false);
if (!$this->dockerVersion) {
$this->dockerVersion = 'Not installed.';
}
$this->dockerComposeVersion = instantRemoteProcess(['docker compose version|head -2|grep -i version'], $this->server, false);
$this->dockerComposeVersion = instant_remote_process(['docker compose version|head -2|grep -i version'], $this->server, false);
if (!$this->dockerComposeVersion) {
$this->dockerComposeVersion = 'Not installed.';
}

View File

@ -51,7 +51,7 @@ public function setProxy()
}
public function stopProxy()
{
instantRemoteProcess([
instant_remote_process([
"docker rm -f coolify-proxy",
], $this->server);
$this->server->extra_attributes->proxy_status = 'exited';
@ -65,7 +65,7 @@ public function saveConfiguration()
$docker_compose_yml_base64 = base64_encode($this->proxy_settings);
$this->server->extra_attributes->last_saved_proxy_settings = Str::of($docker_compose_yml_base64)->pipe('md5')->value;
$this->server->save();
instantRemoteProcess([
instant_remote_process([
"echo '$docker_compose_yml_base64' | base64 -d > $proxy_path/docker-compose.yml",
], $this->server);
} catch (\Exception $e) {

View File

@ -60,7 +60,7 @@ public function __construct(
$server = $this->destination->server;
$private_key_location = savePrivateKeyForServer($server);
$private_key_location = save_private_key_for_server($server);
$remoteProcessArgs = new CoolifyTaskArgs(
server_ip: $server->ip,
@ -452,13 +452,13 @@ private function execute_now(
if ($isDebuggable && !$this->application->settings->is_debug) {
$hideFromOutput = true;
}
$remoteProcess = resolve(RunRemoteProcess::class, [
$remote_process = resolve(RunRemoteProcess::class, [
'activity' => $this->activity,
'hideFromOutput' => $hideFromOutput,
'isFinished' => $isFinished,
'ignoreErrors' => $ignoreErrors,
]);
$result = $remoteProcess();
$result = $remote_process();
if ($propertyName) {
$this->activity->properties = $this->activity->properties->merge([
$propertyName => trim($result->output()),

View File

@ -47,7 +47,7 @@ protected function check_all_servers()
$not_found_applications = $applications;
$containers = collect();
foreach ($servers as $server) {
$output = instantRemoteProcess(['docker ps -a -q --format \'{{json .}}\''], $server);
$output = instant_remote_process(['docker ps -a -q --format \'{{json .}}\''], $server);
$containers = $containers->concat(format_docker_command_output_to_json($output));
}
foreach ($containers as $container) {

View File

@ -29,7 +29,7 @@ public function handle(): void
{
try {
$application = Application::find($this->application_id)->first();
instantRemoteProcess(["docker rm -f {$application->uuid}"], $this->server);
instant_remote_process(["docker rm -f {$application->uuid}"], $this->server);
$application->status = get_container_status(server: $application->destination->server, container_id: $application->uuid);
$application->save();
} catch (\Exception $e) {

View File

@ -19,18 +19,19 @@ class CoolifyTask implements ShouldQueue
*/
public function __construct(
public Activity $activity,
){}
) {
}
/**
* Execute the job.
*/
public function handle(): void
{
$remoteProcess = resolve(RunRemoteProcess::class, [
$remote_process = resolve(RunRemoteProcess::class, [
'activity' => $this->activity,
]);
$remoteProcess();
$remote_process();
// @TODO: Remove file at $this->activity->getExtraProperty('private_key_location') after process is finished
}
}

View File

@ -46,10 +46,10 @@ public function handle(): void
if (!$server) {
return;
}
instantRemoteProcess([
instant_remote_process([
"sleep 2"
], $server);
remoteProcess([
remote_process([
"sleep 10"
], $server, ActivityTypes::INLINE->value);
} else {
@ -68,18 +68,18 @@ public function handle(): void
return;
}
instantRemoteProcess([
instant_remote_process([
"curl -fsSL $cdn/docker-compose.yml -o /data/coolify/source/docker-compose.yml",
"curl -fsSL $cdn/docker-compose.prod.yml -o /data/coolify/source/docker-compose.prod.yml",
"curl -fsSL $cdn/.env.production -o /data/coolify/source/.env.production",
"curl -fsSL $cdn/upgrade.sh -o /data/coolify/source/upgrade.sh",
], $server);
instantRemoteProcess([
instant_remote_process([
"docker compose -f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.prod.yml pull",
], $server);
remoteProcess([
remote_process([
"bash /data/coolify/source/upgrade.sh $latest_version"
], $server, ActivityTypes::INLINE->value);
}

View File

@ -31,7 +31,7 @@ public function handle(): void
try {
$servers = Server::all();
foreach ($servers as $server) {
instantRemoteProcess(['docker image prune -f'], $server);
instant_remote_process(['docker image prune -f'], $server);
}
} catch (\Exception $e) {
Log::error($e->getMessage());

View File

@ -27,7 +27,7 @@ public function register(): void
public function boot(): void
{
Queue::after(function (JobProcessed $event) {
// @TODO: Remove `coolify-builder` container after the remoteProcess job is finishged and remoteProcess->type == `deployment`.
// @TODO: Remove `coolify-builder` container after the remote_process job is finishged and remote_process->type == `deployment`.
if ($event->job->resolveName() === CoolifyTask::class) {
}
});

View File

@ -31,7 +31,7 @@ function format_docker_labels_to_json($rawOutput): Collection
function get_container_status(Server $server, string $container_id, bool $throwError = false)
{
$container = instantRemoteProcess(["docker inspect --format '{{json .State}}' {$container_id}"], $server, $throwError);
$container = instant_remote_process(["docker inspect --format '{{json .State}}' {$container_id}"], $server, $throwError);
if (!$container) {
return 'exited';
}

View File

@ -1,5 +1,6 @@
<?php
use App\Actions\CoolifyTask\PrepareCoolifyTask;
use App\Data\CoolifyTaskArgs;
use App\Models\Server;
use Illuminate\Database\Eloquent\Model;
@ -7,96 +8,87 @@
use Illuminate\Support\Facades\Storage;
use Spatie\Activitylog\Models\Activity;
if (!function_exists('remoteProcess')) {
/**
* Run a Remote Process, which SSH's asynchronously into a machine to run the command(s).
* @TODO Change 'root' to 'coolify' when it's able to run Docker commands without sudo
*
*/
function remoteProcess(
array $command,
Server $server,
string $type,
?string $type_uuid = null,
?Model $model = null,
): Activity {
/**
* Run a Remote Process, which SSH's asynchronously into a machine to run the command(s).
* @TODO Change 'root' to 'coolify' when it's able to run Docker commands without sudo
*
*/
function remote_process(
array $command,
Server $server,
string $type,
?string $type_uuid = null,
?Model $model = null,
): Activity {
$command_string = implode("\n", $command);
$command_string = implode("\n", $command);
// @TODO: Check if the user has access to this server
// checkTeam($server->team_id);
// @TODO: Check if the user has access to this server
// checkTeam($server->team_id);
$private_key_location = savePrivateKeyForServer($server);
$private_key_location = save_private_key_for_server($server);
return resolve(PrepareCoolifyTask::class, [
'remoteProcessArgs' => new CoolifyTaskArgs(
server_ip: $server->ip,
private_key_location: $private_key_location,
command: <<<EOT
return resolve(PrepareCoolifyTask::class, [
'remoteProcessArgs' => new CoolifyTaskArgs(
server_ip: $server->ip,
private_key_location: $private_key_location,
command: <<<EOT
{$command_string}
EOT,
port: $server->port,
user: $server->user,
type: $type,
type_uuid: $type_uuid,
model: $model,
),
])();
}
port: $server->port,
user: $server->user,
type: $type,
type_uuid: $type_uuid,
model: $model,
),
])();
}
function save_private_key_for_server(Server $server)
{
$temp_file = "id.root@{$server->ip}";
Storage::disk('ssh-keys')->put($temp_file, $server->privateKey->private_key);
return '/var/www/html/storage/app/ssh-keys/' . $temp_file;
}
if (!function_exists('savePrivateKeyForServer')) {
function savePrivateKeyForServer(Server $server)
{
$temp_file = "id.root@{$server->ip}";
Storage::disk('ssh-keys')->put($temp_file, $server->privateKey->private_key);
return '/var/www/html/storage/app/ssh-keys/' . $temp_file;
function generate_ssh_command(string $private_key_location, string $server_ip, string $user, string $port, string $command, bool $isMux = true)
{
Storage::disk('local')->makeDirectory('.ssh');
$delimiter = 'EOF-COOLIFY-SSH';
$ssh_command = "ssh ";
if ($isMux && config('coolify.mux_enabled')) {
$ssh_command .= '-o ControlMaster=auto -o ControlPersist=1m -o ControlPath=/var/www/html/storage/app/.ssh/ssh_mux_%h_%p_%r ';
}
$ssh_command .= "-i {$private_key_location} "
. '-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null '
. '-o PasswordAuthentication=no '
. '-o ConnectTimeout=3600 '
. '-o ServerAliveInterval=20 '
. '-o RequestTTY=no '
. '-o LogLevel=ERROR '
. "-p {$port} "
. "{$user}@{$server_ip} "
. " 'bash -se' << \\$delimiter" . PHP_EOL
. $command . PHP_EOL
. $delimiter;
return $ssh_command;
}
if (!function_exists('generateSshCommand')) {
function generateSshCommand(string $private_key_location, string $server_ip, string $user, string $port, string $command, bool $isMux = true)
{
Storage::disk('local')->makeDirectory('.ssh');
$delimiter = 'EOF-COOLIFY-SSH';
$ssh_command = "ssh ";
if ($isMux && config('coolify.mux_enabled')) {
$ssh_command .= '-o ControlMaster=auto -o ControlPersist=1m -o ControlPath=/var/www/html/storage/app/.ssh/ssh_mux_%h_%p_%r ';
function instant_remote_process(array $command, Server $server, $throwError = true)
{
$command_string = implode("\n", $command);
$private_key_location = save_private_key_for_server($server);
$ssh_command = generate_ssh_command($private_key_location, $server->ip, $server->user, $server->port, $command_string);
$process = Process::run($ssh_command);
$output = trim($process->output());
$exitCode = $process->exitCode();
if ($exitCode !== 0) {
if (!$throwError) {
return null;
}
$ssh_command .= "-i {$private_key_location} "
. '-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null '
. '-o PasswordAuthentication=no '
. '-o ConnectTimeout=3600 '
. '-o ServerAliveInterval=20 '
. '-o RequestTTY=no '
. '-o LogLevel=ERROR '
. "-p {$port} "
. "{$user}@{$server_ip} "
. " 'bash -se' << \\$delimiter" . PHP_EOL
. $command . PHP_EOL
. $delimiter;
return $ssh_command;
}
}
if (!function_exists('instantRemoteProcess')) {
function instantRemoteProcess(array $command, Server $server, $throwError = true)
{
$command_string = implode("\n", $command);
$private_key_location = savePrivateKeyForServer($server);
$ssh_command = generateSshCommand($private_key_location, $server->ip, $server->user, $server->port, $command_string);
$process = Process::run($ssh_command);
$output = trim($process->output());
$exitCode = $process->exitCode();
if ($exitCode !== 0) {
if (!$throwError) {
return null;
}
throw new \RuntimeException($process->errorOutput());
}
return $output;
throw new \RuntimeException($process->errorOutput());
}
return $output;
}

View File

@ -33,28 +33,28 @@
$containerName = 'coolify_test_' . now()->format('Ymd_his');
$host = Server::where('name', 'testing-local-docker-container')->first();
remoteProcess([
remote_process([
"docker rm -f $(docker ps --filter='name={$coolifyNamePrefix}*' -aq) > /dev/null 2>&1"
], $host);
// Assert there's no containers start with coolify_test_*
$activity = remoteProcess([$areThereCoolifyTestContainers], $host);
$activity = remote_process([$areThereCoolifyTestContainers], $host);
$tidyOutput = RunRemoteProcess::decodeOutput($activity);
$containers = format_docker_command_output_to_json($tidyOutput);
expect($containers)->toBeEmpty();
// start a container nginx -d --name = $containerName
$activity = remoteProcess(["docker run -d --rm --name {$containerName} nginx"], $host);
$activity = remote_process(["docker run -d --rm --name {$containerName} nginx"], $host);
expect($activity->getExtraProperty('exitCode'))->toBe(0);
// docker ps name = $container
$activity = remoteProcess([$areThereCoolifyTestContainers], $host);
$activity = remote_process([$areThereCoolifyTestContainers], $host);
$tidyOutput = RunRemoteProcess::decodeOutput($activity);
$containers = format_docker_command_output_to_json($tidyOutput);
expect($containers->where('Names', $containerName)->count())->toBe(1);
// Stop testing containers
$activity = remoteProcess([
$activity = remote_process([
"docker ps --filter='name={$coolifyNamePrefix}*' -aq && " .
"docker rm -f $(docker ps --filter='name={$coolifyNamePrefix}*' -aq)"
], $host);

View File

@ -17,7 +17,7 @@
$host = Server::where('name', 'testing-local-docker-container')->first();
$activity = remoteProcess([
$activity = remote_process([
'pwd',
'x=1; while [ $x -le 3 ]; do sleep 0.1 && echo "Welcome $x times" $(( x++ )); done',
], $host);