github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/common/upgradeseries.go (about) 1 // Copyright 2018 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 "gopkg.in/juju/names.v2" 10 11 "github.com/juju/juju/apiserver/facade" 12 "github.com/juju/juju/apiserver/params" 13 "github.com/juju/juju/core/model" 14 "github.com/juju/juju/state" 15 ) 16 17 //go:generate mockgen -package mocks -destination mocks/upgradeseries.go github.com/juju/juju/apiserver/common UpgradeSeriesBackend,UpgradeSeriesMachine,UpgradeSeriesUnit 18 19 type UpgradeSeriesBackend interface { 20 Machine(string) (UpgradeSeriesMachine, error) 21 Unit(string) (UpgradeSeriesUnit, error) 22 } 23 24 // UpgradeSeriesMachine describes machine-receiver state methods 25 // for executing a series upgrade. 26 type UpgradeSeriesMachine interface { 27 WatchUpgradeSeriesNotifications() (state.NotifyWatcher, error) 28 Units() ([]UpgradeSeriesUnit, error) 29 UpgradeSeriesStatus() (model.UpgradeSeriesStatus, error) 30 SetUpgradeSeriesStatus(model.UpgradeSeriesStatus, string) error 31 StartUpgradeSeriesUnitCompletion(string) error 32 UpgradeSeriesUnitStatuses() (map[string]state.UpgradeSeriesUnitStatus, error) 33 RemoveUpgradeSeriesLock() error 34 UpgradeSeriesTarget() (string, error) 35 Series() string 36 UpdateMachineSeries(series string, force bool) error 37 } 38 39 // UpgradeSeriesUnit describes unit-receiver state methods 40 // for executing a series upgrade. 41 type UpgradeSeriesUnit interface { 42 Tag() names.Tag 43 AssignedMachineId() (string, error) 44 UpgradeSeriesStatus() (model.UpgradeSeriesStatus, error) 45 SetUpgradeSeriesStatus(model.UpgradeSeriesStatus, string) error 46 } 47 48 // UpgradeSeriesState implements the UpgradeSeriesBackend indirection 49 // over state.State. 50 type UpgradeSeriesState struct { 51 St *state.State 52 } 53 54 func (s UpgradeSeriesState) Machine(id string) (UpgradeSeriesMachine, error) { 55 m, err := s.St.Machine(id) 56 return &upgradeSeriesMachine{m}, err 57 } 58 59 func (s UpgradeSeriesState) Unit(id string) (UpgradeSeriesUnit, error) { 60 return s.St.Unit(id) 61 } 62 63 type upgradeSeriesMachine struct { 64 *state.Machine 65 } 66 67 // Units maintains the UpgradeSeriesMachine indirection by wrapping the call to 68 // state.Machine.Units(). 69 func (m *upgradeSeriesMachine) Units() ([]UpgradeSeriesUnit, error) { 70 units, err := m.Machine.Units() 71 if err != nil { 72 return nil, errors.Trace(err) 73 } 74 75 wrapped := make([]UpgradeSeriesUnit, len(units)) 76 for i, u := range units { 77 wrapped[i] = u 78 } 79 return wrapped, nil 80 } 81 82 type UpgradeSeriesAPI struct { 83 backend UpgradeSeriesBackend 84 resources facade.Resources 85 86 logger loggo.Logger 87 88 accessUnitOrMachine GetAuthFunc 89 AccessMachine GetAuthFunc 90 accessUnit GetAuthFunc 91 } 92 93 // NewUpgradeSeriesAPI returns a new UpgradeSeriesAPI. Currently both 94 // GetAuthFuncs can used to determine current permissions. 95 func NewUpgradeSeriesAPI( 96 backend UpgradeSeriesBackend, 97 resources facade.Resources, 98 authorizer facade.Authorizer, 99 accessMachine GetAuthFunc, 100 accessUnit GetAuthFunc, 101 logger loggo.Logger, 102 ) *UpgradeSeriesAPI { 103 logger.Tracef("NewUpgradeSeriesAPI called with %s", authorizer.GetAuthTag()) 104 return &UpgradeSeriesAPI{ 105 backend: backend, 106 resources: resources, 107 accessUnitOrMachine: AuthAny(accessUnit, accessMachine), 108 AccessMachine: accessMachine, 109 accessUnit: accessUnit, 110 logger: logger, 111 } 112 } 113 114 // WatchUpgradeSeriesNotifications returns a NotifyWatcher for observing changes to upgrade series locks. 115 func (u *UpgradeSeriesAPI) WatchUpgradeSeriesNotifications(args params.Entities) (params.NotifyWatchResults, error) { 116 u.logger.Tracef("Starting WatchUpgradeSeriesNotifications with %+v", args) 117 result := params.NotifyWatchResults{ 118 Results: make([]params.NotifyWatchResult, len(args.Entities)), 119 } 120 canAccess, err := u.accessUnitOrMachine() 121 if err != nil { 122 return params.NotifyWatchResults{}, err 123 } 124 for i, entity := range args.Entities { 125 tag, err := names.ParseTag(entity.Tag) 126 if err != nil { 127 result.Results[i].Error = ServerError(ErrPerm) 128 continue 129 } 130 131 if !canAccess(tag) { 132 result.Results[i].Error = ServerError(ErrPerm) 133 continue 134 } 135 machine, err := u.GetMachine(tag) 136 if err != nil { 137 result.Results[i].Error = ServerError(err) 138 continue 139 } 140 w, err := machine.WatchUpgradeSeriesNotifications() 141 if err != nil { 142 result.Results[i].Error = ServerError(err) 143 continue 144 } 145 watcherId := u.resources.Register(w) 146 result.Results[i].NotifyWatcherId = watcherId 147 } 148 return result, nil 149 } 150 151 // UpgradeSeriesUnitStatus returns the current preparation status of an 152 // upgrading unit. 153 // If no series upgrade is in progress an error is returned instead. 154 func (u *UpgradeSeriesAPI) UpgradeSeriesUnitStatus(args params.Entities) (params.UpgradeSeriesStatusResults, error) { 155 u.logger.Tracef("Starting UpgradeSeriesUnitStatus with %+v", args) 156 return u.unitStatus(args) 157 } 158 159 // SetUpgradeSeriesUnitStatus sets the upgrade series status of the unit. 160 // If no upgrade is in progress an error is returned instead. 161 func (u *UpgradeSeriesAPI) SetUpgradeSeriesUnitStatus( 162 args params.UpgradeSeriesStatusParams, 163 ) (params.ErrorResults, error) { 164 u.logger.Tracef("Starting SetUpgradeSeriesUnitStatus with %+v", args) 165 return u.setUnitStatus(args) 166 } 167 168 func (u *UpgradeSeriesAPI) GetMachine(tag names.Tag) (UpgradeSeriesMachine, error) { 169 var id string 170 switch tag.Kind() { 171 case names.MachineTagKind: 172 id = tag.Id() 173 case names.UnitTagKind: 174 unit, err := u.backend.Unit(tag.Id()) 175 if err != nil { 176 177 } 178 id, err = unit.AssignedMachineId() 179 if err != nil { 180 return nil, err 181 } 182 default: 183 } 184 return u.backend.Machine(id) 185 } 186 187 func (u *UpgradeSeriesAPI) getUnit(tag names.Tag) (UpgradeSeriesUnit, error) { 188 return u.backend.Unit(tag.Id()) 189 } 190 191 // NewExternalUpgradeSeriesAPI can be used for API registration. 192 func NewExternalUpgradeSeriesAPI( 193 st *state.State, 194 resources facade.Resources, 195 authorizer facade.Authorizer, 196 accessMachine GetAuthFunc, 197 accessUnit GetAuthFunc, 198 logger loggo.Logger, 199 ) *UpgradeSeriesAPI { 200 return NewUpgradeSeriesAPI(UpgradeSeriesState{st}, resources, authorizer, accessMachine, accessUnit, logger) 201 } 202 203 func (u *UpgradeSeriesAPI) setUnitStatus(args params.UpgradeSeriesStatusParams) (params.ErrorResults, error) { 204 result := params.ErrorResults{ 205 Results: make([]params.ErrorResult, len(args.Params)), 206 } 207 canAccess, err := u.accessUnit() 208 if err != nil { 209 return params.ErrorResults{}, err 210 } 211 for i, p := range args.Params { 212 //TODO[externalreality] refactor all of this, its being copied often. 213 tag, err := names.ParseUnitTag(p.Entity.Tag) 214 if err != nil { 215 result.Results[i].Error = ServerError(ErrPerm) 216 continue 217 } 218 if !canAccess(tag) { 219 result.Results[i].Error = ServerError(ErrPerm) 220 continue 221 } 222 unit, err := u.getUnit(tag) 223 if err != nil { 224 result.Results[i].Error = ServerError(err) 225 continue 226 } 227 status, err := model.ValidateUpgradeSeriesStatus(p.Status) 228 if err != nil { 229 result.Results[i].Error = ServerError(err) 230 continue 231 } 232 err = unit.SetUpgradeSeriesStatus(status, p.Message) 233 if err != nil { 234 result.Results[i].Error = ServerError(err) 235 continue 236 } 237 } 238 return result, nil 239 } 240 241 func (u *UpgradeSeriesAPI) unitStatus(args params.Entities) (params.UpgradeSeriesStatusResults, error) { 242 canAccess, err := u.accessUnit() 243 if err != nil { 244 return params.UpgradeSeriesStatusResults{}, err 245 } 246 247 results := make([]params.UpgradeSeriesStatusResult, len(args.Entities)) 248 for i, entity := range args.Entities { 249 tag, err := names.ParseUnitTag(entity.Tag) 250 if err != nil { 251 results[i].Error = ServerError(ErrPerm) 252 continue 253 } 254 if !canAccess(tag) { 255 results[i].Error = ServerError(ErrPerm) 256 continue 257 } 258 unit, err := u.getUnit(tag) 259 if err != nil { 260 results[i].Error = ServerError(err) 261 continue 262 } 263 status, err := unit.UpgradeSeriesStatus() 264 if err != nil { 265 results[i].Error = ServerError(err) 266 continue 267 } 268 results[i].Status = status 269 } 270 return params.UpgradeSeriesStatusResults{Results: results}, nil 271 }