1
// Copyright 2015 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
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"
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"
25
type instanceSuite struct {
28
provider environs.EnvironProvider
29
requests []*http.Request
30
sender azuretesting.Senders
32
virtualMachines []compute.VirtualMachine
33
networkInterfaces []network.Interface
34
publicIPAddresses []network.PublicIPAddress
37
var _ = gc.Suite(&instanceSuite{})
39
func (s *instanceSuite) SetUpTest(c *gc.C) {
40
s.BaseSuite.SetUpTest(c)
41
s.provider, _ = newProviders(c, azure.ProviderConfig{
43
RequestInspector: requestRecorder(&s.requests),
45
s.env = openEnviron(c, s.provider, &s.sender)
48
s.networkInterfaces = []network.Interface{
49
makeNetworkInterface("nic-0", "machine-0"),
51
s.publicIPAddresses = nil
52
s.virtualMachines = []compute.VirtualMachine{
53
makeVirtualMachine("machine-0"),
54
makeVirtualMachine("machine-1"),
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"),
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),
72
Properties: &network.InterfacePropertiesFormat{
73
IPConfigurations: &ipConfigurations,
78
func makeIPConfiguration(privateIPAddress string) network.InterfaceIPConfiguration {
79
ipConfiguration := network.InterfaceIPConfiguration{
80
Properties: &network.InterfaceIPConfigurationPropertiesFormat{},
82
if privateIPAddress != "" {
83
ipConfiguration.Properties.PrivateIPAddress = to.StringPtr(privateIPAddress)
85
return ipConfiguration
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),
93
Properties: &network.PublicIPAddressPropertiesFormat{},
96
pip.Properties.IPAddress = to.StringPtr(ipAddress)
101
func makeSecurityGroup(rules ...network.SecurityRule) network.SecurityGroup {
102
return network.SecurityGroup{
103
Properties: &network.SecurityGroupPropertiesFormat{
104
SecurityRules: &rules,
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,
123
func (s *instanceSuite) getInstance(c *gc.C) instance.Instance {
124
instances := s.getInstances(c, "machine-0")
125
c.Assert(instances, gc.HasLen, 1)
129
func (s *instanceSuite) getInstances(c *gc.C, ids ...instance.Id) []instance.Instance {
131
nicsSender := azuretesting.NewSenderWithValue(&network.InterfaceListResult{
132
Value: &s.networkInterfaces,
134
nicsSender.PathPattern = ".*/networkInterfaces"
136
vmsSender := azuretesting.NewSenderWithValue(&compute.VirtualMachineListResult{
137
Value: &s.virtualMachines,
139
vmsSender.PathPattern = ".*/virtualMachines"
141
pipsSender := azuretesting.NewSenderWithValue(&network.PublicIPAddressListResult{
142
Value: &s.publicIPAddresses,
144
pipsSender.PathPattern = ".*/publicIPAddresses"
146
s.sender = azuretesting.Senders{nicsSender, vmsSender, pipsSender}
148
instances, err := s.env.Instances(ids)
149
c.Assert(err, jc.ErrorIsNil)
150
s.sender = azuretesting.Senders{}
155
func networkSecurityGroupSender(rules []network.SecurityRule) *azuretesting.MockSender {
156
nsgSender := azuretesting.NewSenderWithValue(&network.SecurityGroup{
157
Properties: &network.SecurityGroupPropertiesFormat{
158
SecurityRules: &rules,
161
nsgSender.PathPattern = ".*/networkSecurityGroups/juju-internal"
165
func (s *instanceSuite) TestInstanceStatus(c *gc.C) {
166
inst := s.getInstance(c)
167
c.Assert(inst.Status(), gc.Equals, "Successful")
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(), gc.Equals, "")
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(), gc.Equals, "Partially Deleted")
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)
191
func (s *instanceSuite) TestInstanceAddresses(c *gc.C) {
192
nic0IPConfigurations := []network.InterfaceIPConfiguration{
193
makeIPConfiguration("10.0.0.4"),
194
makeIPConfiguration("10.0.0.5"),
196
nic1IPConfigurations := []network.InterfaceIPConfiguration{
197
makeIPConfiguration(""),
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"),
204
makeNetworkInterface("nic-3", "machine-1"),
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"),
210
makePublicIPAddress("pip-2", "machine-1", "1.2.3.6"),
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",
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),
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"),
230
instances := s.getInstances(c, "machine-0", "machine-1")
231
c.Assert(instances, gc.HasLen, 2)
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",
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",
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)
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,
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,
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,
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,
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,
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,
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,
321
s.sender = azuretesting.Senders{nsgSender}
323
ports, err := inst.Ports("0")
324
c.Assert(err, jc.ErrorIsNil)
325
c.Assert(ports, jc.DeepEquals, []jujunetwork.PortRange{{
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}
351
err := inst.ClosePorts("0", []jujunetwork.PortRange{{
360
c.Assert(err, jc.ErrorIsNil)
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"))
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(),
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),
383
s.networkInterfaces = []network.Interface{
384
makeNetworkInterface("nic-0", "machine-0", ipConfiguration),
387
inst := s.getInstance(c)
388
okSender := mocks.NewSender()
389
okSender.EmitContent("{}")
390
nsgSender := networkSecurityGroupSender(nil)
391
s.sender = azuretesting.Senders{nsgSender, okSender, okSender}
393
err := inst.OpenPorts("0", []jujunetwork.PortRange{{
402
c.Assert(err, jc.ErrorIsNil)
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,
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,
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(),
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),
453
s.networkInterfaces = []network.Interface{
454
makeNetworkInterface("nic-0", "machine-0", ipConfiguration),
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,
470
s.sender = azuretesting.Senders{nsgSender, okSender, okSender}
472
err := inst.OpenPorts("0", []jujunetwork.PortRange{{
481
c.Assert(err, jc.ErrorIsNil)
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,
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")
508
var internalSecurityGroupPath = path.Join(
509
"/subscriptions", fakeSubscriptionId,
510
"resourceGroups", "juju-testenv-model-"+testing.ModelTag.Id(),
511
"providers/Microsoft.Network/networkSecurityGroups/juju-internal",
514
func securityRulePath(ruleName string) string {
515
return path.Join(internalSecurityGroupPath, "securityRules", ruleName)