Merge lp:~axwalk/juju-core/jujud-uninstall-mongo 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: 2083
Proposed branch: lp:~axwalk/juju-core/jujud-uninstall-mongo
Merge into: lp:~go-bot/juju-core/trunk
Diff against target: 196 lines (+75/-11)
6 files modified
agent/agent.go (+2/-0)
cmd/jujud/machine.go (+29/-7)
cmd/jujud/machine_test.go (+6/-2)
environs/cloudinit/cloudinit.go (+13/-2)
environs/cloudinit/cloudinit_test.go (+23/-0)
provider/local/environ.go (+2/-0)
To merge this branch: bzr merge lp:~axwalk/juju-core/jujud-uninstall-mongo
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+195565@code.launchpad.net

Commit message

cmd/jujud: remove mongo and data-dir on teardown

Two new agent.conf keys are introduced:
 - AGENT_SERVICE_NAME, and
 - MONGO_SERVICE_NAME.

The former is always added, the latter only
for state servers. For backwards-compatibility
we fall back to os.Getenv("UPSTART_JOB") for
AGENT_SERVICE_NAME.

https://codereview.appspot.com/28270043/

Description of the change

cmd/jujud: remove mongo and data-dir on teardown

Two new agent.conf keys are introduced:
 - AGENT_SERVICE_NAME, and
 - MONGO_SERVICE_NAME.

The former is always added, the latter only
for state servers. For backwards-compatibility
we fall back to os.Getenv("UPSTART_JOB") for
AGENT_SERVICE_NAME.

https://codereview.appspot.com/28270043/

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

Reviewers: mp+195565_code.launchpad.net,

Message:
Please take a look.

Description:
cmd/jujud: remove mongo and data-dir on teardown

Note: this does not remove the services when
using the local provider, as it gives the services
non-standard names. It doesn't have any negative
effects, either, unless you're running the local
provider on a machine provisioned with Juju; but
that's not really sane is it?

https://code.launchpad.net/~axwalk/juju-core/jujud-uninstall-mongo/+merge/195565

(do not edit description out of merge proposal)

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

Affected files (+38, -11 lines):
   A [revision details]
   M cmd/jujud/machine.go
   M cmd/jujud/machine_test.go
   M environs/cloudinit/cloudinit.go
   M upstart/service.go

Index: [revision details]
=== added file '[revision details]'
--- [revision details] 2012-01-01 00:00:00 +0000
+++ [revision details] 2012-01-01 00:00:00 +0000
@@ -0,0 +1,2 @@
+Old revision: tarmac-20131118065206-jpp233o069gz8774
+New revision: <email address hidden>

Index: upstart/service.go
=== modified file 'upstart/service.go'
--- upstart/service.go 2013-09-18 01:25:45 +0000
+++ upstart/service.go 2013-11-18 09:36:25 +0000
@@ -13,6 +13,10 @@
  const (
   maxMongoFiles = 65000
   maxAgentFiles = 20000
+
+ // MongoServiceName is the default name of the upstart
+ // service that runs mongod for Juju.
+ MongoServiceName = "juju-db"
  )

  // MongoUpstartService returns the upstart config for the mongo state
service.
@@ -40,6 +44,12 @@
   }
  }

+// MachineAgentServiceName returns the upstart service name for
+// a machine agent with the given tag.
+func MachineAgentServiceName(tag string) string {
+ return "jujud-" + tag
+}
+
  // MachineAgentUpstartService returns the upstart config for a machine
agent
  // based on the tag and machineId passed in.
  func MachineAgentUpstartService(name, toolsDir, dataDir, logDir, tag,
machineId string, env map[string]string) *Conf {

Index: cmd/jujud/machine.go
=== modified file 'cmd/jujud/machine.go'
--- cmd/jujud/machine.go 2013-10-02 23:18:21 +0000
+++ cmd/jujud/machine.go 2013-11-18 09:36:25 +0000
@@ -304,13 +304,23 @@
   return names.MachineTag(a.MachineId)
  }

-func (m *MachineAgent) uninstallAgent() error {
- // TODO(axw) get this from agent config when it's available
- name := os.Getenv("UPSTART_JOB")
- if name != "" {
- return upstart.NewService(name).Remove()
- }
- return nil
+func (a *MachineAgent) uninstallAgent() error {
+ // NOTE: this will not stop/remove upstart services
+ // for the local provider (only), which has different
+ // service names for different users/environments.
+ service :=
upstart.NewService(upstart.MachineAgentServiceName(a.Conf.config.Tag()))
+ if err := service.Remove(); err != nil {
+ return err
+ }
+ // The machine agent may terminate without knowing its jobs,
+ // for example if the machine's entry in state was removed.
+ // Thus, we do not rely on jobs here, and instead just check
+ // if the upstart config exists.
+ service = upstart.NewService(upstart.MongoServiceName)
+ if err := service.StopAndRemove(); err != nil {
+ return err
+ }
+ return os....

Read more...

Revision history for this message
John A Meinel (jameinel) wrote :

Did you test this works live?

It looks reasonable to me, and seems a lot more straightforward than
creating a script that we then figure out how to run later.

https://codereview.appspot.com/28270043/diff/1/cmd/jujud/machine.go
File cmd/jujud/machine.go (right):

https://codereview.appspot.com/28270043/diff/1/cmd/jujud/machine.go#newcode313
cmd/jujud/machine.go:313: return err
I wonder if we actually want to just return here.
As this is a 'cleanup' style function, I would think you'd want to run
all cleanups and then if any of them had errors report them.

https://codereview.appspot.com/28270043/diff/1/upstart/service.go
File upstart/service.go (right):

https://codereview.appspot.com/28270043/diff/1/upstart/service.go#newcode19
upstart/service.go:19: MongoServiceName = "juju-db"
My first thought is that this shouldn't be data in 'upstart'. Because
the upstart library shouldn't know about agent names (juju should know
about it, and communicate that *to* upstart).

Though it is also strange that upstart knows how many MongoFiles to
support, so I may be off base on that.

https://codereview.appspot.com/28270043/diff/1/upstart/service.go#newcode51
upstart/service.go:51: }
It does feel like if we are going to be providing a new Public api, that
we should be adding a test for how that API behaves.

https://codereview.appspot.com/28270043/

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

> Did you test this works live?

Yes indeed. Works with the null provider. Doesn't break the local
provider (but doesn't remove its services).

I'm going to take a look at adding the upstart service names into
agent.conf again, as I think it may clean things up a bit w.r.t. service
names. We'd solve the local-provider disparity (which is really just an
ugliness, and not a real problem), and do away with the need for
exporting the service names from a package (like upstart).

https://codereview.appspot.com/28270043/diff/1/cmd/jujud/machine.go
File cmd/jujud/machine.go (right):

https://codereview.appspot.com/28270043/diff/1/cmd/jujud/machine.go#newcode313
cmd/jujud/machine.go:313: return err
On 2013/11/18 09:56:46, jameinel wrote:
> I wonder if we actually want to just return here.
> As this is a 'cleanup' style function, I would think you'd want to run
all
> cleanups and then if any of them had errors report them.

Fair call. I'll update to do that.

https://codereview.appspot.com/28270043/diff/1/upstart/service.go
File upstart/service.go (right):

https://codereview.appspot.com/28270043/diff/1/upstart/service.go#newcode19
upstart/service.go:19: MongoServiceName = "juju-db"
On 2013/11/18 09:56:46, jameinel wrote:
> My first thought is that this shouldn't be data in 'upstart'. Because
the
> upstart library shouldn't know about agent names (juju should know
about it, and
> communicate that *to* upstart).

So, I agree, but I also want to keep it close to the definition of the
upstart service. Probably the right thing to do is move it all to
environs/cloudinit.

> Though it is also strange that upstart knows how many MongoFiles to
support, so
> I may be off base on that.

I think it's a bit off that there's any notion of Mongo or MachineAgent
in the upstart package at all.

https://codereview.appspot.com/28270043/diff/1/upstart/service.go#newcode51
upstart/service.go:51: }
On 2013/11/18 09:56:46, jameinel wrote:
> It does feel like if we are going to be providing a new Public api,
that we
> should be adding a test for how that API behaves.

Yes, sorry, I'll add one.

https://codereview.appspot.com/28270043/

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

LGTM with some minor thoughts below.

https://codereview.appspot.com/28270043/diff/20001/cmd/jujud/machine.go
File cmd/jujud/machine.go (right):

https://codereview.appspot.com/28270043/diff/20001/cmd/jujud/machine.go#newcode316
cmd/jujud/machine.go:316: errors = append(errors, err)
I think that having a bunch of errors without context might prove very
hard for a user to interpret if something goes wrong.

I think things might be more understandable if we added some context to
each one;
e.g.

errors = append(errors, fmt.Errorf("cannot remove service %q: %v",
agentServiceName, err))

https://codereview.appspot.com/28270043/diff/20001/cmd/jujud/machine.go#newcode334
cmd/jujud/machine.go:334: }
if len(errors) == 1 {
     return fmt.Errorf("uninstall failed: %v", errors[0])
}

to save the square brackets when only one thing fails?
probably not worth it, just a thought.

https://codereview.appspot.com/28270043/diff/20001/environs/cloudinit/cloudinit.go
File environs/cloudinit/cloudinit.go (right):

https://codereview.appspot.com/28270043/diff/20001/environs/cloudinit/cloudinit.go#newcode313
environs/cloudinit/cloudinit.go:313:
acfg.SetValue(agent.MongoServiceName, mongoServiceName)
Is there nowhere to test this functionality?

https://codereview.appspot.com/28270043/

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

Please take a look.

https://codereview.appspot.com/28270043/diff/20001/cmd/jujud/machine.go
File cmd/jujud/machine.go (right):

https://codereview.appspot.com/28270043/diff/20001/cmd/jujud/machine.go#newcode316
cmd/jujud/machine.go:316: errors = append(errors, err)
On 2013/11/19 09:50:39, rog wrote:
> I think that having a bunch of errors without context might prove very
hard for
> a user to interpret if something goes wrong.

> I think things might be more understandable if we added some context
to each
> one;
> e.g.

> errors = append(errors, fmt.Errorf("cannot remove service %q: %v",
> agentServiceName, err))

Good call. Done.

https://codereview.appspot.com/28270043/diff/20001/cmd/jujud/machine.go#newcode334
cmd/jujud/machine.go:334: }
On 2013/11/19 09:50:39, rog wrote:
> if len(errors) == 1 {
> return fmt.Errorf("uninstall failed: %v", errors[0])
> }

> to save the square brackets when only one thing fails?
> probably not worth it, just a thought.

I concluded it wasn't worthwhile, as it made the code pretty ugly. The
error message will only ever go to the machine log.

https://codereview.appspot.com/28270043/diff/20001/environs/cloudinit/cloudinit.go
File environs/cloudinit/cloudinit.go (right):

https://codereview.appspot.com/28270043/diff/20001/environs/cloudinit/cloudinit.go#newcode313
environs/cloudinit/cloudinit.go:313:
acfg.SetValue(agent.MongoServiceName, mongoServiceName)
On 2013/11/19 09:50:39, rog wrote:
> Is there nowhere to test this functionality?

Gah, I intended to do that, it just slipped my mind. Added a test.

https://codereview.appspot.com/28270043/

Revision history for this message
Go Bot (go-bot) wrote :

Attempt to merge into lp:juju-core failed due to conflicts:

text conflict in environs/cloudinit/cloudinit_test.go

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'agent/agent.go'
2--- agent/agent.go 2013-11-19 13:46:00 +0000
3+++ agent/agent.go 2013-11-20 02:15:23 +0000
4@@ -28,6 +28,8 @@
5 StorageAddr = "STORAGE_ADDR"
6 SharedStorageDir = "SHARED_STORAGE_DIR"
7 SharedStorageAddr = "SHARED_STORAGE_ADDR"
8+ AgentServiceName = "AGENT_SERVICE_NAME"
9+ MongoServiceName = "MONGO_SERVICE_NAME"
10 )
11
12 // The Config interface is the sole way that the agent gets access to the
13
14=== modified file 'cmd/jujud/machine.go'
15--- cmd/jujud/machine.go 2013-11-14 04:28:28 +0000
16+++ cmd/jujud/machine.go 2013-11-20 02:15:23 +0000
17@@ -337,13 +337,35 @@
18 return names.MachineTag(a.MachineId)
19 }
20
21-func (m *MachineAgent) uninstallAgent() error {
22- // TODO(axw) get this from agent config when it's available
23- name := os.Getenv("UPSTART_JOB")
24- if name != "" {
25- return upstart.NewService(name).Remove()
26- }
27- return nil
28+func (a *MachineAgent) uninstallAgent() error {
29+ var errors []error
30+ agentServiceName := a.Conf.config.Value(agent.AgentServiceName)
31+ if agentServiceName == "" {
32+ // For backwards compatibility, handle lack of AgentServiceName.
33+ agentServiceName = os.Getenv("UPSTART_JOB")
34+ }
35+ if agentServiceName != "" {
36+ if err := upstart.NewService(agentServiceName).Remove(); err != nil {
37+ errors = append(errors, fmt.Errorf("cannot remove service %q: %v", agentServiceName, err))
38+ }
39+ }
40+ // The machine agent may terminate without knowing its jobs,
41+ // for example if the machine's entry in state was removed.
42+ // Thus, we do not rely on jobs here, and instead just check
43+ // if the upstart config exists.
44+ mongoServiceName := a.Conf.config.Value(agent.MongoServiceName)
45+ if mongoServiceName != "" {
46+ if err := upstart.NewService(mongoServiceName).StopAndRemove(); err != nil {
47+ errors = append(errors, fmt.Errorf("cannot stop/remove service %q: %v", mongoServiceName, err))
48+ }
49+ }
50+ if err := os.RemoveAll(a.Conf.dataDir); err != nil {
51+ errors = append(errors, err)
52+ }
53+ if len(errors) == 0 {
54+ return nil
55+ }
56+ return fmt.Errorf("uninstall failed: %v", errors)
57 }
58
59 // Below pieces are used for testing,to give us access to the *State opened
60
61=== modified file 'cmd/jujud/machine_test.go'
62--- cmd/jujud/machine_test.go 2013-11-11 22:19:10 +0000
63+++ cmd/jujud/machine_test.go 2013-11-20 02:15:23 +0000
64@@ -149,11 +149,15 @@
65 a := s.newAgent(c, m)
66 err = runWithTimeout(a)
67 c.Assert(err, gc.IsNil)
68+}
69
70- // try again with the machine removed.
71+func (s *MachineSuite) TestWithRemovedMachine(c *gc.C) {
72+ m, _, _ := s.primeAgent(c, state.JobHostUnits, state.JobManageState)
73+ err := m.EnsureDead()
74+ c.Assert(err, gc.IsNil)
75 err = m.Remove()
76 c.Assert(err, gc.IsNil)
77- a = s.newAgent(c, m)
78+ a := s.newAgent(c, m)
79 err = runWithTimeout(a)
80 c.Assert(err, gc.IsNil)
81 }
82
83=== modified file 'environs/cloudinit/cloudinit.go'
84--- environs/cloudinit/cloudinit.go 2013-11-19 00:18:18 +0000
85+++ environs/cloudinit/cloudinit.go 2013-11-20 02:15:23 +0000
86@@ -303,6 +303,10 @@
87 if err != nil {
88 return nil, err
89 }
90+ acfg.SetValue(agent.AgentServiceName, machineAgentServiceName(tag))
91+ if cfg.StateServer {
92+ acfg.SetValue(agent.MongoServiceName, mongoServiceName)
93+ }
94 cmds, err := acfg.WriteCommands()
95 if err != nil {
96 return nil, err
97@@ -311,6 +315,12 @@
98 return acfg, nil
99 }
100
101+const mongoServiceName = "juju-db"
102+
103+func machineAgentServiceName(tag string) string {
104+ return "jujud-" + tag
105+}
106+
107 func (cfg *MachineConfig) addMachineAgentToBoot(c *cloudinit.Config, tag, machineId string) error {
108 // Make the agent run via a symbolic link to the actual tools
109 // directory, so it can upgrade itself without needing to change
110@@ -319,7 +329,7 @@
111 // TODO(dfc) ln -nfs, so it doesn't fail if for some reason that the target already exists
112 c.AddScripts(fmt.Sprintf("ln -s %v %s", cfg.Tools.Version, shquote(toolsDir)))
113
114- name := "jujud-" + tag
115+ name := machineAgentServiceName(tag)
116 conf := upstart.MachineAgentUpstartService(name, toolsDir, cfg.DataDir, "/var/log/juju/", tag, machineId, nil)
117 cmds, err := conf.InstallCommands()
118 if err != nil {
119@@ -340,7 +350,8 @@
120 "dd bs=1M count=1 if=/dev/zero of="+dbDir+"/journal/prealloc.2",
121 )
122
123- conf := upstart.MongoUpstartService("juju-db", cfg.DataDir, dbDir, cfg.StatePort)
124+ name := mongoServiceName
125+ conf := upstart.MongoUpstartService(name, cfg.DataDir, dbDir, cfg.StatePort)
126 cmds, err := conf.InstallCommands()
127 if err != nil {
128 return fmt.Errorf("cannot make cloud-init upstart script for the state database: %v", err)
129
130=== modified file 'environs/cloudinit/cloudinit_test.go'
131--- environs/cloudinit/cloudinit_test.go 2013-11-19 00:18:18 +0000
132+++ environs/cloudinit/cloudinit_test.go 2013-11-20 02:15:23 +0000
133@@ -17,9 +17,11 @@
134 "launchpad.net/juju-core/environs"
135 "launchpad.net/juju-core/environs/cloudinit"
136 "launchpad.net/juju-core/environs/config"
137+ "launchpad.net/juju-core/names"
138 "launchpad.net/juju-core/state"
139 "launchpad.net/juju-core/state/api"
140 "launchpad.net/juju-core/testing"
141+ jc "launchpad.net/juju-core/testing/checkers"
142 "launchpad.net/juju-core/testing/testbase"
143 "launchpad.net/juju-core/tools"
144 "launchpad.net/juju-core/version"
145@@ -324,6 +326,21 @@
146 return tools
147 }
148
149+func getAgentConfig(c *gc.C, tag string, scripts []string) (cfg string) {
150+ re := regexp.MustCompile(`printf '%s\\n' '([^']+)' > .*agents/` + regexp.QuoteMeta(tag) + `/agent\.conf`)
151+ found := false
152+ for _, s := range scripts {
153+ m := re.FindStringSubmatch(s)
154+ if m == nil {
155+ continue
156+ }
157+ cfg = m[1]
158+ found = true
159+ }
160+ c.Assert(found, gc.Equals, true)
161+ return cfg
162+}
163+
164 // check that any --env-config $base64 is valid and matches t.cfg.Config
165 func checkEnvConfig(c *gc.C, cfg *config.Config, x map[interface{}]interface{}, scripts []string) {
166 re := regexp.MustCompile(`--env-config '([^']+)'`)
167@@ -376,10 +393,16 @@
168 checkEnvConfig(c, test.cfg.Config, x, scripts)
169 }
170 checkPackage(c, x, "git", true)
171+ tag := names.MachineTag(test.cfg.MachineId)
172+ acfg := getAgentConfig(c, tag, scripts)
173+ c.Assert(acfg, jc.Contains, "AGENT_SERVICE_NAME: jujud-"+tag)
174 if test.cfg.StateServer {
175 checkPackage(c, x, "mongodb-server", true)
176 source := "ppa:juju/stable"
177 checkAptSource(c, x, source, "", test.cfg.NeedMongoPPA())
178+ c.Assert(acfg, jc.Contains, "MONGO_SERVICE_NAME: juju-db")
179+ } else {
180+ c.Assert(acfg, gc.Not(jc.Contains), "MONGO_SERVICE_NAME")
181 }
182 source := "deb http://ubuntu-cloud.archive.canonical.com/ubuntu precise-updates/cloud-tools main"
183 needCloudArchive := test.cfg.Tools.Version.Series == "precise"
184
185=== modified file 'provider/local/environ.go'
186--- provider/local/environ.go 2013-11-18 05:41:36 +0000
187+++ provider/local/environ.go 2013-11-20 02:15:23 +0000
188@@ -507,6 +507,8 @@
189 agent.StorageAddr: env.config.storageAddr(),
190 agent.SharedStorageDir: env.config.sharedStorageDir(),
191 agent.SharedStorageAddr: env.config.sharedStorageAddr(),
192+ agent.AgentServiceName: env.machineAgentServiceName(),
193+ agent.MongoServiceName: env.mongoServiceName(),
194 }
195 // NOTE: the state address HAS to be localhost, otherwise the mongo
196 // initialization fails. There is some magic code somewhere in the mongo

Subscribers

People subscribed via source and target branches

to status/vote changes: