From 1c5bc41beba6a656d0c75ba1534d5d05e1541e69 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Wed, 26 Aug 2020 15:45:59 -0700 Subject: [PATCH] refactor ovftool validation into vmware driver --- builder/vmware/common/driver.go | 22 +++++++++ builder/vmware/common/driver_config.go | 66 -------------------------- builder/vmware/common/driver_esx5.go | 64 +++++++++++++++++++++++++ builder/vmware/common/driver_mock.go | 6 +++ builder/vmware/iso/builder.go | 5 ++ builder/vmware/vmx/builder.go | 5 ++ 6 files changed, 102 insertions(+), 66 deletions(-) diff --git a/builder/vmware/common/driver.go b/builder/vmware/common/driver.go index 8092ec560..167d3bce6 100644 --- a/builder/vmware/common/driver.go +++ b/builder/vmware/common/driver.go @@ -80,6 +80,9 @@ type Driver interface { // Export the vm to ovf or ova format using ovftool Export([]string) error + + // OvfTool + VerifyOvfTool(bool, bool) error } // NewDriver returns a new driver implementation for this operating @@ -628,3 +631,22 @@ func (d *VmwareDriver) Export(args []string) error { return nil } + +func (d *VmwareDriver) VerifyOvfTool(SkipExport, _ bool) error { + if SkipExport { + return nil + } + + log.Printf("Verifying that ovftool exists...") + // Validate that tool exists, but no need to validate credentials. + ovftool := GetOVFTool() + if ovftool != "" { + return nil + } else { + return fmt.Errorf("Couldn't find ovftool in path! Please either " + + "set `skip_export = true` and remove the `format` option " + + "from your template, or make sure ovftool is installed on " + + "your build system. ") + } + +} diff --git a/builder/vmware/common/driver_config.go b/builder/vmware/common/driver_config.go index 35f9d7187..c87ab3c6c 100644 --- a/builder/vmware/common/driver_config.go +++ b/builder/vmware/common/driver_config.go @@ -3,14 +3,8 @@ package common import ( - "bytes" - "context" "fmt" - "net/url" "os" - "os/exec" - "strings" - "time" "github.com/hashicorp/packer/template/interpolate" ) @@ -93,65 +87,5 @@ func (c *DriverConfig) Validate(SkipExport bool) error { "that you set a value for remote_password") } - if c.RemoteType == "" { - // Validate that tool exists, but no need to validate credentials. - ovftool := GetOVFTool() - if ovftool != "" { - return nil - } else { - return fmt.Errorf("Couldn't find ovftool in path! Please either " + - "set `skip_export = true` and remove the `format` option " + - "from your template, or make sure ovftool is installed on " + - "your build system. ") - } - } - - 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() - - // Generate the uri of the host, with embedded credentials - ovftool_uri := fmt.Sprintf("vi://%s", c.RemoteHost) - u, err := url.Parse(ovftool_uri) - if err != nil { - return fmt.Errorf("Couldn't generate uri for ovftool: %s", err) - } - u.User = url.UserPassword(c.RemoteUser, c.RemotePassword) - - ovfToolArgs := []string{"--noSSLVerify", "--verifyOnly", u.String()} - - 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 } diff --git a/builder/vmware/common/driver_esx5.go b/builder/vmware/common/driver_esx5.go index cb4b2352d..dbece3ff9 100644 --- a/builder/vmware/common/driver_esx5.go +++ b/builder/vmware/common/driver_esx5.go @@ -11,7 +11,9 @@ import ( "io" "log" "net" + "net/url" "os" + "os/exec" "path" "path/filepath" "strconv" @@ -263,6 +265,68 @@ func (d *ESX5Driver) Verify() error { return nil } +func (d *ESX5Driver) VerifyOvfTool(SkipExport, skipValidateCredentials bool) error { + err := d.base.VerifyOvfTool(SkipExport, skipValidateCredentials) + if err != nil { + return err + } + + log.Printf("Verifying that ovftool credentials are valid...") + // 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() + + if skipValidateCredentials { + return nil + } + + if d.Password == "" { + return fmt.Errorf("exporting the vm from esxi with ovftool requires " + + "that you set a value for remote_password") + } + + // Generate the uri of the host, with embedded credentials + ovftool_uri := fmt.Sprintf("vi://%s", d.Host) + u, err := url.Parse(ovftool_uri) + if err != nil { + return fmt.Errorf("Couldn't generate uri for ovftool: %s", err) + } + u.User = url.UserPassword(d.Username, d.Password) + + ovfToolArgs := []string{"--noSSLVerify", "--verifyOnly", u.String()} + + 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 +} + func (d *ESX5Driver) HostIP(multistep.StateBag) (string, error) { conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", d.Host, d.Port)) if err != nil { diff --git a/builder/vmware/common/driver_mock.go b/builder/vmware/common/driver_mock.go index ba5ecc57f..9ece83841 100644 --- a/builder/vmware/common/driver_mock.go +++ b/builder/vmware/common/driver_mock.go @@ -95,6 +95,8 @@ type DriverMock struct { VerifyCalled bool VerifyErr error + + VerifyOvftoolCalled bool } type NetworkMapperMock struct { @@ -279,3 +281,7 @@ func (d *DriverMock) GetVmwareDriver() VmwareDriver { } return state } + +func (d *DriverMock) VerifyOvfTool(_ bool, _ bool) error { + return nil +} diff --git a/builder/vmware/iso/builder.go b/builder/vmware/iso/builder.go index 353cd0199..624bd8c86 100644 --- a/builder/vmware/iso/builder.go +++ b/builder/vmware/iso/builder.go @@ -35,6 +35,11 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack if err != nil { return nil, fmt.Errorf("Failed creating VMware driver: %s", err) } + // Before we get deep into the build, make sure ovftool is present and + // credentials are valid, if we're going to use ovftool. + if err := driver.VerifyOvfTool(b.config.SkipExport, b.config.SkipValidateCredentials); err != nil { + return nil, err + } // Setup the state bag state := new(multistep.BasicStateBag) diff --git a/builder/vmware/vmx/builder.go b/builder/vmware/vmx/builder.go index f9ba881d5..4c303d8bb 100644 --- a/builder/vmware/vmx/builder.go +++ b/builder/vmware/vmx/builder.go @@ -40,6 +40,11 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack if err != nil { return nil, fmt.Errorf("Failed creating VMware driver: %s", err) } + // Before we get deep into the build, make sure ovftool is present and + // credentials are valid, if we're going to use ovftool. + if err := driver.VerifyOvfTool(b.config.SkipExport, b.config.SkipValidateCredentials); err != nil { + return nil, err + } // Set up the state. state := new(multistep.BasicStateBag)