63 lines
3 KiB
Go
63 lines
3 KiB
Go
package builtin
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
|
||
"github.com/vladimir/goherence/internal/module"
|
||
"github.com/vladimir/goherence/internal/ssh"
|
||
)
|
||
|
||
// PackageModule — аналог puppet-style типизированного ресурса package:
|
||
// один модуль на все дистрибутивы, сам выбирающий провайдера (apt/dnf/yum),
|
||
// вместо того чтобы заставлять плейбук писать `when: os_family == ...`
|
||
// для каждой пары модуль/ОС. Выбор провайдера:
|
||
// 1. если в hostVars есть os_family (факты собраны) — используем его напрямую,
|
||
// без единого лишнего запроса на хост;
|
||
// 2. если фактов нет (gather_facts: false) — определяем прямо на месте
|
||
// через наличие apt-get/dnf/yum на хосте (одна дополнительная команда,
|
||
// но только в этом случае — не платим за это, когда факты уже есть).
|
||
type PackageModule struct {
|
||
// OSFamily — предвычисленный на этапе резолва переменных os_family
|
||
// (пусто, если факты не собирались). Заполняется executor'ом так же,
|
||
// как TemplateModule.Vars — см. builtin.RegisterAll.
|
||
OSFamily string
|
||
}
|
||
|
||
func (m *PackageModule) Name() string { return "package" }
|
||
|
||
func (m *PackageModule) Run(ctx context.Context, in module.Input, conn *ssh.Conn) (module.Result, error) {
|
||
family := m.OSFamily
|
||
if family == "" {
|
||
detected, err := detectOSFamily(conn)
|
||
if err != nil {
|
||
return module.Result{}, fmt.Errorf("package: не удалось определить дистрибутив " +
|
||
"(факты не собраны и автоопределение не удалось): " + err.Error())
|
||
}
|
||
family = detected
|
||
}
|
||
|
||
switch family {
|
||
case "Debian":
|
||
return (&AptModule{}).Run(ctx, in, conn)
|
||
case "RedHat":
|
||
return (&PackageRPMModule{}).Run(ctx, in, conn)
|
||
default:
|
||
return module.Result{}, fmt.Errorf(
|
||
"package: неизвестное семейство ОС %q — используй apt или rpm_package явно", family)
|
||
}
|
||
}
|
||
|
||
// detectOSFamily — резервный путь для случая gather_facts: false: одна
|
||
// команда на хосте вместо полноценного facts.Gather (который делает
|
||
// дополнительный `cat /etc/os-release`) — здесь достаточно проверить
|
||
// наличие пакетного менеджера, сам os-release не нужен.
|
||
func detectOSFamily(conn *ssh.Conn) (string, error) {
|
||
if res, err := conn.Run("command -v apt-get"); err == nil && res.ExitCode == 0 {
|
||
return "Debian", nil
|
||
}
|
||
if res, err := conn.Run("command -v dnf || command -v yum"); err == nil && res.ExitCode == 0 {
|
||
return "RedHat", nil
|
||
}
|
||
return "", fmt.Errorf("не найден ни apt-get, ни dnf/yum")
|
||
}
|