github.com/turtlemonvh/terraform@v0.6.9-0.20151204001754-8e40b6b855e8/builtin/provisioners/file/resource_provisioner.go (about)

     1  package file
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"os"
     7  	"time"
     8  
     9  	"github.com/hashicorp/terraform/communicator"
    10  	"github.com/hashicorp/terraform/helper/config"
    11  	"github.com/hashicorp/terraform/terraform"
    12  	"github.com/mitchellh/go-homedir"
    13  )
    14  
    15  // ResourceProvisioner represents a file provisioner
    16  type ResourceProvisioner struct{}
    17  
    18  // Apply executes the file provisioner
    19  func (p *ResourceProvisioner) Apply(
    20  	o terraform.UIOutput,
    21  	s *terraform.InstanceState,
    22  	c *terraform.ResourceConfig) error {
    23  	// Get a new communicator
    24  	comm, err := communicator.New(s)
    25  	if err != nil {
    26  		return err
    27  	}
    28  
    29  	// Get the source and destination
    30  	sRaw := c.Config["source"]
    31  	src, ok := sRaw.(string)
    32  	if !ok {
    33  		return fmt.Errorf("Unsupported 'source' type! Must be string.")
    34  	}
    35  
    36  	src, err = homedir.Expand(src)
    37  	if err != nil {
    38  		return err
    39  	}
    40  
    41  	dRaw := c.Config["destination"]
    42  	dst, ok := dRaw.(string)
    43  	if !ok {
    44  		return fmt.Errorf("Unsupported 'destination' type! Must be string.")
    45  	}
    46  	return p.copyFiles(comm, src, dst)
    47  }
    48  
    49  // Validate checks if the required arguments are configured
    50  func (p *ResourceProvisioner) Validate(c *terraform.ResourceConfig) (ws []string, es []error) {
    51  	v := &config.Validator{
    52  		Required: []string{
    53  			"source",
    54  			"destination",
    55  		},
    56  	}
    57  	return v.Validate(c)
    58  }
    59  
    60  // copyFiles is used to copy the files from a source to a destination
    61  func (p *ResourceProvisioner) copyFiles(comm communicator.Communicator, src, dst string) error {
    62  	// Wait and retry until we establish the connection
    63  	err := retryFunc(comm.Timeout(), func() error {
    64  		err := comm.Connect(nil)
    65  		return err
    66  	})
    67  	if err != nil {
    68  		return err
    69  	}
    70  	defer comm.Disconnect()
    71  
    72  	info, err := os.Stat(src)
    73  	if err != nil {
    74  		return err
    75  	}
    76  
    77  	// If we're uploading a directory, short circuit and do that
    78  	if info.IsDir() {
    79  		if err := comm.UploadDir(dst, src); err != nil {
    80  			return fmt.Errorf("Upload failed: %v", err)
    81  		}
    82  		return nil
    83  	}
    84  
    85  	// We're uploading a file...
    86  	f, err := os.Open(src)
    87  	if err != nil {
    88  		return err
    89  	}
    90  	defer f.Close()
    91  
    92  	err = comm.Upload(dst, f)
    93  	if err != nil {
    94  		return fmt.Errorf("Upload failed: %v", err)
    95  	}
    96  	return err
    97  }
    98  
    99  // retryFunc is used to retry a function for a given duration
   100  func retryFunc(timeout time.Duration, f func() error) error {
   101  	finish := time.After(timeout)
   102  	for {
   103  		err := f()
   104  		if err == nil {
   105  			return nil
   106  		}
   107  		log.Printf("Retryable error: %v", err)
   108  
   109  		select {
   110  		case <-finish:
   111  			return err
   112  		case <-time.After(3 * time.Second):
   113  		}
   114  	}
   115  }