implement start/stop for nodes and clusters

pull/227/head
iwilltry42 5 years ago
parent e2834f5320
commit 098fe0803c
  1. 2
      cmd/delete/deleteCluster.go
  2. 4
      cmd/root.go
  3. 58
      cmd/start/startCluster.go
  4. 26
      cmd/start/startNode.go
  5. 58
      cmd/stop/stopCluster.go
  6. 27
      cmd/stop/stopNode.go
  7. 38
      pkg/cluster/cluster.go
  8. 83
      pkg/runtimes/containerd/container.go
  9. 116
      pkg/runtimes/containerd/node.go
  10. 5
      pkg/runtimes/docker/container.go
  11. 58
      pkg/runtimes/docker/node.go
  12. 4
      pkg/runtimes/runtime.go

@ -67,7 +67,7 @@ func NewCmdDeleteCluster() *cobra.Command {
return cmd
}
// parseDeleteClusterCmd parses the command input into variables required to create a cluster
// parseDeleteClusterCmd parses the command input into variables required to delete clusters
func parseDeleteClusterCmd(cmd *cobra.Command, args []string) (runtimes.Runtime, []*k3d.Cluster) {
// --runtime
rt, err := cmd.Flags().GetString("runtime")

@ -31,6 +31,8 @@ import (
"github.com/rancher/k3d/cmd/create"
"github.com/rancher/k3d/cmd/delete"
"github.com/rancher/k3d/cmd/get"
"github.com/rancher/k3d/cmd/start"
"github.com/rancher/k3d/cmd/stop"
"github.com/rancher/k3d/version"
@ -79,6 +81,8 @@ func init() {
rootCmd.AddCommand(create.NewCmdCreate())
rootCmd.AddCommand(delete.NewCmdDelete())
rootCmd.AddCommand(get.NewCmdGet())
rootCmd.AddCommand(stop.NewCmdStop())
rootCmd.AddCommand(start.NewCmdStart())
rootCmd.AddCommand(&cobra.Command{
Use: "version",

@ -22,9 +22,11 @@ THE SOFTWARE.
package start
import (
"github.com/rancher/k3d/pkg/cluster"
"github.com/rancher/k3d/pkg/runtimes"
"github.com/spf13/cobra"
"github.com/rancher/k3d/pkg/types"
k3d "github.com/rancher/k3d/pkg/types"
log "github.com/sirupsen/logrus"
)
@ -39,14 +41,66 @@ func NewCmdStartCluster() *cobra.Command {
Long: `Start an existing k3d cluster`,
Run: func(cmd *cobra.Command, args []string) {
log.Debugln("start cluster called")
runtime, clusters := parseStartClusterCmd(cmd, args)
if len(clusters) == 0 {
log.Infoln("No clusters found")
} else {
for _, c := range clusters {
if err := cluster.StartCluster(c, runtime); err != nil {
log.Fatalln(err)
}
}
}
log.Debugln("...Finished")
},
}
// add flags
cmd.Flags().StringP("name", "n", types.DefaultClusterName, "Name of the cluster")
cmd.Flags().BoolP("all", "a", false, "Start all existing clusters")
// add subcommands
// done
return cmd
}
// parseStartClusterCmd parses the command input into variables required to start clusters
func parseStartClusterCmd(cmd *cobra.Command, args []string) (runtimes.Runtime, []*k3d.Cluster) {
// --runtime
rt, err := cmd.Flags().GetString("runtime")
if err != nil {
log.Fatalln("No runtime specified")
}
runtime, err := runtimes.GetRuntime(rt)
if err != nil {
log.Fatalln(err)
}
// --all
var clusters []*k3d.Cluster
if all, err := cmd.Flags().GetBool("all"); err != nil {
log.Fatalln(err)
} else if all {
clusters, err = cluster.GetClusters(runtime)
if err != nil {
log.Fatalln(err)
}
return runtime, clusters
}
if len(args) < 1 {
log.Fatalln("Expecting at least one cluster name if `--all` is not set")
}
for _, name := range args {
cluster, err := cluster.GetCluster(&k3d.Cluster{Name: name}, runtime)
if err != nil {
log.Fatalln(err)
}
clusters = append(clusters, cluster)
}
return runtime, clusters
}

@ -22,6 +22,8 @@ THE SOFTWARE.
package start
import (
"github.com/rancher/k3d/pkg/runtimes"
k3d "github.com/rancher/k3d/pkg/types"
"github.com/spf13/cobra"
log "github.com/sirupsen/logrus"
@ -37,9 +39,33 @@ func NewCmdStartNode() *cobra.Command {
Long: `Start an existing k3d node.`,
Run: func(cmd *cobra.Command, args []string) {
log.Debugln("start node called")
runtime, node := parseStartNodeCmd(cmd, args)
if err := runtime.StartNode(node); err != nil {
log.Fatalln(err)
}
},
}
// done
return cmd
}
// parseStartNodeCmd parses the command input into variables required to start a node
func parseStartNodeCmd(cmd *cobra.Command, args []string) (runtimes.Runtime, *k3d.Node) {
// --runtime
rt, err := cmd.Flags().GetString("runtime")
if err != nil {
log.Fatalln("No runtime specified")
}
runtime, err := runtimes.GetRuntime(rt)
if err != nil {
log.Fatalln(err)
}
// node name // TODO: allow node filters, e.g. `k3d start nodes mycluster@worker` to start all worker nodes of cluster 'mycluster'
if len(args) == 0 || len(args[0]) == 0 {
log.Fatalln("No node name given")
}
return runtime, &k3d.Node{Name: args[0]} // TODO: validate and allow for more than one
}

@ -24,7 +24,9 @@ package stop
import (
"github.com/spf13/cobra"
"github.com/rancher/k3d/pkg/types"
"github.com/rancher/k3d/pkg/cluster"
"github.com/rancher/k3d/pkg/runtimes"
k3d "github.com/rancher/k3d/pkg/types"
log "github.com/sirupsen/logrus"
)
@ -39,14 +41,66 @@ func NewCmdStopCluster() *cobra.Command {
Long: `Stop an existing k3d cluster.`,
Run: func(cmd *cobra.Command, args []string) {
log.Debugln("stop cluster called")
runtime, clusters := parseStopClusterCmd(cmd, args)
if len(clusters) == 0 {
log.Infoln("No clusters found")
} else {
for _, c := range clusters {
if err := cluster.StopCluster(c, runtime); err != nil {
log.Fatalln(err)
}
}
}
log.Debugln("...Finished")
},
}
// add flags
cmd.Flags().StringP("name", "n", types.DefaultClusterName, "Name of the cluster")
cmd.Flags().BoolP("all", "a", false, "Start all existing clusters")
// add subcommands
// done
return cmd
}
// parseStopClusterCmd parses the command input into variables required to start clusters
func parseStopClusterCmd(cmd *cobra.Command, args []string) (runtimes.Runtime, []*k3d.Cluster) {
// --runtime
rt, err := cmd.Flags().GetString("runtime")
if err != nil {
log.Fatalln("No runtime specified")
}
runtime, err := runtimes.GetRuntime(rt)
if err != nil {
log.Fatalln(err)
}
// --all
var clusters []*k3d.Cluster
if all, err := cmd.Flags().GetBool("all"); err != nil {
log.Fatalln(err)
} else if all {
clusters, err = cluster.GetClusters(runtime)
if err != nil {
log.Fatalln(err)
}
return runtime, clusters
}
if len(args) < 1 {
log.Fatalln("Expecting at least one cluster name if `--all` is not set")
}
for _, name := range args {
cluster, err := cluster.GetCluster(&k3d.Cluster{Name: name}, runtime)
if err != nil {
log.Fatalln(err)
}
clusters = append(clusters, cluster)
}
return runtime, clusters
}

@ -22,8 +22,11 @@ THE SOFTWARE.
package stop
import (
"github.com/rancher/k3d/pkg/runtimes"
"github.com/spf13/cobra"
k3d "github.com/rancher/k3d/pkg/types"
log "github.com/sirupsen/logrus"
)
@ -37,9 +40,33 @@ func NewCmdStopNode() *cobra.Command {
Long: `Stop an existing k3d node.`,
Run: func(cmd *cobra.Command, args []string) {
log.Debugln("stop node called")
runtime, node := parseStopNodeCmd(cmd, args)
if err := runtime.StopNode(node); err != nil {
log.Fatalln(err)
}
},
}
// done
return cmd
}
// parseStopNodeCmd parses the command input into variables required to stop a node
func parseStopNodeCmd(cmd *cobra.Command, args []string) (runtimes.Runtime, *k3d.Node) {
// --runtime
rt, err := cmd.Flags().GetString("runtime")
if err != nil {
log.Fatalln("No runtime specified")
}
runtime, err := runtimes.GetRuntime(rt)
if err != nil {
log.Fatalln(err)
}
// node name // TODO: allow node filters, e.g. `k3d stop nodes mycluster@worker` to stop all worker nodes of cluster 'mycluster'
if len(args) == 0 || len(args[0]) == 0 {
log.Fatalln("No node name given")
}
return runtime, &k3d.Node{Name: args[0]} // TODO: validate and allow for more than one
}

@ -214,3 +214,41 @@ func GenerateClusterSecret() string {
func generateNodeName(cluster string, role k3d.Role, suffix int) string {
return fmt.Sprintf("%s-%s-%s-%d", k3d.DefaultObjectNamePrefix, cluster, role, suffix)
}
// StartCluster starts a whole cluster (i.e. all nodes of the cluster)
func StartCluster(cluster *k3d.Cluster, runtime k3drt.Runtime) error {
log.Infof("Starting cluster '%s'", cluster.Name)
failed := 0
for _, node := range cluster.Nodes {
if err := runtime.StartNode(node); err != nil {
log.Warningf("Failed to start node '%s': Try to start it manually", node.Name)
failed++
continue
}
}
if failed > 0 {
return fmt.Errorf("Failed to start %d nodes: Try to start them manually", failed)
}
return nil
}
// StopCluster stops a whole cluster (i.e. all nodes of the cluster)
func StopCluster(cluster *k3d.Cluster, runtime k3drt.Runtime) error {
log.Infof("Stopping cluster '%s'", cluster.Name)
failed := 0
for _, node := range cluster.Nodes {
if err := runtime.StopNode(node); err != nil {
log.Warningf("Failed to stop node '%s': Try to stop it manually", node.Name)
failed++
continue
}
}
if failed > 0 {
return fmt.Errorf("Failed to stop %d nodes: Try to stop them manually", failed)
}
return nil
}

@ -21,86 +21,3 @@ THE SOFTWARE.
*/
package containerd
import (
"context"
"github.com/containerd/containerd"
"github.com/containerd/containerd/containers"
k3d "github.com/rancher/k3d/pkg/types"
log "github.com/sirupsen/logrus"
)
// CreateNode creates a new k3d node
func (d Containerd) CreateNode(node *k3d.Node) error {
log.Debugln("containerd.CreateNode...")
// create containerd client
ctx := context.Background()
clientOpts := []containerd.ClientOpt{
containerd.WithDefaultNamespace("k3d"),
}
client, err := containerd.New("/run/containerd/containerd.sock", clientOpts...) // TODO: this is the default address on UNIX, different on Windows
if err != nil {
log.Errorln("Failed to create containerd client")
return err
}
// create container
newContainerOpts := []containerd.NewContainerOpts{
func(ctx context.Context, _ *containerd.Client, c *containers.Container) error {
c.Image = node.Image
c.Labels = node.Labels
return nil
},
}
container, err := client.NewContainer(ctx, node.Name, newContainerOpts...)
if err != nil {
log.Errorln("Couldn't create container")
return err
}
/*
// start container
task, err := container.NewTask(ctx, cio.NewCreator()) // TODO: how the hell does this work?
if err != nil {
log.Errorln("Failed to create task in container", container.ID)
return err
}
task.Start(ctx)
*/
log.Infoln("Created container with ID", container.ID())
return nil
}
// DeleteNode deletes an existing k3d node
func (d Containerd) DeleteNode(node *k3d.Node) error {
log.Debugln("containerd.DeleteNode...")
ctx := context.Background()
clientOpts := []containerd.ClientOpt{
containerd.WithDefaultNamespace("k3d"),
}
client, err := containerd.New("/run/containerd/containerd.sock", clientOpts...) // TODO: this is the default address on UNIX, different on Windows
if err != nil {
log.Errorln("Failed to create containerd client")
return err
}
container, err := client.LoadContainer(ctx, node.Name)
if err != nil {
log.Errorln("Couldn't load container", node.Name)
return err
}
if err = container.Delete(ctx, []containerd.DeleteOpts{}...); err != nil {
log.Errorln("Failed to delete container", container.ID)
return err
}
return nil
}
func (d Containerd) GetNodesByLabel(labels map[string]string) ([]*k3d.Node, error) {
return nil, nil
}

@ -0,0 +1,116 @@
/*
Copyright © 2019 Thorsten Klein <iwilltry42@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
package containerd
import (
"context"
"github.com/containerd/containerd"
"github.com/containerd/containerd/containers"
k3d "github.com/rancher/k3d/pkg/types"
log "github.com/sirupsen/logrus"
)
// CreateNode creates a new k3d node
func (d Containerd) CreateNode(node *k3d.Node) error {
log.Debugln("containerd.CreateNode...")
// create containerd client
ctx := context.Background()
clientOpts := []containerd.ClientOpt{
containerd.WithDefaultNamespace("k3d"),
}
client, err := containerd.New("/run/containerd/containerd.sock", clientOpts...) // TODO: this is the default address on UNIX, different on Windows
if err != nil {
log.Errorln("Failed to create containerd client")
return err
}
// create container
newContainerOpts := []containerd.NewContainerOpts{
func(ctx context.Context, _ *containerd.Client, c *containers.Container) error {
c.Image = node.Image
c.Labels = node.Labels
return nil
},
}
container, err := client.NewContainer(ctx, node.Name, newContainerOpts...)
if err != nil {
log.Errorln("Couldn't create container")
return err
}
/*
// start container
task, err := container.NewTask(ctx, cio.NewCreator()) // TODO: how the hell does this work?
if err != nil {
log.Errorln("Failed to create task in container", container.ID)
return err
}
task.Start(ctx)
*/
log.Infoln("Created container with ID", container.ID())
return nil
}
// DeleteNode deletes an existing k3d node
func (d Containerd) DeleteNode(node *k3d.Node) error {
log.Debugln("containerd.DeleteNode...")
ctx := context.Background()
clientOpts := []containerd.ClientOpt{
containerd.WithDefaultNamespace("k3d"),
}
client, err := containerd.New("/run/containerd/containerd.sock", clientOpts...) // TODO: this is the default address on UNIX, different on Windows
if err != nil {
log.Errorln("Failed to create containerd client")
return err
}
container, err := client.LoadContainer(ctx, node.Name)
if err != nil {
log.Errorln("Couldn't load container", node.Name)
return err
}
if err = container.Delete(ctx, []containerd.DeleteOpts{}...); err != nil {
log.Errorln("Failed to delete container", container.ID)
return err
}
return nil
}
// StartNode starts an existing node
func (d Containerd) StartNode(node *k3d.Node) error {
return nil // TODO: fill
}
// StopNode stops an existing node
func (d Containerd) StopNode(node *k3d.Node) error {
return nil // TODO: fill
}
func (d Containerd) GetNodesByLabel(labels map[string]string) ([]*k3d.Node, error) {
return nil, nil
}

@ -147,6 +147,7 @@ func getNodeContainer(node *k3d.Node) (*types.Container, error) {
containers, err := docker.ContainerList(ctx, types.ContainerListOptions{
Filters: filters,
All: true,
})
if err != nil {
log.Errorln("Failed to list containers")
@ -158,6 +159,10 @@ func getNodeContainer(node *k3d.Node) (*types.Container, error) {
return nil, err
}
if len(containers) == 0 {
return nil, fmt.Errorf("Didn't find container for node '%s'", node.Name)
}
return &containers[0], nil
}

@ -82,6 +82,64 @@ func (d Docker) GetNodesByLabel(labels map[string]string) ([]*k3d.Node, error) {
}
// StartNode starts an existing node
func (d Docker) StartNode(node *k3d.Node) error {
// (0) create docker client
ctx := context.Background()
docker, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
if err != nil {
return fmt.Errorf("Failed to create docker client. %+v", err)
}
// get container which represents the node
nodeContainer, err := getNodeContainer(node)
if err != nil {
log.Errorf("Failed to get container for node '%s'", node.Name)
return err
}
// check if the container is actually managed by
if v, ok := nodeContainer.Labels["app"]; !ok || v != "k3d" {
return fmt.Errorf("Failed to determine if container '%s' is managed by k3d (needs label 'app=k3d')", nodeContainer.ID)
}
// actually start the container
if err := docker.ContainerStart(ctx, nodeContainer.ID, types.ContainerStartOptions{}); err != nil {
return err
}
return nil
}
// StopNode stops an existing node
func (d Docker) StopNode(node *k3d.Node) error {
// (0) create docker client
ctx := context.Background()
docker, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
if err != nil {
return fmt.Errorf("Failed to create docker client. %+v", err)
}
// get container which represents the node
nodeContainer, err := getNodeContainer(node)
if err != nil {
log.Errorf("Failed to get container for node '%s'", node.Name)
return err
}
// check if the container is actually managed by
if v, ok := nodeContainer.Labels["app"]; !ok || v != "k3d" {
return fmt.Errorf("Failed to determine if container '%s' is managed by k3d (needs label 'app=k3d')", nodeContainer.ID)
}
// actually stop the container
if err := docker.ContainerStop(ctx, nodeContainer.ID, nil); err != nil {
return err
}
return nil
}
func getContainersByLabel(labels map[string]string) ([]types.Container, error) {
// (0) create docker client
ctx := context.Background()

@ -44,9 +44,9 @@ type Runtime interface {
CreateNetworkIfNotPresent(name string) (string, bool, error) // @return NETWORK_NAME, EXISTS, ERROR
GetKubeconfig(*k3d.Node) (io.ReadCloser, error)
DeleteNetwork(ID string) error
// StartContainer() error
StartNode(*k3d.Node) error
StopNode(*k3d.Node) error
// ExecContainer() error
// StopContainer() error
// DeleteContainer() error
// GetContainerLogs() error
}

Loading…
Cancel
Save