github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/apiserver/provisioner/provisioninginfo_test.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package provisioner_test 5 6 import ( 7 jc "github.com/juju/testing/checkers" 8 gc "gopkg.in/check.v1" 9 10 "github.com/juju/juju/apiserver/params" 11 "github.com/juju/juju/apiserver/provisioner" 12 apiservertesting "github.com/juju/juju/apiserver/testing" 13 "github.com/juju/juju/constraints" 14 "github.com/juju/juju/environs/tags" 15 "github.com/juju/juju/juju/testing" 16 "github.com/juju/juju/state" 17 "github.com/juju/juju/state/multiwatcher" 18 "github.com/juju/juju/storage" 19 "github.com/juju/juju/storage/poolmanager" 20 storagedummy "github.com/juju/juju/storage/provider/dummy" 21 "github.com/juju/juju/storage/provider/registry" 22 coretesting "github.com/juju/juju/testing" 23 ) 24 25 func (s *withoutControllerSuite) TestProvisioningInfoWithStorage(c *gc.C) { 26 s.registerStorageProviders(c, "static") 27 28 pm := poolmanager.New(state.NewStateSettings(s.State)) 29 _, err := pm.Create("static-pool", "static", map[string]interface{}{"foo": "bar"}) 30 c.Assert(err, jc.ErrorIsNil) 31 32 cons := constraints.MustParse("cpu-cores=123 mem=8G") 33 template := state.MachineTemplate{ 34 Series: "quantal", 35 Jobs: []state.MachineJob{state.JobHostUnits}, 36 Constraints: cons, 37 Placement: "valid", 38 Volumes: []state.MachineVolumeParams{ 39 {Volume: state.VolumeParams{Size: 1000, Pool: "static-pool"}}, 40 {Volume: state.VolumeParams{Size: 2000, Pool: "static-pool"}}, 41 }, 42 } 43 placementMachine, err := s.State.AddOneMachine(template) 44 c.Assert(err, jc.ErrorIsNil) 45 46 args := params.Entities{Entities: []params.Entity{ 47 {Tag: s.machines[0].Tag().String()}, 48 {Tag: placementMachine.Tag().String()}, 49 }} 50 result, err := s.provisioner.ProvisioningInfo(args) 51 c.Assert(err, jc.ErrorIsNil) 52 53 expected := params.ProvisioningInfoResults{ 54 Results: []params.ProvisioningInfoResult{ 55 {Result: ¶ms.ProvisioningInfo{ 56 Series: "quantal", 57 Jobs: []multiwatcher.MachineJob{multiwatcher.JobHostUnits}, 58 Tags: map[string]string{ 59 tags.JujuController: coretesting.ModelTag.Id(), 60 tags.JujuModel: coretesting.ModelTag.Id(), 61 }, 62 }}, 63 {Result: ¶ms.ProvisioningInfo{ 64 Series: "quantal", 65 Constraints: template.Constraints, 66 Placement: template.Placement, 67 Jobs: []multiwatcher.MachineJob{multiwatcher.JobHostUnits}, 68 Tags: map[string]string{ 69 tags.JujuController: coretesting.ModelTag.Id(), 70 tags.JujuModel: coretesting.ModelTag.Id(), 71 }, 72 Volumes: []params.VolumeParams{{ 73 VolumeTag: "volume-0", 74 Size: 1000, 75 Provider: "static", 76 Attributes: map[string]interface{}{"foo": "bar"}, 77 Tags: map[string]string{ 78 tags.JujuController: coretesting.ModelTag.Id(), 79 tags.JujuModel: coretesting.ModelTag.Id(), 80 }, 81 Attachment: ¶ms.VolumeAttachmentParams{ 82 MachineTag: placementMachine.Tag().String(), 83 VolumeTag: "volume-0", 84 Provider: "static", 85 }, 86 }, { 87 VolumeTag: "volume-1", 88 Size: 2000, 89 Provider: "static", 90 Attributes: map[string]interface{}{"foo": "bar"}, 91 Tags: map[string]string{ 92 tags.JujuController: coretesting.ModelTag.Id(), 93 tags.JujuModel: coretesting.ModelTag.Id(), 94 }, 95 Attachment: ¶ms.VolumeAttachmentParams{ 96 MachineTag: placementMachine.Tag().String(), 97 VolumeTag: "volume-1", 98 Provider: "static", 99 }, 100 }}, 101 }}, 102 }, 103 } 104 // The order of volumes is not predictable, so we make sure we 105 // compare the right ones. This only applies to Results[1] since 106 // it is the only result to contain volumes. 107 if expected.Results[1].Result.Volumes[0].VolumeTag != result.Results[1].Result.Volumes[0].VolumeTag { 108 vols := expected.Results[1].Result.Volumes 109 vols[0], vols[1] = vols[1], vols[0] 110 } 111 c.Assert(result, jc.DeepEquals, expected) 112 } 113 114 func (s *withoutControllerSuite) registerStorageProviders(c *gc.C, names ...string) { 115 types := make([]storage.ProviderType, len(names)) 116 for i, name := range names { 117 types[i] = storage.ProviderType(name) 118 if name == "dynamic" { 119 s.registerDynamicStorageProvider(c) 120 } else if name == "static" { 121 s.registerStaticStorageProvider(c) 122 } else { 123 c.Fatalf("unknown storage provider type: %q, expected static or dynamic", name) 124 } 125 } 126 registry.RegisterEnvironStorageProviders("dummy", types...) 127 } 128 129 func (s *withoutControllerSuite) registerDynamicStorageProvider(c *gc.C) { 130 registry.RegisterProvider("dynamic", &storagedummy.StorageProvider{IsDynamic: true}) 131 s.AddCleanup(func(*gc.C) { 132 registry.RegisterProvider("dynamic", nil) 133 }) 134 } 135 136 func (s *withoutControllerSuite) registerStaticStorageProvider(c *gc.C) { 137 registry.RegisterProvider("static", &storagedummy.StorageProvider{IsDynamic: false}) 138 s.AddCleanup(func(*gc.C) { 139 registry.RegisterProvider("static", nil) 140 }) 141 } 142 143 func (s *withoutControllerSuite) TestProvisioningInfoWithSingleNegativeAndPositiveSpaceInConstraints(c *gc.C) { 144 s.addSpacesAndSubnets(c) 145 146 cons := constraints.MustParse("cpu-cores=123 mem=8G spaces=^space1,space2") 147 template := state.MachineTemplate{ 148 Series: "quantal", 149 Jobs: []state.MachineJob{state.JobHostUnits}, 150 Constraints: cons, 151 Placement: "valid", 152 } 153 placementMachine, err := s.State.AddOneMachine(template) 154 c.Assert(err, jc.ErrorIsNil) 155 156 args := params.Entities{Entities: []params.Entity{ 157 {Tag: placementMachine.Tag().String()}, 158 }} 159 result, err := s.provisioner.ProvisioningInfo(args) 160 c.Assert(err, jc.ErrorIsNil) 161 162 expected := params.ProvisioningInfoResults{ 163 Results: []params.ProvisioningInfoResult{{ 164 Result: ¶ms.ProvisioningInfo{ 165 Series: "quantal", 166 Constraints: template.Constraints, 167 Placement: template.Placement, 168 Jobs: []multiwatcher.MachineJob{multiwatcher.JobHostUnits}, 169 Tags: map[string]string{ 170 tags.JujuController: coretesting.ModelTag.Id(), 171 tags.JujuModel: coretesting.ModelTag.Id(), 172 }, 173 SubnetsToZones: map[string][]string{ 174 "subnet-1": []string{"zone1"}, 175 "subnet-2": []string{"zone2"}, 176 }, 177 }, 178 }}} 179 c.Assert(result, jc.DeepEquals, expected) 180 } 181 182 func (s *withoutControllerSuite) addSpacesAndSubnets(c *gc.C) { 183 // Add a couple of spaces. 184 _, err := s.State.AddSpace("space1", "first space id", nil, true) 185 c.Assert(err, jc.ErrorIsNil) 186 _, err = s.State.AddSpace("space2", "", nil, false) // no provider ID 187 c.Assert(err, jc.ErrorIsNil) 188 // Add 1 subnet into space1, and 2 into space2. 189 // Only the first subnet of space2 has AllocatableIPLow|High set. 190 // Each subnet is in a matching zone (e.g "subnet-#" in "zone#"). 191 testing.AddSubnetsWithTemplate(c, s.State, 3, state.SubnetInfo{ 192 CIDR: "10.10.{{.}}.0/24", 193 ProviderId: "subnet-{{.}}", 194 AllocatableIPLow: "{{if (eq . 1)}}10.10.{{.}}.5{{end}}", 195 AllocatableIPHigh: "{{if (eq . 1)}}10.10.{{.}}.254{{end}}", 196 AvailabilityZone: "zone{{.}}", 197 SpaceName: "{{if (eq . 0)}}space1{{else}}space2{{end}}", 198 VLANTag: 42, 199 }) 200 } 201 202 func (s *withoutControllerSuite) TestProvisioningInfoWithEndpointBindings(c *gc.C) { 203 s.addSpacesAndSubnets(c) 204 205 wordpressMachine, err := s.State.AddOneMachine(state.MachineTemplate{ 206 Series: "quantal", 207 Jobs: []state.MachineJob{state.JobHostUnits}, 208 }) 209 c.Assert(err, jc.ErrorIsNil) 210 211 // Use juju names for spaces in bindings, simulating ''juju deploy 212 // --bind...' was called. 213 bindings := map[string]string{ 214 "url": "space1", // has both name and provider ID 215 "db": "space2", // has only name, no provider ID 216 } 217 wordpressCharm := s.AddTestingCharm(c, "wordpress") 218 wordpressService := s.AddTestingServiceWithBindings(c, "wordpress", wordpressCharm, bindings) 219 wordpressUnit, err := wordpressService.AddUnit() 220 c.Assert(err, jc.ErrorIsNil) 221 err = wordpressUnit.AssignToMachine(wordpressMachine) 222 c.Assert(err, jc.ErrorIsNil) 223 224 args := params.Entities{Entities: []params.Entity{ 225 {Tag: wordpressMachine.Tag().String()}, 226 }} 227 result, err := s.provisioner.ProvisioningInfo(args) 228 c.Assert(err, jc.ErrorIsNil) 229 230 expected := params.ProvisioningInfoResults{ 231 Results: []params.ProvisioningInfoResult{{ 232 Result: ¶ms.ProvisioningInfo{ 233 Series: "quantal", 234 Jobs: []multiwatcher.MachineJob{multiwatcher.JobHostUnits}, 235 Tags: map[string]string{ 236 tags.JujuController: coretesting.ModelTag.Id(), 237 tags.JujuModel: coretesting.ModelTag.Id(), 238 tags.JujuUnitsDeployed: wordpressUnit.Name(), 239 }, 240 // Ensure space names are translated to provider IDs, where 241 // possible. 242 EndpointBindings: map[string]string{ 243 "db": "space2", // just name, no provider ID 244 "url": "first space id", // has provider ID 245 // We expect none of the unspecified bindings in the result. 246 }, 247 }, 248 }}} 249 c.Assert(result, jc.DeepEquals, expected) 250 } 251 252 func (s *withoutControllerSuite) TestProvisioningInfoWithUnsuitableSpacesConstraints(c *gc.C) { 253 // Add an empty space. 254 _, err := s.State.AddSpace("empty", "", nil, true) 255 c.Assert(err, jc.ErrorIsNil) 256 257 consEmptySpace := constraints.MustParse("cpu-cores=123 mem=8G spaces=empty") 258 consMissingSpace := constraints.MustParse("cpu-cores=123 mem=8G spaces=missing") 259 templates := []state.MachineTemplate{{ 260 Series: "quantal", 261 Jobs: []state.MachineJob{state.JobHostUnits}, 262 Constraints: consEmptySpace, 263 Placement: "valid", 264 }, { 265 Series: "quantal", 266 Jobs: []state.MachineJob{state.JobHostUnits}, 267 Constraints: consMissingSpace, 268 Placement: "valid", 269 }} 270 placementMachines, err := s.State.AddMachines(templates...) 271 c.Assert(err, jc.ErrorIsNil) 272 c.Assert(placementMachines, gc.HasLen, 2) 273 274 args := params.Entities{Entities: []params.Entity{ 275 {Tag: placementMachines[0].Tag().String()}, 276 {Tag: placementMachines[1].Tag().String()}, 277 }} 278 result, err := s.provisioner.ProvisioningInfo(args) 279 c.Assert(err, jc.ErrorIsNil) 280 281 expectedErrorEmptySpace := `cannot match subnets to zones: ` + 282 `cannot use space "empty" as deployment target: no subnets` 283 expectedErrorMissingSpace := `cannot match subnets to zones: ` + 284 `space "missing"` // " not found" will be appended by NotFoundError helper below. 285 expected := params.ProvisioningInfoResults{Results: []params.ProvisioningInfoResult{ 286 {Error: apiservertesting.ServerError(expectedErrorEmptySpace)}, 287 {Error: apiservertesting.NotFoundError(expectedErrorMissingSpace)}, 288 }} 289 c.Assert(result, jc.DeepEquals, expected) 290 } 291 292 func (s *withoutControllerSuite) TestStorageProviderFallbackToType(c *gc.C) { 293 s.registerStorageProviders(c, "dynamic", "static") 294 295 template := state.MachineTemplate{ 296 Series: "quantal", 297 Jobs: []state.MachineJob{state.JobHostUnits}, 298 Placement: "valid", 299 Volumes: []state.MachineVolumeParams{ 300 {Volume: state.VolumeParams{Size: 1000, Pool: "dynamic"}}, 301 {Volume: state.VolumeParams{Size: 1000, Pool: "static"}}, 302 }, 303 } 304 placementMachine, err := s.State.AddOneMachine(template) 305 c.Assert(err, jc.ErrorIsNil) 306 307 args := params.Entities{Entities: []params.Entity{ 308 {Tag: placementMachine.Tag().String()}, 309 }} 310 result, err := s.provisioner.ProvisioningInfo(args) 311 c.Assert(err, jc.ErrorIsNil) 312 313 c.Assert(result, jc.DeepEquals, params.ProvisioningInfoResults{ 314 Results: []params.ProvisioningInfoResult{ 315 {Result: ¶ms.ProvisioningInfo{ 316 Series: "quantal", 317 Constraints: template.Constraints, 318 Placement: template.Placement, 319 Jobs: []multiwatcher.MachineJob{multiwatcher.JobHostUnits}, 320 Tags: map[string]string{ 321 tags.JujuController: coretesting.ModelTag.Id(), 322 tags.JujuModel: coretesting.ModelTag.Id(), 323 }, 324 Volumes: []params.VolumeParams{{ 325 VolumeTag: "volume-1", 326 Size: 1000, 327 Provider: "static", 328 Attributes: nil, 329 Tags: map[string]string{ 330 tags.JujuController: coretesting.ModelTag.Id(), 331 tags.JujuModel: coretesting.ModelTag.Id(), 332 }, 333 Attachment: ¶ms.VolumeAttachmentParams{ 334 MachineTag: placementMachine.Tag().String(), 335 VolumeTag: "volume-1", 336 Provider: "static", 337 }, 338 }}, 339 }}, 340 }, 341 }) 342 } 343 344 func (s *withoutControllerSuite) TestProvisioningInfoPermissions(c *gc.C) { 345 // Login as a machine agent for machine 0. 346 anAuthorizer := s.authorizer 347 anAuthorizer.EnvironManager = false 348 anAuthorizer.Tag = s.machines[0].Tag() 349 aProvisioner, err := provisioner.NewProvisionerAPI(s.State, s.resources, anAuthorizer) 350 c.Assert(err, jc.ErrorIsNil) 351 c.Assert(aProvisioner, gc.NotNil) 352 353 args := params.Entities{Entities: []params.Entity{ 354 {Tag: s.machines[0].Tag().String()}, 355 {Tag: s.machines[0].Tag().String() + "-lxc-0"}, 356 {Tag: "machine-42"}, 357 {Tag: s.machines[1].Tag().String()}, 358 {Tag: "service-bar"}, 359 }} 360 361 // Only machine 0 and containers therein can be accessed. 362 results, err := aProvisioner.ProvisioningInfo(args) 363 c.Assert(results, jc.DeepEquals, params.ProvisioningInfoResults{ 364 Results: []params.ProvisioningInfoResult{ 365 {Result: ¶ms.ProvisioningInfo{ 366 Series: "quantal", 367 Jobs: []multiwatcher.MachineJob{multiwatcher.JobHostUnits}, 368 Tags: map[string]string{ 369 tags.JujuController: coretesting.ModelTag.Id(), 370 tags.JujuModel: coretesting.ModelTag.Id(), 371 }, 372 }}, 373 {Error: apiservertesting.NotFoundError("machine 0/lxc/0")}, 374 {Error: apiservertesting.ErrUnauthorized}, 375 {Error: apiservertesting.ErrUnauthorized}, 376 {Error: apiservertesting.ErrUnauthorized}, 377 }, 378 }) 379 }