Merge lp:~axwalk/juju-core/lp1279710-cloudinitoutput-on-error into lp:~go-bot/juju-core/trunk

Proposed by Andrew Wilkins
Status: Merged
Approved by: Andrew Wilkins
Approved revision: no longer in the source branch.
Merged at revision: 2427
Proposed branch: lp:~axwalk/juju-core/lp1279710-cloudinitoutput-on-error
Merge into: lp:~go-bot/juju-core/trunk
Diff against target: 377 lines (+179/-29)
9 files modified
environs/manual/provisioner.go (+20/-7)
environs/manual/provisioner_test.go (+32/-0)
provider/common/bootstrap.go (+7/-1)
provider/local/environ.go (+3/-1)
state/apiserver/client/client.go (+2/-12)
state/apiserver/client/client_test.go (+2/-8)
utils/shell/package_test.go (+14/-0)
utils/shell/script.go (+28/-0)
utils/shell/script_test.go (+71/-0)
To merge this branch: bzr merge lp:~axwalk/juju-core/lp1279710-cloudinitoutput-on-error
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+211016@code.launchpad.net

Commit message

Dump cloud-init-output.log on failed bootstrap

Currently if bootstrap (or manual provisioning)
fails, we don't get a useful error message, just
"exit status 1" (because the SSH script failed).
This change is to dump the contents of the
cloud-init-output.log file to stderr when the SSH
script fails.

Fixes lp:1279710

https://codereview.appspot.com/75990043/

Description of the change

Dump cloud-init-output.log on failed bootstrap

Currently if bootstrap (or manual provisioning)
fails, we don't get a useful error message, just
"exit status 1" (because the SSH script failed).
This change is to dump the contents of the
cloud-init-output.log file to stderr when the SSH
script fails.

Fixes lp:1279710

https://codereview.appspot.com/75990043/

To post a comment you must log in.
Revision history for this message
Andrew Wilkins (axwalk) wrote :

Reviewers: mp+211016_code.launchpad.net,

Message:
Please take a look.

Description:
Dump cloud-init-output.log on failed bootstrap

Currently if bootstrap (or manual provisioning)
fails, we don't get a useful error message, just
"exit status 1" (because the SSH script failed).
This change is to dump the contents of the
cloud-init-output.log file to stderr when the SSH
script fails.

Fixes lp:1279710

https://code.launchpad.net/~axwalk/juju-core/lp1279710-cloudinitoutput-on-error/+merge/211016

(do not edit description out of merge proposal)

Please review this at https://codereview.appspot.com/75990043/

Affected files (+76, -28 lines):
   A [revision details]
   M environs/cloudinit/cloudinit.go
   M environs/manual/provisioner.go
   M environs/manual/provisioner_test.go
   M provider/common/bootstrap.go
   M provider/local/environ.go
   M state/apiserver/client/client.go
   M state/apiserver/client/client_test.go

Revision history for this message
Roger Peppe (rogpeppe) wrote :

LGTM assuming you've tested it live, with a few superficial suggestions
below.

https://codereview.appspot.com/75990043/diff/1/environs/cloudinit/cloudinit.go
File environs/cloudinit/cloudinit.go (right):

https://codereview.appspot.com/75990043/diff/1/environs/cloudinit/cloudinit.go#newcode409
environs/cloudinit/cloudinit.go:409: func DumpLogOnError(mcfg
*MachineConfig) string {
This isn't really that deeply connected to MachineConfig,
or environs, and it's perhaps reasonable for the callers to know about
the cloudinit file, so perhaps this might be better in
utils, say, as:

// DumpFileOnErrorScript returns a script that
// prints the contents of the given file to
// standard error if any commands following it exit with an error.
// The returned script is newline terminated.
func DumpFileOnErrorScript(filename string) string

then the callers can use
utils.DumpFileOnErrorScript(mcfg.CloudInitOutputLog)

https://codereview.appspot.com/75990043/diff/1/environs/cloudinit/cloudinit.go#newcode418
environs/cloudinit/cloudinit.go:418: trap "dump_cloudinit_log" EXIT
i didn't know that EXIT works rather than 0, but as both dash and bash
seem to support it, i guess it's ok.

The quotes are unnecessary here BTW.

https://codereview.appspot.com/75990043/diff/1/environs/manual/provisioner.go
File environs/manual/provisioner.go (right):

https://codereview.appspot.com/75990043/diff/1/environs/manual/provisioner.go#newcode319
environs/manual/provisioner.go:319: script = fmt.Sprintf("rm -f %s\n",
utils.ShQuote(mcfg.CloudInitOutputLog)) + script
rather than doing thing backwards, i'd be tempted to build the script up
in order:

var buf bytes.Buffer
// Always remove the cloud-init-output.log file first, if it exists.
fmt.Fprintf(&buf, "rm -f %s\n", ...)
buf.WriteString(cloudinit.DumpLogOnError(mcfg))
configScript, err := sshinit.ConfigureScript(cloudcfg)
...
buf.WriteString(configScript)
return buf.String(), nil

https://codereview.appspot.com/75990043/

Revision history for this message
Andrew Wilkins (axwalk) wrote :

Please take a look.

https://codereview.appspot.com/75990043/diff/1/environs/cloudinit/cloudinit.go
File environs/cloudinit/cloudinit.go (right):

https://codereview.appspot.com/75990043/diff/1/environs/cloudinit/cloudinit.go#newcode409
environs/cloudinit/cloudinit.go:409: func DumpLogOnError(mcfg
*MachineConfig) string {
On 2014/03/14 12:11:39, rog wrote:
> This isn't really that deeply connected to MachineConfig,
> or environs, and it's perhaps reasonable for the callers to know about
> the cloudinit file, so perhaps this might be better in
> utils, say, as:

> // DumpFileOnErrorScript returns a script that
> // prints the contents of the given file to
> // standard error if any commands following it exit with an error.
> // The returned script is newline terminated.
> func DumpFileOnErrorScript(filename string) string

> then the callers can use
utils.DumpFileOnErrorScript(mcfg.CloudInitOutputLog)

Done, though I'm starting a new package: utils/shell. We can move
ShQuote and other shell related things in there over time.

https://codereview.appspot.com/75990043/diff/1/environs/cloudinit/cloudinit.go#newcode418
environs/cloudinit/cloudinit.go:418: trap "dump_cloudinit_log" EXIT
On 2014/03/14 12:11:39, rog wrote:
> i didn't know that EXIT works rather than 0, but as both dash and bash
seem to
> support it, i guess it's ok.

> The quotes are unnecessary here BTW.

Thanks, removed

https://codereview.appspot.com/75990043/diff/1/environs/manual/provisioner.go
File environs/manual/provisioner.go (right):

https://codereview.appspot.com/75990043/diff/1/environs/manual/provisioner.go#newcode319
environs/manual/provisioner.go:319: script = fmt.Sprintf("rm -f %s\n",
utils.ShQuote(mcfg.CloudInitOutputLog)) + script
On 2014/03/14 12:11:39, rog wrote:
> rather than doing thing backwards, i'd be tempted to build the script
up in
> order:

> var buf bytes.Buffer
> // Always remove the cloud-init-output.log file first, if it exists.
> fmt.Fprintf(&buf, "rm -f %s\n", ...)
> buf.WriteString(cloudinit.DumpLogOnError(mcfg))
> configScript, err := sshinit.ConfigureScript(cloudcfg)
> ...
> buf.WriteString(configScript)
> return buf.String(), nil

Done.

https://codereview.appspot.com/75990043/

Revision history for this message
Roger Peppe (rogpeppe) wrote :

On 2014/03/17 03:21:31, axw wrote:
> Please take a look.

https://codereview.appspot.com/75990043/diff/1/environs/cloudinit/cloudinit.go
> File environs/cloudinit/cloudinit.go (right):

https://codereview.appspot.com/75990043/diff/1/environs/cloudinit/cloudinit.go#newcode409
> environs/cloudinit/cloudinit.go:409: func DumpLogOnError(mcfg
*MachineConfig)
> string {
> On 2014/03/14 12:11:39, rog wrote:
> > This isn't really that deeply connected to MachineConfig,
> > or environs, and it's perhaps reasonable for the callers to know
about
> > the cloudinit file, so perhaps this might be better in
> > utils, say, as:
> >
> > // DumpFileOnErrorScript returns a script that
> > // prints the contents of the given file to
> > // standard error if any commands following it exit with an error.
> > // The returned script is newline terminated.
> > func DumpFileOnErrorScript(filename string) string
> >
> > then the callers can use
utils.DumpFileOnErrorScript(mcfg.CloudInitOutputLog)

> Done, though I'm starting a new package: utils/shell. We can move
ShQuote and
> other shell related things in there over time.

https://codereview.appspot.com/75990043/diff/1/environs/cloudinit/cloudinit.go#newcode418
> environs/cloudinit/cloudinit.go:418: trap "dump_cloudinit_log" EXIT
> On 2014/03/14 12:11:39, rog wrote:
> > i didn't know that EXIT works rather than 0, but as both dash and
bash seem to
> > support it, i guess it's ok.
> >
> > The quotes are unnecessary here BTW.

> Thanks, removed

https://codereview.appspot.com/75990043/diff/1/environs/manual/provisioner.go
> File environs/manual/provisioner.go (right):

https://codereview.appspot.com/75990043/diff/1/environs/manual/provisioner.go#newcode319
> environs/manual/provisioner.go:319: script = fmt.Sprintf("rm -f %s\n",
> utils.ShQuote(mcfg.CloudInitOutputLog)) + script
> On 2014/03/14 12:11:39, rog wrote:
> > rather than doing thing backwards, i'd be tempted to build the
script up in
> > order:
> >
> > var buf bytes.Buffer
> > // Always remove the cloud-init-output.log file first, if it exists.
> > fmt.Fprintf(&buf, "rm -f %s\n", ...)
> > buf.WriteString(cloudinit.DumpLogOnError(mcfg))
> > configScript, err := sshinit.ConfigureScript(cloudcfg)
> > ...
> > buf.WriteString(configScript)
> > return buf.String(), nil

> Done.

LGTM

https://codereview.appspot.com/75990043/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'environs/manual/provisioner.go'
2--- environs/manual/provisioner.go 2014-03-05 19:41:34 +0000
3+++ environs/manual/provisioner.go 2014-03-17 08:26:37 +0000
4@@ -4,6 +4,7 @@
5 package manual
6
7 import (
8+ "bytes"
9 "errors"
10 "fmt"
11 "io"
12@@ -24,6 +25,7 @@
13 "launchpad.net/juju-core/state/statecmd"
14 "launchpad.net/juju-core/tools"
15 "launchpad.net/juju-core/utils"
16+ "launchpad.net/juju-core/utils/shell"
17 )
18
19 const manualInstancePrefix = "manual:"
20@@ -143,7 +145,7 @@
21 } else {
22 mcfg, err := statecmd.MachineConfig(stateConn.State, machineId, machineParams.Nonce, args.DataDir)
23 if err == nil {
24- provisioningScript, err = generateProvisioningScript(mcfg)
25+ provisioningScript, err = ProvisioningScript(mcfg)
26 }
27 if err != nil {
28 return "", err
29@@ -199,9 +201,6 @@
30 if err != nil {
31 return "", err
32 }
33- //if p.Series == "" {
34- // p.Series = defaultSeries
35- //}
36 template := state.MachineTemplate{
37 Series: machineParams.Series,
38 Constraints: machineParams.Constraints,
39@@ -294,14 +293,17 @@
40 }
41
42 var provisionMachineAgent = func(host string, mcfg *cloudinit.MachineConfig, progressWriter io.Writer) error {
43- script, err := generateProvisioningScript(mcfg)
44+ script, err := ProvisioningScript(mcfg)
45 if err != nil {
46 return err
47 }
48 return runProvisionScript(script, host, progressWriter)
49 }
50
51-func generateProvisioningScript(mcfg *cloudinit.MachineConfig) (string, error) {
52+// ProvisioningScript generates a bash script that can be
53+// executed on a remote host to carry out the cloud-init
54+// configuration.
55+func ProvisioningScript(mcfg *cloudinit.MachineConfig) (string, error) {
56 cloudcfg := coreCloudinit.New()
57 if err := cloudinit.ConfigureJuju(mcfg, cloudcfg); err != nil {
58 return "", err
59@@ -309,7 +311,18 @@
60 // Explicitly disabling apt_upgrade so as not to trample
61 // the target machine's existing configuration.
62 cloudcfg.SetAptUpgrade(false)
63- return sshinit.ConfigureScript(cloudcfg)
64+ configScript, err := sshinit.ConfigureScript(cloudcfg)
65+ if err != nil {
66+ return "", err
67+ }
68+
69+ var buf bytes.Buffer
70+ // Always remove the cloud-init-output.log file first, if it exists.
71+ fmt.Fprintf(&buf, "rm -f %s\n", utils.ShQuote(mcfg.CloudInitOutputLog))
72+ // If something goes wrong, dump cloud-init-output.log to stderr.
73+ buf.WriteString(shell.DumpFileOnErrorScript(mcfg.CloudInitOutputLog))
74+ buf.WriteString(configScript)
75+ return buf.String(), nil
76 }
77
78 func runProvisionScript(script, host string, progressWriter io.Writer) error {
79
80=== modified file 'environs/manual/provisioner_test.go'
81--- environs/manual/provisioner_test.go 2014-03-13 07:54:56 +0000
82+++ environs/manual/provisioner_test.go 2014-03-17 08:26:37 +0000
83@@ -10,6 +10,9 @@
84 jc "github.com/juju/testing/checkers"
85 gc "launchpad.net/gocheck"
86
87+ coreCloudinit "launchpad.net/juju-core/cloudinit"
88+ "launchpad.net/juju-core/cloudinit/sshinit"
89+ "launchpad.net/juju-core/environs/cloudinit"
90 "launchpad.net/juju-core/environs/manual"
91 envtesting "launchpad.net/juju-core/environs/testing"
92 "launchpad.net/juju-core/instance"
93@@ -17,6 +20,7 @@
94 "launchpad.net/juju-core/state"
95 "launchpad.net/juju-core/state/api/params"
96 "launchpad.net/juju-core/state/statecmd"
97+ "launchpad.net/juju-core/utils/shell"
98 "launchpad.net/juju-core/version"
99 )
100
101@@ -131,3 +135,31 @@
102 c.Check(mcfg.APIInfo.Addrs, gc.DeepEquals, apiInfo.Addrs)
103 c.Check(mcfg.StateInfo.Addrs, gc.DeepEquals, stateInfo.Addrs)
104 }
105+
106+func (s *provisionerSuite) TestProvisioningScript(c *gc.C) {
107+ const series = "precise"
108+ const arch = "amd64"
109+ defer fakeSSH{
110+ Series: series,
111+ Arch: arch,
112+ InitUbuntuUser: true,
113+ }.install(c).Restore()
114+ machineId, err := manual.ProvisionMachine(s.getArgs(c))
115+ c.Assert(err, gc.IsNil)
116+
117+ mcfg, err := statecmd.MachineConfig(s.State, machineId, state.BootstrapNonce, "/var/lib/juju")
118+ c.Assert(err, gc.IsNil)
119+ script, err := manual.ProvisioningScript(mcfg)
120+ c.Assert(err, gc.IsNil)
121+
122+ cloudcfg := coreCloudinit.New()
123+ err = cloudinit.ConfigureJuju(mcfg, cloudcfg)
124+ c.Assert(err, gc.IsNil)
125+ cloudcfg.SetAptUpgrade(false)
126+ sshinitScript, err := sshinit.ConfigureScript(cloudcfg)
127+ c.Assert(err, gc.IsNil)
128+
129+ removeLogFile := "rm -f '/var/log/cloud-init-output.log'\n"
130+ expectedScript := removeLogFile + shell.DumpFileOnErrorScript("/var/log/cloud-init-output.log") + sshinitScript
131+ c.Assert(script, gc.Equals, expectedScript)
132+}
133
134=== modified file 'provider/common/bootstrap.go'
135--- provider/common/bootstrap.go 2014-03-13 05:09:14 +0000
136+++ provider/common/bootstrap.go 2014-03-17 08:26:37 +0000
137@@ -24,6 +24,7 @@
138 coretools "launchpad.net/juju-core/tools"
139 "launchpad.net/juju-core/utils"
140 "launchpad.net/juju-core/utils/parallel"
141+ "launchpad.net/juju-core/utils/shell"
142 "launchpad.net/juju-core/utils/ssh"
143 )
144
145@@ -200,7 +201,12 @@
146 if err := cloudinit.ConfigureJuju(machineConfig, cloudcfg); err != nil {
147 return err
148 }
149- return sshinit.Configure(sshinit.ConfigureParams{
150+ configScript, err := sshinit.ConfigureScript(cloudcfg)
151+ if err != nil {
152+ return err
153+ }
154+ script := shell.DumpFileOnErrorScript(machineConfig.CloudInitOutputLog) + configScript
155+ return sshinit.RunConfigureScript(script, sshinit.ConfigureParams{
156 Host: "ubuntu@" + addr,
157 Client: client,
158 Config: cloudcfg,
159
160=== modified file 'provider/local/environ.go'
161--- provider/local/environ.go 2014-03-14 02:19:35 +0000
162+++ provider/local/environ.go 2014-03-17 08:26:37 +0000
163@@ -38,6 +38,7 @@
164 "launchpad.net/juju-core/state"
165 "launchpad.net/juju-core/state/api"
166 "launchpad.net/juju-core/state/api/params"
167+ "launchpad.net/juju-core/utils/shell"
168 "launchpad.net/juju-core/version"
169 "launchpad.net/juju-core/worker/terminationworker"
170 )
171@@ -177,10 +178,11 @@
172 //
173 // mcfg is supplied for testing purposes.
174 var finishBootstrap = func(mcfg *cloudinit.MachineConfig, cloudcfg *coreCloudinit.Config, ctx environs.BootstrapContext) error {
175- script, err := sshinit.ConfigureScript(cloudcfg)
176+ configScript, err := sshinit.ConfigureScript(cloudcfg)
177 if err != nil {
178 return nil
179 }
180+ script := shell.DumpFileOnErrorScript(mcfg.CloudInitOutputLog) + configScript
181 cmd := exec.Command("sudo", "/bin/bash", "-s")
182 cmd.Stdin = strings.NewReader(script)
183 cmd.Stdout = ctx.GetStdout()
184
185=== modified file 'state/apiserver/client/client.go'
186--- state/apiserver/client/client.go 2014-03-11 03:00:49 +0000
187+++ state/apiserver/client/client.go 2014-03-17 08:26:37 +0000
188@@ -13,11 +13,9 @@
189 "github.com/juju/loggo"
190
191 "launchpad.net/juju-core/charm"
192- coreCloudinit "launchpad.net/juju-core/cloudinit"
193- "launchpad.net/juju-core/cloudinit/sshinit"
194 "launchpad.net/juju-core/environs"
195- "launchpad.net/juju-core/environs/cloudinit"
196 "launchpad.net/juju-core/environs/config"
197+ "launchpad.net/juju-core/environs/manual"
198 envtools "launchpad.net/juju-core/environs/tools"
199 "launchpad.net/juju-core/errors"
200 "launchpad.net/juju-core/instance"
201@@ -611,15 +609,7 @@
202 return result, err
203 }
204 mcfg.DisablePackageCommands = args.DisablePackageCommands
205- cloudcfg := coreCloudinit.New()
206- if err := cloudinit.ConfigureJuju(mcfg, cloudcfg); err != nil {
207- return result, err
208- }
209- // ProvisioningScript is run on an existing machine;
210- // we explicitly disable apt_upgrade so as not to
211- // trample the machine's existing configuration.
212- cloudcfg.SetAptUpgrade(false)
213- result.Script, err = sshinit.ConfigureScript(cloudcfg)
214+ result.Script, err = manual.ProvisioningScript(mcfg)
215 return result, err
216 }
217
218
219=== modified file 'state/apiserver/client/client_test.go'
220--- state/apiserver/client/client_test.go 2014-03-13 07:54:56 +0000
221+++ state/apiserver/client/client_test.go 2014-03-17 08:26:37 +0000
222@@ -15,12 +15,10 @@
223 gc "launchpad.net/gocheck"
224
225 "launchpad.net/juju-core/charm"
226- coreCloudinit "launchpad.net/juju-core/cloudinit"
227- "launchpad.net/juju-core/cloudinit/sshinit"
228 "launchpad.net/juju-core/constraints"
229 "launchpad.net/juju-core/environs"
230- "launchpad.net/juju-core/environs/cloudinit"
231 "launchpad.net/juju-core/environs/config"
232+ "launchpad.net/juju-core/environs/manual"
233 envstorage "launchpad.net/juju-core/environs/storage"
234 ttesting "launchpad.net/juju-core/environs/tools/testing"
235 "launchpad.net/juju-core/errors"
236@@ -1695,11 +1693,7 @@
237 c.Assert(err, gc.IsNil)
238 mcfg, err := statecmd.MachineConfig(s.State, machineId, apiParams.Nonce, "")
239 c.Assert(err, gc.IsNil)
240- cloudcfg := coreCloudinit.New()
241- err = cloudinit.ConfigureJuju(mcfg, cloudcfg)
242- c.Assert(err, gc.IsNil)
243- cloudcfg.SetAptUpgrade(false)
244- sshinitScript, err := sshinit.ConfigureScript(cloudcfg)
245+ sshinitScript, err := manual.ProvisioningScript(mcfg)
246 c.Assert(err, gc.IsNil)
247 // ProvisioningScript internally calls MachineConfig,
248 // which allocates a new, random password. Everything
249
250=== added directory 'utils/shell'
251=== added file 'utils/shell/package_test.go'
252--- utils/shell/package_test.go 1970-01-01 00:00:00 +0000
253+++ utils/shell/package_test.go 2014-03-17 08:26:37 +0000
254@@ -0,0 +1,14 @@
255+// Copyright 2014 Canonical Ltd.
256+// Licensed under the AGPLv3, see LICENCE file for details.
257+
258+package shell_test
259+
260+import (
261+ "testing"
262+
263+ gc "launchpad.net/gocheck"
264+)
265+
266+func Test(t *testing.T) {
267+ gc.TestingT(t)
268+}
269
270=== added file 'utils/shell/script.go'
271--- utils/shell/script.go 1970-01-01 00:00:00 +0000
272+++ utils/shell/script.go 2014-03-17 08:26:37 +0000
273@@ -0,0 +1,28 @@
274+// Copyright 2014 Canonical Ltd.
275+// Licensed under the AGPLv3, see LICENCE file for details.
276+
277+package shell
278+
279+import (
280+ "fmt"
281+
282+ "launchpad.net/juju-core/utils"
283+)
284+
285+// DumpFileOnErrorScript returns a bash script that
286+// may be used to dump the contents of the specified
287+// file to stderr when the shell exits with an error.
288+func DumpFileOnErrorScript(filename string) string {
289+ script := `
290+dump_file() {
291+ code=$?
292+ if [ $code -ne 0 -a -e %s ]; then
293+ cat %s >&2
294+ fi
295+ exit $code
296+}
297+trap dump_file EXIT
298+`[1:]
299+ filename = utils.ShQuote(filename)
300+ return fmt.Sprintf(script, filename, filename)
301+}
302
303=== added file 'utils/shell/script_test.go'
304--- utils/shell/script_test.go 1970-01-01 00:00:00 +0000
305+++ utils/shell/script_test.go 2014-03-17 08:26:37 +0000
306@@ -0,0 +1,71 @@
307+// Copyright 2014 Canonical Ltd.
308+// Licensed under the AGPLv3, see LICENCE file for details.
309+
310+package shell_test
311+
312+import (
313+ "bytes"
314+ "io/ioutil"
315+ "os"
316+ "os/exec"
317+ "path/filepath"
318+ "strings"
319+
320+ gc "launchpad.net/gocheck"
321+
322+ "launchpad.net/juju-core/testing/testbase"
323+ "launchpad.net/juju-core/utils/shell"
324+)
325+
326+type scriptSuite struct {
327+ testbase.LoggingSuite
328+}
329+
330+var _ = gc.Suite(&scriptSuite{})
331+
332+func (*scriptSuite) TestDumpFileOnErrorScriptOutput(c *gc.C) {
333+ script := shell.DumpFileOnErrorScript("a b c")
334+ c.Assert(script, gc.Equals, `
335+dump_file() {
336+ code=$?
337+ if [ $code -ne 0 -a -e 'a b c' ]; then
338+ cat 'a b c' >&2
339+ fi
340+ exit $code
341+}
342+trap dump_file EXIT
343+`[1:])
344+}
345+
346+func (*scriptSuite) TestDumpFileOnErrorScript(c *gc.C) {
347+ tempdir := c.MkDir()
348+ filename := filepath.Join(tempdir, "log.txt")
349+ err := ioutil.WriteFile(filename, []byte("abc"), 0644)
350+ c.Assert(err, gc.IsNil)
351+
352+ dumpScript := shell.DumpFileOnErrorScript(filename)
353+ c.Logf("%s", dumpScript)
354+ run := func(command string) (stdout, stderr string) {
355+ var stdoutBuf, stderrBuf bytes.Buffer
356+ cmd := exec.Command("/bin/bash", "-s")
357+ cmd.Stdin = strings.NewReader(dumpScript + command)
358+ cmd.Stdout = &stdoutBuf
359+ cmd.Stderr = &stderrBuf
360+ cmd.Run()
361+ return stdoutBuf.String(), stderrBuf.String()
362+ }
363+
364+ stdout, stderr := run("exit 0")
365+ c.Assert(stdout, gc.Equals, "")
366+ c.Assert(stderr, gc.Equals, "")
367+
368+ stdout, stderr = run("exit 1")
369+ c.Assert(stdout, gc.Equals, "")
370+ c.Assert(stderr, gc.Equals, "abc")
371+
372+ err = os.Remove(filename)
373+ c.Assert(err, gc.IsNil)
374+ stdout, stderr = run("exit 1")
375+ c.Assert(stdout, gc.Equals, "")
376+ c.Assert(stderr, gc.Equals, "")
377+}

Subscribers

People subscribed via source and target branches

to status/vote changes: