goherence/internal/module/builtin/rpm.go
2026-09-11 10:17:25 +03:00

82 lines
2.8 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 builtin
import (
"context"
"fmt"
"github.com/vladimir/goherence/internal/module"
"github.com/vladimir/goherence/internal/ssh"
)
// PackageRPMModule — аналог ansible.builtin.yum/dnf для RPM-based систем
// (RHEL/CentOS/ALT Linux с apt-rpm тоже подходит частично, но здесь
// используется универсальный `rpm -q` для проверки + `dnf`/`yum` для установки).
type PackageRPMModule struct{}
func (m *PackageRPMModule) Name() string { return "rpm_package" }
func (m *PackageRPMModule) Run(ctx context.Context, in module.Input, conn *ssh.Conn) (module.Result, error) {
name, ok := in.Args["name"].(string)
if !ok {
return module.Result{}, fmt.Errorf("rpm_package: аргумент name обязателен")
}
state, _ := in.Args["state"].(string)
if state == "" {
state = "present"
}
installed, err := m.isInstalled(conn, name)
if err != nil {
return module.Result{}, err
}
wantInstalled := state == "present" || state == "latest"
if installed == wantInstalled {
return module.Result{SchemaVersion: module.SchemaVersion, Changed: false, Msg: name + ": уже в нужном состоянии"}, nil
}
if in.CheckMode {
return module.Result{SchemaVersion: module.SchemaVersion, Changed: true,
Msg: fmt.Sprintf("check mode: %s был бы %s", name, state)}, nil
}
pm, err := m.packageManager(conn)
if err != nil {
return module.Result{}, err
}
var cmd string
if wantInstalled {
cmd = fmt.Sprintf("%s install -y %s", pm, shArg(name))
} else {
cmd = fmt.Sprintf("%s remove -y %s", pm, shArg(name))
}
res, err := conn.Run(cmd)
if err != nil {
return module.Result{}, err
}
if res.ExitCode != 0 {
return module.Result{SchemaVersion: module.SchemaVersion, Failed: true,
Msg: fmt.Sprintf("%s завершился с кодом %d: %s", pm, res.ExitCode, res.Stderr)}, nil
}
return module.Result{SchemaVersion: module.SchemaVersion, Changed: true, Msg: name + ": " + state}, nil
}
func (m *PackageRPMModule) isInstalled(conn *ssh.Conn, name string) (bool, error) {
res, err := conn.Run(fmt.Sprintf("rpm -q %s > /dev/null 2>&1", shArg(name)))
if err != nil {
return false, err
}
return res.ExitCode == 0, nil
}
// packageManager определяет dnf/yum/apt-rpm по наличию бинарника —
// нужно, потому что RPM-based дистрибутивы разошлись по пакетным менеджерам.
func (m *PackageRPMModule) packageManager(conn *ssh.Conn) (string, error) {
for _, candidate := range []string{"dnf", "yum", "apt-get"} {
res, err := conn.Run("command -v " + candidate)
if err == nil && res.ExitCode == 0 {
return candidate, nil
}
}
return "", fmt.Errorf("не найден ни dnf, ни yum, ни apt-get на хосте")
}