github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/agent/meterstatus/meterstatus.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 // Package meterstatus provides the meter status API facade. 5 package meterstatus 6 7 import ( 8 "gopkg.in/juju/names.v2" 9 10 "github.com/juju/errors" 11 "github.com/juju/juju/apiserver/common" 12 "github.com/juju/juju/apiserver/facade" 13 "github.com/juju/juju/apiserver/params" 14 "github.com/juju/juju/state" 15 "github.com/juju/juju/state/watcher" 16 ) 17 18 // MeterStatus defines the methods exported by the meter status API facade. 19 type MeterStatus interface { 20 GetMeterStatus(args params.Entities) (params.MeterStatusResults, error) 21 WatchMeterStatus(args params.Entities) (params.NotifyWatchResults, error) 22 } 23 24 // MeterStatusState represents the state of an model required by the MeterStatus. 25 //go:generate mockgen -package mocks -destination mocks/meterstatus_mock.go github.com/juju/juju/apiserver/facades/agent/meterstatus MeterStatusState 26 type MeterStatusState interface { 27 28 // Application returns a application state by name. 29 Application(name string) (*state.Application, error) 30 31 // Unit returns a unit by name. 32 Unit(id string) (*state.Unit, error) 33 } 34 35 // MeterStatusAPI implements the MeterStatus interface and is the concrete implementation 36 // of the API endpoint. 37 type MeterStatusAPI struct { 38 state MeterStatusState 39 accessUnit common.GetAuthFunc 40 resources facade.Resources 41 } 42 43 // NewMeterStatusFacade provides the signature required for facade registration. 44 func NewMeterStatusFacade(ctx facade.Context) (*MeterStatusAPI, error) { 45 authorizer := ctx.Auth() 46 resources := ctx.Resources() 47 return NewMeterStatusAPI(ctx.State(), resources, authorizer) 48 } 49 50 // NewMeterStatusAPI creates a new API endpoint for dealing with unit meter status. 51 func NewMeterStatusAPI( 52 st MeterStatusState, 53 resources facade.Resources, 54 authorizer facade.Authorizer, 55 ) (*MeterStatusAPI, error) { 56 if !authorizer.AuthUnitAgent() && !authorizer.AuthApplicationAgent() { 57 return nil, common.ErrPerm 58 } 59 return &MeterStatusAPI{ 60 state: st, 61 accessUnit: func() (common.AuthFunc, error) { 62 switch tag := authorizer.GetAuthTag().(type) { 63 case names.ApplicationTag: 64 // If called by an application agent, any of the units 65 // belonging to that application can be accessed. 66 app, err := st.Application(tag.Name) 67 if err != nil { 68 return nil, errors.Trace(err) 69 } 70 allUnits, err := app.AllUnits() 71 if err != nil { 72 return nil, errors.Trace(err) 73 } 74 return func(tag names.Tag) bool { 75 for _, u := range allUnits { 76 if u.Tag() == tag { 77 return true 78 } 79 } 80 return false 81 }, nil 82 case names.UnitTag: 83 return func(tag names.Tag) bool { 84 return authorizer.AuthOwner(tag) 85 }, nil 86 default: 87 return nil, errors.Errorf("expected names.UnitTag or names.ApplicationTag, got %T", tag) 88 } 89 }, 90 resources: resources, 91 }, nil 92 } 93 94 // WatchMeterStatus returns a NotifyWatcher for observing changes 95 // to each unit's meter status. 96 func (m *MeterStatusAPI) WatchMeterStatus(args params.Entities) (params.NotifyWatchResults, error) { 97 result := params.NotifyWatchResults{ 98 Results: make([]params.NotifyWatchResult, len(args.Entities)), 99 } 100 canAccess, err := m.accessUnit() 101 if err != nil { 102 return params.NotifyWatchResults{}, err 103 } 104 for i, entity := range args.Entities { 105 tag, err := names.ParseUnitTag(entity.Tag) 106 if err != nil { 107 result.Results[i].Error = common.ServerError(common.ErrPerm) 108 continue 109 } 110 err = common.ErrPerm 111 var watcherId string 112 if canAccess(tag) { 113 watcherId, err = m.watchOneUnitMeterStatus(tag) 114 } 115 result.Results[i].NotifyWatcherId = watcherId 116 result.Results[i].Error = common.ServerError(err) 117 } 118 return result, nil 119 } 120 121 func (m *MeterStatusAPI) watchOneUnitMeterStatus(tag names.UnitTag) (string, error) { 122 unit, err := m.state.Unit(tag.Id()) 123 if err != nil { 124 return "", err 125 } 126 watch := unit.WatchMeterStatus() 127 if _, ok := <-watch.Changes(); ok { 128 return m.resources.Register(watch), nil 129 } 130 return "", watcher.EnsureErr(watch) 131 } 132 133 // GetMeterStatus returns meter status information for each unit. 134 func (m *MeterStatusAPI) GetMeterStatus(args params.Entities) (params.MeterStatusResults, error) { 135 result := params.MeterStatusResults{ 136 Results: make([]params.MeterStatusResult, len(args.Entities)), 137 } 138 canAccess, err := m.accessUnit() 139 if err != nil { 140 return params.MeterStatusResults{}, common.ErrPerm 141 } 142 for i, entity := range args.Entities { 143 unitTag, err := names.ParseUnitTag(entity.Tag) 144 if err != nil { 145 result.Results[i].Error = common.ServerError(common.ErrPerm) 146 continue 147 } 148 err = common.ErrPerm 149 var status state.MeterStatus 150 if canAccess(unitTag) { 151 var unit *state.Unit 152 unit, err = m.state.Unit(unitTag.Id()) 153 if err == nil { 154 status, err = MeterStatusWrapper(unit.GetMeterStatus) 155 } 156 result.Results[i].Code = status.Code.String() 157 result.Results[i].Info = status.Info 158 } 159 result.Results[i].Error = common.ServerError(err) 160 } 161 return result, nil 162 }