LEFT | RIGHT |
1 // Copyright 2013, 2014 Canonical Ltd. | 1 // Copyright 2013, 2014 Canonical Ltd. |
2 // Licensed under the AGPLv3, see LICENCE file for details. | 2 // Licensed under the AGPLv3, see LICENCE file for details. |
3 | 3 |
4 package local | 4 package local |
5 | 5 |
6 import ( | 6 import ( |
7 "fmt" | 7 "fmt" |
8 "net" | 8 "net" |
9 "os" | 9 "os" |
10 "os/exec" | 10 "os/exec" |
(...skipping 21 matching lines...) Expand all Loading... |
32 "launchpad.net/juju-core/environs/simplestreams" | 32 "launchpad.net/juju-core/environs/simplestreams" |
33 "launchpad.net/juju-core/environs/storage" | 33 "launchpad.net/juju-core/environs/storage" |
34 envtools "launchpad.net/juju-core/environs/tools" | 34 envtools "launchpad.net/juju-core/environs/tools" |
35 "launchpad.net/juju-core/instance" | 35 "launchpad.net/juju-core/instance" |
36 "launchpad.net/juju-core/juju/arch" | 36 "launchpad.net/juju-core/juju/arch" |
37 "launchpad.net/juju-core/juju/osenv" | 37 "launchpad.net/juju-core/juju/osenv" |
38 "launchpad.net/juju-core/provider/common" | 38 "launchpad.net/juju-core/provider/common" |
39 "launchpad.net/juju-core/state" | 39 "launchpad.net/juju-core/state" |
40 "launchpad.net/juju-core/state/api" | 40 "launchpad.net/juju-core/state/api" |
41 "launchpad.net/juju-core/state/api/params" | 41 "launchpad.net/juju-core/state/api/params" |
| 42 "launchpad.net/juju-core/upstart" |
42 "launchpad.net/juju-core/utils/shell" | 43 "launchpad.net/juju-core/utils/shell" |
43 "launchpad.net/juju-core/version" | 44 "launchpad.net/juju-core/version" |
44 "launchpad.net/juju-core/worker/terminationworker" | 45 "launchpad.net/juju-core/worker/terminationworker" |
45 ) | 46 ) |
46 | 47 |
47 // boostrapInstanceId is just the name we give to the bootstrap machine. | 48 // boostrapInstanceId is just the name we give to the bootstrap machine. |
48 // Using "localhost" because it is, and it makes sense. | 49 // Using "localhost" because it is, and it makes sense. |
49 const bootstrapInstanceId instance.Id = "localhost" | 50 const bootstrapInstanceId instance.Id = "localhost" |
50 | 51 |
51 // localEnviron implements Environ. | 52 // localEnviron implements Environ. |
52 var _ environs.Environ = (*localEnviron)(nil) | 53 var _ environs.Environ = (*localEnviron)(nil) |
53 | 54 |
54 // localEnviron implements SupportsCustomSources. | 55 // localEnviron implements SupportsCustomSources. |
55 var _ envtools.SupportsCustomSources = (*localEnviron)(nil) | 56 var _ envtools.SupportsCustomSources = (*localEnviron)(nil) |
56 | 57 |
57 type localEnviron struct { | 58 type localEnviron struct { |
58 » common.NopPrechecker | 59 » common.NopPrecheckerPolicy |
59 » common.DoesSupportUnitPlacement | 60 » common.SupportsUnitPlacementPolicy |
60 | 61 |
61 localMutex sync.Mutex | 62 localMutex sync.Mutex |
62 config *environConfig | 63 config *environConfig |
63 name string | 64 name string |
64 bridgeAddress string | 65 bridgeAddress string |
65 localStorage storage.Storage | 66 localStorage storage.Storage |
66 storageListener net.Listener | 67 storageListener net.Listener |
67 containerManager container.Manager | 68 containerManager container.Manager |
68 } | 69 } |
69 | 70 |
70 // GetToolsSources returns a list of sources which are used to search for simple
streams tools metadata. | 71 // GetToolsSources returns a list of sources which are used to search for simple
streams tools metadata. |
71 func (e *localEnviron) GetToolsSources() ([]simplestreams.DataSource, error) { | 72 func (e *localEnviron) GetToolsSources() ([]simplestreams.DataSource, error) { |
72 // Add the simplestreams source off the control bucket. | 73 // Add the simplestreams source off the control bucket. |
73 return []simplestreams.DataSource{ | 74 return []simplestreams.DataSource{ |
74 storage.NewStorageSimpleStreamsDataSource("cloud storage", e.Sto
rage(), storage.BaseToolsPath)}, nil | 75 storage.NewStorageSimpleStreamsDataSource("cloud storage", e.Sto
rage(), storage.BaseToolsPath)}, nil |
75 } | 76 } |
76 | 77 |
77 // SupportedArchitectures is specified on the EnvironCapability interface. | 78 // SupportedArchitectures is specified on the EnvironCapability interface. |
78 func (*localEnviron) SupportedArchitectures() ([]string, error) { | 79 func (*localEnviron) SupportedArchitectures() ([]string, error) { |
79 localArch := arch.HostArch() | 80 localArch := arch.HostArch() |
80 return []string{localArch}, nil | 81 return []string{localArch}, nil |
81 } | 82 } |
82 | 83 |
| 84 // SupportNetworks is specified on the EnvironCapability interface. |
| 85 func (*localEnviron) SupportNetworks() bool { |
| 86 return false |
| 87 } |
| 88 |
83 // Name is specified in the Environ interface. | 89 // Name is specified in the Environ interface. |
84 func (env *localEnviron) Name() string { | 90 func (env *localEnviron) Name() string { |
85 return env.name | 91 return env.name |
86 } | 92 } |
87 | 93 |
88 func (env *localEnviron) mongoServiceName() string { | 94 func (env *localEnviron) mongoServiceName() string { |
89 return "juju-db-" + env.config.namespace() | 95 return "juju-db-" + env.config.namespace() |
90 } | 96 } |
91 | 97 |
92 func (env *localEnviron) machineAgentServiceName() string { | 98 func (env *localEnviron) machineAgentServiceName() string { |
(...skipping 12 matching lines...) Expand all Loading... |
105 if err := ensureNotRoot(); err != nil { | 111 if err := ensureNotRoot(); err != nil { |
106 return err | 112 return err |
107 } | 113 } |
108 privateKey, err := common.GenerateSystemSSHKey(env) | 114 privateKey, err := common.GenerateSystemSSHKey(env) |
109 if err != nil { | 115 if err != nil { |
110 return err | 116 return err |
111 } | 117 } |
112 | 118 |
113 // Before we write the agent config file, we need to make sure the | 119 // Before we write the agent config file, we need to make sure the |
114 // instance is saved in the StateInfo. | 120 // instance is saved in the StateInfo. |
115 stateFileURL, err := bootstrap.CreateStateFile(env.Storage()) | |
116 if err != nil { | |
117 return err | |
118 } | |
119 if err := bootstrap.SaveState(env.Storage(), &bootstrap.BootstrapState{ | 121 if err := bootstrap.SaveState(env.Storage(), &bootstrap.BootstrapState{ |
120 StateInstances: []instance.Id{bootstrapInstanceId}, | 122 StateInstances: []instance.Id{bootstrapInstanceId}, |
121 }); err != nil { | 123 }); err != nil { |
122 logger.Errorf("failed to save state instances: %v", err) | 124 logger.Errorf("failed to save state instances: %v", err) |
123 return err | 125 return err |
124 } | 126 } |
125 | 127 |
126 vers := version.Current | 128 vers := version.Current |
127 » selectedTools, err := common.EnsureBootstrapTools(env, vers.Series, &ver
s.Arch) | 129 » selectedTools, err := common.EnsureBootstrapTools(ctx, env, vers.Series,
&vers.Arch) |
128 if err != nil { | 130 if err != nil { |
129 return err | 131 return err |
130 } | 132 } |
131 | 133 |
132 // Record the bootstrap IP, so the containers know where to go for stora
ge. | 134 // Record the bootstrap IP, so the containers know where to go for stora
ge. |
133 cfg, err := env.Config().Apply(map[string]interface{}{ | 135 cfg, err := env.Config().Apply(map[string]interface{}{ |
134 "bootstrap-ip": env.bridgeAddress, | 136 "bootstrap-ip": env.bridgeAddress, |
135 }) | 137 }) |
136 if err == nil { | 138 if err == nil { |
137 err = env.SetConfig(cfg) | 139 err = env.SetConfig(cfg) |
138 } | 140 } |
139 if err != nil { | 141 if err != nil { |
140 logger.Errorf("failed to apply bootstrap-ip to config: %v", err) | 142 logger.Errorf("failed to apply bootstrap-ip to config: %v", err) |
141 return err | 143 return err |
142 } | 144 } |
143 | 145 |
144 » mcfg := environs.NewBootstrapMachineConfig(stateFileURL, privateKey) | 146 » mcfg := environs.NewBootstrapMachineConfig(privateKey) |
| 147 » mcfg.InstanceId = bootstrapInstanceId |
145 mcfg.Tools = selectedTools[0] | 148 mcfg.Tools = selectedTools[0] |
146 mcfg.DataDir = env.config.rootDir() | 149 mcfg.DataDir = env.config.rootDir() |
147 mcfg.LogDir = fmt.Sprintf("/var/log/juju-%s", env.config.namespace()) | 150 mcfg.LogDir = fmt.Sprintf("/var/log/juju-%s", env.config.namespace()) |
148 mcfg.Jobs = []params.MachineJob{params.JobManageEnviron} | 151 mcfg.Jobs = []params.MachineJob{params.JobManageEnviron} |
149 » mcfg.CloudInitOutputLog = filepath.Join(env.config.logDir(), "cloud-init
-output.log") | 152 » mcfg.CloudInitOutputLog = filepath.Join(mcfg.DataDir, "cloud-init-output
.log") |
150 mcfg.DisablePackageCommands = true | 153 mcfg.DisablePackageCommands = true |
151 mcfg.MachineAgentServiceName = env.machineAgentServiceName() | 154 mcfg.MachineAgentServiceName = env.machineAgentServiceName() |
152 mcfg.MongoServiceName = env.mongoServiceName() | 155 mcfg.MongoServiceName = env.mongoServiceName() |
153 mcfg.AgentEnvironment = map[string]string{ | 156 mcfg.AgentEnvironment = map[string]string{ |
154 agent.Namespace: env.config.namespace(), | 157 agent.Namespace: env.config.namespace(), |
155 agent.StorageDir: env.config.storageDir(), | 158 agent.StorageDir: env.config.storageDir(), |
156 agent.StorageAddr: env.config.storageAddr(), | 159 agent.StorageAddr: env.config.storageAddr(), |
157 } | 160 } |
158 if err := environs.FinishMachineConfig(mcfg, cfg, cons); err != nil { | 161 if err := environs.FinishMachineConfig(mcfg, cfg, cons); err != nil { |
159 return err | 162 return err |
160 } | 163 } |
161 // don't write proxy settings for local machine | 164 // don't write proxy settings for local machine |
162 mcfg.AptProxySettings = osenv.ProxySettings{} | 165 mcfg.AptProxySettings = osenv.ProxySettings{} |
163 mcfg.ProxySettings = osenv.ProxySettings{} | 166 mcfg.ProxySettings = osenv.ProxySettings{} |
164 cloudcfg := coreCloudinit.New() | 167 cloudcfg := coreCloudinit.New() |
165 // Since rsyslogd is restricted by apparmor to only write to /var/log/** | 168 // Since rsyslogd is restricted by apparmor to only write to /var/log/** |
166 // we now provide a symlink to the written file in the local log dir. | 169 // we now provide a symlink to the written file in the local log dir. |
167 // Also, we leave the old all-machines.log file in | 170 // Also, we leave the old all-machines.log file in |
168 // /var/log/juju-{{namespace}} until we start the environment again. So | 171 // /var/log/juju-{{namespace}} until we start the environment again. So |
169 // potentially remove it at the start of the cloud-init. | 172 // potentially remove it at the start of the cloud-init. |
170 » os.RemoveAll(env.config.logDir()) | 173 » localLogDir := filepath.Join(mcfg.DataDir, "log") |
171 » os.MkdirAll(env.config.logDir(), 0755) | 174 » if err := os.RemoveAll(localLogDir); err != nil { |
| 175 » » return err |
| 176 » } |
| 177 » if err := os.Symlink(mcfg.LogDir, localLogDir); err != nil { |
| 178 » » return err |
| 179 » } |
| 180 » if err := os.Remove(mcfg.CloudInitOutputLog); err != nil && !os.IsNotExi
st(err) { |
| 181 » » return err |
| 182 » } |
172 cloudcfg.AddScripts( | 183 cloudcfg.AddScripts( |
173 fmt.Sprintf("rm -fr %s", mcfg.LogDir), | 184 fmt.Sprintf("rm -fr %s", mcfg.LogDir), |
174 fmt.Sprintf("mkdir -p %s", mcfg.LogDir), | |
175 fmt.Sprintf("chown syslog:adm %s", mcfg.LogDir), | |
176 fmt.Sprintf("rm -f /var/spool/rsyslog/machine-0-%s", env.config.
namespace()), | 185 fmt.Sprintf("rm -f /var/spool/rsyslog/machine-0-%s", env.config.
namespace()), |
177 » » fmt.Sprintf("ln -s %s/all-machines.log %s/", mcfg.LogDir, env.co
nfig.logDir()), | 186 » ) |
178 » » fmt.Sprintf("ln -s %s/machine-0.log %s/", env.config.logDir(), m
cfg.LogDir)) | |
179 if err := cloudinit.ConfigureJuju(mcfg, cloudcfg); err != nil { | 187 if err := cloudinit.ConfigureJuju(mcfg, cloudcfg); err != nil { |
180 return err | 188 return err |
181 } | 189 } |
182 return finishBootstrap(mcfg, cloudcfg, ctx) | 190 return finishBootstrap(mcfg, cloudcfg, ctx) |
183 } | 191 } |
184 | 192 |
185 // finishBootstrap converts the machine config to cloud-config, | 193 // finishBootstrap converts the machine config to cloud-config, |
186 // converts that to a script, and then executes it locally. | 194 // converts that to a script, and then executes it locally. |
187 // | 195 // |
188 // mcfg is supplied for testing purposes. | 196 // mcfg is supplied for testing purposes. |
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
291 storage, err := filestorage.NewFileStorageWriter(env.config.storageDir()
) | 299 storage, err := filestorage.NewFileStorageWriter(env.config.storageDir()
) |
292 if err != nil { | 300 if err != nil { |
293 return err | 301 return err |
294 } | 302 } |
295 env.localStorage = storage | 303 env.localStorage = storage |
296 return nil | 304 return nil |
297 } | 305 } |
298 | 306 |
299 // StartInstance is specified in the InstanceBroker interface. | 307 // StartInstance is specified in the InstanceBroker interface. |
300 func (env *localEnviron) StartInstance(args environs.StartInstanceParams) (insta
nce.Instance, *instance.HardwareCharacteristics, error) { | 308 func (env *localEnviron) StartInstance(args environs.StartInstanceParams) (insta
nce.Instance, *instance.HardwareCharacteristics, error) { |
301 | 309 » if args.MachineConfig.HasNetworks() { |
| 310 » » return nil, nil, fmt.Errorf("starting instances with networks is
not supported yet.") |
| 311 » } |
302 series := args.Tools.OneSeries() | 312 series := args.Tools.OneSeries() |
303 logger.Debugf("StartInstance: %q, %s", args.MachineConfig.MachineId, ser
ies) | 313 logger.Debugf("StartInstance: %q, %s", args.MachineConfig.MachineId, ser
ies) |
304 args.MachineConfig.Tools = args.Tools[0] | 314 args.MachineConfig.Tools = args.Tools[0] |
305 args.MachineConfig.MachineContainerType = env.config.container() | 315 args.MachineConfig.MachineContainerType = env.config.container() |
306 logger.Debugf("tools: %#v", args.MachineConfig.Tools) | 316 logger.Debugf("tools: %#v", args.MachineConfig.Tools) |
307 network := container.BridgeNetworkConfig(env.config.networkBridge()) | 317 network := container.BridgeNetworkConfig(env.config.networkBridge()) |
308 if err := environs.FinishMachineConfig(args.MachineConfig, env.config.Co
nfig, args.Constraints); err != nil { | 318 if err := environs.FinishMachineConfig(args.MachineConfig, env.config.Co
nfig, args.Constraints); err != nil { |
309 return nil, nil, err | 319 return nil, nil, err |
310 } | 320 } |
311 // TODO: evaluate the impact of setting the contstraints on the | 321 // TODO: evaluate the impact of setting the contstraints on the |
312 // machineConfig for all machines rather than just state server nodes. | 322 // machineConfig for all machines rather than just state server nodes. |
313 // This limiation is why the constraints are assigned directly here. | 323 // This limiation is why the constraints are assigned directly here. |
314 args.MachineConfig.Constraints = args.Constraints | 324 args.MachineConfig.Constraints = args.Constraints |
315 args.MachineConfig.AgentEnvironment[agent.Namespace] = env.config.namesp
ace() | 325 args.MachineConfig.AgentEnvironment[agent.Namespace] = env.config.namesp
ace() |
316 » inst, hardware, err := env.containerManager.StartContainer(args.MachineC
onfig, series, network) | 326 » inst, hardware, err := env.containerManager.CreateContainer(args.Machine
Config, series, network) |
317 if err != nil { | 327 if err != nil { |
318 return nil, nil, err | 328 return nil, nil, err |
319 } | 329 } |
320 return inst, hardware, nil | 330 return inst, hardware, nil |
321 } | 331 } |
322 | 332 |
323 // StartInstance is specified in the InstanceBroker interface. | 333 // StartInstance is specified in the InstanceBroker interface. |
324 func (env *localEnviron) StopInstances(instances []instance.Instance) error { | 334 func (env *localEnviron) StopInstances(instances []instance.Instance) error { |
325 for _, inst := range instances { | 335 for _, inst := range instances { |
326 if inst.Id() == bootstrapInstanceId { | 336 if inst.Id() == bootstrapInstanceId { |
327 return fmt.Errorf("cannot stop the bootstrap instance") | 337 return fmt.Errorf("cannot stop the bootstrap instance") |
328 } | 338 } |
329 » » if err := env.containerManager.StopContainer(inst); err != nil { | 339 » » if err := env.containerManager.DestroyContainer(inst); err != ni
l { |
330 return err | 340 return err |
331 } | 341 } |
332 } | 342 } |
333 return nil | 343 return nil |
334 } | 344 } |
335 | 345 |
336 // Instances is specified in the Environ interface. | 346 // Instances is specified in the Environ interface. |
337 func (env *localEnviron) Instances(ids []instance.Id) ([]instance.Instance, erro
r) { | 347 func (env *localEnviron) Instances(ids []instance.Id) ([]instance.Instance, erro
r) { |
338 if len(ids) == 0 { | 348 if len(ids) == 0 { |
339 return nil, nil | 349 return nil, nil |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
400 if os.RemoveAll(env.config.rootDir()) == nil { | 410 if os.RemoveAll(env.config.rootDir()) == nil { |
401 return nil | 411 return nil |
402 } | 412 } |
403 } | 413 } |
404 if !checkIfRoot() { | 414 if !checkIfRoot() { |
405 juju, err := exec.LookPath(os.Args[0]) | 415 juju, err := exec.LookPath(os.Args[0]) |
406 if err != nil { | 416 if err != nil { |
407 return err | 417 return err |
408 } | 418 } |
409 args := []string{ | 419 args := []string{ |
410 » » » osenv.JujuHomeEnvKey + "=" + osenv.JujuHome(), | 420 » » » "env", osenv.JujuHomeEnvKey + "=" + osenv.JujuHome(), |
411 juju, "destroy-environment", "-y", "--force", env.Name()
, | 421 juju, "destroy-environment", "-y", "--force", env.Name()
, |
412 } | 422 } |
413 cmd := exec.Command("sudo", args...) | 423 cmd := exec.Command("sudo", args...) |
414 cmd.Stdout = os.Stdout | 424 cmd.Stdout = os.Stdout |
415 cmd.Stderr = os.Stderr | 425 cmd.Stderr = os.Stderr |
416 return cmd.Run() | 426 return cmd.Run() |
417 } | 427 } |
418 // Kill all running instances. This must be done as | 428 // Kill all running instances. This must be done as |
419 // root, or listing/stopping containers will fail. | 429 // root, or listing/stopping containers will fail. |
420 containers, err := env.containerManager.ListContainers() | 430 containers, err := env.containerManager.ListContainers() |
421 if err != nil { | 431 if err != nil { |
422 return err | 432 return err |
423 } | 433 } |
424 for _, inst := range containers { | 434 for _, inst := range containers { |
425 » » if err := env.containerManager.StopContainer(inst); err != nil { | 435 » » if err := env.containerManager.DestroyContainer(inst); err != ni
l { |
426 return err | 436 return err |
427 } | 437 } |
428 } | 438 } |
429 cmd := exec.Command( | 439 cmd := exec.Command( |
430 "pkill", | 440 "pkill", |
431 fmt.Sprintf("-%d", terminationworker.TerminationSignal), | 441 fmt.Sprintf("-%d", terminationworker.TerminationSignal), |
432 "-f", filepath.Join(regexp.QuoteMeta(env.config.rootDir()), ".*"
, "jujud"), | 442 "-f", filepath.Join(regexp.QuoteMeta(env.config.rootDir()), ".*"
, "jujud"), |
433 ) | 443 ) |
434 if err := cmd.Run(); err != nil { | 444 if err := cmd.Run(); err != nil { |
435 if err, ok := err.(*exec.ExitError); ok { | 445 if err, ok := err.(*exec.ExitError); ok { |
436 // Exit status 1 means no processes were matched: | 446 // Exit status 1 means no processes were matched: |
437 // we don't consider this an error here. | 447 // we don't consider this an error here. |
438 if err.ProcessState.Sys().(syscall.WaitStatus).ExitStatu
s() != 1 { | 448 if err.ProcessState.Sys().(syscall.WaitStatus).ExitStatu
s() != 1 { |
439 return errgo.Annotate(err, "failed to kill jujud
") | 449 return errgo.Annotate(err, "failed to kill jujud
") |
440 } | 450 } |
441 } | 451 } |
442 } | 452 } |
| 453 // Stop the mongo database and machine agent. It's possible that the |
| 454 // service doesn't exist or is not running, so don't check the error. |
| 455 upstart.NewService(env.mongoServiceName()).StopAndRemove() |
| 456 upstart.NewService(env.machineAgentServiceName()).StopAndRemove() |
| 457 |
| 458 // Finally, remove the data-dir. |
443 if err := os.RemoveAll(env.config.rootDir()); err != nil && !os.IsNotExi
st(err) { | 459 if err := os.RemoveAll(env.config.rootDir()); err != nil && !os.IsNotExi
st(err) { |
444 return err | 460 return err |
445 } | 461 } |
446 return nil | 462 return nil |
447 } | 463 } |
448 | 464 |
449 // OpenPorts is specified in the Environ interface. | 465 // OpenPorts is specified in the Environ interface. |
450 func (env *localEnviron) OpenPorts(ports []instance.Port) error { | 466 func (env *localEnviron) OpenPorts(ports []instance.Port) error { |
451 return fmt.Errorf("open ports not implemented") | 467 return fmt.Errorf("open ports not implemented") |
452 } | 468 } |
453 | 469 |
454 // ClosePorts is specified in the Environ interface. | 470 // ClosePorts is specified in the Environ interface. |
455 func (env *localEnviron) ClosePorts(ports []instance.Port) error { | 471 func (env *localEnviron) ClosePorts(ports []instance.Port) error { |
456 return fmt.Errorf("close ports not implemented") | 472 return fmt.Errorf("close ports not implemented") |
457 } | 473 } |
458 | 474 |
459 // Ports is specified in the Environ interface. | 475 // Ports is specified in the Environ interface. |
460 func (env *localEnviron) Ports() ([]instance.Port, error) { | 476 func (env *localEnviron) Ports() ([]instance.Port, error) { |
461 return nil, nil | 477 return nil, nil |
462 } | 478 } |
463 | 479 |
464 // Provider is specified in the Environ interface. | 480 // Provider is specified in the Environ interface. |
465 func (env *localEnviron) Provider() environs.EnvironProvider { | 481 func (env *localEnviron) Provider() environs.EnvironProvider { |
466 return providerInstance | 482 return providerInstance |
467 } | 483 } |
LEFT | RIGHT |