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

     1  // Copyright 2017 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package application_test
     5  
     6  import (
     7  	"io"
     8  	"strings"
     9  	"sync"
    10  
    11  	"github.com/juju/collections/set"
    12  	"github.com/juju/errors"
    13  	"github.com/juju/juju/caas"
    14  	"github.com/juju/juju/storage/provider"
    15  	"github.com/juju/schema"
    16  	jtesting "github.com/juju/testing"
    17  	"gopkg.in/juju/charm.v6"
    18  	csparams "gopkg.in/juju/charmrepo.v3/csclient/params"
    19  	"gopkg.in/juju/environschema.v1"
    20  	"gopkg.in/juju/names.v2"
    21  	"gopkg.in/macaroon.v2-unstable"
    22  
    23  	"github.com/juju/juju/apiserver/common/storagecommon"
    24  	"github.com/juju/juju/apiserver/facades/client/application"
    25  	coreapplication "github.com/juju/juju/core/application"
    26  	"github.com/juju/juju/core/constraints"
    27  	"github.com/juju/juju/core/crossmodel"
    28  	"github.com/juju/juju/core/instance"
    29  	"github.com/juju/juju/core/status"
    30  	"github.com/juju/juju/environs"
    31  	"github.com/juju/juju/network"
    32  	"github.com/juju/juju/state"
    33  	statestorage "github.com/juju/juju/state/storage"
    34  	"github.com/juju/juju/storage"
    35  	"github.com/juju/juju/storage/poolmanager"
    36  	coretesting "github.com/juju/juju/testing"
    37  )
    38  
    39  type mockEnviron struct {
    40  	environs.NetworkingEnviron
    41  
    42  	stub      jtesting.Stub
    43  	spaceInfo *environs.ProviderSpaceInfo
    44  }
    45  
    46  func (e *mockEnviron) ProviderSpaceInfo(space *network.SpaceInfo) (*environs.ProviderSpaceInfo, error) {
    47  	e.stub.MethodCall(e, "ProviderSpaceInfo", space)
    48  	return e.spaceInfo, e.stub.NextErr()
    49  }
    50  
    51  type mockNoNetworkEnviron struct {
    52  	environs.Environ
    53  }
    54  
    55  type mockCharm struct {
    56  	jtesting.Stub
    57  
    58  	charm.Charm
    59  	config *charm.Config
    60  	meta   *charm.Meta
    61  }
    62  
    63  func (m *mockCharm) Meta() *charm.Meta {
    64  	m.MethodCall(m, "Meta")
    65  	return m.meta
    66  }
    67  
    68  func (c *mockCharm) Config() *charm.Config {
    69  	c.MethodCall(c, "Config")
    70  	c.PopNoErr()
    71  	return c.config
    72  }
    73  
    74  type mockApplication struct {
    75  	jtesting.Stub
    76  	application.Application
    77  
    78  	bindings                 map[string]string
    79  	charm                    *mockCharm
    80  	curl                     *charm.URL
    81  	endpoints                []state.Endpoint
    82  	name                     string
    83  	scale                    int
    84  	subordinate              bool
    85  	series                   string
    86  	units                    []*mockUnit
    87  	addedUnit                mockUnit
    88  	config                   coreapplication.ConfigAttributes
    89  	lxdProfileUpgradeChanges chan struct{}
    90  	constraints              constraints.Value
    91  	channel                  csparams.Channel
    92  	exposed                  bool
    93  	remote                   bool
    94  }
    95  
    96  func (m *mockApplication) Name() string {
    97  	m.MethodCall(m, "Name")
    98  	return m.name
    99  }
   100  
   101  func (m *mockApplication) Channel() csparams.Channel {
   102  	m.MethodCall(m, "Channel")
   103  	return m.channel
   104  }
   105  
   106  func (m *mockApplication) Charm() (application.Charm, bool, error) {
   107  	m.MethodCall(m, "Charm")
   108  	return m.charm, true, nil
   109  }
   110  
   111  func (m *mockApplication) CharmURL() (curl *charm.URL, force bool) {
   112  	m.MethodCall(m, "CharmURL")
   113  	return m.curl, true
   114  }
   115  
   116  func (m *mockApplication) CharmConfig() (charm.Settings, error) {
   117  	m.MethodCall(m, "CharmConfig")
   118  	return m.charm.config.DefaultSettings(), m.NextErr()
   119  }
   120  
   121  func (m *mockApplication) Constraints() (constraints.Value, error) {
   122  	m.MethodCall(m, "Constraints")
   123  	return m.constraints, nil
   124  }
   125  
   126  func (m *mockApplication) Endpoints() ([]state.Endpoint, error) {
   127  	m.MethodCall(m, "Endpoints")
   128  	return m.endpoints, nil
   129  }
   130  
   131  func (m *mockApplication) EndpointBindings() (map[string]string, error) {
   132  	m.MethodCall(m, "EndpointBindings")
   133  	return m.bindings, m.NextErr()
   134  }
   135  
   136  func (a *mockApplication) AllUnits() ([]application.Unit, error) {
   137  	a.MethodCall(a, "AllUnits")
   138  	if err := a.NextErr(); err != nil {
   139  		return nil, err
   140  	}
   141  	units := make([]application.Unit, len(a.units))
   142  	for i := range a.units {
   143  		units[i] = a.units[i]
   144  	}
   145  	return units, nil
   146  }
   147  
   148  func (a *mockApplication) SetCharmProfile(charmURL string) error {
   149  	a.MethodCall(a, "SetCharmProfile", charmURL)
   150  	return a.NextErr()
   151  }
   152  
   153  func (a *mockApplication) SetCharm(cfg state.SetCharmConfig) error {
   154  	a.MethodCall(a, "SetCharm", cfg)
   155  	return a.NextErr()
   156  }
   157  
   158  func (a *mockApplication) DestroyOperation() *state.DestroyApplicationOperation {
   159  	a.MethodCall(a, "DestroyOperation")
   160  	return &state.DestroyApplicationOperation{}
   161  }
   162  
   163  func (a *mockApplication) AddUnit(args state.AddUnitParams) (application.Unit, error) {
   164  	a.MethodCall(a, "AddUnit", args)
   165  	if err := a.NextErr(); err != nil {
   166  		return nil, err
   167  	}
   168  	return &a.addedUnit, nil
   169  }
   170  
   171  func (a *mockApplication) GetScale() int {
   172  	a.MethodCall(a, "GetScale")
   173  	return a.scale
   174  }
   175  
   176  func (a *mockApplication) ChangeScale(scaleChange int) (int, error) {
   177  	a.MethodCall(a, "ChangeScale", scaleChange)
   178  	if err := a.NextErr(); err != nil {
   179  		return a.scale, err
   180  	}
   181  	return a.scale + scaleChange, nil
   182  }
   183  
   184  func (a *mockApplication) Scale(scale int) error {
   185  	a.MethodCall(a, "Scale", scale)
   186  	if err := a.NextErr(); err != nil {
   187  		return err
   188  	}
   189  	return nil
   190  }
   191  
   192  func (a *mockApplication) IsPrincipal() bool {
   193  	a.MethodCall(a, "IsPrincipal")
   194  	a.PopNoErr()
   195  	return !a.subordinate
   196  }
   197  
   198  func (a *mockApplication) UpdateApplicationSeries(series string, force bool) error {
   199  	a.MethodCall(a, "UpdateApplicationSeries", series, force)
   200  	return a.NextErr()
   201  }
   202  
   203  func (a *mockApplication) Series() string {
   204  	a.MethodCall(a, "Series")
   205  	a.PopNoErr()
   206  	return a.series
   207  }
   208  
   209  func (a *mockApplication) ApplicationConfig() (coreapplication.ConfigAttributes, error) {
   210  	a.MethodCall(a, "ApplicationConfig")
   211  	return a.config, a.NextErr()
   212  }
   213  
   214  func (a *mockApplication) UpdateApplicationConfig(
   215  	changes coreapplication.ConfigAttributes,
   216  	reset []string,
   217  	extra environschema.Fields,
   218  	defaults schema.Defaults,
   219  ) error {
   220  	a.MethodCall(a, "UpdateApplicationConfig", changes, reset, extra, defaults)
   221  	return a.NextErr()
   222  }
   223  
   224  func (a *mockApplication) UpdateCharmConfig(settings charm.Settings) error {
   225  	a.MethodCall(a, "UpdateCharmConfig", settings)
   226  	return a.NextErr()
   227  }
   228  
   229  func (a *mockApplication) SetExposed() error {
   230  	a.MethodCall(a, "SetExposed")
   231  	return a.NextErr()
   232  }
   233  
   234  func (a *mockApplication) WatchLXDProfileUpgradeNotifications() (state.NotifyWatcher, error) {
   235  	a.MethodCall(a, "WatchLXDProfileUpgradeNotifications")
   236  	return &mockNotifyWatcher{ch: a.lxdProfileUpgradeChanges}, a.NextErr()
   237  }
   238  
   239  func (a *mockApplication) IsExposed() bool {
   240  	a.MethodCall(a, "IsExposed")
   241  	return a.exposed
   242  }
   243  
   244  func (a *mockApplication) IsRemote() bool {
   245  	a.MethodCall(a, "IsRemote")
   246  	return a.remote
   247  }
   248  
   249  type mockNotifyWatcher struct {
   250  	state.NotifyWatcher
   251  	jtesting.Stub
   252  	ch chan struct{}
   253  }
   254  
   255  func (m *mockNotifyWatcher) Changes() <-chan struct{} {
   256  	m.MethodCall(m, "Changes")
   257  	return m.ch
   258  }
   259  
   260  func (m *mockNotifyWatcher) Err() error {
   261  	return m.NextErr()
   262  }
   263  
   264  type mockRemoteApplication struct {
   265  	jtesting.Stub
   266  	name           string
   267  	sourceModelTag names.ModelTag
   268  	endpoints      []state.Endpoint
   269  	bindings       map[string]string
   270  	spaces         []state.RemoteSpace
   271  	offerUUID      string
   272  	offerURL       string
   273  	mac            *macaroon.Macaroon
   274  }
   275  
   276  func (m *mockRemoteApplication) Name() string {
   277  	return m.name
   278  }
   279  
   280  func (m *mockRemoteApplication) SourceModel() names.ModelTag {
   281  	return m.sourceModelTag
   282  }
   283  
   284  func (m *mockRemoteApplication) Endpoints() ([]state.Endpoint, error) {
   285  	return m.endpoints, nil
   286  }
   287  
   288  func (m *mockRemoteApplication) Bindings() map[string]string {
   289  	return m.bindings
   290  }
   291  
   292  func (m *mockRemoteApplication) Spaces() []state.RemoteSpace {
   293  	return m.spaces
   294  }
   295  
   296  func (m *mockRemoteApplication) AddEndpoints(eps []charm.Relation) error {
   297  	for _, ep := range eps {
   298  		m.endpoints = append(m.endpoints, state.Endpoint{
   299  			ApplicationName: m.name,
   300  			Relation: charm.Relation{
   301  				Name:      ep.Name,
   302  				Interface: ep.Interface,
   303  				Role:      ep.Role,
   304  			},
   305  		})
   306  	}
   307  	return nil
   308  }
   309  
   310  func (m *mockRemoteApplication) Destroy() error {
   311  	m.MethodCall(m, "Destroy")
   312  	return nil
   313  }
   314  
   315  type mockBackend struct {
   316  	jtesting.Stub
   317  	application.Backend
   318  
   319  	charm                      *mockCharm
   320  	allmodels                  []application.Model
   321  	users                      set.Strings
   322  	applications               map[string]*mockApplication
   323  	remoteApplications         map[string]application.RemoteApplication
   324  	endpoints                  *[]state.Endpoint
   325  	relations                  map[int]*mockRelation
   326  	offerConnections           map[string]application.OfferConnection
   327  	unitStorageAttachments     map[string][]state.StorageAttachment
   328  	storageInstances           map[string]*mockStorage
   329  	storageInstanceFilesystems map[string]*mockFilesystem
   330  	controllers                map[string]crossmodel.ControllerInfo
   331  	machines                   map[string]*mockMachine
   332  }
   333  
   334  type mockFilesystemAccess struct {
   335  	storagecommon.FilesystemAccess
   336  	*mockBackend
   337  }
   338  
   339  func (m *mockBackend) VolumeAccess() storagecommon.VolumeAccess {
   340  	return nil
   341  }
   342  
   343  func (m *mockBackend) FilesystemAccess() storagecommon.FilesystemAccess {
   344  	return &mockFilesystemAccess{mockBackend: m}
   345  }
   346  
   347  func (m *mockBackend) ControllerTag() names.ControllerTag {
   348  	return coretesting.ControllerTag
   349  }
   350  
   351  func (m *mockBackend) GetBlockForType(t state.BlockType) (state.Block, bool, error) {
   352  	return nil, false, nil
   353  }
   354  
   355  func (m *mockBackend) Charm(curl *charm.URL) (application.Charm, error) {
   356  	m.MethodCall(m, "Charm", curl)
   357  	if err := m.NextErr(); err != nil {
   358  		return nil, err
   359  	}
   360  	if m.charm != nil {
   361  		return m.charm, nil
   362  	}
   363  	return nil, errors.NotFoundf("charm %q", curl)
   364  }
   365  
   366  func (m *mockBackend) Unit(name string) (application.Unit, error) {
   367  	m.MethodCall(m, "Unit", name)
   368  	if err := m.NextErr(); err != nil {
   369  		return nil, err
   370  	}
   371  	var unitApp *mockApplication
   372  	for appName, app := range m.applications {
   373  		if strings.HasPrefix(name, appName+"/") {
   374  			unitApp = app
   375  			break
   376  		}
   377  	}
   378  	if unitApp != nil {
   379  		for _, u := range unitApp.units {
   380  			if u.tag.Id() == name {
   381  				return u, nil
   382  			}
   383  		}
   384  	}
   385  	return nil, errors.NotFoundf("unit %q", name)
   386  }
   387  
   388  func (m *mockBackend) UnitsInError() ([]application.Unit, error) {
   389  	return []application.Unit{
   390  		m.applications["postgresql"].units[0],
   391  	}, nil
   392  }
   393  
   394  func (m *mockBackend) Machine(id string) (application.Machine, error) {
   395  	m.MethodCall(m, "Machine", id)
   396  	for machineId, machine := range m.machines {
   397  		if id == machineId {
   398  			return machine, nil
   399  		}
   400  	}
   401  	return nil, errors.NotFoundf("machine %q", id)
   402  }
   403  
   404  type mockMachine struct {
   405  	jtesting.Stub
   406  
   407  	id                          string
   408  	upgradeCharmProfileComplete string
   409  }
   410  
   411  func (m *mockMachine) IsLockedForSeriesUpgrade() (bool, error) {
   412  	m.MethodCall(m, "IsLockedForSeriesUpgrade")
   413  	return false, m.NextErr()
   414  }
   415  
   416  func (m *mockMachine) IsParentLockedForSeriesUpgrade() (bool, error) {
   417  	m.MethodCall(m, "IsParentLockedForSeriesUpgrade")
   418  	return false, m.NextErr()
   419  }
   420  
   421  func (m *mockMachine) Id() string {
   422  	m.MethodCall(m, "Id")
   423  	return m.id
   424  }
   425  
   426  func (m *mockMachine) UpgradeCharmProfileComplete() (string, error) {
   427  	m.MethodCall(m, "UpgradeCharmProfileComplete")
   428  	return m.upgradeCharmProfileComplete, m.NextErr()
   429  }
   430  
   431  func (m *mockMachine) RemoveUpgradeCharmProfileData() error {
   432  	m.MethodCall(m, "RemoveUpgradeCharmProfileData")
   433  	return m.NextErr()
   434  }
   435  
   436  func (m *mockBackend) InferEndpoints(endpoints ...string) ([]state.Endpoint, error) {
   437  	m.MethodCall(m, "InferEndpoints", endpoints)
   438  	if err := m.NextErr(); err != nil {
   439  		return nil, err
   440  	}
   441  	if m.endpoints != nil {
   442  		return *m.endpoints, nil
   443  	}
   444  	return nil, errors.Errorf("no relations found")
   445  }
   446  
   447  func (m *mockBackend) EndpointsRelation(endpoints ...state.Endpoint) (application.Relation, error) {
   448  	m.MethodCall(m, "EndpointsRelation", endpoints)
   449  	if err := m.NextErr(); err != nil {
   450  		return nil, err
   451  	}
   452  	if rel, ok := m.relations[123]; ok {
   453  		return rel, nil
   454  	}
   455  	return nil, errors.NotFoundf("relation")
   456  }
   457  
   458  func (m *mockBackend) Relation(id int) (application.Relation, error) {
   459  	m.MethodCall(m, "Relation", id)
   460  	if err := m.NextErr(); err != nil {
   461  		return nil, err
   462  	}
   463  	if rel, ok := m.relations[id]; ok {
   464  		return rel, nil
   465  	}
   466  	return nil, errors.NotFoundf("relation")
   467  }
   468  
   469  type mockOfferConnection struct {
   470  	application.OfferConnection
   471  }
   472  
   473  func (m *mockBackend) OfferConnectionForRelation(key string) (application.OfferConnection, error) {
   474  	m.MethodCall(m, "OfferConnectionForRelation", key)
   475  	if err := m.NextErr(); err != nil {
   476  		return nil, err
   477  	}
   478  	if oc, ok := m.offerConnections[key]; ok {
   479  		return oc, nil
   480  	}
   481  	return nil, errors.NotFoundf("offer connection for relation")
   482  }
   483  
   484  func (m *mockBackend) UnitStorageAttachments(tag names.UnitTag) ([]state.StorageAttachment, error) {
   485  	m.MethodCall(m, "UnitStorageAttachments", tag)
   486  	if err := m.NextErr(); err != nil {
   487  		return nil, err
   488  	}
   489  	return m.unitStorageAttachments[tag.Id()], nil
   490  }
   491  
   492  func (m *mockBackend) StorageInstance(tag names.StorageTag) (state.StorageInstance, error) {
   493  	m.MethodCall(m, "StorageInstance", tag)
   494  	if err := m.NextErr(); err != nil {
   495  		return nil, err
   496  	}
   497  	s, ok := m.storageInstances[tag.Id()]
   498  	if !ok {
   499  		return nil, errors.NotFoundf("storage %s", tag.Id())
   500  	}
   501  	return s, nil
   502  }
   503  
   504  func (m *mockFilesystemAccess) StorageInstanceFilesystem(tag names.StorageTag) (state.Filesystem, error) {
   505  	m.MethodCall(m, "StorageInstanceFilesystem", tag)
   506  	if err := m.NextErr(); err != nil {
   507  		return nil, err
   508  	}
   509  	f, ok := m.storageInstanceFilesystems[tag.Id()]
   510  	if !ok {
   511  		return nil, errors.NotFoundf("filesystem for storage %s", tag.Id())
   512  	}
   513  	return f, nil
   514  }
   515  
   516  func (m *mockBackend) AddRemoteApplication(args state.AddRemoteApplicationParams) (application.RemoteApplication, error) {
   517  	m.MethodCall(m, "AddRemoteApplication", args)
   518  	if err := m.NextErr(); err != nil {
   519  		return nil, err
   520  	}
   521  	app := &mockRemoteApplication{
   522  		name:           args.Name,
   523  		sourceModelTag: args.SourceModel,
   524  		offerUUID:      args.OfferUUID,
   525  		offerURL:       args.URL,
   526  		bindings:       args.Bindings,
   527  		mac:            args.Macaroon,
   528  	}
   529  	for _, ep := range args.Endpoints {
   530  		app.endpoints = append(app.endpoints, state.Endpoint{
   531  			ApplicationName: app.name,
   532  			Relation: charm.Relation{
   533  				Name:      ep.Name,
   534  				Interface: ep.Interface,
   535  				Role:      ep.Role,
   536  			},
   537  		})
   538  	}
   539  	for _, sp := range args.Spaces {
   540  		remoteSpaceInfo := state.RemoteSpace{
   541  			CloudType:          sp.CloudType,
   542  			Name:               sp.Name,
   543  			ProviderId:         string(sp.ProviderId),
   544  			ProviderAttributes: sp.ProviderAttributes,
   545  		}
   546  		for _, sn := range sp.Subnets {
   547  			remoteSpaceInfo.Subnets = append(remoteSpaceInfo.Subnets, state.RemoteSubnet{
   548  				CIDR:              sn.CIDR,
   549  				VLANTag:           sn.VLANTag,
   550  				ProviderId:        string(sn.ProviderId),
   551  				ProviderNetworkId: string(sn.ProviderNetworkId),
   552  				AvailabilityZones: sn.AvailabilityZones,
   553  			})
   554  		}
   555  		app.spaces = append(app.spaces, remoteSpaceInfo)
   556  	}
   557  	m.remoteApplications[app.name] = app
   558  	return app, nil
   559  }
   560  
   561  func (m *mockBackend) RemoteApplication(name string) (application.RemoteApplication, error) {
   562  	m.MethodCall(m, "RemoteApplication", name)
   563  	if err := m.NextErr(); err != nil {
   564  		return nil, err
   565  	}
   566  	app, ok := m.remoteApplications[name]
   567  	if !ok {
   568  		return nil, errors.NotFoundf("remote application %q", name)
   569  	}
   570  	return app, nil
   571  }
   572  
   573  func (m *mockBackend) Application(name string) (application.Application, error) {
   574  	m.MethodCall(m, "Application", name)
   575  	if err := m.NextErr(); err != nil {
   576  		return nil, err
   577  	}
   578  	app, ok := m.applications[name]
   579  	if !ok {
   580  		return nil, errors.NotFoundf("application %q", name)
   581  	}
   582  	return app, nil
   583  }
   584  
   585  func (m *mockBackend) ApplyOperation(op state.ModelOperation) error {
   586  	m.MethodCall(m, "ApplyOperation", op)
   587  	return m.NextErr()
   588  }
   589  
   590  type mockExternalController struct {
   591  	uuid string
   592  	info crossmodel.ControllerInfo
   593  }
   594  
   595  func (m *mockExternalController) Id() string {
   596  	return m.uuid
   597  }
   598  
   599  func (m *mockExternalController) ControllerInfo() crossmodel.ControllerInfo {
   600  	return m.info
   601  }
   602  
   603  func (m *mockBackend) SaveController(controllerInfo crossmodel.ControllerInfo, modelUUID string) (application.ExternalController, error) {
   604  	m.controllers[modelUUID] = controllerInfo
   605  	return &mockExternalController{controllerInfo.ControllerTag.Id(), controllerInfo}, nil
   606  }
   607  
   608  type mockBlockChecker struct {
   609  	jtesting.Stub
   610  }
   611  
   612  func (c *mockBlockChecker) ChangeAllowed() error {
   613  	c.MethodCall(c, "ChangeAllowed")
   614  	return c.NextErr()
   615  }
   616  
   617  func (c *mockBlockChecker) RemoveAllowed() error {
   618  	c.MethodCall(c, "RemoveAllowed")
   619  	return c.NextErr()
   620  }
   621  
   622  type mockRelation struct {
   623  	application.Relation
   624  	jtesting.Stub
   625  
   626  	tag             names.Tag
   627  	status          status.Status
   628  	message         string
   629  	suspended       bool
   630  	suspendedReason string
   631  }
   632  
   633  func (r *mockRelation) Tag() names.Tag {
   634  	return r.tag
   635  }
   636  
   637  func (r *mockRelation) SetStatus(status status.StatusInfo) error {
   638  	r.MethodCall(r, "SetStatus")
   639  	r.status = status.Status
   640  	r.message = status.Message
   641  	return r.NextErr()
   642  }
   643  
   644  func (r *mockRelation) SetSuspended(suspended bool, reason string) error {
   645  	r.MethodCall(r, "SetSuspended")
   646  	r.suspended = suspended
   647  	r.suspendedReason = reason
   648  	return r.NextErr()
   649  }
   650  
   651  func (r *mockRelation) Suspended() bool {
   652  	r.MethodCall(r, "Suspended")
   653  	return r.suspended
   654  }
   655  
   656  func (r *mockRelation) SuspendedReason() string {
   657  	r.MethodCall(r, "SuspendedReason")
   658  	return r.suspendedReason
   659  }
   660  
   661  func (r *mockRelation) Destroy() error {
   662  	r.MethodCall(r, "Destroy")
   663  	return r.NextErr()
   664  }
   665  
   666  type mockUnit struct {
   667  	application.Unit
   668  	jtesting.Stub
   669  	tag       names.UnitTag
   670  	machineId string
   671  	name      string
   672  }
   673  
   674  func (u *mockUnit) Tag() names.Tag {
   675  	return u.tag
   676  }
   677  
   678  func (u *mockUnit) UnitTag() names.UnitTag {
   679  	return u.tag
   680  }
   681  
   682  func (u *mockUnit) IsPrincipal() bool {
   683  	u.MethodCall(u, "IsPrincipal")
   684  	u.PopNoErr()
   685  	return true
   686  }
   687  
   688  func (u *mockUnit) DestroyOperation() *state.DestroyUnitOperation {
   689  	u.MethodCall(u, "DestroyOperation")
   690  	return &state.DestroyUnitOperation{}
   691  }
   692  
   693  func (u *mockUnit) AssignWithPolicy(policy state.AssignmentPolicy) error {
   694  	u.MethodCall(u, "AssignWithPolicy", policy)
   695  	return u.NextErr()
   696  }
   697  
   698  func (u *mockUnit) AssignWithPlacement(placement *instance.Placement) error {
   699  	u.MethodCall(u, "AssignWithPlacement", placement)
   700  	return u.NextErr()
   701  }
   702  
   703  func (u *mockUnit) Resolve(retryHooks bool) error {
   704  	u.MethodCall(u, "Resolve", retryHooks)
   705  	return u.NextErr()
   706  }
   707  
   708  func (u *mockUnit) AssignedMachineId() (string, error) {
   709  	u.MethodCall(u, "AssignedMachineId")
   710  	return u.machineId, u.NextErr()
   711  }
   712  
   713  func (u *mockUnit) Name() string {
   714  	u.MethodCall(u, "Name")
   715  	return u.name
   716  }
   717  
   718  type mockStorageAttachment struct {
   719  	state.StorageAttachment
   720  	jtesting.Stub
   721  	unit    names.UnitTag
   722  	storage names.StorageTag
   723  }
   724  
   725  func (a *mockStorageAttachment) Unit() names.UnitTag {
   726  	return a.unit
   727  }
   728  
   729  func (a *mockStorageAttachment) StorageInstance() names.StorageTag {
   730  	return a.storage
   731  }
   732  
   733  type mockStorage struct {
   734  	state.StorageInstance
   735  	jtesting.Stub
   736  	tag   names.StorageTag
   737  	owner names.Tag
   738  }
   739  
   740  func (a *mockStorage) Kind() state.StorageKind {
   741  	return state.StorageKindFilesystem
   742  }
   743  
   744  func (a *mockStorage) StorageTag() names.StorageTag {
   745  	return a.tag
   746  }
   747  
   748  func (a *mockStorage) Owner() (names.Tag, bool) {
   749  	return a.owner, a.owner != nil
   750  }
   751  
   752  type mockFilesystem struct {
   753  	state.Filesystem
   754  	detachable bool
   755  }
   756  
   757  func (f *mockFilesystem) Detachable() bool {
   758  	return f.detachable
   759  }
   760  
   761  type blobs struct {
   762  	sync.Mutex
   763  	m map[string]bool // maps path to added (true), or deleted (false)
   764  }
   765  
   766  // Add adds a path to the list of known paths.
   767  func (b *blobs) Add(path string) {
   768  	b.Lock()
   769  	defer b.Unlock()
   770  	b.check()
   771  	b.m[path] = true
   772  }
   773  
   774  // Remove marks a path as deleted, even if it was not previously Added.
   775  func (b *blobs) Remove(path string) {
   776  	b.Lock()
   777  	defer b.Unlock()
   778  	b.check()
   779  	b.m[path] = false
   780  }
   781  
   782  func (b *blobs) check() {
   783  	if b.m == nil {
   784  		b.m = make(map[string]bool)
   785  	}
   786  }
   787  
   788  type recordingStorage struct {
   789  	statestorage.Storage
   790  	putBarrier *sync.WaitGroup
   791  	blobs      *blobs
   792  }
   793  
   794  func (s *recordingStorage) Put(path string, r io.Reader, size int64) error {
   795  	if s.putBarrier != nil {
   796  		// This goroutine has gotten to Put() so mark it Done() and
   797  		// wait for the other goroutines to get to this point.
   798  		s.putBarrier.Done()
   799  		s.putBarrier.Wait()
   800  	}
   801  	if err := s.Storage.Put(path, r, size); err != nil {
   802  		return errors.Trace(err)
   803  	}
   804  	s.blobs.Add(path)
   805  	return nil
   806  }
   807  
   808  func (s *recordingStorage) Remove(path string) error {
   809  	if err := s.Storage.Remove(path); err != nil {
   810  		return errors.Trace(err)
   811  	}
   812  	s.blobs.Remove(path)
   813  	return nil
   814  }
   815  
   816  type mockStoragePoolManager struct {
   817  	jtesting.Stub
   818  	poolmanager.PoolManager
   819  	storageType storage.ProviderType
   820  }
   821  
   822  func (m *mockStoragePoolManager) Get(name string) (*storage.Config, error) {
   823  	m.MethodCall(m, "Get", name)
   824  	if err := m.NextErr(); err != nil {
   825  		return nil, err
   826  	}
   827  	storageType := m.storageType
   828  	if name == "db" {
   829  		storageType = provider.RootfsProviderType
   830  	}
   831  	return storage.NewConfig(name, storageType, map[string]interface{}{"foo": "bar"})
   832  }
   833  
   834  type mockCaasBroker struct {
   835  	jtesting.Stub
   836  	caas.Broker
   837  	storageClassName string
   838  }
   839  
   840  func (m *mockCaasBroker) GetStorageClassName(labels ...string) (string, error) {
   841  	m.MethodCall(m, "GetStorageClassName", labels)
   842  	if err := m.NextErr(); err != nil {
   843  		return "", err
   844  	}
   845  	return m.storageClassName, m.NextErr()
   846  }