// Outputs reads the outputs from the configured directory storage. func (t *Terraform) Outputs() (map[string]string, error) { // Make a temporary file to store our state tf, err := ioutil.TempFile("", "otto-tf") if err != nil { return nil, err } if execHelper.ShouldCleanup() { defer os.Remove(tf.Name()) } // Read the state from the directory and put it on disk. Lots of // careful management of file handles here. data, err := t.Directory.GetBlob(t.StateId) if err == nil { if data == nil { return nil, nil } _, err = io.Copy(tf, data.Data) data.Close() } tf.Close() if err != nil { return nil, fmt.Errorf("Error loading Terraform state: %s", err) } // Read the outputs as normal. Defers will clean up our temp file. return Outputs(tf.Name()) }
// Execute executes a raw Packer command. func (p *Packer) Execute(commandRaw ...string) error { varfile, err := p.varfile() if err != nil { return err } if execHelper.ShouldCleanup() { defer os.Remove(varfile) } // The command must always be machine-readable. We use this // exclusively to mirror the UI output. command := make([]string, len(commandRaw)+3) command[0] = commandRaw[0] command[1] = "-machine-readable" command[2] = "-var-file" command[3] = varfile copy(command[4:], commandRaw[1:]) // Build the command to execute path := "packer" if p.Path != "" { path = p.Path } cmd := exec.Command(path, command...) cmd.Dir = p.Dir // Build our custom UI that we'll use that'll call the registered // callbacks as well as streaming data to the UI. callbacks := make(map[string]OutputCallback) callbacks["ui"] = p.uiCallback for n, cb := range p.Callbacks { callbacks[n] = cb } ui := &packerUi{Callbacks: callbacks} // Execute! err = execHelper.Run(ui, cmd) ui.Finish() if err != nil { return fmt.Errorf( "Error executing Packer: %s\n\n"+ "The error messages from Packer are usually very informative.\n"+ "Please read it carefully and fix any issues it mentions. If\n"+ "the message isn't clear, please report this to the Otto project.", err) } return nil }
// Execute executes a raw Terraform command func (t *Terraform) Execute(commandRaw ...string) error { command := make([]string, 1, len(commandRaw)*2) command[0] = commandRaw[0] commandArgs := commandRaw[1:] // Determine if we need to skip var flags or not. varSkip := false varSkip = command[0] == "get" // If we have variables, create the var file if !varSkip && len(t.Variables) > 0 { varfile, err := t.varfile() if err != nil { return err } if execHelper.ShouldCleanup() { defer os.Remove(varfile) } // Append the varfile onto our command. command = append(command, "-var-file", varfile) // Log some of the vars we're using for k, _ := range t.Variables { log.Printf("[DEBUG] setting TF var: %s", k) } } // Determine if we need to skip state flags or not. This is just // hardcoded for now. stateSkip := false stateSkip = command[0] == "get" // Output needs state but not state-out; more hard-coding stateOutSkip := false stateOutSkip = command[0] == "output" // If we care about state, then setup the state directory and // load it up. var stateDir, statePath string if !stateSkip && t.StateId != "" && t.Directory != nil { var err error stateDir, err = ioutil.TempDir("", "otto-tf") if err != nil { return err } if execHelper.ShouldCleanup() { defer os.RemoveAll(stateDir) } // State path stateOldPath := filepath.Join(stateDir, "state.old") statePath = filepath.Join(stateDir, "state") // Load the state from the directory data, err := t.Directory.GetBlob(t.StateId) if err != nil { return fmt.Errorf("Error loading Terraform state: %s", err) } if data == nil && command[0] == "destroy" { // Destroy we can just execute, we don't need state return nil } if data != nil { err = data.WriteToFile(stateOldPath) data.Close() } if err != nil { return fmt.Errorf("Error writing Terraform state: %s", err) } // Append the state to the args command = append(command, "-state", stateOldPath) if !stateOutSkip { command = append(command, "-state-out", statePath) } } // Append all the final args command = append(command, commandArgs...) // Build the command to execute log.Printf("[DEBUG] executing terraform: %v", command) path := "terraform" if t.Path != "" { path = t.Path } cmd := exec.Command(path, command...) cmd.Dir = t.Dir // Start the Terraform command. If there is an error we just store // the error but can't exit yet because we have to store partial // state if there is any. err := execHelper.Run(t.Ui, cmd) if err != nil { err = fmt.Errorf("Error running Terraform: %s", err) } // Save the state file if we have it. if t.StateId != "" && t.Directory != nil && statePath != "" && !stateOutSkip { f, ferr := os.Open(statePath) if ferr != nil { return fmt.Errorf( "Error reading Terraform state for saving: %s", ferr) } // Store the state derr := t.Directory.PutBlob(t.StateId, &directory.BlobData{ Data: f, }) // Always close the file f.Close() // If we couldn't save the data, then note the error. This is a // _really_ bad error to get since there isn't a good way to // recover. For now, we just copy the state to the pwd and note // the user. if derr != nil { // TODO: copy state err = fmt.Errorf( "Failed to save Terraform state: %s\n\n"+ "This means that Otto was unable to store the state of your infrastructure.\n"+ "At this time, Otto doesn't support gracefully recovering from this\n"+ "scenario. The state should be in the path below. Please ask the\n"+ "community for assistance.", derr) } } return err }