I intend to create an AWS lambda function using Ruby 2.7 with Selenium. When I try to run my example function i get:
{
"errorMessage": "unable to connect to /opt/chromedriver 127.0.0.1:9515",
"errorType": "Function<Selenium::WebDriver::Error::WebDriverError>",
"stackTrace": [
"/var/task/vendor/bundle/ruby/2.7.0/gems/selenium-webdriver-4.3.0/lib/selenium/webdriver/common/service_manager.rb:138:in `connect_until_stable'",
Which is confusing to me because the file is located in that path. I have created a AWS layer with both chromedriver and chromium headless. After creating the layers i have attached them to the function. The error when the files are not there is different so I am assuming it can reach the files.
My sample program is:
require 'json'
require 'selenium-webdriver'
def lambda_handler(event:, context:)
driver = setup_driver
driver.navigate.to 'http://www.google.com'
element = driver.find_element(name: 'q')
element.send_keys 'Pizza'
element.submit
title = driver.title
driver.quit
{ statusCode: 200, body: JSON.generate(title) }
end
def setup_driver
service = Selenium::WebDriver::Service.chrome(path: '/opt/chromedriver')
Selenium::WebDriver.for :chrome, service: service, options: driver_options
end
def driver_options
options = Selenium::WebDriver::Chrome::Options.new(binary: '/opt/headless-chromium')
options.add_argument('--no-sandbox')
options.add_argument('--headless')
options.add_argument("--window-size=#{800},#{600}")
options.add_argument('--disable-popup-blocking')
options.add_argument('--disable-gpu')
options.add_argument('--disable-dev-shm-usage')
options.add_argument('--single-process')
return options
end
Update:
After locating the proper chromedriver and installing google chrome from the yum repo i managed to get it to work locally.
Here is my Dockerfile
FROM public.ecr.aws/lambda/ruby:2.7
COPY . ${LAMBDA_TASK_ROOT}
RUN bundle config --local silence_root_warning true
RUN bundle install --path vendor/bundle --clean
# USER root
RUN yum -y install wget unzip
# Chromium
COPY google-chrome.repo /etc/yum.repos.d/google-chrome.repo
RUN yum install google-chrome-stable -y
# Chromedriver
RUN wget https://chromedriver.storage.googleapis.com/102.0.5005.61/chromedriver_linux64.zip
RUN unzip chromedriver_linux64.zip -d /usr/bin/
RUN rm chromedriver_linux64.zip
CMD [ "lambda_function.lambda_handler" ]
When i run it locally with:
docker run -p 9000:8080 prueba
And test it in a different terminal:
curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"payload":"hello world!"}'
{"statusCode":200,"body":"\"Pizza - Buscar con Google\""}%
It works wonderful!!!
However when deploying it (By publishing the docker image in the ECR and creating a lambda using given docker image) i get the following error:
{
"errorMessage": "Net::ReadTimeout with #<TCPSocket:(closed)>",
"errorType": "Function<Net::ReadTimeout>",
"stackTrace": [
"/var/lang/lib/ruby/2.7.0/net/protocol.rb:217:in `rbuf_fill'",
"/var/lang/lib/ruby/2.7.0/net/protocol.rb:191:in `readuntil'",
"/var/lang/lib/ruby/2.7.0/net/protocol.rb:201:in `readline'",
"/var/lang/lib/ruby/2.7.0/net/http/response.rb:42:in `read_status_line'",
"/var/lang/lib/ruby/2.7.0/net/http/response.rb:31:in `read_new'",
"/var/lang/lib/ruby/2.7.0/net/http.rb:1528:in `block in transport_request'",
"/var/lang/lib/ruby/2.7.0/net/http.rb:1519:in `catch'",
"/var/lang/lib/ruby/2.7.0/net/http.rb:1519:in `transport_request'",
"/var/lang/lib/ruby/2.7.0/net/http.rb:1492:in `request'",
"/var/task/vendor/bundle/ruby/2.7.0/gems/selenium-webdriver-4.3.0/lib/selenium/webdriver/remote/http/default.rb:118:in `response_for'",
"/var/task/vendor/bundle/ruby/2.7.0/gems/selenium-webdriver-4.3.0/lib/selenium/webdriver/remote/http/default.rb:77:in `request'",
"/var/task/vendor/bundle/ruby/2.7.0/gems/selenium-webdriver-4.3.0/lib/selenium/webdriver/remote/http/common.rb:59:in `call'",
"/var/task/vendor/bundle/ruby/2.7.0/gems/selenium-webdriver-4.3.0/lib/selenium/webdriver/remote/bridge.rb:592:in `execute'",
"/var/task/vendor/bundle/ruby/2.7.0/gems/selenium-webdriver-4.3.0/lib/selenium/webdriver/remote/bridge.rb:52:in `create_session'",
"/var/task/vendor/bundle/ruby/2.7.0/gems/selenium-webdriver-4.3.0/lib/selenium/webdriver/common/driver.rb:314:in `block in create_bridge'",
"/var/task/vendor/bundle/ruby/2.7.0/gems/selenium-webdriver-4.3.0/lib/selenium/webdriver/common/driver.rb:312:in `tap'",
"/var/task/vendor/bundle/ruby/2.7.0/gems/selenium-webdriver-4.3.0/lib/selenium/webdriver/common/driver.rb:312:in `create_bridge'",
"/var/task/vendor/bundle/ruby/2.7.0/gems/selenium-webdriver-4.3.0/lib/selenium/webdriver/common/driver.rb:74:in `initialize'",
"/var/task/vendor/bundle/ruby/2.7.0/gems/selenium-webdriver-4.3.0/lib/selenium/webdriver/common/driver.rb:47:in `new'",
"/var/task/vendor/bundle/ruby/2.7.0/gems/selenium-webdriver-4.3.0/lib/selenium/webdriver/common/driver.rb:47:in `for'",
"/var/task/vendor/bundle/ruby/2.7.0/gems/selenium-webdriver-4.3.0/lib/selenium/webdriver.rb:89:in `for'",
"/var/task/lambda_function.rb:19:in `setup_driver'",
"/var/task/lambda_function.rb:7:in `lambda_handler'"
]
}
I have already added memory and timeout seconds to the lambda function but it still fails. This is odd because it works locally but not on the actual AWS Lambda.