Packer: Trouble running ansible (remote) within a python virtualenv (works for ansible-local)

Created on 25 Apr 2019  ยท  9Comments  ยท  Source: hashicorp/packer

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?

bug community-supported plugin provisioneansible-remote

Most helpful comment

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"
        }

All 9 comments

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.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

craigsimon picture craigsimon  ยท  3Comments

mushon4 picture mushon4  ยท  3Comments

mwhooker picture mwhooker  ยท  3Comments

PartyImp picture PartyImp  ยท  3Comments

sourav82 picture sourav82  ยท  3Comments