github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/worker/storageprovisioner/machines.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/names/v5" 9 "github.com/juju/worker/v3" 10 "github.com/juju/worker/v3/catacomb" 11 12 "github.com/juju/juju/core/instance" 13 "github.com/juju/juju/rpc/params" 14 ) 15 16 // watchMachine starts a machine watcher if there is not already one for the 17 // specified tag. The watcher will notify the worker when the machine changes, 18 // for example when it is provisioned. 19 func watchMachine(ctx *context, tag names.MachineTag) { 20 _, ok := ctx.machines[tag] 21 if ok { 22 return 23 } 24 w, err := newMachineWatcher(ctx.config.Machines, tag, ctx.machineChanges, ctx.config.Logger) 25 if err != nil { 26 ctx.kill(errors.Trace(err)) 27 } else if err := ctx.addWorker(w); err != nil { 28 ctx.kill(errors.Trace(err)) 29 } else { 30 ctx.machines[tag] = w 31 } 32 } 33 34 // refreshMachine refreshes the specified machine's instance ID. If it is set, 35 // then the machine watcher is stopped and pending entities' parameters are 36 // updated. If the machine is not provisioned yet, this method is a no-op. 37 func refreshMachine(ctx *context, tag names.MachineTag) error { 38 w, ok := ctx.machines[tag] 39 if !ok { 40 return errors.Errorf("machine %s is not being watched", tag.Id()) 41 } 42 stopAndRemove := func() error { 43 _ = worker.Stop(w) 44 delete(ctx.machines, tag) 45 return nil 46 } 47 results, err := ctx.config.Machines.InstanceIds([]names.MachineTag{tag}) 48 if err != nil { 49 return errors.Annotate(err, "getting machine instance ID") 50 } 51 if err := results[0].Error; err != nil { 52 if params.IsCodeNotProvisioned(err) { 53 return nil 54 } else if params.IsCodeNotFound(err) { 55 // Machine is gone, so stop watching. 56 return stopAndRemove() 57 } 58 return errors.Annotate(err, "getting machine instance ID") 59 } 60 machineProvisioned(ctx, tag, instance.Id(results[0].Result)) 61 // machine provisioning is the only thing we care about; 62 // stop the watcher. 63 return stopAndRemove() 64 } 65 66 // machineProvisioned is called when a watched machine is provisioned. 67 func machineProvisioned(ctx *context, tag names.MachineTag, instanceId instance.Id) { 68 for _, params := range ctx.incompleteVolumeParams { 69 if params.Attachment.Machine != tag || params.Attachment.InstanceId != "" { 70 continue 71 } 72 params.Attachment.InstanceId = instanceId 73 updatePendingVolume(ctx, params) 74 } 75 for id, params := range ctx.incompleteVolumeAttachmentParams { 76 if params.Machine != tag || params.InstanceId != "" { 77 continue 78 } 79 params.InstanceId = instanceId 80 updatePendingVolumeAttachment(ctx, id, params) 81 } 82 for id, params := range ctx.incompleteFilesystemAttachmentParams { 83 if params.Machine != tag || params.InstanceId != "" { 84 continue 85 } 86 params.InstanceId = instanceId 87 updatePendingFilesystemAttachment(ctx, id, params) 88 } 89 } 90 91 type machineWatcher struct { 92 catacomb catacomb.Catacomb 93 accessor MachineAccessor 94 tag names.MachineTag 95 out chan<- names.MachineTag 96 logger Logger 97 } 98 99 func newMachineWatcher( 100 accessor MachineAccessor, 101 tag names.MachineTag, 102 out chan<- names.MachineTag, 103 logger Logger, 104 ) (*machineWatcher, error) { 105 w := &machineWatcher{ 106 accessor: accessor, 107 tag: tag, 108 out: out, 109 logger: logger, 110 } 111 err := catacomb.Invoke(catacomb.Plan{ 112 Site: &w.catacomb, 113 Work: w.loop, 114 }) 115 if err != nil { 116 return nil, errors.Trace(err) 117 } 118 return w, nil 119 } 120 121 func (mw *machineWatcher) loop() error { 122 w, err := mw.accessor.WatchMachine(mw.tag) 123 if err != nil { 124 return errors.Annotate(err, "watching machine") 125 } 126 if err := mw.catacomb.Add(w); err != nil { 127 return errors.Trace(err) 128 } 129 mw.logger.Debugf("watching machine %s", mw.tag.Id()) 130 defer mw.logger.Debugf("finished watching machine %s", mw.tag.Id()) 131 var out chan<- names.MachineTag 132 for { 133 select { 134 case <-mw.catacomb.Dying(): 135 return mw.catacomb.ErrDying() 136 case _, ok := <-w.Changes(): 137 if !ok { 138 return errors.New("machine watcher closed") 139 } 140 out = mw.out 141 case out <- mw.tag: 142 out = nil 143 } 144 } 145 } 146 147 // Kill is part of the worker.Worker interface. 148 func (mw *machineWatcher) Kill() { 149 mw.catacomb.Kill(nil) 150 } 151 152 // Wait is part of the worker.Worker interface. 153 func (mw *machineWatcher) Wait() error { 154 return mw.catacomb.Wait() 155 }