diff --git a/.web-docs/components/builder/vagrant/README.md b/.web-docs/components/builder/vagrant/README.md index 3e859d33..d1dc9c5a 100644 --- a/.web-docs/components/builder/vagrant/README.md +++ b/.web-docs/components/builder/vagrant/README.md @@ -101,6 +101,8 @@ the Compress post-processor will not work with this builder. be found here. The template variables available to you are `{{ .BoxName }}`, `{{ .SyncedFolder }}`, and `{{.InsertKey}}`, which correspond to the Packer options box_name, synced_folder, and insert_key. + Alternatively, the template variable `{{.DefaultTemplate}}` is available for + use if you wish to extend the default generated template. - `synced_folder` (string) - Path to the folder to be synced to the guest. The path can be absolute or relative to the directory Packer is being run from. diff --git a/builder/vagrant/builder.go b/builder/vagrant/builder.go index e9389c74..8bc2849d 100644 --- a/builder/vagrant/builder.go +++ b/builder/vagrant/builder.go @@ -105,6 +105,8 @@ type Config struct { // be found here. The template variables available to you are // `{{ .BoxName }}`, `{{ .SyncedFolder }}`, and `{{.InsertKey}}`, which // correspond to the Packer options box_name, synced_folder, and insert_key. + // Alternatively, the template variable `{{.DefaultTemplate}}` is available for + // use if you wish to extend the default generated template. Template string `mapstructure:"template" required:"false"` // Path to the folder to be synced to the guest. The path can be absolute // or relative to the directory Packer is being run from. diff --git a/builder/vagrant/step_create_vagrantfile.go b/builder/vagrant/step_create_vagrantfile.go index ecc80c5c..22a898eb 100644 --- a/builder/vagrant/step_create_vagrantfile.go +++ b/builder/vagrant/step_create_vagrantfile.go @@ -6,9 +6,11 @@ package vagrant import ( "context" "fmt" + "io" "log" "os" "path/filepath" + "strings" "text/template" "github.com/hashicorp/packer-plugin-sdk/multistep" @@ -16,16 +18,25 @@ import ( ) type StepCreateVagrantfile struct { - Template string - OutputDir string - SyncedFolder string - GlobalID string - SourceBox string - BoxName string - InsertKey bool + Template string + OutputDir string + SyncedFolder string + GlobalID string + SourceBox string + BoxName string + InsertKey bool + defaultTemplateContent string } -var DEFAULT_TEMPLATE = `Vagrant.configure("2") do |config| +type VagrantfileOptions struct { + SyncedFolder string + SourceBox string + BoxName string + InsertKey bool + DefaultTemplate string +} + +const DEFAULT_TEMPLATE = `Vagrant.configure("2") do |config| config.vm.define "source", autostart: false do |source| source.vm.box = "{{.SourceBox}}" config.ssh.insert_key = {{.InsertKey}} @@ -42,51 +53,55 @@ var DEFAULT_TEMPLATE = `Vagrant.configure("2") do |config| {{- end}} end` -type VagrantfileOptions struct { - SyncedFolder string - SourceBox string - BoxName string - InsertKey bool -} +var defaultTemplate = template.Must(template.New("VagrantTpl").Parse(DEFAULT_TEMPLATE)) + +func (s *StepCreateVagrantfile) createVagrantfile() (tplPath string, err error) { + tplPath, err = filepath.Abs(filepath.Join(s.OutputDir, "Vagrantfile")) + if err != nil { + return + } -func (s *StepCreateVagrantfile) createVagrantfile() (string, error) { - tplPath := filepath.Join(s.OutputDir, "Vagrantfile") templateFile, err := os.Create(tplPath) if err != nil { - retErr := fmt.Errorf("Error creating vagrantfile %s", err.Error()) - return "", retErr + err = fmt.Errorf("Error creating vagrantfile %s", err.Error()) + return + } + + if s.defaultTemplateContent, err = s.renderDefaultTemplate(); err != nil { + return } - var tpl *template.Template if s.Template == "" { - // Generate vagrantfile template based on our default - tpl = template.Must(template.New("VagrantTpl").Parse(DEFAULT_TEMPLATE)) + // Generate vagrantfile template based on our default template + _, err = templateFile.WriteString(s.defaultTemplateContent) } else { - // Read in the template from provided file. + // Otherwise, read in the template from provided file. + var tpl *template.Template tpl, err = template.ParseFiles(s.Template) - if err != nil { - return "", err + if err == nil { + err = s.executeTemplate(tpl, templateFile) } } + return +} +func (s *StepCreateVagrantfile) executeTemplate(tpl *template.Template, file io.Writer) error { opts := &VagrantfileOptions{ - SyncedFolder: s.SyncedFolder, - BoxName: s.BoxName, - SourceBox: s.SourceBox, - InsertKey: s.InsertKey, - } - - err = tpl.Execute(templateFile, opts) - if err != nil { - return "", err + SyncedFolder: s.SyncedFolder, + BoxName: s.BoxName, + SourceBox: s.SourceBox, + InsertKey: s.InsertKey, + DefaultTemplate: s.defaultTemplateContent, } + return tpl.Execute(file, opts) +} - abspath, err := filepath.Abs(tplPath) - if err != nil { - return "", err +func (s *StepCreateVagrantfile) renderDefaultTemplate() (string, error) { + buf := new(strings.Builder) + if err := s.executeTemplate(defaultTemplate, buf); err != nil { + return "", fmt.Errorf("Error rendering default template %w", err) } - - return abspath, nil + return buf.String(), nil } func (s *StepCreateVagrantfile) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { diff --git a/builder/vagrant/step_create_vagrantfile_test.go b/builder/vagrant/step_create_vagrantfile_test.go index 31f2111f..ebd1209b 100644 --- a/builder/vagrant/step_create_vagrantfile_test.go +++ b/builder/vagrant/step_create_vagrantfile_test.go @@ -6,6 +6,7 @@ package vagrant import ( "io/ioutil" "os" + "path/filepath" "strings" "testing" @@ -116,3 +117,62 @@ end` t.Fatalf("EXPECTED: \n%s\n\n RECEIVED: \n%s\n\n", expected, actual) } } + +func TestCreateFile_customTemplate(t *testing.T) { + workdir := t.TempDir() + vagrantfileTemplatePath := filepath.Join(workdir, "Vagrantfile.tpl") + f, err := os.Create(vagrantfileTemplatePath) + if err != nil { + t.Fatal(err.Error()) + } + defer f.Close() + + var TEMPLATE = `{{ .DefaultTemplate }} +Vagrant.configure("2") do |config| + config.vm.provider "virtualbox" do |vb| + vb.customize ['modifyvm', :id, '--nested-hw-virt', 'on'] + end +end` + + _, err = f.WriteString(TEMPLATE) + if err != nil { + t.Fatal(err.Error()) + } + + testy := StepCreateVagrantfile{ + OutputDir: "./", + SourceBox: "apples", + BoxName: "bananas", + Template: vagrantfileTemplatePath, + } + templatePath, err := testy.createVagrantfile() + if err != nil { + t.Fatalf(err.Error()) + } + defer os.Remove(templatePath) + contents, err := ioutil.ReadFile(templatePath) + if err != nil { + t.Fatalf(err.Error()) + } + actual := string(contents) + expected := `Vagrant.configure("2") do |config| + config.vm.define "source", autostart: false do |source| + source.vm.box = "apples" + config.ssh.insert_key = false + end + config.vm.define "output" do |output| + output.vm.box = "bananas" + output.vm.box_url = "file://package.box" + config.ssh.insert_key = false + end + config.vm.synced_folder ".", "/vagrant", disabled: true +end +Vagrant.configure("2") do |config| + config.vm.provider "virtualbox" do |vb| + vb.customize ['modifyvm', :id, '--nested-hw-virt', 'on'] + end +end` + if ok := strings.Compare(actual, expected); ok != 0 { + t.Fatalf("EXPECTED: \n%s\n\n RECEIVED: \n%s\n\n", expected, actual) + } +} diff --git a/docs-partials/builder/vagrant/Config-not-required.mdx b/docs-partials/builder/vagrant/Config-not-required.mdx index cfe471fc..b6d2e709 100644 --- a/docs-partials/builder/vagrant/Config-not-required.mdx +++ b/docs-partials/builder/vagrant/Config-not-required.mdx @@ -48,6 +48,8 @@ be found here. The template variables available to you are `{{ .BoxName }}`, `{{ .SyncedFolder }}`, and `{{.InsertKey}}`, which correspond to the Packer options box_name, synced_folder, and insert_key. + Alternatively, the template variable `{{.DefaultTemplate}}` is available for + use if you wish to extend the default generated template. - `synced_folder` (string) - Path to the folder to be synced to the guest. The path can be absolute or relative to the directory Packer is being run from.