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