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