Skip to content

Commit

Permalink
Merge pull request #704 from iverberk/f-env-ports
Browse files Browse the repository at this point in the history
Pass a combination of ip and port to the task environment.
  • Loading branch information
dadgar committed Jan 29, 2016
2 parents 4f2e755 + c5ef55b commit 9779e9a
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 53 deletions.
11 changes: 2 additions & 9 deletions client/driver/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -341,15 +341,8 @@ func (d *DockerDriver) createContainer(ctx *ExecContext, task *structs.Task, dri
d.logger.Printf("[DEBUG] driver.docker: exposed port %s", containerPort)
}

// This was set above in a call to GetTaskEnv but if we
// have mapped any ports we will need to override them.
//
// TODO refactor the implementation in GetTaskEnv to match
// the 0.2 ports world view. Docker seems to be the only place where
// this is actually needed, but this is kinda hacky.
if len(driverConfig.PortMap) > 0 {
d.taskEnv.SetPorts(network.MapLabelToValues(driverConfig.PortMap))
}
d.taskEnv.SetPortMap(driverConfig.PortMap)

hostConfig.PortBindings = publishedPorts
config.ExposedPorts = exposedPorts
}
Expand Down
9 changes: 5 additions & 4 deletions client/driver/docker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -551,8 +551,8 @@ func TestDockerPortsNoMap(t *testing.T) {
}

expectedEnvironment := map[string]string{
"NOMAD_PORT_main": fmt.Sprintf("%d", res),
"NOMAD_PORT_REDIS": fmt.Sprintf("%d", dyn),
"NOMAD_ADDR_main": fmt.Sprintf("127.0.0.1:%d", res),
"NOMAD_ADDR_REDIS": fmt.Sprintf("127.0.0.1:%d", dyn),
}

for key, val := range expectedEnvironment {
Expand Down Expand Up @@ -606,8 +606,9 @@ func TestDockerPortsMapping(t *testing.T) {
}

expectedEnvironment := map[string]string{
"NOMAD_PORT_main": "8080",
"NOMAD_PORT_REDIS": "6379",
"NOMAD_ADDR_main": "127.0.0.1:8080",
"NOMAD_ADDR_REDIS": "127.0.0.1:6379",
"NOMAD_HOST_PORT_main": "8080",
}

for key, val := range expectedEnvironment {
Expand Down
7 changes: 1 addition & 6 deletions client/driver/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,12 +148,7 @@ func GetTaskEnv(alloc *allocdir.AllocDir, node *structs.Node, task *structs.Task
if task.Resources != nil {
env.SetMemLimit(task.Resources.MemoryMB)
env.SetCpuLimit(task.Resources.CPU)

if len(task.Resources.Networks) > 0 {
network := task.Resources.Networks[0]
env.SetTaskIp(network.IP)
env.SetPorts(network.MapLabelToValues(nil))
}
env.SetNetworks(task.Resources.Networks)
}

return env.Build(), nil
Expand Down
13 changes: 6 additions & 7 deletions client/driver/driver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,13 +113,12 @@ func TestDriver_GetTaskEnv(t *testing.T) {
exp := map[string]string{
"NOMAD_CPU_LIMIT": "1000",
"NOMAD_MEMORY_LIMIT": "500",
"NOMAD_IP": "1.2.3.4",
"NOMAD_PORT_one": "80",
"NOMAD_PORT_two": "443",
"NOMAD_PORT_three": "8080",
"NOMAD_PORT_four": "12345",
"NOMAD_PORT_admin": "8081",
"NOMAD_PORT_web": "8086",
"NOMAD_ADDR_one": "1.2.3.4:80",
"NOMAD_ADDR_two": "1.2.3.4:443",
"NOMAD_ADDR_three": "1.2.3.4:8080",
"NOMAD_ADDR_four": "1.2.3.4:12345",
"NOMAD_ADDR_admin": "1.2.3.4:8081",
"NOMAD_ADDR_web": "1.2.3.4:8086",
"NOMAD_META_CHOCOLATE": "cake",
"NOMAD_META_STRAWBERRY": "icecream",
"HELLO": "world",
Expand Down
45 changes: 25 additions & 20 deletions client/driver/env/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,11 @@ const (

// Prefix for passing both dynamic and static port allocations to
// tasks.
// E.g. $NOMAD_PORT_1 or $NOMAD_PORT_http
PortPrefix = "NOMAD_PORT_"
// E.g. $NOMAD_IP_1=127.0.0.1:1 or $NOMAD_IP_http=127.0.0.1:80
AddrPrefix = "NOMAD_ADDR_"

// Prefix for passing the host port when a portmap is specified.
HostPortPrefix = "NOMAD_HOST_PORT_"

// Prefix for passing task meta data.
MetaPrefix = "NOMAD_META_"
Expand All @@ -54,13 +57,13 @@ const (
type TaskEnvironment struct {
env map[string]string
meta map[string]string
ports map[string]int
allocDir string
taskDir string
cpuLimit int
memLimit int
ip string
node *structs.Node
networks []*structs.NetworkResource
portMap map[string]int

// taskEnv is the variables that will be set in the tasks environment
taskEnv map[string]string
Expand Down Expand Up @@ -103,8 +106,16 @@ func (t *TaskEnvironment) Build() *TaskEnvironment {
}

// Build the ports
for label, port := range t.ports {
t.taskEnv[fmt.Sprintf("%s%s", PortPrefix, label)] = strconv.Itoa(port)
for _, network := range t.networks {
for label, value := range network.MapLabelToValues(t.portMap) {
IPPort := fmt.Sprintf("%s:%d", network.IP, value)
t.taskEnv[fmt.Sprintf("%s%s", AddrPrefix, label)] = IPPort

// Pass an explicit port mapping to the environment
if port, ok := t.portMap[label]; ok {
t.taskEnv[fmt.Sprintf("%s%s", HostPortPrefix, label)] = strconv.Itoa(port)
}
}
}

// Build the directories
Expand All @@ -123,11 +134,6 @@ func (t *TaskEnvironment) Build() *TaskEnvironment {
t.taskEnv[CpuLimit] = strconv.Itoa(t.cpuLimit)
}

// Build the IP
if t.ip != "" {
t.taskEnv[TaskIP] = t.ip
}

// Build the node
if t.node != nil {
// Set up the node values.
Expand Down Expand Up @@ -221,24 +227,23 @@ func (t *TaskEnvironment) ClearCpuLimit() *TaskEnvironment {
return t
}

func (t *TaskEnvironment) SetTaskIp(ip string) *TaskEnvironment {
t.ip = ip
func (t *TaskEnvironment) SetNetworks(networks []*structs.NetworkResource) *TaskEnvironment {
t.networks = networks
return t
}

func (t *TaskEnvironment) ClearTaskIp() *TaskEnvironment {
t.ip = ""
func (t *TaskEnvironment) clearNetworks() *TaskEnvironment {
t.networks = nil
return t
}

// Takes a map of port labels to their port value.
func (t *TaskEnvironment) SetPorts(ports map[string]int) *TaskEnvironment {
t.ports = ports
func (t *TaskEnvironment) SetPortMap(portMap map[string]int) *TaskEnvironment {
t.portMap = portMap
return t
}

func (t *TaskEnvironment) ClearPorts() *TaskEnvironment {
t.ports = nil
func (t *TaskEnvironment) clearPortMap() *TaskEnvironment {
t.portMap = nil
return t
}

Expand Down
42 changes: 37 additions & 5 deletions client/driver/env/env_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"testing"

"github.com/hashicorp/nomad/nomad/mock"
"github.com/hashicorp/nomad/nomad/structs"
)

const (
Expand All @@ -25,6 +26,20 @@ const (
envTwoVal = ":80"
)

var (
// Networks that tests can rely on
networks = []*structs.NetworkResource{
&structs.NetworkResource{
IP: "127.0.0.1",
ReservedPorts: []structs.Port{{"http", 80}},
DynamicPorts: []structs.Port{{"https", 8080}},
},
}
portMap = map[string]int{
"https": 443,
}
)

func testTaskEnvironment() *TaskEnvironment {
n := mock.Node()
n.Attributes = map[string]string{
Expand Down Expand Up @@ -121,11 +136,17 @@ func TestEnvironment_ReplaceEnv_Mixed(t *testing.T) {
func TestEnvironment_AsList(t *testing.T) {
n := mock.Node()
env := NewTaskEnvironment(n).
SetTaskIp("127.0.0.1").SetPorts(map[string]int{"http": 80}).
SetNetworks(networks).
SetPortMap(portMap).
SetMeta(map[string]string{"foo": "baz"}).Build()

act := env.EnvList()
exp := []string{"NOMAD_IP=127.0.0.1", "NOMAD_PORT_http=80", "NOMAD_META_FOO=baz"}
exp := []string{
"NOMAD_ADDR_http=127.0.0.1:80",
"NOMAD_ADDR_https=127.0.0.1:443",
"NOMAD_HOST_PORT_https=443",
"NOMAD_META_FOO=baz",
}
sort.Strings(act)
sort.Strings(exp)
if !reflect.DeepEqual(act, exp) {
Expand All @@ -136,11 +157,18 @@ func TestEnvironment_AsList(t *testing.T) {
func TestEnvironment_ClearEnvvars(t *testing.T) {
n := mock.Node()
env := NewTaskEnvironment(n).
SetTaskIp("127.0.0.1").
SetNetworks(networks).
SetPortMap(portMap).
SetEnvvars(map[string]string{"foo": "baz", "bar": "bang"}).Build()

act := env.EnvList()
exp := []string{"NOMAD_IP=127.0.0.1", "bar=bang", "foo=baz"}
exp := []string{
"NOMAD_ADDR_http=127.0.0.1:80",
"NOMAD_ADDR_https=127.0.0.1:443",
"NOMAD_HOST_PORT_https=443",
"bar=bang",
"foo=baz",
}
sort.Strings(act)
sort.Strings(exp)
if !reflect.DeepEqual(act, exp) {
Expand All @@ -151,7 +179,11 @@ func TestEnvironment_ClearEnvvars(t *testing.T) {
env.ClearEnvvars().Build()

act = env.EnvList()
exp = []string{"NOMAD_IP=127.0.0.1"}
exp = []string{
"NOMAD_ADDR_http=127.0.0.1:80",
"NOMAD_ADDR_https=127.0.0.1:443",
"NOMAD_HOST_PORT_https=443",
}
sort.Strings(act)
sort.Strings(exp)
if !reflect.DeepEqual(act, exp) {
Expand Down
8 changes: 6 additions & 2 deletions website/source/docs/jobspec/networking.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,10 @@ port "http" {}
```

When the task is started, it is passed an environment variable named
`NOMAD_PORT_http` which indicates the port.
`NOMAD_ADDR_http` which indicates a combination of the interface IP and port.

```
NOMAD_PORT_http=53423 ./start-command
NOMAD_ADDR_http=127.0.0.1:53423 ./start-command
```

### Mapped Ports
Expand All @@ -118,4 +118,8 @@ The above example is for the Docker driver. The service is listening on port
`8080` inside the container. The driver will automatically map the dynamic port
to this service.

When the task is started, it is passed an additional environment variable named
`NOMAD_HOST_PORT_http` which indicates the host port that the http service is
bound to.

Please refer to the [Docker](/docs/drivers/docker.html) and [QEMU](/docs/drivers/qemu.html) drivers for additional information.

0 comments on commit 9779e9a

Please sign in to comment.