Skip to content

Commit

Permalink
add support for node specifier in labels
Browse files Browse the repository at this point in the history
  • Loading branch information
lionelnicolas committed Jan 16, 2020
1 parent 5c5c4c5 commit 739486e
Show file tree
Hide file tree
Showing 6 changed files with 182 additions and 61 deletions.
57 changes: 26 additions & 31 deletions cli/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,10 @@ func CreateCluster(c *cli.Context) error {
* Docker container labels that will be added to the k3d node containers
*/
// labels
labels := []string{}
labels = append(labels, c.StringSlice("label")...)
labelmap, err := mapNodesToLabelSpecs(c.StringSlice("label"), GetAllContainerNames(c.String("name"), DefaultServerCount, c.Int("workers")))
if err != nil {
log.Fatal(err)
}

/*
* Arguments passed on to the k3s server and agent, will be filled later
Expand Down Expand Up @@ -207,17 +209,17 @@ func CreateCluster(c *cli.Context) error {
* Defines, with which specifications, the cluster and the nodes inside should be created
*/
clusterSpec := &ClusterSpec{
AgentArgs: k3AgentArgs,
APIPort: *apiPort,
AutoRestart: c.Bool("auto-restart"),
ClusterName: c.String("name"),
Env: env,
Labels: labels,
Image: image,
NodeToPortSpecMap: portmap,
PortAutoOffset: c.Int("port-auto-offset"),
ServerArgs: k3sServerArgs,
Volumes: volumesSpec,
AgentArgs: k3AgentArgs,
APIPort: *apiPort,
AutoRestart: c.Bool("auto-restart"),
ClusterName: c.String("name"),
Env: env,
NodeToLabelSpecMap: labelmap,
Image: image,
NodeToPortSpecMap: portmap,
PortAutoOffset: c.Int("port-auto-offset"),
ServerArgs: k3sServerArgs,
Volumes: volumesSpec,
}

/******************
Expand Down Expand Up @@ -485,17 +487,17 @@ func AddNode(c *cli.Context) error {
nodeCount := c.Int("count")

clusterSpec := &ClusterSpec{
AgentArgs: nil,
APIPort: apiPort{},
AutoRestart: false,
ClusterName: clusterName,
Env: nil,
Labels: nil,
Image: "",
NodeToPortSpecMap: nil,
PortAutoOffset: 0,
ServerArgs: nil,
Volumes: &Volumes{},
AgentArgs: nil,
APIPort: apiPort{},
AutoRestart: false,
ClusterName: clusterName,
Env: nil,
NodeToLabelSpecMap: nil,
Image: "",
NodeToPortSpecMap: nil,
PortAutoOffset: 0,
ServerArgs: nil,
Volumes: &Volumes{},
}

/* (0.1)
Expand Down Expand Up @@ -569,13 +571,6 @@ func AddNode(c *cli.Context) error {
return nil
}

/* (0.6)
* --label, -l <key1=val1>
* Docker container labels that will be added to the k3d node containers
*/
clusterSpec.Labels = []string{}
clusterSpec.Labels = append(clusterSpec.Labels, c.StringSlice("label")...)

/*
* (1) Check cluster
*/
Expand Down
32 changes: 19 additions & 13 deletions cli/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,16 @@ func createServer(spec *ClusterSpec) (string, error) {

containerName := GetContainerName("server", spec.ClusterName, -1)

// labels to be created to the server belong to roles
// all, server, master or <server-container-name>
serverLabels, err := MergeLabelSpecs(spec.NodeToLabelSpecMap, "server", containerName)
if err != nil {
return "", err
}
containerLabels = MergeLabels(containerLabels, serverLabels)

// ports to be assigned to the server belong to roles
// all, server or <server-container-name>
// all, server, master or <server-container-name>
serverPorts, err := MergePortSpecs(spec.NodeToPortSpecMap, "server", containerName)
if err != nil {
return "", err
Expand Down Expand Up @@ -142,16 +150,6 @@ func createWorker(spec *ClusterSpec, postfix int) (string, error) {
containerLabels["created"] = time.Now().Format("2006-01-02 15:04:05")
containerLabels["cluster"] = spec.ClusterName

for _, label := range spec.Labels {
labelSlice := strings.SplitN(label, "=", 2)

if len(labelSlice) > 1 {
containerLabels[labelSlice[0]] = labelSlice[1]
} else {
containerLabels[labelSlice[0]] = ""
}
}

containerName := GetContainerName("worker", spec.ClusterName, postfix)
env := spec.Env

Expand All @@ -166,8 +164,16 @@ func createWorker(spec *ClusterSpec, postfix int) (string, error) {
env = append(spec.Env, fmt.Sprintf("K3S_URL=https://k3d-%s-server:%s", spec.ClusterName, spec.APIPort.Port))
}

// ports to be assigned to the server belong to roles
// all, server or <server-container-name>
// labels to be created to the worker belong to roles
// all, worker, agent or <server-container-name>
workerLabels, err := MergeLabelSpecs(spec.NodeToLabelSpecMap, "worker", containerName)
if err != nil {
return "", err
}
containerLabels = MergeLabels(containerLabels, workerLabels)

// ports to be assigned to the worker belong to roles
// all, worker, agent or <server-container-name>
workerPorts, err := MergePortSpecs(spec.NodeToPortSpecMap, "worker", containerName)
if err != nil {
return "", err
Expand Down
121 changes: 121 additions & 0 deletions cli/label.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package run

import (
"regexp"
"strings"

log "github.com/sirupsen/logrus"
)

// mapNodesToLabelSpecs maps nodes to labelSpecs
func mapNodesToLabelSpecs(specs []string, createdNodes []string) (map[string][]string, error) {
// check node-specifier possibilitites
possibleNodeSpecifiers := []string{"all", "workers", "agents", "server", "master"}
possibleNodeSpecifiers = append(possibleNodeSpecifiers, createdNodes...)

nodeToLabelSpecMap := make(map[string][]string)

for _, spec := range specs {
labelSpec, node := extractLabelNode(spec)

// check if node-specifier is valid (either a role or a name) and append to list if matches
nodeFound := false
for _, name := range possibleNodeSpecifiers {
if node == name {
nodeFound = true
nodeToLabelSpecMap[node] = append(nodeToLabelSpecMap[node], labelSpec)
break
}
}

// node extraction was a false positive, use full spec with default node
if !nodeFound {
nodeToLabelSpecMap[defaultLabelNodes] = append(nodeToLabelSpecMap[defaultLabelNodes], spec)
}
}

return nodeToLabelSpecMap, nil
}

// extractLabelNode separates the node specification from the actual label specs
func extractLabelNode(spec string) (string, string) {
// label defaults to full spec
labelSpec := spec

// node defaults to "all"
node := defaultLabelNodes

// only split at the last "@"
re := regexp.MustCompile(`^(.*)@([^@]+)$`)
match := re.FindStringSubmatch(spec)

if len(match) > 0 {
labelSpec = match[1]
node = match[2]
}

return labelSpec, node
}

// splitLabel separates the label key from the label value
func splitLabel(label string) (string, string) {
// split only on first '=' sign (like `docker run` do)
labelSlice := strings.SplitN(label, "=", 2)

if len(labelSlice) > 1 {
return labelSlice[0], labelSlice[1]
}

// defaults to label key with empty value (like `docker run` do)
return label, ""
}

// MergeLabelSpecs merges labels for a given node
func MergeLabelSpecs(nodeToLabelSpecMap map[string][]string, role, name string) ([]string, error) {
labelSpecs := []string{}

// add portSpecs according to node role
for _, group := range nodeRuleGroupsMap[role] {
for _, v := range nodeToLabelSpecMap[group] {
exists := false
for _, i := range labelSpecs {
if v == i {
exists = true
}
}
if !exists {
labelSpecs = append(labelSpecs, v)
}
}
}

// add portSpecs according to node name
for _, v := range nodeToLabelSpecMap[name] {
exists := false
for _, i := range labelSpecs {
if v == i {
exists = true
}
}
if !exists {
labelSpecs = append(labelSpecs, v)
}
}

return labelSpecs, nil
}

// MergeLabels merges list of labels into a label map
func MergeLabels(labelMap map[string]string, labels []string) map[string]string {
for _, label := range labels {
labelKey, labelValue := splitLabel(label)

if _, found := labelMap[labelKey]; found {
log.Warningf("Overriding already existing label [%s]", labelKey)
}

labelMap[labelKey] = labelValue
}

return labelMap
}
2 changes: 1 addition & 1 deletion cli/port.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ func extractNodes(spec string) ([]string, string) {
return nodes, portSpec
}

// Offset creates a new PublishedPort structure, with all host ports are changed by a fixed 'offset'
// Offset creates a new PublishedPort structure, with all host ports are changed by a fixed 'offset'
func (p PublishedPorts) Offset(offset int) *PublishedPorts {
var newExposedPorts = make(map[nat.Port]struct{}, len(p.ExposedPorts))
var newPortBindings = make(map[nat.Port][]nat.PortBinding, len(p.PortBindings))
Expand Down
25 changes: 14 additions & 11 deletions cli/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ const (
// defaultNodes describes the type of nodes on which a port should be exposed by default
const defaultNodes = "server"

// defaultLabelNodes describes the type of nodes on which a label should be applied by default
const defaultLabelNodes = "all"

// mapping a node role to groups that should be applied to it
var nodeRuleGroupsMap = map[string][]string{
"worker": {"all", "workers", "agents"},
Expand All @@ -32,17 +35,17 @@ type Cluster struct {

// ClusterSpec defines the specs for a cluster that's up for creation
type ClusterSpec struct {
AgentArgs []string
APIPort apiPort
AutoRestart bool
ClusterName string
Env []string
Labels []string
Image string
NodeToPortSpecMap map[string][]string
PortAutoOffset int
ServerArgs []string
Volumes *Volumes
AgentArgs []string
APIPort apiPort
AutoRestart bool
ClusterName string
Env []string
NodeToLabelSpecMap map[string][]string
Image string
NodeToPortSpecMap map[string][]string
PortAutoOffset int
ServerArgs []string
Volumes *Volumes
}

// PublishedPorts is a struct used for exposing container ports on the host system
Expand Down
6 changes: 1 addition & 5 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ func main() {
},
cli.StringSliceFlag{
Name: "label, l",
Usage: "Add one or more docker labels to every node container of the cluster, using Docker notation `key=value` (new flag per label)",
Usage: "Add a docker label to node container (Format: `key[=value][@node-specifier]`, new flag per label)",
},
cli.IntFlag{
Name: "workers, w",
Expand Down Expand Up @@ -158,10 +158,6 @@ func main() {
Name: "env, e",
Usage: "Pass an additional environment variable (new flag per variable)",
},
cli.StringSliceFlag{
Name: "label, l",
Usage: "Add one or more docker labels to every node container of the cluster (Docker notation: `key=value`)",
},
cli.StringSliceFlag{
Name: "volume, v",
Usage: "Mount one or more volumes into every created node (Docker notation: `source:destination`)",
Expand Down

0 comments on commit 739486e

Please sign in to comment.