mirror of
				https://github.com/siderolabs/talos.git
				synced 2025-10-31 08:21:25 +01:00 
			
		
		
		
	`config.Container` implements a multi-doc container which implements both `Container` interface (encoding, validation, etc.), and `Conifg` interface (accessing parts of the config). Refactor `generate` and `bundle` packages to support multi-doc, and provide backwards compatibility. Implement a first (mostly example) machine config document for SideroLink API URL. Many places don't properly support multi-doc yet (e.g. config patches). Signed-off-by: Andrey Smirnov <andrey.smirnov@talos-systems.com>
		
			
				
	
	
		
			133 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			133 lines
		
	
	
		
			3.6 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 cluster provides functions to access, check and inspect Talos clusters.
 | |
| package cluster
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 	"net/netip"
 | |
| 	"sort"
 | |
| 
 | |
| 	"github.com/siderolabs/gen/maps"
 | |
| 	"github.com/siderolabs/gen/slices"
 | |
| 	"k8s.io/client-go/kubernetes"
 | |
| 	"k8s.io/client-go/rest"
 | |
| 
 | |
| 	k8s "github.com/siderolabs/talos/pkg/kubernetes"
 | |
| 	"github.com/siderolabs/talos/pkg/machinery/client"
 | |
| 	"github.com/siderolabs/talos/pkg/machinery/config/machine"
 | |
| )
 | |
| 
 | |
| // ClientProvider builds Talos client by endpoint.
 | |
| //
 | |
| // Client instance should be cached and closed when Close() is called.
 | |
| type ClientProvider interface {
 | |
| 	// Client returns Talos client instance for default (if no endpoints are given) or
 | |
| 	// specific endpoint.
 | |
| 	Client(endpoints ...string) (*client.Client, error)
 | |
| 	// Close client connections.
 | |
| 	Close() error
 | |
| }
 | |
| 
 | |
| // K8sProvider builds Kubernetes client to access Talos cluster.
 | |
| type K8sProvider interface {
 | |
| 	Kubeconfig(ctx context.Context) ([]byte, error)
 | |
| 	K8sRestConfig(ctx context.Context) (*rest.Config, error)
 | |
| 	K8sClient(ctx context.Context) (*kubernetes.Clientset, error)
 | |
| 	K8sHelper(ctx context.Context) (*k8s.Client, error)
 | |
| 	K8sClose() error
 | |
| }
 | |
| 
 | |
| // CrashDumper captures Talos cluster state to the specified writer for debugging.
 | |
| type CrashDumper interface {
 | |
| 	CrashDump(ctx context.Context, out io.Writer)
 | |
| }
 | |
| 
 | |
| // NodeInfo describes a Talos node.
 | |
| type NodeInfo struct {
 | |
| 	InternalIP netip.Addr
 | |
| 	IPs        []netip.Addr
 | |
| }
 | |
| 
 | |
| // Info describes the Talos cluster.
 | |
| type Info interface {
 | |
| 	// Nodes returns list of all node infos.
 | |
| 	Nodes() []NodeInfo
 | |
| 	// NodesByType return list of node endpoints by type.
 | |
| 	NodesByType(machine.Type) []NodeInfo
 | |
| }
 | |
| 
 | |
| // Bootstrapper performs Talos cluster bootstrap.
 | |
| type Bootstrapper interface {
 | |
| 	Bootstrap(ctx context.Context, out io.Writer) error
 | |
| }
 | |
| 
 | |
| // IPsToNodeInfos converts list of IPs to a list of NodeInfos.
 | |
| func IPsToNodeInfos(ips []string) ([]NodeInfo, error) {
 | |
| 	result := make([]NodeInfo, len(ips))
 | |
| 
 | |
| 	for i, ip := range ips {
 | |
| 		info, err := IPToNodeInfo(ip)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 
 | |
| 		result[i] = *info
 | |
| 	}
 | |
| 
 | |
| 	return result, nil
 | |
| }
 | |
| 
 | |
| // IPToNodeInfo converts a node internal IP to a NodeInfo.
 | |
| func IPToNodeInfo(ip string) (*NodeInfo, error) {
 | |
| 	parsed, err := netip.ParseAddr(ip)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	return &NodeInfo{
 | |
| 		InternalIP: parsed,
 | |
| 		IPs:        []netip.Addr{parsed},
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| // NodesMatch asserts that the provided expected set of nodes match the actual set of nodes.
 | |
| //
 | |
| // Each expectedNode IPs should have a non-empty intersection with actualNode IPs.
 | |
| func NodesMatch(expected, actual []NodeInfo) error {
 | |
| 	actualNodes := slices.ToMap(actual, func(n NodeInfo) (*NodeInfo, struct{}) { return &n, struct{}{} })
 | |
| 
 | |
| 	for _, expectedNodeInfo := range expected {
 | |
| 		found := false
 | |
| 
 | |
| 		for actualNodeInfo := range actualNodes {
 | |
| 			// expectedNodeInfo.IPs intersection with actualNodeInfo.IPs is not empty
 | |
| 			if len(maps.Intersect(slices.ToSet(actualNodeInfo.IPs), slices.ToSet(expectedNodeInfo.IPs))) > 0 {
 | |
| 				delete(actualNodes, actualNodeInfo)
 | |
| 
 | |
| 				found = true
 | |
| 
 | |
| 				break
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if !found {
 | |
| 			return fmt.Errorf("can't find expected node with IPs %q", expectedNodeInfo.IPs)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if len(actualNodes) > 0 {
 | |
| 		unexpectedIPs := slices.FlatMap(maps.Keys(actualNodes), func(n *NodeInfo) []netip.Addr { return n.IPs })
 | |
| 
 | |
| 		sort.Slice(unexpectedIPs, func(i, j int) bool { return unexpectedIPs[i].Less(unexpectedIPs[j]) })
 | |
| 
 | |
| 		return fmt.Errorf("unexpected nodes with IPs %q", unexpectedIPs)
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 |