emyl / vagrant-triggers

Allow the definition of arbitrary scripts that will run on the host before and/or after Vagrant commands.
MIT License
546 stars 35 forks source link

Execution of ruby script fails because of missing gems #5

Closed evansj closed 10 years ago

evansj commented 10 years ago

Thanks for this fantastic plugin.

I have a project with a thor script in ./bin/command.

From my command line, it works fine:

$ ./bin/command delete_cert -h ml
Running command: sudo find /data/puppet/var/ssl/ca/signed -regextype  posix-egrep -regex '.*/ml\.local\.pem' -print -delete

on host: puppetmaster

If I invoke this from a hook, it fails:

cfg.trigger.after :destroy, :execute => "./bin/command delete_cert -h ml", :stdout => true
$ vagrant destroy ml
Are you sure you want to destroy the 'ml' VM? [y/N] y
[ml] Forcing shutdown of VM...
[ml] Destroying VM and associated drives...
[ml] Running cleanup tasks for 'shell' provisioner...
[ml] Running triggers after action...
[ml] Executing command "./bin/command delete_cert -h ml"...
The command "./bin/command delete_cert -h ml" returned a failed exit code. The
error output is shown below:

/Applications/Vagrant/embedded/lib/ruby/2.0.0/rubygems/core_ext/kernel_require.rb:53:in `require': cannot load such file -- thor (LoadError)
    from /Applications/Vagrant/embedded/lib/ruby/2.0.0/rubygems/core_ext/kernel_require.rb:53:in `require'
    from ./bin/command:3:in `<main>'

It seems to be executing the script in the context of the embedded Vagrant ruby, rather than invoking as a regular script using the system ruby + gems. Any ideas?

emyl commented 10 years ago

Hi,

Thank you for reporting the issue.

That's an interesting case: as you've pointed out, while Vagrant commands are running, the embedded ruby is set to be the first in $PATH, and your script (that I guess is using the #!/usr/bin/env ruby shebang) will choose to use it instead of using the system one.

I'll address the issue soon, in the meantime you should be able to work around by specifying the absolute path:

cfg.trigger.after :destroy, :execute => "/usr/bin/ruby ./bin/command delete_cert -h ml", :stdout => true

evansj commented 10 years ago

Hi,

That seems to be the problem. Unfortunately I can't easily use the workaround as I'm using rvm locally, so my /usr/bin/ruby environment doesn't have the gems. I can't hard code my own rvm ruby path either as that won't work for the other members of our dev team.

I've experimented with a wrapper script which fixes up $PATH by removing elements which match /Vagrant/ before exec'ing the correct ruby, but the environment passed to the script also has Vagrant in $GEM_HOME and $GEM_PATH, and $GEMRC has also been added. However, just removing them from ENV seems to work, although maybe that's unique to my environment.

vagrant_exec script:

#!/usr/bin/env ruby

ENV['PATH'] = ENV['PATH'].split(File::PATH_SEPARATOR).drop(1).join(File::PATH_SEPARATOR)
ENV.delete_if { |element| element =~ /GEM/ }
exec(ENV, File.join(File.dirname(__FILE__), ARGV[0]), *ARGV.drop(1))

to be called from Vagrantfile like this:

    cfg.trigger.after :destroy, :execute => "./bin/vagrant_exec command delete_cert -h db1", :stdout => true

that ends up invoking "./bin/command delete_cert -h db1" with the system ruby. Which in my case causes that node's certificate to be deleted from the puppet master node so that it will work again the next time the node is recreated from scratch.

Thanks for your help!

evansj commented 10 years ago

Since Vagrant 1.5, this seems to be an issue again. I can't even get a simple ruby command to run now.

bin/test:

#!/usr/bin/env ruby

puts "\n\n\nHello, world!\n\n\n"

Vagrantfile:

# [...]
  config.vm.define :db1 do |cfg|
    cfg.vm.host_name = "db1.local"
    cfg.trigger.after :destroy, :stdout => true do
      run "./bin/test"
    end
# [...]

output:

$ vagrant destroy db1
==> db1: VM not created. Moving on...
==> db1: Running triggers after action...
==> db1: Executing command "./bin/test"...
The command "./bin/test" returned a failed exit code. The
error output is shown below:

/Applications/Vagrant/embedded/gems/gems/bundler-1.5.3/lib/bundler/spec_set.rb:92:in `block in materialize': Could not find vagrant-cachier-0.7.0 in any of the sources (Bundler::GemNotFound)
    from /Applications/Vagrant/embedded/gems/gems/bundler-1.5.3/lib/bundler/spec_set.rb:85:in `map!'
    from /Applications/Vagrant/embedded/gems/gems/bundler-1.5.3/lib/bundler/spec_set.rb:85:in `materialize'
    from /Applications/Vagrant/embedded/gems/gems/bundler-1.5.3/lib/bundler/definition.rb:133:in `specs'
    from /Applications/Vagrant/embedded/gems/gems/bundler-1.5.3/lib/bundler/definition.rb:178:in `specs_for'
    from /Applications/Vagrant/embedded/gems/gems/bundler-1.5.3/lib/bundler/definition.rb:167:in `requested_specs'
    from /Applications/Vagrant/embedded/gems/gems/bundler-1.5.3/lib/bundler/environment.rb:18:in `requested_specs'
    from /Applications/Vagrant/embedded/gems/gems/bundler-1.5.3/lib/bundler/runtime.rb:13:in `setup'
    from /Applications/Vagrant/embedded/gems/gems/bundler-1.5.3/lib/bundler.rb:119:in `setup'
    from /Applications/Vagrant/embedded/gems/gems/bundler-1.5.3/lib/bundler/setup.rb:17:in `<top (required)>'
    from /Users/evansj/.rvm/rubies/ruby-2.0.0-p451/lib/ruby/site_ruby/2.0.0/rubygems/core_ext/kernel_require.rb:55:in `require'
    from /Users/evansj/.rvm/rubies/ruby-2.0.0-p451/lib/ruby/site_ruby/2.0.0/rubygems/core_ext/kernel_require.rb:55:in `require'

I'm not sure if this is a Vagrant issue or a vagrant-triggers issue. I'm on Vagrant 1.5.2 and I have the following plugins installed:

vagrant-cachier (0.7.0)
vagrant-login (1.0.1, system)
vagrant-share (1.0.1, system)
vagrant-triggers (0.3.0)
vagrant-vmware-fusion (2.3.6)

As you pointed out last time, env will try to load the embedded ruby. I have no idea why it tries to load vagrant-cachier in the subprocess. The same error happens if I directly specify /usr/bin/ruby on the shebang line. I'm also using rvm, I wonder if that makes a difference?

emyl commented 10 years ago

I reopen the bug, I'll take a look to it shortly :wink:

davidpando commented 10 years ago

@evansj, kudos for the workaround. Vagrant 1.5.2 inserts 2 items on my path so if used

ENV['PATH'] = ENV['PATH'].split(File::PATH_SEPARATOR).delete_if {|element| element =~ /vagrant/}.join(File::PATH_SEPARATOR)

instead, that way my wrapped ruby scripts pick up the RVM environment.

I don't think is a vagrant-trigger bug but Vagrant, but quite annoying nevertheless.