github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/state/storage_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 jc "github.com/juju/testing/checkers" 8 "github.com/juju/utils/featureflag" 9 gc "gopkg.in/check.v1" 10 11 "github.com/juju/juju/juju/osenv" 12 "github.com/juju/juju/state" 13 ) 14 15 type StorageStateSuite struct { 16 ConnSuite 17 } 18 19 var _ = gc.Suite(&StorageStateSuite{}) 20 21 func (s *StorageStateSuite) SetUpTest(c *gc.C) { 22 s.ConnSuite.SetUpTest(c) 23 24 // This suite is all about storage, so enable the feature by default. 25 s.PatchEnvironment(osenv.JujuFeatureFlagEnvKey, "storage") 26 featureflag.SetFlagsFromEnvironment(osenv.JujuFeatureFlagEnvKey) 27 } 28 29 func makeStorageCons(pool string, size, count uint64) state.StorageConstraints { 30 return state.StorageConstraints{Pool: pool, Size: size, Count: count} 31 } 32 33 func (s *StorageStateSuite) TestAddServiceStorageConstraintsWithoutFeature(c *gc.C) { 34 // Disable the storage feature, and ensure we can deploy a service from 35 // a charm that defines storage, without specifying the storage constraints. 36 s.PatchEnvironment(osenv.JujuFeatureFlagEnvKey, "") 37 featureflag.SetFlagsFromEnvironment(osenv.JujuFeatureFlagEnvKey) 38 39 ch := s.AddTestingCharm(c, "storage-block2") 40 service, err := s.State.AddService("storage-block2", "user-test-admin@local", ch, nil, nil) 41 c.Assert(err, jc.ErrorIsNil) 42 storageConstraints, err := service.StorageConstraints() 43 c.Assert(err, jc.ErrorIsNil) 44 c.Assert(storageConstraints, gc.HasLen, 0) 45 } 46 47 func (s *StorageStateSuite) TestAddServiceStorageConstraints(c *gc.C) { 48 ch := s.AddTestingCharm(c, "storage-block2") 49 addService := func(storage map[string]state.StorageConstraints) (*state.Service, error) { 50 return s.State.AddService("storage-block2", "user-test-admin@local", ch, nil, storage) 51 } 52 assertErr := func(storage map[string]state.StorageConstraints, expect string) { 53 _, err := addService(storage) 54 c.Assert(err, gc.ErrorMatches, expect) 55 } 56 assertErr(nil, `.*no constraints specified for store.*`) 57 58 storage := map[string]state.StorageConstraints{ 59 "multi1to10": makeStorageCons("", 1024, 1), 60 "multi2up": makeStorageCons("", 1024, 1), 61 } 62 assertErr(storage, `cannot add service "storage-block2": charm "storage-block2" store "multi2up": 2 instances required, 1 specified`) 63 storage["multi2up"] = makeStorageCons("", 1024, 2) 64 storage["multi1to10"] = makeStorageCons("", 1024, 11) 65 assertErr(storage, `cannot add service "storage-block2": charm "storage-block2" store "multi1to10": at most 10 instances supported, 11 specified`) 66 storage["multi1to10"] = makeStorageCons("ebs", 1024, 10) 67 assertErr(storage, `cannot add service "storage-block2": storage pools are not implemented`) 68 storage["multi1to10"] = makeStorageCons("", 1024, 10) 69 _, err := addService(storage) 70 c.Assert(err, jc.ErrorIsNil) 71 } 72 73 func (s *StorageStateSuite) TestAddUnit(c *gc.C) { 74 // Each unit added to the service will create storage instances 75 // to satisfy the service's storage constraints. 76 ch := s.AddTestingCharm(c, "storage-block2") 77 storage := map[string]state.StorageConstraints{ 78 "multi1to10": makeStorageCons("", 1024, 1), 79 "multi2up": makeStorageCons("", 1024, 2), 80 } 81 service := s.AddTestingServiceWithStorage(c, "storage-block2", ch, storage) 82 for i := 0; i < 2; i++ { 83 u, err := service.AddUnit() 84 c.Assert(err, jc.ErrorIsNil) 85 storageInstances, err := u.StorageInstances() 86 c.Assert(err, jc.ErrorIsNil) 87 count := make(map[string]int) 88 for _, si := range storageInstances { 89 count[si.StorageName()]++ 90 } 91 c.Assert(count, gc.DeepEquals, map[string]int{ 92 "multi1to10": 1, 93 "multi2up": 2, 94 }) 95 c.Assert(storageInstances[0].Kind(), gc.Equals, state.StorageKindBlock) 96 } 97 } 98 99 func (s *StorageStateSuite) TestRemoveUnit(c *gc.C) { 100 ch := s.AddTestingCharm(c, "storage-block") 101 storage := map[string]state.StorageConstraints{ 102 "data": makeStorageCons("", 1024, 1), 103 } 104 service := s.AddTestingServiceWithStorage(c, "storage-block", ch, storage) 105 unit, err := service.AddUnit() 106 c.Assert(err, jc.ErrorIsNil) 107 err = s.State.AssignUnit(unit, state.AssignCleanEmpty) 108 c.Assert(err, jc.ErrorIsNil) 109 110 storageInstances, err := unit.StorageInstances() 111 c.Assert(err, jc.ErrorIsNil) 112 c.Assert(storageInstances, gc.HasLen, 1) 113 c.Assert(unit.StorageInstanceIds(), gc.HasLen, 1) 114 115 blockDeviceNames := storageInstances[0].BlockDeviceNames() 116 c.Assert(blockDeviceNames, gc.HasLen, 1) 117 blockDevice, err := s.State.BlockDevice(blockDeviceNames[0]) 118 c.Assert(err, jc.ErrorIsNil) 119 blockDeviceStorageInstance, ok := blockDevice.StorageInstance() 120 c.Assert(ok, jc.IsTrue) 121 c.Assert(blockDeviceStorageInstance, gc.Equals, unit.StorageInstanceIds()[0]) 122 123 err = unit.EnsureDead() 124 c.Assert(err, gc.Equals, state.ErrUnitHasStorageInstances) 125 126 // TODO(axw) implement storage instance lifecycle. We currently 127 // just block the destruction of units while there are storage 128 // instances are present. The unit agent should be destroying 129 // storage instances when the storage instances are marked as 130 // dying. 131 132 // For now, we can force-destroy machines which will remove 133 // storage instances from units. 134 err = storageInstances[0].Remove() 135 c.Assert(err, jc.ErrorIsNil) 136 err = unit.Refresh() 137 c.Assert(err, gc.IsNil) 138 c.Assert(unit.StorageInstanceIds(), gc.HasLen, 0) 139 err = unit.EnsureDead() 140 c.Assert(err, jc.ErrorIsNil) 141 142 // There should not be any references from the block device back to the 143 // storage instance anymore. 144 blockDevice, err = s.State.BlockDevice(blockDeviceNames[0]) 145 c.Assert(err, jc.ErrorIsNil) 146 _, ok = blockDevice.StorageInstance() 147 c.Assert(ok, jc.IsFalse) 148 }