Skip to content

Commit

Permalink
Merge pull request #32 from andyz-dev/publish
Browse files Browse the repository at this point in the history
[Feature] Add --publish/--add-port flag for port mappings
  • Loading branch information
andyz-dev authored May 9, 2019
2 parents d0f905d + 65f2820 commit 32cc70b
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 16 deletions.
9 changes: 8 additions & 1 deletion cli/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@ func CreateCluster(c *cli.Context) error {
k3sServerArgs = append(k3sServerArgs, c.StringSlice("server-arg")...)
}

publishedPorts, err := createPublishedPorts(c.StringSlice("publish"))
if (err != nil) {
log.Fatalf("ERROR: failed to parse the publish parameter.\n%+v", err)
}

// create the server
log.Printf("Creating cluster [%s]", c.String("name"))
dockerID, err := createServer(
Expand All @@ -105,6 +110,7 @@ func CreateCluster(c *cli.Context) error {
env,
c.String("name"),
strings.Split(c.String("volume"), ","),
publishedPorts,
)
if err != nil {
log.Fatalf("ERROR: failed to create cluster\n%+v", err)
Expand Down Expand Up @@ -166,8 +172,9 @@ func CreateCluster(c *cli.Context) error {
env,
c.String("name"),
strings.Split(c.String("volume"), ","),
strconv.Itoa(i),
i,
c.String("port"),
publishedPorts,
)
if err != nil {
return fmt.Errorf("ERROR: failed to create worker node for cluster %s\n%+v", c.String("name"), err)
Expand Down
106 changes: 91 additions & 15 deletions cli/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,81 @@ import (
"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()

Expand Down Expand Up @@ -63,7 +138,8 @@ func startContainer(verbose bool, config *container.Config, hostConfig *containe
return resp.ID, nil
}

func createServer(verbose bool, image string, port string, args []string, env []string, name string, volumes []string) (string, error) {
func createServer(verbose bool, image string, port string, args []string, env []string,
name string, volumes []string, pPorts *PublishedPorts) (string, error) {
log.Printf("Creating server using %s...\n", image)

containerLabels := make(map[string]string)
Expand All @@ -74,17 +150,14 @@ func createServer(verbose bool, image string, port string, args []string, env []

containerName := fmt.Sprintf("k3d-%s-server", name)

containerPort := nat.Port(fmt.Sprintf("%s/tcp", port))
apiPortSpec := fmt.Sprintf("0.0.0.0:%s:%s/tcp", port, port)
serverPublishedPorts, err := pPorts.AddPort(apiPortSpec)
if (err != nil) {
log.Fatalf("Error: failed to parse API port spec %s \n%+v", apiPortSpec, err)
}

hostConfig := &container.HostConfig{
PortBindings: nat.PortMap{
containerPort: []nat.PortBinding{
{
HostIP: "0.0.0.0",
HostPort: port,
},
},
},
PortBindings: serverPublishedPorts.PortBindings,
Privileged: true,
}

Expand All @@ -104,9 +177,7 @@ func createServer(verbose bool, image string, port string, args []string, env []
Hostname: containerName,
Image: image,
Cmd: append([]string{"server"}, args...),
ExposedPorts: nat.PortSet{
containerPort: struct{}{},
},
ExposedPorts: serverPublishedPorts.ExposedPorts,
Env: env,
Labels: containerLabels,
}
Expand All @@ -119,22 +190,26 @@ 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 string, serverPort string) (string, error) {
func createWorker(verbose bool, image string, args []string, env []string, name string, volumes []string,
postfix int, serverPort string, pPorts *PublishedPorts) (string, error) {
containerLabels := make(map[string]string)
containerLabels["app"] = "k3d"
containerLabels["component"] = "worker"
containerLabels["created"] = time.Now().Format("2006-01-02 15:04:05")
containerLabels["cluster"] = name

containerName := fmt.Sprintf("k3d-%s-worker-%s", name, postfix)
containerName := fmt.Sprintf("k3d-%s-worker-%d", name, postfix)

env = append(env, fmt.Sprintf("K3S_URL=https://k3d-%s-server:%s", name, serverPort))

workerPublishedPorts := pPorts.Offset(postfix + 1)

hostConfig := &container.HostConfig{
Tmpfs: map[string]string{
"/run": "",
"/var/run": "",
},
PortBindings: workerPublishedPorts.PortBindings,
Privileged: true,
}

Expand All @@ -155,6 +230,7 @@ func createWorker(verbose bool, image string, args []string, env []string, name
Image: image,
Env: env,
Labels: containerLabels,
ExposedPorts: workerPublishedPorts.ExposedPorts,
}

id, err := startContainer(verbose, config, hostConfig, networkingConfig, containerName)
Expand Down
4 changes: 4 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ func main() {
Name: "volume, v",
Usage: "Mount one or more volumes into every node of the cluster (Docker notation: `source:destination[,source:destination]`)",
},
cli.StringSliceFlag{
Name: "publish, add-port",
Usage: "publish k3s node ports to the host (Docker notation: `ip:public:private/proto`, use multiple options to expose more ports)",
},
cli.StringFlag{
// TODO: to be deprecated
Name: "version",
Expand Down

0 comments on commit 32cc70b

Please sign in to comment.