Merge branch 'master' into configure-manifest-dir
This commit is contained in:
commit
afd0aea8df
24
CHANGELOG.md
24
CHANGELOG.md
|
@ -1,9 +1,31 @@
|
|||
## 0.4.1 (unreleased)
|
||||
## 0.5.0 (unreleased)
|
||||
|
||||
|
||||
|
||||
## 0.4.1 (December 7, 2013)
|
||||
|
||||
IMPROVEMENTS:
|
||||
|
||||
* builder/amazon/ebs: New option allows associating a public IP with
|
||||
non-default VPC instances. [GH-660]
|
||||
* builder/openstack: A "proxy\_url" setting was added to define an HTTP
|
||||
proxy to use when building with this builder. [GH-637]
|
||||
|
||||
BUG FIXES:
|
||||
|
||||
* core: Don't change background color on CLI anymore, making things look
|
||||
a tad nicer in some terminals.
|
||||
* core: multiple ISO URLs works properly in all builders. [GH-683]
|
||||
* builder/amazon/chroot: Block when obtaining file lock to allow
|
||||
parallel builds. [GH-689]
|
||||
* builder/amazon/instance: Add location flag to upload bundle command
|
||||
so that building AMIs works out of us-east-1 [GH-679]
|
||||
* builder/vmware: Cleanup of VMX keys works properly so cd-rom won't
|
||||
get stuck with ISO. [GH-685]
|
||||
* builder/vmware: File cleanup is more resilient to file delete races
|
||||
with the operating system. [GH-675]
|
||||
* provisioner/puppet-masterless: Check for hiera config path existence
|
||||
properly. [GH-656]
|
||||
|
||||
## 0.4.0 (November 19, 2013)
|
||||
|
||||
|
|
|
@ -77,7 +77,7 @@ For some additional dependencies, Go needs [Mercurial](http://mercurial.selenic.
|
|||
and [Bazaar](http://bazaar.canonical.com/en/) to be installed.
|
||||
Packer itself doesn't require these, but a dependency of a dependency does.
|
||||
|
||||
You'll also need [`gox`](https://github.com/mitchellh/packer)
|
||||
You'll also need [`gox`](https://github.com/mitchellh/gox)
|
||||
to compile packer. You can install that with:
|
||||
|
||||
```
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
package chroot
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
@ -14,13 +13,8 @@ const LOCK_NB = 4
|
|||
const LOCK_UN = 8
|
||||
|
||||
func lockFile(f *os.File) error {
|
||||
err := syscall.Flock(int(f.Fd()), LOCK_EX|LOCK_NB)
|
||||
err := syscall.Flock(int(f.Fd()), LOCK_EX)
|
||||
if err != nil {
|
||||
errno, ok := err.(syscall.Errno)
|
||||
if ok && errno == syscall.EWOULDBLOCK {
|
||||
return errors.New("file already locked")
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ func (s *StepFlock) Run(state multistep.StateBag) multistep.StepAction {
|
|||
|
||||
// LOCK!
|
||||
if err := lockFile(f); err != nil {
|
||||
err := fmt.Errorf("Error creating lock: %s", err)
|
||||
err := fmt.Errorf("Error obtaining lock: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
|
|
|
@ -11,20 +11,21 @@ import (
|
|||
// RunConfig contains configuration for running an instance from a source
|
||||
// AMI and details on how to access that launched image.
|
||||
type RunConfig struct {
|
||||
SourceAmi string `mapstructure:"source_ami"`
|
||||
IamInstanceProfile string `mapstructure:"iam_instance_profile"`
|
||||
InstanceType string `mapstructure:"instance_type"`
|
||||
UserData string `mapstructure:"user_data"`
|
||||
UserDataFile string `mapstructure:"user_data_file"`
|
||||
RawSSHTimeout string `mapstructure:"ssh_timeout"`
|
||||
SSHUsername string `mapstructure:"ssh_username"`
|
||||
SSHPort int `mapstructure:"ssh_port"`
|
||||
SecurityGroupId string `mapstructure:"security_group_id"`
|
||||
SecurityGroupIds []string `mapstructure:"security_group_ids"`
|
||||
SubnetId string `mapstructure:"subnet_id"`
|
||||
TemporaryKeyPairName string `mapstructure:"temporary_key_pair_name"`
|
||||
VpcId string `mapstructure:"vpc_id"`
|
||||
AvailabilityZone string `mapstructure:"availability_zone"`
|
||||
SourceAmi string `mapstructure:"source_ami"`
|
||||
IamInstanceProfile string `mapstructure:"iam_instance_profile"`
|
||||
InstanceType string `mapstructure:"instance_type"`
|
||||
UserData string `mapstructure:"user_data"`
|
||||
UserDataFile string `mapstructure:"user_data_file"`
|
||||
RawSSHTimeout string `mapstructure:"ssh_timeout"`
|
||||
SSHUsername string `mapstructure:"ssh_username"`
|
||||
SSHPort int `mapstructure:"ssh_port"`
|
||||
SecurityGroupId string `mapstructure:"security_group_id"`
|
||||
SecurityGroupIds []string `mapstructure:"security_group_ids"`
|
||||
SubnetId string `mapstructure:"subnet_id"`
|
||||
AssociatePublicIpAddress bool `mapstructure:"associate_public_ip_address"`
|
||||
TemporaryKeyPairName string `mapstructure:"temporary_key_pair_name"`
|
||||
VpcId string `mapstructure:"vpc_id"`
|
||||
AvailabilityZone string `mapstructure:"availability_zone"`
|
||||
|
||||
// Unexported fields that are calculated from others
|
||||
sshTimeout time.Duration
|
||||
|
|
|
@ -20,7 +20,11 @@ func SSHAddress(e *ec2.EC2, port int) func(multistep.StateBag) (string, error) {
|
|||
if i.DNSName != "" {
|
||||
host = i.DNSName
|
||||
} else if i.VpcId != "" {
|
||||
host = i.PrivateIpAddress
|
||||
if i.PublicIpAddress != "" {
|
||||
host = i.PublicIpAddress
|
||||
} else {
|
||||
host = i.PrivateIpAddress
|
||||
}
|
||||
}
|
||||
|
||||
if host != "" {
|
||||
|
|
|
@ -10,16 +10,17 @@ import (
|
|||
)
|
||||
|
||||
type StepRunSourceInstance struct {
|
||||
Debug bool
|
||||
ExpectedRootDevice string
|
||||
InstanceType string
|
||||
UserData string
|
||||
UserDataFile string
|
||||
SourceAMI string
|
||||
IamInstanceProfile string
|
||||
SubnetId string
|
||||
AvailabilityZone string
|
||||
BlockDevices BlockDevices
|
||||
Debug bool
|
||||
ExpectedRootDevice string
|
||||
InstanceType string
|
||||
UserData string
|
||||
UserDataFile string
|
||||
SourceAMI string
|
||||
IamInstanceProfile string
|
||||
SubnetId string
|
||||
AssociatePublicIpAddress bool
|
||||
AvailabilityZone string
|
||||
BlockDevices BlockDevices
|
||||
|
||||
instance *ec2.Instance
|
||||
}
|
||||
|
@ -47,17 +48,18 @@ func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepActi
|
|||
}
|
||||
|
||||
runOpts := &ec2.RunInstances{
|
||||
KeyName: keyName,
|
||||
ImageId: s.SourceAMI,
|
||||
InstanceType: s.InstanceType,
|
||||
UserData: []byte(userData),
|
||||
MinCount: 0,
|
||||
MaxCount: 0,
|
||||
SecurityGroups: securityGroups,
|
||||
IamInstanceProfile: s.IamInstanceProfile,
|
||||
SubnetId: s.SubnetId,
|
||||
BlockDevices: s.BlockDevices.BuildLaunchDevices(),
|
||||
AvailZone: s.AvailabilityZone,
|
||||
KeyName: keyName,
|
||||
ImageId: s.SourceAMI,
|
||||
InstanceType: s.InstanceType,
|
||||
UserData: []byte(userData),
|
||||
MinCount: 0,
|
||||
MaxCount: 0,
|
||||
SecurityGroups: securityGroups,
|
||||
IamInstanceProfile: s.IamInstanceProfile,
|
||||
SubnetId: s.SubnetId,
|
||||
AssociatePublicIpAddress: s.AssociatePublicIpAddress,
|
||||
BlockDevices: s.BlockDevices.BuildLaunchDevices(),
|
||||
AvailZone: s.AvailabilityZone,
|
||||
}
|
||||
|
||||
ui.Say("Launching a source AWS instance...")
|
||||
|
@ -114,6 +116,10 @@ func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepActi
|
|||
ui.Message(fmt.Sprintf("Public DNS: %s", s.instance.DNSName))
|
||||
}
|
||||
|
||||
if s.instance.PublicIpAddress != "" {
|
||||
ui.Message(fmt.Sprintf("Public IP: %s", s.instance.PublicIpAddress))
|
||||
}
|
||||
|
||||
if s.instance.PrivateIpAddress != "" {
|
||||
ui.Message(fmt.Sprintf("Private IP: %s", s.instance.PrivateIpAddress))
|
||||
}
|
||||
|
|
|
@ -93,16 +93,17 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
VpcId: b.config.VpcId,
|
||||
},
|
||||
&awscommon.StepRunSourceInstance{
|
||||
Debug: b.config.PackerDebug,
|
||||
ExpectedRootDevice: "ebs",
|
||||
InstanceType: b.config.InstanceType,
|
||||
UserData: b.config.UserData,
|
||||
UserDataFile: b.config.UserDataFile,
|
||||
SourceAMI: b.config.SourceAmi,
|
||||
IamInstanceProfile: b.config.IamInstanceProfile,
|
||||
SubnetId: b.config.SubnetId,
|
||||
AvailabilityZone: b.config.AvailabilityZone,
|
||||
BlockDevices: b.config.BlockDevices,
|
||||
Debug: b.config.PackerDebug,
|
||||
ExpectedRootDevice: "ebs",
|
||||
InstanceType: b.config.InstanceType,
|
||||
UserData: b.config.UserData,
|
||||
UserDataFile: b.config.UserDataFile,
|
||||
SourceAMI: b.config.SourceAmi,
|
||||
IamInstanceProfile: b.config.IamInstanceProfile,
|
||||
SubnetId: b.config.SubnetId,
|
||||
AssociatePublicIpAddress: b.config.AssociatePublicIpAddress,
|
||||
AvailabilityZone: b.config.AvailabilityZone,
|
||||
BlockDevices: b.config.BlockDevices,
|
||||
},
|
||||
&common.StepConnectSSH{
|
||||
SSHAddress: awscommon.SSHAddress(ec2conn, b.config.SSHPort),
|
||||
|
|
|
@ -74,6 +74,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
|||
"-s {{.SecretKey}} " +
|
||||
"-d {{.BundleDirectory}} " +
|
||||
"--batch " +
|
||||
"--location {{.Region}} " +
|
||||
"--retry"
|
||||
}
|
||||
|
||||
|
@ -196,16 +197,17 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
VpcId: b.config.VpcId,
|
||||
},
|
||||
&awscommon.StepRunSourceInstance{
|
||||
Debug: b.config.PackerDebug,
|
||||
ExpectedRootDevice: "instance-store",
|
||||
InstanceType: b.config.InstanceType,
|
||||
IamInstanceProfile: b.config.IamInstanceProfile,
|
||||
UserData: b.config.UserData,
|
||||
UserDataFile: b.config.UserDataFile,
|
||||
SourceAMI: b.config.SourceAmi,
|
||||
SubnetId: b.config.SubnetId,
|
||||
AvailabilityZone: b.config.AvailabilityZone,
|
||||
BlockDevices: b.config.BlockDevices,
|
||||
Debug: b.config.PackerDebug,
|
||||
ExpectedRootDevice: "instance-store",
|
||||
InstanceType: b.config.InstanceType,
|
||||
IamInstanceProfile: b.config.IamInstanceProfile,
|
||||
UserData: b.config.UserData,
|
||||
UserDataFile: b.config.UserDataFile,
|
||||
SourceAMI: b.config.SourceAmi,
|
||||
SubnetId: b.config.SubnetId,
|
||||
AssociatePublicIpAddress: b.config.AssociatePublicIpAddress,
|
||||
AvailabilityZone: b.config.AvailabilityZone,
|
||||
BlockDevices: b.config.BlockDevices,
|
||||
},
|
||||
&common.StepConnectSSH{
|
||||
SSHAddress: awscommon.SSHAddress(ec2conn, b.config.SSHPort),
|
||||
|
|
|
@ -11,6 +11,7 @@ type uploadCmdData struct {
|
|||
BucketName string
|
||||
BundleDirectory string
|
||||
ManifestPath string
|
||||
Region string
|
||||
SecretKey string
|
||||
}
|
||||
|
||||
|
@ -23,12 +24,20 @@ func (s *StepUploadBundle) Run(state multistep.StateBag) multistep.StepAction {
|
|||
manifestPath := state.Get("manifest_path").(string)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
var err error
|
||||
region, err := config.Region()
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error retrieving region: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
config.BundleUploadCommand, err = config.tpl.Process(config.BundleUploadCommand, uploadCmdData{
|
||||
AccessKey: config.AccessKey,
|
||||
BucketName: config.S3Bucket,
|
||||
BundleDirectory: config.BundleDestination,
|
||||
ManifestPath: manifestPath,
|
||||
Region: region.Name,
|
||||
SecretKey: config.SecretKey,
|
||||
})
|
||||
if err != nil {
|
||||
|
|
|
@ -4,6 +4,8 @@ import (
|
|||
"fmt"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"github.com/rackspace/gophercloud"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
)
|
||||
|
||||
|
@ -14,6 +16,7 @@ type AccessConfig struct {
|
|||
Project string `mapstructure:"project"`
|
||||
Provider string `mapstructure:"provider"`
|
||||
RawRegion string `mapstructure:"region"`
|
||||
ProxyUrl string `mapstructure:"proxy_url"`
|
||||
}
|
||||
|
||||
// Auth returns a valid Auth object for access to openstack services, or
|
||||
|
@ -23,6 +26,7 @@ func (c *AccessConfig) Auth() (gophercloud.AccessProvider, error) {
|
|||
password := c.Password
|
||||
project := c.Project
|
||||
provider := c.Provider
|
||||
proxy := c.ProxyUrl
|
||||
|
||||
if username == "" {
|
||||
username = os.Getenv("SDK_USERNAME")
|
||||
|
@ -47,6 +51,19 @@ func (c *AccessConfig) Auth() (gophercloud.AccessProvider, error) {
|
|||
authoptions.TenantName = project
|
||||
}
|
||||
|
||||
// For corporate networks it may be the case where we want our API calls
|
||||
// to be sent through a separate HTTP proxy than external traffic.
|
||||
if proxy != "" {
|
||||
url, err := url.Parse(proxy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// The gophercloud.Context has a UseCustomClient method which
|
||||
// would allow us to override with a new instance of http.Client.
|
||||
http.DefaultTransport = &http.Transport{Proxy: http.ProxyURL(url)}
|
||||
}
|
||||
|
||||
return gophercloud.Authenticate(provider, authoptions)
|
||||
}
|
||||
|
||||
|
|
|
@ -217,7 +217,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
|||
|
||||
if !(b.config.Format == "qcow2" || b.config.Format == "raw") {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, errors.New("invalid format, only 'qcow2' or 'img' are allowed"))
|
||||
errs, errors.New("invalid format, only 'qcow2' or 'raw' are allowed"))
|
||||
}
|
||||
|
||||
if !(b.config.Accelerator == "kvm" || b.config.Accelerator == "xen") {
|
||||
|
|
|
@ -314,7 +314,8 @@ func (d *ESX5Driver) checkGuestIPHackEnabled() error {
|
|||
}
|
||||
|
||||
if record["IntValue"] != "1" {
|
||||
return errors.New("GuestIPHack is required, enable with:\n" +
|
||||
return errors.New(
|
||||
"GuestIPHack is required, enable by running this on the ESX machine:\n" +
|
||||
"esxcli system settings advanced set -o /Net/GuestIPHack -i 1")
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"github.com/mitchellh/multistep"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
|
@ -48,8 +49,11 @@ func (stepCleanFiles) Run(state multistep.StateBag) multistep.StepAction {
|
|||
if !keep {
|
||||
ui.Message(fmt.Sprintf("Deleting: %s", path))
|
||||
if err = dir.Remove(path); err != nil {
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
// Only report the error if the file still exists
|
||||
if _, serr := os.Stat(path); serr == nil || !os.IsNotExist(serr) {
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,12 +55,12 @@ func (s stepCleanVMX) Run(state multistep.StateBag) multistep.StepAction {
|
|||
continue
|
||||
}
|
||||
|
||||
filenameKey := match + ".filename"
|
||||
filenameKey := match + "filename"
|
||||
if filename, ok := vmxData[filenameKey]; ok {
|
||||
if filename == isoPath {
|
||||
// Change the CD-ROM device back to auto-detect to eject
|
||||
vmxData[filenameKey] = "auto detect"
|
||||
vmxData[match+".devicetype"] = "cdrom-raw"
|
||||
vmxData[match+"devicetype"] = "cdrom-raw"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -105,21 +105,21 @@ func DownloadableURL(original string) (string, error) {
|
|||
url.Path = strings.Replace(url.Path, `\`, `/`, -1)
|
||||
}
|
||||
|
||||
if _, err := os.Stat(url.Path); err != nil {
|
||||
return "", err
|
||||
}
|
||||
// Only do the filepath transformations if the file appears
|
||||
// to actually exist.
|
||||
if _, err := os.Stat(url.Path); err == nil {
|
||||
url.Path, err = filepath.Abs(url.Path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
url.Path, err = filepath.Abs(url.Path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
url.Path, err = filepath.EvalSymlinks(url.Path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
url.Path, err = filepath.EvalSymlinks(url.Path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
url.Path = filepath.Clean(url.Path)
|
||||
}
|
||||
|
||||
url.Path = filepath.Clean(url.Path)
|
||||
}
|
||||
|
||||
// Make sure it is lowercased
|
||||
|
|
|
@ -144,8 +144,8 @@ func TestDownloadableURL_FilePaths(t *testing.T) {
|
|||
for _, prefix := range []string{"", "file://"} {
|
||||
// Nonexistent file
|
||||
_, err = DownloadableURL(prefix + "i/dont/exist")
|
||||
if err == nil {
|
||||
t.Fatal("expected err")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
// Good file
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
var GitCommit string
|
||||
|
||||
// The version of packer.
|
||||
const Version = "0.4.1"
|
||||
const Version = "0.5.0"
|
||||
|
||||
// Any pre-release marker for the version. If this is "" (empty string),
|
||||
// then it means that it is a final release. Otherwise, this is the
|
||||
|
|
|
@ -152,7 +152,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
|
|||
|
||||
// Validation
|
||||
if p.config.HieraConfigPath != "" {
|
||||
info, err := os.Stat(p.config.ManifestFile)
|
||||
info, err := os.Stat(p.config.HieraConfigPath)
|
||||
if err != nil {
|
||||
errs = packer.MultiErrorAppend(errs,
|
||||
fmt.Errorf("hiera_config_path is invalid: %s", err))
|
||||
|
|
|
@ -210,40 +210,3 @@ prevent packages installed by your provisioners from starting services:
|
|||
]
|
||||
}
|
||||
</pre>
|
||||
|
||||
## Using an IAM Instance Profile
|
||||
|
||||
If AWS keys are not specified in the template or through environment variables
|
||||
Packer will use credentials provided by the instance's IAM profile, if it has one.
|
||||
|
||||
The following policy document provides the minimal set permissions necessary for Packer to work:
|
||||
|
||||
<pre class="prettyprint">
|
||||
{
|
||||
"Statement": [{
|
||||
"Effect": "Allow",
|
||||
"Action" : [
|
||||
"ec2:AttachVolume",
|
||||
"ec2:CreateVolume",
|
||||
"ec2:DeleteVolume",
|
||||
"ec2:DescribeVolumes",
|
||||
"ec2:DetachVolume",
|
||||
|
||||
"ec2:DescribeInstances",
|
||||
|
||||
"ec2:CreateSnapshot",
|
||||
"ec2:DeleteSnapshot",
|
||||
"ec2:DescribeSnapshots",
|
||||
|
||||
"ec2:DescribeImages",
|
||||
"ec2:RegisterImage",
|
||||
|
||||
"ec2:CreateTags"
|
||||
],
|
||||
"Resource" : "*"
|
||||
}]
|
||||
}
|
||||
</pre>
|
||||
|
||||
Depending on what setting you use the following Actions might have to be allowed as well:
|
||||
* `ec2:ModifyImageAttribute` when using `ami_description`
|
||||
|
|
|
@ -111,6 +111,10 @@ Optional:
|
|||
* `subnet_id` (string) - If using VPC, the ID of the subnet, such as
|
||||
"subnet-12345def", where Packer will launch the EC2 instance.
|
||||
|
||||
* `associate_public_ip_address` (bool) - If using a non-default VPC, public
|
||||
IP addresses are not provided by default. If this is toggled, your new
|
||||
instance will get a Public IP.
|
||||
|
||||
* `tags` (object of key/value strings) - Tags applied to the AMI.
|
||||
|
||||
* `user_data` (string) - User data to apply when launching the instance.
|
||||
|
|
|
@ -150,6 +150,10 @@ Optional:
|
|||
* `subnet_id` (string) - If using VPC, the ID of the subnet, such as
|
||||
"subnet-12345def", where Packer will launch the EC2 instance.
|
||||
|
||||
* `associate_public_ip_address` (bool) - If using a non-default VPC, public
|
||||
IP addresses are not provided by default. If this is toggled, your new
|
||||
instance will get a Public IP.
|
||||
|
||||
* `tags` (object of key/value strings) - Tags applied to the AMI.
|
||||
|
||||
* `user_data` (string) - User data to apply when launching the instance.
|
||||
|
@ -269,6 +273,7 @@ sudo -n ec2-upload-bundle \
|
|||
-s {{.SecretKey}} \
|
||||
-d {{.BundleDirectory}} \
|
||||
--batch \
|
||||
--location {{.Region}} \
|
||||
--retry
|
||||
```
|
||||
|
||||
|
|
|
@ -30,3 +30,40 @@ AMI. Packer supports the following builders at the moment:
|
|||
<a href="/docs/builders/amazon-ebs.html">amazon-ebs builder</a>. It is
|
||||
much easier to use and Amazon generally recommends EBS-backed images nowadays.
|
||||
</div>
|
||||
|
||||
## Using an IAM Instance Profile
|
||||
|
||||
If AWS keys are not specified in the template or through environment variables
|
||||
Packer will use credentials provided by the instance's IAM profile, if it has one.
|
||||
|
||||
The following policy document provides the minimal set permissions necessary for Packer to work:
|
||||
|
||||
<pre class="prettyprint">
|
||||
{
|
||||
"Statement": [{
|
||||
"Effect": "Allow",
|
||||
"Action" : [
|
||||
"ec2:AttachVolume",
|
||||
"ec2:CreateVolume",
|
||||
"ec2:DeleteVolume",
|
||||
"ec2:DescribeVolumes",
|
||||
"ec2:DetachVolume",
|
||||
|
||||
"ec2:DescribeInstances",
|
||||
|
||||
"ec2:CreateSnapshot",
|
||||
"ec2:DeleteSnapshot",
|
||||
"ec2:DescribeSnapshots",
|
||||
|
||||
"ec2:DescribeImages",
|
||||
"ec2:RegisterImage",
|
||||
|
||||
"ec2:CreateTags"
|
||||
],
|
||||
"Resource" : "*"
|
||||
}]
|
||||
}
|
||||
</pre>
|
||||
|
||||
Depending on what setting you use the following Actions might have to be allowed as well:
|
||||
* `ec2:ModifyImageAttribute` when using `ami_description`
|
||||
|
|
|
@ -59,7 +59,7 @@ Optional:
|
|||
|
||||
* `ssh_timeout` (string) - The time to wait for SSH to become available
|
||||
before timing out. The format of this value is a duration such as "5s"
|
||||
or "5m". The default SSH timeout is "1m".
|
||||
or "1m". The default SSH timeout is "5m".
|
||||
|
||||
* `ssh_username` (string) - The username to use in order to communicate
|
||||
over SSH to the running server. The default is "root".
|
||||
|
|
|
@ -115,7 +115,7 @@ Optional:
|
|||
commands or kickstart type scripts must have proper adjustments for
|
||||
resulting device names. The Qemu builder uses "virtio" by default.
|
||||
|
||||
* `format` (string) - Either "qcow2" or "img", this specifies the output
|
||||
* `format` (string) - Either "qcow2" or "raw", this specifies the output
|
||||
format of the virtual machine image. This defaults to "qcow2".
|
||||
|
||||
* `headless` (bool) - Packer defaults to building virtual machines by
|
||||
|
|
|
@ -87,7 +87,8 @@ Optional:
|
|||
The 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).
|
||||
[Virtual Disk Manager User's Guide](http://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 put onto a floppy
|
||||
disk that is attached when the VM is booted for the first time. This is
|
||||
|
|
|
@ -46,10 +46,14 @@ briefly. Create a file `example.json` and fill it with the following contents:
|
|||
|
||||
<pre class="prettyprint">
|
||||
{
|
||||
"variables": {
|
||||
"aws_access_key": "",
|
||||
"aws_secret_key": ""
|
||||
},
|
||||
"builders": [{
|
||||
"type": "amazon-ebs",
|
||||
"access_key": "YOUR KEY HERE",
|
||||
"secret_key": "YOUR SECRET KEY HERE",
|
||||
"access_key": "{{user `aws_access_key`}}",
|
||||
"secret_key": "{{user `aws_secret_key`}}",
|
||||
"region": "us-east-1",
|
||||
"source_ami": "ami-de0d9eb7",
|
||||
"instance_type": "t1.micro",
|
||||
|
@ -59,9 +63,11 @@ briefly. Create a file `example.json` and fill it with the following contents:
|
|||
}
|
||||
</pre>
|
||||
|
||||
Please fill in the `access_key` and `secret_key` with the proper values
|
||||
for your account. Your security credentials can be found on
|
||||
[this page](https://console.aws.amazon.com/iam/home?#security_credential).
|
||||
When building, you'll pass in the `aws_access_key` and `aws_access_key` as
|
||||
a [user variable](/docs/templates/user-variables.html), keeping your secret
|
||||
keys out of the template. You can create security credentials
|
||||
on [this page](https://console.aws.amazon.com/iam/home?#security_credential).
|
||||
An example IAM policy document can be found in the [Amazon EC2 builder docs](/docs/builders/amazon.html).
|
||||
|
||||
This is a basic template that is ready-to-go. It should be immediately recognizable
|
||||
as a normal, basic JSON object. Within the object, the `builders` section
|
||||
|
@ -106,7 +112,10 @@ should look similar to below. Note that this process typically takes a
|
|||
few minutes.
|
||||
|
||||
```
|
||||
$ packer build example.json
|
||||
$ packer build \
|
||||
-var 'aws_access_key=YOUR ACCESS KEY' \
|
||||
-var 'aws_secret_key=YOUR SECRET KEY' \
|
||||
example.json
|
||||
==> amazon-ebs: amazon-ebs output will be in this color.
|
||||
|
||||
==> amazon-ebs: Creating temporary keypair for this instance...
|
||||
|
|
|
@ -63,13 +63,23 @@ array.
|
|||
<pre class="prettyprint">
|
||||
{
|
||||
"type": "digitalocean",
|
||||
"api_key": "INSERT API KEY HERE",
|
||||
"client_id": "INSERT CLIENT ID HERE"
|
||||
"api_key": "{{user `do_api_key`}}",
|
||||
"client_id": "{{user `do_client_id`}}"
|
||||
}
|
||||
</pre>
|
||||
|
||||
Fill in your `api_key` and `client_id` for DigitalOcean as necessary.
|
||||
The entire template should now [look like this](https://gist.github.com/mitchellh/51a447e38e7e496eb29c).
|
||||
You'll also need to modify the `variables` section of the template
|
||||
to include the access keys for DigitalOcean.
|
||||
|
||||
<pre class="prettyprint">
|
||||
"variables": {
|
||||
...
|
||||
"do_api_key": "",
|
||||
"do_client_id": ""
|
||||
}
|
||||
</pre>
|
||||
|
||||
The entire template should now [look like this](https://gist.github.com/pearkes/cc5f8505eee5403a43a6).
|
||||
|
||||
Additional builders are simply added to the `builders` array in the template.
|
||||
This tells Packer to build multiple images. The builder `type` values don't
|
||||
|
@ -87,13 +97,18 @@ manual that contains a listing of all the available configuration options.
|
|||
|
||||
## Build
|
||||
|
||||
Now run `packer build example.json`. The output is too verbose to include
|
||||
Now run `packer build` with your user variables. The output is too verbose to include
|
||||
all of it, but a portion of it is reproduced below. Note that the ordering
|
||||
and wording of the lines may be slightly different, but the effect is the
|
||||
same.
|
||||
|
||||
```
|
||||
$ packer build example.json
|
||||
$ packer build \
|
||||
-var 'aws_access_key=YOUR ACCESS KEY' \
|
||||
-var 'aws_secret_key=YOUR SECRET KEY' \
|
||||
-var 'do_api_key=YOUR API KEY' \
|
||||
-var 'do_client_id=YOUR CLIENT ID' \
|
||||
example.json
|
||||
==> amazon-ebs: amazon-ebs output will be in this color.
|
||||
==> digitalocean: digitalocean output will be in this color.
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ block below.
|
|||
|
||||
<pre class="prettyprint">
|
||||
{
|
||||
"variables": [...],
|
||||
"builders": [...],
|
||||
|
||||
"provisioners": [{
|
||||
|
|
Loading…
Reference in New Issue