From 151751991eecffa9ad4dd6bb04814461a1b231f8 Mon Sep 17 00:00:00 2001 From: Saylor Berman Date: Fri, 6 Jun 2025 11:29:14 -0600 Subject: [PATCH 1/2] Set proper IP family policy on NGINX LB Service Problem: When provisioning the NGINX LoadBalancer Service, the IPFamily that's set in the NginxProxy resource (default dual) was not honored. Solution: By default, set the IPFamily to PreferDualStack. If a user has specified otherwise in the NginxProxy resource, then set to SingleStack. The IPFamilies list is populated automatically by k8s based on the policy. --- internal/controller/provisioner/objects.go | 14 ++++++++++++++ internal/controller/provisioner/objects_test.go | 4 ++++ 2 files changed, 18 insertions(+) diff --git a/internal/controller/provisioner/objects.go b/internal/controller/provisioner/objects.go index e4afe5b194..3b43ac8b66 100644 --- a/internal/controller/provisioner/objects.go +++ b/internal/controller/provisioner/objects.go @@ -463,9 +463,12 @@ func buildNginxService( Ports: servicePorts, ExternalTrafficPolicy: servicePolicy, Selector: selectorLabels, + IPFamilyPolicy: helpers.GetPointer(corev1.IPFamilyPolicyPreferDualStack), }, } + setIPFamily(nProxyCfg, svc) + if serviceCfg.LoadBalancerIP != nil { svc.Spec.LoadBalancerIP = *serviceCfg.LoadBalancerIP } @@ -479,6 +482,17 @@ func buildNginxService( return svc } +func setIPFamily(nProxyCfg *graph.EffectiveNginxProxy, svc *corev1.Service) { + if nProxyCfg != nil && nProxyCfg.IPFamily != nil && *nProxyCfg.IPFamily != ngfAPIv1alpha2.Dual { + svc.Spec.IPFamilyPolicy = helpers.GetPointer(corev1.IPFamilyPolicySingleStack) + if *nProxyCfg.IPFamily == ngfAPIv1alpha2.IPv4 { + svc.Spec.IPFamilies = []corev1.IPFamily{corev1.IPv4Protocol} + } else { + svc.Spec.IPFamilies = []corev1.IPFamily{corev1.IPv6Protocol} + } + } +} + func (p *NginxProvisioner) buildNginxDeployment( objectMeta metav1.ObjectMeta, nProxyCfg *graph.EffectiveNginxProxy, diff --git a/internal/controller/provisioner/objects_test.go b/internal/controller/provisioner/objects_test.go index 061efc7c47..20382ff761 100644 --- a/internal/controller/provisioner/objects_test.go +++ b/internal/controller/provisioner/objects_test.go @@ -161,6 +161,7 @@ func TestBuildNginxResourceObjects(t *testing.T) { validateMeta(svc) g.Expect(svc.Spec.Type).To(Equal(defaultServiceType)) g.Expect(svc.Spec.ExternalTrafficPolicy).To(Equal(defaultServicePolicy)) + g.Expect(*svc.Spec.IPFamilyPolicy).To(Equal(corev1.IPFamilyPolicyPreferDualStack)) // service ports is sorted in ascending order by port number when we make the nginx object g.Expect(svc.Spec.Ports).To(Equal([]corev1.ServicePort{ @@ -260,6 +261,7 @@ func TestBuildNginxResourceObjects_NginxProxyConfig(t *testing.T) { resourceName := "gw-nginx" nProxyCfg := &graph.EffectiveNginxProxy{ + IPFamily: helpers.GetPointer(ngfAPIv1alpha2.IPv4), Logging: &ngfAPIv1alpha2.NginxLogging{ ErrorLevel: helpers.GetPointer(ngfAPIv1alpha2.NginxLogLevelDebug), AgentLevel: helpers.GetPointer(ngfAPIv1alpha2.AgentLogLevelDebug), @@ -321,6 +323,8 @@ func TestBuildNginxResourceObjects_NginxProxyConfig(t *testing.T) { g.Expect(svc.Spec.LoadBalancerIP).To(Equal("1.2.3.4")) g.Expect(*svc.Spec.LoadBalancerClass).To(Equal("myLoadBalancerClass")) g.Expect(svc.Spec.LoadBalancerSourceRanges).To(Equal([]string{"5.6.7.8"})) + g.Expect(*svc.Spec.IPFamilyPolicy).To(Equal(corev1.IPFamilyPolicySingleStack)) + g.Expect(svc.Spec.IPFamilies).To(Equal([]corev1.IPFamily{corev1.IPv4Protocol})) depObj := objects[5] dep, ok := depObj.(*appsv1.Deployment) From 7a6fd13b9f7ab3bc1b6c4bea82e56c26398a492f Mon Sep 17 00:00:00 2001 From: Saylor Berman Date: Fri, 6 Jun 2025 12:02:41 -0600 Subject: [PATCH 2/2] Add unit test --- .../controller/provisioner/objects_test.go | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/internal/controller/provisioner/objects_test.go b/internal/controller/provisioner/objects_test.go index 20382ff761..96710f8902 100644 --- a/internal/controller/provisioner/objects_test.go +++ b/internal/controller/provisioner/objects_test.go @@ -965,3 +965,40 @@ func TestBuildNginxResourceObjectsForDeletion_OpenShift(t *testing.T) { g.Expect(ok).To(BeTrue()) validateMeta(roleBinding, deploymentNSName.Name) } + +func TestSetIPFamily(t *testing.T) { + t.Parallel() + g := NewWithT(t) + + newSvc := func() *corev1.Service { + return &corev1.Service{ + Spec: corev1.ServiceSpec{}, + } + } + + // nProxyCfg is nil, should not set anything + svc := newSvc() + setIPFamily(nil, svc) + g.Expect(svc.Spec.IPFamilyPolicy).To(BeNil()) + g.Expect(svc.Spec.IPFamilies).To(BeNil()) + + // nProxyCfg.IPFamily is nil, should not set anything + svc = newSvc() + setIPFamily(&graph.EffectiveNginxProxy{}, svc) + g.Expect(svc.Spec.IPFamilyPolicy).To(BeNil()) + g.Expect(svc.Spec.IPFamilies).To(BeNil()) + + // nProxyCfg.IPFamily is IPv4, should set SingleStack and IPFamilies to IPv4 + svc = newSvc() + ipFamily := ngfAPIv1alpha2.IPv4 + setIPFamily(&graph.EffectiveNginxProxy{IPFamily: &ipFamily}, svc) + g.Expect(svc.Spec.IPFamilyPolicy).To(Equal(helpers.GetPointer(corev1.IPFamilyPolicySingleStack))) + g.Expect(svc.Spec.IPFamilies).To(Equal([]corev1.IPFamily{corev1.IPv4Protocol})) + + // nProxyCfg.IPFamily is IPv6, should set SingleStack and IPFamilies to IPv6 + svc = newSvc() + ipFamily = ngfAPIv1alpha2.IPv6 + setIPFamily(&graph.EffectiveNginxProxy{IPFamily: &ipFamily}, svc) + g.Expect(svc.Spec.IPFamilyPolicy).To(Equal(helpers.GetPointer(corev1.IPFamilyPolicySingleStack))) + g.Expect(svc.Spec.IPFamilies).To(Equal([]corev1.IPFamily{corev1.IPv6Protocol})) +}