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  }