82 lines
2.8 KiB
Go
82 lines
2.8 KiB
Go
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 на хосте")
|
||
}
|