github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/worker/unitassigner/unitassigner.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package unitassigner 5 6 import ( 7 "github.com/juju/errors" 8 "github.com/juju/names/v5" 9 "github.com/juju/worker/v3" 10 11 "github.com/juju/juju/core/status" 12 "github.com/juju/juju/core/watcher" 13 "github.com/juju/juju/rpc/params" 14 ) 15 16 // logger is here to stop the desire of creating a package level logger. 17 // Don't do this, instead use the one passed as manifold config. 18 type logger interface{} 19 20 var _ logger = struct{}{} 21 22 type UnitAssigner interface { 23 AssignUnits(tags []names.UnitTag) ([]error, error) 24 WatchUnitAssignments() (watcher.StringsWatcher, error) 25 SetAgentStatus(args params.SetStatus) error 26 } 27 28 func New(ua UnitAssigner, logger Logger) (worker.Worker, error) { 29 return watcher.NewStringsWorker(watcher.StringsConfig{ 30 Handler: unitAssignerHandler{api: ua, logger: logger}, 31 }) 32 } 33 34 type unitAssignerHandler struct { 35 api UnitAssigner 36 logger Logger 37 } 38 39 func (u unitAssignerHandler) SetUp() (watcher.StringsWatcher, error) { 40 return u.api.WatchUnitAssignments() 41 } 42 43 func (u unitAssignerHandler) Handle(_ <-chan struct{}, ids []string) error { 44 u.logger.Tracef("Handling unit assignments: %q", ids) 45 if len(ids) == 0 { 46 return nil 47 } 48 49 units := make([]names.UnitTag, len(ids)) 50 for i, id := range ids { 51 if !names.IsValidUnit(id) { 52 return errors.Errorf("%q is not a valid unit id", id) 53 } 54 units[i] = names.NewUnitTag(id) 55 } 56 57 results, err := u.api.AssignUnits(units) 58 if err != nil { 59 return err 60 } 61 62 failures := map[string]error{} 63 64 u.logger.Tracef("Unit assignment results: %q", results) 65 // errors are returned in the same order as the ids given. Any errors from 66 // the assign units call must be reported as error statuses on the 67 // respective units (though the assignments will be retried). Not found 68 // errors indicate that the unit was removed before the assignment was 69 // requested, which can be safely ignored. 70 for i, err := range results { 71 if err != nil && !errors.IsNotFound(err) { 72 failures[units[i].String()] = err 73 } 74 } 75 76 if len(failures) > 0 { 77 args := params.SetStatus{ 78 Entities: make([]params.EntityStatusArgs, len(failures)), 79 } 80 81 x := 0 82 for unit, err := range failures { 83 args.Entities[x] = params.EntityStatusArgs{ 84 Tag: unit, 85 Status: status.Error.String(), 86 Info: err.Error(), 87 } 88 x++ 89 } 90 91 return u.api.SetAgentStatus(args) 92 } 93 return nil 94 } 95 96 func (unitAssignerHandler) TearDown() error { 97 return nil 98 }