Csteele5
Csteele5

Reputation: 1292

How can I get my AWS Lambda to access gems stored in vendor/bundle?

I'm writing a Lambda function in Ruby which will eventually send me some notifications in Slack via Webhook. So what I have for my lambda_function file is

require 'json'
require 'webhook'

def lambda_handler(event:, context:)
    # TODO implement
    { statusCode: 200, body: JSON.generate('Hello from Lambda!') }
    Webhook.post('https://mywebhookurl', {message: 'test'})
end

And the directory structure in my inline code editor looks like this:

Gemfile
Gemfile.lock
lambda_function.rb
vendor/
  bundle/
    ruby
      2.3.0
        gems/webhook

also under the 2.3.0 bath are several other folders including build_info, cache, doc etc. In order to get this code onto AWS Lambda, I'm running

zip -r myLambda.zip * to get everything into a zip file and uploaded to Lambda.

However, when I finally go to run a basic test on the lambda, I get the following error:

{
  "errorMessage": "cannot load such file -- webhook",
  "errorType": "Init<LoadError>",
  "stackTrace": [
    "/var/lang/lib/ruby/2.5.0/rubygems/core_ext/kernel_require.rb:59:in `require'",
    "/var/lang/lib/ruby/2.5.0/rubygems/core_ext/kernel_require.rb:59:in `require'",
    "/var/task/lambda_function.rb:2:in `<top (required)>'",
    "/var/lang/lib/ruby/2.5.0/rubygems/core_ext/kernel_require.rb:59:in `require'",
    "/var/lang/lib/ruby/2.5.0/rubygems/core_ext/kernel_require.rb:59:in `require'"
  ]
}

There shouldn't be any more to this as the following tutorial shows exactly how to set up what I have, but doesn't work. Does anyone have success pulling gems from their gemfile in AWS Lambda?

Upvotes: 15

Views: 7725

Answers (3)

I think you should not change the GEM_PATH or having to set $LOAD_PATH in every lambda functions. The "best" way is to do this little hack when you create the layer archive:

bundle install --path vendor/bundle
cd vendor/bundle
mkdir ruby/gems
mv ruby/2.5.0 ruby/gems/
zip -r layer.zip ruby/gems/2.5.0/

Upvotes: 5

ryanjones
ryanjones

Reputation: 5521

I ran into this same problem when building AWS Lambda Layers w/ Ruby. A quick and easy way to get this to work is to add all of the gem paths to Ruby's $LOAD_PATH in your AWS Lambda. IE:

load_paths = Dir["/opt/ruby/gems/2.5.0/**/lib"]
$LOAD_PATH.unshift(*load_paths)

require 'webhook'

Replace "/opt/ruby/gems/2.5.0/**/lib" with "./vendor/bundle/ruby/2.3.0/gems/**/lib" in your case.

When you do require 'webhook' it's going to go and look through all of the paths and come across "./vendor/bundle/ruby/2.3.0/gems/webhook-1.0.0/lib/webhook.rb" and add it into your AWS Lambda. require doesn't require a file extension.

When we run rails through bundler it does a bunch of 'magic' for us including making sure that our $LOAD_PATH is pointing at the gems. Since AWS Lambdas don't use bundler, we need to do some of this 'magic' ourselves.

Upvotes: 19

Casao
Casao

Reputation: 542

You need to make sure the version of Ruby you used locally to bundle matches the version used by Lambda.

Your zip appears to have the gems installed in 2.3.0 but your stack trace lists 2.5.0. This mismatch means lambda runner can’t find your gems.

Upvotes: 14

Related Questions