Merge branch 'master' of https://github.com/mitchellh/packer
49
CHANGELOG.md
|
@ -2,36 +2,63 @@
|
|||
|
||||
BACKWARDS INCOMPATIBILITIES:
|
||||
|
||||
* Packer now ships as a single binary, including plugins. If you install packer 0.9.0 over a previous packer installation, **you must delete all of the packer-* plugin files** or packer will load out-of-date plugins from disk.
|
||||
* Packer now ships as a single binary, including plugins. If you install
|
||||
packer 0.9.0 over a previous packer installation, **you must delete all of
|
||||
the packer-* plugin files** or packer will load out-of-date plugins from
|
||||
disk.
|
||||
* Release binaries are now provided via <https://releases.hashicorp.com>.
|
||||
* Packer 0.9.0 is now built with Go 1.5. Future versions will drop support for building with Go 1.4.
|
||||
* Packer 0.9.0 is now built with Go 1.5. Future versions will drop support
|
||||
for building with Go 1.4.
|
||||
|
||||
FEATURES:
|
||||
|
||||
* **Artifice post-processor**: Override packer artifacts during post-
|
||||
processing. This allows you to extract artifacts from a packer builder
|
||||
and use them with other post-processors like compress, docker, and Atlas.
|
||||
* **New `vmware-esxi` feature**: Packer can now export images from vCloud or vSphere during the build. [GH-1921]
|
||||
processing. This allows you to extract artifacts from a packer builder and
|
||||
use them with other post-processors like compress, docker, and Atlas.
|
||||
* **New `vmware-esxi` feature**: Packer can now export images from vCloud or
|
||||
vSphere during the build. [GH-1921]
|
||||
|
||||
IMPROVEMENTS:
|
||||
|
||||
* core: Packer plugins are now compiled into the main binary, reducing file size and build times, and making packer easier to install. The overall plugin architecture has not changed and third-party plugins can still be loaded from disk. Please make sure your plugins are up-to-date! [GH-2854]
|
||||
* core: Packer plugins are now compiled into the main binary, reducing file
|
||||
size and build times, and making packer easier to install. The overall
|
||||
plugin architecture has not changed and third-party plugins can still be
|
||||
loaded from disk. Please make sure your plugins are up-to-date! [GH-2854]
|
||||
* core: Packer now indicates line numbers for template parse errors [GH-2742]
|
||||
* core: Scripts are executed via `/usr/bin/env bash` instead of `/bin/bash` for broader compatibility. [GH-2913]
|
||||
* core: Scripts are executed via `/usr/bin/env bash` instead of `/bin/bash`
|
||||
for broader compatibility. [GH-2913]
|
||||
* core: `target_path` for builder downloads can now be specified. [GH-2600]
|
||||
* core: WinRM communicator now supports HTTPS protocol [GH-3061]
|
||||
* builder/amazon: Add support for `ebs_optimized` [GH-2806]
|
||||
* builder/amazon: You can now specify `0` for `spot_price` to switch to on demand instances [GH-2845]
|
||||
* builder/google: `account_file` can now be provided as a JSON string [GH-2811]
|
||||
* builder/amazon: You can now specify `0` for `spot_price` to switch to on
|
||||
demand instances [GH-2845]
|
||||
* builder/amazon: Added `ap-northeast-2` (Seoul) [GH-3056]
|
||||
* builder/amazon: packer will try to derive the AZ if only a subnet is
|
||||
specified [GH-3037]
|
||||
* builder/digitalocean: doubled instance wait timeouts to power off or
|
||||
shutdown (now 4 minutes) and to complete a snapshot (now 20 minutes)
|
||||
[GH-2939]
|
||||
* builder/google: `account_file` can now be provided as a JSON string
|
||||
[GH-2811]
|
||||
* builder/google: added support for `preemptible` instances [GH-2982]
|
||||
* builder/google: added support for static external IPs via `address` option
|
||||
[GH-3030]
|
||||
* builder/openstack: added retry on WaitForImage 404 [GH-3009]
|
||||
* builder/parallels: Improve support for Parallels 11 [GH-2662]
|
||||
* builder/parallels: Parallels disks are now compacted by default [GH-2731]
|
||||
* builder/parallels: Packer will look for Parallels in `/Applications/Parallels Desktop.app` if it is not detected automatically [GH-2839]
|
||||
* builder/parallels: Packer will look for Parallels in
|
||||
`/Applications/Parallels Desktop.app` if it is not detected automatically
|
||||
[GH-2839]
|
||||
* builder/docker: Now works remote hosts, such as boot2docker [GH-2846]
|
||||
* builder/qemu: qcow2 images are now compacted by default [GH-2748]
|
||||
* builder/qemu: qcow2 images can now be compressed [GH-2748]
|
||||
* builder/qemu: Now specifies `virtio-scsi` by default [GH-2422]
|
||||
* builder/qemu: Now checks for version-specific options [GH-2376]
|
||||
* builder/docker-import: Can now import Artifice artifacts [GH-2718]
|
||||
* builder/vmware-esxi: Now supports private key auth for remote builds via
|
||||
`remote_private_key_file` [GH-2912]
|
||||
* provisioner/chef: Now supports `encrypted_data_bag_secret_path` option
|
||||
[GH-2653]
|
||||
* provisioner/puppet: Now accepts the `extra_arguments` parameter [GH-2635]
|
||||
* post-processor/atlas: Added support for compile ID. [GH-2775]
|
||||
|
||||
|
@ -44,6 +71,8 @@ BUG FIXES:
|
|||
* builder/amazon: Use snapshot size when volume size is unspecified [GH-2480]
|
||||
* builder/parallels: Now supports interpolation in `prlctl_post` [GH-2828]
|
||||
* builder/vmware: `format` option is now read correctly [GH-2892]
|
||||
* builder/vmware-esxi: Correct endless loop in destroy validation logic
|
||||
[GH-2911]
|
||||
* provisioner/shell: No longer leaves temp scripts behind [GH-1536]
|
||||
* provisioner/winrm: Now waits for reboot to complete before continuing with provisioning [GH-2568]
|
||||
* post-processor/artifice: Fix truncation of files downloaded from Docker. [GH-2793]
|
||||
|
|
|
@ -30,6 +30,11 @@ func TestAMIConfigPrepare_regions(t *testing.T) {
|
|||
t.Fatalf("shouldn't have err: %s", err)
|
||||
}
|
||||
|
||||
c.AMIRegions = listEC2Regions()
|
||||
if err := c.Prepare(nil); err != nil {
|
||||
t.Fatalf("shouldn't have err: %s", err)
|
||||
}
|
||||
|
||||
c.AMIRegions = []string{"foo"}
|
||||
if err := c.Prepare(nil); err == nil {
|
||||
t.Fatal("should have error")
|
||||
|
|
|
@ -1,13 +1,26 @@
|
|||
package common
|
||||
|
||||
// IsValidRegion returns true if the supplied region is a valid AWS
|
||||
func listEC2Regions() []string {
|
||||
return []string{
|
||||
"ap-northeast-1",
|
||||
"ap-northeast-2",
|
||||
"ap-southeast-1",
|
||||
"ap-southeast-2",
|
||||
"cn-north-1",
|
||||
"eu-central-1",
|
||||
"eu-west-1",
|
||||
"sa-east-1",
|
||||
"us-east-1",
|
||||
"us-gov-west-1",
|
||||
"us-west-1",
|
||||
"us-west-2",
|
||||
}
|
||||
}
|
||||
|
||||
// ValidateRegion returns true if the supplied region is a valid AWS
|
||||
// region and false if it's not.
|
||||
func ValidateRegion(region string) bool {
|
||||
var regions = [11]string{"us-east-1", "us-west-2", "us-west-1", "eu-west-1",
|
||||
"eu-central-1", "ap-southeast-1", "ap-southeast-2", "ap-northeast-1",
|
||||
"sa-east-1", "cn-north-1", "us-gov-west-1"}
|
||||
|
||||
for _, valid := range regions {
|
||||
for _, valid := range listEC2Regions() {
|
||||
if region == valid {
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -72,6 +72,17 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
session := session.New(config)
|
||||
ec2conn := ec2.New(session)
|
||||
|
||||
// If the subnet is specified but not the AZ, try to determine the AZ automatically
|
||||
if b.config.SubnetId != "" && b.config.AvailabilityZone == "" {
|
||||
log.Printf("[INFO] Finding AZ for the given subnet '%s'", b.config.SubnetId)
|
||||
resp, err := ec2conn.DescribeSubnets(&ec2.DescribeSubnetsInput{SubnetIds: []*string{&b.config.SubnetId}})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.config.AvailabilityZone = *resp.Subnets[0].AvailabilityZone
|
||||
log.Printf("[INFO] AZ found: '%s'", b.config.AvailabilityZone)
|
||||
}
|
||||
|
||||
// Setup the state bag and initial state for the steps
|
||||
state := new(multistep.BasicStateBag)
|
||||
state.Put("config", b.config)
|
||||
|
|
|
@ -163,6 +163,17 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
session := session.New(config)
|
||||
ec2conn := ec2.New(session)
|
||||
|
||||
// If the subnet is specified but not the AZ, try to determine the AZ automatically
|
||||
if b.config.SubnetId != "" && b.config.AvailabilityZone == "" {
|
||||
log.Printf("[INFO] Finding AZ for the given subnet '%s'", b.config.SubnetId)
|
||||
resp, err := ec2conn.DescribeSubnets(&ec2.DescribeSubnetsInput{SubnetIds: []*string{&b.config.SubnetId}})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.config.AvailabilityZone = *resp.Subnets[0].AvailabilityZone
|
||||
log.Printf("[INFO] AZ found: '%s'", b.config.AvailabilityZone)
|
||||
}
|
||||
|
||||
// Setup the state bag and initial state for the steps
|
||||
state := new(multistep.BasicStateBag)
|
||||
state.Put("config", &b.config)
|
||||
|
|
|
@ -50,7 +50,7 @@ func (s *stepPowerOff) Run(state multistep.StateBag) multistep.StepAction {
|
|||
}
|
||||
|
||||
// Wait for the droplet to become unlocked for future steps
|
||||
if err := waitForDropletUnlocked(client, dropletId, 2*time.Minute); err != nil {
|
||||
if err := waitForDropletUnlocked(client, dropletId, 4*time.Minute); err != nil {
|
||||
// If we get an error the first time, actually report it
|
||||
err := fmt.Errorf("Error powering off droplet: %s", err)
|
||||
state.Put("error", err)
|
||||
|
|
|
@ -72,7 +72,7 @@ func (s *stepShutdown) Run(state multistep.StateBag) multistep.StepAction {
|
|||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
if err := waitForDropletUnlocked(client, dropletId, 2*time.Minute); err != nil {
|
||||
if err := waitForDropletUnlocked(client, dropletId, 4*time.Minute); err != nil {
|
||||
// If we get an error the first time, actually report it
|
||||
err := fmt.Errorf("Error shutting down droplet: %s", err)
|
||||
state.Put("error", err)
|
||||
|
|
|
@ -30,8 +30,8 @@ func (s *stepSnapshot) Run(state multistep.StateBag) multistep.StepAction {
|
|||
|
||||
// Wait for the droplet to become unlocked first. For snapshots
|
||||
// this can end up taking quite a long time, so we hardcode this to
|
||||
// 10 minutes.
|
||||
if err := waitForDropletUnlocked(client, dropletId, 10*time.Minute); err != nil {
|
||||
// 20 minutes.
|
||||
if err := waitForDropletUnlocked(client, dropletId, 20*time.Minute); err != nil {
|
||||
// If we get an error the first time, actually report it
|
||||
err := fmt.Errorf("Error shutting down droplet: %s", err)
|
||||
state.Put("error", err)
|
||||
|
|
|
@ -31,6 +31,7 @@ type Config struct {
|
|||
MachineType string `mapstructure:"machine_type"`
|
||||
Metadata map[string]string `mapstructure:"metadata"`
|
||||
Network string `mapstructure:"network"`
|
||||
Address string `mapstructure:"address"`
|
||||
Preemptible bool `mapstructure:"preemptible"`
|
||||
SourceImage string `mapstructure:"source_image"`
|
||||
SourceImageProjectId string `mapstructure:"source_image_project_id"`
|
||||
|
|
|
@ -47,6 +47,7 @@ type InstanceConfig struct {
|
|||
Metadata map[string]string
|
||||
Name string
|
||||
Network string
|
||||
Address string
|
||||
Preemptible bool
|
||||
Tags []string
|
||||
Zone string
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"golang.org/x/oauth2/google"
|
||||
"golang.org/x/oauth2/jwt"
|
||||
"google.golang.org/api/compute/v1"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// driverGCE is a Driver implementation that actually talks to GCE.
|
||||
|
@ -214,6 +215,23 @@ func (d *driverGCE) RunInstance(c *InstanceConfig) (<-chan error, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// If given a regional ip, get it
|
||||
accessconfig := compute.AccessConfig{
|
||||
Name: "AccessConfig created by Packer",
|
||||
Type: "ONE_TO_ONE_NAT",
|
||||
}
|
||||
|
||||
if c.Address != "" {
|
||||
d.ui.Message(fmt.Sprintf("Looking up address: %s", c.Address))
|
||||
region_url := strings.Split(zone.Region, "/")
|
||||
region := region_url[len(region_url)-1]
|
||||
address, err := d.service.Addresses.Get(d.projectId, region, c.Address).Do()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
accessconfig.NatIP = address.Address
|
||||
}
|
||||
|
||||
// Build up the metadata
|
||||
metadata := make([]*compute.MetadataItems, len(c.Metadata))
|
||||
for k, v := range c.Metadata {
|
||||
|
@ -247,10 +265,7 @@ func (d *driverGCE) RunInstance(c *InstanceConfig) (<-chan error, error) {
|
|||
NetworkInterfaces: []*compute.NetworkInterface{
|
||||
&compute.NetworkInterface{
|
||||
AccessConfigs: []*compute.AccessConfig{
|
||||
&compute.AccessConfig{
|
||||
Name: "AccessConfig created by Packer",
|
||||
Type: "ONE_TO_ONE_NAT",
|
||||
},
|
||||
&accessconfig,
|
||||
},
|
||||
Network: network.SelfLink,
|
||||
},
|
||||
|
|
|
@ -59,6 +59,7 @@ func (s *StepCreateInstance) Run(state multistep.StateBag) multistep.StepAction
|
|||
Metadata: config.getInstanceMetadata(sshPublicKey),
|
||||
Name: name,
|
||||
Network: config.Network,
|
||||
Address: config.Address,
|
||||
Preemptible: config.Preemptible,
|
||||
Tags: config.Tags,
|
||||
Zone: config.Zone,
|
||||
|
|
|
@ -62,12 +62,20 @@ func (s *stepCreateImage) Cleanup(multistep.StateBag) {
|
|||
|
||||
// WaitForImage waits for the given Image ID to become ready.
|
||||
func WaitForImage(client *gophercloud.ServiceClient, imageId string) error {
|
||||
maxNumErrors := 10
|
||||
numErrors := 0
|
||||
|
||||
for {
|
||||
image, err := images.Get(client, imageId).Extract()
|
||||
if err != nil {
|
||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
||||
if ok && errCode.Actual == 500 {
|
||||
log.Printf("[ERROR] 500 error received, will ignore and retry: %s", err)
|
||||
if ok && (errCode.Actual == 500 || errCode.Actual == 404) {
|
||||
numErrors++
|
||||
if numErrors >= maxNumErrors {
|
||||
log.Printf("[ERROR] Maximum number of errors (%d) reached; failing with: %s", numErrors, err)
|
||||
return err
|
||||
}
|
||||
log.Printf("[ERROR] %d error received, will ignore and retry: %s", errCode.Actual, err)
|
||||
time.Sleep(2 * time.Second)
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ type Builder struct {
|
|||
|
||||
type Config struct {
|
||||
common.PackerConfig `mapstructure:",squash"`
|
||||
common.HTTPConfig `mapstructure:",squash"`
|
||||
common.ISOConfig `mapstructure:",squash"`
|
||||
parallelscommon.FloppyConfig `mapstructure:",squash"`
|
||||
parallelscommon.OutputConfig `mapstructure:",squash"`
|
||||
|
@ -39,9 +40,6 @@ type Config struct {
|
|||
GuestOSType string `mapstructure:"guest_os_type"`
|
||||
HardDriveInterface string `mapstructure:"hard_drive_interface"`
|
||||
HostInterfaces []string `mapstructure:"host_interfaces"`
|
||||
HTTPDir string `mapstructure:"http_directory"`
|
||||
HTTPPortMin uint `mapstructure:"http_port_min"`
|
||||
HTTPPortMax uint `mapstructure:"http_port_max"`
|
||||
SkipCompaction bool `mapstructure:"skip_compaction"`
|
||||
VMName string `mapstructure:"vm_name"`
|
||||
|
||||
|
@ -77,6 +75,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
|||
warnings = append(warnings, isoWarnings...)
|
||||
errs = packer.MultiErrorAppend(errs, isoErrs...)
|
||||
|
||||
errs = packer.MultiErrorAppend(errs, b.config.HTTPConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.FloppyConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, b.config.OutputConfig.Prepare(&b.config.ctx, &b.config.PackerConfig)...)
|
||||
|
@ -110,14 +109,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
|||
"Run it to see all available values: `prlctl create x -d list` ")
|
||||
}
|
||||
|
||||
if b.config.HTTPPortMin == 0 {
|
||||
b.config.HTTPPortMin = 8000
|
||||
}
|
||||
|
||||
if b.config.HTTPPortMax == 0 {
|
||||
b.config.HTTPPortMax = 9000
|
||||
}
|
||||
|
||||
if len(b.config.HostInterfaces) == 0 {
|
||||
b.config.HostInterfaces = []string{"en0", "en1", "en2", "en3", "en4", "en5", "en6", "en7",
|
||||
"en8", "en9", "ppp0", "ppp1", "ppp2"}
|
||||
|
@ -132,11 +123,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
|||
errs, errors.New("hard_drive_interface can only be ide, sata, or scsi"))
|
||||
}
|
||||
|
||||
if b.config.HTTPPortMin > b.config.HTTPPortMax {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, errors.New("http_port_min must be less than http_port_max"))
|
||||
}
|
||||
|
||||
// Warnings
|
||||
if b.config.ShutdownCommand == "" {
|
||||
warnings = append(warnings,
|
||||
|
@ -185,7 +171,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
&common.StepCreateFloppy{
|
||||
Files: b.config.FloppyFiles,
|
||||
},
|
||||
new(stepHTTPServer),
|
||||
&common.StepHTTPServer{
|
||||
HTTPDir: b.config.HTTPDir,
|
||||
HTTPPortMin: b.config.HTTPPortMin,
|
||||
HTTPPortMax: b.config.HTTPPortMax,
|
||||
},
|
||||
new(stepCreateVM),
|
||||
new(stepCreateDisk),
|
||||
new(stepSetBootOrder),
|
||||
|
|
|
@ -138,45 +138,6 @@ func TestBuilderPrepare_HardDriveInterface(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestBuilderPrepare_HTTPPort(t *testing.T) {
|
||||
var b Builder
|
||||
config := testConfig()
|
||||
|
||||
// Bad
|
||||
config["http_port_min"] = 1000
|
||||
config["http_port_max"] = 500
|
||||
warns, err := b.Prepare(config)
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
|
||||
// Bad
|
||||
config["http_port_min"] = -500
|
||||
b = Builder{}
|
||||
warns, err = b.Prepare(config)
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
|
||||
// Good
|
||||
config["http_port_min"] = 500
|
||||
config["http_port_max"] = 1000
|
||||
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_InvalidKey(t *testing.T) {
|
||||
var b Builder
|
||||
config := testConfig()
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
package iso
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/mitchellh/multistep"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// This step creates and runs the HTTP server that is serving files from the
|
||||
// directory specified by the 'http_directory` configuration parameter in the
|
||||
// template.
|
||||
//
|
||||
// Uses:
|
||||
// config *config
|
||||
// ui packer.Ui
|
||||
//
|
||||
// Produces:
|
||||
// http_port int - The port the HTTP server started on.
|
||||
type stepHTTPServer struct {
|
||||
l net.Listener
|
||||
}
|
||||
|
||||
func (s *stepHTTPServer) Run(state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("config").(*Config)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
var httpPort uint = 0
|
||||
if config.HTTPDir == "" {
|
||||
state.Put("http_port", httpPort)
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
// Find an available TCP port for our HTTP server
|
||||
var httpAddr string
|
||||
portRange := int(config.HTTPPortMax - config.HTTPPortMin)
|
||||
for {
|
||||
var err error
|
||||
var offset uint = 0
|
||||
|
||||
if portRange > 0 {
|
||||
// Intn will panic if portRange == 0, so we do a check.
|
||||
offset = uint(rand.Intn(portRange))
|
||||
}
|
||||
|
||||
httpPort = offset + config.HTTPPortMin
|
||||
httpAddr = fmt.Sprintf(":%d", httpPort)
|
||||
log.Printf("Trying port: %d", httpPort)
|
||||
s.l, err = net.Listen("tcp", httpAddr)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf("Starting HTTP server on port %d", httpPort))
|
||||
|
||||
// Start the HTTP server and run it in the background
|
||||
fileServer := http.FileServer(http.Dir(config.HTTPDir))
|
||||
server := &http.Server{Addr: httpAddr, Handler: fileServer}
|
||||
go server.Serve(s.l)
|
||||
|
||||
// Save the address into the state so it can be accessed in the future
|
||||
state.Put("http_port", httpPort)
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *stepHTTPServer) Cleanup(multistep.StateBag) {
|
||||
if s.l != nil {
|
||||
// Close the listener so that the HTTP server stops
|
||||
s.l.Close()
|
||||
}
|
||||
}
|
|
@ -79,6 +79,7 @@ type Builder struct {
|
|||
|
||||
type Config struct {
|
||||
common.PackerConfig `mapstructure:",squash"`
|
||||
common.HTTPConfig `mapstructure:",squash"`
|
||||
common.ISOConfig `mapstructure:",squash"`
|
||||
Comm communicator.Config `mapstructure:",squash"`
|
||||
|
||||
|
@ -94,9 +95,6 @@ type Config struct {
|
|||
Format string `mapstructure:"format"`
|
||||
Headless bool `mapstructure:"headless"`
|
||||
DiskImage bool `mapstructure:"disk_image"`
|
||||
HTTPDir string `mapstructure:"http_directory"`
|
||||
HTTPPortMin uint `mapstructure:"http_port_min"`
|
||||
HTTPPortMax uint `mapstructure:"http_port_max"`
|
||||
MachineType string `mapstructure:"machine_type"`
|
||||
NetDevice string `mapstructure:"net_device"`
|
||||
OutputDir string `mapstructure:"output_directory"`
|
||||
|
@ -160,14 +158,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
|||
}
|
||||
}
|
||||
|
||||
if b.config.HTTPPortMin == 0 {
|
||||
b.config.HTTPPortMin = 8000
|
||||
}
|
||||
|
||||
if b.config.HTTPPortMax == 0 {
|
||||
b.config.HTTPPortMax = 9000
|
||||
}
|
||||
|
||||
if b.config.MachineType == "" {
|
||||
b.config.MachineType = "pc"
|
||||
}
|
||||
|
@ -235,6 +225,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
|||
warnings = append(warnings, isoWarnings...)
|
||||
errs = packer.MultiErrorAppend(errs, isoErrs...)
|
||||
|
||||
errs = packer.MultiErrorAppend(errs, b.config.HTTPConfig.Prepare(&b.config.ctx)...)
|
||||
if es := b.config.Comm.Prepare(&b.config.ctx); len(es) > 0 {
|
||||
errs = packer.MultiErrorAppend(errs, es...)
|
||||
}
|
||||
|
@ -274,11 +265,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
|||
errs, errors.New("unrecognized disk cache type"))
|
||||
}
|
||||
|
||||
if b.config.HTTPPortMin > b.config.HTTPPortMax {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, errors.New("http_port_min must be less than http_port_max"))
|
||||
}
|
||||
|
||||
if !b.config.PackerForce {
|
||||
if _, err := os.Stat(b.config.OutputDir); err == nil {
|
||||
errs = packer.MultiErrorAppend(
|
||||
|
@ -357,7 +343,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
new(stepCreateDisk),
|
||||
new(stepCopyDisk),
|
||||
new(stepResizeDisk),
|
||||
new(stepHTTPServer),
|
||||
&common.StepHTTPServer{
|
||||
HTTPDir: b.config.HTTPDir,
|
||||
HTTPPortMin: b.config.HTTPPortMin,
|
||||
HTTPPortMax: b.config.HTTPPortMax,
|
||||
},
|
||||
new(stepForwardSSH),
|
||||
new(stepConfigureVNC),
|
||||
steprun,
|
||||
|
|
|
@ -206,45 +206,6 @@ func TestBuilderPrepare_DiskSize(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestBuilderPrepare_HTTPPort(t *testing.T) {
|
||||
var b Builder
|
||||
config := testConfig()
|
||||
|
||||
// Bad
|
||||
config["http_port_min"] = 1000
|
||||
config["http_port_max"] = 500
|
||||
warns, err := b.Prepare(config)
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
|
||||
// Bad
|
||||
config["http_port_min"] = -500
|
||||
b = Builder{}
|
||||
warns, err = b.Prepare(config)
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
|
||||
// Good
|
||||
config["http_port_min"] = 500
|
||||
config["http_port_max"] = 1000
|
||||
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_Format(t *testing.T) {
|
||||
var b Builder
|
||||
config := testConfig()
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
package qemu
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/mitchellh/multistep"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// This step creates and runs the HTTP server that is serving files from the
|
||||
// directory specified by the 'http_directory` configuration parameter in the
|
||||
// template.
|
||||
//
|
||||
// Uses:
|
||||
// config *config
|
||||
// ui packer.Ui
|
||||
//
|
||||
// Produces:
|
||||
// http_port int - The port the HTTP server started on.
|
||||
type stepHTTPServer struct {
|
||||
l net.Listener
|
||||
}
|
||||
|
||||
func (s *stepHTTPServer) Run(state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("config").(*Config)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
var httpPort uint = 0
|
||||
if config.HTTPDir == "" {
|
||||
state.Put("http_port", httpPort)
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
// Find an available TCP port for our HTTP server
|
||||
var httpAddr string
|
||||
portRange := int(config.HTTPPortMax - config.HTTPPortMin)
|
||||
for {
|
||||
var err error
|
||||
var offset uint = 0
|
||||
|
||||
if portRange > 0 {
|
||||
// Intn will panic if portRange == 0, so we do a check.
|
||||
offset = uint(rand.Intn(portRange))
|
||||
}
|
||||
|
||||
httpPort = offset + config.HTTPPortMin
|
||||
httpAddr = fmt.Sprintf(":%d", httpPort)
|
||||
log.Printf("Trying port: %d", httpPort)
|
||||
s.l, err = net.Listen("tcp", httpAddr)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf("Starting HTTP server on port %d", httpPort))
|
||||
|
||||
// Start the HTTP server and run it in the background
|
||||
fileServer := http.FileServer(http.Dir(config.HTTPDir))
|
||||
server := &http.Server{Addr: httpAddr, Handler: fileServer}
|
||||
go server.Serve(s.l)
|
||||
|
||||
// Save the address into the state so it can be accessed in the future
|
||||
state.Put("http_port", httpPort)
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *stepHTTPServer) Cleanup(multistep.StateBag) {
|
||||
if s.l != nil {
|
||||
// Close the listener so that the HTTP server stops
|
||||
s.l.Close()
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
|
@ -12,10 +11,6 @@ type RunConfig struct {
|
|||
Headless bool `mapstructure:"headless"`
|
||||
RawBootWait string `mapstructure:"boot_wait"`
|
||||
|
||||
HTTPDir string `mapstructure:"http_directory"`
|
||||
HTTPPortMin uint `mapstructure:"http_port_min"`
|
||||
HTTPPortMax uint `mapstructure:"http_port_max"`
|
||||
|
||||
BootWait time.Duration ``
|
||||
}
|
||||
|
||||
|
@ -24,14 +19,6 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error {
|
|||
c.RawBootWait = "10s"
|
||||
}
|
||||
|
||||
if c.HTTPPortMin == 0 {
|
||||
c.HTTPPortMin = 8000
|
||||
}
|
||||
|
||||
if c.HTTPPortMax == 0 {
|
||||
c.HTTPPortMax = 9000
|
||||
}
|
||||
|
||||
var errs []error
|
||||
var err error
|
||||
c.BootWait, err = time.ParseDuration(c.RawBootWait)
|
||||
|
@ -39,10 +26,5 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error {
|
|||
errs = append(errs, fmt.Errorf("Failed parsing boot_wait: %s", err))
|
||||
}
|
||||
|
||||
if c.HTTPPortMin > c.HTTPPortMax {
|
||||
errs = append(errs,
|
||||
errors.New("http_port_min must be less than http_port_max"))
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ type Builder struct {
|
|||
|
||||
type Config struct {
|
||||
common.PackerConfig `mapstructure:",squash"`
|
||||
common.HTTPConfig `mapstructure:",squash"`
|
||||
common.ISOConfig `mapstructure:",squash"`
|
||||
vboxcommon.ExportConfig `mapstructure:",squash"`
|
||||
vboxcommon.ExportOpts `mapstructure:",squash"`
|
||||
|
@ -81,6 +82,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
|||
errs = packer.MultiErrorAppend(errs, b.config.FloppyConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, b.config.OutputConfig.Prepare(&b.config.ctx, &b.config.PackerConfig)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.HTTPConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.ShutdownConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.SSHConfig.Prepare(&b.config.ctx)...)
|
||||
|
@ -194,7 +196,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
&common.StepCreateFloppy{
|
||||
Files: b.config.FloppyFiles,
|
||||
},
|
||||
&vboxcommon.StepHTTPServer{
|
||||
&common.StepHTTPServer{
|
||||
HTTPDir: b.config.HTTPDir,
|
||||
HTTPPortMin: b.config.HTTPPortMin,
|
||||
HTTPPortMax: b.config.HTTPPortMax,
|
||||
|
|
|
@ -260,45 +260,6 @@ func TestBuilderPrepare_HardDriveInterface(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestBuilderPrepare_HTTPPort(t *testing.T) {
|
||||
var b Builder
|
||||
config := testConfig()
|
||||
|
||||
// Bad
|
||||
config["http_port_min"] = 1000
|
||||
config["http_port_max"] = 500
|
||||
warns, err := b.Prepare(config)
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
|
||||
// Bad
|
||||
config["http_port_min"] = -500
|
||||
b = Builder{}
|
||||
warns, err = b.Prepare(config)
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
|
||||
// Good
|
||||
config["http_port_min"] = 500
|
||||
config["http_port_max"] = 1000
|
||||
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_InvalidKey(t *testing.T) {
|
||||
var b Builder
|
||||
config := testConfig()
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
package iso
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/mitchellh/multistep"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// This step creates and runs the HTTP server that is serving files from the
|
||||
// directory specified by the 'http_directory` configuration parameter in the
|
||||
// template.
|
||||
//
|
||||
// Uses:
|
||||
// config *config
|
||||
// ui packer.Ui
|
||||
//
|
||||
// Produces:
|
||||
// http_port int - The port the HTTP server started on.
|
||||
type stepHTTPServer struct {
|
||||
l net.Listener
|
||||
}
|
||||
|
||||
func (s *stepHTTPServer) Run(state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("config").(*Config)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
var httpPort uint = 0
|
||||
if config.HTTPDir == "" {
|
||||
state.Put("http_port", httpPort)
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
// Find an available TCP port for our HTTP server
|
||||
var httpAddr string
|
||||
portRange := int(config.HTTPPortMax - config.HTTPPortMin)
|
||||
for {
|
||||
var err error
|
||||
var offset uint = 0
|
||||
|
||||
if portRange > 0 {
|
||||
// Intn will panic if portRange == 0, so we do a check.
|
||||
offset = uint(rand.Intn(portRange))
|
||||
}
|
||||
|
||||
httpPort = offset + config.HTTPPortMin
|
||||
httpAddr = fmt.Sprintf(":%d", httpPort)
|
||||
log.Printf("Trying port: %d", httpPort)
|
||||
s.l, err = net.Listen("tcp", httpAddr)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf("Starting HTTP server on port %d", httpPort))
|
||||
|
||||
// Start the HTTP server and run it in the background
|
||||
fileServer := http.FileServer(http.Dir(config.HTTPDir))
|
||||
server := &http.Server{Addr: httpAddr, Handler: fileServer}
|
||||
go server.Serve(s.l)
|
||||
|
||||
// Save the address into the state so it can be accessed in the future
|
||||
state.Put("http_port", httpPort)
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *stepHTTPServer) Cleanup(multistep.StateBag) {
|
||||
if s.l != nil {
|
||||
// Close the listener so that the HTTP server stops
|
||||
s.l.Close()
|
||||
}
|
||||
}
|
|
@ -57,7 +57,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
&common.StepCreateFloppy{
|
||||
Files: b.config.FloppyFiles,
|
||||
},
|
||||
&vboxcommon.StepHTTPServer{
|
||||
&common.StepHTTPServer{
|
||||
HTTPDir: b.config.HTTPDir,
|
||||
HTTPPortMin: b.config.HTTPPortMin,
|
||||
HTTPPortMax: b.config.HTTPPortMax,
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
// Config is the configuration structure for the builder.
|
||||
type Config struct {
|
||||
common.PackerConfig `mapstructure:",squash"`
|
||||
common.HTTPConfig `mapstructure:",squash"`
|
||||
vboxcommon.ExportConfig `mapstructure:",squash"`
|
||||
vboxcommon.ExportOpts `mapstructure:",squash"`
|
||||
vboxcommon.FloppyConfig `mapstructure:",squash"`
|
||||
|
@ -77,6 +78,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
|
|||
errs = packer.MultiErrorAppend(errs, c.ExportConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.ExportOpts.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.FloppyConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.HTTPConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.OutputConfig.Prepare(&c.ctx, &c.PackerConfig)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.RunConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.ShutdownConfig.Prepare(&c.ctx)...)
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
|
@ -12,10 +11,6 @@ type RunConfig struct {
|
|||
Headless bool `mapstructure:"headless"`
|
||||
RawBootWait string `mapstructure:"boot_wait"`
|
||||
|
||||
HTTPDir string `mapstructure:"http_directory"`
|
||||
HTTPPortMin uint `mapstructure:"http_port_min"`
|
||||
HTTPPortMax uint `mapstructure:"http_port_max"`
|
||||
|
||||
VNCPortMin uint `mapstructure:"vnc_port_min"`
|
||||
VNCPortMax uint `mapstructure:"vnc_port_max"`
|
||||
|
||||
|
@ -27,14 +22,6 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error {
|
|||
c.RawBootWait = "10s"
|
||||
}
|
||||
|
||||
if c.HTTPPortMin == 0 {
|
||||
c.HTTPPortMin = 8000
|
||||
}
|
||||
|
||||
if c.HTTPPortMax == 0 {
|
||||
c.HTTPPortMax = 9000
|
||||
}
|
||||
|
||||
if c.VNCPortMin == 0 {
|
||||
c.VNCPortMin = 5900
|
||||
}
|
||||
|
@ -53,11 +40,6 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error {
|
|||
}
|
||||
}
|
||||
|
||||
if c.HTTPPortMin > c.HTTPPortMax {
|
||||
errs = append(errs,
|
||||
errors.New("http_port_min must be less than http_port_max"))
|
||||
}
|
||||
|
||||
if c.VNCPortMin > c.VNCPortMax {
|
||||
errs = append(
|
||||
errs, fmt.Errorf("vnc_port_min must be less than vnc_port_max"))
|
||||
|
|
|
@ -1,78 +0,0 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/mitchellh/multistep"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// This step creates and runs the HTTP server that is serving files from the
|
||||
// directory specified by the 'http_directory` configuration parameter in the
|
||||
// template.
|
||||
//
|
||||
// Uses:
|
||||
// ui packer.Ui
|
||||
//
|
||||
// Produces:
|
||||
// http_port int - The port the HTTP server started on.
|
||||
type StepHTTPServer struct {
|
||||
HTTPDir string
|
||||
HTTPPortMin uint
|
||||
HTTPPortMax uint
|
||||
|
||||
l net.Listener
|
||||
}
|
||||
|
||||
func (s *StepHTTPServer) Run(state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
var httpPort uint = 0
|
||||
if s.HTTPDir == "" {
|
||||
state.Put("http_port", httpPort)
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
// Find an available TCP port for our HTTP server
|
||||
var httpAddr string
|
||||
portRange := int(s.HTTPPortMax - s.HTTPPortMin)
|
||||
for {
|
||||
var err error
|
||||
var offset uint = 0
|
||||
|
||||
if portRange > 0 {
|
||||
// Intn will panic if portRange == 0, so we do a check.
|
||||
offset = uint(rand.Intn(portRange))
|
||||
}
|
||||
|
||||
httpPort = offset + s.HTTPPortMin
|
||||
httpAddr = fmt.Sprintf("0.0.0.0:%d", httpPort)
|
||||
log.Printf("Trying port: %d", httpPort)
|
||||
s.l, err = net.Listen("tcp", httpAddr)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf("Starting HTTP server on port %d", httpPort))
|
||||
|
||||
// Start the HTTP server and run it in the background
|
||||
fileServer := http.FileServer(http.Dir(s.HTTPDir))
|
||||
server := &http.Server{Addr: httpAddr, Handler: fileServer}
|
||||
go server.Serve(s.l)
|
||||
|
||||
// Save the address into the state so it can be accessed in the future
|
||||
state.Put("http_port", httpPort)
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepHTTPServer) Cleanup(multistep.StateBag) {
|
||||
if s.l != nil {
|
||||
// Close the listener so that the HTTP server stops
|
||||
s.l.Close()
|
||||
}
|
||||
}
|
|
@ -26,6 +26,7 @@ type Builder struct {
|
|||
|
||||
type Config struct {
|
||||
common.PackerConfig `mapstructure:",squash"`
|
||||
common.HTTPConfig `mapstructure:",squash"`
|
||||
common.ISOConfig `mapstructure:",squash"`
|
||||
vmwcommon.DriverConfig `mapstructure:",squash"`
|
||||
vmwcommon.OutputConfig `mapstructure:",squash"`
|
||||
|
@ -57,6 +58,7 @@ type Config struct {
|
|||
RemotePort uint `mapstructure:"remote_port"`
|
||||
RemoteUser string `mapstructure:"remote_username"`
|
||||
RemotePassword string `mapstructure:"remote_password"`
|
||||
RemotePrivateKey string `mapstructure:"remote_private_key_file"`
|
||||
|
||||
ctx interpolate.Context
|
||||
}
|
||||
|
@ -83,6 +85,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
|||
isoWarnings, isoErrs := b.config.ISOConfig.Prepare(&b.config.ctx)
|
||||
warnings = append(warnings, isoWarnings...)
|
||||
errs = packer.MultiErrorAppend(errs, isoErrs...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.HTTPConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.DriverConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs,
|
||||
b.config.OutputConfig.Prepare(&b.config.ctx, &b.config.PackerConfig)...)
|
||||
|
@ -244,7 +247,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
CustomData: b.config.VMXData,
|
||||
},
|
||||
&vmwcommon.StepSuppressMessages{},
|
||||
&vmwcommon.StepHTTPServer{
|
||||
&common.StepHTTPServer{
|
||||
HTTPDir: b.config.HTTPDir,
|
||||
HTTPPortMin: b.config.HTTPPortMin,
|
||||
HTTPPortMax: b.config.HTTPPortMax,
|
||||
|
|
|
@ -152,45 +152,6 @@ func TestBuilderPrepare_Format(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestBuilderPrepare_HTTPPort(t *testing.T) {
|
||||
var b Builder
|
||||
config := testConfig()
|
||||
|
||||
// Bad
|
||||
config["http_port_min"] = 1000
|
||||
config["http_port_max"] = 500
|
||||
warns, err := b.Prepare(config)
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
|
||||
// Bad
|
||||
config["http_port_min"] = -500
|
||||
b = Builder{}
|
||||
warns, err = b.Prepare(config)
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
|
||||
// Good
|
||||
config["http_port_min"] = 500
|
||||
config["http_port_max"] = 1000
|
||||
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_InvalidKey(t *testing.T) {
|
||||
var b Builder
|
||||
config := testConfig()
|
||||
|
|
|
@ -21,6 +21,7 @@ func NewDriver(config *Config) (vmwcommon.Driver, error) {
|
|||
Port: config.RemotePort,
|
||||
Username: config.RemoteUser,
|
||||
Password: config.RemotePassword,
|
||||
PrivateKey: config.RemotePrivateKey,
|
||||
Datastore: config.RemoteDatastore,
|
||||
CacheDatastore: config.RemoteCacheDatastore,
|
||||
CacheDirectory: config.RemoteCacheDirectory,
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/mitchellh/multistep"
|
||||
commonssh "github.com/mitchellh/packer/common/ssh"
|
||||
"github.com/mitchellh/packer/communicator/ssh"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
gossh "golang.org/x/crypto/ssh"
|
||||
|
@ -27,6 +28,7 @@ type ESX5Driver struct {
|
|||
Port uint
|
||||
Username string
|
||||
Password string
|
||||
PrivateKey string
|
||||
Datastore string
|
||||
CacheDatastore string
|
||||
CacheDirectory string
|
||||
|
@ -340,7 +342,15 @@ func (d *ESX5Driver) connect() error {
|
|||
ssh.PasswordKeyboardInteractive(d.Password)),
|
||||
}
|
||||
|
||||
// TODO(dougm) KeyPath support
|
||||
if d.PrivateKey != "" {
|
||||
signer, err := commonssh.FileSigner(d.PrivateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
auth = append(auth, gossh.PublicKeys(signer))
|
||||
}
|
||||
|
||||
sshConfig := &ssh.Config{
|
||||
Connection: ssh.ConnectFunc("tcp", address),
|
||||
SSHConfig: &gossh.ClientConfig{
|
||||
|
|
|
@ -57,8 +57,8 @@ func (s *StepRegister) Cleanup(state multistep.StateBag) {
|
|||
}
|
||||
// Wait for the machine to actually destroy
|
||||
for {
|
||||
exists, _ := remoteDriver.IsDestroyed()
|
||||
if !exists {
|
||||
destroyed, _ := remoteDriver.IsDestroyed()
|
||||
if destroyed {
|
||||
break
|
||||
}
|
||||
time.Sleep(150 * time.Millisecond)
|
||||
|
|
|
@ -72,7 +72,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
CustomData: b.config.VMXData,
|
||||
},
|
||||
&vmwcommon.StepSuppressMessages{},
|
||||
&vmwcommon.StepHTTPServer{
|
||||
&common.StepHTTPServer{
|
||||
HTTPDir: b.config.HTTPDir,
|
||||
HTTPPortMin: b.config.HTTPPortMin,
|
||||
HTTPPortMax: b.config.HTTPPortMax,
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
// Config is the configuration structure for the builder.
|
||||
type Config struct {
|
||||
common.PackerConfig `mapstructure:",squash"`
|
||||
common.HTTPConfig `mapstructure:",squash"`
|
||||
vmwcommon.DriverConfig `mapstructure:",squash"`
|
||||
vmwcommon.OutputConfig `mapstructure:",squash"`
|
||||
vmwcommon.RunConfig `mapstructure:",squash"`
|
||||
|
@ -56,6 +57,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
|
|||
// Prepare the errors
|
||||
var errs *packer.MultiError
|
||||
errs = packer.MultiErrorAppend(errs, c.DriverConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.HTTPConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.OutputConfig.Prepare(&c.ctx, &c.PackerConfig)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.RunConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.ShutdownConfig.Prepare(&c.ctx)...)
|
||||
|
|
|
@ -53,6 +53,10 @@ func (c *PushCommand) Run(args []string) int {
|
|||
return 1
|
||||
}
|
||||
|
||||
if message != "" {
|
||||
c.Ui.Warn("[DEPRECATED] -m/-message is deprecated and will be removed in a future Packer release")
|
||||
}
|
||||
|
||||
args = f.Args()
|
||||
if len(args) != 1 {
|
||||
f.Usage()
|
||||
|
@ -268,9 +272,6 @@ Usage: packer push [options] TEMPLATE
|
|||
|
||||
Options:
|
||||
|
||||
-m, -message=<detail> A message to identify the purpose or changes in this
|
||||
Packer template much like a VCS commit message
|
||||
|
||||
-name=<name> The destination build in Atlas. This is in a format
|
||||
"username/name".
|
||||
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/mitchellh/packer/template/interpolate"
|
||||
)
|
||||
|
||||
// HTTPConfig contains configuration for the local HTTP Server
|
||||
type HTTPConfig struct {
|
||||
HTTPDir string `mapstructure:"http_directory"`
|
||||
HTTPPortMin uint `mapstructure:"http_port_min"`
|
||||
HTTPPortMax uint `mapstructure:"http_port_max"`
|
||||
}
|
||||
|
||||
func (c *HTTPConfig) Prepare(ctx *interpolate.Context) []error {
|
||||
// Validation
|
||||
var errs []error
|
||||
|
||||
if c.HTTPPortMin == 0 {
|
||||
c.HTTPPortMin = 8000
|
||||
}
|
||||
|
||||
if c.HTTPPortMax == 0 {
|
||||
c.HTTPPortMax = 9000
|
||||
}
|
||||
|
||||
if c.HTTPPortMin > c.HTTPPortMax {
|
||||
errs = append(errs,
|
||||
errors.New("http_port_min must be less than http_port_max"))
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHTTPConfigPrepare_Bounds(t *testing.T) {
|
||||
// Test bad
|
||||
h := HTTPConfig{
|
||||
HTTPPortMin: 1000,
|
||||
HTTPPortMax: 500,
|
||||
}
|
||||
err := h.Prepare(nil)
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
|
||||
// Test good
|
||||
h = HTTPConfig{
|
||||
HTTPPortMin: 0,
|
||||
HTTPPortMax: 0,
|
||||
}
|
||||
err = h.Prepare(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
portMin := uint(8000)
|
||||
if h.HTTPPortMin != portMin {
|
||||
t.Fatalf("HTTPPortMin: expected %d got %d", portMin, h.HTTPPortMin)
|
||||
}
|
||||
portMax := uint(9000)
|
||||
if h.HTTPPortMax != portMax {
|
||||
t.Fatalf("HTTPPortMax: expected %d got %d", portMax, h.HTTPPortMax)
|
||||
}
|
||||
|
||||
// Test good
|
||||
h = HTTPConfig{
|
||||
HTTPPortMin: 500,
|
||||
HTTPPortMax: 1000,
|
||||
}
|
||||
err = h.Prepare(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
}
|
|
@ -26,8 +26,10 @@ type Communicator struct {
|
|||
// New creates a new communicator implementation over WinRM.
|
||||
func New(config *Config) (*Communicator, error) {
|
||||
endpoint := &winrm.Endpoint{
|
||||
Host: config.Host,
|
||||
Port: config.Port,
|
||||
Host: config.Host,
|
||||
Port: config.Port,
|
||||
HTTPS: config.Https,
|
||||
Insecure: config.Insecure,
|
||||
|
||||
/*
|
||||
TODO
|
||||
|
@ -145,6 +147,8 @@ func (c *Communicator) newCopyClient() (*winrmcp.Winrmcp, error) {
|
|||
User: c.config.Username,
|
||||
Password: c.config.Password,
|
||||
},
|
||||
Https: c.config.Https,
|
||||
Insecure: c.config.Insecure,
|
||||
OperationTimeout: c.config.Timeout,
|
||||
MaxOperationsPerShell: 15, // lowest common denominator
|
||||
})
|
||||
|
|
|
@ -11,4 +11,6 @@ type Config struct {
|
|||
Username string
|
||||
Password string
|
||||
Timeout time.Duration
|
||||
Https bool
|
||||
Insecure bool
|
||||
}
|
||||
|
|
|
@ -36,6 +36,8 @@ type Config struct {
|
|||
WinRMHost string `mapstructure:"winrm_host"`
|
||||
WinRMPort int `mapstructure:"winrm_port"`
|
||||
WinRMTimeout time.Duration `mapstructure:"winrm_timeout"`
|
||||
WinRMUseSSL bool `mapstructure:"winrm_use_ssl"`
|
||||
WinRMInsecure bool `mapstructure:"winrm_insecure"`
|
||||
}
|
||||
|
||||
// Port returns the port that will be used for access based on config.
|
||||
|
|
|
@ -129,6 +129,8 @@ func (s *StepConnectWinRM) waitForWinRM(state multistep.StateBag, cancel <-chan
|
|||
Username: user,
|
||||
Password: password,
|
||||
Timeout: s.Config.WinRMTimeout,
|
||||
Https: s.Config.WinRMUseSSL,
|
||||
Insecure: s.Config.WinRMInsecure,
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] WinRM connection err: %s", err)
|
||||
|
|
|
@ -22,23 +22,24 @@ import (
|
|||
type Config struct {
|
||||
common.PackerConfig `mapstructure:",squash"`
|
||||
|
||||
ChefEnvironment string `mapstructure:"chef_environment"`
|
||||
SslVerifyMode string `mapstructure:"ssl_verify_mode"`
|
||||
ConfigTemplate string `mapstructure:"config_template"`
|
||||
ExecuteCommand string `mapstructure:"execute_command"`
|
||||
InstallCommand string `mapstructure:"install_command"`
|
||||
Json map[string]interface{}
|
||||
NodeName string `mapstructure:"node_name"`
|
||||
PreventSudo bool `mapstructure:"prevent_sudo"`
|
||||
RunList []string `mapstructure:"run_list"`
|
||||
ServerUrl string `mapstructure:"server_url"`
|
||||
SkipCleanClient bool `mapstructure:"skip_clean_client"`
|
||||
SkipCleanNode bool `mapstructure:"skip_clean_node"`
|
||||
SkipInstall bool `mapstructure:"skip_install"`
|
||||
StagingDir string `mapstructure:"staging_directory"`
|
||||
ClientKey string `mapstructure:"client_key"`
|
||||
ValidationKeyPath string `mapstructure:"validation_key_path"`
|
||||
ValidationClientName string `mapstructure:"validation_client_name"`
|
||||
ChefEnvironment string `mapstructure:"chef_environment"`
|
||||
EncryptedDataBagSecretPath string `mapstructure:"encrypted_data_bag_secret_path"`
|
||||
SslVerifyMode string `mapstructure:"ssl_verify_mode"`
|
||||
ConfigTemplate string `mapstructure:"config_template"`
|
||||
ExecuteCommand string `mapstructure:"execute_command"`
|
||||
InstallCommand string `mapstructure:"install_command"`
|
||||
Json map[string]interface{}
|
||||
NodeName string `mapstructure:"node_name"`
|
||||
PreventSudo bool `mapstructure:"prevent_sudo"`
|
||||
RunList []string `mapstructure:"run_list"`
|
||||
ServerUrl string `mapstructure:"server_url"`
|
||||
SkipCleanClient bool `mapstructure:"skip_clean_client"`
|
||||
SkipCleanNode bool `mapstructure:"skip_clean_node"`
|
||||
SkipInstall bool `mapstructure:"skip_install"`
|
||||
StagingDir string `mapstructure:"staging_directory"`
|
||||
ClientKey string `mapstructure:"client_key"`
|
||||
ValidationKeyPath string `mapstructure:"validation_key_path"`
|
||||
ValidationClientName string `mapstructure:"validation_client_name"`
|
||||
|
||||
ctx interpolate.Context
|
||||
}
|
||||
|
@ -48,13 +49,15 @@ type Provisioner struct {
|
|||
}
|
||||
|
||||
type ConfigTemplate struct {
|
||||
NodeName string
|
||||
ServerUrl string
|
||||
ClientKey string
|
||||
ValidationKeyPath string
|
||||
ValidationClientName string
|
||||
ChefEnvironment string
|
||||
SslVerifyMode string
|
||||
NodeName string
|
||||
ServerUrl string
|
||||
ClientKey string
|
||||
ValidationKeyPath string
|
||||
ValidationClientName string
|
||||
EncryptedDataBagSecretPath string
|
||||
ChefEnvironment string
|
||||
SslVerifyMode string
|
||||
HasEncryptedDataBagSecretPath bool
|
||||
}
|
||||
|
||||
type ExecuteTemplate struct {
|
||||
|
@ -118,6 +121,15 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
|
|||
errs, fmt.Errorf("server_url must be set"))
|
||||
}
|
||||
|
||||
if p.config.EncryptedDataBagSecretPath != "" {
|
||||
pFileInfo, err := os.Stat(p.config.EncryptedDataBagSecretPath)
|
||||
|
||||
if err != nil || pFileInfo.IsDir() {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, fmt.Errorf("Bad encrypted data bag secret '%s': %s", p.config.EncryptedDataBagSecretPath, err))
|
||||
}
|
||||
}
|
||||
|
||||
jsonValid := true
|
||||
for k, v := range p.config.Json {
|
||||
p.config.Json[k], err = p.deepJsonFix(k, v)
|
||||
|
@ -175,8 +187,16 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
|
|||
}
|
||||
}
|
||||
|
||||
encryptedDataBagSecretPath := ""
|
||||
if p.config.EncryptedDataBagSecretPath != "" {
|
||||
encryptedDataBagSecretPath = fmt.Sprintf("%s/encrypted_data_bag_secret", p.config.StagingDir)
|
||||
if err := p.uploadFile(ui, comm, encryptedDataBagSecretPath, p.config.EncryptedDataBagSecretPath); err != nil {
|
||||
return fmt.Errorf("Error uploading encrypted data bag secret: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
configPath, err := p.createConfig(
|
||||
ui, comm, nodeName, serverUrl, p.config.ClientKey, remoteValidationKeyPath, p.config.ValidationClientName, p.config.ChefEnvironment, p.config.SslVerifyMode)
|
||||
ui, comm, nodeName, serverUrl, p.config.ClientKey, remoteValidationKeyPath, p.config.ValidationClientName, encryptedDataBagSecretPath, p.config.ChefEnvironment, p.config.SslVerifyMode)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error creating Chef config file: %s", err)
|
||||
}
|
||||
|
@ -236,7 +256,17 @@ func (p *Provisioner) uploadDirectory(ui packer.Ui, comm packer.Communicator, ds
|
|||
return comm.UploadDir(dst, src, nil)
|
||||
}
|
||||
|
||||
func (p *Provisioner) createConfig(ui packer.Ui, comm packer.Communicator, nodeName string, serverUrl string, clientKey string, remoteKeyPath string, validationClientName string, chefEnvironment string, sslVerifyMode string) (string, error) {
|
||||
func (p *Provisioner) uploadFile(ui packer.Ui, comm packer.Communicator, dst string, src string) error {
|
||||
f, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
return comm.Upload(dst, f, nil)
|
||||
}
|
||||
|
||||
func (p *Provisioner) createConfig(ui packer.Ui, comm packer.Communicator, nodeName string, serverUrl string, clientKey string, remoteKeyPath string, validationClientName string, encryptedDataBagSecretPath string, chefEnvironment string, sslVerifyMode string) (string, error) {
|
||||
ui.Message("Creating configuration file 'client.rb'")
|
||||
|
||||
// Read the template
|
||||
|
@ -258,13 +288,15 @@ func (p *Provisioner) createConfig(ui packer.Ui, comm packer.Communicator, nodeN
|
|||
|
||||
ctx := p.config.ctx
|
||||
ctx.Data = &ConfigTemplate{
|
||||
NodeName: nodeName,
|
||||
ServerUrl: serverUrl,
|
||||
ClientKey: clientKey,
|
||||
ValidationKeyPath: remoteKeyPath,
|
||||
ValidationClientName: validationClientName,
|
||||
ChefEnvironment: chefEnvironment,
|
||||
SslVerifyMode: sslVerifyMode,
|
||||
NodeName: nodeName,
|
||||
ServerUrl: serverUrl,
|
||||
ClientKey: clientKey,
|
||||
ValidationKeyPath: remoteKeyPath,
|
||||
ValidationClientName: validationClientName,
|
||||
ChefEnvironment: chefEnvironment,
|
||||
SslVerifyMode: sslVerifyMode,
|
||||
EncryptedDataBagSecretPath: encryptedDataBagSecretPath,
|
||||
HasEncryptedDataBagSecretPath: encryptedDataBagSecretPath != "",
|
||||
}
|
||||
configString, err := interpolate.Render(tpl, &ctx)
|
||||
if err != nil {
|
||||
|
@ -587,6 +619,9 @@ log_level :info
|
|||
log_location STDOUT
|
||||
chef_server_url "{{.ServerUrl}}"
|
||||
client_key "{{.ClientKey}}"
|
||||
{{if .HasEncryptedDataBagSecretPath}}
|
||||
encrypted_data_bag_secret "{{.EncryptedDataBagSecretPath}}"
|
||||
{{end}}
|
||||
{{if ne .ValidationClientName ""}}
|
||||
validation_client_name "{{.ValidationClientName}}"
|
||||
{{else}}
|
||||
|
|
|
@ -138,6 +138,49 @@ func TestProvisionerPrepare_serverUrl(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestProvisionerPrepare_encryptedDataBagSecretPath(t *testing.T) {
|
||||
var err error
|
||||
var p Provisioner
|
||||
|
||||
// Test no config template
|
||||
config := testConfig()
|
||||
delete(config, "encrypted_data_bag_secret_path")
|
||||
err = p.Prepare(config)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
// Test with a file
|
||||
tf, err := ioutil.TempFile("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
defer os.Remove(tf.Name())
|
||||
|
||||
config = testConfig()
|
||||
config["encrypted_data_bag_secret_path"] = tf.Name()
|
||||
p = Provisioner{}
|
||||
err = p.Prepare(config)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
// Test with a directory
|
||||
td, err := ioutil.TempDir("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
defer os.RemoveAll(td)
|
||||
|
||||
config = testConfig()
|
||||
config["encrypted_data_bag_secret_path"] = td
|
||||
p = Provisioner{}
|
||||
err = p.Prepare(config)
|
||||
if err == nil {
|
||||
t.Fatal("should have err")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvisioner_createDir(t *testing.T) {
|
||||
p1 := &Provisioner{config: Config{PreventSudo: true}}
|
||||
p2 := &Provisioner{config: Config{PreventSudo: false}}
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
# Get the parent directory of where this script is.
|
||||
SOURCE="${BASH_SOURCE[0]}"
|
||||
while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done
|
||||
DIR="$( cd -P "$( dirname "$SOURCE" )/.." && pwd )"
|
||||
|
||||
# Change into that dir because we expect that
|
||||
cd $DIR
|
||||
|
||||
# Get the version from the command line
|
||||
VERSION=$1
|
||||
if [ -z $VERSION ]; then
|
||||
echo "Please specify a version."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Make sure we have a bintray API key
|
||||
if [ -z $BINTRAY_API_KEY ]; then
|
||||
echo "Please set your bintray API key in the BINTRAY_API_KEY env var."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for ARCHIVE in ./pkg/dist/*; do
|
||||
ARCHIVE_NAME=$(basename ${ARCHIVE})
|
||||
|
||||
echo Uploading: $ARCHIVE_NAME
|
||||
curl \
|
||||
-T ${ARCHIVE} \
|
||||
-umitchellh:${BINTRAY_API_KEY} \
|
||||
"https://api.bintray.com/content/mitchellh/packer/packer/${VERSION}/${ARCHIVE_NAME}"
|
||||
done
|
|
@ -1,40 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# Set the tmpdir
|
||||
if [ -z "$TMPDIR" ]; then
|
||||
TMPDIR="/tmp"
|
||||
fi
|
||||
|
||||
# Create a temporary build dir and make sure we clean it up. For
|
||||
# debugging, comment out the trap line.
|
||||
DEPLOY=`mktemp -d $TMPDIR/packer-www-XXXXXX`
|
||||
trap "rm -rf $DEPLOY" INT TERM EXIT
|
||||
|
||||
# Get the parent directory of where this script is.
|
||||
SOURCE="${BASH_SOURCE[0]}"
|
||||
while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done
|
||||
DIR="$( cd -P "$( dirname "$SOURCE" )/.." && pwd )"
|
||||
|
||||
# Copy into tmpdir
|
||||
shopt -s dotglob
|
||||
cp -r $DIR/website/* $DEPLOY/
|
||||
|
||||
# Change into that directory
|
||||
pushd $DEPLOY &>/dev/null
|
||||
|
||||
# Ignore some stuff
|
||||
touch .gitignore
|
||||
echo ".sass-cache" >> .gitignore
|
||||
echo "build" >> .gitignore
|
||||
echo "vendor" >> .gitignore
|
||||
|
||||
# Add everything
|
||||
git init -q .
|
||||
git add .
|
||||
git commit -q -m "Deploy by $USER"
|
||||
|
||||
git remote add heroku git@heroku.com:packer-www.git
|
||||
git push -f heroku master
|
||||
|
||||
# Go back to our root
|
||||
popd &>/dev/null
|
|
@ -1,2 +0,0 @@
|
|||
https://github.com/heroku/heroku-buildpack-ruby.git
|
||||
https://github.com/hashicorp/heroku-buildpack-middleman.git
|
|
@ -1 +0,0 @@
|
|||
2.2.2
|
|
@ -1,7 +1,5 @@
|
|||
source "https://rubygems.org"
|
||||
|
||||
ruby "2.2.2"
|
||||
|
||||
gem "middleman-hashicorp", github: "hashicorp/middleman-hashicorp"
|
||||
gem "middleman-breadcrumbs"
|
||||
gem "htmlbeautifier"
|
||||
gem "htmlbeautifier"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
GIT
|
||||
remote: git://github.com/hashicorp/middleman-hashicorp.git
|
||||
revision: 15cbda0cf1d963fa71292dee921229e7ee618272
|
||||
revision: 953baf8762b915cf57553bcc82bc946ad777056f
|
||||
specs:
|
||||
middleman-hashicorp (0.2.0)
|
||||
bootstrap-sass (~> 3.3)
|
||||
|
@ -21,18 +21,18 @@ GIT
|
|||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
activesupport (4.2.4)
|
||||
activesupport (4.2.5)
|
||||
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.0.3)
|
||||
autoprefixer-rails (6.2.3)
|
||||
execjs
|
||||
json
|
||||
bootstrap-sass (3.3.5.1)
|
||||
autoprefixer-rails (>= 5.0.0.1)
|
||||
sass (>= 3.3.0)
|
||||
bootstrap-sass (3.3.6)
|
||||
autoprefixer-rails (>= 5.2.1)
|
||||
sass (>= 3.3.4)
|
||||
builder (3.2.2)
|
||||
capybara (2.4.4)
|
||||
mime-types (>= 1.16)
|
||||
|
@ -40,11 +40,11 @@ GEM
|
|||
rack (>= 1.0.0)
|
||||
rack-test (>= 0.5.4)
|
||||
xpath (~> 2.0)
|
||||
chunky_png (1.3.4)
|
||||
chunky_png (1.3.5)
|
||||
coffee-script (2.4.1)
|
||||
coffee-script-source
|
||||
execjs
|
||||
coffee-script-source (1.9.1.1)
|
||||
coffee-script-source (1.10.0)
|
||||
commonjs (0.2.7)
|
||||
compass (1.0.3)
|
||||
chunky_png (~> 1.2)
|
||||
|
@ -63,7 +63,7 @@ GEM
|
|||
eventmachine (>= 0.12.9)
|
||||
http_parser.rb (~> 0.6.0)
|
||||
erubis (2.7.0)
|
||||
eventmachine (1.0.8)
|
||||
eventmachine (1.0.9)
|
||||
execjs (2.6.0)
|
||||
ffi (1.9.10)
|
||||
git-version-bump (0.15.1)
|
||||
|
@ -81,23 +81,23 @@ GEM
|
|||
less (2.6.0)
|
||||
commonjs (~> 0.2.7)
|
||||
libv8 (3.16.14.13)
|
||||
listen (3.0.3)
|
||||
listen (3.0.5)
|
||||
rb-fsevent (>= 0.9.3)
|
||||
rb-inotify (>= 0.9)
|
||||
middleman (3.4.0)
|
||||
middleman (3.4.1)
|
||||
coffee-script (~> 2.2)
|
||||
compass (>= 1.0.0, < 2.0.0)
|
||||
compass-import-once (= 1.0.5)
|
||||
execjs (~> 2.0)
|
||||
haml (>= 4.0.5)
|
||||
kramdown (~> 1.2)
|
||||
middleman-core (= 3.4.0)
|
||||
middleman-core (= 3.4.1)
|
||||
middleman-sprockets (>= 3.1.2)
|
||||
sass (>= 3.4.0, < 4.0)
|
||||
uglifier (~> 2.5)
|
||||
middleman-breadcrumbs (0.2.0)
|
||||
middleman (>= 3.3.5)
|
||||
middleman-core (3.4.0)
|
||||
middleman-core (3.4.1)
|
||||
activesupport (~> 4.1)
|
||||
bundler (~> 1.1)
|
||||
capybara (~> 2.4.4)
|
||||
|
@ -109,7 +109,7 @@ GEM
|
|||
rack (>= 1.4.5, < 2.0)
|
||||
thor (>= 0.15.2, < 2.0)
|
||||
tilt (~> 1.4.1, < 2.0)
|
||||
middleman-livereload (3.4.3)
|
||||
middleman-livereload (3.4.6)
|
||||
em-websocket (~> 0.5.1)
|
||||
middleman-core (>= 3.3)
|
||||
rack-livereload (~> 0.3.15)
|
||||
|
@ -121,15 +121,17 @@ GEM
|
|||
sprockets (~> 2.12.1)
|
||||
sprockets-helpers (~> 1.1.0)
|
||||
sprockets-sass (~> 1.3.0)
|
||||
middleman-syntax (2.0.0)
|
||||
middleman-core (~> 3.2)
|
||||
middleman-syntax (2.1.0)
|
||||
middleman-core (>= 3.2)
|
||||
rouge (~> 1.0)
|
||||
mime-types (2.6.2)
|
||||
mini_portile (0.6.2)
|
||||
minitest (5.8.1)
|
||||
mime-types (3.0)
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2015.1120)
|
||||
mini_portile2 (2.0.0)
|
||||
minitest (5.8.3)
|
||||
multi_json (1.11.2)
|
||||
nokogiri (1.6.6.2)
|
||||
mini_portile (~> 0.6.0)
|
||||
nokogiri (1.6.7.1)
|
||||
mini_portile2 (~> 2.0.0.rc2)
|
||||
padrino-helpers (0.12.5)
|
||||
i18n (~> 0.6, >= 0.6.7)
|
||||
padrino-support (= 0.12.5)
|
||||
|
@ -148,13 +150,13 @@ GEM
|
|||
rack-ssl-enforcer (0.2.9)
|
||||
rack-test (0.6.3)
|
||||
rack (>= 1.0)
|
||||
rb-fsevent (0.9.6)
|
||||
rb-fsevent (0.9.7)
|
||||
rb-inotify (0.9.5)
|
||||
ffi (>= 0.5.0)
|
||||
redcarpet (3.3.3)
|
||||
redcarpet (3.3.4)
|
||||
ref (2.0.0)
|
||||
rouge (1.10.1)
|
||||
sass (3.4.19)
|
||||
sass (3.4.21)
|
||||
sprockets (2.12.4)
|
||||
hike (~> 1.2)
|
||||
multi_json (~> 1.0)
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
web: bundle exec thin start -p $PORT
|
|
@ -5,22 +5,22 @@ $script = <<SCRIPT
|
|||
sudo apt-get -y update
|
||||
|
||||
# RVM/Ruby
|
||||
sudo apt-get -y install curl
|
||||
sudo apt-get -y install git
|
||||
sudo apt-get -qy install curl git libgmp3-dev
|
||||
gpg --keyserver hkp://keys.gnupg.net --recv-keys D39DC0E3
|
||||
# Install rvm and the latest version of ruby
|
||||
curl -sSL https://get.rvm.io | bash -s stable
|
||||
. ~/.bashrc
|
||||
. ~/.bash_profile
|
||||
rvm install 2.0.0
|
||||
rvm --default use 2.0.0
|
||||
rvm install ruby-2.2.2
|
||||
gem install bundler
|
||||
|
||||
# Middleman deps
|
||||
cd /vagrant
|
||||
bundle
|
||||
make dev
|
||||
SCRIPT
|
||||
|
||||
Vagrant.configure(2) do |config|
|
||||
config.vm.box = "chef/ubuntu-12.04"
|
||||
config.vm.box = "bento/ubuntu-14.04"
|
||||
config.vm.network "private_network", ip: "33.33.30.10"
|
||||
config.vm.provision "shell", inline: $script, privileged: false
|
||||
config.vm.synced_folder ".", "/vagrant", type: "rsync"
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"variables": {
|
||||
"aws_access_key_id": "{{ env `AWS_ACCESS_KEY_ID` }}",
|
||||
"aws_secret_access_key": "{{ env `AWS_SECRET_ACCESS_KEY` }}",
|
||||
"aws_region": "{{ env `AWS_REGION` }}",
|
||||
"fastly_api_key": "{{ env `FASTLY_API_KEY` }}"
|
||||
},
|
||||
"builders": [
|
||||
{
|
||||
"type": "docker",
|
||||
"image": "ruby:2.3-slim",
|
||||
"commit": "true"
|
||||
}
|
||||
],
|
||||
"provisioners": [
|
||||
{
|
||||
"type": "file",
|
||||
"source": ".",
|
||||
"destination": "/app"
|
||||
},
|
||||
{
|
||||
"type": "shell",
|
||||
"environment_vars": [
|
||||
"AWS_ACCESS_KEY_ID={{ user `aws_access_key_id` }}",
|
||||
"AWS_SECRET_ACCESS_KEY={{ user `aws_secret_access_key` }}",
|
||||
"AWS_REGION={{ user `aws_region` }}",
|
||||
"FASTLY_API_KEY={{ user `fastly_api_key` }}"
|
||||
],
|
||||
"inline": [
|
||||
"apt-get update",
|
||||
"apt-get install -y build-essential curl git libffi-dev s3cmd wget",
|
||||
"cd /app",
|
||||
|
||||
"bundle check || bundle install --jobs 7",
|
||||
"bundle exec middleman build",
|
||||
|
||||
"/bin/bash ./scripts/deploy.sh"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
PROJECT="packer"
|
||||
PROJECT_URL="www.packer.io"
|
||||
FASTLY_SERVICE_ID="7GrxRJP3PVBuqQbyxYQ0MV"
|
||||
|
||||
# Ensure the proper AWS environment variables are set
|
||||
if [ -z "$AWS_ACCESS_KEY_ID" ]; then
|
||||
echo "Missing AWS_ACCESS_KEY_ID!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$AWS_SECRET_ACCESS_KEY" ]; then
|
||||
echo "Missing AWS_SECRET_ACCESS_KEY!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Ensure the proper Fastly keys are set
|
||||
if [ -z "$FASTLY_API_KEY" ]; then
|
||||
echo "Missing FASTLY_API_KEY!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Ensure we have s3cmd installed
|
||||
if ! command -v "s3cmd" >/dev/null 2>&1; then
|
||||
echo "Missing s3cmd!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get the parent directory of where this script is and change into our website
|
||||
# directory
|
||||
SOURCE="${BASH_SOURCE[0]}"
|
||||
while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done
|
||||
DIR="$(cd -P "$( dirname "$SOURCE" )/.." && pwd)"
|
||||
|
||||
# Delete any .DS_Store files for our OS X friends.
|
||||
find "$DIR" -type f -name '.DS_Store' -delete
|
||||
|
||||
# Upload the files to S3 - we disable mime-type detection by the python library
|
||||
# and just guess from the file extension because it's surprisingly more
|
||||
# accurate, especially for CSS and javascript. We also tag the uploaded files
|
||||
# with the proper Surrogate-Key, which we will later purge in our API call to
|
||||
# Fastly.
|
||||
if [ -z "$NO_UPLOAD" ]; then
|
||||
echo "Uploading to S3..."
|
||||
|
||||
# Check that the site has been built
|
||||
if [ ! -d "$DIR/build" ]; then
|
||||
echo "Missing compiled website! Run 'make build' to compile!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
s3cmd \
|
||||
--quiet \
|
||||
--delete-removed \
|
||||
--guess-mime-type \
|
||||
--no-mime-magic \
|
||||
--acl-public \
|
||||
--recursive \
|
||||
--add-header="Cache-Control: max-age=31536000" \
|
||||
--add-header="x-amz-meta-surrogate-key: site-$PROJECT" \
|
||||
sync "$DIR/build/" "s3://hc-sites/$PROJECT/latest/"
|
||||
fi
|
||||
|
||||
# Perform a soft-purge of the surrogate key.
|
||||
if [ -z "$NO_PURGE" ]; then
|
||||
echo "Purging Fastly cache..."
|
||||
curl \
|
||||
--fail \
|
||||
--silent \
|
||||
--output /dev/null \
|
||||
--request "POST" \
|
||||
--header "Accept: application/json" \
|
||||
--header "Fastly-Key: $FASTLY_API_KEY" \
|
||||
--header "Fastly-Soft-Purge: 1" \
|
||||
"https://api.fastly.com/service/$FASTLY_SERVICE_ID/purge/site-$PROJECT"
|
||||
fi
|
||||
|
||||
# Warm the cache with recursive wget.
|
||||
if [ -z "$NO_WARM" ]; then
|
||||
echo "Warming Fastly cache..."
|
||||
wget \
|
||||
--recursive \
|
||||
--delete-after \
|
||||
--quiet \
|
||||
"https://$PROJECT_URL/"
|
||||
fi
|
Before Width: | Height: | Size: 524 B After Width: | Height: | Size: 346 B |
Before Width: | Height: | Size: 506 B After Width: | Height: | Size: 338 B |
Before Width: | Height: | Size: 597 B After Width: | Height: | Size: 596 B |
Before Width: | Height: | Size: 143 KiB After Width: | Height: | Size: 85 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 96 KiB |
Before Width: | Height: | Size: 978 B After Width: | Height: | Size: 779 B |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 451 B After Width: | Height: | Size: 390 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 158 KiB After Width: | Height: | Size: 155 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 6.2 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
|
@ -14,7 +14,7 @@ dedicated users willing to help through various mediums.
|
|||
**IRC:** `#packer-tool` on Freenode.
|
||||
|
||||
**Mailing List:** [Packer Google
|
||||
Group](http://groups.google.com/group/packer-tool)
|
||||
Group](https://groups.google.com/group/packer-tool)
|
||||
|
||||
**Bug Tracker:** [Issue tracker on
|
||||
GitHub](https://github.com/mitchellh/packer/issues). Please only use this for
|
||||
|
@ -31,14 +31,14 @@ list as contributors come and go.
|
|||
|
||||
<div class="person">
|
||||
|
||||
<img class="pull-left" src="http://www.gravatar.com/avatar/54079122b67de9677c1f93933ce8b63a.png?s=125">
|
||||
<img class="pull-left" src="https://www.gravatar.com/avatar/54079122b67de9677c1f93933ce8b63a.png?s=125">
|
||||
<div class="bio">
|
||||
<h3>Mitchell Hashimoto (<a href="https://github.com/mitchellh">@mitchellh</a>)</h3>
|
||||
<p>
|
||||
Mitchell Hashimoto is the creator of Packer. He developed the
|
||||
core of Packer as well as the Amazon, VirtualBox, and VMware
|
||||
builders. In addition to Packer, Mitchell is the creator of
|
||||
<a href="http://www.vagrantup.com">Vagrant</a>. He is self
|
||||
<a href="https://www.vagrantup.com">Vagrant</a>. He is self
|
||||
described as "automation obsessed."
|
||||
</p>
|
||||
</div>
|
||||
|
@ -47,7 +47,7 @@ list as contributors come and go.
|
|||
|
||||
<div class="person">
|
||||
|
||||
<img class="pull-left" src="http://www.gravatar.com/avatar/2acc31dd6370a54b18f6755cd0710ce6.png?s=125">
|
||||
<img class="pull-left" src="https://www.gravatar.com/avatar/2acc31dd6370a54b18f6755cd0710ce6.png?s=125">
|
||||
<div class="bio">
|
||||
<h3>Jack Pearkes (<a href="https://github.com/pearkes">@pearkes</a>)</h3>
|
||||
<p>
|
||||
|
@ -60,7 +60,7 @@ list as contributors come and go.
|
|||
|
||||
<div class="person">
|
||||
|
||||
<img class="pull-left" src="http://www.gravatar.com/avatar/2f7fc9cb7558e3ea48f5a86fa90a78da.png?s=125">
|
||||
<img class="pull-left" src="https://www.gravatar.com/avatar/2f7fc9cb7558e3ea48f5a86fa90a78da.png?s=125">
|
||||
<div class="bio">
|
||||
<h3>Mark Peek (<a href="https://github.com/markpeek">@markpeek</a>)</h3>
|
||||
<p>
|
||||
|
@ -75,7 +75,7 @@ list as contributors come and go.
|
|||
|
||||
<div class="person">
|
||||
|
||||
<img class="pull-left" src="http://www.gravatar.com/avatar/1fca64df3d7db1e2f258a8956d2b0aff.png?s=125">
|
||||
<img class="pull-left" src="https://www.gravatar.com/avatar/1fca64df3d7db1e2f258a8956d2b0aff.png?s=125">
|
||||
<div class="bio">
|
||||
<h3>Ross Smith II (<a href="https://github.com/rasa" target="_blank">@rasa</a>)</h3>
|
||||
<p>
|
||||
|
@ -90,7 +90,7 @@ open source enthusiast, published author, and freelance consultant.
|
|||
|
||||
<div class="person">
|
||||
|
||||
<img class="pull-left" src="http://www.gravatar.com/avatar/c9f6bf7b5b865012be5eded656ebed7d.png?s=125">
|
||||
<img class="pull-left" src="https://www.gravatar.com/avatar/c9f6bf7b5b865012be5eded656ebed7d.png?s=125">
|
||||
<div class="bio">
|
||||
<h3>Rickard von Essen<br/>(<a href="https://github.com/rickard-von-essen" target="_blank">@rickard-von-essen</a>)</h3>
|
||||
<p>
|
||||
|
|
|
@ -16,7 +16,7 @@ The `amazon-chroot` Packer builder is able to create Amazon AMIs backed by an
|
|||
EBS volume as the root device. For more information on the difference between
|
||||
instance storage and EBS-backed instances, see the ["storage for the root
|
||||
device" section in the EC2
|
||||
documentation](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ComponentsAMIs.html#storage-for-the-root-device).
|
||||
documentation](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ComponentsAMIs.html#storage-for-the-root-device).
|
||||
|
||||
The difference between this builder and the `amazon-ebs` builder is that this
|
||||
builder is able to build an EBS-backed AMI without launching a new EC2 instance.
|
||||
|
@ -34,7 +34,7 @@ account, it is up to you to use, delete, etc. the AMI.
|
|||
|
||||
This builder works by creating a new EBS volume from an existing source AMI and
|
||||
attaching it into an already-running EC2 instance. Once attached, a
|
||||
[chroot](http://en.wikipedia.org/wiki/Chroot) is used to provision the system
|
||||
[chroot](https://en.wikipedia.org/wiki/Chroot) is used to provision the system
|
||||
within that volume. After provisioning, the volume is detached, snapshotted, and
|
||||
an AMI is made.
|
||||
|
||||
|
|
|
@ -13,10 +13,10 @@ page_title: 'Amazon AMI Builder (EBS backed)'
|
|||
Type: `amazon-ebs`
|
||||
|
||||
The `amazon-ebs` Packer builder is able to create Amazon AMIs backed by EBS
|
||||
volumes for use in [EC2](http://aws.amazon.com/ec2/). For more information on
|
||||
volumes for use in [EC2](https://aws.amazon.com/ec2/). For more information on
|
||||
the difference between EBS-backed instances and instance-store backed instances,
|
||||
see the ["storage for the root device" section in the EC2
|
||||
documentation](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ComponentsAMIs.html#storage-for-the-root-device).
|
||||
documentation](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ComponentsAMIs.html#storage-for-the-root-device).
|
||||
|
||||
This builder builds an AMI by launching an EC2 instance from a source AMI,
|
||||
provisioning that running machine, and then creating an AMI from that machine.
|
||||
|
@ -72,7 +72,7 @@ builder.
|
|||
example, "/dev/sdh" or "xvdh"). Required when specifying `volume_size`.
|
||||
- `virtual_name` (string) - The virtual device name. See the documentation on
|
||||
[Block Device
|
||||
Mapping](http://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_BlockDeviceMapping.html)
|
||||
Mapping](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_BlockDeviceMapping.html)
|
||||
for more information
|
||||
- `snapshot_id` (string) - The ID of the snapshot
|
||||
- `volume_type` (string) - The volume type. gp2 for General Purpose (SSD)
|
||||
|
@ -87,7 +87,7 @@ builder.
|
|||
block device mapping of the AMI
|
||||
- `iops` (integer) - The number of I/O operations per second (IOPS) that the
|
||||
volume supports. See the documentation on
|
||||
[IOPs](http://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_EbsBlockDevice.html)
|
||||
[IOPs](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_EbsBlockDevice.html)
|
||||
for more information
|
||||
- `ami_description` (string) - The description to set for the
|
||||
resulting AMI(s). By default this description is empty.
|
||||
|
@ -117,7 +117,7 @@ builder.
|
|||
instance in. Leave this empty to allow Amazon to auto-assign.
|
||||
|
||||
- `ebs_optimized` (boolean) - Mark instance as [EBS
|
||||
Optimized](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSOptimized.html).
|
||||
Optimized](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSOptimized.html).
|
||||
Default `false`.
|
||||
|
||||
- `enhanced_networking` (boolean) - Enable enhanced
|
||||
|
@ -128,7 +128,7 @@ builder.
|
|||
AMI if one with the same name already exists. Default `false`.
|
||||
|
||||
- `iam_instance_profile` (string) - The name of an [IAM instance
|
||||
profile](http://docs.aws.amazon.com/IAM/latest/UserGuide/instance-profiles.html)
|
||||
profile](https://docs.aws.amazon.com/IAM/latest/UserGuide/instance-profiles.html)
|
||||
to launch the EC2 instance with.
|
||||
|
||||
- `launch_block_device_mappings` (array of block device mappings) - Add the
|
||||
|
@ -208,8 +208,8 @@ Here is a basic example. It is completely valid except for the access keys:
|
|||
"access_key": "YOUR KEY HERE",
|
||||
"secret_key": "YOUR SECRET KEY HERE",
|
||||
"region": "us-east-1",
|
||||
"source_ami": "ami-de0d9eb7",
|
||||
"instance_type": "t1.micro",
|
||||
"source_ami": "ami-72b9e018",
|
||||
"instance_type": "t2.micro",
|
||||
"ssh_username": "ubuntu",
|
||||
"ami_name": "packer-quick-start {{timestamp}}"
|
||||
}
|
||||
|
@ -237,8 +237,8 @@ the /dev/sdb and /dev/sdc block device mappings to the finished AMI.
|
|||
"access_key": "YOUR KEY HERE",
|
||||
"secret_key": "YOUR SECRET KEY HERE",
|
||||
"region": "us-east-1",
|
||||
"source_ami": "ami-de0d9eb7",
|
||||
"instance_type": "t1.micro",
|
||||
"source_ami": "ami-72b9e018",
|
||||
"instance_type": "t2.micro",
|
||||
"ssh_username": "ubuntu",
|
||||
"ami_name": "packer-quick-start {{timestamp}}",
|
||||
"ami_block_device_mappings": [
|
||||
|
@ -265,8 +265,8 @@ Here is an example using the optional AMI tags. This will add the tags
|
|||
"access_key": "YOUR KEY HERE",
|
||||
"secret_key": "YOUR SECRET KEY HERE",
|
||||
"region": "us-east-1",
|
||||
"source_ami": "ami-de0d9eb7",
|
||||
"instance_type": "t1.micro",
|
||||
"source_ami": "ami-72b9e018",
|
||||
"instance_type": "t2.micro",
|
||||
"ssh_username": "ubuntu",
|
||||
"ami_name": "packer-quick-start {{timestamp}}",
|
||||
"tags": {
|
||||
|
|
|
@ -16,7 +16,7 @@ The `amazon-instance` Packer builder is able to create Amazon AMIs backed by
|
|||
instance storage as the root device. For more information on the difference
|
||||
between instance storage and EBS-backed instances, see the ["storage for the
|
||||
root device" section in the EC2
|
||||
documentation](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ComponentsAMIs.html#storage-for-the-root-device).
|
||||
documentation](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ComponentsAMIs.html#storage-for-the-root-device).
|
||||
|
||||
This builder builds an AMI by launching an EC2 instance from an existing
|
||||
instance-storage backed AMI, provisioning that running machine, and then
|
||||
|
@ -29,7 +29,7 @@ The builder does *not* manage AMIs. Once it creates an AMI and stores it in your
|
|||
account, it is up to you to use, delete, etc. the AMI.
|
||||
|
||||
-> **Note** This builder requires that the [Amazon EC2 AMI
|
||||
Tools](http://aws.amazon.com/developertools/368) are installed onto the machine.
|
||||
Tools](https://aws.amazon.com/developertools/368) are installed onto the machine.
|
||||
This can be done within a provisioner, but must be done before the builder
|
||||
finishes running.
|
||||
|
||||
|
@ -93,7 +93,7 @@ builder.
|
|||
example, "/dev/sdh" or "xvdh"). Required when specifying `volume_size`.
|
||||
- `virtual_name` (string) - The virtual device name. See the documentation on
|
||||
[Block Device
|
||||
Mapping](http://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_BlockDeviceMapping.html)
|
||||
Mapping](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_BlockDeviceMapping.html)
|
||||
for more information
|
||||
- `snapshot_id` (string) - The ID of the snapshot
|
||||
- `volume_type` (string) - The volume type. gp2 for General Purpose (SSD)
|
||||
|
@ -108,7 +108,7 @@ builder.
|
|||
block device mapping of the AMI
|
||||
- `iops` (integer) - The number of I/O operations per second (IOPS) that the
|
||||
volume supports. See the documentation on
|
||||
[IOPs](http://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_EbsBlockDevice.html)
|
||||
[IOPs](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_EbsBlockDevice.html)
|
||||
for more information
|
||||
- `ami_description` (string) - The description to set for the
|
||||
resulting AMI(s). By default this description is empty.
|
||||
|
@ -158,7 +158,7 @@ builder.
|
|||
the "custom bundle commands" section below for more information.
|
||||
|
||||
- `ebs_optimized` (boolean) - Mark instance as [EBS
|
||||
Optimized](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSOptimized.html).
|
||||
Optimized](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSOptimized.html).
|
||||
Default `false`.
|
||||
|
||||
- `enhanced_networking` (boolean) - Enable enhanced
|
||||
|
@ -169,7 +169,7 @@ builder.
|
|||
AMI if one with the same name already exists. Default `false`.
|
||||
|
||||
- `iam_instance_profile` (string) - The name of an [IAM instance
|
||||
profile](http://docs.aws.amazon.com/IAM/latest/UserGuide/instance-profiles.html)
|
||||
profile](https://docs.aws.amazon.com/IAM/latest/UserGuide/instance-profiles.html)
|
||||
to launch the EC2 instance with.
|
||||
|
||||
- `launch_block_device_mappings` (array of block device mappings) - Add the
|
||||
|
|
|
@ -23,7 +23,7 @@ Packer supports the following builders at the moment:
|
|||
|
||||
- [amazon-chroot](/docs/builders/amazon-chroot.html) - Create EBS-backed AMIs
|
||||
from an existing EC2 instance by mounting the root device and using a
|
||||
[Chroot](http://en.wikipedia.org/wiki/Chroot) environment to provision
|
||||
[Chroot](https://en.wikipedia.org/wiki/Chroot) environment to provision
|
||||
that device. This is an **advanced builder and should not be used by
|
||||
newcomers**. However, it is also the fastest way to build an EBS-backed AMI
|
||||
since no new EC2 instance needs to be launched.
|
||||
|
@ -49,7 +49,7 @@ Credentials are resolved in the following order:
|
|||
1. Values hard-coded in the packer template are always authoritative.
|
||||
2. *Variables* in the packer template may be resolved from command-line flags
|
||||
or from environment variables. Please read about [User
|
||||
Variables](https://packer.io/docs/templates/user-variables.html)
|
||||
Variables](https://www.packer.io/docs/templates/user-variables.html)
|
||||
for details.
|
||||
3. If no credentials are found, packer falls back to automatic lookup.
|
||||
|
||||
|
@ -63,7 +63,7 @@ following steps:
|
|||
- First `AWS_SECRET_ACCESS_KEY`, then `AWS_SECRET_KEY`
|
||||
|
||||
2. Look for [local AWS configuration
|
||||
files](http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html#cli-config-files)
|
||||
files](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html#cli-config-files)
|
||||
- First `~/.aws/credentials`
|
||||
- Next based on `AWS_PROFILE`
|
||||
|
||||
|
@ -80,7 +80,7 @@ packer build on your workstation, in Atlas, or on another build server.
|
|||
## Using an IAM Instance Profile
|
||||
|
||||
If AWS keys are not specified in the template, a
|
||||
[credentials](http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html#cli-config-files)
|
||||
[credentials](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html#cli-config-files)
|
||||
file or through environment variables Packer will use credentials provided by
|
||||
the instance's IAM profile, if it has one.
|
||||
|
||||
|
@ -97,6 +97,7 @@ Packer to work:
|
|||
"ec2:DeleteVolume",
|
||||
"ec2:CreateKeypair",
|
||||
"ec2:DeleteKeypair",
|
||||
"ec2:DescribeSubnets",
|
||||
"ec2:CreateSecurityGroup",
|
||||
"ec2:DeleteSecurityGroup",
|
||||
"ec2:AuthorizeSecurityGroupIngress",
|
||||
|
@ -131,7 +132,7 @@ roles, you may encounter an error like this one:
|
|||
==> amazon-ebs: Error launching source instance: You are not authorized to perform this operation.
|
||||
|
||||
You can read more about why this happens on the [Amazon Security
|
||||
Blog](http://blogs.aws.amazon.com/security/post/Tx3M0IFB5XBOCQX/Granting-Permission-to-Launch-EC2-Instances-with-IAM-Roles-PassRole-Permission).
|
||||
Blog](https://blogs.aws.amazon.com/security/post/Tx3M0IFB5XBOCQX/Granting-Permission-to-Launch-EC2-Instances-with-IAM-Roles-PassRole-Permission).
|
||||
The example policy below may help packer work with IAM roles. Note that this
|
||||
example provides more than the minimal set of permissions needed for packer to
|
||||
work, but specifics will depend on your use-case.
|
||||
|
|
|
@ -14,7 +14,7 @@ page_title: DigitalOcean Builder
|
|||
Type: `digitalocean`
|
||||
|
||||
The `digitalocean` Packer builder is able to create new images for use with
|
||||
[DigitalOcean](http://www.digitalocean.com). The builder takes a source image,
|
||||
[DigitalOcean](https://www.digitalocean.com). The builder takes a source image,
|
||||
runs any provisioning necessary on the image after launching it, then snapshots
|
||||
it into a reusable image. This reusable image can then be used as the foundation
|
||||
of new servers that are launched within DigitalOcean.
|
||||
|
|
|
@ -11,7 +11,7 @@ page_title: Docker Builder
|
|||
|
||||
Type: `docker`
|
||||
|
||||
The `docker` Packer builder builds [Docker](http://www.docker.io) images using
|
||||
The `docker` Packer builder builds [Docker](https://www.docker.io) images using
|
||||
Docker. The builder starts a Docker container, runs provisioners within this
|
||||
container, then exports the container for reuse or commits the image.
|
||||
|
||||
|
@ -26,7 +26,7 @@ the section on [Dockerfiles](#toc_8).
|
|||
The Docker builder must run on a machine that has Docker installed. Therefore
|
||||
the builder only works on machines that support Docker (modern Linux machines).
|
||||
If you want to use Packer to build Docker containers on another platform, use
|
||||
[Vagrant](http://www.vagrantup.com) to start a Linux environment, then run
|
||||
[Vagrant](https://www.vagrantup.com) to start a Linux environment, then run
|
||||
Packer within that environment.
|
||||
|
||||
## Basic Example: Export
|
||||
|
@ -75,7 +75,7 @@ You must specify (only) one of `commit`, `discard`, or `export_path`.
|
|||
|
||||
- `discard` (boolean) - Throw away the container when the build is complete.
|
||||
This is useful for the [artifice
|
||||
post-processor](https://packer.io/docs/post-processors/artifice.html).
|
||||
post-processor](https://www.packer.io/docs/post-processors/artifice.html).
|
||||
|
||||
- `export_path` (string) - The path where the final container will be exported
|
||||
as a tar file.
|
||||
|
@ -214,6 +214,42 @@ nearly-identical sequence definitions, as demonstrated by the example below:
|
|||
}
|
||||
```
|
||||
|
||||
<span id="amazon-ec2-container-registry"></span>
|
||||
|
||||
## Amazon EC2 Container Registry
|
||||
|
||||
Packer can tag and push images for use in
|
||||
[Amazon EC2 Container Registry](https://aws.amazon.com/ecr/). The post
|
||||
processors work as described above and example configuration properties are
|
||||
shown below:
|
||||
|
||||
``` {.javascript}
|
||||
{
|
||||
"post-processors": [
|
||||
[
|
||||
{
|
||||
"type": "docker-tag",
|
||||
"repository": "12345.dkr.ecr.us-east-1.amazonaws.com/packer",
|
||||
"tag": "0.7"
|
||||
},
|
||||
{
|
||||
"type": "docker-push",
|
||||
"login": true,
|
||||
"login_email": "none",
|
||||
"login_username": "AWS",
|
||||
"login_password": "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
|
||||
"login_server": "https://12345.dkr.ecr.us-east-1.amazonaws.com/"
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
See the
|
||||
[AWS documentation](https://docs.aws.amazon.com/AmazonECR/latest/userguide/Registries.html)
|
||||
for steps to obtain Amazon ECR registry credentials.
|
||||
|
||||
|
||||
## Dockerfiles
|
||||
|
||||
This builder allows you to build Docker images *without* Dockerfiles.
|
||||
|
|
|
@ -118,6 +118,9 @@ builder.
|
|||
Not required if you run Packer on a GCE instance with a service account.
|
||||
Instructions for creating file or using service accounts are above.
|
||||
|
||||
- `address` (string) - The name of a pre-allocated static external IP address.
|
||||
Note, must be the name and not the actual IP address.
|
||||
|
||||
- `disk_size` (integer) - The size of the disk in GB. This defaults to `10`,
|
||||
which is 10GB.
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ page_title: 'Parallels Builder (from an ISO)'
|
|||
Type: `parallels-iso`
|
||||
|
||||
The Parallels Packer builder is able to create [Parallels Desktop for
|
||||
Mac](http://www.parallels.com/products/desktop/) virtual machines and export
|
||||
Mac](https://www.parallels.com/products/desktop/) virtual machines and export
|
||||
them in the PVM format, starting from an ISO image.
|
||||
|
||||
The builder builds a virtual machine by creating a new virtual machine from
|
||||
|
|
|
@ -12,7 +12,7 @@ page_title: 'Parallels Builder (from a PVM)'
|
|||
Type: `parallels-pvm`
|
||||
|
||||
This Parallels builder is able to create [Parallels Desktop for
|
||||
Mac](http://www.parallels.com/products/desktop/) virtual machines and export
|
||||
Mac](https://www.parallels.com/products/desktop/) virtual machines and export
|
||||
them in the PVM format, starting from an existing PVM (exported virtual machine
|
||||
image).
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ page_title: Parallels Builder
|
|||
# Parallels Builder
|
||||
|
||||
The Parallels Packer builder is able to create [Parallels Desktop for
|
||||
Mac](http://www.parallels.com/products/desktop/) virtual machines and export
|
||||
Mac](https://www.parallels.com/products/desktop/) virtual machines and export
|
||||
them in the PVM format.
|
||||
|
||||
Packer actually comes with multiple builders able to create Parallels machines,
|
||||
|
@ -30,8 +30,8 @@ the following Parallels builders:
|
|||
## Requirements
|
||||
|
||||
In addition to [Parallels Desktop for
|
||||
Mac](http://www.parallels.com/products/desktop/) this requires the [Parallels
|
||||
Virtualization SDK](http://www.parallels.com/downloads/desktop/).
|
||||
Mac](https://www.parallels.com/products/desktop/) this requires the [Parallels
|
||||
Virtualization SDK](https://www.parallels.com/downloads/desktop/).
|
||||
|
||||
The SDK can be installed by downloading and following the instructions in the
|
||||
dmg.
|
||||
|
|
|
@ -322,7 +322,7 @@ directory of the SSH user.
|
|||
|
||||
In order to perform extra customization of the virtual machine, a template can
|
||||
define extra calls to `VBoxManage` to perform.
|
||||
[VBoxManage](http://www.virtualbox.org/manual/ch08.html) is the command-line
|
||||
[VBoxManage](https://www.virtualbox.org/manual/ch08.html) is the command-line
|
||||
interface to VirtualBox where you can completely control VirtualBox. It can be
|
||||
used to do things such as set RAM, CPUs, etc.
|
||||
|
||||
|
|
|
@ -226,7 +226,7 @@ directory of the SSH user.
|
|||
|
||||
In order to perform extra customization of the virtual machine, a template can
|
||||
define extra calls to `VBoxManage` to perform.
|
||||
[VBoxManage](http://www.virtualbox.org/manual/ch08.html) is the command-line
|
||||
[VBoxManage](https://www.virtualbox.org/manual/ch08.html) is the command-line
|
||||
interface to VirtualBox where you can completely control VirtualBox. It can be
|
||||
used to do things such as set RAM, CPUs, etc.
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ page_title: VirtualBox Builder
|
|||
# VirtualBox Builder
|
||||
|
||||
The VirtualBox Packer builder is able to create
|
||||
[VirtualBox](http://www.virtualbox.org) virtual machines and export them in the
|
||||
[VirtualBox](https://www.virtualbox.org) virtual machines and export them in the
|
||||
OVA or OVF format.
|
||||
|
||||
Packer actually comes with multiple builders able to create VirtualBox machines,
|
||||
|
|
|
@ -15,12 +15,12 @@ Type: `vmware-iso`
|
|||
|
||||
This VMware Packer builder is able to create VMware virtual machines from an ISO
|
||||
file as a source. It currently supports building virtual machines on hosts
|
||||
running [VMware Fusion](http://www.vmware.com/products/fusion/overview.html) for
|
||||
running [VMware Fusion](https://www.vmware.com/products/fusion/overview.html) for
|
||||
OS X, [VMware
|
||||
Workstation](http://www.vmware.com/products/workstation/overview.html) for Linux
|
||||
and Windows, and [VMware Player](http://www.vmware.com/products/player/) on
|
||||
Workstation](https://www.vmware.com/products/workstation/overview.html) for Linux
|
||||
and Windows, and [VMware Player](https://www.vmware.com/products/player/) on
|
||||
Linux. It can also build machines directly on [VMware vSphere
|
||||
Hypervisor](http://www.vmware.com/products/vsphere-hypervisor/) using SSH as
|
||||
Hypervisor](https://www.vmware.com/products/vsphere-hypervisor/) using SSH as
|
||||
opposed to the vSphere API.
|
||||
|
||||
The builder builds a virtual machine by creating a new virtual machine from
|
||||
|
@ -105,7 +105,7 @@ builder.
|
|||
default is "1", which corresponds to a growable virtual disk split in
|
||||
2GB files. This option is for advanced usage, modify only if you know what
|
||||
you're doing. For more information, please consult the [Virtual Disk Manager
|
||||
User's Guide](http://www.vmware.com/pdf/VirtualDiskManager.pdf) for desktop
|
||||
User's Guide](https://www.vmware.com/pdf/VirtualDiskManager.pdf) for desktop
|
||||
VMware clients. For ESXi, refer to the proper ESXi documentation.
|
||||
|
||||
- `floppy_files` (array of strings) - A list of files to place onto a floppy
|
||||
|
@ -187,6 +187,10 @@ builder.
|
|||
the remote machine. By default this is empty. This only has an effect if
|
||||
`remote_type` is enabled.
|
||||
|
||||
- `remote_private_key_file` (string) - The path to the PEM encoded private key
|
||||
file for the user used to access the remote machine. By default this is empty.
|
||||
This only has an effect if `remote_type` is enabled.
|
||||
|
||||
- `remote_type` (string) - The type of remote machine that will be used to
|
||||
build this VM rather than a local desktop product. The only value accepted
|
||||
for this currently is "esx5". If this is not set, a desktop product will
|
||||
|
@ -398,6 +402,8 @@ modify as well:
|
|||
|
||||
- `remote_password` - The SSH password for access to the remote machine.
|
||||
|
||||
- `remote_private_key_file` - The SSH key for access to the remote machine.
|
||||
|
||||
- `format` (string) - Either "ovf", "ova" or "vmx", this specifies the output
|
||||
format of the exported virtual machine. This defaults to "ovf".
|
||||
Before using this option, you need to install `ovftool`.
|
||||
|
|
|
@ -15,10 +15,10 @@ Type: `vmware-vmx`
|
|||
This VMware Packer builder is able to create VMware virtual machines from an
|
||||
existing VMware virtual machine (a VMX file). It currently supports building
|
||||
virtual machines on hosts running [VMware Fusion
|
||||
Professional](http://www.vmware.com/products/fusion-professional/) for OS X,
|
||||
[VMware Workstation](http://www.vmware.com/products/workstation/overview.html)
|
||||
Professional](https://www.vmware.com/products/fusion-professional/) for OS X,
|
||||
[VMware Workstation](https://www.vmware.com/products/workstation/overview.html)
|
||||
for Linux and Windows, and [VMware
|
||||
Player](http://www.vmware.com/products/player/) on Linux.
|
||||
Player](https://www.vmware.com/products/player/) on Linux.
|
||||
|
||||
The builder builds a virtual machine by cloning the VMX file using the clone
|
||||
capabilities introduced in VMware Fusion Professional 6, Workstation 10, and
|
||||
|
|
|
@ -26,7 +26,7 @@ both the post-processor and push commands can be used independently.
|
|||
scripts, to Atlas. Take care not to upload files that you don't intend to, like
|
||||
secrets or large binaries. **If you have secrets in your Packer template, you
|
||||
should [move them into environment
|
||||
variables](https://packer.io/docs/templates/user-variables.html).**
|
||||
variables](https://www.packer.io/docs/templates/user-variables.html).**
|
||||
|
||||
Most push behavior is [configured in your packer
|
||||
template](/docs/templates/push.html). You can override or supplement your
|
||||
|
@ -34,10 +34,6 @@ configuration using the options below.
|
|||
|
||||
## Options
|
||||
|
||||
- `-message` - A message to identify the purpose or changes in this Packer
|
||||
template much like a VCS commit message. This message will be passed to the
|
||||
Packer build service. This option is also available as a short option `-m`.
|
||||
|
||||
- `-token` - Your access token for the Atlas API.
|
||||
|
||||
-> Login to Atlas to [generate an Atlas
|
||||
|
@ -59,7 +55,7 @@ you can also use `-token` on the command line.
|
|||
Push a Packer template:
|
||||
|
||||
``` {.shell}
|
||||
$ packer push -m "Updating the apache version" template.json
|
||||
$ packer push template.json
|
||||
```
|
||||
|
||||
Push a Packer template with a custom token:
|
||||
|
@ -81,7 +77,7 @@ download it during the packer run.
|
|||
|
||||
If you want to build a private `.iso` file you can upload the `.iso` to a secure
|
||||
file hosting service like [Amazon
|
||||
S3](http://docs.aws.amazon.com/AmazonS3/latest/dev/ShareObjectPreSignedURL.html),
|
||||
S3](https://docs.aws.amazon.com/AmazonS3/latest/dev/ShareObjectPreSignedURL.html),
|
||||
[Google Cloud
|
||||
Storage](https://cloud.google.com/storage/docs/gsutil/commands/signurl), or
|
||||
[Azure File
|
||||
|
|
|
@ -157,7 +157,7 @@ it would be convenient to cache the file. This sort of caching is a core part of
|
|||
Packer that is exposed to builders.
|
||||
|
||||
The cache interface is `packer.Cache`. It behaves much like a Go
|
||||
[RWMutex](http://golang.org/pkg/sync/#RWMutex). The builder requests a "lock" on
|
||||
[RWMutex](https://golang.org/pkg/sync/#RWMutex). The builder requests a "lock" on
|
||||
certain cache keys, and is given exclusive access to that key for the duration
|
||||
of the lock. This locking mechanism allows multiple builders to share cache data
|
||||
even though they're running in parallel.
|
||||
|
|
|
@ -14,7 +14,7 @@ reading this, it is assumed that you're comfortable with Packer and also know
|
|||
the [basics of how Plugins work](/docs/extend/plugins.html), from a user
|
||||
standpoint.
|
||||
|
||||
Packer plugins must be written in [Go](http://golang.org/), so it is also
|
||||
Packer plugins must be written in [Go](https://golang.org/), so it is also
|
||||
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
|
||||
makes it extremely easy to develop Packer plugins.
|
||||
|
@ -36,7 +36,7 @@ uses, because they're completely isolated into the process space of the plugin
|
|||
itself.
|
||||
|
||||
And, thanks to Go's
|
||||
[interfaces](http://golang.org/doc/effective_go.html#interfaces_and_types), it
|
||||
[interfaces](https://golang.org/doc/effective_go.html#interfaces_and_types), it
|
||||
doesn't even look like inter-process communication is occurring. You just use
|
||||
the interfaces like normal, but in fact they're being executed in a remote
|
||||
process. Pretty cool.
|
||||
|
|
|
@ -32,9 +32,9 @@ After unzipping the package, the directory should contain a set of binary
|
|||
programs, such as `packer`, `packer-build-amazon-ebs`, etc. The final step to
|
||||
installation is to make sure the directory you installed Packer to is on the
|
||||
PATH. See [this
|
||||
page](http://stackoverflow.com/questions/14637979/how-to-permanently-set-path-on-linux)
|
||||
page](https://stackoverflow.com/questions/14637979/how-to-permanently-set-path-on-linux)
|
||||
for instructions on setting the PATH on Linux and Mac. [This
|
||||
page](http://stackoverflow.com/questions/1618280/where-can-i-set-path-to-make-exe-on-windows)
|
||||
page](https://stackoverflow.com/questions/1618280/where-can-i-set-path-to-make-exe-on-windows)
|
||||
contains instructions for setting the PATH on Windows.
|
||||
|
||||
## Verifying the Installation
|
||||
|
|
|
@ -62,10 +62,10 @@ the log variable `PACKER_LOG_PATH` using powershell environment variables. For
|
|||
example:
|
||||
|
||||
$env:PACKER_LOG=1
|
||||
$env:PACKER_LOG_PATH="packerlog.txt"
|
||||
$env:PACKER_LOG_PATH="packerlog.txt"
|
||||
|
||||
If you find a bug with Packer, please include the detailed log by using a
|
||||
service such as [gist](http://gist.github.com).
|
||||
service such as [gist](https://gist.github.com).
|
||||
|
||||
## Issues Installing Ubuntu Packages
|
||||
|
||||
|
|
|
@ -25,9 +25,9 @@ extracting the docker container and throwing away the EC2 instance.
|
|||
|
||||
After overriding the artifact with artifice, you can use it with other
|
||||
post-processors like
|
||||
[compress](https://packer.io/docs/post-processors/compress.html),
|
||||
[docker-push](https://packer.io/docs/post-processors/docker-push.html),
|
||||
[Atlas](https://packer.io/docs/post-processors/atlas.html), or a third-party
|
||||
[compress](https://www.packer.io/docs/post-processors/compress.html),
|
||||
[docker-push](https://www.packer.io/docs/post-processors/docker-push.html),
|
||||
[Atlas](https://www.packer.io/docs/post-processors/atlas.html), or a third-party
|
||||
post-processor.
|
||||
|
||||
Artifice allows you to use the familiar packer workflow to create a fresh,
|
||||
|
@ -67,7 +67,7 @@ The configuration allows you to specify which files comprise your artifact.
|
|||
This minimal example:
|
||||
|
||||
1. Spins up a cloned VMware virtual machine
|
||||
2. Installs a [consul](https://consul.io/) release
|
||||
2. Installs a [consul](https://www.consul.io/) release
|
||||
3. Downloads the consul binary
|
||||
4. Packages it into a `.tar.gz` file
|
||||
5. Uploads it to Atlas.
|
||||
|
|
|
@ -38,7 +38,7 @@ Here is an example workflow:
|
|||
example `hashicorp/foobar`, to create the artifact in Atlas or update the
|
||||
version if the artifact already exists
|
||||
3. The new version is ready and available to be used in deployments with a tool
|
||||
like [Terraform](https://terraform.io)
|
||||
like [Terraform](https://www.terraform.io)
|
||||
|
||||
## Configuration
|
||||
|
||||
|
@ -88,8 +88,8 @@ you can also use `token` configuration option.
|
|||
"access_key": "{{user `aws_access_key`}}",
|
||||
"secret_key": "{{user `aws_secret_key`}}",
|
||||
"region": "us-east-1",
|
||||
"source_ami": "ami-de0d9eb7",
|
||||
"instance_type": "t1.micro",
|
||||
"source_ami": "ami-72b9e018",
|
||||
"instance_type": "t2.micro",
|
||||
"ssh_username": "ubuntu",
|
||||
"ami_name": "atlas-example {{timestamp}}"
|
||||
}],
|
||||
|
|
|
@ -18,12 +18,12 @@ Type: `vagrant-cloud`
|
|||
|
||||
The Packer Vagrant Cloud post-processor receives a Vagrant box from the
|
||||
`vagrant` post-processor and pushes it to Vagrant Cloud. [Vagrant
|
||||
Cloud](https://vagrantcloud.com) hosts and serves boxes to Vagrant, allowing you
|
||||
Cloud](https://atlas.hashicorp.com) hosts and serves boxes to Vagrant, allowing you
|
||||
to version and distribute boxes to an organization in a simple way.
|
||||
|
||||
You'll need to be familiar with Vagrant Cloud, have an upgraded account to
|
||||
enable box hosting, and be distributing your box via the [shorthand
|
||||
name](http://docs.vagrantup.com/v2/cli/box.html) configuration.
|
||||
name](https://docs.vagrantup.com/v2/cli/box.html) configuration.
|
||||
|
||||
## Workflow
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ page_title: 'Vagrant Post-Processor'
|
|||
Type: `vagrant`
|
||||
|
||||
The Packer Vagrant post-processor takes a build and converts the artifact into a
|
||||
valid [Vagrant](http://www.vagrantup.com) box, if it can. This lets you use
|
||||
valid [Vagrant](https://www.vagrantup.com) box, if it can. This lets you use
|
||||
Packer to automatically create arbitrarily complex Vagrant boxes, and is in fact
|
||||
how the official boxes distributed by Vagrant are created.
|
||||
|
||||
|
@ -22,7 +22,7 @@ If you've never used a post-processor before, please read the documentation on
|
|||
knowledge will be expected for the remainder of this document.
|
||||
|
||||
Because Vagrant boxes are
|
||||
[provider-specific](http://docs.vagrantup.com/v2/boxes/format.html), the Vagrant
|
||||
[provider-specific](https://docs.vagrantup.com/v2/boxes/format.html), the Vagrant
|
||||
post-processor is hardcoded to understand how to convert the artifacts of
|
||||
certain builders into proper boxes for their respective providers.
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ Type: `ansible-local`
|
|||
The `ansible-local` Packer provisioner configures Ansible to run on the machine
|
||||
by Packer from local Playbook and Role files. Playbooks and Roles can be
|
||||
uploaded from your local machine to the remote machine. Ansible is run in [local
|
||||
mode](http://docs.ansible.com/playbooks_delegation.html#local-playbooks) via the
|
||||
mode](https://docs.ansible.com/ansible/playbooks_delegation.html#local-playbooks) via the
|
||||
`ansible-playbook` command.
|
||||
|
||||
-> **Note:** Ansible will *not* be installed automatically by this
|
||||
|
|
|
@ -50,6 +50,10 @@ configuration is actually required.
|
|||
should use a custom configuration template. See the dedicated "Chef
|
||||
Configuration" section below for more details.
|
||||
|
||||
- `encrypted_data_bag_secret_path` (string) - The path to the file containing
|
||||
the secret for encrypted data bags. By default, this is empty, so no secret
|
||||
will be available.
|
||||
|
||||
- `execute_command` (string) - The command used to execute Chef. This has
|
||||
various [configuration template
|
||||
variables](/docs/templates/configuration-templates.html) available. See
|
||||
|
@ -71,9 +75,9 @@ configuration is actually required.
|
|||
then the sudo will be omitted.
|
||||
|
||||
- `run_list` (array of strings) - The [run
|
||||
list](http://docs.chef.io/essentials_node_object_run_lists.html)
|
||||
for Chef. By default this is empty, and will use the run list sent down by
|
||||
the Chef Server.
|
||||
list](http://docs.chef.io/essentials_node_object_run_lists.html) for Chef.
|
||||
By default this is empty, and will use the run list sent down by the
|
||||
Chef Server.
|
||||
|
||||
- `server_url` (string) - The URL to the Chef server. This is required.
|
||||
|
||||
|
@ -136,6 +140,7 @@ This template is a [configuration
|
|||
template](/docs/templates/configuration-templates.html) and has a set of
|
||||
variables available to use:
|
||||
|
||||
- `EncryptedDataBagSecretPath` - The path to the encrypted data bag secret
|
||||
- `NodeName` - The node name set in the configuration.
|
||||
- `ServerUrl` - The URL of the Chef Server set in the configuration.
|
||||
- `ValidationKeyPath` - Path to the validation key, if it is set.
|
||||
|
@ -181,3 +186,65 @@ to 777. This is to ensure that Packer can upload and make use of that directory.
|
|||
However, once the machine is created, you usually don't want to keep these
|
||||
directories with those permissions. To change the permissions on the
|
||||
directories, append a shell provisioner after Chef to modify them.
|
||||
|
||||
## Examples
|
||||
|
||||
### Chef Client Local Mode
|
||||
|
||||
The following example shows how to run the `chef-cilent` provisioner in local
|
||||
mode, while passing a `run_list` using a variable.
|
||||
|
||||
**Local environment variables**
|
||||
|
||||
# Machines Chef directory
|
||||
export PACKER_CHEF_DIR=/var/chef-packer
|
||||
# Comma separated run_list
|
||||
export PACKER_CHEF_RUN_LIST="recipe[apt],recipe[nginx]"
|
||||
...
|
||||
|
||||
**Packer variables**
|
||||
|
||||
Set the necessary Packer variables using environment variables or provide a [var
|
||||
file](/docs/templates/user-variables.html).
|
||||
|
||||
``` {.liquid}
|
||||
"variables": {
|
||||
"chef_dir": "{{env `PACKER_CHEF_DIR`}}",
|
||||
"chef_run_list": "{{env `PACKER_CHEF_RUN_LIST`}}",
|
||||
"chef_client_config_tpl": "{{env `PACKER_CHEF_CLIENT_CONFIG_TPL`}}",
|
||||
"packer_chef_bootstrap_dir": "{{env `PACKER_CHEF_BOOTSTRAP_DIR`}}" ,
|
||||
"packer_uid": "{{env `PACKER_UID`}}",
|
||||
"packer_gid": "{{env `PACKER_GID`}}"
|
||||
}
|
||||
```
|
||||
|
||||
**Setup the** `chef-client` **provisioner**
|
||||
|
||||
Make sure we have the correct directories and permissions for the `chef-client`
|
||||
provisioner. You will need to bootstrap the Chef run by providing the necessary
|
||||
cookbooks using Berkshelf or some other means.
|
||||
|
||||
``` {.liquid}
|
||||
{
|
||||
"type": "file",
|
||||
"source": "{{user `packer_chef_bootstrap_dir`}}",
|
||||
"destination": "/tmp/bootstrap"
|
||||
},
|
||||
{
|
||||
"type": "shell",
|
||||
"inline": [
|
||||
"sudo mkdir -p {{user `chef_dir`}}",
|
||||
"sudo mkdir -p /tmp/packer-chef-client",
|
||||
"sudo chown {{user `packer_uid`}}.{{user `packer_gid`}} /tmp/packer-chef-client",
|
||||
"sudo sh /tmp/bootstrap/bootstrap.sh"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "chef-client",
|
||||
"server_url": "http://localhost:8889",
|
||||
"config_template": "{{user `chef_client_config_tpl`}}/client.rb.tpl",
|
||||
"skip_clean_node": true,
|
||||
"skip_clean_client": true,
|
||||
"run_list": "{{user `chef_run_list`}}"
|
||||
}
|
||||
```
|
||||
|
|