provisioners/file: convert to helper/schema

This commit is contained in:
Mitchell Hashimoto 2016-12-22 16:17:34 -08:00
parent 0fb87cd96b
commit 02a4adc07c
No known key found for this signature in database
GPG Key ID: 744E147AA52F5B0A
3 changed files with 59 additions and 61 deletions

View File

@ -1,6 +1,7 @@
package file package file
import ( import (
"context"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"log" "log"
@ -8,26 +9,48 @@ import (
"time" "time"
"github.com/hashicorp/terraform/communicator" "github.com/hashicorp/terraform/communicator"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/terraform"
"github.com/mitchellh/go-homedir" "github.com/mitchellh/go-homedir"
) )
// ResourceProvisioner represents a file provisioner func Provisioner() terraform.ResourceProvisioner {
type ResourceProvisioner struct{} return &schema.Provisioner{
Schema: map[string]*schema.Schema{
"source": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ConflictsWith: []string{"content"},
},
"content": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ConflictsWith: []string{"source"},
},
"destination": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
},
ApplyFunc: applyFn,
}
}
func applyFn(ctx context.Context) error {
connData := ctx.Value(schema.ProvConnDataKey).(*schema.ResourceData)
data := ctx.Value(schema.ProvConfigDataKey).(*schema.ResourceData)
// Apply executes the file provisioner
func (p *ResourceProvisioner) Apply(
o terraform.UIOutput,
s *terraform.InstanceState,
c *terraform.ResourceConfig) error {
// Get a new communicator // Get a new communicator
comm, err := communicator.New(s) comm, err := communicator.NewData(connData)
if err != nil { if err != nil {
return err return err
} }
// Get the source // Get the source
src, deleteSource, err := p.getSrc(c) src, deleteSource, err := getSrc(data)
if err != nil { if err != nil {
return err return err
} }
@ -36,57 +59,20 @@ func (p *ResourceProvisioner) Apply(
} }
// Get destination // Get destination
dRaw := c.Config["destination"] dst := data.Get("destination").(string)
dst, ok := dRaw.(string) return copyFiles(comm, src, dst)
if !ok {
return fmt.Errorf("Unsupported 'destination' type! Must be string.")
}
return p.copyFiles(comm, src, dst)
}
// Validate checks if the required arguments are configured
func (p *ResourceProvisioner) Validate(c *terraform.ResourceConfig) (ws []string, es []error) {
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))
}
}
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 // getSrc returns the file to use as source
func (p *ResourceProvisioner) getSrc(c *terraform.ResourceConfig) (string, bool, error) { func getSrc(data *schema.ResourceData) (string, bool, error) {
var src string src := data.Get("source").(string)
if content, ok := data.GetOk("content"); ok {
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") file, err := ioutil.TempFile("", "tf-file-content")
if err != nil { if err != nil {
return "", true, err return "", true, err
} }
contentStr, ok := content.(string) if _, err = file.WriteString(content.(string)); err != nil {
if !ok {
return "", true, fmt.Errorf("Unsupported 'content' type! Must be string.")
}
if _, err = file.WriteString(contentStr); err != nil {
return "", true, err return "", true, err
} }
@ -98,7 +84,7 @@ func (p *ResourceProvisioner) getSrc(c *terraform.ResourceConfig) (string, bool,
} }
// 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
func (p *ResourceProvisioner) copyFiles(comm communicator.Communicator, src, dst string) error { func copyFiles(comm communicator.Communicator, src, dst string) error {
// Wait and retry until we establish the connection // Wait and retry until we establish the connection
err := retryFunc(comm.Timeout(), func() error { err := retryFunc(comm.Timeout(), func() error {
err := comm.Connect(nil) err := comm.Connect(nil)

View File

@ -7,16 +7,12 @@ import (
"github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/terraform"
) )
func TestResourceProvisioner_impl(t *testing.T) {
var _ terraform.ResourceProvisioner = new(ResourceProvisioner)
}
func TestResourceProvider_Validate_good_source(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",
}) })
p := new(ResourceProvisioner) p := Provisioner()
warn, errs := p.Validate(c) warn, errs := p.Validate(c)
if len(warn) > 0 { if len(warn) > 0 {
t.Fatalf("Warnings: %v", warn) t.Fatalf("Warnings: %v", warn)
@ -31,7 +27,7 @@ func TestResourceProvider_Validate_good_content(t *testing.T) {
"content": "value to copy", "content": "value to copy",
"destination": "/tmp/bar", "destination": "/tmp/bar",
}) })
p := new(ResourceProvisioner) p := Provisioner()
warn, errs := p.Validate(c) warn, errs := p.Validate(c)
if len(warn) > 0 { if len(warn) > 0 {
t.Fatalf("Warnings: %v", warn) t.Fatalf("Warnings: %v", warn)
@ -45,7 +41,7 @@ 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",
}) })
p := new(ResourceProvisioner) p := Provisioner()
warn, errs := p.Validate(c) warn, errs := p.Validate(c)
if len(warn) > 0 { if len(warn) > 0 {
t.Fatalf("Warnings: %v", warn) t.Fatalf("Warnings: %v", warn)
@ -61,7 +57,7 @@ func TestResourceProvider_Validate_bad_to_many_src(t *testing.T) {
"content": "value to copy", "content": "value to copy",
"destination": "/tmp/bar", "destination": "/tmp/bar",
}) })
p := new(ResourceProvisioner) p := Provisioner()
warn, errs := p.Validate(c) warn, errs := p.Validate(c)
if len(warn) > 0 { if len(warn) > 0 {
t.Fatalf("Warnings: %v", warn) t.Fatalf("Warnings: %v", warn)

View File

@ -8,6 +8,7 @@ import (
"github.com/hashicorp/terraform/communicator/remote" "github.com/hashicorp/terraform/communicator/remote"
"github.com/hashicorp/terraform/communicator/ssh" "github.com/hashicorp/terraform/communicator/ssh"
"github.com/hashicorp/terraform/communicator/winrm" "github.com/hashicorp/terraform/communicator/winrm"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/terraform"
) )
@ -51,3 +52,18 @@ func New(s *terraform.InstanceState) (Communicator, error) {
return nil, fmt.Errorf("connection type '%s' not supported", connType) return nil, fmt.Errorf("connection type '%s' not supported", connType)
} }
} }
// NewData creates a new Communicator from a ResourceData structure that
// represents the connection information.
func NewData(d *schema.ResourceData) (Communicator, error) {
// Turn the ResourceData into a legacy-style ConnInfo struct that
// is used to instantiate the communicator.
raw := d.State()
state := &terraform.InstanceState{
Ephemeral: terraform.EphemeralState{
ConnInfo: raw.Attributes,
},
}
return New(state)
}