diff --git a/Makefile b/Makefile
index 37a97ae4..8f0a42ff 100644
--- a/Makefile
+++ b/Makefile
@@ -34,14 +34,14 @@ NOTICE.txt: go.mod
@bash ./scripts/notice.sh
check-licenses:
- go run github.com/elastic/go-licenser@v0.4.0 -d -exclude tf .
- go run github.com/elastic/go-licenser@v0.4.0 -d -exclude tf -ext .java .
- go run github.com/elastic/go-licenser@v0.4.0 -d -exclude tf -ext .js .
+ go run github.com/elastic/go-licenser@v0.4.0 -d -exclude tf -exclude testing .
+ go run github.com/elastic/go-licenser@v0.4.0 -d -exclude tf -exclude testing -ext .java .
+ go run github.com/elastic/go-licenser@v0.4.0 -d -exclude tf -exclude testing -ext .js .
update-licenses:
- go run github.com/elastic/go-licenser@v0.4.0 -exclude tf .
- go run github.com/elastic/go-licenser@v0.4.0 -exclude tf -ext .java .
- go run github.com/elastic/go-licenser@v0.4.0 -exclude tf -ext .js .
+ go run github.com/elastic/go-licenser@v0.4.0 -exclude tf -exclude testing .
+ go run github.com/elastic/go-licenser@v0.4.0 -exclude tf -exclude testing -ext .java .
+ go run github.com/elastic/go-licenser@v0.4.0 -exclude tf -exclude testing -ext .js .
lint:
go run github.com/golangci/golangci-lint/cmd/golangci-lint@v1.48.0 version
diff --git a/testing/.gitignore b/testing/.gitignore
new file mode 100644
index 00000000..7fc45aa7
--- /dev/null
+++ b/testing/.gitignore
@@ -0,0 +1,9 @@
+**/package
+main
+build
+.terraform
+.terraform.lock*
+.terraform.tfstate*
+terraform.tfstate
+terraform.tfstate.backup
+terraform.tfvars
diff --git a/testing/Makefile b/testing/Makefile
new file mode 100644
index 00000000..5554d2a7
--- /dev/null
+++ b/testing/Makefile
@@ -0,0 +1,43 @@
+USER_NAME?=$(USER)
+AWS_REGION?=us-west-2
+MACHINE_TYPE?=t2.medium
+LOAD_DURATION?=10
+LOAD_ARRIVAL_RATE?=50
+LAMBDA_RUNTIME?=python3.8
+LAMBDA_TIMEOUT?=15
+LAMBDA_RUNTIME_NORM=$(subst '.','_',$(LAMBDA_RUNTIME))
+
+# TODO: @lahsivjar Add automation for terraform fmt and docs
+
+build/$(LAMBDA_RUNTIME_NORM).zip:
+ @mkdir -p build
+ @cd functions/$(LAMBDA_RUNTIME_NORM) && ./build.sh
+
+.PHONY: clean
+clean:
+ @rm -rf build/
+ @rm -rf functions/python3.8/package/
+ @rm -rf functions/go1.x/main
+
+.PHONY: bench
+bench: TF_CMD=apply
+bench: build/$(LAMBDA_RUNTIME_NORM).zip --run-tf
+
+.PHONY: destroy
+destroy: TF_CMD=destroy
+destroy: --run-tf
+
+.PHONY: terraform-init
+terraform-init:
+ @cd benchmarking && terraform init
+
+.PHONY: --run-tf
+--run-tf:
+ @cd benchmarking && terraform $(TF_CMD) -auto-approve \
+ -var 'resource_prefix=$(USER_NAME)' \
+ -var 'aws_region=$(AWS_REGION)' \
+ -var 'machine_type=$(MACHINE_TYPE)' \
+ -var 'load_duration=$(LOAD_DURATION)' \
+ -var 'load_arrival_rate=$(LOAD_ARRIVAL_RATE)' \
+ -var 'lambda_runtime=$(LAMBDA_RUNTIME)' \
+ -var 'lambda_timeout=$(LAMBDA_TIMEOUT)'
diff --git a/testing/benchmarking/main.tf b/testing/benchmarking/main.tf
new file mode 100644
index 00000000..932e504f
--- /dev/null
+++ b/testing/benchmarking/main.tf
@@ -0,0 +1,82 @@
+terraform {
+ required_version = ">= 1.1.8, < 2.0.0"
+ required_providers {
+ aws = {
+ source = "hashicorp/aws"
+ version = "~> 4.0"
+ }
+ null = {
+ source = "hashicorp/null"
+ version = ">=3.1.1"
+ }
+ ec = {
+ source = "elastic/ec"
+ version = ">=0.4.0"
+ }
+ }
+}
+
+locals {
+ load_req_path = "/test"
+ runtimeToHandler = {
+ "python3.8" = "main.handler"
+ "go1.x" = "main"
+ }
+}
+
+provider "ec" {}
+
+provider "aws" {
+ region = var.aws_region
+}
+
+
+module "ec_deployment" {
+ source = "github.com/elastic/apm-server/testing/infra/terraform/modules/ec_deployment"
+
+ region = var.ess_region
+ stack_version = var.stack_version
+
+ deployment_template = var.deployment_template
+ deployment_name_prefix = "${var.resource_prefix}_aws_lambda_test"
+
+ elasticsearch_size = var.elasticsearch_size
+ elasticsearch_zone_count = var.elasticsearch_zone_count
+
+ integrations_server = true
+ apm_server_expvar = false
+ apm_server_pprof = false
+}
+
+module "lambda_deployment" {
+ source = "../tf-modules/lambda_deployment"
+
+ resource_prefix = var.resource_prefix
+
+ build_dir = "../build"
+ apm_aws_extension_path = "../../bin/extension.zip"
+
+ lambda_runtime = var.lambda_runtime
+ lambda_handler = local.runtimeToHandler[var.lambda_runtime]
+ lambda_invoke_path = local.load_req_path
+
+ apm_server_url = module.ec_deployment.apm_url
+ apm_secret_token = module.ec_deployment.apm_secret_token
+}
+
+module "artillery_deployment" {
+ source = "../tf-modules/artillery_deployment"
+
+ depends_on = [
+ module.ec_deployment,
+ module.lambda_deployment,
+ ]
+
+ resource_prefix = var.resource_prefix
+ machine_type = var.machine_type
+
+ load_duration = var.load_duration
+ load_arrival_rate = var.load_arrival_rate
+ load_base_url = module.lambda_deployment.base_url
+ load_req_path = local.load_req_path
+}
diff --git a/testing/benchmarking/variables.tf b/testing/benchmarking/variables.tf
new file mode 100644
index 00000000..8db89496
--- /dev/null
+++ b/testing/benchmarking/variables.tf
@@ -0,0 +1,70 @@
+variable "resource_prefix" {
+ type = string
+ description = "Prefix to add to all created resource"
+}
+
+variable "aws_region" {
+ type = string
+ description = "AWS region to deploy lambda function"
+ default = "us-west-2"
+}
+
+variable "machine_type" {
+ type = string
+ description = "Machine type for artillery nodes"
+ default = "t2.medium"
+}
+
+variable "load_duration" {
+ type = number
+ description = "Duration over which to generate new virtual users"
+ default = 10
+}
+
+variable "load_arrival_rate" {
+ type = number
+ description = "Rate(per second) at which the virtual users are generated"
+ default = 50
+}
+
+variable "lambda_runtime" {
+ type = string
+ description = "The language-specific lambda runtime"
+ default = "python3.8"
+}
+
+variable "lambda_timeout" {
+ type = number
+ description = "Timeout of the lambda function in seconds"
+ default = 15
+}
+
+variable "ess_region" {
+ type = string
+ description = "Optional ESS region where the deployment will be created. Defaults to gcp-us-west2"
+ default = "gcp-us-west2"
+}
+
+variable "deployment_template" {
+ type = string
+ description = "Optional deployment template. Defaults to the CPU optimized template for GCP"
+ default = "gcp-compute-optimized-v2"
+}
+
+variable "stack_version" {
+ type = string
+ description = "Optional stack version"
+ default = "latest"
+}
+
+variable "elasticsearch_size" {
+ type = string
+ description = "Optional Elasticsearch instance size"
+ default = "8g"
+}
+
+variable "elasticsearch_zone_count" {
+ type = number
+ description = "Optional Elasticsearch zone count"
+ default = 2
+}
diff --git a/testing/functions/go1.x/build.sh b/testing/functions/go1.x/build.sh
new file mode 100755
index 00000000..4b11c26f
--- /dev/null
+++ b/testing/functions/go1.x/build.sh
@@ -0,0 +1,9 @@
+# Build file should build the function into a zip deployable in lambda
+# and put the zip in ../build/${LAMBDA_RUNTIME_NO_PERIODS}.zip
+
+#!/bin/bash
+
+set -e
+
+GOARCH=amd64 GOOS=linux go build main.go
+zip ../../build/go1_x.zip main
diff --git a/testing/functions/go1.x/go.mod b/testing/functions/go1.x/go.mod
new file mode 100644
index 00000000..39702ce9
--- /dev/null
+++ b/testing/functions/go1.x/go.mod
@@ -0,0 +1,27 @@
+module apm-aws-lambda-example-go
+
+go 1.19
+
+require (
+ github.com/aws/aws-lambda-go v1.34.1
+ go.elastic.co/apm/module/apmlambda/v2 v2.2.0
+)
+
+require (
+ github.com/armon/go-radix v1.0.0 // indirect
+ github.com/elastic/go-licenser v0.4.0 // indirect
+ github.com/elastic/go-sysinfo v1.7.1 // indirect
+ github.com/elastic/go-windows v1.0.1 // indirect
+ github.com/jcchavezs/porto v0.1.0 // indirect
+ github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 // indirect
+ github.com/pkg/errors v0.9.1 // indirect
+ github.com/prometheus/procfs v0.7.3 // indirect
+ github.com/santhosh-tekuri/jsonschema v1.2.4 // indirect
+ go.elastic.co/apm/v2 v2.2.0 // indirect
+ go.elastic.co/fastjson v1.1.0 // indirect
+ golang.org/x/mod v0.5.1 // indirect
+ golang.org/x/sys v0.0.0-20220209214540-3681064d5158 // indirect
+ golang.org/x/tools v0.1.9 // indirect
+ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
+ howett.net/plist v1.0.0 // indirect
+)
diff --git a/testing/functions/go1.x/go.sum b/testing/functions/go1.x/go.sum
new file mode 100644
index 00000000..2f644972
--- /dev/null
+++ b/testing/functions/go1.x/go.sum
@@ -0,0 +1,104 @@
+github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
+github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
+github.com/aws/aws-lambda-go v1.8.0/go.mod h1:zUsUQhAUjYzR8AuduJPCfhBuKWUaDbQiPOG+ouzmE1A=
+github.com/aws/aws-lambda-go v1.34.1 h1:M3a/uFYBjii+tDcOJ0wL/WyFi2550FHoECdPf27zvOs=
+github.com/aws/aws-lambda-go v1.34.1/go.mod h1:jwFe2KmMsHmffA1X2R09hH6lFzJQxzI8qK17ewzbQMM=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/elastic/go-licenser v0.4.0 h1:jLq6A5SilDS/Iz1ABRkO6BHy91B9jBora8FwGRsDqUI=
+github.com/elastic/go-licenser v0.4.0/go.mod h1:V56wHMpmdURfibNBggaSBfqgPxyT1Tldns1i87iTEvU=
+github.com/elastic/go-sysinfo v1.7.1 h1:Wx4DSARcKLllpKT2TnFVdSUJOsybqMYCNQZq1/wO+s0=
+github.com/elastic/go-sysinfo v1.7.1/go.mod h1:i1ZYdU10oLNfRzq4vq62BEwD2fH8KaWh6eh0ikPT9F0=
+github.com/elastic/go-windows v1.0.0/go.mod h1:TsU0Nrp7/y3+VwE82FoZF8gC/XFg/Elz6CcloAxnPgU=
+github.com/elastic/go-windows v1.0.1 h1:AlYZOldA+UJ0/2nBuqWdo90GFCgG9xuyw9SYzGUtJm0=
+github.com/elastic/go-windows v1.0.1/go.mod h1:FoVvqWSun28vaDQPbj2Elfc0JahhPB7WQEGa3c814Ss=
+github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=
+github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/jcchavezs/porto v0.1.0 h1:Xmxxn25zQMmgE7/yHYmh19KcItG81hIwfbEEFnd6w/Q=
+github.com/jcchavezs/porto v0.1.0/go.mod h1:fESH0gzDHiutHRdX2hv27ojnOVFco37hg1W6E9EZF4A=
+github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
+github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 h1:rp+c0RAYOWj8l6qbCUTSiRLG/iKnW3K3/QfPPuSsBt4=
+github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9688Y0wesXCyonoVr47MasHilkuLMqGhRZ4Hpak=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/procfs v0.0.0-20190425082905-87a4384529e0/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=
+github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
+github.com/santhosh-tekuri/jsonschema v1.2.4 h1:hNhW8e7t+H1vgY+1QeEQpveR6D4+OwKPXCfD2aieJis=
+github.com/santhosh-tekuri/jsonschema v1.2.4/go.mod h1:TEAUOeZSmIxTTuHatJzrvARHiuO9LYd+cIxzgEHCQI4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
+github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+go.elastic.co/apm/module/apmlambda/v2 v2.2.0 h1:LzelCQq2OAMWCgW093YxeBw1tSEPRkDO4Uu2PLouBOE=
+go.elastic.co/apm/module/apmlambda/v2 v2.2.0/go.mod h1:1V84GyLMeJlCFFOqKV3lcVJjWQpZ8IhfIoWjjT69Ugs=
+go.elastic.co/apm/v2 v2.2.0 h1:F4iM9XJKzZEcXU+NPOFLYXLXQf/3hU7rrcNRKWufKGQ=
+go.elastic.co/apm/v2 v2.2.0/go.mod h1:KGQn56LtRmkQjt2qw4+c1Jz8gv9rCBUU/m21uxrqcps=
+go.elastic.co/fastjson v1.1.0 h1:3MrGBWWVIxe/xvsbpghtkFoPciPhOCmjsR/HfwEeQR4=
+go.elastic.co/fastjson v1.1.0/go.mod h1:boNGISWMjQsUPy/t6yqt2/1Wx4YNPSe+mZjlyw9vKKI=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38=
+golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191025021431-6c3a3bfe00ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211102192858-4dd72447c267/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220209214540-3681064d5158 h1:rm+CHSpPEEW2IsXUib1ThaHIjuBVZjxNgSKmBLFfD4c=
+golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
+golang.org/x/tools v0.1.9 h1:j9KsMiaP1c3B0OTQGth0/k+miLGTgLsAFUCrF2vLcF8=
+golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0=
+howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM=
+howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
diff --git a/testing/functions/go1.x/main.go b/testing/functions/go1.x/main.go
new file mode 100644
index 00000000..aa0407f7
--- /dev/null
+++ b/testing/functions/go1.x/main.go
@@ -0,0 +1,34 @@
+package main
+
+import (
+ "context"
+ "fmt"
+ "log"
+ "strconv"
+
+ "github.com/aws/aws-lambda-go/events"
+ "github.com/aws/aws-lambda-go/lambda"
+ "github.com/aws/aws-lambda-go/lambdacontext"
+
+ _ "go.elastic.co/apm/module/apmlambda/v2"
+)
+
+var coldstart = true
+
+func Handle(ctx context.Context, req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
+ lc, _ := lambdacontext.FromContext(ctx)
+ log.Println("Example function log", lc.AwsRequestID)
+ response := events.APIGatewayProxyResponse{
+ StatusCode: 200,
+ Body: fmt.Sprintf("Hello from go!%s", lc.AwsRequestID),
+ Headers: map[string]string{
+ "coldstart": strconv.FormatBool(coldstart),
+ },
+ }
+ coldstart = false
+ return response, nil
+}
+
+func main() {
+ lambda.Start(Handle)
+}
diff --git a/testing/functions/python3.8/build.sh b/testing/functions/python3.8/build.sh
new file mode 100755
index 00000000..4fc7a40e
--- /dev/null
+++ b/testing/functions/python3.8/build.sh
@@ -0,0 +1,10 @@
+# Build file should build the function into a zip deployable in lambda
+# and put the zip in ../build/${LAMBDA_RUNTIME_NO_PERIODS}.zip
+
+#!/bin/bash
+
+set -e
+
+pip install -t ./package -r requirements.txt
+(cd package && zip -r ../../../build/python3_8.zip .)
+zip -g ../../build/python3_8.zip main.py
diff --git a/testing/functions/python3.8/main.py b/testing/functions/python3.8/main.py
new file mode 100644
index 00000000..1e6ba08f
--- /dev/null
+++ b/testing/functions/python3.8/main.py
@@ -0,0 +1,18 @@
+import json
+from elasticapm import capture_serverless
+
+coldstart = True
+@capture_serverless()
+def handler(event, context):
+ global coldstart
+ print("Example function log", context.aws_request_id)
+ resp = {
+ "statusCode": 200,
+ "body": json.dumps("Hello from Lambda!"+context.aws_request_id),
+ "headers": {
+ "coldstart": coldstart,
+ }
+ }
+ coldstart = False
+ return resp
+
diff --git a/testing/functions/python3.8/requirements.txt b/testing/functions/python3.8/requirements.txt
new file mode 100644
index 00000000..aa75aeb7
--- /dev/null
+++ b/testing/functions/python3.8/requirements.txt
@@ -0,0 +1 @@
+elastic_apm==6.12.0
diff --git a/testing/tf-modules/artillery_deployment/README.md b/testing/tf-modules/artillery_deployment/README.md
new file mode 100644
index 00000000..9d24764f
--- /dev/null
+++ b/testing/tf-modules/artillery_deployment/README.md
@@ -0,0 +1,57 @@
+## Terraform module for deploying artillery
+
+The module is used to deploy and run [artillery](https://www.artillery.io/) in
+cloud for benchmarking lambda functions.
+
+
+## Requirements
+
+| Name | Version |
+|------|---------|
+| [aws](#requirement\_aws) | ~> 4.0 |
+| [null](#requirement\_null) | >=3.1.1 |
+
+## Providers
+
+| Name | Version |
+|------|---------|
+| [aws](#provider\_aws) | 4.37.0 |
+| [null](#provider\_null) | 3.2.0 |
+| [tls](#provider\_tls) | 4.0.4 |
+
+## Modules
+
+No modules.
+
+## Resources
+
+| Name | Type |
+|------|------|
+| [aws_instance.artillery](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance) | resource |
+| [aws_internet_gateway.artillery](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/internet_gateway) | resource |
+| [aws_key_pair.artillery](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/key_pair) | resource |
+| [aws_route.artillery](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route) | resource |
+| [aws_security_group.artillery](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
+| [aws_subnet.artillery](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource |
+| [aws_vpc.artillery](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc) | resource |
+| [null_resource.run_artillery](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource |
+| [tls_private_key.artillery](https://registry.terraform.io/providers/hashicorp/tls/latest/docs/resources/private_key) | resource |
+| [aws_ami.ubuntu](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ami) | data source |
+| [tls_public_key.artillery](https://registry.terraform.io/providers/hashicorp/tls/latest/docs/data-sources/public_key) | data source |
+
+## Inputs
+
+| Name | Description | Type | Default | Required |
+|------|-------------|------|---------|:--------:|
+| [aws\_region](#input\_aws\_region) | AWS region to deploy lambda function | `string` | `"us-west-2"` | no |
+| [load\_arrival\_rate](#input\_load\_arrival\_rate) | Rate(per second) at which the virtual users are generated | `number` | `50` | no |
+| [load\_base\_url](#input\_load\_base\_url) | Base URL for load generation | `string` | n/a | yes |
+| [load\_duration](#input\_load\_duration) | Duration over which to generate new virtual users | `number` | `10` | no |
+| [load\_req\_path](#input\_load\_req\_path) | Request path for load generation | `string` | n/a | yes |
+| [machine\_type](#input\_machine\_type) | Machine type for artillery nodes | `string` | `"t2.medium"` | no |
+| [resource\_prefix](#input\_resource\_prefix) | Prefix to add to all created resource | `string` | n/a | yes |
+
+## Outputs
+
+No outputs.
+
diff --git a/testing/tf-modules/artillery_deployment/files/coldstart-metrics.js b/testing/tf-modules/artillery_deployment/files/coldstart-metrics.js
new file mode 100644
index 00000000..e7ab75ba
--- /dev/null
+++ b/testing/tf-modules/artillery_deployment/files/coldstart-metrics.js
@@ -0,0 +1,12 @@
+module.exports = { generateColdstartAwareMetrics };
+
+function generateColdstartAwareMetrics(req, res, context, events, done) {
+ if(res.headers["coldstart"] === "true") {
+ events.emit("histogram", `with-coldstart`, res.timings.phases.firstByte);
+ events.emit('counter', `with-coldstart.codes.${res.statusCode}`, 1);
+ } else {
+ events.emit("histogram", `without-coldstart`, res.timings.phases.firstByte);
+ events.emit('counter', `without-coldstart.codes.${res.statusCode}`, 1);
+ }
+ return done();
+}
diff --git a/testing/tf-modules/artillery_deployment/files/config.yml.tpl b/testing/tf-modules/artillery_deployment/files/config.yml.tpl
new file mode 100644
index 00000000..17fdedd5
--- /dev/null
+++ b/testing/tf-modules/artillery_deployment/files/config.yml.tpl
@@ -0,0 +1,13 @@
+config:
+ target: ${load_base_url}
+ processor: "./coldstart-metrics.js"
+ phases:
+ - duration: ${load_duration}
+ arrivalRate: ${load_arrival_rate}
+
+scenarios:
+ - name: get
+ afterResponse: "generateColdstartAwareMetrics"
+ flow:
+ - get:
+ url: ${load_req_path}
diff --git a/testing/tf-modules/artillery_deployment/files/install_artillery.sh b/testing/tf-modules/artillery_deployment/files/install_artillery.sh
new file mode 100644
index 00000000..62698d21
--- /dev/null
+++ b/testing/tf-modules/artillery_deployment/files/install_artillery.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+set -e
+
+INSTALL_NVM_VER=v0.34.0
+
+# Install nvm
+curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/$INSTALL_NVM_VER/install.sh | bash
+source ~/.nvm/nvm.sh
+
+# Install npm lts
+nvm install --lts
+nvm use default
+
+# Install artillery
+npm install --quiet --no-progress -g artillery@latest
+
+# Check version
+artillery --version
diff --git a/testing/tf-modules/artillery_deployment/files/run_artillery.sh b/testing/tf-modules/artillery_deployment/files/run_artillery.sh
new file mode 100644
index 00000000..dd3afcfa
--- /dev/null
+++ b/testing/tf-modules/artillery_deployment/files/run_artillery.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+set -e
+
+source ~/.nvm/nvm.sh
+artillery run config.yml
diff --git a/testing/tf-modules/artillery_deployment/main.tf b/testing/tf-modules/artillery_deployment/main.tf
new file mode 100644
index 00000000..d14729da
--- /dev/null
+++ b/testing/tf-modules/artillery_deployment/main.tf
@@ -0,0 +1,156 @@
+resource "tls_private_key" "artillery" {
+ algorithm = "RSA"
+ rsa_bits = "4096"
+}
+
+data "tls_public_key" "artillery" {
+ private_key_openssh = tls_private_key.artillery.private_key_openssh
+}
+
+data "aws_ami" "ubuntu" {
+ most_recent = true
+
+ filter {
+ name = "name"
+ values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
+ }
+
+ filter {
+ name = "virtualization-type"
+ values = ["hvm"]
+ }
+
+ owners = ["099720109477"] # Canonical
+}
+
+resource "aws_vpc" "artillery" {
+ cidr_block = "172.16.0.0/28"
+
+ tags = {
+ Name = "${var.resource_prefix}_apm_aws_lambda_artillery"
+ }
+}
+
+resource "aws_subnet" "artillery" {
+ vpc_id = aws_vpc.artillery.id
+ cidr_block = "172.16.0.0/28"
+ map_public_ip_on_launch = true
+
+ tags = {
+ Name = "${var.resource_prefix}_apm_aws_lambda_artillery"
+ }
+}
+
+resource "aws_internet_gateway" "artillery" {
+ vpc_id = aws_vpc.artillery.id
+
+ tags = {
+ Name = "${var.resource_prefix}_apm_aws_lambda_artillery"
+ }
+}
+
+resource "aws_route" "artillery" {
+ destination_cidr_block = "0.0.0.0/0"
+ route_table_id = aws_vpc.artillery.default_route_table_id
+ gateway_id = aws_internet_gateway.artillery.id
+}
+
+resource "aws_security_group" "artillery" {
+ name = "${var.resource_prefix}_apm_aws_lambda_artillery"
+ vpc_id = aws_vpc.artillery.id
+ egress = [
+ {
+ description = "Allow all egress traffic"
+ cidr_blocks = ["0.0.0.0/0"]
+ ipv6_cidr_blocks = []
+ prefix_list_ids = []
+ security_groups = []
+ from_port = 0
+ to_port = 0
+ protocol = "-1"
+ self = false
+ }
+ ]
+ ingress = [
+ {
+ description = "Allow SSH from all sources"
+ cidr_blocks = ["0.0.0.0/0"]
+ ipv6_cidr_blocks = []
+ prefix_list_ids = []
+ security_groups = []
+ from_port = 22
+ to_port = 22
+ protocol = "tcp"
+ self = false
+ },
+ ]
+}
+
+resource "aws_key_pair" "artillery" {
+ key_name = "${var.resource_prefix}_apm_aws_lambda_artillery"
+ public_key = data.tls_public_key.artillery.public_key_openssh
+}
+
+resource "aws_instance" "artillery" {
+ ami = data.aws_ami.ubuntu.id
+ instance_type = var.machine_type
+ key_name = aws_key_pair.artillery.key_name
+ subnet_id = aws_subnet.artillery.id
+ vpc_security_group_ids = [aws_security_group.artillery.id]
+
+ lifecycle {
+ ignore_changes = [ami]
+ }
+
+ connection {
+ type = "ssh"
+ host = self.public_ip
+ user = "ubuntu"
+ private_key = tls_private_key.artillery.private_key_openssh
+ timeout = "1m"
+ }
+
+ provisioner "remote-exec" {
+ script = "${path.module}/files/install_artillery.sh"
+ }
+}
+
+resource "null_resource" "run_artillery" {
+ triggers = {
+ always_run = "${timestamp()}"
+ }
+
+ depends_on = [
+ aws_instance.artillery
+ ]
+
+ connection {
+ type = "ssh"
+ host = aws_instance.artillery.public_ip
+ user = "ubuntu"
+ private_key = tls_private_key.artillery.private_key_openssh
+ timeout = "1m"
+ }
+
+ provisioner "file" {
+ content = templatefile(
+ "${path.module}/files/config.yml.tpl",
+ {
+ load_base_url = var.load_base_url
+ load_req_path = var.load_req_path
+ load_duration = var.load_duration
+ load_arrival_rate = var.load_arrival_rate
+ }
+ )
+ destination = "config.yml"
+ }
+
+ provisioner "file" {
+ source = "${path.module}/files/coldstart-metrics.js"
+ destination = "coldstart-metrics.js"
+ }
+
+ provisioner "remote-exec" {
+ script = "${path.module}/files/run_artillery.sh"
+ }
+}
diff --git a/testing/tf-modules/artillery_deployment/variables.tf b/testing/tf-modules/artillery_deployment/variables.tf
new file mode 100644
index 00000000..5f4b3945
--- /dev/null
+++ b/testing/tf-modules/artillery_deployment/variables.tf
@@ -0,0 +1,32 @@
+variable "resource_prefix" {
+ type = string
+ description = "Prefix to add to all created resource"
+}
+
+variable "machine_type" {
+ type = string
+ description = "Machine type for artillery nodes"
+ default = "t2.medium"
+}
+
+variable "load_duration" {
+ type = number
+ description = "Duration over which to generate new virtual users"
+ default = 10
+}
+
+variable "load_arrival_rate" {
+ type = number
+ description = "Rate(per second) at which the virtual users are generated"
+ default = 50
+}
+
+variable "load_base_url" {
+ type = string
+ description = "Base URL for load generation"
+}
+
+variable "load_req_path" {
+ type = string
+ description = "Request path for load generation"
+}
diff --git a/testing/tf-modules/lambda_deployment/README.md b/testing/tf-modules/lambda_deployment/README.md
new file mode 100644
index 00000000..651835b9
--- /dev/null
+++ b/testing/tf-modules/lambda_deployment/README.md
@@ -0,0 +1,59 @@
+## Terraform module to deploy Lambda functions on cloud
+
+The module is used to deploy lambda functions for supported runtimes on cloud
+for the purpose of benchmarking. The lambda function is configured to be
+triggered with an no auth API gateway endpoint.
+
+
+
+## Requirements
+
+| Name | Version |
+|------|---------|
+| [aws](#requirement\_aws) | ~> 4.0 |
+
+## Providers
+
+| Name | Version |
+|------|---------|
+| [aws](#provider\_aws) | 4.37.0 |
+
+## Modules
+
+No modules.
+
+## Resources
+
+| Name | Type |
+|------|------|
+| [aws_apigatewayv2_api.trigger](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/apigatewayv2_api) | resource |
+| [aws_apigatewayv2_integration.trigger](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/apigatewayv2_integration) | resource |
+| [aws_apigatewayv2_route.trigger](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/apigatewayv2_route) | resource |
+| [aws_apigatewayv2_stage.trigger](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/apigatewayv2_stage) | resource |
+| [aws_iam_role.iam_for_lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
+| [aws_lambda_function.test_fn](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_function) | resource |
+| [aws_lambda_layer_version.extn_layer](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_layer_version) | resource |
+| [aws_lambda_permission.trigger](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_permission) | resource |
+
+## Inputs
+
+| Name | Description | Type | Default | Required |
+|------|-------------|------|---------|:--------:|
+| [apm\_aws\_extension\_path](#input\_apm\_aws\_extension\_path) | Path to the zip file containing extension code | `string` | n/a | yes |
+| [apm\_secret\_token](#input\_apm\_secret\_token) | Secret token for auth against the given server URL | `string` | n/a | yes |
+| [apm\_server\_url](#input\_apm\_server\_url) | APM Server URL for sending the generated load | `string` | n/a | yes |
+| [aws\_region](#input\_aws\_region) | AWS region to deploy lambda function | `string` | `"us-west-2"` | no |
+| [build\_dir](#input\_build\_dir) | Prefix to add to all created resource | `string` | n/a | yes |
+| [lambda\_handler](#input\_lambda\_handler) | Entrypoint for the lambda function | `string` | `"main.handler"` | no |
+| [lambda\_invoke\_path](#input\_lambda\_invoke\_path) | Request path to invoke the test lambda function | `string` | `"/test"` | no |
+| [lambda\_runtime](#input\_lambda\_runtime) | The language-specific lambda runtime | `string` | `"python3.8"` | no |
+| [lambda\_timeout](#input\_lambda\_timeout) | Timeout of the lambda function in seconds | `number` | `15` | no |
+| [resource\_prefix](#input\_resource\_prefix) | Prefix to add to all created resource | `string` | n/a | yes |
+
+## Outputs
+
+| Name | Description |
+|------|-------------|
+| [base\_url](#output\_base\_url) | Base URL for triggering the test lambda function |
+| [trigger\_url](#output\_trigger\_url) | URL for triggering the test lambda function |
+
diff --git a/testing/tf-modules/lambda_deployment/main.tf b/testing/tf-modules/lambda_deployment/main.tf
new file mode 100644
index 00000000..9ab7118e
--- /dev/null
+++ b/testing/tf-modules/lambda_deployment/main.tf
@@ -0,0 +1,87 @@
+locals {
+ name_from_runtime = replace(var.lambda_runtime, ".", "_")
+ lambda_function_path = "${var.build_dir}/${local.name_from_runtime}.zip"
+ lambda_function_name = "${var.resource_prefix}_${local.name_from_runtime}_apm_aws_lambda"
+}
+
+resource "aws_iam_role" "iam_for_lambda" {
+ name = "${var.resource_prefix}_apm_aws_lambda_iam"
+
+ assume_role_policy = <