Repository: deis/deis

Issue:
controller exposes all container port bindings instead of first
tested on deis revision: df1c0edd7f9f44999da5d11d5f2c674bf0b17a78  Tue Jun 10 14:00:57 2014 -0700

CoreOS 343.0.0

deis executes the following code:
 core@core1 ~ $ docker inspect -f '{{range $i, $e := .HostConfig.PortBindings }}{{$p := index $e 0}}{{$p.HostPort}}{{end}}' wp_v2.cmd.1
4915349154

the result is a concatenation of the 2 ports which obiously doesn't work.



Discussion:
That's definitely not the intention of that code. `{{$p := index $e 0}}` should only be grabbing the first port. If we're printing both then it's a bug. I'll look into it.

Can confirm that having two exposed ports gets you a concatenated string. To reproduce:

```
$ docker run --name helloworld -d -p 1111:1111 -p 2222:2222 busybox /bin/sh -c "while true; do echo hello world; sleep 1; done"
$ core@deis-1 ~ $ docker inspect -f '{{range $i, $e := .HostConfig.PortBindings }}{{$p := index $e 0}}{{$p.HostPort}}{{end}}' helloworld
11112222
```

I tried hacking around, but I couldn't find a solution without pre-emptively knowing what port was exposed beforehand as per [the docs](http://docs.docker.com/reference/commandline/cli/#inspect).

As a note, we are executing this code here: https://github.com/deis/deis/blob/master/controller/scheduler/coreos.py#L212-L213

Changed the title to reflect master.

For a temporary fix, we could just expose the first port available (in this case, that'd be the lowest port number because the docker API sorts them numerically). That way we can support more app images that expose more than one port, but the lowest port number is the HTTP port.

Is there a way to work with an image that has two ports? For example, Gerrit requires a web port and an ssh port for pushing the repository, how can I make that work?

The problem is that things like Gerrit fundamentally differ from the application workflow that things like Deis assume. 

@eMxyzptlk  Deis is more like a transitional PaaS which assume its apps like 12-factor model.

Try kubernetes or mesos, those would satisfy you.

I'll keep Deis because in-house we are following the 12-factor model. I'll just deploy Gerrit to another server outside of Deis in this case.

Okay, that sounds like a good alternative. Unfortunately the reason why we cannot support something like Gerrit at this time is because of

a) this issue
b) no tcp support in the router for applications: #1315 

This issue with multiple ports should get resolved in the meantime, but we need to properly flesh out a design for how we can support multi-port applications... Which will probably roll out in a major release considering that it's a backwards-incompatible change.


Resolving patch:
diff --git a/controller/scheduler/fleet.py b/controller/scheduler/fleet.py
--- a/controller/scheduler/fleet.py
+++ b/controller/scheduler/fleet.py
@@ -379,7 +379,7 @@ def attach(self, name):
     {"section": "Unit", "name": "Description", "value": "{name}"},
     {"section": "Service", "name": "ExecStartPre", "value": '''/bin/sh -c "IMAGE=$(etcdctl get /deis/registry/host 2>&1):$(etcdctl get /deis/registry/port 2>&1)/{image}; docker pull $IMAGE"'''},  # noqa
     {"section": "Service", "name": "ExecStartPre", "value": '''/bin/sh -c "docker inspect {name} >/dev/null 2>&1 && docker rm -f {name} || true"'''},  # noqa
-    {"section": "Service", "name": "ExecStart", "value": '''/bin/sh -c "IMAGE=$(etcdctl get /deis/registry/host 2>&1):$(etcdctl get /deis/registry/port 2>&1)/{image}; port=$(docker inspect -f '{{{{range $k, $v := .ContainerConfig.ExposedPorts }}}}{{{{$k}}}}{{{{end}}}}' $IMAGE | cut -d/ -f1) ; docker run --name {name} {memory} {cpu} {hostname} -P -e PORT=$port $IMAGE {command}"'''},  # noqa
+    {"section": "Service", "name": "ExecStart", "value": '''/bin/sh -c "IMAGE=$(etcdctl get /deis/registry/host 2>&1):$(etcdctl get /deis/registry/port 2>&1)/{image}; docker run --name {name} {memory} {cpu} {hostname} -P $IMAGE {command}"'''},  # noqa
     {"section": "Service", "name": "ExecStop", "value": '''/usr/bin/docker stop {name}'''},
     {"section": "Service", "name": "ExecStop", "value": '''/usr/bin/docker rm -f {name}'''},
     {"section": "Service", "name": "TimeoutStartSec", "value": "20m"},


Test patch: