github.com/lymingtonprecision/terraform@v0.9.9-0.20170613092852-62acef9611a9/builtin/provisioners/file/resource_provisioner.go (about) 1 package file 2 3 import ( 4 "context" 5 "fmt" 6 "io/ioutil" 7 "log" 8 "os" 9 "time" 10 11 "github.com/hashicorp/terraform/communicator" 12 "github.com/hashicorp/terraform/helper/schema" 13 "github.com/hashicorp/terraform/terraform" 14 "github.com/mitchellh/go-homedir" 15 ) 16 17 func Provisioner() terraform.ResourceProvisioner { 18 return &schema.Provisioner{ 19 Schema: map[string]*schema.Schema{ 20 "source": &schema.Schema{ 21 Type: schema.TypeString, 22 Optional: true, 23 ConflictsWith: []string{"content"}, 24 }, 25 26 "content": &schema.Schema{ 27 Type: schema.TypeString, 28 Optional: true, 29 ConflictsWith: []string{"source"}, 30 }, 31 32 "destination": &schema.Schema{ 33 Type: schema.TypeString, 34 Required: true, 35 }, 36 }, 37 38 ApplyFunc: applyFn, 39 ValidateFunc: validateFn, 40 } 41 } 42 43 func applyFn(ctx context.Context) error { 44 connState := ctx.Value(schema.ProvRawStateKey).(*terraform.InstanceState) 45 data := ctx.Value(schema.ProvConfigDataKey).(*schema.ResourceData) 46 47 // Get a new communicator 48 comm, err := communicator.New(connState) 49 if err != nil { 50 return err 51 } 52 53 // Get the source 54 src, deleteSource, err := getSrc(data) 55 if err != nil { 56 return err 57 } 58 if deleteSource { 59 defer os.Remove(src) 60 } 61 62 // Begin the file copy 63 dst := data.Get("destination").(string) 64 resultCh := make(chan error, 1) 65 go func() { 66 resultCh <- copyFiles(comm, src, dst) 67 }() 68 69 // Allow the file copy to complete unless there is an interrupt. 70 // If there is an interrupt we make no attempt to cleanly close 71 // the connection currently. We just abruptly exit. Because Terraform 72 // taints the resource, this is fine. 73 select { 74 case err := <-resultCh: 75 return err 76 case <-ctx.Done(): 77 return fmt.Errorf("file transfer interrupted") 78 } 79 } 80 81 func validateFn(d *schema.ResourceData) (ws []string, es []error) { 82 numSrc := 0 83 if _, ok := d.GetOk("source"); ok == true { 84 numSrc++ 85 } 86 if _, ok := d.GetOk("content"); ok == true { 87 numSrc++ 88 } 89 if numSrc != 1 { 90 es = append(es, fmt.Errorf("Must provide one of 'content' or 'source'")) 91 } 92 return 93 } 94 95 // getSrc returns the file to use as source 96 func getSrc(data *schema.ResourceData) (string, bool, error) { 97 src := data.Get("source").(string) 98 if content, ok := data.GetOk("content"); ok { 99 file, err := ioutil.TempFile("", "tf-file-content") 100 if err != nil { 101 return "", true, err 102 } 103 104 if _, err = file.WriteString(content.(string)); err != nil { 105 return "", true, err 106 } 107 108 return file.Name(), true, nil 109 } 110 111 expansion, err := homedir.Expand(src) 112 return expansion, false, err 113 } 114 115 // copyFiles is used to copy the files from a source to a destination 116 func copyFiles(comm communicator.Communicator, src, dst string) error { 117 // Wait and retry until we establish the connection 118 err := retryFunc(comm.Timeout(), func() error { 119 err := comm.Connect(nil) 120 return err 121 }) 122 if err != nil { 123 return err 124 } 125 defer comm.Disconnect() 126 127 info, err := os.Stat(src) 128 if err != nil { 129 return err 130 } 131 132 // If we're uploading a directory, short circuit and do that 133 if info.IsDir() { 134 if err := comm.UploadDir(dst, src); err != nil { 135 return fmt.Errorf("Upload failed: %v", err) 136 } 137 return nil 138 } 139 140 // We're uploading a file... 141 f, err := os.Open(src) 142 if err != nil { 143 return err 144 } 145 defer f.Close() 146 147 err = comm.Upload(dst, f) 148 if err != nil { 149 return fmt.Errorf("Upload failed: %v", err) 150 } 151 return err 152 } 153 154 // retryFunc is used to retry a function for a given duration 155 func retryFunc(timeout time.Duration, f func() error) error { 156 finish := time.After(timeout) 157 for { 158 err := f() 159 if err == nil { 160 return nil 161 } 162 log.Printf("Retryable error: %v", err) 163 164 select { 165 case <-finish: 166 return err 167 case <-time.After(3 * time.Second): 168 } 169 } 170 }