1
1
package exec
2
2
3
3
import (
4
+ "io"
5
+ "os"
4
6
"os/exec"
5
7
"strings"
6
8
9
+ logutil "github.com/docker/infrakit/pkg/log"
7
10
"github.com/docker/infrakit/pkg/template"
8
- log "github.com/golang/glog"
9
11
)
10
12
13
+ var log = logutil .New ("module" , "util/exec" )
14
+
11
15
// Command is the template which is rendered before it's executed
12
16
type Command string
13
17
@@ -26,35 +30,50 @@ func (c Command) Run(args ...string) error {
26
30
return c .builder ().Run (args ... )
27
31
}
28
32
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
+
29
42
// WithOptions adds the template options
30
43
func (c Command ) WithOptions (options template.Options ) * Builder {
31
- b := c .builder ()
32
- b .options = options
33
- return b
44
+ return c .builder ().WithOptions (options )
34
45
}
35
46
36
47
// WithFunc adds a function that can be used in the template
37
48
func (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 )
41
50
}
42
51
43
52
// WithContext sets the context for the template
44
53
func (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 ()
48
65
}
49
66
50
67
// Builder collects options until it's run
51
68
type 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
58
77
}
59
78
60
79
func (c Command ) builder () * Builder {
@@ -64,6 +83,18 @@ func (c Command) builder() *Builder {
64
83
}
65
84
}
66
85
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
+
67
98
// WithOptions adds the template options
68
99
func (b * Builder ) WithOptions (options template.Options ) * Builder {
69
100
b .options = options
@@ -100,6 +131,104 @@ func (b *Builder) Start(args ...string) error {
100
131
return run .Start ()
101
132
}
102
133
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
+
103
232
// Run does a Cmd.Run on the command
104
233
func (b * Builder ) Run (args ... string ) error {
105
234
run , err := b .exec (args ... )
@@ -143,7 +272,11 @@ func (b *Builder) exec(args ...string) (*exec.Cmd, error) {
143
272
if err != nil {
144
273
return nil , err
145
274
}
146
- log .V ( 50 ). Infoln ( "exec: " , command )
275
+ log .Debug ( "exec" , "command " , command )
147
276
b .cmd = exec .Command (command [0 ], command [1 :]... )
277
+ if b .inheritEnvs {
278
+ b .cmd .Env = append (os .Environ (), b .envs ... )
279
+ }
280
+
148
281
return b .cmd , nil
149
282
}
0 commit comments