From 4b7a98584a2ffa8b13de0fd483630c97dc521e25 Mon Sep 17 00:00:00 2001 From: James Nugent Date: Fri, 15 Jan 2016 21:59:33 +0000 Subject: [PATCH] provider/docker: Mount named volumes in containers This adds support for specifying named volumes for mounting in a `docker_container` resource. --- .../docker/resource_docker_container.go | 18 ++++++ .../docker/resource_docker_container_funcs.go | 9 ++- .../docker/resource_docker_container_test.go | 63 +++++++++++++++++++ .../docker/r/container.html.markdown | 8 ++- 4 files changed, 93 insertions(+), 5 deletions(-) diff --git a/builtin/providers/docker/resource_docker_container.go b/builtin/providers/docker/resource_docker_container.go index c8f363e3f..3cff902a7 100644 --- a/builtin/providers/docker/resource_docker_container.go +++ b/builtin/providers/docker/resource_docker_container.go @@ -137,6 +137,20 @@ func resourceDockerContainer() *schema.Resource { Type: schema.TypeString, Optional: true, ForceNew: true, + ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { + value := v.(string) + if !regexp.MustCompile(`^/`).MatchString(value) { + es = append(es, fmt.Errorf( + "%q must be an absolute path", k)) + } + return + }, + }, + + "volume_name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, }, "read_only": &schema.Schema{ @@ -383,6 +397,10 @@ func resourceDockerVolumesHash(v interface{}) int { buf.WriteString(fmt.Sprintf("%v-", v.(string))) } + if v, ok := m["volume_name"]; ok { + buf.WriteString(fmt.Sprintf("%v-", v.(string))) + } + if v, ok := m["read_only"]; ok { buf.WriteString(fmt.Sprintf("%v-", v.(bool))) } diff --git a/builtin/providers/docker/resource_docker_container_funcs.go b/builtin/providers/docker/resource_docker_container_funcs.go index 47878f934..39d2c09f0 100644 --- a/builtin/providers/docker/resource_docker_container_funcs.go +++ b/builtin/providers/docker/resource_docker_container_funcs.go @@ -353,7 +353,10 @@ func volumeSetToDockerVolumes(volumes *schema.Set) (map[string]struct{}, []strin volume := volumeInt.(map[string]interface{}) fromContainer := volume["from_container"].(string) containerPath := volume["container_path"].(string) - hostPath := volume["host_path"].(string) + volumeName := volume["volume_name"].(string) + if len(volumeName) == 0 { + volumeName = volume["host_path"].(string) + } readOnly := volume["read_only"].(bool) switch { @@ -363,13 +366,13 @@ func volumeSetToDockerVolumes(volumes *schema.Set) (map[string]struct{}, []strin return retVolumeMap, retHostConfigBinds, retVolumeFromContainers, errors.New("Both a container and a path specified in a volume entry") case len(fromContainer) != 0: retVolumeFromContainers = append(retVolumeFromContainers, fromContainer) - case len(hostPath) != 0: + case len(volumeName) != 0: readWrite := "rw" if readOnly { readWrite = "ro" } retVolumeMap[containerPath] = struct{}{} - retHostConfigBinds = append(retHostConfigBinds, hostPath+":"+containerPath+":"+readWrite) + retHostConfigBinds = append(retHostConfigBinds, volumeName+":"+containerPath+":"+readWrite) default: retVolumeMap[containerPath] = struct{}{} } diff --git a/builtin/providers/docker/resource_docker_container_test.go b/builtin/providers/docker/resource_docker_container_test.go index aa1eee961..8536c78ce 100644 --- a/builtin/providers/docker/resource_docker_container_test.go +++ b/builtin/providers/docker/resource_docker_container_test.go @@ -25,6 +25,48 @@ func TestAccDockerContainer_basic(t *testing.T) { }) } +func TestAccDockerContainer_volume(t *testing.T) { + var c dc.Container + + testCheck := func(*terraform.State) error { + if len(c.Mounts) != 2 { + return fmt.Errorf("Incorrect number of mounts: expected 2, got %d", len(c.Mounts)) + } + + for _, v := range c.Mounts { + if v.Name != "testAccDockerContainerVolume_volume" { + continue + } + + if v.Destination != "/tmp/volume" { + return fmt.Errorf("Bad destination on mount: expected /tmp/volume, got %q", v.Destination) + } + + if v.Mode != "rw" { + return fmt.Errorf("Bad mode on mount: expected rw, got %q", v.Mode) + } + + return nil + } + + return fmt.Errorf("Mount for testAccDockerContainerVolume_volume not found") + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccDockerContainerVolumeConfig, + Check: resource.ComposeTestCheckFunc( + testAccContainerRunning("docker_container.foo", &c), + testCheck, + ), + }, + }, + }) +} + func TestAccDockerContainer_customized(t *testing.T) { var c dc.Container @@ -145,6 +187,27 @@ resource "docker_container" "foo" { } ` +const testAccDockerContainerVolumeConfig = ` +resource "docker_image" "foo" { + name = "nginx:latest" +} + +resource "docker_volume" "foo" { + name = "testAccDockerContainerVolume_volume" +} + +resource "docker_container" "foo" { + name = "tf-test" + image = "${docker_image.foo.latest}" + + volumes { + volume_name = "${docker_volume.foo.name}" + container_path = "/tmp/volume" + read_only = false + } +} +` + const testAccDockerContainerCustomizedConfig = ` resource "docker_image" "foo" { name = "nginx:latest" diff --git a/website/source/docs/providers/docker/r/container.html.markdown b/website/source/docs/providers/docker/r/container.html.markdown index dd39a34ff..b417d741f 100644 --- a/website/source/docs/providers/docker/r/container.html.markdown +++ b/website/source/docs/providers/docker/r/container.html.markdown @@ -107,12 +107,16 @@ the following: * `from_container` - (Optional, string) The container where the volume is coming from. -* `container_path` - (Optional, string) The path in the container where the - volume will be mounted. * `host_path` - (Optional, string) The path on the host where the volume is coming from. +* `volume_name` - (Optional, string) The name of the docker volume which + should be mounted. +* `container_path` - (Optional, string) The path in the container where the + volume will be mounted. * `read_only` - (Optional, bool) If true, this volume will be readonly. Defaults to false. + +One of `from_container`, `host_path` or `volume_name` must be set. ## Attributes Reference