Skip to content

Commit 9edcaf7

Browse files
authored
Merge pull request kubernetes#85252 from prameshj/fwrules-port
Specify a port range to ILB firewall rule create.
2 parents 30e6238 + 44f0b26 commit 9edcaf7

File tree

2 files changed

+125
-10
lines changed

2 files changed

+125
-10
lines changed

staging/src/k8s.io/legacy-cloud-providers/gce/gce_loadbalancer_internal.go

Lines changed: 56 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"context"
2323
"encoding/json"
2424
"fmt"
25+
"sort"
2526
"strconv"
2627
"strings"
2728

@@ -48,7 +49,7 @@ func (g *Cloud) ensureInternalLoadBalancer(clusterName, clusterID string, svc *v
4849
}
4950

5051
nm := types.NamespacedName{Name: svc.Name, Namespace: svc.Namespace}
51-
ports, protocol := getPortsAndProtocol(svc.Spec.Ports)
52+
ports, _, protocol := getPortsAndProtocol(svc.Spec.Ports)
5253
if protocol != v1.ProtocolTCP && protocol != v1.ProtocolUDP {
5354
return nil, fmt.Errorf("Invalid protocol %s, only TCP and UDP are supported", string(protocol))
5455
}
@@ -231,7 +232,7 @@ func (g *Cloud) updateInternalLoadBalancer(clusterName, clusterID string, svc *v
231232
}
232233

233234
// Generate the backend service name
234-
_, protocol := getPortsAndProtocol(svc.Spec.Ports)
235+
_, _, protocol := getPortsAndProtocol(svc.Spec.Ports)
235236
scheme := cloud.SchemeInternal
236237
loadBalancerName := g.GetLoadBalancerName(context.TODO(), clusterName, svc)
237238
backendServiceName := makeBackendServiceName(loadBalancerName, clusterID, shareBackendService(svc), scheme, protocol, svc.Spec.SessionAffinity)
@@ -241,7 +242,7 @@ func (g *Cloud) updateInternalLoadBalancer(clusterName, clusterID string, svc *v
241242

242243
func (g *Cloud) ensureInternalLoadBalancerDeleted(clusterName, clusterID string, svc *v1.Service) error {
243244
loadBalancerName := g.GetLoadBalancerName(context.TODO(), clusterName, svc)
244-
_, protocol := getPortsAndProtocol(svc.Spec.Ports)
245+
_, _, protocol := getPortsAndProtocol(svc.Spec.Ports)
245246
scheme := cloud.SchemeInternal
246247
sharedBackend := shareBackendService(svc)
247248
sharedHealthCheck := !servicehelpers.RequestsOnlyLocalTraffic(svc)
@@ -344,7 +345,7 @@ func (g *Cloud) teardownInternalHealthCheckAndFirewall(svc *v1.Service, hcName s
344345
return nil
345346
}
346347

347-
func (g *Cloud) ensureInternalFirewall(svc *v1.Service, fwName, fwDesc string, sourceRanges []string, ports []string, protocol v1.Protocol, nodes []*v1.Node, legacyFwName string) error {
348+
func (g *Cloud) ensureInternalFirewall(svc *v1.Service, fwName, fwDesc string, sourceRanges []string, portRanges []string, protocol v1.Protocol, nodes []*v1.Node, legacyFwName string) error {
348349
klog.V(2).Infof("ensureInternalFirewall(%v): checking existing firewall", fwName)
349350
targetTags, err := g.GetNodeTags(nodeNames(nodes))
350351
if err != nil {
@@ -388,7 +389,7 @@ func (g *Cloud) ensureInternalFirewall(svc *v1.Service, fwName, fwDesc string, s
388389
Allowed: []*compute.FirewallAllowed{
389390
{
390391
IPProtocol: strings.ToLower(string(protocol)),
391-
Ports: ports,
392+
Ports: portRanges,
392393
},
393394
},
394395
}
@@ -421,12 +422,12 @@ func (g *Cloud) ensureInternalFirewall(svc *v1.Service, fwName, fwDesc string, s
421422
func (g *Cloud) ensureInternalFirewalls(loadBalancerName, ipAddress, clusterID string, nm types.NamespacedName, svc *v1.Service, healthCheckPort string, sharedHealthCheck bool, nodes []*v1.Node) error {
422423
// First firewall is for ingress traffic
423424
fwDesc := makeFirewallDescription(nm.String(), ipAddress)
424-
ports, protocol := getPortsAndProtocol(svc.Spec.Ports)
425+
_, portRanges, protocol := getPortsAndProtocol(svc.Spec.Ports)
425426
sourceRanges, err := servicehelpers.GetLoadBalancerSourceRanges(svc)
426427
if err != nil {
427428
return err
428429
}
429-
err = g.ensureInternalFirewall(svc, MakeFirewallName(loadBalancerName), fwDesc, sourceRanges.StringSlice(), ports, protocol, nodes, loadBalancerName)
430+
err = g.ensureInternalFirewall(svc, MakeFirewallName(loadBalancerName), fwDesc, sourceRanges.StringSlice(), portRanges, protocol, nodes, loadBalancerName)
430431
if err != nil {
431432
return err
432433
}
@@ -747,17 +748,62 @@ func backendSvcEqual(a, b *compute.BackendService) bool {
747748
backendsListEqual(a.Backends, b.Backends)
748749
}
749750

750-
func getPortsAndProtocol(svcPorts []v1.ServicePort) (ports []string, protocol v1.Protocol) {
751+
func getPortsAndProtocol(svcPorts []v1.ServicePort) (ports []string, portRanges []string, protocol v1.Protocol) {
751752
if len(svcPorts) == 0 {
752-
return []string{}, v1.ProtocolUDP
753+
return []string{}, []string{}, v1.ProtocolUDP
753754
}
754755

755756
// GCP doesn't support multiple protocols for a single load balancer
756757
protocol = svcPorts[0].Protocol
758+
portInts := []int{}
757759
for _, p := range svcPorts {
758760
ports = append(ports, strconv.Itoa(int(p.Port)))
761+
portInts = append(portInts, int(p.Port))
759762
}
760-
return ports, protocol
763+
764+
return ports, getPortRanges(portInts), protocol
765+
}
766+
767+
func getPortRanges(ports []int) (ranges []string) {
768+
if len(ports) < 1 {
769+
return ranges
770+
}
771+
sort.Ints(ports)
772+
773+
start := ports[0]
774+
prev := ports[0]
775+
for ix, current := range ports {
776+
switch {
777+
case current == prev:
778+
// Loop over duplicates, except if the end of list is reached.
779+
if ix == len(ports)-1 {
780+
if start == current {
781+
ranges = append(ranges, fmt.Sprintf("%d", current))
782+
} else {
783+
ranges = append(ranges, fmt.Sprintf("%d-%d", start, current))
784+
}
785+
}
786+
case current == prev+1:
787+
// continue the streak, create the range if this is the last element in the list.
788+
if ix == len(ports)-1 {
789+
ranges = append(ranges, fmt.Sprintf("%d-%d", start, current))
790+
}
791+
default:
792+
// current is not prev + 1, streak is broken. Construct the range and handle last element case.
793+
if start == prev {
794+
ranges = append(ranges, fmt.Sprintf("%d", prev))
795+
} else {
796+
ranges = append(ranges, fmt.Sprintf("%d-%d", start, prev))
797+
}
798+
if ix == len(ports)-1 {
799+
ranges = append(ranges, fmt.Sprintf("%d", current))
800+
}
801+
// reset start element
802+
start = current
803+
}
804+
prev = current
805+
}
806+
return ranges
761807
}
762808

763809
func (g *Cloud) getBackendServiceLink(name string) string {

staging/src/k8s.io/legacy-cloud-providers/gce/gce_loadbalancer_internal_test.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ package gce
2121
import (
2222
"context"
2323
"fmt"
24+
"reflect"
2425
"strings"
2526
"testing"
2627

@@ -1395,3 +1396,71 @@ func TestEnsureInternalLoadBalancerCustomSubnet(t *testing.T) {
13951396
}
13961397
assertInternalLbResourcesDeleted(t, gce, svc, vals, true)
13971398
}
1399+
1400+
func TestGetPortRanges(t *testing.T) {
1401+
t.Parallel()
1402+
for _, tc := range []struct {
1403+
Desc string
1404+
Input []int
1405+
Result []string
1406+
}{
1407+
{Desc: "All Unique", Input: []int{8, 66, 23, 13, 89}, Result: []string{"8", "13", "23", "66", "89"}},
1408+
{Desc: "All Unique Sorted", Input: []int{1, 7, 9, 16, 26}, Result: []string{"1", "7", "9", "16", "26"}},
1409+
{Desc: "Ranges", Input: []int{56, 78, 67, 79, 21, 80, 12}, Result: []string{"12", "21", "56", "67", "78-80"}},
1410+
{Desc: "Ranges Sorted", Input: []int{5, 7, 90, 1002, 1003, 1004, 1005, 2501}, Result: []string{"5", "7", "90", "1002-1005", "2501"}},
1411+
{Desc: "Ranges Duplicates", Input: []int{15, 37, 900, 2002, 2003, 2003, 2004, 2004}, Result: []string{"15", "37", "900", "2002-2004"}},
1412+
{Desc: "Duplicates", Input: []int{10, 10, 10, 10, 10}, Result: []string{"10"}},
1413+
{Desc: "Only ranges", Input: []int{18, 19, 20, 21, 22, 55, 56, 77, 78, 79, 3504, 3505, 3506}, Result: []string{"18-22", "55-56", "77-79", "3504-3506"}},
1414+
{Desc: "Single Range", Input: []int{6000, 6001, 6002, 6003, 6004, 6005}, Result: []string{"6000-6005"}},
1415+
{Desc: "One value", Input: []int{12}, Result: []string{"12"}},
1416+
{Desc: "Empty", Input: []int{}, Result: nil},
1417+
} {
1418+
result := getPortRanges(tc.Input)
1419+
if !reflect.DeepEqual(result, tc.Result) {
1420+
t.Errorf("Expected %v, got %v for test case %s", tc.Result, result, tc.Desc)
1421+
}
1422+
}
1423+
}
1424+
1425+
func TestEnsureInternalFirewallPortRanges(t *testing.T) {
1426+
gce, err := fakeGCECloud(DefaultTestClusterValues())
1427+
require.NoError(t, err)
1428+
vals := DefaultTestClusterValues()
1429+
svc := fakeLoadbalancerService(string(LBTypeInternal))
1430+
lbName := gce.GetLoadBalancerName(context.TODO(), "", svc)
1431+
fwName := MakeFirewallName(lbName)
1432+
tc := struct {
1433+
Input []int
1434+
Result []string
1435+
}{
1436+
Input: []int{15, 37, 900, 2002, 2003, 2003, 2004, 2004}, Result: []string{"15", "37", "900", "2002-2004"},
1437+
}
1438+
c := gce.c.(*cloud.MockGCE)
1439+
c.MockFirewalls.InsertHook = nil
1440+
c.MockFirewalls.UpdateHook = nil
1441+
1442+
nodes, err := createAndInsertNodes(gce, []string{"test-node-1"}, vals.ZoneName)
1443+
require.NoError(t, err)
1444+
sourceRange := []string{"10.0.0.0/20"}
1445+
// Manually create a firewall rule with the legacy name - lbName
1446+
gce.ensureInternalFirewall(
1447+
svc,
1448+
fwName,
1449+
"firewall with legacy name",
1450+
sourceRange,
1451+
getPortRanges(tc.Input),
1452+
v1.ProtocolTCP,
1453+
nodes,
1454+
"")
1455+
if err != nil {
1456+
t.Errorf("Unexpected error %v when ensuring legacy firewall %s for svc %+v", err, lbName, svc)
1457+
}
1458+
existingFirewall, err := gce.GetFirewall(fwName)
1459+
if err != nil || existingFirewall == nil || len(existingFirewall.Allowed) == 0 {
1460+
t.Errorf("Unexpected error %v when looking up firewall %s, Got firewall %+v", err, fwName, existingFirewall)
1461+
}
1462+
existingPorts := existingFirewall.Allowed[0].Ports
1463+
if !reflect.DeepEqual(existingPorts, tc.Result) {
1464+
t.Errorf("Expected firewall rule with ports %v,got %v", tc.Result, existingPorts)
1465+
}
1466+
}

0 commit comments

Comments
 (0)