When working with Chef-solo in a Vagrant environment, you might need to execute commands as a specific non-root user (like 'vagrant') rather than the default root user. This becomes particularly important when dealing with user-specific installations like npm packages.
The main obstacle occurs because Chef executes commands with a different environment than what you get when SSHing as the user. Even when specifying user "vagrant"
in your execute block, the PATH environment variable might not include the user-specific binary locations.
# This will fail despite specifying the vagrant user
execute "install npm packages" do
cwd "/home/vagrant"
user "vagrant"
command "npm install -g q zombie should mocha coffee-script"
end
When you SSH as vagrant, your PATH includes user-specific locations like /home/vagrant/.local/bin
. However, when Chef runs commands as the same user via sudo, it gets a sanitized PATH that doesn't include these locations.
Option 1: Using the Full Path to npm
The most straightforward solution is to use the absolute path to npm in your command:
execute "install npm packages" do
cwd "/home/vagrant"
user "vagrant"
command "/home/vagrant/.local/bin/npm install -g q zombie should mocha coffee-script"
end
Option 2: Set the Correct Environment
You can explicitly set the environment variables, including PATH:
execute "install npm packages" do
cwd "/home/vagrant"
user "vagrant"
environment ({
'PATH' => "/home/vagrant/.local/bin:#{ENV['PATH']}",
'HOME' => "/home/vagrant"
})
command "npm install -g q zombie should mocha coffee-script"
end
Option 3: Use a Wrapper Script
For complex scenarios, create a wrapper script that sets up the environment:
template "/tmp/npm_install.sh" do
source "npm_install.sh.erb"
mode "0755"
end
execute "run npm install" do
cwd "/home/vagrant"
user "vagrant"
command "/tmp/npm_install.sh"
end
Remember that when running as a non-root user:
- The user must have proper permissions on the working directory
- Global npm installations (-g flag) might still require root privileges
- Consider using
nodeenv
ornvm
for better node.js environment management
For production environments, consider using the community nodejs cookbook which handles these scenarios more elegantly:
include_recipe "nodejs"
nodejs_npm "install packages" do
packages ["q", "zombie", "should", "mocha", "coffee-script"]
path "/home/vagrant"
user "vagrant"
end
When working with Chef's execute
resource, we often encounter PATH issues when trying to run commands as non-root users. The original example shows npm installation failing because:
execute "install q and zombiejs" do
cwd "/home/vagrant"
user "vagrant"
action :run
command "npm install -g q zombie should mocha coffee-script"
end
Fails with:
execute[install q and zombiejs] (chef-redtail::default line 205) had an error: Errno::ENOENT: No such file or directory - npm install -g q zombie should mocha coffee-script
The root cause becomes clear when examining PATH variables:
# Regular shell:
$ echo $PATH
/home/vagrant/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
# When executed through sudo:
$ sudo -H -u vagrant -i echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
The key difference is the missing /home/vagrant/.local/bin
path where npm is installed.
Solution 1: Explicit PATH Setting
The most reliable approach is to explicitly set the PATH:
execute "install npm packages" do
command "npm install -g q zombie should mocha coffee-script"
cwd "/home/vagrant"
user "vagrant"
environment ({
"PATH" => "/home/vagrant/.local/bin:#{ENV['PATH']}"
})
end
Solution 2: Using the Full npm Path
Alternatively, use the absolute path to npm:
execute "install npm packages" do
command "/home/vagrant/.local/bin/npm install -g q zombie should mocha coffee-script"
cwd "/home/vagrant"
user "vagrant"
end
Solution 3: Shell Wrapper Approach
For complex environment needs:
execute "install npm packages" do
command "bash -c 'source /home/vagrant/.bashrc && npm install -g q zombie should mocha coffee-script'"
cwd "/home/vagrant"
user "vagrant"
end
To inspect the actual execution environment:
execute "debug environment" do
command "env > /tmp/chef_env.log"
cwd "/home/vagrant"
user "vagrant"
end
Then check the generated file to verify all environment variables.
For npm specifically, consider using the node_npm resource:
node_npm "install global packages" do
packages ["q", "zombie", "should", "mocha", "coffee-script"]
path "/home/vagrant"
user "vagrant"
options ["-g"]
end