github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/agent/upgradeseries/upgradeseries.go (about) 1 // Copyright 2018 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package upgradeseries 5 6 import ( 7 "github.com/juju/loggo" 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/core/model" 15 ) 16 17 var logger = loggo.GetLogger("juju.apiserver.upgradeseries") 18 19 // API serves methods required by the machine agent upgrade-series worker. 20 type API struct { 21 *common.UpgradeSeriesAPI 22 common.LeadershipPinningAPI 23 24 st common.UpgradeSeriesBackend 25 auth facade.Authorizer 26 resources facade.Resources 27 } 28 29 // NewAPI creates a new instance of the API with the given context 30 func NewAPI(ctx facade.Context) (*API, error) { 31 leadership, err := common.NewLeadershipPinningFacade(ctx) 32 if err != nil { 33 if errors.IsNotImplemented(errors.Cause(err)) { 34 leadership = disabledLeadershipPinningFacade{} 35 } else { 36 return nil, errors.Trace(err) 37 } 38 } 39 return NewUpgradeSeriesAPI(common.UpgradeSeriesState{St: ctx.State()}, ctx.Resources(), ctx.Auth(), leadership) 40 } 41 42 // NewUpgradeSeriesAPI creates a new instance of the API server using the 43 // dedicated state indirection. 44 func NewUpgradeSeriesAPI( 45 st common.UpgradeSeriesBackend, 46 resources facade.Resources, 47 authorizer facade.Authorizer, 48 leadership common.LeadershipPinningAPI, 49 ) (*API, error) { 50 if !authorizer.AuthMachineAgent() { 51 return nil, common.ErrPerm 52 } 53 54 accessMachine := func() (common.AuthFunc, error) { 55 return func(tag names.Tag) bool { 56 return authorizer.AuthOwner(tag) 57 }, nil 58 } 59 accessUnit := func() (common.AuthFunc, error) { 60 return func(tag names.Tag) bool { 61 return false 62 }, nil 63 } 64 65 return &API{ 66 st: st, 67 resources: resources, 68 auth: authorizer, 69 UpgradeSeriesAPI: common.NewUpgradeSeriesAPI(st, resources, authorizer, accessMachine, accessUnit, logger), 70 LeadershipPinningAPI: leadership, 71 }, nil 72 } 73 74 // MachineStatus gets the current upgrade-series status of a machine. 75 func (a *API) MachineStatus(args params.Entities) (params.UpgradeSeriesStatusResults, error) { 76 result := params.UpgradeSeriesStatusResults{} 77 78 canAccess, err := a.AccessMachine() 79 if err != nil { 80 return result, err 81 } 82 83 results := make([]params.UpgradeSeriesStatusResult, len(args.Entities)) 84 for i, entity := range args.Entities { 85 machine, err := a.authAndMachine(entity, canAccess) 86 if err != nil { 87 results[i].Error = common.ServerError(err) 88 continue 89 } 90 status, err := machine.UpgradeSeriesStatus() 91 if err != nil { 92 results[i].Error = common.ServerError(err) 93 continue 94 } 95 results[i].Status = status 96 } 97 98 result.Results = results 99 return result, nil 100 } 101 102 // SetMachineStatus sets the current upgrade-series status of a machine. 103 func (a *API) SetMachineStatus(args params.UpgradeSeriesStatusParams) (params.ErrorResults, error) { 104 result := params.ErrorResults{} 105 106 canAccess, err := a.AccessMachine() 107 if err != nil { 108 return result, err 109 } 110 111 results := make([]params.ErrorResult, len(args.Params)) 112 for i, param := range args.Params { 113 machine, err := a.authAndMachine(param.Entity, canAccess) 114 if err != nil { 115 results[i].Error = common.ServerError(err) 116 continue 117 } 118 err = machine.SetUpgradeSeriesStatus(param.Status, param.Message) 119 if err != nil { 120 results[i].Error = common.ServerError(err) 121 } 122 } 123 124 result.Results = results 125 return result, nil 126 } 127 128 // TargetSeries returns the series that a machine has been locked 129 // for upgrading to. 130 func (a *API) TargetSeries(args params.Entities) (params.StringResults, error) { 131 result := params.StringResults{} 132 133 canAccess, err := a.AccessMachine() 134 if err != nil { 135 return result, err 136 } 137 138 results := make([]params.StringResult, len(args.Entities)) 139 for i, entity := range args.Entities { 140 machine, err := a.authAndMachine(entity, canAccess) 141 if err != nil { 142 results[i].Error = common.ServerError(err) 143 continue 144 } 145 target, err := machine.UpgradeSeriesTarget() 146 if err != nil { 147 results[i].Error = common.ServerError(err) 148 } 149 results[i].Result = target 150 } 151 152 result.Results = results 153 return result, nil 154 } 155 156 // StartUnitCompletion starts the upgrade series completion phase for all subordinate 157 // units of a given machine. 158 func (a *API) StartUnitCompletion(args params.UpgradeSeriesStartUnitCompletionParam) (params.ErrorResults, error) { 159 result := params.ErrorResults{ 160 Results: make([]params.ErrorResult, len(args.Entities)), 161 } 162 canAccess, err := a.AccessMachine() 163 if err != nil { 164 return params.ErrorResults{}, err 165 } 166 for i, entity := range args.Entities { 167 machine, err := a.authAndMachine(entity, canAccess) 168 if err != nil { 169 result.Results[i].Error = common.ServerError(err) 170 continue 171 } 172 err = machine.StartUpgradeSeriesUnitCompletion(args.Message) 173 if err != nil { 174 result.Results[i].Error = common.ServerError(err) 175 continue 176 } 177 } 178 return result, nil 179 } 180 181 // FinishUpgradeSeries is the last action in the upgrade workflow and is 182 // called after all machine and unit statuses are "completed". 183 // It updates the machine series to reflect the completed upgrade, then 184 // removes the upgrade-series lock. 185 func (a *API) FinishUpgradeSeries(args params.UpdateSeriesArgs) (params.ErrorResults, error) { 186 result := params.ErrorResults{ 187 Results: make([]params.ErrorResult, len(args.Args)), 188 } 189 canAccess, err := a.AccessMachine() 190 if err != nil { 191 return params.ErrorResults{}, err 192 } 193 for i, arg := range args.Args { 194 machine, err := a.authAndMachine(arg.Entity, canAccess) 195 if err != nil { 196 result.Results[i].Error = common.ServerError(err) 197 continue 198 } 199 200 // Actually running "do-release-upgrade" is not required to complete a 201 // series upgrade, so we compare the incoming host OS with the machine. 202 // Only update if they differ, because calling UpgradeSeriesTarget 203 // cascades through units and subordinates to verify series support, 204 // which we might as well skip unless an update is required. 205 ms := machine.Series() 206 if arg.Series == ms { 207 logger.Debugf("%q series is unchanged from %q", arg.Entity.Tag, ms) 208 } else { 209 if err := machine.UpdateMachineSeries(arg.Series, true); err != nil { 210 result.Results[i].Error = common.ServerError(err) 211 continue 212 } 213 } 214 215 err = machine.RemoveUpgradeSeriesLock() 216 if err != nil { 217 result.Results[i].Error = common.ServerError(err) 218 continue 219 } 220 } 221 return result, nil 222 } 223 224 // UnitsPrepared returns the units running on this machine that have completed 225 // their upgrade-series preparation, and are ready to be stopped and have their 226 // unit agent services converted for the target series. 227 func (a *API) UnitsPrepared(args params.Entities) (params.EntitiesResults, error) { 228 result, err := a.unitsInState(args, model.UpgradeSeriesPrepareCompleted) 229 return result, errors.Trace(err) 230 } 231 232 // UnitsCompleted returns the units running on this machine that have completed 233 // the upgrade-series workflow and are in their normal running state. 234 func (a *API) UnitsCompleted(args params.Entities) (params.EntitiesResults, error) { 235 result, err := a.unitsInState(args, model.UpgradeSeriesCompleted) 236 return result, errors.Trace(err) 237 } 238 239 func (a *API) unitsInState(args params.Entities, status model.UpgradeSeriesStatus) (params.EntitiesResults, error) { 240 result := params.EntitiesResults{} 241 242 canAccess, err := a.AccessMachine() 243 if err != nil { 244 return result, err 245 } 246 247 results := make([]params.EntitiesResult, len(args.Entities)) 248 for i, entity := range args.Entities { 249 machine, err := a.authAndMachine(entity, canAccess) 250 if err != nil { 251 results[i].Error = common.ServerError(err) 252 continue 253 } 254 255 statuses, err := machine.UpgradeSeriesUnitStatuses() 256 if err != nil { 257 results[i].Error = common.ServerError(err) 258 continue 259 } 260 261 var entities []params.Entity 262 for id, s := range statuses { 263 if s.Status == status { 264 entities = append(entities, params.Entity{Tag: names.NewUnitTag(id).String()}) 265 } 266 } 267 results[i].Entities = entities 268 } 269 270 result.Results = results 271 return result, nil 272 } 273 274 func (a *API) authAndMachine(e params.Entity, canAccess common.AuthFunc) (common.UpgradeSeriesMachine, error) { 275 tag, err := names.ParseMachineTag(e.Tag) 276 if err != nil { 277 return nil, err 278 } 279 if !canAccess(tag) { 280 return nil, common.ErrPerm 281 } 282 return a.GetMachine(tag) 283 } 284 285 // disabledLeadershipPinningFacade implements the LeadershipPinningAPI, but 286 // provides a no-operation for pinning operations 287 type disabledLeadershipPinningFacade struct{} 288 289 // PinMachineApplications implements common.LeadershipPinningAPI 290 func (disabledLeadershipPinningFacade) PinMachineApplications() (params.PinApplicationsResults, error) { 291 return params.PinApplicationsResults{}, errors.NotImplementedf( 292 "unable to get leadership pinner; pinning is not available with the legacy lease manager") 293 } 294 295 // UnpinMachineApplications implements common.LeadershipPinningAPI 296 func (disabledLeadershipPinningFacade) UnpinMachineApplications() (params.PinApplicationsResults, error) { 297 return params.PinApplicationsResults{}, nil 298 } 299 300 // PinnedLeadership implements common.LeadershipPinningAPI 301 func (disabledLeadershipPinningFacade) PinnedLeadership() (params.PinnedLeadershipResult, error) { 302 return params.PinnedLeadershipResult{}, nil 303 }