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.
199 lines
5.6 KiB
199 lines
5.6 KiB
package run
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"strings"
|
|
|
|
"github.com/docker/go-connections/nat"
|
|
)
|
|
|
|
// PublishedPorts is a struct used for exposing container ports on the host system
|
|
type PublishedPorts struct {
|
|
ExposedPorts map[nat.Port]struct{}
|
|
PortBindings map[nat.Port][]nat.PortBinding
|
|
}
|
|
|
|
// defaultNodes describes the type of nodes on which a port should be exposed by default
|
|
const defaultNodes = "server"
|
|
|
|
// mapping a node role to groups that should be applied to it
|
|
var nodeRuleGroupsMap = map[string][]string{
|
|
"worker": []string{"all", "workers"},
|
|
"server": []string{"all", "server", "master"},
|
|
}
|
|
|
|
// mapNodesToPortSpecs maps nodes to portSpecs
|
|
func mapNodesToPortSpecs(specs []string, createdNodes []string) (map[string][]string, error) {
|
|
|
|
if err := validatePortSpecs(specs); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// check node-specifier possibilitites
|
|
possibleNodeSpecifiers := []string{"all", "workers", "server", "master"}
|
|
possibleNodeSpecifiers = append(possibleNodeSpecifiers, createdNodes...)
|
|
|
|
nodeToPortSpecMap := make(map[string][]string)
|
|
|
|
for _, spec := range specs {
|
|
nodes, portSpec := extractNodes(spec)
|
|
|
|
for _, node := range nodes {
|
|
// 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
|
|
nodeToPortSpecMap[node] = append(nodeToPortSpecMap[node], portSpec)
|
|
break
|
|
}
|
|
}
|
|
if !nodeFound {
|
|
log.Printf("WARNING: Unknown node-specifier [%s] in port mapping entry [%s]", node, spec)
|
|
}
|
|
}
|
|
}
|
|
|
|
return nodeToPortSpecMap, nil
|
|
}
|
|
|
|
// CreatePublishedPorts is the factory function for PublishedPorts
|
|
func CreatePublishedPorts(specs []string) (*PublishedPorts, error) {
|
|
if len(specs) == 0 {
|
|
var newExposedPorts = make(map[nat.Port]struct{}, 1)
|
|
var newPortBindings = make(map[nat.Port][]nat.PortBinding, 1)
|
|
return &PublishedPorts{ExposedPorts: newExposedPorts, PortBindings: newPortBindings}, nil
|
|
}
|
|
|
|
newExposedPorts, newPortBindings, err := nat.ParsePortSpecs(specs)
|
|
return &PublishedPorts{ExposedPorts: newExposedPorts, PortBindings: newPortBindings}, err
|
|
}
|
|
|
|
// validatePortSpecs matches the provided port specs against a set of rules to enable early exit if something is wrong
|
|
func validatePortSpecs(specs []string) error {
|
|
for _, spec := range specs {
|
|
atSplit := strings.Split(spec, "@")
|
|
_, err := nat.ParsePortSpec(atSplit[0])
|
|
if err != nil {
|
|
return fmt.Errorf("ERROR: Invalid port specification [%s] in port mapping [%s]\n%+v", atSplit[0], spec, err)
|
|
}
|
|
if len(atSplit) > 0 {
|
|
for i := 1; i < len(atSplit); i++ {
|
|
if err := ValidateHostname(atSplit[i]); err != nil {
|
|
return fmt.Errorf("ERROR: Invalid node-specifier [%s] in port mapping [%s]\n%+v", atSplit[i], spec, err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// extractNodes separates the node specification from the actual port specs
|
|
func extractNodes(spec string) ([]string, string) {
|
|
// extract nodes
|
|
nodes := []string{}
|
|
atSplit := strings.Split(spec, "@")
|
|
portSpec := atSplit[0]
|
|
if len(atSplit) > 1 {
|
|
nodes = atSplit[1:]
|
|
}
|
|
if len(nodes) == 0 {
|
|
nodes = append(nodes, defaultNodes)
|
|
}
|
|
return nodes, portSpec
|
|
}
|
|
|
|
// 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))
|
|
|
|
for k, v := range p.ExposedPorts {
|
|
newExposedPorts[k] = v
|
|
}
|
|
|
|
for k, v := range p.PortBindings {
|
|
bindings := make([]nat.PortBinding, len(v))
|
|
for i, b := range v {
|
|
port, _ := nat.ParsePort(b.HostPort)
|
|
bindings[i].HostIP = b.HostIP
|
|
bindings[i].HostPort = fmt.Sprintf("%d", port*offset)
|
|
}
|
|
newPortBindings[k] = bindings
|
|
}
|
|
|
|
return &PublishedPorts{ExposedPorts: newExposedPorts, PortBindings: newPortBindings}
|
|
}
|
|
|
|
// AddPort creates a new PublishedPort struct with one more port, based on 'portSpec'
|
|
func (p *PublishedPorts) AddPort(portSpec string) (*PublishedPorts, error) {
|
|
portMappings, err := nat.ParsePortSpec(portSpec)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var newExposedPorts = make(map[nat.Port]struct{}, len(p.ExposedPorts)+1)
|
|
var newPortBindings = make(map[nat.Port][]nat.PortBinding, len(p.PortBindings)+1)
|
|
|
|
// Populate the new maps
|
|
for k, v := range p.ExposedPorts {
|
|
newExposedPorts[k] = v
|
|
}
|
|
|
|
for k, v := range p.PortBindings {
|
|
newPortBindings[k] = v
|
|
}
|
|
|
|
// Add new ports
|
|
for _, portMapping := range portMappings {
|
|
port := portMapping.Port
|
|
if _, exists := newExposedPorts[port]; !exists {
|
|
newExposedPorts[port] = struct{}{}
|
|
}
|
|
|
|
bslice, exists := newPortBindings[port]
|
|
if !exists {
|
|
bslice = []nat.PortBinding{}
|
|
}
|
|
newPortBindings[port] = append(bslice, portMapping.Binding)
|
|
}
|
|
|
|
return &PublishedPorts{ExposedPorts: newExposedPorts, PortBindings: newPortBindings}, nil
|
|
}
|
|
|
|
// MergePortSpecs merges published ports for a given node
|
|
func MergePortSpecs(nodeToPortSpecMap map[string][]string, role, name string) ([]string, error) {
|
|
|
|
portSpecs := []string{}
|
|
|
|
// add portSpecs according to node role
|
|
for _, group := range nodeRuleGroupsMap[role] {
|
|
for _, v := range nodeToPortSpecMap[group] {
|
|
exists := false
|
|
for _, i := range portSpecs {
|
|
if v == i {
|
|
exists = true
|
|
}
|
|
}
|
|
if !exists {
|
|
portSpecs = append(portSpecs, v)
|
|
}
|
|
}
|
|
}
|
|
|
|
// add portSpecs according to node name
|
|
for _, v := range nodeToPortSpecMap[name] {
|
|
exists := false
|
|
for _, i := range portSpecs {
|
|
if v == i {
|
|
exists = true
|
|
}
|
|
}
|
|
if !exists {
|
|
portSpecs = append(portSpecs, v)
|
|
}
|
|
}
|
|
|
|
return portSpecs, nil
|
|
}
|
|
|