Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

provider/docker: Docker upload #4921

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 68 additions & 1 deletion builtin/providers/docker/resource_docker_container.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure whether transitive dependencies are the way to go here. Any suggestions?

"github.com/hashicorp/terraform/helper/hashcode"
"github.com/hashicorp/terraform/helper/schema"
)
Expand Down Expand Up @@ -348,6 +352,54 @@ func resourceDockerContainer() *schema.Resource {
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
},

"uploads": &schema.Schema{
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be renamed if necessary

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 {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure why but during my tests, it turns out that the generated state file leaves the path here but while doing a plan, the checksum is generated. This leads to changes such as

    uploads.#:                         "1" => "1"
    uploads.241032095.local_path:      "files/" => ""
    uploads.241032095.remote_path:     "/tmp/" => ""
    uploads.4134509434.local_path:     "" => "7c95cb15f351188c8a680e2a2e15df87d163cc8a" (forces new resource)
    uploads.4134509434.remote_path:    "" => "/tmp/" (forces new resource)

Could this be related to the many issues related to StateFunc that cropped up everywhere :(

switch v.(type) {
case string:
reader, err := archive.Tar(v.(string), archive.Uncompressed)
if err != nil {
return "invalid"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no nice error management in StateFunc, shame :(

}

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,
},
},
}
}
Expand Down Expand Up @@ -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())
}
31 changes: 31 additions & 0 deletions builtin/providers/docker/resource_docker_container_funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
}
14 changes: 13 additions & 1 deletion website/source/docs/providers/docker/r/container.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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.

<a id="ports"></a>
### Ports
Expand All @@ -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.
Expand All @@ -124,6 +125,17 @@ the following:

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

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