github.com/rvichery/terraform@v0.11.10/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  	// Get the source
    52  	src, deleteSource, err := getSrc(data)
    53  	if err != nil {
    54  		return err
    55  	}
    56  	if deleteSource {
    57  		defer os.Remove(src)
    58  	}
    59  
    60  	// Begin the file copy
    61  	dst := data.Get("destination").(string)
    62  
    63  	if err := copyFiles(ctx, comm, src, dst); err != nil {
    64  		return err
    65  	}
    66  	return nil
    67  }
    68  
    69  func validateFn(c *terraform.ResourceConfig) (ws []string, es []error) {
    70  	if !c.IsSet("source") && !c.IsSet("content") {
    71  		es = append(es, fmt.Errorf("Must provide one of 'source' or 'content'"))
    72  	}
    73  
    74  	return ws, es
    75  }
    76  
    77  // getSrc returns the file to use as source
    78  func getSrc(data *schema.ResourceData) (string, bool, error) {
    79  	src := data.Get("source").(string)
    80  	if content, ok := data.GetOk("content"); ok {
    81  		file, err := ioutil.TempFile("", "tf-file-content")
    82  		if err != nil {
    83  			return "", true, err
    84  		}
    85  
    86  		if _, err = file.WriteString(content.(string)); err != nil {
    87  			return "", true, err
    88  		}
    89  
    90  		return file.Name(), true, nil
    91  	}
    92  
    93  	expansion, err := homedir.Expand(src)
    94  	return expansion, false, err
    95  }
    96  
    97  // copyFiles is used to copy the files from a source to a destination
    98  func copyFiles(ctx context.Context, comm communicator.Communicator, src, dst string) error {
    99  	retryCtx, cancel := context.WithTimeout(ctx, comm.Timeout())
   100  	defer cancel()
   101  
   102  	// Wait and retry until we establish the connection
   103  	err := communicator.Retry(retryCtx, 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  }