Skip to content

Commit

Permalink
Merge pull request #42 from vladimirvivien/exec-echo
Browse files Browse the repository at this point in the history
Support for console output for RUN and CAPTURE
  • Loading branch information
vladimirvivien authored Jan 9, 2020
2 parents 5e455c9 + 91df766 commit a95de45
Show file tree
Hide file tree
Showing 11 changed files with 212 additions and 21 deletions.
20 changes: 19 additions & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,13 +135,22 @@ Or, with its named parameter `cmd:`:
CAPTURE cmd:"/bin/journalctl -l -u kube-apiserver"
```

### CAPTURE file names
#### CAPTURE file names
The captured output will be written to a file whose name is derived from the command string as follows:

```
_bin_journalctl__l__u_kube-api-server.txt
```

#### CAPTURE Echo output
The CAPTURE command can also copy its result to standard output using the `echo` parameter:

```
CAPTURE cmd:"/bin/journalctl -l -u kube-apiserver" echo:"true"
```

Note that you have to use the named parameter format.

### COPY
This directive specifies one or more files (and/or directories) as data sources that are copied
into the arachive bundle as shown in the following example
Expand Down Expand Up @@ -324,6 +333,15 @@ COPY /tmp/containers
RUN /usr/bin/rm -rf /tmp/containers
```

#### RUN Echo output
The RUJN command can also copy its result to standard output using the `echo` parameter:

```
RUN cmd:"/bin/journalctl -l -u kube-apiserver" echo:"true"
```

Note that you have to use the named parameter format.

### WORKDIR
In a Diagnostics.file, `WORKDIR` specifies the working directory used when building the archive bundle as shown in the following example:

Expand Down
48 changes: 48 additions & 0 deletions exec/capture_exec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,29 @@ func TestExecLocalCAPTURE(t *testing.T) {
return nil
},
},
{
name: "CAPTURE with echo on",
source: func() string {
return `CAPTURE cmd:"/bin/echo 'HELLO WORLD'" echo:"true"`
},
exec: func(s *script.Script) error {
machine := s.Preambles[script.CmdFrom][0].(*script.FromCommand).Machines()[0].Address()
workdir := s.Preambles[script.CmdWorkDir][0].(*script.WorkdirCommand)
capCmd := s.Actions[0].(*script.CaptureCommand)

e := New(s)
if err := e.Execute(); err != nil {
return err
}

fileName := filepath.Join(workdir.Path(), machine, fmt.Sprintf("%s.txt", sanitizeStr(capCmd.GetCmdString())))
if _, err := os.Stat(fileName); err != nil && !os.IsNotExist(err) {
return err
}

return nil
},
},
{
name: "CAPTURE command as unknown user",
source: func() string {
Expand Down Expand Up @@ -328,6 +351,31 @@ func TestExecRemoteCAPTURE(t *testing.T) {
},
shouldFail: true,
},
{
name: "CAPTURE with echo on",
source: func() string {
src := `FROM 127.0.0.1:22
AUTHCONFIG username:${USER} private-key:${HOME}/.ssh/id_rsa
CAPTURE cmd:'/bin/echo "HELLO WORLD"' echo:"on"`
return src
},
exec: func(s *script.Script) error {
machine := s.Preambles[script.CmdFrom][0].(*script.FromCommand).Machines()[0].Address()
workdir := s.Preambles[script.CmdWorkDir][0].(*script.WorkdirCommand)
capCmd := s.Actions[0].(*script.CaptureCommand)

e := New(s)
if err := e.Execute(); err != nil {
return err
}

fileName := filepath.Join(workdir.Path(), sanitizeStr(machine), fmt.Sprintf("%s.txt", sanitizeStr(capCmd.GetCmdString())))
if _, err := os.Stat(fileName); err != nil {
return err
}
return nil
},
},
{
name: "CAPTURE remote command with bad AUTHCONFIG user",
source: func() string {
Expand Down
23 changes: 16 additions & 7 deletions exec/local_exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ import (

// exeLocally runs script using locally installed tool
func exeLocally(asCmd *script.AsCommand, action script.Command, workdir string) error {

//for _, action := range src.Actions {
switch cmd := action.(type) {
case *script.CopyCommand:
if err := copyLocally(asCmd, cmd, workdir); err != nil {
Expand All @@ -37,7 +35,6 @@ func exeLocally(asCmd *script.AsCommand, action script.Command, workdir string)
default:
logrus.Errorf("Unsupported command %T", cmd)
}
//}

return nil
}
Expand Down Expand Up @@ -67,10 +64,16 @@ func captureLocally(asCmd *script.AsCommand, cmdCap *script.CaptureCommand, envs
cliErr := fmt.Errorf("local command %s failed: %s", cliCmd, err)
logrus.Warn(cliErr)

return writeError(cliErr, filePath)
return writeCmdError(cliErr, filePath, cmdStr)
}

echo := false
switch cmdCap.GetEcho() {
case "true", "yes", "on":
echo = true
}

if err := writeFile(cmdReader, filePath); err != nil {
if err := writeCmdOutput(cmdReader, filePath, echo, cmdStr); err != nil {
return err
}

Expand Down Expand Up @@ -108,10 +111,16 @@ func runLocally(asCmd *script.AsCommand, cmdRun *script.RunCommand, workdir stri
}

// save result of CMD
if err := os.Setenv("CMD_RESULT", strings.TrimSpace(string(bytes))); err != nil {
result := strings.TrimSpace(string(bytes))
if err := os.Setenv("CMD_RESULT", result); err != nil {
return fmt.Errorf("RUN: set CMD_RESULT: %s", err)
}

switch cmdRun.GetEcho() {
case "true", "yes", "on":
fmt.Printf("%s\n%s\n", cmdRun.GetCmdString(), result)
}

return nil
}

Expand Down Expand Up @@ -165,7 +174,7 @@ func copyLocally(asCmd *script.AsCommand, cmd *script.CopyCommand, dest string)
msgBytes, _ := ioutil.ReadAll(output)
cliErr := fmt.Errorf("local file copy failed: %s: %s: %s", path, string(msgBytes), err)
logrus.Warn(cliErr)
return writeError(cliErr, targetPath)
return writeCmdError(cliErr, targetPath, cpCmd)
}
}

Expand Down
17 changes: 14 additions & 3 deletions exec/remote_exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,16 @@ func captureRemotely(user, privKey, hostAddr string, cmdCap *script.CaptureComma
if err != nil {
sshErr := fmt.Errorf("CAPTURE remote command %s failed: %s", cmdStr, err)
logrus.Warn(sshErr)
return writeError(sshErr, filePath)
return writeCmdError(sshErr, filePath, cmdStr)
}

if err := writeFile(cmdReader, filePath); err != nil {
echo := false
switch cmdCap.GetEcho() {
case "true", "yes", "on":
echo = true
}

if err := writeCmdOutput(cmdReader, filePath, echo, cmdStr); err != nil {
return err
}

Expand Down Expand Up @@ -122,6 +128,11 @@ func runRemotely(user, privKey, hostAddr string, cmdRun *script.RunCommand, work
return fmt.Errorf("RUN: set CMD_RESULT: %s: %s", result, err)
}

switch cmdRun.GetEcho() {
case "true", "yes", "on":
fmt.Printf("%s\n%s\n", cmdRun.GetCmdString(), result)
}

return nil
}

Expand Down Expand Up @@ -184,7 +195,7 @@ func copyRemotely(user, privKey string, machine *script.Machine, asCmd *script.A
msgBytes, _ := ioutil.ReadAll(output)
cliErr := fmt.Errorf("scp command failed: %s: %s", err, string(msgBytes))
logrus.Warn(cliErr)
return writeError(cliErr, targetPath)
return writeCmdError(cliErr, targetPath, fmt.Sprintf("%s %s", cliScpName, strings.Join(args, " ")))
}
logrus.Debug("Remote copy succeeded:", remotePath)
}
Expand Down
40 changes: 40 additions & 0 deletions exec/run_exec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,24 @@ func TestExecLocalRUN(t *testing.T) {
return nil
},
},
{
name: "RUN with echo on",
source: func() string {
return `RUN cmd:"/bin/echo 'HELLO WORLD'" echo:"true"`
},
exec: func(s *script.Script) error {

e := New(s)
if err := e.Execute(); err != nil {
return err
}
result := os.Getenv("CMD_RESULT")
if result != "HELLO WORLD" {
return fmt.Errorf("RUN has unexpected CMD_RESULT: %s", result)
}
return nil
},
},
}

for _, test := range tests {
Expand Down Expand Up @@ -361,6 +379,28 @@ func TestExecRemoteRUN(t *testing.T) {
return nil
},
},
{
name: "RUN with echo on",
source: func() string {
return `FROM 127.0.0.1:22
AUTHCONFIG username:${USER} private-key:${HOME}/.ssh/id_rsa
RUN cmd:'/bin/echo "HELLO WORLD"' echo:"on"
`
},
exec: func(s *script.Script) error {

e := New(s)
if err := e.Execute(); err != nil {
return err
}

result := os.Getenv("CMD_RESULT")
if result != "HELLO WORLD" {
return fmt.Errorf("RUN has unexpected CMD_RESULT: %s", result)
}
return nil
},
},
}

for _, test := range tests {
Expand Down
19 changes: 15 additions & 4 deletions exec/support.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package exec

import (
"fmt"
"io"
"os"
"regexp"
Expand All @@ -20,21 +21,31 @@ func sanitizeStr(cmd string) string {
return strSanitization.ReplaceAllString(cmd, "_")
}

func writeFile(source io.Reader, filePath string) error {
func writeCmdOutput(source io.Reader, filePath string, echo bool, cmd string) error {
file, err := os.Create(filePath)
if err != nil {
return err
}
defer file.Close()
if _, err := io.Copy(file, source); err != nil {

reader := source
if echo {
fmt.Fprintf(file, "%s\n", cmd)
fmt.Fprintf(os.Stdout, "%s\n", cmd)

reader = io.TeeReader(source, os.Stdout)
}

if _, err := io.Copy(file, reader); err != nil {
return err
}

logrus.Debugf("Wrote file %s", filePath)

return nil
}

func writeError(err error, filePath string) error {
func writeCmdError(err error, filePath string, cmdStr string) error {
errReader := strings.NewReader(err.Error())
return writeFile(errReader, filePath)
return writeCmdOutput(errReader, filePath, false, cmdStr)
}
7 changes: 7 additions & 0 deletions script/capture_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,10 @@ func (c *CaptureCommand) GetEffectiveCmdStr() (string, error) {
}
return cmdStr, nil
}

// GetEcho returns the echo param for command. When
// set to {yes|true|on} the result of the command will be
// redirected to the stdout|stderr
func (c *CaptureCommand) GetEcho() string {
return c.RunCommand.GetEcho()
}
26 changes: 22 additions & 4 deletions script/capture_cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,10 +231,6 @@ func TestCommandCAPTURE(t *testing.T) {
return fmt.Errorf("Script has unexpected actions, needs %d", len(s.Actions))
}
cmd := s.Actions[0].(*CaptureCommand)
if cmd.Args()["cmd"] != cmd.GetCmdString() {
return fmt.Errorf("CAPTURE action with unexpected CLI string %s", cmd.GetCmdString())
}

if cmd.Args()["cmd"] != cmd.GetCmdString() {
return fmt.Errorf("CAPTURE action with unexpected command string %s", cmd.GetCmdString())
}
Expand All @@ -261,6 +257,28 @@ func TestCommandCAPTURE(t *testing.T) {
return nil
},
},
{
name: "CAPTURE with echo param",
source: func() string {
return `CAPTURE shell:"/bin/bash -c" cmd:"echo 'HELLO WORLD'" echo:"true"`
},
script: func(s *Script) error {
if len(s.Actions) != 1 {
return fmt.Errorf("Script has unexpected actions, needs %d", len(s.Actions))
}
cmd := s.Actions[0].(*CaptureCommand)
if cmd.Args()["cmd"] != cmd.GetCmdString() {
return fmt.Errorf("CAPTURE action with unexpected CLI string %s", cmd.GetCmdString())
}
if cmd.Args()["shell"] != cmd.GetCmdShell() {
return fmt.Errorf("CAPTURE action with unexpected shell %s", cmd.GetCmdShell())
}
if cmd.Args()["echo"] != cmd.GetEcho() {
return fmt.Errorf("CAPTURE action with unexpected echo param %s", cmd.GetCmdShell())
}
return nil
},
},
}

for _, test := range tests {
Expand Down
4 changes: 2 additions & 2 deletions script/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,14 @@ var (
Cmds = map[string]CommandMeta{
CmdAs: CommandMeta{Name: CmdAs, MinArgs: 1, MaxArgs: 2, Supported: true},
CmdAuthConfig: CommandMeta{Name: CmdAuthConfig, MinArgs: 1, MaxArgs: 3, Supported: true},
CmdCapture: CommandMeta{Name: CmdCapture, MinArgs: 1, MaxArgs: 2, Supported: true},
CmdCapture: CommandMeta{Name: CmdCapture, MinArgs: 1, MaxArgs: 3, Supported: true},
CmdCopy: CommandMeta{Name: CmdCopy, MinArgs: 1, MaxArgs: -1, Supported: true},
CmdEnv: CommandMeta{Name: CmdEnv, MinArgs: 1, MaxArgs: -1, Supported: true},
CmdFrom: CommandMeta{Name: CmdFrom, MinArgs: 1, MaxArgs: -1, Supported: true},
CmdKubeConfig: CommandMeta{Name: CmdKubeConfig, MinArgs: 1, MaxArgs: 1, Supported: true},
CmdKubeGet: CommandMeta{Name: CmdKubeGet, MinArgs: 1, MaxArgs: -1, Supported: true},
CmdOutput: CommandMeta{Name: CmdOutput, MinArgs: 1, MaxArgs: 1, Supported: true},
CmdRun: CommandMeta{Name: CmdRun, MinArgs: 1, MaxArgs: 2, Supported: true},
CmdRun: CommandMeta{Name: CmdRun, MinArgs: 1, MaxArgs: 3, Supported: true},
CmdWorkDir: CommandMeta{Name: CmdWorkDir, MinArgs: 1, MaxArgs: 1, Supported: true},
}
)
Expand Down
7 changes: 7 additions & 0 deletions script/run_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,10 @@ func (c *RunCommand) GetEffectiveCmdStr() (string, error) {
}
return cmdStr, nil
}

// GetEcho returns the echo param for command. When
// set to {yes|true|on} the result of the command will be
// redirected to the stdout|stderr
func (c *RunCommand) GetEcho() string {
return ExpandEnv(c.cmd.args["echo"])
}
Loading

0 comments on commit a95de45

Please sign in to comment.