github.com/tonnydourado/packer@v0.6.1-0.20140701134019-5d0cd9676a37/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  	log.Printf("Built with Go Version: %s", runtime.Version())
    86  
    87  	// Prepare stdin for plugin usage by switching it to a pipe
    88  	setupStdin()
    89  
    90  	config, err := loadConfig()
    91  	if err != nil {
    92  		fmt.Fprintf(os.Stderr, "Error loading configuration: \n\n%s\n", err)
    93  		return 1
    94  	}
    95  
    96  	log.Printf("Packer config: %+v", config)
    97  
    98  	cacheDir := os.Getenv("PACKER_CACHE_DIR")
    99  	if cacheDir == "" {
   100  		cacheDir = "packer_cache"
   101  	}
   102  
   103  	cacheDir, err = filepath.Abs(cacheDir)
   104  	if err != nil {
   105  		fmt.Fprintf(os.Stderr, "Error preparing cache directory: \n\n%s\n", err)
   106  		return 1
   107  	}
   108  
   109  	log.Printf("Setting cache directory: %s", cacheDir)
   110  	cache := &packer.FileCache{CacheDir: cacheDir}
   111  
   112  	// Determine if we're in machine-readable mode by mucking around with
   113  	// the arguments...
   114  	args, machineReadable := extractMachineReadable(os.Args[1:])
   115  
   116  	defer plugin.CleanupClients()
   117  
   118  	// Create the environment configuration
   119  	envConfig := packer.DefaultEnvironmentConfig()
   120  	envConfig.Cache = cache
   121  	envConfig.Commands = config.CommandNames()
   122  	envConfig.Components.Builder = config.LoadBuilder
   123  	envConfig.Components.Command = config.LoadCommand
   124  	envConfig.Components.Hook = config.LoadHook
   125  	envConfig.Components.PostProcessor = config.LoadPostProcessor
   126  	envConfig.Components.Provisioner = config.LoadProvisioner
   127  	if machineReadable {
   128  		envConfig.Ui = &packer.MachineReadableUi{
   129  			Writer: os.Stdout,
   130  		}
   131  
   132  		// Set this so that we don't get colored output in our machine-
   133  		// readable UI.
   134  		if err := os.Setenv("PACKER_NO_COLOR", "1"); err != nil {
   135  			fmt.Fprintf(os.Stderr, "Packer failed to initialize UI: %s\n", err)
   136  			return 1
   137  		}
   138  	}
   139  
   140  	env, err := packer.NewEnvironment(envConfig)
   141  	if err != nil {
   142  		fmt.Fprintf(os.Stderr, "Packer initialization error: \n\n%s\n", err)
   143  		return 1
   144  	}
   145  
   146  	setupSignalHandlers(env)
   147  
   148  	exitCode, err := env.Cli(args)
   149  	if err != nil {
   150  		fmt.Fprintf(os.Stderr, "Error executing CLI: %s\n", err.Error())
   151  		return 1
   152  	}
   153  
   154  	return exitCode
   155  }
   156  
   157  // extractMachineReadable checks the args for the machine readable
   158  // flag and returns whether or not it is on. It modifies the args
   159  // to remove this flag.
   160  func extractMachineReadable(args []string) ([]string, bool) {
   161  	for i, arg := range args {
   162  		if arg == "-machine-readable" {
   163  			// We found it. Slice it out.
   164  			result := make([]string, len(args)-1)
   165  			copy(result, args[:i])
   166  			copy(result[i:], args[i+1:])
   167  			return result, true
   168  		}
   169  	}
   170  
   171  	return args, false
   172  }
   173  
   174  func loadConfig() (*config, error) {
   175  	var config config
   176  	if err := decodeConfig(bytes.NewBufferString(defaultConfig), &config); err != nil {
   177  		return nil, err
   178  	}
   179  
   180  	mustExist := true
   181  	configFilePath := os.Getenv("PACKER_CONFIG")
   182  	if configFilePath == "" {
   183  		var err error
   184  		configFilePath, err = configFile()
   185  		mustExist = false
   186  
   187  		if err != nil {
   188  			log.Printf("Error detecting default config file path: %s", err)
   189  		}
   190  	}
   191  
   192  	if configFilePath == "" {
   193  		return &config, nil
   194  	}
   195  
   196  	log.Printf("Attempting to open config file: %s", configFilePath)
   197  	f, err := os.Open(configFilePath)
   198  	if err != nil {
   199  		if !os.IsNotExist(err) {
   200  			return nil, err
   201  		}
   202  
   203  		if mustExist {
   204  			return nil, err
   205  		}
   206  
   207  		log.Println("File doesn't exist, but doesn't need to. Ignoring.")
   208  		return &config, nil
   209  	}
   210  	defer f.Close()
   211  
   212  	if err := decodeConfig(f, &config); err != nil {
   213  		return nil, err
   214  	}
   215  
   216  	return &config, nil
   217  }
   218  
   219  // logOutput determines where we should send logs (if anywhere).
   220  func logOutput() (logOutput io.Writer, err error) {
   221  	logOutput = ioutil.Discard
   222  	if os.Getenv("PACKER_LOG") != "" {
   223  		logOutput = os.Stderr
   224  
   225  		if logPath := os.Getenv("PACKER_LOG_PATH"); logPath != "" {
   226  			var err error
   227  			logOutput, err = os.Create(logPath)
   228  			if err != nil {
   229  				return nil, err
   230  			}
   231  		}
   232  	}
   233  
   234  	return
   235  }