208 lines
7.1 KiB
Go
208 lines
7.1 KiB
Go
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
|
||
}
|