Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add sudo support to copy, mcopy, delete and wait #67

Merged
merged 3 commits into from
May 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ Each command type supports the following options:
- `ignore_errors`: if set to `true` the command will not fail the task in case of an error.
- `no_auto`: if set to `true` the command will not be executed automatically, but can be executed manually using the `--only` flag.
- `local`: if set to `true` the command will be executed on the local host (the one running the `spot` command) instead of the remote host(s).
- `sudo`: if set to `true` the script command will be executed with `sudo` privileges.
- `sudo`: if set to `true` the command will be executed with `sudo` privileges.

example setting `ignore_errors` and `no_auto` options:

Expand All @@ -257,7 +257,7 @@ example setting `ignore_errors` and `no_auto` options:
options: {ignore_errors: true, no_auto: true}
```

Please note that the `sudo` option is only supported for the `script` command type. This limitation exists because there is no direct and universal method for uploading files over SFTP with sudo privileges. As a workaround, users can first use the `copy` command to transfer files to a temporary location, and then execute a `script` command with `sudo: true` to move those files to their final destination. Alternatively, using the root user directly in the playbook will allow direct file transfer to any restricted location and enable running privileged commands without the need to use sudo.
Please note that the `sudo` option is not supported for the `sync` command type, but all other command types support it.


### Script Execution
Expand Down
14 changes: 7 additions & 7 deletions pkg/config/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,17 +73,17 @@ func (cmd *Cmd) GetScript() (string, io.Reader) {
elems := strings.Split(cmd.Script, "\n")
if len(elems) > 1 {
log.Printf("[DEBUG] command %q is multiline, using script file", cmd.Name)
return "", cmd.getScriptFile()
return "", cmd.scriptFile(cmd.Script)
}

log.Printf("[DEBUG] command %q is single line, using script string", cmd.Name)
return cmd.getScriptCommand(), nil
return cmd.scriptCommand(cmd.Script), nil
}

// GetScriptCommand concatenates all script line in commands into one a string to be executed by shell.
// Empty string is returned if no script is defined.
func (cmd *Cmd) getScriptCommand() string {
if cmd.Script == "" {
func (cmd *Cmd) scriptCommand(inp string) string {
if inp == "" {
return ""
}

Expand All @@ -100,7 +100,7 @@ func (cmd *Cmd) getScriptCommand() string {
res += strings.Join(secrets, " ") + " "
}

elems := strings.Split(cmd.Script, "\n")
elems := strings.Split(inp, "\n")
var parts []string // nolint
for _, el := range elems {
c := strings.TrimSpace(el)
Expand All @@ -118,7 +118,7 @@ func (cmd *Cmd) getScriptCommand() string {

// GetScriptFile returns a reader for script file. All the line in the command used as a script, with hashbang,
// set -e and environment variables.
func (cmd *Cmd) getScriptFile() io.Reader {
func (cmd *Cmd) scriptFile(inp string) io.Reader {
var buf bytes.Buffer

buf.WriteString("#!/bin/sh\n") // add hashbang
Expand All @@ -133,7 +133,7 @@ func (cmd *Cmd) getScriptFile() io.Reader {
}
}

elems := strings.Split(cmd.Script, "\n")
elems := strings.Split(inp, "\n")
for _, el := range elems {
c := strings.TrimSpace(el)
if len(c) < 2 {
Expand Down
8 changes: 4 additions & 4 deletions pkg/config/command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,21 +123,21 @@ func TestCmd_getScriptCommand(t *testing.T) {
t.Run("script", func(t *testing.T) {
cmd := c.Tasks[0].Commands[3]
assert.Equal(t, "git", cmd.Name, "name")
res := cmd.getScriptCommand()
res := cmd.scriptCommand(cmd.Script)
assert.Equal(t, `sh -c "git clone https://example.com/remark42.git /srv || true; cd /srv; git pull"`, res)
})

t.Run("no-script", func(t *testing.T) {
cmd := c.Tasks[0].Commands[1]
assert.Equal(t, "copy configuration", cmd.Name)
res := cmd.getScriptCommand()
res := cmd.scriptCommand(cmd.Script)
assert.Equal(t, "", res)
})

t.Run("script with env", func(t *testing.T) {
cmd := c.Tasks[0].Commands[4]
assert.Equal(t, "docker", cmd.Name)
res := cmd.getScriptCommand()
res := cmd.scriptCommand(cmd.Script)
assert.Equal(t, `sh -c "BAR='qux' FOO='bar' docker pull umputun/remark42:latest; docker stop remark42 || true; docker rm remark42 || true; docker run -d --name remark42 -p 8080:8080 umputun/remark42:latest"`, res)
})
}
Expand Down Expand Up @@ -212,7 +212,7 @@ func TestCmd_getScriptFile(t *testing.T) {

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
reader := tt.cmd.getScriptFile()
reader := tt.cmd.scriptFile(tt.cmd.Script)
scriptContentBytes, err := io.ReadAll(reader)
assert.NoError(t, err)
scriptContent := string(scriptContentBytes)
Expand Down
Loading