diff --git a/CHANGELOG.md b/CHANGELOG.md index e5e646ca0..adbeb030d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -63,6 +63,8 @@ [#1409](https://github.com/Kong/gateway-operator/pull/1409) - Support `NodePort` as ingress service type for `DataPlane` [#1430](https://github.com/Kong/gateway-operator/pull/1430) +- Allow setting `NodePort` port number for ingress service for `DataPlane`. + [#1516](https://github.com/Kong/gateway-operator/pull/1516) - Bump default `DataPlane` image to 3.10 Default image changes from `kong` to `kong/kong-gateway`. [#1405](https://github.com/Kong/gateway-operator/pull/1405) diff --git a/config/samples/dataplane-with-nodeport.yaml b/config/samples/dataplane-with-nodeport.yaml new file mode 100644 index 000000000..1a4ea7742 --- /dev/null +++ b/config/samples/dataplane-with-nodeport.yaml @@ -0,0 +1,42 @@ +apiVersion: gateway-operator.konghq.com/v1beta1 +kind: DataPlane +metadata: + name: dataplane-node-port +spec: + deployment: + podTemplateSpec: + spec: + containers: + - name: proxy + # renovate: datasource=docker versioning=docker + image: kong/kong-gateway:3.10 + env: + - name: KONG_LOG_LEVEL + value: debug + - name: KONG_PROXY_LISTEN + value: 0.0.0.0:9000 reuseport backlog=16384 + - name: KONG_PORT_MAPS + value: 8080:9000 + resources: + requests: + memory: "64Mi" + cpu: "250m" + limits: + memory: "1024Mi" + cpu: "1000m" + readinessProbe: + initialDelaySeconds: 1 + periodSeconds: 1 + network: + services: + ingress: + annotations: + foo: bar + ports: + - name: http + port: 8080 + targetPort: 9000 + - name: http + port: 8083 + targetPort: 9000 + nodePort: 30083 diff --git a/controller/dataplane/owned_resources.go b/controller/dataplane/owned_resources.go index b60fb5831..3fb78bc3c 100644 --- a/controller/dataplane/owned_resources.go +++ b/controller/dataplane/owned_resources.go @@ -364,11 +364,7 @@ func ensureIngressServiceForDataPlane( existingService.Spec.Selector = generatedService.Spec.Selector updated = true } - if !cmp.Equal(generatedService.Spec.Ports, existingService.Spec.Ports, cmp.FilterPath(func(p cmp.Path) bool { - // We need to check all the service values but the NodePort, as this field is assigned by - // the K8S controlplane components. - return p.Last().String() == ".NodePort" - }, cmp.Ignore())) { + if !comparePorts(existingService.Spec.Ports, generatedService.Spec.Ports, dataPlane) { existingService.Spec.Ports = generatedService.Spec.Ports updated = true } @@ -385,3 +381,44 @@ func ensureIngressServiceForDataPlane( return op.Created, generatedService, cl.Create(ctx, generatedService) } + +// comparePorts compares the ports of two services. +// It returns true if the ports are equal, ignoring the NodePort field +// for the ingress service if it was not set in the dataplane spec. +func comparePorts( + a, b []corev1.ServicePort, + dataPlane *operatorv1beta1.DataPlane, +) bool { + if len(a) != len(b) { + return false + } + for i := range a { + if a[i].Name != b[i].Name { + return false + } + if a[i].Protocol != b[i].Protocol { + return false + } + if a[i].Port != b[i].Port { + return false + } + if a[i].TargetPort != b[i].TargetPort { + return false + } + if a[i].AppProtocol != b[i].AppProtocol { + return false + } + + if a[i].NodePort != b[i].NodePort { + if dataPlane != nil && + dataPlane.Spec.Network.Services != nil && + dataPlane.Spec.Network.Services.Ingress != nil && + len(dataPlane.Spec.Network.Services.Ingress.Ports) > i && + dataPlane.Spec.Network.Services.Ingress.Ports[i].NodePort != 0 { + return false + } + } + + } + return true +} diff --git a/controller/dataplane/owned_resources_test.go b/controller/dataplane/owned_resources_test.go index f15be2506..165cca8a9 100644 --- a/controller/dataplane/owned_resources_test.go +++ b/controller/dataplane/owned_resources_test.go @@ -331,3 +331,76 @@ func TestEnsureIngressServiceForDataPlane(t *testing.T) { }) } } + +func TestComparePorts(t *testing.T) { + testCases := []struct { + name string + a []corev1.ServicePort + b []corev1.ServicePort + dataPlane *operatorv1beta1.DataPlane + expected bool + }{ + { + name: "should return true when NodePort differs but not specified in DataPlane spec", + a: []corev1.ServicePort{{Name: "port-80", Port: 80, NodePort: 30080}}, + b: []corev1.ServicePort{{Name: "port-80", Port: 80, NodePort: 30081}}, + dataPlane: builder.NewDataPlaneBuilder(). + WithIngressServicePorts([]operatorv1beta1.DataPlaneServicePort{ + { + Name: "http", + Port: 80, + TargetPort: intstr.FromInt(8000), + // NodePort not specified + }, + }).Build(), + expected: true, + }, + { + name: "should return false when NodePort differs and is specified in DataPlane spec", + a: []corev1.ServicePort{{Name: "port-80", Port: 80, NodePort: 30080}}, + b: []corev1.ServicePort{{Name: "port-80", Port: 80, NodePort: 30081}}, + dataPlane: builder.NewDataPlaneBuilder(). + WithIngressServicePorts([]operatorv1beta1.DataPlaneServicePort{ + { + Name: "http", + Port: 80, + TargetPort: intstr.FromInt(8000), + NodePort: 30080, + }, + }).Build(), + expected: false, + }, + { + name: "should return true when multiple ports match except NodePort which is not specified", + a: []corev1.ServicePort{ + {Name: "port-80", Port: 80, NodePort: 30080}, + {Name: "port-443", Port: 443, NodePort: 30443}, + }, + b: []corev1.ServicePort{ + {Name: "port-80", Port: 80, NodePort: 30081}, + {Name: "port-443", Port: 443, NodePort: 30444}, + }, + dataPlane: builder.NewDataPlaneBuilder(). + WithIngressServicePorts([]operatorv1beta1.DataPlaneServicePort{ + { + Name: "http", + Port: 80, + TargetPort: intstr.FromInt(8000), + }, + { + Name: "https", + Port: 443, + TargetPort: intstr.FromInt(8443), + }, + }).Build(), + expected: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + actual := comparePorts(tc.a, tc.b, tc.dataPlane) + require.Equal(t, tc.expected, actual) + }) + } +} diff --git a/go.mod b/go.mod index 0c1d9bb76..4ade928da 100644 --- a/go.mod +++ b/go.mod @@ -259,7 +259,7 @@ require ( replace ( k8s.io/api => k8s.io/api v0.32.3 k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.32.3 - k8s.io/apimachinery => k8s.io/apimachinery v0.32.3 + k8s.io/apimachinery => k8s.io/apimachinery v0.32.4 k8s.io/apiserver => k8s.io/apiserver v0.32.3 k8s.io/cli-runtime => k8s.io/cli-runtime v0.32.3 k8s.io/client-go => k8s.io/client-go v0.32.3 @@ -269,12 +269,12 @@ replace ( k8s.io/component-base => k8s.io/component-base v0.32.3 k8s.io/component-helpers => k8s.io/component-helpers v0.32.3 k8s.io/controller-manager => k8s.io/controller-manager v0.32.3 - k8s.io/cri-api => k8s.io/cri-api v0.32.3 + k8s.io/cri-api => k8s.io/cri-api v0.32.4 k8s.io/cri-client => k8s.io/cri-client v0.32.3 k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.32.3 k8s.io/dynamic-resource-allocation => k8s.io/dynamic-resource-allocation v0.32.3 k8s.io/endpointslice => k8s.io/endpointslice v0.32.3 - k8s.io/externaljwt => k8s.io/externaljwt v0.32.3 + k8s.io/externaljwt => k8s.io/externaljwt v0.32.4 k8s.io/kms => k8s.io/kms v0.32.3 k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.32.3 k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.32.3 @@ -284,7 +284,7 @@ replace ( k8s.io/kubelet => k8s.io/kubelet v0.32.3 k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.30.3 k8s.io/metrics => k8s.io/metrics v0.32.3 - k8s.io/mount-utils => k8s.io/mount-utils v0.32.3 + k8s.io/mount-utils => k8s.io/mount-utils v0.32.4 k8s.io/pod-security-admission => k8s.io/pod-security-admission v0.32.3 k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.32.3 k8s.io/sample-cli-plugin => k8s.io/sample-cli-plugin v0.32.3 diff --git a/go.sum b/go.sum index 30bcb93ba..d5640f130 100644 --- a/go.sum +++ b/go.sum @@ -686,8 +686,8 @@ k8s.io/api v0.32.3 h1:Hw7KqxRusq+6QSplE3NYG4MBxZw1BZnq4aP4cJVINls= k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k= k8s.io/apiextensions-apiserver v0.32.3 h1:4D8vy+9GWerlErCwVIbcQjsWunF9SUGNu7O7hiQTyPY= k8s.io/apiextensions-apiserver v0.32.3/go.mod h1:8YwcvVRMVzw0r1Stc7XfGAzB/SIVLunqApySV5V7Dss= -k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U= -k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= +k8s.io/apimachinery v0.32.4 h1:8EEksaxA7nd7xWJkkwLDN4SvWS5ot9g6Z/VZb3ju25I= +k8s.io/apimachinery v0.32.4/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= k8s.io/apiserver v0.32.3 h1:kOw2KBuHOA+wetX1MkmrxgBr648ksz653j26ESuWNY8= k8s.io/apiserver v0.32.3/go.mod h1:q1x9B8E/WzShF49wh3ADOh6muSfpmFL0I2t+TG0Zdgc= k8s.io/client-go v0.32.3 h1:RKPVltzopkSgHS7aS98QdscAgtgah/+zmpAogooIqVU= diff --git a/pkg/utils/kubernetes/resources/services.go b/pkg/utils/kubernetes/resources/services.go index b3c5260c6..df205bbbb 100644 --- a/pkg/utils/kubernetes/resources/services.go +++ b/pkg/utils/kubernetes/resources/services.go @@ -158,6 +158,7 @@ func ServicePortsFromDataPlaneIngressOpt(dataplane *operatorv1beta1.DataPlane) S Protocol: corev1.ProtocolTCP, // Currently, only TCP protocol supported. Port: p.Port, TargetPort: targetPort, + NodePort: p.NodePort, }) alreadyUsedPorts[p.Port] = struct{}{} }