github.com/phobos182/packer@v0.2.3-0.20130819023704-c84d2aeffc68/packer.go (about)

     1  // This is the main package for the `packer` application.
     2  package main
     3  
     4  import (
     5  	"bytes"
     6  	"fmt"
     7  	"github.com/mitchellh/packer/packer"
     8  	"github.com/mitchellh/packer/packer/plugin"
     9  	"github.com/mitchellh/panicwrap"
    10  	"io"
    11  	"io/ioutil"
    12  	"log"
    13  	"os"
    14  	"path/filepath"
    15  	"runtime"
    16  )
    17  
    18  func main() {
    19  	// Call realMain instead of doing the work here so we can use
    20  	// `defer` statements within the function and have them work properly.
    21  	// (defers aren't called with os.Exit)
    22  	os.Exit(realMain())
    23  }
    24  
    25  // realMain is executed from main and returns the exit status to exit with.
    26  func realMain() int {
    27  	// If there is no explicit number of Go threads to use, then set it
    28  	if os.Getenv("GOMAXPROCS") == "" {
    29  		runtime.GOMAXPROCS(runtime.NumCPU())
    30  	}
    31  
    32  	// Determine where logs should go in general (requested by the user)
    33  	logWriter, err := logOutput()
    34  	if err != nil {
    35  		fmt.Fprintf(os.Stderr, "Couldn't setup log output: %s", err)
    36  		return 1
    37  	}
    38  
    39  	// We also always send logs to a temporary file that we use in case
    40  	// there is a panic. Otherwise, we delete it.
    41  	logTempFile, err := ioutil.TempFile("", "packer-log")
    42  	if err != nil {
    43  		fmt.Fprintf(os.Stderr, "Couldn't setup logging tempfile: %s", err)
    44  		return 1
    45  	}
    46  	defer os.Remove(logTempFile.Name())
    47  	defer logTempFile.Close()
    48  
    49  	// Reset the log variables to minimize work in the subprocess
    50  	os.Setenv("PACKER_LOG", "")
    51  	os.Setenv("PACKER_LOG_FILE", "")
    52  
    53  	// Create the configuration for panicwrap and wrap our executable
    54  	wrapConfig := &panicwrap.WrapConfig{
    55  		Handler: panicHandler(logTempFile),
    56  		Writer:  io.MultiWriter(logTempFile, logWriter),
    57  	}
    58  
    59  	exitStatus, err := panicwrap.Wrap(wrapConfig)
    60  	if err != nil {
    61  		fmt.Fprintf(os.Stderr, "Couldn't start Packer: %s", err)
    62  		return 1
    63  	}
    64  
    65  	if exitStatus >= 0 {
    66  		return exitStatus
    67  	}
    68  
    69  	// We're the child, so just close the tempfile we made in order to
    70  	// save file handles since the tempfile is only used by the parent.
    71  	logTempFile.Close()
    72  
    73  	return wrappedMain()
    74  }
    75  
    76  // wrappedMain is called only when we're wrapped by panicwrap and
    77  // returns the exit status to exit with.
    78  func wrappedMain() int {
    79  	log.SetOutput(os.Stderr)
    80  
    81  	log.Printf(
    82  		"Packer Version: %s %s %s",
    83  		packer.Version, packer.VersionPrerelease, packer.GitCommit)
    84  	log.Printf("Packer Target OS/Arch: %s %s", runtime.GOOS, runtime.GOARCH)
    85  
    86  	// Prepare stdin for plugin usage by switching it to a pipe
    87  	setupStdin()
    88  
    89  	config, err := loadConfig()
    90  	if err != nil {
    91  		fmt.Fprintf(os.Stderr, "Error loading configuration: \n\n%s\n", err)
    92  		return 1
    93  	}
    94  
    95  	log.Printf("Packer config: %+v", config)
    96  
    97  	cacheDir := os.Getenv("PACKER_CACHE_DIR")
    98  	if cacheDir == "" {
    99  		cacheDir = "packer_cache"
   100  	}
   101  
   102  	cacheDir, err = filepath.Abs(cacheDir)
   103  	if err != nil {
   104  		fmt.Fprintf(os.Stderr, "Error preparing cache directory: \n\n%s\n", err)
   105  		return 1
   106  	}
   107  
   108  	if err := os.MkdirAll(cacheDir, 0755); err != nil {
   109  		fmt.Fprintf(os.Stderr, "Error preparing cache directory: \n\n%s\n", err)
   110  		return 1
   111  	}
   112  
   113  	log.Printf("Setting cache directory: %s", cacheDir)
   114  	cache := &packer.FileCache{CacheDir: cacheDir}
   115  
   116  	// Determine if we're in machine-readable mode by mucking around with
   117  	// the arguments...
   118  	args, machineReadable := extractMachineReadable(os.Args[1:])
   119  
   120  	defer plugin.CleanupClients()
   121  
   122  	// Create the environment configuration
   123  	envConfig := packer.DefaultEnvironmentConfig()
   124  	envConfig.Cache = cache
   125  	envConfig.Commands = config.CommandNames()
   126  	envConfig.Components.Builder = config.LoadBuilder
   127  	envConfig.Components.Command = config.LoadCommand
   128  	envConfig.Components.Hook = config.LoadHook
   129  	envConfig.Components.PostProcessor = config.LoadPostProcessor
   130  	envConfig.Components.Provisioner = config.LoadProvisioner
   131  	if machineReadable {
   132  		envConfig.Ui = &packer.MachineReadableUi{
   133  			Writer: os.Stdout,
   134  		}
   135  	}
   136  
   137  	env, err := packer.NewEnvironment(envConfig)
   138  	if err != nil {
   139  		fmt.Fprintf(os.Stderr, "Packer initialization error: \n\n%s\n", err)
   140  		return 1
   141  	}
   142  
   143  	setupSignalHandlers(env)
   144  
   145  	exitCode, err := env.Cli(args)
   146  	if err != nil {
   147  		fmt.Fprintf(os.Stderr, "Error executing CLI: %s\n", err.Error())
   148  		return 1
   149  	}
   150  
   151  	return exitCode
   152  }
   153  
   154  // extractMachineReadable checks the args for the machine readable
   155  // flag and returns whether or not it is on. It modifies the args
   156  // to remove this flag.
   157  func extractMachineReadable(args []string) ([]string, bool) {
   158  	for i, arg := range args {
   159  		if arg == "-machine-readable" {
   160  			// We found it. Slice it out.
   161  			result := make([]string, len(args)-1)
   162  			copy(result, args[:i])
   163  			copy(result[i:], args[i+1:])
   164  			return result, true
   165  		}
   166  	}
   167  
   168  	return args, false
   169  }
   170  
   171  func loadConfig() (*config, error) {
   172  	var config config
   173  	if err := decodeConfig(bytes.NewBufferString(defaultConfig), &config); err != nil {
   174  		return nil, err
   175  	}
   176  
   177  	mustExist := true
   178  	configFilePath := os.Getenv("PACKER_CONFIG")
   179  	if configFilePath == "" {
   180  		var err error
   181  		configFilePath, err = configFile()
   182  		mustExist = false
   183  
   184  		if err != nil {
   185  			log.Printf("Error detecting default config file path: %s", err)
   186  		}
   187  	}
   188  
   189  	if configFilePath == "" {
   190  		return &config, nil
   191  	}
   192  
   193  	log.Printf("Attempting to open config file: %s", configFilePath)
   194  	f, err := os.Open(configFilePath)
   195  	if err != nil {
   196  		if !os.IsNotExist(err) {
   197  			return nil, err
   198  		}
   199  
   200  		if mustExist {
   201  			return nil, err
   202  		}
   203  
   204  		log.Println("File doesn't exist, but doesn't need to. Ignoring.")
   205  		return &config, nil
   206  	}
   207  	defer f.Close()
   208  
   209  	if err := decodeConfig(f, &config); err != nil {
   210  		return nil, err
   211  	}
   212  
   213  	return &config, nil
   214  }
   215  
   216  // logOutput determines where we should send logs (if anywhere).
   217  func logOutput() (logOutput io.Writer, err error) {
   218  	logOutput = ioutil.Discard
   219  	if os.Getenv("PACKER_LOG") != "" {
   220  		logOutput = os.Stderr
   221  
   222  		if logPath := os.Getenv("PACKER_LOG_PATH"); logPath != "" {
   223  			var err error
   224  			logOutput, err = os.Create(logPath)
   225  			if err != nil {
   226  				return nil, err
   227  			}
   228  		}
   229  	}
   230  
   231  	return
   232  }