github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/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  	"io"
     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  	"github.com/juju/utils/featureflag"
    17  	"gopkg.in/juju/charm.v4"
    18  
    19  	"github.com/juju/juju/api"
    20  	"github.com/juju/juju/apiserver/common"
    21  	"github.com/juju/juju/apiserver/highavailability"
    22  	"github.com/juju/juju/apiserver/params"
    23  	"github.com/juju/juju/environs/config"
    24  	"github.com/juju/juju/environs/manual"
    25  	"github.com/juju/juju/instance"
    26  	jjj "github.com/juju/juju/juju"
    27  	"github.com/juju/juju/network"
    28  	"github.com/juju/juju/state"
    29  	"github.com/juju/juju/state/multiwatcher"
    30  	statestorage "github.com/juju/juju/state/storage"
    31  	"github.com/juju/juju/storage"
    32  	"github.com/juju/juju/version"
    33  )
    34  
    35  func init() {
    36  	common.RegisterStandardFacade("Client", 0, NewClient)
    37  }
    38  
    39  var (
    40  	logger = loggo.GetLogger("juju.apiserver.client")
    41  
    42  	newStateStorage = statestorage.NewStorage
    43  )
    44  
    45  type API struct {
    46  	state     *state.State
    47  	auth      common.Authorizer
    48  	resources *common.Resources
    49  	client    *Client
    50  	// statusSetter provides common methods for updating an entity's provisioning status.
    51  	statusSetter *common.StatusSetter
    52  	toolsFinder  *common.ToolsFinder
    53  }
    54  
    55  // Client serves client-specific API methods.
    56  type Client struct {
    57  	api   *API
    58  	check *common.BlockChecker
    59  }
    60  
    61  // NewClient creates a new instance of the Client Facade.
    62  func NewClient(st *state.State, resources *common.Resources, authorizer common.Authorizer) (*Client, error) {
    63  	if !authorizer.AuthClient() {
    64  		return nil, common.ErrPerm
    65  	}
    66  	env, err := st.Environment()
    67  	if err != nil {
    68  		return nil, err
    69  	}
    70  	urlGetter := common.NewToolsURLGetter(env.UUID(), st)
    71  	return &Client{
    72  		api: &API{
    73  			state:        st,
    74  			auth:         authorizer,
    75  			resources:    resources,
    76  			statusSetter: common.NewStatusSetter(st, common.AuthAlways()),
    77  			toolsFinder:  common.NewToolsFinder(st, st, urlGetter),
    78  		},
    79  		check: common.NewBlockChecker(st)}, nil
    80  }
    81  
    82  func (c *Client) WatchAll() (params.AllWatcherId, error) {
    83  	w := c.api.state.Watch()
    84  	return params.AllWatcherId{
    85  		AllWatcherId: c.api.resources.Register(w),
    86  	}, nil
    87  }
    88  
    89  // ServiceSet implements the server side of Client.ServiceSet. Values set to an
    90  // empty string will be unset.
    91  //
    92  // (Deprecated) Use NewServiceSetForClientAPI instead, to preserve values set to
    93  // an empty string, and use ServiceUnset to unset values.
    94  func (c *Client) ServiceSet(p params.ServiceSet) error {
    95  	if err := c.check.ChangeAllowed(); err != nil {
    96  		return errors.Trace(err)
    97  	}
    98  	svc, err := c.api.state.Service(p.ServiceName)
    99  	if err != nil {
   100  		return err
   101  	}
   102  	return serviceSetSettingsStrings(svc, p.Options)
   103  }
   104  
   105  // NewServiceSetForClientAPI implements the server side of
   106  // Client.NewServiceSetForClientAPI. This is exactly like ServiceSet except that
   107  // it does not unset values that are set to an empty string.  ServiceUnset
   108  // should be used for that.
   109  //
   110  // TODO(Nate): rename this to ServiceSet (and remove the deprecated ServiceSet)
   111  // when the GUI handles the new behavior.
   112  // TODO(mattyw, all): This api call should be move to the new service facade. The client api version will then need bumping.
   113  func (c *Client) NewServiceSetForClientAPI(p params.ServiceSet) error {
   114  	svc, err := c.api.state.Service(p.ServiceName)
   115  	if err != nil {
   116  		return err
   117  	}
   118  	return newServiceSetSettingsStringsForClientAPI(svc, p.Options)
   119  }
   120  
   121  // ServiceUnset implements the server side of Client.ServiceUnset.
   122  // TODO(mattyw, all): This api call should be move to the new service facade. The client api version will then need bumping.
   123  func (c *Client) ServiceUnset(p params.ServiceUnset) error {
   124  	if err := c.check.ChangeAllowed(); err != nil {
   125  		return errors.Trace(err)
   126  	}
   127  	svc, err := c.api.state.Service(p.ServiceName)
   128  	if err != nil {
   129  		return err
   130  	}
   131  	settings := make(charm.Settings)
   132  	for _, option := range p.Options {
   133  		settings[option] = nil
   134  	}
   135  	return svc.UpdateConfigSettings(settings)
   136  }
   137  
   138  // ServiceSetYAML implements the server side of Client.ServerSetYAML.
   139  // TODO(mattyw, all): This api call should be move to the new service facade. The client api version will then need bumping.
   140  func (c *Client) ServiceSetYAML(p params.ServiceSetYAML) error {
   141  	if err := c.check.ChangeAllowed(); err != nil {
   142  		return errors.Trace(err)
   143  	}
   144  	svc, err := c.api.state.Service(p.ServiceName)
   145  	if err != nil {
   146  		return err
   147  	}
   148  	return serviceSetSettingsYAML(svc, p.Config)
   149  }
   150  
   151  // ServiceCharmRelations implements the server side of Client.ServiceCharmRelations.
   152  // TODO(mattyw, all): This api call should be move to the new service facade. The client api version will then need bumping.
   153  func (c *Client) ServiceCharmRelations(p params.ServiceCharmRelations) (params.ServiceCharmRelationsResults, error) {
   154  	var results params.ServiceCharmRelationsResults
   155  	service, err := c.api.state.Service(p.ServiceName)
   156  	if err != nil {
   157  		return results, err
   158  	}
   159  	endpoints, err := service.Endpoints()
   160  	if err != nil {
   161  		return results, err
   162  	}
   163  	results.CharmRelations = make([]string, len(endpoints))
   164  	for i, endpoint := range endpoints {
   165  		results.CharmRelations[i] = endpoint.Relation.Name
   166  	}
   167  	return results, nil
   168  }
   169  
   170  // Resolved implements the server side of Client.Resolved.
   171  func (c *Client) Resolved(p params.Resolved) error {
   172  	if err := c.check.ChangeAllowed(); err != nil {
   173  		return errors.Trace(err)
   174  	}
   175  	unit, err := c.api.state.Unit(p.UnitName)
   176  	if err != nil {
   177  		return err
   178  	}
   179  	return unit.Resolve(p.Retry)
   180  }
   181  
   182  // PublicAddress implements the server side of Client.PublicAddress.
   183  func (c *Client) PublicAddress(p params.PublicAddress) (results params.PublicAddressResults, err error) {
   184  	switch {
   185  	case names.IsValidMachine(p.Target):
   186  		machine, err := c.api.state.Machine(p.Target)
   187  		if err != nil {
   188  			return results, err
   189  		}
   190  		addr := network.SelectPublicAddress(machine.Addresses())
   191  		if addr == "" {
   192  			return results, fmt.Errorf("machine %q has no public address", machine)
   193  		}
   194  		return params.PublicAddressResults{PublicAddress: addr}, nil
   195  
   196  	case names.IsValidUnit(p.Target):
   197  		unit, err := c.api.state.Unit(p.Target)
   198  		if err != nil {
   199  			return results, err
   200  		}
   201  		addr, ok := unit.PublicAddress()
   202  		if !ok {
   203  			return results, fmt.Errorf("unit %q has no public address", unit)
   204  		}
   205  		return params.PublicAddressResults{PublicAddress: addr}, nil
   206  	}
   207  	return results, fmt.Errorf("unknown unit or machine %q", p.Target)
   208  }
   209  
   210  // PrivateAddress implements the server side of Client.PrivateAddress.
   211  func (c *Client) PrivateAddress(p params.PrivateAddress) (results params.PrivateAddressResults, err error) {
   212  	switch {
   213  	case names.IsValidMachine(p.Target):
   214  		machine, err := c.api.state.Machine(p.Target)
   215  		if err != nil {
   216  			return results, err
   217  		}
   218  		addr := network.SelectInternalAddress(machine.Addresses(), false)
   219  		if addr == "" {
   220  			return results, fmt.Errorf("machine %q has no internal address", machine)
   221  		}
   222  		return params.PrivateAddressResults{PrivateAddress: addr}, nil
   223  
   224  	case names.IsValidUnit(p.Target):
   225  		unit, err := c.api.state.Unit(p.Target)
   226  		if err != nil {
   227  			return results, err
   228  		}
   229  		addr, ok := unit.PrivateAddress()
   230  		if !ok {
   231  			return results, fmt.Errorf("unit %q has no internal address", unit)
   232  		}
   233  		return params.PrivateAddressResults{PrivateAddress: addr}, nil
   234  	}
   235  	return results, fmt.Errorf("unknown unit or machine %q", p.Target)
   236  }
   237  
   238  // ServiceExpose changes the juju-managed firewall to expose any ports that
   239  // were also explicitly marked by units as open.
   240  // TODO(mattyw, all): This api call should be move to the new service facade. The client api version will then need bumping.
   241  func (c *Client) ServiceExpose(args params.ServiceExpose) error {
   242  	if err := c.check.ChangeAllowed(); err != nil {
   243  		return errors.Trace(err)
   244  	}
   245  	svc, err := c.api.state.Service(args.ServiceName)
   246  	if err != nil {
   247  		return err
   248  	}
   249  	return svc.SetExposed()
   250  }
   251  
   252  // ServiceUnexpose changes the juju-managed firewall to unexpose any ports that
   253  // were also explicitly marked by units as open.
   254  // TODO(mattyw, all): This api call should be move to the new service facade. The client api version will then need bumping.
   255  func (c *Client) ServiceUnexpose(args params.ServiceUnexpose) error {
   256  	if err := c.check.ChangeAllowed(); err != nil {
   257  		return errors.Trace(err)
   258  	}
   259  	svc, err := c.api.state.Service(args.ServiceName)
   260  	if err != nil {
   261  		return err
   262  	}
   263  	return svc.ClearExposed()
   264  }
   265  
   266  var CharmStore charm.Repository = charm.Store
   267  
   268  func networkTagsToNames(tags []string) ([]string, error) {
   269  	netNames := make([]string, len(tags))
   270  	for i, tag := range tags {
   271  		t, err := names.ParseNetworkTag(tag)
   272  		if err != nil {
   273  			return nil, err
   274  		}
   275  		netNames[i] = t.Id()
   276  	}
   277  	return netNames, nil
   278  }
   279  
   280  // ServiceDeploy fetches the charm from the charm store and deploys it.
   281  // AddCharm or AddLocalCharm should be called to add the charm
   282  // before calling ServiceDeploy, although for backward compatibility
   283  // this is not necessary until 1.16 support is removed.
   284  func (c *Client) ServiceDeploy(args params.ServiceDeploy) error {
   285  	if err := c.check.ChangeAllowed(); err != nil {
   286  		return errors.Trace(err)
   287  	}
   288  	curl, err := charm.ParseURL(args.CharmUrl)
   289  	if err != nil {
   290  		return err
   291  	}
   292  	if curl.Revision < 0 {
   293  		return fmt.Errorf("charm url must include revision")
   294  	}
   295  
   296  	if args.ToMachineSpec != "" && names.IsValidMachine(args.ToMachineSpec) {
   297  		_, err = c.api.state.Machine(args.ToMachineSpec)
   298  		if err != nil {
   299  			return errors.Annotatef(err, `cannot deploy "%v" to machine %v`, args.ServiceName, args.ToMachineSpec)
   300  		}
   301  	}
   302  
   303  	// Try to find the charm URL in state first.
   304  	ch, err := c.api.state.Charm(curl)
   305  	if errors.IsNotFound(err) {
   306  		// Remove this whole if block when 1.16 compatibility is dropped.
   307  		if curl.Schema != "cs" {
   308  			return fmt.Errorf(`charm url has unsupported schema %q`, curl.Schema)
   309  		}
   310  		err = c.AddCharm(params.CharmURL{args.CharmUrl})
   311  		if err != nil {
   312  			return err
   313  		}
   314  		ch, err = c.api.state.Charm(curl)
   315  		if err != nil {
   316  			return err
   317  		}
   318  	} else if err != nil {
   319  		return err
   320  	}
   321  
   322  	// TODO(axw) stop checking feature flag once storage has graduated.
   323  	var storageConstraints map[string]storage.Constraints
   324  	if featureflag.Enabled(storage.FeatureFlag) {
   325  		// Validate the storage parameters against the charm metadata,
   326  		// and ensure there are no conflicting parameters.
   327  		if err := validateCharmStorage(args, ch); err != nil {
   328  			return err
   329  		}
   330  		// Handle stores with no corresponding constraints.
   331  		for store, charmStorage := range ch.Meta().Storage {
   332  			if _, ok := args.Storage[store]; ok {
   333  				// TODO(axw) if pool is not specified, we should set it to
   334  				// the environment's default pool.
   335  				continue
   336  			}
   337  			if charmStorage.Shared {
   338  				// TODO(axw) get the environment's default shared storage
   339  				// pool, and create constraints here.
   340  				return errors.Errorf(
   341  					"no constraints specified for shared charm storage %q",
   342  					store,
   343  				)
   344  			}
   345  			// TODO(axw) when storage pools, providers etc. are implemented,
   346  			// and we have a "loop" storage provider, we should create minimal
   347  			// constraints with the "loop" pool here.
   348  			return errors.Errorf(
   349  				"no constraints specified for charm storage %q, loop not implemented",
   350  				store,
   351  			)
   352  		}
   353  		storageConstraints = args.Storage
   354  	}
   355  
   356  	var settings charm.Settings
   357  	if len(args.ConfigYAML) > 0 {
   358  		settings, err = ch.Config().ParseSettingsYAML([]byte(args.ConfigYAML), args.ServiceName)
   359  	} else if len(args.Config) > 0 {
   360  		// Parse config in a compatible way (see function comment).
   361  		settings, err = parseSettingsCompatible(ch, args.Config)
   362  	}
   363  	if err != nil {
   364  		return err
   365  	}
   366  	// Convert network tags to names for any given networks.
   367  	requestedNetworks, err := networkTagsToNames(args.Networks)
   368  	if err != nil {
   369  		return err
   370  	}
   371  
   372  	_, err = jjj.DeployService(c.api.state,
   373  		jjj.DeployServiceParams{
   374  			ServiceName: args.ServiceName,
   375  			// TODO(dfc) ServiceOwner should be a tag
   376  			ServiceOwner:   c.api.auth.GetAuthTag().String(),
   377  			Charm:          ch,
   378  			NumUnits:       args.NumUnits,
   379  			ConfigSettings: settings,
   380  			Constraints:    args.Constraints,
   381  			ToMachineSpec:  args.ToMachineSpec,
   382  			Networks:       requestedNetworks,
   383  			Storage:        storageConstraints,
   384  		})
   385  	return err
   386  }
   387  
   388  // ServiceDeployWithNetworks works exactly like ServiceDeploy, but
   389  // allows specifying networks to include or exclude on the machine
   390  // where the charm gets deployed (either with args.Network or with
   391  // constraints).
   392  func (c *Client) ServiceDeployWithNetworks(args params.ServiceDeploy) error {
   393  	return c.ServiceDeploy(args)
   394  }
   395  
   396  func validateCharmStorage(args params.ServiceDeploy, ch *state.Charm) error {
   397  	if len(args.Storage) == 0 {
   398  		return nil
   399  	}
   400  	if len(args.ToMachineSpec) != 0 {
   401  		// TODO(axw) when we support dynamic disk provisioning, we can
   402  		// relax this. We will need to consult the storage provider to
   403  		// decide whether or not this is allowable.
   404  		return errors.New("cannot specify storage and machine placement")
   405  	}
   406  	// Remaining validation is done in state.AddService.
   407  	return nil
   408  }
   409  
   410  // ServiceUpdate updates the service attributes, including charm URL,
   411  // minimum number of units, settings and constraints.
   412  // All parameters in params.ServiceUpdate except the service name are optional.
   413  func (c *Client) ServiceUpdate(args params.ServiceUpdate) error {
   414  	if !args.ForceCharmUrl {
   415  		if err := c.check.ChangeAllowed(); err != nil {
   416  			return errors.Trace(err)
   417  		}
   418  	}
   419  	service, err := c.api.state.Service(args.ServiceName)
   420  	if err != nil {
   421  		return err
   422  	}
   423  	// Set the charm for the given service.
   424  	if args.CharmUrl != "" {
   425  		if err = c.serviceSetCharm(service, args.CharmUrl, args.ForceCharmUrl); err != nil {
   426  			return err
   427  		}
   428  	}
   429  	// Set the minimum number of units for the given service.
   430  	if args.MinUnits != nil {
   431  		if err = service.SetMinUnits(*args.MinUnits); err != nil {
   432  			return err
   433  		}
   434  	}
   435  	// Set up service's settings.
   436  	if args.SettingsYAML != "" {
   437  		if err = serviceSetSettingsYAML(service, args.SettingsYAML); err != nil {
   438  			return err
   439  		}
   440  	} else if len(args.SettingsStrings) > 0 {
   441  		if err = serviceSetSettingsStrings(service, args.SettingsStrings); err != nil {
   442  			return err
   443  		}
   444  	}
   445  	// Update service's constraints.
   446  	if args.Constraints != nil {
   447  		return service.SetConstraints(*args.Constraints)
   448  	}
   449  	return nil
   450  }
   451  
   452  // serviceSetCharm sets the charm for the given service.
   453  func (c *Client) serviceSetCharm(service *state.Service, url string, force bool) error {
   454  	curl, err := charm.ParseURL(url)
   455  	if err != nil {
   456  		return err
   457  	}
   458  	sch, err := c.api.state.Charm(curl)
   459  	if errors.IsNotFound(err) {
   460  		// Charms should be added before trying to use them, with
   461  		// AddCharm or AddLocalCharm API calls. When they're not,
   462  		// we're reverting to 1.16 compatibility mode.
   463  		return c.serviceSetCharm1dot16(service, curl, force)
   464  	}
   465  	if err != nil {
   466  		return err
   467  	}
   468  	return service.SetCharm(sch, force)
   469  }
   470  
   471  // serviceSetCharm1dot16 sets the charm for the given service in 1.16
   472  // compatibility mode. Remove this when support for 1.16 is dropped.
   473  func (c *Client) serviceSetCharm1dot16(service *state.Service, curl *charm.URL, force bool) error {
   474  	if curl.Schema != "cs" {
   475  		return fmt.Errorf(`charm url has unsupported schema %q`, curl.Schema)
   476  	}
   477  	if curl.Revision < 0 {
   478  		return fmt.Errorf("charm url must include revision")
   479  	}
   480  	err := c.AddCharm(params.CharmURL{curl.String()})
   481  	if err != nil {
   482  		return err
   483  	}
   484  	ch, err := c.api.state.Charm(curl)
   485  	if err != nil {
   486  		return err
   487  	}
   488  	return service.SetCharm(ch, force)
   489  }
   490  
   491  // serviceSetSettingsYAML updates the settings for the given service,
   492  // taking the configuration from a YAML string.
   493  func serviceSetSettingsYAML(service *state.Service, settings string) error {
   494  	ch, _, err := service.Charm()
   495  	if err != nil {
   496  		return err
   497  	}
   498  	changes, err := ch.Config().ParseSettingsYAML([]byte(settings), service.Name())
   499  	if err != nil {
   500  		return err
   501  	}
   502  	return service.UpdateConfigSettings(changes)
   503  }
   504  
   505  // serviceSetSettingsStrings updates the settings for the given service,
   506  // taking the configuration from a map of strings.
   507  func serviceSetSettingsStrings(service *state.Service, settings map[string]string) error {
   508  	ch, _, err := service.Charm()
   509  	if err != nil {
   510  		return err
   511  	}
   512  	// Parse config in a compatible way (see function comment).
   513  	changes, err := parseSettingsCompatible(ch, settings)
   514  	if err != nil {
   515  		return err
   516  	}
   517  	return service.UpdateConfigSettings(changes)
   518  }
   519  
   520  // newServiceSetSettingsStringsForClientAPI updates the settings for the given
   521  // service, taking the configuration from a map of strings.
   522  //
   523  // TODO(Nate): replace serviceSetSettingsStrings with this onces the GUI no
   524  // longer expects to be able to unset values by sending an empty string.
   525  func newServiceSetSettingsStringsForClientAPI(service *state.Service, settings map[string]string) error {
   526  	ch, _, err := service.Charm()
   527  	if err != nil {
   528  		return err
   529  	}
   530  
   531  	// Validate the settings.
   532  	changes, err := ch.Config().ParseSettingsStrings(settings)
   533  	if err != nil {
   534  		return err
   535  	}
   536  
   537  	return service.UpdateConfigSettings(changes)
   538  }
   539  
   540  // ServiceSetCharm sets the charm for a given service.
   541  // TODO(mattyw, all): This api call should be move to the new service facade. The client api version will then need bumping.
   542  func (c *Client) ServiceSetCharm(args params.ServiceSetCharm) error {
   543  	// when forced, don't block
   544  	if !args.Force {
   545  		if err := c.check.ChangeAllowed(); err != nil {
   546  			return errors.Trace(err)
   547  		}
   548  	}
   549  	service, err := c.api.state.Service(args.ServiceName)
   550  	if err != nil {
   551  		return err
   552  	}
   553  	return c.serviceSetCharm(service, args.CharmUrl, args.Force)
   554  }
   555  
   556  // addServiceUnits adds a given number of units to a service.
   557  func addServiceUnits(state *state.State, args params.AddServiceUnits) ([]*state.Unit, error) {
   558  	service, err := state.Service(args.ServiceName)
   559  	if err != nil {
   560  		return nil, err
   561  	}
   562  	if args.NumUnits < 1 {
   563  		return nil, fmt.Errorf("must add at least one unit")
   564  	}
   565  	if args.NumUnits > 1 && args.ToMachineSpec != "" {
   566  		return nil, fmt.Errorf("cannot use NumUnits with ToMachineSpec")
   567  	}
   568  
   569  	if args.ToMachineSpec != "" && names.IsValidMachine(args.ToMachineSpec) {
   570  		_, err = state.Machine(args.ToMachineSpec)
   571  		if err != nil {
   572  			return nil, errors.Annotatef(err, `cannot add units for service "%v" to machine %v`, args.ServiceName, args.ToMachineSpec)
   573  		}
   574  	}
   575  	return jjj.AddUnits(state, service, args.NumUnits, args.ToMachineSpec)
   576  }
   577  
   578  // AddServiceUnits adds a given number of units to a service.
   579  func (c *Client) AddServiceUnits(args params.AddServiceUnits) (params.AddServiceUnitsResults, error) {
   580  	if err := c.check.ChangeAllowed(); err != nil {
   581  		return params.AddServiceUnitsResults{}, errors.Trace(err)
   582  	}
   583  	units, err := addServiceUnits(c.api.state, args)
   584  	if err != nil {
   585  		return params.AddServiceUnitsResults{}, err
   586  	}
   587  	unitNames := make([]string, len(units))
   588  	for i, unit := range units {
   589  		unitNames[i] = unit.String()
   590  	}
   591  	return params.AddServiceUnitsResults{Units: unitNames}, nil
   592  }
   593  
   594  // DestroyServiceUnits removes a given set of service units.
   595  func (c *Client) DestroyServiceUnits(args params.DestroyServiceUnits) error {
   596  	if err := c.check.RemoveAllowed(); err != nil {
   597  		return errors.Trace(err)
   598  	}
   599  	var errs []string
   600  	for _, name := range args.UnitNames {
   601  		unit, err := c.api.state.Unit(name)
   602  		switch {
   603  		case errors.IsNotFound(err):
   604  			err = fmt.Errorf("unit %q does not exist", name)
   605  		case err != nil:
   606  		case unit.Life() != state.Alive:
   607  			continue
   608  		case unit.IsPrincipal():
   609  			err = unit.Destroy()
   610  		default:
   611  			err = fmt.Errorf("unit %q is a subordinate", name)
   612  		}
   613  		if err != nil {
   614  			errs = append(errs, err.Error())
   615  		}
   616  	}
   617  	return destroyErr("units", args.UnitNames, errs)
   618  }
   619  
   620  // ServiceDestroy destroys a given service.
   621  // TODO(mattyw, all): This api call should be move to the new service facade. The client api version will then need bumping.
   622  func (c *Client) ServiceDestroy(args params.ServiceDestroy) error {
   623  	if err := c.check.RemoveAllowed(); err != nil {
   624  		return errors.Trace(err)
   625  	}
   626  	svc, err := c.api.state.Service(args.ServiceName)
   627  	if err != nil {
   628  		return err
   629  	}
   630  	return svc.Destroy()
   631  }
   632  
   633  // GetServiceConstraints returns the constraints for a given service.
   634  // TODO(mattyw, all): This api call should be move to the new service facade. The client api version will then need bumping.
   635  func (c *Client) GetServiceConstraints(args params.GetServiceConstraints) (params.GetConstraintsResults, error) {
   636  	svc, err := c.api.state.Service(args.ServiceName)
   637  	if err != nil {
   638  		return params.GetConstraintsResults{}, err
   639  	}
   640  	cons, err := svc.Constraints()
   641  	return params.GetConstraintsResults{cons}, err
   642  }
   643  
   644  // GetEnvironmentConstraints returns the constraints for the environment.
   645  func (c *Client) GetEnvironmentConstraints() (params.GetConstraintsResults, error) {
   646  	cons, err := c.api.state.EnvironConstraints()
   647  	if err != nil {
   648  		return params.GetConstraintsResults{}, err
   649  	}
   650  	return params.GetConstraintsResults{cons}, nil
   651  }
   652  
   653  // SetServiceConstraints sets the constraints for a given service.
   654  // TODO(mattyw, all): This api call should be move to the new service facade. The client api version will then need bumping.
   655  func (c *Client) SetServiceConstraints(args params.SetConstraints) error {
   656  	if err := c.check.ChangeAllowed(); err != nil {
   657  		return errors.Trace(err)
   658  	}
   659  	svc, err := c.api.state.Service(args.ServiceName)
   660  	if err != nil {
   661  		return err
   662  	}
   663  	return svc.SetConstraints(args.Constraints)
   664  }
   665  
   666  // SetEnvironmentConstraints sets the constraints for the environment.
   667  func (c *Client) SetEnvironmentConstraints(args params.SetConstraints) error {
   668  	if err := c.check.ChangeAllowed(); err != nil {
   669  		return errors.Trace(err)
   670  	}
   671  	return c.api.state.SetEnvironConstraints(args.Constraints)
   672  }
   673  
   674  // AddRelation adds a relation between the specified endpoints and returns the relation info.
   675  func (c *Client) AddRelation(args params.AddRelation) (params.AddRelationResults, error) {
   676  	if err := c.check.ChangeAllowed(); err != nil {
   677  		return params.AddRelationResults{}, errors.Trace(err)
   678  	}
   679  	inEps, err := c.api.state.InferEndpoints(args.Endpoints...)
   680  	if err != nil {
   681  		return params.AddRelationResults{}, err
   682  	}
   683  	rel, err := c.api.state.AddRelation(inEps...)
   684  	if err != nil {
   685  		return params.AddRelationResults{}, err
   686  	}
   687  	outEps := make(map[string]charm.Relation)
   688  	for _, inEp := range inEps {
   689  		outEp, err := rel.Endpoint(inEp.ServiceName)
   690  		if err != nil {
   691  			return params.AddRelationResults{}, err
   692  		}
   693  		outEps[inEp.ServiceName] = outEp.Relation
   694  	}
   695  	return params.AddRelationResults{Endpoints: outEps}, nil
   696  }
   697  
   698  // DestroyRelation removes the relation between the specified endpoints.
   699  func (c *Client) DestroyRelation(args params.DestroyRelation) error {
   700  	if err := c.check.RemoveAllowed(); err != nil {
   701  		return errors.Trace(err)
   702  	}
   703  	eps, err := c.api.state.InferEndpoints(args.Endpoints...)
   704  	if err != nil {
   705  		return err
   706  	}
   707  	rel, err := c.api.state.EndpointsRelation(eps...)
   708  	if err != nil {
   709  		return err
   710  	}
   711  	return rel.Destroy()
   712  }
   713  
   714  // AddMachines adds new machines with the supplied parameters.
   715  func (c *Client) AddMachines(args params.AddMachines) (params.AddMachinesResults, error) {
   716  	return c.AddMachinesV2(args)
   717  }
   718  
   719  // AddMachinesV2 adds new machines with the supplied parameters.
   720  func (c *Client) AddMachinesV2(args params.AddMachines) (params.AddMachinesResults, error) {
   721  	results := params.AddMachinesResults{
   722  		Machines: make([]params.AddMachinesResult, len(args.MachineParams)),
   723  	}
   724  	if err := c.check.ChangeAllowed(); err != nil {
   725  		return results, errors.Trace(err)
   726  	}
   727  	for i, p := range args.MachineParams {
   728  		m, err := c.addOneMachine(p)
   729  		results.Machines[i].Error = common.ServerError(err)
   730  		if err == nil {
   731  			results.Machines[i].Machine = m.Id()
   732  		}
   733  	}
   734  	return results, nil
   735  }
   736  
   737  // InjectMachines injects a machine into state with provisioned status.
   738  func (c *Client) InjectMachines(args params.AddMachines) (params.AddMachinesResults, error) {
   739  	return c.AddMachines(args)
   740  }
   741  
   742  func (c *Client) addOneMachine(p params.AddMachineParams) (*state.Machine, error) {
   743  	if p.ParentId != "" && p.ContainerType == "" {
   744  		return nil, fmt.Errorf("parent machine specified without container type")
   745  	}
   746  	if p.ContainerType != "" && p.Placement != nil {
   747  		return nil, fmt.Errorf("container type and placement are mutually exclusive")
   748  	}
   749  	if p.Placement != nil {
   750  		// Extract container type and parent from container placement directives.
   751  		containerType, err := instance.ParseContainerType(p.Placement.Scope)
   752  		if err == nil {
   753  			p.ContainerType = containerType
   754  			p.ParentId = p.Placement.Directive
   755  			p.Placement = nil
   756  		}
   757  	}
   758  
   759  	if p.ContainerType != "" || p.Placement != nil {
   760  		// Guard against dubious client by making sure that
   761  		// the following attributes can only be set when we're
   762  		// not using placement.
   763  		p.InstanceId = ""
   764  		p.Nonce = ""
   765  		p.HardwareCharacteristics = instance.HardwareCharacteristics{}
   766  		p.Addrs = nil
   767  	}
   768  
   769  	if p.Series == "" {
   770  		conf, err := c.api.state.EnvironConfig()
   771  		if err != nil {
   772  			return nil, err
   773  		}
   774  		p.Series = config.PreferredSeries(conf)
   775  	}
   776  
   777  	var placementDirective string
   778  	if p.Placement != nil {
   779  		env, err := c.api.state.Environment()
   780  		if err != nil {
   781  			return nil, err
   782  		}
   783  		// For 1.21 we should support both UUID and name, and with 1.22
   784  		// just support UUID
   785  		if p.Placement.Scope != env.Name() && p.Placement.Scope != env.UUID() {
   786  			return nil, fmt.Errorf("invalid environment name %q", p.Placement.Scope)
   787  		}
   788  		placementDirective = p.Placement.Directive
   789  	}
   790  
   791  	// TODO(axw) stop checking feature flag once storage has graduated.
   792  	var blockDeviceParams []state.BlockDeviceParams
   793  	if featureflag.Enabled(storage.FeatureFlag) {
   794  		// TODO(axw) unify storage and free block device constraints in state.
   795  		for _, cons := range p.Disks {
   796  			if cons.Pool != "" {
   797  				// TODO(axw) implement pools. If pool is not specified,
   798  				// determine default pool and set here.
   799  				return nil, errors.Errorf("storage pools not implemented")
   800  			}
   801  			if cons.Size == 0 {
   802  				return nil, errors.Errorf("invalid size %v", cons.Size)
   803  			}
   804  			if cons.Count == 0 {
   805  				return nil, errors.Errorf("invalid count %v", cons.Count)
   806  			}
   807  			params := state.BlockDeviceParams{
   808  				Size: cons.Size,
   809  			}
   810  			for i := uint64(0); i < cons.Count; i++ {
   811  				blockDeviceParams = append(blockDeviceParams, params)
   812  			}
   813  		}
   814  	}
   815  
   816  	jobs, err := stateJobs(p.Jobs)
   817  	if err != nil {
   818  		return nil, err
   819  	}
   820  	template := state.MachineTemplate{
   821  		Series:       p.Series,
   822  		Constraints:  p.Constraints,
   823  		BlockDevices: blockDeviceParams,
   824  		InstanceId:   p.InstanceId,
   825  		Jobs:         jobs,
   826  		Nonce:        p.Nonce,
   827  		HardwareCharacteristics: p.HardwareCharacteristics,
   828  		Addresses:               p.Addrs,
   829  		Placement:               placementDirective,
   830  	}
   831  	if p.ContainerType == "" {
   832  		return c.api.state.AddOneMachine(template)
   833  	}
   834  	if p.ParentId != "" {
   835  		return c.api.state.AddMachineInsideMachine(template, p.ParentId, p.ContainerType)
   836  	}
   837  	return c.api.state.AddMachineInsideNewMachine(template, template, p.ContainerType)
   838  }
   839  
   840  func stateJobs(jobs []multiwatcher.MachineJob) ([]state.MachineJob, error) {
   841  	newJobs := make([]state.MachineJob, len(jobs))
   842  	for i, job := range jobs {
   843  		newJob, err := machineJobFromParams(job)
   844  		if err != nil {
   845  			return nil, err
   846  		}
   847  		newJobs[i] = newJob
   848  	}
   849  	return newJobs, nil
   850  }
   851  
   852  // machineJobFromParams returns the job corresponding to multiwatcher.MachineJob.
   853  // TODO(dfc) this function should live in apiserver/params, move there once
   854  // state does not depend on apiserver/params
   855  func machineJobFromParams(job multiwatcher.MachineJob) (state.MachineJob, error) {
   856  	switch job {
   857  	case multiwatcher.JobHostUnits:
   858  		return state.JobHostUnits, nil
   859  	case multiwatcher.JobManageEnviron:
   860  		return state.JobManageEnviron, nil
   861  	case multiwatcher.JobManageNetworking:
   862  		return state.JobManageNetworking, nil
   863  	case multiwatcher.JobManageStateDeprecated:
   864  		// Deprecated in 1.18.
   865  		return state.JobManageStateDeprecated, nil
   866  	default:
   867  		return -1, errors.Errorf("invalid machine job %q", job)
   868  	}
   869  }
   870  
   871  // ProvisioningScript returns a shell script that, when run,
   872  // provisions a machine agent on the machine executing the script.
   873  func (c *Client) ProvisioningScript(args params.ProvisioningScriptParams) (params.ProvisioningScriptResult, error) {
   874  	var result params.ProvisioningScriptResult
   875  	mcfg, err := MachineConfig(c.api.state, args.MachineId, args.Nonce, args.DataDir)
   876  	if err != nil {
   877  		return result, err
   878  	}
   879  
   880  	// Until DisablePackageCommands is retired, for backwards
   881  	// compatibility, we must respect the client's request and
   882  	// override any environment settings the user may have specified.
   883  	// If the client does specify this setting, it will only ever be
   884  	// true. False indicates the client doesn't care and we should use
   885  	// what's specified in the environments.yaml file.
   886  	if args.DisablePackageCommands {
   887  		mcfg.EnableOSRefreshUpdate = false
   888  		mcfg.EnableOSUpgrade = false
   889  	} else if cfg, err := c.api.state.EnvironConfig(); err != nil {
   890  		return result, err
   891  	} else {
   892  		mcfg.EnableOSUpgrade = cfg.EnableOSUpgrade()
   893  		mcfg.EnableOSRefreshUpdate = cfg.EnableOSRefreshUpdate()
   894  	}
   895  
   896  	result.Script, err = manual.ProvisioningScript(mcfg)
   897  	return result, err
   898  }
   899  
   900  // DestroyMachines removes a given set of machines.
   901  func (c *Client) DestroyMachines(args params.DestroyMachines) error {
   902  	var errs []string
   903  	for _, id := range args.MachineNames {
   904  		machine, err := c.api.state.Machine(id)
   905  		switch {
   906  		case errors.IsNotFound(err):
   907  			err = fmt.Errorf("machine %s does not exist", id)
   908  		case err != nil:
   909  		case args.Force:
   910  			err = machine.ForceDestroy()
   911  		case machine.Life() != state.Alive:
   912  			continue
   913  		default:
   914  			{
   915  				if err := c.check.RemoveAllowed(); err != nil {
   916  					return errors.Trace(err)
   917  				}
   918  				err = machine.Destroy()
   919  			}
   920  		}
   921  		if err != nil {
   922  			errs = append(errs, err.Error())
   923  		}
   924  	}
   925  	return destroyErr("machines", args.MachineNames, errs)
   926  }
   927  
   928  // CharmInfo returns information about the requested charm.
   929  func (c *Client) CharmInfo(args params.CharmInfo) (api.CharmInfo, error) {
   930  	curl, err := charm.ParseURL(args.CharmURL)
   931  	if err != nil {
   932  		return api.CharmInfo{}, err
   933  	}
   934  	charm, err := c.api.state.Charm(curl)
   935  	if err != nil {
   936  		return api.CharmInfo{}, err
   937  	}
   938  	info := api.CharmInfo{
   939  		Revision: charm.Revision(),
   940  		URL:      curl.String(),
   941  		Config:   charm.Config(),
   942  		Meta:     charm.Meta(),
   943  		Actions:  charm.Actions(),
   944  	}
   945  	return info, nil
   946  }
   947  
   948  // EnvironmentInfo returns information about the current environment (default
   949  // series and type).
   950  func (c *Client) EnvironmentInfo() (api.EnvironmentInfo, error) {
   951  	state := c.api.state
   952  	conf, err := state.EnvironConfig()
   953  	if err != nil {
   954  		return api.EnvironmentInfo{}, err
   955  	}
   956  	env, err := state.Environment()
   957  	if err != nil {
   958  		return api.EnvironmentInfo{}, err
   959  	}
   960  
   961  	info := api.EnvironmentInfo{
   962  		DefaultSeries: config.PreferredSeries(conf),
   963  		ProviderType:  conf.Type(),
   964  		Name:          conf.Name(),
   965  		UUID:          env.UUID(),
   966  	}
   967  	return info, nil
   968  }
   969  
   970  // ShareEnvironment allows the given user(s) access to the environment.
   971  func (c *Client) ShareEnvironment(args params.ModifyEnvironUsers) (result params.ErrorResults, err error) {
   972  	var createdBy names.UserTag
   973  	var ok bool
   974  	if createdBy, ok = c.api.auth.GetAuthTag().(names.UserTag); !ok {
   975  		return result, errors.Errorf("api connection is not through a user")
   976  	}
   977  
   978  	result = params.ErrorResults{
   979  		Results: make([]params.ErrorResult, len(args.Changes)),
   980  	}
   981  	if len(args.Changes) == 0 {
   982  		return result, nil
   983  	}
   984  
   985  	for i, arg := range args.Changes {
   986  		userTagString := arg.UserTag
   987  		user, err := names.ParseUserTag(userTagString)
   988  		if err != nil {
   989  			result.Results[i].Error = common.ServerError(errors.Annotate(err, "could not share environment"))
   990  			continue
   991  		}
   992  		switch arg.Action {
   993  		case params.AddEnvUser:
   994  			_, err := c.api.state.AddEnvironmentUser(user, createdBy)
   995  			if err != nil {
   996  				err = errors.Annotate(err, "could not share environment")
   997  				result.Results[i].Error = common.ServerError(err)
   998  			}
   999  		case params.RemoveEnvUser:
  1000  			err := c.api.state.RemoveEnvironmentUser(user)
  1001  			if err != nil {
  1002  				err = errors.Annotate(err, "could not unshare environment")
  1003  				result.Results[i].Error = common.ServerError(err)
  1004  			}
  1005  		default:
  1006  			result.Results[i].Error = common.ServerError(errors.Errorf("unknown action %q", arg.Action))
  1007  		}
  1008  	}
  1009  	return result, nil
  1010  }
  1011  
  1012  // GetAnnotations returns annotations about a given entity.
  1013  // This API is now deprecated - "Annotations" client should be used instead.
  1014  // TODO(anastasiamac) remove for Juju 2.x
  1015  func (c *Client) GetAnnotations(args params.GetAnnotations) (params.GetAnnotationsResults, error) {
  1016  	nothing := params.GetAnnotationsResults{}
  1017  	tag, err := c.parseEntityTag(args.Tag)
  1018  	if err != nil {
  1019  		return nothing, errors.Trace(err)
  1020  	}
  1021  	entity, err := c.findEntity(tag)
  1022  	if err != nil {
  1023  		return nothing, errors.Trace(err)
  1024  	}
  1025  	ann, err := c.api.state.Annotations(entity)
  1026  	if err != nil {
  1027  		return nothing, errors.Trace(err)
  1028  	}
  1029  	return params.GetAnnotationsResults{Annotations: ann}, nil
  1030  }
  1031  
  1032  func (c *Client) parseEntityTag(tag0 string) (names.Tag, error) {
  1033  	tag, err := names.ParseTag(tag0)
  1034  	if err != nil {
  1035  		return nil, errors.Trace(err)
  1036  	}
  1037  	if tag.Kind() == names.CharmTagKind {
  1038  		return nil, common.NotSupportedError(tag, "client.annotations")
  1039  	}
  1040  	return tag, nil
  1041  }
  1042  
  1043  func (c *Client) findEntity(tag names.Tag) (state.GlobalEntity, error) {
  1044  	entity0, err := c.api.state.FindEntity(tag)
  1045  	if err != nil {
  1046  		return nil, err
  1047  	}
  1048  	entity, ok := entity0.(state.GlobalEntity)
  1049  	if !ok {
  1050  		return nil, common.NotSupportedError(tag, "annotations")
  1051  	}
  1052  	return entity, nil
  1053  }
  1054  
  1055  // SetAnnotations stores annotations about a given entity.
  1056  // This API is now deprecated - "Annotations" client should be used instead.
  1057  // TODO(anastasiamac) remove for Juju 2.x
  1058  func (c *Client) SetAnnotations(args params.SetAnnotations) error {
  1059  	tag, err := c.parseEntityTag(args.Tag)
  1060  	if err != nil {
  1061  		return errors.Trace(err)
  1062  	}
  1063  	entity, err := c.findEntity(tag)
  1064  	if err != nil {
  1065  		return errors.Trace(err)
  1066  	}
  1067  	return c.api.state.SetAnnotations(entity, args.Pairs)
  1068  }
  1069  
  1070  // parseSettingsCompatible parses setting strings in a way that is
  1071  // compatible with the behavior before this CL based on the issue
  1072  // http://pad.lv/1194945. Until then setting an option to an empty
  1073  // string caused it to reset to the default value. We now allow
  1074  // empty strings as actual values, but we want to preserve the API
  1075  // behavior.
  1076  func parseSettingsCompatible(ch *state.Charm, settings map[string]string) (charm.Settings, error) {
  1077  	setSettings := map[string]string{}
  1078  	unsetSettings := charm.Settings{}
  1079  	// Split settings into those which set and those which unset a value.
  1080  	for name, value := range settings {
  1081  		if value == "" {
  1082  			unsetSettings[name] = nil
  1083  			continue
  1084  		}
  1085  		setSettings[name] = value
  1086  	}
  1087  	// Validate the settings.
  1088  	changes, err := ch.Config().ParseSettingsStrings(setSettings)
  1089  	if err != nil {
  1090  		return nil, err
  1091  	}
  1092  	// Validate the unsettings and merge them into the changes.
  1093  	unsetSettings, err = ch.Config().ValidateSettings(unsetSettings)
  1094  	if err != nil {
  1095  		return nil, err
  1096  	}
  1097  	for name := range unsetSettings {
  1098  		changes[name] = nil
  1099  	}
  1100  	return changes, nil
  1101  }
  1102  
  1103  // AgentVersion returns the current version that the API server is running.
  1104  func (c *Client) AgentVersion() (params.AgentVersionResult, error) {
  1105  	return params.AgentVersionResult{Version: version.Current.Number}, nil
  1106  }
  1107  
  1108  // EnvironmentGet implements the server-side part of the
  1109  // get-environment CLI command.
  1110  func (c *Client) EnvironmentGet() (params.EnvironmentConfigResults, error) {
  1111  	result := params.EnvironmentConfigResults{}
  1112  	// Get the existing environment config from the state.
  1113  	config, err := c.api.state.EnvironConfig()
  1114  	if err != nil {
  1115  		return result, err
  1116  	}
  1117  	result.Config = config.AllAttrs()
  1118  	return result, nil
  1119  }
  1120  
  1121  // EnvironmentSet implements the server-side part of the
  1122  // set-environment CLI command.
  1123  func (c *Client) EnvironmentSet(args params.EnvironmentSet) error {
  1124  	if err := c.check.ChangeAllowed(); err != nil {
  1125  		// if trying to change value for block-changes, we would want to let it go.
  1126  		if v, present := args.Config[config.PreventAllChangesKey]; !present {
  1127  			return errors.Trace(err)
  1128  		} else if block, ok := v.(bool); ok && block {
  1129  			// still want to block changes
  1130  			return errors.Trace(err)
  1131  		}
  1132  		// else if block is false, we want to unblock changes
  1133  	}
  1134  	// Make sure we don't allow changing agent-version.
  1135  	checkAgentVersion := func(updateAttrs map[string]interface{}, removeAttrs []string, oldConfig *config.Config) error {
  1136  		if v, found := updateAttrs["agent-version"]; found {
  1137  			oldVersion, _ := oldConfig.AgentVersion()
  1138  			if v != oldVersion.String() {
  1139  				return fmt.Errorf("agent-version cannot be changed")
  1140  			}
  1141  		}
  1142  		return nil
  1143  	}
  1144  	// Replace any deprecated attributes with their new values.
  1145  	attrs := config.ProcessDeprecatedAttributes(args.Config)
  1146  	// TODO(waigani) 2014-3-11 #1167616
  1147  	// Add a txn retry loop to ensure that the settings on disk have not
  1148  	// changed underneath us.
  1149  	return c.api.state.UpdateEnvironConfig(attrs, nil, checkAgentVersion)
  1150  }
  1151  
  1152  // EnvironmentUnset implements the server-side part of the
  1153  // set-environment CLI command.
  1154  func (c *Client) EnvironmentUnset(args params.EnvironmentUnset) error {
  1155  	if err := c.check.ChangeAllowed(); err != nil {
  1156  		return errors.Trace(err)
  1157  	}
  1158  	// TODO(waigani) 2014-3-11 #1167616
  1159  	// Add a txn retry loop to ensure that the settings on disk have not
  1160  	// changed underneath us.
  1161  	return c.api.state.UpdateEnvironConfig(nil, args.Keys, nil)
  1162  }
  1163  
  1164  // SetEnvironAgentVersion sets the environment agent version.
  1165  func (c *Client) SetEnvironAgentVersion(args params.SetEnvironAgentVersion) error {
  1166  	if err := c.check.ChangeAllowed(); err != nil {
  1167  		return errors.Trace(err)
  1168  	}
  1169  	return c.api.state.SetEnvironAgentVersion(args.Version)
  1170  }
  1171  
  1172  // AbortCurrentUpgrade aborts and archives the current upgrade
  1173  // synchronisation record, if any.
  1174  func (c *Client) AbortCurrentUpgrade() error {
  1175  	if err := c.check.ChangeAllowed(); err != nil {
  1176  		return errors.Trace(err)
  1177  	}
  1178  	return c.api.state.AbortCurrentUpgrade()
  1179  }
  1180  
  1181  // FindTools returns a List containing all tools matching the given parameters.
  1182  func (c *Client) FindTools(args params.FindToolsParams) (params.FindToolsResult, error) {
  1183  	return c.api.toolsFinder.FindTools(args)
  1184  }
  1185  
  1186  func destroyErr(desc string, ids, errs []string) error {
  1187  	if len(errs) == 0 {
  1188  		return nil
  1189  	}
  1190  	msg := "some %s were not destroyed"
  1191  	if len(errs) == len(ids) {
  1192  		msg = "no %s were destroyed"
  1193  	}
  1194  	msg = fmt.Sprintf(msg, desc)
  1195  	return fmt.Errorf("%s: %s", msg, strings.Join(errs, "; "))
  1196  }
  1197  
  1198  // AddCharm adds the given charm URL (which must include revision) to
  1199  // the environment, if it does not exist yet. Local charms are not
  1200  // supported, only charm store URLs. See also AddLocalCharm().
  1201  func (c *Client) AddCharm(args params.CharmURL) error {
  1202  	charmURL, err := charm.ParseURL(args.URL)
  1203  	if err != nil {
  1204  		return err
  1205  	}
  1206  	if charmURL.Schema != "cs" {
  1207  		return fmt.Errorf("only charm store charm URLs are supported, with cs: schema")
  1208  	}
  1209  	if charmURL.Revision < 0 {
  1210  		return fmt.Errorf("charm URL must include revision")
  1211  	}
  1212  
  1213  	// First, check if a pending or a real charm exists in state.
  1214  	stateCharm, err := c.api.state.PrepareStoreCharmUpload(charmURL)
  1215  	if err == nil && stateCharm.IsUploaded() {
  1216  		// Charm already in state (it was uploaded already).
  1217  		return nil
  1218  	} else if err != nil {
  1219  		return err
  1220  	}
  1221  
  1222  	// Get the charm and its information from the store.
  1223  	envConfig, err := c.api.state.EnvironConfig()
  1224  	if err != nil {
  1225  		return err
  1226  	}
  1227  	config.SpecializeCharmRepo(CharmStore, envConfig)
  1228  	downloadedCharm, err := CharmStore.Get(charmURL)
  1229  	if err != nil {
  1230  		return errors.Annotatef(err, "cannot download charm %q", charmURL.String())
  1231  	}
  1232  
  1233  	// Open it and calculate the SHA256 hash.
  1234  	downloadedBundle, ok := downloadedCharm.(*charm.CharmArchive)
  1235  	if !ok {
  1236  		return errors.Errorf("expected a charm archive, got %T", downloadedCharm)
  1237  	}
  1238  	archive, err := os.Open(downloadedBundle.Path)
  1239  	if err != nil {
  1240  		return errors.Annotate(err, "cannot read downloaded charm")
  1241  	}
  1242  	defer archive.Close()
  1243  	bundleSHA256, size, err := utils.ReadSHA256(archive)
  1244  	if err != nil {
  1245  		return errors.Annotate(err, "cannot calculate SHA256 hash of charm")
  1246  	}
  1247  	if _, err := archive.Seek(0, 0); err != nil {
  1248  		return errors.Annotate(err, "cannot rewind charm archive")
  1249  	}
  1250  
  1251  	// Store the charm archive in environment storage.
  1252  	return StoreCharmArchive(
  1253  		c.api.state,
  1254  		charmURL,
  1255  		downloadedCharm,
  1256  		archive,
  1257  		size,
  1258  		bundleSHA256,
  1259  	)
  1260  }
  1261  
  1262  // StoreCharmArchive stores a charm archive in environment storage.
  1263  func StoreCharmArchive(st *state.State, curl *charm.URL, ch charm.Charm, r io.Reader, size int64, sha256 string) error {
  1264  	storage := newStateStorage(st.EnvironUUID(), st.MongoSession())
  1265  	storagePath, err := charmArchiveStoragePath(curl)
  1266  	if err != nil {
  1267  		return errors.Annotate(err, "cannot generate charm archive name")
  1268  	}
  1269  	if err := storage.Put(storagePath, r, size); err != nil {
  1270  		return errors.Annotate(err, "cannot add charm to storage")
  1271  	}
  1272  
  1273  	// Now update the charm data in state and mark it as no longer pending.
  1274  	_, err = st.UpdateUploadedCharm(ch, curl, storagePath, sha256)
  1275  	if err != nil {
  1276  		alreadyUploaded := err == state.ErrCharmRevisionAlreadyModified ||
  1277  			errors.Cause(err) == state.ErrCharmRevisionAlreadyModified ||
  1278  			state.IsCharmAlreadyUploadedError(err)
  1279  		if err := storage.Remove(storagePath); err != nil {
  1280  			if alreadyUploaded {
  1281  				logger.Errorf("cannot remove duplicated charm archive from storage: %v", err)
  1282  			} else {
  1283  				logger.Errorf("cannot remove unsuccessfully recorded charm archive from storage: %v", err)
  1284  			}
  1285  		}
  1286  		if alreadyUploaded {
  1287  			// Somebody else managed to upload and update the charm in
  1288  			// state before us. This is not an error.
  1289  			return nil
  1290  		}
  1291  	}
  1292  	return nil
  1293  }
  1294  
  1295  func (c *Client) ResolveCharms(args params.ResolveCharms) (params.ResolveCharmResults, error) {
  1296  	var results params.ResolveCharmResults
  1297  
  1298  	envConfig, err := c.api.state.EnvironConfig()
  1299  	if err != nil {
  1300  		return params.ResolveCharmResults{}, err
  1301  	}
  1302  	config.SpecializeCharmRepo(CharmStore, envConfig)
  1303  
  1304  	for _, ref := range args.References {
  1305  		result := params.ResolveCharmResult{}
  1306  		curl, err := c.resolveCharm(&ref, CharmStore)
  1307  		if err != nil {
  1308  			result.Error = err.Error()
  1309  		} else {
  1310  			result.URL = curl
  1311  		}
  1312  		results.URLs = append(results.URLs, result)
  1313  	}
  1314  	return results, nil
  1315  }
  1316  
  1317  func (c *Client) resolveCharm(ref *charm.Reference, repo charm.Repository) (*charm.URL, error) {
  1318  	if ref.Schema != "cs" {
  1319  		return nil, fmt.Errorf("only charm store charm references are supported, with cs: schema")
  1320  	}
  1321  
  1322  	// Resolve the charm location with the repository.
  1323  	return repo.Resolve(ref)
  1324  }
  1325  
  1326  // charmArchiveStoragePath returns a string that is suitable as a
  1327  // storage path, using a random UUID to avoid colliding with concurrent
  1328  // uploads.
  1329  func charmArchiveStoragePath(curl *charm.URL) (string, error) {
  1330  	uuid, err := utils.NewUUID()
  1331  	if err != nil {
  1332  		return "", err
  1333  	}
  1334  	return fmt.Sprintf("charms/%s-%s", curl.String(), uuid), nil
  1335  }
  1336  
  1337  // RetryProvisioning marks a provisioning error as transient on the machines.
  1338  func (c *Client) RetryProvisioning(p params.Entities) (params.ErrorResults, error) {
  1339  	if err := c.check.ChangeAllowed(); err != nil {
  1340  		return params.ErrorResults{}, errors.Trace(err)
  1341  	}
  1342  	entityStatus := make([]params.EntityStatus, len(p.Entities))
  1343  	for i, entity := range p.Entities {
  1344  		entityStatus[i] = params.EntityStatus{Tag: entity.Tag, Data: map[string]interface{}{"transient": true}}
  1345  	}
  1346  	return c.api.statusSetter.UpdateStatus(params.SetStatus{
  1347  		Entities: entityStatus,
  1348  	})
  1349  }
  1350  
  1351  // APIHostPorts returns the API host/port addresses stored in state.
  1352  func (c *Client) APIHostPorts() (result params.APIHostPortsResult, err error) {
  1353  	if result.Servers, err = c.api.state.APIHostPorts(); err != nil {
  1354  		return params.APIHostPortsResult{}, err
  1355  	}
  1356  	return result, nil
  1357  }
  1358  
  1359  // EnsureAvailability ensures the availability of Juju state servers.
  1360  // DEPRECATED: remove when we stop supporting 1.20 and earlier clients.
  1361  // This API is now on the HighAvailability facade.
  1362  func (c *Client) EnsureAvailability(args params.StateServersSpecs) (params.StateServersChangeResults, error) {
  1363  	if err := c.check.ChangeAllowed(); err != nil {
  1364  		return params.StateServersChangeResults{}, errors.Trace(err)
  1365  	}
  1366  	results := params.StateServersChangeResults{Results: make([]params.StateServersChangeResult, len(args.Specs))}
  1367  	for i, stateServersSpec := range args.Specs {
  1368  		result, err := highavailability.EnsureAvailabilitySingle(c.api.state, stateServersSpec)
  1369  		results.Results[i].Result = result
  1370  		results.Results[i].Error = common.ServerError(err)
  1371  	}
  1372  	return results, nil
  1373  }