How to Execute Commands as Non-Root User (vagrant) in Chef-Solo Recipes


8 views

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 or nvm 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