github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/storage/constraints_test.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package storage_test 5 6 import ( 7 jc "github.com/juju/testing/checkers" 8 gc "gopkg.in/check.v1" 9 10 "github.com/juju/juju/storage" 11 "github.com/juju/juju/testing" 12 ) 13 14 type ConstraintsSuite struct { 15 testing.BaseSuite 16 } 17 18 var _ = gc.Suite(&ConstraintsSuite{}) 19 20 func (s *ConstraintsSuite) TestParseConstraintsStoragePool(c *gc.C) { 21 s.testParse(c, "pool,1M", storage.Constraints{ 22 Pool: "pool", 23 Count: 1, 24 Size: 1, 25 }) 26 s.testParse(c, "pool,", storage.Constraints{ 27 Pool: "pool", 28 Count: 1, 29 }) 30 s.testParse(c, "1M", storage.Constraints{ 31 Size: 1, 32 Count: 1, 33 }) 34 } 35 36 func (s *ConstraintsSuite) TestParseConstraintsCountSize(c *gc.C) { 37 s.testParse(c, "p,1G", storage.Constraints{ 38 Pool: "p", 39 Count: 1, 40 Size: 1024, 41 }) 42 s.testParse(c, "p,1,0.5T", storage.Constraints{ 43 Pool: "p", 44 Count: 1, 45 Size: 1024 * 512, 46 }) 47 s.testParse(c, "p,0.125P,3", storage.Constraints{ 48 Pool: "p", 49 Count: 3, 50 Size: 1024 * 1024 * 128, 51 }) 52 } 53 54 func (s *ConstraintsSuite) TestParseConstraintsOptions(c *gc.C) { 55 s.testParse(c, "p,1M,", storage.Constraints{ 56 Pool: "p", 57 Count: 1, 58 Size: 1, 59 }) 60 } 61 62 func (s *ConstraintsSuite) TestParseConstraintsCountRange(c *gc.C) { 63 s.testParseError(c, "p,0,100M", `cannot parse count: count must be greater than zero, got "0"`) 64 s.testParseError(c, "p,00,100M", `cannot parse count: count must be greater than zero, got "00"`) 65 s.testParseError(c, "p,-1,100M", `cannot parse count: count must be greater than zero, got "-1"`) 66 s.testParseError(c, "", `storage constraints require at least one field to be specified`) 67 s.testParseError(c, ",", `storage constraints require at least one field to be specified`) 68 } 69 70 func (s *ConstraintsSuite) TestParseConstraintsSizeRange(c *gc.C) { 71 s.testParseError(c, "p,-100M", `cannot parse size: expected a non-negative number, got "-100M"`) 72 } 73 74 func (s *ConstraintsSuite) TestParseMultiplePoolNames(c *gc.C) { 75 s.testParseError(c, "pool1,anyoldjunk", `pool name is already set to "pool1", new value "anyoldjunk" not valid`) 76 s.testParseError(c, "pool1,pool2", `pool name is already set to "pool1", new value "pool2" not valid`) 77 s.testParseError(c, "pool1,pool2,pool3", `pool name is already set to "pool1", new value "pool2" not valid`) 78 } 79 80 func (s *ConstraintsSuite) TestParseConstraintsUnknown(c *gc.C) { 81 // Regression test for #1855181 82 s.testParseError(c, "p,100M database-b", `unrecognized storage constraint "100M database-b" not valid`) 83 s.testParseError(c, "p,$1234", `unrecognized storage constraint "\$1234" not valid`) 84 } 85 86 func (*ConstraintsSuite) testParse(c *gc.C, s string, expect storage.Constraints) { 87 cons, err := storage.ParseConstraints(s) 88 c.Check(err, jc.ErrorIsNil) 89 c.Check(cons, gc.DeepEquals, expect) 90 } 91 92 func (*ConstraintsSuite) testParseError(c *gc.C, s, expectErr string) { 93 _, err := storage.ParseConstraints(s) 94 c.Check(err, gc.ErrorMatches, expectErr) 95 } 96 97 func (s *ConstraintsSuite) TestValidPoolName(c *gc.C) { 98 c.Assert(storage.IsValidPoolName("pool"), jc.IsTrue) 99 c.Assert(storage.IsValidPoolName("p-ool"), jc.IsTrue) 100 c.Assert(storage.IsValidPoolName("p-00l"), jc.IsTrue) 101 c.Assert(storage.IsValidPoolName("p?00l"), jc.IsTrue) 102 c.Assert(storage.IsValidPoolName("p-?00l"), jc.IsTrue) 103 c.Assert(storage.IsValidPoolName("p"), jc.IsTrue) 104 c.Assert(storage.IsValidPoolName("P"), jc.IsTrue) 105 c.Assert(storage.IsValidPoolName("p?0?l"), jc.IsTrue) 106 } 107 108 func (s *ConstraintsSuite) TestInvalidPoolName(c *gc.C) { 109 c.Assert(storage.IsValidPoolName("7ool"), jc.IsFalse) 110 c.Assert(storage.IsValidPoolName("/ool"), jc.IsFalse) 111 c.Assert(storage.IsValidPoolName("-00l"), jc.IsFalse) 112 c.Assert(storage.IsValidPoolName("*00l"), jc.IsFalse) 113 } 114 115 func (s *ConstraintsSuite) TestParseStorageConstraints(c *gc.C) { 116 s.testParseStorageConstraints(c, 117 []string{"data=p,1M,"}, true, 118 map[string]storage.Constraints{"data": { 119 Pool: "p", 120 Count: 1, 121 Size: 1, 122 }}) 123 s.testParseStorageConstraints(c, 124 []string{"data"}, false, 125 map[string]storage.Constraints{"data": { 126 Count: 1, 127 }}) 128 s.testParseStorageConstraints(c, 129 []string{"data=3", "cache"}, false, 130 map[string]storage.Constraints{ 131 "data": { 132 Count: 3, 133 }, 134 "cache": { 135 Count: 1, 136 }, 137 }) 138 } 139 140 func (s *ConstraintsSuite) TestParseStorageConstraintsErrors(c *gc.C) { 141 s.testStorageConstraintsError(c, 142 []string{"data"}, true, 143 `.*where "constraints" must be specified.*`) 144 s.testStorageConstraintsError(c, 145 []string{"data=p,=1M,"}, false, 146 `.*expected "name=constraints" or "name", got .*`) 147 s.testStorageConstraintsError(c, 148 []string{"data", "data"}, false, 149 `storage "data" specified more than once`) 150 s.testStorageConstraintsError(c, 151 []string{"data=-1"}, false, 152 `.*cannot parse constraints for storage "data".*`) 153 s.testStorageConstraintsError(c, 154 []string{"data="}, false, 155 `.*cannot parse constraints for storage "data".*`) 156 } 157 158 func (*ConstraintsSuite) testParseStorageConstraints(c *gc.C, 159 s []string, 160 mustHave bool, 161 expect map[string]storage.Constraints, 162 ) { 163 cons, err := storage.ParseConstraintsMap(s, mustHave) 164 c.Check(err, jc.ErrorIsNil) 165 c.Assert(len(cons), gc.Equals, len(expect)) 166 for k, v := range expect { 167 c.Check(cons[k], gc.DeepEquals, v) 168 } 169 } 170 171 func (*ConstraintsSuite) testStorageConstraintsError(c *gc.C, s []string, mustHave bool, e string) { 172 _, err := storage.ParseConstraintsMap(s, mustHave) 173 c.Check(err, gc.ErrorMatches, e) 174 } 175 176 func (s *ConstraintsSuite) TestToString(c *gc.C) { 177 _, err := storage.ToString(storage.Constraints{}) 178 c.Assert(err, gc.ErrorMatches, "must provide one of pool or size or count") 179 180 for _, t := range []struct { 181 pool string 182 count uint64 183 size uint64 184 expected string 185 }{ 186 {"loop", 0, 0, "loop"}, 187 {"loop", 1, 0, "loop,1"}, 188 {"loop", 0, 1024, "loop,1024M"}, 189 {"loop", 1, 1024, "loop,1,1024M"}, 190 {"", 0, 1024, "1024M"}, 191 {"", 1, 0, "1"}, 192 {"", 1, 1024, "1,1024M"}, 193 } { 194 str, err := storage.ToString(storage.Constraints{ 195 Pool: t.pool, 196 Size: t.size, 197 Count: t.count, 198 }) 199 c.Assert(err, jc.ErrorIsNil) 200 c.Assert(str, gc.Equals, t.expected) 201 202 // Test roundtrip, count defaults to 1. 203 if t.count == 0 { 204 t.count = 1 205 } 206 s.testParse(c, str, storage.Constraints{ 207 Pool: t.pool, 208 Size: t.size, 209 Count: t.count, 210 }) 211 } 212 }