github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/provider/azure/instance_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package azure_test 5 6 import ( 7 "net/http" 8 "path" 9 10 "github.com/Azure/azure-sdk-for-go/Godeps/_workspace/src/github.com/Azure/go-autorest/autorest/mocks" 11 "github.com/Azure/azure-sdk-for-go/Godeps/_workspace/src/github.com/Azure/go-autorest/autorest/to" 12 "github.com/Azure/azure-sdk-for-go/arm/compute" 13 "github.com/Azure/azure-sdk-for-go/arm/network" 14 jc "github.com/juju/testing/checkers" 15 gc "gopkg.in/check.v1" 16 17 "github.com/juju/juju/environs" 18 "github.com/juju/juju/instance" 19 jujunetwork "github.com/juju/juju/network" 20 "github.com/juju/juju/provider/azure" 21 "github.com/juju/juju/provider/azure/internal/azuretesting" 22 "github.com/juju/juju/testing" 23 ) 24 25 type instanceSuite struct { 26 testing.BaseSuite 27 28 provider environs.EnvironProvider 29 requests []*http.Request 30 sender azuretesting.Senders 31 env environs.Environ 32 virtualMachines []compute.VirtualMachine 33 networkInterfaces []network.Interface 34 publicIPAddresses []network.PublicIPAddress 35 } 36 37 var _ = gc.Suite(&instanceSuite{}) 38 39 func (s *instanceSuite) SetUpTest(c *gc.C) { 40 s.BaseSuite.SetUpTest(c) 41 s.provider, _ = newProviders(c, azure.ProviderConfig{ 42 Sender: &s.sender, 43 RequestInspector: requestRecorder(&s.requests), 44 }) 45 s.env = openEnviron(c, s.provider, &s.sender) 46 s.sender = nil 47 s.requests = nil 48 s.networkInterfaces = []network.Interface{ 49 makeNetworkInterface("nic-0", "machine-0"), 50 } 51 s.publicIPAddresses = nil 52 s.virtualMachines = []compute.VirtualMachine{ 53 makeVirtualMachine("machine-0"), 54 makeVirtualMachine("machine-1"), 55 } 56 } 57 58 func makeVirtualMachine(name string) compute.VirtualMachine { 59 return compute.VirtualMachine{ 60 Name: to.StringPtr(name), 61 Properties: &compute.VirtualMachineProperties{ 62 ProvisioningState: to.StringPtr("Successful"), 63 }, 64 } 65 } 66 67 func makeNetworkInterface(nicName, vmName string, ipConfigurations ...network.InterfaceIPConfiguration) network.Interface { 68 tags := map[string]*string{"juju-machine-name": &vmName} 69 return network.Interface{ 70 Name: to.StringPtr(nicName), 71 Tags: &tags, 72 Properties: &network.InterfacePropertiesFormat{ 73 IPConfigurations: &ipConfigurations, 74 }, 75 } 76 } 77 78 func makeIPConfiguration(privateIPAddress string) network.InterfaceIPConfiguration { 79 ipConfiguration := network.InterfaceIPConfiguration{ 80 Properties: &network.InterfaceIPConfigurationPropertiesFormat{}, 81 } 82 if privateIPAddress != "" { 83 ipConfiguration.Properties.PrivateIPAddress = to.StringPtr(privateIPAddress) 84 } 85 return ipConfiguration 86 } 87 88 func makePublicIPAddress(pipName, vmName, ipAddress string) network.PublicIPAddress { 89 tags := map[string]*string{"juju-machine-name": &vmName} 90 pip := network.PublicIPAddress{ 91 Name: to.StringPtr(pipName), 92 Tags: &tags, 93 Properties: &network.PublicIPAddressPropertiesFormat{}, 94 } 95 if ipAddress != "" { 96 pip.Properties.IPAddress = to.StringPtr(ipAddress) 97 } 98 return pip 99 } 100 101 func makeSecurityGroup(rules ...network.SecurityRule) network.SecurityGroup { 102 return network.SecurityGroup{ 103 Properties: &network.SecurityGroupPropertiesFormat{ 104 SecurityRules: &rules, 105 }, 106 } 107 } 108 109 func makeSecurityRule(name, ipAddress, ports string) network.SecurityRule { 110 return network.SecurityRule{ 111 Name: to.StringPtr(name), 112 Properties: &network.SecurityRulePropertiesFormat{ 113 Protocol: network.SecurityRuleProtocolTCP, 114 DestinationAddressPrefix: to.StringPtr(ipAddress), 115 DestinationPortRange: to.StringPtr(ports), 116 Access: network.Allow, 117 Priority: to.IntPtr(200), 118 Direction: network.Inbound, 119 }, 120 } 121 } 122 123 func (s *instanceSuite) getInstance(c *gc.C) instance.Instance { 124 instances := s.getInstances(c, "machine-0") 125 c.Assert(instances, gc.HasLen, 1) 126 return instances[0] 127 } 128 129 func (s *instanceSuite) getInstances(c *gc.C, ids ...instance.Id) []instance.Instance { 130 131 nicsSender := azuretesting.NewSenderWithValue(&network.InterfaceListResult{ 132 Value: &s.networkInterfaces, 133 }) 134 nicsSender.PathPattern = ".*/networkInterfaces" 135 136 vmsSender := azuretesting.NewSenderWithValue(&compute.VirtualMachineListResult{ 137 Value: &s.virtualMachines, 138 }) 139 vmsSender.PathPattern = ".*/virtualMachines" 140 141 pipsSender := azuretesting.NewSenderWithValue(&network.PublicIPAddressListResult{ 142 Value: &s.publicIPAddresses, 143 }) 144 pipsSender.PathPattern = ".*/publicIPAddresses" 145 146 s.sender = azuretesting.Senders{nicsSender, vmsSender, pipsSender} 147 148 instances, err := s.env.Instances(ids) 149 c.Assert(err, jc.ErrorIsNil) 150 s.sender = azuretesting.Senders{} 151 s.requests = nil 152 return instances 153 } 154 155 func networkSecurityGroupSender(rules []network.SecurityRule) *azuretesting.MockSender { 156 nsgSender := azuretesting.NewSenderWithValue(&network.SecurityGroup{ 157 Properties: &network.SecurityGroupPropertiesFormat{ 158 SecurityRules: &rules, 159 }, 160 }) 161 nsgSender.PathPattern = ".*/networkSecurityGroups/juju-internal" 162 return nsgSender 163 } 164 165 func (s *instanceSuite) TestInstanceStatus(c *gc.C) { 166 inst := s.getInstance(c) 167 c.Assert(inst.Status().Message, gc.Equals, "Successful") 168 } 169 170 func (s *instanceSuite) TestInstanceStatusNilProvisioningState(c *gc.C) { 171 s.virtualMachines[0].Properties.ProvisioningState = nil 172 inst := s.getInstance(c) 173 c.Assert(inst.Status().Message, gc.Equals, "") 174 } 175 176 func (s *instanceSuite) TestInstanceStatusNoVM(c *gc.C) { 177 // Instances will still return an instance if there's a NIC, which is 178 // the last thing we delete. If there's no VM, we return the string 179 // "Partially Deleted" from Instance.Status(). 180 s.virtualMachines = nil 181 inst := s.getInstance(c) 182 c.Assert(inst.Status().Message, gc.Equals, "Partially Deleted") 183 } 184 185 func (s *instanceSuite) TestInstanceAddressesEmpty(c *gc.C) { 186 addresses, err := s.getInstance(c).Addresses() 187 c.Assert(err, jc.ErrorIsNil) 188 c.Assert(addresses, gc.HasLen, 0) 189 } 190 191 func (s *instanceSuite) TestInstanceAddresses(c *gc.C) { 192 nic0IPConfigurations := []network.InterfaceIPConfiguration{ 193 makeIPConfiguration("10.0.0.4"), 194 makeIPConfiguration("10.0.0.5"), 195 } 196 nic1IPConfigurations := []network.InterfaceIPConfiguration{ 197 makeIPConfiguration(""), 198 } 199 s.networkInterfaces = []network.Interface{ 200 makeNetworkInterface("nic-0", "machine-0", nic0IPConfigurations...), 201 makeNetworkInterface("nic-1", "machine-0", nic1IPConfigurations...), 202 makeNetworkInterface("nic-2", "machine-0"), 203 // unrelated NIC 204 makeNetworkInterface("nic-3", "machine-1"), 205 } 206 s.publicIPAddresses = []network.PublicIPAddress{ 207 makePublicIPAddress("pip-0", "machine-0", "1.2.3.4"), 208 makePublicIPAddress("pip-1", "machine-0", "1.2.3.5"), 209 // unrelated PIP 210 makePublicIPAddress("pip-2", "machine-1", "1.2.3.6"), 211 } 212 addresses, err := s.getInstance(c).Addresses() 213 c.Assert(err, jc.ErrorIsNil) 214 c.Assert(addresses, jc.DeepEquals, jujunetwork.NewAddresses( 215 "10.0.0.4", "10.0.0.5", "1.2.3.4", "1.2.3.5", 216 )) 217 } 218 219 func (s *instanceSuite) TestMultipleInstanceAddresses(c *gc.C) { 220 nic0IPConfiguration := makeIPConfiguration("10.0.0.4") 221 nic1IPConfiguration := makeIPConfiguration("10.0.0.5") 222 s.networkInterfaces = []network.Interface{ 223 makeNetworkInterface("nic-0", "machine-0", nic0IPConfiguration), 224 makeNetworkInterface("nic-1", "machine-1", nic1IPConfiguration), 225 } 226 s.publicIPAddresses = []network.PublicIPAddress{ 227 makePublicIPAddress("pip-0", "machine-0", "1.2.3.4"), 228 makePublicIPAddress("pip-1", "machine-1", "1.2.3.5"), 229 } 230 instances := s.getInstances(c, "machine-0", "machine-1") 231 c.Assert(instances, gc.HasLen, 2) 232 233 inst0Addresses, err := instances[0].Addresses() 234 c.Assert(err, jc.ErrorIsNil) 235 c.Assert(inst0Addresses, jc.DeepEquals, jujunetwork.NewAddresses( 236 "10.0.0.4", "1.2.3.4", 237 )) 238 239 inst1Addresses, err := instances[1].Addresses() 240 c.Assert(err, jc.ErrorIsNil) 241 c.Assert(inst1Addresses, jc.DeepEquals, jujunetwork.NewAddresses( 242 "10.0.0.5", "1.2.3.5", 243 )) 244 } 245 246 func (s *instanceSuite) TestInstancePortsEmpty(c *gc.C) { 247 inst := s.getInstance(c) 248 nsgSender := networkSecurityGroupSender(nil) 249 s.sender = azuretesting.Senders{nsgSender} 250 ports, err := inst.Ports("0") 251 c.Assert(err, jc.ErrorIsNil) 252 c.Assert(ports, gc.HasLen, 0) 253 } 254 255 func (s *instanceSuite) TestInstancePorts(c *gc.C) { 256 inst := s.getInstance(c) 257 nsgSender := networkSecurityGroupSender([]network.SecurityRule{{ 258 Name: to.StringPtr("machine-0-xyzzy"), 259 Properties: &network.SecurityRulePropertiesFormat{ 260 Protocol: network.SecurityRuleProtocolUDP, 261 DestinationPortRange: to.StringPtr("*"), 262 Access: network.Allow, 263 Priority: to.IntPtr(200), 264 Direction: network.Inbound, 265 }, 266 }, { 267 Name: to.StringPtr("machine-0-tcpcp"), 268 Properties: &network.SecurityRulePropertiesFormat{ 269 Protocol: network.SecurityRuleProtocolTCP, 270 DestinationPortRange: to.StringPtr("1000-2000"), 271 Access: network.Allow, 272 Priority: to.IntPtr(201), 273 Direction: network.Inbound, 274 }, 275 }, { 276 Name: to.StringPtr("machine-0-http"), 277 Properties: &network.SecurityRulePropertiesFormat{ 278 Protocol: network.SecurityRuleProtocolAsterisk, 279 DestinationPortRange: to.StringPtr("80"), 280 Access: network.Allow, 281 Priority: to.IntPtr(202), 282 Direction: network.Inbound, 283 }, 284 }, { 285 Name: to.StringPtr("machine-00-ignored"), 286 Properties: &network.SecurityRulePropertiesFormat{ 287 Protocol: network.SecurityRuleProtocolTCP, 288 DestinationPortRange: to.StringPtr("80"), 289 Access: network.Allow, 290 Priority: to.IntPtr(202), 291 Direction: network.Inbound, 292 }, 293 }, { 294 Name: to.StringPtr("machine-0-ignored"), 295 Properties: &network.SecurityRulePropertiesFormat{ 296 Protocol: network.SecurityRuleProtocolTCP, 297 DestinationPortRange: to.StringPtr("80"), 298 Access: network.Deny, 299 Priority: to.IntPtr(202), 300 Direction: network.Inbound, 301 }, 302 }, { 303 Name: to.StringPtr("machine-0-ignored"), 304 Properties: &network.SecurityRulePropertiesFormat{ 305 Protocol: network.SecurityRuleProtocolTCP, 306 DestinationPortRange: to.StringPtr("80"), 307 Access: network.Allow, 308 Priority: to.IntPtr(202), 309 Direction: network.Outbound, 310 }, 311 }, { 312 Name: to.StringPtr("machine-0-ignored"), 313 Properties: &network.SecurityRulePropertiesFormat{ 314 Protocol: network.SecurityRuleProtocolTCP, 315 DestinationPortRange: to.StringPtr("80"), 316 Access: network.Allow, 317 Priority: to.IntPtr(199), // internal range 318 Direction: network.Inbound, 319 }, 320 }}) 321 s.sender = azuretesting.Senders{nsgSender} 322 323 ports, err := inst.Ports("0") 324 c.Assert(err, jc.ErrorIsNil) 325 c.Assert(ports, jc.DeepEquals, []jujunetwork.PortRange{{ 326 FromPort: 0, 327 ToPort: 65535, 328 Protocol: "udp", 329 }, { 330 FromPort: 1000, 331 ToPort: 2000, 332 Protocol: "tcp", 333 }, { 334 FromPort: 80, 335 ToPort: 80, 336 Protocol: "tcp", 337 }, { 338 FromPort: 80, 339 ToPort: 80, 340 Protocol: "udp", 341 }}) 342 } 343 344 func (s *instanceSuite) TestInstanceClosePorts(c *gc.C) { 345 inst := s.getInstance(c) 346 sender := mocks.NewSender() 347 notFoundSender := mocks.NewSender() 348 notFoundSender.EmitStatus("rule not found", http.StatusNotFound) 349 s.sender = azuretesting.Senders{sender, notFoundSender} 350 351 err := inst.ClosePorts("0", []jujunetwork.PortRange{{ 352 Protocol: "tcp", 353 FromPort: 1000, 354 ToPort: 1000, 355 }, { 356 Protocol: "udp", 357 FromPort: 1000, 358 ToPort: 2000, 359 }}) 360 c.Assert(err, jc.ErrorIsNil) 361 362 c.Assert(s.requests, gc.HasLen, 2) 363 c.Assert(s.requests[0].Method, gc.Equals, "DELETE") 364 c.Assert(s.requests[0].URL.Path, gc.Equals, securityRulePath("machine-0-tcp-1000")) 365 c.Assert(s.requests[1].Method, gc.Equals, "DELETE") 366 c.Assert(s.requests[1].URL.Path, gc.Equals, securityRulePath("machine-0-udp-1000-2000")) 367 } 368 369 func (s *instanceSuite) TestInstanceOpenPorts(c *gc.C) { 370 internalSubnetId := path.Join( 371 "/subscriptions", fakeSubscriptionId, 372 "resourceGroups/arbitrary/providers/Microsoft.Network/virtualnetworks/juju-internal/subnets", 373 "juju-testenv-model-"+testing.ModelTag.Id(), 374 ) 375 ipConfiguration := network.InterfaceIPConfiguration{ 376 Properties: &network.InterfaceIPConfigurationPropertiesFormat{ 377 PrivateIPAddress: to.StringPtr("10.0.0.4"), 378 Subnet: &network.SubResource{ 379 ID: to.StringPtr(internalSubnetId), 380 }, 381 }, 382 } 383 s.networkInterfaces = []network.Interface{ 384 makeNetworkInterface("nic-0", "machine-0", ipConfiguration), 385 } 386 387 inst := s.getInstance(c) 388 okSender := mocks.NewSender() 389 okSender.EmitContent("{}") 390 nsgSender := networkSecurityGroupSender(nil) 391 s.sender = azuretesting.Senders{nsgSender, okSender, okSender} 392 393 err := inst.OpenPorts("0", []jujunetwork.PortRange{{ 394 Protocol: "tcp", 395 FromPort: 1000, 396 ToPort: 1000, 397 }, { 398 Protocol: "udp", 399 FromPort: 1000, 400 ToPort: 2000, 401 }}) 402 c.Assert(err, jc.ErrorIsNil) 403 404 c.Assert(s.requests, gc.HasLen, 3) 405 c.Assert(s.requests[0].Method, gc.Equals, "GET") 406 c.Assert(s.requests[0].URL.Path, gc.Equals, internalSecurityGroupPath) 407 c.Assert(s.requests[1].Method, gc.Equals, "PUT") 408 c.Assert(s.requests[1].URL.Path, gc.Equals, securityRulePath("machine-0-tcp-1000")) 409 assertRequestBody(c, s.requests[1], &network.SecurityRule{ 410 Properties: &network.SecurityRulePropertiesFormat{ 411 Description: to.StringPtr("1000/tcp"), 412 Protocol: network.SecurityRuleProtocolTCP, 413 SourcePortRange: to.StringPtr("*"), 414 SourceAddressPrefix: to.StringPtr("*"), 415 DestinationPortRange: to.StringPtr("1000"), 416 DestinationAddressPrefix: to.StringPtr("10.0.0.4"), 417 Access: network.Allow, 418 Priority: to.IntPtr(200), 419 Direction: network.Inbound, 420 }, 421 }) 422 c.Assert(s.requests[2].Method, gc.Equals, "PUT") 423 c.Assert(s.requests[2].URL.Path, gc.Equals, securityRulePath("machine-0-udp-1000-2000")) 424 assertRequestBody(c, s.requests[2], &network.SecurityRule{ 425 Properties: &network.SecurityRulePropertiesFormat{ 426 Description: to.StringPtr("1000-2000/udp"), 427 Protocol: network.SecurityRuleProtocolUDP, 428 SourcePortRange: to.StringPtr("*"), 429 SourceAddressPrefix: to.StringPtr("*"), 430 DestinationPortRange: to.StringPtr("1000-2000"), 431 DestinationAddressPrefix: to.StringPtr("10.0.0.4"), 432 Access: network.Allow, 433 Priority: to.IntPtr(201), 434 Direction: network.Inbound, 435 }, 436 }) 437 } 438 439 func (s *instanceSuite) TestInstanceOpenPortsAlreadyOpen(c *gc.C) { 440 internalSubnetId := path.Join( 441 "/subscriptions", fakeSubscriptionId, 442 "resourceGroups/arbitrary/providers/Microsoft.Network/virtualnetworks/juju-internal/subnets", 443 "juju-testenv-model-"+testing.ModelTag.Id(), 444 ) 445 ipConfiguration := network.InterfaceIPConfiguration{ 446 Properties: &network.InterfaceIPConfigurationPropertiesFormat{ 447 PrivateIPAddress: to.StringPtr("10.0.0.4"), 448 Subnet: &network.SubResource{ 449 ID: to.StringPtr(internalSubnetId), 450 }, 451 }, 452 } 453 s.networkInterfaces = []network.Interface{ 454 makeNetworkInterface("nic-0", "machine-0", ipConfiguration), 455 } 456 457 inst := s.getInstance(c) 458 okSender := mocks.NewSender() 459 okSender.EmitContent("{}") 460 nsgSender := networkSecurityGroupSender([]network.SecurityRule{{ 461 Name: to.StringPtr("machine-0-tcp-1000"), 462 Properties: &network.SecurityRulePropertiesFormat{ 463 Protocol: network.SecurityRuleProtocolAsterisk, 464 DestinationPortRange: to.StringPtr("1000"), 465 Access: network.Allow, 466 Priority: to.IntPtr(202), 467 Direction: network.Inbound, 468 }, 469 }}) 470 s.sender = azuretesting.Senders{nsgSender, okSender, okSender} 471 472 err := inst.OpenPorts("0", []jujunetwork.PortRange{{ 473 Protocol: "tcp", 474 FromPort: 1000, 475 ToPort: 1000, 476 }, { 477 Protocol: "udp", 478 FromPort: 1000, 479 ToPort: 2000, 480 }}) 481 c.Assert(err, jc.ErrorIsNil) 482 483 c.Assert(s.requests, gc.HasLen, 2) 484 c.Assert(s.requests[0].Method, gc.Equals, "GET") 485 c.Assert(s.requests[0].URL.Path, gc.Equals, internalSecurityGroupPath) 486 c.Assert(s.requests[1].Method, gc.Equals, "PUT") 487 c.Assert(s.requests[1].URL.Path, gc.Equals, securityRulePath("machine-0-udp-1000-2000")) 488 assertRequestBody(c, s.requests[1], &network.SecurityRule{ 489 Properties: &network.SecurityRulePropertiesFormat{ 490 Description: to.StringPtr("1000-2000/udp"), 491 Protocol: network.SecurityRuleProtocolUDP, 492 SourcePortRange: to.StringPtr("*"), 493 SourceAddressPrefix: to.StringPtr("*"), 494 DestinationPortRange: to.StringPtr("1000-2000"), 495 DestinationAddressPrefix: to.StringPtr("10.0.0.4"), 496 Access: network.Allow, 497 Priority: to.IntPtr(200), 498 Direction: network.Inbound, 499 }, 500 }) 501 } 502 503 func (s *instanceSuite) TestInstanceOpenPortsNoInternalAddress(c *gc.C) { 504 err := s.getInstance(c).OpenPorts("0", nil) 505 c.Assert(err, gc.ErrorMatches, "internal network address not found") 506 } 507 508 var internalSecurityGroupPath = path.Join( 509 "/subscriptions", fakeSubscriptionId, 510 "resourceGroups", "juju-testenv-model-"+testing.ModelTag.Id(), 511 "providers/Microsoft.Network/networkSecurityGroups/juju-internal", 512 ) 513 514 func securityRulePath(ruleName string) string { 515 return path.Join(internalSecurityGroupPath, "securityRules", ruleName) 516 }