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