I'm trying to provision Ruby + Rails on a guest VM. Here's what I have in my playbook.yml
:
---
- hosts: all
sudo: true
tasks:
- apt: update_cache=yes
- apt: name={{ item }} state=present
with_items:
- build-essential
- git-core
- zlib1g-dev
- libssl-dev
- libreadline-dev
- libmysqlclient-dev
- libyaml-dev
- libxml2-dev
- libxslt1-dev
- libcurl4-openssl-dev
- python-software-properties
- libffi-dev
- curl
- command: git clone git://github.com/sstephenson/rbenv.git .rbenv
- shell: echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
- shell: echo 'eval "$(rbenv init -)"' >> ~/.bashrc
- shell: exec $SHELL
- command: git clone git://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
- shell: echo 'export PATH="$HOME/.rbenv/plugins/ruby-build/bin:$PATH"' >> ~/.bashrc
- shell: exec $SHELL
- command: git clone https://github.com/sstephenson/rbenv-gem-rehash.git ~/.rbenv/plugins/rbenv-gem-rehash
- command: rbenv install 2.2.3
- command: rbenv global 2.2.3
- shell: echo 'gem{{":"}} --no-ri --no-rdoc' >> ~/.gemrc
- command: gem install bundler
- command: add-apt-repository -y ppa:chris-lea/node.js
- apt: update_cache=yes
- apt: name=nodejs state=present
- command: gem install rails -v 4.2.2
- command: rbenv rehash
When provisioning, Ansible always hangs at task - shell: exec $SHELL
and doesn't return. But when I SSH into the machine and run exec $SHELL
, it executes and returns. When I use - command: exec $SHELL
instead in my playbook, Ansible prints this error:
failed: [default] => {"cmd": "exec /bin/bash", "failed": true, "rc": 2}
msg: [Errno 2] No such file or directory
I can confirm that /bin/bash
is present.
What's going on? I've been struggling with this for half a day now. Please advise.
Well first of all I wouldn't recommend that you do things like this:
- shell: echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
- shell: echo 'eval "$(rbenv init -)"' >> ~/.bashrc
If you happen to run this playbook against a host multiple times then you'll end up with multiple instances of each of these commands in your .bashrc file, which you don't want. You should consider templating your .bashrc file, or at the very least using something like lineinfile to make sure these entries are added only once.
As far as this task goes:
- shell: exec $SHELL
This is something you simply should never do. This certainly won't have the result you may be expecting, and it indicates that you don't fully understand how tasks (and in particular the shell task) work. exec
replaces the exiting shell with the specified command. If you didn't get an error when attempting this then what you would effectively be doing is replacing the shell launched by Ansible to run this command. What would theoretically happen is that the shell Ansible launches gets replaced with another shell, and Ansible would then simply hang because its waiting for the shell to terminate, but the shell you exec'd isn't doing anything so it will never terminate.
Once this task executed your playbook wouldn't continue running. You would either need to ctrl+c to terminate ansible-playbook, or you'd need to log into the remote host and kill the shell ansible is waiting on, at which point the playbook would terminate due to the task failing.
Switching this to -command: shell $SHELL
also fails because exec
is a built-in shell command, but as the Ansible documentation for the command module states:
The given command ... will not be processed through the shell
Since the command
module is executing the command directly and not through a shell then shell built-in's like exec
won't be recognized. This is why you get the error message that you indicated above.
Perhaps you should provide a detailed explanation of what you're trying to do and why you think you need to be invoking exec $SHELL
in this manner.
You should also consider changing tasks like these:
- command: gem install bundler
to use the Ansible gem module instead. In general you want to use the built-in Ansible modules wherever possible, and only use shell/command, etc. as a last resort. The built-in modules provide better error handling, parameter validation, etc. among other things.