builder/vmware: support vmx_template_path [GH-270]
This commit is contained in:
parent
c753946c84
commit
1566b4d858
|
@ -16,6 +16,8 @@ IMPROVEMENTS:
|
||||||
* builder/amazon: Tagging now works with all amazon builder types.
|
* builder/amazon: Tagging now works with all amazon builder types.
|
||||||
* builder/vmware: Option `ssh_skip_request_pty` for not requesting a PTY
|
* builder/vmware: Option `ssh_skip_request_pty` for not requesting a PTY
|
||||||
for the SSH connection. [GH-270]
|
for the SSH connection. [GH-270]
|
||||||
|
* builder/vmware: Specify a `vmx_template_path` in order to customize
|
||||||
|
the generated VMX. [GH-270]
|
||||||
* command/build: Machine-readable output now contains build errors, if any.
|
* command/build: Machine-readable output now contains build errors, if any.
|
||||||
* command/build: An "end" sentinel is outputted in machine-readable output
|
* command/build: An "end" sentinel is outputted in machine-readable output
|
||||||
for artifact listing so it is easier to know when it is over.
|
for artifact listing so it is easier to know when it is over.
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"github.com/mitchellh/multistep"
|
"github.com/mitchellh/multistep"
|
||||||
"github.com/mitchellh/packer/common"
|
"github.com/mitchellh/packer/common"
|
||||||
"github.com/mitchellh/packer/packer"
|
"github.com/mitchellh/packer/packer"
|
||||||
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
|
@ -49,6 +50,7 @@ type config struct {
|
||||||
ToolsUploadFlavor string `mapstructure:"tools_upload_flavor"`
|
ToolsUploadFlavor string `mapstructure:"tools_upload_flavor"`
|
||||||
ToolsUploadPath string `mapstructure:"tools_upload_path"`
|
ToolsUploadPath string `mapstructure:"tools_upload_path"`
|
||||||
VMXData map[string]string `mapstructure:"vmx_data"`
|
VMXData map[string]string `mapstructure:"vmx_data"`
|
||||||
|
VMXTemplatePath string `mapstructure:"vmx_template_path"`
|
||||||
VNCPortMin uint `mapstructure:"vnc_port_min"`
|
VNCPortMin uint `mapstructure:"vnc_port_min"`
|
||||||
VNCPortMax uint `mapstructure:"vnc_port_max"`
|
VNCPortMax uint `mapstructure:"vnc_port_max"`
|
||||||
|
|
||||||
|
@ -152,6 +154,7 @@ func (b *Builder) Prepare(raws ...interface{}) error {
|
||||||
"boot_wait": &b.config.RawBootWait,
|
"boot_wait": &b.config.RawBootWait,
|
||||||
"shutdown_timeout": &b.config.RawShutdownTimeout,
|
"shutdown_timeout": &b.config.RawShutdownTimeout,
|
||||||
"ssh_wait_timeout": &b.config.RawSSHWaitTimeout,
|
"ssh_wait_timeout": &b.config.RawSSHWaitTimeout,
|
||||||
|
"vmx_template_path": &b.config.VMXTemplatePath,
|
||||||
}
|
}
|
||||||
|
|
||||||
for n, ptr := range templates {
|
for n, ptr := range templates {
|
||||||
|
@ -298,6 +301,14 @@ func (b *Builder) Prepare(raws ...interface{}) error {
|
||||||
errs, fmt.Errorf("tools_upload_path invalid: %s", err))
|
errs, fmt.Errorf("tools_upload_path invalid: %s", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if b.config.VMXTemplatePath != "" {
|
||||||
|
if err := b.validateVMXTemplatePath(); err != nil {
|
||||||
|
errs = packer.MultiErrorAppend(
|
||||||
|
errs, fmt.Errorf("vmx_template_path is invalid: %s", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
if b.config.VNCPortMin > b.config.VNCPortMax {
|
if b.config.VNCPortMin > b.config.VNCPortMax {
|
||||||
errs = packer.MultiErrorAppend(
|
errs = packer.MultiErrorAppend(
|
||||||
errs, fmt.Errorf("vnc_port_min must be less than vnc_port_max"))
|
errs, fmt.Errorf("vnc_port_min must be less than vnc_port_max"))
|
||||||
|
@ -414,3 +425,18 @@ func (b *Builder) Cancel() {
|
||||||
b.runner.Cancel()
|
b.runner.Cancel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *Builder) validateVMXTemplatePath() error {
|
||||||
|
f, err := os.Open(b.config.VMXTemplatePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
data, err := ioutil.ReadAll(f)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.config.tpl.Validate(string(data))
|
||||||
|
}
|
||||||
|
|
|
@ -437,6 +437,56 @@ func TestBuilderPrepare_ToolsUploadPath(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBuilderPrepare_VMXTemplatePath(t *testing.T) {
|
||||||
|
var b Builder
|
||||||
|
config := testConfig()
|
||||||
|
|
||||||
|
// Test bad
|
||||||
|
config["vmx_template_path"] = "/i/dont/exist/forreal"
|
||||||
|
err := b.Prepare(config)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("should have error")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test good
|
||||||
|
tf, err := ioutil.TempFile("", "packer")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
defer os.Remove(tf.Name())
|
||||||
|
defer tf.Close()
|
||||||
|
|
||||||
|
if _, err := tf.Write([]byte("HELLO!")); err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config["vmx_template_path"] = tf.Name()
|
||||||
|
b = Builder{}
|
||||||
|
err = b.Prepare(config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("should not have error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bad template
|
||||||
|
tf2, err := ioutil.TempFile("", "packer")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
defer os.Remove(tf2.Name())
|
||||||
|
defer tf2.Close()
|
||||||
|
|
||||||
|
if _, err := tf2.Write([]byte("{{foo}")); err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config["vmx_template_path"] = tf2.Name()
|
||||||
|
b = Builder{}
|
||||||
|
err = b.Prepare(config)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("should have error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestBuilderPrepare_VNCPort(t *testing.T) {
|
func TestBuilderPrepare_VNCPort(t *testing.T) {
|
||||||
var b Builder
|
var b Builder
|
||||||
config := testConfig()
|
config := testConfig()
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
package vmware
|
package vmware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/mitchellh/multistep"
|
"github.com/mitchellh/multistep"
|
||||||
"github.com/mitchellh/packer/packer"
|
"github.com/mitchellh/packer/packer"
|
||||||
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"text/template"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type vmxTemplateData struct {
|
type vmxTemplateData struct {
|
||||||
|
@ -42,11 +42,37 @@ func (stepCreateVMX) Run(state map[string]interface{}) multistep.StepAction {
|
||||||
ISOPath: isoPath,
|
ISOPath: isoPath,
|
||||||
}
|
}
|
||||||
|
|
||||||
var buf bytes.Buffer
|
vmxTemplate := DefaultVMXTemplate
|
||||||
t := template.Must(template.New("vmx").Parse(DefaultVMXTemplate))
|
if config.VMXTemplatePath != "" {
|
||||||
t.Execute(&buf, tplData)
|
f, err := os.Open(config.VMXTemplatePath)
|
||||||
|
if err != nil {
|
||||||
|
err := fmt.Errorf("Error reading VMX template: %s", err)
|
||||||
|
state["error"] = err
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
vmxData := ParseVMX(buf.String())
|
rawBytes, err := ioutil.ReadAll(f)
|
||||||
|
if err != nil {
|
||||||
|
err := fmt.Errorf("Error reading VMX template: %s", err)
|
||||||
|
state["error"] = err
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
vmxTemplate = string(rawBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
vmxContents, err := config.tpl.Process(vmxTemplate, tplData)
|
||||||
|
if err != nil {
|
||||||
|
err := fmt.Errorf("Error procesing VMX template: %s", err)
|
||||||
|
state["error"] = err
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
vmxData := ParseVMX(vmxContents)
|
||||||
if config.VMXData != nil {
|
if config.VMXData != nil {
|
||||||
log.Println("Setting custom VMX data...")
|
log.Println("Setting custom VMX data...")
|
||||||
for k, v := range config.VMXData {
|
for k, v := range config.VMXData {
|
||||||
|
|
|
@ -190,6 +190,13 @@ Optional:
|
||||||
uses a randomly chosen port in this range that appears available. By default
|
uses a randomly chosen port in this range that appears available. By default
|
||||||
this is 5900 to 6000. The minimum and maximum ports are inclusive.
|
this is 5900 to 6000. The minimum and maximum ports are inclusive.
|
||||||
|
|
||||||
|
* `vmx_template_path` (string) - Path to a
|
||||||
|
[configuration template](/docs/templates/configuration-templates.html) that
|
||||||
|
defines the contents of the virtual machine VMX file for VMware. This is
|
||||||
|
for **advanced users only** as this can render the virtual machine
|
||||||
|
non-functional. See below for more information. For basic VMX modifications,
|
||||||
|
try `vmx_data` first.
|
||||||
|
|
||||||
## Boot Command
|
## Boot Command
|
||||||
|
|
||||||
The `boot_command` configuration is very important: it specifies the keys
|
The `boot_command` configuration is very important: it specifies the keys
|
||||||
|
@ -240,3 +247,29 @@ an Ubuntu 12.04 installer:
|
||||||
"initrd=/install/initrd.gz -- <enter>"
|
"initrd=/install/initrd.gz -- <enter>"
|
||||||
]
|
]
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
## VMX Template
|
||||||
|
|
||||||
|
The heart of a VMware machine is the "vmx" file. This contains all the
|
||||||
|
virtual hardware metadata necessary for the VM to function. Packer by default
|
||||||
|
uses a [safe, flexible VMX file](https://github.com/mitchellh/packer/blob/20541a7eda085aa5cf35bfed5069592ca49d106e/builder/vmware/step_create_vmx.go#L84).
|
||||||
|
But for advanced users, this template can be customized. This allows
|
||||||
|
Packer to build virtual machines of effectively any guest operating system
|
||||||
|
type.
|
||||||
|
|
||||||
|
<div class="alert alert-block alert-warn">
|
||||||
|
<p>
|
||||||
|
<strong>This is an advanced feature.</strong> Modifying the VMX template
|
||||||
|
can easily cause your virtual machine to not boot properly. Please only
|
||||||
|
modify the template if you know what you're doing.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
Within the template, a handful of variables are available so that your
|
||||||
|
template can continue working with the rest of the Packer machinery. Using
|
||||||
|
these variables isn't required, however.
|
||||||
|
|
||||||
|
* `Name` - The name of the virtual machine.
|
||||||
|
* `GuestOS` - The VMware-valid guest OS type.
|
||||||
|
* `DiskName` - The filename (without the suffix) of the main virtual disk.
|
||||||
|
* `ISOPath` - The path to the ISO to use for the OS installation.
|
||||||
|
|
Loading…
Reference in New Issue