gopkg.in/hashicorp/packer.v1@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  }