2019-05-31 08:27:41 -04:00
|
|
|
//go:generate struct-markdown
|
|
|
|
|
2013-12-27 10:37:39 -05:00
|
|
|
package common
|
|
|
|
|
|
|
|
import (
|
2018-11-07 12:42:03 -05:00
|
|
|
"bytes"
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"net/url"
|
2014-10-28 11:37:12 -04:00
|
|
|
"os"
|
2018-11-07 12:42:03 -05:00
|
|
|
"os/exec"
|
|
|
|
"strings"
|
|
|
|
"time"
|
2013-12-27 10:37:39 -05:00
|
|
|
|
2017-04-04 16:39:01 -04:00
|
|
|
"github.com/hashicorp/packer/template/interpolate"
|
2013-12-27 10:37:39 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
type DriverConfig struct {
|
2020-03-24 11:16:25 -04:00
|
|
|
// When set to true, Packer will cleanup the cache folder where the ISO file is stored during the build on the remote machine.
|
|
|
|
// By default, this is set to false.
|
|
|
|
CleanUpRemoteCache bool `mapstructure:"cleanup_remote_cache" required:"false"`
|
2019-05-28 11:50:58 -04:00
|
|
|
// Path to "VMware Fusion.app". By default this is
|
2019-06-06 10:29:25 -04:00
|
|
|
// /Applications/VMware Fusion.app but this setting allows you to
|
|
|
|
// customize this.
|
|
|
|
FusionAppPath string `mapstructure:"fusion_app_path" required:"false"`
|
2019-05-28 11:50:58 -04:00
|
|
|
// The type of remote machine that will be used to
|
2019-06-06 10:29:25 -04:00
|
|
|
// build this VM rather than a local desktop product. The only value accepted
|
|
|
|
// for this currently is esx5. If this is not set, a desktop product will
|
|
|
|
// be used. By default, this is not set.
|
|
|
|
RemoteType string `mapstructure:"remote_type" required:"false"`
|
2019-05-28 11:50:58 -04:00
|
|
|
// The path to the datastore where the VM will be stored
|
2019-06-06 10:29:25 -04:00
|
|
|
// on the ESXi machine.
|
|
|
|
RemoteDatastore string `mapstructure:"remote_datastore" required:"false"`
|
2019-05-28 11:50:58 -04:00
|
|
|
// The path to the datastore where supporting files
|
2019-06-06 10:29:25 -04:00
|
|
|
// will be stored during the build on the remote machine.
|
|
|
|
RemoteCacheDatastore string `mapstructure:"remote_cache_datastore" required:"false"`
|
2019-05-28 11:50:58 -04:00
|
|
|
// The path where the ISO and/or floppy files will
|
2019-06-06 10:29:25 -04:00
|
|
|
// be stored during the build on the remote machine. The path is relative to
|
|
|
|
// the remote_cache_datastore on the remote machine.
|
|
|
|
RemoteCacheDirectory string `mapstructure:"remote_cache_directory" required:"false"`
|
2019-05-28 11:50:58 -04:00
|
|
|
// The host of the remote machine used for access.
|
2019-06-06 10:29:25 -04:00
|
|
|
// This is only required if remote_type is enabled.
|
|
|
|
RemoteHost string `mapstructure:"remote_host" required:"false"`
|
2019-05-28 11:50:58 -04:00
|
|
|
// The SSH port of the remote machine
|
2019-06-06 10:29:25 -04:00
|
|
|
RemotePort int `mapstructure:"remote_port" required:"false"`
|
2019-05-28 11:50:58 -04:00
|
|
|
// The SSH username used to access the remote machine.
|
2019-06-06 10:29:25 -04:00
|
|
|
RemoteUser string `mapstructure:"remote_username" required:"false"`
|
2019-05-28 11:50:58 -04:00
|
|
|
// The SSH password for access to the remote machine.
|
2019-06-06 10:29:25 -04:00
|
|
|
RemotePassword string `mapstructure:"remote_password" required:"false"`
|
2019-05-28 11:50:58 -04:00
|
|
|
// The SSH key for access to the remote machine.
|
2019-06-06 10:29:25 -04:00
|
|
|
RemotePrivateKey string `mapstructure:"remote_private_key_file" required:"false"`
|
2019-05-28 11:50:58 -04:00
|
|
|
// When Packer is preparing to run a
|
2019-06-06 10:29:25 -04:00
|
|
|
// 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.
|
|
|
|
SkipValidateCredentials bool `mapstructure:"skip_validate_credentials" required:"false"`
|
2013-12-27 10:37:39 -05:00
|
|
|
}
|
|
|
|
|
2015-05-27 17:16:28 -04:00
|
|
|
func (c *DriverConfig) Prepare(ctx *interpolate.Context) []error {
|
2014-10-28 11:21:39 -04:00
|
|
|
if c.FusionAppPath == "" {
|
|
|
|
c.FusionAppPath = os.Getenv("FUSION_APP_PATH")
|
|
|
|
}
|
2013-12-27 10:37:39 -05:00
|
|
|
if c.FusionAppPath == "" {
|
|
|
|
c.FusionAppPath = "/Applications/VMware Fusion.app"
|
|
|
|
}
|
2017-02-06 02:13:37 -05:00
|
|
|
if c.RemoteUser == "" {
|
|
|
|
c.RemoteUser = "root"
|
|
|
|
}
|
|
|
|
if c.RemoteDatastore == "" {
|
|
|
|
c.RemoteDatastore = "datastore1"
|
|
|
|
}
|
|
|
|
if c.RemoteCacheDatastore == "" {
|
|
|
|
c.RemoteCacheDatastore = c.RemoteDatastore
|
|
|
|
}
|
|
|
|
if c.RemoteCacheDirectory == "" {
|
|
|
|
c.RemoteCacheDirectory = "packer_cache"
|
|
|
|
}
|
|
|
|
if c.RemotePort == 0 {
|
|
|
|
c.RemotePort = 22
|
|
|
|
}
|
2013-12-27 10:37:39 -05:00
|
|
|
|
2015-05-27 17:16:28 -04:00
|
|
|
return nil
|
2013-12-27 10:37:39 -05:00
|
|
|
}
|
2018-11-07 12:42:03 -05:00
|
|
|
|
|
|
|
func (c *DriverConfig) Validate(SkipExport bool) error {
|
2018-11-12 12:46:02 -05:00
|
|
|
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
|
|
|
|
}
|
2018-11-07 12:42:03 -05:00
|
|
|
|
2018-11-12 12:46:02 -05:00
|
|
|
// 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()
|
2020-07-23 20:03:42 -04:00
|
|
|
|
|
|
|
// 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()}
|
2018-11-07 12:42:03 -05:00
|
|
|
|
2018-11-12 12:46:02 -05:00
|
|
|
var out bytes.Buffer
|
|
|
|
cmdCtx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
|
|
|
defer cancel()
|
|
|
|
cmd := exec.CommandContext(cmdCtx, ovftool, ovfToolArgs...)
|
|
|
|
cmd.Stdout = &out
|
2018-11-07 12:42:03 -05:00
|
|
|
|
2018-11-12 12:46:02 -05:00
|
|
|
// 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.")
|
2018-11-07 12:42:03 -05:00
|
|
|
}
|
2018-11-12 12:46:02 -05:00
|
|
|
return err
|
2018-11-07 12:42:03 -05:00
|
|
|
}
|
|
|
|
}
|
2018-11-12 12:46:02 -05:00
|
|
|
|
2018-11-07 12:42:03 -05:00
|
|
|
return nil
|
|
|
|
}
|