From 43eb626b0ce8c5167dc9354ae2154b8e5184ba4a Mon Sep 17 00:00:00 2001 From: iwilltry42 Date: Mon, 30 Sep 2019 12:33:17 +0200 Subject: [PATCH] specify role, image and labels --- cmd/create/createCluster.go | 4 ++- cmd/create/createNode.go | 41 +++++++++++++++++++++++----- go.mod | 2 -- pkg/cluster/node.go | 38 ++++++++++++++++++++++---- pkg/runtimes/containerd/container.go | 34 ++++++++++++++++------- pkg/runtimes/docker/container.go | 9 +++--- pkg/types/types.go | 23 ++++++++++------ 7 files changed, 112 insertions(+), 39 deletions(-) diff --git a/cmd/create/createCluster.go b/cmd/create/createCluster.go index c7ae641e..71300864 100644 --- a/cmd/create/createCluster.go +++ b/cmd/create/createCluster.go @@ -49,8 +49,10 @@ func NewCmdCreateCluster() *cobra.Command { } // add flags - cmd.Flags().StringP("api-port", "a", "6443", "Specify the Kubernetes cluster API server port (Format: `--api-port [host:]port`") + cmd.Flags().StringP("api-port", "a", "6443", "Specify the Kubernetes API server port (Format: `--api-port [host:]port`") + cmd.Flags().IntP("masters", "m", 1, "Specify how many masters you want to create") cmd.Flags().IntP("workers", "w", 0, "Specify how many workers you want to create") + cmd.Flags().String("config", "", "Specify a cluster configuration file") // TODO: to implement // add subcommands diff --git a/cmd/create/createNode.go b/cmd/create/createNode.go index c8e6e8c2..83d1ee9e 100644 --- a/cmd/create/createNode.go +++ b/cmd/create/createNode.go @@ -42,40 +42,67 @@ func NewCmdCreateNode() *cobra.Command { Long: `Create a new containerized k3s node (k3s in docker).`, 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) { - runtime, nodes := parseCreateNodeCmd(cmd, args) - cluster.CreateNodes(nodes, runtime) + cluster.CreateNodes(parseCreateNodeCmd(cmd, args)) }, } // add flags cmd.Flags().Int("replicas", 1, "Number of replicas of this node specification.") + cmd.Flags().String("role", "worker", "Specify node role [master, worker]") cmd.Flags().StringP("cluster", "c", "", "Select the cluster that the node shall connect to.") + cmd.Flags().String("image", k3d.DefaultK3sImageRepo, "Specify k3s image used for the node(s)") // done return cmd } // parseCreateNodeCmd parses the command input into variables required to create a cluster -func parseCreateNodeCmd(cmd *cobra.Command, args []string) (runtimes.Runtime, []*k3d.Node) { +func parseCreateNodeCmd(cmd *cobra.Command, args []string) ([]*k3d.Node, runtimes.Runtime) { + + // --runtime rt, err := cmd.Flags().GetString("runtime") if err != nil { - log.Fatalln("Runtime not defined") + log.Fatalln("No runtime specified") } runtime, err := runtimes.GetRuntime(rt) if err != nil { log.Fatalln(err) } + // --replicas replicas, err := cmd.Flags().GetInt("replicas") if err != nil { - log.Errorln("Failed to parse flag '--replicas'") + log.Errorln("No replica count specified") + log.Fatalln(err) + } + + // --role + role, err := cmd.Flags().GetString("role") + if err != nil { + log.Errorln("No node role specified") + log.Fatalln(err) + } + if _, ok := k3d.DefaultK3dRoles[role]; !ok { + log.Fatalf("Unknown node role '%s'\n", role) + } + + // --image + image, err := cmd.Flags().GetString("image") + if err != nil { + log.Errorln("No image specified") log.Fatalln(err) } + // generate list of nodes nodes := []*k3d.Node{} for i := 0; i < replicas; i++ { - nodes = append(nodes, &k3d.Node{Name: fmt.Sprintf("%s-%d", args[0], i)}) + node := &k3d.Node{ + Name: fmt.Sprintf("%s-%s-%d", k3d.DefaultObjectNamePrefix, args[0], i), + Role: role, + Image: image, + } + nodes = append(nodes, node) } - return runtime, nodes + return nodes, runtime } diff --git a/go.mod b/go.mod index 5317131f..4c2dcb88 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,6 @@ require ( github.com/google/go-cmp v0.3.0 // indirect github.com/gorilla/mux v1.7.3 // indirect github.com/imdario/mergo v0.3.7 // indirect - github.com/mitchellh/go-homedir v1.1.0 github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c // indirect github.com/opencontainers/go-digest v1.0.0-rc1 // indirect github.com/opencontainers/image-spec v1.0.1 // indirect @@ -29,7 +28,6 @@ require ( github.com/opencontainers/runtime-spec v1.0.1 // indirect github.com/sirupsen/logrus v1.4.2 github.com/spf13/cobra v0.0.5 - github.com/spf13/viper v1.3.2 github.com/stretchr/testify v1.4.0 // indirect github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 // indirect go.etcd.io/bbolt v1.3.3 // indirect diff --git a/pkg/cluster/node.go b/pkg/cluster/node.go index f2980f92..4d42d797 100644 --- a/pkg/cluster/node.go +++ b/pkg/cluster/node.go @@ -23,6 +23,8 @@ THE SOFTWARE. package cluster import ( + "fmt" + k3drt "github.com/rancher/k3d/pkg/runtimes" k3dContainerd "github.com/rancher/k3d/pkg/runtimes/containerd" k3dDocker "github.com/rancher/k3d/pkg/runtimes/docker" @@ -40,17 +42,40 @@ func CreateNodes(nodes []*k3d.Node, runtime k3drt.Runtime) { // TODO: pass `--at } // CreateNode creates a new containerized k3s node -func CreateNode(nodeSpec *k3d.Node, runtime k3drt.Runtime) error { - log.Debugf("Creating node from spec\n%+v", nodeSpec) - if err := runtime.CreateNode(nodeSpec); err != nil { - log.Error(err) +func CreateNode(node *k3d.Node, runtime k3drt.Runtime) error { + log.Debugf("Creating node from spec\n%+v", node) + + /* + * CONFIGURATION + */ + + // global node configuration (applies for any node role) + node.Labels = k3d.DefaultObjectLabels + + // specify options depending on node role + if node.Role == "worker" { // TODO: check here AND in CLI or only here? + node.Cmd = []string{"agent"} + node.Labels["role"] = "worker" + } else if node.Role == "master" { + node.Cmd = []string{"server"} + node.Labels["role"] = "master" + } else { + return fmt.Errorf("Unknown node role '%s'", node.Role) + } + + /* + * CREATION + */ + if err := runtime.CreateNode(node); err != nil { + return err } + log.Debugln("...success") return nil } // DeleteNode deletes an existing node -func DeleteNode(nodeSpec *k3d.Node, runtimeChoice string) error { +func DeleteNode(node *k3d.Node, runtimeChoice string) error { var runtime k3drt.Runtime if runtimeChoice == "docker" { runtime = k3dDocker.Docker{} @@ -58,9 +83,10 @@ func DeleteNode(nodeSpec *k3d.Node, runtimeChoice string) error { runtime = k3dContainerd.Containerd{} } - if err := runtime.DeleteNode(nodeSpec); err != nil { + if err := runtime.DeleteNode(node); err != nil { log.Error(err) } + log.Infoln("Deleted", node.Name) log.Debugln("...success") return nil } diff --git a/pkg/runtimes/containerd/container.go b/pkg/runtimes/containerd/container.go index 1e366545..324ec10a 100644 --- a/pkg/runtimes/containerd/container.go +++ b/pkg/runtimes/containerd/container.go @@ -32,8 +32,10 @@ import ( ) // CreateNode creates a new k3d node -func (d Containerd) CreateNode(nodeSpec *k3d.Node) error { +func (d Containerd) CreateNode(node *k3d.Node) error { log.Debugln("containerd.CreateNode...") + + // create containerd client ctx := context.Background() clientOpts := []containerd.ClientOpt{ containerd.WithDefaultNamespace("k3d"), @@ -43,26 +45,38 @@ func (d Containerd) CreateNode(nodeSpec *k3d.Node) error { 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 = "docker.io/nginx:latest" - c.Labels = map[string]string{ - "runtime": "containerd", - } + c.Image = node.Image + c.Labels = node.Labels return nil }, } - resp, err := client.NewContainer(ctx, "test-containerd", newContainerOpts...) + container, err := client.NewContainer(ctx, node.Name, newContainerOpts...) if err != nil { log.Errorln("Couldn't create container") return err } - log.Infoln("Created container with ID", resp.ID()) + + /* + // 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(nodeSpec *k3d.Node) error { +func (d Containerd) DeleteNode(node *k3d.Node) error { log.Debugln("containerd.DeleteNode...") ctx := context.Background() clientOpts := []containerd.ClientOpt{ @@ -74,9 +88,9 @@ func (d Containerd) DeleteNode(nodeSpec *k3d.Node) error { return err } - container, err := client.LoadContainer(ctx, nodeSpec.Name) + container, err := client.LoadContainer(ctx, node.Name) if err != nil { - log.Errorln("Couldn't load container", nodeSpec.Name) + log.Errorln("Couldn't load container", node.Name) return err } if err = container.Delete(ctx, []containerd.DeleteOpts{}...); err != nil { diff --git a/pkg/runtimes/docker/container.go b/pkg/runtimes/docker/container.go index 7f3c9fd0..7ca2f9b4 100644 --- a/pkg/runtimes/docker/container.go +++ b/pkg/runtimes/docker/container.go @@ -35,7 +35,7 @@ import ( ) // CreateNode creates a new container -func (d Docker) CreateNode(nodeSpec *k3d.Node) error { +func (d Docker) CreateNode(node *k3d.Node) error { log.Debugln("docker.CreateNode...") ctx := context.Background() // TODO: check how kind handles contexts docker, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) @@ -44,11 +44,12 @@ func (d Docker) CreateNode(nodeSpec *k3d.Node) error { } containerConfig := container.Config{ - Cmd: []string{"sh"}, - Image: "nginx", + Cmd: node.Cmd, + Image: node.Image, + Labels: node.Labels, } - resp, err := docker.ContainerCreate(ctx, &containerConfig, &container.HostConfig{}, &network.NetworkingConfig{}, nodeSpec.Name) + resp, err := docker.ContainerCreate(ctx, &containerConfig, &container.HostConfig{}, &network.NetworkingConfig{}, node.Name) if err != nil { log.Error("Couldn't create container") return err diff --git a/pkg/types/types.go b/pkg/types/types.go index 3dc60141..5cd3da0c 100644 --- a/pkg/types/types.go +++ b/pkg/types/types.go @@ -36,7 +36,10 @@ const DefaultClusterNameMaxLength = 32 const DefaultK3sImageRepo = "docker.io/rancher/k3s" // DefaultObjectNamePrefix defines the name prefix for every object created by k3d -const DefaultObjectNamePrefix = "k3d-" +const DefaultObjectNamePrefix = "k3d" + +// DefaultK3dRoles defines the roles available for nodes +var DefaultK3dRoles = map[string]bool{"master": true, "worker": true} // DefaultObjectLabels specifies a set of labels that will be attached to k3d objects by default var DefaultObjectLabels = map[string]string{ @@ -53,14 +56,16 @@ type Cluster struct { // Node describes a k3d node type Node struct { - Name string `yaml:"name" json:"name,omitempty"` - Role string `yaml:"role" json:"role,omitempty"` - Image string `yaml:"image" json:"image,omitempty"` - Volumes []string `yaml:"volumes" json:"volumes,omitempty"` - Env []string `yaml:"env" json:"env,omitempty"` - Args []string `yaml:"extra_args" json:"extraArgs,omitempty"` - Ports []string `yaml:"port_mappings" json:"portMappings,omitempty"` // TODO: make a struct out of this? - Restart bool `yaml:"restart" json:"restart,omitempty"` + Name string `yaml:"name" json:"name,omitempty"` + Role string `yaml:"role" json:"role,omitempty"` + Image string `yaml:"image" json:"image,omitempty"` + Volumes []string `yaml:"volumes" json:"volumes,omitempty"` + Env []string `yaml:"env" json:"env,omitempty"` + Cmd []string // filled automatically depending on role (master: cmd='server'; worker: cmd='agent') + Args []string `yaml:"extra_args" json:"extraArgs,omitempty"` + Ports []string `yaml:"port_mappings" json:"portMappings,omitempty"` // TODO: make a struct out of this? + Restart bool `yaml:"restart" json:"restart,omitempty"` + Labels map[string]string // filled automatically } // Network describes a container network used by k3d clusters