mirror of
https://github.com/containers/podman.git
synced 2025-05-17 23:26:08 +08:00
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
This commit is contained in:
@ -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
|
||||
|
@ -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" \
|
||||
|
2
Makefile
2
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
|
||||
|
@ -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}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
25
cni/87-podman-bridge.conflist
Normal file
25
cni/87-podman-bridge.conflist
Normal file
@ -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
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
@ -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" }
|
||||
]
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 ]
|
||||
}
|
||||
|
Reference in New Issue
Block a user