builder/vmware: move outputdir stuff to common

This commit is contained in:
Mitchell Hashimoto 2013-12-24 11:21:02 -07:00
parent 8bd3ca4470
commit 50f8b2c1a3
7 changed files with 323 additions and 93 deletions

View File

@ -0,0 +1,15 @@
package common
// OutputDir is an interface type that abstracts the creation and handling
// of the output directory for VMware-based products. The abstraction is made
// so that the output directory can be properly made on remote (ESXi) based
// VMware products as well as local.
type OutputDir interface {
DirExists() (bool, error)
ListFiles() ([]string, error)
MkdirAll() error
Remove(string) error
RemoveAll() error
SetOutputDir(string)
String() string
}

View File

@ -0,0 +1,53 @@
package common
import (
"os"
"path/filepath"
)
// LocalOutputDir is an OutputDir implementation where the directory
// is on the local machine.
type LocalOutputDir struct {
Dir string
}
func (d *LocalOutputDir) DirExists() (bool, error) {
_, err := os.Stat(d.Dir)
return err == nil, nil
}
func (d *LocalOutputDir) ListFiles() ([]string, error) {
files := make([]string, 0, 10)
visit := func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() {
files = append(files, path)
}
return nil
}
return files, filepath.Walk(d.Dir, visit)
}
func (d *LocalOutputDir) MkdirAll() error {
return os.MkdirAll(d.Dir, 0755)
}
func (d *LocalOutputDir) Remove(path string) error {
return os.Remove(path)
}
func (d *LocalOutputDir) RemoveAll() error {
return os.RemoveAll(d.Dir)
}
func (d *LocalOutputDir) SetOutputDir(path string) {
d.Dir = path
}
func (d *LocalOutputDir) String() string {
return d.Dir
}

View File

@ -0,0 +1,9 @@
package common
import (
"testing"
)
func TestLocalOuputDir_impl(t *testing.T) {
var _ OutputDir = new(LocalOutputDir)
}

View File

@ -0,0 +1,74 @@
package common
import (
"fmt"
"log"
"time"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
)
// StepOutputDir sets up the output directory by creating it if it does
// not exist, deleting it if it does exist and we're forcing, and cleaning
// it up when we're done with it.
type StepOutputDir struct {
Force bool
success bool
}
func (s *StepOutputDir) Run(state multistep.StateBag) multistep.StepAction {
dir := state.Get("dir").(OutputDir)
ui := state.Get("ui").(packer.Ui)
exists, err := dir.DirExists()
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
if exists {
if s.Force {
ui.Say("Deleting previous output directory...")
dir.RemoveAll()
} else {
state.Put("error", fmt.Errorf(
"Output directory '%s' already exists.", dir.String()))
return multistep.ActionHalt
}
}
if err := dir.MkdirAll(); err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
s.success = true
return multistep.ActionContinue
}
func (s *StepOutputDir) Cleanup(state multistep.StateBag) {
if !s.success {
return
}
_, cancelled := state.GetOk(multistep.StateCancelled)
_, halted := state.GetOk(multistep.StateHalted)
if cancelled || halted {
dir := state.Get("dir").(OutputDir)
ui := state.Get("ui").(packer.Ui)
ui.Say("Deleting output directory...")
for i := 0; i < 5; i++ {
err := dir.RemoveAll()
if err == nil {
break
}
log.Printf("Error removing output dir: %s", err)
time.Sleep(2 * time.Second)
}
}
}

View File

@ -0,0 +1,150 @@
package common
import (
"github.com/mitchellh/multistep"
"io/ioutil"
"os"
"testing"
)
func testOutputDir(t *testing.T) *LocalOutputDir {
td, err := ioutil.TempDir("", "packer")
if err != nil {
t.Fatalf("err: %s", err)
}
os.RemoveAll(td)
return &LocalOutputDir{Dir: td}
}
func TestStepOutputDir_impl(t *testing.T) {
var _ multistep.Step = new(StepOutputDir)
}
func TestStepOutputDir(t *testing.T) {
state := testState(t)
step := new(StepOutputDir)
dir := testOutputDir(t)
state.Put("dir", dir)
// Test the run
if action := step.Run(state); action != multistep.ActionContinue {
t.Fatalf("bad action: %#v", action)
}
if _, ok := state.GetOk("error"); ok {
t.Fatal("should NOT have error")
}
if _, err := os.Stat(dir.Dir); err != nil {
t.Fatalf("err: %s", err)
}
// Test the cleanup
step.Cleanup(state)
if _, err := os.Stat(dir.Dir); err != nil {
t.Fatalf("err: %s", err)
}
}
func TestStepOutputDir_existsNoForce(t *testing.T) {
state := testState(t)
step := new(StepOutputDir)
dir := testOutputDir(t)
state.Put("dir", dir)
// Make sure the dir exists
if err := os.MkdirAll(dir.Dir, 0755); err != nil {
t.Fatalf("err: %s", err)
}
// Test the run
if action := step.Run(state); action != multistep.ActionHalt {
t.Fatalf("bad action: %#v", action)
}
if _, ok := state.GetOk("error"); !ok {
t.Fatal("should have error")
}
// Test the cleanup
step.Cleanup(state)
if _, err := os.Stat(dir.Dir); err != nil {
t.Fatal("should not delete dir")
}
}
func TestStepOutputDir_existsForce(t *testing.T) {
state := testState(t)
step := new(StepOutputDir)
step.Force = true
dir := testOutputDir(t)
state.Put("dir", dir)
// Make sure the dir exists
if err := os.MkdirAll(dir.Dir, 0755); err != nil {
t.Fatalf("err: %s", err)
}
// Test the run
if action := step.Run(state); action != multistep.ActionContinue {
t.Fatalf("bad action: %#v", action)
}
if _, ok := state.GetOk("error"); ok {
t.Fatal("should NOT have error")
}
if _, err := os.Stat(dir.Dir); err != nil {
t.Fatalf("err: %s", err)
}
}
func TestStepOutputDir_cancel(t *testing.T) {
state := testState(t)
step := new(StepOutputDir)
dir := testOutputDir(t)
state.Put("dir", dir)
// Test the run
if action := step.Run(state); action != multistep.ActionContinue {
t.Fatalf("bad action: %#v", action)
}
if _, ok := state.GetOk("error"); ok {
t.Fatal("should NOT have error")
}
if _, err := os.Stat(dir.Dir); err != nil {
t.Fatalf("err: %s", err)
}
// Test cancel/halt
state.Put(multistep.StateCancelled, true)
step.Cleanup(state)
if _, err := os.Stat(dir.Dir); err == nil {
t.Fatal("directory should not exist")
}
}
func TestStepOutputDir_halt(t *testing.T) {
state := testState(t)
step := new(StepOutputDir)
dir := testOutputDir(t)
state.Put("dir", dir)
// Test the run
if action := step.Run(state); action != multistep.ActionContinue {
t.Fatalf("bad action: %#v", action)
}
if _, ok := state.GetOk("error"); ok {
t.Fatal("should NOT have error")
}
if _, err := os.Stat(dir.Dir); err != nil {
t.Fatalf("err: %s", err)
}
// Test cancel/halt
state.Put(multistep.StateHalted, true)
step.Cleanup(state)
if _, err := os.Stat(dir.Dir); err == nil {
t.Fatal("directory should not exist")
}
}

View File

@ -346,6 +346,25 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
return nil, fmt.Errorf("Failed creating VMware driver: %s", err)
}
// Determine the output dir implementation
var dir OutputDir
switch d := driver.(type) {
case OutputDir:
dir = d
default:
dir = new(vmwcommon.LocalOutputDir)
}
dir.SetOutputDir(b.config.OutputDir)
// Setup the state bag
state := new(multistep.BasicStateBag)
state.Put("cache", cache)
state.Put("config", &b.config)
state.Put("dir", dir)
state.Put("driver", driver)
state.Put("hook", hook)
state.Put("ui", ui)
// Seed the random number generator
rand.Seed(time.Now().UTC().UnixNano())
@ -358,7 +377,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
ResultKey: "iso_path",
Url: b.config.ISOUrls,
},
&stepPrepareOutputDir{},
&vmwcommon.StepOutputDir{
Force: b.config.PackerForce,
},
&common.StepCreateFloppy{
Files: b.config.FloppyFiles,
},
@ -390,14 +411,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
&stepCompactDisk{},
}
// Setup the state bag
state := new(multistep.BasicStateBag)
state.Put("cache", cache)
state.Put("config", &b.config)
state.Put("driver", driver)
state.Put("hook", hook)
state.Put("ui", ui)
// Run!
if b.config.PackerDebug {
b.runner = &multistep.DebugRunner{

View File

@ -1,84 +0,0 @@
package iso
import (
"fmt"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"log"
"time"
)
type stepPrepareOutputDir struct {
dir OutputDir
}
func (s *stepPrepareOutputDir) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*config)
ui := state.Get("ui").(packer.Ui)
dir := s.outputDir(state)
dir.SetOutputDir(config.OutputDir)
exists, err := dir.DirExists()
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
if exists {
if config.PackerForce {
ui.Say("Deleting previous output directory...")
dir.RemoveAll()
} else {
state.Put("error", fmt.Errorf(
"Output directory '%s' already exists.", config.OutputDir))
return multistep.ActionHalt
}
}
if err := dir.MkdirAll(); err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
s.dir = dir
state.Put("dir", dir)
return multistep.ActionContinue
}
func (s *stepPrepareOutputDir) Cleanup(state multistep.StateBag) {
_, cancelled := state.GetOk(multistep.StateCancelled)
_, halted := state.GetOk(multistep.StateHalted)
if cancelled || halted {
ui := state.Get("ui").(packer.Ui)
if s.dir != nil {
ui.Say("Deleting output directory...")
for i := 0; i < 5; i++ {
err := s.dir.RemoveAll()
if err == nil {
break
}
log.Printf("Error removing output dir: %s", err)
time.Sleep(2 * time.Second)
}
}
}
}
func (s *stepPrepareOutputDir) outputDir(state multistep.StateBag) (dir OutputDir) {
driver := state.Get("driver").(Driver)
switch d := driver.(type) {
case OutputDir:
log.Printf("Using driver as the OutputDir implementation")
dir = d
default:
log.Printf("Using localOutputDir implementation")
dir = new(localOutputDir)
}
return
}