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