Merge pull request #7561 from hashicorp/jorgemarey-f-file-content

Cleanup of #3906
This commit is contained in:
James Nugent 2016-07-08 19:51:22 +01:00 committed by GitHub
commit 6084d732ae
3 changed files with 102 additions and 19 deletions

View File

@ -2,12 +2,12 @@ package file
import ( import (
"fmt" "fmt"
"io/ioutil"
"log" "log"
"os" "os"
"time" "time"
"github.com/hashicorp/terraform/communicator" "github.com/hashicorp/terraform/communicator"
"github.com/hashicorp/terraform/helper/config"
"github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/terraform"
"github.com/mitchellh/go-homedir" "github.com/mitchellh/go-homedir"
) )
@ -26,18 +26,16 @@ func (p *ResourceProvisioner) Apply(
return err return err
} }
// Get the source and destination // Get the source
sRaw := c.Config["source"] src, deleteSource, err := p.getSrc(c)
src, ok := sRaw.(string)
if !ok {
return fmt.Errorf("Unsupported 'source' type! Must be string.")
}
src, err = homedir.Expand(src)
if err != nil { if err != nil {
return err return err
} }
if deleteSource {
defer os.Remove(src)
}
// Get destination
dRaw := c.Config["destination"] dRaw := c.Config["destination"]
dst, ok := dRaw.(string) dst, ok := dRaw.(string)
if !ok { if !ok {
@ -48,13 +46,55 @@ func (p *ResourceProvisioner) Apply(
// Validate checks if the required arguments are configured // Validate checks if the required arguments are configured
func (p *ResourceProvisioner) Validate(c *terraform.ResourceConfig) (ws []string, es []error) { func (p *ResourceProvisioner) Validate(c *terraform.ResourceConfig) (ws []string, es []error) {
v := &config.Validator{ numDst := 0
Required: []string{ numSrc := 0
"source", for name := range c.Raw {
"destination", 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 // copyFiles is used to copy the files from a source to a destination

View File

@ -11,7 +11,7 @@ func TestResourceProvisioner_impl(t *testing.T) {
var _ terraform.ResourceProvisioner = new(ResourceProvisioner) 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{}{ c := testConfig(t, map[string]interface{}{
"source": "/tmp/foo", "source": "/tmp/foo",
"destination": "/tmp/bar", "destination": "/tmp/bar",
@ -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{}{ c := testConfig(t, map[string]interface{}{
"source": "nope", "source": "nope",
}) })
@ -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( func testConfig(
t *testing.T, t *testing.T,
c map[string]interface{}) *terraform.ResourceConfig { c map[string]interface{}) *terraform.ResourceConfig {

View File

@ -24,6 +24,12 @@ resource "aws_instance" "web" {
destination = "/etc/myapp.conf" 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 # Copies the configs.d folder to /etc/configs.d
provisioner "file" { provisioner "file" {
source = "conf/configs.d" source = "conf/configs.d"
@ -42,8 +48,14 @@ resource "aws_instance" "web" {
The following arguments are supported: The following arguments are supported:
* `source` - (Required) This is the source file or folder. It can be specified as relative * `source` - This is the source file or folder. It can be specified as relative
to the current working directory or as an absolute path. 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 * `destination` - (Required) This is the destination path. It must be specified as an
absolute path. absolute path.