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