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