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") }