Merge remote-tracking branch 'origin/master' into dynamic-source-ami
This commit is contained in:
commit
defdd1ecf3
|
@ -12,7 +12,7 @@ FOR BUGS:
|
|||
|
||||
Describe the problem and include the following information:
|
||||
|
||||
- Packer Version
|
||||
- Packer version from `packer version`
|
||||
- Host platform
|
||||
- Debug log output from `PACKER_LOG=1 packer build template.json`.
|
||||
Please paste this in a gist https://gist.github.com
|
||||
|
|
|
@ -26,6 +26,7 @@ IMPROVEMENTS:
|
|||
* builder/digitalocean: Use `state_timeout` for unlock and off transitions.
|
||||
[GH-3444]
|
||||
* builder/google: Added support for `image_family` [GH-3503]
|
||||
* builder/google: Use gcloud application default credentials. [GH-3655]
|
||||
* builder/null: Can now be used with WinRM [GH-2525]
|
||||
* builder/parallels: Now pauses between `boot_command` entries when running
|
||||
with `-debug` [GH-3547]
|
||||
|
@ -43,6 +44,8 @@ IMPROVEMENTS:
|
|||
[GH-3347]
|
||||
* builder/qemu: Now pauses between `boot_command` entries when running with
|
||||
`-debug` [GH-3547]
|
||||
* provisioner/ansible: Improved logging and error handling [GH-3477]
|
||||
* provisioner/ansible-local: Support for ansible-galaxy [GH-3350] [GH-3836]
|
||||
* provisioner/chef: Added `knife_command` option and added a correct default
|
||||
value for Windows [GH-3622]
|
||||
* provisioner/puppet: Added `execute_command` option [GH-3614]
|
||||
|
@ -56,8 +59,11 @@ BUG FIXES:
|
|||
* post-processor/vsphere: Fix upload failures with vsphere [GH-3321]
|
||||
* provisioner/ansible: Properly set host key checking even when a custom ENV
|
||||
is specified [GH-3568]
|
||||
* builder/amazon: Use `temporary_key_pair_name` when specified. [GH-3739]
|
||||
* builder/amazon: Add 0.5 cents to discovered spot price. [GH-3662]
|
||||
* builder/azure: check for empty resource group [GH-3606]
|
||||
* builder/azure: fix token validity test [GH-3609]
|
||||
* builder/virtualbox: Respect `ssh_host` [GH-3617]
|
||||
* builder/vmware: Re-introduce case sensitive VMX keys [GH-2707]
|
||||
* builder/vmware: Don't check for poweron errors on ESXi [GH-3195]
|
||||
* builder/vmware: Respect `ssh_host`/`winrm_host` on ESXi [GH-3738]
|
||||
|
|
|
@ -55,10 +55,14 @@ type RunConfig struct {
|
|||
}
|
||||
|
||||
func (c *RunConfig) Prepare(ctx *interpolate.Context) []error {
|
||||
// if we are not given an explicit keypairname, create a temporary one
|
||||
// If we are not given an explicit ssh_keypair_name,
|
||||
// then create a temporary one, but only if the
|
||||
// temporary_key_pair_name has not been provided.
|
||||
if c.SSHKeyPairName == "" {
|
||||
c.TemporaryKeyPairName = fmt.Sprintf(
|
||||
"packer %s", uuid.TimeOrderedUUID())
|
||||
if c.TemporaryKeyPairName == "" {
|
||||
c.TemporaryKeyPairName = fmt.Sprintf(
|
||||
"packer_%s", uuid.TimeOrderedUUID())
|
||||
}
|
||||
}
|
||||
|
||||
if c.WindowsPasswordTimeout == 0 {
|
||||
|
|
|
@ -3,6 +3,7 @@ package common
|
|||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/mitchellh/packer/helper/communicator"
|
||||
|
@ -166,6 +167,21 @@ func TestRunConfigPrepare_TemporaryKeyPairName(t *testing.T) {
|
|||
}
|
||||
|
||||
if c.TemporaryKeyPairName == "" {
|
||||
t.Fatal("keypair empty")
|
||||
t.Fatal("keypair name is empty")
|
||||
}
|
||||
|
||||
// Match prefix and UUID, e.g. "packer_5790d491-a0b8-c84c-c9d2-2aea55086550".
|
||||
r := regexp.MustCompile(`\Apacker_(?:(?i)[a-f\d]{8}(?:-[a-f\d]{4}){3}-[a-f\d]{12}?)\z`)
|
||||
if !r.MatchString(c.TemporaryKeyPairName) {
|
||||
t.Fatal("keypair name is not valid")
|
||||
}
|
||||
|
||||
c.TemporaryKeyPairName = "ssh-key-123"
|
||||
if err := c.Prepare(nil); len(err) != 0 {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if c.TemporaryKeyPairName != "ssh-key-123" {
|
||||
t.Fatal("keypair name does not match")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -141,6 +141,10 @@ func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepActi
|
|||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
} else {
|
||||
// Add 0.5 cents to minimum spot bid to ensure capacity will be available
|
||||
// Avoids price-too-low error in active markets which can fluctuate
|
||||
price = price + 0.005
|
||||
}
|
||||
|
||||
spotPrice = strconv.FormatFloat(price, 'f', -1, 64)
|
||||
|
|
|
@ -118,21 +118,21 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
BlockDevices: b.config.BlockDevices,
|
||||
},
|
||||
&awscommon.StepRunSourceInstance{
|
||||
Debug: b.config.PackerDebug,
|
||||
ExpectedRootDevice: "ebs",
|
||||
SpotPrice: b.config.SpotPrice,
|
||||
SpotPriceProduct: b.config.SpotPriceAutoProduct,
|
||||
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,
|
||||
EbsOptimized: b.config.EbsOptimized,
|
||||
AvailabilityZone: b.config.AvailabilityZone,
|
||||
BlockDevices: b.config.BlockDevices,
|
||||
Tags: b.config.RunTags,
|
||||
Debug: b.config.PackerDebug,
|
||||
ExpectedRootDevice: "ebs",
|
||||
SpotPrice: b.config.SpotPrice,
|
||||
SpotPriceProduct: b.config.SpotPriceAutoProduct,
|
||||
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,
|
||||
EbsOptimized: b.config.EbsOptimized,
|
||||
AvailabilityZone: b.config.AvailabilityZone,
|
||||
BlockDevices: b.config.BlockDevices,
|
||||
Tags: b.config.RunTags,
|
||||
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
|
||||
},
|
||||
&stepTagEBSVolumes{
|
||||
|
|
|
@ -50,15 +50,20 @@ func NewDriverGCE(ui packer.Ui, p string, a *AccountFile) (Driver, error) {
|
|||
// your service account.
|
||||
client = conf.Client(oauth2.NoContext)
|
||||
} else {
|
||||
log.Printf("[INFO] Requesting Google token via GCE Service Role...")
|
||||
client = &http.Client{
|
||||
Transport: &oauth2.Transport{
|
||||
// Fetch from Google Compute Engine's metadata server to retrieve
|
||||
// an access token for the provided account.
|
||||
// If no account is specified, "default" is used.
|
||||
Source: google.ComputeTokenSource(""),
|
||||
},
|
||||
}
|
||||
log.Printf("[INFO] Requesting Google token via GCE API Default Client Token Source...")
|
||||
client, err = google.DefaultClient(oauth2.NoContext, DriverScopes...)
|
||||
// The DefaultClient uses the DefaultTokenSource of the google lib.
|
||||
// The DefaultTokenSource uses the "Application Default Credentials"
|
||||
// It looks for credentials in the following places, preferring the first location found:
|
||||
// 1. A JSON file whose path is specified by the
|
||||
// GOOGLE_APPLICATION_CREDENTIALS environment variable.
|
||||
// 2. A JSON file in a location known to the gcloud command-line tool.
|
||||
// On Windows, this is %APPDATA%/gcloud/application_default_credentials.json.
|
||||
// On other systems, $HOME/.config/gcloud/application_default_credentials.json.
|
||||
// 3. On Google App Engine it uses the appengine.AccessToken function.
|
||||
// 4. On Google Compute Engine and Google App Engine Managed VMs, it fetches
|
||||
// credentials from the metadata server.
|
||||
// (In this final case any provided scopes are ignored.)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
|
|
@ -7,8 +7,10 @@ import (
|
|||
gossh "golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
func CommHost(state multistep.StateBag) (string, error) {
|
||||
return "127.0.0.1", nil
|
||||
func CommHost(host string) func(multistep.StateBag) (string, error) {
|
||||
return func(state multistep.StateBag) (string, error) {
|
||||
return host, nil
|
||||
}
|
||||
}
|
||||
|
||||
func SSHPort(state multistep.StateBag) (int, error) {
|
||||
|
|
|
@ -21,6 +21,10 @@ type SSHConfig struct {
|
|||
}
|
||||
|
||||
func (c *SSHConfig) Prepare(ctx *interpolate.Context) []error {
|
||||
if c.Comm.SSHHost == "" {
|
||||
c.Comm.SSHHost = "127.0.0.1"
|
||||
}
|
||||
|
||||
if c.SSHHostPortMin == 0 {
|
||||
c.SSHHostPortMin = 2222
|
||||
}
|
||||
|
|
|
@ -235,7 +235,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
},
|
||||
&communicator.StepConnect{
|
||||
Config: &b.config.SSHConfig.Comm,
|
||||
Host: vboxcommon.CommHost,
|
||||
Host: vboxcommon.CommHost(b.config.SSHConfig.Comm.SSHHost),
|
||||
SSHConfig: vboxcommon.SSHConfigFunc(b.config.SSHConfig),
|
||||
SSHPort: vboxcommon.SSHPort,
|
||||
},
|
||||
|
|
|
@ -104,7 +104,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
},
|
||||
&communicator.StepConnect{
|
||||
Config: &b.config.SSHConfig.Comm,
|
||||
Host: vboxcommon.CommHost,
|
||||
Host: vboxcommon.CommHost(b.config.SSHConfig.Comm.SSHHost),
|
||||
SSHConfig: vboxcommon.SSHConfigFunc(b.config.SSHConfig),
|
||||
SSHPort: vboxcommon.SSHPort,
|
||||
},
|
||||
|
|
|
@ -242,6 +242,11 @@ func (ESX5Driver) UpdateVMX(_, password string, port uint, data map[string]strin
|
|||
|
||||
func (d *ESX5Driver) CommHost(state multistep.StateBag) (string, error) {
|
||||
config := state.Get("config").(*Config)
|
||||
sshc := config.SSHConfig.Comm
|
||||
port := sshc.SSHPort
|
||||
if sshc.Type == "winrm" {
|
||||
port = sshc.WinRMPort
|
||||
}
|
||||
|
||||
if address, ok := state.GetOk("vm_address"); ok {
|
||||
return address.(string), nil
|
||||
|
@ -286,7 +291,7 @@ func (d *ESX5Driver) CommHost(state multistep.StateBag) (string, error) {
|
|||
}
|
||||
// When multiple NICs are connected to the same network, choose
|
||||
// one that has a route back. This Dial should ensure that.
|
||||
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", record["IPAddress"], d.Port), 2*time.Second)
|
||||
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", record["IPAddress"], port), 2*time.Second)
|
||||
if err != nil {
|
||||
if e, ok := err.(*net.OpError); ok {
|
||||
if e.Timeout() {
|
||||
|
|
|
@ -9,6 +9,8 @@ azure_group_name=
|
|||
azure_storage_name=
|
||||
azure_subscription_id= # Derived from the account after login
|
||||
azure_tenant_id= # Derived from the account after login
|
||||
location=
|
||||
azure_object_id=
|
||||
|
||||
showhelp() {
|
||||
echo "azure-setup"
|
||||
|
@ -88,7 +90,7 @@ askSubscription() {
|
|||
|
||||
askName() {
|
||||
echo ""
|
||||
echo "Choose a name for your resource group, storage account, and client"
|
||||
echo "Choose a name for your resource group, storage account and client"
|
||||
echo "client. This is arbitrary, but it must not already be in use by"
|
||||
echo "any of those resources. ALPHANUMERIC ONLY. Ex: mypackerbuild"
|
||||
echo -n "> "
|
||||
|
@ -113,9 +115,17 @@ askSecret() {
|
|||
fi
|
||||
}
|
||||
|
||||
askLocation() {
|
||||
azure location list
|
||||
echo ""
|
||||
echo "Choose which region your resource group and storage account will be created."
|
||||
echo -n "> "
|
||||
read location
|
||||
}
|
||||
|
||||
createResourceGroup() {
|
||||
echo "==> Creating resource group"
|
||||
azure group create -n $meta_name -l westus
|
||||
azure group create -n $meta_name -l $location
|
||||
if [ $? -eq 0 ]; then
|
||||
azure_group_name=$meta_name
|
||||
else
|
||||
|
@ -126,7 +136,7 @@ createResourceGroup() {
|
|||
|
||||
createStorageAccount() {
|
||||
echo "==> Creating storage account"
|
||||
azure storage account create -g $meta_name -l westus --sku-name LRS --kind Storage $meta_name
|
||||
azure storage account create -g $meta_name -l $location --sku-name LRS --kind Storage $meta_name
|
||||
if [ $? -eq 0 ]; then
|
||||
azure_storage_name=$meta_name
|
||||
else
|
||||
|
@ -135,18 +145,10 @@ createStorageAccount() {
|
|||
fi
|
||||
}
|
||||
|
||||
createApplication() {
|
||||
echo "==> Creating application"
|
||||
azure_client_id=$(azure ad app create -n $meta_name -i http://$meta_name --home-page http://$meta_name -p $azure_client_secret --json | jq -r .appId)
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error creating application: $meta_name @ http://$meta_name"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
createServicePrinciple() {
|
||||
echo "==> Creating service principal"
|
||||
azure ad sp create $azure_client_id
|
||||
azure_object_id=$(azure ad sp create -n $meta_name --home-page http://$meta_name --identifier-uris http://$meta_name/example -p $azure_client_secret --json | jq -r .objectId)
|
||||
azure_client_id=$(azure ad app show -c $meta_name --json | jq -r .[0].appId)
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error creating service principal: $azure_client_id"
|
||||
exit 1
|
||||
|
@ -155,7 +157,7 @@ createServicePrinciple() {
|
|||
|
||||
createPermissions() {
|
||||
echo "==> Creating permissions"
|
||||
azure role assignment create -o "Owner" --spn http://$meta_name -c /subscriptions/$azure_subscription_id
|
||||
azure role assignment create --objectId $azure_object_id -o "Owner" -c /subscriptions/$azure_subscription_id
|
||||
# We want to use this more conservative scope but it does not work with the
|
||||
# current implementation which uses temporary resource groups
|
||||
# azure role assignment create --spn http://$meta_name -g $azure_group_name -o "API Management Service Contributor"
|
||||
|
@ -169,11 +171,15 @@ showConfigs() {
|
|||
echo ""
|
||||
echo "Use the following configuration for your packer template:"
|
||||
echo ""
|
||||
echo "{"
|
||||
echo " \"client_id\": \"$azure_client_id\","
|
||||
echo " \"client_secret\": \"$azure_client_secret\","
|
||||
echo " \"object_id\": \"$azure_object_id\","
|
||||
echo " \"subscription_id\": \"$azure_subscription_id\","
|
||||
echo " \"tenant_id\": \"$azure_tenant_id\","
|
||||
echo " \"resource_group_name\": \"$azure_group_name\","
|
||||
echo " \"storage_account\": \"$azure_storage_name\","
|
||||
echo " \"subscription_id\": \"$azure_subscription_id\","
|
||||
echo "}"
|
||||
echo ""
|
||||
}
|
||||
|
||||
|
@ -186,6 +192,7 @@ setup() {
|
|||
askSubscription
|
||||
askName
|
||||
askSecret
|
||||
askLocation
|
||||
|
||||
# Some of the resources take a while to converge in the API. To make the
|
||||
# script more reliable we'll add a sleep after we create each resource.
|
||||
|
@ -194,8 +201,6 @@ setup() {
|
|||
sleep 5
|
||||
createStorageAccount
|
||||
sleep 5
|
||||
createApplication
|
||||
sleep 5
|
||||
createServicePrinciple
|
||||
sleep 5
|
||||
createPermissions
|
||||
|
|
|
@ -52,6 +52,12 @@ type Config struct {
|
|||
|
||||
// The optional inventory groups
|
||||
InventoryGroups []string `mapstructure:"inventory_groups"`
|
||||
|
||||
// The optional ansible-galaxy requirements file
|
||||
GalaxyFile string `mapstructure:"galaxy_file"`
|
||||
|
||||
// The command to run ansible-galaxy
|
||||
GalaxyCommand string
|
||||
}
|
||||
|
||||
type Provisioner struct {
|
||||
|
@ -74,6 +80,9 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
|
|||
if p.config.Command == "" {
|
||||
p.config.Command = "ANSIBLE_FORCE_COLOR=1 PYTHONUNBUFFERED=1 ansible-playbook"
|
||||
}
|
||||
if p.config.GalaxyCommand == "" {
|
||||
p.config.GalaxyCommand = "ansible-galaxy"
|
||||
}
|
||||
|
||||
if p.config.StagingDir == "" {
|
||||
p.config.StagingDir = DefaultStagingDir
|
||||
|
@ -94,6 +103,14 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
|
|||
}
|
||||
}
|
||||
|
||||
// Check that the galaxy file exists, if configured
|
||||
if len(p.config.GalaxyFile) > 0 {
|
||||
err = validateFileConfig(p.config.GalaxyFile, "galaxy_file", true)
|
||||
if err != nil {
|
||||
errs = packer.MultiErrorAppend(errs, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the playbook_dir directory exists, if configured
|
||||
if len(p.config.PlaybookDir) > 0 {
|
||||
if err := validateDirConfig(p.config.PlaybookDir, "playbook_dir"); err != nil {
|
||||
|
@ -181,6 +198,15 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
|
|||
}()
|
||||
}
|
||||
|
||||
if len(p.config.GalaxyFile) > 0 {
|
||||
ui.Message("Uploading galaxy file...")
|
||||
src = p.config.GalaxyFile
|
||||
dst = filepath.ToSlash(filepath.Join(p.config.StagingDir, filepath.Base(src)))
|
||||
if err := p.uploadFile(ui, comm, dst, src); err != nil {
|
||||
return fmt.Errorf("Error uploading galaxy file: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
ui.Message("Uploading inventory file...")
|
||||
src = p.config.InventoryFile
|
||||
dst = filepath.ToSlash(filepath.Join(p.config.StagingDir, filepath.Base(src)))
|
||||
|
@ -242,6 +268,27 @@ func (p *Provisioner) Cancel() {
|
|||
os.Exit(0)
|
||||
}
|
||||
|
||||
func (p *Provisioner) executeGalaxy(ui packer.Ui, comm packer.Communicator) error {
|
||||
rolesDir := filepath.ToSlash(filepath.Join(p.config.StagingDir, "roles"))
|
||||
galaxyFile := filepath.ToSlash(filepath.Join(p.config.StagingDir, filepath.Base(p.config.GalaxyFile)))
|
||||
|
||||
// ansible-galaxy install -r requirements.yml -p roles/
|
||||
command := fmt.Sprintf("cd %s && %s install -r %s -p %s",
|
||||
p.config.StagingDir, p.config.GalaxyCommand, galaxyFile, rolesDir)
|
||||
ui.Message(fmt.Sprintf("Executing Ansible Galaxy: %s", command))
|
||||
cmd := &packer.RemoteCmd{
|
||||
Command: command,
|
||||
}
|
||||
if err := cmd.StartWithUi(comm, ui); err != nil {
|
||||
return err
|
||||
}
|
||||
if cmd.ExitStatus != 0 {
|
||||
// ansible-galaxy version 2.0.0.2 doesn't return exit codes on error..
|
||||
return fmt.Errorf("Non-zero exit status: %d", cmd.ExitStatus)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Provisioner) executeAnsible(ui packer.Ui, comm packer.Communicator) error {
|
||||
playbook := filepath.ToSlash(filepath.Join(p.config.StagingDir, filepath.Base(p.config.PlaybookFile)))
|
||||
inventory := filepath.ToSlash(filepath.Join(p.config.StagingDir, filepath.Base(p.config.InventoryFile)))
|
||||
|
@ -251,6 +298,13 @@ func (p *Provisioner) executeAnsible(ui packer.Ui, comm packer.Communicator) err
|
|||
extraArgs = " " + strings.Join(p.config.ExtraArguments, " ")
|
||||
}
|
||||
|
||||
// Fetch external dependencies
|
||||
if len(p.config.GalaxyFile) > 0 {
|
||||
if err := p.executeGalaxy(ui, comm); err != nil {
|
||||
return fmt.Errorf("Error executing Ansible Galaxy: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
command := fmt.Sprintf("cd %s && %s %s%s -c local -i %s",
|
||||
p.config.StagingDir, p.config.Command, playbook, extraArgs, inventory)
|
||||
ui.Message(fmt.Sprintf("Executing Ansible: %s", command))
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
|
||||
"github.com/mitchellh/packer/packer"
|
||||
|
@ -100,62 +101,71 @@ func (c *adapter) handleSession(newChannel ssh.NewChannel) error {
|
|||
for req := range in {
|
||||
switch req.Type {
|
||||
case "pty-req":
|
||||
log.Println("ansible provisioner pty-req request")
|
||||
// accept pty-req requests, but don't actually do anything. Necessary for OpenSSH and sudo.
|
||||
req.Reply(true, nil)
|
||||
|
||||
case "env":
|
||||
req.Reply(true, nil)
|
||||
|
||||
req, err := newEnvRequest(req)
|
||||
if err != nil {
|
||||
c.ui.Error(err.Error())
|
||||
req.Reply(false, nil)
|
||||
continue
|
||||
}
|
||||
env = append(env, req.Payload)
|
||||
case "exec":
|
||||
log.Printf("new env request: %s", req.Payload)
|
||||
req.Reply(true, nil)
|
||||
|
||||
case "exec":
|
||||
req, err := newExecRequest(req)
|
||||
if err != nil {
|
||||
c.ui.Error(err.Error())
|
||||
req.Reply(false, nil)
|
||||
close(done)
|
||||
continue
|
||||
}
|
||||
|
||||
if len(req.Payload) > 0 {
|
||||
cmd := &packer.RemoteCmd{
|
||||
Stdin: channel,
|
||||
Stdout: channel,
|
||||
Stderr: channel.Stderr(),
|
||||
Command: string(req.Payload),
|
||||
}
|
||||
log.Printf("new exec request: %s", req.Payload)
|
||||
|
||||
if err := c.comm.Start(cmd); err != nil {
|
||||
c.ui.Error(err.Error())
|
||||
close(done)
|
||||
return
|
||||
}
|
||||
go func(cmd *packer.RemoteCmd, channel ssh.Channel) {
|
||||
cmd.Wait()
|
||||
|
||||
exitStatus := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(exitStatus, uint32(cmd.ExitStatus))
|
||||
channel.SendRequest("exit-status", false, exitStatus)
|
||||
close(done)
|
||||
}(cmd, channel)
|
||||
if len(req.Payload) == 0 {
|
||||
req.Reply(false, nil)
|
||||
close(done)
|
||||
return
|
||||
}
|
||||
|
||||
cmd := &packer.RemoteCmd{
|
||||
Stdin: channel,
|
||||
Stdout: channel,
|
||||
Stderr: channel.Stderr(),
|
||||
Command: string(req.Payload),
|
||||
}
|
||||
|
||||
if err := c.comm.Start(cmd); err != nil {
|
||||
c.ui.Error(err.Error())
|
||||
req.Reply(false, nil)
|
||||
close(done)
|
||||
return
|
||||
}
|
||||
|
||||
go func(cmd *packer.RemoteCmd, channel ssh.Channel) {
|
||||
cmd.Wait()
|
||||
exitStatus := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(exitStatus, uint32(cmd.ExitStatus))
|
||||
channel.SendRequest("exit-status", false, exitStatus)
|
||||
close(done)
|
||||
}(cmd, channel)
|
||||
req.Reply(true, nil)
|
||||
|
||||
case "subsystem":
|
||||
req, err := newSubsystemRequest(req)
|
||||
if err != nil {
|
||||
c.ui.Error(err.Error())
|
||||
req.Reply(false, nil)
|
||||
continue
|
||||
}
|
||||
|
||||
log.Printf("new subsystem request: %s", req.Payload)
|
||||
switch req.Payload {
|
||||
case "sftp":
|
||||
c.ui.Say("starting sftp subsystem")
|
||||
req.Reply(true, nil)
|
||||
sftpCmd := c.sftpCmd
|
||||
if len(sftpCmd) == 0 {
|
||||
sftpCmd = "/usr/lib/sftp-server -e"
|
||||
|
@ -167,16 +177,22 @@ func (c *adapter) handleSession(newChannel ssh.NewChannel) error {
|
|||
Command: sftpCmd,
|
||||
}
|
||||
|
||||
c.ui.Say("starting sftp subsystem")
|
||||
if err := c.comm.Start(cmd); err != nil {
|
||||
c.ui.Error(err.Error())
|
||||
req.Reply(false, nil)
|
||||
close(done)
|
||||
return
|
||||
}
|
||||
|
||||
req.Reply(true, nil)
|
||||
go func() {
|
||||
cmd.Wait()
|
||||
close(done)
|
||||
}()
|
||||
|
||||
default:
|
||||
c.ui.Error(fmt.Sprintf("unsupported subsystem requested: %s", req.Payload))
|
||||
req.Reply(false, nil)
|
||||
|
||||
}
|
||||
|
@ -205,6 +221,10 @@ type envRequestPayload struct {
|
|||
Value string
|
||||
}
|
||||
|
||||
func (p envRequestPayload) String() string {
|
||||
return fmt.Sprintf("%s=%s", p.Name, p.Value)
|
||||
}
|
||||
|
||||
func newEnvRequest(raw *ssh.Request) (*envRequest, error) {
|
||||
r := new(envRequest)
|
||||
r.Request = raw
|
||||
|
@ -238,6 +258,10 @@ type execRequest struct {
|
|||
|
||||
type execRequestPayload string
|
||||
|
||||
func (p execRequestPayload) String() string {
|
||||
return string(p)
|
||||
}
|
||||
|
||||
func newExecRequest(raw *ssh.Request) (*execRequest, error) {
|
||||
r := new(execRequest)
|
||||
r.Request = raw
|
||||
|
@ -260,6 +284,10 @@ type subsystemRequest struct {
|
|||
|
||||
type subsystemRequestPayload string
|
||||
|
||||
func (p subsystemRequestPayload) String() string {
|
||||
return string(p)
|
||||
}
|
||||
|
||||
func newSubsystemRequest(raw *ssh.Request) (*subsystemRequest, error) {
|
||||
r := new(subsystemRequest)
|
||||
r.Request = raw
|
||||
|
|
|
@ -110,6 +110,10 @@ builder.
|
|||
launch the resulting AMI(s). By default no additional users other than the
|
||||
user creating the AMI has permissions to launch it.
|
||||
|
||||
- `ami_virtualization_type` (string) - The type of virtualization for the AMI
|
||||
you are building. This option must match the supported virtualization
|
||||
type of `source_ami`. Can be "paravirtual" or "hvm".
|
||||
|
||||
- `associate_public_ip_address` (boolean) - 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.
|
||||
|
|
|
@ -89,6 +89,7 @@ Packer to work:
|
|||
|
||||
``` {.javascript}
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [{
|
||||
"Effect": "Allow",
|
||||
"Action" : [
|
||||
|
@ -114,13 +115,15 @@ Packer to work:
|
|||
"ec2:DescribeSnapshots",
|
||||
"ec2:DescribeImages",
|
||||
"ec2:RegisterImage",
|
||||
"ec2:DeregisterImage",
|
||||
"ec2:CreateTags",
|
||||
"ec2:ModifyImageAttribute",
|
||||
"ec2:GetPasswordData",
|
||||
"ec2:DescribeTags",
|
||||
"ec2:DescribeImageAttribute",
|
||||
"ec2:CopyImage",
|
||||
"ec2:DescribeRegions"
|
||||
"ec2:DescribeRegions",
|
||||
"ec2:ModifyInstanceAttribute"
|
||||
],
|
||||
"Resource" : "*"
|
||||
}]
|
||||
|
|
|
@ -74,6 +74,22 @@ straightforwarded, it is documented here.
|
|||
4. Click "Generate new JSON key" for the Service Account you just created. A
|
||||
JSON file will be downloaded automatically. This is your *account file*.
|
||||
|
||||
### Precedence of Authentication Methods
|
||||
|
||||
Packer looks for credentials in the following places, preferring the first location found:
|
||||
|
||||
1. A `account_file` option in your packer file.
|
||||
|
||||
2. A JSON file (Service Account) whose path is specified by the `GOOGLE_APPLICATION_CREDENTIALS` environment variable.
|
||||
|
||||
3. A JSON file in a location known to the `gcloud` command-line tool. (`gcloud` creates it when it's configured)
|
||||
|
||||
On Windows, this is: `%APPDATA%/gcloud/application_default_credentials.json`.
|
||||
|
||||
On other systems: `$HOME/.config/gcloud/application_default_credentials.json`.
|
||||
|
||||
4. On Google Compute Engine and Google App Engine Managed VMs, it fetches credentials from the metadata server. (Needs a correct VM authentication scope configuration, see above)
|
||||
|
||||
## Basic Example
|
||||
|
||||
Below is a fully functioning example. It doesn't do anything useful, since no
|
||||
|
|
|
@ -262,6 +262,15 @@ builder and not otherwise conflicting with the qemuargs):
|
|||
qemu-system-x86 -m 1024m --no-acpi -netdev user,id=mynet0,hostfwd=hostip:hostport-guestip:guestport -device virtio-net,netdev=mynet0"
|
||||
</pre>
|
||||
|
||||
\~> **Windows Users:** [QEMU for Windows](https://qemu.weilnetz.de/) builds are available though an environmental variable does need
|
||||
to be set for QEMU for Windows to redirect stdout to the console instead of stdout.txt.
|
||||
|
||||
The following shows the environment variable that needs to be set for Windows QEMU support:
|
||||
|
||||
```json
|
||||
setx SDL_STDIO_REDIRECT=0
|
||||
```
|
||||
|
||||
You can also use the `SSHHostPort` template variable to produce a packer
|
||||
template that can be invoked by `make` in parallel:
|
||||
|
||||
|
|
|
@ -101,8 +101,44 @@ builder.
|
|||
for the VM. By default, this is 40000 (about 40 GB).
|
||||
|
||||
- `export_opts` (array of strings) - Additional options to pass to the
|
||||
`VBoxManage export`. This can be useful for passing product information to
|
||||
include in the resulting appliance file.
|
||||
[VBoxManage export](https://www.virtualbox.org/manual/ch08.html#vboxmanage-export).
|
||||
This can be useful for passing product information to include in the
|
||||
resulting appliance file. Packer JSON configuration file example:
|
||||
|
||||
``` {.json}
|
||||
{
|
||||
"type": "virtualbox-iso",
|
||||
"export_opts":
|
||||
[
|
||||
"--manifest",
|
||||
"--vsys", "0",
|
||||
"--description", "{{user `vm_description`}}",
|
||||
"--version", "{{user `vm_version`}}"
|
||||
],
|
||||
"format": "ova",
|
||||
}
|
||||
```
|
||||
|
||||
A VirtualBox [VM description](https://www.virtualbox.org/manual/ch08.html#idm3756)
|
||||
may contain arbitrary strings; the GUI interprets HTML formatting.
|
||||
However, the JSON format does not allow arbitrary newlines within a
|
||||
value. Add a multi-line description by preparing the string in the
|
||||
shell before the packer call like this (shell `>` continuation
|
||||
character snipped for easier copy & paste):
|
||||
|
||||
``` {.shell}
|
||||
|
||||
vm_description='some
|
||||
multiline
|
||||
description'
|
||||
|
||||
vm_version='0.2.0'
|
||||
|
||||
packer build \
|
||||
-var "vm_description=${vm_description}" \
|
||||
-var "vm_version=${vm_version}" \
|
||||
"packer_conf.json"
|
||||
```
|
||||
|
||||
- `floppy_files` (array of strings) - A list of files to place onto a floppy
|
||||
disk that is attached when the VM is booted. This is most useful for
|
||||
|
|
|
@ -83,8 +83,44 @@ builder.
|
|||
specified, the default is 10 seconds.
|
||||
|
||||
- `export_opts` (array of strings) - Additional options to pass to the
|
||||
`VBoxManage export`. This can be useful for passing product information to
|
||||
include in the resulting appliance file.
|
||||
[VBoxManage export](https://www.virtualbox.org/manual/ch08.html#vboxmanage-export).
|
||||
This can be useful for passing product information to include in the
|
||||
resulting appliance file. Packer JSON configuration file example:
|
||||
|
||||
``` {.json}
|
||||
{
|
||||
"type": "virtualbox-ovf",
|
||||
"export_opts":
|
||||
[
|
||||
"--manifest",
|
||||
"--vsys", "0",
|
||||
"--description", "{{user `vm_description`}}",
|
||||
"--version", "{{user `vm_version`}}"
|
||||
],
|
||||
"format": "ova",
|
||||
}
|
||||
```
|
||||
|
||||
A VirtualBox [VM description](https://www.virtualbox.org/manual/ch08.html#idm3756)
|
||||
may contain arbitrary strings; the GUI interprets HTML formatting.
|
||||
However, the JSON format does not allow arbitrary newlines within a
|
||||
value. Add a multi-line description by preparing the string in the
|
||||
shell before the packer call like this (shell `>` continuation
|
||||
character snipped for easier copy & paste):
|
||||
|
||||
``` {.shell}
|
||||
|
||||
vm_description='some
|
||||
multiline
|
||||
description'
|
||||
|
||||
vm_version='0.2.0'
|
||||
|
||||
packer build \
|
||||
-var "vm_description=${vm_description}" \
|
||||
-var "vm_version=${vm_version}" \
|
||||
"packer_conf.json"
|
||||
```
|
||||
|
||||
- `floppy_files` (array of strings) - A list of files to place onto a floppy
|
||||
disk that is attached when the VM is booted. This is most useful for
|
||||
|
|
|
@ -22,8 +22,13 @@ continuing. This will allow you to inspect state and so on.
|
|||
In debug mode once the remote instance is instantiated, Packer will emit to the
|
||||
current directory an ephemeral private ssh key as a .pem file. Using that you
|
||||
can `ssh -i <key.pem>` into the remote build instance and see what is going on
|
||||
for debugging. The ephemeral key will be deleted at the end of the packer run
|
||||
during cleanup.
|
||||
for debugging. The key will only be emitted for cloud-based builders. The
|
||||
ephemeral key will be deleted at the end of the packer run during cleanup.
|
||||
|
||||
For a local builder, the SSH session initiated will be visible in the detail
|
||||
provided when `PACKER_LOG=1` environment variable is set prior to a build,
|
||||
and you can connect to the local machine using the userid and password defined
|
||||
in the kickstart or preseed associated with initialzing the local VM.
|
||||
|
||||
### Windows
|
||||
|
||||
|
|
|
@ -107,6 +107,11 @@ chi-appservers
|
|||
your local system. These will be uploaded to the remote machine under
|
||||
`staging_directory`/playbooks. By default, this is empty.
|
||||
|
||||
- `galaxy_file` (string) - A requirements file which provides a way to install
|
||||
roles with the [ansible-galaxy
|
||||
cli](http://docs.ansible.com/ansible/galaxy.html#the-ansible-galaxy-command-line-tool)
|
||||
on the remote machine. By default, this is empty.
|
||||
|
||||
- `group_vars` (string) - a path to the directory containing ansible group
|
||||
variables on your local system to be copied to the remote machine. By
|
||||
default, this is empty.
|
||||
|
|
|
@ -68,7 +68,7 @@ listed below:
|
|||
- `puppet_server` (string) - Hostname of the Puppet server. By default
|
||||
"puppet" will be used.
|
||||
|
||||
- `staging_directory` (string) - This is the directory where all the
|
||||
- `staging_dir` (string) - This is the directory where all the
|
||||
configuration of Puppet by Packer will be placed. By default this
|
||||
is "/tmp/packer-puppet-server". This directory doesn't need to exist but
|
||||
must have proper permissions so that the SSH user that Packer uses is able
|
||||
|
|
|
@ -113,6 +113,9 @@ With a properly validated template. It is time to build your first image. This
|
|||
is done by calling `packer build` with the template file. The output should look
|
||||
similar to below. Note that this process typically takes a few minutes.
|
||||
|
||||
-> **Note:** When using packer on Windows, replace the single-quotes in the
|
||||
command below with double-quotes.
|
||||
|
||||
``` {.text}
|
||||
$ packer build \
|
||||
-var 'aws_access_key=YOUR ACCESS KEY' \
|
||||
|
|
Loading…
Reference in New Issue