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