Implement cd_files option. The Addfile code is modified from the floppy_files
tooling. Tested on OSX, `Windows, and Linux. Documented command line dependencies. Added acceptance tests (behind testacc flag, since there are dependencies). This option is only implemented for the vmware and hyperv iso builders. It will be implemented for others in later PRs in order to make review easier.
This commit is contained in:
parent
dfe8aa51b0
commit
d2dd158fe2
|
@ -37,6 +37,7 @@ const (
|
|||
|
||||
type CommonConfig struct {
|
||||
common.FloppyConfig `mapstructure:",squash"`
|
||||
common.CDConfig `mapstructure:",squash"`
|
||||
// The block size of the VHD to be created.
|
||||
// Recommended disk block size for Linux hyper-v guests is 1 MiB. This
|
||||
// defaults to "32" MiB.
|
||||
|
@ -210,8 +211,8 @@ func (c *CommonConfig) Prepare(ctx *interpolate.Context, pc *common.PackerConfig
|
|||
}
|
||||
|
||||
// Errors
|
||||
floppyerrs := c.FloppyConfig.Prepare(ctx)
|
||||
errs = append(errs, floppyerrs...)
|
||||
errs = append(errs, c.FloppyConfig.Prepare(ctx)...)
|
||||
errs = append(errs, c.CDConfig.Prepare(ctx)...)
|
||||
if c.GuestAdditionsMode == "" {
|
||||
if c.GuestAdditionsPath != "" {
|
||||
c.GuestAdditionsMode = "attach"
|
||||
|
|
|
@ -34,7 +34,17 @@ func (s *StepMountSecondaryDvdImages) Run(ctx context.Context, state multistep.S
|
|||
// For IDE, there are only 2 controllers (0,1) with 2 locations each (0,1)
|
||||
var dvdProperties []DvdControllerProperties
|
||||
|
||||
for _, isoPath := range s.IsoPaths {
|
||||
isoPaths := s.IsoPaths
|
||||
|
||||
// Add our custom CD, if it exists
|
||||
cd_path, ok := state.Get("cd_path").(string)
|
||||
if ok {
|
||||
if cd_path != "" {
|
||||
isoPaths = append(isoPaths, cd_path)
|
||||
}
|
||||
}
|
||||
|
||||
for _, isoPath := range isoPaths {
|
||||
var properties DvdControllerProperties
|
||||
|
||||
controllerNumber, controllerLocation, err := driver.CreateDvdDrive(vmName, isoPath, s.Generation)
|
||||
|
|
|
@ -254,7 +254,10 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||
GuestAdditionsPath: b.config.GuestAdditionsPath,
|
||||
Generation: b.config.Generation,
|
||||
},
|
||||
|
||||
&common.StepCreateCD{
|
||||
Files: b.config.CDConfig.CDFiles,
|
||||
Label: b.config.CDConfig.CDLabel,
|
||||
},
|
||||
&hypervcommon.StepMountSecondaryDvdImages{
|
||||
IsoPaths: b.config.SecondaryDvdImages,
|
||||
Generation: b.config.Generation,
|
||||
|
|
|
@ -79,6 +79,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"`
|
||||
DiskBlockSize *uint `mapstructure:"disk_block_size" required:"false" cty:"disk_block_size" hcl:"disk_block_size"`
|
||||
RamSize *uint `mapstructure:"memory" required:"false" cty:"memory" hcl:"memory"`
|
||||
SecondaryDvdImages []string `mapstructure:"secondary_iso_images" required:"false" cty:"secondary_iso_images" hcl:"secondary_iso_images"`
|
||||
|
@ -195,6 +197,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},
|
||||
"disk_block_size": &hcldec.AttrSpec{Name: "disk_block_size", Type: cty.Number, Required: false},
|
||||
"memory": &hcldec.AttrSpec{Name: "memory", Type: cty.Number, Required: false},
|
||||
"secondary_iso_images": &hcldec.AttrSpec{Name: "secondary_iso_images", Type: cty.List(cty.String), Required: false},
|
||||
|
|
|
@ -79,6 +79,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"`
|
||||
DiskBlockSize *uint `mapstructure:"disk_block_size" required:"false" cty:"disk_block_size" hcl:"disk_block_size"`
|
||||
RamSize *uint `mapstructure:"memory" required:"false" cty:"memory" hcl:"memory"`
|
||||
SecondaryDvdImages []string `mapstructure:"secondary_iso_images" required:"false" cty:"secondary_iso_images" hcl:"secondary_iso_images"`
|
||||
|
@ -197,6 +199,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},
|
||||
"disk_block_size": &hcldec.AttrSpec{Name: "disk_block_size", Type: cty.Number, Required: false},
|
||||
"memory": &hcldec.AttrSpec{Name: "memory", Type: cty.Number, Required: false},
|
||||
"secondary_iso_images": &hcldec.AttrSpec{Name: "secondary_iso_images", Type: cty.List(cty.String), Required: false},
|
||||
|
|
|
@ -76,12 +76,22 @@ 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,
|
||||
},
|
||||
&vmwcommon.StepRemoteUpload{
|
||||
Key: "floppy_path",
|
||||
Message: "Uploading Floppy to remote machine...",
|
||||
DoCleanup: true,
|
||||
Checksum: "none",
|
||||
},
|
||||
&vmwcommon.StepRemoteUpload{
|
||||
Key: "cd_path",
|
||||
Message: "Uploading CD to remote machine...",
|
||||
DoCleanup: true,
|
||||
Checksum: "none",
|
||||
},
|
||||
&vmwcommon.StepRemoteUpload{
|
||||
Key: "iso_path",
|
||||
Message: "Uploading ISO to remote machine...",
|
||||
|
|
|
@ -23,6 +23,7 @@ type Config struct {
|
|||
common.HTTPConfig `mapstructure:",squash"`
|
||||
common.ISOConfig `mapstructure:",squash"`
|
||||
common.FloppyConfig `mapstructure:",squash"`
|
||||
common.CDConfig `mapstructure:",squash"`
|
||||
bootcommand.VNCConfig `mapstructure:",squash"`
|
||||
vmwcommon.DriverConfig `mapstructure:",squash"`
|
||||
vmwcommon.HWConfig `mapstructure:",squash"`
|
||||
|
@ -110,6 +111,7 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
|
|||
errs = packer.MultiErrorAppend(errs, c.ToolsConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.VMXConfig.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.VNCConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.ExportConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.DiskConfig.Prepare(&c.ctx)...)
|
||||
|
|
|
@ -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"`
|
||||
|
@ -167,6 +169,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},
|
||||
|
|
|
@ -34,6 +34,7 @@ type vmxTemplateData struct {
|
|||
DiskType string
|
||||
CDROMType string
|
||||
CDROMType_PrimarySecondary string
|
||||
CDROM_PATH string
|
||||
|
||||
Network_Type string
|
||||
Network_Device string
|
||||
|
@ -112,6 +113,7 @@ func (s *stepCreateVMX) Run(ctx context.Context, state multistep.StateBag) multi
|
|||
|
||||
ictx := config.ctx
|
||||
|
||||
// Mount extra vmdks we created earlier.
|
||||
if len(config.AdditionalDiskSize) > 0 {
|
||||
for i := range config.AdditionalDiskSize {
|
||||
ictx.Data = &additionalDiskTemplateData{
|
||||
|
@ -238,6 +240,15 @@ func (s *stepCreateVMX) Run(ctx context.Context, state multistep.StateBag) multi
|
|||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// Add our custom CD, if it exists
|
||||
cd_path, ok := state.Get("cd_path").(string)
|
||||
if ok {
|
||||
if cd_path != "" {
|
||||
vmxTemplate += ExtraCDRomTemplate
|
||||
templateData.CDROM_PATH = cd_path
|
||||
}
|
||||
}
|
||||
|
||||
/// Now that we figured out the CDROM device to add, store it
|
||||
/// to the list of temporary build devices in our statebag
|
||||
tmpBuildDevices := state.Get("temporaryDevices").([]string)
|
||||
|
@ -596,3 +607,9 @@ scsi0:{{ .DiskNumber }}.fileName = "{{ .DiskName}}-{{ .DiskNumber }}.vmdk"
|
|||
scsi0:{{ .DiskNumber }}.present = "TRUE"
|
||||
scsi0:{{ .DiskNumber }}.redo = ""
|
||||
`
|
||||
|
||||
const ExtraCDRomTemplate = `
|
||||
{{ .CDROMType }}1:{{ .CDROMType_PrimarySecondary }}.present = "TRUE"
|
||||
{{ .CDROMType }}1:{{ .CDROMType_PrimarySecondary }}.fileName = "{{ .CDROM_PATH }}"
|
||||
{{ .CDROMType }}1:{{ .CDROMType_PrimarySecondary }}.deviceType = "cdrom-image"
|
||||
`
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
//go:generate struct-markdown
|
||||
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/packer/template/interpolate"
|
||||
)
|
||||
|
||||
// An iso (CD) containing custom files can be made available for your build.
|
||||
//
|
||||
// By default, no extra CD will be attached. All files listed in this setting
|
||||
// get placed into the root directory of the CD and the CD is attached as the
|
||||
// second CD device.
|
||||
//
|
||||
// This config exists to work around modern operating systems that have no
|
||||
// way to mount floppy disks, which was our previous go-to for adding files at
|
||||
// boot time.
|
||||
type CDConfig struct {
|
||||
// A list of files to place onto a CD that is attached when the VM is
|
||||
// booted. This can include either files or directories; any directories
|
||||
// will be copied onto the CD recursively, preserving directory structure
|
||||
// hierarchy. Symlinks will have the link's target copied into the directory
|
||||
// tree on the CD where the symlink was. File globbing is allowed.
|
||||
//
|
||||
// Usage example (JSON):
|
||||
//
|
||||
// ```json
|
||||
// "cd_files": ["./somedirectory/meta-data", "./somedirectory/user-data"],
|
||||
// "cd_label": "cidata",
|
||||
// ```
|
||||
//
|
||||
// Usage example (HCL):
|
||||
//
|
||||
// ```hcl
|
||||
// cd_files = ["./somedirectory/meta-data", "./somedirectory/user-data"]
|
||||
// cd_label = "cidata"
|
||||
// ```
|
||||
//
|
||||
// The above will create a CD with two files, user-data and meta-data in the
|
||||
// CD root. This specific example is how you would create a CD that can be
|
||||
// used for an Ubuntu 20.04 autoinstall.
|
||||
//
|
||||
// Since globbing is also supported,
|
||||
//
|
||||
// ```hcl
|
||||
// cd_files = ["./somedirectory/*"]
|
||||
// cd_label = "cidata"
|
||||
// ```
|
||||
//
|
||||
// Would also be an acceptable way to define the above cd. The difference
|
||||
// between providing the directory with or without the glob is whether the
|
||||
// directory itself or its contents will be at the CD root.
|
||||
//
|
||||
// Use of this option assums that you have a command line tool isntalled
|
||||
// that can handle the iso creation. If you are running Packer from an OSX host,
|
||||
// the required tool is is hdiutil which comes preinstalled.
|
||||
// On linux hosts, you need to have mkisofs.
|
||||
// On Windows, you must have oscdimg.exe. oscdimg.exe is part of the
|
||||
// Windows ADK tooks, downloadable from
|
||||
// https://docs.microsoft.com/en-us/windows-hardware/get-started/adk-install#winADK
|
||||
CDFiles []string `mapstructure:"cd_files"`
|
||||
CDLabel string `mapstructure:"cd_label"`
|
||||
}
|
||||
|
||||
func (c *CDConfig) Prepare(ctx *interpolate.Context) []error {
|
||||
var errs []error
|
||||
var err error
|
||||
|
||||
if c.CDFiles == nil {
|
||||
c.CDFiles = make([]string, 0)
|
||||
}
|
||||
|
||||
// Create new file list based on globbing.
|
||||
var files []string
|
||||
for _, path := range c.CDFiles {
|
||||
if strings.ContainsAny(path, "*?[") {
|
||||
var globbedFiles []string
|
||||
globbedFiles, err = filepath.Glob(path)
|
||||
if len(globbedFiles) > 0 {
|
||||
files = append(files, globbedFiles...)
|
||||
}
|
||||
} else {
|
||||
_, err = os.Stat(path)
|
||||
files = append(files, path)
|
||||
}
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("Bad CD disk file '%s': %s", path, err))
|
||||
}
|
||||
c.CDFiles = files
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCDPrepare(t *testing.T) {
|
||||
type testCases struct {
|
||||
CDConfig CDConfig
|
||||
ErrExpected bool
|
||||
Reason string
|
||||
ExpectedCDFiles []string
|
||||
}
|
||||
tcs := []testCases{
|
||||
{
|
||||
CDConfig: CDConfig{},
|
||||
ErrExpected: false,
|
||||
Reason: "TestNilCD: nil CD array should not fail",
|
||||
ExpectedCDFiles: []string{},
|
||||
},
|
||||
{
|
||||
CDConfig: CDConfig{CDFiles: make([]string, 0)},
|
||||
ErrExpected: false,
|
||||
Reason: "TestEmptyArrayCD: empty CD array should never fail",
|
||||
ExpectedCDFiles: []string{},
|
||||
},
|
||||
{
|
||||
CDConfig: CDConfig{CDFiles: []string{"extra_iso_config.go"}},
|
||||
ErrExpected: false,
|
||||
Reason: "TestExistingCDFile: array with existing CD should not fail",
|
||||
ExpectedCDFiles: []string{"extra_iso_config.go"},
|
||||
},
|
||||
{
|
||||
CDConfig: CDConfig{CDFiles: []string{"does_not_exist.foo"}},
|
||||
ErrExpected: true,
|
||||
Reason: "TestNonExistingCDFile: array with non existing CD should return errors",
|
||||
ExpectedCDFiles: []string{"does_not_exist.foo"},
|
||||
},
|
||||
{
|
||||
CDConfig: CDConfig{CDFiles: []string{"extra_iso_config*"}},
|
||||
ErrExpected: false,
|
||||
Reason: "TestGlobbingCDFile: Glob should work",
|
||||
ExpectedCDFiles: []string{"extra_iso_config.go", "extra_iso_config_test.go"},
|
||||
},
|
||||
}
|
||||
for _, tc := range tcs {
|
||||
c := tc.CDConfig
|
||||
errs := c.Prepare(nil)
|
||||
if (len(errs) != 0) != tc.ErrExpected {
|
||||
t.Fatal(tc.Reason)
|
||||
}
|
||||
assert.Equal(t, c.CDFiles, tc.ExpectedCDFiles)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultiErrorCDFiles(t *testing.T) {
|
||||
c := CDConfig{
|
||||
CDFiles: []string{"extra_iso_config.foo", "extra_iso_config.go",
|
||||
"extra_iso_config.bar", "extra_iso_config_test.go", "extra_iso_config.baz"},
|
||||
}
|
||||
|
||||
errs := c.Prepare(nil)
|
||||
if len(errs) == 0 {
|
||||
t.Fatal("array with non existing CD should return errors")
|
||||
}
|
||||
|
||||
expectedErrors := 3
|
||||
if count := len(errs); count != expectedErrors {
|
||||
t.Fatalf("array with %v non existing CD should return %v errors but it is returning %v", expectedErrors, expectedErrors, count)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,201 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"github.com/hashicorp/packer/helper/builder/localexec"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"github.com/hashicorp/packer/packer/tmp"
|
||||
)
|
||||
|
||||
// StepCreateCD will create a CD disk with the given files.
|
||||
type StepCreateCD struct {
|
||||
// Files can be either files or directories. Any files provided here will
|
||||
// be written to the root of the CD. Directories will be written to the
|
||||
// root of the CD as well, but will retain their subdirectory structure.
|
||||
Files []string
|
||||
Label string
|
||||
|
||||
CDPath string
|
||||
|
||||
filesAdded map[string]bool
|
||||
}
|
||||
|
||||
func (s *StepCreateCD) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
if len(s.Files) == 0 {
|
||||
log.Println("No CD files specified. CD disk will not be made.")
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
ui.Say("Creating CD disk...")
|
||||
|
||||
if s.Label == "" {
|
||||
s.Label = "packer"
|
||||
} else {
|
||||
log.Printf("CD label is set to %s", s.Label)
|
||||
}
|
||||
|
||||
// Track what files are added. Used for testing step.
|
||||
s.filesAdded = make(map[string]bool)
|
||||
|
||||
// Create a temporary file to be our CD drive
|
||||
CDF, err := tmp.File("packer*.iso")
|
||||
// Set the path so we can remove it later
|
||||
CDPath := CDF.Name()
|
||||
CDF.Close()
|
||||
os.Remove(CDPath)
|
||||
if err != nil {
|
||||
state.Put("error",
|
||||
fmt.Errorf("Error creating temporary file for CD: %s", err))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
log.Printf("CD path: %s", CDPath)
|
||||
s.CDPath = CDPath
|
||||
|
||||
// Consolidate all files provided into a single directory to become our
|
||||
// "root" directory.
|
||||
rootFolder, err := tmp.Dir("packer_to_cdrom")
|
||||
if err != nil {
|
||||
state.Put("error",
|
||||
fmt.Errorf("Error creating temporary file for CD: %s", err))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
for _, toAdd := range s.Files {
|
||||
err = s.AddFile(rootFolder, toAdd)
|
||||
if err != nil {
|
||||
state.Put("error",
|
||||
fmt.Errorf("Error creating temporary file for CD: %s", err))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
|
||||
cmd := retrieveCommandForOS(s.Label, rootFolder, CDPath)
|
||||
err = localexec.RunAndStream(cmd, ui, []string{})
|
||||
if err != nil {
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Message("Done copying paths from CD_dirs")
|
||||
|
||||
// Set the path to the CD so it can be used later
|
||||
state.Put("cd_path", CDPath)
|
||||
|
||||
if err != nil {
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepCreateCD) Cleanup(multistep.StateBag) {
|
||||
if s.CDPath != "" {
|
||||
log.Printf("Deleting CD disk: %s", s.CDPath)
|
||||
os.Remove(s.CDPath)
|
||||
}
|
||||
}
|
||||
|
||||
func retrieveCommandForOS(label string, source string, dest string) *exec.Cmd {
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
cmd := exec.Command("oscdimg")
|
||||
args := []string{"-j1", "-o", "-m", "-l" + label, source, dest}
|
||||
cmd.Args = append(cmd.Args, args...)
|
||||
return cmd
|
||||
case "darwin":
|
||||
cmd := exec.Command("hdiutil")
|
||||
args := []string{"makehybrid", "-o", dest, "-hfs",
|
||||
"-joliet", "-iso", "-default-volume-name", label, source}
|
||||
cmd.Args = append(cmd.Args, args...)
|
||||
return cmd
|
||||
default:
|
||||
cmd := exec.Command("mkisofs")
|
||||
args := []string{"-joliet", "-volid", label, "-o", dest, source}
|
||||
cmd.Args = append(cmd.Args, args...)
|
||||
return cmd
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StepCreateCD) AddFile(dst, src string) error {
|
||||
finfo, err := os.Stat(src)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error adding path to CD: %s", err)
|
||||
}
|
||||
|
||||
// add a file
|
||||
if !finfo.IsDir() {
|
||||
inputF, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer inputF.Close()
|
||||
|
||||
// Create a new file in the root directory
|
||||
dest, err := os.Create(filepath.Join(dst, finfo.Name()))
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error opening file for copy %s to CD root", src)
|
||||
}
|
||||
defer dest.Close()
|
||||
nBytes, err := io.Copy(dest, inputF)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error copying %s to CD root", src)
|
||||
}
|
||||
s.filesAdded[src] = true
|
||||
log.Printf("Wrote %d bytes to %s", nBytes, finfo.Name())
|
||||
return err
|
||||
}
|
||||
|
||||
// Add a directory and its subdirectories
|
||||
visit := func(pathname string, fi os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// add a file
|
||||
if !fi.IsDir() {
|
||||
inputF, err := os.Open(pathname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer inputF.Close()
|
||||
|
||||
fileDst, err := os.Create(filepath.Join(dst, pathname))
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error opening file %s on CD", src)
|
||||
}
|
||||
defer fileDst.Close()
|
||||
nBytes, err := io.Copy(fileDst, inputF)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error copying %s to CD", src)
|
||||
}
|
||||
s.filesAdded[pathname] = true
|
||||
log.Printf("Wrote %d bytes to %s", nBytes, pathname)
|
||||
return err
|
||||
}
|
||||
|
||||
if fi.Mode().IsDir() {
|
||||
// create the directory on the CD, continue walk.
|
||||
err := os.Mkdir(filepath.Join(dst, pathname), fi.Mode())
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error creating new directory %s: %s",
|
||||
filepath.Join(dst, pathname), err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
return filepath.Walk(src, visit)
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
func TestStepCreateCD_Impl(t *testing.T) {
|
||||
var raw interface{}
|
||||
raw = new(StepCreateCD)
|
||||
if _, ok := raw.(multistep.Step); !ok {
|
||||
t.Fatalf("StepCreateCD should be a step")
|
||||
}
|
||||
}
|
||||
|
||||
func testStepCreateCDState(t *testing.T) multistep.StateBag {
|
||||
state := new(multistep.BasicStateBag)
|
||||
state.Put("ui", &packer.BasicUi{
|
||||
Reader: new(bytes.Buffer),
|
||||
Writer: new(bytes.Buffer),
|
||||
})
|
||||
return state
|
||||
}
|
||||
|
||||
func TestStepCreateCD(t *testing.T) {
|
||||
if os.Getenv("PACKER_ACC") == "" {
|
||||
t.Skip("This test is only run with PACKER_ACC=1 due to the requirement of access to the disk management binaries.")
|
||||
}
|
||||
state := testStepCreateCDState(t)
|
||||
step := new(StepCreateCD)
|
||||
|
||||
dir, err := ioutil.TempDir("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
files := make([]string, 3)
|
||||
|
||||
tempFileNames := []string{"test_cd_roms.tmp", "test cd files.tmp",
|
||||
"Test-Test-Test5.tmp"}
|
||||
for i, fname := range tempFileNames {
|
||||
files[i] = path.Join(dir, fname)
|
||||
|
||||
_, err := os.Create(files[i])
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
step.Files = files
|
||||
action := step.Run(context.Background(), state)
|
||||
|
||||
if err, ok := state.GetOk("error"); ok {
|
||||
t.Fatalf("state should be ok for %v: %s", step.Files, err)
|
||||
}
|
||||
|
||||
if action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v for %v", action, step.Files)
|
||||
}
|
||||
|
||||
CD_path := state.Get("cd_path").(string)
|
||||
|
||||
if _, err := os.Stat(CD_path); err != nil {
|
||||
t.Fatalf("file not found: %s for %v", CD_path, step.Files)
|
||||
}
|
||||
|
||||
if len(step.filesAdded) != 3 {
|
||||
t.Fatalf("expected 3 files, found %d for %v", len(step.filesAdded), step.Files)
|
||||
}
|
||||
|
||||
step.Cleanup(state)
|
||||
|
||||
if _, err := os.Stat(CD_path); err == nil {
|
||||
t.Fatalf("file found: %s for %v", CD_path, step.Files)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepCreateCD_missing(t *testing.T) {
|
||||
if os.Getenv("PACKER_ACC") == "" {
|
||||
t.Skip("This test is only run with PACKER_ACC=1 due to the requirement of access to the disk management binaries.")
|
||||
}
|
||||
state := testStepCreateCDState(t)
|
||||
step := new(StepCreateCD)
|
||||
|
||||
dir, err := ioutil.TempDir("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
expected := 0
|
||||
|
||||
step.Files = []string{"missing file.tmp"}
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionHalt {
|
||||
t.Fatalf("bad action: %#v for %v", action, step.Files)
|
||||
}
|
||||
|
||||
if _, ok := state.GetOk("error"); !ok {
|
||||
t.Fatalf("state should not be ok for %v", step.Files)
|
||||
}
|
||||
|
||||
CD_path := state.Get("cd_path")
|
||||
|
||||
if CD_path != nil {
|
||||
t.Fatalf("CD_path is not nil for %v", step.Files)
|
||||
}
|
||||
|
||||
if len(step.filesAdded) != expected {
|
||||
t.Fatalf("expected %d, found %d for %v", expected, len(step.filesAdded), step.Files)
|
||||
}
|
||||
}
|
7
go.sum
7
go.sum
|
@ -159,6 +159,8 @@ github.com/digitalocean/godo v1.11.1 h1:OsTh37YFKk+g6DnAOrkXJ9oDArTkRx5UTkBJ2EWA
|
|||
github.com/digitalocean/godo v1.11.1/go.mod h1:h6faOIcZ8lWIwNQ+DN7b3CgX4Kwby5T+nbpNqkUIozU=
|
||||
github.com/dimchansky/utfbom v1.1.0 h1:FcM3g+nofKgUteL8dm/UpdRXNC9KmADgTpLKsu0TRo4=
|
||||
github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
|
||||
github.com/diskfs/go-diskfs v1.1.1 h1:rMjLpaydtXGVZb7mdkRGK1+//30i76nKAit89zUzeaI=
|
||||
github.com/diskfs/go-diskfs v1.1.1/go.mod h1:afUPxxu+x1snp4aCY2bKR0CoZ/YFJewV3X2UEr2nPZE=
|
||||
github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY=
|
||||
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
|
||||
github.com/docker/docker v0.0.0-20180422163414-57142e89befe h1:VW8TnWi0CZgg7oCv0wH6evNwkzcJg/emnw4HrVIWws4=
|
||||
|
@ -362,8 +364,6 @@ github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8
|
|||
github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/hcl/v2 v2.3.0 h1:iRly8YaMwTBAKhn1Ybk7VSdzbnopghktCD031P8ggUE=
|
||||
github.com/hashicorp/hcl/v2 v2.3.0/go.mod h1:d+FwDBbOLvpAM3Z6J7gPj/VoAGkNe/gm352ZhjJ/Zv8=
|
||||
github.com/hashicorp/hcl/v2 v2.6.0 h1:3krZOfGY6SziUXa6H9PJU6TyohHn7I+ARYnhbeNBz+o=
|
||||
github.com/hashicorp/hcl/v2 v2.6.0/go.mod h1:bQTN5mpo+jewjJgh8jr0JUguIi7qPHUF6yIfAEN3jqY=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
|
@ -761,6 +761,7 @@ golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 h1:ogLJMz+qpzav7lGMh10LMvAkM/fAoGlaiiHYiFYdm80=
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
@ -881,6 +882,8 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogR
|
|||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.27 h1:kJdccidYzt3CaHD1crCFTS1hxyhSi059NhOFUf03YFo=
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||
gopkg.in/djherbis/times.v1 v1.2.0 h1:UCvDKl1L/fmBygl2Y7hubXCnY7t4Yj46ZrBFNUipFbM=
|
||||
gopkg.in/djherbis/times.v1 v1.2.0/go.mod h1:AQlg6unIsrsCEdQYhTzERy542dz6SFdQFZFv6mUY0P8=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk=
|
||||
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
|
|
|
@ -105,6 +105,10 @@ builder.
|
|||
|
||||
@include 'common/FloppyConfig-not-required.mdx'
|
||||
|
||||
### CD configuration reference
|
||||
|
||||
@include 'common/CDConfig-not-required.mdx'
|
||||
|
||||
## Communicator configuration reference
|
||||
|
||||
### Optional common fields:
|
||||
|
@ -136,8 +140,8 @@ for the version of Hyper-V that is running.
|
|||
|
||||
Floppy drives are no longer supported by generation 2 machines. This requires
|
||||
you to take another approach when dealing with preseed or answer files. Two
|
||||
possible options are using virtual DVD drives or using Packers built in web
|
||||
server.
|
||||
possible options are using your own virtual DVD drives, or the cd_files option,
|
||||
or using Packer's built in web server.
|
||||
|
||||
When dealing with Windows you need to enable UEFI drives for generation 2
|
||||
virtual machines.
|
||||
|
|
|
@ -78,6 +78,7 @@ the items listed here, you will want to look at the general configuration
|
|||
references for [ISO](#iso-configuration),
|
||||
[HTTP](#http-directory-configuration),
|
||||
[Floppy](#floppy-configuration),
|
||||
[CD](#cd-configuration),
|
||||
[Boot](#boot-configuration),
|
||||
[Driver](#driver-configuration),
|
||||
[Hardware](#hardware-configuration),
|
||||
|
@ -129,6 +130,9 @@ necessary for this build to succeed and can be found further down the page.
|
|||
|
||||
@include 'common/FloppyConfig-not-required.mdx'
|
||||
|
||||
### CD configuration
|
||||
@include 'common/CDConfig-not-required.mdx'
|
||||
|
||||
### Shutdown configuration
|
||||
|
||||
#### Optional:
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
<!-- Code generated from the comments of the CDConfig struct in common/extra_iso_config.go; DO NOT EDIT MANUALLY -->
|
||||
|
||||
- `cd_files` ([]string) - A list of files to place onto a CD that is attached when the VM is
|
||||
booted. This can include either files or directories; any directories
|
||||
will be copied onto the CD recursively, preserving directory structure
|
||||
hierarchy. Symlinks will have the link's target copied into the directory
|
||||
tree on the CD where the symlink was. File globbing is allowed.
|
||||
|
||||
Usage example (JSON):
|
||||
|
||||
```json
|
||||
"cd_files": ["./somedirectory/meta-data", "./somedirectory/user-data"],
|
||||
"cd_label": "cidata",
|
||||
```
|
||||
|
||||
Usage example (HCL):
|
||||
|
||||
```hcl
|
||||
cd_files = ["./somedirectory/meta-data", "./somedirectory/user-data"]
|
||||
cd_label = "cidata"
|
||||
```
|
||||
|
||||
The above will create a CD with two files, user-data and meta-data in the
|
||||
CD root. This specific example is how you would create a CD that can be
|
||||
used for an Ubuntu 20.04 autoinstall.
|
||||
|
||||
Since globbing is also supported,
|
||||
|
||||
```hcl
|
||||
cd_files = ["./somedirectory/*"]
|
||||
cd_label = "cidata"
|
||||
```
|
||||
|
||||
Would also be an acceptable way to define the above cd. The difference
|
||||
between providing the directory with or without the glob is whether the
|
||||
directory itself or its contents will be at the CD root.
|
||||
|
||||
Use of this option assums that you have a command line tool isntalled
|
||||
that can handle the iso creation. If you are running Packer from an OSX host,
|
||||
the required tool is is hdiutil which comes preinstalled.
|
||||
On linux hosts, you need to have mkisofs.
|
||||
On Windows, you must have oscdimg.exe. oscdimg.exe is part of the
|
||||
Windows ADK tooks, downloadable from
|
||||
https://docs.microsoft.com/en-us/windows-hardware/get-started/adk-install#winADK
|
||||
|
||||
- `cd_label` (string) - CD Label
|
|
@ -0,0 +1,11 @@
|
|||
<!-- Code generated from the comments of the CDConfig struct in common/extra_iso_config.go; DO NOT EDIT MANUALLY -->
|
||||
|
||||
An iso (CD) containing custom files can be made available for your build.
|
||||
|
||||
By default, no extra CD will be attached. All files listed in this setting
|
||||
get placed into the root directory of the CD and the CD is attached as the
|
||||
second CD device.
|
||||
|
||||
This config exists to work around modern operating systems that have no
|
||||
way to mount floppy disks, which was our previous go-to for adding files at
|
||||
boot time.
|
Loading…
Reference in New Issue