1
// This Source Code Form is subject to the terms of the Mozilla Public
2
// License, v. 2.0. If a copy of the MPL was not distributed with this
3
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
// Package cluster provides functions to access, check and inspect Talos clusters.
15
"github.com/siderolabs/gen/maps"
16
"github.com/siderolabs/gen/xslices"
17
"k8s.io/client-go/kubernetes"
18
"k8s.io/client-go/rest"
20
k8s "github.com/siderolabs/talos/pkg/kubernetes"
21
"github.com/siderolabs/talos/pkg/machinery/client"
22
"github.com/siderolabs/talos/pkg/machinery/config/machine"
25
// ClientProvider builds Talos client by endpoint.
27
// Client instance should be cached and closed when Close() is called.
28
type ClientProvider interface {
29
// Client returns Talos client instance for default (if no endpoints are given) or
31
Client(endpoints ...string) (*client.Client, error)
32
// Close client connections.
36
// K8sProvider builds Kubernetes client to access Talos cluster.
37
type K8sProvider interface {
38
Kubeconfig(ctx context.Context) ([]byte, error)
39
K8sRestConfig(ctx context.Context) (*rest.Config, error)
40
K8sClient(ctx context.Context) (*kubernetes.Clientset, error)
41
K8sHelper(ctx context.Context) (*k8s.Client, error)
45
// CrashDumper captures Talos cluster state to the specified writer for debugging.
46
type CrashDumper interface {
47
CrashDump(ctx context.Context, out io.Writer)
50
// NodeInfo describes a Talos node.
56
// Info describes the Talos cluster.
58
// Nodes returns list of all node infos.
60
// NodesByType return list of node endpoints by type.
61
NodesByType(machine.Type) []NodeInfo
64
// Bootstrapper performs Talos cluster bootstrap.
65
type Bootstrapper interface {
66
Bootstrap(ctx context.Context, out io.Writer) error
69
// IPsToNodeInfos converts list of IPs to a list of NodeInfos.
70
func IPsToNodeInfos(ips []string) ([]NodeInfo, error) {
71
result := make([]NodeInfo, len(ips))
73
for i, ip := range ips {
74
info, err := IPToNodeInfo(ip)
85
// IPToNodeInfo converts a node internal IP to a NodeInfo.
86
func IPToNodeInfo(ip string) (*NodeInfo, error) {
87
parsed, err := netip.ParseAddr(ip)
94
IPs: []netip.Addr{parsed},
98
// NodesMatch asserts that the provided expected set of nodes match the actual set of nodes.
100
// Each expectedNode IPs should have a non-empty intersection with actualNode IPs.
101
func NodesMatch(expected, actual []NodeInfo) error {
102
actualNodes := xslices.ToMap(actual, func(n NodeInfo) (*NodeInfo, struct{}) { return &n, struct{}{} })
104
for _, expectedNodeInfo := range expected {
107
for actualNodeInfo := range actualNodes {
108
// expectedNodeInfo.IPs intersection with actualNodeInfo.IPs is not empty
109
if len(maps.Intersect(xslices.ToSet(actualNodeInfo.IPs), xslices.ToSet(expectedNodeInfo.IPs))) > 0 {
110
delete(actualNodes, actualNodeInfo)
119
return fmt.Errorf("can't find expected node with IPs %q", expectedNodeInfo.IPs)
123
if len(actualNodes) > 0 {
124
unexpectedIPs := xslices.FlatMap(maps.Keys(actualNodes), func(n *NodeInfo) []netip.Addr { return n.IPs })
126
sort.Slice(unexpectedIPs, func(i, j int) bool { return unexpectedIPs[i].Less(unexpectedIPs[j]) })
128
return fmt.Errorf("unexpected nodes with IPs %q", unexpectedIPs)