github.com/tarrant/terraform@v0.3.8-0.20150402012457-f68c9eee638e/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/helper/config"
    10  	helper "github.com/hashicorp/terraform/helper/ssh"
    11  	"github.com/hashicorp/terraform/terraform"
    12  )
    13  
    14  type ResourceProvisioner struct{}
    15  
    16  func (p *ResourceProvisioner) Apply(
    17  	o terraform.UIOutput,
    18  	s *terraform.InstanceState,
    19  	c *terraform.ResourceConfig) error {
    20  	// Ensure the connection type is SSH
    21  	if err := helper.VerifySSH(s); err != nil {
    22  		return err
    23  	}
    24  
    25  	// Get the SSH configuration
    26  	conf, err := helper.ParseSSHConfig(s)
    27  	if err != nil {
    28  		return err
    29  	}
    30  
    31  	// Get the source and destination
    32  	sRaw := c.Config["source"]
    33  	src, ok := sRaw.(string)
    34  	if !ok {
    35  		return fmt.Errorf("Unsupported 'source' type! Must be string.")
    36  	}
    37  
    38  	dRaw := c.Config["destination"]
    39  	dst, ok := dRaw.(string)
    40  	if !ok {
    41  		return fmt.Errorf("Unsupported 'destination' type! Must be string.")
    42  	}
    43  	return p.copyFiles(conf, src, dst)
    44  }
    45  
    46  func (p *ResourceProvisioner) Validate(c *terraform.ResourceConfig) (ws []string, es []error) {
    47  	v := &config.Validator{
    48  		Required: []string{
    49  			"source",
    50  			"destination",
    51  		},
    52  	}
    53  	return v.Validate(c)
    54  }
    55  
    56  // copyFiles is used to copy the files from a source to a destination
    57  func (p *ResourceProvisioner) copyFiles(conf *helper.SSHConfig, src, dst string) error {
    58  	// Get the SSH client config
    59  	config, err := helper.PrepareConfig(conf)
    60  	if err != nil {
    61  		return err
    62  	}
    63  	defer config.CleanupConfig()
    64  
    65  	// Wait and retry until we establish the SSH connection
    66  	var comm *helper.SSHCommunicator
    67  	err = retryFunc(conf.TimeoutVal, func() error {
    68  		host := fmt.Sprintf("%s:%d", conf.Host, conf.Port)
    69  		comm, err = helper.New(host, config)
    70  		return err
    71  	})
    72  	if err != nil {
    73  		return err
    74  	}
    75  
    76  	info, err := os.Stat(src)
    77  	if err != nil {
    78  		return err
    79  	}
    80  
    81  	// If we're uploading a directory, short circuit and do that
    82  	if info.IsDir() {
    83  		if err := comm.UploadDir(dst, src, nil); err != nil {
    84  			return fmt.Errorf("Upload failed: %v", err)
    85  		}
    86  		return nil
    87  	}
    88  
    89  	// We're uploading a file...
    90  	f, err := os.Open(src)
    91  	if err != nil {
    92  		return err
    93  	}
    94  	defer f.Close()
    95  
    96  	err = comm.Upload(dst, f)
    97  	if err != nil {
    98  		return fmt.Errorf("Upload failed: %v", err)
    99  	}
   100  	return err
   101  }
   102  
   103  // retryFunc is used to retry a function for a given duration
   104  func retryFunc(timeout time.Duration, f func() error) error {
   105  	finish := time.After(timeout)
   106  	for {
   107  		err := f()
   108  		if err == nil {
   109  			return nil
   110  		}
   111  		log.Printf("Retryable error: %v", err)
   112  
   113  		select {
   114  		case <-finish:
   115  			return err
   116  		case <-time.After(3 * time.Second):
   117  		}
   118  	}
   119  }