github.com/ddnomad/packer@v1.3.2/provisioner/file/provisioner.go (about) 1 package file 2 3 import ( 4 "errors" 5 "fmt" 6 "io" 7 "os" 8 "path/filepath" 9 "strings" 10 11 "github.com/hashicorp/packer/common" 12 "github.com/hashicorp/packer/helper/config" 13 "github.com/hashicorp/packer/packer" 14 "github.com/hashicorp/packer/template/interpolate" 15 ) 16 17 type Config struct { 18 common.PackerConfig `mapstructure:",squash"` 19 20 // The local path of the file to upload. 21 Source string 22 Sources []string 23 24 // The remote path where the local file will be uploaded to. 25 Destination string 26 27 // Direction 28 Direction string 29 30 // False if the sources have to exist. 31 Generated bool 32 33 ctx interpolate.Context 34 } 35 36 type Provisioner struct { 37 config Config 38 } 39 40 func (p *Provisioner) Prepare(raws ...interface{}) error { 41 err := config.Decode(&p.config, &config.DecodeOpts{ 42 Interpolate: true, 43 InterpolateContext: &p.config.ctx, 44 InterpolateFilter: &interpolate.RenderFilter{ 45 Exclude: []string{}, 46 }, 47 }, raws...) 48 if err != nil { 49 return err 50 } 51 52 if p.config.Direction == "" { 53 p.config.Direction = "upload" 54 } 55 56 var errs *packer.MultiError 57 58 if p.config.Direction != "download" && p.config.Direction != "upload" { 59 errs = packer.MultiErrorAppend(errs, 60 errors.New("Direction must be one of: download, upload.")) 61 } 62 if p.config.Source != "" { 63 p.config.Sources = append(p.config.Sources, p.config.Source) 64 } 65 66 if p.config.Direction == "upload" { 67 for _, src := range p.config.Sources { 68 if _, err := os.Stat(src); p.config.Generated == false && err != nil { 69 errs = packer.MultiErrorAppend(errs, 70 fmt.Errorf("Bad source '%s': %s", src, err)) 71 } 72 } 73 } 74 75 if len(p.config.Sources) < 1 { 76 errs = packer.MultiErrorAppend(errs, 77 errors.New("Source must be specified.")) 78 } 79 80 if p.config.Destination == "" { 81 errs = packer.MultiErrorAppend(errs, 82 errors.New("Destination must be specified.")) 83 } 84 85 if errs != nil && len(errs.Errors) > 0 { 86 return errs 87 } 88 89 return nil 90 } 91 92 func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error { 93 if p.config.Direction == "download" { 94 return p.ProvisionDownload(ui, comm) 95 } else { 96 return p.ProvisionUpload(ui, comm) 97 } 98 } 99 100 func (p *Provisioner) ProvisionDownload(ui packer.Ui, comm packer.Communicator) error { 101 for _, src := range p.config.Sources { 102 dst := p.config.Destination 103 ui.Say(fmt.Sprintf("Downloading %s => %s", src, dst)) 104 // ensure destination dir exists. p.config.Destination may either be a file or a dir. 105 dir := dst 106 // if it doesn't end with a /, set dir as the parent dir 107 if !strings.HasSuffix(dst, "/") { 108 dir = filepath.Dir(dir) 109 } else if !strings.HasSuffix(src, "/") && !strings.HasSuffix(src, "*") { 110 dst = filepath.Join(dst, filepath.Base(src)) 111 } 112 if dir != "" { 113 err := os.MkdirAll(dir, os.FileMode(0755)) 114 if err != nil { 115 return err 116 } 117 } 118 // if the src was a dir, download the dir 119 if strings.HasSuffix(src, "/") || strings.ContainsAny(src, "*?[") { 120 return comm.DownloadDir(src, dst, nil) 121 } 122 123 f, err := os.OpenFile(dst, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644) 124 if err != nil { 125 return err 126 } 127 defer f.Close() 128 129 // Get a default progress bar 130 pb := packer.NoopProgressBar{} 131 pb.Start(0) // TODO: find size ? Remove ? 132 defer pb.Finish() 133 134 // Create MultiWriter for the current progress 135 pf := io.MultiWriter(f) 136 137 // Download the file 138 if err = comm.Download(src, pf); err != nil { 139 ui.Error(fmt.Sprintf("Download failed: %s", err)) 140 return err 141 } 142 } 143 return nil 144 } 145 146 func (p *Provisioner) ProvisionUpload(ui packer.Ui, comm packer.Communicator) error { 147 for _, src := range p.config.Sources { 148 dst := p.config.Destination 149 150 ui.Say(fmt.Sprintf("Uploading %s => %s", src, dst)) 151 152 info, err := os.Stat(src) 153 if err != nil { 154 return err 155 } 156 157 // If we're uploading a directory, short circuit and do that 158 if info.IsDir() { 159 return comm.UploadDir(p.config.Destination, src, nil) 160 } 161 162 // We're uploading a file... 163 f, err := os.Open(src) 164 if err != nil { 165 return err 166 } 167 defer f.Close() 168 169 fi, err := f.Stat() 170 if err != nil { 171 return err 172 } 173 174 if strings.HasSuffix(dst, "/") { 175 dst = filepath.Join(dst, filepath.Base(src)) 176 } 177 178 // Get a default progress bar 179 bar := ui.ProgressBar() 180 bar.Start(info.Size()) 181 defer bar.Finish() 182 183 // Create ProxyReader for the current progress 184 pf := bar.NewProxyReader(f) 185 186 // Upload the file 187 if err = comm.Upload(dst, pf, &fi); err != nil { 188 if strings.Contains(err.Error(), "Error restoring file") { 189 ui.Error(fmt.Sprintf("Upload failed: %s; this can occur when "+ 190 "your file destination is a folder without a trailing "+ 191 "slash.", err)) 192 } 193 ui.Error(fmt.Sprintf("Upload failed: %s", err)) 194 return err 195 } 196 } 197 return nil 198 } 199 200 func (p *Provisioner) Cancel() { 201 // Just hard quit. It isn't a big deal if what we're doing keeps 202 // running on the other side. 203 os.Exit(0) 204 }