From 19d042151a7e6a85089b4c71131adbc1325086c4 Mon Sep 17 00:00:00 2001 From: Colin Hebert Date: Sun, 31 Jan 2016 16:56:55 +1100 Subject: [PATCH 1/2] Create uploads section for docker containers --- .../docker/resource_docker_container.go | 65 ++++++++++++++++++- .../docker/resource_docker_container_funcs.go | 31 +++++++++ .../docker/r/container.html.markdown | 14 +++- 3 files changed, 108 insertions(+), 2 deletions(-) diff --git a/builtin/providers/docker/resource_docker_container.go b/builtin/providers/docker/resource_docker_container.go index 3cff902a7184..19c82c06f983 100644 --- a/builtin/providers/docker/resource_docker_container.go +++ b/builtin/providers/docker/resource_docker_container.go @@ -3,11 +3,14 @@ package docker import ( "bytes" "fmt" - + "crypto/sha1" + "io" + "os" "regexp" "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/schema" + "github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive" ) func resourceDockerContainer() *schema.Resource { @@ -341,6 +344,51 @@ func resourceDockerContainer() *schema.Resource { Elem: &schema.Schema{Type: schema.TypeString}, Set: stringSetHash, }, + + "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, + ForceNew: true, + ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { + if _, err := os.Stat(k); err != nil { + es = append(es, fmt.Errorf( + "%q must be valid path: %s", k, err)) + } + return + }, + StateFunc: func(v interface{}) string { + switch v.(type) { + case string: + reader, err := archive.Tar(srcPath, 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, + ForceNew: true, + }, + }, + }, + Set: resourceDockerUploadHash, + }, }, } } @@ -408,6 +456,21 @@ 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()) +} + func stringSetHash(v interface{}) int { return hashcode.String(v.(string)) } diff --git a/builtin/providers/docker/resource_docker_container_funcs.go b/builtin/providers/docker/resource_docker_container_funcs.go index 0f276b0f6e4c..323c83563644 100644 --- a/builtin/providers/docker/resource_docker_container_funcs.go +++ b/builtin/providers/docker/resource_docker_container_funcs.go @@ -8,6 +8,7 @@ import ( dc "github.com/fsouza/go-dockerclient" "github.com/hashicorp/terraform/helper/schema" + "github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive" ) var ( @@ -170,6 +171,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, hostConfig); err != nil { return fmt.Errorf("Unable to start container: %s", err) @@ -383,3 +397,20 @@ func volumeSetToDockerVolumes(volumes *schema.Set) (map[string]struct{}, []strin return retVolumeMap, retHostConfigBinds, retVolumeFromContainers, nil } + +func uploadFile(upload map[string]interface{}) (dc.UploadToContainerOptions, error) { + local_path := upload["local_path"].(string) + remote_path := upload["remote_path"].(string) + + stream, err := archive.Tar(srcPath, archive.Uncompressed) + if err != nil { + return nil, fmt.Errorf("Unable to send %s to container: %s", local_path, err) + } + + uploadOpts := dc.UploadToContainerOptions{ + InputStream: stream, + Path: remote_path, + 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 a60b06bf718c..574cf0b89128 100644 --- a/website/source/docs/providers/docker/r/container.html.markdown +++ b/website/source/docs/providers/docker/r/container.html.markdown @@ -74,6 +74,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 @@ -96,7 +97,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. @@ -121,6 +122,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: From 52d8ad5ed5e5069a91076c6262dddf68953b4f05 Mon Sep 17 00:00:00 2001 From: Colin Hebert Date: Sun, 31 Jan 2016 18:50:52 +1100 Subject: [PATCH 2/2] Check value not key for upload --- .../docker/resource_docker_container.go | 22 +++++++++++-------- .../docker/resource_docker_container_funcs.go | 20 ++++++++--------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/builtin/providers/docker/resource_docker_container.go b/builtin/providers/docker/resource_docker_container.go index 19c82c06f983..5f638b39016e 100644 --- a/builtin/providers/docker/resource_docker_container.go +++ b/builtin/providers/docker/resource_docker_container.go @@ -2,15 +2,16 @@ package docker import ( "bytes" - "fmt" "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" - "github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive" ) func resourceDockerContainer() *schema.Resource { @@ -353,26 +354,28 @@ func resourceDockerContainer() *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) { - if _, err := os.Stat(k); err != nil { + path := v.(string) + if _, err := os.Stat(path); err != nil { es = append(es, fmt.Errorf( - "%q must be valid path: %s", k, err)) + "%q must be valid path: %s", path, err)) } return }, StateFunc: func(v interface{}) string { switch v.(type) { case string: - reader, err := archive.Tar(srcPath, archive.Uncompressed) + reader, err := archive.Tar(v.(string), archive.Uncompressed) if err != nil { return "invalid" } - hash := sha1.New() - if _, err := io.Copy(hash, reader); err != nil { + hash := sha1.New() + if _, err := io.Copy(hash, reader); err != nil { return "invalid" - } + } return hex.EncodeToString(hash.Sum(nil)[:]) default: @@ -383,11 +386,12 @@ func resourceDockerContainer() *schema.Resource { "remote_path": &schema.Schema{ Type: schema.TypeString, + Required: true, ForceNew: true, }, }, }, - Set: resourceDockerUploadHash, + Set: resourceDockerUploadsHash, }, }, } diff --git a/builtin/providers/docker/resource_docker_container_funcs.go b/builtin/providers/docker/resource_docker_container_funcs.go index 323c83563644..c64d7aa9778f 100644 --- a/builtin/providers/docker/resource_docker_container_funcs.go +++ b/builtin/providers/docker/resource_docker_container_funcs.go @@ -7,8 +7,8 @@ import ( "time" dc "github.com/fsouza/go-dockerclient" - "github.com/hashicorp/terraform/helper/schema" "github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive" + "github.com/hashicorp/terraform/helper/schema" ) var ( @@ -178,7 +178,7 @@ func resourceDockerContainerCreate(d *schema.ResourceData, meta interface{}) err return err } - if err := client.UploadToContainer(retContainer.ID, uploadOpts); err != nil { + if err := client.UploadToContainer(retContainer.ID, *uploadOpts); err != nil { return fmt.Errorf("Unable to upload to container: %s", err) } } @@ -398,18 +398,18 @@ func volumeSetToDockerVolumes(volumes *schema.Set) (map[string]struct{}, []strin return retVolumeMap, retHostConfigBinds, retVolumeFromContainers, nil } -func uploadFile(upload map[string]interface{}) (dc.UploadToContainerOptions, error) { - local_path := upload["local_path"].(string) - remote_path := upload["remote_path"].(string) +func uploadFile(upload map[string]interface{}) (*dc.UploadToContainerOptions, error) { + localPath := upload["local_path"].(string) + remotePath := upload["remote_path"].(string) - stream, err := archive.Tar(srcPath, archive.Uncompressed) + stream, err := archive.Tar(localPath, archive.Uncompressed) if err != nil { - return nil, fmt.Errorf("Unable to send %s to container: %s", local_path, err) + return nil, fmt.Errorf("Unable to send %s to container: %s", localPath, err) } - uploadOpts := dc.UploadToContainerOptions{ - InputStream: stream, - Path: remote_path, + uploadOpts := &dc.UploadToContainerOptions{ + InputStream: stream, + Path: remotePath, NoOverwriteDirNonDir: false, } return uploadOpts, nil