grim/convey

Bump the version for release
v0.14.0-alpha3
2018-02-20, Gary Kramlich
166a6d1979fa
Bump the version for release
// Convey
// Copyright 2016-2018 Gary Kramlich <grim@reaperworld.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// Package command provides utilities for running external commands.
package command
import (
"bufio"
"fmt"
"os/exec"
"sync"
"time"
"bitbucket.org/rw_grim/convey/logging"
"github.com/aphistic/gomol"
)
// Run runs the command specified in cmdTemplate which is rendered with the
// given params and logs stderr and stdout to a new log adapter.
func Run(name string, cmdv []string, timeout time.Duration) error {
var (
logger = logging.NewAdapter(name)
outCollector = newLogCollector(logger, gomol.LevelInfo)
errCollector = newLogCollector(logger, gomol.LevelError)
)
return run(name, cmdv, timeout, outCollector, errCollector)
}
// RunOutput works just like Run but returns stdout and stderr instead of
// logging it.
func RunOutput(name string, cmdv []string, timeout time.Duration) (string, string, error) {
var (
wg = &sync.WaitGroup{}
outCollector = newStringCollector(wg)
errCollector = newStringCollector(wg)
)
wg.Add(2)
err := run(name, cmdv, timeout, outCollector.handler, errCollector.handler)
wg.Wait()
return outCollector.output, errCollector.output, err
}
func run(name string, cmdv []string, timeout time.Duration, outHandler, errHandler collector) error {
logger := logging.NewAdapter(name)
if lErr := logger.Debugf("running command \"%v\"", cmdv); lErr != nil {
fmt.Printf("error reporting debug: %s\n", lErr)
}
cmd := exec.Command(cmdv[0], cmdv[1:]...)
// setup the stdout wrapper
outReader, err := cmd.StdoutPipe()
if err != nil {
return err
}
// setup the stderr wrapper
errReader, err := cmd.StderrPipe()
if err != nil {
return err
}
// Pass output to the handlers
go outHandler(bufio.NewScanner(outReader))
go errHandler(bufio.NewScanner(errReader))
// now run the command
err = cmd.Start()
if err != nil {
return err
}
var timer *time.Timer
if timeout != 0 {
timer = time.AfterFunc(timeout, func() {
// kill process, unblock the cmd.Wait() below
killErr := cmd.Process.Kill()
if killErr != nil {
fmt.Printf("error killing process: %s\n", killErr)
}
// log that the process timed out
if lErr := logger.Errorf("command %v timed out after %v", cmdv, timeout); lErr != nil {
fmt.Printf("error reporting error: %s\n", lErr)
}
})
}
err = cmd.Wait()
// if cmd.Wait() returns before the timeout, we'll have a timer stop
if timer != nil {
timer.Stop()
}
return err
}