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