Monitoring & Service Management (#163)

Monitoring & Service Management
This commit is contained in:
Saeed Vaziry
2024-04-13 11:47:56 +02:00
committed by GitHub
parent 87ec0af697
commit 052e28d2e3
95 changed files with 2423 additions and 341 deletions

View File

@ -0,0 +1,42 @@
<?php
namespace App\SSH\Services;
use App\Models\Service;
abstract class AbstractService implements ServiceInterface
{
public function __construct(protected Service $service)
{
}
public function creationRules(array $input): array
{
return [];
}
public function creationData(array $input): array
{
return [];
}
public function deletionRules(): array
{
return [];
}
public function data(): array
{
return [];
}
public function install(): void
{
//
}
public function uninstall(): void
{
//
}
}

View File

@ -1,18 +0,0 @@
<?php
namespace App\SSH\Services\AddOnServices;
use App\SSH\Services\ServiceInterface;
abstract class AbstractAddOnService implements ServiceInterface
{
abstract public function creationRules(array $input): array;
abstract public function creationData(array $input): array;
abstract public function create(): void;
abstract public function delete(): void;
abstract public function data(): array;
}

View File

@ -2,40 +2,78 @@
namespace App\SSH\Services\Database;
use App\Enums\BackupStatus;
use App\Models\BackupFile;
use App\Models\Server;
use App\Models\Service;
use App\SSH\HasScripts;
use App\SSH\Services\ServiceInterface;
use App\SSH\Services\AbstractService;
use Closure;
abstract class AbstractDatabase implements Database, ServiceInterface
abstract class AbstractDatabase extends AbstractService implements Database
{
use HasScripts;
protected Service $service;
protected Server $server;
abstract protected function getScriptsDir(): string;
public function __construct(Service $service)
public function creationRules(array $input): array
{
$this->service = $service;
$this->server = $service->server;
return [
'type' => [
'required',
function (string $attribute, mixed $value, Closure $fail) {
$databaseExists = $this->service->server->database();
if ($databaseExists) {
$fail('You already have a database service on the server.');
}
},
],
];
}
public function install(): void
{
$version = $this->service->version;
$command = $this->getScript($this->service->name.'/install-'.$version.'.sh');
$this->server->ssh()->exec($command, 'install-'.$this->service->name.'-'.$version);
$status = $this->server->systemd()->status($this->service->unit);
$this->service->server->ssh()->exec($command, 'install-'.$this->service->name.'-'.$version);
$status = $this->service->server->systemd()->status($this->service->unit);
$this->service->validateInstall($status);
$this->service->server->os()->cleanup();
}
public function deletionRules(): array
{
return [
'service' => [
function (string $attribute, mixed $value, Closure $fail) {
$hasDatabase = $this->service->server->databases()->exists();
if ($hasDatabase) {
$fail('You have database(s) on the server.');
}
$hasDatabaseUser = $this->service->server->databaseUsers()->exists();
if ($hasDatabaseUser) {
$fail('You have database user(s) on the server.');
}
$hasRunningBackup = $this->service->server->backups()
->where('status', BackupStatus::RUNNING)
->exists();
if ($hasRunningBackup) {
$fail('You have database backup(s) on the server.');
}
},
],
];
}
public function uninstall(): void
{
$version = $this->service->version;
$command = $this->getScript($this->service->name.'/uninstall.sh');
$this->service->server->ssh()->exec($command, 'uninstall-'.$this->service->name.'-'.$version);
$this->service->server->os()->cleanup();
}
public function create(string $name): void
{
$this->server->ssh()->exec(
$this->service->server->ssh()->exec(
$this->getScript($this->getScriptsDir().'/create.sh', [
'name' => $name,
]),
@ -45,7 +83,7 @@ public function create(string $name): void
public function delete(string $name): void
{
$this->server->ssh()->exec(
$this->service->server->ssh()->exec(
$this->getScript($this->getScriptsDir().'/delete.sh', [
'name' => $name,
]),
@ -55,7 +93,7 @@ public function delete(string $name): void
public function createUser(string $username, string $password, string $host): void
{
$this->server->ssh()->exec(
$this->service->server->ssh()->exec(
$this->getScript($this->getScriptsDir().'/create-user.sh', [
'username' => $username,
'password' => $password,
@ -67,7 +105,7 @@ public function createUser(string $username, string $password, string $host): vo
public function deleteUser(string $username, string $host): void
{
$this->server->ssh()->exec(
$this->service->server->ssh()->exec(
$this->getScript($this->getScriptsDir().'/delete-user.sh', [
'username' => $username,
'host' => $host,
@ -78,7 +116,7 @@ public function deleteUser(string $username, string $host): void
public function link(string $username, string $host, array $databases): void
{
$ssh = $this->server->ssh();
$ssh = $this->service->server->ssh();
foreach ($databases as $database) {
$ssh->exec(
@ -94,7 +132,7 @@ public function link(string $username, string $host, array $databases): void
public function unlink(string $username, string $host): void
{
$this->server->ssh()->exec(
$this->service->server->ssh()->exec(
$this->getScript($this->getScriptsDir().'/unlink.sh', [
'username' => $username,
'host' => $host,
@ -106,7 +144,7 @@ public function unlink(string $username, string $host): void
public function runBackup(BackupFile $backupFile): void
{
// backup
$this->server->ssh()->exec(
$this->service->server->ssh()->exec(
$this->getScript($this->getScriptsDir().'/backup.sh', [
'file' => $backupFile->name,
'database' => $backupFile->backup->database->name,
@ -115,13 +153,13 @@ public function runBackup(BackupFile $backupFile): void
);
// upload to storage
$upload = $backupFile->backup->storage->provider()->ssh($this->server)->upload(
$upload = $backupFile->backup->storage->provider()->ssh($this->service->server)->upload(
$backupFile->path(),
$backupFile->storagePath(),
);
// cleanup
$this->server->ssh()->exec('rm '.$backupFile->name.'.zip');
$this->service->server->ssh()->exec('rm '.$backupFile->name.'.zip');
$backupFile->size = $upload['size'];
$backupFile->save();
@ -130,12 +168,12 @@ public function runBackup(BackupFile $backupFile): void
public function restoreBackup(BackupFile $backupFile, string $database): void
{
// download
$backupFile->backup->storage->provider()->ssh($this->server)->download(
$backupFile->backup->storage->provider()->ssh($this->service->server)->download(
$backupFile->storagePath(),
$backupFile->name.'.zip',
);
$this->server->ssh()->exec(
$this->service->server->ssh()->exec(
$this->getScript($this->getScriptsDir().'/restore.sh', [
'database' => $database,
'file' => $backupFile->name,

View File

@ -9,4 +9,6 @@ sudo DEBIAN_FRONTEND=noninteractive apt-get update
sudo DEBIAN_FRONTEND=noninteractive apt-get install mariadb-server mariadb-backup -y
sudo systemctl unmask mysql.service
sudo service mysql start

View File

@ -9,4 +9,6 @@ sudo DEBIAN_FRONTEND=noninteractive apt-get update
sudo DEBIAN_FRONTEND=noninteractive apt-get install mariadb-server mariadb-backup -y
sudo systemctl unmask mysql.service
sudo service mysql start

View File

@ -0,0 +1,9 @@
sudo service mysql stop
sudo DEBIAN_FRONTEND=noninteractive apt-get remove mariadb-server mariadb-backup -y
sudo rm -rf /etc/mysql
sudo rm -rf /var/lib/mysql
sudo rm -rf /var/log/mysql
sudo rm -rf /var/run/mysqld
sudo rm -rf /var/run/mysqld/mysqld.sock

View File

@ -1,5 +1,7 @@
sudo DEBIAN_FRONTEND=noninteractive apt-get install mysql-server -y
sudo systemctl unmask mysql.service
sudo service mysql enable
sudo service mysql start

View File

@ -6,6 +6,8 @@ sudo DEBIAN_FRONTEND=noninteractive apt-get update
sudo DEBIAN_FRONTEND=noninteractive apt-get install mysql-server -y
sudo systemctl unmask mysql.service
sudo service mysql enable
sudo service mysql start

View File

@ -0,0 +1,9 @@
sudo service mysql stop
sudo DEBIAN_FRONTEND=noninteractive apt-get remove mysql-server -y
sudo rm -rf /etc/mysql
sudo rm -rf /var/lib/mysql
sudo rm -rf /var/log/mysql
sudo rm -rf /var/run/mysqld
sudo rm -rf /var/run/mysqld/mysqld.sock

View File

@ -0,0 +1,11 @@
sudo service postgresql stop
sudo DEBIAN_FRONTEND=noninteractive apt-get remove postgresql-* -y
sudo rm -rf /etc/postgresql
sudo rm -rf /var/lib/postgresql
sudo rm -rf /var/log/postgresql
sudo rm -rf /var/run/postgresql
sudo rm -rf /var/run/postgresql/postmaster.pid
sudo rm -rf /var/run/postgresql/.s.PGSQL.5432
sudo rm -rf /var/run/postgresql/.s.PGSQL.5432.lock

View File

@ -2,15 +2,8 @@
namespace App\SSH\Services\Firewall;
use App\Models\Service;
use App\SSH\Services\ServiceInterface;
use App\SSH\Services\AbstractService;
abstract class AbstractFirewall implements Firewall, ServiceInterface
abstract class AbstractFirewall extends AbstractService implements Firewall
{
protected Service $service;
public function __construct(Service $service)
{
$this->service = $service;
}
}

View File

@ -14,6 +14,12 @@ public function install(): void
$this->getScript('ufw/install-ufw.sh'),
'install-ufw'
);
$this->service->server->os()->cleanup();
}
public function uninstall(): void
{
//
}
public function addRule(string $type, string $protocol, int $port, string $source, ?string $mask): void

View File

@ -3,20 +3,43 @@
namespace App\SSH\Services\PHP;
use App\Exceptions\SSHCommandError;
use App\Models\Service;
use App\SSH\HasScripts;
use App\SSH\Services\ServiceInterface;
use App\SSH\Services\AbstractService;
use Closure;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
class PHP implements ServiceInterface
class PHP extends AbstractService
{
use HasScripts;
protected Service $service;
public function __construct(Service $service)
public function creationRules(array $input): array
{
$this->service = $service;
return [
'version' => [
'required',
Rule::in(config('core.php_versions')),
Rule::unique('services', 'version')
->where('type', 'php')
->where('server_id', $this->service->server_id),
],
];
}
public function deletionRules(): array
{
return [
'service' => [
function (string $attribute, mixed $value, Closure $fail) {
$hasSite = $this->service->server->sites()
->where('php_version', $this->service->version)
->exists();
if ($hasSite) {
$fail('Some sites are using this PHP version.');
}
},
],
];
}
public function install(): void
@ -29,6 +52,7 @@ public function install(): void
]),
'install-php-'.$this->service->version
);
$this->service->server->os()->cleanup();
}
public function uninstall(): void
@ -39,6 +63,7 @@ public function uninstall(): void
]),
'uninstall-php-'.$this->service->version
);
$this->service->server->os()->cleanup();
}
public function setDefaultCli(): void

View File

@ -2,15 +2,37 @@
namespace App\SSH\Services\ProcessManager;
use App\Models\Service;
use App\SSH\Services\ServiceInterface;
use App\SSH\Services\AbstractService;
use Closure;
abstract class AbstractProcessManager implements ProcessManager, ServiceInterface
abstract class AbstractProcessManager extends AbstractService implements ProcessManager
{
protected Service $service;
public function __construct(Service $service)
public function creationRules(array $input): array
{
$this->service = $service;
return [
'type' => [
'required',
function (string $attribute, mixed $value, Closure $fail) {
$processManagerExists = $this->service->server->processManager();
if ($processManagerExists) {
$fail('You already have a process manager service on the server.');
}
},
],
];
}
public function deletionRules(): array
{
return [
'service' => [
function (string $attribute, mixed $value, Closure $fail) {
$hasQueue = $this->service->server->queues()->exists();
if ($hasQueue) {
$fail('You have queue(s) on the server.');
}
},
],
];
}
}

View File

@ -15,6 +15,18 @@ public function install(): void
$this->getScript('supervisor/install-supervisor.sh'),
'install-supervisor'
);
$this->service->server->os()->cleanup();
}
public function uninstall(): void
{
$this->service->server->ssh()->exec(
$this->getScript('supervisor/uninstall-supervisor.sh'),
'uninstall-supervisor'
);
$status = $this->service->server->systemd()->status($this->service->unit);
$this->service->validateInstall($status);
$this->service->server->os()->cleanup();
}
/**

View File

@ -0,0 +1,8 @@
sudo service supervisor stop
sudo DEBIAN_FRONTEND=noninteractive apt-get remove supervisor -y
sudo rm -rf /etc/supervisor
sudo rm -rf /var/log/supervisor
sudo rm -rf /var/run/supervisor
sudo rm -rf /var/run/supervisor/supervisor.sock

View File

@ -2,16 +2,27 @@
namespace App\SSH\Services\Redis;
use App\Models\Service;
use App\SSH\HasScripts;
use App\SSH\Services\ServiceInterface;
use App\SSH\Services\AbstractService;
use Closure;
class Redis implements ServiceInterface
class Redis extends AbstractService
{
use HasScripts;
public function __construct(protected Service $service)
public function creationRules(array $input): array
{
return [
'type' => [
'required',
function (string $attribute, mixed $value, Closure $fail) {
$redisExists = $this->service->server->memoryDatabase();
if ($redisExists) {
$fail('You already have a Redis service on the server.');
}
},
],
];
}
public function install(): void
@ -20,5 +31,17 @@ public function install(): void
$this->getScript('install.sh'),
'install-redis'
);
$status = $this->service->server->systemd()->status($this->service->unit);
$this->service->validateInstall($status);
$this->service->server->os()->cleanup();
}
public function uninstall(): void
{
$this->service->server->ssh()->exec(
$this->getScript('uninstall.sh'),
'uninstall-redis'
);
$this->service->server->os()->cleanup();
}
}

View File

@ -0,0 +1,15 @@
sudo service redis stop
sudo DEBIAN_FRONTEND=noninteractive apt-get remove redis-server -y
sudo rm -rf /etc/redis
sudo rm -rf /var/lib/redis
sudo rm -rf /var/log/redis
sudo rm -rf /var/run/redis
sudo rm -rf /var/run/redis/redis-server.pid
sudo rm -rf /var/run/redis/redis-server.sock
sudo rm -rf /var/run/redis/redis-server.sock
sudo DEBIAN_FRONTEND=noninteractive sudo apt-get autoremove -y
sudo DEBIAN_FRONTEND=noninteractive sudo apt-get autoclean -y

View File

@ -4,5 +4,15 @@
interface ServiceInterface
{
public function creationRules(array $input): array;
public function creationData(array $input): array;
public function deletionRules(): array;
public function data(): array;
public function install(): void;
public function uninstall(): void;
}

View File

@ -0,0 +1,85 @@
<?php
namespace App\SSH\Services\VitoAgent;
use App\Models\Metric;
use App\SSH\HasScripts;
use App\SSH\Services\AbstractService;
use Illuminate\Support\Facades\Http;
use Illuminate\Validation\Rule;
use Ramsey\Uuid\Uuid;
class VitoAgent extends AbstractService
{
use HasScripts;
const TAGS_URL = 'https://api.github.com/repos/vitodeploy/agent/tags';
const DOWNLOAD_URL = 'https://github.com/vitodeploy/agent/releases/download/%s';
public function creationRules(array $input): array
{
return [
'type' => [
Rule::unique('services', 'type')->where('server_id', $this->service->server_id),
],
'version' => [
'required',
Rule::in(['latest']),
],
];
}
public function creationData(array $input): array
{
return [
'url' => '',
'secret' => Uuid::uuid4()->toString(),
];
}
public function data(): array
{
return [
'url' => $this->service->type_data['url'] ?? null,
'secret' => $this->service->type_data['secret'] ?? null,
];
}
public function install(): void
{
$tags = Http::get(self::TAGS_URL)->json();
if (empty($tags)) {
throw new \Exception('Failed to fetch tags');
}
$this->service->version = $tags[0]['name'];
$this->service->save();
$downloadUrl = sprintf(self::DOWNLOAD_URL, $this->service->version);
$data = $this->data();
$data['url'] = route('api.servers.agent', [$this->service->server, $this->service->id]);
$this->service->type_data = $data;
$this->service->save();
$this->service->refresh();
$this->service->server->ssh()->exec(
$this->getScript('install.sh', [
'download_url' => $downloadUrl,
'config_url' => $this->data()['url'],
'config_secret' => $this->data()['secret'],
]),
'install-vito-agent'
);
$status = $this->service->server->systemd()->status($this->service->unit);
$this->service->validateInstall($status);
}
public function uninstall(): void
{
$this->service->server->ssh()->exec(
$this->getScript('uninstall.sh'),
'uninstall-vito-agent'
);
Metric::where('server_id', $this->service->server_id)->delete();
}
}

View File

@ -0,0 +1,53 @@
arch=$(uname -m)
if [ "$arch" == "x86_64" ]; then
executable="vitoagent-linux-amd64"
elif [ "$arch" == "i686" ]; then
executable="vitoagent-linux-amd"
elif [ "$arch" == "armv7l" ]; then
executable="vitoagent-linux-arm"
elif [ "$arch" == "aarch64" ]; then
executable="vitoagent-linux-arm64"
else
executable="vitoagent-linux-amd64"
fi
wget __download_url__/$executable
chmod +x ./$executable
sudo mv ./$executable /usr/local/bin/vito-agent
# create service
export VITO_AGENT_SERVICE="
[Unit]
Description=Vito Agent
After=network.target
[Service]
Type=simple
User=root
ExecStart=/usr/local/bin/vito-agent
Restart=on-failure
[Install]
WantedBy=multi-user.target
"
echo "${VITO_AGENT_SERVICE}" | sudo tee /etc/systemd/system/vito-agent.service
sudo mkdir -p /etc/vito-agent
export VITO_AGENT_CONFIG="
{
\"url\": \"__config_url__\",
\"secret\": \"__config_secret__\"
}
"
echo "${VITO_AGENT_CONFIG}" | sudo tee /etc/vito-agent/config.json
sudo systemctl daemon-reload
sudo systemctl enable vito-agent
sudo systemctl start vito-agent
echo "Vito Agent installed successfully"

View File

@ -0,0 +1,13 @@
sudo service vito-agent stop
sudo systemctl disable vito-agent
sudo rm -f /usr/local/bin/vito-agent
sudo rm -f /etc/systemd/system/vito-agent.service
sudo rm -rf /etc/vito-agent
sudo systemctl daemon-reload
echo "Vito Agent uninstalled successfully"

View File

@ -2,12 +2,8 @@
namespace App\SSH\Services\Webserver;
use App\Models\Service;
use App\SSH\Services\ServiceInterface;
use App\SSH\Services\AbstractService;
abstract class AbstractWebserver implements ServiceInterface, Webserver
abstract class AbstractWebserver extends AbstractService implements Webserver
{
public function __construct(protected Service $service)
{
}
}

View File

@ -6,6 +6,7 @@
use App\Models\Site;
use App\Models\Ssl;
use App\SSH\HasScripts;
use Closure;
use Illuminate\Support\Str;
use Throwable;
@ -23,6 +24,31 @@ public function install(): void
]),
'install-nginx'
);
$this->service->server->os()->cleanup();
}
public function deletionRules(): array
{
return [
'service' => [
function (string $attribute, mixed $value, Closure $fail) {
$hasSite = $this->service->server->sites()
->exists();
if ($hasSite) {
$fail('Cannot uninstall webserver while you have websites using it.');
}
},
],
];
}
public function uninstall(): void
{
$this->service->server->ssh()->exec(
$this->getScript('nginx/uninstall-nginx.sh'),
'uninstall-nginx'
);
$this->service->server->os()->cleanup();
}
public function createVHost(Site $site): void

View File

@ -0,0 +1,12 @@
sudo service nginx stop
sudo DEBIAN_FRONTEND=noninteractive apt-get purge nginx nginx-common nginx-full -y
sudo rm -rf /etc/nginx
sudo rm -rf /var/log/nginx
sudo rm -rf /var/lib/nginx
sudo rm -rf /var/cache/nginx
sudo rm -rf /usr/share/nginx
sudo rm -rf /etc/systemd/system/nginx.service
sudo systemctl daemon-reload