github.com/jdextraze/terraform@v0.6.17-0.20160511153921-e33847c8a8af/main.go (about)

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