Skip to content

Commit 6e19e87

Browse files
Prevent CPLB VIPs to be etcd's peer address
This fixes only the automatic address detection for etcd. If a user configures them manually it doesn't attempt to prevent the error. Signed-off-by: Juan-Luis de Sousa-Valadas Castaño <[email protected]>
1 parent 8e5624c commit 6e19e87

File tree

5 files changed

+54
-25
lines changed

5 files changed

+54
-25
lines changed

cmd/controller/controller.go

+25
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import (
3434
workercmd "github.com/k0sproject/k0s/cmd/worker"
3535
"github.com/k0sproject/k0s/internal/pkg/dir"
3636
"github.com/k0sproject/k0s/internal/pkg/file"
37+
"github.com/k0sproject/k0s/internal/pkg/iface"
3738
k0slog "github.com/k0sproject/k0s/internal/pkg/log"
3839
"github.com/k0sproject/k0s/internal/pkg/sysinfo"
3940
"github.com/k0sproject/k0s/internal/sync/value"
@@ -231,6 +232,9 @@ func (c *command) start(ctx context.Context) error {
231232
K0sVars: c.K0sVars,
232233
LogLevel: c.LogLevels.Etcd,
233234
}
235+
// if cplb is disabled use the default primary address
236+
storageBackend.(*controller.Etcd).Config.PeerAddress = etcdPeerAddress(nodeConfig.Spec.Network.ControlPlaneLoadBalancing)
237+
234238
default:
235239
return fmt.Errorf("invalid storage type: %s", nodeConfig.Spec.Storage.Type)
236240
}
@@ -641,6 +645,27 @@ func (c *command) start(ctx context.Context) error {
641645
return nil
642646
}
643647

648+
func etcdPeerAddress(cplb *v1beta1.ControlPlaneLoadBalancingSpec) string {
649+
if cplb != nil && cplb.Enabled {
650+
cplbAddresses := cplb.VirtualIPs()
651+
addresses, err := iface.GetPublicAddresses()
652+
if err != nil {
653+
logrus.WithError(err).Errorf("Failed to get the primary address, using %s", addresses[0])
654+
return addresses[0]
655+
}
656+
for _, addr := range addresses {
657+
if !slices.Contains(cplbAddresses, addr) {
658+
return addr
659+
}
660+
}
661+
}
662+
addr, err := iface.FirstPublicAddress()
663+
if err != nil {
664+
logrus.WithError(err).Errorf("Failed to get the primary address, using %s", addr)
665+
}
666+
return addr
667+
}
668+
644669
func (c *command) startWorker(ctx context.Context, profile string, nodeConfig *v1beta1.ClusterConfig) error {
645670
var bootstrapConfig string
646671
if !file.Exists(c.K0sVars.KubeletAuthConfigPath) {

internal/pkg/iface/iface.go

+20-8
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,18 @@ func CollectAllIPs() (addresses []net.IP, err error) {
5959
// FirstPublicAddress return the first found non-local IPv4 address that's not part of pod network
6060
// if any interface does not have any IPv4 address then return the first found non-local IPv6 address
6161
func FirstPublicAddress() (string, error) {
62+
addr, err := GetPublicAddresses()
63+
return addr[0], err
64+
}
65+
66+
// GetPublicAddresses returns a list of all public network addresses on a node.
67+
// The slice contains the ipv4 addresses followed by the ipv6 addresses
68+
func GetPublicAddresses() ([]string, error) {
6269
ifs, err := net.Interfaces()
6370
if err != nil {
64-
return "127.0.0.1", fmt.Errorf("failed to list network interfaces: %w", err)
71+
return []string{"127.0.0.1"}, fmt.Errorf("failed to list network interfaces: %w", err)
6572
}
66-
ipv6addr := ""
73+
publicAddresses := []string{}
6774
for _, i := range ifs {
6875
switch {
6976
// Skip calico CNI interface
@@ -91,18 +98,23 @@ func FirstPublicAddress() (string, error) {
9198
// check the address type and skip if loopback
9299
if ipnet, ok := a.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
93100
if ipnet.IP.To4() != nil {
94-
return ipnet.IP.String(), nil
101+
publicAddresses = append(publicAddresses, ipnet.IP.String())
95102
}
96-
if ipnet.IP.To16() != nil && ipv6addr == "" {
97-
ipv6addr = ipnet.IP.String()
103+
}
104+
}
105+
for _, a := range addresses {
106+
// check the address type and skip if loopback
107+
if ipnet, ok := a.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
108+
if ipnet.IP.To16() != nil {
109+
publicAddresses = append(publicAddresses, ipnet.IP.String())
98110
}
99111
}
100112
}
101113
}
102-
if ipv6addr != "" {
103-
return ipv6addr, nil
114+
if len(publicAddresses) > 0 {
115+
return publicAddresses, nil
104116
}
105117

106118
logrus.Warn("failed to find any non-local, non podnetwork addresses on host, defaulting public address to 127.0.0.1")
107-
return "127.0.0.1", nil
119+
return []string{"127.0.0.1"}, nil
108120
}

pkg/apis/k0s/v1beta1/clusterconfig_types_test.go

-8
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ import (
2020
"encoding/json"
2121
"testing"
2222

23-
"github.com/k0sproject/k0s/internal/pkg/iface"
24-
2523
corev1 "k8s.io/api/core/v1"
2624
"sigs.k8s.io/yaml"
2725

@@ -55,9 +53,6 @@ metadata:
5553
c, err := ConfigFromString(yamlData)
5654
assert.NoError(t, err)
5755
assert.Equal(t, EtcdStorageType, c.Spec.Storage.Type)
58-
addr, err := iface.FirstPublicAddress()
59-
assert.NoError(t, err)
60-
assert.Equal(t, addr, c.Spec.Storage.Etcd.PeerAddress)
6156
}
6257

6358
func TestEmptyClusterSpec(t *testing.T) {
@@ -135,9 +130,6 @@ spec:
135130
c, err := ConfigFromString(yamlData)
136131
assert.NoError(t, err)
137132
assert.Equal(t, EtcdStorageType, c.Spec.Storage.Type)
138-
addr, err := iface.FirstPublicAddress()
139-
assert.NoError(t, err)
140-
assert.Equal(t, addr, c.Spec.Storage.Etcd.PeerAddress)
141133
}
142134

143135
func TestNetworkValidation_Custom(t *testing.T) {

pkg/apis/k0s/v1beta1/cplb.go

+9
Original file line numberDiff line numberDiff line change
@@ -309,3 +309,12 @@ func (k *KeepalivedSpec) Validate() (errs []error) {
309309

310310
return errs
311311
}
312+
313+
// VirtualIPs returns a list of virtual IPs used by the VRRP instances.
314+
func (c *ControlPlaneLoadBalancingSpec) VirtualIPs() []string {
315+
var vips []string
316+
for _, vrrp := range c.Keepalived.VRRPInstances {
317+
vips = append(vips, vrrp.VirtualIPs...)
318+
}
319+
return vips
320+
}

pkg/apis/k0s/v1beta1/storage.go

-9
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,10 @@ import (
2727
"slices"
2828
"strings"
2929

30-
"github.com/k0sproject/k0s/internal/pkg/iface"
3130
"github.com/k0sproject/k0s/pkg/config/kine"
3231
"github.com/k0sproject/k0s/pkg/constant"
3332

3433
"k8s.io/apimachinery/pkg/util/validation/field"
35-
36-
"github.com/sirupsen/logrus"
3734
)
3835

3936
var _ Validateable = (*StorageSpec)(nil)
@@ -158,14 +155,8 @@ type ExternalCluster struct {
158155

159156
// DefaultEtcdConfig creates EtcdConfig with sane defaults
160157
func DefaultEtcdConfig() *EtcdConfig {
161-
addr, err := iface.FirstPublicAddress()
162-
if err != nil {
163-
logrus.Warnf("failed to resolve etcd peering address automatically, using loopback")
164-
addr = "127.0.0.1"
165-
}
166158
return &EtcdConfig{
167159
ExternalCluster: nil,
168-
PeerAddress: addr,
169160
ExtraArgs: make(map[string]string),
170161
}
171162
}

0 commit comments

Comments
 (0)