55 lines
2.2 KiB
Go
55 lines
2.2 KiB
Go
package builtin
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
|
||
"github.com/vladimir/goherence/internal/module"
|
||
"github.com/vladimir/goherence/internal/ssh"
|
||
)
|
||
|
||
// ShellModule — единственный по-настоящему императивный builtin-модуль.
|
||
// Его использование намеренно ограничивается линтером (internal/lint) —
|
||
// сам модуль здесь просто честно исполняет команду, никакой магии.
|
||
type ShellModule struct{}
|
||
|
||
func (m *ShellModule) Name() string { return "shell" }
|
||
|
||
func (m *ShellModule) Run(ctx context.Context, in module.Input, conn *ssh.Conn) (module.Result, error) {
|
||
cmd, ok := in.Args["cmd"].(string)
|
||
if !ok {
|
||
return module.Result{}, fmt.Errorf("shell: аргумент cmd обязателен и должен быть строкой")
|
||
}
|
||
|
||
if in.CheckMode {
|
||
return module.Result{
|
||
SchemaVersion: module.SchemaVersion,
|
||
Changed: true, // shell не умеет предсказывать идемпотентность — check-mode всегда "would change"
|
||
Msg: "check mode: команда не выполнена: " + cmd,
|
||
}, nil
|
||
}
|
||
|
||
res, err := conn.Run(cmd)
|
||
if err != nil {
|
||
return module.Result{}, err
|
||
}
|
||
|
||
return module.Result{
|
||
SchemaVersion: module.SchemaVersion,
|
||
Changed: true, // shell не имеет понятия состояния — считается изменяющим всегда
|
||
Failed: res.ExitCode != 0,
|
||
Msg: res.Stdout,
|
||
Diff: map[string]string{
|
||
"stderr": res.Stderr,
|
||
"exit_code": fmt.Sprintf("%d", res.ExitCode),
|
||
},
|
||
}, nil
|
||
}
|
||
|
||
// CommandModule — то же самое, что shell, но семантически "без shell-обвязки"
|
||
// (в реальном Ansible command не проходит через /bin/sh, что важно для
|
||
// экранирования). Для первой версии реализация идентична shell —
|
||
// разница добавляется отдельно, когда понадобится exec без shell-интерпретации.
|
||
type CommandModule struct{ ShellModule }
|
||
|
||
func (m *CommandModule) Name() string { return "command" }
|