From ad21367b21db0e780a796d11c1e36638f393de87 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Fri, 11 Jan 2019 14:06:15 -0800 Subject: [PATCH 01/10] vagrant builder --- builder/vagrant/artifact.go | 50 ++++ builder/vagrant/builder.go | 222 ++++++++++++++++++ builder/vagrant/driver.go | 80 +++++++ builder/vagrant/driver_2_2.go | 163 +++++++++++++ builder/vagrant/ssh.go | 19 ++ builder/vagrant/step_add_box.go | 83 +++++++ builder/vagrant/step_initialize_vagrant.go | 119 ++++++++++ .../vagrant/step_initialize_vagrant_test.go | 60 +++++ builder/vagrant/step_package.go | 44 ++++ builder/vagrant/step_ssh_config.go | 51 ++++ builder/vagrant/step_up.go | 57 +++++ command/plugin.go | 2 + .../source/docs/builders/alicloud-ecs.html.md | 45 ++-- .../docs/builders/amazon-chroot.html.md | 10 +- website/source/docs/builders/amazon.html.md | 6 +- website/source/docs/builders/azure.html.md | 33 +-- .../source/docs/builders/digitalocean.html.md | 6 +- website/source/docs/builders/docker.html.md | 6 +- .../docs/builders/googlecompute.html.md | 6 +- .../docs/builders/hetzner-cloud.html.md | 4 +- website/source/docs/builders/lxc.html.md | 2 +- website/source/docs/builders/lxd.html.md | 6 +- website/source/docs/builders/ncloud.html.md | 1 - .../source/docs/builders/oneandone.html.md | 2 +- .../source/docs/builders/openstack.html.md | 4 +- .../docs/builders/oracle-classic.html.md | 9 +- .../source/docs/builders/profitbricks.html.md | 2 +- website/source/docs/builders/scaleway.html.md | 4 +- website/source/docs/builders/triton.html.md | 2 +- website/source/docs/builders/vagrant.html.md | 66 ++++++ website/source/docs/commands/index.html.md | 2 +- .../docs/extending/custom-builders.html.md | 2 +- .../extending/custom-post-processors.html.md | 2 +- .../extending/custom-provisioners.html.md | 2 +- website/source/docs/extending/plugins.html.md | 7 +- .../post-processors/alicloud-import.html.md | 15 +- .../post-processors/amazon-import.html.md | 13 +- .../post-processors/docker-import.html.md | 7 +- .../googlecompute-import.html.md | 2 +- .../docs/post-processors/shell-local.html.md | 29 +-- .../docs/post-processors/vagrant.html.md | 4 +- .../post-processors/vsphere-template.html.md | 18 +- .../docs/provisioners/ansible-local.html.md | 1 + .../docs/provisioners/breakpoint.html.md | 24 +- .../provisioners/puppet-masterless.html.md | 9 +- .../docs/provisioners/shell-local.html.md | 27 +-- .../docs/provisioners/windows-restart.html.md | 20 +- .../docs/templates/communicator.html.md | 18 +- website/source/docs/templates/engine.html.md | 35 +-- 49 files changed, 1211 insertions(+), 190 deletions(-) create mode 100644 builder/vagrant/artifact.go create mode 100644 builder/vagrant/builder.go create mode 100644 builder/vagrant/driver.go create mode 100644 builder/vagrant/driver_2_2.go create mode 100644 builder/vagrant/ssh.go create mode 100644 builder/vagrant/step_add_box.go create mode 100644 builder/vagrant/step_initialize_vagrant.go create mode 100644 builder/vagrant/step_initialize_vagrant_test.go create mode 100644 builder/vagrant/step_package.go create mode 100644 builder/vagrant/step_ssh_config.go create mode 100644 builder/vagrant/step_up.go create mode 100644 website/source/docs/builders/vagrant.html.md diff --git a/builder/vagrant/artifact.go b/builder/vagrant/artifact.go new file mode 100644 index 000000000..ec5ed3282 --- /dev/null +++ b/builder/vagrant/artifact.go @@ -0,0 +1,50 @@ +package vagrant + +import ( + "fmt" + "path/filepath" + + "github.com/hashicorp/packer/packer" +) + +// This is the common builder ID to all of these artifacts. +const BuilderId = "vagrant" + +// Artifact is the result of running the vagrant builder, namely a set +// of files associated with the resulting machine. +type artifact struct { + OutputDir string + BoxName string +} + +// NewArtifact returns a vagrant artifact containing the .box file +func NewArtifact(dir string) (packer.Artifact, error) { + return &artifact{ + OutputDir: dir, + BoxName: "package.box", + }, nil +} + +func (*artifact) BuilderId() string { + return BuilderId +} + +func (a *artifact) Files() []string { + return []string{a.BoxName} +} + +func (a *artifact) Id() string { + return filepath.Join(a.OutputDir, a.BoxName) +} + +func (a *artifact) String() string { + return fmt.Sprintf("Vagrant box is %s", a.Id()) +} + +func (a *artifact) State(name string) interface{} { + return nil +} + +func (a *artifact) Destroy() error { + return nil +} diff --git a/builder/vagrant/builder.go b/builder/vagrant/builder.go new file mode 100644 index 000000000..fa2a94a76 --- /dev/null +++ b/builder/vagrant/builder.go @@ -0,0 +1,222 @@ +package vagrant + +import ( + "errors" + "fmt" + "log" + "strings" + "time" + + "github.com/hashicorp/packer/common" + "github.com/hashicorp/packer/common/bootcommand" + "github.com/hashicorp/packer/helper/communicator" + "github.com/hashicorp/packer/helper/config" + "github.com/hashicorp/packer/helper/multistep" + "github.com/hashicorp/packer/packer" + "github.com/hashicorp/packer/template/interpolate" +) + +// Builder implements packer.Builder and builds the actual VirtualBox +// images. +type Builder struct { + config *Config + runner multistep.Runner +} + +type SSHConfig struct { + Comm communicator.Config `mapstructure:",squash"` +} + +type Config struct { + common.PackerConfig `mapstructure:",squash"` + common.HTTPConfig `mapstructure:",squash"` + common.ISOConfig `mapstructure:",squash"` + common.FloppyConfig `mapstructure:",squash"` + bootcommand.BootConfig `mapstructure:",squash"` + SSHConfig `mapstructure:",squash"` + + // This is the name of the new virtual machine. + // By default this is "packer-BUILDNAME", where "BUILDNAME" is the name of the build. + OutputDir string `mapstructure:"output_dir"` + SourceBox string `mapstructure:"source_box"` + SourceBoxName string `mapstructure:"source_box_name"` + Provider string `mapstructure:"provider"` + + Communicator string `mapstructure:"communicator"` + + // What vagrantfile to use + VagrantfileTpl string `mapstructure:"vagrantfile_template"` + + // Whether to Halt, Suspend, or Destroy the box + TeardownMethod string `mapstructure:"teardown_method"` + + // Options for the "vagrant init" command + BoxVersion string `mapstructure:"box_version"` + Minimal bool `mapstructure:"init_minimal"` + Template string `mapstructure:"template"` + SyncedFolder string `mapstructure:"synced_folder"` + + // Options for the "vagrant box add" command + AddCACert string `mapstructure:"add_cacert"` + AddCAPath string `mapstructure:"add_capath"` + AddCert string `mapstructure:"add_cert"` + AddClean bool `mapstructure:"add_clean"` + AddForce bool `mapstructure:"add_force"` + AddInsecure bool `mapstructure:"add_insecure"` + + // Don't package the Vagrant box after build. + SkipPackage bool `mapstructure:"skip_package"` + OutputVagrantfile string `mapstructure:"output_vagrantfile"` + PackageInclude []string `mapstructure:"package_include"` + + ctx interpolate.Context +} + +// Prepare processes the build configuration parameters. +func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { + b.config = new(Config) + err := config.Decode(&b.config, &config.DecodeOpts{ + Interpolate: true, + InterpolateContext: &b.config.ctx, + InterpolateFilter: &interpolate.RenderFilter{ + Exclude: []string{ + "boot_command", + }, + }, + }, raws...) + if err != nil { + return nil, err + } + + // Accumulate any errors and warnings + var errs *packer.MultiError + warnings := make([]string, 0) + + if b.config.OutputDir == "" { + b.config.OutputDir = fmt.Sprintf("output-%s", b.config.PackerBuildName) + } + + if b.config.Comm.SSHTimeout == 0 { + b.config.Comm.SSHTimeout = 10 * time.Minute + } + + if b.config.Comm.Type != "ssh" { + errs = packer.MultiErrorAppend(errs, + fmt.Errorf(`The Vagrant builder currently only supports the ssh communicator"`)) + } + + if b.config.TeardownMethod == "" { + b.config.TeardownMethod = "destroy" + } else { + matches := false + for _, name := range []string{"halt", "suspend", "destroy"} { + if strings.ToLower(b.config.TeardownMethod) == name { + matches = true + } + } + if !matches { + errs = packer.MultiErrorAppend(errs, + fmt.Errorf(`TeardownMethod must be "halt", "suspend", or "destroy"`)) + } + } + + if errs != nil && len(errs.Errors) > 0 { + return warnings, errs + } + + return warnings, nil +} + +// Run executes a Packer build and returns a packer.Artifact representing +// a VirtualBox appliance. +func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) { + // Create the driver that we'll use to communicate with VirtualBox + driver, err := NewDriver() + if err != nil { + return nil, fmt.Errorf("Failed creating VirtualBox driver: %s", err) + } + + // Set up the state. + state := new(multistep.BasicStateBag) + state.Put("config", b.config) + state.Put("debug", b.config.PackerDebug) + state.Put("driver", driver) + state.Put("cache", cache) + state.Put("hook", hook) + state.Put("ui", ui) + + // Build the steps. + steps := []multistep.Step{} + if !b.config.SkipPackage { + steps = append(steps, + &common.StepOutputDir{ + Force: b.config.PackerForce, + Path: b.config.OutputDir, + }) + } + steps = append(steps, + &StepInitializeVagrant{ + BoxName: b.config.SourceBoxName, + BoxVersion: b.config.BoxVersion, + Minimal: b.config.Minimal, + Template: b.config.Template, + SourceBox: b.config.SourceBox, + OutputDir: b.config.OutputDir, + }, + &StepAddBox{ + BoxVersion: b.config.BoxVersion, + CACert: b.config.AddCACert, + CAPath: b.config.AddCAPath, + DownloadCert: b.config.AddCert, + Clean: b.config.AddClean, + Force: b.config.AddForce, + Insecure: b.config.AddInsecure, + Provider: b.config.Provider, + SourceBox: b.config.SourceBox, + BoxName: b.config.SourceBoxName, + }, + &StepUp{ + b.config.TeardownMethod, + b.config.Provider, + }, + &StepSSHConfig{}, + &communicator.StepConnect{ + Config: &b.config.SSHConfig.Comm, + Host: CommHost(), + SSHConfig: b.config.SSHConfig.Comm.SSHConfigFunc(), + }, + new(common.StepProvision), + &StepPackage{ + SkipPackage: b.config.SkipPackage, + Include: b.config.PackageInclude, + Vagrantfile: b.config.OutputVagrantfile, + }) + + // Run the steps. + b.runner = common.NewRunnerWithPauseFn(steps, b.config.PackerConfig, ui, state) + b.runner.Run(state) + + // Report any errors. + if rawErr, ok := state.GetOk("error"); ok { + return nil, rawErr.(error) + } + + // If we were interrupted or cancelled, then just exit. + if _, ok := state.GetOk(multistep.StateCancelled); ok { + return nil, errors.New("Build was cancelled.") + } + + if _, ok := state.GetOk(multistep.StateHalted); ok { + return nil, errors.New("Build was halted.") + } + + return NewArtifact(b.config.OutputDir) +} + +// Cancel. +func (b *Builder) Cancel() { + if b.runner != nil { + log.Println("Cancelling the step runner...") + b.runner.Cancel() + } +} diff --git a/builder/vagrant/driver.go b/builder/vagrant/driver.go new file mode 100644 index 000000000..08c31b8c8 --- /dev/null +++ b/builder/vagrant/driver.go @@ -0,0 +1,80 @@ +package vagrant + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" +) + +// A driver is able to talk to Vagrant and perform certain +// operations with it. + +type VagrantDriver interface { + // Calls "vagrant init" + Init([]string) error + + // Calls "vagrant add" + Add([]string) error + + // Calls "vagrant up" + Up([]string) (string, string, error) + + // Calls "vagrant halt" + Halt() error + + // Calls "vagrant suspend" + Suspend() error + + SSHConfig() (*VagrantSSHConfig, error) + + // Calls "vagrant destroy" + Destroy() error + + // Calls "vagrant package"[ + Package([]string) error + + // Verify checks to make sure that this driver should function + // properly. If there is any indication the driver can't function, + // this will return an error. + Verify() error + + // Version reads the version of VirtualBox that is installed. + Version() (string, error) +} + +func NewDriver() (VagrantDriver, error) { + // Hardcode path for now while I'm developing. Obviously this path needs + // to be discovered based on OS. + vagrantBinary := "vagrant" + if runtime.GOOS == "windows" { + vagrantBinary = "vagrant.exe" + } + + if _, err := exec.LookPath(vagrantBinary); err != nil { + return nil, fmt.Errorf("Error: Packer cannot find Vagrant in the path: %s", err.Error()) + } + + driver := &Vagrant_2_2_Driver{ + vagrantBinary: vagrantBinary, + } + + if err := driver.Verify(); err != nil { + return nil, err + } + + return driver, nil +} + +func findVBoxManageWindows(paths string) string { + for _, path := range strings.Split(paths, ";") { + path = filepath.Join(path, "VBoxManage.exe") + if _, err := os.Stat(path); err == nil { + return path + } + } + + return "" +} diff --git a/builder/vagrant/driver_2_2.go b/builder/vagrant/driver_2_2.go new file mode 100644 index 000000000..13a87dd59 --- /dev/null +++ b/builder/vagrant/driver_2_2.go @@ -0,0 +1,163 @@ +package vagrant + +import ( + "bytes" + "fmt" + "log" + "os" + "os/exec" + "regexp" + "strings" +) + +type Vagrant_2_2_Driver struct { + vagrantBinary string +} + +// Calls "vagrant init" +func (d *Vagrant_2_2_Driver) Init(args []string) error { + _, _, err := d.vagrantCmd(append([]string{"init"}, args...)...) + return err +} + +// Calls "vagrant add" +func (d *Vagrant_2_2_Driver) Add(args []string) error { + // vagrant box add partyvm ubuntu-14.04.vmware.box + _, _, err := d.vagrantCmd(append([]string{"box", "add"}, args...)...) + return err +} + +// Calls "vagrant up" +func (d *Vagrant_2_2_Driver) Up(args []string) (string, string, error) { + stdout, stderr, err := d.vagrantCmd(append([]string{"up"}, args...)...) + return stdout, stderr, err +} + +// Calls "vagrant halt" +func (d *Vagrant_2_2_Driver) Halt() error { + _, _, err := d.vagrantCmd([]string{"halt"}...) + return err +} + +// Calls "vagrant suspend" +func (d *Vagrant_2_2_Driver) Suspend() error { + _, _, err := d.vagrantCmd([]string{"suspend"}...) + return err +} + +// Calls "vagrant destroy" +func (d *Vagrant_2_2_Driver) Destroy() error { + _, _, err := d.vagrantCmd([]string{"destroy", "-f"}...) + return err +} + +// Calls "vagrant package" +func (d *Vagrant_2_2_Driver) Package(args []string) error { + _, _, err := d.vagrantCmd([]string{"package"}...) + return err +} + +// Verify makes sure that Vagrant exists at the given path +func (d *Vagrant_2_2_Driver) Verify() error { + vagrantPath, err := exec.LookPath(d.vagrantBinary) + if err != nil { + return fmt.Errorf("Can't find Vagrant binary!") + } + _, err = os.Stat(vagrantPath) + if err != nil { + return fmt.Errorf("Can't find Vagrant binary.") + } + return nil +} + +type VagrantSSHConfig struct { + Hostname string + User string + Port string + UserKnownHostsFile string + StrictHostKeyChecking bool + PasswordAuthentication bool + IdentityFile string + IdentitiesOnly bool + LogLevel string +} + +func parseSSHConfig(lines []string, value string) string { + out := "" + for _, line := range lines { + if index := strings.Index(line, value); index != -1 { + out = line[index+len(value):] + } + } + return out +} + +func yesno(yn string) bool { + if yn == "no" { + return false + } + return true +} + +func (d *Vagrant_2_2_Driver) SSHConfig() (*VagrantSSHConfig, error) { + // vagrant ssh-config --host 8df7860 + stdout, _, err := d.vagrantCmd([]string{"ssh-config"}...) + sshConf := &VagrantSSHConfig{} + + lines := strings.Split(stdout, "\n") + sshConf.Hostname = parseSSHConfig(lines, "HostName ") + sshConf.User = parseSSHConfig(lines, "User ") + sshConf.Port = parseSSHConfig(lines, "Port ") + sshConf.UserKnownHostsFile = parseSSHConfig(lines, "UserKnownHostsFile ") + sshConf.IdentityFile = parseSSHConfig(lines, "IdentityFile ") + sshConf.LogLevel = parseSSHConfig(lines, "LogLevel ") + + // handle the booleans + sshConf.StrictHostKeyChecking = yesno(parseSSHConfig(lines, "StrictHostKeyChecking ")) + sshConf.PasswordAuthentication = yesno(parseSSHConfig(lines, "PasswordAuthentication ")) + sshConf.IdentitiesOnly = yesno((parseSSHConfig(lines, "IdentitiesOnly "))) + + return sshConf, err +} + +// Version reads the version of VirtualBox that is installed. +func (d *Vagrant_2_2_Driver) Version() (string, error) { + stdoutString, _, err := d.vagrantCmd([]string{"version"}...) + // Example stdout: + + // Installed Version: 2.2.3 + // + // Vagrant was unable to check for the latest version of Vagrant. + // Please check manually at https://www.vagrantup.com + + // Use regex to find version + reg := regexp.MustCompile(`(\d+\.)?(\d+\.)?(\*|\d+)`) + version := reg.FindString(stdoutString) + if version == "" { + return "", err + } + + return version, nil +} + +func (d *Vagrant_2_2_Driver) vagrantCmd(args ...string) (string, string, error) { + var stdout, stderr bytes.Buffer + + log.Printf("Calling Vagrant CLI: %#v", args) + cmd := exec.Command(d.vagrantBinary, args...) + cmd.Stdout = &stdout + cmd.Stderr = &stderr + err := cmd.Run() + + stdoutString := strings.TrimSpace(stdout.String()) + stderrString := strings.TrimSpace(stderr.String()) + + if _, ok := err.(*exec.ExitError); ok { + err = fmt.Errorf("Vagrant error: %s", stderrString) + } + + log.Printf("[vagrant driver] stdout: %s", stdoutString) + log.Printf("[vagrant driver] stderr: %s", stderrString) + + return stdoutString, stderrString, err +} diff --git a/builder/vagrant/ssh.go b/builder/vagrant/ssh.go new file mode 100644 index 000000000..d4a56cb17 --- /dev/null +++ b/builder/vagrant/ssh.go @@ -0,0 +1,19 @@ +package vagrant + +import ( + "github.com/hashicorp/packer/helper/multistep" +) + +func CommHost() func(multistep.StateBag) (string, error) { + return func(state multistep.StateBag) (string, error) { + config := state.Get("config").(*Config) + return config.Comm.SSHHost, nil + } +} + +func SSHPort() func(multistep.StateBag) (int, error) { + return func(state multistep.StateBag) (int, error) { + config := state.Get("config").(*Config) + return config.Comm.SSHPort, nil + } +} diff --git a/builder/vagrant/step_add_box.go b/builder/vagrant/step_add_box.go new file mode 100644 index 000000000..cdbfe3518 --- /dev/null +++ b/builder/vagrant/step_add_box.go @@ -0,0 +1,83 @@ +package vagrant + +import ( + "context" + "log" + "strings" + + "github.com/hashicorp/packer/helper/multistep" + "github.com/hashicorp/packer/packer" +) + +type StepAddBox struct { + BoxVersion string + CACert string + CAPath string + DownloadCert string + Clean bool + Force bool + Insecure bool + Provider string + SourceBox string + BoxName string +} + +func (s *StepAddBox) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { + driver := state.Get("driver").(VagrantDriver) + ui := state.Get("ui").(packer.Ui) + + ui.Say("Adding box using vagrant box add..") + addArgs := []string{} + + if strings.HasSuffix(s.SourceBox, ".box") { + // The box isn't a namespace like you'd pull from vagrant cloud + addArgs = append(addArgs, s.BoxName) + } + + addArgs = append(addArgs, s.SourceBox) + + if s.BoxVersion != "" { + addArgs = append(addArgs, "--box-version", s.BoxVersion) + } + + if s.CACert != "" { + addArgs = append(addArgs, "--cacert", s.CACert) + } + + if s.CAPath != "" { + addArgs = append(addArgs, "--capath", s.CAPath) + } + + if s.DownloadCert != "" { + addArgs = append(addArgs, "--cert", s.DownloadCert) + } + + if s.Clean { + addArgs = append(addArgs, "--clean") + } + + if s.Force { + addArgs = append(addArgs, "--force") + } + + if s.Insecure { + addArgs = append(addArgs, "--insecure") + } + + if s.Provider != "" { + addArgs = append(addArgs, "--provider", s.Provider) + } + + log.Printf("[vagrant] Calling box add with following args %s", strings.Join(addArgs, " ")) + // Call vagrant using prepared arguments + err := driver.Add(addArgs) + if err != nil { + state.Put("error", err) + return multistep.ActionHalt + } + + return multistep.ActionContinue +} + +func (s *StepAddBox) Cleanup(state multistep.StateBag) { +} diff --git a/builder/vagrant/step_initialize_vagrant.go b/builder/vagrant/step_initialize_vagrant.go new file mode 100644 index 000000000..0f6357a48 --- /dev/null +++ b/builder/vagrant/step_initialize_vagrant.go @@ -0,0 +1,119 @@ +package vagrant + +import ( + "context" + "fmt" + "os" + "path/filepath" + "text/template" + + "github.com/hashicorp/packer/helper/multistep" + "github.com/hashicorp/packer/packer" +) + +type StepInitializeVagrant struct { + BoxName string + BoxVersion string + Minimal bool + Template string + SourceBox string + OutputDir string + SyncedFolder string +} + +var DEFAULT_TEMPLATE = `Vagrant.configure("2") do |config| + config.vm.box = "{{.BoxName}}" + {{ if ne .SyncedFolder "" -}} + config.vm.synced_folder "{{.SyncedFolder}}", "/vagrant" + {{- else -}} + config.vm.synced_folder ".", "/vagrant", disabled: true + {{- end}} +end` + +type VagrantfileOptions struct { + SyncedFolder string + BoxName string +} + +func (s *StepInitializeVagrant) createInitializeCommand() (string, error) { + tplPath := filepath.Join(s.OutputDir, "packer-vagrantfile-template.erb") + templateFile, err := os.Create(tplPath) + templateFile.Chmod(0777) + if err != nil { + retErr := fmt.Errorf("Error creating vagrantfile %s", err.Error()) + return "", retErr + } + + var tpl *template.Template + if s.Template == "" { + // Generate vagrantfile template based on our default + tpl = template.Must(template.New("VagrantTpl").Parse(DEFAULT_TEMPLATE)) + } else { + // Read in the template from provided file. + tpl, err = template.ParseFiles(s.Template) + if err != nil { + return "", err + } + } + + opts := &VagrantfileOptions{ + SyncedFolder: s.SyncedFolder, + BoxName: s.SourceBox, + } + + err = tpl.Execute(templateFile, opts) + if err != nil { + return "", err + } + + abspath, err := filepath.Abs(tplPath) + if err != nil { + return "", err + } + + return abspath, nil +} +func (s *StepInitializeVagrant) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { + driver := state.Get("driver").(VagrantDriver) + ui := state.Get("ui").(packer.Ui) + + ui.Say("Initializing Vagrant in build directory...") + + // Prepare arguments + initArgs := []string{} + + if s.BoxName != "" { + initArgs = append(initArgs, s.BoxName) + } + + initArgs = append(initArgs, s.SourceBox) + + if s.BoxVersion != "" { + initArgs = append(initArgs, "--box-version", s.BoxVersion) + } + + if s.Minimal { + initArgs = append(initArgs, "-m") + } + + tplPath, err := s.createInitializeCommand() + if err != nil { + state.Put("error", err) + return multistep.ActionHalt + } + + initArgs = append(initArgs, "--template", tplPath) + + os.Chdir(s.OutputDir) + // Call vagrant using prepared arguments + err = driver.Init(initArgs) + if err != nil { + state.Put("error", err) + return multistep.ActionHalt + } + + return multistep.ActionContinue +} + +func (s *StepInitializeVagrant) Cleanup(state multistep.StateBag) { +} diff --git a/builder/vagrant/step_initialize_vagrant_test.go b/builder/vagrant/step_initialize_vagrant_test.go new file mode 100644 index 000000000..158ec34af --- /dev/null +++ b/builder/vagrant/step_initialize_vagrant_test.go @@ -0,0 +1,60 @@ +package vagrant + +import ( + "io/ioutil" + "os" + "strings" + "testing" + + "github.com/hashicorp/packer/helper/multistep" +) + +func TestStepInitialize_Impl(t *testing.T) { + var raw interface{} + raw = new(StepInitializeVagrant) + if _, ok := raw.(multistep.Step); !ok { + t.Fatalf("initialize should be a step") + } +} + +func TestCreateFile(t *testing.T) { + testy := StepInitializeVagrant{ + OutputDir: "./", + SourceBox: "bananas", + } + templatePath, err := testy.createInitializeCommand() + if err != nil { + t.Fatalf(err.Error()) + } + contents, err := ioutil.ReadFile(templatePath) + actual := string(contents) + expected := `Vagrant.configure("2") do |config| + config.vm.box = "bananas" + config.vm.synced_folder ".", "/vagrant", disabled: true +end` + if ok := strings.Compare(actual, expected); ok != 0 { + t.Fatalf("EXPECTED: \n%s\n\n RECEIVED: \n%s\n\n", expected, actual) + } + os.Remove(templatePath) +} + +func TestCreateFile_customSync(t *testing.T) { + testy := StepInitializeVagrant{ + OutputDir: "./", + SyncedFolder: "myfolder/foldertimes", + } + templatePath, err := testy.createInitializeCommand() + if err != nil { + t.Fatalf(err.Error()) + } + contents, err := ioutil.ReadFile(templatePath) + actual := string(contents) + expected := `Vagrant.configure("2") do |config| + config.vm.box = "" + config.vm.synced_folder "myfolder/foldertimes", "/vagrant" +end` + if ok := strings.Compare(actual, expected); ok != 0 { + t.Fatalf("EXPECTED: \n%s\n\n RECEIVED: \n%s\n\n", expected, actual) + } + os.Remove(templatePath) +} diff --git a/builder/vagrant/step_package.go b/builder/vagrant/step_package.go new file mode 100644 index 000000000..d61b5c1ce --- /dev/null +++ b/builder/vagrant/step_package.go @@ -0,0 +1,44 @@ +package vagrant + +import ( + "context" + "strings" + + "github.com/hashicorp/packer/helper/multistep" + "github.com/hashicorp/packer/packer" +) + +type StepPackage struct { + SkipPackage bool + Include []string + Vagrantfile string +} + +func (s *StepPackage) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { + driver := state.Get("driver").(VagrantDriver) + ui := state.Get("ui").(packer.Ui) + + if s.SkipPackage { + ui.Say("skip_package flag set; not going to call Vagrant package on this box.") + return multistep.ActionContinue + } + ui.Say("Packaging box...") + packageArgs := []string{} + if len(s.Include) > 0 { + packageArgs = append(packageArgs, "--include", strings.Join(s.Include, ",")) + } + if s.Vagrantfile != "" { + packageArgs = append(packageArgs, "--vagrantfile", s.Vagrantfile) + } + + err := driver.Package(packageArgs) + if err != nil { + state.Put("error", err) + return multistep.ActionHalt + } + + return multistep.ActionContinue +} + +func (s *StepPackage) Cleanup(state multistep.StateBag) { +} diff --git a/builder/vagrant/step_ssh_config.go b/builder/vagrant/step_ssh_config.go new file mode 100644 index 000000000..175224727 --- /dev/null +++ b/builder/vagrant/step_ssh_config.go @@ -0,0 +1,51 @@ +package vagrant + +import ( + "context" + "strconv" + + "github.com/hashicorp/packer/helper/multistep" +) + +// Vagrant already sets up ssh on the guests; our job is to find out what +// it did. We can do that with the ssh-config command. Example output: + +// $ vagrant ssh-config +// Host default +// HostName 172.16.41.194 +// User vagrant +// Port 22 +// UserKnownHostsFile /dev/null +// StrictHostKeyChecking no +// PasswordAuthentication no +// IdentityFile /Users/mmarsh/Projects/vagrant-boxes/ubuntu/.vagrant/machines/default/vmware_fusion/private_key +// IdentitiesOnly yes +// LogLevel FATAL + +type StepSSHConfig struct{} + +func (s *StepSSHConfig) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { + driver := state.Get("driver").(VagrantDriver) + config := state.Get("config").(*Config) + + sshConfig, err := driver.SSHConfig() + if err != nil { + state.Put("error", err) + return multistep.ActionHalt + } + + config.Comm.SSHPrivateKeyFile = sshConfig.IdentityFile + config.Comm.SSHUsername = sshConfig.User + config.Comm.SSHHost = sshConfig.Hostname + port, err := strconv.Atoi(sshConfig.Port) + if err != nil { + state.Put("error", err) + return multistep.ActionHalt + } + config.Comm.SSHPort = port + + return multistep.ActionContinue +} + +func (s *StepSSHConfig) Cleanup(state multistep.StateBag) { +} diff --git a/builder/vagrant/step_up.go b/builder/vagrant/step_up.go new file mode 100644 index 000000000..93aad8b63 --- /dev/null +++ b/builder/vagrant/step_up.go @@ -0,0 +1,57 @@ +package vagrant + +import ( + "context" + "fmt" + + "github.com/hashicorp/packer/helper/multistep" + "github.com/hashicorp/packer/packer" +) + +type StepUp struct { + TeardownMethod string + Provider string +} + +func (s *StepUp) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { + driver := state.Get("driver").(VagrantDriver) + ui := state.Get("ui").(packer.Ui) + + ui.Say("Calling Vagrant Up...") + + var args []string + if s.Provider != "" { + args = append(args, fmt.Sprintf("--provider=%s", s.Provider)) + } + + _, _, err := driver.Up(args) + + if err != nil { + state.Put("error", err) + return multistep.ActionHalt + } + + return multistep.ActionContinue +} + +func (s *StepUp) Cleanup(state multistep.StateBag) { + driver := state.Get("driver").(VagrantDriver) + ui := state.Get("ui").(packer.Ui) + + ui.Say(fmt.Sprintf("%sing Vagrant box...", s.TeardownMethod)) + + var err error + if s.TeardownMethod == "halt" { + err = driver.Halt() + } else if s.TeardownMethod == "suspend" { + err = driver.Suspend() + } else if s.TeardownMethod == "destroy" { + err = driver.Destroy() + } else { + // Should never get here because of template validation + state.Put("error", fmt.Errorf("Invalid teardown method selected; must be either halt, suspend, or destory.")) + } + if err != nil { + state.Put("error", fmt.Errorf("Error halting Vagrant machine; please try to do this manually")) + } +} diff --git a/command/plugin.go b/command/plugin.go index 2d2272640..4311a55c9 100644 --- a/command/plugin.go +++ b/command/plugin.go @@ -43,6 +43,7 @@ import ( scalewaybuilder "github.com/hashicorp/packer/builder/scaleway" tencentcloudcvmbuilder "github.com/hashicorp/packer/builder/tencentcloud/cvm" tritonbuilder "github.com/hashicorp/packer/builder/triton" + vagrantbuilder "github.com/hashicorp/packer/builder/vagrant" virtualboxisobuilder "github.com/hashicorp/packer/builder/virtualbox/iso" virtualboxovfbuilder "github.com/hashicorp/packer/builder/virtualbox/ovf" vmwareisobuilder "github.com/hashicorp/packer/builder/vmware/iso" @@ -116,6 +117,7 @@ var Builders = map[string]packer.Builder{ "scaleway": new(scalewaybuilder.Builder), "tencentcloud-cvm": new(tencentcloudcvmbuilder.Builder), "triton": new(tritonbuilder.Builder), + "vagrant": new(vagrantbuilder.Builder), "virtualbox-iso": new(virtualboxisobuilder.Builder), "virtualbox-ovf": new(virtualboxovfbuilder.Builder), "vmware-iso": new(vmwareisobuilder.Builder), diff --git a/website/source/docs/builders/alicloud-ecs.html.md b/website/source/docs/builders/alicloud-ecs.html.md index 8585cee65..945b95090 100644 --- a/website/source/docs/builders/alicloud-ecs.html.md +++ b/website/source/docs/builders/alicloud-ecs.html.md @@ -147,28 +147,30 @@ builder. Snapshots from on or before July 15, 2013 cannot be used to create a disk. - -- `image_ignore_data_disks`(boolean) - If this value is true, the image created - will not include any snapshot of data disks. This option would be useful for - any circumstance that default data disks with instance types are not concerned. - The default value is false. -- `wait_snapshot_ready_timeout`(number) - Timeout of creating snapshot(s). The - default timeout is 3600 seconds if this option is not set or is set to 0. For - those disks containing lots of data, it may require a higher timeout value. +- `image_ignore_data_disks`(boolean) - If this value is true, the image + created will not include any snapshot of data disks. This option would be + useful for any circumstance that default data disks with instance types are + not concerned. The default value is false. + +- `wait_snapshot_ready_timeout`(number) - Timeout of creating snapshot(s). + The default timeout is 3600 seconds if this option is not set or is set + to 0. For those disks containing lots of data, it may require a higher + timeout value. - `image_force_delete` (boolean) - If this value is true, when the target - image names including those copied are duplicated with existing images, - it will delete the existing images and then create the target images, - otherwise, the creation will fail. The default value is false. Check - `image_name` and `image_copy_names` options for names of target images. - If [-force](https://packer.io/docs/commands/build.html#force) option is provided - in `build` command, this option can be omitted and taken as true. + image names including those copied are duplicated with existing images, it + will delete the existing images and then create the target images, + otherwise, the creation will fail. The default value is false. Check + `image_name` and `image_copy_names` options for names of target images. If + [-force](https://packer.io/docs/commands/build.html#force) option is + provided in `build` command, this option can be omitted and taken as true. - `image_force_delete_snapshots` (boolean) - If this value is true, when delete the duplicated existing images, the source snapshots of those images - will be delete either. If [-force](https://packer.io/docs/commands/build.html#force) - option is provided in `build` command, this option can be omitted and taken as true. + will be delete either. If + [-force](https://packer.io/docs/commands/build.html#force) option is + provided in `build` command, this option can be omitted and taken as true. - `image_share_account` (array of string) - The IDs of to-be-added Aliyun accounts to which the image is shared. The number of accounts is 1 to 10. @@ -299,10 +301,13 @@ Here is a basic example for Alicloud. \~> Note: Images can become deprecated after a while; run `aliyun ecs DescribeImages` to find one that exists. -\~> Note: Since WinRM is closed by default in the system image. If you are planning -to use Windows as the base image, you need enable it by userdata in order to connect to -the instance, check [alicloud_windows.json](https://github.com/hashicorp/packer/tree/master/examples/alicloud/basic/alicloud_windows.json) -and [winrm_enable_userdata.ps1](https://github.com/hashicorp/packer/tree/master/examples/alicloud/basic/winrm_enable_userdata.ps1) for details. +\~> Note: Since WinRM is closed by default in the system image. If you are +planning to use Windows as the base image, you need enable it by userdata in +order to connect to the instance, check +[alicloud\_windows.json](https://github.com/hashicorp/packer/tree/master/examples/alicloud/basic/alicloud_windows.json) +and +[winrm\_enable\_userdata.ps1](https://github.com/hashicorp/packer/tree/master/examples/alicloud/basic/winrm_enable_userdata.ps1) +for details. See the [examples/alicloud](https://github.com/hashicorp/packer/tree/master/examples/alicloud) diff --git a/website/source/docs/builders/amazon-chroot.html.md b/website/source/docs/builders/amazon-chroot.html.md index 24622e049..acbed1997 100644 --- a/website/source/docs/builders/amazon-chroot.html.md +++ b/website/source/docs/builders/amazon-chroot.html.md @@ -24,7 +24,7 @@ builder is able to build an EBS-backed AMI without launching a new EC2 instance. This can dramatically speed up AMI builds for organizations who need the extra fast build. -~> **This is an advanced builder** If you're just getting started with +\~> **This is an advanced builder** If you're just getting started with Packer, we recommend starting with the [amazon-ebs builder](/docs/builders/amazon-ebs.html), which is much easier to use. @@ -154,8 +154,8 @@ each category, the available configuration keys are alphabetized. associated with AMIs, which have been deregistered by `force_deregister`. Default `false`. -- `insecure_skip_tls_verify` (boolean) - This allows skipping TLS verification of - the AWS EC2 endpoint. The default is `false`. +- `insecure_skip_tls_verify` (boolean) - This allows skipping TLS + verification of the AWS EC2 endpoint. The default is `false`. - `kms_key_id` (string) - ID, alias or ARN of the KMS key to use for boot volume encryption. This only applies to the main `region`, other regions @@ -457,8 +457,8 @@ services: ### Ansible provisioner -Running ansible against `amazon-chroot` requires changing the Ansible connection -to chroot and running Ansible as root/sudo. +Running ansible against `amazon-chroot` requires changing the Ansible +connection to chroot and running Ansible as root/sudo. ### Using Instances with NVMe block devices. diff --git a/website/source/docs/builders/amazon.html.md b/website/source/docs/builders/amazon.html.md index 931ff86ae..f722cd7d3 100644 --- a/website/source/docs/builders/amazon.html.md +++ b/website/source/docs/builders/amazon.html.md @@ -218,9 +218,9 @@ fail. If that's the case, you might see an error like this: ==> amazon-ebs: Error querying AMI: AuthFailure: AWS was not able to validate the provided access credentials If you suspect your system's date is wrong, you can compare it against -. On Linux/OS X, you can run the `date` command to get -the current time. If you're on Linux, you can try setting the time with ntp by -running `sudo ntpd -q`. +http://www.time.gov/. On +Linux/OS X, you can run the `date` command to get the current time. If you're +on Linux, you can try setting the time with ntp by running `sudo ntpd -q`. ### `exceeded wait attempts` while waiting for tasks to complete diff --git a/website/source/docs/builders/azure.html.md b/website/source/docs/builders/azure.html.md index 8caf44848..a4b86d2c7 100644 --- a/website/source/docs/builders/azure.html.md +++ b/website/source/docs/builders/azure.html.md @@ -206,8 +206,8 @@ Providing `temp_resource_group_name` or `location` in combination with - `os_disk_size_gb` (number) Specify the size of the OS disk in GB (gigabytes). Values of zero or less than zero are ignored. -- `disk_caching_type` (string) Specify the disk caching type. Valid values are None, ReadOnly, - and ReadWrite. The default value is ReadWrite. +- `disk_caching_type` (string) Specify the disk caching type. Valid values + are None, ReadOnly, and ReadWrite. The default value is ReadWrite. - `disk_additional_size` (array of integers) - The size(s) of any additional hard disks for the VM in gigabytes. If this is not specified then the VM @@ -325,13 +325,14 @@ Providing `temp_resource_group_name` or `location` in combination with value and defaults to false. **Important** Setting this true means that your builds are faster, however any failed deletes are not reported. -- `managed_image_os_disk_snapshot_name` (string) If managed\_image\_os\_disk\_snapshot\_name - is set, a snapshot of the OS disk is created with the same name as this value before the - VM is captured. +- `managed_image_os_disk_snapshot_name` (string) If + managed\_image\_os\_disk\_snapshot\_name is set, a snapshot of the OS disk + is created with the same name as this value before the VM is captured. -- `managed_image_data_disk_snapshot_prefix` (string) If managed\_image\_data\_disk\_snapshot\_prefix - is set, snapshot of the data disk(s) is created with the same prefix as this value before the VM - is captured. +- `managed_image_data_disk_snapshot_prefix` (string) If + managed\_image\_data\_disk\_snapshot\_prefix is set, snapshot of the data + disk(s) is created with the same prefix as this value before the VM is + captured. ## Basic Example @@ -403,14 +404,14 @@ here](https://technet.microsoft.com/en-us/library/hh824815.aspx) } ``` -The Windows Guest Agent participates in the Sysprep process. The -agent must be fully installed before the VM can be sysprep'ed. To -ensure this is true all agent services must be running before -executing sysprep.exe. The above JSON snippet shows one way to do this -in the PowerShell provisioner. This snippet is **only** required if -the VM is configured to install the agent, which is the default. To -learn more about disabling the Windows Guest Agent please see [Install the VM Agent](https://docs.microsoft.com/en-us/azure/virtual-machines/extensions/agent-windows#install-the-vm-agent). - +The Windows Guest Agent participates in the Sysprep process. The agent must be +fully installed before the VM can be sysprep'ed. To ensure this is true all +agent services must be running before executing sysprep.exe. The above JSON +snippet shows one way to do this in the PowerShell provisioner. This snippet is +**only** required if the VM is configured to install the agent, which is the +default. To learn more about disabling the Windows Guest Agent please see +[Install the VM +Agent](https://docs.microsoft.com/en-us/azure/virtual-machines/extensions/agent-windows#install-the-vm-agent). ### Linux diff --git a/website/source/docs/builders/digitalocean.html.md b/website/source/docs/builders/digitalocean.html.md index 1d382869d..b7a4f298d 100644 --- a/website/source/docs/builders/digitalocean.html.md +++ b/website/source/docs/builders/digitalocean.html.md @@ -41,17 +41,17 @@ builder. - `image` (string) - The name (or slug) of the base image to use. This is the image that will be used to launch a new droplet and provision it. See - https://developers.digitalocean.com/documentation/v2/#list-all-images + https://developers.digitalocean.com/documentation/v2/\#list-all-images for details on how to get a list of the accepted image names/slugs. - `region` (string) - The name (or slug) of the region to launch the droplet in. Consequently, this is the region where the snapshot will be available. See - https://developers.digitalocean.com/documentation/v2/#list-all-regions + https://developers.digitalocean.com/documentation/v2/\#list-all-regions for the accepted region names/slugs. - `size` (string) - The name (or slug) of the droplet size to use. See - https://developers.digitalocean.com/documentation/v2/#list-all-sizes + https://developers.digitalocean.com/documentation/v2/\#list-all-sizes for the accepted size names/slugs. ### Optional: diff --git a/website/source/docs/builders/docker.html.md b/website/source/docs/builders/docker.html.md index 6612bb464..e5e1879ba 100644 --- a/website/source/docs/builders/docker.html.md +++ b/website/source/docs/builders/docker.html.md @@ -185,9 +185,9 @@ You must specify (only) one of `commit`, `discard`, or `export_path`. information see the [section on ECR](#amazon-ec2-container-registry). - `exec_user` (string) - Username or UID (format: - <name\\\|uid>\[:<group\\\|gid>\]) to run remote commands with. - You may need this if you get permission errors trying to run the `shell` or - other provisioners. + <name\\\\\|uid>\[:<group\\\\\|gid>\]) to run remote commands + with. You may need this if you get permission errors trying to run the + `shell` or other provisioners. - `login` (boolean) - Defaults to false. If true, the builder will login in order to pull the image. The builder only logs in for the duration of the diff --git a/website/source/docs/builders/googlecompute.html.md b/website/source/docs/builders/googlecompute.html.md index f1d672adb..24a83b05f 100644 --- a/website/source/docs/builders/googlecompute.html.md +++ b/website/source/docs/builders/googlecompute.html.md @@ -19,8 +19,8 @@ based on existing images. It is possible to build images from scratch, but not with the `googlecompute` Packer builder. The process is recommended only for advanced users, please see \[Building GCE Images from Scratch\] -() and the -[Google Compute Import +(https://cloud.google.com/compute/docs/tutorials/building-images) +and the [Google Compute Import Post-Processor](/docs/post-processors/googlecompute-import.html) for more information. @@ -139,7 +139,7 @@ using the gcloud command. gcloud compute firewall-rules create allow-winrm --allow tcp:5986 Or alternatively by navigating to -. +https://console.cloud.google.com/networking/firewalls/list. Once this is set up, the following is a complete working packer config after setting a valid `account_file` and `project_id`: diff --git a/website/source/docs/builders/hetzner-cloud.html.md b/website/source/docs/builders/hetzner-cloud.html.md index 96e945521..fe886dba9 100644 --- a/website/source/docs/builders/hetzner-cloud.html.md +++ b/website/source/docs/builders/hetzner-cloud.html.md @@ -75,7 +75,9 @@ builder. - `ssh_keys` (array of strings) - List of SSH keys by name or id to be added to image on launch. -- `rescue` (string) - Enable and boot in to the specified rescue system. This enables simple installation of custom operating systems. `linux64` `linux32` or `freebsd64` +- `rescue` (string) - Enable and boot in to the specified rescue system. This + enables simple installation of custom operating systems. `linux64` + `linux32` or `freebsd64` ## Basic Example diff --git a/website/source/docs/builders/lxc.html.md b/website/source/docs/builders/lxc.html.md index f17b20e5f..c94042856 100644 --- a/website/source/docs/builders/lxc.html.md +++ b/website/source/docs/builders/lxc.html.md @@ -19,7 +19,7 @@ as a tar.gz of the root file system. The LXC builder requires a modern linux kernel and the `lxc` or `lxc1` package. This builder does not work with LXD. -~> Note: to build Centos images on a Debian family host, you will need the +\~> Note: to build Centos images on a Debian family host, you will need the `yum` package installed.
Some provisioners such as `ansible-local` get confused when running in a container of a different family. E.G. it will attempt to use `apt-get` to install packages, when running in a Centos diff --git a/website/source/docs/builders/lxd.html.md b/website/source/docs/builders/lxd.html.md index 2523f1541..45a2937a9 100644 --- a/website/source/docs/builders/lxd.html.md +++ b/website/source/docs/builders/lxd.html.md @@ -47,7 +47,7 @@ Below is a fully functioning example. container. This can be a (local or remote) image (name or fingerprint). E.G. `my-base-image`, `ubuntu-daily:x`, `08fababf6f27`, ... - ~> Note: The builder may appear to pause if required to download a + \~> Note: The builder may appear to pause if required to download a remote image, as they are usually 100-200MB. `/var/log/lxd/lxd.log` will mention starting such downloads. @@ -68,8 +68,8 @@ Below is a fully functioning example. - `publish_properties` (map\[string\]string) - Pass key values to the publish step to be set as properties on the output image. This is most helpful to set the description, but can be used to set anything needed. See - for more - properties. + https://stgraber.org/2016/03/30/lxd-2-0-image-management-512/ + for more properties. - `launch_config` (map\[string\]string) - List of key/value pairs you wish to pass to `lxc launch` via `--config`. Defaults to empty. diff --git a/website/source/docs/builders/ncloud.html.md b/website/source/docs/builders/ncloud.html.md index 1aacf633f..ddc91b257 100644 --- a/website/source/docs/builders/ncloud.html.md +++ b/website/source/docs/builders/ncloud.html.md @@ -90,7 +90,6 @@ Here is a basic example for windows server. ] } - Here is a basic example for linux server. { diff --git a/website/source/docs/builders/oneandone.html.md b/website/source/docs/builders/oneandone.html.md index dca56dc85..5b9007823 100644 --- a/website/source/docs/builders/oneandone.html.md +++ b/website/source/docs/builders/oneandone.html.md @@ -44,7 +44,7 @@ builder. while waiting for the build to complete. Default value "600". - `url` (string) - Endpoint for the 1&1 REST API. Default URL - "" + "https://cloudpanel-api.1and1.com/v1" ## Example diff --git a/website/source/docs/builders/openstack.html.md b/website/source/docs/builders/openstack.html.md index c67460b4c..837409e9f 100644 --- a/website/source/docs/builders/openstack.html.md +++ b/website/source/docs/builders/openstack.html.md @@ -278,8 +278,8 @@ builder. isn't specified, the default enforced by your OpenStack cluster will be used. -- `volume_size` (int) - Size of the Block Storage service volume in GB. If this - isn't specified, it is set to source image min disk value (if set) or +- `volume_size` (int) - Size of the Block Storage service volume in GB. If + this isn't specified, it is set to source image min disk value (if set) or calculated from the source image bytes size. Note that in some cases this needs to be specified, if `use_blockstorage_volume` is true. diff --git a/website/source/docs/builders/oracle-classic.html.md b/website/source/docs/builders/oracle-classic.html.md index e917abeda..1fef81830 100644 --- a/website/source/docs/builders/oracle-classic.html.md +++ b/website/source/docs/builders/oracle-classic.html.md @@ -141,9 +141,10 @@ If this is set, a few more options become available. - `builder_communicator` (communicator) - This represents an [`ssh communicator`](/docs/templates/communicator.html#ssh-communicator), and can be configured as such. If you use a different builder image, you - may need to change the `ssh_username`, for example. That might look like this: + may need to change the `ssh_username`, for example. That might look like + this: - ```json + ``` json { "builders": [ { @@ -170,7 +171,7 @@ If this is set, a few more options become available. amount of memory, and other resources allocated to the builder instance. Default: `oc3`. -* `builder_upload_image_command` (string) - The command to run to upload the +- `builder_upload_image_command` (string) - The command to run to upload the image to Object Storage Classic. This is for advanced users only, and you should consult the default in code to decide on the changes to make. For most users the default should suffice. @@ -272,7 +273,7 @@ attributes file: Here is an example using a persistent volume. Note the `persistent_volume_size` setting. -```json +``` json { "variables": { "opc_username": "{{ env `OPC_USERNAME`}}", diff --git a/website/source/docs/builders/profitbricks.html.md b/website/source/docs/builders/profitbricks.html.md index 79fdb2a84..3762a9d97 100644 --- a/website/source/docs/builders/profitbricks.html.md +++ b/website/source/docs/builders/profitbricks.html.md @@ -60,7 +60,7 @@ builder. - `snapshot_password` (string) - Password for the snapshot. - `url` (string) - Endpoint for the ProfitBricks REST API. Default URL - "" + "https://api.profitbricks.com/rest/v2" ## Example diff --git a/website/source/docs/builders/scaleway.html.md b/website/source/docs/builders/scaleway.html.md index e2ce32676..f22043e0d 100644 --- a/website/source/docs/builders/scaleway.html.md +++ b/website/source/docs/builders/scaleway.html.md @@ -48,8 +48,8 @@ builder. - `image` (string) - The UUID of the base image to use. This is the image that will be used to launch a new server and provision it. See - get the complete list of the - accepted image UUID. + https://api-marketplace.scaleway.com/images + get the complete list of the accepted image UUID. - `region` (string) - The name of the region to launch the server in (`par1` or `ams1`). Consequently, this is the region where the snapshot will be diff --git a/website/source/docs/builders/triton.html.md b/website/source/docs/builders/triton.html.md index df5173022..e3b003190 100644 --- a/website/source/docs/builders/triton.html.md +++ b/website/source/docs/builders/triton.html.md @@ -30,7 +30,7 @@ This reusable image can then be used to launch new machines. The builder does *not* manage images. Once it creates an image, it is up to you to use it or delete it. -~> **Private installations of Triton must have custom images enabled!** To +\~> **Private installations of Triton must have custom images enabled!** To use the Triton builder with a private/on-prem installation of Joyent's Triton software, you'll need an operator to manually [enable custom images](https://docs.joyent.com/private-cloud/install/image-management) after diff --git a/website/source/docs/builders/vagrant.html.md b/website/source/docs/builders/vagrant.html.md new file mode 100644 index 000000000..f5d1829c0 --- /dev/null +++ b/website/source/docs/builders/vagrant.html.md @@ -0,0 +1,66 @@ +The Vagrant builder is intended for building new boxes from already-existing +boxes. Your source should be a URL or path to a .box file or a Vagrant Cloud +box name such as `hashicorp/precise64`. This builder is not currently intended +to work with an already-set-up Vagrant environment. + +Packer will not install vagrant, nor will it install the underlying +virtualization platforms or extra providers; We expect when you run this +builder that you have already installed what you need. + +Required: + +`source_box` (string) - URL of the vagrant box to use, or the name of the +vagrant box. For example, `hashicorp/precise64` or +`https://boxes.company.com/my-company.box` are valid source boxes. If using a URL like the latter example above, you will also need to provide a `box_name`. + +Optional: + +`output_dir` (string) - The directory to create that will contain +your output box. We always create this directory and run from inside of it to +prevent Vagrant init collisions. If unset, it will be set to `packer-` plus +your buildname. + +`box_name` (string) - if your source\_box is a boxfile that we need to add to +Vagrant, this is the name to give it. + +`vagrantfile_template` (string) - a path to an ERB template to use for the +vagrantfile when calling `vagrant init`. See the blog post +[here](https://www.hashicorp.com/blog/hashicorp-vagrant-2-0-2#customized-vagrantfile-templates) +for some more details on how this works. Available variables are `box_name`, +`box_url`, and `box_version`. + +`teardown_method` (string) - Whether to halt, suspend, or destroy the box when +the build has completed. Defaults to "halt" + +`box_version` (string) - What box version to use when initializing Vagrant. + +`init_minimal` (bool) - If true, will add the --minimal flag to the Vagrant +init command, creating a minimal vagrantfile instead of one filled with helpful +comments. + +`add_cacert` (string) - Equivalent to setting the +[`--cacert`](https://www.vagrantup.com/docs/cli/box.html#cacert-certfile) +option in `vagrant add`; defaults to unset. + +`add_capath` (string) - Equivalent to setting the +[`--capath`](https://www.vagrantup.com/docs/cli/box.html#capath-certdir) option +in `vagrant add`; defaults to unset. + +`add_cert` (string) - Equivalent to setting the +[`--cert`](https://www.vagrantup.com/docs/cli/box.html#cert-certfile) option in +`vagrant add`; defaults to unset. + +`add_clean` (bool) - Equivalent to setting the +[`--clean`](https://www.vagrantup.com/docs/cli/box.html#clean) flag in +`vagrant add`; defaults to unset. + +`add_force` (bool) - Equivalent to setting the +[`--force`](https://www.vagrantup.com/docs/cli/box.html#force) flag in +`vagrant add`; defaults to unset. + +`add_insecure` (bool) - Equivalent to setting the +[`--force`](https://www.vagrantup.com/docs/cli/box.html#insecure) flag in +`vagrant add`; defaults to unset. + +`skip_package` (bool) - if true, Packer will not call `vagrant package` to +package your base box into its own standalone .box file. diff --git a/website/source/docs/commands/index.html.md b/website/source/docs/commands/index.html.md index 8f71e386c..2aeecde6f 100644 --- a/website/source/docs/commands/index.html.md +++ b/website/source/docs/commands/index.html.md @@ -58,7 +58,7 @@ The format will be covered in more detail later. But as you can see, the output immediately becomes machine-friendly. Try some other commands with the `-machine-readable` flag to see! -~> The `-machine-readable` flag is designed for automated environments and +\~> The `-machine-readable` flag is designed for automated environments and is mutually-exclusive with the `-debug` flag, which is designed for interactive environments. diff --git a/website/source/docs/extending/custom-builders.html.md b/website/source/docs/extending/custom-builders.html.md index 6d1cbe03a..302401533 100644 --- a/website/source/docs/extending/custom-builders.html.md +++ b/website/source/docs/extending/custom-builders.html.md @@ -19,7 +19,7 @@ plugin interface, and this page documents how to do that. Prior to reading this page, it is assumed you have read the page on [plugin development basics](/docs/extending/plugins.html). -~> **Warning!** This is an advanced topic. If you're new to Packer, we +\~> **Warning!** This is an advanced topic. If you're new to Packer, we recommend getting a bit more comfortable before you dive into writing plugins. ## The Interface diff --git a/website/source/docs/extending/custom-post-processors.html.md b/website/source/docs/extending/custom-post-processors.html.md index b177cf0a3..0ec5f71a5 100644 --- a/website/source/docs/extending/custom-post-processors.html.md +++ b/website/source/docs/extending/custom-post-processors.html.md @@ -24,7 +24,7 @@ development basics](/docs/extending/plugins.html). Post-processor plugins implement the `packer.PostProcessor` interface and are served using the `plugin.ServePostProcessor` function. -~> **Warning!** This is an advanced topic. If you're new to Packer, we +\~> **Warning!** This is an advanced topic. If you're new to Packer, we recommend getting a bit more comfortable before you dive into writing plugins. ## The Interface diff --git a/website/source/docs/extending/custom-provisioners.html.md b/website/source/docs/extending/custom-provisioners.html.md index 75f412e4f..3d3a1721d 100644 --- a/website/source/docs/extending/custom-provisioners.html.md +++ b/website/source/docs/extending/custom-provisioners.html.md @@ -23,7 +23,7 @@ development basics](/docs/extending/plugins.html). Provisioner plugins implement the `packer.Provisioner` interface and are served using the `plugin.ServeProvisioner` function. -~> **Warning!** This is an advanced topic. If you're new to Packer, we +\~> **Warning!** This is an advanced topic. If you're new to Packer, we recommend getting a bit more comfortable before you dive into writing plugins. ## The Interface diff --git a/website/source/docs/extending/plugins.html.md b/website/source/docs/extending/plugins.html.md index 1a027c4b4..901bfc91b 100644 --- a/website/source/docs/extending/plugins.html.md +++ b/website/source/docs/extending/plugins.html.md @@ -56,7 +56,8 @@ later, it will take precedence over one found earlier. 3. The `%APPDATA%/packer.d/plugins` if `%APPDATA%` is defined (windows) -4. The `%USERPROFILE%/packer.d/plugins` if `%USERPROFILE%` is defined (windows) +4. The `%USERPROFILE%/packer.d/plugins` if `%USERPROFILE%` is defined + (windows) 5. The current working directory. @@ -83,7 +84,7 @@ assumed that you're familiar with the language. This page will not be a Go language tutorial. Thankfully, if you are familiar with Go, the Go toolchain provides many conveniences to help to develop Packer plugins. -~> **Warning!** This is an advanced topic. If you're new to Packer, we +\~> **Warning!** This is an advanced topic. If you're new to Packer, we recommend getting a bit more comfortable before you dive into writing plugins. ### Plugin System Architecture @@ -159,7 +160,7 @@ using standard installation procedures. The specifics of how to implement each type of interface are covered in the relevant subsections available in the navigation to the left. -~> **Lock your dependencies!** Using `govendor` is highly recommended since +\~> **Lock your dependencies!** Using `govendor` is highly recommended since the Packer codebase will continue to improve, potentially breaking APIs along the way until there is a stable release. By locking your dependencies, your plugins will continue to work with the version of Packer you lock to. diff --git a/website/source/docs/post-processors/alicloud-import.html.md b/website/source/docs/post-processors/alicloud-import.html.md index c421883d7..56d68f501 100644 --- a/website/source/docs/post-processors/alicloud-import.html.md +++ b/website/source/docs/post-processors/alicloud-import.html.md @@ -42,7 +42,8 @@ are two categories: required and optional parameters. - `image_name` (string) - The name of the user-defined image, \[2, 128\] English or Chinese characters. It must begin with an uppercase/lowercase letter or a Chinese character, and may contain numbers, `_` or `-`. It - cannot begin with or . + cannot begin with http:// or + https://. - `oss_bucket_name` (string) - The name of the OSS bucket where the RAW or VHD file will be copied to for import. If the Bucket isn't exist, @@ -52,7 +53,7 @@ are two categories: required and optional parameters. - `image_platform` (string) - platform such `CentOS` -- `image_architecture` (string) - Platform type of the image system:i386 \| +- `image_architecture` (string) - Platform type of the image system:i386 \\\| x86\_64 - `format` (string) - The format of the image for import, now alicloud only @@ -70,7 +71,9 @@ are two categories: required and optional parameters. - `image_description` (string) - The description of the image, with a length limit of 0 to 256 characters. Leaving it blank means null, which is the - default value. It cannot begin with or . + default value. It cannot begin with + http:// or + https://. - `image_force_delete` (boolean) - If this value is true, when the target image name is duplicated with an existing image, it will delete the @@ -79,9 +82,9 @@ are two categories: required and optional parameters. - `image_system_size` (number) - Size of the system disk, in GB, values range: - - cloud - 5 ~ 2000 - - cloud\_efficiency - 20 ~ 2048 - - cloud\_ssd - 20 ~ 2048 + - cloud - 5 \~ 2000 + - cloud\_efficiency - 20 \~ 2048 + - cloud\_ssd - 20 \~ 2048 ## Basic Example diff --git a/website/source/docs/post-processors/amazon-import.html.md b/website/source/docs/post-processors/amazon-import.html.md index 700542a40..d1dceed8c 100644 --- a/website/source/docs/post-processors/amazon-import.html.md +++ b/website/source/docs/post-processors/amazon-import.html.md @@ -14,7 +14,7 @@ Type: `amazon-import` The Packer Amazon Import post-processor takes an OVA artifact from various builders and imports it to an AMI available to Amazon Web Services EC2. -~> This post-processor is for advanced users. It depends on specific IAM +\~> This post-processor is for advanced users. It depends on specific IAM roles inside AWS and is best used with images that operate with the EC2 configuration model (eg, cloud-init for Linux systems). Please ensure you read the [prerequisites for @@ -85,12 +85,13 @@ Optional: provider whose API is compatible with aws EC2. Specify another endpoint like this `https://ec2.custom.endpoint.com`. -- `format` (string) - One of: `ova`, `raw`, `vhd`, `vhdx`, or `vmdk`. This specifies - the format of the source virtual machine image. The resulting artifact from the builder - is assumed to have a file extension matching the format. This defaults to `ova`. +- `format` (string) - One of: `ova`, `raw`, `vhd`, `vhdx`, or `vmdk`. This + specifies the format of the source virtual machine image. The resulting + artifact from the builder is assumed to have a file extension matching the + format. This defaults to `ova`. -- `insecure_skip_tls_verify` (boolean) - This allows skipping TLS verification of - the AWS EC2 endpoint. The default is `false`. +- `insecure_skip_tls_verify` (boolean) - This allows skipping TLS + verification of the AWS EC2 endpoint. The default is `false`. - `license_type` (string) - The license type to be used for the Amazon Machine Image (AMI) after importing. Valid values: `AWS` or `BYOL` diff --git a/website/source/docs/post-processors/docker-import.html.md b/website/source/docs/post-processors/docker-import.html.md index 8802a18e7..4fb3b0fe3 100644 --- a/website/source/docs/post-processors/docker-import.html.md +++ b/website/source/docs/post-processors/docker-import.html.md @@ -38,7 +38,6 @@ is optional. commit. Example of instructions are `CMD`, `ENTRYPOINT`, `ENV`, and `EXPOSE`. Example: `[ "USER ubuntu", "WORKDIR /app", "EXPOSE 8080" ]` - ## Example An example is shown below, showing only the post-processor configuration: @@ -61,9 +60,9 @@ to a registry, if you want. ## Changing Metadata Below is an example using the changes argument of the post-processor. This -feature allows the tarball metadata to be changed when imported into the -Docker environment. It is derived from the `docker import --change` command -line [option to +feature allows the tarball metadata to be changed when imported into the Docker +environment. It is derived from the `docker import --change` command line +[option to Docker](https://docs.docker.com/engine/reference/commandline/import/). Example uses of all of the options, assuming one is building an NGINX image diff --git a/website/source/docs/post-processors/googlecompute-import.html.md b/website/source/docs/post-processors/googlecompute-import.html.md index 4676dfd3c..c7ff21f09 100644 --- a/website/source/docs/post-processors/googlecompute-import.html.md +++ b/website/source/docs/post-processors/googlecompute-import.html.md @@ -14,7 +14,7 @@ Type: `googlecompute-import` The Google Compute Image Import post-processor takes a compressed raw disk image and imports it to a GCE image available to Google Compute Engine. -~> This post-processor is for advanced users. Please ensure you read the +\~> This post-processor is for advanced users. Please ensure you read the [GCE import documentation](https://cloud.google.com/compute/docs/images/import-existing-image) before using this post-processor. diff --git a/website/source/docs/post-processors/shell-local.html.md b/website/source/docs/post-processors/shell-local.html.md index 279548b10..517c094bc 100644 --- a/website/source/docs/post-processors/shell-local.html.md +++ b/website/source/docs/post-processors/shell-local.html.md @@ -60,14 +60,13 @@ Optional parameters: Packer injects some environmental variables by default into the environment, as well, which are covered in the section below. -- `env_var_format` (string) - When we parse the environment_vars that you +- `env_var_format` (string) - When we parse the environment\_vars that you provide, this gives us a string template to use in order to make sure that we are setting the environment vars correctly. By default on Windows hosts - this format is `set %s=%s && ` and on Unix, it is `%s='%s' `. You probably + this format is `set %s=%s &&` and on Unix, it is `%s='%s'`. You probably won't need to change this format, but you can see usage examples for where it is necessary below. - - `execute_command` (array of strings) - The command used to execute the script. By default this is `["/bin/sh", "-c", "{{.Vars}}", "{{.Script}}"]` on unix and `["cmd", "/c", "{{.Vars}}", "{{.Script}}"]` on windows. This is @@ -250,10 +249,10 @@ are cleaned up. For a shell script, that means the script **must** exit with a zero code. You *must* be extra careful to `exit 0` when necessary. - ## Usage Examples: ### Windows Host + Example of running a .cmd file on windows: { @@ -295,7 +294,6 @@ Contents of "example\_bash.sh": Example of running a powershell script on windows: Required customizations: env\_var\_format and execute\_command - { "type": "shell-local", "environment_vars": ["SHELLLOCALTEST=ShellTest4"], @@ -317,6 +315,7 @@ customizations: env\_var\_format, tempfile\_extension, and execute\_command } ### Unix Host + Example of running a bash script on unix: { @@ -336,19 +335,15 @@ Example of running a bash "inline" on unix: Example of running a python script on unix: -``` - { - "type": "shell-local", - "script": "hello.py", - "environment_vars": ["HELLO_USER=packeruser"], - "execute_command": ["/bin/sh", "-c", "{{.Vars}} /usr/local/bin/python {{.Script}}"] - } -``` + { + "type": "shell-local", + "script": "hello.py", + "environment_vars": ["HELLO_USER=packeruser"], + "execute_command": ["/bin/sh", "-c", "{{.Vars}} /usr/local/bin/python {{.Script}}"] + } Where "hello.py" contains: -``` -import os + import os -print('Hello, %s!' % os.getenv("HELLO_USER")) -``` \ No newline at end of file + print('Hello, %s!' % os.getenv("HELLO_USER")) diff --git a/website/source/docs/post-processors/vagrant.html.md b/website/source/docs/post-processors/vagrant.html.md index c0ccf6241..253472ed3 100644 --- a/website/source/docs/post-processors/vagrant.html.md +++ b/website/source/docs/post-processors/vagrant.html.md @@ -144,5 +144,5 @@ The following Docker input artifacts are supported: ### QEMU/libvirt -The `libvirt` provider supports QEMU artifacts built using any these accelerators: none, -kvm, tcg, or hvf. +The `libvirt` provider supports QEMU artifacts built using any these +accelerators: none, kvm, tcg, or hvf. diff --git a/website/source/docs/post-processors/vsphere-template.html.md b/website/source/docs/post-processors/vsphere-template.html.md index 9057aca14..08fbf46c9 100644 --- a/website/source/docs/post-processors/vsphere-template.html.md +++ b/website/source/docs/post-processors/vsphere-template.html.md @@ -60,16 +60,16 @@ Optional: - `insecure` (boolean) - If it's true skip verification of server certificate. Default is false - -- `snapshot_enable` (boolean) - Create a snapshot before marking as a + +- `snapshot_enable` (boolean) - Create a snapshot before marking as a template. Default is false - -- `snapshot_name` (string) - Name for the snapshot. - Required when `snapshot_enable` is `true` - -- `snapshot_description` (string) - Description for the snapshot. - Required when `snapshot_enable` is `true` - + +- `snapshot_name` (string) - Name for the snapshot. Required when + `snapshot_enable` is `true` + +- `snapshot_description` (string) - Description for the snapshot. Required + when `snapshot_enable` is `true` + ## Using the vSphere Template with local builders Once the [vSphere](/docs/post-processors/vsphere.html) takes an artifact from diff --git a/website/source/docs/provisioners/ansible-local.html.md b/website/source/docs/provisioners/ansible-local.html.md index 5972cff1d..63cd15125 100644 --- a/website/source/docs/provisioners/ansible-local.html.md +++ b/website/source/docs/provisioners/ansible-local.html.md @@ -68,6 +68,7 @@ Optional: example: + "extra_arguments": [ "--extra-vars \"Region={{user `Region`}} Stage={{user `Stage`}}\"" ] - `inventory_groups` (string) - A comma-separated list of groups to which diff --git a/website/source/docs/provisioners/breakpoint.html.md b/website/source/docs/provisioners/breakpoint.html.md index edcc48405..d251e534b 100644 --- a/website/source/docs/provisioners/breakpoint.html.md +++ b/website/source/docs/provisioners/breakpoint.html.md @@ -1,8 +1,8 @@ --- description: | - The breakpoint provisioner will pause until the user presses "enter" to - resume the build. This is intended for debugging purposes, and allows you - to halt at a particular part of the provisioning process. + The breakpoint provisioner will pause until the user presses "enter" to resume + the build. This is intended for debugging purposes, and allows you to halt at a + particular part of the provisioning process. layout: docs page_title: 'breakpoint - Provisioners' sidebar_current: 'docs-provisioners-breakpoint' @@ -12,9 +12,9 @@ sidebar_current: 'docs-provisioners-breakpoint' Type: `breakpoint` -The breakpoint provisioner will pause until the user presses "enter" to -resume the build. This is intended for debugging purposes, and allows you -to halt at a particular part of the provisioning process. +The breakpoint provisioner will pause until the user presses "enter" to resume +the build. This is intended for debugging purposes, and allows you to halt at a +particular part of the provisioning process. This is independent of the `-debug` flag, which will instead halt at every step and between every provisioner. @@ -33,8 +33,8 @@ and between every provisioner. ### Optional - `disable` (boolean) - If `true`, skip the breakpoint. Useful for when you - have set multiple breakpoints and want to toggle them off or on. - Default: `false` + have set multiple breakpoints and want to toggle them off or on. Default: + `false` - `note` (string) - a string to include explaining the purpose or location of the breakpoint. For example, you may find it useful to number your @@ -48,10 +48,8 @@ output prompting you to press "enter" to continue the build when you are ready. For example: -``` -==> docker: Pausing at breakpoint provisioner with note "foo bar baz". -==> docker: Press enter to continue. -``` + ==> docker: Pausing at breakpoint provisioner with note "foo bar baz". + ==> docker: Press enter to continue. Once you press enter, the build will resume and run normally until it either -completes or errors. \ No newline at end of file +completes or errors. diff --git a/website/source/docs/provisioners/puppet-masterless.html.md b/website/source/docs/provisioners/puppet-masterless.html.md index e28a10e7d..41ced3fde 100644 --- a/website/source/docs/provisioners/puppet-masterless.html.md +++ b/website/source/docs/provisioners/puppet-masterless.html.md @@ -73,7 +73,8 @@ Optional parameters: ] - `facter` (object of key:value strings) - Additional - [facts](https://docs.puppet.com/facter/) to make available to the Puppet run. + [facts](https://docs.puppet.com/facter/) to make available to the Puppet + run. - `guest_os_type` (string) - The remote host's OS type ('windows' or 'unix') to tailor command-line and path separators. (default: unix). @@ -88,9 +89,9 @@ Optional parameters: This is useful if your main manifest uses imports, but the directory might not contain the `manifest_file` itself. -~> `manifest_dir` is passed to Puppet as `--manifestdir` option. This option -was deprecated in puppet 3.6, and removed in puppet 4.0. If you have multiple -manifests you should use `manifest_file` instead. +\~> `manifest_dir` is passed to Puppet as `--manifestdir` option. This +option was deprecated in puppet 3.6, and removed in puppet 4.0. If you have +multiple manifests you should use `manifest_file` instead. - `module_paths` (array of strings) - Array of local module directories to be uploaded. diff --git a/website/source/docs/provisioners/shell-local.html.md b/website/source/docs/provisioners/shell-local.html.md index cf9b6cc60..95f099d92 100644 --- a/website/source/docs/provisioners/shell-local.html.md +++ b/website/source/docs/provisioners/shell-local.html.md @@ -74,10 +74,10 @@ Optional parameters: this as an environment variable. For example: `"environment_vars": "WINRMPASS={{.WinRMPassword}}"` -- `env_var_format` (string) - When we parse the environment_vars that you +- `env_var_format` (string) - When we parse the environment\_vars that you provide, this gives us a string template to use in order to make sure that we are setting the environment vars correctly. By default on Windows hosts - this format is `set %s=%s && ` and on Unix, it is `%s='%s' `. You probably + this format is `set %s=%s &&` and on Unix, it is `%s='%s'`. You probably won't need to change this format, but you can see usage examples for where it is necessary below. @@ -230,6 +230,7 @@ For a shell script, that means the script **must** exit with a zero code. You ## Usage Examples: ### Windows Host + Example of running a .cmd file on windows: { @@ -271,7 +272,6 @@ Contents of "example\_bash.sh": Example of running a powershell script on windows: Required customizations: env\_var\_format and execute\_command - { "type": "shell-local", "environment_vars": ["SHELLLOCALTEST=ShellTest4"], @@ -293,6 +293,7 @@ customizations: env\_var\_format, tempfile\_extension, and execute\_command } ### Unix Host + Example of running a bash script on unix: { @@ -312,19 +313,15 @@ Example of running a bash "inline" on unix: Example of running a python script on unix: -``` - { - "type": "shell-local", - "script": "hello.py", - "environment_vars": ["HELLO_USER=packeruser"], - "execute_command": ["/bin/sh", "-c", "{{.Vars}} /usr/local/bin/python {{.Script}}"] - } -``` + { + "type": "shell-local", + "script": "hello.py", + "environment_vars": ["HELLO_USER=packeruser"], + "execute_command": ["/bin/sh", "-c", "{{.Vars}} /usr/local/bin/python {{.Script}}"] + } Where "hello.py" contains: -``` -import os + import os -print('Hello, %s!' % os.getenv("HELLO_USER")) -``` \ No newline at end of file + print('Hello, %s!' % os.getenv("HELLO_USER")) diff --git a/website/source/docs/provisioners/windows-restart.html.md b/website/source/docs/provisioners/windows-restart.html.md index b9ee04567..bb340efc8 100644 --- a/website/source/docs/provisioners/windows-restart.html.md +++ b/website/source/docs/provisioners/windows-restart.html.md @@ -41,10 +41,10 @@ Optional parameters: - `check_registry` (bool) - if `true`, checks for several registry keys that indicate that the system is going to reboot. This is useful if an installation kicks off a reboot and you want the provisioner to wait for - that reboot to complete before reconnecting. Please note that this option is - a beta feature, and we generally recommend that you finish installs that - auto-reboot (like windows updates) during your autounattend phase before our - winrm provisioner connects. + that reboot to complete before reconnecting. Please note that this option + is a beta feature, and we generally recommend that you finish installs that + auto-reboot (like windows updates) during your autounattend phase before + our winrm provisioner connects. - `registry_keys` (array of strings) - if `check-registry` is `true`, windows-restart will not reconnect until after all of the listed keys are @@ -52,13 +52,11 @@ Optional parameters: default: - ``` - var DefaultRegistryKeys = []string{ - "HKLM:SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\RebootPending", - "HKLM:SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\PackagesPending", - "HKLM:Software\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\RebootInProgress", - } - ``` + var DefaultRegistryKeys = []string{ + "HKLM:SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\RebootPending", + "HKLM:SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\PackagesPending", + "HKLM:Software\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\RebootInProgress", + } - `restart_command` (string) - The command to execute to initiate the restart. By default this is `shutdown /r /f /t 0 /c "packer restart"`. diff --git a/website/source/docs/templates/communicator.html.md b/website/source/docs/templates/communicator.html.md index b2133f825..86267d225 100644 --- a/website/source/docs/templates/communicator.html.md +++ b/website/source/docs/templates/communicator.html.md @@ -75,9 +75,9 @@ The SSH communicator has the following options: - `ssh_bastion_port` (number) - The port of the bastion host. Defaults to `22`. -- `ssh_bastion_private_key_file` (string) - Path to a PEM encoded private - key file to use to authenticate with the bastion host. The `~` can be used - in path and will be expanded to the home directory of current user. +- `ssh_bastion_private_key_file` (string) - Path to a PEM encoded private key + file to use to authenticate with the bastion host. The `~` can be used in + path and will be expanded to the home directory of current user. - `ssh_bastion_username` (string) - The username to connect to the bastion host. @@ -155,23 +155,25 @@ Packer supports the following ciphers: - arcfour128 - arcfour256 - arcfour -- -- +- +- And the following MACs: - hmac-sha1 - hmac-sha1-96 - hmac-sha2-256 -- +- ## WinRM Communicator The WinRM communicator has the following options. - `winrm_host` (string) - The address for WinRM to connect to. - - NOTE: If using an Amazon EBS builder, you can specify the interface WinRM connects to via [`ssh_interface`](https://www.packer.io/docs/builders/amazon-ebs.html#ssh_interface) + + NOTE: If using an Amazon EBS builder, you can specify the interface WinRM + connects to via + [`ssh_interface`](https://www.packer.io/docs/builders/amazon-ebs.html#ssh_interface) - `winrm_insecure` (boolean) - If `true`, do not check server certificate chain and host name. diff --git a/website/source/docs/templates/engine.html.md b/website/source/docs/templates/engine.html.md index aae419098..1cebe5fc5 100644 --- a/website/source/docs/templates/engine.html.md +++ b/website/source/docs/templates/engine.html.md @@ -44,7 +44,8 @@ Here is a full list of the available functions for reference. reference](/docs/templates/engine.html#isotime-function-format-reference). - `lower` - Lowercases the string. - `pwd` - The working directory while executing Packer. -- `sed` - Use [a golang implementation of sed](https://github.com/rwtodd/Go.Sed) to parse an input string. +- `sed` - Use [a golang implementation of + sed](https://github.com/rwtodd/Go.Sed) to parse an input string. - `split` - Split an input string using separator and return the requested substring. - `template_dir` - The directory to the template for the build. @@ -227,6 +228,7 @@ Formatting for the function `isotime` uses the magic reference date **Mon Jan 2 + *The values in parentheses are the abbreviated, or 24-hour clock values* Note that "-0700" is always formatted into "+0000" because `isotime` is always @@ -272,10 +274,10 @@ builder in this example. The function `split` takes an input string, a seperator string, and a numeric component value and returns the requested substring. -Please note that you cannot use the `split` function on user variables, -because we can't nest the functions currently. This function is indended to -be used on builder variables like build_name. If you need a split user -variable, the best way to do it is to create a separate variable. +Please note that you cannot use the `split` function on user variables, because +we can't nest the functions currently. This function is indended to be used on +builder variables like build\_name. If you need a split user variable, the best +way to do it is to create a separate variable. Here are some examples using the above options: @@ -310,16 +312,15 @@ this case, on the `fixed-string` value): # sed Function Format Reference -See the library documentation https://github.com/rwtodd/Go.Sed for notes about -the difference between this golang implementation of sed and the regexes you may -be used to. A very simple example of this functionality: +See the library documentation +https://github.com/rwtodd/Go.Sed +for notes about the difference between this golang implementation of sed and +the regexes you may be used to. A very simple example of this functionality: -``` - "provisioners": [ - { - "type": "shell-local", - "environment_vars": ["EXAMPLE={{ sed \"s/null/awesome/\" build_type}}"], - "inline": ["echo build_type is $EXAMPLE\n"] - } - ] -``` + "provisioners": [ + { + "type": "shell-local", + "environment_vars": ["EXAMPLE={{ sed \"s/null/awesome/\" build_type}}"], + "inline": ["echo build_type is $EXAMPLE\n"] + } + ] From 52892699ca81493f0adcc28fad17f7ea322831cc Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 24 Jan 2019 16:56:57 -0800 Subject: [PATCH 02/10] make it work with a local vagrant box --- builder/vagrant/builder.go | 56 +++++++++--- builder/vagrant/step_add_box.go | 6 ++ website/source/docs/builders/vagrant.html.md | 92 +++++++++++--------- 3 files changed, 102 insertions(+), 52 deletions(-) diff --git a/builder/vagrant/builder.go b/builder/vagrant/builder.go index fa2a94a76..959ed277a 100644 --- a/builder/vagrant/builder.go +++ b/builder/vagrant/builder.go @@ -37,10 +37,13 @@ type Config struct { // This is the name of the new virtual machine. // By default this is "packer-BUILDNAME", where "BUILDNAME" is the name of the build. - OutputDir string `mapstructure:"output_dir"` - SourceBox string `mapstructure:"source_box"` - SourceBoxName string `mapstructure:"source_box_name"` - Provider string `mapstructure:"provider"` + OutputDir string `mapstructure:"output_dir"` + SourceBox string `mapstructure:"source_box"` + Checksum string `mapstructure:"checksum"` + ChecksumType string `mapstructure:"checksum_type"` + BoxName string `mapstructure:"box_name"` + + Provider string `mapstructure:"provider"` Communicator string `mapstructure:"communicator"` @@ -104,6 +107,26 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { errs = packer.MultiErrorAppend(errs, fmt.Errorf(`The Vagrant builder currently only supports the ssh communicator"`)) } + // The box isn't a namespace like you'd pull from vagrant cloud + if b.config.BoxName == "" { + b.config.BoxName = fmt.Sprintf("packer_%s", b.config.PackerBuildName) + } + + if b.config.SourceBox == "" { + errs = packer.MultiErrorAppend(errs, fmt.Errorf("source_path is required")) + } else { + if !strings.HasSuffix(b.config.SourceBox, ".box") { + b.config.SourceBox, err = common.ValidatedURL(b.config.SourceBox) + if err != nil { + errs = packer.MultiErrorAppend(errs, fmt.Errorf("source_path is invalid: %s", err)) + } + fileOK := common.FileExistsLocally(b.config.SourceBox) + if !fileOK { + packer.MultiErrorAppend(errs, + fmt.Errorf("Source file '%s' needs to exist at time of config validation!", b.config.SourceBox)) + } + } + } if b.config.TeardownMethod == "" { b.config.TeardownMethod = "destroy" @@ -147,21 +170,30 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe // Build the steps. steps := []multistep.Step{} - if !b.config.SkipPackage { - steps = append(steps, - &common.StepOutputDir{ - Force: b.config.PackerForce, - Path: b.config.OutputDir, - }) + // Download if source box isn't from vagrant cloud. + if !strings.HasSuffix(b.config.SourceBox, ".box") { + steps = append(steps, &common.StepDownload{ + Checksum: b.config.Checksum, + ChecksumType: b.config.ChecksumType, + Description: "Box", + Extension: "box", + ResultKey: "box_path", + Url: []string{b.config.SourceBox}, + }) } + steps = append(steps, + &common.StepOutputDir{ + Force: b.config.PackerForce, + Path: b.config.OutputDir, + }, &StepInitializeVagrant{ - BoxName: b.config.SourceBoxName, BoxVersion: b.config.BoxVersion, Minimal: b.config.Minimal, Template: b.config.Template, SourceBox: b.config.SourceBox, OutputDir: b.config.OutputDir, + BoxName: b.config.BoxName, }, &StepAddBox{ BoxVersion: b.config.BoxVersion, @@ -173,7 +205,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe Insecure: b.config.AddInsecure, Provider: b.config.Provider, SourceBox: b.config.SourceBox, - BoxName: b.config.SourceBoxName, + BoxName: b.config.BoxName, }, &StepUp{ b.config.TeardownMethod, diff --git a/builder/vagrant/step_add_box.go b/builder/vagrant/step_add_box.go index cdbfe3518..26ec1816c 100644 --- a/builder/vagrant/step_add_box.go +++ b/builder/vagrant/step_add_box.go @@ -2,6 +2,7 @@ package vagrant import ( "context" + "fmt" "log" "strings" @@ -25,12 +26,17 @@ type StepAddBox struct { func (s *StepAddBox) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { driver := state.Get("driver").(VagrantDriver) ui := state.Get("ui").(packer.Ui) + config := state.Get("config").(*Config) ui.Say("Adding box using vagrant box add..") addArgs := []string{} if strings.HasSuffix(s.SourceBox, ".box") { // The box isn't a namespace like you'd pull from vagrant cloud + if s.BoxName == "" { + s.BoxName = fmt.Sprintf("packer_%s", config.PackerBuildName) + } + addArgs = append(addArgs, s.BoxName) } diff --git a/website/source/docs/builders/vagrant.html.md b/website/source/docs/builders/vagrant.html.md index f5d1829c0..a7c90dded 100644 --- a/website/source/docs/builders/vagrant.html.md +++ b/website/source/docs/builders/vagrant.html.md @@ -9,58 +9,70 @@ builder that you have already installed what you need. Required: -`source_box` (string) - URL of the vagrant box to use, or the name of the -vagrant box. For example, `hashicorp/precise64` or -`https://boxes.company.com/my-company.box` are valid source boxes. If using a URL like the latter example above, you will also need to provide a `box_name`. +- `source_box` (string) - URL of the vagrant box to use, or the name of the + vagrant box. `hashicorp/precise64`, `./mylocalbox.box` and + `https://boxes.company.com/my-company.box` are all valid source boxes. If your + source is a .box file, whether locally or from a URL like the latter example + above, you will also need to provide a `box_name`. Optional: -`output_dir` (string) - The directory to create that will contain -your output box. We always create this directory and run from inside of it to -prevent Vagrant init collisions. If unset, it will be set to `packer-` plus -your buildname. +- `output_dir` (string) - The directory to create that will contain + your output box. We always create this directory and run from inside of it to + prevent Vagrant init collisions. If unset, it will be set to `packer-` plus + your buildname. -`box_name` (string) - if your source\_box is a boxfile that we need to add to -Vagrant, this is the name to give it. +- `box_name` (string) - if your source\_box is a boxfile that we need to add + to Vagrant, this is the name to give it. If left blank, will default to + "packer_" plus your buildname. -`vagrantfile_template` (string) - a path to an ERB template to use for the -vagrantfile when calling `vagrant init`. See the blog post -[here](https://www.hashicorp.com/blog/hashicorp-vagrant-2-0-2#customized-vagrantfile-templates) -for some more details on how this works. Available variables are `box_name`, -`box_url`, and `box_version`. +- `checksum` (string) - The checksum for the .box file. The type of the + checksum is specified with `checksum_type`, documented below. -`teardown_method` (string) - Whether to halt, suspend, or destroy the box when -the build has completed. Defaults to "halt" +- `checksum_type` (string) - The type of the checksum specified in `checksum`. + Valid values are `none`, `md5`, `sha1`, `sha256`, or `sha512`. Although the + checksum will not be verified when `checksum_type` is set to "none", this is + not recommended since OVA files can be very large and corruption does happen + from time to time. -`box_version` (string) - What box version to use when initializing Vagrant. +- `vagrantfile_template` (string) - a path to an ERB template to use for the + vagrantfile when calling `vagrant init`. See the blog post + [here](https://www.hashicorp.com/blog/hashicorp-vagrant-2-0-2#customized-vagrantfile-templates) + for some more details on how this works. Available variables are `box_name`, + `box_url`, and `box_version`. -`init_minimal` (bool) - If true, will add the --minimal flag to the Vagrant -init command, creating a minimal vagrantfile instead of one filled with helpful -comments. +- `teardown_method` (string) - Whether to halt, suspend, or destroy the box when + the build has completed. Defaults to "halt" -`add_cacert` (string) - Equivalent to setting the -[`--cacert`](https://www.vagrantup.com/docs/cli/box.html#cacert-certfile) -option in `vagrant add`; defaults to unset. +- `box_version` (string) - What box version to use when initializing Vagrant. -`add_capath` (string) - Equivalent to setting the -[`--capath`](https://www.vagrantup.com/docs/cli/box.html#capath-certdir) option -in `vagrant add`; defaults to unset. +- `init_minimal` (bool) - If true, will add the --minimal flag to the Vagrant + init command, creating a minimal vagrantfile instead of one filled with helpful + comments. -`add_cert` (string) - Equivalent to setting the -[`--cert`](https://www.vagrantup.com/docs/cli/box.html#cert-certfile) option in -`vagrant add`; defaults to unset. +- `add_cacert` (string) - Equivalent to setting the + [`--cacert`](https://www.vagrantup.com/docs/cli/box.html#cacert-certfile) + option in `vagrant add`; defaults to unset. -`add_clean` (bool) - Equivalent to setting the -[`--clean`](https://www.vagrantup.com/docs/cli/box.html#clean) flag in -`vagrant add`; defaults to unset. +- `add_capath` (string) - Equivalent to setting the + [`--capath`](https://www.vagrantup.com/docs/cli/box.html#capath-certdir) option + in `vagrant add`; defaults to unset. -`add_force` (bool) - Equivalent to setting the -[`--force`](https://www.vagrantup.com/docs/cli/box.html#force) flag in -`vagrant add`; defaults to unset. +- `add_cert` (string) - Equivalent to setting the + [`--cert`](https://www.vagrantup.com/docs/cli/box.html#cert-certfile) option in + `vagrant add`; defaults to unset. -`add_insecure` (bool) - Equivalent to setting the -[`--force`](https://www.vagrantup.com/docs/cli/box.html#insecure) flag in -`vagrant add`; defaults to unset. +- `add_clean` (bool) - Equivalent to setting the + [`--clean`](https://www.vagrantup.com/docs/cli/box.html#clean) flag in + `vagrant add`; defaults to unset. -`skip_package` (bool) - if true, Packer will not call `vagrant package` to -package your base box into its own standalone .box file. +- `add_force` (bool) - Equivalent to setting the + [`--force`](https://www.vagrantup.com/docs/cli/box.html#force) flag in + `vagrant add`; defaults to unset. + +- `add_insecure` (bool) - Equivalent to setting the + [`--force`](https://www.vagrantup.com/docs/cli/box.html#insecure) flag in + `vagrant add`; defaults to unset. + +- `skip_package` (bool) - if true, Packer will not call `vagrant package` to + package your base box into its own standalone .box file. From af7131b1698b78c29dc1a7c6bbc834c7b484881e Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Fri, 25 Jan 2019 12:32:44 -0800 Subject: [PATCH 03/10] tests, logic cleanup, docs for vagrant builder --- builder/vagrant/artifact.go | 2 +- builder/vagrant/artifact_test.go | 40 ++++++ builder/vagrant/builder.go | 39 ++++-- builder/vagrant/builder_test.go | 91 ++++++++++++++ builder/vagrant/driver.go | 22 +--- builder/vagrant/driver_2_2.go | 52 ++++++-- builder/vagrant/step_add_box.go | 28 +++-- builder/vagrant/step_add_box_test.go | 62 ++++++++++ builder/vagrant/step_initialize_vagrant.go | 31 +++-- .../vagrant/step_initialize_vagrant_test.go | 52 +++++++- builder/vagrant/step_package.go | 5 + builder/vagrant/step_ssh_config.go | 6 +- builder/vagrant/step_up.go | 11 +- website/source/docs/builders/amazon.html.md | 2 +- .../source/docs/builders/digitalocean.html.md | 6 +- website/source/docs/builders/docker.html.md | 12 +- .../docs/builders/googlecompute.html.md | 3 +- .../source/docs/builders/oneandone.html.md | 2 +- .../source/docs/builders/profitbricks.html.md | 2 +- website/source/docs/builders/scaleway.html.md | 2 +- website/source/docs/builders/vagrant.html.md | 115 +++++++++++------- .../post-processors/alicloud-import.html.md | 11 +- .../docs/post-processors/shell-local.html.md | 2 +- .../docs/provisioners/shell-local.html.md | 2 +- .../docs/templates/communicator.html.md | 6 +- 25 files changed, 472 insertions(+), 134 deletions(-) create mode 100644 builder/vagrant/artifact_test.go create mode 100644 builder/vagrant/builder_test.go create mode 100644 builder/vagrant/step_add_box_test.go diff --git a/builder/vagrant/artifact.go b/builder/vagrant/artifact.go index ec5ed3282..8a3c053b1 100644 --- a/builder/vagrant/artifact.go +++ b/builder/vagrant/artifact.go @@ -38,7 +38,7 @@ func (a *artifact) Id() string { } func (a *artifact) String() string { - return fmt.Sprintf("Vagrant box is %s", a.Id()) + return fmt.Sprintf("Vagrant box is %s", a.Id()) } func (a *artifact) State(name string) interface{} { diff --git a/builder/vagrant/artifact_test.go b/builder/vagrant/artifact_test.go new file mode 100644 index 000000000..794f5b1da --- /dev/null +++ b/builder/vagrant/artifact_test.go @@ -0,0 +1,40 @@ +package vagrant + +import ( + "strings" + "testing" + + "github.com/hashicorp/packer/packer" +) + +func TestArtifact_Impl(t *testing.T) { + var raw interface{} = &artifact{} + + if _, ok := raw.(packer.Artifact); !ok { + t.Fatalf("Artifact does not implement packer.Artifact") + } +} + +func TestArtifactId(t *testing.T) { + a := &artifact{ + OutputDir: "/my/dir", + BoxName: "package.box", + } + + expected := "/my/dir/package.box" + if strings.Compare(a.Id(), expected) != 0 { + t.Fatalf("artifact ID should match: expected: %s received: %s", expected, a.Id()) + } +} + +func TestArtifactString(t *testing.T) { + a := &artifact{ + OutputDir: "/my/dir", + BoxName: "package.box", + } + expected := "Vagrant box is /my/dir/package.box" + + if strings.Compare(a.String(), expected) != 0 { + t.Fatalf("artifact string should match: expected: %s received: %s", expected, a.String()) + } +} diff --git a/builder/vagrant/builder.go b/builder/vagrant/builder.go index 959ed277a..b44f4a56d 100644 --- a/builder/vagrant/builder.go +++ b/builder/vagrant/builder.go @@ -38,7 +38,8 @@ type Config struct { // This is the name of the new virtual machine. // By default this is "packer-BUILDNAME", where "BUILDNAME" is the name of the build. OutputDir string `mapstructure:"output_dir"` - SourceBox string `mapstructure:"source_box"` + SourceBox string `mapstructure:"source_path"` + GlobalID string `mapstructure:"global_id"` Checksum string `mapstructure:"checksum"` ChecksumType string `mapstructure:"checksum_type"` BoxName string `mapstructure:"box_name"` @@ -60,6 +61,7 @@ type Config struct { SyncedFolder string `mapstructure:"synced_folder"` // Options for the "vagrant box add" command + SkipAdd bool `mapstructure:"skip_add"` AddCACert string `mapstructure:"add_cacert"` AddCAPath string `mapstructure:"add_capath"` AddCert string `mapstructure:"add_cert"` @@ -113,23 +115,35 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { } if b.config.SourceBox == "" { - errs = packer.MultiErrorAppend(errs, fmt.Errorf("source_path is required")) + if b.config.GlobalID == "" { + errs = packer.MultiErrorAppend(errs, fmt.Errorf("source_path is required unless you have set global_id")) + } } else { - if !strings.HasSuffix(b.config.SourceBox, ".box") { + if b.config.GlobalID != "" { + errs = packer.MultiErrorAppend(errs, fmt.Errorf("You may either set global_id or source_path but not both")) + } + if strings.HasSuffix(b.config.SourceBox, ".box") { b.config.SourceBox, err = common.ValidatedURL(b.config.SourceBox) if err != nil { errs = packer.MultiErrorAppend(errs, fmt.Errorf("source_path is invalid: %s", err)) } fileOK := common.FileExistsLocally(b.config.SourceBox) if !fileOK { - packer.MultiErrorAppend(errs, + errs = packer.MultiErrorAppend(errs, fmt.Errorf("Source file '%s' needs to exist at time of config validation!", b.config.SourceBox)) } } } if b.config.TeardownMethod == "" { - b.config.TeardownMethod = "destroy" + // If we're using a box that's already opened on the system, don't + // automatically destroy it. If we open the box ourselves, then go ahead + // and kill it by default. + if b.config.GlobalID != "" { + b.config.TeardownMethod = "halt" + } else { + b.config.TeardownMethod = "destroy" + } } else { matches := false for _, name := range []string{"halt", "suspend", "destroy"} { @@ -171,7 +185,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe // Build the steps. steps := []multistep.Step{} // Download if source box isn't from vagrant cloud. - if !strings.HasSuffix(b.config.SourceBox, ".box") { + if strings.HasSuffix(b.config.SourceBox, ".box") { steps = append(steps, &common.StepDownload{ Checksum: b.config.Checksum, ChecksumType: b.config.ChecksumType, @@ -181,7 +195,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe Url: []string{b.config.SourceBox}, }) } - steps = append(steps, &common.StepOutputDir{ Force: b.config.PackerForce, @@ -194,6 +207,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe SourceBox: b.config.SourceBox, OutputDir: b.config.OutputDir, BoxName: b.config.BoxName, + GlobalID: b.config.GlobalID, }, &StepAddBox{ BoxVersion: b.config.BoxVersion, @@ -206,12 +220,16 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe Provider: b.config.Provider, SourceBox: b.config.SourceBox, BoxName: b.config.BoxName, + GlobalID: b.config.GlobalID, }, &StepUp{ - b.config.TeardownMethod, - b.config.Provider, + TeardownMethod: b.config.TeardownMethod, + Provider: b.config.Provider, + GlobalID: b.config.GlobalID, + }, + &StepSSHConfig{ + b.config.GlobalID, }, - &StepSSHConfig{}, &communicator.StepConnect{ Config: &b.config.SSHConfig.Comm, Host: CommHost(), @@ -222,6 +240,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe SkipPackage: b.config.SkipPackage, Include: b.config.PackageInclude, Vagrantfile: b.config.OutputVagrantfile, + GlobalID: b.config.GlobalID, }) // Run the steps. diff --git a/builder/vagrant/builder_test.go b/builder/vagrant/builder_test.go new file mode 100644 index 000000000..65c2e5e9f --- /dev/null +++ b/builder/vagrant/builder_test.go @@ -0,0 +1,91 @@ +package vagrant + +import ( + "testing" + + "github.com/hashicorp/packer/packer" +) + +func TestBuilder_ImplementsBuilder(t *testing.T) { + var raw interface{} + raw = &Builder{} + if _, ok := raw.(packer.Builder); !ok { + t.Fatalf("Builder should be a builder") + } +} + +func TestBuilder_Prepare_ValidateSource(t *testing.T) { + b := &Builder{} + type testCase struct { + config map[string]interface{} + errExpected bool + reason string + } + + cases := []testCase{ + { + config: map[string]interface{}{ + "global_id": "a3559ec", + }, + errExpected: true, + reason: "Need to set SSH communicator.", + }, + { + config: map[string]interface{}{ + "global_id": "a3559ec", + "communicator": "ssh", + }, + errExpected: false, + reason: "Shouldn't fail because we've set global_id", + }, + { + config: map[string]interface{}{ + "communicator": "ssh", + }, + errExpected: true, + reason: "Should fail because we must set source_path or global_id", + }, + { + config: map[string]interface{}{ + "source_path": "./mybox", + "communicator": "ssh", + }, + errExpected: false, + reason: "Source path is set; we should be fine", + }, + { + config: map[string]interface{}{ + "source_path": "./mybox", + "communicator": "ssh", + "global_id": "a3559ec", + }, + errExpected: true, + reason: "Both source path and global are set: we should error.", + }, + { + config: map[string]interface{}{ + "communicator": "ssh", + "global_id": "a3559ec", + "teardown_method": "suspend", + }, + errExpected: false, + reason: "Valid argument for teardown method", + }, + { + config: map[string]interface{}{ + "communicator": "ssh", + "global_id": "a3559ec", + "teardown_method": "surspernd", + }, + errExpected: true, + reason: "Inalid argument for teardown method", + }, + } + + for _, tc := range cases { + _, err := b.Prepare(tc.config) + if (err != nil) != tc.errExpected { + t.Fatalf("Unexpected behavior from test case %#v; %s.", tc.config, tc.reason) + } + } +} diff --git a/builder/vagrant/driver.go b/builder/vagrant/driver.go index 08c31b8c8..2318a0382 100644 --- a/builder/vagrant/driver.go +++ b/builder/vagrant/driver.go @@ -2,11 +2,8 @@ package vagrant import ( "fmt" - "os" "os/exec" - "path/filepath" "runtime" - "strings" ) // A driver is able to talk to Vagrant and perform certain @@ -23,15 +20,15 @@ type VagrantDriver interface { Up([]string) (string, string, error) // Calls "vagrant halt" - Halt() error + Halt(string) error // Calls "vagrant suspend" - Suspend() error + Suspend(string) error - SSHConfig() (*VagrantSSHConfig, error) + SSHConfig(string) (*VagrantSSHConfig, error) // Calls "vagrant destroy" - Destroy() error + Destroy(string) error // Calls "vagrant package"[ Package([]string) error @@ -67,14 +64,3 @@ func NewDriver() (VagrantDriver, error) { return driver, nil } - -func findVBoxManageWindows(paths string) string { - for _, path := range strings.Split(paths, ";") { - path = filepath.Join(path, "VBoxManage.exe") - if _, err := os.Stat(path); err == nil { - return path - } - } - - return "" -} diff --git a/builder/vagrant/driver_2_2.go b/builder/vagrant/driver_2_2.go index 13a87dd59..9886939b1 100644 --- a/builder/vagrant/driver_2_2.go +++ b/builder/vagrant/driver_2_2.go @@ -8,8 +8,12 @@ import ( "os/exec" "regexp" "strings" + + "github.com/hashicorp/go-version" ) +const VAGRANT_MIN_VERSION = ">= 2.0.2" + type Vagrant_2_2_Driver struct { vagrantBinary string } @@ -34,26 +38,38 @@ func (d *Vagrant_2_2_Driver) Up(args []string) (string, string, error) { } // Calls "vagrant halt" -func (d *Vagrant_2_2_Driver) Halt() error { - _, _, err := d.vagrantCmd([]string{"halt"}...) +func (d *Vagrant_2_2_Driver) Halt(id string) error { + args := []string{"halt"} + if id != "" { + args = append(args, id) + } + _, _, err := d.vagrantCmd(args...) return err } // Calls "vagrant suspend" -func (d *Vagrant_2_2_Driver) Suspend() error { - _, _, err := d.vagrantCmd([]string{"suspend"}...) +func (d *Vagrant_2_2_Driver) Suspend(id string) error { + args := []string{"suspend"} + if id != "" { + args = append(args, id) + } + _, _, err := d.vagrantCmd(args...) return err } // Calls "vagrant destroy" -func (d *Vagrant_2_2_Driver) Destroy() error { - _, _, err := d.vagrantCmd([]string{"destroy", "-f"}...) +func (d *Vagrant_2_2_Driver) Destroy(id string) error { + args := []string{"destroy", "-f"} + if id != "" { + args = append(args, id) + } + _, _, err := d.vagrantCmd(args...) return err } // Calls "vagrant package" func (d *Vagrant_2_2_Driver) Package(args []string) error { - _, _, err := d.vagrantCmd([]string{"package"}...) + _, _, err := d.vagrantCmd(append([]string{"package"}, args...)...) return err } @@ -67,6 +83,18 @@ func (d *Vagrant_2_2_Driver) Verify() error { if err != nil { return fmt.Errorf("Can't find Vagrant binary.") } + + constraints, err := version.NewConstraint(VAGRANT_MIN_VERSION) + vers, err := d.Version() + v, err := version.NewVersion(vers) + if err != nil { + return fmt.Errorf("Error figuring out Vagrant version.") + } + + if !constraints.Check(v) { + return fmt.Errorf("installed Vagrant version must be >=2.0.2") + } + return nil } @@ -99,9 +127,13 @@ func yesno(yn string) bool { return true } -func (d *Vagrant_2_2_Driver) SSHConfig() (*VagrantSSHConfig, error) { +func (d *Vagrant_2_2_Driver) SSHConfig(id string) (*VagrantSSHConfig, error) { // vagrant ssh-config --host 8df7860 - stdout, _, err := d.vagrantCmd([]string{"ssh-config"}...) + args := []string{"ssh-config"} + if id != "" { + args = append(args, id) + } + stdout, _, err := d.vagrantCmd(args...) sshConf := &VagrantSSHConfig{} lines := strings.Split(stdout, "\n") @@ -122,7 +154,7 @@ func (d *Vagrant_2_2_Driver) SSHConfig() (*VagrantSSHConfig, error) { // Version reads the version of VirtualBox that is installed. func (d *Vagrant_2_2_Driver) Version() (string, error) { - stdoutString, _, err := d.vagrantCmd([]string{"version"}...) + stdoutString, _, err := d.vagrantCmd([]string{"--version"}...) // Example stdout: // Installed Version: 2.2.3 diff --git a/builder/vagrant/step_add_box.go b/builder/vagrant/step_add_box.go index 26ec1816c..a1832969e 100644 --- a/builder/vagrant/step_add_box.go +++ b/builder/vagrant/step_add_box.go @@ -2,7 +2,6 @@ package vagrant import ( "context" - "fmt" "log" "strings" @@ -21,22 +20,14 @@ type StepAddBox struct { Provider string SourceBox string BoxName string + GlobalID string } -func (s *StepAddBox) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { - driver := state.Get("driver").(VagrantDriver) - ui := state.Get("ui").(packer.Ui) - config := state.Get("config").(*Config) +func (s *StepAddBox) generateAddArgs() []string { - ui.Say("Adding box using vagrant box add..") addArgs := []string{} if strings.HasSuffix(s.SourceBox, ".box") { - // The box isn't a namespace like you'd pull from vagrant cloud - if s.BoxName == "" { - s.BoxName = fmt.Sprintf("packer_%s", config.PackerBuildName) - } - addArgs = append(addArgs, s.BoxName) } @@ -74,6 +65,21 @@ func (s *StepAddBox) Run(_ context.Context, state multistep.StateBag) multistep. addArgs = append(addArgs, "--provider", s.Provider) } + return addArgs +} + +func (s *StepAddBox) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { + driver := state.Get("driver").(VagrantDriver) + ui := state.Get("ui").(packer.Ui) + + if s.GlobalID != "" { + ui.Say("Using a global-id; skipping Vagrant add command...") + return multistep.ActionContinue + } + + ui.Say("Adding box using vagrant box add..") + addArgs := s.generateAddArgs() + log.Printf("[vagrant] Calling box add with following args %s", strings.Join(addArgs, " ")) // Call vagrant using prepared arguments err := driver.Add(addArgs) diff --git a/builder/vagrant/step_add_box_test.go b/builder/vagrant/step_add_box_test.go new file mode 100644 index 000000000..29feca5d1 --- /dev/null +++ b/builder/vagrant/step_add_box_test.go @@ -0,0 +1,62 @@ +package vagrant + +import ( + "strings" + "testing" + + "github.com/hashicorp/packer/helper/multistep" +) + +func TestStepAdd_Impl(t *testing.T) { + var raw interface{} + raw = new(StepAddBox) + if _, ok := raw.(multistep.Step); !ok { + t.Fatalf("initialize should be a step") + } +} + +func TestPrepAddArgs(t *testing.T) { + type testArgs struct { + Step StepAddBox + Expected []string + } + addTests := []testArgs{ + { + Step: StepAddBox{ + SourceBox: "my_source_box.box", + BoxName: "AWESOME BOX", + }, + Expected: []string{"AWESOME BOX", "my_source_box.box"}, + }, + { + Step: StepAddBox{ + SourceBox: "my_source_box", + BoxName: "AWESOME BOX", + }, + Expected: []string{"my_source_box"}, + }, + { + Step: StepAddBox{ + BoxVersion: "eleventyone", + CACert: "adfasdf", + CAPath: "adfasdf", + DownloadCert: "adfasdf", + Clean: true, + Force: true, + Insecure: true, + Provider: "virtualbox", + SourceBox: "bananabox.box", + BoxName: "bananas", + }, + Expected: []string{"bananas", "bananabox.box", "--box-version", "eleventyone", "--cacert", "adfasdf", "--capath", "adfasdf", "--cert", "adfasdf", "--clean", "--force", "--insecure", "--provider", "virtualbox"}, + }, + } + for _, addTest := range addTests { + addArgs := addTest.Step.generateAddArgs() + for i, val := range addTest.Expected { + if strings.Compare(addArgs[i], val) != 0 { + t.Fatalf("expected %#v but received %#v", addTest.Expected, addArgs) + } + } + } +} diff --git a/builder/vagrant/step_initialize_vagrant.go b/builder/vagrant/step_initialize_vagrant.go index 0f6357a48..43a824090 100644 --- a/builder/vagrant/step_initialize_vagrant.go +++ b/builder/vagrant/step_initialize_vagrant.go @@ -19,6 +19,7 @@ type StepInitializeVagrant struct { SourceBox string OutputDir string SyncedFolder string + GlobalID string } var DEFAULT_TEMPLATE = `Vagrant.configure("2") do |config| @@ -73,12 +74,8 @@ func (s *StepInitializeVagrant) createInitializeCommand() (string, error) { return abspath, nil } -func (s *StepInitializeVagrant) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { - driver := state.Get("driver").(VagrantDriver) - ui := state.Get("ui").(packer.Ui) - - ui.Say("Initializing Vagrant in build directory...") +func (s *StepInitializeVagrant) prepInitArgs() ([]string, error) { // Prepare arguments initArgs := []string{} @@ -98,12 +95,32 @@ func (s *StepInitializeVagrant) Run(_ context.Context, state multistep.StateBag) tplPath, err := s.createInitializeCommand() if err != nil { - state.Put("error", err) - return multistep.ActionHalt + return initArgs, err } initArgs = append(initArgs, "--template", tplPath) + return initArgs, nil +} + +func (s *StepInitializeVagrant) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { + driver := state.Get("driver").(VagrantDriver) + ui := state.Get("ui").(packer.Ui) + + // Skip the initialize step if we're trying to launch from a global ID. + if s.GlobalID != "" { + ui.Say("Using a global-id; skipping Vagrant init in this directory...") + return multistep.ActionContinue + } + + ui.Say("Initializing Vagrant in build directory...") + + initArgs, err := s.prepInitArgs() + if err != nil { + state.Put("error", err) + return multistep.ActionHalt + } + os.Chdir(s.OutputDir) // Call vagrant using prepared arguments err = driver.Init(initArgs) diff --git a/builder/vagrant/step_initialize_vagrant_test.go b/builder/vagrant/step_initialize_vagrant_test.go index 158ec34af..9f91b0631 100644 --- a/builder/vagrant/step_initialize_vagrant_test.go +++ b/builder/vagrant/step_initialize_vagrant_test.go @@ -44,6 +44,7 @@ func TestCreateFile_customSync(t *testing.T) { SyncedFolder: "myfolder/foldertimes", } templatePath, err := testy.createInitializeCommand() + defer os.Remove(templatePath) if err != nil { t.Fatalf(err.Error()) } @@ -56,5 +57,54 @@ end` if ok := strings.Compare(actual, expected); ok != 0 { t.Fatalf("EXPECTED: \n%s\n\n RECEIVED: \n%s\n\n", expected, actual) } - os.Remove(templatePath) +} + +func TestPrepInitArgs(t *testing.T) { + type testArgs struct { + Step StepInitializeVagrant + Expected []string + } + initTests := []testArgs{ + { + Step: StepInitializeVagrant{ + SourceBox: "my_source_box.box", + }, + Expected: []string{"my_source_box.box", "--template"}, + }, + { + Step: StepInitializeVagrant{ + SourceBox: "my_source_box", + BoxName: "My Box", + }, + Expected: []string{"My Box", "my_source_box", "--template"}, + }, + { + Step: StepInitializeVagrant{ + SourceBox: "my_source_box", + BoxName: "My Box", + BoxVersion: "42", + }, + Expected: []string{"My Box", "my_source_box", "--box-version", "42", "--template"}, + }, + { + Step: StepInitializeVagrant{ + SourceBox: "my_source_box", + BoxName: "My Box", + Minimal: true, + }, + Expected: []string{"My Box", "my_source_box", "-m", "--template"}, + }, + } + for _, initTest := range initTests { + initArgs, err := initTest.Step.prepInitArgs() + defer os.Remove(initArgs[len(initArgs)-1]) + if err != nil { + t.Fatalf(err.Error()) + } + for i, val := range initTest.Expected { + if strings.Compare(initArgs[i], val) != 0 { + t.Fatalf("expected %#v but received %#v", initTest.Expected, initArgs[:len(initArgs)-1]) + } + } + } } diff --git a/builder/vagrant/step_package.go b/builder/vagrant/step_package.go index d61b5c1ce..8200296b4 100644 --- a/builder/vagrant/step_package.go +++ b/builder/vagrant/step_package.go @@ -12,6 +12,7 @@ type StepPackage struct { SkipPackage bool Include []string Vagrantfile string + GlobalID string } func (s *StepPackage) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { @@ -24,6 +25,10 @@ func (s *StepPackage) Run(_ context.Context, state multistep.StateBag) multistep } ui.Say("Packaging box...") packageArgs := []string{} + if s.GlobalID != "" { + packageArgs = append(packageArgs, s.GlobalID) + } + if len(s.Include) > 0 { packageArgs = append(packageArgs, "--include", strings.Join(s.Include, ",")) } diff --git a/builder/vagrant/step_ssh_config.go b/builder/vagrant/step_ssh_config.go index 175224727..68fc13aa3 100644 --- a/builder/vagrant/step_ssh_config.go +++ b/builder/vagrant/step_ssh_config.go @@ -22,13 +22,15 @@ import ( // IdentitiesOnly yes // LogLevel FATAL -type StepSSHConfig struct{} +type StepSSHConfig struct { + GlobalID string +} func (s *StepSSHConfig) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { driver := state.Get("driver").(VagrantDriver) config := state.Get("config").(*Config) - sshConfig, err := driver.SSHConfig() + sshConfig, err := driver.SSHConfig(s.GlobalID) if err != nil { state.Put("error", err) return multistep.ActionHalt diff --git a/builder/vagrant/step_up.go b/builder/vagrant/step_up.go index 93aad8b63..6f2584c5f 100644 --- a/builder/vagrant/step_up.go +++ b/builder/vagrant/step_up.go @@ -11,6 +11,7 @@ import ( type StepUp struct { TeardownMethod string Provider string + GlobalID string } func (s *StepUp) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { @@ -20,6 +21,10 @@ func (s *StepUp) Run(_ context.Context, state multistep.StateBag) multistep.Step ui.Say("Calling Vagrant Up...") var args []string + if s.GlobalID != "" { + args = append(args, s.GlobalID) + } + if s.Provider != "" { args = append(args, fmt.Sprintf("--provider=%s", s.Provider)) } @@ -42,11 +47,11 @@ func (s *StepUp) Cleanup(state multistep.StateBag) { var err error if s.TeardownMethod == "halt" { - err = driver.Halt() + err = driver.Halt(s.GlobalID) } else if s.TeardownMethod == "suspend" { - err = driver.Suspend() + err = driver.Suspend(s.GlobalID) } else if s.TeardownMethod == "destroy" { - err = driver.Destroy() + err = driver.Destroy(s.GlobalID) } else { // Should never get here because of template validation state.Put("error", fmt.Errorf("Invalid teardown method selected; must be either halt, suspend, or destory.")) diff --git a/website/source/docs/builders/amazon.html.md b/website/source/docs/builders/amazon.html.md index f722cd7d3..9e3fb1ba9 100644 --- a/website/source/docs/builders/amazon.html.md +++ b/website/source/docs/builders/amazon.html.md @@ -218,7 +218,7 @@ fail. If that's the case, you might see an error like this: ==> amazon-ebs: Error querying AMI: AuthFailure: AWS was not able to validate the provided access credentials If you suspect your system's date is wrong, you can compare it against -http://www.time.gov/. On +. On Linux/OS X, you can run the `date` command to get the current time. If you're on Linux, you can try setting the time with ntp by running `sudo ntpd -q`. diff --git a/website/source/docs/builders/digitalocean.html.md b/website/source/docs/builders/digitalocean.html.md index b7a4f298d..1d382869d 100644 --- a/website/source/docs/builders/digitalocean.html.md +++ b/website/source/docs/builders/digitalocean.html.md @@ -41,17 +41,17 @@ builder. - `image` (string) - The name (or slug) of the base image to use. This is the image that will be used to launch a new droplet and provision it. See - https://developers.digitalocean.com/documentation/v2/\#list-all-images + https://developers.digitalocean.com/documentation/v2/#list-all-images for details on how to get a list of the accepted image names/slugs. - `region` (string) - The name (or slug) of the region to launch the droplet in. Consequently, this is the region where the snapshot will be available. See - https://developers.digitalocean.com/documentation/v2/\#list-all-regions + https://developers.digitalocean.com/documentation/v2/#list-all-regions for the accepted region names/slugs. - `size` (string) - The name (or slug) of the droplet size to use. See - https://developers.digitalocean.com/documentation/v2/\#list-all-sizes + https://developers.digitalocean.com/documentation/v2/#list-all-sizes for the accepted size names/slugs. ### Optional: diff --git a/website/source/docs/builders/docker.html.md b/website/source/docs/builders/docker.html.md index e5e1879ba..a0dddfc78 100644 --- a/website/source/docs/builders/docker.html.md +++ b/website/source/docs/builders/docker.html.md @@ -184,10 +184,10 @@ You must specify (only) one of `commit`, `discard`, or `export_path`. `login_username`, and `login_password` will be ignored. For more information see the [section on ECR](#amazon-ec2-container-registry). -- `exec_user` (string) - Username or UID (format: - <name\\\\\|uid>\[:<group\\\\\|gid>\]) to run remote commands - with. You may need this if you get permission errors trying to run the - `shell` or other provisioners. +- `exec_user` (string) - Username (UID) to run remote commands with. You can + also set the group name/ID if you want: (UID or UID:GID). + You may need this if you get permission errors trying to run the `shell` or + other provisioners. - `login` (boolean) - Defaults to false. If true, the builder will login in order to pull the image. The builder only logs in for the duration of the @@ -380,8 +380,8 @@ portable provisioning scripts. ## Overriding the host directory -By default, Packer creates a temporary folder under your home directory, and -uses that to stage files for uploading into the container. If you would like to +By default, Packer creates a temporary folder under your home directory, and +uses that to stage files for uploading into the container. If you would like to change the path to this temporary folder, you can set the `PACKER_TMP_DIR`. This can be useful, for example, if you have your home directory permissions set up to disallow access from the docker daemon. diff --git a/website/source/docs/builders/googlecompute.html.md b/website/source/docs/builders/googlecompute.html.md index 24a83b05f..681fad15e 100644 --- a/website/source/docs/builders/googlecompute.html.md +++ b/website/source/docs/builders/googlecompute.html.md @@ -18,8 +18,7 @@ based on existing images. It is possible to build images from scratch, but not with the `googlecompute` Packer builder. The process is recommended only for advanced users, please see -\[Building GCE Images from Scratch\] -(https://cloud.google.com/compute/docs/tutorials/building-images) +[Building GCE Images from Scratch](https://cloud.google.com/compute/docs/tutorials/building-images) and the [Google Compute Import Post-Processor](/docs/post-processors/googlecompute-import.html) for more information. diff --git a/website/source/docs/builders/oneandone.html.md b/website/source/docs/builders/oneandone.html.md index 5b9007823..dca56dc85 100644 --- a/website/source/docs/builders/oneandone.html.md +++ b/website/source/docs/builders/oneandone.html.md @@ -44,7 +44,7 @@ builder. while waiting for the build to complete. Default value "600". - `url` (string) - Endpoint for the 1&1 REST API. Default URL - "https://cloudpanel-api.1and1.com/v1" + "" ## Example diff --git a/website/source/docs/builders/profitbricks.html.md b/website/source/docs/builders/profitbricks.html.md index 3762a9d97..79fdb2a84 100644 --- a/website/source/docs/builders/profitbricks.html.md +++ b/website/source/docs/builders/profitbricks.html.md @@ -60,7 +60,7 @@ builder. - `snapshot_password` (string) - Password for the snapshot. - `url` (string) - Endpoint for the ProfitBricks REST API. Default URL - "https://api.profitbricks.com/rest/v2" + "" ## Example diff --git a/website/source/docs/builders/scaleway.html.md b/website/source/docs/builders/scaleway.html.md index f22043e0d..4678b4222 100644 --- a/website/source/docs/builders/scaleway.html.md +++ b/website/source/docs/builders/scaleway.html.md @@ -48,7 +48,7 @@ builder. - `image` (string) - The UUID of the base image to use. This is the image that will be used to launch a new server and provision it. See - https://api-marketplace.scaleway.com/images + [the images list](https://api-marketplace.scaleway.com/images) get the complete list of the accepted image UUID. - `region` (string) - The name of the region to launch the server in (`par1` diff --git a/website/source/docs/builders/vagrant.html.md b/website/source/docs/builders/vagrant.html.md index a7c90dded..2782975d0 100644 --- a/website/source/docs/builders/vagrant.html.md +++ b/website/source/docs/builders/vagrant.html.md @@ -1,30 +1,53 @@ The Vagrant builder is intended for building new boxes from already-existing boxes. Your source should be a URL or path to a .box file or a Vagrant Cloud -box name such as `hashicorp/precise64`. This builder is not currently intended -to work with an already-set-up Vagrant environment. +box name such as `hashicorp/precise64`. Packer will not install vagrant, nor will it install the underlying virtualization platforms or extra providers; We expect when you run this builder that you have already installed what you need. +By default, this builder will initialize a new Vagrant workspace, launch your +box from that workspace, provision it, call `vagrant package` to package it +into a new box, and then destroy the original box. Please note that vagrant +will _not_ remove the box file from your system (we don't call +`vagrant box remove`). + +You can change the behavior so that the builder doesn't destroy the box by +setting the `teardown_method` option. You can change the behavior so the builder +doesn't package it (not all provisioners support the `vagrant package` command) +by setting the `skip package` option. You can also change the behavior so that +rather than inititalizing a new Vagrant workspace, you use an already defined +one, by using `global_id` instead of `source_box`. + Required: -- `source_box` (string) - URL of the vagrant box to use, or the name of the - vagrant box. `hashicorp/precise64`, `./mylocalbox.box` and - `https://boxes.company.com/my-company.box` are all valid source boxes. If your - source is a .box file, whether locally or from a URL like the latter example - above, you will also need to provide a `box_name`. +- `source_box` (string) - URL of the vagrant box to use, or the name of the + vagrant box. `hashicorp/precise64`, `./mylocalbox.box` and + `https://example.com/my-box.box` are all valid source boxes. If your + source is a .box file, whether locally or from a URL like the latter example + above, you will also need to provide a `box_name`. This option is required, + unless you set `global_id`. You may only set one or the other, not both. + + or + +- `global_id` (string) - the global id of a Vagrant box already added to Vagrant + on your system. You can find the global id of your Vagrant boxes using the + command `vagrant global-status`; your global_id will be a 7-digit number and + letter comination that you'll find in the leftmost column of the + global-status output. If you choose to use `global_id` instead of + `source_box`, Packer will skip the Vagrant initialize and add steps, and + simply launch the box directly using the global id. Optional: -- `output_dir` (string) - The directory to create that will contain - your output box. We always create this directory and run from inside of it to - prevent Vagrant init collisions. If unset, it will be set to `packer-` plus - your buildname. +- `output_dir` (string) - The directory to create that will contain + your output box. We always create this directory and run from inside of it to + prevent Vagrant init collisions. If unset, it will be set to `packer-` plus + your buildname. - `box_name` (string) - if your source\_box is a boxfile that we need to add - to Vagrant, this is the name to give it. If left blank, will default to - "packer_" plus your buildname. + to Vagrant, this is the name to give it. If left blank, will default to + "packer_" plus your buildname. - `checksum` (string) - The checksum for the .box file. The type of the checksum is specified with `checksum_type`, documented below. @@ -35,44 +58,48 @@ Optional: not recommended since OVA files can be very large and corruption does happen from time to time. -- `vagrantfile_template` (string) - a path to an ERB template to use for the - vagrantfile when calling `vagrant init`. See the blog post - [here](https://www.hashicorp.com/blog/hashicorp-vagrant-2-0-2#customized-vagrantfile-templates) - for some more details on how this works. Available variables are `box_name`, - `box_url`, and `box_version`. +- `vagrantfile_template` (string) - a path to an ERB template to use for the + vagrantfile when calling `vagrant init`. See the blog post + [here](https://www.hashicorp.com/blog/hashicorp-vagrant-2-0-2#customized-vagrantfile-templates) + for some more details on how this works. Available variables are `box_name`, + `box_url`, and `box_version`. -- `teardown_method` (string) - Whether to halt, suspend, or destroy the box when - the build has completed. Defaults to "halt" +- `skip_add` (string) - Don't call "vagrant add" to add the box to your local + environment; this is necesasry if you want to launch a box that is already + added to your vagrant environment. -- `box_version` (string) - What box version to use when initializing Vagrant. +- `teardown_method` (string) - Whether to halt, suspend, or destroy the box when + the build has completed. Defaults to "halt" -- `init_minimal` (bool) - If true, will add the --minimal flag to the Vagrant - init command, creating a minimal vagrantfile instead of one filled with helpful - comments. +- `box_version` (string) - What box version to use when initializing Vagrant. -- `add_cacert` (string) - Equivalent to setting the - [`--cacert`](https://www.vagrantup.com/docs/cli/box.html#cacert-certfile) - option in `vagrant add`; defaults to unset. +- `init_minimal` (bool) - If true, will add the --minimal flag to the Vagrant + init command, creating a minimal vagrantfile instead of one filled with helpful + comments. -- `add_capath` (string) - Equivalent to setting the - [`--capath`](https://www.vagrantup.com/docs/cli/box.html#capath-certdir) option - in `vagrant add`; defaults to unset. +- `add_cacert` (string) - Equivalent to setting the + [`--cacert`](https://www.vagrantup.com/docs/cli/box.html#cacert-certfile) + option in `vagrant add`; defaults to unset. -- `add_cert` (string) - Equivalent to setting the - [`--cert`](https://www.vagrantup.com/docs/cli/box.html#cert-certfile) option in - `vagrant add`; defaults to unset. +- `add_capath` (string) - Equivalent to setting the + [`--capath`](https://www.vagrantup.com/docs/cli/box.html#capath-certdir) option + in `vagrant add`; defaults to unset. -- `add_clean` (bool) - Equivalent to setting the - [`--clean`](https://www.vagrantup.com/docs/cli/box.html#clean) flag in - `vagrant add`; defaults to unset. +- `add_cert` (string) - Equivalent to setting the + [`--cert`](https://www.vagrantup.com/docs/cli/box.html#cert-certfile) option in + `vagrant add`; defaults to unset. -- `add_force` (bool) - Equivalent to setting the - [`--force`](https://www.vagrantup.com/docs/cli/box.html#force) flag in - `vagrant add`; defaults to unset. +- `add_clean` (bool) - Equivalent to setting the + [`--clean`](https://www.vagrantup.com/docs/cli/box.html#clean) flag in + `vagrant add`; defaults to unset. -- `add_insecure` (bool) - Equivalent to setting the - [`--force`](https://www.vagrantup.com/docs/cli/box.html#insecure) flag in - `vagrant add`; defaults to unset. +- `add_force` (bool) - Equivalent to setting the + [`--force`](https://www.vagrantup.com/docs/cli/box.html#force) flag in + `vagrant add`; defaults to unset. -- `skip_package` (bool) - if true, Packer will not call `vagrant package` to - package your base box into its own standalone .box file. +- `add_insecure` (bool) - Equivalent to setting the + [`--force`](https://www.vagrantup.com/docs/cli/box.html#insecure) flag in + `vagrant add`; defaults to unset. + +- `skip_package` (bool) - if true, Packer will not call `vagrant package` to + package your base box into its own standalone .box file. diff --git a/website/source/docs/post-processors/alicloud-import.html.md b/website/source/docs/post-processors/alicloud-import.html.md index 56d68f501..7f26c42d7 100644 --- a/website/source/docs/post-processors/alicloud-import.html.md +++ b/website/source/docs/post-processors/alicloud-import.html.md @@ -42,8 +42,7 @@ are two categories: required and optional parameters. - `image_name` (string) - The name of the user-defined image, \[2, 128\] English or Chinese characters. It must begin with an uppercase/lowercase letter or a Chinese character, and may contain numbers, `_` or `-`. It - cannot begin with http:// or - https://. + cannot begin with `http://` or `https://` - `oss_bucket_name` (string) - The name of the OSS bucket where the RAW or VHD file will be copied to for import. If the Bucket isn't exist, @@ -53,8 +52,8 @@ are two categories: required and optional parameters. - `image_platform` (string) - platform such `CentOS` -- `image_architecture` (string) - Platform type of the image system:i386 \\\| - x86\_64 +- `image_architecture` (string) - Platform type of the image system: `i386` or + `x86_64` - `format` (string) - The format of the image for import, now alicloud only support RAW and VHD. @@ -71,9 +70,7 @@ are two categories: required and optional parameters. - `image_description` (string) - The description of the image, with a length limit of 0 to 256 characters. Leaving it blank means null, which is the - default value. It cannot begin with - http:// or - https://. + default value. It cannot begin with `http://` or `https://`. - `image_force_delete` (boolean) - If this value is true, when the target image name is duplicated with an existing image, it will delete the diff --git a/website/source/docs/post-processors/shell-local.html.md b/website/source/docs/post-processors/shell-local.html.md index 517c094bc..7599febf2 100644 --- a/website/source/docs/post-processors/shell-local.html.md +++ b/website/source/docs/post-processors/shell-local.html.md @@ -63,7 +63,7 @@ Optional parameters: - `env_var_format` (string) - When we parse the environment\_vars that you provide, this gives us a string template to use in order to make sure that we are setting the environment vars correctly. By default on Windows hosts - this format is `set %s=%s &&` and on Unix, it is `%s='%s'`. You probably + this format is `set %s=%s && ` and on Unix, it is `%s='%s' `. You probably won't need to change this format, but you can see usage examples for where it is necessary below. diff --git a/website/source/docs/provisioners/shell-local.html.md b/website/source/docs/provisioners/shell-local.html.md index 95f099d92..3dc81fe66 100644 --- a/website/source/docs/provisioners/shell-local.html.md +++ b/website/source/docs/provisioners/shell-local.html.md @@ -77,7 +77,7 @@ Optional parameters: - `env_var_format` (string) - When we parse the environment\_vars that you provide, this gives us a string template to use in order to make sure that we are setting the environment vars correctly. By default on Windows hosts - this format is `set %s=%s &&` and on Unix, it is `%s='%s'`. You probably + this format is `set %s=%s && ` and on Unix, it is `%s='%s' `. You probably won't need to change this format, but you can see usage examples for where it is necessary below. diff --git a/website/source/docs/templates/communicator.html.md b/website/source/docs/templates/communicator.html.md index 86267d225..c8054f623 100644 --- a/website/source/docs/templates/communicator.html.md +++ b/website/source/docs/templates/communicator.html.md @@ -155,15 +155,15 @@ Packer supports the following ciphers: - arcfour128 - arcfour256 - arcfour -- -- +- `es128-gcm@openssh.com` +- `acha20-poly1305@openssh.com` And the following MACs: - hmac-sha1 - hmac-sha1-96 - hmac-sha2-256 -- +- `hmac-sha2-256-etm@openssh.com` ## WinRM Communicator From a7d9d62996ed9c39a12196eab662334cf8a2b3c5 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Mon, 4 Feb 2019 16:11:25 -0800 Subject: [PATCH 04/10] fix tests on windows --- builder/vagrant/artifact_test.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/builder/vagrant/artifact_test.go b/builder/vagrant/artifact_test.go index 794f5b1da..63e1db1ec 100644 --- a/builder/vagrant/artifact_test.go +++ b/builder/vagrant/artifact_test.go @@ -1,6 +1,7 @@ package vagrant import ( + "runtime" "strings" "testing" @@ -22,6 +23,9 @@ func TestArtifactId(t *testing.T) { } expected := "/my/dir/package.box" + if runtime.GOOS == "windows" { + expected = strings.Replace(expected, "/", "\\", -1) + } if strings.Compare(a.Id(), expected) != 0 { t.Fatalf("artifact ID should match: expected: %s received: %s", expected, a.Id()) } @@ -33,6 +37,9 @@ func TestArtifactString(t *testing.T) { BoxName: "package.box", } expected := "Vagrant box is /my/dir/package.box" + if runtime.GOOS == "windows" { + expected = strings.Replace(expected, "/", "\\", -1) + } if strings.Compare(a.String(), expected) != 0 { t.Fatalf("artifact string should match: expected: %s received: %s", expected, a.String()) From e8c655bf157867f8403776ce00e8a17ebd471dfa Mon Sep 17 00:00:00 2001 From: Adrien Delorme Date: Tue, 5 Feb 2019 09:31:38 -0800 Subject: [PATCH 05/10] Update website/source/docs/builders/vagrant.html.md Co-Authored-By: SwampDragons --- website/source/docs/builders/vagrant.html.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/builders/vagrant.html.md b/website/source/docs/builders/vagrant.html.md index 2782975d0..ae661629f 100644 --- a/website/source/docs/builders/vagrant.html.md +++ b/website/source/docs/builders/vagrant.html.md @@ -21,7 +21,7 @@ one, by using `global_id` instead of `source_box`. Required: -- `source_box` (string) - URL of the vagrant box to use, or the name of the +- `source_path` (string) - URL of the vagrant box to use, or the name of the vagrant box. `hashicorp/precise64`, `./mylocalbox.box` and `https://example.com/my-box.box` are all valid source boxes. If your source is a .box file, whether locally or from a URL like the latter example From 608b7cb7a32dff09a9400ec82217855df8f0b38f Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Tue, 5 Feb 2019 15:16:47 -0800 Subject: [PATCH 06/10] review comments --- builder/vagrant/step_initialize_vagrant.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/builder/vagrant/step_initialize_vagrant.go b/builder/vagrant/step_initialize_vagrant.go index 43a824090..51b4710f6 100644 --- a/builder/vagrant/step_initialize_vagrant.go +++ b/builder/vagrant/step_initialize_vagrant.go @@ -36,10 +36,9 @@ type VagrantfileOptions struct { BoxName string } -func (s *StepInitializeVagrant) createInitializeCommand() (string, error) { +func (s *StepInitializeVagrant) getVagrantfileTemplate() (string, error) { tplPath := filepath.Join(s.OutputDir, "packer-vagrantfile-template.erb") templateFile, err := os.Create(tplPath) - templateFile.Chmod(0777) if err != nil { retErr := fmt.Errorf("Error creating vagrantfile %s", err.Error()) return "", retErr @@ -93,7 +92,7 @@ func (s *StepInitializeVagrant) prepInitArgs() ([]string, error) { initArgs = append(initArgs, "-m") } - tplPath, err := s.createInitializeCommand() + tplPath, err := s.getVagrantfileTemplate() if err != nil { return initArgs, err } From e56d7f723420946b37620706096ac5224829426e Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Tue, 5 Feb 2019 15:34:59 -0800 Subject: [PATCH 07/10] fix tests --- builder/vagrant/step_initialize_vagrant_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builder/vagrant/step_initialize_vagrant_test.go b/builder/vagrant/step_initialize_vagrant_test.go index 9f91b0631..4df7284f4 100644 --- a/builder/vagrant/step_initialize_vagrant_test.go +++ b/builder/vagrant/step_initialize_vagrant_test.go @@ -22,7 +22,7 @@ func TestCreateFile(t *testing.T) { OutputDir: "./", SourceBox: "bananas", } - templatePath, err := testy.createInitializeCommand() + templatePath, err := testy.getVagrantfileTemplate() if err != nil { t.Fatalf(err.Error()) } @@ -43,7 +43,7 @@ func TestCreateFile_customSync(t *testing.T) { OutputDir: "./", SyncedFolder: "myfolder/foldertimes", } - templatePath, err := testy.createInitializeCommand() + templatePath, err := testy.getVagrantfileTemplate() defer os.Remove(templatePath) if err != nil { t.Fatalf(err.Error()) From dc848ea5d75ed896ed08599560d691a5a3626c59 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 7 Feb 2019 12:39:56 -0800 Subject: [PATCH 08/10] just make vagrantfile instead of calling init --- builder/vagrant/builder.go | 15 +-- ..._vagrant.go => step_create_vagrantfile.go} | 55 ++------- .../vagrant/step_create_vagrantfile_test.go | 60 ++++++++++ .../vagrant/step_initialize_vagrant_test.go | 110 ------------------ website/source/docs/builders/vagrant.html.md | 14 +-- 5 files changed, 80 insertions(+), 174 deletions(-) rename builder/vagrant/{step_initialize_vagrant.go => step_create_vagrantfile.go} (60%) create mode 100644 builder/vagrant/step_create_vagrantfile_test.go delete mode 100644 builder/vagrant/step_initialize_vagrant_test.go diff --git a/builder/vagrant/builder.go b/builder/vagrant/builder.go index b44f4a56d..d14f6df8e 100644 --- a/builder/vagrant/builder.go +++ b/builder/vagrant/builder.go @@ -56,7 +56,6 @@ type Config struct { // Options for the "vagrant init" command BoxVersion string `mapstructure:"box_version"` - Minimal bool `mapstructure:"init_minimal"` Template string `mapstructure:"template"` SyncedFolder string `mapstructure:"synced_folder"` @@ -200,14 +199,12 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe Force: b.config.PackerForce, Path: b.config.OutputDir, }, - &StepInitializeVagrant{ - BoxVersion: b.config.BoxVersion, - Minimal: b.config.Minimal, - Template: b.config.Template, - SourceBox: b.config.SourceBox, - OutputDir: b.config.OutputDir, - BoxName: b.config.BoxName, - GlobalID: b.config.GlobalID, + &StepCreateVagrantfile{ + Template: b.config.Template, + SyncedFolder: b.config.SyncedFolder, + SourceBox: b.config.SourceBox, + OutputDir: b.config.OutputDir, + GlobalID: b.config.GlobalID, }, &StepAddBox{ BoxVersion: b.config.BoxVersion, diff --git a/builder/vagrant/step_initialize_vagrant.go b/builder/vagrant/step_create_vagrantfile.go similarity index 60% rename from builder/vagrant/step_initialize_vagrant.go rename to builder/vagrant/step_create_vagrantfile.go index 51b4710f6..375b9b423 100644 --- a/builder/vagrant/step_initialize_vagrant.go +++ b/builder/vagrant/step_create_vagrantfile.go @@ -3,6 +3,7 @@ package vagrant import ( "context" "fmt" + "log" "os" "path/filepath" "text/template" @@ -11,10 +12,7 @@ import ( "github.com/hashicorp/packer/packer" ) -type StepInitializeVagrant struct { - BoxName string - BoxVersion string - Minimal bool +type StepCreateVagrantfile struct { Template string SourceBox string OutputDir string @@ -36,8 +34,8 @@ type VagrantfileOptions struct { BoxName string } -func (s *StepInitializeVagrant) getVagrantfileTemplate() (string, error) { - tplPath := filepath.Join(s.OutputDir, "packer-vagrantfile-template.erb") +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()) @@ -74,36 +72,7 @@ func (s *StepInitializeVagrant) getVagrantfileTemplate() (string, error) { return abspath, nil } -func (s *StepInitializeVagrant) prepInitArgs() ([]string, error) { - // Prepare arguments - initArgs := []string{} - - if s.BoxName != "" { - initArgs = append(initArgs, s.BoxName) - } - - initArgs = append(initArgs, s.SourceBox) - - if s.BoxVersion != "" { - initArgs = append(initArgs, "--box-version", s.BoxVersion) - } - - if s.Minimal { - initArgs = append(initArgs, "-m") - } - - tplPath, err := s.getVagrantfileTemplate() - if err != nil { - return initArgs, err - } - - initArgs = append(initArgs, "--template", tplPath) - - return initArgs, nil -} - -func (s *StepInitializeVagrant) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { - driver := state.Get("driver").(VagrantDriver) +func (s *StepCreateVagrantfile) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { ui := state.Get("ui").(packer.Ui) // Skip the initialize step if we're trying to launch from a global ID. @@ -112,24 +81,18 @@ func (s *StepInitializeVagrant) Run(_ context.Context, state multistep.StateBag) return multistep.ActionContinue } - ui.Say("Initializing Vagrant in build directory...") - - initArgs, err := s.prepInitArgs() + ui.Say("Creating a Vagrantfile in the build directory...") + vagrantfilePath, err := s.createVagrantfile() if err != nil { state.Put("error", err) return multistep.ActionHalt } + log.Printf("Created vagrantfile at %s", vagrantfilePath) os.Chdir(s.OutputDir) - // Call vagrant using prepared arguments - err = driver.Init(initArgs) - if err != nil { - state.Put("error", err) - return multistep.ActionHalt - } return multistep.ActionContinue } -func (s *StepInitializeVagrant) Cleanup(state multistep.StateBag) { +func (s *StepCreateVagrantfile) Cleanup(state multistep.StateBag) { } diff --git a/builder/vagrant/step_create_vagrantfile_test.go b/builder/vagrant/step_create_vagrantfile_test.go new file mode 100644 index 000000000..d3bcf21b9 --- /dev/null +++ b/builder/vagrant/step_create_vagrantfile_test.go @@ -0,0 +1,60 @@ +package vagrant + +import ( + "io/ioutil" + "os" + "strings" + "testing" + + "github.com/hashicorp/packer/helper/multistep" +) + +func TestStepCreateVagrantfile_Impl(t *testing.T) { + var raw interface{} + raw = new(StepCreateVagrantfile) + if _, ok := raw.(multistep.Step); !ok { + t.Fatalf("initialize should be a step") + } +} + +func TestCreateFile(t *testing.T) { + testy := StepCreateVagrantfile{ + OutputDir: "./", + SourceBox: "bananas", + } + templatePath, err := testy.createVagrantfile() + if err != nil { + t.Fatalf(err.Error()) + } + defer os.Remove(templatePath) + contents, err := ioutil.ReadFile(templatePath) + actual := string(contents) + expected := `Vagrant.configure("2") do |config| + config.vm.box = "bananas" + config.vm.synced_folder ".", "/vagrant", disabled: true +end` + if ok := strings.Compare(actual, expected); ok != 0 { + t.Fatalf("EXPECTED: \n%s\n\n RECEIVED: \n%s\n\n", expected, actual) + } +} + +func TestCreateFile_customSync(t *testing.T) { + testy := StepCreateVagrantfile{ + OutputDir: "./", + SyncedFolder: "myfolder/foldertimes", + } + templatePath, err := testy.createVagrantfile() + if err != nil { + t.Fatalf(err.Error()) + } + defer os.Remove(templatePath) + contents, err := ioutil.ReadFile(templatePath) + actual := string(contents) + expected := `Vagrant.configure("2") do |config| + config.vm.box = "" + config.vm.synced_folder "myfolder/foldertimes", "/vagrant" +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/builder/vagrant/step_initialize_vagrant_test.go b/builder/vagrant/step_initialize_vagrant_test.go deleted file mode 100644 index 4df7284f4..000000000 --- a/builder/vagrant/step_initialize_vagrant_test.go +++ /dev/null @@ -1,110 +0,0 @@ -package vagrant - -import ( - "io/ioutil" - "os" - "strings" - "testing" - - "github.com/hashicorp/packer/helper/multistep" -) - -func TestStepInitialize_Impl(t *testing.T) { - var raw interface{} - raw = new(StepInitializeVagrant) - if _, ok := raw.(multistep.Step); !ok { - t.Fatalf("initialize should be a step") - } -} - -func TestCreateFile(t *testing.T) { - testy := StepInitializeVagrant{ - OutputDir: "./", - SourceBox: "bananas", - } - templatePath, err := testy.getVagrantfileTemplate() - if err != nil { - t.Fatalf(err.Error()) - } - contents, err := ioutil.ReadFile(templatePath) - actual := string(contents) - expected := `Vagrant.configure("2") do |config| - config.vm.box = "bananas" - config.vm.synced_folder ".", "/vagrant", disabled: true -end` - if ok := strings.Compare(actual, expected); ok != 0 { - t.Fatalf("EXPECTED: \n%s\n\n RECEIVED: \n%s\n\n", expected, actual) - } - os.Remove(templatePath) -} - -func TestCreateFile_customSync(t *testing.T) { - testy := StepInitializeVagrant{ - OutputDir: "./", - SyncedFolder: "myfolder/foldertimes", - } - templatePath, err := testy.getVagrantfileTemplate() - defer os.Remove(templatePath) - if err != nil { - t.Fatalf(err.Error()) - } - contents, err := ioutil.ReadFile(templatePath) - actual := string(contents) - expected := `Vagrant.configure("2") do |config| - config.vm.box = "" - config.vm.synced_folder "myfolder/foldertimes", "/vagrant" -end` - if ok := strings.Compare(actual, expected); ok != 0 { - t.Fatalf("EXPECTED: \n%s\n\n RECEIVED: \n%s\n\n", expected, actual) - } -} - -func TestPrepInitArgs(t *testing.T) { - type testArgs struct { - Step StepInitializeVagrant - Expected []string - } - initTests := []testArgs{ - { - Step: StepInitializeVagrant{ - SourceBox: "my_source_box.box", - }, - Expected: []string{"my_source_box.box", "--template"}, - }, - { - Step: StepInitializeVagrant{ - SourceBox: "my_source_box", - BoxName: "My Box", - }, - Expected: []string{"My Box", "my_source_box", "--template"}, - }, - { - Step: StepInitializeVagrant{ - SourceBox: "my_source_box", - BoxName: "My Box", - BoxVersion: "42", - }, - Expected: []string{"My Box", "my_source_box", "--box-version", "42", "--template"}, - }, - { - Step: StepInitializeVagrant{ - SourceBox: "my_source_box", - BoxName: "My Box", - Minimal: true, - }, - Expected: []string{"My Box", "my_source_box", "-m", "--template"}, - }, - } - for _, initTest := range initTests { - initArgs, err := initTest.Step.prepInitArgs() - defer os.Remove(initArgs[len(initArgs)-1]) - if err != nil { - t.Fatalf(err.Error()) - } - for i, val := range initTest.Expected { - if strings.Compare(initArgs[i], val) != 0 { - t.Fatalf("expected %#v but received %#v", initTest.Expected, initArgs[:len(initArgs)-1]) - } - } - } -} diff --git a/website/source/docs/builders/vagrant.html.md b/website/source/docs/builders/vagrant.html.md index ae661629f..fc93d00c9 100644 --- a/website/source/docs/builders/vagrant.html.md +++ b/website/source/docs/builders/vagrant.html.md @@ -58,11 +58,11 @@ Optional: not recommended since OVA files can be very large and corruption does happen from time to time. -- `vagrantfile_template` (string) - a path to an ERB template to use for the - vagrantfile when calling `vagrant init`. See the blog post - [here](https://www.hashicorp.com/blog/hashicorp-vagrant-2-0-2#customized-vagrantfile-templates) - for some more details on how this works. Available variables are `box_name`, - `box_url`, and `box_version`. +- `vagrantfile_template` (string) - a path to a golang template for a + vagrantfile. Our default template can be found + [here](https://github.com/hashicorp/packer/tree/master/builder/vagrant/step_initialize_vagrant.go#L23-L30). So far the only template variables available to you are {{ .BoxName }} and + {{ .SyncedFolder }}, which correspond to the Packer options `box_name` and + `synced_folder` - `skip_add` (string) - Don't call "vagrant add" to add the box to your local environment; this is necesasry if you want to launch a box that is already @@ -73,10 +73,6 @@ Optional: - `box_version` (string) - What box version to use when initializing Vagrant. -- `init_minimal` (bool) - If true, will add the --minimal flag to the Vagrant - init command, creating a minimal vagrantfile instead of one filled with helpful - comments. - - `add_cacert` (string) - Equivalent to setting the [`--cacert`](https://www.vagrantup.com/docs/cli/box.html#cacert-certfile) option in `vagrant add`; defaults to unset. From 5057220ad2e12aa5d75790408ff884c8a38dfca3 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 7 Feb 2019 14:35:01 -0800 Subject: [PATCH 09/10] use VAGRANT_CWD rather than changing packer run directories --- builder/vagrant/builder.go | 7 ++++++- builder/vagrant/driver.go | 3 ++- builder/vagrant/driver_2_2.go | 4 ++++ builder/vagrant/step_create_vagrantfile.go | 2 -- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/builder/vagrant/builder.go b/builder/vagrant/builder.go index d14f6df8e..469270298 100644 --- a/builder/vagrant/builder.go +++ b/builder/vagrant/builder.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "log" + "path/filepath" "strings" "time" @@ -167,7 +168,11 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { // a VirtualBox appliance. func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) { // Create the driver that we'll use to communicate with VirtualBox - driver, err := NewDriver() + VagrantCWD, err := filepath.Abs(b.config.OutputDir) + if err != nil { + return nil, err + } + driver, err := NewDriver(VagrantCWD) if err != nil { return nil, fmt.Errorf("Failed creating VirtualBox driver: %s", err) } diff --git a/builder/vagrant/driver.go b/builder/vagrant/driver.go index 2318a0382..56f7e8f68 100644 --- a/builder/vagrant/driver.go +++ b/builder/vagrant/driver.go @@ -42,7 +42,7 @@ type VagrantDriver interface { Version() (string, error) } -func NewDriver() (VagrantDriver, error) { +func NewDriver(outputDir string) (VagrantDriver, error) { // Hardcode path for now while I'm developing. Obviously this path needs // to be discovered based on OS. vagrantBinary := "vagrant" @@ -56,6 +56,7 @@ func NewDriver() (VagrantDriver, error) { driver := &Vagrant_2_2_Driver{ vagrantBinary: vagrantBinary, + VagrantCWD: outputDir, } if err := driver.Verify(); err != nil { diff --git a/builder/vagrant/driver_2_2.go b/builder/vagrant/driver_2_2.go index 9886939b1..01652f5c3 100644 --- a/builder/vagrant/driver_2_2.go +++ b/builder/vagrant/driver_2_2.go @@ -6,6 +6,7 @@ import ( "log" "os" "os/exec" + "path/filepath" "regexp" "strings" @@ -16,6 +17,7 @@ const VAGRANT_MIN_VERSION = ">= 2.0.2" type Vagrant_2_2_Driver struct { vagrantBinary string + VagrantCWD string } // Calls "vagrant init" @@ -69,6 +71,7 @@ func (d *Vagrant_2_2_Driver) Destroy(id string) error { // Calls "vagrant package" func (d *Vagrant_2_2_Driver) Package(args []string) error { + args = append(args, "--output", filepath.Join(d.VagrantCWD, "package.box")) _, _, err := d.vagrantCmd(append([]string{"package"}, args...)...) return err } @@ -177,6 +180,7 @@ func (d *Vagrant_2_2_Driver) vagrantCmd(args ...string) (string, string, error) log.Printf("Calling Vagrant CLI: %#v", args) cmd := exec.Command(d.vagrantBinary, args...) + cmd.Env = append(os.Environ(), fmt.Sprintf("VAGRANT_CWD=%s", d.VagrantCWD)) cmd.Stdout = &stdout cmd.Stderr = &stderr err := cmd.Run() diff --git a/builder/vagrant/step_create_vagrantfile.go b/builder/vagrant/step_create_vagrantfile.go index 375b9b423..40ead21dd 100644 --- a/builder/vagrant/step_create_vagrantfile.go +++ b/builder/vagrant/step_create_vagrantfile.go @@ -89,8 +89,6 @@ func (s *StepCreateVagrantfile) Run(_ context.Context, state multistep.StateBag) } log.Printf("Created vagrantfile at %s", vagrantfilePath) - os.Chdir(s.OutputDir) - return multistep.ActionContinue } From 9f702af6d9e6a06cc55be282081437a042a3103a Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 14 Feb 2019 14:46:14 -0800 Subject: [PATCH 10/10] we lost the skip_add step somewhere --- builder/vagrant/builder.go | 1 + builder/vagrant/step_add_box.go | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/builder/vagrant/builder.go b/builder/vagrant/builder.go index 469270298..a0cd3202a 100644 --- a/builder/vagrant/builder.go +++ b/builder/vagrant/builder.go @@ -223,6 +223,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe SourceBox: b.config.SourceBox, BoxName: b.config.BoxName, GlobalID: b.config.GlobalID, + SkipAdd: b.config.SkipAdd, }, &StepUp{ TeardownMethod: b.config.TeardownMethod, diff --git a/builder/vagrant/step_add_box.go b/builder/vagrant/step_add_box.go index a1832969e..9c1e23eb6 100644 --- a/builder/vagrant/step_add_box.go +++ b/builder/vagrant/step_add_box.go @@ -21,10 +21,10 @@ type StepAddBox struct { SourceBox string BoxName string GlobalID string + SkipAdd bool } func (s *StepAddBox) generateAddArgs() []string { - addArgs := []string{} if strings.HasSuffix(s.SourceBox, ".box") { @@ -72,6 +72,11 @@ func (s *StepAddBox) Run(_ context.Context, state multistep.StateBag) multistep. driver := state.Get("driver").(VagrantDriver) ui := state.Get("ui").(packer.Ui) + if s.SkipAdd { + ui.Say("skip_add was set so we assume the box is already in Vagrant...") + return multistep.ActionContinue + } + if s.GlobalID != "" { ui.Say("Using a global-id; skipping Vagrant add command...") return multistep.ActionContinue