/
address_finder.go
126 lines (105 loc) · 3.86 KB
/
address_finder.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package dataplane
import (
"context"
"fmt"
"net"
"strings"
"sync"
netv1 "k8s.io/api/networking/v1"
utilvalidation "k8s.io/apimachinery/pkg/util/validation"
)
// -----------------------------------------------------------------------------
// AddressFinder - Public Types
// -----------------------------------------------------------------------------
// AddressGetter is a function which can dynamically retrieve the list of IPs
// that the data-plane is listening on for ingress network traffic.
type AddressGetter func(ctx context.Context) ([]string, error)
// AddressFinder is a threadsafe metadata object which can provide the current
// live addresses in use by the dataplane at any point in time.
type AddressFinder struct {
overrideAddresses []string
addressGetter AddressGetter
lock sync.RWMutex
}
// NewAddressFinder provides a new AddressFinder which can be used to find the
// current listening addresses of the data-plane for ingress network traffic.
func NewAddressFinder() *AddressFinder {
return &AddressFinder{}
}
// -----------------------------------------------------------------------------
// AddressFinder - Public Methods
// -----------------------------------------------------------------------------
// SetGetter provides a callback function that the AddressFinder will use to
// dynamically retrieve the addresses of the data-plane.
func (a *AddressFinder) SetGetter(getter AddressGetter) {
a.lock.Lock()
defer a.lock.Unlock()
a.addressGetter = getter
}
// SetOverrides hard codes a specific list of addresses to be the addresses
// that this finder produces for the data-plane. To disable overrides, call
// this method again with an empty list.
func (a *AddressFinder) SetOverrides(addrs []string) {
a.lock.Lock()
defer a.lock.Unlock()
a.overrideAddresses = addrs
}
// GetAddresses provides a list of the addresses which the data-plane is
// listening on for ingress network traffic. Addresses can either be IP
// addresses or hostnames.
func (a *AddressFinder) GetAddresses(ctx context.Context) ([]string, error) {
a.lock.RLock()
defer a.lock.RUnlock()
if len(a.overrideAddresses) > 0 {
return a.overrideAddresses, nil
}
if a.addressGetter != nil {
return a.addressGetter(ctx)
}
return nil, fmt.Errorf("data-plane addresses can't be retrieved: no valid method available")
}
// GetLoadBalancerAddresses provides a list of the addresses which the
// data-plane is listening on for ingress network traffic, but provides the
// addresses in Kubernetes corev1.LoadBalancerIngress format. Addresses can be
// IP addresses or hostnames.
func (a *AddressFinder) GetLoadBalancerAddresses(ctx context.Context) ([]netv1.IngressLoadBalancerIngress, error) {
addrs, err := a.GetAddresses(ctx)
if err != nil {
return nil, err
}
return getAddressHelper(addrs)
}
// getAddressHelper converts a string slice of addresses (IPs or hostnames) into an IngressLoadBalancerIngress
// (https://pkg.go.dev/k8s.io/api/networking/v1#IngressLoadBalancerIngress), or an error if one of the given strings
// is neither a valid IP nor a valid hostname.
func getAddressHelper(addrs []string) ([]netv1.IngressLoadBalancerIngress, error) {
var loadBalancerAddresses []netv1.IngressLoadBalancerIngress
for _, addr := range addrs {
ing := netv1.IngressLoadBalancerIngress{}
if net.ParseIP(addr) != nil {
ing.IP = addr
} else {
if err := isValidHostname(addr); err != nil {
return nil, err
}
ing.Hostname = addr
}
loadBalancerAddresses = append(loadBalancerAddresses, ing)
}
return loadBalancerAddresses, nil
}
func isValidHostname(hostname string) error {
if hostname == "" {
return fmt.Errorf("empty address found")
}
var invalid bool
for _, label := range strings.Split(hostname, ".") {
if len(utilvalidation.IsDNS1123Label(label)) > 0 {
invalid = true
}
}
if invalid {
return fmt.Errorf("%s is not a valid DNS hostname", hostname)
}
return nil
}