github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/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(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 vnn := s.env.getVirtualNetworkName() 140 responses := prepareDeploymentInfoResponse(c, gwacl.Deployment{ 141 RoleInstanceList: []gwacl.RoleInstance{{ 142 RoleName: s.role.RoleName, 143 IPAddress: "1.2.3.4", 144 }}, 145 VirtualNetworkName: vnn, 146 }) 147 gwacl.PatchManagementAPIResponses(responses) 148 149 expected := []network.Address{ 150 { 151 "1.2.3.4", 152 network.IPv4Address, 153 vnn, 154 network.ScopeCloudLocal, 155 }, 156 { 157 s.service.ServiceName + "." + AzureDomainName, 158 network.HostName, 159 "", 160 network.ScopePublic, 161 }, 162 } 163 164 addrs, err := s.instance.Addresses() 165 c.Check(err, jc.ErrorIsNil) 166 c.Check(addrs, jc.SameContents, expected) 167 } 168 169 func (s *instanceSuite) TestOpenPorts(c *gc.C) { 170 // Close the default ports. 171 configSetNetwork((*gwacl.Role)(s.role)).InputEndpoints = nil 172 173 responses := preparePortChangeConversation(c, s.role) 174 record := gwacl.PatchManagementAPIResponses(responses) 175 err := s.instance.OpenPorts("machine-id", []network.PortRange{ 176 {79, 79, "tcp"}, {587, 587, "tcp"}, {9, 9, "udp"}, 177 }) 178 c.Assert(err, jc.ErrorIsNil) 179 180 assertPortChangeConversation(c, *record, []expectedRequest{ 181 {"GET", ".*/deployments/deployment-one/roles/role-one"}, // GetRole 182 {"PUT", ".*/deployments/deployment-one/roles/role-one"}, // UpdateRole 183 }) 184 185 // A representative UpdateRole payload includes configuration for the 186 // ports requested. 187 role := &gwacl.PersistentVMRole{} 188 err = role.Deserialize((*record)[1].Payload) 189 c.Assert(err, jc.ErrorIsNil) 190 c.Check( 191 *configSetNetwork((*gwacl.Role)(role)).InputEndpoints, 192 gc.DeepEquals, 193 []gwacl.InputEndpoint{ 194 makeInputEndpoint(79, "tcp"), 195 makeInputEndpoint(587, "tcp"), 196 makeInputEndpoint(9, "udp"), 197 }, 198 ) 199 } 200 201 func (s *instanceSuite) TestOpenPortsFailsWhenUnableToGetRole(c *gc.C) { 202 responses := preparePortChangeConversation(c, s.role) 203 failPortChangeConversationAt(1, responses) // 1st request, GetRole 204 record := gwacl.PatchManagementAPIResponses(responses) 205 err := s.instance.OpenPorts("machine-id", []network.PortRange{ 206 {79, 79, "tcp"}, {587, 587, "tcp"}, {9, 9, "udp"}, 207 }) 208 c.Check(err, gc.ErrorMatches, "GET request failed [(]500: Internal Server Error[)]") 209 c.Check(*record, gc.HasLen, 1) 210 } 211 212 func (s *instanceSuite) TestOpenPortsFailsWhenUnableToUpdateRole(c *gc.C) { 213 responses := preparePortChangeConversation(c, s.role) 214 failPortChangeConversationAt(2, responses) // 2nd request, UpdateRole 215 record := gwacl.PatchManagementAPIResponses(responses) 216 err := s.instance.OpenPorts("machine-id", []network.PortRange{ 217 {79, 79, "tcp"}, {587, 587, "tcp"}, {9, 9, "udp"}, 218 }) 219 c.Check(err, gc.ErrorMatches, "PUT request failed [(]500: Internal Server Error[)]") 220 c.Check(*record, gc.HasLen, 2) 221 } 222 223 func (s *instanceSuite) TestClosePorts(c *gc.C) { 224 type test struct { 225 inputPorts []network.PortRange 226 removePorts []network.PortRange 227 outputPorts []network.PortRange 228 } 229 230 tests := []test{{ 231 inputPorts: []network.PortRange{{1, 1, "tcp"}, {2, 2, "tcp"}, {3, 3, "udp"}}, 232 removePorts: nil, 233 outputPorts: []network.PortRange{{1, 1, "tcp"}, {2, 2, "tcp"}, {3, 3, "udp"}}, 234 }, { 235 inputPorts: []network.PortRange{{1, 1, "tcp"}}, 236 removePorts: []network.PortRange{{1, 1, "udp"}}, 237 outputPorts: []network.PortRange{{1, 1, "tcp"}}, 238 }, { 239 inputPorts: []network.PortRange{{1, 1, "tcp"}, {2, 2, "tcp"}, {3, 3, "udp"}}, 240 removePorts: []network.PortRange{{1, 1, "tcp"}, {2, 2, "tcp"}, {3, 3, "udp"}}, 241 outputPorts: []network.PortRange{}, 242 }, { 243 inputPorts: []network.PortRange{{1, 1, "tcp"}, {2, 2, "tcp"}, {3, 3, "udp"}}, 244 removePorts: []network.PortRange{{99, 99, "tcp"}}, 245 outputPorts: []network.PortRange{{1, 1, "tcp"}, {2, 2, "tcp"}, {3, 3, "udp"}}, 246 }} 247 248 for i, test := range tests { 249 c.Logf("test %d: %#v", i, test) 250 251 inputEndpoints := make([]gwacl.InputEndpoint, len(test.inputPorts)) 252 for i, port := range test.inputPorts { 253 inputEndpoints[i] = makeInputEndpoint(port.FromPort, port.Protocol) 254 } 255 configSetNetwork(s.role).InputEndpoints = &inputEndpoints 256 responses := preparePortChangeConversation(c, s.role) 257 record := gwacl.PatchManagementAPIResponses(responses) 258 259 err := s.instance.ClosePorts("machine-id", test.removePorts) 260 c.Assert(err, jc.ErrorIsNil) 261 assertPortChangeConversation(c, *record, []expectedRequest{ 262 {"GET", ".*/deployments/deployment-one/roles/role-one"}, // GetRole 263 {"PUT", ".*/deployments/deployment-one/roles/role-one"}, // UpdateRole 264 }) 265 266 // The first UpdateRole removes all endpoints from the role's 267 // configuration. 268 roleOne := &gwacl.PersistentVMRole{} 269 err = roleOne.Deserialize((*record)[1].Payload) 270 c.Assert(err, jc.ErrorIsNil) 271 endpoints := configSetNetwork((*gwacl.Role)(roleOne)).InputEndpoints 272 if len(test.outputPorts) == 0 { 273 c.Check(endpoints, gc.IsNil) 274 } else { 275 c.Check(endpoints, gc.NotNil) 276 c.Check(convertAndFilterEndpoints(*endpoints, s.env, false), gc.DeepEquals, test.outputPorts) 277 } 278 } 279 } 280 281 func (s *instanceSuite) TestClosePortsFailsWhenUnableToGetRole(c *gc.C) { 282 responses := preparePortChangeConversation(c, s.role) 283 failPortChangeConversationAt(1, responses) // 1st request, GetRole 284 record := gwacl.PatchManagementAPIResponses(responses) 285 err := s.instance.ClosePorts("machine-id", []network.PortRange{ 286 {79, 79, "tcp"}, {587, 587, "tcp"}, {9, 9, "udp"}, 287 }) 288 c.Check(err, gc.ErrorMatches, "GET request failed [(]500: Internal Server Error[)]") 289 c.Check(*record, gc.HasLen, 1) 290 } 291 292 func (s *instanceSuite) TestClosePortsFailsWhenUnableToUpdateRole(c *gc.C) { 293 responses := preparePortChangeConversation(c, s.role) 294 failPortChangeConversationAt(2, responses) // 2nd request, UpdateRole 295 record := gwacl.PatchManagementAPIResponses(responses) 296 err := s.instance.ClosePorts("machine-id", []network.PortRange{ 297 {79, 79, "tcp"}, {587, 587, "tcp"}, {9, 9, "udp"}, 298 }) 299 c.Check(err, gc.ErrorMatches, "PUT request failed [(]500: Internal Server Error[)]") 300 c.Check(*record, gc.HasLen, 2) 301 } 302 303 func (s *instanceSuite) TestConvertAndFilterEndpoints(c *gc.C) { 304 endpoints := []gwacl.InputEndpoint{ 305 { 306 LocalPort: 123, 307 Protocol: "udp", 308 Name: "test123", 309 Port: 1123, 310 }, 311 { 312 LocalPort: 456, 313 Protocol: "tcp", 314 Name: "test456", 315 Port: 44, 316 }} 317 endpoints = append(endpoints, s.env.getInitialEndpoints(true)...) 318 expectedPorts := []network.PortRange{ 319 {1123, 1123, "udp"}, 320 {44, 44, "tcp"}} 321 network.SortPortRanges(expectedPorts) 322 c.Check(convertAndFilterEndpoints(endpoints, s.env, true), gc.DeepEquals, expectedPorts) 323 } 324 325 func (s *instanceSuite) TestConvertAndFilterEndpointsEmptySlice(c *gc.C) { 326 ports := convertAndFilterEndpoints([]gwacl.InputEndpoint{}, s.env, true) 327 c.Check(ports, gc.HasLen, 0) 328 } 329 330 func (s *instanceSuite) TestPorts(c *gc.C) { 331 s.testPorts(c, false) 332 s.testPorts(c, true) 333 } 334 335 func (s *instanceSuite) testPorts(c *gc.C, maskStateServerPorts bool) { 336 // Update the role's endpoints by hand. 337 configSetNetwork(s.role).InputEndpoints = &[]gwacl.InputEndpoint{{ 338 LocalPort: 223, 339 Protocol: "udp", 340 Name: "test223", 341 Port: 2123, 342 }, { 343 LocalPort: 123, 344 Protocol: "udp", 345 Name: "test123", 346 Port: 1123, 347 }, { 348 LocalPort: 456, 349 Protocol: "tcp", 350 Name: "test456", 351 Port: 4456, 352 }, { 353 LocalPort: s.env.Config().APIPort(), 354 Protocol: "tcp", 355 Name: "apiserver", 356 Port: s.env.Config().APIPort(), 357 }} 358 359 responses := preparePortChangeConversation(c, s.role) 360 record := gwacl.PatchManagementAPIResponses(responses) 361 s.instance.maskStateServerPorts = maskStateServerPorts 362 ports, err := s.instance.Ports("machine-id") 363 c.Assert(err, jc.ErrorIsNil) 364 assertPortChangeConversation(c, *record, []expectedRequest{ 365 {"GET", ".*/deployments/deployment-one/roles/role-one"}, // GetRole 366 }) 367 368 expected := []network.PortRange{ 369 {4456, 4456, "tcp"}, 370 {1123, 1123, "udp"}, 371 {2123, 2123, "udp"}, 372 } 373 if !maskStateServerPorts { 374 expected = append(expected, network.PortRange{s.env.Config().APIPort(), s.env.Config().APIPort(), "tcp"}) 375 network.SortPortRanges(expected) 376 } 377 c.Check(ports, gc.DeepEquals, expected) 378 }