Skip to content

Commit

Permalink
provider/docker: Upload files into container before first start (#9520)
Browse files Browse the repository at this point in the history
* Create uploads section for docker containers

* Upload a single file, load its content from state
  • Loading branch information
mkuzmin authored and stack72 committed Dec 5, 2016
1 parent aba6c90 commit 05145db
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 0 deletions.
38 changes: 38 additions & 0 deletions builtin/providers/docker/resource_docker_container.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
},
}
}
Expand Down Expand Up @@ -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())
}
35 changes: 35 additions & 0 deletions builtin/providers/docker/resource_docker_container_funcs.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package docker

import (
"archive/tar"
"bytes"
"errors"
"fmt"
"strconv"
Expand Down Expand Up @@ -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)
Expand Down
67 changes: 67 additions & 0 deletions builtin/providers/docker/resource_docker_container_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package docker

import (
"archive/tar"
"bytes"
"fmt"
"testing"

Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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"
}
}
`
11 changes: 11 additions & 0 deletions website/source/docs/providers/docker/r/container.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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.

<a id="ports"></a>
### Ports
Expand Down Expand Up @@ -127,6 +128,16 @@ the following:

One of `from_container`, `host_path` or `volume_name` must be set.

<a id="upload"></a>
### 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:
Expand Down

0 comments on commit 05145db

Please sign in to comment.