github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/worker/provisioner/container_initialisation.go (about) 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 "time" 10 11 "github.com/juju/errors" 12 "github.com/juju/mutex" 13 "github.com/juju/utils/clock" 14 15 "github.com/juju/juju/agent" 16 apiprovisioner "github.com/juju/juju/api/provisioner" 17 "github.com/juju/juju/apiserver/params" 18 "github.com/juju/juju/container" 19 "github.com/juju/juju/container/kvm" 20 "github.com/juju/juju/container/lxd" 21 "github.com/juju/juju/environs" 22 "github.com/juju/juju/instance" 23 "github.com/juju/juju/state" 24 "github.com/juju/juju/watcher" 25 "github.com/juju/juju/worker" 26 ) 27 28 // ContainerSetup is a StringsWatchHandler that is notified when containers 29 // are created on the given machine. It will set up the machine to be able 30 // to create containers and start a suitable provisioner. 31 type ContainerSetup struct { 32 runner worker.Runner 33 supportedContainers []instance.ContainerType 34 provisioner *apiprovisioner.State 35 machine *apiprovisioner.Machine 36 config agent.Config 37 initLockName string 38 39 // Save the workerName so the worker thread can be stopped. 40 workerName string 41 // setupDone[containerType] is non zero if the container setup has been invoked 42 // for that container type. 43 setupDone map[instance.ContainerType]*int32 44 // The number of provisioners started. Once all necessary provisioners have 45 // been started, the container watcher can be stopped. 46 numberProvisioners int32 47 } 48 49 // ContainerSetupParams are used to initialise a container setup handler. 50 type ContainerSetupParams struct { 51 Runner worker.Runner 52 WorkerName string 53 SupportedContainers []instance.ContainerType 54 Machine *apiprovisioner.Machine 55 Provisioner *apiprovisioner.State 56 Config agent.Config 57 InitLockName string 58 } 59 60 // NewContainerSetupHandler returns a StringsWatchHandler which is notified when 61 // containers are created on the given machine. 62 func NewContainerSetupHandler(params ContainerSetupParams) watcher.StringsHandler { 63 return &ContainerSetup{ 64 runner: params.Runner, 65 machine: params.Machine, 66 supportedContainers: params.SupportedContainers, 67 provisioner: params.Provisioner, 68 config: params.Config, 69 workerName: params.WorkerName, 70 initLockName: params.InitLockName, 71 } 72 } 73 74 // SetUp is defined on the StringsWatchHandler interface. 75 func (cs *ContainerSetup) SetUp() (watcher watcher.StringsWatcher, err error) { 76 // Set up the semaphores for each container type. 77 cs.setupDone = make(map[instance.ContainerType]*int32, len(instance.ContainerTypes)) 78 for _, containerType := range instance.ContainerTypes { 79 zero := int32(0) 80 cs.setupDone[containerType] = &zero 81 } 82 // Listen to all container lifecycle events on our machine. 83 if watcher, err = cs.machine.WatchAllContainers(); err != nil { 84 return nil, err 85 } 86 return watcher, nil 87 } 88 89 // Handle is called whenever containers change on the machine being watched. 90 // Machines start out with no containers so the first time Handle is called, 91 // it will be because a container has been added. 92 func (cs *ContainerSetup) Handle(abort <-chan struct{}, containerIds []string) (resultError error) { 93 // Consume the initial watcher event. 94 if len(containerIds) == 0 { 95 return nil 96 } 97 98 logger.Infof("initial container setup with ids: %v", containerIds) 99 for _, id := range containerIds { 100 containerType := state.ContainerTypeFromId(id) 101 // If this container type has been dealt with, do nothing. 102 if atomic.LoadInt32(cs.setupDone[containerType]) != 0 { 103 continue 104 } 105 if err := cs.initialiseAndStartProvisioner(abort, containerType); err != nil { 106 logger.Errorf("starting container provisioner for %v: %v", containerType, err) 107 // Just because dealing with one type of container fails, we won't exit the entire 108 // function because we still want to try and start other container types. So we 109 // take note of and return the first such error. 110 if resultError == nil { 111 resultError = err 112 } 113 } 114 } 115 return resultError 116 } 117 118 func (cs *ContainerSetup) initialiseAndStartProvisioner(abort <-chan struct{}, containerType instance.ContainerType) (resultError error) { 119 // Flag that this container type has been handled. 120 atomic.StoreInt32(cs.setupDone[containerType], 1) 121 122 defer func() { 123 if resultError != nil { 124 logger.Warningf("not stopping machine agent container watcher due to error: %v", resultError) 125 return 126 } 127 if atomic.AddInt32(&cs.numberProvisioners, 1) == int32(len(cs.supportedContainers)) { 128 // We only care about the initial container creation. 129 // This worker has done its job so stop it. 130 // We do not expect there will be an error, and there's not much we can do anyway. 131 if err := cs.runner.StopWorker(cs.workerName); err != nil { 132 logger.Warningf("stopping machine agent container watcher: %v", err) 133 } 134 } 135 }() 136 137 logger.Debugf("setup and start provisioner for %s containers", containerType) 138 toolsFinder := getToolsFinder(cs.provisioner) 139 initialiser, broker, toolsFinder, err := cs.getContainerArtifacts(containerType, toolsFinder) 140 if err != nil { 141 return errors.Annotate(err, "initialising container infrastructure on host machine") 142 } 143 if err := cs.runInitialiser(abort, containerType, initialiser); err != nil { 144 return errors.Annotate(err, "setting up container dependencies on host machine") 145 } 146 return StartProvisioner(cs.runner, containerType, cs.provisioner, cs.config, broker, toolsFinder) 147 } 148 149 // runInitialiser runs the container initialiser with the initialisation hook held. 150 func (cs *ContainerSetup) runInitialiser(abort <-chan struct{}, containerType instance.ContainerType, initialiser container.Initialiser) error { 151 logger.Debugf("running initialiser for %s containers", containerType) 152 spec := mutex.Spec{ 153 Name: cs.initLockName, 154 Clock: clock.WallClock, 155 // If we don't get the lock straigh away, there is no point trying multiple 156 // times per second for an operation that is likelty to take multiple seconds. 157 Delay: time.Second, 158 Cancel: abort, 159 } 160 logger.Debugf("acquire lock %q for container initialisation", cs.initLockName) 161 releaser, err := mutex.Acquire(spec) 162 if err != nil { 163 return errors.Annotate(err, "failed to acquire initialization lock") 164 } 165 logger.Debugf("lock %q acquired", cs.initLockName) 166 defer logger.Debugf("release lock %q for container initialisation", cs.initLockName) 167 defer releaser.Release() 168 169 if err := initialiser.Initialise(); err != nil { 170 return errors.Trace(err) 171 } 172 173 return nil 174 } 175 176 // TearDown is defined on the StringsWatchHandler interface. 177 func (cs *ContainerSetup) TearDown() error { 178 // Nothing to do here. 179 return nil 180 } 181 182 // getContainerArtifacts returns type-specific interfaces for 183 // managing containers. 184 // 185 // The ToolsFinder passed in may be replaced or wrapped to 186 // enforce container-specific constraints. 187 func (cs *ContainerSetup) getContainerArtifacts( 188 containerType instance.ContainerType, toolsFinder ToolsFinder, 189 ) ( 190 container.Initialiser, 191 environs.InstanceBroker, 192 ToolsFinder, 193 error, 194 ) { 195 var initialiser container.Initialiser 196 var broker environs.InstanceBroker 197 198 managerConfig, err := containerManagerConfig(containerType, cs.provisioner, cs.config) 199 if err != nil { 200 return nil, nil, nil, err 201 } 202 203 switch containerType { 204 case instance.KVM: 205 initialiser = kvm.NewContainerInitialiser() 206 broker, err = NewKvmBroker( 207 cs.provisioner, 208 cs.config, 209 managerConfig, 210 ) 211 if err != nil { 212 logger.Errorf("failed to create new kvm broker") 213 return nil, nil, nil, err 214 } 215 case instance.LXD: 216 series, err := cs.machine.Series() 217 if err != nil { 218 return nil, nil, nil, err 219 } 220 221 initialiser = lxd.NewContainerInitialiser(series) 222 manager, err := lxd.NewContainerManager(managerConfig) 223 if err != nil { 224 return nil, nil, nil, err 225 } 226 broker, err = NewLxdBroker( 227 cs.provisioner, 228 manager, 229 cs.config, 230 ) 231 if err != nil { 232 logger.Errorf("failed to create new lxd broker") 233 return nil, nil, nil, err 234 } 235 default: 236 return nil, nil, nil, fmt.Errorf("unknown container type: %v", containerType) 237 } 238 239 return initialiser, broker, toolsFinder, nil 240 } 241 242 func containerManagerConfig( 243 containerType instance.ContainerType, 244 provisioner *apiprovisioner.State, 245 agentConfig agent.Config, 246 ) (container.ManagerConfig, error) { 247 // Ask the provisioner for the container manager configuration. 248 managerConfigResult, err := provisioner.ContainerManagerConfig( 249 params.ContainerManagerConfigParams{Type: containerType}, 250 ) 251 if err != nil { 252 return nil, errors.Trace(err) 253 } 254 managerConfig := container.ManagerConfig(managerConfigResult.ManagerConfig) 255 return managerConfig, nil 256 } 257 258 // Override for testing. 259 var StartProvisioner = startProvisionerWorker 260 261 // startProvisionerWorker kicks off a provisioner task responsible for creating containers 262 // of the specified type on the machine. 263 func startProvisionerWorker( 264 runner worker.Runner, 265 containerType instance.ContainerType, 266 provisioner *apiprovisioner.State, 267 config agent.Config, 268 broker environs.InstanceBroker, 269 toolsFinder ToolsFinder, 270 ) error { 271 272 workerName := fmt.Sprintf("%s-provisioner", containerType) 273 // The provisioner task is created after a container record has 274 // already been added to the machine. It will see that the 275 // container does not have an instance yet and create one. 276 return runner.StartWorker(workerName, func() (worker.Worker, error) { 277 w, err := NewContainerProvisioner(containerType, provisioner, config, broker, toolsFinder) 278 if err != nil { 279 return nil, errors.Trace(err) 280 } 281 return w, nil 282 }) 283 }