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 }