// Prepares the signal handlers so that we handle interrupts properly. // The signal handler exists in a goroutine. func setupSignalHandlers(env packer.Environment) { ch := make(chan os.Signal, 1) signal.Notify(ch, os.Interrupt) go func() { <-ch log.Println("First interrupt. Ignoring, will let plugins handle...") <-ch log.Println("Second interrupt. Exiting now.") env.Ui().Error("Interrupt signal received twice. Forcefully exiting now.") // Force kill all the plugins plugin.CleanupClients() os.Exit(1) }() }
// Prepares the signal handlers so that we handle interrupts properly. // The signal handler exists in a goroutine. func setupSignalHandlers(env packer.Environment) { ch := make(chan os.Signal, 1) signal.Notify(ch, os.Interrupt) go func() { // First interrupt. We mostly ignore this because it allows the // plugins time to cleanup. <-ch log.Println("First interrupt. Ignoring to allow plugins to clean up.") // Second interrupt. Go down hard. <-ch log.Println("Second interrupt. Exiting now.") env.Ui().Error("Interrupt signal received twice. Forcefully exiting now.") // Force kill all the plugins plugin.CleanupClients() os.Exit(1) }() }
func (c Command) Run(env packer.Environment, args []string) int { var cfgSyntaxOnly bool buildOptions := new(cmdcommon.BuildOptions) cmdFlags := flag.NewFlagSet("validate", flag.ContinueOnError) cmdFlags.Usage = func() { env.Ui().Say(c.Help()) } cmdFlags.BoolVar(&cfgSyntaxOnly, "syntax-only", false, "check syntax only") cmdcommon.BuildOptionFlags(cmdFlags, buildOptions) if err := cmdFlags.Parse(args); err != nil { return 1 } args = cmdFlags.Args() if len(args) != 1 { cmdFlags.Usage() return 1 } if err := buildOptions.Validate(); err != nil { env.Ui().Error(err.Error()) env.Ui().Error("") env.Ui().Error(c.Help()) return 1 } userVars, err := buildOptions.AllUserVars() if err != nil { env.Ui().Error(fmt.Sprintf("Error compiling user variables: %s", err)) env.Ui().Error("") env.Ui().Error(c.Help()) return 1 } // Parse the template into a machine-usable format log.Printf("Reading template: %s", args[0]) tpl, err := packer.ParseTemplateFile(args[0], userVars) if err != nil { env.Ui().Error(fmt.Sprintf("Failed to parse template: %s", err)) return 1 } if cfgSyntaxOnly { env.Ui().Say("Syntax-only check passed. Everything looks okay.") return 0 } errs := make([]error, 0) warnings := make(map[string][]string) // The component finder for our builds components := &packer.ComponentFinder{ Builder: env.Builder, Hook: env.Hook, PostProcessor: env.PostProcessor, Provisioner: env.Provisioner, } // Otherwise, get all the builds builds, err := buildOptions.Builds(tpl, components) if err != nil { env.Ui().Error(err.Error()) return 1 } // Check the configuration of all builds for _, b := range builds { log.Printf("Preparing build: %s", b.Name()) warns, err := b.Prepare() if len(warns) > 0 { warnings[b.Name()] = warns } if err != nil { errs = append(errs, fmt.Errorf("Errors validating build '%s'. %s", b.Name(), err)) } } if len(errs) > 0 { env.Ui().Error("Template validation failed. Errors are shown below.\n") for i, err := range errs { env.Ui().Error(err.Error()) if (i + 1) < len(errs) { env.Ui().Error("") } } return 1 } if len(warnings) > 0 { env.Ui().Say("Template validation succeeded, but there were some warnings.") env.Ui().Say("These are ONLY WARNINGS, and Packer will attempt to build the") env.Ui().Say("template despite them, but they should be paid attention to.\n") for build, warns := range warnings { env.Ui().Say(fmt.Sprintf("Warnings for build '%s':\n", build)) for _, warning := range warns { env.Ui().Say(fmt.Sprintf("* %s", warning)) } } return 0 } env.Ui().Say("Template validated successfully.") return 0 }
func (c Command) Run(env packer.Environment, args []string) int { var cfgDebug bool var cfgExcept []string var cfgOnly []string cmdFlags := flag.NewFlagSet("build", flag.ContinueOnError) cmdFlags.Usage = func() { env.Ui().Say(c.Help()) } cmdFlags.BoolVar(&cfgDebug, "debug", false, "debug mode for builds") cmdFlags.Var((*stringSliceValue)(&cfgExcept), "except", "build all builds except these") cmdFlags.Var((*stringSliceValue)(&cfgOnly), "only", "only build the given builds by name") if err := cmdFlags.Parse(args); err != nil { return 1 } args = cmdFlags.Args() if len(args) != 1 { cmdFlags.Usage() return 1 } if len(cfgOnly) > 0 && len(cfgExcept) > 0 { env.Ui().Error("Only one of '-except' or '-only' may be specified.\n") env.Ui().Error(c.Help()) return 1 } // Read the file into a byte array so that we can parse the template log.Printf("Reading template: %s", args[0]) tplData, err := ioutil.ReadFile(args[0]) if err != nil { env.Ui().Error(fmt.Sprintf("Failed to read template file: %s", err)) return 1 } // Parse the template into a machine-usable format log.Println("Parsing template...") tpl, err := packer.ParseTemplate(tplData) if err != nil { env.Ui().Error(fmt.Sprintf("Failed to parse template: %s", err)) return 1 } // The component finder for our builds components := &packer.ComponentFinder{ Builder: env.Builder, Hook: env.Hook, PostProcessor: env.PostProcessor, Provisioner: env.Provisioner, } // Go through each builder and compile the builds that we care about buildNames := tpl.BuildNames() builds := make([]packer.Build, 0, len(buildNames)) for _, buildName := range buildNames { if len(cfgExcept) > 0 { found := false for _, only := range cfgExcept { if buildName == only { found = true break } } if found { log.Printf("Skipping build '%s' because specified by -except.", buildName) continue } } if len(cfgOnly) > 0 { found := false for _, only := range cfgOnly { if buildName == only { found = true break } } if !found { log.Printf("Skipping build '%s' because not specified by -only.", buildName) continue } } log.Printf("Creating build: %s", buildName) build, err := tpl.Build(buildName, components) if err != nil { env.Ui().Error(fmt.Sprintf("Failed to create build '%s': \n\n%s", buildName, err)) return 1 } builds = append(builds, build) } if cfgDebug { env.Ui().Say("Debug mode enabled. Builds will not be parallelized.") } // Compile all the UIs for the builds colors := [5]packer.UiColor{ packer.UiColorGreen, packer.UiColorCyan, packer.UiColorMagenta, packer.UiColorYellow, packer.UiColorBlue, } buildUis := make(map[string]packer.Ui) for i, b := range builds { ui := &packer.ColoredUi{ Color: colors[i%len(colors)], Ui: env.Ui(), } buildUis[b.Name()] = ui ui.Say(fmt.Sprintf("%s output will be in this color.", b.Name())) } // Add a newline between the color output and the actual output env.Ui().Say("") log.Printf("Build debug mode: %v", cfgDebug) // Set the debug mode and prepare all the builds for _, b := range builds { log.Printf("Preparing build: %s", b.Name()) b.SetDebug(cfgDebug) err := b.Prepare() if err != nil { env.Ui().Error(err.Error()) return 1 } } // Run all the builds in parallel and wait for them to complete var interruptWg, wg sync.WaitGroup interrupted := false artifacts := make(map[string][]packer.Artifact) errors := make(map[string]error) for _, b := range builds { // Increment the waitgroup so we wait for this item to finish properly wg.Add(1) // Handle interrupts for this build sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, os.Interrupt) defer signal.Stop(sigCh) go func(b packer.Build) { <-sigCh interruptWg.Add(1) defer interruptWg.Done() interrupted = true log.Printf("Stopping build: %s", b.Name()) b.Cancel() log.Printf("Build cancelled: %s", b.Name()) }(b) // Run the build in a goroutine go func(b packer.Build) { defer wg.Done() name := b.Name() log.Printf("Starting build run: %s", name) ui := buildUis[name] runArtifacts, err := b.Run(ui, env.Cache()) if err != nil { ui.Error(fmt.Sprintf("Build '%s' errored: %s", name, err)) errors[name] = err } else { ui.Say(fmt.Sprintf("Build '%s' finished.", name)) artifacts[name] = runArtifacts } }(b) if cfgDebug { log.Printf("Debug enabled, so waiting for build to finish: %s", b.Name()) wg.Wait() } if interrupted { log.Println("Interrupted, not going to start any more builds.") break } } // Wait for both the builds to complete and the interrupt handler, // if it is interrupted. log.Printf("Waiting on builds to complete...") wg.Wait() log.Printf("Builds completed. Waiting on interrupt barrier...") interruptWg.Wait() if interrupted { env.Ui().Say("Cleanly cancelled builds after being interrupted.") return 1 } if len(errors) > 0 { env.Ui().Error("\n==> Some builds didn't complete successfully and had errors:") for name, err := range errors { env.Ui().Error(fmt.Sprintf("--> %s: %s", name, err)) } } if len(artifacts) > 0 { env.Ui().Say("\n==> Builds finished. The artifacts of successful builds are:") for name, buildArtifacts := range artifacts { for _, artifact := range buildArtifacts { var message bytes.Buffer fmt.Fprintf(&message, "--> %s: ", name) if artifact != nil { fmt.Fprintf(&message, artifact.String()) } else { fmt.Fprint(&message, "<nothing>") } env.Ui().Say(message.String()) } } } else { env.Ui().Say("\n==> Builds finished but no artifacts were created.") } return 0 }
func (c Command) Run(env packer.Environment, args []string) int { var cfgSyntaxOnly bool cmdFlags := flag.NewFlagSet("validate", flag.ContinueOnError) cmdFlags.Usage = func() { env.Ui().Say(c.Help()) } cmdFlags.BoolVar(&cfgSyntaxOnly, "syntax-only", false, "check syntax only") if err := cmdFlags.Parse(args); err != nil { return 1 } args = cmdFlags.Args() if len(args) != 1 { cmdFlags.Usage() return 1 } // Read the file into a byte array so that we can parse the template log.Printf("Reading template: %s", args[0]) tplData, err := ioutil.ReadFile(args[0]) if err != nil { env.Ui().Error(fmt.Sprintf("Failed to read template file: %s", err)) return 1 } // Parse the template into a machine-usable format log.Println("Parsing template...") tpl, err := packer.ParseTemplate(tplData) if err != nil { env.Ui().Error(fmt.Sprintf("Failed to parse template: %s", err)) return 1 } if cfgSyntaxOnly { env.Ui().Say("Syntax-only check passed. Everything looks okay.") return 0 } errs := make([]error, 0) // The component finder for our builds components := &packer.ComponentFinder{ Builder: env.Builder, Hook: env.Hook, PostProcessor: env.PostProcessor, Provisioner: env.Provisioner, } // Otherwise, get all the builds buildNames := tpl.BuildNames() builds := make([]packer.Build, 0, len(buildNames)) for _, buildName := range buildNames { log.Printf("Creating build from template for: %s", buildName) build, err := tpl.Build(buildName, components) if err != nil { errs = append(errs, fmt.Errorf("Build '%s': %s", buildName, err)) continue } builds = append(builds, build) } // Check the configuration of all builds for _, b := range builds { log.Printf("Preparing build: %s", b.Name()) err := b.Prepare() if err != nil { errs = append(errs, fmt.Errorf("Errors validating build '%s'. %s", b.Name(), err)) } } if len(errs) > 0 { env.Ui().Error("Template validation failed. Errors are shown below.\n") for i, err := range errs { env.Ui().Error(err.Error()) if (i + 1) < len(errs) { env.Ui().Error("") } } return 1 } env.Ui().Say("Template validated successfully.") return 0 }
func (c Command) Run(env packer.Environment, args []string) int { var cfgDebug bool var cfgForce bool buildOptions := new(cmdcommon.BuildOptions) cmdFlags := flag.NewFlagSet("build", flag.ContinueOnError) cmdFlags.Usage = func() { env.Ui().Say(c.Help()) } cmdFlags.BoolVar(&cfgDebug, "debug", false, "debug mode for builds") cmdFlags.BoolVar(&cfgForce, "force", false, "force a build if artifacts exist") cmdcommon.BuildOptionFlags(cmdFlags, buildOptions) if err := cmdFlags.Parse(args); err != nil { return 1 } args = cmdFlags.Args() if len(args) != 1 { cmdFlags.Usage() return 1 } if err := buildOptions.Validate(); err != nil { env.Ui().Error(err.Error()) env.Ui().Error("") env.Ui().Error(c.Help()) return 1 } userVars, err := buildOptions.AllUserVars() if err != nil { env.Ui().Error(fmt.Sprintf("Error compiling user variables: %s", err)) env.Ui().Error("") env.Ui().Error(c.Help()) return 1 } // Read the file into a byte array so that we can parse the template log.Printf("Reading template: %s", args[0]) tpl, err := packer.ParseTemplateFile(args[0]) if err != nil { env.Ui().Error(fmt.Sprintf("Failed to parse template: %s", err)) return 1 } // The component finder for our builds components := &packer.ComponentFinder{ Builder: env.Builder, Hook: env.Hook, PostProcessor: env.PostProcessor, Provisioner: env.Provisioner, } // Go through each builder and compile the builds that we care about builds, err := buildOptions.Builds(tpl, components) if err != nil { env.Ui().Error(err.Error()) return 1 } if cfgDebug { env.Ui().Say("Debug mode enabled. Builds will not be parallelized.") } // Compile all the UIs for the builds colors := [5]packer.UiColor{ packer.UiColorGreen, packer.UiColorCyan, packer.UiColorMagenta, packer.UiColorYellow, packer.UiColorBlue, } buildUis := make(map[string]packer.Ui) for i, b := range builds { ui := &packer.ColoredUi{ Color: colors[i%len(colors)], Ui: env.Ui(), } buildUis[b.Name()] = ui ui.Say(fmt.Sprintf("%s output will be in this color.", b.Name())) } // Add a newline between the color output and the actual output env.Ui().Say("") log.Printf("Build debug mode: %v", cfgDebug) log.Printf("Force build: %v", cfgForce) // Set the debug and force mode and prepare all the builds for _, b := range builds { log.Printf("Preparing build: %s", b.Name()) b.SetDebug(cfgDebug) b.SetForce(cfgForce) warnings, err := b.Prepare(userVars) if err != nil { env.Ui().Error(err.Error()) return 1 } if len(warnings) > 0 { ui := buildUis[b.Name()] ui.Say(fmt.Sprintf("Warnings for build '%s':\n", b.Name())) for _, warning := range warnings { ui.Say(fmt.Sprintf("* %s", warning)) } ui.Say("") } } // Run all the builds in parallel and wait for them to complete var interruptWg, wg sync.WaitGroup interrupted := false artifacts := make(map[string][]packer.Artifact) errors := make(map[string]error) for _, b := range builds { // Increment the waitgroup so we wait for this item to finish properly wg.Add(1) // Handle interrupts for this build sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, os.Interrupt) defer signal.Stop(sigCh) go func(b packer.Build) { <-sigCh interruptWg.Add(1) defer interruptWg.Done() interrupted = true log.Printf("Stopping build: %s", b.Name()) b.Cancel() log.Printf("Build cancelled: %s", b.Name()) }(b) // Run the build in a goroutine go func(b packer.Build) { defer wg.Done() name := b.Name() log.Printf("Starting build run: %s", name) ui := buildUis[name] runArtifacts, err := b.Run(ui, env.Cache()) if err != nil { ui.Error(fmt.Sprintf("Build '%s' errored: %s", name, err)) errors[name] = err } else { ui.Say(fmt.Sprintf("Build '%s' finished.", name)) artifacts[name] = runArtifacts } }(b) if cfgDebug { log.Printf("Debug enabled, so waiting for build to finish: %s", b.Name()) wg.Wait() } if interrupted { log.Println("Interrupted, not going to start any more builds.") break } } // Wait for both the builds to complete and the interrupt handler, // if it is interrupted. log.Printf("Waiting on builds to complete...") wg.Wait() log.Printf("Builds completed. Waiting on interrupt barrier...") interruptWg.Wait() if interrupted { env.Ui().Say("Cleanly cancelled builds after being interrupted.") return 1 } if len(errors) > 0 { env.Ui().Machine("error-count", strconv.FormatInt(int64(len(errors)), 10)) env.Ui().Error("\n==> Some builds didn't complete successfully and had errors:") for name, err := range errors { // Create a UI for the machine readable stuff to be targetted ui := &packer.TargettedUi{ Target: name, Ui: env.Ui(), } ui.Machine("error", err.Error()) env.Ui().Error(fmt.Sprintf("--> %s: %s", name, err)) } } if len(artifacts) > 0 { env.Ui().Say("\n==> Builds finished. The artifacts of successful builds are:") for name, buildArtifacts := range artifacts { // Create a UI for the machine readable stuff to be targetted ui := &packer.TargettedUi{ Target: name, Ui: env.Ui(), } // Machine-readable helpful ui.Machine("artifact-count", strconv.FormatInt(int64(len(buildArtifacts)), 10)) for i, artifact := range buildArtifacts { var message bytes.Buffer fmt.Fprintf(&message, "--> %s: ", name) if artifact != nil { fmt.Fprintf(&message, artifact.String()) } else { fmt.Fprint(&message, "<nothing>") } iStr := strconv.FormatInt(int64(i), 10) if artifact != nil { ui.Machine("artifact", iStr, "builder-id", artifact.BuilderId()) ui.Machine("artifact", iStr, "id", artifact.Id()) ui.Machine("artifact", iStr, "string", artifact.String()) files := artifact.Files() ui.Machine("artifact", iStr, "files-count", strconv.FormatInt(int64(len(files)), 10)) for fi, file := range files { fiStr := strconv.FormatInt(int64(fi), 10) ui.Machine("artifact", iStr, "file", fiStr, file) } } else { ui.Machine("artifact", iStr, "nil") } ui.Machine("artifact", iStr, "end") env.Ui().Say(message.String()) } } } else { env.Ui().Say("\n==> Builds finished but no artifacts were created.") } if len(errors) > 0 { // If any errors occurred, exit with a non-zero exit status return 1 } return 0 }
func (c Command) Run(env packer.Environment, args []string) int { flags := flag.NewFlagSet("inspect", flag.ContinueOnError) flags.Usage = func() { env.Ui().Say(c.Help()) } if err := flags.Parse(args); err != nil { return 1 } args = flags.Args() if len(args) != 1 { flags.Usage() return 1 } // Read the file into a byte array so that we can parse the template log.Printf("Reading template: %#v", args[0]) tpl, err := packer.ParseTemplateFile(args[0]) if err != nil { env.Ui().Error(fmt.Sprintf("Failed to parse template: %s", err)) return 1 } // Convenience... ui := env.Ui() // Variables ui.Say("Variables and their defaults:\n") if len(tpl.Variables) == 0 { ui.Say(" <No variables>") } else { keys := make([]string, 0, len(tpl.Variables)) max := 0 for k, _ := range tpl.Variables { keys = append(keys, k) if len(k) > max { max = len(k) } } sort.Strings(keys) for _, k := range keys { v := tpl.Variables[k] padding := strings.Repeat(" ", max-len(k)) output := fmt.Sprintf(" %s%s = %s", k, padding, v) ui.Machine("template-variable", k, v) ui.Say(output) } } ui.Say("") // Builders ui.Say("Builders:\n") if len(tpl.Builders) == 0 { ui.Say(" <No builders>") } else { keys := make([]string, 0, len(tpl.Builders)) max := 0 for k, _ := range tpl.Builders { keys = append(keys, k) if len(k) > max { max = len(k) } } sort.Strings(keys) for _, k := range keys { v := tpl.Builders[k] padding := strings.Repeat(" ", max-len(k)) output := fmt.Sprintf(" %s%s", k, padding) if v.Name != v.Type { output = fmt.Sprintf("%s (%s)", output, v.Type) } ui.Machine("template-builder", k, v.Type) ui.Say(output) } } ui.Say("") // Provisioners ui.Say("Provisioners:\n") if len(tpl.Provisioners) == 0 { ui.Say(" <No provisioners>") } else { for _, v := range tpl.Provisioners { ui.Machine("template-provisioner", v.Type) ui.Say(fmt.Sprintf(" %s", v.Type)) } } return 0 }
func (c Command) Run(env packer.Environment, args []string) int { flags := flag.NewFlagSet("inspect", flag.ContinueOnError) flags.Usage = func() { env.Ui().Say(c.Help()) } if err := flags.Parse(args); err != nil { return 1 } args = flags.Args() if len(args) != 1 { flags.Usage() return 1 } // Read the file into a byte array so that we can parse the template log.Printf("Reading template: %#v", args[0]) tpl, err := packer.ParseTemplateFile(args[0], nil) if err != nil { env.Ui().Error(fmt.Sprintf("Failed to parse template: %s", err)) return 1 } // Convenience... ui := env.Ui() // Description if tpl.Description != "" { ui.Say("Description:\n") ui.Say(tpl.Description + "\n") } // Variables if len(tpl.Variables) == 0 { ui.Say("Variables:\n") ui.Say(" <No variables>") } else { requiredHeader := false for k, v := range tpl.Variables { if v.Required { if !requiredHeader { requiredHeader = true ui.Say("Required variables:\n") } ui.Machine("template-variable", k, v.Default, "1") ui.Say(" " + k) } } if requiredHeader { ui.Say("") } ui.Say("Optional variables and their defaults:\n") keys := make([]string, 0, len(tpl.Variables)) max := 0 for k, _ := range tpl.Variables { keys = append(keys, k) if len(k) > max { max = len(k) } } sort.Strings(keys) for _, k := range keys { v := tpl.Variables[k] if v.Required { continue } padding := strings.Repeat(" ", max-len(k)) output := fmt.Sprintf(" %s%s = %s", k, padding, v.Default) ui.Machine("template-variable", k, v.Default, "0") ui.Say(output) } } ui.Say("") // Builders ui.Say("Builders:\n") if len(tpl.Builders) == 0 { ui.Say(" <No builders>") } else { keys := make([]string, 0, len(tpl.Builders)) max := 0 for k, _ := range tpl.Builders { keys = append(keys, k) if len(k) > max { max = len(k) } } sort.Strings(keys) for _, k := range keys { v := tpl.Builders[k] padding := strings.Repeat(" ", max-len(k)) output := fmt.Sprintf(" %s%s", k, padding) if v.Name != v.Type { output = fmt.Sprintf("%s (%s)", output, v.Type) } ui.Machine("template-builder", k, v.Type) ui.Say(output) } } ui.Say("") // Provisioners ui.Say("Provisioners:\n") if len(tpl.Provisioners) == 0 { ui.Say(" <No provisioners>") } else { for _, v := range tpl.Provisioners { ui.Machine("template-provisioner", v.Type) ui.Say(fmt.Sprintf(" %s", v.Type)) } } ui.Say("\nNote: If your build names contain user variables or template\n" + "functions such as 'timestamp', these are processed at build time,\n" + "and therefore only show in their raw form here.") return 0 }
func (c Command) Run(env packer.Environment, args []string) int { cmdFlags := flag.NewFlagSet("fix", flag.ContinueOnError) cmdFlags.Usage = func() { env.Ui().Say(c.Help()) } if err := cmdFlags.Parse(args); err != nil { return 1 } args = cmdFlags.Args() if len(args) != 1 { cmdFlags.Usage() return 1 } // Read the file for decoding tplF, err := os.Open(args[0]) if err != nil { env.Ui().Error(fmt.Sprintf("Error opening template: %s", err)) return 1 } defer tplF.Close() // Decode the JSON into a generic map structure var templateData map[string]interface{} decoder := json.NewDecoder(tplF) if err := decoder.Decode(&templateData); err != nil { env.Ui().Error(fmt.Sprintf("Error parsing template: %s", err)) return 1 } // Close the file since we're done with that tplF.Close() // Run the template through the various fixers fixers := []Fixer{Fixers["iso-md5"]} input := templateData for _, fixer := range fixers { var err error input, err = fixer.Fix(input) if err != nil { env.Ui().Error(fmt.Sprintf("Error fixing: %s", err)) return 1 } } var output bytes.Buffer encoder := json.NewEncoder(&output) if err := encoder.Encode(input); err != nil { env.Ui().Error(fmt.Sprintf("Error encoding: %s", err)) return 1 } var indented bytes.Buffer if err := json.Indent(&indented, output.Bytes(), "", " "); err != nil { env.Ui().Error(fmt.Sprintf("Error encoding: %s", err)) return 1 } result := indented.String() result = strings.Replace(result, `\u003c`, "<", -1) result = strings.Replace(result, `\u003e`, ">", -1) env.Ui().Say(result) return 0 }