diff --git a/.evergreen/atlas b/.evergreen/atlas new file mode 120000 index 0000000000..ef871b9a28 --- /dev/null +++ b/.evergreen/atlas @@ -0,0 +1 @@ +../.mod/drivers-evergreen-tools/.evergreen/atlas \ No newline at end of file diff --git a/.evergreen/config.yml b/.evergreen/config.yml index 76631a375a..e41d305254 100644 --- a/.evergreen/config.yml +++ b/.evergreen/config.yml @@ -482,6 +482,8 @@ task_groups: script: | ${PREPARE_SHELL} + echo "Setting up Atlas cluster" + DRIVERS_ATLAS_PUBLIC_API_KEY="${DRIVERS_ATLAS_PUBLIC_API_KEY}" \ DRIVERS_ATLAS_PRIVATE_API_KEY="${DRIVERS_ATLAS_PRIVATE_API_KEY}" \ DRIVERS_ATLAS_GROUP_ID="${DRIVERS_ATLAS_GROUP_ID}" \ @@ -492,6 +494,8 @@ task_groups: task_id="${task_id}" \ execution="${execution}" \ $DRIVERS_TOOLS/.evergreen/atlas/setup-atlas-cluster.sh + + echo "MONGODB_URI=${MONGODB_URI}" - command: expansions.update params: file: src/atlas-expansion.yml @@ -513,6 +517,52 @@ task_groups: tasks: - test-full-atlas-task + - name: test_aws_lambda_task_group + setup_group_can_fail_task: true + setup_group_timeout_secs: 1800 # 30 minutes + setup_group: + - func: fetch source + - func: create expansions + - command: shell.exec + params: + shell: "bash" + working_dir: "src" + script: | + ${PREPARE_SHELL} + + echo "Setting up Atlas cluster" + + DRIVERS_ATLAS_PUBLIC_API_KEY="${DRIVERS_ATLAS_PUBLIC_API_KEY}" \ + DRIVERS_ATLAS_PRIVATE_API_KEY="${DRIVERS_ATLAS_PRIVATE_API_KEY}" \ + DRIVERS_ATLAS_GROUP_ID="${DRIVERS_ATLAS_GROUP_ID}" \ + DRIVERS_ATLAS_LAMBDA_USER="${DRIVERS_ATLAS_LAMBDA_USER}" \ + DRIVERS_ATLAS_LAMBDA_PASSWORD="${DRIVERS_ATLAS_LAMBDA_PASSWORD}" \ + LAMBDA_STACK_NAME="dbx-ruby-lambda" \ + MONGODB_VERSION="7.0" \ + task_id="${task_id}" \ + execution="${execution}" \ + $DRIVERS_TOOLS/.evergreen/atlas/setup-atlas-cluster.sh + - command: expansions.update + params: + file: src/atlas-expansion.yml + teardown_group: + - command: shell.exec + params: + shell: "bash" + working_dir: "src" + script: | + ${PREPARE_SHELL} + + DRIVERS_ATLAS_PUBLIC_API_KEY="${DRIVERS_ATLAS_PUBLIC_API_KEY}" \ + DRIVERS_ATLAS_PRIVATE_API_KEY="${DRIVERS_ATLAS_PRIVATE_API_KEY}" \ + DRIVERS_ATLAS_GROUP_ID="${DRIVERS_ATLAS_GROUP_ID}" \ + LAMBDA_STACK_NAME="dbx-ruby-lambda" \ + task_id="${task_id}" \ + execution="${execution}" \ + $DRIVERS_TOOLS/.evergreen/atlas/teardown-atlas-cluster.sh + tasks: + - test-aws-lambda-deployed + - name: testgcpkms_task_group setup_group_can_fail_task: true setup_group_timeout_secs: 1800 # 30 minutes @@ -702,6 +752,38 @@ tasks: export AZUREKMS_VMNAME=${AZUREKMS_VMNAME} export AZUREKMS_PRIVATEKEYPATH="/tmp/testazurekms_private_key_file" AZUREKMS_CMD="TEST_FLE_AZURE_AUTO=1 RVM_RUBY=ruby-3.1 FLE=helper TOPOLOGY=standalone MONGODB_VERSION=6.0 MONGO_RUBY_DRIVER_AZURE_TENANT_ID="${MONGO_RUBY_DRIVER_AZURE_TENANT_ID}" MONGO_RUBY_DRIVER_AZURE_CLIENT_ID="${MONGO_RUBY_DRIVER_AZURE_CLIENT_ID}" MONGO_RUBY_DRIVER_AZURE_CLIENT_SECRET="${MONGO_RUBY_DRIVER_AZURE_CLIENT_SECRET}" MONGO_RUBY_DRIVER_AZURE_IDENTITY_PLATFORM_ENDPOINT="${MONGO_RUBY_DRIVER_AZURE_IDENTITY_PLATFORM_ENDPOINT}" MONGO_RUBY_DRIVER_AZURE_KEY_VAULT_ENDPOINT="${testazurekms_keyvaultendpoint}" MONGO_RUBY_DRIVER_AZURE_KEY_NAME="${testazurekms_keyname}" ./.evergreen/run-tests-azure.sh" .evergreen/csfle/azurekms/run-command.sh + + - name: "test-aws-lambda-deployed" + commands: + - command: ec2.assume_role + params: + role_arn: ${LAMBDA_AWS_ROLE_ARN} + duration_seconds: 3600 + - command: shell.exec + type: test + params: + working_dir: "src" + shell: "bash" + script: | + ${PREPARE_SHELL} + export MONGODB_URI=${MONGODB_URI} + .evergreen/run-tests-deployed-lambda.sh + env: + TEST_LAMBDA_DIRECTORY: ${PROJECT_DIRECTORY}/spec/faas/ruby-sam-app + AWS_REGION: us-east-1 + PROJECT_DIRECTORY: ${PROJECT_DIRECTORY} + DRIVERS_TOOLS: ${DRIVERS_TOOLS} + DRIVERS_ATLAS_PUBLIC_API_KEY: ${DRIVERS_ATLAS_PUBLIC_API_KEY} + DRIVERS_ATLAS_PRIVATE_API_KEY: ${DRIVERS_ATLAS_PRIVATE_API_KEY} + DRIVERS_ATLAS_LAMBDA_USER: ${DRIVERS_ATLAS_LAMBDA_USER} + DRIVERS_ATLAS_LAMBDA_PASSWORD: ${DRIVERS_ATLAS_LAMBDA_PASSWORD} + DRIVERS_ATLAS_GROUP_ID: ${DRIVERS_ATLAS_GROUP_ID} + AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID} + AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY} + AWS_SESSION_TOKEN: ${AWS_SESSION_TOKEN} + LAMBDA_STACK_NAME: "dbx-ruby-lambda" + RVM_RUBY: ruby-3.2 + MONGODB_URI: ${MONGODB_URI} axes: - id: preload @@ -1689,3 +1771,11 @@ buildvariants: display_name: "Atlas serverless ${ruby} single mongos" tasks: - name: test-serverless + + - matrix_name: "aws-lambda" + matrix_spec: + ruby: "ruby-3.2" + os: ubuntu2204 + display_name: "AWS Lambda" + tasks: + - name: test_aws_lambda_task_group diff --git a/.evergreen/config/common.yml.erb b/.evergreen/config/common.yml.erb index e78f08d846..90e8def128 100644 --- a/.evergreen/config/common.yml.erb +++ b/.evergreen/config/common.yml.erb @@ -479,6 +479,8 @@ task_groups: script: | ${PREPARE_SHELL} + echo "Setting up Atlas cluster" + DRIVERS_ATLAS_PUBLIC_API_KEY="${DRIVERS_ATLAS_PUBLIC_API_KEY}" \ DRIVERS_ATLAS_PRIVATE_API_KEY="${DRIVERS_ATLAS_PRIVATE_API_KEY}" \ DRIVERS_ATLAS_GROUP_ID="${DRIVERS_ATLAS_GROUP_ID}" \ @@ -489,6 +491,8 @@ task_groups: task_id="${task_id}" \ execution="${execution}" \ $DRIVERS_TOOLS/.evergreen/atlas/setup-atlas-cluster.sh + + echo "MONGODB_URI=${MONGODB_URI}" - command: expansions.update params: file: src/atlas-expansion.yml @@ -510,6 +514,52 @@ task_groups: tasks: - test-full-atlas-task + - name: test_aws_lambda_task_group + setup_group_can_fail_task: true + setup_group_timeout_secs: 1800 # 30 minutes + setup_group: + - func: fetch source + - func: create expansions + - command: shell.exec + params: + shell: "bash" + working_dir: "src" + script: | + ${PREPARE_SHELL} + + echo "Setting up Atlas cluster" + + DRIVERS_ATLAS_PUBLIC_API_KEY="${DRIVERS_ATLAS_PUBLIC_API_KEY}" \ + DRIVERS_ATLAS_PRIVATE_API_KEY="${DRIVERS_ATLAS_PRIVATE_API_KEY}" \ + DRIVERS_ATLAS_GROUP_ID="${DRIVERS_ATLAS_GROUP_ID}" \ + DRIVERS_ATLAS_LAMBDA_USER="${DRIVERS_ATLAS_LAMBDA_USER}" \ + DRIVERS_ATLAS_LAMBDA_PASSWORD="${DRIVERS_ATLAS_LAMBDA_PASSWORD}" \ + LAMBDA_STACK_NAME="dbx-ruby-lambda" \ + MONGODB_VERSION="7.0" \ + task_id="${task_id}" \ + execution="${execution}" \ + $DRIVERS_TOOLS/.evergreen/atlas/setup-atlas-cluster.sh + - command: expansions.update + params: + file: src/atlas-expansion.yml + teardown_group: + - command: shell.exec + params: + shell: "bash" + working_dir: "src" + script: | + ${PREPARE_SHELL} + + DRIVERS_ATLAS_PUBLIC_API_KEY="${DRIVERS_ATLAS_PUBLIC_API_KEY}" \ + DRIVERS_ATLAS_PRIVATE_API_KEY="${DRIVERS_ATLAS_PRIVATE_API_KEY}" \ + DRIVERS_ATLAS_GROUP_ID="${DRIVERS_ATLAS_GROUP_ID}" \ + LAMBDA_STACK_NAME="dbx-ruby-lambda" \ + task_id="${task_id}" \ + execution="${execution}" \ + $DRIVERS_TOOLS/.evergreen/atlas/teardown-atlas-cluster.sh + tasks: + - test-aws-lambda-deployed + - name: testgcpkms_task_group setup_group_can_fail_task: true setup_group_timeout_secs: 1800 # 30 minutes @@ -699,3 +749,35 @@ tasks: export AZUREKMS_VMNAME=${AZUREKMS_VMNAME} export AZUREKMS_PRIVATEKEYPATH="/tmp/testazurekms_private_key_file" AZUREKMS_CMD="TEST_FLE_AZURE_AUTO=1 RVM_RUBY=ruby-3.1 FLE=helper TOPOLOGY=standalone MONGODB_VERSION=6.0 MONGO_RUBY_DRIVER_AZURE_TENANT_ID="${MONGO_RUBY_DRIVER_AZURE_TENANT_ID}" MONGO_RUBY_DRIVER_AZURE_CLIENT_ID="${MONGO_RUBY_DRIVER_AZURE_CLIENT_ID}" MONGO_RUBY_DRIVER_AZURE_CLIENT_SECRET="${MONGO_RUBY_DRIVER_AZURE_CLIENT_SECRET}" MONGO_RUBY_DRIVER_AZURE_IDENTITY_PLATFORM_ENDPOINT="${MONGO_RUBY_DRIVER_AZURE_IDENTITY_PLATFORM_ENDPOINT}" MONGO_RUBY_DRIVER_AZURE_KEY_VAULT_ENDPOINT="${testazurekms_keyvaultendpoint}" MONGO_RUBY_DRIVER_AZURE_KEY_NAME="${testazurekms_keyname}" ./.evergreen/run-tests-azure.sh" .evergreen/csfle/azurekms/run-command.sh + + - name: "test-aws-lambda-deployed" + commands: + - command: ec2.assume_role + params: + role_arn: ${LAMBDA_AWS_ROLE_ARN} + duration_seconds: 3600 + - command: shell.exec + type: test + params: + working_dir: "src" + shell: "bash" + script: | + ${PREPARE_SHELL} + export MONGODB_URI=${MONGODB_URI} + .evergreen/run-tests-deployed-lambda.sh + env: + TEST_LAMBDA_DIRECTORY: ${PROJECT_DIRECTORY}/spec/faas/ruby-sam-app + AWS_REGION: us-east-1 + PROJECT_DIRECTORY: ${PROJECT_DIRECTORY} + DRIVERS_TOOLS: ${DRIVERS_TOOLS} + DRIVERS_ATLAS_PUBLIC_API_KEY: ${DRIVERS_ATLAS_PUBLIC_API_KEY} + DRIVERS_ATLAS_PRIVATE_API_KEY: ${DRIVERS_ATLAS_PRIVATE_API_KEY} + DRIVERS_ATLAS_LAMBDA_USER: ${DRIVERS_ATLAS_LAMBDA_USER} + DRIVERS_ATLAS_LAMBDA_PASSWORD: ${DRIVERS_ATLAS_LAMBDA_PASSWORD} + DRIVERS_ATLAS_GROUP_ID: ${DRIVERS_ATLAS_GROUP_ID} + AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID} + AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY} + AWS_SESSION_TOKEN: ${AWS_SESSION_TOKEN} + LAMBDA_STACK_NAME: "dbx-ruby-lambda" + RVM_RUBY: ruby-3.2 + MONGODB_URI: ${MONGODB_URI} diff --git a/.evergreen/config/standard.yml.erb b/.evergreen/config/standard.yml.erb index 89cbc81ef1..aa7f68c59d 100644 --- a/.evergreen/config/standard.yml.erb +++ b/.evergreen/config/standard.yml.erb @@ -520,3 +520,11 @@ buildvariants: display_name: "Atlas serverless ${ruby} single mongos" tasks: - name: test-serverless + + - matrix_name: "aws-lambda" + matrix_spec: + ruby: <%= latest_ruby %> + os: ubuntu2204 + display_name: "AWS Lambda" + tasks: + - name: test_aws_lambda_task_group diff --git a/.evergreen/run-deployed-lambda-aws-tests.sh b/.evergreen/run-deployed-lambda-aws-tests.sh new file mode 100755 index 0000000000..32eebfbf2b --- /dev/null +++ b/.evergreen/run-deployed-lambda-aws-tests.sh @@ -0,0 +1,117 @@ +#!/bin/bash +set -o errexit # Exit the script with error if any of the commands fail + +# Explanation of required environment variables: +# +# TEST_LAMBDA_DIRECTORY: The root of the project's Lambda sam project. +# DRIVERS_ATLAS_PUBLIC_API_KEY: The public Atlas key for the drivers org. +# DRIVERS_ATLAS_PRIVATE_API_KEY: The private Atlas key for the drivers org. +# DRIVERS_ATLAS_LAMBDA_USER: The user for the lambda cluster. +# DRIVERS_ATLAS_LAMBDA_PASSWORD: The password for the user. +# DRIVERS_ATLAS_GROUP_ID: The id of the individual projects under the drivers org, per language. +# LAMBDA_STACK_NAME: The name of the stack on lambda "dbx--lambda" +# AWS_REGION: The region for the function - generally us-east-1 + +VARLIST=( +TEST_LAMBDA_DIRECTORY +DRIVERS_ATLAS_PUBLIC_API_KEY +DRIVERS_ATLAS_PRIVATE_API_KEY +DRIVERS_ATLAS_LAMBDA_USER +DRIVERS_ATLAS_LAMBDA_PASSWORD +DRIVERS_ATLAS_GROUP_ID +LAMBDA_STACK_NAME +AWS_REGION +) + +# Ensure that all variables required to run the test are set, otherwise throw +# an error. +for VARNAME in ${VARLIST[*]}; do +[[ -z "${!VARNAME}" ]] && echo "ERROR: $VARNAME not set" && exit 1; +done + +# Set up the common variables +. `dirname "$0"`/atlas/setup-variables.sh + +# Restarts the cluster's primary node. +restart_cluster_primary () +{ + echo "Testing Atlas primary restart..." + curl \ + --digest -u ${DRIVERS_ATLAS_PUBLIC_API_KEY}:${DRIVERS_ATLAS_PRIVATE_API_KEY} \ + -X POST \ + "${ATLAS_BASE_URL}/groups/${DRIVERS_ATLAS_GROUP_ID}/clusters/${FUNCTION_NAME}/restartPrimaries" +} + +# Deploys a lambda function to the set stack name. +deploy_lambda_function () +{ + echo "Deploying Lambda function..." + sam deploy \ + --stack-name "${FUNCTION_NAME}" \ + --capabilities CAPABILITY_IAM \ + --resolve-s3 \ + --parameter-overrides "MongoDbUri=${MONGODB_URI}" \ + --region ${AWS_REGION} +} + +# Get the ARN for the Lambda function we created and export it. +get_lambda_function_arn () +{ + echo "Getting Lambda function ARN..." + LAMBDA_FUNCTION_ARN=$(sam list stack-outputs \ + --stack-name ${FUNCTION_NAME} \ + --region ${AWS_REGION} \ + --output json | jq '.[] | select(.OutputKey == "MongoDBFunction") | .OutputValue' | tr -d '"' + ) + echo "Lambda function ARN: $LAMBDA_FUNCTION_ARN" + export LAMBDA_FUNCTION_ARN=$LAMBDA_FUNCTION_ARN +} + +delete_lambda_function () +{ + echo "Deleting Lambda Function..." + sam delete --stack-name ${FUNCTION_NAME} --no-prompts --region us-east-1 +} + +cleanup () +{ + delete_lambda_function +} + +trap cleanup EXIT SIGHUP + +cd "${TEST_LAMBDA_DIRECTORY}" + +sam build --use-container + +deploy_lambda_function + +get_lambda_function_arn + + +check_lambda_output () { + if grep -q FunctionError output.json + then + echo "Exiting due to FunctionError!" + exit 1 + fi + cat output.json | jq -r '.LogResult' | base64 --decode +} + +aws lambda invoke --function-name ${LAMBDA_FUNCTION_ARN} --log-type Tail lambda-invoke-standard.json > output.json +cat lambda-invoke-standard.json +check_lambda_output + +echo "Sleeping 1 minute to build up some streaming protocol heartbeats..." +sleep 60 +aws lambda invoke --function-name ${LAMBDA_FUNCTION_ARN} --log-type Tail lambda-invoke-frozen.json > output.json +cat lambda-invoke-frozen.json +check_lambda_output + +restart_cluster_primary + +echo "Sleeping 1 minute to build up some streaming protocol heartbeats..." +sleep 60 +aws lambda invoke --function-name ${LAMBDA_FUNCTION_ARN} --log-type Tail lambda-invoke-outage.json > output.json +cat lambda-invoke-outage.json +check_lambda_output diff --git a/.evergreen/run-tests-deployed-lambda.sh b/.evergreen/run-tests-deployed-lambda.sh new file mode 100755 index 0000000000..9b5d01d526 --- /dev/null +++ b/.evergreen/run-tests-deployed-lambda.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +set -ex + +. `dirname "$0"`/../spec/shared/shlib/distro.sh +. `dirname "$0"`/../spec/shared/shlib/set_env.sh +. `dirname "$0"`/functions.sh + +set_env_vars +set_env_python +set_env_ruby + +export MONGODB_URI=${MONGODB_URI} +export TEST_LAMBDA_DIRECTORY=`dirname "$0"`/../spec/faas/ruby-sam-app + +. `dirname "$0"`/run-deployed-lambda-aws-tests.sh diff --git a/.gitignore b/.gitignore index 24a54bc470..f1acca9738 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,6 @@ profile/benchmarking/data secrets-export.sh secrets-expansion.yml atlas-expansion.yml +# AWS SAM-generated files +spec/faas/ruby-sam-app/.aws-sam +spec/faas/ruby-sam-app/events/event.json diff --git a/.mod/drivers-evergreen-tools b/.mod/drivers-evergreen-tools index 1f018c7a24..c3335b46e7 160000 --- a/.mod/drivers-evergreen-tools +++ b/.mod/drivers-evergreen-tools @@ -1 +1 @@ -Subproject commit 1f018c7a248c4fcda6cb7a77043fd673755e0986 +Subproject commit c3335b46e7190beeab6b65eb9a76bd0e09f69903 diff --git a/.rubocop.yml b/.rubocop.yml index 788bd56daf..c21c088e8c 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -8,6 +8,7 @@ AllCops: NewCops: enable Exclude: - 'spec/shared/**/*' + - 'spec/faas/**/*' - 'vendor/**/*' Bundler: diff --git a/spec/faas/ruby-sam-app/.gitignore b/spec/faas/ruby-sam-app/.gitignore new file mode 100644 index 0000000000..4bccb52c85 --- /dev/null +++ b/spec/faas/ruby-sam-app/.gitignore @@ -0,0 +1,345 @@ + +# Created by https://www.toptal.com/developers/gitignore/api/osx,linux,python,windows,pycharm,visualstudiocode,sam +# Edit at https://www.toptal.com/developers/gitignore?templates=osx,linux,python,windows,pycharm,visualstudiocode,sam + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### OSX ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### PyCharm ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### PyCharm Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint +.idea/**/sonarlint/ + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin +.idea/**/sonarIssues.xml + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced +.idea/**/markdown-navigator.xml +.idea/**/markdown-navigator-enh.xml +.idea/**/markdown-navigator/ + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 +.idea/$CACHE_FILE$ + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream +.idea/codestream.xml + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +pytestdebug.log + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ +doc/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +#poetry.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +# .env +.env/ +.venv/ +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ +pythonenv* + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# operating system-related files +# file properties cache/storage on macOS +*.DS_Store +# thumbnail cache on Windows +Thumbs.db + +# profiling data +.prof + + +### SAM ### +# Ignore build directories for the AWS Serverless Application Model (SAM) +# Info: https://aws.amazon.com/serverless/sam/ +# Docs: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-reference.html + +**/.aws-sam + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +### Windows ### +# Windows thumbnail cache files +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/osx,linux,python,windows,pycharm,visualstudiocode,sam diff --git a/spec/faas/ruby-sam-app/Gemfile b/spec/faas/ruby-sam-app/Gemfile new file mode 100644 index 0000000000..64004bea4c --- /dev/null +++ b/spec/faas/ruby-sam-app/Gemfile @@ -0,0 +1,9 @@ +source "https://rubygems.org" + +gem "httparty" +gem "mongo" + +group :test do + gem "test-unit" + gem "mocha" +end diff --git a/spec/faas/ruby-sam-app/mongodb/Gemfile b/spec/faas/ruby-sam-app/mongodb/Gemfile new file mode 100644 index 0000000000..8eee1f8e03 --- /dev/null +++ b/spec/faas/ruby-sam-app/mongodb/Gemfile @@ -0,0 +1,4 @@ +source "https://rubygems.org" + +gem "httparty" +gem "mongo" diff --git a/spec/faas/ruby-sam-app/mongodb/app.rb b/spec/faas/ruby-sam-app/mongodb/app.rb new file mode 100644 index 0000000000..0eb7e9e46a --- /dev/null +++ b/spec/faas/ruby-sam-app/mongodb/app.rb @@ -0,0 +1,149 @@ +# frozen_string_literal: true + +require 'mongo' +require 'json' + +class StatsAggregator + + def initialize + @open_connections = 0 + @heartbeats_count = 0 + @total_heartbeat_time = 0 + @commands_count = 0 + @total_command_time = 0 + end + + def add_command(duration) + @commands_count += 1 + @total_command_time += duration + end + + def add_heartbeat(duration) + @heartbeats_count += 1 + @total_heartbeat_time += duration + end + + def add_connection + @open_connections += 1 + end + + def remove_connection + @open_connections -= 1 + end + + def average_heartbeat_time + if @heartbeats_count == 0 + 0 + else + @total_heartbeat_time / @heartbeats_count + end + end + + def average_command_time + if @commands_count == 0 + 0 + else + @total_command_time / @commands_count + end + end + + def reset + @open_connections = 0 + @heartbeats_count = 0 + @total_heartbeat_time = 0 + @commands_count = 0 + @total_command_time = 0 + end + + def result + { + average_heartbeat_time: average_heartbeat_time, + average_command_time: average_command_time, + heartbeats_count: @heartbeats_count, + open_connections: @open_connections, + } + end +end + +class CommandMonitor + + def initialize(stats_aggregator) + @stats_aggregator = stats_aggregator + end + + def started(event); end + + def failed(event) + @stats_aggregator.add_command(event.duration) + end + + def succeeded(event) + @stats_aggregator.add_command(event.duration) + end +end + +class HeartbeatMonitor + + def initialize(stats_aggregator) + @stats_aggregator = stats_aggregator + end + + def started(event); end + + def succeeded(event) + @stats_aggregator.add_heartbeat(event.duration) + end + + def failed(event) + @stats_aggregator.add_heartbeat(event.duration) + end +end + +class PoolMonitor + + def initialize(stats_aggregator) + @stats_aggregator = stats_aggregator + end + + def published(event) + case event + when Mongo::Monitoring::Event::Cmap::ConnectionCreated + @stats_aggregator.add_connection + when Mongo::Monitoring::Event::Cmap::ConnectionClosed + @stats_aggregator.remove_connection + end + end +end + +$stats_aggregator = StatsAggregator.new + +command_monitor = CommandMonitor.new($stats_aggregator) +heartbeat_monitor = HeartbeatMonitor.new($stats_aggregator) +pool_monitor = PoolMonitor.new($stats_aggregator) + +sdam_proc = proc do |client| + client.subscribe(Mongo::Monitoring::COMMAND, command_monitor) + client.subscribe(Mongo::Monitoring::SERVER_HEARTBEAT, heartbeat_monitor) + client.subscribe(Mongo::Monitoring::CONNECTION_POOL, pool_monitor) +end + +puts 'Connecting' +$client = Mongo::Client.new(ENV['MONGODB_URI'], sdam_proc: sdam_proc) +# Populate the connection pool +$client.use('lambda_test').database.list_collections +puts 'Connected' + +def lambda_handler(event:, context:) + db = $client.use('lambda_test') + collection = db[:test_collection] + result = collection.insert_one({ name: 'test' }) + collection.delete_one({ _id: result.inserted_id }) + response = $stats_aggregator.result.to_json + $stats_aggregator.reset + puts "Response: #{response}" + + { + statusCode: 200, + body: response + } +end diff --git a/spec/faas/ruby-sam-app/template.yaml b/spec/faas/ruby-sam-app/template.yaml new file mode 100644 index 0000000000..c42df95e3b --- /dev/null +++ b/spec/faas/ruby-sam-app/template.yaml @@ -0,0 +1,48 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + Sample SAM Template for ruby-sam-app + +# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst +Globals: + Function: + Timeout: 30 + MemorySize: 128 + +Parameters: + MongoDbUri: + Type: String + Description: The MongoDB connection string. + +Resources: + MongoDBFunction: + Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction + Properties: + CodeUri: mongodb/ + Environment: + Variables: + MONGODB_URI: !Ref MongoDbUri + Handler: app.lambda_handler + Runtime: ruby3.2 + Architectures: + - x86_64 + Events: + MongoDB: + Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api + Properties: + Path: /mongodb + Method: get + +Outputs: + # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function + # Find out more about other implicit resources you can reference within SAM + # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api + MongoDBApi: + Description: "API Gateway endpoint URL for Prod stage for MongoDB function" + Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/mongodb/" + MongoDBFunction: + Description: "MongoDB Lambda Function ARN" + Value: !GetAtt MongoDBFunction.Arn + MongoDBFunctionIamRole: + Description: "Implicit IAM Role created for MongoDB function" + Value: !GetAtt MongoDBFunctionRole.Arn