diff --git a/cmd/packer-sdc/internal/renderdocs/docs-partials/packer-plugin-sdk/multistep/commonsteps/CDConfig-not-required.mdx b/cmd/packer-sdc/internal/renderdocs/docs-partials/packer-plugin-sdk/multistep/commonsteps/CDConfig-not-required.mdx index a817ad9cf..686caf335 100644 --- a/cmd/packer-sdc/internal/renderdocs/docs-partials/packer-plugin-sdk/multistep/commonsteps/CDConfig-not-required.mdx +++ b/cmd/packer-sdc/internal/renderdocs/docs-partials/packer-plugin-sdk/multistep/commonsteps/CDConfig-not-required.mdx @@ -44,6 +44,22 @@ * hdiutil (normally found in macOS) * oscdimg (normally found in Windows as part of the Windows ADK) +- `cd_content` (map[string]string) - Key/Values to add to the CD. The keys represent the paths, and the values + contents. It can be used alongside `cd_files`, which is useful to add large + files without loading them into memory. If any paths are specified by both, + the contents in `cd_content` will take precedence. + + Usage example (HCL): + + ```hcl + cd_files = ["vendor-data"] + cd_content = { + "meta-data" = jsonencode(local.instance_data) + "user-data" = templatefile("user-data", { packages = ["nginx"] }) + } + cd_label = "cidata" + ``` + - `cd_label` (string) - CD Label diff --git a/multistep/commonsteps/extra_iso_config.go b/multistep/commonsteps/extra_iso_config.go index 6cfaf51c8..649ef4321 100644 --- a/multistep/commonsteps/extra_iso_config.go +++ b/multistep/commonsteps/extra_iso_config.go @@ -65,7 +65,23 @@ type CDConfig struct { // * hdiutil (normally found in macOS) // * oscdimg (normally found in Windows as part of the Windows ADK) CDFiles []string `mapstructure:"cd_files"` - CDLabel string `mapstructure:"cd_label"` + // Key/Values to add to the CD. The keys represent the paths, and the values + // contents. It can be used alongside `cd_files`, which is useful to add large + // files without loading them into memory. If any paths are specified by both, + // the contents in `cd_content` will take precedence. + // + // Usage example (HCL): + // + // ```hcl + // cd_files = ["vendor-data"] + // cd_content = { + // "meta-data" = jsonencode(local.instance_data) + // "user-data" = templatefile("user-data", { packages = ["nginx"] }) + // } + // cd_label = "cidata" + // ``` + CDContent map[string]string `mapstructure:"cd_content"` + CDLabel string `mapstructure:"cd_label"` } func (c *CDConfig) Prepare(ctx *interpolate.Context) []error { diff --git a/multistep/commonsteps/step_create_cdrom.go b/multistep/commonsteps/step_create_cdrom.go index d275cd48f..8950a60e8 100644 --- a/multistep/commonsteps/step_create_cdrom.go +++ b/multistep/commonsteps/step_create_cdrom.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "io" + "io/ioutil" "log" "os" "os/exec" @@ -22,8 +23,9 @@ 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 + Files []string + Content map[string]string + Label string CDPath string @@ -31,7 +33,7 @@ type StepCreateCD struct { } func (s *StepCreateCD) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - if len(s.Files) == 0 { + if len(s.Files) == 0 && len(s.Content) == 0 { log.Println("No CD files specified. CD disk will not be made.") return multistep.ActionContinue } @@ -79,6 +81,15 @@ func (s *StepCreateCD) Run(ctx context.Context, state multistep.StateBag) multis } } + for path, content := range s.Content { + err = s.AddContent(rootFolder, path, content) + if err != nil { + state.Put("error", + fmt.Errorf("Error creating temporary file for CD: %s", err)) + return multistep.ActionHalt + } + } + cmd, err := retrieveCDISOCreationCommand(s.Label, rootFolder, CDPath) if err != nil { state.Put("error", err) @@ -291,3 +302,18 @@ func (s *StepCreateCD) AddFile(dst, src string) error { return filepath.Walk(src, visit) } + +func (s *StepCreateCD) AddContent(dst, path, content string) error { + // Join Cleans the path so we can join it without path traversal issues. + dstPath := filepath.Join(dst, path) + dstDir := filepath.Dir(dstPath) + err := os.MkdirAll(dstDir, 0777) + if err != nil { + return fmt.Errorf("error creating new directory %s: %s", dstDir, err) + } + err = ioutil.WriteFile(dstPath, []byte(content), 0666) + if err != nil { + return fmt.Errorf("Error writing file %s on CD: %s", path, err) + } + return nil +} diff --git a/multistep/commonsteps/step_create_cdrom_test.go b/multistep/commonsteps/step_create_cdrom_test.go index eb278ace9..35761809b 100644 --- a/multistep/commonsteps/step_create_cdrom_test.go +++ b/multistep/commonsteps/step_create_cdrom_test.go @@ -92,17 +92,25 @@ func TestStepCreateCD(t *testing.T) { } defer os.RemoveAll(dir) - expected := map[string]string{ + createFiles(t, dir, map[string]string{ "test folder/b/test1": "1", "test folder/b/test2": "2", "test folder 2/x": "3", "test_cd_roms.tmp": "4", "test cd files.tmp": "5", "Test-Test-Test5.tmp": "6", + "subfolder/meta-data": "subfolder/meta-data from files", + "subfolder/user-data": "subfolder/user-data from files", + "user-data": "user-data from files", + "vendor-data": "vendor-data from files", + }) + step.Content = map[string]string{ + "subfolder not created by files/test.tmp": "test", + "subfolder/meta-data": "subfolder/meta-data from content", + "user-data": "user-data from content", } - createFiles(t, dir, expected) - files := []string{"test folder", "test folder 2/", "test_cd_roms.tmp", "test cd files.tmp", "Test-Test-Test5.tmp"} + files := []string{"test folder", "test folder 2/", "test_cd_roms.tmp", "test cd files.tmp", "Test-Test-Test5.tmp", "subfolder", "user-data", "vendor-data"} step.Files = make([]string, len(files)) for i, fname := range files { @@ -124,7 +132,19 @@ func TestStepCreateCD(t *testing.T) { t.Fatalf("file not found: %s for %v", CD_path, step.Files) } - checkFiles(t, step.rootFolder, expected) + checkFiles(t, step.rootFolder, map[string]string{ + "test folder/b/test1": "1", + "test folder/b/test2": "2", + "test folder 2/x": "3", + "test_cd_roms.tmp": "4", + "test cd files.tmp": "5", + "Test-Test-Test5.tmp": "6", + "subfolder not created by files/test.tmp": "test", + "subfolder/meta-data": "subfolder/meta-data from content", + "subfolder/user-data": "subfolder/user-data from files", + "user-data": "user-data from content", + "vendor-data": "vendor-data from files", + }) step.Cleanup(state) @@ -150,6 +170,9 @@ func TestStepCreateCD_missing(t *testing.T) { defer os.RemoveAll(dir) step.Files = []string{"missing file.tmp"} + step.Content = map[string]string{ + "test_cd_roms.tmp": "should not be created", + } if action := step.Run(context.Background(), state); action != multistep.ActionHalt { t.Fatalf("bad action: %#v for %v", action, step.Files) }