github.com/rogpeppe/juju@v0.0.0-20140613142852-6337964b789e/state/apiserver/client/client.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package client
     5  
     6  import (
     7  	"fmt"
     8  	"net/url"
     9  	"os"
    10  	"strings"
    11  
    12  	"github.com/juju/charm"
    13  	"github.com/juju/errors"
    14  	"github.com/juju/loggo"
    15  	"github.com/juju/names"
    16  	"github.com/juju/utils"
    17  
    18  	"github.com/juju/juju/environs"
    19  	"github.com/juju/juju/environs/config"
    20  	"github.com/juju/juju/environs/manual"
    21  	envtools "github.com/juju/juju/environs/tools"
    22  	"github.com/juju/juju/instance"
    23  	"github.com/juju/juju/juju"
    24  	"github.com/juju/juju/network"
    25  	"github.com/juju/juju/state"
    26  	"github.com/juju/juju/state/api"
    27  	"github.com/juju/juju/state/api/params"
    28  	"github.com/juju/juju/state/apiserver/common"
    29  	coretools "github.com/juju/juju/tools"
    30  	"github.com/juju/juju/version"
    31  )
    32  
    33  var logger = loggo.GetLogger("juju.state.apiserver.client")
    34  
    35  type API struct {
    36  	state     *state.State
    37  	auth      common.Authorizer
    38  	resources *common.Resources
    39  	client    *Client
    40  	// statusSetter provides common methods for updating an entity's provisioning status.
    41  	statusSetter *common.StatusSetter
    42  }
    43  
    44  // Client serves client-specific API methods.
    45  type Client struct {
    46  	api *API
    47  }
    48  
    49  // NewAPI creates a new instance of the Client API.
    50  func NewAPI(st *state.State, resources *common.Resources, authorizer common.Authorizer) *API {
    51  	r := &API{
    52  		state:        st,
    53  		auth:         authorizer,
    54  		resources:    resources,
    55  		statusSetter: common.NewStatusSetter(st, common.AuthAlways(true)),
    56  	}
    57  	r.client = &Client{
    58  		api: r,
    59  	}
    60  	return r
    61  }
    62  
    63  // Client returns an object that provides access
    64  // to methods accessible to non-agent clients.
    65  func (r *API) Client(id string) (*Client, error) {
    66  	if !r.auth.AuthClient() {
    67  		return nil, common.ErrPerm
    68  	}
    69  	if id != "" {
    70  		// Safeguard id for possible future use.
    71  		return nil, common.ErrBadId
    72  	}
    73  	return r.client, nil
    74  }
    75  
    76  func (c *Client) WatchAll() (params.AllWatcherId, error) {
    77  	w := c.api.state.Watch()
    78  	return params.AllWatcherId{
    79  		AllWatcherId: c.api.resources.Register(w),
    80  	}, nil
    81  }
    82  
    83  // ServiceSet implements the server side of Client.ServiceSet. Values set to an
    84  // empty string will be unset.
    85  //
    86  // (Deprecated) Use NewServiceSetForClientAPI instead, to preserve values set to
    87  // an empty string, and use ServiceUnset to unset values.
    88  func (c *Client) ServiceSet(p params.ServiceSet) error {
    89  	svc, err := c.api.state.Service(p.ServiceName)
    90  	if err != nil {
    91  		return err
    92  	}
    93  	return serviceSetSettingsStrings(svc, p.Options)
    94  }
    95  
    96  // NewServiceSetForClientAPI implements the server side of
    97  // Client.NewServiceSetForClientAPI. This is exactly like ServiceSet except that
    98  // it does not unset values that are set to an empty string.  ServiceUnset
    99  // should be used for that.
   100  //
   101  // TODO(Nate): rename this to ServiceSet (and remove the deprecated ServiceSet)
   102  // when the GUI handles the new behavior.
   103  func (c *Client) NewServiceSetForClientAPI(p params.ServiceSet) error {
   104  	svc, err := c.api.state.Service(p.ServiceName)
   105  	if err != nil {
   106  		return err
   107  	}
   108  	return newServiceSetSettingsStringsForClientAPI(svc, p.Options)
   109  }
   110  
   111  // ServiceUnset implements the server side of Client.ServiceUnset.
   112  func (c *Client) ServiceUnset(p params.ServiceUnset) error {
   113  	svc, err := c.api.state.Service(p.ServiceName)
   114  	if err != nil {
   115  		return err
   116  	}
   117  	settings := make(charm.Settings)
   118  	for _, option := range p.Options {
   119  		settings[option] = nil
   120  	}
   121  	return svc.UpdateConfigSettings(settings)
   122  }
   123  
   124  // ServiceSetYAML implements the server side of Client.ServerSetYAML.
   125  func (c *Client) ServiceSetYAML(p params.ServiceSetYAML) error {
   126  	svc, err := c.api.state.Service(p.ServiceName)
   127  	if err != nil {
   128  		return err
   129  	}
   130  	return serviceSetSettingsYAML(svc, p.Config)
   131  }
   132  
   133  // ServiceCharmRelations implements the server side of Client.ServiceCharmRelations.
   134  func (c *Client) ServiceCharmRelations(p params.ServiceCharmRelations) (params.ServiceCharmRelationsResults, error) {
   135  	var results params.ServiceCharmRelationsResults
   136  	service, err := c.api.state.Service(p.ServiceName)
   137  	if err != nil {
   138  		return results, err
   139  	}
   140  	endpoints, err := service.Endpoints()
   141  	if err != nil {
   142  		return results, err
   143  	}
   144  	results.CharmRelations = make([]string, len(endpoints))
   145  	for i, endpoint := range endpoints {
   146  		results.CharmRelations[i] = endpoint.Relation.Name
   147  	}
   148  	return results, nil
   149  }
   150  
   151  // Resolved implements the server side of Client.Resolved.
   152  func (c *Client) Resolved(p params.Resolved) error {
   153  	unit, err := c.api.state.Unit(p.UnitName)
   154  	if err != nil {
   155  		return err
   156  	}
   157  	return unit.Resolve(p.Retry)
   158  }
   159  
   160  // PublicAddress implements the server side of Client.PublicAddress.
   161  func (c *Client) PublicAddress(p params.PublicAddress) (results params.PublicAddressResults, err error) {
   162  	switch {
   163  	case names.IsMachine(p.Target):
   164  		machine, err := c.api.state.Machine(p.Target)
   165  		if err != nil {
   166  			return results, err
   167  		}
   168  		addr := network.SelectPublicAddress(machine.Addresses())
   169  		if addr == "" {
   170  			return results, fmt.Errorf("machine %q has no public address", machine)
   171  		}
   172  		return params.PublicAddressResults{PublicAddress: addr}, nil
   173  
   174  	case names.IsUnit(p.Target):
   175  		unit, err := c.api.state.Unit(p.Target)
   176  		if err != nil {
   177  			return results, err
   178  		}
   179  		addr, ok := unit.PublicAddress()
   180  		if !ok {
   181  			return results, fmt.Errorf("unit %q has no public address", unit)
   182  		}
   183  		return params.PublicAddressResults{PublicAddress: addr}, nil
   184  	}
   185  	return results, fmt.Errorf("unknown unit or machine %q", p.Target)
   186  }
   187  
   188  // PrivateAddress implements the server side of Client.PrivateAddress.
   189  func (c *Client) PrivateAddress(p params.PrivateAddress) (results params.PrivateAddressResults, err error) {
   190  	switch {
   191  	case names.IsMachine(p.Target):
   192  		machine, err := c.api.state.Machine(p.Target)
   193  		if err != nil {
   194  			return results, err
   195  		}
   196  		addr := network.SelectInternalAddress(machine.Addresses(), false)
   197  		if addr == "" {
   198  			return results, fmt.Errorf("machine %q has no internal address", machine)
   199  		}
   200  		return params.PrivateAddressResults{PrivateAddress: addr}, nil
   201  
   202  	case names.IsUnit(p.Target):
   203  		unit, err := c.api.state.Unit(p.Target)
   204  		if err != nil {
   205  			return results, err
   206  		}
   207  		addr, ok := unit.PrivateAddress()
   208  		if !ok {
   209  			return results, fmt.Errorf("unit %q has no internal address", unit)
   210  		}
   211  		return params.PrivateAddressResults{PrivateAddress: addr}, nil
   212  	}
   213  	return results, fmt.Errorf("unknown unit or machine %q", p.Target)
   214  }
   215  
   216  // ServiceExpose changes the juju-managed firewall to expose any ports that
   217  // were also explicitly marked by units as open.
   218  func (c *Client) ServiceExpose(args params.ServiceExpose) error {
   219  	svc, err := c.api.state.Service(args.ServiceName)
   220  	if err != nil {
   221  		return err
   222  	}
   223  	return svc.SetExposed()
   224  }
   225  
   226  // ServiceUnexpose changes the juju-managed firewall to unexpose any ports that
   227  // were also explicitly marked by units as open.
   228  func (c *Client) ServiceUnexpose(args params.ServiceUnexpose) error {
   229  	svc, err := c.api.state.Service(args.ServiceName)
   230  	if err != nil {
   231  		return err
   232  	}
   233  	return svc.ClearExposed()
   234  }
   235  
   236  var CharmStore charm.Repository = charm.Store
   237  
   238  func networkTagsToNames(tags []string) ([]string, error) {
   239  	netNames := make([]string, len(tags))
   240  	for i, tag := range tags {
   241  		t, err := names.ParseTag(tag, names.NetworkTagKind)
   242  		if err != nil {
   243  			return nil, err
   244  		}
   245  		netNames[i] = t.Id()
   246  	}
   247  	return netNames, nil
   248  }
   249  
   250  // ServiceDeploy fetches the charm from the charm store and deploys it.
   251  // AddCharm or AddLocalCharm should be called to add the charm
   252  // before calling ServiceDeploy, although for backward compatibility
   253  // this is not necessary until 1.16 support is removed.
   254  func (c *Client) ServiceDeploy(args params.ServiceDeploy) error {
   255  	curl, err := charm.ParseURL(args.CharmUrl)
   256  	if err != nil {
   257  		return err
   258  	}
   259  	if curl.Revision < 0 {
   260  		return fmt.Errorf("charm url must include revision")
   261  	}
   262  
   263  	// Try to find the charm URL in state first.
   264  	ch, err := c.api.state.Charm(curl)
   265  	if errors.IsNotFound(err) {
   266  		// Remove this whole if block when 1.16 compatibility is dropped.
   267  		if curl.Schema != "cs" {
   268  			return fmt.Errorf(`charm url has unsupported schema %q`, curl.Schema)
   269  		}
   270  		err = c.AddCharm(params.CharmURL{args.CharmUrl})
   271  		if err != nil {
   272  			return err
   273  		}
   274  		ch, err = c.api.state.Charm(curl)
   275  		if err != nil {
   276  			return err
   277  		}
   278  	} else if err != nil {
   279  		return err
   280  	}
   281  
   282  	var settings charm.Settings
   283  	if len(args.ConfigYAML) > 0 {
   284  		settings, err = ch.Config().ParseSettingsYAML([]byte(args.ConfigYAML), args.ServiceName)
   285  	} else if len(args.Config) > 0 {
   286  		// Parse config in a compatile way (see function comment).
   287  		settings, err = parseSettingsCompatible(ch, args.Config)
   288  	}
   289  	if err != nil {
   290  		return err
   291  	}
   292  	// Convert network tags to names for any given networks.
   293  	requestedNetworks, err := networkTagsToNames(args.Networks)
   294  	if err != nil {
   295  		return err
   296  	}
   297  
   298  	_, err = juju.DeployService(c.api.state,
   299  		juju.DeployServiceParams{
   300  			ServiceName:    args.ServiceName,
   301  			ServiceOwner:   c.api.auth.GetAuthTag(),
   302  			Charm:          ch,
   303  			NumUnits:       args.NumUnits,
   304  			ConfigSettings: settings,
   305  			Constraints:    args.Constraints,
   306  			ToMachineSpec:  args.ToMachineSpec,
   307  			Networks:       requestedNetworks,
   308  		})
   309  	return err
   310  }
   311  
   312  // ServiceDeployWithNetworks works exactly like ServiceDeploy, but
   313  // allows specifying networks to include or exclude on the machine
   314  // where the charm gets deployed (either with args.Network or with
   315  // constraints).
   316  func (c *Client) ServiceDeployWithNetworks(args params.ServiceDeploy) error {
   317  	return c.ServiceDeploy(args)
   318  }
   319  
   320  // ServiceUpdate updates the service attributes, including charm URL,
   321  // minimum number of units, settings and constraints.
   322  // All parameters in params.ServiceUpdate except the service name are optional.
   323  func (c *Client) ServiceUpdate(args params.ServiceUpdate) error {
   324  	service, err := c.api.state.Service(args.ServiceName)
   325  	if err != nil {
   326  		return err
   327  	}
   328  	// Set the charm for the given service.
   329  	if args.CharmUrl != "" {
   330  		if err = c.serviceSetCharm(service, args.CharmUrl, args.ForceCharmUrl); err != nil {
   331  			return err
   332  		}
   333  	}
   334  	// Set the minimum number of units for the given service.
   335  	if args.MinUnits != nil {
   336  		if err = service.SetMinUnits(*args.MinUnits); err != nil {
   337  			return err
   338  		}
   339  	}
   340  	// Set up service's settings.
   341  	if args.SettingsYAML != "" {
   342  		if err = serviceSetSettingsYAML(service, args.SettingsYAML); err != nil {
   343  			return err
   344  		}
   345  	} else if len(args.SettingsStrings) > 0 {
   346  		if err = serviceSetSettingsStrings(service, args.SettingsStrings); err != nil {
   347  			return err
   348  		}
   349  	}
   350  	// Update service's constraints.
   351  	if args.Constraints != nil {
   352  		return service.SetConstraints(*args.Constraints)
   353  	}
   354  	return nil
   355  }
   356  
   357  // serviceSetCharm sets the charm for the given service.
   358  func (c *Client) serviceSetCharm(service *state.Service, url string, force bool) error {
   359  	curl, err := charm.ParseURL(url)
   360  	if err != nil {
   361  		return err
   362  	}
   363  	sch, err := c.api.state.Charm(curl)
   364  	if errors.IsNotFound(err) {
   365  		// Charms should be added before trying to use them, with
   366  		// AddCharm or AddLocalCharm API calls. When they're not,
   367  		// we're reverting to 1.16 compatibility mode.
   368  		return c.serviceSetCharm1dot16(service, curl, force)
   369  	}
   370  	if err != nil {
   371  		return err
   372  	}
   373  	return service.SetCharm(sch, force)
   374  }
   375  
   376  // serviceSetCharm1dot16 sets the charm for the given service in 1.16
   377  // compatibility mode. Remove this when support for 1.16 is dropped.
   378  func (c *Client) serviceSetCharm1dot16(service *state.Service, curl *charm.URL, force bool) error {
   379  	if curl.Schema != "cs" {
   380  		return fmt.Errorf(`charm url has unsupported schema %q`, curl.Schema)
   381  	}
   382  	if curl.Revision < 0 {
   383  		return fmt.Errorf("charm url must include revision")
   384  	}
   385  	err := c.AddCharm(params.CharmURL{curl.String()})
   386  	if err != nil {
   387  		return err
   388  	}
   389  	ch, err := c.api.state.Charm(curl)
   390  	if err != nil {
   391  		return err
   392  	}
   393  	return service.SetCharm(ch, force)
   394  }
   395  
   396  // serviceSetSettingsYAML updates the settings for the given service,
   397  // taking the configuration from a YAML string.
   398  func serviceSetSettingsYAML(service *state.Service, settings string) error {
   399  	ch, _, err := service.Charm()
   400  	if err != nil {
   401  		return err
   402  	}
   403  	changes, err := ch.Config().ParseSettingsYAML([]byte(settings), service.Name())
   404  	if err != nil {
   405  		return err
   406  	}
   407  	return service.UpdateConfigSettings(changes)
   408  }
   409  
   410  // serviceSetSettingsStrings updates the settings for the given service,
   411  // taking the configuration from a map of strings.
   412  func serviceSetSettingsStrings(service *state.Service, settings map[string]string) error {
   413  	ch, _, err := service.Charm()
   414  	if err != nil {
   415  		return err
   416  	}
   417  	// Parse config in a compatible way (see function comment).
   418  	changes, err := parseSettingsCompatible(ch, settings)
   419  	if err != nil {
   420  		return err
   421  	}
   422  	return service.UpdateConfigSettings(changes)
   423  }
   424  
   425  // newServiceSetSettingsStringsForClientAPI updates the settings for the given
   426  // service, taking the configuration from a map of strings.
   427  //
   428  // TODO(Nate): replace serviceSetSettingsStrings with this onces the GUI no
   429  // longer expects to be able to unset values by sending an empty string.
   430  func newServiceSetSettingsStringsForClientAPI(service *state.Service, settings map[string]string) error {
   431  	ch, _, err := service.Charm()
   432  	if err != nil {
   433  		return err
   434  	}
   435  
   436  	// Validate the settings.
   437  	changes, err := ch.Config().ParseSettingsStrings(settings)
   438  	if err != nil {
   439  		return err
   440  	}
   441  
   442  	return service.UpdateConfigSettings(changes)
   443  }
   444  
   445  // ServiceSetCharm sets the charm for a given service.
   446  func (c *Client) ServiceSetCharm(args params.ServiceSetCharm) error {
   447  	service, err := c.api.state.Service(args.ServiceName)
   448  	if err != nil {
   449  		return err
   450  	}
   451  	return c.serviceSetCharm(service, args.CharmUrl, args.Force)
   452  }
   453  
   454  // addServiceUnits adds a given number of units to a service.
   455  func addServiceUnits(state *state.State, args params.AddServiceUnits) ([]*state.Unit, error) {
   456  	service, err := state.Service(args.ServiceName)
   457  	if err != nil {
   458  		return nil, err
   459  	}
   460  	if args.NumUnits < 1 {
   461  		return nil, fmt.Errorf("must add at least one unit")
   462  	}
   463  	if args.NumUnits > 1 && args.ToMachineSpec != "" {
   464  		return nil, fmt.Errorf("cannot use NumUnits with ToMachineSpec")
   465  	}
   466  	return juju.AddUnits(state, service, args.NumUnits, args.ToMachineSpec)
   467  }
   468  
   469  // AddServiceUnits adds a given number of units to a service.
   470  func (c *Client) AddServiceUnits(args params.AddServiceUnits) (params.AddServiceUnitsResults, error) {
   471  	units, err := addServiceUnits(c.api.state, args)
   472  	if err != nil {
   473  		return params.AddServiceUnitsResults{}, err
   474  	}
   475  	unitNames := make([]string, len(units))
   476  	for i, unit := range units {
   477  		unitNames[i] = unit.String()
   478  	}
   479  	return params.AddServiceUnitsResults{Units: unitNames}, nil
   480  }
   481  
   482  // DestroyServiceUnits removes a given set of service units.
   483  func (c *Client) DestroyServiceUnits(args params.DestroyServiceUnits) error {
   484  	var errs []string
   485  	for _, name := range args.UnitNames {
   486  		unit, err := c.api.state.Unit(name)
   487  		switch {
   488  		case errors.IsNotFound(err):
   489  			err = fmt.Errorf("unit %q does not exist", name)
   490  		case err != nil:
   491  		case unit.Life() != state.Alive:
   492  			continue
   493  		case unit.IsPrincipal():
   494  			err = unit.Destroy()
   495  		default:
   496  			err = fmt.Errorf("unit %q is a subordinate", name)
   497  		}
   498  		if err != nil {
   499  			errs = append(errs, err.Error())
   500  		}
   501  	}
   502  	return destroyErr("units", args.UnitNames, errs)
   503  }
   504  
   505  // ServiceDestroy destroys a given service.
   506  func (c *Client) ServiceDestroy(args params.ServiceDestroy) error {
   507  	svc, err := c.api.state.Service(args.ServiceName)
   508  	if err != nil {
   509  		return err
   510  	}
   511  	return svc.Destroy()
   512  }
   513  
   514  // GetServiceConstraints returns the constraints for a given service.
   515  func (c *Client) GetServiceConstraints(args params.GetServiceConstraints) (params.GetConstraintsResults, error) {
   516  	svc, err := c.api.state.Service(args.ServiceName)
   517  	if err != nil {
   518  		return params.GetConstraintsResults{}, err
   519  	}
   520  	cons, err := svc.Constraints()
   521  	return params.GetConstraintsResults{cons}, err
   522  }
   523  
   524  // GetEnvironmentConstraints returns the constraints for the environment.
   525  func (c *Client) GetEnvironmentConstraints() (params.GetConstraintsResults, error) {
   526  	cons, err := c.api.state.EnvironConstraints()
   527  	if err != nil {
   528  		return params.GetConstraintsResults{}, err
   529  	}
   530  	return params.GetConstraintsResults{cons}, nil
   531  }
   532  
   533  // SetServiceConstraints sets the constraints for a given service.
   534  func (c *Client) SetServiceConstraints(args params.SetConstraints) error {
   535  	svc, err := c.api.state.Service(args.ServiceName)
   536  	if err != nil {
   537  		return err
   538  	}
   539  	return svc.SetConstraints(args.Constraints)
   540  }
   541  
   542  // SetEnvironmentConstraints sets the constraints for the environment.
   543  func (c *Client) SetEnvironmentConstraints(args params.SetConstraints) error {
   544  	return c.api.state.SetEnvironConstraints(args.Constraints)
   545  }
   546  
   547  // AddRelation adds a relation between the specified endpoints and returns the relation info.
   548  func (c *Client) AddRelation(args params.AddRelation) (params.AddRelationResults, error) {
   549  	inEps, err := c.api.state.InferEndpoints(args.Endpoints)
   550  	if err != nil {
   551  		return params.AddRelationResults{}, err
   552  	}
   553  	rel, err := c.api.state.AddRelation(inEps...)
   554  	if err != nil {
   555  		return params.AddRelationResults{}, err
   556  	}
   557  	outEps := make(map[string]charm.Relation)
   558  	for _, inEp := range inEps {
   559  		outEp, err := rel.Endpoint(inEp.ServiceName)
   560  		if err != nil {
   561  			return params.AddRelationResults{}, err
   562  		}
   563  		outEps[inEp.ServiceName] = outEp.Relation
   564  	}
   565  	return params.AddRelationResults{Endpoints: outEps}, nil
   566  }
   567  
   568  // DestroyRelation removes the relation between the specified endpoints.
   569  func (c *Client) DestroyRelation(args params.DestroyRelation) error {
   570  	eps, err := c.api.state.InferEndpoints(args.Endpoints)
   571  	if err != nil {
   572  		return err
   573  	}
   574  	rel, err := c.api.state.EndpointsRelation(eps...)
   575  	if err != nil {
   576  		return err
   577  	}
   578  	return rel.Destroy()
   579  }
   580  
   581  // AddMachines adds new machines with the supplied parameters.
   582  func (c *Client) AddMachines(args params.AddMachines) (params.AddMachinesResults, error) {
   583  	return c.AddMachinesV2(args)
   584  }
   585  
   586  // AddMachinesV2 adds new machines with the supplied parameters.
   587  func (c *Client) AddMachinesV2(args params.AddMachines) (params.AddMachinesResults, error) {
   588  	results := params.AddMachinesResults{
   589  		Machines: make([]params.AddMachinesResult, len(args.MachineParams)),
   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  		if p.Placement.Scope != env.Name() {
   648  			return nil, fmt.Errorf("invalid environment name %q", p.Placement.Scope)
   649  		}
   650  		placementDirective = p.Placement.Directive
   651  	}
   652  
   653  	jobs, err := stateJobs(p.Jobs)
   654  	if err != nil {
   655  		return nil, err
   656  	}
   657  	template := state.MachineTemplate{
   658  		Series:      p.Series,
   659  		Constraints: p.Constraints,
   660  		InstanceId:  p.InstanceId,
   661  		Jobs:        jobs,
   662  		Nonce:       p.Nonce,
   663  		HardwareCharacteristics: p.HardwareCharacteristics,
   664  		Addresses:               p.Addrs,
   665  		Placement:               placementDirective,
   666  	}
   667  	if p.ContainerType == "" {
   668  		return c.api.state.AddOneMachine(template)
   669  	}
   670  	if p.ParentId != "" {
   671  		return c.api.state.AddMachineInsideMachine(template, p.ParentId, p.ContainerType)
   672  	}
   673  	return c.api.state.AddMachineInsideNewMachine(template, template, p.ContainerType)
   674  }
   675  
   676  func stateJobs(jobs []params.MachineJob) ([]state.MachineJob, error) {
   677  	newJobs := make([]state.MachineJob, len(jobs))
   678  	for i, job := range jobs {
   679  		newJob, err := state.MachineJobFromParams(job)
   680  		if err != nil {
   681  			return nil, err
   682  		}
   683  		newJobs[i] = newJob
   684  	}
   685  	return newJobs, nil
   686  }
   687  
   688  // ProvisioningScript returns a shell script that, when run,
   689  // provisions a machine agent on the machine executing the script.
   690  func (c *Client) ProvisioningScript(args params.ProvisioningScriptParams) (params.ProvisioningScriptResult, error) {
   691  	var result params.ProvisioningScriptResult
   692  	mcfg, err := MachineConfig(c.api.state, args.MachineId, args.Nonce, args.DataDir)
   693  	if err != nil {
   694  		return result, err
   695  	}
   696  	mcfg.DisablePackageCommands = args.DisablePackageCommands
   697  	result.Script, err = manual.ProvisioningScript(mcfg)
   698  	return result, err
   699  }
   700  
   701  // DestroyMachines removes a given set of machines.
   702  func (c *Client) DestroyMachines(args params.DestroyMachines) error {
   703  	var errs []string
   704  	for _, id := range args.MachineNames {
   705  		machine, err := c.api.state.Machine(id)
   706  		switch {
   707  		case errors.IsNotFound(err):
   708  			err = fmt.Errorf("machine %s does not exist", id)
   709  		case err != nil:
   710  		case args.Force:
   711  			err = machine.ForceDestroy()
   712  		case machine.Life() != state.Alive:
   713  			continue
   714  		default:
   715  			err = machine.Destroy()
   716  		}
   717  		if err != nil {
   718  			errs = append(errs, err.Error())
   719  		}
   720  	}
   721  	return destroyErr("machines", args.MachineNames, errs)
   722  }
   723  
   724  // CharmInfo returns information about the requested charm.
   725  func (c *Client) CharmInfo(args params.CharmInfo) (api.CharmInfo, error) {
   726  	curl, err := charm.ParseURL(args.CharmURL)
   727  	if err != nil {
   728  		return api.CharmInfo{}, err
   729  	}
   730  	charm, err := c.api.state.Charm(curl)
   731  	if err != nil {
   732  		return api.CharmInfo{}, err
   733  	}
   734  	info := api.CharmInfo{
   735  		Revision: charm.Revision(),
   736  		URL:      curl.String(),
   737  		Config:   charm.Config(),
   738  		Meta:     charm.Meta(),
   739  	}
   740  	return info, nil
   741  }
   742  
   743  // EnvironmentInfo returns information about the current environment (default
   744  // series and type).
   745  func (c *Client) EnvironmentInfo() (api.EnvironmentInfo, error) {
   746  	state := c.api.state
   747  	conf, err := state.EnvironConfig()
   748  	if err != nil {
   749  		return api.EnvironmentInfo{}, err
   750  	}
   751  	env, err := state.Environment()
   752  	if err != nil {
   753  		return api.EnvironmentInfo{}, err
   754  	}
   755  
   756  	info := api.EnvironmentInfo{
   757  		DefaultSeries: config.PreferredSeries(conf),
   758  		ProviderType:  conf.Type(),
   759  		Name:          conf.Name(),
   760  		UUID:          env.UUID(),
   761  	}
   762  	return info, nil
   763  }
   764  
   765  // GetAnnotations returns annotations about a given entity.
   766  func (c *Client) GetAnnotations(args params.GetAnnotations) (params.GetAnnotationsResults, error) {
   767  	nothing := params.GetAnnotationsResults{}
   768  	entity, err := c.findEntity(args.Tag)
   769  	if err != nil {
   770  		return nothing, err
   771  	}
   772  	ann, err := entity.Annotations()
   773  	if err != nil {
   774  		return nothing, err
   775  	}
   776  	return params.GetAnnotationsResults{Annotations: ann}, nil
   777  }
   778  
   779  func (c *Client) findEntity(tag string) (state.Annotator, error) {
   780  	entity0, err := c.api.state.FindEntity(tag)
   781  	if err != nil {
   782  		return nil, err
   783  	}
   784  	entity, ok := entity0.(state.Annotator)
   785  	if !ok {
   786  		return nil, common.NotSupportedError(tag, "annotations")
   787  	}
   788  	return entity, nil
   789  }
   790  
   791  // SetAnnotations stores annotations about a given entity.
   792  func (c *Client) SetAnnotations(args params.SetAnnotations) error {
   793  	entity, err := c.findEntity(args.Tag)
   794  	if err != nil {
   795  		return err
   796  	}
   797  	return entity.SetAnnotations(args.Pairs)
   798  }
   799  
   800  // parseSettingsCompatible parses setting strings in a way that is
   801  // compatible with the behavior before this CL based on the issue
   802  // http://pad.lv/1194945. Until then setting an option to an empty
   803  // string caused it to reset to the default value. We now allow
   804  // empty strings as actual values, but we want to preserve the API
   805  // behavior.
   806  func parseSettingsCompatible(ch *state.Charm, settings map[string]string) (charm.Settings, error) {
   807  	setSettings := map[string]string{}
   808  	unsetSettings := charm.Settings{}
   809  	// Split settings into those which set and those which unset a value.
   810  	for name, value := range settings {
   811  		if value == "" {
   812  			unsetSettings[name] = nil
   813  			continue
   814  		}
   815  		setSettings[name] = value
   816  	}
   817  	// Validate the settings.
   818  	changes, err := ch.Config().ParseSettingsStrings(setSettings)
   819  	if err != nil {
   820  		return nil, err
   821  	}
   822  	// Validate the unsettings and merge them into the changes.
   823  	unsetSettings, err = ch.Config().ValidateSettings(unsetSettings)
   824  	if err != nil {
   825  		return nil, err
   826  	}
   827  	for name := range unsetSettings {
   828  		changes[name] = nil
   829  	}
   830  	return changes, nil
   831  }
   832  
   833  // AgentVersion returns the current version that the API server is running.
   834  func (c *Client) AgentVersion() (params.AgentVersionResult, error) {
   835  	return params.AgentVersionResult{Version: version.Current.Number}, nil
   836  }
   837  
   838  // EnvironmentGet implements the server-side part of the
   839  // get-environment CLI command.
   840  func (c *Client) EnvironmentGet() (params.EnvironmentGetResults, error) {
   841  	result := params.EnvironmentGetResults{}
   842  	// Get the existing environment config from the state.
   843  	config, err := c.api.state.EnvironConfig()
   844  	if err != nil {
   845  		return result, err
   846  	}
   847  	result.Config = config.AllAttrs()
   848  	return result, nil
   849  }
   850  
   851  // EnvironmentSet implements the server-side part of the
   852  // set-environment CLI command.
   853  func (c *Client) EnvironmentSet(args params.EnvironmentSet) error {
   854  	// Make sure we don't allow changing agent-version.
   855  	checkAgentVersion := func(updateAttrs map[string]interface{}, removeAttrs []string, oldConfig *config.Config) error {
   856  		if v, found := updateAttrs["agent-version"]; found {
   857  			oldVersion, _ := oldConfig.AgentVersion()
   858  			if v != oldVersion.String() {
   859  				return fmt.Errorf("agent-version cannot be changed")
   860  			}
   861  		}
   862  		return nil
   863  	}
   864  	// TODO(waigani) 2014-3-11 #1167616
   865  	// Add a txn retry loop to ensure that the settings on disk have not
   866  	// changed underneath us.
   867  	return c.api.state.UpdateEnvironConfig(args.Config, nil, checkAgentVersion)
   868  }
   869  
   870  // EnvironmentUnset implements the server-side part of the
   871  // set-environment CLI command.
   872  func (c *Client) EnvironmentUnset(args params.EnvironmentUnset) error {
   873  	// TODO(waigani) 2014-3-11 #1167616
   874  	// Add a txn retry loop to ensure that the settings on disk have not
   875  	// changed underneath us.
   876  	return c.api.state.UpdateEnvironConfig(nil, args.Keys, nil)
   877  }
   878  
   879  // SetEnvironAgentVersion sets the environment agent version.
   880  func (c *Client) SetEnvironAgentVersion(args params.SetEnvironAgentVersion) error {
   881  	return c.api.state.SetEnvironAgentVersion(args.Version)
   882  }
   883  
   884  // FindTools returns a List containing all tools matching the given parameters.
   885  func (c *Client) FindTools(args params.FindToolsParams) (params.FindToolsResults, error) {
   886  	result := params.FindToolsResults{}
   887  	// Get the existing environment config from the state.
   888  	envConfig, err := c.api.state.EnvironConfig()
   889  	if err != nil {
   890  		return result, err
   891  	}
   892  	env, err := environs.New(envConfig)
   893  	if err != nil {
   894  		return result, err
   895  	}
   896  	filter := coretools.Filter{
   897  		Arch:   args.Arch,
   898  		Series: args.Series,
   899  	}
   900  	result.List, err = envtools.FindTools(env, args.MajorVersion, args.MinorVersion, filter, envtools.DoNotAllowRetry)
   901  	result.Error = common.ServerError(err)
   902  	return result, nil
   903  }
   904  
   905  func destroyErr(desc string, ids, errs []string) error {
   906  	if len(errs) == 0 {
   907  		return nil
   908  	}
   909  	msg := "some %s were not destroyed"
   910  	if len(errs) == len(ids) {
   911  		msg = "no %s were destroyed"
   912  	}
   913  	msg = fmt.Sprintf(msg, desc)
   914  	return fmt.Errorf("%s: %s", msg, strings.Join(errs, "; "))
   915  }
   916  
   917  // AddCharm adds the given charm URL (which must include revision) to
   918  // the environment, if it does not exist yet. Local charms are not
   919  // supported, only charm store URLs. See also AddLocalCharm().
   920  func (c *Client) AddCharm(args params.CharmURL) error {
   921  	charmURL, err := charm.ParseURL(args.URL)
   922  	if err != nil {
   923  		return err
   924  	}
   925  	if charmURL.Schema != "cs" {
   926  		return fmt.Errorf("only charm store charm URLs are supported, with cs: schema")
   927  	}
   928  	if charmURL.Revision < 0 {
   929  		return fmt.Errorf("charm URL must include revision")
   930  	}
   931  
   932  	// First, check if a pending or a real charm exists in state.
   933  	stateCharm, err := c.api.state.PrepareStoreCharmUpload(charmURL)
   934  	if err == nil && stateCharm.IsUploaded() {
   935  		// Charm already in state (it was uploaded already).
   936  		return nil
   937  	} else if err != nil {
   938  		return err
   939  	}
   940  
   941  	// Get the charm and its information from the store.
   942  	envConfig, err := c.api.state.EnvironConfig()
   943  	if err != nil {
   944  		return err
   945  	}
   946  	store := config.SpecializeCharmRepo(CharmStore, envConfig)
   947  	downloadedCharm, err := store.Get(charmURL)
   948  	if err != nil {
   949  		return errors.Annotatef(err, "cannot download charm %q", charmURL.String())
   950  	}
   951  
   952  	// Open it and calculate the SHA256 hash.
   953  	downloadedBundle, ok := downloadedCharm.(*charm.Bundle)
   954  	if !ok {
   955  		return errors.Errorf("expected a charm archive, got %T", downloadedCharm)
   956  	}
   957  	archive, err := os.Open(downloadedBundle.Path)
   958  	if err != nil {
   959  		return errors.Annotate(err, "cannot read downloaded charm")
   960  	}
   961  	defer archive.Close()
   962  	bundleSHA256, size, err := utils.ReadSHA256(archive)
   963  	if err != nil {
   964  		return errors.Annotate(err, "cannot calculate SHA256 hash of charm")
   965  	}
   966  	if _, err := archive.Seek(0, 0); err != nil {
   967  		return errors.Annotate(err, "cannot rewind charm archive")
   968  	}
   969  
   970  	// Get the environment storage and upload the charm.
   971  	env, err := environs.New(envConfig)
   972  	if err != nil {
   973  		return errors.Annotate(err, "cannot access environment")
   974  	}
   975  	storage := env.Storage()
   976  	archiveName, err := CharmArchiveName(charmURL.Name, charmURL.Revision)
   977  	if err != nil {
   978  		return errors.Annotate(err, "cannot generate charm archive name")
   979  	}
   980  	if err := storage.Put(archiveName, archive, size); err != nil {
   981  		return errors.Annotate(err, "cannot upload charm to provider storage")
   982  	}
   983  	storageURL, err := storage.URL(archiveName)
   984  	if err != nil {
   985  		return errors.Annotate(err, "cannot get storage URL for charm")
   986  	}
   987  	bundleURL, err := url.Parse(storageURL)
   988  	if err != nil {
   989  		return errors.Annotate(err, "cannot parse storage URL")
   990  	}
   991  
   992  	// Finally, update the charm data in state and mark it as no longer pending.
   993  	_, err = c.api.state.UpdateUploadedCharm(downloadedCharm, charmURL, bundleURL, bundleSHA256)
   994  	if err == state.ErrCharmRevisionAlreadyModified ||
   995  		state.IsCharmAlreadyUploadedError(err) {
   996  		// This is not an error, it just signifies somebody else
   997  		// managed to upload and update the charm in state before
   998  		// us. This means we have to delete what we just uploaded
   999  		// to storage.
  1000  		if err := storage.Remove(archiveName); err != nil {
  1001  			errors.Annotate(err, "cannot remove duplicated charm from storage")
  1002  		}
  1003  		return nil
  1004  	}
  1005  	return err
  1006  }
  1007  
  1008  func (c *Client) ResolveCharms(args params.ResolveCharms) (params.ResolveCharmResults, error) {
  1009  	var results params.ResolveCharmResults
  1010  
  1011  	envConfig, err := c.api.state.EnvironConfig()
  1012  	if err != nil {
  1013  		return params.ResolveCharmResults{}, err
  1014  	}
  1015  	repo := config.SpecializeCharmRepo(CharmStore, envConfig)
  1016  
  1017  	for _, ref := range args.References {
  1018  		result := params.ResolveCharmResult{}
  1019  		curl, err := c.resolveCharm(ref, repo)
  1020  		if err != nil {
  1021  			result.Error = err.Error()
  1022  		} else {
  1023  			result.URL = curl
  1024  		}
  1025  		results.URLs = append(results.URLs, result)
  1026  	}
  1027  	return results, nil
  1028  }
  1029  
  1030  func (c *Client) resolveCharm(ref charm.Reference, repo charm.Repository) (*charm.URL, error) {
  1031  	if ref.Schema != "cs" {
  1032  		return nil, fmt.Errorf("only charm store charm references are supported, with cs: schema")
  1033  	}
  1034  
  1035  	// Resolve the charm location with the repository.
  1036  	return repo.Resolve(ref)
  1037  }
  1038  
  1039  // CharmArchiveName returns a string that is suitable as a file name
  1040  // in a storage URL. It is constructed from the charm name, revision
  1041  // and a random UUID string.
  1042  func CharmArchiveName(name string, revision int) (string, error) {
  1043  	uuid, err := utils.NewUUID()
  1044  	if err != nil {
  1045  		return "", err
  1046  	}
  1047  	return charm.Quote(fmt.Sprintf("%s-%d-%s", name, revision, uuid)), nil
  1048  }
  1049  
  1050  // RetryProvisioning marks a provisioning error as transient on the machines.
  1051  func (c *Client) RetryProvisioning(p params.Entities) (params.ErrorResults, error) {
  1052  	entityStatus := make([]params.EntityStatus, len(p.Entities))
  1053  	for i, entity := range p.Entities {
  1054  		entityStatus[i] = params.EntityStatus{Tag: entity.Tag, Data: params.StatusData{"transient": true}}
  1055  	}
  1056  	return c.api.statusSetter.UpdateStatus(params.SetStatus{
  1057  		Entities: entityStatus,
  1058  	})
  1059  }
  1060  
  1061  // APIHostPorts returns the API host/port addresses stored in state.
  1062  func (c *Client) APIHostPorts() (result params.APIHostPortsResult, err error) {
  1063  	if result.Servers, err = c.api.state.APIHostPorts(); err != nil {
  1064  		return params.APIHostPortsResult{}, err
  1065  	}
  1066  	return result, nil
  1067  }
  1068  
  1069  // EnsureAvailability ensures the availability of Juju state servers.
  1070  func (c *Client) EnsureAvailability(args params.EnsureAvailability) error {
  1071  	series := args.Series
  1072  	if series == "" {
  1073  		ssi, err := c.api.state.StateServerInfo()
  1074  		if err != nil {
  1075  			return err
  1076  		}
  1077  		// We should always have at least one voting machine
  1078  		// If we *really* wanted we could just pick whatever series is
  1079  		// in the majority, but really, if we always copy the value of
  1080  		// the first one, then they'll stay in sync.
  1081  		if len(ssi.VotingMachineIds) == 0 {
  1082  			// Better than a panic()?
  1083  			return fmt.Errorf("internal error, failed to find any voting machines")
  1084  		}
  1085  		templateMachine, err := c.api.state.Machine(ssi.VotingMachineIds[0])
  1086  		if err != nil {
  1087  			return err
  1088  		}
  1089  		series = templateMachine.Series()
  1090  	}
  1091  	return c.api.state.EnsureAvailability(args.NumStateServers, args.Constraints, series)
  1092  }