Merge lp:~jameinel/juju-core/api-connect-upgrade-1199915 into lp:~go-bot/juju-core/trunk

Proposed by John A Meinel
Status: Merged
Approved by: John A Meinel
Approved revision: no longer in the source branch.
Merged at revision: 1435
Proposed branch: lp:~jameinel/juju-core/api-connect-upgrade-1199915
Merge into: lp:~go-bot/juju-core/trunk
Diff against target: 191 lines (+166/-0)
4 files modified
cmd/jujud/machine.go (+4/-0)
cmd/jujud/upgradevalidation.go (+73/-0)
utils/apt.go (+45/-0)
utils/apt_test.go (+44/-0)
To merge this branch: bzr merge lp:~jameinel/juju-core/api-connect-upgrade-1199915
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+174207@code.launchpad.net

Commit message

cmd/jujud/machineagent: install LXC on start

Related to bug #1199913. On startup we will 'apt-get install lxc' if we
need to. This uses the containers/lxc code to detect if we have lxc
available, and if it fails then we request to install it.

I've tested this live, and it does solve the immediate problem.

Also, the test suite still passes because all the things that call
MachineAgent.Run are already using the lxc.TestSuite and so the
containers code "provides" lxc via the MockFactory.

One test we could do is that we try to install lxc if we run
the machine agent (and inject a MockFactory that fails the List
request). But I felt live testing was appropriate for this.

https://codereview.appspot.com/10986044/

Description of the change

cmd/jujud/machineagent: install LXC on start

Related to bug #1199913. On startup we will 'apt-get install lxc' if we
need to. This uses the containers/lxc code to detect if we have lxc
available, and if it fails then we request to install it.

I've tested this live, and it does solve the immediate problem.

Also, the test suite still passes because all the things that call
MachineAgent.Run are already using the lxc.TestSuite and so the
containers code "provides" lxc via the MockFactory.

One test we could do is that we try to install lxc if we run
the machine agent (and inject a MockFactory that fails the List
request). But I felt live testing was appropriate for this.

https://codereview.appspot.com/10986044/

To post a comment you must log in.
Revision history for this message
John A Meinel (jameinel) wrote :

Reviewers: mp+174207_code.launchpad.net,

Message:
Please take a look.

Description:
cmd/jujud/machineagent: install LXC on start

Related to bug #1199913. On startup we will 'apt-get install lxc' if we
need to. This uses the containers/lxc code to detect if we have lxc
available, and if it fails then we request to install it.

I've tested this live, and it does solve the immediate problem.

Also, the test suite still passes because all the things that call
MachineAgent.Run are already using the lxc.TestSuite and so the
containers code "provides" lxc via the MockFactory.

One test we could do is that we try to install lxc if we run
the machine agent (and inject a MockFactory that fails the List
request). But I felt live testing was appropriate for this.

https://code.launchpad.net/~jameinel/juju-core/api-connect-upgrade-1199915/+merge/174207

(do not edit description out of merge proposal)

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

Affected files:
   A [revision details]
   M cmd/jujud/machine.go
   A cmd/jujud/upgradevalidation.go
   A utils/apt.go
   A utils/apt_test.go

Revision history for this message
William Reade (fwereade) wrote :

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

https://codereview.appspot.com/10986044/diff/1/cmd/jujud/machine.go#newcode82
cmd/jujud/machine.go:82: log.Errorf("we were unable to install the lxc
package, unable to continue: %v", err)
Quibble quibble waffle waffle, this will break on other platforms.
Unbreaks upgrade for now though, so fair enough.

https://codereview.appspot.com/10986044/diff/1/utils/apt.go
File utils/apt.go (right):

https://codereview.appspot.com/10986044/diff/1/utils/apt.go#newcode18
utils/apt.go:18: // apt concurrently, we should use that same
locking here
We already have that. uniter.go:116

https://codereview.appspot.com/10986044/

Revision history for this message
Dimiter Naydenov (dimitern) wrote :

LGTM

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

https://codereview.appspot.com/10986044/diff/1/cmd/jujud/machine.go#newcode82
cmd/jujud/machine.go:82: log.Errorf("we were unable to install the lxc
package, unable to continue: %v", err)
On 2013/07/11 14:03:11, fwereade wrote:
> Quibble quibble waffle waffle, this will break on other platforms.
Unbreaks
> upgrade for now though, so fair enough.

Add TODO describing this please.

https://codereview.appspot.com/10986044/

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

LGTM with verbose warnings; I would *really* appreciate a followup that
somehow tested this situation live, but I'm at a little bit of a loss
how to do that sanely.

https://codereview.appspot.com/10986044/diff/5002/cmd/jujud/upgradevalidation.go
File cmd/jujud/upgradevalidation.go (right):

https://codereview.appspot.com/10986044/diff/5002/cmd/jujud/upgradevalidation.go#newcode30
cmd/jujud/upgradevalidation.go:30: // Note: Should we try to do the
install anyway, just without the lock?
Yeah, I think so. Log a great big warning and see what you can do. If we
fail, we bomb out, and upstart gives us another try; eventually we'll
mess with the unit badly enough that we succeed, I suspect, and then we
have a working machine again... and at least the unit is somewhat
user-fixable.

https://codereview.appspot.com/10986044/

Revision history for this message
Dimiter Naydenov (dimitern) wrote :

LGTM, just remove a TODO about locks.

https://codereview.appspot.com/10986044/diff/5002/utils/apt.go
File utils/apt.go (right):

https://codereview.appspot.com/10986044/diff/5002/utils/apt.go#newcode17
utils/apt.go:17: // TODO: When we have a unit-level lock to avoid
multiple unit agents running
delete this now?

https://codereview.appspot.com/10986044/

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-07-11 12:11:14 +0000
3+++ cmd/jujud/machine.go 2013-07-11 15:53:26 +0000
4@@ -80,6 +80,10 @@
5 if err := a.Conf.read(a.Tag()); err != nil {
6 return err
7 }
8+ if err := EnsureWeHaveLXC(a.Conf.DataDir); err != nil {
9+ log.Errorf("we were unable to install the lxc package, unable to continue: %v", err)
10+ return err
11+ }
12 charm.CacheDir = filepath.Join(a.Conf.DataDir, "charmcache")
13
14 // ensureStateWorker ensures that there is a worker that
15
16=== added file 'cmd/jujud/upgradevalidation.go'
17--- cmd/jujud/upgradevalidation.go 1970-01-01 00:00:00 +0000
18+++ cmd/jujud/upgradevalidation.go 2013-07-11 15:53:26 +0000
19@@ -0,0 +1,73 @@
20+// Copyright 2012, 2013 Canonical Ltd.
21+// Licensed under the AGPLv3, see LICENCE file for details.
22+
23+package main
24+
25+import (
26+ "path/filepath"
27+ "time"
28+
29+ "launchpad.net/loggo"
30+
31+ "launchpad.net/juju-core/container/lxc"
32+ "launchpad.net/juju-core/utils"
33+ "launchpad.net/juju-core/utils/fslock"
34+)
35+
36+// Functions in this package are here to help us with post-upgrade installing,
37+// etc. It is used to validate the system status after we have upgraded.
38+
39+var validationLogger = loggo.GetLogger("juju.jujud.validation")
40+
41+var lockTimeout = 1 * time.Minute
42+
43+// getUniterLock grabs the "uniter-hook-execution" lock and holds it, or an error
44+func getUniterLock(dataDir, message string) (*fslock.Lock, error) {
45+ // Taken from worker/uniter/uniter.go setupLocks()
46+ lockDir := filepath.Join(dataDir, "locks")
47+ hookLock, err := fslock.NewLock(lockDir, "uniter-hook-execution")
48+ if err != nil {
49+ return nil, err
50+ }
51+ err = hookLock.LockWithTimeout(lockTimeout, message)
52+ if err != nil {
53+ return nil, err
54+ }
55+ return hookLock, nil
56+}
57+
58+// EnsureWeHaveLXC checks if we have lxc installed, and installs it if we
59+// don't. Juju 1.11 added the ability to deploy into LXC containers, and uses
60+// functionality from the lxc package in order to do so. Juju 1.10 did not
61+// install lxc, so we ensure it is installed.
62+// See http://bugs.launchpad.net/bug/1199913
63+// dataDir is the root location where data files are put. It is used to grab
64+// the uniter-hook-execution lock so that we don't try run to apt-get at the
65+// same time that a hook might want to run it.
66+func EnsureWeHaveLXC(dataDir string) error {
67+ manager := lxc.NewContainerManager("lxc-test")
68+ if _, err := manager.ListContainers(); err == nil {
69+ validationLogger.Debugf("found lxc, not installing")
70+ // We already have it, nothing more to do
71+ return nil
72+ }
73+ validationLogger.Debugf("got error looking for lxc, attempting to install")
74+ if dataDir != "" {
75+ lock, err := getUniterLock(dataDir, "apt-get install lxc for juju 1.11 upgrade")
76+ if err == nil {
77+ defer lock.Unlock()
78+ } else {
79+ validationLogger.Warningf("Failed to acquire lock: %v, will try to install lxc anyway", lock)
80+ }
81+ // If we got an error trying to acquire the lock, we try to install
82+ // lxc anyway. Worst case the install will fail, which is where we
83+ // are already
84+ }
85+ // TODO: This is not platform independent. If jujud is running on
86+ // something other than a debian-based Linux, and we are missing
87+ // lxc, this call will always fail. However, in juju 1.11+ we
88+ // install lxc via cloud-init or whatever bootstrap code we use.
89+ // So this is really only upgrade compatibility and juju 1.10
90+ // only supports debian-based anyway
91+ return utils.AptGetInstall("lxc")
92+}
93
94=== added file 'utils/apt.go'
95--- utils/apt.go 1970-01-01 00:00:00 +0000
96+++ utils/apt.go 2013-07-11 15:53:26 +0000
97@@ -0,0 +1,45 @@
98+// Copyright 2012, 2013 Canonical Ltd.
99+// Licensed under the AGPLv3, see LICENCE file for details.
100+
101+package utils
102+
103+import (
104+ "os"
105+ "os/exec"
106+
107+ "launchpad.net/loggo"
108+)
109+
110+var aptLogger = loggo.GetLogger("juju.utils.apt")
111+
112+// Some helpful functions for running apt in a sane way
113+
114+// osRunCommand calls cmd.Run, this is used as an overloading point so we can
115+// test what *would* be run without actually executing another program
116+func osRunCommand(cmd *exec.Cmd) error {
117+ return cmd.Run()
118+}
119+
120+var runCommand = osRunCommand
121+
122+// This is the default apt-get command used in cloud-init, the various settings
123+// mean that apt won't actually block waiting for a prompt from the user.
124+var aptGetCommand = []string{
125+ "apt-get", "--option=Dpkg::Options::=--force-confold",
126+ "--option=Dpkg::options::=--force-unsafe-io", "--assume-yes", "--quiet",
127+}
128+
129+// aptEnvOptions are options we need to pass to apt-get to not have it prompt
130+// the user
131+var aptGetEnvOptions = []string{"DEBIAN_FRONTEND=noninteractive"}
132+
133+// AptGetInstall runs 'apt-get install packages' for the packages listed here
134+func AptGetInstall(packages ...string) error {
135+ cmdArgs := append([]string(nil), aptGetCommand...)
136+ cmdArgs = append(cmdArgs, "install")
137+ cmdArgs = append(cmdArgs, packages...)
138+ aptLogger.Infof("Running: %s", cmdArgs)
139+ cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...)
140+ cmd.Env = append(os.Environ(), aptGetEnvOptions...)
141+ return runCommand(cmd)
142+}
143
144=== added file 'utils/apt_test.go'
145--- utils/apt_test.go 1970-01-01 00:00:00 +0000
146+++ utils/apt_test.go 2013-07-11 15:53:26 +0000
147@@ -0,0 +1,44 @@
148+// Copyright 2012, 2013 Canonical Ltd.
149+// Licensed under the AGPLv3, see LICENCE file for details.
150+
151+package utils
152+
153+import (
154+ "os/exec"
155+
156+ gc "launchpad.net/gocheck"
157+)
158+
159+type AptSuite struct{}
160+
161+var _ = gc.Suite(&AptSuite{})
162+
163+// hookRunCommand intercepts runCommand to a function that passes the actual
164+// command back via a channel, and returns the error passed into this function.
165+// It also returns a cleanup function so you can restore the original function
166+func (s *AptSuite) hookRunCommand(err error) (<-chan *exec.Cmd, func()) {
167+ cmdChan := make(chan *exec.Cmd, 1)
168+ origRunCommand := runCommand
169+ cleanup := func() {
170+ runCommand = origRunCommand
171+ }
172+ runCommand = func(cmd *exec.Cmd) error {
173+ cmdChan <- cmd
174+ return err
175+ }
176+ return cmdChan, cleanup
177+}
178+
179+func (s *AptSuite) TestOnePackage(c *gc.C) {
180+ cmdChan, cleanup := s.hookRunCommand(nil)
181+ defer cleanup()
182+ err := AptGetInstall("test-package")
183+ c.Assert(err, gc.IsNil)
184+ cmd := <-cmdChan
185+ c.Assert(cmd.Args, gc.DeepEquals, []string{
186+ "apt-get", "--option=Dpkg::Options::=--force-confold",
187+ "--option=Dpkg::options::=--force-unsafe-io", "--assume-yes", "--quiet",
188+ "install", "test-package",
189+ })
190+ c.Assert(cmd.Env[len(cmd.Env)-1], gc.Equals, "DEBIAN_FRONTEND=noninteractive")
191+}

Subscribers

People subscribed via source and target branches

to status/vote changes: