From b5171c0b425faa26b1209922c03a313b311a3105 Mon Sep 17 00:00:00 2001 From: Ryan Johnson Date: Sun, 23 Jun 2024 18:53:30 -0700 Subject: [PATCH] fix: generate default `netmap.conf`, if required Generates the `netmap.conf` in the default location if the file does not exist in the default path or the alternate path. There have been reported issues (GH, Discuss, blogs) of fresh installations of Workstation 17 (and 16) not generating the default file which will cause a build to fail on a newly configured system. Ref: #65 Signed-off-by: Ryan Johnson --- builder/vmware/common/driver_workstation9.go | 195 ++++++++++++++++--- 1 file changed, 170 insertions(+), 25 deletions(-) diff --git a/builder/vmware/common/driver_workstation9.go b/builder/vmware/common/driver_workstation9.go index 7a75bdb8..4078f149 100644 --- a/builder/vmware/common/driver_workstation9.go +++ b/builder/vmware/common/driver_workstation9.go @@ -6,15 +6,52 @@ package common import ( "errors" "fmt" + "html/template" "log" "os" "os/exec" "path/filepath" + "runtime" + "sort" "strings" "github.com/hashicorp/packer-plugin-sdk/multistep" ) +// Template for the network mapper configuration file, 'netmap.conf'. +// This file is used to map network devices to their respective network names. +// This template is used to generate the file if if the default file does not +// exist on the system. +const netmapTemplate = ` +# This file is automatically generated. +# Hand-editing this file is not recommended. +network0.name = "Bridged" +network0.device = "vmnet0" +network1.name = "HostOnly" +network1.device = "vmnet1" +{{- range . }} +{{- if gt .Index 1 }} +{{- if lt .Index 8 }} +network{{ .Index }}.name = "VMNet{{ .Index }}" +network{{ .Index }}.device = "vmnet{{ .Index }}" +{{- else if eq .Index 8 }} +network8.name = "NAT" +network8.device = "vmnet8" +{{- else }} +network{{ .Index }}.name = "VMNet{{ .Index }}" +network{{ .Index }}.device = "vmnet{{ .Index }}" +{{- end }} +{{- end }} +{{- end }} +` + +// NetmapConfig is a struct that represents a network mapper configuration. +type NetmapConfig struct { + Index int + Name string + Device string +} + // Workstation9Driver is a driver that can run VMware Workstation 9 type Workstation9Driver struct { VmwareDriver @@ -173,33 +210,57 @@ func (d *Workstation9Driver) Verify() error { } d.VmwareDriver.NetworkMapper = func() (NetworkNameMapper, error) { - pathNetmap := workstationNetmapConfPath() - - // Check that the file for the networkmapper configuration exists. If there's no - // error, then the file exists and we can proceed to read the configuration out of it. - if _, err := os.Stat(pathNetmap); err == nil { - log.Printf("Located networkmapper configuration file: %s", pathNetmap) + if runtime.GOOS == "windows" { + // Check if the network mapper configuration file exists. + _, err := checkNetmapConfExists() + if err != nil { + return nil, fmt.Errorf("error checking if the network mapper configuration file exists: %v", err) + } + + // Get the default network mapper configuration file path. + pathNetmap := workstationNetmapConfPath() + + // Check if the network mapper configuration file exists. + _, err = os.Stat(pathNetmap) + if err != nil { + // If the error is because the file does not exist, return a specific error + if os.IsNotExist(err) { + return nil, fmt.Errorf("networkmapper configuration file does not exist: %s", pathNetmap) + } + // For other errors, return a generic error. + return nil, fmt.Errorf("error checking network mapper configuration file: %v", err) + } + + // If the network mapper configuration file exists, read the configuration. + log.Printf("Located the network mapper configuration file: %s", pathNetmap) return ReadNetmapConfig(pathNetmap) + } else { + pathNetmap := workstationNetmapConfPath() + + // Check if the network mapper configuration file exists. + if _, err := os.Stat(pathNetmap); err == nil { + log.Printf("Located network mapper configuration file: %s", pathNetmap) + return ReadNetmapConfig(pathNetmap) + } + + // The file does not exist, check the alternate configuration path. + libpath, _ := workstationVMwareRoot() + pathNetworking := filepath.Join(libpath, "networking") + if _, err := os.Stat(pathNetworking); err != nil { + return nil, fmt.Errorf("error determining network mappings from files in path: %s", libpath) + } + + // If the network mapper configuration file exists, read the configuration. + log.Printf("Located networking mapper configuration file: %s", pathNetworking) + fd, err := os.Open(pathNetworking) + if err != nil { + return nil, err + } + defer fd.Close() + + // Then we pass the handle to the networking configuration parser. + return ReadNetworkingConfig(fd) } - - // If we weren't able to find the networkmapper configuration file, then fall back - // to the networking file which might also be in the configuration directory. - libpath, _ := workstationVMwareRoot() - pathNetworking := filepath.Join(libpath, "networking") - if _, err := os.Stat(pathNetworking); err != nil { - return nil, fmt.Errorf("error determining network mappings from files in path: %s", libpath) - } - - // We were able to successfully stat the file.. So, now we can open a handle to it. - log.Printf("Located networking configuration file: %s", pathNetworking) - fd, err := os.Open(pathNetworking) - if err != nil { - return nil, err - } - defer fd.Close() - - // Then we pass the handle to the networking configuration parser. - return ReadNetworkingConfig(fd) } return nil } @@ -215,3 +276,87 @@ func (d *Workstation9Driver) ToolsInstall() error { func (d *Workstation9Driver) GetVmwareDriver() VmwareDriver { return d.VmwareDriver } + +// checkNetmapConfExists checks if the network mapper configuration file exists. +// If not, if attempts to generate the file using generateNetmapConfig. +func checkNetmapConfExists() (string, error) { + pathNetmap := workstationNetmapConfPath() + + // Check if the default network mapper configuration file exists. + if _, err := os.Stat(pathNetmap); os.IsNotExist(err) { + // The file does not exist, check the alternate configuration path. + libpath, _ := workstationVMwareRoot() + pathNetworking := filepath.Join(libpath, "networking") + if _, err := os.Stat(pathNetworking); err == nil { + // Alternate networking configuration path exists. Using this path. + log.Printf("Using alternate networking configuration path: %s", pathNetworking) + return pathNetworking, nil + } + + // Neither the default nor alternate exists. + // Generate the network mapper configuration file in the default path. + log.Printf("Generating a network mapper configuration file in the default path: %s", pathNetmap) + if err := generateNetmapConfig(); err != nil { + return "", fmt.Errorf("error generating a network mapper configuration file in the default path %s: %v", pathNetmap, err) + } + log.Printf("Generating a network mapper configuration file in the default path: %s", pathNetmap) + } else if err != nil { + // There was an error other than the file not existing. + return "", err + } + + // A network mapper configuration file now exists in the default path. + return pathNetmap, nil +} + +// generateNetmapConfig creates the network mapper configuration file in the +// default path using the netmapTemplate. +func generateNetmapConfig() error { + var paths []string + if os.Getenv("ProgramData") != "" { + paths = append(paths, + filepath.Join(os.Getenv("ProgramData"), "VMware")) + } + + basePaths := paths + + // Initialize networks with device names for specific networks. + networks := []NetmapConfig{ + {0, "Bridged", "vmnet0"}, + {1, "HostOnly", "vmnet1"}, + {8, "NAT", "vmnet8"}, + } + for i := 2; i <= 19; i++ { + if i != 8 { // Skip NAT, already added. + networks = append(networks, NetmapConfig{i, fmt.Sprintf("VMNet%d", i), fmt.Sprintf("vmnet%d", i)}) + } + } + + // Sort the networks slice by Index to ensure correct order in the generated file. + sort.Slice(networks, func(i, j int) bool { + return networks[i].Index < networks[j].Index + }) + + t, err := template.New("netmap").Parse(netmapTemplate) + if err != nil { + return err + } + + var sb strings.Builder + if err := t.Execute(&sb, networks); err != nil { + return err + } + netmapContent := []byte(sb.String()) + + for _, basePath := range basePaths { + path := filepath.Join(basePath, "netmap.conf") + if err := os.MkdirAll(basePath, 0755); err != nil { + return err + } + if err := os.WriteFile(path, netmapContent, 0644); err != nil { + return err + } + } + + return nil +}