github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/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 "strings" 9 "time" 10 11 "github.com/juju/errors" 12 "github.com/juju/loggo" 13 "github.com/juju/names" 14 "gopkg.in/juju/charm.v5" 15 16 "github.com/juju/juju/api" 17 "github.com/juju/juju/apiserver/common" 18 "github.com/juju/juju/apiserver/highavailability" 19 "github.com/juju/juju/apiserver/params" 20 "github.com/juju/juju/apiserver/service" 21 "github.com/juju/juju/environs/config" 22 "github.com/juju/juju/environs/manual" 23 "github.com/juju/juju/instance" 24 jjj "github.com/juju/juju/juju" 25 "github.com/juju/juju/network" 26 "github.com/juju/juju/state" 27 "github.com/juju/juju/version" 28 ) 29 30 func init() { 31 common.RegisterStandardFacade("Client", 0, NewClient) 32 } 33 34 var logger = loggo.GetLogger("juju.apiserver.client") 35 36 type API struct { 37 state *state.State 38 auth common.Authorizer 39 resources *common.Resources 40 client *Client 41 // statusSetter provides common methods for updating an entity's provisioning status. 42 statusSetter *common.StatusSetter 43 toolsFinder *common.ToolsFinder 44 } 45 46 // Client serves client-specific API methods. 47 type Client struct { 48 api *API 49 check *common.BlockChecker 50 } 51 52 // NewClient creates a new instance of the Client Facade. 53 func NewClient(st *state.State, resources *common.Resources, authorizer common.Authorizer) (*Client, error) { 54 if !authorizer.AuthClient() { 55 return nil, common.ErrPerm 56 } 57 urlGetter := common.NewToolsURLGetter(st.EnvironUUID(), st) 58 return &Client{ 59 api: &API{ 60 state: st, 61 auth: authorizer, 62 resources: resources, 63 statusSetter: common.NewStatusSetter(st, common.AuthAlways()), 64 toolsFinder: common.NewToolsFinder(st, st, urlGetter), 65 }, 66 check: common.NewBlockChecker(st)}, nil 67 } 68 69 func (c *Client) WatchAll() (params.AllWatcherId, error) { 70 w := c.api.state.Watch() 71 return params.AllWatcherId{ 72 AllWatcherId: c.api.resources.Register(w), 73 }, nil 74 } 75 76 // ServiceSet implements the server side of Client.ServiceSet. Values set to an 77 // empty string will be unset. 78 // 79 // (Deprecated) Use NewServiceSetForClientAPI instead, to preserve values set to 80 // an empty string, and use ServiceUnset to unset values. 81 func (c *Client) ServiceSet(p params.ServiceSet) error { 82 if err := c.check.ChangeAllowed(); err != nil { 83 return errors.Trace(err) 84 } 85 svc, err := c.api.state.Service(p.ServiceName) 86 if err != nil { 87 return err 88 } 89 return service.ServiceSetSettingsStrings(svc, p.Options) 90 } 91 92 // NewServiceSetForClientAPI implements the server side of 93 // Client.NewServiceSetForClientAPI. This is exactly like ServiceSet except that 94 // it does not unset values that are set to an empty string. ServiceUnset 95 // should be used for that. 96 // 97 // TODO(Nate): rename this to ServiceSet (and remove the deprecated ServiceSet) 98 // when the GUI handles the new behavior. 99 // TODO(mattyw, all): This api call should be move to the new service facade. The client api version will then need bumping. 100 func (c *Client) NewServiceSetForClientAPI(p params.ServiceSet) error { 101 svc, err := c.api.state.Service(p.ServiceName) 102 if err != nil { 103 return err 104 } 105 return newServiceSetSettingsStringsForClientAPI(svc, p.Options) 106 } 107 108 // ServiceUnset implements the server side of Client.ServiceUnset. 109 // TODO(mattyw, all): This api call should be move to the new service facade. The client api version will then need bumping. 110 func (c *Client) ServiceUnset(p params.ServiceUnset) error { 111 if err := c.check.ChangeAllowed(); err != nil { 112 return errors.Trace(err) 113 } 114 svc, err := c.api.state.Service(p.ServiceName) 115 if err != nil { 116 return err 117 } 118 settings := make(charm.Settings) 119 for _, option := range p.Options { 120 settings[option] = nil 121 } 122 return svc.UpdateConfigSettings(settings) 123 } 124 125 // ServiceSetYAML implements the server side of Client.ServerSetYAML. 126 // TODO(mattyw, all): This api call should be move to the new service facade. The client api version will then need bumping. 127 func (c *Client) ServiceSetYAML(p params.ServiceSetYAML) error { 128 if err := c.check.ChangeAllowed(); err != nil { 129 return errors.Trace(err) 130 } 131 svc, err := c.api.state.Service(p.ServiceName) 132 if err != nil { 133 return err 134 } 135 return serviceSetSettingsYAML(svc, p.Config) 136 } 137 138 // ServiceCharmRelations implements the server side of Client.ServiceCharmRelations. 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) ServiceCharmRelations(p params.ServiceCharmRelations) (params.ServiceCharmRelationsResults, error) { 141 var results params.ServiceCharmRelationsResults 142 service, err := c.api.state.Service(p.ServiceName) 143 if err != nil { 144 return results, err 145 } 146 endpoints, err := service.Endpoints() 147 if err != nil { 148 return results, err 149 } 150 results.CharmRelations = make([]string, len(endpoints)) 151 for i, endpoint := range endpoints { 152 results.CharmRelations[i] = endpoint.Relation.Name 153 } 154 return results, nil 155 } 156 157 // Resolved implements the server side of Client.Resolved. 158 func (c *Client) Resolved(p params.Resolved) error { 159 if err := c.check.ChangeAllowed(); err != nil { 160 return errors.Trace(err) 161 } 162 unit, err := c.api.state.Unit(p.UnitName) 163 if err != nil { 164 return err 165 } 166 return unit.Resolve(p.Retry) 167 } 168 169 // PublicAddress implements the server side of Client.PublicAddress. 170 func (c *Client) PublicAddress(p params.PublicAddress) (results params.PublicAddressResults, err error) { 171 switch { 172 case names.IsValidMachine(p.Target): 173 machine, err := c.api.state.Machine(p.Target) 174 if err != nil { 175 return results, err 176 } 177 addr := network.SelectPublicAddress(machine.Addresses()) 178 if addr == "" { 179 return results, fmt.Errorf("machine %q has no public address", machine) 180 } 181 return params.PublicAddressResults{PublicAddress: addr}, nil 182 183 case names.IsValidUnit(p.Target): 184 unit, err := c.api.state.Unit(p.Target) 185 if err != nil { 186 return results, err 187 } 188 addr, ok := unit.PublicAddress() 189 if !ok { 190 return results, fmt.Errorf("unit %q has no public address", unit) 191 } 192 return params.PublicAddressResults{PublicAddress: addr}, nil 193 } 194 return results, fmt.Errorf("unknown unit or machine %q", p.Target) 195 } 196 197 // PrivateAddress implements the server side of Client.PrivateAddress. 198 func (c *Client) PrivateAddress(p params.PrivateAddress) (results params.PrivateAddressResults, err error) { 199 switch { 200 case names.IsValidMachine(p.Target): 201 machine, err := c.api.state.Machine(p.Target) 202 if err != nil { 203 return results, err 204 } 205 addr := network.SelectInternalAddress(machine.Addresses(), false) 206 if addr == "" { 207 return results, fmt.Errorf("machine %q has no internal address", machine) 208 } 209 return params.PrivateAddressResults{PrivateAddress: addr}, nil 210 211 case names.IsValidUnit(p.Target): 212 unit, err := c.api.state.Unit(p.Target) 213 if err != nil { 214 return results, err 215 } 216 addr, ok := unit.PrivateAddress() 217 if !ok { 218 return results, fmt.Errorf("unit %q has no internal address", unit) 219 } 220 return params.PrivateAddressResults{PrivateAddress: addr}, nil 221 } 222 return results, fmt.Errorf("unknown unit or machine %q", p.Target) 223 } 224 225 // ServiceExpose changes the juju-managed firewall to expose any ports that 226 // were also explicitly marked by units as open. 227 // TODO(mattyw, all): This api call should be move to the new service facade. The client api version will then need bumping. 228 func (c *Client) ServiceExpose(args params.ServiceExpose) error { 229 if err := c.check.ChangeAllowed(); err != nil { 230 return errors.Trace(err) 231 } 232 svc, err := c.api.state.Service(args.ServiceName) 233 if err != nil { 234 return err 235 } 236 return svc.SetExposed() 237 } 238 239 // ServiceUnexpose changes the juju-managed firewall to unexpose any ports that 240 // were also explicitly marked by units as open. 241 // TODO(mattyw, all): This api call should be move to the new service facade. The client api version will then need bumping. 242 func (c *Client) ServiceUnexpose(args params.ServiceUnexpose) error { 243 if err := c.check.ChangeAllowed(); err != nil { 244 return errors.Trace(err) 245 } 246 svc, err := c.api.state.Service(args.ServiceName) 247 if err != nil { 248 return err 249 } 250 return svc.ClearExposed() 251 } 252 253 // ServiceDeploy fetches the charm from the charm store and deploys it. 254 // AddCharm or AddLocalCharm should be called to add the charm 255 // before calling ServiceDeploy, although for backward compatibility 256 // this is not necessary until 1.16 support is removed. 257 func (c *Client) ServiceDeploy(args params.ServiceDeploy) error { 258 if err := c.check.ChangeAllowed(); err != nil { 259 return errors.Trace(err) 260 } 261 return service.DeployService(c.api.state, c.api.auth.GetAuthTag().String(), args) 262 } 263 264 // ServiceDeployWithNetworks works exactly like ServiceDeploy, but 265 // allows specifying networks to include or exclude on the machine 266 // where the charm gets deployed (either with args.Network or with 267 // constraints). 268 // 269 // TODO(dimitern): Drop the special handling of networks in favor of 270 // spaces constraints, once possible. 271 func (c *Client) ServiceDeployWithNetworks(args params.ServiceDeploy) error { 272 return c.ServiceDeploy(args) 273 } 274 275 // ServiceUpdate updates the service attributes, including charm URL, 276 // minimum number of units, settings and constraints. 277 // All parameters in params.ServiceUpdate except the service name are optional. 278 func (c *Client) ServiceUpdate(args params.ServiceUpdate) error { 279 if !args.ForceCharmUrl { 280 if err := c.check.ChangeAllowed(); err != nil { 281 return errors.Trace(err) 282 } 283 } 284 svc, err := c.api.state.Service(args.ServiceName) 285 if err != nil { 286 return err 287 } 288 // Set the charm for the given service. 289 if args.CharmUrl != "" { 290 if err = c.serviceSetCharm(svc, args.CharmUrl, args.ForceCharmUrl); err != nil { 291 return err 292 } 293 } 294 // Set the minimum number of units for the given service. 295 if args.MinUnits != nil { 296 if err = svc.SetMinUnits(*args.MinUnits); err != nil { 297 return err 298 } 299 } 300 // Set up service's settings. 301 if args.SettingsYAML != "" { 302 if err = serviceSetSettingsYAML(svc, args.SettingsYAML); err != nil { 303 return err 304 } 305 } else if len(args.SettingsStrings) > 0 { 306 if err = service.ServiceSetSettingsStrings(svc, args.SettingsStrings); err != nil { 307 return err 308 } 309 } 310 // Update service's constraints. 311 if args.Constraints != nil { 312 return svc.SetConstraints(*args.Constraints) 313 } 314 return nil 315 } 316 317 // serviceSetCharm sets the charm for the given service. 318 func (c *Client) serviceSetCharm(service *state.Service, url string, force bool) error { 319 curl, err := charm.ParseURL(url) 320 if err != nil { 321 return err 322 } 323 sch, err := c.api.state.Charm(curl) 324 if errors.IsNotFound(err) { 325 // Charms should be added before trying to use them, with 326 // AddCharm or AddLocalCharm API calls. When they're not, 327 // we're reverting to 1.16 compatibility mode. 328 return c.serviceSetCharm1dot16(service, curl, force) 329 } 330 if err != nil { 331 return err 332 } 333 return service.SetCharm(sch, force) 334 } 335 336 // serviceSetCharm1dot16 sets the charm for the given service in 1.16 337 // compatibility mode. Remove this when support for 1.16 is dropped. 338 func (c *Client) serviceSetCharm1dot16(service *state.Service, curl *charm.URL, force bool) error { 339 if curl.Schema != "cs" { 340 return fmt.Errorf(`charm url has unsupported schema %q`, curl.Schema) 341 } 342 if curl.Revision < 0 { 343 return fmt.Errorf("charm url must include revision") 344 } 345 err := c.AddCharm(params.CharmURL{ 346 URL: curl.String(), 347 }) 348 if err != nil { 349 return err 350 } 351 ch, err := c.api.state.Charm(curl) 352 if err != nil { 353 return err 354 } 355 return service.SetCharm(ch, force) 356 } 357 358 // serviceSetSettingsYAML updates the settings for the given service, 359 // taking the configuration from a YAML string. 360 func serviceSetSettingsYAML(service *state.Service, settings string) error { 361 ch, _, err := service.Charm() 362 if err != nil { 363 return err 364 } 365 changes, err := ch.Config().ParseSettingsYAML([]byte(settings), service.Name()) 366 if err != nil { 367 return err 368 } 369 return service.UpdateConfigSettings(changes) 370 } 371 372 // newServiceSetSettingsStringsForClientAPI updates the settings for the given 373 // service, taking the configuration from a map of strings. 374 // 375 // TODO(Nate): replace serviceSetSettingsStrings with this onces the GUI no 376 // longer expects to be able to unset values by sending an empty string. 377 func newServiceSetSettingsStringsForClientAPI(service *state.Service, settings map[string]string) error { 378 ch, _, err := service.Charm() 379 if err != nil { 380 return err 381 } 382 383 // Validate the settings. 384 changes, err := ch.Config().ParseSettingsStrings(settings) 385 if err != nil { 386 return err 387 } 388 389 return service.UpdateConfigSettings(changes) 390 } 391 392 // ServiceSetCharm sets the charm for a given service. 393 // TODO(mattyw, all): This api call should be move to the new service facade. The client api version will then need bumping. 394 func (c *Client) ServiceSetCharm(args params.ServiceSetCharm) error { 395 // when forced, don't block 396 if !args.Force { 397 if err := c.check.ChangeAllowed(); err != nil { 398 return errors.Trace(err) 399 } 400 } 401 service, err := c.api.state.Service(args.ServiceName) 402 if err != nil { 403 return err 404 } 405 return c.serviceSetCharm(service, args.CharmUrl, args.Force) 406 } 407 408 // addServiceUnits adds a given number of units to a service. 409 func addServiceUnits(state *state.State, args params.AddServiceUnits) ([]*state.Unit, error) { 410 service, err := state.Service(args.ServiceName) 411 if err != nil { 412 return nil, err 413 } 414 if args.NumUnits < 1 { 415 return nil, fmt.Errorf("must add at least one unit") 416 } 417 418 // New API uses placement directives. 419 if len(args.Placement) > 0 { 420 return jjj.AddUnitsWithPlacement(state, service, args.NumUnits, args.Placement) 421 } 422 423 // Otherwise we use the older machine spec. 424 if args.NumUnits > 1 && args.ToMachineSpec != "" { 425 return nil, fmt.Errorf("cannot use NumUnits with ToMachineSpec") 426 } 427 428 if args.ToMachineSpec != "" && names.IsValidMachine(args.ToMachineSpec) { 429 _, err = state.Machine(args.ToMachineSpec) 430 if err != nil { 431 return nil, errors.Annotatef(err, `cannot add units for service "%v" to machine %v`, args.ServiceName, args.ToMachineSpec) 432 } 433 } 434 return jjj.AddUnits(state, service, args.NumUnits, args.ToMachineSpec) 435 } 436 437 // AddServiceUnits adds a given number of units to a service. 438 func (c *Client) AddServiceUnits(args params.AddServiceUnits) (params.AddServiceUnitsResults, error) { 439 return c.AddServiceUnitsWithPlacement(args) 440 } 441 442 // AddServiceUnits adds a given number of units to a service. 443 func (c *Client) AddServiceUnitsWithPlacement(args params.AddServiceUnits) (params.AddServiceUnitsResults, error) { 444 if err := c.check.ChangeAllowed(); err != nil { 445 return params.AddServiceUnitsResults{}, errors.Trace(err) 446 } 447 units, err := addServiceUnits(c.api.state, args) 448 if err != nil { 449 return params.AddServiceUnitsResults{}, err 450 } 451 unitNames := make([]string, len(units)) 452 for i, unit := range units { 453 unitNames[i] = unit.String() 454 } 455 return params.AddServiceUnitsResults{Units: unitNames}, nil 456 } 457 458 // DestroyServiceUnits removes a given set of service units. 459 func (c *Client) DestroyServiceUnits(args params.DestroyServiceUnits) error { 460 if err := c.check.RemoveAllowed(); err != nil { 461 return errors.Trace(err) 462 } 463 var errs []string 464 for _, name := range args.UnitNames { 465 unit, err := c.api.state.Unit(name) 466 switch { 467 case errors.IsNotFound(err): 468 err = fmt.Errorf("unit %q does not exist", name) 469 case err != nil: 470 case unit.Life() != state.Alive: 471 continue 472 case unit.IsPrincipal(): 473 err = unit.Destroy() 474 default: 475 err = fmt.Errorf("unit %q is a subordinate", name) 476 } 477 if err != nil { 478 errs = append(errs, err.Error()) 479 } 480 } 481 return destroyErr("units", args.UnitNames, errs) 482 } 483 484 // ServiceDestroy destroys a given service. 485 // TODO(mattyw, all): This api call should be move to the new service facade. The client api version will then need bumping. 486 func (c *Client) ServiceDestroy(args params.ServiceDestroy) error { 487 if err := c.check.RemoveAllowed(); err != nil { 488 return errors.Trace(err) 489 } 490 svc, err := c.api.state.Service(args.ServiceName) 491 if err != nil { 492 return err 493 } 494 return svc.Destroy() 495 } 496 497 // GetServiceConstraints returns the constraints for a given service. 498 // TODO(mattyw, all): This api call should be move to the new service facade. The client api version will then need bumping. 499 func (c *Client) GetServiceConstraints(args params.GetServiceConstraints) (params.GetConstraintsResults, error) { 500 svc, err := c.api.state.Service(args.ServiceName) 501 if err != nil { 502 return params.GetConstraintsResults{}, err 503 } 504 cons, err := svc.Constraints() 505 return params.GetConstraintsResults{cons}, err 506 } 507 508 // GetEnvironmentConstraints returns the constraints for the environment. 509 func (c *Client) GetEnvironmentConstraints() (params.GetConstraintsResults, error) { 510 cons, err := c.api.state.EnvironConstraints() 511 if err != nil { 512 return params.GetConstraintsResults{}, err 513 } 514 return params.GetConstraintsResults{cons}, nil 515 } 516 517 // SetServiceConstraints sets the constraints for a given service. 518 // TODO(mattyw, all): This api call should be move to the new service facade. The client api version will then need bumping. 519 func (c *Client) SetServiceConstraints(args params.SetConstraints) error { 520 if err := c.check.ChangeAllowed(); err != nil { 521 return errors.Trace(err) 522 } 523 svc, err := c.api.state.Service(args.ServiceName) 524 if err != nil { 525 return err 526 } 527 return svc.SetConstraints(args.Constraints) 528 } 529 530 // SetEnvironmentConstraints sets the constraints for the environment. 531 func (c *Client) SetEnvironmentConstraints(args params.SetConstraints) error { 532 if err := c.check.ChangeAllowed(); err != nil { 533 return errors.Trace(err) 534 } 535 return c.api.state.SetEnvironConstraints(args.Constraints) 536 } 537 538 // AddRelation adds a relation between the specified endpoints and returns the relation info. 539 func (c *Client) AddRelation(args params.AddRelation) (params.AddRelationResults, error) { 540 if err := c.check.ChangeAllowed(); err != nil { 541 return params.AddRelationResults{}, errors.Trace(err) 542 } 543 inEps, err := c.api.state.InferEndpoints(args.Endpoints...) 544 if err != nil { 545 return params.AddRelationResults{}, err 546 } 547 rel, err := c.api.state.AddRelation(inEps...) 548 if err != nil { 549 return params.AddRelationResults{}, err 550 } 551 outEps := make(map[string]charm.Relation) 552 for _, inEp := range inEps { 553 outEp, err := rel.Endpoint(inEp.ServiceName) 554 if err != nil { 555 return params.AddRelationResults{}, err 556 } 557 outEps[inEp.ServiceName] = outEp.Relation 558 } 559 return params.AddRelationResults{Endpoints: outEps}, nil 560 } 561 562 // DestroyRelation removes the relation between the specified endpoints. 563 func (c *Client) DestroyRelation(args params.DestroyRelation) error { 564 if err := c.check.RemoveAllowed(); err != nil { 565 return errors.Trace(err) 566 } 567 eps, err := c.api.state.InferEndpoints(args.Endpoints...) 568 if err != nil { 569 return err 570 } 571 rel, err := c.api.state.EndpointsRelation(eps...) 572 if err != nil { 573 return err 574 } 575 return rel.Destroy() 576 } 577 578 // AddMachines adds new machines with the supplied parameters. 579 func (c *Client) AddMachines(args params.AddMachines) (params.AddMachinesResults, error) { 580 return c.AddMachinesV2(args) 581 } 582 583 // AddMachinesV2 adds new machines with the supplied parameters. 584 func (c *Client) AddMachinesV2(args params.AddMachines) (params.AddMachinesResults, error) { 585 results := params.AddMachinesResults{ 586 Machines: make([]params.AddMachinesResult, len(args.MachineParams)), 587 } 588 if err := c.check.ChangeAllowed(); err != nil { 589 return results, errors.Trace(err) 590 } 591 for i, p := range args.MachineParams { 592 m, err := c.addOneMachine(p) 593 results.Machines[i].Error = common.ServerError(err) 594 if err == nil { 595 results.Machines[i].Machine = m.Id() 596 } 597 } 598 return results, nil 599 } 600 601 // InjectMachines injects a machine into state with provisioned status. 602 func (c *Client) InjectMachines(args params.AddMachines) (params.AddMachinesResults, error) { 603 return c.AddMachines(args) 604 } 605 606 func (c *Client) addOneMachine(p params.AddMachineParams) (*state.Machine, error) { 607 if p.ParentId != "" && p.ContainerType == "" { 608 return nil, fmt.Errorf("parent machine specified without container type") 609 } 610 if p.ContainerType != "" && p.Placement != nil { 611 return nil, fmt.Errorf("container type and placement are mutually exclusive") 612 } 613 if p.Placement != nil { 614 // Extract container type and parent from container placement directives. 615 containerType, err := instance.ParseContainerType(p.Placement.Scope) 616 if err == nil { 617 p.ContainerType = containerType 618 p.ParentId = p.Placement.Directive 619 p.Placement = nil 620 } 621 } 622 623 if p.ContainerType != "" || p.Placement != nil { 624 // Guard against dubious client by making sure that 625 // the following attributes can only be set when we're 626 // not using placement. 627 p.InstanceId = "" 628 p.Nonce = "" 629 p.HardwareCharacteristics = instance.HardwareCharacteristics{} 630 p.Addrs = nil 631 } 632 633 if p.Series == "" { 634 conf, err := c.api.state.EnvironConfig() 635 if err != nil { 636 return nil, err 637 } 638 p.Series = config.PreferredSeries(conf) 639 } 640 641 var placementDirective string 642 if p.Placement != nil { 643 env, err := c.api.state.Environment() 644 if err != nil { 645 return nil, err 646 } 647 // For 1.21 we should support both UUID and name, and with 1.22 648 // just support UUID 649 if p.Placement.Scope != env.Name() && p.Placement.Scope != env.UUID() { 650 return nil, fmt.Errorf("invalid environment name %q", p.Placement.Scope) 651 } 652 placementDirective = p.Placement.Directive 653 } 654 655 jobs, err := common.StateJobs(p.Jobs) 656 if err != nil { 657 return nil, err 658 } 659 template := state.MachineTemplate{ 660 Series: p.Series, 661 Constraints: p.Constraints, 662 InstanceId: p.InstanceId, 663 Jobs: jobs, 664 Nonce: p.Nonce, 665 HardwareCharacteristics: p.HardwareCharacteristics, 666 Addresses: params.NetworkAddresses(p.Addrs), 667 Placement: placementDirective, 668 } 669 if p.ContainerType == "" { 670 return c.api.state.AddOneMachine(template) 671 } 672 if p.ParentId != "" { 673 return c.api.state.AddMachineInsideMachine(template, p.ParentId, p.ContainerType) 674 } 675 return c.api.state.AddMachineInsideNewMachine(template, template, p.ContainerType) 676 } 677 678 // ProvisioningScript returns a shell script that, when run, 679 // provisions a machine agent on the machine executing the script. 680 func (c *Client) ProvisioningScript(args params.ProvisioningScriptParams) (params.ProvisioningScriptResult, error) { 681 var result params.ProvisioningScriptResult 682 icfg, err := InstanceConfig(c.api.state, args.MachineId, args.Nonce, args.DataDir) 683 if err != nil { 684 return result, err 685 } 686 687 // Until DisablePackageCommands is retired, for backwards 688 // compatibility, we must respect the client's request and 689 // override any environment settings the user may have specified. 690 // If the client does specify this setting, it will only ever be 691 // true. False indicates the client doesn't care and we should use 692 // what's specified in the environments.yaml file. 693 if args.DisablePackageCommands { 694 icfg.EnableOSRefreshUpdate = false 695 icfg.EnableOSUpgrade = false 696 } else if cfg, err := c.api.state.EnvironConfig(); err != nil { 697 return result, err 698 } else { 699 icfg.EnableOSUpgrade = cfg.EnableOSUpgrade() 700 icfg.EnableOSRefreshUpdate = cfg.EnableOSRefreshUpdate() 701 } 702 703 result.Script, err = manual.ProvisioningScript(icfg) 704 return result, err 705 } 706 707 // DestroyMachines removes a given set of machines. 708 func (c *Client) DestroyMachines(args params.DestroyMachines) error { 709 var errs []string 710 for _, id := range args.MachineNames { 711 machine, err := c.api.state.Machine(id) 712 switch { 713 case errors.IsNotFound(err): 714 err = fmt.Errorf("machine %s does not exist", id) 715 case err != nil: 716 case args.Force: 717 err = machine.ForceDestroy() 718 case machine.Life() != state.Alive: 719 continue 720 default: 721 { 722 if err := c.check.RemoveAllowed(); err != nil { 723 return errors.Trace(err) 724 } 725 err = machine.Destroy() 726 } 727 } 728 if err != nil { 729 errs = append(errs, err.Error()) 730 } 731 } 732 return destroyErr("machines", args.MachineNames, errs) 733 } 734 735 // CharmInfo returns information about the requested charm. 736 func (c *Client) CharmInfo(args params.CharmInfo) (api.CharmInfo, error) { 737 curl, err := charm.ParseURL(args.CharmURL) 738 if err != nil { 739 return api.CharmInfo{}, err 740 } 741 charm, err := c.api.state.Charm(curl) 742 if err != nil { 743 return api.CharmInfo{}, err 744 } 745 info := api.CharmInfo{ 746 Revision: charm.Revision(), 747 URL: curl.String(), 748 Config: charm.Config(), 749 Meta: charm.Meta(), 750 Actions: charm.Actions(), 751 } 752 return info, nil 753 } 754 755 // EnvironmentInfo returns information about the current environment (default 756 // series and type). 757 func (c *Client) EnvironmentInfo() (api.EnvironmentInfo, error) { 758 state := c.api.state 759 conf, err := state.EnvironConfig() 760 if err != nil { 761 return api.EnvironmentInfo{}, err 762 } 763 env, err := state.Environment() 764 if err != nil { 765 return api.EnvironmentInfo{}, err 766 } 767 768 info := api.EnvironmentInfo{ 769 DefaultSeries: config.PreferredSeries(conf), 770 ProviderType: conf.Type(), 771 Name: conf.Name(), 772 UUID: env.UUID(), 773 ServerUUID: env.ServerUUID(), 774 } 775 return info, nil 776 } 777 778 // ShareEnvironment manages allowing and denying the given user(s) access to the environment. 779 func (c *Client) ShareEnvironment(args params.ModifyEnvironUsers) (result params.ErrorResults, err error) { 780 var createdBy names.UserTag 781 var ok bool 782 if createdBy, ok = c.api.auth.GetAuthTag().(names.UserTag); !ok { 783 return result, errors.Errorf("api connection is not through a user") 784 } 785 786 result = params.ErrorResults{ 787 Results: make([]params.ErrorResult, len(args.Changes)), 788 } 789 if len(args.Changes) == 0 { 790 return result, nil 791 } 792 793 for i, arg := range args.Changes { 794 userTagString := arg.UserTag 795 user, err := names.ParseUserTag(userTagString) 796 if err != nil { 797 result.Results[i].Error = common.ServerError(errors.Annotate(err, "could not share environment")) 798 continue 799 } 800 switch arg.Action { 801 case params.AddEnvUser: 802 _, err := c.api.state.AddEnvironmentUser(user, createdBy, "") 803 if err != nil { 804 err = errors.Annotate(err, "could not share environment") 805 result.Results[i].Error = common.ServerError(err) 806 } 807 case params.RemoveEnvUser: 808 err := c.api.state.RemoveEnvironmentUser(user) 809 if err != nil { 810 err = errors.Annotate(err, "could not unshare environment") 811 result.Results[i].Error = common.ServerError(err) 812 } 813 default: 814 result.Results[i].Error = common.ServerError(errors.Errorf("unknown action %q", arg.Action)) 815 } 816 } 817 return result, nil 818 } 819 820 // EnvUserInfo returns information on all users in the environment. 821 func (c *Client) EnvUserInfo() (params.EnvUserInfoResults, error) { 822 var results params.EnvUserInfoResults 823 env, err := c.api.state.Environment() 824 if err != nil { 825 return results, errors.Trace(err) 826 } 827 users, err := env.Users() 828 if err != nil { 829 return results, errors.Trace(err) 830 } 831 832 for _, user := range users { 833 var lastConn *time.Time 834 userLastConn, err := user.LastConnection() 835 if err != nil { 836 if !state.IsNeverConnectedError(err) { 837 return results, errors.Trace(err) 838 } 839 } else { 840 lastConn = &userLastConn 841 } 842 results.Results = append(results.Results, params.EnvUserInfoResult{ 843 Result: ¶ms.EnvUserInfo{ 844 UserName: user.UserName(), 845 DisplayName: user.DisplayName(), 846 CreatedBy: user.CreatedBy(), 847 DateCreated: user.DateCreated(), 848 LastConnection: lastConn, 849 }, 850 }) 851 } 852 return results, nil 853 } 854 855 // GetAnnotations returns annotations about a given entity. 856 // This API is now deprecated - "Annotations" client should be used instead. 857 // TODO(anastasiamac) remove for Juju 2.x 858 func (c *Client) GetAnnotations(args params.GetAnnotations) (params.GetAnnotationsResults, error) { 859 nothing := params.GetAnnotationsResults{} 860 tag, err := c.parseEntityTag(args.Tag) 861 if err != nil { 862 return nothing, errors.Trace(err) 863 } 864 entity, err := c.findEntity(tag) 865 if err != nil { 866 return nothing, errors.Trace(err) 867 } 868 ann, err := c.api.state.Annotations(entity) 869 if err != nil { 870 return nothing, errors.Trace(err) 871 } 872 return params.GetAnnotationsResults{Annotations: ann}, nil 873 } 874 875 func (c *Client) parseEntityTag(tag0 string) (names.Tag, error) { 876 tag, err := names.ParseTag(tag0) 877 if err != nil { 878 return nil, errors.Trace(err) 879 } 880 if tag.Kind() == names.CharmTagKind { 881 return nil, common.NotSupportedError(tag, "client.annotations") 882 } 883 return tag, nil 884 } 885 886 func (c *Client) findEntity(tag names.Tag) (state.GlobalEntity, error) { 887 entity0, err := c.api.state.FindEntity(tag) 888 if err != nil { 889 return nil, err 890 } 891 entity, ok := entity0.(state.GlobalEntity) 892 if !ok { 893 return nil, common.NotSupportedError(tag, "annotations") 894 } 895 return entity, nil 896 } 897 898 // SetAnnotations stores annotations about a given entity. 899 // This API is now deprecated - "Annotations" client should be used instead. 900 // TODO(anastasiamac) remove for Juju 2.x 901 func (c *Client) SetAnnotations(args params.SetAnnotations) error { 902 tag, err := c.parseEntityTag(args.Tag) 903 if err != nil { 904 return errors.Trace(err) 905 } 906 entity, err := c.findEntity(tag) 907 if err != nil { 908 return errors.Trace(err) 909 } 910 return c.api.state.SetAnnotations(entity, args.Pairs) 911 } 912 913 // AgentVersion returns the current version that the API server is running. 914 func (c *Client) AgentVersion() (params.AgentVersionResult, error) { 915 return params.AgentVersionResult{Version: version.Current.Number}, nil 916 } 917 918 // EnvironmentGet implements the server-side part of the 919 // get-environment CLI command. 920 func (c *Client) EnvironmentGet() (params.EnvironmentConfigResults, error) { 921 result := params.EnvironmentConfigResults{} 922 // Get the existing environment config from the state. 923 config, err := c.api.state.EnvironConfig() 924 if err != nil { 925 return result, err 926 } 927 result.Config = config.AllAttrs() 928 return result, nil 929 } 930 931 // EnvironmentSet implements the server-side part of the 932 // set-environment CLI command. 933 func (c *Client) EnvironmentSet(args params.EnvironmentSet) error { 934 if err := c.check.ChangeAllowed(); err != nil { 935 return errors.Trace(err) 936 } 937 // Make sure we don't allow changing agent-version. 938 checkAgentVersion := func(updateAttrs map[string]interface{}, removeAttrs []string, oldConfig *config.Config) error { 939 if v, found := updateAttrs["agent-version"]; found { 940 oldVersion, _ := oldConfig.AgentVersion() 941 if v != oldVersion.String() { 942 return fmt.Errorf("agent-version cannot be changed") 943 } 944 } 945 return nil 946 } 947 // Replace any deprecated attributes with their new values. 948 attrs := config.ProcessDeprecatedAttributes(args.Config) 949 // TODO(waigani) 2014-3-11 #1167616 950 // Add a txn retry loop to ensure that the settings on disk have not 951 // changed underneath us. 952 return c.api.state.UpdateEnvironConfig(attrs, nil, checkAgentVersion) 953 } 954 955 // EnvironmentUnset implements the server-side part of the 956 // set-environment CLI command. 957 func (c *Client) EnvironmentUnset(args params.EnvironmentUnset) error { 958 if err := c.check.ChangeAllowed(); err != nil { 959 return errors.Trace(err) 960 } 961 // TODO(waigani) 2014-3-11 #1167616 962 // Add a txn retry loop to ensure that the settings on disk have not 963 // changed underneath us. 964 return c.api.state.UpdateEnvironConfig(nil, args.Keys, nil) 965 } 966 967 // SetEnvironAgentVersion sets the environment agent version. 968 func (c *Client) SetEnvironAgentVersion(args params.SetEnvironAgentVersion) error { 969 if err := c.check.ChangeAllowed(); err != nil { 970 return errors.Trace(err) 971 } 972 return c.api.state.SetEnvironAgentVersion(args.Version) 973 } 974 975 // AbortCurrentUpgrade aborts and archives the current upgrade 976 // synchronisation record, if any. 977 func (c *Client) AbortCurrentUpgrade() error { 978 if err := c.check.ChangeAllowed(); err != nil { 979 return errors.Trace(err) 980 } 981 return c.api.state.AbortCurrentUpgrade() 982 } 983 984 // FindTools returns a List containing all tools matching the given parameters. 985 func (c *Client) FindTools(args params.FindToolsParams) (params.FindToolsResult, error) { 986 return c.api.toolsFinder.FindTools(args) 987 } 988 989 func destroyErr(desc string, ids, errs []string) error { 990 if len(errs) == 0 { 991 return nil 992 } 993 msg := "some %s were not destroyed" 994 if len(errs) == len(ids) { 995 msg = "no %s were destroyed" 996 } 997 msg = fmt.Sprintf(msg, desc) 998 return fmt.Errorf("%s: %s", msg, strings.Join(errs, "; ")) 999 } 1000 1001 func (c *Client) AddCharm(args params.CharmURL) error { 1002 return service.AddCharmWithAuthorization(c.api.state, params.AddCharmWithAuthorization{ 1003 URL: args.URL, 1004 }) 1005 } 1006 1007 // AddCharmWithAuthorization adds the given charm URL (which must include revision) to 1008 // the environment, if it does not exist yet. Local charms are not 1009 // supported, only charm store URLs. See also AddLocalCharm(). 1010 // 1011 // The authorization macaroon, args.CharmStoreMacaroon, may be 1012 // omitted, in which case this call is equivalent to AddCharm. 1013 func (c *Client) AddCharmWithAuthorization(args params.AddCharmWithAuthorization) error { 1014 return service.AddCharmWithAuthorization(c.api.state, args) 1015 } 1016 1017 // ResolveCharm resolves the best available charm URLs with series, for charm 1018 // locations without a series specified. 1019 func (c *Client) ResolveCharms(args params.ResolveCharms) (params.ResolveCharmResults, error) { 1020 return service.ResolveCharms(c.api.state, args) 1021 } 1022 1023 // RetryProvisioning marks a provisioning error as transient on the machines. 1024 func (c *Client) RetryProvisioning(p params.Entities) (params.ErrorResults, error) { 1025 if err := c.check.ChangeAllowed(); err != nil { 1026 return params.ErrorResults{}, errors.Trace(err) 1027 } 1028 entityStatus := make([]params.EntityStatusArgs, len(p.Entities)) 1029 for i, entity := range p.Entities { 1030 entityStatus[i] = params.EntityStatusArgs{Tag: entity.Tag, Data: map[string]interface{}{"transient": true}} 1031 } 1032 return c.api.statusSetter.UpdateStatus(params.SetStatus{ 1033 Entities: entityStatus, 1034 }) 1035 } 1036 1037 // APIHostPorts returns the API host/port addresses stored in state. 1038 func (c *Client) APIHostPorts() (result params.APIHostPortsResult, err error) { 1039 var servers [][]network.HostPort 1040 if servers, err = c.api.state.APIHostPorts(); err != nil { 1041 return params.APIHostPortsResult{}, err 1042 } 1043 result.Servers = params.FromNetworkHostsPorts(servers) 1044 return result, nil 1045 } 1046 1047 // EnsureAvailability ensures the availability of Juju state servers. 1048 // DEPRECATED: remove when we stop supporting 1.20 and earlier clients. 1049 // This API is now on the HighAvailability facade. 1050 func (c *Client) EnsureAvailability(args params.StateServersSpecs) (params.StateServersChangeResults, error) { 1051 if err := c.check.ChangeAllowed(); err != nil { 1052 return params.StateServersChangeResults{}, errors.Trace(err) 1053 } 1054 results := params.StateServersChangeResults{Results: make([]params.StateServersChangeResult, len(args.Specs))} 1055 for i, stateServersSpec := range args.Specs { 1056 result, err := highavailability.EnsureAvailabilitySingle(c.api.state, stateServersSpec) 1057 results.Results[i].Result = result 1058 results.Results[i].Error = common.ServerError(err) 1059 } 1060 return results, nil 1061 } 1062 1063 // DestroyEnvironment will try to destroy the current environment. 1064 // If there is a block on destruction, this method will return an error. 1065 func (c *Client) DestroyEnvironment() (err error) { 1066 if err := c.check.DestroyAllowed(); err != nil { 1067 return errors.Trace(err) 1068 } 1069 1070 environTag := c.api.state.EnvironTag() 1071 return errors.Trace(common.DestroyEnvironment(c.api.state, environTag)) 1072 }