I've previously used the ansible-local provisioner without a problem. I would download and install ansible into a python virtualenv on the remote machine being provisioned by Packer. I would then run ansible-local by specifying a command which activated the python virtualenv so that the provisioner would pickup the correct executable. No problems.
See below:
{
"type": "ansible-local",
"playbook_dir": "ansible",
"playbook_file": "ansible/playbook.yml",
"command": "source /home/ec2-user/venv/bin/activate && ANSIBLE_FORCE_COLOR=1 PYTHONUNBUFFERED=1 ansible-playbook",
"extra_arguments": [
"--extra-vars",
"ansible_python_interpreter=home/ec2-user/venv/golden_image_venv/bin/python2.7"
]
},
However, when I tried to run the same template using ansible (remote) I had issues. Depending on how I structure the command I get the error "executable file not found in $PATH" or "no file or directory found". I installed my python virtualenv the same way on the machine running Packer.
See below:
{
"type": "ansible",
"playbook_file": "/home/ec2-user/fix-packer-ansible-terraform/ansible/playbook.yml",
"command": "source /home/ec2-user/venv/bin/activate && ANSIBLE_FORCE_COLOR=1 PYTHONUNBUFFERED=1 /home/ec2-user/venv/bin/ansible-playbook",
"extra_arguments": [
"--extra-vars",
"roles_path=/home/ec2-user/fix-packer-ansible-terraform/ansible/roles/"
]
}
ansible installation:
ansible-playbook 2.7.10
config file = None
configured module search path = [u'/home/ec2-user/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
ansible python module location = /home/ec2-user/venv/local/lib/python2.7/site-packages/ansible
executable location = /home/ec2-user/venv/bin/ansible-playbook
python version = 2.7.16 (default, Mar 18 2019, 18:38:44) [GCC 4.8.5 20150623 (Red Hat 4.8.5-28)]
I dug a bit deeper and see that in the source code (https://github.com/hashicorp/packer/blob/master/provisioner/ansible/provisioner.go) the line cmd := exec.Command(p.config.Command, args...)
is being called. It looks like the way the go exec package is being used it's not possible to activate the python virtualenv and call the ansible-playbook in the same command parameter specified in the Packer template.
Is anyone aware of any workarounds? Why does this work in the ansible-local provisioner and not the ansible (remote) provisioner? How can I run ansible provisioner within a python virtualenv using ansible (remote) provisioner?
Without more detail to draw on, my guess here is that the problem is that on the remote machine your $PATH isn't configured to contain either ansible or virtualenv, and that's why you're getting the file not found errors. What happens if you prefix your "command" with PATH=$PATH:/path/to/your/ansible/binary:/path/to/your/virtualenv/binary
?
u prefix your "command" with
PATH=$PATH:/path/to/your/ansible/binary:/path/to/your/virtualenv/binary
?
This works if I'm just calling a single command such as ansible-playbook. However, this does not allow me to activate the virtualenv and call ansible-playbook in the same command chaining with &&.
I believe this is related to how the command parameter is implemented in ansible provisioner vs. ansible-local provisioner.
The source code you've linked is for the ansible (remote) provisioner, not the local one. The "ansible-local" one doesn't use Exec.Command; it calls the command over an SSH session, because it's running the command on your guest machine.
I threw together a repro case using your installation code from 7529, and it looks like it works for me.
{
"builders": [
{
"ami_name": "repro_7569",
"instance_type": "t2.micro",
"source_ami_filter": {
"filters": {
"virtualization-type": "hvm",
"name": "amzn-ami-hvm-*-x86_64-gp2",
"root-device-type": "ebs"
},
"owners": ["137112412989"],
"most_recent": true
},
"ssh_username": "ec2-user",
"type": "amazon-ebs"
}
],
"provisioners": [
{
"type": "shell",
"inline": [
"sudo yum update -y",
"sudo yum install python-pip -y",
"sudo yum install python-virtualenv -y",
"sudo yum -y install rpm-build",
"virtualenv --python=/usr/bin/python2.7 /tmp/venv",
"source /tmp/venv/bin/activate",
"deactivate",
"virtualenv --relocatable /tmp/venv",
"source /tmp/venv/bin/activate",
"python -m pip install --upgrade pip",
"python -m pip install ansible --progress-bar pretty",
"deactivate"
],
"remote_folder": "~"
},
{
"type": "ansible-local",
"command": "source /tmp/venv/bin/activate && ANSIBLE_FORCE_COLOR=1 PYTHONUNBUFFERED=1 /tmp/venv/bin/ansible-playbook",
"playbook_file": "./ansible/playbook.yml"
}
]
}
Can I have some more complete logs or a repro case that shows where I'm going wrong here?
oh wait I misread, I see. ansible-local works and it's remote that doesn't. going back for further repro, apologies.
Yep now that I'm using the right provisioner I have a repro. Funny how that works.
Okay, I'm not sure there's a good workaround for this. You may be able to get a wrapping script to work -- for example, creating a "call_ansible.sh" script which contains something along the lines of
#!/bin/bash
source /tmp/venv/bin/activate && ANSIBLE_FORCE_COLOR=1 PYTHONUNBUFFERED=1 /tmp/venv/bin/ansible-playbook $*
and then setting your "command" to "call_ansible.sh"
but I didn't get a chance to test long enough to come up with a working example.
The ansible provisioner is community-supported, which means that normally HashiCorp engineers don't normally spend a lot of time on it other than reviewing PRs; this is a decision I'm considering revisiting because it's popular but has no actual owner.
I'll think on whether I have the bandwidth to bring this provisioner into a list of more fully-supported ones, but for now I'd recommend using your ansible-local workaround.
If someone wants to make this workable before I can get around to it, I suspect the "right" answer is to change "command" to accept an array of strings.
I did get a working wrapper --
call_ansible.sh:
#!/bin/bash
source /tmp/venv/bin/activate && ANSIBLE_FORCE_COLOR=1 PYTHONUNBUFFERED=1 /tmp/venv/bin/ansible-playbook "$@"
ansible provisioner json:
{
"type": "ansible",
"command": "/path/to/call_ansible.sh",
"skip_version_check": true,
"playbook_file": "./playbook_remote.yml"
}
I'm going to lock this issue because it has been closed for _30 days_ โณ. This helps our maintainers find and focus on the active issues.
If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.
Most helpful comment
I did get a working wrapper --
call_ansible.sh:
ansible provisioner json: