This commit is contained in:
iwilltry42 2019-05-08 13:37:24 +02:00
parent 65f2820b3f
commit d35f12a77e
3 changed files with 178 additions and 91 deletions

View File

@ -71,8 +71,18 @@ func CreateCluster(c *cli.Context) error {
k3sServerArgs = append(k3sServerArgs, c.StringSlice("server-arg")...)
}
fmt.Println("==========")
pm, err := createPortMap(c.StringSlice("publish"))
fmt.Printf("pm: %+v \n err: %+v \n", pm, err)
for _, x := range *pm {
fmt.Printf("x: %+v\n -> Ports: %+v\n", x, x.Ports)
}
fmt.Println("==========")
publishedPorts, err := createPublishedPorts(c.StringSlice("publish"))
if (err != nil) {
fmt.Printf("pm: %+v \n err: %+v \n", publishedPorts, err)
fmt.Println("==========")
if err != nil {
log.Fatalf("ERROR: failed to parse the publish parameter.\n%+v", err)
}

View File

@ -18,84 +18,8 @@ import (
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/client"
"github.com/docker/go-connections/nat"
)
type PublishedPorts struct {
ExposedPorts map[nat.Port]struct{}
PortBindings map[nat.Port][]nat.PortBinding
}
// 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
}
// Create 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}
}
// Create 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
}
func startContainer(verbose bool, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, containerName string) (string, error) {
ctx := context.Background()
@ -139,7 +63,7 @@ func startContainer(verbose bool, config *container.Config, hostConfig *containe
}
func createServer(verbose bool, image string, port string, args []string, env []string,
name string, volumes []string, pPorts *PublishedPorts) (string, error) {
name string, volumes []string, pPorts *PublishedPorts) (string, error) {
log.Printf("Creating server using %s...\n", image)
containerLabels := make(map[string]string)
@ -152,13 +76,13 @@ func createServer(verbose bool, image string, port string, args []string, env []
apiPortSpec := fmt.Sprintf("0.0.0.0:%s:%s/tcp", port, port)
serverPublishedPorts, err := pPorts.AddPort(apiPortSpec)
if (err != nil) {
if err != nil {
log.Fatalf("Error: failed to parse API port spec %s \n%+v", apiPortSpec, err)
}
hostConfig := &container.HostConfig{
PortBindings: serverPublishedPorts.PortBindings,
Privileged: true,
Privileged: true,
}
if len(volumes) > 0 && volumes[0] != "" {
@ -174,12 +98,12 @@ func createServer(verbose bool, image string, port string, args []string, env []
}
config := &container.Config{
Hostname: containerName,
Image: image,
Cmd: append([]string{"server"}, args...),
Hostname: containerName,
Image: image,
Cmd: append([]string{"server"}, args...),
ExposedPorts: serverPublishedPorts.ExposedPorts,
Env: env,
Labels: containerLabels,
Env: env,
Labels: containerLabels,
}
id, err := startContainer(verbose, config, hostConfig, networkingConfig, containerName)
if err != nil {
@ -191,7 +115,7 @@ func createServer(verbose bool, image string, port string, args []string, env []
// createWorker creates/starts a k3s agent node that connects to the server
func createWorker(verbose bool, image string, args []string, env []string, name string, volumes []string,
postfix int, serverPort string, pPorts *PublishedPorts) (string, error) {
postfix int, serverPort string, pPorts *PublishedPorts) (string, error) {
containerLabels := make(map[string]string)
containerLabels["app"] = "k3d"
containerLabels["component"] = "worker"
@ -210,7 +134,7 @@ func createWorker(verbose bool, image string, args []string, env []string, name
"/var/run": "",
},
PortBindings: workerPublishedPorts.PortBindings,
Privileged: true,
Privileged: true,
}
if len(volumes) > 0 && volumes[0] != "" {
@ -226,10 +150,10 @@ func createWorker(verbose bool, image string, args []string, env []string, name
}
config := &container.Config{
Hostname: containerName,
Image: image,
Env: env,
Labels: containerLabels,
Hostname: containerName,
Image: image,
Env: env,
Labels: containerLabels,
ExposedPorts: workerPublishedPorts.ExposedPorts,
}

153
cli/port.go Normal file
View File

@ -0,0 +1,153 @@
package run
import (
"fmt"
"regexp"
"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
}
// Portmap maps node roles/names to a set of PublishedPorts
type Portmap struct {
Node string
Ports *PublishedPorts
}
// defaultNodes describes the type of nodes on which a port should be exposed by default
const defaultNodes = "all"
// createPortMap creates a list of portmaps that map nodes (roles or names) to a list of published ports
func createPortMap(specs []string) (*[]Portmap, error) {
if err := validatePortSpecs(specs); err != nil {
return nil, err
}
nodeToPortSpecMap := make(map[string][]string)
for _, spec := range specs {
nodes, portSpec := extractNodes(spec)
for _, node := range nodes {
nodeToPortSpecMap[node] = append(nodeToPortSpecMap[node], portSpec)
}
}
portmaps := []Portmap{}
for node, portSpecs := range nodeToPortSpecMap {
ports, err := createPublishedPorts(portSpecs)
if err != nil {
return nil, err
}
newPortMap := Portmap{
Node: node,
Ports: ports,
}
portmaps = append(portmaps, newPortMap)
}
return &portmaps, nil
}
// 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 {
// regex matching (no sophisticated IP/Hostname matching at the moment)
regex := regexp.MustCompile(`^(((?P<host>[\w\.]+)?:)?((?P<hostPort>[0-9]{0,6}):)?(?P<containerPort>[0-9]{1,6}))((/(?P<protocol>udp|tcp))?(?P<nodes>(@(?P<node>[\w-]+))*))$`)
for _, spec := range specs {
if !regex.MatchString(spec) {
return fmt.Errorf("[ERROR] Provided port spec [%s] didn't match format specification", spec)
}
}
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
}