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 }