github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/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  	stdcontext "context"
     8  	"net/http"
     9  	"path"
    10  
    11  	"github.com/Azure/azure-sdk-for-go/sdk/azcore"
    12  	"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
    13  	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v2"
    14  	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork"
    15  	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources"
    16  	jc "github.com/juju/testing/checkers"
    17  	gc "gopkg.in/check.v1"
    18  
    19  	"github.com/juju/juju/core/instance"
    20  	corenetwork "github.com/juju/juju/core/network"
    21  	"github.com/juju/juju/core/network/firewall"
    22  	"github.com/juju/juju/core/status"
    23  	"github.com/juju/juju/environs"
    24  	"github.com/juju/juju/environs/context"
    25  	"github.com/juju/juju/environs/instances"
    26  	"github.com/juju/juju/provider/azure"
    27  	"github.com/juju/juju/provider/azure/internal/azuretesting"
    28  	"github.com/juju/juju/testing"
    29  )
    30  
    31  type instanceSuite struct {
    32  	testing.BaseSuite
    33  
    34  	provider          environs.EnvironProvider
    35  	requests          []*http.Request
    36  	sender            azuretesting.Senders
    37  	env               environs.Environ
    38  	deployments       []*armresources.DeploymentExtended
    39  	vms               []*armcompute.VirtualMachine
    40  	networkInterfaces []*armnetwork.Interface
    41  	publicIPAddresses []*armnetwork.PublicIPAddress
    42  
    43  	callCtx             *context.CloudCallContext
    44  	invalidteCredential bool
    45  }
    46  
    47  var _ = gc.Suite(&instanceSuite{})
    48  
    49  func (s *instanceSuite) SetUpTest(c *gc.C) {
    50  	s.BaseSuite.SetUpTest(c)
    51  	s.provider = newProvider(c, azure.ProviderConfig{
    52  		Sender:           &s.sender,
    53  		RequestInspector: &azuretesting.RequestRecorderPolicy{Requests: &s.requests},
    54  		CreateTokenCredential: func(appId, appPassword, tenantID string, opts azcore.ClientOptions) (azcore.TokenCredential, error) {
    55  			return &azuretesting.FakeCredential{}, nil
    56  		},
    57  	})
    58  	s.env = openEnviron(c, s.provider, &s.sender)
    59  	s.sender = nil
    60  	s.requests = nil
    61  	s.networkInterfaces = []*armnetwork.Interface{
    62  		makeNetworkInterface("nic-0", "machine-0"),
    63  	}
    64  	s.publicIPAddresses = nil
    65  	s.deployments = []*armresources.DeploymentExtended{
    66  		makeDeployment("machine-0", armresources.ProvisioningStateSucceeded),
    67  		makeDeployment("machine-1", armresources.ProvisioningStateCreating),
    68  	}
    69  	s.vms = []*armcompute.VirtualMachine{{
    70  		Name: to.Ptr("machine-0"),
    71  		Tags: map[string]*string{
    72  			"juju-controller-uuid": to.Ptr(testing.ControllerTag.Id()),
    73  			"juju-model-uuid":      to.Ptr(testing.ModelTag.Id()),
    74  			"juju-is-controller":   to.Ptr("true"),
    75  		},
    76  		Properties: &armcompute.VirtualMachineProperties{
    77  			ProvisioningState: to.Ptr("Succeeded")},
    78  	}}
    79  	s.callCtx = &context.CloudCallContext{
    80  		Context: stdcontext.TODO(),
    81  		InvalidateCredentialFunc: func(string) error {
    82  			s.invalidteCredential = true
    83  			return nil
    84  		},
    85  	}
    86  }
    87  
    88  func makeDeployment(name string, provisioningState armresources.ProvisioningState) *armresources.DeploymentExtended {
    89  	dependsOn := []*armresources.BasicDependency{{
    90  		ResourceType: to.Ptr("Microsoft.Compute/availabilitySets"),
    91  		ResourceName: to.Ptr("mysql"),
    92  	}}
    93  	dependencies := []*armresources.Dependency{{
    94  		ResourceType: to.Ptr("Microsoft.Compute/virtualMachines"),
    95  		DependsOn:    dependsOn,
    96  	}}
    97  	return &armresources.DeploymentExtended{
    98  		Name: to.Ptr(name),
    99  		Properties: &armresources.DeploymentPropertiesExtended{
   100  			ProvisioningState: to.Ptr(provisioningState),
   101  			Dependencies:      dependencies,
   102  		},
   103  		Tags: map[string]*string{
   104  			"juju-model-uuid": to.Ptr(testing.ModelTag.Id()),
   105  		},
   106  	}
   107  }
   108  
   109  func makeNetworkInterface(nicName, vmName string, ipConfigurations ...*armnetwork.InterfaceIPConfiguration) *armnetwork.Interface {
   110  	tags := map[string]*string{"juju-machine-name": &vmName}
   111  	return &armnetwork.Interface{
   112  		Name: to.Ptr(nicName),
   113  		Tags: tags,
   114  		Properties: &armnetwork.InterfacePropertiesFormat{
   115  			IPConfigurations: ipConfigurations,
   116  			Primary:          to.Ptr(true),
   117  		},
   118  	}
   119  }
   120  
   121  func makeIPConfiguration(privateIPAddress string) *armnetwork.InterfaceIPConfiguration {
   122  	ipConfiguration := &armnetwork.InterfaceIPConfiguration{
   123  		Properties: &armnetwork.InterfaceIPConfigurationPropertiesFormat{},
   124  	}
   125  	if privateIPAddress != "" {
   126  		ipConfiguration.Properties.PrivateIPAddress = to.Ptr(privateIPAddress)
   127  	}
   128  	return ipConfiguration
   129  }
   130  
   131  func makePublicIPAddress(pipName, vmName, ipAddress string) *armnetwork.PublicIPAddress {
   132  	tags := map[string]*string{"juju-machine-name": &vmName}
   133  	pip := &armnetwork.PublicIPAddress{
   134  		Name:       to.Ptr(pipName),
   135  		Tags:       tags,
   136  		Properties: &armnetwork.PublicIPAddressPropertiesFormat{},
   137  	}
   138  	if ipAddress != "" {
   139  		pip.Properties.IPAddress = to.Ptr(ipAddress)
   140  	}
   141  	return pip
   142  }
   143  
   144  func makeSecurityGroup(rules ...*armnetwork.SecurityRule) armnetwork.SecurityGroup {
   145  	return armnetwork.SecurityGroup{
   146  		Name: to.Ptr("juju-internal-nsg"),
   147  		ID:   to.Ptr(internalSecurityGroupPath),
   148  		Properties: &armnetwork.SecurityGroupPropertiesFormat{
   149  			SecurityRules: rules,
   150  		},
   151  	}
   152  }
   153  
   154  func makeSecurityRule(name, ipAddress, ports string) *armnetwork.SecurityRule {
   155  	return &armnetwork.SecurityRule{
   156  		Name: to.Ptr(name),
   157  		Properties: &armnetwork.SecurityRulePropertiesFormat{
   158  			Protocol:                 to.Ptr(armnetwork.SecurityRuleProtocolTCP),
   159  			DestinationAddressPrefix: to.Ptr(ipAddress),
   160  			DestinationPortRange:     to.Ptr(ports),
   161  			Access:                   to.Ptr(armnetwork.SecurityRuleAccessAllow),
   162  			Priority:                 to.Ptr(int32(200)),
   163  			Direction:                to.Ptr(armnetwork.SecurityRuleDirectionInbound),
   164  		},
   165  	}
   166  }
   167  
   168  func (s *instanceSuite) getInstance(c *gc.C, instID instance.Id) instances.Instance {
   169  	instances := s.getInstances(c, instID)
   170  	c.Assert(instances, gc.HasLen, 1)
   171  	return instances[0]
   172  }
   173  
   174  func (s *instanceSuite) getInstances(c *gc.C, ids ...instance.Id) []instances.Instance {
   175  	s.sender = s.getInstancesSender()
   176  	instances, err := s.env.Instances(s.callCtx, ids)
   177  	c.Assert(err, jc.ErrorIsNil)
   178  	s.sender = azuretesting.Senders{}
   179  	s.requests = nil
   180  	return instances
   181  }
   182  
   183  func (s *instanceSuite) getInstancesSender() azuretesting.Senders {
   184  	deploymentsSender := azuretesting.NewSenderWithValue(&armresources.DeploymentListResult{
   185  		Value: s.deployments,
   186  	})
   187  	deploymentsSender.PathPattern = ".*/deployments"
   188  	vmSender := azuretesting.NewSenderWithValue(&armcompute.VirtualMachineListResult{
   189  		Value: s.vms,
   190  	})
   191  	vmSender.PathPattern = ".*/virtualMachines"
   192  	nicsSender := azuretesting.NewSenderWithValue(&armnetwork.InterfaceListResult{
   193  		Value: s.networkInterfaces,
   194  	})
   195  	nicsSender.PathPattern = ".*/networkInterfaces"
   196  	pipsSender := azuretesting.NewSenderWithValue(&armnetwork.PublicIPAddressListResult{
   197  		Value: s.publicIPAddresses,
   198  	})
   199  	pipsSender.PathPattern = ".*/publicIPAddresses"
   200  	return azuretesting.Senders{deploymentsSender, vmSender, nicsSender, pipsSender}
   201  }
   202  
   203  func networkSecurityGroupSender(rules []*armnetwork.SecurityRule) *azuretesting.MockSender {
   204  	nsgSender := azuretesting.NewSenderWithValue(&armnetwork.SecurityGroup{
   205  		Properties: &armnetwork.SecurityGroupPropertiesFormat{
   206  			SecurityRules: rules,
   207  		},
   208  	})
   209  	nsgSender.PathPattern = ".*/networkSecurityGroups/juju-internal-nsg"
   210  	return nsgSender
   211  }
   212  
   213  func (s *instanceSuite) TestInstanceStatus(c *gc.C) {
   214  	inst := s.getInstance(c, "machine-0")
   215  	assertInstanceStatus(c, inst.Status(s.callCtx), status.Running, "")
   216  }
   217  
   218  func (s *instanceSuite) TestInstanceStatusDeploying(c *gc.C) {
   219  	s.deployments[1].Properties.ProvisioningState = to.Ptr(armresources.ProvisioningStateCreating)
   220  	inst := s.getInstance(c, "machine-1")
   221  	assertInstanceStatus(c, inst.Status(s.callCtx), status.Provisioning, "")
   222  }
   223  
   224  func (s *instanceSuite) TestInstanceStatusDeploymentFailed(c *gc.C) {
   225  	s.deployments[1].Properties.ProvisioningState = to.Ptr(armresources.ProvisioningStateFailed)
   226  	s.deployments[1].Properties.Error = &armresources.ErrorResponse{
   227  		Details: []*armresources.ErrorResponse{{
   228  			Message: to.Ptr("boom"),
   229  		}},
   230  	}
   231  	inst := s.getInstance(c, "machine-1")
   232  	assertInstanceStatus(c, inst.Status(s.callCtx), status.ProvisioningError, "boom")
   233  }
   234  
   235  func (s *instanceSuite) TestInstanceStatusDeploymentCanceled(c *gc.C) {
   236  	s.deployments[1].Properties.ProvisioningState = to.Ptr(armresources.ProvisioningStateCanceled)
   237  	inst := s.getInstance(c, "machine-1")
   238  	assertInstanceStatus(c, inst.Status(s.callCtx), status.ProvisioningError, "Canceled")
   239  }
   240  
   241  func (s *instanceSuite) TestInstanceStatusUnsetProvisioningState(c *gc.C) {
   242  	s.deployments[1].Properties.ProvisioningState = to.Ptr(armresources.ProvisioningStateNotSpecified)
   243  	inst := s.getInstance(c, "machine-1")
   244  	assertInstanceStatus(c, inst.Status(s.callCtx), status.Allocating, "")
   245  }
   246  
   247  func assertInstanceStatus(c *gc.C, actual instance.Status, status status.Status, message string) {
   248  	c.Assert(actual, jc.DeepEquals, instance.Status{
   249  		Status:  status,
   250  		Message: message,
   251  	})
   252  }
   253  
   254  func (s *instanceSuite) TestInstanceAddressesEmpty(c *gc.C) {
   255  	addresses, err := s.getInstance(c, "machine-0").Addresses(s.callCtx)
   256  	c.Assert(err, jc.ErrorIsNil)
   257  	c.Assert(addresses, gc.HasLen, 0)
   258  }
   259  
   260  func (s *instanceSuite) TestInstanceAddresses(c *gc.C) {
   261  	nic0IPConfigurations := []*armnetwork.InterfaceIPConfiguration{
   262  		makeIPConfiguration("10.0.0.4"),
   263  		makeIPConfiguration("10.0.0.5"),
   264  	}
   265  	nic1IPConfigurations := []*armnetwork.InterfaceIPConfiguration{
   266  		makeIPConfiguration(""),
   267  	}
   268  	s.networkInterfaces = []*armnetwork.Interface{
   269  		makeNetworkInterface("nic-0", "machine-0", nic0IPConfigurations...),
   270  		makeNetworkInterface("nic-1", "machine-0", nic1IPConfigurations...),
   271  		makeNetworkInterface("nic-2", "machine-0"),
   272  		// unrelated NIC
   273  		makeNetworkInterface("nic-3", "machine-1"),
   274  	}
   275  	s.publicIPAddresses = []*armnetwork.PublicIPAddress{
   276  		makePublicIPAddress("pip-0", "machine-0", "1.2.3.4"),
   277  		makePublicIPAddress("pip-1", "machine-0", "1.2.3.5"),
   278  		// unrelated PIP
   279  		makePublicIPAddress("pip-2", "machine-1", "1.2.3.6"),
   280  	}
   281  	addresses, err := s.getInstance(c, "machine-0").Addresses(s.callCtx)
   282  	c.Assert(err, jc.ErrorIsNil)
   283  	c.Assert(addresses, jc.DeepEquals, corenetwork.NewMachineAddresses([]string{
   284  		"10.0.0.4", "10.0.0.5", "1.2.3.4", "1.2.3.5",
   285  	}).AsProviderAddresses())
   286  }
   287  
   288  func (s *instanceSuite) TestMultipleInstanceAddresses(c *gc.C) {
   289  	nic0IPConfiguration := makeIPConfiguration("10.0.0.4")
   290  	nic1IPConfiguration := makeIPConfiguration("10.0.0.5")
   291  	s.networkInterfaces = []*armnetwork.Interface{
   292  		makeNetworkInterface("nic-0", "machine-0", nic0IPConfiguration),
   293  		makeNetworkInterface("nic-1", "machine-1", nic1IPConfiguration),
   294  	}
   295  	s.publicIPAddresses = []*armnetwork.PublicIPAddress{
   296  		makePublicIPAddress("pip-0", "machine-0", "1.2.3.4"),
   297  		makePublicIPAddress("pip-1", "machine-1", "1.2.3.5"),
   298  	}
   299  	instances := s.getInstances(c, "machine-0", "machine-1")
   300  	c.Assert(instances, gc.HasLen, 2)
   301  
   302  	inst0Addresses, err := instances[0].Addresses(s.callCtx)
   303  	c.Assert(err, jc.ErrorIsNil)
   304  	c.Assert(inst0Addresses, jc.DeepEquals, corenetwork.NewMachineAddresses([]string{
   305  		"10.0.0.4", "1.2.3.4",
   306  	}).AsProviderAddresses())
   307  
   308  	inst1Addresses, err := instances[1].Addresses(s.callCtx)
   309  	c.Assert(err, jc.ErrorIsNil)
   310  	c.Assert(inst1Addresses, jc.DeepEquals, corenetwork.NewMachineAddresses([]string{
   311  		"10.0.0.5", "1.2.3.5",
   312  	}).AsProviderAddresses())
   313  }
   314  
   315  func (s *instanceSuite) TestIngressRulesEmpty(c *gc.C) {
   316  	inst := s.getInstance(c, "machine-0")
   317  	fwInst, ok := inst.(instances.InstanceFirewaller)
   318  	c.Assert(ok, gc.Equals, true)
   319  	nsgSender := networkSecurityGroupSender(nil)
   320  	s.sender = azuretesting.Senders{nsgSender}
   321  	rules, err := fwInst.IngressRules(s.callCtx, "0")
   322  	c.Assert(err, jc.ErrorIsNil)
   323  	c.Assert(rules, gc.HasLen, 0)
   324  }
   325  
   326  func (s *instanceSuite) setupSecurityGroupRules(nsgRules ...*armnetwork.SecurityRule) *azuretesting.Senders {
   327  	nsg := &armnetwork.SecurityGroup{
   328  		ID:   &internalSecurityGroupPath,
   329  		Name: to.Ptr("juju-internal-nsg"),
   330  		Properties: &armnetwork.SecurityGroupPropertiesFormat{
   331  			SecurityRules: nsgRules,
   332  		},
   333  	}
   334  	nic0IPConfigurations := []*armnetwork.InterfaceIPConfiguration{
   335  		makeIPConfiguration("10.0.0.4"),
   336  	}
   337  	nic0IPConfigurations[0].Properties.Primary = to.Ptr(true)
   338  	nic0IPConfigurations[0].Properties.Subnet = &armnetwork.Subnet{
   339  		ID: &internalSubnetPath,
   340  		Properties: &armnetwork.SubnetPropertiesFormat{
   341  			NetworkSecurityGroup: nsg,
   342  		},
   343  	}
   344  	s.networkInterfaces = []*armnetwork.Interface{
   345  		makeNetworkInterface("nic-0", "machine-0", nic0IPConfigurations...),
   346  		makeNetworkInterface("nic-2", "machine-0"),
   347  		// unrelated NIC
   348  		makeNetworkInterface("nic-3", "machine-1"),
   349  	}
   350  	return &azuretesting.Senders{
   351  		makeSender(internalSubnetPath, nic0IPConfigurations[0].Properties.Subnet), // GET: subnets to get security group
   352  		networkSecurityGroupSender(nsgRules),
   353  	}
   354  }
   355  
   356  func (s *instanceSuite) TestIngressRules(c *gc.C) {
   357  	nsgRules := []*armnetwork.SecurityRule{{
   358  		Name: to.Ptr("machine-0-xyzzy"),
   359  		Properties: &armnetwork.SecurityRulePropertiesFormat{
   360  			Protocol:             to.Ptr(armnetwork.SecurityRuleProtocolUDP),
   361  			DestinationPortRange: to.Ptr("*"),
   362  			Access:               to.Ptr(armnetwork.SecurityRuleAccessAllow),
   363  			Priority:             to.Ptr(int32(200)),
   364  			Direction:            to.Ptr(armnetwork.SecurityRuleDirectionInbound),
   365  		},
   366  	}, {
   367  		Name: to.Ptr("machine-0-tcpcp-1"),
   368  		Properties: &armnetwork.SecurityRulePropertiesFormat{
   369  			Protocol:             to.Ptr(armnetwork.SecurityRuleProtocolTCP),
   370  			DestinationPortRange: to.Ptr("1000-2000"),
   371  			SourceAddressPrefix:  to.Ptr("*"),
   372  			Access:               to.Ptr(armnetwork.SecurityRuleAccessAllow),
   373  			Priority:             to.Ptr(int32(201)),
   374  			Direction:            to.Ptr(armnetwork.SecurityRuleDirectionInbound),
   375  		},
   376  	}, {
   377  		Name: to.Ptr("machine-0-tcpcp-2"),
   378  		Properties: &armnetwork.SecurityRulePropertiesFormat{
   379  			Protocol:             to.Ptr(armnetwork.SecurityRuleProtocolTCP),
   380  			DestinationPortRange: to.Ptr("1000-2000"),
   381  			SourceAddressPrefix:  to.Ptr("192.168.1.0/24"),
   382  			Access:               to.Ptr(armnetwork.SecurityRuleAccessAllow),
   383  			Priority:             to.Ptr(int32(201)),
   384  			Direction:            to.Ptr(armnetwork.SecurityRuleDirectionInbound),
   385  		},
   386  	}, {
   387  		Name: to.Ptr("machine-0-tcpcp-3"),
   388  		Properties: &armnetwork.SecurityRulePropertiesFormat{
   389  			Protocol:             to.Ptr(armnetwork.SecurityRuleProtocolTCP),
   390  			DestinationPortRange: to.Ptr("1000-2000"),
   391  			SourceAddressPrefix:  to.Ptr("10.0.0.0/24"),
   392  			Access:               to.Ptr(armnetwork.SecurityRuleAccessAllow),
   393  			Priority:             to.Ptr(int32(201)),
   394  			Direction:            to.Ptr(armnetwork.SecurityRuleDirectionInbound),
   395  		},
   396  	}, {
   397  		Name: to.Ptr("machine-0-http"),
   398  		Properties: &armnetwork.SecurityRulePropertiesFormat{
   399  			Protocol:             to.Ptr(armnetwork.SecurityRuleProtocolAsterisk),
   400  			DestinationPortRange: to.Ptr("80"),
   401  			Access:               to.Ptr(armnetwork.SecurityRuleAccessAllow),
   402  			Priority:             to.Ptr(int32(202)),
   403  			Direction:            to.Ptr(armnetwork.SecurityRuleDirectionInbound),
   404  		},
   405  	}, {
   406  		Name: to.Ptr("machine-00-ignored"),
   407  		Properties: &armnetwork.SecurityRulePropertiesFormat{
   408  			Protocol:             to.Ptr(armnetwork.SecurityRuleProtocolTCP),
   409  			DestinationPortRange: to.Ptr("80"),
   410  			Access:               to.Ptr(armnetwork.SecurityRuleAccessAllow),
   411  			Priority:             to.Ptr(int32(202)),
   412  			Direction:            to.Ptr(armnetwork.SecurityRuleDirectionInbound),
   413  		},
   414  	}, {
   415  		Name: to.Ptr("machine-0-ignored"),
   416  		Properties: &armnetwork.SecurityRulePropertiesFormat{
   417  			Protocol:             to.Ptr(armnetwork.SecurityRuleProtocolTCP),
   418  			DestinationPortRange: to.Ptr("80"),
   419  			Access:               to.Ptr(armnetwork.SecurityRuleAccessDeny),
   420  			Priority:             to.Ptr(int32(202)),
   421  			Direction:            to.Ptr(armnetwork.SecurityRuleDirectionInbound),
   422  		},
   423  	}, {
   424  		Name: to.Ptr("machine-0-ignored"),
   425  		Properties: &armnetwork.SecurityRulePropertiesFormat{
   426  			Protocol:             to.Ptr(armnetwork.SecurityRuleProtocolTCP),
   427  			DestinationPortRange: to.Ptr("80"),
   428  			Access:               to.Ptr(armnetwork.SecurityRuleAccessAllow),
   429  			Priority:             to.Ptr(int32(202)),
   430  			Direction:            to.Ptr(armnetwork.SecurityRuleDirectionOutbound),
   431  		},
   432  	}, {
   433  		Name: to.Ptr("machine-0-ignored"),
   434  		Properties: &armnetwork.SecurityRulePropertiesFormat{
   435  			Protocol:             to.Ptr(armnetwork.SecurityRuleProtocolTCP),
   436  			DestinationPortRange: to.Ptr("80"),
   437  			Access:               to.Ptr(armnetwork.SecurityRuleAccessAllow),
   438  			Priority:             to.Ptr(int32(199)), // internal range
   439  			Direction:            to.Ptr(armnetwork.SecurityRuleDirectionInbound),
   440  		},
   441  	}}
   442  	nsgSender := s.setupSecurityGroupRules(nsgRules...)
   443  	inst := s.getInstance(c, "machine-0")
   444  	s.sender = *nsgSender
   445  
   446  	fwInst, ok := inst.(instances.InstanceFirewaller)
   447  	c.Assert(ok, gc.Equals, true)
   448  
   449  	rules, err := fwInst.IngressRules(s.callCtx, "0")
   450  	c.Assert(err, jc.ErrorIsNil)
   451  	c.Assert(rules, jc.DeepEquals, firewall.IngressRules{
   452  		firewall.NewIngressRule(corenetwork.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR),
   453  		firewall.NewIngressRule(corenetwork.MustParsePortRange("1000-2000/tcp"), firewall.AllNetworksIPV4CIDR, "192.168.1.0/24", "10.0.0.0/24"),
   454  		firewall.NewIngressRule(corenetwork.MustParsePortRange("1-65535/udp"), firewall.AllNetworksIPV4CIDR),
   455  		firewall.NewIngressRule(corenetwork.MustParsePortRange("80/udp"), firewall.AllNetworksIPV4CIDR),
   456  	})
   457  }
   458  
   459  func (s *instanceSuite) TestInstanceClosePorts(c *gc.C) {
   460  	nsgSender := s.setupSecurityGroupRules()
   461  	inst := s.getInstance(c, "machine-0")
   462  	fwInst, ok := inst.(instances.InstanceFirewaller)
   463  	c.Assert(ok, gc.Equals, true)
   464  
   465  	sender := &azuretesting.MockSender{}
   466  	notFoundSender := &azuretesting.MockSender{}
   467  	notFoundSender.AppendAndRepeatResponse(azuretesting.NewResponseWithStatus(
   468  		"rule not found", http.StatusNotFound,
   469  	), 2)
   470  	s.sender = azuretesting.Senders{nsgSender, sender, notFoundSender, notFoundSender, notFoundSender}
   471  
   472  	err := fwInst.ClosePorts(s.callCtx, "0", firewall.IngressRules{
   473  		firewall.NewIngressRule(corenetwork.MustParsePortRange("1000/tcp")),
   474  		firewall.NewIngressRule(corenetwork.MustParsePortRange("1000-2000/udp")),
   475  		firewall.NewIngressRule(corenetwork.MustParsePortRange("1000-2000/udp"), "192.168.1.0/24", "10.0.0.0/24"),
   476  	})
   477  	c.Assert(err, jc.ErrorIsNil)
   478  
   479  	c.Assert(s.requests, gc.HasLen, 5)
   480  	c.Assert(s.requests[0].Method, gc.Equals, "GET")
   481  	c.Assert(s.requests[0].URL.Path, gc.Equals, internalSubnetPath)
   482  	c.Assert(s.requests[1].Method, gc.Equals, "DELETE")
   483  	c.Assert(s.requests[1].URL.Path, gc.Equals, securityRulePath("machine-0-tcp-1000"))
   484  	c.Assert(s.requests[2].Method, gc.Equals, "DELETE")
   485  	c.Assert(s.requests[2].URL.Path, gc.Equals, securityRulePath("machine-0-udp-1000-2000"))
   486  	c.Assert(s.requests[3].Method, gc.Equals, "DELETE")
   487  	c.Assert(s.requests[3].URL.Path, gc.Equals, securityRulePath("machine-0-udp-1000-2000-cidr-10-0-0-0-24"))
   488  	c.Assert(s.requests[4].Method, gc.Equals, "DELETE")
   489  	c.Assert(s.requests[4].URL.Path, gc.Equals, securityRulePath("machine-0-udp-1000-2000-cidr-192-168-1-0-24"))
   490  }
   491  
   492  func (s *instanceSuite) TestInstanceOpenPorts(c *gc.C) {
   493  	nsgSender := s.setupSecurityGroupRules()
   494  	inst := s.getInstance(c, "machine-0")
   495  	fwInst, ok := inst.(instances.InstanceFirewaller)
   496  	c.Assert(ok, gc.Equals, true)
   497  
   498  	okSender := &azuretesting.MockSender{}
   499  	okSender.AppendResponse(azuretesting.NewResponseWithContent("{}"))
   500  	s.sender = azuretesting.Senders{nsgSender, okSender, okSender, okSender, okSender}
   501  
   502  	err := fwInst.OpenPorts(s.callCtx, "0", firewall.IngressRules{
   503  		firewall.NewIngressRule(corenetwork.MustParsePortRange("1000/tcp")),
   504  		firewall.NewIngressRule(corenetwork.MustParsePortRange("1000-2000/udp")),
   505  		firewall.NewIngressRule(corenetwork.MustParsePortRange("1000-2000/tcp"), "192.168.1.0/24", "10.0.0.0/24"),
   506  	})
   507  	c.Assert(err, jc.ErrorIsNil)
   508  
   509  	c.Assert(s.requests, gc.HasLen, 5)
   510  	c.Assert(s.requests[0].Method, gc.Equals, "GET")
   511  	c.Assert(s.requests[0].URL.Path, gc.Equals, internalSubnetPath)
   512  	c.Assert(s.requests[1].Method, gc.Equals, "PUT")
   513  	c.Assert(s.requests[1].URL.Path, gc.Equals, securityRulePath("machine-0-tcp-1000"))
   514  	assertRequestBody(c, s.requests[1], &armnetwork.SecurityRule{
   515  		Properties: &armnetwork.SecurityRulePropertiesFormat{
   516  			Description:              to.Ptr("1000/tcp from *"),
   517  			Protocol:                 to.Ptr(armnetwork.SecurityRuleProtocolTCP),
   518  			SourcePortRange:          to.Ptr("*"),
   519  			SourceAddressPrefix:      to.Ptr("*"),
   520  			DestinationPortRange:     to.Ptr("1000"),
   521  			DestinationAddressPrefix: to.Ptr("10.0.0.4"),
   522  			Access:                   to.Ptr(armnetwork.SecurityRuleAccessAllow),
   523  			Priority:                 to.Ptr(int32(200)),
   524  			Direction:                to.Ptr(armnetwork.SecurityRuleDirectionInbound),
   525  		},
   526  	})
   527  	c.Assert(s.requests[2].Method, gc.Equals, "PUT")
   528  	c.Assert(s.requests[2].URL.Path, gc.Equals, securityRulePath("machine-0-udp-1000-2000"))
   529  	assertRequestBody(c, s.requests[2], &armnetwork.SecurityRule{
   530  		Properties: &armnetwork.SecurityRulePropertiesFormat{
   531  			Description:              to.Ptr("1000-2000/udp from *"),
   532  			Protocol:                 to.Ptr(armnetwork.SecurityRuleProtocolUDP),
   533  			SourcePortRange:          to.Ptr("*"),
   534  			SourceAddressPrefix:      to.Ptr("*"),
   535  			DestinationPortRange:     to.Ptr("1000-2000"),
   536  			DestinationAddressPrefix: to.Ptr("10.0.0.4"),
   537  			Access:                   to.Ptr(armnetwork.SecurityRuleAccessAllow),
   538  			Priority:                 to.Ptr(int32(201)),
   539  			Direction:                to.Ptr(armnetwork.SecurityRuleDirectionInbound),
   540  		},
   541  	})
   542  	c.Assert(s.requests[3].Method, gc.Equals, "PUT")
   543  	c.Assert(s.requests[3].URL.Path, gc.Equals, securityRulePath("machine-0-tcp-1000-2000-cidr-10-0-0-0-24"))
   544  	assertRequestBody(c, s.requests[3], &armnetwork.SecurityRule{
   545  		Properties: &armnetwork.SecurityRulePropertiesFormat{
   546  			Description:              to.Ptr("1000-2000/tcp from 10.0.0.0/24"),
   547  			Protocol:                 to.Ptr(armnetwork.SecurityRuleProtocolTCP),
   548  			SourcePortRange:          to.Ptr("*"),
   549  			SourceAddressPrefix:      to.Ptr("10.0.0.0/24"),
   550  			DestinationPortRange:     to.Ptr("1000-2000"),
   551  			DestinationAddressPrefix: to.Ptr("10.0.0.4"),
   552  			Access:                   to.Ptr(armnetwork.SecurityRuleAccessAllow),
   553  			Priority:                 to.Ptr(int32(202)),
   554  			Direction:                to.Ptr(armnetwork.SecurityRuleDirectionInbound),
   555  		},
   556  	})
   557  	c.Assert(s.requests[4].Method, gc.Equals, "PUT")
   558  	c.Assert(s.requests[4].URL.Path, gc.Equals, securityRulePath("machine-0-tcp-1000-2000-cidr-192-168-1-0-24"))
   559  	assertRequestBody(c, s.requests[4], &armnetwork.SecurityRule{
   560  		Properties: &armnetwork.SecurityRulePropertiesFormat{
   561  			Description:              to.Ptr("1000-2000/tcp from 192.168.1.0/24"),
   562  			Protocol:                 to.Ptr(armnetwork.SecurityRuleProtocolTCP),
   563  			SourcePortRange:          to.Ptr("*"),
   564  			SourceAddressPrefix:      to.Ptr("192.168.1.0/24"),
   565  			DestinationPortRange:     to.Ptr("1000-2000"),
   566  			DestinationAddressPrefix: to.Ptr("10.0.0.4"),
   567  			Access:                   to.Ptr(armnetwork.SecurityRuleAccessAllow),
   568  			Priority:                 to.Ptr(int32(203)),
   569  			Direction:                to.Ptr(armnetwork.SecurityRuleDirectionInbound),
   570  		},
   571  	})
   572  }
   573  
   574  func (s *instanceSuite) TestInstanceOpenPortsAlreadyOpen(c *gc.C) {
   575  	nsgRule := &armnetwork.SecurityRule{
   576  		Name: to.Ptr("machine-0-tcp-1000"),
   577  		Properties: &armnetwork.SecurityRulePropertiesFormat{
   578  			Protocol:             to.Ptr(armnetwork.SecurityRuleProtocolAsterisk),
   579  			DestinationPortRange: to.Ptr("1000"),
   580  			Access:               to.Ptr(armnetwork.SecurityRuleAccessAllow),
   581  			Priority:             to.Ptr(int32(202)),
   582  			Direction:            to.Ptr(armnetwork.SecurityRuleDirectionInbound),
   583  		},
   584  	}
   585  	nsgSender := s.setupSecurityGroupRules(nsgRule)
   586  	inst := s.getInstance(c, "machine-0")
   587  	fwInst, ok := inst.(instances.InstanceFirewaller)
   588  	c.Assert(ok, gc.Equals, true)
   589  
   590  	okSender := &azuretesting.MockSender{}
   591  	okSender.AppendResponse(azuretesting.NewResponseWithContent("{}"))
   592  	s.sender = azuretesting.Senders{nsgSender, okSender, okSender}
   593  
   594  	err := fwInst.OpenPorts(s.callCtx, "0", firewall.IngressRules{
   595  		firewall.NewIngressRule(corenetwork.MustParsePortRange("1000/tcp")),
   596  		firewall.NewIngressRule(corenetwork.MustParsePortRange("1000-2000/udp")),
   597  	})
   598  	c.Assert(err, jc.ErrorIsNil)
   599  
   600  	c.Assert(s.requests, gc.HasLen, 2)
   601  	c.Assert(s.requests[0].Method, gc.Equals, "GET")
   602  	c.Assert(s.requests[0].URL.Path, gc.Equals, internalSubnetPath)
   603  	c.Assert(s.requests[1].Method, gc.Equals, "PUT")
   604  	c.Assert(s.requests[1].URL.Path, gc.Equals, securityRulePath("machine-0-udp-1000-2000"))
   605  	assertRequestBody(c, s.requests[1], &armnetwork.SecurityRule{
   606  		Properties: &armnetwork.SecurityRulePropertiesFormat{
   607  			Description:              to.Ptr("1000-2000/udp from *"),
   608  			Protocol:                 to.Ptr(armnetwork.SecurityRuleProtocolUDP),
   609  			SourcePortRange:          to.Ptr("*"),
   610  			SourceAddressPrefix:      to.Ptr("*"),
   611  			DestinationPortRange:     to.Ptr("1000-2000"),
   612  			DestinationAddressPrefix: to.Ptr("10.0.0.4"),
   613  			Access:                   to.Ptr(armnetwork.SecurityRuleAccessAllow),
   614  			Priority:                 to.Ptr(int32(200)),
   615  			Direction:                to.Ptr(armnetwork.SecurityRuleDirectionInbound),
   616  		},
   617  	})
   618  }
   619  
   620  func (s *instanceSuite) TestInstanceOpenPortsNoInternalAddress(c *gc.C) {
   621  	s.networkInterfaces = []*armnetwork.Interface{
   622  		makeNetworkInterface("nic-0", "machine-0"),
   623  	}
   624  	inst := s.getInstance(c, "machine-0")
   625  	fwInst, ok := inst.(instances.InstanceFirewaller)
   626  	c.Assert(ok, gc.Equals, true)
   627  	err := fwInst.OpenPorts(s.callCtx, "0", nil)
   628  	c.Assert(err, jc.ErrorIsNil)
   629  	c.Assert(s.requests, gc.HasLen, 0)
   630  }
   631  
   632  func (s *instanceSuite) TestAllInstances(c *gc.C) {
   633  	s.sender = s.getInstancesSender()
   634  	instances, err := s.env.AllInstances(s.callCtx)
   635  	c.Assert(err, jc.ErrorIsNil)
   636  	c.Assert(instances, gc.HasLen, 2)
   637  	c.Assert(instances[0].Id(), gc.Equals, instance.Id("machine-0"))
   638  	c.Assert(instances[1].Id(), gc.Equals, instance.Id("machine-1"))
   639  }
   640  
   641  func (s *instanceSuite) TestAllRunningInstances(c *gc.C) {
   642  	s.sender = s.getInstancesSender()
   643  	instances, err := s.env.AllRunningInstances(s.callCtx)
   644  	c.Assert(err, jc.ErrorIsNil)
   645  	c.Assert(instances, gc.HasLen, 2)
   646  	c.Assert(instances[0].Id(), gc.Equals, instance.Id("machine-0"))
   647  	c.Assert(instances[1].Id(), gc.Equals, instance.Id("machine-1"))
   648  }
   649  
   650  func (s *instanceSuite) TestControllerInstancesSomePending(c *gc.C) {
   651  	*((s.deployments[1].Properties.Dependencies)[0].DependsOn)[0].ResourceName = "juju-controller"
   652  	s.sender = s.getInstancesSender()
   653  	ids, err := s.env.ControllerInstances(s.callCtx, testing.ControllerTag.Id())
   654  	c.Assert(err, jc.ErrorIsNil)
   655  	c.Assert(ids, gc.HasLen, 2)
   656  	c.Assert(ids[0], gc.Equals, instance.Id("machine-0"))
   657  	c.Assert(ids[1], gc.Equals, instance.Id("machine-1"))
   658  }
   659  
   660  func (s *instanceSuite) TestControllerInstances(c *gc.C) {
   661  	s.sender = s.getInstancesSender()
   662  	ids, err := s.env.ControllerInstances(s.callCtx, testing.ControllerTag.Id())
   663  	c.Assert(err, jc.ErrorIsNil)
   664  	c.Assert(ids, gc.HasLen, 1)
   665  	c.Assert(ids[0], gc.Equals, instance.Id("machine-0"))
   666  }
   667  
   668  var internalSecurityGroupPath = path.Join(
   669  	"/subscriptions", fakeManagedSubscriptionId,
   670  	"resourceGroups", "juju-testmodel-"+testing.ModelTag.Id()[:8],
   671  	"providers/Microsoft.Network/networkSecurityGroups/juju-internal-nsg",
   672  )
   673  
   674  var internalSubnetPath = path.Join(
   675  	"/subscriptions", fakeManagedSubscriptionId,
   676  	"resourceGroups/juju-testmodel-model-deadbeef-0bad-400d-8000-4b1d0d06f00d",
   677  	"providers/Microsoft.Network/virtualNetworks/juju-internal-network/subnets/juju-internal-subnet",
   678  )
   679  
   680  func securityRulePath(ruleName string) string {
   681  	return path.Join(internalSecurityGroupPath, "securityRules", ruleName)
   682  }