Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: add port mapping for container #833

Merged
merged 1 commit into from
Mar 8, 2018
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
3 changes: 1 addition & 2 deletions apis/swagger.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1184,8 +1184,7 @@ definitions:
PortBindings:
type: "object"
description: "A map of exposed container ports and the host port they should map to."
additionalProperties:
$ref: "#/definitions/PortBinding"
$ref: "#/definitions/PortMap"
AutoRemove:
type: "boolean"
description: "Automatically remove the container when the container's process exits. This has no effect if `RestartPolicy` is set."
Expand Down
23 changes: 3 additions & 20 deletions apis/types/host_config.go

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

2 changes: 2 additions & 0 deletions cli/common_flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ func addCommonFlags(flagSet *pflag.FlagSet) *container {
flagSet.StringVar(&c.name, "name", "", "Specify name of container")

flagSet.StringSliceVar(&c.networks, "net", nil, "Set networks to container")
flagSet.StringSliceVarP(&c.ports, "port", "p", nil, "Set container ports mapping")
flagSet.StringSliceVar(&c.expose, "expose", nil, "Set expose container's ports")

flagSet.StringVar(&c.pidMode, "pid", "", "PID namespace to use")
flagSet.BoolVar(&c.privileged, "privileged", false, "Give extended privileges to the container")
Expand Down
68 changes: 58 additions & 10 deletions cli/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/alibaba/pouch/apis/types"
"github.com/alibaba/pouch/pkg/runconfig"
"github.com/docker/go-connections/nat"

units "github.com/docker/go-units"
strfmt "github.com/go-openapi/strfmt"
Expand Down Expand Up @@ -45,6 +46,9 @@ type container struct {
utsMode string
sysctls []string
networks []string
ports []string
expose []string
publicAll bool
securityOpt []string
capAdd []string
capDrop []string
Expand Down Expand Up @@ -131,18 +135,61 @@ func (c *container) config() (*types.ContainerCreateConfig, error) {
}
}

// parse port binding
Copy link
Collaborator

@allencloud allencloud Mar 8, 2018

Choose a reason for hiding this comment

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

In the future, we could encapsulate the following code block into a function like parsePortMapping

func parsePortMapping(ports []string)(ports [string]nats, portBindings xxxx)

maybe has the incorrect type, but the similar meaning. In addition, this is much easier to write unit test cases.

tmpPorts, tmpPortBindings, err := nat.ParsePortSpecs(c.ports)
if err != nil {
return nil, err
}
// translate ports and portbingings
ports := map[string]interface{}{}
for n, p := range tmpPorts {
ports[string(n)] = p
}
portBindings := make(types.PortMap)
for n, pbs := range tmpPortBindings {
portBindings[string(n)] = []types.PortBinding{}
for _, tmpPb := range pbs {
pb := types.PortBinding{HostIP: tmpPb.HostIP, HostPort: tmpPb.HostPort}
portBindings[string(n)] = append(portBindings[string(n)], pb)
}
}

for _, e := range c.expose {
if strings.Contains(e, ":") {
return nil, fmt.Errorf("invalid port format for --expose: %s", e)
}

//support two formats for expose, original format <portnum>/[<proto>] or <startport-endport>/[<proto>]
proto, port := nat.SplitProtoPort(e)
//parse the start and end port and create a sequence of ports to expose
//if expose a port, the start and end port are the same
start, end, err := nat.ParsePortRange(port)
if err != nil {
return nil, fmt.Errorf("invalid range format for --expose: %s, error: %s", e, err)
}
for i := start; i <= end; i++ {
p, err := nat.NewPort(proto, strconv.FormatUint(i, 10))
if err != nil {
return nil, err
}
if _, exists := ports[string(p)]; !exists {
ports[string(p)] = struct{}{}
}
}
}
config := &types.ContainerCreateConfig{
ContainerConfig: types.ContainerConfig{
Tty: c.tty,
Env: c.env,
Entrypoint: strings.Fields(c.entrypoint),
WorkingDir: c.workdir,
User: c.user,
Hostname: strfmt.Hostname(c.hostname),
Labels: labels,
Rich: c.rich,
RichMode: c.richMode,
InitScript: c.initScript,
Tty: c.tty,
Env: c.env,
Entrypoint: strings.Fields(c.entrypoint),
WorkingDir: c.workdir,
User: c.user,
Hostname: strfmt.Hostname(c.hostname),
Labels: labels,
Rich: c.rich,
RichMode: c.richMode,
InitScript: c.initScript,
ExposedPorts: ports,
},

HostConfig: &types.HostConfig{
Expand Down Expand Up @@ -182,6 +229,7 @@ func (c *container) config() (*types.ContainerCreateConfig, error) {
NetworkMode: networkMode,
CapAdd: c.capAdd,
CapDrop: c.capDrop,
PortBindings: portBindings,
},

NetworkingConfig: networkingConfig,
Expand Down
1 change: 1 addition & 0 deletions daemon/mgr/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -1019,6 +1019,7 @@ func (mgr *ContainerManager) buildContainerEndpoint(c *ContainerMeta) *networkty
DNSOptions: c.HostConfig.DNSOptions,
DNSSearch: c.HostConfig.DNSSearch,
MacAddress: c.Config.MacAddress,
PublishAllPorts: c.HostConfig.PublishAllPorts,
ExposedPorts: c.Config.ExposedPorts,
PortBindings: c.HostConfig.PortBindings,
NetworkConfig: c.NetworkSettings,
Expand Down
60 changes: 60 additions & 0 deletions daemon/mgr/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/alibaba/pouch/pkg/randomid"

netlog "github.com/Sirupsen/logrus"
"github.com/docker/go-connections/nat"
"github.com/docker/libnetwork"
nwconfig "github.com/docker/libnetwork/config"
"github.com/docker/libnetwork/netlabel"
Expand Down Expand Up @@ -508,6 +509,65 @@ func (nm *NetworkManager) sandboxOptions(endpoint *types.Endpoint) ([]libnetwork
// TODO: secondary ip address
// TODO: parse extra hosts
// TODO: port mapping
var bindings = make(nat.PortMap)
if endpoint.PortBindings != nil {
for p, b := range endpoint.PortBindings {
bindings[nat.Port(p)] = []nat.PortBinding{}
for _, bb := range b {
bindings[nat.Port(p)] = append(bindings[nat.Port(p)], nat.PortBinding{
HostIP: bb.HostIP,
HostPort: bb.HostPort,
})
}
}
}

portSpecs := endpoint.ExposedPorts
var ports = make([]nat.Port, len(portSpecs))
var i int
for p := range endpoint.ExposedPorts {
ports[i] = nat.Port(p)
i++
}
nat.SortPortMap(ports, bindings)

var (
exposeList []networktypes.TransportPort
pbList []networktypes.PortBinding
)
for _, port := range ports {
expose := networktypes.TransportPort{}
expose.Proto = networktypes.ParseProtocol(port.Proto())
expose.Port = uint16(port.Int())
exposeList = append(exposeList, expose)

pb := networktypes.PortBinding{Port: expose.Port, Proto: expose.Proto}
binding := bindings[port]
for i := 0; i < len(binding); i++ {
pbCopy := pb.GetCopy()
newP, err := nat.NewPort(nat.SplitProtoPort(binding[i].HostPort))
var portStart, portEnd int
if err == nil {
portStart, portEnd, err = newP.Range()
}
if err != nil {
return nil, fmt.Errorf("failed to parsing HostPort value(%s):%v", binding[i].HostPort, err)
}
pbCopy.HostPort = uint16(portStart)
pbCopy.HostPortEnd = uint16(portEnd)
pbCopy.HostIP = net.ParseIP(binding[i].HostIP)
pbList = append(pbList, pbCopy)
}

if endpoint.PublishAllPorts && len(binding) == 0 {
pbList = append(pbList, pb)
}
}

sandboxOptions = append(sandboxOptions,
libnetwork.OptionPortMapping(pbList),
libnetwork.OptionExposedPorts(exposeList))

return sandboxOptions, nil
}

Expand Down
3 changes: 2 additions & 1 deletion network/types/endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ type Endpoint struct {
NetworkDisabled bool
NetworkMode string
MacAddress string
PublishAllPorts bool
ExposedPorts map[string]interface{}
PortBindings map[string]types.PortBinding
PortBindings types.PortMap

NetworkConfig *types.NetworkSettings
EndpointConfig *types.EndpointSettings
Expand Down
34 changes: 34 additions & 0 deletions test/cli_network_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"runtime"
"strings"
"time"

"github.com/alibaba/pouch/test/command"
"github.com/alibaba/pouch/test/environment"
Expand Down Expand Up @@ -283,3 +284,36 @@ func (suite *PouchNetworkSuite) TestNetworkCreateDup(c *check.C) {

command.PouchRun("network", "remove", funcname)
}

func (suite *PouchNetworkSuite) TestNetworkPortMapping(c *check.C) {
pc, _, _, _ := runtime.Caller(0)
tmpname := strings.Split(runtime.FuncForPC(pc).Name(), ".")
var funcname string
for i := range tmpname {
funcname = tmpname[i]
}

ret := icmd.RunCommand("which", "curl")
if ret.ExitCode != 0 {
c.Skip("Host does not have curl")
}

expct := icmd.Expected{
ExitCode: 0,
Out: "It works",
}

image := "registry.hub.docker.com/library/httpd"

command.PouchRun("pull", image).Assert(c, icmd.Success)
command.PouchRun("run", "-d",
"--name", funcname,
"-p", "9999:80",
image).Assert(c, icmd.Success)

time.Sleep(1 * time.Second)
err := icmd.RunCommand("curl", "localhost:9999").Compare(expct)
c.Assert(err, check.IsNil)

command.PouchRun("rm", "-f", funcname)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

good test!

Loading