github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/apiserver/client/client.go (about) 1 // Copyright 2013, 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package client 5 6 import ( 7 "fmt" 8 "io" 9 "os" 10 "strings" 11 12 "github.com/juju/errors" 13 "github.com/juju/loggo" 14 "github.com/juju/names" 15 "github.com/juju/utils" 16 "github.com/juju/utils/featureflag" 17 "gopkg.in/juju/charm.v4" 18 19 "github.com/juju/juju/api" 20 "github.com/juju/juju/apiserver/common" 21 "github.com/juju/juju/apiserver/highavailability" 22 "github.com/juju/juju/apiserver/params" 23 "github.com/juju/juju/environs/config" 24 "github.com/juju/juju/environs/manual" 25 "github.com/juju/juju/instance" 26 jjj "github.com/juju/juju/juju" 27 "github.com/juju/juju/network" 28 "github.com/juju/juju/state" 29 "github.com/juju/juju/state/multiwatcher" 30 statestorage "github.com/juju/juju/state/storage" 31 "github.com/juju/juju/storage" 32 "github.com/juju/juju/version" 33 ) 34 35 func init() { 36 common.RegisterStandardFacade("Client", 0, NewClient) 37 } 38 39 var ( 40 logger = loggo.GetLogger("juju.apiserver.client") 41 42 newStateStorage = statestorage.NewStorage 43 ) 44 45 type API struct { 46 state *state.State 47 auth common.Authorizer 48 resources *common.Resources 49 client *Client 50 // statusSetter provides common methods for updating an entity's provisioning status. 51 statusSetter *common.StatusSetter 52 toolsFinder *common.ToolsFinder 53 } 54 55 // Client serves client-specific API methods. 56 type Client struct { 57 api *API 58 check *common.BlockChecker 59 } 60 61 // NewClient creates a new instance of the Client Facade. 62 func NewClient(st *state.State, resources *common.Resources, authorizer common.Authorizer) (*Client, error) { 63 if !authorizer.AuthClient() { 64 return nil, common.ErrPerm 65 } 66 env, err := st.Environment() 67 if err != nil { 68 return nil, err 69 } 70 urlGetter := common.NewToolsURLGetter(env.UUID(), st) 71 return &Client{ 72 api: &API{ 73 state: st, 74 auth: authorizer, 75 resources: resources, 76 statusSetter: common.NewStatusSetter(st, common.AuthAlways()), 77 toolsFinder: common.NewToolsFinder(st, st, urlGetter), 78 }, 79 check: common.NewBlockChecker(st)}, nil 80 } 81 82 func (c *Client) WatchAll() (params.AllWatcherId, error) { 83 w := c.api.state.Watch() 84 return params.AllWatcherId{ 85 AllWatcherId: c.api.resources.Register(w), 86 }, nil 87 } 88 89 // ServiceSet implements the server side of Client.ServiceSet. Values set to an 90 // empty string will be unset. 91 // 92 // (Deprecated) Use NewServiceSetForClientAPI instead, to preserve values set to 93 // an empty string, and use ServiceUnset to unset values. 94 func (c *Client) ServiceSet(p params.ServiceSet) error { 95 if err := c.check.ChangeAllowed(); err != nil { 96 return errors.Trace(err) 97 } 98 svc, err := c.api.state.Service(p.ServiceName) 99 if err != nil { 100 return err 101 } 102 return serviceSetSettingsStrings(svc, p.Options) 103 } 104 105 // NewServiceSetForClientAPI implements the server side of 106 // Client.NewServiceSetForClientAPI. This is exactly like ServiceSet except that 107 // it does not unset values that are set to an empty string. ServiceUnset 108 // should be used for that. 109 // 110 // TODO(Nate): rename this to ServiceSet (and remove the deprecated ServiceSet) 111 // when the GUI handles the new behavior. 112 // TODO(mattyw, all): This api call should be move to the new service facade. The client api version will then need bumping. 113 func (c *Client) NewServiceSetForClientAPI(p params.ServiceSet) error { 114 svc, err := c.api.state.Service(p.ServiceName) 115 if err != nil { 116 return err 117 } 118 return newServiceSetSettingsStringsForClientAPI(svc, p.Options) 119 } 120 121 // ServiceUnset implements the server side of Client.ServiceUnset. 122 // TODO(mattyw, all): This api call should be move to the new service facade. The client api version will then need bumping. 123 func (c *Client) ServiceUnset(p params.ServiceUnset) error { 124 if err := c.check.ChangeAllowed(); err != nil { 125 return errors.Trace(err) 126 } 127 svc, err := c.api.state.Service(p.ServiceName) 128 if err != nil { 129 return err 130 } 131 settings := make(charm.Settings) 132 for _, option := range p.Options { 133 settings[option] = nil 134 } 135 return svc.UpdateConfigSettings(settings) 136 } 137 138 // ServiceSetYAML implements the server side of Client.ServerSetYAML. 139 // TODO(mattyw, all): This api call should be move to the new service facade. The client api version will then need bumping. 140 func (c *Client) ServiceSetYAML(p params.ServiceSetYAML) error { 141 if err := c.check.ChangeAllowed(); err != nil { 142 return errors.Trace(err) 143 } 144 svc, err := c.api.state.Service(p.ServiceName) 145 if err != nil { 146 return err 147 } 148 return serviceSetSettingsYAML(svc, p.Config) 149 } 150 151 // ServiceCharmRelations implements the server side of Client.ServiceCharmRelations. 152 // TODO(mattyw, all): This api call should be move to the new service facade. The client api version will then need bumping. 153 func (c *Client) ServiceCharmRelations(p params.ServiceCharmRelations) (params.ServiceCharmRelationsResults, error) { 154 var results params.ServiceCharmRelationsResults 155 service, err := c.api.state.Service(p.ServiceName) 156 if err != nil { 157 return results, err 158 } 159 endpoints, err := service.Endpoints() 160 if err != nil { 161 return results, err 162 } 163 results.CharmRelations = make([]string, len(endpoints)) 164 for i, endpoint := range endpoints { 165 results.CharmRelations[i] = endpoint.Relation.Name 166 } 167 return results, nil 168 } 169 170 // Resolved implements the server side of Client.Resolved. 171 func (c *Client) Resolved(p params.Resolved) error { 172 if err := c.check.ChangeAllowed(); err != nil { 173 return errors.Trace(err) 174 } 175 unit, err := c.api.state.Unit(p.UnitName) 176 if err != nil { 177 return err 178 } 179 return unit.Resolve(p.Retry) 180 } 181 182 // PublicAddress implements the server side of Client.PublicAddress. 183 func (c *Client) PublicAddress(p params.PublicAddress) (results params.PublicAddressResults, err error) { 184 switch { 185 case names.IsValidMachine(p.Target): 186 machine, err := c.api.state.Machine(p.Target) 187 if err != nil { 188 return results, err 189 } 190 addr := network.SelectPublicAddress(machine.Addresses()) 191 if addr == "" { 192 return results, fmt.Errorf("machine %q has no public address", machine) 193 } 194 return params.PublicAddressResults{PublicAddress: addr}, nil 195 196 case names.IsValidUnit(p.Target): 197 unit, err := c.api.state.Unit(p.Target) 198 if err != nil { 199 return results, err 200 } 201 addr, ok := unit.PublicAddress() 202 if !ok { 203 return results, fmt.Errorf("unit %q has no public address", unit) 204 } 205 return params.PublicAddressResults{PublicAddress: addr}, nil 206 } 207 return results, fmt.Errorf("unknown unit or machine %q", p.Target) 208 } 209 210 // PrivateAddress implements the server side of Client.PrivateAddress. 211 func (c *Client) PrivateAddress(p params.PrivateAddress) (results params.PrivateAddressResults, err error) { 212 switch { 213 case names.IsValidMachine(p.Target): 214 machine, err := c.api.state.Machine(p.Target) 215 if err != nil { 216 return results, err 217 } 218 addr := network.SelectInternalAddress(machine.Addresses(), false) 219 if addr == "" { 220 return results, fmt.Errorf("machine %q has no internal address", machine) 221 } 222 return params.PrivateAddressResults{PrivateAddress: addr}, nil 223 224 case names.IsValidUnit(p.Target): 225 unit, err := c.api.state.Unit(p.Target) 226 if err != nil { 227 return results, err 228 } 229 addr, ok := unit.PrivateAddress() 230 if !ok { 231 return results, fmt.Errorf("unit %q has no internal address", unit) 232 } 233 return params.PrivateAddressResults{PrivateAddress: addr}, nil 234 } 235 return results, fmt.Errorf("unknown unit or machine %q", p.Target) 236 } 237 238 // ServiceExpose changes the juju-managed firewall to expose any ports that 239 // were also explicitly marked by units as open. 240 // TODO(mattyw, all): This api call should be move to the new service facade. The client api version will then need bumping. 241 func (c *Client) ServiceExpose(args params.ServiceExpose) error { 242 if err := c.check.ChangeAllowed(); err != nil { 243 return errors.Trace(err) 244 } 245 svc, err := c.api.state.Service(args.ServiceName) 246 if err != nil { 247 return err 248 } 249 return svc.SetExposed() 250 } 251 252 // ServiceUnexpose changes the juju-managed firewall to unexpose any ports that 253 // were also explicitly marked by units as open. 254 // TODO(mattyw, all): This api call should be move to the new service facade. The client api version will then need bumping. 255 func (c *Client) ServiceUnexpose(args params.ServiceUnexpose) error { 256 if err := c.check.ChangeAllowed(); err != nil { 257 return errors.Trace(err) 258 } 259 svc, err := c.api.state.Service(args.ServiceName) 260 if err != nil { 261 return err 262 } 263 return svc.ClearExposed() 264 } 265 266 var CharmStore charm.Repository = charm.Store 267 268 func networkTagsToNames(tags []string) ([]string, error) { 269 netNames := make([]string, len(tags)) 270 for i, tag := range tags { 271 t, err := names.ParseNetworkTag(tag) 272 if err != nil { 273 return nil, err 274 } 275 netNames[i] = t.Id() 276 } 277 return netNames, nil 278 } 279 280 // ServiceDeploy fetches the charm from the charm store and deploys it. 281 // AddCharm or AddLocalCharm should be called to add the charm 282 // before calling ServiceDeploy, although for backward compatibility 283 // this is not necessary until 1.16 support is removed. 284 func (c *Client) ServiceDeploy(args params.ServiceDeploy) error { 285 if err := c.check.ChangeAllowed(); err != nil { 286 return errors.Trace(err) 287 } 288 curl, err := charm.ParseURL(args.CharmUrl) 289 if err != nil { 290 return err 291 } 292 if curl.Revision < 0 { 293 return fmt.Errorf("charm url must include revision") 294 } 295 296 if args.ToMachineSpec != "" && names.IsValidMachine(args.ToMachineSpec) { 297 _, err = c.api.state.Machine(args.ToMachineSpec) 298 if err != nil { 299 return errors.Annotatef(err, `cannot deploy "%v" to machine %v`, args.ServiceName, args.ToMachineSpec) 300 } 301 } 302 303 // Try to find the charm URL in state first. 304 ch, err := c.api.state.Charm(curl) 305 if errors.IsNotFound(err) { 306 // Remove this whole if block when 1.16 compatibility is dropped. 307 if curl.Schema != "cs" { 308 return fmt.Errorf(`charm url has unsupported schema %q`, curl.Schema) 309 } 310 err = c.AddCharm(params.CharmURL{args.CharmUrl}) 311 if err != nil { 312 return err 313 } 314 ch, err = c.api.state.Charm(curl) 315 if err != nil { 316 return err 317 } 318 } else if err != nil { 319 return err 320 } 321 322 // TODO(axw) stop checking feature flag once storage has graduated. 323 var storageConstraints map[string]storage.Constraints 324 if featureflag.Enabled(storage.FeatureFlag) { 325 // Validate the storage parameters against the charm metadata, 326 // and ensure there are no conflicting parameters. 327 if err := validateCharmStorage(args, ch); err != nil { 328 return err 329 } 330 // Handle stores with no corresponding constraints. 331 for store, charmStorage := range ch.Meta().Storage { 332 if _, ok := args.Storage[store]; ok { 333 // TODO(axw) if pool is not specified, we should set it to 334 // the environment's default pool. 335 continue 336 } 337 if charmStorage.Shared { 338 // TODO(axw) get the environment's default shared storage 339 // pool, and create constraints here. 340 return errors.Errorf( 341 "no constraints specified for shared charm storage %q", 342 store, 343 ) 344 } 345 // TODO(axw) when storage pools, providers etc. are implemented, 346 // and we have a "loop" storage provider, we should create minimal 347 // constraints with the "loop" pool here. 348 return errors.Errorf( 349 "no constraints specified for charm storage %q, loop not implemented", 350 store, 351 ) 352 } 353 storageConstraints = args.Storage 354 } 355 356 var settings charm.Settings 357 if len(args.ConfigYAML) > 0 { 358 settings, err = ch.Config().ParseSettingsYAML([]byte(args.ConfigYAML), args.ServiceName) 359 } else if len(args.Config) > 0 { 360 // Parse config in a compatible way (see function comment). 361 settings, err = parseSettingsCompatible(ch, args.Config) 362 } 363 if err != nil { 364 return err 365 } 366 // Convert network tags to names for any given networks. 367 requestedNetworks, err := networkTagsToNames(args.Networks) 368 if err != nil { 369 return err 370 } 371 372 _, err = jjj.DeployService(c.api.state, 373 jjj.DeployServiceParams{ 374 ServiceName: args.ServiceName, 375 // TODO(dfc) ServiceOwner should be a tag 376 ServiceOwner: c.api.auth.GetAuthTag().String(), 377 Charm: ch, 378 NumUnits: args.NumUnits, 379 ConfigSettings: settings, 380 Constraints: args.Constraints, 381 ToMachineSpec: args.ToMachineSpec, 382 Networks: requestedNetworks, 383 Storage: storageConstraints, 384 }) 385 return err 386 } 387 388 // ServiceDeployWithNetworks works exactly like ServiceDeploy, but 389 // allows specifying networks to include or exclude on the machine 390 // where the charm gets deployed (either with args.Network or with 391 // constraints). 392 func (c *Client) ServiceDeployWithNetworks(args params.ServiceDeploy) error { 393 return c.ServiceDeploy(args) 394 } 395 396 func validateCharmStorage(args params.ServiceDeploy, ch *state.Charm) error { 397 if len(args.Storage) == 0 { 398 return nil 399 } 400 if len(args.ToMachineSpec) != 0 { 401 // TODO(axw) when we support dynamic disk provisioning, we can 402 // relax this. We will need to consult the storage provider to 403 // decide whether or not this is allowable. 404 return errors.New("cannot specify storage and machine placement") 405 } 406 // Remaining validation is done in state.AddService. 407 return nil 408 } 409 410 // ServiceUpdate updates the service attributes, including charm URL, 411 // minimum number of units, settings and constraints. 412 // All parameters in params.ServiceUpdate except the service name are optional. 413 func (c *Client) ServiceUpdate(args params.ServiceUpdate) error { 414 if !args.ForceCharmUrl { 415 if err := c.check.ChangeAllowed(); err != nil { 416 return errors.Trace(err) 417 } 418 } 419 service, err := c.api.state.Service(args.ServiceName) 420 if err != nil { 421 return err 422 } 423 // Set the charm for the given service. 424 if args.CharmUrl != "" { 425 if err = c.serviceSetCharm(service, args.CharmUrl, args.ForceCharmUrl); err != nil { 426 return err 427 } 428 } 429 // Set the minimum number of units for the given service. 430 if args.MinUnits != nil { 431 if err = service.SetMinUnits(*args.MinUnits); err != nil { 432 return err 433 } 434 } 435 // Set up service's settings. 436 if args.SettingsYAML != "" { 437 if err = serviceSetSettingsYAML(service, args.SettingsYAML); err != nil { 438 return err 439 } 440 } else if len(args.SettingsStrings) > 0 { 441 if err = serviceSetSettingsStrings(service, args.SettingsStrings); err != nil { 442 return err 443 } 444 } 445 // Update service's constraints. 446 if args.Constraints != nil { 447 return service.SetConstraints(*args.Constraints) 448 } 449 return nil 450 } 451 452 // serviceSetCharm sets the charm for the given service. 453 func (c *Client) serviceSetCharm(service *state.Service, url string, force bool) error { 454 curl, err := charm.ParseURL(url) 455 if err != nil { 456 return err 457 } 458 sch, err := c.api.state.Charm(curl) 459 if errors.IsNotFound(err) { 460 // Charms should be added before trying to use them, with 461 // AddCharm or AddLocalCharm API calls. When they're not, 462 // we're reverting to 1.16 compatibility mode. 463 return c.serviceSetCharm1dot16(service, curl, force) 464 } 465 if err != nil { 466 return err 467 } 468 return service.SetCharm(sch, force) 469 } 470 471 // serviceSetCharm1dot16 sets the charm for the given service in 1.16 472 // compatibility mode. Remove this when support for 1.16 is dropped. 473 func (c *Client) serviceSetCharm1dot16(service *state.Service, curl *charm.URL, force bool) error { 474 if curl.Schema != "cs" { 475 return fmt.Errorf(`charm url has unsupported schema %q`, curl.Schema) 476 } 477 if curl.Revision < 0 { 478 return fmt.Errorf("charm url must include revision") 479 } 480 err := c.AddCharm(params.CharmURL{curl.String()}) 481 if err != nil { 482 return err 483 } 484 ch, err := c.api.state.Charm(curl) 485 if err != nil { 486 return err 487 } 488 return service.SetCharm(ch, force) 489 } 490 491 // serviceSetSettingsYAML updates the settings for the given service, 492 // taking the configuration from a YAML string. 493 func serviceSetSettingsYAML(service *state.Service, settings string) error { 494 ch, _, err := service.Charm() 495 if err != nil { 496 return err 497 } 498 changes, err := ch.Config().ParseSettingsYAML([]byte(settings), service.Name()) 499 if err != nil { 500 return err 501 } 502 return service.UpdateConfigSettings(changes) 503 } 504 505 // serviceSetSettingsStrings updates the settings for the given service, 506 // taking the configuration from a map of strings. 507 func serviceSetSettingsStrings(service *state.Service, settings map[string]string) error { 508 ch, _, err := service.Charm() 509 if err != nil { 510 return err 511 } 512 // Parse config in a compatible way (see function comment). 513 changes, err := parseSettingsCompatible(ch, settings) 514 if err != nil { 515 return err 516 } 517 return service.UpdateConfigSettings(changes) 518 } 519 520 // newServiceSetSettingsStringsForClientAPI updates the settings for the given 521 // service, taking the configuration from a map of strings. 522 // 523 // TODO(Nate): replace serviceSetSettingsStrings with this onces the GUI no 524 // longer expects to be able to unset values by sending an empty string. 525 func newServiceSetSettingsStringsForClientAPI(service *state.Service, settings map[string]string) error { 526 ch, _, err := service.Charm() 527 if err != nil { 528 return err 529 } 530 531 // Validate the settings. 532 changes, err := ch.Config().ParseSettingsStrings(settings) 533 if err != nil { 534 return err 535 } 536 537 return service.UpdateConfigSettings(changes) 538 } 539 540 // ServiceSetCharm sets the charm for a given service. 541 // TODO(mattyw, all): This api call should be move to the new service facade. The client api version will then need bumping. 542 func (c *Client) ServiceSetCharm(args params.ServiceSetCharm) error { 543 // when forced, don't block 544 if !args.Force { 545 if err := c.check.ChangeAllowed(); err != nil { 546 return errors.Trace(err) 547 } 548 } 549 service, err := c.api.state.Service(args.ServiceName) 550 if err != nil { 551 return err 552 } 553 return c.serviceSetCharm(service, args.CharmUrl, args.Force) 554 } 555 556 // addServiceUnits adds a given number of units to a service. 557 func addServiceUnits(state *state.State, args params.AddServiceUnits) ([]*state.Unit, error) { 558 service, err := state.Service(args.ServiceName) 559 if err != nil { 560 return nil, err 561 } 562 if args.NumUnits < 1 { 563 return nil, fmt.Errorf("must add at least one unit") 564 } 565 if args.NumUnits > 1 && args.ToMachineSpec != "" { 566 return nil, fmt.Errorf("cannot use NumUnits with ToMachineSpec") 567 } 568 569 if args.ToMachineSpec != "" && names.IsValidMachine(args.ToMachineSpec) { 570 _, err = state.Machine(args.ToMachineSpec) 571 if err != nil { 572 return nil, errors.Annotatef(err, `cannot add units for service "%v" to machine %v`, args.ServiceName, args.ToMachineSpec) 573 } 574 } 575 return jjj.AddUnits(state, service, args.NumUnits, args.ToMachineSpec) 576 } 577 578 // AddServiceUnits adds a given number of units to a service. 579 func (c *Client) AddServiceUnits(args params.AddServiceUnits) (params.AddServiceUnitsResults, error) { 580 if err := c.check.ChangeAllowed(); err != nil { 581 return params.AddServiceUnitsResults{}, errors.Trace(err) 582 } 583 units, err := addServiceUnits(c.api.state, args) 584 if err != nil { 585 return params.AddServiceUnitsResults{}, err 586 } 587 unitNames := make([]string, len(units)) 588 for i, unit := range units { 589 unitNames[i] = unit.String() 590 } 591 return params.AddServiceUnitsResults{Units: unitNames}, nil 592 } 593 594 // DestroyServiceUnits removes a given set of service units. 595 func (c *Client) DestroyServiceUnits(args params.DestroyServiceUnits) error { 596 if err := c.check.RemoveAllowed(); err != nil { 597 return errors.Trace(err) 598 } 599 var errs []string 600 for _, name := range args.UnitNames { 601 unit, err := c.api.state.Unit(name) 602 switch { 603 case errors.IsNotFound(err): 604 err = fmt.Errorf("unit %q does not exist", name) 605 case err != nil: 606 case unit.Life() != state.Alive: 607 continue 608 case unit.IsPrincipal(): 609 err = unit.Destroy() 610 default: 611 err = fmt.Errorf("unit %q is a subordinate", name) 612 } 613 if err != nil { 614 errs = append(errs, err.Error()) 615 } 616 } 617 return destroyErr("units", args.UnitNames, errs) 618 } 619 620 // ServiceDestroy destroys a given service. 621 // TODO(mattyw, all): This api call should be move to the new service facade. The client api version will then need bumping. 622 func (c *Client) ServiceDestroy(args params.ServiceDestroy) error { 623 if err := c.check.RemoveAllowed(); err != nil { 624 return errors.Trace(err) 625 } 626 svc, err := c.api.state.Service(args.ServiceName) 627 if err != nil { 628 return err 629 } 630 return svc.Destroy() 631 } 632 633 // GetServiceConstraints returns the constraints for a given service. 634 // TODO(mattyw, all): This api call should be move to the new service facade. The client api version will then need bumping. 635 func (c *Client) GetServiceConstraints(args params.GetServiceConstraints) (params.GetConstraintsResults, error) { 636 svc, err := c.api.state.Service(args.ServiceName) 637 if err != nil { 638 return params.GetConstraintsResults{}, err 639 } 640 cons, err := svc.Constraints() 641 return params.GetConstraintsResults{cons}, err 642 } 643 644 // GetEnvironmentConstraints returns the constraints for the environment. 645 func (c *Client) GetEnvironmentConstraints() (params.GetConstraintsResults, error) { 646 cons, err := c.api.state.EnvironConstraints() 647 if err != nil { 648 return params.GetConstraintsResults{}, err 649 } 650 return params.GetConstraintsResults{cons}, nil 651 } 652 653 // SetServiceConstraints sets the constraints for a given service. 654 // TODO(mattyw, all): This api call should be move to the new service facade. The client api version will then need bumping. 655 func (c *Client) SetServiceConstraints(args params.SetConstraints) error { 656 if err := c.check.ChangeAllowed(); err != nil { 657 return errors.Trace(err) 658 } 659 svc, err := c.api.state.Service(args.ServiceName) 660 if err != nil { 661 return err 662 } 663 return svc.SetConstraints(args.Constraints) 664 } 665 666 // SetEnvironmentConstraints sets the constraints for the environment. 667 func (c *Client) SetEnvironmentConstraints(args params.SetConstraints) error { 668 if err := c.check.ChangeAllowed(); err != nil { 669 return errors.Trace(err) 670 } 671 return c.api.state.SetEnvironConstraints(args.Constraints) 672 } 673 674 // AddRelation adds a relation between the specified endpoints and returns the relation info. 675 func (c *Client) AddRelation(args params.AddRelation) (params.AddRelationResults, error) { 676 if err := c.check.ChangeAllowed(); err != nil { 677 return params.AddRelationResults{}, errors.Trace(err) 678 } 679 inEps, err := c.api.state.InferEndpoints(args.Endpoints...) 680 if err != nil { 681 return params.AddRelationResults{}, err 682 } 683 rel, err := c.api.state.AddRelation(inEps...) 684 if err != nil { 685 return params.AddRelationResults{}, err 686 } 687 outEps := make(map[string]charm.Relation) 688 for _, inEp := range inEps { 689 outEp, err := rel.Endpoint(inEp.ServiceName) 690 if err != nil { 691 return params.AddRelationResults{}, err 692 } 693 outEps[inEp.ServiceName] = outEp.Relation 694 } 695 return params.AddRelationResults{Endpoints: outEps}, nil 696 } 697 698 // DestroyRelation removes the relation between the specified endpoints. 699 func (c *Client) DestroyRelation(args params.DestroyRelation) error { 700 if err := c.check.RemoveAllowed(); err != nil { 701 return errors.Trace(err) 702 } 703 eps, err := c.api.state.InferEndpoints(args.Endpoints...) 704 if err != nil { 705 return err 706 } 707 rel, err := c.api.state.EndpointsRelation(eps...) 708 if err != nil { 709 return err 710 } 711 return rel.Destroy() 712 } 713 714 // AddMachines adds new machines with the supplied parameters. 715 func (c *Client) AddMachines(args params.AddMachines) (params.AddMachinesResults, error) { 716 return c.AddMachinesV2(args) 717 } 718 719 // AddMachinesV2 adds new machines with the supplied parameters. 720 func (c *Client) AddMachinesV2(args params.AddMachines) (params.AddMachinesResults, error) { 721 results := params.AddMachinesResults{ 722 Machines: make([]params.AddMachinesResult, len(args.MachineParams)), 723 } 724 if err := c.check.ChangeAllowed(); err != nil { 725 return results, errors.Trace(err) 726 } 727 for i, p := range args.MachineParams { 728 m, err := c.addOneMachine(p) 729 results.Machines[i].Error = common.ServerError(err) 730 if err == nil { 731 results.Machines[i].Machine = m.Id() 732 } 733 } 734 return results, nil 735 } 736 737 // InjectMachines injects a machine into state with provisioned status. 738 func (c *Client) InjectMachines(args params.AddMachines) (params.AddMachinesResults, error) { 739 return c.AddMachines(args) 740 } 741 742 func (c *Client) addOneMachine(p params.AddMachineParams) (*state.Machine, error) { 743 if p.ParentId != "" && p.ContainerType == "" { 744 return nil, fmt.Errorf("parent machine specified without container type") 745 } 746 if p.ContainerType != "" && p.Placement != nil { 747 return nil, fmt.Errorf("container type and placement are mutually exclusive") 748 } 749 if p.Placement != nil { 750 // Extract container type and parent from container placement directives. 751 containerType, err := instance.ParseContainerType(p.Placement.Scope) 752 if err == nil { 753 p.ContainerType = containerType 754 p.ParentId = p.Placement.Directive 755 p.Placement = nil 756 } 757 } 758 759 if p.ContainerType != "" || p.Placement != nil { 760 // Guard against dubious client by making sure that 761 // the following attributes can only be set when we're 762 // not using placement. 763 p.InstanceId = "" 764 p.Nonce = "" 765 p.HardwareCharacteristics = instance.HardwareCharacteristics{} 766 p.Addrs = nil 767 } 768 769 if p.Series == "" { 770 conf, err := c.api.state.EnvironConfig() 771 if err != nil { 772 return nil, err 773 } 774 p.Series = config.PreferredSeries(conf) 775 } 776 777 var placementDirective string 778 if p.Placement != nil { 779 env, err := c.api.state.Environment() 780 if err != nil { 781 return nil, err 782 } 783 // For 1.21 we should support both UUID and name, and with 1.22 784 // just support UUID 785 if p.Placement.Scope != env.Name() && p.Placement.Scope != env.UUID() { 786 return nil, fmt.Errorf("invalid environment name %q", p.Placement.Scope) 787 } 788 placementDirective = p.Placement.Directive 789 } 790 791 // TODO(axw) stop checking feature flag once storage has graduated. 792 var blockDeviceParams []state.BlockDeviceParams 793 if featureflag.Enabled(storage.FeatureFlag) { 794 // TODO(axw) unify storage and free block device constraints in state. 795 for _, cons := range p.Disks { 796 if cons.Pool != "" { 797 // TODO(axw) implement pools. If pool is not specified, 798 // determine default pool and set here. 799 return nil, errors.Errorf("storage pools not implemented") 800 } 801 if cons.Size == 0 { 802 return nil, errors.Errorf("invalid size %v", cons.Size) 803 } 804 if cons.Count == 0 { 805 return nil, errors.Errorf("invalid count %v", cons.Count) 806 } 807 params := state.BlockDeviceParams{ 808 Size: cons.Size, 809 } 810 for i := uint64(0); i < cons.Count; i++ { 811 blockDeviceParams = append(blockDeviceParams, params) 812 } 813 } 814 } 815 816 jobs, err := stateJobs(p.Jobs) 817 if err != nil { 818 return nil, err 819 } 820 template := state.MachineTemplate{ 821 Series: p.Series, 822 Constraints: p.Constraints, 823 BlockDevices: blockDeviceParams, 824 InstanceId: p.InstanceId, 825 Jobs: jobs, 826 Nonce: p.Nonce, 827 HardwareCharacteristics: p.HardwareCharacteristics, 828 Addresses: p.Addrs, 829 Placement: placementDirective, 830 } 831 if p.ContainerType == "" { 832 return c.api.state.AddOneMachine(template) 833 } 834 if p.ParentId != "" { 835 return c.api.state.AddMachineInsideMachine(template, p.ParentId, p.ContainerType) 836 } 837 return c.api.state.AddMachineInsideNewMachine(template, template, p.ContainerType) 838 } 839 840 func stateJobs(jobs []multiwatcher.MachineJob) ([]state.MachineJob, error) { 841 newJobs := make([]state.MachineJob, len(jobs)) 842 for i, job := range jobs { 843 newJob, err := machineJobFromParams(job) 844 if err != nil { 845 return nil, err 846 } 847 newJobs[i] = newJob 848 } 849 return newJobs, nil 850 } 851 852 // machineJobFromParams returns the job corresponding to multiwatcher.MachineJob. 853 // TODO(dfc) this function should live in apiserver/params, move there once 854 // state does not depend on apiserver/params 855 func machineJobFromParams(job multiwatcher.MachineJob) (state.MachineJob, error) { 856 switch job { 857 case multiwatcher.JobHostUnits: 858 return state.JobHostUnits, nil 859 case multiwatcher.JobManageEnviron: 860 return state.JobManageEnviron, nil 861 case multiwatcher.JobManageNetworking: 862 return state.JobManageNetworking, nil 863 case multiwatcher.JobManageStateDeprecated: 864 // Deprecated in 1.18. 865 return state.JobManageStateDeprecated, nil 866 default: 867 return -1, errors.Errorf("invalid machine job %q", job) 868 } 869 } 870 871 // ProvisioningScript returns a shell script that, when run, 872 // provisions a machine agent on the machine executing the script. 873 func (c *Client) ProvisioningScript(args params.ProvisioningScriptParams) (params.ProvisioningScriptResult, error) { 874 var result params.ProvisioningScriptResult 875 mcfg, err := MachineConfig(c.api.state, args.MachineId, args.Nonce, args.DataDir) 876 if err != nil { 877 return result, err 878 } 879 880 // Until DisablePackageCommands is retired, for backwards 881 // compatibility, we must respect the client's request and 882 // override any environment settings the user may have specified. 883 // If the client does specify this setting, it will only ever be 884 // true. False indicates the client doesn't care and we should use 885 // what's specified in the environments.yaml file. 886 if args.DisablePackageCommands { 887 mcfg.EnableOSRefreshUpdate = false 888 mcfg.EnableOSUpgrade = false 889 } else if cfg, err := c.api.state.EnvironConfig(); err != nil { 890 return result, err 891 } else { 892 mcfg.EnableOSUpgrade = cfg.EnableOSUpgrade() 893 mcfg.EnableOSRefreshUpdate = cfg.EnableOSRefreshUpdate() 894 } 895 896 result.Script, err = manual.ProvisioningScript(mcfg) 897 return result, err 898 } 899 900 // DestroyMachines removes a given set of machines. 901 func (c *Client) DestroyMachines(args params.DestroyMachines) error { 902 var errs []string 903 for _, id := range args.MachineNames { 904 machine, err := c.api.state.Machine(id) 905 switch { 906 case errors.IsNotFound(err): 907 err = fmt.Errorf("machine %s does not exist", id) 908 case err != nil: 909 case args.Force: 910 err = machine.ForceDestroy() 911 case machine.Life() != state.Alive: 912 continue 913 default: 914 { 915 if err := c.check.RemoveAllowed(); err != nil { 916 return errors.Trace(err) 917 } 918 err = machine.Destroy() 919 } 920 } 921 if err != nil { 922 errs = append(errs, err.Error()) 923 } 924 } 925 return destroyErr("machines", args.MachineNames, errs) 926 } 927 928 // CharmInfo returns information about the requested charm. 929 func (c *Client) CharmInfo(args params.CharmInfo) (api.CharmInfo, error) { 930 curl, err := charm.ParseURL(args.CharmURL) 931 if err != nil { 932 return api.CharmInfo{}, err 933 } 934 charm, err := c.api.state.Charm(curl) 935 if err != nil { 936 return api.CharmInfo{}, err 937 } 938 info := api.CharmInfo{ 939 Revision: charm.Revision(), 940 URL: curl.String(), 941 Config: charm.Config(), 942 Meta: charm.Meta(), 943 Actions: charm.Actions(), 944 } 945 return info, nil 946 } 947 948 // EnvironmentInfo returns information about the current environment (default 949 // series and type). 950 func (c *Client) EnvironmentInfo() (api.EnvironmentInfo, error) { 951 state := c.api.state 952 conf, err := state.EnvironConfig() 953 if err != nil { 954 return api.EnvironmentInfo{}, err 955 } 956 env, err := state.Environment() 957 if err != nil { 958 return api.EnvironmentInfo{}, err 959 } 960 961 info := api.EnvironmentInfo{ 962 DefaultSeries: config.PreferredSeries(conf), 963 ProviderType: conf.Type(), 964 Name: conf.Name(), 965 UUID: env.UUID(), 966 } 967 return info, nil 968 } 969 970 // ShareEnvironment allows the given user(s) access to the environment. 971 func (c *Client) ShareEnvironment(args params.ModifyEnvironUsers) (result params.ErrorResults, err error) { 972 var createdBy names.UserTag 973 var ok bool 974 if createdBy, ok = c.api.auth.GetAuthTag().(names.UserTag); !ok { 975 return result, errors.Errorf("api connection is not through a user") 976 } 977 978 result = params.ErrorResults{ 979 Results: make([]params.ErrorResult, len(args.Changes)), 980 } 981 if len(args.Changes) == 0 { 982 return result, nil 983 } 984 985 for i, arg := range args.Changes { 986 userTagString := arg.UserTag 987 user, err := names.ParseUserTag(userTagString) 988 if err != nil { 989 result.Results[i].Error = common.ServerError(errors.Annotate(err, "could not share environment")) 990 continue 991 } 992 switch arg.Action { 993 case params.AddEnvUser: 994 _, err := c.api.state.AddEnvironmentUser(user, createdBy) 995 if err != nil { 996 err = errors.Annotate(err, "could not share environment") 997 result.Results[i].Error = common.ServerError(err) 998 } 999 case params.RemoveEnvUser: 1000 err := c.api.state.RemoveEnvironmentUser(user) 1001 if err != nil { 1002 err = errors.Annotate(err, "could not unshare environment") 1003 result.Results[i].Error = common.ServerError(err) 1004 } 1005 default: 1006 result.Results[i].Error = common.ServerError(errors.Errorf("unknown action %q", arg.Action)) 1007 } 1008 } 1009 return result, nil 1010 } 1011 1012 // GetAnnotations returns annotations about a given entity. 1013 // This API is now deprecated - "Annotations" client should be used instead. 1014 // TODO(anastasiamac) remove for Juju 2.x 1015 func (c *Client) GetAnnotations(args params.GetAnnotations) (params.GetAnnotationsResults, error) { 1016 nothing := params.GetAnnotationsResults{} 1017 tag, err := c.parseEntityTag(args.Tag) 1018 if err != nil { 1019 return nothing, errors.Trace(err) 1020 } 1021 entity, err := c.findEntity(tag) 1022 if err != nil { 1023 return nothing, errors.Trace(err) 1024 } 1025 ann, err := c.api.state.Annotations(entity) 1026 if err != nil { 1027 return nothing, errors.Trace(err) 1028 } 1029 return params.GetAnnotationsResults{Annotations: ann}, nil 1030 } 1031 1032 func (c *Client) parseEntityTag(tag0 string) (names.Tag, error) { 1033 tag, err := names.ParseTag(tag0) 1034 if err != nil { 1035 return nil, errors.Trace(err) 1036 } 1037 if tag.Kind() == names.CharmTagKind { 1038 return nil, common.NotSupportedError(tag, "client.annotations") 1039 } 1040 return tag, nil 1041 } 1042 1043 func (c *Client) findEntity(tag names.Tag) (state.GlobalEntity, error) { 1044 entity0, err := c.api.state.FindEntity(tag) 1045 if err != nil { 1046 return nil, err 1047 } 1048 entity, ok := entity0.(state.GlobalEntity) 1049 if !ok { 1050 return nil, common.NotSupportedError(tag, "annotations") 1051 } 1052 return entity, nil 1053 } 1054 1055 // SetAnnotations stores annotations about a given entity. 1056 // This API is now deprecated - "Annotations" client should be used instead. 1057 // TODO(anastasiamac) remove for Juju 2.x 1058 func (c *Client) SetAnnotations(args params.SetAnnotations) error { 1059 tag, err := c.parseEntityTag(args.Tag) 1060 if err != nil { 1061 return errors.Trace(err) 1062 } 1063 entity, err := c.findEntity(tag) 1064 if err != nil { 1065 return errors.Trace(err) 1066 } 1067 return c.api.state.SetAnnotations(entity, args.Pairs) 1068 } 1069 1070 // parseSettingsCompatible parses setting strings in a way that is 1071 // compatible with the behavior before this CL based on the issue 1072 // http://pad.lv/1194945. Until then setting an option to an empty 1073 // string caused it to reset to the default value. We now allow 1074 // empty strings as actual values, but we want to preserve the API 1075 // behavior. 1076 func parseSettingsCompatible(ch *state.Charm, settings map[string]string) (charm.Settings, error) { 1077 setSettings := map[string]string{} 1078 unsetSettings := charm.Settings{} 1079 // Split settings into those which set and those which unset a value. 1080 for name, value := range settings { 1081 if value == "" { 1082 unsetSettings[name] = nil 1083 continue 1084 } 1085 setSettings[name] = value 1086 } 1087 // Validate the settings. 1088 changes, err := ch.Config().ParseSettingsStrings(setSettings) 1089 if err != nil { 1090 return nil, err 1091 } 1092 // Validate the unsettings and merge them into the changes. 1093 unsetSettings, err = ch.Config().ValidateSettings(unsetSettings) 1094 if err != nil { 1095 return nil, err 1096 } 1097 for name := range unsetSettings { 1098 changes[name] = nil 1099 } 1100 return changes, nil 1101 } 1102 1103 // AgentVersion returns the current version that the API server is running. 1104 func (c *Client) AgentVersion() (params.AgentVersionResult, error) { 1105 return params.AgentVersionResult{Version: version.Current.Number}, nil 1106 } 1107 1108 // EnvironmentGet implements the server-side part of the 1109 // get-environment CLI command. 1110 func (c *Client) EnvironmentGet() (params.EnvironmentConfigResults, error) { 1111 result := params.EnvironmentConfigResults{} 1112 // Get the existing environment config from the state. 1113 config, err := c.api.state.EnvironConfig() 1114 if err != nil { 1115 return result, err 1116 } 1117 result.Config = config.AllAttrs() 1118 return result, nil 1119 } 1120 1121 // EnvironmentSet implements the server-side part of the 1122 // set-environment CLI command. 1123 func (c *Client) EnvironmentSet(args params.EnvironmentSet) error { 1124 if err := c.check.ChangeAllowed(); err != nil { 1125 // if trying to change value for block-changes, we would want to let it go. 1126 if v, present := args.Config[config.PreventAllChangesKey]; !present { 1127 return errors.Trace(err) 1128 } else if block, ok := v.(bool); ok && block { 1129 // still want to block changes 1130 return errors.Trace(err) 1131 } 1132 // else if block is false, we want to unblock changes 1133 } 1134 // Make sure we don't allow changing agent-version. 1135 checkAgentVersion := func(updateAttrs map[string]interface{}, removeAttrs []string, oldConfig *config.Config) error { 1136 if v, found := updateAttrs["agent-version"]; found { 1137 oldVersion, _ := oldConfig.AgentVersion() 1138 if v != oldVersion.String() { 1139 return fmt.Errorf("agent-version cannot be changed") 1140 } 1141 } 1142 return nil 1143 } 1144 // Replace any deprecated attributes with their new values. 1145 attrs := config.ProcessDeprecatedAttributes(args.Config) 1146 // TODO(waigani) 2014-3-11 #1167616 1147 // Add a txn retry loop to ensure that the settings on disk have not 1148 // changed underneath us. 1149 return c.api.state.UpdateEnvironConfig(attrs, nil, checkAgentVersion) 1150 } 1151 1152 // EnvironmentUnset implements the server-side part of the 1153 // set-environment CLI command. 1154 func (c *Client) EnvironmentUnset(args params.EnvironmentUnset) error { 1155 if err := c.check.ChangeAllowed(); err != nil { 1156 return errors.Trace(err) 1157 } 1158 // TODO(waigani) 2014-3-11 #1167616 1159 // Add a txn retry loop to ensure that the settings on disk have not 1160 // changed underneath us. 1161 return c.api.state.UpdateEnvironConfig(nil, args.Keys, nil) 1162 } 1163 1164 // SetEnvironAgentVersion sets the environment agent version. 1165 func (c *Client) SetEnvironAgentVersion(args params.SetEnvironAgentVersion) error { 1166 if err := c.check.ChangeAllowed(); err != nil { 1167 return errors.Trace(err) 1168 } 1169 return c.api.state.SetEnvironAgentVersion(args.Version) 1170 } 1171 1172 // AbortCurrentUpgrade aborts and archives the current upgrade 1173 // synchronisation record, if any. 1174 func (c *Client) AbortCurrentUpgrade() error { 1175 if err := c.check.ChangeAllowed(); err != nil { 1176 return errors.Trace(err) 1177 } 1178 return c.api.state.AbortCurrentUpgrade() 1179 } 1180 1181 // FindTools returns a List containing all tools matching the given parameters. 1182 func (c *Client) FindTools(args params.FindToolsParams) (params.FindToolsResult, error) { 1183 return c.api.toolsFinder.FindTools(args) 1184 } 1185 1186 func destroyErr(desc string, ids, errs []string) error { 1187 if len(errs) == 0 { 1188 return nil 1189 } 1190 msg := "some %s were not destroyed" 1191 if len(errs) == len(ids) { 1192 msg = "no %s were destroyed" 1193 } 1194 msg = fmt.Sprintf(msg, desc) 1195 return fmt.Errorf("%s: %s", msg, strings.Join(errs, "; ")) 1196 } 1197 1198 // AddCharm adds the given charm URL (which must include revision) to 1199 // the environment, if it does not exist yet. Local charms are not 1200 // supported, only charm store URLs. See also AddLocalCharm(). 1201 func (c *Client) AddCharm(args params.CharmURL) error { 1202 charmURL, err := charm.ParseURL(args.URL) 1203 if err != nil { 1204 return err 1205 } 1206 if charmURL.Schema != "cs" { 1207 return fmt.Errorf("only charm store charm URLs are supported, with cs: schema") 1208 } 1209 if charmURL.Revision < 0 { 1210 return fmt.Errorf("charm URL must include revision") 1211 } 1212 1213 // First, check if a pending or a real charm exists in state. 1214 stateCharm, err := c.api.state.PrepareStoreCharmUpload(charmURL) 1215 if err == nil && stateCharm.IsUploaded() { 1216 // Charm already in state (it was uploaded already). 1217 return nil 1218 } else if err != nil { 1219 return err 1220 } 1221 1222 // Get the charm and its information from the store. 1223 envConfig, err := c.api.state.EnvironConfig() 1224 if err != nil { 1225 return err 1226 } 1227 config.SpecializeCharmRepo(CharmStore, envConfig) 1228 downloadedCharm, err := CharmStore.Get(charmURL) 1229 if err != nil { 1230 return errors.Annotatef(err, "cannot download charm %q", charmURL.String()) 1231 } 1232 1233 // Open it and calculate the SHA256 hash. 1234 downloadedBundle, ok := downloadedCharm.(*charm.CharmArchive) 1235 if !ok { 1236 return errors.Errorf("expected a charm archive, got %T", downloadedCharm) 1237 } 1238 archive, err := os.Open(downloadedBundle.Path) 1239 if err != nil { 1240 return errors.Annotate(err, "cannot read downloaded charm") 1241 } 1242 defer archive.Close() 1243 bundleSHA256, size, err := utils.ReadSHA256(archive) 1244 if err != nil { 1245 return errors.Annotate(err, "cannot calculate SHA256 hash of charm") 1246 } 1247 if _, err := archive.Seek(0, 0); err != nil { 1248 return errors.Annotate(err, "cannot rewind charm archive") 1249 } 1250 1251 // Store the charm archive in environment storage. 1252 return StoreCharmArchive( 1253 c.api.state, 1254 charmURL, 1255 downloadedCharm, 1256 archive, 1257 size, 1258 bundleSHA256, 1259 ) 1260 } 1261 1262 // StoreCharmArchive stores a charm archive in environment storage. 1263 func StoreCharmArchive(st *state.State, curl *charm.URL, ch charm.Charm, r io.Reader, size int64, sha256 string) error { 1264 storage := newStateStorage(st.EnvironUUID(), st.MongoSession()) 1265 storagePath, err := charmArchiveStoragePath(curl) 1266 if err != nil { 1267 return errors.Annotate(err, "cannot generate charm archive name") 1268 } 1269 if err := storage.Put(storagePath, r, size); err != nil { 1270 return errors.Annotate(err, "cannot add charm to storage") 1271 } 1272 1273 // Now update the charm data in state and mark it as no longer pending. 1274 _, err = st.UpdateUploadedCharm(ch, curl, storagePath, sha256) 1275 if err != nil { 1276 alreadyUploaded := err == state.ErrCharmRevisionAlreadyModified || 1277 errors.Cause(err) == state.ErrCharmRevisionAlreadyModified || 1278 state.IsCharmAlreadyUploadedError(err) 1279 if err := storage.Remove(storagePath); err != nil { 1280 if alreadyUploaded { 1281 logger.Errorf("cannot remove duplicated charm archive from storage: %v", err) 1282 } else { 1283 logger.Errorf("cannot remove unsuccessfully recorded charm archive from storage: %v", err) 1284 } 1285 } 1286 if alreadyUploaded { 1287 // Somebody else managed to upload and update the charm in 1288 // state before us. This is not an error. 1289 return nil 1290 } 1291 } 1292 return nil 1293 } 1294 1295 func (c *Client) ResolveCharms(args params.ResolveCharms) (params.ResolveCharmResults, error) { 1296 var results params.ResolveCharmResults 1297 1298 envConfig, err := c.api.state.EnvironConfig() 1299 if err != nil { 1300 return params.ResolveCharmResults{}, err 1301 } 1302 config.SpecializeCharmRepo(CharmStore, envConfig) 1303 1304 for _, ref := range args.References { 1305 result := params.ResolveCharmResult{} 1306 curl, err := c.resolveCharm(&ref, CharmStore) 1307 if err != nil { 1308 result.Error = err.Error() 1309 } else { 1310 result.URL = curl 1311 } 1312 results.URLs = append(results.URLs, result) 1313 } 1314 return results, nil 1315 } 1316 1317 func (c *Client) resolveCharm(ref *charm.Reference, repo charm.Repository) (*charm.URL, error) { 1318 if ref.Schema != "cs" { 1319 return nil, fmt.Errorf("only charm store charm references are supported, with cs: schema") 1320 } 1321 1322 // Resolve the charm location with the repository. 1323 return repo.Resolve(ref) 1324 } 1325 1326 // charmArchiveStoragePath returns a string that is suitable as a 1327 // storage path, using a random UUID to avoid colliding with concurrent 1328 // uploads. 1329 func charmArchiveStoragePath(curl *charm.URL) (string, error) { 1330 uuid, err := utils.NewUUID() 1331 if err != nil { 1332 return "", err 1333 } 1334 return fmt.Sprintf("charms/%s-%s", curl.String(), uuid), nil 1335 } 1336 1337 // RetryProvisioning marks a provisioning error as transient on the machines. 1338 func (c *Client) RetryProvisioning(p params.Entities) (params.ErrorResults, error) { 1339 if err := c.check.ChangeAllowed(); err != nil { 1340 return params.ErrorResults{}, errors.Trace(err) 1341 } 1342 entityStatus := make([]params.EntityStatus, len(p.Entities)) 1343 for i, entity := range p.Entities { 1344 entityStatus[i] = params.EntityStatus{Tag: entity.Tag, Data: map[string]interface{}{"transient": true}} 1345 } 1346 return c.api.statusSetter.UpdateStatus(params.SetStatus{ 1347 Entities: entityStatus, 1348 }) 1349 } 1350 1351 // APIHostPorts returns the API host/port addresses stored in state. 1352 func (c *Client) APIHostPorts() (result params.APIHostPortsResult, err error) { 1353 if result.Servers, err = c.api.state.APIHostPorts(); err != nil { 1354 return params.APIHostPortsResult{}, err 1355 } 1356 return result, nil 1357 } 1358 1359 // EnsureAvailability ensures the availability of Juju state servers. 1360 // DEPRECATED: remove when we stop supporting 1.20 and earlier clients. 1361 // This API is now on the HighAvailability facade. 1362 func (c *Client) EnsureAvailability(args params.StateServersSpecs) (params.StateServersChangeResults, error) { 1363 if err := c.check.ChangeAllowed(); err != nil { 1364 return params.StateServersChangeResults{}, errors.Trace(err) 1365 } 1366 results := params.StateServersChangeResults{Results: make([]params.StateServersChangeResult, len(args.Specs))} 1367 for i, stateServersSpec := range args.Specs { 1368 result, err := highavailability.EnsureAvailabilitySingle(c.api.state, stateServersSpec) 1369 results.Results[i].Result = result 1370 results.Results[i].Error = common.ServerError(err) 1371 } 1372 return results, nil 1373 }