github.com/dahs81/otto@v0.2.1-0.20160126165905-6400716cf085/helper/packer/packer.go (about)

     1  package packer
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"os"
     8  	"os/exec"
     9  	"path/filepath"
    10  
    11  	"github.com/hashicorp/go-version"
    12  	"github.com/hashicorp/otto/context"
    13  	execHelper "github.com/hashicorp/otto/helper/exec"
    14  	"github.com/hashicorp/otto/helper/hashitools"
    15  	"github.com/hashicorp/otto/ui"
    16  )
    17  
    18  var (
    19  	packerMinVersion = version.Must(version.NewVersion("0.8.6"))
    20  )
    21  
    22  // Project returns the hashitools Project for this.
    23  func Project(ctx *context.Shared) *hashitools.Project {
    24  	return &hashitools.Project{
    25  		Name:       "packer",
    26  		MinVersion: packerMinVersion,
    27  		Installer: &hashitools.GoInstaller{
    28  			Name: "packer",
    29  			Dir:  filepath.Join(ctx.InstallDir),
    30  			Ui:   ctx.Ui,
    31  		},
    32  	}
    33  }
    34  
    35  // Packer wraps `packer` execution into an easy-to-use API
    36  type Packer struct {
    37  	// Path is the path to Packer itself. If empty, "packer"
    38  	// will be used and looked up via the PATH var.
    39  	Path string
    40  
    41  	// Dir is the working directory where all Packer commands are executed
    42  	Dir string
    43  
    44  	// Ui, if given, will be used to stream output from the Packer commands.
    45  	// If this is nil, then the output will be logged but won't be visible
    46  	// to the user.
    47  	Ui ui.Ui
    48  
    49  	// Callbacks is a list of callbacks that will be called for certain
    50  	// event types within the output
    51  	Callbacks map[string]OutputCallback
    52  
    53  	// Variables is a list of variables to pass to Packer.
    54  	Variables map[string]string
    55  }
    56  
    57  // Execute executes a raw Packer command.
    58  func (p *Packer) Execute(commandRaw ...string) error {
    59  	varfile, err := p.varfile()
    60  	if err != nil {
    61  		return err
    62  	}
    63  	if execHelper.ShouldCleanup() {
    64  		defer os.Remove(varfile)
    65  	}
    66  
    67  	// The command must always be machine-readable. We use this
    68  	// exclusively to mirror the UI output.
    69  	command := make([]string, len(commandRaw)+3)
    70  	command[0] = commandRaw[0]
    71  	command[1] = "-machine-readable"
    72  	command[2] = "-var-file"
    73  	command[3] = varfile
    74  	copy(command[4:], commandRaw[1:])
    75  
    76  	// Build the command to execute
    77  	path := "packer"
    78  	if p.Path != "" {
    79  		path = p.Path
    80  	}
    81  	cmd := exec.Command(path, command...)
    82  	cmd.Dir = p.Dir
    83  
    84  	// Build our custom UI that we'll use that'll call the registered
    85  	// callbacks as well as streaming data to the UI.
    86  	callbacks := make(map[string]OutputCallback)
    87  	callbacks["ui"] = p.uiCallback
    88  	for n, cb := range p.Callbacks {
    89  		callbacks[n] = cb
    90  	}
    91  	ui := &packerUi{Callbacks: callbacks}
    92  
    93  	// Execute!
    94  	err = execHelper.Run(ui, cmd)
    95  	ui.Finish()
    96  	if err != nil {
    97  		return fmt.Errorf(
    98  			"Error executing Packer: %s\n\n"+
    99  				"The error messages from Packer are usually very informative.\n"+
   100  				"Please read it carefully and fix any issues it mentions. If\n"+
   101  				"the message isn't clear, please report this to the Otto project.",
   102  			err)
   103  	}
   104  
   105  	return nil
   106  }
   107  
   108  func (p *Packer) uiCallback(o *Output) {
   109  	// If we don't have a UI return
   110  	if p.Ui == nil {
   111  		p.Ui = &ui.Logged{Ui: &ui.Null{}}
   112  	}
   113  
   114  	// Output the things to our own UI!
   115  	p.Ui.Raw(o.Data[1] + "\n")
   116  }
   117  
   118  func (p *Packer) varfile() (string, error) {
   119  	f, err := ioutil.TempFile("", "otto")
   120  	if err != nil {
   121  		return "", err
   122  	}
   123  
   124  	err = json.NewEncoder(f).Encode(p.Variables)
   125  	f.Close()
   126  	if err != nil {
   127  		os.Remove(f.Name())
   128  	}
   129  
   130  	return f.Name(), err
   131  }