github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/worker/caasfirewaller/application_worker.go (about) 1 // Copyright 2017 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package caasfirewaller 5 6 import ( 7 "strings" 8 9 "github.com/juju/charm/v12" 10 "github.com/juju/errors" 11 "github.com/juju/names/v5" 12 "github.com/juju/worker/v3" 13 "github.com/juju/worker/v3/catacomb" 14 15 "github.com/juju/juju/environs/tags" 16 ) 17 18 type applicationWorker struct { 19 catacomb catacomb.Catacomb 20 controllerUUID string 21 modelUUID string 22 application string 23 applicationGetter ApplicationGetter 24 serviceExposer ServiceExposer 25 lifeGetter LifeGetter 26 charmGetter CharmGetter 27 28 initial bool 29 previouslyExposed bool 30 31 logger Logger 32 } 33 34 func newApplicationWorker( 35 controllerUUID string, 36 modelUUID string, 37 application string, 38 applicationGetter ApplicationGetter, 39 applicationExposer ServiceExposer, 40 lifeGetter LifeGetter, 41 charmGetter CharmGetter, 42 logger Logger, 43 ) (worker.Worker, error) { 44 w := &applicationWorker{ 45 controllerUUID: controllerUUID, 46 modelUUID: modelUUID, 47 application: application, 48 applicationGetter: applicationGetter, 49 serviceExposer: applicationExposer, 50 lifeGetter: lifeGetter, 51 charmGetter: charmGetter, 52 initial: true, 53 logger: logger, 54 } 55 if err := catacomb.Invoke(catacomb.Plan{ 56 Site: &w.catacomb, 57 Work: w.loop, 58 }); err != nil { 59 return nil, errors.Trace(err) 60 } 61 return w, nil 62 } 63 64 // Kill is part of the worker.Worker interface. 65 func (w *applicationWorker) Kill() { 66 w.catacomb.Kill(nil) 67 } 68 69 // Wait is part of the worker.Worker interface. 70 func (w *applicationWorker) Wait() error { 71 return w.catacomb.Wait() 72 } 73 74 func (w *applicationWorker) loop() (err error) { 75 defer func() { 76 // If the application has been deleted, we can return nil. 77 if errors.IsNotFound(err) { 78 w.logger.Debugf("caas firewaller application %v has been removed", w.application) 79 err = nil 80 } 81 }() 82 appWatcher, err := w.applicationGetter.WatchApplication(w.application) 83 if err != nil { 84 return errors.Trace(err) 85 } 86 if err := w.catacomb.Add(appWatcher); err != nil { 87 return errors.Trace(err) 88 } 89 90 for { 91 select { 92 case <-w.catacomb.Dying(): 93 return w.catacomb.ErrDying() 94 case _, ok := <-appWatcher.Changes(): 95 if !ok { 96 return errors.New("application watcher closed") 97 } 98 99 // If charm is (now) a v2 charm, exit the worker. 100 format, err := w.charmFormat() 101 if errors.IsNotFound(err) { 102 w.logger.Debugf("application %q no longer exists", w.application) 103 return nil 104 } else if err != nil { 105 return errors.Trace(err) 106 } 107 if format >= charm.FormatV2 { 108 w.logger.Debugf("application %q v1 worker got v2 charm event, stopping", w.application) 109 return nil 110 } 111 112 if err := w.processApplicationChange(); err != nil { 113 if strings.Contains(err.Error(), "unexpected EOF") { 114 return nil 115 } 116 return errors.Trace(err) 117 } 118 } 119 } 120 } 121 122 func (w *applicationWorker) charmFormat() (charm.Format, error) { 123 charmInfo, err := w.charmGetter.ApplicationCharmInfo(w.application) 124 if err != nil { 125 return charm.FormatUnknown, errors.Annotatef(err, "failed to get charm info for application %q", w.application) 126 } 127 return charm.MetaFormat(charmInfo.Charm()), nil 128 } 129 130 func (w *applicationWorker) processApplicationChange() (err error) { 131 defer func() { 132 // Not found could be because the app got removed or there's 133 // no container service created yet as the app is still being set up. 134 if errors.IsNotFound(err) { 135 // Perhaps the app got removed while we were processing. 136 if _, err2 := w.lifeGetter.Life(w.application); err2 != nil { 137 err = err2 138 return 139 } 140 // Ignore not found error because the ip could be not ready yet at this stage. 141 w.logger.Warningf("processing change for application %q, %v", w.application, err) 142 err = nil 143 } 144 }() 145 146 exposed, err := w.applicationGetter.IsExposed(w.application) 147 if err != nil { 148 return errors.Trace(err) 149 } 150 if !w.initial && exposed == w.previouslyExposed { 151 return nil 152 } 153 154 w.initial = false 155 w.previouslyExposed = exposed 156 if exposed { 157 appConfig, err := w.applicationGetter.ApplicationConfig(w.application) 158 if err != nil { 159 return errors.Trace(err) 160 } 161 resourceTags := tags.ResourceTags( 162 names.NewModelTag(w.modelUUID), 163 names.NewControllerTag(w.controllerUUID), 164 ) 165 if err := w.serviceExposer.ExposeService(w.application, resourceTags, appConfig); err != nil { 166 return errors.Trace(err) 167 } 168 return nil 169 } 170 if err := w.serviceExposer.UnexposeService(w.application); err != nil { 171 return errors.Trace(err) 172 } 173 return nil 174 }