diff --git a/client/driver/docker.go b/client/driver/docker.go index 7ac703d46..19475f802 100644 --- a/client/driver/docker.go +++ b/client/driver/docker.go @@ -136,6 +136,7 @@ type DockerDriverConfig struct { WorkDir string `mapstructure:"work_dir"` // Working directory inside the container Logging []DockerLoggingOpts `mapstructure:"logging"` // Logging options for syslog server Volumes []string `mapstructure:"volumes"` // Host-Volumes to mount in, syntax: /path/to/host/directory:/destination/path/in/container + ForcePull bool `mapstructure:"force_pull"` // Always force pull before running image, usefull if your tags are mutable } // Validate validates a docker driver config @@ -331,6 +332,9 @@ func (d *DockerDriver) Validate(config map[string]interface{}) error { "volumes": &fields.FieldSchema{ Type: fields.TypeArray, }, + "force_pull": &fields.FieldSchema{ + Type: fields.TypeBool, + }, }, } @@ -890,9 +894,11 @@ func (d *DockerDriver) createImage(driverConfig *DockerDriverConfig, client *doc var dockerImage *docker.Image var err error // We're going to check whether the image is already downloaded. If the tag - // is "latest" we have to check for a new version every time so we don't + // is "latest", or ForcePull is set, we have to check for a new version every time so we don't // bother to check and cache the id here. We'll download first, then cache. - if tag != "latest" { + if driverConfig.ForcePull { + d.logger.Printf("[DEBUG] driver.docker: force pull image '%s:%s' instead of inspecting local", repo, tag) + } else if tag != "latest" { dockerImage, err = client.InspectImage(image) } diff --git a/client/driver/docker_test.go b/client/driver/docker_test.go index 580ed3dfe..9ac374b5a 100644 --- a/client/driver/docker_test.go +++ b/client/driver/docker_test.go @@ -699,6 +699,36 @@ func TestDockerDriver_Labels(t *testing.T) { } } +func TestDockerDriver_ForcePull_IsInvalidConfig(t *testing.T) { + task, _, _ := dockerTask() + task.Config["force_pull"] = "nothing" + + driverCtx, execCtx := testDriverContexts(task) + driverCtx.config.Options = map[string]string{"docker.cleanup.image": "false"} + driver := NewDockerDriver(driverCtx) + if err := driver.Prestart(execCtx, task); err == nil { + execCtx.AllocDir.Destroy() + t.Fatalf("error expected in prestart") + } else { + execCtx.AllocDir.Destroy() + } +} + +func TestDockerDriver_ForcePull(t *testing.T) { + task, _, _ := dockerTask() + task.Config["force_pull"] = "true" + + client, handle, cleanup := dockerSetup(t, task) + defer cleanup() + + waitForExist(t, client, handle.(*DockerHandle)) + + _, err := client.InspectContainer(handle.(*DockerHandle).ContainerID()) + if err != nil { + t.Fatalf("err: %v", err) + } +} + func TestDockerDriver_DNS(t *testing.T) { task, _, _ := dockerTask() task.Config["dns_servers"] = []string{"8.8.8.8", "8.8.4.4"} diff --git a/website/source/docs/drivers/docker.html.md b/website/source/docs/drivers/docker.html.md index 08ca130ad..0955670e7 100644 --- a/website/source/docs/drivers/docker.html.md +++ b/website/source/docs/drivers/docker.html.md @@ -164,6 +164,10 @@ The `docker` driver supports the following configuration in the job spec: * `interactive` - (Optional) `true` or `false` (default). Keep STDIN open on the container. +* `force_pull` - (Optional) `true` or `false` (default). Always pull latest image + instead of using existing local image. Should be set to `true` if repository tags + are mutable. + * `shm_size` - (Optional) The size (bytes) of /dev/shm for the container. * `logging` - (Optional) A key-value map of Docker logging options. The default