Left: | ||
Right: |
LEFT | RIGHT |
---|---|
(no file at all) | |
1 // Copyright 2012, 2013 Canonical Ltd. | |
2 // Licensed under the AGPLv3, see LICENCE file for details. | |
3 | |
4 package provisioner | |
5 | |
6 import ( | |
7 "fmt" | |
8 "sync/atomic" | |
9 | |
10 "launchpad.net/juju-core/agent" | |
11 "launchpad.net/juju-core/instance" | |
12 apiprovisioner "launchpad.net/juju-core/state/api/provisioner" | |
13 "launchpad.net/juju-core/state/api/watcher" | |
14 "launchpad.net/juju-core/worker" | |
15 ) | |
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. | |
20 type ContainerSetup struct { | |
21 runner worker.Runner | |
22 containerType instance.ContainerType | |
23 provisioner *apiprovisioner.State | |
24 machine *apiprovisioner.Machine | |
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 | |
31 } | |
32 | |
33 // NewContainerSetupHandler returns a StringsWatchHandler which is notified when | |
34 // containers are created on the given machine. | |
35 func NewContainerSetupHandler(runner worker.Runner, workerName string, container instance.ContainerType, | |
36 machine *apiprovisioner.Machine, provisioner *apiprovisioner.State, | |
37 config agent.Config) worker.StringsWatchHandler { | |
38 | |
39 return &ContainerSetup{ | |
40 runner: runner, | |
41 containerType: container, | |
42 machine: machine, | |
43 provisioner: provisioner, | |
44 config: config, | |
45 workerName: workerName, | |
46 } | |
47 } | |
48 | |
49 // SetUp is defined on the StringsWatchHandler interface. | |
50 func (cs *ContainerSetup) SetUp() (watcher watcher.StringsWatcher, err error) { | |
51 if watcher, err = cs.machine.WatchContainers(cs.containerType); err != n il { | |
52 return nil, err | |
53 } | |
54 return watcher, nil | |
55 } | |
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. | |
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 | |
73 logger.Tracef("initial container setup with ids: %v", containerIds) | |
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 } | |
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
| |
81 return fmt.Errorf("setting up container dependnecies on host mac hine: %v", err) | |
82 } | |
83 var provisionerType ProvisionerType | |
84 switch cs.containerType { | |
85 case instance.LXC: | |
86 provisionerType = LXC | |
87 case instance.KVM: | |
88 provisionerType = KVM | |
89 default: | |
90 return fmt.Errorf("invalid container type %q", cs.containerType) | |
91 } | |
92 return StartProvisioner(cs.runner, provisionerType, cs.provisioner, cs.c onfig) | |
93 } | |
94 | |
95 // TearDown is defined on the StringsWatchHandler interface. | |
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
| |
119 }) | |
120 } | |
LEFT | RIGHT |