provider/docker: Mount named volumes in containers

This adds support for specifying named volumes for mounting in a
`docker_container` resource.
This commit is contained in:
James Nugent 2016-01-15 21:59:33 +00:00
parent 52d1e214fb
commit 4b7a98584a
4 changed files with 93 additions and 5 deletions

View File

@ -137,6 +137,20 @@ func resourceDockerContainer() *schema.Resource {
Type: schema.TypeString, Type: schema.TypeString,
Optional: true, Optional: true,
ForceNew: 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{ "read_only": &schema.Schema{
@ -383,6 +397,10 @@ func resourceDockerVolumesHash(v interface{}) int {
buf.WriteString(fmt.Sprintf("%v-", v.(string))) 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 { if v, ok := m["read_only"]; ok {
buf.WriteString(fmt.Sprintf("%v-", v.(bool))) buf.WriteString(fmt.Sprintf("%v-", v.(bool)))
} }

View File

@ -353,7 +353,10 @@ func volumeSetToDockerVolumes(volumes *schema.Set) (map[string]struct{}, []strin
volume := volumeInt.(map[string]interface{}) volume := volumeInt.(map[string]interface{})
fromContainer := volume["from_container"].(string) fromContainer := volume["from_container"].(string)
containerPath := volume["container_path"].(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) readOnly := volume["read_only"].(bool)
switch { 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") return retVolumeMap, retHostConfigBinds, retVolumeFromContainers, errors.New("Both a container and a path specified in a volume entry")
case len(fromContainer) != 0: case len(fromContainer) != 0:
retVolumeFromContainers = append(retVolumeFromContainers, fromContainer) retVolumeFromContainers = append(retVolumeFromContainers, fromContainer)
case len(hostPath) != 0: case len(volumeName) != 0:
readWrite := "rw" readWrite := "rw"
if readOnly { if readOnly {
readWrite = "ro" readWrite = "ro"
} }
retVolumeMap[containerPath] = struct{}{} retVolumeMap[containerPath] = struct{}{}
retHostConfigBinds = append(retHostConfigBinds, hostPath+":"+containerPath+":"+readWrite) retHostConfigBinds = append(retHostConfigBinds, volumeName+":"+containerPath+":"+readWrite)
default: default:
retVolumeMap[containerPath] = struct{}{} retVolumeMap[containerPath] = struct{}{}
} }

View File

@ -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) { func TestAccDockerContainer_customized(t *testing.T) {
var c dc.Container 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 = ` const testAccDockerContainerCustomizedConfig = `
resource "docker_image" "foo" { resource "docker_image" "foo" {
name = "nginx:latest" name = "nginx:latest"

View File

@ -107,13 +107,17 @@ the following:
* `from_container` - (Optional, string) The container where the volume is * `from_container` - (Optional, string) The container where the volume is
coming from. 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 * `host_path` - (Optional, string) The path on the host where the volume
is coming from. 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. * `read_only` - (Optional, bool) If true, this volume will be readonly.
Defaults to false. Defaults to false.
One of `from_container`, `host_path` or `volume_name` must be set.
## Attributes Reference ## Attributes Reference
The following attributes are exported: The following attributes are exported: