Skip to content

Commit 18f1499

Browse files
authored
feat(status) add UDP publish service (#3325)
Add CLI arguments for setting a UDP publish service and address overrides. If set, these will be used to populate status information for UDPIngresses. If not set, UDPIngress status will fall back to the default publish service/override, with the expectation that it is a dual-transport LoadBalancer.
1 parent 95c97ce commit 18f1499

File tree

13 files changed

+199
-36
lines changed

13 files changed

+199
-36
lines changed

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,10 @@ Adding a new version? You'll need three changes:
7575
configuration.
7676
[#3359](https://github.com/Kong/kubernetes-ingress-controller/pull/3359)
7777
- Added `version` command
78-
[#3379](https://github.com/Kong/kubernetes-ingress-controller/pull/3379)
78+
[#3379](https://github.com/Kong/kubernetes-ingress-controller/pull/3379)
79+
- Added `--publish-service-udp` to indicate the Service that handles inbound
80+
UDP traffic.
81+
[#3325](https://github.com/Kong/kubernetes-ingress-controller/pull/3325)
7982
- Added possibility to configure multiple Kong Gateways through the
8083
`--kong-admin-url` CLI flag (which can be specified multiple times) or through
8184
a corresponding environment variable `CONTROLLER_KONG_ADMIN_URL` (which can

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,7 @@ test.istio: gotestsum
432432
KUBECONFIG ?= "${HOME}/.kube/config"
433433
KONG_NAMESPACE ?= kong-system
434434
KONG_PROXY_SERVICE ?= ingress-controller-kong-proxy
435+
KONG_PROXY_UDP_SERVICE ?= ingress-controller-kong-udp-proxy
435436
KONG_ADMIN_SERVICE ?= ingress-controller-kong-admin
436437
KONG_ADMIN_PORT ?= 8001
437438
KONG_ADMIN_URL ?= "http://$(shell kubectl -n $(KONG_NAMESPACE) get service $(KONG_ADMIN_SERVICE) -o=go-template='{{range .status.loadBalancer.ingress}}{{.ip}}{{end}}'):$(KONG_ADMIN_PORT)"
@@ -446,6 +447,7 @@ debug: install _ensure-namespace
446447
--anonymous-reports=false \
447448
--kong-admin-url $(KONG_ADMIN_URL) \
448449
--publish-service $(KONG_NAMESPACE)/$(KONG_PROXY_SERVICE) \
450+
--publish-service-udp $(KONG_NAMESPACE)/$(KONG_PROXY_UDP_SERVICE) \
449451
--kubeconfig $(KUBECONFIG) \
450452
--feature-gates=$(KONG_CONTROLLER_FEATURE_GATES)
451453

@@ -494,6 +496,7 @@ _run:
494496
--anonymous-reports=false \
495497
--kong-admin-url $(KONG_ADMIN_URL) \
496498
--publish-service $(KONG_NAMESPACE)/$(KONG_PROXY_SERVICE) \
499+
--publish-service-udp $(KONG_NAMESPACE)/$(KONG_PROXY_UDP_SERVICE) \
497500
--kubeconfig $(KUBECONFIG) \
498501
--feature-gates=$(KONG_CONTROLLER_FEATURE_GATES)
499502

hack/generators/controllers/networking/main.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ var inputControllersNeeded = &typesNeeded{
206206
Package: kongv1beta1,
207207
Plural: "udpingresses",
208208
CacheType: "UDPIngress",
209+
IsUDP: true,
209210
NeedsStatusPermissions: true,
210211
CapableOfStatusUpdates: true,
211212
AcceptsIngressClassNameAnnotation: true,
@@ -333,6 +334,7 @@ type typeNeeded struct {
333334
Plural string
334335
CacheType string
335336
RBACVerbs []string
337+
IsUDP bool
336338

337339
// AcceptsIngressClassNameAnnotation indicates that the object accepts (and the controller will listen to)
338340
// the "kubernetes.io/ingress.class" annotation to decide whether or not the object is supported.
@@ -627,7 +629,11 @@ func (r *{{.PackageAlias}}{{.Kind}}Reconciler) Reconcile(ctx context.Context, re
627629
}
628630
629631
log.V(util.DebugLevel).Info("determining gateway addresses for object status updates", "namespace", req.Namespace, "name", req.Name)
630-
addrs, err := r.DataplaneAddressFinder.GetLoadBalancerAddresses()
632+
{{- if .IsUDP }}
633+
addrs, err := r.DataplaneAddressFinder.GetUDPLoadBalancerAddresses(ctx)
634+
{{- else }}
635+
addrs, err := r.DataplaneAddressFinder.GetLoadBalancerAddresses(ctx)
636+
{{- end }}
631637
if err != nil {
632638
return ctrl.Result{}, err
633639
}

internal/controllers/configuration/zz_generated_controllers.go

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/controllers/knative/knative.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ func (r *Knativev1alpha1IngressReconciler) Reconcile(ctx context.Context, req ct
208208
}
209209

210210
log.V(util.DebugLevel).Info("determining gateway addresses for object status updates", "namespace", req.Namespace, "name", req.Name)
211-
addrs, err := r.DataplaneAddressFinder.GetLoadBalancerAddresses()
211+
addrs, err := r.DataplaneAddressFinder.GetLoadBalancerAddresses(ctx)
212212
if err != nil {
213213
return ctrl.Result{}, err
214214
}

internal/dataplane/address_finder.go

Lines changed: 58 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package dataplane
22

33
import (
4+
"context"
45
"fmt"
56
"net"
67
"strings"
@@ -16,13 +17,14 @@ import (
1617

1718
// AddressGetter is a function which can dynamically retrieve the list of IPs
1819
// that the data-plane is listening on for ingress network traffic.
19-
type AddressGetter func() ([]string, error)
20+
type AddressGetter func(ctx context.Context) ([]string, error)
2021

2122
// AddressFinder is a threadsafe metadata object which can provide the current
2223
// live addresses in use by the dataplane at any point in time.
2324
type AddressFinder struct {
24-
overrideAddresses []string
25-
addressGetter AddressGetter
25+
overrideAddresses []string
26+
overrideAddressesUDP []string
27+
addressGetter AddressGetter
2628

2729
lock sync.RWMutex
2830
}
@@ -54,10 +56,19 @@ func (a *AddressFinder) SetOverrides(addrs []string) {
5456
a.overrideAddresses = addrs
5557
}
5658

59+
// SetUDPOverrides hard codes a specific list of addresses to be the UDP addresses
60+
// that this finder produces for the data-plane. To disable overrides, call
61+
// this method again with an empty list.
62+
func (a *AddressFinder) SetUDPOverrides(addrs []string) {
63+
a.lock.Lock()
64+
defer a.lock.Unlock()
65+
a.overrideAddressesUDP = addrs
66+
}
67+
5768
// GetAddresses provides a list of the addresses which the data-plane is
5869
// listening on for ingress network traffic. Addresses can either be IP
5970
// addresses or hostnames.
60-
func (a *AddressFinder) GetAddresses() ([]string, error) {
71+
func (a *AddressFinder) GetAddresses(ctx context.Context) ([]string, error) {
6172
a.lock.RLock()
6273
defer a.lock.RUnlock()
6374

@@ -66,22 +77,50 @@ func (a *AddressFinder) GetAddresses() ([]string, error) {
6677
}
6778

6879
if a.addressGetter != nil {
69-
return a.addressGetter()
80+
return a.addressGetter(ctx)
7081
}
7182

7283
return nil, fmt.Errorf("data-plane addresses can't be retrieved: no valid method available")
7384
}
7485

86+
// GetUDPAddresses provides a list of the UDP addresses which the data-plane is
87+
// listening on for ingress network traffic. Addresses can either be IP
88+
// addresses or hostnames. If UDP settings are not configured, falls back to GetAddresses().
89+
func (a *AddressFinder) GetUDPAddresses(ctx context.Context) ([]string, error) {
90+
a.lock.RLock()
91+
defer a.lock.RUnlock()
92+
93+
if len(a.overrideAddressesUDP) > 0 {
94+
return a.overrideAddressesUDP, nil
95+
}
96+
97+
if len(a.overrideAddresses) > 0 && a.addressGetter == nil {
98+
return a.overrideAddresses, nil
99+
}
100+
101+
if a.addressGetter != nil {
102+
return a.addressGetter(ctx)
103+
}
104+
105+
return a.GetAddresses(ctx)
106+
}
107+
75108
// GetLoadBalancerAddresses provides a list of the addresses which the
76109
// data-plane is listening on for ingress network traffic, but provides the
77110
// addresses in Kubernetes corev1.LoadBalancerIngress format. Addresses can be
78111
// IP addresses or hostnames.
79-
func (a *AddressFinder) GetLoadBalancerAddresses() ([]netv1.IngressLoadBalancerIngress, error) {
80-
addrs, err := a.GetAddresses()
112+
func (a *AddressFinder) GetLoadBalancerAddresses(ctx context.Context) ([]netv1.IngressLoadBalancerIngress, error) {
113+
addrs, err := a.GetAddresses(ctx)
81114
if err != nil {
82115
return nil, err
83116
}
117+
return getAddressHelper(addrs)
118+
}
84119

120+
// getAddressHelper converts a string slice of addresses (IPs or hostnames) into an IngressLoadBalancerIngress
121+
// (https://pkg.go.dev/k8s.io/api/networking/v1#IngressLoadBalancerIngress), or an error if one of the given strings
122+
// is neither a valid IP nor a valid hostname.
123+
func getAddressHelper(addrs []string) ([]netv1.IngressLoadBalancerIngress, error) {
85124
var loadBalancerAddresses []netv1.IngressLoadBalancerIngress
86125
for _, addr := range addrs {
87126
ing := netv1.IngressLoadBalancerIngress{}
@@ -99,6 +138,18 @@ func (a *AddressFinder) GetLoadBalancerAddresses() ([]netv1.IngressLoadBalancerI
99138
return loadBalancerAddresses, nil
100139
}
101140

141+
// GetUDPLoadBalancerAddresses provides a list of the addresses which the
142+
// data-plane is listening on for UDP network traffic, but provides the
143+
// addresses in Kubernetes corev1.LoadBalancerIngress format. Addresses can be
144+
// IP addresses or hostnames.
145+
func (a *AddressFinder) GetUDPLoadBalancerAddresses(ctx context.Context) ([]netv1.IngressLoadBalancerIngress, error) {
146+
addrs, err := a.GetUDPAddresses(ctx)
147+
if err != nil {
148+
return nil, err
149+
}
150+
return getAddressHelper(addrs)
151+
}
152+
102153
// -----------------------------------------------------------------------------
103154
//
104155
// -----------------------------------------------------------------------------
Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package dataplane
22

33
import (
4+
"context"
45
"fmt"
56
"testing"
67

@@ -15,43 +16,44 @@ func TestAddressFinder(t *testing.T) {
1516
require.Nil(t, finder.addressGetter)
1617

1718
t.Log("verifying that a finder with no overrides or getter produces an error")
18-
addrs, err := finder.GetAddresses()
19+
ctx := context.Background()
20+
addrs, err := finder.GetAddresses(ctx)
1921
require.Error(t, err)
2022
require.Empty(t, addrs)
2123
require.Equal(t, "data-plane addresses can't be retrieved: no valid method available", err.Error())
2224

2325
t.Log("generating a fake AddressGetter")
2426
defaultAddrs := []string{"127.0.0.1", "127.0.0.2"}
2527
overrideAddrs := []string{"192.168.1.1", "192.168.1.2", "192.168.1.3"}
26-
fakeGetter := func() ([]string, error) { return defaultAddrs, nil }
28+
fakeGetter := func(ctx context.Context) ([]string, error) { return defaultAddrs, nil }
2729

2830
t.Log("verifying getting a list of addresses from the finder after a getter function is provided")
2931
finder.SetGetter(fakeGetter)
30-
addrs, err = finder.GetAddresses()
32+
addrs, err = finder.GetAddresses(ctx)
3133
require.NoError(t, err)
3234
require.Equal(t, defaultAddrs, addrs)
3335

3436
t.Log("verifying that overrides take precedent over the getter")
3537
finder.SetOverrides(overrideAddrs)
36-
addrs, err = finder.GetAddresses()
38+
addrs, err = finder.GetAddresses(ctx)
3739
require.NoError(t, err)
3840
require.Equal(t, overrideAddrs, addrs)
3941

4042
t.Log("verifying disabling overrides")
4143
finder.SetOverrides(nil)
42-
addrs, err = finder.GetAddresses()
44+
addrs, err = finder.GetAddresses(ctx)
4345
require.NoError(t, err)
4446
require.Equal(t, defaultAddrs, addrs)
4547

4648
t.Log("verifying k8s load balancer formatted version of the addresses")
47-
lbs, err := finder.GetLoadBalancerAddresses()
49+
lbs, err := finder.GetLoadBalancerAddresses(ctx)
4850
require.NoError(t, err)
4951
require.Equal(t, []netv1.IngressLoadBalancerIngress{{IP: defaultAddrs[0]}, {IP: defaultAddrs[1]}}, lbs)
5052

5153
t.Log("verifying valid DNS names are formatting properly")
5254
dnsAddrs := []string{"127.0.0.1", "example1.konghq.com", "example2.konghq.com"}
5355
finder.SetOverrides(dnsAddrs)
54-
lbs, err = finder.GetLoadBalancerAddresses()
56+
lbs, err = finder.GetLoadBalancerAddresses(ctx)
5557
require.NoError(t, err)
5658
require.Equal(t, []netv1.IngressLoadBalancerIngress{
5759
{IP: dnsAddrs[0]},
@@ -61,16 +63,55 @@ func TestAddressFinder(t *testing.T) {
6163

6264
t.Log("verifying empty addresses return an error")
6365
finder.SetOverrides([]string{""})
64-
lbs, err = finder.GetLoadBalancerAddresses()
66+
lbs, err = finder.GetLoadBalancerAddresses(ctx)
6567
require.Error(t, err)
6668
require.Empty(t, lbs)
6769
require.Equal(t, "empty address found", err.Error())
6870

6971
t.Log("verifying invalid DNS names return an error")
7072
invalidDNSAddrs := []string{"[email protected]"}
7173
finder.SetOverrides(invalidDNSAddrs)
72-
lbs, err = finder.GetLoadBalancerAddresses()
74+
lbs, err = finder.GetLoadBalancerAddresses(ctx)
7375
require.Error(t, err)
7476
require.Empty(t, lbs)
7577
require.Equal(t, fmt.Sprintf("%s is not a valid DNS hostname", invalidDNSAddrs[0]), err.Error())
7678
}
79+
80+
func TestUDPAddressFinder(t *testing.T) {
81+
t.Log("generating a new AddressFinder")
82+
finder := NewAddressFinder()
83+
require.NotNil(t, finder)
84+
require.Nil(t, finder.addressGetter)
85+
86+
t.Log("generating fake AddressGetters")
87+
ctx := context.Background()
88+
defaultAddrs := []string{"127.0.0.1", "127.0.0.2"}
89+
overrideAddrs := []string{"192.168.1.1", "192.168.1.2", "192.168.1.3"}
90+
fakeGetter := func(ctx context.Context) ([]string, error) { return defaultAddrs, nil }
91+
92+
defaultUDPAddrs := []string{"127.1.0.1", "127.1.0.2"}
93+
overrideUDPAddrs := []string{"192.168.2.1", "192.168.2.2", "192.168.2.3"}
94+
fakeUDPGetter := func(ctx context.Context) ([]string, error) { return defaultUDPAddrs, nil }
95+
96+
t.Log("verifying getting a list of addresses from the finder after a getter function is provided")
97+
finder.SetGetter(fakeGetter)
98+
addrs, err := finder.GetUDPAddresses(ctx)
99+
require.NoError(t, err)
100+
require.Equal(t, defaultAddrs, addrs)
101+
102+
t.Log("verifying that overrides take precedent over the getter")
103+
finder.SetOverrides(overrideAddrs)
104+
addrs, err = finder.GetAddresses(ctx)
105+
require.NoError(t, err)
106+
require.Equal(t, overrideAddrs, addrs)
107+
108+
finder.SetGetter(fakeUDPGetter)
109+
addrs, err = finder.GetUDPAddresses(ctx)
110+
require.NoError(t, err)
111+
require.Equal(t, defaultUDPAddrs, addrs)
112+
113+
finder.SetUDPOverrides(overrideUDPAddrs)
114+
addrs, err = finder.GetUDPAddresses(ctx)
115+
require.NoError(t, err)
116+
require.Equal(t, overrideUDPAddrs, addrs)
117+
}

internal/manager/config.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,11 @@ type Config struct {
6767
GatewayAPIControllerName string
6868

6969
// Ingress status
70-
PublishService types.NamespacedName
71-
PublishStatusAddress []string
72-
UpdateStatus bool
70+
PublishServiceUDP types.NamespacedName
71+
PublishService types.NamespacedName
72+
PublishStatusAddress []string
73+
PublishStatusAddressUDP []string
74+
UpdateStatus bool
7375

7476
// Kubernetes API toggling
7577
IngressExtV1beta1Enabled bool
@@ -180,6 +182,11 @@ func (c *Config) FlagSet() *pflag.FlagSet {
180182
flagSet.StringSliceVar(&c.PublishStatusAddress, "publish-status-address", []string{},
181183
`User-provided addresses in comma-separated string format, for use in lieu of "publish-service" `+
182184
`when that Service lacks useful address information (for example, in bare-metal environments).`)
185+
flagSet.Var(NewValidatedValue(&c.PublishServiceUDP, namespacedNameFromFlagValue), "publish-service-udp", `Service fronting UDP routing resources in
186+
"namespace/name" format. The controller will update UDP route status information with this Service's
187+
endpoints. If omitted, the same Service will be used for both TCP and UDP routes.`)
188+
flagSet.StringSliceVar(&c.PublishStatusAddressUDP, "publish-status-address-udp", []string{}, `User-provided
189+
address CSV, for use in lieu of "publish-service-udp" when that Service lacks useful address information.`)
183190
flagSet.BoolVar(&c.UpdateStatus, "update-status", true,
184191
`Indicates if the ingress controller should update the status of resources (e.g. IP/Hostname for v1.Ingress, e.t.c.)`)
185192

0 commit comments

Comments
 (0)