Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0b23c8addf | |||
| aa658e982b |
@@ -1,101 +1,3 @@
|
||||
## 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
@@ -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`.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -27,19 +27,16 @@ 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
|
||||
@@ -81,14 +78,18 @@ func (b *Builder) Prepare(raws ...interface{}) 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{}) 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 {
|
||||
@@ -143,7 +146,7 @@ func (b *Builder) Prepare(raws ...interface{}) error {
|
||||
return errs
|
||||
}
|
||||
|
||||
log.Println(common.ScrubConfig(b.config), b.config.AccessKey, b.config.SecretKey)
|
||||
log.Printf("Config: %+v", b.config)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -82,14 +82,3 @@ func TestBuilderPrepare_SourceAmi(t *testing.T) {
|
||||
t.Errorf("err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuilderPrepare_CommandWrapper(t *testing.T) {
|
||||
b := &Builder{}
|
||||
config := testConfig()
|
||||
|
||||
config["command_wrapper"] = "echo hi; {{.Command}}"
|
||||
err := b.Prepare(config)
|
||||
if err != nil {
|
||||
t.Errorf("err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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 +0,0 @@
|
||||
package chroot
|
||||
@@ -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.")
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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())
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -56,7 +56,7 @@ func (b *Builder) Prepare(raws ...interface{}) error {
|
||||
return errs
|
||||
}
|
||||
|
||||
log.Println(common.ScrubConfig(b.config), b.config.AccessKey, b.config.SecretKey)
|
||||
log.Printf("Config: %+v", b.config)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -83,7 +83,7 @@ func (b *Builder) Prepare(raws ...interface{}) error {
|
||||
"-u {{.AccountId}} " +
|
||||
"-c {{.CertPath}} " +
|
||||
"-r {{.Architecture}} " +
|
||||
"-e {{.PrivatePath}}/* " +
|
||||
"-e {{.PrivatePath}} " +
|
||||
"-d {{.Destination}} " +
|
||||
"-p {{.Prefix}} " +
|
||||
"--batch"
|
||||
@@ -159,7 +159,7 @@ func (b *Builder) Prepare(raws ...interface{}) error {
|
||||
return errs
|
||||
}
|
||||
|
||||
log.Println(common.ScrubConfig(b.config), b.config.AccessKey, b.config.SecretKey)
|
||||
log.Printf("Config: %+v", b.config)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -164,7 +164,7 @@ func (b *Builder) Prepare(raws ...interface{}) error {
|
||||
return errs
|
||||
}
|
||||
|
||||
common.ScrubConfig(b.config, b.config.ClientID, b.config.APIKey)
|
||||
log.Printf("Config: %+v", b.config)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package digitalocean
|
||||
|
||||
import (
|
||||
"cgl.tideland.biz/identifier"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/mitchellh/multistep"
|
||||
"github.com/mitchellh/packer/common/uuid"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
)
|
||||
|
||||
@@ -20,7 +21,7 @@ func (s *stepCreateDroplet) Run(state multistep.StateBag) multistep.StepAction {
|
||||
ui.Say("Creating droplet...")
|
||||
|
||||
// Some random droplet name as it's temporary
|
||||
name := fmt.Sprintf("packer-%s", uuid.TimeOrderedUUID())
|
||||
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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ func (b *Builder) Prepare(raws ...interface{}) error {
|
||||
return errs
|
||||
}
|
||||
|
||||
log.Println(common.ScrubConfig(b.config), b.config.Password)
|
||||
log.Printf("Config: %+v", b.config)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -28,7 +28,6 @@ type config struct {
|
||||
DiskSize uint `mapstructure:"disk_size"`
|
||||
FloppyFiles []string `mapstructure:"floppy_files"`
|
||||
Format string `mapstructure:"format"`
|
||||
GuestAdditionsAttach bool `mapstructure:"guest_additions_attach"`
|
||||
GuestAdditionsPath string `mapstructure:"guest_additions_path"`
|
||||
GuestAdditionsURL string `mapstructure:"guest_additions_url"`
|
||||
GuestAdditionsSHA256 string `mapstructure:"guest_additions_sha256"`
|
||||
@@ -154,7 +153,6 @@ func (b *Builder) Prepare(raws ...interface{}) 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,
|
||||
@@ -363,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),
|
||||
|
||||
@@ -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.GuestAdditionsAttach {
|
||||
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))
|
||||
}
|
||||
}
|
||||
@@ -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())
|
||||
|
||||
@@ -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")))
|
||||
|
||||
@@ -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.GuestAdditionsAttach {
|
||||
log.Println("Not uploading guest additions since we're attaching.")
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
version, err := driver.Version()
|
||||
if err != nil {
|
||||
state.Put("error", fmt.Errorf("Error reading version for guest additions upload: %s", err))
|
||||
|
||||
@@ -148,7 +148,6 @@ func (b *Builder) Prepare(raws ...interface{}) 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,
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
+2
-13
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"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())
|
||||
rand1 := rand.Uint32()
|
||||
rand2 := rand.Uint32()
|
||||
rand3 := rand.Uint32()
|
||||
return fmt.Sprintf("%08x-%04x-%04x-%04x-%04x%08x",
|
||||
unix,
|
||||
uint16(rand1>>16),
|
||||
uint16(rand1&0xffff),
|
||||
uint16(rand2>>16),
|
||||
uint16(rand2&0xffff),
|
||||
rand3)
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTimeOrderedUuid(t *testing.T) {
|
||||
uuid := TimeOrderedUUID()
|
||||
if len(uuid) != 36 {
|
||||
t.Fatalf("bad: %s", uuid)
|
||||
}
|
||||
}
|
||||
@@ -238,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
|
||||
}
|
||||
@@ -416,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
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
+33
-64
@@ -1,6 +1,7 @@
|
||||
package packer
|
||||
|
||||
import (
|
||||
"cgl.tideland.biz/asserts"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
@@ -40,43 +41,32 @@ 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.(*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) {
|
||||
@@ -100,6 +90,8 @@ func TestBuild_Prepare_Twice(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBuild_Prepare_Debug(t *testing.T) {
|
||||
assert := asserts.NewTestingAsserts(t, true)
|
||||
|
||||
packerConfig := testDefaultPackerConfig()
|
||||
packerConfig[DebugConfigKey] = true
|
||||
|
||||
@@ -108,21 +100,13 @@ func TestBuild_Prepare_Debug(t *testing.T) {
|
||||
|
||||
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) {
|
||||
@@ -202,49 +186,37 @@ func TestBuildPrepare_variablesRequired(t *testing.T) {
|
||||
}
|
||||
|
||||
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.(*TestBuilder)
|
||||
if !builder.runCalled {
|
||||
t.Fatal("should be called")
|
||||
}
|
||||
assert.True(builder.runCalled, "run should be called")
|
||||
|
||||
// Verify hooks are disapatchable
|
||||
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) {
|
||||
@@ -384,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.(*TestBuilder)
|
||||
if !builder.cancelCalled {
|
||||
t.Fatal("cancel should be called")
|
||||
}
|
||||
assert.True(builder.cancelCalled, "cancel should be called")
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
+97
-154
@@ -2,11 +2,12 @@ package packer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"cgl.tideland.biz/asserts"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
@@ -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,6 +90,8 @@ func TestEnvironment_NilComponents(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEnvironment_Builder(t *testing.T) {
|
||||
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
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
+4
-18
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -50,9 +50,6 @@ 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(helperBuilder))
|
||||
case "command":
|
||||
@@ -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
@@ -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")
|
||||
}
|
||||
|
||||
+2
-2
@@ -52,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,7 +92,7 @@ func (b *BuildServer) Prepare(v map[string]string, reply *error) error {
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
+23
-48
@@ -1,6 +1,7 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"cgl.tideland.biz/asserts"
|
||||
"errors"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"net/rpc"
|
||||
@@ -59,6 +60,8 @@ func (b *testBuild) Cancel() {
|
||||
}
|
||||
|
||||
func TestBuildRPC(t *testing.T) {
|
||||
assert := asserts.NewTestingAsserts(t, true)
|
||||
|
||||
// Create the interface to test
|
||||
b := new(testBuild)
|
||||
|
||||
@@ -69,23 +72,16 @@ func TestBuildRPC(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")
|
||||
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)
|
||||
}
|
||||
@@ -98,65 +94,44 @@ func TestBuildRPC(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")
|
||||
}
|
||||
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")
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"log"
|
||||
"net"
|
||||
"net/rpc"
|
||||
)
|
||||
|
||||
@@ -94,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
|
||||
}
|
||||
@@ -118,12 +119,12 @@ func (b *BuilderServer) Prepare(args *BuilderPrepareArgs, reply *error) error {
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
+24
-50
@@ -1,10 +1,10 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"cgl.tideland.biz/asserts"
|
||||
"errors"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"net/rpc"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -49,6 +49,8 @@ func (b *testBuilder) Cancel() {
|
||||
}
|
||||
|
||||
func TestBuilderRPC(t *testing.T) {
|
||||
assert := asserts.NewTestingAsserts(t, true)
|
||||
|
||||
// Create the interface to test
|
||||
b := new(testBuilder)
|
||||
|
||||
@@ -59,88 +61,60 @@ func TestBuilderRPC(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")
|
||||
|
||||
// Test Prepare
|
||||
config := 42
|
||||
bClient := Builder(client)
|
||||
bClient.Prepare(config)
|
||||
if !b.prepareCalled {
|
||||
t.Fatal("should be called")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(b.prepareConfig, []interface{}{42}) {
|
||||
t.Fatalf("bad: %#v", b.prepareConfig)
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
if !b.runCalled {
|
||||
t.Fatal("run should be called")
|
||||
}
|
||||
assert.Nil(err, "should have no error")
|
||||
assert.True(b.runCalled, "runs hould be called")
|
||||
|
||||
if b.runCalled {
|
||||
b.runCache.Lock("foo")
|
||||
if !cache.lockCalled {
|
||||
t.Fatal("should be called")
|
||||
}
|
||||
assert.True(cache.lockCalled, "lock should be called")
|
||||
|
||||
b.runHook.Run("foo", nil, nil, nil)
|
||||
if !hook.RunCalled {
|
||||
t.Fatal("should be called")
|
||||
}
|
||||
assert.True(hook.RunCalled, "run should be called")
|
||||
|
||||
b.runUi.Say("format")
|
||||
if !ui.sayCalled {
|
||||
t.Fatal("say should be called")
|
||||
}
|
||||
assert.True(ui.sayCalled, "say should be called")
|
||||
assert.Equal(ui.sayMessage, "format", "message should be correct")
|
||||
|
||||
if ui.sayMessage != "format" {
|
||||
t.Fatalf("bad: %s", ui.sayMessage)
|
||||
}
|
||||
|
||||
if artifact.Id() != testBuilderArtifact.Id() {
|
||||
t.Fatalf("bad: %s", artifact.Id())
|
||||
}
|
||||
assert.Equal(artifact.Id(), testBuilderArtifact.Id(), "should have artifact Id")
|
||||
}
|
||||
|
||||
// Test run with nil result
|
||||
b.nilRunResult = true
|
||||
artifact, err = bClient.Run(ui, hook, cache)
|
||||
if artifact != nil {
|
||||
t.Fatalf("bad: %#v", artifact)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %#v", err)
|
||||
}
|
||||
assert.Nil(artifact, "should be nil")
|
||||
assert.Nil(err, "should have no error")
|
||||
|
||||
// Test with an error
|
||||
b.errRunResult = true
|
||||
b.nilRunResult = false
|
||||
artifact, err = bClient.Run(ui, hook, cache)
|
||||
if artifact != nil {
|
||||
t.Fatalf("bad: %#v", artifact)
|
||||
}
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
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
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"cgl.tideland.biz/asserts"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"net/rpc"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -1,19 +1,25 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"cgl.tideland.biz/asserts"
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBasicError_ImplementsError(t *testing.T) {
|
||||
var _ error = new(BasicError)
|
||||
assert := asserts.NewTestingAsserts(t, true)
|
||||
|
||||
var r error
|
||||
e := &BasicError{""}
|
||||
|
||||
assert.Implementor(e, &r, "should be an error")
|
||||
}
|
||||
|
||||
func TestBasicError_MatchesMessage(t *testing.T) {
|
||||
assert := asserts.NewTestingAsserts(t, true)
|
||||
|
||||
err := errors.New("foo")
|
||||
wrapped := NewBasicError(err)
|
||||
|
||||
if wrapped.Error() != err.Error() {
|
||||
t.Fatalf("bad: %#v", wrapped.Error())
|
||||
}
|
||||
assert.Equal(wrapped.Error(), err.Error(), "should have the same error")
|
||||
}
|
||||
|
||||
+1
-1
@@ -46,7 +46,7 @@ func (h *hook) Cancel() {
|
||||
}
|
||||
|
||||
func (h *HookServer) Run(args *HookRunArgs, reply *interface{}) error {
|
||||
client, err := rpcDial(args.RPCAddress)
|
||||
client, err := rpc.Dial("tcp", args.RPCAddress)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+12
-10
@@ -1,6 +1,7 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"cgl.tideland.biz/asserts"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"net/rpc"
|
||||
"reflect"
|
||||
@@ -10,6 +11,8 @@ import (
|
||||
)
|
||||
|
||||
func TestHookRPC(t *testing.T) {
|
||||
assert := asserts.NewTestingAsserts(t, true)
|
||||
|
||||
// Create the UI to test
|
||||
h := new(packer.MockHook)
|
||||
|
||||
@@ -20,28 +23,27 @@ func TestHookRPC(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")
|
||||
|
||||
hClient := Hook(client)
|
||||
|
||||
// Test Run
|
||||
ui := &testUi{}
|
||||
hClient.Run("foo", ui, nil, 42)
|
||||
if !h.RunCalled {
|
||||
t.Fatal("should be called")
|
||||
}
|
||||
assert.True(h.RunCalled, "run should be called")
|
||||
|
||||
// Test Cancel
|
||||
hClient.Cancel()
|
||||
if !h.CancelCalled {
|
||||
t.Fatal("should be called")
|
||||
}
|
||||
assert.True(h.CancelCalled, "cancel should be called")
|
||||
}
|
||||
|
||||
func TestHook_Implements(t *testing.T) {
|
||||
var _ packer.Hook = new(hook)
|
||||
assert := asserts.NewTestingAsserts(t, true)
|
||||
|
||||
var r packer.Hook
|
||||
h := &hook{nil}
|
||||
|
||||
assert.Implementor(h, &r, "should be a Hook")
|
||||
}
|
||||
|
||||
func TestHook_cancelWhileRun(t *testing.T) {
|
||||
|
||||
+7
-12
@@ -1,6 +1,7 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"cgl.tideland.biz/asserts"
|
||||
"net"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -12,27 +13,21 @@ func addrPort(address net.Addr) string {
|
||||
}
|
||||
|
||||
func Test_netListenerInRange(t *testing.T) {
|
||||
assert := asserts.NewTestingAsserts(t, true)
|
||||
|
||||
// Open up port 10000 so that we take up a port
|
||||
L1000, err := net.Listen("tcp", "127.0.0.1:11000")
|
||||
defer L1000.Close()
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
}
|
||||
assert.Nil(err, "should be able to bind to port 10000")
|
||||
|
||||
if err == nil {
|
||||
// Verify it selects an open port
|
||||
L := netListenerInRange(11000, 11005)
|
||||
if L == nil {
|
||||
t.Fatal("L should not be nil")
|
||||
}
|
||||
if addrPort(L.Addr()) != "11001" {
|
||||
t.Fatalf("bad: %s", L.Addr())
|
||||
}
|
||||
assert.NotNil(L, "should have a listener")
|
||||
assert.Equal(addrPort(L.Addr()), "11001", "should bind to open port")
|
||||
|
||||
// Returns nil if there are no open ports
|
||||
L = netListenerInRange(11000, 11000)
|
||||
if L != nil {
|
||||
t.Fatalf("bad: %#v", L)
|
||||
}
|
||||
assert.Nil(L, "should not get a listener")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ func (p *postProcessor) PostProcess(ui packer.Ui, a packer.Artifact) (packer.Art
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
client, err := rpcDial(response.RPCAddress)
|
||||
client, err := rpc.Dial("tcp", response.RPCAddress)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
@@ -75,7 +75,7 @@ func (p *PostProcessorServer) Configure(args *PostProcessorConfigureArgs, reply
|
||||
}
|
||||
|
||||
func (p *PostProcessorServer) PostProcess(address string, reply *PostProcessorProcessResponse) error {
|
||||
client, err := rpcDial(address)
|
||||
client, err := rpc.Dial("tcp", address)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ func (p *ProvisionerServer) Prepare(args *ProvisionerPrepareArgs, reply *error)
|
||||
}
|
||||
|
||||
func (p *ProvisionerServer) Provision(args *ProvisionerProvisionArgs, reply *interface{}) error {
|
||||
client, err := rpcDial(args.RPCAddress)
|
||||
client, err := rpc.Dial("tcp", args.RPCAddress)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"cgl.tideland.biz/asserts"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"net/rpc"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestProvisionerRPC(t *testing.T) {
|
||||
assert := asserts.NewTestingAsserts(t, true)
|
||||
|
||||
// Create the interface to test
|
||||
p := new(packer.MockProvisioner)
|
||||
|
||||
@@ -18,33 +20,23 @@ func TestProvisionerRPC(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")
|
||||
|
||||
// Test Prepare
|
||||
config := 42
|
||||
pClient := Provisioner(client)
|
||||
pClient.Prepare(config)
|
||||
if !p.PrepCalled {
|
||||
t.Fatal("should be called")
|
||||
}
|
||||
if !reflect.DeepEqual(p.PrepConfigs, []interface{}{42}) {
|
||||
t.Fatalf("bad: %#v", p.PrepConfigs)
|
||||
}
|
||||
assert.True(p.PrepCalled, "prepare should be called")
|
||||
assert.Equal(p.PrepConfigs, []interface{}{42}, "prepare should be called with right arg")
|
||||
|
||||
// Test Provision
|
||||
ui := &testUi{}
|
||||
comm := &packer.MockCommunicator{}
|
||||
pClient.Provision(ui, comm)
|
||||
if !p.ProvCalled {
|
||||
t.Fatal("should be called")
|
||||
}
|
||||
assert.True(p.ProvCalled, "provision should be called")
|
||||
|
||||
p.ProvUi.Say("foo")
|
||||
if !ui.sayCalled {
|
||||
t.Fatal("should be called")
|
||||
}
|
||||
assert.True(ui.sayCalled, "say should be called")
|
||||
|
||||
// Test Cancel
|
||||
pClient.Cancel()
|
||||
@@ -54,5 +46,10 @@ func TestProvisionerRPC(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestProvisioner_Implements(t *testing.T) {
|
||||
var _ packer.Provisioner = Provisioner(nil)
|
||||
assert := asserts.NewTestingAsserts(t, true)
|
||||
|
||||
var r packer.Provisioner
|
||||
p := Provisioner(nil)
|
||||
|
||||
assert.Implementor(p, &r, "should be a provisioner")
|
||||
}
|
||||
|
||||
+10
-21
@@ -1,6 +1,7 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"cgl.tideland.biz/asserts"
|
||||
"net/rpc"
|
||||
"reflect"
|
||||
"testing"
|
||||
@@ -48,6 +49,8 @@ func (u *testUi) Say(message string) {
|
||||
}
|
||||
|
||||
func TestUiRPC(t *testing.T) {
|
||||
assert := asserts.NewTestingAsserts(t, true)
|
||||
|
||||
// Create the UI to test
|
||||
ui := new(testUi)
|
||||
|
||||
@@ -66,33 +69,19 @@ func TestUiRPC(t *testing.T) {
|
||||
|
||||
// Basic error and say tests
|
||||
result, err := uiClient.Ask("query")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
if !ui.askCalled {
|
||||
t.Fatal("should be called")
|
||||
}
|
||||
if ui.askQuery != "query" {
|
||||
t.Fatalf("bad: %s", ui.askQuery)
|
||||
}
|
||||
if result != "foo" {
|
||||
t.Fatalf("bad: %#v", result)
|
||||
}
|
||||
assert.Nil(err, "should not error")
|
||||
assert.True(ui.askCalled, "ask should be called")
|
||||
assert.Equal(ui.askQuery, "query", "should be correct")
|
||||
assert.Equal(result, "foo", "should have correct result")
|
||||
|
||||
uiClient.Error("message")
|
||||
if ui.errorMessage != "message" {
|
||||
t.Fatalf("bad: %#v", ui.errorMessage)
|
||||
}
|
||||
assert.Equal(ui.errorMessage, "message", "message should be correct")
|
||||
|
||||
uiClient.Message("message")
|
||||
if ui.messageMessage != "message" {
|
||||
t.Fatalf("bad: %#v", ui.errorMessage)
|
||||
}
|
||||
assert.Equal(ui.messageMessage, "message", "message should be correct")
|
||||
|
||||
uiClient.Say("message")
|
||||
if ui.sayMessage != "message" {
|
||||
t.Fatalf("bad: %#v", ui.errorMessage)
|
||||
}
|
||||
assert.Equal(ui.sayMessage, "message", "message should be correct")
|
||||
|
||||
uiClient.Machine("foo", "bar", "baz")
|
||||
if !ui.machineCalled {
|
||||
|
||||
+23
-139
@@ -48,8 +48,6 @@ type RawBuilderConfig struct {
|
||||
// configuration. It contains the type of the post processor as well as the
|
||||
// raw configuration that is handed to the post-processor for it to process.
|
||||
type RawPostProcessorConfig struct {
|
||||
TemplateOnlyExcept `mapstructure:",squash"`
|
||||
|
||||
Type string
|
||||
KeepInputArtifact bool `mapstructure:"keep_input_artifact"`
|
||||
RawConfig map[string]interface{}
|
||||
@@ -59,8 +57,6 @@ type RawPostProcessorConfig struct {
|
||||
// It contains the type of the provisioner as well as the raw configuration
|
||||
// that is handed to the provisioner for it to process.
|
||||
type RawProvisionerConfig struct {
|
||||
TemplateOnlyExcept `mapstructure:",squash"`
|
||||
|
||||
Type string
|
||||
Override map[string]interface{}
|
||||
|
||||
@@ -124,25 +120,18 @@ func ParseTemplate(data []byte) (t *Template, err error) {
|
||||
// Gather all the variables
|
||||
for k, v := range rawTpl.Variables {
|
||||
var variable RawVariable
|
||||
variable.Default = ""
|
||||
variable.Required = v == nil
|
||||
|
||||
// Create a new mapstructure decoder in order to decode the default
|
||||
// value since this is the only value in the regular template that
|
||||
// can be weakly typed.
|
||||
decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
|
||||
Result: &variable.Default,
|
||||
WeaklyTypedInput: true,
|
||||
})
|
||||
if err != nil {
|
||||
// This should never happen.
|
||||
panic(err)
|
||||
}
|
||||
if v != nil {
|
||||
def, ok := v.(string)
|
||||
if !ok {
|
||||
errors = append(errors,
|
||||
fmt.Errorf("variable '%s': default value must be string or null", k))
|
||||
continue
|
||||
}
|
||||
|
||||
err = decoder.Decode(v)
|
||||
if err != nil {
|
||||
errors = append(errors,
|
||||
fmt.Errorf("Error decoding default value for user var '%s': %s", k, err))
|
||||
continue
|
||||
variable.Default = def
|
||||
}
|
||||
|
||||
t.Variables[k] = variable
|
||||
@@ -200,50 +189,32 @@ func ParseTemplate(data []byte) (t *Template, err error) {
|
||||
continue
|
||||
}
|
||||
|
||||
configs := make([]RawPostProcessorConfig, 0, len(rawPP))
|
||||
t.PostProcessors[i] = make([]RawPostProcessorConfig, len(rawPP))
|
||||
configs := t.PostProcessors[i]
|
||||
for j, pp := range rawPP {
|
||||
var config RawPostProcessorConfig
|
||||
if err := mapstructure.Decode(pp, &config); err != nil {
|
||||
config := &configs[j]
|
||||
if err := mapstructure.Decode(pp, config); err != nil {
|
||||
if merr, ok := err.(*mapstructure.Error); ok {
|
||||
for _, err := range merr.Errors {
|
||||
errors = append(errors,
|
||||
fmt.Errorf("Post-processor #%d.%d: %s", i+1, j+1, err))
|
||||
errors = append(errors, fmt.Errorf("Post-processor #%d.%d: %s", i+1, j+1, err))
|
||||
}
|
||||
} else {
|
||||
errors = append(errors,
|
||||
fmt.Errorf("Post-processor %d.%d: %s", i+1, j+1, err))
|
||||
errors = append(errors, fmt.Errorf("Post-processor %d.%d: %s", i+1, j+1, err))
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if config.Type == "" {
|
||||
errors = append(errors,
|
||||
fmt.Errorf("Post-processor %d.%d: missing 'type'", i+1, j+1))
|
||||
errors = append(errors, fmt.Errorf("Post-processor %d.%d: missing 'type'", i+1, j+1))
|
||||
continue
|
||||
}
|
||||
|
||||
// Remove the input keep_input_artifact option
|
||||
config.TemplateOnlyExcept.Prune(pp)
|
||||
delete(pp, "keep_input_artifact")
|
||||
|
||||
// Verify that the only settings are good
|
||||
if errs := config.TemplateOnlyExcept.Validate(t.Builders); len(errs) > 0 {
|
||||
for _, err := range errs {
|
||||
errors = append(errors,
|
||||
fmt.Errorf("Post-processor %d.%d: %s", i+1, j+1, err))
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
config.RawConfig = pp
|
||||
|
||||
// Add it to the list of configs
|
||||
configs = append(configs, config)
|
||||
}
|
||||
|
||||
t.PostProcessors[i] = configs
|
||||
}
|
||||
|
||||
// Gather all the provisioners
|
||||
@@ -266,8 +237,9 @@ func ParseTemplate(data []byte) (t *Template, err error) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Delete the keys that we used
|
||||
raw.TemplateOnlyExcept.Prune(v)
|
||||
// The provisioners not only don't need or want the override settings
|
||||
// (as they are processed as part of the preparation below), but will
|
||||
// actively reject them as invalid configuration.
|
||||
delete(v, "override")
|
||||
|
||||
// Verify that the override keys exist...
|
||||
@@ -278,14 +250,6 @@ func ParseTemplate(data []byte) (t *Template, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
// Verify that the only settings are good
|
||||
if errs := raw.TemplateOnlyExcept.Validate(t.Builders); len(errs) > 0 {
|
||||
for _, err := range errs {
|
||||
errors = append(errors,
|
||||
fmt.Errorf("provisioner %d: %s", i+1, err))
|
||||
}
|
||||
}
|
||||
|
||||
raw.RawConfig = v
|
||||
}
|
||||
|
||||
@@ -436,12 +400,8 @@ func (t *Template) Build(name string, components *ComponentFinder) (b Build, err
|
||||
// Prepare the post-processors
|
||||
postProcessors := make([][]coreBuildPostProcessor, 0, len(t.PostProcessors))
|
||||
for _, rawPPs := range t.PostProcessors {
|
||||
current := make([]coreBuildPostProcessor, 0, len(rawPPs))
|
||||
for _, rawPP := range rawPPs {
|
||||
if rawPP.TemplateOnlyExcept.Skip(name) {
|
||||
continue
|
||||
}
|
||||
|
||||
current := make([]coreBuildPostProcessor, len(rawPPs))
|
||||
for i, rawPP := range rawPPs {
|
||||
pp, err := components.PostProcessor(rawPP.Type)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -451,18 +411,12 @@ func (t *Template) Build(name string, components *ComponentFinder) (b Build, err
|
||||
return nil, fmt.Errorf("PostProcessor type not found: %s", rawPP.Type)
|
||||
}
|
||||
|
||||
current = append(current, coreBuildPostProcessor{
|
||||
current[i] = coreBuildPostProcessor{
|
||||
processor: pp,
|
||||
processorType: rawPP.Type,
|
||||
config: rawPP.RawConfig,
|
||||
keepInputArtifact: rawPP.KeepInputArtifact,
|
||||
})
|
||||
}
|
||||
|
||||
// If we have no post-processors in this chain, just continue.
|
||||
// This can happen if the post-processors skip certain builds.
|
||||
if len(current) == 0 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
postProcessors = append(postProcessors, current)
|
||||
@@ -471,10 +425,6 @@ func (t *Template) Build(name string, components *ComponentFinder) (b Build, err
|
||||
// Prepare the provisioners
|
||||
provisioners := make([]coreBuildProvisioner, 0, len(t.Provisioners))
|
||||
for _, rawProvisioner := range t.Provisioners {
|
||||
if rawProvisioner.TemplateOnlyExcept.Skip(name) {
|
||||
continue
|
||||
}
|
||||
|
||||
var provisioner Provisioner
|
||||
provisioner, err = components.Provisioner(rawProvisioner.Type)
|
||||
if err != nil {
|
||||
@@ -521,69 +471,3 @@ func (t *Template) Build(name string, components *ComponentFinder) (b Build, err
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// TemplateOnlyExcept contains the logic required for "only" and "except"
|
||||
// meta-parameters.
|
||||
type TemplateOnlyExcept struct {
|
||||
Only []string
|
||||
Except []string
|
||||
}
|
||||
|
||||
// Prune will prune out the used values from the raw map.
|
||||
func (t *TemplateOnlyExcept) Prune(raw map[string]interface{}) {
|
||||
delete(raw, "except")
|
||||
delete(raw, "only")
|
||||
}
|
||||
|
||||
// Skip tests if we should skip putting this item onto a build.
|
||||
func (t *TemplateOnlyExcept) Skip(name string) bool {
|
||||
if len(t.Only) > 0 {
|
||||
onlyFound := false
|
||||
for _, n := range t.Only {
|
||||
if n == name {
|
||||
onlyFound = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !onlyFound {
|
||||
// Skip this provisioner
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// If the name is in the except list, then skip that
|
||||
for _, n := range t.Except {
|
||||
if n == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Validates the only/except parameters.
|
||||
func (t *TemplateOnlyExcept) Validate(b map[string]RawBuilderConfig) (e []error) {
|
||||
if len(t.Only) > 0 && len(t.Except) > 0 {
|
||||
e = append(e,
|
||||
fmt.Errorf("Only one of 'only' or 'except' may be specified."))
|
||||
}
|
||||
|
||||
if len(t.Only) > 0 {
|
||||
for _, n := range t.Only {
|
||||
if _, ok := b[n]; !ok {
|
||||
e = append(e,
|
||||
fmt.Errorf("'only' specified builder '%s' not found", n))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, n := range t.Except {
|
||||
if _, ok := b[n]; !ok {
|
||||
e = append(e,
|
||||
fmt.Errorf("'except' specified builder '%s' not found", n))
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
+107
-604
@@ -1,6 +1,7 @@
|
||||
package packer
|
||||
|
||||
import (
|
||||
"cgl.tideland.biz/asserts"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
@@ -8,33 +9,6 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func testTemplateComponentFinder() *ComponentFinder {
|
||||
builder := testBuilder()
|
||||
pp := new(TestPostProcessor)
|
||||
provisioner := &MockProvisioner{}
|
||||
|
||||
builderMap := map[string]Builder{
|
||||
"test-builder": builder,
|
||||
}
|
||||
|
||||
ppMap := map[string]PostProcessor{
|
||||
"test-pp": pp,
|
||||
}
|
||||
|
||||
provisionerMap := map[string]Provisioner{
|
||||
"test-prov": provisioner,
|
||||
}
|
||||
|
||||
builderFactory := func(n string) (Builder, error) { return builderMap[n], nil }
|
||||
ppFactory := func(n string) (PostProcessor, error) { return ppMap[n], nil }
|
||||
provFactory := func(n string) (Provisioner, error) { return provisionerMap[n], nil }
|
||||
return &ComponentFinder{
|
||||
Builder: builderFactory,
|
||||
PostProcessor: ppFactory,
|
||||
Provisioner: provFactory,
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseTemplateFile_basic(t *testing.T) {
|
||||
data := `
|
||||
{
|
||||
@@ -93,6 +67,8 @@ func TestParseTemplateFile_stdin(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestParseTemplate_Basic(t *testing.T) {
|
||||
assert := asserts.NewTestingAsserts(t, true)
|
||||
|
||||
data := `
|
||||
{
|
||||
"builders": [{"type": "something"}]
|
||||
@@ -100,18 +76,14 @@ func TestParseTemplate_Basic(t *testing.T) {
|
||||
`
|
||||
|
||||
result, err := ParseTemplate([]byte(data))
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
if result == nil {
|
||||
t.Fatal("should have result")
|
||||
}
|
||||
if len(result.Builders) != 1 {
|
||||
t.Fatalf("bad: %#v", result.Builders)
|
||||
}
|
||||
assert.Nil(err, "should not error")
|
||||
assert.NotNil(result, "template should not be nil")
|
||||
assert.Length(result.Builders, 1, "one builder")
|
||||
}
|
||||
|
||||
func TestParseTemplate_Invalid(t *testing.T) {
|
||||
assert := asserts.NewTestingAsserts(t, true)
|
||||
|
||||
// Note there is an extra comma below for a purposeful
|
||||
// syntax error in the JSON.
|
||||
data := `
|
||||
@@ -121,15 +93,13 @@ func TestParseTemplate_Invalid(t *testing.T) {
|
||||
`
|
||||
|
||||
result, err := ParseTemplate([]byte(data))
|
||||
if err == nil {
|
||||
t.Fatal("shold have error")
|
||||
}
|
||||
if result != nil {
|
||||
t.Fatal("should not have result")
|
||||
}
|
||||
assert.NotNil(err, "should have an error")
|
||||
assert.Nil(result, "should have no result")
|
||||
}
|
||||
|
||||
func TestParseTemplate_InvalidKeys(t *testing.T) {
|
||||
assert := asserts.NewTestingAsserts(t, true)
|
||||
|
||||
// Note there is an extra comma below for a purposeful
|
||||
// syntax error in the JSON.
|
||||
data := `
|
||||
@@ -140,15 +110,13 @@ func TestParseTemplate_InvalidKeys(t *testing.T) {
|
||||
`
|
||||
|
||||
result, err := ParseTemplate([]byte(data))
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
if result != nil {
|
||||
t.Fatal("should not have result")
|
||||
}
|
||||
assert.NotNil(err, "should have an error")
|
||||
assert.Nil(result, "should have no result")
|
||||
}
|
||||
|
||||
func TestParseTemplate_BuilderWithoutType(t *testing.T) {
|
||||
assert := asserts.NewTestingAsserts(t, true)
|
||||
|
||||
data := `
|
||||
{
|
||||
"builders": [{}]
|
||||
@@ -156,12 +124,12 @@ func TestParseTemplate_BuilderWithoutType(t *testing.T) {
|
||||
`
|
||||
|
||||
_, err := ParseTemplate([]byte(data))
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
assert.NotNil(err, "should have error")
|
||||
}
|
||||
|
||||
func TestParseTemplate_BuilderWithNonStringType(t *testing.T) {
|
||||
assert := asserts.NewTestingAsserts(t, true)
|
||||
|
||||
data := `
|
||||
{
|
||||
"builders": [{
|
||||
@@ -171,12 +139,12 @@ func TestParseTemplate_BuilderWithNonStringType(t *testing.T) {
|
||||
`
|
||||
|
||||
_, err := ParseTemplate([]byte(data))
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
assert.NotNil(err, "should have error")
|
||||
}
|
||||
|
||||
func TestParseTemplate_BuilderWithoutName(t *testing.T) {
|
||||
assert := asserts.NewTestingAsserts(t, true)
|
||||
|
||||
data := `
|
||||
{
|
||||
"builders": [
|
||||
@@ -188,26 +156,18 @@ func TestParseTemplate_BuilderWithoutName(t *testing.T) {
|
||||
`
|
||||
|
||||
result, err := ParseTemplate([]byte(data))
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
if result == nil {
|
||||
t.Fatal("should have result")
|
||||
}
|
||||
if len(result.Builders) != 1 {
|
||||
t.Fatalf("bad: %#v", result.Builders)
|
||||
}
|
||||
assert.Nil(err, "should not error")
|
||||
assert.NotNil(result, "template should not be nil")
|
||||
assert.Length(result.Builders, 1, "should have one builder")
|
||||
|
||||
builder, ok := result.Builders["amazon-ebs"]
|
||||
if !ok {
|
||||
t.Fatal("should be ok")
|
||||
}
|
||||
if builder.Type != "amazon-ebs" {
|
||||
t.Fatalf("bad: %#v", builder.Type)
|
||||
}
|
||||
assert.True(ok, "should have amazon-ebs builder")
|
||||
assert.Equal(builder.Type, "amazon-ebs", "builder should be amazon-ebs")
|
||||
}
|
||||
|
||||
func TestParseTemplate_BuilderWithName(t *testing.T) {
|
||||
assert := asserts.NewTestingAsserts(t, true)
|
||||
|
||||
data := `
|
||||
{
|
||||
"builders": [
|
||||
@@ -220,23 +180,13 @@ func TestParseTemplate_BuilderWithName(t *testing.T) {
|
||||
`
|
||||
|
||||
result, err := ParseTemplate([]byte(data))
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
if result == nil {
|
||||
t.Fatal("should have result")
|
||||
}
|
||||
if len(result.Builders) != 1 {
|
||||
t.Fatalf("bad: %#v", result.Builders)
|
||||
}
|
||||
assert.Nil(err, "should not error")
|
||||
assert.NotNil(result, "template should not be nil")
|
||||
assert.Length(result.Builders, 1, "should have one builder")
|
||||
|
||||
builder, ok := result.Builders["bob"]
|
||||
if !ok {
|
||||
t.Fatal("should be ok")
|
||||
}
|
||||
if builder.Type != "amazon-ebs" {
|
||||
t.Fatalf("bad: %#v", builder.Type)
|
||||
}
|
||||
assert.True(ok, "should have bob builder")
|
||||
assert.Equal(builder.Type, "amazon-ebs", "builder should be amazon-ebs")
|
||||
|
||||
RawConfig := builder.RawConfig
|
||||
if RawConfig == nil {
|
||||
@@ -253,6 +203,8 @@ func TestParseTemplate_BuilderWithName(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestParseTemplate_BuilderWithConflictingName(t *testing.T) {
|
||||
assert := asserts.NewTestingAsserts(t, true)
|
||||
|
||||
data := `
|
||||
{
|
||||
"builders": [
|
||||
@@ -269,12 +221,12 @@ func TestParseTemplate_BuilderWithConflictingName(t *testing.T) {
|
||||
`
|
||||
|
||||
_, err := ParseTemplate([]byte(data))
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
assert.NotNil(err, "should have error")
|
||||
}
|
||||
|
||||
func TestParseTemplate_Hooks(t *testing.T) {
|
||||
assert := asserts.NewTestingAsserts(t, true)
|
||||
|
||||
data := `
|
||||
{
|
||||
|
||||
@@ -287,23 +239,13 @@ func TestParseTemplate_Hooks(t *testing.T) {
|
||||
`
|
||||
|
||||
result, err := ParseTemplate([]byte(data))
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
if result == nil {
|
||||
t.Fatal("should have result")
|
||||
}
|
||||
if len(result.Hooks) != 1 {
|
||||
t.Fatalf("bad: %#v", result.Hooks)
|
||||
}
|
||||
assert.Nil(err, "should not error")
|
||||
assert.NotNil(result, "template should not be nil")
|
||||
assert.Length(result.Hooks, 1, "should have one hook")
|
||||
|
||||
hooks, ok := result.Hooks["event"]
|
||||
if !ok {
|
||||
t.Fatal("should be okay")
|
||||
}
|
||||
if !reflect.DeepEqual(hooks, []string{"foo", "bar"}) {
|
||||
t.Fatalf("bad: %#v", hooks)
|
||||
}
|
||||
assert.True(ok, "should have hook")
|
||||
assert.Equal(hooks, []string{"foo", "bar"}, "hooks should be correct")
|
||||
}
|
||||
|
||||
func TestParseTemplate_PostProcessors(t *testing.T) {
|
||||
@@ -363,6 +305,8 @@ func TestParseTemplate_PostProcessors(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestParseTemplate_ProvisionerWithoutType(t *testing.T) {
|
||||
assert := asserts.NewTestingAsserts(t, true)
|
||||
|
||||
data := `
|
||||
{
|
||||
"builders": [{"type": "foo"}],
|
||||
@@ -372,12 +316,12 @@ func TestParseTemplate_ProvisionerWithoutType(t *testing.T) {
|
||||
`
|
||||
|
||||
_, err := ParseTemplate([]byte(data))
|
||||
if err == nil {
|
||||
t.Fatal("err should not be nil")
|
||||
}
|
||||
assert.NotNil(err, "should have error")
|
||||
}
|
||||
|
||||
func TestParseTemplate_ProvisionerWithNonStringType(t *testing.T) {
|
||||
assert := asserts.NewTestingAsserts(t, true)
|
||||
|
||||
data := `
|
||||
{
|
||||
"builders": [{"type": "foo"}],
|
||||
@@ -389,12 +333,12 @@ func TestParseTemplate_ProvisionerWithNonStringType(t *testing.T) {
|
||||
`
|
||||
|
||||
_, err := ParseTemplate([]byte(data))
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
assert.NotNil(err, "should have error")
|
||||
}
|
||||
|
||||
func TestParseTemplate_Provisioners(t *testing.T) {
|
||||
assert := asserts.NewTestingAsserts(t, true)
|
||||
|
||||
data := `
|
||||
{
|
||||
"builders": [{"type": "foo"}],
|
||||
@@ -408,21 +352,11 @@ func TestParseTemplate_Provisioners(t *testing.T) {
|
||||
`
|
||||
|
||||
result, err := ParseTemplate([]byte(data))
|
||||
if err != nil {
|
||||
t.Fatal("err: %s", err)
|
||||
}
|
||||
if result == nil {
|
||||
t.Fatal("should have result")
|
||||
}
|
||||
if len(result.Provisioners) != 1 {
|
||||
t.Fatalf("bad: %#v", result.Provisioners)
|
||||
}
|
||||
if result.Provisioners[0].Type != "shell" {
|
||||
t.Fatalf("bad: %#v", result.Provisioners[0].Type)
|
||||
}
|
||||
if result.Provisioners[0].RawConfig == nil {
|
||||
t.Fatal("should have raw config")
|
||||
}
|
||||
assert.Nil(err, "should not error")
|
||||
assert.NotNil(result, "template should not be nil")
|
||||
assert.Length(result.Provisioners, 1, "should have one provisioner")
|
||||
assert.Equal(result.Provisioners[0].Type, "shell", "provisioner should be shell")
|
||||
assert.NotNil(result.Provisioners[0].RawConfig, "should have raw config")
|
||||
}
|
||||
|
||||
func TestParseTemplate_Variables(t *testing.T) {
|
||||
@@ -430,8 +364,7 @@ func TestParseTemplate_Variables(t *testing.T) {
|
||||
{
|
||||
"variables": {
|
||||
"foo": "bar",
|
||||
"bar": null,
|
||||
"baz": 27
|
||||
"bar": null
|
||||
},
|
||||
|
||||
"builders": [{"type": "something"}]
|
||||
@@ -443,7 +376,7 @@ func TestParseTemplate_Variables(t *testing.T) {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if result.Variables == nil || len(result.Variables) != 3 {
|
||||
if result.Variables == nil || len(result.Variables) != 2 {
|
||||
t.Fatalf("bad vars: %#v", result.Variables)
|
||||
}
|
||||
|
||||
@@ -462,14 +395,6 @@ func TestParseTemplate_Variables(t *testing.T) {
|
||||
if !result.Variables["bar"].Required {
|
||||
t.Fatal("bar should be required")
|
||||
}
|
||||
|
||||
if result.Variables["baz"].Default != "27" {
|
||||
t.Fatal("default should be empty")
|
||||
}
|
||||
|
||||
if result.Variables["baz"].Required {
|
||||
t.Fatal("baz should not be required")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseTemplate_variablesBadDefault(t *testing.T) {
|
||||
@@ -490,6 +415,8 @@ func TestParseTemplate_variablesBadDefault(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTemplate_BuildNames(t *testing.T) {
|
||||
assert := asserts.NewTestingAsserts(t, true)
|
||||
|
||||
data := `
|
||||
{
|
||||
"builders": [
|
||||
@@ -506,18 +433,16 @@ func TestTemplate_BuildNames(t *testing.T) {
|
||||
`
|
||||
|
||||
result, err := ParseTemplate([]byte(data))
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
assert.Nil(err, "should not error")
|
||||
|
||||
buildNames := result.BuildNames()
|
||||
sort.Strings(buildNames)
|
||||
if !reflect.DeepEqual(buildNames, []string{"bob", "chris"}) {
|
||||
t.Fatalf("bad: %#v", buildNames)
|
||||
}
|
||||
assert.Equal(buildNames, []string{"bob", "chris"}, "should have proper builds")
|
||||
}
|
||||
|
||||
func TestTemplate_BuildUnknown(t *testing.T) {
|
||||
assert := asserts.NewTestingAsserts(t, true)
|
||||
|
||||
data := `
|
||||
{
|
||||
"builders": [
|
||||
@@ -530,20 +455,16 @@ func TestTemplate_BuildUnknown(t *testing.T) {
|
||||
`
|
||||
|
||||
template, err := ParseTemplate([]byte(data))
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
}
|
||||
assert.Nil(err, "should not error")
|
||||
|
||||
build, err := template.Build("nope", nil)
|
||||
if build != nil {
|
||||
t.Fatalf("build should be nil: %#v", build)
|
||||
}
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
assert.Nil(build, "build should be nil")
|
||||
assert.NotNil(err, "should have error")
|
||||
}
|
||||
|
||||
func TestTemplate_BuildUnknownBuilder(t *testing.T) {
|
||||
assert := asserts.NewTestingAsserts(t, true)
|
||||
|
||||
data := `
|
||||
{
|
||||
"builders": [
|
||||
@@ -556,22 +477,18 @@ func TestTemplate_BuildUnknownBuilder(t *testing.T) {
|
||||
`
|
||||
|
||||
template, err := ParseTemplate([]byte(data))
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
assert.Nil(err, "should not error")
|
||||
|
||||
builderFactory := func(string) (Builder, error) { return nil, nil }
|
||||
components := &ComponentFinder{Builder: builderFactory}
|
||||
build, err := template.Build("test1", components)
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
if build != nil {
|
||||
t.Fatalf("bad: %#v", build)
|
||||
}
|
||||
assert.Nil(build, "build should be nil")
|
||||
assert.NotNil(err, "should have error")
|
||||
}
|
||||
|
||||
func TestTemplate_Build_NilBuilderFunc(t *testing.T) {
|
||||
assert := asserts.NewTestingAsserts(t, true)
|
||||
|
||||
data := `
|
||||
{
|
||||
"builders": [
|
||||
@@ -590,18 +507,14 @@ func TestTemplate_Build_NilBuilderFunc(t *testing.T) {
|
||||
`
|
||||
|
||||
template, err := ParseTemplate([]byte(data))
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
assert.Nil(err, "should not error")
|
||||
|
||||
defer func() {
|
||||
p := recover()
|
||||
if p == nil {
|
||||
t.Fatal("should panic")
|
||||
}
|
||||
assert.NotNil(p, "should panic")
|
||||
|
||||
if p.(string) != "no builder function" {
|
||||
t.Fatalf("bad panic: %s", p.(string))
|
||||
if p != nil {
|
||||
assert.Equal(p.(string), "no builder function", "right panic")
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -609,6 +522,8 @@ func TestTemplate_Build_NilBuilderFunc(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTemplate_Build_NilProvisionerFunc(t *testing.T) {
|
||||
assert := asserts.NewTestingAsserts(t, true)
|
||||
|
||||
data := `
|
||||
{
|
||||
"builders": [
|
||||
@@ -627,18 +542,14 @@ func TestTemplate_Build_NilProvisionerFunc(t *testing.T) {
|
||||
`
|
||||
|
||||
template, err := ParseTemplate([]byte(data))
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
assert.Nil(err, "should not error")
|
||||
|
||||
defer func() {
|
||||
p := recover()
|
||||
if p == nil {
|
||||
t.Fatal("should panic")
|
||||
}
|
||||
assert.NotNil(p, "should panic")
|
||||
|
||||
if p.(string) != "no provisioner function" {
|
||||
t.Fatalf("bad panic: %s", p.(string))
|
||||
if p != nil {
|
||||
assert.Equal(p.(string), "no provisioner function", "right panic")
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -648,6 +559,8 @@ func TestTemplate_Build_NilProvisionerFunc(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTemplate_Build_NilProvisionerFunc_WithNoProvisioners(t *testing.T) {
|
||||
assert := asserts.NewTestingAsserts(t, true)
|
||||
|
||||
data := `
|
||||
{
|
||||
"builders": [
|
||||
@@ -662,9 +575,7 @@ func TestTemplate_Build_NilProvisionerFunc_WithNoProvisioners(t *testing.T) {
|
||||
`
|
||||
|
||||
template, err := ParseTemplate([]byte(data))
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
assert.Nil(err, "should not error")
|
||||
|
||||
template.Build("test1", &ComponentFinder{
|
||||
Builder: func(string) (Builder, error) { return nil, nil },
|
||||
@@ -672,6 +583,8 @@ func TestTemplate_Build_NilProvisionerFunc_WithNoProvisioners(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTemplate_Build(t *testing.T) {
|
||||
assert := asserts.NewTestingAsserts(t, true)
|
||||
|
||||
data := `
|
||||
{
|
||||
"builders": [
|
||||
@@ -702,9 +615,7 @@ func TestTemplate_Build(t *testing.T) {
|
||||
}
|
||||
|
||||
template, err := ParseTemplate([]byte(data))
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
assert.Nil(err, "should not error")
|
||||
|
||||
builder := testBuilder()
|
||||
builderMap := map[string]Builder{
|
||||
@@ -733,40 +644,18 @@ func TestTemplate_Build(t *testing.T) {
|
||||
// Get the build, verifying we can get it without issue, but also
|
||||
// that the proper builder was looked up and used for the build.
|
||||
build, err := template.Build("test1", components)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
assert.Nil(err, "should not error")
|
||||
|
||||
coreBuild, ok := build.(*coreBuild)
|
||||
if !ok {
|
||||
t.Fatal("should be ok")
|
||||
}
|
||||
if coreBuild.builder != builder {
|
||||
t.Fatalf("bad: %#v", coreBuild.builder)
|
||||
}
|
||||
if !reflect.DeepEqual(coreBuild.builderConfig, expectedConfig) {
|
||||
t.Fatalf("bad: %#v", coreBuild.builderConfig)
|
||||
}
|
||||
if len(coreBuild.provisioners) != 1 {
|
||||
t.Fatalf("bad: %#v", coreBuild.provisioners)
|
||||
}
|
||||
if len(coreBuild.postProcessors) != 2 {
|
||||
t.Fatalf("bad: %#v", coreBuild.postProcessors)
|
||||
}
|
||||
|
||||
if len(coreBuild.postProcessors[0]) != 1 {
|
||||
t.Fatalf("bad: %#v", coreBuild.postProcessors[0])
|
||||
}
|
||||
if len(coreBuild.postProcessors[1]) != 2 {
|
||||
t.Fatalf("bad: %#v", coreBuild.postProcessors[1])
|
||||
}
|
||||
|
||||
if coreBuild.postProcessors[1][0].keepInputArtifact {
|
||||
t.Fatal("postProcessors[1][0] should not keep input artifact")
|
||||
}
|
||||
if !coreBuild.postProcessors[1][1].keepInputArtifact {
|
||||
t.Fatal("postProcessors[1][1] should keep input artifact")
|
||||
}
|
||||
assert.True(ok, "should be a core build")
|
||||
assert.Equal(coreBuild.builder, builder, "should have the same builder")
|
||||
assert.Equal(coreBuild.builderConfig, expectedConfig, "should have proper config")
|
||||
assert.Equal(len(coreBuild.provisioners), 1, "should have one provisioner")
|
||||
assert.Equal(len(coreBuild.postProcessors), 2, "should have pps")
|
||||
assert.Equal(len(coreBuild.postProcessors[0]), 1, "should have correct number")
|
||||
assert.Equal(len(coreBuild.postProcessors[1]), 2, "should have correct number")
|
||||
assert.False(coreBuild.postProcessors[1][0].keepInputArtifact, "shoule be correct")
|
||||
assert.True(coreBuild.postProcessors[1][1].keepInputArtifact, "shoule be correct")
|
||||
|
||||
config := coreBuild.postProcessors[1][1].config
|
||||
if _, ok := config["keep_input_artifact"]; ok {
|
||||
@@ -774,387 +663,9 @@ func TestTemplate_Build(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTemplateBuild_exceptOnlyPP(t *testing.T) {
|
||||
data := `
|
||||
{
|
||||
"builders": [
|
||||
{
|
||||
"name": "test1",
|
||||
"type": "test-builder"
|
||||
},
|
||||
{
|
||||
"name": "test2",
|
||||
"type": "test-builder"
|
||||
}
|
||||
],
|
||||
|
||||
"post-processors": [
|
||||
{
|
||||
"type": "test-pp",
|
||||
"except": ["test1"],
|
||||
"only": ["test1"]
|
||||
}
|
||||
]
|
||||
}
|
||||
`
|
||||
|
||||
_, err := ParseTemplate([]byte(data))
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTemplateBuild_exceptOnlyProv(t *testing.T) {
|
||||
data := `
|
||||
{
|
||||
"builders": [
|
||||
{
|
||||
"name": "test1",
|
||||
"type": "test-builder"
|
||||
},
|
||||
{
|
||||
"name": "test2",
|
||||
"type": "test-builder"
|
||||
}
|
||||
],
|
||||
|
||||
"provisioners": [
|
||||
{
|
||||
"type": "test-prov",
|
||||
"except": ["test1"],
|
||||
"only": ["test1"]
|
||||
}
|
||||
]
|
||||
}
|
||||
`
|
||||
|
||||
_, err := ParseTemplate([]byte(data))
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTemplateBuild_exceptPPInvalid(t *testing.T) {
|
||||
data := `
|
||||
{
|
||||
"builders": [
|
||||
{
|
||||
"name": "test1",
|
||||
"type": "test-builder"
|
||||
},
|
||||
{
|
||||
"name": "test2",
|
||||
"type": "test-builder"
|
||||
}
|
||||
],
|
||||
|
||||
"post-processors": [
|
||||
{
|
||||
"type": "test-pp",
|
||||
"except": ["test5"]
|
||||
}
|
||||
]
|
||||
}
|
||||
`
|
||||
|
||||
_, err := ParseTemplate([]byte(data))
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTemplateBuild_exceptPP(t *testing.T) {
|
||||
data := `
|
||||
{
|
||||
"builders": [
|
||||
{
|
||||
"name": "test1",
|
||||
"type": "test-builder"
|
||||
},
|
||||
{
|
||||
"name": "test2",
|
||||
"type": "test-builder"
|
||||
}
|
||||
],
|
||||
|
||||
"post-processors": [
|
||||
{
|
||||
"type": "test-pp",
|
||||
"except": ["test1"]
|
||||
}
|
||||
]
|
||||
}
|
||||
`
|
||||
|
||||
template, err := ParseTemplate([]byte(data))
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
// Verify test1 has no post-processors
|
||||
build, err := template.Build("test1", testTemplateComponentFinder())
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
cbuild := build.(*coreBuild)
|
||||
if len(cbuild.postProcessors) > 0 {
|
||||
t.Fatal("should have no postProcessors")
|
||||
}
|
||||
|
||||
// Verify test2 has no post-processors
|
||||
build, err = template.Build("test2", testTemplateComponentFinder())
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
cbuild = build.(*coreBuild)
|
||||
if len(cbuild.postProcessors) != 1 {
|
||||
t.Fatalf("invalid: %d", len(cbuild.postProcessors))
|
||||
}
|
||||
}
|
||||
|
||||
func TestTemplateBuild_exceptProvInvalid(t *testing.T) {
|
||||
data := `
|
||||
{
|
||||
"builders": [
|
||||
{
|
||||
"name": "test1",
|
||||
"type": "test-builder"
|
||||
},
|
||||
{
|
||||
"name": "test2",
|
||||
"type": "test-builder"
|
||||
}
|
||||
],
|
||||
|
||||
"provisioners": [
|
||||
{
|
||||
"type": "test-prov",
|
||||
"except": ["test5"]
|
||||
}
|
||||
]
|
||||
}
|
||||
`
|
||||
|
||||
_, err := ParseTemplate([]byte(data))
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTemplateBuild_exceptProv(t *testing.T) {
|
||||
data := `
|
||||
{
|
||||
"builders": [
|
||||
{
|
||||
"name": "test1",
|
||||
"type": "test-builder"
|
||||
},
|
||||
{
|
||||
"name": "test2",
|
||||
"type": "test-builder"
|
||||
}
|
||||
],
|
||||
|
||||
"provisioners": [
|
||||
{
|
||||
"type": "test-prov",
|
||||
"except": ["test1"]
|
||||
}
|
||||
]
|
||||
}
|
||||
`
|
||||
|
||||
template, err := ParseTemplate([]byte(data))
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
// Verify test1 has no provisioners
|
||||
build, err := template.Build("test1", testTemplateComponentFinder())
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
cbuild := build.(*coreBuild)
|
||||
if len(cbuild.provisioners) > 0 {
|
||||
t.Fatal("should have no provisioners")
|
||||
}
|
||||
|
||||
// Verify test2 has no provisioners
|
||||
build, err = template.Build("test2", testTemplateComponentFinder())
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
cbuild = build.(*coreBuild)
|
||||
if len(cbuild.provisioners) != 1 {
|
||||
t.Fatalf("invalid: %d", len(cbuild.provisioners))
|
||||
}
|
||||
}
|
||||
|
||||
func TestTemplateBuild_onlyPPInvalid(t *testing.T) {
|
||||
data := `
|
||||
{
|
||||
"builders": [
|
||||
{
|
||||
"name": "test1",
|
||||
"type": "test-builder"
|
||||
},
|
||||
{
|
||||
"name": "test2",
|
||||
"type": "test-builder"
|
||||
}
|
||||
],
|
||||
|
||||
"post-processors": [
|
||||
{
|
||||
"type": "test-pp",
|
||||
"only": ["test5"]
|
||||
}
|
||||
]
|
||||
}
|
||||
`
|
||||
|
||||
_, err := ParseTemplate([]byte(data))
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTemplateBuild_onlyPP(t *testing.T) {
|
||||
data := `
|
||||
{
|
||||
"builders": [
|
||||
{
|
||||
"name": "test1",
|
||||
"type": "test-builder"
|
||||
},
|
||||
{
|
||||
"name": "test2",
|
||||
"type": "test-builder"
|
||||
}
|
||||
],
|
||||
|
||||
"post-processors": [
|
||||
{
|
||||
"type": "test-pp",
|
||||
"only": ["test2"]
|
||||
}
|
||||
]
|
||||
}
|
||||
`
|
||||
|
||||
template, err := ParseTemplate([]byte(data))
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
// Verify test1 has no post-processors
|
||||
build, err := template.Build("test1", testTemplateComponentFinder())
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
cbuild := build.(*coreBuild)
|
||||
if len(cbuild.postProcessors) > 0 {
|
||||
t.Fatal("should have no postProcessors")
|
||||
}
|
||||
|
||||
// Verify test2 has no post-processors
|
||||
build, err = template.Build("test2", testTemplateComponentFinder())
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
cbuild = build.(*coreBuild)
|
||||
if len(cbuild.postProcessors) != 1 {
|
||||
t.Fatalf("invalid: %d", len(cbuild.postProcessors))
|
||||
}
|
||||
}
|
||||
|
||||
func TestTemplateBuild_onlyProvInvalid(t *testing.T) {
|
||||
data := `
|
||||
{
|
||||
"builders": [
|
||||
{
|
||||
"name": "test1",
|
||||
"type": "test-builder"
|
||||
},
|
||||
{
|
||||
"name": "test2",
|
||||
"type": "test-builder"
|
||||
}
|
||||
],
|
||||
|
||||
"provisioners": [
|
||||
{
|
||||
"type": "test-prov",
|
||||
"only": ["test5"]
|
||||
}
|
||||
]
|
||||
}
|
||||
`
|
||||
|
||||
_, err := ParseTemplate([]byte(data))
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTemplateBuild_onlyProv(t *testing.T) {
|
||||
data := `
|
||||
{
|
||||
"builders": [
|
||||
{
|
||||
"name": "test1",
|
||||
"type": "test-builder"
|
||||
},
|
||||
{
|
||||
"name": "test2",
|
||||
"type": "test-builder"
|
||||
}
|
||||
],
|
||||
|
||||
"provisioners": [
|
||||
{
|
||||
"type": "test-prov",
|
||||
"only": ["test2"]
|
||||
}
|
||||
]
|
||||
}
|
||||
`
|
||||
|
||||
template, err := ParseTemplate([]byte(data))
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
// Verify test1 has no provisioners
|
||||
build, err := template.Build("test1", testTemplateComponentFinder())
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
cbuild := build.(*coreBuild)
|
||||
if len(cbuild.provisioners) > 0 {
|
||||
t.Fatal("should have no provisioners")
|
||||
}
|
||||
|
||||
// Verify test2 has no provisioners
|
||||
build, err = template.Build("test2", testTemplateComponentFinder())
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
cbuild = build.(*coreBuild)
|
||||
if len(cbuild.provisioners) != 1 {
|
||||
t.Fatalf("invalid: %d", len(cbuild.provisioners))
|
||||
}
|
||||
}
|
||||
|
||||
func TestTemplate_Build_ProvisionerOverride(t *testing.T) {
|
||||
assert := asserts.NewTestingAsserts(t, true)
|
||||
|
||||
data := `
|
||||
{
|
||||
"builders": [
|
||||
@@ -1214,20 +725,12 @@ func TestTemplate_Build_ProvisionerOverride(t *testing.T) {
|
||||
// Get the build, verifying we can get it without issue, but also
|
||||
// that the proper builder was looked up and used for the build.
|
||||
build, err := template.Build("test1", components)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
assert.Nil(err, "should not error")
|
||||
|
||||
coreBuild, ok := build.(*coreBuild)
|
||||
if !ok {
|
||||
t.Fatal("should be okay")
|
||||
}
|
||||
if len(coreBuild.provisioners) != 1 {
|
||||
t.Fatalf("bad: %#v", coreBuild.provisioners)
|
||||
}
|
||||
if len(coreBuild.provisioners[0].config) != 2 {
|
||||
t.Fatalf("bad: %#v", coreBuild.provisioners[0].config)
|
||||
}
|
||||
assert.True(ok, "should be a core build")
|
||||
assert.Equal(len(coreBuild.provisioners), 1, "should have one provisioner")
|
||||
assert.Equal(len(coreBuild.provisioners[0].config), 2, "should have two configs on the provisioner")
|
||||
}
|
||||
|
||||
func TestTemplate_Build_ProvisionerOverrideBad(t *testing.T) {
|
||||
|
||||
+15
-44
@@ -2,6 +2,7 @@ package packer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"cgl.tideland.biz/asserts"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
@@ -37,40 +38,25 @@ func TestColoredUi(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTargettedUi(t *testing.T) {
|
||||
assert := asserts.NewTestingAsserts(t, true)
|
||||
|
||||
bufferUi := testUi()
|
||||
targettedUi := &TargettedUi{
|
||||
Target: "foo",
|
||||
Ui: bufferUi,
|
||||
}
|
||||
|
||||
var actual, expected string
|
||||
targettedUi.Say("foo")
|
||||
actual = readWriter(bufferUi)
|
||||
expected = "==> foo: foo\n"
|
||||
if actual != expected {
|
||||
t.Fatalf("bad: %#v", actual)
|
||||
}
|
||||
assert.Equal(readWriter(bufferUi), "==> foo: foo\n", "should have prefix")
|
||||
|
||||
targettedUi.Message("foo")
|
||||
actual = readWriter(bufferUi)
|
||||
expected = " foo: foo\n"
|
||||
if actual != expected {
|
||||
t.Fatalf("bad: %#v", actual)
|
||||
}
|
||||
assert.Equal(readWriter(bufferUi), " foo: foo\n", "should have prefix")
|
||||
|
||||
targettedUi.Error("bar")
|
||||
actual = readWriter(bufferUi)
|
||||
expected = "==> foo: bar\n"
|
||||
if actual != expected {
|
||||
t.Fatalf("bad: %#v", actual)
|
||||
}
|
||||
assert.Equal(readWriter(bufferUi), "==> foo: bar\n", "should have prefix")
|
||||
|
||||
targettedUi.Say("foo\nbar")
|
||||
actual = readWriter(bufferUi)
|
||||
expected = "==> foo: foo\n==> foo: bar\n"
|
||||
if actual != expected {
|
||||
t.Fatalf("bad: %#v", actual)
|
||||
}
|
||||
assert.Equal(readWriter(bufferUi), "==> foo: foo\n==> foo: bar\n", "should multiline")
|
||||
}
|
||||
|
||||
func TestColoredUi_ImplUi(t *testing.T) {
|
||||
@@ -98,42 +84,27 @@ func TestBasicUi_ImplUi(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBasicUi_Error(t *testing.T) {
|
||||
assert := asserts.NewTestingAsserts(t, true)
|
||||
|
||||
bufferUi := testUi()
|
||||
|
||||
var actual, expected string
|
||||
bufferUi.Error("foo")
|
||||
actual = readWriter(bufferUi)
|
||||
expected = "foo\n"
|
||||
if actual != expected {
|
||||
t.Fatalf("bad: %#v", actual)
|
||||
}
|
||||
assert.Equal(readWriter(bufferUi), "foo\n", "basic output")
|
||||
|
||||
bufferUi.Error("5")
|
||||
actual = readWriter(bufferUi)
|
||||
expected = "5\n"
|
||||
if actual != expected {
|
||||
t.Fatalf("bad: %#v", actual)
|
||||
}
|
||||
assert.Equal(readWriter(bufferUi), "5\n", "formatting")
|
||||
}
|
||||
|
||||
func TestBasicUi_Say(t *testing.T) {
|
||||
assert := asserts.NewTestingAsserts(t, true)
|
||||
|
||||
bufferUi := testUi()
|
||||
|
||||
var actual, expected string
|
||||
|
||||
bufferUi.Say("foo")
|
||||
actual = readWriter(bufferUi)
|
||||
expected = "foo\n"
|
||||
if actual != expected {
|
||||
t.Fatalf("bad: %#v", actual)
|
||||
}
|
||||
assert.Equal(readWriter(bufferUi), "foo\n", "basic output")
|
||||
|
||||
bufferUi.Say("5")
|
||||
actual = readWriter(bufferUi)
|
||||
expected = "5\n"
|
||||
if actual != expected {
|
||||
t.Fatalf("bad: %#v", actual)
|
||||
}
|
||||
assert.Equal(readWriter(bufferUi), "5\n", "formatting")
|
||||
}
|
||||
|
||||
func TestMachineReadableUi_ImplUi(t *testing.T) {
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ import (
|
||||
var GitCommit string
|
||||
|
||||
// The version of packer.
|
||||
const Version = "0.3.10"
|
||||
const Version = "0.3.7"
|
||||
|
||||
// Any pre-release marker for the version. If this is "" (empty string),
|
||||
// then it means that it is a final release. Otherwise, this is the
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/packer/packer/plugin"
|
||||
"github.com/mitchellh/packer/provisioner/ansible-local"
|
||||
)
|
||||
|
||||
func main() {
|
||||
plugin.ServeProvisioner(new(ansiblelocal.Provisioner))
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
package main
|
||||
@@ -1,7 +1,6 @@
|
||||
package vagrant
|
||||
|
||||
import (
|
||||
"compress/flate"
|
||||
"fmt"
|
||||
"github.com/mitchellh/packer/common"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
@@ -9,7 +8,6 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -18,7 +16,6 @@ type AWSBoxConfig struct {
|
||||
|
||||
OutputPath string `mapstructure:"output"`
|
||||
VagrantfileTemplate string `mapstructure:"vagrantfile_template"`
|
||||
CompressionLevel string `mapstructure:"compression_level"`
|
||||
|
||||
tpl *packer.ConfigTemplate
|
||||
}
|
||||
@@ -49,7 +46,6 @@ func (p *AWSBoxPostProcessor) Configure(raws ...interface{}) error {
|
||||
validates := map[string]*string{
|
||||
"output": &p.config.OutputPath,
|
||||
"vagrantfile_template": &p.config.VagrantfileTemplate,
|
||||
"compression_level": &p.config.CompressionLevel,
|
||||
}
|
||||
|
||||
for n, ptr := range validates {
|
||||
@@ -131,14 +127,6 @@ func (p *AWSBoxPostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact
|
||||
vf.Write([]byte(vagrantfileContents))
|
||||
vf.Close()
|
||||
|
||||
var level int = flate.DefaultCompression
|
||||
if p.config.CompressionLevel != "" {
|
||||
level, err = strconv.Atoi(p.config.CompressionLevel)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
}
|
||||
|
||||
// Create the metadata
|
||||
metadata := map[string]string{"provider": "aws"}
|
||||
if err := WriteMetadata(dir, metadata); err != nil {
|
||||
@@ -146,7 +134,7 @@ func (p *AWSBoxPostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact
|
||||
}
|
||||
|
||||
// Compress the directory to the given output path
|
||||
if err := DirToBox(outputPath, dir, ui, level); err != nil {
|
||||
if err := DirToBox(outputPath, dir, ui); err != nil {
|
||||
err = fmt.Errorf("error creating box: %s", err)
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
@@ -12,10 +12,9 @@ import (
|
||||
)
|
||||
|
||||
var builtins = map[string]string{
|
||||
"mitchellh.amazonebs": "aws",
|
||||
"mitchellh.amazon.instance": "aws",
|
||||
"mitchellh.virtualbox": "virtualbox",
|
||||
"mitchellh.vmware": "vmware",
|
||||
"mitchellh.amazonebs": "aws",
|
||||
"mitchellh.virtualbox": "virtualbox",
|
||||
"mitchellh.vmware": "vmware",
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
@@ -25,12 +24,15 @@ type Config struct {
|
||||
}
|
||||
|
||||
type PostProcessor struct {
|
||||
config Config
|
||||
premade map[string]packer.PostProcessor
|
||||
extraConfig map[string]interface{}
|
||||
config Config
|
||||
premade map[string]packer.PostProcessor
|
||||
rawConfigs []interface{}
|
||||
}
|
||||
|
||||
func (p *PostProcessor) Configure(raws ...interface{}) error {
|
||||
// Store the raw configs for usage later
|
||||
p.rawConfigs = raws
|
||||
|
||||
_, err := common.DecodeConfig(&p.config, raws...)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -43,8 +45,10 @@ func (p *PostProcessor) Configure(raws ...interface{}) error {
|
||||
tpl.UserVars = p.config.PackerUserVars
|
||||
|
||||
// Defaults
|
||||
ppExtraConfig := make(map[string]interface{})
|
||||
if p.config.OutputPath == "" {
|
||||
p.config.OutputPath = "packer_{{ .BuildName }}_{{.Provider}}.box"
|
||||
ppExtraConfig["output"] = p.config.OutputPath
|
||||
}
|
||||
|
||||
// Accumulate any errors
|
||||
@@ -54,18 +58,10 @@ func (p *PostProcessor) Configure(raws ...interface{}) error {
|
||||
errs, fmt.Errorf("Error parsing output template: %s", err))
|
||||
}
|
||||
|
||||
// Store extra configuration we'll send to each post-processor type
|
||||
p.extraConfig = make(map[string]interface{})
|
||||
p.extraConfig["output"] = p.config.OutputPath
|
||||
p.extraConfig["packer_build_name"] = p.config.PackerBuildName
|
||||
p.extraConfig["packer_builder_type"] = p.config.PackerBuilderType
|
||||
p.extraConfig["packer_debug"] = p.config.PackerDebug
|
||||
p.extraConfig["packer_force"] = p.config.PackerForce
|
||||
p.extraConfig["packer_user_variables"] = p.config.PackerUserVars
|
||||
// Store the extra configuration for post-processors
|
||||
p.rawConfigs = append(p.rawConfigs, ppExtraConfig)
|
||||
|
||||
// TODO(mitchellh): Properly handle multiple raw configs. This isn't
|
||||
// very pressing at the moment because at the time of this comment
|
||||
// only the first member of raws can contain the actual type-overrides.
|
||||
// TODO(mitchellh): Properly handle multiple raw configs
|
||||
var mapConfig map[string]interface{}
|
||||
if err := mapstructure.Decode(raws[0], &mapConfig); err != nil {
|
||||
errs = packer.MultiErrorAppend(errs,
|
||||
@@ -75,14 +71,18 @@ func (p *PostProcessor) Configure(raws ...interface{}) error {
|
||||
|
||||
p.premade = make(map[string]packer.PostProcessor)
|
||||
for k, raw := range mapConfig {
|
||||
pp, err := p.subPostProcessor(k, raw, p.extraConfig)
|
||||
if err != nil {
|
||||
errs = packer.MultiErrorAppend(errs, err)
|
||||
pp := keyToPostProcessor(k)
|
||||
if pp == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if pp == nil {
|
||||
continue
|
||||
// Create the proper list of configurations
|
||||
ppConfigs := make([]interface{}, 0, len(p.rawConfigs)+1)
|
||||
copy(ppConfigs, p.rawConfigs)
|
||||
ppConfigs = append(ppConfigs, raw)
|
||||
|
||||
if err := pp.Configure(ppConfigs...); err != nil {
|
||||
errs = packer.MultiErrorAppend(errs, err)
|
||||
}
|
||||
|
||||
p.premade[k] = pp
|
||||
@@ -106,37 +106,20 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac
|
||||
pp, ok := p.premade[ppName]
|
||||
if !ok {
|
||||
log.Printf("Premade post-processor for '%s' not found. Creating.", ppName)
|
||||
|
||||
var err error
|
||||
pp, err = p.subPostProcessor(ppName, nil, p.extraConfig)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
pp = keyToPostProcessor(ppName)
|
||||
if pp == nil {
|
||||
return nil, false, fmt.Errorf("Vagrant box post-processor not found: %s", ppName)
|
||||
}
|
||||
|
||||
if err := pp.Configure(p.rawConfigs...); err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf("Creating Vagrant box for '%s' provider", ppName))
|
||||
return pp.PostProcess(ui, artifact)
|
||||
}
|
||||
|
||||
func (p *PostProcessor) subPostProcessor(key string, specific interface{}, extra map[string]interface{}) (packer.PostProcessor, error) {
|
||||
pp := keyToPostProcessor(key)
|
||||
if pp == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if err := pp.Configure(extra, specific); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return pp, nil
|
||||
}
|
||||
|
||||
// keyToPostProcessor maps a configuration key to the actual post-processor
|
||||
// it will be configuring. This returns a new instance of that post-processor.
|
||||
func keyToPostProcessor(key string) packer.PostProcessor {
|
||||
switch key {
|
||||
case "aws":
|
||||
|
||||
@@ -44,7 +44,7 @@ func CopyContents(dst, src string) error {
|
||||
// DirToBox takes the directory and compresses it into a Vagrant-compatible
|
||||
// box. This function does not perform checks to verify that dir is
|
||||
// actually a proper box. This is an expected precondition.
|
||||
func DirToBox(dst, dir string, ui packer.Ui, level int) error {
|
||||
func DirToBox(dst, dir string, ui packer.Ui) error {
|
||||
log.Printf("Turning dir into box: %s => %s", dir, dst)
|
||||
dstF, err := os.Create(dst)
|
||||
if err != nil {
|
||||
@@ -52,10 +52,7 @@ func DirToBox(dst, dir string, ui packer.Ui, level int) error {
|
||||
}
|
||||
defer dstF.Close()
|
||||
|
||||
gzipWriter, err := gzip.NewWriterLevel(dstF, level)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
gzipWriter := gzip.NewWriter(dstF)
|
||||
defer gzipWriter.Close()
|
||||
|
||||
tarWriter := tar.NewWriter(gzipWriter)
|
||||
|
||||
@@ -2,7 +2,6 @@ package vagrant
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"compress/flate"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/mitchellh/packer/common"
|
||||
@@ -13,7 +12,6 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type VBoxBoxConfig struct {
|
||||
@@ -21,7 +19,6 @@ type VBoxBoxConfig struct {
|
||||
|
||||
OutputPath string `mapstructure:"output"`
|
||||
VagrantfileTemplate string `mapstructure:"vagrantfile_template"`
|
||||
CompressionLevel string `mapstructure:"compression_level"`
|
||||
|
||||
tpl *packer.ConfigTemplate
|
||||
}
|
||||
@@ -52,7 +49,6 @@ func (p *VBoxBoxPostProcessor) Configure(raws ...interface{}) error {
|
||||
validates := map[string]*string{
|
||||
"output": &p.config.OutputPath,
|
||||
"vagrantfile_template": &p.config.VagrantfileTemplate,
|
||||
"compression_level": &p.config.CompressionLevel,
|
||||
}
|
||||
|
||||
for n, ptr := range validates {
|
||||
@@ -96,7 +92,7 @@ func (p *VBoxBoxPostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifac
|
||||
// directory so we can get the resulting disk and OVF.
|
||||
if extension := filepath.Ext(path); extension == ".ova" {
|
||||
ui.Message(fmt.Sprintf("Unpacking OVA: %s", path))
|
||||
if err := DecompressOva(dir, path); err != nil {
|
||||
if err := DecompressOva(dir, filepath.Base(path)); err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
} else {
|
||||
@@ -145,14 +141,6 @@ func (p *VBoxBoxPostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifac
|
||||
vf.Write([]byte(vagrantfileContents))
|
||||
vf.Close()
|
||||
|
||||
var level int = flate.DefaultCompression
|
||||
if p.config.CompressionLevel != "" {
|
||||
level, err = strconv.Atoi(p.config.CompressionLevel)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
}
|
||||
|
||||
// Create the metadata
|
||||
metadata := map[string]string{"provider": "virtualbox"}
|
||||
if err := WriteMetadata(dir, metadata); err != nil {
|
||||
@@ -167,7 +155,7 @@ func (p *VBoxBoxPostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifac
|
||||
|
||||
// Compress the directory to the given output path
|
||||
ui.Message(fmt.Sprintf("Compressing box..."))
|
||||
if err := DirToBox(outputPath, dir, ui, level); err != nil {
|
||||
if err := DirToBox(outputPath, dir, ui); err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
package vagrant
|
||||
|
||||
import (
|
||||
"compress/flate"
|
||||
"fmt"
|
||||
"github.com/mitchellh/packer/common"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type VMwareBoxConfig struct {
|
||||
@@ -16,7 +14,6 @@ type VMwareBoxConfig struct {
|
||||
|
||||
OutputPath string `mapstructure:"output"`
|
||||
VagrantfileTemplate string `mapstructure:"vagrantfile_template"`
|
||||
CompressionLevel string `mapstructure:"compression_level"`
|
||||
|
||||
tpl *packer.ConfigTemplate
|
||||
}
|
||||
@@ -43,7 +40,6 @@ func (p *VMwareBoxPostProcessor) Configure(raws ...interface{}) error {
|
||||
validates := map[string]*string{
|
||||
"output": &p.config.OutputPath,
|
||||
"vagrantfile_template": &p.config.VagrantfileTemplate,
|
||||
"compression_level": &p.config.CompressionLevel,
|
||||
}
|
||||
|
||||
for n, ptr := range validates {
|
||||
@@ -115,14 +111,6 @@ func (p *VMwareBoxPostProcessor) PostProcess(ui packer.Ui, artifact packer.Artif
|
||||
vf.Close()
|
||||
}
|
||||
|
||||
var level int = flate.DefaultCompression
|
||||
if p.config.CompressionLevel != "" {
|
||||
level, err = strconv.Atoi(p.config.CompressionLevel)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
}
|
||||
|
||||
// Create the metadata
|
||||
metadata := map[string]string{"provider": "vmware_desktop"}
|
||||
if err := WriteMetadata(dir, metadata); err != nil {
|
||||
@@ -131,7 +119,7 @@ func (p *VMwareBoxPostProcessor) PostProcess(ui packer.Ui, artifact packer.Artif
|
||||
|
||||
// Compress the directory to the given output path
|
||||
ui.Message(fmt.Sprintf("Compressing box..."))
|
||||
if err := DirToBox(outputPath, dir, ui, level); err != nil {
|
||||
if err := DirToBox(outputPath, dir, ui); err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
|
||||
@@ -1,225 +0,0 @@
|
||||
package ansiblelocal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/mitchellh/packer/common"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
const DefaultStagingDir = "/tmp/packer-provisioner-ansible-local"
|
||||
|
||||
type Config struct {
|
||||
common.PackerConfig `mapstructure:",squash"`
|
||||
tpl *packer.ConfigTemplate
|
||||
|
||||
// The main playbook file to execute.
|
||||
PlaybookFile string `mapstructure:"playbook_file"`
|
||||
|
||||
// An array of local paths of playbook files to upload.
|
||||
PlaybookPaths []string `mapstructure:"playbook_paths"`
|
||||
|
||||
// An array of local paths of roles to upload.
|
||||
RolePaths []string `mapstructure:"role_paths"`
|
||||
|
||||
// The directory where files will be uploaded. Packer requires write
|
||||
// permissions in this directory.
|
||||
StagingDir string `mapstructure:"staging_directory"`
|
||||
}
|
||||
|
||||
type Provisioner struct {
|
||||
config Config
|
||||
}
|
||||
|
||||
func (p *Provisioner) Prepare(raws ...interface{}) error {
|
||||
md, err := common.DecodeConfig(&p.config, raws...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.config.tpl, err = packer.NewConfigTemplate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.config.tpl.UserVars = p.config.PackerUserVars
|
||||
|
||||
// Accumulate any errors
|
||||
errs := common.CheckUnusedConfig(md)
|
||||
|
||||
if p.config.StagingDir == "" {
|
||||
p.config.StagingDir = DefaultStagingDir
|
||||
}
|
||||
|
||||
// Templates
|
||||
templates := map[string]*string{
|
||||
"staging_dir": &p.config.StagingDir,
|
||||
}
|
||||
|
||||
for n, ptr := range templates {
|
||||
var err error
|
||||
*ptr, err = p.config.tpl.Process(*ptr, nil)
|
||||
if err != nil {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, fmt.Errorf("Error processing %s: %s", n, err))
|
||||
}
|
||||
}
|
||||
|
||||
// Validation
|
||||
err = validateFileConfig(p.config.PlaybookFile, "playbook_file", true)
|
||||
if err != nil {
|
||||
errs = packer.MultiErrorAppend(errs, err)
|
||||
}
|
||||
for _, path := range p.config.PlaybookPaths {
|
||||
err := validateFileConfig(path, "playbook_paths", false)
|
||||
if err != nil {
|
||||
errs = packer.MultiErrorAppend(errs, err)
|
||||
}
|
||||
}
|
||||
for _, path := range p.config.RolePaths {
|
||||
if err := validateDirConfig(path, "role_paths"); err != nil {
|
||||
errs = packer.MultiErrorAppend(errs, err)
|
||||
}
|
||||
}
|
||||
if errs != nil && len(errs.Errors) > 0 {
|
||||
return errs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
|
||||
ui.Say("Provisioning with Ansible...")
|
||||
|
||||
ui.Message("Creating Ansible staging directory...")
|
||||
if err := p.createDir(ui, comm, p.config.StagingDir); err != nil {
|
||||
return fmt.Errorf("Error creating staging directory: %s", err)
|
||||
}
|
||||
|
||||
ui.Message("Uploading main Playbook file...")
|
||||
src := p.config.PlaybookFile
|
||||
dst := filepath.Join(p.config.StagingDir, filepath.Base(src))
|
||||
if err := p.uploadFile(ui, comm, dst, src); err != nil {
|
||||
return fmt.Errorf("Error uploading main playbook: %s", err)
|
||||
}
|
||||
|
||||
if len(p.config.RolePaths) > 0 {
|
||||
ui.Message("Uploading role directories...")
|
||||
for _, src := range p.config.RolePaths {
|
||||
dst := filepath.Join(p.config.StagingDir, "roles", filepath.Base(src))
|
||||
if err := p.uploadDir(ui, comm, dst, src); err != nil {
|
||||
return fmt.Errorf("Error uploading roles: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(p.config.PlaybookPaths) > 0 {
|
||||
ui.Message("Uploading additional Playbooks...")
|
||||
if err := p.createDir(ui, comm, filepath.Join(p.config.StagingDir, "playbooks")); err != nil {
|
||||
return fmt.Errorf("Error creating playbooks directory: %s", err)
|
||||
}
|
||||
for _, src := range p.config.PlaybookPaths {
|
||||
dst := filepath.Join(p.config.StagingDir, "playbooks", filepath.Base(src))
|
||||
if err := p.uploadFile(ui, comm, dst, src); err != nil {
|
||||
return fmt.Errorf("Error uploading playbooks: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := p.executeAnsible(ui, comm); err != nil {
|
||||
return fmt.Errorf("Error executing Ansible: %s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Provisioner) Cancel() {
|
||||
// Just hard quit. It isn't a big deal if what we're doing keeps
|
||||
// running on the other side.
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func (p *Provisioner) executeAnsible(ui packer.Ui, comm packer.Communicator) error {
|
||||
playbook := filepath.Join(p.config.StagingDir, filepath.Base(p.config.PlaybookFile))
|
||||
|
||||
// The inventory must be set to "127.0.0.1,". The comma is important
|
||||
// as its the only way to override the ansible inventory when dealing
|
||||
// with a single host.
|
||||
command := fmt.Sprintf("ansible-playbook %s -c local -i %s", playbook, `"127.0.0.1,"`)
|
||||
|
||||
ui.Message(fmt.Sprintf("Executing Ansible: %s", command))
|
||||
cmd := &packer.RemoteCmd{
|
||||
Command: command,
|
||||
}
|
||||
if err := cmd.StartWithUi(comm, ui); err != nil {
|
||||
return err
|
||||
}
|
||||
if cmd.ExitStatus != 0 {
|
||||
return fmt.Errorf("Non-zero exit status: %d", cmd.ExitStatus)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateDirConfig(path string, config string) error {
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %s is invalid: %s", config, path, err)
|
||||
} else if !info.IsDir() {
|
||||
return fmt.Errorf("%s: %s must point to a directory", config, path)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateFileConfig(name string, config string, req bool) error {
|
||||
if req {
|
||||
if name == "" {
|
||||
return fmt.Errorf("%s must be specified.", config)
|
||||
}
|
||||
}
|
||||
info, err := os.Stat(name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %s is invalid: %s", config, name, err)
|
||||
} else if info.IsDir() {
|
||||
return fmt.Errorf("%s: %s must point to a file", config, name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Provisioner) uploadFile(ui packer.Ui, comm packer.Communicator, dst, src string) error {
|
||||
f, err := os.Open(src)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error opening: %s", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if err = comm.Upload(dst, f); err != nil {
|
||||
return fmt.Errorf("Error uploading %s: %s", src, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Provisioner) createDir(ui packer.Ui, comm packer.Communicator, dir string) error {
|
||||
ui.Message(fmt.Sprintf("Creating directory: %s", dir))
|
||||
cmd := &packer.RemoteCmd{
|
||||
Command: fmt.Sprintf("mkdir -p '%s'", dir),
|
||||
}
|
||||
if err := cmd.StartWithUi(comm, ui); err != nil {
|
||||
return err
|
||||
}
|
||||
if cmd.ExitStatus != 0 {
|
||||
return fmt.Errorf("Non-zero exit status.")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Provisioner) uploadDir(ui packer.Ui, comm packer.Communicator, dst, src string) error {
|
||||
if err := p.createDir(ui, comm, dst); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Make sure there is a trailing "/" so that the directory isn't
|
||||
// created on the other side.
|
||||
if src[len(src)-1] != '/' {
|
||||
src = src + "/"
|
||||
}
|
||||
return comm.UploadDir(dst, src, nil)
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
package ansiblelocal
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func testConfig() map[string]interface{} {
|
||||
m := make(map[string]interface{})
|
||||
return m
|
||||
}
|
||||
|
||||
func TestProvisioner_Impl(t *testing.T) {
|
||||
var raw interface{}
|
||||
raw = &Provisioner{}
|
||||
if _, ok := raw.(packer.Provisioner); !ok {
|
||||
t.Fatalf("must be a Provisioner")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvisionerPrepare_Defaults(t *testing.T) {
|
||||
var p Provisioner
|
||||
config := testConfig()
|
||||
|
||||
playbook_file, err := ioutil.TempFile("", "playbook")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
defer os.Remove(playbook_file.Name())
|
||||
|
||||
config["playbook_file"] = playbook_file.Name()
|
||||
err = p.Prepare(config)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if p.config.StagingDir != DefaultStagingDir {
|
||||
t.Fatalf("unexpected staging dir %s, expected %s",
|
||||
p.config.StagingDir, DefaultStagingDir)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvisionerPrepare_PlaybookFile(t *testing.T) {
|
||||
var p Provisioner
|
||||
config := testConfig()
|
||||
|
||||
err := p.Prepare(config)
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
|
||||
config["playbook_file"] = ""
|
||||
err = p.Prepare(config)
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
|
||||
playbook_file, err := ioutil.TempFile("", "playbook")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
defer os.Remove(playbook_file.Name())
|
||||
|
||||
config["playbook_file"] = playbook_file.Name()
|
||||
err = p.Prepare(config)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"fmt"
|
||||
"github.com/mitchellh/packer/common"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -18,12 +17,7 @@ import (
|
||||
type Config struct {
|
||||
common.PackerConfig `mapstructure:",squash"`
|
||||
|
||||
ChefEnvironment string `mapstructure:"chef_environment"`
|
||||
ConfigTemplate string `mapstructure:"config_template"`
|
||||
CookbookPaths []string `mapstructure:"cookbook_paths"`
|
||||
RolesPath string `mapstructure:"roles_path"`
|
||||
DataBagsPath string `mapstructure:"data_bags_path"`
|
||||
EnvironmentsPath string `mapstructure:"environments_path"`
|
||||
ExecuteCommand string `mapstructure:"execute_command"`
|
||||
InstallCommand string `mapstructure:"install_command"`
|
||||
RemoteCookbookPaths []string `mapstructure:"remote_cookbook_paths"`
|
||||
@@ -41,18 +35,7 @@ type Provisioner struct {
|
||||
}
|
||||
|
||||
type ConfigTemplate struct {
|
||||
CookbookPaths string
|
||||
DataBagsPath string
|
||||
RolesPath string
|
||||
EnvironmentsPath string
|
||||
ChefEnvironment string
|
||||
|
||||
// Templates don't support boolean statements until Go 1.2. In the
|
||||
// mean time, we do this.
|
||||
// TODO(mitchellh): Remove when Go 1.2 is released
|
||||
HasDataBagsPath bool
|
||||
HasRolesPath bool
|
||||
HasEnvironmentsPath bool
|
||||
CookbookPaths string
|
||||
}
|
||||
|
||||
type ExecuteTemplate struct {
|
||||
@@ -97,12 +80,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
|
||||
errs := common.CheckUnusedConfig(md)
|
||||
|
||||
templates := map[string]*string{
|
||||
"config_template": &p.config.ConfigTemplate,
|
||||
"data_bags_path": &p.config.DataBagsPath,
|
||||
"roles_path": &p.config.RolesPath,
|
||||
"staging_dir": &p.config.StagingDir,
|
||||
"environments_path": &p.config.EnvironmentsPath,
|
||||
"chef_environment": &p.config.ChefEnvironment,
|
||||
"staging_dir": &p.config.StagingDir,
|
||||
}
|
||||
|
||||
for n, ptr := range templates {
|
||||
@@ -143,17 +121,6 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
|
||||
}
|
||||
}
|
||||
|
||||
if p.config.ConfigTemplate != "" {
|
||||
fi, err := os.Stat(p.config.ConfigTemplate)
|
||||
if err != nil {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, fmt.Errorf("Bad config template path: %s", err))
|
||||
} else if fi.IsDir() {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, fmt.Errorf("Config template path must be a file: %s", err))
|
||||
}
|
||||
}
|
||||
|
||||
for _, path := range p.config.CookbookPaths {
|
||||
pFileInfo, err := os.Stat(path)
|
||||
|
||||
@@ -163,33 +130,6 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
|
||||
}
|
||||
}
|
||||
|
||||
if p.config.RolesPath != "" {
|
||||
pFileInfo, err := os.Stat(p.config.RolesPath)
|
||||
|
||||
if err != nil || !pFileInfo.IsDir() {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, fmt.Errorf("Bad roles path '%s': %s", p.config.RolesPath, err))
|
||||
}
|
||||
}
|
||||
|
||||
if p.config.DataBagsPath != "" {
|
||||
pFileInfo, err := os.Stat(p.config.DataBagsPath)
|
||||
|
||||
if err != nil || !pFileInfo.IsDir() {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, fmt.Errorf("Bad data bags path '%s': %s", p.config.DataBagsPath, err))
|
||||
}
|
||||
}
|
||||
|
||||
if p.config.EnvironmentsPath != "" {
|
||||
pFileInfo, err := os.Stat(p.config.EnvironmentsPath)
|
||||
|
||||
if err != nil || !pFileInfo.IsDir() {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, fmt.Errorf("Bad environments path '%s': %s", p.config.EnvironmentsPath, err))
|
||||
}
|
||||
}
|
||||
|
||||
// Process the user variables within the JSON and set the JSON.
|
||||
// Do this early so that we can validate and show errors.
|
||||
p.config.Json, err = p.processJsonUserVars()
|
||||
@@ -226,31 +166,7 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
|
||||
cookbookPaths = append(cookbookPaths, targetPath)
|
||||
}
|
||||
|
||||
rolesPath := ""
|
||||
if p.config.RolesPath != "" {
|
||||
rolesPath = fmt.Sprintf("%s/roles", p.config.StagingDir)
|
||||
if err := p.uploadDirectory(ui, comm, rolesPath, p.config.RolesPath); err != nil {
|
||||
return fmt.Errorf("Error uploading roles: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
dataBagsPath := ""
|
||||
if p.config.DataBagsPath != "" {
|
||||
dataBagsPath = fmt.Sprintf("%s/data_bags", p.config.StagingDir)
|
||||
if err := p.uploadDirectory(ui, comm, dataBagsPath, p.config.DataBagsPath); err != nil {
|
||||
return fmt.Errorf("Error uploading data bags: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
environmentsPath := ""
|
||||
if p.config.EnvironmentsPath != "" {
|
||||
environmentsPath = fmt.Sprintf("%s/environments", p.config.StagingDir)
|
||||
if err := p.uploadDirectory(ui, comm, environmentsPath, p.config.EnvironmentsPath); err != nil {
|
||||
return fmt.Errorf("Error uploading environments: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
configPath, err := p.createConfig(ui, comm, cookbookPaths, rolesPath, dataBagsPath, environmentsPath, p.config.ChefEnvironment)
|
||||
configPath, err := p.createConfig(ui, comm, cookbookPaths)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error creating Chef config file: %s", err)
|
||||
}
|
||||
@@ -287,7 +203,7 @@ func (p *Provisioner) uploadDirectory(ui packer.Ui, comm packer.Communicator, ds
|
||||
return comm.UploadDir(dst, src, nil)
|
||||
}
|
||||
|
||||
func (p *Provisioner) createConfig(ui packer.Ui, comm packer.Communicator, localCookbooks []string, rolesPath string, dataBagsPath string, environmentsPath string, chefEnvironment string) (string, error) {
|
||||
func (p *Provisioner) createConfig(ui packer.Ui, comm packer.Communicator, localCookbooks []string) (string, error) {
|
||||
ui.Message("Creating configuration file 'solo.rb'")
|
||||
|
||||
cookbook_paths := make([]string, len(p.config.RemoteCookbookPaths)+len(localCookbooks))
|
||||
@@ -300,32 +216,8 @@ func (p *Provisioner) createConfig(ui packer.Ui, comm packer.Communicator, local
|
||||
cookbook_paths[i] = fmt.Sprintf(`"%s"`, path)
|
||||
}
|
||||
|
||||
// Read the template
|
||||
tpl := DefaultConfigTemplate
|
||||
if p.config.ConfigTemplate != "" {
|
||||
f, err := os.Open(p.config.ConfigTemplate)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
tplBytes, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
tpl = string(tplBytes)
|
||||
}
|
||||
|
||||
configString, err := p.config.tpl.Process(tpl, &ConfigTemplate{
|
||||
CookbookPaths: strings.Join(cookbook_paths, ","),
|
||||
RolesPath: rolesPath,
|
||||
DataBagsPath: dataBagsPath,
|
||||
EnvironmentsPath: environmentsPath,
|
||||
HasRolesPath: rolesPath != "",
|
||||
HasDataBagsPath: dataBagsPath != "",
|
||||
HasEnvironmentsPath: environmentsPath != "",
|
||||
ChefEnvironment: chefEnvironment,
|
||||
configString, err := p.config.tpl.Process(DefaultConfigTemplate, &ConfigTemplate{
|
||||
CookbookPaths: strings.Join(cookbook_paths, ","),
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -476,15 +368,5 @@ func (p *Provisioner) processJsonUserVars() (map[string]interface{}, error) {
|
||||
}
|
||||
|
||||
var DefaultConfigTemplate = `
|
||||
cookbook_path [{{.CookbookPaths}}]
|
||||
{{if .HasRolesPath}}
|
||||
role_path "{{.RolesPath}}"
|
||||
{{end}}
|
||||
{{if .HasDataBagsPath}}
|
||||
data_bag_path "{{.DataBagsPath}}"
|
||||
{{end}}
|
||||
{{if .HasEnvironmentsPath}}
|
||||
environments_path "{{.EnvironmentsPath}}"
|
||||
chef_environment "{{.ChefEnvironment}}"
|
||||
{{end}}
|
||||
cookbook_path [{{.CookbookPaths}}]
|
||||
`
|
||||
|
||||
@@ -19,65 +19,6 @@ func TestProvisioner_Impl(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvisionerPrepare_chefEnvironment(t *testing.T) {
|
||||
var p Provisioner
|
||||
|
||||
config := testConfig()
|
||||
config["chef_environment"] = "some-env"
|
||||
|
||||
err := p.Prepare(config)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if p.config.ChefEnvironment != "some-env" {
|
||||
t.Fatalf("unexpected: %#v", p.config.ChefEnvironment)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvisionerPrepare_configTemplate(t *testing.T) {
|
||||
var err error
|
||||
var p Provisioner
|
||||
|
||||
// Test no config template
|
||||
config := testConfig()
|
||||
delete(config, "config_template")
|
||||
err = p.Prepare(config)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
// Test with a file
|
||||
tf, err := ioutil.TempFile("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
defer os.Remove(tf.Name())
|
||||
|
||||
config = testConfig()
|
||||
config["config_template"] = tf.Name()
|
||||
p = Provisioner{}
|
||||
err = p.Prepare(config)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
// Test with a directory
|
||||
td, err := ioutil.TempDir("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
defer os.RemoveAll(td)
|
||||
|
||||
config = testConfig()
|
||||
config["config_template"] = td
|
||||
p = Provisioner{}
|
||||
err = p.Prepare(config)
|
||||
if err == nil {
|
||||
t.Fatal("should have err")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvisionerPrepare_cookbookPaths(t *testing.T) {
|
||||
var p Provisioner
|
||||
|
||||
@@ -91,25 +32,11 @@ func TestProvisionerPrepare_cookbookPaths(t *testing.T) {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
rolesPath, err := ioutil.TempDir("", "roles")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
dataBagsPath, err := ioutil.TempDir("", "data_bags")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
defer os.Remove(path1)
|
||||
defer os.Remove(path2)
|
||||
defer os.Remove(rolesPath)
|
||||
defer os.Remove(dataBagsPath)
|
||||
|
||||
config := testConfig()
|
||||
config["cookbook_paths"] = []string{path1, path2}
|
||||
config["roles_path"] = rolesPath
|
||||
config["data_bags_path"] = dataBagsPath
|
||||
|
||||
err = p.Prepare(config)
|
||||
if err != nil {
|
||||
@@ -123,80 +50,6 @@ func TestProvisionerPrepare_cookbookPaths(t *testing.T) {
|
||||
if p.config.CookbookPaths[0] != path1 || p.config.CookbookPaths[1] != path2 {
|
||||
t.Fatalf("unexpected: %#v", p.config.CookbookPaths)
|
||||
}
|
||||
|
||||
if p.config.RolesPath != rolesPath {
|
||||
t.Fatalf("unexpected: %#v", p.config.RolesPath)
|
||||
}
|
||||
|
||||
if p.config.DataBagsPath != dataBagsPath {
|
||||
t.Fatalf("unexpected: %#v", p.config.DataBagsPath)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvisionerPrepare_dataBagsPath(t *testing.T) {
|
||||
var p Provisioner
|
||||
|
||||
dataBagsPath, err := ioutil.TempDir("", "data_bags")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
defer os.Remove(dataBagsPath)
|
||||
|
||||
config := testConfig()
|
||||
config["data_bags_path"] = dataBagsPath
|
||||
|
||||
err = p.Prepare(config)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if p.config.DataBagsPath != dataBagsPath {
|
||||
t.Fatalf("unexpected: %#v", p.config.DataBagsPath)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvisionerPrepare_environmentsPath(t *testing.T) {
|
||||
var p Provisioner
|
||||
|
||||
environmentsPath, err := ioutil.TempDir("", "environments")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
defer os.Remove(environmentsPath)
|
||||
|
||||
config := testConfig()
|
||||
config["environments_path"] = environmentsPath
|
||||
|
||||
err = p.Prepare(config)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if p.config.EnvironmentsPath != environmentsPath {
|
||||
t.Fatalf("unexpected: %#v", p.config.EnvironmentsPath)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvisionerPrepare_rolesPath(t *testing.T) {
|
||||
var p Provisioner
|
||||
|
||||
rolesPath, err := ioutil.TempDir("", "roles")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
defer os.Remove(rolesPath)
|
||||
|
||||
config := testConfig()
|
||||
config["roles_path"] = rolesPath
|
||||
|
||||
err = p.Prepare(config)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if p.config.RolesPath != rolesPath {
|
||||
t.Fatalf("unexpected: %#v", p.config.RolesPath)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvisionerPrepare_json(t *testing.T) {
|
||||
|
||||
@@ -69,7 +69,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
|
||||
|
||||
// Set some defaults
|
||||
if p.config.ExecuteCommand == "" {
|
||||
p.config.ExecuteCommand = "{{.FacterVars}} {{if .Sudo}} sudo -E {{end}}" +
|
||||
p.config.ExecuteCommand = "{{.FacterVars}}{{if .Sudo}} sudo -E {{end}}" +
|
||||
"puppet apply --verbose --modulepath='{{.ModulePath}}' " +
|
||||
"{{if .HasHieraConfigPath}}--hiera_config='{{.HieraConfigPath}}' {{end}}" +
|
||||
"--detailed-exitcodes " +
|
||||
@@ -82,9 +82,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
|
||||
|
||||
// Templates
|
||||
templates := map[string]*string{
|
||||
"hiera_config_path": &p.config.HieraConfigPath,
|
||||
"manifest_file": &p.config.ManifestFile,
|
||||
"staging_dir": &p.config.StagingDir,
|
||||
"staging_dir": &p.config.StagingDir,
|
||||
}
|
||||
|
||||
for n, ptr := range templates {
|
||||
@@ -217,6 +215,7 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
|
||||
}
|
||||
|
||||
// Upload manifests
|
||||
ui.Message("Uploading manifests...")
|
||||
remoteManifestFile, err := p.uploadManifests(ui, comm)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error uploading manifests: %s", err)
|
||||
@@ -250,7 +249,7 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if cmd.ExitStatus != 0 && cmd.ExitStatus != 2 {
|
||||
if cmd.ExitStatus != 0 {
|
||||
return fmt.Errorf("Puppet exited with a non-zero exit status: %d", cmd.ExitStatus)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
"github.com/mitchellh/packer/common"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const DefaultTempConfigDir = "/tmp/salt"
|
||||
@@ -144,13 +146,12 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
|
||||
}
|
||||
|
||||
ui.Message(fmt.Sprintf("Uploading local state tree: %s", p.config.LocalStateTree))
|
||||
if err = comm.UploadDir(fmt.Sprintf("%s/states", p.config.TempConfigDir),
|
||||
p.config.LocalStateTree, []string{".git"}); err != nil {
|
||||
if err = UploadLocalDirectory(p.config.LocalStateTree, fmt.Sprintf("%s/states", p.config.TempConfigDir), comm, ui); err != nil {
|
||||
return fmt.Errorf("Error uploading local state tree to remote: %s", err)
|
||||
}
|
||||
|
||||
ui.Message(fmt.Sprintf("Moving %s/states to /srv/salt", p.config.TempConfigDir))
|
||||
cmd = &packer.RemoteCmd{Command: fmt.Sprintf("sudo mv %s/states /srv/salt", p.config.TempConfigDir)}
|
||||
ui.Message(fmt.Sprintf("Moving %s to /srv/salt", p.config.TempConfigDir))
|
||||
cmd = &packer.RemoteCmd{Command: fmt.Sprintf("sudo mv %s/states /srv/salt/", p.config.TempConfigDir)}
|
||||
if err = cmd.StartWithUi(comm, ui); err != nil || cmd.ExitStatus != 0 {
|
||||
if err == nil {
|
||||
err = fmt.Errorf("Bad exit status: %d", cmd.ExitStatus)
|
||||
@@ -160,9 +161,18 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
|
||||
}
|
||||
|
||||
if p.config.LocalPillarRoots != "" {
|
||||
ui.Message(fmt.Sprintf("Creating remote pillar directory: %s/pillar", p.config.TempConfigDir))
|
||||
cmd := &packer.RemoteCmd{Command: fmt.Sprintf("mkdir -p %s/pillar", p.config.TempConfigDir)}
|
||||
if err = cmd.StartWithUi(comm, ui); err != nil || cmd.ExitStatus != 0 {
|
||||
if err == nil {
|
||||
err = fmt.Errorf("Bad exit status: %d", cmd.ExitStatus)
|
||||
}
|
||||
|
||||
return fmt.Errorf("Error creating remote pillar directory: %s", err)
|
||||
}
|
||||
|
||||
ui.Message(fmt.Sprintf("Uploading local pillar roots: %s", p.config.LocalPillarRoots))
|
||||
if err = comm.UploadDir(fmt.Sprintf("%s/pillar", p.config.TempConfigDir),
|
||||
p.config.LocalPillarRoots, []string{".git"}); err != nil {
|
||||
if err = UploadLocalDirectory(p.config.LocalPillarRoots, fmt.Sprintf("%s/pillar", p.config.TempConfigDir), comm, ui); err != nil {
|
||||
return fmt.Errorf("Error uploading local pillar roots to remote: %s", err)
|
||||
}
|
||||
|
||||
@@ -196,6 +206,44 @@ func (p *Provisioner) Cancel() {
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func UploadLocalDirectory(localDir string, remoteDir string, comm packer.Communicator, ui packer.Ui) (err error) {
|
||||
visitPath := func(localPath string, f os.FileInfo, err error) (err2 error) {
|
||||
localRelPath := strings.Replace(localPath, localDir, "", 1)
|
||||
localRelPath = strings.Replace(localRelPath, "\\", "/", -1)
|
||||
remotePath := filepath.Join(remoteDir, localRelPath)
|
||||
if f.IsDir() && f.Name() == ".git" {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
if f.IsDir() {
|
||||
// Make remote directory
|
||||
cmd := &packer.RemoteCmd{Command: fmt.Sprintf("mkdir -p %s", remotePath)}
|
||||
if err = cmd.StartWithUi(comm, ui); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// Upload file to existing directory
|
||||
file, err := os.Open(localPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error opening file: %s", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
ui.Message(fmt.Sprintf("Uploading file %s: %s", localPath, remotePath))
|
||||
if err = comm.Upload(remotePath, file); err != nil {
|
||||
return fmt.Errorf("Error uploading file: %s", err)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
err = filepath.Walk(localDir, visitPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error uploading local directory %s: %s", localDir, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func uploadMinionConfig(comm packer.Communicator, dst string, src string) error {
|
||||
f, err := os.Open(src)
|
||||
if err != nil {
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"fmt"
|
||||
"github.com/mitchellh/packer/common"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
@@ -21,10 +20,6 @@ const DefaultRemotePath = "/tmp/script.sh"
|
||||
type config struct {
|
||||
common.PackerConfig `mapstructure:",squash"`
|
||||
|
||||
// If true, the script contains binary and line endings will not be
|
||||
// converted from Windows to Unix-style.
|
||||
Binary bool
|
||||
|
||||
// An inline script to execute. Multiple strings are all executed
|
||||
// in the context of a single shell.
|
||||
Inline []string
|
||||
@@ -264,12 +259,7 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
|
||||
return err
|
||||
}
|
||||
|
||||
var r io.Reader = f
|
||||
if !p.config.Binary {
|
||||
r = &UnixReader{Reader: r}
|
||||
}
|
||||
|
||||
if err := comm.Upload(p.config.RemotePath, r); err != nil {
|
||||
if err := comm.Upload(p.config.RemotePath, f); err != nil {
|
||||
return fmt.Errorf("Error uploading script: %s", err)
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user