github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/worker/storageprovisioner/storageprovisioner.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package storageprovisioner 5 6 import ( 7 "github.com/juju/errors" 8 "github.com/juju/loggo" 9 "github.com/juju/names" 10 "github.com/juju/utils/set" 11 "launchpad.net/tomb" 12 13 apiwatcher "github.com/juju/juju/api/watcher" 14 "github.com/juju/juju/apiserver/params" 15 "github.com/juju/juju/environs/config" 16 "github.com/juju/juju/state/watcher" 17 "github.com/juju/juju/storage" 18 "github.com/juju/juju/storage/provider" 19 "github.com/juju/juju/worker" 20 ) 21 22 var logger = loggo.GetLogger("juju.worker.storageprovisioner") 23 24 var newManagedFilesystemSource = provider.NewManagedFilesystemSource 25 26 // VolumeAccessor defines an interface used to allow a storage provisioner 27 // worker to perform volume related operations. 28 type VolumeAccessor interface { 29 // WatchBlockDevices watches for changes to the block devices of the 30 // specified machine. 31 WatchBlockDevices(names.MachineTag) (apiwatcher.NotifyWatcher, error) 32 33 // WatchVolumes watches for changes to volumes that this storage 34 // provisioner is responsible for. 35 WatchVolumes() (apiwatcher.StringsWatcher, error) 36 37 // WatchVolumeAttachments watches for changes to volume attachments 38 // that this storage provisioner is responsible for. 39 WatchVolumeAttachments() (apiwatcher.MachineStorageIdsWatcher, error) 40 41 // Volumes returns details of volumes with the specified tags. 42 Volumes([]names.VolumeTag) ([]params.VolumeResult, error) 43 44 // VolumeBlockDevices returns details of block devices corresponding to 45 // the specified volume attachment IDs. 46 VolumeBlockDevices([]params.MachineStorageId) ([]params.BlockDeviceResult, error) 47 48 // VolumeAttachments returns details of volume attachments with 49 // the specified tags. 50 VolumeAttachments([]params.MachineStorageId) ([]params.VolumeAttachmentResult, error) 51 52 // VolumeParams returns the parameters for creating the volumes 53 // with the specified tags. 54 VolumeParams([]names.VolumeTag) ([]params.VolumeParamsResult, error) 55 56 // VolumeAttachmentParams returns the parameters for creating the 57 // volume attachments with the specified tags. 58 VolumeAttachmentParams([]params.MachineStorageId) ([]params.VolumeAttachmentParamsResult, error) 59 60 // SetVolumeInfo records the details of newly provisioned volumes. 61 SetVolumeInfo([]params.Volume) ([]params.ErrorResult, error) 62 63 // SetVolumeAttachmentInfo records the details of newly provisioned 64 // volume attachments. 65 SetVolumeAttachmentInfo([]params.VolumeAttachment) ([]params.ErrorResult, error) 66 } 67 68 // FilesystemAccessor defines an interface used to allow a storage provisioner 69 // worker to perform filesystem related operations. 70 type FilesystemAccessor interface { 71 // WatchFilesystems watches for changes to filesystems that this 72 // storage provisioner is responsible for. 73 WatchFilesystems() (apiwatcher.StringsWatcher, error) 74 75 // WatchFilesystemAttachments watches for changes to filesystem attachments 76 // that this storage provisioner is responsible for. 77 WatchFilesystemAttachments() (apiwatcher.MachineStorageIdsWatcher, error) 78 79 // Filesystems returns details of filesystems with the specified tags. 80 Filesystems([]names.FilesystemTag) ([]params.FilesystemResult, error) 81 82 // FilesystemAttachments returns details of filesystem attachments with 83 // the specified tags. 84 FilesystemAttachments([]params.MachineStorageId) ([]params.FilesystemAttachmentResult, error) 85 86 // FilesystemParams returns the parameters for creating the filesystems 87 // with the specified tags. 88 FilesystemParams([]names.FilesystemTag) ([]params.FilesystemParamsResult, error) 89 90 // FilesystemAttachmentParams returns the parameters for creating the 91 // filesystem attachments with the specified tags. 92 FilesystemAttachmentParams([]params.MachineStorageId) ([]params.FilesystemAttachmentParamsResult, error) 93 94 // SetFilesystemInfo records the details of newly provisioned filesystems. 95 SetFilesystemInfo([]params.Filesystem) ([]params.ErrorResult, error) 96 97 // SetFilesystemAttachmentInfo records the details of newly provisioned 98 // filesystem attachments. 99 SetFilesystemAttachmentInfo([]params.FilesystemAttachment) ([]params.ErrorResult, error) 100 } 101 102 // MachineAccessor defines an interface used to allow a storage provisioner 103 // worker to perform machine related operations. 104 type MachineAccessor interface { 105 // WatchMachine watches for changes to the specified machine. 106 WatchMachine(names.MachineTag) (apiwatcher.NotifyWatcher, error) 107 108 // InstanceIds returns the instance IDs of each machine. 109 InstanceIds([]names.MachineTag) ([]params.StringResult, error) 110 } 111 112 // LifecycleManager defines an interface used to enable a storage provisioner 113 // worker to perform lifcycle-related operations on storage entities and 114 // attachments. 115 type LifecycleManager interface { 116 // Life returns the lifecycle state of the specified entities. 117 Life([]names.Tag) ([]params.LifeResult, error) 118 119 // Remove removes the specified entities from state. 120 Remove([]names.Tag) ([]params.ErrorResult, error) 121 122 // AttachmentLife returns the lifecycle state of the specified 123 // machine/entity attachments. 124 AttachmentLife([]params.MachineStorageId) ([]params.LifeResult, error) 125 126 // RemoveAttachments removes the specified machine/entity attachments 127 // from state. 128 RemoveAttachments([]params.MachineStorageId) ([]params.ErrorResult, error) 129 } 130 131 // EnvironAccessor defines an interface used to enable a storage provisioner 132 // worker to watch changes to and read environment config, to use when 133 // provisioning storage. 134 type EnvironAccessor interface { 135 // WatchForEnvironConfigChanges returns a watcher that will be notified 136 // whenever the environment config changes in state. 137 WatchForEnvironConfigChanges() (apiwatcher.NotifyWatcher, error) 138 139 // EnvironConfig returns the current environment config. 140 EnvironConfig() (*config.Config, error) 141 } 142 143 // NewStorageProvisioner returns a Worker which manages 144 // provisioning (deprovisioning), and attachment (detachment) 145 // of first-class volumes and filesystems. 146 // 147 // Machine-scoped storage workers will be provided with 148 // a storage directory, while environment-scoped workers 149 // will not. If the directory path is non-empty, then it 150 // will be passed to the storage source via its config. 151 func NewStorageProvisioner( 152 scope names.Tag, 153 storageDir string, 154 v VolumeAccessor, 155 f FilesystemAccessor, 156 l LifecycleManager, 157 e EnvironAccessor, 158 m MachineAccessor, 159 ) worker.Worker { 160 w := &storageprovisioner{ 161 scope: scope, 162 storageDir: storageDir, 163 volumes: v, 164 filesystems: f, 165 life: l, 166 environ: e, 167 machines: m, 168 } 169 go func() { 170 defer w.tomb.Done() 171 err := w.loop() 172 if err != tomb.ErrDying { 173 logger.Errorf("%s", err) 174 } 175 w.tomb.Kill(err) 176 }() 177 return w 178 } 179 180 type storageprovisioner struct { 181 tomb tomb.Tomb 182 scope names.Tag 183 storageDir string 184 volumes VolumeAccessor 185 filesystems FilesystemAccessor 186 life LifecycleManager 187 environ EnvironAccessor 188 machines MachineAccessor 189 } 190 191 // Kill implements Worker.Kill(). 192 func (w *storageprovisioner) Kill() { 193 w.tomb.Kill(nil) 194 } 195 196 // Wait implements Worker.Wait(). 197 func (w *storageprovisioner) Wait() error { 198 return w.tomb.Wait() 199 } 200 201 func (w *storageprovisioner) loop() error { 202 var environConfigChanges <-chan struct{} 203 var volumesWatcher apiwatcher.StringsWatcher 204 var filesystemsWatcher apiwatcher.StringsWatcher 205 var volumesChanges <-chan []string 206 var filesystemsChanges <-chan []string 207 var volumeAttachmentsWatcher apiwatcher.MachineStorageIdsWatcher 208 var filesystemAttachmentsWatcher apiwatcher.MachineStorageIdsWatcher 209 var volumeAttachmentsChanges <-chan []params.MachineStorageId 210 var filesystemAttachmentsChanges <-chan []params.MachineStorageId 211 var machineBlockDevicesWatcher apiwatcher.NotifyWatcher 212 var machineBlockDevicesChanges <-chan struct{} 213 machineChanges := make(chan names.MachineTag) 214 215 environConfigWatcher, err := w.environ.WatchForEnvironConfigChanges() 216 if err != nil { 217 return errors.Annotate(err, "watching environ config") 218 } 219 defer watcher.Stop(environConfigWatcher, &w.tomb) 220 environConfigChanges = environConfigWatcher.Changes() 221 222 // Machine-scoped provisioners need to watch block devices, to create 223 // volume-backed filesystems. 224 if machineTag, ok := w.scope.(names.MachineTag); ok { 225 machineBlockDevicesWatcher, err = w.volumes.WatchBlockDevices(machineTag) 226 if err != nil { 227 return errors.Annotate(err, "watching block devices") 228 } 229 defer watcher.Stop(machineBlockDevicesWatcher, &w.tomb) 230 machineBlockDevicesChanges = machineBlockDevicesWatcher.Changes() 231 } 232 233 // The other watchers are started dynamically; stop only if started. 234 defer w.maybeStopWatcher(volumesWatcher) 235 defer w.maybeStopWatcher(volumeAttachmentsWatcher) 236 defer w.maybeStopWatcher(filesystemsWatcher) 237 defer w.maybeStopWatcher(filesystemAttachmentsWatcher) 238 239 startWatchers := func() error { 240 var err error 241 volumesWatcher, err = w.volumes.WatchVolumes() 242 if err != nil { 243 return errors.Annotate(err, "watching volumes") 244 } 245 filesystemsWatcher, err = w.filesystems.WatchFilesystems() 246 if err != nil { 247 return errors.Annotate(err, "watching filesystems") 248 } 249 volumeAttachmentsWatcher, err = w.volumes.WatchVolumeAttachments() 250 if err != nil { 251 return errors.Annotate(err, "watching volume attachments") 252 } 253 filesystemAttachmentsWatcher, err = w.filesystems.WatchFilesystemAttachments() 254 if err != nil { 255 return errors.Annotate(err, "watching filesystem attachments") 256 } 257 volumesChanges = volumesWatcher.Changes() 258 filesystemsChanges = filesystemsWatcher.Changes() 259 volumeAttachmentsChanges = volumeAttachmentsWatcher.Changes() 260 filesystemAttachmentsChanges = filesystemAttachmentsWatcher.Changes() 261 return nil 262 } 263 264 ctx := context{ 265 scope: w.scope, 266 storageDir: w.storageDir, 267 volumeAccessor: w.volumes, 268 filesystemAccessor: w.filesystems, 269 life: w.life, 270 machineAccessor: w.machines, 271 volumes: make(map[names.VolumeTag]storage.Volume), 272 volumeAttachments: make(map[params.MachineStorageId]storage.VolumeAttachment), 273 volumeBlockDevices: make(map[names.VolumeTag]storage.BlockDevice), 274 filesystems: make(map[names.FilesystemTag]storage.Filesystem), 275 filesystemAttachments: make(map[params.MachineStorageId]storage.FilesystemAttachment), 276 machines: make(map[names.MachineTag]*machineWatcher), 277 machineChanges: machineChanges, 278 pendingVolumes: make(map[names.VolumeTag]storage.VolumeParams), 279 pendingVolumeAttachments: make(map[params.MachineStorageId]storage.VolumeAttachmentParams), 280 pendingVolumeBlockDevices: make(set.Tags), 281 pendingFilesystems: make(map[names.FilesystemTag]storage.FilesystemParams), 282 pendingFilesystemAttachments: make(map[params.MachineStorageId]storage.FilesystemAttachmentParams), 283 pendingDyingFilesystemAttachments: make(map[params.MachineStorageId]storage.FilesystemAttachmentParams), 284 } 285 ctx.managedFilesystemSource = newManagedFilesystemSource( 286 ctx.volumeBlockDevices, ctx.filesystems, 287 ) 288 defer func() { 289 for _, w := range ctx.machines { 290 w.stop() 291 } 292 }() 293 294 for { 295 // Check if any pending operations can be fulfilled. 296 if err := processPending(&ctx); err != nil { 297 return errors.Trace(err) 298 } 299 300 select { 301 case <-w.tomb.Dying(): 302 return tomb.ErrDying 303 case _, ok := <-environConfigChanges: 304 if !ok { 305 return watcher.EnsureErr(environConfigWatcher) 306 } 307 environConfig, err := w.environ.EnvironConfig() 308 if err != nil { 309 return errors.Annotate(err, "getting environ config") 310 } 311 if ctx.environConfig == nil { 312 // We've received the initial environ config, 313 // so we can begin provisioning storage. 314 if err := startWatchers(); err != nil { 315 return err 316 } 317 } 318 ctx.environConfig = environConfig 319 case changes, ok := <-volumesChanges: 320 if !ok { 321 return watcher.EnsureErr(volumesWatcher) 322 } 323 if err := volumesChanged(&ctx, changes); err != nil { 324 return errors.Trace(err) 325 } 326 case changes, ok := <-volumeAttachmentsChanges: 327 if !ok { 328 return watcher.EnsureErr(volumeAttachmentsWatcher) 329 } 330 if err := volumeAttachmentsChanged(&ctx, changes); err != nil { 331 return errors.Trace(err) 332 } 333 case changes, ok := <-filesystemsChanges: 334 if !ok { 335 return watcher.EnsureErr(filesystemsWatcher) 336 } 337 if err := filesystemsChanged(&ctx, changes); err != nil { 338 return errors.Trace(err) 339 } 340 case changes, ok := <-filesystemAttachmentsChanges: 341 if !ok { 342 return watcher.EnsureErr(filesystemAttachmentsWatcher) 343 } 344 if err := filesystemAttachmentsChanged(&ctx, changes); err != nil { 345 return errors.Trace(err) 346 } 347 case _, ok := <-machineBlockDevicesChanges: 348 if !ok { 349 return watcher.EnsureErr(machineBlockDevicesWatcher) 350 } 351 if err := machineBlockDevicesChanged(&ctx); err != nil { 352 return errors.Trace(err) 353 } 354 case machineTag := <-machineChanges: 355 if err := refreshMachine(&ctx, machineTag); err != nil { 356 return errors.Trace(err) 357 } 358 } 359 } 360 } 361 362 // processPending checks if the pending operations' prerequisites have 363 // been met, and processes them if so. 364 func processPending(ctx *context) error { 365 if err := processPendingVolumes(ctx); err != nil { 366 return errors.Annotate(err, "processing pending volumes") 367 } 368 if err := processPendingVolumeAttachments(ctx); err != nil { 369 return errors.Annotate(err, "processing pending volume attachments") 370 } 371 if err := processPendingVolumeBlockDevices(ctx); err != nil { 372 return errors.Annotate(err, "processing pending block devices") 373 } 374 if err := processPendingFilesystems(ctx); err != nil { 375 return errors.Annotate(err, "processing pending filesystems") 376 } 377 if err := processPendingDyingFilesystemAttachments(ctx); err != nil { 378 return errors.Annotate(err, "processing pending, dying filesystem attachments") 379 } 380 if err := processPendingFilesystemAttachments(ctx); err != nil { 381 return errors.Annotate(err, "processing pending filesystem attachments") 382 } 383 return nil 384 } 385 386 func (p *storageprovisioner) maybeStopWatcher(w watcher.Stopper) { 387 if w != nil { 388 watcher.Stop(w, &p.tomb) 389 } 390 } 391 392 type context struct { 393 scope names.Tag 394 environConfig *config.Config 395 storageDir string 396 volumeAccessor VolumeAccessor 397 filesystemAccessor FilesystemAccessor 398 life LifecycleManager 399 machineAccessor MachineAccessor 400 401 // volumes contains information about provisioned volumes. 402 volumes map[names.VolumeTag]storage.Volume 403 404 // volumeAttachments contains information about attached volumes. 405 volumeAttachments map[params.MachineStorageId]storage.VolumeAttachment 406 407 // volumeBlockDevices contains information about block devices 408 // corresponding to volumes attached to the scope-machine. This 409 // is only used by the machine-scoped storage provisioner. 410 volumeBlockDevices map[names.VolumeTag]storage.BlockDevice 411 412 // filesystems contains information about provisioned filesystems. 413 filesystems map[names.FilesystemTag]storage.Filesystem 414 415 // filesystemAttachments contains information about attached filesystems. 416 filesystemAttachments map[params.MachineStorageId]storage.FilesystemAttachment 417 418 // machines contains information about machines in the worker's scope. 419 machines map[names.MachineTag]*machineWatcher 420 421 // machineChanges is a channel that machine watchers will send to once 422 // their machine is known to have been provisioned. 423 machineChanges chan<- names.MachineTag 424 425 // pendingVolumes contains parameters for volumes that are yet to be 426 // created. 427 pendingVolumes map[names.VolumeTag]storage.VolumeParams 428 429 // pendingVolumeAttachments contains parameters for volume attachments 430 // that are yet to be created. 431 pendingVolumeAttachments map[params.MachineStorageId]storage.VolumeAttachmentParams 432 433 // pendingVolumeBlockDevices contains the tags of volumes about whose 434 // block devices we wish to enquire. 435 pendingVolumeBlockDevices set.Tags 436 437 // pendingFilesystems contains parameters for filesystems that are 438 // yet to be created. 439 pendingFilesystems map[names.FilesystemTag]storage.FilesystemParams 440 441 // pendingFilesystemAttachments contains parameters for filesystem attachments 442 // that are yet to be created. 443 pendingFilesystemAttachments map[params.MachineStorageId]storage.FilesystemAttachmentParams 444 445 // pendingDyingFilesystemAttachments contains parameters for filesystem attachments 446 // that are to be destroyed. 447 pendingDyingFilesystemAttachments map[params.MachineStorageId]storage.FilesystemAttachmentParams 448 449 // managedFilesystemSource is a storage.FilesystemSource that 450 // manages filesystems backed by volumes attached to the host 451 // machine. 452 managedFilesystemSource storage.FilesystemSource 453 }