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