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 }