16
20
"launchpad.net/juju-core/instance"
17
21
"launchpad.net/juju-core/state"
18
22
"launchpad.net/juju-core/state/api"
23
"launchpad.net/juju-core/upstart"
24
"launchpad.net/juju-core/utils"
27
// lxcBridgeName is the name of the network interface that the local provider
28
// uses to determine the ip address to use for machine-0 such that the
29
// containers being created are able to communicate with it simply.
30
const lxcBridgeName = "lxcbr0"
32
// upstartScriptLocation is parameterised purely for testing purposes as we
33
// don't really want to be installing and starting scripts as root for
35
var upstartScriptLocation = "/etc/init"
37
// A request may fail to due "eventual consistency" semantics, which
38
// should resolve fairly quickly. A request may also fail due to a slow
39
// state transition (for instance an instance taking a while to release
40
// a security group after termination). The former failure mode is
41
// dealt with by shortAttempt, the latter by longAttempt.
42
var shortAttempt = utils.AttemptStrategy{
43
Total: 1 * time.Second,
44
Delay: 50 * time.Millisecond,
21
47
// localEnviron implements Environ.
22
48
var _ environs.Environ = (*localEnviron)(nil)
63
func (env *localEnviron) mongoServiceName() string {
64
return "juju-db-" + env.config.namespace()
67
// ensureCertOwner checks to make sure that the cert files created
68
// by the bootstrap command are owned by the user and not root.
69
func (env *localEnviron) ensureCertOwner() error {
71
config.JujuHomePath(env.name + "-cert.pem"),
72
config.JujuHomePath(env.name + "-private-key.pem"),
75
uid, gid, err := sudoCallerIds()
79
if uid != 0 || gid != 0 {
80
for _, filename := range files {
81
if err := os.Chown(filename, uid, gid); err != nil {
37
89
// Bootstrap is specified in the Environ interface.
38
90
func (env *localEnviron) Bootstrap(cons constraints.Value) error {
39
return fmt.Errorf("not implemented")
91
logger.Infof("bootstrapping environment %q", env.name)
92
if !env.config.runningAsRoot {
93
return fmt.Errorf("bootstrapping a local environment must be done as root")
95
if err := env.config.createDirs(); err != nil {
96
logger.Errorf("failed to create necessary directories: %v", err)
100
if err := env.ensureCertOwner(); err != nil {
101
logger.Errorf("failed to reassign ownership of the certs to the user: %v", err)
104
// TODO(thumper): check that the constraints don't include "container=lxc" for now.
106
var noRetry = utils.AttemptStrategy{}
107
if err := environs.VerifyBootstrapInit(env, noRetry); err != nil {
111
cert, key, err := env.setupLocalMongoService()
116
// Work out the ip address of the lxc bridge, and use that for the mongo config.
117
bridgeAddress, err := env.findBridgeAddress()
121
logger.Debugf("found %q as address for %q", bridgeAddress, lxcBridgeName)
123
// Before we write the agent config file, we need to make sure the
124
// instance is saved in the StateInfo.
125
bootstrapId := instance.Id("localhost")
126
if err := environs.SaveState(env.Storage(), &environs.BootstrapState{[]instance.Id{bootstrapId}}); err != nil {
127
logger.Errorf("failed to save state instances: %v", err)
131
// Need to write out the agent file for machine-0 before initializing
132
// state, as as part of that process, it will reset the password in the
134
if err := env.writeBootstrapAgentConfFile(cert, key); err != nil {
138
// Have to initialize the state configuration with localhost so we get
139
// "special" permissions.
140
stateConnection, err := env.initialStateConfiguration("localhost", cons)
144
defer stateConnection.Close()
146
// TODO(thumper): upload tools into the storage
148
// TODO(thumper): start the machine agent for machine-0
42
153
// StateInfo is specified in the Environ interface.
43
154
func (env *localEnviron) StateInfo() (*state.Info, *api.Info, error) {
44
return nil, nil, fmt.Errorf("not implemented")
155
return environs.StateInfo(env)
47
158
// Config is specified in the Environ interface.
119
230
// Instances is specified in the Environ interface.
120
231
func (env *localEnviron) Instances(ids []instance.Id) ([]instance.Instance, error) {
121
return nil, fmt.Errorf("not implemented")
232
// NOTE: do we actually care about checking the existance of the instances?
233
// I posit that here we don't really care, and that we are only called with
234
// instance ids that we know exist.
238
insts := make([]instance.Instance, len(ids))
239
for i, id := range ids {
240
insts[i] = &localInstance{id, env}
124
245
// AllInstances is specified in the Environ interface.
125
func (env *localEnviron) AllInstances() ([]instance.Instance, error) {
126
return nil, fmt.Errorf("not implemented")
246
func (env *localEnviron) AllInstances() (instances []instance.Instance, err error) {
247
// TODO(thumper): get all the instances from the container manager
248
instances = append(instances, &localInstance{"localhost", env})
249
return instances, nil
129
252
// Storage is specified in the Environ interface.
139
262
// Destroy is specified in the Environ interface.
140
263
func (env *localEnviron) Destroy(insts []instance.Instance) error {
141
return fmt.Errorf("not implemented")
264
if !env.config.runningAsRoot {
265
return fmt.Errorf("destroying a local environment must be done as root")
268
logger.Infof("removing service %s", env.mongoServiceName())
269
mongo := upstart.NewService(env.mongoServiceName())
270
mongo.InitDir = upstartScriptLocation
271
if err := mongo.Remove(); err != nil {
272
logger.Errorf("could not remove mongo service: %v", err)
276
// Remove the rootdir.
277
logger.Infof("removing state dir %s", env.config.rootDir())
278
if err := os.RemoveAll(env.config.rootDir()); err != nil {
279
logger.Errorf("could not remove local state dir: %v", err)
144
286
// OpenPorts is specified in the Environ interface.
160
302
func (env *localEnviron) Provider() environs.EnvironProvider {
306
// setupLocalMongoService returns the cert and key if there was no error.
307
func (env *localEnviron) setupLocalMongoService() ([]byte, []byte, error) {
308
journalDir := filepath.Join(env.config.mongoDir(), "journal")
309
logger.Debugf("create mongo journal dir: %v", journalDir)
310
if err := os.MkdirAll(journalDir, 0755); err != nil {
311
logger.Errorf("failed to make mongo journal dir %s: %v", journalDir, err)
315
logger.Debugf("generate server cert")
316
cert, key, err := env.config.GenerateStateServerCertAndKey()
318
logger.Errorf("failed to generate server cert: %v", err)
321
if err := ioutil.WriteFile(
322
env.config.configFile("server.pem"),
323
append(cert, key...),
325
logger.Errorf("failed to write server.pem: %v", err)
329
mongo := upstart.MongoUpstartService(
330
env.mongoServiceName(),
331
env.config.rootDir(),
332
env.config.mongoDir(),
333
env.config.StatePort())
334
mongo.InitDir = upstartScriptLocation
335
logger.Infof("installing service %s to %s", env.mongoServiceName(), mongo.InitDir)
336
if err := mongo.Install(); err != nil {
337
logger.Errorf("could not install mongo service: %v", err)
340
return cert, key, nil
343
func (env *localEnviron) findBridgeAddress() (string, error) {
344
bridge, err := net.InterfaceByName(lxcBridgeName)
346
logger.Errorf("cannot find network interface %q: %v", lxcBridgeName, err)
349
addrs, err := bridge.Addrs()
351
logger.Errorf("cannot get addresses for network interface %q: %v", lxcBridgeName, err)
354
return utils.GetIPv4Address(addrs)
357
func (env *localEnviron) writeBootstrapAgentConfFile(cert, key []byte) error {
358
info, apiInfo, err := env.StateInfo()
360
logger.Errorf("failed to get state info to write bootstrap agent file: %v", err)
363
tag := state.MachineTag("0")
367
DataDir: env.config.rootDir(),
370
StateServerCert: cert,
372
StatePort: env.config.StatePort(),
373
APIPort: env.config.StatePort(),
374
MachineNonce: state.BootstrapNonce,
376
if err := conf.Write(); err != nil {
377
logger.Errorf("failed to write bootstrap agent file: %v", err)
383
func (env *localEnviron) initialStateConfiguration(addr string, cons constraints.Value) (*state.State, error) {
384
// We don't check the existance of the CACert here as if it wasn't set, we
385
// wouldn't get this far.
386
cfg := env.config.Config
387
caCert, _ := cfg.CACert()
388
addr = fmt.Sprintf("%s:%d", addr, cfg.StatePort())
390
Addrs: []string{addr},
393
timeout := state.DialOpts{10 * time.Second}
394
bootstrap, err := environs.BootstrapConfig(cfg)
398
st, err := state.Initialize(info, bootstrap, timeout)
400
logger.Errorf("failed to initialize state: %v", err)
403
logger.Debugf("state initialized")
405
passwordHash := utils.PasswordHash(cfg.AdminSecret())
406
if err := environs.BootstrapUsers(st, cfg, passwordHash); err != nil {
410
jobs := []state.MachineJob{state.JobManageEnviron, state.JobManageState}
412
if err := environs.ConfigureBootstrapMachine(st, cfg, cons, env.config.rootDir(), jobs); err != nil {
417
// Return an open state reference.