diff --git a/builtin/providers/docker/resource_docker_container.go b/builtin/providers/docker/resource_docker_container.go index 3bcf922315c2..9a7542dafcb3 100644 --- a/builtin/providers/docker/resource_docker_container.go +++ b/builtin/providers/docker/resource_docker_container.go @@ -2,10 +2,14 @@ package docker import ( "bytes" + "crypto/sha1" + "encoding/hex" "fmt" - + "io" + "os" "regexp" + "github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive" "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/schema" ) @@ -348,6 +352,54 @@ func resourceDockerContainer() *schema.Resource { Elem: &schema.Schema{Type: schema.TypeString}, Set: schema.HashString, }, + + "uploads": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "local_path": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { + path := v.(string) + if _, err := os.Stat(path); err != nil { + es = append(es, fmt.Errorf( + "%q must be valid path: %s", path, err)) + } + return + }, + StateFunc: func(v interface{}) string { + switch v.(type) { + case string: + reader, err := archive.Tar(v.(string), archive.Uncompressed) + if err != nil { + return "invalid" + } + + hash := sha1.New() + if _, err := io.Copy(hash, reader); err != nil { + return "invalid" + } + + return hex.EncodeToString(hash.Sum(nil)[:]) + default: + return "" + } + }, + }, + + "remote_path": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + }, + }, + Set: resourceDockerUploadsHash, + }, }, } } @@ -414,3 +466,18 @@ func resourceDockerVolumesHash(v interface{}) int { return hashcode.String(buf.String()) } + +func resourceDockerUploadsHash(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + + if v, ok := m["local_path"]; ok { + buf.WriteString(fmt.Sprintf("%v-", v.(string))) + } + + if v, ok := m["remote_path"]; 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 00090294c636..fdd459529b96 100644 --- a/builtin/providers/docker/resource_docker_container_funcs.go +++ b/builtin/providers/docker/resource_docker_container_funcs.go @@ -7,6 +7,7 @@ import ( "time" dc "github.com/fsouza/go-dockerclient" + "github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive" "github.com/hashicorp/terraform/helper/schema" ) @@ -174,6 +175,19 @@ func resourceDockerContainerCreate(d *schema.ResourceData, meta interface{}) err } } + if v, ok := d.GetOk("uploads"); ok { + for _, upload := range v.(*schema.Set).List() { + uploadOpts, err := uploadFile(upload.(map[string]interface{})) + if err != nil { + return err + } + + if err := client.UploadToContainer(retContainer.ID, *uploadOpts); err != nil { + return fmt.Errorf("Unable to upload to container: %s", err) + } + } + } + creationTime = time.Now() if err := client.StartContainer(retContainer.ID, nil); err != nil { return fmt.Errorf("Unable to start container: %s", err) @@ -387,3 +401,20 @@ func volumeSetToDockerVolumes(volumes *schema.Set) (map[string]struct{}, []strin return retVolumeMap, retHostConfigBinds, retVolumeFromContainers, nil } + +func uploadFile(upload map[string]interface{}) (*dc.UploadToContainerOptions, error) { + localPath := upload["local_path"].(string) + remotePath := upload["remote_path"].(string) + + stream, err := archive.Tar(localPath, archive.Uncompressed) + if err != nil { + return nil, fmt.Errorf("Unable to send %s to container: %s", localPath, err) + } + + uploadOpts := &dc.UploadToContainerOptions{ + InputStream: stream, + Path: remotePath, + NoOverwriteDirNonDir: false, + } + return uploadOpts, nil +} diff --git a/website/source/docs/providers/docker/r/container.html.markdown b/website/source/docs/providers/docker/r/container.html.markdown index 7441e88ac112..7c3916e3d987 100644 --- a/website/source/docs/providers/docker/r/container.html.markdown +++ b/website/source/docs/providers/docker/r/container.html.markdown @@ -77,6 +77,7 @@ The following arguments are supported: * `network_mode` - (Optional, string) Network mode of the container. * `networks` - (Optional, set of strings) Id of the networks in which the container is. +* `uploads` - (Optional) See [Uploads](#uploads) below for details. ### Ports @@ -99,7 +100,7 @@ the extra host mappings for the container. Each `host_entry` block supports the following: * `host` - (Required, int) Hostname to add. -* `ip` - (Required, int) IP address this hostname should resolve to.. +* `ip` - (Required, int) IP address this hostname should resolve to. This is equivalent to using the `--add-host` option when using the `run` command of the Docker CLI. @@ -124,6 +125,17 @@ the following: One of `from_container`, `host_path` or `volume_name` must be set. + +### Uploads + +`uploads` is a block within the configuration that can be repeated to specify +files and directories to upload to the container before starting it. +Each `uploads` supports the following + +* `local_path` - (Required, string) local path to a file or directory to upload. +* `remote_path` - (Required, string) path to a file or directory in the + container. + ## Attributes Reference The following attributes are exported: