package builtin import ( "context" "fmt" "strings" "github.com/vladimir/goherence/internal/module" "github.com/vladimir/goherence/internal/ssh" ) // AptModule — аналог ansible.builtin.apt: приводит наличие пакета // к желаемому state (present/absent). Идемпотентен через `dpkg -s` // перед любым изменяющим действием. type AptModule struct{} func (m *AptModule) Name() string { return "apt" } func (m *AptModule) 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("apt: аргумент 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 } var cmd string if wantInstalled { cmd = fmt.Sprintf("DEBIAN_FRONTEND=noninteractive apt-get install -y %s", shArg(name)) } else { cmd = fmt.Sprintf("DEBIAN_FRONTEND=noninteractive apt-get remove -y %s", 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("apt-get завершился с кодом %d: %s", res.ExitCode, res.Stderr)}, nil } return module.Result{SchemaVersion: module.SchemaVersion, Changed: true, Msg: name + ": " + state}, nil } func (m *AptModule) isInstalled(conn *ssh.Conn, name string) (bool, error) { res, err := conn.Run(fmt.Sprintf("dpkg -s %s 2>/dev/null | grep -q '^Status: install ok installed'", shArg(name))) if err != nil { return false, err } return res.ExitCode == 0, nil } func shArg(s string) string { return "'" + strings.ReplaceAll(s, "'", `'\''`) + "'" }