command: build should be converted to new API, compiles
This commit is contained in:
parent
3ebfe06ec8
commit
9d89ca8e07
|
@ -0,0 +1,2 @@
|
|||
- var-file doesn't work
|
||||
- prov/post-processors/hooks don't work
|
114
command/build.go
114
command/build.go
|
@ -2,16 +2,16 @@ package command
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
cmdcommon "github.com/mitchellh/packer/common/command"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"github.com/mitchellh/packer/template"
|
||||
)
|
||||
|
||||
type BuildCommand struct {
|
||||
|
@ -20,71 +20,52 @@ type BuildCommand struct {
|
|||
|
||||
func (c BuildCommand) Run(args []string) int {
|
||||
var cfgColor, cfgDebug, cfgForce, cfgParallel bool
|
||||
buildOptions := new(cmdcommon.BuildOptions)
|
||||
|
||||
env, err := c.Meta.Environment()
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error initializing environment: %s", err))
|
||||
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, "")
|
||||
flags.BoolVar(&cfgParallel, "parallel", true, "")
|
||||
if err := flags.Parse(args); err != nil {
|
||||
return 1
|
||||
}
|
||||
|
||||
cmdFlags := flag.NewFlagSet("build", flag.ContinueOnError)
|
||||
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()
|
||||
args = flags.Args()
|
||||
if len(args) != 1 {
|
||||
cmdFlags.Usage()
|
||||
flags.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()
|
||||
// Parse the template
|
||||
tpl, err := template.ParseFile(args[0])
|
||||
if err != nil {
|
||||
env.Ui().Error(fmt.Sprintf("Error compiling user variables: %s", err))
|
||||
env.Ui().Error("")
|
||||
env.Ui().Error(c.Help())
|
||||
c.Ui.Error(fmt.Sprintf("Failed to parse template: %s", err))
|
||||
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], userVars)
|
||||
// Get the core
|
||||
core, err := c.Meta.Core(tpl)
|
||||
if err != nil {
|
||||
env.Ui().Error(fmt.Sprintf("Failed to parse template: %s", err))
|
||||
c.Ui.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
// The component finder for our builds
|
||||
components := &packer.ComponentFinder{
|
||||
Builder: env.Builder,
|
||||
Hook: env.Hook,
|
||||
PostProcessor: env.PostProcessor,
|
||||
Provisioner: env.Provisioner,
|
||||
}
|
||||
// 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))
|
||||
}
|
||||
|
||||
// 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
|
||||
builds = append(builds, b)
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -95,24 +76,23 @@ func (c BuildCommand) Run(args []string) int {
|
|||
packer.UiColorYellow,
|
||||
packer.UiColorBlue,
|
||||
}
|
||||
|
||||
buildUis := make(map[string]packer.Ui)
|
||||
for i, b := range builds {
|
||||
for i, b := range buildNames {
|
||||
var ui packer.Ui
|
||||
ui = env.Ui()
|
||||
ui = c.Ui
|
||||
if cfgColor {
|
||||
ui = &packer.ColoredUi{
|
||||
Color: colors[i%len(colors)],
|
||||
Ui: env.Ui(),
|
||||
Ui: ui,
|
||||
}
|
||||
}
|
||||
|
||||
buildUis[b.Name()] = ui
|
||||
ui.Say(fmt.Sprintf("%s output will be in this color.", b.Name()))
|
||||
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
|
||||
env.Ui().Say("")
|
||||
c.Ui.Say("")
|
||||
|
||||
log.Printf("Build debug mode: %v", cfgDebug)
|
||||
log.Printf("Force build: %v", cfgForce)
|
||||
|
@ -125,7 +105,7 @@ func (c BuildCommand) Run(args []string) int {
|
|||
|
||||
warnings, err := b.Prepare()
|
||||
if err != nil {
|
||||
env.Ui().Error(err.Error())
|
||||
c.Ui.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
if len(warnings) > 0 {
|
||||
|
@ -169,7 +149,7 @@ func (c BuildCommand) Run(args []string) int {
|
|||
name := b.Name()
|
||||
log.Printf("Starting build run: %s", name)
|
||||
ui := buildUis[name]
|
||||
runArtifacts, err := b.Run(ui, env.Cache())
|
||||
runArtifacts, err := b.Run(ui, c.CoreConfig.Cache)
|
||||
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Build '%s' errored: %s", name, err))
|
||||
|
@ -205,34 +185,34 @@ func (c BuildCommand) Run(args []string) int {
|
|||
interruptWg.Wait()
|
||||
|
||||
if interrupted {
|
||||
env.Ui().Say("Cleanly cancelled builds after being interrupted.")
|
||||
c.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))
|
||||
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 {
|
||||
// Create a UI for the machine readable stuff to be targetted
|
||||
ui := &packer.TargettedUi{
|
||||
Target: name,
|
||||
Ui: env.Ui(),
|
||||
Ui: c.Ui,
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
// Create a UI for the machine readable stuff to be targetted
|
||||
ui := &packer.TargettedUi{
|
||||
Target: name,
|
||||
Ui: env.Ui(),
|
||||
Ui: c.Ui,
|
||||
}
|
||||
|
||||
// Machine-readable helpful
|
||||
|
@ -267,11 +247,11 @@ func (c BuildCommand) Run(args []string) int {
|
|||
}
|
||||
|
||||
ui.Machine("artifact", iStr, "end")
|
||||
env.Ui().Say(message.String())
|
||||
c.Ui.Say(message.String())
|
||||
}
|
||||
}
|
||||
} 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 {
|
||||
|
|
|
@ -1,20 +1,23 @@
|
|||
package command
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/mitchellh/cli"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
)
|
||||
|
||||
const fixturesDir = "./test-fixtures"
|
||||
|
||||
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(
|
||||
"Bad exit code.\n\nStdout:\n\n%s\n\nStderr:\n\n%s",
|
||||
ui.OutputWriter.String(),
|
||||
ui.ErrorWriter.String())
|
||||
out.String(),
|
||||
err.String())
|
||||
}
|
||||
|
||||
func testFixture(n string) string {
|
||||
|
@ -22,7 +25,12 @@ func testFixture(n string) string {
|
|||
}
|
||||
|
||||
func testMeta(t *testing.T) Meta {
|
||||
var out, err bytes.Buffer
|
||||
|
||||
return Meta{
|
||||
Ui: new(cli.MockUi),
|
||||
Ui: &packer.BasicUi{
|
||||
Writer: &out,
|
||||
ErrorWriter: &err,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
145
command/meta.go
145
command/meta.go
|
@ -1,13 +1,152 @@
|
|||
package command
|
||||
|
||||
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/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 {
|
||||
EnvConfig *packer.EnvironmentConfig
|
||||
Ui cli.Ui
|
||||
CoreConfig *packer.CoreConfig
|
||||
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) {
|
||||
|
|
|
@ -221,7 +221,7 @@ func (c *PushCommand) Run(args []string) int {
|
|||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
// the latest version as well.
|
||||
if c.CheckFunc != nil {
|
||||
// Separate the prior output with a newline
|
||||
c.Ui.Output("")
|
||||
c.Ui.Say("")
|
||||
|
||||
// Check the latest version
|
||||
info, err := c.CheckFunc()
|
||||
|
@ -68,7 +68,7 @@ func (c *VersionCommand) Run(args []string) int {
|
|||
"Error checking latest version: %s", err))
|
||||
}
|
||||
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"+
|
||||
"is %s. You can update by downloading from www.packer.io",
|
||||
info.Latest))
|
||||
|
|
|
@ -27,8 +27,9 @@ func init() {
|
|||
}
|
||||
|
||||
meta := command.Meta{
|
||||
EnvConfig: &EnvConfig,
|
||||
Ui: Ui,
|
||||
CoreConfig: &CoreConfig,
|
||||
EnvConfig: &EnvConfig,
|
||||
Ui: Ui,
|
||||
}
|
||||
|
||||
Commands = map[string]cli.CommandFactory{
|
||||
|
|
|
@ -13,6 +13,9 @@ import (
|
|||
"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.
|
||||
var EnvConfig packer.EnvironmentConfig
|
||||
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
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
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
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
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
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)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue