From dd20d7b4b84eb3670da23004f68679e6e3ea8416 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patryk=20Ma=C5=82ek?= Date: Wed, 24 May 2023 18:43:10 +0200 Subject: [PATCH] wip: add --gateway-discovery-dns-strategy flag --- CHANGELOG.md | 18 +- internal/adminapi/endpoints.go | 110 ++++++-- internal/adminapi/endpoints_envtest_test.go | 11 +- internal/adminapi/endpoints_test.go | 267 +++++++++--------- .../configuration/kongadminapi_controller.go | 18 +- .../kongadminapi_controller_envtest_test.go | 53 +--- internal/dataplane/parser/parser_test.go | 59 +--- internal/manager/config.go | 24 +- internal/manager/config/types/dnsstrategy.go | 45 +++ internal/manager/config_validation.go | 9 + internal/manager/controllerdef.go | 1 + internal/manager/setup.go | 2 +- internal/util/builder/endpointport.go | 43 +++ test/integration/webhook_test.go | 10 +- 14 files changed, 388 insertions(+), 282 deletions(-) create mode 100644 internal/manager/config/types/dnsstrategy.go create mode 100644 internal/util/builder/endpointport.go diff --git a/CHANGELOG.md b/CHANGELOG.md index fee7806bf4..3e6a24cefe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -103,7 +103,7 @@ Adding a new version? You'll need three changes: [#3963](https://github.com/Kong/kubernetes-ingress-controller/pull/3963) - Added translator to translate `HTTPRoute` and `GRPCRoute` in gateway APIs to expression based kong routes. Similar to ingresses, this translator is only - enabled when feature gate `ExpressionRoutes` is turned on and the managed + enabled when feature gate `ExpressionRoutes` is turned on and the managed Kong gateway runs in router flavor `expressions`. [#3956](https://github.com/Kong/kubernetes-ingress-controller/pull/3956) [#3988](https://github.com/Kong/kubernetes-ingress-controller/pull/3988) @@ -125,13 +125,25 @@ Adding a new version? You'll need three changes: Runtime Group Admin API. [#4029](https://github.com/Kong/kubernetes-ingress-controller/pull/4029) - Record an event attached to KIC pod after applying configuration to Kong. If - the applying succeeded, a `Normal` event with `KongConfigurationSucceeded` - reason is recorded. If the applying failed, a `Warning` event with + the applying succeeded, a `Normal` event with `KongConfigurationSucceeded` + reason is recorded. If the applying failed, a `Warning` event with `KongConfigurationApplyFailed` reason is recorded. [#4054](https://github.com/Kong/kubernetes-ingress-controller/pull/4054) - Disable translation to expression routes when feature gate `ExpressionRoutes` is enabled but feature gate `CombinedRoutes` is not enabled. [#4057](https://github.com/Kong/kubernetes-ingress-controller/pull/4057) +- Added `--gateway-discovery-dns-strategy` flag which allows specifying which + DNS strategy to use when generating Gateway's Admin API addresses. + [#4071](https://github.com/Kong/kubernetes-ingress-controller/pull/4071) + + There are 3 options available + - `ip` (default): which will make KIC create Admin API addresses built out of + IP addresses. + - `pod`: will make KIC build addresses using the following template: + `pod-ip-address.my-namespace.pod`. + - `service`: will make KIC build addresses using the following template: + `pod-ip-address.service-name.my-namespace.svc`. + This is known to not work on GKE becuase it uses `kube-dns` instead of coredns. ### Changed diff --git a/internal/adminapi/endpoints.go b/internal/adminapi/endpoints.go index 27fdcac640..a0a9bdd417 100644 --- a/internal/adminapi/endpoints.go +++ b/internal/adminapi/endpoints.go @@ -11,6 +11,8 @@ import ( k8stypes "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/sets" "sigs.k8s.io/controller-runtime/pkg/client" + + cfgtypes "github.com/kong/kubernetes-ingress-controller/v2/internal/manager/config/types" ) // DiscoveredAdminAPI represents an Admin API discovered from a Kubernetes Service. @@ -27,6 +29,7 @@ func GetAdminAPIsForService( kubeClient client.Client, service k8stypes.NamespacedName, portNames sets.Set[string], + dnsStrategy cfgtypes.DNSStrategy, ) (sets.Set[DiscoveredAdminAPI], error) { const ( defaultEndpointSliceListPagingLimit = 100 @@ -55,7 +58,11 @@ func GetAdminAPIsForService( } for _, es := range endpointsList.Items { - addresses = addresses.Union(AdminAPIsFromEndpointSlice(es, portNames)) + adminAPI, err := AdminAPIsFromEndpointSlice(es, portNames, dnsStrategy) + if err != nil { + return nil, err + } + addresses = addresses.Union(adminAPI) } if endpointsList.Continue == "" { @@ -68,7 +75,11 @@ func GetAdminAPIsForService( // AdminAPIsFromEndpointSlice returns a list of Admin APIs when given // an EndpointSlice. -func AdminAPIsFromEndpointSlice(endpoints discoveryv1.EndpointSlice, portNames sets.Set[string]) sets.Set[DiscoveredAdminAPI] { +func AdminAPIsFromEndpointSlice( + endpoints discoveryv1.EndpointSlice, + portNames sets.Set[string], + dnsStrategy cfgtypes.DNSStrategy, +) (sets.Set[DiscoveredAdminAPI], error) { discoveredAdminAPIs := sets.New[DiscoveredAdminAPI]() for _, p := range endpoints.Ports { if p.Name == nil { @@ -96,42 +107,79 @@ func AdminAPIsFromEndpointSlice(endpoints discoveryv1.EndpointSlice, portNames s if e.TargetRef == nil || e.TargetRef.Kind != "Pod" { continue } - podNN := k8stypes.NamespacedName{ - Name: e.TargetRef.Name, - Namespace: e.TargetRef.Namespace, - } if len(e.Addresses) < 1 { continue } - // Endpoint's addresses are assumed to be fungible, therefore we pick only the first one. - // For the context please see the `Endpoint.Addresses` godoc. - addr := strings.ReplaceAll(e.Addresses[0], ".", "-") - - var adminAPI DiscoveredAdminAPI - // NOTE: We assume https here because the referenced Admin API - // server will live in another Pod/elsewhere so allowing http would - // not be considered best practice. - if serviceName == "" { - // If we couldn't find a service that's the owner of provided EndpointSlice - // then fallback to providing a DNS name for the Pod only. - adminAPI = DiscoveredAdminAPI{ - Address: fmt.Sprintf("https://%s.%s.pod:%d", - addr, endpoints.Namespace, *p.Port, - ), - PodRef: podNN, - } - } else { - adminAPI = DiscoveredAdminAPI{ - Address: fmt.Sprintf("https://%s.%s.%s.svc:%d", - addr, serviceName, endpoints.Namespace, *p.Port, - ), - PodRef: podNN, - } + svc := k8stypes.NamespacedName{ + Name: serviceName, + Namespace: endpoints.Namespace, + } + + adminAPI, err := adminAPIFromEndpoint(e, p, svc, dnsStrategy) + if err != nil { + return nil, err } discoveredAdminAPIs = discoveredAdminAPIs.Insert(adminAPI) } } - return discoveredAdminAPIs + return discoveredAdminAPIs, nil +} + +func adminAPIFromEndpoint( + endpoint discoveryv1.Endpoint, + port discoveryv1.EndpointPort, + service k8stypes.NamespacedName, + dnsStrategy cfgtypes.DNSStrategy, +) (DiscoveredAdminAPI, error) { + podNN := k8stypes.NamespacedName{ + Name: endpoint.TargetRef.Name, + Namespace: endpoint.TargetRef.Namespace, + } + + // NOTE: Endpoint's addresses are assumed to be fungible, therefore we pick + // only the first one. + // For the context please see the `Endpoint.Addresses` godoc. + eAddress := endpoint.Addresses[0] + + // NOTE: We assume https below because the referenced Admin API + // server will live in another Pod/elsewhere so allowing http would + // not be considered best practice. + + switch dnsStrategy { + case cfgtypes.ServiceScopedPodDNSStrategy: + if service.Name == "" { + return DiscoveredAdminAPI{}, fmt.Errorf( + "service name is empty for an endpoint with TargetRef %s/%s", + endpoint.TargetRef.Namespace, endpoint.TargetRef.Name, + ) + } + + ipAddr := strings.ReplaceAll(eAddress, ".", "-") + address := fmt.Sprintf("%s.%s.%s.svc", ipAddr, service.Name, service.Namespace) + + return DiscoveredAdminAPI{ + Address: fmt.Sprintf("https://%s:%d", address, *port.Port), + PodRef: podNN, + }, nil + + case cfgtypes.NamespaceScopedPodDNSStrategy: + ipAddr := strings.ReplaceAll(eAddress, ".", "-") + address := fmt.Sprintf("%s.%s.pod", ipAddr, service.Namespace) + + return DiscoveredAdminAPI{ + Address: fmt.Sprintf("https://%s:%d", address, *port.Port), + PodRef: podNN, + }, nil + + case cfgtypes.IPDNSStrategy: + return DiscoveredAdminAPI{ + Address: fmt.Sprintf("https://%s:%d", eAddress, *port.Port), + PodRef: podNN, + }, nil + + default: + return DiscoveredAdminAPI{}, fmt.Errorf("unknown dns strategy: %s", dnsStrategy) + } } diff --git a/internal/adminapi/endpoints_envtest_test.go b/internal/adminapi/endpoints_envtest_test.go index f07de2aa49..d83e332618 100644 --- a/internal/adminapi/endpoints_envtest_test.go +++ b/internal/adminapi/endpoints_envtest_test.go @@ -20,6 +20,8 @@ import ( ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" "github.com/kong/kubernetes-ingress-controller/v2/internal/adminapi" + cfgtypes "github.com/kong/kubernetes-ingress-controller/v2/internal/manager/config/types" + "github.com/kong/kubernetes-ingress-controller/v2/internal/util/builder" "github.com/kong/kubernetes-ingress-controller/v2/test/envtest" ) @@ -88,18 +90,13 @@ func TestGetAdminAPIsForServiceReturnsAllAddressesCorrectlyPagingThroughResults( TargetRef: testPodReference("pod-1", ns.Name), }, }, - Ports: []discoveryv1.EndpointPort{ - { - Name: lo.ToPtr("admin"), - Port: lo.ToPtr(int32(8444)), - }, - }, + Ports: builder.NewEndpointPort(8444).WithName("admin").IntoSlice(), } require.NoError(t, client.Create(ctx, &es)) } } - got, err := adminapi.GetAdminAPIsForService(ctx, client, service, sets.New("admin")) + got, err := adminapi.GetAdminAPIsForService(ctx, client, service, sets.New("admin"), cfgtypes.IPDNSStrategy) require.NoError(t, err) require.Len(t, got, tc.subnetD*tc.subnetC, "GetAdminAPIsForService should return all valid addresses") }) diff --git a/internal/adminapi/endpoints_test.go b/internal/adminapi/endpoints_test.go index 1b8702132b..35fafea61c 100644 --- a/internal/adminapi/endpoints_test.go +++ b/internal/adminapi/endpoints_test.go @@ -2,6 +2,8 @@ package adminapi import ( "context" + "errors" + "fmt" "testing" "github.com/google/uuid" @@ -13,7 +15,10 @@ import ( k8stypes "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/sets" "sigs.k8s.io/controller-runtime/pkg/client" - fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + + cfgtypes "github.com/kong/kubernetes-ingress-controller/v2/internal/manager/config/types" + "github.com/kong/kubernetes-ingress-controller/v2/internal/util/builder" ) func TestAddressesFromEndpointSlice(t *testing.T) { @@ -41,10 +46,12 @@ func TestAddressesFromEndpointSlice(t *testing.T) { ) tests := []struct { - name string - endpoints discoveryv1.EndpointSlice - want sets.Set[DiscoveredAdminAPI] - portNames sets.Set[string] + name string + endpoints discoveryv1.EndpointSlice + want sets.Set[DiscoveredAdminAPI] + portNames sets.Set[string] + dnsStrategy cfgtypes.DNSStrategy + expectedErr error }{ { name: "basic", @@ -61,27 +68,51 @@ func TestAddressesFromEndpointSlice(t *testing.T) { TargetRef: testPodReference(namespaceName, "pod-1"), }, }, - Ports: []discoveryv1.EndpointPort{ + Ports: builder.NewEndpointPort(8444).WithName("admin").IntoSlice(), + }, + portNames: sets.New("admin"), + want: sets.New( + DiscoveredAdminAPI{ + Address: "https://10-0-0-1.ns.pod:8444", + PodRef: k8stypes.NamespacedName{ + Name: "pod-1", Namespace: namespaceName, + }, + }, + ), + dnsStrategy: cfgtypes.NamespaceScopedPodDNSStrategy, + }, + { + name: "basic", + endpoints: discoveryv1.EndpointSlice{ + ObjectMeta: endpointsSliceObjectMeta, + AddressType: discoveryv1.AddressTypeIPv4, + Endpoints: []discoveryv1.Endpoint{ { - Name: lo.ToPtr("admin"), - Port: lo.ToPtr(int32(8444)), + Addresses: []string{"10.0.0.1", "10.0.0.2"}, + Conditions: discoveryv1.EndpointConditions{ + Ready: lo.ToPtr(true), + Terminating: lo.ToPtr(false), + }, + TargetRef: testPodReference(namespaceName, "pod-1"), }, }, + Ports: builder.NewEndpointPort(8444).WithName("admin").IntoSlice(), }, portNames: sets.New("admin"), want: sets.New( DiscoveredAdminAPI{ - Address: "https://10-0-0-1.ns.pod:8444", + Address: "https://10.0.0.1:8444", PodRef: k8stypes.NamespacedName{ Name: "pod-1", Namespace: namespaceName, }, }, ), + dnsStrategy: cfgtypes.IPDNSStrategy, }, { - name: "basic with owner reference", + name: "basic", endpoints: discoveryv1.EndpointSlice{ - ObjectMeta: endpointsSliceWithOwnerReferenceObjectMeta, + ObjectMeta: endpointsSliceObjectMeta, AddressType: discoveryv1.AddressTypeIPv4, Endpoints: []discoveryv1.Endpoint{ { @@ -93,12 +124,36 @@ func TestAddressesFromEndpointSlice(t *testing.T) { TargetRef: testPodReference(namespaceName, "pod-1"), }, }, - Ports: []discoveryv1.EndpointPort{ + Ports: builder.NewEndpointPort(8444).WithName("admin").IntoSlice(), + }, + portNames: sets.New("admin"), + want: sets.New( + DiscoveredAdminAPI{ + Address: "https://10.0.0.1:8444", + PodRef: k8stypes.NamespacedName{ + Name: "pod-1", Namespace: namespaceName, + }, + }, + ), + dnsStrategy: cfgtypes.ServiceScopedPodDNSStrategy, + expectedErr: errors.New("service name is empty for an endpoint with TargetRef ns/pod-1"), + }, + { + name: "basic with owner reference", + endpoints: discoveryv1.EndpointSlice{ + ObjectMeta: endpointsSliceWithOwnerReferenceObjectMeta, + AddressType: discoveryv1.AddressTypeIPv4, + Endpoints: []discoveryv1.Endpoint{ { - Name: lo.ToPtr("admin"), - Port: lo.ToPtr(int32(8444)), + Addresses: []string{"10.0.0.1", "10.0.0.2"}, + Conditions: discoveryv1.EndpointConditions{ + Ready: lo.ToPtr(true), + Terminating: lo.ToPtr(false), + }, + TargetRef: testPodReference(namespaceName, "pod-1"), }, }, + Ports: builder.NewEndpointPort(8444).WithName("admin").IntoSlice(), }, portNames: sets.New("admin"), want: sets.New( @@ -109,6 +164,7 @@ func TestAddressesFromEndpointSlice(t *testing.T) { }, }, ), + dnsStrategy: cfgtypes.ServiceScopedPodDNSStrategy, }, { name: "not ready endpoints are not returned", @@ -125,15 +181,11 @@ func TestAddressesFromEndpointSlice(t *testing.T) { TargetRef: testPodReference(namespaceName, "pod-1"), }, }, - Ports: []discoveryv1.EndpointPort{ - { - Name: lo.ToPtr("admin"), - Port: lo.ToPtr(int32(8444)), - }, - }, + Ports: builder.NewEndpointPort(8444).WithName("admin").IntoSlice(), }, - portNames: sets.New("admin"), - want: sets.New[DiscoveredAdminAPI](), + portNames: sets.New("admin"), + want: sets.New[DiscoveredAdminAPI](), + dnsStrategy: cfgtypes.IPDNSStrategy, }, { name: "not ready and terminating endpoints are not returned", @@ -153,15 +205,11 @@ func TestAddressesFromEndpointSlice(t *testing.T) { TargetRef: testPodReference(namespaceName, "pod-1"), }, }, - Ports: []discoveryv1.EndpointPort{ - { - Name: lo.ToPtr("admin"), - Port: lo.ToPtr(int32(8444)), - }, - }, + Ports: builder.NewEndpointPort(8444).WithName("admin").IntoSlice(), }, - portNames: sets.New("admin"), - want: sets.New[DiscoveredAdminAPI](), + portNames: sets.New("admin"), + want: sets.New[DiscoveredAdminAPI](), + dnsStrategy: cfgtypes.IPDNSStrategy, }, { name: "multiple endpoints are concatenated properly", @@ -194,12 +242,7 @@ func TestAddressesFromEndpointSlice(t *testing.T) { TargetRef: testPodReference(namespaceName, "pod-3"), }, }, - Ports: []discoveryv1.EndpointPort{ - { - Name: lo.ToPtr("admin"), - Port: lo.ToPtr(int32(8444)), - }, - }, + Ports: builder.NewEndpointPort(8444).WithName("admin").IntoSlice(), }, portNames: sets.New("admin"), want: sets.New( @@ -218,6 +261,7 @@ func TestAddressesFromEndpointSlice(t *testing.T) { }, }, ), + dnsStrategy: cfgtypes.NamespaceScopedPodDNSStrategy, }, { name: "multiple endpoints with owner reference are concatenated properly", @@ -250,12 +294,7 @@ func TestAddressesFromEndpointSlice(t *testing.T) { TargetRef: testPodReference(namespaceName, "pod-3"), }, }, - Ports: []discoveryv1.EndpointPort{ - { - Name: lo.ToPtr("admin"), - Port: lo.ToPtr(int32(8444)), - }, - }, + Ports: builder.NewEndpointPort(8444).WithName("admin").IntoSlice(), }, portNames: sets.New("admin"), want: sets.New( @@ -274,6 +313,7 @@ func TestAddressesFromEndpointSlice(t *testing.T) { }, }, ), + dnsStrategy: cfgtypes.ServiceScopedPodDNSStrategy, }, { name: "ports not called 'admin' are not added", @@ -306,17 +346,13 @@ func TestAddressesFromEndpointSlice(t *testing.T) { TargetRef: testPodReference(namespaceName, "pod-3"), }, }, - Ports: []discoveryv1.EndpointPort{ - { - Name: lo.ToPtr("non-admin-port-name"), - Port: lo.ToPtr(int32(8444)), - }, - }, + Ports: builder.NewEndpointPort(8444).WithName("non-admin-port-name").IntoSlice(), }, - want: sets.New[DiscoveredAdminAPI](), + want: sets.New[DiscoveredAdminAPI](), + dnsStrategy: cfgtypes.IPDNSStrategy, }, { - name: "ports without names are not taken into account ", + name: "ports without names are not taken into account", endpoints: discoveryv1.EndpointSlice{ ObjectMeta: endpointsSliceObjectMeta, AddressType: discoveryv1.AddressTypeIPv4, @@ -330,14 +366,11 @@ func TestAddressesFromEndpointSlice(t *testing.T) { TargetRef: testPodReference(namespaceName, "pod-1"), }, }, - Ports: []discoveryv1.EndpointPort{ - { - Port: lo.ToPtr(int32(8444)), - }, - }, + Ports: builder.NewEndpointPort(8444).IntoSlice(), }, - portNames: sets.New("admin"), - want: sets.New[DiscoveredAdminAPI](), + portNames: sets.New("admin"), + want: sets.New[DiscoveredAdminAPI](), + dnsStrategy: cfgtypes.IPDNSStrategy, }, { name: "multiple ports names", @@ -355,14 +388,8 @@ func TestAddressesFromEndpointSlice(t *testing.T) { }, }, Ports: []discoveryv1.EndpointPort{ - { - Name: lo.ToPtr("admin-tls"), - Port: lo.ToPtr(int32(8443)), - }, - { - Name: lo.ToPtr("admin"), - Port: lo.ToPtr(int32(8444)), - }, + builder.NewEndpointPort(8443).WithName("admin-tls").Build(), + builder.NewEndpointPort(8444).WithName("admin").Build(), }, }, portNames: sets.New("admin", "admin-tls"), @@ -382,9 +409,10 @@ func TestAddressesFromEndpointSlice(t *testing.T) { }, }, ), + dnsStrategy: cfgtypes.NamespaceScopedPodDNSStrategy, }, { - name: "endpoints with no target ref are ignored", + name: "endpoints with no target ref return error for service scopec dns strategy", endpoints: discoveryv1.EndpointSlice{ ObjectMeta: endpointsSliceObjectMeta, AddressType: discoveryv1.AddressTypeIPv4, @@ -398,15 +426,11 @@ func TestAddressesFromEndpointSlice(t *testing.T) { TargetRef: nil, }, }, - Ports: []discoveryv1.EndpointPort{ - { - Name: lo.ToPtr("admin"), - Port: lo.ToPtr(int32(8444)), - }, - }, + Ports: builder.NewEndpointPort(8444).WithName("admin").IntoSlice(), }, - portNames: sets.New("admin"), - want: sets.New[DiscoveredAdminAPI](), + portNames: sets.New("admin"), + want: sets.New[DiscoveredAdminAPI](), + dnsStrategy: cfgtypes.IPDNSStrategy, }, { name: "endpoints with target ref other than Pod are ignored", @@ -423,21 +447,25 @@ func TestAddressesFromEndpointSlice(t *testing.T) { TargetRef: &corev1.ObjectReference{Kind: "Node", Namespace: namespaceName, Name: "node-1"}, }, }, - Ports: []discoveryv1.EndpointPort{ - { - Name: lo.ToPtr("admin"), - Port: lo.ToPtr(int32(8444)), - }, - }, + Ports: builder.NewEndpointPort(8444).WithName("admin").IntoSlice(), }, - portNames: sets.New("admin"), - want: sets.New[DiscoveredAdminAPI](), + portNames: sets.New("admin"), + want: sets.New[DiscoveredAdminAPI](), + dnsStrategy: cfgtypes.IPDNSStrategy, }, } for _, tt := range tests { tt := tt - t.Run(tt.name, func(t *testing.T) { - require.Equal(t, tt.want, AdminAPIsFromEndpointSlice(tt.endpoints, tt.portNames)) + + t.Run(fmt.Sprintf("dnsstrategy_%s/%s", tt.dnsStrategy, tt.name), func(t *testing.T) { + require.NoError(t, tt.dnsStrategy.Validate()) + + adminAPI, err := AdminAPIsFromEndpointSlice(tt.endpoints, tt.portNames, tt.dnsStrategy) + if tt.expectedErr != nil { + require.EqualError(t, err, tt.expectedErr.Error()) + } else { + require.Equal(t, tt.want, adminAPI) + } }) } } @@ -459,11 +487,12 @@ func TestGetAdminAPIsForService(t *testing.T) { ) tests := []struct { - name string - service k8stypes.NamespacedName - objects []client.ObjectList - want sets.Set[DiscoveredAdminAPI] - wantErr bool + name string + service k8stypes.NamespacedName + objects []client.ObjectList + dnsStrategy cfgtypes.DNSStrategy + want sets.Set[DiscoveredAdminAPI] + wantErr bool }{ { name: "basic", @@ -487,12 +516,7 @@ func TestGetAdminAPIsForService(t *testing.T) { TargetRef: testPodReference(namespaceName, "pod-1"), }, }, - Ports: []discoveryv1.EndpointPort{ - { - Name: lo.ToPtr("admin"), - Port: lo.ToPtr(int32(8444)), - }, - }, + Ports: builder.NewEndpointPort(8444).WithName("admin").IntoSlice(), }, }, }, @@ -511,12 +535,7 @@ func TestGetAdminAPIsForService(t *testing.T) { TargetRef: testPodReference(namespaceName, "pod-2"), }, }, - Ports: []discoveryv1.EndpointPort{ - { - Name: lo.ToPtr("admin"), - Port: lo.ToPtr(int32(8444)), - }, - }, + Ports: builder.NewEndpointPort(8444).WithName("admin").IntoSlice(), }, }, }, @@ -535,12 +554,7 @@ func TestGetAdminAPIsForService(t *testing.T) { TargetRef: testPodReference(namespaceName, "pod-3"), }, }, - Ports: []discoveryv1.EndpointPort{ - { - Name: lo.ToPtr("admin"), - Port: lo.ToPtr(int32(8444)), - }, - }, + Ports: builder.NewEndpointPort(8444).WithName("admin").IntoSlice(), }, }, }, @@ -559,6 +573,7 @@ func TestGetAdminAPIsForService(t *testing.T) { }, }, ), + dnsStrategy: cfgtypes.NamespaceScopedPodDNSStrategy, }, { name: "ports not matching the specified port names are not taken into account", @@ -582,17 +597,13 @@ func TestGetAdminAPIsForService(t *testing.T) { TargetRef: testPodReference(namespaceName, "pod-1"), }, }, - Ports: []discoveryv1.EndpointPort{ - { - Name: lo.ToPtr("non-admin-port"), - Port: lo.ToPtr(int32(8444)), - }, - }, + Ports: builder.NewEndpointPort(8444).WithName("non-admin-port").IntoSlice(), }, }, }, }, - want: sets.New[DiscoveredAdminAPI](), + want: sets.New[DiscoveredAdminAPI](), + dnsStrategy: cfgtypes.IPDNSStrategy, }, { name: "Endpoints without a TargetRef are not matched", @@ -615,17 +626,13 @@ func TestGetAdminAPIsForService(t *testing.T) { }, }, }, - Ports: []discoveryv1.EndpointPort{ - { - Name: lo.ToPtr("admin"), - Port: lo.ToPtr(int32(8444)), - }, - }, + Ports: builder.NewEndpointPort(8444).WithName("admin").IntoSlice(), }, }, }, }, - want: sets.New[DiscoveredAdminAPI](), + want: sets.New[DiscoveredAdminAPI](), + dnsStrategy: cfgtypes.IPDNSStrategy, }, { name: "not Ready Endpoints are not matched", @@ -648,28 +655,26 @@ func TestGetAdminAPIsForService(t *testing.T) { TargetRef: testPodReference(namespaceName, "pod-1"), }, }, - Ports: []discoveryv1.EndpointPort{ - { - Name: lo.ToPtr("admin"), - Port: lo.ToPtr(int32(8444)), - }, - }, + Ports: builder.NewEndpointPort(8444).WithName("admin").IntoSlice(), }, }, }, }, - want: sets.New[DiscoveredAdminAPI](), + want: sets.New[DiscoveredAdminAPI](), + dnsStrategy: cfgtypes.IPDNSStrategy, }, } for _, tt := range tests { tt := tt - t.Run(tt.name, func(t *testing.T) { - fakeClient := fakeclient.NewClientBuilder(). + t.Run(fmt.Sprintf("dnsstrategy_%s/%s", tt.dnsStrategy, tt.name), func(t *testing.T) { + require.NoError(t, tt.dnsStrategy.Validate()) + + fakeClient := fake.NewClientBuilder(). WithLists(tt.objects...). Build() portNames := sets.New("admin") - got, err := GetAdminAPIsForService(context.Background(), fakeClient, tt.service, portNames) + got, err := GetAdminAPIsForService(context.Background(), fakeClient, tt.service, portNames, tt.dnsStrategy) if tt.wantErr { require.Error(t, err) return diff --git a/internal/controllers/configuration/kongadminapi_controller.go b/internal/controllers/configuration/kongadminapi_controller.go index 4580d82402..974214d649 100644 --- a/internal/controllers/configuration/kongadminapi_controller.go +++ b/internal/controllers/configuration/kongadminapi_controller.go @@ -2,6 +2,7 @@ package configuration import ( "context" + "fmt" "time" "github.com/go-logr/logr" @@ -20,6 +21,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/source" "github.com/kong/kubernetes-ingress-controller/v2/internal/adminapi" + cfgtypes "github.com/kong/kubernetes-ingress-controller/v2/internal/manager/config/types" "github.com/kong/kubernetes-ingress-controller/v2/internal/util" ) @@ -38,6 +40,7 @@ type KongAdminAPIServiceReconciler struct { // EndpointsNotifier is used to notify about Admin API endpoints changes. // We're going to call this only with endpoints when they change. EndpointsNotifier EndpointsNotifier + DNSStrategy cfgtypes.DNSStrategy Cache DiscoveredAdminAPIsCache } @@ -127,7 +130,13 @@ func (r *KongAdminAPIServiceReconciler) Reconcile(ctx context.Context, req ctrl. if !ok { // If we don't have an entry for this EndpointSlice then save it and notify // about the change. - r.Cache[req.NamespacedName] = adminapi.AdminAPIsFromEndpointSlice(endpoints, r.PortNames) + var err error + r.Cache[req.NamespacedName], err = adminapi.AdminAPIsFromEndpointSlice(endpoints, r.PortNames, r.DNSStrategy) + if err != nil { + return reconcile.Result{}, fmt.Errorf( + "failed getting Admin API from endpoints: %s/%s: %w", endpoints.Namespace, endpoints.Name, err, + ) + } r.notify() return ctrl.Result{}, nil } @@ -135,7 +144,12 @@ func (r *KongAdminAPIServiceReconciler) Reconcile(ctx context.Context, req ctrl. // We do have an entry for this EndpointSlice. // If the address set is the same, do nothing. // If the address set has changed, update the cache and send a notification. - addresses := adminapi.AdminAPIsFromEndpointSlice(endpoints, r.PortNames) + addresses, err := adminapi.AdminAPIsFromEndpointSlice(endpoints, r.PortNames, r.DNSStrategy) + if err != nil { + return reconcile.Result{}, fmt.Errorf( + "failed getting Admin API from endpoints: %s/%s: %w", endpoints.Namespace, endpoints.Name, err, + ) + } if cached.Equal(addresses) { // No change, don't notify return ctrl.Result{}, nil diff --git a/internal/controllers/configuration/kongadminapi_controller_envtest_test.go b/internal/controllers/configuration/kongadminapi_controller_envtest_test.go index 13d4c13fef..ad2296b6f7 100644 --- a/internal/controllers/configuration/kongadminapi_controller_envtest_test.go +++ b/internal/controllers/configuration/kongadminapi_controller_envtest_test.go @@ -28,6 +28,8 @@ import ( "github.com/kong/kubernetes-ingress-controller/v2/internal/adminapi" "github.com/kong/kubernetes-ingress-controller/v2/internal/controllers/configuration" + "github.com/kong/kubernetes-ingress-controller/v2/internal/manager/config/types" + "github.com/kong/kubernetes-ingress-controller/v2/internal/util/builder" "github.com/kong/kubernetes-ingress-controller/v2/test/envtest" ) @@ -86,6 +88,7 @@ func startKongAdminAPIServiceReconciler(ctx context.Context, t *testing.T, clien PortNames: sets.New("admin"), EndpointsNotifier: n, Log: mgr.GetLogger(), + DNSStrategy: types.ServiceScopedPodDNSStrategy, }).SetupWithManager(mgr), ) // This wait group makes it so that we wait for manager to exit. @@ -158,18 +161,9 @@ func TestKongAdminAPIController(t *testing.T) { }, }, Ports: []discoveryv1.EndpointPort{ - { - Name: lo.ToPtr("admin"), - Port: lo.ToPtr(int32(8080)), - }, - { - Name: lo.ToPtr("admin-tls"), - Port: lo.ToPtr(int32(8444)), - }, - { - Name: lo.ToPtr("kong-admin-tls"), - Port: lo.ToPtr(int32(8445)), - }, + builder.NewEndpointPort(8080).WithName("admin").Build(), + builder.NewEndpointPort(8444).WithName("admin-tls").Build(), + builder.NewEndpointPort(8445).WithName("kong-admin-tls").Build(), }, } require.NoError(t, client.Create(ctx, &endpoints, &ctrlclient.CreateOptions{})) @@ -242,12 +236,7 @@ func TestKongAdminAPIController(t *testing.T) { }, }, }, - Ports: []discoveryv1.EndpointPort{ - { - Name: lo.ToPtr("admin"), - Port: lo.ToPtr(int32(8080)), - }, - }, + Ports: builder.NewEndpointPort(8080).WithName("admin").IntoSlice(), } require.NoError(t, client.Create(ctx, &endpoints, &ctrlclient.CreateOptions{})) @@ -312,12 +301,7 @@ func TestKongAdminAPIController(t *testing.T) { }, }, }, - Ports: []discoveryv1.EndpointPort{ - { - Name: lo.ToPtr("admin"), - Port: lo.ToPtr(int32(8080)), - }, - }, + Ports: builder.NewEndpointPort(8080).WithName("admin").IntoSlice(), } require.NoError(t, client.Create(ctx, &endpoints, &ctrlclient.CreateOptions{})) @@ -362,12 +346,7 @@ func TestKongAdminAPIController(t *testing.T) { }, }, }, - Ports: []discoveryv1.EndpointPort{ - { - Name: lo.ToPtr("admin"), - Port: lo.ToPtr(int32(8080)), - }, - }, + Ports: builder.NewEndpointPort(8080).WithName("admin").IntoSlice(), } require.NoError(t, client.Create(ctx, &endpoints2, &ctrlclient.CreateOptions{})) @@ -453,12 +432,7 @@ func TestKongAdminAPIController(t *testing.T) { }, }, }, - Ports: []discoveryv1.EndpointPort{ - { - Name: lo.ToPtr("admin"), - Port: lo.ToPtr(int32(8080)), - }, - }, + Ports: builder.NewEndpointPort(8080).WithName("admin").IntoSlice(), } require.NoError(t, client.Create(ctx, &endpoints, &ctrlclient.CreateOptions{})) @@ -558,12 +532,7 @@ func TestKongAdminAPIController(t *testing.T) { }, }, }, - Ports: []discoveryv1.EndpointPort{ - { - Name: lo.ToPtr("admin"), - Port: lo.ToPtr(int32(8080)), - }, - }, + Ports: builder.NewEndpointPort(8080).WithName("admin").IntoSlice(), } require.NoError(t, client.Create(ctx, &endpoints, &ctrlclient.CreateOptions{})) diff --git a/internal/dataplane/parser/parser_test.go b/internal/dataplane/parser/parser_test.go index c8186a4a2c..6aaab90758 100644 --- a/internal/dataplane/parser/parser_test.go +++ b/internal/dataplane/parser/parser_test.go @@ -27,6 +27,7 @@ import ( "github.com/kong/kubernetes-ingress-controller/v2/internal/manager/featuregates" "github.com/kong/kubernetes-ingress-controller/v2/internal/store" "github.com/kong/kubernetes-ingress-controller/v2/internal/util" + "github.com/kong/kubernetes-ingress-controller/v2/internal/util/builder" "github.com/kong/kubernetes-ingress-controller/v2/internal/versions" configurationv1 "github.com/kong/kubernetes-ingress-controller/v2/pkg/apis/configuration/v1" ) @@ -4307,11 +4308,7 @@ func TestGetEndpoints(t *testing.T) { NodeName: lo.ToPtr("dummy"), }, }, - Ports: []discoveryv1.EndpointPort{ - { - Protocol: lo.ToPtr(corev1.ProtocolUDP), - }, - }, + Ports: builder.NewEndpointPort(80).WithProtocol(corev1.ProtocolUDP).IntoSlice(), }, }, nil }, @@ -4386,13 +4383,7 @@ func TestGetEndpoints(t *testing.T) { NodeName: lo.ToPtr("dummy"), }, }, - Ports: []discoveryv1.EndpointPort{ - { - Protocol: lo.ToPtr(corev1.ProtocolTCP), - Port: lo.ToPtr(int32(80)), - Name: lo.ToPtr("another-name"), - }, - }, + Ports: builder.NewEndpointPort(80).WithName("another-name").WithProtocol(corev1.ProtocolTCP).IntoSlice(), }, }, nil }, @@ -4471,17 +4462,10 @@ func TestGetEndpoints(t *testing.T) { NodeName: lo.ToPtr("dummy"), }, }, + Ports: []discoveryv1.EndpointPort{ - { - Name: lo.ToPtr("port-1"), - Protocol: lo.ToPtr(corev1.ProtocolTCP), - Port: lo.ToPtr(int32(80)), - }, - { - Name: lo.ToPtr("port-1"), - Protocol: lo.ToPtr(corev1.ProtocolTCP), - Port: lo.ToPtr(int32(80)), - }, + builder.NewEndpointPort(80).WithName("port-1").WithProtocol(corev1.ProtocolTCP).Build(), + builder.NewEndpointPort(80).WithName("port-1").WithProtocol(corev1.ProtocolTCP).Build(), }, }, }, nil @@ -4641,7 +4625,6 @@ func TestKnativeSelectSplit(t *testing.T) { } func TestPickPort(t *testing.T) { - assert := assert.New(t) svc0 := corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: "service-0", @@ -4704,21 +4687,9 @@ func TestPickPort(t *testing.T) { }, }, Ports: []discoveryv1.EndpointPort{ - { - Name: lo.ToPtr("port1"), - Port: lo.ToPtr(int32(111)), - Protocol: lo.ToPtr(corev1.ProtocolTCP), - }, - { - Name: lo.ToPtr("port2"), - Port: lo.ToPtr(int32(222)), - Protocol: lo.ToPtr(corev1.ProtocolTCP), - }, - { - Name: lo.ToPtr("port3"), - Port: lo.ToPtr(int32(333)), - Protocol: lo.ToPtr(corev1.ProtocolTCP), - }, + builder.NewEndpointPort(111).WithName("port1").WithProtocol(corev1.ProtocolTCP).Build(), + builder.NewEndpointPort(222).WithName("port2").WithProtocol(corev1.ProtocolTCP).Build(), + builder.NewEndpointPort(333).WithName("port3").WithProtocol(corev1.ProtocolTCP).Build(), }, }, { @@ -4734,13 +4705,7 @@ func TestPickPort(t *testing.T) { Addresses: []string{"2.2.2.2"}, }, }, - Ports: []discoveryv1.EndpointPort{ - { - Name: lo.ToPtr("port1"), - Port: lo.ToPtr(int32(9999)), - Protocol: lo.ToPtr(corev1.ProtocolTCP), - }, - }, + Ports: builder.NewEndpointPort(9999).WithName("port1").WithProtocol(corev1.ProtocolTCP).IntoSlice(), }, } @@ -4913,13 +4878,13 @@ func TestPickPort(t *testing.T) { } { t.Run(tt.name, func(t *testing.T) { store, err := store.NewFakeStore(tt.objs) - assert.NoError(err) + require.NoError(t, err) p := mustNewParser(t, store) result := p.BuildKongConfig() require.Empty(t, result.TranslationFailures) - assert.Equal(tt.wantTarget, *result.KongState.Upstreams[0].Targets[0].Target.Target) + require.Equal(t, tt.wantTarget, *result.KongState.Upstreams[0].Targets[0].Target.Target) }) } } diff --git a/internal/manager/config.go b/internal/manager/config.go index 915300be78..4a92161fa5 100644 --- a/internal/manager/config.go +++ b/internal/manager/config.go @@ -18,6 +18,7 @@ import ( "github.com/kong/kubernetes-ingress-controller/v2/internal/controllers/gateway" "github.com/kong/kubernetes-ingress-controller/v2/internal/dataplane" "github.com/kong/kubernetes-ingress-controller/v2/internal/konnect" + cfgtypes "github.com/kong/kubernetes-ingress-controller/v2/internal/manager/config/types" "github.com/kong/kubernetes-ingress-controller/v2/internal/manager/featuregates" "github.com/kong/kubernetes-ingress-controller/v2/internal/manager/flags" ) @@ -53,16 +54,17 @@ type Config struct { CacheSyncTimeout time.Duration // Kong Proxy configurations - APIServerHost string - APIServerQPS int - APIServerBurst int - MetricsAddr string - ProbeAddr string - KongAdminURLs []string - KongAdminSvc OptionalNamespacedName - KondAdminSvcPortNames []string - ProxySyncSeconds float32 - ProxyTimeoutSeconds float32 + APIServerHost string + APIServerQPS int + APIServerBurst int + MetricsAddr string + ProbeAddr string + KongAdminURLs []string + KongAdminSvc OptionalNamespacedName + GatewayDiscoveryDNSStrategy cfgtypes.DNSStrategy + KondAdminSvcPortNames []string + ProxySyncSeconds float32 + ProxyTimeoutSeconds float32 // Kubernetes configurations KubeconfigPath string @@ -159,6 +161,8 @@ func (c *Config) FlagSet() *pflag.FlagSet { `Kong Admin API Service namespaced name in "namespace/name" format, to use for Kong Gateway service discovery.`) flagSet.StringSliceVar(&c.KondAdminSvcPortNames, "kong-admin-svc-port-names", []string{"admin", "admin-tls", "kong-admin", "kong-admin-tls"}, "Names of ports on Kong Admin API service to take into account when doing gateway discovery.") + flagSet.Var(flags.NewValidatedValue(&c.GatewayDiscoveryDNSStrategy, dnsStrategyFromFlagValue, flags.WithDefault(cfgtypes.IPDNSStrategy), flags.WithTypeNameOverride[cfgtypes.DNSStrategy]("dns-strategy")), + "gateway-discovery-dns-strategy", "DNS strategy to use when creating Gateway's Admin API addresses. One of: ip, service, pod.") // Kong Proxy and Proxy Cache configurations flagSet.StringVar(&c.APIServerHost, "apiserver-host", "", `The Kubernetes API server URL. If not set, the controller will use cluster config discovery.`) diff --git a/internal/manager/config/types/dnsstrategy.go b/internal/manager/config/types/dnsstrategy.go new file mode 100644 index 0000000000..b2237aa8e5 --- /dev/null +++ b/internal/manager/config/types/dnsstrategy.go @@ -0,0 +1,45 @@ +package types + +import "fmt" + +// DNSStrategy defines the strategy which KIC will use to create Pod addresses. +type DNSStrategy string + +const ( + // IPDNSStrategy defines a strategy where instead of DNS names KIC creates + // addresses from IP addresses. + IPDNSStrategy DNSStrategy = "ip" + // ServiceScopedPodDNSStrategy defines a strategy where KIC creates addresses + // using the following template: + // pod-ip-address.service-name.my-namespace.svc.cluster.local + // Ref: https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#a-aaaa-records-1 + // + // Note: this is known to not work on GKE because it uses kube-dns instead + // of coredns. GKE docs explicitly mention that: + // > kube-dns only creates DNS records for Services that have Endpoints. + // + // Ref: https://cloud.google.com/kubernetes-engine/docs/how-to/kube-dns#service-dns-records + ServiceScopedPodDNSStrategy DNSStrategy = "service" + // NamespaceScopedPodDNSStrategy defines a strategy where KIC creates addresses + // using the following template: + // pod-ip-address.my-namespace.pod.cluster-domain.example + // Ref: https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#a-aaaa-records-1 + NamespaceScopedPodDNSStrategy DNSStrategy = "pod" +) + +func (d DNSStrategy) Validate() error { + switch d { + case IPDNSStrategy: + return nil + case ServiceScopedPodDNSStrategy: + return nil + case NamespaceScopedPodDNSStrategy: + return nil + default: + return fmt.Errorf("unknown dns strategy: %s", d) + } +} + +func (d DNSStrategy) String() string { + return string(d) +} diff --git a/internal/manager/config_validation.go b/internal/manager/config_validation.go index 49bc53165a..d0a1ea5a41 100644 --- a/internal/manager/config_validation.go +++ b/internal/manager/config_validation.go @@ -10,6 +10,7 @@ import ( k8stypes "k8s.io/apimachinery/pkg/types" "github.com/kong/kubernetes-ingress-controller/v2/internal/adminapi" + cfgtypes "github.com/kong/kubernetes-ingress-controller/v2/internal/manager/config/types" ) // *FromFlagValue functions are used to validate single flag values and set those in Config. @@ -42,6 +43,14 @@ func gatewayAPIControllerNameFromFlagValue(flagValue string) (string, error) { return flagValue, nil } +func dnsStrategyFromFlagValue(flagValue string) (cfgtypes.DNSStrategy, error) { + strategy := cfgtypes.DNSStrategy(flagValue) + if err := strategy.Validate(); err != nil { + return cfgtypes.DNSStrategy(""), err + } + return strategy, nil +} + // Validate validates the config. It should be used to validate the config variables' interdependencies. // When a single variable is to be validated, *FromFlagValue function should be implemented. func (c *Config) Validate() error { diff --git a/internal/manager/controllerdef.go b/internal/manager/controllerdef.go index 200e0d22c0..d86f6b4dba 100644 --- a/internal/manager/controllerdef.go +++ b/internal/manager/controllerdef.go @@ -91,6 +91,7 @@ func setupControllers( Log: ctrl.Log.WithName("controllers").WithName("KongAdminAPIService"), CacheSyncTimeout: c.CacheSyncTimeout, EndpointsNotifier: kongAdminAPIEndpointsNotifier, + DNSStrategy: c.GatewayDiscoveryDNSStrategy, }, }, // --------------------------------------------------------------------------- diff --git a/internal/manager/setup.go b/internal/manager/setup.go index f7753c477f..abd4cb4e2e 100644 --- a/internal/manager/setup.go +++ b/internal/manager/setup.go @@ -316,7 +316,7 @@ func (c *Config) adminAPIClientFromServiceDiscovery(ctx context.Context, logger // configuration validation and sending code. var adminAPIs []adminapi.DiscoveredAdminAPI err = retry.Do(func() error { - s, err := adminapi.GetAdminAPIsForService(ctx, kubeClient, kongAdminSvcNN, sets.New(c.KondAdminSvcPortNames...)) + s, err := adminapi.GetAdminAPIsForService(ctx, kubeClient, kongAdminSvcNN, sets.New(c.KondAdminSvcPortNames...), c.GatewayDiscoveryDNSStrategy) if err != nil { return retry.Unrecoverable(err) } diff --git a/internal/util/builder/endpointport.go b/internal/util/builder/endpointport.go new file mode 100644 index 0000000000..fbe3002cc2 --- /dev/null +++ b/internal/util/builder/endpointport.go @@ -0,0 +1,43 @@ +package builder + +import ( + "github.com/samber/lo" + corev1 "k8s.io/api/core/v1" + discoveryv1 "k8s.io/api/discovery/v1" +) + +// EndpointPortBuilder is a builder for discovery v1 EndpointPort. +// Primarily used for testing. +type EndpointPortBuilder struct { + ep discoveryv1.EndpointPort +} + +func NewEndpointPort(port int32) *EndpointPortBuilder { + return &EndpointPortBuilder{ + ep: discoveryv1.EndpointPort{ + Port: lo.ToPtr(port), + }, + } +} + +// WithProtocol sets the protocol on the endpoint port. +func (b *EndpointPortBuilder) WithProtocol(proto corev1.Protocol) *EndpointPortBuilder { + b.ep.Protocol = lo.ToPtr(proto) + return b +} + +// WithName sets the name on the endpoint port. +func (b *EndpointPortBuilder) WithName(name string) *EndpointPortBuilder { + b.ep.Name = lo.ToPtr(name) + return b +} + +// Build returns the configured EndpointPort. +func (b *EndpointPortBuilder) Build() discoveryv1.EndpointPort { + return b.ep +} + +// IntoSlice returns the configured EndpointPort in a slice. +func (b *EndpointPortBuilder) IntoSlice() []discoveryv1.EndpointPort { + return []discoveryv1.EndpointPort{b.ep} +} diff --git a/test/integration/webhook_test.go b/test/integration/webhook_test.go index ef30822c71..a199f4da3a 100644 --- a/test/integration/webhook_test.go +++ b/test/integration/webhook_test.go @@ -14,7 +14,6 @@ import ( "github.com/google/uuid" "github.com/kong/kubernetes-testing-framework/pkg/clusters" "github.com/kong/kubernetes-testing-framework/pkg/clusters/types/kind" - "github.com/samber/lo" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" admregv1 "k8s.io/api/admissionregistration/v1" @@ -26,6 +25,7 @@ import ( "k8s.io/client-go/kubernetes" "github.com/kong/kubernetes-ingress-controller/v2/internal/annotations" + "github.com/kong/kubernetes-ingress-controller/v2/internal/util/builder" testutils "github.com/kong/kubernetes-ingress-controller/v2/internal/util/test" kongv1 "github.com/kong/kubernetes-ingress-controller/v2/pkg/apis/configuration/v1" "github.com/kong/kubernetes-ingress-controller/v2/pkg/clientset" @@ -655,13 +655,7 @@ func ensureWebhookService(ctx context.Context, name string) (func() error, error NodeName: &nodeName, }, }, - Ports: []discoveryv1.EndpointPort{ - { - Name: lo.ToPtr("default"), - Port: lo.ToPtr(int32(testutils.AdmissionWebhookListenPort)), - Protocol: lo.ToPtr(corev1.ProtocolTCP), - }, - }, + Ports: builder.NewEndpointPort(testutils.AdmissionWebhookListenPort).WithName("default").WithProtocol(corev1.ProtocolTCP).IntoSlice(), }, metav1.CreateOptions{}) if err != nil { return nil, fmt.Errorf("creating webhook endpoints: %w", err)