Files
dokku/plugins/common/subprocess.go
Jose Diaz-Gonzalez 1a052969dc feat: app filtering to commands listing or iterating over apps
This increases the time to list apps from .05s to .2s~.3s on the simplest check, but the increased time is worth it for actually verifyng access to an app (should that need arise).

We'll also want to add the same to app existence checks.
2022-02-26 00:37:54 -05:00

148 lines
3.7 KiB
Go

package common
import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"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
}
// Start is a wrapper around exec.Command.Start()
func (sc *ShellCmd) Start() error {
sc.setup()
return sc.Command.Start()
}
// 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...)
}
// PlugnTriggerExists returns whether a plugin trigger exists (ignoring the existence of any within the 20_events plugin)
func PlugnTriggerExists(triggerName string) bool {
pluginPath := MustGetEnv("PLUGIN_ENABLED_PATH")
glob := filepath.Join(pluginPath, "*", triggerName)
exists := false
files, _ := filepath.Glob(glob)
for _, file := range files {
plugin := strings.Trim(strings.TrimPrefix(strings.TrimSuffix(file, "/user-auth-app"), pluginPath), "/")
if plugin != "20_events" {
exists = true
break
}
}
return exists
}