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, &params.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  }