images / images (push) Successful in 14m46s
A Service of type LoadBalancer with loadBalancerClass reweave.levk.dev/node-address names its address in spec.loadBalancerIP. Each node's weave-npc publishes the IPv4 addresses on its host interfaces and its weave bridge (Node annotations reweave.levk.dev/addresses and reweave.levk.dev/weave). The one node that holds a Service's address is its holder: it alone writes status.loadBalancer (ipMode Proxy, so kube-proxy installs nothing for the address) and DNATs the address's ports to ready endpoints (EndpointSlices), without SNAT, from nat PREROUTING ahead of kube-proxy. Every other node marks the connections the holder forwards to its pods (new, from the holder's bridge MAC, from outside the pod network) and routes their replies back to the holder's bridge through a per-holder policy rule and table, so the holder's conntrack undoes the DNAT and the reply leaves from the address the client connected to. Fail closed: an address no node holds, or that several nodes hold, is served by no node, and its status is cleared (by its holder, or by the first node by name if none holds it). Compute is the policy as a pure function; Datapath replaces exactly the chains it owns with iptables-restore --noflush and keeps its rules and tables exact. weave-npc runs it unless --lb=false; the ClusterRole gains services (watch), services/status (patch), endpointslices (watch), nodes (patch). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UHXzGrsxfkGk8LmwrBbHNZ
88 lines
2.2 KiB
Go
88 lines
2.2 KiB
Go
package lb
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/vishvananda/netlink"
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
// Local is what this node publishes about itself, read from its interfaces.
|
|
type Local struct {
|
|
Addresses []net.IP
|
|
WeaveIP net.IP
|
|
WeaveMAC net.HardwareAddr
|
|
PodNet *net.IPNet // the weave allocation range: the bridge address's network
|
|
}
|
|
|
|
// overlay- and container-side interfaces: their addresses are never held ones
|
|
var overlayPrefixes = []string{"weave", "datapath", "vxlan-", "veth", "docker", "cni"}
|
|
|
|
func overlay(name string) bool {
|
|
for _, p := range overlayPrefixes {
|
|
if strings.HasPrefix(name, p) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// ReadLocal lists the global IPv4 addresses on host interfaces, and the weave bridge's address and MAC.
|
|
func ReadLocal() (Local, error) {
|
|
var l Local
|
|
links, err := netlink.LinkList()
|
|
if err != nil {
|
|
return l, err
|
|
}
|
|
for _, link := range links {
|
|
name := link.Attrs().Name
|
|
addrs, err := netlink.AddrList(link, unix.AF_INET)
|
|
if err != nil {
|
|
return l, fmt.Errorf("%s: %v", name, err)
|
|
}
|
|
if name == bridge {
|
|
for _, a := range addrs {
|
|
if a.IPNet != nil && a.IP.To4() != nil {
|
|
l.WeaveIP = a.IP.To4()
|
|
l.WeaveMAC = link.Attrs().HardwareAddr
|
|
l.PodNet = &net.IPNet{IP: a.IP.Mask(a.Mask), Mask: a.Mask}
|
|
break
|
|
}
|
|
}
|
|
continue
|
|
}
|
|
if overlay(name) || link.Attrs().Flags&net.FlagLoopback != 0 {
|
|
continue
|
|
}
|
|
for _, a := range addrs {
|
|
if a.Scope == unix.RT_SCOPE_UNIVERSE && a.IP.To4() != nil && a.IP.IsGlobalUnicast() {
|
|
l.Addresses = append(l.Addresses, a.IP.To4())
|
|
}
|
|
}
|
|
}
|
|
// an address inside the pod network is never a host one (whatever interface it is on)
|
|
if l.PodNet != nil {
|
|
kept := l.Addresses[:0]
|
|
for _, ip := range l.Addresses {
|
|
if !l.PodNet.Contains(ip) {
|
|
kept = append(kept, ip)
|
|
}
|
|
}
|
|
l.Addresses = kept
|
|
}
|
|
sort.Slice(l.Addresses, func(i, j int) bool { return l.Addresses[i].String() < l.Addresses[j].String() })
|
|
return l, nil
|
|
}
|
|
|
|
// Annotations is what this node's Node object should carry for l.
|
|
func (l Local) Annotations() map[string]string {
|
|
a := map[string]string{AnnAddresses: FormatAddresses(l.Addresses)}
|
|
if l.WeaveIP != nil && l.WeaveMAC != nil {
|
|
a[AnnWeave] = l.WeaveIP.String() + "," + l.WeaveMAC.String()
|
|
}
|
|
return a
|
|
}
|