github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/apiserver/facades/controller/caasunitprovisioner/provisioner.go (about)

     1  // Copyright 2017 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package caasunitprovisioner
     5  
     6  import (
     7  	"fmt"
     8  	"sort"
     9  	"time"
    10  
    11  	"github.com/juju/charm/v12"
    12  	"github.com/juju/clock"
    13  	"github.com/juju/collections/set"
    14  	"github.com/juju/errors"
    15  	"github.com/juju/loggo"
    16  	"github.com/juju/names/v5"
    17  
    18  	"github.com/juju/juju/apiserver/common"
    19  	charmscommon "github.com/juju/juju/apiserver/common/charms"
    20  	"github.com/juju/juju/apiserver/common/storagecommon"
    21  	apiservererrors "github.com/juju/juju/apiserver/errors"
    22  	"github.com/juju/juju/apiserver/facade"
    23  	"github.com/juju/juju/apiserver/facades/client/application"
    24  	"github.com/juju/juju/apiserver/facades/controller/caasoperatorprovisioner"
    25  	k8sconstants "github.com/juju/juju/caas/kubernetes/provider/constants"
    26  	"github.com/juju/juju/cloudconfig/podcfg"
    27  	"github.com/juju/juju/controller"
    28  	"github.com/juju/juju/core/status"
    29  	"github.com/juju/juju/docker"
    30  	"github.com/juju/juju/environs/config"
    31  	"github.com/juju/juju/environs/tags"
    32  	"github.com/juju/juju/rpc/params"
    33  	"github.com/juju/juju/state"
    34  	stateerrors "github.com/juju/juju/state/errors"
    35  	"github.com/juju/juju/state/watcher"
    36  	"github.com/juju/juju/storage"
    37  	"github.com/juju/juju/storage/poolmanager"
    38  )
    39  
    40  var logger = loggo.GetLogger("juju.apiserver.controller.caasunitprovisioner")
    41  
    42  type Facade struct {
    43  	*common.LifeGetter
    44  	entityWatcher   *common.AgentEntityWatcher
    45  	charmInfoAPI    *charmscommon.CharmInfoAPI
    46  	appCharmInfoAPI *charmscommon.ApplicationCharmInfoAPI
    47  
    48  	resources          facade.Resources
    49  	state              CAASUnitProvisionerState
    50  	storage            StorageBackend
    51  	storagePoolManager poolmanager.PoolManager
    52  	registry           storage.ProviderRegistry
    53  	devices            DeviceBackend
    54  	clock              clock.Clock
    55  }
    56  
    57  // NewFacade returns a new CAAS unit provisioner Facade facade.
    58  func NewFacade(
    59  	resources facade.Resources,
    60  	authorizer facade.Authorizer,
    61  	st CAASUnitProvisionerState,
    62  	sb StorageBackend,
    63  	db DeviceBackend,
    64  	storagePoolManager poolmanager.PoolManager,
    65  	registry storage.ProviderRegistry,
    66  	charmInfoAPI *charmscommon.CharmInfoAPI,
    67  	appCharmInfoAPI *charmscommon.ApplicationCharmInfoAPI,
    68  	clock clock.Clock,
    69  ) (*Facade, error) {
    70  	if !authorizer.AuthController() {
    71  		return nil, apiservererrors.ErrPerm
    72  	}
    73  	accessApplication := common.AuthFuncForTagKind(names.ApplicationTagKind)
    74  	return &Facade{
    75  		entityWatcher:   common.NewAgentEntityWatcher(st, resources, accessApplication),
    76  		charmInfoAPI:    charmInfoAPI,
    77  		appCharmInfoAPI: appCharmInfoAPI,
    78  		LifeGetter: common.NewLifeGetter(
    79  			st, common.AuthAny(
    80  				common.AuthFuncForTagKind(names.ApplicationTagKind),
    81  				common.AuthFuncForTagKind(names.UnitTagKind),
    82  			),
    83  		),
    84  		resources:          resources,
    85  		state:              st,
    86  		storage:            sb,
    87  		devices:            db,
    88  		storagePoolManager: storagePoolManager,
    89  		registry:           registry,
    90  		clock:              clock,
    91  	}, nil
    92  }
    93  
    94  // WatchApplications starts a StringsWatcher to watch applications
    95  // deployed to this model.
    96  func (f *Facade) WatchApplications() (params.StringsWatchResult, error) {
    97  	watch := f.state.WatchApplications()
    98  	if changes, ok := <-watch.Changes(); ok {
    99  		return params.StringsWatchResult{
   100  			StringsWatcherId: f.resources.Register(watch),
   101  			Changes:          changes,
   102  		}, nil
   103  	}
   104  	return params.StringsWatchResult{}, watcher.EnsureErr(watch)
   105  }
   106  
   107  // CharmInfo returns information about the requested charm.
   108  func (f *Facade) CharmInfo(args params.CharmURL) (params.Charm, error) {
   109  	return f.charmInfoAPI.CharmInfo(args)
   110  }
   111  
   112  // ApplicationCharmInfo returns information about an application's charm.
   113  func (f *Facade) ApplicationCharmInfo(args params.Entity) (params.Charm, error) {
   114  	return f.appCharmInfoAPI.ApplicationCharmInfo(args)
   115  }
   116  
   117  // Watch starts a NotifyWatcher for each entity given.
   118  func (f *Facade) Watch(args params.Entities) (params.NotifyWatchResults, error) {
   119  	return f.entityWatcher.Watch(args)
   120  }
   121  
   122  // WatchApplicationsScale starts a NotifyWatcher to watch changes
   123  // to the applications' scale.
   124  func (f *Facade) WatchApplicationsScale(args params.Entities) (params.NotifyWatchResults, error) {
   125  	results := params.NotifyWatchResults{
   126  		Results: make([]params.NotifyWatchResult, len(args.Entities)),
   127  	}
   128  	for i, arg := range args.Entities {
   129  		id, err := f.watchApplicationScale(arg.Tag)
   130  		if err != nil {
   131  			results.Results[i].Error = apiservererrors.ServerError(err)
   132  			continue
   133  		}
   134  		results.Results[i].NotifyWatcherId = id
   135  	}
   136  	return results, nil
   137  }
   138  
   139  func (f *Facade) watchApplicationScale(tagString string) (string, error) {
   140  	tag, err := names.ParseApplicationTag(tagString)
   141  	if err != nil {
   142  		return "", errors.Trace(err)
   143  	}
   144  	app, err := f.state.Application(tag.Id())
   145  	if err != nil {
   146  		return "", errors.Trace(err)
   147  	}
   148  	w := app.WatchScale()
   149  	if _, ok := <-w.Changes(); ok {
   150  		return f.resources.Register(w), nil
   151  	}
   152  	return "", watcher.EnsureErr(w)
   153  }
   154  
   155  // WatchPodSpec starts a NotifyWatcher to watch changes to the
   156  // pod spec for specified units in this model.
   157  func (f *Facade) WatchPodSpec(args params.Entities) (params.NotifyWatchResults, error) {
   158  	model, err := f.state.Model()
   159  	if err != nil {
   160  		return params.NotifyWatchResults{}, errors.Trace(err)
   161  	}
   162  	results := params.NotifyWatchResults{
   163  		Results: make([]params.NotifyWatchResult, len(args.Entities)),
   164  	}
   165  	for i, arg := range args.Entities {
   166  		id, err := f.watchPodSpec(model, arg.Tag)
   167  		if err != nil {
   168  			results.Results[i].Error = apiservererrors.ServerError(err)
   169  			continue
   170  		}
   171  		results.Results[i].NotifyWatcherId = id
   172  	}
   173  	return results, nil
   174  }
   175  
   176  func (f *Facade) watchPodSpec(model Model, tagString string) (string, error) {
   177  	tag, err := names.ParseApplicationTag(tagString)
   178  	if err != nil {
   179  		return "", errors.Trace(err)
   180  	}
   181  	w, err := model.WatchPodSpec(tag)
   182  	if err != nil {
   183  		return "", errors.Trace(err)
   184  	}
   185  	if _, ok := <-w.Changes(); ok {
   186  		return f.resources.Register(w), nil
   187  	}
   188  	return "", watcher.EnsureErr(w)
   189  }
   190  
   191  // ApplicationsScale returns the scaling info for specified applications in this model.
   192  func (f *Facade) ApplicationsScale(args params.Entities) (params.IntResults, error) {
   193  	results := params.IntResults{
   194  		Results: make([]params.IntResult, len(args.Entities)),
   195  	}
   196  	for i, arg := range args.Entities {
   197  		scale, err := f.applicationScale(arg.Tag)
   198  		if err != nil {
   199  			results.Results[i].Error = apiservererrors.ServerError(err)
   200  			continue
   201  		}
   202  		results.Results[i].Result = scale
   203  	}
   204  	logger.Debugf("application scale result: %#v", results)
   205  	return results, nil
   206  }
   207  
   208  func (f *Facade) applicationScale(tagString string) (int, error) {
   209  	appTag, err := names.ParseApplicationTag(tagString)
   210  	if err != nil {
   211  		return 0, errors.Trace(err)
   212  	}
   213  	app, err := f.state.Application(appTag.Id())
   214  	if err != nil {
   215  		return 0, errors.Trace(err)
   216  	}
   217  	return app.GetScale(), nil
   218  }
   219  
   220  // ApplicationsTrust returns the trust status for specified applications in this model.
   221  func (f *Facade) ApplicationsTrust(args params.Entities) (params.BoolResults, error) {
   222  	results := params.BoolResults{
   223  		Results: make([]params.BoolResult, len(args.Entities)),
   224  	}
   225  	for i, arg := range args.Entities {
   226  		trust, err := f.applicationTrust(arg.Tag)
   227  		if err != nil {
   228  			results.Results[i].Error = apiservererrors.ServerError(err)
   229  			continue
   230  		}
   231  		results.Results[i].Result = trust
   232  	}
   233  	logger.Debugf("application trust result: %#v", results)
   234  	return results, nil
   235  }
   236  
   237  func (f *Facade) applicationTrust(tagString string) (bool, error) {
   238  	appTag, err := names.ParseApplicationTag(tagString)
   239  	if err != nil {
   240  		return false, errors.Trace(err)
   241  	}
   242  	app, err := f.state.Application(appTag.Id())
   243  	if err != nil {
   244  		return false, errors.Trace(err)
   245  	}
   246  	cfg, err := app.ApplicationConfig()
   247  	if err != nil {
   248  		return false, errors.Trace(err)
   249  	}
   250  	return cfg.GetBool(application.TrustConfigOptionName, false), nil
   251  }
   252  
   253  // WatchApplicationsTrustHash starts a StringsWatcher to watch changes
   254  // to the applications' trust status.
   255  func (f *Facade) WatchApplicationsTrustHash(args params.Entities) (params.StringsWatchResults, error) {
   256  	results := params.StringsWatchResults{
   257  		Results: make([]params.StringsWatchResult, len(args.Entities)),
   258  	}
   259  	for i, arg := range args.Entities {
   260  		id, err := f.watchApplicationTrustHash(arg.Tag)
   261  		if err != nil {
   262  			results.Results[i].Error = apiservererrors.ServerError(err)
   263  			continue
   264  		}
   265  		results.Results[i].StringsWatcherId = id
   266  	}
   267  	return results, nil
   268  }
   269  
   270  func (f *Facade) watchApplicationTrustHash(tagString string) (string, error) {
   271  	tag, err := names.ParseApplicationTag(tagString)
   272  	if err != nil {
   273  		return "", errors.Trace(err)
   274  	}
   275  	app, err := f.state.Application(tag.Id())
   276  	if err != nil {
   277  		return "", errors.Trace(err)
   278  	}
   279  	// This is currently implemented by just watching the
   280  	// app config settings which is where the trust value
   281  	// is stored. A similar pattern is used for model config
   282  	// watchers pending better filtering on watchers.
   283  	w := app.WatchConfigSettingsHash()
   284  	if _, ok := <-w.Changes(); ok {
   285  		return f.resources.Register(w), nil
   286  	}
   287  	return "", watcher.EnsureErr(w)
   288  }
   289  
   290  // DeploymentMode returns the deployment mode of the given applications' charms.
   291  func (f *Facade) DeploymentMode(args params.Entities) (params.StringResults, error) {
   292  	results := params.StringResults{
   293  		Results: make([]params.StringResult, len(args.Entities)),
   294  	}
   295  	for i, arg := range args.Entities {
   296  		mode, err := f.applicationDeploymentMode(arg.Tag)
   297  		if err != nil {
   298  			results.Results[i].Error = apiservererrors.ServerError(err)
   299  			continue
   300  		}
   301  		results.Results[i].Result = mode
   302  	}
   303  	return results, nil
   304  }
   305  
   306  func (f *Facade) applicationDeploymentMode(tagString string) (string, error) {
   307  	appTag, err := names.ParseApplicationTag(tagString)
   308  	if err != nil {
   309  		return "", errors.Trace(err)
   310  	}
   311  	app, err := f.state.Application(appTag.Id())
   312  	if err != nil {
   313  		return "", errors.Trace(err)
   314  	}
   315  	ch, _, err := app.Charm()
   316  	if err != nil {
   317  		return "", errors.Trace(err)
   318  	}
   319  	var mode charm.DeploymentMode
   320  	if d := ch.Meta().Deployment; d != nil {
   321  		mode = d.DeploymentMode
   322  	}
   323  	if mode == "" {
   324  		mode = charm.ModeWorkload
   325  	}
   326  	return string(mode), nil
   327  }
   328  
   329  // ProvisioningInfo returns the provisioning info for specified applications in this model.
   330  func (f *Facade) ProvisioningInfo(args params.Entities) (params.KubernetesProvisioningInfoResults, error) {
   331  	model, err := f.state.Model()
   332  	if err != nil {
   333  		return params.KubernetesProvisioningInfoResults{}, errors.Trace(err)
   334  	}
   335  	results := params.KubernetesProvisioningInfoResults{
   336  		Results: make([]params.KubernetesProvisioningInfoResult, len(args.Entities)),
   337  	}
   338  	for i, arg := range args.Entities {
   339  		info, err := f.provisioningInfo(model, arg.Tag)
   340  		if err != nil {
   341  			results.Results[i].Error = apiservererrors.ServerError(err)
   342  			continue
   343  		}
   344  		results.Results[i].Result = info
   345  	}
   346  	return results, nil
   347  }
   348  
   349  func (f *Facade) provisioningInfo(model Model, tagString string) (*params.KubernetesProvisioningInfo, error) {
   350  	appTag, err := names.ParseApplicationTag(tagString)
   351  	if err != nil {
   352  		return nil, errors.Trace(err)
   353  	}
   354  	// First the pod spec.
   355  	podSpec, err := model.PodSpec(appTag)
   356  	if err != nil {
   357  		return nil, errors.Trace(err)
   358  	}
   359  	rawSpec, err := model.RawK8sSpec(appTag)
   360  	if err != nil {
   361  		return nil, errors.Trace(err)
   362  	}
   363  	if podSpec != "" && rawSpec != "" {
   364  		// This should never happen.
   365  		return nil, errors.New("both k8s spec and raw k8s spec were set")
   366  	}
   367  
   368  	// Now get any required storage. We need to provision storage
   369  	// at the same time as the pod as it can't be attached later.
   370  
   371  	// All units are currently homogeneous so we just
   372  	// need to get info for the first alive unit.
   373  	app, err := f.state.Application(appTag.Id())
   374  	if err != nil {
   375  		return nil, errors.Trace(err)
   376  	}
   377  	modelConfig, err := model.ModelConfig()
   378  	if err != nil {
   379  		return nil, errors.Trace(err)
   380  	}
   381  
   382  	controllerCfg, err := f.state.ControllerConfig()
   383  	if err != nil {
   384  		return nil, errors.Trace(err)
   385  	}
   386  	vers, ok := modelConfig.AgentVersion()
   387  	if !ok {
   388  		return nil, errors.NewNotValid(nil,
   389  			fmt.Sprintf("agent version is missing in model config %q", modelConfig.Name()),
   390  		)
   391  	}
   392  	registryPath, err := podcfg.GetJujuOCIImagePath(controllerCfg, vers)
   393  	if err != nil {
   394  		return nil, errors.Trace(err)
   395  	}
   396  
   397  	imageRepoDetails, err := docker.NewImageRepoDetails(controllerCfg.CAASImageRepo())
   398  	if err != nil {
   399  		return nil, errors.Annotatef(err, "parsing %s", controller.CAASImageRepo)
   400  	}
   401  	imageRepo := params.NewDockerImageInfo(imageRepoDetails, registryPath)
   402  	logger.Tracef("imageRepo %v", imageRepo)
   403  	filesystemParams, err := f.applicationFilesystemParams(app, controllerCfg, modelConfig)
   404  	if err != nil {
   405  		return nil, errors.Trace(err)
   406  	}
   407  
   408  	devices, err := f.devicesParams(app)
   409  	if err != nil {
   410  		return nil, errors.Trace(err)
   411  	}
   412  	cons, err := app.Constraints()
   413  	if err != nil {
   414  		return nil, errors.Trace(err)
   415  	}
   416  	mergedCons, err := f.state.ResolveConstraints(cons)
   417  	if err != nil {
   418  		return nil, errors.Trace(err)
   419  	}
   420  	resourceTags := tags.ResourceTags(
   421  		names.NewModelTag(modelConfig.UUID()),
   422  		names.NewControllerTag(controllerCfg.ControllerUUID()),
   423  		modelConfig,
   424  	)
   425  
   426  	ch, _, err := app.Charm()
   427  	if err != nil {
   428  		return nil, errors.Trace(err)
   429  	}
   430  
   431  	info := &params.KubernetesProvisioningInfo{
   432  		PodSpec:              podSpec,
   433  		RawK8sSpec:           rawSpec,
   434  		Filesystems:          filesystemParams,
   435  		Devices:              devices,
   436  		Constraints:          mergedCons,
   437  		Tags:                 resourceTags,
   438  		CharmModifiedVersion: app.CharmModifiedVersion(),
   439  		ImageRepo:            imageRepo,
   440  	}
   441  	deployInfo := ch.Meta().Deployment
   442  	if deployInfo != nil {
   443  		info.DeploymentInfo = &params.KubernetesDeploymentInfo{
   444  			DeploymentType: string(deployInfo.DeploymentType),
   445  			ServiceType:    string(deployInfo.ServiceType),
   446  		}
   447  	}
   448  	return info, nil
   449  }
   450  
   451  func filesystemParams(
   452  	app Application,
   453  	cons state.StorageConstraints,
   454  	storageName string,
   455  	controllerUUID string,
   456  	modelConfig *config.Config,
   457  	poolManager poolmanager.PoolManager,
   458  	registry storage.ProviderRegistry,
   459  ) (*params.KubernetesFilesystemParams, error) {
   460  
   461  	filesystemTags, err := storagecommon.StorageTags(nil, modelConfig.UUID(), controllerUUID, modelConfig)
   462  	if err != nil {
   463  		return nil, errors.Annotate(err, "computing storage tags")
   464  	}
   465  	filesystemTags[tags.JujuStorageOwner] = app.Name()
   466  
   467  	storageClassName, _ := modelConfig.AllAttrs()[k8sconstants.WorkloadStorageKey].(string)
   468  	if cons.Pool == "" && storageClassName == "" {
   469  		return nil, errors.Errorf("storage pool for %q must be specified since there's no model default storage class", storageName)
   470  	}
   471  	fsParams, err := caasoperatorprovisioner.CharmStorageParams(controllerUUID, storageClassName, modelConfig, cons.Pool, poolManager, registry)
   472  	if err != nil {
   473  		return nil, errors.Maskf(err, "getting filesystem storage parameters")
   474  	}
   475  
   476  	fsParams.Size = cons.Size
   477  	fsParams.StorageName = storageName
   478  	fsParams.Tags = filesystemTags
   479  	return fsParams, nil
   480  }
   481  
   482  // applicationFilesystemParams retrieves FilesystemParams for the filesystems
   483  // that should be provisioned with, and attached to, pods of the application.
   484  func (f *Facade) applicationFilesystemParams(
   485  	app Application,
   486  	controllerConfig controller.Config,
   487  	modelConfig *config.Config,
   488  ) ([]params.KubernetesFilesystemParams, error) {
   489  	storageConstraints, err := app.StorageConstraints()
   490  	if err != nil {
   491  		return nil, errors.Trace(err)
   492  	}
   493  
   494  	ch, _, err := app.Charm()
   495  	if err != nil {
   496  		return nil, errors.Trace(err)
   497  	}
   498  
   499  	var allFilesystemParams []params.KubernetesFilesystemParams
   500  	// To always guarantee the same order, sort by names.
   501  	var sNames []string
   502  	for name := range storageConstraints {
   503  		sNames = append(sNames, name)
   504  	}
   505  	sort.Strings(sNames)
   506  	for _, name := range sNames {
   507  		cons := storageConstraints[name]
   508  		fsParams, err := filesystemParams(
   509  			app, cons, name,
   510  			controllerConfig.ControllerUUID(),
   511  			modelConfig,
   512  			f.storagePoolManager, f.registry,
   513  		)
   514  		if err != nil {
   515  			return nil, errors.Annotatef(err, "getting filesystem %q parameters", name)
   516  		}
   517  		for i := 0; i < int(cons.Count); i++ {
   518  			charmStorage := ch.Meta().Storage[name]
   519  			id := fmt.Sprintf("%s/%v", name, i)
   520  			tag := names.NewStorageTag(id)
   521  			location, err := state.FilesystemMountPoint(charmStorage, tag, "ubuntu")
   522  			if err != nil {
   523  				return nil, errors.Trace(err)
   524  			}
   525  			filesystemAttachmentParams := params.KubernetesFilesystemAttachmentParams{
   526  				Provider:   fsParams.Provider,
   527  				MountPoint: location,
   528  				ReadOnly:   charmStorage.ReadOnly,
   529  			}
   530  			fsParams.Attachment = &filesystemAttachmentParams
   531  			allFilesystemParams = append(allFilesystemParams, *fsParams)
   532  		}
   533  	}
   534  	return allFilesystemParams, nil
   535  }
   536  
   537  func (f *Facade) devicesParams(app Application) ([]params.KubernetesDeviceParams, error) {
   538  	devices, err := app.DeviceConstraints()
   539  	if err != nil {
   540  		return nil, errors.Trace(err)
   541  	}
   542  	logger.Debugf("getting device constraints from state: %#v", devices)
   543  	var devicesParams []params.KubernetesDeviceParams
   544  	for _, d := range devices {
   545  		devicesParams = append(devicesParams, params.KubernetesDeviceParams{
   546  			Type:       params.DeviceType(d.Type),
   547  			Count:      d.Count,
   548  			Attributes: d.Attributes,
   549  		})
   550  	}
   551  	return devicesParams, nil
   552  }
   553  
   554  // ApplicationsConfig returns the config for the specified applications.
   555  func (f *Facade) ApplicationsConfig(args params.Entities) (params.ApplicationGetConfigResults, error) {
   556  	results := params.ApplicationGetConfigResults{
   557  		Results: make([]params.ConfigResult, len(args.Entities)),
   558  	}
   559  	for i, arg := range args.Entities {
   560  		result, err := f.getApplicationConfig(arg.Tag)
   561  		results.Results[i].Config = result
   562  		results.Results[i].Error = apiservererrors.ServerError(err)
   563  	}
   564  	return results, nil
   565  }
   566  
   567  func (f *Facade) getApplicationConfig(tagString string) (map[string]interface{}, error) {
   568  	tag, err := names.ParseApplicationTag(tagString)
   569  	if err != nil {
   570  		return nil, errors.Trace(err)
   571  	}
   572  	app, err := f.state.Application(tag.Id())
   573  	if err != nil {
   574  		return nil, errors.Trace(err)
   575  	}
   576  	return app.ApplicationConfig()
   577  }
   578  
   579  // UpdateApplicationsUnits updates the Juju data model to reflect the given
   580  // units of the specified application.
   581  func (f *Facade) UpdateApplicationsUnits(args params.UpdateApplicationUnitArgs) (params.UpdateApplicationUnitResults, error) {
   582  	result := params.UpdateApplicationUnitResults{
   583  		Results: make([]params.UpdateApplicationUnitResult, len(args.Args)),
   584  	}
   585  	if len(args.Args) == 0 {
   586  		return result, nil
   587  	}
   588  	for i, appUpdate := range args.Args {
   589  		appTag, err := names.ParseApplicationTag(appUpdate.ApplicationTag)
   590  		if err != nil {
   591  			result.Results[i].Error = apiservererrors.ServerError(err)
   592  			continue
   593  		}
   594  		app, err := f.state.Application(appTag.Id())
   595  		if err != nil {
   596  			result.Results[i].Error = apiservererrors.ServerError(err)
   597  			continue
   598  		}
   599  		appStatus := appUpdate.Status
   600  		if appStatus.Status != "" && appStatus.Status != status.Unknown {
   601  			now := f.clock.Now()
   602  			err = app.SetOperatorStatus(status.StatusInfo{
   603  				Status:  appStatus.Status,
   604  				Message: appStatus.Info,
   605  				Data:    appStatus.Data,
   606  				Since:   &now,
   607  			})
   608  			if err != nil {
   609  				result.Results[i].Error = apiservererrors.ServerError(err)
   610  				continue
   611  			}
   612  		}
   613  		appUnitInfo, err := f.updateUnitsFromCloud(app, appUpdate.Scale, appUpdate.Generation, appUpdate.Units)
   614  		if err != nil {
   615  			// Mask any not found errors as the worker (caller) treats them specially
   616  			// and they are not relevant here.
   617  			result.Results[i].Error = apiservererrors.ServerError(errors.Mask(err))
   618  		}
   619  
   620  		// Errors from SetScale will also include unit info.
   621  		if appUnitInfo != nil {
   622  			result.Results[i].Info = &params.UpdateApplicationUnitsInfo{
   623  				Units: appUnitInfo,
   624  			}
   625  		}
   626  	}
   627  	return result, nil
   628  }
   629  
   630  // updateStatus constructs the agent and cloud container status values.
   631  func (f *Facade) updateStatus(params params.ApplicationUnitParams) (
   632  	agentStatus *status.StatusInfo,
   633  	cloudContainerStatus *status.StatusInfo,
   634  ) {
   635  	var containerStatus status.Status
   636  	switch status.Status(params.Status) {
   637  	case status.Unknown:
   638  		// The container runtime can spam us with unimportant
   639  		// status updates, so ignore any irrelevant ones.
   640  		return nil, nil
   641  	case status.Allocating:
   642  		// The container runtime has decided to restart the pod.
   643  		agentStatus = &status.StatusInfo{
   644  			Status:  status.Allocating,
   645  			Message: params.Info,
   646  		}
   647  		containerStatus = status.Waiting
   648  	case status.Running:
   649  		// A pod has finished starting so the workload is now active.
   650  		agentStatus = &status.StatusInfo{
   651  			Status: status.Idle,
   652  		}
   653  		containerStatus = status.Running
   654  	case status.Error:
   655  		agentStatus = &status.StatusInfo{
   656  			Status:  status.Error,
   657  			Message: params.Info,
   658  			Data:    params.Data,
   659  		}
   660  		containerStatus = status.Error
   661  	case status.Blocked:
   662  		containerStatus = status.Blocked
   663  		agentStatus = &status.StatusInfo{
   664  			Status: status.Idle,
   665  		}
   666  	}
   667  	cloudContainerStatus = &status.StatusInfo{
   668  		Status:  containerStatus,
   669  		Message: params.Info,
   670  		Data:    params.Data,
   671  	}
   672  	return agentStatus, cloudContainerStatus
   673  }
   674  
   675  // updateUnitsFromCloud takes a slice of unit information provided by an external
   676  // source (typically a cloud update event) and merges that with the existing unit
   677  // data model in state. The passed in units are the complete set for the cloud, so
   678  // any existing units in state with provider ids which aren't in the set will be removed.
   679  func (f *Facade) updateUnitsFromCloud(app Application, scale *int,
   680  	generation *int64, unitUpdates []params.ApplicationUnitParams) ([]params.ApplicationUnitInfo, error) {
   681  	logger.Debugf("unit updates: %#v", unitUpdates)
   682  	if scale != nil {
   683  		logger.Debugf("application scale: %v", *scale)
   684  		if *scale > 0 && len(unitUpdates) == 0 {
   685  			// no ops for empty units because we can not determine if it's stateful or not in this case.
   686  			logger.Debugf("ignoring empty k8s event for %q", app.Tag().String())
   687  			return nil, nil
   688  		}
   689  	}
   690  	// Set up the initial data structures.
   691  	existingStateUnits, err := app.AllUnits()
   692  	if err != nil {
   693  		return nil, errors.Trace(err)
   694  	}
   695  	stateUnitsById := make(map[string]stateUnit)
   696  	cloudPodsById := make(map[string]params.ApplicationUnitParams)
   697  
   698  	// Record all unit provider ids known to exist in the cloud.
   699  	for _, u := range unitUpdates {
   700  		cloudPodsById[u.ProviderId] = u
   701  	}
   702  
   703  	stateUnitExistsInCloud := func(providerId string) bool {
   704  		if providerId == "" {
   705  			return false
   706  		}
   707  		_, ok := cloudPodsById[providerId]
   708  		return ok
   709  	}
   710  
   711  	unitInfo := &updateStateUnitParams{
   712  		stateUnitsInCloud: make(map[string]Unit),
   713  		deletedRemoved:    true,
   714  	}
   715  	var (
   716  		// aliveStateIds holds the provider ids of alive units in state.
   717  		aliveStateIds = set.NewStrings()
   718  
   719  		// extraStateIds holds the provider ids of units in state which
   720  		// no longer exist in the cloud.
   721  		extraStateIds = set.NewStrings()
   722  	)
   723  
   724  	// Loop over any existing state units and record those which do not yet have
   725  	// provider ids, and those which have been removed or updated.
   726  	for _, u := range existingStateUnits {
   727  		var providerId string
   728  		info, err := u.ContainerInfo()
   729  		if err != nil && !errors.IsNotFound(err) {
   730  			return nil, errors.Trace(err)
   731  		}
   732  		if err == nil {
   733  			providerId = info.ProviderId()
   734  		}
   735  
   736  		unitAlive := u.Life() == state.Alive
   737  		if !unitAlive {
   738  			continue
   739  		}
   740  		if providerId == "" {
   741  			logger.Debugf("unit %q is not associated with any pod", u.Name())
   742  			unitInfo.unassociatedUnits = append(unitInfo.unassociatedUnits, u)
   743  			continue
   744  		}
   745  
   746  		stateUnitsById[providerId] = stateUnit{Unit: u}
   747  		stateUnitInCloud := stateUnitExistsInCloud(providerId)
   748  		aliveStateIds.Add(providerId)
   749  		if stateUnitInCloud {
   750  			logger.Debugf("unit %q (%v) has changed in the cloud", u.Name(), providerId)
   751  			unitInfo.stateUnitsInCloud[u.UnitTag().String()] = u
   752  		} else {
   753  			logger.Debugf("unit %q (%v) has removed in the cloud", u.Name(), providerId)
   754  			extraStateIds.Add(providerId)
   755  		}
   756  	}
   757  
   758  	// Do it in sorted order so it's deterministic for tests.
   759  	var ids []string
   760  	for id := range cloudPodsById {
   761  		ids = append(ids, id)
   762  	}
   763  	sort.Strings(ids)
   764  
   765  	// Sort extra ids also to guarantee order.
   766  	var extraIds []string
   767  	for id := range extraStateIds {
   768  		extraIds = append(extraIds, id)
   769  	}
   770  	sort.Strings(extraIds)
   771  	unassociatedUnitCount := len(unitInfo.unassociatedUnits)
   772  	extraUnitsInStateCount := 0
   773  	if scale != nil {
   774  		extraUnitsInStateCount = len(stateUnitsById) + unassociatedUnitCount - *scale
   775  	}
   776  
   777  	for _, id := range ids {
   778  		u := cloudPodsById[id]
   779  		unitInfo.deletedRemoved = !u.Stateful
   780  		if aliveStateIds.Contains(id) {
   781  			u.UnitTag = stateUnitsById[id].UnitTag().String()
   782  			unitInfo.existingCloudPods = append(unitInfo.existingCloudPods, u)
   783  			continue
   784  		}
   785  
   786  		// First attempt to add any new cloud pod not yet represented in state
   787  		// to a unit which does not yet have a provider id.
   788  		if unassociatedUnitCount > 0 {
   789  			unassociatedUnitCount--
   790  			unitInfo.addedCloudPods = append(unitInfo.addedCloudPods, u)
   791  			continue
   792  		}
   793  
   794  		// A new pod was added to the cloud but does not yet have a unit in state.
   795  		unitInfo.addedCloudPods = append(unitInfo.addedCloudPods, u)
   796  	}
   797  
   798  	// If there are any extra provider ids left over after allocating all the cloud pods,
   799  	// then consider those state units as terminated.
   800  	logger.Debugf("alive state ids %v", aliveStateIds.Values())
   801  	logger.Debugf("extra state ids %v", extraStateIds.Values())
   802  	logger.Debugf("extra units in state: %v", extraUnitsInStateCount)
   803  	for _, providerId := range extraIds {
   804  		u := stateUnitsById[providerId]
   805  		logger.Debugf("unit %q (%v) has been removed from the cloud", u.Name(), providerId)
   806  		// If the unit in state is surplus to the application scale, remove it from state also.
   807  		// We retain units in state that are not surplus to cloud requirements as they will
   808  		// be regenerated by the cloud and we want to keep a stable unit name.
   809  		u.delete = unitInfo.deletedRemoved && scale != nil
   810  		if !u.delete && extraUnitsInStateCount > 0 {
   811  			logger.Debugf("deleting %v because it exceeds the scale of %v", u.Name(), scale)
   812  			u.delete = true
   813  			extraUnitsInStateCount--
   814  		}
   815  		unitInfo.removedUnits = append(unitInfo.removedUnits, u)
   816  	}
   817  
   818  	if err := f.updateStateUnits(app, unitInfo); err != nil {
   819  		return nil, errors.Trace(err)
   820  	}
   821  
   822  	var providerIds []string
   823  	for _, u := range unitUpdates {
   824  		providerIds = append(providerIds, u.ProviderId)
   825  	}
   826  	m, err := f.state.Model()
   827  	if err != nil {
   828  		return nil, errors.Trace(err)
   829  	}
   830  	containers, err := m.Containers(providerIds...)
   831  	if err != nil {
   832  		return nil, errors.Trace(err)
   833  	}
   834  	var appUnitInfo []params.ApplicationUnitInfo
   835  	for _, c := range containers {
   836  		appUnitInfo = append(appUnitInfo, params.ApplicationUnitInfo{
   837  			ProviderId: c.ProviderId(),
   838  			UnitTag:    names.NewUnitTag(c.Unit()).String(),
   839  		})
   840  	}
   841  
   842  	if scale == nil {
   843  		return appUnitInfo, nil
   844  	}
   845  	// Update the scale last now that the state
   846  	// model accurately reflects the cluster pods.
   847  	currentScale := app.GetScale()
   848  	var gen int64
   849  	if generation != nil {
   850  		gen = *generation
   851  	}
   852  	if currentScale != *scale {
   853  		return appUnitInfo, app.SetScale(*scale, gen, false)
   854  	}
   855  	return appUnitInfo, nil
   856  }
   857  
   858  type stateUnit struct {
   859  	Unit
   860  	delete bool
   861  }
   862  
   863  type updateStateUnitParams struct {
   864  	stateUnitsInCloud map[string]Unit
   865  	addedCloudPods    []params.ApplicationUnitParams
   866  	existingCloudPods []params.ApplicationUnitParams
   867  	removedUnits      []stateUnit
   868  	unassociatedUnits []Unit
   869  	deletedRemoved    bool
   870  }
   871  
   872  type filesystemInfo struct {
   873  	unitTag      names.UnitTag
   874  	providerId   string
   875  	mountPoint   string
   876  	readOnly     bool
   877  	size         uint64
   878  	filesystemId string
   879  }
   880  
   881  type volumeInfo struct {
   882  	unitTag    names.UnitTag
   883  	providerId string
   884  	readOnly   bool
   885  	persistent bool
   886  	size       uint64
   887  	volumeId   string
   888  }
   889  
   890  func (f *Facade) updateStateUnits(app Application, unitInfo *updateStateUnitParams) error {
   891  
   892  	if app.Life() != state.Alive {
   893  		// We ignore any updates for dying applications.
   894  		logger.Debugf("ignoring unit updates for dying application: %v", app.Name())
   895  		return nil
   896  	}
   897  
   898  	logger.Tracef("added cloud units: %+v", unitInfo.addedCloudPods)
   899  	logger.Tracef("existing cloud units: %+v", unitInfo.existingCloudPods)
   900  	logger.Tracef("removed units: %+v", unitInfo.removedUnits)
   901  	logger.Tracef("unassociated units: %+v", unitInfo.unassociatedUnits)
   902  
   903  	// Now we have the added, removed, updated units all sorted,
   904  	// generate the state update operations.
   905  	var unitUpdate state.UpdateUnitsOperation
   906  
   907  	filesystemUpdates := make(map[string]filesystemInfo)
   908  	filesystemStatus := make(map[string]status.StatusInfo)
   909  	volumeUpdates := make(map[string]volumeInfo)
   910  	volumeStatus := make(map[string]status.StatusInfo)
   911  
   912  	for _, u := range unitInfo.removedUnits {
   913  		// If a unit is removed from the cloud, all filesystems are considered detached.
   914  		unitStorage, err := f.storage.UnitStorageAttachments(u.UnitTag())
   915  		if err != nil {
   916  			return errors.Trace(err)
   917  		}
   918  		for _, sa := range unitStorage {
   919  			fs, err := f.storage.StorageInstanceFilesystem(sa.StorageInstance())
   920  			if err != nil {
   921  				return errors.Trace(err)
   922  			}
   923  			filesystemStatus[fs.FilesystemTag().String()] = status.StatusInfo{Status: status.Detached}
   924  		}
   925  
   926  		if u.delete {
   927  			unitUpdate.Deletes = append(unitUpdate.Deletes, u.DestroyOperation())
   928  		}
   929  		// We'll set the status as Terminated. This will either be transient, as will
   930  		// occur when a pod is restarted external to Juju, or permanent if the pod has
   931  		// been deleted external to Juju. In the latter case, juju remove-unit will be
   932  		// need to clean things up on the Juju side.
   933  		cloudContainerStatus := &status.StatusInfo{
   934  			Status:  status.Terminated,
   935  			Message: "unit stopped by the cloud",
   936  		}
   937  		agentStatus := &status.StatusInfo{
   938  			Status: status.Idle,
   939  		}
   940  		updateProps := state.UnitUpdateProperties{
   941  			CloudContainerStatus: cloudContainerStatus,
   942  			AgentStatus:          agentStatus,
   943  		}
   944  		unitUpdate.Updates = append(unitUpdate.Updates,
   945  			u.UpdateOperation(updateProps))
   946  	}
   947  
   948  	processUnitParams := func(unitParams params.ApplicationUnitParams) *state.UnitUpdateProperties {
   949  		agentStatus, cloudContainerStatus := f.updateStatus(unitParams)
   950  		return &state.UnitUpdateProperties{
   951  			ProviderId:           &unitParams.ProviderId,
   952  			Address:              &unitParams.Address,
   953  			Ports:                &unitParams.Ports,
   954  			AgentStatus:          agentStatus,
   955  			CloudContainerStatus: cloudContainerStatus,
   956  		}
   957  	}
   958  
   959  	processFilesystemParams := func(processedFilesystemIds set.Strings, unitTag names.UnitTag, unitParams params.ApplicationUnitParams) error {
   960  		// Once a unit is available in the cluster, we consider
   961  		// its filesystem(s) to be attached since the unit is
   962  		// not considered ready until this happens.
   963  		filesystemInfoByName := make(map[string][]params.KubernetesFilesystemInfo)
   964  		for _, fsInfo := range unitParams.FilesystemInfo {
   965  			infos := filesystemInfoByName[fsInfo.StorageName]
   966  			infos = append(infos, fsInfo)
   967  			filesystemInfoByName[fsInfo.StorageName] = infos
   968  		}
   969  
   970  		for storageName, infos := range filesystemInfoByName {
   971  			logger.Debugf("updating storage %v for %v", storageName, unitTag)
   972  			if len(infos) == 0 {
   973  				continue
   974  			}
   975  
   976  			unitStorage, err := f.storage.UnitStorageAttachments(unitTag)
   977  			if err != nil {
   978  				return errors.Trace(err)
   979  			}
   980  
   981  			// Loop over all the storage for the unit and skip storage not
   982  			// relevant for storageName.
   983  			// TODO(caas) - Add storage bankend API to get all unit storage instances for a named storage.
   984  			for _, sa := range unitStorage {
   985  				si, err := f.storage.StorageInstance(sa.StorageInstance())
   986  				if errors.IsNotFound(err) {
   987  					logger.Warningf("ignoring non-existent storage instance %v for unit %v", sa.StorageInstance(), unitTag.Id())
   988  					continue
   989  				}
   990  				if err != nil {
   991  					return errors.Trace(err)
   992  				}
   993  				if si.StorageName() != storageName {
   994  					continue
   995  				}
   996  				fs, err := f.storage.StorageInstanceFilesystem(sa.StorageInstance())
   997  				if err != nil {
   998  					return errors.Trace(err)
   999  				}
  1000  				fsInfo := infos[0]
  1001  				processedFilesystemIds.Add(fsInfo.FilesystemId)
  1002  
  1003  				// k8s reports provisioned info even when the volume is not ready.
  1004  				// Only update state when volume is created so Juju doesn't think
  1005  				// the volume is active when it's not.
  1006  				if fsInfo.Status != status.Pending.String() {
  1007  					filesystemUpdates[fs.FilesystemTag().String()] = filesystemInfo{
  1008  						unitTag:      unitTag,
  1009  						providerId:   unitParams.ProviderId,
  1010  						mountPoint:   fsInfo.MountPoint,
  1011  						readOnly:     fsInfo.ReadOnly,
  1012  						size:         fsInfo.Size,
  1013  						filesystemId: fsInfo.FilesystemId,
  1014  					}
  1015  				}
  1016  				filesystemStatus[fs.FilesystemTag().String()] = status.StatusInfo{
  1017  					Status:  status.Status(fsInfo.Status),
  1018  					Message: fsInfo.Info,
  1019  					Data:    fsInfo.Data,
  1020  				}
  1021  
  1022  				// If the filesystem has a backing volume, get that info also.
  1023  				if _, err := fs.Volume(); err == nil {
  1024  					vol, err := f.storage.StorageInstanceVolume(sa.StorageInstance())
  1025  					if err != nil {
  1026  						return errors.Trace(err)
  1027  					}
  1028  					if fsInfo.Volume.Status != status.Pending.String() {
  1029  						volumeUpdates[vol.VolumeTag().String()] = volumeInfo{
  1030  							unitTag:    unitTag,
  1031  							providerId: unitParams.ProviderId,
  1032  							size:       fsInfo.Volume.Size,
  1033  							volumeId:   fsInfo.Volume.VolumeId,
  1034  							persistent: fsInfo.Volume.Persistent,
  1035  							readOnly:   fsInfo.ReadOnly,
  1036  						}
  1037  					}
  1038  					volumeStatus[vol.VolumeTag().String()] = status.StatusInfo{
  1039  						Status:  status.Status(fsInfo.Volume.Status),
  1040  						Message: fsInfo.Volume.Info,
  1041  						Data:    fsInfo.Volume.Data,
  1042  					}
  1043  				}
  1044  
  1045  				infos = infos[1:]
  1046  				if len(infos) == 0 {
  1047  					break
  1048  				}
  1049  			}
  1050  		}
  1051  		return nil
  1052  	}
  1053  
  1054  	var unitParamsWithFilesystemInfo []params.ApplicationUnitParams
  1055  
  1056  	for _, unitParams := range unitInfo.existingCloudPods {
  1057  		u, ok := unitInfo.stateUnitsInCloud[unitParams.UnitTag]
  1058  		if !ok {
  1059  			logger.Warningf("unexpected unit parameters %+v not in state", unitParams)
  1060  			continue
  1061  		}
  1062  		updateProps := processUnitParams(unitParams)
  1063  		if len(unitParams.FilesystemInfo) > 0 {
  1064  			unitParamsWithFilesystemInfo = append(unitParamsWithFilesystemInfo, unitParams)
  1065  		}
  1066  		unitUpdate.Updates = append(unitUpdate.Updates,
  1067  			u.UpdateOperation(*updateProps))
  1068  	}
  1069  
  1070  	// For newly added units in the cloud, either update state units which
  1071  	// exist but which do not yet have provider ids (recording the provider
  1072  	// id as well), or add a brand new unit.
  1073  	idx := 0
  1074  	for _, unitParams := range unitInfo.addedCloudPods {
  1075  		if idx < len(unitInfo.unassociatedUnits) {
  1076  			u := unitInfo.unassociatedUnits[idx]
  1077  			updateProps := processUnitParams(unitParams)
  1078  			unitUpdate.Updates = append(unitUpdate.Updates,
  1079  				u.UpdateOperation(*updateProps))
  1080  			idx++
  1081  			if len(unitParams.FilesystemInfo) > 0 {
  1082  				unitParamsWithFilesystemInfo = append(unitParamsWithFilesystemInfo, unitParams)
  1083  			}
  1084  			continue
  1085  		}
  1086  
  1087  		// Process units added directly in the cloud instead of via Juju.
  1088  		updateProps := processUnitParams(unitParams)
  1089  		if len(unitParams.FilesystemInfo) > 0 {
  1090  			unitParamsWithFilesystemInfo = append(unitParamsWithFilesystemInfo, unitParams)
  1091  		}
  1092  		unitUpdate.Adds = append(unitUpdate.Adds,
  1093  			app.AddOperation(*updateProps))
  1094  	}
  1095  	err := app.UpdateUnits(&unitUpdate)
  1096  	// We ignore any updates for dying applications.
  1097  	if stateerrors.IsNotAlive(err) {
  1098  		return nil
  1099  	} else if err != nil {
  1100  		return errors.Trace(err)
  1101  	}
  1102  
  1103  	// Now update filesystem info - attachment data and status.
  1104  	// For units added to the cloud directly, we first need to lookup the
  1105  	// newly created unit tag from Juju using the cloud provider ids.
  1106  	var providerIds []string
  1107  	for _, unitParams := range unitParamsWithFilesystemInfo {
  1108  		if unitParams.UnitTag == "" {
  1109  			providerIds = append(providerIds, unitParams.ProviderId)
  1110  		}
  1111  	}
  1112  	m, err := f.state.Model()
  1113  	if err != nil {
  1114  		return errors.Trace(err)
  1115  	}
  1116  	var providerIdToUnit = make(map[string]names.UnitTag)
  1117  	containers, err := m.Containers(providerIds...)
  1118  	if err != nil {
  1119  		return errors.Trace(err)
  1120  	}
  1121  	for _, c := range containers {
  1122  		providerIdToUnit[c.ProviderId()] = names.NewUnitTag(c.Unit())
  1123  	}
  1124  
  1125  	processedFilesystemIds := set.NewStrings()
  1126  	for _, unitParams := range unitParamsWithFilesystemInfo {
  1127  		var (
  1128  			unitTag names.UnitTag
  1129  			ok      bool
  1130  		)
  1131  		// For units added to the cloud directly, we first need to lookup the
  1132  		// newly created unit tag from Juju using the cloud provider ids.
  1133  		if unitParams.UnitTag == "" {
  1134  			unitTag, ok = providerIdToUnit[unitParams.ProviderId]
  1135  			if !ok {
  1136  				logger.Warningf("cannot update filesystem data for unknown pod %q", unitParams.ProviderId)
  1137  				continue
  1138  			}
  1139  		} else {
  1140  			unitTag, _ = names.ParseUnitTag(unitParams.UnitTag)
  1141  		}
  1142  		if err := processFilesystemParams(processedFilesystemIds, unitTag, unitParams); err != nil {
  1143  			return errors.Annotatef(err, "processing filesystem info for unit %q", unitTag.Id())
  1144  		}
  1145  	}
  1146  
  1147  	// If pods are recreated on the Kubernetes side, new units are created on the Juju
  1148  	// side and so any previously attached filesystems become orphaned and need to
  1149  	// be cleaned up.
  1150  	appName := app.Name()
  1151  	if err := f.cleanupOrphanedFilesystems(processedFilesystemIds); err != nil {
  1152  		return errors.Annotatef(err, "deleting orphaned filesystems for %v", appName)
  1153  	}
  1154  
  1155  	// First do the volume updates as volumes need to be attached before the filesystem updates.
  1156  	if err := f.updateVolumeInfo(volumeUpdates, volumeStatus); err != nil {
  1157  		return errors.Annotatef(err, "updating volume information for %v", appName)
  1158  	}
  1159  
  1160  	err = f.updateFilesystemInfo(filesystemUpdates, filesystemStatus)
  1161  	return errors.Annotatef(err, "updating filesystem information for %v", appName)
  1162  }
  1163  
  1164  func (f *Facade) cleanupOrphanedFilesystems(processedFilesystemIds set.Strings) error {
  1165  	// TODO(caas) - record unit id on the filesystem so we can query by unit
  1166  	allFilesystems, err := f.storage.AllFilesystems()
  1167  	if err != nil {
  1168  		return errors.Trace(err)
  1169  	}
  1170  	for _, fs := range allFilesystems {
  1171  		fsInfo, err := fs.Info()
  1172  		if errors.IsNotProvisioned(err) {
  1173  			continue
  1174  		}
  1175  		if err != nil {
  1176  			return errors.Trace(err)
  1177  		}
  1178  		if !processedFilesystemIds.Contains(fsInfo.FilesystemId) {
  1179  			continue
  1180  		}
  1181  
  1182  		storageTag, err := fs.Storage()
  1183  		if err != nil && !errors.IsNotFound(err) {
  1184  			return errors.Trace(err)
  1185  		}
  1186  		if err != nil {
  1187  			continue
  1188  		}
  1189  
  1190  		si, err := f.storage.StorageInstance(storageTag)
  1191  		if err != nil && !errors.IsNotFound(err) {
  1192  			return errors.Trace(err)
  1193  		}
  1194  		if err != nil {
  1195  			continue
  1196  		}
  1197  		_, ok := si.Owner()
  1198  		if ok {
  1199  			continue
  1200  		}
  1201  
  1202  		logger.Debugf("found orphaned filesystem %v", fs.FilesystemTag())
  1203  		// TODO (anastasiamac 2019-04-04) We can now force storage removal
  1204  		// but for now, while we have not an arg passed in, just hardcode.
  1205  		err = f.storage.DestroyStorageInstance(storageTag, false, false, time.Duration(0))
  1206  		if err != nil && !errors.IsNotFound(err) {
  1207  			return errors.Trace(err)
  1208  		}
  1209  		err = f.storage.DestroyFilesystem(fs.FilesystemTag(), false)
  1210  		if err != nil && !errors.IsNotFound(err) {
  1211  			return errors.Trace(err)
  1212  		}
  1213  	}
  1214  	return nil
  1215  }
  1216  
  1217  func (f *Facade) updateVolumeInfo(volumeUpdates map[string]volumeInfo, volumeStatus map[string]status.StatusInfo) error {
  1218  	// Do it in sorted order so it's deterministic for tests.
  1219  	var volTags []string
  1220  	for tag := range volumeUpdates {
  1221  		volTags = append(volTags, tag)
  1222  	}
  1223  	sort.Strings(volTags)
  1224  
  1225  	logger.Debugf("updating volume data: %+v", volumeUpdates)
  1226  	for _, tagString := range volTags {
  1227  		volTag, _ := names.ParseVolumeTag(tagString)
  1228  		volData := volumeUpdates[tagString]
  1229  
  1230  		vol, err := f.storage.Volume(volTag)
  1231  		if err != nil {
  1232  			return errors.Trace(err)
  1233  		}
  1234  		// If we have already recorded the provisioning info,
  1235  		// it's an error to try and do it again.
  1236  		_, err = vol.Info()
  1237  		if err != nil && !errors.IsNotProvisioned(err) {
  1238  			return errors.Trace(err)
  1239  		}
  1240  		if err != nil {
  1241  			// Provisioning info not set yet.
  1242  			err = f.storage.SetVolumeInfo(volTag, state.VolumeInfo{
  1243  				Size:       volData.size,
  1244  				VolumeId:   volData.volumeId,
  1245  				Persistent: volData.persistent,
  1246  			})
  1247  			if err != nil {
  1248  				return errors.Trace(err)
  1249  			}
  1250  		}
  1251  
  1252  		err = f.storage.SetVolumeAttachmentInfo(volData.unitTag, volTag, state.VolumeAttachmentInfo{
  1253  			ReadOnly: volData.readOnly,
  1254  		})
  1255  		if err != nil {
  1256  			return errors.Trace(err)
  1257  		}
  1258  	}
  1259  
  1260  	// Do it in sorted order so it's deterministic for tests.
  1261  	volTags = []string{}
  1262  	for tag := range volumeStatus {
  1263  		volTags = append(volTags, tag)
  1264  	}
  1265  	sort.Strings(volTags)
  1266  
  1267  	logger.Debugf("updating volume status: %+v", volumeStatus)
  1268  	for _, tagString := range volTags {
  1269  		volTag, _ := names.ParseVolumeTag(tagString)
  1270  		volStatus := volumeStatus[tagString]
  1271  		vol, err := f.storage.Volume(volTag)
  1272  		if err != nil {
  1273  			return errors.Trace(err)
  1274  		}
  1275  		now := f.clock.Now()
  1276  		err = vol.SetStatus(status.StatusInfo{
  1277  			Status:  volStatus.Status,
  1278  			Message: volStatus.Message,
  1279  			Data:    volStatus.Data,
  1280  			Since:   &now,
  1281  		})
  1282  		if err != nil {
  1283  			return errors.Trace(err)
  1284  		}
  1285  	}
  1286  
  1287  	return nil
  1288  }
  1289  
  1290  func (f *Facade) updateFilesystemInfo(filesystemUpdates map[string]filesystemInfo, filesystemStatus map[string]status.StatusInfo) error {
  1291  	// Do it in sorted order so it's deterministic for tests.
  1292  	var fsTags []string
  1293  	for tag := range filesystemUpdates {
  1294  		fsTags = append(fsTags, tag)
  1295  	}
  1296  	sort.Strings(fsTags)
  1297  
  1298  	logger.Debugf("updating filesystem data: %+v", filesystemUpdates)
  1299  	for _, tagString := range fsTags {
  1300  		fsTag, _ := names.ParseFilesystemTag(tagString)
  1301  		fsData := filesystemUpdates[tagString]
  1302  
  1303  		fs, err := f.storage.Filesystem(fsTag)
  1304  		if err != nil {
  1305  			return errors.Trace(err)
  1306  		}
  1307  		// If we have already recorded the provisioning info,
  1308  		// it's an error to try and do it again.
  1309  		_, err = fs.Info()
  1310  		if err != nil && !errors.IsNotProvisioned(err) {
  1311  			return errors.Trace(err)
  1312  		}
  1313  		if err != nil {
  1314  			// Provisioning info not set yet.
  1315  			err = f.storage.SetFilesystemInfo(fsTag, state.FilesystemInfo{
  1316  				Size:         fsData.size,
  1317  				FilesystemId: fsData.filesystemId,
  1318  			})
  1319  			if err != nil {
  1320  				return errors.Trace(err)
  1321  			}
  1322  		}
  1323  
  1324  		err = f.storage.SetFilesystemAttachmentInfo(fsData.unitTag, fsTag, state.FilesystemAttachmentInfo{
  1325  			MountPoint: fsData.mountPoint,
  1326  			ReadOnly:   fsData.readOnly,
  1327  		})
  1328  		if err != nil {
  1329  			return errors.Trace(err)
  1330  		}
  1331  	}
  1332  
  1333  	// Do it in sorted order so it's deterministic for tests.
  1334  	fsTags = []string{}
  1335  	for tag := range filesystemStatus {
  1336  		fsTags = append(fsTags, tag)
  1337  	}
  1338  	sort.Strings(fsTags)
  1339  
  1340  	logger.Debugf("updating filesystem status: %+v", filesystemStatus)
  1341  	for _, tagString := range fsTags {
  1342  		fsTag, _ := names.ParseFilesystemTag(tagString)
  1343  		fsStatus := filesystemStatus[tagString]
  1344  		fs, err := f.storage.Filesystem(fsTag)
  1345  		if err != nil {
  1346  			return errors.Trace(err)
  1347  		}
  1348  		now := f.clock.Now()
  1349  		err = fs.SetStatus(status.StatusInfo{
  1350  			Status:  fsStatus.Status,
  1351  			Message: fsStatus.Message,
  1352  			Data:    fsStatus.Data,
  1353  			Since:   &now,
  1354  		})
  1355  		if err != nil {
  1356  			return errors.Trace(err)
  1357  		}
  1358  	}
  1359  
  1360  	return nil
  1361  }
  1362  
  1363  // ClearApplicationsResources clears the flags which indicate
  1364  // applications still have resources in the cluster.
  1365  func (f *Facade) ClearApplicationsResources(args params.Entities) (params.ErrorResults, error) {
  1366  	result := params.ErrorResults{
  1367  		Results: make([]params.ErrorResult, len(args.Entities)),
  1368  	}
  1369  	if len(args.Entities) == 0 {
  1370  		return result, nil
  1371  	}
  1372  	for i, entity := range args.Entities {
  1373  		appTag, err := names.ParseApplicationTag(entity.Tag)
  1374  		if err != nil {
  1375  			result.Results[i].Error = apiservererrors.ServerError(err)
  1376  			continue
  1377  		}
  1378  		app, err := f.state.Application(appTag.Id())
  1379  		if err != nil {
  1380  			result.Results[i].Error = apiservererrors.ServerError(err)
  1381  			continue
  1382  		}
  1383  		err = app.ClearResources()
  1384  		if err != nil {
  1385  			result.Results[i].Error = apiservererrors.ServerError(err)
  1386  		}
  1387  	}
  1388  	return result, nil
  1389  }
  1390  
  1391  // UpdateApplicationsService updates the Juju data model to reflect the given
  1392  // service details of the specified application.
  1393  func (f *Facade) UpdateApplicationsService(args params.UpdateApplicationServiceArgs) (params.ErrorResults, error) {
  1394  	result := params.ErrorResults{
  1395  		Results: make([]params.ErrorResult, len(args.Args)),
  1396  	}
  1397  	if len(args.Args) == 0 {
  1398  		return result, nil
  1399  	}
  1400  	for i, appUpdate := range args.Args {
  1401  		appTag, err := names.ParseApplicationTag(appUpdate.ApplicationTag)
  1402  		if err != nil {
  1403  			result.Results[i].Error = apiservererrors.ServerError(err)
  1404  			continue
  1405  		}
  1406  		app, err := f.state.Application(appTag.Id())
  1407  		if err != nil {
  1408  			result.Results[i].Error = apiservererrors.ServerError(err)
  1409  			continue
  1410  		}
  1411  
  1412  		sAddrs, err := params.ToProviderAddresses(appUpdate.Addresses...).ToSpaceAddresses(f.state)
  1413  		if err != nil {
  1414  			result.Results[i].Error = apiservererrors.ServerError(err)
  1415  			continue
  1416  		}
  1417  
  1418  		if err := app.UpdateCloudService(appUpdate.ProviderId, sAddrs); err != nil {
  1419  			result.Results[i].Error = apiservererrors.ServerError(err)
  1420  		}
  1421  		if appUpdate.Scale != nil {
  1422  			var generation int64
  1423  			if appUpdate.Generation != nil {
  1424  				generation = *appUpdate.Generation
  1425  			}
  1426  			if err := app.SetScale(*appUpdate.Scale, generation, false); err != nil {
  1427  				result.Results[i].Error = apiservererrors.ServerError(err)
  1428  			}
  1429  		}
  1430  	}
  1431  	return result, nil
  1432  }
  1433  
  1434  // SetOperatorStatus updates the operator status for each given application.
  1435  func (f *Facade) SetOperatorStatus(args params.SetStatus) (params.ErrorResults, error) {
  1436  	result := params.ErrorResults{
  1437  		Results: make([]params.ErrorResult, len(args.Entities)),
  1438  	}
  1439  	for i, arg := range args.Entities {
  1440  		appTag, err := names.ParseApplicationTag(arg.Tag)
  1441  		if err != nil {
  1442  			result.Results[i].Error = apiservererrors.ServerError(err)
  1443  			continue
  1444  		}
  1445  		app, err := f.state.Application(appTag.Id())
  1446  		if err != nil {
  1447  			result.Results[i].Error = apiservererrors.ServerError(err)
  1448  			continue
  1449  		}
  1450  		now := f.clock.Now()
  1451  		s := status.StatusInfo{
  1452  			Status:  status.Status(arg.Status),
  1453  			Message: arg.Info,
  1454  			Data:    arg.Data,
  1455  			Since:   &now,
  1456  		}
  1457  		if err := app.SetOperatorStatus(s); err != nil {
  1458  			result.Results[i].Error = apiservererrors.ServerError(err)
  1459  		}
  1460  	}
  1461  	return result, nil
  1462  }