Skip to content

Commit 13dd522

Browse files
committed
fix: Refactor LB service-port merging
1 parent 2dabb65 commit 13dd522

File tree

2 files changed

+88
-24
lines changed

2 files changed

+88
-24
lines changed

internal/renderer/service_util.go

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"errors"
66
"fmt"
77
"regexp"
8+
"slices"
89
"strconv"
910
"strings"
1011

@@ -580,31 +581,27 @@ func (r *renderer) getServiceProtocol(proto gwapiv1.ProtocolType) (string, error
580581
return serviceProto, nil
581582
}
582583

583-
// TODO: understand and refactor
584-
// merge serviceports wth existing svc
585-
// - p2 overrides ps1 on conflict
586-
// - service-ports not existing in ps2 are deleted from result
587-
func mergeServicePorts(ps1, ps2 []corev1.ServicePort) []corev1.ServicePort {
588-
// init
589-
ret := make([]corev1.ServicePort, len(ps2))
590-
for i := range ps2 {
591-
ps2[i].DeepCopyInto(&ret[i])
592-
}
593-
594-
// if a service-port exists in ps1, then merge
595-
for i := range ret {
596-
for j := range ps1 {
597-
if ret[i].Name == ps1[j].Name {
598-
tmp := ret[i].DeepCopy()
599-
// copy ps1
600-
ps1[j].DeepCopyInto(&ret[i])
601-
// then update
602-
ret[i].Protocol = tmp.Protocol
603-
ret[i].Port = tmp.Port
604-
ret[i].TargetPort = tmp.TargetPort
605-
break
606-
}
584+
// mergeServicePorts merges serviceports created by stunner into the existing svc
585+
// - stunner sp overrides the existing sp on conflict
586+
// - service-ports not existing in stunner sp are deleted from the result
587+
func mergeServicePorts(existingSp, stnrSp []corev1.ServicePort) []corev1.ServicePort {
588+
ret := []corev1.ServicePort{}
589+
590+
for i := range stnrSp {
591+
spIdx := slices.IndexFunc(existingSp,
592+
func(sp corev1.ServicePort) bool { return sp.Name == stnrSp[i].Name })
593+
// if new service-port: overwrite
594+
if spIdx == -1 {
595+
ret = append(ret, stnrSp[i])
596+
continue
607597
}
598+
// existing service-port: merge
599+
sp := existingSp[spIdx].DeepCopy()
600+
sp.Protocol = stnrSp[i].Protocol
601+
sp.Port = stnrSp[i].Port
602+
// do not touch ndoeport and targetport: if requested in an annotation then we will
603+
// update these later, otherwise leave them as chosen by K8s
604+
ret = append(ret, *sp)
608605
}
609606

610607
return ret

internal/renderer/service_util_test.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2343,6 +2343,73 @@ func TestRenderServiceUtil(t *testing.T) {
23432343
assert.Equal(t, 103, p, "targetport 3")
23442344
},
23452345
},
2346+
{
2347+
name: "lb service - service nodeport/targetport retained",
2348+
cls: []gwapiv1.GatewayClass{testutils.TestGwClass},
2349+
cfs: []stnrgwv1.GatewayConfig{testutils.TestGwConfig},
2350+
gws: []gwapiv1.Gateway{testutils.TestGw},
2351+
svcs: []corev1.Service{testutils.TestSvc},
2352+
prep: func(c *renderTestConfig) {
2353+
// make sure UDP and TCP are both handled
2354+
gw := testutils.TestGw.DeepCopy()
2355+
mixedProtoAnnotation := map[string]string{
2356+
opdefault.MixedProtocolAnnotationKey: "true",
2357+
}
2358+
gw.ObjectMeta.SetAnnotations(mixedProtoAnnotation)
2359+
c.gws = []gwapiv1.Gateway{*gw}
2360+
2361+
s1 := testutils.TestSvc.DeepCopy()
2362+
s1.SetName("gateway-1")
2363+
s1.SetNamespace("testnamespace")
2364+
s1.Spec.Ports = []corev1.ServicePort{
2365+
{
2366+
Name: "gateway-1-listener-udp",
2367+
Protocol: corev1.ProtocolUDP,
2368+
Port: 1,
2369+
NodePort: 30001,
2370+
TargetPort: intstr.FromInt(1),
2371+
},
2372+
{
2373+
Name: "dummy",
2374+
Protocol: corev1.ProtocolTCP,
2375+
Port: 123,
2376+
NodePort: 31234,
2377+
TargetPort: intstr.FromString("dummy"),
2378+
},
2379+
}
2380+
s1.SetOwnerReferences([]metav1.OwnerReference{{
2381+
APIVersion: gwapiv1.GroupVersion.String(),
2382+
Kind: "Gateway",
2383+
UID: testutils.TestGw.GetUID(),
2384+
Name: testutils.TestGw.GetName(),
2385+
}})
2386+
c.svcs = []corev1.Service{*s1}
2387+
},
2388+
tester: func(t *testing.T, r *renderer) {
2389+
gc, err := r.getGatewayClass()
2390+
assert.NoError(t, err, "gw-class found")
2391+
c := &RenderContext{gc: gc, log: log}
2392+
c.gwConf, err = r.getGatewayConfig4Class(c)
2393+
assert.NoError(t, err, "gw-conf found")
2394+
2395+
gws := r.getGateways4Class(c)
2396+
assert.Len(t, gws, 1, "gateways for class")
2397+
gw := gws[0]
2398+
2399+
s, _ := r.createLbService4Gateway(c, gw)
2400+
assert.NotNil(t, s, "svc create")
2401+
assert.Equal(t, c.gwConf.GetNamespace(), s.GetNamespace(), "namespace ok")
2402+
assert.Equal(t, corev1.ServiceTypeLoadBalancer, s.Spec.Type, "lb type")
2403+
ports := s.Spec.Ports
2404+
assert.Len(t, ports, 2, "service-port len")
2405+
assert.Equal(t, "gateway-1-listener-udp", ports[0].Name, "port 1 name")
2406+
assert.Equal(t, int32(30001), ports[0].NodePort, "port 1 np") // default
2407+
assert.Equal(t, intstr.FromInt(1), ports[0].TargetPort, "port 1 tp") // default
2408+
assert.Equal(t, "gateway-1-listener-tcp", ports[1].Name, "port 1 name")
2409+
assert.Equal(t, int32(0), ports[1].NodePort, "port 2 np")
2410+
assert.Equal(t, intstr.IntOrString{}, ports[1].TargetPort, "port 2 tp")
2411+
},
2412+
},
23462413
{
23472414
name: "lb service - STUNner-specific annotation removed unless GW also sets it",
23482415
cls: []gwapiv1.GatewayClass{testutils.TestGwClass},

0 commit comments

Comments
 (0)