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