summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbaude <bbaude@redhat.com>2018-01-04 12:59:33 -0600
committerAtomic Bot <atomic-devel@projectatomic.io>2018-01-20 18:51:21 +0000
commit946b4ced544e5988a971da12c7e34a684ab0e39d (patch)
tree026fa6619b6b98a9cf08ec66b8cd6dd27a714736
parent67f06cf1cfda17387bd094f671672c6b51b2c5cd (diff)
downloadpodman-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.yml5
-rw-r--r--Dockerfile2
-rw-r--r--Makefile2
-rw-r--r--cmd/podman/create.go48
-rw-r--r--cmd/podman/spec.go57
-rw-r--r--cni/87-podman-bridge.conflist25
-rw-r--r--cni/97-podman-bridge.conf15
-rw-r--r--libpod/networking.go1
-rw-r--r--libpod/options.go2
-rw-r--r--test/podman_networking.bats21
10 files changed, 153 insertions, 25 deletions
diff --git a/.papr.yml b/.papr.yml
index 6ca7e7152..b82f7df61 100644
--- a/.papr.yml
+++ b/.papr.yml
@@ -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" \
diff --git a/Makefile b/Makefile
index 40ed074a6..cee8257d6 100644
--- a/Makefile
+++ b/Makefile
@@ -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 ]
+}