github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/state/constraintsvalidation_test.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state_test
     5  
     6  import (
     7  	"regexp"
     8  
     9  	"github.com/juju/errors"
    10  	jc "github.com/juju/testing/checkers"
    11  	gc "gopkg.in/check.v1"
    12  
    13  	"github.com/juju/juju/constraints"
    14  	"github.com/juju/juju/state"
    15  )
    16  
    17  type applicationConstraintsSuite struct {
    18  	ConnSuite
    19  
    20  	applicationName string
    21  	testCharm       *state.Charm
    22  }
    23  
    24  var _ = gc.Suite(&applicationConstraintsSuite{})
    25  
    26  type constraintsValidationSuite struct {
    27  	ConnSuite
    28  }
    29  
    30  var _ = gc.Suite(&constraintsValidationSuite{})
    31  
    32  func (s *constraintsValidationSuite) SetUpTest(c *gc.C) {
    33  	s.ConnSuite.SetUpTest(c)
    34  	s.policy.GetConstraintsValidator = func() (constraints.Validator, error) {
    35  		validator := constraints.NewValidator()
    36  		validator.RegisterConflicts(
    37  			[]string{constraints.InstanceType},
    38  			[]string{constraints.Mem, constraints.Arch},
    39  		)
    40  		validator.RegisterUnsupported([]string{constraints.CpuPower})
    41  		return validator, nil
    42  	}
    43  }
    44  
    45  func (s *constraintsValidationSuite) addOneMachine(c *gc.C, cons constraints.Value) (*state.Machine, error) {
    46  	return s.State.AddOneMachine(state.MachineTemplate{
    47  		Series:      "quantal",
    48  		Jobs:        []state.MachineJob{state.JobHostUnits},
    49  		Constraints: cons,
    50  	})
    51  }
    52  
    53  var setConstraintsTests = []struct {
    54  	about        string
    55  	consToSet    string
    56  	consFallback string
    57  
    58  	effectiveModelCons   string // model constraints after setting consFallback
    59  	effectiveServiceCons string // service constraints after setting consToSet
    60  	effectiveUnitCons    string // unit constraints after setting consToSet on the service
    61  	effectiveMachineCons string // machine constraints after setting consToSet
    62  }{{
    63  	about:        "(implictly) empty constraints are OK and stored as empty",
    64  	consToSet:    "",
    65  	consFallback: "",
    66  
    67  	effectiveModelCons:   "",
    68  	effectiveServiceCons: "",
    69  	effectiveUnitCons:    "",
    70  	effectiveMachineCons: "",
    71  }, {
    72  	about:        "(implicitly) empty fallback constraints never override set constraints",
    73  	consToSet:    "instance-type=foo-42 cpu-power=9001 spaces=bar",
    74  	consFallback: "",
    75  
    76  	effectiveModelCons: "", // model constraints are stored as empty.
    77  	// i.e. there are no fallbacks and all the following cases are the same.
    78  	effectiveServiceCons: "instance-type=foo-42 cpu-power=9001 spaces=bar",
    79  	effectiveUnitCons:    "instance-type=foo-42 cpu-power=9001 spaces=bar",
    80  	effectiveMachineCons: "instance-type=foo-42 cpu-power=9001 spaces=bar",
    81  }, {
    82  	about:        "(implicitly) empty constraints never override explictly set fallbacks",
    83  	consToSet:    "",
    84  	consFallback: "arch=amd64 cores=42 mem=2G tags=foo",
    85  
    86  	effectiveModelCons:   "arch=amd64 cores=42 mem=2G tags=foo",
    87  	effectiveServiceCons: "", // set as given.
    88  	effectiveUnitCons:    "arch=amd64 cores=42 mem=2G tags=foo",
    89  	// set as given, then merged with fallbacks; since consToSet is
    90  	// empty, the effective values inherit everything from fallbacks;
    91  	// like the unit, but only because the service constraints are
    92  	// also empty.
    93  	effectiveMachineCons: "arch=amd64 cores=42 mem=2G tags=foo",
    94  }, {
    95  	about:        "(explicitly) empty constraints are OK and stored as given",
    96  	consToSet:    "cores= cpu-power= root-disk= instance-type= container= tags= spaces=",
    97  	consFallback: "",
    98  
    99  	effectiveModelCons:   "",
   100  	effectiveServiceCons: "cores= cpu-power= root-disk= instance-type= container= tags= spaces=",
   101  	effectiveUnitCons:    "cores= cpu-power= root-disk= instance-type= container= tags= spaces=",
   102  	effectiveMachineCons: "cores= cpu-power= instance-type= root-disk= tags= spaces=", // container= is dropped
   103  }, {
   104  	about:        "(explicitly) empty fallback constraints are OK and stored as given",
   105  	consToSet:    "",
   106  	consFallback: "cores= cpu-power= root-disk= instance-type= container= tags= spaces=",
   107  
   108  	effectiveModelCons:   "cores= cpu-power= root-disk= instance-type= container= tags= spaces=",
   109  	effectiveServiceCons: "",
   110  	effectiveUnitCons:    "cores= cpu-power= root-disk= instance-type= container= tags= spaces=",
   111  	effectiveMachineCons: "cores= cpu-power= instance-type= root-disk= tags= spaces=", // container= is dropped
   112  }, {
   113  	about:                "(explicitly) empty constraints and fallbacks are OK and stored as given",
   114  	consToSet:            "arch= mem= cores= container=",
   115  	consFallback:         "cores= cpu-power= root-disk= instance-type= container= tags= spaces=",
   116  	effectiveModelCons:   "cores= cpu-power= root-disk= instance-type= container= tags= spaces=",
   117  	effectiveServiceCons: "arch= mem= cores= container=",
   118  	effectiveUnitCons:    "arch= container= cores= cpu-power= mem= root-disk= tags= spaces=",
   119  	effectiveMachineCons: "arch= cores= cpu-power= mem= root-disk= tags= spaces=", // container= is dropped
   120  }, {
   121  	about:        "(explicitly) empty constraints override set fallbacks for deployment and provisioning",
   122  	consToSet:    "cores= arch= spaces= cpu-power=",
   123  	consFallback: "cores=42 arch=amd64 tags=foo spaces=default,^dmz mem=4G",
   124  
   125  	effectiveModelCons:   "cores=42 arch=amd64 tags=foo spaces=default,^dmz mem=4G",
   126  	effectiveServiceCons: "cores= arch= spaces= cpu-power=",
   127  	effectiveUnitCons:    "arch= cores= cpu-power= mem=4G tags=foo spaces=",
   128  	effectiveMachineCons: "arch= cores= cpu-power= mem=4G tags=foo spaces=",
   129  	// we're also checking if m.SetConstraints() does the same with
   130  	// regards to the effective constraints as AddMachine(), because
   131  	// some of these tests proved they had different behavior (i.e.
   132  	// container= was not set to empty)
   133  }, {
   134  	about:        "non-empty constraints always override empty or unset fallbacks",
   135  	consToSet:    "cores=42 root-disk=20G arch=amd64 tags=foo,bar",
   136  	consFallback: "cores= arch= tags=",
   137  
   138  	effectiveModelCons:   "cores= arch= tags=",
   139  	effectiveServiceCons: "cores=42 root-disk=20G arch=amd64 tags=foo,bar",
   140  	effectiveUnitCons:    "cores=42 root-disk=20G arch=amd64 tags=foo,bar",
   141  	effectiveMachineCons: "cores=42 root-disk=20G arch=amd64 tags=foo,bar",
   142  }, {
   143  	about:        "non-empty constraints always override set fallbacks",
   144  	consToSet:    "cores=42 root-disk=20G arch=amd64 tags=foo,bar",
   145  	consFallback: "cores=12 root-disk=10G arch=i386  tags=bar",
   146  
   147  	effectiveModelCons:   "cores=12 root-disk=10G arch=i386  tags=bar",
   148  	effectiveServiceCons: "cores=42 root-disk=20G arch=amd64 tags=foo,bar",
   149  	effectiveUnitCons:    "cores=42 root-disk=20G arch=amd64 tags=foo,bar",
   150  	effectiveMachineCons: "cores=42 root-disk=20G arch=amd64 tags=foo,bar",
   151  }, {
   152  	about:        "non-empty constraints override conflicting set fallbacks",
   153  	consToSet:    "mem=8G arch=amd64 cores=4 tags=bar",
   154  	consFallback: "instance-type=small cpu-power=1000", // instance-type conflicts mem, arch
   155  
   156  	effectiveModelCons:   "instance-type=small cpu-power=1000",
   157  	effectiveServiceCons: "mem=8G arch=amd64 cores=4 tags=bar",
   158  	// both of the following contain the explicitly set constraints after
   159  	// resolving any conflicts with fallbacks (by dropping them).
   160  	effectiveUnitCons:    "mem=8G arch=amd64 cores=4 tags=bar cpu-power=1000",
   161  	effectiveMachineCons: "mem=8G arch=amd64 cores=4 tags=bar cpu-power=1000",
   162  }, {
   163  	about:        "set fallbacks are overriden the same way for provisioning and deployment",
   164  	consToSet:    "tags= cpu-power= spaces=bar",
   165  	consFallback: "tags=foo cpu-power=42",
   166  
   167  	// a variation of the above case showing there's no difference
   168  	// between deployment (service, unit) and provisioning (machine)
   169  	// constraints when it comes to effective values.
   170  	effectiveModelCons:   "tags=foo cpu-power=42",
   171  	effectiveServiceCons: "cpu-power= tags= spaces=bar",
   172  	effectiveUnitCons:    "cpu-power= tags= spaces=bar",
   173  	effectiveMachineCons: "cpu-power= tags= spaces=bar",
   174  }, {
   175  	about:        "container type can only be used for deployment, not provisioning",
   176  	consToSet:    "container=kvm arch=amd64",
   177  	consFallback: "container=lxd mem=8G",
   178  
   179  	// service deployment constraints are transformed into machine
   180  	// provisioning constraints, and the container type only makes
   181  	// sense currently as a deployment constraint, so it's cleared
   182  	// when merging service/model deployment constraints into
   183  	// effective machine provisioning constraints.
   184  	effectiveModelCons:   "container=lxd mem=8G",
   185  	effectiveServiceCons: "container=kvm arch=amd64",
   186  	effectiveUnitCons:    "container=kvm mem=8G arch=amd64",
   187  	effectiveMachineCons: "mem=8G arch=amd64",
   188  }, {
   189  	about:        "specify image virt-type when deploying applications on multi-hypervisor aware openstack",
   190  	consToSet:    "virt-type=kvm",
   191  	consFallback: "",
   192  
   193  	// application deployment constraints are transformed into machine
   194  	// provisioning constraints. Unit constraints must also have virt-type set
   195  	// to ensure consistency in scalability.
   196  	effectiveModelCons:   "",
   197  	effectiveServiceCons: "virt-type=kvm",
   198  	effectiveUnitCons:    "virt-type=kvm",
   199  	effectiveMachineCons: "virt-type=kvm",
   200  }, {
   201  	about:        "ensure model and application constraints are separate",
   202  	consToSet:    "virt-type=kvm",
   203  	consFallback: "mem=2G",
   204  
   205  	// application deployment constraints are transformed into machine
   206  	// provisioning constraints. Unit constraints must also have virt-type set
   207  	// to ensure consistency in scalability.
   208  	effectiveModelCons:   "mem=2G",
   209  	effectiveServiceCons: "virt-type=kvm",
   210  	effectiveUnitCons:    "mem=2G virt-type=kvm",
   211  	effectiveMachineCons: "mem=2G virt-type=kvm",
   212  }}
   213  
   214  func (s *constraintsValidationSuite) TestMachineConstraints(c *gc.C) {
   215  	for i, t := range setConstraintsTests {
   216  		c.Logf(
   217  			"test %d: %s\nconsToSet: %q\nconsFallback: %q\n",
   218  			i, t.about, t.consToSet, t.consFallback,
   219  		)
   220  		// Set fallbacks as model constraints and verify them.
   221  		err := s.State.SetModelConstraints(constraints.MustParse(t.consFallback))
   222  		c.Check(err, jc.ErrorIsNil)
   223  		econs, err := s.State.ModelConstraints()
   224  		c.Check(econs, jc.DeepEquals, constraints.MustParse(t.effectiveModelCons))
   225  		// Set the machine provisioning constraints.
   226  		m, err := s.addOneMachine(c, constraints.MustParse(t.consToSet))
   227  		c.Check(err, jc.ErrorIsNil)
   228  		// New machine provisioning constraints get merged with the fallbacks.
   229  		cons, err := m.Constraints()
   230  		c.Check(err, jc.ErrorIsNil)
   231  		c.Check(cons, jc.DeepEquals, constraints.MustParse(t.effectiveMachineCons))
   232  		// Changing them should result in the same result before provisioning.
   233  		err = m.SetConstraints(constraints.MustParse(t.consToSet))
   234  		c.Check(err, jc.ErrorIsNil)
   235  		cons, err = m.Constraints()
   236  		c.Check(err, jc.ErrorIsNil)
   237  		c.Check(cons, jc.DeepEquals, constraints.MustParse(t.effectiveMachineCons))
   238  	}
   239  }
   240  
   241  func (s *constraintsValidationSuite) TestServiceConstraints(c *gc.C) {
   242  	charm := s.AddTestingCharm(c, "wordpress")
   243  	service := s.AddTestingService(c, "wordpress", charm)
   244  	for i, t := range setConstraintsTests {
   245  		c.Logf(
   246  			"test %d: %s\nconsToSet: %q\nconsFallback: %q\n",
   247  			i, t.about, t.consToSet, t.consFallback,
   248  		)
   249  		// Set fallbacks as model constraints and verify them.
   250  		err := s.State.SetModelConstraints(constraints.MustParse(t.consFallback))
   251  		c.Check(err, jc.ErrorIsNil)
   252  		econs, err := s.State.ModelConstraints()
   253  		c.Check(econs, jc.DeepEquals, constraints.MustParse(t.effectiveModelCons))
   254  		// Set the service deployment constraints.
   255  		err = service.SetConstraints(constraints.MustParse(t.consToSet))
   256  		c.Check(err, jc.ErrorIsNil)
   257  		u, err := service.AddUnit()
   258  		c.Check(err, jc.ErrorIsNil)
   259  		// New unit deployment constraints get merged with the fallbacks.
   260  		ucons, err := u.Constraints()
   261  		c.Check(err, jc.ErrorIsNil)
   262  		c.Check(*ucons, jc.DeepEquals, constraints.MustParse(t.effectiveUnitCons))
   263  		// Service constraints remain as set.
   264  		scons, err := service.Constraints()
   265  		c.Check(err, jc.ErrorIsNil)
   266  		c.Check(scons, jc.DeepEquals, constraints.MustParse(t.effectiveServiceCons))
   267  	}
   268  }
   269  
   270  func (s *applicationConstraintsSuite) SetUpTest(c *gc.C) {
   271  	s.ConnSuite.SetUpTest(c)
   272  	s.policy.GetConstraintsValidator = func() (constraints.Validator, error) {
   273  		validator := constraints.NewValidator()
   274  		validator.RegisterVocabulary(constraints.VirtType, []string{"kvm"})
   275  		return validator, nil
   276  	}
   277  	s.applicationName = "wordpress"
   278  	s.testCharm = s.AddTestingCharm(c, s.applicationName)
   279  }
   280  
   281  func (s *applicationConstraintsSuite) TestAddApplicationInvalidConstraints(c *gc.C) {
   282  	cons := constraints.MustParse("virt-type=blah")
   283  	_, err := s.State.AddApplication(state.AddApplicationArgs{
   284  		Name:        s.applicationName,
   285  		Series:      "",
   286  		Charm:       s.testCharm,
   287  		Constraints: cons,
   288  	})
   289  	c.Assert(errors.Cause(err), gc.ErrorMatches, regexp.QuoteMeta("invalid constraint value: virt-type=blah\nvalid values are: [kvm]"))
   290  }
   291  
   292  func (s *applicationConstraintsSuite) TestAddApplicationValidConstraints(c *gc.C) {
   293  	cons := constraints.MustParse("virt-type=kvm")
   294  	service, err := s.State.AddApplication(state.AddApplicationArgs{
   295  		Name:        s.applicationName,
   296  		Series:      "",
   297  		Charm:       s.testCharm,
   298  		Constraints: cons,
   299  	})
   300  	c.Assert(err, jc.ErrorIsNil)
   301  	c.Assert(service, gc.NotNil)
   302  }