diff --git a/builtin/providers/docker/resource_docker_container.go b/builtin/providers/docker/resource_docker_container.go
index 01b2453ad..2fb4efc29 100644
--- a/builtin/providers/docker/resource_docker_container.go
+++ b/builtin/providers/docker/resource_docker_container.go
@@ -369,6 +369,29 @@ func resourceDockerContainer() *schema.Resource {
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
},
+
+ "upload": &schema.Schema{
+ Type: schema.TypeSet,
+ Optional: true,
+ ForceNew: true,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "content": &schema.Schema{
+ Type: schema.TypeString,
+ Required: true,
+ // This is intentional. The container is mutated once, and never updated later.
+ // New configuration forces a new deployment, even with the same binaries.
+ ForceNew: true,
+ },
+ "file": &schema.Schema{
+ Type: schema.TypeString,
+ Required: true,
+ ForceNew: true,
+ },
+ },
+ },
+ Set: resourceDockerUploadHash,
+ },
},
}
}
@@ -435,3 +458,18 @@ func resourceDockerVolumesHash(v interface{}) int {
return hashcode.String(buf.String())
}
+
+func resourceDockerUploadHash(v interface{}) int {
+ var buf bytes.Buffer
+ m := v.(map[string]interface{})
+
+ if v, ok := m["content"]; ok {
+ buf.WriteString(fmt.Sprintf("%v-", v.(string)))
+ }
+
+ if v, ok := m["file"]; ok {
+ buf.WriteString(fmt.Sprintf("%v-", v.(string)))
+ }
+
+ return hashcode.String(buf.String())
+}
diff --git a/builtin/providers/docker/resource_docker_container_funcs.go b/builtin/providers/docker/resource_docker_container_funcs.go
index 9668fd0a7..f74264a77 100644
--- a/builtin/providers/docker/resource_docker_container_funcs.go
+++ b/builtin/providers/docker/resource_docker_container_funcs.go
@@ -1,6 +1,8 @@
package docker
import (
+ "archive/tar"
+ "bytes"
"errors"
"fmt"
"strconv"
@@ -187,6 +189,39 @@ func resourceDockerContainerCreate(d *schema.ResourceData, meta interface{}) err
}
}
+ if v, ok := d.GetOk("upload"); ok {
+ for _, upload := range v.(*schema.Set).List() {
+ content := upload.(map[string]interface{})["content"].(string)
+ file := upload.(map[string]interface{})["file"].(string)
+
+ buf := new(bytes.Buffer)
+ tw := tar.NewWriter(buf)
+ hdr := &tar.Header{
+ Name: file,
+ Mode: 0644,
+ Size: int64(len(content)),
+ }
+ if err := tw.WriteHeader(hdr); err != nil {
+ return fmt.Errorf("Error creating tar archive: %s", err)
+ }
+ if _, err := tw.Write([]byte(content)); err != nil {
+ return fmt.Errorf("Error creating tar archive: %s", err)
+ }
+ if err := tw.Close(); err != nil {
+ return fmt.Errorf("Error creating tar archive: %s", err)
+ }
+
+ uploadOpts := dc.UploadToContainerOptions{
+ InputStream: bytes.NewReader(buf.Bytes()),
+ Path: "/",
+ }
+
+ if err := client.UploadToContainer(retContainer.ID, uploadOpts); err != nil {
+ return fmt.Errorf("Unable to upload volume content: %s", err)
+ }
+ }
+ }
+
creationTime = time.Now()
if err := client.StartContainer(retContainer.ID, nil); err != nil {
return fmt.Errorf("Unable to start container: %s", err)
diff --git a/builtin/providers/docker/resource_docker_container_test.go b/builtin/providers/docker/resource_docker_container_test.go
index 1c4da8cdd..99f0ab3e9 100644
--- a/builtin/providers/docker/resource_docker_container_test.go
+++ b/builtin/providers/docker/resource_docker_container_test.go
@@ -1,6 +1,8 @@
package docker
import (
+ "archive/tar"
+ "bytes"
"fmt"
"testing"
@@ -180,6 +182,55 @@ func TestAccDockerContainer_customized(t *testing.T) {
})
}
+func TestAccDockerContainer_upload(t *testing.T) {
+ var c dc.Container
+
+ testCheck := func(*terraform.State) error {
+ client := testAccProvider.Meta().(*dc.Client)
+
+ buf := new(bytes.Buffer)
+ opts := dc.DownloadFromContainerOptions{
+ OutputStream: buf,
+ Path: "/terraform/test.txt",
+ }
+
+ if err := client.DownloadFromContainer(c.ID, opts); 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.String()
+
+ if content != "foo" {
+ return fmt.Errorf("file content is invalid")
+ }
+
+ return nil
+ }
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ Providers: testAccProviders,
+ Steps: []resource.TestStep{
+ resource.TestStep{
+ Config: testAccDockerContainerUploadConfig,
+ 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]
@@ -285,3 +336,19 @@ resource "docker_container" "foo" {
}
}
`
+
+const testAccDockerContainerUploadConfig = `
+resource "docker_image" "foo" {
+ name = "nginx:latest"
+}
+
+resource "docker_container" "foo" {
+ name = "tf-test"
+ image = "${docker_image.foo.latest}"
+
+ upload {
+ content = "foo"
+ file = "/terraform/test.txt"
+ }
+}
+`
diff --git a/website/source/docs/providers/docker/r/container.html.markdown b/website/source/docs/providers/docker/r/container.html.markdown
index 4b928c126..0774edb4a 100644
--- a/website/source/docs/providers/docker/r/container.html.markdown
+++ b/website/source/docs/providers/docker/r/container.html.markdown
@@ -80,6 +80,7 @@ The following arguments are supported:
* `networks` - (Optional, set of strings) Id of the networks in which the
container is.
* `destroy_grace_seconds` - (Optional, int) If defined will attempt to stop the container before destroying. Container will be destroyed after `n` seconds or on successful stop.
+* `upload` - (Optional, block) See [File Upload](#upload) below for details.
### Ports
@@ -127,6 +128,16 @@ the following:
One of `from_container`, `host_path` or `volume_name` must be set.
+
+### File Upload
+
+`upload` is a block within the configuration that can be repeated to specify
+files to upload to the container before starting it.
+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.
+
## Attributes Reference
The following attributes are exported: