createNode: add --wait and --timeout flags
- new struct: createNodeOpts for wait and timeout values - new commands for creating/adding multiple nodes which then wait for all nodes to be up, if specified - tests/e2e: new test for adding a master node
This commit is contained in:
parent
eeaa25dce6
commit
42ee62e552
@ -23,6 +23,7 @@ package create
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
@ -36,6 +37,8 @@ import (
|
|||||||
// NewCmdCreateNode returns a new cobra command
|
// NewCmdCreateNode returns a new cobra command
|
||||||
func NewCmdCreateNode() *cobra.Command {
|
func NewCmdCreateNode() *cobra.Command {
|
||||||
|
|
||||||
|
createNodeOpts := k3d.CreateNodeOpts{}
|
||||||
|
|
||||||
// create new command
|
// create new command
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "node NAME",
|
Use: "node NAME",
|
||||||
@ -44,12 +47,10 @@ func NewCmdCreateNode() *cobra.Command {
|
|||||||
Args: cobra.ExactArgs(1), // exactly one name accepted // TODO: if not specified, inherit from cluster that the node shall belong to, if that is specified
|
Args: cobra.ExactArgs(1), // exactly one name accepted // TODO: if not specified, inherit from cluster that the node shall belong to, if that is specified
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
nodes, cluster := parseCreateNodeCmd(cmd, args)
|
nodes, cluster := parseCreateNodeCmd(cmd, args)
|
||||||
for _, node := range nodes {
|
if err := k3dc.AddNodesToCluster(cmd.Context(), runtimes.SelectedRuntime, nodes, cluster, createNodeOpts); err != nil {
|
||||||
if err := k3dc.AddNodeToCluster(cmd.Context(), runtimes.SelectedRuntime, node, cluster); err != nil {
|
log.Errorf("Failed to add nodes '%+v' to cluster '%s'", nodes, cluster.Name)
|
||||||
log.Errorf("Failed to add node '%s' to cluster '%s'", node.Name, cluster.Name)
|
|
||||||
log.Errorln(err)
|
log.Errorln(err)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,6 +64,9 @@ func NewCmdCreateNode() *cobra.Command {
|
|||||||
|
|
||||||
cmd.Flags().StringP("image", "i", fmt.Sprintf("%s:%s", k3d.DefaultK3sImageRepo, version.GetK3sVersion(false)), "Specify k3s image used for the node(s)")
|
cmd.Flags().StringP("image", "i", fmt.Sprintf("%s:%s", k3d.DefaultK3sImageRepo, version.GetK3sVersion(false)), "Specify k3s image used for the node(s)")
|
||||||
|
|
||||||
|
cmd.Flags().BoolVar(&createNodeOpts.Wait, "wait", false, "Wait for the node(s) to be ready before returning.")
|
||||||
|
cmd.Flags().DurationVar(&createNodeOpts.Timeout, "timeout", 0*time.Second, "Maximum waiting time for '--wait' before canceling/returning.")
|
||||||
|
|
||||||
// done
|
// done
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|||||||
@ -155,7 +155,7 @@ func CreateCluster(ctx context.Context, runtime k3drt.Runtime, cluster *k3d.Clus
|
|||||||
|
|
||||||
// create node
|
// create node
|
||||||
log.Infof("Creating node '%s'", node.Name)
|
log.Infof("Creating node '%s'", node.Name)
|
||||||
if err := CreateNode(ctx, runtime, node); err != nil {
|
if err := CreateNode(ctx, runtime, node, k3d.CreateNodeOpts{}); err != nil {
|
||||||
log.Errorln("Failed to create node")
|
log.Errorln("Failed to create node")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -301,7 +301,7 @@ func CreateCluster(ctx context.Context, runtime k3drt.Runtime, cluster *k3d.Clus
|
|||||||
}
|
}
|
||||||
cluster.Nodes = append(cluster.Nodes, lbNode) // append lbNode to list of cluster nodes, so it will be considered during rollback
|
cluster.Nodes = append(cluster.Nodes, lbNode) // append lbNode to list of cluster nodes, so it will be considered during rollback
|
||||||
log.Infof("Creating LoadBalancer '%s'", lbNode.Name)
|
log.Infof("Creating LoadBalancer '%s'", lbNode.Name)
|
||||||
if err := CreateNode(ctx, runtime, lbNode); err != nil {
|
if err := CreateNode(ctx, runtime, lbNode, k3d.CreateNodeOpts{}); err != nil {
|
||||||
log.Errorln("Failed to create loadbalancer")
|
log.Errorln("Failed to create loadbalancer")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,10 +33,11 @@ import (
|
|||||||
"github.com/rancher/k3d/pkg/runtimes"
|
"github.com/rancher/k3d/pkg/runtimes"
|
||||||
k3d "github.com/rancher/k3d/pkg/types"
|
k3d "github.com/rancher/k3d/pkg/types"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AddNodeToCluster adds a node to an existing cluster
|
// AddNodeToCluster adds a node to an existing cluster
|
||||||
func AddNodeToCluster(ctx context.Context, runtime runtimes.Runtime, node *k3d.Node, cluster *k3d.Cluster) error {
|
func AddNodeToCluster(ctx context.Context, runtime runtimes.Runtime, node *k3d.Node, cluster *k3d.Cluster, createNodeOpts k3d.CreateNodeOpts) error {
|
||||||
cluster, err := GetCluster(ctx, runtime, cluster)
|
cluster, err := GetCluster(ctx, runtime, cluster)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Failed to find specified cluster '%s'", cluster.Name)
|
log.Errorf("Failed to find specified cluster '%s'", cluster.Name)
|
||||||
@ -126,7 +127,7 @@ func AddNodeToCluster(ctx context.Context, runtime runtimes.Runtime, node *k3d.N
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := CreateNode(ctx, runtime, node); err != nil {
|
if err := CreateNode(ctx, runtime, node, k3d.CreateNodeOpts{}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,17 +142,70 @@ func AddNodeToCluster(ctx context.Context, runtime runtimes.Runtime, node *k3d.N
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateNodes creates a list of nodes
|
// AddNodesToCluster adds multiple nodes to a chosen cluster
|
||||||
func CreateNodes(ctx context.Context, runtime runtimes.Runtime, nodes []*k3d.Node) { // TODO: pass `--atomic` flag, so we stop and return an error if any node creation fails?
|
func AddNodesToCluster(ctx context.Context, runtime runtimes.Runtime, nodes []*k3d.Node, cluster *k3d.Cluster, createNodeOpts k3d.CreateNodeOpts) error {
|
||||||
|
if createNodeOpts.Timeout > 0*time.Second {
|
||||||
|
var cancel context.CancelFunc
|
||||||
|
ctx, cancel = context.WithTimeout(ctx, createNodeOpts.Timeout)
|
||||||
|
defer cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeWaitGroup, ctx := errgroup.WithContext(ctx)
|
||||||
for _, node := range nodes {
|
for _, node := range nodes {
|
||||||
if err := CreateNode(ctx, runtime, node); err != nil {
|
if err := AddNodeToCluster(ctx, runtime, node, cluster, k3d.CreateNodeOpts{}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if createNodeOpts.Wait {
|
||||||
|
currentNode := node
|
||||||
|
nodeWaitGroup.Go(func() error {
|
||||||
|
log.Debugf("Starting to wait for node '%s'", currentNode.Name)
|
||||||
|
return WaitForNodeLogMessage(ctx, runtime, currentNode, k3d.ReadyLogMessageByRole[currentNode.Role], time.Time{})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := nodeWaitGroup.Wait(); err != nil {
|
||||||
|
log.Errorln("Failed to bring up all nodes in time. Check the logs:")
|
||||||
|
log.Errorf(">>> %+v", err)
|
||||||
|
return fmt.Errorf("Failed to add nodes")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateNodes creates a list of nodes
|
||||||
|
func CreateNodes(ctx context.Context, runtime runtimes.Runtime, nodes []*k3d.Node, createNodeOpts k3d.CreateNodeOpts) error { // TODO: pass `--atomic` flag, so we stop and return an error if any node creation fails?
|
||||||
|
if createNodeOpts.Timeout > 0*time.Second {
|
||||||
|
var cancel context.CancelFunc
|
||||||
|
ctx, cancel = context.WithTimeout(ctx, createNodeOpts.Timeout)
|
||||||
|
defer cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeWaitGroup, ctx := errgroup.WithContext(ctx)
|
||||||
|
for _, node := range nodes {
|
||||||
|
if err := CreateNode(ctx, runtime, node, k3d.CreateNodeOpts{}); err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
}
|
}
|
||||||
|
if createNodeOpts.Wait {
|
||||||
|
currentNode := node
|
||||||
|
nodeWaitGroup.Go(func() error {
|
||||||
|
log.Debugf("Starting to wait for node '%s'", currentNode.Name)
|
||||||
|
return WaitForNodeLogMessage(ctx, runtime, currentNode, k3d.ReadyLogMessageByRole[currentNode.Role], time.Time{})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := nodeWaitGroup.Wait(); err != nil {
|
||||||
|
log.Errorln("Failed to bring up all nodes in time. Check the logs:")
|
||||||
|
log.Errorf(">>> %+v", err)
|
||||||
|
return fmt.Errorf("Failed to create nodes")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// CreateNode creates a new containerized k3s node
|
// CreateNode creates a new containerized k3s node
|
||||||
func CreateNode(ctx context.Context, runtime runtimes.Runtime, node *k3d.Node) error {
|
func CreateNode(ctx context.Context, runtime runtimes.Runtime, node *k3d.Node, createNodeOpts k3d.CreateNodeOpts) error {
|
||||||
log.Debugf("Creating node from spec\n%+v", node)
|
log.Debugf("Creating node from spec\n%+v", node)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@ -47,7 +47,7 @@ const DefaultObjectNamePrefix = "k3d"
|
|||||||
// ReadyLogMessageMaster defines the log messages we wait for until a master node is considered ready
|
// ReadyLogMessageMaster defines the log messages we wait for until a master node is considered ready
|
||||||
var ReadyLogMessageByRole = map[Role]string{
|
var ReadyLogMessageByRole = map[Role]string{
|
||||||
MasterRole: "Wrote kubeconfig",
|
MasterRole: "Wrote kubeconfig",
|
||||||
WorkerRole: "",
|
WorkerRole: "Successfully registered node",
|
||||||
LoadBalancerRole: "start worker processes",
|
LoadBalancerRole: "start worker processes",
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,6 +130,18 @@ type StartClusterOpts struct {
|
|||||||
Timeout time.Duration
|
Timeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateNodeOpts describes a set of options one can set when creating a new node
|
||||||
|
type CreateNodeOpts struct {
|
||||||
|
Wait bool
|
||||||
|
Timeout time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartNodeOpts describes a set of options one can set when (re-)starting a node
|
||||||
|
type StartNodeOpts struct {
|
||||||
|
Wait bool
|
||||||
|
Timeout time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
// ClusterNetwork describes a network which a cluster is running in
|
// ClusterNetwork describes a network which a cluster is running in
|
||||||
type ClusterNetwork struct {
|
type ClusterNetwork struct {
|
||||||
Name string `yaml:"name" json:"name,omitempty"`
|
Name string `yaml:"name" json:"name,omitempty"`
|
||||||
|
|||||||
@ -39,14 +39,11 @@ info "Checking that we have 2 nodes online..."
|
|||||||
check_multi_node "$clustername" 2 || failed "failed to verify number of nodes"
|
check_multi_node "$clustername" 2 || failed "failed to verify number of nodes"
|
||||||
|
|
||||||
# 4. adding another worker node
|
# 4. adding another worker node
|
||||||
# info "Adding one worker node..."
|
info "Adding one worker node..."
|
||||||
# LOG_LEVEL=debug $EXE create node "extra-worker" --cluster "$clustername" --role "worker" || failed "failed to add worker node"
|
LOG_LEVEL=debug $EXE create node "extra-worker" --cluster "$clustername" --role "worker" --wait --timeout 360s || failed "failed to add worker node"
|
||||||
#
|
|
||||||
# info "Waiting for a bit to give the new node enough time to boot and register..."
|
info "Checking that we have 3 nodes available now..."
|
||||||
# sleep 10
|
check_multi_node "$clustername" 3 || failed "failed to verify number of nodes"
|
||||||
#
|
|
||||||
# info "Checking that we have 3 nodes available now..."
|
|
||||||
# check_multi_node "$clustername" 3 || failed "failed to verify number of nodes"
|
|
||||||
|
|
||||||
# 4. load an image into the cluster
|
# 4. load an image into the cluster
|
||||||
info "Loading an image into the cluster..."
|
info "Loading an image into the cluster..."
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user