buildCloudInit( $order, $bootstrap, (string) ($provisioning['user_data'] ?? '') ); return $provisioning; } public function buildCloudInit(CustomerHostingOrder $order, array $bootstrap, string $existingCloudInit = ''): string { $parsed = $this->parseGeneratedCloudInit($existingCloudInit); $release = $this->releaseService->ensureRelease(); $releaseUrl = $this->releaseService->temporaryDownloadUrl($release['version']); $envFile = implode("\n", [ 'ORDER_ID='.$order->id, 'BOOTSTRAP_TOKEN='.$bootstrap['bootstrap_token'], 'REGISTRATION_URL='.$bootstrap['registration_url'], 'HEARTBEAT_URL='.$bootstrap['heartbeat_url'], 'AGENT_RELEASE_VERSION='.$release['version'], '', ]); $packages = array_values(array_unique(array_merge($parsed['packages'], [ 'curl', 'wget', 'jq', 'ca-certificates', 'git', 'unzip', 'zip', ]))); $runCommands = array_merge([ $this->managedStackInstallCommand($releaseUrl, $release['checksum_sha256']), 'bash -lc \'mkdir -p /etc/ladill /var/lib/ladill-server-agent /var/www /tmp/ladill-server-agent-release\'', 'bash -lc \'cd /tmp/ladill-server-agent-release && bash install.sh\'', 'bash -lc \'chmod 0600 /etc/ladill/server-agent.env\'', $this->managedStackEnableServicesCommand(), ], $parsed['run_commands']); return $this->contaboProvider->generateCloudInit([ 'packages' => $packages, 'write_files' => [ [ 'path' => '/etc/ladill/server-agent.env', 'permissions' => '0600', 'owner' => 'root:root', 'encoding' => 'b64', 'content' => base64_encode($envFile), ], ], 'run_commands' => $runCommands, ]); } /** * @return array{packages: array, run_commands: array} */ private function parseGeneratedCloudInit(string $cloudInit): array { $packages = []; $runCommands = []; $section = null; foreach (preg_split("/\r\n|\n|\r/", $cloudInit) ?: [] as $line) { $trimmed = trim($line); if ($trimmed === '' || $trimmed === '#cloud-config') { continue; } if (! str_starts_with($line, ' ')) { $section = match ($trimmed) { 'packages:' => 'packages', 'runcmd:' => 'runcmd', default => null, }; continue; } if ($section === 'packages' && str_starts_with($trimmed, '- ')) { $packages[] = trim(substr($trimmed, 2)); } if ($section === 'runcmd' && str_starts_with($trimmed, '- ')) { $runCommands[] = trim(substr($trimmed, 2)); } } return [ 'packages' => array_values(array_filter($packages)), 'run_commands' => array_values(array_filter($runCommands)), ]; } private function managedStackInstallCommand(string $releaseUrl, string $checksum): string { $script = str_replace( ['__RELEASE_URL__', '__CHECKSUM__'], [$releaseUrl, $checksum], <<<'BASH' set -e detect_pm() { if command -v apt-get >/dev/null 2>&1; then echo apt-get return 0 fi if command -v dnf >/dev/null 2>&1; then echo dnf return 0 fi if command -v yum >/dev/null 2>&1; then echo yum return 0 fi if command -v zypper >/dev/null 2>&1; then echo zypper return 0 fi return 1 } install_composer_if_missing() { if command -v composer >/dev/null 2>&1; then return 0 fi curl -fsSL https://getcomposer.org/installer -o /tmp/composer-setup.php php /tmp/composer-setup.php --install-dir=/usr/local/bin --filename=composer rm -f /tmp/composer-setup.php } pm="$(detect_pm || true)" if [ -z "${pm}" ]; then echo "Ladill managed stack currently supports apt, dnf, yum, or zypper based Linux distributions only." >&2 exit 1 fi case "${pm}" in apt-get) export DEBIAN_FRONTEND=noninteractive apt-get update apt-get install -y nginx mariadb-server php-fpm php-cli php-mysql php-curl php-xml php-zip php-mbstring php-gd php-intl php-bcmath certbot jq curl wget ca-certificates cron composer git unzip zip bubblewrap ;; dnf) dnf install -y epel-release || true dnf install -y nginx mariadb-server php-fpm php-cli php-mysqlnd certbot jq curl wget ca-certificates cronie git unzip zip bubblewrap dnf install -y php-curl php-xml php-zip php-mbstring php-gd php-intl php-bcmath || true ;; yum) yum install -y epel-release || true yum install -y nginx mariadb-server php-fpm php-cli php-mysqlnd certbot jq curl wget ca-certificates cronie git unzip zip bubblewrap yum install -y php-curl php-xml php-zip php-mbstring php-gd php-intl php-bcmath || true ;; zypper) zypper --non-interactive refresh zypper --non-interactive install -y nginx mariadb php8 php8-fpm php8-mysql certbot jq curl wget ca-certificates cron git unzip zip bubblewrap zypper --non-interactive install -y php8-curl php8-xmlreader php8-xmlwriter php8-zip php8-mbstring php8-gd php8-intl php8-bcmath || true ;; esac install_composer_if_missing mkdir -p /etc/ladill /var/lib/ladill-server-agent /var/www /tmp/ladill-server-agent-release curl -fsSL "__RELEASE_URL__" -o /tmp/ladill-server-agent-release/agent.tar.gz echo "__CHECKSUM__ /tmp/ladill-server-agent-release/agent.tar.gz" | sha256sum -c - tar -xzf /tmp/ladill-server-agent-release/agent.tar.gz -C /tmp/ladill-server-agent-release BASH ); return 'bash -lc '.escapeshellarg($script); } private function managedStackEnableServicesCommand(): string { return 'bash -lc '.escapeshellarg(<<<'BASH' set -e service_exists() { systemctl list-unit-files "$1.service" --no-legend 2>/dev/null | grep -q "^$1\.service" } enable_if_present() { for service in "$@"; do if [ -n "$service" ] && service_exists "$service"; then systemctl enable --now "$service" >/dev/null 2>&1 || true return 0 fi done return 1 } chmod 0600 /etc/ladill/server-agent.env systemctl daemon-reload enable_if_present nginx enable_if_present mariadb mysql enable_if_present cron crond enable_if_present php-fpm php8-fpm php8.4-fpm php8.3-fpm php8.2-fpm php8.1-fpm php8.0-fpm enable_if_present ladill-server-agent BASH ); } }