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 }