Merge lp:~axwalk/juju-core/lp1238948-sigabrt-uninstall 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: 2188
Proposed branch: lp:~axwalk/juju-core/lp1238948-sigabrt-uninstall
Merge into: lp:~go-bot/juju-core/trunk
Diff against target: 246 lines (+175/-2)
5 files modified
cmd/jujud/machine.go (+4/-0)
provider/null/environ.go (+23/-1)
provider/null/environ_test.go (+29/-1)
worker/terminationworker/worker.go (+60/-0)
worker/terminationworker/worker_test.go (+59/-0)
To merge this branch: bzr merge lp:~axwalk/juju-core/lp1238948-sigabrt-uninstall
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+200767@code.launchpad.net

Commit message

provider/null: implement Environ.Destroy

A new worker is introduced: worker/terminationworker.
This worker simply waits for a signal (SIGABRT), and
then exits with ErrTerminateAgent. This causes the
agent to uninstall itself. We do not use SIGTERM, as
that would cause "service stop jujud-machine-N" to
uninstall the agent.

The null/manual provider has been updated at the same
time to implement Environ.Destroy. The Destroy method
simply connects to the bootstrap machine via SSH, and
issues "sudo pkill -SIGABRT jujud". We only do this
for machine 0, as Destroy will never be reached until
all non-manager machines are destroyed.

Fixes #1238948

https://codereview.appspot.com/48950043/

Description of the change

provider/null: implement Environ.Destroy

A new worker is introduced: worker/terminationworker.
This worker simply waits for a signal (SIGABRT), and
then exits with ErrTerminateAgent. This causes the
agent to uninstall itself. We do not use SIGTERM, as
that would cause "service stop jujud-machine-N" to
uninstall the agent.

The null/manual provider has been updated at the same
time to implement Environ.Destroy. The Destroy method
simply connects to the bootstrap machine via SSH, and
issues "sudo pkill -SIGABRT jujud". We only do this
for machine 0, as Destroy will never be reached until
all non-manager machines are destroyed.

Fixes #1238948

https://codereview.appspot.com/48950043/

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

Reviewers: mp+200767_code.launchpad.net,

Message:
Please take a look.

Description:
provider/null: implement Environ.Destroy

A new worker is introduced: worker/terminationworker.
This worker simply waits for a signal (SIGABRT), and
then exits with ErrTerminateAgent. This causes the
agent to uninstall itself. We do not use SIGTERM, as
that would cause "service stop jujud-machine-N" to
uninstall the agent.

The null/manual provider has been updated at the same
time to implement Environ.Destroy. The Destroy method
simply connects to the bootstrap machine via SSH, and
issues "sudo pkill -SIGABRT jujud". We only do this
for machine 0, as Destroy will never be reached until
all non-manager machines are destroyed.

Fixes #1238948

https://code.launchpad.net/~axwalk/juju-core/lp1238948-sigabrt-uninstall/+merge/200767

(do not edit description out of merge proposal)

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

Affected files (+177, -2 lines):
   A [revision details]
   M cmd/jujud/machine.go
   M provider/null/environ.go
   M provider/null/environ_test.go
   A worker/terminationworker/worker.go
   A worker/terminationworker/worker_test.go

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

On 2014/01/08 05:57:42, axw wrote:
> Please take a look.

LGTM

https://codereview.appspot.com/48950043/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'cmd/jujud/machine.go'
2--- cmd/jujud/machine.go 2013-12-13 02:44:21 +0000
3+++ cmd/jujud/machine.go 2014-01-08 06:05:52 +0000
4@@ -39,6 +39,7 @@
5 "launchpad.net/juju-core/worker/minunitsworker"
6 "launchpad.net/juju-core/worker/provisioner"
7 "launchpad.net/juju-core/worker/resumer"
8+ "launchpad.net/juju-core/worker/terminationworker"
9 "launchpad.net/juju-core/worker/upgrader"
10 )
11
12@@ -136,6 +137,9 @@
13 a.runner.StartWorker("api", func() (worker.Worker, error) {
14 return a.APIWorker(ensureStateWorker)
15 })
16+ a.runner.StartWorker("termination", func() (worker.Worker, error) {
17+ return terminationworker.NewWorker(), nil
18+ })
19 err := a.runner.Wait()
20 if err == worker.ErrTerminateAgent {
21 err = a.uninstallAgent()
22
23=== modified file 'provider/null/environ.go'
24--- provider/null/environ.go 2014-01-06 07:17:46 +0000
25+++ provider/null/environ.go 2014-01-08 06:05:52 +0000
26@@ -4,9 +4,12 @@
27 package null
28
29 import (
30+ "bytes"
31 "errors"
32+ "fmt"
33 "net"
34 "path"
35+ "strings"
36 "sync"
37
38 "launchpad.net/loggo"
39@@ -26,7 +29,9 @@
40 "launchpad.net/juju-core/state"
41 "launchpad.net/juju-core/state/api"
42 "launchpad.net/juju-core/tools"
43+ "launchpad.net/juju-core/utils/ssh"
44 "launchpad.net/juju-core/worker/localstorage"
45+ "launchpad.net/juju-core/worker/terminationworker"
46 )
47
48 const (
49@@ -228,8 +233,25 @@
50 return nil
51 }
52
53+var runSSHCommand = func(host string, command []string) (stderr string, err error) {
54+ cmd := ssh.Command(host, command, ssh.NoPasswordAuthentication)
55+ var stderrBuf bytes.Buffer
56+ cmd.Stderr = &stderrBuf
57+ err = cmd.Run()
58+ return stderrBuf.String(), err
59+}
60+
61 func (e *nullEnviron) Destroy() error {
62- return errors.New("null provider destruction is not implemented yet")
63+ stderr, err := runSSHCommand(
64+ "ubuntu@"+e.envConfig().bootstrapHost(),
65+ []string{"sudo", "pkill", fmt.Sprintf("-%d", terminationworker.TerminationSignal), "jujud"},
66+ )
67+ if err != nil {
68+ if stderr := strings.TrimSpace(stderr); len(stderr) > 0 {
69+ err = fmt.Errorf("%v (%v)", err, stderr)
70+ }
71+ }
72+ return err
73 }
74
75 func (e *nullEnviron) OpenPorts(ports []instance.Port) error {
76
77=== modified file 'provider/null/environ_test.go'
78--- provider/null/environ_test.go 2014-01-03 05:14:11 +0000
79+++ provider/null/environ_test.go 2014-01-08 06:05:52 +0000
80@@ -80,7 +80,35 @@
81 }
82
83 func (s *environSuite) TestDestroy(c *gc.C) {
84- c.Assert(s.env.Destroy(), gc.ErrorMatches, "null provider destruction is not implemented yet")
85+ var resultStderr string
86+ var resultErr error
87+ runSSHCommandTesting := func(host string, command []string) (string, error) {
88+ c.Assert(host, gc.Equals, "ubuntu@hostname")
89+ c.Assert(command, gc.DeepEquals, []string{"sudo", "pkill", "-6", "jujud"})
90+ return resultStderr, resultErr
91+ }
92+ s.PatchValue(&runSSHCommand, runSSHCommandTesting)
93+ type test struct {
94+ stderr string
95+ err error
96+ match string
97+ }
98+ tests := []test{
99+ {"", nil, ""},
100+ {"abc", nil, ""},
101+ {"", errors.New("oh noes"), "oh noes"},
102+ {"123", errors.New("abc"), "abc \\(123\\)"},
103+ }
104+ for i, t := range tests {
105+ c.Logf("test %d: %v", i, t)
106+ resultStderr, resultErr = t.stderr, t.err
107+ err := s.env.Destroy()
108+ if t.match == "" {
109+ c.Assert(err, gc.IsNil)
110+ } else {
111+ c.Assert(err, gc.ErrorMatches, t.match)
112+ }
113+ }
114 }
115
116 func (s *environSuite) TestLocalStorageConfig(c *gc.C) {
117
118=== added directory 'worker/terminationworker'
119=== added file 'worker/terminationworker/worker.go'
120--- worker/terminationworker/worker.go 1970-01-01 00:00:00 +0000
121+++ worker/terminationworker/worker.go 2014-01-08 06:05:52 +0000
122@@ -0,0 +1,60 @@
123+// Copyright 2014 Canonical Ltd.
124+// Licensed under the AGPLv3, see LICENCE file for details.
125+
126+package terminationworker
127+
128+import (
129+ "os"
130+ "os/signal"
131+ "syscall"
132+
133+ "launchpad.net/tomb"
134+
135+ "launchpad.net/juju-core/worker"
136+)
137+
138+// TerminationSignal is the signal that
139+// indicates the agent should terminate
140+// and uninstall itself.
141+//
142+// We do not use SIGTERM as SIGTERM is the
143+// default signal used to initiate a graceful
144+// shutdown.
145+const TerminationSignal = syscall.SIGABRT
146+
147+// NewWorker returns a worker that wais for a
148+
149+type terminationWorker struct {
150+ tomb tomb.Tomb
151+}
152+
153+// TerminationSignal signal and exits with
154+// ErrTerminateAgent.
155+func NewWorker() worker.Worker {
156+ u := &terminationWorker{}
157+ go func() {
158+ defer u.tomb.Done()
159+ u.tomb.Kill(u.loop())
160+ }()
161+ return u
162+}
163+
164+func (u *terminationWorker) Kill() {
165+ u.tomb.Kill(nil)
166+}
167+
168+func (u *terminationWorker) Wait() error {
169+ return u.tomb.Wait()
170+}
171+
172+func (u *terminationWorker) loop() (err error) {
173+ c := make(chan os.Signal, 1)
174+ signal.Notify(c, TerminationSignal)
175+ defer signal.Stop(c)
176+ select {
177+ case <-c:
178+ return worker.ErrTerminateAgent
179+ case <-u.tomb.Dying():
180+ return nil
181+ }
182+}
183
184=== added file 'worker/terminationworker/worker_test.go'
185--- worker/terminationworker/worker_test.go 1970-01-01 00:00:00 +0000
186+++ worker/terminationworker/worker_test.go 2014-01-08 06:05:52 +0000
187@@ -0,0 +1,59 @@
188+// Copyright 2014 Canonical Ltd.
189+// Licensed under the AGPLv3, see LICENCE file for details.
190+
191+package terminationworker_test
192+
193+import (
194+ "os"
195+ "os/signal"
196+ stdtesting "testing"
197+
198+ gc "launchpad.net/gocheck"
199+
200+ "launchpad.net/juju-core/testing/testbase"
201+ "launchpad.net/juju-core/worker"
202+ "launchpad.net/juju-core/worker/terminationworker"
203+)
204+
205+func TestPackage(t *stdtesting.T) {
206+ gc.TestingT(t)
207+}
208+
209+var _ = gc.Suite(&TerminationWorkerSuite{})
210+
211+type TerminationWorkerSuite struct {
212+ testbase.LoggingSuite
213+ // c is a channel that will wait for the termination
214+ // signal, to prevent signals terminating the process.
215+ c chan os.Signal
216+}
217+
218+func (s *TerminationWorkerSuite) SetUpTest(c *gc.C) {
219+ s.LoggingSuite.SetUpTest(c)
220+ s.c = make(chan os.Signal, 1)
221+ signal.Notify(s.c, terminationworker.TerminationSignal)
222+}
223+
224+func (s *TerminationWorkerSuite) TearDownTest(c *gc.C) {
225+ close(s.c)
226+ signal.Stop(s.c)
227+ s.LoggingSuite.TearDownTest(c)
228+}
229+
230+func (s *TerminationWorkerSuite) TestStartStop(c *gc.C) {
231+ w := terminationworker.NewWorker()
232+ w.Kill()
233+ err := w.Wait()
234+ c.Assert(err, gc.IsNil)
235+}
236+
237+func (s *TerminationWorkerSuite) TestSignal(c *gc.C) {
238+ w := terminationworker.NewWorker()
239+ proc, err := os.FindProcess(os.Getpid())
240+ c.Assert(err, gc.IsNil)
241+ defer proc.Release()
242+ err = proc.Signal(terminationworker.TerminationSignal)
243+ c.Assert(err, gc.IsNil)
244+ err = w.Wait()
245+ c.Assert(err, gc.Equals, worker.ErrTerminateAgent)
246+}

Subscribers

People subscribed via source and target branches

to status/vote changes: