github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/provider/azure/instance_test.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package azure
     5  
     6  import (
     7  	"encoding/base64"
     8  	"fmt"
     9  	"net/http"
    10  
    11  	jc "github.com/juju/testing/checkers"
    12  	gc "gopkg.in/check.v1"
    13  	"launchpad.net/gwacl"
    14  
    15  	"github.com/juju/juju/instance"
    16  	"github.com/juju/juju/network"
    17  	"github.com/juju/juju/testing"
    18  )
    19  
    20  type instanceSuite struct {
    21  	testing.BaseSuite
    22  	env        *azureEnviron
    23  	service    *gwacl.HostedService
    24  	deployment *gwacl.Deployment
    25  	role       *gwacl.Role
    26  	instance   *azureInstance
    27  }
    28  
    29  var _ = gc.Suite(&instanceSuite{})
    30  
    31  func (s *instanceSuite) SetUpTest(c *gc.C) {
    32  	s.env = makeEnviron(c)
    33  	s.service = makeDeployment(s.env, "service-name")
    34  	s.deployment = &s.service.Deployments[0]
    35  	s.deployment.Name = "deployment-one"
    36  	s.role = &s.deployment.RoleList[0]
    37  	s.role.RoleName = "role-one"
    38  	inst, err := s.env.getInstance(s.service, s.role.RoleName)
    39  	c.Assert(err, jc.ErrorIsNil)
    40  	c.Assert(inst, gc.FitsTypeOf, &azureInstance{})
    41  	s.instance = inst.(*azureInstance)
    42  }
    43  
    44  func configSetNetwork(role *gwacl.Role) *gwacl.ConfigurationSet {
    45  	for i, configSet := range role.ConfigurationSets {
    46  		if configSet.ConfigurationSetType == gwacl.CONFIG_SET_NETWORK {
    47  			return &role.ConfigurationSets[i]
    48  		}
    49  	}
    50  	return nil
    51  }
    52  
    53  // makeHostedServiceDescriptor creates a HostedServiceDescriptor with the
    54  // given service name.
    55  func makeHostedServiceDescriptor(name string) *gwacl.HostedServiceDescriptor {
    56  	labelBase64 := base64.StdEncoding.EncodeToString([]byte("label"))
    57  	return &gwacl.HostedServiceDescriptor{ServiceName: name, Label: labelBase64}
    58  }
    59  
    60  func (*instanceSuite) TestId(c *gc.C) {
    61  	azInstance := azureInstance{instanceId: "whatever"}
    62  	c.Check(azInstance.Id(), gc.Equals, instance.Id("whatever"))
    63  }
    64  
    65  func (*instanceSuite) TestStatus(c *gc.C) {
    66  	var inst azureInstance
    67  	c.Check(inst.Status(), gc.Equals, "")
    68  	inst.roleInstance = &gwacl.RoleInstance{InstanceStatus: "anyoldthing"}
    69  	c.Check(inst.Status(), gc.Equals, "anyoldthing")
    70  }
    71  
    72  func makeInputEndpoint(port int, protocol string) gwacl.InputEndpoint {
    73  	name := fmt.Sprintf("%s%d-%d", protocol, port, port)
    74  	probe := &gwacl.LoadBalancerProbe{Port: port, Protocol: "TCP"}
    75  	if protocol == "udp" {
    76  		// We just use port 22 (SSH) for the
    77  		// probe when a UDP port is exposed.
    78  		probe.Port = 22
    79  	}
    80  	return gwacl.InputEndpoint{
    81  		LocalPort: port,
    82  		Name:      fmt.Sprintf("%s_range_%d", name, port),
    83  		LoadBalancedEndpointSetName: name,
    84  		LoadBalancerProbe:           probe,
    85  		Port:                        port,
    86  		Protocol:                    protocol,
    87  	}
    88  }
    89  
    90  func serialize(c *gc.C, object gwacl.AzureObject) []byte {
    91  	xml, err := object.Serialize()
    92  	c.Assert(err, jc.ErrorIsNil)
    93  	return []byte(xml)
    94  }
    95  
    96  func prepareDeploymentInfoResponse(
    97  	c *gc.C, dep gwacl.Deployment) []gwacl.DispatcherResponse {
    98  	return []gwacl.DispatcherResponse{
    99  		gwacl.NewDispatcherResponse(
   100  			serialize(c, &dep), http.StatusOK, nil),
   101  	}
   102  }
   103  
   104  func preparePortChangeConversation(c *gc.C, role *gwacl.Role) []gwacl.DispatcherResponse {
   105  	persistentRole := &gwacl.PersistentVMRole{
   106  		XMLNS:             gwacl.XMLNS,
   107  		RoleName:          role.RoleName,
   108  		ConfigurationSets: role.ConfigurationSets,
   109  	}
   110  	return []gwacl.DispatcherResponse{
   111  		// GetRole returns a PersistentVMRole.
   112  		gwacl.NewDispatcherResponse(serialize(c, persistentRole), http.StatusOK, nil),
   113  		// UpdateRole expects a 200 response, that's all.
   114  		gwacl.NewDispatcherResponse(nil, http.StatusOK, nil),
   115  	}
   116  }
   117  
   118  // point is 1-indexed; it represents which request should fail.
   119  func failPortChangeConversationAt(point int, responses []gwacl.DispatcherResponse) {
   120  	responses[point-1] = gwacl.NewDispatcherResponse(
   121  		nil, http.StatusInternalServerError, nil)
   122  }
   123  
   124  type expectedRequest struct {
   125  	method     string
   126  	urlpattern string
   127  }
   128  
   129  func assertPortChangeConversation(c *gc.C, record []*gwacl.X509Request, expected []expectedRequest) {
   130  	c.Assert(record, gc.HasLen, len(expected))
   131  	for index, request := range record {
   132  		c.Check(request.Method, gc.Equals, expected[index].method)
   133  		c.Check(request.URL, gc.Matches, expected[index].urlpattern)
   134  	}
   135  }
   136  
   137  func (s *instanceSuite) TestAddresses(c *gc.C) {
   138  	vnn := s.env.getVirtualNetworkName()
   139  	responses := prepareDeploymentInfoResponse(c, gwacl.Deployment{
   140  		RoleInstanceList: []gwacl.RoleInstance{{
   141  			RoleName:  s.role.RoleName,
   142  			IPAddress: "1.2.3.4",
   143  		}},
   144  		VirtualNetworkName: vnn,
   145  	})
   146  	gwacl.PatchManagementAPIResponses(responses)
   147  
   148  	expected := []network.Address{
   149  		{
   150  			"1.2.3.4",
   151  			network.IPv4Address,
   152  			vnn,
   153  			network.ScopeCloudLocal,
   154  		},
   155  		{
   156  			s.service.ServiceName + "." + AzureDomainName,
   157  			network.HostName,
   158  			"",
   159  			network.ScopePublic,
   160  		},
   161  	}
   162  
   163  	addrs, err := s.instance.Addresses()
   164  	c.Check(err, jc.ErrorIsNil)
   165  	c.Check(addrs, jc.SameContents, expected)
   166  }
   167  
   168  func (s *instanceSuite) TestOpenPorts(c *gc.C) {
   169  	// Close the default ports.
   170  	configSetNetwork((*gwacl.Role)(s.role)).InputEndpoints = nil
   171  
   172  	responses := preparePortChangeConversation(c, s.role)
   173  	record := gwacl.PatchManagementAPIResponses(responses)
   174  	err := s.instance.OpenPorts("machine-id", []network.PortRange{
   175  		{79, 79, "tcp"}, {587, 587, "tcp"}, {9, 9, "udp"},
   176  	})
   177  	c.Assert(err, jc.ErrorIsNil)
   178  
   179  	assertPortChangeConversation(c, *record, []expectedRequest{
   180  		{"GET", ".*/deployments/deployment-one/roles/role-one"}, // GetRole
   181  		{"PUT", ".*/deployments/deployment-one/roles/role-one"}, // UpdateRole
   182  	})
   183  
   184  	// A representative UpdateRole payload includes configuration for the
   185  	// ports requested.
   186  	role := &gwacl.PersistentVMRole{}
   187  	err = role.Deserialize((*record)[1].Payload)
   188  	c.Assert(err, jc.ErrorIsNil)
   189  	c.Check(
   190  		*configSetNetwork((*gwacl.Role)(role)).InputEndpoints,
   191  		gc.DeepEquals,
   192  		[]gwacl.InputEndpoint{
   193  			makeInputEndpoint(79, "tcp"),
   194  			makeInputEndpoint(587, "tcp"),
   195  			makeInputEndpoint(9, "udp"),
   196  		},
   197  	)
   198  }
   199  
   200  func (s *instanceSuite) TestOpenPortsFailsWhenUnableToGetRole(c *gc.C) {
   201  	responses := preparePortChangeConversation(c, s.role)
   202  	failPortChangeConversationAt(1, responses) // 1st request, GetRole
   203  	record := gwacl.PatchManagementAPIResponses(responses)
   204  	err := s.instance.OpenPorts("machine-id", []network.PortRange{
   205  		{79, 79, "tcp"}, {587, 587, "tcp"}, {9, 9, "udp"},
   206  	})
   207  	c.Check(err, gc.ErrorMatches, "GET request failed [(]500: Internal Server Error[)]")
   208  	c.Check(*record, gc.HasLen, 1)
   209  }
   210  
   211  func (s *instanceSuite) TestOpenPortsFailsWhenUnableToUpdateRole(c *gc.C) {
   212  	responses := preparePortChangeConversation(c, s.role)
   213  	failPortChangeConversationAt(2, responses) // 2nd request, UpdateRole
   214  	record := gwacl.PatchManagementAPIResponses(responses)
   215  	err := s.instance.OpenPorts("machine-id", []network.PortRange{
   216  		{79, 79, "tcp"}, {587, 587, "tcp"}, {9, 9, "udp"},
   217  	})
   218  	c.Check(err, gc.ErrorMatches, "PUT request failed [(]500: Internal Server Error[)]")
   219  	c.Check(*record, gc.HasLen, 2)
   220  }
   221  
   222  func (s *instanceSuite) TestClosePorts(c *gc.C) {
   223  	type test struct {
   224  		inputPorts  []network.PortRange
   225  		removePorts []network.PortRange
   226  		outputPorts []network.PortRange
   227  	}
   228  
   229  	tests := []test{{
   230  		inputPorts:  []network.PortRange{{1, 1, "tcp"}, {2, 2, "tcp"}, {3, 3, "udp"}},
   231  		removePorts: nil,
   232  		outputPorts: []network.PortRange{{1, 1, "tcp"}, {2, 2, "tcp"}, {3, 3, "udp"}},
   233  	}, {
   234  		inputPorts:  []network.PortRange{{1, 1, "tcp"}},
   235  		removePorts: []network.PortRange{{1, 1, "udp"}},
   236  		outputPorts: []network.PortRange{{1, 1, "tcp"}},
   237  	}, {
   238  		inputPorts:  []network.PortRange{{1, 1, "tcp"}, {2, 2, "tcp"}, {3, 3, "udp"}},
   239  		removePorts: []network.PortRange{{1, 1, "tcp"}, {2, 2, "tcp"}, {3, 3, "udp"}},
   240  		outputPorts: []network.PortRange{},
   241  	}, {
   242  		inputPorts:  []network.PortRange{{1, 1, "tcp"}, {2, 2, "tcp"}, {3, 3, "udp"}},
   243  		removePorts: []network.PortRange{{99, 99, "tcp"}},
   244  		outputPorts: []network.PortRange{{1, 1, "tcp"}, {2, 2, "tcp"}, {3, 3, "udp"}},
   245  	}}
   246  
   247  	for i, test := range tests {
   248  		c.Logf("test %d: %#v", i, test)
   249  
   250  		inputEndpoints := make([]gwacl.InputEndpoint, len(test.inputPorts))
   251  		for i, port := range test.inputPorts {
   252  			inputEndpoints[i] = makeInputEndpoint(port.FromPort, port.Protocol)
   253  		}
   254  		configSetNetwork(s.role).InputEndpoints = &inputEndpoints
   255  		responses := preparePortChangeConversation(c, s.role)
   256  		record := gwacl.PatchManagementAPIResponses(responses)
   257  
   258  		err := s.instance.ClosePorts("machine-id", test.removePorts)
   259  		c.Assert(err, jc.ErrorIsNil)
   260  		assertPortChangeConversation(c, *record, []expectedRequest{
   261  			{"GET", ".*/deployments/deployment-one/roles/role-one"}, // GetRole
   262  			{"PUT", ".*/deployments/deployment-one/roles/role-one"}, // UpdateRole
   263  		})
   264  
   265  		// The first UpdateRole removes all endpoints from the role's
   266  		// configuration.
   267  		roleOne := &gwacl.PersistentVMRole{}
   268  		err = roleOne.Deserialize((*record)[1].Payload)
   269  		c.Assert(err, jc.ErrorIsNil)
   270  		endpoints := configSetNetwork((*gwacl.Role)(roleOne)).InputEndpoints
   271  		if len(test.outputPorts) == 0 {
   272  			c.Check(endpoints, gc.IsNil)
   273  		} else {
   274  			c.Check(endpoints, gc.NotNil)
   275  			c.Check(convertAndFilterEndpoints(*endpoints, s.env, false), gc.DeepEquals, test.outputPorts)
   276  		}
   277  	}
   278  }
   279  
   280  func (s *instanceSuite) TestClosePortsFailsWhenUnableToGetRole(c *gc.C) {
   281  	responses := preparePortChangeConversation(c, s.role)
   282  	failPortChangeConversationAt(1, responses) // 1st request, GetRole
   283  	record := gwacl.PatchManagementAPIResponses(responses)
   284  	err := s.instance.ClosePorts("machine-id", []network.PortRange{
   285  		{79, 79, "tcp"}, {587, 587, "tcp"}, {9, 9, "udp"},
   286  	})
   287  	c.Check(err, gc.ErrorMatches, "GET request failed [(]500: Internal Server Error[)]")
   288  	c.Check(*record, gc.HasLen, 1)
   289  }
   290  
   291  func (s *instanceSuite) TestClosePortsFailsWhenUnableToUpdateRole(c *gc.C) {
   292  	responses := preparePortChangeConversation(c, s.role)
   293  	failPortChangeConversationAt(2, responses) // 2nd request, UpdateRole
   294  	record := gwacl.PatchManagementAPIResponses(responses)
   295  	err := s.instance.ClosePorts("machine-id", []network.PortRange{
   296  		{79, 79, "tcp"}, {587, 587, "tcp"}, {9, 9, "udp"},
   297  	})
   298  	c.Check(err, gc.ErrorMatches, "PUT request failed [(]500: Internal Server Error[)]")
   299  	c.Check(*record, gc.HasLen, 2)
   300  }
   301  
   302  func (s *instanceSuite) TestConvertAndFilterEndpoints(c *gc.C) {
   303  	endpoints := []gwacl.InputEndpoint{
   304  		{
   305  			LocalPort: 123,
   306  			Protocol:  "udp",
   307  			Name:      "test123",
   308  			Port:      1123,
   309  		},
   310  		{
   311  			LocalPort: 456,
   312  			Protocol:  "tcp",
   313  			Name:      "test456",
   314  			Port:      44,
   315  		}}
   316  	endpoints = append(endpoints, s.env.getInitialEndpoints(true)...)
   317  	expectedPorts := []network.PortRange{
   318  		{1123, 1123, "udp"},
   319  		{44, 44, "tcp"}}
   320  	network.SortPortRanges(expectedPorts)
   321  	c.Check(convertAndFilterEndpoints(endpoints, s.env, true), gc.DeepEquals, expectedPorts)
   322  }
   323  
   324  func (s *instanceSuite) TestConvertAndFilterEndpointsEmptySlice(c *gc.C) {
   325  	ports := convertAndFilterEndpoints([]gwacl.InputEndpoint{}, s.env, true)
   326  	c.Check(ports, gc.HasLen, 0)
   327  }
   328  
   329  func (s *instanceSuite) TestPorts(c *gc.C) {
   330  	s.testPorts(c, false)
   331  	s.testPorts(c, true)
   332  }
   333  
   334  func (s *instanceSuite) testPorts(c *gc.C, maskStateServerPorts bool) {
   335  	// Update the role's endpoints by hand.
   336  	configSetNetwork(s.role).InputEndpoints = &[]gwacl.InputEndpoint{{
   337  		LocalPort: 223,
   338  		Protocol:  "udp",
   339  		Name:      "test223",
   340  		Port:      2123,
   341  	}, {
   342  		LocalPort: 123,
   343  		Protocol:  "udp",
   344  		Name:      "test123",
   345  		Port:      1123,
   346  	}, {
   347  		LocalPort: 456,
   348  		Protocol:  "tcp",
   349  		Name:      "test456",
   350  		Port:      4456,
   351  	}, {
   352  		LocalPort: s.env.Config().APIPort(),
   353  		Protocol:  "tcp",
   354  		Name:      "apiserver",
   355  		Port:      s.env.Config().APIPort(),
   356  	}}
   357  
   358  	responses := preparePortChangeConversation(c, s.role)
   359  	record := gwacl.PatchManagementAPIResponses(responses)
   360  	s.instance.maskStateServerPorts = maskStateServerPorts
   361  	ports, err := s.instance.Ports("machine-id")
   362  	c.Assert(err, jc.ErrorIsNil)
   363  	assertPortChangeConversation(c, *record, []expectedRequest{
   364  		{"GET", ".*/deployments/deployment-one/roles/role-one"}, // GetRole
   365  	})
   366  
   367  	expected := []network.PortRange{
   368  		{4456, 4456, "tcp"},
   369  		{1123, 1123, "udp"},
   370  		{2123, 2123, "udp"},
   371  	}
   372  	if !maskStateServerPorts {
   373  		expected = append(expected, network.PortRange{s.env.Config().APIPort(), s.env.Config().APIPort(), "tcp"})
   374  		network.SortPortRanges(expected)
   375  	}
   376  	c.Check(ports, gc.DeepEquals, expected)
   377  }