github.com/spenczar/terraform@v0.6.9-0.20151213231906-86882e39edae/main.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"io/ioutil"
     7  	"log"
     8  	"os"
     9  	"sync"
    10  
    11  	"github.com/hashicorp/terraform/helper/logging"
    12  	"github.com/hashicorp/terraform/plugin"
    13  	"github.com/mitchellh/cli"
    14  	"github.com/mitchellh/panicwrap"
    15  	"github.com/mitchellh/prefixedio"
    16  )
    17  
    18  func main() {
    19  	os.Exit(realMain())
    20  }
    21  
    22  func realMain() int {
    23  	var wrapConfig panicwrap.WrapConfig
    24  
    25  	if !panicwrap.Wrapped(&wrapConfig) {
    26  		// Determine where logs should go in general (requested by the user)
    27  		logWriter, err := logging.LogOutput()
    28  		if err != nil {
    29  			fmt.Fprintf(os.Stderr, "Couldn't setup log output: %s", err)
    30  			return 1
    31  		}
    32  
    33  		// We always send logs to a temporary file that we use in case
    34  		// there is a panic. Otherwise, we delete it.
    35  		logTempFile, err := ioutil.TempFile("", "terraform-log")
    36  		if err != nil {
    37  			fmt.Fprintf(os.Stderr, "Couldn't setup logging tempfile: %s", err)
    38  			return 1
    39  		}
    40  		defer os.Remove(logTempFile.Name())
    41  		defer logTempFile.Close()
    42  
    43  		// Setup the prefixed readers that send data properly to
    44  		// stdout/stderr.
    45  		doneCh := make(chan struct{})
    46  		outR, outW := io.Pipe()
    47  		go copyOutput(outR, doneCh)
    48  
    49  		// Create the configuration for panicwrap and wrap our executable
    50  		wrapConfig.Handler = panicHandler(logTempFile)
    51  		wrapConfig.Writer = io.MultiWriter(logTempFile, logWriter)
    52  		wrapConfig.Stdout = outW
    53  		exitStatus, err := panicwrap.Wrap(&wrapConfig)
    54  		if err != nil {
    55  			fmt.Fprintf(os.Stderr, "Couldn't start Terraform: %s", err)
    56  			return 1
    57  		}
    58  
    59  		// If >= 0, we're the parent, so just exit
    60  		if exitStatus >= 0 {
    61  			// Close the stdout writer so that our copy process can finish
    62  			outW.Close()
    63  
    64  			// Wait for the output copying to finish
    65  			<-doneCh
    66  
    67  			return exitStatus
    68  		}
    69  
    70  		// We're the child, so just close the tempfile we made in order to
    71  		// save file handles since the tempfile is only used by the parent.
    72  		logTempFile.Close()
    73  	}
    74  
    75  	// Call the real main
    76  	return wrappedMain()
    77  }
    78  
    79  func wrappedMain() int {
    80  	log.SetOutput(os.Stderr)
    81  	log.Printf(
    82  		"[INFO] Terraform version: %s %s %s",
    83  		Version, VersionPrerelease, GitCommit)
    84  
    85  	// Load the configuration
    86  	config := BuiltinConfig
    87  	if err := config.Discover(); err != nil {
    88  		Ui.Error(fmt.Sprintf("Error discovering plugins: %s", err))
    89  		return 1
    90  	}
    91  
    92  	// Run checkpoint
    93  	go runCheckpoint(&config)
    94  
    95  	// Make sure we clean up any managed plugins at the end of this
    96  	defer plugin.CleanupClients()
    97  
    98  	// Get the command line args. We shortcut "--version" and "-v" to
    99  	// just show the version.
   100  	args := os.Args[1:]
   101  	for _, arg := range args {
   102  		if arg == "-v" || arg == "-version" || arg == "--version" {
   103  			newArgs := make([]string, len(args)+1)
   104  			newArgs[0] = "version"
   105  			copy(newArgs[1:], args)
   106  			args = newArgs
   107  			break
   108  		}
   109  	}
   110  
   111  	cli := &cli.CLI{
   112  		Args:       args,
   113  		Commands:   Commands,
   114  		HelpFunc:   cli.BasicHelpFunc("terraform"),
   115  		HelpWriter: os.Stdout,
   116  	}
   117  
   118  	// Load the configuration file if we have one, that can be used to
   119  	// define extra providers and provisioners.
   120  	clicfgFile, err := cliConfigFile()
   121  	if err != nil {
   122  		Ui.Error(fmt.Sprintf("Error loading CLI configuration: \n\n%s", err))
   123  		return 1
   124  	}
   125  
   126  	if clicfgFile != "" {
   127  		usrcfg, err := LoadConfig(clicfgFile)
   128  		if err != nil {
   129  			Ui.Error(fmt.Sprintf("Error loading CLI configuration: \n\n%s", err))
   130  			return 1
   131  		}
   132  
   133  		config = *config.Merge(usrcfg)
   134  	}
   135  
   136  	// Initialize the TFConfig settings for the commands...
   137  	ContextOpts.Providers = config.ProviderFactories()
   138  	ContextOpts.Provisioners = config.ProvisionerFactories()
   139  
   140  	exitCode, err := cli.Run()
   141  	if err != nil {
   142  		Ui.Error(fmt.Sprintf("Error executing CLI: %s", err.Error()))
   143  		return 1
   144  	}
   145  
   146  	return exitCode
   147  }
   148  
   149  func cliConfigFile() (string, error) {
   150  	mustExist := true
   151  	configFilePath := os.Getenv("TERRAFORM_CONFIG")
   152  	if configFilePath == "" {
   153  		var err error
   154  		configFilePath, err = ConfigFile()
   155  		mustExist = false
   156  
   157  		if err != nil {
   158  			log.Printf(
   159  				"[ERROR] Error detecting default CLI config file path: %s",
   160  				err)
   161  		}
   162  	}
   163  
   164  	log.Printf("[DEBUG] Attempting to open CLI config file: %s", configFilePath)
   165  	f, err := os.Open(configFilePath)
   166  	if err == nil {
   167  		f.Close()
   168  		return configFilePath, nil
   169  	}
   170  
   171  	if mustExist || !os.IsNotExist(err) {
   172  		return "", err
   173  	}
   174  
   175  	log.Println("[DEBUG] File doesn't exist, but doesn't need to. Ignoring.")
   176  	return "", nil
   177  }
   178  
   179  // copyOutput uses output prefixes to determine whether data on stdout
   180  // should go to stdout or stderr. This is due to panicwrap using stderr
   181  // as the log and error channel.
   182  func copyOutput(r io.Reader, doneCh chan<- struct{}) {
   183  	defer close(doneCh)
   184  
   185  	pr, err := prefixedio.NewReader(r)
   186  	if err != nil {
   187  		panic(err)
   188  	}
   189  
   190  	stderrR, err := pr.Prefix(ErrorPrefix)
   191  	if err != nil {
   192  		panic(err)
   193  	}
   194  	stdoutR, err := pr.Prefix(OutputPrefix)
   195  	if err != nil {
   196  		panic(err)
   197  	}
   198  	defaultR, err := pr.Prefix("")
   199  	if err != nil {
   200  		panic(err)
   201  	}
   202  
   203  	var wg sync.WaitGroup
   204  	wg.Add(3)
   205  	go func() {
   206  		defer wg.Done()
   207  		io.Copy(os.Stderr, stderrR)
   208  	}()
   209  	go func() {
   210  		defer wg.Done()
   211  		io.Copy(os.Stdout, stdoutR)
   212  	}()
   213  	go func() {
   214  		defer wg.Done()
   215  		io.Copy(os.Stdout, defaultR)
   216  	}()
   217  
   218  	wg.Wait()
   219  }