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

     1  // Copyright 2013, 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package client
     5  
     6  import (
     7  	"fmt"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/juju/errors"
    12  	"github.com/juju/loggo"
    13  	"github.com/juju/names"
    14  	"gopkg.in/juju/charm.v6-unstable"
    15  
    16  	"github.com/juju/juju/api"
    17  	"github.com/juju/juju/apiserver/common"
    18  	"github.com/juju/juju/apiserver/highavailability"
    19  	"github.com/juju/juju/apiserver/params"
    20  	"github.com/juju/juju/apiserver/service"
    21  	"github.com/juju/juju/environs/config"
    22  	"github.com/juju/juju/environs/manual"
    23  	"github.com/juju/juju/instance"
    24  	jjj "github.com/juju/juju/juju"
    25  	"github.com/juju/juju/network"
    26  	"github.com/juju/juju/state"
    27  	"github.com/juju/juju/version"
    28  )
    29  
    30  func init() {
    31  	common.RegisterStandardFacade("Client", 0, NewClient)
    32  }
    33  
    34  var logger = loggo.GetLogger("juju.apiserver.client")
    35  
    36  type API struct {
    37  	stateAccessor stateInterface
    38  	auth          common.Authorizer
    39  	resources     *common.Resources
    40  	client        *Client
    41  	// statusSetter provides common methods for updating an entity's provisioning status.
    42  	statusSetter *common.StatusSetter
    43  	toolsFinder  *common.ToolsFinder
    44  }
    45  
    46  // TODO(wallyworld) - remove this method
    47  // state returns a state.State instance for this API.
    48  // Until all code is refactored to use interfaces, we
    49  // need this helper to keep older code happy.
    50  func (api *API) state() *state.State {
    51  	return api.stateAccessor.(*stateShim).State
    52  }
    53  
    54  // Client serves client-specific API methods.
    55  type Client struct {
    56  	api   *API
    57  	check *common.BlockChecker
    58  }
    59  
    60  var getState = func(st *state.State) stateInterface {
    61  	return &stateShim{st}
    62  }
    63  
    64  // NewClient creates a new instance of the Client Facade.
    65  func NewClient(st *state.State, resources *common.Resources, authorizer common.Authorizer) (*Client, error) {
    66  	if !authorizer.AuthClient() {
    67  		return nil, common.ErrPerm
    68  	}
    69  	apiState := getState(st)
    70  	urlGetter := common.NewToolsURLGetter(apiState.EnvironUUID(), apiState)
    71  	return &Client{
    72  		api: &API{
    73  			stateAccessor: apiState,
    74  			auth:          authorizer,
    75  			resources:     resources,
    76  			statusSetter:  common.NewStatusSetter(st, common.AuthAlways()),
    77  			toolsFinder:   common.NewToolsFinder(st, st, urlGetter),
    78  		},
    79  		check: common.NewBlockChecker(st)}, nil
    80  }
    81  
    82  func (c *Client) WatchAll() (params.AllWatcherId, error) {
    83  	w := c.api.stateAccessor.Watch()
    84  	return params.AllWatcherId{
    85  		AllWatcherId: c.api.resources.Register(w),
    86  	}, nil
    87  }
    88  
    89  // ServiceSet implements the server side of Client.ServiceSet. Values set to an
    90  // empty string will be unset.
    91  //
    92  // (Deprecated) Use NewServiceSetForClientAPI instead, to preserve values set to
    93  // an empty string, and use ServiceUnset to unset values.
    94  func (c *Client) ServiceSet(p params.ServiceSet) error {
    95  	if err := c.check.ChangeAllowed(); err != nil {
    96  		return errors.Trace(err)
    97  	}
    98  	svc, err := c.api.stateAccessor.Service(p.ServiceName)
    99  	if err != nil {
   100  		return err
   101  	}
   102  	return service.ServiceSetSettingsStrings(svc, p.Options)
   103  }
   104  
   105  // NewServiceSetForClientAPI implements the server side of
   106  // Client.NewServiceSetForClientAPI. This is exactly like ServiceSet except that
   107  // it does not unset values that are set to an empty string.  ServiceUnset
   108  // should be used for that.
   109  //
   110  // TODO(Nate): rename this to ServiceSet (and remove the deprecated ServiceSet)
   111  // when the GUI handles the new behavior.
   112  // TODO(mattyw, all): This api call should be move to the new service facade. The client api version will then need bumping.
   113  func (c *Client) NewServiceSetForClientAPI(p params.ServiceSet) error {
   114  	svc, err := c.api.stateAccessor.Service(p.ServiceName)
   115  	if err != nil {
   116  		return err
   117  	}
   118  	return newServiceSetSettingsStringsForClientAPI(svc, p.Options)
   119  }
   120  
   121  // ServiceUnset implements the server side of Client.ServiceUnset.
   122  // TODO(mattyw, all): This api call should be move to the new service facade. The client api version will then need bumping.
   123  func (c *Client) ServiceUnset(p params.ServiceUnset) error {
   124  	if err := c.check.ChangeAllowed(); err != nil {
   125  		return errors.Trace(err)
   126  	}
   127  	svc, err := c.api.stateAccessor.Service(p.ServiceName)
   128  	if err != nil {
   129  		return err
   130  	}
   131  	settings := make(charm.Settings)
   132  	for _, option := range p.Options {
   133  		settings[option] = nil
   134  	}
   135  	return svc.UpdateConfigSettings(settings)
   136  }
   137  
   138  // ServiceSetYAML implements the server side of Client.ServerSetYAML.
   139  // TODO(mattyw, all): This api call should be move to the new service facade. The client api version will then need bumping.
   140  func (c *Client) ServiceSetYAML(p params.ServiceSetYAML) error {
   141  	if err := c.check.ChangeAllowed(); err != nil {
   142  		return errors.Trace(err)
   143  	}
   144  	svc, err := c.api.stateAccessor.Service(p.ServiceName)
   145  	if err != nil {
   146  		return err
   147  	}
   148  	return serviceSetSettingsYAML(svc, p.Config)
   149  }
   150  
   151  // ServiceCharmRelations implements the server side of Client.ServiceCharmRelations.
   152  // TODO(mattyw, all): This api call should be move to the new service facade. The client api version will then need bumping.
   153  func (c *Client) ServiceCharmRelations(p params.ServiceCharmRelations) (params.ServiceCharmRelationsResults, error) {
   154  	var results params.ServiceCharmRelationsResults
   155  	service, err := c.api.stateAccessor.Service(p.ServiceName)
   156  	if err != nil {
   157  		return results, err
   158  	}
   159  	endpoints, err := service.Endpoints()
   160  	if err != nil {
   161  		return results, err
   162  	}
   163  	results.CharmRelations = make([]string, len(endpoints))
   164  	for i, endpoint := range endpoints {
   165  		results.CharmRelations[i] = endpoint.Relation.Name
   166  	}
   167  	return results, nil
   168  }
   169  
   170  // Resolved implements the server side of Client.Resolved.
   171  func (c *Client) Resolved(p params.Resolved) error {
   172  	if err := c.check.ChangeAllowed(); err != nil {
   173  		return errors.Trace(err)
   174  	}
   175  	unit, err := c.api.stateAccessor.Unit(p.UnitName)
   176  	if err != nil {
   177  		return err
   178  	}
   179  	return unit.Resolve(p.Retry)
   180  }
   181  
   182  // PublicAddress implements the server side of Client.PublicAddress.
   183  func (c *Client) PublicAddress(p params.PublicAddress) (results params.PublicAddressResults, err error) {
   184  	switch {
   185  	case names.IsValidMachine(p.Target):
   186  		machine, err := c.api.stateAccessor.Machine(p.Target)
   187  		if err != nil {
   188  			return results, err
   189  		}
   190  		addr, err := machine.PublicAddress()
   191  		if err != nil {
   192  			return results, errors.Annotatef(err, "error fetching address for machine %q", machine)
   193  		}
   194  		return params.PublicAddressResults{PublicAddress: addr.Value}, nil
   195  
   196  	case names.IsValidUnit(p.Target):
   197  		unit, err := c.api.stateAccessor.Unit(p.Target)
   198  		if err != nil {
   199  			return results, err
   200  		}
   201  		addr, err := unit.PublicAddress()
   202  		if err != nil {
   203  			return results, errors.Annotatef(err, "error fetching address for unit %q", unit)
   204  		}
   205  		return params.PublicAddressResults{PublicAddress: addr.Value}, nil
   206  	}
   207  	return results, errors.Errorf("unknown unit or machine %q", p.Target)
   208  }
   209  
   210  // PrivateAddress implements the server side of Client.PrivateAddress.
   211  func (c *Client) PrivateAddress(p params.PrivateAddress) (results params.PrivateAddressResults, err error) {
   212  	switch {
   213  	case names.IsValidMachine(p.Target):
   214  		machine, err := c.api.stateAccessor.Machine(p.Target)
   215  		if err != nil {
   216  			return results, err
   217  		}
   218  		addr, err := machine.PrivateAddress()
   219  		if err != nil {
   220  			return results, errors.Annotatef(err, "error fetching address for machine %q", machine)
   221  		}
   222  		return params.PrivateAddressResults{PrivateAddress: addr.Value}, nil
   223  
   224  	case names.IsValidUnit(p.Target):
   225  		unit, err := c.api.stateAccessor.Unit(p.Target)
   226  		if err != nil {
   227  			return results, err
   228  		}
   229  		addr, err := unit.PrivateAddress()
   230  		if err != nil {
   231  			return results, errors.Annotatef(err, "error fetching address for unit %q", unit)
   232  		}
   233  		return params.PrivateAddressResults{PrivateAddress: addr.Value}, nil
   234  	}
   235  	return results, fmt.Errorf("unknown unit or machine %q", p.Target)
   236  
   237  }
   238  
   239  // ServiceExpose changes the juju-managed firewall to expose any ports that
   240  // were also explicitly marked by units as open.
   241  // TODO(mattyw, all): This api call should be move to the new service facade. The client api version will then need bumping.
   242  func (c *Client) ServiceExpose(args params.ServiceExpose) error {
   243  	if err := c.check.ChangeAllowed(); err != nil {
   244  		return errors.Trace(err)
   245  	}
   246  	svc, err := c.api.stateAccessor.Service(args.ServiceName)
   247  	if err != nil {
   248  		return err
   249  	}
   250  	return svc.SetExposed()
   251  }
   252  
   253  // ServiceUnexpose changes the juju-managed firewall to unexpose any ports that
   254  // were also explicitly marked by units as open.
   255  // TODO(mattyw, all): This api call should be move to the new service facade. The client api version will then need bumping.
   256  func (c *Client) ServiceUnexpose(args params.ServiceUnexpose) error {
   257  	if err := c.check.ChangeAllowed(); err != nil {
   258  		return errors.Trace(err)
   259  	}
   260  	svc, err := c.api.stateAccessor.Service(args.ServiceName)
   261  	if err != nil {
   262  		return err
   263  	}
   264  	return svc.ClearExposed()
   265  }
   266  
   267  // ServiceDeploy fetches the charm from the charm store and deploys it.
   268  // AddCharm or AddLocalCharm should be called to add the charm
   269  // before calling ServiceDeploy, although for backward compatibility
   270  // this is not necessary until 1.16 support is removed.
   271  func (c *Client) ServiceDeploy(args params.ServiceDeploy) error {
   272  	if err := c.check.ChangeAllowed(); err != nil {
   273  		return errors.Trace(err)
   274  	}
   275  	return service.DeployService(c.api.state(), c.api.auth.GetAuthTag().String(), args)
   276  }
   277  
   278  // ServiceDeployWithNetworks works exactly like ServiceDeploy, but
   279  // allows specifying networks to include or exclude on the machine
   280  // where the charm gets deployed (either with args.Network or with
   281  // constraints).
   282  //
   283  // TODO(dimitern): Drop the special handling of networks in favor of
   284  // spaces constraints, once possible.
   285  func (c *Client) ServiceDeployWithNetworks(args params.ServiceDeploy) error {
   286  	return c.ServiceDeploy(args)
   287  }
   288  
   289  // ServiceUpdate updates the service attributes, including charm URL,
   290  // minimum number of units, settings and constraints.
   291  // All parameters in params.ServiceUpdate except the service name are optional.
   292  func (c *Client) ServiceUpdate(args params.ServiceUpdate) error {
   293  	if !args.ForceCharmUrl {
   294  		if err := c.check.ChangeAllowed(); err != nil {
   295  			return errors.Trace(err)
   296  		}
   297  	}
   298  	svc, err := c.api.stateAccessor.Service(args.ServiceName)
   299  	if err != nil {
   300  		return err
   301  	}
   302  	// Set the charm for the given service.
   303  	if args.CharmUrl != "" {
   304  		if err = c.serviceSetCharm(svc, args.CharmUrl, args.ForceCharmUrl); err != nil {
   305  			return err
   306  		}
   307  	}
   308  	// Set the minimum number of units for the given service.
   309  	if args.MinUnits != nil {
   310  		if err = svc.SetMinUnits(*args.MinUnits); err != nil {
   311  			return err
   312  		}
   313  	}
   314  	// Set up service's settings.
   315  	if args.SettingsYAML != "" {
   316  		if err = serviceSetSettingsYAML(svc, args.SettingsYAML); err != nil {
   317  			return err
   318  		}
   319  	} else if len(args.SettingsStrings) > 0 {
   320  		if err = service.ServiceSetSettingsStrings(svc, args.SettingsStrings); err != nil {
   321  			return err
   322  		}
   323  	}
   324  	// Update service's constraints.
   325  	if args.Constraints != nil {
   326  		return svc.SetConstraints(*args.Constraints)
   327  	}
   328  	return nil
   329  }
   330  
   331  // serviceSetCharm sets the charm for the given service.
   332  func (c *Client) serviceSetCharm(service *state.Service, url string, force bool) error {
   333  	curl, err := charm.ParseURL(url)
   334  	if err != nil {
   335  		return err
   336  	}
   337  	sch, err := c.api.stateAccessor.Charm(curl)
   338  	if errors.IsNotFound(err) {
   339  		// Charms should be added before trying to use them, with
   340  		// AddCharm or AddLocalCharm API calls. When they're not,
   341  		// we're reverting to 1.16 compatibility mode.
   342  		return c.serviceSetCharm1dot16(service, curl, force)
   343  	}
   344  	if err != nil {
   345  		return err
   346  	}
   347  	return service.SetCharm(sch, force)
   348  }
   349  
   350  // serviceSetCharm1dot16 sets the charm for the given service in 1.16
   351  // compatibility mode. Remove this when support for 1.16 is dropped.
   352  func (c *Client) serviceSetCharm1dot16(service *state.Service, curl *charm.URL, force bool) error {
   353  	if curl.Schema != "cs" {
   354  		return fmt.Errorf(`charm url has unsupported schema %q`, curl.Schema)
   355  	}
   356  	if curl.Revision < 0 {
   357  		return fmt.Errorf("charm url must include revision")
   358  	}
   359  	err := c.AddCharm(params.CharmURL{
   360  		URL: curl.String(),
   361  	})
   362  	if err != nil {
   363  		return err
   364  	}
   365  	ch, err := c.api.stateAccessor.Charm(curl)
   366  	if err != nil {
   367  		return err
   368  	}
   369  	return service.SetCharm(ch, force)
   370  }
   371  
   372  // serviceSetSettingsYAML updates the settings for the given service,
   373  // taking the configuration from a YAML string.
   374  func serviceSetSettingsYAML(service *state.Service, settings string) error {
   375  	ch, _, err := service.Charm()
   376  	if err != nil {
   377  		return err
   378  	}
   379  	changes, err := ch.Config().ParseSettingsYAML([]byte(settings), service.Name())
   380  	if err != nil {
   381  		return err
   382  	}
   383  	return service.UpdateConfigSettings(changes)
   384  }
   385  
   386  // newServiceSetSettingsStringsForClientAPI updates the settings for the given
   387  // service, taking the configuration from a map of strings.
   388  //
   389  // TODO(Nate): replace serviceSetSettingsStrings with this onces the GUI no
   390  // longer expects to be able to unset values by sending an empty string.
   391  func newServiceSetSettingsStringsForClientAPI(service *state.Service, settings map[string]string) error {
   392  	ch, _, err := service.Charm()
   393  	if err != nil {
   394  		return err
   395  	}
   396  
   397  	// Validate the settings.
   398  	changes, err := ch.Config().ParseSettingsStrings(settings)
   399  	if err != nil {
   400  		return err
   401  	}
   402  
   403  	return service.UpdateConfigSettings(changes)
   404  }
   405  
   406  // ServiceSetCharm sets the charm for a given service.
   407  // TODO(mattyw, all): This api call should be move to the new service facade. The client api version will then need bumping.
   408  func (c *Client) ServiceSetCharm(args params.ServiceSetCharm) error {
   409  	// when forced, don't block
   410  	if !args.Force {
   411  		if err := c.check.ChangeAllowed(); err != nil {
   412  			return errors.Trace(err)
   413  		}
   414  	}
   415  	service, err := c.api.stateAccessor.Service(args.ServiceName)
   416  	if err != nil {
   417  		return err
   418  	}
   419  	return c.serviceSetCharm(service, args.CharmUrl, args.Force)
   420  }
   421  
   422  // addServiceUnits adds a given number of units to a service.
   423  func addServiceUnits(st *state.State, args params.AddServiceUnits) ([]*state.Unit, error) {
   424  	service, err := st.Service(args.ServiceName)
   425  	if err != nil {
   426  		return nil, err
   427  	}
   428  	if args.NumUnits < 1 {
   429  		return nil, fmt.Errorf("must add at least one unit")
   430  	}
   431  
   432  	// New API uses placement directives.
   433  	if len(args.Placement) > 0 {
   434  		return jjj.AddUnitsWithPlacement(st, service, args.NumUnits, args.Placement)
   435  	}
   436  
   437  	// Otherwise we use the older machine spec.
   438  	if args.NumUnits > 1 && args.ToMachineSpec != "" {
   439  		return nil, fmt.Errorf("cannot use NumUnits with ToMachineSpec")
   440  	}
   441  
   442  	if args.ToMachineSpec != "" && names.IsValidMachine(args.ToMachineSpec) {
   443  		_, err = st.Machine(args.ToMachineSpec)
   444  		if err != nil {
   445  			return nil, errors.Annotatef(err, `cannot add units for service "%v" to machine %v`, args.ServiceName, args.ToMachineSpec)
   446  		}
   447  	}
   448  	return jjj.AddUnits(st, service, args.NumUnits, args.ToMachineSpec)
   449  }
   450  
   451  // AddServiceUnits adds a given number of units to a service.
   452  func (c *Client) AddServiceUnits(args params.AddServiceUnits) (params.AddServiceUnitsResults, error) {
   453  	return c.AddServiceUnitsWithPlacement(args)
   454  }
   455  
   456  // AddServiceUnits adds a given number of units to a service.
   457  func (c *Client) AddServiceUnitsWithPlacement(args params.AddServiceUnits) (params.AddServiceUnitsResults, error) {
   458  	if err := c.check.ChangeAllowed(); err != nil {
   459  		return params.AddServiceUnitsResults{}, errors.Trace(err)
   460  	}
   461  	units, err := addServiceUnits(c.api.state(), args)
   462  	if err != nil {
   463  		return params.AddServiceUnitsResults{}, err
   464  	}
   465  	unitNames := make([]string, len(units))
   466  	for i, unit := range units {
   467  		unitNames[i] = unit.String()
   468  	}
   469  	return params.AddServiceUnitsResults{Units: unitNames}, nil
   470  }
   471  
   472  // DestroyServiceUnits removes a given set of service units.
   473  func (c *Client) DestroyServiceUnits(args params.DestroyServiceUnits) error {
   474  	if err := c.check.RemoveAllowed(); err != nil {
   475  		return errors.Trace(err)
   476  	}
   477  	var errs []string
   478  	for _, name := range args.UnitNames {
   479  		unit, err := c.api.stateAccessor.Unit(name)
   480  		switch {
   481  		case errors.IsNotFound(err):
   482  			err = fmt.Errorf("unit %q does not exist", name)
   483  		case err != nil:
   484  		case unit.Life() != state.Alive:
   485  			continue
   486  		case unit.IsPrincipal():
   487  			err = unit.Destroy()
   488  		default:
   489  			err = fmt.Errorf("unit %q is a subordinate", name)
   490  		}
   491  		if err != nil {
   492  			errs = append(errs, err.Error())
   493  		}
   494  	}
   495  	return destroyErr("units", args.UnitNames, errs)
   496  }
   497  
   498  // ServiceDestroy destroys a given service.
   499  // TODO(mattyw, all): This api call should be move to the new service facade. The client api version will then need bumping.
   500  func (c *Client) ServiceDestroy(args params.ServiceDestroy) error {
   501  	if err := c.check.RemoveAllowed(); err != nil {
   502  		return errors.Trace(err)
   503  	}
   504  	svc, err := c.api.stateAccessor.Service(args.ServiceName)
   505  	if err != nil {
   506  		return err
   507  	}
   508  	return svc.Destroy()
   509  }
   510  
   511  // GetServiceConstraints returns the constraints for a given service.
   512  // TODO(mattyw, all): This api call should be move to the new service facade. The client api version will then need bumping.
   513  func (c *Client) GetServiceConstraints(args params.GetServiceConstraints) (params.GetConstraintsResults, error) {
   514  	svc, err := c.api.stateAccessor.Service(args.ServiceName)
   515  	if err != nil {
   516  		return params.GetConstraintsResults{}, err
   517  	}
   518  	cons, err := svc.Constraints()
   519  	return params.GetConstraintsResults{cons}, err
   520  }
   521  
   522  // GetEnvironmentConstraints returns the constraints for the environment.
   523  func (c *Client) GetEnvironmentConstraints() (params.GetConstraintsResults, error) {
   524  	cons, err := c.api.stateAccessor.EnvironConstraints()
   525  	if err != nil {
   526  		return params.GetConstraintsResults{}, err
   527  	}
   528  	return params.GetConstraintsResults{cons}, nil
   529  }
   530  
   531  // SetServiceConstraints sets the constraints for a given service.
   532  // TODO(mattyw, all): This api call should be move to the new service facade. The client api version will then need bumping.
   533  func (c *Client) SetServiceConstraints(args params.SetConstraints) error {
   534  	if err := c.check.ChangeAllowed(); err != nil {
   535  		return errors.Trace(err)
   536  	}
   537  	svc, err := c.api.stateAccessor.Service(args.ServiceName)
   538  	if err != nil {
   539  		return err
   540  	}
   541  	return svc.SetConstraints(args.Constraints)
   542  }
   543  
   544  // SetEnvironmentConstraints sets the constraints for the environment.
   545  func (c *Client) SetEnvironmentConstraints(args params.SetConstraints) error {
   546  	if err := c.check.ChangeAllowed(); err != nil {
   547  		return errors.Trace(err)
   548  	}
   549  	return c.api.stateAccessor.SetEnvironConstraints(args.Constraints)
   550  }
   551  
   552  // AddRelation adds a relation between the specified endpoints and returns the relation info.
   553  func (c *Client) AddRelation(args params.AddRelation) (params.AddRelationResults, error) {
   554  	if err := c.check.ChangeAllowed(); err != nil {
   555  		return params.AddRelationResults{}, errors.Trace(err)
   556  	}
   557  	inEps, err := c.api.stateAccessor.InferEndpoints(args.Endpoints...)
   558  	if err != nil {
   559  		return params.AddRelationResults{}, err
   560  	}
   561  	rel, err := c.api.stateAccessor.AddRelation(inEps...)
   562  	if err != nil {
   563  		return params.AddRelationResults{}, err
   564  	}
   565  	outEps := make(map[string]charm.Relation)
   566  	for _, inEp := range inEps {
   567  		outEp, err := rel.Endpoint(inEp.ServiceName)
   568  		if err != nil {
   569  			return params.AddRelationResults{}, err
   570  		}
   571  		outEps[inEp.ServiceName] = outEp.Relation
   572  	}
   573  	return params.AddRelationResults{Endpoints: outEps}, nil
   574  }
   575  
   576  // DestroyRelation removes the relation between the specified endpoints.
   577  func (c *Client) DestroyRelation(args params.DestroyRelation) error {
   578  	if err := c.check.RemoveAllowed(); err != nil {
   579  		return errors.Trace(err)
   580  	}
   581  	eps, err := c.api.stateAccessor.InferEndpoints(args.Endpoints...)
   582  	if err != nil {
   583  		return err
   584  	}
   585  	rel, err := c.api.stateAccessor.EndpointsRelation(eps...)
   586  	if err != nil {
   587  		return err
   588  	}
   589  	return rel.Destroy()
   590  }
   591  
   592  // AddMachines adds new machines with the supplied parameters.
   593  func (c *Client) AddMachines(args params.AddMachines) (params.AddMachinesResults, error) {
   594  	return c.AddMachinesV2(args)
   595  }
   596  
   597  // AddMachinesV2 adds new machines with the supplied parameters.
   598  func (c *Client) AddMachinesV2(args params.AddMachines) (params.AddMachinesResults, error) {
   599  	results := params.AddMachinesResults{
   600  		Machines: make([]params.AddMachinesResult, len(args.MachineParams)),
   601  	}
   602  	if err := c.check.ChangeAllowed(); err != nil {
   603  		return results, errors.Trace(err)
   604  	}
   605  	for i, p := range args.MachineParams {
   606  		m, err := c.addOneMachine(p)
   607  		results.Machines[i].Error = common.ServerError(err)
   608  		if err == nil {
   609  			results.Machines[i].Machine = m.Id()
   610  		}
   611  	}
   612  	return results, nil
   613  }
   614  
   615  // InjectMachines injects a machine into state with provisioned status.
   616  func (c *Client) InjectMachines(args params.AddMachines) (params.AddMachinesResults, error) {
   617  	return c.AddMachines(args)
   618  }
   619  
   620  func (c *Client) addOneMachine(p params.AddMachineParams) (*state.Machine, error) {
   621  	if p.ParentId != "" && p.ContainerType == "" {
   622  		return nil, fmt.Errorf("parent machine specified without container type")
   623  	}
   624  	if p.ContainerType != "" && p.Placement != nil {
   625  		return nil, fmt.Errorf("container type and placement are mutually exclusive")
   626  	}
   627  	if p.Placement != nil {
   628  		// Extract container type and parent from container placement directives.
   629  		containerType, err := instance.ParseContainerType(p.Placement.Scope)
   630  		if err == nil {
   631  			p.ContainerType = containerType
   632  			p.ParentId = p.Placement.Directive
   633  			p.Placement = nil
   634  		}
   635  	}
   636  
   637  	if p.ContainerType != "" || p.Placement != nil {
   638  		// Guard against dubious client by making sure that
   639  		// the following attributes can only be set when we're
   640  		// not using placement.
   641  		p.InstanceId = ""
   642  		p.Nonce = ""
   643  		p.HardwareCharacteristics = instance.HardwareCharacteristics{}
   644  		p.Addrs = nil
   645  	}
   646  
   647  	if p.Series == "" {
   648  		conf, err := c.api.stateAccessor.EnvironConfig()
   649  		if err != nil {
   650  			return nil, err
   651  		}
   652  		p.Series = config.PreferredSeries(conf)
   653  	}
   654  
   655  	var placementDirective string
   656  	if p.Placement != nil {
   657  		env, err := c.api.stateAccessor.Environment()
   658  		if err != nil {
   659  			return nil, err
   660  		}
   661  		// For 1.21 we should support both UUID and name, and with 1.22
   662  		// just support UUID
   663  		if p.Placement.Scope != env.Name() && p.Placement.Scope != env.UUID() {
   664  			return nil, fmt.Errorf("invalid environment name %q", p.Placement.Scope)
   665  		}
   666  		placementDirective = p.Placement.Directive
   667  	}
   668  
   669  	jobs, err := common.StateJobs(p.Jobs)
   670  	if err != nil {
   671  		return nil, err
   672  	}
   673  	template := state.MachineTemplate{
   674  		Series:      p.Series,
   675  		Constraints: p.Constraints,
   676  		InstanceId:  p.InstanceId,
   677  		Jobs:        jobs,
   678  		Nonce:       p.Nonce,
   679  		HardwareCharacteristics: p.HardwareCharacteristics,
   680  		Addresses:               params.NetworkAddresses(p.Addrs),
   681  		Placement:               placementDirective,
   682  	}
   683  	if p.ContainerType == "" {
   684  		return c.api.stateAccessor.AddOneMachine(template)
   685  	}
   686  	if p.ParentId != "" {
   687  		return c.api.stateAccessor.AddMachineInsideMachine(template, p.ParentId, p.ContainerType)
   688  	}
   689  	return c.api.stateAccessor.AddMachineInsideNewMachine(template, template, p.ContainerType)
   690  }
   691  
   692  // ProvisioningScript returns a shell script that, when run,
   693  // provisions a machine agent on the machine executing the script.
   694  func (c *Client) ProvisioningScript(args params.ProvisioningScriptParams) (params.ProvisioningScriptResult, error) {
   695  	var result params.ProvisioningScriptResult
   696  	icfg, err := InstanceConfig(c.api.state(), args.MachineId, args.Nonce, args.DataDir)
   697  	if err != nil {
   698  		return result, err
   699  	}
   700  
   701  	// Until DisablePackageCommands is retired, for backwards
   702  	// compatibility, we must respect the client's request and
   703  	// override any environment settings the user may have specified.
   704  	// If the client does specify this setting, it will only ever be
   705  	// true. False indicates the client doesn't care and we should use
   706  	// what's specified in the environments.yaml file.
   707  	if args.DisablePackageCommands {
   708  		icfg.EnableOSRefreshUpdate = false
   709  		icfg.EnableOSUpgrade = false
   710  	} else if cfg, err := c.api.stateAccessor.EnvironConfig(); err != nil {
   711  		return result, err
   712  	} else {
   713  		icfg.EnableOSUpgrade = cfg.EnableOSUpgrade()
   714  		icfg.EnableOSRefreshUpdate = cfg.EnableOSRefreshUpdate()
   715  	}
   716  
   717  	result.Script, err = manual.ProvisioningScript(icfg)
   718  	return result, err
   719  }
   720  
   721  // DestroyMachines removes a given set of machines.
   722  func (c *Client) DestroyMachines(args params.DestroyMachines) error {
   723  	var errs []string
   724  	for _, id := range args.MachineNames {
   725  		machine, err := c.api.stateAccessor.Machine(id)
   726  		switch {
   727  		case errors.IsNotFound(err):
   728  			err = fmt.Errorf("machine %s does not exist", id)
   729  		case err != nil:
   730  		case args.Force:
   731  			err = machine.ForceDestroy()
   732  		case machine.Life() != state.Alive:
   733  			continue
   734  		default:
   735  			{
   736  				if err := c.check.RemoveAllowed(); err != nil {
   737  					return errors.Trace(err)
   738  				}
   739  				err = machine.Destroy()
   740  			}
   741  		}
   742  		if err != nil {
   743  			errs = append(errs, err.Error())
   744  		}
   745  	}
   746  	return destroyErr("machines", args.MachineNames, errs)
   747  }
   748  
   749  // CharmInfo returns information about the requested charm.
   750  func (c *Client) CharmInfo(args params.CharmInfo) (api.CharmInfo, error) {
   751  	curl, err := charm.ParseURL(args.CharmURL)
   752  	if err != nil {
   753  		return api.CharmInfo{}, err
   754  	}
   755  	charm, err := c.api.stateAccessor.Charm(curl)
   756  	if err != nil {
   757  		return api.CharmInfo{}, err
   758  	}
   759  	info := api.CharmInfo{
   760  		Revision: charm.Revision(),
   761  		URL:      curl.String(),
   762  		Config:   charm.Config(),
   763  		Meta:     charm.Meta(),
   764  		Actions:  charm.Actions(),
   765  	}
   766  	return info, nil
   767  }
   768  
   769  // EnvironmentInfo returns information about the current environment (default
   770  // series and type).
   771  func (c *Client) EnvironmentInfo() (api.EnvironmentInfo, error) {
   772  	state := c.api.stateAccessor
   773  	conf, err := state.EnvironConfig()
   774  	if err != nil {
   775  		return api.EnvironmentInfo{}, err
   776  	}
   777  	env, err := state.Environment()
   778  	if err != nil {
   779  		return api.EnvironmentInfo{}, err
   780  	}
   781  
   782  	info := api.EnvironmentInfo{
   783  		DefaultSeries: config.PreferredSeries(conf),
   784  		ProviderType:  conf.Type(),
   785  		Name:          conf.Name(),
   786  		UUID:          env.UUID(),
   787  		ServerUUID:    env.ServerUUID(),
   788  	}
   789  	return info, nil
   790  }
   791  
   792  // ShareEnvironment manages allowing and denying the given user(s) access to the environment.
   793  func (c *Client) ShareEnvironment(args params.ModifyEnvironUsers) (result params.ErrorResults, err error) {
   794  	var createdBy names.UserTag
   795  	var ok bool
   796  	if createdBy, ok = c.api.auth.GetAuthTag().(names.UserTag); !ok {
   797  		return result, errors.Errorf("api connection is not through a user")
   798  	}
   799  
   800  	result = params.ErrorResults{
   801  		Results: make([]params.ErrorResult, len(args.Changes)),
   802  	}
   803  	if len(args.Changes) == 0 {
   804  		return result, nil
   805  	}
   806  
   807  	for i, arg := range args.Changes {
   808  		userTagString := arg.UserTag
   809  		user, err := names.ParseUserTag(userTagString)
   810  		if err != nil {
   811  			result.Results[i].Error = common.ServerError(errors.Annotate(err, "could not share environment"))
   812  			continue
   813  		}
   814  		switch arg.Action {
   815  		case params.AddEnvUser:
   816  			_, err := c.api.stateAccessor.AddEnvironmentUser(user, createdBy, "")
   817  			if err != nil {
   818  				err = errors.Annotate(err, "could not share environment")
   819  				result.Results[i].Error = common.ServerError(err)
   820  			}
   821  		case params.RemoveEnvUser:
   822  			err := c.api.stateAccessor.RemoveEnvironmentUser(user)
   823  			if err != nil {
   824  				err = errors.Annotate(err, "could not unshare environment")
   825  				result.Results[i].Error = common.ServerError(err)
   826  			}
   827  		default:
   828  			result.Results[i].Error = common.ServerError(errors.Errorf("unknown action %q", arg.Action))
   829  		}
   830  	}
   831  	return result, nil
   832  }
   833  
   834  // EnvUserInfo returns information on all users in the environment.
   835  func (c *Client) EnvUserInfo() (params.EnvUserInfoResults, error) {
   836  	var results params.EnvUserInfoResults
   837  	env, err := c.api.stateAccessor.Environment()
   838  	if err != nil {
   839  		return results, errors.Trace(err)
   840  	}
   841  	users, err := env.Users()
   842  	if err != nil {
   843  		return results, errors.Trace(err)
   844  	}
   845  
   846  	for _, user := range users {
   847  		var lastConn *time.Time
   848  		userLastConn, err := user.LastConnection()
   849  		if err != nil {
   850  			if !state.IsNeverConnectedError(err) {
   851  				return results, errors.Trace(err)
   852  			}
   853  		} else {
   854  			lastConn = &userLastConn
   855  		}
   856  		results.Results = append(results.Results, params.EnvUserInfoResult{
   857  			Result: &params.EnvUserInfo{
   858  				UserName:       user.UserName(),
   859  				DisplayName:    user.DisplayName(),
   860  				CreatedBy:      user.CreatedBy(),
   861  				DateCreated:    user.DateCreated(),
   862  				LastConnection: lastConn,
   863  			},
   864  		})
   865  	}
   866  	return results, nil
   867  }
   868  
   869  // GetAnnotations returns annotations about a given entity.
   870  // This API is now deprecated - "Annotations" client should be used instead.
   871  // TODO(anastasiamac) remove for Juju 2.x
   872  func (c *Client) GetAnnotations(args params.GetAnnotations) (params.GetAnnotationsResults, error) {
   873  	nothing := params.GetAnnotationsResults{}
   874  	tag, err := c.parseEntityTag(args.Tag)
   875  	if err != nil {
   876  		return nothing, errors.Trace(err)
   877  	}
   878  	entity, err := c.findEntity(tag)
   879  	if err != nil {
   880  		return nothing, errors.Trace(err)
   881  	}
   882  	ann, err := c.api.stateAccessor.Annotations(entity)
   883  	if err != nil {
   884  		return nothing, errors.Trace(err)
   885  	}
   886  	return params.GetAnnotationsResults{Annotations: ann}, nil
   887  }
   888  
   889  func (c *Client) parseEntityTag(tag0 string) (names.Tag, error) {
   890  	tag, err := names.ParseTag(tag0)
   891  	if err != nil {
   892  		return nil, errors.Trace(err)
   893  	}
   894  	if tag.Kind() == names.CharmTagKind {
   895  		return nil, common.NotSupportedError(tag, "client.annotations")
   896  	}
   897  	return tag, nil
   898  }
   899  
   900  func (c *Client) findEntity(tag names.Tag) (state.GlobalEntity, error) {
   901  	entity0, err := c.api.stateAccessor.FindEntity(tag)
   902  	if err != nil {
   903  		return nil, err
   904  	}
   905  	entity, ok := entity0.(state.GlobalEntity)
   906  	if !ok {
   907  		return nil, common.NotSupportedError(tag, "annotations")
   908  	}
   909  	return entity, nil
   910  }
   911  
   912  // SetAnnotations stores annotations about a given entity.
   913  // This API is now deprecated - "Annotations" client should be used instead.
   914  // TODO(anastasiamac) remove for Juju 2.x
   915  func (c *Client) SetAnnotations(args params.SetAnnotations) error {
   916  	tag, err := c.parseEntityTag(args.Tag)
   917  	if err != nil {
   918  		return errors.Trace(err)
   919  	}
   920  	entity, err := c.findEntity(tag)
   921  	if err != nil {
   922  		return errors.Trace(err)
   923  	}
   924  	return c.api.stateAccessor.SetAnnotations(entity, args.Pairs)
   925  }
   926  
   927  // AgentVersion returns the current version that the API server is running.
   928  func (c *Client) AgentVersion() (params.AgentVersionResult, error) {
   929  	return params.AgentVersionResult{Version: version.Current.Number}, nil
   930  }
   931  
   932  // EnvironmentGet implements the server-side part of the
   933  // get-environment CLI command.
   934  func (c *Client) EnvironmentGet() (params.EnvironmentConfigResults, error) {
   935  	result := params.EnvironmentConfigResults{}
   936  	// Get the existing environment config from the state.
   937  	config, err := c.api.stateAccessor.EnvironConfig()
   938  	if err != nil {
   939  		return result, err
   940  	}
   941  	result.Config = config.AllAttrs()
   942  	return result, nil
   943  }
   944  
   945  // EnvironmentSet implements the server-side part of the
   946  // set-environment CLI command.
   947  func (c *Client) EnvironmentSet(args params.EnvironmentSet) error {
   948  	if err := c.check.ChangeAllowed(); err != nil {
   949  		return errors.Trace(err)
   950  	}
   951  	// Make sure we don't allow changing agent-version.
   952  	checkAgentVersion := func(updateAttrs map[string]interface{}, removeAttrs []string, oldConfig *config.Config) error {
   953  		if v, found := updateAttrs["agent-version"]; found {
   954  			oldVersion, _ := oldConfig.AgentVersion()
   955  			if v != oldVersion.String() {
   956  				return fmt.Errorf("agent-version cannot be changed")
   957  			}
   958  		}
   959  		return nil
   960  	}
   961  	// Replace any deprecated attributes with their new values.
   962  	attrs := config.ProcessDeprecatedAttributes(args.Config)
   963  	// TODO(waigani) 2014-3-11 #1167616
   964  	// Add a txn retry loop to ensure that the settings on disk have not
   965  	// changed underneath us.
   966  	return c.api.stateAccessor.UpdateEnvironConfig(attrs, nil, checkAgentVersion)
   967  }
   968  
   969  // EnvironmentUnset implements the server-side part of the
   970  // set-environment CLI command.
   971  func (c *Client) EnvironmentUnset(args params.EnvironmentUnset) error {
   972  	if err := c.check.ChangeAllowed(); err != nil {
   973  		return errors.Trace(err)
   974  	}
   975  	// TODO(waigani) 2014-3-11 #1167616
   976  	// Add a txn retry loop to ensure that the settings on disk have not
   977  	// changed underneath us.
   978  	return c.api.stateAccessor.UpdateEnvironConfig(nil, args.Keys, nil)
   979  }
   980  
   981  // SetEnvironAgentVersion sets the environment agent version.
   982  func (c *Client) SetEnvironAgentVersion(args params.SetEnvironAgentVersion) error {
   983  	if err := c.check.ChangeAllowed(); err != nil {
   984  		return errors.Trace(err)
   985  	}
   986  	return c.api.stateAccessor.SetEnvironAgentVersion(args.Version)
   987  }
   988  
   989  // AbortCurrentUpgrade aborts and archives the current upgrade
   990  // synchronisation record, if any.
   991  func (c *Client) AbortCurrentUpgrade() error {
   992  	if err := c.check.ChangeAllowed(); err != nil {
   993  		return errors.Trace(err)
   994  	}
   995  	return c.api.stateAccessor.AbortCurrentUpgrade()
   996  }
   997  
   998  // FindTools returns a List containing all tools matching the given parameters.
   999  func (c *Client) FindTools(args params.FindToolsParams) (params.FindToolsResult, error) {
  1000  	return c.api.toolsFinder.FindTools(args)
  1001  }
  1002  
  1003  func destroyErr(desc string, ids, errs []string) error {
  1004  	if len(errs) == 0 {
  1005  		return nil
  1006  	}
  1007  	msg := "some %s were not destroyed"
  1008  	if len(errs) == len(ids) {
  1009  		msg = "no %s were destroyed"
  1010  	}
  1011  	msg = fmt.Sprintf(msg, desc)
  1012  	return fmt.Errorf("%s: %s", msg, strings.Join(errs, "; "))
  1013  }
  1014  
  1015  func (c *Client) AddCharm(args params.CharmURL) error {
  1016  	return service.AddCharmWithAuthorization(c.api.state(), params.AddCharmWithAuthorization{
  1017  		URL: args.URL,
  1018  	})
  1019  }
  1020  
  1021  // AddCharmWithAuthorization adds the given charm URL (which must include revision) to
  1022  // the environment, if it does not exist yet. Local charms are not
  1023  // supported, only charm store URLs. See also AddLocalCharm().
  1024  //
  1025  // The authorization macaroon, args.CharmStoreMacaroon, may be
  1026  // omitted, in which case this call is equivalent to AddCharm.
  1027  func (c *Client) AddCharmWithAuthorization(args params.AddCharmWithAuthorization) error {
  1028  	return service.AddCharmWithAuthorization(c.api.state(), args)
  1029  }
  1030  
  1031  // ResolveCharm resolves the best available charm URLs with series, for charm
  1032  // locations without a series specified.
  1033  func (c *Client) ResolveCharms(args params.ResolveCharms) (params.ResolveCharmResults, error) {
  1034  	return service.ResolveCharms(c.api.state(), args)
  1035  }
  1036  
  1037  // RetryProvisioning marks a provisioning error as transient on the machines.
  1038  func (c *Client) RetryProvisioning(p params.Entities) (params.ErrorResults, error) {
  1039  	if err := c.check.ChangeAllowed(); err != nil {
  1040  		return params.ErrorResults{}, errors.Trace(err)
  1041  	}
  1042  	entityStatus := make([]params.EntityStatusArgs, len(p.Entities))
  1043  	for i, entity := range p.Entities {
  1044  		entityStatus[i] = params.EntityStatusArgs{Tag: entity.Tag, Data: map[string]interface{}{"transient": true}}
  1045  	}
  1046  	return c.api.statusSetter.UpdateStatus(params.SetStatus{
  1047  		Entities: entityStatus,
  1048  	})
  1049  }
  1050  
  1051  // APIHostPorts returns the API host/port addresses stored in state.
  1052  func (c *Client) APIHostPorts() (result params.APIHostPortsResult, err error) {
  1053  	var servers [][]network.HostPort
  1054  	if servers, err = c.api.stateAccessor.APIHostPorts(); err != nil {
  1055  		return params.APIHostPortsResult{}, err
  1056  	}
  1057  	result.Servers = params.FromNetworkHostsPorts(servers)
  1058  	return result, nil
  1059  }
  1060  
  1061  // EnsureAvailability ensures the availability of Juju state servers.
  1062  // DEPRECATED: remove when we stop supporting 1.20 and earlier clients.
  1063  // This API is now on the HighAvailability facade.
  1064  func (c *Client) EnsureAvailability(args params.StateServersSpecs) (params.StateServersChangeResults, error) {
  1065  	if err := c.check.ChangeAllowed(); err != nil {
  1066  		return params.StateServersChangeResults{}, errors.Trace(err)
  1067  	}
  1068  	results := params.StateServersChangeResults{Results: make([]params.StateServersChangeResult, len(args.Specs))}
  1069  	for i, stateServersSpec := range args.Specs {
  1070  		result, err := highavailability.EnsureAvailabilitySingle(c.api.state(), stateServersSpec)
  1071  		results.Results[i].Result = result
  1072  		results.Results[i].Error = common.ServerError(err)
  1073  	}
  1074  	return results, nil
  1075  }
  1076  
  1077  // DestroyEnvironment will try to destroy the current environment.
  1078  // If there is a block on destruction, this method will return an error.
  1079  func (c *Client) DestroyEnvironment() (err error) {
  1080  	if err := c.check.DestroyAllowed(); err != nil {
  1081  		return errors.Trace(err)
  1082  	}
  1083  
  1084  	environTag := c.api.stateAccessor.EnvironTag()
  1085  	return errors.Trace(common.DestroyEnvironment(c.api.state(), environTag))
  1086  }