package builtin import ( "bytes" "context" "fmt" "github.com/vladimir/goherence/internal/module" "github.com/vladimir/goherence/internal/ssh" ) // CopyModule — аналог ansible.builtin.copy: приводит файл на хосте // к заданному содержимому. Идемпотентен: сначала читает текущее // содержимое и сравнивает, "changed" выставляется только при реальном отличии. type CopyModule struct{} func (m *CopyModule) Name() string { return "copy" } func (m *CopyModule) Run(ctx context.Context, in module.Input, conn *ssh.Conn) (module.Result, error) { dest, ok := in.Args["dest"].(string) if !ok { return module.Result{}, fmt.Errorf("copy: аргумент dest обязателен") } content, ok := in.Args["content"].(string) if !ok { return module.Result{}, fmt.Errorf("copy: аргумент content обязателен (первая версия не читает src-файлы с control node)") } mode, _ := in.Args["mode"].(string) if mode == "" { mode = "0644" } current, err := conn.ReadFile(dest) same := err == nil && bytes.Equal(current, []byte(content)) if same { return module.Result{SchemaVersion: module.SchemaVersion, Changed: false, Msg: "уже в нужном состоянии"}, nil } if in.CheckMode { return module.Result{SchemaVersion: module.SchemaVersion, Changed: true, Msg: "check mode: файл был бы изменён: " + dest}, nil } if err := conn.WriteFile(dest, []byte(content), mode); err != nil { return module.Result{}, fmt.Errorf("copy: запись %s: %w", dest, err) } return module.Result{ SchemaVersion: module.SchemaVersion, Changed: true, Msg: "файл записан: " + dest, }, nil }