Merge lp:~axwalk/juju-core/lp1296475-block-sigint-bootstrap 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: 2481
Proposed branch: lp:~axwalk/juju-core/lp1296475-block-sigint-bootstrap
Merge into: lp:~go-bot/juju-core/trunk
Diff against target: 442 lines (+201/-23)
13 files modified
cmd/juju/bootstrap.go (+14/-1)
cmd/juju/bootstrap_test.go (+8/-5)
cmd/juju/common.go (+1/-0)
environs/bootstrap/bootstrap_test.go (+6/-6)
environs/bootstrap/export_test.go (+8/-0)
environs/bootstrap/interruptiblestorage.go (+62/-0)
environs/bootstrap/interruptiblestorage_test.go (+74/-0)
environs/bootstrap/synctools.go (+20/-5)
environs/interface.go (+2/-0)
provider/common/bootstrap.go (+3/-3)
provider/dummy/environs.go (+1/-1)
provider/local/environ.go (+1/-1)
provider/manual/environ.go (+1/-1)
To merge this branch: bzr merge lp:~axwalk/juju-core/lp1296475-block-sigint-bootstrap
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+212558@code.launchpad.net

Commit message

Block SIGINT during bootstrap

This change is to catch SIGINT during bootstrap
for all providers. Also, if SIGINT is delivered
while tools are being uploaded, the upload will
be cancelled.

If the user Ctrl-C's while the local provider's
bootstrap script is being executed, it will be
interrupted and will return and error to juju.
Juju will then attempt to destroy the environment
and remove the .jenv file.

We will now also send some very basic feedback to
stderr when uploading tools.

Fixes lp:1296475

https://codereview.appspot.com/76160046/

Description of the change

Block SIGINT during bootstrap

This change is to catch SIGINT during bootstrap
for all providers. Also, if SIGINT is delivered
while tools are being uploaded, the upload will
be cancelled.

If the user Ctrl-C's while the local provider's
bootstrap script is being executed, it will be
interrupted and will return and error to juju.
Juju will then attempt to destroy the environment
and remove the .jenv file.

We will now also send some very basic feedback to
stderr when uploading tools.

Fixes lp:1296475

https://codereview.appspot.com/76160046/

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

Reviewers: mp+212558_code.launchpad.net,

Message:
Please take a look.

Description:
Block SIGINT during bootstrap

This change is to catch SIGINT during bootstrap
for all providers. Also, if SIGINT is delivered
while tools are being uploaded, the upload will
be cancelled.

If the user Ctrl-C's while the local provider's
bootstrap script is being executed, it will be
interrupted and will return and error to juju.
Juju will then attempt to destroy the environment
and remove the .jenv file.

We will now also send some very basic feedback to
stderr when uploading tools.

Fixes lp:1296475

https://code.launchpad.net/~axwalk/juju-core/lp1296475-block-sigint-bootstrap/+merge/212558

(do not edit description out of merge proposal)

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

Affected files (+192, -18 lines):
   A [revision details]
   M cmd/juju/bootstrap.go
   M cmd/juju/common.go
   M environs/bootstrap/bootstrap_test.go
   A environs/bootstrap/export_test.go
   A environs/bootstrap/interruptiblestorage.go
   A environs/bootstrap/interruptiblestorage_test.go
   M environs/bootstrap/synctools.go
   M provider/common/bootstrap.go
   M provider/dummy/environs.go
   M provider/local/environ.go
   M provider/manual/environ.go

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

https://codereview.appspot.com/76160046/diff/20001/cmd/juju/bootstrap.go
File cmd/juju/bootstrap.go (right):

https://codereview.appspot.com/76160046/diff/20001/cmd/juju/bootstrap.go#newcode112
cmd/juju/bootstrap.go:112: fmt.Fprintln(ctx.GetStderr(), "Interrupt
signalled: waiting for bootstrap to exit")
You could use the new awesome:
   ctx.Infof("Interrupt signalled: waiting for bootstrap to exit")

https://codereview.appspot.com/76160046/diff/20001/cmd/juju/common.go
File cmd/juju/common.go (right):

https://codereview.appspot.com/76160046/diff/20001/cmd/juju/common.go#newcode44
cmd/juju/common.go:44: fmt.Fprintf(ctx.GetStderr(), "%s failed,
destroying environment\n", action)
ctx.Infof("%s failed, destroying environment", action)

A new line is added to the end if missing.

https://codereview.appspot.com/76160046/diff/20001/environs/bootstrap/interruptiblestorage.go
File environs/bootstrap/interruptiblestorage.go (right):

https://codereview.appspot.com/76160046/diff/20001/environs/bootstrap/interruptiblestorage.go#newcode48
environs/bootstrap/interruptiblestorage.go:48: n, err = r.Reader.Read(p)
Is it expected that this will continue until finished even if
interrupted?

Is there a race condition here between the return 0, interruptedError
below and this assignment?

https://codereview.appspot.com/76160046/diff/20001/environs/bootstrap/synctools.go
File environs/bootstrap/synctools.go (right):

https://codereview.appspot.com/76160046/diff/20001/environs/bootstrap/synctools.go#newcode35
environs/bootstrap/synctools.go:35: logger.Infof("checking that upload
is possible")
we could change this now to use the Verbosef method (if we add it to the
BootstrapContext interface)

https://codereview.appspot.com/76160046/diff/20001/environs/bootstrap/synctools.go#newcode56
environs/bootstrap/synctools.go:56: fmt.Fprintln(ctx.GetStderr(),
"cancelling tools upload")
we should put Infof and Verbosef onto the bootstrap context I think.

https://codereview.appspot.com/76160046/

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

Please take a look.

https://codereview.appspot.com/76160046/diff/20001/cmd/juju/bootstrap.go
File cmd/juju/bootstrap.go (right):

https://codereview.appspot.com/76160046/diff/20001/cmd/juju/bootstrap.go#newcode112
cmd/juju/bootstrap.go:112: fmt.Fprintln(ctx.GetStderr(), "Interrupt
signalled: waiting for bootstrap to exit")
On 2014/03/26 02:41:47, thumper wrote:
> You could use the new awesome:
> ctx.Infof("Interrupt signalled: waiting for bootstrap to exit")

Done.

https://codereview.appspot.com/76160046/diff/20001/cmd/juju/common.go
File cmd/juju/common.go (right):

https://codereview.appspot.com/76160046/diff/20001/cmd/juju/common.go#newcode44
cmd/juju/common.go:44: fmt.Fprintf(ctx.GetStderr(), "%s failed,
destroying environment\n", action)
On 2014/03/26 02:41:47, thumper wrote:
> ctx.Infof("%s failed, destroying environment", action)

> A new line is added to the end if missing.

Done.

https://codereview.appspot.com/76160046/diff/20001/environs/bootstrap/interruptiblestorage.go
File environs/bootstrap/interruptiblestorage.go (right):

https://codereview.appspot.com/76160046/diff/20001/environs/bootstrap/interruptiblestorage.go#newcode48
environs/bootstrap/interruptiblestorage.go:48: n, err = r.Reader.Read(p)
On 2014/03/26 02:41:47, thumper wrote:
> Is it expected that this will continue until finished even if
interrupted?

> Is there a race condition here between the return 0, interruptedError
below and
> this assignment?

Yes, good call. Updated to use locals rather than named results.

https://codereview.appspot.com/76160046/diff/20001/environs/bootstrap/synctools.go
File environs/bootstrap/synctools.go (right):

https://codereview.appspot.com/76160046/diff/20001/environs/bootstrap/synctools.go#newcode35
environs/bootstrap/synctools.go:35: logger.Infof("checking that upload
is possible")
On 2014/03/26 02:41:47, thumper wrote:
> we could change this now to use the Verbosef method (if we add it to
the
> BootstrapContext interface)

I don't think this particular one should be logged to stderr, but I have
added Infof/Verbosef to BootstrapContext and changed the other ones
over.

https://codereview.appspot.com/76160046/diff/20001/environs/bootstrap/synctools.go#newcode56
environs/bootstrap/synctools.go:56: fmt.Fprintln(ctx.GetStderr(),
"cancelling tools upload")
On 2014/03/26 02:41:47, thumper wrote:
> we should put Infof and Verbosef onto the bootstrap context I think.

Done.

https://codereview.appspot.com/76160046/

Revision history for this message
Tim Penhey (thumper) wrote :
Revision history for this message
Wayne Witzel III (wwitzel3) wrote :

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'cmd/juju/bootstrap.go'
--- cmd/juju/bootstrap.go 2014-03-18 05:03:38 +0000
+++ cmd/juju/bootstrap.go 2014-03-26 03:32:09 +0000
@@ -100,6 +100,19 @@
100 if err := bootstrap.EnsureNotBootstrapped(environ); err != nil {100 if err := bootstrap.EnsureNotBootstrapped(environ); err != nil {
101 return err101 return err
102 }102 }
103
104 // Block interruption during bootstrap. Providers may also
105 // register for interrupt notification so they can exit early.
106 interrupted := make(chan os.Signal, 1)
107 defer close(interrupted)
108 ctx.InterruptNotify(interrupted)
109 defer ctx.StopInterruptNotify(interrupted)
110 go func() {
111 for _ = range interrupted {
112 ctx.Infof("Interrupt signalled: waiting for bootstrap to exit")
113 }
114 }()
115
103 // If --metadata-source is specified, override the default tools metadata source so116 // If --metadata-source is specified, override the default tools metadata source so
104 // SyncTools can use it, and also upload any image metadata.117 // SyncTools can use it, and also upload any image metadata.
105 if c.MetadataSource != "" {118 if c.MetadataSource != "" {
@@ -122,7 +135,7 @@
122 c.UploadTools = true135 c.UploadTools = true
123 }136 }
124 if c.UploadTools {137 if c.UploadTools {
125 err = bootstrap.UploadTools(environ, c.Constraints.Arch, true, c.Series...)138 err = bootstrap.UploadTools(ctx, environ, c.Constraints.Arch, true, c.Series...)
126 if err != nil {139 if err != nil {
127 return err140 return err
128 }141 }
129142
=== modified file 'cmd/juju/bootstrap_test.go'
--- cmd/juju/bootstrap_test.go 2014-03-24 20:08:45 +0000
+++ cmd/juju/bootstrap_test.go 2014-03-26 03:32:09 +0000
@@ -374,7 +374,9 @@
374 ctx2 := coretesting.Context(c)374 ctx2 := coretesting.Context(c)
375 code2 := cmd.Main(&BootstrapCommand{}, ctx2, nil)375 code2 := cmd.Main(&BootstrapCommand{}, ctx2, nil)
376 c.Check(code2, gc.Equals, 1)376 c.Check(code2, gc.Equals, 1)
377 c.Check(coretesting.Stderr(ctx2), gc.Equals, "error: environment is already bootstrapped\n")377 expectedErrText := "Bootstrap failed, destroying environment\n"
378 expectedErrText += "error: environment is already bootstrapped\n"
379 c.Check(coretesting.Stderr(ctx2), gc.Equals, expectedErrText)
378 c.Check(coretesting.Stdout(ctx2), gc.Equals, "")380 c.Check(coretesting.Stdout(ctx2), gc.Equals, "")
379}381}
380382
@@ -540,8 +542,8 @@
540 code := cmd.Main(&BootstrapCommand{}, context, nil)542 code := cmd.Main(&BootstrapCommand{}, context, nil)
541 c.Assert(code, gc.Equals, 1)543 c.Assert(code, gc.Equals, 1)
542 errText := context.Stderr.(*bytes.Buffer).String()544 errText := context.Stderr.(*bytes.Buffer).String()
543 errText = strings.Replace(errText, "\n", "", -1)545 expectedErrText := "Bootstrap failed, destroying environment\n"
544 expectedErrText := "error: cannot upload bootstrap tools: Juju cannot bootstrap because no tools are available for your environment.*"546 expectedErrText += "error: cannot upload bootstrap tools: Juju cannot bootstrap because no tools are available for your environment(.|\n)*"
545 c.Assert(errText, gc.Matches, expectedErrText)547 c.Assert(errText, gc.Matches, expectedErrText)
546}548}
547549
@@ -556,8 +558,9 @@
556 code := cmd.Main(&BootstrapCommand{}, context, nil)558 code := cmd.Main(&BootstrapCommand{}, context, nil)
557 c.Assert(code, gc.Equals, 1)559 c.Assert(code, gc.Equals, 1)
558 errText := context.Stderr.(*bytes.Buffer).String()560 errText := context.Stderr.(*bytes.Buffer).String()
559 errText = strings.Replace(errText, "\n", "", -1)561 expectedErrText := "uploading tools for series \\[precise raring\\]\n"
560 expectedErrText := "error: cannot upload bootstrap tools: an error"562 expectedErrText += "Bootstrap failed, destroying environment\n"
563 expectedErrText += "error: cannot upload bootstrap tools: an error\n"
561 c.Assert(errText, gc.Matches, expectedErrText)564 c.Assert(errText, gc.Matches, expectedErrText)
562}565}
563566
564567
=== modified file 'cmd/juju/common.go'
--- cmd/juju/common.go 2014-03-10 00:45:41 +0000
+++ cmd/juju/common.go 2014-03-26 03:32:09 +0000
@@ -39,6 +39,7 @@
39 }39 }
40 cleanup := func() {40 cleanup := func() {
41 if !existing {41 if !existing {
42 ctx.Infof("%s failed, destroying environment", action)
42 destroyPreparedEnviron(environ, store, resultErr, action)43 destroyPreparedEnviron(environ, store, resultErr, action)
43 }44 }
44 }45 }
4546
=== modified file 'environs/bootstrap/bootstrap_test.go'
--- environs/bootstrap/bootstrap_test.go 2014-03-19 22:02:00 +0000
+++ environs/bootstrap/bootstrap_test.go 2014-03-26 03:32:09 +0000
@@ -216,7 +216,7 @@
216 s.setDummyStorage(c, env)216 s.setDummyStorage(c, env)
217 envtesting.RemoveFakeTools(c, env.Storage())217 envtesting.RemoveFakeTools(c, env.Storage())
218 arch := "ppc64"218 arch := "ppc64"
219 _, err := bootstrap.EnsureToolsAvailability(env, env.Config().DefaultSeries(), &arch)219 _, err := bootstrap.EnsureToolsAvailability(coretesting.Context(c), env, env.Config().DefaultSeries(), &arch)
220 c.Assert(err, gc.NotNil)220 c.Assert(err, gc.NotNil)
221 stripped := strings.Replace(err.Error(), "\n", "", -1)221 stripped := strings.Replace(err.Error(), "\n", "", -1)
222 c.Assert(stripped,222 c.Assert(stripped,
@@ -232,7 +232,7 @@
232 env := newEnviron("foo", useDefaultKeys, nil)232 env := newEnviron("foo", useDefaultKeys, nil)
233 s.setDummyStorage(c, env)233 s.setDummyStorage(c, env)
234 envtesting.RemoveFakeTools(c, env.Storage())234 envtesting.RemoveFakeTools(c, env.Storage())
235 _, err := bootstrap.EnsureToolsAvailability(env, env.Config().DefaultSeries(), nil)235 _, err := bootstrap.EnsureToolsAvailability(coretesting.Context(c), env, env.Config().DefaultSeries(), nil)
236 c.Assert(err, gc.NotNil)236 c.Assert(err, gc.NotNil)
237 stripped := strings.Replace(err.Error(), "\n", "", -1)237 stripped := strings.Replace(err.Error(), "\n", "", -1)
238 c.Assert(stripped,238 c.Assert(stripped,
@@ -245,7 +245,7 @@
245 env := newEnviron("foo", useDefaultKeys, map[string]interface{}{"agent-version": "1.16.0"})245 env := newEnviron("foo", useDefaultKeys, map[string]interface{}{"agent-version": "1.16.0"})
246 s.setDummyStorage(c, env)246 s.setDummyStorage(c, env)
247 envtesting.RemoveFakeTools(c, env.Storage())247 envtesting.RemoveFakeTools(c, env.Storage())
248 _, err := bootstrap.EnsureToolsAvailability(env, env.Config().DefaultSeries(), nil)248 _, err := bootstrap.EnsureToolsAvailability(coretesting.Context(c), env, env.Config().DefaultSeries(), nil)
249 c.Assert(err, gc.NotNil)249 c.Assert(err, gc.NotNil)
250 stripped := strings.Replace(err.Error(), "\n", "", -1)250 stripped := strings.Replace(err.Error(), "\n", "", -1)
251 c.Assert(stripped,251 c.Assert(stripped,
@@ -259,7 +259,7 @@
259 env := newEnviron("foo", useDefaultKeys, nil)259 env := newEnviron("foo", useDefaultKeys, nil)
260 s.setDummyStorage(c, env)260 s.setDummyStorage(c, env)
261 envtesting.RemoveFakeTools(c, env.Storage())261 envtesting.RemoveFakeTools(c, env.Storage())
262 _, err := bootstrap.EnsureToolsAvailability(env, env.Config().DefaultSeries(), nil)262 _, err := bootstrap.EnsureToolsAvailability(coretesting.Context(c), env, env.Config().DefaultSeries(), nil)
263 c.Assert(err, gc.NotNil)263 c.Assert(err, gc.NotNil)
264 stripped := strings.Replace(err.Error(), "\n", "", -1)264 stripped := strings.Replace(err.Error(), "\n", "", -1)
265 c.Assert(stripped,265 c.Assert(stripped,
@@ -309,7 +309,7 @@
309 return "arm64"309 return "arm64"
310 })310 })
311 arch := "arm64"311 arch := "arm64"
312 agentTools, err := bootstrap.EnsureToolsAvailability(env, env.Config().DefaultSeries(), &arch)312 agentTools, err := bootstrap.EnsureToolsAvailability(coretesting.Context(c), env, env.Config().DefaultSeries(), &arch)
313 c.Assert(err, gc.IsNil)313 c.Assert(err, gc.IsNil)
314 c.Assert(agentTools, gc.HasLen, 1)314 c.Assert(agentTools, gc.HasLen, 1)
315 expectedVers := version.Current315 expectedVers := version.Current
@@ -352,7 +352,7 @@
352 return "arm64"352 return "arm64"
353 })353 })
354 arch := "arm64"354 arch := "arm64"
355 err := bootstrap.UploadTools(env, &arch, allowRelease, "precise")355 err := bootstrap.UploadTools(coretesting.Context(c), env, &arch, allowRelease, "precise")
356 if errMessage != "" {356 if errMessage != "" {
357 stripped := strings.Replace(err.Error(), "\n", "", -1)357 stripped := strings.Replace(err.Error(), "\n", "", -1)
358 c.Assert(stripped, gc.Matches, errMessage)358 c.Assert(stripped, gc.Matches, errMessage)
359359
=== added file 'environs/bootstrap/export_test.go'
--- environs/bootstrap/export_test.go 1970-01-01 00:00:00 +0000
+++ environs/bootstrap/export_test.go 2014-03-26 03:32:09 +0000
@@ -0,0 +1,8 @@
1// Copyright 2014 Canonical Ltd.
2// Licensed under the AGPLv3, see LICENCE file for details.
3
4package bootstrap
5
6var (
7 NewInterruptibleStorage = newInterruptibleStorage
8)
09
=== added file 'environs/bootstrap/interruptiblestorage.go'
--- environs/bootstrap/interruptiblestorage.go 1970-01-01 00:00:00 +0000
+++ environs/bootstrap/interruptiblestorage.go 2014-03-26 03:32:09 +0000
@@ -0,0 +1,62 @@
1// Copyright 2014 Canonical Ltd.
2// Licensed under the AGPLv3, see LICENCE file for details.
3
4package bootstrap
5
6import (
7 "errors"
8 "io"
9
10 "launchpad.net/juju-core/environs/storage"
11)
12
13var interruptedError = errors.New("interrupted")
14
15// interruptibleStorage is a storage.Storage that sits
16// between the user and another Storage, allowing the
17// Put method to be interrupted.
18type interruptibleStorage struct {
19 storage.Storage
20 interrupt <-chan struct{}
21}
22
23// newInterruptibleStorage wraps the provided Storage so that Put
24// will immediately return an error if the provided channel is
25// closed.
26func newInterruptibleStorage(s storage.Storage, interrupt <-chan struct{}) storage.Storage {
27 return &interruptibleStorage{s, interrupt}
28}
29
30type interruptibleReader struct {
31 io.Reader
32 interrupt <-chan struct{}
33}
34
35func (r *interruptibleReader) Read(p []byte) (int, error) {
36 // if the interrupt channel is already
37 // closed, just drop out immediately.
38 select {
39 case <-r.interrupt:
40 return 0, interruptedError
41 default:
42 }
43
44 // read and wait for interruption concurrently
45 var n int
46 var err error
47 done := make(chan struct{})
48 go func() {
49 defer close(done)
50 n, err = r.Reader.Read(p)
51 }()
52 select {
53 case <-done:
54 return n, err
55 case <-r.interrupt:
56 return 0, interruptedError
57 }
58}
59
60func (s *interruptibleStorage) Put(name string, r io.Reader, length int64) error {
61 return s.Storage.Put(name, &interruptibleReader{r, s.interrupt}, length)
62}
063
=== added file 'environs/bootstrap/interruptiblestorage_test.go'
--- environs/bootstrap/interruptiblestorage_test.go 1970-01-01 00:00:00 +0000
+++ environs/bootstrap/interruptiblestorage_test.go 2014-03-26 03:32:09 +0000
@@ -0,0 +1,74 @@
1// Copyright 2014 Canonical Ltd.
2// Licensed under the AGPLv3, see LICENCE file for details.
3
4package bootstrap_test
5
6import (
7 "fmt"
8
9 gc "launchpad.net/gocheck"
10
11 "launchpad.net/juju-core/environs/bootstrap"
12 envtesting "launchpad.net/juju-core/environs/testing"
13 "launchpad.net/juju-core/testing/testbase"
14)
15
16type interruptibleStorageSuite struct {
17 testbase.LoggingSuite
18}
19
20var _ = gc.Suite(&interruptibleStorageSuite{})
21
22type errorReader struct {
23 close chan struct{}
24 wait chan struct{}
25 called int
26 err error
27}
28
29func (r *errorReader) Read(buf []byte) (int, error) {
30 if r.close != nil {
31 close(r.close)
32 }
33 if r.wait != nil {
34 <-r.wait
35 }
36 r.called++
37 return 0, r.err
38}
39
40func (s *interruptibleStorageSuite) TestInterruptStorage(c *gc.C) {
41 closer, stor, _ := envtesting.CreateLocalTestStorage(c)
42 s.AddCleanup(func(c *gc.C) { closer.Close() })
43 reader := &errorReader{
44 err: fmt.Errorf("read failed"),
45 }
46 interrupted := make(chan struct{})
47 istor := bootstrap.NewInterruptibleStorage(stor, interrupted)
48
49 err := istor.Put("name", reader, 3)
50 c.Assert(err, gc.ErrorMatches, ".*: read failed")
51 c.Assert(reader.called, gc.Equals, 1)
52
53 // If the channel is already closed, then the
54 // underlying reader is never deferred to.
55 close(interrupted)
56 err = istor.Put("name", reader, 3)
57 c.Assert(err, gc.ErrorMatches, ".*: interrupted")
58 c.Assert(reader.called, gc.Equals, 1)
59}
60
61func (s *interruptibleStorageSuite) TestInterruptStorageConcurrently(c *gc.C) {
62 closer, stor, _ := envtesting.CreateLocalTestStorage(c)
63 s.AddCleanup(func(c *gc.C) { closer.Close() })
64 reader := &errorReader{
65 close: make(chan struct{}),
66 wait: make(chan struct{}),
67 err: fmt.Errorf("read failed"),
68 }
69 istor := bootstrap.NewInterruptibleStorage(stor, reader.close)
70 err := istor.Put("name", reader, 3)
71 c.Assert(err, gc.ErrorMatches, ".*: interrupted")
72 c.Assert(reader.called, gc.Equals, 0) // reader is blocked
73 close(reader.wait)
74}
075
=== modified file 'environs/bootstrap/synctools.go'
--- environs/bootstrap/synctools.go 2014-03-19 22:02:00 +0000
+++ environs/bootstrap/synctools.go 2014-03-26 03:32:09 +0000
@@ -5,6 +5,7 @@
55
6import (6import (
7 "fmt"7 "fmt"
8 "os"
89
9 "launchpad.net/juju-core/environs"10 "launchpad.net/juju-core/environs"
10 "launchpad.net/juju-core/environs/config"11 "launchpad.net/juju-core/environs/config"
@@ -30,7 +31,7 @@
30// the environment storage, after which it sets the agent-version. If forceVersion is true,31// the environment storage, after which it sets the agent-version. If forceVersion is true,
31// we allow uploading release tools versions and allow uploading even when the agent-version is32// we allow uploading release tools versions and allow uploading even when the agent-version is
32// already set in the environment.33// already set in the environment.
33func UploadTools(env environs.Environ, toolsArch *string, forceVersion bool, bootstrapSeries ...string) error {34func UploadTools(ctx environs.BootstrapContext, env environs.Environ, toolsArch *string, forceVersion bool, bootstrapSeries ...string) error {
34 logger.Infof("checking that upload is possible")35 logger.Infof("checking that upload is possible")
35 // Check the series are valid.36 // Check the series are valid.
36 for _, series := range bootstrapSeries {37 for _, series := range bootstrapSeries {
@@ -43,11 +44,25 @@
43 return err44 return err
44 }45 }
4546
47 // Make storage interruptible.
48 interrupted := make(chan os.Signal, 1)
49 interruptStorage := make(chan struct{})
50 ctx.InterruptNotify(interrupted)
51 defer ctx.StopInterruptNotify(interrupted)
52 defer close(interrupted)
53 go func() {
54 defer close(interruptStorage) // closing interrupts all uploads
55 if _, ok := <-interrupted; ok {
56 ctx.Infof("cancelling tools upload")
57 }
58 }()
59 stor := newInterruptibleStorage(env.Storage(), interruptStorage)
60
46 cfg := env.Config()61 cfg := env.Config()
47 explicitVersion := uploadVersion(version.Current.Number, nil)62 explicitVersion := uploadVersion(version.Current.Number, nil)
48 uploadSeries := SeriesToUpload(cfg, bootstrapSeries)63 uploadSeries := SeriesToUpload(cfg, bootstrapSeries)
49 logger.Infof("uploading tools for series %s", uploadSeries)64 ctx.Infof("uploading tools for series %s", uploadSeries)
50 tools, err := sync.Upload(env.Storage(), &explicitVersion, uploadSeries...)65 tools, err := sync.Upload(stor, &explicitVersion, uploadSeries...)
51 if err != nil {66 if err != nil {
52 return err67 return err
53 }68 }
@@ -131,7 +146,7 @@
131146
132// EnsureToolsAvailability verifies the tools are available. If no tools are147// EnsureToolsAvailability verifies the tools are available. If no tools are
133// found, it will automatically synchronize them.148// found, it will automatically synchronize them.
134func EnsureToolsAvailability(env environs.Environ, series string, toolsArch *string) (coretools.List, error) {149func EnsureToolsAvailability(ctx environs.BootstrapContext, env environs.Environ, series string, toolsArch *string) (coretools.List, error) {
135 cfg := env.Config()150 cfg := env.Config()
136 var vers *version.Number151 var vers *version.Number
137 if agentVersion, ok := cfg.AgentVersion(); ok {152 if agentVersion, ok := cfg.AgentVersion(); ok {
@@ -160,7 +175,7 @@
160 // No tools available so our only hope is to build locally and upload.175 // No tools available so our only hope is to build locally and upload.
161 logger.Warningf("no prepackaged tools available")176 logger.Warningf("no prepackaged tools available")
162 uploadSeries := SeriesToUpload(cfg, nil)177 uploadSeries := SeriesToUpload(cfg, nil)
163 if err := UploadTools(env, toolsArch, false, append(uploadSeries, series)...); err != nil {178 if err := UploadTools(ctx, env, toolsArch, false, append(uploadSeries, series)...); err != nil {
164 logger.Errorf("%s", noToolsMessage)179 logger.Errorf("%s", noToolsMessage)
165 return nil, fmt.Errorf("cannot upload bootstrap tools: %v", err)180 return nil, fmt.Errorf("cannot upload bootstrap tools: %v", err)
166 }181 }
167182
=== modified file 'environs/interface.go'
--- environs/interface.go 2014-03-17 07:44:21 +0000
+++ environs/interface.go 2014-03-26 03:32:09 +0000
@@ -183,6 +183,8 @@
183 GetStdin() io.Reader183 GetStdin() io.Reader
184 GetStdout() io.Writer184 GetStdout() io.Writer
185 GetStderr() io.Writer185 GetStderr() io.Writer
186 Infof(format string, params ...interface{})
187 Verbosef(format string, params ...interface{})
186188
187 // InterruptNotify starts watching for interrupt signals189 // InterruptNotify starts watching for interrupt signals
188 // on behalf of the caller, sending them to the supplied190 // on behalf of the caller, sending them to the supplied
189191
=== modified file 'provider/common/bootstrap.go'
--- provider/common/bootstrap.go 2014-03-21 14:39:23 +0000
+++ provider/common/bootstrap.go 2014-03-26 03:32:09 +0000
@@ -42,7 +42,7 @@
42 defer func() { handleBootstrapError(err, ctx, inst, env) }()42 defer func() { handleBootstrapError(err, ctx, inst, env) }()
4343
44 // First thing, ensure we have tools otherwise there's no point.44 // First thing, ensure we have tools otherwise there's no point.
45 selectedTools, err := EnsureBootstrapTools(env, env.Config().DefaultSeries(), cons.Arch)45 selectedTools, err := EnsureBootstrapTools(ctx, env, env.Config().DefaultSeries(), cons.Arch)
46 if err != nil {46 if err != nil {
47 return err47 return err
48 }48 }
@@ -403,8 +403,8 @@
403// EnsureBootstrapTools finds tools, syncing with an external tools source as403// EnsureBootstrapTools finds tools, syncing with an external tools source as
404// necessary; it then selects the newest tools to bootstrap with, and sets404// necessary; it then selects the newest tools to bootstrap with, and sets
405// agent-version.405// agent-version.
406func EnsureBootstrapTools(env environs.Environ, series string, arch *string) (coretools.List, error) {406func EnsureBootstrapTools(ctx environs.BootstrapContext, env environs.Environ, series string, arch *string) (coretools.List, error) {
407 possibleTools, err := bootstrap.EnsureToolsAvailability(env, series, arch)407 possibleTools, err := bootstrap.EnsureToolsAvailability(ctx, env, series, arch)
408 if err != nil {408 if err != nil {
409 return nil, err409 return nil, err
410 }410 }
411411
=== modified file 'provider/dummy/environs.go'
--- provider/dummy/environs.go 2014-03-18 05:03:38 +0000
+++ provider/dummy/environs.go 2014-03-26 03:32:09 +0000
@@ -543,7 +543,7 @@
543}543}
544544
545func (e *environ) Bootstrap(ctx environs.BootstrapContext, cons constraints.Value) error {545func (e *environ) Bootstrap(ctx environs.BootstrapContext, cons constraints.Value) error {
546 selectedTools, err := common.EnsureBootstrapTools(e, e.Config().DefaultSeries(), cons.Arch)546 selectedTools, err := common.EnsureBootstrapTools(ctx, e, e.Config().DefaultSeries(), cons.Arch)
547 if err != nil {547 if err != nil {
548 return err548 return err
549 }549 }
550550
=== modified file 'provider/local/environ.go'
--- provider/local/environ.go 2014-03-25 06:35:04 +0000
+++ provider/local/environ.go 2014-03-26 03:32:09 +0000
@@ -118,7 +118,7 @@
118 }118 }
119119
120 vers := version.Current120 vers := version.Current
121 selectedTools, err := common.EnsureBootstrapTools(env, vers.Series, &vers.Arch)121 selectedTools, err := common.EnsureBootstrapTools(ctx, env, vers.Series, &vers.Arch)
122 if err != nil {122 if err != nil {
123 return err123 return err
124 }124 }
125125
=== modified file 'provider/manual/environ.go'
--- provider/manual/environ.go 2014-03-25 13:18:46 +0000
+++ provider/manual/environ.go 2014-03-26 03:32:09 +0000
@@ -113,7 +113,7 @@
113 if err != nil {113 if err != nil {
114 return err114 return err
115 }115 }
116 selectedTools, err := common.EnsureBootstrapTools(e, series, hc.Arch)116 selectedTools, err := common.EnsureBootstrapTools(ctx, e, series, hc.Arch)
117 if err != nil {117 if err != nil {
118 return err118 return err
119 }119 }

Subscribers

People subscribed via source and target branches

to status/vote changes: