Skip to content

Commit 3b79a50

Browse files
committed
make tunnel logic idempotent
Loadbalancers only sync once, so the tunnel logic didn't require to be called multiple times, however, with Gateway we can sync and change listeners and ports, so we need to make the tunnel logic idempotent and reconcile the host data plane with the configured portmaps.
1 parent 83ac769 commit 3b79a50

File tree

1 file changed

+52
-7
lines changed

1 file changed

+52
-7
lines changed

pkg/tunnels/tunnel.go

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,31 +41,49 @@ func (t *TunnelManager) SetupTunnels(containerName string) error {
4141
}
4242

4343
klog.V(0).Infof("setting IPv4 address %s associated to container %s", ipv4, containerName)
44-
output, err := AddIPToLocalInterface(ipv4)
45-
if err != nil {
46-
return fmt.Errorf("error adding IP to local interface: %w - %s", err, output)
44+
// check if the IP is already assigned to the local interface
45+
if !ipOnHost(ipv4) {
46+
output, err := AddIPToLocalInterface(ipv4)
47+
if err != nil {
48+
return fmt.Errorf("error adding IP to local interface: %w - %s", err, output)
49+
}
4750
}
4851

4952
// create tunnel from the ip:svcport to the localhost:portmap
5053
t.mu.Lock()
5154
defer t.mu.Unlock()
55+
_, ok := t.tunnels[containerName]
56+
if !ok {
57+
t.tunnels[containerName] = map[string]*tunnel{}
58+
}
59+
60+
// Reconcile: Remove tunnels that are not in portmaps
61+
for containerPort, tun := range t.tunnels[containerName] {
62+
if _, ok := portmaps[containerPort]; !ok {
63+
klog.V(0).Infof("removing tunnel for %s %s as it is no longer in portmaps", containerName, containerPort)
64+
tun.Stop() // nolint: errcheck
65+
delete(t.tunnels[containerName], containerPort)
66+
}
67+
}
68+
5269
// There is one IP per Service and a tunnel per Service Port
5370
for containerPort, hostPort := range portmaps {
5471
parts := strings.Split(containerPort, "/")
5572
if len(parts) != 2 {
5673
return fmt.Errorf("expected format port/protocol for container port, got %s", containerPort)
5774
}
5875

76+
if _, ok := t.tunnels[containerName][containerPort]; ok {
77+
klog.V(2).Infof("tunnel for %s %s already exists", containerName, containerPort)
78+
continue
79+
}
80+
5981
tun := NewTunnel(ipv4, parts[0], parts[1], "localhost", hostPort)
6082
// TODO check if we can leak tunnels
6183
err = tun.Start()
6284
if err != nil {
6385
return err
6486
}
65-
_, ok := t.tunnels[containerName]
66-
if !ok {
67-
t.tunnels[containerName] = map[string]*tunnel{}
68-
}
6987
t.tunnels[containerName][containerPort] = tun
7088
}
7189
return nil
@@ -88,6 +106,7 @@ func (t *TunnelManager) RemoveTunnels(containerName string) error {
88106
}
89107
tunnel.Stop() // nolint: errcheck
90108
}
109+
delete(t.tunnels, containerName)
91110

92111
klog.V(0).Infof("Removing IPv4 address %s associated to local interface", tunnelIP)
93112
output, err := RemoveIPFromLocalInterface(tunnelIP)
@@ -275,3 +294,29 @@ func (t *tunnel) handleUDPConnection(conn *net.UDPConn) error {
275294

276295
return err
277296
}
297+
298+
func ipOnHost(ip string) bool {
299+
ifaces, err := net.Interfaces()
300+
if err != nil {
301+
return false
302+
}
303+
for _, i := range ifaces {
304+
addrs, err := i.Addrs()
305+
if err != nil {
306+
continue
307+
}
308+
for _, addr := range addrs {
309+
var ipAddr net.IP
310+
switch v := addr.(type) {
311+
case *net.IPNet:
312+
ipAddr = v.IP
313+
case *net.IPAddr:
314+
ipAddr = v.IP
315+
}
316+
if ipAddr != nil && ipAddr.String() == ip {
317+
return true
318+
}
319+
}
320+
}
321+
return false
322+
}

0 commit comments

Comments
 (0)