mirror of
https://github.com/kreuzwerker/terraform-provider-docker.git
synced 2025-12-23 16:19:42 -05:00
Add support to attach devices to containers (#54)
This commit is contained in:
parent
5e85cb68ae
commit
46d1fc59eb
4 changed files with 163 additions and 0 deletions
|
|
@ -292,6 +292,33 @@ func resourceDockerContainer() *schema.Resource {
|
|||
ForceNew: true,
|
||||
},
|
||||
|
||||
"devices": &schema.Schema{
|
||||
Type: schema.TypeSet,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"host_path": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
|
||||
"container_path": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
|
||||
"permissions": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
"destroy_grace_seconds": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
|
|
|
|||
|
|
@ -135,6 +135,10 @@ func resourceDockerContainerCreate(d *schema.ResourceData, meta interface{}) err
|
|||
}
|
||||
}
|
||||
|
||||
if v, ok := d.GetOk("devices"); ok {
|
||||
hostConfig.Devices = deviceSetToDockerDevices(v.(*schema.Set))
|
||||
}
|
||||
|
||||
if v, ok := d.GetOk("dns"); ok {
|
||||
hostConfig.DNS = stringSetToStringSlice(v.(*schema.Set))
|
||||
}
|
||||
|
|
@ -463,3 +467,29 @@ func volumeSetToDockerVolumes(volumes *schema.Set) (map[string]struct{}, []strin
|
|||
|
||||
return retVolumeMap, retHostConfigBinds, retVolumeFromContainers, nil
|
||||
}
|
||||
|
||||
func deviceSetToDockerDevices(devices *schema.Set) []dc.Device {
|
||||
retDevices := []dc.Device{}
|
||||
for _, deviceInt := range devices.List() {
|
||||
deviceMap := deviceInt.(map[string]interface{})
|
||||
hostPath := deviceMap["host_path"].(string)
|
||||
containerPath := deviceMap["container_path"].(string)
|
||||
permissions := deviceMap["permissions"].(string)
|
||||
|
||||
switch {
|
||||
case len(containerPath) == 0:
|
||||
containerPath = hostPath
|
||||
fallthrough
|
||||
case len(permissions) == 0:
|
||||
permissions = "rwm"
|
||||
}
|
||||
|
||||
device := dc.Device{
|
||||
PathOnHost: hostPath,
|
||||
PathInContainer: containerPath,
|
||||
CgroupPermissions: permissions,
|
||||
}
|
||||
retDevices = append(retDevices, device)
|
||||
}
|
||||
return retDevices
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import (
|
|||
"archive/tar"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
dc "github.com/fsouza/go-dockerclient"
|
||||
|
|
@ -274,6 +275,79 @@ func TestAccDockerContainer_upload(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestAccDockerContainer_device(t *testing.T) {
|
||||
var c dc.Container
|
||||
|
||||
testCheck := func(*terraform.State) error {
|
||||
client := testAccProvider.Meta().(*ProviderConfig).DockerClient
|
||||
|
||||
createExecOpts := dc.CreateExecOptions{
|
||||
Cmd: []string{"dd", "if=/dev/zero_test", "of=/tmp/test.txt", "count=10", "bs=1"},
|
||||
Container: c.ID,
|
||||
}
|
||||
|
||||
exec, err := client.CreateExec(createExecOpts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to create a exec instance on container: %s", err)
|
||||
}
|
||||
|
||||
startExecOpts := dc.StartExecOptions{
|
||||
OutputStream: os.Stdout,
|
||||
ErrorStream: os.Stdout,
|
||||
}
|
||||
|
||||
if err := client.StartExec(exec.ID, startExecOpts); err != nil {
|
||||
return fmt.Errorf("Unable to run exec a instance on container: %s", err)
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
downloadFileOpts := dc.DownloadFromContainerOptions{
|
||||
OutputStream: buf,
|
||||
Path: "/tmp/test.txt",
|
||||
}
|
||||
|
||||
if err := client.DownloadFromContainer(c.ID, downloadFileOpts); err != nil {
|
||||
return fmt.Errorf("Unable to download a file from container: %s", err)
|
||||
}
|
||||
|
||||
r := bytes.NewReader(buf.Bytes())
|
||||
tr := tar.NewReader(r)
|
||||
|
||||
if _, err := tr.Next(); err != nil {
|
||||
return fmt.Errorf("Unable to read content of tar archive: %s", err)
|
||||
}
|
||||
|
||||
fbuf := new(bytes.Buffer)
|
||||
fbuf.ReadFrom(tr)
|
||||
content := fbuf.Bytes()
|
||||
|
||||
if len(content) != 10 {
|
||||
return fmt.Errorf("Incorrect size of file: %d", len(content))
|
||||
}
|
||||
for _, value := range content {
|
||||
if value != 0 {
|
||||
return fmt.Errorf("Incorrect content in file: %v", content)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccDockerContainerDeviceConfig,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccContainerRunning("docker_container.foo", &c),
|
||||
testCheck,
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccContainerRunning(n string, container *dc.Container) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
|
|
@ -408,3 +482,19 @@ resource "docker_container" "foo" {
|
|||
}
|
||||
}
|
||||
`
|
||||
|
||||
const testAccDockerContainerDeviceConfig = `
|
||||
resource "docker_image" "foo" {
|
||||
name = "nginx:latest"
|
||||
}
|
||||
|
||||
resource "docker_container" "foo" {
|
||||
name = "tf-test"
|
||||
image = "${docker_image.foo.latest}"
|
||||
|
||||
devices {
|
||||
host_path = "/dev/zero"
|
||||
container_path = "/dev/zero_test"
|
||||
}
|
||||
}
|
||||
`
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ The following arguments are supported:
|
|||
* `host` - (Optional, block) See [Extra Hosts](#extra_hosts) below for
|
||||
details.
|
||||
* `privileged` - (Optional, bool) Run container in privileged mode.
|
||||
* `devices` - (Optional, bool) See [Devices](#devices) below for details.
|
||||
* `publish_all_ports` - (Optional, bool) Publish all ports of the container.
|
||||
* `volumes` - (Optional, block) See [Volumes](#volumes) below for details.
|
||||
* `memory` - (Optional, int) The memory limit for the container in MBs.
|
||||
|
|
@ -161,6 +162,21 @@ Each `upload` supports the following
|
|||
* `content` - (Required, string) A content of a file to upload.
|
||||
* `file` - (Required, string) path to a file in the container.
|
||||
|
||||
<a id="devices"></a>
|
||||
### Devices
|
||||
|
||||
`devices` is a block within the configuration that can be repeated to specify
|
||||
the devices exposed to a container. Each `devices` block supports
|
||||
the following:
|
||||
|
||||
* `host_path` - (Required, string) The path on the host where the device
|
||||
is located.
|
||||
* `container_path` - (Optional, string) The path in the container where the
|
||||
device will be binded.
|
||||
* `permissions` - (Optional, string) The cgroup permissions given to the
|
||||
container to access the device.
|
||||
Defaults to `rwm`.
|
||||
|
||||
## Attributes Reference
|
||||
|
||||
The following attributes are exported:
|
||||
|
|
|
|||
Loading…
Reference in a new issue