Skip to content

Commit becad2c

Browse files
author
Roger Peppe
committed
merge lp:juju-core/trunk
2 parents 133800d + 4caaa80 commit becad2c

File tree

5 files changed

+240
-36
lines changed

5 files changed

+240
-36
lines changed

environs/azure/environ.go

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,33 @@ func (env *azureEnviron) newOSDisk(sourceImageName string) *gwacl.OSVirtualHardD
551551
return gwacl.NewOSVirtualHardDisk("", "", "", mediaLink, sourceImageName, "Linux")
552552
}
553553

554+
// getInitialEndpoints returns a slice of the endpoints every instance should have open
555+
// (ssh port, etc).
556+
func (env *azureEnviron) getInitialEndpoints() []gwacl.InputEndpoint {
557+
config := env.Config()
558+
return []gwacl.InputEndpoint{
559+
{
560+
LocalPort: 22,
561+
Name: "sshport",
562+
Port: 22,
563+
Protocol: "tcp",
564+
},
565+
// TODO: Ought to have this only for state servers.
566+
{
567+
LocalPort: config.StatePort(),
568+
Name: "stateport",
569+
Port: config.StatePort(),
570+
Protocol: "tcp",
571+
},
572+
// TODO: Ought to have this only for API servers.
573+
{
574+
LocalPort: config.APIPort(),
575+
Name: "apiport",
576+
Port: config.APIPort(),
577+
Protocol: "tcp",
578+
}}
579+
}
580+
554581
// newRole creates a gwacl.Role object (an Azure Virtual Machine) which uses
555582
// the given Virtual Hard Drive.
556583
//
@@ -569,31 +596,9 @@ func (env *azureEnviron) newRole(roleSize string, vhd *gwacl.OSVirtualHardDisk,
569596
username := "ubuntu"
570597
password := gwacl.MakeRandomPassword()
571598
linuxConfigurationSet := gwacl.NewLinuxProvisioningConfigurationSet(hostname, username, password, userData, "true")
572-
config := env.Config()
573599
// Generate a Network Configuration with the initially required ports
574600
// open.
575-
networkConfigurationSet := gwacl.NewNetworkConfigurationSet([]gwacl.InputEndpoint{
576-
{
577-
LocalPort: 22,
578-
Name: "sshport",
579-
Port: 22,
580-
Protocol: "TCP",
581-
},
582-
// TODO: Ought to have this only for state servers.
583-
{
584-
LocalPort: config.StatePort(),
585-
Name: "stateport",
586-
Port: config.StatePort(),
587-
Protocol: "TCP",
588-
},
589-
// TODO: Ought to have this only for API servers.
590-
{
591-
LocalPort: config.APIPort(),
592-
Name: "apiport",
593-
Port: config.APIPort(),
594-
Protocol: "TCP",
595-
},
596-
}, nil)
601+
networkConfigurationSet := gwacl.NewNetworkConfigurationSet(env.getInitialEndpoints(), nil)
597602
roleName := gwacl.MakeRandomRoleName("juju")
598603
// The ordering of these configuration sets is significant for the tests.
599604
return gwacl.NewRole(

environs/azure/environ_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -947,21 +947,21 @@ func (*environSuite) TestNewRole(c *C) {
947947
sshEndpoint, ok := endpoints[22]
948948
c.Assert(ok, Equals, true)
949949
c.Check(sshEndpoint.LocalPort, Equals, 22)
950-
c.Check(sshEndpoint.Protocol, Equals, "TCP")
950+
c.Check(sshEndpoint.Protocol, Equals, "tcp")
951951

952952
// There's also an endpoint for the state (mongodb) port.
953953
// TODO: Ought to have this only for state servers.
954954
stateEndpoint, ok := endpoints[env.Config().StatePort()]
955955
c.Assert(ok, Equals, true)
956956
c.Check(stateEndpoint.LocalPort, Equals, env.Config().StatePort())
957-
c.Check(stateEndpoint.Protocol, Equals, "TCP")
957+
c.Check(stateEndpoint.Protocol, Equals, "tcp")
958958

959959
// And one for the API port.
960960
// TODO: Ought to have this only for API servers.
961961
apiEndpoint, ok := endpoints[env.Config().APIPort()]
962962
c.Assert(ok, Equals, true)
963963
c.Check(apiEndpoint.LocalPort, Equals, env.Config().APIPort())
964-
c.Check(apiEndpoint.Protocol, Equals, "TCP")
964+
c.Check(apiEndpoint.Protocol, Equals, "tcp")
965965
}
966966

967967
func (*environSuite) TestNewDeployment(c *C) {

environs/azure/instance.go

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@ package azure
55

66
import (
77
"fmt"
8+
"strings"
89

910
"launchpad.net/gwacl"
1011

1112
"launchpad.net/juju-core/environs"
1213
"launchpad.net/juju-core/instance"
14+
"launchpad.net/juju-core/state"
15+
"launchpad.net/juju-core/worker/firewaller"
1316
)
1417

1518
type azureInstance struct {
@@ -156,8 +159,82 @@ func (azInstance *azureInstance) closeEndpoints(context *azureManagementContext,
156159
return nil
157160
}
158161

162+
// convertAndFilterEndpoints converts a slice of gwacl.InputEndpoint into a slice of instance.Port.
163+
func convertEndpointsToPorts(endpoints []gwacl.InputEndpoint) []instance.Port {
164+
ports := []instance.Port{}
165+
for _, endpoint := range endpoints {
166+
ports = append(ports, instance.Port{
167+
Protocol: strings.ToLower(endpoint.Protocol),
168+
Number: endpoint.Port,
169+
})
170+
}
171+
return ports
172+
}
173+
174+
// convertAndFilterEndpoints converts a slice of gwacl.InputEndpoint into a slice of instance.Port
175+
// and filters out the initial endpoints that every instance should have opened (ssh port, etc.).
176+
func convertAndFilterEndpoints(endpoints []gwacl.InputEndpoint, env *azureEnviron) []instance.Port {
177+
return firewaller.Diff(
178+
convertEndpointsToPorts(endpoints),
179+
convertEndpointsToPorts(env.getInitialEndpoints()))
180+
}
181+
159182
// Ports is specified in the Instance interface.
160183
func (azInstance *azureInstance) Ports(machineId string) ([]instance.Port, error) {
161-
// TODO: implement this.
162-
return []instance.Port{}, nil
184+
env := azInstance.environ
185+
context, err := env.getManagementAPI()
186+
if err != nil {
187+
return nil, err
188+
}
189+
defer env.releaseManagementAPI(context)
190+
191+
ports, err := azInstance.listPorts(context)
192+
if err != nil {
193+
return nil, err
194+
}
195+
state.SortPorts(ports)
196+
return ports, nil
197+
}
198+
199+
// listPorts returns the slice of ports (instance.Port) that this machine
200+
// has opened. The returned list does not contain the "initial ports"
201+
// (i.e. the ports every instance shoud have opened). The caller is
202+
// responsible for locking and unlocking the environ and releasing the
203+
// management context.
204+
func (azInstance *azureInstance) listPorts(context *azureManagementContext) ([]instance.Port, error) {
205+
deployments, err := context.ListAllDeployments(&gwacl.ListAllDeploymentsRequest{
206+
ServiceName: azInstance.ServiceName,
207+
})
208+
if err != nil {
209+
return nil, err
210+
}
211+
212+
env := azInstance.environ
213+
switch {
214+
// Only zero or one deployment is a valid state (instance==service).
215+
case len(deployments) > 1:
216+
return nil, fmt.Errorf("more than one Azure deployment inside the service named %q", azInstance.ServiceName)
217+
case len(deployments) == 1:
218+
deployment := deployments[0]
219+
switch {
220+
// Only zero or one role is a valid state (instance==service).
221+
case len(deployment.RoleList) > 1:
222+
return nil, fmt.Errorf("more than one Azure role inside the deployment named %q", deployment.Name)
223+
case len(deployment.RoleList) == 1:
224+
role := deployment.RoleList[0]
225+
226+
endpoints, err := context.ListRoleEndpoints(&gwacl.ListRoleEndpointsRequest{
227+
ServiceName: azInstance.ServiceName,
228+
DeploymentName: deployment.Name,
229+
RoleName: role.RoleName,
230+
})
231+
if err != nil {
232+
return nil, err
233+
}
234+
ports := convertAndFilterEndpoints(endpoints, env)
235+
return ports, nil
236+
}
237+
return nil, nil
238+
}
239+
return nil, nil
163240
}

environs/azure/instance_test.go

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,3 +329,125 @@ func (*instanceSuite) TestClosePortsFailsWhenUnableToUpdateRole(c *C) {
329329
c.Check(err, ErrorMatches, "PUT request failed [(]500: Internal Server Error[)]")
330330
c.Check(*record, HasLen, 3)
331331
}
332+
333+
func (*instanceSuite) TestConvertAndFilterEndpoints(c *C) {
334+
env := makeEnviron(c)
335+
endpoints := []gwacl.InputEndpoint{
336+
{
337+
LocalPort: 123,
338+
Protocol: "udp",
339+
Name: "test123",
340+
Port: 1123,
341+
},
342+
{
343+
LocalPort: 456,
344+
Protocol: "tcp",
345+
Name: "test456",
346+
Port: 44,
347+
}}
348+
endpoints = append(endpoints, env.getInitialEndpoints()...)
349+
expectedPorts := []instance.Port{
350+
{
351+
Number: 1123,
352+
Protocol: "udp",
353+
},
354+
{
355+
Number: 44,
356+
Protocol: "tcp",
357+
}}
358+
c.Check(convertAndFilterEndpoints(endpoints, env), DeepEquals, expectedPorts)
359+
}
360+
361+
func (*instanceSuite) TestConvertAndFilterEndpointsEmptySlice(c *C) {
362+
env := makeEnviron(c)
363+
ports := convertAndFilterEndpoints([]gwacl.InputEndpoint{}, env)
364+
c.Check(ports, HasLen, 0)
365+
}
366+
367+
func (*instanceSuite) TestPorts(c *C) {
368+
service := makeHostedServiceDescriptor("service-name")
369+
endpoints := []gwacl.InputEndpoint{
370+
{
371+
LocalPort: 223,
372+
Protocol: "udp",
373+
Name: "test223",
374+
Port: 2123,
375+
},
376+
{
377+
LocalPort: 123,
378+
Protocol: "udp",
379+
Name: "test123",
380+
Port: 1123,
381+
},
382+
{
383+
LocalPort: 456,
384+
Protocol: "tcp",
385+
Name: "test456",
386+
Port: 4456,
387+
}}
388+
389+
responses := preparePortChangeConversation(c, service,
390+
makeDeployment("deployment-one",
391+
makeRole("role-one", endpoints...)))
392+
record := gwacl.PatchManagementAPIResponses(responses)
393+
azInstance := azureInstance{*service, makeEnviron(c)}
394+
395+
ports, err := azInstance.Ports("machine-id")
396+
397+
c.Assert(err, IsNil)
398+
assertPortChangeConversation(c, *record, []expectedRequest{
399+
{"GET", ".*/services/hostedservices/service-name[?].*"}, // GetHostedServiceProperties
400+
{"GET", ".*/deployments/deployment-one/roles/role-one"}, // GetRole
401+
})
402+
403+
c.Check(
404+
ports,
405+
DeepEquals,
406+
// The result is sorted using state.SortPorts() (i.e. first by protocol,
407+
// then by number).
408+
[]instance.Port{
409+
instance.Port{Number: 4456, Protocol: "tcp"},
410+
instance.Port{Number: 1123, Protocol: "udp"},
411+
instance.Port{Number: 2123, Protocol: "udp"},
412+
})
413+
}
414+
415+
func (*instanceSuite) TestPortsErrorsIfMoreThanOneRole(c *C) {
416+
service := makeHostedServiceDescriptor("service-name")
417+
responses := preparePortChangeConversation(c, service,
418+
makeDeployment("deployment-one",
419+
makeRole("role-one"), makeRole("role-two")))
420+
gwacl.PatchManagementAPIResponses(responses)
421+
azInstance := azureInstance{*service, makeEnviron(c)}
422+
423+
_, err := azInstance.Ports("machine-id")
424+
425+
c.Check(err, ErrorMatches, ".*more than one Azure role inside the deployment.*")
426+
}
427+
428+
func (*instanceSuite) TestPortsErrorsIfMoreThanOneDeployment(c *C) {
429+
service := makeHostedServiceDescriptor("service-name")
430+
responses := preparePortChangeConversation(c, service,
431+
makeDeployment("deployment-one",
432+
makeRole("role-one")),
433+
makeDeployment("deployment-two",
434+
makeRole("role-two")))
435+
gwacl.PatchManagementAPIResponses(responses)
436+
azInstance := azureInstance{*service, makeEnviron(c)}
437+
438+
_, err := azInstance.Ports("machine-id")
439+
440+
c.Check(err, ErrorMatches, ".*more than one Azure deployment inside the service.*")
441+
}
442+
443+
func (*instanceSuite) TestPortsReturnsEmptySliceIfNoDeployment(c *C) {
444+
service := makeHostedServiceDescriptor("service-name")
445+
responses := preparePortChangeConversation(c, service)
446+
gwacl.PatchManagementAPIResponses(responses)
447+
azInstance := azureInstance{*service, makeEnviron(c)}
448+
449+
ports, err := azInstance.Ports("machine-id")
450+
451+
c.Assert(err, IsNil)
452+
c.Check(ports, HasLen, 0)
453+
}

worker/firewaller/firewaller.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -235,8 +235,8 @@ func (fw *Firewaller) reconcileGlobal() error {
235235
wantedPorts = append(wantedPorts, port)
236236
}
237237
// Check which ports to open or to close.
238-
toOpen := diff(wantedPorts, initialPorts)
239-
toClose := diff(initialPorts, wantedPorts)
238+
toOpen := Diff(wantedPorts, initialPorts)
239+
toClose := Diff(initialPorts, wantedPorts)
240240
if len(toOpen) > 0 {
241241
log.Infof("worker/firewaller: opening global ports %v", toOpen)
242242
if err := fw.environ.OpenPorts(toOpen); err != nil {
@@ -283,8 +283,8 @@ func (fw *Firewaller) reconcileInstances() error {
283283
return err
284284
}
285285
// Check which ports to open or to close.
286-
toOpen := diff(machined.ports, initialPorts)
287-
toClose := diff(initialPorts, machined.ports)
286+
toOpen := Diff(machined.ports, initialPorts)
287+
toClose := Diff(initialPorts, machined.ports)
288288
if len(toOpen) > 0 {
289289
log.Infof("worker/firewaller: opening instance ports %v for machine %s",
290290
toOpen, machined.id)
@@ -375,8 +375,8 @@ func (fw *Firewaller) flushMachine(machined *machineData) error {
375375
for port := range ports {
376376
want = append(want, port)
377377
}
378-
toOpen := diff(want, machined.ports)
379-
toClose := diff(machined.ports, want)
378+
toOpen := Diff(want, machined.ports)
379+
toClose := Diff(machined.ports, want)
380380
machined.ports = want
381381
if fw.globalMode {
382382
return fw.flushGlobalPorts(toOpen, toClose)
@@ -744,8 +744,8 @@ func (sd *serviceData) Stop() error {
744744
return sd.tomb.Wait()
745745
}
746746

747-
// diff returns all the ports that exist in A but not B.
748-
func diff(A, B []instance.Port) (missing []instance.Port) {
747+
// Diff returns all the ports that exist in A but not B.
748+
func Diff(A, B []instance.Port) (missing []instance.Port) {
749749
next:
750750
for _, a := range A {
751751
for _, b := range B {

0 commit comments

Comments
 (0)