mirror of
https://github.com/siderolabs/talos.git
synced 2025-10-08 14:11:13 +02:00
There's a cyclic dependency on siderolink library which imports talos machinery back. We will fix that after we get talos pushed under a new name. Signed-off-by: Andrey Smirnov <andrey.smirnov@talos-systems.com>
132 lines
3.5 KiB
Go
132 lines
3.5 KiB
Go
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
package check
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"net/url"
|
|
"sort"
|
|
|
|
"github.com/siderolabs/gen/maps"
|
|
"github.com/siderolabs/gen/slices"
|
|
|
|
"github.com/siderolabs/talos/pkg/cluster"
|
|
machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine"
|
|
"github.com/siderolabs/talos/pkg/machinery/client"
|
|
"github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1/machine"
|
|
)
|
|
|
|
// EtcdConsistentAssertion checks that etcd membership is consistent across nodes.
|
|
//
|
|
//nolint:gocyclo
|
|
func EtcdConsistentAssertion(ctx context.Context, cl ClusterInfo) error {
|
|
cli, err := cl.Client()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var nodes []cluster.NodeInfo
|
|
|
|
initNodes := cl.NodesByType(machine.TypeInit)
|
|
nodes = append(nodes, initNodes...)
|
|
controlPlaneNodes := cl.NodesByType(machine.TypeControlPlane)
|
|
|
|
nodes = append(nodes, controlPlaneNodes...)
|
|
|
|
nodesCtx := client.WithNodes(ctx, mapIPsToStrings(mapNodeInfosToInternalIPs(nodes))...)
|
|
|
|
resp, err := cli.EtcdMemberList(nodesCtx, &machineapi.EtcdMemberListRequest{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
type data struct {
|
|
hostname string
|
|
id uint64
|
|
isLearner bool
|
|
}
|
|
|
|
knownMembers := map[data]struct{}{}
|
|
|
|
messages := resp.GetMessages()
|
|
if len(messages) == 0 {
|
|
return errors.New("no messages returned")
|
|
}
|
|
|
|
sort.Slice(messages, func(i, j int) bool {
|
|
return messages[i].GetMetadata().GetHostname() < messages[j].GetMetadata().GetHostname()
|
|
})
|
|
|
|
for i, message := range messages {
|
|
if i == 0 {
|
|
// Fill data using first message
|
|
for _, member := range message.Members {
|
|
knownMembers[data{member.Hostname, member.Id, member.IsLearner}] = struct{}{}
|
|
}
|
|
|
|
continue
|
|
}
|
|
|
|
node := message.Metadata.GetHostname()
|
|
|
|
if len(message.Members) != len(knownMembers) {
|
|
expected := maps.ToSlice(knownMembers, func(k data, v struct{}) string { return k.hostname })
|
|
actual := slices.Map(message.Members, (*machineapi.EtcdMember).GetHostname)
|
|
|
|
return fmt.Errorf("%s: expected to have %v members, got %v", node, expected, actual)
|
|
}
|
|
|
|
// check that member list is the same on all nodes
|
|
for _, member := range message.Members {
|
|
if _, found := knownMembers[data{member.Hostname, member.Id, member.IsLearner}]; !found {
|
|
return fmt.Errorf("%s: found unexpected etcd member %s", node, member.Hostname)
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// EtcdControlPlaneNodesAssertion checks that etcd nodes are control plane nodes.
|
|
func EtcdControlPlaneNodesAssertion(ctx context.Context, cl ClusterInfo) error {
|
|
cli, err := cl.Client()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
nodes := append(cl.NodesByType(machine.TypeInit), cl.NodesByType(machine.TypeControlPlane)...)
|
|
|
|
resp, err := cli.EtcdMemberList(ctx, &machineapi.EtcdMemberListRequest{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
members := resp.GetMessages()[0].GetMembers()
|
|
|
|
var memberIPs []string
|
|
|
|
for _, member := range members {
|
|
for _, peerURL := range member.GetPeerUrls() {
|
|
parsed, err2 := url.Parse(peerURL)
|
|
if err2 != nil {
|
|
return err2
|
|
}
|
|
|
|
memberIP := parsed.Hostname()
|
|
memberIPs = append(memberIPs, memberIP)
|
|
}
|
|
}
|
|
|
|
controlPlaneNodeIPs := mapIPsToStrings(flatMapNodeInfosToIPs(nodes))
|
|
if !maps.Contains(slices.ToSet(controlPlaneNodeIPs), memberIPs) {
|
|
return fmt.Errorf("etcd member ips %q are not subset of control plane node ips %q",
|
|
memberIPs, controlPlaneNodeIPs)
|
|
}
|
|
|
|
return nil
|
|
}
|