Implement cd_files for virtualbox builders.
Since the work to mount isos was duplicated already across the step_mount_iso and step_mount_guest_additions, this required a refactor to prevent further code duplication and make the ports and devices easier to follow
This commit is contained in:
parent
bcd3c33e49
commit
b73825bb72
|
@ -4,6 +4,7 @@ package common
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/packer/template/interpolate"
|
||||
)
|
||||
|
@ -19,13 +20,36 @@ const (
|
|||
type GuestAdditionsConfig struct {
|
||||
Communicator string `mapstructure:"communicator"`
|
||||
// The method by which guest additions are
|
||||
// made available to the guest for installation. Valid options are upload,
|
||||
// attach, or disable. If the mode is attach the guest additions ISO will
|
||||
// be attached as a CD device to the virtual machine. If the mode is upload
|
||||
// made available to the guest for installation. Valid options are `upload`,
|
||||
// `attach`, or `disable`. If the mode is `attach` the guest additions ISO will
|
||||
// be attached as a CD device to the virtual machine. If the mode is `upload`
|
||||
// the guest additions ISO will be uploaded to the path specified by
|
||||
// guest_additions_path. The default value is upload. If disable is used,
|
||||
// `guest_additions_path`. The default value is `upload`. If `disable` is used,
|
||||
// guest additions won't be downloaded, either.
|
||||
GuestAdditionsMode string `mapstructure:"guest_additions_mode" required:"false"`
|
||||
GuestAdditionsMode string `mapstructure:"guest_additions_mode"`
|
||||
// The interface type to use to mount guest additions when
|
||||
// guest_additions_mode is set to attach. Will default to the value set in
|
||||
// iso_interface, if iso_interface is set. Will default to "ide", if
|
||||
// iso_interface is not set. Options are "ide" and "sata".
|
||||
GuestAdditionsInterface string `mapstructure:"guest_additions_interface" required:"false"`
|
||||
// The path on the guest virtual machine
|
||||
// where the VirtualBox guest additions ISO will be uploaded. By default this
|
||||
// is `VBoxGuestAdditions.iso` which should upload into the login directory of
|
||||
// the user. This is a [configuration
|
||||
// template](/docs/templates/engine) where the `Version`
|
||||
// variable is replaced with the VirtualBox version.
|
||||
GuestAdditionsPath string `mapstructure:"guest_additions_path"`
|
||||
// The SHA256 checksum of the guest
|
||||
// additions ISO that will be uploaded to the guest VM. By default the
|
||||
// checksums will be downloaded from the VirtualBox website, so this only needs
|
||||
// to be set if you want to be explicit about the checksum.
|
||||
GuestAdditionsSHA256 string `mapstructure:"guest_additions_sha256"`
|
||||
// The URL of the guest additions ISO
|
||||
// to upload. This can also be a file URL if the ISO is at a local path. By
|
||||
// default, the VirtualBox builder will attempt to find the guest additions ISO
|
||||
// on the local file system. If it is not available locally, the builder will
|
||||
// download the proper guest additions ISO from the internet.
|
||||
GuestAdditionsURL string `mapstructure:"guest_additions_url" required:"false"`
|
||||
}
|
||||
|
||||
func (c *GuestAdditionsConfig) Prepare(ctx *interpolate.Context) []error {
|
||||
|
@ -36,5 +60,36 @@ func (c *GuestAdditionsConfig) Prepare(ctx *interpolate.Context) []error {
|
|||
"'disable' when communicator = 'none'."))
|
||||
}
|
||||
|
||||
if c.GuestAdditionsMode == "" {
|
||||
c.GuestAdditionsMode = "upload"
|
||||
}
|
||||
|
||||
if c.GuestAdditionsPath == "" {
|
||||
c.GuestAdditionsPath = "VBoxGuestAdditions.iso"
|
||||
}
|
||||
|
||||
if c.GuestAdditionsSHA256 != "" {
|
||||
c.GuestAdditionsSHA256 = strings.ToLower(c.GuestAdditionsSHA256)
|
||||
}
|
||||
|
||||
validMode := false
|
||||
validModes := []string{
|
||||
GuestAdditionsModeDisable,
|
||||
GuestAdditionsModeAttach,
|
||||
GuestAdditionsModeUpload,
|
||||
}
|
||||
|
||||
for _, mode := range validModes {
|
||||
if c.GuestAdditionsMode == mode {
|
||||
validMode = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !validMode {
|
||||
errs = append(errs,
|
||||
fmt.Errorf("guest_additions_mode is invalid. Must be one of: %v", validModes))
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
|
|
@ -1,105 +0,0 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
// This step attaches the VirtualBox guest additions as a inserted CD onto
|
||||
// the virtual machine.
|
||||
//
|
||||
// Uses:
|
||||
// config *config
|
||||
// driver Driver
|
||||
// guest_additions_path string
|
||||
// ui packer.Ui
|
||||
// vmName string
|
||||
//
|
||||
// Produces:
|
||||
type StepAttachGuestAdditions struct {
|
||||
attachedPath string
|
||||
GuestAdditionsMode string
|
||||
GuestAdditionsInterface string
|
||||
}
|
||||
|
||||
func (s *StepAttachGuestAdditions) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
driver := state.Get("driver").(Driver)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
vmName := state.Get("vmName").(string)
|
||||
|
||||
// If we're not attaching the guest additions then just return
|
||||
if s.GuestAdditionsMode != GuestAdditionsModeAttach {
|
||||
log.Println("Not attaching guest additions since we're uploading.")
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
// Get the guest additions path since we're doing it
|
||||
guestAdditionsPath := state.Get("guest_additions_path").(string)
|
||||
|
||||
// Attach the guest additions to the computer
|
||||
|
||||
controllerName := "IDE Controller"
|
||||
port := "1"
|
||||
device := "0"
|
||||
if s.GuestAdditionsInterface == "sata" {
|
||||
controllerName = "SATA Controller"
|
||||
port = "2"
|
||||
device = "0"
|
||||
}
|
||||
|
||||
log.Println("Attaching guest additions ISO onto IDE controller...")
|
||||
command := []string{
|
||||
"storageattach", vmName,
|
||||
"--storagectl", controllerName,
|
||||
"--port", port,
|
||||
"--device", device,
|
||||
"--type", "dvddrive",
|
||||
"--medium", guestAdditionsPath,
|
||||
}
|
||||
if err := driver.VBoxManage(command...); err != nil {
|
||||
err := fmt.Errorf("Error attaching guest additions: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// Track the path so that we can unregister it from VirtualBox later
|
||||
s.attachedPath = guestAdditionsPath
|
||||
state.Put("guest_additions_attached", true)
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepAttachGuestAdditions) Cleanup(state multistep.StateBag) {
|
||||
if s.attachedPath == "" {
|
||||
return
|
||||
}
|
||||
|
||||
driver := state.Get("driver").(Driver)
|
||||
vmName := state.Get("vmName").(string)
|
||||
|
||||
controllerName := "IDE Controller"
|
||||
port := "1"
|
||||
device := "0"
|
||||
if s.GuestAdditionsInterface == "sata" {
|
||||
controllerName = "SATA Controller"
|
||||
port = "2"
|
||||
device = "0"
|
||||
}
|
||||
|
||||
command := []string{
|
||||
"storageattach", vmName,
|
||||
"--storagectl", controllerName,
|
||||
"--port", port,
|
||||
"--device", device,
|
||||
"--medium", "none",
|
||||
}
|
||||
|
||||
// Remove the ISO. Note that this will probably fail since
|
||||
// stepRemoveDevices does this as well. No big deal.
|
||||
driver.VBoxManage(command...)
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
// This step attaches the boot ISO, cd_files iso, and guest additions to the
|
||||
// virtual machine, if present.
|
||||
type StepAttachISOs struct {
|
||||
AttachBootIso bool
|
||||
ISOInterface string
|
||||
GuestAdditionsMode string
|
||||
GuestAdditionsInterface string
|
||||
diskUnmountCommands map[string][]string
|
||||
}
|
||||
|
||||
func (s *StepAttachISOs) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
// Check whether there is anything to attach
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
ui.Say("Mounting ISOs...")
|
||||
diskMountMap := map[string]string{}
|
||||
s.diskUnmountCommands = map[string][]string{}
|
||||
// Track the bootable iso (only used in virtualbox-iso builder. )
|
||||
if s.AttachBootIso {
|
||||
isoPath := state.Get("iso_path").(string)
|
||||
diskMountMap["boot_iso"] = isoPath
|
||||
}
|
||||
|
||||
// Determine if we even have a cd_files disk to attach
|
||||
if cdPathRaw, ok := state.GetOk("cd_path"); ok {
|
||||
cdFilesPath := cdPathRaw.(string)
|
||||
diskMountMap["cd_files"] = cdFilesPath
|
||||
}
|
||||
|
||||
// Determine if we have guest additions to attach
|
||||
if s.GuestAdditionsMode != GuestAdditionsModeAttach {
|
||||
log.Println("Not attaching guest additions since we're uploading.")
|
||||
} else {
|
||||
// Get the guest additions path since we're doing it
|
||||
guestAdditionsPath := state.Get("guest_additions_path").(string)
|
||||
diskMountMap["guest_additions"] = guestAdditionsPath
|
||||
}
|
||||
|
||||
if len(diskMountMap) == 0 {
|
||||
ui.Message("No ISOs to mount; continuing...")
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
driver := state.Get("driver").(Driver)
|
||||
vmName := state.Get("vmName").(string)
|
||||
|
||||
for diskCategory, isoPath := range diskMountMap {
|
||||
// If it's a symlink, resolve it to its target.
|
||||
resolvedIsoPath, err := filepath.EvalSymlinks(isoPath)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error resolving symlink for ISO: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
isoPath = resolvedIsoPath
|
||||
|
||||
// We have three different potential isos we can attach, so let's
|
||||
// assign each one its own spot so they don't conflict.
|
||||
var controllerName, device, port string
|
||||
switch diskCategory {
|
||||
case "boot_iso":
|
||||
// figure out controller path
|
||||
controllerName = "IDE Controller"
|
||||
port = "0"
|
||||
device = "1"
|
||||
if s.ISOInterface == "sata" {
|
||||
controllerName = "SATA Controller"
|
||||
port = "1"
|
||||
device = "0"
|
||||
}
|
||||
ui.Message("Mounting boot ISO...")
|
||||
case "guest_additions":
|
||||
controllerName = "IDE Controller"
|
||||
port = "1"
|
||||
device = "0"
|
||||
if s.GuestAdditionsInterface == "sata" {
|
||||
controllerName = "SATA Controller"
|
||||
port = "2"
|
||||
device = "0"
|
||||
}
|
||||
ui.Message("Mounting guest additions ISO...")
|
||||
case "cd_files":
|
||||
controllerName = "IDE Controller"
|
||||
port = "1"
|
||||
device = "1"
|
||||
if s.ISOInterface == "sata" {
|
||||
controllerName = "SATA Controller"
|
||||
port = "3"
|
||||
device = "0"
|
||||
}
|
||||
ui.Message("Mounting cd_files ISO...")
|
||||
}
|
||||
|
||||
// Attach the disk to the controller
|
||||
command := []string{
|
||||
"storageattach", vmName,
|
||||
"--storagectl", controllerName,
|
||||
"--port", port,
|
||||
"--device", device,
|
||||
"--type", "dvddrive",
|
||||
"--medium", isoPath,
|
||||
}
|
||||
if err := driver.VBoxManage(command...); err != nil {
|
||||
err := fmt.Errorf("Error attaching ISO: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// Track the disks we've mounted so we can remove them without having
|
||||
// to re-derive what was mounted where
|
||||
unmountCommand := []string{
|
||||
"storageattach", vmName,
|
||||
"--storagectl", controllerName,
|
||||
"--port", port,
|
||||
"--device", device,
|
||||
"--type", "dvddrive",
|
||||
"--medium", "none",
|
||||
}
|
||||
|
||||
s.diskUnmountCommands[diskCategory] = unmountCommand
|
||||
}
|
||||
|
||||
state.Put("disk_unmount_commands", s.diskUnmountCommands)
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepAttachISOs) Cleanup(state multistep.StateBag) {
|
||||
if len(s.diskUnmountCommands) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
driver := state.Get("driver").(Driver)
|
||||
|
||||
for _, command := range s.diskUnmountCommands {
|
||||
// Remove the ISO. Note that this will probably fail since
|
||||
// stepRemoveDevices does this as well. No big deal.
|
||||
err := driver.VBoxManage(command...)
|
||||
if err != nil {
|
||||
log.Printf("error detaching iso; probably was already detached " +
|
||||
"in step_remove_devices")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,7 +22,6 @@ import (
|
|||
// Produces:
|
||||
type StepRemoveDevices struct {
|
||||
Bundling VBoxBundleConfig
|
||||
GuestAdditionsInterface string
|
||||
}
|
||||
|
||||
func (s *StepRemoveDevices) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
|
@ -73,58 +72,28 @@ func (s *StepRemoveDevices) Run(ctx context.Context, state multistep.StateBag) m
|
|||
}
|
||||
}
|
||||
|
||||
if !s.Bundling.BundleISO {
|
||||
if _, ok := state.GetOk("attachedIso"); ok {
|
||||
controllerName := "IDE Controller"
|
||||
port := "0"
|
||||
device := "1"
|
||||
if _, ok := state.GetOk("attachedIsoOnSata"); ok {
|
||||
controllerName = "SATA Controller"
|
||||
port = "1"
|
||||
device = "0"
|
||||
var isoUnmountCommands map[string][]string
|
||||
isoUnmountCommandsRaw, ok := state.GetOk("disk_unmount_commands")
|
||||
if !ok {
|
||||
// No disks to unmount
|
||||
return multistep.ActionContinue
|
||||
} else {
|
||||
isoUnmountCommands = isoUnmountCommandsRaw.(map[string][]string)
|
||||
}
|
||||
|
||||
command := []string{
|
||||
"storageattach", vmName,
|
||||
"--storagectl", controllerName,
|
||||
"--port", port,
|
||||
"--device", device,
|
||||
"--medium", "none",
|
||||
for diskCategory, unmountCommand := range isoUnmountCommands {
|
||||
if diskCategory == "boot_iso" && s.Bundling.BundleISO {
|
||||
// skip the unmount if user wants to bundle the iso
|
||||
continue
|
||||
}
|
||||
|
||||
if err := driver.VBoxManage(command...); err != nil {
|
||||
if err := driver.VBoxManage(unmountCommand...); err != nil {
|
||||
err := fmt.Errorf("Error detaching ISO: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := state.GetOk("guest_additions_attached"); ok {
|
||||
ui.Message("Removing guest additions drive...")
|
||||
controllerName := "IDE Controller"
|
||||
port := "1"
|
||||
device := "0"
|
||||
if s.GuestAdditionsInterface == "sata" {
|
||||
controllerName = "SATA Controller"
|
||||
port = "2"
|
||||
device = "0"
|
||||
}
|
||||
command := []string{
|
||||
"storageattach", vmName,
|
||||
"--storagectl", controllerName,
|
||||
"--port", port,
|
||||
"--device", device,
|
||||
"--medium", "none",
|
||||
}
|
||||
if err := driver.VBoxManage(command...); err != nil {
|
||||
err := fmt.Errorf("Error removing guest additions: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
|
|
@ -37,7 +37,17 @@ func TestStepRemoveDevices_attachedIso(t *testing.T) {
|
|||
state := testState(t)
|
||||
step := new(StepRemoveDevices)
|
||||
|
||||
state.Put("attachedIso", true)
|
||||
diskUnmountCommands := map[string][]string{
|
||||
"boot_iso": []string{
|
||||
"storageattach", "myvm",
|
||||
"--storagectl", "IDE Controller",
|
||||
"--port", "0",
|
||||
"--device", "1",
|
||||
"--type", "dvddrive",
|
||||
"--medium", "none",
|
||||
},
|
||||
}
|
||||
state.Put("disk_unmount_commands", diskUnmountCommands)
|
||||
state.Put("vmName", "foo")
|
||||
|
||||
driver := state.Get("driver").(*DriverMock)
|
||||
|
@ -63,8 +73,17 @@ func TestStepRemoveDevices_attachedIsoOnSata(t *testing.T) {
|
|||
state := testState(t)
|
||||
step := new(StepRemoveDevices)
|
||||
|
||||
state.Put("attachedIso", true)
|
||||
state.Put("attachedIsoOnSata", true)
|
||||
diskUnmountCommands := map[string][]string{
|
||||
"boot_iso": []string{
|
||||
"storageattach", "myvm",
|
||||
"--storagectl", "SATA Controller",
|
||||
"--port", "0",
|
||||
"--device", "1",
|
||||
"--type", "dvddrive",
|
||||
"--medium", "none",
|
||||
},
|
||||
}
|
||||
state.Put("disk_unmount_commands", diskUnmountCommands)
|
||||
state.Put("vmName", "foo")
|
||||
|
||||
driver := state.Get("driver").(*DriverMock)
|
||||
|
|
|
@ -7,7 +7,6 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
vboxcommon "github.com/hashicorp/packer/builder/virtualbox/common"
|
||||
|
@ -32,6 +31,7 @@ type Config struct {
|
|||
common.HTTPConfig `mapstructure:",squash"`
|
||||
common.ISOConfig `mapstructure:",squash"`
|
||||
common.FloppyConfig `mapstructure:",squash"`
|
||||
common.CDConfig `mapstructure:",squash"`
|
||||
bootcommand.BootConfig `mapstructure:",squash"`
|
||||
vboxcommon.ExportConfig `mapstructure:",squash"`
|
||||
vboxcommon.OutputConfig `mapstructure:",squash"`
|
||||
|
@ -46,29 +46,6 @@ type Config struct {
|
|||
// The size, in megabytes, of the hard disk to create for the VM. By
|
||||
// default, this is 40000 (about 40 GB).
|
||||
DiskSize uint `mapstructure:"disk_size" required:"false"`
|
||||
// The path on the guest virtual machine where the VirtualBox guest
|
||||
// additions ISO will be uploaded. By default this is
|
||||
// VBoxGuestAdditions.iso which should upload into the login directory of
|
||||
// the user. This is a configuration template where the `{{ .Version }}`
|
||||
// variable is replaced with the VirtualBox version.
|
||||
GuestAdditionsPath string `mapstructure:"guest_additions_path" required:"false"`
|
||||
// The SHA256 checksum of the guest additions ISO that will be uploaded to
|
||||
// the guest VM. By default the checksums will be downloaded from the
|
||||
// VirtualBox website, so this only needs to be set if you want to be
|
||||
// explicit about the checksum.
|
||||
GuestAdditionsSHA256 string `mapstructure:"guest_additions_sha256" required:"false"`
|
||||
// The URL to the guest additions ISO to upload. This can also be a file
|
||||
// URL if the ISO is at a local path. By default, the VirtualBox builder
|
||||
// will attempt to find the guest additions ISO on the local file system.
|
||||
// If it is not available locally, the builder will download the proper
|
||||
// guest additions ISO from the internet. This is a template engine, and you
|
||||
// have access to the variable `{{ .Version }}`.
|
||||
GuestAdditionsURL string `mapstructure:"guest_additions_url" required:"false"`
|
||||
// The interface type to use to mount guest additions when
|
||||
// guest_additions_mode is set to attach. Will default to the value set in
|
||||
// iso_interface, if iso_interface is set. Will default to "ide", if
|
||||
// iso_interface is not set. Options are "ide" and "sata".
|
||||
GuestAdditionsInterface string `mapstructure:"guest_additions_interface" required:"false"`
|
||||
// The guest OS type being installed. By default this is other, but you can
|
||||
// get dramatic performance improvements by setting this to the proper
|
||||
// value. To view all available values for this run VBoxManage list
|
||||
|
@ -167,6 +144,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
|||
errs = packer.MultiErrorAppend(errs, b.config.ExportConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.ExportConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.FloppyConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.CDConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, b.config.OutputConfig.Prepare(&b.config.ctx, &b.config.PackerConfig)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.HTTPConfig.Prepare(&b.config.ctx)...)
|
||||
|
@ -184,14 +162,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
|||
b.config.DiskSize = 40000
|
||||
}
|
||||
|
||||
if b.config.GuestAdditionsMode == "" {
|
||||
b.config.GuestAdditionsMode = "upload"
|
||||
}
|
||||
|
||||
if b.config.GuestAdditionsPath == "" {
|
||||
b.config.GuestAdditionsPath = "VBoxGuestAdditions.iso"
|
||||
}
|
||||
|
||||
if b.config.HardDriveInterface == "" {
|
||||
b.config.HardDriveInterface = "ide"
|
||||
}
|
||||
|
@ -244,29 +214,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
|||
errs, errors.New("iso_interface can only be ide or sata"))
|
||||
}
|
||||
|
||||
validMode := false
|
||||
validModes := []string{
|
||||
vboxcommon.GuestAdditionsModeDisable,
|
||||
vboxcommon.GuestAdditionsModeAttach,
|
||||
vboxcommon.GuestAdditionsModeUpload,
|
||||
}
|
||||
|
||||
for _, mode := range validModes {
|
||||
if b.config.GuestAdditionsMode == mode {
|
||||
validMode = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !validMode {
|
||||
errs = packer.MultiErrorAppend(errs,
|
||||
fmt.Errorf("guest_additions_mode is invalid. Must be one of: %v", validModes))
|
||||
}
|
||||
|
||||
if b.config.GuestAdditionsSHA256 != "" {
|
||||
b.config.GuestAdditionsSHA256 = strings.ToLower(b.config.GuestAdditionsSHA256)
|
||||
}
|
||||
|
||||
// Warnings
|
||||
if b.config.ShutdownCommand == "" {
|
||||
warnings = append(warnings,
|
||||
|
@ -312,6 +259,10 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||
Directories: b.config.FloppyConfig.FloppyDirectories,
|
||||
Label: b.config.FloppyConfig.FloppyLabel,
|
||||
},
|
||||
&common.StepCreateCD{
|
||||
Files: b.config.CDConfig.CDFiles,
|
||||
Label: b.config.CDConfig.CDLabel,
|
||||
},
|
||||
new(vboxcommon.StepHTTPIPDiscover),
|
||||
&common.StepHTTPServer{
|
||||
HTTPDir: b.config.HTTPDir,
|
||||
|
@ -327,8 +278,9 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||
new(vboxcommon.StepSuppressMessages),
|
||||
new(stepCreateVM),
|
||||
new(stepCreateDisk),
|
||||
new(stepAttachISO),
|
||||
&vboxcommon.StepAttachGuestAdditions{
|
||||
&vboxcommon.StepAttachISOs{
|
||||
AttachBootIso: true,
|
||||
ISOInterface: b.config.ISOInterface,
|
||||
GuestAdditionsMode: b.config.GuestAdditionsMode,
|
||||
GuestAdditionsInterface: b.config.GuestAdditionsInterface,
|
||||
},
|
||||
|
@ -387,7 +339,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||
},
|
||||
&vboxcommon.StepRemoveDevices{
|
||||
Bundling: b.config.VBoxBundleConfig,
|
||||
GuestAdditionsInterface: b.config.GuestAdditionsInterface,
|
||||
},
|
||||
&vboxcommon.StepVBoxManage{
|
||||
Commands: b.config.VBoxManagePost,
|
||||
|
|
|
@ -28,6 +28,8 @@ type FlatConfig struct {
|
|||
FloppyFiles []string `mapstructure:"floppy_files" cty:"floppy_files" hcl:"floppy_files"`
|
||||
FloppyDirectories []string `mapstructure:"floppy_dirs" cty:"floppy_dirs" hcl:"floppy_dirs"`
|
||||
FloppyLabel *string `mapstructure:"floppy_label" cty:"floppy_label" hcl:"floppy_label"`
|
||||
CDFiles []string `mapstructure:"cd_files" cty:"cd_files" hcl:"cd_files"`
|
||||
CDLabel *string `mapstructure:"cd_label" cty:"cd_label" hcl:"cd_label"`
|
||||
BootGroupInterval *string `mapstructure:"boot_keygroup_interval" cty:"boot_keygroup_interval" hcl:"boot_keygroup_interval"`
|
||||
BootWait *string `mapstructure:"boot_wait" cty:"boot_wait" hcl:"boot_wait"`
|
||||
BootCommand []string `mapstructure:"boot_command" cty:"boot_command" hcl:"boot_command"`
|
||||
|
@ -105,12 +107,12 @@ type FlatConfig struct {
|
|||
VBoxManagePost [][]string `mapstructure:"vboxmanage_post" required:"false" cty:"vboxmanage_post" hcl:"vboxmanage_post"`
|
||||
VBoxVersionFile *string `mapstructure:"virtualbox_version_file" required:"false" cty:"virtualbox_version_file" hcl:"virtualbox_version_file"`
|
||||
BundleISO *bool `mapstructure:"bundle_iso" required:"false" cty:"bundle_iso" hcl:"bundle_iso"`
|
||||
GuestAdditionsMode *string `mapstructure:"guest_additions_mode" required:"false" cty:"guest_additions_mode" hcl:"guest_additions_mode"`
|
||||
DiskSize *uint `mapstructure:"disk_size" required:"false" cty:"disk_size" hcl:"disk_size"`
|
||||
GuestAdditionsPath *string `mapstructure:"guest_additions_path" required:"false" cty:"guest_additions_path" hcl:"guest_additions_path"`
|
||||
GuestAdditionsSHA256 *string `mapstructure:"guest_additions_sha256" required:"false" cty:"guest_additions_sha256" hcl:"guest_additions_sha256"`
|
||||
GuestAdditionsURL *string `mapstructure:"guest_additions_url" required:"false" cty:"guest_additions_url" hcl:"guest_additions_url"`
|
||||
GuestAdditionsMode *string `mapstructure:"guest_additions_mode" cty:"guest_additions_mode" hcl:"guest_additions_mode"`
|
||||
GuestAdditionsInterface *string `mapstructure:"guest_additions_interface" required:"false" cty:"guest_additions_interface" hcl:"guest_additions_interface"`
|
||||
GuestAdditionsPath *string `mapstructure:"guest_additions_path" cty:"guest_additions_path" hcl:"guest_additions_path"`
|
||||
GuestAdditionsSHA256 *string `mapstructure:"guest_additions_sha256" cty:"guest_additions_sha256" hcl:"guest_additions_sha256"`
|
||||
GuestAdditionsURL *string `mapstructure:"guest_additions_url" required:"false" cty:"guest_additions_url" hcl:"guest_additions_url"`
|
||||
DiskSize *uint `mapstructure:"disk_size" required:"false" cty:"disk_size" hcl:"disk_size"`
|
||||
GuestOSType *string `mapstructure:"guest_os_type" required:"false" cty:"guest_os_type" hcl:"guest_os_type"`
|
||||
HardDriveDiscard *bool `mapstructure:"hard_drive_discard" required:"false" cty:"hard_drive_discard" hcl:"hard_drive_discard"`
|
||||
HardDriveInterface *string `mapstructure:"hard_drive_interface" required:"false" cty:"hard_drive_interface" hcl:"hard_drive_interface"`
|
||||
|
@ -154,6 +156,8 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
|||
"floppy_files": &hcldec.AttrSpec{Name: "floppy_files", Type: cty.List(cty.String), Required: false},
|
||||
"floppy_dirs": &hcldec.AttrSpec{Name: "floppy_dirs", Type: cty.List(cty.String), Required: false},
|
||||
"floppy_label": &hcldec.AttrSpec{Name: "floppy_label", Type: cty.String, Required: false},
|
||||
"cd_files": &hcldec.AttrSpec{Name: "cd_files", Type: cty.List(cty.String), Required: false},
|
||||
"cd_label": &hcldec.AttrSpec{Name: "cd_label", Type: cty.String, Required: false},
|
||||
"boot_keygroup_interval": &hcldec.AttrSpec{Name: "boot_keygroup_interval", Type: cty.String, Required: false},
|
||||
"boot_wait": &hcldec.AttrSpec{Name: "boot_wait", Type: cty.String, Required: false},
|
||||
"boot_command": &hcldec.AttrSpec{Name: "boot_command", Type: cty.List(cty.String), Required: false},
|
||||
|
@ -232,11 +236,11 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
|||
"virtualbox_version_file": &hcldec.AttrSpec{Name: "virtualbox_version_file", Type: cty.String, Required: false},
|
||||
"bundle_iso": &hcldec.AttrSpec{Name: "bundle_iso", Type: cty.Bool, Required: false},
|
||||
"guest_additions_mode": &hcldec.AttrSpec{Name: "guest_additions_mode", Type: cty.String, Required: false},
|
||||
"disk_size": &hcldec.AttrSpec{Name: "disk_size", Type: cty.Number, Required: false},
|
||||
"guest_additions_interface": &hcldec.AttrSpec{Name: "guest_additions_interface", Type: cty.String, Required: false},
|
||||
"guest_additions_path": &hcldec.AttrSpec{Name: "guest_additions_path", Type: cty.String, Required: false},
|
||||
"guest_additions_sha256": &hcldec.AttrSpec{Name: "guest_additions_sha256", Type: cty.String, Required: false},
|
||||
"guest_additions_url": &hcldec.AttrSpec{Name: "guest_additions_url", Type: cty.String, Required: false},
|
||||
"guest_additions_interface": &hcldec.AttrSpec{Name: "guest_additions_interface", Type: cty.String, Required: false},
|
||||
"disk_size": &hcldec.AttrSpec{Name: "disk_size", Type: cty.Number, Required: false},
|
||||
"guest_os_type": &hcldec.AttrSpec{Name: "guest_os_type", Type: cty.String, Required: false},
|
||||
"hard_drive_discard": &hcldec.AttrSpec{Name: "hard_drive_discard", Type: cty.Bool, Required: false},
|
||||
"hard_drive_interface": &hcldec.AttrSpec{Name: "hard_drive_interface", Type: cty.String, Required: false},
|
||||
|
|
|
@ -1,105 +0,0 @@
|
|||
package iso
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
vboxcommon "github.com/hashicorp/packer/builder/virtualbox/common"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
// This step attaches the ISO to the virtual machine.
|
||||
//
|
||||
// Uses:
|
||||
//
|
||||
// Produces:
|
||||
type stepAttachISO struct {
|
||||
diskPath string
|
||||
}
|
||||
|
||||
func (s *stepAttachISO) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("config").(*Config)
|
||||
driver := state.Get("driver").(vboxcommon.Driver)
|
||||
isoPath := state.Get("iso_path").(string)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
vmName := state.Get("vmName").(string)
|
||||
|
||||
controllerName := "IDE Controller"
|
||||
port := "0"
|
||||
device := "1"
|
||||
if config.ISOInterface == "sata" {
|
||||
controllerName = "SATA Controller"
|
||||
port = "1"
|
||||
device = "0"
|
||||
}
|
||||
|
||||
// If it's a symlink, resolve it to it's target.
|
||||
resolvedIsoPath, err := filepath.EvalSymlinks(isoPath)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error resolving symlink for ISO: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
isoPath = resolvedIsoPath
|
||||
|
||||
// Attach the disk to the controller
|
||||
command := []string{
|
||||
"storageattach", vmName,
|
||||
"--storagectl", controllerName,
|
||||
"--port", port,
|
||||
"--device", device,
|
||||
"--type", "dvddrive",
|
||||
"--medium", isoPath,
|
||||
}
|
||||
if err := driver.VBoxManage(command...); err != nil {
|
||||
err := fmt.Errorf("Error attaching ISO: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// Track the path so that we can unregister it from VirtualBox later
|
||||
s.diskPath = isoPath
|
||||
|
||||
// Set some state so we know to remove
|
||||
state.Put("attachedIso", true)
|
||||
if controllerName == "SATA Controller" {
|
||||
state.Put("attachedIsoOnSata", true)
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *stepAttachISO) Cleanup(state multistep.StateBag) {
|
||||
if s.diskPath == "" {
|
||||
return
|
||||
}
|
||||
|
||||
config := state.Get("config").(*Config)
|
||||
driver := state.Get("driver").(vboxcommon.Driver)
|
||||
vmName := state.Get("vmName").(string)
|
||||
|
||||
controllerName := "IDE Controller"
|
||||
port := "0"
|
||||
device := "1"
|
||||
if config.ISOInterface == "sata" {
|
||||
controllerName = "SATA Controller"
|
||||
port = "1"
|
||||
device = "0"
|
||||
}
|
||||
|
||||
command := []string{
|
||||
"storageattach", vmName,
|
||||
"--storagectl", controllerName,
|
||||
"--port", port,
|
||||
"--device", device,
|
||||
"--medium", "none",
|
||||
}
|
||||
|
||||
// Remove the ISO. Note that this will probably fail since
|
||||
// stepRemoveDevices does this as well. No big deal.
|
||||
driver.VBoxManage(command...)
|
||||
}
|
|
@ -60,6 +60,10 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||
Directories: b.config.FloppyConfig.FloppyDirectories,
|
||||
Label: b.config.FloppyConfig.FloppyLabel,
|
||||
},
|
||||
&common.StepCreateCD{
|
||||
Files: b.config.CDConfig.CDFiles,
|
||||
Label: b.config.CDConfig.CDLabel,
|
||||
},
|
||||
new(vboxcommon.StepHTTPIPDiscover),
|
||||
&common.StepHTTPServer{
|
||||
HTTPDir: b.config.HTTPDir,
|
||||
|
@ -91,7 +95,9 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||
ImportFlags: b.config.ImportFlags,
|
||||
KeepRegistered: b.config.KeepRegistered,
|
||||
},
|
||||
&vboxcommon.StepAttachGuestAdditions{
|
||||
&vboxcommon.StepAttachISOs{
|
||||
AttachBootIso: false,
|
||||
ISOInterface: b.config.GuestAdditionsInterface,
|
||||
GuestAdditionsMode: b.config.GuestAdditionsMode,
|
||||
GuestAdditionsInterface: b.config.GuestAdditionsInterface,
|
||||
},
|
||||
|
@ -148,9 +154,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||
DisableShutdown: b.config.DisableShutdown,
|
||||
ACPIShutdown: b.config.ACPIShutdown,
|
||||
},
|
||||
&vboxcommon.StepRemoveDevices{
|
||||
GuestAdditionsInterface: b.config.GuestAdditionsInterface,
|
||||
},
|
||||
&vboxcommon.StepRemoveDevices{},
|
||||
&vboxcommon.StepVBoxManage{
|
||||
Commands: b.config.VBoxManagePost,
|
||||
Ctx: b.config.ctx,
|
||||
|
|
|
@ -5,7 +5,6 @@ package ovf
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
vboxcommon "github.com/hashicorp/packer/builder/virtualbox/common"
|
||||
"github.com/hashicorp/packer/common"
|
||||
|
@ -20,6 +19,7 @@ type Config struct {
|
|||
common.PackerConfig `mapstructure:",squash"`
|
||||
common.HTTPConfig `mapstructure:",squash"`
|
||||
common.FloppyConfig `mapstructure:",squash"`
|
||||
common.CDConfig `mapstructure:",squash"`
|
||||
bootcommand.BootConfig `mapstructure:",squash"`
|
||||
vboxcommon.ExportConfig `mapstructure:",squash"`
|
||||
vboxcommon.OutputConfig `mapstructure:",squash"`
|
||||
|
@ -50,38 +50,6 @@ type Config struct {
|
|||
// this is not recommended since these files can be very large and
|
||||
// corruption does happen from time to time.
|
||||
Checksum string `mapstructure:"checksum" required:"true"`
|
||||
// The method by which guest additions are
|
||||
// made available to the guest for installation. Valid options are upload,
|
||||
// attach, or disable. If the mode is attach the guest additions ISO will
|
||||
// be attached as a CD device to the virtual machine. If the mode is upload
|
||||
// the guest additions ISO will be uploaded to the path specified by
|
||||
// guest_additions_path. The default value is upload. If disable is used,
|
||||
// guest additions won't be downloaded, either.
|
||||
GuestAdditionsMode string `mapstructure:"guest_additions_mode" required:"false"`
|
||||
// The path on the guest virtual machine
|
||||
// where the VirtualBox guest additions ISO will be uploaded. By default this
|
||||
// is VBoxGuestAdditions.iso which should upload into the login directory of
|
||||
// the user. This is a configuration
|
||||
// template where the Version
|
||||
// variable is replaced with the VirtualBox version.
|
||||
GuestAdditionsPath string `mapstructure:"guest_additions_path" required:"false"`
|
||||
// The interface type to use to mount
|
||||
// guest additions when guest_additions_mode is set to attach. Will
|
||||
// default to the value set in iso_interface, if iso_interface is set.
|
||||
// Will default to "ide", if iso_interface is not set. Options are "ide" and
|
||||
// "sata".
|
||||
GuestAdditionsInterface string `mapstructure:"guest_additions_interface" required:"false"`
|
||||
// The SHA256 checksum of the guest
|
||||
// additions ISO that will be uploaded to the guest VM. By default the
|
||||
// checksums will be downloaded from the VirtualBox website, so this only needs
|
||||
// to be set if you want to be explicit about the checksum.
|
||||
GuestAdditionsSHA256 string `mapstructure:"guest_additions_sha256" required:"false"`
|
||||
// The URL to the guest additions ISO
|
||||
// to upload. This can also be a file URL if the ISO is at a local path. By
|
||||
// default, the VirtualBox builder will attempt to find the guest additions ISO
|
||||
// on the local file system. If it is not available locally, the builder will
|
||||
// download the proper guest additions ISO from the internet.
|
||||
GuestAdditionsURL string `mapstructure:"guest_additions_url" required:"false"`
|
||||
// Additional flags to pass to
|
||||
// VBoxManage import. This can be used to add additional command-line flags
|
||||
// such as --eula-accept to accept a EULA in the OVF.
|
||||
|
@ -131,17 +99,6 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
|
|||
}
|
||||
|
||||
// Defaults
|
||||
if c.GuestAdditionsMode == "" {
|
||||
c.GuestAdditionsMode = "upload"
|
||||
}
|
||||
|
||||
if c.GuestAdditionsPath == "" {
|
||||
c.GuestAdditionsPath = "VBoxGuestAdditions.iso"
|
||||
}
|
||||
if c.GuestAdditionsInterface == "" {
|
||||
c.GuestAdditionsInterface = "ide"
|
||||
}
|
||||
|
||||
if c.VMName == "" {
|
||||
c.VMName = fmt.Sprintf(
|
||||
"packer-%s-%d", c.PackerBuildName, interpolate.InitTime.Unix())
|
||||
|
@ -152,6 +109,7 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
|
|||
errs = packer.MultiErrorAppend(errs, c.ExportConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.ExportConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.FloppyConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.CDConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.HTTPConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.OutputConfig.Prepare(&c.ctx, &c.PackerConfig)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.RunConfig.Prepare(&c.ctx)...)
|
||||
|
@ -166,27 +124,8 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
|
|||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("source_path is required"))
|
||||
}
|
||||
|
||||
validMode := false
|
||||
validModes := []string{
|
||||
vboxcommon.GuestAdditionsModeDisable,
|
||||
vboxcommon.GuestAdditionsModeAttach,
|
||||
vboxcommon.GuestAdditionsModeUpload,
|
||||
}
|
||||
|
||||
for _, mode := range validModes {
|
||||
if c.GuestAdditionsMode == mode {
|
||||
validMode = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !validMode {
|
||||
errs = packer.MultiErrorAppend(errs,
|
||||
fmt.Errorf("guest_additions_mode is invalid. Must be one of: %v", validModes))
|
||||
}
|
||||
|
||||
if c.GuestAdditionsSHA256 != "" {
|
||||
c.GuestAdditionsSHA256 = strings.ToLower(c.GuestAdditionsSHA256)
|
||||
if c.GuestAdditionsInterface == "" {
|
||||
c.GuestAdditionsInterface = "ide"
|
||||
}
|
||||
|
||||
// Warnings
|
||||
|
|
|
@ -23,6 +23,8 @@ type FlatConfig struct {
|
|||
FloppyFiles []string `mapstructure:"floppy_files" cty:"floppy_files" hcl:"floppy_files"`
|
||||
FloppyDirectories []string `mapstructure:"floppy_dirs" cty:"floppy_dirs" hcl:"floppy_dirs"`
|
||||
FloppyLabel *string `mapstructure:"floppy_label" cty:"floppy_label" hcl:"floppy_label"`
|
||||
CDFiles []string `mapstructure:"cd_files" cty:"cd_files" hcl:"cd_files"`
|
||||
CDLabel *string `mapstructure:"cd_label" cty:"cd_label" hcl:"cd_label"`
|
||||
BootGroupInterval *string `mapstructure:"boot_keygroup_interval" cty:"boot_keygroup_interval" hcl:"boot_keygroup_interval"`
|
||||
BootWait *string `mapstructure:"boot_wait" cty:"boot_wait" hcl:"boot_wait"`
|
||||
BootCommand []string `mapstructure:"boot_command" cty:"boot_command" hcl:"boot_command"`
|
||||
|
@ -95,12 +97,12 @@ type FlatConfig struct {
|
|||
VBoxManage [][]string `mapstructure:"vboxmanage" required:"false" cty:"vboxmanage" hcl:"vboxmanage"`
|
||||
VBoxManagePost [][]string `mapstructure:"vboxmanage_post" required:"false" cty:"vboxmanage_post" hcl:"vboxmanage_post"`
|
||||
VBoxVersionFile *string `mapstructure:"virtualbox_version_file" required:"false" cty:"virtualbox_version_file" hcl:"virtualbox_version_file"`
|
||||
GuestAdditionsMode *string `mapstructure:"guest_additions_mode" required:"false" cty:"guest_additions_mode" hcl:"guest_additions_mode"`
|
||||
Checksum *string `mapstructure:"checksum" required:"true" cty:"checksum" hcl:"checksum"`
|
||||
GuestAdditionsPath *string `mapstructure:"guest_additions_path" required:"false" cty:"guest_additions_path" hcl:"guest_additions_path"`
|
||||
GuestAdditionsMode *string `mapstructure:"guest_additions_mode" cty:"guest_additions_mode" hcl:"guest_additions_mode"`
|
||||
GuestAdditionsInterface *string `mapstructure:"guest_additions_interface" required:"false" cty:"guest_additions_interface" hcl:"guest_additions_interface"`
|
||||
GuestAdditionsSHA256 *string `mapstructure:"guest_additions_sha256" required:"false" cty:"guest_additions_sha256" hcl:"guest_additions_sha256"`
|
||||
GuestAdditionsPath *string `mapstructure:"guest_additions_path" cty:"guest_additions_path" hcl:"guest_additions_path"`
|
||||
GuestAdditionsSHA256 *string `mapstructure:"guest_additions_sha256" cty:"guest_additions_sha256" hcl:"guest_additions_sha256"`
|
||||
GuestAdditionsURL *string `mapstructure:"guest_additions_url" required:"false" cty:"guest_additions_url" hcl:"guest_additions_url"`
|
||||
Checksum *string `mapstructure:"checksum" required:"true" cty:"checksum" hcl:"checksum"`
|
||||
ImportFlags []string `mapstructure:"import_flags" required:"false" cty:"import_flags" hcl:"import_flags"`
|
||||
ImportOpts *string `mapstructure:"import_opts" required:"false" cty:"import_opts" hcl:"import_opts"`
|
||||
SourcePath *string `mapstructure:"source_path" required:"true" cty:"source_path" hcl:"source_path"`
|
||||
|
@ -136,6 +138,8 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
|||
"floppy_files": &hcldec.AttrSpec{Name: "floppy_files", Type: cty.List(cty.String), Required: false},
|
||||
"floppy_dirs": &hcldec.AttrSpec{Name: "floppy_dirs", Type: cty.List(cty.String), Required: false},
|
||||
"floppy_label": &hcldec.AttrSpec{Name: "floppy_label", Type: cty.String, Required: false},
|
||||
"cd_files": &hcldec.AttrSpec{Name: "cd_files", Type: cty.List(cty.String), Required: false},
|
||||
"cd_label": &hcldec.AttrSpec{Name: "cd_label", Type: cty.String, Required: false},
|
||||
"boot_keygroup_interval": &hcldec.AttrSpec{Name: "boot_keygroup_interval", Type: cty.String, Required: false},
|
||||
"boot_wait": &hcldec.AttrSpec{Name: "boot_wait", Type: cty.String, Required: false},
|
||||
"boot_command": &hcldec.AttrSpec{Name: "boot_command", Type: cty.List(cty.String), Required: false},
|
||||
|
@ -209,11 +213,11 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
|||
"vboxmanage_post": &hcldec.AttrSpec{Name: "vboxmanage_post", Type: cty.List(cty.List(cty.String)), Required: false},
|
||||
"virtualbox_version_file": &hcldec.AttrSpec{Name: "virtualbox_version_file", Type: cty.String, Required: false},
|
||||
"guest_additions_mode": &hcldec.AttrSpec{Name: "guest_additions_mode", Type: cty.String, Required: false},
|
||||
"checksum": &hcldec.AttrSpec{Name: "checksum", Type: cty.String, Required: false},
|
||||
"guest_additions_path": &hcldec.AttrSpec{Name: "guest_additions_path", Type: cty.String, Required: false},
|
||||
"guest_additions_interface": &hcldec.AttrSpec{Name: "guest_additions_interface", Type: cty.String, Required: false},
|
||||
"guest_additions_path": &hcldec.AttrSpec{Name: "guest_additions_path", Type: cty.String, Required: false},
|
||||
"guest_additions_sha256": &hcldec.AttrSpec{Name: "guest_additions_sha256", Type: cty.String, Required: false},
|
||||
"guest_additions_url": &hcldec.AttrSpec{Name: "guest_additions_url", Type: cty.String, Required: false},
|
||||
"checksum": &hcldec.AttrSpec{Name: "checksum", Type: cty.String, Required: false},
|
||||
"import_flags": &hcldec.AttrSpec{Name: "import_flags", Type: cty.List(cty.String), Required: false},
|
||||
"import_opts": &hcldec.AttrSpec{Name: "import_opts", Type: cty.String, Required: false},
|
||||
"source_path": &hcldec.AttrSpec{Name: "source_path", Type: cty.String, Required: false},
|
||||
|
|
|
@ -54,6 +54,10 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||
Files: b.config.FloppyConfig.FloppyFiles,
|
||||
Directories: b.config.FloppyConfig.FloppyDirectories,
|
||||
},
|
||||
&common.StepCreateCD{
|
||||
Files: b.config.CDConfig.CDFiles,
|
||||
Label: b.config.CDConfig.CDLabel,
|
||||
},
|
||||
&StepSetSnapshot{
|
||||
Name: b.config.VMName,
|
||||
AttachSnapshot: b.config.AttachSnapshot,
|
||||
|
@ -75,8 +79,11 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||
&StepImport{
|
||||
Name: b.config.VMName,
|
||||
},
|
||||
&vboxcommon.StepAttachGuestAdditions{
|
||||
&vboxcommon.StepAttachISOs{
|
||||
AttachBootIso: false,
|
||||
ISOInterface: b.config.GuestAdditionsInterface,
|
||||
GuestAdditionsMode: b.config.GuestAdditionsMode,
|
||||
GuestAdditionsInterface: b.config.GuestAdditionsInterface,
|
||||
},
|
||||
&vboxcommon.StepConfigureVRDP{
|
||||
VRDPBindAddress: b.config.VRDPBindAddress,
|
||||
|
@ -131,6 +138,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||
DisableShutdown: b.config.DisableShutdown,
|
||||
ACPIShutdown: b.config.ACPIShutdown,
|
||||
},
|
||||
&vboxcommon.StepRemoveDevices{},
|
||||
&vboxcommon.StepVBoxManage{
|
||||
Commands: b.config.VBoxManagePost,
|
||||
Ctx: b.config.ctx,
|
||||
|
|
|
@ -6,7 +6,6 @@ package vm
|
|||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
vboxcommon "github.com/hashicorp/packer/builder/virtualbox/common"
|
||||
|
@ -22,6 +21,7 @@ type Config struct {
|
|||
common.PackerConfig `mapstructure:",squash"`
|
||||
common.HTTPConfig `mapstructure:",squash"`
|
||||
common.FloppyConfig `mapstructure:",squash"`
|
||||
common.CDConfig `mapstructure:",squash"`
|
||||
bootcommand.BootConfig `mapstructure:",squash"`
|
||||
vboxcommon.ExportConfig `mapstructure:",squash"`
|
||||
vboxcommon.OutputConfig `mapstructure:",squash"`
|
||||
|
@ -30,33 +30,7 @@ type Config struct {
|
|||
vboxcommon.ShutdownConfig `mapstructure:",squash"`
|
||||
vboxcommon.VBoxManageConfig `mapstructure:",squash"`
|
||||
vboxcommon.VBoxVersionConfig `mapstructure:",squash"`
|
||||
|
||||
// The method by which guest additions are
|
||||
// made available to the guest for installation. Valid options are `upload`,
|
||||
// `attach`, or `disable`. If the mode is `attach` the guest additions ISO will
|
||||
// be attached as a CD device to the virtual machine. If the mode is `upload`
|
||||
// the guest additions ISO will be uploaded to the path specified by
|
||||
// `guest_additions_path`. The default value is `upload`. If `disable` is used,
|
||||
// guest additions won't be downloaded, either.
|
||||
GuestAdditionsMode string `mapstructure:"guest_additions_mode"`
|
||||
// The path on the guest virtual machine
|
||||
// where the VirtualBox guest additions ISO will be uploaded. By default this
|
||||
// is `VBoxGuestAdditions.iso` which should upload into the login directory of
|
||||
// the user. This is a [configuration
|
||||
// template](/docs/templates/engine) where the `Version`
|
||||
// variable is replaced with the VirtualBox version.
|
||||
GuestAdditionsPath string `mapstructure:"guest_additions_path"`
|
||||
// The SHA256 checksum of the guest
|
||||
// additions ISO that will be uploaded to the guest VM. By default the
|
||||
// checksums will be downloaded from the VirtualBox website, so this only needs
|
||||
// to be set if you want to be explicit about the checksum.
|
||||
GuestAdditionsSHA256 string `mapstructure:"guest_additions_sha256"`
|
||||
// The URL to the guest additions ISO
|
||||
// to upload. This can also be a file URL if the ISO is at a local path. By
|
||||
// default, the VirtualBox builder will attempt to find the guest additions ISO
|
||||
// on the local file system. If it is not available locally, the builder will
|
||||
// download the proper guest additions ISO from the internet.
|
||||
GuestAdditionsURL string `mapstructure:"guest_additions_url" required:"false"`
|
||||
vboxcommon.GuestAdditionsConfig `mapstructure:",squash"`
|
||||
// This is the name of the virtual machine to which the
|
||||
// builder shall attach.
|
||||
VMName string `mapstructure:"vm_name" required:"true"`
|
||||
|
@ -109,14 +83,6 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
|
|||
}
|
||||
|
||||
// Defaults
|
||||
if c.GuestAdditionsMode == "" {
|
||||
c.GuestAdditionsMode = "upload"
|
||||
}
|
||||
|
||||
if c.GuestAdditionsPath == "" {
|
||||
c.GuestAdditionsPath = "VBoxGuestAdditions.iso"
|
||||
}
|
||||
|
||||
if c.PostShutdownDelay == 0 {
|
||||
c.PostShutdownDelay = 2 * time.Second
|
||||
}
|
||||
|
@ -125,6 +91,7 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
|
|||
var errs *packer.MultiError
|
||||
errs = packer.MultiErrorAppend(errs, c.ExportConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.FloppyConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.CDConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.HTTPConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.OutputConfig.Prepare(&c.ctx, &c.PackerConfig)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.RunConfig.Prepare(&c.ctx)...)
|
||||
|
@ -133,6 +100,11 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
|
|||
errs = packer.MultiErrorAppend(errs, c.VBoxManageConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.VBoxVersionConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.BootConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.GuestAdditionsConfig.Prepare(&c.ctx)...)
|
||||
|
||||
if c.GuestAdditionsInterface == "" {
|
||||
c.GuestAdditionsInterface = "ide"
|
||||
}
|
||||
|
||||
log.Printf("PostShutdownDelay: %s", c.PostShutdownDelay)
|
||||
|
||||
|
@ -141,29 +113,6 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
|
|||
fmt.Errorf("vm_name is required"))
|
||||
}
|
||||
|
||||
validMode := false
|
||||
validModes := []string{
|
||||
vboxcommon.GuestAdditionsModeDisable,
|
||||
vboxcommon.GuestAdditionsModeAttach,
|
||||
vboxcommon.GuestAdditionsModeUpload,
|
||||
}
|
||||
|
||||
for _, mode := range validModes {
|
||||
if c.GuestAdditionsMode == mode {
|
||||
validMode = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !validMode {
|
||||
errs = packer.MultiErrorAppend(errs,
|
||||
fmt.Errorf("guest_additions_mode is invalid. Must be one of: %v", validModes))
|
||||
}
|
||||
|
||||
if c.GuestAdditionsSHA256 != "" {
|
||||
c.GuestAdditionsSHA256 = strings.ToLower(c.GuestAdditionsSHA256)
|
||||
}
|
||||
|
||||
// Warnings
|
||||
var warnings []string
|
||||
if c.TargetSnapshot == "" && c.SkipExport {
|
||||
|
|
|
@ -23,6 +23,8 @@ type FlatConfig struct {
|
|||
FloppyFiles []string `mapstructure:"floppy_files" cty:"floppy_files" hcl:"floppy_files"`
|
||||
FloppyDirectories []string `mapstructure:"floppy_dirs" cty:"floppy_dirs" hcl:"floppy_dirs"`
|
||||
FloppyLabel *string `mapstructure:"floppy_label" cty:"floppy_label" hcl:"floppy_label"`
|
||||
CDFiles []string `mapstructure:"cd_files" cty:"cd_files" hcl:"cd_files"`
|
||||
CDLabel *string `mapstructure:"cd_label" cty:"cd_label" hcl:"cd_label"`
|
||||
BootGroupInterval *string `mapstructure:"boot_keygroup_interval" cty:"boot_keygroup_interval" hcl:"boot_keygroup_interval"`
|
||||
BootWait *string `mapstructure:"boot_wait" cty:"boot_wait" hcl:"boot_wait"`
|
||||
BootCommand []string `mapstructure:"boot_command" cty:"boot_command" hcl:"boot_command"`
|
||||
|
@ -96,6 +98,7 @@ type FlatConfig struct {
|
|||
VBoxManagePost [][]string `mapstructure:"vboxmanage_post" required:"false" cty:"vboxmanage_post" hcl:"vboxmanage_post"`
|
||||
VBoxVersionFile *string `mapstructure:"virtualbox_version_file" required:"false" cty:"virtualbox_version_file" hcl:"virtualbox_version_file"`
|
||||
GuestAdditionsMode *string `mapstructure:"guest_additions_mode" cty:"guest_additions_mode" hcl:"guest_additions_mode"`
|
||||
GuestAdditionsInterface *string `mapstructure:"guest_additions_interface" required:"false" cty:"guest_additions_interface" hcl:"guest_additions_interface"`
|
||||
GuestAdditionsPath *string `mapstructure:"guest_additions_path" cty:"guest_additions_path" hcl:"guest_additions_path"`
|
||||
GuestAdditionsSHA256 *string `mapstructure:"guest_additions_sha256" cty:"guest_additions_sha256" hcl:"guest_additions_sha256"`
|
||||
GuestAdditionsURL *string `mapstructure:"guest_additions_url" required:"false" cty:"guest_additions_url" hcl:"guest_additions_url"`
|
||||
|
@ -133,6 +136,8 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
|||
"floppy_files": &hcldec.AttrSpec{Name: "floppy_files", Type: cty.List(cty.String), Required: false},
|
||||
"floppy_dirs": &hcldec.AttrSpec{Name: "floppy_dirs", Type: cty.List(cty.String), Required: false},
|
||||
"floppy_label": &hcldec.AttrSpec{Name: "floppy_label", Type: cty.String, Required: false},
|
||||
"cd_files": &hcldec.AttrSpec{Name: "cd_files", Type: cty.List(cty.String), Required: false},
|
||||
"cd_label": &hcldec.AttrSpec{Name: "cd_label", Type: cty.String, Required: false},
|
||||
"boot_keygroup_interval": &hcldec.AttrSpec{Name: "boot_keygroup_interval", Type: cty.String, Required: false},
|
||||
"boot_wait": &hcldec.AttrSpec{Name: "boot_wait", Type: cty.String, Required: false},
|
||||
"boot_command": &hcldec.AttrSpec{Name: "boot_command", Type: cty.List(cty.String), Required: false},
|
||||
|
@ -206,6 +211,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
|||
"vboxmanage_post": &hcldec.AttrSpec{Name: "vboxmanage_post", Type: cty.List(cty.List(cty.String)), Required: false},
|
||||
"virtualbox_version_file": &hcldec.AttrSpec{Name: "virtualbox_version_file", Type: cty.String, Required: false},
|
||||
"guest_additions_mode": &hcldec.AttrSpec{Name: "guest_additions_mode", Type: cty.String, Required: false},
|
||||
"guest_additions_interface": &hcldec.AttrSpec{Name: "guest_additions_interface", Type: cty.String, Required: false},
|
||||
"guest_additions_path": &hcldec.AttrSpec{Name: "guest_additions_path", Type: cty.String, Required: false},
|
||||
"guest_additions_sha256": &hcldec.AttrSpec{Name: "guest_additions_sha256", Type: cty.String, Required: false},
|
||||
"guest_additions_url": &hcldec.AttrSpec{Name: "guest_additions_url", Type: cty.String, Required: false},
|
||||
|
|
|
@ -3,9 +3,32 @@
|
|||
- `communicator` (string) - Communicator
|
||||
|
||||
- `guest_additions_mode` (string) - The method by which guest additions are
|
||||
made available to the guest for installation. Valid options are upload,
|
||||
attach, or disable. If the mode is attach the guest additions ISO will
|
||||
be attached as a CD device to the virtual machine. If the mode is upload
|
||||
made available to the guest for installation. Valid options are `upload`,
|
||||
`attach`, or `disable`. If the mode is `attach` the guest additions ISO will
|
||||
be attached as a CD device to the virtual machine. If the mode is `upload`
|
||||
the guest additions ISO will be uploaded to the path specified by
|
||||
guest_additions_path. The default value is upload. If disable is used,
|
||||
`guest_additions_path`. The default value is `upload`. If `disable` is used,
|
||||
guest additions won't be downloaded, either.
|
||||
|
||||
- `guest_additions_interface` (string) - The interface type to use to mount guest additions when
|
||||
guest_additions_mode is set to attach. Will default to the value set in
|
||||
iso_interface, if iso_interface is set. Will default to "ide", if
|
||||
iso_interface is not set. Options are "ide" and "sata".
|
||||
|
||||
- `guest_additions_path` (string) - The path on the guest virtual machine
|
||||
where the VirtualBox guest additions ISO will be uploaded. By default this
|
||||
is `VBoxGuestAdditions.iso` which should upload into the login directory of
|
||||
the user. This is a [configuration
|
||||
template](/docs/templates/engine) where the `Version`
|
||||
variable is replaced with the VirtualBox version.
|
||||
|
||||
- `guest_additions_sha256` (string) - The SHA256 checksum of the guest
|
||||
additions ISO that will be uploaded to the guest VM. By default the
|
||||
checksums will be downloaded from the VirtualBox website, so this only needs
|
||||
to be set if you want to be explicit about the checksum.
|
||||
|
||||
- `guest_additions_url` (string) - The URL of the guest additions ISO
|
||||
to upload. This can also be a file URL if the ISO is at a local path. By
|
||||
default, the VirtualBox builder will attempt to find the guest additions ISO
|
||||
on the local file system. If it is not available locally, the builder will
|
||||
download the proper guest additions ISO from the internet.
|
||||
|
|
|
@ -3,29 +3,6 @@
|
|||
- `disk_size` (uint) - The size, in megabytes, of the hard disk to create for the VM. By
|
||||
default, this is 40000 (about 40 GB).
|
||||
|
||||
- `guest_additions_path` (string) - The path on the guest virtual machine where the VirtualBox guest
|
||||
additions ISO will be uploaded. By default this is
|
||||
VBoxGuestAdditions.iso which should upload into the login directory of
|
||||
the user. This is a configuration template where the `{{ .Version }}`
|
||||
variable is replaced with the VirtualBox version.
|
||||
|
||||
- `guest_additions_sha256` (string) - The SHA256 checksum of the guest additions ISO that will be uploaded to
|
||||
the guest VM. By default the checksums will be downloaded from the
|
||||
VirtualBox website, so this only needs to be set if you want to be
|
||||
explicit about the checksum.
|
||||
|
||||
- `guest_additions_url` (string) - The URL to the guest additions ISO to upload. This can also be a file
|
||||
URL if the ISO is at a local path. By default, the VirtualBox builder
|
||||
will attempt to find the guest additions ISO on the local file system.
|
||||
If it is not available locally, the builder will download the proper
|
||||
guest additions ISO from the internet. This is a template engine, and you
|
||||
have access to the variable `{{ .Version }}`.
|
||||
|
||||
- `guest_additions_interface` (string) - The interface type to use to mount guest additions when
|
||||
guest_additions_mode is set to attach. Will default to the value set in
|
||||
iso_interface, if iso_interface is set. Will default to "ide", if
|
||||
iso_interface is not set. Options are "ide" and "sata".
|
||||
|
||||
- `guest_os_type` (string) - The guest OS type being installed. By default this is other, but you can
|
||||
get dramatic performance improvements by setting this to the proper
|
||||
value. To view all available values for this run VBoxManage list
|
||||
|
|
|
@ -1,37 +1,5 @@
|
|||
<!-- Code generated from the comments of the Config struct in builder/virtualbox/ovf/config.go; DO NOT EDIT MANUALLY -->
|
||||
|
||||
- `guest_additions_mode` (string) - The method by which guest additions are
|
||||
made available to the guest for installation. Valid options are upload,
|
||||
attach, or disable. If the mode is attach the guest additions ISO will
|
||||
be attached as a CD device to the virtual machine. If the mode is upload
|
||||
the guest additions ISO will be uploaded to the path specified by
|
||||
guest_additions_path. The default value is upload. If disable is used,
|
||||
guest additions won't be downloaded, either.
|
||||
|
||||
- `guest_additions_path` (string) - The path on the guest virtual machine
|
||||
where the VirtualBox guest additions ISO will be uploaded. By default this
|
||||
is VBoxGuestAdditions.iso which should upload into the login directory of
|
||||
the user. This is a configuration
|
||||
template where the Version
|
||||
variable is replaced with the VirtualBox version.
|
||||
|
||||
- `guest_additions_interface` (string) - The interface type to use to mount
|
||||
guest additions when guest_additions_mode is set to attach. Will
|
||||
default to the value set in iso_interface, if iso_interface is set.
|
||||
Will default to "ide", if iso_interface is not set. Options are "ide" and
|
||||
"sata".
|
||||
|
||||
- `guest_additions_sha256` (string) - The SHA256 checksum of the guest
|
||||
additions ISO that will be uploaded to the guest VM. By default the
|
||||
checksums will be downloaded from the VirtualBox website, so this only needs
|
||||
to be set if you want to be explicit about the checksum.
|
||||
|
||||
- `guest_additions_url` (string) - The URL to the guest additions ISO
|
||||
to upload. This can also be a file URL if the ISO is at a local path. By
|
||||
default, the VirtualBox builder will attempt to find the guest additions ISO
|
||||
on the local file system. If it is not available locally, the builder will
|
||||
download the proper guest additions ISO from the internet.
|
||||
|
||||
- `import_flags` ([]string) - Additional flags to pass to
|
||||
VBoxManage import. This can be used to add additional command-line flags
|
||||
such as --eula-accept to accept a EULA in the OVF.
|
||||
|
|
|
@ -1,31 +1,5 @@
|
|||
<!-- Code generated from the comments of the Config struct in builder/virtualbox/vm/config.go; DO NOT EDIT MANUALLY -->
|
||||
|
||||
- `guest_additions_mode` (string) - The method by which guest additions are
|
||||
made available to the guest for installation. Valid options are `upload`,
|
||||
`attach`, or `disable`. If the mode is `attach` the guest additions ISO will
|
||||
be attached as a CD device to the virtual machine. If the mode is `upload`
|
||||
the guest additions ISO will be uploaded to the path specified by
|
||||
`guest_additions_path`. The default value is `upload`. If `disable` is used,
|
||||
guest additions won't be downloaded, either.
|
||||
|
||||
- `guest_additions_path` (string) - The path on the guest virtual machine
|
||||
where the VirtualBox guest additions ISO will be uploaded. By default this
|
||||
is `VBoxGuestAdditions.iso` which should upload into the login directory of
|
||||
the user. This is a [configuration
|
||||
template](/docs/templates/engine) where the `Version`
|
||||
variable is replaced with the VirtualBox version.
|
||||
|
||||
- `guest_additions_sha256` (string) - The SHA256 checksum of the guest
|
||||
additions ISO that will be uploaded to the guest VM. By default the
|
||||
checksums will be downloaded from the VirtualBox website, so this only needs
|
||||
to be set if you want to be explicit about the checksum.
|
||||
|
||||
- `guest_additions_url` (string) - The URL to the guest additions ISO
|
||||
to upload. This can also be a file URL if the ISO is at a local path. By
|
||||
default, the VirtualBox builder will attempt to find the guest additions ISO
|
||||
on the local file system. If it is not available locally, the builder will
|
||||
download the proper guest additions ISO from the internet.
|
||||
|
||||
- `attach_snapshot` (string) - Default to `null/empty`. The name of an
|
||||
**existing** snapshot to which the builder shall attach the VM before
|
||||
starting it. If no snapshot is specified the builder will simply start the
|
||||
|
|
Loading…
Reference in New Issue