github.com/bhameyie/otto@v0.2.1-0.20160406174117-16052efa52ec/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 }