talos-upgrade/pkg/node/node.go
2023-10-20 17:28:58 -04:00

135 lines
2.9 KiB
Go

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
}