Skip to content

Instantly share code, notes, and snippets.

@DanHam
Created July 2, 2018 23:57
Show Gist options
  • Save DanHam/edea482abf362c271748599b96d4fd7c to your computer and use it in GitHub Desktop.
Save DanHam/edea482abf362c271748599b96d4fd7c to your computer and use it in GitHub Desktop.
Coverage report for builder/vmware/vmx
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<style>
body {
background: black;
color: rgb(80, 80, 80);
}
body, pre, #legend span {
font-family: Menlo, monospace;
font-weight: bold;
}
#topbar {
background: black;
position: fixed;
top: 0; left: 0; right: 0;
height: 42px;
border-bottom: 1px solid rgb(80, 80, 80);
}
#content {
margin-top: 50px;
}
#nav, #legend {
float: left;
margin-left: 10px;
}
#legend {
margin-top: 12px;
}
#nav {
margin-top: 10px;
}
#legend span {
margin: 0 5px;
}
.cov0 { color: rgb(192, 0, 0) }
.cov1 { color: rgb(128, 128, 128) }
.cov2 { color: rgb(116, 140, 131) }
.cov3 { color: rgb(104, 152, 134) }
.cov4 { color: rgb(92, 164, 137) }
.cov5 { color: rgb(80, 176, 140) }
.cov6 { color: rgb(68, 188, 143) }
.cov7 { color: rgb(56, 200, 146) }
.cov8 { color: rgb(44, 212, 149) }
.cov9 { color: rgb(32, 224, 152) }
.cov10 { color: rgb(20, 236, 155) }
</style>
</head>
<body>
<div id="topbar">
<div id="nav">
<select id="files">
<option value="file0">github.com/hashicorp/packer/builder/vmware/vmx/builder.go (16.7%)</option>
<option value="file1">github.com/hashicorp/packer/builder/vmware/vmx/config.go (89.7%)</option>
<option value="file2">github.com/hashicorp/packer/builder/vmware/vmx/step_clone_vmx.go (78.4%)</option>
</select>
</div>
<div id="legend">
<span>not tracked</span>
<span class="cov0">not covered</span>
<span class="cov8">covered</span>
</div>
</div>
<div id="content">
<pre class="file" id="file0" style="display: none">package vmx
import (
"errors"
"fmt"
"log"
"time"
vmwcommon "github.com/hashicorp/packer/builder/vmware/common"
"github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
// Builder implements packer.Builder and builds the actual VMware
// images.
type Builder struct {
config *Config
runner multistep.Runner
}
// Prepare processes the build configuration parameters.
func (b *Builder) Prepare(raws ...interface{}) ([]string, error) <span class="cov8" title="1">{
c, warnings, errs := NewConfig(raws...)
if errs != nil </span><span class="cov8" title="1">{
return warnings, errs
}</span>
<span class="cov8" title="1">b.config = c
return warnings, nil</span>
}
// Run executes a Packer build and returns a packer.Artifact representing
// a VMware image.
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) <span class="cov0" title="0">{
driver, err := vmwcommon.NewDriver(&amp;b.config.DriverConfig, &amp;b.config.SSHConfig)
if err != nil </span><span class="cov0" title="0">{
return nil, fmt.Errorf("Failed creating VMware driver: %s", err)
}</span>
// Setup the directory
<span class="cov0" title="0">dir := new(vmwcommon.LocalOutputDir)
dir.SetOutputDir(b.config.OutputDir)
// Set up the state.
state := new(multistep.BasicStateBag)
state.Put("config", b.config)
state.Put("debug", b.config.PackerDebug)
state.Put("dir", dir)
state.Put("driver", driver)
state.Put("hook", hook)
state.Put("ui", ui)
// Build the steps.
steps := []multistep.Step{
&amp;vmwcommon.StepPrepareTools{
RemoteType: b.config.RemoteType,
ToolsUploadFlavor: b.config.ToolsUploadFlavor,
},
&amp;vmwcommon.StepOutputDir{
Force: b.config.PackerForce,
},
&amp;common.StepCreateFloppy{
Files: b.config.FloppyConfig.FloppyFiles,
Directories: b.config.FloppyConfig.FloppyDirectories,
},
&amp;StepCloneVMX{
OutputDir: b.config.OutputDir,
Path: b.config.SourcePath,
VMName: b.config.VMName,
},
&amp;vmwcommon.StepConfigureVMX{
CustomData: b.config.VMXData,
},
&amp;vmwcommon.StepSuppressMessages{},
&amp;common.StepHTTPServer{
HTTPDir: b.config.HTTPDir,
HTTPPortMin: b.config.HTTPPortMin,
HTTPPortMax: b.config.HTTPPortMax,
},
&amp;vmwcommon.StepConfigureVNC{
Enabled: !b.config.DisableVNC,
VNCBindAddress: b.config.VNCBindAddress,
VNCPortMin: b.config.VNCPortMin,
VNCPortMax: b.config.VNCPortMax,
VNCDisablePassword: b.config.VNCDisablePassword,
},
&amp;vmwcommon.StepRun{
DurationBeforeStop: 5 * time.Second,
Headless: b.config.Headless,
},
&amp;vmwcommon.StepTypeBootCommand{
BootWait: b.config.BootWait,
VNCEnabled: !b.config.DisableVNC,
BootCommand: b.config.FlatBootCommand(),
VMName: b.config.VMName,
Ctx: b.config.ctx,
},
&amp;communicator.StepConnect{
Config: &amp;b.config.SSHConfig.Comm,
Host: driver.CommHost,
SSHConfig: vmwcommon.SSHConfigFunc(&amp;b.config.SSHConfig),
},
&amp;vmwcommon.StepUploadTools{
RemoteType: b.config.RemoteType,
ToolsUploadFlavor: b.config.ToolsUploadFlavor,
ToolsUploadPath: b.config.ToolsUploadPath,
Ctx: b.config.ctx,
},
&amp;common.StepProvision{},
&amp;vmwcommon.StepShutdown{
Command: b.config.ShutdownCommand,
Timeout: b.config.ShutdownTimeout,
},
&amp;vmwcommon.StepCleanFiles{},
&amp;vmwcommon.StepCompactDisk{
Skip: b.config.SkipCompaction,
},
&amp;vmwcommon.StepConfigureVMX{
CustomData: b.config.VMXDataPost,
SkipFloppy: true,
},
&amp;vmwcommon.StepCleanVMX{
RemoveEthernetInterfaces: b.config.VMXConfig.VMXRemoveEthernet,
VNCEnabled: !b.config.DisableVNC,
},
}
// Run the steps.
b.runner = common.NewRunnerWithPauseFn(steps, b.config.PackerConfig, ui, state)
b.runner.Run(state)
// Report any errors.
if rawErr, ok := state.GetOk("error"); ok </span><span class="cov0" title="0">{
return nil, rawErr.(error)
}</span>
// If we were interrupted or cancelled, then just exit.
<span class="cov0" title="0">if _, ok := state.GetOk(multistep.StateCancelled); ok </span><span class="cov0" title="0">{
return nil, errors.New("Build was cancelled.")
}</span>
<span class="cov0" title="0">if _, ok := state.GetOk(multistep.StateHalted); ok </span><span class="cov0" title="0">{
return nil, errors.New("Build was halted.")
}</span>
<span class="cov0" title="0">return vmwcommon.NewLocalArtifact(b.config.VMName, b.config.OutputDir)</span>
}
// Cancel.
func (b *Builder) Cancel() <span class="cov0" title="0">{
if b.runner != nil </span><span class="cov0" title="0">{
log.Println("Cancelling the step runner...")
b.runner.Cancel()
}</span>
}
</pre>
<pre class="file" id="file1" style="display: none">package vmx
import (
"fmt"
"os"
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"
)
// Config is the configuration structure for the builder.
type Config struct {
common.PackerConfig `mapstructure:",squash"`
common.HTTPConfig `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"`
RemoteType string `mapstructure:"remote_type"`
SkipCompaction bool `mapstructure:"skip_compaction"`
SourcePath string `mapstructure:"source_path"`
VMName string `mapstructure:"vm_name"`
ctx interpolate.Context
}
func NewConfig(raws ...interface{}) (*Config, []string, error) <span class="cov8" title="1">{
c := new(Config)
err := config.Decode(c, &amp;config.DecodeOpts{
Interpolate: true,
InterpolateContext: &amp;c.ctx,
InterpolateFilter: &amp;interpolate.RenderFilter{
Exclude: []string{
"boot_command",
"tools_upload_path",
},
},
}, raws...)
if err != nil </span><span class="cov0" title="0">{
return nil, nil, err
}</span>
// Defaults
<span class="cov8" title="1">if c.VMName == "" </span><span class="cov8" title="1">{
c.VMName = fmt.Sprintf(
"packer-%s-%d", c.PackerBuildName, interpolate.InitTime.Unix())
}</span>
// Prepare the errors
<span class="cov8" title="1">var errs *packer.MultiError
errs = packer.MultiErrorAppend(errs, c.DriverConfig.Prepare(&amp;c.ctx)...)
errs = packer.MultiErrorAppend(errs, c.HTTPConfig.Prepare(&amp;c.ctx)...)
errs = packer.MultiErrorAppend(errs, c.OutputConfig.Prepare(&amp;c.ctx, &amp;c.PackerConfig)...)
errs = packer.MultiErrorAppend(errs, c.RunConfig.Prepare(&amp;c.ctx)...)
errs = packer.MultiErrorAppend(errs, c.ShutdownConfig.Prepare(&amp;c.ctx)...)
errs = packer.MultiErrorAppend(errs, c.SSHConfig.Prepare(&amp;c.ctx)...)
errs = packer.MultiErrorAppend(errs, c.ToolsConfig.Prepare(&amp;c.ctx)...)
errs = packer.MultiErrorAppend(errs, c.VMXConfig.Prepare(&amp;c.ctx)...)
errs = packer.MultiErrorAppend(errs, c.FloppyConfig.Prepare(&amp;c.ctx)...)
errs = packer.MultiErrorAppend(errs, c.VNCConfig.Prepare(&amp;c.ctx)...)
if c.SourcePath == "" </span><span class="cov8" title="1">{
errs = packer.MultiErrorAppend(errs, fmt.Errorf("source_path is blank, but is required"))
}</span> else<span class="cov8" title="1"> {
if _, err := os.Stat(c.SourcePath); err != nil </span><span class="cov8" title="1">{
errs = packer.MultiErrorAppend(errs,
fmt.Errorf("source_path is invalid: %s", err))
}</span>
}
// Warnings
<span class="cov8" title="1">var warnings []string
if c.ShutdownCommand == "" </span><span class="cov0" title="0">{
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.")
}</span>
<span class="cov8" title="1">if c.Headless &amp;&amp; c.DisableVNC </span><span class="cov0" title="0">{
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.")
}</span>
// Check for any errors.
<span class="cov8" title="1">if errs != nil &amp;&amp; len(errs.Errors) &gt; 0 </span><span class="cov8" title="1">{
return nil, warnings, errs
}</span>
<span class="cov8" title="1">return c, warnings, nil</span>
}
</pre>
<pre class="file" id="file2" style="display: none">package vmx
import (
"context"
"fmt"
"log"
"path/filepath"
"regexp"
vmwcommon "github.com/hashicorp/packer/builder/vmware/common"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
// StepCloneVMX takes a VMX file and clones the VM into the output directory.
type StepCloneVMX struct {
OutputDir string
Path string
VMName string
}
func (s *StepCloneVMX) Run(_ context.Context, state multistep.StateBag) multistep.StepAction <span class="cov8" title="1">{
driver := state.Get("driver").(vmwcommon.Driver)
ui := state.Get("ui").(packer.Ui)
// Set the path we want for the new .vmx file and clone
vmxPath := filepath.Join(s.OutputDir, s.VMName+".vmx")
ui.Say("Cloning source VM...")
log.Printf("Cloning from: %s", s.Path)
log.Printf("Cloning to: %s", vmxPath)
if err := driver.Clone(vmxPath, s.Path); err != nil </span><span class="cov0" title="0">{
state.Put("error", err)
return multistep.ActionHalt
}</span>
// Read in the machine configuration from the cloned VMX file
//
// * The main driver needs the path to the vmx (set above) and the
// network type so that it can work out things like IP's and MAC
// addresses
// * The disk compaction step needs the paths to all attached disks
<span class="cov8" title="1">vmxData, err := vmwcommon.ReadVMX(vmxPath)
if err != nil </span><span class="cov0" title="0">{
state.Put("error", err)
return multistep.ActionHalt
}</span>
<span class="cov8" title="1">var diskFilenames []string
// The VMX file stores the path to a configured disk, and information
// about that disks attachment to a virtual adapter/controller, as a
// key/value pair.
// For a virtual disk attached to bus ID 3 of the virtual machines
// first SCSI adapter the key/value pair would look something like:
// scsi0:3.fileName = "relative/path/to/scsiDisk.vmdk"
// The supported adapter types and configuration maximums for each type
// vary according to the VMware platform type and version, and the
// Virtual Machine Hardware version used. See the 'Virtual Machine
// Maximums' section within VMware's 'Configuration Maximums'
// documentation for each platform:
// https://kb.vmware.com/s/article/1003497
// Information about the supported Virtual Machine Hardware versions:
// https://kb.vmware.com/s/article/1003746
// The following regexp is used to match all possible disk attachment
// points that may be found in the VMX file across all VMware
// platforms/versions and Virtual Machine Hardware versions
diskPathKeyRe := regexp.MustCompile(`(?i)^(scsi|sata|ide|nvme)[[:digit:]]:[[:digit:]]{1,2}\.fileName`)
for k, v := range vmxData </span><span class="cov8" title="1">{
match := diskPathKeyRe.FindString(k)
if match != "" &amp;&amp; filepath.Ext(v) == ".vmdk" </span><span class="cov8" title="1">{
diskFilenames = append(diskFilenames, v)
}</span>
}
// Write out the relative, host filesystem paths to the disks
<span class="cov8" title="1">var diskFullPaths []string
for _, diskFilename := range diskFilenames </span><span class="cov8" title="1">{
log.Printf("Found attached disk with filename: %s", diskFilename)
diskFullPaths = append(diskFullPaths, filepath.Join(s.OutputDir, diskFilename))
}</span>
<span class="cov8" title="1">if len(diskFullPaths) == 0 </span><span class="cov0" title="0">{
state.Put("error", fmt.Errorf("Could not enumerate disk info from the vmx file"))
return multistep.ActionHalt
}</span>
// Determine the network type by reading out of the .vmx
<span class="cov8" title="1">var networkType string
if _, ok := vmxData["ethernet0.connectiontype"]; ok </span><span class="cov8" title="1">{
networkType = vmxData["ethernet0.connectiontype"]
log.Printf("Discovered the network type: %s", networkType)
}</span>
<span class="cov8" title="1">if networkType == "" </span><span class="cov0" title="0">{
networkType = "nat"
log.Printf("Defaulting to network type: %s", networkType)
}</span>
// Stash all required information in our state bag
<span class="cov8" title="1">state.Put("vmx_path", vmxPath)
state.Put("disk_full_paths", diskFullPaths)
state.Put("vmnetwork", networkType)
return multistep.ActionContinue</span>
}
func (s *StepCloneVMX) Cleanup(state multistep.StateBag) {<span class="cov0" title="0">
}</span>
</pre>
</div>
</body>
<script>
(function() {
var files = document.getElementById('files');
var visible;
files.addEventListener('change', onChange, false);
function select(part) {
if (visible)
visible.style.display = 'none';
visible = document.getElementById(part);
if (!visible)
return;
files.value = part;
visible.style.display = 'block';
location.hash = part;
}
function onChange() {
select(files.value);
window.scrollTo(0, 0);
}
if (location.hash != "") {
select(location.hash.substr(1));
}
if (!visible) {
select("file0");
}
})();
</script>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment