github.com/chalford/terraform@v0.3.7-0.20150113080010-a78c69a8c81f/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 64 // Wait and retry until we establish the SSH connection 65 var comm *helper.SSHCommunicator 66 err = retryFunc(conf.TimeoutVal, func() error { 67 host := fmt.Sprintf("%s:%d", conf.Host, conf.Port) 68 comm, err = helper.New(host, config) 69 return err 70 }) 71 if err != nil { 72 return err 73 } 74 75 info, err := os.Stat(src) 76 if err != nil { 77 return err 78 } 79 80 // If we're uploading a directory, short circuit and do that 81 if info.IsDir() { 82 if err := comm.UploadDir(dst, src, nil); err != nil { 83 return fmt.Errorf("Upload failed: %v", err) 84 } 85 return nil 86 } 87 88 // We're uploading a file... 89 f, err := os.Open(src) 90 if err != nil { 91 return err 92 } 93 defer f.Close() 94 95 err = comm.Upload(dst, f) 96 if err != nil { 97 return fmt.Errorf("Upload failed: %v", err) 98 } 99 return err 100 } 101 102 // retryFunc is used to retry a function for a given duration 103 func retryFunc(timeout time.Duration, f func() error) error { 104 finish := time.After(timeout) 105 for { 106 err := f() 107 if err == nil { 108 return nil 109 } 110 log.Printf("Retryable error: %v", err) 111 112 select { 113 case <-finish: 114 return err 115 case <-time.After(3 * time.Second): 116 } 117 } 118 }