2013-03-24 17:03:53 -04:00
|
|
|
// The packer package contains the core components of Packer.
|
|
|
|
package packer
|
|
|
|
|
2013-03-24 19:28:35 -04:00
|
|
|
import (
|
2013-04-20 21:00:03 -04:00
|
|
|
"errors"
|
2013-03-24 19:28:35 -04:00
|
|
|
"fmt"
|
2013-05-08 17:45:17 -04:00
|
|
|
"log"
|
2013-03-24 19:28:35 -04:00
|
|
|
"os"
|
|
|
|
"sort"
|
|
|
|
"strings"
|
|
|
|
)
|
2013-03-24 17:03:53 -04:00
|
|
|
|
2013-05-10 19:41:35 -04:00
|
|
|
// The function type used to lookup Builder implementations.
|
2013-05-07 23:42:49 -04:00
|
|
|
type BuilderFunc func(name string) (Builder, error)
|
2013-05-05 17:47:17 -04:00
|
|
|
|
2013-05-10 19:41:35 -04:00
|
|
|
// The function type used to lookup Command implementations.
|
2013-05-07 23:42:49 -04:00
|
|
|
type CommandFunc func(name string) (Command, error)
|
2013-05-05 17:47:17 -04:00
|
|
|
|
2013-05-11 02:15:13 -04:00
|
|
|
// The function type used to lookup Hook implementations.
|
|
|
|
type HookFunc func(name string) (Hook, error)
|
|
|
|
|
2013-05-22 18:39:30 -04:00
|
|
|
// The function type used to lookup Provisioner implementations.
|
|
|
|
type ProvisionerFunc func(name string) (Provisioner, error)
|
|
|
|
|
2013-05-11 02:15:13 -04:00
|
|
|
// ComponentFinder is a struct that contains the various function
|
|
|
|
// pointers necessary to look up components of Packer such as builders,
|
|
|
|
// commands, etc.
|
|
|
|
type ComponentFinder struct {
|
2013-05-22 18:39:30 -04:00
|
|
|
Builder BuilderFunc
|
|
|
|
Command CommandFunc
|
|
|
|
Hook HookFunc
|
|
|
|
Provisioner ProvisionerFunc
|
2013-05-11 02:15:13 -04:00
|
|
|
}
|
|
|
|
|
2013-05-02 17:03:55 -04:00
|
|
|
// The environment interface provides access to the configuration and
|
|
|
|
// state of a single Packer run.
|
2013-03-24 17:03:53 -04:00
|
|
|
//
|
2013-05-02 17:03:55 -04:00
|
|
|
// It allows for things such as executing CLI commands, getting the
|
|
|
|
// list of available builders, and more.
|
|
|
|
type Environment interface {
|
2013-05-11 02:15:13 -04:00
|
|
|
Builder(string) (Builder, error)
|
2013-06-10 00:09:09 -04:00
|
|
|
Cache() Cache
|
2013-05-11 02:15:13 -04:00
|
|
|
Cli([]string) (int, error)
|
|
|
|
Hook(string) (Hook, error)
|
2013-05-22 18:39:30 -04:00
|
|
|
Provisioner(string) (Provisioner, error)
|
2013-05-02 17:03:55 -04:00
|
|
|
Ui() Ui
|
|
|
|
}
|
|
|
|
|
|
|
|
// An implementation of an Environment that represents the Packer core
|
|
|
|
// environment.
|
|
|
|
type coreEnvironment struct {
|
2013-06-10 00:09:09 -04:00
|
|
|
cache Cache
|
2013-05-20 19:50:35 -04:00
|
|
|
commands []string
|
|
|
|
components ComponentFinder
|
|
|
|
ui Ui
|
2013-03-24 17:03:53 -04:00
|
|
|
}
|
|
|
|
|
2013-04-15 23:26:38 -04:00
|
|
|
// This struct configures new environments.
|
|
|
|
type EnvironmentConfig struct {
|
2013-06-10 00:09:09 -04:00
|
|
|
Cache Cache
|
2013-05-20 19:50:35 -04:00
|
|
|
Commands []string
|
|
|
|
Components ComponentFinder
|
|
|
|
Ui Ui
|
2013-04-15 23:26:38 -04:00
|
|
|
}
|
|
|
|
|
2013-04-20 21:00:03 -04:00
|
|
|
// DefaultEnvironmentConfig returns a default EnvironmentConfig that can
|
|
|
|
// be used to create a new enviroment with NewEnvironment with sane defaults.
|
|
|
|
func DefaultEnvironmentConfig() *EnvironmentConfig {
|
|
|
|
config := &EnvironmentConfig{}
|
2013-05-05 17:47:17 -04:00
|
|
|
config.Commands = make([]string, 0)
|
2013-04-20 21:00:03 -04:00
|
|
|
config.Ui = &ReaderWriterUi{os.Stdin, os.Stdout}
|
|
|
|
return config
|
|
|
|
}
|
2013-04-15 23:26:38 -04:00
|
|
|
|
2013-04-20 21:00:03 -04:00
|
|
|
// This creates a new environment
|
2013-05-02 17:03:55 -04:00
|
|
|
func NewEnvironment(config *EnvironmentConfig) (resultEnv Environment, err error) {
|
2013-04-20 21:00:03 -04:00
|
|
|
if config == nil {
|
|
|
|
err = errors.New("config must be given to initialize environment")
|
|
|
|
return
|
2013-04-15 23:26:38 -04:00
|
|
|
}
|
|
|
|
|
2013-05-02 17:03:55 -04:00
|
|
|
env := &coreEnvironment{}
|
2013-06-10 00:09:09 -04:00
|
|
|
env.cache = config.Cache
|
2013-05-05 17:47:17 -04:00
|
|
|
env.commands = config.Commands
|
2013-05-11 02:15:13 -04:00
|
|
|
env.components = config.Components
|
2013-04-20 21:00:03 -04:00
|
|
|
env.ui = config.Ui
|
|
|
|
|
2013-05-11 02:15:13 -04:00
|
|
|
// We want to make sure the components have valid function pointers.
|
|
|
|
// If a function pointer was not given, we assume that the function
|
|
|
|
// will just return a nil component.
|
|
|
|
if env.components.Builder == nil {
|
|
|
|
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 }
|
|
|
|
}
|
|
|
|
|
2013-05-22 18:39:30 -04:00
|
|
|
if env.components.Provisioner == nil {
|
|
|
|
env.components.Provisioner = func(string) (Provisioner, error) { return nil, nil }
|
|
|
|
}
|
|
|
|
|
2013-06-10 00:18:06 -04:00
|
|
|
// The default cache is just the system temporary directory
|
|
|
|
if env.cache == nil {
|
|
|
|
env.cache = &FileCache{CacheDir: os.TempDir()}
|
|
|
|
}
|
|
|
|
|
2013-05-02 17:03:55 -04:00
|
|
|
resultEnv = env
|
2013-04-20 21:00:03 -04:00
|
|
|
return
|
2013-03-24 17:03:53 -04:00
|
|
|
}
|
|
|
|
|
2013-05-05 18:12:55 -04:00
|
|
|
// Returns a builder of the given name that is registered with this
|
|
|
|
// environment.
|
2013-05-07 23:42:49 -04:00
|
|
|
func (e *coreEnvironment) Builder(name string) (b Builder, err error) {
|
2013-05-11 02:15:13 -04:00
|
|
|
b, err = e.components.Builder(name)
|
2013-05-07 23:42:49 -04:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if b == nil {
|
|
|
|
err = fmt.Errorf("No builder returned for name: %s", name)
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
2013-05-05 18:12:55 -04:00
|
|
|
}
|
|
|
|
|
2013-06-10 00:09:09 -04:00
|
|
|
// Returns the cache for this environment
|
|
|
|
func (e *coreEnvironment) Cache() Cache {
|
|
|
|
return e.cache
|
|
|
|
}
|
|
|
|
|
2013-05-11 02:15:13 -04:00
|
|
|
// Returns a hook of the given name that is registered with this
|
|
|
|
// environment.
|
|
|
|
func (e *coreEnvironment) Hook(name string) (h Hook, err error) {
|
|
|
|
h, err = e.components.Hook(name)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if h == nil {
|
|
|
|
err = fmt.Errorf("No hook returned for name: %s", name)
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2013-05-22 18:39:30 -04:00
|
|
|
// Returns a provisioner for the given name that is registered with this
|
|
|
|
// environment.
|
|
|
|
func (e *coreEnvironment) Provisioner(name string) (p Provisioner, err error) {
|
|
|
|
p, err = e.components.Provisioner(name)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if p == nil {
|
|
|
|
err = fmt.Errorf("No provisioner returned for name: %s", name)
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2013-03-24 17:03:53 -04:00
|
|
|
// Executes a command as if it was typed on the command-line interface.
|
|
|
|
// The return value is the exit code of the command.
|
2013-05-07 23:42:49 -04:00
|
|
|
func (e *coreEnvironment) Cli(args []string) (result int, err error) {
|
2013-05-08 17:45:17 -04:00
|
|
|
log.Printf("Environment.Cli: %#v\n", args)
|
|
|
|
|
2013-06-02 14:49:01 -04:00
|
|
|
// If we have no arguments, just short-circuit here and print the help
|
|
|
|
if len(args) == 0 {
|
2013-05-02 17:03:55 -04:00
|
|
|
e.printHelp()
|
2013-05-07 23:42:49 -04:00
|
|
|
return 1, nil
|
2013-03-24 17:03:53 -04:00
|
|
|
}
|
|
|
|
|
2013-06-02 18:03:02 -04:00
|
|
|
// 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 v[0] != '-' {
|
|
|
|
args = args[i:]
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Printf("command + args: %#v", args)
|
|
|
|
|
2013-05-05 17:47:17 -04:00
|
|
|
version := args[0] == "version"
|
|
|
|
if !version {
|
2013-03-24 17:03:53 -04:00
|
|
|
for _, arg := range args {
|
|
|
|
if arg == "--version" || arg == "-v" {
|
2013-05-05 17:47:17 -04:00
|
|
|
version = true
|
2013-03-24 17:03:53 -04:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2013-05-05 17:47:17 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
var command Command
|
|
|
|
if version {
|
|
|
|
command = new(versionCommand)
|
|
|
|
}
|
|
|
|
|
|
|
|
if command == nil {
|
2013-05-11 02:15:13 -04:00
|
|
|
command, err = e.components.Command(args[0])
|
2013-05-07 23:42:49 -04:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
2013-03-24 17:03:53 -04:00
|
|
|
|
|
|
|
// If we still don't have a command, show the help.
|
|
|
|
if command == nil {
|
2013-05-08 17:45:17 -04:00
|
|
|
log.Printf("Environment.CLI: command not found: %s\n", args[0])
|
2013-05-02 17:03:55 -04:00
|
|
|
e.printHelp()
|
2013-05-07 23:42:49 -04:00
|
|
|
return 1, nil
|
2013-03-24 17:03:53 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-02 14:49:01 -04:00
|
|
|
// If we're supposed to print help, then print the help of the
|
|
|
|
// command rather than running it.
|
2013-06-02 18:03:02 -04:00
|
|
|
if isHelp {
|
|
|
|
e.ui.Say(command.Help())
|
|
|
|
return 0, nil
|
2013-06-02 14:49:01 -04:00
|
|
|
}
|
|
|
|
|
2013-05-08 19:59:36 -04:00
|
|
|
log.Printf("Executing command: %s\n", args[0])
|
2013-05-07 23:42:49 -04:00
|
|
|
return command.Run(e, args[1:]), nil
|
2013-03-24 17:03:53 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Prints the CLI help to the UI.
|
2013-05-02 17:03:55 -04:00
|
|
|
func (e *coreEnvironment) printHelp() {
|
2013-03-24 19:28:35 -04:00
|
|
|
// Created a sorted slice of the map keys and record the longest
|
|
|
|
// command name so we can better format the output later.
|
|
|
|
i := 0
|
|
|
|
maxKeyLen := 0
|
2013-05-05 17:47:17 -04:00
|
|
|
for _, command := range e.commands {
|
|
|
|
if len(command) > maxKeyLen {
|
|
|
|
maxKeyLen = len(command)
|
2013-03-24 19:28:35 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
i++
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sort the keys
|
2013-05-05 17:47:17 -04:00
|
|
|
sort.Strings(e.commands)
|
2013-03-24 19:28:35 -04:00
|
|
|
|
2013-06-01 22:15:32 -04:00
|
|
|
e.ui.Say("usage: packer [--version] [--help] <command> [<args>]\n")
|
|
|
|
e.ui.Say("Available commands are:")
|
2013-05-05 17:47:17 -04:00
|
|
|
for _, key := range e.commands {
|
2013-05-07 23:42:49 -04:00
|
|
|
var synopsis string
|
|
|
|
|
2013-05-11 02:15:13 -04:00
|
|
|
command, err := e.components.Command(key)
|
2013-05-07 23:42:49 -04:00
|
|
|
if err != nil {
|
|
|
|
synopsis = fmt.Sprintf("Error loading command: %s", err.Error())
|
2013-05-08 20:28:05 -04:00
|
|
|
} else if command == nil {
|
|
|
|
continue
|
2013-05-07 23:42:49 -04:00
|
|
|
} else {
|
|
|
|
synopsis = command.Synopsis()
|
|
|
|
}
|
2013-03-24 19:28:35 -04:00
|
|
|
|
|
|
|
// Pad the key with spaces so that they're all the same width
|
2013-03-24 19:36:02 -04:00
|
|
|
key = fmt.Sprintf("%v%v", key, strings.Repeat(" ", maxKeyLen-len(key)))
|
2013-03-24 19:28:35 -04:00
|
|
|
|
|
|
|
// Output the command and the synopsis
|
2013-06-01 22:15:32 -04:00
|
|
|
e.ui.Say(fmt.Sprintf(" %v %v", key, synopsis))
|
2013-03-24 19:28:35 -04:00
|
|
|
}
|
2013-03-24 17:03:53 -04:00
|
|
|
}
|
2013-03-24 19:41:58 -04:00
|
|
|
|
|
|
|
// Returns the UI for the environment. The UI is the interface that should
|
|
|
|
// be used for all communication with the outside world.
|
2013-05-02 17:03:55 -04:00
|
|
|
func (e *coreEnvironment) Ui() Ui {
|
2013-03-24 19:41:58 -04:00
|
|
|
return e.ui
|
|
|
|
}
|