github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/storage_dynamicadd_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state_test
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"github.com/juju/names/v5"
     9  	jc "github.com/juju/testing/checkers"
    10  	gc "gopkg.in/check.v1"
    11  
    12  	"github.com/juju/juju/state"
    13  )
    14  
    15  type storageAddSuite struct {
    16  	StorageStateSuiteBase
    17  
    18  	unitTag    names.UnitTag
    19  	machineTag names.MachineTag
    20  
    21  	originalStorageCount    int
    22  	originalVolumeCount     int
    23  	originalFilesystemCount int
    24  }
    25  
    26  var _ = gc.Suite(&storageAddSuite{})
    27  
    28  func (s *storageAddSuite) setupMultipleStoragesForAdd(c *gc.C) *state.Unit {
    29  	storageCons := map[string]state.StorageConstraints{
    30  		"multi1to10": makeStorageCons("persistent-block", 0, 3),
    31  	}
    32  	charm := s.AddTestingCharm(c, "storage-block2")
    33  	application, err := s.State.AddApplication(state.AddApplicationArgs{
    34  		Name: "storage-block2", Charm: charm, Storage: storageCons,
    35  		CharmOrigin: &state.CharmOrigin{Platform: &state.Platform{
    36  			OS:      "ubuntu",
    37  			Channel: "22.04/stable",
    38  		}},
    39  	})
    40  	c.Assert(err, jc.ErrorIsNil)
    41  	u, err := application.AddUnit(state.AddUnitParams{})
    42  	c.Assert(err, jc.ErrorIsNil)
    43  	s.unitTag = u.UnitTag()
    44  	all, err := s.storageBackend.AllStorageInstances()
    45  	c.Assert(err, jc.ErrorIsNil)
    46  	s.originalStorageCount = len(all)
    47  	return u
    48  }
    49  
    50  func (s *storageAddSuite) assignUnit(c *gc.C, u *state.Unit) {
    51  	// Assign unit to machine to get volumes and filesystems
    52  	err := s.State.AssignUnit(u, state.AssignCleanEmpty)
    53  	c.Assert(err, jc.ErrorIsNil)
    54  	machineId, err := u.AssignedMachineId()
    55  	c.Assert(err, jc.ErrorIsNil)
    56  
    57  	m, err := s.State.Machine(machineId)
    58  	c.Assert(err, jc.ErrorIsNil)
    59  	s.machineTag = m.MachineTag()
    60  
    61  	volumes, err := s.storageBackend.AllVolumes()
    62  	c.Assert(err, jc.ErrorIsNil)
    63  	s.originalVolumeCount = len(volumes)
    64  
    65  	filesystems, err := s.storageBackend.MachineFilesystemAttachments(s.machineTag)
    66  	c.Assert(err, jc.ErrorIsNil)
    67  	s.originalFilesystemCount = len(filesystems)
    68  }
    69  
    70  func (s *storageAddSuite) assertStorageCount(c *gc.C, count int) {
    71  	all, err := s.storageBackend.AllStorageInstances()
    72  	c.Assert(err, jc.ErrorIsNil)
    73  	c.Assert(all, gc.HasLen, count)
    74  }
    75  
    76  func (s *storageAddSuite) assertVolumeCount(c *gc.C, count int) {
    77  	all, err := s.storageBackend.AllVolumes()
    78  	c.Assert(err, jc.ErrorIsNil)
    79  	c.Assert(all, gc.HasLen, count)
    80  }
    81  
    82  func (s *storageAddSuite) assertFileSystemCount(c *gc.C, count int) {
    83  	all, err := s.storageBackend.MachineFilesystemAttachments(s.machineTag)
    84  	c.Assert(err, jc.ErrorIsNil)
    85  	c.Assert(all, gc.HasLen, count)
    86  }
    87  
    88  func (s *storageAddSuite) TestAddStorageToUnit(c *gc.C) {
    89  	u := s.setupMultipleStoragesForAdd(c)
    90  	s.assignUnit(c, u)
    91  
    92  	tags, err := s.storageBackend.AddStorageForUnit(s.unitTag, "multi1to10", makeStorageCons("loop-pool", 4096, 1))
    93  	c.Assert(err, jc.ErrorIsNil)
    94  	c.Assert(tags, jc.DeepEquals, []names.StorageTag{
    95  		names.NewStorageTag("multi1to10/5"),
    96  	})
    97  
    98  	s.assertStorageCount(c, s.originalStorageCount+1)
    99  	s.assertVolumeCount(c, s.originalVolumeCount+1)
   100  	s.assertFileSystemCount(c, s.originalFilesystemCount)
   101  	assertMachineStorageRefs(c, s.storageBackend, s.machineTag)
   102  
   103  	allVolumeParams := allMachineVolumeParams(c, s.storageBackend, s.machineTag)
   104  	c.Assert(allVolumeParams, jc.SameContents, []state.VolumeParams{
   105  		{Pool: "persistent-block", Size: 1024}, // multi1to10
   106  		{Pool: "persistent-block", Size: 1024}, // multi1to10
   107  		{Pool: "persistent-block", Size: 1024}, // multi1to10
   108  		{Pool: "loop", Size: 2048},             // multi2up
   109  		{Pool: "loop", Size: 2048},             // multi2up
   110  		{Pool: "loop-pool", Size: 4096},        // added above
   111  	})
   112  }
   113  
   114  func (s *storageAddSuite) TestAddStorageToUnitInheritPoolAndSize(c *gc.C) {
   115  	u := s.setupMultipleStoragesForAdd(c)
   116  	s.assignUnit(c, u)
   117  
   118  	_, err := s.storageBackend.AddStorageForUnit(s.unitTag, "multi1to10", state.StorageConstraints{Count: 1})
   119  	c.Assert(err, jc.ErrorIsNil)
   120  
   121  	allVolumeParams := allMachineVolumeParams(c, s.storageBackend, s.machineTag)
   122  	c.Assert(allVolumeParams, jc.SameContents, []state.VolumeParams{
   123  		{Pool: "persistent-block", Size: 1024},
   124  		{Pool: "persistent-block", Size: 1024},
   125  		{Pool: "persistent-block", Size: 1024},
   126  		{Pool: "persistent-block", Size: 1024},
   127  		{Pool: "loop", Size: 2048},
   128  		{Pool: "loop", Size: 2048},
   129  	})
   130  }
   131  
   132  func (s *storageAddSuite) TestAddStorageToUnitNotAssigned(c *gc.C) {
   133  	u := s.setupMultipleStoragesForAdd(c)
   134  	// don't assign unit
   135  
   136  	_, err := s.storageBackend.AddStorageForUnit(s.unitTag, "multi1to10", makeStorageCons("loop-pool", 4096, 1))
   137  	c.Assert(err, jc.ErrorIsNil)
   138  	s.assertStorageCount(c, s.originalStorageCount+1)
   139  	s.assertVolumeCount(c, 0)
   140  	s.assertFileSystemCount(c, 0)
   141  
   142  	s.assignUnit(c, u)
   143  	s.assertVolumeCount(c, 6)
   144  	s.assertFileSystemCount(c, 0)
   145  
   146  	allVolumeParams := allMachineVolumeParams(c, s.storageBackend, s.machineTag)
   147  	c.Assert(allVolumeParams, jc.SameContents, []state.VolumeParams{
   148  		{Pool: "persistent-block", Size: 1024},
   149  		{Pool: "persistent-block", Size: 1024},
   150  		{Pool: "persistent-block", Size: 1024},
   151  		{Pool: "loop", Size: 2048},
   152  		{Pool: "loop", Size: 2048},
   153  		{Pool: "loop-pool", Size: 4096},
   154  	})
   155  }
   156  
   157  func allMachineVolumeParams(c *gc.C, sb *state.StorageBackend, m names.MachineTag) []state.VolumeParams {
   158  	var allVolumeParams []state.VolumeParams
   159  	volumeAttachments, err := sb.MachineVolumeAttachments(m)
   160  	c.Assert(err, jc.ErrorIsNil)
   161  	for _, a := range volumeAttachments {
   162  		volume, err := sb.Volume(a.Volume())
   163  		c.Assert(err, jc.ErrorIsNil)
   164  		volumeParams, ok := volume.Params()
   165  		c.Assert(ok, jc.IsTrue)
   166  		allVolumeParams = append(allVolumeParams, volumeParams)
   167  	}
   168  	return allVolumeParams
   169  }
   170  
   171  func (s *storageAddSuite) TestAddStorageWithCount(c *gc.C) {
   172  	u := s.setupMultipleStoragesForAdd(c)
   173  	s.assignUnit(c, u)
   174  	tags, err := s.storageBackend.AddStorageForUnit(s.unitTag, "multi1to10", makeStorageCons("loop-pool", 1024, 2))
   175  	c.Assert(err, jc.ErrorIsNil)
   176  	c.Assert(tags, jc.DeepEquals, []names.StorageTag{
   177  		names.NewStorageTag("multi1to10/5"),
   178  		names.NewStorageTag("multi1to10/6"),
   179  	})
   180  	s.assertStorageCount(c, s.originalStorageCount+2)
   181  	s.assertVolumeCount(c, s.originalVolumeCount+2)
   182  	s.assertFileSystemCount(c, s.originalFilesystemCount)
   183  	assertMachineStorageRefs(c, s.storageBackend, s.machineTag)
   184  }
   185  
   186  func (s *storageAddSuite) TestAddStorageMultipleCalls(c *gc.C) {
   187  	u := s.setupMultipleStoragesForAdd(c)
   188  	s.assignUnit(c, u)
   189  
   190  	_, err := s.storageBackend.AddStorageForUnit(s.unitTag, "multi1to10", makeStorageCons("loop-pool", 1024, 2))
   191  	c.Assert(err, jc.ErrorIsNil)
   192  	s.assertStorageCount(c, s.originalStorageCount+2)
   193  
   194  	// Should not succeed as the number of storages after
   195  	// this call would be 11 whereas our upper limit is 10 here.
   196  	_, err = s.storageBackend.AddStorageForUnit(s.unitTag, "multi1to10", makeStorageCons("loop-pool", 1024, 6))
   197  	c.Assert(err, gc.ErrorMatches,
   198  		`adding "multi1to10" storage to storage-block2/0: `+
   199  			`attaching 6 storage instances brings the total to 11, exceeding the maximum of 10`)
   200  	s.assertStorageCount(c, s.originalStorageCount+2)
   201  	s.assertVolumeCount(c, s.originalVolumeCount+2)
   202  	s.assertFileSystemCount(c, s.originalFilesystemCount)
   203  	assertMachineStorageRefs(c, s.storageBackend, s.machineTag)
   204  }
   205  
   206  func (s *storageAddSuite) TestAddStorageToDyingUnitFails(c *gc.C) {
   207  	s.setupMultipleStoragesForAdd(c)
   208  
   209  	defer state.SetBeforeHooks(c, s.State, func() {
   210  		u, err := s.State.Unit(s.unitTag.Id())
   211  		c.Assert(err, jc.ErrorIsNil)
   212  		err = u.Destroy()
   213  		c.Assert(err, jc.ErrorIsNil)
   214  	}).Check()
   215  
   216  	_, err := s.storageBackend.AddStorageForUnit(s.unitTag, "multi1to10", makeStorageCons("loop-pool", 1024, 1))
   217  	c.Assert(err, gc.ErrorMatches, `adding "multi1to10" storage to storage-block2/0: unit is not found or not alive`)
   218  
   219  	s.assertStorageCount(c, s.originalStorageCount)
   220  }
   221  
   222  func (s *storageAddSuite) TestAddStorageExceedCount(c *gc.C) {
   223  	_, u, _ := s.setupSingleStorage(c, "block", "loop-pool")
   224  	s.assertStorageCount(c, 1)
   225  
   226  	_, err := s.storageBackend.AddStorageForUnit(u.UnitTag(), "data", makeStorageCons("loop-pool", 1024, 1))
   227  	c.Assert(err, gc.ErrorMatches, `adding "data" storage to storage-block/0: cannot attach, storage is singular`)
   228  	s.assertStorageCount(c, 1)
   229  	s.assertVolumeCount(c, 0)
   230  	s.assertFileSystemCount(c, 0)
   231  }
   232  
   233  func (s *storageAddSuite) createAndAssignUnitWithSingleStorage(c *gc.C) names.UnitTag {
   234  	_, u, _ := s.setupSingleStorage(c, "block", "loop-pool")
   235  	s.assertStorageCount(c, 1)
   236  
   237  	// Assign unit to machine to get volumes and filesystems
   238  	err := s.State.AssignUnit(u, state.AssignCleanEmpty)
   239  	c.Assert(err, jc.ErrorIsNil)
   240  
   241  	volumes, err := s.storageBackend.AllVolumes()
   242  	c.Assert(err, jc.ErrorIsNil)
   243  	s.originalVolumeCount = len(volumes)
   244  
   245  	filesystems, err := s.storageBackend.MachineFilesystemAttachments(s.machineTag)
   246  	c.Assert(err, jc.ErrorIsNil)
   247  	s.originalFilesystemCount = len(filesystems)
   248  
   249  	return u.UnitTag()
   250  }
   251  
   252  func (s *storageAddSuite) TestAddStorageMinCount(c *gc.C) {
   253  	unit := s.createAndAssignUnitWithSingleStorage(c)
   254  	_, err := s.storageBackend.AddStorageForUnit(unit, "allecto", makeStorageCons("loop-pool", 1024, 1))
   255  	c.Assert(err, jc.ErrorIsNil)
   256  	s.assertStorageCount(c, 2)
   257  	s.assertVolumeCount(c, 2)
   258  	s.assertFileSystemCount(c, 0)
   259  	assertMachineStorageRefs(c, s.storageBackend, s.machineTag)
   260  }
   261  
   262  func (s *storageAddSuite) TestAddStorageZeroCount(c *gc.C) {
   263  	unit := s.createAndAssignUnitWithSingleStorage(c)
   264  	_, err := s.storageBackend.AddStorageForUnit(unit, "allecto", state.StorageConstraints{Pool: "loop-pool", Size: 1024})
   265  	c.Assert(errors.Cause(err), gc.ErrorMatches, "adding storage where instance count is 0 not valid")
   266  	s.assertStorageCount(c, 1)
   267  	s.assertVolumeCount(c, 1)
   268  	s.assertFileSystemCount(c, 0)
   269  	assertMachineStorageRefs(c, s.storageBackend, s.machineTag)
   270  }
   271  
   272  func (s *storageAddSuite) TestAddStorageTriggerDefaultPopulated(c *gc.C) {
   273  	u := s.setupMultipleStoragesForAdd(c)
   274  	s.assignUnit(c, u)
   275  
   276  	_, err := s.storageBackend.AddStorageForUnit(s.unitTag, "multi1to10", state.StorageConstraints{Count: 1})
   277  	c.Assert(err, jc.ErrorIsNil)
   278  	s.assertStorageCount(c, s.originalStorageCount+1)
   279  	s.assertVolumeCount(c, s.originalVolumeCount+1)
   280  	s.assertFileSystemCount(c, s.originalFilesystemCount)
   281  	assertMachineStorageRefs(c, s.storageBackend, s.machineTag)
   282  }
   283  
   284  func (s *storageAddSuite) TestAddStorageDiffPool(c *gc.C) {
   285  	u := s.setupMultipleStoragesForAdd(c)
   286  	s.assignUnit(c, u)
   287  
   288  	_, err := s.storageBackend.AddStorageForUnit(s.unitTag, "multi1to10", state.StorageConstraints{Pool: "loop-pool", Count: 1})
   289  	c.Assert(err, jc.ErrorIsNil)
   290  	s.assertStorageCount(c, s.originalStorageCount+1)
   291  	s.assertVolumeCount(c, s.originalVolumeCount+1)
   292  	s.assertFileSystemCount(c, s.originalFilesystemCount)
   293  	assertMachineStorageRefs(c, s.storageBackend, s.machineTag)
   294  }
   295  
   296  func (s *storageAddSuite) TestAddStorageDiffSize(c *gc.C) {
   297  	u := s.setupMultipleStoragesForAdd(c)
   298  	s.assignUnit(c, u)
   299  
   300  	_, err := s.storageBackend.AddStorageForUnit(s.unitTag, "multi1to10", state.StorageConstraints{Size: 2048, Count: 1})
   301  	c.Assert(err, jc.ErrorIsNil)
   302  	s.assertStorageCount(c, s.originalStorageCount+1)
   303  	s.assertVolumeCount(c, s.originalVolumeCount+1)
   304  	s.assertFileSystemCount(c, s.originalFilesystemCount)
   305  	assertMachineStorageRefs(c, s.storageBackend, s.machineTag)
   306  }
   307  
   308  func (s *storageAddSuite) TestAddStorageLessMinSize(c *gc.C) {
   309  	u := s.setupMultipleStoragesForAdd(c)
   310  	s.assignUnit(c, u)
   311  
   312  	_, err := s.storageBackend.AddStorageForUnit(s.unitTag, "multi2up", state.StorageConstraints{Size: 2, Count: 1})
   313  	c.Assert(err, gc.ErrorMatches, `.*charm "storage-block2" store "multi2up": minimum storage size is 2.0 GB, 2.0 MB specified.*`)
   314  	s.assertStorageCount(c, s.originalStorageCount)
   315  	s.assertVolumeCount(c, s.originalVolumeCount)
   316  	s.assertFileSystemCount(c, s.originalFilesystemCount)
   317  	assertMachineStorageRefs(c, s.storageBackend, s.machineTag)
   318  }
   319  
   320  func (s *storageAddSuite) TestAddStorageWrongName(c *gc.C) {
   321  	u := s.setupMultipleStoragesForAdd(c)
   322  	s.assignUnit(c, u)
   323  
   324  	_, err := s.storageBackend.AddStorageForUnit(s.unitTag, "furball", state.StorageConstraints{Size: 2})
   325  	c.Assert(err, gc.ErrorMatches, `.*charm storage "furball" not found.*`)
   326  	s.assertStorageCount(c, s.originalStorageCount)
   327  	s.assertVolumeCount(c, s.originalVolumeCount)
   328  	s.assertFileSystemCount(c, s.originalFilesystemCount)
   329  }
   330  
   331  func (s *storageAddSuite) TestAddStorageConcurrently(c *gc.C) {
   332  	u := s.setupMultipleStoragesForAdd(c)
   333  	s.assignUnit(c, u)
   334  
   335  	addStorage := func() {
   336  		_, err := s.storageBackend.AddStorageForUnit(s.unitTag, "multi1to10", state.StorageConstraints{Count: 1})
   337  		c.Assert(err, jc.ErrorIsNil)
   338  	}
   339  	defer state.SetBeforeHooks(c, s.State, addStorage).Check()
   340  	addStorage()
   341  	s.assertStorageCount(c, s.originalStorageCount+2)
   342  	s.assertVolumeCount(c, s.originalVolumeCount+2)
   343  	s.assertFileSystemCount(c, s.originalFilesystemCount)
   344  	assertMachineStorageRefs(c, s.storageBackend, s.machineTag)
   345  }
   346  
   347  func (s *storageAddSuite) TestAddStorageConcurrentlyExceedCount(c *gc.C) {
   348  	u := s.setupMultipleStoragesForAdd(c)
   349  	s.assignUnit(c, u)
   350  
   351  	count := 6
   352  	addStorage := func() {
   353  		_, err := s.storageBackend.AddStorageForUnit(s.unitTag, "multi1to10", state.StorageConstraints{Count: uint64(count)})
   354  		c.Assert(err, jc.ErrorIsNil)
   355  	}
   356  	defer state.SetBeforeHooks(c, s.State, addStorage).Check()
   357  	_, err := s.storageBackend.AddStorageForUnit(s.unitTag, "multi1to10", state.StorageConstraints{Count: uint64(count)})
   358  	c.Assert(err, gc.ErrorMatches,
   359  		`adding "multi1to10" storage to storage-block2/0: `+
   360  			`attaching 6 storage instances brings the total to 15, exceeding the maximum of 10`)
   361  
   362  	// Only "count" number of instances should have been added.
   363  	s.assertStorageCount(c, s.originalStorageCount+count)
   364  	s.assertVolumeCount(c, s.originalVolumeCount+count)
   365  	s.assertFileSystemCount(c, s.originalFilesystemCount)
   366  	assertMachineStorageRefs(c, s.storageBackend, s.machineTag)
   367  }
   368  
   369  func (s *storageAddSuite) TestAddStorageFilesystem(c *gc.C) {
   370  	_, u, _ := s.setupSingleStorage(c, "filesystem", "loop-pool")
   371  
   372  	// Assign unit to machine to get volumes and filesystems
   373  	err := s.State.AssignUnit(u, state.AssignCleanEmpty)
   374  	c.Assert(err, jc.ErrorIsNil)
   375  	machineId, err := u.AssignedMachineId()
   376  	c.Assert(err, jc.ErrorIsNil)
   377  	s.machineTag = names.NewMachineTag(machineId)
   378  	s.assertFileSystemCount(c, 1)
   379  
   380  	s.assertStorageCount(c, 1)
   381  	s.assertVolumeCount(c, 1)
   382  	s.assertFileSystemCount(c, 1)
   383  
   384  	_, err = s.storageBackend.AddStorageForUnit(u.UnitTag(), "data", makeStorageCons("loop-pool", 1024, 1))
   385  	c.Assert(err, jc.ErrorIsNil)
   386  	s.assertStorageCount(c, 2)
   387  	s.assertVolumeCount(c, 2)
   388  	s.assertFileSystemCount(c, 2)
   389  	assertMachineStorageRefs(c, s.storageBackend, s.machineTag)
   390  }
   391  
   392  func (s *storageAddSuite) TestAddStorageStatic(c *gc.C) {
   393  	// Create a unit with static storage; ensure that storage-add
   394  	// fails to add more of this kind of storage.
   395  	_, u, _ := s.setupSingleStorage(c, "filesystem", "static")
   396  	s.assertStorageCount(c, 1)
   397  
   398  	// Assign unit to machine to get volumes and filesystems
   399  	err := s.State.AssignUnit(u, state.AssignCleanEmpty)
   400  	c.Assert(err, jc.ErrorIsNil)
   401  	machineId, err := u.AssignedMachineId()
   402  	c.Assert(err, jc.ErrorIsNil)
   403  	s.machineTag = names.NewMachineTag(machineId)
   404  	s.assertFileSystemCount(c, 1)
   405  
   406  	_, err = s.storageBackend.AddStorageForUnit(
   407  		u.UnitTag(), "data",
   408  		makeStorageCons("static", 1024, 1),
   409  	)
   410  	c.Assert(err, gc.ErrorMatches, `adding "data" storage to storage-filesystem/0: `+
   411  		"creating machine storage for storage data/1: "+
   412  		`"static" storage provider does not support dynamic storage`)
   413  	s.assertStorageCount(c, 1)    // no change
   414  	s.assertFileSystemCount(c, 1) // no change
   415  	assertMachineStorageRefs(c, s.storageBackend, s.machineTag)
   416  }