135 lines
2.9 KiB
Go
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
|
|
}
|