github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/client/machinemanager/machinemanager.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package machinemanager 5 6 import ( 7 "fmt" 8 9 "github.com/juju/errors" 10 "github.com/juju/loggo" 11 "github.com/juju/os" 12 "github.com/juju/os/series" 13 "gopkg.in/juju/names.v2" 14 15 "github.com/juju/juju/apiserver/common" 16 "github.com/juju/juju/apiserver/common/storagecommon" 17 "github.com/juju/juju/apiserver/facade" 18 "github.com/juju/juju/apiserver/params" 19 "github.com/juju/juju/core/instance" 20 "github.com/juju/juju/core/status" 21 "github.com/juju/juju/environs/config" 22 "github.com/juju/juju/environs/context" 23 "github.com/juju/juju/permission" 24 "github.com/juju/juju/state" 25 ) 26 27 var logger = loggo.GetLogger("juju.apiserver.machinemanager") 28 29 // MachineManagerAPI provides access to the MachineManager API facade. 30 type MachineManagerAPI struct { 31 st Backend 32 storageAccess storageInterface 33 pool Pool 34 authorizer facade.Authorizer 35 check *common.BlockChecker 36 resources facade.Resources 37 38 modelTag names.ModelTag 39 callContext context.ProviderCallContext 40 } 41 42 // NewFacade create a new server-side MachineManager API facade. This 43 // is used for facade registration. 44 func NewFacade(ctx facade.Context) (*MachineManagerAPI, error) { 45 st := ctx.State() 46 model, err := st.Model() 47 if err != nil { 48 return nil, errors.Trace(err) 49 } 50 backend := &stateShim{State: st} 51 storageAccess, err := getStorageState(st) 52 if err != nil { 53 return nil, errors.Trace(err) 54 } 55 pool := &poolShim{ctx.StatePool()} 56 return NewMachineManagerAPI(backend, storageAccess, pool, ctx.Auth(), model.ModelTag(), state.CallContext(st), ctx.Resources()) 57 } 58 59 // Version 4 of MachineManagerAPI 60 type MachineManagerAPIV4 struct { 61 *MachineManagerAPIV5 62 } 63 64 // Version 5 of Machine Manager API. 65 // Adds CreateUpgradeSeriesLock and removes UpdateMachineSeries. 66 type MachineManagerAPIV5 struct { 67 *MachineManagerAPI 68 } 69 70 // NewFacadeV4 creates a new server-side MachineManager API facade. 71 func NewFacadeV4(ctx facade.Context) (*MachineManagerAPIV4, error) { 72 machineManagerAPIV5, err := NewFacadeV5(ctx) 73 if err != nil { 74 return nil, errors.Trace(err) 75 } 76 return &MachineManagerAPIV4{machineManagerAPIV5}, nil 77 } 78 79 // NewFacadeV5 creates a new server-side MachineManager API facade. 80 func NewFacadeV5(ctx facade.Context) (*MachineManagerAPIV5, error) { 81 machineManagerAPI, err := NewFacade(ctx) 82 if err != nil { 83 return nil, errors.Trace(err) 84 } 85 return &MachineManagerAPIV5{machineManagerAPI}, nil 86 } 87 88 // NewMachineManagerAPI creates a new server-side MachineManager API facade. 89 func NewMachineManagerAPI( 90 backend Backend, 91 storageAccess storageInterface, 92 pool Pool, 93 auth facade.Authorizer, 94 modelTag names.ModelTag, 95 callCtx context.ProviderCallContext, 96 resources facade.Resources, 97 ) (*MachineManagerAPI, error) { 98 if !auth.AuthClient() { 99 return nil, common.ErrPerm 100 } 101 return &MachineManagerAPI{ 102 st: backend, 103 storageAccess: storageAccess, 104 pool: pool, 105 authorizer: auth, 106 check: common.NewBlockChecker(backend), 107 modelTag: modelTag, 108 callContext: callCtx, 109 resources: resources, 110 }, nil 111 } 112 113 func (mm *MachineManagerAPI) checkCanWrite() error { 114 return mm.checkAccess(permission.WriteAccess) 115 } 116 117 func (mm *MachineManagerAPI) checkCanRead() error { 118 return mm.checkAccess(permission.ReadAccess) 119 } 120 121 func (mm *MachineManagerAPI) checkAccess(access permission.Access) error { 122 canAccess, err := mm.authorizer.HasPermission(access, mm.modelTag) 123 if err != nil { 124 return errors.Trace(err) 125 } 126 if !canAccess { 127 return common.ErrPerm 128 } 129 return nil 130 } 131 132 // AddMachines adds new machines with the supplied parameters. 133 func (mm *MachineManagerAPI) AddMachines(args params.AddMachines) (params.AddMachinesResults, error) { 134 results := params.AddMachinesResults{ 135 Machines: make([]params.AddMachinesResult, len(args.MachineParams)), 136 } 137 if err := mm.checkCanWrite(); err != nil { 138 return results, err 139 } 140 if err := mm.check.ChangeAllowed(); err != nil { 141 return results, errors.Trace(err) 142 } 143 for i, p := range args.MachineParams { 144 m, err := mm.addOneMachine(p) 145 results.Machines[i].Error = common.ServerError(err) 146 if err == nil { 147 results.Machines[i].Machine = m.Id() 148 } 149 } 150 return results, nil 151 } 152 153 func (mm *MachineManagerAPI) addOneMachine(p params.AddMachineParams) (*state.Machine, error) { 154 if p.ParentId != "" && p.ContainerType == "" { 155 return nil, fmt.Errorf("parent machine specified without container type") 156 } 157 if p.ContainerType != "" && p.Placement != nil { 158 return nil, fmt.Errorf("container type and placement are mutually exclusive") 159 } 160 if p.Placement != nil { 161 // Extract container type and parent from container placement directives. 162 containerType, err := instance.ParseContainerType(p.Placement.Scope) 163 if err == nil { 164 p.ContainerType = containerType 165 p.ParentId = p.Placement.Directive 166 p.Placement = nil 167 } 168 } 169 170 if p.ContainerType != "" || p.Placement != nil { 171 // Guard against dubious client by making sure that 172 // the following attributes can only be set when we're 173 // not using placement. 174 p.InstanceId = "" 175 p.Nonce = "" 176 p.HardwareCharacteristics = instance.HardwareCharacteristics{} 177 p.Addrs = nil 178 } 179 180 if p.Series == "" { 181 model, err := mm.st.Model() 182 if err != nil { 183 return nil, errors.Trace(err) 184 } 185 conf, err := model.Config() 186 if err != nil { 187 return nil, errors.Trace(err) 188 } 189 p.Series = config.PreferredSeries(conf) 190 } 191 192 var placementDirective string 193 if p.Placement != nil { 194 model, err := mm.st.Model() 195 if err != nil { 196 return nil, errors.Trace(err) 197 } 198 // For 1.21 we should support both UUID and name, and with 1.22 199 // just support UUID 200 if p.Placement.Scope != model.Name() && p.Placement.Scope != model.UUID() { 201 return nil, fmt.Errorf("invalid model name %q", p.Placement.Scope) 202 } 203 placementDirective = p.Placement.Directive 204 } 205 206 volumes := make([]state.HostVolumeParams, 0, len(p.Disks)) 207 for _, cons := range p.Disks { 208 if cons.Count == 0 { 209 return nil, errors.Errorf("invalid volume params: count not specified") 210 } 211 // Pool and Size are validated by AddMachineX. 212 volumeParams := state.VolumeParams{ 213 Pool: cons.Pool, 214 Size: cons.Size, 215 } 216 volumeAttachmentParams := state.VolumeAttachmentParams{} 217 for i := uint64(0); i < cons.Count; i++ { 218 volumes = append(volumes, state.HostVolumeParams{ 219 volumeParams, volumeAttachmentParams, 220 }) 221 } 222 } 223 224 jobs, err := common.StateJobs(p.Jobs) 225 if err != nil { 226 return nil, errors.Trace(err) 227 } 228 template := state.MachineTemplate{ 229 Series: p.Series, 230 Constraints: p.Constraints, 231 Volumes: volumes, 232 InstanceId: p.InstanceId, 233 Jobs: jobs, 234 Nonce: p.Nonce, 235 HardwareCharacteristics: p.HardwareCharacteristics, 236 Addresses: params.NetworkAddresses(p.Addrs...), 237 Placement: placementDirective, 238 } 239 if p.ContainerType == "" { 240 return mm.st.AddOneMachine(template) 241 } 242 if p.ParentId != "" { 243 return mm.st.AddMachineInsideMachine(template, p.ParentId, p.ContainerType) 244 } 245 return mm.st.AddMachineInsideNewMachine(template, template, p.ContainerType) 246 } 247 248 // DestroyMachine removes a set of machines from the model. 249 func (mm *MachineManagerAPI) DestroyMachine(args params.Entities) (params.DestroyMachineResults, error) { 250 return mm.destroyMachine(args, false, false) 251 } 252 253 // ForceDestroyMachine forcibly removes a set of machines from the model. 254 func (mm *MachineManagerAPI) ForceDestroyMachine(args params.Entities) (params.DestroyMachineResults, error) { 255 return mm.destroyMachine(args, true, false) 256 } 257 258 // DestroyMachineWithParams removes a set of machines from the model. 259 func (mm *MachineManagerAPI) DestroyMachineWithParams(args params.DestroyMachinesParams) (params.DestroyMachineResults, error) { 260 entities := params.Entities{Entities: make([]params.Entity, len(args.MachineTags))} 261 for i, tag := range args.MachineTags { 262 entities.Entities[i].Tag = tag 263 } 264 return mm.destroyMachine(entities, args.Force, args.Keep) 265 } 266 267 func (mm *MachineManagerAPI) destroyMachine(args params.Entities, force, keep bool) (params.DestroyMachineResults, error) { 268 if err := mm.checkCanWrite(); err != nil { 269 return params.DestroyMachineResults{}, err 270 } 271 if err := mm.check.RemoveAllowed(); err != nil { 272 return params.DestroyMachineResults{}, err 273 } 274 destroyMachine := func(entity params.Entity) (*params.DestroyMachineInfo, error) { 275 machineTag, err := names.ParseMachineTag(entity.Tag) 276 if err != nil { 277 return nil, err 278 } 279 machine, err := mm.st.Machine(machineTag.Id()) 280 if err != nil { 281 return nil, err 282 } 283 if keep { 284 logger.Infof("destroy machine %v but keep instance", machineTag.Id()) 285 if err := machine.SetKeepInstance(keep); err != nil { 286 return nil, err 287 } 288 } 289 var info params.DestroyMachineInfo 290 units, err := machine.Units() 291 if err != nil { 292 return nil, err 293 } 294 storageSeen := names.NewSet() 295 for _, unit := range units { 296 info.DestroyedUnits = append( 297 info.DestroyedUnits, 298 params.Entity{Tag: unit.UnitTag().String()}, 299 ) 300 storage, err := storagecommon.UnitStorage(mm.storageAccess, unit.UnitTag()) 301 if err != nil { 302 return nil, err 303 } 304 305 // Filter out storage we've already seen. Shared 306 // storage may be attached to multiple units. 307 var unseen []state.StorageInstance 308 for _, storage := range storage { 309 storageTag := storage.StorageTag() 310 if storageSeen.Contains(storageTag) { 311 continue 312 } 313 storageSeen.Add(storageTag) 314 unseen = append(unseen, storage) 315 } 316 storage = unseen 317 318 destroyed, detached, err := storagecommon.ClassifyDetachedStorage( 319 mm.storageAccess.VolumeAccess(), mm.storageAccess.FilesystemAccess(), storage) 320 if err != nil { 321 return nil, err 322 } 323 info.DestroyedStorage = append(info.DestroyedStorage, destroyed...) 324 info.DetachedStorage = append(info.DetachedStorage, detached...) 325 } 326 destroy := machine.Destroy 327 if force { 328 destroy = machine.ForceDestroy 329 } 330 if err := destroy(); err != nil { 331 return nil, err 332 } 333 return &info, nil 334 } 335 results := make([]params.DestroyMachineResult, len(args.Entities)) 336 for i, entity := range args.Entities { 337 info, err := destroyMachine(entity) 338 if err != nil { 339 results[i].Error = common.ServerError(err) 340 continue 341 } 342 results[i].Info = info 343 } 344 return params.DestroyMachineResults{results}, nil 345 } 346 347 // UpgradeSeriesValidate validates that the incoming arguments correspond to a 348 // valid series upgrade for the target machine. 349 // If they do, a list of the machine's current units is returned for use in 350 // soliciting user confirmation of the command. 351 func (mm *MachineManagerAPI) UpgradeSeriesValidate( 352 args params.UpdateSeriesArgs, 353 ) (params.UpgradeSeriesUnitsResults, error) { 354 err := mm.checkCanRead() 355 if err != nil { 356 return params.UpgradeSeriesUnitsResults{}, err 357 } 358 359 results := make([]params.UpgradeSeriesUnitsResult, len(args.Args)) 360 for i, arg := range args.Args { 361 tag := arg.Entity.Tag 362 machine, err := mm.machineFromTag(tag) 363 if err != nil { 364 results[i].Error = common.ServerError(err) 365 continue 366 } 367 368 if machine.IsManager() { 369 results[i].Error = common.ServerError( 370 errors.Errorf("%s is a controller and cannot be targeted for series upgrade", tag)) 371 continue 372 } 373 374 err = mm.validateSeries(arg.Series, machine.Series(), tag) 375 if err != nil { 376 results[i].Error = common.ServerError(err) 377 continue 378 } 379 380 unitNames, err := mm.verifiedUnits(machine, arg.Series, arg.Force) 381 if err != nil { 382 results[i].Error = common.ServerError(err) 383 continue 384 } 385 results[i].UnitNames = unitNames 386 } 387 388 return params.UpgradeSeriesUnitsResults{Results: results}, nil 389 } 390 391 // UpgradeSeriesPrepare prepares a machine for a OS series upgrade. 392 func (mm *MachineManagerAPI) UpgradeSeriesPrepare(args params.UpdateSeriesArg) (params.ErrorResult, error) { 393 if err := mm.checkCanWrite(); err != nil { 394 return params.ErrorResult{}, err 395 } 396 if err := mm.check.ChangeAllowed(); err != nil { 397 return params.ErrorResult{}, err 398 } 399 err := mm.upgradeSeriesPrepare(args) 400 if err != nil { 401 return params.ErrorResult{Error: common.ServerError(err)}, nil 402 } 403 return params.ErrorResult{}, nil 404 } 405 406 func (mm *MachineManagerAPI) upgradeSeriesPrepare(arg params.UpdateSeriesArg) error { 407 if arg.Series == "" { 408 return ¶ms.Error{ 409 Message: "series missing from args", 410 Code: params.CodeBadRequest, 411 } 412 } 413 machineTag, err := names.ParseMachineTag(arg.Entity.Tag) 414 if err != nil { 415 return errors.Trace(err) 416 } 417 machine, err := mm.st.Machine(machineTag.Id()) 418 if err != nil { 419 return errors.Trace(err) 420 } 421 unitNames, err := mm.verifiedUnits(machine, arg.Series, arg.Force) 422 if err != nil { 423 return errors.Trace(err) 424 } 425 426 if err = machine.CreateUpgradeSeriesLock(unitNames, arg.Series); err != nil { 427 // TODO 2018-06-28 managed series upgrade 428 // improve error handling based on error type, there will be cases where retrying 429 // the hooks is needed etc. 430 return errors.Trace(err) 431 } 432 defer func() { 433 if err != nil { 434 if err2 := machine.RemoveUpgradeSeriesLock(); err2 != nil { 435 err = errors.Annotatef(err, "%s occurred while cleaning up from", err2) 436 } 437 } 438 }() 439 return nil 440 } 441 442 // UpgradeSeriesComplete marks a machine as having completed a managed series upgrade. 443 func (mm *MachineManagerAPI) UpgradeSeriesComplete(args params.UpdateSeriesArg) (params.ErrorResult, error) { 444 if err := mm.checkCanWrite(); err != nil { 445 return params.ErrorResult{}, err 446 } 447 if err := mm.check.ChangeAllowed(); err != nil { 448 return params.ErrorResult{}, err 449 } 450 if err := mm.check.ChangeAllowed(); err != nil { 451 return params.ErrorResult{}, err 452 } 453 err := mm.completeUpgradeSeries(args) 454 if err != nil { 455 return params.ErrorResult{Error: common.ServerError(err)}, nil 456 } 457 458 return params.ErrorResult{}, nil 459 } 460 461 func (mm *MachineManagerAPI) completeUpgradeSeries(arg params.UpdateSeriesArg) error { 462 machine, err := mm.machineFromTag(arg.Entity.Tag) 463 if err != nil { 464 return errors.Trace(err) 465 } 466 return machine.CompleteUpgradeSeries() 467 } 468 469 func (mm *MachineManagerAPI) removeUpgradeSeriesLock(arg params.UpdateSeriesArg) error { 470 machine, err := mm.machineFromTag(arg.Entity.Tag) 471 if err != nil { 472 return errors.Trace(err) 473 } 474 return machine.RemoveUpgradeSeriesLock() 475 } 476 477 // WatchUpgradeSeriesNotifications returns a watcher that fires on upgrade series events. 478 func (mm *MachineManagerAPI) WatchUpgradeSeriesNotifications(args params.Entities) (params.NotifyWatchResults, error) { 479 err := mm.checkCanRead() 480 if err != nil { 481 return params.NotifyWatchResults{}, err 482 } 483 result := params.NotifyWatchResults{ 484 Results: make([]params.NotifyWatchResult, len(args.Entities)), 485 } 486 for i, entity := range args.Entities { 487 tag, err := names.ParseTag(entity.Tag) 488 if err != nil { 489 result.Results[i].Error = common.ServerError(common.ErrPerm) 490 continue 491 } 492 watcherId := "" 493 machine, err := mm.st.Machine(tag.Id()) 494 if err != nil { 495 result.Results[i].Error = common.ServerError(err) 496 continue 497 } 498 w, err := machine.WatchUpgradeSeriesNotifications() 499 if err != nil { 500 result.Results[i].Error = common.ServerError(err) 501 continue 502 } 503 watcherId = mm.resources.Register(w) 504 result.Results[i].NotifyWatcherId = watcherId 505 } 506 return result, nil 507 } 508 509 // GetUpgradeSeriesMessages returns all new messages associated with upgrade 510 // series events. Messages that have already been retrieved once are not 511 // returned by this method. 512 func (mm *MachineManagerAPI) GetUpgradeSeriesMessages(args params.UpgradeSeriesNotificationParams) (params.StringsResults, error) { 513 if err := mm.checkCanRead(); err != nil { 514 return params.StringsResults{}, err 515 } 516 results := params.StringsResults{ 517 Results: make([]params.StringsResult, len(args.Params)), 518 } 519 for i, param := range args.Params { 520 machine, err := mm.machineFromTag(param.Entity.Tag) 521 if err != nil { 522 err = errors.Trace(err) 523 results.Results[i].Error = common.ServerError(err) 524 continue 525 } 526 messages, finished, err := machine.GetUpgradeSeriesMessages() 527 if err != nil { 528 results.Results[i].Error = common.ServerError(err) 529 continue 530 } 531 if finished { 532 // If there are no more messages we stop the watcher resource. 533 err = mm.resources.Stop(param.WatcherId) 534 if err != nil { 535 results.Results[i].Error = common.ServerError(err) 536 continue 537 } 538 } 539 results.Results[i].Result = messages 540 } 541 return results, nil 542 } 543 544 func (mm *MachineManagerAPI) machineFromTag(tag string) (Machine, error) { 545 machineTag, err := names.ParseMachineTag(tag) 546 if err != nil { 547 return nil, errors.Trace(err) 548 } 549 machine, err := mm.st.Machine(machineTag.Id()) 550 if err != nil { 551 return nil, errors.Trace(err) 552 } 553 return machine, nil 554 } 555 556 // verifiedUnits verifies that the machine units and their tree of subordinates 557 // all support the input series. If not, an error is returned. 558 // If they do, the agent statuses are checked to ensure that they are all in 559 // the idle state i.e. not installing, running hooks, or needing intervention. 560 // the final check is that the unit itself is not in an error state. 561 func (mm *MachineManagerAPI) verifiedUnits(machine Machine, series string, force bool) ([]string, error) { 562 principals := machine.Principals() 563 units, err := machine.VerifyUnitsSeries(principals, series, force) 564 if err != nil { 565 return nil, errors.Trace(err) 566 } 567 568 unitNames := make([]string, len(units)) 569 for i, u := range units { 570 agentStatus, err := u.AgentStatus() 571 if err != nil { 572 return nil, errors.Trace(err) 573 } 574 if agentStatus.Status != status.Idle { 575 return nil, errors.Errorf("unit %s is not ready to start a series upgrade; its agent status is: %q %s", 576 u.Name(), agentStatus.Status, agentStatus.Message) 577 } 578 unitStatus, err := u.Status() 579 if err != nil { 580 return nil, errors.Trace(err) 581 } 582 if unitStatus.Status == status.Error { 583 return nil, errors.Errorf("unit %s is not ready to start a series upgrade; its status is: \"error\" %s", 584 u.Name(), unitStatus.Message) 585 } 586 587 unitNames[i] = u.UnitTag().Id() 588 } 589 return unitNames, nil 590 } 591 592 // isSeriesLessThan returns a bool indicating whether the first argument's 593 // version is lexicographically less than the second argument's, thus indicating 594 // that the series represents an older version of the operating system. The 595 // output is only valid for Ubuntu series. 596 func isSeriesLessThan(series1, series2 string) (bool, error) { 597 version1, err := series.SeriesVersion(series1) 598 if err != nil { 599 return false, err 600 } 601 version2, err := series.SeriesVersion(series2) 602 if err != nil { 603 return false, err 604 } 605 return version2 > version1, nil 606 } 607 608 // DEPRECATED: UpdateMachineSeries returns an error. 609 func (mm *MachineManagerAPIV4) UpdateMachineSeries(_ params.UpdateSeriesArgs) (params.ErrorResults, error) { 610 return params.ErrorResults{ 611 Results: []params.ErrorResult{{ 612 Error: common.ServerError(errors.New("UpdateMachineSeries is no longer supported")), 613 }}, 614 }, nil 615 } 616 617 func (mm *MachineManagerAPI) validateSeries(argumentSeries, currentSeries string, machineTag string) error { 618 if argumentSeries == "" { 619 return ¶ms.Error{ 620 Message: "series missing from args", 621 Code: params.CodeBadRequest, 622 } 623 } 624 625 opSys, err := series.GetOSFromSeries(argumentSeries) 626 if err != nil { 627 return errors.Trace(err) 628 } 629 if opSys != os.Ubuntu { 630 return errors.Errorf("series %q is from OS %q and is not a valid upgrade target", 631 argumentSeries, opSys.String()) 632 } 633 634 opSys, err = series.GetOSFromSeries(currentSeries) 635 if err != nil { 636 return errors.Trace(err) 637 } 638 if opSys != os.Ubuntu { 639 return errors.Errorf("%s is running %s and is not valid for Ubuntu series upgrade", 640 machineTag, opSys.String()) 641 } 642 643 if argumentSeries == currentSeries { 644 return errors.Errorf("%s is already running series %s", machineTag, argumentSeries) 645 } 646 647 isOlderSeries, err := isSeriesLessThan(argumentSeries, currentSeries) 648 if err != nil { 649 return errors.Trace(err) 650 } 651 if isOlderSeries { 652 return errors.Errorf("machine %s is running %s which is a newer series than %s.", 653 machineTag, currentSeries, argumentSeries) 654 } 655 656 return nil 657 }