github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/state/api/client.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package api 5 6 import ( 7 "encoding/json" 8 "fmt" 9 "io/ioutil" 10 "net/http" 11 "os" 12 "time" 13 14 "launchpad.net/juju-core/charm" 15 "launchpad.net/juju-core/constraints" 16 "launchpad.net/juju-core/instance" 17 "launchpad.net/juju-core/state/api/params" 18 "launchpad.net/juju-core/utils" 19 "launchpad.net/juju-core/version" 20 ) 21 22 // Client represents the client-accessible part of the state. 23 type Client struct { 24 st *State 25 } 26 27 // MachineStatus holds status info about a machine. 28 type MachineStatus struct { 29 Err error 30 AgentState params.Status 31 AgentStateInfo string 32 AgentVersion string 33 DNSName string 34 InstanceId instance.Id 35 InstanceState string 36 Life string 37 Series string 38 Id string 39 Containers map[string]MachineStatus 40 Hardware string 41 } 42 43 // ServiceStatus holds status info about a service. 44 type ServiceStatus struct { 45 Err error 46 Charm string 47 Exposed bool 48 Life string 49 Relations map[string][]string 50 CanUpgradeTo string 51 SubordinateTo []string 52 Units map[string]UnitStatus 53 } 54 55 // UnitStatus holds status info about a unit. 56 type UnitStatus struct { 57 Err error 58 AgentState params.Status 59 AgentStateInfo string 60 AgentVersion string 61 Life string 62 Machine string 63 OpenedPorts []string 64 PublicAddress string 65 Charm string 66 Subordinates map[string]UnitStatus 67 } 68 69 // Status holds information about the status of a juju environment. 70 type Status struct { 71 EnvironmentName string 72 Machines map[string]MachineStatus 73 Services map[string]ServiceStatus 74 } 75 76 // Status returns the status of the juju environment. 77 func (c *Client) Status(patterns []string) (*Status, error) { 78 var result Status 79 p := params.StatusParams{Patterns: patterns} 80 if err := c.st.Call("Client", "", "FullStatus", p, &result); err != nil { 81 return nil, err 82 } 83 return &result, nil 84 } 85 86 // LegacyMachineStatus holds just the instance-id of a machine. 87 type LegacyMachineStatus struct { 88 InstanceId string // Not type instance.Id just to match original api. 89 } 90 91 // LegacyStatus holds minimal information on the status of a juju environment. 92 type LegacyStatus struct { 93 Machines map[string]LegacyMachineStatus 94 } 95 96 // LegacyStatus is a stub version of Status that 1.16 introduced. Should be 97 // removed along with structs when api versioning makes it safe to do so. 98 func (c *Client) LegacyStatus() (*LegacyStatus, error) { 99 var result LegacyStatus 100 if err := c.st.Call("Client", "", "Status", nil, &result); err != nil { 101 return nil, err 102 } 103 return &result, nil 104 } 105 106 // ServiceSet sets configuration options on a service. 107 func (c *Client) ServiceSet(service string, options map[string]string) error { 108 p := params.ServiceSet{ 109 ServiceName: service, 110 Options: options, 111 } 112 // TODO(Nate): Put this back to ServiceSet when the GUI stops expecting 113 // ServiceSet to unset values set to an empty string. 114 return c.st.Call("Client", "", "NewServiceSetForClientAPI", p, nil) 115 } 116 117 // ServiceUnset resets configuration options on a service. 118 func (c *Client) ServiceUnset(service string, options []string) error { 119 p := params.ServiceUnset{ 120 ServiceName: service, 121 Options: options, 122 } 123 return c.st.Call("Client", "", "ServiceUnset", p, nil) 124 } 125 126 // Resolved clears errors on a unit. 127 func (c *Client) Resolved(unit string, retry bool) error { 128 p := params.Resolved{ 129 UnitName: unit, 130 Retry: retry, 131 } 132 return c.st.Call("Client", "", "Resolved", p, nil) 133 } 134 135 // PublicAddress returns the public address of the specified 136 // machine or unit. 137 func (c *Client) PublicAddress(target string) (string, error) { 138 var results params.PublicAddressResults 139 p := params.PublicAddress{Target: target} 140 err := c.st.Call("Client", "", "PublicAddress", p, &results) 141 return results.PublicAddress, err 142 } 143 144 // ServiceSetYAML sets configuration options on a service 145 // given options in YAML format. 146 func (c *Client) ServiceSetYAML(service string, yaml string) error { 147 p := params.ServiceSetYAML{ 148 ServiceName: service, 149 Config: yaml, 150 } 151 return c.st.Call("Client", "", "ServiceSetYAML", p, nil) 152 } 153 154 // ServiceGet returns the configuration for the named service. 155 func (c *Client) ServiceGet(service string) (*params.ServiceGetResults, error) { 156 var results params.ServiceGetResults 157 params := params.ServiceGet{ServiceName: service} 158 err := c.st.Call("Client", "", "ServiceGet", params, &results) 159 return &results, err 160 } 161 162 // AddRelation adds a relation between the specified endpoints and returns the relation info. 163 func (c *Client) AddRelation(endpoints ...string) (*params.AddRelationResults, error) { 164 var addRelRes params.AddRelationResults 165 params := params.AddRelation{Endpoints: endpoints} 166 err := c.st.Call("Client", "", "AddRelation", params, &addRelRes) 167 return &addRelRes, err 168 } 169 170 // DestroyRelation removes the relation between the specified endpoints. 171 func (c *Client) DestroyRelation(endpoints ...string) error { 172 params := params.DestroyRelation{Endpoints: endpoints} 173 return c.st.Call("Client", "", "DestroyRelation", params, nil) 174 } 175 176 // ServiceCharmRelations returns the service's charms relation names. 177 func (c *Client) ServiceCharmRelations(service string) ([]string, error) { 178 var results params.ServiceCharmRelationsResults 179 params := params.ServiceCharmRelations{ServiceName: service} 180 err := c.st.Call("Client", "", "ServiceCharmRelations", params, &results) 181 return results.CharmRelations, err 182 } 183 184 // AddMachines adds new machines with the supplied parameters. 185 func (c *Client) AddMachines(machineParams []params.AddMachineParams) ([]params.AddMachinesResult, error) { 186 args := params.AddMachines{ 187 MachineParams: machineParams, 188 } 189 results := new(params.AddMachinesResults) 190 err := c.st.Call("Client", "", "AddMachines", args, results) 191 return results.Machines, err 192 } 193 194 // ProvisioningScript returns a shell script that, when run, 195 // provisions a machine agent on the machine executing the script. 196 func (c *Client) ProvisioningScript(args params.ProvisioningScriptParams) (script string, err error) { 197 var result params.ProvisioningScriptResult 198 if err = c.st.Call("Client", "", "ProvisioningScript", args, &result); err != nil { 199 return "", err 200 } 201 return result.Script, nil 202 } 203 204 // DestroyMachines removes a given set of machines. 205 func (c *Client) DestroyMachines(machines ...string) error { 206 params := params.DestroyMachines{MachineNames: machines} 207 return c.st.Call("Client", "", "DestroyMachines", params, nil) 208 } 209 210 // ForceDestroyMachines removes a given set of machines and all associated units. 211 func (c *Client) ForceDestroyMachines(machines ...string) error { 212 params := params.DestroyMachines{Force: true, MachineNames: machines} 213 return c.st.Call("Client", "", "DestroyMachines", params, nil) 214 } 215 216 // ServiceExpose changes the juju-managed firewall to expose any ports that 217 // were also explicitly marked by units as open. 218 func (c *Client) ServiceExpose(service string) error { 219 params := params.ServiceExpose{ServiceName: service} 220 return c.st.Call("Client", "", "ServiceExpose", params, nil) 221 } 222 223 // ServiceUnexpose changes the juju-managed firewall to unexpose any ports that 224 // were also explicitly marked by units as open. 225 func (c *Client) ServiceUnexpose(service string) error { 226 params := params.ServiceUnexpose{ServiceName: service} 227 return c.st.Call("Client", "", "ServiceUnexpose", params, nil) 228 } 229 230 // ServiceDeploy obtains the charm, either locally or from the charm store, 231 // and deploys it. 232 func (c *Client) ServiceDeploy(charmUrl string, serviceName string, numUnits int, configYAML string, cons constraints.Value, toMachineSpec string) error { 233 params := params.ServiceDeploy{ 234 ServiceName: serviceName, 235 CharmUrl: charmUrl, 236 NumUnits: numUnits, 237 ConfigYAML: configYAML, 238 Constraints: cons, 239 ToMachineSpec: toMachineSpec, 240 } 241 return c.st.Call("Client", "", "ServiceDeploy", params, nil) 242 } 243 244 // ServiceUpdate updates the service attributes, including charm URL, 245 // minimum number of units, settings and constraints. 246 // TODO(frankban) deprecate redundant API calls that this supercedes. 247 func (c *Client) ServiceUpdate(args params.ServiceUpdate) error { 248 return c.st.Call("Client", "", "ServiceUpdate", args, nil) 249 } 250 251 // ServiceSetCharm sets the charm for a given service. 252 func (c *Client) ServiceSetCharm(serviceName string, charmUrl string, force bool) error { 253 args := params.ServiceSetCharm{ 254 ServiceName: serviceName, 255 CharmUrl: charmUrl, 256 Force: force, 257 } 258 return c.st.Call("Client", "", "ServiceSetCharm", args, nil) 259 } 260 261 // ServiceGetCharmURL returns the charm URL the given service is 262 // running at present. 263 func (c *Client) ServiceGetCharmURL(serviceName string) (*charm.URL, error) { 264 result := new(params.StringResult) 265 args := params.ServiceGet{ServiceName: serviceName} 266 err := c.st.Call("Client", "", "ServiceGetCharmURL", args, &result) 267 if err != nil { 268 return nil, err 269 } 270 return charm.ParseURL(result.Result) 271 } 272 273 // AddServiceUnits adds a given number of units to a service. 274 func (c *Client) AddServiceUnits(service string, numUnits int, machineSpec string) ([]string, error) { 275 args := params.AddServiceUnits{ 276 ServiceName: service, 277 NumUnits: numUnits, 278 ToMachineSpec: machineSpec, 279 } 280 results := new(params.AddServiceUnitsResults) 281 err := c.st.Call("Client", "", "AddServiceUnits", args, results) 282 return results.Units, err 283 } 284 285 // DestroyServiceUnits decreases the number of units dedicated to a service. 286 func (c *Client) DestroyServiceUnits(unitNames ...string) error { 287 params := params.DestroyServiceUnits{unitNames} 288 return c.st.Call("Client", "", "DestroyServiceUnits", params, nil) 289 } 290 291 // ServiceDestroy destroys a given service. 292 func (c *Client) ServiceDestroy(service string) error { 293 params := params.ServiceDestroy{ 294 ServiceName: service, 295 } 296 return c.st.Call("Client", "", "ServiceDestroy", params, nil) 297 } 298 299 // GetServiceConstraints returns the constraints for the given service. 300 func (c *Client) GetServiceConstraints(service string) (constraints.Value, error) { 301 results := new(params.GetConstraintsResults) 302 err := c.st.Call("Client", "", "GetServiceConstraints", params.GetServiceConstraints{service}, results) 303 return results.Constraints, err 304 } 305 306 // GetEnvironmentConstraints returns the constraints for the environment. 307 func (c *Client) GetEnvironmentConstraints() (constraints.Value, error) { 308 results := new(params.GetConstraintsResults) 309 err := c.st.Call("Client", "", "GetEnvironmentConstraints", nil, results) 310 return results.Constraints, err 311 } 312 313 // SetServiceConstraints specifies the constraints for the given service. 314 func (c *Client) SetServiceConstraints(service string, constraints constraints.Value) error { 315 params := params.SetConstraints{ 316 ServiceName: service, 317 Constraints: constraints, 318 } 319 return c.st.Call("Client", "", "SetServiceConstraints", params, nil) 320 } 321 322 // SetEnvironmentConstraints specifies the constraints for the environment. 323 func (c *Client) SetEnvironmentConstraints(constraints constraints.Value) error { 324 params := params.SetConstraints{ 325 Constraints: constraints, 326 } 327 return c.st.Call("Client", "", "SetEnvironmentConstraints", params, nil) 328 } 329 330 // CharmInfo holds information about a charm. 331 type CharmInfo struct { 332 Revision int 333 URL string 334 Config *charm.Config 335 Meta *charm.Meta 336 } 337 338 // CharmInfo returns information about the requested charm. 339 func (c *Client) CharmInfo(charmURL string) (*CharmInfo, error) { 340 args := params.CharmInfo{CharmURL: charmURL} 341 info := new(CharmInfo) 342 if err := c.st.Call("Client", "", "CharmInfo", args, info); err != nil { 343 return nil, err 344 } 345 return info, nil 346 } 347 348 // EnvironmentInfo holds information about the Juju environment. 349 type EnvironmentInfo struct { 350 DefaultSeries string 351 ProviderType string 352 Name string 353 UUID string 354 } 355 356 // EnvironmentInfo returns details about the Juju environment. 357 func (c *Client) EnvironmentInfo() (*EnvironmentInfo, error) { 358 info := new(EnvironmentInfo) 359 err := c.st.Call("Client", "", "EnvironmentInfo", nil, info) 360 return info, err 361 } 362 363 // WatchAll holds the id of the newly-created AllWatcher. 364 type WatchAll struct { 365 AllWatcherId string 366 } 367 368 // WatchAll returns an AllWatcher, from which you can request the Next 369 // collection of Deltas. 370 func (c *Client) WatchAll() (*AllWatcher, error) { 371 info := new(WatchAll) 372 if err := c.st.Call("Client", "", "WatchAll", nil, info); err != nil { 373 return nil, err 374 } 375 return newAllWatcher(c, &info.AllWatcherId), nil 376 } 377 378 // GetAnnotations returns annotations that have been set on the given entity. 379 func (c *Client) GetAnnotations(tag string) (map[string]string, error) { 380 args := params.GetAnnotations{tag} 381 ann := new(params.GetAnnotationsResults) 382 err := c.st.Call("Client", "", "GetAnnotations", args, ann) 383 return ann.Annotations, err 384 } 385 386 // SetAnnotations sets the annotation pairs on the given entity. 387 // Currently annotations are supported on machines, services, 388 // units and the environment itself. 389 func (c *Client) SetAnnotations(tag string, pairs map[string]string) error { 390 args := params.SetAnnotations{tag, pairs} 391 return c.st.Call("Client", "", "SetAnnotations", args, nil) 392 } 393 394 // Close closes the Client's underlying State connection 395 // Client is unique among the api.State facades in closing its own State 396 // connection, but it is conventional to use a Client object without any access 397 // to its underlying state connection. 398 func (c *Client) Close() error { 399 return c.st.Close() 400 } 401 402 // EnvironmentGet returns all environment settings. 403 func (c *Client) EnvironmentGet() (map[string]interface{}, error) { 404 result := params.EnvironmentGetResults{} 405 err := c.st.Call("Client", "", "EnvironmentGet", nil, &result) 406 return result.Config, err 407 } 408 409 // EnvironmentSet sets the given key-value pairs in the environment. 410 func (c *Client) EnvironmentSet(config map[string]interface{}) error { 411 args := params.EnvironmentSet{Config: config} 412 return c.st.Call("Client", "", "EnvironmentSet", args, nil) 413 } 414 415 // SetEnvironAgentVersion sets the environment agent-version setting 416 // to the given value. 417 func (c *Client) SetEnvironAgentVersion(version version.Number) error { 418 args := params.SetEnvironAgentVersion{Version: version} 419 return c.st.Call("Client", "", "SetEnvironAgentVersion", args, nil) 420 } 421 422 // FindTools returns a List containing all tools matching the specified parameters. 423 func (c *Client) FindTools(majorVersion, minorVersion int, 424 series, arch string) (result params.FindToolsResults, err error) { 425 426 args := params.FindToolsParams{ 427 MajorVersion: majorVersion, 428 MinorVersion: minorVersion, 429 Arch: arch, 430 Series: series, 431 } 432 err = c.st.Call("Client", "", "FindTools", args, &result) 433 return result, err 434 } 435 436 // RunOnAllMachines runs the command on all the machines with the specified 437 // timeout. 438 func (c *Client) RunOnAllMachines(commands string, timeout time.Duration) ([]params.RunResult, error) { 439 var results params.RunResults 440 args := params.RunParams{Commands: commands, Timeout: timeout} 441 err := c.st.Call("Client", "", "RunOnAllMachines", args, &results) 442 return results.Results, err 443 } 444 445 // Run the Commands specified on the machines identified through the ids 446 // provided in the machines, services and units slices. 447 func (c *Client) Run(run params.RunParams) ([]params.RunResult, error) { 448 var results params.RunResults 449 err := c.st.Call("Client", "", "Run", run, &results) 450 return results.Results, err 451 } 452 453 // DestroyEnvironment puts the environment into a "dying" state, 454 // and removes all non-manager machine instances. DestroyEnvironment 455 // will fail if there are any manually-provisioned non-manager machines 456 // in state. 457 func (c *Client) DestroyEnvironment() error { 458 return c.st.Call("Client", "", "DestroyEnvironment", nil, nil) 459 } 460 461 // AddLocalCharm prepares the given charm with a local: schema in its 462 // URL, and uploads it via the API server, returning the assigned 463 // charm URL. If the API server does not support charm uploads, an 464 // error satisfying params.IsCodeNotImplemented() is returned. 465 func (c *Client) AddLocalCharm(curl *charm.URL, ch charm.Charm) (*charm.URL, error) { 466 if curl.Schema != "local" { 467 return nil, fmt.Errorf("expected charm URL with local: schema, got %q", curl.String()) 468 } 469 // Package the charm for uploading. 470 var archive *os.File 471 switch ch := ch.(type) { 472 case *charm.Dir: 473 var err error 474 if archive, err = ioutil.TempFile("", "charm"); err != nil { 475 return nil, fmt.Errorf("cannot create temp file: %v", err) 476 } 477 defer os.Remove(archive.Name()) 478 defer archive.Close() 479 if err := ch.BundleTo(archive); err != nil { 480 return nil, fmt.Errorf("cannot repackage charm: %v", err) 481 } 482 if _, err := archive.Seek(0, 0); err != nil { 483 return nil, fmt.Errorf("cannot rewind packaged charm: %v", err) 484 } 485 case *charm.Bundle: 486 var err error 487 if archive, err = os.Open(ch.Path); err != nil { 488 return nil, fmt.Errorf("cannot read charm archive: %v", err) 489 } 490 defer archive.Close() 491 default: 492 return nil, fmt.Errorf("unknown charm type %T", ch) 493 } 494 495 // Prepare the upload request. 496 url := fmt.Sprintf("%s/charms?series=%s", c.st.serverRoot, curl.Series) 497 req, err := http.NewRequest("POST", url, archive) 498 if err != nil { 499 return nil, fmt.Errorf("cannot create upload request: %v", err) 500 } 501 req.SetBasicAuth(c.st.tag, c.st.password) 502 req.Header.Set("Content-Type", "application/zip") 503 504 // Send the request. 505 506 // BUG(dimitern) 2013-12-17 bug #1261780 507 // Due to issues with go 1.1.2, fixed later, we cannot use a 508 // regular TLS client with the CACert here, because we get "x509: 509 // cannot validate certificate for 127.0.0.1 because it doesn't 510 // contain any IP SANs". Once we use a later go version, this 511 // should be changed to connect to the API server with a regular 512 // HTTP+TLS enabled client, using the CACert (possily cached, like 513 // the tag and password) passed in api.Open()'s info argument. 514 resp, err := utils.GetNonValidatingHTTPClient().Do(req) 515 if err != nil { 516 return nil, fmt.Errorf("cannot upload charm: %v", err) 517 } 518 if resp.StatusCode == http.StatusMethodNotAllowed { 519 // API server is 1.16 or older, so charm upload 520 // is not supported; notify the client. 521 return nil, ¶ms.Error{ 522 Message: "charm upload is not supported by the API server", 523 Code: params.CodeNotImplemented, 524 } 525 } 526 527 // Now parse the response & return. 528 body, err := ioutil.ReadAll(resp.Body) 529 if err != nil { 530 return nil, fmt.Errorf("cannot read charm upload response: %v", err) 531 } 532 defer resp.Body.Close() 533 var jsonResponse params.CharmsResponse 534 if err := json.Unmarshal(body, &jsonResponse); err != nil { 535 return nil, fmt.Errorf("cannot unmarshal upload response: %v", err) 536 } 537 if jsonResponse.Error != "" { 538 return nil, fmt.Errorf("error uploading charm: %v", jsonResponse.Error) 539 } 540 return charm.MustParseURL(jsonResponse.CharmURL), nil 541 } 542 543 // AddCharm adds the given charm URL (which must include revision) to 544 // the environment, if it does not exist yet. Local charms are not 545 // supported, only charm store URLs. See also AddLocalCharm() in the 546 // client-side API. 547 func (c *Client) AddCharm(curl *charm.URL) error { 548 args := params.CharmURL{URL: curl.String()} 549 return c.st.Call("Client", "", "AddCharm", args, nil) 550 }