-
-
Notifications
You must be signed in to change notification settings - Fork 31
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* add dry run mode with --dry * lint: minor warns
- Loading branch information
Showing
7 changed files
with
262 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package executor | ||
|
||
import ( | ||
"bufio" | ||
"bytes" | ||
"context" | ||
"io" | ||
"log" | ||
"os" | ||
"strings" | ||
) | ||
|
||
// Dry is an executor for dry run, just prints commands and files to be copied, synced, deleted. | ||
// Useful for debugging and testing, doesn't actually execute anything. | ||
type Dry struct { | ||
hostAddr string | ||
hostName string | ||
} | ||
|
||
// NewDry creates new executor for dry run | ||
func NewDry(hostAddr, hostName string) *Dry { | ||
return &Dry{hostAddr: hostAddr, hostName: hostName} | ||
} | ||
|
||
// Run shows the command content, doesn't execute it | ||
func (ex *Dry) Run(_ context.Context, cmd string, verbose bool) (out []string, err error) { | ||
log.Printf("[DEBUG] run %s", cmd) | ||
outLog, _ := MakeOutAndErrWriters(ex.hostAddr, ex.hostName, verbose) | ||
var stdoutBuf bytes.Buffer | ||
mwr := io.MultiWriter(outLog, &stdoutBuf) | ||
mwr.Write([]byte(cmd)) //nolint | ||
for _, line := range strings.Split(stdoutBuf.String(), "\n") { | ||
if line != "" { | ||
out = append(out, line) | ||
} | ||
} | ||
return out, nil | ||
} | ||
|
||
// Upload doesn't actually upload, just prints the command | ||
func (ex *Dry) Upload(_ context.Context, local, remote string, mkdir bool) (err error) { | ||
log.Printf("[DEBUG] upload %s to %s, mkdir: %v", local, remote, mkdir) | ||
if strings.Contains(remote, "spot-script") { | ||
outLog, outErr := MakeOutAndErrWriters(ex.hostAddr, ex.hostName, true) | ||
outErr.Write([]byte("command script " + remote)) //nolint | ||
// read local file and write it to outLog | ||
f, err := os.Open(local) //nolint | ||
if err != nil { | ||
return err | ||
} | ||
defer f.Close() //nolint ro file | ||
|
||
scanner := bufio.NewScanner(f) | ||
for scanner.Scan() { | ||
outLog.Write([]byte(scanner.Text())) //nolint | ||
} | ||
if err := scanner.Err(); err != nil { | ||
return err | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
// Download file from remote server with scp | ||
func (ex *Dry) Download(_ context.Context, remote, local string, mkdir bool) (err error) { | ||
log.Printf("[DEBUG] download %s to %s, mkdir: %v", local, remote, mkdir) | ||
return nil | ||
} | ||
|
||
// Sync doesn't sync anything, just prints the command | ||
func (ex *Dry) Sync(_ context.Context, localDir, remoteDir string, del bool) ([]string, error) { | ||
log.Printf("[DEBUG] sync %s to %s, delite: %v", localDir, remoteDir, del) | ||
return nil, nil | ||
} | ||
|
||
// Delete doesn't delete anything, just prints the command | ||
func (ex *Dry) Delete(_ context.Context, remoteFile string, recursive bool) (err error) { | ||
log.Printf("[DEBUG] delete %s, recursive: %v", remoteFile, recursive) | ||
return nil | ||
} | ||
|
||
// Close doesn't do anything | ||
func (ex *Dry) Close() error { | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
package executor | ||
|
||
import ( | ||
"bytes" | ||
"context" | ||
"io" | ||
"log" | ||
"os" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestDry_Run(t *testing.T) { | ||
ctx := context.Background() | ||
dry := NewDry("hostAddr", "hostName") | ||
res, err := dry.Run(ctx, "ls -la /srv", true) | ||
require.NoError(t, err) | ||
require.Len(t, res, 1) | ||
require.Equal(t, "ls -la /srv", res[0]) | ||
} | ||
|
||
func TestDryUpload(t *testing.T) { | ||
tempFile, err := os.CreateTemp("", "spot-script") | ||
require.NoError(t, err) | ||
defer os.Remove(tempFile.Name()) | ||
|
||
content := "line1\nline2\nline3\n" | ||
_, err = tempFile.WriteString(content) | ||
require.NoError(t, err) | ||
tempFile.Close() | ||
|
||
dry := &Dry{ | ||
hostAddr: "host1.example.com", | ||
hostName: "host1", | ||
} | ||
|
||
stdout := captureOutput(func() { | ||
err = dry.Upload(context.Background(), tempFile.Name(), "remote/path/spot-script", true) | ||
}) | ||
|
||
require.NoError(t, err) | ||
|
||
// check for logs with the "command script" and file content in the output | ||
assert.Contains(t, stdout, "command script remote/path/spot-script", | ||
"expected log entry containing 'command script' not found") | ||
require.Contains(t, stdout, "line1", "expected log entry containing 'line1' not found") | ||
require.Contains(t, stdout, "line2", "expected log entry containing 'line2' not found") | ||
require.Contains(t, stdout, "line3", "expected log entry containing 'line3' not found") | ||
} | ||
|
||
func TestDryUpload_FileOpenError(t *testing.T) { | ||
nonExistentFile := "non_existent_file" | ||
|
||
dry := &Dry{ | ||
hostAddr: "host1.example.com", | ||
hostName: "host1", | ||
} | ||
|
||
err := dry.Upload(context.Background(), nonExistentFile, "remote/path/spot-script", true) | ||
require.Error(t, err) | ||
assert.Contains(t, err.Error(), "open non_existent_file", "expected error message containing 'open non_existent_file' not found") | ||
} | ||
|
||
func TestDryOperations(t *testing.T) { | ||
dry := &Dry{ | ||
hostAddr: "host1.example.com", | ||
hostName: "host1", | ||
} | ||
|
||
testCases := []struct { | ||
name string | ||
operation func() error | ||
expectedLog string | ||
}{ | ||
{ | ||
name: "download", | ||
operation: func() error { | ||
return dry.Download(context.Background(), "remote/path", "local/path", true) | ||
}, | ||
expectedLog: "[DEBUG] download local/path to remote/path, mkdir: true", | ||
}, | ||
{ | ||
name: "sync", | ||
operation: func() error { | ||
_, err := dry.Sync(context.Background(), "local/dir", "remote/dir", true) | ||
return err | ||
}, | ||
expectedLog: "[DEBUG] sync local/dir to remote/dir, delite: true", | ||
}, | ||
{ | ||
name: "delete", | ||
operation: func() error { | ||
return dry.Delete(context.Background(), "remote/file", true) | ||
}, | ||
expectedLog: "[DEBUG] delete remote/file, recursive: true", | ||
}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
t.Run(tc.name, func(t *testing.T) { | ||
buff := bytes.NewBuffer(nil) | ||
log.SetOutput(buff) | ||
err := tc.operation() | ||
require.NoError(t, err) | ||
stdout := buff.String() | ||
// check for logs with the expected log entry in the output | ||
assert.Contains(t, stdout, tc.expectedLog, "expected log entry not found") | ||
}) | ||
} | ||
} | ||
|
||
func captureOutput(f func()) (stdout string) { | ||
// redirect stdout | ||
oldStdout := os.Stdout | ||
rout, wout, _ := os.Pipe() | ||
os.Stdout = wout | ||
|
||
// execute the function | ||
f() | ||
|
||
// stop capturing | ||
wout.Close() | ||
os.Stdout = oldStdout | ||
|
||
// read the captured output | ||
stdoutBuf, _ := io.ReadAll(rout) | ||
|
||
return string(stdoutBuf) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters