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