diff --git a/docs/cli-arguments.md b/docs/cli-arguments.md index 70289ef75f..ca6abb343f 100644 --- a/docs/cli-arguments.md +++ b/docs/cli-arguments.md @@ -43,7 +43,6 @@ | `--enable-reverse-sync` | `bool` | Send configuration to Kong even if the configuration checksum has not changed since previous update. | `false` | | `--feature-gates` | `list of string=bool` | A set of comma separated key=value pairs that describe feature gates for alpha/beta/experimental features. See the Feature Gates documentation for information and available options: https://github.com/Kong/kubernetes-ingress-controller/blob/main/FEATURE_GATES.md. | | | `--gateway-api-controller-name` | `string` | The controller name to match on Gateway API resources. | `konghq.com/kic-gateway-controller` | -| `--gateway-discovery-dns-strategy` | `dns-strategy` | DNS strategy to use when creating Gateway's Admin API addresses. One of: ip, service, pod. | `"ip"` | | `--gateway-discovery-readiness-check-interval` | `duration` | Interval of readiness checks on gateway admin API clients for discovery. | `10s` | | `--gateway-discovery-readiness-check-timeout` | `duration` | Timeout of readiness checks on gateway admin clients. | `5s` | | `--gateway-to-reconcile` | `namespaced-name` | Gateway namespaced name in "namespace/name" format. Makes KIC reconcile only the specified Gateway. | | diff --git a/internal/adminapi/client.go b/internal/adminapi/client.go index e6f9d0b3d8..b419956f32 100644 --- a/internal/adminapi/client.go +++ b/internal/adminapi/client.go @@ -204,25 +204,27 @@ func (c *Client) PodReference() (k8stypes.NamespacedName, bool) { } type ClientFactory struct { - workspace string - httpClientOpts HTTPClientOpts - adminToken string + workspace string + opts ClientOpts + adminToken string } -func NewClientFactoryForWorkspace(workspace string, httpClientOpts HTTPClientOpts, adminToken string) ClientFactory { +func NewClientFactoryForWorkspace(workspace string, httpClientOpts ClientOpts, adminToken string) ClientFactory { return ClientFactory{ - workspace: workspace, - httpClientOpts: httpClientOpts, - adminToken: adminToken, + workspace: workspace, + opts: httpClientOpts, + adminToken: adminToken, } } func (cf ClientFactory) CreateAdminAPIClient(ctx context.Context, discoveredAdminAPI DiscoveredAdminAPI) (*Client, error) { - httpclient, err := MakeHTTPClient(&cf.httpClientOpts, cf.adminToken) - if err != nil { - return nil, err + opts := cf.opts + opts.ResolveTo = ResolveTo{ + From: discoveredAdminAPI.Authority, + To: discoveredAdminAPI.ResolveTo, } - cl, err := NewKongClientForWorkspace(ctx, discoveredAdminAPI.Address, cf.workspace, httpclient) + + cl, err := NewKongClientForWorkspace(ctx, discoveredAdminAPI.Address, cf.workspace, opts, cf.adminToken) if err != nil { return nil, err } diff --git a/internal/adminapi/client_test.go b/internal/adminapi/client_test.go index 2cc9dec217..b7f7230fcf 100644 --- a/internal/adminapi/client_test.go +++ b/internal/adminapi/client_test.go @@ -13,7 +13,7 @@ import ( ) func TestClientFactory_CreateAdminAPIClientAttachesPodReference(t *testing.T) { - factory := adminapi.NewClientFactoryForWorkspace("workspace", adminapi.HTTPClientOpts{}, "") + factory := adminapi.NewClientFactoryForWorkspace("workspace", adminapi.ClientOpts{}, "") adminAPIHandler := mocks.NewAdminAPIHandler(t) adminAPIServer := httptest.NewServer(adminAPIHandler) diff --git a/internal/adminapi/endpoints.go b/internal/adminapi/endpoints.go index c3d4f9a78d..0329a5b5bb 100644 --- a/internal/adminapi/endpoints.go +++ b/internal/adminapi/endpoints.go @@ -11,40 +11,37 @@ 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/v3/internal/manager/config/types" ) // DiscoveredAdminAPI represents an Admin API discovered from a Kubernetes Service. type DiscoveredAdminAPI struct { + // https://10-68-0-5.dataplane-admin-kong-rqwr9-sc49t.default.svc:8444 Address string - PodRef k8stypes.NamespacedName + + // 10-68-0-5.dataplane-admin-kong-rqwr9-sc49t.default.svc:8444 + Authority string + + // 10.68.0.5:8444 + ResolveTo string + + PodRef k8stypes.NamespacedName } type Discoverer struct { // portNames is the set of port names that Admin API Service ports will be // matched against. portNames sets.Set[string] - - // dnsStrategy is the DNS strategy to use when resolving Admin API Service - // addresses. - dnsStrategy cfgtypes.DNSStrategy } func NewDiscoverer( adminAPIPortNames sets.Set[string], - dnsStrategy cfgtypes.DNSStrategy, ) (*Discoverer, error) { if adminAPIPortNames.Len() == 0 { return nil, fmt.Errorf("no admin API port names provided") } - if err := dnsStrategy.Validate(); err != nil { - return nil, fmt.Errorf("invalid dns strategy: %w", err) - } return &Discoverer{ - portNames: adminAPIPortNames, - dnsStrategy: dnsStrategy, + portNames: adminAPIPortNames, }, nil } @@ -140,7 +137,7 @@ func (d *Discoverer) AdminAPIsFromEndpointSlice( Namespace: endpoints.Namespace, } - adminAPI, err := adminAPIFromEndpoint(e, p, svc, d.dnsStrategy, endpoints.AddressType) + adminAPI, err := adminAPIFromEndpoint(e, p, svc, endpoints.AddressType) if err != nil { return nil, err } @@ -154,9 +151,10 @@ func adminAPIFromEndpoint( endpoint discoveryv1.Endpoint, port discoveryv1.EndpointPort, service k8stypes.NamespacedName, - dnsStrategy cfgtypes.DNSStrategy, addressFamily discoveryv1.AddressType, ) (DiscoveredAdminAPI, error) { + // XXX: maybe I have to care about addressFamily? + _ = addressFamily podNN := k8stypes.NamespacedName{ Name: endpoint.TargetRef.Name, Namespace: endpoint.TargetRef.Namespace, @@ -171,43 +169,20 @@ func adminAPIFromEndpoint( // 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) + if service.Name == "" { + return DiscoveredAdminAPI{}, fmt.Errorf( + "service name is empty for an endpoint with TargetRef %s/%s", + endpoint.TargetRef.Namespace, endpoint.TargetRef.Name, + ) + } - return DiscoveredAdminAPI{ - Address: fmt.Sprintf("https://%s:%d", address, *port.Port), - PodRef: podNN, - }, nil + ipAddr := strings.ReplaceAll(eAddress, ".", "-") + address := fmt.Sprintf("%s.%s.%s.svc", ipAddr, service.Name, service.Namespace) - case cfgtypes.IPDNSStrategy: - bounded := eAddress - if addressFamily == discoveryv1.AddressTypeIPv6 { - bounded = fmt.Sprintf("[%s]", bounded) - } - return DiscoveredAdminAPI{ - Address: fmt.Sprintf("https://%s:%d", bounded, *port.Port), - PodRef: podNN, - }, nil - - default: - return DiscoveredAdminAPI{}, fmt.Errorf("unknown dns strategy: %s", dnsStrategy) - } + return DiscoveredAdminAPI{ + Address: fmt.Sprintf("https://%s:%d", address, *port.Port), + Authority: fmt.Sprintf("%s:%d", address, *port.Port), + ResolveTo: fmt.Sprintf("%s:%d", eAddress, *port.Port), + PodRef: podNN, + }, nil } diff --git a/internal/adminapi/endpoints_test.go b/internal/adminapi/endpoints_test.go index 17037bf79b..827118451a 100644 --- a/internal/adminapi/endpoints_test.go +++ b/internal/adminapi/endpoints_test.go @@ -3,7 +3,6 @@ package adminapi import ( "context" "errors" - "fmt" "testing" "github.com/google/uuid" @@ -17,7 +16,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" - cfgtypes "github.com/kong/kubernetes-ingress-controller/v3/internal/manager/config/types" "github.com/kong/kubernetes-ingress-controller/v3/internal/util/builder" ) @@ -50,37 +48,36 @@ func TestDiscoverer_AddressesFromEndpointSlice(t *testing.T) { endpoints discoveryv1.EndpointSlice want sets.Set[DiscoveredAdminAPI] portNames sets.Set[string] - dnsStrategy cfgtypes.DNSStrategy expectedErr error }{ - { - name: "basic", - endpoints: discoveryv1.EndpointSlice{ - ObjectMeta: endpointsSliceObjectMeta, - AddressType: discoveryv1.AddressTypeIPv4, - Endpoints: []discoveryv1.Endpoint{ - { - 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", - PodRef: k8stypes.NamespacedName{ - Name: "pod-1", Namespace: namespaceName, - }, - }, - ), - dnsStrategy: cfgtypes.NamespaceScopedPodDNSStrategy, - }, + // { + // name: "basic IPDNSStrategy IPv6", + // endpoints: discoveryv1.EndpointSlice{ + // ObjectMeta: endpointsSliceObjectMeta, + // AddressType: discoveryv1.AddressTypeIPv6, + // Endpoints: []discoveryv1.Endpoint{ + // { + // Addresses: []string{"fe80::cae2:65ff:fe7b:2852", "fe80::cae2:65ff:fe7b:2853"}, + // 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://[fe80::cae2:65ff:fe7b:2852]:8444", + // PodRef: k8stypes.NamespacedName{ + // Name: "pod-1", Namespace: namespaceName, + // }, + // }, + // ), + // dnsStrategy: cfgtypes.IPDNSStrategy, + // }, { name: "basic", endpoints: discoveryv1.EndpointSlice{ @@ -107,63 +104,6 @@ func TestDiscoverer_AddressesFromEndpointSlice(t *testing.T) { }, }, ), - dnsStrategy: cfgtypes.IPDNSStrategy, - }, - { - name: "basic IPDNSStrategy IPv6", - endpoints: discoveryv1.EndpointSlice{ - ObjectMeta: endpointsSliceObjectMeta, - AddressType: discoveryv1.AddressTypeIPv6, - Endpoints: []discoveryv1.Endpoint{ - { - Addresses: []string{"fe80::cae2:65ff:fe7b:2852", "fe80::cae2:65ff:fe7b:2853"}, - 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://[fe80::cae2:65ff:fe7b:2852]:8444", - PodRef: k8stypes.NamespacedName{ - Name: "pod-1", Namespace: namespaceName, - }, - }, - ), - dnsStrategy: cfgtypes.IPDNSStrategy, - }, - { - name: "basic", - endpoints: discoveryv1.EndpointSlice{ - ObjectMeta: endpointsSliceObjectMeta, - AddressType: discoveryv1.AddressTypeIPv4, - Endpoints: []discoveryv1.Endpoint{ - { - 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: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"), }, { @@ -190,14 +130,15 @@ func TestDiscoverer_AddressesFromEndpointSlice(t *testing.T) { PodRef: k8stypes.NamespacedName{ Name: "pod-1", Namespace: namespaceName, }, + Authority: "10-0-0-1.kong-admin.ns.svc:8444", + ResolveTo: "10.0.0.1:8444", }, ), - dnsStrategy: cfgtypes.ServiceScopedPodDNSStrategy, }, { name: "not ready endpoints are returned", endpoints: discoveryv1.EndpointSlice{ - ObjectMeta: endpointsSliceObjectMeta, + ObjectMeta: endpointsSliceWithOwnerReferenceObjectMeta, AddressType: discoveryv1.AddressTypeIPv4, Endpoints: []discoveryv1.Endpoint{ { @@ -212,14 +153,16 @@ func TestDiscoverer_AddressesFromEndpointSlice(t *testing.T) { Ports: builder.NewEndpointPort(8444).WithName("admin").IntoSlice(), }, portNames: sets.New("admin"), - want: sets.New[DiscoveredAdminAPI]( + want: sets.New( DiscoveredAdminAPI{ - Address: "https://10.0.0.1:8444", + Address: "https://10-0-0-1.kong-admin.ns.svc:8444", + Authority: "10-0-0-1.kong-admin.ns.svc:8444", + ResolveTo: "10.0.0.1:8444", PodRef: k8stypes.NamespacedName{ Name: "pod-1", Namespace: namespaceName, }, }), - dnsStrategy: cfgtypes.IPDNSStrategy, + // dnsStrategy: cfgtypes.IPDNSStrategy, }, { name: "ready and terminating endpoints are not returned", @@ -241,12 +184,12 @@ func TestDiscoverer_AddressesFromEndpointSlice(t *testing.T) { }, Ports: builder.NewEndpointPort(8444).WithName("admin").IntoSlice(), }, - portNames: sets.New("admin"), - want: sets.New[DiscoveredAdminAPI](), - dnsStrategy: cfgtypes.IPDNSStrategy, + portNames: sets.New("admin"), + want: sets.New[DiscoveredAdminAPI](), + // dnsStrategy: cfgtypes.IPDNSStrategy, }, { - name: "multiple endpoints are concatenated properly", + name: "multiple endpoints without owner reference returns error", endpoints: discoveryv1.EndpointSlice{ ObjectMeta: endpointsSliceObjectMeta, AddressType: discoveryv1.AddressTypeIPv4, @@ -278,24 +221,9 @@ func TestDiscoverer_AddressesFromEndpointSlice(t *testing.T) { }, 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{ - Namespace: namespaceName, - Name: "pod-1", - }, - }, - DiscoveredAdminAPI{ - Address: "https://10-0-1-1.ns.pod:8444", - PodRef: k8stypes.NamespacedName{ - Namespace: namespaceName, - Name: "pod-2", - }, - }, - ), - dnsStrategy: cfgtypes.NamespaceScopedPodDNSStrategy, + portNames: sets.New("admin"), + expectedErr: errors.New("service name is empty for an endpoint with TargetRef ns/pod-1"), + // dnsStrategy: cfgtypes.NamespaceScopedPodDNSStrategy, }, { name: "multiple endpoints with owner reference are concatenated properly", @@ -338,6 +266,8 @@ func TestDiscoverer_AddressesFromEndpointSlice(t *testing.T) { Namespace: namespaceName, Name: "pod-1", }, + Authority: "10-0-0-1.kong-admin.ns.svc:8444", + ResolveTo: "10.0.0.1:8444", }, DiscoveredAdminAPI{ Address: "https://10-0-1-1.kong-admin.ns.svc:8444", @@ -345,9 +275,10 @@ func TestDiscoverer_AddressesFromEndpointSlice(t *testing.T) { Namespace: namespaceName, Name: "pod-2", }, + Authority: "10-0-1-1.kong-admin.ns.svc:8444", + ResolveTo: "10.0.1.1:8444", }, ), - dnsStrategy: cfgtypes.ServiceScopedPodDNSStrategy, }, { name: "ports not called 'admin' are not added", @@ -382,9 +313,9 @@ func TestDiscoverer_AddressesFromEndpointSlice(t *testing.T) { }, Ports: builder.NewEndpointPort(8444).WithName("non-admin-port-name").IntoSlice(), }, - want: sets.New[DiscoveredAdminAPI](), - portNames: sets.New("admin"), - dnsStrategy: cfgtypes.IPDNSStrategy, + want: sets.New[DiscoveredAdminAPI](), + portNames: sets.New("admin"), + // dnsStrategy: cfgtypes.IPDNSStrategy, }, { name: "ports without names are not taken into account", @@ -403,14 +334,14 @@ func TestDiscoverer_AddressesFromEndpointSlice(t *testing.T) { }, Ports: builder.NewEndpointPort(8444).IntoSlice(), }, - portNames: sets.New("admin"), - want: sets.New[DiscoveredAdminAPI](), - dnsStrategy: cfgtypes.IPDNSStrategy, + portNames: sets.New("admin"), + want: sets.New[DiscoveredAdminAPI](), + // dnsStrategy: cfgtypes.IPDNSStrategy, }, { name: "multiple ports names", endpoints: discoveryv1.EndpointSlice{ - ObjectMeta: endpointsSliceObjectMeta, + ObjectMeta: endpointsSliceWithOwnerReferenceObjectMeta, AddressType: discoveryv1.AddressTypeIPv4, Endpoints: []discoveryv1.Endpoint{ { @@ -430,21 +361,24 @@ func TestDiscoverer_AddressesFromEndpointSlice(t *testing.T) { portNames: sets.New("admin", "admin-tls"), want: sets.New( DiscoveredAdminAPI{ - Address: "https://10-0-0-1.ns.pod:8443", + Address: "https://10-0-0-1.kong-admin.ns.svc:8443", + Authority: "10-0-0-1.kong-admin.ns.svc:8443", + ResolveTo: "10.0.0.1:8443", PodRef: k8stypes.NamespacedName{ Namespace: namespaceName, Name: "pod-1", }, }, DiscoveredAdminAPI{ - Address: "https://10-0-0-1.ns.pod:8444", + Address: "https://10-0-0-1.kong-admin.ns.svc:8444", + Authority: "10-0-0-1.kong-admin.ns.svc:8444", + ResolveTo: "10.0.0.1:8444", PodRef: k8stypes.NamespacedName{ Namespace: namespaceName, Name: "pod-1", }, }, ), - dnsStrategy: cfgtypes.NamespaceScopedPodDNSStrategy, }, { name: "endpoints with no target ref return error for service scopec dns strategy", @@ -463,9 +397,9 @@ func TestDiscoverer_AddressesFromEndpointSlice(t *testing.T) { }, Ports: builder.NewEndpointPort(8444).WithName("admin").IntoSlice(), }, - portNames: sets.New("admin"), - want: sets.New[DiscoveredAdminAPI](), - dnsStrategy: cfgtypes.IPDNSStrategy, + portNames: sets.New("admin"), + want: sets.New[DiscoveredAdminAPI](), + // dnsStrategy: cfgtypes.IPDNSStrategy, }, { name: "endpoints with target ref other than Pod are ignored", @@ -484,49 +418,56 @@ func TestDiscoverer_AddressesFromEndpointSlice(t *testing.T) { }, Ports: builder.NewEndpointPort(8444).WithName("admin").IntoSlice(), }, - portNames: sets.New("admin"), - want: sets.New[DiscoveredAdminAPI](), - dnsStrategy: cfgtypes.IPDNSStrategy, + portNames: sets.New("admin"), + want: sets.New[DiscoveredAdminAPI](), + // dnsStrategy: cfgtypes.IPDNSStrategy, }, } for _, tt := range tests { - t.Run(fmt.Sprintf("dnsstrategy_%s/%s", tt.dnsStrategy, tt.name), func(t *testing.T) { - discoverer, err := NewDiscoverer(tt.portNames, tt.dnsStrategy) + t.Run(tt.name, func(t *testing.T) { + discoverer, err := NewDiscoverer(tt.portNames) require.NoError(t, err) got, err := discoverer.AdminAPIsFromEndpointSlice(tt.endpoints) if tt.expectedErr != nil { require.EqualError(t, err, tt.expectedErr.Error()) - } else { - require.Equal(t, tt.want, got) + return } + require.NoError(t, err) + require.Equal(t, tt.want, got) }) } } func TestDiscoverer_GetAdminAPIsForService(t *testing.T) { - const namespaceName = "ns" + const ( + namespaceName = "ns" + serviceName = "kong-admin" + ) - var ( - serviceName = uuid.NewString() - matchingServiceObjectMetaFunc = func() metav1.ObjectMeta { - return metav1.ObjectMeta{ - Name: uuid.NewString(), - Namespace: namespaceName, - Labels: map[string]string{ - "kubernetes.io/service-name": serviceName, + matchingServiceObjectWithOwnerRef := func() metav1.ObjectMeta { + return metav1.ObjectMeta{ + Name: uuid.NewString(), + Namespace: namespaceName, + Labels: map[string]string{ + "kubernetes.io/service-name": serviceName, + }, + OwnerReferences: []metav1.OwnerReference{ + { + APIVersion: "v1", + Name: serviceName, + Kind: "Service", }, - } + }, } - ) + } tests := []struct { - name string - service k8stypes.NamespacedName - objects []client.ObjectList - dnsStrategy cfgtypes.DNSStrategy - want sets.Set[DiscoveredAdminAPI] - wantErr bool + name string + service k8stypes.NamespacedName + objects []client.ObjectList + want sets.Set[DiscoveredAdminAPI] + wantErr bool }{ { name: "basic", @@ -538,7 +479,7 @@ func TestDiscoverer_GetAdminAPIsForService(t *testing.T) { &discoveryv1.EndpointSliceList{ Items: []discoveryv1.EndpointSlice{ { - ObjectMeta: matchingServiceObjectMetaFunc(), + ObjectMeta: matchingServiceObjectWithOwnerRef(), AddressType: discoveryv1.AddressTypeIPv4, Endpoints: []discoveryv1.Endpoint{ { @@ -557,7 +498,7 @@ func TestDiscoverer_GetAdminAPIsForService(t *testing.T) { &discoveryv1.EndpointSliceList{ Items: []discoveryv1.EndpointSlice{ { - ObjectMeta: matchingServiceObjectMetaFunc(), + ObjectMeta: matchingServiceObjectWithOwnerRef(), AddressType: discoveryv1.AddressTypeIPv4, Endpoints: []discoveryv1.Endpoint{ { @@ -576,7 +517,7 @@ func TestDiscoverer_GetAdminAPIsForService(t *testing.T) { &discoveryv1.EndpointSliceList{ Items: []discoveryv1.EndpointSlice{ { - ObjectMeta: matchingServiceObjectMetaFunc(), + ObjectMeta: matchingServiceObjectWithOwnerRef(), AddressType: discoveryv1.AddressTypeIPv4, Endpoints: []discoveryv1.Endpoint{ { @@ -595,19 +536,24 @@ func TestDiscoverer_GetAdminAPIsForService(t *testing.T) { }, want: sets.New( DiscoveredAdminAPI{ - Address: "https://10-0-0-1.ns.pod:8444", PodRef: k8stypes.NamespacedName{ + Address: "https://10-0-0-1.kong-admin.ns.svc:8444", + Authority: "10-0-0-1.kong-admin.ns.svc:8444", + ResolveTo: "10.0.0.1:8444", + PodRef: k8stypes.NamespacedName{ Namespace: namespaceName, Name: "pod-1", }, }, DiscoveredAdminAPI{ - Address: "https://9-0-0-1.ns.pod:8444", PodRef: k8stypes.NamespacedName{ + Address: "https://9-0-0-1.kong-admin.ns.svc:8444", + Authority: "9-0-0-1.kong-admin.ns.svc:8444", + ResolveTo: "9.0.0.1:8444", + PodRef: k8stypes.NamespacedName{ Namespace: namespaceName, Name: "pod-2", }, }, ), - dnsStrategy: cfgtypes.NamespaceScopedPodDNSStrategy, }, { name: "ports not matching the specified port names are not taken into account", @@ -619,7 +565,7 @@ func TestDiscoverer_GetAdminAPIsForService(t *testing.T) { &discoveryv1.EndpointSliceList{ Items: []discoveryv1.EndpointSlice{ { - ObjectMeta: matchingServiceObjectMetaFunc(), + ObjectMeta: matchingServiceObjectWithOwnerRef(), AddressType: discoveryv1.AddressTypeIPv4, Endpoints: []discoveryv1.Endpoint{ { @@ -636,8 +582,7 @@ func TestDiscoverer_GetAdminAPIsForService(t *testing.T) { }, }, }, - want: sets.New[DiscoveredAdminAPI](), - dnsStrategy: cfgtypes.IPDNSStrategy, + want: sets.New[DiscoveredAdminAPI](), }, { name: "Endpoints without a TargetRef are not matched", @@ -649,7 +594,7 @@ func TestDiscoverer_GetAdminAPIsForService(t *testing.T) { &discoveryv1.EndpointSliceList{ Items: []discoveryv1.EndpointSlice{ { - ObjectMeta: matchingServiceObjectMetaFunc(), + ObjectMeta: matchingServiceObjectWithOwnerRef(), AddressType: discoveryv1.AddressTypeIPv4, Endpoints: []discoveryv1.Endpoint{ { @@ -665,8 +610,7 @@ func TestDiscoverer_GetAdminAPIsForService(t *testing.T) { }, }, }, - want: sets.New[DiscoveredAdminAPI](), - dnsStrategy: cfgtypes.IPDNSStrategy, + want: sets.New[DiscoveredAdminAPI](), }, { name: "terminating Endpoints are not matched", @@ -678,7 +622,7 @@ func TestDiscoverer_GetAdminAPIsForService(t *testing.T) { &discoveryv1.EndpointSliceList{ Items: []discoveryv1.EndpointSlice{ { - ObjectMeta: matchingServiceObjectMetaFunc(), + ObjectMeta: matchingServiceObjectWithOwnerRef(), AddressType: discoveryv1.AddressTypeIPv4, Endpoints: []discoveryv1.Endpoint{ { @@ -695,20 +639,17 @@ func TestDiscoverer_GetAdminAPIsForService(t *testing.T) { }, }, }, - want: sets.New[DiscoveredAdminAPI](), - dnsStrategy: cfgtypes.IPDNSStrategy, + want: sets.New[DiscoveredAdminAPI](), }, } for _, tt := range tests { - t.Run(fmt.Sprintf("dnsstrategy_%s/%s", tt.dnsStrategy, tt.name), func(t *testing.T) { - require.NoError(t, tt.dnsStrategy.Validate()) - + t.Run(tt.name, func(t *testing.T) { fakeClient := fake.NewClientBuilder(). WithLists(tt.objects...). Build() portNames := sets.New("admin") - discoverer, err := NewDiscoverer(portNames, tt.dnsStrategy) + discoverer, err := NewDiscoverer(portNames) require.NoError(t, err) got, err := discoverer.GetAdminAPIsForService(context.Background(), fakeClient, tt.service) @@ -716,7 +657,7 @@ func TestDiscoverer_GetAdminAPIsForService(t *testing.T) { require.Error(t, err) return } - + require.NoError(t, err) require.Equal(t, tt.want, got) }) } diff --git a/internal/adminapi/kong.go b/internal/adminapi/kong.go index 144cfb1ebe..870cd17aaa 100644 --- a/internal/adminapi/kong.go +++ b/internal/adminapi/kong.go @@ -6,9 +6,11 @@ import ( "crypto/x509" "errors" "fmt" + "net" "net/http" "os" "strings" + "time" "github.com/blang/semver/v4" "github.com/kong/go-kong/kong" @@ -43,7 +45,12 @@ func (e KongGatewayUnsupportedVersionError) Error() string { // NewKongAPIClient returns a Kong API client for a given root API URL. // It ensures that proper User-Agent is set. Do not use kong.NewClient directly. -func NewKongAPIClient(adminURL string, httpClient *http.Client) (*kong.Client, error) { +func NewKongAPIClient(adminURL string, kongAdminAPIConfig ClientOpts, kongAdminToken string) (*kong.Client, error) { + httpClient, err := makeHTTPClient(kongAdminAPIConfig, kongAdminToken) + if err != nil { + return nil, err + } + client, err := kong.NewClient(kong.String(adminURL), httpClient) //nolint:forbidigo if err != nil { return nil, fmt.Errorf("creating Kong client: %w", err) @@ -57,10 +64,10 @@ func NewKongAPIClient(adminURL string, httpClient *http.Client) (*kong.Client, e // or KongGatewayUnsupportedVersionError if it can't check Kong Gateway's version or it is not >= 3.4.1. // If the workspace does not already exist, NewKongClientForWorkspace will create it. func NewKongClientForWorkspace( - ctx context.Context, adminURL string, wsName string, httpClient *http.Client, + ctx context.Context, adminURL string, wsName string, kongAdminAPIConfig ClientOpts, kongAdminToken string, ) (*Client, error) { // Create the base client, and if no workspace was provided then return that. - client, err := NewKongAPIClient(adminURL, httpClient) + client, err := NewKongAPIClient(adminURL, kongAdminAPIConfig, kongAdminToken) if err != nil { return nil, fmt.Errorf("creating Kong client: %w", err) } @@ -116,8 +123,8 @@ func NewKongClientForWorkspace( return cl, nil } -// HTTPClientOpts defines parameters that configure an HTTP client. -type HTTPClientOpts struct { +// ClientOpts defines parameters that configure a client for Kong Admin API. +type ClientOpts struct { // Disable verification of TLS certificate of Kong's Admin endpoint. TLSSkipVerify bool // SNI name to use to verify the certificate presented by Kong in TLS. @@ -130,14 +137,21 @@ type HTTPClientOpts struct { Headers []string // TLSClient is TLS client config. TLSClient TLSClientConfig + // ResolveTo + ResolveTo ResolveTo +} + +type ResolveTo struct { + From string + To string } const ( HeaderNameAdminToken = "Kong-Admin-Token" ) -// MakeHTTPClient returns an HTTP client with the specified mTLS/headers configuration. -func MakeHTTPClient(opts *HTTPClientOpts, kongAdminToken string) (*http.Client, error) { +// makeHTTPClient returns an HTTP client with the specified mTLS/headers configuration. +func makeHTTPClient(opts ClientOpts, kongAdminToken string) (*http.Client, error) { var tlsConfig tls.Config if opts.TLSSkipVerify { @@ -185,6 +199,22 @@ func MakeHTTPClient(opts *HTTPClientOpts, kongAdminToken string) (*http.Client, transport := http.DefaultTransport.(*http.Transport).Clone() transport.TLSClientConfig = &tlsConfig + if lo.IsNotEmpty(opts.ResolveTo) { + // It provides something similar to `curl --resolve` for the Admin API client. + transport.DialContext = func(ctx context.Context, network, passedAddr string) (net.Conn, error) { + fmt.Println("> DialContext network", network) + // 10-68-0-5.dataplane-admin-kong-rqwr9-sc49t.default.svc:8444 + fmt.Println("> DialContext passedAddr", passedAddr) + if passedAddr == opts.ResolveTo.From { + passedAddr = opts.ResolveTo.To + fmt.Println("> DialContext change passedAddr to ", passedAddr) + } + return (&net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + }).DialContext(ctx, network, passedAddr) + } + } return &http.Client{ Transport: &HeaderRoundTripper{ headers: prepareHeaders(opts.Headers, kongAdminToken), diff --git a/internal/adminapi/kong_test.go b/internal/adminapi/kong_test.go index 37a9f861bc..c1fc7742b2 100644 --- a/internal/adminapi/kong_test.go +++ b/internal/adminapi/kong_test.go @@ -20,11 +20,11 @@ import ( "github.com/kong/kubernetes-ingress-controller/v3/test/mocks" ) -func TestMakeHTTPClientWithTLSOpts(t *testing.T) { +func TestAdminAPIClientWithTLSOpts(t *testing.T) { cert, key := certificate.MustGenerateCertPEMFormat() caCert := cert - opts := adminapi.HTTPClientOpts{ + opts := adminapi.ClientOpts{ TLSSkipVerify: true, TLSServerName: "", CACertPath: "", @@ -37,23 +37,17 @@ func TestMakeHTTPClientWithTLSOpts(t *testing.T) { } t.Run("without kong admin token", func(t *testing.T) { - c, err := adminapi.MakeHTTPClient(&opts, "") - require.NoError(t, err) - require.NotNil(t, c) - validate(t, c, caCert, cert, key, "") + validate(t, opts, caCert, cert, key, "") }) t.Run("with kong admin token", func(t *testing.T) { const kongAdminToken = "my-token" - c, err := adminapi.MakeHTTPClient(&opts, kongAdminToken) - require.NoError(t, err) - require.NotNil(t, c) - validate(t, c, caCert, cert, key, kongAdminToken) + validate(t, opts, caCert, cert, key, kongAdminToken) }) } -func TestMakeHTTPClientWithTLSOptsAndFilePaths(t *testing.T) { - cert, key := certificate.MustGenerateCertPEMFormat() +func TestAdminAPIClientWithTLSOptsAndFilePaths(t *testing.T) { + cert, key := certificate.MustGenerateCertPEMFormat(certificate.WithDNSNames("localhost")) caCert := cert certDir := t.TempDir() @@ -76,9 +70,8 @@ func TestMakeHTTPClientWithTLSOptsAndFilePaths(t *testing.T) { require.NoError(t, err) require.Len(t, key, writtenBytes) - opts := adminapi.HTTPClientOpts{ - TLSSkipVerify: true, - TLSServerName: "", + opts := adminapi.ClientOpts{ + TLSServerName: "localhost", CACertPath: caFile.Name(), CACert: "", Headers: nil, @@ -89,18 +82,12 @@ func TestMakeHTTPClientWithTLSOptsAndFilePaths(t *testing.T) { } t.Run("without kong admin token", func(t *testing.T) { - c, err := adminapi.MakeHTTPClient(&opts, "") - require.NoError(t, err) - require.NotNil(t, c) - validate(t, c, caCert, cert, key, "") + validate(t, opts, caCert, cert, key, "") }) t.Run("with kong admin token", func(t *testing.T) { const kongAdminToken = "my-token" - c, err := adminapi.MakeHTTPClient(&opts, kongAdminToken) - require.NoError(t, err) - require.NotNil(t, c) - validate(t, c, caCert, cert, key, kongAdminToken) + validate(t, opts, caCert, cert, key, kongAdminToken) }) } @@ -218,7 +205,8 @@ func TestNewKongClientForWorkspace(t *testing.T) { context.Background(), adminAPIServer.URL, tc.workspace, - adminAPIServer.Client(), + adminapi.ClientOpts{}, + "", ) if tc.expectError != nil { @@ -243,7 +231,7 @@ func TestNewKongClientForWorkspace(t *testing.T) { // whether the passed client can connect to it successfully. func validate( t *testing.T, - httpClient *http.Client, + opts adminapi.ClientOpts, caPEM []byte, certPEM []byte, certPrivateKeyPEM []byte, @@ -288,8 +276,13 @@ func validate( server.StartTLS() defer server.Close() - response, err := httpClient.Get(server.URL) - require.NoError(t, err, "HTTP client failed to issue a GET request") + cl, err := adminapi.NewKongAPIClient(server.URL, opts, kongAdminToken) + require.NoError(t, err, "failed to create Kong API client") + + req, err := http.NewRequest(http.MethodGet, server.URL, nil) + require.NoError(t, err, "failed to create basic HTTP request") + response, err := cl.DoRAW(context.Background(), req) + require.NoError(t, err, "Kong API client failed to issue a basic request") defer response.Body.Close() data, err := io.ReadAll(response.Body) diff --git a/internal/adminapi/konnect.go b/internal/adminapi/konnect.go index e5fae99620..ed394ec7a9 100644 --- a/internal/adminapi/konnect.go +++ b/internal/adminapi/konnect.go @@ -2,7 +2,6 @@ package adminapi import ( "context" - "crypto/tls" "errors" "fmt" "net/http" @@ -13,7 +12,6 @@ import ( "github.com/kong/go-kong/kong" "github.com/kong/kubernetes-ingress-controller/v3/internal/konnect/tracing" - tlsutil "github.com/kong/kubernetes-ingress-controller/v3/internal/util/tls" ) type KonnectConfig struct { @@ -34,30 +32,12 @@ type KonnectConfig struct { } func NewKongClientForKonnectControlPlane(c KonnectConfig) (*KonnectClient, error) { - clientCertificate, err := tlsutil.ExtractClientCertificates( - []byte(c.TLSClient.Cert), - c.TLSClient.CertFile, - []byte(c.TLSClient.Key), - c.TLSClient.KeyFile, - ) - if err != nil { - return nil, fmt.Errorf("failed to extract client certificates: %w", err) - } - if clientCertificate == nil { - return nil, fmt.Errorf("client certificate is missing") - } - - tlsConfig := tls.Config{ - Certificates: []tls.Certificate{*clientCertificate}, - MinVersion: tls.VersionTLS12, - } - transport := http.DefaultTransport.(*http.Transport).Clone() - transport.TLSClientConfig = &tlsConfig client, err := NewKongAPIClient( fmt.Sprintf("%s/%s/%s", c.Address, "kic/api/control-planes", c.ControlPlaneID), - &http.Client{ - Transport: transport, + ClientOpts{ + TLSClient: c.TLSClient, }, + "", ) if err != nil { return nil, err diff --git a/internal/dataplane/configfetcher/config_fetcher_test.go b/internal/dataplane/configfetcher/config_fetcher_test.go index 22836261b0..ee65728908 100644 --- a/internal/dataplane/configfetcher/config_fetcher_test.go +++ b/internal/dataplane/configfetcher/config_fetcher_test.go @@ -29,7 +29,8 @@ func TestTryFetchingValidConfigFromGateways(t *testing.T) { // the status of the Kong Gateway but just returns the client. client, err := adminapi.NewKongAPIClient( adminAPIServer.URL, - adminAPIServer.Client(), + adminapi.ClientOpts{}, + "", ) require.NoError(t, err) require.NotNil(t, client) diff --git a/internal/dataplane/kong_client.go b/internal/dataplane/kong_client.go index c9bebdefa2..45d3c56b9c 100644 --- a/internal/dataplane/kong_client.go +++ b/internal/dataplane/kong_client.go @@ -750,7 +750,7 @@ func (c *KongClient) sendToClient( config sendconfig.Config, isFallback bool, ) (string, error) { - logger := c.logger.WithValues("url", client.AdminAPIClient().BaseRootURL()) + logger := c.logger.WithValues("url", client.BaseRootURL(), "resolvedTo", "XYZ") deckGenParams := deckgen.GenerateDeckContentParams{ SelectorTags: config.FilterTags, diff --git a/internal/konnect/config_synchronizer_test.go b/internal/konnect/config_synchronizer_test.go index 1b939769c7..36f9193a93 100644 --- a/internal/konnect/config_synchronizer_test.go +++ b/internal/konnect/config_synchronizer_test.go @@ -3,7 +3,6 @@ package konnect_test import ( "context" "fmt" - "net/http" "testing" "time" @@ -250,7 +249,7 @@ func TestConfigSynchronizer_StatusNotificationIsSent(t *testing.T) { func mustSampleKonnectClient(t *testing.T) *adminapi.KonnectClient { t.Helper() - c, err := adminapi.NewKongAPIClient(fmt.Sprintf("https://%s.konghq.tech", uuid.NewString()), &http.Client{}) + c, err := adminapi.NewKongAPIClient(fmt.Sprintf("https://%s.konghq.tech", uuid.NewString()), adminapi.ClientOpts{}, "") require.NoError(t, err) rgID := uuid.NewString() return adminapi.NewKonnectClient(c, rgID, false) diff --git a/internal/manager/config.go b/internal/manager/config.go index cf817499bb..91c0f1c76a 100644 --- a/internal/manager/config.go +++ b/internal/manager/config.go @@ -50,7 +50,7 @@ type Config struct { LogFormat string // Kong high-level controller manager configurations - KongAdminAPIConfig adminapi.HTTPClientOpts + KongAdminAPIConfig adminapi.ClientOpts KongAdminInitializationRetries uint KongAdminInitializationRetryDelay time.Duration KongAdminToken string @@ -76,7 +76,6 @@ type Config struct { ProbeAddr string KongAdminURLs []string KongAdminSvc OptionalNamespacedName - GatewayDiscoveryDNSStrategy cfgtypes.DNSStrategy GatewayDiscoveryReadinessCheckInterval time.Duration GatewayDiscoveryReadinessCheckTimeout time.Duration KongAdminSvcPortNames []string @@ -216,8 +215,9 @@ 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.KongAdminSvcPortNames, "kong-admin-svc-port-names", []string{"admin-tls", "kong-admin-tls"}, "Name(s) of ports on Kong Admin API service in comma-separated format (or specify this flag multiple times) 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.") + f := "gateway-discovery-dns-strategy" + flagSet.String(f, "ip", "DNS strategy to use when creating Gateway's Admin API addresses. One of: ip, service, pod. AAAA") + _ = flagSet.MarkDeprecated(f, "aaaa") flagSet.DurationVar(&c.GatewayDiscoveryReadinessCheckInterval, "gateway-discovery-readiness-check-interval", clients.DefaultReadinessReconciliationInterval, "Interval of readiness checks on gateway admin API clients for discovery.") flagSet.DurationVar(&c.GatewayDiscoveryReadinessCheckTimeout, "gateway-discovery-readiness-check-timeout", clients.DefaultReadinessCheckTimeout, diff --git a/internal/manager/config/types/dnsstrategy.go b/internal/manager/config/types/dnsstrategy.go deleted file mode 100644 index b2237aa8e5..0000000000 --- a/internal/manager/config/types/dnsstrategy.go +++ /dev/null @@ -1,45 +0,0 @@ -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 0f5723ac2f..9dbc2952fe 100644 --- a/internal/manager/config_validation.go +++ b/internal/manager/config_validation.go @@ -56,14 +56,6 @@ 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/config_validation_test.go b/internal/manager/config_validation_test.go index a944c1abf1..ff0645ae5b 100644 --- a/internal/manager/config_validation_test.go +++ b/internal/manager/config_validation_test.go @@ -252,7 +252,7 @@ func TestConfigValidate(t *testing.T) { t.Run("Admin API", func(t *testing.T) { validWithClientTLS := func() manager.Config { return manager.Config{ - KongAdminAPIConfig: adminapi.HTTPClientOpts{ + KongAdminAPIConfig: adminapi.ClientOpts{ TLSClient: adminapi.TLSClientConfig{ // We do not set valid cert or key, and it's still considered valid as at this level we only care // about them being not empty. Their validity is to be verified later on by the Admin API client @@ -266,7 +266,7 @@ func TestConfigValidate(t *testing.T) { t.Run("no TLS client is allowed", func(t *testing.T) { c := manager.Config{ - KongAdminAPIConfig: adminapi.HTTPClientOpts{ + KongAdminAPIConfig: adminapi.ClientOpts{ TLSClient: adminapi.TLSClientConfig{}, }, } diff --git a/internal/manager/run.go b/internal/manager/run.go index b8b2da96a0..6bfefe0ae5 100644 --- a/internal/manager/run.go +++ b/internal/manager/run.go @@ -79,7 +79,7 @@ func Run( healthServer.setHealthzCheck(healthz.Ping) healthServer.Start(ctx, c.ProbeAddr, setupLog.WithName("health-check")) - adminAPIsDiscoverer, err := adminapi.NewDiscoverer(sets.New(c.KongAdminSvcPortNames...), c.GatewayDiscoveryDNSStrategy) + adminAPIsDiscoverer, err := adminapi.NewDiscoverer(sets.New(c.KongAdminSvcPortNames...)) if err != nil { return fmt.Errorf("failed to create admin apis discoverer: %w", err) } diff --git a/internal/manager/setup.go b/internal/manager/setup.go index 9e13f83ed9..63e01236dd 100644 --- a/internal/manager/setup.go +++ b/internal/manager/setup.go @@ -332,11 +332,6 @@ func (c *Config) adminAPIClients( discoverer *adminapi.Discoverer, factory adminapi.ClientFactory, ) ([]*adminapi.Client, error) { - httpclient, err := adminapi.MakeHTTPClient(&c.KongAdminAPIConfig, c.KongAdminToken) - if err != nil { - return nil, err - } - // If kong-admin-svc flag has been specified then use it to get the list // of Kong Admin API endpoints. if kongAdminSvc, ok := c.KongAdminSvc.Get(); ok { @@ -351,7 +346,7 @@ func (c *Config) adminAPIClients( addresses := c.KongAdminURLs clients := make([]*adminapi.Client, 0, len(addresses)) for _, address := range addresses { - cl, err := adminapi.NewKongClientForWorkspace(ctx, address, c.KongWorkspace, httpclient) + cl, err := adminapi.NewKongClientForWorkspace(ctx, address, c.KongWorkspace, c.KongAdminAPIConfig, c.KongAdminToken) if err != nil { return nil, err } diff --git a/scripts/cli-arguments-docs-gen/main.go b/scripts/cli-arguments-docs-gen/main.go index 7c17411512..2d740016bd 100644 --- a/scripts/cli-arguments-docs-gen/main.go +++ b/scripts/cli-arguments-docs-gen/main.go @@ -77,7 +77,7 @@ func getTypeForHuman(flag *pflag.Flag) string { case "types.MetricsAccessFilter": return "string" // The below are types that are human readable out-of-the-box, in case of missing one extend the list. - case "bool", "string", "int", "uint", "duration", "dns-strategy", "namespaced-name": + case "bool", "string", "int", "uint", "duration", "namespaced-name": return typ default: panic(fmt.Sprintf("unknown type %q", typ)) diff --git a/test/e2e/all_in_one_test.go b/test/e2e/all_in_one_test.go index ef457bf292..4a2c96c9f5 100644 --- a/test/e2e/all_in_one_test.go +++ b/test/e2e/all_in_one_test.go @@ -4,7 +4,6 @@ package e2e import ( "context" - "crypto/tls" "fmt" "net/http" "os" @@ -12,7 +11,6 @@ import ( "testing" "time" - "github.com/hashicorp/go-cleanhttp" "github.com/kong/kubernetes-testing-framework/pkg/clusters/addons/kong" "github.com/kong/kubernetes-testing-framework/pkg/environments" "github.com/stretchr/testify/require" @@ -339,15 +337,6 @@ func ensureAllProxyReplicasAreConfigured(ctx context.Context, t *testing.T, env require.NoError(t, err) t.Logf("ensuring all %d proxy replicas are configured", len(pods)) - client := cleanhttp.DefaultClient() - tr := cleanhttp.DefaultTransport() - // Anything related to TLS can be ignored, because only availability is being tested here. - // Testing communicating over TLS is done as part of actual E2E test. - tr.TLSClientConfig = &tls.Config{ - InsecureSkipVerify: true, //nolint:gosec - } - client.Transport = tr - wg := sync.WaitGroup{} for _, pod := range pods { wg.Add(1) @@ -359,7 +348,9 @@ func ensureAllProxyReplicasAreConfigured(ctx context.Context, t *testing.T, env localPort := startPortForwarder(forwardCtx, t, env, proxyDeploymentNN.Namespace, pod.Name, "8444") address := fmt.Sprintf("https://localhost:%d", localPort) - kongClient, err := adminapi.NewKongAPIClient(address, client) + // Anything related to TLS can be ignored, because only availability is being tested here. + // Testing communicating over TLS is done as part of actual E2E test. + kongClient, err := adminapi.NewKongAPIClient(address, adminapi.ClientOpts{TLSSkipVerify: true}, "") require.NoError(t, err) verifyIngressWithEchoBackendsInAdminAPI(ctx, t, kongClient, numberOfEchoBackends) diff --git a/test/e2e/konnect_test.go b/test/e2e/konnect_test.go index 077cf69742..2bde9b5812 100644 --- a/test/e2e/konnect_test.go +++ b/test/e2e/konnect_test.go @@ -5,14 +5,12 @@ package e2e import ( "bytes" "context" - "crypto/tls" "fmt" "os/exec" "sync" "testing" "time" - "github.com/hashicorp/go-cleanhttp" environment "github.com/kong/kubernetes-testing-framework/pkg/environments" "github.com/samber/lo" "github.com/stretchr/testify/assert" @@ -299,21 +297,14 @@ func requireAllProxyReplicasIDsConsistentWithKonnect( nodeAPIClient := createKonnectNodeClient(t, rg, cert, key) getNodeIDFromAdminAPI := func(proxyPod corev1.Pod) string { - client := cleanhttp.DefaultClient() - tr := cleanhttp.DefaultTransport() - // Anything related to TLS can be ignored, because only availability is being tested here. - // Testing communicating over TLS is done as part of actual E2E test. - tr.TLSClientConfig = &tls.Config{ - InsecureSkipVerify: true, //nolint:gosec - } - client.Transport = tr - forwardCtx, cancel := context.WithCancel(ctx) defer cancel() localPort := startPortForwarder(forwardCtx, t, env, proxyDeploymentNN.Namespace, proxyPod.Name, "8444") address := fmt.Sprintf("https://localhost:%d", localPort) - kongClient, err := adminapi.NewKongAPIClient(address, client) + // Anything related to TLS can be ignored, because only availability is being tested here. + // Testing communicating as part of actual E2E test. + kongClient, err := adminapi.NewKongAPIClient(address, adminapi.ClientOpts{TLSSkipVerify: true}, "") require.NoError(t, err) nodeID, err := adminapi.NewClient(kongClient).NodeID(ctx) diff --git a/test/envtest/adminapi_discoverer_envtest_test.go b/test/envtest/adminapi_discoverer_envtest_test.go index 8a5c0ac577..b79c528b45 100644 --- a/test/envtest/adminapi_discoverer_envtest_test.go +++ b/test/envtest/adminapi_discoverer_envtest_test.go @@ -17,7 +17,6 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "github.com/kong/kubernetes-ingress-controller/v3/internal/adminapi" - cfgtypes "github.com/kong/kubernetes-ingress-controller/v3/internal/manager/config/types" "github.com/kong/kubernetes-ingress-controller/v3/internal/util/builder" ) @@ -49,14 +48,38 @@ func TestDiscoverer_GetAdminAPIsForServiceReturnsAllAddressesCorrectlyPagingThro ctx, cancel := context.WithCancel(context.Background()) defer cancel() + const ( + serviceName = "test-service" + portName = "admin" + portNumber = 8444 + ) var ( - ns = CreateNamespace(ctx, t, client) - serviceName = uuid.NewString() - service = k8stypes.NamespacedName{ - Namespace: ns.Name, - Name: serviceName, + ns = CreateNamespace(ctx, t, client) + serviceObj = corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: ns.Name, + Name: serviceName, + OwnerReferences: []metav1.OwnerReference{ + { + APIVersion: "v1", + Kind: "Service", + Name: ns.Name, + UID: ns.UID, + }, + }, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: portName, + Protocol: corev1.ProtocolTCP, + Port: portNumber, + }, + }, + }, } ) + require.NoError(t, client.Create(ctx, &serviceObj)) for i := 0; i < tc.subnetC; i++ { for j := 0; j < tc.subnetD; j++ { @@ -67,6 +90,14 @@ func TestDiscoverer_GetAdminAPIsForServiceReturnsAllAddressesCorrectlyPagingThro Labels: map[string]string{ "kubernetes.io/service-name": serviceName, }, + OwnerReferences: []metav1.OwnerReference{ + { + APIVersion: "v1", + Kind: "Service", + Name: serviceName, + UID: serviceObj.UID, + }, + }, }, AddressType: discoveryv1.AddressTypeIPv4, Endpoints: []discoveryv1.Endpoint{ @@ -79,16 +110,16 @@ func TestDiscoverer_GetAdminAPIsForServiceReturnsAllAddressesCorrectlyPagingThro TargetRef: testPodReference("pod-1", ns.Name), }, }, - Ports: builder.NewEndpointPort(8444).WithName("admin").IntoSlice(), + Ports: builder.NewEndpointPort(portNumber).WithName(portName).IntoSlice(), } require.NoError(t, client.Create(ctx, &es)) } } - discoverer, err := adminapi.NewDiscoverer(sets.New("admin"), cfgtypes.IPDNSStrategy) + discoverer, err := adminapi.NewDiscoverer(sets.New(portName)) require.NoError(t, err) - got, err := discoverer.GetAdminAPIsForService(ctx, client, service) + got, err := discoverer.GetAdminAPIsForService(ctx, client, k8stypes.NamespacedName{Name: serviceObj.Name, Namespace: serviceObj.Namespace}) require.NoError(t, err) require.Len(t, got, tc.subnetD*tc.subnetC, "GetAdminAPIsForService should return all valid addresses") }) diff --git a/test/envtest/kongadminapi_controller_envtest_test.go b/test/envtest/kongadminapi_controller_envtest_test.go index e91a9768ad..b42722d77e 100644 --- a/test/envtest/kongadminapi_controller_envtest_test.go +++ b/test/envtest/kongadminapi_controller_envtest_test.go @@ -29,7 +29,6 @@ import ( "github.com/kong/kubernetes-ingress-controller/v3/internal/adminapi" "github.com/kong/kubernetes-ingress-controller/v3/internal/controllers/configuration" - "github.com/kong/kubernetes-ingress-controller/v3/internal/manager/config/types" "github.com/kong/kubernetes-ingress-controller/v3/internal/util/builder" ) @@ -90,7 +89,7 @@ func startKongAdminAPIServiceReconciler(ctx context.Context, t *testing.T, clien n = ¬ifier{t: t} - adminAPIsDiscoverer, err := adminapi.NewDiscoverer(sets.New("admin"), types.ServiceScopedPodDNSStrategy) + adminAPIsDiscoverer, err := adminapi.NewDiscoverer(sets.New("admin")) require.NoError(t, err) require.NoError(t, @@ -186,14 +185,18 @@ func TestKongAdminAPIController(t *testing.T) { assert.ElementsMatch(t, []adminapi.DiscoveredAdminAPI{ { - Address: fmt.Sprintf("https://10-0-0-1.%s.%s.svc:8080", adminService.Name, adminService.Namespace), + Address: fmt.Sprintf("https://10-0-0-1.%s.%s.svc:8080", adminService.Name, adminService.Namespace), + Authority: fmt.Sprintf("10-0-0-1.%s.%s.svc:8080", adminService.Name, adminService.Namespace), + ResolveTo: "10.0.0.1:8080", PodRef: k8stypes.NamespacedName{ Namespace: adminPod.Namespace, Name: adminPod.Name, }, }, { - Address: fmt.Sprintf("https://10-0-0-2.%s.%s.svc:8080", adminService.Name, adminService.Namespace), + Address: fmt.Sprintf("https://10-0-0-2.%s.%s.svc:8080", adminService.Name, adminService.Namespace), + Authority: fmt.Sprintf("10-0-0-2.%s.%s.svc:8080", adminService.Name, adminService.Namespace), + ResolveTo: "10.0.0.2:8080", PodRef: k8stypes.NamespacedName{ Namespace: adminPod.Namespace, Name: adminPod.Name, @@ -258,7 +261,9 @@ func TestKongAdminAPIController(t *testing.T) { assert.ElementsMatch(t, []adminapi.DiscoveredAdminAPI{ { - Address: fmt.Sprintf("https://10-0-0-2.%s.%s.svc:8080", adminService.Name, adminService.Namespace), + Address: fmt.Sprintf("https://10-0-0-2.%s.%s.svc:8080", adminService.Name, adminService.Namespace), + Authority: fmt.Sprintf("10-0-0-2.%s.%s.svc:8080", adminService.Name, adminService.Namespace), + ResolveTo: "10.0.0.2:8080", PodRef: k8stypes.NamespacedName{ Namespace: adminPod.Namespace, Name: adminPod.Name, @@ -368,28 +373,36 @@ func TestKongAdminAPIController(t *testing.T) { assert.ElementsMatch(t, []adminapi.DiscoveredAdminAPI{ { - Address: fmt.Sprintf("https://10-0-0-1.%s.%s.svc:8080", adminService.Name, adminService.Namespace), + Address: fmt.Sprintf("https://10-0-0-1.%s.%s.svc:8080", adminService.Name, adminService.Namespace), + Authority: fmt.Sprintf("10-0-0-1.%s.%s.svc:8080", adminService.Name, adminService.Namespace), + ResolveTo: "10.0.0.1:8080", PodRef: k8stypes.NamespacedName{ Namespace: adminPod.Namespace, Name: adminPod.Name, }, }, { - Address: fmt.Sprintf("https://10-0-0-2.%s.%s.svc:8080", adminService.Name, adminService.Namespace), + Address: fmt.Sprintf("https://10-0-0-2.%s.%s.svc:8080", adminService.Name, adminService.Namespace), + Authority: fmt.Sprintf("10-0-0-2.%s.%s.svc:8080", adminService.Name, adminService.Namespace), + ResolveTo: "10.0.0.2:8080", PodRef: k8stypes.NamespacedName{ Namespace: adminPod.Namespace, Name: adminPod.Name, }, }, { - Address: fmt.Sprintf("https://10-0-0-10.%s.%s.svc:8080", adminService.Name, adminService.Namespace), + Address: fmt.Sprintf("https://10-0-0-10.%s.%s.svc:8080", adminService.Name, adminService.Namespace), + Authority: fmt.Sprintf("10-0-0-10.%s.%s.svc:8080", adminService.Name, adminService.Namespace), + ResolveTo: "10.0.0.10:8080", PodRef: k8stypes.NamespacedName{ Namespace: adminPod.Namespace, Name: adminPod.Name, }, }, { - Address: fmt.Sprintf("https://10-0-0-20.%s.%s.svc:8080", adminService.Name, adminService.Namespace), + Address: fmt.Sprintf("https://10-0-0-20.%s.%s.svc:8080", adminService.Name, adminService.Namespace), + Authority: fmt.Sprintf("10-0-0-20.%s.%s.svc:8080", adminService.Name, adminService.Namespace), + ResolveTo: "10.0.0.20:8080", PodRef: k8stypes.NamespacedName{ Namespace: adminPod.Namespace, Name: adminPod.Name, @@ -454,14 +467,18 @@ func TestKongAdminAPIController(t *testing.T) { assert.ElementsMatch(t, []adminapi.DiscoveredAdminAPI{ { - Address: fmt.Sprintf("https://10-0-0-1.%s.%s.svc:8080", adminService.Name, adminService.Namespace), + Address: fmt.Sprintf("https://10-0-0-1.%s.%s.svc:8080", adminService.Name, adminService.Namespace), + Authority: fmt.Sprintf("10-0-0-1.%s.%s.svc:8080", adminService.Name, adminService.Namespace), + ResolveTo: "10.0.0.1:8080", PodRef: k8stypes.NamespacedName{ Namespace: adminPod.Namespace, Name: adminPod.Name, }, }, { - Address: fmt.Sprintf("https://10-0-0-2.%s.%s.svc:8080", adminService.Name, adminService.Namespace), + Address: fmt.Sprintf("https://10-0-0-2.%s.%s.svc:8080", adminService.Name, adminService.Namespace), + Authority: fmt.Sprintf("10-0-0-2.%s.%s.svc:8080", adminService.Name, adminService.Namespace), + ResolveTo: "10.0.0.2:8080", PodRef: k8stypes.NamespacedName{ Namespace: adminPod.Namespace, Name: adminPod.Name, @@ -489,7 +506,9 @@ func TestKongAdminAPIController(t *testing.T) { assert.ElementsMatch(t, []adminapi.DiscoveredAdminAPI{ { - Address: fmt.Sprintf("https://10-0-0-1.%s.%s.svc:8080", adminService.Name, adminService.Namespace), + Address: fmt.Sprintf("https://10-0-0-1.%s.%s.svc:8080", adminService.Name, adminService.Namespace), + Authority: fmt.Sprintf("10-0-0-1.%s.%s.svc:8080", adminService.Name, adminService.Namespace), + ResolveTo: "10.0.0.1:8080", PodRef: k8stypes.NamespacedName{ Namespace: adminPod.Namespace, Name: adminPod.Name, @@ -554,14 +573,18 @@ func TestKongAdminAPIController(t *testing.T) { assert.ElementsMatch(t, []adminapi.DiscoveredAdminAPI{ { - Address: fmt.Sprintf("https://10-0-0-1.%s.%s.svc:8080", adminService.Name, adminService.Namespace), + Address: fmt.Sprintf("https://10-0-0-1.%s.%s.svc:8080", adminService.Name, adminService.Namespace), + Authority: fmt.Sprintf("10-0-0-1.%s.%s.svc:8080", adminService.Name, adminService.Namespace), + ResolveTo: "10.0.0.1:8080", PodRef: k8stypes.NamespacedName{ Namespace: adminPod.Namespace, Name: adminPod.Name, }, }, { - Address: fmt.Sprintf("https://10-0-0-2.%s.%s.svc:8080", adminService.Name, adminService.Namespace), + Address: fmt.Sprintf("https://10-0-0-2.%s.%s.svc:8080", adminService.Name, adminService.Namespace), + Authority: fmt.Sprintf("10-0-0-2.%s.%s.svc:8080", adminService.Name, adminService.Namespace), + ResolveTo: "10.0.0.2:8080", PodRef: k8stypes.NamespacedName{ Namespace: adminPod.Namespace, Name: adminPod.Name, diff --git a/test/internal/helpers/kong.go b/test/internal/helpers/kong.go index 547e330241..46062b6d0c 100644 --- a/test/internal/helpers/kong.go +++ b/test/internal/helpers/kong.go @@ -17,11 +17,7 @@ import ( // GetKongRootConfig gets version and root configurations of Kong from / endpoint of the provided Admin API URL. func GetKongRootConfig(ctx context.Context, proxyAdminURL *url.URL, kongTestPassword string) (map[string]any, error) { - httpClient, err := adminapi.MakeHTTPClient(&adminapi.HTTPClientOpts{}, kongTestPassword) - if err != nil { - return nil, fmt.Errorf("failed creating specific HTTP client for Kong API URL: %q: %w", proxyAdminURL, err) - } - kc, err := adminapi.NewKongAPIClient(proxyAdminURL.String(), httpClient) + kc, err := adminapi.NewKongAPIClient(proxyAdminURL.String(), adminapi.ClientOpts{}, kongTestPassword) if err != nil { return nil, fmt.Errorf("failed creating Kong API client for URL: %q: %w", proxyAdminURL, err) } @@ -100,12 +96,7 @@ func GetKongRouterFlavor(ctx context.Context, proxyAdminURL *url.URL, kongTestPa // GetKongLicenses fetches all licenses applied to Kong gateway. func GetKongLicenses(ctx context.Context, proxyAdminURL *url.URL, kongTestPassword string) ([]*kong.License, error) { - httpClient, err := adminapi.MakeHTTPClient(&adminapi.HTTPClientOpts{}, kongTestPassword) - if err != nil { - return nil, err - } - - kc, err := adminapi.NewKongAPIClient(proxyAdminURL.String(), httpClient) + kc, err := adminapi.NewKongAPIClient(proxyAdminURL.String(), adminapi.ClientOpts{}, kongTestPassword) if err != nil { return nil, err } diff --git a/test/kongintegration/dbmode_update_strategy_test.go b/test/kongintegration/dbmode_update_strategy_test.go index 5a0cdcf3ef..60aa3cffff 100644 --- a/test/kongintegration/dbmode_update_strategy_test.go +++ b/test/kongintegration/dbmode_update_strategy_test.go @@ -3,7 +3,6 @@ package kongintegration import ( "context" "errors" - "net/http" "testing" "time" @@ -41,7 +40,7 @@ func TestUpdateStrategyDBMode(t *testing.T) { _ = containers.NewPostgres(ctx, t, net) kongC := containers.NewKong(ctx, t, containers.KongWithDBMode(net.Name)) - kongClient, err := adminapi.NewKongAPIClient(kongC.AdminURL(ctx, t), &http.Client{}) + kongClient, err := adminapi.NewKongAPIClient(kongC.AdminURL(ctx, t), adminapi.ClientOpts{}, "") require.NoError(t, err) logbase, err := zap.NewDevelopment() diff --git a/test/kongintegration/expression_router_test.go b/test/kongintegration/expression_router_test.go index 9d221fb7fc..23e659bdb4 100644 --- a/test/kongintegration/expression_router_test.go +++ b/test/kongintegration/expression_router_test.go @@ -32,7 +32,7 @@ func TestExpressionsRouterMatchers_GenerateValidExpressions(t *testing.T) { ctx := context.Background() kongC := containers.NewKong(ctx, t) - kongClient, err := adminapi.NewKongAPIClient(kongC.AdminURL(ctx, t), helpers.DefaultHTTPClient()) + kongClient, err := adminapi.NewKongAPIClient(kongC.AdminURL(ctx, t), adminapi.ClientOpts{}, "") require.NoError(t, err) httpBinC := containers.NewHTTPBin(ctx, t) diff --git a/test/kongintegration/inmemory_update_strategy_test.go b/test/kongintegration/inmemory_update_strategy_test.go index c0a1416e8f..c00761ac3a 100644 --- a/test/kongintegration/inmemory_update_strategy_test.go +++ b/test/kongintegration/inmemory_update_strategy_test.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "errors" - "net/http" "testing" "time" @@ -39,7 +38,7 @@ func TestUpdateStrategyInMemory_PropagatesResourcesErrors(t *testing.T) { ctx := context.Background() kongC := containers.NewKong(ctx, t) - kongClient, err := adminapi.NewKongAPIClient(kongC.AdminURL(ctx, t), &http.Client{}) + kongClient, err := adminapi.NewKongAPIClient(kongC.AdminURL(ctx, t), adminapi.ClientOpts{}, "") require.NoError(t, err) logbase, err := zap.NewDevelopment() diff --git a/test/kongintegration/kong_client_golden_tests_outputs_test.go b/test/kongintegration/kong_client_golden_tests_outputs_test.go index 2520866f4f..33dc07b810 100644 --- a/test/kongintegration/kong_client_golden_tests_outputs_test.go +++ b/test/kongintegration/kong_client_golden_tests_outputs_test.go @@ -24,7 +24,6 @@ import ( "github.com/kong/kubernetes-ingress-controller/v3/internal/adminapi" "github.com/kong/kubernetes-ingress-controller/v3/internal/dataplane/sendconfig" - "github.com/kong/kubernetes-ingress-controller/v3/test/internal/helpers" "github.com/kong/kubernetes-ingress-controller/v3/test/internal/helpers/konnect" "github.com/kong/kubernetes-ingress-controller/v3/test/internal/testenv" "github.com/kong/kubernetes-ingress-controller/v3/test/kongintegration/containers" @@ -66,7 +65,7 @@ func TestKongClientGoldenTestsOutputs(t *testing.T) { kongC := containers.NewKong(ctx, t, containers.KongWithRouterFlavor("expressions")) - kongClient, err := adminapi.NewKongAPIClient(kongC.AdminURL(ctx, t), helpers.DefaultHTTPClient()) + kongClient, err := adminapi.NewKongAPIClient(kongC.AdminURL(ctx, t), adminapi.ClientOpts{}, "") require.NoError(t, err) for _, goldenTestOutputPath := range expressionRoutesOutputsPaths { @@ -80,7 +79,7 @@ func TestKongClientGoldenTestsOutputs(t *testing.T) { t.Parallel() kongC := containers.NewKong(ctx, t, containers.KongWithRouterFlavor("traditional")) - kongClient, err := adminapi.NewKongAPIClient(kongC.AdminURL(ctx, t), helpers.DefaultHTTPClient()) + kongClient, err := adminapi.NewKongAPIClient(kongC.AdminURL(ctx, t), adminapi.ClientOpts{}, "") require.NoError(t, err) for _, goldenTestOutputPath := range defaultOutputsPaths { diff --git a/test/kongintegration/kongupstreampolicy_test.go b/test/kongintegration/kongupstreampolicy_test.go index 618bc33896..8b868666b9 100644 --- a/test/kongintegration/kongupstreampolicy_test.go +++ b/test/kongintegration/kongupstreampolicy_test.go @@ -2,7 +2,6 @@ package kongintegration import ( "context" - "net/http" "testing" "time" @@ -35,7 +34,7 @@ func TestKongUpstreamPolicyTranslation(t *testing.T) { ctx := context.Background() kongC := containers.NewKong(ctx, t) - kongClient, err := adminapi.NewKongAPIClient(kongC.AdminURL(ctx, t), &http.Client{}) + kongClient, err := adminapi.NewKongAPIClient(kongC.AdminURL(ctx, t), adminapi.ClientOpts{}, "") require.NoError(t, err) updateStrategy := sendconfig.NewUpdateStrategyInMemory( kongClient,