1
// Copyright 2012, 2013 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
13
"launchpad.net/juju-core/agent"
14
"launchpad.net/juju-core/container/lxc"
15
"launchpad.net/juju-core/environs/provider"
16
"launchpad.net/juju-core/instance"
17
"launchpad.net/juju-core/state"
18
"launchpad.net/juju-core/state/api"
19
"launchpad.net/juju-core/utils"
20
"launchpad.net/juju-core/utils/fslock"
23
// Functions in this package are here to help us with post-upgrade installing,
24
// etc. It is used to validate the system status after we have upgraded.
26
var validationLogger = loggo.GetLogger("juju.jujud.validation")
28
var lockTimeout = 1 * time.Minute
30
// getUniterLock grabs the "uniter-hook-execution" lock and holds it, or an error
31
func getUniterLock(dataDir, message string) (*fslock.Lock, error) {
32
// Taken from worker/uniter/uniter.go setupLocks()
33
lockDir := filepath.Join(dataDir, "locks")
34
hookLock, err := fslock.NewLock(lockDir, "uniter-hook-execution")
38
err = hookLock.LockWithTimeout(lockTimeout, message)
45
// EnsureWeHaveLXC checks if we have lxc installed, and installs it if we
46
// don't. Juju 1.11 added the ability to deploy into LXC containers, and uses
47
// functionality from the lxc package in order to do so. Juju 1.10 did not
48
// install lxc, so we ensure it is installed.
49
// See http://bugs.launchpad.net/bug/1199913
50
// dataDir is the root location where data files are put. It is used to grab
51
// the uniter-hook-execution lock so that we don't try run to apt-get at the
52
// same time that a hook might want to run it.
53
func EnsureWeHaveLXC(dataDir, machineTag string) error {
54
// We need to short circuit this in two places:
55
// 1. if we are running a local provider, then the machines are lxc
56
// containers, and if we install lxc on them, it adds an lxc bridge
57
// network device with the same ip address as the hosts bridge. This
58
// screws up all the networking routes.
59
// 2. if the machine is an lxc container, we need to avoid installing lxc
60
// package for exactly the same reasons.
61
// Later, post-precise LTS, when we have updated lxc, we can bring this
62
// back in to have nested lxc, but until then, we have to avoid it.
63
containerType := state.ContainerTypeFromId(state.MachineIdFromTag(machineTag))
64
providerType := os.Getenv("JUJU_PROVIDER_TYPE")
65
if providerType == provider.Local || containerType == instance.LXC {
68
manager := lxc.NewContainerManager(lxc.ManagerConfig{Name: "lxc-test"})
69
if _, err := manager.ListContainers(); err == nil {
70
validationLogger.Debugf("found lxc, not installing")
71
// We already have it, nothing more to do
74
validationLogger.Debugf("got error looking for lxc, attempting to install")
76
lock, err := getUniterLock(dataDir, "apt-get install lxc for juju 1.11 upgrade")
80
validationLogger.Warningf("Failed to acquire lock: %v, will try to install lxc anyway", lock)
82
// If we got an error trying to acquire the lock, we try to install
83
// lxc anyway. Worst case the install will fail, which is where we
86
// TODO: This is not platform independent. If jujud is running on
87
// something other than a debian-based Linux, and we are missing
88
// lxc, this call will always fail. However, in juju 1.11+ we
89
// install lxc via cloud-init or whatever bootstrap code we use.
90
// So this is really only upgrade compatibility and juju 1.10
91
// only supports debian-based anyway
92
return utils.AptGetInstall("lxc")
95
type passwordSetter interface {
96
SetPassword(password string) error
99
// apiAddresser just implements APIAddresses
100
// This is implemented by the standard *state.State object
101
type apiAddresser interface {
102
APIAddresses() ([]string, error)
105
func apiInfoFromStateInfo(stInfo *state.Info, stConn apiAddresser) *api.Info {
106
addrs, err := stConn.APIAddresses()
108
validationLogger.Warningf("Unable to get the list of valid API Addresses: %v", err)
112
CACert: stInfo.CACert,
114
Password: stInfo.Password,
118
// EnsureAPIInfo makes sure we can connect as an agent to the API server 1.10
119
// did not set an API password for machine agents, 1.11 sets it the same as the
120
// mongo password. 1.10 also does not set any API Info at all for Unit agents.
121
// conf is the agent.conf for this machine/unit agent. agentConn is the direct
122
// connection to the State database
123
func EnsureAPIInfo(conf *agent.Conf, stConn *state.State, agentConn AgentState) error {
124
// In 1.10 Machine Agents will have an API Info section, but will not
125
// have properly configured the Password field, so when upgrading to
126
// 1.11 we must set that field
127
// In 1.10 Unit Agents will not have an API Info section at all, so we
128
// must set everything from the StateInfo (correcting the Addrs for API
129
// port vs Mongo port)
130
// A new 1.11 Unit Agent will have an OldPassword but no Password set
131
// (in StateInfo or APIInfo). Because we only change the password on
132
// API connect, not on DB connect. We don't want to make extra
133
// SetPassword calls when we already have a valid password set
134
if conf.APIInfo != nil && (conf.APIInfo.Password != "" || conf.StateInfo.Password == "") {
135
// We must have set it earlier
138
setter, ok := agentConn.(passwordSetter)
140
panic("AgentState is missing a SetPassword method?")
142
if conf.APIInfo == nil {
143
// Unit agents didn't get any APIInfo in 1.10
144
conf.APIInfo = apiInfoFromStateInfo(conf.StateInfo, stConn)
145
validationLogger.Infof(
146
"agent.conf APIInfo is not set. Setting to {Addrs: %s, Tag: %s}",
151
validationLogger.Infof("agent.conf APIInfo password is not set. Setting to state password")
152
conf.APIInfo.Password = conf.StateInfo.Password
154
password := conf.StateInfo.Password
156
// In 1.11 we don't set a new password on connect (because it is
157
// done in the API code, and we don't have any API workers).
158
// We want to make sure the API user has the correct password
159
// (OldPassword). We *don't* want to set APIInfo.Password
160
// because then when we do connect via the API we wouldn't
161
// change the password. So this is only used for SetPassword
162
validationLogger.Infof(
163
"agent.conf StateInfo password is \"\". Setting Agent password for %s to OldPassword",
165
password = conf.OldPassword
167
// We set the actual password before writing it to disk, because
168
// otherwise we would not set it correctly in the future
169
if err := setter.SetPassword(password); err != nil {
172
if err := conf.Write(); err != nil {