Skip to content

Commit

Permalink
Improve stdout and stderr capture in hook managmement
Browse files Browse the repository at this point in the history
mostly for tests
  • Loading branch information
rfay committed Oct 9, 2019
1 parent 21e454c commit e626744
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 52 deletions.
4 changes: 2 additions & 2 deletions cmd/ddev/cmd/config.go
Expand Up @@ -164,7 +164,7 @@ func handleConfigRun(cmd *cobra.Command, args []string) {
util.Failed("Please do not use `ddev config` in your home directory")
}

err = app.ProcessHooks("pre-config")
_, _, err = app.ProcessHooks("pre-config")
if err != nil {
util.Failed("Failed to process hook 'pre-config'")
}
Expand Down Expand Up @@ -210,7 +210,7 @@ func handleConfigRun(cmd *cobra.Command, args []string) {
if err != nil {
util.Failed("Failed to write provider config: %v", err)
}
err = app.ProcessHooks("post-config")
_, _, err = app.ProcessHooks("post-config")
if err != nil {
util.Failed("Failed to process hook 'post-config'")
}
Expand Down
7 changes: 3 additions & 4 deletions pkg/ddevapp/composer.go
Expand Up @@ -12,7 +12,7 @@ import (

// Composer runs composer commands in the web container, managing pre- and post- hooks
func (app *DdevApp) Composer(args []string) (string, string, error) {
err := app.ProcessHooks("pre-composer")
_, _, err := app.ProcessHooks("pre-composer")
if err != nil {
return "", "", fmt.Errorf("Failed to process pre-composer hooks: %v", err)
}
Expand All @@ -30,10 +30,9 @@ func (app *DdevApp) Composer(args []string) (string, string, error) {
if runtime.GOOS == "windows" && !nodeps.IsDockerToolbox() {
fileutil.ReplaceSimulatedLinks(app.AppRoot)
}
err = app.ProcessHooks("post-composer")
_, _, err = app.ProcessHooks("post-composer")
if err != nil {
return stdout, stderr, fmt.Errorf("Failed to process post-composer hooks: %v", err)
return "", "", fmt.Errorf("Failed to process post-composer hooks: %v", err)
}
return stdout, stderr, nil

}
60 changes: 34 additions & 26 deletions pkg/ddevapp/ddevapp.go
Expand Up @@ -168,7 +168,7 @@ func (app *DdevApp) FindContainerByType(containerType string) (*docker.APIContai

// Describe returns a map which provides detailed information on services associated with the running site.
func (app *DdevApp) Describe() (map[string]interface{}, error) {
err := app.ProcessHooks("pre-describe")
_, _, err := app.ProcessHooks("pre-describe")
if err != nil {
return nil, fmt.Errorf("Failed to process pre-describe hooks: %v", err)
}
Expand Down Expand Up @@ -229,7 +229,7 @@ func (app *DdevApp) Describe() (map[string]interface{}, error) {
appDesc["bgsyncimg"] = app.BgsyncImage
appDesc["dbaimg"] = app.DBAImage

err = app.ProcessHooks("post-describe")
_, _, err = app.ProcessHooks("post-describe")
if err != nil {
return nil, fmt.Errorf("Failed to process post-describe hooks: %v", err)
}
Expand Down Expand Up @@ -299,7 +299,7 @@ func (app *DdevApp) ImportDB(imPath string, extPath string, progress bool) error
return err
}

err = app.ProcessHooks("pre-import-db")
_, _, err = app.ProcessHooks("pre-import-db")
if err != nil {
return err
}
Expand Down Expand Up @@ -403,7 +403,7 @@ func (app *DdevApp) ImportDB(imPath string, extPath string, progress bool) error
return fmt.Errorf("failed to clean up %s after import: %v", dbPath, err)
}

err = app.ProcessHooks("post-import-db")
_, _, err = app.ProcessHooks("post-import-db")
if err != nil {
return err
}
Expand Down Expand Up @@ -518,7 +518,7 @@ type PullOptions struct {
// Pull performs an import from the a configured provider plugin, if one exists.
func (app *DdevApp) Pull(provider Provider, opts *PullOptions) error {
var err error
err = app.ProcessHooks("pre-pull")
_, _, err = app.ProcessHooks("pre-pull")
if err != nil {
return fmt.Errorf("Failed to process pre-pull hooks: %v", err)
}
Expand Down Expand Up @@ -579,7 +579,7 @@ func (app *DdevApp) Pull(provider Provider, opts *PullOptions) error {
}
}
}
err = app.ProcessHooks("post-pull")
_, _, err = app.ProcessHooks("post-pull")
if err != nil {
return fmt.Errorf("Failed to process post-pull hooks: %v", err)
}
Expand All @@ -591,15 +591,15 @@ func (app *DdevApp) Pull(provider Provider, opts *PullOptions) error {
func (app *DdevApp) ImportFiles(importPath string, extPath string) error {
app.DockerEnv()

if err := app.ProcessHooks("pre-import-files"); err != nil {
if _, _, err := app.ProcessHooks("pre-import-files"); err != nil {
return err
}

if err := app.ImportFilesAction(importPath, extPath); err != nil {
return err
}

if err := app.ProcessHooks("post-import-files"); err != nil {
if _, _, err := app.ProcessHooks("post-import-files"); err != nil {
return err
}

Expand Down Expand Up @@ -652,29 +652,37 @@ func (app *DdevApp) ComposeFiles() ([]string, error) {
}

// ProcessHooks executes Tasks defined in Hooks
func (app *DdevApp) ProcessHooks(hookName string) error {
func (app *DdevApp) ProcessHooks(hookName string) (string, string, error) {
var stdout, stderr string
if cmds := app.Hooks[hookName]; len(cmds) > 0 {
output.UserOut.Printf("Executing %s hook...", hookName)
}

for _, c := range app.Hooks[hookName] {
a := NewTask(app, c)
if a == nil {
return fmt.Errorf("unable to create task from %v", c)
return "", "", fmt.Errorf("unable to create task from %v", c)
}

output.UserOut.Printf("=== Running task: %s, output below", a.GetDescription())

stdout, stderr, err := a.Execute()
//output.UserOut.Println(stdout + "\n" + stderr)
taskout, taskerr, err := a.Execute()
if taskout != "" {
output.UserOut.Println(taskout)
}
if taskerr != "" {
output.UserOut.Errorln(taskerr)
}

if err != nil {
output.UserOut.Errorf("task failed: %v: %s %s", a.GetDescription(), stdout, stderr)
output.UserOut.Errorf("task failed: %v: %v", a.GetDescription(), err)
output.UserOut.Warn("A task failure does not mean that ddev failed, but your hook configuration has a command that failed.")
}
stdout = stdout + taskout
stderr = stderr + taskerr
}

return nil
return stdout, stderr, nil
}

// Start initiates docker-compose up
Expand Down Expand Up @@ -708,7 +716,7 @@ func (app *DdevApp) Start() error {
return err
}

err = app.ProcessHooks("pre-start")
_, _, err = app.ProcessHooks("pre-start")
if err != nil {
return err
}
Expand Down Expand Up @@ -804,7 +812,7 @@ func (app *DdevApp) Start() error {
return err
}

err = app.ProcessHooks("post-start")
_, _, err = app.ProcessHooks("post-start")
if err != nil {
return err
}
Expand Down Expand Up @@ -839,7 +847,7 @@ func (app *DdevApp) Exec(opts *ExecOpts) (string, string, error) {
if opts.Service == "" {
return "", "", fmt.Errorf("no service provided")
}
err := app.ProcessHooks("pre-exec")
_, _, err := app.ProcessHooks("pre-exec")
if err != nil {
return "", "", fmt.Errorf("Failed to process pre-exec hooks: %v", err)
}
Expand Down Expand Up @@ -893,7 +901,7 @@ func (app *DdevApp) Exec(opts *ExecOpts) (string, string, error) {
stdoutResult, stderrResult, err = dockerutil.ComposeCmd(files, exec...)
}

hookErr := app.ProcessHooks("post-exec")
_, _, hookErr := app.ProcessHooks("post-exec")
if hookErr != nil {
return stdoutResult, stderrResult, fmt.Errorf("Failed to process post-exec hooks: %v", hookErr)
}
Expand Down Expand Up @@ -1115,7 +1123,7 @@ func (app *DdevApp) Pause() error {
return fmt.Errorf("no project to stop")
}

err := app.ProcessHooks("pre-pause")
_, _, err := app.ProcessHooks("pre-pause")
if err != nil {
return err
}
Expand All @@ -1128,7 +1136,7 @@ func (app *DdevApp) Pause() error {
if _, _, err := dockerutil.ComposeCmd(files, "stop"); err != nil {
return err
}
err = app.ProcessHooks("post-pause")
_, _, err = app.ProcessHooks("post-pause")
if err != nil {
return err
}
Expand Down Expand Up @@ -1230,7 +1238,7 @@ func (app *DdevApp) DetermineSettingsPathLocation() (string, error) {
// Snapshot forces a mariadb snapshot of the db to be written into .ddev/db_snapshots
// Returns the dirname of the snapshot and err
func (app *DdevApp) Snapshot(snapshotName string) (string, error) {
err := app.ProcessHooks("pre-snapshot")
_, _, err := app.ProcessHooks("pre-snapshot")
if err != nil {
return "", fmt.Errorf("Failed to process pre-stop hooks: %v", err)
}
Expand Down Expand Up @@ -1268,7 +1276,7 @@ func (app *DdevApp) Snapshot(snapshotName string) (string, error) {
}

util.Success("Created database snapshot %s in %s", snapshotName, hostSnapshotDir)
err = app.ProcessHooks("post-snapshot")
_, _, err = app.ProcessHooks("post-snapshot")
if err != nil {
return snapshotName, fmt.Errorf("Failed to process pre-stop hooks: %v", err)
}
Expand All @@ -1279,7 +1287,7 @@ func (app *DdevApp) Snapshot(snapshotName string) (string, error) {
// The project must be stopped and docker volume removed and recreated for this to work.
func (app *DdevApp) RestoreSnapshot(snapshotName string) error {
var err error
err = app.ProcessHooks("pre-restore-snapshot")
_, _, err = app.ProcessHooks("pre-restore-snapshot")
if err != nil {
return fmt.Errorf("Failed to process pre-restore-snapshot hooks: %v", err)
}
Expand Down Expand Up @@ -1330,7 +1338,7 @@ func (app *DdevApp) RestoreSnapshot(snapshotName string) error {
util.CheckErr(err)

util.Success("Restored database snapshot: %s", hostSnapshotDir)
err = app.ProcessHooks("post-restore-snapshot")
_, _, err = app.ProcessHooks("post-restore-snapshot")
if err != nil {
return fmt.Errorf("Failed to process post-restore-snapshot hooks: %v", err)
}
Expand All @@ -1342,7 +1350,7 @@ func (app *DdevApp) Stop(removeData bool, createSnapshot bool) error {
app.DockerEnv()
var err error

err = app.ProcessHooks("pre-stop")
_, _, err = app.ProcessHooks("pre-stop")
if err != nil {
return fmt.Errorf("Failed to process pre-stop hooks: %v", err)
}
Expand Down Expand Up @@ -1394,7 +1402,7 @@ func (app *DdevApp) Stop(removeData bool, createSnapshot bool) error {
util.Success("Project data/database removed from docker volume for project %s", app.Name)
}

err = app.ProcessHooks("post-stop")
_, _, err = app.ProcessHooks("post-stop")
if err != nil {
return fmt.Errorf("Failed to process post-stop hooks: %v", err)
}
Expand Down
26 changes: 14 additions & 12 deletions pkg/ddevapp/ddevapp_test.go
Expand Up @@ -1533,21 +1533,23 @@ func TestProcessHooks(t *testing.T) {
},
}

stdout := util.CaptureUserOut()
err = app.ProcessHooks("hook-test")
goStdout := util.CaptureUserOut()

stdout, _, err := app.ProcessHooks("hook-test")
assert.NoError(err)

// Ignore color in output, can be different in different OS's
out := vtclean.Clean(stdout(), false)

assert.Contains(out, "Executing hook-test hook")
assert.Contains(out, "Exec command 'ls /usr/local/bin/composer' in container/service 'web'")
assert.Contains(out, "Exec command 'echo something' on the host")
assert.Contains(out, "Exec command 'echo MYSQL_USER=${MYSQL_USER}' in container/service 'db'")
assert.Contains(out, "MYSQL_USER=db")
assert.Contains(out, "Exec command 'echo TestProcessHooks > /var/www/html/TestProcessHooks${DDEV_ROUTER_HTTPS_PORT}.txt' in container/service 'web'")
assert.Contains(out, "Exec command 'touch /var/tmp/TestProcessHooks && touch /var/www/html/touch_works_after_and.txt' in container/service 'web',")
assert.Contains(out, "Twig, the flexible, fast, and secure template")
stdout = vtclean.Clean(stdout, false)
goStdoutStr := vtclean.Clean(goStdout(), false)

assert.Contains(goStdoutStr, "Executing hook-test hook")
assert.Contains(goStdoutStr, "Exec command 'ls /usr/local/bin/composer' in container/service 'web'")
assert.Contains(goStdoutStr, "Exec command 'echo something' on the host")
assert.Contains(goStdoutStr, "Exec command 'echo MYSQL_USER=${MYSQL_USER}' in container/service 'db'")
assert.Contains(stdout, "MYSQL_USER=db")
assert.Contains(goStdoutStr, "Exec command 'echo TestProcessHooks > /var/www/html/TestProcessHooks${DDEV_ROUTER_HTTPS_PORT}.txt' in container/service 'web'")
assert.Contains(goStdoutStr, "Exec command 'touch /var/tmp/TestProcessHooks && touch /var/www/html/touch_works_after_and.txt' in container/service 'web',")
assert.Contains(stdout, "Twig, the flexible, fast, and secure template")
assert.FileExists(filepath.Join(app.AppRoot, fmt.Sprintf("TestProcessHooks%s.txt", app.RouterHTTPSPort)))
assert.FileExists(filepath.Join(app.AppRoot, "touch_works_after_and.txt"))

Expand Down
14 changes: 6 additions & 8 deletions pkg/ddevapp/task.go
@@ -1,7 +1,6 @@
package ddevapp

import (
"bytes"
"fmt"
"os"
"os/exec"
Expand Down Expand Up @@ -42,7 +41,6 @@ func (c ExecTask) Execute() (string, string, error) {
stdout, stderr, err := c.app.Exec(&ExecOpts{
Service: c.service,
Cmd: c.exec,
Tty: true,
})

return stdout, stderr, err
Expand Down Expand Up @@ -70,14 +68,14 @@ func (c ExecHostTask) Execute() (string, string, error) {

execAry := strings.Split(c.exec, " ")

cmd := exec.Command(execAry[0], execAry[1:]...)
var stdout, stderr bytes.Buffer
cmd.Stderr = &stderr
cmd.Stdout = &stdout
err = cmd.Run()
stderr := []byte{}
stdout, err := exec.Command(execAry[0], execAry[1:]...).Output()
if exitErr, ok := err.(*exec.ExitError); ok {
stderr = exitErr.Stderr
}

_ = os.Chdir(cwd)
return stdout.String(), stderr.String(), err
return string(stdout), string(stderr), err
}

// Execute (ComposerTask) runs a composer command in the web container
Expand Down

0 comments on commit e626744

Please sign in to comment.