Merge pull request #9784 from hashicorp/cleanup_local_vs_remote_output_directories
implement a remote_output_directory option, and heavily comment outpu…
This commit is contained in:
@@ -10,13 +10,39 @@ import (
|
||||
)
|
||||
|
||||
type OutputConfig struct {
|
||||
// This is the path to the directory where the
|
||||
// resulting virtual machine will be created. This may be relative or absolute.
|
||||
// If relative, the path is relative to the working directory when packer
|
||||
// is executed. This directory must not exist or be empty prior to running
|
||||
// the builder. By default this is output-BUILDNAME where "BUILDNAME" is the
|
||||
// name of the build.
|
||||
// This is the path on your local machine (the one running Packer) to the
|
||||
// directory where the resulting virtual machine will be created.
|
||||
// This may be relative or absolute. If relative, the path is relative to
|
||||
// the working directory when packer is executed.
|
||||
//
|
||||
// If you are running a remote esx build, the output_dir is the path on your
|
||||
// local machine (the machine running Packer) to which Packer will export
|
||||
// the vm if you have `"skip_export": false`. If you want to manage the
|
||||
// virtual machine's path on the remote datastore, use `remote_output_dir`.
|
||||
//
|
||||
// This directory must not exist or be empty prior to running
|
||||
// the builder.
|
||||
//
|
||||
// By default this is output-BUILDNAME where "BUILDNAME" is the name of the
|
||||
// build.
|
||||
OutputDir string `mapstructure:"output_directory" required:"false"`
|
||||
// This is the directoy on your remote esx host where you will save your
|
||||
// vm, relative to your remote_datastore.
|
||||
//
|
||||
// This option's default value is your `vm_name`, and the final path of your
|
||||
// vm will be vmfs/volumes/$remote_datastore/$vm_name/$vm_name.vmx where
|
||||
// `$remote_datastore` and `$vm_name` match their corresponding template
|
||||
// options
|
||||
//
|
||||
// For example, setting `"remote_output_directory": "path/to/subdir`
|
||||
// will create a directory `/vmfs/volumes/remote_datastore/path/to/subdir`.
|
||||
//
|
||||
// Packer will not create the remote datastore for you; it must already
|
||||
// exist. However, Packer will create all directories defined in the option
|
||||
// that do not currently exist.
|
||||
//
|
||||
// This option will be ignored unless you are building on a remote esx host.
|
||||
RemoteOutputDir string `mapstructure:"remote_output_directory" required:"false"`
|
||||
}
|
||||
|
||||
func (c *OutputConfig) Prepare(ctx *interpolate.Context, pc *common.PackerConfig) []error {
|
||||
|
||||
@@ -33,7 +33,11 @@ type RemoteDriverMock struct {
|
||||
RemovedCachePath string
|
||||
CacheRemoved bool
|
||||
|
||||
ReturnValDirExists bool
|
||||
|
||||
ReloadVMErr error
|
||||
|
||||
outputDir string
|
||||
}
|
||||
|
||||
func (d *RemoteDriverMock) UploadISO(path string, checksum string, ui packer.Ui) (string, error) {
|
||||
@@ -81,3 +85,33 @@ func (d *RemoteDriverMock) RemoveCache(localPath string) error {
|
||||
func (d *RemoteDriverMock) ReloadVM() error {
|
||||
return d.ReloadVMErr
|
||||
}
|
||||
|
||||
// the following functions satisfy the Outputdir interface
|
||||
|
||||
func (d *RemoteDriverMock) DirExists() (bool, error) {
|
||||
return d.ReturnValDirExists, nil
|
||||
}
|
||||
|
||||
func (d *RemoteDriverMock) ListFiles() ([]string, error) {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
func (d *RemoteDriverMock) MkdirAll() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *RemoteDriverMock) Remove(string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *RemoteDriverMock) RemoveAll() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *RemoteDriverMock) SetOutputDir(s string) {
|
||||
d.outputDir = s
|
||||
}
|
||||
|
||||
func (d *RemoteDriverMock) String() string {
|
||||
return d.outputDir
|
||||
}
|
||||
|
||||
@@ -16,13 +16,62 @@ import (
|
||||
type StepOutputDir struct {
|
||||
Force bool
|
||||
|
||||
OutputConfig *OutputConfig
|
||||
VMName string
|
||||
|
||||
RemoteType string
|
||||
|
||||
success bool
|
||||
}
|
||||
|
||||
func (s *StepOutputDir) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
dir := state.Get("dir").(OutputDir)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
func (s *StepOutputDir) SetOutputAndExportDirs(state multistep.StateBag) OutputDir {
|
||||
driver := state.Get("driver")
|
||||
|
||||
// Hold on to your pants. The output configuration is a little more complex
|
||||
// than you'd expect because of all the moving parts between local and
|
||||
// remote output, and exports, and legacy behavior.
|
||||
var dir OutputDir
|
||||
switch d := driver.(type) {
|
||||
case OutputDir:
|
||||
// The driver fulfils the OutputDir interface so that it can create
|
||||
// output files on the remote instance.
|
||||
dir = d
|
||||
default:
|
||||
// The driver will be running the build and creating the output
|
||||
// directory locally
|
||||
dir = new(LocalOutputDir)
|
||||
}
|
||||
|
||||
// If remote type is esx, we need to track both the output dir on the remote
|
||||
// instance and the output dir locally. exportOutputPath is where we track
|
||||
// the local output dir.
|
||||
exportOutputPath := s.OutputConfig.OutputDir
|
||||
|
||||
if s.RemoteType != "" {
|
||||
if s.OutputConfig.RemoteOutputDir != "" {
|
||||
// User set the remote output dir.
|
||||
s.OutputConfig.OutputDir = s.OutputConfig.RemoteOutputDir
|
||||
} else {
|
||||
// Default output dir to vm name. On remote esx instance, this will
|
||||
// become something like /vmfs/volumes/mydatastore/vmname/vmname.vmx
|
||||
s.OutputConfig.OutputDir = s.VMName
|
||||
}
|
||||
}
|
||||
// Remember, this one's either the output from a local build, or the remote
|
||||
// output from a remote build. Not the local export path for a remote build.
|
||||
dir.SetOutputDir(s.OutputConfig.OutputDir)
|
||||
|
||||
// Set dir in the state for use in file cleanup and artifact
|
||||
state.Put("dir", dir)
|
||||
state.Put("export_output_path", exportOutputPath)
|
||||
return dir
|
||||
}
|
||||
|
||||
func (s *StepOutputDir) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
ui.Say("Configuring output and export directories...")
|
||||
|
||||
dir := s.SetOutputAndExportDirs(state)
|
||||
exists, err := dir.DirExists()
|
||||
if err != nil {
|
||||
state.Put("error", err)
|
||||
@@ -31,7 +80,7 @@ func (s *StepOutputDir) Run(ctx context.Context, state multistep.StateBag) multi
|
||||
|
||||
if exists {
|
||||
if s.Force {
|
||||
ui.Say("Deleting previous output directory...")
|
||||
ui.Message("Deleting previous output directory...")
|
||||
dir.RemoveAll()
|
||||
} else {
|
||||
state.Put("error", fmt.Errorf(
|
||||
|
||||
@@ -9,16 +9,14 @@ import (
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
)
|
||||
|
||||
func testOutputDir(t *testing.T) *LocalOutputDir {
|
||||
func testOutputDir(t *testing.T) string {
|
||||
td, err := ioutil.TempDir("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
os.RemoveAll(td)
|
||||
|
||||
result := new(LocalOutputDir)
|
||||
result.SetOutputDir(td)
|
||||
return result
|
||||
return td
|
||||
}
|
||||
|
||||
func TestStepOutputDir_impl(t *testing.T) {
|
||||
@@ -27,12 +25,20 @@ func TestStepOutputDir_impl(t *testing.T) {
|
||||
|
||||
func TestStepOutputDir(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepOutputDir)
|
||||
driver := new(DriverMock)
|
||||
state.Put("driver", driver)
|
||||
|
||||
dir := testOutputDir(t)
|
||||
td := testOutputDir(t)
|
||||
outconfig := &OutputConfig{
|
||||
OutputDir: td,
|
||||
}
|
||||
|
||||
step := &StepOutputDir{
|
||||
OutputConfig: outconfig,
|
||||
VMName: "testVM",
|
||||
}
|
||||
// Delete the test output directory when done
|
||||
defer os.RemoveAll(dir.dir)
|
||||
state.Put("dir", dir)
|
||||
defer os.RemoveAll(td)
|
||||
|
||||
// Test the run
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
@@ -41,29 +47,37 @@ func TestStepOutputDir(t *testing.T) {
|
||||
if _, ok := state.GetOk("error"); ok {
|
||||
t.Fatal("should NOT have error")
|
||||
}
|
||||
if _, err := os.Stat(dir.dir); err != nil {
|
||||
if _, err := os.Stat(td); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
// Test the cleanup
|
||||
step.Cleanup(state)
|
||||
if _, err := os.Stat(dir.dir); err != nil {
|
||||
if _, err := os.Stat(td); 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)
|
||||
td := testOutputDir(t)
|
||||
outconfig := &OutputConfig{
|
||||
OutputDir: td,
|
||||
}
|
||||
|
||||
step := &StepOutputDir{
|
||||
OutputConfig: outconfig,
|
||||
VMName: "testVM",
|
||||
}
|
||||
// Delete the test output directory when done
|
||||
defer os.RemoveAll(td)
|
||||
|
||||
// Make sure the dir exists
|
||||
if err := os.MkdirAll(dir.dir, 0755); err != nil {
|
||||
if err := os.MkdirAll(td, 0755); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
defer os.RemoveAll(dir.dir)
|
||||
defer os.RemoveAll(td)
|
||||
|
||||
// Test the run
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionHalt {
|
||||
@@ -75,24 +89,33 @@ func TestStepOutputDir_existsNoForce(t *testing.T) {
|
||||
|
||||
// Test the cleanup
|
||||
step.Cleanup(state)
|
||||
if _, err := os.Stat(dir.dir); err != nil {
|
||||
if _, err := os.Stat(td); err != nil {
|
||||
t.Fatal("should not delete dir")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepOutputDir_existsForce(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepOutputDir)
|
||||
|
||||
td := testOutputDir(t)
|
||||
outconfig := &OutputConfig{
|
||||
OutputDir: td,
|
||||
}
|
||||
|
||||
step := &StepOutputDir{
|
||||
OutputConfig: outconfig,
|
||||
VMName: "testVM",
|
||||
}
|
||||
step.Force = true
|
||||
|
||||
dir := testOutputDir(t)
|
||||
state.Put("dir", dir)
|
||||
// Delete the test output directory when done
|
||||
defer os.RemoveAll(td)
|
||||
|
||||
// Make sure the dir exists
|
||||
if err := os.MkdirAll(dir.dir, 0755); err != nil {
|
||||
if err := os.MkdirAll(td, 0755); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
defer os.RemoveAll(dir.dir)
|
||||
defer os.RemoveAll(td)
|
||||
|
||||
// Test the run
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
@@ -101,17 +124,22 @@ func TestStepOutputDir_existsForce(t *testing.T) {
|
||||
if _, ok := state.GetOk("error"); ok {
|
||||
t.Fatal("should NOT have error")
|
||||
}
|
||||
if _, err := os.Stat(dir.dir); err != nil {
|
||||
if _, err := os.Stat(td); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepOutputDir_cancel(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepOutputDir)
|
||||
td := testOutputDir(t)
|
||||
outconfig := &OutputConfig{
|
||||
OutputDir: td,
|
||||
}
|
||||
|
||||
dir := testOutputDir(t)
|
||||
state.Put("dir", dir)
|
||||
step := &StepOutputDir{
|
||||
OutputConfig: outconfig,
|
||||
VMName: "testVM",
|
||||
}
|
||||
|
||||
// Test the run
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
@@ -120,24 +148,29 @@ func TestStepOutputDir_cancel(t *testing.T) {
|
||||
if _, ok := state.GetOk("error"); ok {
|
||||
t.Fatal("should NOT have error")
|
||||
}
|
||||
if _, err := os.Stat(dir.dir); err != nil {
|
||||
if _, err := os.Stat(td); 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 {
|
||||
if _, err := os.Stat(td); err == nil {
|
||||
t.Fatal("directory should not exist")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepOutputDir_halt(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepOutputDir)
|
||||
td := testOutputDir(t)
|
||||
outconfig := &OutputConfig{
|
||||
OutputDir: td,
|
||||
}
|
||||
|
||||
dir := testOutputDir(t)
|
||||
state.Put("dir", dir)
|
||||
step := &StepOutputDir{
|
||||
OutputConfig: outconfig,
|
||||
VMName: "testVM",
|
||||
}
|
||||
|
||||
// Test the run
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
@@ -146,14 +179,46 @@ func TestStepOutputDir_halt(t *testing.T) {
|
||||
if _, ok := state.GetOk("error"); ok {
|
||||
t.Fatal("should NOT have error")
|
||||
}
|
||||
if _, err := os.Stat(dir.dir); err != nil {
|
||||
if _, err := os.Stat(td); 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 {
|
||||
if _, err := os.Stat(td); err == nil {
|
||||
t.Fatal("directory should not exist")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepOutputDir_Remote(t *testing.T) {
|
||||
// Tests remote driver
|
||||
state := testState(t)
|
||||
driver := new(RemoteDriverMock)
|
||||
state.Put("driver", driver)
|
||||
|
||||
td := testOutputDir(t)
|
||||
outconfig := &OutputConfig{
|
||||
OutputDir: td,
|
||||
RemoteOutputDir: "remote_path",
|
||||
}
|
||||
|
||||
step := &StepOutputDir{
|
||||
OutputConfig: outconfig,
|
||||
VMName: "testVM",
|
||||
RemoteType: "esx5",
|
||||
}
|
||||
// Delete the test output directory when done
|
||||
defer os.RemoveAll(td)
|
||||
|
||||
// Test the run
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
// We don't pre-create the output path for export but we do set it in state.
|
||||
exportOutputPath := state.Get("export_output_path").(string)
|
||||
if exportOutputPath != td {
|
||||
t.Fatalf("err: should have set export_output_path!")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,8 +12,20 @@ import (
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
func testLocalOutputDir(t *testing.T) *LocalOutputDir {
|
||||
td, err := ioutil.TempDir("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
os.RemoveAll(td)
|
||||
|
||||
result := new(LocalOutputDir)
|
||||
result.SetOutputDir(td)
|
||||
return result
|
||||
}
|
||||
|
||||
func testStepShutdownState(t *testing.T) multistep.StateBag {
|
||||
dir := testOutputDir(t)
|
||||
dir := testLocalOutputDir(t)
|
||||
if err := dir.MkdirAll(); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
@@ -36,29 +36,10 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
return nil, fmt.Errorf("Failed creating VMware driver: %s", err)
|
||||
}
|
||||
|
||||
// Determine the output dir implementation
|
||||
var dir vmwcommon.OutputDir
|
||||
switch d := driver.(type) {
|
||||
case vmwcommon.OutputDir:
|
||||
dir = d
|
||||
default:
|
||||
dir = new(vmwcommon.LocalOutputDir)
|
||||
}
|
||||
|
||||
// The OutputDir will track remote esxi output; exportOutputPath preserves
|
||||
// the path to the output on the machine running Packer.
|
||||
exportOutputPath := b.config.OutputDir
|
||||
|
||||
if b.config.RemoteType != "" {
|
||||
b.config.OutputDir = b.config.VMName
|
||||
}
|
||||
dir.SetOutputDir(b.config.OutputDir)
|
||||
|
||||
// Setup the state bag
|
||||
state := new(multistep.BasicStateBag)
|
||||
state.Put("config", &b.config)
|
||||
state.Put("debug", b.config.PackerDebug)
|
||||
state.Put("dir", dir)
|
||||
state.Put("driver", driver)
|
||||
state.Put("hook", hook)
|
||||
state.Put("ui", ui)
|
||||
@@ -80,7 +61,10 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
Url: b.config.ISOUrls,
|
||||
},
|
||||
&vmwcommon.StepOutputDir{
|
||||
Force: b.config.PackerForce,
|
||||
Force: b.config.PackerForce,
|
||||
OutputConfig: &b.config.OutputConfig,
|
||||
RemoteType: b.config.RemoteType,
|
||||
VMName: b.config.VMName,
|
||||
},
|
||||
&common.StepCreateFloppy{
|
||||
Files: b.config.FloppyConfig.FloppyFiles,
|
||||
@@ -179,7 +163,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
SkipExport: b.config.SkipExport,
|
||||
VMName: b.config.VMName,
|
||||
OVFToolOptions: b.config.OVFToolOptions,
|
||||
OutputDir: exportOutputPath,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -202,6 +185,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
}
|
||||
|
||||
// Compile the artifact list
|
||||
exportOutputPath := state.Get("export_output_path").(string) // set in StepOutputDir
|
||||
return vmwcommon.NewArtifact(b.config.RemoteType, b.config.Format, exportOutputPath,
|
||||
b.config.VMName, b.config.SkipExport, b.config.KeepRegistered, state)
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ type FlatConfig struct {
|
||||
Serial *string `mapstructure:"serial" required:"false" cty:"serial" hcl:"serial"`
|
||||
Parallel *string `mapstructure:"parallel" required:"false" cty:"parallel" hcl:"parallel"`
|
||||
OutputDir *string `mapstructure:"output_directory" required:"false" cty:"output_directory" hcl:"output_directory"`
|
||||
RemoteOutputDir *string `mapstructure:"remote_output_directory" required:"false" cty:"remote_output_directory" hcl:"remote_output_directory"`
|
||||
Headless *bool `mapstructure:"headless" required:"false" cty:"headless" hcl:"headless"`
|
||||
VNCBindAddress *string `mapstructure:"vnc_bind_address" required:"false" cty:"vnc_bind_address" hcl:"vnc_bind_address"`
|
||||
VNCPortMin *int `mapstructure:"vnc_port_min" required:"false" cty:"vnc_port_min" hcl:"vnc_port_min"`
|
||||
@@ -194,6 +195,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"serial": &hcldec.AttrSpec{Name: "serial", Type: cty.String, Required: false},
|
||||
"parallel": &hcldec.AttrSpec{Name: "parallel", Type: cty.String, Required: false},
|
||||
"output_directory": &hcldec.AttrSpec{Name: "output_directory", Type: cty.String, Required: false},
|
||||
"remote_output_directory": &hcldec.AttrSpec{Name: "remote_output_directory", Type: cty.String, Required: false},
|
||||
"headless": &hcldec.AttrSpec{Name: "headless", Type: cty.Bool, Required: false},
|
||||
"vnc_bind_address": &hcldec.AttrSpec{Name: "vnc_bind_address", Type: cty.String, Required: false},
|
||||
"vnc_port_min": &hcldec.AttrSpec{Name: "vnc_port_min", Type: cty.Number, Required: false},
|
||||
|
||||
@@ -41,28 +41,9 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
return nil, fmt.Errorf("Failed creating VMware driver: %s", err)
|
||||
}
|
||||
|
||||
// Determine the output dir implementation
|
||||
var dir vmwcommon.OutputDir
|
||||
switch d := driver.(type) {
|
||||
case vmwcommon.OutputDir:
|
||||
dir = d
|
||||
default:
|
||||
dir = new(vmwcommon.LocalOutputDir)
|
||||
}
|
||||
|
||||
// The OutputDir will track remote esxi output; exportOutputPath preserves
|
||||
// the path to the output on the machine running Packer.
|
||||
exportOutputPath := b.config.OutputDir
|
||||
|
||||
if b.config.RemoteType != "" {
|
||||
b.config.OutputDir = b.config.VMName
|
||||
}
|
||||
dir.SetOutputDir(b.config.OutputDir)
|
||||
|
||||
// Set up the state.
|
||||
state := new(multistep.BasicStateBag)
|
||||
state.Put("debug", b.config.PackerDebug)
|
||||
state.Put("dir", dir)
|
||||
state.Put("driver", driver)
|
||||
state.Put("hook", hook)
|
||||
state.Put("ui", ui)
|
||||
@@ -77,7 +58,10 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
ToolsUploadFlavor: b.config.ToolsUploadFlavor,
|
||||
},
|
||||
&vmwcommon.StepOutputDir{
|
||||
Force: b.config.PackerForce,
|
||||
Force: b.config.PackerForce,
|
||||
OutputConfig: &b.config.OutputConfig,
|
||||
RemoteType: b.config.RemoteType,
|
||||
VMName: b.config.VMName,
|
||||
},
|
||||
&common.StepCreateFloppy{
|
||||
Files: b.config.FloppyConfig.FloppyFiles,
|
||||
@@ -91,8 +75,8 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
Checksum: "none",
|
||||
},
|
||||
&StepCloneVMX{
|
||||
OutputDir: b.config.OutputDir,
|
||||
Path: b.config.SourcePath,
|
||||
OutputDir: &b.config.OutputDir,
|
||||
VMName: b.config.VMName,
|
||||
Linked: b.config.Linked,
|
||||
},
|
||||
@@ -177,7 +161,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
SkipExport: b.config.SkipExport,
|
||||
VMName: b.config.VMName,
|
||||
OVFToolOptions: b.config.OVFToolOptions,
|
||||
OutputDir: exportOutputPath,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -201,6 +184,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
|
||||
// Artifact
|
||||
log.Printf("Generating artifact...")
|
||||
exportOutputPath := state.Get("export_output_path").(string) // set in StepOutputDir
|
||||
return vmwcommon.NewArtifact(b.config.RemoteType, b.config.Format, exportOutputPath,
|
||||
b.config.VMName, b.config.SkipExport, b.config.KeepRegistered, state)
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ type FlatConfig struct {
|
||||
RemotePrivateKey *string `mapstructure:"remote_private_key_file" required:"false" cty:"remote_private_key_file" hcl:"remote_private_key_file"`
|
||||
SkipValidateCredentials *bool `mapstructure:"skip_validate_credentials" required:"false" cty:"skip_validate_credentials" hcl:"skip_validate_credentials"`
|
||||
OutputDir *string `mapstructure:"output_directory" required:"false" cty:"output_directory" hcl:"output_directory"`
|
||||
RemoteOutputDir *string `mapstructure:"remote_output_directory" required:"false" cty:"remote_output_directory" hcl:"remote_output_directory"`
|
||||
Headless *bool `mapstructure:"headless" required:"false" cty:"headless" hcl:"headless"`
|
||||
VNCBindAddress *string `mapstructure:"vnc_bind_address" required:"false" cty:"vnc_bind_address" hcl:"vnc_bind_address"`
|
||||
VNCPortMin *int `mapstructure:"vnc_port_min" required:"false" cty:"vnc_port_min" hcl:"vnc_port_min"`
|
||||
@@ -156,6 +157,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"remote_private_key_file": &hcldec.AttrSpec{Name: "remote_private_key_file", Type: cty.String, Required: false},
|
||||
"skip_validate_credentials": &hcldec.AttrSpec{Name: "skip_validate_credentials", Type: cty.Bool, Required: false},
|
||||
"output_directory": &hcldec.AttrSpec{Name: "output_directory", Type: cty.String, Required: false},
|
||||
"remote_output_directory": &hcldec.AttrSpec{Name: "remote_output_directory", Type: cty.String, Required: false},
|
||||
"headless": &hcldec.AttrSpec{Name: "headless", Type: cty.Bool, Required: false},
|
||||
"vnc_bind_address": &hcldec.AttrSpec{Name: "vnc_bind_address", Type: cty.String, Required: false},
|
||||
"vnc_port_min": &hcldec.AttrSpec{Name: "vnc_port_min", Type: cty.Number, Required: false},
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
|
||||
// StepCloneVMX takes a VMX file and clones the VM into the output directory.
|
||||
type StepCloneVMX struct {
|
||||
OutputDir string
|
||||
OutputDir *string
|
||||
Path string
|
||||
VMName string
|
||||
Linked bool
|
||||
@@ -33,7 +33,7 @@ func (s *StepCloneVMX) Run(ctx context.Context, state multistep.StateBag) multis
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
// Set the path we want for the new .vmx file and clone
|
||||
vmxPath := filepath.Join(s.OutputDir, s.VMName+".vmx")
|
||||
vmxPath := filepath.Join(*s.OutputDir, s.VMName+".vmx")
|
||||
ui.Say("Cloning source VM...")
|
||||
log.Printf("Cloning from: %s", s.Path)
|
||||
log.Printf("Cloning to: %s", vmxPath)
|
||||
@@ -96,7 +96,7 @@ func (s *StepCloneVMX) Run(ctx context.Context, state multistep.StateBag) multis
|
||||
var diskFullPaths []string
|
||||
for _, diskFilename := range diskFilenames {
|
||||
log.Printf("Found attached disk with filename: %s", diskFilename)
|
||||
diskFullPaths = append(diskFullPaths, filepath.Join(s.OutputDir, diskFilename))
|
||||
diskFullPaths = append(diskFullPaths, filepath.Join(*s.OutputDir, diskFilename))
|
||||
}
|
||||
|
||||
if len(diskFullPaths) == 0 {
|
||||
|
||||
@@ -62,7 +62,7 @@ func TestStepCloneVMX(t *testing.T) {
|
||||
|
||||
state := testState(t)
|
||||
step := new(StepCloneVMX)
|
||||
step.OutputDir = td
|
||||
step.OutputDir = &td
|
||||
step.Path = sourcePath
|
||||
step.VMName = "foo"
|
||||
|
||||
|
||||
@@ -1,8 +1,34 @@
|
||||
<!-- Code generated from the comments of the OutputConfig struct in builder/vmware/common/output_config.go; DO NOT EDIT MANUALLY -->
|
||||
|
||||
- `output_directory` (string) - This is the path to the directory where the
|
||||
resulting virtual machine will be created. This may be relative or absolute.
|
||||
If relative, the path is relative to the working directory when packer
|
||||
is executed. This directory must not exist or be empty prior to running
|
||||
the builder. By default this is output-BUILDNAME where "BUILDNAME" is the
|
||||
name of the build.
|
||||
- `output_directory` (string) - This is the path on your local machine (the one running Packer) to the
|
||||
directory where the resulting virtual machine will be created.
|
||||
This may be relative or absolute. If relative, the path is relative to
|
||||
the working directory when packer is executed.
|
||||
|
||||
If you are running a remote esx build, the output_dir is the path on your
|
||||
local machine (the machine running Packer) to which Packer will export
|
||||
the vm if you have `"skip_export": false`. If you want to manage the
|
||||
virtual machine's path on the remote datastore, use `remote_output_dir`.
|
||||
|
||||
This directory must not exist or be empty prior to running
|
||||
the builder.
|
||||
|
||||
By default this is output-BUILDNAME where "BUILDNAME" is the name of the
|
||||
build.
|
||||
|
||||
- `remote_output_directory` (string) - This is the directoy on your remote esx host where you will save your
|
||||
vm, relative to your remote_datastore.
|
||||
|
||||
This option's default value is your `vm_name`, and the final path of your
|
||||
vm will be vmfs/volumes/$remote_datastore/$vm_name/$vm_name.vmx where
|
||||
`$remote_datastore` and `$vm_name` match their corresponding template
|
||||
options
|
||||
|
||||
For example, setting `"remote_output_directory": "path/to/subdir`
|
||||
will create a directory `/vmfs/volumes/remote_datastore/path/to/subdir`.
|
||||
|
||||
Packer will not create the remote datastore for you; it must already
|
||||
exist. However, Packer will create all directories defined in the option
|
||||
that do not currently exist.
|
||||
|
||||
This option will be ignored unless you are building on a remote esx host.
|
||||
|
||||
Reference in New Issue
Block a user