diff --git a/builder/vmware/common/driver.go b/builder/vmware/common/driver.go index d2ccff0f..1046d858 100644 --- a/builder/vmware/common/driver.go +++ b/builder/vmware/common/driver.go @@ -14,7 +14,6 @@ import ( "os/exec" "regexp" "runtime" - "strconv" "strings" "time" @@ -23,6 +22,20 @@ import ( ) const ( + // VMware Fusion. + fusionProductName = "VMware Fusion" + fusionMinVersion = "13.5.0" + + // VMware Workstation. + workstationProductName = "VMware Workstation" + workstationMinVersion = "17.5.0" + + // VMware Workstation Player. + playerProductName = "VMware Workstation Player" + playerMinVersion = "17.5.0" + playerInstallationPathKey = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\vmplayer.exe" + playerRegistryKey = "SYSTEM\\CurrentControlSet\\services\\VMnetDHCP\\Parameters" + // OVF Tool. ovfToolDownloadURL = "https://developer.broadcom.com/tools/open-virtualization-format-ovf-tool/latest" ovfToolMinVersion = "4.6.0" @@ -63,6 +76,14 @@ const ( netmapConfFile = "netmap.conf" ) +// Initialize version objects +var ( + fusionMinVersionObj = version.Must(version.NewVersion(fusionMinVersion)) + workstationMinVersionObj = version.Must(version.NewVersion(workstationMinVersion)) + playerMinVersionObj = version.Must(version.NewVersion(playerMinVersion)) + ovfToolMinVersionObj = version.Must(version.NewVersion(ovfToolMinVersion)) +) + // The possible paths to the DHCP leases file. var dhcpLeasesPaths = []string{ "dhcp/dhcp.leases", @@ -88,9 +109,6 @@ var technicalPreview = regexp.MustCompile(technicalPreviewRegex) // The VMware OVF Tool version. var ovfToolVersion = regexp.MustCompile(ovfToolVersionRegex) -// The minimum recommended version of the VMware OVF Tool. -var ovfToolMinRecommended = version.Must(version.NewVersion(ovfToolMinVersion)) - // A driver is able to talk to VMware, control virtual machines, etc. type Driver interface { // Clone clones the VMX and the disk to the destination path. The @@ -162,8 +180,6 @@ type Driver interface { VerifyOvfTool(bool, bool) error } -// NewDriver returns a new driver implementation for this operating -// system, or an error if the driver couldn't be initialized. func NewDriver(dconfig *DriverConfig, config *SSHConfig, vmName string) (Driver, error) { var drivers []Driver @@ -212,7 +228,7 @@ func NewDriver(dconfig *DriverConfig, config *SSHConfig, vmName string) (Driver, func runAndLog(cmd *exec.Cmd) (string, string, error) { var stdout, stderr bytes.Buffer - log.Printf("Executing: %s %s", cmd.Path, strings.Join(cmd.Args[1:], " ")) + log.Printf("[INFO] Running: %s %s", cmd.Path, strings.Join(cmd.Args[1:], " ")) cmd.Stdout = &stdout cmd.Stderr = &stderr err := cmd.Run() @@ -256,38 +272,11 @@ func runAndLog(cmd *exec.Cmd) (string, string, error) { return returnStdout, returnStderr, err } -// Still used for Workstation and Player until conversion. -func normalizeVersion(version string) (string, error) { - i, err := strconv.Atoi(version) - if err != nil { - return "", fmt.Errorf("returned a non-integer version %q: %s", version, err) - } - - return fmt.Sprintf("%02d", i), nil -} - -// Still used for Workstation and Player until conversion. -func compareVersions(versionFound string, versionWanted string, product string) error { - found, err := normalizeVersion(versionFound) - if err != nil { - return err - } - - wanted, err := normalizeVersion(versionWanted) - if err != nil { - return err - } - - if found < wanted { - return fmt.Errorf("requires %s or later, found %s", versionWanted, versionFound) - } - - return nil -} - -func compareVersionObjects(versionFound *version.Version, versionWanted *version.Version, product string) error { - if versionFound.LessThan(versionWanted) { - return fmt.Errorf("requires %s or later, found %s", versionWanted.String(), versionFound.String()) +// compareVersionObjects compares two version.Version objects and returns an +// error if the found version is less than the required version. +func compareVersionObjects(versionFound, versionRequired *version.Version, product string) error { + if versionFound.LessThan(versionRequired) { + return fmt.Errorf("[ERROR] Requires %s %s or later; %s installed", product, versionRequired.String(), versionFound.String()) } return nil } @@ -375,7 +364,7 @@ func (d *VmwareDriver) GuestAddress(state multistep.StateBag) (string, error) { return "", errors.New("unable to determine MAC address") } } - log.Printf("GuestAddress discovered MAC address: %s", macAddress) + log.Printf("[INFO] GuestAddress discovered MAC address: %s", macAddress) res, err := net.ParseMAC(macAddress) if err != nil { @@ -399,7 +388,7 @@ func (d *VmwareDriver) PotentialGuestIP(state multistep.StateBag) ([]string, err // log them to see what was detected for _, device := range devices { - log.Printf("GuestIP discovered device matching %s: %s", network, device) + log.Printf("[INFO] GuestIP discovered device matching %s: %s", network, device) } // we were unable to find the device, maybe it's a custom one... @@ -417,7 +406,7 @@ func (d *VmwareDriver) PotentialGuestIP(state multistep.StateBag) ([]string, err if err != nil { return []string{}, err } - log.Printf("GuestIP discovered custom device matching %s: %s", network, device) + log.Printf("[INFO] GuestIP discovered custom device matching %s: %s", network, device) } // figure out our MAC address for looking up the guest address @@ -433,7 +422,7 @@ func (d *VmwareDriver) PotentialGuestIP(state multistep.StateBag) ([]string, err for _, device := range devices { // figure out the correct dhcp leases dhcpLeasesPath := d.DhcpLeasesPath(device) - log.Printf("Trying DHCP leases path: %s", dhcpLeasesPath) + log.Printf("[INFO] Trying DHCP leases path: %s", dhcpLeasesPath) if dhcpLeasesPath == "" { return []string{}, fmt.Errorf("no DHCP leases path found for device %s", device) } @@ -518,7 +507,7 @@ func (d *VmwareDriver) PotentialGuestIP(state multistep.StateBag) ([]string, err // set the apple dhcp leases path appleDhcpLeasesPath := "/var/db/dhcpd_leases" - log.Printf("Trying Apple DHCP leases path: %s", appleDhcpLeasesPath) + log.Printf("[INFO] Trying Apple DHCP leases path: %s", appleDhcpLeasesPath) // open up the path to the apple dhcpd leases fh, err := os.Open(appleDhcpLeasesPath) @@ -577,7 +566,7 @@ func (d *VmwareDriver) HostAddress(state multistep.StateBag) (string, error) { // log them to see what was detected for _, device := range devices { - log.Printf("HostAddress discovered device matching %s: %s", network, device) + log.Printf("[INFO] HostAddress discovered device matching %s: %s", network, device) } // we were unable to find the device, maybe it's a custom one... @@ -595,7 +584,7 @@ func (d *VmwareDriver) HostAddress(state multistep.StateBag) (string, error) { if err != nil { return "", err } - log.Printf("HostAddress discovered custom device matching %s: %s", network, device) + log.Printf("[INFO] HostAddress discovered custom device matching %s: %s", network, device) } var lastError error @@ -657,7 +646,7 @@ func (d *VmwareDriver) HostIP(state multistep.StateBag) (string, error) { // log them to see what was detected for _, device := range devices { - log.Printf("HostIP discovered device matching %s: %s", network, device) + log.Printf("[INFO] HostIP discovered device matching %s: %s", network, device) } // we were unable to find the device, maybe it's a custom one... @@ -675,7 +664,7 @@ func (d *VmwareDriver) HostIP(state multistep.StateBag) (string, error) { if err != nil { return "", err } - log.Printf("HostIP discovered custom device matching %s: %s", network, device) + log.Printf("[INFO] HostIP discovered custom device matching %s: %s", network, device) } var lastError error @@ -740,7 +729,7 @@ func CheckOvfToolVersion(ovftoolPath string) error { return errors.New("failed to execute ovftool") } versionOutput := string(output) - log.Printf("Returned ovftool version: %s.", versionOutput) + log.Printf("[INFO] Returned ovftool version: %s.", versionOutput) versionString := ovfToolVersion.FindString(versionOutput) if versionString == "" { @@ -753,8 +742,8 @@ func CheckOvfToolVersion(ovftoolPath string) error { return fmt.Errorf("failed to parse ovftool version: %v", err) } - if currentVersion.LessThan(ovfToolMinRecommended) { - log.Printf("[WARN] The version of ovftool (%s) is below the minimum recommended version (%s). Please download the latest version from %s.", currentVersion, ovfToolMinRecommended, ovfToolDownloadURL) + if currentVersion.LessThan(ovfToolMinVersionObj) { + log.Printf("[WARN] The version of ovftool (%s) is below the minimum recommended version (%s). Please download the latest version from %s.", currentVersion, ovfToolMinVersionObj, ovfToolDownloadURL) // Log a warning; do not return an error. // TODO: Transition this to an error in a future major release. } @@ -780,13 +769,13 @@ func (d *VmwareDriver) VerifyOvfTool(SkipExport, _ bool) error { return nil } - log.Printf("Verifying that ovftool exists...") + log.Printf("[INFO] Verifying that ovftool exists...") ovftoolPath := GetOvfTool() if ovftoolPath == "" { return errors.New("ovftool not found; install and include it in your PATH") } - log.Printf("Checking ovftool version...") + log.Printf("[INFO] Checking ovftool version...") if err := CheckOvfToolVersion(ovftoolPath); err != nil { return fmt.Errorf("%v", err) } diff --git a/builder/vmware/common/driver_fusion.go b/builder/vmware/common/driver_fusion.go index f5509b70..613dc1fe 100644 --- a/builder/vmware/common/driver_fusion.go +++ b/builder/vmware/common/driver_fusion.go @@ -17,22 +17,6 @@ import ( "github.com/hashicorp/packer-plugin-sdk/multistep" ) -const ( - // VMware Fusion application name. - fusionProductName = "VMware Fusion" - - // VMware Fusion versions. - // TODO: Update to best effort comply with the Broadcom Product Lifecycle. - minimumFusionVersion = "6.0.0" - isoPathChangeFusionVersion = "13.0.0" -) - -// Initialize version objects -var ( - minimumFusionVersionObj, _ = version.NewVersion(minimumFusionVersion) - isoPathChangeFusionVersionObj, _ = version.NewVersion(isoPathChangeFusionVersion) -) - const fusionSuppressPlist = ` @@ -206,13 +190,13 @@ func (d *FusionDriver) Clone(dst, src string, linked bool, snapshot string) erro } func (d *FusionDriver) Verify() error { - version, err := d.getFusionVersion() + fusionVersion, err := d.getFusionVersion() log.Printf("[INFO] Checking %s version...", fusionProductName) if err != nil { return fmt.Errorf("error getting %s version: %s", fusionProductName, err) } - log.Printf("[INFO] %s: %s", fusionProductName, version) + log.Printf("[INFO] %s: %s", fusionProductName, fusionVersion) log.Printf("[INFO] Checking %s paths...", fusionProductName) if _, err := os.Stat(d.AppPath); err != nil { @@ -283,38 +267,19 @@ func (d *FusionDriver) Verify() error { return ReadNetworkingConfig(fd) } - return compareVersionObjects(version, minimumFusionVersionObj, fusionProductName) + return compareVersionObjects(fusionVersion, fusionMinVersionObj, fusionProductName) } func (d *FusionDriver) ToolsIsoPath(k string) string { - versionStr, err := d.getFusionVersion() - if err != nil { - log.Printf("[WARN] Unable to return %s version: %s. Using the default path.", fusionProductName, err) - return d.toolsIsoPath(archAMD64, d.isoFileName(k)) - } - - versionMatch := productVersion.FindStringSubmatch(versionStr.String()) - if len(versionMatch) < 2 { - log.Printf("[WARN] Unable to extract version from string: %s. Using the default path.", versionStr) - return d.toolsIsoPath(archAMD64, d.isoFileName(k)) - } - - parsedVersion, err := version.NewVersion(versionMatch[1]) - if err != nil { - log.Printf("[WARN] Unable to parse %s version: %s. Using the default path.", fusionProductName, err) - return d.toolsIsoPath(archAMD64, d.isoFileName(k)) - } - - if isoPathChangeFusionVersionObj == nil { - log.Printf("[WARN] Unable to parse %s version for comparison. Using the default path.", fusionProductName) - return d.toolsIsoPath(archAMD64, d.isoFileName(k)) - } - - arch := archAMD64 - if parsedVersion.GreaterThanOrEqual(isoPathChangeFusionVersionObj) && k == osWindows && runtime.GOARCH == archARM64 { + var arch string + switch runtime.GOARCH { + case "amd64": + arch = archAMD64 + default: arch = archARM64 } + log.Printf("[INFO] Selected architecture: %s", arch) return d.toolsIsoPath(arch, d.isoFileName(k)) } diff --git a/builder/vmware/common/driver_player.go b/builder/vmware/common/driver_player.go index 85b4f070..942aed9b 100644 --- a/builder/vmware/common/driver_player.go +++ b/builder/vmware/common/driver_player.go @@ -14,15 +14,6 @@ import ( "github.com/hashicorp/packer-plugin-sdk/multistep" ) -const ( - // VMware Workstation Player application name. - playerProductName = "VMware Workstation Player" - - // VMware Workstation Player versions. - // TODO: Update to best effort comply with the Broadcom Product Lifecycle. - minimumPlayerVersion = "6.0.0" -) - // PlayerDriver is a driver for VMware Workstation Player. type PlayerDriver struct { VmwareDriver @@ -159,21 +150,35 @@ func (d *PlayerDriver) SuppressMessages(vmxPath string) error { func (d *PlayerDriver) Verify() error { var err error - log.Printf("[INFO] Checking %s paths...", playerProductName) + log.Printf("[INFO] Searching for %s...", playerProductName) - if d.AppPath == "" { - if d.AppPath, err = playerFindVmplayer(); err != nil { - return fmt.Errorf("%s not found: %s", playerProductName, err) - } + if err := playerVerifyVersion(playerMinVersionObj.String()); err != nil { + return fmt.Errorf("version verification failed: %s", err) + } + + components := map[string]*string{ + appPlayer: &d.AppPath, + appVmrun: &d.VmrunPath, } - log.Printf("[INFO] - %s app path: %s", playerProductName, d.AppPath) - if d.VmrunPath == "" { - if d.VmrunPath, err = playerFindVmrun(); err != nil { - return fmt.Errorf("%s not found: %s", appVmrun, err) + for name, path := range components { + if *path == "" { + var finderFunc func() (string, error) + switch name { + case appPlayer: + finderFunc = playerFindVmplayer + case appVmrun: + finderFunc = playerFindVmrun + } + + if foundPath, err := finderFunc(); err != nil { + return fmt.Errorf("%s not found: %s", name, err) + } else { + *path = foundPath + log.Printf("[INFO] - %s found at: %s", name, *path) + } } } - log.Printf("[INFO] - %s found at: %s", appVmrun, d.VmrunPath) if d.VdiskManagerPath == "" { d.VdiskManagerPath, err = playerFindVdiskManager() @@ -187,44 +192,21 @@ func (d *PlayerDriver) Verify() error { return fmt.Errorf("error finding either %s or %s: %s", appVdiskManager, appQemuImg, err) } - log.Printf("[INFO] - %s found at: %s", appVdiskManager, d.VdiskManagerPath) - log.Printf("[INFO] - %s found at: %s", appQemuImg, d.QemuImgPath) - - if _, err := os.Stat(d.AppPath); err != nil { - if os.IsNotExist(err) { - return fmt.Errorf("%s not found at: %s", playerProductName, d.AppPath) - } - return err - } - log.Printf("[INFO] - %s found at: %s", playerProductName, d.AppPath) - - if _, err := os.Stat(d.VmrunPath); err != nil { - if os.IsNotExist(err) { - return fmt.Errorf("%s not found at: %s", appVmrun, d.VmrunPath) - } - return err - } - log.Printf("[INFO] - %s found at: %s", appVmrun, d.VmrunPath) - - if d.VdiskManagerPath != "" { - if _, err := os.Stat(d.VdiskManagerPath); err != nil { - if os.IsNotExist(err) { - return fmt.Errorf("%s not found at: %s", appVdiskManager, d.VdiskManagerPath) + for name, path := range map[string]string{ + appVdiskManager: d.VdiskManagerPath, + appQemuImg: d.QemuImgPath, + } { + if path != "" { + if _, err := os.Stat(path); err != nil { + if os.IsNotExist(err) { + return fmt.Errorf("%s not found at: %s", name, path) + } + return err } - return err + log.Printf("[INFO] - %s found at: %s", name, path) } - log.Printf("[INFO] - %s found at: %s", appVdiskManager, d.VdiskManagerPath) - } else { - if _, err := os.Stat(d.QemuImgPath); err != nil { - if os.IsNotExist(err) { - return fmt.Errorf("%s not found at: %s", appQemuImg, d.QemuImgPath) - } - return err - } - log.Printf("[INFO] - %s found at: %s", appQemuImg, d.QemuImgPath) } - // Assigning the path callbacks to VmwareDriver d.VmwareDriver.DhcpLeasesPath = func(device string) string { return playerDhcpLeasesPath(device) } @@ -261,7 +243,7 @@ func (d *PlayerDriver) Verify() error { return ReadNetworkingConfig(fd) } - return playerVerifyVersion(minimumPlayerVersion) + return nil } func (d *PlayerDriver) ToolsIsoPath(flavor string) string { diff --git a/builder/vmware/common/driver_player_unix.go b/builder/vmware/common/driver_player_unix.go index f1f235ed..643d5c3a 100644 --- a/builder/vmware/common/driver_player_unix.go +++ b/builder/vmware/common/driver_player_unix.go @@ -14,6 +14,8 @@ import ( "path/filepath" "regexp" "runtime" + + "github.com/hashicorp/go-version" ) // VMware Workstation Player for Linux. @@ -89,7 +91,7 @@ func playerNetmapConfPath() string { return filepath.Join(base, netmapConfFile) } -func playerVerifyVersion(version string) error { +func playerVerifyVersion(requiredVersion string) error { if runtime.GOOS != osLinux { return fmt.Errorf("driver is only supported on linux and windows, not %s", runtime.GOOS) } @@ -104,12 +106,24 @@ func playerVerifyVersion(version string) error { return err } - versionRe := regexp.MustCompile(`(?i)VMware Player (\d+)\.`) + versionRe := regexp.MustCompile(`(?i)VMware Player (\d+)\.(\d+)\.(\d+)`) matches := versionRe.FindStringSubmatch(stderr.String()) if matches == nil { return fmt.Errorf("error parsing version from output: %s", stderr.String()) } - log.Printf("[INFO] VMware Workstation Player: %s", matches[1]) - return compareVersions(matches[1], version, "Player") + fullVersion := fmt.Sprintf("%s.%s.%s", matches[1], matches[2], matches[3]) + log.Printf("[INFO] %s: %s", playerProductName, fullVersion) + + parsedVersionFound, err := version.NewVersion(fullVersion) + if err != nil { + return fmt.Errorf("invalid version found: %w", err) + } + + parsedVersionRequired, err := version.NewVersion(requiredVersion) + if err != nil { + return fmt.Errorf("invalid version required: %w", err) + } + + return compareVersionObjects(parsedVersionFound, parsedVersionRequired, playerProductName) } diff --git a/builder/vmware/common/driver_player_windows.go b/builder/vmware/common/driver_player_windows.go index e1ea33b2..76db1e97 100644 --- a/builder/vmware/common/driver_player_windows.go +++ b/builder/vmware/common/driver_player_windows.go @@ -13,15 +13,12 @@ import ( "path/filepath" "regexp" "syscall" + + "github.com/hashicorp/go-version" ) // VMware Workstation Player for Windows. -const ( - playerInstallationPathKey = `SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\vmplayer.exe` - playerRegistryKey = "SYSTEM\\CurrentControlSet\\services\\VMnetDHCP\\Parameters" -) - func playerExecutable(executable string) (string, error) { path, err := exec.LookPath(executable + ".exe") if err == nil { @@ -165,7 +162,7 @@ func playerDataFilePaths() []string { return paths } -func playerVerifyVersion(version string) error { +func playerVerifyVersion(requiredVersion string) error { key := `SOFTWARE\Wow6432Node\VMware, Inc.\VMware Player` subkey := "ProductVersion" productVersion, err := readRegString(syscall.HKEY_LOCAL_MACHINE, key, subkey) @@ -178,12 +175,25 @@ func playerVerifyVersion(version string) error { } } - versionRe := regexp.MustCompile(`^(\d+)\.`) + versionRe := regexp.MustCompile(`^(\d+)\.(\d+)\.(\d+)`) matches := versionRe.FindStringSubmatch(productVersion) - if matches == nil { + + if matches == nil || len(matches) < 4 { return fmt.Errorf("error retrieving the version from registry key %s\\%s: '%s'", key, subkey, productVersion) } - log.Printf("[INFO] VMware Workstation Player: %s", matches[1]) - return compareVersions(matches[1], version, "Player") + fullVersion := fmt.Sprintf("%s.%s.%s", matches[1], matches[2], matches[3]) + log.Printf("[INFO] %s: %s", playerProductName, fullVersion) + + parsedVersionFound, err := version.NewVersion(fullVersion) + if err != nil { + return fmt.Errorf("invalid version found: %w", err) + } + + parsedVersionRequired, err := version.NewVersion(requiredVersion) + if err != nil { + return fmt.Errorf("invalid version required: %w", err) + } + + return compareVersionObjects(parsedVersionFound, parsedVersionRequired, playerProductName) } diff --git a/builder/vmware/common/driver_workstation10.go b/builder/vmware/common/driver_workstation10.go index f55e329f..5c1d01bd 100644 --- a/builder/vmware/common/driver_workstation10.go +++ b/builder/vmware/common/driver_workstation10.go @@ -50,7 +50,7 @@ func (d *Workstation10Driver) Verify() error { return err } - return workstationVerifyVersion(VMWARE_WS_VERSION) + return workstationVerifyVersion(workstationMinVersionObj.String()) } func (d *Workstation10Driver) GetVmwareDriver() VmwareDriver { diff --git a/builder/vmware/common/driver_workstation10_windows.go b/builder/vmware/common/driver_workstation10_windows.go index 943b7d23..90632f74 100644 --- a/builder/vmware/common/driver_workstation10_windows.go +++ b/builder/vmware/common/driver_workstation10_windows.go @@ -10,9 +10,11 @@ import ( "log" "regexp" "syscall" + + "github.com/hashicorp/go-version" ) -func workstationVerifyVersion(version string) error { +func workstationVerifyVersion(requiredVersion string) error { key := `SOFTWARE\Wow6432Node\VMware, Inc.\VMware Workstation` subkey := "ProductVersion" productVersion, err := readRegString(syscall.HKEY_LOCAL_MACHINE, key, subkey) @@ -26,13 +28,24 @@ func workstationVerifyVersion(version string) error { } } - versionRe := regexp.MustCompile(`^(\d+)\.`) + versionRe := regexp.MustCompile(`^(\d+\.\d+\.\d+)`) matches := versionRe.FindStringSubmatch(productVersion) if matches == nil { return fmt.Errorf( `Could not find a VMware Workstation version in registry key %s\%s: '%s'`, key, subkey, productVersion) } - log.Printf("[INFO] Detected VMware Workstation version: %s", matches[1]) + fullVersion := matches[1] + log.Printf("[INFO] %s: %s", workstationProductName, fullVersion) + + parsedVersionFound, err := version.NewVersion(fullVersion) + if err != nil { + return fmt.Errorf("invalid version found: %w", err) + } + + parsedVersionRequired, err := version.NewVersion(requiredVersion) + if err != nil { + return fmt.Errorf("invalid version required: %w", err) + } - return compareVersions(matches[1], version, "Workstation") + return compareVersionObjects(parsedVersionFound, parsedVersionRequired, workstationProductName) } diff --git a/builder/vmware/common/driver_workstation_unix.go b/builder/vmware/common/driver_workstation_unix.go index 7481b540..cfcfe44c 100644 --- a/builder/vmware/common/driver_workstation_unix.go +++ b/builder/vmware/common/driver_workstation_unix.go @@ -16,6 +16,8 @@ import ( "path/filepath" "regexp" "runtime" + + "github.com/hashicorp/go-version" ) func workstationCheckLicense() error { @@ -140,13 +142,24 @@ func workstationVerifyVersion(version string) error { return workstationTestVersion(version, stderr.String()) } -func workstationTestVersion(wanted, versionOutput string) error { - versionRe := regexp.MustCompile(`(?i)VMware Workstation (\d+)\.`) +func workstationTestVersion(requiredVersion, versionOutput string) error { + versionRe := regexp.MustCompile(`(?i)VMware Workstation (\d+\.\d+\.\d+)`) matches := versionRe.FindStringSubmatch(versionOutput) if matches == nil { - return fmt.Errorf("error parsing version output: %s", wanted) + return fmt.Errorf("error parsing version output: %s", versionOutput) + } + fullVersion := matches[1] + log.Printf("[INFO] %s: %s", workstationProductName, fullVersion) + + parsedVersionFound, err := version.NewVersion(fullVersion) + if err != nil { + return fmt.Errorf("invalid version found: %w", err) + } + + parsedVersionRequired, err := version.NewVersion(requiredVersion) + if err != nil { + return fmt.Errorf("invalid version required: %w", err) } - log.Printf("Detected VMware Workstation version: %s", matches[1]) - return compareVersions(matches[1], wanted, "Workstation") + return compareVersionObjects(parsedVersionFound, parsedVersionRequired, workstationProductName) }