grim/convey
Clone
Summary
Browse
Changes
Graph
closing merged branch
replace-stage
2017-10-03, Gary Kramlich
2c96fdf950b8
closing merged branch
/*
* Convey
* Copyright 2016-2017 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
import
(
"bufio"
"bytes"
"os/exec"
"strings"
"sync"
"text/template"
"time"
"github.com/mattn/go-shellwords"
"bitbucket.org/rw_grim/convey/logging"
"github.com/aphistic/gomol"
)
var
(
lock
sync
.
Mutex
)
func
Run
(
name
,
cmdTemplate
string
,
params
map
[
string
]
interface
{},
timeout
time
.
Duration
)
error
{
var
(
logger
=
logging
.
NewAdapter
(
name
)
outCollector
=
newLogCollector
(
logger
,
gomol
.
LevelInfo
)
errCollector
=
newLogCollector
(
logger
,
gomol
.
LevelError
)
)
return
run
(
name
,
cmdTemplate
,
params
,
timeout
,
outCollector
,
errCollector
)
}
func
RunOutput
(
name
,
cmdTemplate
string
,
params
map
[
string
]
interface
{},
timeout
time
.
Duration
)
(
string
,
string
,
error
)
{
var
(
wg
=
&
sync
.
WaitGroup
{}
outCollector
=
newStringCollector
(
wg
)
errCollector
=
newStringCollector
(
wg
)
)
wg
.
Add
(
2
)
err
:=
run
(
name
,
cmdTemplate
,
params
,
timeout
,
outCollector
.
handler
,
errCollector
.
handler
)
wg
.
Wait
()
return
outCollector
.
output
,
errCollector
.
output
,
err
}
func
execTemplate
(
name
,
cmdTemplate
string
,
params
map
[
string
]
interface
{})
([]
string
,
error
)
{
// we use multiline comments to make the code readable, which leaves
// embedding newlines, so remove those.
cleanTemplate
:=
strings
.
Replace
(
cmdTemplate
,
"\n"
,
""
,
-
1
)
// go's template stuff is *NOT* thread safe, so we need to lock around it
lock
.
Lock
()
tmpl
,
err
:=
template
.
New
(
name
).
Parse
(
cleanTemplate
)
lock
.
Unlock
()
if
err
!=
nil
{
return
nil
,
err
}
// now execute the template
cmd
:=
new
(
bytes
.
Buffer
)
err
=
tmpl
.
Execute
(
cmd
,
params
)
if
err
!=
nil
{
return
nil
,
err
}
return
shellwords
.
Parse
(
cmd
.
String
())
}
func
run
(
name
,
cmdTemplate
string
,
params
map
[
string
]
interface
{},
timeout
time
.
Duration
,
outHandler
,
errHandler
collector
)
error
{
logger
:=
logging
.
NewAdapter
(
name
)
cmdv
,
err
:=
execTemplate
(
name
,
cmdTemplate
,
params
)
if
err
!=
nil
{
return
err
}
logger
.
Debugf
(
"running command \"%v\""
,
cmdv
)
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
cmd
.
Process
.
Kill
()
// log that the process timed out
logger
.
Errorf
(
"command %v timed out after %v"
,
cmdv
,
timeout
)
})
}
err
=
cmd
.
Wait
()
if
timer
!=
nil
{
timer
.
Stop
()
}
return
err
}