github.com/StackPointCloud/packer@v0.10.2-0.20180716202532-b28098e0f79b/provisioner/file/provisioner.go (about)

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