diff --git a/pkg/apis/meta/v1alpha1/net.go b/pkg/apis/meta/v1alpha1/net.go index 159d42d22..44c9885ae 100644 --- a/pkg/apis/meta/v1alpha1/net.go +++ b/pkg/apis/meta/v1alpha1/net.go @@ -6,6 +6,8 @@ import ( "net" "strconv" "strings" + + "github.com/docker/go-connections/nat" ) // PortMapping defines a port mapping between the VM and the host @@ -41,65 +43,59 @@ type PortMappings []PortMapping var _ fmt.Stringer = PortMappings{} -var errInvalidPortMappingFormat = fmt.Errorf("port mappings must be of form [:]:[/]") - func ParsePortMappings(input []string) (PortMappings, error) { result := make(PortMappings, 0, len(input)) - for _, portMapping := range input { - ports := strings.Split(portMapping, ":") - if len(ports) > 3 || len(ports) < 2 { - return nil, errInvalidPortMappingFormat + _, bindings, err := nat.ParsePortSpecs(input) + if err != nil { + return nil, err + } + + for port, bindings := range bindings { + if len(bindings) > 1 { + // TODO: For now only support mapping a VM port to a single host IP/port + return nil, fmt.Errorf("only one host binding per VM binding supported for now, received %d", len(bindings)) } + binding := bindings[0] + var err error var bindAddress net.IP + var hostPort uint64 + var vmPort uint64 var protocol Protocol - offset := 0 - - if len(ports) == 3 { - offset = 1 - if bindAddress = net.ParseIP(ports[0]); bindAddress == nil { - return nil, errInvalidPortMappingFormat + if len(binding.HostIP) > 0 { + if bindAddress = net.ParseIP(binding.HostIP); bindAddress == nil { + return nil, fmt.Errorf("invalid bind address: %q", binding.HostIP) } } - hostPort, err := strconv.ParseUint(ports[0+offset], 10, 64) - if err != nil { - return nil, err + if hostPort, err = strconv.ParseUint(binding.HostPort, 10, 64); err != nil { + return nil, fmt.Errorf("invalid host port: %q", binding.HostPort) } - proto := strings.Split(ports[1+offset], "/") - - if len(proto) > 2 { - return nil, errInvalidPortMappingFormat + if vmPort, err = strconv.ParseUint(port.Port(), 10, 64); err != nil { + return nil, fmt.Errorf("invalid VM port: %q", port.Port()) } - if len(proto) == 2 { - if protocol, err = protocolFromString(proto[1]); err != nil { - return nil, err - } + if protocol, err = protocolFromString(port.Proto()); err != nil { + return nil, err } - vmPort, err := strconv.ParseUint(proto[0], 10, 64) - if err != nil { - return nil, err + mapping := PortMapping{ + BindAddress: bindAddress, + HostPort: hostPort, + VMPort: vmPort, + Protocol: protocol, } for _, portMapping := range result { - if portMapping.HostPort == hostPort && portMapping.Protocol == protocol { + if portMapping.HostPort == mapping.HostPort && portMapping.Protocol == mapping.Protocol { return nil, fmt.Errorf("cannot use a port/protocol combination on the host twice") } } - // TODO: Check for duplicate VM ports - - result = append(result, PortMapping{ - BindAddress: bindAddress, - HostPort: hostPort, - VMPort: vmPort, - Protocol: protocol, - }) + result = append(result, mapping) } return result, nil @@ -145,10 +141,6 @@ func (p Protocol) String() string { return string(p) } -func (p Protocol) MarshalJSON() ([]byte, error) { - return json.Marshal(p) -} - func (p *Protocol) UnmarshalJSON(b []byte) (err error) { var s string if err = json.Unmarshal(b, &s); err != nil { diff --git a/pkg/runtime/docker/client.go b/pkg/runtime/docker/client.go index 6138473d5..319fc97b1 100644 --- a/pkg/runtime/docker/client.go +++ b/pkg/runtime/docker/client.go @@ -10,15 +10,12 @@ import ( "github.com/docker/docker/api/types/container" cont "github.com/docker/docker/api/types/container" "github.com/docker/docker/client" - "github.com/docker/go-connections/nat" - meta "github.com/weaveworks/ignite/pkg/apis/meta/v1alpha1" "github.com/weaveworks/ignite/pkg/runtime" "github.com/weaveworks/ignite/pkg/util" ) const ( dockerNetNSFmt = "/proc/%v/ns/net" - portFormat = "%d/tcp" // TODO: Support protocols other than TCP ) // dockerClient is a runtime.Interface @@ -104,27 +101,6 @@ func (dc *dockerClient) AttachContainer(container string) (err error) { } func (dc *dockerClient) RunContainer(image string, config *runtime.ContainerConfig, name string) (string, error) { - portBindings := make(nat.PortMap) - for _, portMapping := range config.PortBindings { - var hostIP string - if portMapping.BindAddress != nil { - hostIP = portMapping.BindAddress.String() - } - - protocol := portMapping.Protocol - if len(protocol) == 0 { - // Docker uses TCP by default - protocol = meta.ProtocolTCP - } - - portBindings[nat.Port(fmt.Sprintf("%d/%s", portMapping.VMPort, protocol.String()))] = []nat.PortBinding{ - { - HostIP: hostIP, - HostPort: fmt.Sprintf(portFormat, portMapping.HostPort), - }, - } - } - binds := make([]string, 0, len(config.Binds)) for _, bind := range config.Binds { binds = append(binds, fmt.Sprintf("%s:%s", bind.HostPath, bind.ContainerPath)) @@ -152,7 +128,7 @@ func (dc *dockerClient) RunContainer(image string, config *runtime.ContainerConf }, &container.HostConfig{ Binds: binds, NetworkMode: container.NetworkMode(config.NetworkMode), - PortBindings: portBindings, + PortBindings: portBindingsToPortMap(config.PortBindings), AutoRemove: config.AutoRemove, CapAdd: config.CapAdds, Resources: container.Resources{ diff --git a/pkg/runtime/docker/port.go b/pkg/runtime/docker/port.go new file mode 100644 index 000000000..8522701c7 --- /dev/null +++ b/pkg/runtime/docker/port.go @@ -0,0 +1,35 @@ +package docker + +import ( + "fmt" + "strconv" + + "github.com/docker/go-connections/nat" + meta "github.com/weaveworks/ignite/pkg/apis/meta/v1alpha1" +) + +func portBindingsToPortMap(portMappings meta.PortMappings) nat.PortMap { + portMap := make(nat.PortMap) + + for _, portMapping := range portMappings { + var hostIP string + if portMapping.BindAddress != nil { + hostIP = portMapping.BindAddress.String() + } + + protocol := portMapping.Protocol + if len(protocol) == 0 { + // Docker uses TCP by default + protocol = meta.ProtocolTCP + } + + portMap[nat.Port(fmt.Sprintf("%d/%s", portMapping.VMPort, protocol.String()))] = []nat.PortBinding{ + { + HostIP: hostIP, + HostPort: strconv.FormatUint(portMapping.HostPort, 10), + }, + } + } + + return portMap +}