github.com/rogpeppe/juju@v0.0.0-20140613142852-6337964b789e/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 "launchpad.net/gocheck" 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, gc.IsNil) 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", protocol, 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: name, 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, gc.IsNil) 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 network.Address{ 150 "1.2.3.4", 151 network.IPv4Address, 152 vnn, 153 network.ScopeCloudLocal, 154 }, 155 network.Address{ 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, gc.IsNil) 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.Port{ 175 {"tcp", 79}, {"tcp", 587}, {"udp", 9}, 176 }) 177 c.Assert(err, gc.IsNil) 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, gc.IsNil) 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.Port{ 205 {"tcp", 79}, {"tcp", 587}, {"udp", 9}, 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.Port{ 216 {"tcp", 79}, {"tcp", 587}, {"udp", 9}, 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.Port 225 removePorts []network.Port 226 outputPorts []network.Port 227 } 228 229 tests := []test{{ 230 inputPorts: []network.Port{{"tcp", 1}, {"tcp", 2}, {"udp", 3}}, 231 removePorts: nil, 232 outputPorts: []network.Port{{"tcp", 1}, {"tcp", 2}, {"udp", 3}}, 233 }, { 234 inputPorts: []network.Port{{"tcp", 1}}, 235 removePorts: []network.Port{{"udp", 1}}, 236 outputPorts: []network.Port{{"tcp", 1}}, 237 }, { 238 inputPorts: []network.Port{{"tcp", 1}, {"tcp", 2}, {"udp", 3}}, 239 removePorts: []network.Port{{"tcp", 1}, {"tcp", 2}, {"udp", 3}}, 240 outputPorts: []network.Port{}, 241 }, { 242 inputPorts: []network.Port{{"tcp", 1}, {"tcp", 2}, {"udp", 3}}, 243 removePorts: []network.Port{{"tcp", 99}}, 244 outputPorts: []network.Port{{"tcp", 1}, {"tcp", 2}, {"udp", 3}}, 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.Number, 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, gc.IsNil) 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, gc.IsNil) 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.Port{ 285 {"tcp", 79}, {"tcp", 587}, {"udp", 9}, 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.Port{ 296 {"tcp", 79}, {"tcp", 587}, {"udp", 9}, 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.Port{ 318 { 319 Number: 1123, 320 Protocol: "udp", 321 }, 322 { 323 Number: 44, 324 Protocol: "tcp", 325 }} 326 c.Check(convertAndFilterEndpoints(endpoints, s.env, true), gc.DeepEquals, expectedPorts) 327 } 328 329 func (s *instanceSuite) TestConvertAndFilterEndpointsEmptySlice(c *gc.C) { 330 ports := convertAndFilterEndpoints([]gwacl.InputEndpoint{}, s.env, true) 331 c.Check(ports, gc.HasLen, 0) 332 } 333 334 func (s *instanceSuite) TestPorts(c *gc.C) { 335 s.testPorts(c, false) 336 s.testPorts(c, true) 337 } 338 339 func (s *instanceSuite) testPorts(c *gc.C, maskStateServerPorts bool) { 340 // Update the role's endpoints by hand. 341 configSetNetwork(s.role).InputEndpoints = &[]gwacl.InputEndpoint{{ 342 LocalPort: 223, 343 Protocol: "udp", 344 Name: "test223", 345 Port: 2123, 346 }, { 347 LocalPort: 123, 348 Protocol: "udp", 349 Name: "test123", 350 Port: 1123, 351 }, { 352 LocalPort: 456, 353 Protocol: "tcp", 354 Name: "test456", 355 Port: 4456, 356 }, { 357 LocalPort: s.env.Config().StatePort(), 358 Protocol: "tcp", 359 Name: "stateserver", 360 Port: s.env.Config().StatePort(), 361 }, { 362 LocalPort: s.env.Config().APIPort(), 363 Protocol: "tcp", 364 Name: "apiserver", 365 Port: s.env.Config().APIPort(), 366 }} 367 368 responses := preparePortChangeConversation(c, s.role) 369 record := gwacl.PatchManagementAPIResponses(responses) 370 s.instance.maskStateServerPorts = maskStateServerPorts 371 ports, err := s.instance.Ports("machine-id") 372 c.Assert(err, gc.IsNil) 373 assertPortChangeConversation(c, *record, []expectedRequest{ 374 {"GET", ".*/deployments/deployment-one/roles/role-one"}, // GetRole 375 }) 376 377 expected := []network.Port{ 378 {Number: 4456, Protocol: "tcp"}, 379 {Number: 1123, Protocol: "udp"}, 380 {Number: 2123, Protocol: "udp"}, 381 } 382 if !maskStateServerPorts { 383 expected = append(expected, network.Port{Number: s.env.Config().StatePort(), Protocol: "tcp"}) 384 expected = append(expected, network.Port{Number: s.env.Config().APIPort(), Protocol: "tcp"}) 385 network.SortPorts(expected) 386 } 387 c.Check(ports, gc.DeepEquals, expected) 388 }