diff options
author | baude <bbaude@redhat.com> | 2018-01-04 12:59:33 -0600 |
---|---|---|
committer | Atomic Bot <atomic-devel@projectatomic.io> | 2018-01-20 18:51:21 +0000 |
commit | 946b4ced544e5988a971da12c7e34a684ab0e39d (patch) | |
tree | 026fa6619b6b98a9cf08ec66b8cd6dd27a714736 | |
parent | 67f06cf1cfda17387bd094f671672c6b51b2c5cd (diff) | |
download | podman-946b4ced544e5988a971da12c7e34a684ab0e39d.tar.gz podman-946b4ced544e5988a971da12c7e34a684ab0e39d.tar.bz2 podman-946b4ced544e5988a971da12c7e34a684ab0e39d.zip |
Enable port bindings
Set up nbetworking ports for the following use cases:
* bind the same port between host and container
* bind a specific host port to a different container port
* bind a random host port to a specific container port
Signed-off-by: baude <bbaude@redhat.com>
Closes: #214
Approved by: baude
-rw-r--r-- | .papr.yml | 5 | ||||
-rw-r--r-- | Dockerfile | 2 | ||||
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | cmd/podman/create.go | 48 | ||||
-rw-r--r-- | cmd/podman/spec.go | 57 | ||||
-rw-r--r-- | cni/87-podman-bridge.conflist | 25 | ||||
-rw-r--r-- | cni/97-podman-bridge.conf | 15 | ||||
-rw-r--r-- | libpod/networking.go | 1 | ||||
-rw-r--r-- | libpod/options.go | 2 | ||||
-rw-r--r-- | test/podman_networking.bats | 21 |
10 files changed, 153 insertions, 25 deletions
@@ -16,6 +16,11 @@ tests: packages: - containernetworking-cni + +extra-repos: + - name: updatestesting + baseurl: http://download.fedoraproject.org/pub/fedora/linux/updates/testing/27/x86_64/ + --- inherit: true diff --git a/Dockerfile b/Dockerfile index 6764bdf1a..b562ddd1f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -68,7 +68,7 @@ RUN set -x \ && rm -rf "$GOPATH" # Install CNI plugins -ENV CNI_COMMIT dcf7368eeab15e2affc6256f0bb1e84dd46a34de +ENV CNI_COMMIT 7480240de9749f9a0a5c8614b17f1f03e0c06ab9 RUN set -x \ && export GOPATH="$(mktemp -d)" \ && git clone https://github.com/containernetworking/plugins.git "$GOPATH/src/github.com/containernetworking/plugins" \ @@ -157,7 +157,7 @@ install.completions: install ${SELINUXOPT} -m 644 -D completions/bash/podman ${BASHINSTALLDIR} install.cni: - install ${SELINUXOPT} -D -m 644 cni/97-podman-bridge.conf ${ETCDIR}/cni/net.d/97-podman-bridge.conf + install ${SELINUXOPT} -D -m 644 cni/87-podman-bridge.conflist ${ETCDIR}/cni/net.d/87-podman-bridge.conflist install.docker: docker-docs install ${SELINUXOPT} -D -m 755 docker $(BINDIR)/docker diff --git a/cmd/podman/create.go b/cmd/podman/create.go index 55425638f..28bd0a60e 100644 --- a/cmd/podman/create.go +++ b/cmd/podman/create.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "io" + "net" "os" "strconv" "strings" @@ -287,15 +288,25 @@ func parseSecurityOpt(config *createConfig, securityOpts []string) error { return err } +// isPortInPortBindings determines if an exposed host port is in user +// provided ports +func isPortInPortBindings(pb map[nat.Port][]nat.PortBinding, port nat.Port) bool { + var hostPorts []string + for _, i := range pb { + hostPorts = append(hostPorts, i[0].HostPort) + } + return libpod.StringInSlice(port.Port(), hostPorts) +} + func exposedPorts(c *cli.Context, imageExposedPorts map[string]struct{}) (map[nat.Port]struct{}, map[nat.Port][]nat.PortBinding, error) { // TODO Handle exposed ports from image // Currently ignoring imageExposedPorts - - ports, portBindings, err := nat.ParsePortSpecs(c.StringSlice("publish")) + var ports map[nat.Port]struct{} + ports = make(map[nat.Port]struct{}) + _, portBindings, err := nat.ParsePortSpecs(c.StringSlice("publish")) if err != nil { return nil, nil, err } - for _, e := range c.StringSlice("expose") { // Merge in exposed ports to the map of published ports if strings.Contains(e, ":") { @@ -314,6 +325,28 @@ func exposedPorts(c *cli.Context, imageExposedPorts map[string]struct{}) (map[na if err != nil { return nil, nil, err } + // check if the port in question is already being used + if isPortInPortBindings(portBindings, p) { + return nil, nil, errors.Errorf("host port %s already used in --publish option", p.Port()) + } + + if c.Bool("publish-all") { + l, err := net.Listen("tcp", ":0") + if err != nil { + return nil, nil, errors.Wrapf(err, "unable to get free port") + } + _, randomPort, err := net.SplitHostPort(l.Addr().String()) + if err != nil { + return nil, nil, errors.Wrapf(err, "unable to determine free port") + } + rp, err := strconv.Atoi(randomPort) + if err != nil { + return nil, nil, errors.Wrapf(err, "unable to convert random port to int") + } + logrus.Debug(fmt.Sprintf("Using random host port %s with container port %d", randomPort, p.Int())) + portBindings[p] = CreatePortBinding(rp, "") + continue + } if _, exists := ports[p]; !exists { ports[p] = struct{}{} } @@ -669,3 +702,12 @@ func parseCreateOpts(c *cli.Context, runtime *libpod.Runtime, imageName string, } return config, nil } + +//CreatePortBinding takes port (int) and IP (string) and creates an array of portbinding structs +func CreatePortBinding(hostPort int, hostIP string) []nat.PortBinding { + pb := nat.PortBinding{ + HostPort: strconv.Itoa(hostPort), + } + pb.HostIP = hostIP + return []nat.PortBinding{pb} +} diff --git a/cmd/podman/spec.go b/cmd/podman/spec.go index 152d1740c..cb9efdcb2 100644 --- a/cmd/podman/spec.go +++ b/cmd/podman/spec.go @@ -2,6 +2,7 @@ package main import ( "io/ioutil" + "strconv" "strings" "github.com/cri-o/ocicni/pkg/ocicni" @@ -543,6 +544,8 @@ func (c *createConfig) GetTmpfsMounts() []spec.Mount { func (c *createConfig) GetContainerCreateOptions() ([]libpod.CtrCreateOption, error) { var options []libpod.CtrCreateOption + var portBindings []ocicni.PortMapping + var err error // Uncomment after talking to mheon about unimplemented funcs // options = append(options, libpod.WithLabels(c.labels)) @@ -554,17 +557,25 @@ func (c *createConfig) GetContainerCreateOptions() ([]libpod.CtrCreateOption, er logrus.Debugf("appending name %s", c.Name) options = append(options, libpod.WithName(c.Name)) } - // TODO parse ports into libpod format and include + + // TODO deal with ports defined in image metadata + if len(c.PortBindings) > 0 || len(c.ExposedPorts) > 0 { + portBindings, err = c.CreatePortBindings() + if err != nil { + return nil, errors.Wrapf(err, "unable to create port bindings") + } + } + if c.NetMode.IsContainer() { connectedCtr, err := c.Runtime.LookupContainer(c.NetMode.ConnectedContainer()) if err != nil { return nil, errors.Wrapf(err, "container %q not found", c.NetMode.ConnectedContainer()) } - options = append(options, libpod.WithNetNSFrom(connectedCtr)) } else if !c.NetMode.IsHost() { - options = append(options, libpod.WithNetNS([]ocicni.PortMapping{})) + options = append(options, libpod.WithNetNS(portBindings)) } + if c.PidMode.IsContainer() { connectedCtr, err := c.Runtime.LookupContainer(c.PidMode.Container()) if err != nil { @@ -622,3 +633,43 @@ func makeThrottleArray(throttleInput []string) ([]spec.LinuxThrottleDevice, erro } return ltds, nil } + +// CreatePortBindings iterates ports mappings and exposed ports into a format CNI understands +func (c *createConfig) CreatePortBindings() ([]ocicni.PortMapping, error) { + var portBindings []ocicni.PortMapping + for containerPb, hostPb := range c.PortBindings { + var pm ocicni.PortMapping + pm.ContainerPort = int32(containerPb.Int()) + for _, i := range hostPb { + var hostPort int + var err error + pm.HostIP = i.HostIP + if i.HostPort == "" { + hostPort = containerPb.Int() + } else { + hostPort, err = strconv.Atoi(i.HostPort) + if err != nil { + return nil, errors.Wrapf(err, "unable to convert host port to integer") + } + } + + pm.HostPort = int32(hostPort) + // CNI requires us to make both udp and tcp structs + pm.Protocol = "udp" + portBindings = append(portBindings, pm) + pm.Protocol = "tcp" + portBindings = append(portBindings, pm) + } + } + for j := range c.ExposedPorts { + var expose ocicni.PortMapping + expose.HostPort = int32(j.Int()) + expose.ContainerPort = int32(j.Int()) + // CNI requires us to make both udp and tcp structs + expose.Protocol = "udp" + portBindings = append(portBindings, expose) + expose.Protocol = "tcp" + portBindings = append(portBindings, expose) + } + return portBindings, nil +} diff --git a/cni/87-podman-bridge.conflist b/cni/87-podman-bridge.conflist new file mode 100644 index 000000000..a5e241c80 --- /dev/null +++ b/cni/87-podman-bridge.conflist @@ -0,0 +1,25 @@ +{ + "cniVersion": "0.3.0", + "name": "podman", + "plugins": [ + { + "type": "bridge", + "bridge": "cni0", + "isGateway": true, + "ipMasq": true, + "ipam": { + "type": "host-local", + "subnet": "10.88.0.0/16", + "routes": [ + { "dst": "0.0.0.0/0" } + ] + } + }, + { + "type": "portmap", + "capabilities": { + "portMappings": true + } + } + ] +} diff --git a/cni/97-podman-bridge.conf b/cni/97-podman-bridge.conf deleted file mode 100644 index 27fc096c4..000000000 --- a/cni/97-podman-bridge.conf +++ /dev/null @@ -1,15 +0,0 @@ -{ - "cniVersion": "0.3.0", - "name": "podman", - "type": "bridge", - "bridge": "cni0", - "isGateway": true, - "ipMasq": true, - "ipam": { - "type": "host-local", - "subnet": "10.88.0.0/16", - "routes": [ - { "dst": "0.0.0.0/0" } - ] - } -} diff --git a/libpod/networking.go b/libpod/networking.go index 41bd65d25..40756cf88 100644 --- a/libpod/networking.go +++ b/libpod/networking.go @@ -35,7 +35,6 @@ func (r *Runtime) createNetNS(ctr *Container) (err error) { }() logrus.Debugf("Made network namespace at %s for container %s", ctrNS.Path(), ctr.ID()) - podNetwork := getPodNetwork(ctr.ID(), ctr.Name(), ctrNS.Path(), ctr.config.PortMappings) _, err = r.netPlugin.SetUpPod(podNetwork) diff --git a/libpod/options.go b/libpod/options.go index 63a72628f..f82cb20c4 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -584,7 +584,7 @@ func WithNetNS(portMappings []ocicni.PortMapping) CtrCreateOption { } ctr.config.CreateNetNS = true - copy(ctr.config.PortMappings, portMappings) + ctr.config.PortMappings = portMappings return nil } diff --git a/test/podman_networking.bats b/test/podman_networking.bats index ba30a9897..b2196b4bc 100644 --- a/test/podman_networking.bats +++ b/test/podman_networking.bats @@ -27,3 +27,24 @@ function setup() { echo "$output" [ "$status" -eq 0 ] } + +@test "expose port 222" { + run ${PODMAN_BINARY} ${PODMAN_OPTIONS} run -dt --expose 222-223 ${ALPINE} /bin/sh + echo "$output" + [ "$status" -eq 0 ] + run bash -c "iptables -t nat -L" + echo "$output" + [ "$status" -eq 0 ] + run bash -c "iptables -t nat -L | grep 223" + echo "$output" + [ "$status" -eq 0 ] +} + +@test "expose host port 80 to container port 8000" { + run ${PODMAN_BINARY} ${PODMAN_OPTIONS} run -dt -p 80:8000 ${ALPINE} /bin/sh + echo "$output" + [ "$status" -eq 0 ] + run bash -c "iptables -t nat -L | grep 8000" + echo "$output" + [ "$status" -eq 0 ] +} |