fix(ansible): add lineinfile backrefs support
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
6fb5ebe920
commit
6eee9866e5
3 changed files with 74 additions and 6 deletions
|
|
@ -752,6 +752,7 @@ func moduleLineinfileWithClient(_ *Executor, client sshRunner, args map[string]a
|
|||
line := getStringArg(args, "line", "")
|
||||
regexpArg := getStringArg(args, "regexp", "")
|
||||
state := getStringArg(args, "state", "present")
|
||||
backrefs := getBoolArg(args, "backrefs", false)
|
||||
|
||||
if state == "absent" {
|
||||
if regexpArg != "" {
|
||||
|
|
@ -764,12 +765,24 @@ func moduleLineinfileWithClient(_ *Executor, client sshRunner, args map[string]a
|
|||
} else {
|
||||
// state == present
|
||||
if regexpArg != "" {
|
||||
// Replace line matching regexp
|
||||
// Replace line matching regexp.
|
||||
escapedLine := replaceAll(line, "/", "\\/")
|
||||
cmd := sprintf("sed -i 's/%s/%s/' %q", regexpArg, escapedLine, path)
|
||||
sedFlags := "-i"
|
||||
if backrefs {
|
||||
matchCmd := sprintf("grep -Eq %q %q", regexpArg, path)
|
||||
_, _, matchRC, _ := client.Run(context.Background(), matchCmd)
|
||||
if matchRC != 0 {
|
||||
return &TaskResult{Changed: false}, nil
|
||||
}
|
||||
sedFlags = "-E -i"
|
||||
}
|
||||
cmd := sprintf("sed %s 's/%s/%s/' %q", sedFlags, regexpArg, escapedLine, path)
|
||||
_, _, rc, _ := client.Run(context.Background(), cmd)
|
||||
if rc != 0 {
|
||||
// Line not found, append
|
||||
if backrefs {
|
||||
return &TaskResult{Changed: false}, nil
|
||||
}
|
||||
// Line not found, append.
|
||||
cmd = sprintf("echo %q >> %q", line, path)
|
||||
_, _, _, _ = client.Run(context.Background(), cmd)
|
||||
}
|
||||
|
|
|
|||
21
modules.go
21
modules.go
|
|
@ -438,6 +438,7 @@ func (e *Executor) moduleLineinfile(ctx context.Context, client *SSHClient, args
|
|||
line := getStringArg(args, "line", "")
|
||||
regexp := getStringArg(args, "regexp", "")
|
||||
state := getStringArg(args, "state", "present")
|
||||
backrefs := getBoolArg(args, "backrefs", false)
|
||||
|
||||
if state == "absent" {
|
||||
if regexp != "" {
|
||||
|
|
@ -450,12 +451,26 @@ func (e *Executor) moduleLineinfile(ctx context.Context, client *SSHClient, args
|
|||
} else {
|
||||
// state == present
|
||||
if regexp != "" {
|
||||
// Replace line matching regexp
|
||||
// Replace line matching regexp.
|
||||
escapedLine := replaceAll(line, "/", "\\/")
|
||||
cmd := sprintf("sed -i 's/%s/%s/' %q", regexp, escapedLine, path)
|
||||
sedFlags := "-i"
|
||||
if backrefs {
|
||||
// When backrefs is enabled, Ansible only replaces matching lines
|
||||
// and does not append a new line when the pattern is absent.
|
||||
matchCmd := sprintf("grep -Eq %q %q", regexp, path)
|
||||
_, _, matchRC, _ := client.Run(ctx, matchCmd)
|
||||
if matchRC != 0 {
|
||||
return &TaskResult{Changed: false}, nil
|
||||
}
|
||||
sedFlags = "-E -i"
|
||||
}
|
||||
cmd := sprintf("sed %s 's/%s/%s/' %q", sedFlags, regexp, escapedLine, path)
|
||||
_, _, rc, _ := client.Run(ctx, cmd)
|
||||
if rc != 0 {
|
||||
// Line not found, append
|
||||
if backrefs {
|
||||
return &TaskResult{Changed: false}, nil
|
||||
}
|
||||
// Line not found, append.
|
||||
cmd = sprintf("echo %q >> %q", line, path)
|
||||
_, _, _, _ = client.Run(ctx, cmd)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -387,6 +387,46 @@ func TestModulesFile_ModuleLineinfile_Good_RegexpFallsBackToAppend(t *testing.T)
|
|||
assert.True(t, mock.hasExecuted(`echo`))
|
||||
}
|
||||
|
||||
func TestModulesFile_ModuleLineinfile_Good_BackrefsReplaceMatchOnly(t *testing.T) {
|
||||
e, mock := newTestExecutorWithMock("host1")
|
||||
|
||||
result, err := moduleLineinfileWithClient(e, mock, map[string]any{
|
||||
"path": "/etc/example.conf",
|
||||
"regexp": "^(foo=).*$",
|
||||
"line": "\\1bar",
|
||||
"backrefs": true,
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.True(t, result.Changed)
|
||||
assert.True(t, mock.hasExecuted(`grep -Eq`))
|
||||
cmd := mock.lastCommand()
|
||||
assert.Equal(t, "Run", cmd.Method)
|
||||
assert.Contains(t, cmd.Cmd, "sed -E -i")
|
||||
assert.Contains(t, cmd.Cmd, "s/^(foo=).*$")
|
||||
assert.Contains(t, cmd.Cmd, "\\1bar")
|
||||
assert.Contains(t, cmd.Cmd, `"/etc/example.conf"`)
|
||||
assert.False(t, mock.hasExecuted(`echo`))
|
||||
}
|
||||
|
||||
func TestModulesFile_ModuleLineinfile_Good_BackrefsNoMatchNoAppend(t *testing.T) {
|
||||
e, mock := newTestExecutorWithMock("host1")
|
||||
mock.expectCommand("grep -Eq", "", "", 1)
|
||||
|
||||
result, err := moduleLineinfileWithClient(e, mock, map[string]any{
|
||||
"path": "/etc/example.conf",
|
||||
"regexp": "^(foo=).*$",
|
||||
"line": "\\1bar",
|
||||
"backrefs": true,
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.False(t, result.Changed)
|
||||
assert.Equal(t, 1, mock.commandCount())
|
||||
assert.Contains(t, mock.lastCommand().Cmd, "grep -Eq")
|
||||
assert.False(t, mock.hasExecuted(`echo`))
|
||||
}
|
||||
|
||||
func TestModulesFile_ModuleLineinfile_Bad_MissingPath(t *testing.T) {
|
||||
e, mock := newTestExecutorWithMock("host1")
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue