I want to run my lambda function locally (I am currently using ruby 2.7.1), but when I require a gem that needs native dependencies it fails because it doesn't find them.
I tried to use the 'pg' gem to connect to a postgresql database. Then I proceeded to run sam build and sam local invoke HelloWorldFunction --event events/event.json, which failed with the next error:
Invoking app.lambda_handler (ruby2.7)
Failed to download a new amazon/aws-sam-cli-emulation-image-ruby2.7:rapid-1.0.0 image.
Invoking with the already downloaded image.
Mounting /home/user/sam-app/.aws-sam/build/HelloWorldFunction as /var/task:ro,delegated
inside runtime container
Init error when loading handler app.lambda_handler
{
"errorMessage": "libpq.so.5: cannot open shared object file: No such file or directory - /var/task/vendor/bundle/ruby/2.7.0/gems/pg-1.2.3/lib/pg_ext.so",
"errorType": "Init<LoadError>",
"stackTrace": [
"/var/lang/lib/ruby/site_ruby/2.7.0/rubygems/core_ext/kernel_require.rb:92:in `require'",
"/var/lang/lib/ruby/site_ruby/2.7.0/rubygems/core_ext/kernel_require.rb:92:in `require'",
"/var/task/vendor/bundle/ruby/2.7.0/gems/pg-1.2.3/lib/pg.rb:5:in `<top (required)>'",
"/var/lang/lib/ruby/site_ruby/2.7.0/rubygems/core_ext/kernel_require.rb:92:in `require'",
"/var/lang/lib/ruby/site_ruby/2.7.0/rubygems/core_ext/kernel_require.rb:92:in `require'",
"/var/task/app.rb:3:in `<top (required)>'",
"/var/lang/lib/ruby/site_ruby/2.7.0/rubygems/core_ext/kernel_require.rb:92:in `require'",
"/var/lang/lib/ruby/site_ruby/2.7.0/rubygems/core_ext/kernel_require.rb:92:in `require'"
]
}
Next thing I tried was to execute sam build --use-container and got a Gem::Ext::BuildError with the message ERROR: Failed to build gem native extension.
It seems that the external libraries that the gem needs are not being included.
My questions are:
How can I make this possible using AWS SAM? And what would be the best approach for this?
How can I solve this problem for testing the lambda locally and also leave it working in the productive environment?
I read something about using Layers for this but I don't fully understand, as it's my first time working with lambda functions.
It also seems that the sam proyect has some open issues about solving this, but it won't be in the near future.
Any help will be very much appreciated!
Thank you!
Layers in Lambda basically give you a way of installing dependencies that aren't related directly to your function but are used by it. It is a really great way to support re-use and to speed up build and deploy times. The idea here is that you would install common or shared dependencies like the postgresql libraries into the layer, then just reference them from your function.
In short, they give you a way to place things on the filesystem that the lambda can access at runtime.
However, there are a few interlinked issues in this case:
the pg gem requires packages to be installed on the OS and files to be available at build-time
the pg gem does not install the dependencies itself
This means that using layers is a little less simple than would be ideal.
For your issue, try the following:
Create a new directory called pg_layer
In that directory, create a file called Makefile (the case is important) with the following content:
LIB_DIR = $(ARTIFACTS_DIR)/lib
LIB_FILES = \
/usr/lib64/libpq.so.* \
/usr/lib64/libldap_r-2.4.so.2* \
/usr/lib64/liblber-2.4.so.* \
/usr/lib64/libsasl2.so.* \
/usr/lib64/libssl3.so \
/usr/lib64/libsmime3.so \
/usr/lib64/libnss3.so
build-PGLayer:
yum install -y amazon-linux-extras
yum install -y postgresql-devel postgresql-libs
mkdir -p $(ARTIFACTS_DIR)/vendor/bundle
mkdir -p $(LIB_DIR)
for f in $(LIB_FILES); do cp $$f $(LIB_DIR); done
cp -r . $(ARTIFACTS_DIR)
mkdir -p $(ARTIFACTS_DIR)/ruby/gems
gem install pg --install-dir $(ARTIFACTS_DIR)/ruby/gems/2.7.0
Alter your template.yaml file, and add the below to your Resources section:
PGLayer:
Type: AWS::Serverless::LayerVersion
Properties:
CompatibleRuntimes:
- ruby2.7
ContentUri: 'pg_layer'
Metadata:
BuildMethod: makefile
In the template.yaml file, alter your function definition to add the below to the Properties:
Layers:
- !Ref PGLayer
Remove pg from your function's Gemfile (it will be available automatically because of the layer and leaving it there will prevent you from building the function)
When you're ready to build, use the command sam build --use-container.
To run your lambda function locally with the layer, use sam local start-api.
When you're ready to deploy sam deploy will push up your entire application, including the layer, and configure the function to use the layer.
Related
I have a lambda functions that has somewhat non standard packaging. I am using a Makefile to help me package what I need and use it as my build method with sam build command. However I don't see this makefile being executed. Can't figure out why not.
Here is what I have :
sam_template.yaml:
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
subscriptions_functions
Sample SAM Template for subscriptions_functions
Globals:
Function:
Timeout: 3
Resources:
GetSubscriptionsFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: .
Handler: app.lambda_handler_individual_methods
Runtime: python3.7
Events:
GetSubscriptions:
Type: Api
Properties:
Path: /subscriptions
Method: get
Metadata:
BuildMethod: makefile
Environment:
Variables:
SERVICE_METHOD_NAME: 'xyz'
REQ_CLASS_NAME: 'xyz'
RES_CLASS_NAME: 'xyz'
Makefile: (the name is based on some AWS examples)
build-GetSubscriptionsFunction:
#echo "Buliding artifacts with sls. Destination dir " $(ARTIFACTS_DIR)
sls package --env aws
mkdir -p $(ARTIFACTS_DIR)
unzip .serverless/subscriptions.zip -d $(ARTIFACTS_DIR)
cp requirements.txt $(ARTIFACTS_DIR)
python -m pip install -r requirements.txt -t $(ARTIFACTS_DIR)
rm -rf $(ARTIFACTS_DIR)/bin
Build succeeded when I run sam build -t sam_template.yaml , but I can tell the Makefile didn't run (no messages printed out and it would create a .serverless directory, but it didn't)
Anyone has an idea what is wrong in this setup?
so I figured it out and it wasn't anything to do with the syntax.
I was running from IntelliJ terminal. Since I was hitting a wall with this one, I started pocking around and running few other SAM commands. Running sam validate also kept failing, but with an error pointing to unset default region.
My region was properly set in both .aws/config and I even tried to export an env variable AWS_DEFAULT_REGION , but nothing worked. It kept failing with unset region.
So I started looking at my plugins in IntelliJ and turns out I had both AWS Toolkit and Debugger for AWS Lambda (by Thundera) installed.
I uninstalled the later and I'm back in business. Not clear on why this plugin was interfering with my console and cli, but it did. Getting rid of it did the trick
I have a Ruby script that I want to run when a pull-request is created. This pull-request validates a series of conditions to be sure the pull-request can be merged. It's a very simple script with no external gems, just standard Ruby.
I'm trying to run this script on a job on a run step. The problem is, I'm not sure the path where the file should be saved.
The script is called: validator.rb. From my local computer I can run the script using:
ruby -r ./validator.rb -e "Validator.new.validate_something 'One parameter'"
This works fine locally but when I push this to GitHub it is failing. I saved my script as .github/workflows/ruby-scripts so my job looks like this:
jobs:
title:
name: "Title"
runs-on: ubuntu-latest
steps:
- run: ruby -r ./ruby-scripts/validator.rb -e "Validator.new.validate_something '${{ github.event.pull_request.title}}'"
And I get:
Run ruby -r ./ruby-scripts/validator.rb -e "Validator.new.validate 'Create README.md'"
/usr/lib/ruby/vendor_ruby/rubygems/defaults/operating_system.rb:10: warning: constant Gem::ConfigMap is deprecated
/usr/lib/ruby/vendor_ruby/rubygems/defaults/operating_system.rb:10: warning: constant Gem::ConfigMap is deprecated
/usr/lib/ruby/vendor_ruby/rubygems/defaults/operating_system.rb:29: warning: constant Gem::ConfigMap is deprecated
/usr/lib/ruby/vendor_ruby/rubygems/defaults/operating_system.rb:30: warning: constant Gem::ConfigMap is deprecated
/usr/lib/ruby/vendor_ruby/rubygems/defaults/operating_system.rb:10: warning: constant Gem::ConfigMap is deprecated
/usr/local/lib/site_ruby/2.5.0/rubygems/core_ext/kernel_require.rb:92:in `require': cannot load such file -- ./ruby-scripts/validator.rb (LoadError)
from /usr/local/lib/site_ruby/2.5.0/rubygems/core_ext/kernel_require.rb:92:in `require'
##[error]Process completed with exit code 1.
I tried with all the possible combination of paths and it fails each time.
Running pwd and ls returned:
- run: pwd => /home/runner/work/repo-name/repo-name
- run: ls => shell: /bin/bash -e {0}
What is the right way to do this?
As I've mentioned in the comments, the reason why your workflow isn't working is that you forgot the crucial step that checks-out your repository. By default, the workspace is empty unless you check out the repository with the Checkout GitHub Action.
From the GitHub Action's README:
This action checks-out your repository under $GITHUB_WORKSPACE, so your workflow can access it.
Here's an example:
- name: Checkout Repo
uses: actions/checkout#v2
(That's it, really)
(Note: You don't have to specify a name for the step.)
For me, the problem was also the location of the file. The ruby script was running in the root of the repository, not in the same path as the workflow YAML (as I was expecting).
I was running run: ruby -r ./my-file.rb, where my-file.rb was next to the workflow yaml.
I realized this by adding this step:
- run: ruby -e 'p `ls`.split("\n")'
Which printed the output of ls in a ruby array.
I fixed using:
run: ruby ./.github/workflows/my_file.rb
I m trying to use travis CI with nodejs and I m facing a problem like this one.
What I want here, is only to use ftp to upload my file, not to run any command.
No Rakefile found (looking for: rakefile, Rakefile, rakefile.rb, Rakefile.rb)
I dont know what is the problem actually... Here's my travis.yml file :
env:
global:
- "FTP_USER=xxx"
- "FTP_PASSWORD=xxx"
after_success:
"curl --ftp-create-dirs -T uploadfilename -u $FTP_USER:$FTP_PASSWORD ftp://xxxx.fr/www/mochatest"
What am I doing wrong ?
I faced similar scenario when I decided to use Travis-CI with HTML+JS app. I finally ended up deployment from my package.json file via "posttest" section. I wrote some code which uses ftp-deploy npm module to upload the files. Test repository here.
I am new to Ruby/Calabash and managed to set a dedicated calabash automation framework for ios with a page object model pattern and its running successfully.
I want to extend the same framework for android too. I created a dedicated folder for ios and android inside features folder and thought of having their respective page objects inside those folder.
But when I ran calabash-android, calabash finds a similar page class exists in ios folder and started throwing the error message. I want to follow same naming convention for ios and android pages without having this name-clash. Is it possible?
superclass mismatch for class AuthenticationPage (TypeError)
/Users/MACUSER/Documents/Automation/features/ios/pages/authentication_page. rb:5:in `<top (required)>'
/Library/Ruby/Gems/2.0.0/gems/cucumber-1.3.18/lib/cucumber/rb_support/rb_language.rb:95:in `load'
/Library/Ruby/Gems/2.0.0/gems/cucumber-1.3.18/lib/cucumber/rb_support/rb_language.rb:95:in `load_code_file'
/Library/Ruby/Gems/2.0.0/gems/cucumber-1.3.18/lib/cucumber/runtime/support_code.rb:180:in `load_file'
/Library/Ruby/Gems/2.0.0/gems/cucumber-1.3.18/lib/cucumber/runtime/support_code.rb:83:in `block in load_files!'
/Library/Ruby/Gems/2.0.0/gems/cucumber-1.3.18/lib/cucumber/runtime/support_code.rb:82:in `each'
/Library/Ruby/Gems/2.0.0/gems/cucumber-1.3.18/lib/cucumber/runtime/support_code.rb:82:in `load_files!'
/Library/Ruby/Gems/2.0.0/gems/cucumber-1.3.18/lib/cucumber/runtime.rb:184:in `load_step_definitions'
/Library/Ruby/Gems/2.0.0/gems/cucumber-1.3.18/lib/cucumber/runtime.rb:42:in `run!'
/Library/Ruby/Gems/2.0.0/gems/cucumber-1.3.18/lib/cucumber/cli/main.rb:47:in `execute!'
/Library/Ruby/Gems/2.0.0/gems/cucumber-1.3.18/bin/cucumber:13:in `<top (required)>'
/usr/bin/cucumber:23:in `load'
/usr/bin/cucumber:23:in `<main>'
Based on your description of the issue, it is not clear what the problem is.
I think it would help if you added more details about your folder structures and files.
But as you have not mentioned profiles as all I am suspecting that you are not using an .yml file.
When you execute your tests you should define what profile you are running and have one for iOS and one for Android. For each profile you will define what folders to include.
Like this
android: PLATFORM=android RESET_BETWEEN_SCENARIOS=1 -r features/support -r features/android/support -r features/android/helpers -r features/step_definitions -r features/android/pages/
And then when you execute the tests you define for what profile
calabash-android run path_to.apk -p android features/login.feature
If you have not already you should look at either Xamarin cross-platform tutorial or on the Github page for same
had similar problem, solved by adding exclude option "--exclude ios" to android profile in config/cucumber.yml file (and "--exclude android" for ios respectively)
---
android: PLATFORM=android --exclude ios -r features/support -r features/android -r features/step_definitions -r features/android/pages
ios: PLATFORM=ios APP_BUNDLE_PATH=path_to_your.app --exclude android -r features/support -r features/ios/support -r features/ios/helpers -r features/step_definitions -r features/ios/pages
seems to be cucumber bug, because according to cucumber docs -r switch should prevent loading all files except those specified explicitly
-r, --require LIBRARY|DIR Require files before executing the features. If this
option is not specified, all *.rb files that are
siblings or below the features will be loaded auto-
matically. Automatic loading is disabled when this
option is specified, and all loading becomes explicit.
Files under directories named "support" are always
loaded first.
...
-e, --exclude PATTERN Don't run feature files or require ruby files matching PATTERN
Xamarin is saying you should give the profile and the config in the command --profile ios --config=config/cucumber.yml. See this:
test-cloud submit prebuilt/Moda-cal.ipa 93dbwrmwrb0d65099640f23 --devices 99dwdhw846 --series "ip7" --locale "en_US" --app-name "Moda" --user gunesmes#gmail.com --profile ios --config=config/cucumber.yml
test-cloud submit prebuilt/Moda.apk 93dbwrmwrb06sfu440f23 --devices 9933nb846 --series "nex" --locale "en_US" --app-name "Moda" --user gunesmes#gmail.com --profile android --config=config/cucumber.yml
My gem uses a local copy of Tidy HTML Validator. It's available in lib/tidy/tidy. My tests run perfectly locally, but on Travis CI, I'm getting the error that Tidy can't be found.
Errno::ENOENT:
No such file or directory - tidy
The code looks like this:
stdin, stdout, stderr = Open3.popen3('tidy -quiet')
Is there a restriction or something that prevents executing a local executable? I also call a local copy of the Jigsaw CSS Validator which is a Java jar file and is executed like this:
`java -jar css-validator.jar --output=soap12 file:#{uri}`
This works perfectly on Travis CI. Could there be a problem with Open3?
Here's the link to the GitHub repo: https://github.com/jmuheim/headhunter
Here's the link to the failing Travis CI build: https://travis-ci.org/jmuheim/headhunter
Update
I have rewritten the code that executes the external java executable to also use Open3, and it's working. So the problem has to do with tidy.
For further debugging I have renamed tidy into tidy2 (to make sure that really the wanted executable is called), and I added a manual File.exist? 'tidy' before calling it, so this would raise an error if it couldn't be found.
Dir.chdir(VALIDATOR_DIR) do
raise "Could not find tidy in #{Dir.pwd}" unless File.exists? EXECUTABLE
# Docs for Tidy: http://tidy.sourceforge.net/docs/quickref.html
stdin, stdout, stderr = Open3.popen3("#{EXECUTABLE} -quiet")
# ...
end
The file is definitely found, but executing it still results in this error.
1) Headhunter::HtmlValidator#validate returns a local response when calling the validator succeeds
Failure/Error: subject.validate('invalid.html', read_file('html_validator/invalid.html'))
Errno::ENOENT:
No such file or directory - tidy2
# ./lib/headhunter/html_validator.rb:20:in `block in validate'
# ./lib/headhunter/html_validator.rb:16:in `chdir'
# ./lib/headhunter/html_validator.rb:16:in `validate'
# ./spec/lib/headhunter/html_validator_spec.rb:9:in `block (4 levels) in <top (required)>'
# ./spec/lib/headhunter/html_validator_spec.rb:8:in `block (3 levels) in <top (required)>'
So the only thing I can guess now is that be there is a permissions problem? Is there a way to set permissions to local executables with Travis CI?
I also tried putting tidy into bin like somebody suggested in the comments, but this didn't seem to change a thing, too.
It looks like your tidy2 executable is a Mach-O binary for OS X, but the Travis worker runs Linux. Try to include a suitable ELF binary from http://tidy.sourceforge.net/#binaries.
A couple things:
Typically the environment is different than your current environment when spawning programs so you have to check what the environment is. This is the case in cron, monit, and for some ruby method calls.
In your case, for Open, it uses Process.spawn so you're not guaranteed to be in your own directory. Open.popen3 takes a :chdir option, so set that to your Dir.pwd and see what happens.
You can also try just using absolute paths.
Sources
http://ruby-doc.org/stdlib-1.9.3/libdoc/open3/rdoc/Open3.html#method-c-popen3
https://stackoverflow.com/a/5917927/463225
http://ruby-doc.org/core-1.9.3/Process.html#method-c-spawn