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

73 lines
2.2 KiB
Go
Raw Permalink 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"
"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, "'", `'\''`) + "'"
}