// watchLoop watches the machine for units added or removed. func (md *machineData) watchLoop(unitw apiwatcher.StringsWatcher) { defer md.tomb.Done() defer watcher.Stop(unitw, &md.tomb) for { select { case <-md.tomb.Dying(): return case change, ok := <-unitw.Changes(): if !ok { _, err := md.machine() if !params.IsCodeNotFound(err) { md.fw.tomb.Kill(watcher.EnsureErr(unitw)) } return } select { case md.fw.unitsChange <- &unitsChange{md, change}: case <-md.tomb.Dying(): return } } } }
func (w *storageprovisioner) loop() error { var environConfigChanges <-chan struct{} var volumesWatcher apiwatcher.StringsWatcher var filesystemsWatcher apiwatcher.StringsWatcher var volumesChanges <-chan []string var filesystemsChanges <-chan []string var volumeAttachmentsWatcher apiwatcher.MachineStorageIdsWatcher var filesystemAttachmentsWatcher apiwatcher.MachineStorageIdsWatcher var volumeAttachmentsChanges <-chan []params.MachineStorageId var filesystemAttachmentsChanges <-chan []params.MachineStorageId var machineBlockDevicesWatcher apiwatcher.NotifyWatcher var machineBlockDevicesChanges <-chan struct{} machineChanges := make(chan names.MachineTag) environConfigWatcher, err := w.environ.WatchForEnvironConfigChanges() if err != nil { return errors.Annotate(err, "watching environ config") } defer watcher.Stop(environConfigWatcher, &w.tomb) environConfigChanges = environConfigWatcher.Changes() // Machine-scoped provisioners need to watch block devices, to create // volume-backed filesystems. if machineTag, ok := w.scope.(names.MachineTag); ok { machineBlockDevicesWatcher, err = w.volumes.WatchBlockDevices(machineTag) if err != nil { return errors.Annotate(err, "watching block devices") } defer watcher.Stop(machineBlockDevicesWatcher, &w.tomb) machineBlockDevicesChanges = machineBlockDevicesWatcher.Changes() } // The other watchers are started dynamically; stop only if started. defer w.maybeStopWatcher(volumesWatcher) defer w.maybeStopWatcher(volumeAttachmentsWatcher) defer w.maybeStopWatcher(filesystemsWatcher) defer w.maybeStopWatcher(filesystemAttachmentsWatcher) startWatchers := func() error { var err error volumesWatcher, err = w.volumes.WatchVolumes() if err != nil { return errors.Annotate(err, "watching volumes") } filesystemsWatcher, err = w.filesystems.WatchFilesystems() if err != nil { return errors.Annotate(err, "watching filesystems") } volumeAttachmentsWatcher, err = w.volumes.WatchVolumeAttachments() if err != nil { return errors.Annotate(err, "watching volume attachments") } filesystemAttachmentsWatcher, err = w.filesystems.WatchFilesystemAttachments() if err != nil { return errors.Annotate(err, "watching filesystem attachments") } volumesChanges = volumesWatcher.Changes() filesystemsChanges = filesystemsWatcher.Changes() volumeAttachmentsChanges = volumeAttachmentsWatcher.Changes() filesystemAttachmentsChanges = filesystemAttachmentsWatcher.Changes() return nil } ctx := context{ scope: w.scope, storageDir: w.storageDir, volumeAccessor: w.volumes, filesystemAccessor: w.filesystems, life: w.life, machineAccessor: w.machines, statusSetter: w.status, time: w.clock, volumes: make(map[names.VolumeTag]storage.Volume), volumeAttachments: make(map[params.MachineStorageId]storage.VolumeAttachment), volumeBlockDevices: make(map[names.VolumeTag]storage.BlockDevice), filesystems: make(map[names.FilesystemTag]storage.Filesystem), filesystemAttachments: make(map[params.MachineStorageId]storage.FilesystemAttachment), machines: make(map[names.MachineTag]*machineWatcher), machineChanges: machineChanges, schedule: schedule.NewSchedule(w.clock), pendingVolumeBlockDevices: make(set.Tags), incompleteVolumeParams: make(map[names.VolumeTag]storage.VolumeParams), incompleteVolumeAttachmentParams: make(map[params.MachineStorageId]storage.VolumeAttachmentParams), pendingFilesystems: make(map[names.FilesystemTag]storage.FilesystemParams), pendingFilesystemAttachments: make(map[params.MachineStorageId]storage.FilesystemAttachmentParams), pendingDyingFilesystemAttachments: make(map[params.MachineStorageId]storage.FilesystemAttachmentParams), } ctx.managedFilesystemSource = newManagedFilesystemSource( ctx.volumeBlockDevices, ctx.filesystems, ) defer func() { for _, w := range ctx.machines { w.stop() } }() for { // Check if any pending operations can be fulfilled. if err := processPending(&ctx); err != nil { return errors.Trace(err) } select { case <-w.tomb.Dying(): return tomb.ErrDying case _, ok := <-environConfigChanges: if !ok { return watcher.EnsureErr(environConfigWatcher) } environConfig, err := w.environ.EnvironConfig() if err != nil { return errors.Annotate(err, "getting environ config") } if ctx.environConfig == nil { // We've received the initial environ config, // so we can begin provisioning storage. if err := startWatchers(); err != nil { return err } } ctx.environConfig = environConfig case changes, ok := <-volumesChanges: if !ok { return watcher.EnsureErr(volumesWatcher) } if err := volumesChanged(&ctx, changes); err != nil { return errors.Trace(err) } case changes, ok := <-volumeAttachmentsChanges: if !ok { return watcher.EnsureErr(volumeAttachmentsWatcher) } if err := volumeAttachmentsChanged(&ctx, changes); err != nil { return errors.Trace(err) } case changes, ok := <-filesystemsChanges: if !ok { return watcher.EnsureErr(filesystemsWatcher) } if err := filesystemsChanged(&ctx, changes); err != nil { return errors.Trace(err) } case changes, ok := <-filesystemAttachmentsChanges: if !ok { return watcher.EnsureErr(filesystemAttachmentsWatcher) } if err := filesystemAttachmentsChanged(&ctx, changes); err != nil { return errors.Trace(err) } case _, ok := <-machineBlockDevicesChanges: if !ok { return watcher.EnsureErr(machineBlockDevicesWatcher) } if err := machineBlockDevicesChanged(&ctx); err != nil { return errors.Trace(err) } case machineTag := <-machineChanges: if err := refreshMachine(&ctx, machineTag); err != nil { return errors.Trace(err) } case <-ctx.schedule.Next(): // Ready to pick something(s) off the pending queue. if err := processSchedule(&ctx); err != nil { return errors.Trace(err) } } } }