Beispiel #1
0
func TestCore_pushInterpolate(t *testing.T) {
	cases := []struct {
		File   string
		Vars   map[string]string
		Result template.Push
	}{
		{
			"push-vars.json",
			map[string]string{"foo": "bar"},
			template.Push{Name: "bar"},
		},
	}

	for _, tc := range cases {
		tpl, err := template.ParseFile(fixtureDir(tc.File))
		if err != nil {
			t.Fatalf("err: %s\n\n%s", tc.File, err)
		}

		core, err := NewCore(&CoreConfig{
			Template:  tpl,
			Variables: tc.Vars,
		})
		if err != nil {
			t.Fatalf("err: %s\n\n%s", tc.File, err)
		}

		expected := core.Template.Push
		if !reflect.DeepEqual(expected, tc.Result) {
			t.Fatalf("err: %s\n\n%#v", tc.File, expected)
		}
	}
}
Beispiel #2
0
func testCoreTemplate(t *testing.T, c *CoreConfig, p string) {
	tpl, err := template.ParseFile(p)
	if err != nil {
		t.Fatalf("err: %s\n\n%s", p, err)
	}

	c.Template = tpl
}
Beispiel #3
0
func TestCoreBuildNames(t *testing.T) {
	cases := []struct {
		File   string
		Vars   map[string]string
		Result []string
	}{
		{
			"build-names-basic.json",
			nil,
			[]string{"something"},
		},

		{
			"build-names-func.json",
			nil,
			[]string{"TUBES"},
		},
	}

	for _, tc := range cases {
		tpl, err := template.ParseFile(fixtureDir(tc.File))
		if err != nil {
			t.Fatalf("err: %s\n\n%s", tc.File, err)
		}

		core, err := NewCore(&CoreConfig{
			Template:  tpl,
			Variables: tc.Vars,
		})
		if err != nil {
			t.Fatalf("err: %s\n\n%s", tc.File, err)
		}

		names := core.BuildNames()
		if !reflect.DeepEqual(names, tc.Result) {
			t.Fatalf("err: %s\n\n%#v", tc.File, names)
		}
	}
}
Beispiel #4
0
func (c BuildCommand) Run(args []string) int {
	var cfgColor, cfgDebug, cfgForce, cfgParallel bool
	var cfgOnError string
	flags := c.Meta.FlagSet("build", FlagSetBuildFilter|FlagSetVars)
	flags.Usage = func() { c.Ui.Say(c.Help()) }
	flags.BoolVar(&cfgColor, "color", true, "")
	flags.BoolVar(&cfgDebug, "debug", false, "")
	flags.BoolVar(&cfgForce, "force", false, "")
	flagOnError := enumflag.New(&cfgOnError, "cleanup", "abort", "ask")
	flags.Var(flagOnError, "on-error", "")
	flags.BoolVar(&cfgParallel, "parallel", true, "")
	if err := flags.Parse(args); err != nil {
		return 1
	}

	args = flags.Args()
	if len(args) != 1 {
		flags.Usage()
		return 1
	}

	// Parse the template
	var tpl *template.Template
	var err error
	tpl, err = template.ParseFile(args[0])
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Failed to parse template: %s", err))
		return 1
	}

	// Get the core
	core, err := c.Meta.Core(tpl)
	if err != nil {
		c.Ui.Error(err.Error())
		return 1
	}

	// Get the builds we care about
	buildNames := c.Meta.BuildNames(core)
	builds := make([]packer.Build, 0, len(buildNames))
	for _, n := range buildNames {
		b, err := core.Build(n)
		if err != nil {
			c.Ui.Error(fmt.Sprintf(
				"Failed to initialize build '%s': %s",
				n, err))
			continue
		}

		builds = append(builds, b)
	}

	if cfgDebug {
		c.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 buildNames {
		var ui packer.Ui
		ui = c.Ui
		if cfgColor {
			ui = &packer.ColoredUi{
				Color: colors[i%len(colors)],
				Ui:    ui,
			}
		}

		buildUis[b] = ui
		ui.Say(fmt.Sprintf("%s output will be in this color.", b))
	}

	// Add a newline between the color output and the actual output
	c.Ui.Say("")

	log.Printf("Build debug mode: %v", cfgDebug)
	log.Printf("Force build: %v", cfgForce)
	log.Printf("On error: %v", cfgOnError)

	// 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)
		b.SetOnError(cfgOnError)

		warnings, err := b.Prepare()
		if err != nil {
			c.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
	var artifacts = struct {
		sync.RWMutex
		m map[string][]packer.Artifact
	}{m: 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, c.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.Lock()
				artifacts.m[name] = runArtifacts
				artifacts.Unlock()
			}
		}(b)

		if cfgDebug {
			log.Printf("Debug enabled, so waiting for build to finish: %s", b.Name())
			wg.Wait()
		}

		if !cfgParallel {
			log.Printf("Parallelization disabled, 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 {
		c.Ui.Say("Cleanly cancelled builds after being interrupted.")
		return 1
	}

	if len(errors) > 0 {
		c.Ui.Machine("error-count", strconv.FormatInt(int64(len(errors)), 10))

		c.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:     c.Ui,
			}

			ui.Machine("error", err.Error())

			c.Ui.Error(fmt.Sprintf("--> %s: %s", name, err))
		}
	}

	if len(artifacts.m) > 0 {
		c.Ui.Say("\n==> Builds finished. The artifacts of successful builds are:")
		for name, buildArtifacts := range artifacts.m {
			// Create a UI for the machine readable stuff to be targetted
			ui := &packer.TargettedUi{
				Target: name,
				Ui:     c.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.Fprint(&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")
				c.Ui.Say(message.String())
			}
		}
	} else {
		c.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
}
Beispiel #5
0
func (c *InspectCommand) Run(args []string) int {
	flags := c.Meta.FlagSet("inspect", FlagSetNone)
	flags.Usage = func() { c.Ui.Say(c.Help()) }
	if err := flags.Parse(args); err != nil {
		return 1
	}

	args = flags.Args()
	if len(args) != 1 {
		flags.Usage()
		return 1
	}

	// Parse the template
	tpl, err := template.ParseFile(args[0])
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Failed to parse template: %s", err))
		return 1
	}

	// Convenience...
	ui := c.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
}
Beispiel #6
0
func (c *PushCommand) Run(args []string) int {
	var token string
	var message string
	var name string
	var create bool

	f := c.Meta.FlagSet("push", FlagSetVars)
	f.Usage = func() { c.Ui.Error(c.Help()) }
	f.StringVar(&token, "token", "", "token")
	f.StringVar(&message, "m", "", "message")
	f.StringVar(&message, "message", "", "message")
	f.StringVar(&name, "name", "", "name")
	f.BoolVar(&create, "create", false, "create (deprecated)")
	if err := f.Parse(args); err != nil {
		return 1
	}

	if message != "" {
		c.Ui.Say("[DEPRECATED] -m/-message is deprecated and will be removed in a future Packer release")
	}

	args = f.Args()
	if len(args) != 1 {
		f.Usage()
		return 1
	}

	// Print deprecations
	if create {
		c.Ui.Error(fmt.Sprintf("The '-create' option is now the default and is\n" +
			"longer used. It will be removed in the next version."))
	}

	// Parse the template
	tpl, err := template.ParseFile(args[0])
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Failed to parse template: %s", err))
		return 1
	}

	// Get the core
	core, err := c.Meta.Core(tpl)
	if err != nil {
		c.Ui.Error(err.Error())
		return 1
	}
	push := core.Template.Push

	// If we didn't pass name from the CLI, use the template
	if name == "" {
		name = push.Name
	}

	// Validate some things
	if name == "" {
		c.Ui.Error(fmt.Sprintf(
			"The 'push' section must be specified in the template with\n" +
				"at least the 'name' option set. Alternatively, you can pass the\n" +
				"name parameter from the CLI."))
		return 1
	}

	if !reName.MatchString(name) {
		c.Ui.Error(errInvalidName.Error())
		return 1
	}

	// Determine our token
	if token == "" {
		token = push.Token
	}

	// Build our client
	defer func() { c.client = nil }()
	c.client = atlas.DefaultClient()
	if push.Address != "" {
		c.client, err = atlas.NewClient(push.Address)
		if err != nil {
			c.Ui.Error(fmt.Sprintf(
				"Error setting up API client: %s", err))
			return 1
		}
	}
	if token != "" {
		c.client.Token = token
	}

	// Build the archiving options
	var opts archive.ArchiveOpts
	opts.Include = push.Include
	opts.Exclude = push.Exclude
	opts.VCS = push.VCS
	opts.Extra = map[string]string{
		archiveTemplateEntry: args[0],
	}

	// Determine the path we're archiving. This logic is a bit complicated
	// as there are three possibilities:
	//
	//   1.) BaseDir is an absolute path, just use that.
	//
	//   2.) BaseDir is empty, so we use the directory of the template.
	//
	//   3.) BaseDir is relative, so we use the path relative to the directory
	//       of the template.
	//
	path := push.BaseDir
	if path == "" || !filepath.IsAbs(path) {
		tplPath, err := filepath.Abs(args[0])
		if err != nil {
			c.Ui.Error(fmt.Sprintf("Error determining path to archive: %s", err))
			return 1
		}
		tplPath = filepath.Dir(tplPath)
		if path != "" {
			tplPath = filepath.Join(tplPath, path)
		}
		path, err = filepath.Abs(tplPath)
		if err != nil {
			c.Ui.Error(fmt.Sprintf("Error determining path to archive: %s", err))
			return 1
		}
	}

	// Find the Atlas post-processors, if possible
	var atlasPPs []*template.PostProcessor
	for _, list := range tpl.PostProcessors {
		for _, pp := range list {
			if pp.Type == "atlas" {
				atlasPPs = append(atlasPPs, pp)
			}
		}
	}

	// Build the upload options
	var uploadOpts uploadOpts
	uploadOpts.Slug = name
	uploadOpts.Builds = make(map[string]*uploadBuildInfo)
	for _, b := range tpl.Builders {
		info := &uploadBuildInfo{Type: b.Type}

		// Determine if we're artifacting this build
		for _, pp := range atlasPPs {
			if !pp.Skip(b.Name) {
				info.Artifact = true
				break
			}
		}

		uploadOpts.Builds[b.Name] = info
	}

	// Add the upload metadata
	metadata := make(map[string]interface{})
	if message != "" {
		metadata["message"] = message
	}
	metadata["template"] = tpl.RawContents
	metadata["template_name"] = filepath.Base(args[0])
	uploadOpts.Metadata = metadata

	// Warn about builds not having post-processors.
	var badBuilds []string
	for name, b := range uploadOpts.Builds {
		if b.Artifact {
			continue
		}

		badBuilds = append(badBuilds, name)
	}
	if len(badBuilds) > 0 {
		c.Ui.Error(fmt.Sprintf(
			"Warning! One or more of the builds in this template does not\n"+
				"have an Atlas post-processor. Artifacts from this template will\n"+
				"not appear in the Atlas artifact registry.\n\n"+
				"This is just a warning. Atlas will still build your template\n"+
				"and assume other post-processors are sending the artifacts where\n"+
				"they need to go.\n\n"+
				"Builds: %s\n\n", strings.Join(badBuilds, ", ")))
	}

	// Start the archiving process
	r, err := archive.CreateArchive(path, &opts)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error archiving: %s", err))
		return 1
	}
	defer r.Close()

	// Start the upload process
	doneCh, uploadErrCh, err := c.upload(r, &uploadOpts)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error starting upload: %s", err))
		return 1
	}

	// Make a ctrl-C channel
	sigCh := make(chan os.Signal, 1)
	signal.Notify(sigCh, os.Interrupt)
	defer signal.Stop(sigCh)

	err = nil
	select {
	case err = <-uploadErrCh:
		err = fmt.Errorf("Error uploading: %s", err)
	case <-sigCh:
		err = fmt.Errorf("Push cancelled from Ctrl-C")
	case <-doneCh:
	}

	if err != nil {
		c.Ui.Error(err.Error())
		return 1
	}

	c.Ui.Say(fmt.Sprintf("Push successful to '%s'", name))
	return 0
}
Beispiel #7
0
func (c *ValidateCommand) Run(args []string) int {
	var cfgSyntaxOnly bool
	flags := c.Meta.FlagSet("validate", FlagSetBuildFilter|FlagSetVars)
	flags.Usage = func() { c.Ui.Say(c.Help()) }
	flags.BoolVar(&cfgSyntaxOnly, "syntax-only", false, "check syntax only")
	if err := flags.Parse(args); err != nil {
		return 1
	}

	args = flags.Args()
	if len(args) != 1 {
		flags.Usage()
		return 1
	}

	// Parse the template
	tpl, err := template.ParseFile(args[0])
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Failed to parse template: %s", err))
		return 1
	}

	// If we're only checking syntax, then we're done already
	if cfgSyntaxOnly {
		c.Ui.Say("Syntax-only check passed. Everything looks okay.")
		return 0
	}

	// Get the core
	core, err := c.Meta.Core(tpl)
	if err != nil {
		c.Ui.Error(err.Error())
		return 1
	}

	errs := make([]error, 0)
	warnings := make(map[string][]string)

	// Get the builds we care about
	buildNames := c.Meta.BuildNames(core)
	builds := make([]packer.Build, 0, len(buildNames))
	for _, n := range buildNames {
		b, err := core.Build(n)
		if err != nil {
			c.Ui.Error(fmt.Sprintf(
				"Failed to initialize build '%s': %s",
				n, err))
			return 1
		}

		builds = append(builds, b)
	}

	// 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 {
		c.Ui.Error("Template validation failed. Errors are shown below.\n")
		for i, err := range errs {
			c.Ui.Error(err.Error())

			if (i + 1) < len(errs) {
				c.Ui.Error("")
			}
		}

		return 1
	}

	if len(warnings) > 0 {
		c.Ui.Say("Template validation succeeded, but there were some warnings.")
		c.Ui.Say("These are ONLY WARNINGS, and Packer will attempt to build the")
		c.Ui.Say("template despite them, but they should be paid attention to.\n")

		for build, warns := range warnings {
			c.Ui.Say(fmt.Sprintf("Warnings for build '%s':\n", build))
			for _, warning := range warns {
				c.Ui.Say(fmt.Sprintf("* %s", warning))
			}
		}

		return 0
	}

	c.Ui.Say("Template validated successfully.")
	return 0
}