createNode: copy as many details as possible from existing

- we now use the full containerJSON details when getting a node
- we now use as many details as possible to copy k3d settings from an
existing node when adding a new node to a running cluster
pull/212/head
iwilltry42 4 years ago
parent da8adf5469
commit c061104b53
No known key found for this signature in database
GPG Key ID: 7BA57AD1CFF16110
  1. 1
      docs/usage/.pages
  2. 2
      go.mod
  3. 30
      pkg/cluster/node.go
  4. 31
      pkg/runtimes/docker/node.go
  5. 77
      pkg/runtimes/docker/translate.go

@ -2,4 +2,5 @@ title: Usage
arrange:
- commands.md
- kubeconfig.md
- multimaster.md
- guides

@ -21,7 +21,7 @@ require (
github.com/gogo/protobuf v1.3.1 // indirect
github.com/golang/protobuf v1.4.0 // indirect
github.com/heroku/docker-registry-client v0.0.0-20190909225348-afc9e1acc3d5
github.com/imdario/mergo v0.3.9 // indirect
github.com/imdario/mergo v0.3.9
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de
github.com/mitchellh/go-homedir v1.1.0
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c // indirect

@ -25,10 +25,10 @@ package cluster
import (
"bytes"
"context"
"fmt"
"strings"
"time"
"github.com/imdario/mergo"
"github.com/rancher/k3d/pkg/runtimes"
k3d "github.com/rancher/k3d/pkg/types"
log "github.com/sirupsen/logrus"
@ -70,27 +70,21 @@ func AddNodeToCluster(runtime runtimes.Runtime, node *k3d.Node, cluster *k3d.Clu
}
}
// get node details
chosenNode, err = GetNode(chosenNode, runtime)
if err != nil {
return err
}
log.Debugf("Copying configuration from existing node %+v", chosenNode)
// get config from labels
for k, v := range chosenNode.Labels {
if strings.HasPrefix(k, "k3d") {
node.Labels[k] = v
}
if k == "k3d.cluster.url" {
node.Env = append(node.Env, fmt.Sprintf("K3S_URL=%s", v))
}
if k == "k3d.cluster.secret" {
node.Env = append(node.Env, fmt.Sprintf("K3S_TOKEN=%s", v))
}
// merge node config of new node into existing node config
if err := mergo.MergeWithOverwrite(chosenNode, *node); err != nil {
log.Errorln("Failed to merge new node config into existing node config")
return err
}
// backup: get config from environment variables
for _, env := range chosenNode.Env {
if strings.HasPrefix(env, "K3S_") {
node.Env = append(node.Env, env)
}
}
node = chosenNode
log.Debugf("Resulting node %+v", node)

@ -38,7 +38,6 @@ import (
// CreateNode creates a new container
func (d Docker) CreateNode(node *k3d.Node) error {
log.Debugln("docker.CreateNode...")
// translate node spec to docker container specs
dockerNode, err := TranslateNodeToContainer(node)
@ -172,9 +171,30 @@ func getContainersByLabel(labels map[string]string) ([]types.Container, error) {
log.Errorln("Failed to list containers")
return nil, err
}
return containers, nil
}
// getContainer details returns the containerjson with more details
func getContainerDetails(containerID string) (types.ContainerJSON, error) {
// (0) create docker client
ctx := context.Background()
docker, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
if err != nil {
return types.ContainerJSON{}, fmt.Errorf("Failed to create docker client. %+v", err)
}
defer docker.Close()
containerDetails, err := docker.ContainerInspect(ctx, containerID)
if err != nil {
log.Errorln("Failed to get details for container '%s'", containerID)
return types.ContainerJSON{}, err
}
return containerDetails, nil
}
// GetNode tries to get a node container by its name
func (d Docker) GetNode(node *k3d.Node) (*k3d.Node, error) {
container, err := getNodeContainer(node)
@ -183,9 +203,14 @@ func (d Docker) GetNode(node *k3d.Node) (*k3d.Node, error) {
return nil, err
}
node, err = TranslateContainerToNode(container)
containerDetails, err := getContainerDetails(container.ID)
if err != nil {
return nil, err
}
node, err = TranslateContainerDetailsToNode(containerDetails)
if err != nil {
log.Errorf("Failed to translate container for node '%s' to node object", node.Name)
log.Errorf("Failed to translate container details for node '%s' to node object", node.Name)
return nil, err
}

@ -23,6 +23,7 @@ THE SOFTWARE.
package docker
import (
"fmt"
"strings"
"github.com/docker/docker/api/types"
@ -117,3 +118,79 @@ func TranslateContainerToNode(cont *types.Container) (*k3d.Node, error) {
}
return node, nil
}
// TranslateContainerDetailsToNode translates a docker containerJSON object into a k3d node representation
func TranslateContainerDetailsToNode(containerDetails types.ContainerJSON) (*k3d.Node, error) {
// translate portMap to string representation
ports := []string{}
for containerPort, portBindingList := range containerDetails.HostConfig.PortBindings {
for _, hostInfo := range portBindingList {
ports = append(ports, fmt.Sprintf("%s:%s:%s", hostInfo.HostIP, hostInfo.HostPort, containerPort))
}
}
// restart -> we only set 'unless-stopped' upon cluster creation
restart := false
if containerDetails.HostConfig.RestartPolicy.IsAlways() || containerDetails.HostConfig.RestartPolicy.IsUnlessStopped() {
restart = true
}
// get the clusterNetwork
clusterNetwork := ""
for networkName := range containerDetails.NetworkSettings.Networks {
if strings.HasPrefix(networkName, fmt.Sprintf("%s-%s", k3d.DefaultObjectNamePrefix, containerDetails.Config.Labels["k3d.cluster"])) { // FIXME: catch error if label 'k3d.cluster' does not exist, but this should also never be the case
clusterNetwork = networkName
}
}
// masterOpts
masterOpts := k3d.MasterOpts{IsInit: false}
for k, v := range containerDetails.Config.Labels {
/*
node.Labels["k3d.master.api.hostIP"] = node.MasterOpts.ExposeAPI.HostIP // TODO: maybe get docker machine IP here
node.Labels["k3d.master.api.host"] = node.MasterOpts.ExposeAPI.Host
node.Labels["k3d.master.api.port"] = node.MasterOpts.ExposeAPI.Port
*/
if k == "k3d.master.api.hostIP" {
masterOpts.ExposeAPI.HostIP = v
} else if k == "k3d.master.api.host" {
masterOpts.ExposeAPI.Host = v
} else if k == "k3d.master.api.port" {
masterOpts.ExposeAPI.Port = v
}
}
// env vars: only copy K3S_* and K3D_* // FIXME: should we really do this? Might be unexpected, if user has e.g. HTTP_PROXY vars
env := []string{}
for _, envVar := range containerDetails.Config.Env {
if strings.HasPrefix(envVar, "K3D_") || strings.HasPrefix(envVar, "K3S_") {
env = append(env, envVar)
}
}
// labels: only copy k3d.* labels
labels := map[string]string{}
for k, v := range containerDetails.Config.Labels {
if strings.HasPrefix(k, "k3d") {
labels[k] = v
}
}
node := &k3d.Node{
Name: strings.TrimPrefix(containerDetails.Name, "/"), // container name with leading '/' cut off
Role: k3d.NodeRoles[containerDetails.Config.Labels["k3d.role"]],
Image: containerDetails.Image,
Volumes: containerDetails.HostConfig.Binds,
Env: env,
Cmd: containerDetails.Config.Cmd,
Args: []string{}, // empty, since Cmd already contains flags
Ports: ports,
Restart: restart,
Labels: labels,
Network: clusterNetwork,
MasterOpts: masterOpts,
WorkerOpts: k3d.WorkerOpts{},
}
return node, nil
}

Loading…
Cancel
Save