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

Cleanup of #3906 #7561

Merged
merged 3 commits into from
Jul 8, 2016
Merged
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
70 changes: 55 additions & 15 deletions builtin/provisioners/file/resource_provisioner.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ package file

import (
"fmt"
"io/ioutil"
"log"
"os"
"time"

"github.com/hashicorp/terraform/communicator"
"github.com/hashicorp/terraform/helper/config"
"github.com/hashicorp/terraform/terraform"
"github.com/mitchellh/go-homedir"
)
Expand All @@ -26,18 +26,16 @@ func (p *ResourceProvisioner) Apply(
return err
}

// Get the source and destination
sRaw := c.Config["source"]
src, ok := sRaw.(string)
if !ok {
return fmt.Errorf("Unsupported 'source' type! Must be string.")
}

src, err = homedir.Expand(src)
// Get the source
src, deleteSource, err := p.getSrc(c)
if err != nil {
return err
}
if deleteSource {
defer os.Remove(src)
}

// Get destination
dRaw := c.Config["destination"]
dst, ok := dRaw.(string)
if !ok {
Expand All @@ -48,13 +46,55 @@ func (p *ResourceProvisioner) Apply(

// Validate checks if the required arguments are configured
func (p *ResourceProvisioner) Validate(c *terraform.ResourceConfig) (ws []string, es []error) {
v := &config.Validator{
Required: []string{
"source",
"destination",
},
numDst := 0
numSrc := 0
for name := range c.Raw {
switch name {
case "destination":
numDst++
case "source", "content":
numSrc++
default:
es = append(es, fmt.Errorf("Unknown configuration '%s'", name))
}
}
return v.Validate(c)
if numSrc != 1 || numDst != 1 {
es = append(es, fmt.Errorf("Must provide one of 'content' or 'source' and 'destination' to file"))
}
return
}

// getSrc returns the file to use as source
func (p *ResourceProvisioner) getSrc(c *terraform.ResourceConfig) (string, bool, error) {
var src string

sRaw, ok := c.Config["source"]
if ok {
if src, ok = sRaw.(string); !ok {
return "", false, fmt.Errorf("Unsupported 'source' type! Must be string.")
}
}

content, ok := c.Config["content"]
if ok {
file, err := ioutil.TempFile("", "tf-file-content")
if err != nil {
return "", true, err
}

contentStr, ok := content.(string)
if !ok {
return "", true, fmt.Errorf("Unsupported 'content' type! Must be string.")
}
if _, err = file.WriteString(contentStr); err != nil {
return "", true, err
}

return file.Name(), true, nil
}

expansion, err := homedir.Expand(src)
return expansion, false, err
}

// copyFiles is used to copy the files from a source to a destination
Expand Down
35 changes: 33 additions & 2 deletions builtin/provisioners/file/resource_provisioner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ func TestResourceProvisioner_impl(t *testing.T) {
var _ terraform.ResourceProvisioner = new(ResourceProvisioner)
}

func TestResourceProvider_Validate_good(t *testing.T) {
func TestResourceProvider_Validate_good_source(t *testing.T) {
c := testConfig(t, map[string]interface{}{
"source": "/tmp/foo",
"destination": "/tmp/bar",
Expand All @@ -26,7 +26,22 @@ func TestResourceProvider_Validate_good(t *testing.T) {
}
}

func TestResourceProvider_Validate_bad(t *testing.T) {
func TestResourceProvider_Validate_good_content(t *testing.T) {
c := testConfig(t, map[string]interface{}{
"content": "value to copy",
"destination": "/tmp/bar",
})
p := new(ResourceProvisioner)
warn, errs := p.Validate(c)
if len(warn) > 0 {
t.Fatalf("Warnings: %v", warn)
}
if len(errs) > 0 {
t.Fatalf("Errors: %v", errs)
}
}

func TestResourceProvider_Validate_bad_not_destination(t *testing.T) {
c := testConfig(t, map[string]interface{}{
"source": "nope",
})
Expand All @@ -40,6 +55,22 @@ func TestResourceProvider_Validate_bad(t *testing.T) {
}
}

func TestResourceProvider_Validate_bad_to_many_src(t *testing.T) {
c := testConfig(t, map[string]interface{}{
"source": "nope",
"content": "value to copy",
"destination": "/tmp/bar",
})
p := new(ResourceProvisioner)
warn, errs := p.Validate(c)
if len(warn) > 0 {
t.Fatalf("Warnings: %v", warn)
}
if len(errs) == 0 {
t.Fatalf("Should have errors")
}
}

func testConfig(
t *testing.T,
c map[string]interface{}) *terraform.ResourceConfig {
Expand Down
16 changes: 14 additions & 2 deletions website/source/docs/provisioners/file.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ resource "aws_instance" "web" {
destination = "/etc/myapp.conf"
}

# Copies the string in content into /tmp/file.log
provisioner "file" {
content = "ami used: ${self.ami}"
destination = "/tmp/file.log"
}

# Copies the configs.d folder to /etc/configs.d
provisioner "file" {
source = "conf/configs.d"
Expand All @@ -42,8 +48,14 @@ resource "aws_instance" "web" {

The following arguments are supported:

* `source` - (Required) This is the source file or folder. It can be specified as relative
to the current working directory or as an absolute path.
* `source` - This is the source file or folder. It can be specified as relative
to the current working directory or as an absolute path. This cannot be provided with `content`.

* `content` - This is the content to copy on the destination. If destination is a file,
the content will be written on that file, in case of a directory a file named
*tf-file-content* is created. It's recommended to use a file as destination. A
[`template_file`](/docs/providers/template/r/file.html) might be referenced in here, or
any interpolation syntax for that matter. This cannot be provided with `source`.

* `destination` - (Required) This is the destination path. It must be specified as an
absolute path.
Expand Down