package node import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/resource" "github.com/pkg/errors" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" "golang.org/x/mod/semver" ) type Role string const ( RoleControlPlane = "controlplane" RoleWorker = "worker" ) type Node struct { Client *client.Client Metadata *resource.Metadata Spec *cluster.MemberSpec } func (n *Node) Update() error { if n == nil || n.Client == nil { return errors.New("no Talos client found in Node") } updatedNodeResp, err := n.Client.COSI.Get(context.Background(), resource.NewMetadata("cluster", "Members.cluster.talos.dev", n.Metadata.ID(), n.Metadata.Version())) if err != nil { return errors.Wrap(err, "failed to update node data") } updatedNode, ok := updatedNodeResp.Spec().(*cluster.MemberSpec) if !ok { return errors.New("failed to type assert node data") } n.Metadata = updatedNodeResp.Metadata() n.Spec = updatedNode return nil } func (n *Node) Version() (string, error) { if n.Spec == nil { return "", errors.New("no node member data yet") } matches := VersionRegexp.FindStringSubmatch(n.Spec.OperatingSystem) if len(matches) != 2 { return "", errors.Errorf("failed to parse OperatingSystem %q for version", n.Spec.OperatingSystem) } if !semver.IsValid(matches[1]) { return "", errors.Errorf("invalid semver in release version: %q", matches[1]) } return matches[1], nil } func GetNodes(ctx context.Context, c *client.Client) (ret []*Node, err error) { if c == nil { return nil, errors.New("no Talos client provided") } list, err := c.COSI.List(ctx, resource.NewMetadata("cluster", "Members.cluster.talos.dev", "", resource.VersionUndefined)) if err != nil { return nil, fmt.Errorf("failed to list cluster members: %w", err) } for _, i := range list.Items { node, ok := i.Spec().(*cluster.MemberSpec) if !ok { return nil, fmt.Errorf("failed to interpret result as a node: %w", err) } ret = append(ret, &Node{ Client: c, Metadata: i.Metadata(), Spec: node, }) } return ret, nil } func LowestVersion(list []*Node) (lowest string, err error) { if list == nil { return "", errors.New("no nodes") } for _, n := range list { nodeVersion, err := n.Version() if err != nil { return "", errors.Wrapf(err, "failed to parse node version") } if lowest == "" { lowest = nodeVersion } if semver.Compare(lowest, nodeVersion) > 0 { lowest = nodeVersion } } return lowest, nil } func HighestVersion(list []*Node) (highest string, err error) { if list == nil { return "", errors.New("no nodes") } for _, n := range list { nodeVersion, err := n.Version() if err != nil { return "", errors.Wrapf(err, "failed to parse node version") } if highest == "" { highest = nodeVersion } if semver.Compare(highest, nodeVersion) < 0 { highest = nodeVersion } } return highest, nil }