github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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/services/network/mgmt/2018-08-01/network"
    11  	"github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2018-05-01/resources"
    12  	"github.com/Azure/go-autorest/autorest/mocks"
    13  	"github.com/Azure/go-autorest/autorest/to"
    14  	jc "github.com/juju/testing/checkers"
    15  	gc "gopkg.in/check.v1"
    16  
    17  	"github.com/juju/juju/core/instance"
    18  	"github.com/juju/juju/core/status"
    19  	"github.com/juju/juju/environs"
    20  	"github.com/juju/juju/environs/context"
    21  	"github.com/juju/juju/environs/instances"
    22  	jujunetwork "github.com/juju/juju/network"
    23  	"github.com/juju/juju/provider/azure"
    24  	"github.com/juju/juju/provider/azure/internal/azuretesting"
    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  	callCtx             *context.CloudCallContext
    40  	invalidteCredential bool
    41  }
    42  
    43  var _ = gc.Suite(&instanceSuite{})
    44  
    45  func (s *instanceSuite) SetUpTest(c *gc.C) {
    46  	s.BaseSuite.SetUpTest(c)
    47  	s.provider = newProvider(c, azure.ProviderConfig{
    48  		Sender:                     &s.sender,
    49  		RequestInspector:           azuretesting.RequestRecorder(&s.requests),
    50  		RandomWindowsAdminPassword: func() string { return "sorandom" },
    51  	})
    52  	s.env = openEnviron(c, s.provider, &s.sender)
    53  	azure.SetRetries(s.env)
    54  	s.sender = nil
    55  	s.requests = nil
    56  	s.networkInterfaces = []network.Interface{
    57  		makeNetworkInterface("nic-0", "machine-0"),
    58  	}
    59  	s.publicIPAddresses = nil
    60  	s.deployments = []resources.DeploymentExtended{
    61  		makeDeployment("machine-0"),
    62  		makeDeployment("machine-1"),
    63  	}
    64  	s.callCtx = &context.CloudCallContext{
    65  		InvalidateCredentialFunc: func(string) error {
    66  			s.invalidteCredential = true
    67  			return nil
    68  		},
    69  	}
    70  }
    71  
    72  func makeDeployment(name string) resources.DeploymentExtended {
    73  	dependsOn := []resources.BasicDependency{{
    74  		ResourceType: to.StringPtr("Microsoft.Compute/availabilitySets"),
    75  		ResourceName: to.StringPtr("mysql"),
    76  	}}
    77  	dependencies := []resources.Dependency{{
    78  		ResourceType: to.StringPtr("Microsoft.Compute/virtualMachines"),
    79  		DependsOn:    &dependsOn,
    80  	}}
    81  	return resources.DeploymentExtended{
    82  		Name: to.StringPtr(name),
    83  		Properties: &resources.DeploymentPropertiesExtended{
    84  			ProvisioningState: to.StringPtr("Succeeded"),
    85  			Dependencies:      &dependencies,
    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  		InterfacePropertiesFormat: &network.InterfacePropertiesFormat{
    96  			IPConfigurations: &ipConfigurations,
    97  		},
    98  	}
    99  }
   100  
   101  func makeIPConfiguration(privateIPAddress string) network.InterfaceIPConfiguration {
   102  	ipConfiguration := network.InterfaceIPConfiguration{
   103  		InterfaceIPConfigurationPropertiesFormat: &network.InterfaceIPConfigurationPropertiesFormat{},
   104  	}
   105  	if privateIPAddress != "" {
   106  		ipConfiguration.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  		PublicIPAddressPropertiesFormat: &network.PublicIPAddressPropertiesFormat{},
   117  	}
   118  	if ipAddress != "" {
   119  		pip.IPAddress = to.StringPtr(ipAddress)
   120  	}
   121  	return pip
   122  }
   123  
   124  func makeSecurityGroup(rules ...network.SecurityRule) network.SecurityGroup {
   125  	return network.SecurityGroup{
   126  		SecurityGroupPropertiesFormat: &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  		SecurityRulePropertiesFormat: &network.SecurityRulePropertiesFormat{
   136  			Protocol:                 network.SecurityRuleProtocolTCP,
   137  			DestinationAddressPrefix: to.StringPtr(ipAddress),
   138  			DestinationPortRange:     to.StringPtr(ports),
   139  			Access:                   network.SecurityRuleAccessAllow,
   140  			Priority:                 to.Int32Ptr(200),
   141  			Direction:                network.SecurityRuleDirectionInbound,
   142  		},
   143  	}
   144  }
   145  
   146  func (s *instanceSuite) getInstance(c *gc.C) instances.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) []instances.Instance {
   153  	s.sender = s.getInstancesSender()
   154  	instances, err := s.env.Instances(s.callCtx, 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  		SecurityGroupPropertiesFormat: &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(s.callCtx), 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(s.callCtx), 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(s.callCtx), 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(s.callCtx), status.Allocating, "")
   208  }
   209  
   210  func assertInstanceStatus(c *gc.C, actual instance.Status, status status.Status, message string) {
   211  	c.Assert(actual, jc.DeepEquals, instance.Status{
   212  		Status:  status,
   213  		Message: message,
   214  	})
   215  }
   216  
   217  func (s *instanceSuite) TestInstanceAddressesEmpty(c *gc.C) {
   218  	addresses, err := s.getInstance(c).Addresses(s.callCtx)
   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(s.callCtx)
   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(s.callCtx)
   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(s.callCtx)
   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) TestIngressRulesEmpty(c *gc.C) {
   279  	inst := s.getInstance(c)
   280  	fwInst, ok := inst.(instances.InstanceFirewaller)
   281  	c.Assert(ok, gc.Equals, true)
   282  	nsgSender := networkSecurityGroupSender(nil)
   283  	s.sender = azuretesting.Senders{nsgSender}
   284  	rules, err := fwInst.IngressRules(s.callCtx, "0")
   285  	c.Assert(err, jc.ErrorIsNil)
   286  	c.Assert(rules, gc.HasLen, 0)
   287  }
   288  
   289  func (s *instanceSuite) TestIngressRules(c *gc.C) {
   290  	inst := s.getInstance(c)
   291  	nsgSender := networkSecurityGroupSender([]network.SecurityRule{{
   292  		Name: to.StringPtr("machine-0-xyzzy"),
   293  		SecurityRulePropertiesFormat: &network.SecurityRulePropertiesFormat{
   294  			Protocol:             network.SecurityRuleProtocolUDP,
   295  			DestinationPortRange: to.StringPtr("*"),
   296  			Access:               network.SecurityRuleAccessAllow,
   297  			Priority:             to.Int32Ptr(200),
   298  			Direction:            network.SecurityRuleDirectionInbound,
   299  		},
   300  	}, {
   301  		Name: to.StringPtr("machine-0-tcpcp-1"),
   302  		SecurityRulePropertiesFormat: &network.SecurityRulePropertiesFormat{
   303  			Protocol:             network.SecurityRuleProtocolTCP,
   304  			DestinationPortRange: to.StringPtr("1000-2000"),
   305  			SourceAddressPrefix:  to.StringPtr("*"),
   306  			Access:               network.SecurityRuleAccessAllow,
   307  			Priority:             to.Int32Ptr(201),
   308  			Direction:            network.SecurityRuleDirectionInbound,
   309  		},
   310  	}, {
   311  		Name: to.StringPtr("machine-0-tcpcp-2"),
   312  		SecurityRulePropertiesFormat: &network.SecurityRulePropertiesFormat{
   313  			Protocol:             network.SecurityRuleProtocolTCP,
   314  			DestinationPortRange: to.StringPtr("1000-2000"),
   315  			SourceAddressPrefix:  to.StringPtr("192.168.1.0/24"),
   316  			Access:               network.SecurityRuleAccessAllow,
   317  			Priority:             to.Int32Ptr(201),
   318  			Direction:            network.SecurityRuleDirectionInbound,
   319  		},
   320  	}, {
   321  		Name: to.StringPtr("machine-0-tcpcp-3"),
   322  		SecurityRulePropertiesFormat: &network.SecurityRulePropertiesFormat{
   323  			Protocol:             network.SecurityRuleProtocolTCP,
   324  			DestinationPortRange: to.StringPtr("1000-2000"),
   325  			SourceAddressPrefix:  to.StringPtr("10.0.0.0/24"),
   326  			Access:               network.SecurityRuleAccessAllow,
   327  			Priority:             to.Int32Ptr(201),
   328  			Direction:            network.SecurityRuleDirectionInbound,
   329  		},
   330  	}, {
   331  		Name: to.StringPtr("machine-0-http"),
   332  		SecurityRulePropertiesFormat: &network.SecurityRulePropertiesFormat{
   333  			Protocol:             network.SecurityRuleProtocolAsterisk,
   334  			DestinationPortRange: to.StringPtr("80"),
   335  			Access:               network.SecurityRuleAccessAllow,
   336  			Priority:             to.Int32Ptr(202),
   337  			Direction:            network.SecurityRuleDirectionInbound,
   338  		},
   339  	}, {
   340  		Name: to.StringPtr("machine-00-ignored"),
   341  		SecurityRulePropertiesFormat: &network.SecurityRulePropertiesFormat{
   342  			Protocol:             network.SecurityRuleProtocolTCP,
   343  			DestinationPortRange: to.StringPtr("80"),
   344  			Access:               network.SecurityRuleAccessAllow,
   345  			Priority:             to.Int32Ptr(202),
   346  			Direction:            network.SecurityRuleDirectionInbound,
   347  		},
   348  	}, {
   349  		Name: to.StringPtr("machine-0-ignored"),
   350  		SecurityRulePropertiesFormat: &network.SecurityRulePropertiesFormat{
   351  			Protocol:             network.SecurityRuleProtocolTCP,
   352  			DestinationPortRange: to.StringPtr("80"),
   353  			Access:               network.SecurityRuleAccessDeny,
   354  			Priority:             to.Int32Ptr(202),
   355  			Direction:            network.SecurityRuleDirectionInbound,
   356  		},
   357  	}, {
   358  		Name: to.StringPtr("machine-0-ignored"),
   359  		SecurityRulePropertiesFormat: &network.SecurityRulePropertiesFormat{
   360  			Protocol:             network.SecurityRuleProtocolTCP,
   361  			DestinationPortRange: to.StringPtr("80"),
   362  			Access:               network.SecurityRuleAccessAllow,
   363  			Priority:             to.Int32Ptr(202),
   364  			Direction:            network.SecurityRuleDirectionOutbound,
   365  		},
   366  	}, {
   367  		Name: to.StringPtr("machine-0-ignored"),
   368  		SecurityRulePropertiesFormat: &network.SecurityRulePropertiesFormat{
   369  			Protocol:             network.SecurityRuleProtocolTCP,
   370  			DestinationPortRange: to.StringPtr("80"),
   371  			Access:               network.SecurityRuleAccessAllow,
   372  			Priority:             to.Int32Ptr(199), // internal range
   373  			Direction:            network.SecurityRuleDirectionInbound,
   374  		},
   375  	}})
   376  	s.sender = azuretesting.Senders{nsgSender}
   377  	fwInst, ok := inst.(instances.InstanceFirewaller)
   378  	c.Assert(ok, gc.Equals, true)
   379  
   380  	rules, err := fwInst.IngressRules(s.callCtx, "0")
   381  	c.Assert(err, jc.ErrorIsNil)
   382  	c.Assert(rules, jc.DeepEquals, []jujunetwork.IngressRule{
   383  		jujunetwork.MustNewIngressRule("tcp", 80, 80, "0.0.0.0/0"),
   384  		jujunetwork.MustNewIngressRule("tcp", 1000, 2000, "0.0.0.0/0", "192.168.1.0/24", "10.0.0.0/24"),
   385  		jujunetwork.MustNewIngressRule("udp", 0, 65535, "0.0.0.0/0"),
   386  		jujunetwork.MustNewIngressRule("udp", 80, 80, "0.0.0.0/0"),
   387  	})
   388  }
   389  
   390  func (s *instanceSuite) TestInstanceClosePorts(c *gc.C) {
   391  	inst := s.getInstance(c)
   392  	fwInst, ok := inst.(instances.InstanceFirewaller)
   393  	c.Assert(ok, gc.Equals, true)
   394  
   395  	sender := mocks.NewSender()
   396  	notFoundSender := mocks.NewSender()
   397  	notFoundSender.AppendAndRepeatResponse(mocks.NewResponseWithStatus(
   398  		"rule not found", http.StatusNotFound,
   399  	), 2)
   400  	s.sender = azuretesting.Senders{sender, notFoundSender, notFoundSender, notFoundSender}
   401  
   402  	err := fwInst.ClosePorts(s.callCtx, "0", []jujunetwork.IngressRule{
   403  		jujunetwork.MustNewIngressRule("tcp", 1000, 1000),
   404  		jujunetwork.MustNewIngressRule("udp", 1000, 2000),
   405  		jujunetwork.MustNewIngressRule("udp", 1000, 2000, "192.168.1.0/24", "10.0.0.0/24"),
   406  	})
   407  	c.Assert(err, jc.ErrorIsNil)
   408  
   409  	c.Assert(s.requests, gc.HasLen, 4)
   410  	c.Assert(s.requests[0].Method, gc.Equals, "DELETE")
   411  	c.Assert(s.requests[0].URL.Path, gc.Equals, securityRulePath("machine-0-tcp-1000"))
   412  	c.Assert(s.requests[1].Method, gc.Equals, "DELETE")
   413  	c.Assert(s.requests[1].URL.Path, gc.Equals, securityRulePath("machine-0-udp-1000-2000"))
   414  	c.Assert(s.requests[2].Method, gc.Equals, "DELETE")
   415  	c.Assert(s.requests[2].URL.Path, gc.Equals, securityRulePath("machine-0-udp-1000-2000-cidr-192-168-1-0-24"))
   416  	c.Assert(s.requests[3].Method, gc.Equals, "DELETE")
   417  	c.Assert(s.requests[3].URL.Path, gc.Equals, securityRulePath("machine-0-udp-1000-2000-cidr-10-0-0-0-24"))
   418  }
   419  
   420  func (s *instanceSuite) TestInstanceOpenPorts(c *gc.C) {
   421  	internalSubnetId := path.Join(
   422  		"/subscriptions", fakeSubscriptionId,
   423  		"resourceGroups/juju-testmodel-model-deadbeef-0bad-400d-8000-4b1d0d06f00d",
   424  		"providers/Microsoft.Network/virtualnetworks/juju-internal-network/subnets/juju-internal-subnet",
   425  	)
   426  	ipConfiguration := network.InterfaceIPConfiguration{
   427  		InterfaceIPConfigurationPropertiesFormat: &network.InterfaceIPConfigurationPropertiesFormat{
   428  			Primary:          to.BoolPtr(true),
   429  			PrivateIPAddress: to.StringPtr("10.0.0.4"),
   430  			Subnet: &network.Subnet{
   431  				ID: to.StringPtr(internalSubnetId),
   432  			},
   433  		},
   434  	}
   435  	s.networkInterfaces = []network.Interface{
   436  		makeNetworkInterface("nic-0", "machine-0", ipConfiguration),
   437  	}
   438  
   439  	inst := s.getInstance(c)
   440  	fwInst, ok := inst.(instances.InstanceFirewaller)
   441  	c.Assert(ok, gc.Equals, true)
   442  
   443  	okSender := mocks.NewSender()
   444  	okSender.AppendResponse(mocks.NewResponseWithContent("{}"))
   445  	nsgSender := networkSecurityGroupSender(nil)
   446  	s.sender = azuretesting.Senders{nsgSender, okSender, okSender, okSender, okSender}
   447  
   448  	err := fwInst.OpenPorts(s.callCtx, "0", []jujunetwork.IngressRule{
   449  		jujunetwork.MustNewIngressRule("tcp", 1000, 1000),
   450  		jujunetwork.MustNewIngressRule("udp", 1000, 2000),
   451  		jujunetwork.MustNewIngressRule("tcp", 1000, 2000, "192.168.1.0/24", "10.0.0.0/24"),
   452  	})
   453  	c.Assert(err, jc.ErrorIsNil)
   454  
   455  	c.Assert(s.requests, gc.HasLen, 5)
   456  	c.Assert(s.requests[0].Method, gc.Equals, "GET")
   457  	c.Assert(s.requests[0].URL.Path, gc.Equals, internalSecurityGroupPath)
   458  	c.Assert(s.requests[1].Method, gc.Equals, "PUT")
   459  	c.Assert(s.requests[1].URL.Path, gc.Equals, securityRulePath("machine-0-tcp-1000"))
   460  	assertRequestBody(c, s.requests[1], &network.SecurityRule{
   461  		SecurityRulePropertiesFormat: &network.SecurityRulePropertiesFormat{
   462  			Description:              to.StringPtr("1000/tcp from *"),
   463  			Protocol:                 network.SecurityRuleProtocolTCP,
   464  			SourcePortRange:          to.StringPtr("*"),
   465  			SourceAddressPrefix:      to.StringPtr("*"),
   466  			DestinationPortRange:     to.StringPtr("1000"),
   467  			DestinationAddressPrefix: to.StringPtr("10.0.0.4"),
   468  			Access:                   network.SecurityRuleAccessAllow,
   469  			Priority:                 to.Int32Ptr(200),
   470  			Direction:                network.SecurityRuleDirectionInbound,
   471  		},
   472  	})
   473  	c.Assert(s.requests[2].Method, gc.Equals, "PUT")
   474  	c.Assert(s.requests[2].URL.Path, gc.Equals, securityRulePath("machine-0-udp-1000-2000"))
   475  	assertRequestBody(c, s.requests[2], &network.SecurityRule{
   476  		SecurityRulePropertiesFormat: &network.SecurityRulePropertiesFormat{
   477  			Description:              to.StringPtr("1000-2000/udp from *"),
   478  			Protocol:                 network.SecurityRuleProtocolUDP,
   479  			SourcePortRange:          to.StringPtr("*"),
   480  			SourceAddressPrefix:      to.StringPtr("*"),
   481  			DestinationPortRange:     to.StringPtr("1000-2000"),
   482  			DestinationAddressPrefix: to.StringPtr("10.0.0.4"),
   483  			Access:                   network.SecurityRuleAccessAllow,
   484  			Priority:                 to.Int32Ptr(201),
   485  			Direction:                network.SecurityRuleDirectionInbound,
   486  		},
   487  	})
   488  	c.Assert(s.requests[3].Method, gc.Equals, "PUT")
   489  	c.Assert(s.requests[3].URL.Path, gc.Equals, securityRulePath("machine-0-tcp-1000-2000-cidr-192-168-1-0-24"))
   490  	assertRequestBody(c, s.requests[3], &network.SecurityRule{
   491  		SecurityRulePropertiesFormat: &network.SecurityRulePropertiesFormat{
   492  			Description:              to.StringPtr("1000-2000/tcp from 192.168.1.0/24"),
   493  			Protocol:                 network.SecurityRuleProtocolTCP,
   494  			SourcePortRange:          to.StringPtr("*"),
   495  			SourceAddressPrefix:      to.StringPtr("192.168.1.0/24"),
   496  			DestinationPortRange:     to.StringPtr("1000-2000"),
   497  			DestinationAddressPrefix: to.StringPtr("10.0.0.4"),
   498  			Access:                   network.SecurityRuleAccessAllow,
   499  			Priority:                 to.Int32Ptr(202),
   500  			Direction:                network.SecurityRuleDirectionInbound,
   501  		},
   502  	})
   503  	c.Assert(s.requests[4].Method, gc.Equals, "PUT")
   504  	c.Assert(s.requests[4].URL.Path, gc.Equals, securityRulePath("machine-0-tcp-1000-2000-cidr-10-0-0-0-24"))
   505  	assertRequestBody(c, s.requests[4], &network.SecurityRule{
   506  		SecurityRulePropertiesFormat: &network.SecurityRulePropertiesFormat{
   507  			Description:              to.StringPtr("1000-2000/tcp from 10.0.0.0/24"),
   508  			Protocol:                 network.SecurityRuleProtocolTCP,
   509  			SourcePortRange:          to.StringPtr("*"),
   510  			SourceAddressPrefix:      to.StringPtr("10.0.0.0/24"),
   511  			DestinationPortRange:     to.StringPtr("1000-2000"),
   512  			DestinationAddressPrefix: to.StringPtr("10.0.0.4"),
   513  			Access:                   network.SecurityRuleAccessAllow,
   514  			Priority:                 to.Int32Ptr(203),
   515  			Direction:                network.SecurityRuleDirectionInbound,
   516  		},
   517  	})
   518  }
   519  
   520  func (s *instanceSuite) TestInstanceOpenPortsAlreadyOpen(c *gc.C) {
   521  	internalSubnetId := path.Join(
   522  		"/subscriptions", fakeSubscriptionId,
   523  		"resourceGroups/juju-testmodel-model-deadbeef-0bad-400d-8000-4b1d0d06f00d",
   524  		"providers/Microsoft.Network/virtualnetworks/juju-internal-network/subnets/juju-internal-subnet",
   525  	)
   526  	ipConfiguration := network.InterfaceIPConfiguration{
   527  		InterfaceIPConfigurationPropertiesFormat: &network.InterfaceIPConfigurationPropertiesFormat{
   528  			Primary:          to.BoolPtr(true),
   529  			PrivateIPAddress: to.StringPtr("10.0.0.4"),
   530  			Subnet: &network.Subnet{
   531  				ID: to.StringPtr(internalSubnetId),
   532  			},
   533  		},
   534  	}
   535  	s.networkInterfaces = []network.Interface{
   536  		makeNetworkInterface("nic-0", "machine-0", ipConfiguration),
   537  	}
   538  
   539  	inst := s.getInstance(c)
   540  	fwInst, ok := inst.(instances.InstanceFirewaller)
   541  	c.Assert(ok, gc.Equals, true)
   542  
   543  	okSender := mocks.NewSender()
   544  	okSender.AppendResponse(mocks.NewResponseWithContent("{}"))
   545  	nsgSender := networkSecurityGroupSender([]network.SecurityRule{{
   546  		Name: to.StringPtr("machine-0-tcp-1000"),
   547  		SecurityRulePropertiesFormat: &network.SecurityRulePropertiesFormat{
   548  			Protocol:             network.SecurityRuleProtocolAsterisk,
   549  			DestinationPortRange: to.StringPtr("1000"),
   550  			Access:               network.SecurityRuleAccessAllow,
   551  			Priority:             to.Int32Ptr(202),
   552  			Direction:            network.SecurityRuleDirectionInbound,
   553  		},
   554  	}})
   555  	s.sender = azuretesting.Senders{nsgSender, okSender, okSender}
   556  
   557  	err := fwInst.OpenPorts(s.callCtx, "0", []jujunetwork.IngressRule{
   558  		jujunetwork.MustNewIngressRule("tcp", 1000, 1000),
   559  		jujunetwork.MustNewIngressRule("udp", 1000, 2000),
   560  	})
   561  	c.Assert(err, jc.ErrorIsNil)
   562  
   563  	c.Assert(s.requests, gc.HasLen, 2)
   564  	c.Assert(s.requests[0].Method, gc.Equals, "GET")
   565  	c.Assert(s.requests[0].URL.Path, gc.Equals, internalSecurityGroupPath)
   566  	c.Assert(s.requests[1].Method, gc.Equals, "PUT")
   567  	c.Assert(s.requests[1].URL.Path, gc.Equals, securityRulePath("machine-0-udp-1000-2000"))
   568  	assertRequestBody(c, s.requests[1], &network.SecurityRule{
   569  		SecurityRulePropertiesFormat: &network.SecurityRulePropertiesFormat{
   570  			Description:              to.StringPtr("1000-2000/udp from *"),
   571  			Protocol:                 network.SecurityRuleProtocolUDP,
   572  			SourcePortRange:          to.StringPtr("*"),
   573  			SourceAddressPrefix:      to.StringPtr("*"),
   574  			DestinationPortRange:     to.StringPtr("1000-2000"),
   575  			DestinationAddressPrefix: to.StringPtr("10.0.0.4"),
   576  			Access:                   network.SecurityRuleAccessAllow,
   577  			Priority:                 to.Int32Ptr(200),
   578  			Direction:                network.SecurityRuleDirectionInbound,
   579  		},
   580  	})
   581  }
   582  
   583  func (s *instanceSuite) TestInstanceOpenPortsNoInternalAddress(c *gc.C) {
   584  	inst := s.getInstance(c)
   585  	fwInst, ok := inst.(instances.InstanceFirewaller)
   586  	c.Assert(ok, gc.Equals, true)
   587  	err := fwInst.OpenPorts(s.callCtx, "0", nil)
   588  	c.Assert(err, gc.ErrorMatches, "internal network address not found")
   589  }
   590  
   591  func (s *instanceSuite) TestAllInstances(c *gc.C) {
   592  	s.sender = s.getInstancesSender()
   593  	instances, err := s.env.AllInstances(s.callCtx)
   594  	c.Assert(err, jc.ErrorIsNil)
   595  	c.Assert(instances, gc.HasLen, 2)
   596  	c.Assert(instances[0].Id(), gc.Equals, instance.Id("machine-0"))
   597  	c.Assert(instances[1].Id(), gc.Equals, instance.Id("machine-1"))
   598  }
   599  
   600  func (s *instanceSuite) TestControllerInstances(c *gc.C) {
   601  	*(*(*s.deployments[0].Properties.Dependencies)[0].DependsOn)[0].ResourceName = "juju-controller"
   602  	s.sender = s.getInstancesSender()
   603  	ids, err := s.env.ControllerInstances(s.callCtx, "foo")
   604  	c.Assert(err, jc.ErrorIsNil)
   605  	c.Assert(ids, gc.HasLen, 1)
   606  	c.Assert(ids[0], gc.Equals, instance.Id("machine-0"))
   607  }
   608  
   609  var internalSecurityGroupPath = path.Join(
   610  	"/subscriptions", fakeSubscriptionId,
   611  	"resourceGroups", "juju-testmodel-model-"+testing.ModelTag.Id(),
   612  	"providers/Microsoft.Network/networkSecurityGroups/juju-internal-nsg",
   613  )
   614  
   615  func securityRulePath(ruleName string) string {
   616  	return path.Join(internalSecurityGroupPath, "securityRules", ruleName)
   617  }