Compare commits

..

2 Commits

Author SHA1 Message Date
Mitchell Hashimoto 0b23c8addf v0.3.7 2013-09-09 14:48:42 -07:00
Mitchell Hashimoto aa658e982b provisioner/puppet-masterless: detailed exit codes 2013-09-09 14:46:25 -07:00
150 changed files with 1356 additions and 4638 deletions
-130
View File
@@ -1,133 +1,3 @@
## 0.3.11 (November 4, 2013)
FEATURES:
* builder/amazon/ebs: Ability to specify which availability zone to create
instance in. [GH-536]
IMPROVEMENTS:
* core: builders can now give warnings during validation. warnings won't
fail the build but may hint at potential future problems.
* builder/digitalocean: Can now specify a droplet name
* builder/virtualbox: Can now disable guest addition download entirely
by setting "guest_additions_mode" to "disable" [GH-580]
* builder/virtualbox,vmware: ISO urls can now be https [GH-587]
* builder/virtualbox,vmware: Warning if shutdown command is not specified,
since it is a common case of data loss.
BUG FIXES:
* core: Won't panic when writing to a bad pipe. [GH-560]
* builder/amazon/all: Properly scrub access key and secret key from logs.
[GH-554]
* builder/openstack: Properly scrub password from logs [GH-554]
* builder/virtualbox: No panic if SSH host port min/max is the same. [GH-594]
* builder/vmware: checks if `ifconfig` is in `/sbin` [GH-591]
* builder/vmware: Host IP lookup works for non-C locales. [GH-592]
* common/uuid: Use cryptographically secure PRNG when generating
UUIDs. [GH-552]
* communicator/ssh: File uploads that exceed the size of memory no longer
cause crashes. [GH-561]
## 0.3.10 (October 20, 2013)
FEATURES:
* Ansible provisioner
IMPROVEMENTS:
* post-processor/vagrant: support instance-store AMIs built by Packer. [GH-502]
* post-processor/vagrant: can now specify compression level to use
when creating the box. [GH-506]
BUG FIXES:
* builder/all: timeout waiting for SSH connection is a failure. [GH-491]
* builder/amazon: Scrub sensitive data from the logs. [GH-521]
* builder/amazon: Handle the situation where an EC2 instance might not
be immediately available. [GH-522]
* builder/amazon/chroot: Files copied into the chroot remove destination
before copy, fixing issues with dangling symlinks. [GH-500]
* builder/digitalocean: don't panic if erroneous API response doesn't
contain error message. [GH-492]
* builder/digitalocean: scrub API keys from config debug output [GH-516]
* builder/virtualbox: error if VirtualBox version cant be detected. [GH-488]
* builder/virtualbox: detect if vboxdrv isn't properly setup. [GH-488]
* builder/virtualbox: sleep a bit before export to ensure the sesssion
is unlocked. [GH-512]
* builder/virtualbox: create SATA drives properly on VirtualBox 4.3 [GH-547]
* builder/virtualbox: support user templates in SSH key path. [GH-539]
* builder/vmware: support user templates in SSH key path. [GH-539]
* communicator/ssh: Fix issue where a panic could arise from a nil
dereference. [GH-525]
* post-processor/vagrant: Fix issue with VirtualBox OVA. [GH-548]
* provisioner/salt: Move salt states to correct remote directory. [GH-513]
* provisioner/shell: Won't block on certain scripts on Windows anymore.
[GH-507]
## 0.3.9 (October 2, 2013)
FEATURES:
* The Amazon chroot builder is now able to run without any `sudo` privileges
by using the "command_wrapper" configuration. [GH-430]
* Chef provisioner supports environments. [GH-483]
BUG FIXES:
* core: default user variable values don't need to be strings. [GH-456]
* builder/amazon-chroot: Fix errors with waitin for state change. [GH-459]
* builder/digitalocean: Use proper error message JSON key (DO API change).
* communicator/ssh: SCP uploads now work properly when directories
contain symlinks. [GH-449]
* provisioner/chef-solo: Data bags and roles path are now properly
populated when set. [GH-470]
* provisioner/shell: Windows line endings are actually properly changed
to Unix line endings. [GH-477]
## 0.3.8 (September 22, 2013)
FEATURES:
* core: You can now specify `only` and `except` configurations on any
provisioner or post-processor to specify a list of builds that they
are valid for. [GH-438]
* builders/virtualbox: Guest additions can be attached rather than uploaded,
easier to handle for Windows guests. [GH-405]
* provisioner/chef-solo: Ability to specify a custom Chef configuration
template.
* provisioner/chef-solo: Roles and data bags support. [GH-348]
IMPROVEMENTS:
* core: User variables can now be used for integer, boolean, etc.
values. [GH-418]
* core: Plugins made with incompatible versions will no longer load.
* builder/amazon/all: Interrupts work while waiting for AMI to be ready.
* provisioner/shell: Script line-endings are automatically converted to
Unix-style line-endings. Can be disabled by setting "binary" to "true".
[GH-277]
BUG FIXES:
* core: Set TCP KeepAlives on internally created RPC connections so that
they don't die. [GH-416]
* builder/amazon/all: While waiting for AMI, will detect "failed" state.
* builder/amazon/all: Waiting for state will detect if the resource (AMI,
instance, etc.) disappears from under it.
* builder/amazon/instance: Exclude only contents of /tmp, not /tmp
itself. [GH-437]
* builder/amazon/instance: Make AccessKey/SecretKey available to bundle
command even when they come from the environment. [GH-434]
* builder/virtualbox: F1-F12 and delete scancodes now work. [GH-425]
* post-processor/vagrant: Override configurations properly work. [GH-426]
* provisioner/puppet-masterless: Fix failure case when both facter vars
are used and prevent_sudo. [GH-415]
* provisioner/puppet-masterless: User variables now work properly in
manifest file and hiera path. [GH-448]
## 0.3.7 (September 9, 2013)
BACKWARDS INCOMPATIBILITIES:
+1 -9
View File
@@ -18,12 +18,6 @@ it raises the chances we can quickly merge or address your contributions.
* Make sure you test against the latest released version. It is possible
we already fixed the bug you're experiencing.
* Run the command with debug ouput with the environment variable
`PACKER_LOG`. For example: `PACKER_LOG=1 packer build template.json`. Take
the *entire* output and create a [gist](https://gist.github.com) for linking
to in your issue. Packer should strip sensitive keys from the output,
but take a look through just in case.
* Provide a reproducible test case. If a contributor can't reproduce an
issue, then it dramatically lowers the chances it'll get fixed. And in
some cases, the issue will eventually be closed.
@@ -53,9 +47,7 @@ it raises the chances we can quickly merge or address your contributions.
If you have never worked with Go before, you will have to complete the
following steps in order to be able to compile and test Packer.
1. Install Go. On a Mac, you can `brew install go`. Make sure the Go
version is at least Go 1.1. Packer will not work with anything less than
Go 1.1.
1. Install Go. On a Mac, you can `brew install go`.
2. Set and export the `GOPATH` environment variable. For example, you can
add `export GOPATH=$HOME/Documents/golang` to your `.bash_profile`.
+3 -9
View File
@@ -2,22 +2,16 @@ NO_COLOR=\033[0m
OK_COLOR=\033[32;01m
ERROR_COLOR=\033[31;01m
WARN_COLOR=\033[33;01m
DEPS = $(go list -f '{{range .TestImports}}{{.}} {{end}}' ./...)
all: deps
@mkdir -p bin/
@echo "$(OK_COLOR)==> Building$(NO_COLOR)"
@bash --norc -i ./scripts/build.sh
@./scripts/build.sh
deps:
@echo "$(OK_COLOR)==> Installing dependencies$(NO_COLOR)"
@go get -d -v ./...
@echo $(DEPS) | xargs -n1 go get -d
updatedeps:
@echo "$(OK_COLOR)==> Updating all dependencies$(NO_COLOR)"
@go get -d -v -u ./...
@echo $(DEPS) | xargs -n1 go get -d -u
@go list -f '{{range .TestImports}}{{.}} {{end}}' ./... | xargs -n1 go get -d
clean:
@rm -rf bin/ local/ pkg/ src/ website/.sass-cache website/build
@@ -29,4 +23,4 @@ test: deps
@echo "$(OK_COLOR)==> Testing Packer...$(NO_COLOR)"
go test ./...
.PHONY: all clean deps format test updatedeps
.PHONY: all deps format test
+18 -23
View File
@@ -27,33 +27,30 @@ type Config struct {
awscommon.AMIConfig `mapstructure:",squash"`
ChrootMounts [][]string `mapstructure:"chroot_mounts"`
CommandWrapper string `mapstructure:"command_wrapper"`
CopyFiles []string `mapstructure:"copy_files"`
DevicePath string `mapstructure:"device_path"`
MountCommand string `mapstructure:"mount_command"`
MountPath string `mapstructure:"mount_path"`
SourceAmi string `mapstructure:"source_ami"`
UnmountCommand string `mapstructure:"unmount_command"`
tpl *packer.ConfigTemplate
}
type wrappedCommandTemplate struct {
Command string
}
type Builder struct {
config Config
runner multistep.Runner
}
func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
func (b *Builder) Prepare(raws ...interface{}) error {
md, err := common.DecodeConfig(&b.config, raws...)
if err != nil {
return nil, err
return err
}
b.config.tpl, err = packer.NewConfigTemplate()
if err != nil {
return nil, err
return err
}
b.config.tpl.UserVars = b.config.PackerUserVars
b.config.tpl.Funcs(awscommon.TemplateFuncs)
@@ -81,14 +78,18 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
b.config.CopyFiles = []string{"/etc/resolv.conf"}
}
if b.config.CommandWrapper == "" {
b.config.CommandWrapper = "{{.Command}}"
if b.config.MountCommand == "" {
b.config.MountCommand = "mount"
}
if b.config.MountPath == "" {
b.config.MountPath = "packer-amazon-chroot-volumes/{{.Device}}"
}
if b.config.UnmountCommand == "" {
b.config.UnmountCommand = "umount"
}
// Accumulate any errors
errs := common.CheckUnusedConfig(md)
errs = packer.MultiErrorAppend(errs, b.config.AccessConfig.Prepare(b.config.tpl)...)
@@ -126,8 +127,10 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
}
templates := map[string]*string{
"device_path": &b.config.DevicePath,
"source_ami": &b.config.SourceAmi,
"device_path": &b.config.DevicePath,
"mount_command": &b.config.MountCommand,
"source_ami": &b.config.SourceAmi,
"unmount_command": &b.config.UnmountCommand,
}
for n, ptr := range templates {
@@ -140,11 +143,11 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
}
if errs != nil && len(errs.Errors) > 0 {
return nil, errs
return errs
}
log.Println(common.ScrubConfig(b.config, b.config.AccessKey, b.config.SecretKey))
return nil, nil
log.Printf("Config: %+v", b.config)
return nil
}
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
@@ -164,20 +167,12 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
ec2conn := ec2.New(auth, region)
wrappedCommand := func(command string) (string, error) {
return b.config.tpl.Process(
b.config.CommandWrapper, &wrappedCommandTemplate{
Command: command,
})
}
// Setup the state bag and initial state for the steps
state := new(multistep.BasicStateBag)
state.Put("config", &b.config)
state.Put("ec2", ec2conn)
state.Put("hook", hook)
state.Put("ui", ui)
state.Put("wrappedCommand", CommandWrapper(wrappedCommand))
// Build the steps
steps := []multistep.Step{
+7 -42
View File
@@ -26,10 +26,7 @@ func TestBuilderPrepare_AMIName(t *testing.T) {
// Test good
config["ami_name"] = "foo"
warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err := b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -37,10 +34,7 @@ func TestBuilderPrepare_AMIName(t *testing.T) {
// Test bad
config["ami_name"] = "foo {{"
b = Builder{}
warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err = b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -48,10 +42,7 @@ func TestBuilderPrepare_AMIName(t *testing.T) {
// Test bad
delete(config, "ami_name")
b = Builder{}
warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err = b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -62,10 +53,7 @@ func TestBuilderPrepare_ChrootMounts(t *testing.T) {
config := testConfig()
config["chroot_mounts"] = nil
warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err := b.Prepare(config)
if err != nil {
t.Errorf("err: %s", err)
}
@@ -73,10 +61,7 @@ func TestBuilderPrepare_ChrootMounts(t *testing.T) {
config["chroot_mounts"] = [][]string{
[]string{"bad"},
}
warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err = b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -86,33 +71,13 @@ func TestBuilderPrepare_SourceAmi(t *testing.T) {
config := testConfig()
config["source_ami"] = ""
warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err := b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
config["source_ami"] = "foo"
warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
if err != nil {
t.Errorf("err: %s", err)
}
}
func TestBuilderPrepare_CommandWrapper(t *testing.T) {
b := &Builder{}
config := testConfig()
config["command_wrapper"] = "echo hi; {{.Command}}"
warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err = b.Prepare(config)
if err != nil {
t.Errorf("err: %s", err)
}
-15
View File
@@ -1,15 +0,0 @@
package chroot
import (
"os/exec"
)
// CommandWrapper is a type that given a command, will possibly modify that
// command in-flight. This might return an error.
type CommandWrapper func(string) (string, error)
// ShellCommand takes a command string and returns an *exec.Cmd to execute
// it within the context of a shell (/bin/sh).
func ShellCommand(command string) *exec.Cmd {
return exec.Command("/bin/sh", "-c", command)
}
+38 -24
View File
@@ -1,10 +1,8 @@
package chroot
import (
"fmt"
"github.com/mitchellh/packer/packer"
"io"
"io/ioutil"
"log"
"os"
"os/exec"
@@ -15,18 +13,16 @@ import (
// Communicator is a special communicator that works by executing
// commands locally but within a chroot.
type Communicator struct {
Chroot string
CmdWrapper CommandWrapper
Chroot string
}
func (c *Communicator) Start(cmd *packer.RemoteCmd) error {
command, err := c.CmdWrapper(
fmt.Sprintf("chroot %s %s", c.Chroot, cmd.Command))
chrootCmdPath, err := exec.LookPath("chroot")
if err != nil {
return err
}
localCmd := ShellCommand(command)
localCmd := exec.Command(chrootCmdPath, c.Chroot, "/bin/sh", "-c", cmd.Command)
localCmd.Stdin = cmd.Stdin
localCmd.Stdout = cmd.Stdout
localCmd.Stderr = cmd.Stderr
@@ -50,7 +46,7 @@ func (c *Communicator) Start(cmd *packer.RemoteCmd) error {
}
log.Printf(
"Chroot execution exited with '%d': '%s'",
"Chroot executation ended with '%d': '%s'",
exitStatus, cmd.Command)
cmd.SetExited(exitStatus)
}()
@@ -61,31 +57,49 @@ func (c *Communicator) Start(cmd *packer.RemoteCmd) error {
func (c *Communicator) Upload(dst string, r io.Reader) error {
dst = filepath.Join(c.Chroot, dst)
log.Printf("Uploading to chroot dir: %s", dst)
tf, err := ioutil.TempFile("", "packer-amazon-chroot")
if err != nil {
return fmt.Errorf("Error preparing shell script: %s", err)
}
defer os.Remove(tf.Name())
io.Copy(tf, r)
cpCmd, err := c.CmdWrapper(fmt.Sprintf("cp %s %s", tf.Name(), dst))
f, err := os.Create(dst)
if err != nil {
return err
}
defer f.Close()
return ShellCommand(cpCmd).Run()
if _, err := io.Copy(f, r); err != nil {
return err
}
return nil
}
func (c *Communicator) UploadDir(dst string, src string, exclude []string) error {
// TODO: remove any file copied if it appears in `exclude`
chrootDest := filepath.Join(c.Chroot, dst)
log.Printf("Uploading directory '%s' to '%s'", src, chrootDest)
cpCmd, err := c.CmdWrapper(fmt.Sprintf("cp -R %s* %s", src, chrootDest))
if err != nil {
return err
walkFn := func(fullPath string, info os.FileInfo, err error) error {
if err != nil {
return err
}
path, err := filepath.Rel(src, fullPath)
if err != nil {
return err
}
for _, e := range exclude {
if e == path {
log.Printf("Skipping excluded file: %s", path)
return nil
}
}
dstPath := filepath.Join(dst, path)
f, err := os.Open(fullPath)
if err != nil {
return err
}
defer f.Close()
return c.Upload(dstPath, f)
}
return ShellCommand(cpCmd).Run()
log.Printf("Uploading directory '%s' to '%s'", src, dst)
return filepath.Walk(src, walkFn)
}
func (c *Communicator) Download(src string, w io.Writer) error {
-1
View File
@@ -1 +0,0 @@
package chroot
-44
View File
@@ -1,44 +0,0 @@
package chroot
import (
"fmt"
"io/ioutil"
"os"
"testing"
)
func TestCopyFile(t *testing.T) {
first, err := ioutil.TempFile("", "copy_files_test")
if err != nil {
t.Fatalf("couldn't create temp file.")
}
defer os.Remove(first.Name())
newName := first.Name() + "-new"
payload := "copy_files_test.go payload"
if _, err = first.WriteString(payload); err != nil {
t.Fatalf("Couldn't write payload to first file.")
}
first.Sync()
cmd := ShellCommand(fmt.Sprintf("cp %s %s", first.Name(), newName))
if err := cmd.Run(); err != nil {
t.Fatalf("Couldn't copy file")
}
defer os.Remove(newName)
second, err := os.Open(newName)
if err != nil {
t.Fatalf("Couldn't open copied file.")
}
defer second.Close()
var copiedPayload = make([]byte, len(payload))
if _, err := second.Read(copiedPayload); err != nil {
t.Fatalf("Couldn't open copied file for reading.")
}
if string(copiedPayload) != payload {
t.Fatalf("payload not copied.")
}
}
+6 -7
View File
@@ -60,8 +60,7 @@ func (s *StepAttachVolume) Run(state multistep.StateBag) multistep.StepAction {
return nil, "", errors.New("No attachments on volume.")
}
a := resp.Volumes[0].Attachments[0]
return a, a.Status, nil
return nil, resp.Volumes[0].Attachments[0].Status, nil
},
}
@@ -112,12 +111,12 @@ func (s *StepAttachVolume) CleanupFunc(state multistep.StateBag) error {
return nil, "", err
}
v := resp.Volumes[0]
if len(v.Attachments) > 0 {
return v, v.Attachments[0].Status, nil
} else {
return v, "detached", nil
state := "detached"
if len(resp.Volumes[0].Attachments) > 0 {
state = resp.Volumes[0].Attachments[0].Status
}
return nil, state, nil
},
}
@@ -15,12 +15,10 @@ func (s *StepChrootProvision) Run(state multistep.StateBag) multistep.StepAction
hook := state.Get("hook").(packer.Hook)
mountPath := state.Get("mount_path").(string)
ui := state.Get("ui").(packer.Ui)
wrappedCommand := state.Get("wrappedCommand").(CommandWrapper)
// Create our communicator
comm := &Communicator{
Chroot: mountPath,
CmdWrapper: wrappedCommand,
Chroot: mountPath,
}
// Provision
+44 -26
View File
@@ -1,11 +1,12 @@
package chroot
import (
"bytes"
"fmt"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"io"
"log"
"os"
"path/filepath"
)
@@ -22,8 +23,6 @@ func (s *StepCopyFiles) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*Config)
mountPath := state.Get("mount_path").(string)
ui := state.Get("ui").(packer.Ui)
wrappedCommand := state.Get("wrappedCommand").(CommandWrapper)
stderr := new(bytes.Buffer)
s.files = make([]string, 0, len(config.CopyFiles))
if len(config.CopyFiles) > 0 {
@@ -33,20 +32,8 @@ func (s *StepCopyFiles) Run(state multistep.StateBag) multistep.StepAction {
chrootPath := filepath.Join(mountPath, path)
log.Printf("Copying '%s' to '%s'", path, chrootPath)
cmdText, err := wrappedCommand(fmt.Sprintf("cp --remove-destination %s %s", path, chrootPath))
if err != nil {
err := fmt.Errorf("Error building copy command: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
stderr.Reset()
cmd := ShellCommand(cmdText)
cmd.Stderr = stderr
if err := cmd.Run(); err != nil {
err := fmt.Errorf(
"Error copying file: %s\nnStderr: %s", err, stderr.String())
if err := s.copySingle(chrootPath, path); err != nil {
err := fmt.Errorf("Error copying file: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
@@ -67,18 +54,11 @@ func (s *StepCopyFiles) Cleanup(state multistep.StateBag) {
}
}
func (s *StepCopyFiles) CleanupFunc(state multistep.StateBag) error {
wrappedCommand := state.Get("wrappedCommand").(CommandWrapper)
func (s *StepCopyFiles) CleanupFunc(multistep.StateBag) error {
if s.files != nil {
for _, file := range s.files {
log.Printf("Removing: %s", file)
localCmdText, err := wrappedCommand(fmt.Sprintf("rm -f %s", file))
if err != nil {
return err
}
localCmd := ShellCommand(localCmdText)
if err := localCmd.Run(); err != nil {
if err := os.Remove(file); err != nil {
return err
}
}
@@ -87,3 +67,41 @@ func (s *StepCopyFiles) CleanupFunc(state multistep.StateBag) error {
s.files = nil
return nil
}
func (s *StepCopyFiles) copySingle(dst, src string) error {
// Stat the src file so we can copy the mode later
srcInfo, err := os.Stat(src)
if err != nil {
return err
}
// Remove any existing destination file
if err := os.Remove(dst); err != nil {
return err
}
// Copy the files
srcF, err := os.Open(src)
if err != nil {
return err
}
defer srcF.Close()
dstF, err := os.Create(dst)
if err != nil {
return err
}
defer dstF.Close()
if _, err := io.Copy(dstF, srcF); err != nil {
return err
}
dstF.Close()
// Match the mode
if err := os.Chmod(dst, srcInfo.Mode()); err != nil {
return err
}
return nil
}
+1 -2
View File
@@ -75,8 +75,7 @@ func (s *StepCreateVolume) Run(state multistep.StateBag) multistep.StepAction {
return nil, "", err
}
v := resp.Volumes[0]
return v, v.Status, nil
return nil, resp.Volumes[0].Status, nil
},
}
+6 -18
View File
@@ -7,6 +7,7 @@ import (
"github.com/mitchellh/packer/packer"
"log"
"os"
"os/exec"
"path/filepath"
)
@@ -27,7 +28,6 @@ func (s *StepMountDevice) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*Config)
ui := state.Get("ui").(packer.Ui)
device := state.Get("device").(string)
wrappedCommand := state.Get("wrappedCommand").(CommandWrapper)
mountPath, err := config.tpl.Process(config.MountPath, &mountPathData{
Device: filepath.Base(device),
@@ -59,16 +59,8 @@ func (s *StepMountDevice) Run(state multistep.StateBag) multistep.StepAction {
ui.Say("Mounting the root device...")
stderr := new(bytes.Buffer)
mountCommand, err := wrappedCommand(
fmt.Sprintf("mount %s %s", device, mountPath))
if err != nil {
err := fmt.Errorf("Error creating mount command: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
cmd := ShellCommand(mountCommand)
mountCommand := fmt.Sprintf("%s %s %s", config.MountCommand, device, mountPath)
cmd := exec.Command("/bin/sh", "-c", mountCommand)
cmd.Stderr = stderr
if err := cmd.Run(); err != nil {
err := fmt.Errorf(
@@ -98,16 +90,12 @@ func (s *StepMountDevice) CleanupFunc(state multistep.StateBag) error {
return nil
}
config := state.Get("config").(*Config)
ui := state.Get("ui").(packer.Ui)
wrappedCommand := state.Get("wrappedCommand").(CommandWrapper)
ui.Say("Unmounting the root device...")
unmountCommand, err := wrappedCommand(fmt.Sprintf("umount %s", s.mountPath))
if err != nil {
return fmt.Errorf("Error creating unmount command: %s", err)
}
cmd := ShellCommand(unmountCommand)
unmountCommand := fmt.Sprintf("%s %s", config.UnmountCommand, s.mountPath)
cmd := exec.Command("/bin/sh", "-c", unmountCommand)
if err := cmd.Run(); err != nil {
return fmt.Errorf("Error unmounting root device: %s", err)
}
+9 -18
View File
@@ -6,6 +6,7 @@ import (
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"os"
"os/exec"
)
// StepMountExtra mounts the attached device.
@@ -20,7 +21,6 @@ func (s *StepMountExtra) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*Config)
mountPath := state.Get("mount_path").(string)
ui := state.Get("ui").(packer.Ui)
wrappedCommand := state.Get("wrappedCommand").(CommandWrapper)
s.mounts = make([]string, 0, len(config.ChrootMounts))
@@ -42,19 +42,13 @@ func (s *StepMountExtra) Run(state multistep.StateBag) multistep.StepAction {
ui.Message(fmt.Sprintf("Mounting: %s", mountInfo[2]))
stderr := new(bytes.Buffer)
mountCommand, err := wrappedCommand(fmt.Sprintf(
"mount %s %s %s",
mountCommand := fmt.Sprintf(
"%s %s %s %s",
config.MountCommand,
flags,
mountInfo[1],
innerPath))
if err != nil {
err := fmt.Errorf("Error creating mount command: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
cmd := ShellCommand(mountCommand)
innerPath)
cmd := exec.Command("/bin/sh", "-c", mountCommand)
cmd.Stderr = stderr
if err := cmd.Run(); err != nil {
err := fmt.Errorf(
@@ -85,18 +79,15 @@ func (s *StepMountExtra) CleanupFunc(state multistep.StateBag) error {
return nil
}
wrappedCommand := state.Get("wrappedCommand").(CommandWrapper)
config := state.Get("config").(*Config)
for len(s.mounts) > 0 {
var path string
lastIndex := len(s.mounts) - 1
path, s.mounts = s.mounts[lastIndex], s.mounts[:lastIndex]
unmountCommand, err := wrappedCommand(fmt.Sprintf("umount %s", path))
if err != nil {
return fmt.Errorf("Error creating unmount command: %s", err)
}
unmountCommand := fmt.Sprintf("%s %s", config.UnmountCommand, path)
stderr := new(bytes.Buffer)
cmd := ShellCommand(unmountCommand)
cmd := exec.Command("/bin/sh", "-c", unmountCommand)
cmd.Stderr = stderr
if err := cmd.Run(); err != nil {
return fmt.Errorf(
+1 -9
View File
@@ -52,16 +52,8 @@ func (s *StepRegisterAMI) Run(state multistep.StateBag) multistep.StepAction {
state.Put("amis", amis)
// Wait for the image to become ready
stateChange := awscommon.StateChangeConf{
Conn: ec2conn,
Pending: []string{"pending"},
Target: "available",
Refresh: awscommon.AMIStateRefreshFunc(ec2conn, registerResp.ImageId),
StepState: state,
}
ui.Say("Waiting for AMI to become ready...")
if _, err := awscommon.WaitForState(&stateChange); err != nil {
if err := awscommon.WaitForAMI(ec2conn, registerResp.ImageId); err != nil {
err := fmt.Errorf("Error waiting for AMI: %s", err)
state.Put("error", err)
ui.Error(err.Error())
+1 -2
View File
@@ -51,8 +51,7 @@ func (s *StepSnapshot) Run(state multistep.StateBag) multistep.StepAction {
return nil, "", errors.New("No snapshots found.")
}
s := resp.Snapshots[0]
return s, s.Status, nil
return nil, resp.Snapshots[0].Status, nil
},
}
+1 -8
View File
@@ -18,14 +18,7 @@ type AccessConfig struct {
// Auth returns a valid aws.Auth object for access to AWS services, or
// an error if the authentication couldn't be resolved.
func (c *AccessConfig) Auth() (aws.Auth, error) {
auth, err := aws.GetAuth(c.AccessKey, c.SecretKey)
if err == nil {
// Store the accesskey and secret that we got...
c.AccessKey = auth.AccessKey
c.SecretKey = auth.SecretKey
}
return auth, err
return aws.GetAuth(c.AccessKey, c.SecretKey)
}
// Region returns the aws.Region object for access to AWS services, requesting
+30
View File
@@ -0,0 +1,30 @@
package common
import (
"github.com/mitchellh/goamz/ec2"
"log"
"time"
)
// WaitForAMI waits for the given AMI ID to become ready.
func WaitForAMI(c *ec2.EC2, imageId string) error {
for {
imageResp, err := c.Images([]string{imageId}, ec2.NewFilter())
if err != nil {
if ec2err, ok := err.(*ec2.Error); ok && ec2err.Code == "InvalidAMIID.NotFound" {
log.Println("AMI not found, probably state issues on AWS side. Trying again.")
continue
}
return err
}
if imageResp.Images[0].State == "available" {
return nil
}
log.Printf("Image in state %s, sleeping 2s before checking again",
imageResp.Images[0].State)
time.Sleep(2 * time.Second)
}
}
+11 -7
View File
@@ -1,15 +1,21 @@
package common
import (
"cgl.tideland.biz/asserts"
"github.com/mitchellh/packer/packer"
"testing"
)
func TestArtifact_Impl(t *testing.T) {
var _ packer.Artifact = new(Artifact)
assert := asserts.NewTestingAsserts(t, true)
var actual packer.Artifact
assert.Implementor(&Artifact{}, &actual, "should be an Artifact")
}
func TestArtifactId(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
expected := `east:foo,west:bar`
amis := make(map[string]string)
@@ -21,12 +27,12 @@ func TestArtifactId(t *testing.T) {
}
result := a.Id()
if result != expected {
t.Fatalf("bad: %s", result)
}
assert.Equal(result, expected, "should match output")
}
func TestArtifactString(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
expected := `AMIs were created:
east: foo
@@ -38,7 +44,5 @@ west: bar`
a := &Artifact{Amis: amis}
result := a.String()
if result != expected {
t.Fatalf("bad: %s", result)
}
assert.Equal(result, expected, "should match output")
}
+5 -8
View File
@@ -1,12 +1,14 @@
package common
import (
"cgl.tideland.biz/asserts"
"github.com/mitchellh/goamz/ec2"
"reflect"
"testing"
)
func TestBlockDevice(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
ec2Mapping := []ec2.BlockDeviceMapping{
ec2.BlockDeviceMapping{
DeviceName: "/dev/sdb",
@@ -34,11 +36,6 @@ func TestBlockDevice(t *testing.T) {
LaunchMappings: []BlockDevice{blockDevice},
}
if !reflect.DeepEqual(ec2Mapping, blockDevices.BuildAMIDevices()) {
t.Fatalf("bad: %#v", ec2Mapping)
}
if !reflect.DeepEqual(ec2Mapping, blockDevices.BuildLaunchDevices()) {
t.Fatalf("bad: %#v", ec2Mapping)
}
assert.Equal(ec2Mapping, blockDevices.BuildAMIDevices(), "should match output")
assert.Equal(ec2Mapping, blockDevices.BuildLaunchDevices(), "should match output")
}
@@ -30,48 +30,17 @@ type StateChangeConf struct {
Target string
}
// AMIStateRefreshFunc returns a StateRefreshFunc that is used to watch
// an AMI for state changes.
func AMIStateRefreshFunc(conn *ec2.EC2, imageId string) StateRefreshFunc {
return func() (interface{}, string, error) {
resp, err := conn.Images([]string{imageId}, ec2.NewFilter())
if err != nil {
if ec2err, ok := err.(*ec2.Error); ok && ec2err.Code == "InvalidAMIID.NotFound" {
// Set this to nil as if we didn't find anything.
resp = nil
} else {
log.Printf("Error on AMIStateRefresh: %s", err)
return nil, "", err
}
}
if resp == nil || len(resp.Images) == 0 {
// Sometimes AWS has consistency issues and doesn't see the
// AMI. Return an empty state.
return nil, "", nil
}
i := resp.Images[0]
return i, i.State, nil
}
}
// InstanceStateRefreshFunc returns a StateRefreshFunc that is used to watch
// an EC2 instance.
func InstanceStateRefreshFunc(conn *ec2.EC2, i *ec2.Instance) StateRefreshFunc {
return func() (interface{}, string, error) {
resp, err := conn.Instances([]string{i.InstanceId}, ec2.NewFilter())
if err != nil {
if ec2err, ok := err.(*ec2.Error); ok && ec2err.Code == "InvalidInstanceID.NotFound" {
// Set this to nil as if we didn't find anything.
resp = nil
} else {
log.Printf("Error on InstanceStateRefresh: %s", err)
return nil, "", err
}
log.Printf("Error on InstanceStateRefresh: %s", err)
return nil, "", err
}
if resp == nil || len(resp.Reservations) == 0 || len(resp.Reservations[0].Instances) == 0 {
if len(resp.Reservations) == 0 || len(resp.Reservations[0].Instances) == 0 {
// Sometimes AWS just has consistency issues and doesn't see
// our instance yet. Return an empty state.
return nil, "", nil
@@ -87,8 +56,6 @@ func InstanceStateRefreshFunc(conn *ec2.EC2, i *ec2.Instance) StateRefreshFunc {
func WaitForState(conf *StateChangeConf) (i interface{}, err error) {
log.Printf("Waiting for state to become: %s", conf.Target)
notfoundTick := 0
for {
var currentState string
i, currentState, err = conf.Refresh()
@@ -96,17 +63,9 @@ func WaitForState(conf *StateChangeConf) (i interface{}, err error) {
return
}
if i == nil {
// If we didn't find the resource, check if we have been
// not finding it for awhile, and if so, report an error.
notfoundTick += 1
if notfoundTick > 20 {
return nil, errors.New("couldn't find resource")
}
} else {
// Reset the counter for when a resource isn't found
notfoundTick = 0
// Check states only if we were able to refresh to an instance
// that exists.
if i != nil {
if currentState == conf.Target {
return
}
-2
View File
@@ -23,7 +23,6 @@ type RunConfig struct {
SubnetId string `mapstructure:"subnet_id"`
TemporaryKeyPairName string `mapstructure:"temporary_key_pair_name"`
VpcId string `mapstructure:"vpc_id"`
AvailabilityZone string `mapstructure:"availability_zone"`
// Unexported fields that are calculated from others
sshTimeout time.Duration
@@ -84,7 +83,6 @@ func (c *RunConfig) Prepare(t *packer.ConfigTemplate) []error {
"subnet_id": &c.SubnetId,
"temporary_key_pair_name": &c.TemporaryKeyPairName,
"vpc_id": &c.VpcId,
"availability_zone": &c.AvailabilityZone,
}
for n, ptr := range templates {
+2 -11
View File
@@ -40,17 +40,8 @@ func (s *StepAMIRegionCopy) Run(state multistep.StateBag) multistep.StepAction {
return multistep.ActionHalt
}
stateChange := StateChangeConf{
Conn: regionconn,
Pending: []string{"pending"},
Target: "available",
Refresh: AMIStateRefreshFunc(regionconn, resp.ImageId),
StepState: state,
}
ui.Say(fmt.Sprintf("Waiting for AMI (%s) in region (%s) to become ready...",
resp.ImageId, region))
if _, err := WaitForState(&stateChange); err != nil {
ui.Say(fmt.Sprintf("Waiting for AMI (%s) in region (%s) to become ready...", resp.ImageId, region))
if err := WaitForAMI(regionconn, resp.ImageId); err != nil {
err := fmt.Errorf("Error waiting for AMI (%s) in region (%s): %s", resp.ImageId, region, err)
state.Put("error", err)
ui.Error(err.Error())
@@ -18,7 +18,6 @@ type StepRunSourceInstance struct {
SourceAMI string
IamInstanceProfile string
SubnetId string
AvailabilityZone string
BlockDevices BlockDevices
instance *ec2.Instance
@@ -52,7 +51,6 @@ func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepActi
IamInstanceProfile: s.IamInstanceProfile,
SubnetId: s.SubnetId,
BlockDevices: s.BlockDevices.BuildLaunchDevices(),
AvailZone: s.AvailabilityZone,
}
ui.Say("Launching a source AWS instance...")
+3 -2
View File
@@ -1,10 +1,11 @@
package common
import (
"cgl.tideland.biz/identifier"
"encoding/hex"
"fmt"
"github.com/mitchellh/goamz/ec2"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/common/uuid"
"github.com/mitchellh/packer/packer"
"log"
"time"
@@ -34,7 +35,7 @@ func (s *StepSecurityGroup) Run(state multistep.StateBag) multistep.StepAction {
// Create the group
ui.Say("Creating temporary security group for this instance...")
groupName := fmt.Sprintf("packer %s", uuid.TimeOrderedUUID())
groupName := fmt.Sprintf("packer %s", hex.EncodeToString(identifier.NewUUID().Raw()))
log.Printf("Temporary group name: %s", groupName)
group := ec2.SecurityGroup{
Name: groupName,
+6 -7
View File
@@ -33,15 +33,15 @@ type Builder struct {
runner multistep.Runner
}
func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
func (b *Builder) Prepare(raws ...interface{}) error {
md, err := common.DecodeConfig(&b.config, raws...)
if err != nil {
return nil, err
return err
}
b.config.tpl, err = packer.NewConfigTemplate()
if err != nil {
return nil, err
return err
}
b.config.tpl.UserVars = b.config.PackerUserVars
b.config.tpl.Funcs(awscommon.TemplateFuncs)
@@ -53,11 +53,11 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare(b.config.tpl)...)
if errs != nil && len(errs.Errors) > 0 {
return nil, errs
return errs
}
log.Println(common.ScrubConfig(b.config, b.config.AccessKey, b.config.SecretKey))
return nil, nil
log.Printf("Config: %+v", b.config)
return nil
}
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
@@ -101,7 +101,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
SourceAMI: b.config.SourceAmi,
IamInstanceProfile: b.config.IamInstanceProfile,
SubnetId: b.config.SubnetId,
AvailabilityZone: b.config.AvailabilityZone,
BlockDevices: b.config.BlockDevices,
},
&common.StepConnectSSH{
+5 -20
View File
@@ -31,10 +31,7 @@ func TestBuilder_Prepare_BadType(t *testing.T) {
"access_key": []string{},
}
warnings, err := b.Prepare(c)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err := b.Prepare(c)
if err == nil {
t.Fatalf("prepare should fail")
}
@@ -46,10 +43,7 @@ func TestBuilderPrepare_AMIName(t *testing.T) {
// Test good
config["ami_name"] = "foo"
warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err := b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -57,10 +51,7 @@ func TestBuilderPrepare_AMIName(t *testing.T) {
// Test bad
config["ami_name"] = "foo {{"
b = Builder{}
warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err = b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -68,10 +59,7 @@ func TestBuilderPrepare_AMIName(t *testing.T) {
// Test bad
delete(config, "ami_name")
b = Builder{}
warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err = b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -83,10 +71,7 @@ func TestBuilderPrepare_InvalidKey(t *testing.T) {
// Add a random key
config["i_should_not_be_valid"] = true
warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err := b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
+1 -9
View File
@@ -39,16 +39,8 @@ func (s *stepCreateAMI) Run(state multistep.StateBag) multistep.StepAction {
state.Put("amis", amis)
// Wait for the image to become ready
stateChange := awscommon.StateChangeConf{
Conn: ec2conn,
Pending: []string{"pending"},
Target: "available",
Refresh: awscommon.AMIStateRefreshFunc(ec2conn, createResp.ImageId),
StepState: state,
}
ui.Say("Waiting for AMI to become ready...")
if _, err := awscommon.WaitForState(&stateChange); err != nil {
if err := awscommon.WaitForAMI(ec2conn, createResp.ImageId); err != nil {
err := fmt.Errorf("Error waiting for AMI: %s", err)
state.Put("error", err)
ui.Error(err.Error())
+7 -8
View File
@@ -45,15 +45,15 @@ type Builder struct {
runner multistep.Runner
}
func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
func (b *Builder) Prepare(raws ...interface{}) error {
md, err := common.DecodeConfig(&b.config, raws...)
if err != nil {
return nil, err
return err
}
b.config.tpl, err = packer.NewConfigTemplate()
if err != nil {
return nil, err
return err
}
b.config.tpl.UserVars = b.config.PackerUserVars
b.config.tpl.Funcs(awscommon.TemplateFuncs)
@@ -83,7 +83,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
"-u {{.AccountId}} " +
"-c {{.CertPath}} " +
"-r {{.Architecture}} " +
"-e {{.PrivatePath}}/* " +
"-e {{.PrivatePath}} " +
"-d {{.Destination}} " +
"-p {{.Prefix}} " +
"--batch"
@@ -156,11 +156,11 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
}
if errs != nil && len(errs.Errors) > 0 {
return nil, errs
return errs
}
log.Println(common.ScrubConfig(b.config, b.config.AccessKey, b.config.SecretKey))
return nil, nil
log.Printf("Config: %+v", b.config)
return nil
}
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
@@ -204,7 +204,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
UserDataFile: b.config.UserDataFile,
SourceAMI: b.config.SourceAmi,
SubnetId: b.config.SubnetId,
AvailabilityZone: b.config.AvailabilityZone,
BlockDevices: b.config.BlockDevices,
},
&common.StepConnectSSH{
+18 -72
View File
@@ -40,28 +40,19 @@ func TestBuilderPrepare_AccountId(t *testing.T) {
config := testConfig()
config["account_id"] = ""
warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err := b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
config["account_id"] = "foo"
warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err = b.Prepare(config)
if err != nil {
t.Errorf("err: %s", err)
}
config["account_id"] = "0123-0456-7890"
warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
@@ -77,10 +68,7 @@ func TestBuilderPrepare_AMIName(t *testing.T) {
// Test good
config["ami_name"] = "foo"
warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err := b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -88,10 +76,7 @@ func TestBuilderPrepare_AMIName(t *testing.T) {
// Test bad
config["ami_name"] = "foo {{"
b = Builder{}
warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err = b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -99,10 +84,7 @@ func TestBuilderPrepare_AMIName(t *testing.T) {
// Test bad
delete(config, "ami_name")
b = Builder{}
warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err = b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -113,10 +95,7 @@ func TestBuilderPrepare_BundleDestination(t *testing.T) {
config := testConfig()
config["bundle_destination"] = ""
warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err := b.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
@@ -131,10 +110,7 @@ func TestBuilderPrepare_BundlePrefix(t *testing.T) {
config := testConfig()
config["bundle_prefix"] = ""
warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err := b.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
@@ -150,10 +126,7 @@ func TestBuilderPrepare_InvalidKey(t *testing.T) {
// Add a random key
config["i_should_not_be_valid"] = true
warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err := b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -164,19 +137,13 @@ func TestBuilderPrepare_S3Bucket(t *testing.T) {
config := testConfig()
config["s3_bucket"] = ""
warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err := b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
config["s3_bucket"] = "foo"
warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err = b.Prepare(config)
if err != nil {
t.Errorf("err: %s", err)
}
@@ -187,19 +154,13 @@ func TestBuilderPrepare_X509CertPath(t *testing.T) {
config := testConfig()
config["x509_cert_path"] = ""
warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err := b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
config["x509_cert_path"] = "i/am/a/file/that/doesnt/exist"
warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err = b.Prepare(config)
if err == nil {
t.Error("should have error")
}
@@ -211,10 +172,7 @@ func TestBuilderPrepare_X509CertPath(t *testing.T) {
defer os.Remove(tf.Name())
config["x509_cert_path"] = tf.Name()
warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -225,19 +183,13 @@ func TestBuilderPrepare_X509KeyPath(t *testing.T) {
config := testConfig()
config["x509_key_path"] = ""
warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err := b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
config["x509_key_path"] = "i/am/a/file/that/doesnt/exist"
warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err = b.Prepare(config)
if err == nil {
t.Error("should have error")
}
@@ -249,10 +201,7 @@ func TestBuilderPrepare_X509KeyPath(t *testing.T) {
defer os.Remove(tf.Name())
config["x509_key_path"] = tf.Name()
warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -263,10 +212,7 @@ func TestBuilderPrepare_X509UploadPath(t *testing.T) {
config := testConfig()
config["x509_upload_path"] = ""
warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err := b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
+1 -9
View File
@@ -37,16 +37,8 @@ func (s *StepRegisterAMI) Run(state multistep.StateBag) multistep.StepAction {
state.Put("amis", amis)
// Wait for the image to become ready
stateChange := awscommon.StateChangeConf{
Conn: ec2conn,
Pending: []string{"pending"},
Target: "available",
Refresh: awscommon.AMIStateRefreshFunc(ec2conn, registerResp.ImageId),
StepState: state,
}
ui.Say("Waiting for AMI to become ready...")
if _, err := awscommon.WaitForState(&stateChange); err != nil {
if err := awscommon.WaitForAMI(ec2conn, registerResp.ImageId); err != nil {
err := fmt.Errorf("Error waiting for AMI: %s", err)
state.Put("error", err)
ui.Error(err.Error())
+1 -7
View File
@@ -227,13 +227,7 @@ func NewRequest(d DigitalOceanClient, path string, params url.Values) (map[strin
}
if status == "ERROR" {
statusRaw, ok := decodedResponse["message"]
if ok {
status = statusRaw.(string)
} else {
status = fmt.Sprintf(
"Unknown error. Full response body: %s", body)
}
status = decodedResponse["error_message"].(string)
}
lastErr = errors.New(fmt.Sprintf("Received error from DigitalOcean (%d): %s",
+6 -14
View File
@@ -8,7 +8,6 @@ import (
"fmt"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/common"
"github.com/mitchellh/packer/common/uuid"
"github.com/mitchellh/packer/packer"
"log"
"os"
@@ -31,7 +30,6 @@ type config struct {
ImageID uint `mapstructure:"image_id"`
SnapshotName string `mapstructure:"snapshot_name"`
DropletName string `mapstructure:"droplet_name"`
SSHUsername string `mapstructure:"ssh_username"`
SSHPort uint `mapstructure:"ssh_port"`
@@ -51,15 +49,15 @@ type Builder struct {
runner multistep.Runner
}
func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
func (b *Builder) Prepare(raws ...interface{}) error {
md, err := common.DecodeConfig(&b.config, raws...)
if err != nil {
return nil, err
return err
}
b.config.tpl, err = packer.NewConfigTemplate()
if err != nil {
return nil, err
return err
}
b.config.tpl.UserVars = b.config.PackerUserVars
@@ -97,11 +95,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
b.config.SnapshotName = "packer-{{timestamp}}"
}
if b.config.DropletName == "" {
// Default to packer-[time-ordered-uuid]
b.config.DropletName = fmt.Sprintf("packer-%s", uuid.TimeOrderedUUID())
}
if b.config.SSHUsername == "" {
// Default to "root". You can override this if your
// SourceImage has a different user account then the DO default
@@ -128,7 +121,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
"client_id": &b.config.ClientID,
"api_key": &b.config.APIKey,
"snapshot_name": &b.config.SnapshotName,
"droplet_name": &b.config.DropletName,
"ssh_username": &b.config.SSHUsername,
"ssh_timeout": &b.config.RawSSHTimeout,
"state_timeout": &b.config.RawStateTimeout,
@@ -169,11 +161,11 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
b.config.stateTimeout = stateTimeout
if errs != nil && len(errs.Errors) > 0 {
return nil, errs
return errs
}
common.ScrubConfig(b.config, b.config.ClientID, b.config.APIKey)
return nil, nil
log.Printf("Config: %+v", b.config)
return nil
}
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
+25 -152
View File
@@ -34,10 +34,7 @@ func TestBuilder_Prepare_BadType(t *testing.T) {
"api_key": []string{},
}
warnings, err := b.Prepare(c)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err := b.Prepare(c)
if err == nil {
t.Fatalf("prepare should fail")
}
@@ -49,10 +46,7 @@ func TestBuilderPrepare_APIKey(t *testing.T) {
// Test good
config["api_key"] = "foo"
warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err := b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -64,10 +58,7 @@ func TestBuilderPrepare_APIKey(t *testing.T) {
// Test bad
delete(config, "api_key")
b = Builder{}
warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err = b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -76,10 +67,7 @@ func TestBuilderPrepare_APIKey(t *testing.T) {
delete(config, "api_key")
os.Setenv("DIGITALOCEAN_API_KEY", "foo")
defer os.Setenv("DIGITALOCEAN_API_KEY", "")
warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -91,10 +79,7 @@ func TestBuilderPrepare_ClientID(t *testing.T) {
// Test good
config["client_id"] = "foo"
warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err := b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -106,10 +91,7 @@ func TestBuilderPrepare_ClientID(t *testing.T) {
// Test bad
delete(config, "client_id")
b = Builder{}
warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err = b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -118,10 +100,7 @@ func TestBuilderPrepare_ClientID(t *testing.T) {
delete(config, "client_id")
os.Setenv("DIGITALOCEAN_CLIENT_ID", "foo")
defer os.Setenv("DIGITALOCEAN_CLIENT_ID", "")
warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -133,10 +112,7 @@ func TestBuilderPrepare_InvalidKey(t *testing.T) {
// Add a random key
config["i_should_not_be_valid"] = true
warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err := b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -147,10 +123,7 @@ func TestBuilderPrepare_RegionID(t *testing.T) {
config := testConfig()
// Test default
warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err := b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -162,10 +135,7 @@ func TestBuilderPrepare_RegionID(t *testing.T) {
// Test set
config["region_id"] = 2
b = Builder{}
warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -180,10 +150,7 @@ func TestBuilderPrepare_SizeID(t *testing.T) {
config := testConfig()
// Test default
warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err := b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -195,10 +162,7 @@ func TestBuilderPrepare_SizeID(t *testing.T) {
// Test set
config["size_id"] = 67
b = Builder{}
warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -213,10 +177,7 @@ func TestBuilderPrepare_ImageID(t *testing.T) {
config := testConfig()
// Test default
warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err := b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -228,10 +189,7 @@ func TestBuilderPrepare_ImageID(t *testing.T) {
// Test set
config["size_id"] = 2
b = Builder{}
warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -246,10 +204,7 @@ func TestBuilderPrepare_SSHUsername(t *testing.T) {
config := testConfig()
// Test default
warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err := b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -261,10 +216,7 @@ func TestBuilderPrepare_SSHUsername(t *testing.T) {
// Test set
config["ssh_username"] = "foo"
b = Builder{}
warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -279,10 +231,7 @@ func TestBuilderPrepare_SSHTimeout(t *testing.T) {
config := testConfig()
// Test default
warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err := b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -294,10 +243,7 @@ func TestBuilderPrepare_SSHTimeout(t *testing.T) {
// Test set
config["ssh_timeout"] = "30s"
b = Builder{}
warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -305,10 +251,7 @@ func TestBuilderPrepare_SSHTimeout(t *testing.T) {
// Test bad
config["ssh_timeout"] = "tubes"
b = Builder{}
warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err = b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -320,10 +263,7 @@ func TestBuilderPrepare_StateTimeout(t *testing.T) {
config := testConfig()
// Test default
warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err := b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -335,10 +275,7 @@ func TestBuilderPrepare_StateTimeout(t *testing.T) {
// Test set
config["state_timeout"] = "5m"
b = Builder{}
warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -346,10 +283,7 @@ func TestBuilderPrepare_StateTimeout(t *testing.T) {
// Test bad
config["state_timeout"] = "tubes"
b = Builder{}
warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err = b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -361,10 +295,7 @@ func TestBuilderPrepare_SnapshotName(t *testing.T) {
config := testConfig()
// Test default
warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err := b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -376,10 +307,7 @@ func TestBuilderPrepare_SnapshotName(t *testing.T) {
// Test set
config["snapshot_name"] = "foobarbaz"
b = Builder{}
warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -387,10 +315,7 @@ func TestBuilderPrepare_SnapshotName(t *testing.T) {
// Test set with template
config["snapshot_name"] = "{{timestamp}}"
b = Builder{}
warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -401,55 +326,3 @@ func TestBuilderPrepare_SnapshotName(t *testing.T) {
}
}
func TestBuilderPrepare_DropletName(t *testing.T) {
var b Builder
config := testConfig()
// Test default
warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
if b.config.DropletName == "" {
t.Errorf("invalid: %s", b.config.DropletName)
}
// Test normal set
config["droplet_name"] = "foobar"
b = Builder{}
warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
// Test with template
config["droplet_name"] = "foobar-{{timestamp}}"
b = Builder{}
warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
// Test with bad template
config["droplet_name"] = "foobar-{{"
b = Builder{}
warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
if err == nil {
t.Fatal("should have error")
}
}
+6 -2
View File
@@ -1,6 +1,8 @@
package digitalocean
import (
"cgl.tideland.biz/identifier"
"encoding/hex"
"fmt"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
@@ -18,9 +20,11 @@ func (s *stepCreateDroplet) Run(state multistep.StateBag) multistep.StepAction {
ui.Say("Creating droplet...")
// Create the droplet based on configuration
dropletId, err := client.CreateDroplet(c.DropletName, c.SizeID, c.ImageID, c.RegionID, sshKeyId)
// Some random droplet name as it's temporary
name := fmt.Sprintf("packer-%s", hex.EncodeToString(identifier.NewUUID().Raw()))
// Create the droplet based on configuration
dropletId, err := client.CreateDroplet(name, c.SizeID, c.ImageID, c.RegionID, sshKeyId)
if err != nil {
err := fmt.Errorf("Error creating droplet: %s", err)
state.Put("error", err)
+5 -5
View File
@@ -1,14 +1,15 @@
package digitalocean
import (
"cgl.tideland.biz/identifier"
"code.google.com/p/go.crypto/ssh"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/hex"
"encoding/pem"
"fmt"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/common/uuid"
"github.com/mitchellh/packer/packer"
"log"
)
@@ -37,12 +38,11 @@ func (s *stepCreateSSHKey) Run(state multistep.StateBag) multistep.StepAction {
state.Put("privateKey", string(pem.EncodeToMemory(&priv_blk)))
// Marshal the public key into SSH compatible format
// TODO properly handle the public key error
pub, _ := ssh.NewPublicKey(&priv.PublicKey)
pub_sshformat := string(ssh.MarshalAuthorizedKey(pub))
pub := priv.PublicKey
pub_sshformat := string(ssh.MarshalAuthorizedKey(&pub))
// The name of the public key on DO
name := fmt.Sprintf("packer-%s", uuid.TimeOrderedUUID())
name := fmt.Sprintf("packer-%s", hex.EncodeToString(identifier.NewUUID().Raw()))
// Create the key!
keyId, err := client.CreateKey(name, pub_sshformat)
+11 -7
View File
@@ -1,15 +1,21 @@
package openstack
import (
"cgl.tideland.biz/asserts"
"github.com/mitchellh/packer/packer"
"testing"
)
func TestArtifact_Impl(t *testing.T) {
var _ packer.Artifact = new(Artifact)
assert := asserts.NewTestingAsserts(t, true)
var actual packer.Artifact
assert.Implementor(&Artifact{}, &actual, "should be an Artifact")
}
func TestArtifactId(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
expected := `b8cdf55b-c916-40bd-b190-389ec144c4ed`
a := &Artifact{
@@ -17,19 +23,17 @@ func TestArtifactId(t *testing.T) {
}
result := a.Id()
if result != expected {
t.Fatalf("bad: %s", result)
}
assert.Equal(result, expected, "should match output")
}
func TestArtifactString(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
expected := "An image was created: b8cdf55b-c916-40bd-b190-389ec144c4ed"
a := &Artifact{
ImageId: "b8cdf55b-c916-40bd-b190-389ec144c4ed",
}
result := a.String()
if result != expected {
t.Fatalf("bad: %s", result)
}
assert.Equal(result, expected, "should match output")
}
+6 -6
View File
@@ -29,15 +29,15 @@ type Builder struct {
runner multistep.Runner
}
func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
func (b *Builder) Prepare(raws ...interface{}) error {
md, err := common.DecodeConfig(&b.config, raws...)
if err != nil {
return nil, err
return err
}
b.config.tpl, err = packer.NewConfigTemplate()
if err != nil {
return nil, err
return err
}
b.config.tpl.UserVars = b.config.PackerUserVars
@@ -48,11 +48,11 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare(b.config.tpl)...)
if errs != nil && len(errs.Errors) > 0 {
return nil, errs
return errs
}
log.Println(common.ScrubConfig(b.config, b.config.Password))
return nil, nil
log.Printf("Config: %+v", b.config)
return nil
}
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
+5 -20
View File
@@ -32,10 +32,7 @@ func TestBuilder_Prepare_BadType(t *testing.T) {
"password": []string{},
}
warns, err := b.Prepare(c)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err := b.Prepare(c)
if err == nil {
t.Fatalf("prepare should fail")
}
@@ -47,10 +44,7 @@ func TestBuilderPrepare_ImageName(t *testing.T) {
// Test good
config["image_name"] = "foo"
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err := b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -58,10 +52,7 @@ func TestBuilderPrepare_ImageName(t *testing.T) {
// Test bad
config["image_name"] = "foo {{"
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -69,10 +60,7 @@ func TestBuilderPrepare_ImageName(t *testing.T) {
// Test bad
delete(config, "image_name")
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -84,10 +72,7 @@ func TestBuilderPrepare_InvalidKey(t *testing.T) {
// Add a random key
config["i_should_not_be_valid"] = true
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err := b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
+3 -2
View File
@@ -1,9 +1,10 @@
package openstack
import (
"cgl.tideland.biz/identifier"
"encoding/hex"
"fmt"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/common/uuid"
"github.com/mitchellh/packer/packer"
"github.com/rackspace/gophercloud"
"log"
@@ -18,7 +19,7 @@ func (s *StepKeyPair) Run(state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
ui.Say("Creating temporary keypair for this instance...")
keyName := fmt.Sprintf("packer %s", uuid.TimeOrderedUUID())
keyName := fmt.Sprintf("packer %s", hex.EncodeToString(identifier.NewUUID().Raw()))
log.Printf("temporary keypair name: %s", keyName)
keyResp, err := csp.CreateKeyPair(gophercloud.NewKeyPair{Name: keyName})
if err != nil {
+6 -49
View File
@@ -16,14 +16,6 @@ import (
const BuilderId = "mitchellh.virtualbox"
// These are the different valid mode values for "guest_additions_mode" which
// determine how guest additions are delivered to the guest.
const (
GuestAdditionsModeDisable string = "disable"
GuestAdditionsModeAttach = "attach"
GuestAdditionsModeUpload = "upload"
)
type Builder struct {
config config
runner multistep.Runner
@@ -36,7 +28,6 @@ type config struct {
DiskSize uint `mapstructure:"disk_size"`
FloppyFiles []string `mapstructure:"floppy_files"`
Format string `mapstructure:"format"`
GuestAdditionsMode string `mapstructure:"guest_additions_mode"`
GuestAdditionsPath string `mapstructure:"guest_additions_path"`
GuestAdditionsURL string `mapstructure:"guest_additions_url"`
GuestAdditionsSHA256 string `mapstructure:"guest_additions_sha256"`
@@ -72,21 +63,20 @@ type config struct {
tpl *packer.ConfigTemplate
}
func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
func (b *Builder) Prepare(raws ...interface{}) error {
md, err := common.DecodeConfig(&b.config, raws...)
if err != nil {
return nil, err
return err
}
b.config.tpl, err = packer.NewConfigTemplate()
if err != nil {
return nil, err
return err
}
b.config.tpl.UserVars = b.config.PackerUserVars
// Accumulate any errors and warnings
// Accumulate any errors
errs := common.CheckUnusedConfig(md)
warnings := make([]string, 0)
if b.config.DiskSize == 0 {
b.config.DiskSize = 40000
@@ -96,10 +86,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
b.config.FloppyFiles = make([]string, 0)
}
if b.config.GuestAdditionsMode == "" {
b.config.GuestAdditionsMode = "upload"
}
if b.config.GuestAdditionsPath == "" {
b.config.GuestAdditionsPath = "VBoxGuestAdditions.iso"
}
@@ -158,7 +144,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
// Errors
templates := map[string]*string{
"guest_additions_mode": &b.config.GuestAdditionsMode,
"guest_additions_sha256": &b.config.GuestAdditionsSHA256,
"guest_os_type": &b.config.GuestOSType,
"hard_drive_interface": &b.config.HardDriveInterface,
@@ -168,7 +153,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
"iso_url": &b.config.RawSingleISOUrl,
"output_directory": &b.config.OutputDir,
"shutdown_command": &b.config.ShutdownCommand,
"ssh_key_path": &b.config.SSHKeyPath,
"ssh_password": &b.config.SSHPassword,
"ssh_username": &b.config.SSHUser,
"virtualbox_version_file": &b.config.VBoxVersionFile,
@@ -278,25 +262,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
}
}
validMode := false
validModes := []string{
GuestAdditionsModeDisable,
GuestAdditionsModeAttach,
GuestAdditionsModeUpload,
}
for _, mode := range validModes {
if b.config.GuestAdditionsMode == mode {
validMode = true
break
}
}
if !validMode {
errs = packer.MultiErrorAppend(errs,
fmt.Errorf("guest_additions_mode is invalid. Must be one of: %v", validModes))
}
if b.config.GuestAdditionsSHA256 != "" {
b.config.GuestAdditionsSHA256 = strings.ToLower(b.config.GuestAdditionsSHA256)
}
@@ -364,18 +329,11 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
}
}
// Warnings
if b.config.ShutdownCommand == "" {
warnings = append(warnings,
"A shutdown_command was not specified. Without a shutdown command, Packer\n"+
"will forcibly halt the virtual machine, which may result in data loss.")
}
if errs != nil && len(errs.Errors) > 0 {
return warnings, errs
return errs
}
return warnings, nil
return nil
}
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
@@ -403,7 +361,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
new(stepCreateVM),
new(stepCreateDisk),
new(stepAttachISO),
new(stepAttachGuestAdditions),
new(stepAttachFloppy),
new(stepForwardSSH),
new(stepVBoxManage),
+53 -273
View File
@@ -43,7 +43,6 @@ func testConfig() map[string]interface{} {
"iso_checksum": "foo",
"iso_checksum_type": "md5",
"iso_url": "http://www.google.com/",
"shutdown_command": "yes",
"ssh_username": "foo",
packer.BuildNameConfigKey: "foo",
@@ -61,18 +60,11 @@ func TestBuilder_ImplementsBuilder(t *testing.T) {
func TestBuilderPrepare_Defaults(t *testing.T) {
var b Builder
config := testConfig()
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err := b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
if b.config.GuestAdditionsMode != GuestAdditionsModeUpload {
t.Errorf("bad guest additions mode: %s", b.config.GuestAdditionsMode)
}
if b.config.GuestOSType != "Other" {
t.Errorf("bad guest OS type: %s", b.config.GuestOSType)
}
@@ -108,10 +100,7 @@ func TestBuilderPrepare_BootWait(t *testing.T) {
// Test a default boot_wait
delete(config, "boot_wait")
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err := b.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
@@ -122,10 +111,7 @@ func TestBuilderPrepare_BootWait(t *testing.T) {
// Test with a bad boot_wait
config["boot_wait"] = "this is not good"
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -133,10 +119,7 @@ func TestBuilderPrepare_BootWait(t *testing.T) {
// Test with a good one
config["boot_wait"] = "5s"
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -147,10 +130,7 @@ func TestBuilderPrepare_DiskSize(t *testing.T) {
config := testConfig()
delete(config, "disk_size")
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err := b.Prepare(config)
if err != nil {
t.Fatalf("bad err: %s", err)
}
@@ -161,10 +141,7 @@ func TestBuilderPrepare_DiskSize(t *testing.T) {
config["disk_size"] = 60000
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -179,10 +156,7 @@ func TestBuilderPrepare_FloppyFiles(t *testing.T) {
config := testConfig()
delete(config, "floppy_files")
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err := b.Prepare(config)
if err != nil {
t.Fatalf("bad err: %s", err)
}
@@ -193,10 +167,7 @@ func TestBuilderPrepare_FloppyFiles(t *testing.T) {
config["floppy_files"] = []string{"foo", "bar"}
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -207,56 +178,12 @@ func TestBuilderPrepare_FloppyFiles(t *testing.T) {
}
}
func TestBuilderPrepare_GuestAdditionsMode(t *testing.T) {
var b Builder
config := testConfig()
// test default mode
delete(config, "guest_additions_mode")
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatalf("bad err: %s", err)
}
// Test another mode
config["guest_additions_mode"] = "attach"
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
if b.config.GuestAdditionsMode != GuestAdditionsModeAttach {
t.Fatalf("bad: %s", b.config.GuestAdditionsMode)
}
// Test bad mode
config["guest_additions_mode"] = "teleport"
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err == nil {
t.Fatal("should error")
}
}
func TestBuilderPrepare_GuestAdditionsPath(t *testing.T) {
var b Builder
config := testConfig()
delete(config, "guest_additions_path")
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err := b.Prepare(config)
if err != nil {
t.Fatalf("bad err: %s", err)
}
@@ -267,10 +194,7 @@ func TestBuilderPrepare_GuestAdditionsPath(t *testing.T) {
config["guest_additions_path"] = "foo"
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -285,10 +209,7 @@ func TestBuilderPrepare_GuestAdditionsSHA256(t *testing.T) {
config := testConfig()
delete(config, "guest_additions_sha256")
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err := b.Prepare(config)
if err != nil {
t.Fatalf("bad err: %s", err)
}
@@ -299,10 +220,7 @@ func TestBuilderPrepare_GuestAdditionsSHA256(t *testing.T) {
config["guest_additions_sha256"] = "FOO"
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -317,10 +235,7 @@ func TestBuilderPrepare_GuestAdditionsURL(t *testing.T) {
config := testConfig()
config["guest_additions_url"] = ""
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err := b.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
@@ -331,10 +246,7 @@ func TestBuilderPrepare_GuestAdditionsURL(t *testing.T) {
config["guest_additions_url"] = "http://www.packer.io"
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err != nil {
t.Errorf("should not have error: %s", err)
}
@@ -346,10 +258,7 @@ func TestBuilderPrepare_HardDriveInterface(t *testing.T) {
// Test a default boot_wait
delete(config, "hard_drive_interface")
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err := b.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
@@ -361,10 +270,7 @@ func TestBuilderPrepare_HardDriveInterface(t *testing.T) {
// Test with a bad
config["hard_drive_interface"] = "fake"
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -372,10 +278,7 @@ func TestBuilderPrepare_HardDriveInterface(t *testing.T) {
// Test with a good
config["hard_drive_interface"] = "sata"
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -388,10 +291,7 @@ func TestBuilderPrepare_HTTPPort(t *testing.T) {
// Bad
config["http_port_min"] = 1000
config["http_port_max"] = 500
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err := b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -399,10 +299,7 @@ func TestBuilderPrepare_HTTPPort(t *testing.T) {
// Bad
config["http_port_min"] = -500
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -411,10 +308,7 @@ func TestBuilderPrepare_HTTPPort(t *testing.T) {
config["http_port_min"] = 500
config["http_port_max"] = 1000
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -426,10 +320,7 @@ func TestBuilderPrepare_Format(t *testing.T) {
// Bad
config["format"] = "illegal value"
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err := b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -437,10 +328,7 @@ func TestBuilderPrepare_Format(t *testing.T) {
// Good
config["format"] = "ova"
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -448,10 +336,7 @@ func TestBuilderPrepare_Format(t *testing.T) {
// Good
config["format"] = "ovf"
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -463,10 +348,7 @@ func TestBuilderPrepare_InvalidKey(t *testing.T) {
// Add a random key
config["i_should_not_be_valid"] = true
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err := b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -478,10 +360,7 @@ func TestBuilderPrepare_ISOChecksum(t *testing.T) {
// Test bad
config["iso_checksum"] = ""
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err := b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -489,10 +368,7 @@ func TestBuilderPrepare_ISOChecksum(t *testing.T) {
// Test good
config["iso_checksum"] = "FOo"
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -508,10 +384,7 @@ func TestBuilderPrepare_ISOChecksumType(t *testing.T) {
// Test bad
config["iso_checksum_type"] = ""
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err := b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -519,10 +392,7 @@ func TestBuilderPrepare_ISOChecksumType(t *testing.T) {
// Test good
config["iso_checksum_type"] = "mD5"
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -534,10 +404,7 @@ func TestBuilderPrepare_ISOChecksumType(t *testing.T) {
// Test unknown
config["iso_checksum_type"] = "fake"
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -552,10 +419,7 @@ func TestBuilderPrepare_ISOUrl(t *testing.T) {
// Test both epty
config["iso_url"] = ""
b = Builder{}
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err := b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -563,10 +427,7 @@ func TestBuilderPrepare_ISOUrl(t *testing.T) {
// Test iso_url set
config["iso_url"] = "http://www.packer.io"
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err != nil {
t.Errorf("should not have error: %s", err)
}
@@ -580,10 +441,7 @@ func TestBuilderPrepare_ISOUrl(t *testing.T) {
config["iso_url"] = "http://www.packer.io"
config["iso_urls"] = []string{"http://www.packer.io"}
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -596,10 +454,7 @@ func TestBuilderPrepare_ISOUrl(t *testing.T) {
}
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err != nil {
t.Errorf("should not have error: %s", err)
}
@@ -626,10 +481,7 @@ func TestBuilderPrepare_OutputDir(t *testing.T) {
config["output_directory"] = dir
b = Builder{}
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -637,40 +489,19 @@ func TestBuilderPrepare_OutputDir(t *testing.T) {
// Test with a good one
config["output_directory"] = "i-hope-i-dont-exist"
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
}
func TestBuilderPrepare_ShutdownCommand(t *testing.T) {
var b Builder
config := testConfig()
delete(config, "shutdown_command")
warns, err := b.Prepare(config)
if err != nil {
t.Fatalf("bad: %s", err)
}
if len(warns) != 1 {
t.Fatalf("bad: %#v", warns)
}
}
func TestBuilderPrepare_ShutdownTimeout(t *testing.T) {
var b Builder
config := testConfig()
// Test with a bad value
config["shutdown_timeout"] = "this is not good"
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err := b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -678,10 +509,7 @@ func TestBuilderPrepare_ShutdownTimeout(t *testing.T) {
// Test with a good one
config["shutdown_timeout"] = "5s"
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -695,10 +523,7 @@ func TestBuilderPrepare_SSHHostPort(t *testing.T) {
config["ssh_host_port_min"] = 1000
config["ssh_host_port_max"] = 500
b = Builder{}
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err := b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -706,10 +531,7 @@ func TestBuilderPrepare_SSHHostPort(t *testing.T) {
// Bad
config["ssh_host_port_min"] = -500
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -718,10 +540,7 @@ func TestBuilderPrepare_SSHHostPort(t *testing.T) {
config["ssh_host_port_min"] = 500
config["ssh_host_port_max"] = 1000
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -733,20 +552,14 @@ func TestBuilderPrepare_sshKeyPath(t *testing.T) {
config["ssh_key_path"] = ""
b = Builder{}
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err := b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
config["ssh_key_path"] = "/i/dont/exist"
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -765,10 +578,7 @@ func TestBuilderPrepare_sshKeyPath(t *testing.T) {
config["ssh_key_path"] = tf.Name()
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -779,10 +589,7 @@ func TestBuilderPrepare_sshKeyPath(t *testing.T) {
tf.Write([]byte(testPem))
config["ssh_key_path"] = tf.Name()
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
@@ -794,20 +601,14 @@ func TestBuilderPrepare_SSHUser(t *testing.T) {
config["ssh_username"] = ""
b = Builder{}
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err := b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
config["ssh_username"] = "exists"
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -819,10 +620,7 @@ func TestBuilderPrepare_SSHWaitTimeout(t *testing.T) {
// Test a default boot_wait
delete(config, "ssh_wait_timeout")
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err := b.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
@@ -834,10 +632,7 @@ func TestBuilderPrepare_SSHWaitTimeout(t *testing.T) {
// Test with a bad value
config["ssh_wait_timeout"] = "this is not good"
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -845,10 +640,7 @@ func TestBuilderPrepare_SSHWaitTimeout(t *testing.T) {
// Test with a good one
config["ssh_wait_timeout"] = "5s"
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -860,10 +652,7 @@ func TestBuilderPrepare_VBoxManage(t *testing.T) {
// Test with empty
delete(config, "vboxmanage")
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err := b.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
@@ -878,10 +667,7 @@ func TestBuilderPrepare_VBoxManage(t *testing.T) {
}
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -901,10 +687,7 @@ func TestBuilderPrepare_VBoxVersionFile(t *testing.T) {
// Test empty
delete(config, "virtualbox_version_file")
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err := b.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
@@ -916,10 +699,7 @@ func TestBuilderPrepare_VBoxVersionFile(t *testing.T) {
// Test with a good one
config["virtualbox_version_file"] = "foo"
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
+2 -38
View File
@@ -11,15 +11,8 @@ import (
)
// A driver is able to talk to VirtualBox and perform certain
// operations with it. Some of the operations on here may seem overly
// specific, but they were built specifically in mind to handle features
// of the VirtualBox builder for Packer, and to abstract differences in
// versions out of the builder steps, so sometimes the methods are
// extremely specific.
// operations with it.
type Driver interface {
// Create a SATA controller.
CreateSATAController(vm string, controller string) error
// Checks if the VM with the given name is running.
IsRunning(string) (bool, error)
@@ -47,27 +40,6 @@ type VBox42Driver struct {
VBoxManagePath string
}
func (d *VBox42Driver) CreateSATAController(vmName string, name string) error {
version, err := d.Version()
if err != nil {
return err
}
portCountArg := "--sataportcount"
if strings.HasPrefix(version, "4.3") {
portCountArg = "--portcount"
}
command := []string{
"storagectl", vmName,
"--name", name,
"--add", "sata",
portCountArg, "1",
}
return d.VBoxManage(command...)
}
func (d *VBox42Driver) IsRunning(name string) (bool, error) {
var stdout bytes.Buffer
@@ -163,17 +135,9 @@ func (d *VBox42Driver) Version() (string, error) {
versionOutput := strings.TrimSpace(stdout.String())
log.Printf("VBoxManage --version output: %s", versionOutput)
// If the "--version" output contains vboxdrv, then this is indicative
// of problems with the VirtualBox setup and we shouldn't really continue,
// whether or not we can read the version.
if strings.Contains(versionOutput, "vboxdrv") {
return "", fmt.Errorf("VirtualBox is not properly setup: %s", versionOutput)
}
versionRe := regexp.MustCompile("[^.0-9]")
matches := versionRe.Split(versionOutput, 2)
if len(matches) == 0 || matches[0] == "" {
if len(matches) == 0 {
return "", fmt.Errorf("No version found: %s", versionOutput)
}
@@ -1,81 +0,0 @@
package virtualbox
import (
"fmt"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"log"
)
// This step attaches the VirtualBox guest additions as a inserted CD onto
// the virtual machine.
//
// Uses:
// config *config
// driver Driver
// guest_additions_path string
// ui packer.Ui
// vmName string
//
// Produces:
type stepAttachGuestAdditions struct {
attachedPath string
}
func (s *stepAttachGuestAdditions) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*config)
driver := state.Get("driver").(Driver)
guestAdditionsPath := state.Get("guest_additions_path").(string)
ui := state.Get("ui").(packer.Ui)
vmName := state.Get("vmName").(string)
// If we're not attaching the guest additions then just return
if config.GuestAdditionsMode != GuestAdditionsModeAttach {
log.Println("Not attaching guest additions since we're uploading.")
return multistep.ActionContinue
}
// Attach the guest additions to the computer
log.Println("Attaching guest additions ISO onto IDE controller...")
command := []string{
"storageattach", vmName,
"--storagectl", "IDE Controller",
"--port", "1",
"--device", "0",
"--type", "dvddrive",
"--medium", guestAdditionsPath,
}
if err := driver.VBoxManage(command...); err != nil {
err := fmt.Errorf("Error attaching guest additions: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
// Track the path so that we can unregister it from VirtualBox later
s.attachedPath = guestAdditionsPath
return multistep.ActionContinue
}
func (s *stepAttachGuestAdditions) Cleanup(state multistep.StateBag) {
if s.attachedPath == "" {
return
}
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packer.Ui)
vmName := state.Get("vmName").(string)
command := []string{
"storageattach", vmName,
"--storagectl", "IDE Controller",
"--port", "1",
"--device", "0",
"--medium", "none",
}
if err := driver.VBoxManage(command...); err != nil {
ui.Error(fmt.Sprintf("Error unregistering guest additions: %s", err))
}
}
+7 -1
View File
@@ -56,7 +56,13 @@ func (s *stepCreateDisk) Run(state multistep.StateBag) multistep.StepAction {
// that.
if config.HardDriveInterface == "sata" {
controllerName = "SATA Controller"
if err := driver.CreateSATAController(vmName, controllerName); err != nil {
command = []string{
"storagectl", vmName,
"--name", controllerName,
"--add", "sata",
"--sataportcount", "1",
}
if err := driver.VBoxManage(command...); err != nil {
err := fmt.Errorf("Error creating disk controller: %s", err)
state.Put("error", err)
ui.Error(err.Error())
+1 -1
View File
@@ -43,7 +43,7 @@ func (s *stepCreateVM) Run(state multistep.StateBag) multistep.StepAction {
return multistep.ActionHalt
}
// Set the VM name property on the first command
// Set the VM name propery on the first command
if s.vmName == "" {
s.vmName = name
}
-6
View File
@@ -4,9 +4,7 @@ import (
"fmt"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"log"
"path/filepath"
"time"
)
// This step cleans up forwarded ports and exports the VM to an OVF.
@@ -23,10 +21,6 @@ func (s *stepExport) Run(state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
vmName := state.Get("vmName").(string)
// Wait a second to ensure VM is really shutdown.
log.Println("1 second timeout to ensure VM is really shutdown")
time.Sleep(1 * time.Second)
// Clear out the Packer-created forwarding rule
ui.Say("Preparing to export machine...")
ui.Message(fmt.Sprintf("Deleting forwarded port mapping for SSH (host port %d)", state.Get("sshHostPort")))
+1 -8
View File
@@ -25,16 +25,9 @@ func (s *stepForwardSSH) Run(state multistep.StateBag) multistep.StepAction {
log.Printf("Looking for available SSH port between %d and %d", config.SSHHostPortMin, config.SSHHostPortMax)
var sshHostPort uint
var offset uint = 0
portRange := int(config.SSHHostPortMax - config.SSHHostPortMin)
if portRange > 0 {
// Have to check if > 0 to avoid a panic
offset = uint(rand.Intn(portRange))
}
for {
sshHostPort = offset + config.SSHHostPortMin
sshHostPort = uint(rand.Intn(portRange)) + config.SSHHostPortMin
log.Printf("Trying port: %d", sshHostPort)
l, err := net.Listen("tcp", fmt.Sprintf(":%d", sshHostPort))
if err == nil {
+15 -20
View File
@@ -92,34 +92,29 @@ func (s *stepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAction
func (*stepTypeBootCommand) Cleanup(multistep.StateBag) {}
func scancodes(message string) []string {
// Scancodes reference: http://www.win.tue.nl/~aeb/linux/kbd/scancodes-1.html
//
// Scancodes represent raw keyboard output and are fed to the VM by the
// VBoxManage controlvm keyboardputscancode program.
//
// Scancodes are recorded here in pairs. The first entry represents
// the key press and the second entry represents the key release and is
// derived from the first by the addition of 0x80.
special := make(map[string][]string)
special["<bs>"] = []string{"0e", "8e"}
special["<del>"] = []string{"53", "d3"}
special["<bs>"] = []string{"ff", "08"}
special["<del>"] = []string{"ff", "ff"}
special["<enter>"] = []string{"1c", "9c"}
special["<esc>"] = []string{"01", "81"}
special["<f1>"] = []string{"3b", "bb"}
special["<f2>"] = []string{"3c", "bc"}
special["<f3>"] = []string{"3d", "bd"}
special["<f4>"] = []string{"3e", "be"}
special["<f5>"] = []string{"3f", "bf"}
special["<f6>"] = []string{"40", "c0"}
special["<f7>"] = []string{"41", "c1"}
special["<f8>"] = []string{"42", "c2"}
special["<f9>"] = []string{"43", "c3"}
special["<f10>"] = []string{"44", "c4"}
special["<f1>"] = []string{"ff", "be"}
special["<f2>"] = []string{"ff", "bf"}
special["<f3>"] = []string{"ff", "c0"}
special["<f4>"] = []string{"ff", "c1"}
special["<f5>"] = []string{"ff", "c2"}
special["<f6>"] = []string{"ff", "c3"}
special["<f7>"] = []string{"ff", "c4"}
special["<f8>"] = []string{"ff", "c5"}
special["<f9>"] = []string{"ff", "c6"}
special["<f10>"] = []string{"ff", "c7"}
special["<f11>"] = []string{"ff", "c8"}
special["<f12>"] = []string{"ff", "c9"}
special["<return>"] = []string{"1c", "9c"}
special["<tab>"] = []string{"0f", "8f"}
shiftedChars := "~!@#$%^&*()_+{}|:\"<>?"
// Scancodes reference: http://www.win.tue.nl/~aeb/linux/kbd/scancodes-1.html
scancodeIndex := make(map[string]uint)
scancodeIndex["1234567890-="] = 0x02
scancodeIndex["!@#$%^&*()_+"] = 0x02
@@ -4,7 +4,6 @@ import (
"fmt"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"log"
"os"
)
@@ -22,12 +21,6 @@ func (s *stepUploadGuestAdditions) Run(state multistep.StateBag) multistep.StepA
guestAdditionsPath := state.Get("guest_additions_path").(string)
ui := state.Get("ui").(packer.Ui)
// If we're attaching then don't do this, since we attached.
if config.GuestAdditionsMode != GuestAdditionsModeUpload {
log.Println("Not uploading guest additions since mode is not upload")
return multistep.ActionContinue
}
version, err := driver.Version()
if err != nil {
state.Put("error", fmt.Errorf("Error reading version for guest additions upload: %s", err))
+5 -14
View File
@@ -66,21 +66,20 @@ type config struct {
tpl *packer.ConfigTemplate
}
func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
func (b *Builder) Prepare(raws ...interface{}) error {
md, err := common.DecodeConfig(&b.config, raws...)
if err != nil {
return nil, err
return err
}
b.config.tpl, err = packer.NewConfigTemplate()
if err != nil {
return nil, err
return err
}
b.config.tpl.UserVars = b.config.PackerUserVars
// Accumulate any errors
errs := common.CheckUnusedConfig(md)
warnings := make([]string, 0)
if b.config.DiskName == "" {
b.config.DiskName = "disk"
@@ -149,7 +148,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
"iso_url": &b.config.RawSingleISOUrl,
"output_directory": &b.config.OutputDir,
"shutdown_command": &b.config.ShutdownCommand,
"ssh_key_path": &b.config.SSHKeyPath,
"ssh_password": &b.config.SSHPassword,
"ssh_username": &b.config.SSHUser,
"tools_upload_flavor": &b.config.ToolsUploadFlavor,
@@ -327,18 +325,11 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
errs, fmt.Errorf("vnc_port_min must be less than vnc_port_max"))
}
// Warnings
if b.config.ShutdownCommand == "" {
warnings = append(warnings,
"A shutdown_command was not specified. Without a shutdown command, Packer\n"+
"will forcibly halt the virtual machine, which may result in data loss.")
}
if errs != nil && len(errs.Errors) > 0 {
return warnings, errs
return errs
}
return warnings, nil
return nil
}
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
+45 -196
View File
@@ -44,7 +44,6 @@ func testConfig() map[string]interface{} {
"iso_checksum": "foo",
"iso_checksum_type": "md5",
"iso_url": "http://www.packer.io",
"shutdown_command": "foo",
"ssh_username": "foo",
packer.BuildNameConfigKey: "foo",
@@ -65,10 +64,7 @@ func TestBuilderPrepare_BootWait(t *testing.T) {
// Test a default boot_wait
delete(config, "boot_wait")
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err := b.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
@@ -79,10 +75,7 @@ func TestBuilderPrepare_BootWait(t *testing.T) {
// Test with a bad boot_wait
config["boot_wait"] = "this is not good"
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -90,10 +83,7 @@ func TestBuilderPrepare_BootWait(t *testing.T) {
// Test with a good one
config["boot_wait"] = "5s"
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -105,10 +95,7 @@ func TestBuilderPrepare_ISOChecksum(t *testing.T) {
// Test bad
config["iso_checksum"] = ""
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err := b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -116,10 +103,7 @@ func TestBuilderPrepare_ISOChecksum(t *testing.T) {
// Test good
config["iso_checksum"] = "FOo"
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -135,10 +119,7 @@ func TestBuilderPrepare_ISOChecksumType(t *testing.T) {
// Test bad
config["iso_checksum_type"] = ""
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err := b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -146,10 +127,7 @@ func TestBuilderPrepare_ISOChecksumType(t *testing.T) {
// Test good
config["iso_checksum_type"] = "mD5"
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -161,10 +139,7 @@ func TestBuilderPrepare_ISOChecksumType(t *testing.T) {
// Test unknown
config["iso_checksum_type"] = "fake"
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -172,10 +147,7 @@ func TestBuilderPrepare_ISOChecksumType(t *testing.T) {
func TestBuilderPrepare_Defaults(t *testing.T) {
var b Builder
config := testConfig()
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err := b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -202,10 +174,7 @@ func TestBuilderPrepare_DiskSize(t *testing.T) {
config := testConfig()
delete(config, "disk_size")
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err := b.Prepare(config)
if err != nil {
t.Fatalf("bad err: %s", err)
}
@@ -216,10 +185,7 @@ func TestBuilderPrepare_DiskSize(t *testing.T) {
config["disk_size"] = 60000
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -234,10 +200,7 @@ func TestBuilderPrepare_FloppyFiles(t *testing.T) {
config := testConfig()
delete(config, "floppy_files")
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err := b.Prepare(config)
if err != nil {
t.Fatalf("bad err: %s", err)
}
@@ -248,10 +211,7 @@ func TestBuilderPrepare_FloppyFiles(t *testing.T) {
config["floppy_files"] = []string{"foo", "bar"}
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -269,10 +229,7 @@ func TestBuilderPrepare_HTTPPort(t *testing.T) {
// Bad
config["http_port_min"] = 1000
config["http_port_max"] = 500
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err := b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -280,10 +237,7 @@ func TestBuilderPrepare_HTTPPort(t *testing.T) {
// Bad
config["http_port_min"] = -500
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -292,10 +246,7 @@ func TestBuilderPrepare_HTTPPort(t *testing.T) {
config["http_port_min"] = 500
config["http_port_max"] = 1000
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -307,10 +258,7 @@ func TestBuilderPrepare_InvalidKey(t *testing.T) {
// Add a random key
config["i_should_not_be_valid"] = true
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err := b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -325,10 +273,7 @@ func TestBuilderPrepare_ISOUrl(t *testing.T) {
// Test both epty
config["iso_url"] = ""
b = Builder{}
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err := b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -336,10 +281,7 @@ func TestBuilderPrepare_ISOUrl(t *testing.T) {
// Test iso_url set
config["iso_url"] = "http://www.packer.io"
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err != nil {
t.Errorf("should not have error: %s", err)
}
@@ -353,10 +295,7 @@ func TestBuilderPrepare_ISOUrl(t *testing.T) {
config["iso_url"] = "http://www.packer.io"
config["iso_urls"] = []string{"http://www.packer.io"}
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -369,10 +308,7 @@ func TestBuilderPrepare_ISOUrl(t *testing.T) {
}
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err != nil {
t.Errorf("should not have error: %s", err)
}
@@ -399,10 +335,7 @@ func TestBuilderPrepare_OutputDir(t *testing.T) {
config["output_directory"] = dir
b = Builder{}
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -410,40 +343,19 @@ func TestBuilderPrepare_OutputDir(t *testing.T) {
// Test with a good one
config["output_directory"] = "i-hope-i-dont-exist"
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
}
func TestBuilderPrepare_ShutdownCommand(t *testing.T) {
var b Builder
config := testConfig()
delete(config, "shutdown_command")
warns, err := b.Prepare(config)
if err != nil {
t.Fatalf("bad: %s", err)
}
if len(warns) != 1 {
t.Fatalf("bad: %#v", warns)
}
}
func TestBuilderPrepare_ShutdownTimeout(t *testing.T) {
var b Builder
config := testConfig()
// Test with a bad value
config["shutdown_timeout"] = "this is not good"
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err := b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -451,10 +363,7 @@ func TestBuilderPrepare_ShutdownTimeout(t *testing.T) {
// Test with a good one
config["shutdown_timeout"] = "5s"
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -466,20 +375,14 @@ func TestBuilderPrepare_sshKeyPath(t *testing.T) {
config["ssh_key_path"] = ""
b = Builder{}
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err := b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
config["ssh_key_path"] = "/i/dont/exist"
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -498,10 +401,7 @@ func TestBuilderPrepare_sshKeyPath(t *testing.T) {
config["ssh_key_path"] = tf.Name()
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -512,10 +412,7 @@ func TestBuilderPrepare_sshKeyPath(t *testing.T) {
tf.Write([]byte(testPem))
config["ssh_key_path"] = tf.Name()
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
@@ -526,20 +423,14 @@ func TestBuilderPrepare_SSHUser(t *testing.T) {
config := testConfig()
config["ssh_username"] = ""
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err := b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
config["ssh_username"] = "exists"
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -551,10 +442,7 @@ func TestBuilderPrepare_SSHPort(t *testing.T) {
// Test with a bad value
delete(config, "ssh_port")
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err := b.Prepare(config)
if err != nil {
t.Fatalf("bad err: %s", err)
}
@@ -566,10 +454,7 @@ func TestBuilderPrepare_SSHPort(t *testing.T) {
// Test with a good one
config["ssh_port"] = 44
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -585,10 +470,7 @@ func TestBuilderPrepare_SSHWaitTimeout(t *testing.T) {
// Test with a bad value
config["ssh_wait_timeout"] = "this is not good"
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err := b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -596,10 +478,7 @@ func TestBuilderPrepare_SSHWaitTimeout(t *testing.T) {
// Test with a good one
config["ssh_wait_timeout"] = "5s"
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -611,10 +490,7 @@ func TestBuilderPrepare_ToolsUploadPath(t *testing.T) {
// Test a default
delete(config, "tools_upload_path")
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err := b.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
@@ -626,10 +502,7 @@ func TestBuilderPrepare_ToolsUploadPath(t *testing.T) {
// Test with a bad value
config["tools_upload_path"] = "{{{nope}"
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -637,10 +510,7 @@ func TestBuilderPrepare_ToolsUploadPath(t *testing.T) {
// Test with a good one
config["tools_upload_path"] = "hey"
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -652,10 +522,7 @@ func TestBuilderPrepare_VMXTemplatePath(t *testing.T) {
// Test bad
config["vmx_template_path"] = "/i/dont/exist/forreal"
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err := b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -674,10 +541,7 @@ func TestBuilderPrepare_VMXTemplatePath(t *testing.T) {
config["vmx_template_path"] = tf.Name()
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -696,10 +560,7 @@ func TestBuilderPrepare_VMXTemplatePath(t *testing.T) {
config["vmx_template_path"] = tf2.Name()
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -712,10 +573,7 @@ func TestBuilderPrepare_VNCPort(t *testing.T) {
// Bad
config["vnc_port_min"] = 1000
config["vnc_port_max"] = 500
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err := b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -723,10 +581,7 @@ func TestBuilderPrepare_VNCPort(t *testing.T) {
// Bad
config["vnc_port_min"] = -500
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
@@ -735,10 +590,7 @@ func TestBuilderPrepare_VNCPort(t *testing.T) {
config["vnc_port_min"] = 500
config["vnc_port_max"] = 1000
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
@@ -753,10 +605,7 @@ func TestBuilderPrepare_VMXData(t *testing.T) {
"two": "bar",
}
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
err := b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
+3 -21
View File
@@ -3,7 +3,6 @@ package vmware
import (
"bytes"
"errors"
"os"
"os/exec"
"regexp"
)
@@ -14,31 +13,14 @@ type IfconfigIPFinder struct {
}
func (f *IfconfigIPFinder) HostIP() (string, error) {
var ifconfigPath string
// On some systems, ifconfig is in /sbin which is generally not
// on the PATH for a standard user, so we just check that first.
if _, err := os.Stat("/sbin/ifconfig"); err == nil {
ifconfigPath = "/sbin/ifconfig"
}
if ifconfigPath == "" {
var err error
ifconfigPath, err = exec.LookPath("ifconfig")
if err != nil {
return "", err
}
ifconfigPath, err := exec.LookPath("ifconfig")
if err != nil {
return "", err
}
stdout := new(bytes.Buffer)
cmd := exec.Command(ifconfigPath, f.Device)
cmd.Env = append(cmd.Env, os.Environ()...)
// Force LANG=C so that the output is what we expect it to be
// despite the locale.
cmd.Env = append(cmd.Env, "LANG=C")
cmd.Stdout = stdout
cmd.Stderr = new(bytes.Buffer)
if err := cmd.Run(); err != nil {
+1 -10
View File
@@ -113,20 +113,11 @@ func (c Command) Run(env packer.Environment, args []string) int {
log.Printf("Preparing build: %s", b.Name())
b.SetDebug(cfgDebug)
b.SetForce(cfgForce)
warnings, err := b.Prepare(userVars)
err := b.Prepare(userVars)
if err != nil {
env.Ui().Error(err.Error())
return 1
}
if len(warnings) > 0 {
ui := buildUis[b.Name()]
ui.Say(fmt.Sprintf("Warnings for build '%s':\n", b.Name()))
for _, warning := range warnings {
ui.Say(fmt.Sprintf("* %s", warning))
}
ui.Say("")
}
}
// Run all the builds in parallel and wait for them to complete
+11 -10
View File
@@ -2,6 +2,7 @@ package build
import (
"bytes"
"cgl.tideland.biz/asserts"
"github.com/mitchellh/packer/packer"
"testing"
)
@@ -22,33 +23,33 @@ func testEnvironment() packer.Environment {
}
func TestCommand_Implements(t *testing.T) {
var _ packer.Command = new(Command)
assert := asserts.NewTestingAsserts(t, true)
var actual packer.Command
assert.Implementor(new(Command), &actual, "should be a Command")
}
func TestCommand_Run_NoArgs(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
command := new(Command)
result := command.Run(testEnvironment(), make([]string, 0))
if result != 1 {
t.Fatalf("bad: %d", result)
}
assert.Equal(result, 1, "no args should error")
}
func TestCommand_Run_MoreThanOneArg(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
command := new(Command)
args := []string{"one", "two"}
result := command.Run(testEnvironment(), args)
if result != 1 {
t.Fatalf("bad: %d", result)
}
assert.Equal(result, 1, "More than one arg should fail")
}
func TestCommand_Run_MissingFile(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
command := new(Command)
args := []string{"i-better-not-exist"}
result := command.Run(testEnvironment(), args)
if result != 1 {
t.Fatalf("bad: %d", result)
}
assert.Equal(result, 1, "a non-existent file should error")
}
-1
View File
@@ -53,7 +53,6 @@ func (c Command) Run(env packer.Environment, args []string) int {
fixers := []string{
"iso-md5",
"createtime",
"virtualbox-gaattach",
}
input := templateData
+2 -7
View File
@@ -6,10 +6,6 @@ type Fixer interface {
// in some way, and returns the new, transformed structure. The
// Fix method is allowed to mutate the input.
Fix(input map[string]interface{}) (map[string]interface{}, error)
// Synopsis returns a string description of what the fixer actually
// does.
Synopsis() string
}
// Fixers is the map of all available fixers, by name.
@@ -17,8 +13,7 @@ var Fixers map[string]Fixer
func init() {
Fixers = map[string]Fixer{
"iso-md5": new(FixerISOMD5),
"createtime": new(FixerCreateTime),
"virtualbox-gaattach": new(FixerVirtualBoxGAAttach),
"iso-md5": new(FixerISOMD5),
"createtime": new(FixerCreateTime),
}
}
-4
View File
@@ -49,7 +49,3 @@ func (FixerCreateTime) Fix(input map[string]interface{}) (map[string]interface{}
input["builders"] = tpl.Builders
return input, nil
}
func (FixerCreateTime) Synopsis() string {
return `Replaces ".CreateTime" in builder configs with "{{timestamp}}"`
}
-4
View File
@@ -41,7 +41,3 @@ func (FixerISOMD5) Fix(input map[string]interface{}) (map[string]interface{}, er
input["builders"] = tpl.Builders
return input, nil
}
func (FixerISOMD5) Synopsis() string {
return `Replaces "iso_md5" in builders with "iso_checksum"`
}
-63
View File
@@ -1,63 +0,0 @@
package fix
import (
"github.com/mitchellh/mapstructure"
)
// FixerVirtualBoxGAAttach changes the "guest_additions_attach" of a template
// to "guest_additions_mode".
type FixerVirtualBoxGAAttach struct{}
func (FixerVirtualBoxGAAttach) Fix(input map[string]interface{}) (map[string]interface{}, error) {
// The type we'll decode into; we only care about builders
type template struct {
Builders []map[string]interface{}
}
// Decode the input into our structure, if we can
var tpl template
if err := mapstructure.Decode(input, &tpl); err != nil {
return nil, err
}
for _, builder := range tpl.Builders {
builderTypeRaw, ok := builder["type"]
if !ok {
continue
}
builderType, ok := builderTypeRaw.(string)
if !ok {
continue
}
if builderType != "virtualbox" {
continue
}
gaAttachRaw, ok := builder["guest_additions_attach"]
if !ok {
continue
}
gaAttach, ok := gaAttachRaw.(bool)
if !ok {
continue
}
gaMode := "upload"
if gaAttach {
gaMode = "attach"
}
delete(builder, "guest_additions_attach")
builder["guest_additions_mode"] = gaMode
}
input["builders"] = tpl.Builders
return input, nil
}
func (FixerVirtualBoxGAAttach) Synopsis() string {
return `Updates VirtualBox builders using "guest_additions_attach" to use "guest_additions_mode"`
}
@@ -1,88 +0,0 @@
package fix
import (
"reflect"
"testing"
)
func TestFixerVirtualBoxGAAttach_Impl(t *testing.T) {
var _ Fixer = new(FixerVirtualBoxGAAttach)
}
func TestFixerVirtualBoxGAAttach_Fix(t *testing.T) {
cases := []struct {
Input map[string]interface{}
Expected map[string]interface{}
}{
// No attach field
{
Input: map[string]interface{}{
"type": "virtualbox",
},
Expected: map[string]interface{}{
"type": "virtualbox",
},
},
// Attach field == false
{
Input: map[string]interface{}{
"type": "virtualbox",
"guest_additions_attach": false,
},
Expected: map[string]interface{}{
"type": "virtualbox",
"guest_additions_mode": "upload",
},
},
// Attach field == true
{
Input: map[string]interface{}{
"type": "virtualbox",
"guest_additions_attach": true,
},
Expected: map[string]interface{}{
"type": "virtualbox",
"guest_additions_mode": "attach",
},
},
// Attach field is not a bool
{
Input: map[string]interface{}{
"type": "virtualbox",
"guest_additions_attach": "what",
},
Expected: map[string]interface{}{
"type": "virtualbox",
"guest_additions_attach": "what",
},
},
}
for _, tc := range cases {
var f FixerVirtualBoxGAAttach
input := map[string]interface{}{
"builders": []map[string]interface{}{tc.Input},
}
expected := map[string]interface{}{
"builders": []map[string]interface{}{tc.Expected},
}
output, err := f.Fix(input)
if err != nil {
t.Fatalf("err: %s", err)
}
if !reflect.DeepEqual(output, expected) {
t.Fatalf("unexpected: %#v\nexpected: %#v\n", output, expected)
}
}
}
+1 -20
View File
@@ -62,7 +62,6 @@ func (c Command) Run(env packer.Environment, args []string) int {
}
errs := make([]error, 0)
warnings := make(map[string][]string)
// The component finder for our builds
components := &packer.ComponentFinder{
@@ -82,10 +81,7 @@ func (c Command) Run(env packer.Environment, args []string) int {
// Check the configuration of all builds
for _, b := range builds {
log.Printf("Preparing build: %s", b.Name())
warns, err := b.Prepare(userVars)
if len(warns) > 0 {
warnings[b.Name()] = warns
}
err := b.Prepare(userVars)
if err != nil {
errs = append(errs, fmt.Errorf("Errors validating build '%s'. %s", b.Name(), err))
}
@@ -104,21 +100,6 @@ func (c Command) Run(env packer.Environment, args []string) int {
return 1
}
if len(warnings) > 0 {
env.Ui().Say("Template validation succeeded, but there were some warnings.")
env.Ui().Say("These are ONLY WARNINGS, and Packer will attempt to build the")
env.Ui().Say("template despite them, but they should be paid attention to.\n")
for build, warns := range warnings {
env.Ui().Say(fmt.Sprintf("Warnings for build '%s':\n", build))
for _, warning := range warns {
env.Ui().Say(fmt.Sprintf("* %s", warning))
}
}
return 0
}
env.Ui().Say("Template validated successfully.")
return 0
}
+2 -13
View File
@@ -12,16 +12,6 @@ import (
"strings"
)
// ScrubConfig is a helper that returns a string representation of
// any struct with the given values stripped out.
func ScrubConfig(target interface{}, values ...string) string {
conf := fmt.Sprintf("Config: %+v", target)
for _, value := range values {
conf = strings.Replace(conf, value, "<Filtered>", -1)
}
return conf
}
// CheckUnusedConfig is a helper that makes sure that the there are no
// unused configuration keys, properly ignoring keys that don't matter.
func CheckUnusedConfig(md *mapstructure.Metadata) *packer.MultiError {
@@ -51,9 +41,8 @@ func CheckUnusedConfig(md *mapstructure.Metadata) *packer.MultiError {
func DecodeConfig(target interface{}, raws ...interface{}) (*mapstructure.Metadata, error) {
var md mapstructure.Metadata
decoderConfig := &mapstructure.DecoderConfig{
Metadata: &md,
Result: target,
WeaklyTypedInput: true,
Metadata: &md,
Result: target,
}
decoder, err := mapstructure.NewDecoder(decoderConfig)
-17
View File
@@ -159,20 +159,3 @@ func TestDownloadableURL_FilePaths(t *testing.T) {
}
}
}
func TestScrubConfig(t *testing.T) {
type Inner struct {
Baz string
}
type Local struct {
Foo string
Bar string
Inner
}
c := Local{"foo", "bar", Inner{"bar"}}
expect := "Config: {Foo:foo Bar:<Filtered> Inner:{Baz:<Filtered>}}"
conf := ScrubConfig(c, c.Bar)
if conf != expect {
t.Fatalf("got %s, expected %s", conf, expect)
}
}
+1 -2
View File
@@ -73,8 +73,7 @@ func HashForType(t string) hash.Hash {
func NewDownloadClient(c *DownloadConfig) *DownloadClient {
if c.DownloaderMap == nil {
c.DownloaderMap = map[string]Downloader{
"http": new(HTTPDownloader),
"https": new(HTTPDownloader),
"http": new(HTTPDownloader),
}
}
+1 -3
View File
@@ -72,9 +72,7 @@ WaitLoop:
state.Put("communicator", comm)
break WaitLoop
case <-timeout:
err := fmt.Errorf("Timeout waiting for SSH.")
state.Put("error", err)
ui.Error(err.Error())
ui.Error("Timeout waiting for SSH.")
close(cancel)
return multistep.ActionHalt
case <-time.After(1 * time.Second):
-24
View File
@@ -1,24 +0,0 @@
package uuid
import (
"crypto/rand"
"fmt"
"time"
)
// Generates a time ordered UUID. Top 32 bits are a timestamp,
// bottom 96 are random.
func TimeOrderedUUID() string {
unix := uint32(time.Now().UTC().Unix())
b := make([]byte, 12)
n, err := rand.Read(b)
if n != len(b) {
err = fmt.Errorf("Not enough entropy available")
}
if err != nil {
panic(err)
}
return fmt.Sprintf("%08x-%04x-%04x-%04x-%04x%08x",
unix, b[0:2], b[2:4], b[4:6], b[6:8], b[8:])
}
-12
View File
@@ -1,12 +0,0 @@
package uuid
import (
"testing"
)
func TestTimeOrderedUuid(t *testing.T) {
uuid := TimeOrderedUUID()
if len(uuid) != 36 {
t.Fatalf("bad: %s", uuid)
}
}
+14 -61
View File
@@ -8,7 +8,6 @@ import (
"fmt"
"github.com/mitchellh/packer/packer"
"io"
"io/ioutil"
"log"
"net"
"os"
@@ -239,14 +238,6 @@ func (c *comm) reconnect() (err error) {
log.Printf("reconnecting to TCP connection for SSH")
c.conn, err = c.config.Connection()
if err != nil {
// Explicitly set this to the REAL nil. Connection() can return
// a nil implementation of net.Conn which will make the
// "if c.conn == nil" check fail above. Read here for more information
// on this psychotic language feature:
//
// http://golang.org/doc/faq#nil_error
c.conn = nil
log.Printf("reconnection error: %s", err)
return
}
@@ -363,49 +354,30 @@ func checkSCPStatus(r *bufio.Reader) error {
}
func scpUploadFile(dst string, src io.Reader, w io.Writer, r *bufio.Reader) error {
// Create a temporary file where we can copy the contents of the src
// so that we can determine the length, since SCP is length-prefixed.
tf, err := ioutil.TempFile("", "packer-upload")
if err != nil {
return fmt.Errorf("Error creating temporary file for upload: %s", err)
}
defer os.Remove(tf.Name())
defer tf.Close()
log.Println("Copying input data into temporary file so we can read the length")
if _, err := io.Copy(tf, src); err != nil {
// Determine the length of the upload content by copying it
// into an in-memory buffer. Note that this means what we upload
// must fit into memory.
log.Println("Copying input data into in-memory buffer so we can get the length")
inputBuf := new(bytes.Buffer)
if _, err := io.Copy(inputBuf, src); err != nil {
return err
}
// Sync the file so that the contents are definitely on disk, then
// read the length of it.
if err := tf.Sync(); err != nil {
return fmt.Errorf("Error creating temporary file for upload: %s", err)
}
// Seek the file to the beginning so we can re-read all of it
if _, err := tf.Seek(0, 0); err != nil {
return fmt.Errorf("Error creating temporary file for upload: %s", err)
}
fi, err := tf.Stat()
if err != nil {
return fmt.Errorf("Error creating temporary file for upload: %s", err)
}
// Start the protocol
log.Println("Beginning file upload...")
fmt.Fprintln(w, "C0644", fi.Size(), dst)
if err := checkSCPStatus(r); err != nil {
fmt.Fprintln(w, "C0644", inputBuf.Len(), dst)
err := checkSCPStatus(r)
if err != nil {
return err
}
if _, err := io.Copy(w, tf); err != nil {
if _, err := io.Copy(w, inputBuf); err != nil {
return err
}
fmt.Fprint(w, "\x00")
if err := checkSCPStatus(r); err != nil {
err = checkSCPStatus(r)
if err != nil {
return err
}
@@ -436,27 +408,8 @@ func scpUploadDir(root string, fs []os.FileInfo, w io.Writer, r *bufio.Reader) e
for _, fi := range fs {
realPath := filepath.Join(root, fi.Name())
// Track if this is actually a symlink to a directory. If it is
// a symlink to a file we don't do any special behavior because uploading
// a file just works. If it is a directory, we need to know so we
// treat it as such.
isSymlinkToDir := false
if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
symPath, err := filepath.EvalSymlinks(realPath)
if err != nil {
return err
}
symFi, err := os.Lstat(symPath)
if err != nil {
return err
}
isSymlinkToDir = symFi.IsDir()
}
if !fi.IsDir() && !isSymlinkToDir {
// It is a regular file (or symlink to a file), just upload it
if !fi.IsDir() {
// It is a regular file, just upload it
f, err := os.Open(realPath)
if err != nil {
return err
+3 -4
View File
@@ -1,7 +1,6 @@
package ssh
import (
"code.google.com/p/go.crypto/ssh"
"crypto"
"crypto/dsa"
"crypto/rsa"
@@ -54,15 +53,15 @@ func (k *SimpleKeychain) AddPEMKeyPassword(key string, password string) (err err
}
// Key method for ssh.ClientKeyring interface
func (k *SimpleKeychain) Key(i int) (ssh.PublicKey, error) {
func (k *SimpleKeychain) Key(i int) (interface{}, error) {
if i < 0 || i >= len(k.keys) {
return nil, nil
}
switch key := k.keys[i].(type) {
case *rsa.PrivateKey:
return ssh.NewPublicKey(&key.PublicKey)
return &key.PublicKey, nil
case *dsa.PrivateKey:
return ssh.NewPublicKey(&key.PublicKey)
return &key.PublicKey, nil
}
panic("unknown key type")
}
-1
View File
@@ -40,7 +40,6 @@ const defaultConfig = `
},
"provisioners": {
"ansible-local": "packer-provisioner-ansible-local",
"chef-solo": "packer-provisioner-chef-solo",
"file": "packer-provisioner-file",
"puppet-masterless": "packer-provisioner-puppet-masterless",
+5 -6
View File
@@ -38,9 +38,8 @@ type Build interface {
Name() string
// Prepare configures the various components of this build and reports
// any errors in doing so (such as syntax errors, validation errors, etc.).
// It also reports any warnings.
Prepare(v map[string]string) ([]string, error)
// any errors in doing so (such as syntax errors, validation errors, etc.)
Prepare(v map[string]string) error
// Run runs the actual builder, returning an artifact implementation
// of what is built. If anything goes wrong, an error is returned.
@@ -116,7 +115,7 @@ func (b *coreBuild) Name() string {
// Prepare prepares the build by doing some initialization for the builder
// and any hooks. This _must_ be called prior to Run. The parameter is the
// overrides for the variables within the template (if any).
func (b *coreBuild) Prepare(userVars map[string]string) (warn []string, err error) {
func (b *coreBuild) Prepare(userVars map[string]string) (err error) {
b.l.Lock()
defer b.l.Unlock()
@@ -155,7 +154,7 @@ func (b *coreBuild) Prepare(userVars map[string]string) (warn []string, err erro
// If there were any problem with variables, return an error right
// away because we can't be certain anything else will actually work.
if len(varErrs) > 0 {
return nil, &MultiError{
return &MultiError{
Errors: varErrs,
}
}
@@ -169,7 +168,7 @@ func (b *coreBuild) Prepare(userVars map[string]string) (warn []string, err erro
}
// Prepare the builder
warn, err = b.builder.Prepare(b.builderConfig, packerConfig)
err = b.builder.Prepare(b.builderConfig, packerConfig)
if err != nil {
log.Printf("Build '%s' prepare failure: %s\n", b.name, err)
return
+57 -119
View File
@@ -1,6 +1,7 @@
package packer
import (
"cgl.tideland.biz/asserts"
"reflect"
"testing"
)
@@ -8,7 +9,7 @@ import (
func testBuild() *coreBuild {
return &coreBuild{
name: "test",
builder: &MockBuilder{ArtifactId: "b"},
builder: &TestBuilder{artifactId: "b"},
builderConfig: 42,
builderType: "foo",
hooks: map[string][]Hook{
@@ -26,6 +27,10 @@ func testBuild() *coreBuild {
}
}
func testBuilder() *TestBuilder {
return &TestBuilder{}
}
func testDefaultPackerConfig() map[string]interface{} {
return map[string]interface{}{
BuildNameConfigKey: "test",
@@ -36,52 +41,37 @@ func testDefaultPackerConfig() map[string]interface{} {
}
}
func TestBuild_Name(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
build := testBuild()
if build.Name() != "test" {
t.Fatalf("bad: %s", build.Name())
}
assert.Equal(build.Name(), "test", "should have a name")
}
func TestBuild_Prepare(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
packerConfig := testDefaultPackerConfig()
build := testBuild()
builder := build.builder.(*MockBuilder)
builder := build.builder.(*TestBuilder)
build.Prepare(nil)
if !builder.PrepareCalled {
t.Fatal("should be called")
}
if !reflect.DeepEqual(builder.PrepareConfig, []interface{}{42, packerConfig}) {
t.Fatalf("bad: %#v", builder.PrepareConfig)
}
assert.True(builder.prepareCalled, "prepare should be called")
assert.Equal(builder.prepareConfig, []interface{}{42, packerConfig}, "prepare config should be 42")
coreProv := build.provisioners[0]
prov := coreProv.provisioner.(*MockProvisioner)
if !prov.PrepCalled {
t.Fatal("prep should be called")
}
if !reflect.DeepEqual(prov.PrepConfigs, []interface{}{42, packerConfig}) {
t.Fatalf("bad: %#v", prov.PrepConfigs)
}
assert.True(prov.PrepCalled, "prepare should be called")
assert.Equal(prov.PrepConfigs, []interface{}{42, packerConfig}, "prepare should be called with proper config")
corePP := build.postProcessors[0][0]
pp := corePP.processor.(*TestPostProcessor)
if !pp.configCalled {
t.Fatal("should be called")
}
if !reflect.DeepEqual(pp.configVal, []interface{}{make(map[string]interface{}), packerConfig}) {
t.Fatalf("bad: %#v", pp.configVal)
}
assert.True(pp.configCalled, "config should be called")
assert.Equal(pp.configVal, []interface{}{make(map[string]interface{}), packerConfig}, "config should have right value")
}
func TestBuild_Prepare_Twice(t *testing.T) {
build := testBuild()
warn, err := build.Prepare(nil)
if len(warn) > 0 {
t.Fatalf("bad: %#v", warn)
}
if err != nil {
if err := build.Prepare(nil); err != nil {
t.Fatalf("bad error: %s", err)
}
@@ -99,46 +89,24 @@ func TestBuild_Prepare_Twice(t *testing.T) {
build.Prepare(nil)
}
func TestBuildPrepare_BuilderWarniings(t *testing.T) {
expected := []string{"foo"}
build := testBuild()
builder := build.builder.(*MockBuilder)
builder.PrepareWarnings = expected
warn, err := build.Prepare(nil)
if err != nil {
t.Fatalf("err: %s", err)
}
if !reflect.DeepEqual(warn, expected) {
t.Fatalf("bad: %#v", warn)
}
}
func TestBuild_Prepare_Debug(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
packerConfig := testDefaultPackerConfig()
packerConfig[DebugConfigKey] = true
build := testBuild()
builder := build.builder.(*MockBuilder)
builder := build.builder.(*TestBuilder)
build.SetDebug(true)
build.Prepare(nil)
if !builder.PrepareCalled {
t.Fatalf("should be called")
}
if !reflect.DeepEqual(builder.PrepareConfig, []interface{}{42, packerConfig}) {
t.Fatalf("bad: %#v", builder.PrepareConfig)
}
assert.True(builder.prepareCalled, "prepare should be called")
assert.Equal(builder.prepareConfig, []interface{}{42, packerConfig}, "prepare config should be 42")
coreProv := build.provisioners[0]
prov := coreProv.provisioner.(*MockProvisioner)
if !prov.PrepCalled {
t.Fatal("prepare should be called")
}
if !reflect.DeepEqual(prov.PrepConfigs, []interface{}{42, packerConfig}) {
t.Fatalf("bad: %#v", prov.PrepConfigs)
}
assert.True(prov.PrepCalled, "prepare should be called")
assert.Equal(prov.PrepConfigs, []interface{}{42, packerConfig}, "prepare should be called with proper config")
}
func TestBuildPrepare_variables_default(t *testing.T) {
@@ -149,22 +117,19 @@ func TestBuildPrepare_variables_default(t *testing.T) {
build := testBuild()
build.variables["foo"] = coreBuildVariable{Default: "bar"}
builder := build.builder.(*MockBuilder)
builder := build.builder.(*TestBuilder)
warn, err := build.Prepare(nil)
if len(warn) > 0 {
t.Fatalf("bad: %#v", warn)
}
err := build.Prepare(nil)
if err != nil {
t.Fatalf("err: %s", err)
}
if !builder.PrepareCalled {
if !builder.prepareCalled {
t.Fatal("prepare should be called")
}
if !reflect.DeepEqual(builder.PrepareConfig[1], packerConfig) {
t.Fatalf("prepare bad: %#v", builder.PrepareConfig[1])
if !reflect.DeepEqual(builder.prepareConfig[1], packerConfig) {
t.Fatalf("prepare bad: %#v", builder.prepareConfig[1])
}
}
@@ -172,10 +137,7 @@ func TestBuildPrepare_variables_nonexist(t *testing.T) {
build := testBuild()
build.variables["foo"] = coreBuildVariable{Default: "bar"}
warn, err := build.Prepare(map[string]string{"bar": "baz"})
if len(warn) > 0 {
t.Fatalf("bad: %#v", warn)
}
err := build.Prepare(map[string]string{"bar": "baz"})
if err == nil {
t.Fatal("should have had error")
}
@@ -189,22 +151,19 @@ func TestBuildPrepare_variables_override(t *testing.T) {
build := testBuild()
build.variables["foo"] = coreBuildVariable{Default: "bar"}
builder := build.builder.(*MockBuilder)
builder := build.builder.(*TestBuilder)
warn, err := build.Prepare(map[string]string{"foo": "baz"})
if len(warn) > 0 {
t.Fatalf("bad: %#v", warn)
}
err := build.Prepare(map[string]string{"foo": "baz"})
if err != nil {
t.Fatalf("err: %s", err)
}
if !builder.PrepareCalled {
if !builder.prepareCalled {
t.Fatal("prepare should be called")
}
if !reflect.DeepEqual(builder.PrepareConfig[1], packerConfig) {
t.Fatalf("prepare bad: %#v", builder.PrepareConfig[1])
if !reflect.DeepEqual(builder.prepareConfig[1], packerConfig) {
t.Fatalf("prepare bad: %#v", builder.prepareConfig[1])
}
}
@@ -212,10 +171,7 @@ func TestBuildPrepare_variablesRequired(t *testing.T) {
build := testBuild()
build.variables["foo"] = coreBuildVariable{Required: true}
warn, err := build.Prepare(map[string]string{})
if len(warn) > 0 {
t.Fatalf("bad: %#v", warn)
}
err := build.Prepare(map[string]string{})
if err == nil {
t.Fatal("should have had error")
}
@@ -223,59 +179,44 @@ func TestBuildPrepare_variablesRequired(t *testing.T) {
// Test with setting the value
build = testBuild()
build.variables["foo"] = coreBuildVariable{Required: true}
warn, err = build.Prepare(map[string]string{"foo": ""})
if len(warn) > 0 {
t.Fatalf("bad: %#v", warn)
}
err = build.Prepare(map[string]string{"foo": ""})
if err != nil {
t.Fatalf("should not have error: %s", err)
}
}
func TestBuild_Run(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
cache := &TestCache{}
ui := testUi()
build := testBuild()
build.Prepare(nil)
artifacts, err := build.Run(ui, cache)
if err != nil {
t.Fatalf("err: %s", err)
}
if len(artifacts) != 2 {
t.Fatalf("bad: %#v", artifacts)
}
assert.Nil(err, "should not error")
assert.Equal(len(artifacts), 2, "should have two artifacts")
// Verify builder was run
builder := build.builder.(*MockBuilder)
if !builder.RunCalled {
t.Fatal("should be called")
}
builder := build.builder.(*TestBuilder)
assert.True(builder.runCalled, "run should be called")
// Verify hooks are disapatchable
dispatchHook := builder.RunHook
dispatchHook := builder.runHook
dispatchHook.Run("foo", nil, nil, 42)
hook := build.hooks["foo"][0].(*MockHook)
if !hook.RunCalled {
t.Fatal("should be called")
}
if hook.RunData != 42 {
t.Fatalf("bad: %#v", hook.RunData)
}
assert.True(hook.RunCalled, "run should be called")
assert.Equal(hook.RunData, 42, "should have correct data")
// Verify provisioners run
dispatchHook.Run(HookProvision, nil, nil, 42)
prov := build.provisioners[0].provisioner.(*MockProvisioner)
if !prov.ProvCalled {
t.Fatal("should be called")
}
assert.True(prov.ProvCalled, "provision should be called")
// Verify post-processor was run
pp := build.postProcessors[0][0].processor.(*TestPostProcessor)
if !pp.ppCalled {
t.Fatal("should be called")
}
assert.True(pp.ppCalled, "post processor should be called")
}
func TestBuild_Run_Artifacts(t *testing.T) {
@@ -415,26 +356,23 @@ func TestBuild_Run_Artifacts(t *testing.T) {
}
func TestBuild_RunBeforePrepare(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
defer func() {
p := recover()
if p == nil {
t.Fatal("should panic")
}
if p.(string) != "Prepare must be called first" {
t.Fatalf("bad: %s", p.(string))
}
assert.NotNil(p, "should panic")
assert.Equal(p.(string), "Prepare must be called first", "right panic")
}()
testBuild().Run(testUi(), &TestCache{})
}
func TestBuild_Cancel(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
build := testBuild()
build.Cancel()
builder := build.builder.(*MockBuilder)
if !builder.CancelCalled {
t.Fatal("cancel should be called")
}
builder := build.builder.(*TestBuilder)
assert.True(builder.cancelCalled, "cancel should be called")
}
+1 -4
View File
@@ -22,10 +22,7 @@ type Builder interface {
//
// Each of the configuration values should merge into the final
// configuration.
//
// Prepare should return a list of warnings along with any errors
// that occured while preparing.
Prepare(...interface{}) ([]string, error)
Prepare(...interface{}) error
// Run is where the actual build should take place. It takes a Build and a Ui.
Run(ui Ui, hook Hook, cache Cache) (Artifact, error)
+3 -19
View File
@@ -1,17 +1,10 @@
package packer
import (
"errors"
)
// MockBuilder is an implementation of Builder that can be used for tests.
// You can set some fake return values and you can keep track of what
// methods were called on the builder. It is fairly basic.
type MockBuilder struct {
ArtifactId string
PrepareWarnings []string
RunErrResult bool
RunNilResult bool
ArtifactId string
PrepareCalled bool
PrepareConfig []interface{}
@@ -22,10 +15,10 @@ type MockBuilder struct {
CancelCalled bool
}
func (tb *MockBuilder) Prepare(config ...interface{}) ([]string, error) {
func (tb *MockBuilder) Prepare(config ...interface{}) error {
tb.PrepareCalled = true
tb.PrepareConfig = config
return tb.PrepareWarnings, nil
return nil
}
func (tb *MockBuilder) Run(ui Ui, h Hook, c Cache) (Artifact, error) {
@@ -33,15 +26,6 @@ func (tb *MockBuilder) Run(ui Ui, h Hook, c Cache) (Artifact, error) {
tb.RunHook = h
tb.RunUi = ui
tb.RunCache = c
if tb.RunErrResult {
return nil, errors.New("foo")
}
if tb.RunNilResult {
return nil, nil
}
return &MockArtifact{
IdValue: tb.ArtifactId,
}, nil
+30
View File
@@ -1 +1,31 @@
package packer
type TestBuilder struct {
artifactId string
prepareCalled bool
prepareConfig []interface{}
runCalled bool
runCache Cache
runHook Hook
runUi Ui
cancelCalled bool
}
func (tb *TestBuilder) Prepare(config ...interface{}) error {
tb.prepareCalled = true
tb.prepareConfig = config
return nil
}
func (tb *TestBuilder) Run(ui Ui, h Hook, c Cache) (Artifact, error) {
tb.runCalled = true
tb.runHook = h
tb.runUi = ui
tb.runCache = c
return &TestArtifact{id: tb.artifactId}, nil
}
func (tb *TestBuilder) Cancel() {
tb.cancelCalled = true
}
+3 -2
View File
@@ -2,8 +2,9 @@ package packer
import (
"bytes"
"cgl.tideland.biz/identifier"
"encoding/hex"
"fmt"
"github.com/mitchellh/packer/common/uuid"
"strconv"
"text/template"
"time"
@@ -93,5 +94,5 @@ func templateTimestamp() string {
}
func templateUuid() string {
return uuid.TimeOrderedUUID()
return hex.EncodeToString(identifier.NewUUID().Raw())
}
+1 -1
View File
@@ -80,7 +80,7 @@ func TestConfigTemplateProcess_uuid(t *testing.T) {
t.Fatalf("err: %s", err)
}
if len(result) != 36 {
if len(result) != 32 {
t.Fatalf("err: %s", result)
}
}
+99 -156
View File
@@ -2,11 +2,12 @@ package packer
import (
"bytes"
"cgl.tideland.biz/asserts"
"errors"
"fmt"
"io/ioutil"
"log"
"os"
"reflect"
"strings"
"testing"
)
@@ -17,7 +18,7 @@ func init() {
}
func testComponentFinder() *ComponentFinder {
builderFactory := func(n string) (Builder, error) { return new(MockBuilder), nil }
builderFactory := func(n string) (Builder, error) { return testBuilder(), nil }
ppFactory := func(n string) (PostProcessor, error) { return new(TestPostProcessor), nil }
provFactory := func(n string) (Provisioner, error) { return new(MockProvisioner), nil }
return &ComponentFinder{
@@ -43,48 +44,40 @@ func testEnvironment() Environment {
}
func TestEnvironment_DefaultConfig_Commands(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
config := DefaultEnvironmentConfig()
if len(config.Commands) != 0 {
t.Fatalf("bad: %#v", config.Commands)
}
assert.Empty(config.Commands, "should have no commands")
}
func TestEnvironment_DefaultConfig_Ui(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
config := DefaultEnvironmentConfig()
if config.Ui == nil {
t.Fatal("config.Ui should not be nil")
}
assert.NotNil(config.Ui, "default UI should not be nil")
rwUi, ok := config.Ui.(*BasicUi)
if !ok {
t.Fatal("default UI should be BasicUi")
}
if rwUi.Writer != os.Stdout {
t.Fatal("default UI should go to stdout")
}
if rwUi.Reader != os.Stdin {
t.Fatal("default UI reader should go to stdin")
}
assert.True(ok, "default UI should be BasicUi")
assert.Equal(rwUi.Writer, os.Stdout, "default UI should go to stdout")
assert.Equal(rwUi.Reader, os.Stdin, "default UI should read from stdin")
}
func TestNewEnvironment_NoConfig(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
env, err := NewEnvironment(nil)
if env != nil {
t.Fatal("env should be nil")
}
if err == nil {
t.Fatal("should have error")
}
assert.Nil(env, "env should be nil")
assert.NotNil(err, "should be an error")
}
func TestEnvironment_NilComponents(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
config := DefaultEnvironmentConfig()
config.Components = *new(ComponentFinder)
env, err := NewEnvironment(config)
if err != nil {
t.Fatalf("err: %s", err)
}
assert.Nil(err, "should not have an error")
// All of these should not cause panics... so we don't assert
// anything but if there is a panic in the test then yeah, something
@@ -97,7 +90,9 @@ func TestEnvironment_NilComponents(t *testing.T) {
}
func TestEnvironment_Builder(t *testing.T) {
builder := &MockBuilder{}
assert := asserts.NewTestingAsserts(t, true)
builder := &TestBuilder{}
builders := make(map[string]Builder)
builders["foo"] = builder
@@ -106,43 +101,33 @@ func TestEnvironment_Builder(t *testing.T) {
env, _ := NewEnvironment(config)
returnedBuilder, err := env.Builder("foo")
if err != nil {
t.Fatalf("err: %s", err)
}
if returnedBuilder != builder {
t.Fatalf("bad: %#v", returnedBuilder)
}
assert.Nil(err, "should be no error")
assert.Equal(returnedBuilder, builder, "should return correct builder")
}
func TestEnvironment_Builder_NilError(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
config := DefaultEnvironmentConfig()
config.Components.Builder = func(n string) (Builder, error) { return nil, nil }
env, _ := NewEnvironment(config)
returnedBuilder, err := env.Builder("foo")
if err == nil {
t.Fatal("should have error")
}
if returnedBuilder != nil {
t.Fatalf("bad: %#v", returnedBuilder)
}
assert.NotNil(err, "should be an error")
assert.Nil(returnedBuilder, "should be no builder")
}
func TestEnvironment_Builder_Error(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
config := DefaultEnvironmentConfig()
config.Components.Builder = func(n string) (Builder, error) { return nil, errors.New("foo") }
env, _ := NewEnvironment(config)
returnedBuilder, err := env.Builder("foo")
if err == nil {
t.Fatal("should have error")
}
if err.Error() != "foo" {
t.Fatalf("bad err: %s", err)
}
if returnedBuilder != nil {
t.Fatalf("should be nil: %#v", returnedBuilder)
}
assert.NotNil(err, "should be an error")
assert.Equal(err.Error(), "foo", "should be correct error")
assert.Nil(returnedBuilder, "should be no builder")
}
func TestEnvironment_Cache(t *testing.T) {
@@ -154,20 +139,20 @@ func TestEnvironment_Cache(t *testing.T) {
}
func TestEnvironment_Cli_Error(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
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)
}
assert.NotNil(err, "should be an error")
assert.Equal(err.Error(), "foo", "should be correct error")
}
func TestEnvironment_Cli_CallsRun(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
command := &TestCommand{}
commands := make(map[string]Command)
commands["foo"] = command
@@ -178,33 +163,25 @@ func TestEnvironment_Cli_CallsRun(t *testing.T) {
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)
}
assert.Nil(err, "should be no error")
assert.Equal(exitCode, 0, "runs foo command")
assert.True(command.runCalled, "run should've been called")
assert.Equal(command.runEnv, env, "should've ran with env")
assert.Equal(command.runArgs, []string{"bar", "baz"}, "should have right args")
}
func TestEnvironment_DefaultCli_Empty(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
defaultEnv := testEnvironment()
exitCode, _ := defaultEnv.Cli([]string{})
if exitCode != 1 {
t.Fatalf("bad: %d", exitCode)
}
assert.Equal(exitCode, 1, "CLI with no args")
}
func TestEnvironment_DefaultCli_Help(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
defaultEnv := testEnvironment()
// A little lambda to help us test the output actually contains help
@@ -212,52 +189,44 @@ func TestEnvironment_DefaultCli_Help(t *testing.T) {
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)
}
assert.True(strings.Contains(output, "usage: packer"), "should print help")
}
// Test "--help"
exitCode, _ := defaultEnv.Cli([]string{"--help"})
if exitCode != 1 {
t.Fatalf("bad: %d", exitCode)
}
assert.Equal(exitCode, 1, "--help should print")
testOutput()
// Test "-h"
exitCode, _ = defaultEnv.Cli([]string{"--help"})
if exitCode != 1 {
t.Fatalf("bad: %d", exitCode)
}
assert.Equal(exitCode, 1, "--help should print")
testOutput()
}
func TestEnvironment_DefaultCli_Version(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
defaultEnv := testEnvironment()
versionCommands := []string{"version", "--version", "-v"}
for _, command := range versionCommands {
exitCode, _ := defaultEnv.Cli([]string{command})
if exitCode != 0 {
t.Fatalf("bad: %d", exitCode)
}
assert.Equal(exitCode, 0, fmt.Sprintf("%s should work", command))
// 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)
}
assert.Equal(exitCode, 0, fmt.Sprintf("%s should work anywhere", command))
} else {
if exitCode != 1 {
t.Fatalf("bad: %d", exitCode)
}
assert.Equal(exitCode, 1, fmt.Sprintf("%s should NOT work anywhere", command))
}
}
}
func TestEnvironment_Hook(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
hook := &MockHook{}
hooks := make(map[string]Hook)
hooks["foo"] = hook
@@ -267,46 +236,38 @@ func TestEnvironment_Hook(t *testing.T) {
env, _ := NewEnvironment(config)
returned, err := env.Hook("foo")
if err != nil {
t.Fatalf("err: %s", err)
}
if returned != hook {
t.Fatalf("bad: %#v", returned)
}
assert.Nil(err, "should be no error")
assert.Equal(returned, hook, "should return correct hook")
}
func TestEnvironment_Hook_NilError(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
config := DefaultEnvironmentConfig()
config.Components.Hook = func(n string) (Hook, error) { return nil, nil }
env, _ := NewEnvironment(config)
returned, err := env.Hook("foo")
if err == nil {
t.Fatal("should have error")
}
if returned != nil {
t.Fatalf("bad: %#v", returned)
}
assert.NotNil(err, "should be an error")
assert.Nil(returned, "should be no hook")
}
func TestEnvironment_Hook_Error(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
config := DefaultEnvironmentConfig()
config.Components.Hook = func(n string) (Hook, error) { return nil, errors.New("foo") }
env, _ := NewEnvironment(config)
returned, err := env.Hook("foo")
if err == nil {
t.Fatal("should have error")
}
if err.Error() != "foo" {
t.Fatalf("err: %s", err)
}
if returned != nil {
t.Fatalf("bad: %#v", returned)
}
assert.NotNil(err, "should be an error")
assert.Equal(err.Error(), "foo", "should be correct error")
assert.Nil(returned, "should be no hook")
}
func TestEnvironment_PostProcessor(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
pp := &TestPostProcessor{}
pps := make(map[string]PostProcessor)
pps["foo"] = pp
@@ -316,46 +277,38 @@ func TestEnvironment_PostProcessor(t *testing.T) {
env, _ := NewEnvironment(config)
returned, err := env.PostProcessor("foo")
if err != nil {
t.Fatalf("err: %s", err)
}
if returned != pp {
t.Fatalf("bad: %#v", returned)
}
assert.Nil(err, "should be no error")
assert.Equal(returned, pp, "should return correct pp")
}
func TestEnvironment_PostProcessor_NilError(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
config := DefaultEnvironmentConfig()
config.Components.PostProcessor = func(n string) (PostProcessor, error) { return nil, nil }
env, _ := NewEnvironment(config)
returned, err := env.PostProcessor("foo")
if err == nil {
t.Fatal("should have error")
}
if returned != nil {
t.Fatalf("bad: %#v", returned)
}
assert.NotNil(err, "should be an error")
assert.Nil(returned, "should be no pp")
}
func TestEnvironment_PostProcessor_Error(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
config := DefaultEnvironmentConfig()
config.Components.PostProcessor = func(n string) (PostProcessor, error) { return nil, errors.New("foo") }
env, _ := NewEnvironment(config)
returned, err := env.PostProcessor("foo")
if err == nil {
t.Fatal("should be an error")
}
if err.Error() != "foo" {
t.Fatalf("bad err: %s", err)
}
if returned != nil {
t.Fatalf("bad: %#v", returned)
}
assert.NotNil(err, "should be an error")
assert.Equal(err.Error(), "foo", "should be correct error")
assert.Nil(returned, "should be no pp")
}
func TestEnvironmentProvisioner(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
p := &MockProvisioner{}
ps := make(map[string]Provisioner)
ps["foo"] = p
@@ -365,29 +318,25 @@ func TestEnvironmentProvisioner(t *testing.T) {
env, _ := NewEnvironment(config)
returned, err := env.Provisioner("foo")
if err != nil {
t.Fatalf("err: %s", err)
}
if returned != p {
t.Fatalf("bad: %#v", returned)
}
assert.Nil(err, "should be no error")
assert.Equal(returned, p, "should return correct provisioner")
}
func TestEnvironmentProvisioner_NilError(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
config := DefaultEnvironmentConfig()
config.Components.Provisioner = func(n string) (Provisioner, error) { return nil, nil }
env, _ := NewEnvironment(config)
returned, err := env.Provisioner("foo")
if err == nil {
t.Fatal("should have error")
}
if returned != nil {
t.Fatalf("bad: %#v", returned)
}
assert.NotNil(err, "should be an error")
assert.Nil(returned, "should be no provisioner")
}
func TestEnvironmentProvisioner_Error(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
config := DefaultEnvironmentConfig()
config.Components.Provisioner = func(n string) (Provisioner, error) {
return nil, errors.New("foo")
@@ -395,18 +344,14 @@ func TestEnvironmentProvisioner_Error(t *testing.T) {
env, _ := NewEnvironment(config)
returned, err := env.Provisioner("foo")
if err == nil {
t.Fatal("should have error")
}
if err.Error() != "foo" {
t.Fatalf("err: %s", err)
}
if returned != nil {
t.Fatalf("bad: %#v", returned)
}
assert.NotNil(err, "should be an error")
assert.Equal(err.Error(), "foo", "should be correct error")
assert.Nil(returned, "should be no provisioner")
}
func TestEnvironment_SettingUi(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
ui := &BasicUi{
Reader: new(bytes.Buffer),
Writer: new(bytes.Buffer),
@@ -417,7 +362,5 @@ func TestEnvironment_SettingUi(t *testing.T) {
env, _ := NewEnvironment(config)
if env.Ui() != ui {
t.Fatalf("UI should be equal: %#v", env.Ui())
}
assert.Equal(env.Ui(), ui, "UIs should be equal")
}
+12 -10
View File
@@ -1,6 +1,7 @@
package packer
import (
"cgl.tideland.biz/asserts"
"sync"
"testing"
"time"
@@ -41,7 +42,12 @@ func (h *CancelHook) Cancel() {
}
func TestDispatchHook_Implements(t *testing.T) {
var _ Hook = new(DispatchHook)
assert := asserts.NewTestingAsserts(t, true)
var r Hook
c := &DispatchHook{}
assert.Implementor(c, &r, "should be a Hook")
}
func TestDispatchHook_Run_NoHooks(t *testing.T) {
@@ -51,6 +57,8 @@ func TestDispatchHook_Run_NoHooks(t *testing.T) {
}
func TestDispatchHook_Run(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
hook := &MockHook{}
mapping := make(map[string][]Hook)
@@ -58,15 +66,9 @@ func TestDispatchHook_Run(t *testing.T) {
dh := &DispatchHook{Mapping: mapping}
dh.Run("foo", nil, nil, 42)
if !hook.RunCalled {
t.Fatal("should be called")
}
if hook.RunName != "foo" {
t.Fatalf("bad: %s", hook.RunName)
}
if hook.RunData != 42 {
t.Fatalf("bad: %#v", hook.RunData)
}
assert.True(hook.RunCalled, "run should be called")
assert.Equal(hook.RunName, "foo", "should be proper event")
assert.Equal(hook.RunData, 42, "should be correct data")
}
func TestDispatchHook_cancel(t *testing.T) {
+4 -3
View File
@@ -1,6 +1,7 @@
package packer
import (
"cgl.tideland.biz/asserts"
"errors"
"testing"
)
@@ -14,6 +15,8 @@ func TestMultiError_Impl(t *testing.T) {
}
func TestMultiErrorError(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
expected := `2 error(s) occurred:
* foo
@@ -25,9 +28,7 @@ func TestMultiErrorError(t *testing.T) {
}
multi := &MultiError{errors}
if multi.Error() != expected {
t.Fatalf("bad: %s", multi.Error())
}
assert.Equal(multi.Error(), expected, "should have proper error")
}
func TestMultiErrorAppend_MultiError(t *testing.T) {
+1 -1
View File
@@ -10,7 +10,7 @@ type cmdBuilder struct {
client *Client
}
func (b *cmdBuilder) Prepare(config ...interface{}) ([]string, error) {
func (b *cmdBuilder) Prepare(config ...interface{}) error {
defer func() {
r := recover()
b.checkExit(r, nil)
+13
View File
@@ -1,10 +1,23 @@
package plugin
import (
"github.com/mitchellh/packer/packer"
"os/exec"
"testing"
)
type helperBuilder byte
func (helperBuilder) Prepare(...interface{}) error {
return nil
}
func (helperBuilder) Run(packer.Ui, packer.Hook, packer.Cache) (packer.Artifact, error) {
return nil, nil
}
func (helperBuilder) Cancel() {}
func TestBuilder_NoExist(t *testing.T) {
c := NewClient(&ClientConfig{Cmd: exec.Command("i-should-not-exist")})
defer c.Kill()
+4 -18
View File
@@ -317,24 +317,10 @@ func (c *Client) Start() (address string, err error) {
err = errors.New("timeout while waiting for plugin to start")
case <-exitCh:
err = errors.New("plugin exited before we could connect")
case lineBytes := <-linesCh:
// Trim the line and split by "|" in order to get the parts of
// the output.
line := strings.TrimSpace(string(lineBytes))
parts := strings.SplitN(line, "|", 2)
if len(parts) < 2 {
err = fmt.Errorf("Unrecognized remote plugin message: %s", line)
return
}
// Test the API version
if parts[0] != APIVersion {
err = fmt.Errorf("Incompatible API version with plugin. "+
"Plugin version: %s, Ours: %s", parts[0], APIVersion)
return
}
c.address = parts[1]
case line := <-linesCh:
// Trim the address and reset the err since we were able
// to read some sort of address.
c.address = strings.TrimSpace(string(line))
address = c.address
}
-15
View File
@@ -37,21 +37,6 @@ func TestClient(t *testing.T) {
}
}
func TestClientStart_badVersion(t *testing.T) {
config := &ClientConfig{
Cmd: helperProcess("bad-version"),
StartTimeout: 50 * time.Millisecond,
}
c := NewClient(config)
defer c.Kill()
_, err := c.Start()
if err == nil {
t.Fatal("err should not be nil")
}
}
func TestClient_Start_Timeout(t *testing.T) {
config := &ClientConfig{
Cmd: helperProcess("start-timeout"),
+1 -8
View File
@@ -30,16 +30,9 @@ var Interrupts int32 = 0
const MagicCookieKey = "PACKER_PLUGIN_MAGIC_COOKIE"
const MagicCookieValue = "d602bf8f470bc67ca7faa0386276bbdd4330efaf76d1a219cb4d6991ca9872b2"
// The APIVersion is outputted along with the RPC address. The plugin
// client validates this API version and will show an error if it doesn't
// know how to speak it.
const APIVersion = "1"
// This serves a single RPC connection on the given RPC server on
// a random port.
func serve(server *rpc.Server) (err error) {
log.Printf("Plugin build against Packer '%s'", packer.GitCommit)
if os.Getenv(MagicCookieKey) != MagicCookieValue {
return errors.New("Please do not execute plugins directly. Packer will execute these for you.")
}
@@ -82,7 +75,7 @@ func serve(server *rpc.Server) (err error) {
// Output the address to stdout
log.Printf("Plugin address: %s\n", address)
fmt.Printf("%s|%s\n", APIVersion, address)
fmt.Println(address)
os.Stdout.Sync()
// Accept a connection
+4 -7
View File
@@ -50,11 +50,8 @@ func TestHelperProcess(*testing.T) {
cmd, args := args[0], args[1:]
switch cmd {
case "bad-version":
fmt.Printf("%s1|:1234\n", APIVersion)
<-make(chan int)
case "builder":
ServeBuilder(new(packer.MockBuilder))
ServeBuilder(new(helperBuilder))
case "command":
ServeCommand(new(helperCommand))
case "hook":
@@ -62,7 +59,7 @@ func TestHelperProcess(*testing.T) {
case "invalid-rpc-address":
fmt.Println("lolinvalid")
case "mock":
fmt.Printf("%s|:1234\n", APIVersion)
fmt.Println(":1234")
<-make(chan int)
case "post-processor":
ServePostProcessor(new(helperPostProcessor))
@@ -72,11 +69,11 @@ func TestHelperProcess(*testing.T) {
time.Sleep(1 * time.Minute)
os.Exit(1)
case "stderr":
fmt.Printf("%s|:1234\n", APIVersion)
fmt.Println(":1234")
log.Println("HELLO")
log.Println("WORLD")
case "stdin":
fmt.Printf("%s|:1234\n", APIVersion)
fmt.Println(":1234")
data := make([]byte, 5)
if _, err := os.Stdin.Read(data); err != nil {
log.Printf("stdin read error: %s", err)
+14 -20
View File
@@ -1,9 +1,9 @@
package rpc
import (
"cgl.tideland.biz/asserts"
"github.com/mitchellh/packer/packer"
"net/rpc"
"reflect"
"testing"
)
@@ -30,6 +30,8 @@ func (testArtifact) Destroy() error {
}
func TestArtifactRPC(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
// Create the interface to test
a := new(testArtifact)
@@ -40,29 +42,21 @@ func TestArtifactRPC(t *testing.T) {
// Create the client over RPC and run some methods to verify it works
client, err := rpc.Dial("tcp", address)
if err != nil {
t.Fatalf("err: %s", err)
}
assert.Nil(err, "should be able to connect")
aClient := Artifact(client)
// Test
if aClient.BuilderId() != "bid" {
t.Fatalf("bad: %s", aClient.BuilderId())
}
if !reflect.DeepEqual(aClient.Files(), []string{"a", "b"}) {
t.Fatalf("bad: %#v", aClient.Files())
}
if aClient.Id() != "id" {
t.Fatalf("bad: %s", aClient.Id())
}
if aClient.String() != "string" {
t.Fatalf("bad: %s", aClient.String())
}
assert.Equal(aClient.BuilderId(), "bid", "should have correct builder ID")
assert.Equal(aClient.Files(), []string{"a", "b"}, "should have correct builder ID")
assert.Equal(aClient.Id(), "id", "should have correct builder ID")
assert.Equal(aClient.String(), "string", "should have correct builder ID")
}
func TestArtifact_Implements(t *testing.T) {
var _ packer.Artifact = Artifact(nil)
assert := asserts.NewTestingAsserts(t, true)
var r packer.Artifact
a := Artifact(nil)
assert.Implementor(a, &r, "should be an Artifact")
}
+8 -18
View File
@@ -21,11 +21,6 @@ type BuildRunArgs struct {
UiRPCAddress string
}
type BuildPrepareResponse struct {
Warnings []string
Error error
}
func Build(client *rpc.Client) *build {
return &build{client}
}
@@ -35,13 +30,12 @@ func (b *build) Name() (result string) {
return
}
func (b *build) Prepare(v map[string]string) ([]string, error) {
var resp BuildPrepareResponse
if cerr := b.client.Call("Build.Prepare", v, &resp); cerr != nil {
return nil, cerr
func (b *build) Prepare(v map[string]string) (err error) {
if cerr := b.client.Call("Build.Prepare", v, &err); cerr != nil {
return cerr
}
return resp.Warnings, resp.Error
return
}
func (b *build) Run(ui packer.Ui, cache packer.Cache) ([]packer.Artifact, error) {
@@ -58,7 +52,7 @@ func (b *build) Run(ui packer.Ui, cache packer.Cache) ([]packer.Artifact, error)
artifacts := make([]packer.Artifact, len(result))
for i, addr := range result {
client, err := rpcDial(addr)
client, err := rpc.Dial("tcp", addr)
if err != nil {
return nil, err
}
@@ -92,17 +86,13 @@ func (b *BuildServer) Name(args *interface{}, reply *string) error {
return nil
}
func (b *BuildServer) Prepare(v map[string]string, resp *BuildPrepareResponse) error {
warnings, err := b.build.Prepare(v)
*resp = BuildPrepareResponse{
Warnings: warnings,
Error: err,
}
func (b *BuildServer) Prepare(v map[string]string, reply *error) error {
*reply = b.build.Prepare(v)
return nil
}
func (b *BuildServer) Run(args *BuildRunArgs, reply *[]string) error {
client, err := rpcDial(args.UiRPCAddress)
client, err := rpc.Dial("tcp", args.UiRPCAddress)
if err != nil {
return err
}
+36 -82
View File
@@ -1,26 +1,25 @@
package rpc
import (
"cgl.tideland.biz/asserts"
"errors"
"github.com/mitchellh/packer/packer"
"net/rpc"
"reflect"
"testing"
)
var testBuildArtifact = &testArtifact{}
type testBuild struct {
nameCalled bool
prepareCalled bool
prepareVars map[string]string
prepareWarnings []string
runCalled bool
runCache packer.Cache
runUi packer.Ui
setDebugCalled bool
setForceCalled bool
cancelCalled bool
nameCalled bool
prepareCalled bool
prepareVars map[string]string
runCalled bool
runCache packer.Cache
runUi packer.Ui
setDebugCalled bool
setForceCalled bool
cancelCalled bool
errRunResult bool
}
@@ -30,10 +29,10 @@ func (b *testBuild) Name() string {
return "name"
}
func (b *testBuild) Prepare(v map[string]string) ([]string, error) {
func (b *testBuild) Prepare(v map[string]string) error {
b.prepareCalled = true
b.prepareVars = v
return b.prepareWarnings, nil
return nil
}
func (b *testBuild) Run(ui packer.Ui, cache packer.Cache) ([]packer.Artifact, error) {
@@ -60,7 +59,9 @@ func (b *testBuild) Cancel() {
b.cancelCalled = true
}
func buildRPCClient(t *testing.T) (*testBuild, packer.Build) {
func TestBuildRPC(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
// Create the interface to test
b := new(testBuild)
@@ -71,27 +72,16 @@ func buildRPCClient(t *testing.T) (*testBuild, packer.Build) {
// Create the client over RPC and run some methods to verify it works
client, err := rpc.Dial("tcp", address)
if err != nil {
t.Fatalf("err: %s", err)
}
return b, Build(client)
}
func TestBuild(t *testing.T) {
b, bClient := buildRPCClient(t)
assert.Nil(err, "should be able to connect")
bClient := Build(client)
// Test Name
bClient.Name()
if !b.nameCalled {
t.Fatal("name should be called")
}
assert.True(b.nameCalled, "name should be called")
// Test Prepare
bClient.Prepare(map[string]string{"foo": "bar"})
if !b.prepareCalled {
t.Fatal("prepare should be called")
}
assert.True(b.prepareCalled, "prepare should be called")
if len(b.prepareVars) != 1 {
t.Fatalf("bad vars: %#v", b.prepareVars)
}
@@ -104,80 +94,44 @@ func TestBuild(t *testing.T) {
cache := new(testCache)
ui := new(testUi)
artifacts, err := bClient.Run(ui, cache)
if !b.runCalled {
t.Fatal("run should be called")
}
if err != nil {
t.Fatalf("err: %s", err)
}
if len(artifacts) != 1 {
t.Fatalf("bad: %#v", artifacts)
}
if artifacts[0].BuilderId() != "bid" {
t.Fatalf("bad: %#v", artifacts)
}
assert.True(b.runCalled, "run should be called")
assert.Nil(err, "should not error")
assert.Equal(len(artifacts), 1, "should have one artifact")
assert.Equal(artifacts[0].BuilderId(), "bid", "should have proper builder id")
// Test the UI given to run, which should be fully functional
if b.runCalled {
b.runCache.Lock("foo")
if !cache.lockCalled {
t.Fatal("lock shuld be called")
}
assert.True(cache.lockCalled, "lock should be called")
b.runUi.Say("format")
if !ui.sayCalled {
t.Fatal("say should be called")
}
if ui.sayMessage != "format" {
t.Fatalf("bad: %#v", ui.sayMessage)
}
assert.True(ui.sayCalled, "say should be called")
assert.Equal(ui.sayMessage, "format", "message should be correct")
}
// Test run with an error
b.errRunResult = true
_, err = bClient.Run(ui, cache)
if err == nil {
t.Fatal("should error")
}
assert.NotNil(err, "should not nil")
// Test SetDebug
bClient.SetDebug(true)
if !b.setDebugCalled {
t.Fatal("should be called")
}
assert.True(b.setDebugCalled, "should be called")
// Test SetForce
bClient.SetForce(true)
if !b.setForceCalled {
t.Fatal("should be called")
}
assert.True(b.setForceCalled, "should be called")
// Test Cancel
bClient.Cancel()
if !b.cancelCalled {
t.Fatal("should be called")
}
}
func TestBuildPrepare_Warnings(t *testing.T) {
b, bClient := buildRPCClient(t)
expected := []string{"foo"}
b.prepareWarnings = expected
warnings, err := bClient.Prepare(nil)
if err != nil {
t.Fatalf("err: %s", err)
}
if !reflect.DeepEqual(warnings, expected) {
t.Fatalf("bad: %#v", warnings)
}
assert.True(b.cancelCalled, "cancel should be called")
}
func TestBuild_ImplementsBuild(t *testing.T) {
var _ packer.Build = Build(nil)
assert := asserts.NewTestingAsserts(t, true)
var realBuild packer.Build
b := Build(nil)
assert.Implementor(b, &realBuild, "should be a Build")
}
+11 -20
View File
@@ -5,6 +5,7 @@ import (
"fmt"
"github.com/mitchellh/packer/packer"
"log"
"net"
"net/rpc"
)
@@ -29,11 +30,6 @@ type BuilderRunArgs struct {
ResponseAddress string
}
type BuilderPrepareResponse struct {
Warnings []string
Error error
}
type BuilderRunResponse struct {
Err error
RPCAddress string
@@ -43,14 +39,13 @@ func Builder(client *rpc.Client) *builder {
return &builder{client}
}
func (b *builder) Prepare(config ...interface{}) ([]string, error) {
var resp BuilderPrepareResponse
cerr := b.client.Call("Builder.Prepare", &BuilderPrepareArgs{config}, &resp)
func (b *builder) Prepare(config ...interface{}) (err error) {
cerr := b.client.Call("Builder.Prepare", &BuilderPrepareArgs{config}, &err)
if cerr != nil {
return nil, cerr
err = cerr
}
return resp.Warnings, resp.Error
return
}
func (b *builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
@@ -100,7 +95,7 @@ func (b *builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
return nil, nil
}
client, err := rpcDial(response.RPCAddress)
client, err := rpc.Dial("tcp", response.RPCAddress)
if err != nil {
return nil, err
}
@@ -114,26 +109,22 @@ func (b *builder) Cancel() {
}
}
func (b *BuilderServer) Prepare(args *BuilderPrepareArgs, reply *BuilderPrepareResponse) error {
warnings, err := b.builder.Prepare(args.Configs...)
func (b *BuilderServer) Prepare(args *BuilderPrepareArgs, reply *error) error {
err := b.builder.Prepare(args.Configs...)
if err != nil {
err = NewBasicError(err)
*reply = NewBasicError(err)
}
*reply = BuilderPrepareResponse{
Warnings: warnings,
Error: err,
}
return nil
}
func (b *BuilderServer) Run(args *BuilderRunArgs, reply *interface{}) error {
client, err := rpcDial(args.RPCAddress)
client, err := rpc.Dial("tcp", args.RPCAddress)
if err != nil {
return err
}
responseC, err := tcpDial(args.ResponseAddress)
responseC, err := net.Dial("tcp", args.ResponseAddress)
if err != nil {
return err
}
+84 -113
View File
@@ -1,16 +1,58 @@
package rpc
import (
"cgl.tideland.biz/asserts"
"errors"
"github.com/mitchellh/packer/packer"
"net/rpc"
"reflect"
"testing"
)
var testBuilderArtifact = &testArtifact{}
func builderRPCClient(t *testing.T) (*packer.MockBuilder, packer.Builder) {
b := new(packer.MockBuilder)
type testBuilder struct {
prepareCalled bool
prepareConfig []interface{}
runCalled bool
runCache packer.Cache
runHook packer.Hook
runUi packer.Ui
cancelCalled bool
errRunResult bool
nilRunResult bool
}
func (b *testBuilder) Prepare(config ...interface{}) error {
b.prepareCalled = true
b.prepareConfig = config
return nil
}
func (b *testBuilder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
b.runCache = cache
b.runCalled = true
b.runHook = hook
b.runUi = ui
if b.errRunResult {
return nil, errors.New("foo")
} else if b.nilRunResult {
return nil, nil
} else {
return testBuilderArtifact, nil
}
}
func (b *testBuilder) Cancel() {
b.cancelCalled = true
}
func TestBuilderRPC(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
// Create the interface to test
b := new(testBuilder)
// Start the server
server := rpc.NewServer()
@@ -19,131 +61,60 @@ func builderRPCClient(t *testing.T) (*packer.MockBuilder, packer.Builder) {
// Create the client over RPC and run some methods to verify it works
client, err := rpc.Dial("tcp", address)
if err != nil {
t.Fatalf("err: %s", err)
}
return b, Builder(client)
}
func TestBuilderPrepare(t *testing.T) {
b, bClient := builderRPCClient(t)
assert.Nil(err, "should be able to connect")
// Test Prepare
config := 42
warnings, err := bClient.Prepare(config)
if err != nil {
t.Fatalf("bad: %s", err)
}
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
if !b.PrepareCalled {
t.Fatal("should be called")
}
if !reflect.DeepEqual(b.PrepareConfig, []interface{}{42}) {
t.Fatalf("bad: %#v", b.PrepareConfig)
}
}
func TestBuilderPrepare_Warnings(t *testing.T) {
b, bClient := builderRPCClient(t)
expected := []string{"foo"}
b.PrepareWarnings = expected
// Test Prepare
warnings, err := bClient.Prepare(nil)
if err != nil {
t.Fatalf("bad: %s", err)
}
if !reflect.DeepEqual(warnings, expected) {
t.Fatalf("bad: %#v", warnings)
}
}
func TestBuilderRun(t *testing.T) {
b, bClient := builderRPCClient(t)
bClient := Builder(client)
bClient.Prepare(config)
assert.True(b.prepareCalled, "prepare should be called")
assert.Equal(b.prepareConfig, []interface{}{42}, "prepare should be called with right arg")
// Test Run
cache := new(testCache)
hook := &packer.MockHook{}
ui := &testUi{}
artifact, err := bClient.Run(ui, hook, cache)
if err != nil {
t.Fatalf("err: %s", err)
assert.Nil(err, "should have no error")
assert.True(b.runCalled, "runs hould be called")
if b.runCalled {
b.runCache.Lock("foo")
assert.True(cache.lockCalled, "lock should be called")
b.runHook.Run("foo", nil, nil, nil)
assert.True(hook.RunCalled, "run should be called")
b.runUi.Say("format")
assert.True(ui.sayCalled, "say should be called")
assert.Equal(ui.sayMessage, "format", "message should be correct")
assert.Equal(artifact.Id(), testBuilderArtifact.Id(), "should have artifact Id")
}
if !b.RunCalled {
t.Fatal("run should be called")
}
// Test run with nil result
b.nilRunResult = true
artifact, err = bClient.Run(ui, hook, cache)
assert.Nil(artifact, "should be nil")
assert.Nil(err, "should have no error")
b.RunCache.Lock("foo")
if !cache.lockCalled {
t.Fatal("should be called")
}
b.RunHook.Run("foo", nil, nil, nil)
if !hook.RunCalled {
t.Fatal("should be called")
}
b.RunUi.Say("format")
if !ui.sayCalled {
t.Fatal("say should be called")
}
if ui.sayMessage != "format" {
t.Fatalf("bad: %s", ui.sayMessage)
}
if artifact.Id() != testBuilderArtifact.Id() {
t.Fatalf("bad: %s", artifact.Id())
}
}
func TestBuilderRun_nilResult(t *testing.T) {
b, bClient := builderRPCClient(t)
b.RunNilResult = true
cache := new(testCache)
hook := &packer.MockHook{}
ui := &testUi{}
artifact, err := bClient.Run(ui, hook, cache)
if artifact != nil {
t.Fatalf("bad: %#v", artifact)
}
if err != nil {
t.Fatalf("bad: %#v", err)
}
}
func TestBuilderRun_ErrResult(t *testing.T) {
b, bClient := builderRPCClient(t)
b.RunErrResult = true
cache := new(testCache)
hook := &packer.MockHook{}
ui := &testUi{}
artifact, err := bClient.Run(ui, hook, cache)
if artifact != nil {
t.Fatalf("bad: %#v", artifact)
}
if err == nil {
t.Fatal("should have error")
}
}
func TestBuilderCancel(t *testing.T) {
b, bClient := builderRPCClient(t)
// Test with an error
b.errRunResult = true
b.nilRunResult = false
artifact, err = bClient.Run(ui, hook, cache)
assert.Nil(artifact, "should be nil")
assert.NotNil(err, "should have error")
// Test Cancel
bClient.Cancel()
if !b.CancelCalled {
t.Fatal("cancel should be called")
}
assert.True(b.cancelCalled, "cancel should be called")
}
func TestBuilder_ImplementsBuilder(t *testing.T) {
var _ packer.Builder = Builder(nil)
assert := asserts.NewTestingAsserts(t, true)
var realBuilder packer.Builder
b := Builder(nil)
assert.Implementor(b, &realBuilder, "should be a Builder")
}
+12 -27
View File
@@ -1,6 +1,7 @@
package rpc
import (
"cgl.tideland.biz/asserts"
"github.com/mitchellh/packer/packer"
"net/rpc"
"testing"
@@ -48,6 +49,8 @@ func TestCache_Implements(t *testing.T) {
}
func TestCacheRPC(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
// Create the interface to test
c := new(testCache)
@@ -58,44 +61,26 @@ func TestCacheRPC(t *testing.T) {
// Create the client over RPC and run some methods to verify it works
rpcClient, err := rpc.Dial("tcp", address)
if err != nil {
t.Fatalf("bad: %s", err)
}
assert.Nil(err, "should be able to connect")
client := Cache(rpcClient)
// Test Lock
client.Lock("foo")
if !c.lockCalled {
t.Fatal("should be called")
}
if c.lockKey != "foo" {
t.Fatalf("bad: %s", c.lockKey)
}
assert.True(c.lockCalled, "should be called")
assert.Equal(c.lockKey, "foo", "should have proper key")
// Test Unlock
client.Unlock("foo")
if !c.unlockCalled {
t.Fatal("should be called")
}
if c.unlockKey != "foo" {
t.Fatalf("bad: %s", c.unlockKey)
}
assert.True(c.unlockCalled, "should be called")
assert.Equal(c.unlockKey, "foo", "should have proper key")
// Test RLock
client.RLock("foo")
if !c.rlockCalled {
t.Fatal("should be called")
}
if c.rlockKey != "foo" {
t.Fatalf("bad: %s", c.rlockKey)
}
assert.True(c.rlockCalled, "should be called")
assert.Equal(c.rlockKey, "foo", "should have proper key")
// Test RUnlock
client.RUnlock("foo")
if !c.runlockCalled {
t.Fatal("should be called")
}
if c.runlockKey != "foo" {
t.Fatalf("bad: %s", c.runlockKey)
}
assert.True(c.runlockCalled, "should be called")
assert.Equal(c.runlockKey, "foo", "should have proper key")
}
+1 -1
View File
@@ -66,7 +66,7 @@ func (c *CommandServer) Help(args *interface{}, reply *string) error {
}
func (c *CommandServer) Run(args *CommandRunArgs, reply *int) error {
client, err := rpcDial(args.RPCAddress)
client, err := rpc.Dial("tcp", args.RPCAddress)
if err != nil {
return err
}
+18 -24
View File
@@ -1,9 +1,9 @@
package rpc
import (
"cgl.tideland.biz/asserts"
"github.com/mitchellh/packer/packer"
"net/rpc"
"reflect"
"testing"
)
@@ -29,6 +29,8 @@ func (tc *TestCommand) Synopsis() string {
}
func TestRPCCommand(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
// Create the command
command := new(TestCommand)
@@ -40,45 +42,37 @@ func TestRPCCommand(t *testing.T) {
// Create the command client over RPC and run some methods to verify
// we get the proper behavior.
client, err := rpc.Dial("tcp", address)
if err != nil {
t.Fatalf("err: %s", err)
}
assert.Nil(err, "should be no error")
clientComm := Command(client)
//Test Help
help := clientComm.Help()
if help != "bar" {
t.Fatalf("bad: %s", help)
}
assert.Equal(help, "bar", "helps hould be correct")
// Test run
runArgs := []string{"foo", "bar"}
testEnv := &testEnvironment{}
exitCode := clientComm.Run(testEnv, runArgs)
if !reflect.DeepEqual(command.runArgs, runArgs) {
t.Fatalf("bad: %#v", command.runArgs)
}
if exitCode != 0 {
t.Fatalf("bad: %d", exitCode)
}
assert.Equal(command.runArgs, runArgs, "Correct args should be sent")
assert.Equal(exitCode, 0, "Exit code should be correct")
if command.runEnv == nil {
t.Fatal("runEnv should not be nil")
}
command.runEnv.Ui()
if !testEnv.uiCalled {
t.Fatal("ui should be called")
assert.NotNil(command.runEnv, "should have an env")
if command.runEnv != nil {
command.runEnv.Ui()
assert.True(testEnv.uiCalled, "UI should be called on env")
}
// Test Synopsis
synopsis := clientComm.Synopsis()
if synopsis != "foo" {
t.Fatalf("bad: %#v", synopsis)
}
assert.Equal(synopsis, "foo", "Synopsis should be correct")
}
func TestCommand_Implements(t *testing.T) {
var _ packer.Command = Command(nil)
assert := asserts.NewTestingAsserts(t, true)
var r packer.Command
c := Command(nil)
assert.Implementor(c, &r, "should be a Builder")
}
+6 -6
View File
@@ -177,7 +177,7 @@ func (c *CommunicatorServer) Start(args *CommunicatorStartArgs, reply *interface
toClose := make([]net.Conn, 0)
if args.StdinAddress != "" {
stdinC, err := tcpDial(args.StdinAddress)
stdinC, err := net.Dial("tcp", args.StdinAddress)
if err != nil {
return err
}
@@ -187,7 +187,7 @@ func (c *CommunicatorServer) Start(args *CommunicatorStartArgs, reply *interface
}
if args.StdoutAddress != "" {
stdoutC, err := tcpDial(args.StdoutAddress)
stdoutC, err := net.Dial("tcp", args.StdoutAddress)
if err != nil {
return err
}
@@ -197,7 +197,7 @@ func (c *CommunicatorServer) Start(args *CommunicatorStartArgs, reply *interface
}
if args.StderrAddress != "" {
stderrC, err := tcpDial(args.StderrAddress)
stderrC, err := net.Dial("tcp", args.StderrAddress)
if err != nil {
return err
}
@@ -208,7 +208,7 @@ func (c *CommunicatorServer) Start(args *CommunicatorStartArgs, reply *interface
// Connect to the response address so we can write our result to it
// when ready.
responseC, err := tcpDial(args.ResponseAddress)
responseC, err := net.Dial("tcp", args.ResponseAddress)
if err != nil {
return err
}
@@ -234,7 +234,7 @@ func (c *CommunicatorServer) Start(args *CommunicatorStartArgs, reply *interface
}
func (c *CommunicatorServer) Upload(args *CommunicatorUploadArgs, reply *interface{}) (err error) {
readerC, err := tcpDial(args.ReaderAddress)
readerC, err := net.Dial("tcp", args.ReaderAddress)
if err != nil {
return
}
@@ -250,7 +250,7 @@ func (c *CommunicatorServer) UploadDir(args *CommunicatorUploadDirArgs, reply *e
}
func (c *CommunicatorServer) Download(args *CommunicatorDownloadArgs, reply *interface{}) (err error) {
writerC, err := tcpDial(args.WriterAddress)
writerC, err := net.Dial("tcp", args.WriterAddress)
if err != nil {
return
}
-33
View File
@@ -1,33 +0,0 @@
package rpc
import (
"net"
"net/rpc"
)
// rpcDial makes a TCP connection to a remote RPC server and returns
// the client. This will set the connection up properly so that keep-alives
// are set and so on and should be used to make all RPC connections within
// this package.
func rpcDial(address string) (*rpc.Client, error) {
tcpConn, err := tcpDial(address)
if err != nil {
return nil, err
}
// Create an RPC client around our connection
return rpc.NewClient(tcpConn), nil
}
// tcpDial connects via TCP to the designated address.
func tcpDial(address string) (*net.TCPConn, error) {
conn, err := net.Dial("tcp", address)
if err != nil {
return nil, err
}
// Set a keep-alive so that the connection stays alive even when idle
tcpConn := conn.(*net.TCPConn)
tcpConn.SetKeepAlive(true)
return tcpConn, nil
}
+6 -6
View File
@@ -28,7 +28,7 @@ func (e *Environment) Builder(name string) (b packer.Builder, err error) {
return
}
client, err := rpcDial(reply)
client, err := rpc.Dial("tcp", reply)
if err != nil {
return
}
@@ -43,7 +43,7 @@ func (e *Environment) Cache() packer.Cache {
panic(err)
}
client, err := rpcDial(reply)
client, err := rpc.Dial("tcp", reply)
if err != nil {
panic(err)
}
@@ -64,7 +64,7 @@ func (e *Environment) Hook(name string) (h packer.Hook, err error) {
return
}
client, err := rpcDial(reply)
client, err := rpc.Dial("tcp", reply)
if err != nil {
return
}
@@ -80,7 +80,7 @@ func (e *Environment) PostProcessor(name string) (p packer.PostProcessor, err er
return
}
client, err := rpcDial(reply)
client, err := rpc.Dial("tcp", reply)
if err != nil {
return
}
@@ -96,7 +96,7 @@ func (e *Environment) Provisioner(name string) (p packer.Provisioner, err error)
return
}
client, err := rpcDial(reply)
client, err := rpc.Dial("tcp", reply)
if err != nil {
return
}
@@ -109,7 +109,7 @@ func (e *Environment) Ui() packer.Ui {
var reply string
e.client.Call("Environment.Ui", new(interface{}), &reply)
client, err := rpcDial(reply)
client, err := rpc.Dial("tcp", reply)
if err != nil {
panic(err)
}
+23 -42
View File
@@ -1,13 +1,13 @@
package rpc
import (
"cgl.tideland.biz/asserts"
"github.com/mitchellh/packer/packer"
"net/rpc"
"reflect"
"testing"
)
var testEnvBuilder = &packer.MockBuilder{}
var testEnvBuilder = &testBuilder{}
var testEnvCache = &testCache{}
var testEnvUi = &testUi{}
@@ -65,6 +65,8 @@ func (e *testEnvironment) Ui() packer.Ui {
}
func TestEnvironmentRPC(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
// Create the interface to test
e := &testEnvironment{}
@@ -75,70 +77,49 @@ func TestEnvironmentRPC(t *testing.T) {
// Create the client over RPC and run some methods to verify it works
client, err := rpc.Dial("tcp", address)
if err != nil {
t.Fatalf("err: %s", err)
}
assert.Nil(err, "should be able to connect")
eClient := &Environment{client}
// Test Builder
builder, _ := eClient.Builder("foo")
if !e.builderCalled {
t.Fatal("builder should be called")
}
if e.builderName != "foo" {
t.Fatalf("bad: %#v", e.builderName)
}
assert.True(e.builderCalled, "Builder should be called")
assert.Equal(e.builderName, "foo", "Correct name for Builder")
builder.Prepare(nil)
if !testEnvBuilder.PrepareCalled {
t.Fatal("should be called")
}
assert.True(testEnvBuilder.prepareCalled, "Prepare should be called")
// Test Cache
cache := eClient.Cache()
cache.Lock("foo")
if !testEnvCache.lockCalled {
t.Fatal("should be called")
}
assert.True(testEnvCache.lockCalled, "lock 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)
}
assert.True(e.cliCalled, "CLI should be called")
assert.Equal(e.cliArgs, cliArgs, "args should match")
assert.Equal(result, 42, "result shuld be 42")
// Test Provisioner
_, _ = eClient.Provisioner("foo")
if !e.provCalled {
t.Fatal("should be called")
}
if e.provName != "foo" {
t.Fatalf("bad: %s", e.provName)
}
assert.True(e.provCalled, "provisioner should be called")
assert.Equal(e.provName, "foo", "should have proper name")
// Test Ui
ui := eClient.Ui()
if !e.uiCalled {
t.Fatal("should be called")
}
assert.True(e.uiCalled, "Ui should've been called")
// Test calls on the Ui
ui.Say("format")
if !testEnvUi.sayCalled {
t.Fatal("should be called")
}
if testEnvUi.sayMessage != "format" {
t.Fatalf("bad: %#v", testEnvUi.sayMessage)
}
assert.True(testEnvUi.sayCalled, "Say should be called")
assert.Equal(testEnvUi.sayMessage, "format", "message should match")
}
func TestEnvironment_ImplementsEnvironment(t *testing.T) {
var _ packer.Environment = new(Environment)
assert := asserts.NewTestingAsserts(t, true)
var realVar packer.Environment
e := &Environment{nil}
assert.Implementor(e, &realVar, "should be an Environment")
}

Some files were not shown because too many files have changed in this diff Show More