Merge pull request #6977 from hashicorp/fix_6425
Validate username/password for ovftool during prepare.
This commit is contained in:
commit
cf293c3310
|
@ -1,22 +1,30 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer/template/interpolate"
|
||||
)
|
||||
|
||||
type DriverConfig struct {
|
||||
FusionAppPath string `mapstructure:"fusion_app_path"`
|
||||
RemoteType string `mapstructure:"remote_type"`
|
||||
RemoteDatastore string `mapstructure:"remote_datastore"`
|
||||
RemoteCacheDatastore string `mapstructure:"remote_cache_datastore"`
|
||||
RemoteCacheDirectory string `mapstructure:"remote_cache_directory"`
|
||||
RemoteHost string `mapstructure:"remote_host"`
|
||||
RemotePort uint `mapstructure:"remote_port"`
|
||||
RemoteUser string `mapstructure:"remote_username"`
|
||||
RemotePassword string `mapstructure:"remote_password"`
|
||||
RemotePrivateKey string `mapstructure:"remote_private_key_file"`
|
||||
FusionAppPath string `mapstructure:"fusion_app_path"`
|
||||
RemoteType string `mapstructure:"remote_type"`
|
||||
RemoteDatastore string `mapstructure:"remote_datastore"`
|
||||
RemoteCacheDatastore string `mapstructure:"remote_cache_datastore"`
|
||||
RemoteCacheDirectory string `mapstructure:"remote_cache_directory"`
|
||||
RemoteHost string `mapstructure:"remote_host"`
|
||||
RemotePort uint `mapstructure:"remote_port"`
|
||||
RemoteUser string `mapstructure:"remote_username"`
|
||||
RemotePassword string `mapstructure:"remote_password"`
|
||||
RemotePrivateKey string `mapstructure:"remote_private_key_file"`
|
||||
SkipValidateCredentials bool `mapstructure:"skip_validate_credentials"`
|
||||
}
|
||||
|
||||
func (c *DriverConfig) Prepare(ctx *interpolate.Context) []error {
|
||||
|
@ -44,3 +52,55 @@ func (c *DriverConfig) Prepare(ctx *interpolate.Context) []error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *DriverConfig) Validate(SkipExport bool) error {
|
||||
if c.RemoteType == "" || SkipExport == true {
|
||||
return nil
|
||||
}
|
||||
if c.RemotePassword == "" {
|
||||
return fmt.Errorf("exporting the vm (with ovftool) requires that " +
|
||||
"you set a value for remote_password")
|
||||
}
|
||||
if c.SkipValidateCredentials {
|
||||
return nil
|
||||
}
|
||||
|
||||
// check that password is valid by sending a dummy ovftool command
|
||||
// now, so that we don't fail for a simple mistake after a long
|
||||
// build
|
||||
ovftool := GetOVFTool()
|
||||
ovfToolArgs := []string{"--verifyOnly", fmt.Sprintf("vi://" +
|
||||
url.QueryEscape(c.RemoteUser) + ":" +
|
||||
url.QueryEscape(c.RemotePassword) + "@" +
|
||||
c.RemoteHost)}
|
||||
|
||||
var out bytes.Buffer
|
||||
cmdCtx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
defer cancel()
|
||||
cmd := exec.CommandContext(cmdCtx, ovftool, ovfToolArgs...)
|
||||
cmd.Stdout = &out
|
||||
|
||||
// Need to manually close stdin or else the ofvtool call will hang
|
||||
// forever in a situation where the user has provided an invalid
|
||||
// password or username
|
||||
stdin, _ := cmd.StdinPipe()
|
||||
defer stdin.Close()
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
outString := out.String()
|
||||
// The command *should* fail with this error, if it
|
||||
// authenticates properly.
|
||||
if !strings.Contains(outString, "Found wrong kind of object") {
|
||||
err := fmt.Errorf("ovftool validation error: %s; %s",
|
||||
err, outString)
|
||||
if strings.Contains(outString,
|
||||
"Enter login information for source") {
|
||||
err = fmt.Errorf("The username or password you " +
|
||||
"provided to ovftool is invalid.")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -26,6 +26,18 @@ type StepExport struct {
|
|||
OutputDir string
|
||||
}
|
||||
|
||||
func GetOVFTool() string {
|
||||
ovftool := "ovftool"
|
||||
if runtime.GOOS == "windows" {
|
||||
ovftool = "ovftool.exe"
|
||||
}
|
||||
|
||||
if _, err := exec.LookPath(ovftool); err != nil {
|
||||
return ""
|
||||
}
|
||||
return ovftool
|
||||
}
|
||||
|
||||
func (s *StepExport) generateArgs(c *DriverConfig, displayName string, hidePassword bool) []string {
|
||||
password := url.QueryEscape(c.RemotePassword)
|
||||
if hidePassword {
|
||||
|
@ -57,13 +69,9 @@ func (s *StepExport) Run(_ context.Context, state multistep.StateBag) multistep.
|
|||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
ovftool := "ovftool"
|
||||
if runtime.GOOS == "windows" {
|
||||
ovftool = "ovftool.exe"
|
||||
}
|
||||
|
||||
if _, err := exec.LookPath(ovftool); err != nil {
|
||||
err = fmt.Errorf("Error %s not found: %s", ovftool, err)
|
||||
ovftool := GetOVFTool()
|
||||
if ovftool == "" {
|
||||
err := fmt.Errorf("Error %s not found: ", ovftool)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
|
|
|
@ -3,20 +3,14 @@ package iso
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
vmwcommon "github.com/hashicorp/packer/builder/vmware/common"
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/common/bootcommand"
|
||||
"github.com/hashicorp/packer/helper/communicator"
|
||||
"github.com/hashicorp/packer/helper/config"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"github.com/hashicorp/packer/template/interpolate"
|
||||
)
|
||||
|
||||
type Builder struct {
|
||||
|
@ -24,208 +18,15 @@ type Builder struct {
|
|||
runner multistep.Runner
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
common.PackerConfig `mapstructure:",squash"`
|
||||
common.HTTPConfig `mapstructure:",squash"`
|
||||
common.ISOConfig `mapstructure:",squash"`
|
||||
common.FloppyConfig `mapstructure:",squash"`
|
||||
bootcommand.VNCConfig `mapstructure:",squash"`
|
||||
vmwcommon.DriverConfig `mapstructure:",squash"`
|
||||
vmwcommon.OutputConfig `mapstructure:",squash"`
|
||||
vmwcommon.RunConfig `mapstructure:",squash"`
|
||||
vmwcommon.ShutdownConfig `mapstructure:",squash"`
|
||||
vmwcommon.SSHConfig `mapstructure:",squash"`
|
||||
vmwcommon.ToolsConfig `mapstructure:",squash"`
|
||||
vmwcommon.VMXConfig `mapstructure:",squash"`
|
||||
vmwcommon.ExportConfig `mapstructure:",squash"`
|
||||
|
||||
// disk drives
|
||||
AdditionalDiskSize []uint `mapstructure:"disk_additional_size"`
|
||||
DiskAdapterType string `mapstructure:"disk_adapter_type"`
|
||||
DiskName string `mapstructure:"vmdk_name"`
|
||||
DiskSize uint `mapstructure:"disk_size"`
|
||||
DiskTypeId string `mapstructure:"disk_type_id"`
|
||||
Format string `mapstructure:"format"`
|
||||
|
||||
// cdrom drive
|
||||
CdromAdapterType string `mapstructure:"cdrom_adapter_type"`
|
||||
|
||||
// platform information
|
||||
GuestOSType string `mapstructure:"guest_os_type"`
|
||||
Version string `mapstructure:"version"`
|
||||
VMName string `mapstructure:"vm_name"`
|
||||
|
||||
// Network adapter and type
|
||||
NetworkAdapterType string `mapstructure:"network_adapter_type"`
|
||||
Network string `mapstructure:"network"`
|
||||
|
||||
// device presence
|
||||
Sound bool `mapstructure:"sound"`
|
||||
USB bool `mapstructure:"usb"`
|
||||
|
||||
// communication ports
|
||||
Serial string `mapstructure:"serial"`
|
||||
Parallel string `mapstructure:"parallel"`
|
||||
|
||||
VMXDiskTemplatePath string `mapstructure:"vmx_disk_template_path"`
|
||||
VMXTemplatePath string `mapstructure:"vmx_template_path"`
|
||||
|
||||
ctx interpolate.Context
|
||||
}
|
||||
|
||||
// Prepare processes the build configuration parameters.
|
||||
func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
||||
err := config.Decode(&b.config, &config.DecodeOpts{
|
||||
Interpolate: true,
|
||||
InterpolateContext: &b.config.ctx,
|
||||
InterpolateFilter: &interpolate.RenderFilter{
|
||||
Exclude: []string{
|
||||
"boot_command",
|
||||
"tools_upload_path",
|
||||
},
|
||||
},
|
||||
}, raws...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Accumulate any errors and warnings
|
||||
var errs *packer.MultiError
|
||||
warnings := make([]string, 0)
|
||||
|
||||
isoWarnings, isoErrs := b.config.ISOConfig.Prepare(&b.config.ctx)
|
||||
warnings = append(warnings, isoWarnings...)
|
||||
errs = packer.MultiErrorAppend(errs, isoErrs...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.HTTPConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.DriverConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs,
|
||||
b.config.OutputConfig.Prepare(&b.config.ctx, &b.config.PackerConfig)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.ShutdownConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.SSHConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.ToolsConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.VMXConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.FloppyConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.VNCConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.ExportConfig.Prepare(&b.config.ctx)...)
|
||||
|
||||
if b.config.DiskName == "" {
|
||||
b.config.DiskName = "disk"
|
||||
}
|
||||
|
||||
if b.config.DiskSize == 0 {
|
||||
b.config.DiskSize = 40000
|
||||
}
|
||||
|
||||
if b.config.DiskAdapterType == "" {
|
||||
// Default is lsilogic
|
||||
b.config.DiskAdapterType = "lsilogic"
|
||||
}
|
||||
|
||||
if !b.config.SkipCompaction {
|
||||
if b.config.RemoteType == "esx5" {
|
||||
if b.config.DiskTypeId == "" {
|
||||
b.config.SkipCompaction = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if b.config.DiskTypeId == "" {
|
||||
// Default is growable virtual disk split in 2GB files.
|
||||
b.config.DiskTypeId = "1"
|
||||
|
||||
if b.config.RemoteType == "esx5" {
|
||||
b.config.DiskTypeId = "zeroedthick"
|
||||
}
|
||||
}
|
||||
|
||||
if b.config.RemoteType == "esx5" {
|
||||
if b.config.DiskTypeId != "thin" && !b.config.SkipCompaction {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, fmt.Errorf("skip_compaction must be 'true' for disk_type_id: %s", b.config.DiskTypeId))
|
||||
}
|
||||
}
|
||||
|
||||
if b.config.GuestOSType == "" {
|
||||
b.config.GuestOSType = "other"
|
||||
}
|
||||
|
||||
if b.config.VMName == "" {
|
||||
b.config.VMName = fmt.Sprintf("packer-%s", b.config.PackerBuildName)
|
||||
}
|
||||
|
||||
if b.config.Version == "" {
|
||||
b.config.Version = "9"
|
||||
}
|
||||
|
||||
if b.config.VMXTemplatePath != "" {
|
||||
if err := b.validateVMXTemplatePath(); err != nil {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, fmt.Errorf("vmx_template_path is invalid: %s", err))
|
||||
}
|
||||
|
||||
} else {
|
||||
warn := b.checkForVMXTemplateAndVMXDataCollisions()
|
||||
if warn != "" {
|
||||
warnings = append(warnings, warn)
|
||||
}
|
||||
}
|
||||
|
||||
if b.config.Network == "" {
|
||||
b.config.Network = "nat"
|
||||
}
|
||||
|
||||
if !b.config.Sound {
|
||||
b.config.Sound = false
|
||||
}
|
||||
|
||||
if !b.config.USB {
|
||||
b.config.USB = false
|
||||
}
|
||||
|
||||
// Remote configuration validation
|
||||
if b.config.RemoteType != "" {
|
||||
if b.config.RemoteHost == "" {
|
||||
errs = packer.MultiErrorAppend(errs,
|
||||
fmt.Errorf("remote_host must be specified"))
|
||||
}
|
||||
|
||||
if b.config.RemoteType != "esx5" {
|
||||
errs = packer.MultiErrorAppend(errs,
|
||||
fmt.Errorf("Only 'esx5' value is accepted for remote_type"))
|
||||
}
|
||||
}
|
||||
|
||||
if b.config.Format == "" {
|
||||
b.config.Format = "ovf"
|
||||
}
|
||||
|
||||
if !(b.config.Format == "ova" || b.config.Format == "ovf" || b.config.Format == "vmx") {
|
||||
errs = packer.MultiErrorAppend(errs,
|
||||
fmt.Errorf("format must be one of ova, ovf, or vmx"))
|
||||
}
|
||||
|
||||
if b.config.RemoteType == "esx5" && b.config.SkipExport != true && b.config.RemotePassword == "" {
|
||||
errs = packer.MultiErrorAppend(errs,
|
||||
fmt.Errorf("exporting the vm (with ovftool) requires that you set a value for remote_password"))
|
||||
}
|
||||
|
||||
// Warnings
|
||||
if b.config.ShutdownCommand == "" {
|
||||
warnings = append(warnings,
|
||||
"A shutdown_command was not specified. Without a shutdown command, Packer\n"+
|
||||
"will forcibly halt the virtual machine, which may result in data loss.")
|
||||
}
|
||||
|
||||
if b.config.Headless && b.config.DisableVNC {
|
||||
warnings = append(warnings,
|
||||
"Headless mode uses VNC to retrieve output. Since VNC has been disabled,\n"+
|
||||
"you won't be able to see any output.")
|
||||
}
|
||||
|
||||
if errs != nil && len(errs.Errors) > 0 {
|
||||
c, warnings, errs := NewConfig(raws...)
|
||||
if errs != nil {
|
||||
return warnings, errs
|
||||
}
|
||||
|
||||
b.config = *c
|
||||
|
||||
return warnings, nil
|
||||
}
|
||||
|
||||
|
@ -406,56 +207,3 @@ func (b *Builder) Cancel() {
|
|||
b.runner.Cancel()
|
||||
}
|
||||
}
|
||||
|
||||
// Validate the vmx_data option against the default vmx template to warn
|
||||
// user if anything is being overridden.
|
||||
func (b *Builder) checkForVMXTemplateAndVMXDataCollisions() string {
|
||||
if b.config.VMXTemplatePath != "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
var overridden []string
|
||||
tplLines := strings.Split(DefaultVMXTemplate, "\n")
|
||||
tplLines = append(tplLines,
|
||||
fmt.Sprintf("%s0:0.present", strings.ToLower(b.config.DiskAdapterType)),
|
||||
fmt.Sprintf("%s0:0.fileName", strings.ToLower(b.config.DiskAdapterType)),
|
||||
fmt.Sprintf("%s0:0.deviceType", strings.ToLower(b.config.DiskAdapterType)),
|
||||
fmt.Sprintf("%s0:1.present", strings.ToLower(b.config.DiskAdapterType)),
|
||||
fmt.Sprintf("%s0:1.fileName", strings.ToLower(b.config.DiskAdapterType)),
|
||||
fmt.Sprintf("%s0:1.deviceType", strings.ToLower(b.config.DiskAdapterType)),
|
||||
)
|
||||
|
||||
for _, line := range tplLines {
|
||||
if strings.Contains(line, `{{`) {
|
||||
key := line[:strings.Index(line, " =")]
|
||||
if _, ok := b.config.VMXData[key]; ok {
|
||||
overridden = append(overridden, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(overridden) > 0 {
|
||||
warnings := fmt.Sprintf("Your vmx data contains the following "+
|
||||
"variable(s), which Packer normally sets when it generates its "+
|
||||
"own default vmx template. This may cause your build to fail or "+
|
||||
"behave unpredictably: %s", strings.Join(overridden, ", "))
|
||||
return warnings
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Make sure custom vmx template exists and that data can be read from it
|
||||
func (b *Builder) validateVMXTemplatePath() error {
|
||||
f, err := os.Open(b.config.VMXTemplatePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
data, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return interpolate.Validate(string(data), &b.config.ctx)
|
||||
}
|
||||
|
|
|
@ -146,6 +146,7 @@ func TestBuilderPrepare_RemoteType(t *testing.T) {
|
|||
config["format"] = "ovf"
|
||||
config["remote_host"] = "foobar.example.com"
|
||||
config["remote_password"] = "supersecret"
|
||||
config["skip_validate_credentials"] = true
|
||||
// Bad
|
||||
config["remote_type"] = "foobar"
|
||||
warns, err := b.Prepare(config)
|
||||
|
@ -202,6 +203,7 @@ func TestBuilderPrepare_RemoteExport(t *testing.T) {
|
|||
|
||||
config["remote_type"] = "esx5"
|
||||
config["remote_host"] = "foobar.example.com"
|
||||
config["skip_validate_credentials"] = true
|
||||
// Bad
|
||||
config["remote_password"] = ""
|
||||
warns, err := b.Prepare(config)
|
||||
|
|
|
@ -0,0 +1,271 @@
|
|||
package iso
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
vmwcommon "github.com/hashicorp/packer/builder/vmware/common"
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/common/bootcommand"
|
||||
"github.com/hashicorp/packer/helper/config"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"github.com/hashicorp/packer/template/interpolate"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
common.PackerConfig `mapstructure:",squash"`
|
||||
common.HTTPConfig `mapstructure:",squash"`
|
||||
common.ISOConfig `mapstructure:",squash"`
|
||||
common.FloppyConfig `mapstructure:",squash"`
|
||||
bootcommand.VNCConfig `mapstructure:",squash"`
|
||||
vmwcommon.DriverConfig `mapstructure:",squash"`
|
||||
vmwcommon.OutputConfig `mapstructure:",squash"`
|
||||
vmwcommon.RunConfig `mapstructure:",squash"`
|
||||
vmwcommon.ShutdownConfig `mapstructure:",squash"`
|
||||
vmwcommon.SSHConfig `mapstructure:",squash"`
|
||||
vmwcommon.ToolsConfig `mapstructure:",squash"`
|
||||
vmwcommon.VMXConfig `mapstructure:",squash"`
|
||||
vmwcommon.ExportConfig `mapstructure:",squash"`
|
||||
|
||||
// disk drives
|
||||
AdditionalDiskSize []uint `mapstructure:"disk_additional_size"`
|
||||
DiskAdapterType string `mapstructure:"disk_adapter_type"`
|
||||
DiskName string `mapstructure:"vmdk_name"`
|
||||
DiskSize uint `mapstructure:"disk_size"`
|
||||
DiskTypeId string `mapstructure:"disk_type_id"`
|
||||
Format string `mapstructure:"format"`
|
||||
|
||||
// cdrom drive
|
||||
CdromAdapterType string `mapstructure:"cdrom_adapter_type"`
|
||||
|
||||
// platform information
|
||||
GuestOSType string `mapstructure:"guest_os_type"`
|
||||
Version string `mapstructure:"version"`
|
||||
VMName string `mapstructure:"vm_name"`
|
||||
|
||||
// Network adapter and type
|
||||
NetworkAdapterType string `mapstructure:"network_adapter_type"`
|
||||
Network string `mapstructure:"network"`
|
||||
|
||||
// device presence
|
||||
Sound bool `mapstructure:"sound"`
|
||||
USB bool `mapstructure:"usb"`
|
||||
|
||||
// communication ports
|
||||
Serial string `mapstructure:"serial"`
|
||||
Parallel string `mapstructure:"parallel"`
|
||||
|
||||
VMXDiskTemplatePath string `mapstructure:"vmx_disk_template_path"`
|
||||
VMXTemplatePath string `mapstructure:"vmx_template_path"`
|
||||
|
||||
ctx interpolate.Context
|
||||
}
|
||||
|
||||
func NewConfig(raws ...interface{}) (*Config, []string, error) {
|
||||
c := new(Config)
|
||||
err := config.Decode(c, &config.DecodeOpts{
|
||||
Interpolate: true,
|
||||
InterpolateContext: &c.ctx,
|
||||
InterpolateFilter: &interpolate.RenderFilter{
|
||||
Exclude: []string{
|
||||
"boot_command",
|
||||
"tools_upload_path",
|
||||
},
|
||||
},
|
||||
}, raws...)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Accumulate any errors and warnings
|
||||
var errs *packer.MultiError
|
||||
warnings := make([]string, 0)
|
||||
|
||||
isoWarnings, isoErrs := c.ISOConfig.Prepare(&c.ctx)
|
||||
warnings = append(warnings, isoWarnings...)
|
||||
errs = packer.MultiErrorAppend(errs, isoErrs...)
|
||||
errs = packer.MultiErrorAppend(errs, c.HTTPConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.DriverConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs,
|
||||
c.OutputConfig.Prepare(&c.ctx, &c.PackerConfig)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.RunConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.ShutdownConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.SSHConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.ToolsConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.VMXConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.FloppyConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.VNCConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.ExportConfig.Prepare(&c.ctx)...)
|
||||
|
||||
if c.DiskName == "" {
|
||||
c.DiskName = "disk"
|
||||
}
|
||||
|
||||
if c.DiskSize == 0 {
|
||||
c.DiskSize = 40000
|
||||
}
|
||||
|
||||
if c.DiskAdapterType == "" {
|
||||
// Default is lsilogic
|
||||
c.DiskAdapterType = "lsilogic"
|
||||
}
|
||||
|
||||
if !c.SkipCompaction {
|
||||
if c.RemoteType == "esx5" {
|
||||
if c.DiskTypeId == "" {
|
||||
c.SkipCompaction = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if c.DiskTypeId == "" {
|
||||
// Default is growable virtual disk split in 2GB files.
|
||||
c.DiskTypeId = "1"
|
||||
|
||||
if c.RemoteType == "esx5" {
|
||||
c.DiskTypeId = "zeroedthick"
|
||||
}
|
||||
}
|
||||
|
||||
if c.RemoteType == "esx5" {
|
||||
if c.DiskTypeId != "thin" && !c.SkipCompaction {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, fmt.Errorf("skip_compaction must be 'true' for disk_type_id: %s", c.DiskTypeId))
|
||||
}
|
||||
}
|
||||
|
||||
if c.GuestOSType == "" {
|
||||
c.GuestOSType = "other"
|
||||
}
|
||||
|
||||
if c.VMName == "" {
|
||||
c.VMName = fmt.Sprintf("packer-%s", c.PackerBuildName)
|
||||
}
|
||||
|
||||
if c.Version == "" {
|
||||
c.Version = "9"
|
||||
}
|
||||
|
||||
if c.VMXTemplatePath != "" {
|
||||
if err := c.validateVMXTemplatePath(); err != nil {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, fmt.Errorf("vmx_template_path is invalid: %s", err))
|
||||
}
|
||||
} else {
|
||||
warn := c.checkForVMXTemplateAndVMXDataCollisions()
|
||||
if warn != "" {
|
||||
warnings = append(warnings, warn)
|
||||
}
|
||||
}
|
||||
|
||||
if c.Network == "" {
|
||||
c.Network = "nat"
|
||||
}
|
||||
|
||||
if !c.Sound {
|
||||
c.Sound = false
|
||||
}
|
||||
|
||||
if !c.USB {
|
||||
c.USB = false
|
||||
}
|
||||
|
||||
// Remote configuration validation
|
||||
if c.RemoteType != "" {
|
||||
if c.RemoteHost == "" {
|
||||
errs = packer.MultiErrorAppend(errs,
|
||||
fmt.Errorf("remote_host must be specified"))
|
||||
}
|
||||
|
||||
if c.RemoteType != "esx5" {
|
||||
errs = packer.MultiErrorAppend(errs,
|
||||
fmt.Errorf("Only 'esx5' value is accepted for remote_type"))
|
||||
}
|
||||
}
|
||||
|
||||
if c.Format == "" {
|
||||
c.Format = "ovf"
|
||||
}
|
||||
|
||||
if !(c.Format == "ova" || c.Format == "ovf" || c.Format == "vmx") {
|
||||
errs = packer.MultiErrorAppend(errs,
|
||||
fmt.Errorf("format must be one of ova, ovf, or vmx"))
|
||||
}
|
||||
|
||||
err = c.DriverConfig.Validate(c.SkipExport)
|
||||
if err != nil {
|
||||
errs = packer.MultiErrorAppend(errs, err)
|
||||
}
|
||||
|
||||
// Warnings
|
||||
if c.ShutdownCommand == "" {
|
||||
warnings = append(warnings,
|
||||
"A shutdown_command was not specified. Without a shutdown command, Packer\n"+
|
||||
"will forcibly halt the virtual machine, which may result in data loss.")
|
||||
}
|
||||
|
||||
if c.Headless && c.DisableVNC {
|
||||
warnings = append(warnings,
|
||||
"Headless mode uses VNC to retrieve output. Since VNC has been disabled,\n"+
|
||||
"you won't be able to see any output.")
|
||||
}
|
||||
|
||||
if errs != nil && len(errs.Errors) > 0 {
|
||||
return nil, warnings, errs
|
||||
}
|
||||
|
||||
return c, warnings, nil
|
||||
}
|
||||
|
||||
func (c *Config) checkForVMXTemplateAndVMXDataCollisions() string {
|
||||
if c.VMXTemplatePath != "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
var overridden []string
|
||||
tplLines := strings.Split(DefaultVMXTemplate, "\n")
|
||||
tplLines = append(tplLines,
|
||||
fmt.Sprintf("%s0:0.present", strings.ToLower(c.DiskAdapterType)),
|
||||
fmt.Sprintf("%s0:0.fileName", strings.ToLower(c.DiskAdapterType)),
|
||||
fmt.Sprintf("%s0:0.deviceType", strings.ToLower(c.DiskAdapterType)),
|
||||
fmt.Sprintf("%s0:1.present", strings.ToLower(c.DiskAdapterType)),
|
||||
fmt.Sprintf("%s0:1.fileName", strings.ToLower(c.DiskAdapterType)),
|
||||
fmt.Sprintf("%s0:1.deviceType", strings.ToLower(c.DiskAdapterType)),
|
||||
)
|
||||
|
||||
for _, line := range tplLines {
|
||||
if strings.Contains(line, `{{`) {
|
||||
key := line[:strings.Index(line, " =")]
|
||||
if _, ok := c.VMXData[key]; ok {
|
||||
overridden = append(overridden, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(overridden) > 0 {
|
||||
warnings := fmt.Sprintf("Your vmx data contains the following "+
|
||||
"variable(s), which Packer normally sets when it generates its "+
|
||||
"own default vmx template. This may cause your build to fail or "+
|
||||
"behave unpredictably: %s", strings.Join(overridden, ", "))
|
||||
return warnings
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Make sure custom vmx template exists and that data can be read from it
|
||||
func (c *Config) validateVMXTemplatePath() error {
|
||||
f, err := os.Open(c.VMXTemplatePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
data, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return interpolate.Validate(string(data), &c.ctx)
|
||||
}
|
|
@ -93,6 +93,11 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
|
|||
}
|
||||
}
|
||||
|
||||
err = c.DriverConfig.Validate(c.SkipExport)
|
||||
if err != nil {
|
||||
errs = packer.MultiErrorAppend(errs, err)
|
||||
}
|
||||
|
||||
if c.Format == "" {
|
||||
c.Format = "ovf"
|
||||
}
|
||||
|
|
|
@ -365,6 +365,12 @@ builder.
|
|||
Hypervisor](/docs/builders/vmware-iso.html#building-on-a-remote-vsphere-hypervisor)
|
||||
section below for more info.
|
||||
|
||||
- `skip_validate_credentials` (boolean) - When Packer is preparing to run a
|
||||
remote esxi build, and export is not disable, by default it runs a no-op
|
||||
ovftool command to make sure that the remote_username and remote_password
|
||||
given are valid. If you set this flag to `true`, Packer will skip this
|
||||
validation. Default: `false`.
|
||||
|
||||
- `sound` (boolean) - Enable VMware's virtual soundcard device for the VM.
|
||||
|
||||
- `tools_upload_flavor` (string) - The flavor of the VMware Tools ISO to
|
||||
|
|
|
@ -132,6 +132,12 @@ builder.
|
|||
the builder. By default this is `output-BUILDNAME` where "BUILDNAME" is the
|
||||
name of the build.
|
||||
|
||||
- `skip_validate_credentials` (boolean) - When Packer is preparing to run a
|
||||
remote esxi build, and export is not disable, by default it runs a no-op
|
||||
ovftool command to make sure that the remote_username and remote_password
|
||||
given are valid. If you set this flag to `true`, Packer will skip this
|
||||
validation. Default: `false`.
|
||||
|
||||
- `remote_cache_datastore` (string) - The path to the datastore where
|
||||
supporting files will be stored during the build on the remote machine. By
|
||||
default this is the same as the `remote_datastore` option. This only has an
|
||||
|
|
Loading…
Reference in New Issue