github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/worker/provisioner/containerworker.go (about) 1 // Copyright 2022 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package provisioner 5 6 import ( 7 "fmt" 8 "sync" 9 10 "github.com/juju/errors" 11 "github.com/juju/worker/v3" 12 "github.com/juju/worker/v3/catacomb" 13 14 "github.com/juju/juju/core/watcher" 15 ) 16 17 // NewContainerSetupAndProvisioner returns a ContainerSetupAndProvisioner. 18 func NewContainerSetupAndProvisioner(cs *ContainerSetup, getContainerWatcherFunc GetContainerWatcherFunc) (worker.Worker, error) { 19 containerWatcher, err := getContainerWatcherFunc() 20 if err != nil { 21 return nil, err 22 } 23 w := &ContainerSetupAndProvisioner{ 24 catacomb: catacomb.Catacomb{}, 25 containerWatcher: containerWatcher, 26 logger: cs.logger, 27 cs: cs, 28 } 29 30 if err := catacomb.Invoke(catacomb.Plan{ 31 Site: &w.catacomb, 32 Work: w.work, 33 Init: []worker.Worker{w.containerWatcher}, 34 }); err != nil { 35 return nil, errors.Trace(err) 36 } 37 return w, nil 38 } 39 40 // ContainerSetupAndProvisioner is a worker that waits for a container of type 41 // defined in its config to be found for deployment. Then initializes 42 // the container system and starts a container provisioner of that type. 43 type ContainerSetupAndProvisioner struct { 44 catacomb catacomb.Catacomb 45 46 cs *ContainerSetup 47 48 containerWatcher watcher.StringsWatcher 49 logger Logger 50 provisioner Provisioner 51 52 // For introspection Report 53 mu sync.Mutex 54 } 55 56 func (w *ContainerSetupAndProvisioner) work() error { 57 // Wait for a container of w.ContainerType type to be 58 // found. 59 if err := w.waitForContainers(); err != nil { 60 return err 61 } 62 if err := w.checkDying(); err != nil { 63 return err 64 } 65 66 // The container watcher is no longer needed 67 if err := worker.Stop(w.containerWatcher); err != nil { 68 return err 69 } 70 // For introspection Report 71 w.mu.Lock() 72 w.containerWatcher = nil 73 w.mu.Unlock() 74 75 // Set up w.ContainerType provisioning dependencies 76 // on this machine. 77 if err := w.cs.initialiseContainers(w.catacomb.Dying()); err != nil { 78 return err 79 } 80 if err := w.checkDying(); err != nil { 81 return err 82 } 83 84 // Configure and Add the w.ContainerType Provisioner 85 provisioner, err := w.cs.initialiseContainerProvisioner() 86 if err != nil { 87 return err 88 } 89 if err := w.checkDying(); err != nil { 90 return err 91 } 92 if err := w.catacomb.Add(provisioner); err != nil { 93 return err 94 } 95 96 // For introspection Report 97 w.mu.Lock() 98 w.provisioner = provisioner 99 w.mu.Unlock() 100 101 // The container provisioner is now doing all the work, sit and wait 102 // to be shutdown. 103 select { 104 case <-w.catacomb.Dying(): 105 return w.catacomb.ErrDying() 106 } 107 } 108 109 // checkDying, returns an error if this worker's catacomb 110 // is dying. Needed as the work is not done in a single work. 111 func (w *ContainerSetupAndProvisioner) checkDying() error { 112 select { 113 case <-w.catacomb.Dying(): 114 return w.catacomb.ErrDying() 115 default: 116 return nil 117 } 118 } 119 120 // waitForContainers waits for a container of the type 121 // configured in this worker to be deployed and returns. 122 func (w *ContainerSetupAndProvisioner) waitForContainers() error { 123 for { 124 select { 125 case <-w.catacomb.Dying(): 126 return w.catacomb.ErrDying() 127 case containerIds, ok := <-w.containerWatcher.Changes(): 128 if !ok { 129 return errors.New("container watcher closed") 130 } 131 if len(containerIds) == 0 { 132 continue 133 } 134 return nil 135 } 136 } 137 } 138 139 // Kill is part of the worker.Worker interface. 140 func (w *ContainerSetupAndProvisioner) Kill() { 141 w.catacomb.Kill(nil) 142 } 143 144 // Wait is part of the worker.Worker interface. 145 func (w *ContainerSetupAndProvisioner) Wait() error { 146 return w.catacomb.Wait() 147 } 148 149 // Report provides information for the engine report. 150 func (w *ContainerSetupAndProvisioner) Report() map[string]interface{} { 151 w.mu.Lock() 152 153 result := make(map[string]interface{}, 0) 154 155 if w.containerWatcher != nil { 156 watcherName := fmt.Sprintf("%s-container-watcher", string(w.cs.containerType)) 157 result[watcherName] = fmt.Sprintf("waiting for containers") 158 } 159 if w.provisioner != nil { 160 provisionerName := fmt.Sprintf("%s-provisioner", string(w.cs.containerType)) 161 result[provisionerName] = fmt.Sprintf("setup and running") 162 } 163 164 w.mu.Unlock() 165 return result 166 }