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