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