Skip to content

Commit

Permalink
Merge pull request #970 from hashicorp/f-pass-env
Browse files Browse the repository at this point in the history
Pass environment variables from host to exec based tasks
  • Loading branch information
dadgar committed Mar 23, 2016
2 parents 048fa5a + 5352bab commit 276b7a4
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 1 deletion.
12 changes: 12 additions & 0 deletions client/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,18 @@ import (
"github.com/hashicorp/nomad/nomad/structs"
)

var (
// DefaultEnvBlacklist is the default set of environment variables that are
// filtered when passing the environment variables of the host to a task.
DefaultEnvBlacklist = strings.Join([]string{
"CONSUL_TOKEN",
"VAULT_TOKEN",
"ATLAS_TOKEN",
"AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_SESSION_TOKEN",
"GOOGLE_APPLICATION_CREDENTIALS",
}, ",")
)

// RPCHandler can be provided to the Client if there is a local server
// to avoid going over the network. If not provided, the Client will
// maintain a connection pool to the servers
Expand Down
34 changes: 34 additions & 0 deletions client/driver/env/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package env

import (
"fmt"
"os"
"strconv"
"strings"

Expand Down Expand Up @@ -305,6 +306,39 @@ func (t *TaskEnvironment) AppendEnvvars(m map[string]string) *TaskEnvironment {
return t
}

// AppendHostEnvvars adds the host environment variables to the tasks. The
// filter parameter can be use to filter host environment from entering the
// tasks.
func (t *TaskEnvironment) AppendHostEnvvars(filter []string) *TaskEnvironment {
hostEnv := os.Environ()
if t.Env == nil {
t.Env = make(map[string]string, len(hostEnv))
}

// Index the filtered environment variables.
index := make(map[string]struct{}, len(filter))
for _, f := range filter {
index[f] = struct{}{}
}

for _, e := range hostEnv {
parts := strings.Split(e, "=")
key, value := parts[0], parts[1]

// Skip filtered environment variables
if _, filtered := index[key]; filtered {
continue
}

// Don't override the tasks environment variables.
if _, existing := t.Env[key]; !existing {
t.Env[key] = value
}
}

return t
}

func (t *TaskEnvironment) ClearEnvvars() *TaskEnvironment {
t.Env = nil
return t
Expand Down
21 changes: 21 additions & 0 deletions client/driver/env/env_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package env

import (
"fmt"
"os"
"reflect"
"sort"
"strings"
"testing"

"github.com/hashicorp/nomad/nomad/mock"
Expand Down Expand Up @@ -204,3 +206,22 @@ func TestEnvironment_Interprolate(t *testing.T) {
t.Fatalf("env.List() returned %v; want %v", act, exp)
}
}

func TestEnvironment_AppendHostEnvVars(t *testing.T) {
host := os.Environ()
if len(host) < 2 {
t.Skip("No host environment variables. Can't test")
}
skip := strings.Split(host[0], "=")[0]
env := testTaskEnvironment().
AppendHostEnvvars([]string{skip}).
Build()

act := env.EnvMap()
if len(act) < 1 {
t.Fatalf("Host environment variables not properly set")
}
if _, ok := act[skip]; ok {
t.Fatalf("Didn't filter environment variable %q", skip)
}
}
8 changes: 7 additions & 1 deletion client/driver/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"log"
"os/exec"
"path/filepath"
"strings"
"syscall"
"time"

Expand Down Expand Up @@ -74,13 +75,18 @@ func (d *ExecDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle,
if err := mapstructure.WeakDecode(task.Config, &driverConfig); err != nil {
return nil, err
}

// Get the command to be ran
command := driverConfig.Command
if err := validateCommand(command, "args"); err != nil {
return nil, err
}

// Create a location to download the artifact.
// Set the host environment variables.
filter := strings.Split(d.config.ReadDefault("env.blacklist", config.DefaultEnvBlacklist), ",")
d.taskEnv.AppendHostEnvvars(filter)

// Get the task directory for storing the executor logs.
taskDir, ok := ctx.AllocDir.TaskDirs[d.DriverContext.taskName]
if !ok {
return nil, fmt.Errorf("Could not find task directory for task: %v", d.DriverContext.taskName)
Expand Down
5 changes: 5 additions & 0 deletions client/driver/java.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,11 @@ func (d *JavaDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle,
if err := mapstructure.WeakDecode(task.Config, &driverConfig); err != nil {
return nil, err
}

// Set the host environment variables.
filter := strings.Split(d.config.ReadDefault("env.blacklist", config.DefaultEnvBlacklist), ",")
d.taskEnv.AppendHostEnvvars(filter)

taskDir, ok := ctx.AllocDir.TaskDirs[d.DriverContext.taskName]
if !ok {
return nil, fmt.Errorf("Could not find task directory for task: %v", d.DriverContext.taskName)
Expand Down
5 changes: 5 additions & 0 deletions client/driver/raw_exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"log"
"os/exec"
"path/filepath"
"strings"
"time"

"github.com/hashicorp/go-plugin"
Expand Down Expand Up @@ -82,6 +83,10 @@ func (d *RawExecDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandl
return nil, err
}

// Set the host environment variables.
filter := strings.Split(d.config.ReadDefault("env.blacklist", config.DefaultEnvBlacklist), ",")
d.taskEnv.AppendHostEnvvars(filter)

bin, err := discover.NomadExecutable()
if err != nil {
return nil, fmt.Errorf("unable to find the nomad binary: %v", err)
Expand Down
11 changes: 11 additions & 0 deletions website/source/docs/agent/config.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,17 @@ documentation [here](/docs/drivers/index.html)
If the whitelist is empty, all drivers are fingerprinted and enabled where
applicable.
* `env.blacklist`: Nomad passes the host environment variables to `exec`,
`raw_exec` and `java` tasks. `env.blacklist` is a comma seperated list of
environment variable keys not to pass to these tasks. If specified, the
defaults are overriden. The following are the default:
* `CONSUL_TOKEN`
* `VAULT_TOKEN`
* `ATLAS_TOKEN`
* `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_SESSION_TOKEN`
* `GOOGLE_APPLICATION_CREDENTIALS`
* `fingerprint.whitelist`: A comma separated list of whitelisted fingerprinters.
If specified, fingerprinters not in the whitelist will be disabled. If the
whitelist is empty, all fingerprinters are used.
Expand Down

0 comments on commit 276b7a4

Please sign in to comment.