Left: | ||
Right: |
LEFT | RIGHT |
---|---|
1 // Copyright 2012, 2013 Canonical Ltd. | 1 // Copyright 2012, 2013 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 provisioner | 4 package provisioner |
5 | 5 |
6 import ( | 6 import ( |
7 "fmt" | 7 "fmt" |
8 "sync/atomic" | |
9 | |
8 "launchpad.net/juju-core/agent" | 10 "launchpad.net/juju-core/agent" |
9 "launchpad.net/juju-core/instance" | 11 "launchpad.net/juju-core/instance" |
10 apiprovisioner "launchpad.net/juju-core/state/api/provisioner" | 12 apiprovisioner "launchpad.net/juju-core/state/api/provisioner" |
11 "launchpad.net/juju-core/state/api/watcher" | 13 "launchpad.net/juju-core/state/api/watcher" |
12 "launchpad.net/juju-core/worker" | 14 "launchpad.net/juju-core/worker" |
13 ) | 15 ) |
14 | 16 |
17 // ContainerSetup is a StringsWatchHandler that is notified when containers of | |
18 // the specified type are created on the given machine. It will set up the | |
19 // machine to be able to create containers and start a provisioner. | |
15 type ContainerSetup struct { | 20 type ContainerSetup struct { |
16 runner worker.Runner | 21 runner worker.Runner |
17 workerName string | |
18 containerType instance.ContainerType | 22 containerType instance.ContainerType |
19 provisioner *apiprovisioner.State | 23 provisioner *apiprovisioner.State |
20 machine *apiprovisioner.Machine | 24 machine *apiprovisioner.Machine |
21 config agent.Config | 25 config agent.Config |
26 | |
27 // Save the workerName so the worker thread can be stopped. | |
28 workerName string | |
29 // setupDone is non zero if the container setup has been invoked. | |
30 setupDone int32 | |
22 } | 31 } |
23 | 32 |
33 // NewContainerSetupHandler returns a StringsWatchHandler which is notified when | |
34 // containers are created on the given machine. | |
24 func NewContainerSetupHandler(runner worker.Runner, workerName string, container instance.ContainerType, | 35 func NewContainerSetupHandler(runner worker.Runner, workerName string, container instance.ContainerType, |
25 machine *apiprovisioner.Machine, provisioner *apiprovisioner.State, | 36 machine *apiprovisioner.Machine, provisioner *apiprovisioner.State, |
26 config agent.Config) worker.StringsWatchHandler { | 37 config agent.Config) worker.StringsWatchHandler { |
27 | 38 |
28 return &ContainerSetup{ | 39 return &ContainerSetup{ |
29 runner: runner, | 40 runner: runner, |
30 » » workerName: workerName, | 41 » » containerType: container, |
31 machine: machine, | 42 machine: machine, |
32 containerType: container, | |
33 provisioner: provisioner, | 43 provisioner: provisioner, |
34 config: config, | 44 config: config, |
45 workerName: workerName, | |
35 } | 46 } |
36 } | 47 } |
37 | 48 |
38 func (cs *ContainerSetup) SetUp() (watcher.StringsWatcher, error) { | 49 // SetUp is defined on the StringsWatchHandler interface. |
39 » containerWatcher, err := cs.machine.WatchContainers(cs.containerType) | 50 func (cs *ContainerSetup) SetUp() (watcher watcher.StringsWatcher, err error) { |
40 » if err != nil { | 51 » if watcher, err = cs.machine.WatchContainers(cs.containerType); err != n il { |
41 return nil, err | 52 return nil, err |
42 } | 53 } |
43 » return containerWatcher, nil | 54 » return watcher, nil |
44 } | 55 } |
45 | 56 |
57 // Handle is called whenever containers change on the machine being watched. | |
58 // All machines start out with so containers so the first time Handle is called, | |
59 // it will be because a container has been added. | |
46 func (cs *ContainerSetup) Handle(containerIds []string) error { | 60 func (cs *ContainerSetup) Handle(containerIds []string) error { |
61 // Consume the initial watcher event. | |
62 if len(containerIds) == 0 { | |
63 return nil | |
64 } | |
65 | |
66 // This callback must only be invoked once. Stopping the watcher | |
67 // below should be sufficient but I'm paranoid. | |
68 if atomic.LoadInt32(&cs.setupDone) != 0 { | |
69 return nil | |
70 } | |
71 atomic.StoreInt32(&cs.setupDone, 1) | |
72 | |
47 logger.Tracef("initial container setup with ids: %v", containerIds) | 73 logger.Tracef("initial container setup with ids: %v", containerIds) |
48 » cs.runner.StopWorker(cs.workerName) | 74 » // We only care about the initial container creation. |
75 » // This worker has done its job so stop it. | |
76 » // We do not expect there will be an error, and there's not much we can do anyway. | |
77 » if err := cs.runner.StopWorker(cs.workerName); err != nil { | |
78 » » logger.Warningf("stopping machine agent container watcher: %v", err) | |
79 » } | |
49 if err := cs.ensureContainerDependencies(); err != nil { | 80 if err := cs.ensureContainerDependencies(); err != nil { |
william.reade
2013/11/18 15:30:58
I still think this is better done lazily inside th
| |
50 return fmt.Errorf("setting up container dependnecies on host mac hine: %v", err) | 81 return fmt.Errorf("setting up container dependnecies on host mac hine: %v", err) |
51 } | 82 } |
52 return cs.startProvisioner() | |
53 } | |
54 | |
55 func (cs *ContainerSetup) TearDown() error { | |
56 // Nothing to do here. | |
57 return nil | |
58 } | |
59 | |
60 func (cs *ContainerSetup) ensureContainerDependencies() error { | |
61 // TODO | |
62 return nil | |
63 } | |
64 | |
65 func (cs *ContainerSetup) startProvisioner() error { | |
66 | |
67 // TODO - add check so that startProvisionerCallback is only ever called once, for each container type | |
68 workerName := fmt.Sprintf("%s-provisioner", cs.containerType) | |
69 var provisionerType ProvisionerType | 83 var provisionerType ProvisionerType |
70 switch cs.containerType { | 84 switch cs.containerType { |
71 case instance.LXC: | 85 case instance.LXC: |
72 provisionerType = LXC | 86 provisionerType = LXC |
73 case instance.KVM: | 87 case instance.KVM: |
74 provisionerType = KVM | 88 provisionerType = KVM |
75 default: | 89 default: |
76 return fmt.Errorf("invalid container type %q", cs.containerType) | 90 return fmt.Errorf("invalid container type %q", cs.containerType) |
77 } | 91 } |
92 return StartProvisioner(cs.runner, provisionerType, cs.provisioner, cs.c onfig) | |
93 } | |
78 | 94 |
79 » return cs.runner.StartWorker(workerName, func() (worker.Worker, error) { | 95 // TearDown is defined on the StringsWatchHandler interface. |
80 » » return NewProvisioner(provisionerType, cs.provisioner, cs.config ), nil | 96 func (cs *ContainerSetup) TearDown() error { |
97 » // Nothing to do here. | |
98 » return nil | |
99 } | |
100 | |
101 func (cs *ContainerSetup) ensureContainerDependencies() error { | |
102 » // TODO(wallyworld) - install whatever dependencies are required to supp ort starting containers | |
103 » return nil | |
104 } | |
105 | |
106 // Override for testing. | |
107 var StartProvisioner = startProvisionerWorker | |
108 | |
109 // startProvisionerWorker kicks off a provisioner task responsible for creating containers | |
110 // of the specified type on the machine. | |
111 func startProvisionerWorker(runner worker.Runner, provisionerType ProvisionerTyp e, | |
112 » provisioner *apiprovisioner.State, config agent.Config) error { | |
113 | |
114 » workerName := fmt.Sprintf("%s-provisioner", provisionerType) | |
115 » // The provisioner task is created after a container record has already been added to the machine. | |
116 » // It will see that the container does not have an instance yet and crea te one. | |
117 » return runner.StartWorker(workerName, func() (worker.Worker, error) { | |
118 » » return NewProvisioner(provisionerType, provisioner, config), nil | |
william.reade
2013/11/18 15:30:58
This is all still kinda messed up (nothing to do w
| |
81 }) | 119 }) |
82 } | 120 } |
LEFT | RIGHT |