diff --git a/client/driver/docker.go b/client/driver/docker.go index f9711ae91e13..fb05c646d73d 100644 --- a/client/driver/docker.go +++ b/client/driver/docker.go @@ -177,6 +177,7 @@ type DockerDriverConfig struct { LoadImage string `mapstructure:"load"` // LoadImage is a path to an image archive file Command string `mapstructure:"command"` // The Command to run when the container starts up Args []string `mapstructure:"args"` // The arguments to the Command + Entrypoint []string `mapstructure:"entrypoint"` // Override the containers entrypoint IpcMode string `mapstructure:"ipc_mode"` // The IPC mode of the container - host and none NetworkMode string `mapstructure:"network_mode"` // The network mode of the container - host, nat and none NetworkAliases []string `mapstructure:"network_aliases"` // The network-scoped alias for the container @@ -299,6 +300,7 @@ func NewDockerDriverConfig(task *structs.Task, env *env.TaskEnv) (*DockerDriverC // Interpolate everything that is a string dconf.ImageName = env.ReplaceEnv(dconf.ImageName) dconf.Command = env.ReplaceEnv(dconf.Command) + dconf.Entrypoint = env.ParseAndReplace(dconf.Entrypoint) dconf.IpcMode = env.ReplaceEnv(dconf.IpcMode) dconf.NetworkMode = env.ReplaceEnv(dconf.NetworkMode) dconf.NetworkAliases = env.ParseAndReplace(dconf.NetworkAliases) @@ -559,6 +561,9 @@ func (d *DockerDriver) Validate(config map[string]interface{}) error { "args": { Type: fields.TypeArray, }, + "entrypoint": { + Type: fields.TypeArray, + }, "ipc_mode": { Type: fields.TypeString, }, @@ -1070,6 +1075,7 @@ func (d *DockerDriver) createContainerConfig(ctx *ExecContext, task *structs.Tas // create the config block that will later be consumed by go-dockerclient config := &docker.Config{ Image: d.imageID, + Entrypoint: driverConfig.Entrypoint, Hostname: driverConfig.Hostname, User: task.User, Tty: driverConfig.TTY, diff --git a/client/driver/docker_test.go b/client/driver/docker_test.go index cd6849adbf5c..a0fd49209b7a 100644 --- a/client/driver/docker_test.go +++ b/client/driver/docker_test.go @@ -28,6 +28,7 @@ import ( "github.com/hashicorp/nomad/nomad/structs" tu "github.com/hashicorp/nomad/testutil" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func dockerIsRemote(t *testing.T) bool { @@ -106,13 +107,14 @@ func testDockerDriverContexts(t *testing.T, task *structs.Task) *testContext { func dockerSetupWithClient(t *testing.T, task *structs.Task, client *docker.Client) (*docker.Client, *DockerHandle, func()) { t.Helper() tctx := testDockerDriverContexts(t, task) - //tctx.DriverCtx.config.Options = map[string]string{"docker.cleanup.image": "false"} driver := NewDockerDriver(tctx.DriverCtx) copyImage(t, tctx.ExecCtx.TaskDir, "busybox.tar") presp, err := driver.Prestart(tctx.ExecCtx, task) if err != nil { - driver.Cleanup(tctx.ExecCtx, presp.CreatedResources) + if presp != nil && presp.CreatedResources != nil { + driver.Cleanup(tctx.ExecCtx, presp.CreatedResources) + } tctx.AllocDir.Destroy() t.Fatalf("error in prestart: %v", err) } @@ -2171,7 +2173,32 @@ func TestDockerDriver_Device_Success(t *testing.T) { assert.NotEmpty(t, container.HostConfig.Devices, "Expected one device") assert.Equal(t, expectedDevice, container.HostConfig.Devices[0], "Incorrect device ") +} + +func TestDockerDriver_Entrypoint(t *testing.T) { + if !tu.IsTravis() { + t.Parallel() + } + if !testutil.DockerIsConnected(t) { + t.Skip("Docker not connected") + } + + entrypoint := []string{"/bin/sh", "-c"} + task, _, _ := dockerTask(t) + task.Config["entrypoint"] = entrypoint + + client, handle, cleanup := dockerSetup(t, task) + defer cleanup() + + waitForExist(t, client, handle) + + container, err := client.InspectContainer(handle.ContainerID()) + if err != nil { + t.Fatalf("err: %v", err) + } + require.Len(t, container.Config.Entrypoint, 2, "Expected one entrypoint") + require.Equal(t, entrypoint, container.Config.Entrypoint, "Incorrect entrypoint ") } func TestDockerDriver_Kill(t *testing.T) { diff --git a/client/driver/env/env.go b/client/driver/env/env.go index 9e63accbc0a9..69100141bedf 100644 --- a/client/driver/env/env.go +++ b/client/driver/env/env.go @@ -162,6 +162,10 @@ func (t *TaskEnv) All() map[string]string { // ParseAndReplace takes the user supplied args replaces any instance of an // environment variable or Nomad variable in the args with the actual value. func (t *TaskEnv) ParseAndReplace(args []string) []string { + if args == nil { + return nil + } + replaced := make([]string, len(args)) for i, arg := range args { replaced[i] = hargs.ReplaceEnv(arg, t.EnvMap, t.NodeAttrs) diff --git a/website/source/docs/drivers/docker.html.md b/website/source/docs/drivers/docker.html.md index 7c90693a60f5..32d438cb1563 100644 --- a/website/source/docs/drivers/docker.html.md +++ b/website/source/docs/drivers/docker.html.md @@ -83,6 +83,8 @@ The `docker` driver supports the following configuration in the job spec. Only * `dns_servers` - (Optional) A list of DNS servers for the container to use (e.g. ["8.8.8.8", "8.8.4.4"]). Requires Docker v1.10 or greater. +* `entrypoint` - (Optional) A string list overriding the image's entrypoint. + * `extra_hosts` - (Optional) A list of hosts, given as host:IP, to be added to `/etc/hosts`.