Merge pull request #9796 from hashicorp/do_8778
create secondary iso containing user's desired files
This commit is contained in:
commit
8aaa1bd7a6
|
@ -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 assumes that you have a command line tool installed
|
||||
// that can handle the iso creation. If you are running Packer from an OSX host,
|
||||
// the required tool 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, 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.
|
||||
|
@ -852,4 +856,4 @@ is actually installed and ready to be connected to.
|
|||
|
||||
For more information about the hyper-v daemons and supported distributions, see
|
||||
the Microsoft docs at
|
||||
https://docs.microsoft.com/en-us/windows-server/virtualization/hyper-v/supported-linux-and-freebsd-virtual-machines-for-hyper-v-on-windows
|
||||
https://docs.microsoft.com/en-us/windows-server/virtualization/hyper-v/supported-linux-and-freebsd-virtual-machines-for-hyper-v-on-windows
|
||||
|
|
|
@ -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 assumes that you have a command line tool installed
|
||||
that can handle the iso creation. If you are running Packer from an OSX host,
|
||||
the required tool 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