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