github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/agent/provisioner/provisioner.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package provisioner 5 6 import ( 7 "time" 8 9 "github.com/juju/collections/set" 10 "github.com/juju/errors" 11 "github.com/juju/loggo" 12 "gopkg.in/juju/charm.v6" 13 "gopkg.in/juju/names.v2" 14 15 "github.com/juju/juju/apiserver/common" 16 "github.com/juju/juju/apiserver/common/networkingcommon" 17 "github.com/juju/juju/apiserver/common/storagecommon" 18 "github.com/juju/juju/apiserver/facade" 19 "github.com/juju/juju/apiserver/params" 20 "github.com/juju/juju/container" 21 "github.com/juju/juju/core/constraints" 22 "github.com/juju/juju/core/instance" 23 "github.com/juju/juju/core/lxdprofile" 24 "github.com/juju/juju/core/status" 25 "github.com/juju/juju/environs" 26 "github.com/juju/juju/environs/config" 27 "github.com/juju/juju/environs/context" 28 "github.com/juju/juju/network" 29 "github.com/juju/juju/network/containerizer" 30 "github.com/juju/juju/state" 31 "github.com/juju/juju/state/stateenvirons" 32 "github.com/juju/juju/state/watcher" 33 "github.com/juju/juju/storage" 34 "github.com/juju/juju/storage/poolmanager" 35 ) 36 37 var logger = loggo.GetLogger("juju.apiserver.provisioner") 38 39 // ProvisionerAPI provides access to the Provisioner API facade. 40 type ProvisionerAPI struct { 41 *common.ControllerConfigAPI 42 *common.Remover 43 *common.StatusSetter 44 *common.StatusGetter 45 *common.DeadEnsurer 46 *common.PasswordChanger 47 *common.LifeGetter 48 *common.StateAddresser 49 *common.APIAddresser 50 *common.ModelWatcher 51 *common.ModelMachinesWatcher 52 *common.InstanceIdGetter 53 *common.ToolsFinder 54 *common.ToolsGetter 55 *networkingcommon.NetworkConfigAPI 56 57 st *state.State 58 m *state.Model 59 resources facade.Resources 60 authorizer facade.Authorizer 61 storageProviderRegistry storage.ProviderRegistry 62 storagePoolManager poolmanager.PoolManager 63 configGetter environs.EnvironConfigGetter 64 getAuthFunc common.GetAuthFunc 65 getCanModify common.GetAuthFunc 66 providerCallContext context.ProviderCallContext 67 } 68 69 // NewProvisionerAPI creates a new server-side ProvisionerAPI facade. 70 func NewProvisionerAPI(st *state.State, resources facade.Resources, authorizer facade.Authorizer) (*ProvisionerAPI, error) { 71 if !authorizer.AuthMachineAgent() && !authorizer.AuthController() { 72 return nil, common.ErrPerm 73 } 74 getAuthFunc := func() (common.AuthFunc, error) { 75 isModelManager := authorizer.AuthController() 76 isMachineAgent := authorizer.AuthMachineAgent() 77 authEntityTag := authorizer.GetAuthTag() 78 79 return func(tag names.Tag) bool { 80 if isMachineAgent && tag == authEntityTag { 81 // A machine agent can always access its own machine. 82 return true 83 } 84 switch tag := tag.(type) { 85 case names.MachineTag: 86 parentId := state.ParentId(tag.Id()) 87 if parentId == "" { 88 // All top-level machines are accessible by the controller. 89 return isModelManager 90 } 91 // All containers with the authenticated machine as a 92 // parent are accessible by it. 93 // TODO(dfc) sometimes authEntity tag is nil, which is fine because nil is 94 // only equal to nil, but it suggests someone is passing an authorizer 95 // with a nil tag. 96 return isMachineAgent && names.NewMachineTag(parentId) == authEntityTag 97 default: 98 return false 99 } 100 }, nil 101 } 102 getCanModify := func() (common.AuthFunc, error) { 103 return authorizer.AuthOwner, nil 104 } 105 getAuthOwner := func() (common.AuthFunc, error) { 106 return authorizer.AuthOwner, nil 107 } 108 model, err := st.Model() 109 if err != nil { 110 return nil, err 111 } 112 configGetter := stateenvirons.EnvironConfigGetter{st, model} 113 env, err := environs.GetEnviron(configGetter, environs.New) 114 if err != nil { 115 return nil, err 116 } 117 urlGetter := common.NewToolsURLGetter(model.UUID(), st) 118 storageProviderRegistry := stateenvirons.NewStorageProviderRegistry(env) 119 120 callCtx := state.CallContext(st) 121 return &ProvisionerAPI{ 122 Remover: common.NewRemover(st, false, getAuthFunc), 123 StatusSetter: common.NewStatusSetter(st, getAuthFunc), 124 StatusGetter: common.NewStatusGetter(st, getAuthFunc), 125 DeadEnsurer: common.NewDeadEnsurer(st, getAuthFunc), 126 PasswordChanger: common.NewPasswordChanger(st, getAuthFunc), 127 LifeGetter: common.NewLifeGetter(st, getAuthFunc), 128 StateAddresser: common.NewStateAddresser(st), 129 APIAddresser: common.NewAPIAddresser(st, resources), 130 ModelWatcher: common.NewModelWatcher(model, resources, authorizer), 131 ModelMachinesWatcher: common.NewModelMachinesWatcher(st, resources, authorizer), 132 ControllerConfigAPI: common.NewStateControllerConfig(st), 133 InstanceIdGetter: common.NewInstanceIdGetter(st, getAuthFunc), 134 ToolsFinder: common.NewToolsFinder(configGetter, st, urlGetter), 135 ToolsGetter: common.NewToolsGetter(st, configGetter, st, urlGetter, getAuthOwner), 136 NetworkConfigAPI: networkingcommon.NewNetworkConfigAPI(st, callCtx, getCanModify), 137 st: st, 138 m: model, 139 resources: resources, 140 authorizer: authorizer, 141 configGetter: configGetter, 142 storageProviderRegistry: storageProviderRegistry, 143 storagePoolManager: poolmanager.New(state.NewStateSettings(st), storageProviderRegistry), 144 getAuthFunc: getAuthFunc, 145 getCanModify: getCanModify, 146 providerCallContext: callCtx, 147 }, nil 148 } 149 150 // ProvisionerAPIV4 provides v4 (and v3 for some reason) of the provisioner facade. 151 type ProvisionerAPIV4 struct { 152 *ProvisionerAPIV5 153 } 154 155 // ProvisionerAPIV5 provides v5 of the provisioner facade. 156 type ProvisionerAPIV5 struct { 157 *ProvisionerAPIV6 158 } 159 160 // ProvisionerAPIV6 provides v6 of the provisioner facade. 161 type ProvisionerAPIV6 struct { 162 *ProvisionerAPIV7 163 } 164 165 // ProvisionerAPIV7 provides v7 of the provisioner facade. 166 type ProvisionerAPIV7 struct { 167 *ProvisionerAPI 168 } 169 170 // NewProvisionerAPIV4 creates a new server-side version 4 Provisioner API facade. 171 func NewProvisionerAPIV4(st *state.State, resources facade.Resources, authorizer facade.Authorizer) (*ProvisionerAPIV4, error) { 172 provisionerAPI, err := NewProvisionerAPIV5(st, resources, authorizer) 173 if err != nil { 174 return nil, errors.Trace(err) 175 } 176 return &ProvisionerAPIV4{provisionerAPI}, nil 177 } 178 179 // NewProvisionerAPIV5 creates a new server-side Provisioner API facade. 180 func NewProvisionerAPIV5(st *state.State, resources facade.Resources, authorizer facade.Authorizer) (*ProvisionerAPIV5, error) { 181 provisionerAPI, err := NewProvisionerAPIV6(st, resources, authorizer) 182 if err != nil { 183 return nil, errors.Trace(err) 184 } 185 return &ProvisionerAPIV5{provisionerAPI}, nil 186 } 187 188 // NewProvisionerAPIV6 creates a new server-side Provisioner API facade. 189 func NewProvisionerAPIV6(st *state.State, resources facade.Resources, authorizer facade.Authorizer) (*ProvisionerAPIV6, error) { 190 provisionerAPI, err := NewProvisionerAPIV7(st, resources, authorizer) 191 if err != nil { 192 return nil, errors.Trace(err) 193 } 194 return &ProvisionerAPIV6{provisionerAPI}, nil 195 } 196 197 // NewProvisionerAPIV7 creates a new server-side Provisioner API facade. 198 func NewProvisionerAPIV7(st *state.State, resources facade.Resources, authorizer facade.Authorizer) (*ProvisionerAPIV7, error) { 199 provisionerAPI, err := NewProvisionerAPI(st, resources, authorizer) 200 if err != nil { 201 return nil, errors.Trace(err) 202 } 203 return &ProvisionerAPIV7{provisionerAPI}, nil 204 } 205 206 func (p *ProvisionerAPI) getMachine(canAccess common.AuthFunc, tag names.MachineTag) (*state.Machine, error) { 207 if !canAccess(tag) { 208 return nil, common.ErrPerm 209 } 210 entity, err := p.st.FindEntity(tag) 211 if err != nil { 212 return nil, err 213 } 214 // The authorization function guarantees that the tag represents a 215 // machine. 216 return entity.(*state.Machine), nil 217 } 218 219 func (p *ProvisionerAPI) watchOneMachineContainers(arg params.WatchContainer) (params.StringsWatchResult, error) { 220 nothing := params.StringsWatchResult{} 221 canAccess, err := p.getAuthFunc() 222 if err != nil { 223 return nothing, common.ErrPerm 224 } 225 tag, err := names.ParseMachineTag(arg.MachineTag) 226 if err != nil { 227 return nothing, common.ErrPerm 228 } 229 if !canAccess(tag) { 230 return nothing, common.ErrPerm 231 } 232 machine, err := p.st.Machine(tag.Id()) 233 if err != nil { 234 return nothing, err 235 } 236 var watch state.StringsWatcher 237 if arg.ContainerType != "" { 238 watch = machine.WatchContainers(instance.ContainerType(arg.ContainerType)) 239 } else { 240 watch = machine.WatchAllContainers() 241 } 242 // Consume the initial event and forward it to the result. 243 if changes, ok := <-watch.Changes(); ok { 244 return params.StringsWatchResult{ 245 StringsWatcherId: p.resources.Register(watch), 246 Changes: changes, 247 }, nil 248 } 249 return nothing, watcher.EnsureErr(watch) 250 } 251 252 // WatchContainers starts a StringsWatcher to watch containers deployed to 253 // any machine passed in args. 254 func (p *ProvisionerAPI) WatchContainers(args params.WatchContainers) (params.StringsWatchResults, error) { 255 result := params.StringsWatchResults{ 256 Results: make([]params.StringsWatchResult, len(args.Params)), 257 } 258 for i, arg := range args.Params { 259 watcherResult, err := p.watchOneMachineContainers(arg) 260 result.Results[i] = watcherResult 261 result.Results[i].Error = common.ServerError(err) 262 } 263 return result, nil 264 } 265 266 // WatchAllContainers starts a StringsWatcher to watch all containers deployed to 267 // any machine passed in args. 268 func (p *ProvisionerAPI) WatchAllContainers(args params.WatchContainers) (params.StringsWatchResults, error) { 269 return p.WatchContainers(args) 270 } 271 272 func (p *ProvisionerAPI) watchOneMachineContainersCharmProfiles(arg params.WatchContainer) (params.StringsWatchResult, error) { 273 nothing := params.StringsWatchResult{} 274 canAccess, err := p.getAuthFunc() 275 if err != nil { 276 return nothing, common.ErrPerm 277 } 278 tag, err := names.ParseMachineTag(arg.MachineTag) 279 if err != nil { 280 return nothing, common.ErrPerm 281 } 282 if !canAccess(tag) { 283 return nothing, common.ErrPerm 284 } 285 machine, err := p.st.Machine(tag.Id()) 286 if err != nil { 287 return nothing, err 288 } 289 var watch state.StringsWatcher 290 if arg.ContainerType != "" { 291 watch, err = machine.WatchContainersCharmProfiles(instance.ContainerType(arg.ContainerType)) 292 if err != nil { 293 return nothing, common.ErrPerm 294 } 295 } else { 296 return nothing, errors.BadRequestf("ContainerType not specified") 297 } 298 // Consume the initial event and forward it to the result. 299 if changes, ok := <-watch.Changes(); ok { 300 return params.StringsWatchResult{ 301 StringsWatcherId: p.resources.Register(watch), 302 Changes: changes, 303 }, nil 304 } 305 return nothing, watcher.EnsureErr(watch) 306 } 307 308 // WatchContainersCharmProfiles starts a StringsWatcher to notifies when 309 // the provisioner should update the charm profiles used by a container on 310 // the given machine. 311 func (p *ProvisionerAPI) WatchContainersCharmProfiles(args params.WatchContainers) (params.StringsWatchResults, error) { 312 result := params.StringsWatchResults{ 313 Results: make([]params.StringsWatchResult, len(args.Params)), 314 } 315 for i, arg := range args.Params { 316 watcherResult, err := p.watchOneMachineContainersCharmProfiles(arg) 317 result.Results[i] = watcherResult 318 result.Results[i].Error = common.ServerError(err) 319 } 320 return result, nil 321 } 322 323 // SetSupportedContainers updates the list of containers supported by the machines passed in args. 324 func (p *ProvisionerAPI) SetSupportedContainers(args params.MachineContainersParams) (params.ErrorResults, error) { 325 result := params.ErrorResults{ 326 Results: make([]params.ErrorResult, len(args.Params)), 327 } 328 329 canAccess, err := p.getAuthFunc() 330 if err != nil { 331 return result, err 332 } 333 for i, arg := range args.Params { 334 tag, err := names.ParseMachineTag(arg.MachineTag) 335 if err != nil { 336 logger.Warningf("SetSupportedContainers called with %q which is not a valid machine tag: %v", arg.MachineTag, err) 337 result.Results[i].Error = common.ServerError(common.ErrPerm) 338 continue 339 } 340 machine, err := p.getMachine(canAccess, tag) 341 if err != nil { 342 result.Results[i].Error = common.ServerError(err) 343 continue 344 } 345 if len(arg.ContainerTypes) == 0 { 346 err = machine.SupportsNoContainers() 347 } else { 348 err = machine.SetSupportedContainers(arg.ContainerTypes) 349 } 350 if err != nil { 351 result.Results[i].Error = common.ServerError(err) 352 } 353 } 354 return result, nil 355 } 356 357 // ContainerManagerConfig returns information from the model config that is 358 // needed for configuring the container manager. 359 func (p *ProvisionerAPI) ContainerManagerConfig(args params.ContainerManagerConfigParams) (params.ContainerManagerConfig, error) { 360 var result params.ContainerManagerConfig 361 cfg := make(map[string]string) 362 cfg[container.ConfigModelUUID] = p.st.ModelUUID() 363 364 switch args.Type { 365 case instance.LXD: 366 // TODO(jam): DefaultMTU needs to be handled here 367 } 368 369 mConfig, err := p.m.ModelConfig() 370 if err != nil { 371 return result, err 372 } 373 if url, set := mConfig.ContainerImageMetadataURL(); set { 374 cfg[config.ContainerImageMetadataURLKey] = url 375 } 376 cfg[config.ContainerImageStreamKey] = mConfig.ContainerImageStream() 377 378 result.ManagerConfig = cfg 379 return result, nil 380 } 381 382 // ContainerConfig returns information from the model config that is 383 // needed for container cloud-init. 384 func (p *ProvisionerAPI) ContainerConfig() (params.ContainerConfig, error) { 385 result := params.ContainerConfig{} 386 config, err := p.m.ModelConfig() 387 if err != nil { 388 return result, err 389 } 390 391 result.UpdateBehavior = ¶ms.UpdateBehavior{ 392 config.EnableOSRefreshUpdate(), 393 config.EnableOSUpgrade(), 394 } 395 result.ProviderType = config.Type() 396 result.AuthorizedKeys = config.AuthorizedKeys() 397 result.SSLHostnameVerification = config.SSLHostnameVerification() 398 result.LegacyProxy = config.LegacyProxySettings() 399 result.JujuProxy = config.JujuProxySettings() 400 result.AptProxy = config.AptProxySettings() 401 result.AptMirror = config.AptMirror() 402 result.CloudInitUserData = config.CloudInitUserData() 403 result.ContainerInheritProperties = config.ContainerInheritProperies() 404 return result, nil 405 } 406 407 // ContainerConfig returns information from the model config that is 408 // needed for container cloud-init. 409 func (p *ProvisionerAPIV5) ContainerConfig() (params.ContainerConfigV5, error) { 410 var empty params.ContainerConfigV5 411 cfg, err := p.ProvisionerAPI.ContainerConfig() 412 if err != nil { 413 return empty, err 414 } 415 416 return params.ContainerConfigV5{ 417 ProviderType: cfg.ProviderType, 418 AuthorizedKeys: cfg.AuthorizedKeys, 419 SSLHostnameVerification: cfg.SSLHostnameVerification, 420 Proxy: cfg.LegacyProxy, 421 AptProxy: cfg.AptProxy, 422 AptMirror: cfg.AptMirror, 423 CloudInitUserData: cfg.CloudInitUserData, 424 ContainerInheritProperties: cfg.ContainerInheritProperties, 425 UpdateBehavior: cfg.UpdateBehavior, 426 }, nil 427 } 428 429 // MachinesWithTransientErrors returns status data for machines with provisioning 430 // errors which are transient. 431 func (p *ProvisionerAPI) MachinesWithTransientErrors() (params.StatusResults, error) { 432 var results params.StatusResults 433 canAccessFunc, err := p.getAuthFunc() 434 if err != nil { 435 return results, err 436 } 437 // TODO (wallyworld) - add state.State API for more efficient machines query 438 machines, err := p.st.AllMachines() 439 if err != nil { 440 return results, err 441 } 442 for _, machine := range machines { 443 if !canAccessFunc(machine.Tag()) { 444 continue 445 } 446 if _, provisionedErr := machine.InstanceId(); provisionedErr == nil { 447 // Machine may have been provisioned but machiner hasn't set the 448 // status to Started yet. 449 continue 450 } 451 var result params.StatusResult 452 statusInfo, err := machine.InstanceStatus() 453 if err != nil { 454 continue 455 } 456 result.Status = statusInfo.Status.String() 457 result.Info = statusInfo.Message 458 result.Data = statusInfo.Data 459 if statusInfo.Status != status.Error && statusInfo.Status != status.ProvisioningError { 460 continue 461 } 462 // Transient errors are marked as such in the status data. 463 if transient, ok := result.Data["transient"].(bool); !ok || !transient { 464 continue 465 } 466 result.Id = machine.Id() 467 result.Life = params.Life(machine.Life().String()) 468 results.Results = append(results.Results, result) 469 } 470 return results, nil 471 } 472 473 // Series returns the deployed series for each given machine entity. 474 func (p *ProvisionerAPI) Series(args params.Entities) (params.StringResults, error) { 475 result := params.StringResults{ 476 Results: make([]params.StringResult, len(args.Entities)), 477 } 478 canAccess, err := p.getAuthFunc() 479 if err != nil { 480 return result, err 481 } 482 for i, entity := range args.Entities { 483 tag, err := names.ParseMachineTag(entity.Tag) 484 if err != nil { 485 result.Results[i].Error = common.ServerError(common.ErrPerm) 486 continue 487 } 488 machine, err := p.getMachine(canAccess, tag) 489 if err == nil { 490 result.Results[i].Result = machine.Series() 491 } 492 result.Results[i].Error = common.ServerError(err) 493 } 494 return result, nil 495 } 496 497 // AvailabilityZone returns a provider-specific availability zone for each given machine entity 498 func (p *ProvisionerAPI) AvailabilityZone(args params.Entities) (params.StringResults, error) { 499 result := params.StringResults{ 500 Results: make([]params.StringResult, len(args.Entities)), 501 } 502 canAccess, err := p.getAuthFunc() 503 if err != nil { 504 return result, err 505 } 506 for i, entity := range args.Entities { 507 tag, err := names.ParseMachineTag(entity.Tag) 508 if err != nil { 509 result.Results[i].Error = common.ServerError(common.ErrPerm) 510 continue 511 } 512 machine, err := p.getMachine(canAccess, tag) 513 if err == nil { 514 hc, err := machine.HardwareCharacteristics() 515 if err == nil { 516 if hc.AvailabilityZone != nil { 517 result.Results[i].Result = *hc.AvailabilityZone 518 } else { 519 result.Results[i].Result = "" 520 } 521 } else { 522 result.Results[i].Error = common.ServerError(err) 523 } 524 } 525 } 526 return result, nil 527 } 528 529 // KeepInstance returns the keep-instance value for each given machine entity. 530 func (p *ProvisionerAPI) KeepInstance(args params.Entities) (params.BoolResults, error) { 531 result := params.BoolResults{ 532 533 Results: make([]params.BoolResult, len(args.Entities)), 534 } 535 canAccess, err := p.getAuthFunc() 536 if err != nil { 537 return result, err 538 } 539 for i, entity := range args.Entities { 540 tag, err := names.ParseMachineTag(entity.Tag) 541 if err != nil { 542 result.Results[i].Error = common.ServerError(common.ErrPerm) 543 continue 544 } 545 machine, err := p.getMachine(canAccess, tag) 546 if err == nil { 547 keep, err := machine.KeepInstance() 548 result.Results[i].Result = keep 549 result.Results[i].Error = common.ServerError(err) 550 } 551 result.Results[i].Error = common.ServerError(err) 552 } 553 return result, nil 554 } 555 556 // DistributionGroup returns, for each given machine entity, 557 // a slice of instance.Ids that belong to the same distribution 558 // group as that machine. This information may be used to 559 // distribute instances for high availability. 560 func (p *ProvisionerAPI) DistributionGroup(args params.Entities) (params.DistributionGroupResults, error) { 561 result := params.DistributionGroupResults{ 562 Results: make([]params.DistributionGroupResult, len(args.Entities)), 563 } 564 canAccess, err := p.getAuthFunc() 565 if err != nil { 566 return result, err 567 } 568 for i, entity := range args.Entities { 569 tag, err := names.ParseMachineTag(entity.Tag) 570 if err != nil { 571 result.Results[i].Error = common.ServerError(common.ErrPerm) 572 continue 573 } 574 machine, err := p.getMachine(canAccess, tag) 575 if err == nil { 576 // If the machine is a controller, return 577 // controller instances. Otherwise, return 578 // instances with services in common with the machine 579 // being provisioned. 580 if machine.IsManager() { 581 result.Results[i].Result, err = controllerInstances(p.st) 582 } else { 583 result.Results[i].Result, err = commonServiceInstances(p.st, machine) 584 } 585 } 586 result.Results[i].Error = common.ServerError(err) 587 } 588 return result, nil 589 } 590 591 // controllerInstances returns all environ manager instances. 592 func controllerInstances(st *state.State) ([]instance.Id, error) { 593 info, err := st.ControllerInfo() 594 if err != nil { 595 return nil, err 596 } 597 instances := make([]instance.Id, 0, len(info.MachineIds)) 598 for _, id := range info.MachineIds { 599 machine, err := st.Machine(id) 600 if err != nil { 601 return nil, err 602 } 603 instanceId, err := machine.InstanceId() 604 if err == nil { 605 instances = append(instances, instanceId) 606 } else if !errors.IsNotProvisioned(err) { 607 return nil, err 608 } 609 } 610 return instances, nil 611 } 612 613 // commonServiceInstances returns instances with 614 // services in common with the specified machine. 615 func commonServiceInstances(st *state.State, m *state.Machine) ([]instance.Id, error) { 616 units, err := m.Units() 617 if err != nil { 618 return nil, err 619 } 620 instanceIdSet := make(set.Strings) 621 for _, unit := range units { 622 if !unit.IsPrincipal() { 623 continue 624 } 625 instanceIds, err := state.ApplicationInstances(st, unit.ApplicationName()) 626 if err != nil { 627 return nil, err 628 } 629 for _, instanceId := range instanceIds { 630 instanceIdSet.Add(string(instanceId)) 631 } 632 } 633 instanceIds := make([]instance.Id, instanceIdSet.Size()) 634 // Sort values to simplify testing. 635 for i, instanceId := range instanceIdSet.SortedValues() { 636 instanceIds[i] = instance.Id(instanceId) 637 } 638 return instanceIds, nil 639 } 640 641 // DistributionGroupByMachineId isn't on the v4 API. 642 func (p *ProvisionerAPIV4) DistributionGroupByMachineId(_, _ struct{}) {} 643 644 // DistributionGroupByMachineId returns, for each given machine entity, 645 // a slice of machine.Ids that belong to the same distribution 646 // group as that machine. This information may be used to 647 // distribute instances for high availability. 648 func (p *ProvisionerAPI) DistributionGroupByMachineId(args params.Entities) (params.StringsResults, error) { 649 result := params.StringsResults{ 650 Results: make([]params.StringsResult, len(args.Entities)), 651 } 652 canAccess, err := p.getAuthFunc() 653 if err != nil { 654 return params.StringsResults{}, err 655 } 656 for i, entity := range args.Entities { 657 tag, err := names.ParseMachineTag(entity.Tag) 658 if err != nil { 659 result.Results[i].Error = common.ServerError(common.ErrPerm) 660 continue 661 } 662 machine, err := p.getMachine(canAccess, tag) 663 if err == nil { 664 // If the machine is a controller, return 665 // controller instances. Otherwise, return 666 // instances with services in common with the machine 667 // being provisioned. 668 if machine.IsManager() { 669 result.Results[i].Result, err = controllerMachineIds(p.st, machine) 670 } else { 671 result.Results[i].Result, err = commonApplicationMachineId(p.st, machine) 672 } 673 } 674 result.Results[i].Error = common.ServerError(err) 675 } 676 return result, nil 677 } 678 679 // controllerMachineIds returns a slice of all other environ manager machine.Ids. 680 func controllerMachineIds(st *state.State, m *state.Machine) ([]string, error) { 681 info, err := st.ControllerInfo() 682 if err != nil { 683 return nil, err 684 } 685 result := set.NewStrings(info.MachineIds...) 686 result.Remove(m.Id()) 687 return result.SortedValues(), nil 688 } 689 690 // commonApplicationMachineId returns a slice of machine.Ids with 691 // applications in common with the specified machine. 692 func commonApplicationMachineId(st *state.State, m *state.Machine) ([]string, error) { 693 applications := m.Principals() 694 var union set.Strings 695 for _, app := range applications { 696 machines, err := state.ApplicationMachines(st, app) 697 if err != nil { 698 return nil, err 699 } 700 union = union.Union(set.NewStrings(machines...)) 701 } 702 union.Remove(m.Id()) 703 return union.SortedValues(), nil 704 } 705 706 // Constraints returns the constraints for each given machine entity. 707 func (p *ProvisionerAPI) Constraints(args params.Entities) (params.ConstraintsResults, error) { 708 result := params.ConstraintsResults{ 709 Results: make([]params.ConstraintsResult, len(args.Entities)), 710 } 711 canAccess, err := p.getAuthFunc() 712 if err != nil { 713 return result, err 714 } 715 for i, entity := range args.Entities { 716 tag, err := names.ParseMachineTag(entity.Tag) 717 if err != nil { 718 result.Results[i].Error = common.ServerError(common.ErrPerm) 719 continue 720 } 721 machine, err := p.getMachine(canAccess, tag) 722 if err == nil { 723 var cons constraints.Value 724 cons, err = machine.Constraints() 725 if err == nil { 726 result.Results[i].Constraints = cons 727 } 728 } 729 result.Results[i].Error = common.ServerError(err) 730 } 731 return result, nil 732 } 733 734 // SetInstanceInfo sets the provider specific machine id, nonce, 735 // metadata and network info for each given machine. Once set, the 736 // instance id cannot be changed. 737 func (p *ProvisionerAPI) SetInstanceInfo(args params.InstancesInfo) (params.ErrorResults, error) { 738 result := params.ErrorResults{ 739 Results: make([]params.ErrorResult, len(args.Machines)), 740 } 741 canAccess, err := p.getAuthFunc() 742 if err != nil { 743 return result, err 744 } 745 setInstanceInfo := func(arg params.InstanceInfo) error { 746 tag, err := names.ParseMachineTag(arg.Tag) 747 if err != nil { 748 return common.ErrPerm 749 } 750 machine, err := p.getMachine(canAccess, tag) 751 if err != nil { 752 return err 753 } 754 volumes, err := storagecommon.VolumesToState(arg.Volumes) 755 if err != nil { 756 return err 757 } 758 volumeAttachments, err := storagecommon.VolumeAttachmentInfosToState(arg.VolumeAttachments) 759 if err != nil { 760 return err 761 } 762 763 devicesArgs, devicesAddrs := networkingcommon.NetworkConfigsToStateArgs(arg.NetworkConfig) 764 765 err = machine.SetInstanceInfo( 766 arg.InstanceId, arg.DisplayName, arg.Nonce, arg.Characteristics, 767 devicesArgs, devicesAddrs, 768 volumes, volumeAttachments, arg.CharmProfiles, 769 ) 770 if err != nil { 771 return errors.Annotatef(err, "cannot record provisioning info for %q", arg.InstanceId) 772 } 773 return nil 774 } 775 for i, arg := range args.Machines { 776 err := setInstanceInfo(arg) 777 result.Results[i].Error = common.ServerError(err) 778 } 779 return result, nil 780 } 781 782 // WatchMachineErrorRetry returns a NotifyWatcher that notifies when 783 // the provisioner should retry provisioning machines with transient errors. 784 func (p *ProvisionerAPI) WatchMachineErrorRetry() (params.NotifyWatchResult, error) { 785 result := params.NotifyWatchResult{} 786 if !p.authorizer.AuthController() { 787 return result, common.ErrPerm 788 } 789 watch := newWatchMachineErrorRetry() 790 // Consume any initial event and forward it to the result. 791 if _, ok := <-watch.Changes(); ok { 792 result.NotifyWatcherId = p.resources.Register(watch) 793 } else { 794 return result, watcher.EnsureErr(watch) 795 } 796 return result, nil 797 } 798 799 // WatchModelMachinesCharmProfiles returns a StringsWatcher that notifies when 800 // the provisioner should update the charm profiles used by a machine. 801 func (p *ProvisionerAPI) WatchModelMachinesCharmProfiles() (params.StringsWatchResult, error) { 802 result := params.StringsWatchResult{} 803 if !p.authorizer.AuthController() { 804 return result, common.ErrPerm 805 } 806 watch, err := p.st.WatchModelMachinesCharmProfiles() 807 if err != nil { 808 return result, common.ErrPerm 809 } 810 // Consume any initial event and forward it to the result. 811 if changes, ok := <-watch.Changes(); ok { 812 result.StringsWatcherId = p.resources.Register(watch) 813 result.Changes = changes 814 } else { 815 return result, watcher.EnsureErr(watch) 816 } 817 return result, nil 818 } 819 820 // ReleaseContainerAddresses finds addresses allocated to a container and marks 821 // them as Dead, to be released and removed. It accepts container tags as 822 // arguments. 823 func (p *ProvisionerAPI) ReleaseContainerAddresses(args params.Entities) (params.ErrorResults, error) { 824 result := params.ErrorResults{ 825 Results: make([]params.ErrorResult, len(args.Entities)), 826 } 827 828 canAccess, err := p.getAuthFunc() 829 if err != nil { 830 logger.Errorf("failed to get an authorisation function: %v", err) 831 return result, errors.Trace(err) 832 } 833 // Loop over the passed container tags. 834 for i, entity := range args.Entities { 835 tag, err := names.ParseMachineTag(entity.Tag) 836 if err != nil { 837 logger.Warningf("failed to parse machine tag %q: %v", entity.Tag, err) 838 result.Results[i].Error = common.ServerError(common.ErrPerm) 839 continue 840 } 841 842 // The auth function (canAccess) checks that the machine is a 843 // top level machine (we filter those out next) or that the 844 // machine has the host as a parent. 845 guest, err := p.getMachine(canAccess, tag) 846 if err != nil { 847 logger.Warningf("failed to get machine %q: %v", tag, err) 848 result.Results[i].Error = common.ServerError(err) 849 continue 850 } else if !guest.IsContainer() { 851 err = errors.Errorf("cannot mark addresses for removal for %q: not a container", tag) 852 result.Results[i].Error = common.ServerError(err) 853 continue 854 } 855 856 // TODO(dimitern): Release those via the provider once we have 857 // Environ.ReleaseContainerAddresses. See LP bug http://pad.lv/1585878 858 err = guest.RemoveAllAddresses() 859 if err != nil { 860 logger.Warningf("failed to remove container %q addresses: %v", tag, err) 861 result.Results[i].Error = common.ServerError(err) 862 continue 863 } 864 } 865 866 return result, nil 867 } 868 869 // PrepareContainerInterfaceInfo allocates an address and returns information to 870 // configure networking for a container. It accepts container tags as arguments. 871 func (p *ProvisionerAPI) PrepareContainerInterfaceInfo(args params.Entities) ( 872 params.MachineNetworkConfigResults, 873 error, 874 ) { 875 return p.prepareOrGetContainerInterfaceInfo(args, false) 876 } 877 878 // GetContainerInterfaceInfo returns information to configure networking for a 879 // container. It accepts container tags as arguments. 880 func (p *ProvisionerAPI) GetContainerInterfaceInfo(args params.Entities) ( 881 params.MachineNetworkConfigResults, 882 error, 883 ) { 884 return p.prepareOrGetContainerInterfaceInfo(args, true) 885 } 886 887 // Machine is an indirection for use in container provisioning. 888 //go:generate mockgen -package mocks -destination mocks/machine_mock.go github.com/juju/juju/apiserver/facades/agent/provisioner Machine 889 //go:generate mockgen -package mocks -destination mocks/containerizer_mock.go github.com/juju/juju/network/containerizer LinkLayerDevice,Unit,Application,Charm 890 type Machine interface { 891 containerizer.Container 892 InstanceId() (instance.Id, error) 893 IsManual() (bool, error) 894 MachineTag() names.MachineTag 895 Units() ([]containerizer.Unit, error) 896 } 897 898 // perContainerHandler is the interface we need to trigger processing on 899 // every container passed in as a list of things to process. 900 type perContainerHandler interface { 901 // ProcessOneContainer is called once we've assured ourselves that all of 902 // the access permissions are correct and the container is ready to be 903 // processed. 904 // env is the Environment you are working, idx is the index for this 905 // container (used for deciding where to store results), host is the 906 // machine that is hosting the container. 907 // Any errors that are returned from ProcessOneContainer will be turned 908 // into ServerError and handed to SetError 909 ProcessOneContainer(env environs.Environ, callContext context.ProviderCallContext, idx int, host, guest Machine) error 910 // SetError will be called whenever there is a problem with the a given 911 // request. Generally this just does result.Results[i].Error = error 912 // but the Result type is opaque so we can't do it ourselves. 913 SetError(resultIndex int, err error) 914 // ConfigType indicates the type of config the handler is getting for 915 // for error messaging. 916 ConfigType() string 917 } 918 919 func (p *ProvisionerAPI) processEachContainer(args params.Entities, handler perContainerHandler) error { 920 env, hostMachine, canAccess, err := p.prepareContainerAccessEnvironment() 921 if err != nil { 922 // Overall error 923 return errors.Trace(err) 924 } 925 _, err = hostMachine.InstanceId() 926 if errors.IsNotProvisioned(err) { 927 err = errors.NotProvisionedf("cannot prepare container %s config: host machine %q", handler.ConfigType(), hostMachine) 928 return err 929 } else if err != nil { 930 return errors.Trace(err) 931 } 932 933 for i, entity := range args.Entities { 934 machineTag, err := names.ParseMachineTag(entity.Tag) 935 if err != nil { 936 handler.SetError(i, err) 937 continue 938 } 939 // The auth function (canAccess) checks that the machine is a 940 // top level machine (we filter those out next) or that the 941 // machine has the host as a parent. 942 guest, err := p.getMachine(canAccess, machineTag) 943 if err != nil { 944 handler.SetError(i, err) 945 continue 946 } else if !guest.IsContainer() { 947 err = errors.Errorf("cannot prepare %s config for %q: not a container", handler.ConfigType(), machineTag) 948 handler.SetError(i, err) 949 continue 950 } 951 952 if err := handler.ProcessOneContainer( 953 env, p.providerCallContext, i, 954 &containerizer.MachineShim{Machine: hostMachine}, 955 &containerizer.MachineShim{Machine: guest}, 956 ); err != nil { 957 handler.SetError(i, err) 958 continue 959 } 960 } 961 return nil 962 } 963 964 type prepareOrGetContext struct { 965 result params.MachineNetworkConfigResults 966 maintain bool 967 } 968 969 // Implements perContainerHandler.SetError 970 func (ctx *prepareOrGetContext) SetError(idx int, err error) { 971 ctx.result.Results[idx].Error = common.ServerError(err) 972 } 973 974 // Implements perContainerHandler.ConfigType 975 func (ctx *prepareOrGetContext) ConfigType() string { 976 return "network" 977 } 978 979 // Implements perContainerHandler.ProcessOneContainer 980 func (ctx *prepareOrGetContext) ProcessOneContainer( 981 env environs.Environ, callContext context.ProviderCallContext, idx int, host, guest Machine, 982 ) error { 983 instanceId, err := guest.InstanceId() 984 if ctx.maintain { 985 if err == nil { 986 // Since we want to configure and create NICs on the 987 // container before it starts, it must also be not 988 // provisioned yet. 989 return errors.Errorf("container %q already provisioned as %q", guest.Id(), instanceId) 990 } 991 } 992 // The only error we allow is NotProvisioned 993 if err != nil && !errors.IsNotProvisioned(err) { 994 return errors.Trace(err) 995 } 996 997 bridgePolicy := containerizer.BridgePolicy{ 998 NetBondReconfigureDelay: env.Config().NetBondReconfigureDelay(), 999 ContainerNetworkingMethod: env.Config().ContainerNetworkingMethod(), 1000 } 1001 1002 // TODO(jam): 2017-01-31 PopulateContainerLinkLayerDevices should really 1003 // just be returning the ones we'd like to exist, and then we turn those 1004 // into things we'd like to tell the Host machine to create, and then *it* 1005 // reports back what actually exists when its done. 1006 if err := bridgePolicy.PopulateContainerLinkLayerDevices(host, guest); err != nil { 1007 return errors.Trace(err) 1008 } 1009 1010 containerDevices, err := guest.AllLinkLayerDevices() 1011 if err != nil { 1012 return errors.Trace(err) 1013 } 1014 1015 // We do not ask the provider to allocate addresses for manually provisioned 1016 // machines as we do not expect such machines to be recognised (LP:1796106). 1017 askProviderForAddress := false 1018 hostIsManual, err := host.IsManual() 1019 if err != nil { 1020 return errors.Trace(err) 1021 } 1022 if !hostIsManual { 1023 askProviderForAddress = environs.SupportsContainerAddresses(callContext, env) 1024 } 1025 1026 preparedInfo := make([]network.InterfaceInfo, len(containerDevices)) 1027 for j, device := range containerDevices { 1028 parentDevice, err := device.ParentDevice() 1029 if err != nil || parentDevice == nil { 1030 return errors.Errorf( 1031 "cannot get parent %q of container device %q: %v", 1032 device.ParentName(), device.Name(), err, 1033 ) 1034 } 1035 parentAddrs, err := parentDevice.Addresses() 1036 if err != nil { 1037 return errors.Trace(err) 1038 } 1039 1040 info := network.InterfaceInfo{ 1041 InterfaceName: device.Name(), 1042 MACAddress: device.MACAddress(), 1043 ConfigType: network.ConfigManual, 1044 InterfaceType: network.InterfaceType(device.Type()), 1045 NoAutoStart: !device.IsAutoStart(), 1046 Disabled: !device.IsUp(), 1047 MTU: int(device.MTU()), 1048 ParentInterfaceName: parentDevice.Name(), 1049 } 1050 1051 if len(parentAddrs) > 0 { 1052 logger.Debugf("host machine device %q has addresses %v", parentDevice.Name(), parentAddrs) 1053 firstAddress := parentAddrs[0] 1054 if askProviderForAddress { 1055 parentDeviceSubnet, err := firstAddress.Subnet() 1056 if err != nil { 1057 return errors.Annotatef(err, 1058 "cannot get subnet %q used by address %q of host machine device %q", 1059 firstAddress.SubnetCIDR(), firstAddress.Value(), parentDevice.Name(), 1060 ) 1061 } 1062 info.ConfigType = network.ConfigStatic 1063 info.CIDR = parentDeviceSubnet.CIDR() 1064 info.ProviderSubnetId = parentDeviceSubnet.ProviderId() 1065 info.VLANTag = parentDeviceSubnet.VLANTag() 1066 info.IsDefaultGateway = firstAddress.IsDefaultGateway() 1067 } else { 1068 info.ConfigType = network.ConfigDHCP 1069 info.CIDR = firstAddress.SubnetCIDR() 1070 info.ProviderSubnetId = "" 1071 info.VLANTag = 0 1072 } 1073 } else { 1074 logger.Infof("host machine device %q has no addresses %v", parentDevice.Name(), parentAddrs) 1075 // TODO(jam): 2017-02-15, have a concrete test for this case, as it 1076 // seems to be the common case in the wild. 1077 info.ConfigType = network.ConfigDHCP 1078 info.ProviderSubnetId = "" 1079 info.VLANTag = 0 1080 } 1081 1082 logger.Tracef("prepared info for container interface %q: %+v", info.InterfaceName, info) 1083 preparedInfo[j] = info 1084 } 1085 1086 hostInstanceId, err := host.InstanceId() 1087 if err != nil { 1088 // this should have already been checked in the processEachContainer helper 1089 return errors.Trace(err) 1090 } 1091 1092 allocatedInfo := preparedInfo 1093 if askProviderForAddress { 1094 // supportContainerAddresses already checks that we can cast to an environ.Networking 1095 networking := env.(environs.Networking) 1096 allocatedInfo, err = networking.AllocateContainerAddresses( 1097 callContext, hostInstanceId, guest.MachineTag(), preparedInfo) 1098 if err != nil { 1099 return errors.Trace(err) 1100 } 1101 logger.Debugf("got allocated info from provider: %+v", allocatedInfo) 1102 } else { 1103 logger.Debugf("using dhcp allocated addresses") 1104 } 1105 1106 allocatedConfig := networkingcommon.NetworkConfigFromInterfaceInfo(allocatedInfo) 1107 logger.Debugf("allocated network config: %+v", allocatedConfig) 1108 ctx.result.Results[idx].Config = allocatedConfig 1109 return nil 1110 } 1111 1112 func (p *ProvisionerAPI) prepareOrGetContainerInterfaceInfo(args params.Entities, maintain bool) (params.MachineNetworkConfigResults, error) { 1113 ctx := &prepareOrGetContext{ 1114 result: params.MachineNetworkConfigResults{ 1115 Results: make([]params.MachineNetworkConfigResult, len(args.Entities)), 1116 }, 1117 maintain: maintain, 1118 } 1119 1120 if err := p.processEachContainer(args, ctx); err != nil { 1121 return ctx.result, errors.Trace(err) 1122 } 1123 return ctx.result, nil 1124 } 1125 1126 // prepareContainerAccessEnvironment retrieves the environment, host machine, and access 1127 // for working with containers. 1128 func (p *ProvisionerAPI) prepareContainerAccessEnvironment() (environs.Environ, *state.Machine, common.AuthFunc, error) { 1129 env, err := environs.GetEnviron(p.configGetter, environs.New) 1130 if err != nil { 1131 return nil, nil, nil, errors.Trace(err) 1132 } 1133 // TODO(jam): 2017-02-01 NetworkingEnvironFromModelConfig used to do this, but it doesn't feel good 1134 if env.Config().Type() == "dummy" { 1135 return nil, nil, nil, errors.NotSupportedf("dummy provider network config") 1136 } 1137 1138 canAccess, err := p.getAuthFunc() 1139 if err != nil { 1140 return nil, nil, nil, errors.Annotate(err, "cannot authenticate request") 1141 } 1142 hostAuthTag := p.authorizer.GetAuthTag() 1143 if hostAuthTag == nil { 1144 return nil, nil, nil, errors.Errorf("authenticated entity tag is nil") 1145 } 1146 hostTag, err := names.ParseMachineTag(hostAuthTag.String()) 1147 if err != nil { 1148 return nil, nil, nil, errors.Trace(err) 1149 } 1150 host, err := p.getMachine(canAccess, hostTag) 1151 if err != nil { 1152 return nil, nil, nil, errors.Trace(err) 1153 } 1154 return env, host, canAccess, nil 1155 } 1156 1157 type hostChangesContext struct { 1158 result params.HostNetworkChangeResults 1159 } 1160 1161 // Implements perContainerHandler.ProcessOneContainer 1162 func (ctx *hostChangesContext) ProcessOneContainer( 1163 env environs.Environ, callContext context.ProviderCallContext, idx int, host, guest Machine, 1164 ) error { 1165 bridgePolicy := containerizer.BridgePolicy{ 1166 NetBondReconfigureDelay: env.Config().NetBondReconfigureDelay(), 1167 ContainerNetworkingMethod: env.Config().ContainerNetworkingMethod(), 1168 } 1169 bridges, reconfigureDelay, err := bridgePolicy.FindMissingBridgesForContainer(host, guest) 1170 if err != nil { 1171 return err 1172 } 1173 1174 ctx.result.Results[idx].ReconfigureDelay = reconfigureDelay 1175 for _, bridgeInfo := range bridges { 1176 ctx.result.Results[idx].NewBridges = append( 1177 ctx.result.Results[idx].NewBridges, 1178 params.DeviceBridgeInfo{ 1179 HostDeviceName: bridgeInfo.DeviceName, 1180 BridgeName: bridgeInfo.BridgeName, 1181 MACAddress: bridgeInfo.MACAddress, 1182 }) 1183 } 1184 return nil 1185 } 1186 1187 // Implements perContainerHandler.SetError 1188 func (ctx *hostChangesContext) SetError(idx int, err error) { 1189 ctx.result.Results[idx].Error = common.ServerError(err) 1190 } 1191 1192 // Implements perContainerHandler.ConfigType 1193 func (ctx *hostChangesContext) ConfigType() string { 1194 return "network" 1195 } 1196 1197 // HostChangesForContainers returns the set of changes that need to be done 1198 // to the host machine to prepare it for the containers to be created. 1199 // Pass in a list of the containers that you want the changes for. 1200 func (p *ProvisionerAPI) HostChangesForContainers(args params.Entities) (params.HostNetworkChangeResults, error) { 1201 ctx := &hostChangesContext{ 1202 result: params.HostNetworkChangeResults{ 1203 Results: make([]params.HostNetworkChange, len(args.Entities)), 1204 }, 1205 } 1206 if err := p.processEachContainer(args, ctx); err != nil { 1207 return ctx.result, errors.Trace(err) 1208 } 1209 return ctx.result, nil 1210 } 1211 1212 type containerProfileContext struct { 1213 result params.ContainerProfileResults 1214 modelName string 1215 } 1216 1217 // Implements perContainerHandler.ProcessOneContainer 1218 func (ctx *containerProfileContext) ProcessOneContainer( 1219 _ environs.Environ, _ context.ProviderCallContext, idx int, _, guest Machine, 1220 ) error { 1221 units, err := guest.Units() 1222 if err != nil { 1223 ctx.result.Results[idx].Error = common.ServerError(err) 1224 return errors.Trace(err) 1225 } 1226 var resPro []*params.ContainerLXDProfile 1227 for _, unit := range units { 1228 app, err := unit.Application() 1229 if err != nil { 1230 ctx.SetError(idx, err) 1231 return errors.Trace(err) 1232 } 1233 ch, _, err := app.Charm() 1234 if err != nil { 1235 ctx.SetError(idx, err) 1236 return errors.Trace(err) 1237 } 1238 profile := ch.LXDProfile() 1239 if profile == nil || (profile != nil && profile.Empty()) { 1240 logger.Tracef("no profile to return for %q", unit.Name()) 1241 continue 1242 } 1243 resPro = append(resPro, ¶ms.ContainerLXDProfile{ 1244 Profile: params.CharmLXDProfile{ 1245 Config: profile.Config, 1246 Description: profile.Description, 1247 Devices: profile.Devices, 1248 }, 1249 Name: lxdprofile.Name(ctx.modelName, app.Name(), ch.Revision()), 1250 }) 1251 } 1252 1253 ctx.result.Results[idx].LXDProfiles = resPro 1254 return nil 1255 } 1256 1257 // Implements perContainerHandler.SetError 1258 func (ctx *containerProfileContext) SetError(idx int, err error) { 1259 ctx.result.Results[idx].Error = common.ServerError(err) 1260 } 1261 1262 // Implements perContainerHandler.ConfigType 1263 func (ctx *containerProfileContext) ConfigType() string { 1264 return "LXD profile" 1265 } 1266 1267 // GetContainerProfileInfo returns information to configure a lxd profile(s) for a 1268 // container based on the charms deployed to the container. It accepts container 1269 // tags as arguments. Unlike machineLXDProfileNames which has the environ 1270 // write the lxd profiles and returns the names of profiles already written. 1271 func (p *ProvisionerAPI) GetContainerProfileInfo(args params.Entities) (params.ContainerProfileResults, error) { 1272 ctx := &containerProfileContext{ 1273 result: params.ContainerProfileResults{ 1274 Results: make([]params.ContainerProfileResult, len(args.Entities)), 1275 }, 1276 modelName: p.m.Name(), 1277 } 1278 if err := p.processEachContainer(args, ctx); err != nil { 1279 return ctx.result, errors.Trace(err) 1280 } 1281 return ctx.result, nil 1282 } 1283 1284 // InstanceStatus returns the instance status for each given entity. 1285 // Only machine tags are accepted. 1286 func (p *ProvisionerAPI) InstanceStatus(args params.Entities) (params.StatusResults, error) { 1287 result := params.StatusResults{ 1288 Results: make([]params.StatusResult, len(args.Entities)), 1289 } 1290 canAccess, err := p.getAuthFunc() 1291 if err != nil { 1292 logger.Errorf("failed to get an authorisation function: %v", err) 1293 return result, errors.Trace(err) 1294 } 1295 for i, arg := range args.Entities { 1296 mTag, err := names.ParseMachineTag(arg.Tag) 1297 if err != nil { 1298 logger.Warningf("InstanceStatus called with %q which is not a valid machine tag: %v", arg.Tag, err) 1299 result.Results[i].Error = common.ServerError(common.ErrPerm) 1300 continue 1301 } 1302 machine, err := p.getMachine(canAccess, mTag) 1303 if err == nil { 1304 var statusInfo status.StatusInfo 1305 statusInfo, err = machine.InstanceStatus() 1306 result.Results[i].Status = statusInfo.Status.String() 1307 result.Results[i].Info = statusInfo.Message 1308 result.Results[i].Data = statusInfo.Data 1309 result.Results[i].Since = statusInfo.Since 1310 } 1311 result.Results[i].Error = common.ServerError(err) 1312 } 1313 return result, nil 1314 } 1315 1316 func (p *ProvisionerAPI) setOneInstanceStatus(canAccess common.AuthFunc, arg params.EntityStatusArgs) error { 1317 logger.Debugf("SetInstanceStatus called with: %#v", arg) 1318 mTag, err := names.ParseMachineTag(arg.Tag) 1319 if err != nil { 1320 logger.Warningf("SetInstanceStatus called with %q which is not a valid machine tag: %v", arg.Tag, err) 1321 return common.ErrPerm 1322 } 1323 machine, err := p.getMachine(canAccess, mTag) 1324 if err != nil { 1325 logger.Debugf("SetInstanceStatus unable to get machine %q", mTag) 1326 return err 1327 } 1328 // TODO(perrito666) 2016-05-02 lp:1558657 1329 now := time.Now() 1330 s := status.StatusInfo{ 1331 Status: status.Status(arg.Status), 1332 Message: arg.Info, 1333 Data: arg.Data, 1334 Since: &now, 1335 } 1336 1337 // TODO(jam): 2017-01-29 These two status should be set in a single 1338 // transaction, not in two separate transactions. Otherwise you can see 1339 // one toggle, but not the other. 1340 if err = machine.SetInstanceStatus(s); err != nil { 1341 logger.Debugf("failed to SetInstanceStatus for %q: %v", mTag, err) 1342 return err 1343 } 1344 if status.Status(arg.Status) == status.ProvisioningError || 1345 status.Status(arg.Status) == status.Error { 1346 s.Status = status.Error 1347 logger.Debugf("SetInstanceStatus triggering SetStatus for %#v", s) 1348 if err = machine.SetStatus(s); err != nil { 1349 return err 1350 } 1351 } 1352 return nil 1353 } 1354 1355 // SetInstanceStatus updates the instance status for each given 1356 // entity. Only machine tags are accepted. 1357 func (p *ProvisionerAPI) SetInstanceStatus(args params.SetStatus) (params.ErrorResults, error) { 1358 result := params.ErrorResults{ 1359 Results: make([]params.ErrorResult, len(args.Entities)), 1360 } 1361 canAccess, err := p.getAuthFunc() 1362 if err != nil { 1363 logger.Errorf("failed to get an authorisation function: %v", err) 1364 return result, errors.Trace(err) 1365 } 1366 for i, arg := range args.Entities { 1367 err = p.setOneInstanceStatus(canAccess, arg) 1368 result.Results[i].Error = common.ServerError(err) 1369 } 1370 return result, nil 1371 } 1372 1373 // MarkMachinesForRemoval indicates that the specified machines are 1374 // ready to have any provider-level resources cleaned up and then be 1375 // removed. 1376 func (p *ProvisionerAPI) MarkMachinesForRemoval(machines params.Entities) (params.ErrorResults, error) { 1377 results := make([]params.ErrorResult, len(machines.Entities)) 1378 canAccess, err := p.getAuthFunc() 1379 if err != nil { 1380 logger.Errorf("failed to get an authorisation function: %v", err) 1381 return params.ErrorResults{}, errors.Trace(err) 1382 } 1383 for i, machine := range machines.Entities { 1384 results[i].Error = common.ServerError(p.markOneMachineForRemoval(machine.Tag, canAccess)) 1385 } 1386 return params.ErrorResults{Results: results}, nil 1387 } 1388 1389 func (p *ProvisionerAPI) markOneMachineForRemoval(machineTag string, canAccess common.AuthFunc) error { 1390 mTag, err := names.ParseMachineTag(machineTag) 1391 if err != nil { 1392 return errors.Trace(err) 1393 } 1394 machine, err := p.getMachine(canAccess, mTag) 1395 if err != nil { 1396 return errors.Trace(err) 1397 } 1398 return machine.MarkForRemoval() 1399 } 1400 1401 func (p *ProvisionerAPI) SetHostMachineNetworkConfig(args params.SetMachineNetworkConfig) error { 1402 return p.SetObservedNetworkConfig(args) 1403 } 1404 1405 // CACert returns the certificate used to validate the state connection. 1406 func (a *ProvisionerAPI) CACert() (params.BytesResult, error) { 1407 cfg, err := a.st.ControllerConfig() 1408 if err != nil { 1409 return params.BytesResult{}, errors.Trace(err) 1410 } 1411 caCert, _ := cfg.CACert() 1412 return params.BytesResult{Result: []byte(caCert)}, nil 1413 } 1414 1415 // CharmProfileChangeInfo retrieves the info necessary to change a charm 1416 // profile used by a machine. 1417 func (p *ProvisionerAPI) CharmProfileChangeInfo(machines params.Entities) (params.ProfileChangeResults, error) { 1418 results := make([]params.ProfileChangeResult, len(machines.Entities)) 1419 canAccess, err := p.getAuthFunc() 1420 if err != nil { 1421 logger.Errorf("failed to get an authorisation function: %v", err) 1422 return params.ProfileChangeResults{}, errors.Trace(err) 1423 } 1424 for i, machine := range machines.Entities { 1425 mTag, err := names.ParseMachineTag(machine.Tag) 1426 if err != nil { 1427 results[i].Error = common.ServerError(err) 1428 continue 1429 } 1430 machine, err := p.getMachine(canAccess, mTag) 1431 if err != nil { 1432 results[i].Error = common.ServerError(err) 1433 continue 1434 } 1435 result, err := machineChangeProfileChangeInfo(&profileMachineShim{Machine: machine}, &profileBackendShim{p.st}) 1436 if err != nil { 1437 results[i].Error = common.ServerError(err) 1438 continue 1439 } 1440 results[i] = result 1441 } 1442 return params.ProfileChangeResults{Results: results}, nil 1443 } 1444 1445 func machineChangeProfileChangeInfo(machine ProfileMachine, st ProfileBackend) (params.ProfileChangeResult, error) { 1446 nothing := params.ProfileChangeResult{} 1447 1448 appName, err := machine.UpgradeCharmProfileApplication() 1449 if err != nil { 1450 return nothing, errors.Trace(err) 1451 } 1452 if appName == "" { 1453 return nothing, errors.Trace(errors.New("no appname for profile charm upgrade")) 1454 } 1455 profileNames, err := machine.CharmProfiles() 1456 if err != nil { 1457 return nothing, errors.Trace(err) 1458 } 1459 // If oldProfileName ends up as an empty string, 1460 // the charm has added an lxd-profile where it didn't have one before. 1461 oldProfileName, err := lxdprofile.MatchProfileNameByAppName(profileNames, appName) 1462 if err != nil { 1463 return nothing, errors.Trace(err) 1464 } 1465 1466 url, err := machine.UpgradeCharmProfileCharmURL() 1467 if err != nil { 1468 return nothing, errors.Trace(err) 1469 } 1470 switch { 1471 case url == "" && oldProfileName != "": 1472 // Remove the old profile from the machine, 1473 // the unit is being removed. 1474 return params.ProfileChangeResult{ 1475 OldProfileName: oldProfileName, 1476 }, nil 1477 case url == "": 1478 return nothing, errors.Trace(errors.New("no url for profile charm upgrade, no profile to remove")) 1479 } 1480 1481 chURL, err := charm.ParseURL(url) 1482 if err != nil { 1483 return nothing, errors.Trace(err) 1484 } 1485 1486 ch, err := st.Charm(chURL) 1487 if err != nil { 1488 return nothing, errors.Trace(err) 1489 } 1490 var profile *params.CharmLXDProfile 1491 chProfile := ch.LXDProfile() 1492 if chProfile != nil && !chProfile.Empty() { 1493 p := params.CharmLXDProfile(*chProfile) 1494 profile = &p 1495 } 1496 chRev := ch.Revision() 1497 1498 var newProfileName string 1499 switch { 1500 case oldProfileName == "": 1501 newProfileName = lxdprofile.Name(machine.ModelName(), appName, chRev) 1502 logger.Tracef("new profile %s is being added to machine-%s", machine.Id(), newProfileName) 1503 case profile == nil: 1504 logger.Tracef("profile %s is being removed from machine-%s, no replacement", machine.Id(), oldProfileName) 1505 newProfileName = "" 1506 default: 1507 newProfileName, err = lxdprofile.ProfileReplaceRevision(oldProfileName, chRev) 1508 if err != nil { 1509 return nothing, errors.Trace(err) 1510 } 1511 logger.Tracef("profile %s is being replaced with %s on machine-%s", oldProfileName, newProfileName, machine.Id()) 1512 } 1513 1514 meta := ch.Meta() 1515 1516 return params.ProfileChangeResult{ 1517 OldProfileName: oldProfileName, 1518 NewProfileName: newProfileName, 1519 Profile: profile, 1520 Subordinate: meta.Subordinate, 1521 }, nil 1522 } 1523 1524 // SetCharmProfiles records the given slice of charm profile names. 1525 func (p *ProvisionerAPI) SetCharmProfiles(args params.SetProfileArgs) (params.ErrorResults, error) { 1526 results := make([]params.ErrorResult, len(args.Args)) 1527 canAccess, err := p.getAuthFunc() 1528 if err != nil { 1529 logger.Errorf("failed to get an authorisation function: %v", err) 1530 return params.ErrorResults{}, errors.Trace(err) 1531 } 1532 for i, a := range args.Args { 1533 results[i].Error = common.ServerError(p.setOneMachineCharmProfiles(a.Entity.Tag, a.Profiles, canAccess)) 1534 } 1535 return params.ErrorResults{Results: results}, nil 1536 } 1537 1538 func (p *ProvisionerAPI) setOneMachineCharmProfiles(machineTag string, profiles []string, canAccess common.AuthFunc) error { 1539 mTag, err := names.ParseMachineTag(machineTag) 1540 if err != nil { 1541 return errors.Trace(err) 1542 } 1543 machine, err := p.getMachine(canAccess, mTag) 1544 if err != nil { 1545 return errors.Trace(err) 1546 } 1547 return machine.SetCharmProfiles(profiles) 1548 } 1549 1550 // SetUpgradeCharmProfileComplete recorded that the result of updating 1551 // the machine's charm profile(s) 1552 func (p *ProvisionerAPI) SetUpgradeCharmProfileComplete(args params.SetProfileUpgradeCompleteArgs) (params.ErrorResults, error) { 1553 results := make([]params.ErrorResult, len(args.Args)) 1554 canAccess, err := p.getAuthFunc() 1555 if err != nil { 1556 logger.Errorf("failed to get an authorisation function: %v", err) 1557 return params.ErrorResults{}, errors.Trace(err) 1558 } 1559 for i, a := range args.Args { 1560 results[i].Error = common.ServerError(p.oneUpgradeCharmProfileComplete(a.Entity.Tag, a.Message, canAccess)) 1561 } 1562 return params.ErrorResults{Results: results}, nil 1563 } 1564 1565 func (p *ProvisionerAPI) oneUpgradeCharmProfileComplete(machineTag string, msg string, canAccess common.AuthFunc) error { 1566 mTag, err := names.ParseMachineTag(machineTag) 1567 if err != nil { 1568 return errors.Trace(err) 1569 } 1570 machine, err := p.getMachine(canAccess, mTag) 1571 if err != nil { 1572 return errors.Trace(err) 1573 } 1574 return machine.SetUpgradeCharmProfileComplete(msg) 1575 } 1576 1577 // RemoveUpgradeCharmProfileData completely removes the instance charm profile 1578 // data for a machine, even if the machine is dead. 1579 func (p *ProvisionerAPI) RemoveUpgradeCharmProfileData(args params.Entities) (params.ErrorResults, error) { 1580 results := make([]params.ErrorResult, len(args.Entities)) 1581 canAccess, err := p.getAuthFunc() 1582 if err != nil { 1583 logger.Errorf("failed to get an authorisation function: %v", err) 1584 return params.ErrorResults{}, errors.Trace(err) 1585 } 1586 for i, a := range args.Entities { 1587 results[i].Error = common.ServerError(p.oneRemoveUpgradeCharmProfileData(a.Tag, canAccess)) 1588 } 1589 return params.ErrorResults{Results: results}, nil 1590 } 1591 1592 func (p *ProvisionerAPI) oneRemoveUpgradeCharmProfileData(machineTag string, canAccess common.AuthFunc) error { 1593 mTag, err := names.ParseMachineTag(machineTag) 1594 if err != nil { 1595 return errors.Trace(err) 1596 } 1597 machine, err := p.getMachine(canAccess, mTag) 1598 if err != nil { 1599 return errors.Trace(err) 1600 } 1601 return machine.RemoveUpgradeCharmProfileData() 1602 }