11package exec
22
33import (
4+ "io"
5+ "os"
46 "os/exec"
57 "strings"
68
9+ logutil "github.com/docker/infrakit/pkg/log"
710 "github.com/docker/infrakit/pkg/template"
8- log "github.com/golang/glog"
911)
1012
13+ var log = logutil .New ("module" , "util/exec" )
14+
1115// Command is the template which is rendered before it's executed
1216type Command string
1317
@@ -26,35 +30,50 @@ func (c Command) Run(args ...string) error {
2630 return c .builder ().Run (args ... )
2731}
2832
33+ // String returns the interpolated version of the command
34+ func (c Command ) String (args ... string ) (string , error ) {
35+ p , err := c .builder ().generate (args ... )
36+ if err == nil {
37+ return strings .Join (p , " " ), nil
38+ }
39+ return string (c ), err
40+ }
41+
2942// WithOptions adds the template options
3043func (c Command ) WithOptions (options template.Options ) * Builder {
31- b := c .builder ()
32- b .options = options
33- return b
44+ return c .builder ().WithOptions (options )
3445}
3546
3647// WithFunc adds a function that can be used in the template
3748func (c Command ) WithFunc (name string , function interface {}) * Builder {
38- b := c .builder ()
39- b .funcs [name ] = function
40- return b
49+ return c .builder ().WithFunc (name , function )
4150}
4251
4352// WithContext sets the context for the template
4453func (c Command ) WithContext (context interface {}) * Builder {
45- b := c .builder ()
46- b .context = context
47- return b
54+ return c .builder ().WithContext (context )
55+ }
56+
57+ // InheritEnvs determines whether the process should inherit the envs of the parent
58+ func (c Command ) InheritEnvs (v bool ) * Builder {
59+ return c .builder ().InheritEnvs (v )
60+ }
61+
62+ // NewCommand creates an instance of the command builder to allow detailed configuration
63+ func NewCommand (s string ) * Builder {
64+ return Command (s ).builder ()
4865}
4966
5067// Builder collects options until it's run
5168type Builder struct {
52- command Command
53- options template.Options
54- funcs map [string ]interface {}
55- context interface {}
56- rendered string // rendered command string
57- cmd * exec.Cmd
69+ command Command
70+ options template.Options
71+ inheritEnvs bool
72+ envs []string
73+ funcs map [string ]interface {}
74+ context interface {}
75+ rendered string // rendered command string
76+ cmd * exec.Cmd
5877}
5978
6079func (c Command ) builder () * Builder {
@@ -64,6 +83,18 @@ func (c Command) builder() *Builder {
6483 }
6584}
6685
86+ // InheritEnvs determines whether the process should inherit the envs of the parent
87+ func (b * Builder ) InheritEnvs (v bool ) * Builder {
88+ b .inheritEnvs = v
89+ return b
90+ }
91+
92+ // WithEnvs adds environment variables for the exec, in format of key=value
93+ func (b * Builder ) WithEnvs (kv ... string ) * Builder {
94+ b .envs = append (b .envs , kv ... )
95+ return b
96+ }
97+
6798// WithOptions adds the template options
6899func (b * Builder ) WithOptions (options template.Options ) * Builder {
69100 b .options = options
@@ -100,6 +131,104 @@ func (b *Builder) Start(args ...string) error {
100131 return run .Start ()
101132}
102133
134+ // Step is something you do with the processes streams
135+ type Step func (stdin io.WriteCloser , stdout io.ReadCloser , stderr io.ReadCloser ) error
136+
137+ // Thenable is a fluent builder for chaining tasks
138+ type Thenable struct {
139+ steps []Step
140+ }
141+
142+ // Do creates a thenable
143+ func Do (f Step ) * Thenable {
144+ return & Thenable {
145+ steps : []Step {f },
146+ }
147+ }
148+
149+ // Then adds another step
150+ func (t * Thenable ) Then (then Step ) * Thenable {
151+ t .steps = append (t .steps , then )
152+ return t
153+ }
154+
155+ // Done returns the final function
156+ func (t * Thenable ) Done () Step {
157+ steps := t .steps
158+ return func (stdin io.WriteCloser , stdout , stderr io.ReadCloser ) error {
159+ for _ , step := range steps {
160+ if err := step (stdin , stdout , stderr ); err != nil {
161+ return err
162+ }
163+ }
164+ return nil
165+ }
166+ }
167+
168+ // SendInput is a convenience function for writing to the exec process's stdin. When the function completes, the
169+ // stdin is closed.
170+ func SendInput (f func (io.WriteCloser ) error ) Step {
171+ return func (stdin io.WriteCloser , stdout , stderr io.ReadCloser ) error {
172+ defer stdin .Close ()
173+ return f (stdin )
174+ }
175+ }
176+
177+ // RedirectStdout sends stdout to given writer
178+ func RedirectStdout (out io.Writer ) Step {
179+ return func (stdin io.WriteCloser , stdout , stderr io.ReadCloser ) error {
180+ _ , err := io .Copy (out , stdout )
181+ return err
182+ }
183+ }
184+
185+ // RedirectStderr sends stdout to given writer
186+ func RedirectStderr (out io.Writer ) Step {
187+ return func (stdin io.WriteCloser , stdout , stderr io.ReadCloser ) error {
188+ _ , err := io .Copy (out , stderr )
189+ return err
190+ }
191+ }
192+
193+ // MergeOutput combines the stdout and stderr into the given stream
194+ func MergeOutput (out io.Writer ) Step {
195+ return func (stdin io.WriteCloser , stdout , stderr io.ReadCloser ) error {
196+ _ , err := io .Copy (out , io .MultiReader (stdout , stderr ))
197+ return err
198+ }
199+ }
200+
201+ // StartWithStreams starts the the process and then calls the function which allows
202+ // the streams to be wired. Calling the provided function blocks.
203+ func (b * Builder ) StartWithStreams (f Step ,
204+ args ... string ) error {
205+
206+ _ , err := b .exec (args ... )
207+ if err != nil {
208+ return err
209+ }
210+
211+ pOut , err := b .cmd .StdoutPipe ()
212+ if err != nil {
213+ return err
214+ }
215+ pErr , err := b .cmd .StderrPipe ()
216+ if err != nil {
217+ return err
218+ }
219+ pIn , err := b .cmd .StdinPipe ()
220+ if err != nil {
221+ return err
222+ }
223+
224+ err = b .cmd .Start ()
225+ if err != nil {
226+ return err
227+ }
228+
229+ return f (pIn , pOut , pErr )
230+ }
231+
103232// Run does a Cmd.Run on the command
104233func (b * Builder ) Run (args ... string ) error {
105234 run , err := b .exec (args ... )
@@ -143,7 +272,11 @@ func (b *Builder) exec(args ...string) (*exec.Cmd, error) {
143272 if err != nil {
144273 return nil , err
145274 }
146- log .V ( 50 ). Infoln ( "exec: " , command )
275+ log .Debug ( "exec" , "command " , command )
147276 b .cmd = exec .Command (command [0 ], command [1 :]... )
277+ if b .inheritEnvs {
278+ b .cmd .Env = append (os .Environ (), b .envs ... )
279+ }
280+
148281 return b .cmd , nil
149282}
0 commit comments