github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/api/client.go (about) 1 // Copyright 2013, 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package api 5 6 import ( 7 "bytes" 8 "crypto/tls" 9 "encoding/json" 10 "fmt" 11 "io" 12 "io/ioutil" 13 "net/http" 14 "net/url" 15 "os" 16 "path" 17 "strings" 18 "time" 19 20 "github.com/juju/errors" 21 "github.com/juju/loggo" 22 "github.com/juju/names" 23 "github.com/juju/utils" 24 "golang.org/x/net/websocket" 25 "gopkg.in/juju/charm.v5" 26 "gopkg.in/macaroon.v1" 27 28 "github.com/juju/juju/api/base" 29 "github.com/juju/juju/apiserver/params" 30 "github.com/juju/juju/constraints" 31 "github.com/juju/juju/instance" 32 "github.com/juju/juju/network" 33 "github.com/juju/juju/state/multiwatcher" 34 "github.com/juju/juju/tools" 35 "github.com/juju/juju/version" 36 ) 37 38 // Client represents the client-accessible part of the state. 39 type Client struct { 40 base.ClientFacade 41 facade base.FacadeCaller 42 st *State 43 } 44 45 // NetworksSpecification holds the enabled and disabled networks for a 46 // service. 47 type NetworksSpecification struct { 48 Enabled []string 49 Disabled []string 50 } 51 52 // AgentStatus holds status info about a machine or unit agent. 53 type AgentStatus struct { 54 Status params.Status 55 Info string 56 Data map[string]interface{} 57 Since *time.Time 58 Kind params.HistoryKind 59 Version string 60 Life string 61 Err error 62 } 63 64 // MachineStatus holds status info about a machine. 65 type MachineStatus struct { 66 Agent AgentStatus 67 68 // The following fields mirror fields in AgentStatus (introduced 69 // in 1.19.x). The old fields below are being kept for 70 // compatibility with old clients. 71 // They can be removed once API versioning lands. 72 AgentState params.Status 73 AgentStateInfo string 74 AgentVersion string 75 Life string 76 Err error 77 78 DNSName string 79 InstanceId instance.Id 80 InstanceState string 81 Series string 82 Id string 83 Containers map[string]MachineStatus 84 Hardware string 85 Jobs []multiwatcher.MachineJob 86 HasVote bool 87 WantsVote bool 88 } 89 90 // ServiceStatus holds status info about a service. 91 type ServiceStatus struct { 92 Err error 93 Charm string 94 Exposed bool 95 Life string 96 Relations map[string][]string 97 Networks NetworksSpecification 98 CanUpgradeTo string 99 SubordinateTo []string 100 Units map[string]UnitStatus 101 Status AgentStatus 102 } 103 104 // UnitStatusHistory holds a slice of statuses. 105 type UnitStatusHistory struct { 106 Statuses []AgentStatus 107 } 108 109 // UnitStatus holds status info about a unit. 110 type UnitStatus struct { 111 // UnitAgent holds the status for a unit's agent. 112 UnitAgent AgentStatus 113 114 // Workload holds the status for a unit's workload 115 Workload AgentStatus 116 117 // Until Juju 2.0, we need to continue to return legacy agent state values 118 // as top level struct attributes when the "FullStatus" API is called. 119 AgentState params.Status 120 AgentStateInfo string 121 AgentVersion string 122 Life string 123 Err error 124 125 Machine string 126 OpenedPorts []string 127 PublicAddress string 128 Charm string 129 Subordinates map[string]UnitStatus 130 } 131 132 // RelationStatus holds status info about a relation. 133 type RelationStatus struct { 134 Id int 135 Key string 136 Interface string 137 Scope charm.RelationScope 138 Endpoints []EndpointStatus 139 } 140 141 // EndpointStatus holds status info about a single endpoint 142 type EndpointStatus struct { 143 ServiceName string 144 Name string 145 Role charm.RelationRole 146 Subordinate bool 147 } 148 149 func (epStatus *EndpointStatus) String() string { 150 return epStatus.ServiceName + ":" + epStatus.Name 151 } 152 153 // NetworkStatus holds status info about a network. 154 type NetworkStatus struct { 155 Err error 156 ProviderId network.Id 157 CIDR string 158 VLANTag int 159 } 160 161 // Status holds information about the status of a juju environment. 162 type Status struct { 163 EnvironmentName string 164 Machines map[string]MachineStatus 165 Services map[string]ServiceStatus 166 Networks map[string]NetworkStatus 167 Relations []RelationStatus 168 } 169 170 // Status returns the status of the juju environment. 171 func (c *Client) Status(patterns []string) (*Status, error) { 172 var result Status 173 p := params.StatusParams{Patterns: patterns} 174 if err := c.facade.FacadeCall("FullStatus", p, &result); err != nil { 175 return nil, err 176 } 177 return &result, nil 178 } 179 180 // UnitStatusHistory retrieves the last <size> results of <kind:combined|agent|workload> status 181 // for <unitName> unit 182 func (c *Client) UnitStatusHistory(kind params.HistoryKind, unitName string, size int) (*UnitStatusHistory, error) { 183 var results UnitStatusHistory 184 args := params.StatusHistory{ 185 Kind: kind, 186 Size: size, 187 Name: unitName, 188 } 189 err := c.facade.FacadeCall("UnitStatusHistory", args, &results) 190 if err != nil { 191 if params.IsCodeNotImplemented(err) { 192 return &UnitStatusHistory{}, errors.NotImplementedf("UnitStatusHistory") 193 } 194 return &UnitStatusHistory{}, errors.Trace(err) 195 } 196 return &results, nil 197 } 198 199 // LegacyMachineStatus holds just the instance-id of a machine. 200 type LegacyMachineStatus struct { 201 InstanceId string // Not type instance.Id just to match original api. 202 } 203 204 // LegacyStatus holds minimal information on the status of a juju environment. 205 type LegacyStatus struct { 206 Machines map[string]LegacyMachineStatus 207 } 208 209 // LegacyStatus is a stub version of Status that 1.16 introduced. Should be 210 // removed along with structs when api versioning makes it safe to do so. 211 func (c *Client) LegacyStatus() (*LegacyStatus, error) { 212 var result LegacyStatus 213 if err := c.facade.FacadeCall("Status", nil, &result); err != nil { 214 return nil, err 215 } 216 return &result, nil 217 } 218 219 // ServiceSet sets configuration options on a service. 220 func (c *Client) ServiceSet(service string, options map[string]string) error { 221 p := params.ServiceSet{ 222 ServiceName: service, 223 Options: options, 224 } 225 // TODO(Nate): Put this back to ServiceSet when the GUI stops expecting 226 // ServiceSet to unset values set to an empty string. 227 return c.facade.FacadeCall("NewServiceSetForClientAPI", p, nil) 228 } 229 230 // ServiceUnset resets configuration options on a service. 231 func (c *Client) ServiceUnset(service string, options []string) error { 232 p := params.ServiceUnset{ 233 ServiceName: service, 234 Options: options, 235 } 236 return c.facade.FacadeCall("ServiceUnset", p, nil) 237 } 238 239 // Resolved clears errors on a unit. 240 func (c *Client) Resolved(unit string, retry bool) error { 241 p := params.Resolved{ 242 UnitName: unit, 243 Retry: retry, 244 } 245 return c.facade.FacadeCall("Resolved", p, nil) 246 } 247 248 // RetryProvisioning updates the provisioning status of a machine allowing the 249 // provisioner to retry. 250 func (c *Client) RetryProvisioning(machines ...names.MachineTag) ([]params.ErrorResult, error) { 251 p := params.Entities{} 252 p.Entities = make([]params.Entity, len(machines)) 253 for i, machine := range machines { 254 p.Entities[i] = params.Entity{Tag: machine.String()} 255 } 256 var results params.ErrorResults 257 err := c.facade.FacadeCall("RetryProvisioning", p, &results) 258 return results.Results, err 259 } 260 261 // PublicAddress returns the public address of the specified 262 // machine or unit. For a machine, target is an id not a tag. 263 func (c *Client) PublicAddress(target string) (string, error) { 264 var results params.PublicAddressResults 265 p := params.PublicAddress{Target: target} 266 err := c.facade.FacadeCall("PublicAddress", p, &results) 267 return results.PublicAddress, err 268 } 269 270 // PrivateAddress returns the private address of the specified 271 // machine or unit. 272 func (c *Client) PrivateAddress(target string) (string, error) { 273 var results params.PrivateAddressResults 274 p := params.PrivateAddress{Target: target} 275 err := c.facade.FacadeCall("PrivateAddress", p, &results) 276 return results.PrivateAddress, err 277 } 278 279 // ServiceSetYAML sets configuration options on a service 280 // given options in YAML format. 281 func (c *Client) ServiceSetYAML(service string, yaml string) error { 282 p := params.ServiceSetYAML{ 283 ServiceName: service, 284 Config: yaml, 285 } 286 return c.facade.FacadeCall("ServiceSetYAML", p, nil) 287 } 288 289 // ServiceGet returns the configuration for the named service. 290 func (c *Client) ServiceGet(service string) (*params.ServiceGetResults, error) { 291 var results params.ServiceGetResults 292 params := params.ServiceGet{ServiceName: service} 293 err := c.facade.FacadeCall("ServiceGet", params, &results) 294 return &results, err 295 } 296 297 // AddRelation adds a relation between the specified endpoints and returns the relation info. 298 func (c *Client) AddRelation(endpoints ...string) (*params.AddRelationResults, error) { 299 var addRelRes params.AddRelationResults 300 params := params.AddRelation{Endpoints: endpoints} 301 err := c.facade.FacadeCall("AddRelation", params, &addRelRes) 302 return &addRelRes, err 303 } 304 305 // DestroyRelation removes the relation between the specified endpoints. 306 func (c *Client) DestroyRelation(endpoints ...string) error { 307 params := params.DestroyRelation{Endpoints: endpoints} 308 return c.facade.FacadeCall("DestroyRelation", params, nil) 309 } 310 311 // ServiceCharmRelations returns the service's charms relation names. 312 func (c *Client) ServiceCharmRelations(service string) ([]string, error) { 313 var results params.ServiceCharmRelationsResults 314 params := params.ServiceCharmRelations{ServiceName: service} 315 err := c.facade.FacadeCall("ServiceCharmRelations", params, &results) 316 return results.CharmRelations, err 317 } 318 319 // AddMachines1dot18 adds new machines with the supplied parameters. 320 // 321 // TODO(axw) 2014-04-11 #XXX 322 // This exists for backwards compatibility; 323 // We cannot remove this code while clients > 1.20 need to talk to 1.18 324 // servers (which is something we need for an undetermined amount of time). 325 func (c *Client) AddMachines1dot18(machineParams []params.AddMachineParams) ([]params.AddMachinesResult, error) { 326 args := params.AddMachines{ 327 MachineParams: machineParams, 328 } 329 results := new(params.AddMachinesResults) 330 err := c.facade.FacadeCall("AddMachines", args, results) 331 return results.Machines, err 332 } 333 334 // AddMachines adds new machines with the supplied parameters. 335 func (c *Client) AddMachines(machineParams []params.AddMachineParams) ([]params.AddMachinesResult, error) { 336 args := params.AddMachines{ 337 MachineParams: machineParams, 338 } 339 results := new(params.AddMachinesResults) 340 err := c.facade.FacadeCall("AddMachinesV2", args, results) 341 return results.Machines, err 342 } 343 344 // ProvisioningScript returns a shell script that, when run, 345 // provisions a machine agent on the machine executing the script. 346 func (c *Client) ProvisioningScript(args params.ProvisioningScriptParams) (script string, err error) { 347 var result params.ProvisioningScriptResult 348 if err = c.facade.FacadeCall("ProvisioningScript", args, &result); err != nil { 349 return "", err 350 } 351 return result.Script, nil 352 } 353 354 // DestroyMachines removes a given set of machines. 355 func (c *Client) DestroyMachines(machines ...string) error { 356 params := params.DestroyMachines{MachineNames: machines} 357 return c.facade.FacadeCall("DestroyMachines", params, nil) 358 } 359 360 // ForceDestroyMachines removes a given set of machines and all associated units. 361 func (c *Client) ForceDestroyMachines(machines ...string) error { 362 params := params.DestroyMachines{Force: true, MachineNames: machines} 363 return c.facade.FacadeCall("DestroyMachines", params, nil) 364 } 365 366 // ServiceExpose changes the juju-managed firewall to expose any ports that 367 // were also explicitly marked by units as open. 368 func (c *Client) ServiceExpose(service string) error { 369 params := params.ServiceExpose{ServiceName: service} 370 return c.facade.FacadeCall("ServiceExpose", params, nil) 371 } 372 373 // ServiceUnexpose changes the juju-managed firewall to unexpose any ports that 374 // were also explicitly marked by units as open. 375 func (c *Client) ServiceUnexpose(service string) error { 376 params := params.ServiceUnexpose{ServiceName: service} 377 return c.facade.FacadeCall("ServiceUnexpose", params, nil) 378 } 379 380 // ServiceDeployWithNetworks works exactly like ServiceDeploy, but 381 // allows the specification of requested networks that must be present 382 // on the machines where the service is deployed. Another way to specify 383 // networks to include/exclude is using constraints. 384 func (c *Client) ServiceDeployWithNetworks( 385 charmURL string, 386 serviceName string, 387 numUnits int, 388 configYAML string, 389 cons constraints.Value, 390 toMachineSpec string, 391 networks []string, 392 ) error { 393 params := params.ServiceDeploy{ 394 ServiceName: serviceName, 395 CharmUrl: charmURL, 396 NumUnits: numUnits, 397 ConfigYAML: configYAML, 398 Constraints: cons, 399 ToMachineSpec: toMachineSpec, 400 Networks: networks, 401 } 402 return c.facade.FacadeCall("ServiceDeployWithNetworks", params, nil) 403 } 404 405 // ServiceDeploy obtains the charm, either locally or from the charm store, 406 // and deploys it. 407 func (c *Client) ServiceDeploy(charmURL string, serviceName string, numUnits int, configYAML string, cons constraints.Value, toMachineSpec string) error { 408 params := params.ServiceDeploy{ 409 ServiceName: serviceName, 410 CharmUrl: charmURL, 411 NumUnits: numUnits, 412 ConfigYAML: configYAML, 413 Constraints: cons, 414 ToMachineSpec: toMachineSpec, 415 } 416 return c.facade.FacadeCall("ServiceDeploy", params, nil) 417 } 418 419 // ServiceUpdate updates the service attributes, including charm URL, 420 // minimum number of units, settings and constraints. 421 // TODO(frankban) deprecate redundant API calls that this supercedes. 422 func (c *Client) ServiceUpdate(args params.ServiceUpdate) error { 423 return c.facade.FacadeCall("ServiceUpdate", args, nil) 424 } 425 426 // ServiceSetCharm sets the charm for a given service. 427 func (c *Client) ServiceSetCharm(serviceName string, charmUrl string, force bool) error { 428 args := params.ServiceSetCharm{ 429 ServiceName: serviceName, 430 CharmUrl: charmUrl, 431 Force: force, 432 } 433 return c.facade.FacadeCall("ServiceSetCharm", args, nil) 434 } 435 436 // ServiceGetCharmURL returns the charm URL the given service is 437 // running at present. 438 func (c *Client) ServiceGetCharmURL(serviceName string) (*charm.URL, error) { 439 result := new(params.StringResult) 440 args := params.ServiceGet{ServiceName: serviceName} 441 err := c.facade.FacadeCall("ServiceGetCharmURL", args, &result) 442 if err != nil { 443 return nil, err 444 } 445 return charm.ParseURL(result.Result) 446 } 447 448 // AddServiceUnits adds a given number of units to a service. 449 func (c *Client) AddServiceUnits(service string, numUnits int, machineSpec string) ([]string, error) { 450 args := params.AddServiceUnits{ 451 ServiceName: service, 452 NumUnits: numUnits, 453 ToMachineSpec: machineSpec, 454 } 455 results := new(params.AddServiceUnitsResults) 456 err := c.facade.FacadeCall("AddServiceUnits", args, results) 457 return results.Units, err 458 } 459 460 // DestroyServiceUnits decreases the number of units dedicated to a service. 461 func (c *Client) DestroyServiceUnits(unitNames ...string) error { 462 params := params.DestroyServiceUnits{unitNames} 463 return c.facade.FacadeCall("DestroyServiceUnits", params, nil) 464 } 465 466 // ServiceDestroy destroys a given service. 467 func (c *Client) ServiceDestroy(service string) error { 468 params := params.ServiceDestroy{ 469 ServiceName: service, 470 } 471 return c.facade.FacadeCall("ServiceDestroy", params, nil) 472 } 473 474 // GetServiceConstraints returns the constraints for the given service. 475 func (c *Client) GetServiceConstraints(service string) (constraints.Value, error) { 476 results := new(params.GetConstraintsResults) 477 err := c.facade.FacadeCall("GetServiceConstraints", params.GetServiceConstraints{service}, results) 478 return results.Constraints, err 479 } 480 481 // GetEnvironmentConstraints returns the constraints for the environment. 482 func (c *Client) GetEnvironmentConstraints() (constraints.Value, error) { 483 results := new(params.GetConstraintsResults) 484 err := c.facade.FacadeCall("GetEnvironmentConstraints", nil, results) 485 return results.Constraints, err 486 } 487 488 // SetServiceConstraints specifies the constraints for the given service. 489 func (c *Client) SetServiceConstraints(service string, constraints constraints.Value) error { 490 params := params.SetConstraints{ 491 ServiceName: service, 492 Constraints: constraints, 493 } 494 return c.facade.FacadeCall("SetServiceConstraints", params, nil) 495 } 496 497 // SetEnvironmentConstraints specifies the constraints for the environment. 498 func (c *Client) SetEnvironmentConstraints(constraints constraints.Value) error { 499 params := params.SetConstraints{ 500 Constraints: constraints, 501 } 502 return c.facade.FacadeCall("SetEnvironmentConstraints", params, nil) 503 } 504 505 // CharmInfo holds information about a charm. 506 type CharmInfo struct { 507 Revision int 508 URL string 509 Config *charm.Config 510 Meta *charm.Meta 511 Actions *charm.Actions 512 } 513 514 // CharmInfo returns information about the requested charm. 515 func (c *Client) CharmInfo(charmURL string) (*CharmInfo, error) { 516 args := params.CharmInfo{CharmURL: charmURL} 517 info := new(CharmInfo) 518 if err := c.facade.FacadeCall("CharmInfo", args, info); err != nil { 519 return nil, err 520 } 521 return info, nil 522 } 523 524 // EnvironmentInfo holds information about the Juju environment. 525 type EnvironmentInfo struct { 526 DefaultSeries string 527 ProviderType string 528 Name string 529 UUID string 530 ServerUUID string 531 } 532 533 // EnvironmentInfo returns details about the Juju environment. 534 func (c *Client) EnvironmentInfo() (*EnvironmentInfo, error) { 535 info := new(EnvironmentInfo) 536 err := c.facade.FacadeCall("EnvironmentInfo", nil, info) 537 return info, err 538 } 539 540 // EnvironmentUUID returns the environment UUID from the client connection. 541 func (c *Client) EnvironmentUUID() string { 542 tag, err := c.st.EnvironTag() 543 if err != nil { 544 logger.Warningf("environ tag not an environ: %v", err) 545 return "" 546 } 547 return tag.Id() 548 } 549 550 // ShareEnvironment allows the given users access to the environment. 551 func (c *Client) ShareEnvironment(users ...names.UserTag) error { 552 var args params.ModifyEnvironUsers 553 for _, user := range users { 554 if &user != nil { 555 args.Changes = append(args.Changes, params.ModifyEnvironUser{ 556 UserTag: user.String(), 557 Action: params.AddEnvUser, 558 }) 559 } 560 } 561 562 var result params.ErrorResults 563 err := c.facade.FacadeCall("ShareEnvironment", args, &result) 564 if err != nil { 565 return errors.Trace(err) 566 } 567 568 for i, r := range result.Results { 569 if r.Error != nil && r.Error.Code == params.CodeAlreadyExists { 570 logger.Warningf("environment is already shared with %s", users[i].Username()) 571 result.Results[i].Error = nil 572 } 573 } 574 return result.Combine() 575 } 576 577 // EnvironmentUserInfo returns information on all users in the environment. 578 func (c *Client) EnvironmentUserInfo() ([]params.EnvUserInfo, error) { 579 var results params.EnvUserInfoResults 580 err := c.facade.FacadeCall("EnvUserInfo", nil, &results) 581 if err != nil { 582 return nil, errors.Trace(err) 583 } 584 585 info := []params.EnvUserInfo{} 586 for i, result := range results.Results { 587 if result.Result == nil { 588 return nil, errors.Errorf("unexpected nil result at position %d", i) 589 } 590 info = append(info, *result.Result) 591 } 592 return info, nil 593 } 594 595 // UnshareEnvironment removes access to the environment for the given users. 596 func (c *Client) UnshareEnvironment(users ...names.UserTag) error { 597 var args params.ModifyEnvironUsers 598 for _, user := range users { 599 if &user != nil { 600 args.Changes = append(args.Changes, params.ModifyEnvironUser{ 601 UserTag: user.String(), 602 Action: params.RemoveEnvUser, 603 }) 604 } 605 } 606 607 var result params.ErrorResults 608 err := c.facade.FacadeCall("ShareEnvironment", args, &result) 609 if err != nil { 610 return errors.Trace(err) 611 } 612 613 for i, r := range result.Results { 614 if r.Error != nil && r.Error.Code == params.CodeNotFound { 615 logger.Warningf("environment was not previously shared with user %s", users[i].Username()) 616 result.Results[i].Error = nil 617 } 618 } 619 return result.Combine() 620 } 621 622 // WatchAll holds the id of the newly-created AllWatcher. 623 type WatchAll struct { 624 AllWatcherId string 625 } 626 627 // WatchAll returns an AllWatcher, from which you can request the Next 628 // collection of Deltas. 629 func (c *Client) WatchAll() (*AllWatcher, error) { 630 info := new(WatchAll) 631 if err := c.facade.FacadeCall("WatchAll", nil, info); err != nil { 632 return nil, err 633 } 634 return newAllWatcher(c.st, &info.AllWatcherId), nil 635 } 636 637 // GetAnnotations returns annotations that have been set on the given entity. 638 // This API is now deprecated - "Annotations" client should be used instead. 639 // TODO(anastasiamac) remove for Juju 2.x 640 func (c *Client) GetAnnotations(tag string) (map[string]string, error) { 641 args := params.GetAnnotations{tag} 642 ann := new(params.GetAnnotationsResults) 643 err := c.facade.FacadeCall("GetAnnotations", args, ann) 644 return ann.Annotations, err 645 } 646 647 // SetAnnotations sets the annotation pairs on the given entity. 648 // Currently annotations are supported on machines, services, 649 // units and the environment itself. 650 // This API is now deprecated - "Annotations" client should be used instead. 651 // TODO(anastasiamac) remove for Juju 2.x 652 func (c *Client) SetAnnotations(tag string, pairs map[string]string) error { 653 args := params.SetAnnotations{tag, pairs} 654 return c.facade.FacadeCall("SetAnnotations", args, nil) 655 } 656 657 // Close closes the Client's underlying State connection 658 // Client is unique among the api.State facades in closing its own State 659 // connection, but it is conventional to use a Client object without any access 660 // to its underlying state connection. 661 func (c *Client) Close() error { 662 return c.st.Close() 663 } 664 665 // EnvironmentGet returns all environment settings. 666 func (c *Client) EnvironmentGet() (map[string]interface{}, error) { 667 result := params.EnvironmentConfigResults{} 668 err := c.facade.FacadeCall("EnvironmentGet", nil, &result) 669 return result.Config, err 670 } 671 672 // EnvironmentSet sets the given key-value pairs in the environment. 673 func (c *Client) EnvironmentSet(config map[string]interface{}) error { 674 args := params.EnvironmentSet{Config: config} 675 return c.facade.FacadeCall("EnvironmentSet", args, nil) 676 } 677 678 // EnvironmentUnset sets the given key-value pairs in the environment. 679 func (c *Client) EnvironmentUnset(keys ...string) error { 680 args := params.EnvironmentUnset{Keys: keys} 681 return c.facade.FacadeCall("EnvironmentUnset", args, nil) 682 } 683 684 // SetEnvironAgentVersion sets the environment agent-version setting 685 // to the given value. 686 func (c *Client) SetEnvironAgentVersion(version version.Number) error { 687 args := params.SetEnvironAgentVersion{Version: version} 688 return c.facade.FacadeCall("SetEnvironAgentVersion", args, nil) 689 } 690 691 // AbortCurrentUpgrade aborts and archives the current upgrade 692 // synchronisation record, if any. 693 func (c *Client) AbortCurrentUpgrade() error { 694 return c.facade.FacadeCall("AbortCurrentUpgrade", nil, nil) 695 } 696 697 // FindTools returns a List containing all tools matching the specified parameters. 698 func (c *Client) FindTools( 699 majorVersion, minorVersion int, 700 series, arch string, 701 ) (result params.FindToolsResult, err error) { 702 args := params.FindToolsParams{ 703 MajorVersion: majorVersion, 704 MinorVersion: minorVersion, 705 Arch: arch, 706 Series: series, 707 } 708 err = c.facade.FacadeCall("FindTools", args, &result) 709 return result, err 710 } 711 712 // RunOnAllMachines runs the command on all the machines with the specified 713 // timeout. 714 func (c *Client) RunOnAllMachines(commands string, timeout time.Duration) ([]params.RunResult, error) { 715 var results params.RunResults 716 args := params.RunParams{Commands: commands, Timeout: timeout} 717 err := c.facade.FacadeCall("RunOnAllMachines", args, &results) 718 return results.Results, err 719 } 720 721 // Run the Commands specified on the machines identified through the ids 722 // provided in the machines, services and units slices. 723 func (c *Client) Run(run params.RunParams) ([]params.RunResult, error) { 724 var results params.RunResults 725 err := c.facade.FacadeCall("Run", run, &results) 726 return results.Results, err 727 } 728 729 // DestroyEnvironment puts the environment into a "dying" state, 730 // and removes all non-manager machine instances. DestroyEnvironment 731 // will fail if there are any manually-provisioned non-manager machines 732 // in state. 733 func (c *Client) DestroyEnvironment() error { 734 return c.facade.FacadeCall("DestroyEnvironment", nil, nil) 735 } 736 737 // AddLocalCharm prepares the given charm with a local: schema in its 738 // URL, and uploads it via the API server, returning the assigned 739 // charm URL. If the API server does not support charm uploads, an 740 // error satisfying params.IsCodeNotImplemented() is returned. 741 func (c *Client) AddLocalCharm(curl *charm.URL, ch charm.Charm) (*charm.URL, error) { 742 if curl.Schema != "local" { 743 return nil, errors.Errorf("expected charm URL with local: schema, got %q", curl.String()) 744 } 745 // Package the charm for uploading. 746 var archive *os.File 747 switch ch := ch.(type) { 748 case *charm.CharmDir: 749 var err error 750 if archive, err = ioutil.TempFile("", "charm"); err != nil { 751 return nil, errors.Annotate(err, "cannot create temp file") 752 } 753 defer os.Remove(archive.Name()) 754 defer archive.Close() 755 if err := ch.ArchiveTo(archive); err != nil { 756 return nil, errors.Annotate(err, "cannot repackage charm") 757 } 758 if _, err := archive.Seek(0, 0); err != nil { 759 return nil, errors.Annotate(err, "cannot rewind packaged charm") 760 } 761 case *charm.CharmArchive: 762 var err error 763 if archive, err = os.Open(ch.Path); err != nil { 764 return nil, errors.Annotate(err, "cannot read charm archive") 765 } 766 defer archive.Close() 767 default: 768 return nil, errors.Errorf("unknown charm type %T", ch) 769 } 770 771 endPoint, err := c.apiEndpoint("charms", "series="+curl.Series) 772 if err != nil { 773 return nil, errors.Trace(err) 774 } 775 776 // wrap archive in a noopCloser to prevent the underlying transport closing 777 // the request body. This is neccessary to prevent a data race on the underlying 778 // *os.File as the http transport _may_ issue Close once the body is sent, or it 779 // may not if there is an error. 780 noop := &noopCloser{archive} 781 req, err := http.NewRequest("POST", endPoint, noop) 782 if err != nil { 783 return nil, errors.Annotate(err, "cannot create upload request") 784 } 785 req.SetBasicAuth(c.st.tag, c.st.password) 786 req.Header.Set("Content-Type", "application/zip") 787 788 // Send the request. 789 790 // BUG(dimitern) 2013-12-17 bug #1261780 791 // Due to issues with go 1.1.2, fixed later, we cannot use a 792 // regular TLS client with the CACert here, because we get "x509: 793 // cannot validate certificate for 127.0.0.1 because it doesn't 794 // contain any IP SANs". Once we use a later go version, this 795 // should be changed to connect to the API server with a regular 796 // HTTP+TLS enabled client, using the CACert (possily cached, like 797 // the tag and password) passed in api.Open()'s info argument. 798 resp, err := utils.GetNonValidatingHTTPClient().Do(req) 799 if err != nil { 800 return nil, errors.Annotate(err, "cannot upload charm") 801 } 802 defer resp.Body.Close() 803 804 // Now parse the response & return. 805 body, err := ioutil.ReadAll(resp.Body) 806 if err != nil { 807 return nil, errors.Annotate(err, "cannot read charm upload response") 808 } 809 if resp.StatusCode != http.StatusOK { 810 return nil, errors.Errorf("charm upload failed: %v (%s)", resp.StatusCode, bytes.TrimSpace(body)) 811 } 812 813 var jsonResponse params.CharmsResponse 814 if err := json.Unmarshal(body, &jsonResponse); err != nil { 815 return nil, errors.Annotate(err, "cannot unmarshal upload response") 816 } 817 if jsonResponse.Error != "" { 818 return nil, errors.Errorf("error uploading charm: %v", jsonResponse.Error) 819 } 820 return charm.MustParseURL(jsonResponse.CharmURL), nil 821 } 822 823 // noopCloser implements io.ReadCloser, but does not close the underlying io.ReadCloser. 824 // This is necessary to ensure the ownership of io.ReadCloser implementations that are 825 // passed to the net/http Transport which may (under some circumstances), call Close on 826 // the body passed to a request. 827 type noopCloser struct { 828 io.ReadCloser 829 } 830 831 func (n *noopCloser) Close() error { 832 833 // do not propogate the Close method to the underlying ReadCloser. 834 return nil 835 } 836 837 func (c *Client) apiEndpoint(destination, query string) (string, error) { 838 root, err := c.apiRoot() 839 if err != nil { 840 return "", errors.Trace(err) 841 } 842 843 upURL := url.URL{ 844 Scheme: c.st.serverScheme, 845 Host: c.st.Addr(), 846 Path: path.Join(root, destination), 847 RawQuery: query, 848 } 849 return upURL.String(), nil 850 } 851 852 func (c *Client) apiRoot() (string, error) { 853 var apiRoot string 854 if _, err := c.st.ServerTag(); err == nil { 855 envTag, err := c.st.EnvironTag() 856 if err != nil { 857 return "", errors.Annotate(err, "cannot get API endpoint address") 858 } 859 860 apiRoot = fmt.Sprintf("/environment/%s/", envTag.Id()) 861 } else { 862 // If the server tag is not set, then the agent version is < 1.23. We 863 // use the old API endpoint for backwards compatibility. 864 apiRoot = "/" 865 } 866 return apiRoot, nil 867 } 868 869 // AddCharm adds the given charm URL (which must include revision) to 870 // the environment, if it does not exist yet. Local charms are not 871 // supported, only charm store URLs. See also AddLocalCharm() in the 872 // client-side API. 873 // 874 // If the AddCharm API call fails because of an authorization error 875 // when retrieving the charm from the charm store, an error 876 // satisfying params.IsCodeUnauthorized will be returned. 877 func (c *Client) AddCharm(curl *charm.URL) error { 878 args := params.CharmURL{ 879 URL: curl.String(), 880 } 881 return c.facade.FacadeCall("AddCharm", args, nil) 882 } 883 884 // AddCharmWithAuthorization is like AddCharm except it also provides 885 // the given charmstore macaroon for the juju server to use when 886 // obtaining the charm from the charm store. The macaroon is 887 // conventionally obtained from the /delegatable-macaroon endpoint in 888 // the charm store. 889 // 890 // If the AddCharmWithAuthorization API call fails because of an 891 // authorization error when retrieving the charm from the charm store, 892 // an error satisfying params.IsCodeUnauthorized will be returned. 893 func (c *Client) AddCharmWithAuthorization(curl *charm.URL, csMac *macaroon.Macaroon) error { 894 args := params.AddCharmWithAuthorization{ 895 URL: curl.String(), 896 CharmStoreMacaroon: csMac, 897 } 898 return c.facade.FacadeCall("AddCharmWithAuthorization", args, nil) 899 } 900 901 // ResolveCharm resolves the best available charm URLs with series, for charm 902 // locations without a series specified. 903 func (c *Client) ResolveCharm(ref *charm.Reference) (*charm.URL, error) { 904 args := params.ResolveCharms{References: []charm.Reference{*ref}} 905 result := new(params.ResolveCharmResults) 906 if err := c.facade.FacadeCall("ResolveCharms", args, result); err != nil { 907 return nil, err 908 } 909 if len(result.URLs) == 0 { 910 return nil, errors.New("unexpected empty response") 911 } 912 urlInfo := result.URLs[0] 913 if urlInfo.Error != "" { 914 return nil, errors.New(urlInfo.Error) 915 } 916 return urlInfo.URL, nil 917 } 918 919 // UploadTools uploads tools at the specified location to the API server over HTTPS. 920 func (c *Client) UploadTools(r io.Reader, vers version.Binary, additionalSeries ...string) (*tools.Tools, error) { 921 // Prepare the upload request. 922 query := fmt.Sprintf("binaryVersion=%s&series=%s", 923 vers, 924 strings.Join(additionalSeries, ","), 925 ) 926 927 endPoint, err := c.apiEndpoint("tools", query) 928 if err != nil { 929 return nil, errors.Trace(err) 930 } 931 932 req, err := http.NewRequest("POST", endPoint, r) 933 if err != nil { 934 return nil, errors.Annotate(err, "cannot create upload request") 935 } 936 req.SetBasicAuth(c.st.tag, c.st.password) 937 req.Header.Set("Content-Type", "application/x-tar-gz") 938 939 // Send the request. 940 941 // BUG(dimitern) 2013-12-17 bug #1261780 942 // Due to issues with go 1.1.2, fixed later, we cannot use a 943 // regular TLS client with the CACert here, because we get "x509: 944 // cannot validate certificate for 127.0.0.1 because it doesn't 945 // contain any IP SANs". Once we use a later go version, this 946 // should be changed to connect to the API server with a regular 947 // HTTP+TLS enabled client, using the CACert (possily cached, like 948 // the tag and password) passed in api.Open()'s info argument. 949 resp, err := utils.GetNonValidatingHTTPClient().Do(req) 950 if err != nil { 951 return nil, errors.Annotate(err, "cannot upload tools") 952 } 953 defer resp.Body.Close() 954 955 // Now parse the response & return. 956 body, err := ioutil.ReadAll(resp.Body) 957 if err != nil { 958 return nil, errors.Annotate(err, "cannot read tools upload response") 959 } 960 if resp.StatusCode != http.StatusOK { 961 message := fmt.Sprintf("%s", bytes.TrimSpace(body)) 962 if resp.StatusCode == http.StatusBadRequest && strings.Contains(message, params.CodeOperationBlocked) { 963 // Operation Blocked errors must contain correct error code and message. 964 return nil, ¶ms.Error{Code: params.CodeOperationBlocked, Message: message} 965 } 966 return nil, errors.Errorf("tools upload failed: %v (%s)", resp.StatusCode, message) 967 } 968 969 var jsonResponse params.ToolsResult 970 if err := json.Unmarshal(body, &jsonResponse); err != nil { 971 return nil, errors.Annotate(err, "cannot unmarshal upload response") 972 } 973 if err := jsonResponse.Error; err != nil { 974 return nil, errors.Annotate(err, "error uploading tools") 975 } 976 return jsonResponse.Tools, nil 977 } 978 979 // APIHostPorts returns a slice of network.HostPort for each API server. 980 func (c *Client) APIHostPorts() ([][]network.HostPort, error) { 981 var result params.APIHostPortsResult 982 if err := c.facade.FacadeCall("APIHostPorts", nil, &result); err != nil { 983 return nil, err 984 } 985 return result.NetworkHostsPorts(), nil 986 } 987 988 // EnsureAvailability ensures the availability of Juju state servers. 989 // DEPRECATED: remove when we stop supporting 1.20 and earlier servers. 990 // This API is now on the HighAvailability facade. 991 func (c *Client) EnsureAvailability(numStateServers int, cons constraints.Value, series string) (params.StateServersChanges, error) { 992 var results params.StateServersChangeResults 993 envTag, err := c.st.EnvironTag() 994 if err != nil { 995 return params.StateServersChanges{}, errors.Trace(err) 996 } 997 arg := params.StateServersSpecs{ 998 Specs: []params.StateServersSpec{{ 999 EnvironTag: envTag.String(), 1000 NumStateServers: numStateServers, 1001 Constraints: cons, 1002 Series: series, 1003 }}} 1004 err = c.facade.FacadeCall("EnsureAvailability", arg, &results) 1005 if err != nil { 1006 return params.StateServersChanges{}, err 1007 } 1008 if len(results.Results) != 1 { 1009 return params.StateServersChanges{}, errors.Errorf("expected 1 result, got %d", len(results.Results)) 1010 } 1011 result := results.Results[0] 1012 if result.Error != nil { 1013 return params.StateServersChanges{}, result.Error 1014 } 1015 return result.Result, nil 1016 } 1017 1018 // AgentVersion reports the version number of the api server. 1019 func (c *Client) AgentVersion() (version.Number, error) { 1020 var result params.AgentVersionResult 1021 if err := c.facade.FacadeCall("AgentVersion", nil, &result); err != nil { 1022 return version.Number{}, err 1023 } 1024 return result.Version, nil 1025 } 1026 1027 // websocketDialConfig is called instead of websocket.DialConfig so we can 1028 // override it in tests. 1029 var websocketDialConfig = func(config *websocket.Config) (io.ReadCloser, error) { 1030 return websocket.DialConfig(config) 1031 } 1032 1033 // DebugLogParams holds parameters for WatchDebugLog that control the 1034 // filtering of the log messages. If the structure is zero initialized, the 1035 // entire log file is sent back starting from the end, and until the user 1036 // closes the connection. 1037 type DebugLogParams struct { 1038 // IncludeEntity lists entity tags to include in the response. Tags may 1039 // finish with a '*' to match a prefix e.g.: unit-mysql-*, machine-2. If 1040 // none are set, then all lines are considered included. 1041 IncludeEntity []string 1042 // IncludeModule lists logging modules to include in the response. If none 1043 // are set all modules are considered included. If a module is specified, 1044 // all the submodules also match. 1045 IncludeModule []string 1046 // ExcludeEntity lists entity tags to exclude from the response. As with 1047 // IncludeEntity the values may finish with a '*'. 1048 ExcludeEntity []string 1049 // ExcludeModule lists logging modules to exclude from the resposne. If a 1050 // module is specified, all the submodules are also excluded. 1051 ExcludeModule []string 1052 // Limit defines the maximum number of lines to return. Once this many 1053 // have been sent, the socket is closed. If zero, all filtered lines are 1054 // sent down the connection until the client closes the connection. 1055 Limit uint 1056 // Backlog tells the server to try to go back this many lines before 1057 // starting filtering. If backlog is zero and replay is false, then there 1058 // may be an initial delay until the next matching log message is written. 1059 Backlog uint 1060 // Level specifies the minimum logging level to be sent back in the response. 1061 Level loggo.Level 1062 // Replay tells the server to start at the start of the log file rather 1063 // than the end. If replay is true, backlog is ignored. 1064 Replay bool 1065 } 1066 1067 // WatchDebugLog returns a ReadCloser that the caller can read the log 1068 // lines from. Only log lines that match the filtering specified in 1069 // the DebugLogParams are returned. It returns an error that satisfies 1070 // errors.IsNotImplemented when the API server does not support the 1071 // end-point. 1072 // 1073 // TODO(dimitern) We already have errors.IsNotImplemented - why do we 1074 // need to define a different error for this purpose here? 1075 func (c *Client) WatchDebugLog(args DebugLogParams) (io.ReadCloser, error) { 1076 // The websocket connection just hangs if the server doesn't have the log 1077 // end point. So do a version check, as version was added at the same time 1078 // as the remote end point. 1079 _, err := c.AgentVersion() 1080 if err != nil { 1081 return nil, errors.NotSupportedf("WatchDebugLog") 1082 } 1083 // Prepare URL. 1084 attrs := url.Values{} 1085 if args.Replay { 1086 attrs.Set("replay", fmt.Sprint(args.Replay)) 1087 } 1088 if args.Limit > 0 { 1089 attrs.Set("maxLines", fmt.Sprint(args.Limit)) 1090 } 1091 if args.Backlog > 0 { 1092 attrs.Set("backlog", fmt.Sprint(args.Backlog)) 1093 } 1094 if args.Level != loggo.UNSPECIFIED { 1095 attrs.Set("level", fmt.Sprint(args.Level)) 1096 } 1097 attrs["includeEntity"] = args.IncludeEntity 1098 attrs["includeModule"] = args.IncludeModule 1099 attrs["excludeEntity"] = args.ExcludeEntity 1100 attrs["excludeModule"] = args.ExcludeModule 1101 1102 path := "/log" 1103 if _, ok := c.st.ServerVersion(); ok { 1104 // If the server version is set, then we know the server is capable of 1105 // serving debug log at the environment path. We also fully expect 1106 // that the server has returned a valid environment tag. 1107 envTag, err := c.st.EnvironTag() 1108 if err != nil { 1109 return nil, errors.Annotate(err, "very unexpected") 1110 } 1111 path = fmt.Sprintf("/environment/%s/log", envTag.Id()) 1112 } 1113 1114 target := url.URL{ 1115 Scheme: "wss", 1116 Host: c.st.addr, 1117 Path: path, 1118 RawQuery: attrs.Encode(), 1119 } 1120 cfg, err := websocket.NewConfig(target.String(), "http://localhost/") 1121 cfg.Header = utils.BasicAuthHeader(c.st.tag, c.st.password) 1122 cfg.TlsConfig = &tls.Config{RootCAs: c.st.certPool, ServerName: "juju-apiserver"} 1123 connection, err := websocketDialConfig(cfg) 1124 if err != nil { 1125 return nil, err 1126 } 1127 // Read the initial error and translate to a real error. 1128 // Read up to the first new line character. We can't use bufio here as it 1129 // reads too much from the reader. 1130 line := make([]byte, 4096) 1131 n, err := connection.Read(line) 1132 if err != nil { 1133 return nil, errors.Annotate(err, "unable to read initial response") 1134 } 1135 line = line[0:n] 1136 1137 logger.Debugf("initial line: %q", line) 1138 var errResult params.ErrorResult 1139 err = json.Unmarshal(line, &errResult) 1140 if err != nil { 1141 return nil, errors.Annotate(err, "unable to unmarshal initial response") 1142 } 1143 if errResult.Error != nil { 1144 return nil, errResult.Error 1145 } 1146 return connection, nil 1147 }