Merge lp:~thumper/juju-core/juju-run-on-machine-with-no-units into lp:~go-bot/juju-core/trunk

Proposed by Tim Penhey
Status: Merged
Approved by: Tim Penhey
Approved revision: no longer in the source branch.
Merged at revision: 2319
Proposed branch: lp:~thumper/juju-core/juju-run-on-machine-with-no-units
Merge into: lp:~go-bot/juju-core/trunk
Diff against target: 348 lines (+222/-7)
7 files modified
environs/cloudinit/cloudinit.go (+9/-1)
environs/cloudinit/cloudinit_test.go (+4/-2)
upgrades/export_test.go (+7/-1)
upgrades/steps118.go (+44/-2)
upgrades/steps118_test.go (+117/-0)
upgrades/upgrade.go (+36/-1)
upgrades/upgrade_test.go (+5/-0)
To merge this branch: bzr merge lp:~thumper/juju-core/juju-run-on-machine-with-no-units
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+206050@code.launchpad.net

Commit message

Fix the juju-run on machines with no units.

This branch also adds an upgrade step to make sure
that existing environments also get the goodness.

https://codereview.appspot.com/62390043/

Description of the change

Fix the juju-run on machines with no units.

This branch also adds an upgrade step to make sure
that existing environments also get the goodness.

https://codereview.appspot.com/62390043/

To post a comment you must log in.
Revision history for this message
Tim Penhey (thumper) wrote :

Reviewers: mp+206050_code.launchpad.net,

Message:
Please take a look.

Description:
Fix the juju-run on machines with no units.

This branch also adds an upgrade step to make sure
that existing environments also get the goodness.

https://code.launchpad.net/~thumper/juju-core/juju-run-on-machine-with-no-units/+merge/206050

(do not edit description out of merge proposal)

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

Affected files (+100, -6 lines):
   [revision details]
   environs/cloudinit/cloudinit.go
   environs/cloudinit/cloudinit_test.go
   upgrades/steps118.go
   upgrades/upgrade.go
   upgrades/upgrade_test.go

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

LGTM. We need one more test to ensure the upgrade operation is
registered correctly but I'll add that in my next lot of work as I have
to do it anyway.

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

https://codereview.appspot.com/62390043/diff/1/environs/cloudinit/cloudinit.go#newcode282
environs/cloudinit/cloudinit.go:282:
part of me wishes that "locks" were a const somewhere

https://codereview.appspot.com/62390043/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'environs/cloudinit/cloudinit.go'
2--- environs/cloudinit/cloudinit.go 2014-02-11 16:39:04 +0000
3+++ environs/cloudinit/cloudinit.go 2014-02-13 03:37:16 +0000
4@@ -275,8 +275,16 @@
5 shquote(cfg.ProxySettings.AsScriptEnvironment())))
6 }
7
8+ // Make the lock dir and change the ownership of the lock dir itself to
9+ // ubuntu:ubuntu from root:root so the juju-run command run as the ubuntu
10+ // user is able to get access to the hook execution lock (like the uniter
11+ // itself does.)
12+ lockDir := path.Join(cfg.DataDir, "locks")
13 c.AddScripts(
14- fmt.Sprintf("mkdir -p %s", cfg.DataDir),
15+ fmt.Sprintf("mkdir -p %s", lockDir),
16+ // We only try to change ownership if there is an ubuntu user
17+ // defined, and we determine this by the existance of the home dir.
18+ fmt.Sprintf("[ -e /home/ubuntu ] && chown ubuntu:ubuntu %s", lockDir),
19 fmt.Sprintf("mkdir -p %s", cfg.LogDir),
20 )
21
22
23=== modified file 'environs/cloudinit/cloudinit_test.go'
24--- environs/cloudinit/cloudinit_test.go 2014-02-11 16:39:04 +0000
25+++ environs/cloudinit/cloudinit_test.go 2014-02-13 03:37:16 +0000
26@@ -100,7 +100,8 @@
27 printf '%s\\n' 'FAKE_NONCE' > '/var/lib/juju/nonce.txt'
28 test -e /proc/self/fd/9 \|\| exec 9>&2
29 \(\[ ! -e /home/ubuntu/.profile \] \|\| grep -q '.juju-proxy' /home/ubuntu/.profile\) \|\| printf .* >> /home/ubuntu/.profile
30-mkdir -p /var/lib/juju
31+mkdir -p /var/lib/juju/locks
32+\[ -e /home/ubuntu \] && chown ubuntu:ubuntu /var/lib/juju/locks
33 mkdir -p /var/log/juju
34 echo 'Fetching tools.*
35 bin='/var/lib/juju/tools/1\.2\.3-precise-amd64'
36@@ -227,7 +228,8 @@
37 printf '%s\\n' 'FAKE_NONCE' > '/var/lib/juju/nonce.txt'
38 test -e /proc/self/fd/9 \|\| exec 9>&2
39 \(\[ ! -e /home/ubuntu/\.profile \] \|\| grep -q '.juju-proxy' /home/ubuntu/.profile\) \|\| printf .* >> /home/ubuntu/.profile
40-mkdir -p /var/lib/juju
41+mkdir -p /var/lib/juju/locks
42+\[ -e /home/ubuntu \] && chown ubuntu:ubuntu /var/lib/juju/locks
43 mkdir -p /var/log/juju
44 echo 'Fetching tools.*
45 bin='/var/lib/juju/tools/1\.2\.3-linux-amd64'
46
47=== modified file 'upgrades/export_test.go'
48--- upgrades/export_test.go 2014-02-10 05:34:06 +0000
49+++ upgrades/export_test.go 2014-02-13 03:37:16 +0000
50@@ -3,4 +3,10 @@
51
52 package upgrades
53
54-var UpgradeOperations = &upgradeOperations
55+var (
56+ UpgradeOperations = &upgradeOperations
57+
58+ UbuntuHome = &ubuntuHome
59+
60+ EnsureLockDirExistsAndUbuntuWritable = ensureLockDirExistsAndUbuntuWritable
61+)
62
63=== modified file 'upgrades/steps118.go'
64--- upgrades/steps118.go 2014-02-10 05:34:06 +0000
65+++ upgrades/steps118.go 2014-02-13 03:37:16 +0000
66@@ -3,9 +3,51 @@
67
68 package upgrades
69
70+import (
71+ "fmt"
72+ "path"
73+
74+ "launchpad.net/juju-core/utils/exec"
75+)
76+
77 // stepsFor118 returns upgrade steps to upgrade to a Juju 1.18 deployment.
78 func stepsFor118(context Context) []UpgradeStep {
79 return []UpgradeStep{
80- // Nothing yet.
81- }
82+ // Nothing yet.
83+ &upgradeStep{
84+ description: "make $DATADIR/locks owned by ubuntu:ubuntu",
85+ targets: []UpgradeTarget{HostMachine},
86+ run: ensureLockDirExistsAndUbuntuWritable,
87+ },
88+ }
89+}
90+
91+var ubuntuHome = "/home/ubuntu"
92+
93+// Previously the lock directory was created when the uniter started. This
94+// allows serialization of all of the hook execution across units running on a
95+// single machine. This lock directory is now also used but the juju-run
96+// command on the host machine. juju-run also gets a lock on the hook
97+// execution fslock prior to execution. However, the lock directory was owned
98+// by root, and the juju-run process was being executed by the ubuntu user, so
99+// we need to change the ownership of the lock directory to ubuntu:ubuntu.
100+// Also we need to make sure that this directory exists on machines with no
101+// units.
102+func ensureLockDirExistsAndUbuntuWritable(context Context) error {
103+ lockDir := path.Join(context.AgentConfig().DataDir(), "locks")
104+ // We only try to change ownership if there is an ubuntu user
105+ // defined, and we determine this by the existance of the home dir.
106+ command := fmt.Sprintf(""+
107+ "mkdir -p %s\n"+
108+ "[ -e %s ] && chown ubuntu:ubuntu %s\n",
109+ lockDir, ubuntuHome, lockDir)
110+ logger.Tracef("command: %s", command)
111+ result, err := exec.RunCommands(exec.RunParams{
112+ Commands: command,
113+ })
114+ if err != nil {
115+ return err
116+ }
117+ logger.Tracef("stdout: %s", result.Stdout)
118+ return nil
119 }
120
121=== added file 'upgrades/steps118_test.go'
122--- upgrades/steps118_test.go 1970-01-01 00:00:00 +0000
123+++ upgrades/steps118_test.go 2014-02-13 03:37:16 +0000
124@@ -0,0 +1,117 @@
125+// Copyright 2014 Canonical Ltd.
126+// Licensed under the AGPLv3, see LICENCE file for details.
127+
128+package upgrades_test
129+
130+import (
131+ "fmt"
132+ "io/ioutil"
133+ "os"
134+ "path/filepath"
135+
136+ "github.com/loggo/loggo"
137+ gc "launchpad.net/gocheck"
138+
139+ "launchpad.net/juju-core/agent"
140+ "launchpad.net/juju-core/state/api"
141+ "launchpad.net/juju-core/testing"
142+ jc "launchpad.net/juju-core/testing/checkers"
143+ "launchpad.net/juju-core/upgrades"
144+)
145+
146+type ensureLockDirSuite struct {
147+ testing.FakeHomeSuite
148+ bin string
149+ home string
150+ datadir string
151+ lockdir string
152+ ctx upgrades.Context
153+}
154+
155+var _ = gc.Suite(&ensureLockDirSuite{})
156+
157+// fakecommand outputs its arguments to stdout for verification
158+var fakecommand = `#!/bin/bash
159+
160+echo $@ | tee $0.args
161+`
162+
163+func (s *ensureLockDirSuite) SetUpTest(c *gc.C) {
164+ s.FakeHomeSuite.SetUpTest(c)
165+
166+ s.bin = c.MkDir()
167+ s.PatchEnvironment("PATH", s.bin+":"+os.Getenv("PATH"))
168+
169+ err := ioutil.WriteFile(
170+ filepath.Join(s.bin, "chown"),
171+ []byte(fakecommand), 0777)
172+ c.Assert(err, gc.IsNil)
173+
174+ loggo.GetLogger("juju.upgrade").SetLogLevel(loggo.TRACE)
175+
176+ s.home = c.MkDir()
177+ s.PatchValue(upgrades.UbuntuHome, s.home)
178+
179+ s.datadir = c.MkDir()
180+ s.lockdir = filepath.Join(s.datadir, "locks")
181+ s.ctx = &ensureContext{&mockAgentConfig{datadir: s.datadir}}
182+}
183+
184+func (s *ensureLockDirSuite) assertChownCalled(c *gc.C) {
185+ bytes, err := ioutil.ReadFile(filepath.Join(s.bin, "chown.args"))
186+ c.Assert(err, gc.IsNil)
187+ c.Assert(string(bytes), gc.Equals, fmt.Sprintf("ubuntu:ubuntu %s\n", s.lockdir))
188+}
189+
190+func (s *ensureLockDirSuite) assertNoChownCalled(c *gc.C) {
191+ c.Assert(filepath.Join(s.bin, "chown.args"), jc.DoesNotExist)
192+}
193+
194+func (s *ensureLockDirSuite) TestLockDirCreated(c *gc.C) {
195+ err := upgrades.EnsureLockDirExistsAndUbuntuWritable(s.ctx)
196+ c.Assert(err, gc.IsNil)
197+
198+ c.Assert(s.lockdir, jc.IsDirectory)
199+ s.assertChownCalled(c)
200+}
201+
202+func (s *ensureLockDirSuite) TestIdempotent(c *gc.C) {
203+ err := upgrades.EnsureLockDirExistsAndUbuntuWritable(s.ctx)
204+ c.Assert(err, gc.IsNil)
205+
206+ err = upgrades.EnsureLockDirExistsAndUbuntuWritable(s.ctx)
207+ c.Assert(err, gc.IsNil)
208+
209+ c.Assert(s.lockdir, jc.IsDirectory)
210+ s.assertChownCalled(c)
211+}
212+
213+func (s *ensureLockDirSuite) TestNoChownIfNoHome(c *gc.C) {
214+ s.PatchValue(upgrades.UbuntuHome, filepath.Join(s.home, "not-exist"))
215+ err := upgrades.EnsureLockDirExistsAndUbuntuWritable(s.ctx)
216+ c.Assert(err, gc.IsNil)
217+
218+ c.Assert(s.lockdir, jc.IsDirectory)
219+ s.assertNoChownCalled(c)
220+}
221+
222+type ensureContext struct {
223+ agentConfig *mockAgentConfig
224+}
225+
226+func (c *ensureContext) APIState() *api.State {
227+ return nil
228+}
229+
230+func (c *ensureContext) AgentConfig() agent.Config {
231+ return c.agentConfig
232+}
233+
234+type mockAgentConfig struct {
235+ agent.Config
236+ datadir string
237+}
238+
239+func (mock *mockAgentConfig) DataDir() string {
240+ return mock.datadir
241+}
242
243=== modified file 'upgrades/upgrade.go'
244--- upgrades/upgrade.go 2014-02-12 10:29:45 +0000
245+++ upgrades/upgrade.go 2014-02-13 03:37:16 +0000
246@@ -6,10 +6,15 @@
247 import (
248 "fmt"
249
250+ "github.com/loggo/loggo"
251+
252+ "launchpad.net/juju-core/agent"
253 "launchpad.net/juju-core/state/api"
254 "launchpad.net/juju-core/version"
255 )
256
257+var logger = loggo.GetLogger("juju.upgrade")
258+
259 // UpgradeStep defines an idempotent operation that is run to perform
260 // a specific upgrade step.
261 type UpgradeStep interface {
262@@ -41,6 +46,7 @@
263
264 const (
265 // HostMachine is a machine on which units are deployed.
266+ // all machines?
267 HostMachine = UpgradeTarget("hostMachine")
268
269 // StateServer is a machine participating in a Juju state server cluster.
270@@ -69,6 +75,8 @@
271 type Context interface {
272 // APIState returns an API connection to state.
273 APIState() *api.State
274+ // AgentConfig returns the agent config for the machine that is being upgraded.
275+ AgentConfig() agent.Config
276 }
277
278 // UpgradeContext is a default Context implementation.
279@@ -76,7 +84,8 @@
280 // Work in progress........
281 // Exactly what a context needs is to be determined as the
282 // implementation evolves.
283- st *api.State
284+ st *api.State
285+ agentConfig agent.Config
286 }
287
288 // APIState is defined on the Context interface.
289@@ -84,6 +93,11 @@
290 return c.st
291 }
292
293+// AgentConfig is defined on the Context interface.
294+func (c *UpgradeContext) AgentConfig() agent.Config {
295+ return c.agentConfig
296+}
297+
298 // upgradeOperation provides base attributes for any upgrade step.
299 type upgradeOperation struct {
300 description string
301@@ -158,3 +172,24 @@
302 }
303 return nil
304 }
305+
306+type upgradeStep struct {
307+ description string
308+ targets []UpgradeTarget
309+ run func(Context) error
310+}
311+
312+// Description is defined on the UpgradeStep interface.
313+func (step *upgradeStep) Description() string {
314+ return step.description
315+}
316+
317+// Targets is defined on the UpgradeStep interface.
318+func (step *upgradeStep) Targets() []UpgradeTarget {
319+ return step.targets
320+}
321+
322+// Run is defined on the UpgradeStep interface.
323+func (step *upgradeStep) Run(context Context) error {
324+ return step.run(context)
325+}
326
327=== modified file 'upgrades/upgrade_test.go'
328--- upgrades/upgrade_test.go 2014-02-12 07:24:28 +0000
329+++ upgrades/upgrade_test.go 2014-02-13 03:37:16 +0000
330@@ -10,6 +10,7 @@
331
332 gc "launchpad.net/gocheck"
333
334+ "launchpad.net/juju-core/agent"
335 "launchpad.net/juju-core/state/api"
336 jc "launchpad.net/juju-core/testing/checkers"
337 "launchpad.net/juju-core/testing/testbase"
338@@ -70,6 +71,10 @@
339 return nil
340 }
341
342+func (c *mockContext) AgentConfig() agent.Config {
343+ return nil
344+}
345+
346 func targets(targets ...upgrades.UpgradeTarget) (upgradeTargets []upgrades.UpgradeTarget) {
347 for _, t := range targets {
348 upgradeTargets = append(upgradeTargets, t)

Subscribers

People subscribed via source and target branches

to status/vote changes: