Thanks to visit codestin.com
Credit goes to github.com

Skip to content
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
20 changes: 8 additions & 12 deletions daemon/networkdriver/bridge/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"io/ioutil"
"net"
"os"
"strings"
"strconv"
"sync"

log "github.com/Sirupsen/logrus"
Expand All @@ -14,6 +14,7 @@ import (
"github.com/docker/docker/daemon/networkdriver/portallocator"
"github.com/docker/docker/daemon/networkdriver/portmapper"
"github.com/docker/docker/engine"
"github.com/docker/docker/nat"
"github.com/docker/docker/pkg/iptables"
"github.com/docker/docker/pkg/networkfs/resolvconf"
"github.com/docker/docker/pkg/parsers/kernel"
Expand Down Expand Up @@ -515,18 +516,13 @@ func LinkContainers(job *engine.Job) engine.Status {
ignoreErrors = job.GetenvBool("IgnoreErrors")
ports = job.GetenvList("Ports")
)
split := func(p string) (string, string) {
parts := strings.Split(p, "/")
return parts[0], parts[1]
}

for _, p := range ports {
port, proto := split(p)
for _, value := range ports {
port := nat.Port(value)
if output, err := iptables.Raw(action, "FORWARD",
"-i", bridgeIface, "-o", bridgeIface,
"-p", proto,
"-p", port.Proto(),
"-s", parentIP,
"--dport", port,
"--dport", strconv.Itoa(port.Int()),
"-d", childIP,
"-j", "ACCEPT"); !ignoreErrors && err != nil {
return job.Error(err)
Expand All @@ -536,9 +532,9 @@ func LinkContainers(job *engine.Job) engine.Status {

if output, err := iptables.Raw(action, "FORWARD",
"-i", bridgeIface, "-o", bridgeIface,
"-p", proto,
"-p", port.Proto(),
"-s", childIP,
"--sport", port,
"--sport", strconv.Itoa(port.Int()),
"-d", parentIP,
"-j", "ACCEPT"); !ignoreErrors && err != nil {
return job.Error(err)
Expand Down
2 changes: 1 addition & 1 deletion docs/man/docker-create.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ docker-create - Create a new container
Read in a line delimited file of environment variables

**--expose**=[]
Expose a port from the container without publishing it to your host
Expose a port or a range of ports (e.g. --expose=3300-3310) from the container without publishing it to your host

**-h**, **--hostname**=""
Container host name
Expand Down
8 changes: 2 additions & 6 deletions docs/man/docker-run.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,12 +132,8 @@ ENTRYPOINT.
**--env-file**=[]
Read in a line delimited file of environment variables

**--expose**=*port*
Expose a port from the container without publishing it to your host. A
containers port can be exposed to other containers in three ways: 1) The
developer can expose the port using the EXPOSE parameter of the Dockerfile, 2)
the operator can use the **--expose** option with **docker run**, or 3) the
container can be started with the **--link**.
**--expose**=[]
Expose a port or a range of ports (e.g. --expose=3300-3310) from the container without publishing it to your host

**-h**, **--hostname**=*hostname*
Sets the container host name that is available inside the container.
Expand Down
4 changes: 2 additions & 2 deletions docs/sources/reference/commandline/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,7 @@ Creates a new container.
-e, --env=[] Set environment variables
--entrypoint="" Overwrite the default ENTRYPOINT of the image
--env-file=[] Read in a line delimited file of environment variables
--expose=[] Expose a port from the container without publishing it to your host
--expose=[] Expose a port or a range of ports (e.g. --expose=3300-3310) from the container without publishing it to your host
-h, --hostname="" Container host name
-i, --interactive=false Keep STDIN open even if not attached
--link=[] Add link to another container in the form of name:alias
Expand Down Expand Up @@ -1211,7 +1211,7 @@ removed before the image is removed.
-e, --env=[] Set environment variables
--entrypoint="" Overwrite the default ENTRYPOINT of the image
--env-file=[] Read in a line delimited file of environment variables
--expose=[] Expose a port from the container without publishing it to your host
--expose=[] Expose a port or a range of ports (e.g. --expose=3300-3310) from the container without publishing it to your host
-h, --hostname="" Container host name
-i, --interactive=false Keep STDIN open even if not attached
--link=[] Add link to another container in the form of name:alias
Expand Down
4 changes: 2 additions & 2 deletions docs/sources/reference/run.md
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ the `EXPOSE` instruction to give a hint to the operator about what
incoming ports might provide services. The following options work with
or override the Dockerfile's exposed defaults:

--expose=[]: Expose a port from the container
--expose=[]: Expose a port or a range of ports from the container
without publishing it to your host
-P=false : Publish all exposed ports to the host interfaces
-p=[] : Publish a container᾿s port to the host (format:
Expand All @@ -422,7 +422,7 @@ or override the Dockerfile's exposed defaults:
(use 'docker port' to see the actual mapping)
--link="" : Add link to another container (name:alias)

As mentioned previously, `EXPOSE` (and `--expose`) make a port available
As mentioned previously, `EXPOSE` (and `--expose`) makes ports available

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I know you didn't make this wording, but maybe we can change this now. ping @SvenDowideit
I think this whole paragraph is not right. Technically expose doesn't make any ports available inside the container.

advertises ports which the process(es) **in** the container is listening on for incoming connections for the purposes of
service discovery.
In cases where **inter-container communication** is disabled, this will allow **linked** containers to communicate
over these ports and these ports only.

Or something like that.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@SvenDowideit waiting for confirmation on the text.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I agree with @cpuguy83 - expose indicates to docker that there is something that it can 'link' - I find it a bit confused because I can set up docker run -p 1234:2134 on non-exposed ports too.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I wouldn't hold up this PR for that tho

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I rebased it, code should be ready for merge

**in** a container for incoming connections. The port number on the
inside of the container (where the service listens) does not need to be
the same number as the port exposed on the outside of the container
Expand Down
30 changes: 30 additions & 0 deletions integration-cli/docker_cli_run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ import (
"reflect"
"regexp"
"sort"
"strconv"
"strings"
"sync"
"testing"
"time"

"github.com/docker/docker/nat"
"github.com/docker/docker/pkg/mount"
"github.com/docker/docker/pkg/networkfs/resolvconf"
"github.com/kr/pty"
Expand Down Expand Up @@ -2473,3 +2475,31 @@ func TestRunSlowStdoutConsumer(t *testing.T) {

logDone("run - slow consumer")
}

func TestRunAllowPortRangeThroughExpose(t *testing.T) {
cmd := exec.Command(dockerBinary, "run", "-d", "--expose", "3000-3003", "-P", "busybox", "top")
out, _, err := runCommandWithOutput(cmd)
if err != nil {
t.Fatal(err)
}
id := strings.TrimSpace(out)
portstr, err := inspectFieldJSON(id, "NetworkSettings.Ports")
if err != nil {
t.Fatal(err)
}
var ports nat.PortMap
err = unmarshalJSON([]byte(portstr), &ports)
for port, binding := range ports {
portnum, _ := strconv.Atoi(strings.Split(string(port), "/")[0])
if portnum < 3000 || portnum > 3003 {
t.Fatalf("Port is out of range ", portnum, binding, out)
}
if binding == nil || len(binding) != 1 || len(binding[0].HostPort) == 0 {
t.Fatal("Port is not mapped for the port "+port, out)
}
}
if err := deleteContainer(id); err != nil {
t.Fatal(err)
}
logDone("run - allow port range through --expose flag")
}
43 changes: 40 additions & 3 deletions links/links.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,20 @@ func (l *Link) Alias() string {
return alias
}

func nextContiguous(ports []nat.Port, value int, index int) int {
if index+1 == len(ports) {
return index
}
for i := index + 1; i < len(ports); i++ {
if ports[i].Int() > value+1 {
return i - 1
}

value++
}
return len(ports) - 1
}

func (l *Link) ToEnv() []string {
env := []string{}
alias := strings.Replace(strings.ToUpper(l.Alias()), "-", "_", -1)
Expand All @@ -55,12 +69,35 @@ func (l *Link) ToEnv() []string {
env = append(env, fmt.Sprintf("%s_PORT=%s://%s:%s", alias, p.Proto(), l.ChildIP, p.Port()))
}

// Load exposed ports into the environment
for _, p := range l.Ports {
//sort the ports so that we can bulk the continuous ports together
nat.Sort(l.Ports, func(ip, jp nat.Port) bool {
// If the two ports have the same number, tcp takes priority
// Sort in desc order
return ip.Int() < jp.Int() || (ip.Int() == jp.Int() && strings.ToLower(ip.Proto()) == "tcp")
})

for i := 0; i < len(l.Ports); {
p := l.Ports[i]
j := nextContiguous(l.Ports, p.Int(), i)
if j > i+1 {
env = append(env, fmt.Sprintf("%s_PORT_%s_%s_START=%s://%s:%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Proto(), l.ChildIP, p.Port()))
env = append(env, fmt.Sprintf("%s_PORT_%s_%s_ADDR=%s", alias, p.Port(), strings.ToUpper(p.Proto()), l.ChildIP))
env = append(env, fmt.Sprintf("%s_PORT_%s_%s_PROTO=%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Proto()))
env = append(env, fmt.Sprintf("%s_PORT_%s_%s_PORT_START=%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Port()))

q := l.Ports[j]
env = append(env, fmt.Sprintf("%s_PORT_%s_%s_END=%s://%s:%s", alias, p.Port(), strings.ToUpper(q.Proto()), q.Proto(), l.ChildIP, q.Port()))
env = append(env, fmt.Sprintf("%s_PORT_%s_%s_PORT_END=%s", alias, p.Port(), strings.ToUpper(q.Proto()), q.Port()))

i = j + 1
continue
}

env = append(env, fmt.Sprintf("%s_PORT_%s_%s=%s://%s:%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Proto(), l.ChildIP, p.Port()))
env = append(env, fmt.Sprintf("%s_PORT_%s_%s_ADDR=%s", alias, p.Port(), strings.ToUpper(p.Proto()), l.ChildIP))
env = append(env, fmt.Sprintf("%s_PORT_%s_%s_PORT=%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Port()))
env = append(env, fmt.Sprintf("%s_PORT_%s_%s_PROTO=%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Proto()))
i++
}

// Load the linked container's name into the environment
Expand Down Expand Up @@ -125,7 +162,7 @@ func (l *Link) toggle(action string, ignoreErrors bool) error {

out := make([]string, len(l.Ports))
for i, p := range l.Ports {
out[i] = fmt.Sprintf("%s/%s", p.Port(), p.Proto())
out[i] = string(p)
}
job.SetenvList("Ports", out)

Expand Down
49 changes: 49 additions & 0 deletions links/links_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,52 @@ func TestLinkEnv(t *testing.T) {
t.Fatalf("Expected gordon, got %s", env["DOCKER_ENV_PASSWORD"])
}
}

func TestLinkMultipleEnv(t *testing.T) {
ports := make(nat.PortSet)
ports[nat.Port("6379/tcp")] = struct{}{}
ports[nat.Port("6380/tcp")] = struct{}{}
ports[nat.Port("6381/tcp")] = struct{}{}

link, err := NewLink("172.0.17.3", "172.0.17.2", "/db/docker", []string{"PASSWORD=gordon"}, ports, nil)
if err != nil {
t.Fatal(err)
}

rawEnv := link.ToEnv()
env := make(map[string]string, len(rawEnv))
for _, e := range rawEnv {
parts := strings.Split(e, "=")
if len(parts) != 2 {
t.FailNow()
}
env[parts[0]] = parts[1]
}
if env["DOCKER_PORT"] != "tcp://172.0.17.2:6379" {
t.Fatalf("Expected 172.0.17.2:6379, got %s", env["DOCKER_PORT"])
}
if env["DOCKER_PORT_6379_TCP_START"] != "tcp://172.0.17.2:6379" {
t.Fatalf("Expected tcp://172.0.17.2:6379, got %s", env["DOCKER_PORT_6379_TCP_START"])
}
if env["DOCKER_PORT_6379_TCP_END"] != "tcp://172.0.17.2:6381" {
t.Fatalf("Expected tcp://172.0.17.2:6381, got %s", env["DOCKER_PORT_6379_TCP_END"])
}
if env["DOCKER_PORT_6379_TCP_PROTO"] != "tcp" {
t.Fatalf("Expected tcp, got %s", env["DOCKER_PORT_6379_TCP_PROTO"])
}
if env["DOCKER_PORT_6379_TCP_ADDR"] != "172.0.17.2" {
t.Fatalf("Expected 172.0.17.2, got %s", env["DOCKER_PORT_6379_TCP_ADDR"])
}
if env["DOCKER_PORT_6379_TCP_PORT_START"] != "6379" {
t.Fatalf("Expected 6379, got %s", env["DOCKER_PORT_6379_TCP_PORT_START"])
}
if env["DOCKER_PORT_6379_TCP_PORT_END"] != "6381" {
t.Fatalf("Expected 6381, got %s", env["DOCKER_PORT_6379_TCP_PORT_END"])
}
if env["DOCKER_NAME"] != "/db/docker" {
t.Fatalf("Expected /db/docker, got %s", env["DOCKER_NAME"])
}
if env["DOCKER_ENV_PASSWORD"] != "gordon" {
t.Fatalf("Expected gordon, got %s", env["DOCKER_ENV_PASSWORD"])
}
}
39 changes: 16 additions & 23 deletions nat/nat.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,44 +42,37 @@ func ParsePort(rawPort string) (int, error) {
}

func (p Port) Proto() string {
parts := strings.Split(string(p), "/")
if len(parts) == 1 {
return "tcp"
}
return parts[1]
proto, _ := SplitProtoPort(string(p))
return proto
}

func (p Port) Port() string {
return strings.Split(string(p), "/")[0]
_, port := SplitProtoPort(string(p))
return port
}

func (p Port) Int() int {
i, err := ParsePort(p.Port())
port, err := ParsePort(p.Port())
if err != nil {
panic(err)
}
return i
return port
}

// Splits a port in the format of proto/port
func SplitProtoPort(rawPort string) (string, string) {
var port string
var proto string

parts := strings.Split(rawPort, "/")

if len(parts) == 0 || parts[0] == "" { // we have "" or ""/
port = ""
proto = ""
} else { // we have # or #/ or #/...
port = parts[0]
if len(parts) > 1 && parts[1] != "" {
proto = parts[1] // we have #/...
} else {
proto = "tcp" // we have # or #/
}
l := len(parts)
if len(rawPort) == 0 || l == 0 || len(parts[0]) == 0 {
return "", ""
}
if l == 1 {
return "tcp", rawPort
}
if len(parts[1]) == 0 {
return "tcp", parts[0]
}
return proto, port
return parts[1], parts[0]
}

func validateProto(proto string) bool {
Expand Down
4 changes: 2 additions & 2 deletions nat/nat_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,13 @@ func TestSplitProtoPort(t *testing.T) {
proto, port = SplitProtoPort("")

if proto != "" || port != "" {
t.Fatal("parsing an empty string yielded surprising results")
t.Fatal("parsing an empty string yielded surprising results", proto, port)
}

proto, port = SplitProtoPort("1234")

if proto != "tcp" || port != "1234" {
t.Fatal("tcp is not the default protocol for portspec '1234'")
t.Fatal("tcp is not the default protocol for portspec '1234'", proto, port)
}

proto, port = SplitProtoPort("1234/")
Expand Down
23 changes: 19 additions & 4 deletions runconfig/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func Parse(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Config,
cmd.Var(&flEnvFile, []string{"-env-file"}, "Read in a line delimited file of environment variables")

cmd.Var(&flPublish, []string{"p", "-publish"}, fmt.Sprintf("Publish a container's port to the host\nformat: %s\n(use 'docker port' to see the actual mapping)", nat.PortSpecTemplateFormat))
cmd.Var(&flExpose, []string{"#expose", "-expose"}, "Expose a port from the container without publishing it to your host")
cmd.Var(&flExpose, []string{"#expose", "-expose"}, "Expose a port or a range of ports (e.g. --expose=3300-3310) from the container without publishing it to your host")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is the text that should be used in docker-run.1.md, cli.md and run.md - as the text written should look identical to what the user sees when they type docker run ---help.

I have a script that will update it for you (though you need to ignore some of its changes) - docs/docs-update.py ./bundles/1.2.0-dev/binary/docker - this will use your modified docker binary to update the documentation. :)

cmd.Var(&flDns, []string{"#dns", "-dns"}, "Set custom DNS servers")
cmd.Var(&flDnsSearch, []string{"-dns-search"}, "Set custom DNS search domains (Use --dns-search=. if you don't wish to set the search domain)")
cmd.Var(&flExtraHosts, []string{"-add-host"}, "Add a custom host-to-IP mapping (host:ip)")
Expand Down Expand Up @@ -197,9 +197,24 @@ func Parse(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Config,
if strings.Contains(e, ":") {
return nil, nil, cmd, fmt.Errorf("Invalid port format for --expose: %s", e)
}
p := nat.NewPort(nat.SplitProtoPort(e))
if _, exists := ports[p]; !exists {
ports[p] = struct{}{}
//support two formats for expose, original format <portnum>/[<proto>] or <startport-endport>/[<proto>]
if strings.Contains(e, "-") {
proto, port := nat.SplitProtoPort(e)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Seems like this parsing should go into a separate function?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

IMHO, at this point this code is not used by anyone else, if I do support -p option I would prefer to factor. What do you think?

//parse the start and end port and create a sequence of ports to expose
parts := strings.Split(port, "-")
start, _ := strconv.Atoi(parts[0])
end, _ := strconv.Atoi(parts[1])
for i := start; i <= end; i++ {
p := nat.NewPort(proto, strconv.Itoa(i))
if _, exists := ports[p]; !exists {
ports[p] = struct{}{}
}
}
} else {
p := nat.NewPort(nat.SplitProtoPort(e))
if _, exists := ports[p]; !exists {
ports[p] = struct{}{}
}
}
}

Expand Down