github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/client/application/application.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  // Package application contains api calls for functionality
     5  // related to deploying and managing applications and their
     6  // related charms.
     7  package application
     8  
     9  import (
    10  	"fmt"
    11  	"net"
    12  
    13  	"github.com/juju/errors"
    14  	"github.com/juju/loggo"
    15  	"github.com/juju/schema"
    16  	"gopkg.in/juju/charm.v6"
    17  	csparams "gopkg.in/juju/charmrepo.v3/csclient/params"
    18  	"gopkg.in/juju/environschema.v1"
    19  	"gopkg.in/juju/names.v2"
    20  	"gopkg.in/macaroon.v2-unstable"
    21  	goyaml "gopkg.in/yaml.v2"
    22  
    23  	"github.com/juju/juju/apiserver/common"
    24  	"github.com/juju/juju/apiserver/common/storagecommon"
    25  	"github.com/juju/juju/apiserver/facade"
    26  	"github.com/juju/juju/apiserver/params"
    27  	"github.com/juju/juju/caas"
    28  	k8s "github.com/juju/juju/caas/kubernetes/provider"
    29  	"github.com/juju/juju/core/application"
    30  	"github.com/juju/juju/core/constraints"
    31  	"github.com/juju/juju/core/crossmodel"
    32  	"github.com/juju/juju/core/instance"
    33  	"github.com/juju/juju/core/status"
    34  	"github.com/juju/juju/environs"
    35  	"github.com/juju/juju/network"
    36  	"github.com/juju/juju/permission"
    37  	"github.com/juju/juju/state"
    38  	"github.com/juju/juju/state/stateenvirons"
    39  	"github.com/juju/juju/storage/poolmanager"
    40  )
    41  
    42  var logger = loggo.GetLogger("juju.apiserver.application")
    43  
    44  // APIv4 provides the Application API facade for versions 1-4.
    45  type APIv4 struct {
    46  	*APIv5
    47  }
    48  
    49  // APIv5 provides the Application API facade for version 5.
    50  type APIv5 struct {
    51  	*APIv6
    52  }
    53  
    54  // APIv6 provides the Application API facade for version 6.
    55  type APIv6 struct {
    56  	*APIv7
    57  }
    58  
    59  // APIv7 provides the Application API facade for version 7.
    60  type APIv7 struct {
    61  	*APIv8
    62  }
    63  
    64  // APIv8 provides the Application API facade for version 8.
    65  type APIv8 struct {
    66  	*APIv9
    67  }
    68  
    69  // APIv9 provides the Application API facade for version 9.
    70  type APIv9 struct {
    71  	*APIBase
    72  }
    73  
    74  // APIBase implements the shared application interface and is the concrete
    75  // implementation of the api end point.
    76  //
    77  // API provides the Application API facade for version 5.
    78  type APIBase struct {
    79  	backend       Backend
    80  	storageAccess storageInterface
    81  
    82  	authorizer facade.Authorizer
    83  	check      BlockChecker
    84  
    85  	modelTag  names.ModelTag
    86  	modelType state.ModelType
    87  	modelName string
    88  
    89  	resources facade.Resources
    90  
    91  	// TODO(axw) stateCharm only exists because I ran out
    92  	// of time unwinding all of the tendrils of state. We
    93  	// should pass a charm.Charm and charm.URL back into
    94  	// state wherever we pass in a state.Charm currently.
    95  	stateCharm func(Charm) *state.Charm
    96  
    97  	storagePoolManager    poolmanager.PoolManager
    98  	caasBroker            caas.Broker
    99  	deployApplicationFunc func(ApplicationDeployer, DeployApplicationParams) (Application, error)
   100  }
   101  
   102  // NewFacadeV4 provides the signature required for facade registration
   103  // for versions 1-4.
   104  func NewFacadeV4(ctx facade.Context) (*APIv4, error) {
   105  	api, err := NewFacadeV5(ctx)
   106  	if err != nil {
   107  		return nil, errors.Trace(err)
   108  	}
   109  	return &APIv4{api}, nil
   110  }
   111  
   112  // NewFacadeV5 provides the signature required for facade registration
   113  // for version 5.
   114  func NewFacadeV5(ctx facade.Context) (*APIv5, error) {
   115  	api, err := NewFacadeV6(ctx)
   116  	if err != nil {
   117  		return nil, errors.Trace(err)
   118  	}
   119  	return &APIv5{api}, nil
   120  }
   121  
   122  // NewFacadeV6 provides the signature required for facade registration
   123  // for version 6.
   124  func NewFacadeV6(ctx facade.Context) (*APIv6, error) {
   125  	api, err := NewFacadeV7(ctx)
   126  	if err != nil {
   127  		return nil, errors.Trace(err)
   128  	}
   129  	return &APIv6{api}, nil
   130  }
   131  
   132  // NewFacadeV7 provides the signature required for facade registration
   133  // for version 7.
   134  func NewFacadeV7(ctx facade.Context) (*APIv7, error) {
   135  	api, err := NewFacadeV8(ctx)
   136  	if err != nil {
   137  		return nil, errors.Trace(err)
   138  	}
   139  	return &APIv7{api}, nil
   140  }
   141  
   142  // NewFacadeV8 provides the signature required for facade registration
   143  // for version 8.
   144  func NewFacadeV8(ctx facade.Context) (*APIv8, error) {
   145  	api, err := NewFacadeV9(ctx)
   146  	if err != nil {
   147  		return nil, errors.Trace(err)
   148  	}
   149  	return &APIv8{api}, nil
   150  }
   151  
   152  func NewFacadeV9(ctx facade.Context) (*APIv9, error) {
   153  	api, err := newFacadeBase(ctx)
   154  	if err != nil {
   155  		return nil, errors.Trace(err)
   156  	}
   157  	return &APIv9{api}, nil
   158  }
   159  
   160  func newFacadeBase(ctx facade.Context) (*APIBase, error) {
   161  	model, err := ctx.State().Model()
   162  	if err != nil {
   163  		return nil, errors.Annotate(err, "getting model")
   164  	}
   165  	storageAccess, err := getStorageState(ctx.State())
   166  	if err != nil {
   167  		return nil, errors.Annotate(err, "getting state")
   168  	}
   169  	blockChecker := common.NewBlockChecker(ctx.State())
   170  	stateCharm := CharmToStateCharm
   171  
   172  	var storagePoolManager poolmanager.PoolManager
   173  	var caasBroker caas.Broker
   174  	if model.Type() == state.ModelTypeCAAS {
   175  		caasBroker, err = stateenvirons.GetNewCAASBrokerFunc(caas.New)(ctx.State())
   176  		if err != nil {
   177  			return nil, errors.Annotate(err, "getting caas client")
   178  		}
   179  		storageProviderRegistry := stateenvirons.NewStorageProviderRegistry(caasBroker)
   180  		storagePoolManager = poolmanager.New(state.NewStateSettings(ctx.State()), storageProviderRegistry)
   181  	}
   182  
   183  	resources := ctx.Resources()
   184  
   185  	return NewAPIBase(
   186  		&stateShim{ctx.State()},
   187  		storageAccess,
   188  		ctx.Auth(),
   189  		blockChecker,
   190  		model.ModelTag(),
   191  		model.Type(),
   192  		model.Name(),
   193  		stateCharm,
   194  		DeployApplication,
   195  		storagePoolManager,
   196  		resources,
   197  		caasBroker,
   198  	)
   199  }
   200  
   201  // NewAPIBase returns a new application API facade.
   202  func NewAPIBase(
   203  	backend Backend,
   204  	storageAccess storageInterface,
   205  	authorizer facade.Authorizer,
   206  	blockChecker BlockChecker,
   207  	modelTag names.ModelTag,
   208  	modelType state.ModelType,
   209  	modelName string,
   210  	stateCharm func(Charm) *state.Charm,
   211  	deployApplication func(ApplicationDeployer, DeployApplicationParams) (Application, error),
   212  	storagePoolManager poolmanager.PoolManager,
   213  	resources facade.Resources,
   214  	caasBroker caas.Broker,
   215  ) (*APIBase, error) {
   216  	if !authorizer.AuthClient() {
   217  		return nil, common.ErrPerm
   218  	}
   219  	return &APIBase{
   220  		backend:               backend,
   221  		storageAccess:         storageAccess,
   222  		authorizer:            authorizer,
   223  		check:                 blockChecker,
   224  		modelTag:              modelTag,
   225  		modelType:             modelType,
   226  		modelName:             modelName,
   227  		stateCharm:            stateCharm,
   228  		deployApplicationFunc: deployApplication,
   229  		storagePoolManager:    storagePoolManager,
   230  		resources:             resources,
   231  		caasBroker:            caasBroker,
   232  	}, nil
   233  }
   234  
   235  func (api *APIBase) checkPermission(tag names.Tag, perm permission.Access) error {
   236  	allowed, err := api.authorizer.HasPermission(perm, tag)
   237  	if err != nil {
   238  		return errors.Trace(err)
   239  	}
   240  	if !allowed {
   241  		return common.ErrPerm
   242  	}
   243  	return nil
   244  }
   245  
   246  func (api *APIBase) checkCanRead() error {
   247  	return api.checkPermission(api.modelTag, permission.ReadAccess)
   248  }
   249  
   250  func (api *APIBase) checkCanWrite() error {
   251  	return api.checkPermission(api.modelTag, permission.WriteAccess)
   252  }
   253  
   254  // SetMetricCredentials sets credentials on the application.
   255  func (api *APIBase) SetMetricCredentials(args params.ApplicationMetricCredentials) (params.ErrorResults, error) {
   256  	if err := api.checkCanWrite(); err != nil {
   257  		return params.ErrorResults{}, errors.Trace(err)
   258  	}
   259  	result := params.ErrorResults{
   260  		Results: make([]params.ErrorResult, len(args.Creds)),
   261  	}
   262  	if len(args.Creds) == 0 {
   263  		return result, nil
   264  	}
   265  	for i, a := range args.Creds {
   266  		application, err := api.backend.Application(a.ApplicationName)
   267  		if err != nil {
   268  			result.Results[i].Error = common.ServerError(err)
   269  			continue
   270  		}
   271  		err = application.SetMetricCredentials(a.MetricCredentials)
   272  		if err != nil {
   273  			result.Results[i].Error = common.ServerError(err)
   274  		}
   275  	}
   276  	return result, nil
   277  }
   278  
   279  // Deploy fetches the charms from the charm store and deploys them
   280  // using the specified placement directives.
   281  // V5 deploy did not support policy, so pass through an empty string.
   282  func (api *APIv5) Deploy(args params.ApplicationsDeployV5) (params.ErrorResults, error) {
   283  	noDefinedPolicy := ""
   284  	var newArgs params.ApplicationsDeploy
   285  	for _, value := range args.Applications {
   286  		newArgs.Applications = append(newArgs.Applications, params.ApplicationDeploy{
   287  			ApplicationName:  value.ApplicationName,
   288  			Series:           value.Series,
   289  			CharmURL:         value.CharmURL,
   290  			Channel:          value.Channel,
   291  			NumUnits:         value.NumUnits,
   292  			Config:           value.Config,
   293  			ConfigYAML:       value.ConfigYAML,
   294  			Constraints:      value.Constraints,
   295  			Placement:        value.Placement,
   296  			Policy:           noDefinedPolicy,
   297  			Storage:          value.Storage,
   298  			AttachStorage:    value.AttachStorage,
   299  			EndpointBindings: value.EndpointBindings,
   300  			Resources:        value.Resources,
   301  		})
   302  	}
   303  	return api.APIBase.Deploy(newArgs)
   304  }
   305  
   306  // Deploy fetches the charms from the charm store and deploys them
   307  // using the specified placement directives.
   308  // V6 deploy did not support devices, so pass through an empty map.
   309  func (api *APIv6) Deploy(args params.ApplicationsDeployV6) (params.ErrorResults, error) {
   310  	var newArgs params.ApplicationsDeploy
   311  	for _, value := range args.Applications {
   312  		newArgs.Applications = append(newArgs.Applications, params.ApplicationDeploy{
   313  			ApplicationName:  value.ApplicationName,
   314  			Series:           value.Series,
   315  			CharmURL:         value.CharmURL,
   316  			Channel:          value.Channel,
   317  			NumUnits:         value.NumUnits,
   318  			Config:           value.Config,
   319  			ConfigYAML:       value.ConfigYAML,
   320  			Constraints:      value.Constraints,
   321  			Placement:        value.Placement,
   322  			Policy:           value.Policy,
   323  			Devices:          nil, // set Devices to nil because v6 and lower versions do not support it
   324  			Storage:          value.Storage,
   325  			AttachStorage:    value.AttachStorage,
   326  			EndpointBindings: value.EndpointBindings,
   327  			Resources:        value.Resources,
   328  		})
   329  	}
   330  	return api.APIBase.Deploy(newArgs)
   331  }
   332  
   333  // Deploy fetches the charms from the charm store and deploys them
   334  // using the specified placement directives.
   335  func (api *APIBase) Deploy(args params.ApplicationsDeploy) (params.ErrorResults, error) {
   336  	if err := api.checkCanWrite(); err != nil {
   337  		return params.ErrorResults{}, errors.Trace(err)
   338  	}
   339  	result := params.ErrorResults{
   340  		Results: make([]params.ErrorResult, len(args.Applications)),
   341  	}
   342  	if err := api.check.ChangeAllowed(); err != nil {
   343  		return result, errors.Trace(err)
   344  	}
   345  
   346  	for i, arg := range args.Applications {
   347  		err := deployApplication(api.backend, api.modelType, api.modelName, api.stateCharm, arg, api.deployApplicationFunc, api.storagePoolManager, api.caasBroker)
   348  		result.Results[i].Error = common.ServerError(err)
   349  
   350  		if err != nil && len(arg.Resources) != 0 {
   351  			// Remove any pending resources - these would have been
   352  			// converted into real resources if the application had
   353  			// been created successfully, but will otherwise be
   354  			// leaked. lp:1705730
   355  			// TODO(babbageclunk): rework the deploy API so the
   356  			// resources are created transactionally to avoid needing
   357  			// to do this.
   358  			resources, err := api.backend.Resources()
   359  			if err != nil {
   360  				logger.Errorf("couldn't get backend.Resources")
   361  				continue
   362  			}
   363  			err = resources.RemovePendingAppResources(arg.ApplicationName, arg.Resources)
   364  			if err != nil {
   365  				logger.Errorf("couldn't remove pending resources for %q", arg.ApplicationName)
   366  			}
   367  		}
   368  	}
   369  	return result, nil
   370  }
   371  
   372  func applicationConfigSchema(modelType state.ModelType) (environschema.Fields, schema.Defaults, error) {
   373  	if modelType != state.ModelTypeCAAS {
   374  		return trustFields, trustDefaults, nil
   375  	}
   376  	// TODO(caas) - get the schema from the provider
   377  	defaults := caas.ConfigDefaults(k8s.ConfigDefaults())
   378  	schema, err := caas.ConfigSchema(k8s.ConfigSchema())
   379  	if err != nil {
   380  		return nil, nil, err
   381  	}
   382  	return AddTrustSchemaAndDefaults(schema, defaults)
   383  }
   384  
   385  func splitApplicationAndCharmConfig(modelType state.ModelType, inConfig map[string]string) (
   386  	appCfg map[string]interface{},
   387  	charmCfg map[string]string,
   388  	_ error,
   389  ) {
   390  
   391  	providerSchema, _, err := applicationConfigSchema(modelType)
   392  	if err != nil {
   393  		return nil, nil, errors.Trace(err)
   394  	}
   395  	appConfigKeys := application.KnownConfigKeys(providerSchema)
   396  
   397  	appConfigAttrs := make(map[string]interface{})
   398  	charmConfig := make(map[string]string)
   399  	for k, v := range inConfig {
   400  		if appConfigKeys.Contains(k) {
   401  			appConfigAttrs[k] = v
   402  		} else {
   403  			charmConfig[k] = v
   404  		}
   405  	}
   406  	return appConfigAttrs, charmConfig, nil
   407  }
   408  
   409  // splitApplicationAndCharmConfigFromYAML extracts app specific settings from a charm config YAML
   410  // and returns those app settings plus a YAML with just the charm settings left behind.
   411  func splitApplicationAndCharmConfigFromYAML(modelType state.ModelType, inYaml, appName string) (
   412  	appCfg map[string]interface{},
   413  	outYaml string,
   414  	_ error,
   415  ) {
   416  	var allSettings map[string]map[string]interface{}
   417  	if err := goyaml.Unmarshal([]byte(inYaml), &allSettings); err != nil {
   418  		return nil, "", errors.Annotate(err, "cannot parse settings data")
   419  	}
   420  	settings, ok := allSettings[appName]
   421  	if !ok {
   422  		return nil, "", errors.Errorf("no settings found for %q", appName)
   423  	}
   424  
   425  	providerSchema, _, err := applicationConfigSchema(modelType)
   426  	if err != nil {
   427  		return nil, "", errors.Trace(err)
   428  	}
   429  	appConfigKeys := application.KnownConfigKeys(providerSchema)
   430  
   431  	appConfigAttrs := make(map[string]interface{})
   432  	for k, v := range settings {
   433  		if appConfigKeys.Contains(k) {
   434  			appConfigAttrs[k] = v
   435  			delete(settings, k)
   436  		}
   437  	}
   438  	if len(settings) == 0 {
   439  		return appConfigAttrs, "", nil
   440  	}
   441  
   442  	allSettings[appName] = settings
   443  	charmConfig, err := goyaml.Marshal(allSettings)
   444  	if err != nil {
   445  		return nil, "", errors.Annotate(err, "cannot marshall charm settings")
   446  	}
   447  	return appConfigAttrs, string(charmConfig), nil
   448  }
   449  
   450  // deployApplication fetches the charm from the charm store and deploys it.
   451  // The logic has been factored out into a common function which is called by
   452  // both the legacy API on the client facade, as well as the new application facade.
   453  func deployApplication(
   454  	backend Backend,
   455  	modelType state.ModelType,
   456  	modelName string,
   457  	stateCharm func(Charm) *state.Charm,
   458  	args params.ApplicationDeploy,
   459  	deployApplicationFunc func(ApplicationDeployer, DeployApplicationParams) (Application, error),
   460  	storagePoolManager poolmanager.PoolManager,
   461  	caasBroker caas.Broker,
   462  ) error {
   463  	curl, err := charm.ParseURL(args.CharmURL)
   464  	if err != nil {
   465  		return errors.Trace(err)
   466  	}
   467  	if curl.Revision < 0 {
   468  		return errors.Errorf("charm url must include revision")
   469  	}
   470  
   471  	if modelType != state.ModelTypeIAAS {
   472  		if len(args.AttachStorage) > 0 {
   473  			return errors.Errorf(
   474  				"AttachStorage may not be specified for %s models",
   475  				modelType,
   476  			)
   477  		}
   478  		if len(args.Placement) > 1 {
   479  			return errors.Errorf(
   480  				"only 1 placement directive is supported for %s models, got %d",
   481  				modelType,
   482  				len(args.Placement),
   483  			)
   484  		}
   485  
   486  		// CAAS models will use an "operator-storage" storage pool if it exists.
   487  		sp, err := storagePoolManager.Get(caas.OperatorStoragePoolName)
   488  		if err != nil && !errors.IsNotFound(err) {
   489  			return errors.Trace(err)
   490  		}
   491  		if err == nil {
   492  			if sp.Provider() != k8s.K8s_ProviderType {
   493  				return errors.Errorf(
   494  					"the %q storage pool requires a provider type of %q, not %q", caas.OperatorStoragePoolName, k8s.K8s_ProviderType, sp.Provider())
   495  			}
   496  		} else {
   497  			// No operator-storage pool so try and see if there's a cluster default.
   498  			if _, err := caasBroker.GetStorageClassName(caas.OperatorStorageClassLabels(args.ApplicationName, modelName)...); err != nil {
   499  				return errors.Annotatef(
   500  					err,
   501  					"deploying a Kubernetes application requires a suitable storage class.\n"+
   502  						"None were found in the cluster. Either create a default storage class\n"+
   503  						"or create a Juju storage-pool called 'operator-storage' to define how operator"+
   504  						"storage should be allocated. See https://discourse.jujucharms.com/t/getting-started/152.",
   505  				)
   506  			}
   507  		}
   508  
   509  		var defaultStorageClass *string
   510  		for storageName, cons := range args.Storage {
   511  			if cons.Pool == "" && defaultStorageClass == nil {
   512  				// In case no storage pool is specified for charm storage, pre-load any cluster default.
   513  				result, err := caasBroker.GetStorageClassName(caas.UnitStorageClassLabels(args.ApplicationName, modelName)...)
   514  				if err != nil && !errors.IsNotFound(err) {
   515  					return errors.Trace(err)
   516  				}
   517  				defaultStorageClass = &result
   518  			}
   519  			if cons.Pool == "" && *defaultStorageClass == "" {
   520  				return errors.Errorf("storage pool for %q must be specified since there's no cluster default storage class", storageName)
   521  			}
   522  			if cons.Pool != "" {
   523  				sp, err := storagePoolManager.Get(cons.Pool)
   524  				if err != nil {
   525  					return errors.Trace(err)
   526  				}
   527  				if sp.Provider() != k8s.K8s_ProviderType {
   528  					return errors.Errorf("invalid storage provider type %q for %q", sp.Provider(), storageName)
   529  				}
   530  			}
   531  		}
   532  	}
   533  
   534  	// This check is done early so that errors deeper in the call-stack do not
   535  	// leave an application deployment in an unrecoverable error state.
   536  	if err := checkMachinePlacement(backend, args); err != nil {
   537  		return errors.Trace(err)
   538  	}
   539  
   540  	// Try to find the charm URL in state first.
   541  	ch, err := backend.Charm(curl)
   542  	if err != nil {
   543  		return errors.Trace(err)
   544  	}
   545  
   546  	if err := checkMinVersion(ch); err != nil {
   547  		return errors.Trace(err)
   548  	}
   549  
   550  	// Split out the app config from the charm config for any config
   551  	// passed in as a map as opposed to YAML.
   552  	var appConfig map[string]interface{}
   553  	var charmConfig map[string]string
   554  	if len(args.Config) > 0 {
   555  		if appConfig, charmConfig, err = splitApplicationAndCharmConfig(modelType, args.Config); err != nil {
   556  			return errors.Trace(err)
   557  		}
   558  	}
   559  
   560  	// Split out the app config from the charm config for any config
   561  	// passed in as YAML.
   562  	var charmYamlConfig string
   563  	appSettings := make(map[string]interface{})
   564  	if len(args.ConfigYAML) > 0 {
   565  		if appSettings, charmYamlConfig, err = splitApplicationAndCharmConfigFromYAML(modelType, args.ConfigYAML, args.ApplicationName); err != nil {
   566  			return errors.Trace(err)
   567  		}
   568  	}
   569  
   570  	// Overlay any app settings in YAML with those from config map.
   571  	for k, v := range appConfig {
   572  		appSettings[k] = v
   573  	}
   574  
   575  	var applicationConfig *application.Config
   576  	schema, defaults, err := applicationConfigSchema(modelType)
   577  	if err != nil {
   578  		return errors.Trace(err)
   579  	}
   580  	applicationConfig, err = application.NewConfig(appSettings, schema, defaults)
   581  	if err != nil {
   582  		return errors.Trace(err)
   583  	}
   584  
   585  	var settings = make(charm.Settings)
   586  	if len(charmYamlConfig) > 0 {
   587  		settings, err = ch.Config().ParseSettingsYAML([]byte(charmYamlConfig), args.ApplicationName)
   588  		if err != nil {
   589  			return errors.Trace(err)
   590  		}
   591  	}
   592  	// Overlay any settings in YAML with those from config map.
   593  	if len(charmConfig) > 0 {
   594  		// Parse config in a compatible way (see function comment).
   595  		overrideSettings, err := parseSettingsCompatible(ch.Config(), charmConfig)
   596  		if err != nil {
   597  			return errors.Trace(err)
   598  		}
   599  		for k, v := range overrideSettings {
   600  			settings[k] = v
   601  		}
   602  	}
   603  
   604  	// Parse storage tags in AttachStorage.
   605  	if len(args.AttachStorage) > 0 && args.NumUnits != 1 {
   606  		return errors.Errorf("AttachStorage is non-empty, but NumUnits is %d", args.NumUnits)
   607  	}
   608  	attachStorage := make([]names.StorageTag, len(args.AttachStorage))
   609  	for i, tagString := range args.AttachStorage {
   610  		tag, err := names.ParseStorageTag(tagString)
   611  		if err != nil {
   612  			return errors.Trace(err)
   613  		}
   614  		attachStorage[i] = tag
   615  	}
   616  
   617  	_, err = deployApplicationFunc(backend, DeployApplicationParams{
   618  		ApplicationName:   args.ApplicationName,
   619  		Series:            args.Series,
   620  		Charm:             stateCharm(ch),
   621  		Channel:           csparams.Channel(args.Channel),
   622  		NumUnits:          args.NumUnits,
   623  		ApplicationConfig: applicationConfig,
   624  		CharmConfig:       settings,
   625  		Constraints:       args.Constraints,
   626  		Placement:         args.Placement,
   627  		Storage:           args.Storage,
   628  		Devices:           args.Devices,
   629  		AttachStorage:     attachStorage,
   630  		EndpointBindings:  args.EndpointBindings,
   631  		Resources:         args.Resources,
   632  	})
   633  	return errors.Trace(err)
   634  }
   635  
   636  // checkMachinePlacement does a non-exhaustive validation of any supplied
   637  // placement directives.
   638  // If the placement scope is for a machine, ensure that the machine exists.
   639  // If the placement is for a machine or a container on an existing machine,
   640  // check that the machine is not locked for series upgrade.
   641  func checkMachinePlacement(backend Backend, args params.ApplicationDeploy) error {
   642  	errTemplate := "cannot deploy %q to machine %s"
   643  	app := args.ApplicationName
   644  
   645  	for _, p := range args.Placement {
   646  		dir := p.Directive
   647  
   648  		toProvisionedMachine := p.Scope == instance.MachineScope
   649  		if !toProvisionedMachine && dir == "" {
   650  			continue
   651  		}
   652  
   653  		m, err := backend.Machine(dir)
   654  		if err != nil {
   655  			if errors.IsNotFound(err) && !toProvisionedMachine {
   656  				continue
   657  			}
   658  			return errors.Annotatef(err, errTemplate, app, dir)
   659  		}
   660  
   661  		locked, err := m.IsLockedForSeriesUpgrade()
   662  		if locked {
   663  			err = errors.New("machine is locked for series upgrade")
   664  		}
   665  		if err != nil {
   666  			return errors.Annotatef(err, errTemplate, app, dir)
   667  		}
   668  
   669  		locked, err = m.IsParentLockedForSeriesUpgrade()
   670  		if locked {
   671  			err = errors.New("parent machine is locked for series upgrade")
   672  		}
   673  		if err != nil {
   674  			return errors.Annotatef(err, errTemplate, app, dir)
   675  		}
   676  	}
   677  
   678  	return nil
   679  }
   680  
   681  // ApplicationSetSettingsStrings updates the settings for the given application,
   682  // taking the configuration from a map of strings.
   683  func ApplicationSetSettingsStrings(application Application, settings map[string]string) error {
   684  	ch, _, err := application.Charm()
   685  	if err != nil {
   686  		return errors.Trace(err)
   687  	}
   688  	// Parse config in a compatible way (see function comment).
   689  	changes, err := parseSettingsCompatible(ch.Config(), settings)
   690  	if err != nil {
   691  		return errors.Trace(err)
   692  	}
   693  	return application.UpdateCharmConfig(changes)
   694  }
   695  
   696  // parseSettingsCompatible parses setting strings in a way that is
   697  // compatible with the behavior before this CL based on the issue
   698  // http://pad.lv/1194945. Until then setting an option to an empty
   699  // string caused it to reset to the default value. We now allow
   700  // empty strings as actual values, but we want to preserve the API
   701  // behavior.
   702  func parseSettingsCompatible(charmConfig *charm.Config, settings map[string]string) (charm.Settings, error) {
   703  	setSettings := map[string]string{}
   704  	unsetSettings := charm.Settings{}
   705  	// Split settings into those which set and those which unset a value.
   706  	for name, value := range settings {
   707  		if value == "" {
   708  			unsetSettings[name] = nil
   709  			continue
   710  		}
   711  		setSettings[name] = value
   712  	}
   713  	// Validate the settings.
   714  	changes, err := charmConfig.ParseSettingsStrings(setSettings)
   715  	if err != nil {
   716  		return nil, errors.Trace(err)
   717  	}
   718  	// Validate the unsettings and merge them into the changes.
   719  	unsetSettings, err = charmConfig.ValidateSettings(unsetSettings)
   720  	if err != nil {
   721  		return nil, errors.Trace(err)
   722  	}
   723  	for name := range unsetSettings {
   724  		changes[name] = nil
   725  	}
   726  	return changes, nil
   727  }
   728  
   729  // Update updates the application attributes, including charm URL,
   730  // minimum number of units, charm config and constraints.
   731  // All parameters in params.ApplicationUpdate except the application name are optional.
   732  func (api *APIBase) Update(args params.ApplicationUpdate) error {
   733  	if err := api.checkCanWrite(); err != nil {
   734  		return err
   735  	}
   736  	if !args.ForceCharmURL {
   737  		if err := api.check.ChangeAllowed(); err != nil {
   738  			return errors.Trace(err)
   739  		}
   740  	}
   741  	app, err := api.backend.Application(args.ApplicationName)
   742  	if err != nil {
   743  		return errors.Trace(err)
   744  	}
   745  	// Set the charm for the given application.
   746  	if args.CharmURL != "" {
   747  		// For now we do not support changing the channel through Update().
   748  		// TODO(ericsnow) Support it?
   749  		channel := app.Channel()
   750  		if err = api.applicationSetCharm(
   751  			args.ApplicationName,
   752  			app,
   753  			args.CharmURL,
   754  			channel,
   755  			nil, // charm settings (strings map)
   756  			"",  // charm settings (YAML)
   757  			args.ForceSeries,
   758  			args.ForceCharmURL,
   759  			args.Force,
   760  			nil, // resource IDs
   761  			nil, // storage constraints
   762  		); err != nil {
   763  			return errors.Trace(err)
   764  		}
   765  	}
   766  	// Set the minimum number of units for the given application.
   767  	if args.MinUnits != nil {
   768  		if err = app.SetMinUnits(*args.MinUnits); err != nil {
   769  			return errors.Trace(err)
   770  		}
   771  	}
   772  	// Set up application's settings.
   773  	if args.SettingsYAML != "" {
   774  		if err = applicationSetCharmConfigYAML(args.ApplicationName, app, args.SettingsYAML); err != nil {
   775  			return errors.Annotate(err, "setting configuration from YAML")
   776  		}
   777  	} else if len(args.SettingsStrings) > 0 {
   778  		if err = ApplicationSetSettingsStrings(app, args.SettingsStrings); err != nil {
   779  			return errors.Trace(err)
   780  		}
   781  	}
   782  	// Update application's constraints.
   783  	if args.Constraints != nil {
   784  		return app.SetConstraints(*args.Constraints)
   785  	}
   786  	return nil
   787  }
   788  
   789  // UpdateApplicationSeries updates the application series. Series for
   790  // subordinates updated too.
   791  func (api *APIBase) UpdateApplicationSeries(args params.UpdateSeriesArgs) (params.ErrorResults, error) {
   792  	if err := api.checkCanWrite(); err != nil {
   793  		return params.ErrorResults{}, err
   794  	}
   795  	if err := api.check.ChangeAllowed(); err != nil {
   796  		return params.ErrorResults{}, errors.Trace(err)
   797  	}
   798  	results := params.ErrorResults{
   799  		Results: make([]params.ErrorResult, len(args.Args)),
   800  	}
   801  	for i, arg := range args.Args {
   802  		err := api.updateOneApplicationSeries(arg)
   803  		results.Results[i].Error = common.ServerError(err)
   804  	}
   805  	return results, nil
   806  }
   807  
   808  func (api *APIBase) updateOneApplicationSeries(arg params.UpdateSeriesArg) error {
   809  	if arg.Series == "" {
   810  		return &params.Error{
   811  			Message: "series missing from args",
   812  			Code:    params.CodeBadRequest,
   813  		}
   814  	}
   815  	applicationTag, err := names.ParseApplicationTag(arg.Entity.Tag)
   816  	if err != nil {
   817  		return errors.Trace(err)
   818  	}
   819  	app, err := api.backend.Application(applicationTag.Id())
   820  	if err != nil {
   821  		return errors.Trace(err)
   822  	}
   823  	if !app.IsPrincipal() {
   824  		return &params.Error{
   825  			Message: fmt.Sprintf("%q is a subordinate application, update-series not supported", applicationTag.Id()),
   826  			Code:    params.CodeNotSupported,
   827  		}
   828  	}
   829  	if arg.Series == app.Series() {
   830  		return nil // no-op
   831  	}
   832  	return app.UpdateApplicationSeries(arg.Series, arg.Force)
   833  }
   834  
   835  // SetCharm sets the charm for a given for the application.
   836  func (api *APIBase) SetCharm(args params.ApplicationSetCharm) error {
   837  	if err := api.checkCanWrite(); err != nil {
   838  		return err
   839  	}
   840  	// when forced units in error, don't block
   841  	if !args.ForceUnits {
   842  		if err := api.check.ChangeAllowed(); err != nil {
   843  			return errors.Trace(err)
   844  		}
   845  	}
   846  	application, err := api.backend.Application(args.ApplicationName)
   847  	if err != nil {
   848  		return errors.Trace(err)
   849  	}
   850  	if err := application.SetCharmProfile(args.CharmURL); err != nil {
   851  		return errors.Annotatef(err, "unable to set charm profile")
   852  	}
   853  
   854  	channel := csparams.Channel(args.Channel)
   855  	return api.applicationSetCharm(
   856  		args.ApplicationName,
   857  		application,
   858  		args.CharmURL,
   859  		channel,
   860  		args.ConfigSettings,
   861  		args.ConfigSettingsYAML,
   862  		args.ForceSeries,
   863  		args.ForceUnits,
   864  		args.Force,
   865  		args.ResourceIDs,
   866  		args.StorageConstraints,
   867  	)
   868  }
   869  
   870  // applicationSetCharm sets the charm for the given for the application.
   871  func (api *APIBase) applicationSetCharm(
   872  	appName string,
   873  	application Application,
   874  	url string,
   875  	channel csparams.Channel,
   876  	configSettingsStrings map[string]string,
   877  	configSettingsYAML string,
   878  	forceSeries,
   879  	forceUnits,
   880  	force bool,
   881  	resourceIDs map[string]string,
   882  	storageConstraints map[string]params.StorageConstraints,
   883  ) error {
   884  	curl, err := charm.ParseURL(url)
   885  	if err != nil {
   886  		return errors.Trace(err)
   887  	}
   888  	sch, err := api.backend.Charm(curl)
   889  	if err != nil {
   890  		return errors.Trace(err)
   891  	}
   892  	var settings charm.Settings
   893  	if configSettingsYAML != "" {
   894  		settings, err = sch.Config().ParseSettingsYAML([]byte(configSettingsYAML), appName)
   895  	} else if len(configSettingsStrings) > 0 {
   896  		settings, err = parseSettingsCompatible(sch.Config(), configSettingsStrings)
   897  	}
   898  	if err != nil {
   899  		return errors.Annotate(err, "parsing config settings")
   900  	}
   901  	var stateStorageConstraints map[string]state.StorageConstraints
   902  	if len(storageConstraints) > 0 {
   903  		stateStorageConstraints = make(map[string]state.StorageConstraints)
   904  		for name, cons := range storageConstraints {
   905  			stateCons := state.StorageConstraints{Pool: cons.Pool}
   906  			if cons.Size != nil {
   907  				stateCons.Size = *cons.Size
   908  			}
   909  			if cons.Count != nil {
   910  				stateCons.Count = *cons.Count
   911  			}
   912  			stateStorageConstraints[name] = stateCons
   913  		}
   914  	}
   915  	cfg := state.SetCharmConfig{
   916  		Charm:              api.stateCharm(sch),
   917  		Channel:            channel,
   918  		ConfigSettings:     settings,
   919  		ForceSeries:        forceSeries,
   920  		ForceUnits:         forceUnits,
   921  		Force:              force,
   922  		ResourceIDs:        resourceIDs,
   923  		StorageConstraints: stateStorageConstraints,
   924  	}
   925  	return application.SetCharm(cfg)
   926  }
   927  
   928  // charmConfigFromGetYaml will parse a yaml produced by juju get and generate
   929  // charm.Settings from it that can then be sent to the application.
   930  func charmConfigFromGetYaml(yamlContents map[string]interface{}) (charm.Settings, error) {
   931  	onlySettings := charm.Settings{}
   932  	settingsMap, ok := yamlContents["settings"].(map[interface{}]interface{})
   933  	if !ok {
   934  		return nil, errors.New("unknown format for settings")
   935  	}
   936  
   937  	for setting := range settingsMap {
   938  		s, ok := settingsMap[setting].(map[interface{}]interface{})
   939  		if !ok {
   940  			return nil, errors.Errorf("unknown format for settings section %v", setting)
   941  		}
   942  		// some keys might not have a value, we don't care about those.
   943  		v, ok := s["value"]
   944  		if !ok {
   945  			continue
   946  		}
   947  		stringSetting, ok := setting.(string)
   948  		if !ok {
   949  			return nil, errors.Errorf("unexpected setting key, expected string got %T", setting)
   950  		}
   951  		onlySettings[stringSetting] = v
   952  	}
   953  	return onlySettings, nil
   954  }
   955  
   956  // applicationSetCharmConfigYAML updates the charm config for the
   957  // given application, taking the configuration from a YAML string.
   958  func applicationSetCharmConfigYAML(appName string, application Application, settings string) error {
   959  	b := []byte(settings)
   960  	var all map[string]interface{}
   961  	if err := goyaml.Unmarshal(b, &all); err != nil {
   962  		return errors.Annotate(err, "parsing settings data")
   963  	}
   964  	// The file is already in the right format.
   965  	if _, ok := all[appName]; !ok {
   966  		changes, err := charmConfigFromGetYaml(all)
   967  		if err != nil {
   968  			return errors.Annotate(err, "processing YAML generated by get")
   969  		}
   970  		return errors.Annotate(application.UpdateCharmConfig(changes), "updating settings with application YAML")
   971  	}
   972  
   973  	ch, _, err := application.Charm()
   974  	if err != nil {
   975  		return errors.Annotate(err, "obtaining charm for this application")
   976  	}
   977  
   978  	changes, err := ch.Config().ParseSettingsYAML(b, appName)
   979  	if err != nil {
   980  		return errors.Annotate(err, "creating config from YAML")
   981  	}
   982  	return errors.Annotate(application.UpdateCharmConfig(changes), "updating settings")
   983  }
   984  
   985  // GetCharmURL returns the charm URL the given application is
   986  // running at present.
   987  func (api *APIBase) GetCharmURL(args params.ApplicationGet) (params.StringResult, error) {
   988  	if err := api.checkCanWrite(); err != nil {
   989  		return params.StringResult{}, errors.Trace(err)
   990  	}
   991  	application, err := api.backend.Application(args.ApplicationName)
   992  	if err != nil {
   993  		return params.StringResult{}, errors.Trace(err)
   994  	}
   995  	charmURL, _ := application.CharmURL()
   996  	return params.StringResult{Result: charmURL.String()}, nil
   997  }
   998  
   999  // Set implements the server side of Application.Set.
  1000  // It does not unset values that are set to an empty string.
  1001  // Unset should be used for that.
  1002  func (api *APIBase) Set(p params.ApplicationSet) error {
  1003  	if err := api.checkCanWrite(); err != nil {
  1004  		return err
  1005  	}
  1006  	if err := api.check.ChangeAllowed(); err != nil {
  1007  		return errors.Trace(err)
  1008  	}
  1009  	app, err := api.backend.Application(p.ApplicationName)
  1010  	if err != nil {
  1011  		return err
  1012  	}
  1013  	ch, _, err := app.Charm()
  1014  	if err != nil {
  1015  		return err
  1016  	}
  1017  	// Validate the settings.
  1018  	changes, err := ch.Config().ParseSettingsStrings(p.Options)
  1019  	if err != nil {
  1020  		return err
  1021  	}
  1022  
  1023  	return app.UpdateCharmConfig(changes)
  1024  
  1025  }
  1026  
  1027  // Unset implements the server side of Client.Unset.
  1028  func (api *APIBase) Unset(p params.ApplicationUnset) error {
  1029  	if err := api.checkCanWrite(); err != nil {
  1030  		return err
  1031  	}
  1032  	if err := api.check.ChangeAllowed(); err != nil {
  1033  		return errors.Trace(err)
  1034  	}
  1035  	app, err := api.backend.Application(p.ApplicationName)
  1036  	if err != nil {
  1037  		return err
  1038  	}
  1039  	settings := make(charm.Settings)
  1040  	for _, option := range p.Options {
  1041  		settings[option] = nil
  1042  	}
  1043  	return app.UpdateCharmConfig(settings)
  1044  }
  1045  
  1046  // CharmRelations implements the server side of Application.CharmRelations.
  1047  func (api *APIBase) CharmRelations(p params.ApplicationCharmRelations) (params.ApplicationCharmRelationsResults, error) {
  1048  	var results params.ApplicationCharmRelationsResults
  1049  	if err := api.checkCanRead(); err != nil {
  1050  		return results, errors.Trace(err)
  1051  	}
  1052  
  1053  	application, err := api.backend.Application(p.ApplicationName)
  1054  	if err != nil {
  1055  		return results, errors.Trace(err)
  1056  	}
  1057  	endpoints, err := application.Endpoints()
  1058  	if err != nil {
  1059  		return results, errors.Trace(err)
  1060  	}
  1061  	results.CharmRelations = make([]string, len(endpoints))
  1062  	for i, endpoint := range endpoints {
  1063  		results.CharmRelations[i] = endpoint.Relation.Name
  1064  	}
  1065  	return results, nil
  1066  }
  1067  
  1068  // Expose changes the juju-managed firewall to expose any ports that
  1069  // were also explicitly marked by units as open.
  1070  func (api *APIBase) Expose(args params.ApplicationExpose) error {
  1071  	if err := api.checkCanWrite(); err != nil {
  1072  		return errors.Trace(err)
  1073  	}
  1074  	if err := api.check.ChangeAllowed(); err != nil {
  1075  		return errors.Trace(err)
  1076  	}
  1077  	app, err := api.backend.Application(args.ApplicationName)
  1078  	if err != nil {
  1079  		return errors.Trace(err)
  1080  	}
  1081  	if api.modelType == state.ModelTypeCAAS {
  1082  		appConfig, err := app.ApplicationConfig()
  1083  		if err != nil {
  1084  			return errors.Trace(err)
  1085  		}
  1086  		if appConfig.GetString(caas.JujuExternalHostNameKey, "") == "" {
  1087  			return errors.Errorf(
  1088  				"cannot expose a CAAS application without a %q value set, run\n"+
  1089  					"juju config %s %s=<value>", caas.JujuExternalHostNameKey, args.ApplicationName, caas.JujuExternalHostNameKey)
  1090  		}
  1091  	}
  1092  	return app.SetExposed()
  1093  }
  1094  
  1095  // Unexpose changes the juju-managed firewall to unexpose any ports that
  1096  // were also explicitly marked by units as open.
  1097  func (api *APIBase) Unexpose(args params.ApplicationUnexpose) error {
  1098  	if err := api.checkCanWrite(); err != nil {
  1099  		return err
  1100  	}
  1101  	if err := api.check.ChangeAllowed(); err != nil {
  1102  		return errors.Trace(err)
  1103  	}
  1104  	app, err := api.backend.Application(args.ApplicationName)
  1105  	if err != nil {
  1106  		return err
  1107  	}
  1108  	return app.ClearExposed()
  1109  }
  1110  
  1111  // AddUnits adds a given number of units to an application.
  1112  func (api *APIv5) AddUnits(args params.AddApplicationUnitsV5) (params.AddApplicationUnitsResults, error) {
  1113  	noDefinedPolicy := ""
  1114  	return api.APIBase.AddUnits(params.AddApplicationUnits{
  1115  		ApplicationName: args.ApplicationName,
  1116  		NumUnits:        args.NumUnits,
  1117  		Placement:       args.Placement,
  1118  		Policy:          noDefinedPolicy,
  1119  		AttachStorage:   args.AttachStorage,
  1120  	})
  1121  }
  1122  
  1123  // AddUnits adds a given number of units to an application.
  1124  func (api *APIBase) AddUnits(args params.AddApplicationUnits) (params.AddApplicationUnitsResults, error) {
  1125  	if api.modelType == state.ModelTypeCAAS {
  1126  		return params.AddApplicationUnitsResults{}, errors.NotSupportedf("adding units on a non-container model")
  1127  	}
  1128  	if err := api.checkCanWrite(); err != nil {
  1129  		return params.AddApplicationUnitsResults{}, errors.Trace(err)
  1130  	}
  1131  	if err := api.check.ChangeAllowed(); err != nil {
  1132  		return params.AddApplicationUnitsResults{}, errors.Trace(err)
  1133  	}
  1134  	units, err := addApplicationUnits(api.backend, api.modelType, args)
  1135  	if err != nil {
  1136  		return params.AddApplicationUnitsResults{}, errors.Trace(err)
  1137  	}
  1138  	unitNames := make([]string, len(units))
  1139  	for i, unit := range units {
  1140  		unitNames[i] = unit.UnitTag().Id()
  1141  	}
  1142  	return params.AddApplicationUnitsResults{Units: unitNames}, nil
  1143  }
  1144  
  1145  // addApplicationUnits adds a given number of units to an application.
  1146  func addApplicationUnits(backend Backend, modelType state.ModelType, args params.AddApplicationUnits) ([]Unit, error) {
  1147  	if args.NumUnits < 1 {
  1148  		return nil, errors.New("must add at least one unit")
  1149  	}
  1150  
  1151  	assignUnits := true
  1152  	if modelType != state.ModelTypeIAAS {
  1153  		// In a CAAS model, there are no machines for
  1154  		// units to be assigned to.
  1155  		assignUnits = false
  1156  		if len(args.AttachStorage) > 0 {
  1157  			return nil, errors.Errorf(
  1158  				"AttachStorage may not be specified for %s models",
  1159  				modelType,
  1160  			)
  1161  		}
  1162  		if len(args.Placement) > 1 {
  1163  			return nil, errors.Errorf(
  1164  				"only 1 placement directive is supported for %s models, got %d",
  1165  				modelType,
  1166  				len(args.Placement),
  1167  			)
  1168  		}
  1169  	}
  1170  
  1171  	// Parse storage tags in AttachStorage.
  1172  	if len(args.AttachStorage) > 0 && args.NumUnits != 1 {
  1173  		return nil, errors.Errorf("AttachStorage is non-empty, but NumUnits is %d", args.NumUnits)
  1174  	}
  1175  	attachStorage := make([]names.StorageTag, len(args.AttachStorage))
  1176  	for i, tagString := range args.AttachStorage {
  1177  		tag, err := names.ParseStorageTag(tagString)
  1178  		if err != nil {
  1179  			return nil, errors.Trace(err)
  1180  		}
  1181  		attachStorage[i] = tag
  1182  	}
  1183  	application, err := backend.Application(args.ApplicationName)
  1184  	if err != nil {
  1185  		return nil, errors.Trace(err)
  1186  	}
  1187  	return addUnits(
  1188  		application,
  1189  		args.ApplicationName,
  1190  		args.NumUnits,
  1191  		args.Placement,
  1192  		attachStorage,
  1193  		assignUnits,
  1194  	)
  1195  }
  1196  
  1197  // DestroyUnits removes a given set of application units.
  1198  //
  1199  // NOTE(axw) this exists only for backwards compatibility,
  1200  // for API facade versions 1-3; clients should prefer its
  1201  // successor, DestroyUnit, below. Until all consumers have
  1202  // been updated, or we bump a major version, we can't drop
  1203  // this.
  1204  //
  1205  // TODO(axw) 2017-03-16 #1673323
  1206  // Drop this in Juju 3.0.
  1207  func (api *APIBase) DestroyUnits(args params.DestroyApplicationUnits) error {
  1208  	var errs []error
  1209  	entities := params.DestroyUnitsParams{
  1210  		Units: make([]params.DestroyUnitParams, 0, len(args.UnitNames)),
  1211  	}
  1212  	for _, unitName := range args.UnitNames {
  1213  		if !names.IsValidUnit(unitName) {
  1214  			errs = append(errs, errors.NotValidf("unit name %q", unitName))
  1215  			continue
  1216  		}
  1217  		entities.Units = append(entities.Units, params.DestroyUnitParams{
  1218  			UnitTag: names.NewUnitTag(unitName).String(),
  1219  		})
  1220  	}
  1221  	results, err := api.DestroyUnit(entities)
  1222  	if err != nil {
  1223  		return errors.Trace(err)
  1224  	}
  1225  	for _, result := range results.Results {
  1226  		if result.Error != nil {
  1227  			errs = append(errs, result.Error)
  1228  		}
  1229  	}
  1230  	return common.DestroyErr("units", args.UnitNames, errs)
  1231  }
  1232  
  1233  // DestroyUnit removes a given set of application units.
  1234  //
  1235  // NOTE(axw) this provides backwards compatibility for facade version 4.
  1236  func (api *APIv4) DestroyUnit(args params.Entities) (params.DestroyUnitResults, error) {
  1237  	v5args := params.DestroyUnitsParams{
  1238  		Units: make([]params.DestroyUnitParams, len(args.Entities)),
  1239  	}
  1240  	for i, arg := range args.Entities {
  1241  		v5args.Units[i].UnitTag = arg.Tag
  1242  	}
  1243  	return api.APIBase.DestroyUnit(v5args)
  1244  }
  1245  
  1246  // DestroyUnit removes a given set of application units.
  1247  func (api *APIBase) DestroyUnit(args params.DestroyUnitsParams) (params.DestroyUnitResults, error) {
  1248  	if api.modelType == state.ModelTypeCAAS {
  1249  		return params.DestroyUnitResults{}, errors.NotSupportedf("removing units on a non-container model")
  1250  	}
  1251  	if err := api.checkCanWrite(); err != nil {
  1252  		return params.DestroyUnitResults{}, errors.Trace(err)
  1253  	}
  1254  	if err := api.check.RemoveAllowed(); err != nil {
  1255  		return params.DestroyUnitResults{}, errors.Trace(err)
  1256  	}
  1257  	destroyUnit := func(arg params.DestroyUnitParams) (*params.DestroyUnitInfo, error) {
  1258  		unitTag, err := names.ParseUnitTag(arg.UnitTag)
  1259  		if err != nil {
  1260  			return nil, errors.Trace(err)
  1261  		}
  1262  		name := unitTag.Id()
  1263  		unit, err := api.backend.Unit(name)
  1264  		if errors.IsNotFound(err) {
  1265  			return nil, errors.Errorf("unit %q does not exist", name)
  1266  		} else if err != nil {
  1267  			return nil, errors.Trace(err)
  1268  		}
  1269  		if !unit.IsPrincipal() {
  1270  			return nil, errors.Errorf("unit %q is a subordinate", name)
  1271  		}
  1272  		var info params.DestroyUnitInfo
  1273  		storage, err := storagecommon.UnitStorage(api.storageAccess, unit.UnitTag())
  1274  		if err != nil {
  1275  			return nil, errors.Trace(err)
  1276  		}
  1277  
  1278  		if arg.DestroyStorage {
  1279  			for _, s := range storage {
  1280  				info.DestroyedStorage = append(
  1281  					info.DestroyedStorage,
  1282  					params.Entity{s.StorageTag().String()},
  1283  				)
  1284  			}
  1285  		} else {
  1286  			info.DestroyedStorage, info.DetachedStorage, err = storagecommon.ClassifyDetachedStorage(
  1287  				api.storageAccess.VolumeAccess(), api.storageAccess.FilesystemAccess(), storage,
  1288  			)
  1289  			if err != nil {
  1290  				return nil, errors.Trace(err)
  1291  			}
  1292  		}
  1293  		op := unit.DestroyOperation()
  1294  		op.DestroyStorage = arg.DestroyStorage
  1295  		if err := api.backend.ApplyOperation(op); err != nil {
  1296  			return nil, errors.Trace(err)
  1297  		}
  1298  		return &info, nil
  1299  	}
  1300  	results := make([]params.DestroyUnitResult, len(args.Units))
  1301  	for i, entity := range args.Units {
  1302  		info, err := destroyUnit(entity)
  1303  		if err != nil {
  1304  			results[i].Error = common.ServerError(err)
  1305  			continue
  1306  		}
  1307  		results[i].Info = info
  1308  	}
  1309  	return params.DestroyUnitResults{results}, nil
  1310  }
  1311  
  1312  // Destroy destroys a given application, local or remote.
  1313  //
  1314  // NOTE(axw) this exists only for backwards compatibility,
  1315  // for API facade versions 1-3; clients should prefer its
  1316  // successor, DestroyApplication, below. Until all consumers
  1317  // have been updated, or we bump a major version, we can't
  1318  // drop this.
  1319  //
  1320  // TODO(axw) 2017-03-16 #1673323
  1321  // Drop this in Juju 3.0.
  1322  func (api *APIBase) Destroy(in params.ApplicationDestroy) error {
  1323  	if !names.IsValidApplication(in.ApplicationName) {
  1324  		return errors.NotValidf("application name %q", in.ApplicationName)
  1325  	}
  1326  	args := params.DestroyApplicationsParams{
  1327  		Applications: []params.DestroyApplicationParams{{
  1328  			ApplicationTag: names.NewApplicationTag(in.ApplicationName).String(),
  1329  		}},
  1330  	}
  1331  	results, err := api.DestroyApplication(args)
  1332  	if err != nil {
  1333  		return errors.Trace(err)
  1334  	}
  1335  	if err := results.Results[0].Error; err != nil {
  1336  		return common.ServerError(err)
  1337  	}
  1338  	return nil
  1339  }
  1340  
  1341  // DestroyApplication removes a given set of applications.
  1342  //
  1343  // NOTE(axw) this provides backwards compatibility for facade version 4.
  1344  func (api *APIv4) DestroyApplication(args params.Entities) (params.DestroyApplicationResults, error) {
  1345  	v5args := params.DestroyApplicationsParams{
  1346  		Applications: make([]params.DestroyApplicationParams, len(args.Entities)),
  1347  	}
  1348  	for i, arg := range args.Entities {
  1349  		v5args.Applications[i].ApplicationTag = arg.Tag
  1350  	}
  1351  	return api.APIBase.DestroyApplication(v5args)
  1352  }
  1353  
  1354  // DestroyApplication removes a given set of applications.
  1355  func (api *APIBase) DestroyApplication(args params.DestroyApplicationsParams) (params.DestroyApplicationResults, error) {
  1356  	if err := api.checkCanWrite(); err != nil {
  1357  		return params.DestroyApplicationResults{}, err
  1358  	}
  1359  	if err := api.check.RemoveAllowed(); err != nil {
  1360  		return params.DestroyApplicationResults{}, errors.Trace(err)
  1361  	}
  1362  	destroyApp := func(arg params.DestroyApplicationParams) (*params.DestroyApplicationInfo, error) {
  1363  		tag, err := names.ParseApplicationTag(arg.ApplicationTag)
  1364  		if err != nil {
  1365  			return nil, err
  1366  		}
  1367  		var info params.DestroyApplicationInfo
  1368  		app, err := api.backend.Application(tag.Id())
  1369  		if err != nil {
  1370  			return nil, err
  1371  		}
  1372  		units, err := app.AllUnits()
  1373  		if err != nil {
  1374  			return nil, err
  1375  		}
  1376  		storageSeen := names.NewSet()
  1377  		for _, unit := range units {
  1378  			info.DestroyedUnits = append(
  1379  				info.DestroyedUnits,
  1380  				params.Entity{unit.UnitTag().String()},
  1381  			)
  1382  			storage, err := storagecommon.UnitStorage(api.storageAccess, unit.UnitTag())
  1383  			if err != nil {
  1384  				return nil, err
  1385  			}
  1386  
  1387  			// Filter out storage we've already seen. Shared
  1388  			// storage may be attached to multiple units.
  1389  			var unseen []state.StorageInstance
  1390  			for _, stor := range storage {
  1391  				storageTag := stor.StorageTag()
  1392  				if storageSeen.Contains(storageTag) {
  1393  					continue
  1394  				}
  1395  				storageSeen.Add(storageTag)
  1396  				unseen = append(unseen, stor)
  1397  			}
  1398  			storage = unseen
  1399  
  1400  			if arg.DestroyStorage {
  1401  				for _, s := range storage {
  1402  					info.DestroyedStorage = append(
  1403  						info.DestroyedStorage,
  1404  						params.Entity{s.StorageTag().String()},
  1405  					)
  1406  				}
  1407  			} else {
  1408  				destroyed, detached, err := storagecommon.ClassifyDetachedStorage(
  1409  					api.storageAccess.VolumeAccess(), api.storageAccess.FilesystemAccess(), storage,
  1410  				)
  1411  				if err != nil {
  1412  					return nil, err
  1413  				}
  1414  				info.DestroyedStorage = append(info.DestroyedStorage, destroyed...)
  1415  				info.DetachedStorage = append(info.DetachedStorage, detached...)
  1416  			}
  1417  		}
  1418  		op := app.DestroyOperation()
  1419  		op.DestroyStorage = arg.DestroyStorage
  1420  		if err := api.backend.ApplyOperation(op); err != nil {
  1421  			return nil, err
  1422  		}
  1423  		return &info, nil
  1424  	}
  1425  	results := make([]params.DestroyApplicationResult, len(args.Applications))
  1426  	for i, arg := range args.Applications {
  1427  		info, err := destroyApp(arg)
  1428  		if err != nil {
  1429  			results[i].Error = common.ServerError(err)
  1430  			continue
  1431  		}
  1432  		results[i].Info = info
  1433  	}
  1434  	return params.DestroyApplicationResults{results}, nil
  1435  }
  1436  
  1437  // DestroyConsumedApplications removes a given set of consumed (remote) applications.
  1438  func (api *APIBase) DestroyConsumedApplications(args params.DestroyConsumedApplicationsParams) (params.ErrorResults, error) {
  1439  	if err := api.checkCanWrite(); err != nil {
  1440  		return params.ErrorResults{}, err
  1441  	}
  1442  	if err := api.check.RemoveAllowed(); err != nil {
  1443  		return params.ErrorResults{}, errors.Trace(err)
  1444  	}
  1445  	results := make([]params.ErrorResult, len(args.Applications))
  1446  	for i, arg := range args.Applications {
  1447  		appTag, err := names.ParseApplicationTag(arg.ApplicationTag)
  1448  		if err != nil {
  1449  			results[i].Error = common.ServerError(err)
  1450  			continue
  1451  		}
  1452  		app, err := api.backend.RemoteApplication(appTag.Id())
  1453  		if err != nil {
  1454  			results[i].Error = common.ServerError(err)
  1455  			continue
  1456  		}
  1457  		err = app.Destroy()
  1458  		if err != nil {
  1459  			results[i].Error = common.ServerError(err)
  1460  			continue
  1461  		}
  1462  	}
  1463  	return params.ErrorResults{results}, nil
  1464  }
  1465  
  1466  // ScaleApplications isn't on the V7 API.
  1467  func (u *APIv7) ScaleApplications(_, _ struct{}) {}
  1468  
  1469  // ScaleApplications scales the specified application to the requested number of units.
  1470  func (api *APIBase) ScaleApplications(args params.ScaleApplicationsParams) (params.ScaleApplicationResults, error) {
  1471  	if api.modelType != state.ModelTypeCAAS {
  1472  		return params.ScaleApplicationResults{}, errors.NotSupportedf("scaling applications on a non-container model")
  1473  	}
  1474  	if err := api.checkCanWrite(); err != nil {
  1475  		return params.ScaleApplicationResults{}, errors.Trace(err)
  1476  	}
  1477  	scaleApplication := func(arg params.ScaleApplicationParams) (*params.ScaleApplicationInfo, error) {
  1478  		if arg.Scale == 0 && arg.ScaleChange == 0 {
  1479  			return nil, errors.NotValidf("scale of 0")
  1480  		} else if arg.Scale < 0 && arg.ScaleChange == 0 {
  1481  			return nil, errors.NotValidf("scale < 0")
  1482  		} else if arg.Scale != 0 && arg.ScaleChange != 0 {
  1483  			return nil, errors.NotValidf("requesting both scale and scale-change")
  1484  		}
  1485  
  1486  		appTag, err := names.ParseApplicationTag(arg.ApplicationTag)
  1487  		if err != nil {
  1488  			return nil, errors.Trace(err)
  1489  		}
  1490  		name := appTag.Id()
  1491  		app, err := api.backend.Application(name)
  1492  		if errors.IsNotFound(err) {
  1493  			return nil, errors.Errorf("application %q does not exist", name)
  1494  		} else if err != nil {
  1495  			return nil, errors.Trace(err)
  1496  		}
  1497  		var info params.ScaleApplicationInfo
  1498  		if arg.ScaleChange != 0 {
  1499  			newScale, err := app.ChangeScale(arg.ScaleChange)
  1500  			if err != nil {
  1501  				return nil, errors.Trace(err)
  1502  			}
  1503  			info.Scale = newScale
  1504  		} else {
  1505  			if err := app.Scale(arg.Scale); err != nil {
  1506  				return nil, errors.Trace(err)
  1507  			}
  1508  			info.Scale = arg.Scale
  1509  		}
  1510  		return &info, nil
  1511  	}
  1512  	results := make([]params.ScaleApplicationResult, len(args.Applications))
  1513  	for i, entity := range args.Applications {
  1514  		info, err := scaleApplication(entity)
  1515  		if err != nil {
  1516  			results[i].Error = common.ServerError(err)
  1517  			continue
  1518  		}
  1519  		results[i].Info = info
  1520  	}
  1521  	return params.ScaleApplicationResults{results}, nil
  1522  }
  1523  
  1524  // GetConstraints returns the constraints for a given application.
  1525  func (api *APIBase) GetConstraints(args params.Entities) (params.ApplicationGetConstraintsResults, error) {
  1526  	if err := api.checkCanRead(); err != nil {
  1527  		return params.ApplicationGetConstraintsResults{}, errors.Trace(err)
  1528  	}
  1529  	results := params.ApplicationGetConstraintsResults{
  1530  		Results: make([]params.ApplicationConstraint, len(args.Entities)),
  1531  	}
  1532  	for i, arg := range args.Entities {
  1533  		cons, err := api.getConstraints(arg.Tag)
  1534  		results.Results[i].Constraints = cons
  1535  		results.Results[i].Error = common.ServerError(err)
  1536  	}
  1537  	return results, nil
  1538  }
  1539  
  1540  func (api *APIBase) getConstraints(entity string) (constraints.Value, error) {
  1541  	tag, err := names.ParseTag(entity)
  1542  	if err != nil {
  1543  		return constraints.Value{}, err
  1544  	}
  1545  	switch kind := tag.Kind(); kind {
  1546  	case names.ApplicationTagKind:
  1547  		app, err := api.backend.Application(tag.Id())
  1548  		if err != nil {
  1549  			return constraints.Value{}, err
  1550  		}
  1551  		return app.Constraints()
  1552  	default:
  1553  		return constraints.Value{}, errors.Errorf("unexpected tag type, expected application, got %s", kind)
  1554  	}
  1555  }
  1556  
  1557  // SetConstraints sets the constraints for a given application.
  1558  func (api *APIBase) SetConstraints(args params.SetConstraints) error {
  1559  	if err := api.checkCanWrite(); err != nil {
  1560  		return err
  1561  	}
  1562  	if err := api.check.ChangeAllowed(); err != nil {
  1563  		return errors.Trace(err)
  1564  	}
  1565  	app, err := api.backend.Application(args.ApplicationName)
  1566  	if err != nil {
  1567  		return err
  1568  	}
  1569  	return app.SetConstraints(args.Constraints)
  1570  }
  1571  
  1572  // AddRelation adds a relation between the specified endpoints and returns the relation info.
  1573  func (api *APIBase) AddRelation(args params.AddRelation) (_ params.AddRelationResults, err error) {
  1574  	var rel Relation
  1575  	defer func() {
  1576  		if err != nil && rel != nil {
  1577  			if err := rel.Destroy(); err != nil {
  1578  				logger.Errorf("cannot destroy aborted relation %q: %v", rel.Tag().Id(), err)
  1579  			}
  1580  		}
  1581  	}()
  1582  
  1583  	if err := api.check.ChangeAllowed(); err != nil {
  1584  		return params.AddRelationResults{}, errors.Trace(err)
  1585  	}
  1586  	if err := api.checkCanWrite(); err != nil {
  1587  		return params.AddRelationResults{}, errors.Trace(err)
  1588  	}
  1589  
  1590  	// Validate any CIDRs.
  1591  	for _, cidr := range args.ViaCIDRs {
  1592  		if _, _, err := net.ParseCIDR(cidr); err != nil {
  1593  			return params.AddRelationResults{}, errors.Trace(err)
  1594  		}
  1595  		if cidr == "0.0.0.0/0" {
  1596  			return params.AddRelationResults{}, errors.Errorf("CIDR %q not allowed", cidr)
  1597  		}
  1598  	}
  1599  
  1600  	inEps, err := api.backend.InferEndpoints(args.Endpoints...)
  1601  	if err != nil {
  1602  		return params.AddRelationResults{}, errors.Trace(err)
  1603  	}
  1604  	if rel, err = api.backend.AddRelation(inEps...); err != nil {
  1605  		return params.AddRelationResults{}, errors.Trace(err)
  1606  	}
  1607  	if _, err := api.backend.SaveEgressNetworks(rel.Tag().Id(), args.ViaCIDRs); err != nil {
  1608  		return params.AddRelationResults{}, errors.Trace(err)
  1609  	}
  1610  
  1611  	outEps := make(map[string]params.CharmRelation)
  1612  	for _, inEp := range inEps {
  1613  		outEp, err := rel.Endpoint(inEp.ApplicationName)
  1614  		if err != nil {
  1615  			return params.AddRelationResults{}, errors.Trace(err)
  1616  		}
  1617  		outEps[inEp.ApplicationName] = params.CharmRelation{
  1618  			Name:      outEp.Relation.Name,
  1619  			Role:      string(outEp.Relation.Role),
  1620  			Interface: outEp.Relation.Interface,
  1621  			Optional:  outEp.Relation.Optional,
  1622  			Limit:     outEp.Relation.Limit,
  1623  			Scope:     string(outEp.Relation.Scope),
  1624  		}
  1625  	}
  1626  	return params.AddRelationResults{Endpoints: outEps}, nil
  1627  }
  1628  
  1629  // DestroyRelation removes the relation between the
  1630  // specified endpoints or an id.
  1631  func (api *APIBase) DestroyRelation(args params.DestroyRelation) (err error) {
  1632  	if err := api.checkCanWrite(); err != nil {
  1633  		return err
  1634  	}
  1635  	if err := api.check.RemoveAllowed(); err != nil {
  1636  		return errors.Trace(err)
  1637  	}
  1638  	var (
  1639  		rel Relation
  1640  		eps []state.Endpoint
  1641  	)
  1642  	if len(args.Endpoints) > 0 {
  1643  		eps, err = api.backend.InferEndpoints(args.Endpoints...)
  1644  		if err != nil {
  1645  			return err
  1646  		}
  1647  		rel, err = api.backend.EndpointsRelation(eps...)
  1648  	} else {
  1649  		rel, err = api.backend.Relation(args.RelationId)
  1650  	}
  1651  	if err != nil {
  1652  		return err
  1653  	}
  1654  	return rel.Destroy()
  1655  }
  1656  
  1657  // SetRelationsSuspended sets the suspended status of the specified relations.
  1658  func (api *APIBase) SetRelationsSuspended(args params.RelationSuspendedArgs) (params.ErrorResults, error) {
  1659  	var statusResults params.ErrorResults
  1660  	if err := api.checkCanWrite(); err != nil {
  1661  		return statusResults, errors.Trace(err)
  1662  	}
  1663  	if err := api.check.ChangeAllowed(); err != nil {
  1664  		return statusResults, errors.Trace(err)
  1665  	}
  1666  
  1667  	changeOne := func(arg params.RelationSuspendedArg) error {
  1668  		rel, err := api.backend.Relation(arg.RelationId)
  1669  		if err != nil {
  1670  			return errors.Trace(err)
  1671  		}
  1672  		if rel.Suspended() == arg.Suspended {
  1673  			return nil
  1674  		}
  1675  		_, err = api.backend.OfferConnectionForRelation(rel.Tag().Id())
  1676  		if errors.IsNotFound(err) {
  1677  			return errors.Errorf("cannot set suspend status for %q which is not associated with an offer", rel.Tag().Id())
  1678  		}
  1679  		message := arg.Message
  1680  		if !arg.Suspended {
  1681  			message = ""
  1682  		}
  1683  		err = rel.SetSuspended(arg.Suspended, message)
  1684  		if err != nil {
  1685  			return errors.Trace(err)
  1686  		}
  1687  
  1688  		statusValue := status.Joining
  1689  		if arg.Suspended {
  1690  			statusValue = status.Suspending
  1691  		}
  1692  		return rel.SetStatus(status.StatusInfo{
  1693  			Status:  statusValue,
  1694  			Message: arg.Message,
  1695  		})
  1696  	}
  1697  	results := make([]params.ErrorResult, len(args.Args))
  1698  	for i, arg := range args.Args {
  1699  		err := changeOne(arg)
  1700  		results[i].Error = common.ServerError(err)
  1701  	}
  1702  	statusResults.Results = results
  1703  	return statusResults, nil
  1704  }
  1705  
  1706  // Consume adds remote applications to the model without creating any
  1707  // relations.
  1708  func (api *APIBase) Consume(args params.ConsumeApplicationArgs) (params.ErrorResults, error) {
  1709  	var consumeResults params.ErrorResults
  1710  	if err := api.checkCanWrite(); err != nil {
  1711  		return consumeResults, errors.Trace(err)
  1712  	}
  1713  	if err := api.check.ChangeAllowed(); err != nil {
  1714  		return consumeResults, errors.Trace(err)
  1715  	}
  1716  
  1717  	results := make([]params.ErrorResult, len(args.Args))
  1718  	for i, arg := range args.Args {
  1719  		err := api.consumeOne(arg)
  1720  		results[i].Error = common.ServerError(err)
  1721  	}
  1722  	consumeResults.Results = results
  1723  	return consumeResults, nil
  1724  }
  1725  
  1726  func (api *APIBase) consumeOne(arg params.ConsumeApplicationArg) error {
  1727  	sourceModelTag, err := names.ParseModelTag(arg.SourceModelTag)
  1728  	if err != nil {
  1729  		return errors.Trace(err)
  1730  	}
  1731  
  1732  	// Maybe save the details of the controller hosting the offer.
  1733  	if arg.ControllerInfo != nil {
  1734  		controllerTag, err := names.ParseControllerTag(arg.ControllerInfo.ControllerTag)
  1735  		if err != nil {
  1736  			return errors.Trace(err)
  1737  		}
  1738  		// Only save controller details if the offer comes from
  1739  		// a different controller.
  1740  		if controllerTag.Id() != api.backend.ControllerTag().Id() {
  1741  			if _, err = api.backend.SaveController(crossmodel.ControllerInfo{
  1742  				ControllerTag: controllerTag,
  1743  				Alias:         arg.ControllerInfo.Alias,
  1744  				Addrs:         arg.ControllerInfo.Addrs,
  1745  				CACert:        arg.ControllerInfo.CACert,
  1746  			}, sourceModelTag.Id()); err != nil {
  1747  				return errors.Trace(err)
  1748  			}
  1749  		}
  1750  	}
  1751  
  1752  	appName := arg.ApplicationAlias
  1753  	if appName == "" {
  1754  		appName = arg.OfferName
  1755  	}
  1756  	_, err = api.saveRemoteApplication(sourceModelTag, appName, arg.ApplicationOfferDetails, arg.Macaroon)
  1757  	return err
  1758  }
  1759  
  1760  // saveRemoteApplication saves the details of the specified remote application and its endpoints
  1761  // to the state model so relations to the remote application can be created.
  1762  func (api *APIBase) saveRemoteApplication(
  1763  	sourceModelTag names.ModelTag,
  1764  	applicationName string,
  1765  	offer params.ApplicationOfferDetails,
  1766  	mac *macaroon.Macaroon,
  1767  ) (RemoteApplication, error) {
  1768  	remoteEps := make([]charm.Relation, len(offer.Endpoints))
  1769  	for j, ep := range offer.Endpoints {
  1770  		remoteEps[j] = charm.Relation{
  1771  			Name:      ep.Name,
  1772  			Role:      ep.Role,
  1773  			Interface: ep.Interface,
  1774  		}
  1775  	}
  1776  
  1777  	remoteSpaces := make([]*environs.ProviderSpaceInfo, len(offer.Spaces))
  1778  	for i, space := range offer.Spaces {
  1779  		remoteSpaces[i] = providerSpaceInfoFromParams(space)
  1780  	}
  1781  
  1782  	// If the a remote application with the same name and endpoints from the same
  1783  	// source model already exists, we will use that one.
  1784  	remoteApp, err := api.maybeUpdateExistingApplicationEndpoints(applicationName, sourceModelTag, remoteEps)
  1785  	if err == nil {
  1786  		return remoteApp, nil
  1787  	} else if !errors.IsNotFound(err) {
  1788  		return nil, errors.Trace(err)
  1789  	}
  1790  
  1791  	return api.backend.AddRemoteApplication(state.AddRemoteApplicationParams{
  1792  		Name:        applicationName,
  1793  		OfferUUID:   offer.OfferUUID,
  1794  		URL:         offer.OfferURL,
  1795  		SourceModel: sourceModelTag,
  1796  		Endpoints:   remoteEps,
  1797  		Spaces:      remoteSpaces,
  1798  		Bindings:    offer.Bindings,
  1799  		Macaroon:    mac,
  1800  	})
  1801  }
  1802  
  1803  // providerSpaceInfoFromParams converts a params.RemoteSpace to the
  1804  // equivalent ProviderSpaceInfo.
  1805  func providerSpaceInfoFromParams(space params.RemoteSpace) *environs.ProviderSpaceInfo {
  1806  	result := &environs.ProviderSpaceInfo{
  1807  		CloudType:          space.CloudType,
  1808  		ProviderAttributes: space.ProviderAttributes,
  1809  		SpaceInfo: network.SpaceInfo{
  1810  			Name:       space.Name,
  1811  			ProviderId: network.Id(space.ProviderId),
  1812  		},
  1813  	}
  1814  	for _, subnet := range space.Subnets {
  1815  		resultSubnet := network.SubnetInfo{
  1816  			CIDR:              subnet.CIDR,
  1817  			ProviderId:        network.Id(subnet.ProviderId),
  1818  			ProviderNetworkId: network.Id(subnet.ProviderNetworkId),
  1819  			SpaceProviderId:   network.Id(subnet.ProviderSpaceId),
  1820  			VLANTag:           subnet.VLANTag,
  1821  			AvailabilityZones: subnet.Zones,
  1822  		}
  1823  		result.Subnets = append(result.Subnets, resultSubnet)
  1824  	}
  1825  	return result
  1826  }
  1827  
  1828  // maybeUpdateExistingApplicationEndpoints looks for a remote application with the
  1829  // specified name and source model tag and tries to update its endpoints with the
  1830  // new ones specified. If the endpoints are compatible, the newly updated remote
  1831  // application is returned.
  1832  func (api *APIBase) maybeUpdateExistingApplicationEndpoints(
  1833  	applicationName string, sourceModelTag names.ModelTag, remoteEps []charm.Relation,
  1834  ) (RemoteApplication, error) {
  1835  	existingRemoteApp, err := api.backend.RemoteApplication(applicationName)
  1836  	if err != nil {
  1837  		return nil, errors.Trace(err)
  1838  	}
  1839  	if err != nil && !errors.IsNotFound(err) {
  1840  		return nil, errors.Trace(err)
  1841  	}
  1842  	if existingRemoteApp.SourceModel().Id() != sourceModelTag.Id() {
  1843  		return nil, errors.AlreadyExistsf("remote application called %q from a different model", applicationName)
  1844  	}
  1845  	newEpsMap := make(map[charm.Relation]bool)
  1846  	for _, ep := range remoteEps {
  1847  		newEpsMap[ep] = true
  1848  	}
  1849  	existingEps, err := existingRemoteApp.Endpoints()
  1850  	if err != nil {
  1851  		return nil, errors.Trace(err)
  1852  	}
  1853  	maybeSameEndpoints := len(newEpsMap) == len(existingEps)
  1854  	existingEpsByName := make(map[string]charm.Relation)
  1855  	for _, ep := range existingEps {
  1856  		existingEpsByName[ep.Name] = ep.Relation
  1857  		delete(newEpsMap, ep.Relation)
  1858  	}
  1859  	sameEndpoints := maybeSameEndpoints && len(newEpsMap) == 0
  1860  	if sameEndpoints {
  1861  		return existingRemoteApp, nil
  1862  	}
  1863  
  1864  	// Gather the new endpoints. All new endpoints passed to AddEndpoints()
  1865  	// below must not have the same name as an existing endpoint.
  1866  	var newEps []charm.Relation
  1867  	for ep := range newEpsMap {
  1868  		// See if we are attempting to update endpoints with the same name but
  1869  		// different relation data.
  1870  		if existing, ok := existingEpsByName[ep.Name]; ok && existing != ep {
  1871  			return nil, errors.Errorf("conflicting endpoint %v", ep.Name)
  1872  		}
  1873  		newEps = append(newEps, ep)
  1874  	}
  1875  
  1876  	if len(newEps) > 0 {
  1877  		// Update the existing remote app to have the new, additional endpoints.
  1878  		if err := existingRemoteApp.AddEndpoints(newEps); err != nil {
  1879  			return nil, errors.Trace(err)
  1880  		}
  1881  	}
  1882  	return existingRemoteApp, nil
  1883  }
  1884  
  1885  // Mask the new methods from the V4 API. The API reflection code in
  1886  // rpc/rpcreflect/type.go:newMethod skips 2-argument methods, so this
  1887  // removes the method as far as the RPC machinery is concerned.
  1888  
  1889  // UpdateApplicationSeries isn't on the V4 API.
  1890  func (u *APIv4) UpdateApplicationSeries(_, _ struct{}) {}
  1891  
  1892  // GetConfig isn't on the V4 API.
  1893  func (u *APIv4) GetConfig(_, _ struct{}) {}
  1894  
  1895  // GetConstraints returns the v4 implementation of GetConstraints.
  1896  func (api *APIv4) GetConstraints(args params.GetApplicationConstraints) (params.GetConstraintsResults, error) {
  1897  	if err := api.checkCanRead(); err != nil {
  1898  		return params.GetConstraintsResults{}, errors.Trace(err)
  1899  	}
  1900  	app, err := api.backend.Application(args.ApplicationName)
  1901  	if err != nil {
  1902  		return params.GetConstraintsResults{}, errors.Trace(err)
  1903  	}
  1904  	cons, err := app.Constraints()
  1905  	return params.GetConstraintsResults{cons}, errors.Trace(err)
  1906  }
  1907  
  1908  // Mask the new methods from the v4 and v5 API. The API reflection code in
  1909  // rpc/rpcreflect/type.go:newMethod skips 2-argument methods, so this
  1910  // removes the method as far as the RPC machinery is concerned.
  1911  //
  1912  // Since the v4 builds on v5, we can just make the methods unavailable on v5
  1913  // and they will also be unavailable on v4.
  1914  
  1915  // CharmConfig isn't on the v5 API.
  1916  func (u *APIv5) CharmConfig(_, _ struct{}) {}
  1917  
  1918  // CharmConfig is a shim to GetConfig on APIv5. It returns only charm config.
  1919  // Version 8 and below accept params.Entities, where later versions must accept
  1920  // a model generation
  1921  func (api *APIv8) CharmConfig(args params.Entities) (params.ApplicationGetConfigResults, error) {
  1922  	return api.GetConfig(args)
  1923  }
  1924  
  1925  // CharmConfig returns charm config for the input list of applications and
  1926  // model generations.
  1927  func (api *APIBase) CharmConfig(args params.ApplicationGetArgs) (params.ApplicationGetConfigResults, error) {
  1928  	if err := api.checkCanRead(); err != nil {
  1929  		return params.ApplicationGetConfigResults{}, err
  1930  	}
  1931  	results := params.ApplicationGetConfigResults{
  1932  		Results: make([]params.ConfigResult, len(args.Args)),
  1933  	}
  1934  	for i, arg := range args.Args {
  1935  		config, err := api.getCharmConfig(arg.ApplicationName)
  1936  		results.Results[i].Config = config
  1937  		results.Results[i].Error = common.ServerError(err)
  1938  	}
  1939  	return results, nil
  1940  }
  1941  
  1942  // GetConfig returns the charm config for each of the input applications.
  1943  func (api *APIBase) GetConfig(args params.Entities) (params.ApplicationGetConfigResults, error) {
  1944  	if err := api.checkCanRead(); err != nil {
  1945  		return params.ApplicationGetConfigResults{}, err
  1946  	}
  1947  	results := params.ApplicationGetConfigResults{
  1948  		Results: make([]params.ConfigResult, len(args.Entities)),
  1949  	}
  1950  	for i, arg := range args.Entities {
  1951  		tag, err := names.ParseTag(arg.Tag)
  1952  		if err != nil {
  1953  			results.Results[i].Error = common.ServerError(err)
  1954  			continue
  1955  		}
  1956  		if tag.Kind() != names.ApplicationTagKind {
  1957  			results.Results[i].Error = common.ServerError(
  1958  				errors.Errorf("unexpected tag type, expected application, got %s", tag.Kind()))
  1959  			continue
  1960  		}
  1961  
  1962  		config, err := api.getCharmConfig(tag.Id())
  1963  		results.Results[i].Config = config
  1964  		results.Results[i].Error = common.ServerError(err)
  1965  	}
  1966  	return results, nil
  1967  }
  1968  
  1969  func (api *APIBase) getCharmConfig(appName string) (map[string]interface{}, error) {
  1970  	app, err := api.backend.Application(appName)
  1971  	if err != nil {
  1972  		return nil, err
  1973  	}
  1974  	settings, err := app.CharmConfig()
  1975  	if err != nil {
  1976  		return nil, err
  1977  	}
  1978  	ch, _, err := app.Charm()
  1979  	if err != nil {
  1980  		return nil, err
  1981  	}
  1982  	return describe(settings, ch.Config()), nil
  1983  }
  1984  
  1985  // SetApplicationsConfig isn't on the v5 API.
  1986  func (u *APIv5) SetApplicationsConfig(_, _ struct{}) {}
  1987  
  1988  // SetApplicationsConfig implements the server side of Application.SetApplicationsConfig.
  1989  // It does not unset values that are set to an empty string.
  1990  // Unset should be used for that.
  1991  func (api *APIBase) SetApplicationsConfig(args params.ApplicationConfigSetArgs) (params.ErrorResults, error) {
  1992  	var result params.ErrorResults
  1993  	if err := api.checkCanWrite(); err != nil {
  1994  		return result, errors.Trace(err)
  1995  	}
  1996  	if err := api.check.ChangeAllowed(); err != nil {
  1997  		return result, errors.Trace(err)
  1998  	}
  1999  	result.Results = make([]params.ErrorResult, len(args.Args))
  2000  	for i, arg := range args.Args {
  2001  		err := api.setApplicationConfig(arg)
  2002  		result.Results[i].Error = common.ServerError(err)
  2003  	}
  2004  	return result, nil
  2005  }
  2006  
  2007  func (api *APIBase) setApplicationConfig(arg params.ApplicationConfigSet) error {
  2008  	app, err := api.backend.Application(arg.ApplicationName)
  2009  	if err != nil {
  2010  		return errors.Trace(err)
  2011  	}
  2012  
  2013  	appConfigAttrs, charmConfig, err := splitApplicationAndCharmConfig(api.modelType, arg.Config)
  2014  	if err != nil {
  2015  		return errors.Trace(err)
  2016  	}
  2017  	schema, defaults, err := applicationConfigSchema(api.modelType)
  2018  	if err != nil {
  2019  		return errors.Trace(err)
  2020  	}
  2021  
  2022  	if len(appConfigAttrs) > 0 {
  2023  		if err := app.UpdateApplicationConfig(appConfigAttrs, nil, schema, defaults); err != nil {
  2024  			return errors.Annotate(err, "updating application config values")
  2025  		}
  2026  	}
  2027  	if len(charmConfig) > 0 {
  2028  		ch, _, err := app.Charm()
  2029  		if err != nil {
  2030  			return err
  2031  		}
  2032  		// Validate the charm and application config.
  2033  		charmConfigChanges, err := ch.Config().ParseSettingsStrings(charmConfig)
  2034  		if err != nil {
  2035  			return err
  2036  		}
  2037  		if err := app.UpdateCharmConfig(charmConfigChanges); err != nil {
  2038  			return errors.Annotate(err, "updating application charm settings")
  2039  		}
  2040  	}
  2041  	return nil
  2042  }
  2043  
  2044  // UnsetApplicationsConfig isn't on the v5 API.
  2045  func (u *APIv5) UnsetApplicationsConfig(_, _ struct{}) {}
  2046  
  2047  // UnsetApplicationsConfig implements the server side of Application.UnsetApplicationsConfig.
  2048  func (api *APIBase) UnsetApplicationsConfig(args params.ApplicationConfigUnsetArgs) (params.ErrorResults, error) {
  2049  	var result params.ErrorResults
  2050  	if err := api.checkCanWrite(); err != nil {
  2051  		return result, errors.Trace(err)
  2052  	}
  2053  	if err := api.check.ChangeAllowed(); err != nil {
  2054  		return result, errors.Trace(err)
  2055  	}
  2056  	result.Results = make([]params.ErrorResult, len(args.Args))
  2057  	for i, arg := range args.Args {
  2058  		err := api.unsetApplicationConfig(arg)
  2059  		result.Results[i].Error = common.ServerError(err)
  2060  	}
  2061  	return result, nil
  2062  }
  2063  
  2064  func (api *APIBase) unsetApplicationConfig(arg params.ApplicationUnset) error {
  2065  	app, err := api.backend.Application(arg.ApplicationName)
  2066  	if err != nil {
  2067  		return errors.Trace(err)
  2068  	}
  2069  
  2070  	schema, defaults, err := applicationConfigSchema(api.modelType)
  2071  	if err != nil {
  2072  		return errors.Trace(err)
  2073  	}
  2074  	appConfigFields := application.KnownConfigKeys(schema)
  2075  
  2076  	var appConfigKeys []string
  2077  	charmSettings := make(charm.Settings)
  2078  	for _, name := range arg.Options {
  2079  		if appConfigFields.Contains(name) {
  2080  			appConfigKeys = append(appConfigKeys, name)
  2081  		} else {
  2082  			charmSettings[name] = nil
  2083  		}
  2084  	}
  2085  
  2086  	if len(appConfigKeys) > 0 {
  2087  		if err := app.UpdateApplicationConfig(nil, appConfigKeys, schema, defaults); err != nil {
  2088  			return errors.Annotate(err, "updating application config values")
  2089  		}
  2090  	}
  2091  	if len(charmSettings) > 0 {
  2092  		if err := app.UpdateCharmConfig(charmSettings); err != nil {
  2093  			return errors.Annotate(err, "updating application charm settings")
  2094  		}
  2095  	}
  2096  	return nil
  2097  }
  2098  
  2099  // ResolveUnitErrors isn't on the v5 API.
  2100  func (u *APIv5) ResolveUnitErrors(_, _ struct{}) {}
  2101  
  2102  // ResolveUnitErrors marks errors on the specified units as resolved.
  2103  func (api *APIBase) ResolveUnitErrors(p params.UnitsResolved) (params.ErrorResults, error) {
  2104  	if p.All {
  2105  		unitsWithErrors, err := api.backend.UnitsInError()
  2106  		if err != nil {
  2107  			return params.ErrorResults{}, errors.Trace(err)
  2108  		}
  2109  		for _, u := range unitsWithErrors {
  2110  			if err := u.Resolve(p.Retry); err != nil {
  2111  				return params.ErrorResults{}, errors.Annotatef(err, "resolve error for unit %q", u.UnitTag().Id())
  2112  			}
  2113  		}
  2114  	}
  2115  
  2116  	var result params.ErrorResults
  2117  	if err := api.checkCanWrite(); err != nil {
  2118  		return result, errors.Trace(err)
  2119  	}
  2120  	if err := api.check.ChangeAllowed(); err != nil {
  2121  		return result, errors.Trace(err)
  2122  	}
  2123  
  2124  	result.Results = make([]params.ErrorResult, len(p.Tags.Entities))
  2125  	for i, entity := range p.Tags.Entities {
  2126  		tag, err := names.ParseUnitTag(entity.Tag)
  2127  		if err != nil {
  2128  			result.Results[i].Error = common.ServerError(err)
  2129  			continue
  2130  		}
  2131  		unit, err := api.backend.Unit(tag.Id())
  2132  		if err != nil {
  2133  			result.Results[i].Error = common.ServerError(err)
  2134  			continue
  2135  		}
  2136  		err = unit.Resolve(p.Retry)
  2137  		result.Results[i].Error = common.ServerError(err)
  2138  	}
  2139  	return result, nil
  2140  }
  2141  
  2142  // ApplicationInfo isn't on the v8 API.
  2143  func (u *APIv8) ApplicationInfo(_, _ struct{}) {}
  2144  
  2145  // ApplicationsInfo returns applications information.
  2146  func (api *APIBase) ApplicationsInfo(in params.Entities) (params.ApplicationInfoResults, error) {
  2147  	out := make([]params.ApplicationInfoResult, len(in.Entities))
  2148  	for i, one := range in.Entities {
  2149  		tag, err := names.ParseApplicationTag(one.Tag)
  2150  		if err != nil {
  2151  			out[i].Error = common.ServerError(err)
  2152  			continue
  2153  		}
  2154  		app, err := api.backend.Application(tag.Name)
  2155  		if err != nil {
  2156  			out[i].Error = common.ServerError(err)
  2157  			continue
  2158  		}
  2159  
  2160  		details, err := api.getConfig(params.ApplicationGet{ApplicationName: tag.Name}, describe)
  2161  		if err != nil {
  2162  			out[i].Error = common.ServerError(err)
  2163  			continue
  2164  		}
  2165  
  2166  		bindings, err := app.EndpointBindings()
  2167  		if err != nil {
  2168  			out[i].Error = common.ServerError(err)
  2169  			continue
  2170  		}
  2171  
  2172  		out[i].Result = &params.ApplicationInfo{
  2173  			Tag:              tag.String(),
  2174  			Charm:            details.Charm,
  2175  			Series:           details.Series,
  2176  			Channel:          details.Channel,
  2177  			Constraints:      details.Constraints,
  2178  			Principal:        app.IsPrincipal(),
  2179  			Exposed:          app.IsExposed(),
  2180  			Remote:           app.IsRemote(),
  2181  			EndpointBindings: bindings,
  2182  		}
  2183  	}
  2184  	return params.ApplicationInfoResults{out}, nil
  2185  }