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 }