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 = <