packer-cn/provisioner/ansible/provisioner.go

974 lines
32 KiB
Go
Raw Normal View History

//go:generate mapstructure-to-hcl2 -type Config
//go:generate struct-markdown
2015-03-10 00:11:57 -04:00
package ansible
import (
"bufio"
"bytes"
"context"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
2015-03-10 00:11:57 -04:00
"errors"
"fmt"
"io"
"io/ioutil"
"log"
"net"
"os"
"os/exec"
"os/user"
2015-03-10 00:11:57 -04:00
"path/filepath"
"regexp"
2015-03-10 00:11:57 -04:00
"strconv"
"strings"
"sync"
"unicode"
2015-03-10 00:11:57 -04:00
"golang.org/x/crypto/ssh"
build using HCL2 (#8423) This follows #8232 which added the code to generate the code required to parse HCL files for each packer component. All old config files of packer will keep on working the same. Packer takes one argument. When a directory is passed, all files in the folder with a name ending with “.pkr.hcl” or “.pkr.json” will be parsed using the HCL2 format. When a file ending with “.pkr.hcl” or “.pkr.json” is passed it will be parsed using the HCL2 format. For every other case; the old packer style will be used. ## 1. the hcl2template pkg can create a packer.Build from a set of HCL (v2) files I had to make the packer.coreBuild (which is our one and only packer.Build ) a public struct with public fields ## 2. Components interfaces get a new ConfigSpec Method to read a file from an HCL file. This is a breaking change for packer plugins. a packer component can be a: builder/provisioner/post-processor each component interface now gets a `ConfigSpec() hcldec.ObjectSpec` which allows packer to tell what is the layout of the hcl2 config meant to configure that specific component. This ObjectSpec is sent through the wire (RPC) and a cty.Value is now sent through the already existing configuration entrypoints: Provisioner.Prepare(raws ...interface{}) error Builder.Prepare(raws ...interface{}) ([]string, error) PostProcessor.Configure(raws ...interface{}) error close #1768 Example hcl files: ```hcl // file amazon-ebs-kms-key/run.pkr.hcl build { sources = [ "source.amazon-ebs.first", ] provisioner "shell" { inline = [ "sleep 5" ] } post-processor "shell-local" { inline = [ "sleep 5" ] } } // amazon-ebs-kms-key/source.pkr.hcl source "amazon-ebs" "first" { ami_name = "hcl2-test" region = "us-east-1" instance_type = "t2.micro" kms_key_id = "c729958f-c6ba-44cd-ab39-35ab68ce0a6c" encrypt_boot = true source_ami_filter { filters { virtualization-type = "hvm" name = "amzn-ami-hvm-????.??.?.????????-x86_64-gp2" root-device-type = "ebs" } most_recent = true owners = ["amazon"] } launch_block_device_mappings { device_name = "/dev/xvda" volume_size = 20 volume_type = "gp2" delete_on_termination = "true" } launch_block_device_mappings { device_name = "/dev/xvdf" volume_size = 500 volume_type = "gp2" delete_on_termination = true encrypted = true } ami_regions = ["eu-central-1"] run_tags { Name = "packer-solr-something" stack-name = "DevOps Tools" } communicator = "ssh" ssh_pty = true ssh_username = "ec2-user" associate_public_ip_address = true } ```
2019-12-17 05:25:56 -05:00
"github.com/hashicorp/hcl/v2/hcldec"
2017-04-04 16:39:01 -04:00
"github.com/hashicorp/packer/common"
2019-02-12 02:10:57 -05:00
"github.com/hashicorp/packer/common/adapter"
2017-04-04 16:39:01 -04:00
"github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/packer"
Add tmp package that offers Dir & File funcs this regroups the calls of tmp func calls into one package. the tmp pkg allows to store tmp files under a common directory for easier managment Squashed commit of the following: commit 74b674c01560c6b547e9cab5e8afb486f0fe7d6c Merge: fc94b5714 66001525d Author: Matthew Patton <mpatton@enquizit.com> Date: Fri Nov 2 02:02:38 2018 -0400 Merge branch 'remove_TMPDIR_docs' of github.com:tb3088/packer into remove_TMPDIR_docs commit fc94b571428447aaf2e5171967c95cdf9e5c73ff Author: Matthew Patton <mpatton@enquizit.com> Date: Fri Nov 2 01:42:38 2018 -0400 update tests to honor configured temporary directory commit 5a562359f4937af506607dd96ed1c46426093f52 Author: Matthew Patton <mpatton@enquizit.com> Date: Thu Nov 1 18:52:38 2018 -0400 sync straggler to refactor. implement tests at packer/configfile instead of buried in docker. commit 9d5a4a0935bfd049e2f8f50ad73e4455b42c9410 Author: Matthew Patton <mpatton@enquizit.com> Date: Thu Nov 1 15:39:46 2018 -0400 insert URL to GoLang API for os.TempDir() commit fbae4c2e051898aecd8705e9d8a20353430ede23 Author: Matthew Patton <mpatton@enquizit.com> Date: Thu Nov 1 07:41:29 2018 -0400 revise documentation to match code commit 3590fae8bd0afa92a161a49ed794ee32e357d583 Author: Matthew Patton <mpatton@enquizit.com> Date: Thu Nov 1 07:21:59 2018 -0400 refacter config_file. replace all hard-coded os.TempDir with wrapper commit d5c5306a97b648ded06a8eb76ce728f07c9924f0 Author: Matthew Patton <mpatton@enquizit.com> Date: Wed Oct 31 20:11:36 2018 -0400 close massive file overwrite hole with TempDir commit 0a72297da86089fbd07e4f7d9472a92fa4d1c733 Author: Matthew Patton <mpatton@enquizit.com> Date: Wed Oct 31 01:06:00 2018 -0400 adjust var declaration scope commit 20f68228b6c372d984ea5b055cfc8bda9b110ac5 Author: Matthew Patton <mpatton@enquizit.com> Date: Wed Oct 31 00:54:35 2018 -0400 use mktemp() equivalent to create temporary directory commit c73ebe3d8321664a2685a0baa8e441dd895b0db4 Author: Matthew Patton <mpatton@enquizit.com> Date: Tue Oct 30 20:40:58 2018 -0400 remove extraneous variable declaration, fix FOR loop commit 63549b8bd745a0090b15ed0b0ebf644162db27db Author: Matthew Patton <mpatton@enquizit.com> Date: Tue Oct 30 19:30:44 2018 -0400 match styistic convension with rest of docs commit 976101074942db36f10d3a686d6d29ddb7c01926 Author: Matthew Patton <mpatton@enquizit.com> Date: Tue Oct 30 19:08:39 2018 -0400 revert dangling config file change commit 95159afbc05eac96ed11c3183af5fb0abe2f2d5c Author: Matthew Patton <matthew.patton@itgfirm.com> Date: Fri Dec 29 23:53:43 2017 -0500 replace invalid TMPDIR variable wth PACKER_TMP_DIR. update ConfigTmpDir() to try common temporary paths first and only write to configDir() as a last resort. commit 66001525d72de56a4cf9339b900c46f59bc6e91a Author: Matthew Patton <mpatton@enquizit.com> Date: Fri Nov 2 01:42:38 2018 -0400 update tests to honor configured temporary directory commit e9b6adefeae8c65eb8aa47fef38cbf0aa424338c Author: Matthew Patton <mpatton@enquizit.com> Date: Thu Nov 1 18:52:38 2018 -0400 sync straggler to refactor. implement tests at packer/configfile instead of buried in docker. commit 852113ed076e2d14e5dca6815ea680da1e2896bb Author: Matthew Patton <mpatton@enquizit.com> Date: Thu Nov 1 15:39:46 2018 -0400 insert URL to GoLang API for os.TempDir() commit 54add38d1d1c6e283cd444b367ed8bd49a5f3699 Author: Matthew Patton <mpatton@enquizit.com> Date: Thu Nov 1 07:41:29 2018 -0400 revise documentation to match code commit 6b5b8f6d4edad0e187ca13d64ac4118f34eee643 Author: Matthew Patton <mpatton@enquizit.com> Date: Thu Nov 1 07:21:59 2018 -0400 refacter config_file. replace all hard-coded os.TempDir with wrapper commit c22092c601c33484327674f322c7379fa41506d7 Author: Matthew Patton <mpatton@enquizit.com> Date: Wed Oct 31 20:11:36 2018 -0400 close massive file overwrite hole with TempDir commit 7a730450916bf8e5dbc2a741ec233a49466ab7cc Author: Matthew Patton <mpatton@enquizit.com> Date: Wed Oct 31 01:06:00 2018 -0400 adjust var declaration scope commit 0f2933adb6f6922dfeab78a95371a444ec8918ab Author: Matthew Patton <mpatton@enquizit.com> Date: Wed Oct 31 00:54:35 2018 -0400 use mktemp() equivalent to create temporary directory commit d74839ede05dacf712b55a7bb48aec19fe6b007f Author: Matthew Patton <mpatton@enquizit.com> Date: Tue Oct 30 20:40:58 2018 -0400 remove extraneous variable declaration, fix FOR loop commit eb65416619437e4a3dec90277770803dd5b2281c Author: Matthew Patton <mpatton@enquizit.com> Date: Tue Oct 30 19:30:44 2018 -0400 match styistic convension with rest of docs commit acaa2b31ed463219c4ef099f351eec72406e2989 Author: Matthew Patton <mpatton@enquizit.com> Date: Tue Oct 30 19:08:39 2018 -0400 revert dangling config file change commit e573fde668dcb418561e61535c1d68b2888f5b0f Author: Matthew Patton <matthew.patton@itgfirm.com> Date: Fri Dec 29 23:53:43 2017 -0500 replace invalid TMPDIR variable wth PACKER_TMP_DIR. update ConfigTmpDir() to try common temporary paths first and only write to configDir() as a last resort. commit 39a9874afc9dea71b36753375fb7feb61ffecf69 Merge: 8a413cfe8 3315812c2 Author: Matthew Patton <mpatton@enquizit.com> Date: Tue Oct 30 18:19:26 2018 -0400 Merge branch 'master' of https://github.com/hashicorp/packer into prestine commit 8a413cfe83b0a70dbd99c32d936334ca5788ca9b Merge: e07491de5 4e14710a6 Author: Matthew Patton <mpatton@enquizit.com> Date: Mon Oct 1 20:18:10 2018 -0400 Merge remote-tracking branch 'upstream/master' into prestine commit e07491de59cead0b337a7f57f4a6c625e1f560ab Merge: 42610a35d a1fa35dff Author: Matthew Patton <mpatton@enquizit.com> Date: Tue Aug 21 13:26:19 2018 -0400 Merge remote-tracking branch 'upstream/master' into prestine commit 42610a35d5ef65af8844a58ad70e2ec75262da6a Merge: 5298142da 0d63cf7bc Author: Matthew Patton <matthew.patton@itgfirm.com> Date: Tue Jun 19 22:45:05 2018 -0400 Merge remote-tracking branch 'upstream/master' into prestine commit 5298142da6da95354844f201deeeef3c599e48b2 Merge: 7bb110bc7 9d9736552 Author: Matthew Patton <mpatton@enquizit.com> Date: Mon Jun 11 15:10:09 2018 -0400 Merge remote-tracking branch 'upstream/master' into prestine commit 7bb110bc744c9797fcaec25a265d9b85a22d6d6b Merge: a61c869ca 6189d66e7 Author: Matthew Patton <matthew.patton@itgfirm.com> Date: Wed May 9 23:41:22 2018 -0400 Merge remote-tracking branch 'upstream/master' into prestine commit a61c869ca7268acf0e3b1e1fa7a8ee7feea65984 Merge: 098101dd9 4be5f072c Author: Matthew Patton <matthew.patton@itgfirm.com> Date: Fri May 4 10:13:04 2018 -0400 Merge remote-tracking branch 'upstream/master' into prestine commit 098101dd99f08b0ca110d33eff6904537c6d21de Merge: fefaf0fa6 554b2b4a5 Author: Matthew Patton <matthew.patton@itgfirm.com> Date: Tue May 1 20:52:30 2018 -0400 Merge branch 'EOL-handling' into prestine Co-Authored-By: Matthew Patton <pattonme@yahoo.com>
2018-12-12 09:45:00 -05:00
"github.com/hashicorp/packer/packer/tmp"
2017-04-04 16:39:01 -04:00
"github.com/hashicorp/packer/template/interpolate"
2015-03-10 00:11:57 -04:00
)
type Config struct {
common.PackerConfig `mapstructure:",squash"`
ctx interpolate.Context
// The command to invoke ansible. Defaults to
// `ansible-playbook`. If you would like to provide a more complex command,
// for example, something that sets up a virtual environment before calling
// ansible, take a look at the ansible wrapper guide below for inspiration.
// Please note that Packer expects Command to be a path to an executable.
// Arbitrary bash scripting will not work and needs to go inside an
// executable script.
Command string `mapstructure:"command"`
// Extra arguments to pass to Ansible.
// These arguments _will not_ be passed through a shell and arguments should
// not be quoted. Usage example:
//
// ```json
// "extra_arguments": [ "--extra-vars", "Region={{user `Region`}} Stage={{user `Stage`}}" ]
// ```
//
// If you are running a Windows build on AWS, Azure, Google Compute, or OpenStack
// and would like to access the auto-generated password that Packer uses to
// connect to a Windows instance via WinRM, you can use the template variable
// `{{.WinRMPassword}}` in this option. For example:
//
// ```json
// "extra_arguments": [
// "--extra-vars", "winrm_password={{ .WinRMPassword }}"
// ]
// ```
2015-03-10 00:11:57 -04:00
ExtraArguments []string `mapstructure:"extra_arguments"`
// Environment variables to set before
// running Ansible. Usage example:
//
// ```json
// "ansible_env_vars": [ "ANSIBLE_HOST_KEY_CHECKING=False", "ANSIBLE_SSH_ARGS='-o ForwardAgent=yes -o ControlMaster=auto -o ControlPersist=60s'", "ANSIBLE_NOCOLOR=True" ]
// ```
//
// This is a [template engine](/docs/templates/engine). Therefore, you
// may use user variables and template functions in this field.
//
// For example, if you are running a Windows build on AWS, Azure,
// Google Compute, or OpenStack and would like to access the auto-generated
// password that Packer uses to connect to a Windows instance via WinRM, you
// can use the template variable `{{.WinRMPassword}}` in this option. Example:
//
// ```json
// "ansible_env_vars": [ "WINRM_PASSWORD={{.WinRMPassword}}" ],
// ```
AnsibleEnvVars []string `mapstructure:"ansible_env_vars"`
// The playbook to be run by Ansible.
PlaybookFile string `mapstructure:"playbook_file" required:"true"`
2020-08-25 09:53:41 -04:00
// Specifies --ssh-extra-args on command line defaults to -o IdentitiesOnly=yes
AnsibleSSHExtraArgs []string `mapstructure:"ansible_ssh_extra_args"`
// The groups into which the Ansible host should
// be placed. When unspecified, the host is not associated with any groups.
Groups []string `mapstructure:"groups"`
// The groups which should be present in
// inventory file but remain empty.
EmptyGroups []string `mapstructure:"empty_groups"`
// The alias by which the Ansible host should be
// known. Defaults to `default`. This setting is ignored when using a custom
// inventory file.
HostAlias string `mapstructure:"host_alias"`
// The `ansible_user` to use. Defaults to the user running
// packer, NOT the user set for your communicator. If you want to use the same
// user as the communicator, you will need to manually set it again in this
// field.
User string `mapstructure:"user"`
// The port on which to attempt to listen for SSH
// connections. This value is a starting point. The provisioner will attempt
// listen for SSH connections on the first available of ten ports, starting at
// `local_port`. A system-chosen port is used when `local_port` is missing or
// empty.
LocalPort int `mapstructure:"local_port"`
// The SSH key that will be used to run the SSH
// server on the host machine to forward commands to the target machine.
// Ansible connects to this server and will validate the identity of the
// server using the system known_hosts. The default behavior is to generate
// and use a onetime key. Host key checking is disabled via the
// `ANSIBLE_HOST_KEY_CHECKING` environment variable if the key is generated.
SSHHostKeyFile string `mapstructure:"ssh_host_key_file"`
// The SSH public key of the Ansible
// `ssh_user`. The default behavior is to generate and use a onetime key. If
// this key is generated, the corresponding private key is passed to
// `ansible-playbook` with the `-e ansible_ssh_private_key_file` option.
SSHAuthorizedKeyFile string `mapstructure:"ssh_authorized_key_file"`
// The command to run on the machine being
// provisioned by Packer to handle the SFTP protocol that Ansible will use to
// transfer files. The command should read and write on stdin and stdout,
// respectively. Defaults to `/usr/lib/sftp-server -e`.
SFTPCmd string `mapstructure:"sftp_command"`
// Check if ansible is installed prior to
// running. Set this to `true`, for example, if you're going to install
// ansible during the packer run.
SkipVersionCheck bool `mapstructure:"skip_version_check"`
UseSFTP bool `mapstructure:"use_sftp"`
// The directory in which to place the
// temporary generated Ansible inventory file. By default, this is the
// system-specific temporary file location. The fully-qualified name of this
// temporary file will be passed to the `-i` argument of the `ansible` command
// when this provisioner runs ansible. Specify this if you have an existing
// inventory directory with `host_vars` `group_vars` that you would like to
// use in the playbook that this provisioner will run.
InventoryDirectory string `mapstructure:"inventory_directory"`
// This template represents the format for the lines added to the temporary
// inventory file that Packer will create to run Ansible against your image.
// The default for recent versions of Ansible is:
// "{{ .HostAlias }} ansible_host={{ .Host }} ansible_user={{ .User }} ansible_port={{ .Port }}\n"
// Available template engines are: This option is a template engine;
// variables available to you include the examples in the default (Host,
// HostAlias, User, Port) as well as any variables available to you via the
// "build" template engine.
InventoryFileTemplate string `mapstructure:"inventory_file_template"`
// The inventory file to use during provisioning.
// When unspecified, Packer will create a temporary inventory file and will
// use the `host_alias`.
InventoryFile string `mapstructure:"inventory_file"`
// If `true`, the Ansible provisioner will
// not delete the temporary inventory file it creates in order to connect to
// the instance. This is useful if you are trying to debug your ansible run
// and using "--on-error=ask" in order to leave your instance running while you
// test your playbook. this option is not used if you set an `inventory_file`.
KeepInventoryFile bool `mapstructure:"keep_inventory_file"`
// A requirements file which provides a way to
// install roles or collections with the [ansible-galaxy
// cli](https://docs.ansible.com/ansible/latest/galaxy/user_guide.html#the-ansible-galaxy-command-line-tool)
// on the local machine before executing `ansible-playbook`. By default, this is empty.
GalaxyFile string `mapstructure:"galaxy_file"`
// The command to invoke ansible-galaxy. By default, this is
// `ansible-galaxy`.
GalaxyCommand string `mapstructure:"galaxy_command"`
// Force overwriting an existing role.
// Adds `--force` option to `ansible-galaxy` command. By default, this is
// `false`.
GalaxyForceInstall bool `mapstructure:"galaxy_force_install"`
// The path to the directory on your local system in which to
// install the roles. Adds `--roles-path /path/to/your/roles` to
// `ansible-galaxy` command. By default, this is empty, and thus `--roles-path`
// option is not added to the command.
RolesPath string `mapstructure:"roles_path"`
// The path to the directory on your local system in which to
// install the collections. Adds `--collections-path /path/to/your/collections` to
// `ansible-galaxy` command. By default, this is empty, and thus `--collections-path`
// option is not added to the command.
CollectionsPath string `mapstructure:"collections_path"`
// When `true`, set up a localhost proxy adapter
// so that Ansible has an IP address to connect to, even if your guest does not
// have an IP address. For example, the adapter is necessary for Docker builds
// to use the Ansible provisioner. If you set this option to `false`, but
// Packer cannot find an IP address to connect Ansible to, it will
// automatically set up the adapter anyway.
//
// In order for Ansible to connect properly even when use_proxy is false, you
// need to make sure that you are either providing a valid username and ssh key
// to the ansible provisioner directly, or that the username and ssh key
// being used by the ssh communicator will work for your needs. If you do not
// provide a user to ansible, it will use the user associated with your
// builder, not the user running Packer.
// use_proxy=false is currently only supported for SSH and WinRM.
//
// Currently, this defaults to `true` for all connection types. In the future,
// this option will be changed to default to `false` for SSH and WinRM
// connections where the provisioner has access to a host IP.
2020-02-04 13:42:55 -05:00
UseProxy config.Trilean `mapstructure:"use_proxy"`
userWasEmpty bool
2015-03-10 00:11:57 -04:00
}
type Provisioner struct {
config Config
2019-02-12 02:10:57 -05:00
adapter *adapter.Adapter
done chan struct{}
ansibleVersion string
ansibleMajVersion uint
generatedData map[string]interface{}
2020-02-04 13:42:55 -05:00
setupAdapterFunc func(ui packer.Ui, comm packer.Communicator) (string, error)
executeAnsibleFunc func(ui packer.Ui, comm packer.Communicator, privKeyFile string) error
}
build using HCL2 (#8423) This follows #8232 which added the code to generate the code required to parse HCL files for each packer component. All old config files of packer will keep on working the same. Packer takes one argument. When a directory is passed, all files in the folder with a name ending with “.pkr.hcl” or “.pkr.json” will be parsed using the HCL2 format. When a file ending with “.pkr.hcl” or “.pkr.json” is passed it will be parsed using the HCL2 format. For every other case; the old packer style will be used. ## 1. the hcl2template pkg can create a packer.Build from a set of HCL (v2) files I had to make the packer.coreBuild (which is our one and only packer.Build ) a public struct with public fields ## 2. Components interfaces get a new ConfigSpec Method to read a file from an HCL file. This is a breaking change for packer plugins. a packer component can be a: builder/provisioner/post-processor each component interface now gets a `ConfigSpec() hcldec.ObjectSpec` which allows packer to tell what is the layout of the hcl2 config meant to configure that specific component. This ObjectSpec is sent through the wire (RPC) and a cty.Value is now sent through the already existing configuration entrypoints: Provisioner.Prepare(raws ...interface{}) error Builder.Prepare(raws ...interface{}) ([]string, error) PostProcessor.Configure(raws ...interface{}) error close #1768 Example hcl files: ```hcl // file amazon-ebs-kms-key/run.pkr.hcl build { sources = [ "source.amazon-ebs.first", ] provisioner "shell" { inline = [ "sleep 5" ] } post-processor "shell-local" { inline = [ "sleep 5" ] } } // amazon-ebs-kms-key/source.pkr.hcl source "amazon-ebs" "first" { ami_name = "hcl2-test" region = "us-east-1" instance_type = "t2.micro" kms_key_id = "c729958f-c6ba-44cd-ab39-35ab68ce0a6c" encrypt_boot = true source_ami_filter { filters { virtualization-type = "hvm" name = "amzn-ami-hvm-????.??.?.????????-x86_64-gp2" root-device-type = "ebs" } most_recent = true owners = ["amazon"] } launch_block_device_mappings { device_name = "/dev/xvda" volume_size = 20 volume_type = "gp2" delete_on_termination = "true" } launch_block_device_mappings { device_name = "/dev/xvdf" volume_size = 500 volume_type = "gp2" delete_on_termination = true encrypted = true } ami_regions = ["eu-central-1"] run_tags { Name = "packer-solr-something" stack-name = "DevOps Tools" } communicator = "ssh" ssh_pty = true ssh_username = "ec2-user" associate_public_ip_address = true } ```
2019-12-17 05:25:56 -05:00
func (p *Provisioner) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() }
2015-03-10 00:11:57 -04:00
func (p *Provisioner) Prepare(raws ...interface{}) error {
p.done = make(chan struct{})
err := config.Decode(&p.config, &config.DecodeOpts{
Interpolate: true,
InterpolateContext: &p.config.ctx,
InterpolateFilter: &interpolate.RenderFilter{
Exclude: []string{
"inventory_file_template",
},
2015-03-10 00:11:57 -04:00
},
}, raws...)
if err != nil {
return err
}
// Defaults
if p.config.Command == "" {
p.config.Command = "ansible-playbook"
}
if p.config.GalaxyCommand == "" {
p.config.GalaxyCommand = "ansible-galaxy"
}
if p.config.HostAlias == "" {
p.config.HostAlias = "default"
}
2015-03-10 00:11:57 -04:00
var errs *packer.MultiError
err = validateFileConfig(p.config.PlaybookFile, "playbook_file", true)
if err != nil {
errs = packer.MultiErrorAppend(errs, err)
}
// 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 authorized key file exists
if len(p.config.SSHAuthorizedKeyFile) > 0 {
err = validateFileConfig(p.config.SSHAuthorizedKeyFile, "ssh_authorized_key_file", true)
if err != nil {
log.Println(p.config.SSHAuthorizedKeyFile, "does not exist")
errs = packer.MultiErrorAppend(errs, err)
}
2015-03-10 00:11:57 -04:00
}
if len(p.config.SSHHostKeyFile) > 0 {
err = validateFileConfig(p.config.SSHHostKeyFile, "ssh_host_key_file", true)
if err != nil {
log.Println(p.config.SSHHostKeyFile, "does not exist")
errs = packer.MultiErrorAppend(errs, err)
}
} else {
p.config.AnsibleEnvVars = append(p.config.AnsibleEnvVars, "ANSIBLE_HOST_KEY_CHECKING=False")
}
if !p.config.UseSFTP {
p.config.AnsibleEnvVars = append(p.config.AnsibleEnvVars, "ANSIBLE_SCP_IF_SSH=True")
2015-03-10 00:11:57 -04:00
}
if p.config.LocalPort > 65535 {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("local_port: %d must be a valid port", p.config.LocalPort))
2015-03-10 00:11:57 -04:00
}
if len(p.config.InventoryDirectory) > 0 {
err = validateInventoryDirectoryConfig(p.config.InventoryDirectory)
if err != nil {
log.Println(p.config.InventoryDirectory, "does not exist")
errs = packer.MultiErrorAppend(errs, err)
}
}
2017-06-07 14:12:07 -04:00
if !p.config.SkipVersionCheck {
err = p.getVersion()
if err != nil {
errs = packer.MultiErrorAppend(errs, err)
}
}
if p.config.User == "" {
2020-02-04 13:42:55 -05:00
p.config.userWasEmpty = true
usr, err := user.Current()
if err != nil {
errs = packer.MultiErrorAppend(errs, err)
} else {
p.config.User = usr.Username
}
2016-02-26 16:16:22 -05:00
}
if p.config.User == "" {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("user: could not determine current user from environment."))
}
2020-02-04 13:42:55 -05:00
// These fields exist so that we can replace the functions for testing
// logic inside of the Provision func; in actual use, these don't ever
// need to get set.
if p.setupAdapterFunc == nil {
p.setupAdapterFunc = p.setupAdapter
}
if p.executeAnsibleFunc == nil {
p.executeAnsibleFunc = p.executeAnsible
}
2015-03-10 00:11:57 -04:00
if errs != nil && len(errs.Errors) > 0 {
return errs
}
return nil
}
func (p *Provisioner) getVersion() error {
out, err := exec.Command(p.config.Command, "--version").Output()
if err != nil {
return fmt.Errorf(
"Error running \"%s --version\": %s", p.config.Command, err.Error())
}
versionRe := regexp.MustCompile(`\w (\d+\.\d+[.\d+]*)`)
matches := versionRe.FindStringSubmatch(string(out))
if matches == nil {
return fmt.Errorf(
"Could not find %s version in output:\n%s", p.config.Command, string(out))
}
version := matches[1]
log.Printf("%s version: %s", p.config.Command, version)
p.ansibleVersion = version
majVer, err := strconv.ParseUint(strings.Split(version, ".")[0], 10, 0)
if err != nil {
return fmt.Errorf("Could not parse major version from \"%s\".", version)
}
p.ansibleMajVersion = uint(majVer)
return nil
}
2020-02-04 13:42:55 -05:00
func (p *Provisioner) setupAdapter(ui packer.Ui, comm packer.Communicator) (string, error) {
ui.Message("Setting up proxy adapter for Ansible....")
k, err := newUserKey(p.config.SSHAuthorizedKeyFile)
2015-03-10 00:11:57 -04:00
if err != nil {
2020-02-04 13:42:55 -05:00
return "", err
}
hostSigner, err := newSigner(p.config.SSHHostKeyFile)
2019-08-27 19:52:52 -04:00
if err != nil {
2020-02-04 13:42:55 -05:00
return "", fmt.Errorf("error creating host signer: %s", err)
2015-03-10 00:11:57 -04:00
}
keyChecker := ssh.CertChecker{
UserKeyFallback: func(conn ssh.ConnMetadata, pubKey ssh.PublicKey) (*ssh.Permissions, error) {
if user := conn.User(); user != p.config.User {
return nil, errors.New(fmt.Sprintf("authentication failed: %s is not a valid user", user))
2015-03-10 00:11:57 -04:00
}
if !bytes.Equal(k.Marshal(), pubKey.Marshal()) {
return nil, errors.New("authentication failed: unauthorized key")
2015-03-10 00:11:57 -04:00
}
return nil, nil
},
IsUserAuthority: func(k ssh.PublicKey) bool { return true },
2015-03-10 00:11:57 -04:00
}
2016-02-08 20:34:06 -05:00
2015-03-10 00:11:57 -04:00
config := &ssh.ServerConfig{
AuthLogCallback: func(conn ssh.ConnMetadata, method string, err error) {
log.Printf("authentication attempt from %s to %s as %s using %s", conn.RemoteAddr(), conn.LocalAddr(), conn.User(), method)
2015-03-10 00:11:57 -04:00
},
PublicKeyCallback: keyChecker.Authenticate,
//NoClientAuth: true,
}
config.AddHostKey(hostSigner)
2015-03-10 00:11:57 -04:00
localListener, err := func() (net.Listener, error) {
port := p.config.LocalPort
tries := 1
if port != 0 {
tries = 10
}
for i := 0; i < tries; i++ {
2015-03-10 00:11:57 -04:00
l, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", port))
port++
if err != nil {
ui.Say(err.Error())
continue
2015-03-10 00:11:57 -04:00
}
_, portStr, err := net.SplitHostPort(l.Addr().String())
if err != nil {
ui.Say(err.Error())
continue
}
2019-03-19 09:47:21 -04:00
p.config.LocalPort, err = strconv.Atoi(portStr)
if err != nil {
ui.Say(err.Error())
continue
}
return l, nil
2015-03-10 00:11:57 -04:00
}
return nil, errors.New("Error setting up SSH proxy connection")
}()
if err != nil {
2020-02-04 13:42:55 -05:00
return "", err
2015-03-10 00:11:57 -04:00
}
ui = &packer.SafeUi{
Sem: make(chan int, 1),
Ui: ui,
}
2019-02-12 02:10:57 -05:00
p.adapter = adapter.NewAdapter(p.done, localListener, config, p.config.SFTPCmd, ui, comm)
2015-03-10 00:11:57 -04:00
2020-02-04 13:42:55 -05:00
return k.privKeyFile, nil
}
2020-03-27 19:44:41 -04:00
const DefaultSSHInventoryFilev2 = "{{ .HostAlias }} ansible_host={{ .Host }} ansible_user={{ .User }} ansible_port={{ .Port }}\n"
const DefaultSSHInventoryFilev1 = "{{ .HostAlias }} ansible_ssh_host={{ .Host }} ansible_ssh_user={{ .User }} ansible_ssh_port={{ .Port }}\n"
const DefaultWinRMInventoryFilev2 = "{{ .HostAlias}} ansible_host={{ .Host }} ansible_connection=winrm ansible_winrm_transport=basic ansible_shell_type=powershell ansible_user={{ .User}} ansible_port={{ .Port }}\n"
func (p *Provisioner) createInventoryFile() error {
2020-02-04 13:42:55 -05:00
log.Printf("Creating inventory file for Ansible run...")
tf, err := ioutil.TempFile(p.config.InventoryDirectory, "packer-provisioner-ansible")
if err != nil {
return fmt.Errorf("Error preparing inventory file: %s", err)
}
// If user has defiend their own inventory template, use it.
hostTemplate := p.config.InventoryFileTemplate
if hostTemplate == "" {
// figure out which inventory line template to use
hostTemplate = DefaultSSHInventoryFilev2
if p.ansibleMajVersion < 2 {
hostTemplate = DefaultSSHInventoryFilev1
}
if p.config.UseProxy.False() && p.generatedData["ConnType"] == "winrm" {
hostTemplate = DefaultWinRMInventoryFilev2
}
}
// interpolate template to generate host with necessary vars.
ctxData := p.generatedData
ctxData["HostAlias"] = p.config.HostAlias
ctxData["User"] = p.config.User
if !p.config.UseProxy.False() {
ctxData["Host"] = "127.0.0.1"
ctxData["Port"] = p.config.LocalPort
}
p.config.ctx.Data = ctxData
host, err := interpolate.Render(hostTemplate, &p.config.ctx)
if err != nil {
return fmt.Errorf("Error generating inventory file from template: %s", err)
2020-02-04 13:42:55 -05:00
}
2015-03-10 00:11:57 -04:00
2020-02-04 13:42:55 -05:00
w := bufio.NewWriter(tf)
w.WriteString(host)
2015-03-10 00:11:57 -04:00
2020-02-04 13:42:55 -05:00
for _, group := range p.config.Groups {
fmt.Fprintf(w, "[%s]\n%s", group, host)
}
for _, group := range p.config.EmptyGroups {
fmt.Fprintf(w, "[%s]\n", group)
}
if err := w.Flush(); err != nil {
tf.Close()
os.Remove(tf.Name())
return fmt.Errorf("Error preparing inventory file: %s", err)
}
tf.Close()
p.config.InventoryFile = tf.Name()
return nil
}
func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, generatedData map[string]interface{}) error {
ui.Say("Provisioning with Ansible...")
// Interpolate env vars to check for generated values like password and port
p.generatedData = generatedData
p.config.ctx.Data = generatedData
for i, envVar := range p.config.AnsibleEnvVars {
envVar, err := interpolate.Render(envVar, &p.config.ctx)
if err != nil {
return fmt.Errorf("Could not interpolate ansible env vars: %s", err)
}
p.config.AnsibleEnvVars[i] = envVar
}
// Interpolate extra vars to check for generated values like password and port
for i, arg := range p.config.ExtraArguments {
arg, err := interpolate.Render(arg, &p.config.ctx)
2015-03-10 00:11:57 -04:00
if err != nil {
2020-02-04 13:42:55 -05:00
return fmt.Errorf("Could not interpolate ansible env vars: %s", err)
2015-03-10 00:11:57 -04:00
}
2020-02-04 13:42:55 -05:00
p.config.ExtraArguments[i] = arg
}
// Set up proxy if host IP is missing or communicator type is wrong.
if p.config.UseProxy.False() {
hostIP := generatedData["Host"].(string)
if hostIP == "" {
ui.Error("Warning: use_proxy is false, but instance does" +
" not have an IP address to give to Ansible. Falling back" +
" to use localhost proxy.")
p.config.UseProxy = config.TriTrue
}
connType := generatedData["ConnType"]
if connType != "ssh" && connType != "winrm" {
ui.Error("Warning: use_proxy is false, but communicator is " +
"neither ssh nor winrm, so without the proxy ansible will not" +
" function. Falling back to localhost proxy.")
p.config.UseProxy = config.TriTrue
}
2020-02-04 13:42:55 -05:00
}
2020-02-04 13:42:55 -05:00
privKeyFile := ""
if !p.config.UseProxy.False() {
// We set up the proxy if useProxy is either true or unset.
pkf, err := p.setupAdapterFunc(ui, comm)
if err != nil {
return err
}
2020-02-04 13:42:55 -05:00
// This is necessary to avoid accidentally redeclaring
// privKeyFile in the scope of this if statement.
privKeyFile = pkf
defer func() {
log.Print("shutting down the SSH proxy")
close(p.done)
p.adapter.Shutdown()
}()
go p.adapter.Serve()
2020-02-04 13:42:55 -05:00
// Remove the private key file
if len(privKeyFile) > 0 {
defer os.Remove(privKeyFile)
}
2020-02-04 13:42:55 -05:00
} else {
connType := generatedData["ConnType"].(string)
switch connType {
case "ssh":
ui.Message("Not using Proxy adapter for Ansible run:\n" +
"\tUsing ssh keys from Packer communicator...")
// In this situation, we need to make sure we have the
// private key we actually use to access the instance.
SSHPrivateKeyFile := generatedData["SSHPrivateKeyFile"].(string)
SSHAgentAuth := generatedData["SSHAgentAuth"].(bool)
if SSHPrivateKeyFile != "" || SSHAgentAuth {
privKeyFile = SSHPrivateKeyFile
} else {
// See if we can get a private key and write that to a tmpfile
2020-03-27 19:44:41 -04:00
SSHPrivateKey := generatedData["SSHPrivateKey"].(string)
tmpSSHPrivateKey, err := tmp.File("ansible-key")
if err != nil {
return fmt.Errorf("Error writing private key to temp file for"+
"ansible connection: %v", err)
}
2020-03-27 19:44:41 -04:00
_, err = tmpSSHPrivateKey.WriteString(SSHPrivateKey)
if err != nil {
return errors.New("failed to write private key to temp file")
}
err = tmpSSHPrivateKey.Close()
if err != nil {
return errors.New("failed to close private key temp file")
}
privKeyFile = tmpSSHPrivateKey.Name()
2020-02-04 13:42:55 -05:00
}
// Also make sure that the username matches the SSH keys given.
if p.config.userWasEmpty {
p.config.User = generatedData["User"].(string)
}
case "winrm":
ui.Message("Not using Proxy adapter for Ansible run:\n" +
"\tUsing WinRM Password from Packer communicator...")
2020-02-04 13:42:55 -05:00
}
}
2020-02-04 13:42:55 -05:00
if len(p.config.InventoryFile) == 0 {
// Create the inventory file
err := p.createInventoryFile()
2020-02-04 13:42:55 -05:00
if err != nil {
return err
2015-03-10 00:11:57 -04:00
}
if !p.config.KeepInventoryFile {
// Delete the generated inventory file
defer func() {
os.Remove(p.config.InventoryFile)
p.config.InventoryFile = ""
}()
}
2015-03-10 00:11:57 -04:00
}
2020-02-04 13:42:55 -05:00
if err := p.executeAnsibleFunc(ui, comm, privKeyFile); err != nil {
2015-03-10 00:11:57 -04:00
return fmt.Errorf("Error executing Ansible: %s", err)
}
return nil
}
func (p *Provisioner) executeGalaxy(ui packer.Ui, comm packer.Communicator) error {
galaxyFile := filepath.ToSlash(p.config.GalaxyFile)
2019-07-27 07:56:14 -04:00
// ansible-galaxy install -r requirements.yml
roleArgs := []string{"install", "-r", galaxyFile}
// Instead of modifying args depending on config values and removing or modifying values from
// the slice between role and collection installs, just use 2 slices and simplify everything
collectionArgs := []string{"collection", "install", "-r", galaxyFile}
// Add force to arguments
if p.config.GalaxyForceInstall {
roleArgs = append(roleArgs, "-f")
collectionArgs = append(collectionArgs, "-f")
}
2019-07-27 07:56:14 -04:00
// Add roles_path argument if specified
if p.config.RolesPath != "" {
roleArgs = append(roleArgs, "-p", filepath.ToSlash(p.config.RolesPath))
}
// Add collections_path argument if specified
if p.config.CollectionsPath != "" {
collectionArgs = append(collectionArgs, "-p", filepath.ToSlash(p.config.CollectionsPath))
2019-07-27 07:56:14 -04:00
}
roleInstallError := p.invokeGalaxyCommand(roleArgs, ui, comm)
// Return the error if the role installation failed before attempting the collection install
if roleInstallError != nil {
return roleInstallError
}
2020-09-05 16:19:05 -04:00
// If all is well, proceed with collection install
// This variable isn't strictly necessary but including for readability to match the role installation
collectionInstallError := p.invokeGalaxyCommand(collectionArgs, ui, comm)
return collectionInstallError
}
// Intended to be invoked from p.executeGalaxy depending on the Ansible Galaxy parameters passed to Packer
func (p *Provisioner) invokeGalaxyCommand(args []string, ui packer.Ui, comm packer.Communicator) error {
ui.Message(fmt.Sprintf("Executing Ansible Galaxy"))
cmd := exec.Command(p.config.GalaxyCommand, args...)
stdout, err := cmd.StdoutPipe()
if err != nil {
return err
}
stderr, err := cmd.StderrPipe()
if err != nil {
return err
}
wg := sync.WaitGroup{}
repeat := func(r io.ReadCloser) {
reader := bufio.NewReader(r)
for {
line, err := reader.ReadString('\n')
if line != "" {
line = strings.TrimRightFunc(line, unicode.IsSpace)
ui.Message(line)
}
if err != nil {
if err == io.EOF {
break
} else {
ui.Error(err.Error())
break
}
}
}
wg.Done()
}
wg.Add(2)
go repeat(stdout)
go repeat(stderr)
if err := cmd.Start(); err != nil {
return err
}
wg.Wait()
err = cmd.Wait()
if err != nil {
return fmt.Errorf("Non-zero exit status: %s", err)
}
return nil
}
func (p *Provisioner) createCmdArgs(httpAddr, inventory, playbook, privKeyFile string) (args []string, envVars []string) {
args = []string{}
2015-03-10 00:11:57 -04:00
2020-06-02 18:30:03 -04:00
//Setting up AnsibleEnvVars at begining so additional checks can take them into account
if len(p.config.AnsibleEnvVars) > 0 {
envVars = append(envVars, p.config.AnsibleEnvVars...)
}
if p.config.PackerBuildName != "" {
// HCL configs don't currently have the PakcerBuildName. Don't
// cause weirdness with a half-set variable
args = append(args, "-e", fmt.Sprintf("packer_build_name=%q", p.config.PackerBuildName))
}
args = append(args, "-e", fmt.Sprintf("packer_builder_type=%s", p.config.PackerBuilderType))
// expose packer_http_addr extra variable
if httpAddr != common.HttpAddrNotImplemented {
args = append(args, "-e", fmt.Sprintf("packer_http_addr=%s", httpAddr))
}
if p.generatedData["ConnType"] == "ssh" && len(privKeyFile) > 0 {
2020-03-31 16:13:01 -04:00
// Add ssh extra args to set IdentitiesOnly
2020-08-25 09:53:41 -04:00
if len(p.config.AnsibleSSHExtraArgs) > 0 {
var sshExtraArgs string
for _, sshExtraArg := range p.config.AnsibleSSHExtraArgs {
sshExtraArgs = sshExtraArgs + sshExtraArg
}
args = append(args, "--ssh-extra-args", fmt.Sprintf("'%s'", sshExtraArgs))
} else {
args = append(args, "--ssh-extra-args", "'-o IdentitiesOnly=yes'")
}
2020-03-31 16:13:01 -04:00
}
2015-03-10 00:11:57 -04:00
args = append(args, p.config.ExtraArguments...)
2020-05-21 22:15:13 -04:00
2020-06-02 18:30:03 -04:00
// Add password to ansible call.
if !checkArg("ansible_password", args) && p.config.UseProxy.False() && p.generatedData["ConnType"] == "winrm" {
args = append(args, "-e", fmt.Sprintf("ansible_password=%s", p.generatedData["Password"]))
}
2016-02-08 20:34:06 -05:00
2020-06-02 18:30:03 -04:00
if !checkArg("ansible_password", args) && len(privKeyFile) > 0 {
// "-e ansible_ssh_private_key_file" is preferable to "--private-key"
// because it is a higher priority variable and therefore won't get
// overridden by dynamic variables. See #5852 for more details.
args = append(args, "-e", fmt.Sprintf("ansible_ssh_private_key_file=%s", privKeyFile))
}
if checkArg("ansible_password", args) && p.generatedData["ConnType"] == "ssh" {
if !checkArg("ansible_host_key_checking", args) && !checkArg("ANSIBLE_HOST_KEY_CHECKING", envVars) {
args = append(args, "-e", "ansible_host_key_checking=False")
}
}
2020-05-21 22:15:13 -04:00
// This must be the last arg appended to args
args = append(args, "-i", inventory, playbook)
return args, envVars
}
func (p *Provisioner) executeAnsible(ui packer.Ui, comm packer.Communicator, privKeyFile string) error {
playbook, _ := filepath.Abs(p.config.PlaybookFile)
inventory := p.config.InventoryFile
httpAddr := p.generatedData["PackerHTTPAddr"].(string)
// 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)
}
}
args, envvars := p.createCmdArgs(httpAddr, inventory, playbook, privKeyFile)
2015-03-10 00:11:57 -04:00
cmd := exec.Command(p.config.Command, args...)
cmd.Env = os.Environ()
if len(envvars) > 0 {
cmd.Env = append(cmd.Env, envvars...)
}
2015-03-10 00:11:57 -04:00
stdout, err := cmd.StdoutPipe()
if err != nil {
return err
}
stderr, err := cmd.StderrPipe()
if err != nil {
return err
}
wg := sync.WaitGroup{}
repeat := func(r io.ReadCloser) {
reader := bufio.NewReader(r)
for {
line, err := reader.ReadString('\n')
if line != "" {
line = strings.TrimRightFunc(line, unicode.IsSpace)
ui.Message(line)
}
if err != nil {
if err == io.EOF {
break
} else {
ui.Error(err.Error())
break
}
}
2015-03-10 00:11:57 -04:00
}
wg.Done()
}
wg.Add(2)
go repeat(stdout)
go repeat(stderr)
// remove winrm password from command, if it's been added
flattenedCmd := strings.Join(cmd.Args, " ")
sanitized := flattenedCmd
2020-03-31 19:55:32 -04:00
winRMPass, ok := p.generatedData["WinRMPassword"]
if ok && winRMPass != "" {
sanitized = strings.Replace(sanitized,
winRMPass.(string), "*****", -1)
}
2020-06-02 18:30:03 -04:00
if checkArg("ansible_password", args) {
usePass, ok := p.generatedData["Password"]
if ok && usePass != "" {
sanitized = strings.Replace(sanitized, usePass.(string), "*****", -1)
}
}
ui.Say(fmt.Sprintf("Executing Ansible: %s", sanitized))
2017-03-29 16:38:31 -04:00
if err := cmd.Start(); err != nil {
return err
}
2015-03-10 00:11:57 -04:00
wg.Wait()
err = cmd.Wait()
if err != nil {
return fmt.Errorf("Non-zero exit status: %s", err)
}
return nil
}
func validateFileConfig(name string, config string, req bool) error {
if req {
if name == "" {
return fmt.Errorf("%s must be specified.", config)
}
}
info, err := os.Stat(name)
if err != nil {
return fmt.Errorf("%s: %s is invalid: %s", config, name, err)
} else if info.IsDir() {
return fmt.Errorf("%s: %s must point to a file", config, name)
}
return nil
}
func validateInventoryDirectoryConfig(name string) error {
info, err := os.Stat(name)
if err != nil {
return fmt.Errorf("inventory_directory: %s is invalid: %s", name, err)
} else if !info.IsDir() {
return fmt.Errorf("inventory_directory: %s must point to a directory", name)
}
return nil
}
type userKey struct {
ssh.PublicKey
privKeyFile string
}
func newUserKey(pubKeyFile string) (*userKey, error) {
userKey := new(userKey)
if len(pubKeyFile) > 0 {
pubKeyBytes, err := ioutil.ReadFile(pubKeyFile)
if err != nil {
return nil, errors.New("Failed to read public key")
}
userKey.PublicKey, _, _, _, err = ssh.ParseAuthorizedKey(pubKeyBytes)
if err != nil {
return nil, errors.New("Failed to parse authorized key")
}
return userKey, nil
}
key, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, errors.New("Failed to generate key pair")
}
userKey.PublicKey, err = ssh.NewPublicKey(key.Public())
if err != nil {
return nil, errors.New("Failed to extract public key from generated key pair")
}
// To support Ansible calling back to us we need to write
// this file down
privateKeyDer := x509.MarshalPKCS1PrivateKey(key)
privateKeyBlock := pem.Block{
Type: "RSA PRIVATE KEY",
Headers: nil,
Bytes: privateKeyDer,
}
Add tmp package that offers Dir & File funcs this regroups the calls of tmp func calls into one package. the tmp pkg allows to store tmp files under a common directory for easier managment Squashed commit of the following: commit 74b674c01560c6b547e9cab5e8afb486f0fe7d6c Merge: fc94b5714 66001525d Author: Matthew Patton <mpatton@enquizit.com> Date: Fri Nov 2 02:02:38 2018 -0400 Merge branch 'remove_TMPDIR_docs' of github.com:tb3088/packer into remove_TMPDIR_docs commit fc94b571428447aaf2e5171967c95cdf9e5c73ff Author: Matthew Patton <mpatton@enquizit.com> Date: Fri Nov 2 01:42:38 2018 -0400 update tests to honor configured temporary directory commit 5a562359f4937af506607dd96ed1c46426093f52 Author: Matthew Patton <mpatton@enquizit.com> Date: Thu Nov 1 18:52:38 2018 -0400 sync straggler to refactor. implement tests at packer/configfile instead of buried in docker. commit 9d5a4a0935bfd049e2f8f50ad73e4455b42c9410 Author: Matthew Patton <mpatton@enquizit.com> Date: Thu Nov 1 15:39:46 2018 -0400 insert URL to GoLang API for os.TempDir() commit fbae4c2e051898aecd8705e9d8a20353430ede23 Author: Matthew Patton <mpatton@enquizit.com> Date: Thu Nov 1 07:41:29 2018 -0400 revise documentation to match code commit 3590fae8bd0afa92a161a49ed794ee32e357d583 Author: Matthew Patton <mpatton@enquizit.com> Date: Thu Nov 1 07:21:59 2018 -0400 refacter config_file. replace all hard-coded os.TempDir with wrapper commit d5c5306a97b648ded06a8eb76ce728f07c9924f0 Author: Matthew Patton <mpatton@enquizit.com> Date: Wed Oct 31 20:11:36 2018 -0400 close massive file overwrite hole with TempDir commit 0a72297da86089fbd07e4f7d9472a92fa4d1c733 Author: Matthew Patton <mpatton@enquizit.com> Date: Wed Oct 31 01:06:00 2018 -0400 adjust var declaration scope commit 20f68228b6c372d984ea5b055cfc8bda9b110ac5 Author: Matthew Patton <mpatton@enquizit.com> Date: Wed Oct 31 00:54:35 2018 -0400 use mktemp() equivalent to create temporary directory commit c73ebe3d8321664a2685a0baa8e441dd895b0db4 Author: Matthew Patton <mpatton@enquizit.com> Date: Tue Oct 30 20:40:58 2018 -0400 remove extraneous variable declaration, fix FOR loop commit 63549b8bd745a0090b15ed0b0ebf644162db27db Author: Matthew Patton <mpatton@enquizit.com> Date: Tue Oct 30 19:30:44 2018 -0400 match styistic convension with rest of docs commit 976101074942db36f10d3a686d6d29ddb7c01926 Author: Matthew Patton <mpatton@enquizit.com> Date: Tue Oct 30 19:08:39 2018 -0400 revert dangling config file change commit 95159afbc05eac96ed11c3183af5fb0abe2f2d5c Author: Matthew Patton <matthew.patton@itgfirm.com> Date: Fri Dec 29 23:53:43 2017 -0500 replace invalid TMPDIR variable wth PACKER_TMP_DIR. update ConfigTmpDir() to try common temporary paths first and only write to configDir() as a last resort. commit 66001525d72de56a4cf9339b900c46f59bc6e91a Author: Matthew Patton <mpatton@enquizit.com> Date: Fri Nov 2 01:42:38 2018 -0400 update tests to honor configured temporary directory commit e9b6adefeae8c65eb8aa47fef38cbf0aa424338c Author: Matthew Patton <mpatton@enquizit.com> Date: Thu Nov 1 18:52:38 2018 -0400 sync straggler to refactor. implement tests at packer/configfile instead of buried in docker. commit 852113ed076e2d14e5dca6815ea680da1e2896bb Author: Matthew Patton <mpatton@enquizit.com> Date: Thu Nov 1 15:39:46 2018 -0400 insert URL to GoLang API for os.TempDir() commit 54add38d1d1c6e283cd444b367ed8bd49a5f3699 Author: Matthew Patton <mpatton@enquizit.com> Date: Thu Nov 1 07:41:29 2018 -0400 revise documentation to match code commit 6b5b8f6d4edad0e187ca13d64ac4118f34eee643 Author: Matthew Patton <mpatton@enquizit.com> Date: Thu Nov 1 07:21:59 2018 -0400 refacter config_file. replace all hard-coded os.TempDir with wrapper commit c22092c601c33484327674f322c7379fa41506d7 Author: Matthew Patton <mpatton@enquizit.com> Date: Wed Oct 31 20:11:36 2018 -0400 close massive file overwrite hole with TempDir commit 7a730450916bf8e5dbc2a741ec233a49466ab7cc Author: Matthew Patton <mpatton@enquizit.com> Date: Wed Oct 31 01:06:00 2018 -0400 adjust var declaration scope commit 0f2933adb6f6922dfeab78a95371a444ec8918ab Author: Matthew Patton <mpatton@enquizit.com> Date: Wed Oct 31 00:54:35 2018 -0400 use mktemp() equivalent to create temporary directory commit d74839ede05dacf712b55a7bb48aec19fe6b007f Author: Matthew Patton <mpatton@enquizit.com> Date: Tue Oct 30 20:40:58 2018 -0400 remove extraneous variable declaration, fix FOR loop commit eb65416619437e4a3dec90277770803dd5b2281c Author: Matthew Patton <mpatton@enquizit.com> Date: Tue Oct 30 19:30:44 2018 -0400 match styistic convension with rest of docs commit acaa2b31ed463219c4ef099f351eec72406e2989 Author: Matthew Patton <mpatton@enquizit.com> Date: Tue Oct 30 19:08:39 2018 -0400 revert dangling config file change commit e573fde668dcb418561e61535c1d68b2888f5b0f Author: Matthew Patton <matthew.patton@itgfirm.com> Date: Fri Dec 29 23:53:43 2017 -0500 replace invalid TMPDIR variable wth PACKER_TMP_DIR. update ConfigTmpDir() to try common temporary paths first and only write to configDir() as a last resort. commit 39a9874afc9dea71b36753375fb7feb61ffecf69 Merge: 8a413cfe8 3315812c2 Author: Matthew Patton <mpatton@enquizit.com> Date: Tue Oct 30 18:19:26 2018 -0400 Merge branch 'master' of https://github.com/hashicorp/packer into prestine commit 8a413cfe83b0a70dbd99c32d936334ca5788ca9b Merge: e07491de5 4e14710a6 Author: Matthew Patton <mpatton@enquizit.com> Date: Mon Oct 1 20:18:10 2018 -0400 Merge remote-tracking branch 'upstream/master' into prestine commit e07491de59cead0b337a7f57f4a6c625e1f560ab Merge: 42610a35d a1fa35dff Author: Matthew Patton <mpatton@enquizit.com> Date: Tue Aug 21 13:26:19 2018 -0400 Merge remote-tracking branch 'upstream/master' into prestine commit 42610a35d5ef65af8844a58ad70e2ec75262da6a Merge: 5298142da 0d63cf7bc Author: Matthew Patton <matthew.patton@itgfirm.com> Date: Tue Jun 19 22:45:05 2018 -0400 Merge remote-tracking branch 'upstream/master' into prestine commit 5298142da6da95354844f201deeeef3c599e48b2 Merge: 7bb110bc7 9d9736552 Author: Matthew Patton <mpatton@enquizit.com> Date: Mon Jun 11 15:10:09 2018 -0400 Merge remote-tracking branch 'upstream/master' into prestine commit 7bb110bc744c9797fcaec25a265d9b85a22d6d6b Merge: a61c869ca 6189d66e7 Author: Matthew Patton <matthew.patton@itgfirm.com> Date: Wed May 9 23:41:22 2018 -0400 Merge remote-tracking branch 'upstream/master' into prestine commit a61c869ca7268acf0e3b1e1fa7a8ee7feea65984 Merge: 098101dd9 4be5f072c Author: Matthew Patton <matthew.patton@itgfirm.com> Date: Fri May 4 10:13:04 2018 -0400 Merge remote-tracking branch 'upstream/master' into prestine commit 098101dd99f08b0ca110d33eff6904537c6d21de Merge: fefaf0fa6 554b2b4a5 Author: Matthew Patton <matthew.patton@itgfirm.com> Date: Tue May 1 20:52:30 2018 -0400 Merge branch 'EOL-handling' into prestine Co-Authored-By: Matthew Patton <pattonme@yahoo.com>
2018-12-12 09:45:00 -05:00
tf, err := tmp.File("ansible-key")
if err != nil {
return nil, errors.New("failed to create temp file for generated key")
}
_, err = tf.Write(pem.EncodeToMemory(&privateKeyBlock))
if err != nil {
return nil, errors.New("failed to write private key to temp file")
}
err = tf.Close()
if err != nil {
return nil, errors.New("failed to close private key temp file")
}
userKey.privKeyFile = tf.Name()
return userKey, nil
}
type signer struct {
ssh.Signer
}
func newSigner(privKeyFile string) (*signer, error) {
signer := new(signer)
if len(privKeyFile) > 0 {
privateBytes, err := ioutil.ReadFile(privKeyFile)
if err != nil {
return nil, errors.New("Failed to load private host key")
}
signer.Signer, err = ssh.ParsePrivateKey(privateBytes)
if err != nil {
return nil, errors.New("Failed to parse private host key")
}
return signer, nil
}
key, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, errors.New("Failed to generate server key pair")
}
signer.Signer, err = ssh.NewSignerFromKey(key)
if err != nil {
return nil, errors.New("Failed to extract private key from generated key pair")
}
return signer, nil
}
2020-06-02 18:30:03 -04:00
//checkArg Evaluates if argname is in args
func checkArg(argname string, args []string) bool {
for _, arg := range args {
for _, ansibleArg := range strings.Split(arg, "=") {
if ansibleArg == argname {
return true
}
}
}
return false
}