Commit 835b8c69 authored by Mitchell Hashimoto's avatar Mitchell Hashimoto

Merge pull request #1629 from mitchellh/f-no-command-plugins

Remove command plugins, command interface from core, etc.
parents 4406c20a c51cd3e3
......@@ -6,11 +6,10 @@ import (
"path/filepath"
"github.com/hashicorp/go-checkpoint"
"github.com/mitchellh/packer/packer"
"github.com/mitchellh/packer/command"
)
func init() {
packer.VersionChecker = packerVersionCheck
checkpointResult = make(chan *checkpoint.CheckResponse, 1)
}
......@@ -33,9 +32,9 @@ func runCheckpoint(c *config) {
return
}
version := packer.Version
if packer.VersionPrerelease != "" {
version += fmt.Sprintf(".%s", packer.VersionPrerelease)
version := Version
if VersionPrerelease != "" {
version += fmt.Sprintf(".%s", VersionPrerelease)
}
signaturePath := filepath.Join(configDir, "checkpoint_signature")
......@@ -58,21 +57,23 @@ func runCheckpoint(c *config) {
checkpointResult <- resp
}
// packerVersionCheck implements packer.VersionCheckFunc and is used
// commandVersionCheck implements command.VersionCheckFunc and is used
// as the version checker.
func packerVersionCheck(current string) (packer.VersionCheckInfo, error) {
func commandVersionCheck() (command.VersionCheckInfo, error) {
// Wait for the result to come through
info := <-checkpointResult
if info == nil {
var zero packer.VersionCheckInfo
var zero command.VersionCheckInfo
return zero, nil
}
// Build the alerts that we may have received about our version
alerts := make([]string, len(info.Alerts))
for i, a := range info.Alerts {
alerts[i] = a.Message
}
return packer.VersionCheckInfo{
return command.VersionCheckInfo{
Outdated: info.Outdated,
Latest: info.CurrentVersion,
Alerts: alerts,
......
package build
package command
import (
"bytes"
......@@ -14,16 +14,20 @@ import (
"sync"
)
type Command byte
func (Command) Help() string {
return strings.TrimSpace(helpText)
type BuildCommand struct {
Meta
}
func (c Command) Run(env packer.Environment, args []string) int {
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))
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")
......@@ -278,6 +282,28 @@ func (c Command) Run(env packer.Environment, args []string) int {
return 0
}
func (Command) Synopsis() string {
func (BuildCommand) Help() string {
helpText := `
Usage: packer build [options] TEMPLATE
Will execute multiple builds in parallel as defined in the template.
The various artifacts created by the template will be outputted.
Options:
-debug Debug mode enabled for builds
-force Force a build to continue if artifacts exist, deletes existing artifacts
-machine-readable Machine-readable output
-except=foo,bar,baz Build all builds other than these
-only=foo,bar,baz Only build the given builds by name
-parallel=false Disable parallelization (on by default)
-var 'key=value' Variable for templates, can be used multiple times.
-var-file=path JSON file containing user variables.
`
return strings.TrimSpace(helpText)
}
func (BuildCommand) Synopsis() string {
return "build image(s) from template"
}
package build
import (
"bytes"
"github.com/mitchellh/packer/packer"
"testing"
)
func testEnvironment() packer.Environment {
config := packer.DefaultEnvironmentConfig()
config.Ui = &packer.BasicUi{
Reader: new(bytes.Buffer),
Writer: new(bytes.Buffer),
}
env, err := packer.NewEnvironment(config)
if err != nil {
panic(err)
}
return env
}
func TestCommand_Implements(t *testing.T) {
var _ packer.Command = new(Command)
}
func TestCommand_Run_NoArgs(t *testing.T) {
command := new(Command)
result := command.Run(testEnvironment(), make([]string, 0))
if result != 1 {
t.Fatalf("bad: %d", result)
}
}
func TestCommand_Run_MoreThanOneArg(t *testing.T) {
command := new(Command)
args := []string{"one", "two"}
result := command.Run(testEnvironment(), args)
if result != 1 {
t.Fatalf("bad: %d", result)
}
}
func TestCommand_Run_MissingFile(t *testing.T) {
command := new(Command)
args := []string{"i-better-not-exist"}
result := command.Run(testEnvironment(), args)
if result != 1 {
t.Fatalf("bad: %d", result)
}
}
package build
const helpText = `
Usage: packer build [options] TEMPLATE
Will execute multiple builds in parallel as defined in the template.
The various artifacts created by the template will be outputted.
Options:
-debug Debug mode enabled for builds
-force Force a build to continue if artifacts exist, deletes existing artifacts
-machine-readable Machine-readable output
-except=foo,bar,baz Build all builds other than these
-only=foo,bar,baz Only build the given builds by name
-parallel=false Disable parallelization (on by default)
-var 'key=value' Variable for templates, can be used multiple times.
-var-file=path JSON file containing user variables.
`
package fix
package command
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"github.com/mitchellh/packer/packer"
"log"
"os"
"strings"
)
type Command byte
"github.com/mitchellh/packer/fix"
)
func (Command) Help() string {
return strings.TrimSpace(helpString)
type FixCommand struct {
Meta
}
func (c Command) Run(env packer.Environment, args []string) int {
func (c *FixCommand) Run(args []string) int {
env, err := c.Meta.Environment()
if err != nil {
c.Ui.Error(fmt.Sprintf("Error initializing environment: %s", err))
return 1
}
cmdFlags := flag.NewFlagSet("fix", flag.ContinueOnError)
cmdFlags.Usage = func() { env.Ui().Say(c.Help()) }
if err := cmdFlags.Parse(args); err != nil {
......@@ -50,9 +55,9 @@ func (c Command) Run(env packer.Environment, args []string) int {
tplF.Close()
input := templateData
for _, name := range FixerOrder {
for _, name := range fix.FixerOrder {
var err error
fixer, ok := Fixers[name]
fixer, ok := fix.Fixers[name]
if !ok {
panic("fixer not found: " + name)
}
......@@ -85,6 +90,30 @@ func (c Command) Run(env packer.Environment, args []string) int {
return 0
}
func (c Command) Synopsis() string {
func (*FixCommand) Help() string {
helpText := `
Usage: packer fix [options] TEMPLATE
Reads the JSON template and attempts to fix known backwards
incompatibilities. The fixed template will be outputted to standard out.
If the template cannot be fixed due to an error, the command will exit
with a non-zero exit status. Error messages will appear on standard error.
Fixes that are run:
iso-md5 Replaces "iso_md5" in builders with newer "iso_checksum"
createtime Replaces ".CreateTime" in builder configs with "{{timestamp}}"
virtualbox-gaattach Updates VirtualBox builders using "guest_additions_attach"
to use "guest_additions_mode"
pp-vagrant-override Replaces old-style provider overrides for the Vagrant
post-processor to new-style as of Packer 0.5.0.
virtualbox-rename Updates "virtualbox" builders to "virtualbox-iso"
`
return strings.TrimSpace(helpText)
}
func (c *FixCommand) Synopsis() string {
return "fixes templates from old versions of packer"
}
package fix
import (
"github.com/mitchellh/packer/packer"
"testing"
)
func TestCommand_Impl(t *testing.T) {
var raw interface{}
raw = new(Command)
if _, ok := raw.(packer.Command); !ok {
t.Fatalf("must be a Command")
}
}
package fix
const helpString = `
Usage: packer fix [options] TEMPLATE
Reads the JSON template and attempts to fix known backwards
incompatibilities. The fixed template will be outputted to standard out.
If the template cannot be fixed due to an error, the command will exit
with a non-zero exit status. Error messages will appear on standard error.
Fixes that are run:
iso-md5 Replaces "iso_md5" in builders with newer "iso_checksum"
createtime Replaces ".CreateTime" in builder configs with "{{timestamp}}"
virtualbox-gaattach Updates VirtualBox builders using "guest_additions_attach"
to use "guest_additions_mode"
pp-vagrant-override Replaces old-style provider overrides for the Vagrant
post-processor to new-style as of Packer 0.5.0.
virtualbox-rename Updates "virtualbox" builders to "virtualbox-iso"
`
package inspect
package command
import (
"flag"
......@@ -9,17 +9,17 @@ import (
"strings"
)
type Command struct{}
func (Command) Help() string {
return strings.TrimSpace(helpText)
type InspectCommand struct{
Meta
}
func (c Command) Synopsis() string {
return "see components of a template"
}
func (c *InspectCommand) Run(args []string) int {
env, err := c.Meta.Environment()
if err != nil {
c.Ui.Error(fmt.Sprintf("Error initializing environment: %s", err))
return 1
}
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 {
......@@ -148,3 +148,23 @@ func (c Command) Run(env packer.Environment, args []string) int {
return 0
}
func (*InspectCommand) Help() string {
helpText := `
Usage: packer inspect TEMPLATE
Inspects a template, parsing and outputting the components a template
defines. This does not validate the contents of a template (other than
basic syntax by necessity).
Options:
-machine-readable Machine-readable output
`
return strings.TrimSpace(helpText)
}
func (c *InspectCommand) Synopsis() string {
return "see components of a template"
}
package inspect
import (
"github.com/mitchellh/packer/packer"
"testing"
)
func TestCommand_Impl(t *testing.T) {
var raw interface{}
raw = new(Command)
if _, ok := raw.(packer.Command); !ok {
t.Fatalf("must be a Command")
}
}
package inspect
const helpText = `
Usage: packer inspect TEMPLATE
Inspects a template, parsing and outputting the components a template
defines. This does not validate the contents of a template (other than
basic syntax by necessity).
Options:
-machine-readable Machine-readable output
`
package command
import (
"github.com/mitchellh/cli"
"github.com/mitchellh/packer/packer"
)
type Meta struct {
EnvConfig *packer.EnvironmentConfig
Ui cli.Ui
}
func (m *Meta) Environment() (packer.Environment, error) {
return packer.NewEnvironment(m.EnvConfig)
}
package validate
package command
import (
"flag"
......@@ -9,16 +9,20 @@ import (
"strings"
)
type Command byte
func (Command) Help() string {
return strings.TrimSpace(helpString)
type ValidateCommand struct {
Meta
}
func (c Command) Run(env packer.Environment, args []string) int {
func (c *ValidateCommand) Run(args []string) int {
var cfgSyntaxOnly bool
buildOptions := new(cmdcommon.BuildOptions)
env, err := c.Meta.Environment()
if err != nil {
c.Ui.Error(fmt.Sprintf("Error initializing environment: %s", err))
return 1
}
cmdFlags := flag.NewFlagSet("validate", flag.ContinueOnError)
cmdFlags.Usage = func() { env.Ui().Say(c.Help()) }
cmdFlags.BoolVar(&cfgSyntaxOnly, "syntax-only", false, "check syntax only")
......@@ -123,6 +127,29 @@ func (c Command) Run(env packer.Environment, args []string) int {
return 0
}
func (Command) Synopsis() string {
func (*ValidateCommand) Help() string {
helpText := `
Usage: packer validate [options] TEMPLATE
Checks the template is valid by parsing the template and also
checking the configuration with the various builders, provisioners, etc.
If it is not valid, the errors will be shown and the command will exit
with a non-zero exit status. If it is valid, it will exit with a zero
exit status.
Options:
-syntax-only Only check syntax. Do not verify config of the template.
-except=foo,bar,baz Validate all builds other than these
-only=foo,bar,baz Validate only these builds
-var 'key=value' Variable for templates, can be used multiple times.
-var-file=path JSON file containing user variables.
`
return strings.TrimSpace(helpText)
}
func (*ValidateCommand) Synopsis() string {
return "check that a template is valid"
}
package validate
import (
"github.com/mitchellh/packer/packer"
"testing"
)
func TestCommand_Impl(t *testing.T) {
var raw interface{}
raw = new(Command)
if _, ok := raw.(packer.Command); !ok {
t.Fatalf("must be a Command")
}
}
package validate
const helpString = `
Usage: packer validate [options] TEMPLATE
Checks the template is valid by parsing the template and also
checking the configuration with the various builders, provisioners, etc.
If it is not valid, the errors will be shown and the command will exit
with a non-zero exit status. If it is valid, it will exit with a zero
exit status.
Options:
-syntax-only Only check syntax. Do not verify config of the template.
-except=foo,bar,baz Validate all builds other than these
-only=foo,bar,baz Validate only these builds
-var 'key=value' Variable for templates, can be used multiple times.
-var-file=path JSON file containing user variables.
`
package command
import (
"bytes"
"fmt"
)
// VersionCommand is a Command implementation prints the version.
type VersionCommand struct {
Meta
Revision string
Version string
VersionPrerelease string
CheckFunc VersionCheckFunc
}
// VersionCheckFunc is the callback called by the Version command to
// check if there is a new version of Packer.
type VersionCheckFunc func() (VersionCheckInfo, error)
// VersionCheckInfo is the return value for the VersionCheckFunc callback
// and tells the Version command information about the latest version
// of Packer.
type VersionCheckInfo struct {
Outdated bool
Latest string
Alerts []string
}
func (c *VersionCommand) Help() string {
return ""
}
func (c *VersionCommand) Run(args []string) int {
env, err := c.Meta.Environment()
if err != nil {
c.Ui.Error(fmt.Sprintf("Error initializing environment: %s", err))
return 1
}
env.Ui().Machine("version", c.Version)
env.Ui().Machine("version-prelease", c.VersionPrerelease)
env.Ui().Machine("version-commit", c.Revision)
var versionString bytes.Buffer
fmt.Fprintf(&versionString, "Packer v%s", c.Version)
if c.VersionPrerelease != "" {
fmt.Fprintf(&versionString, ".%s", c.VersionPrerelease)
if c.Revision != "" {
fmt.Fprintf(&versionString, " (%s)", c.Revision)
}
}
c.Ui.Output(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("")
// Check the latest version
info, err := c.CheckFunc()
if err != nil {
c.Ui.Error(fmt.Sprintf(
"Error checking latest version: %s", err))
}
if info.Outdated {
c.Ui.Output(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))
}
}
return 0
}
func (c *VersionCommand) Synopsis() string {
return "Prints the Packer version"
}
package command
import (
"testing"
"github.com/mitchellh/cli"
)
func TestVersionCommand_implements(t *testing.T) {
var _ cli.Command = &VersionCommand{}
}
package main
import (
"os"
"os/signal"
"github.com/mitchellh/cli"
"github.com/mitchellh/packer/command"
)
// Commands is the mapping of all the available Terraform commands.
var Commands map[string]cli.CommandFactory
// Ui is the cli.Ui used for communicating to the outside world.
var Ui cli.Ui
const ErrorPrefix = "e:"
const OutputPrefix = "o:"
func init() {
Ui = &cli.PrefixedUi{
AskPrefix: OutputPrefix,
OutputPrefix: OutputPrefix,
InfoPrefix: OutputPrefix,
ErrorPrefix: ErrorPrefix,
Ui: &cli.BasicUi{Writer: os.Stdout},
}
meta := command.Meta{
EnvConfig: &EnvConfig,
Ui: Ui,
}
Commands = map[string]cli.CommandFactory{
"build": func() (cli.Command, error) {
return &command.BuildCommand{
Meta: meta,
}, nil
},
"fix": func() (cli.Command, error) {
return &command.FixCommand{
Meta: meta,
}, nil
},
"inspect": func() (cli.Command, error) {
return &command.InspectCommand{
Meta: meta,
}, nil
},
"validate": func() (cli.Command, error) {
return &command.ValidateCommand{
Meta: meta,
}, nil
},
"version": func() (cli.Command, error) {
return &command.VersionCommand{
Meta: meta,
Revision: GitCommit,
Version: Version,
VersionPrerelease: VersionPrerelease,
CheckFunc: commandVersionCheck,
}, nil
},
}
}
// makeShutdownCh creates an interrupt listener and returns a channel.
// A message will be sent on the channel for every interrupt received.
func makeShutdownCh() <-chan struct{} {
resultCh := make(chan struct{})
signalCh := make(chan os.Signal, 4)
signal.Notify(signalCh, os.Interrupt)
go func() {
for {
<-signalCh
resultCh <- struct{}{}
}
}()
return resultCh
}
......@@ -3,10 +3,11 @@ package common
import (
"encoding/hex"
"fmt"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"log"
"time"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
)
// StepDownload downloads a remote file using the download client within
......@@ -70,7 +71,7 @@ func (s *StepDownload) Run(state multistep.StateBag) multistep.StepAction {
CopyFile: false,
Hash: HashForType(s.ChecksumType),
Checksum: checksum,
UserAgent: packer.VersionString(),
UserAgent: "Packer",
}
path, err, retry := s.download(config, state)
......
......@@ -13,6 +13,9 @@ import (
"github.com/mitchellh/packer/packer/plugin"
)
// EnvConfig is the global EnvironmentConfig we use to initialize the CLI.
var EnvConfig packer.EnvironmentConfig
type config struct {
DisableCheckpoint bool `json:"disable_checkpoint"`
DisableCheckpointSignature bool `json:"disable_checkpoint_signature"`
......@@ -20,7 +23,6 @@ type config struct {
PluginMaxPort uint
Builders map[string]string
Commands map[string]string
PostProcessors map[string]string `json:"post-processors"`
Provisioners map[string]string
}
......@@ -79,15 +81,6 @@ func (c *config) Discover() error {
return nil
}
// Returns an array of defined command names.
func (c *config) CommandNames() (result []string) {
result = make([]string, 0, len(c.Commands))
for name := range c.Commands {
result = append(result, name)
}
return
}
// This is a proper packer.BuilderFunc that can be used to load packer.Builder
// implementations from the defined plugins.
func (c *config) LoadBuilder(name string) (packer.Builder, error) {
......@@ -101,19 +94,6 @@ func (c *config) LoadBuilder(name string) (packer.Builder, error) {
return c.pluginClient(bin).Builder()
}
// This is a proper packer.CommandFunc that can be used to load packer.Command
// implementations from the defined plugins.
func (c *config) LoadCommand(name string) (packer.Command, error) {
log.Printf("Loading command: %s\n", name)
bin, ok := c.Commands[name]
if !ok {
log.Printf("Command not found: %s\n", name)
return nil, nil
}
return c.pluginClient(bin).Command()
}
// This is a proper implementation of packer.HookFunc that can be used
// to load packer.Hook implementations from the defined plugins.
func (c *config) LoadHook(name string) (packer.Hook, error) {
......@@ -163,12 +143,6 @@ func (c *config) discover(path string) error {
return err
}
err = c.discoverSingle(
filepath.Join(path, "packer-command-*"), &c.Commands)
if err != nil {
return err
}
err = c.discoverSingle(
filepath.Join(path, "packer-post-processor-*"), &c.PostProcessors)
if err != nil {
......
package main
import (
"io"
"os"
)
// These are the environmental variables that determine if we log, and if
// we log whether or not the log should go to a file.
const EnvLog = "PACKER_LOG" //Set to True
const EnvLogFile = "PACKER_LOG_PATH" //Set to a file
// logOutput determines where we should send logs (if anywhere).
func logOutput() (logOutput io.Writer, err error) {
logOutput = nil
if os.Getenv(EnvLog) != "" {
logOutput = os.Stderr
if logPath := os.Getenv(EnvLogFile); logPath != "" {
var err error
logOutput, err = os.Create(logPath)
if err != nil {
return nil, err
}
}
}
return
}
......@@ -9,10 +9,13 @@ import (
"os"
"path/filepath"
"runtime"
"sync"
"github.com/mitchellh/cli"
"github.com/mitchellh/packer/packer"
"github.com/mitchellh/packer/packer/plugin"
"github.com/mitchellh/panicwrap"
"github.com/mitchellh/prefixedio"
)
func main() {
......@@ -24,19 +27,20 @@ func main() {
// realMain is executed from main and returns the exit status to exit with.
func realMain() int {
// If there is no explicit number of Go threads to use, then set it
if os.Getenv("GOMAXPROCS") == "" {
runtime.GOMAXPROCS(runtime.NumCPU())
}
var wrapConfig panicwrap.WrapConfig
if !panicwrap.Wrapped(&wrapConfig) {
// Determine where logs should go in general (requested by the user)
logWriter, err := logOutput()
if err != nil {
fmt.Fprintf(os.Stderr, "Couldn't setup log output: %s", err)
return 1
}
if logWriter == nil {
logWriter = ioutil.Discard
}
// We also always send logs to a temporary file that we use in case
// We always send logs to a temporary file that we use in case
// there is a panic. Otherwise, we delete it.
logTempFile, err := ioutil.TempFile("", "packer-log")
if err != nil {
......@@ -46,41 +50,59 @@ func realMain() int {
defer os.Remove(logTempFile.Name())
defer logTempFile.Close()
// Reset the log variables to minimize work in the subprocess
os.Setenv("PACKER_LOG", "")
os.Setenv("PACKER_LOG_FILE", "")
// Tell the logger to log to this file
os.Setenv(EnvLog, "")
os.Setenv(EnvLogFile, "")
// Create the configuration for panicwrap and wrap our executable
wrapConfig := &panicwrap.WrapConfig{
Handler: panicHandler(logTempFile),
Writer: io.MultiWriter(logTempFile, logWriter),
}
// Setup the prefixed readers that send data properly to
// stdout/stderr.
doneCh := make(chan struct{})
outR, outW := io.Pipe()
go copyOutput(outR, doneCh)
exitStatus, err := panicwrap.Wrap(wrapConfig)
// Create the configuration for panicwrap and wrap our executable
wrapConfig.Handler = panicHandler(logTempFile)
wrapConfig.Writer = io.MultiWriter(logTempFile, logWriter)
wrapConfig.Stdout = outW
exitStatus, err := panicwrap.Wrap(&wrapConfig)
if err != nil {
fmt.Fprintf(os.Stderr, "Couldn't start Packer: %s", err)
return 1
}
// If >= 0, we're the parent, so just exit
if exitStatus >= 0 {
// Close the stdout writer so that our copy process can finish
outW.Close()
// Wait for the output copying to finish
<-doneCh
return exitStatus
}
// We're the child, so just close the tempfile we made in order to
// save file handles since the tempfile is only used by the parent.
logTempFile.Close()
}
// Call the real main
return wrappedMain()
}
// wrappedMain is called only when we're wrapped by panicwrap and
// returns the exit status to exit with.
func wrappedMain() int {
// If there is no explicit number of Go threads to use, then set it
if os.Getenv("GOMAXPROCS") == "" {
runtime.GOMAXPROCS(runtime.NumCPU())
}
log.SetOutput(os.Stderr)
log.Printf(
"Packer Version: %s %s %s",
packer.Version, packer.VersionPrerelease, packer.GitCommit)
"[INFO] Packer version: %s %s %s",
Version, VersionPrerelease, GitCommit)
log.Printf("Packer Target OS/Arch: %s %s", runtime.GOOS, runtime.GOARCH)
log.Printf("Built with Go Version: %s", runtime.Version())
......@@ -118,16 +140,14 @@ func wrappedMain() int {
defer plugin.CleanupClients()
// Create the environment configuration
envConfig := packer.DefaultEnvironmentConfig()
envConfig.Cache = cache
envConfig.Commands = config.CommandNames()
envConfig.Components.Builder = config.LoadBuilder
envConfig.Components.Command = config.LoadCommand
envConfig.Components.Hook = config.LoadHook
envConfig.Components.PostProcessor = config.LoadPostProcessor
envConfig.Components.Provisioner = config.LoadProvisioner
EnvConfig = *packer.DefaultEnvironmentConfig()
EnvConfig.Cache = cache
EnvConfig.Components.Builder = config.LoadBuilder
EnvConfig.Components.Hook = config.LoadHook
EnvConfig.Components.PostProcessor = config.LoadPostProcessor
EnvConfig.Components.Provisioner = config.LoadProvisioner
if machineReadable {
envConfig.Ui = &packer.MachineReadableUi{
EnvConfig.Ui = &packer.MachineReadableUi{
Writer: os.Stdout,
}
......@@ -139,17 +159,18 @@ func wrappedMain() int {
}
}
env, err := packer.NewEnvironment(envConfig)
if err != nil {
fmt.Fprintf(os.Stderr, "Packer initialization error: \n\n%s\n", err)
return 1
}
//setupSignalHandlers(env)
setupSignalHandlers(env)
cli := &cli.CLI{
Args: args,
Commands: Commands,
HelpFunc: cli.BasicHelpFunc("packer"),
HelpWriter: os.Stdout,
}
exitCode, err := env.Cli(args)
exitCode, err := cli.Run()
if err != nil {
fmt.Fprintf(os.Stderr, "Error executing CLI: %s\n", err.Error())
fmt.Fprintf(os.Stderr, "Error executing CLI: %s\n", err)
return 1
}
......@@ -220,20 +241,44 @@ func loadConfig() (*config, error) {
return &config, nil
}
// logOutput determines where we should send logs (if anywhere).
func logOutput() (logOutput io.Writer, err error) {
logOutput = ioutil.Discard
if os.Getenv("PACKER_LOG") != "" {
logOutput = os.Stderr
// copyOutput uses output prefixes to determine whether data on stdout
// should go to stdout or stderr. This is due to panicwrap using stderr
// as the log and error channel.
func copyOutput(r io.Reader, doneCh chan<- struct{}) {
defer close(doneCh)
if logPath := os.Getenv("PACKER_LOG_PATH"); logPath != "" {
var err error
logOutput, err = os.Create(logPath)
pr, err := prefixedio.NewReader(r)
if err != nil {
return nil, err
panic(err)
}
stderrR, err := pr.Prefix(ErrorPrefix)
if err != nil {
panic(err)
}
stdoutR, err := pr.Prefix(OutputPrefix)
if err != nil {
panic(err)
}
defaultR, err := pr.Prefix("")
if err != nil {
panic(err)
}
return
var wg sync.WaitGroup
wg.Add(3)
go func() {
defer wg.Done()
io.Copy(os.Stderr, stderrR)
}()
go func() {
defer wg.Done()
io.Copy(os.Stdout, stdoutR)
}()
go func() {
defer wg.Done()
io.Copy(os.Stdout, defaultR)
}()
wg.Wait()
}
package packer
// A command is a runnable sub-command of the `packer` application.
// When `packer` is called with the proper subcommand, this will be
// called.
//
// The mapping of command names to command interfaces is in the
// Environment struct.
type Command interface {
// Help should return long-form help text that includes the command-line
// usage, a brief few sentences explaining the function of the command,
// and the complete list of flags the command accepts.
Help() string
// Run should run the actual command with the given environmet and
// command-line arguments. It should return the exit status when it is
// finished.
Run(env Environment, args []string) int
// Synopsis should return a one-line, short synopsis of the command.
// This should be less than 50 characters ideally.
Synopsis() string
}
package packer
type TestCommand struct {
runArgs []string
runCalled bool
runEnv Environment
}
func (tc *TestCommand) Help() string {
return "bar"
}
func (tc *TestCommand) Run(env Environment, args []string) int {
tc.runCalled = true
tc.runArgs = args
tc.runEnv = env
return 0
}
func (tc *TestCommand) Synopsis() string {
return "foo"
}
......@@ -4,19 +4,12 @@ package packer
import (
"errors"
"fmt"
"log"
"os"
"sort"
"strings"
"sync"
)
// The function type used to lookup Builder implementations.
type BuilderFunc func(name string) (Builder, error)
// The function type used to lookup Command implementations.
type CommandFunc func(name string) (Command, error)
// The function type used to lookup Hook implementations.
type HookFunc func(name string) (Hook, error)
......@@ -31,7 +24,6 @@ type ProvisionerFunc func(name string) (Provisioner, error)
// commands, etc.
type ComponentFinder struct {
Builder BuilderFunc
Command CommandFunc
Hook HookFunc
PostProcessor PostProcessorFunc
Provisioner ProvisionerFunc
......@@ -45,7 +37,6 @@ type ComponentFinder struct {
type Environment interface {
Builder(string) (Builder, error)
Cache() Cache
Cli([]string) (int, error)
Hook(string) (Hook, error)
PostProcessor(string) (PostProcessor, error)
Provisioner(string) (Provisioner, error)
......@@ -56,7 +47,6 @@ type Environment interface {
// environment.
type coreEnvironment struct {
cache Cache
commands []string
components ComponentFinder
ui Ui
}
......@@ -64,22 +54,14 @@ type coreEnvironment struct {
// This struct configures new environments.
type EnvironmentConfig struct {
Cache Cache
Commands []string
Components ComponentFinder
Ui Ui
}
type helpCommandEntry struct {
i int
key string
synopsis string
}
// DefaultEnvironmentConfig returns a default EnvironmentConfig that can
// be used to create a new enviroment with NewEnvironment with sane defaults.
func DefaultEnvironmentConfig() *EnvironmentConfig {
config := &EnvironmentConfig{}
config.Commands = make([]string, 0)
config.Ui = &BasicUi{
Reader: os.Stdin,
Writer: os.Stdout,
......@@ -98,7 +80,6 @@ func NewEnvironment(config *EnvironmentConfig) (resultEnv Environment, err error
env := &coreEnvironment{}
env.cache = config.Cache
env.commands = config.Commands
env.components = config.Components
env.ui = config.Ui
......@@ -109,10 +90,6 @@ func NewEnvironment(config *EnvironmentConfig) (resultEnv Environment, err error
env.components.Builder = func(string) (Builder, error) { return nil, nil }
}
if env.components.Command == nil {
env.components.Command = func(string) (Command, error) { return nil, nil }
}
if env.components.Hook == nil {
env.components.Hook = func(string) (Hook, error) { return nil, nil }
}
......@@ -199,159 +176,6 @@ func (e *coreEnvironment) Provisioner(name string) (p Provisioner, err error) {
return
}
// Executes a command as if it was typed on the command-line interface.
// The return value is the exit code of the command.
func (e *coreEnvironment) Cli(args []string) (result int, err error) {
log.Printf("Environment.Cli: %#v\n", args)
// If we have no arguments, just short-circuit here and print the help
if len(args) == 0 {
e.printHelp()
return 1, nil
}
// This variable will track whether or not we're supposed to print
// the help or not.
isHelp := false
for _, arg := range args {
if arg == "-h" || arg == "--help" {
isHelp = true
break
}
}
// Trim up to the command name
for i, v := range args {
if len(v) > 0 && v[0] != '-' {
args = args[i:]
break
}
}
log.Printf("command + args: %#v", args)
version := args[0] == "version"
if !version {
for _, arg := range args {
if arg == "--version" || arg == "-v" {
version = true
break
}
}
}
var command Command
if version {
command = new(versionCommand)
}
if command == nil {
command, err = e.components.Command(args[0])
if err != nil {
return
}
// If we still don't have a command, show the help.
if command == nil {
e.ui.Error(fmt.Sprintf("Unknown command: %s\n", args[0]))
e.printHelp()
return 1, nil
}
}
// If we're supposed to print help, then print the help of the
// command rather than running it.
if isHelp {
e.ui.Say(command.Help())
return 0, nil
}
log.Printf("Executing command: %s\n", args[0])
return command.Run(e, args[1:]), nil
}
// Prints the CLI help to the UI.
func (e *coreEnvironment) printHelp() {
// Created a sorted slice of the map keys and record the longest
// command name so we can better format the output later.
maxKeyLen := 0
for _, command := range e.commands {
if len(command) > maxKeyLen {
maxKeyLen = len(command)
}
}
// Sort the keys
sort.Strings(e.commands)
// Create the communication/sync mechanisms to get the synopsis' of
// the various commands. We do this in parallel since the overhead
// of the subprocess underneath is very expensive and this speeds things
// up an incredible amount.
var wg sync.WaitGroup
ch := make(chan *helpCommandEntry)
for i, key := range e.commands {
wg.Add(1)
// Get the synopsis in a goroutine since it may take awhile
// to subprocess out.
go func(i int, key string) {
defer wg.Done()
var synopsis string
command, err := e.components.Command(key)
if err != nil {
synopsis = fmt.Sprintf("Error loading command: %s", err.Error())
} else if command == nil {
return
} else {
synopsis = command.Synopsis()
}
// Pad the key with spaces so that they're all the same width
key = fmt.Sprintf("%s%s", key, strings.Repeat(" ", maxKeyLen-len(key)))
// Output the command and the synopsis
ch <- &helpCommandEntry{
i: i,
key: key,
synopsis: synopsis,
}
}(i, key)
}
e.ui.Say("usage: packer [--version] [--help] <command> [<args>]\n")
e.ui.Say("Available commands are:")
// Make a goroutine that just waits for all the synopsis gathering
// to complete, and then output it.
synopsisDone := make(chan struct{})
go func() {
defer close(synopsisDone)
entries := make([]string, len(e.commands))
for entry := range ch {
e.ui.Machine("command", entry.key, entry.synopsis)
message := fmt.Sprintf(" %s %s", entry.key, entry.synopsis)
entries[entry.i] = message
}
for _, message := range entries {
if message != "" {
e.ui.Say(message)
}
}
}()
// Wait to complete getting the synopsis' then close the channel
wg.Wait()
close(ch)
<-synopsisDone
e.ui.Say("\nGlobally recognized options:")
e.ui.Say(" -machine-readable Machine-readable output format.")
}
// Returns the UI for the environment. The UI is the interface that should
// be used for all communication with the outside world.
func (e *coreEnvironment) Ui() Ui {
......
......@@ -6,8 +6,6 @@ import (
"io/ioutil"
"log"
"os"
"reflect"
"strings"
"testing"
)
......@@ -43,13 +41,6 @@ func testEnvironment() Environment {
return env
}
func TestEnvironment_DefaultConfig_Commands(t *testing.T) {
config := DefaultEnvironmentConfig()
if len(config.Commands) != 0 {
t.Fatalf("bad: %#v", config.Commands)
}
}
func TestEnvironment_DefaultConfig_Ui(t *testing.T) {
config := DefaultEnvironmentConfig()
if config.Ui == nil {
......@@ -91,7 +82,6 @@ func TestEnvironment_NilComponents(t *testing.T) {
// anything but if there is a panic in the test then yeah, something
// went wrong.
env.Builder("foo")
env.Cli([]string{"foo"})
env.Hook("foo")
env.PostProcessor("foo")
env.Provisioner("foo")
......@@ -154,117 +144,6 @@ func TestEnvironment_Cache(t *testing.T) {
}
}
func TestEnvironment_Cli_Error(t *testing.T) {
config := DefaultEnvironmentConfig()
config.Components.Command = func(n string) (Command, error) { return nil, errors.New("foo") }
env, _ := NewEnvironment(config)
_, err := env.Cli([]string{"foo"})
if err == nil {
t.Fatal("should have error")
}
if err.Error() != "foo" {
t.Fatalf("bad: %s", err)
}
}
func TestEnvironment_Cli_CallsRun(t *testing.T) {
command := &TestCommand{}
commands := make(map[string]Command)
commands["foo"] = command
config := &EnvironmentConfig{}
config.Commands = []string{"foo"}
config.Components.Command = func(n string) (Command, error) { return commands[n], nil }
env, _ := NewEnvironment(config)
exitCode, err := env.Cli([]string{"foo", "bar", "baz"})
if err != nil {
t.Fatalf("err: %s", err)
}
if exitCode != 0 {
t.Fatalf("bad: %d", exitCode)
}
if !command.runCalled {
t.Fatal("command should be run")
}
if command.runEnv != env {
t.Fatalf("bad env: %#v", command.runEnv)
}
if !reflect.DeepEqual(command.runArgs, []string{"bar", "baz"}) {
t.Fatalf("bad: %#v", command.runArgs)
}
}
func TestEnvironment_DefaultCli_Empty(t *testing.T) {
defaultEnv := testEnvironment()
// Test with no args
exitCode, _ := defaultEnv.Cli([]string{})
if exitCode != 1 {
t.Fatalf("bad: %d", exitCode)
}
// Test with only blank args
exitCode, _ = defaultEnv.Cli([]string{""})
if exitCode != 1 {
t.Fatalf("bad: %d", exitCode)
}
}
func TestEnvironment_DefaultCli_Help(t *testing.T) {
defaultEnv := testEnvironment()
// A little lambda to help us test the output actually contains help
testOutput := func() {
buffer := defaultEnv.Ui().(*BasicUi).Writer.(*bytes.Buffer)
output := buffer.String()
buffer.Reset()
if !strings.Contains(output, "usage: packer") {
t.Fatalf("should contain help: %#v", output)
}
}
// Test "--help"
exitCode, _ := defaultEnv.Cli([]string{"--help"})
if exitCode != 1 {
t.Fatalf("bad: %d", exitCode)
}
testOutput()
// Test "-h"
exitCode, _ = defaultEnv.Cli([]string{"--help"})
if exitCode != 1 {
t.Fatalf("bad: %d", exitCode)
}
testOutput()
}
func TestEnvironment_DefaultCli_Version(t *testing.T) {
defaultEnv := testEnvironment()
versionCommands := []string{"version", "--version", "-v"}
for _, command := range versionCommands {
exitCode, _ := defaultEnv.Cli([]string{command})
if exitCode != 0 {
t.Fatalf("bad: %d", exitCode)
}
// Test the --version and -v can appear anywhere
exitCode, _ = defaultEnv.Cli([]string{"bad", command})
if command != "version" {
if exitCode != 0 {
t.Fatalf("bad: %d", exitCode)
}
} else {
if exitCode != 1 {
t.Fatalf("bad: %d", exitCode)
}
}
}
}
func TestEnvironment_Hook(t *testing.T) {
hook := &MockHook{}
hooks := make(map[string]Hook)
......
......@@ -138,17 +138,6 @@ func (c *Client) Builder() (packer.Builder, error) {
return &cmdBuilder{client.Builder(), c}, nil
}
// Returns a command implementation that is communicating over this
// client. If the client hasn't been started, this will start it.
func (c *Client) Command() (packer.Command, error) {
client, err := c.packrpcClient()
if err != nil {
return nil, err
}
return &cmdCommand{client.Command(), c}, nil
}
// Returns a hook implementation that is communicating over this
// client. If the client hasn't been started, this will start it.
func (c *Client) Hook() (packer.Hook, error) {
......
package plugin
import (
"github.com/mitchellh/packer/packer"
"log"
)
type cmdCommand struct {
command packer.Command
client *Client
}
func (c *cmdCommand) Help() (result string) {
defer func() {
r := recover()
c.checkExit(r, func() { result = "" })
}()
result = c.command.Help()
return
}
func (c *cmdCommand) Run(e packer.Environment, args []string) (exitCode int) {
defer func() {
r := recover()
c.checkExit(r, func() { exitCode = 1 })
}()
exitCode = c.command.Run(e, args)
return
}
func (c *cmdCommand) Synopsis() (result string) {
defer func() {
r := recover()
c.checkExit(r, func() {
result = ""
})
}()
result = c.command.Synopsis()
return
}
func (c *cmdCommand) checkExit(p interface{}, cb func()) {
if c.client.Exited() {
cb()
} else if p != nil && !Killed {
log.Panic(p)
}
}
package plugin
import (
"github.com/mitchellh/packer/packer"
"os/exec"
"testing"
)
type helperCommand byte
func (helperCommand) Help() string {
return "2"
}
func (helperCommand) Run(packer.Environment, []string) int {
return 42
}
func (helperCommand) Synopsis() string {
return "1"
}
func TestCommand_NoExist(t *testing.T) {
c := NewClient(&ClientConfig{Cmd: exec.Command("i-should-not-exist")})
defer c.Kill()
_, err := c.Command()
if err == nil {
t.Fatal("should have error")
}
}
func TestCommand_Good(t *testing.T) {
c := NewClient(&ClientConfig{Cmd: helperProcess("command")})
defer c.Kill()
command, err := c.Command()
if err != nil {
t.Fatalf("should not have error: %s", err)
}
result := command.Synopsis()
if result != "1" {
t.Errorf("synopsis not correct: %s", result)
}
result = command.Help()
if result != "2" {
t.Errorf("help not correct: %s", result)
}
}
......@@ -61,14 +61,6 @@ func TestHelperProcess(*testing.T) {
}
server.RegisterBuilder(new(packer.MockBuilder))
server.Serve()
case "command":
server, err := Server()
if err != nil {
log.Printf("[ERR] %s", err)
os.Exit(1)
}
server.RegisterCommand(new(helperCommand))
server.Serve()
case "hook":
server, err := Server()
if err != nil {
......
......@@ -10,7 +10,6 @@ package plugin
import (
"errors"
"fmt"
"github.com/mitchellh/packer/packer"
packrpc "github.com/mitchellh/packer/packer/rpc"
"io/ioutil"
"log"
......@@ -38,8 +37,6 @@ const APIVersion = "4"
// Server waits for a connection to this plugin and returns a Packer
// RPC server that you can use to register components and serve them.
func Server() (*packrpc.Server, error) {
log.Printf("Plugin build against Packer '%s'", packer.GitCommit)
if os.Getenv(MagicCookieKey) != MagicCookieValue {
return nil, errors.New(
"Please do not execute plugins directly. Packer will execute these for you.")
......
......@@ -90,13 +90,6 @@ func (c *Client) Cache() packer.Cache {
}
}
func (c *Client) Command() packer.Command {
return &command{
client: c.client,
mux: c.mux,
}
}
func (c *Client) Communicator() packer.Communicator {
return &communicator{
client: c.client,
......
package rpc
import (
"github.com/mitchellh/packer/packer"
"net/rpc"
)
// A Command is an implementation of the packer.Command interface where the
// command is actually executed over an RPC connection.
type command struct {
client *rpc.Client
mux *muxBroker
}
// A CommandServer wraps a packer.Command and makes it exportable as part
// of a Golang RPC server.
type CommandServer struct {
command packer.Command
mux *muxBroker
}
type CommandRunArgs struct {
Args []string
StreamId uint32
}
type CommandSynopsisArgs byte
func (c *command) Help() (result string) {
err := c.client.Call("Command.Help", new(interface{}), &result)
if err != nil {
panic(err)
}
return
}
func (c *command) Run(env packer.Environment, args []string) (result int) {
nextId := c.mux.NextId()
server := newServerWithMux(c.mux, nextId)
server.RegisterEnvironment(env)
go server.Serve()
rpcArgs := &CommandRunArgs{
Args: args,
StreamId: nextId,
}
err := c.client.Call("Command.Run", rpcArgs, &result)
if err != nil {
panic(err)
}
return
}
func (c *command) Synopsis() (result string) {
err := c.client.Call("Command.Synopsis", CommandSynopsisArgs(0), &result)
if err != nil {
panic(err)
}
return
}
func (c *CommandServer) Help(args *interface{}, reply *string) error {
*reply = c.command.Help()
return nil
}
func (c *CommandServer) Run(args *CommandRunArgs, reply *int) error {
client, err := newClientWithMux(c.mux, args.StreamId)
if err != nil {
return NewBasicError(err)
}
defer client.Close()
*reply = c.command.Run(client.Environment(), args.Args)
return nil
}
func (c *CommandServer) Synopsis(args *CommandSynopsisArgs, reply *string) error {
*reply = c.command.Synopsis()
return nil
}
package rpc
import (
"github.com/mitchellh/packer/packer"
"reflect"
"testing"
)
type TestCommand struct {
runArgs []string
runCalled bool
runEnv packer.Environment
}
func (tc *TestCommand) Help() string {
return "bar"
}
func (tc *TestCommand) Run(env packer.Environment, args []string) int {
tc.runCalled = true
tc.runArgs = args
tc.runEnv = env
return 0
}
func (tc *TestCommand) Synopsis() string {
return "foo"
}
func TestRPCCommand(t *testing.T) {
// Create the command
command := new(TestCommand)
// Start the server
client, server := testClientServer(t)
defer client.Close()
defer server.Close()
server.RegisterCommand(command)
commClient := client.Command()
//Test Help
help := commClient.Help()
if help != "bar" {
t.Fatalf("bad: %s", help)
}
// Test run
runArgs := []string{"foo", "bar"}
testEnv := &testEnvironment{}
exitCode := commClient.Run(testEnv, runArgs)
if !reflect.DeepEqual(command.runArgs, runArgs) {
t.Fatalf("bad: %#v", command.runArgs)
}
if exitCode != 0 {
t.Fatalf("bad: %d", exitCode)
}
if command.runEnv == nil {
t.Fatal("runEnv should not be nil")
}
// Test Synopsis
synopsis := commClient.Synopsis()
if synopsis != "foo" {
t.Fatalf("bad: %#v", synopsis)
}
}
func TestCommand_Implements(t *testing.T) {
var _ packer.Command = new(command)
}
......@@ -20,10 +20,6 @@ type EnvironmentServer struct {
mux *muxBroker
}
type EnvironmentCliArgs struct {
Args []string
}
func (e *Environment) Builder(name string) (b packer.Builder, err error) {
var streamId uint32
err = e.client.Call("Environment.Builder", name, &streamId)
......@@ -53,12 +49,6 @@ func (e *Environment) Cache() packer.Cache {
return client.Cache()
}
func (e *Environment) Cli(args []string) (result int, err error) {
rpcArgs := &EnvironmentCliArgs{args}
err = e.client.Call("Environment.Cli", rpcArgs, &result)
return
}
func (e *Environment) Hook(name string) (h packer.Hook, err error) {
var streamId uint32
err = e.client.Call("Environment.Hook", name, &streamId)
......@@ -138,11 +128,6 @@ func (e *EnvironmentServer) Cache(args *interface{}, reply *uint32) error {
return nil
}
func (e *EnvironmentServer) Cli(args *EnvironmentCliArgs, reply *int) (err error) {
*reply, err = e.env.Cli(args.Args)
return
}
func (e *EnvironmentServer) Hook(name string, reply *uint32) error {
hook, err := e.env.Hook(name)
if err != nil {
......
......@@ -2,7 +2,6 @@ package rpc
import (
"github.com/mitchellh/packer/packer"
"reflect"
"testing"
)
......@@ -95,19 +94,6 @@ func TestEnvironmentRPC(t *testing.T) {
t.Fatal("should be called")
}
// Test Cli
cliArgs := []string{"foo", "bar"}
result, _ := eClient.Cli(cliArgs)
if !e.cliCalled {
t.Fatal("should be called")
}
if !reflect.DeepEqual(e.cliArgs, cliArgs) {
t.Fatalf("bad: %#v", e.cliArgs)
}
if result != 42 {
t.Fatalf("bad: %#v", result)
}
// Test Provisioner
_, _ = eClient.Provisioner("foo")
if !e.provCalled {
......
......@@ -88,13 +88,6 @@ func (s *Server) RegisterCache(c packer.Cache) {
})
}
func (s *Server) RegisterCommand(c packer.Command) {
s.server.RegisterName(DefaultCommandEndpoint, &CommandServer{
command: c,
mux: s.mux,
})
}
func (s *Server) RegisterCommunicator(c packer.Communicator) {
s.server.RegisterName(DefaultCommunicatorEndpoint, &CommunicatorServer{
c: c,
......
......@@ -119,6 +119,8 @@ func ParseTemplate(data []byte, vars map[string]string) (t *Template, err error)
}
if rawTpl.MinimumPackerVersion != "" {
// TODO: NOPE! Replace this
Version := "1.0"
vCur, err := version.NewVersion(Version)
if err != nil {
panic(err)
......
package packer
import (
"bytes"
"fmt"
)
// The git commit that is being compiled. This will be filled in by the
// compiler for source builds.
var GitCommit string
// This should be check to a callback to check for the latest version.
//
// The global nature of this variable is dirty, but a version checker
// really shouldn't change anyways.
var VersionChecker VersionCheckFunc
// The version of packer.
const Version = "0.7.1"
// Any pre-release marker for the version. If this is "" (empty string),
// then it means that it is a final release. Otherwise, this is the
// pre-release marker.
const VersionPrerelease = ""
// VersionCheckFunc is the callback that is called to check the latest
// version of Packer.
type VersionCheckFunc func(string) (VersionCheckInfo, error)
// VersionCheckInfo is the return value for the VersionCheckFunc that
// contains the latest version information.
type VersionCheckInfo struct {
Outdated bool
Latest string
Alerts []string
}
type versionCommand byte
func (versionCommand) Help() string {
return `usage: packer version
Outputs the version of Packer that is running. There are no additional
command-line flags for this command.`
}
func (versionCommand) Run(env Environment, args []string) int {
env.Ui().Machine("version", Version)
env.Ui().Machine("version-prelease", VersionPrerelease)
env.Ui().Machine("version-commit", GitCommit)
env.Ui().Say(VersionString())
if VersionChecker != nil {
current := Version
if VersionPrerelease != "" {
current += fmt.Sprintf(".%s", VersionPrerelease)
}
info, err := VersionChecker(current)
if err != nil {
env.Ui().Say(fmt.Sprintf("\nError checking latest version: %s", err))
}
if info.Outdated {
env.Ui().Say(fmt.Sprintf(
"\nYour version of Packer is out of date! The latest version\n"+
"is %s. You can update by downloading from www.packer.io.",
info.Latest))
}
}
return 0
}
func (versionCommand) Synopsis() string {
return "print Packer version"
}
// VersionString returns the Packer version in human-readable
// form complete with pre-release and git commit info if it is
// available.
func VersionString() string {
var versionString bytes.Buffer
fmt.Fprintf(&versionString, "Packer v%s", Version)
if VersionPrerelease != "" {
fmt.Fprintf(&versionString, ".%s", VersionPrerelease)
if GitCommit != "" {
fmt.Fprintf(&versionString, " (%s)", GitCommit)
}
}
return versionString.String()
}
......@@ -2,10 +2,11 @@ package main
import (
"fmt"
"github.com/mitchellh/panicwrap"
"io"
"os"
"strings"
"github.com/mitchellh/panicwrap"
)
// This is output if a panic happens.
......
package main
import (
"github.com/mitchellh/packer/command/build"
"github.com/mitchellh/packer/packer/plugin"
)
func main() {
server, err := plugin.Server()
if err != nil {
panic(err)
}
server.RegisterCommand(new(build.Command))
server.Serve()
}
package main
import (
"github.com/mitchellh/packer/command/fix"
"github.com/mitchellh/packer/packer/plugin"
)
func main() {
server, err := plugin.Server()
if err != nil {
panic(err)
}
server.RegisterCommand(new(fix.Command))
server.Serve()
}
package main
import (
"github.com/mitchellh/packer/command/inspect"
"github.com/mitchellh/packer/packer/plugin"
)
func main() {
server, err := plugin.Server()
if err != nil {
panic(err)
}
server.RegisterCommand(new(inspect.Command))
server.Serve()
}
package main
import (
"github.com/mitchellh/packer/command/validate"
"github.com/mitchellh/packer/packer/plugin"
)
func main() {
server, err := plugin.Server()
if err != nil {
panic(err)
}
server.RegisterCommand(new(validate.Command))
server.Serve()
}
package main
// The git commit that was compiled. This will be filled in by the compiler.
var GitCommit string
// The main version number that is being run at the moment.
const Version = "0.8.0"
// A pre-release marker for the version. If this is "" (empty string)
// then it means that it is a final release. Otherwise, this is a pre-release
// such as "dev" (in development), "beta", "rc1", etc.
const VersionPrerelease = "dev"
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