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 }