github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/api/client.go (about)

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