mirror of https://github.com/k3d-io/k3d
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
211 lines
6.9 KiB
211 lines
6.9 KiB
/*
|
|
Copyright © 2020 The k3d Author(s)
|
|
|
|
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 docker
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/docker/docker/api/types"
|
|
docker "github.com/docker/docker/api/types/container"
|
|
"github.com/docker/docker/api/types/network"
|
|
"github.com/docker/go-connections/nat"
|
|
k3d "github.com/rancher/k3d/v4/pkg/types"
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
dockercliopts "github.com/docker/cli/opts"
|
|
)
|
|
|
|
// TranslateNodeToContainer translates a k3d node specification to a docker container representation
|
|
func TranslateNodeToContainer(node *k3d.Node) (*NodeInDocker, error) {
|
|
|
|
/* initialize everything that we need */
|
|
containerConfig := docker.Config{}
|
|
hostConfig := docker.HostConfig{
|
|
Init: &[]bool{true}[0],
|
|
ExtraHosts: node.ExtraHosts,
|
|
}
|
|
networkingConfig := network.NetworkingConfig{}
|
|
|
|
/* Name & Image */
|
|
containerConfig.Hostname = node.Name
|
|
containerConfig.Image = node.Image
|
|
|
|
/* Command & Arguments */
|
|
containerConfig.Cmd = []string{}
|
|
|
|
containerConfig.Cmd = append(containerConfig.Cmd, node.Cmd...) // contains k3s command and role-specific required flags/args
|
|
containerConfig.Cmd = append(containerConfig.Cmd, node.Args...) // extra flags/args
|
|
|
|
/* Environment Variables */
|
|
containerConfig.Env = node.Env
|
|
|
|
/* Labels */
|
|
containerConfig.Labels = node.Labels // has to include the role
|
|
|
|
/* Auto-Restart */
|
|
if node.Restart {
|
|
hostConfig.RestartPolicy = docker.RestartPolicy{
|
|
Name: "unless-stopped",
|
|
}
|
|
}
|
|
|
|
/* Tmpfs Mounts */
|
|
hostConfig.Tmpfs = make(map[string]string)
|
|
for _, mnt := range k3d.DefaultTmpfsMounts {
|
|
hostConfig.Tmpfs[mnt] = ""
|
|
}
|
|
|
|
if node.GPURequest != "" {
|
|
gpuopts := dockercliopts.GpuOpts{}
|
|
if err := gpuopts.Set(node.GPURequest); err != nil {
|
|
return nil, fmt.Errorf("Failed to set GPU Request: %+v", err)
|
|
}
|
|
hostConfig.DeviceRequests = gpuopts.Value()
|
|
}
|
|
|
|
/* They have to run in privileged mode */
|
|
// TODO: can we replace this by a reduced set of capabilities?
|
|
hostConfig.Privileged = true
|
|
|
|
/* Volumes */
|
|
hostConfig.Binds = node.Volumes
|
|
// containerConfig.Volumes = map[string]struct{}{} // TODO: do we need this? We only used binds before
|
|
|
|
/* Ports */
|
|
exposedPorts, portBindings, err := nat.ParsePortSpecs(node.Ports)
|
|
if err != nil {
|
|
log.Errorf("Failed to parse port specs '%v'", node.Ports)
|
|
return nil, err
|
|
}
|
|
containerConfig.ExposedPorts = exposedPorts
|
|
hostConfig.PortBindings = portBindings
|
|
/* Network */
|
|
networkingConfig.EndpointsConfig = map[string]*network.EndpointSettings{
|
|
node.Network: {},
|
|
}
|
|
netInfo, err := GetNetwork(context.Background(), node.Network)
|
|
if err != nil {
|
|
log.Warnln("Failed to get network information")
|
|
log.Warnln(err)
|
|
} else if netInfo.Driver == "host" {
|
|
hostConfig.NetworkMode = "host"
|
|
}
|
|
|
|
return &NodeInDocker{
|
|
ContainerConfig: containerConfig,
|
|
HostConfig: hostConfig,
|
|
NetworkingConfig: networkingConfig,
|
|
}, nil
|
|
}
|
|
|
|
// TranslateContainerToNode translates a docker container object into a k3d node representation
|
|
func TranslateContainerToNode(cont *types.Container) (*k3d.Node, error) {
|
|
node := &k3d.Node{
|
|
Name: strings.TrimPrefix(cont.Names[0], "/"), // container name with leading '/' cut off
|
|
Image: cont.Image,
|
|
Labels: cont.Labels,
|
|
Role: k3d.NodeRoles[cont.Labels[k3d.LabelRole]],
|
|
// TODO: all the rest
|
|
}
|
|
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.LabelClusterName])) { // FIXME: catch error if label 'k3d.cluster' does not exist, but this should also never be the case
|
|
clusterNetwork = networkName
|
|
}
|
|
}
|
|
|
|
// serverOpts
|
|
serverOpts := k3d.ServerOpts{IsInit: false}
|
|
for k, v := range containerDetails.Config.Labels {
|
|
if k == k3d.LabelServerAPIHostIP {
|
|
serverOpts.ExposeAPI.HostIP = v
|
|
} else if k == k3d.LabelServerAPIHost {
|
|
serverOpts.ExposeAPI.Host = v
|
|
} else if k == k3d.LabelServerAPIPort {
|
|
serverOpts.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
|
|
}
|
|
}
|
|
|
|
// status
|
|
nodeState := k3d.NodeState{
|
|
Running: containerDetails.ContainerJSONBase.State.Running,
|
|
Status: containerDetails.ContainerJSONBase.State.Status,
|
|
}
|
|
|
|
node := &k3d.Node{
|
|
Name: strings.TrimPrefix(containerDetails.Name, "/"), // container name with leading '/' cut off
|
|
Role: k3d.NodeRoles[containerDetails.Config.Labels[k3d.LabelRole]],
|
|
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,
|
|
ServerOpts: serverOpts,
|
|
AgentOpts: k3d.AgentOpts{},
|
|
State: nodeState,
|
|
}
|
|
return node, nil
|
|
}
|
|
|