github.com/tompao/terraform@v0.6.10-0.20180215233341-e41b29d0961b/builtin/provisioners/file/resource_provisioner.go (about)

     1  package file
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"os"
     8  
     9  	"github.com/hashicorp/terraform/communicator"
    10  	"github.com/hashicorp/terraform/helper/schema"
    11  	"github.com/hashicorp/terraform/terraform"
    12  	"github.com/mitchellh/go-homedir"
    13  )
    14  
    15  func Provisioner() terraform.ResourceProvisioner {
    16  	return &schema.Provisioner{
    17  		Schema: map[string]*schema.Schema{
    18  			"source": &schema.Schema{
    19  				Type:          schema.TypeString,
    20  				Optional:      true,
    21  				ConflictsWith: []string{"content"},
    22  			},
    23  
    24  			"content": &schema.Schema{
    25  				Type:          schema.TypeString,
    26  				Optional:      true,
    27  				ConflictsWith: []string{"source"},
    28  			},
    29  
    30  			"destination": &schema.Schema{
    31  				Type:     schema.TypeString,
    32  				Required: true,
    33  			},
    34  		},
    35  
    36  		ApplyFunc:    applyFn,
    37  		ValidateFunc: validateFn,
    38  	}
    39  }
    40  
    41  func applyFn(ctx context.Context) error {
    42  	connState := ctx.Value(schema.ProvRawStateKey).(*terraform.InstanceState)
    43  	data := ctx.Value(schema.ProvConfigDataKey).(*schema.ResourceData)
    44  
    45  	// Get a new communicator
    46  	comm, err := communicator.New(connState)
    47  	if err != nil {
    48  		return err
    49  	}
    50  
    51  	ctx, cancel := context.WithTimeout(ctx, comm.Timeout())
    52  	defer cancel()
    53  
    54  	// Get the source
    55  	src, deleteSource, err := getSrc(data)
    56  	if err != nil {
    57  		return err
    58  	}
    59  	if deleteSource {
    60  		defer os.Remove(src)
    61  	}
    62  
    63  	// Begin the file copy
    64  	dst := data.Get("destination").(string)
    65  
    66  	if err := copyFiles(ctx, comm, src, dst); err != nil {
    67  		return err
    68  	}
    69  	return nil
    70  }
    71  
    72  func validateFn(c *terraform.ResourceConfig) (ws []string, es []error) {
    73  	if !c.IsSet("source") && !c.IsSet("content") {
    74  		es = append(es, fmt.Errorf("Must provide one of 'source' or 'content'"))
    75  	}
    76  
    77  	return ws, es
    78  }
    79  
    80  // getSrc returns the file to use as source
    81  func getSrc(data *schema.ResourceData) (string, bool, error) {
    82  	src := data.Get("source").(string)
    83  	if content, ok := data.GetOk("content"); ok {
    84  		file, err := ioutil.TempFile("", "tf-file-content")
    85  		if err != nil {
    86  			return "", true, err
    87  		}
    88  
    89  		if _, err = file.WriteString(content.(string)); err != nil {
    90  			return "", true, err
    91  		}
    92  
    93  		return file.Name(), true, nil
    94  	}
    95  
    96  	expansion, err := homedir.Expand(src)
    97  	return expansion, false, err
    98  }
    99  
   100  // copyFiles is used to copy the files from a source to a destination
   101  func copyFiles(ctx context.Context, comm communicator.Communicator, src, dst string) error {
   102  	// Wait and retry until we establish the connection
   103  	err := communicator.Retry(ctx, func() error {
   104  		return comm.Connect(nil)
   105  	})
   106  	if err != nil {
   107  		return err
   108  	}
   109  
   110  	// disconnect when the context is canceled, which will close this after
   111  	// Apply as well.
   112  	go func() {
   113  		<-ctx.Done()
   114  		comm.Disconnect()
   115  	}()
   116  
   117  	info, err := os.Stat(src)
   118  	if err != nil {
   119  		return err
   120  	}
   121  
   122  	// If we're uploading a directory, short circuit and do that
   123  	if info.IsDir() {
   124  		if err := comm.UploadDir(dst, src); err != nil {
   125  			return fmt.Errorf("Upload failed: %v", err)
   126  		}
   127  		return nil
   128  	}
   129  
   130  	// We're uploading a file...
   131  	f, err := os.Open(src)
   132  	if err != nil {
   133  		return err
   134  	}
   135  	defer f.Close()
   136  
   137  	err = comm.Upload(dst, f)
   138  	if err != nil {
   139  		return fmt.Errorf("Upload failed: %v", err)
   140  	}
   141  	return err
   142  }