diff --git a/CHANGELOG.md b/CHANGELOG.md index 918c117cc..18fa8acb8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,33 @@ ## (Unreleased) +### BUG FIXES: + +* builder/googlecompute: Correct values for `on_host_maintenance`. [GH-4643] +* builder/amazon: Fix crash in `step_region_copy`. [GH-4642] +* core: show correct step name when debugging. [GH-4672] +* builder/virtualbox: fix `none` communicator by allowing skipping upload of + version file. [GH-4678] +* communicator/ssh: fix nil pointer error. [GH-4690] +* builder/hyper-v: Don't wait for shutdown_command to return. [GH-4691] + +### IMRPOVEMENTS: + +* builder/amazon: validate ssh key name/file. [GH-4665] +* builder/amazon: set force_deregister to true on -force. [GH-4649] +* builder/hyper-v: validate output dir in step, not in config. [GH-4645] +* website: fix display on ios devices. [GH-4618] +* builder/openstack: Add ssh agent support. [GH-4655] +* builder/parallels-iso: Configuration of disk type, plain or expanding. + [GH-4621] +* builder/ansible: Clearer error message when we have problems getting the + ansible version. [GH-4694] + ## 0.12.3 (March 1, 2017) +### BACKWARDS INCOMPATIBILITIES: + +* provisioner/ansible: by default, the staging dir will be randomized. [GH-4472] + ### FEATURES: * **New builder:** `ebs-surrogate` for building AMIs from EBS volumes. [GH-4351] @@ -18,7 +44,6 @@ * communicator/winrm: support ProxyFromEnvironment. [GH-4463] * core: make VNC links clickable in terminal. [GH-4497] [GH-4498] * post-processor/amazon-import: support AMI attributes on import [GH-4216] -* provisioner/ansible: use randomized staging dir [GH-4472] * communicator/ssh: Use SSH agent when enabled for bastion step. [GH-4598] * builder/amazon: enable ena when `enhanced_networking` is set. [GH-4578] * builder/vmware-esxi: try for longer to connect to vnc port. [GH-4480] @@ -32,7 +57,6 @@ * builder/azure:: add two new config variables for temp_compute_name and temp_resource_group_name. [GH-4468] - ### BUG FIXES: * builder/amazon: Fix ssh agent authentication. [GH-4597] diff --git a/README.md b/README.md index 5c4905cbb..6c767ce49 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ [report-badge]: https://goreportcard.com/badge/github.com/mitchellh/packer [report]: https://goreportcard.com/report/github.com/mitchellh/packer -* Website: http://www.packer.io +* Website: https://www.packer.io * IRC: `#packer-tool` on Freenode * Mailing list: [Google Groups](http://groups.google.com/group/packer-tool) @@ -27,14 +27,19 @@ comes out of the box with support for the following platforms: * Amazon EC2 (AMI). Both EBS-backed and instance-store AMIs * Azure +* CloudStack * DigitalOcean * Docker * Google Compute Engine +* Hyper-V +* 1&1 * OpenStack * Parallels +* ProfitBricks * QEMU. Both KVM and Xen images. -* VirtualBox +* Triton (Joyent Public Cloud) * VMware +* VirtualBox Support for other platforms can be added via plugins. diff --git a/builder/amazon/chroot/builder.go b/builder/amazon/chroot/builder.go index 18bf8ce3c..17f725212 100644 --- a/builder/amazon/chroot/builder.go +++ b/builder/amazon/chroot/builder.go @@ -78,6 +78,10 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { return nil, err } + if b.config.PackerConfig.PackerForce { + b.config.AMIForceDeregister = true + } + // Defaults if b.config.ChrootMounts == nil { b.config.ChrootMounts = make([][]string, 0) diff --git a/builder/amazon/common/run_config.go b/builder/amazon/common/run_config.go index 815583245..154a519e9 100644 --- a/builder/amazon/common/run_config.go +++ b/builder/amazon/common/run_config.go @@ -75,6 +75,14 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error { // Validation errs := c.Comm.Prepare(ctx) + if c.SSHKeyPairName != "" { + if c.Comm.Type == "winrm" && c.Comm.WinRMPassword == "" && c.Comm.SSHPrivateKey == "" { + errs = append(errs, errors.New("A private_key_file must be provided to retrieve the winrm password when using ssh_keypair_name.")) + } else if c.Comm.SSHPrivateKey == "" && !c.Comm.SSHAgentAuth { + errs = append(errs, errors.New("A private_key_file must be provided or ssh_agent_auth enabled when ssh_keypair_name is specified.")) + } + } + if c.SourceAmi == "" && c.SourceAmiFilter.Empty() { errs = append(errs, errors.New("A source_ami or source_ami_filter must be specified")) } diff --git a/builder/amazon/common/step_ami_region_copy.go b/builder/amazon/common/step_ami_region_copy.go index 1f4e65738..96496fe47 100644 --- a/builder/amazon/common/step_ami_region_copy.go +++ b/builder/amazon/common/step_ami_region_copy.go @@ -2,7 +2,6 @@ package common import ( "fmt" - "sync" "github.com/aws/aws-sdk-go/aws" @@ -128,7 +127,7 @@ func amiRegionCopy(state multistep.StateBag, config *AccessConfig, name string, } for _, blockDeviceMapping := range describeImageResp.Images[0].BlockDeviceMappings { - if blockDeviceMapping.Ebs != nil { + if blockDeviceMapping.Ebs != nil && blockDeviceMapping.Ebs.SnapshotId != nil { snapshotIds = append(snapshotIds, *blockDeviceMapping.Ebs.SnapshotId) } } diff --git a/builder/amazon/ebs/builder.go b/builder/amazon/ebs/builder.go index 13598b96a..c36cb33e8 100644 --- a/builder/amazon/ebs/builder.go +++ b/builder/amazon/ebs/builder.go @@ -58,6 +58,10 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { return nil, err } + if b.config.PackerConfig.PackerForce { + b.config.AMIForceDeregister = true + } + // Accumulate any errors var errs *packer.MultiError errs = packer.MultiErrorAppend(errs, b.config.AccessConfig.Prepare(&b.config.ctx)...) diff --git a/builder/amazon/ebssurrogate/builder.go b/builder/amazon/ebssurrogate/builder.go index df2db83c1..b3b02aeab 100644 --- a/builder/amazon/ebssurrogate/builder.go +++ b/builder/amazon/ebssurrogate/builder.go @@ -58,6 +58,10 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { return nil, err } + if b.config.PackerConfig.PackerForce { + b.config.AMIForceDeregister = true + } + // Accumulate any errors var errs *packer.MultiError errs = packer.MultiErrorAppend(errs, b.config.AccessConfig.Prepare(&b.config.ctx)...) diff --git a/builder/amazon/instance/builder.go b/builder/amazon/instance/builder.go index ce5664d26..d1e6c50a0 100644 --- a/builder/amazon/instance/builder.go +++ b/builder/amazon/instance/builder.go @@ -77,6 +77,10 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { return nil, err } + if b.config.PackerConfig.PackerForce { + b.config.AMIForceDeregister = true + } + if b.config.BundleDestination == "" { b.config.BundleDestination = "/tmp" } diff --git a/builder/googlecompute/config.go b/builder/googlecompute/config.go index 78141bed4..330ea5900 100644 --- a/builder/googlecompute/config.go +++ b/builder/googlecompute/config.go @@ -93,15 +93,20 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { if c.ImageDescription == "" { c.ImageDescription = "Created by Packer" } - // Setting OnHostMaintenance Correct Defaults - // "MIGRATE" : Possible if Preemptible is false - // "TERMINATE": Posssible if Preemptible is true - if c.OnHostMaintenance == "" && c.Preemptible { - c.OnHostMaintenance = "MIGRATE" - } - if c.OnHostMaintenance == "" && !c.Preemptible { + if c.OnHostMaintenance == "MIGRATE" && c.Preemptible { + errs = packer.MultiErrorAppend(errs, + errors.New("on_host_maintenance must be TERMINATE when using preemptible instances.")) + } + // Setting OnHostMaintenance Correct Defaults + // "MIGRATE" : Possible and default if Preemptible is false + // "TERMINATE": Required if Preemptible is true + if c.Preemptible { c.OnHostMaintenance = "TERMINATE" + } else { + if c.OnHostMaintenance == "" { + c.OnHostMaintenance = "MIGRATE" + } } // Make sure user sets a valid value for on_host_maintenance option diff --git a/builder/hyperv/common/driver_ps_4.go b/builder/hyperv/common/driver_ps_4.go index 030ee8f55..ae125259a 100644 --- a/builder/hyperv/common/driver_ps_4.go +++ b/builder/hyperv/common/driver_ps_4.go @@ -64,7 +64,7 @@ func (d *HypervPS4Driver) Verify() error { return err } - if err := d.verifyElevatedMode(); err != nil { + if err := d.verifyHypervPermissions(); err != nil { return err } @@ -293,16 +293,28 @@ func (d *HypervPS4Driver) verifyPSHypervModule() error { return nil } -func (d *HypervPS4Driver) verifyElevatedMode() error { +func (d *HypervPS4Driver) verifyHypervPermissions() error { - log.Printf("Enter method: %s", "verifyElevatedMode") + log.Printf("Enter method: %s", "verifyHypervPermissions") - isAdmin, _ := powershell.IsCurrentUserAnAdministrator() + hypervAdminCmd := "([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole('Hyper-V Administrators')" - if !isAdmin { - err := fmt.Errorf("%s", "Please restart your shell in elevated mode") + var ps powershell.PowerShellCmd + cmdOut, err := ps.Output(hypervAdminCmd) + if err != nil { return err } + res := strings.TrimSpace(string(cmdOut)) + + if res == "False" { + isAdmin, _ := powershell.IsCurrentUserAnAdministrator() + + if !isAdmin { + err := fmt.Errorf("%s", "Current user is not a member of 'Hyper-V Administrators' or 'Administrators' group") + return err + } + } + return nil } diff --git a/builder/hyperv/common/output_config.go b/builder/hyperv/common/output_config.go index ee589d49d..7b5ddcd45 100644 --- a/builder/hyperv/common/output_config.go +++ b/builder/hyperv/common/output_config.go @@ -2,9 +2,9 @@ package common import ( "fmt" + "github.com/mitchellh/packer/common" "github.com/mitchellh/packer/template/interpolate" - "os" ) type OutputConfig struct { @@ -16,13 +16,5 @@ func (c *OutputConfig) Prepare(ctx *interpolate.Context, pc *common.PackerConfig c.OutputDir = fmt.Sprintf("output-%s", pc.PackerBuildName) } - var errs []error - if !pc.PackerForce { - if _, err := os.Stat(c.OutputDir); err == nil { - errs = append(errs, fmt.Errorf( - "Output directory '%s' already exists. It must not exist.", c.OutputDir)) - } - } - - return errs + return nil } diff --git a/builder/hyperv/common/output_config_test.go b/builder/hyperv/common/output_config_test.go index ebd91eab1..3da8bda58 100644 --- a/builder/hyperv/common/output_config_test.go +++ b/builder/hyperv/common/output_config_test.go @@ -1,10 +1,11 @@ package common import ( - "github.com/mitchellh/packer/common" "io/ioutil" "os" "testing" + + "github.com/mitchellh/packer/common" ) func TestOutputConfigPrepare(t *testing.T) { @@ -39,7 +40,7 @@ func TestOutputConfigPrepare_exists(t *testing.T) { PackerForce: false, } errs := c.Prepare(testConfigTemplate(t), pc) - if len(errs) == 0 { - t.Fatal("should have errors") + if len(errs) != 0 { + t.Fatal("should not have errors") } } diff --git a/builder/hyperv/common/step_output_dir.go b/builder/hyperv/common/step_output_dir.go index 209bbabe2..1443e5791 100644 --- a/builder/hyperv/common/step_output_dir.go +++ b/builder/hyperv/common/step_output_dir.go @@ -17,16 +17,30 @@ import ( type StepOutputDir struct { Force bool Path string + + cleanup bool } func (s *StepOutputDir) Run(state multistep.StateBag) multistep.StepAction { ui := state.Get("ui").(packer.Ui) - if _, err := os.Stat(s.Path); err == nil && s.Force { + if _, err := os.Stat(s.Path); err == nil { + if !s.Force { + err := fmt.Errorf( + "Output directory exists: %s\n\n"+ + "Use the force flag to delete it prior to building.", + s.Path) + state.Put("error", err) + return multistep.ActionHalt + } + ui.Say("Deleting previous output directory...") os.RemoveAll(s.Path) } + // Enable cleanup + s.cleanup = true + // Create the directory if err := os.MkdirAll(s.Path, 0755); err != nil { state.Put("error", err) @@ -47,6 +61,10 @@ func (s *StepOutputDir) Run(state multistep.StateBag) multistep.StepAction { } func (s *StepOutputDir) Cleanup(state multistep.StateBag) { + if !s.cleanup { + return + } + _, cancelled := state.GetOk(multistep.StateCancelled) _, halted := state.GetOk(multistep.StateHalted) diff --git a/builder/hyperv/common/step_shutdown.go b/builder/hyperv/common/step_shutdown.go index e22234e27..84a20c26b 100644 --- a/builder/hyperv/common/step_shutdown.go +++ b/builder/hyperv/common/step_shutdown.go @@ -4,10 +4,11 @@ import ( "bytes" "errors" "fmt" - "github.com/mitchellh/multistep" - "github.com/mitchellh/packer/packer" "log" "time" + + "github.com/mitchellh/multistep" + "github.com/mitchellh/packer/packer" ) // This step shuts down the machine. It first attempts to do so gracefully, @@ -51,14 +52,6 @@ func (s *StepShutdown) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionHalt } - // Wait for the command to run so we can print std{err,out} - // We don't care if the command errored, since we'll notice - // if the vm didn't shut down. - cmd.Wait() - - log.Printf("Shutdown stdout: %s", stdout.String()) - log.Printf("Shutdown stderr: %s", stderr.String()) - // Wait for the machine to actually shut down log.Printf("Waiting max %s for shutdown to complete", s.Timeout) shutdownTimer := time.After(s.Timeout) @@ -70,12 +63,14 @@ func (s *StepShutdown) Run(state multistep.StateBag) multistep.StepAction { select { case <-shutdownTimer: + log.Printf("Shutdown stdout: %s", stdout.String()) + log.Printf("Shutdown stderr: %s", stderr.String()) err := errors.New("Timeout while waiting for machine to shut down.") state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt default: - time.Sleep(150 * time.Millisecond) + time.Sleep(500 * time.Millisecond) } } } else { diff --git a/builder/openstack/builder.go b/builder/openstack/builder.go index ecf37cf73..3d4263433 100644 --- a/builder/openstack/builder.go +++ b/builder/openstack/builder.go @@ -75,10 +75,12 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe Flavor: b.config.Flavor, }, &StepKeyPair{ - Debug: b.config.PackerDebug, - DebugKeyPath: fmt.Sprintf("os_%s.pem", b.config.PackerBuildName), - KeyPairName: b.config.SSHKeyPairName, - PrivateKeyFile: b.config.RunConfig.Comm.SSHPrivateKey, + Debug: b.config.PackerDebug, + DebugKeyPath: fmt.Sprintf("os_%s.pem", b.config.PackerBuildName), + KeyPairName: b.config.SSHKeyPairName, + TemporaryKeyPairName: b.config.TemporaryKeyPairName, + PrivateKeyFile: b.config.RunConfig.Comm.SSHPrivateKey, + SSHAgentAuth: b.config.RunConfig.Comm.SSHAgentAuth, }, &StepRunSourceServer{ Name: b.config.ImageName, @@ -110,7 +112,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe computeClient, b.config.SSHInterface, b.config.SSHIPVersion), - SSHConfig: SSHConfig(b.config.RunConfig.Comm.SSHUsername, + SSHConfig: SSHConfig( + b.config.RunConfig.Comm.SSHAgentAuth, + b.config.RunConfig.Comm.SSHUsername, b.config.RunConfig.Comm.SSHPassword), }, &common.StepProvision{}, diff --git a/builder/openstack/run_config.go b/builder/openstack/run_config.go index f0bb4dc7c..eddbae1ed 100644 --- a/builder/openstack/run_config.go +++ b/builder/openstack/run_config.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" + "github.com/mitchellh/packer/common/uuid" "github.com/mitchellh/packer/helper/communicator" "github.com/mitchellh/packer/template/interpolate" ) @@ -11,10 +12,11 @@ import ( // RunConfig contains configuration for running an instance from a source // image and details on how to access that launched image. type RunConfig struct { - Comm communicator.Config `mapstructure:",squash"` - SSHKeyPairName string `mapstructure:"ssh_keypair_name"` - SSHInterface string `mapstructure:"ssh_interface"` - SSHIPVersion string `mapstructure:"ssh_ip_version"` + Comm communicator.Config `mapstructure:",squash"` + SSHKeyPairName string `mapstructure:"ssh_keypair_name"` + TemporaryKeyPairName string `mapstructure:"temporary_key_pair_name"` + SSHInterface string `mapstructure:"ssh_interface"` + SSHIPVersion string `mapstructure:"ssh_ip_version"` SourceImage string `mapstructure:"source_image"` SourceImageName string `mapstructure:"source_image_name"` @@ -38,6 +40,15 @@ type RunConfig struct { } func (c *RunConfig) Prepare(ctx *interpolate.Context) []error { + // If we are not given an explicit ssh_keypair_name or + // ssh_private_key_file, then create a temporary one, but only if the + // temporary_key_pair_name has not been provided and we are not using + // ssh_password. + if c.SSHKeyPairName == "" && c.TemporaryKeyPairName == "" && + c.Comm.SSHPrivateKey == "" && c.Comm.SSHPassword == "" { + + c.TemporaryKeyPairName = fmt.Sprintf("packer_%s", uuid.TimeOrderedUUID()) + } if c.UseFloatingIp && c.FloatingIpPool == "" { c.FloatingIpPool = "public" @@ -45,6 +56,15 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error { // Validation errs := c.Comm.Prepare(ctx) + + if c.SSHKeyPairName != "" { + if c.Comm.Type == "winrm" && c.Comm.WinRMPassword == "" && c.Comm.SSHPrivateKey == "" { + errs = append(errs, errors.New("A private_key_file must be provided to retrieve the winrm password when using ssh_keypair_name.")) + } else if c.Comm.SSHPrivateKey == "" && !c.Comm.SSHAgentAuth { + errs = append(errs, errors.New("A private_key_file must be provided or ssh_agent_auth enabled when ssh_keypair_name is specified.")) + } + } + if c.SourceImage == "" && c.SourceImageName == "" { errs = append(errs, errors.New("Either a source_image or a source_image_name must be specified")) } else if len(c.SourceImage) > 0 && len(c.SourceImageName) > 0 { diff --git a/builder/openstack/ssh.go b/builder/openstack/ssh.go index a5495eed3..9738c278e 100644 --- a/builder/openstack/ssh.go +++ b/builder/openstack/ssh.go @@ -4,6 +4,8 @@ import ( "errors" "fmt" "log" + "net" + "os" "time" "github.com/gophercloud/gophercloud" @@ -12,6 +14,7 @@ import ( "github.com/mitchellh/multistep" packerssh "github.com/mitchellh/packer/communicator/ssh" "golang.org/x/crypto/ssh" + "golang.org/x/crypto/ssh/agent" ) // CommHost looks up the host for the communicator. @@ -63,8 +66,26 @@ func CommHost( // SSHConfig returns a function that can be used for the SSH communicator // config for connecting to the instance created over SSH using a private key // or a password. -func SSHConfig(username, password string) func(multistep.StateBag) (*ssh.ClientConfig, error) { +func SSHConfig(useAgent bool, username, password string) func(multistep.StateBag) (*ssh.ClientConfig, error) { return func(state multistep.StateBag) (*ssh.ClientConfig, error) { + if useAgent { + authSock := os.Getenv("SSH_AUTH_SOCK") + if authSock == "" { + return nil, fmt.Errorf("SSH_AUTH_SOCK is not set") + } + + sshAgent, err := net.Dial("unix", authSock) + if err != nil { + return nil, fmt.Errorf("Cannot connect to SSH Agent socket %q: %s", authSock, err) + } + + return &ssh.ClientConfig{ + User: username, + Auth: []ssh.AuthMethod{ + ssh.PublicKeysCallback(agent.NewClient(sshAgent).Signers), + }, + }, nil + } privateKey, hasKey := state.GetOk("privateKey") diff --git a/builder/openstack/step_key_pair.go b/builder/openstack/step_key_pair.go index 94ca5c768..b28994030 100644 --- a/builder/openstack/step_key_pair.go +++ b/builder/openstack/step_key_pair.go @@ -10,21 +10,24 @@ import ( "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs" "github.com/mitchellh/multistep" - "github.com/mitchellh/packer/common/uuid" "github.com/mitchellh/packer/packer" "golang.org/x/crypto/ssh" ) type StepKeyPair struct { - Debug bool - DebugKeyPath string - KeyPairName string - PrivateKeyFile string + Debug bool + SSHAgentAuth bool + DebugKeyPath string + TemporaryKeyPairName string + KeyPairName string + PrivateKeyFile string keyName string } func (s *StepKeyPair) Run(state multistep.StateBag) multistep.StepAction { + ui := state.Get("ui").(packer.Ui) + if s.PrivateKeyFile != "" { privateKeyBytes, err := ioutil.ReadFile(s.PrivateKeyFile) if err != nil { @@ -39,14 +42,25 @@ func (s *StepKeyPair) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionContinue } - config := state.Get("config").(Config) - ui := state.Get("ui").(packer.Ui) - - if config.Comm.Type == "ssh" && config.Comm.SSHPassword != "" { - ui.Say("Not creating temporary keypair when using password.") + if s.SSHAgentAuth && s.KeyPairName == "" { + ui.Say("Using SSH Agent with key pair in Source image") return multistep.ActionContinue } + if s.SSHAgentAuth && s.KeyPairName != "" { + ui.Say(fmt.Sprintf("Using SSH Agent for existing key pair %s", s.KeyPairName)) + state.Put("keyPair", s.KeyPairName) + return multistep.ActionContinue + } + + if s.TemporaryKeyPairName == "" { + ui.Say("Not using temporary keypair") + state.Put("keyPair", "") + return multistep.ActionContinue + } + + config := state.Get("config").(Config) + // We need the v2 compute client computeClient, err := config.computeV2Client() if err != nil { @@ -55,10 +69,9 @@ func (s *StepKeyPair) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionHalt } - keyName := fmt.Sprintf("packer %s", uuid.TimeOrderedUUID()) - ui.Say(fmt.Sprintf("Creating temporary keypair: %s ...", keyName)) + ui.Say(fmt.Sprintf("Creating temporary keypair: %s ...", s.TemporaryKeyPairName)) keypair, err := keypairs.Create(computeClient, keypairs.CreateOpts{ - Name: keyName, + Name: s.TemporaryKeyPairName, }).Extract() if err != nil { state.Put("error", fmt.Errorf("Error creating temporary keypair: %s", err)) @@ -70,7 +83,7 @@ func (s *StepKeyPair) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionHalt } - ui.Say(fmt.Sprintf("Created temporary keypair: %s", keyName)) + ui.Say(fmt.Sprintf("Created temporary keypair: %s", s.TemporaryKeyPairName)) keypair.PrivateKey = berToDer(keypair.PrivateKey, ui) @@ -101,10 +114,10 @@ func (s *StepKeyPair) Run(state multistep.StateBag) multistep.StepAction { } // Set the keyname so we know to delete it later - s.keyName = keyName + s.keyName = s.TemporaryKeyPairName // Set some state data for use in future steps - state.Put("keyPair", keyName) + state.Put("keyPair", s.keyName) state.Put("privateKey", keypair.PrivateKey) return multistep.ActionContinue @@ -156,11 +169,11 @@ func berToDer(ber string, ui packer.Ui) string { func (s *StepKeyPair) Cleanup(state multistep.StateBag) { // If we used an SSH private key file, do not go about deleting // keypairs - if s.PrivateKeyFile != "" { + if s.PrivateKeyFile != "" || (s.KeyPairName == "" && s.keyName == "") { return } // If no key name is set, then we never created it, so just return - if s.keyName == "" { + if s.TemporaryKeyPairName == "" { return } @@ -171,14 +184,14 @@ func (s *StepKeyPair) Cleanup(state multistep.StateBag) { computeClient, err := config.computeV2Client() if err != nil { ui.Error(fmt.Sprintf( - "Error cleaning up keypair. Please delete the key manually: %s", s.keyName)) + "Error cleaning up keypair. Please delete the key manually: %s", s.TemporaryKeyPairName)) return } - ui.Say(fmt.Sprintf("Deleting temporary keypair: %s ...", s.keyName)) + ui.Say(fmt.Sprintf("Deleting temporary keypair: %s ...", s.TemporaryKeyPairName)) err = keypairs.Delete(computeClient, s.keyName).ExtractErr() if err != nil { ui.Error(fmt.Sprintf( - "Error cleaning up keypair. Please delete the key manually: %s", s.keyName)) + "Error cleaning up keypair. Please delete the key manually: %s", s.TemporaryKeyPairName)) } } diff --git a/builder/parallels/iso/builder.go b/builder/parallels/iso/builder.go index 8aefdf762..e53509c50 100644 --- a/builder/parallels/iso/builder.go +++ b/builder/parallels/iso/builder.go @@ -37,6 +37,7 @@ type Config struct { BootCommand []string `mapstructure:"boot_command"` DiskSize uint `mapstructure:"disk_size"` + DiskType string `mapstructure:"disk_type"` GuestOSType string `mapstructure:"guest_os_type"` HardDriveInterface string `mapstructure:"hard_drive_interface"` HostInterfaces []string `mapstructure:"host_interfaces"` @@ -87,6 +88,10 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { b.config.DiskSize = 40000 } + if b.config.DiskType == "" { + b.config.DiskType = "expand" + } + if b.config.HardDriveInterface == "" { b.config.HardDriveInterface = "sata" } @@ -104,6 +109,17 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { b.config.VMName = fmt.Sprintf("packer-%s", b.config.PackerBuildName) } + if b.config.DiskType != "expand" && b.config.DiskType != "plain" { + errs = packer.MultiErrorAppend( + errs, errors.New("disk_type can only be expand, or plain")) + } + + if b.config.DiskType == "plain" && !b.config.SkipCompaction { + b.config.SkipCompaction = true + warnings = append(warnings, + "'skip_compaction' is enforced to be true for plain disks.") + } + if b.config.HardDriveInterface != "ide" && b.config.HardDriveInterface != "sata" && b.config.HardDriveInterface != "scsi" { errs = packer.MultiErrorAppend( errs, errors.New("hard_drive_interface can only be ide, sata, or scsi")) diff --git a/builder/parallels/iso/builder_test.go b/builder/parallels/iso/builder_test.go index 7be21bd59..b43da12f9 100644 --- a/builder/parallels/iso/builder_test.go +++ b/builder/parallels/iso/builder_test.go @@ -130,6 +130,61 @@ func TestBuilderPrepare_DiskSize(t *testing.T) { } } +func TestBuilderPrepare_DiskType(t *testing.T) { + var b Builder + config := testConfig() + + // Test a default disk_type + delete(config, "disk_type") + warns, err := b.Prepare(config) + if len(warns) > 0 { + t.Fatalf("bad: %#v", warns) + } + if err != nil { + t.Fatalf("err: %s", err) + } + + if b.config.DiskType != "expand" { + t.Fatalf("bad: %s", b.config.DiskType) + } + + // Test with a bad + config["disk_type"] = "fake" + b = Builder{} + warns, err = b.Prepare(config) + if len(warns) > 0 { + t.Fatalf("bad: %#v", warns) + } + if err == nil { + t.Fatal("should have error") + } + + // Test with plain disk with wrong setting for compaction + config["disk_type"] = "plain" + config["skip_compaction"] = false + b = Builder{} + warns, err = b.Prepare(config) + if len(warns) == 0 { + t.Fatalf("should have warning") + } + if err != nil { + t.Fatalf("should not have error: %s", err) + } + + // Test with plain disk with correct setting for compaction + config["disk_type"] = "plain" + config["skip_compaction"] = true + b = Builder{} + warns, err = b.Prepare(config) + if len(warns) > 0 { + t.Fatalf("bad: %#v", warns) + } + if err != nil { + t.Fatalf("should not have error: %s", err) + } + +} + func TestBuilderPrepare_HardDriveInterface(t *testing.T) { var b Builder config := testConfig() diff --git a/builder/parallels/iso/step_create_disk.go b/builder/parallels/iso/step_create_disk.go index e8f458f82..1737dcf6a 100644 --- a/builder/parallels/iso/step_create_disk.go +++ b/builder/parallels/iso/step_create_disk.go @@ -22,6 +22,7 @@ func (s *stepCreateDisk) Run(state multistep.StateBag) multistep.StepAction { command := []string{ "set", vmName, "--device-add", "hdd", + "--type", config.DiskType, "--size", strconv.FormatUint(uint64(config.DiskSize), 10), "--iface", config.HardDriveInterface, } diff --git a/builder/virtualbox/common/vbox_version_config.go b/builder/virtualbox/common/vbox_version_config.go index 9f1479445..5ece4d12a 100644 --- a/builder/virtualbox/common/vbox_version_config.go +++ b/builder/virtualbox/common/vbox_version_config.go @@ -5,12 +5,13 @@ import ( ) type VBoxVersionConfig struct { - VBoxVersionFile string `mapstructure:"virtualbox_version_file"` + VBoxVersionFile *string `mapstructure:"virtualbox_version_file"` } func (c *VBoxVersionConfig) Prepare(ctx *interpolate.Context) []error { - if c.VBoxVersionFile == "" { - c.VBoxVersionFile = ".vbox_version" + if c.VBoxVersionFile == nil { + default_file := ".vbox_version" + c.VBoxVersionFile = &default_file } return nil diff --git a/builder/virtualbox/common/vbox_version_config_test.go b/builder/virtualbox/common/vbox_version_config_test.go index d4cc85565..811ba60ff 100644 --- a/builder/virtualbox/common/vbox_version_config_test.go +++ b/builder/virtualbox/common/vbox_version_config_test.go @@ -15,19 +15,50 @@ func TestVBoxVersionConfigPrepare_BootWait(t *testing.T) { t.Fatalf("should not have error: %s", errs) } - if c.VBoxVersionFile != ".vbox_version" { - t.Fatalf("bad value: %s", c.VBoxVersionFile) + if *c.VBoxVersionFile != ".vbox_version" { + t.Fatalf("bad value: %s", *c.VBoxVersionFile) } // Test with a good one c = new(VBoxVersionConfig) - c.VBoxVersionFile = "foo" + filename := "foo" + c.VBoxVersionFile = &filename errs = c.Prepare(testConfigTemplate(t)) if len(errs) > 0 { t.Fatalf("should not have error: %s", errs) } - if c.VBoxVersionFile != "foo" { - t.Fatalf("bad value: %s", c.VBoxVersionFile) + if *c.VBoxVersionFile != "foo" { + t.Fatalf("bad value: %s", *c.VBoxVersionFile) + } +} + +func TestVBoxVersionConfigPrepare_empty(t *testing.T) { + var c *VBoxVersionConfig + var errs []error + + // Test with nil value + c = new(VBoxVersionConfig) + c.VBoxVersionFile = nil + errs = c.Prepare(testConfigTemplate(t)) + if len(errs) > 0 { + t.Fatalf("should not have error: %s", errs) + } + + if *c.VBoxVersionFile != ".vbox_version" { + t.Fatalf("bad value: %s", *c.VBoxVersionFile) + } + + // Test with empty name + c = new(VBoxVersionConfig) + filename := "" + c.VBoxVersionFile = &filename + errs = c.Prepare(testConfigTemplate(t)) + if len(errs) > 0 { + t.Fatalf("should not have error: %s", errs) + } + + if *c.VBoxVersionFile != "" { + t.Fatalf("bad value: %s", *c.VBoxVersionFile) } } diff --git a/builder/virtualbox/iso/builder.go b/builder/virtualbox/iso/builder.go index abe32590c..f9def2528 100644 --- a/builder/virtualbox/iso/builder.go +++ b/builder/virtualbox/iso/builder.go @@ -246,7 +246,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe WinRMPort: vboxcommon.SSHPort, }, &vboxcommon.StepUploadVersion{ - Path: b.config.VBoxVersionFile, + Path: *b.config.VBoxVersionFile, }, &vboxcommon.StepUploadGuestAdditions{ GuestAdditionsMode: b.config.GuestAdditionsMode, diff --git a/builder/virtualbox/ovf/builder.go b/builder/virtualbox/ovf/builder.go index b7d42183e..9607a0717 100644 --- a/builder/virtualbox/ovf/builder.go +++ b/builder/virtualbox/ovf/builder.go @@ -119,7 +119,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe WinRMPort: vboxcommon.SSHPort, }, &vboxcommon.StepUploadVersion{ - Path: b.config.VBoxVersionFile, + Path: *b.config.VBoxVersionFile, }, &vboxcommon.StepUploadGuestAdditions{ GuestAdditionsMode: b.config.GuestAdditionsMode, diff --git a/command/version.go b/command/version.go index 4fb07a90f..85b61844b 100644 --- a/command/version.go +++ b/command/version.go @@ -40,7 +40,7 @@ func (c *VersionCommand) Run(args []string) int { var versionString bytes.Buffer fmt.Fprintf(&versionString, "Packer v%s", c.Version) if c.VersionPrerelease != "" { - fmt.Fprintf(&versionString, ".%s", c.VersionPrerelease) + fmt.Fprintf(&versionString, "-%s", c.VersionPrerelease) if c.Revision != "" { fmt.Fprintf(&versionString, " (%s)", c.Revision) diff --git a/common/multistep_runner.go b/common/multistep_runner.go index a9b155086..512a3ecd4 100644 --- a/common/multistep_runner.go +++ b/common/multistep_runner.go @@ -61,6 +61,10 @@ type abortStep struct { ui packer.Ui } +func (s abortStep) InnerStepName() string { + return typeName(s.step) +} + func (s abortStep) Run(state multistep.StateBag) multistep.StepAction { return s.step.Run(state) } @@ -82,6 +86,10 @@ type askStep struct { ui packer.Ui } +func (s askStep) InnerStepName() string { + return typeName(s.step) +} + func (s askStep) Run(state multistep.StateBag) (action multistep.StepAction) { for { action = s.step.Run(state) diff --git a/communicator/ssh/communicator.go b/communicator/ssh/communicator.go index b32ad3ea7..e3082bac1 100644 --- a/communicator/ssh/communicator.go +++ b/communicator/ssh/communicator.go @@ -239,9 +239,9 @@ func (c *comm) newSession() (session *ssh.Session, err error) { } if c.client == nil { - err = errors.New("client not available") + return nil, errors.New("client not available") } else { - session, err = c.client.NewSession() + return c.client.NewSession() } } diff --git a/communicator/winrm/communicator.go b/communicator/winrm/communicator.go index c0676d01f..d3c6ee46d 100644 --- a/communicator/winrm/communicator.go +++ b/communicator/winrm/communicator.go @@ -5,6 +5,8 @@ import ( "io" "log" "os" + "path/filepath" + "strings" "sync" "github.com/masterzen/winrm" @@ -129,6 +131,9 @@ func (c *Communicator) Upload(path string, input io.Reader, _ *os.FileInfo) erro // UploadDir implementation of communicator.Communicator interface func (c *Communicator) UploadDir(dst string, src string, exclude []string) error { + if !strings.HasSuffix(src, "/") { + dst = fmt.Sprintf("%s\\%s", dst, filepath.Base(src)) + } log.Printf("Uploading dir '%s' to '%s'", src, dst) wcp, err := c.newCopyClient() if err != nil { diff --git a/provisioner/ansible/provisioner.go b/provisioner/ansible/provisioner.go index b57edd829..642d99bd8 100644 --- a/provisioner/ansible/provisioner.go +++ b/provisioner/ansible/provisioner.go @@ -144,7 +144,8 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { func (p *Provisioner) getVersion() error { out, err := exec.Command(p.config.Command, "--version").Output() if err != nil { - return err + return fmt.Errorf( + "Error running \"%s --version\": %s", p.config.Command, err.Error()) } versionRe := regexp.MustCompile(`\w (\d+\.\d+[.\d+]*)`) diff --git a/provisioner/ansible/provisioner_test.go b/provisioner/ansible/provisioner_test.go index 12a0f4e9b..13042c5e1 100644 --- a/provisioner/ansible/provisioner_test.go +++ b/provisioner/ansible/provisioner_test.go @@ -8,6 +8,7 @@ import ( "io/ioutil" "os" "path" + "strings" "testing" "github.com/mitchellh/packer/packer" @@ -258,6 +259,18 @@ func TestAnsibleGetVersion(t *testing.T) { } } +func TestAnsibleGetVersionError(t *testing.T) { + var p Provisioner + p.config.Command = "./test-fixtures/exit1" + err := p.getVersion() + if err == nil { + t.Fatal("Should return error") + } + if !strings.Contains(err.Error(), "./test-fixtures/exit1 --version") { + t.Fatal("Error message should include command name") + } +} + func TestAnsibleLongMessages(t *testing.T) { if os.Getenv("PACKER_ACC") == "" { t.Skip("This test is only run with PACKER_ACC=1 and it requires Ansible to be installed") diff --git a/provisioner/ansible/test-fixtures/exit1 b/provisioner/ansible/test-fixtures/exit1 new file mode 100755 index 000000000..2bb8d868b --- /dev/null +++ b/provisioner/ansible/test-fixtures/exit1 @@ -0,0 +1,3 @@ +#!/bin/sh + +exit 1 diff --git a/vendor/github.com/mitchellh/multistep/debug_runner.go b/vendor/github.com/mitchellh/multistep/debug_runner.go index 42f68f242..882009494 100644 --- a/vendor/github.com/mitchellh/multistep/debug_runner.go +++ b/vendor/github.com/mitchellh/multistep/debug_runner.go @@ -17,6 +17,13 @@ const ( DebugLocationBeforeCleanup ) +// StepWrapper is an interface that wrapped steps can implement to expose their +// inner step names to the debug runner. +type StepWrapper interface { + // InnerStepName should return the human readable name of the wrapped step. + InnerStepName() string +} + // DebugPauseFn is the type signature for the function that is called // whenever the DebugRunner pauses. It allows the caller time to // inspect the state of the multi-step sequence at a given step. @@ -56,8 +63,14 @@ func (r *DebugRunner) Run(state StateBag) { steps := make([]Step, len(r.Steps)*2) for i, step := range r.Steps { steps[i*2] = step + name := "" + if wrapped, ok := step.(StepWrapper); ok { + name = wrapped.InnerStepName() + } else { + name = reflect.Indirect(reflect.ValueOf(step)).Type().Name() + } steps[(i*2)+1] = &debugStepPause{ - reflect.Indirect(reflect.ValueOf(step)).Type().Name(), + name, pauseFn, } } diff --git a/vendor/vendor.json b/vendor/vendor.json index fd9ddfa96..ffe54fc9f 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -633,9 +633,10 @@ "revision": "281073eb9eb092240d33ef253c404f1cca550309" }, { - "checksumSHA1": "9Vh2o3Vs6HzI8P04ks4kvIpegco=", + "checksumSHA1": "5x1RX5m8SCkCRLyLL8wBc0qJpV8=", "path": "github.com/mitchellh/multistep", - "revision": "162146fc57112954184d90266f4733e900ed05a5" + "revision": "391576a156a54cfbb4cf5d5eda40cf6ffa3e3a4d", + "revisionTime": "2017-03-16T18:53:39Z" }, { "checksumSHA1": "VBo7ciCNRr7wNVFmBTW8sm4PQ14=", diff --git a/version/version.go b/version/version.go index 69a326a45..cae0ca8b1 100644 --- a/version/version.go +++ b/version/version.go @@ -9,18 +9,18 @@ import ( var GitCommit string // The main version number that is being run at the moment. -const Version = "1.0" +const Version = "1.1.0" // A pre-release marker for the version. If this is "" (empty string) // then it means that it is a final release. Otherwise, this is a pre-release // such as "dev" (in development), "beta", "rc1", etc. -const VersionPrerelease = "rc1" +const VersionPrerelease = "dev" func FormattedVersion() string { var versionString bytes.Buffer fmt.Fprintf(&versionString, "%s", Version) if VersionPrerelease != "" { - fmt.Fprintf(&versionString, ".%s", VersionPrerelease) + fmt.Fprintf(&versionString, "-%s", VersionPrerelease) if GitCommit != "" { fmt.Fprintf(&versionString, " (%s)", GitCommit) diff --git a/website/Gemfile b/website/Gemfile index 76e7fafef..08e6fe65e 100644 --- a/website/Gemfile +++ b/website/Gemfile @@ -1,6 +1,3 @@ source "https://rubygems.org" -gem "middleman-hashicorp", - git: "https://github.com/hashicorp/middleman-hashicorp.git" - -gem "htmlbeautifier" +gem "middleman-hashicorp", "0.3.13" diff --git a/website/Gemfile.lock b/website/Gemfile.lock index b5fe9cc0f..0811f6d62 100644 --- a/website/Gemfile.lock +++ b/website/Gemfile.lock @@ -1,30 +1,17 @@ -GIT - remote: https://github.com/hashicorp/middleman-hashicorp.git - revision: 462267352881543bbc5d2606f1ca17a6165ac8ec - specs: - middleman-hashicorp (0.3.5) - bootstrap-sass (~> 3.3) - builder (~> 3.2) - middleman (~> 3.4) - middleman-livereload (~> 3.4) - middleman-syntax (~> 3.0) - redcarpet (~> 3.3) - GEM remote: https://rubygems.org/ specs: - activesupport (4.2.7.1) + activesupport (4.2.8) i18n (~> 0.7) - json (~> 1.7, >= 1.7.7) minitest (~> 5.1) thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) - autoprefixer-rails (6.5.3) + autoprefixer-rails (6.7.6) execjs bootstrap-sass (3.3.7) autoprefixer-rails (>= 5.2.1) sass (>= 3.3.4) - builder (3.2.2) + builder (3.2.3) capybara (2.4.4) mime-types (>= 1.16) nokogiri (>= 1.3.3) @@ -35,7 +22,7 @@ GEM coffee-script (2.4.1) coffee-script-source execjs - coffee-script-source (1.10.0) + coffee-script-source (1.12.2) compass (1.0.3) chunky_png (~> 1.2) compass-core (~> 1.0.2) @@ -52,19 +39,18 @@ GEM eventmachine (>= 0.12.9) http_parser.rb (~> 0.6.0) erubis (2.7.0) - eventmachine (1.2.0.1) + eventmachine (1.2.3) execjs (2.7.0) - ffi (1.9.14) + ffi (1.9.18) haml (4.0.7) tilt hike (1.2.3) hooks (0.4.1) uber (~> 0.0.14) - htmlbeautifier (1.2.0) http_parser.rb (0.6.0) i18n (0.7.0) - json (1.8.3) - kramdown (1.12.0) + json (2.0.3) + kramdown (1.13.2) listen (3.0.8) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) @@ -91,6 +77,14 @@ GEM rack (>= 1.4.5, < 2.0) thor (>= 0.15.2, < 2.0) tilt (~> 1.4.1, < 2.0) + middleman-hashicorp (0.3.13) + bootstrap-sass (~> 3.3) + builder (~> 3.2) + middleman (~> 3.4) + middleman-livereload (~> 3.4) + middleman-syntax (~> 3.0) + redcarpet (~> 3.3) + turbolinks (~> 5.0) middleman-livereload (3.4.6) em-websocket (~> 0.5.1) middleman-core (>= 3.3) @@ -107,9 +101,9 @@ GEM mime-types-data (~> 3.2015) mime-types-data (3.2016.0521) mini_portile2 (2.1.0) - minitest (5.9.1) + minitest (5.10.1) multi_json (1.12.1) - nokogiri (1.6.8.1) + nokogiri (1.7.0.1) mini_portile2 (~> 2.1.0) padrino-helpers (0.12.8.1) i18n (~> 0.6, >= 0.6.7) @@ -123,11 +117,11 @@ GEM rack-test (0.6.3) rack (>= 1.0) rb-fsevent (0.9.8) - rb-inotify (0.9.7) + rb-inotify (0.9.8) ffi (>= 0.5.0) - redcarpet (3.3.4) - rouge (2.0.6) - sass (3.4.22) + redcarpet (3.4.0) + rouge (2.0.7) + sass (3.4.23) sprockets (2.12.4) hike (~> 1.2) multi_json (~> 1.0) @@ -138,9 +132,12 @@ GEM sprockets-sass (1.3.1) sprockets (~> 2.0) tilt (~> 1.1) - thor (0.19.1) - thread_safe (0.3.5) + thor (0.19.4) + thread_safe (0.3.6) tilt (1.4.1) + turbolinks (5.0.1) + turbolinks-source (~> 5) + turbolinks-source (5.0.0) tzinfo (1.2.2) thread_safe (~> 0.1) uber (0.0.15) @@ -154,8 +151,7 @@ PLATFORMS ruby DEPENDENCIES - htmlbeautifier - middleman-hashicorp! + middleman-hashicorp (= 0.3.13) BUNDLED WITH - 1.13.6 + 1.14.6 diff --git a/website/LICENSE.md b/website/LICENSE.md new file mode 100644 index 000000000..3189f43a6 --- /dev/null +++ b/website/LICENSE.md @@ -0,0 +1,10 @@ +# Proprietary License + +This license is temporary while a more official one is drafted. However, +this should make it clear: + +The text contents of this website are MPL 2.0 licensed. + +The design contents of this website are proprietary and may not be reproduced +or reused in any way other than to run the website locally. The license for +the design is owned solely by HashiCorp, Inc. diff --git a/website/Makefile b/website/Makefile index bdb362513..41fcf114e 100644 --- a/website/Makefile +++ b/website/Makefile @@ -1,22 +1,14 @@ -all: build +VERSION?="0.3.13" -init: - bundle +website: + @echo "==> Starting website in Docker..." + @docker run \ + --interactive \ + --rm \ + --tty \ + --publish "4567:4567" \ + --publish "35729:35729" \ + --volume "$(shell pwd):/website" \ + hashicorp/middleman-hashicorp:${VERSION} -docker-dev: - docker run -it --expose 4567 -p 4567:4567 -v "$(PWD)":/usr/src/app -w /usr/src/app ruby:2.3.1 \ - bash -c "apt-get update && apt-get -qy install curl git libgmp3-dev nodejs && \ - gem install bundler && bundle install && make dev" - -dev: init - PACKER_DISABLE_DOWNLOAD_FETCH=true PACKER_VERSION=1.0 bundle exec middleman server - -build: init - PACKER_DISABLE_DOWNLOAD_FETCH=true PACKER_VERSION=1.0 bundle exec middleman build - -format: - bundle exec htmlbeautifier -t 2 source/*.erb - bundle exec htmlbeautifier -t 2 source/layouts/*.erb - @pandoc -v > /dev/null || echo "pandoc must be installed in order to format markdown content" - pandoc -v > /dev/null && find . -iname "*.html.md" | xargs -I{} bash -c "pandoc -r markdown -w markdown --tab-stop=4 --atx-headers -s --columns=80 {} > {}.new"\; || true - pandoc -v > /dev/null && find . -iname "*.html.md" | xargs -I{} bash -c "mv {}.new {}"\; || true +.PHONY: website diff --git a/website/README.md b/website/README.md index e86ccc60e..23ff6b39c 100644 --- a/website/README.md +++ b/website/README.md @@ -1,33 +1,21 @@ # Packer Website -This subdirectory contains the entire source for the [Packer website](http://www.packer.io). -This is a [Middleman](http://middlemanapp.com) project, which builds a static -site from these source files. +This subdirectory contains the entire source for the [Packer Website][packer]. +This is a [Middleman][middleman] project, which builds a static site from these +source files. ## Contributions Welcome! If you find a typo or you feel like you can improve the HTML, CSS, or -JavaScript, we welcome contributions. Feel free to open issues or pull -requests like any normal GitHub project, and we'll merge it in. +JavaScript, we welcome contributions. Feel free to open issues or pull requests +like any normal GitHub project, and we'll merge it in. ## Running the Site Locally -Running the site locally is simple. Clone this repo and run the following -commands: +Running the site locally is simple. Clone this repo and run `make website`. -``` -make dev -``` +Then open up `http://localhost:4567`. Note that some URLs you may need to append +".html" to make them work (in the navigation). -Then open up `localhost:4567`. Note that some URLs you may need to append -".html" to make them work (in the navigation and such). - -## Keeping Tidy - -To keep the source code nicely formatted, there is a `make format` target. This -runs `htmlbeautify` and `pandoc` to reformat the source code so it's nicely formatted. - - make format - -Note that you will need to install pandoc yourself. `make format` will skip it -if you don't have it installed. \ No newline at end of file +[middleman]: https://www.middlemanapp.com +[packer]: https://www.packer.io diff --git a/website/Vagrantfile b/website/Vagrantfile deleted file mode 100644 index bd044c27f..000000000 --- a/website/Vagrantfile +++ /dev/null @@ -1,27 +0,0 @@ -# -*- mode: ruby -*- -# vi: set ft=ruby : - -$script = <