Commit 9d89ca8e authored by Mitchell Hashimoto's avatar Mitchell Hashimoto

command: build should be converted to new API, compiles

parent 3ebfe06e
- var-file doesn't work
- prov/post-processors/hooks don't work
...@@ -2,16 +2,16 @@ package command ...@@ -2,16 +2,16 @@ package command
import ( import (
"bytes" "bytes"
"flag"
"fmt" "fmt"
cmdcommon "github.com/mitchellh/packer/common/command"
"github.com/mitchellh/packer/packer"
"log" "log"
"os" "os"
"os/signal" "os/signal"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
"github.com/mitchellh/packer/packer"
"github.com/mitchellh/packer/template"
) )
type BuildCommand struct { type BuildCommand struct {
...@@ -20,71 +20,52 @@ type BuildCommand struct { ...@@ -20,71 +20,52 @@ type BuildCommand struct {
func (c BuildCommand) Run(args []string) int { func (c BuildCommand) Run(args []string) int {
var cfgColor, cfgDebug, cfgForce, cfgParallel bool var cfgColor, cfgDebug, cfgForce, cfgParallel bool
buildOptions := new(cmdcommon.BuildOptions) flags := c.Meta.FlagSet("build", FlagSetBuildFilter|FlagSetVars)
flags.Usage = func() { c.Ui.Say(c.Help()) }
env, err := c.Meta.Environment() flags.BoolVar(&cfgColor, "color", true, "")
if err != nil { flags.BoolVar(&cfgDebug, "debug", false, "")
c.Ui.Error(fmt.Sprintf("Error initializing environment: %s", err)) flags.BoolVar(&cfgForce, "force", false, "")
flags.BoolVar(&cfgParallel, "parallel", true, "")
if err := flags.Parse(args); err != nil {
return 1 return 1
} }
cmdFlags := flag.NewFlagSet("build", flag.ContinueOnError) args = flags.Args()
cmdFlags.Usage = func() { env.Ui().Say(c.Help()) }
cmdFlags.BoolVar(&cfgColor, "color", true, "enable or disable color")
cmdFlags.BoolVar(&cfgDebug, "debug", false, "debug mode for builds")
cmdFlags.BoolVar(&cfgForce, "force", false, "force a build if artifacts exist")
cmdFlags.BoolVar(&cfgParallel, "parallel", true, "enable/disable parallelization")
cmdcommon.BuildOptionFlags(cmdFlags, buildOptions)
if err := cmdFlags.Parse(args); err != nil {
return 1
}
args = cmdFlags.Args()
if len(args) != 1 { if len(args) != 1 {
cmdFlags.Usage() flags.Usage()
return 1 return 1
} }
if err := buildOptions.Validate(); err != nil { // Parse the template
env.Ui().Error(err.Error()) tpl, err := template.ParseFile(args[0])
env.Ui().Error("")
env.Ui().Error(c.Help())
return 1
}
userVars, err := buildOptions.AllUserVars()
if err != nil { if err != nil {
env.Ui().Error(fmt.Sprintf("Error compiling user variables: %s", err)) c.Ui.Error(fmt.Sprintf("Failed to parse template: %s", err))
env.Ui().Error("")
env.Ui().Error(c.Help())
return 1 return 1
} }
// Read the file into a byte array so that we can parse the template // Get the core
log.Printf("Reading template: %s", args[0]) core, err := c.Meta.Core(tpl)
tpl, err := packer.ParseTemplateFile(args[0], userVars)
if err != nil { if err != nil {
env.Ui().Error(fmt.Sprintf("Failed to parse template: %s", err)) c.Ui.Error(err.Error())
return 1 return 1
} }
// The component finder for our builds // Get the builds we care about
components := &packer.ComponentFinder{ buildNames := c.Meta.BuildNames(core)
Builder: env.Builder, builds := make([]packer.Build, 0, len(buildNames))
Hook: env.Hook, for _, n := range buildNames {
PostProcessor: env.PostProcessor, b, err := core.Build(n)
Provisioner: env.Provisioner, if err != nil {
} c.Ui.Error(fmt.Sprintf(
"Failed to initialize build '%s': %s",
n, err))
}
// Go through each builder and compile the builds that we care about builds = append(builds, b)
builds, err := buildOptions.Builds(tpl, components)
if err != nil {
env.Ui().Error(err.Error())
return 1
} }
if cfgDebug { if cfgDebug {
env.Ui().Say("Debug mode enabled. Builds will not be parallelized.") c.Ui.Say("Debug mode enabled. Builds will not be parallelized.")
} }
// Compile all the UIs for the builds // Compile all the UIs for the builds
...@@ -95,24 +76,23 @@ func (c BuildCommand) Run(args []string) int { ...@@ -95,24 +76,23 @@ func (c BuildCommand) Run(args []string) int {
packer.UiColorYellow, packer.UiColorYellow,
packer.UiColorBlue, packer.UiColorBlue,
} }
buildUis := make(map[string]packer.Ui) buildUis := make(map[string]packer.Ui)
for i, b := range builds { for i, b := range buildNames {
var ui packer.Ui var ui packer.Ui
ui = env.Ui() ui = c.Ui
if cfgColor { if cfgColor {
ui = &packer.ColoredUi{ ui = &packer.ColoredUi{
Color: colors[i%len(colors)], Color: colors[i%len(colors)],
Ui: env.Ui(), Ui: ui,
} }
} }
buildUis[b.Name()] = ui buildUis[b] = ui
ui.Say(fmt.Sprintf("%s output will be in this color.", b.Name())) ui.Say(fmt.Sprintf("%s output will be in this color.", b))
} }
// Add a newline between the color output and the actual output // Add a newline between the color output and the actual output
env.Ui().Say("") c.Ui.Say("")
log.Printf("Build debug mode: %v", cfgDebug) log.Printf("Build debug mode: %v", cfgDebug)
log.Printf("Force build: %v", cfgForce) log.Printf("Force build: %v", cfgForce)
...@@ -125,7 +105,7 @@ func (c BuildCommand) Run(args []string) int { ...@@ -125,7 +105,7 @@ func (c BuildCommand) Run(args []string) int {
warnings, err := b.Prepare() warnings, err := b.Prepare()
if err != nil { if err != nil {
env.Ui().Error(err.Error()) c.Ui.Error(err.Error())
return 1 return 1
} }
if len(warnings) > 0 { if len(warnings) > 0 {
...@@ -169,7 +149,7 @@ func (c BuildCommand) Run(args []string) int { ...@@ -169,7 +149,7 @@ func (c BuildCommand) Run(args []string) int {
name := b.Name() name := b.Name()
log.Printf("Starting build run: %s", name) log.Printf("Starting build run: %s", name)
ui := buildUis[name] ui := buildUis[name]
runArtifacts, err := b.Run(ui, env.Cache()) runArtifacts, err := b.Run(ui, c.CoreConfig.Cache)
if err != nil { if err != nil {
ui.Error(fmt.Sprintf("Build '%s' errored: %s", name, err)) ui.Error(fmt.Sprintf("Build '%s' errored: %s", name, err))
...@@ -205,34 +185,34 @@ func (c BuildCommand) Run(args []string) int { ...@@ -205,34 +185,34 @@ func (c BuildCommand) Run(args []string) int {
interruptWg.Wait() interruptWg.Wait()
if interrupted { if interrupted {
env.Ui().Say("Cleanly cancelled builds after being interrupted.") c.Ui.Say("Cleanly cancelled builds after being interrupted.")
return 1 return 1
} }
if len(errors) > 0 { if len(errors) > 0 {
env.Ui().Machine("error-count", strconv.FormatInt(int64(len(errors)), 10)) c.Ui.Machine("error-count", strconv.FormatInt(int64(len(errors)), 10))
env.Ui().Error("\n==> Some builds didn't complete successfully and had errors:") c.Ui.Error("\n==> Some builds didn't complete successfully and had errors:")
for name, err := range errors { for name, err := range errors {
// Create a UI for the machine readable stuff to be targetted // Create a UI for the machine readable stuff to be targetted
ui := &packer.TargettedUi{ ui := &packer.TargettedUi{
Target: name, Target: name,
Ui: env.Ui(), Ui: c.Ui,
} }
ui.Machine("error", err.Error()) ui.Machine("error", err.Error())
env.Ui().Error(fmt.Sprintf("--> %s: %s", name, err)) c.Ui.Error(fmt.Sprintf("--> %s: %s", name, err))
} }
} }
if len(artifacts) > 0 { if len(artifacts) > 0 {
env.Ui().Say("\n==> Builds finished. The artifacts of successful builds are:") c.Ui.Say("\n==> Builds finished. The artifacts of successful builds are:")
for name, buildArtifacts := range artifacts { for name, buildArtifacts := range artifacts {
// Create a UI for the machine readable stuff to be targetted // Create a UI for the machine readable stuff to be targetted
ui := &packer.TargettedUi{ ui := &packer.TargettedUi{
Target: name, Target: name,
Ui: env.Ui(), Ui: c.Ui,
} }
// Machine-readable helpful // Machine-readable helpful
...@@ -267,11 +247,11 @@ func (c BuildCommand) Run(args []string) int { ...@@ -267,11 +247,11 @@ func (c BuildCommand) Run(args []string) int {
} }
ui.Machine("artifact", iStr, "end") ui.Machine("artifact", iStr, "end")
env.Ui().Say(message.String()) c.Ui.Say(message.String())
} }
} }
} else { } else {
env.Ui().Say("\n==> Builds finished but no artifacts were created.") c.Ui.Say("\n==> Builds finished but no artifacts were created.")
} }
if len(errors) > 0 { if len(errors) > 0 {
......
package command package command
import ( import (
"bytes"
"path/filepath" "path/filepath"
"testing" "testing"
"github.com/mitchellh/cli" "github.com/mitchellh/packer/packer"
) )
const fixturesDir = "./test-fixtures" const fixturesDir = "./test-fixtures"
func fatalCommand(t *testing.T, m Meta) { func fatalCommand(t *testing.T, m Meta) {
ui := m.Ui.(*cli.MockUi) ui := m.Ui.(*packer.BasicUi)
out := ui.Writer.(*bytes.Buffer)
err := ui.ErrorWriter.(*bytes.Buffer)
t.Fatalf( t.Fatalf(
"Bad exit code.\n\nStdout:\n\n%s\n\nStderr:\n\n%s", "Bad exit code.\n\nStdout:\n\n%s\n\nStderr:\n\n%s",
ui.OutputWriter.String(), out.String(),
ui.ErrorWriter.String()) err.String())
} }
func testFixture(n string) string { func testFixture(n string) string {
...@@ -22,7 +25,12 @@ func testFixture(n string) string { ...@@ -22,7 +25,12 @@ func testFixture(n string) string {
} }
func testMeta(t *testing.T) Meta { func testMeta(t *testing.T) Meta {
var out, err bytes.Buffer
return Meta{ return Meta{
Ui: new(cli.MockUi), Ui: &packer.BasicUi{
Writer: &out,
ErrorWriter: &err,
},
} }
} }
package command package command
import ( import (
"github.com/mitchellh/cli" "bufio"
"flag"
"fmt"
"io"
"github.com/mitchellh/packer/helper/flag-kv"
"github.com/mitchellh/packer/helper/flag-slice"
"github.com/mitchellh/packer/packer" "github.com/mitchellh/packer/packer"
"github.com/mitchellh/packer/template"
)
// FlagSetFlags is an enum to define what flags are present in the
// default FlagSet returned by Meta.FlagSet
type FlagSetFlags uint
const (
FlagSetNone FlagSetFlags = 0
FlagSetBuildFilter FlagSetFlags = 1 << iota
FlagSetVars
) )
// Meta contains the meta-options and functionality that nearly every
// Packer command inherits.
type Meta struct { type Meta struct {
EnvConfig *packer.EnvironmentConfig CoreConfig *packer.CoreConfig
Ui cli.Ui EnvConfig *packer.EnvironmentConfig
Ui packer.Ui
// These are set by command-line flags
flagBuildExcept []string
flagBuildOnly []string
flagVars map[string]string
flagVarFiles []string
}
// Core returns the core for the given template given the configured
// CoreConfig and user variables on this Meta.
func (m *Meta) Core(tpl *template.Template) (*packer.Core, error) {
// Copy the config so we don't modify it
config := *m.CoreConfig
config.Template = tpl
config.Variables = m.flagVars
// Init the core
core, err := packer.NewCore(&config)
if err != nil {
return nil, fmt.Errorf("Error initializing core: %s", err)
}
// Validate it
if err := core.Validate(); err != nil {
return nil, err
}
return core, nil
}
// BuildNames returns the list of builds that are in the given core
// that we care about taking into account the only and except flags.
func (m *Meta) BuildNames(c *packer.Core) []string {
// TODO: test
// Filter the "only"
if len(m.flagBuildOnly) > 0 {
// Build a set of all the available names
nameSet := make(map[string]struct{})
for _, n := range c.BuildNames() {
nameSet[n] = struct{}{}
}
// Build our result set which we pre-allocate some sane number
result := make([]string, 0, len(m.flagBuildOnly))
for _, n := range m.flagBuildOnly {
if _, ok := nameSet[n]; ok {
result = append(result, n)
}
}
return result
}
// Filter the "except"
if len(m.flagBuildExcept) > 0 {
// Build a set of the things we don't want
nameSet := make(map[string]struct{})
for _, n := range m.flagBuildExcept {
nameSet[n] = struct{}{}
}
// Build our result set which is the names of all builds except
// those in the given set.
names := c.BuildNames()
result := make([]string, 0, len(names))
for _, n := range names {
if _, ok := nameSet[n]; !ok {
result = append(result, n)
}
}
return result
}
// We care about everything
return c.BuildNames()
}
// FlagSet returns a FlagSet with the common flags that every
// command implements. The exact behavior of FlagSet can be configured
// using the flags as the second parameter, for example to disable
// build settings on the commands that don't handle builds.
func (m *Meta) FlagSet(n string, fs FlagSetFlags) *flag.FlagSet {
f := flag.NewFlagSet(n, flag.ContinueOnError)
// FlagSetBuildFilter tells us to enable the settings for selecting
// builds we care about.
if fs&FlagSetBuildFilter != 0 {
f.Var((*sliceflag.StringFlag)(&m.flagBuildExcept), "except", "")
f.Var((*sliceflag.StringFlag)(&m.flagBuildOnly), "only", "")
}
// FlagSetVars tells us what variables to use
if fs&FlagSetVars != 0 {
f.Var((*kvflag.Flag)(&m.flagVars), "var", "")
f.Var((*sliceflag.StringFlag)(&m.flagVarFiles), "var-file", "")
}
// Create an io.Writer that writes to our Ui properly for errors.
// This is kind of a hack, but it does the job. Basically: create
// a pipe, use a scanner to break it into lines, and output each line
// to the UI. Do this forever.
errR, errW := io.Pipe()
errScanner := bufio.NewScanner(errR)
go func() {
for errScanner.Scan() {
m.Ui.Error(errScanner.Text())
}
}()
f.SetOutput(errW)
return f
}
// ValidateFlags should be called after parsing flags to validate the
// given flags
func (m *Meta) ValidateFlags() error {
// TODO
return nil
} }
func (m *Meta) Environment() (packer.Environment, error) { func (m *Meta) Environment() (packer.Environment, error) {
......
...@@ -221,7 +221,7 @@ func (c *PushCommand) Run(args []string) int { ...@@ -221,7 +221,7 @@ func (c *PushCommand) Run(args []string) int {
return 1 return 1
} }
c.Ui.Output(fmt.Sprintf("Push successful to '%s'", tpl.Push.Name)) c.Ui.Say(fmt.Sprintf("Push successful to '%s'", tpl.Push.Name))
return 0 return 0
} }
......
...@@ -53,13 +53,13 @@ func (c *VersionCommand) Run(args []string) int { ...@@ -53,13 +53,13 @@ func (c *VersionCommand) Run(args []string) int {
} }
} }
c.Ui.Output(versionString.String()) c.Ui.Say(versionString.String())
// If we have a version check function, then let's check for // If we have a version check function, then let's check for
// the latest version as well. // the latest version as well.
if c.CheckFunc != nil { if c.CheckFunc != nil {
// Separate the prior output with a newline // Separate the prior output with a newline
c.Ui.Output("") c.Ui.Say("")
// Check the latest version // Check the latest version
info, err := c.CheckFunc() info, err := c.CheckFunc()
...@@ -68,7 +68,7 @@ func (c *VersionCommand) Run(args []string) int { ...@@ -68,7 +68,7 @@ func (c *VersionCommand) Run(args []string) int {
"Error checking latest version: %s", err)) "Error checking latest version: %s", err))
} }
if info.Outdated { if info.Outdated {
c.Ui.Output(fmt.Sprintf( c.Ui.Say(fmt.Sprintf(
"Your version of Packer is out of date! The latest version\n"+ "Your version of Packer is out of date! The latest version\n"+
"is %s. You can update by downloading from www.packer.io", "is %s. You can update by downloading from www.packer.io",
info.Latest)) info.Latest))
......
...@@ -27,8 +27,9 @@ func init() { ...@@ -27,8 +27,9 @@ func init() {
} }
meta := command.Meta{ meta := command.Meta{
EnvConfig: &EnvConfig, CoreConfig: &CoreConfig,
Ui: Ui, EnvConfig: &EnvConfig,
Ui: Ui,
} }
Commands = map[string]cli.CommandFactory{ Commands = map[string]cli.CommandFactory{
......
...@@ -13,6 +13,9 @@ import ( ...@@ -13,6 +13,9 @@ import (
"github.com/mitchellh/packer/packer/plugin" "github.com/mitchellh/packer/packer/plugin"
) )
// CoreConfig is the global CoreConfig we use to initialize the CLI.
var CoreConfig packer.CoreConfig
// EnvConfig is the global EnvironmentConfig we use to initialize the CLI. // EnvConfig is the global EnvironmentConfig we use to initialize the CLI.
var EnvConfig packer.EnvironmentConfig var EnvConfig packer.EnvironmentConfig
......
package kvflag
import (
"fmt"
"strings"
)
// Flag is a flag.Value implementation for parsing user variables
// from the command-line in the format of '-var key=value'.
type Flag map[string]string
func (v *Flag) String() string {
return ""
}
func (v *Flag) Set(raw string) error {
idx := strings.Index(raw, "=")
if idx == -1 {
return fmt.Errorf("No '=' value in arg: %s", raw)
}
if *v == nil {
*v = make(map[string]string)
}
key, value := raw[0:idx], raw[idx+1:]
(*v)[key] = value
return nil
}
package kvflag
import (
"flag"
"reflect"
"testing"
)
func TestFlag_impl(t *testing.T) {
var _ flag.Value = new(Flag)
}
func TestFlag(t *testing.T) {
cases := []struct {
Input string
Output map[string]string
Error bool
}{
{
"key=value",
map[string]string{"key": "value"},
false,
},
{
"key=",
map[string]string{"key": ""},
false,
},
{
"key=foo=bar",
map[string]string{"key": "foo=bar"},
false,
},
{
"key",
nil,
true,
},
}
for _, tc := range cases {
f := new(Flag)
err := f.Set(tc.Input)
if (err != nil) != tc.Error {
t.Fatalf("bad error. Input: %#v", tc.Input)
}
actual := map[string]string(*f)
if !reflect.DeepEqual(actual, tc.Output) {
t.Fatalf("bad: %#v", actual)
}
}
}
package sliceflag
import "strings"
// StringFlag implements the flag.Value interface and allows multiple
// calls to the same variable to append a list.
type StringFlag []string
func (s *StringFlag) String() string {
return strings.Join(*s, ",")
}
func (s *StringFlag) Set(value string) error {
*s = append(*s, value)
return nil
}
package sliceflag
import (
"flag"
"reflect"
"testing"
)
func TestStringFlag_implements(t *testing.T) {
var raw interface{}
raw = new(StringFlag)
if _, ok := raw.(flag.Value); !ok {
t.Fatalf("StringFlag should be a Value")
}
}
func TestStringFlagSet(t *testing.T) {
sv := new(StringFlag)
err := sv.Set("foo")
if err != nil {
t.Fatalf("err: %s", err)
}
err = sv.Set("bar")
if err != nil {
t.Fatalf("err: %s", err)
}
expected := []string{"foo", "bar"}
if !reflect.DeepEqual([]string(*sv), expected) {
t.Fatalf("Bad: %#v", sv)
}
}
...@@ -159,6 +159,13 @@ func wrappedMain() int { ...@@ -159,6 +159,13 @@ func wrappedMain() int {
} }
} }
// Create the core configuration
CoreConfig = packer.CoreConfig{
Cache: EnvConfig.Cache,
Components: EnvConfig.Components,
Ui: EnvConfig.Ui,
}
//setupSignalHandlers(env) //setupSignalHandlers(env)
cli := &cli.CLI{ cli := &cli.CLI{
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment