Merge pull request #7221 from hashicorp/vagrant_builder

Vagrant builder
This commit is contained in:
Megan Marsh 2019-02-15 10:05:44 -08:00 committed by GitHub
commit 75f574bd4b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 1518 additions and 183 deletions

View File

@ -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
}

View File

@ -0,0 +1,47 @@
package vagrant
import (
"runtime"
"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 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())
}
}
func TestArtifactString(t *testing.T) {
a := &artifact{
OutputDir: "/my/dir",
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())
}
}

276
builder/vagrant/builder.go Normal file
View File

@ -0,0 +1,276 @@
package vagrant
import (
"errors"
"fmt"
"log"
"path/filepath"
"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_path"`
GlobalID string `mapstructure:"global_id"`
Checksum string `mapstructure:"checksum"`
ChecksumType string `mapstructure:"checksum_type"`
BoxName string `mapstructure:"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"`
Template string `mapstructure:"template"`
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"`
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"`))
}
// 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 == "" {
if b.config.GlobalID == "" {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("source_path is required unless you have set global_id"))
}
} else {
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 {
errs = packer.MultiErrorAppend(errs,
fmt.Errorf("Source file '%s' needs to exist at time of config validation!", b.config.SourceBox))
}
}
}
if b.config.TeardownMethod == "" {
// 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"} {
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
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)
}
// 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{}
// 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,
},
&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,
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.BoxName,
GlobalID: b.config.GlobalID,
SkipAdd: b.config.SkipAdd,
},
&StepUp{
TeardownMethod: b.config.TeardownMethod,
Provider: b.config.Provider,
GlobalID: b.config.GlobalID,
},
&StepSSHConfig{
b.config.GlobalID,
},
&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,
GlobalID: b.config.GlobalID,
})
// 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()
}
}

View File

@ -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)
}
}
}

67
builder/vagrant/driver.go Normal file
View File

@ -0,0 +1,67 @@
package vagrant
import (
"fmt"
"os/exec"
"runtime"
)
// 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(string) error
// Calls "vagrant suspend"
Suspend(string) error
SSHConfig(string) (*VagrantSSHConfig, error)
// Calls "vagrant destroy"
Destroy(string) 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(outputDir string) (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,
VagrantCWD: outputDir,
}
if err := driver.Verify(); err != nil {
return nil, err
}
return driver, nil
}

View File

@ -0,0 +1,199 @@
package vagrant
import (
"bytes"
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"
"github.com/hashicorp/go-version"
)
const VAGRANT_MIN_VERSION = ">= 2.0.2"
type Vagrant_2_2_Driver struct {
vagrantBinary string
VagrantCWD 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(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(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(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 {
args = append(args, "--output", filepath.Join(d.VagrantCWD, "package.box"))
_, _, err := d.vagrantCmd(append([]string{"package"}, args...)...)
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.")
}
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
}
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(id string) (*VagrantSSHConfig, error) {
// vagrant ssh-config --host 8df7860
args := []string{"ssh-config"}
if id != "" {
args = append(args, id)
}
stdout, _, err := d.vagrantCmd(args...)
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.Env = append(os.Environ(), fmt.Sprintf("VAGRANT_CWD=%s", d.VagrantCWD))
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
}

19
builder/vagrant/ssh.go Normal file
View File

@ -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
}
}

View File

@ -0,0 +1,100 @@
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
GlobalID string
SkipAdd bool
}
func (s *StepAddBox) generateAddArgs() []string {
addArgs := []string{}
if strings.HasSuffix(s.SourceBox, ".box") {
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)
}
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.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
}
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)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
return multistep.ActionContinue
}
func (s *StepAddBox) Cleanup(state multistep.StateBag) {
}

View File

@ -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)
}
}
}
}

View File

@ -0,0 +1,96 @@
package vagrant
import (
"context"
"fmt"
"log"
"os"
"path/filepath"
"text/template"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
type StepCreateVagrantfile struct {
Template string
SourceBox string
OutputDir string
SyncedFolder string
GlobalID 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 *StepCreateVagrantfile) createVagrantfile() (string, error) {
tplPath := filepath.Join(s.OutputDir, "Vagrantfile")
templateFile, err := os.Create(tplPath)
if err != nil {
retErr := fmt.Errorf("Error creating vagrantfile %s", err.Error())
return "", retErr
}
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 *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.
if s.GlobalID != "" {
ui.Say("Using a global-id; skipping Vagrant init in this directory...")
return multistep.ActionContinue
}
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)
return multistep.ActionContinue
}
func (s *StepCreateVagrantfile) Cleanup(state multistep.StateBag) {
}

View File

@ -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)
}
}

View File

@ -0,0 +1,49 @@
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
GlobalID 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 s.GlobalID != "" {
packageArgs = append(packageArgs, s.GlobalID)
}
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) {
}

View File

@ -0,0 +1,53 @@
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 {
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(s.GlobalID)
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) {
}

View File

@ -0,0 +1,62 @@
package vagrant
import (
"context"
"fmt"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
type StepUp struct {
TeardownMethod string
Provider string
GlobalID 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.GlobalID != "" {
args = append(args, s.GlobalID)
}
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(s.GlobalID)
} else if s.TeardownMethod == "suspend" {
err = driver.Suspend(s.GlobalID)
} else if s.TeardownMethod == "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."))
}
if err != nil {
state.Put("error", fmt.Errorf("Error halting Vagrant machine; please try to do this manually"))
}
}

View File

@ -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"
@ -118,6 +119,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),

View File

@ -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)

View File

@ -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
<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`.
<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

View File

@ -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

View File

@ -184,8 +184,8 @@ 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:
&lt;name\\\|uid&gt;\[:&lt;group\\\|gid&gt;\]) to run remote commands with.
- `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.
@ -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.

View File

@ -18,9 +18,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\]
(<https://cloud.google.com/compute/docs/tutorials/building-images>) and the
[Google Compute Import
[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.
@ -139,7 +138,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>.
<a href="https://console.cloud.google.com/networking/firewalls/list" class="uri">https://console.cloud.google.com/networking/firewalls/list</a>.
Once this is set up, the following is a complete working packer config after
setting a valid `account_file` and `project_id`:

View File

@ -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

View File

@ -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.
~&gt; Note: to build Centos images on a Debian family host, you will need the
\~&gt; Note: to build Centos images on a Debian family host, you will need the
`yum` package installed. <br>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

View File

@ -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`, ...
~&gt; Note: The builder may appear to pause if required to download a
\~&gt; 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
<https://stgraber.org/2016/03/30/lxd-2-0-image-management-512/> for more
properties.
<a href="https://stgraber.org/2016/03/30/lxd-2-0-image-management-512/" class="uri">https://stgraber.org/2016/03/30/lxd-2-0-image-management-512/</a>
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.

View File

@ -90,7 +90,6 @@ Here is a basic example for windows server.
]
}
Here is a basic example for linux server.
{

View File

@ -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.

View File

@ -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`}}",

View File

@ -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
<https://api-marketplace.scaleway.com/images> get the complete list of the
accepted image UUID.
[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`
or `ams1`). Consequently, this is the region where the snapshot will be

View File

@ -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.
~&gt; **Private installations of Triton must have custom images enabled!** To
\~&gt; **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

View File

@ -0,0 +1,101 @@
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`.
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_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
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.
- `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.
- `checksum` (string) - The checksum for the .box file. The type of the
checksum is specified with `checksum_type`, documented below.
- `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.
- `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
added to your vagrant environment.
- `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.
- `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.

View File

@ -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!
~&gt; The `-machine-readable` flag is designed for automated environments and
\~&gt; The `-machine-readable` flag is designed for automated environments and
is mutually-exclusive with the `-debug` flag, which is designed for interactive
environments.

View File

@ -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).
~&gt; **Warning!** This is an advanced topic. If you're new to Packer, we
\~&gt; **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

View File

@ -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.
~&gt; **Warning!** This is an advanced topic. If you're new to Packer, we
\~&gt; **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

View File

@ -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.
~&gt; **Warning!** This is an advanced topic. If you're new to Packer, we
\~&gt; **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

View File

@ -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.
~&gt; **Warning!** This is an advanced topic. If you're new to Packer, we
\~&gt; **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.
~&gt; **Lock your dependencies!** Using `govendor` is highly recommended since
\~&gt; **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.

View File

@ -42,7 +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,
@ -52,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.
@ -70,7 +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
@ -79,9 +79,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

View File

@ -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.
~&gt; This post-processor is for advanced users. It depends on specific IAM
\~&gt; 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`

View File

@ -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

View File

@ -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.
~&gt; This post-processor is for advanced users. Please ensure you read the
\~&gt; 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.

View File

@ -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"))
```
print('Hello, %s!' % os.getenv("HELLO_USER"))

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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.
completes or errors.

View File

@ -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.
~&gt; `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.
\~&gt; `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.

View File

@ -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"))
```
print('Hello, %s!' % os.getenv("HELLO_USER"))

View File

@ -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"`.

View File

@ -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
- <es128-gcm@openssh.com>
- <acha20-poly1305@openssh.com>
- `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>
- `hmac-sha2-256-etm@openssh.com`
## 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.

View File

@ -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
</td>
</tr>
</table>
*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
<a href="https://github.com/rwtodd/Go.Sed" class="uri">https://github.com/rwtodd/Go.Sed</a>
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"]
}
]