New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RunTime Error: PDF could not be generated! Command Error: /usr/bin/env: ruby: No such file or directory #266

Closed
lauramosher opened this Issue Oct 31, 2013 · 9 comments

Comments

Projects
None yet
4 participants
@lauramosher

lauramosher commented Oct 31, 2013

RunTime Error: PDF could not be generated! Command Error: /usr/bin/env: ruby: No such file or directory wicked pdf

This code works fine locally but in production environments it is failing on export. Log below.

INFO: ***************WICKED***************
Rendered index.pdf.haml within layouts/pdf (209.2ms)
Rendered _header.pdf.haml within layouts/pdf (1.5ms)
Completed 500 Internal Server Error in 3406ms
FATAL: RuntimeError(Failed to execute:\n"/opt/application/releases/5405db831e02eb2987cc06b243333776ce9c34b8ab6db3e58e93f39c3f933621/vendor/bundle/ruby/2.0.0/bin/wkhtmltopdf" -q  --header-html "file:////tmp/wicked_header_pdf20131031-15836-12qkok6.html" --footer-right "[page] of [topage]" --footer-font-size 9    --margin-top 50  --encoding "UTF-8"   "file:////tmp/wicked_pdf20131031-15836-b874ab.html" "/tmp/wicked_pdf_generated_file20131031-15836-1ibji4k.pdf" \nError: PDF could not be generated!\n Command Error: /usr/bin/env: ruby: No such file or directory\n)
 vendor/bundle/ruby/2.0.0/gems/wicked_pdf-0.9.7/lib/wicked_pdf.rb:69:in `rescue in pdf_from_string'
 vendor/bundle/ruby/2.0.0/gems/wicked_pdf-0.9.7/lib/wicked_pdf.rb:73:in `pdf_from_string'
 vendor/bundle/ruby/2.0.0/gems/wicked_pdf-0.9.7/lib/pdf_helper.rb:63:in `make_pdf'
 vendor/bundle/ruby/2.0.0/gems/wicked_pdf-0.9.7/lib/pdf_helper.rb:76:in `make_and_send_pdf'
 vendor/bundle/ruby/2.0.0/gems/wicked_pdf-0.9.7/lib/pdf_helper.rb:21:in `render_with_wicked_pdf'

Relevant Code:

Gemfile

gem 'wicked_pdf', '~> 0.9.7'
gem 'wkhtmltopdf-binary', '~> 0.9.9'

Controller

        render pdf: "#{person_name}_#{localize_date(Time.now, :underscore)}",
              layout: 'pdf',
              margin: {
                top: 30
              },
              header: {
                html: {
                  template: '_header.pdf.haml'
                }
              },
              footer: {
                right: I18n.t('pdf.page_description'),
                font_size: 9
              },
              encoding: 'UTF-8'
@unixmonkey

This comment has been minimized.

Show comment
Hide comment
@unixmonkey

unixmonkey Nov 1, 2013

Collaborator

Looks to me that in production, the wkhtmltopdf binary here:

vendor/bundle/ruby/2.0.0/bin/wkhtmltopdf

Is not actually the wkhtmltopdf binary, but a binstub or something, and probably has a shebang on it like this:

#!/usr/bin/env ruby

What the error message is telling you is that it was unable to run ruby through the env binary, meaning ruby is not loaded properly in your PATH (or the PATH of the user the application is running as).

This has nothing to do with wicked_pdf, but is probably due to using RVM or Rbenv or maybe even Bundler.

My suggestion would be to try setting the exe_path directly to where the binary actually lives something like this:

WickedPdf.config = {
  :exe_path => "bundle exec #{Rails.root.join('vendor','bin','wkhtmltopdf')}"
}

with whatever the actual path is.

I've not seen this before, so I'll leave the issue open for a bit in hopes that someone else might be able to help more. Please let me know how it goes!

Collaborator

unixmonkey commented Nov 1, 2013

Looks to me that in production, the wkhtmltopdf binary here:

vendor/bundle/ruby/2.0.0/bin/wkhtmltopdf

Is not actually the wkhtmltopdf binary, but a binstub or something, and probably has a shebang on it like this:

#!/usr/bin/env ruby

What the error message is telling you is that it was unable to run ruby through the env binary, meaning ruby is not loaded properly in your PATH (or the PATH of the user the application is running as).

This has nothing to do with wicked_pdf, but is probably due to using RVM or Rbenv or maybe even Bundler.

My suggestion would be to try setting the exe_path directly to where the binary actually lives something like this:

WickedPdf.config = {
  :exe_path => "bundle exec #{Rails.root.join('vendor','bin','wkhtmltopdf')}"
}

with whatever the actual path is.

I've not seen this before, so I'll leave the issue open for a bit in hopes that someone else might be able to help more. Please let me know how it goes!

@anshdivu

This comment has been minimized.

Show comment
Hide comment
@anshdivu

anshdivu Nov 4, 2013

We found out the reason this error is occurring is because the apache user doesn't have any shell assigned to it.
What do you usually do while using wicked_pdf in production environment? Do you always assign a shell to the user that runs rails server? Is there a way around it, Is there a way to run wicked_pdf binary without running the command through the shell?

anshdivu commented Nov 4, 2013

We found out the reason this error is occurring is because the apache user doesn't have any shell assigned to it.
What do you usually do while using wicked_pdf in production environment? Do you always assign a shell to the user that runs rails server? Is there a way around it, Is there a way to run wicked_pdf binary without running the command through the shell?

@unixmonkey

This comment has been minimized.

Show comment
Hide comment
@unixmonkey

unixmonkey Nov 4, 2013

Collaborator

@anshdivu You could and should assign the binary path directly, and not use a script that runs through /usr/bin/env, which depends on your PATH.

It appears that this is what bundle exec is actually doing (running a binstub with #!/usr/bin/env ruby at the top).

I would do something like this and hardcode the actual paths to the binaries in each of your environments.

WickedPdf.config = {
    exe_path => (Rails.env.production? ? '/opt/bin/wkhtmltopdf' : '/usr/local/bin/wkhtmltopdf')
}

You could just as easily use RUBY_PLATFORM =~ 'linux' as shown here:

https://github.com/unixmonkey/wkhtmltopdf_binary_gem/blob/master/bin/wkhtmltopdf

Are you working on the same project as @Lmosher, or are you just trying to chime in?

Collaborator

unixmonkey commented Nov 4, 2013

@anshdivu You could and should assign the binary path directly, and not use a script that runs through /usr/bin/env, which depends on your PATH.

It appears that this is what bundle exec is actually doing (running a binstub with #!/usr/bin/env ruby at the top).

I would do something like this and hardcode the actual paths to the binaries in each of your environments.

WickedPdf.config = {
    exe_path => (Rails.env.production? ? '/opt/bin/wkhtmltopdf' : '/usr/local/bin/wkhtmltopdf')
}

You could just as easily use RUBY_PLATFORM =~ 'linux' as shown here:

https://github.com/unixmonkey/wkhtmltopdf_binary_gem/blob/master/bin/wkhtmltopdf

Are you working on the same project as @Lmosher, or are you just trying to chime in?

@lauramosher

This comment has been minimized.

Show comment
Hide comment
@lauramosher

lauramosher Nov 4, 2013

I thought the wkhtmltopdf-binary gem set up the WickedPdf configuration for you and installed the binaries for the application rather than needing to install them separate from the application/directly on the server.

To be more specific, we are NOT installing the binaries on the server by hand. The wkhtmltopdf-binary gem (https://rubygems.org/gems/wkhtmltopdf-binary) is installing when we run bundler. I assumed that also updated the WickedPdf configuration so we don't need to specify it ourselves.

What the error message is telling you is that it was unable to run ruby through the env binary, meaning ruby is not > loaded properly in your PATH (or the PATH of the user the application is running as).

Ruby is properly loaded in the PATH in each environment. Apache is running the entire rails application (except wicked_pdf/wkhtmltopdf-binary) without a problem and we verified that Ruby is in the PATH.

The issue we found is that there is no shell assigned to Apache which causes Ruby to throw an error when it tries to run the wkhtmltopdf binary from the vendor folder (where bundler installed the binary gem). I would assume that no matter where the binary path is and no matter how we specify the binary path, we would run into the same problem since, regardless of where it is, it still needs to have a shell to execute the command.

To note: We did run the exact code your gem is running on the server from a separate user and it runs just fine since those users have a shell. Apache does not and therefore breaks whenever your gem tries to run the wkhtmltopdf binary.

So to repeat @anshdivu's questions, is there a way to run the binaries without requiring a command through a shell OR when you run your rails application, does that user always have a shell assigned to it?

(And yes, we are working on the same project.)

lauramosher commented Nov 4, 2013

I thought the wkhtmltopdf-binary gem set up the WickedPdf configuration for you and installed the binaries for the application rather than needing to install them separate from the application/directly on the server.

To be more specific, we are NOT installing the binaries on the server by hand. The wkhtmltopdf-binary gem (https://rubygems.org/gems/wkhtmltopdf-binary) is installing when we run bundler. I assumed that also updated the WickedPdf configuration so we don't need to specify it ourselves.

What the error message is telling you is that it was unable to run ruby through the env binary, meaning ruby is not > loaded properly in your PATH (or the PATH of the user the application is running as).

Ruby is properly loaded in the PATH in each environment. Apache is running the entire rails application (except wicked_pdf/wkhtmltopdf-binary) without a problem and we verified that Ruby is in the PATH.

The issue we found is that there is no shell assigned to Apache which causes Ruby to throw an error when it tries to run the wkhtmltopdf binary from the vendor folder (where bundler installed the binary gem). I would assume that no matter where the binary path is and no matter how we specify the binary path, we would run into the same problem since, regardless of where it is, it still needs to have a shell to execute the command.

To note: We did run the exact code your gem is running on the server from a separate user and it runs just fine since those users have a shell. Apache does not and therefore breaks whenever your gem tries to run the wkhtmltopdf binary.

So to repeat @anshdivu's questions, is there a way to run the binaries without requiring a command through a shell OR when you run your rails application, does that user always have a shell assigned to it?

(And yes, we are working on the same project.)

@unixmonkey

This comment has been minimized.

Show comment
Hide comment
@unixmonkey

unixmonkey Nov 4, 2013

Collaborator

The wkhtmltopdf-binary only installs pre-compiled binaries for Linux386/x64 and OSX, and uses a small ruby script to wrap the call to the right one. It does not update the wicked_pdf configuration.

WickedPdf has some code to attempt to figure out how to run the wkhtmltopdf binary in the absence of a configuration here: https://github.com/mileszs/wicked_pdf/blob/master/lib/wicked_pdf.rb#L243

Hard to say without seeing exactly what your server is doing, but I would guess it is running bundle exec which wkhtmltopdf, which is pointing to wkhtmltopdf-binary's wrapper script, and that is running with #!/usr/bin/ruby, which bypasses the PATH environment variable entirely (which might have unexpected results on a system with no Ruby executable in that path, or uses Ruby 1.8 with dependencies using 1.9 syntax).

There seems to be an open issue on the wkhtmltopdf-binary project related to this here:
zakird/wkhtmltopdf_binary_gem#5
Maybe you can use adamhunter's fork here if that is your issue.

No matter what you do, Ruby's call out to system() is going to invoke a shell of some sort.

Collaborator

unixmonkey commented Nov 4, 2013

The wkhtmltopdf-binary only installs pre-compiled binaries for Linux386/x64 and OSX, and uses a small ruby script to wrap the call to the right one. It does not update the wicked_pdf configuration.

WickedPdf has some code to attempt to figure out how to run the wkhtmltopdf binary in the absence of a configuration here: https://github.com/mileszs/wicked_pdf/blob/master/lib/wicked_pdf.rb#L243

Hard to say without seeing exactly what your server is doing, but I would guess it is running bundle exec which wkhtmltopdf, which is pointing to wkhtmltopdf-binary's wrapper script, and that is running with #!/usr/bin/ruby, which bypasses the PATH environment variable entirely (which might have unexpected results on a system with no Ruby executable in that path, or uses Ruby 1.8 with dependencies using 1.9 syntax).

There seems to be an open issue on the wkhtmltopdf-binary project related to this here:
zakird/wkhtmltopdf_binary_gem#5
Maybe you can use adamhunter's fork here if that is your issue.

No matter what you do, Ruby's call out to system() is going to invoke a shell of some sort.

@lauramosher

This comment has been minimized.

Show comment
Hide comment
@lauramosher

lauramosher Nov 4, 2013

Okay.

Well, from what we can tell, it's finding the proper path (as we ran it using the path in the error directly on the server without a problem). My guess is that it needs the shell and we just need to set it up to use it.

Thanks,

lauramosher commented Nov 4, 2013

Okay.

Well, from what we can tell, it's finding the proper path (as we ran it using the path in the error directly on the server without a problem). My guess is that it needs the shell and we just need to set it up to use it.

Thanks,

@mileszs

This comment has been minimized.

Show comment
Hide comment
@mileszs

mileszs Nov 4, 2013

Owner

You can do that, and it will work. However, your best bet would be to set the configuration to an absolute path to the binary. Then you will not need to enable shell access for your Apache user, which would be a security concern.

I say this just to make sure we're all on the same page. The wkhtmltopdf_binary_gem is automatically pointing to a Ruby script that decides which binary to use. It's not that the binary requires Ruby or shell access. It doesn't. The issue is with the gem -- which is a fine, upstanding gem with great intentions. In a production environment, however, it would be better to 1) know where the binary is that is appropriate for your system, 2) use that path directly instead of a Ruby script that makes decisions based on some strings.

These might all be things you already understood, but it didn't seem like everyone was on the same page, so I thought I'd give it a shot. Does that all make sense?

Owner

mileszs commented Nov 4, 2013

You can do that, and it will work. However, your best bet would be to set the configuration to an absolute path to the binary. Then you will not need to enable shell access for your Apache user, which would be a security concern.

I say this just to make sure we're all on the same page. The wkhtmltopdf_binary_gem is automatically pointing to a Ruby script that decides which binary to use. It's not that the binary requires Ruby or shell access. It doesn't. The issue is with the gem -- which is a fine, upstanding gem with great intentions. In a production environment, however, it would be better to 1) know where the binary is that is appropriate for your system, 2) use that path directly instead of a Ruby script that makes decisions based on some strings.

These might all be things you already understood, but it didn't seem like everyone was on the same page, so I thought I'd give it a shot. Does that all make sense?

@lauramosher

This comment has been minimized.

Show comment
Hide comment
@lauramosher

lauramosher Nov 4, 2013

I understand. I'll update with what we decide to do and (hopefully) a success story.

lauramosher commented Nov 4, 2013

I understand. I'll update with what we decide to do and (hopefully) a success story.

@lauramosher

This comment has been minimized.

Show comment
Hide comment
@lauramosher

lauramosher Nov 11, 2013

Sorry for such a delayed response. We ended up having several long in depth discussions on "best practice" for the project & company.

We came up with several solutions.

Setup WickedPdf.config to use GEM_HOME & Gem.version

A mostly dynamic way to setup the configuration path for multiple systems.

require 'rbconfig'

if RbConfig::CONFIG['host_os'] =~ /linux/
  arch = RbConfig::CONFIG['host_cpu'] == 'x86_64' ? 'wkhtmltopdf_linux_x64' : 'wkhtmltopdf_linux_386'
elsif RbConfig::CONFIG['host_os'] =~ /darwin/
  arch = 'wkhtmltopdf_darwin_386'
else
  raise "Invalid platform. Must be running Intel-based Linux or OSX."
end

WickedPdf.config = {
  exe_path: "#{ENV['GEM_HOME']}/gems/wkhtmltopdf-binary-#{Gem.loaded_specs['wkhtmltopdf-binary'].version}/bin/#{arch}"
}

Since the /gems/bin/wkhtmltopdf binary is just a ruby script, we discovered that this is where it was breaking on production. So we bypassed it by using the same logic, except where it didn't require a shell.

Unfortunately, if we ever updated the gem and the names of the binaries changed, we'd have to continually monitor the script to make sure ours matched.

Setup WickedPdf.config for production only

A very static way to set the configuration, specifc to our enviroment.

if Rails.env.production?
  WickedPdf.config = {
    exe_path: "/opt/application/current/gems/wkhtmltopdf-binary-#{Gem.loaded_specs['wkhtmltopdf-binary'].version}/bin/wkhtmltopdf_linux_x64"
  }
end

Unfortunately, we run into a similar issue to the more dynamic way to set the binary path.

Setup Chef cookbook to setup the production environments

The more involved solution is more server specific to where we wrote a chef cookbook to setup all production environment variables and binaries. This lets us be more flexible where we get the binaries, how they are installed, and how we set the wicked pdf configurations.

lauramosher commented Nov 11, 2013

Sorry for such a delayed response. We ended up having several long in depth discussions on "best practice" for the project & company.

We came up with several solutions.

Setup WickedPdf.config to use GEM_HOME & Gem.version

A mostly dynamic way to setup the configuration path for multiple systems.

require 'rbconfig'

if RbConfig::CONFIG['host_os'] =~ /linux/
  arch = RbConfig::CONFIG['host_cpu'] == 'x86_64' ? 'wkhtmltopdf_linux_x64' : 'wkhtmltopdf_linux_386'
elsif RbConfig::CONFIG['host_os'] =~ /darwin/
  arch = 'wkhtmltopdf_darwin_386'
else
  raise "Invalid platform. Must be running Intel-based Linux or OSX."
end

WickedPdf.config = {
  exe_path: "#{ENV['GEM_HOME']}/gems/wkhtmltopdf-binary-#{Gem.loaded_specs['wkhtmltopdf-binary'].version}/bin/#{arch}"
}

Since the /gems/bin/wkhtmltopdf binary is just a ruby script, we discovered that this is where it was breaking on production. So we bypassed it by using the same logic, except where it didn't require a shell.

Unfortunately, if we ever updated the gem and the names of the binaries changed, we'd have to continually monitor the script to make sure ours matched.

Setup WickedPdf.config for production only

A very static way to set the configuration, specifc to our enviroment.

if Rails.env.production?
  WickedPdf.config = {
    exe_path: "/opt/application/current/gems/wkhtmltopdf-binary-#{Gem.loaded_specs['wkhtmltopdf-binary'].version}/bin/wkhtmltopdf_linux_x64"
  }
end

Unfortunately, we run into a similar issue to the more dynamic way to set the binary path.

Setup Chef cookbook to setup the production environments

The more involved solution is more server specific to where we wrote a chef cookbook to setup all production environment variables and binaries. This lets us be more flexible where we get the binaries, how they are installed, and how we set the wicked pdf configurations.

@unixmonkey unixmonkey closed this Dec 5, 2013

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment