Skip to content
This repository has been archived by the owner on Dec 7, 2023. It is now read-only.

Support configuring BindAddress and Protocol for a PortMapping #299

Merged
merged 4 commits into from
Aug 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 42 additions & 9 deletions docs/api/meta_v1alpha1.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@
- [func ParsePortMappings(input \[\]string) (PortMappings,
error)](#ParsePortMappings)
- [func (p PortMappings) String() string](#PortMappings.String)
- [type Protocol](#Protocol)
- [func (p Protocol) String() string](#Protocol.String)
- [func (p \*Protocol) UnmarshalJSON(b \[\]byte) (err
error)](#Protocol.UnmarshalJSON)
- [type Size](#Size)
- [func NewSizeFromBytes(bytes uint64) Size](#NewSizeFromBytes)
- [func NewSizeFromSectors(sectors uint64)
Expand Down Expand Up @@ -192,15 +196,15 @@ func (d *DMID) Pool() bool
func (d DMID) String() string
```

## <a name="IPAddresses">type</a> [IPAddresses](https://github.com/weaveworks/ignite/tree/master/pkg/apis/meta/v1alpha1/net.go?s=1541:1566#L78)
## <a name="IPAddresses">type</a> [IPAddresses](https://github.com/weaveworks/ignite/tree/master/pkg/apis/meta/v1alpha1/net.go?s=3422:3447#L155)

``` go
type IPAddresses []net.IP
```

IPAddresses represents a list of VM IP addresses

### <a name="IPAddresses.String">func</a> (IPAddresses) [String](https://github.com/weaveworks/ignite/tree/master/pkg/apis/meta/v1alpha1/net.go?s=1604:1640#L82)
### <a name="IPAddresses.String">func</a> (IPAddresses) [String](https://github.com/weaveworks/ignite/tree/master/pkg/apis/meta/v1alpha1/net.go?s=3485:3521#L159)

``` go
func (i IPAddresses) String() string
Expand Down Expand Up @@ -412,43 +416,72 @@ func (o *ObjectMeta) SetUID(uid UID)

SetUID sets the UID of the Object

## <a name="PortMapping">type</a> [PortMapping](https://github.com/weaveworks/ignite/tree/master/pkg/apis/meta/v1alpha1/net.go?s=132:227#L11)
## <a name="PortMapping">type</a> [PortMapping](https://github.com/weaveworks/ignite/tree/master/pkg/apis/meta/v1alpha1/net.go?s=190:398#L14)

``` go
type PortMapping struct {
HostPort uint64 `json:"hostPort"`
VMPort uint64 `json:"vmPort"`
BindAddress net.IP `json:"bindAddress,omitempty"`
HostPort uint64 `json:"hostPort"`
VMPort uint64 `json:"vmPort"`
Protocol Protocol `json:"protocol,omitempty"`
}
```

PortMapping defines a port mapping between the VM and the host

### <a name="PortMapping.String">func</a> (PortMapping) [String](https://github.com/weaveworks/ignite/tree/master/pkg/apis/meta/v1alpha1/net.go?s=265:301#L18)
### <a name="PortMapping.String">func</a> (PortMapping) [String](https://github.com/weaveworks/ignite/tree/master/pkg/apis/meta/v1alpha1/net.go?s=436:472#L23)

``` go
func (p PortMapping) String() string
```

## <a name="PortMappings">type</a> [PortMappings](https://github.com/weaveworks/ignite/tree/master/pkg/apis/meta/v1alpha1/net.go?s=418:449#L23)
## <a name="PortMappings">type</a> [PortMappings](https://github.com/weaveworks/ignite/tree/master/pkg/apis/meta/v1alpha1/net.go?s=826:857#L42)

``` go
type PortMappings []PortMapping
```

PortMappings represents a list of port mappings

### <a name="ParsePortMappings">func</a> [ParsePortMappings](https://github.com/weaveworks/ignite/tree/master/pkg/apis/meta/v1alpha1/net.go?s=488:548#L27)
### <a name="ParsePortMappings">func</a> [ParsePortMappings](https://github.com/weaveworks/ignite/tree/master/pkg/apis/meta/v1alpha1/net.go?s=896:956#L46)

``` go
func ParsePortMappings(input []string) (PortMappings, error)
```

### <a name="PortMappings.String">func</a> (PortMappings) [String](https://github.com/weaveworks/ignite/tree/master/pkg/apis/meta/v1alpha1/net.go?s=1249:1286#L61)
### <a name="PortMappings.String">func</a> (PortMappings) [String](https://github.com/weaveworks/ignite/tree/master/pkg/apis/meta/v1alpha1/net.go?s=2475:2512#L104)

``` go
func (p PortMappings) String() string
```

## <a name="Protocol">type</a> [Protocol](https://github.com/weaveworks/ignite/tree/master/pkg/apis/meta/v1alpha1/net.go?s=2761:2781#L121)

``` go
type Protocol string
```

Protocol specifies a network port protocol

``` go
const (
ProtocolTCP Protocol = "tcp"
ProtocolUDP Protocol = "udp"
)
```

### <a name="Protocol.String">func</a> (Protocol) [String](https://github.com/weaveworks/ignite/tree/master/pkg/apis/meta/v1alpha1/net.go?s=3135:3168#L140)

``` go
func (p Protocol) String() string
```

### <a name="Protocol.UnmarshalJSON">func</a> (\*Protocol) [UnmarshalJSON](https://github.com/weaveworks/ignite/tree/master/pkg/apis/meta/v1alpha1/net.go?s=3192:3246#L144)

``` go
func (p *Protocol) UnmarshalJSON(b []byte) (err error)
```

## <a name="Size">type</a> [Size](https://github.com/weaveworks/ignite/tree/master/pkg/apis/meta/v1alpha1/size.go?s=132:171#L11)

``` go
Expand Down
4 changes: 3 additions & 1 deletion pkg/apis/ignite/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion pkg/apis/ignite/v1alpha2/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pkg/apis/ignite/validation/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ func ValidateVM(obj *api.VM) (allErrs field.ErrorList) {
allErrs = append(allErrs, ValidateFileMappings(&obj.Spec.CopyFiles, field.NewPath(".spec.copyFiles"))...)
allErrs = append(allErrs, ValidateVMStorage(&obj.Spec.Storage, field.NewPath(".spec.storage"))...)
// TODO: Add vCPU, memory, disk max and min sizes
// TODO: Add port mapping validation
return
}

Expand Down
4 changes: 3 additions & 1 deletion pkg/apis/ignite/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

113 changes: 95 additions & 18 deletions pkg/apis/meta/v1alpha1/net.go
Original file line number Diff line number Diff line change
@@ -1,22 +1,41 @@
package v1alpha1

import (
"encoding/json"
"fmt"
"net"
"strconv"
"strings"

"github.com/docker/go-connections/nat"
)

// PortMapping defines a port mapping between the VM and the host
type PortMapping struct {
HostPort uint64 `json:"hostPort"`
VMPort uint64 `json:"vmPort"`
BindAddress net.IP `json:"bindAddress,omitempty"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is optional; I'd prefer for this to be a pointer (although, technically maybe omitempty works here)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

net.IP is a []byte, which can be nil, so omitempty works just fine.

HostPort uint64 `json:"hostPort"`
VMPort uint64 `json:"vmPort"`
Protocol Protocol `json:"protocol,omitempty"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm think that omitempty doesn't work with custom marshallers, but idk. We can test. If so, then we need to use a pointer here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that we're using the standard mashaler and Docker's parser always adding the proto, this isn't an issue.

}

var _ fmt.Stringer = PortMapping{}

func (p PortMapping) String() string {
return fmt.Sprintf("0.0.0.0:%d->%d", p.HostPort, p.VMPort)
var sb strings.Builder

if p.BindAddress != nil {
sb.WriteString(p.BindAddress.String())
} else {
sb.WriteString("0.0.0.0")
}

sb.WriteString(fmt.Sprintf(":%d->%d", p.HostPort, p.VMPort))

if len(p.Protocol) > 0 {
sb.WriteString(fmt.Sprintf("/%s", p.Protocol))
}

return sb.String()
}

// PortMappings represents a list of port mappings
Expand All @@ -27,32 +46,56 @@ var _ fmt.Stringer = PortMappings{}
func ParsePortMappings(input []string) (PortMappings, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm strongly of the opinion that we should not duplicate code here that we already have vendored.
Please use https://github.com/docker/go-connections/blob/master/nat/nat.go#L126 for this task; and turn map[Port][]PortBinding from there into meta.PortMappings.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in a182160.

result := make(PortMappings, 0, len(input))

for _, portMapping := range input {
ports := strings.Split(portMapping, ":")
if len(ports) != 2 {
return nil, fmt.Errorf("port mappings must be of form <host port>:<VM port>")
_, 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))
}

hostPort, err := strconv.ParseUint(ports[0], 10, 64)
if err != nil {
return nil, err
binding := bindings[0]
var err error
var bindAddress net.IP
var hostPort uint64
var vmPort uint64
var protocol Protocol

if len(binding.HostIP) > 0 {
if bindAddress = net.ParseIP(binding.HostIP); bindAddress == nil {
return nil, fmt.Errorf("invalid bind address: %q", binding.HostIP)
}
}

if hostPort, err = strconv.ParseUint(binding.HostPort, 10, 64); err != nil {
return nil, fmt.Errorf("invalid host port: %q", binding.HostPort)
}

if vmPort, err = strconv.ParseUint(port.Port(), 10, 64); err != nil {
return nil, fmt.Errorf("invalid VM port: %q", port.Port())
}

vmPort, err := strconv.ParseUint(ports[1], 10, 64)
if err != nil {
if protocol, err = protocolFromString(port.Proto()); err != nil {
return nil, err
}

mapping := PortMapping{
BindAddress: bindAddress,
HostPort: hostPort,
VMPort: vmPort,
Protocol: protocol,
}

for _, portMapping := range result {
if portMapping.HostPort == hostPort {
return nil, fmt.Errorf("cannot use a port on the host twice")
if portMapping.HostPort == mapping.HostPort && portMapping.Protocol == mapping.Protocol {
return nil, fmt.Errorf("cannot use a port/protocol combination on the host twice")
}
}

result = append(result, PortMapping{
HostPort: hostPort,
VMPort: vmPort,
})
result = append(result, mapping)
}

return result, nil
Expand All @@ -74,6 +117,40 @@ func (p PortMappings) String() string {
return sb.String()
}

// Protocol specifies a network port protocol
type Protocol string

const (
ProtocolTCP Protocol = "tcp"
ProtocolUDP Protocol = "udp"
)

var _ fmt.Stringer = Protocol("")

func protocolFromString(input string) (Protocol, error) {
for _, protocol := range []Protocol{ProtocolTCP, ProtocolUDP} {
if protocol.String() == input {
return protocol, nil
}
}

return "", fmt.Errorf("invalid protocol: %q", input)
}

func (p Protocol) String() string {
return string(p)
}

func (p *Protocol) UnmarshalJSON(b []byte) (err error) {
var s string
if err = json.Unmarshal(b, &s); err != nil {
return err
}

*p, err = protocolFromString(s)
return
}

// IPAddresses represents a list of VM IP addresses
type IPAddresses []net.IP

Expand Down
9 changes: 8 additions & 1 deletion pkg/apis/meta/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions pkg/openapi/openapi_generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 1 addition & 12 deletions pkg/runtime/docker/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +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"
"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
Expand Down Expand Up @@ -103,15 +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 {
portBindings[nat.Port(fmt.Sprintf(portFormat, portMapping.VMPort))] = []nat.PortBinding{
{
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))
Expand Down Expand Up @@ -139,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{
Expand Down
Loading