Files
FemaInstaller/internal/deployer/deployer.go

208 lines
7.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package deployer
import (
"fmt"
"path/filepath"
"strings"
"time"
"gitea.unprism.ru/KRBL/FemaDeployer/internal/ssh"
"gitea.unprism.ru/KRBL/FemaDeployer/pkg/config"
"gitea.unprism.ru/KRBL/FemaDeployer/pkg/fileutils"
gossh "golang.org/x/crypto/ssh"
)
// ... структура ServiceStatus и LogFunc без изменений ...
type ServiceStatus struct {
Name string
Version string
Status string
Raw string
}
type LogFunc func(message string)
// ... структура Deployer и функция NewDeployer без изменений ...
type Deployer struct {
ConnConfig *config.ConnectionConfig
Log LogFunc
}
func NewDeployer(cfg *config.ConnectionConfig, logFunc LogFunc) *Deployer {
return &Deployer{
ConnConfig: cfg,
Log: logFunc,
}
}
// ... функция GetServicesStatus без изменений ...
func (d *Deployer) GetServicesStatus(components []config.Component) ([]ServiceStatus, error) {
d.Log("Подключение для проверки статуса...")
client, err := ssh.CreateSSHClient(ssh.NewClientConfig(d.ConnConfig.IP, d.ConnConfig.Port, d.ConnConfig.Login, d.ConnConfig.Password))
if err != nil {
return nil, fmt.Errorf("ошибка SSH: %w", err)
}
defer client.Close()
var statuses []ServiceStatus
for _, comp := range components {
d.Log(fmt.Sprintf("Проверка статуса для %s...", comp.Name))
status := ServiceStatus{Name: comp.Name, Version: comp.Version}
cmd := fmt.Sprintf("systemctl status %s.service", comp.Name)
output, err := ssh.ExecuteCommandWithOutput(client, cmd)
status.Raw = output
if err != nil {
if strings.Contains(status.Raw, "Loaded: not-found") {
status.Status = "Не найден"
} else if strings.Contains(status.Raw, "inactive (dead)") {
status.Status = "Не активен"
} else {
status.Status = "Ошибка"
}
} else {
status.Status = "Активен"
}
statuses = append(statuses, status)
}
d.Log("Проверка статусов завершена.")
return statuses, nil
}
func (d *Deployer) FullDeploy() error {
client, err := d.connect()
if err != nil {
return err
}
defer client.Close()
if err := d.prepareRemotePackage(client); err != nil {
return err
}
d.Log("Запуск скрипта полной установки...")
installScriptPath := "/tmp/fema_deployment/deployment_package/scripts/install.sh"
// ИСПОЛЬЗУЕМ ExecuteCommandWithOutput для отладки
output, err := ssh.ExecuteCommandWithOutput(client, "sudo "+installScriptPath)
if err != nil {
d.Log("!!! ОШИБКА ВЫПОЛНЕНИЯ СКРИПТА !!!")
d.Log("ВЫВОД СКРИПТА:\n" + output)
return fmt.Errorf("ошибка выполнения install.sh: %w", err)
}
d.Log("ВЫВОД СКРИПТА:\n" + output)
d.Log("Полная установка успешно завершена!")
return nil
}
func (d *Deployer) SelectiveUpdate(components []string) error {
if len(components) == 0 {
return fmt.Errorf("не выбраны компоненты для обновления")
}
client, err := d.connect()
if err != nil {
return err
}
defer client.Close()
if err := d.prepareRemotePackage(client); err != nil {
return err
}
d.Log(fmt.Sprintf("Запуск скрипта выборочного обновления для: %s", strings.Join(components, ", ")))
updateScriptPath := "/tmp/fema_deployment/deployment_package/scripts/update.sh"
cmd := fmt.Sprintf("sudo %s %s", updateScriptPath, strings.Join(components, " "))
// ИСПОЛЬЗУЕМ ExecuteCommandWithOutput для отладки
output, err := ssh.ExecuteCommandWithOutput(client, cmd)
if err != nil {
d.Log("!!! ОШИБКА ВЫПОЛНЕНИЯ СКРИПТА !!!")
d.Log("ВЫВОД СКРИПТА:\n" + output)
return fmt.Errorf("ошибка выполнения update.sh: %w", err)
}
d.Log("ВЫВОД СКРИПТА:\n" + output)
d.Log("Выборочное обновление успешно завершено!")
return nil
}
// --- НОВАЯ ВСПОМОГАТЕЛЬНАЯ ФУНКЦИЯ ---
// prepareRemotePackage загружает, распаковывает и подготавливает пакет на удаленной машине
func (d *Deployer) prepareRemotePackage(client *gossh.Client) error {
remotePath := "/tmp/" + filepath.Base(d.ConnConfig.DeploymentPackagePath)
if err := d.uploadPackage(remotePath); err != nil {
return err
}
d.Log("Распаковка архива на устройстве...")
unpackDir := "/tmp/fema_deployment"
unpackCmds := []string{
fmt.Sprintf("rm -rf %s", unpackDir),
fmt.Sprintf("mkdir -p %s", unpackDir),
fmt.Sprintf("tar -xzf %s -C %s", remotePath, unpackDir),
}
for _, cmd := range unpackCmds {
if err := ssh.ExecuteCommand(client, cmd); err != nil {
return err
}
}
// ГАРАНТИРУЕМ, ЧТО СКРИПТЫ МОЖНО ЗАПУСКАТЬ
d.Log("Установка прав на исполнение для скриптов...")
scriptsPath := filepath.Join(unpackDir, "deployment_package", "scripts")
installScriptPath := filepath.Join(scriptsPath, "install.sh")
updateScriptPath := filepath.Join(scriptsPath, "update.sh")
chmodCmd := fmt.Sprintf("chmod +x %s %s", installScriptPath, updateScriptPath)
if err := ssh.ExecuteCommand(client, chmodCmd); err != nil {
return fmt.Errorf("не удалось установить права на скрипты: %w", err)
}
if err := ssh.ExecuteCommand(client, "cd "+scriptsPath); err != nil {
return fmt.Errorf("не удалось перейти в директорию с скриптами: %w", err)
}
return nil
}
// ... connect и uploadPackage без изменений ...
func (d *Deployer) connect() (*gossh.Client, error) {
d.Log("Подключение к устройству...")
clientConfig := ssh.NewClientConfig(d.ConnConfig.IP, d.ConnConfig.Port, d.ConnConfig.Login, d.ConnConfig.Password)
client, err := ssh.CreateSSHClient(clientConfig)
if err != nil {
d.Log(fmt.Sprintf("Ошибка подключения: %v", err))
return nil, fmt.Errorf("ошибка подключения SSH: %w", err)
}
d.Log("Подключение успешно.")
return client, nil
}
func (d *Deployer) uploadPackage(remotePath string) error {
d.Log("Загрузка пакета развертывания...")
sftpConfig := ssh.NewClientConfig(d.ConnConfig.IP, d.ConnConfig.Port, d.ConnConfig.Login, d.ConnConfig.Password)
sftpClient, err := ssh.CreateSFTPClient(sftpConfig)
if err != nil {
d.Log(fmt.Sprintf("Ошибка SFTP: %v", err))
return fmt.Errorf("ошибка подключения SFTP: %w", err)
}
defer sftpClient.Close()
progressCallback := func(percentage float64, etr time.Duration) {
d.Log(fmt.Sprintf("Загрузка: %.2f%% (осталось ~%v)", percentage, etr.Round(time.Second)))
}
if err := fileutils.UploadFileWithProgress(sftpClient, d.ConnConfig.DeploymentPackagePath, remotePath, progressCallback); err != nil {
d.Log(fmt.Sprintf("Ошибка загрузки файла: %v", err))
return fmt.Errorf("ошибка загрузки файла: %w", err)
}
d.Log("Пакет успешно загружен.")
return nil
}