github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/agent/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 "gopkg.in/juju/names.v2" 9 10 "github.com/juju/juju/apiserver/common" 11 "github.com/juju/juju/apiserver/facade" 12 "github.com/juju/juju/apiserver/params" 13 "github.com/juju/juju/state" 14 "github.com/juju/juju/state/watcher" 15 ) 16 17 // assignerState defines the state methods this facade needs, so they can be mocked 18 // for testing. 19 type assignerState interface { 20 WatchForUnitAssignment() state.StringsWatcher 21 AssignStagedUnits(ids []string) ([]state.UnitAssignmentResult, error) 22 } 23 24 type statusSetter interface { 25 SetStatus(args params.SetStatus) (params.ErrorResults, error) 26 } 27 28 // API implements the functionality for assigning units to machines. 29 type API struct { 30 st assignerState 31 res facade.Resources 32 statusSetter statusSetter 33 } 34 35 // New returns a new unitAssigner api instance. 36 func New(st *state.State, res facade.Resources, _ facade.Authorizer) (*API, error) { 37 setter := common.NewStatusSetter(&common.UnitAgentFinder{st}, common.AuthAlways()) 38 return &API{ 39 st: st, 40 res: res, 41 statusSetter: setter, 42 }, nil 43 } 44 45 // AssignUnits assigns the units with the given ids to the correct machine. The 46 // error results are returned in the same order as the given entities. 47 func (a *API) AssignUnits(args params.Entities) (params.ErrorResults, error) { 48 result := params.ErrorResults{} 49 50 // state uses ids, but the API uses Tags, so we have to convert back and 51 // forth (whee!). The list of ids is (crucially) in the same order as the 52 // list of tags. This is the same order as the list of errors we return. 53 ids := make([]string, len(args.Entities)) 54 for i, e := range args.Entities { 55 tag, err := names.ParseUnitTag(e.Tag) 56 if err != nil { 57 return result, err 58 } 59 ids[i] = tag.Id() 60 } 61 62 res, err := a.st.AssignStagedUnits(ids) 63 if err != nil { 64 return result, common.ServerError(err) 65 } 66 67 // The results come back from state in an undetermined order and do not 68 // include results for units that were not found, so we have to make up for 69 // that here. 70 resultMap := make(map[string]error, len(ids)) 71 for _, r := range res { 72 resultMap[r.Unit] = r.Error 73 } 74 75 result.Results = make([]params.ErrorResult, len(args.Entities)) 76 for i, id := range ids { 77 if err, ok := resultMap[id]; ok { 78 result.Results[i].Error = common.ServerError(err) 79 } else { 80 result.Results[i].Error = 81 common.ServerError(errors.NotFoundf("unit %q", args.Entities[i].Tag)) 82 } 83 } 84 85 return result, nil 86 } 87 88 // WatchUnitAssignments returns a strings watcher that is notified when new unit 89 // assignments are added to the db. 90 func (a *API) WatchUnitAssignments() (params.StringsWatchResult, error) { 91 watch := a.st.WatchForUnitAssignment() 92 if changes, ok := <-watch.Changes(); ok { 93 return params.StringsWatchResult{ 94 StringsWatcherId: a.res.Register(watch), 95 Changes: changes, 96 }, nil 97 } 98 return params.StringsWatchResult{}, watcher.EnsureErr(watch) 99 } 100 101 // SetAgentStatus will set status for agents of Units passed in args, if one 102 // of the args is not an Unit it will fail. 103 func (a *API) SetAgentStatus(args params.SetStatus) (params.ErrorResults, error) { 104 return a.statusSetter.SetStatus(args) 105 }