github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/caasunitprovisioner/deployment_worker.go (about) 1 // Copyright 2017 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package caasunitprovisioner 5 6 import ( 7 "github.com/juju/errors" 8 "gopkg.in/juju/names.v2" 9 "gopkg.in/juju/worker.v1" 10 "gopkg.in/juju/worker.v1/catacomb" 11 12 "github.com/juju/juju/api/caasunitprovisioner" 13 "github.com/juju/juju/apiserver/params" 14 "github.com/juju/juju/caas" 15 "github.com/juju/juju/core/watcher" 16 ) 17 18 // deploymentWorker informs the CAAS broker of how many pods to run and their spec, and 19 // lets the broker figure out how to make that all happen. 20 type deploymentWorker struct { 21 catacomb catacomb.Catacomb 22 application string 23 provisioningStatusSetter ProvisioningStatusSetter 24 broker ServiceBroker 25 applicationGetter ApplicationGetter 26 applicationUpdater ApplicationUpdater 27 provisioningInfoGetter ProvisioningInfoGetter 28 } 29 30 func newDeploymentWorker( 31 application string, 32 provisioningStatusSetter ProvisioningStatusSetter, 33 broker ServiceBroker, 34 provisioningInfoGetter ProvisioningInfoGetter, 35 applicationGetter ApplicationGetter, 36 applicationUpdater ApplicationUpdater, 37 ) (worker.Worker, error) { 38 w := &deploymentWorker{ 39 application: application, 40 provisioningStatusSetter: provisioningStatusSetter, 41 broker: broker, 42 provisioningInfoGetter: provisioningInfoGetter, 43 applicationGetter: applicationGetter, 44 applicationUpdater: applicationUpdater, 45 } 46 if err := catacomb.Invoke(catacomb.Plan{ 47 Site: &w.catacomb, 48 Work: w.loop, 49 }); err != nil { 50 return nil, errors.Trace(err) 51 } 52 return w, nil 53 } 54 55 // Kill is part of the worker.Worker interface. 56 func (w *deploymentWorker) Kill() { 57 w.catacomb.Kill(nil) 58 } 59 60 // Wait is part of the worker.Worker interface. 61 func (w *deploymentWorker) Wait() error { 62 return w.catacomb.Wait() 63 } 64 65 func (w *deploymentWorker) loop() error { 66 appScaleWatcher, err := w.applicationGetter.WatchApplicationScale(w.application) 67 if err != nil { 68 return errors.Trace(err) 69 } 70 w.catacomb.Add(appScaleWatcher) 71 72 var ( 73 cw watcher.NotifyWatcher 74 specChan watcher.NotifyChannel 75 76 currentScale int 77 currentSpec string 78 ) 79 80 gotSpecNotify := false 81 serviceUpdated := false 82 scale := 0 83 for { 84 select { 85 case <-w.catacomb.Dying(): 86 return w.catacomb.ErrDying() 87 case _, ok := <-appScaleWatcher.Changes(): 88 if !ok { 89 return errors.New("watcher closed channel") 90 } 91 var err error 92 scale, err = w.applicationGetter.ApplicationScale(w.application) 93 if err != nil { 94 return errors.Trace(err) 95 } 96 if scale > 0 && specChan == nil { 97 var err error 98 cw, err = w.provisioningInfoGetter.WatchPodSpec(w.application) 99 if err != nil { 100 return errors.Trace(err) 101 } 102 w.catacomb.Add(cw) 103 specChan = cw.Changes() 104 } 105 case _, ok := <-specChan: 106 if !ok { 107 return errors.New("watcher closed channel") 108 } 109 gotSpecNotify = true 110 } 111 if scale > 0 && !gotSpecNotify { 112 continue 113 } 114 info, err := w.provisioningInfoGetter.ProvisioningInfo(w.application) 115 noUnits := errors.Cause(err) == caasunitprovisioner.ErrNoUnits 116 if errors.IsNotFound(err) { 117 // No pod spec defined for a unit yet; 118 // wait for one to be set. 119 continue 120 } else if err != nil && !noUnits { 121 return errors.Trace(err) 122 } 123 if scale == 0 || noUnits { 124 if cw != nil { 125 worker.Stop(cw) 126 specChan = nil 127 } 128 logger.Debugf("no units for %v", w.application) 129 err = w.broker.EnsureService(w.application, w.provisioningStatusSetter.SetOperatorStatus, &caas.ServiceParams{}, 0, nil) 130 if err != nil { 131 return errors.Trace(err) 132 } 133 currentScale = 0 134 continue 135 } 136 specStr := info.PodSpec 137 138 if scale == currentScale && specStr == currentSpec { 139 continue 140 } 141 142 currentScale = scale 143 currentSpec = specStr 144 145 appConfig, err := w.applicationGetter.ApplicationConfig(w.application) 146 if err != nil { 147 return errors.Trace(err) 148 } 149 spec, err := w.broker.Provider().ParsePodSpec(specStr) 150 if err != nil { 151 return errors.Annotate(err, "cannot parse pod spec") 152 } 153 if len(spec.CustomResourceDefinitions) > 0 { 154 err = w.broker.EnsureCustomResourceDefinition(w.application, spec) 155 if err != nil { 156 return errors.Trace(err) 157 } 158 logger.Debugf("created/updated custom resource definition for %q.", w.application) 159 } 160 serviceParams := &caas.ServiceParams{ 161 PodSpec: spec, 162 Constraints: info.Constraints, 163 Placement: info.Placement, 164 ResourceTags: info.Tags, 165 Filesystems: info.Filesystems, 166 Devices: info.Devices, 167 } 168 err = w.broker.EnsureService(w.application, w.provisioningStatusSetter.SetOperatorStatus, serviceParams, currentScale, appConfig) 169 if err != nil { 170 return errors.Trace(err) 171 } 172 logger.Debugf("created/updated deployment for %s for %v units", w.application, currentScale) 173 if !serviceUpdated && !spec.OmitServiceFrontend { 174 // TODO(caas) - add a service watcher 175 service, err := w.broker.Service(w.application) 176 if err != nil && !errors.IsNotFound(err) { 177 return errors.Annotate(err, "cannot get new service details") 178 } 179 err = w.applicationUpdater.UpdateApplicationService(params.UpdateApplicationServiceArg{ 180 ApplicationTag: names.NewApplicationTag(w.application).String(), 181 ProviderId: service.Id, 182 Addresses: params.FromNetworkAddresses(service.Addresses...), 183 }) 184 if err != nil { 185 return errors.Trace(err) 186 } 187 serviceUpdated = true 188 } 189 } 190 }