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