Files
dokku/plugins/common/subprocess.go
Jose Diaz-Gonzalez 86e53f96a9 feat: log all triggers called by golang in trace output
Trace mode is useful for shell code, but less so for golang. Adding debug logging in golang for trigger calls makes it a little easier to see what is happening.
2021-01-23 18:30:33 -05:00

124 lines
3.0 KiB
Go

package common
import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"strings"
"github.com/codeskyblue/go-sh"
)
// ShellCmd represents a shell command to be run for dokku
type ShellCmd struct {
Env map[string]string
Command *exec.Cmd
CommandString string
Args []string
ShowOutput bool
}
// NewShellCmd returns a new ShellCmd struct
func NewShellCmd(command string) *ShellCmd {
items := strings.Split(command, " ")
cmd := items[0]
args := items[1:]
return NewShellCmdWithArgs(cmd, args...)
}
// NewShellCmdWithArgs returns a new ShellCmd struct
func NewShellCmdWithArgs(cmd string, args ...string) *ShellCmd {
commandString := strings.Join(append([]string{cmd}, args...), " ")
return &ShellCmd{
Command: exec.Command(cmd, args...),
CommandString: commandString,
Args: args,
ShowOutput: true,
}
}
func (sc *ShellCmd) setup() {
env := os.Environ()
for k, v := range sc.Env {
env = append(env, fmt.Sprintf("%s=%s", k, v))
}
sc.Command.Env = env
if sc.ShowOutput {
sc.Command.Stdout = os.Stdout
sc.Command.Stderr = os.Stderr
}
}
// Execute is a lightweight wrapper around exec.Command
func (sc *ShellCmd) Execute() bool {
sc.setup()
if err := sc.Command.Run(); err != nil {
return false
}
return true
}
// Output is a lightweight wrapper around exec.Command.Output()
func (sc *ShellCmd) Output() ([]byte, error) {
sc.setup()
return sc.Command.Output()
}
// CombinedOutput is a lightweight wrapper around exec.Command.CombinedOutput()
func (sc *ShellCmd) CombinedOutput() ([]byte, error) {
sc.setup()
return sc.Command.CombinedOutput()
}
// PlugnTrigger fire the given plugn trigger with the given args
func PlugnTrigger(triggerName string, args ...string) error {
LogDebug(fmt.Sprintf("plugn trigger %s %v", triggerName, args))
return PlugnTriggerSetup(triggerName, args...).Run()
}
// PlugnTriggerOutput fire the given plugn trigger with the given args
func PlugnTriggerOutput(triggerName string, args ...string) ([]byte, error) {
LogDebug(fmt.Sprintf("plugn trigger %s %v", triggerName, args))
rE, wE, _ := os.Pipe()
rO, wO, _ := os.Pipe()
session := PlugnTriggerSetup(triggerName, args...)
session.Stderr = wE
session.Stdout = wO
err := session.Run()
wE.Close()
wO.Close()
readStderr, _ := ioutil.ReadAll(rE)
readStdout, _ := ioutil.ReadAll(rO)
stderr := string(readStderr[:])
if err != nil {
err = fmt.Errorf(stderr)
}
if os.Getenv("DOKKU_TRACE") == "1" {
for _, line := range strings.Split(stderr, "\n") {
LogDebug(fmt.Sprintf("plugn trigger %s stderr: %s", triggerName, line))
}
for _, line := range strings.Split(string(readStdout[:]), "\n") {
LogDebug(fmt.Sprintf("plugn trigger %s stdout: %s", triggerName, line))
}
}
return readStdout, err
}
// PlugnTriggerSetup sets up a plugn trigger call
func PlugnTriggerSetup(triggerName string, args ...string) *sh.Session {
shellArgs := make([]interface{}, len(args)+2)
shellArgs[0] = "trigger"
shellArgs[1] = triggerName
for i, arg := range args {
shellArgs[i+2] = arg
}
return sh.Command("plugn", shellArgs...)
}