github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/apiserver/common/unitstate.go (about) 1 // Copyright 2020 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package common 5 6 import ( 7 "github.com/juju/errors" 8 "github.com/juju/loggo" 9 "github.com/juju/names/v5" 10 11 apiservererrors "github.com/juju/juju/apiserver/errors" 12 "github.com/juju/juju/apiserver/facade" 13 "github.com/juju/juju/controller" 14 "github.com/juju/juju/rpc/params" 15 "github.com/juju/juju/state" 16 ) 17 18 //go:generate go run go.uber.org/mock/mockgen -package mocks -destination mocks/unitstate.go github.com/juju/juju/apiserver/common UnitStateBackend,UnitStateUnit 19 //go:generate go run go.uber.org/mock/mockgen -package mocks -destination mocks/modeloperation.go github.com/juju/juju/state ModelOperation 20 21 // UnitStateUnit describes unit-receiver state methods required 22 // for UnitStateAPI. 23 type UnitStateBackend interface { 24 ApplyOperation(state.ModelOperation) error 25 Unit(string) (UnitStateUnit, error) 26 ControllerConfig() (controller.Config, error) 27 } 28 29 // UnitStateUnit describes unit-receiver state methods required 30 // for UnitStateAPI. 31 type UnitStateUnit interface { 32 SetStateOperation(*state.UnitState, state.UnitStateSizeLimits) state.ModelOperation 33 State() (*state.UnitState, error) 34 } 35 36 // UnitStateState implements the UnitStateBackend indirection 37 // over state.State. 38 type UnitStateState struct { 39 St *state.State 40 } 41 42 func (s UnitStateState) ApplyOperation(op state.ModelOperation) error { 43 return s.St.ApplyOperation(op) 44 } 45 46 func (s UnitStateState) Unit(name string) (UnitStateUnit, error) { 47 return s.St.Unit(name) 48 } 49 50 func (s UnitStateState) ControllerConfig() (controller.Config, error) { 51 return s.St.ControllerConfig() 52 } 53 54 type UnitStateAPI struct { 55 backend UnitStateBackend 56 resources facade.Resources 57 58 logger loggo.Logger 59 60 AccessMachine GetAuthFunc 61 accessUnit GetAuthFunc 62 } 63 64 // NewExternalUnitStateAPI can be used for API registration. 65 func NewExternalUnitStateAPI( 66 st *state.State, 67 resources facade.Resources, 68 authorizer facade.Authorizer, 69 accessUnit GetAuthFunc, 70 logger loggo.Logger, 71 ) *UnitStateAPI { 72 return NewUnitStateAPI(UnitStateState{St: st}, resources, authorizer, accessUnit, logger) 73 } 74 75 // NewUnitStateAPI returns a new UnitStateAPI. Currently both 76 // GetAuthFuncs can used to determine current permissions. 77 func NewUnitStateAPI( 78 backend UnitStateBackend, 79 resources facade.Resources, 80 authorizer facade.Authorizer, 81 accessUnit GetAuthFunc, 82 logger loggo.Logger, 83 ) *UnitStateAPI { 84 return &UnitStateAPI{ 85 backend: backend, 86 resources: resources, 87 accessUnit: accessUnit, 88 logger: logger, 89 } 90 } 91 92 // State returns the state persisted by the charm running in this unit 93 // and the state internal to the uniter for this unit. 94 func (u *UnitStateAPI) State(args params.Entities) (params.UnitStateResults, error) { 95 canAccess, err := u.accessUnit() 96 if err != nil { 97 return params.UnitStateResults{}, errors.Trace(err) 98 } 99 100 res := make([]params.UnitStateResult, len(args.Entities)) 101 for i, entity := range args.Entities { 102 unitTag, err := names.ParseUnitTag(entity.Tag) 103 if err != nil { 104 res[i].Error = apiservererrors.ServerError(err) 105 continue 106 } 107 108 if !canAccess(unitTag) { 109 res[i].Error = apiservererrors.ServerError(apiservererrors.ErrPerm) 110 continue 111 } 112 113 unit, err := u.getUnit(unitTag) 114 if err != nil { 115 res[i].Error = apiservererrors.ServerError(err) 116 continue 117 } 118 unitState, err := unit.State() 119 if err != nil { 120 res[i].Error = apiservererrors.ServerError(err) 121 continue 122 } 123 124 res[i].CharmState, _ = unitState.CharmState() 125 res[i].UniterState, _ = unitState.UniterState() 126 res[i].RelationState, _ = unitState.RelationState() 127 res[i].StorageState, _ = unitState.StorageState() 128 res[i].SecretState, _ = unitState.SecretState() 129 res[i].MeterStatusState, _ = unitState.MeterStatusState() 130 } 131 132 return params.UnitStateResults{Results: res}, nil 133 } 134 135 // SetState sets the state persisted by the charm running in this unit 136 // and the state internal to the uniter for this unit. 137 func (u *UnitStateAPI) SetState(args params.SetUnitStateArgs) (params.ErrorResults, error) { 138 canAccess, err := u.accessUnit() 139 if err != nil { 140 return params.ErrorResults{}, errors.Trace(err) 141 } 142 143 ctrlCfg, err := u.backend.ControllerConfig() 144 if err != nil { 145 return params.ErrorResults{}, errors.Trace(err) 146 } 147 148 res := make([]params.ErrorResult, len(args.Args)) 149 for i, arg := range args.Args { 150 unitTag, err := names.ParseUnitTag(arg.Tag) 151 if err != nil { 152 res[i].Error = apiservererrors.ServerError(err) 153 continue 154 } 155 156 if !canAccess(unitTag) { 157 res[i].Error = apiservererrors.ServerError(apiservererrors.ErrPerm) 158 continue 159 } 160 161 unit, err := u.getUnit(unitTag) 162 if err != nil { 163 res[i].Error = apiservererrors.ServerError(err) 164 continue 165 } 166 167 unitState := state.NewUnitState() 168 if arg.CharmState != nil { 169 unitState.SetCharmState(*arg.CharmState) 170 } 171 if arg.UniterState != nil { 172 unitState.SetUniterState(*arg.UniterState) 173 } 174 if arg.RelationState != nil { 175 unitState.SetRelationState(*arg.RelationState) 176 } 177 if arg.StorageState != nil { 178 unitState.SetStorageState(*arg.StorageState) 179 } 180 if arg.SecretState != nil { 181 unitState.SetSecretState(*arg.SecretState) 182 } 183 if arg.MeterStatusState != nil { 184 unitState.SetMeterStatusState(*arg.MeterStatusState) 185 } 186 187 ops := unit.SetStateOperation( 188 unitState, 189 state.UnitStateSizeLimits{ 190 MaxCharmStateSize: ctrlCfg.MaxCharmStateSize(), 191 MaxAgentStateSize: ctrlCfg.MaxAgentStateSize(), 192 }, 193 ) 194 if err = u.backend.ApplyOperation(ops); err != nil { 195 // Log quota-related errors to aid operators 196 if errors.IsQuotaLimitExceeded(err) { 197 logger.Errorf("%s: %v", unitTag, err) 198 } 199 res[i].Error = apiservererrors.ServerError(err) 200 } 201 } 202 203 return params.ErrorResults{Results: res}, nil 204 } 205 206 func (u *UnitStateAPI) getUnit(tag names.UnitTag) (UnitStateUnit, error) { 207 return u.backend.Unit(tag.Id()) 208 }