Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CDConfig: Add cd_content for file templating #61

Merged
merged 3 commits into from
May 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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

<!-- End of code generated from the comments of the CDConfig struct in multistep/commonsteps/extra_iso_config.go; -->
18 changes: 17 additions & 1 deletion multistep/commonsteps/extra_iso_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Comment on lines +68 to +83
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love this 👍🏼

CDLabel string `mapstructure:"cd_label"`
}

func (c *CDConfig) Prepare(ctx *interpolate.Context) []error {
Expand Down
32 changes: 29 additions & 3 deletions multistep/commonsteps/step_create_cdrom.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"os/exec"
Expand All @@ -22,16 +23,17 @@ 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

rootFolder string
}

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
}
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the filepath.Clean call as Join already does that ! :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@azr From testing, doing filepath.Join("/folder", "../hello") would result in /hello which I don't think would be intended behaviour. Doing filepath.Clean on an absolute path would turn ../hello into /hello so it joins to /folder/hello.

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
}
31 changes: 27 additions & 4 deletions multistep/commonsteps/step_create_cdrom_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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)

Expand All @@ -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)
}
Expand Down