~ubuntu-branches/ubuntu/saucy/juju-core/saucy

« back to all changes in this revision

Viewing changes to src/launchpad.net/juju-core/cmd/jujud/upgradevalidation.go

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2013-08-20 16:02:16 UTC
  • mfrom: (1.1.5)
  • Revision ID: package-import@ubuntu.com-20130820160216-5yu1llasa2e2youn
Tags: 1.13.1-0ubuntu1
* New upstream release.
  - Build and install juju metadata plugin.
  - d/NEWS: Add some guidance on upgrading environments from 1.11.x
    to 1.13.x.
* d/NEWS: Add details about lack of upgrade path from juju < 1.11
  and how to interact with older juju environments.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
// Copyright 2012, 2013 Canonical Ltd.
2
 
// Licensed under the AGPLv3, see LICENCE file for details.
3
 
 
4
 
package main
5
 
 
6
 
import (
7
 
        "os"
8
 
        "path/filepath"
9
 
        "time"
10
 
 
11
 
        "launchpad.net/loggo"
12
 
 
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"
21
 
)
22
 
 
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.
25
 
 
26
 
var validationLogger = loggo.GetLogger("juju.jujud.validation")
27
 
 
28
 
var lockTimeout = 1 * time.Minute
29
 
 
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")
35
 
        if err != nil {
36
 
                return nil, err
37
 
        }
38
 
        err = hookLock.LockWithTimeout(lockTimeout, message)
39
 
        if err != nil {
40
 
                return nil, err
41
 
        }
42
 
        return hookLock, nil
43
 
}
44
 
 
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 {
66
 
                return nil
67
 
        }
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
72
 
                return nil
73
 
        }
74
 
        validationLogger.Debugf("got error looking for lxc, attempting to install")
75
 
        if dataDir != "" {
76
 
                lock, err := getUniterLock(dataDir, "apt-get install lxc for juju 1.11 upgrade")
77
 
                if err == nil {
78
 
                        defer lock.Unlock()
79
 
                } else {
80
 
                        validationLogger.Warningf("Failed to acquire lock: %v, will try to install lxc anyway", lock)
81
 
                }
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
84
 
                // are already
85
 
        }
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")
93
 
}
94
 
 
95
 
type passwordSetter interface {
96
 
        SetPassword(password string) error
97
 
}
98
 
 
99
 
// apiAddresser just implements APIAddresses
100
 
// This is implemented by the standard *state.State object
101
 
type apiAddresser interface {
102
 
        APIAddresses() ([]string, error)
103
 
}
104
 
 
105
 
func apiInfoFromStateInfo(stInfo *state.Info, stConn apiAddresser) *api.Info {
106
 
        addrs, err := stConn.APIAddresses()
107
 
        if err != nil {
108
 
                validationLogger.Warningf("Unable to get the list of valid API Addresses: %v", err)
109
 
        }
110
 
        return &api.Info{
111
 
                Addrs:    addrs,
112
 
                CACert:   stInfo.CACert,
113
 
                Tag:      stInfo.Tag,
114
 
                Password: stInfo.Password,
115
 
        }
116
 
}
117
 
 
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
136
 
                return nil
137
 
        }
138
 
        setter, ok := agentConn.(passwordSetter)
139
 
        if !ok {
140
 
                panic("AgentState is missing a SetPassword method?")
141
 
        }
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}",
147
 
                        conf.APIInfo.Addrs,
148
 
                        conf.APIInfo.Tag,
149
 
                )
150
 
        } else {
151
 
                validationLogger.Infof("agent.conf APIInfo password is not set. Setting to state password")
152
 
                conf.APIInfo.Password = conf.StateInfo.Password
153
 
        }
154
 
        password := conf.StateInfo.Password
155
 
        if 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",
164
 
                        conf.APIInfo.Tag)
165
 
                password = conf.OldPassword
166
 
        }
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 {
170
 
                return err
171
 
        }
172
 
        if err := conf.Write(); err != nil {
173
 
                return err
174
 
        }
175
 
        return nil
176
 
}