Merge branch 'next' into improve-cleanup
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Actions\Application;
|
||||
|
||||
use App\Actions\Server\CleanupDocker;
|
||||
use App\Models\Application;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
@@ -9,44 +10,35 @@ class StopApplication
|
||||
{
|
||||
use AsAction;
|
||||
|
||||
public function handle(Application $application, bool $previewDeployments = false)
|
||||
public function handle(Application $application, bool $previewDeployments = false, bool $dockerCleanup = true)
|
||||
{
|
||||
if ($application->destination->server->isSwarm()) {
|
||||
instant_remote_process(["docker stack rm {$application->uuid}"], $application->destination->server);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$servers = collect([]);
|
||||
$servers->push($application->destination->server);
|
||||
$application->additional_servers->map(function ($server) use ($servers) {
|
||||
$servers->push($server);
|
||||
});
|
||||
foreach ($servers as $server) {
|
||||
try {
|
||||
$server = $application->destination->server;
|
||||
if (! $server->isFunctional()) {
|
||||
return 'Server is not functional';
|
||||
}
|
||||
if ($previewDeployments) {
|
||||
$containers = getCurrentApplicationContainerStatus($server, $application->id, includePullrequests: true);
|
||||
} 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(command: ["docker stop --time=30 $containerName"], server: $server, throwError: false);
|
||||
instant_remote_process(command: ["docker rm $containerName"], server: $server, throwError: false);
|
||||
instant_remote_process(command: ["docker rm -f {$containerName}"], server: $server, throwError: false);
|
||||
}
|
||||
}
|
||||
ray('Stopping application: '.$application->name);
|
||||
|
||||
if ($server->isSwarm()) {
|
||||
instant_remote_process(["docker stack rm {$application->uuid}"], $server);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$containersToStop = $application->getContainersToStop($previewDeployments);
|
||||
$application->stopContainers($containersToStop, $server);
|
||||
|
||||
if ($application->build_pack === 'dockercompose') {
|
||||
// remove network
|
||||
$uuid = $application->uuid;
|
||||
instant_remote_process(["docker network disconnect {$uuid} coolify-proxy"], $server, false);
|
||||
instant_remote_process(["docker network rm {$uuid}"], $server, false);
|
||||
$application->delete_connected_networks($application->uuid);
|
||||
}
|
||||
|
||||
if ($dockerCleanup) {
|
||||
CleanupDocker::dispatch($server, true);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
ray($e->getMessage());
|
||||
|
||||
return $e->getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Actions\CoolifyTask;
|
||||
|
||||
use App\Enums\ActivityTypes;
|
||||
use App\Enums\ProcessStatus;
|
||||
use App\Helpers\SshMultiplexingHelper;
|
||||
use App\Jobs\ApplicationDeploymentJob;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Process\ProcessResult;
|
||||
@@ -137,7 +138,7 @@ class RunRemoteProcess
|
||||
$command = $this->activity->getExtraProperty('command');
|
||||
$server = Server::whereUuid($server_uuid)->firstOrFail();
|
||||
|
||||
return generateSshCommand($server, $command);
|
||||
return SshMultiplexingHelper::generateSshCommand($server, $command);
|
||||
}
|
||||
|
||||
protected function handleOutput(string $type, string $output)
|
||||
|
||||
@@ -23,7 +23,7 @@ class StartDragonfly
|
||||
$startCommand = "dragonfly --requirepass {$this->database->dragonfly_password}";
|
||||
|
||||
$container_name = $this->database->uuid;
|
||||
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
|
||||
$this->configuration_dir = database_configuration_dir().'/'.$container_name;
|
||||
|
||||
$this->commands = [
|
||||
"echo 'Starting {$database->name}.'",
|
||||
@@ -75,7 +75,7 @@ class StartDragonfly
|
||||
],
|
||||
],
|
||||
];
|
||||
if (!is_null($this->database->limits_cpuset)) {
|
||||
if (! is_null($this->database->limits_cpuset)) {
|
||||
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
|
||||
}
|
||||
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
||||
@@ -118,10 +118,10 @@ class StartDragonfly
|
||||
$local_persistent_volumes = [];
|
||||
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
|
||||
$local_persistent_volumes[] = $persistentStorage->host_path . ':' . $persistentStorage->mount_path;
|
||||
$local_persistent_volumes[] = $persistentStorage->host_path.':'.$persistentStorage->mount_path;
|
||||
} else {
|
||||
$volume_name = $persistentStorage->name;
|
||||
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
||||
$local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ class StartDragonfly
|
||||
$environment_variables->push("$env->key=$env->real_value");
|
||||
}
|
||||
|
||||
if ($environment_variables->filter(fn($env) => str($env)->contains('REDIS_PASSWORD'))->isEmpty()) {
|
||||
if ($environment_variables->filter(fn ($env) => str($env)->contains('REDIS_PASSWORD'))->isEmpty()) {
|
||||
$environment_variables->push("REDIS_PASSWORD={$this->database->dragonfly_password}");
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ class StartKeydb
|
||||
$startCommand = "keydb-server --requirepass {$this->database->keydb_password} --appendonly yes";
|
||||
|
||||
$container_name = $this->database->uuid;
|
||||
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
|
||||
$this->configuration_dir = database_configuration_dir().'/'.$container_name;
|
||||
|
||||
$this->commands = [
|
||||
"echo 'Starting {$database->name}.'",
|
||||
@@ -74,7 +74,7 @@ class StartKeydb
|
||||
],
|
||||
],
|
||||
];
|
||||
if (!is_null($this->database->limits_cpuset)) {
|
||||
if (! is_null($this->database->limits_cpuset)) {
|
||||
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
|
||||
}
|
||||
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
||||
@@ -94,10 +94,10 @@ class StartKeydb
|
||||
if (count($volume_names) > 0) {
|
||||
$docker_compose['volumes'] = $volume_names;
|
||||
}
|
||||
if (!is_null($this->database->keydb_conf) || !empty($this->database->keydb_conf)) {
|
||||
if (! is_null($this->database->keydb_conf) || ! empty($this->database->keydb_conf)) {
|
||||
$docker_compose['services'][$container_name]['volumes'][] = [
|
||||
'type' => 'bind',
|
||||
'source' => $this->configuration_dir . '/keydb.conf',
|
||||
'source' => $this->configuration_dir.'/keydb.conf',
|
||||
'target' => '/etc/keydb/keydb.conf',
|
||||
'read_only' => true,
|
||||
];
|
||||
@@ -125,10 +125,10 @@ class StartKeydb
|
||||
$local_persistent_volumes = [];
|
||||
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
|
||||
$local_persistent_volumes[] = $persistentStorage->host_path . ':' . $persistentStorage->mount_path;
|
||||
$local_persistent_volumes[] = $persistentStorage->host_path.':'.$persistentStorage->mount_path;
|
||||
} else {
|
||||
$volume_name = $persistentStorage->name;
|
||||
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
||||
$local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ class StartKeydb
|
||||
$environment_variables->push("$env->key=$env->real_value");
|
||||
}
|
||||
|
||||
if ($environment_variables->filter(fn($env) => str($env)->contains('REDIS_PASSWORD'))->isEmpty()) {
|
||||
if ($environment_variables->filter(fn ($env) => str($env)->contains('REDIS_PASSWORD'))->isEmpty()) {
|
||||
$environment_variables->push("REDIS_PASSWORD={$this->database->keydb_password}");
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ class StartMariadb
|
||||
$this->database = $database;
|
||||
|
||||
$container_name = $this->database->uuid;
|
||||
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
|
||||
$this->configuration_dir = database_configuration_dir().'/'.$container_name;
|
||||
|
||||
$this->commands = [
|
||||
"echo 'Starting {$database->name}.'",
|
||||
@@ -69,7 +69,7 @@ class StartMariadb
|
||||
],
|
||||
],
|
||||
];
|
||||
if (!is_null($this->database->limits_cpuset)) {
|
||||
if (! is_null($this->database->limits_cpuset)) {
|
||||
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
|
||||
}
|
||||
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
||||
@@ -89,10 +89,10 @@ class StartMariadb
|
||||
if (count($volume_names) > 0) {
|
||||
$docker_compose['volumes'] = $volume_names;
|
||||
}
|
||||
if (!is_null($this->database->mariadb_conf) || !empty($this->database->mariadb_conf)) {
|
||||
if (! is_null($this->database->mariadb_conf) || ! empty($this->database->mariadb_conf)) {
|
||||
$docker_compose['services'][$container_name]['volumes'][] = [
|
||||
'type' => 'bind',
|
||||
'source' => $this->configuration_dir . '/custom-config.cnf',
|
||||
'source' => $this->configuration_dir.'/custom-config.cnf',
|
||||
'target' => '/etc/mysql/conf.d/custom-config.cnf',
|
||||
'read_only' => true,
|
||||
];
|
||||
@@ -120,10 +120,10 @@ class StartMariadb
|
||||
$local_persistent_volumes = [];
|
||||
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
|
||||
$local_persistent_volumes[] = $persistentStorage->host_path . ':' . $persistentStorage->mount_path;
|
||||
$local_persistent_volumes[] = $persistentStorage->host_path.':'.$persistentStorage->mount_path;
|
||||
} else {
|
||||
$volume_name = $persistentStorage->name;
|
||||
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
||||
$local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,18 +154,18 @@ class StartMariadb
|
||||
$environment_variables->push("$env->key=$env->real_value");
|
||||
}
|
||||
|
||||
if ($environment_variables->filter(fn($env) => str($env)->contains('MARIADB_ROOT_PASSWORD'))->isEmpty()) {
|
||||
if ($environment_variables->filter(fn ($env) => str($env)->contains('MARIADB_ROOT_PASSWORD'))->isEmpty()) {
|
||||
$environment_variables->push("MARIADB_ROOT_PASSWORD={$this->database->mariadb_root_password}");
|
||||
}
|
||||
|
||||
if ($environment_variables->filter(fn($env) => str($env)->contains('MARIADB_DATABASE'))->isEmpty()) {
|
||||
if ($environment_variables->filter(fn ($env) => str($env)->contains('MARIADB_DATABASE'))->isEmpty()) {
|
||||
$environment_variables->push("MARIADB_DATABASE={$this->database->mariadb_database}");
|
||||
}
|
||||
|
||||
if ($environment_variables->filter(fn($env) => str($env)->contains('MARIADB_USER'))->isEmpty()) {
|
||||
if ($environment_variables->filter(fn ($env) => str($env)->contains('MARIADB_USER'))->isEmpty()) {
|
||||
$environment_variables->push("MARIADB_USER={$this->database->mariadb_user}");
|
||||
}
|
||||
if ($environment_variables->filter(fn($env) => str($env)->contains('MARIADB_PASSWORD'))->isEmpty()) {
|
||||
if ($environment_variables->filter(fn ($env) => str($env)->contains('MARIADB_PASSWORD'))->isEmpty()) {
|
||||
$environment_variables->push("MARIADB_PASSWORD={$this->database->mariadb_password}");
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ class StartMongodb
|
||||
$startCommand = 'mongod';
|
||||
|
||||
$container_name = $this->database->uuid;
|
||||
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
|
||||
$this->configuration_dir = database_configuration_dir().'/'.$container_name;
|
||||
|
||||
$this->commands = [
|
||||
"echo 'Starting {$database->name}.'",
|
||||
@@ -77,7 +77,7 @@ class StartMongodb
|
||||
],
|
||||
],
|
||||
];
|
||||
if (!is_null($this->database->limits_cpuset)) {
|
||||
if (! is_null($this->database->limits_cpuset)) {
|
||||
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
|
||||
}
|
||||
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
||||
@@ -97,19 +97,19 @@ class StartMongodb
|
||||
if (count($volume_names) > 0) {
|
||||
$docker_compose['volumes'] = $volume_names;
|
||||
}
|
||||
if (!is_null($this->database->mongo_conf) || !empty($this->database->mongo_conf)) {
|
||||
if (! is_null($this->database->mongo_conf) || ! empty($this->database->mongo_conf)) {
|
||||
$docker_compose['services'][$container_name]['volumes'][] = [
|
||||
'type' => 'bind',
|
||||
'source' => $this->configuration_dir . '/mongod.conf',
|
||||
'source' => $this->configuration_dir.'/mongod.conf',
|
||||
'target' => '/etc/mongo/mongod.conf',
|
||||
'read_only' => true,
|
||||
];
|
||||
$docker_compose['services'][$container_name]['command'] = $startCommand . ' --config /etc/mongo/mongod.conf';
|
||||
$docker_compose['services'][$container_name]['command'] = $startCommand.' --config /etc/mongo/mongod.conf';
|
||||
}
|
||||
$this->add_default_database();
|
||||
$docker_compose['services'][$container_name]['volumes'][] = [
|
||||
'type' => 'bind',
|
||||
'source' => $this->configuration_dir . '/docker-entrypoint-initdb.d',
|
||||
'source' => $this->configuration_dir.'/docker-entrypoint-initdb.d',
|
||||
'target' => '/docker-entrypoint-initdb.d',
|
||||
'read_only' => true,
|
||||
];
|
||||
@@ -136,10 +136,10 @@ class StartMongodb
|
||||
$local_persistent_volumes = [];
|
||||
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
|
||||
$local_persistent_volumes[] = $persistentStorage->host_path . ':' . $persistentStorage->mount_path;
|
||||
$local_persistent_volumes[] = $persistentStorage->host_path.':'.$persistentStorage->mount_path;
|
||||
} else {
|
||||
$volume_name = $persistentStorage->name;
|
||||
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
||||
$local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,15 +170,15 @@ class StartMongodb
|
||||
$environment_variables->push("$env->key=$env->real_value");
|
||||
}
|
||||
|
||||
if ($environment_variables->filter(fn($env) => str($env)->contains('MONGO_INITDB_ROOT_USERNAME'))->isEmpty()) {
|
||||
if ($environment_variables->filter(fn ($env) => str($env)->contains('MONGO_INITDB_ROOT_USERNAME'))->isEmpty()) {
|
||||
$environment_variables->push("MONGO_INITDB_ROOT_USERNAME={$this->database->mongo_initdb_root_username}");
|
||||
}
|
||||
|
||||
if ($environment_variables->filter(fn($env) => str($env)->contains('MONGO_INITDB_ROOT_PASSWORD'))->isEmpty()) {
|
||||
if ($environment_variables->filter(fn ($env) => str($env)->contains('MONGO_INITDB_ROOT_PASSWORD'))->isEmpty()) {
|
||||
$environment_variables->push("MONGO_INITDB_ROOT_PASSWORD={$this->database->mongo_initdb_root_password}");
|
||||
}
|
||||
|
||||
if ($environment_variables->filter(fn($env) => str($env)->contains('MONGO_INITDB_DATABASE'))->isEmpty()) {
|
||||
if ($environment_variables->filter(fn ($env) => str($env)->contains('MONGO_INITDB_DATABASE'))->isEmpty()) {
|
||||
$environment_variables->push("MONGO_INITDB_DATABASE={$this->database->mongo_initdb_database}");
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ class StartMysql
|
||||
$this->database = $database;
|
||||
|
||||
$container_name = $this->database->uuid;
|
||||
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
|
||||
$this->configuration_dir = database_configuration_dir().'/'.$container_name;
|
||||
|
||||
$this->commands = [
|
||||
"echo 'Starting {$database->name}.'",
|
||||
@@ -69,7 +69,7 @@ class StartMysql
|
||||
],
|
||||
],
|
||||
];
|
||||
if (!is_null($this->database->limits_cpuset)) {
|
||||
if (! is_null($this->database->limits_cpuset)) {
|
||||
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
|
||||
}
|
||||
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
||||
@@ -89,10 +89,10 @@ class StartMysql
|
||||
if (count($volume_names) > 0) {
|
||||
$docker_compose['volumes'] = $volume_names;
|
||||
}
|
||||
if (!is_null($this->database->mysql_conf) || !empty($this->database->mysql_conf)) {
|
||||
if (! is_null($this->database->mysql_conf) || ! empty($this->database->mysql_conf)) {
|
||||
$docker_compose['services'][$container_name]['volumes'][] = [
|
||||
'type' => 'bind',
|
||||
'source' => $this->configuration_dir . '/custom-config.cnf',
|
||||
'source' => $this->configuration_dir.'/custom-config.cnf',
|
||||
'target' => '/etc/mysql/conf.d/custom-config.cnf',
|
||||
'read_only' => true,
|
||||
];
|
||||
@@ -120,10 +120,10 @@ class StartMysql
|
||||
$local_persistent_volumes = [];
|
||||
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
|
||||
$local_persistent_volumes[] = $persistentStorage->host_path . ':' . $persistentStorage->mount_path;
|
||||
$local_persistent_volumes[] = $persistentStorage->host_path.':'.$persistentStorage->mount_path;
|
||||
} else {
|
||||
$volume_name = $persistentStorage->name;
|
||||
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
||||
$local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,18 +154,18 @@ class StartMysql
|
||||
$environment_variables->push("$env->key=$env->real_value");
|
||||
}
|
||||
|
||||
if ($environment_variables->filter(fn($env) => str($env)->contains('MYSQL_ROOT_PASSWORD'))->isEmpty()) {
|
||||
if ($environment_variables->filter(fn ($env) => str($env)->contains('MYSQL_ROOT_PASSWORD'))->isEmpty()) {
|
||||
$environment_variables->push("MYSQL_ROOT_PASSWORD={$this->database->mysql_root_password}");
|
||||
}
|
||||
|
||||
if ($environment_variables->filter(fn($env) => str($env)->contains('MYSQL_DATABASE'))->isEmpty()) {
|
||||
if ($environment_variables->filter(fn ($env) => str($env)->contains('MYSQL_DATABASE'))->isEmpty()) {
|
||||
$environment_variables->push("MYSQL_DATABASE={$this->database->mysql_database}");
|
||||
}
|
||||
|
||||
if ($environment_variables->filter(fn($env) => str($env)->contains('MYSQL_USER'))->isEmpty()) {
|
||||
if ($environment_variables->filter(fn ($env) => str($env)->contains('MYSQL_USER'))->isEmpty()) {
|
||||
$environment_variables->push("MYSQL_USER={$this->database->mysql_user}");
|
||||
}
|
||||
if ($environment_variables->filter(fn($env) => str($env)->contains('MYSQL_PASSWORD'))->isEmpty()) {
|
||||
if ($environment_variables->filter(fn ($env) => str($env)->contains('MYSQL_PASSWORD'))->isEmpty()) {
|
||||
$environment_variables->push("MYSQL_PASSWORD={$this->database->mysql_password}");
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,6 @@ class StartPostgresql
|
||||
$this->generate_init_scripts();
|
||||
$this->add_custom_conf();
|
||||
|
||||
|
||||
$docker_compose = [
|
||||
'services' => [
|
||||
$container_name => [
|
||||
|
||||
@@ -24,7 +24,7 @@ class StartRedis
|
||||
$startCommand = "redis-server --requirepass {$this->database->redis_password} --appendonly yes";
|
||||
|
||||
$container_name = $this->database->uuid;
|
||||
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
|
||||
$this->configuration_dir = database_configuration_dir().'/'.$container_name;
|
||||
|
||||
$this->commands = [
|
||||
"echo 'Starting {$database->name}.'",
|
||||
@@ -78,7 +78,7 @@ class StartRedis
|
||||
],
|
||||
],
|
||||
];
|
||||
if (!is_null($this->database->limits_cpuset)) {
|
||||
if (! is_null($this->database->limits_cpuset)) {
|
||||
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
|
||||
}
|
||||
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
||||
@@ -98,10 +98,10 @@ class StartRedis
|
||||
if (count($volume_names) > 0) {
|
||||
$docker_compose['volumes'] = $volume_names;
|
||||
}
|
||||
if (!is_null($this->database->redis_conf) || !empty($this->database->redis_conf)) {
|
||||
if (! is_null($this->database->redis_conf) || ! empty($this->database->redis_conf)) {
|
||||
$docker_compose['services'][$container_name]['volumes'][] = [
|
||||
'type' => 'bind',
|
||||
'source' => $this->configuration_dir . '/redis.conf',
|
||||
'source' => $this->configuration_dir.'/redis.conf',
|
||||
'target' => '/usr/local/etc/redis/redis.conf',
|
||||
'read_only' => true,
|
||||
];
|
||||
@@ -130,10 +130,10 @@ class StartRedis
|
||||
$local_persistent_volumes = [];
|
||||
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
|
||||
$local_persistent_volumes[] = $persistentStorage->host_path . ':' . $persistentStorage->mount_path;
|
||||
$local_persistent_volumes[] = $persistentStorage->host_path.':'.$persistentStorage->mount_path;
|
||||
} else {
|
||||
$volume_name = $persistentStorage->name;
|
||||
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
||||
$local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,7 +164,7 @@ class StartRedis
|
||||
$environment_variables->push("$env->key=$env->real_value");
|
||||
}
|
||||
|
||||
if ($environment_variables->filter(fn($env) => str($env)->contains('REDIS_PASSWORD'))->isEmpty()) {
|
||||
if ($environment_variables->filter(fn ($env) => str($env)->contains('REDIS_PASSWORD'))->isEmpty()) {
|
||||
$environment_variables->push("REDIS_PASSWORD={$this->database->redis_password}");
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Actions\Database;
|
||||
|
||||
use App\Actions\Server\CleanupDocker;
|
||||
use App\Models\StandaloneClickhouse;
|
||||
use App\Models\StandaloneDragonfly;
|
||||
use App\Models\StandaloneKeydb;
|
||||
@@ -10,25 +11,65 @@ use App\Models\StandaloneMongodb;
|
||||
use App\Models\StandaloneMysql;
|
||||
use App\Models\StandalonePostgresql;
|
||||
use App\Models\StandaloneRedis;
|
||||
use Illuminate\Support\Facades\Process;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
class StopDatabase
|
||||
{
|
||||
use AsAction;
|
||||
|
||||
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $database)
|
||||
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $database, bool $isDeleteOperation = false, bool $dockerCleanup = true)
|
||||
{
|
||||
$server = $database->destination->server;
|
||||
if (! $server->isFunctional()) {
|
||||
return 'Server is not functional';
|
||||
}
|
||||
|
||||
instant_remote_process(command: ["docker stop --time=30 $database->uuid"], server: $server, throwError: false);
|
||||
instant_remote_process(command: ["docker rm $database->uuid"], server: $server, throwError: false);
|
||||
instant_remote_process(command: ["docker rm -f $database->uuid"], server: $server, throwError: false);
|
||||
$this->stopContainer($database, $database->uuid, 300);
|
||||
if (! $isDeleteOperation) {
|
||||
if ($dockerCleanup) {
|
||||
CleanupDocker::dispatch($server, true);
|
||||
}
|
||||
}
|
||||
|
||||
if ($database->is_public) {
|
||||
StopDatabaseProxy::run($database);
|
||||
}
|
||||
|
||||
return 'Database stopped successfully';
|
||||
}
|
||||
|
||||
private function stopContainer($database, string $containerName, int $timeout = 300): void
|
||||
{
|
||||
$server = $database->destination->server;
|
||||
|
||||
$process = Process::timeout($timeout)->start("docker stop --time=$timeout $containerName");
|
||||
|
||||
$startTime = time();
|
||||
while ($process->running()) {
|
||||
if (time() - $startTime >= $timeout) {
|
||||
$this->forceStopContainer($containerName, $server);
|
||||
break;
|
||||
}
|
||||
usleep(100000);
|
||||
}
|
||||
|
||||
$this->removeContainer($containerName, $server);
|
||||
}
|
||||
|
||||
private function forceStopContainer(string $containerName, $server): void
|
||||
{
|
||||
instant_remote_process(command: ["docker kill $containerName"], server: $server, throwError: false);
|
||||
}
|
||||
|
||||
private function removeContainer(string $containerName, $server): void
|
||||
{
|
||||
instant_remote_process(command: ["docker rm -f $containerName"], server: $server, throwError: false);
|
||||
}
|
||||
|
||||
private function deleteConnectedNetworks($uuid, $server)
|
||||
{
|
||||
instant_remote_process(["docker network disconnect {$uuid} coolify-proxy"], $server, false);
|
||||
instant_remote_process(["docker network rm {$uuid}"], $server, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -543,7 +543,7 @@ class GetContainersStatus
|
||||
}
|
||||
}
|
||||
}
|
||||
$exitedServices = $exitedServices->unique('id');
|
||||
$exitedServices = $exitedServices->unique('uuid');
|
||||
foreach ($exitedServices as $exitedService) {
|
||||
if (str($exitedService->status)->startsWith('exited')) {
|
||||
continue;
|
||||
@@ -651,8 +651,9 @@ class GetContainersStatus
|
||||
// $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
|
||||
}
|
||||
|
||||
// Check if proxy is running
|
||||
$this->server->proxyType();
|
||||
if (! $this->server->proxySet() || $this->server->proxy->force_stop) {
|
||||
return;
|
||||
}
|
||||
$foundProxyContainer = $this->containers->filter(function ($value, $key) {
|
||||
if ($this->server->isSwarm()) {
|
||||
return data_get($value, 'Spec.Name') === 'coolify-proxy_traefik';
|
||||
|
||||
@@ -26,7 +26,7 @@ class CheckProxy
|
||||
if (is_null($proxyType) || $proxyType === 'NONE' || $server->proxy->force_stop) {
|
||||
return false;
|
||||
}
|
||||
['uptime' => $uptime, 'error' => $error] = $server->validateConnection();
|
||||
['uptime' => $uptime, 'error' => $error] = $server->validateConnection(false);
|
||||
if (! $uptime) {
|
||||
throw new \Exception($error);
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ class StartProxy
|
||||
"echo 'Creating required Docker Compose file.'",
|
||||
"echo 'Starting coolify-proxy.'",
|
||||
'docker stack deploy -c docker-compose.yml coolify-proxy',
|
||||
"echo 'Proxy started successfully.'",
|
||||
"echo 'Successfully started coolify-proxy.'",
|
||||
]);
|
||||
} else {
|
||||
$caddfile = 'import /dynamic/*.caddy';
|
||||
@@ -46,11 +46,14 @@ class StartProxy
|
||||
"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',
|
||||
'if docker ps -a --format "{{.Names}}" | grep -q "^coolify-proxy$"; then',
|
||||
" echo 'Stopping and removing existing coolify-proxy.'",
|
||||
' docker rm -f coolify-proxy || true',
|
||||
" echo 'Successfully stopped and removed existing coolify-proxy.'",
|
||||
'fi',
|
||||
"echo 'Starting coolify-proxy.'",
|
||||
'docker compose up -d --remove-orphans',
|
||||
"echo 'Proxy started successfully.'",
|
||||
"echo 'Successfully started coolify-proxy.'",
|
||||
]);
|
||||
$commands = $commands->merge(connectProxyToNetworks($server));
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Actions\Server;
|
||||
|
||||
use App\Events\CloudflareTunnelConfigured;
|
||||
use App\Models\Server;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
@@ -40,12 +41,17 @@ class ConfigureCloudflared
|
||||
instant_remote_process($commands, $server);
|
||||
} catch (\Throwable $e) {
|
||||
ray($e);
|
||||
$server->settings->is_cloudflare_tunnel = false;
|
||||
$server->settings->save();
|
||||
throw $e;
|
||||
} finally {
|
||||
CloudflareTunnelConfigured::dispatch($server->team_id);
|
||||
|
||||
$commands = collect([
|
||||
'rm -fr /tmp/cloudflared',
|
||||
]);
|
||||
instant_remote_process($commands, $server);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Actions\Service;
|
||||
|
||||
use App\Actions\Server\CleanupDocker;
|
||||
use App\Models\Service;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
@@ -9,11 +10,11 @@ class DeleteService
|
||||
{
|
||||
use AsAction;
|
||||
|
||||
public function handle(Service $service)
|
||||
public function handle(Service $service, bool $deleteConfigurations, bool $deleteVolumes, bool $dockerCleanup, bool $deleteConnectedNetworks)
|
||||
{
|
||||
try {
|
||||
$server = data_get($service, 'server');
|
||||
if ($server->isFunctional()) {
|
||||
if ($deleteVolumes && $server->isFunctional()) {
|
||||
$storagesToDelete = collect([]);
|
||||
|
||||
$service->environment_variables()->delete();
|
||||
@@ -33,13 +34,29 @@ class DeleteService
|
||||
foreach ($storagesToDelete as $storage) {
|
||||
$commands[] = "docker volume rm -f $storage->name";
|
||||
}
|
||||
$commands[] = "docker rm -f $service->uuid";
|
||||
|
||||
instant_remote_process($commands, $server, false);
|
||||
// Execute volume deletion first, this must be done first otherwise volumes will not be deleted.
|
||||
if (! empty($commands)) {
|
||||
foreach ($commands as $command) {
|
||||
$result = instant_remote_process([$command], $server, false);
|
||||
if ($result !== 0) {
|
||||
ray("Failed to execute: $command");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($deleteConnectedNetworks) {
|
||||
$service->delete_connected_networks($service->uuid);
|
||||
}
|
||||
|
||||
instant_remote_process(["docker rm -f $service->uuid"], $server, throwError: false);
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception($e->getMessage());
|
||||
} finally {
|
||||
if ($deleteConfigurations) {
|
||||
$service->delete_configurations();
|
||||
}
|
||||
foreach ($service->applications()->get() as $application) {
|
||||
$application->forceDelete();
|
||||
}
|
||||
@@ -50,6 +67,11 @@ class DeleteService
|
||||
$task->delete();
|
||||
}
|
||||
$service->tags()->detach();
|
||||
$service->forceDelete();
|
||||
|
||||
if ($dockerCleanup) {
|
||||
CleanupDocker::dispatch($server, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Actions\Service;
|
||||
|
||||
use App\Actions\Server\CleanupDocker;
|
||||
use App\Models\Service;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
@@ -9,40 +10,27 @@ class StopService
|
||||
{
|
||||
use AsAction;
|
||||
|
||||
public function handle(Service $service)
|
||||
public function handle(Service $service, bool $isDeleteOperation = false, bool $dockerCleanup = true)
|
||||
{
|
||||
try {
|
||||
$server = $service->destination->server;
|
||||
if (! $server->isFunctional()) {
|
||||
return 'Server is not functional';
|
||||
}
|
||||
ray('Stopping service: '.$service->name);
|
||||
$applications = $service->applications()->get();
|
||||
foreach ($applications as $application) {
|
||||
if ($applications->count() < 6) {
|
||||
instant_remote_process(command: ["docker stop --time=10 {$application->name}-{$service->uuid}"], server: $server, throwError: false);
|
||||
}
|
||||
instant_remote_process(command: ["docker rm {$application->name}-{$service->uuid}"], server: $server, throwError: false);
|
||||
instant_remote_process(command: ["docker rm -f {$application->name}-{$service->uuid}"], server: $server, throwError: false);
|
||||
$application->update(['status' => 'exited']);
|
||||
}
|
||||
$dbs = $service->databases()->get();
|
||||
foreach ($dbs as $db) {
|
||||
if ($dbs->count() < 6) {
|
||||
|
||||
instant_remote_process(command: ["docker stop --time=10 {$db->name}-{$service->uuid}"], server: $server, throwError: false);
|
||||
$containersToStop = $service->getContainersToStop();
|
||||
$service->stopContainers($containersToStop, $server);
|
||||
|
||||
if (! $isDeleteOperation) {
|
||||
$service->delete_connected_networks($service->uuid);
|
||||
if ($dockerCleanup) {
|
||||
CleanupDocker::dispatch($server, true);
|
||||
}
|
||||
instant_remote_process(command: ["docker rm {$db->name}-{$service->uuid}"], server: $server, throwError: false);
|
||||
instant_remote_process(command: ["docker rm -f {$db->name}-{$service->uuid}"], server: $server, throwError: false);
|
||||
$db->update(['status' => 'exited']);
|
||||
}
|
||||
instant_remote_process(["docker network disconnect {$service->uuid} coolify-proxy"], $service->server);
|
||||
instant_remote_process(["docker network rm {$service->uuid}"], $service->server);
|
||||
} catch (\Exception $e) {
|
||||
ray($e->getMessage());
|
||||
|
||||
return $e->getMessage();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user