From 865b374e94f3677296ee9b64f9ff3f5736524466 Mon Sep 17 00:00:00 2001 From: Jean-Louis Voiseux <48380853+jlvoiseux@users.noreply.github.com> Date: Mon, 24 Jan 2022 18:08:08 +0100 Subject: [PATCH 01/20] Adding local lambdas and execution script --- .gitignore | 6 + apm-lambda-extension/e2e-testing/.gitignore | 4 + apm-lambda-extension/e2e-testing/e2e_test.go | 259 ++++++++++++++++++ .../sam-java/sam-testing-java/build.gradle | 14 + .../sam-java/sam-testing-java/gradlew | 172 ++++++++++++ .../sam-java/sam-testing-java/gradlew.bat | 84 ++++++ .../src/main/java/samtestingjava/App.java | 55 ++++ .../src/test/java/helloworld/AppTest.java | 22 ++ .../e2e-testing/sam-java/template.yml | 66 +++++ .../sam-node/sam-testing-node/.npmignore | 1 + .../sam-node/sam-testing-node/app.js | 32 +++ .../sam-node/sam-testing-node/package.json | 20 ++ .../tests/unit/test-handler.js | 22 ++ .../e2e-testing/sam-node/template.yml | 53 ++++ .../sam-python/sam-testing-python/__init__.py | 0 .../sam-python/sam-testing-python/app.py | 20 ++ .../sam-testing-python/requirements.txt | 2 + .../e2e-testing/sam-python/template.yml | 55 ++++ apm-lambda-extension/go.mod | 3 + apm-lambda-extension/go.sum | 8 + 20 files changed, 898 insertions(+) create mode 100644 apm-lambda-extension/e2e-testing/.gitignore create mode 100644 apm-lambda-extension/e2e-testing/e2e_test.go create mode 100644 apm-lambda-extension/e2e-testing/sam-java/sam-testing-java/build.gradle create mode 100755 apm-lambda-extension/e2e-testing/sam-java/sam-testing-java/gradlew create mode 100644 apm-lambda-extension/e2e-testing/sam-java/sam-testing-java/gradlew.bat create mode 100644 apm-lambda-extension/e2e-testing/sam-java/sam-testing-java/src/main/java/samtestingjava/App.java create mode 100644 apm-lambda-extension/e2e-testing/sam-java/sam-testing-java/src/test/java/helloworld/AppTest.java create mode 100644 apm-lambda-extension/e2e-testing/sam-java/template.yml create mode 100644 apm-lambda-extension/e2e-testing/sam-node/sam-testing-node/.npmignore create mode 100644 apm-lambda-extension/e2e-testing/sam-node/sam-testing-node/app.js create mode 100644 apm-lambda-extension/e2e-testing/sam-node/sam-testing-node/package.json create mode 100644 apm-lambda-extension/e2e-testing/sam-node/sam-testing-node/tests/unit/test-handler.js create mode 100644 apm-lambda-extension/e2e-testing/sam-node/template.yml create mode 100644 apm-lambda-extension/e2e-testing/sam-python/sam-testing-python/__init__.py create mode 100644 apm-lambda-extension/e2e-testing/sam-python/sam-testing-python/app.py create mode 100644 apm-lambda-extension/e2e-testing/sam-python/sam-testing-python/requirements.txt create mode 100644 apm-lambda-extension/e2e-testing/sam-python/template.yml diff --git a/.gitignore b/.gitignore index 66fd13c9..6d60d082 100644 --- a/.gitignore +++ b/.gitignore @@ -11,5 +11,11 @@ # Output of the go coverage tool, specifically when used with LiteIDE *.out +# Goland config files +*.idea + +# MacOS DS_Store files +*.DS_Store + # Dependency directories (remove the comment below to include it) # vendor/ diff --git a/apm-lambda-extension/e2e-testing/.gitignore b/apm-lambda-extension/e2e-testing/.gitignore new file mode 100644 index 00000000..7c8f1d6a --- /dev/null +++ b/apm-lambda-extension/e2e-testing/.gitignore @@ -0,0 +1,4 @@ +**/.aws-sam +**/sam-java/agent +**/sam-java/*/gradle +**/.env \ No newline at end of file diff --git a/apm-lambda-extension/e2e-testing/e2e_test.go b/apm-lambda-extension/e2e-testing/e2e_test.go new file mode 100644 index 00000000..2aecf012 --- /dev/null +++ b/apm-lambda-extension/e2e-testing/e2e_test.go @@ -0,0 +1,259 @@ +package e2e_testing + +import ( + "archive/zip" + "bufio" + "encoding/json" + "fmt" + "github.com/elastic/go-elasticsearch/v8" + "github.com/google/uuid" + "github.com/joho/godotenv" + "github.com/stretchr/testify/assert" + "io" + "io/ioutil" + "log" + "net/http" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" +) + +func TestEndToEndExtensionBehavior(t *testing.T) { + + if err := godotenv.Load(); err != nil { + t.Skip("No .env file found alongside e2e_test.go : Skipping end-to-end tests.") + } + if os.Getenv("RUN_E2E_TESTS") != "true" { + t.Skip("Skipping E2E tests. Please set the env. variable RUN_E2E_TESTS=true if you want to run them.") + } + if os.Getenv("ELASTIC_CLOUD_ID") == "" { + log.Println("Please set the ELASTIC_CLOUD_ID environment variable") + os.Exit(1) + } + if os.Getenv("ELASTIC_CLOUD_API_KEY") == "" { + log.Println("Please set the ELASTIC_CLOUD_API_KEY environment variable") + os.Exit(1) + } + if os.Getenv("ELASTIC_APM_SERVER_URL") == "" { + log.Println("Please set the ELASTIC_APM_SERVER_URL environment variable") + os.Exit(1) + } + if os.Getenv("ELASTIC_APM_SERVER_TOKEN") == "" { + log.Println("Please set the ELASTIC_APM_SERVER_TOKEN environment variable") + os.Exit(1) + } + forceBuildLambdasFlag := false + if os.Getenv("FORCE_REBUILD_LOCAL_SAM_LAMBDAS") == "true" { + forceBuildLambdasFlag = true + } + + buildExtensionBinaries() + + assert.True(t, runTest("sam-node", "SamTestingNode", forceBuildLambdasFlag)) + assert.True(t, runTest("sam-python", "SamTestingPython", forceBuildLambdasFlag)) + if !folderExists(filepath.Join("sam-java", "agent")) { + log.Println("Java agent not found ! Collecting archive from Github...") + retrieveJavaAgent("sam-java") + } + assert.True(t, runTest("sam-java", "SamTestingJava", forceBuildLambdasFlag)) + +} + +func buildExtensionBinaries() { + runCommandInDir("make", []string{}, "..", false) +} + +func runTest(path string, serviceName string, buildFlag bool) bool { + log.Printf("Starting to test %s", serviceName) + + if !folderExists(filepath.Join(path, ".aws-sam")) || buildFlag { + log.Printf("Building the Lambda function %s", serviceName) + runCommandInDir("sam", []string{"build"}, path, false) + } + + log.Printf("Invoking the Lambda function %s", serviceName) + uuidWithHyphen := uuid.New().String() + runCommandInDir("sam", []string{"local", "invoke", "--parameter-overrides", + fmt.Sprintf("ParameterKey=ApmServerURL,ParameterValue=%s", os.Getenv("ELASTIC_APM_SERVER_URL")), + fmt.Sprintf("ParameterKey=ApmSecretToken,ParameterValue=%s", os.Getenv("ELASTIC_APM_SERVER_TOKEN")), + fmt.Sprintf("ParameterKey=TestUUID,ParameterValue=%s", uuidWithHyphen)}, + path, false) + log.Printf("%s execution complete", serviceName) + + log.Printf("Querying Elasticsearch for transaction %s bound to %s...", uuidWithHyphen, serviceName) + res := queryElasticsearch(serviceName, uuidWithHyphen) + if res { + log.Printf("SUCCESS : Transaction %s successfully sent to Elasticsearch by %s", uuidWithHyphen, serviceName) + return true + } + log.Printf("FAILURE : Transaction %s bound to %s not found in Elasticsearch", uuidWithHyphen, serviceName) + return false +} + +func retrieveJavaAgent(samJavaPath string) { + + agentFolderPath := filepath.Join(samJavaPath, "agent") + agentArchivePath := filepath.Join(samJavaPath, "agent.zip") + + // Download archive + out, err := os.Create(agentArchivePath) + processError(err) + defer out.Close() + resp, err := http.Get("https://github.com/elastic/apm-agent-java/releases/download/v1.28.4/elastic-apm-java-aws-lambda-layer-1.28.4.zip") + processError(err) + defer resp.Body.Close() + io.Copy(out, resp.Body) + + // Unzip archive and delete it + log.Println("Unzipping Java Agent archive...") + unzip(agentArchivePath, agentFolderPath) + err = os.Remove(agentArchivePath) + processError(err) + + // Change permissions + log.Println("Setting appropriate permissions for Java agent files...") + agentFiles, err := ioutil.ReadDir(agentFolderPath) + processError(err) + for _, f := range agentFiles { + os.Chmod(filepath.Join(agentFolderPath, f.Name()), 0755) + } +} + +func queryElasticsearch(serviceName string, transactionName string) bool { + + cfg := elasticsearch.Config{ + CloudID: os.Getenv("ELASTIC_CLOUD_ID"), + APIKey: os.Getenv("ELASTIC_CLOUD_API_KEY"), + } + + es, err := elasticsearch.NewClient(cfg) + + body := fmt.Sprintf(`{ + "query": { + "bool": { + "must": [ + { + "match": { + "service.name": "%s" + } + }, + { + "match": { + "transaction.name": "%s" + } + } + ] + } + } + }`, serviceName, transactionName) + + res, err := es.Count( + es.Count.WithIndex("apm-*-transaction-*"), + es.Count.WithBody(strings.NewReader(body)), + es.Count.WithPretty(), + ) + processError(err) + defer res.Body.Close() + + var r map[string]interface{} + err = json.NewDecoder(res.Body).Decode(&r) + processError(err) + hitsNum := int(r["count"].(float64)) + if hitsNum > 0 { + return true + } + return false +} + +func runCommandInDir(command string, args []string, dir string, printOutput bool) { + e := exec.Command(command, args...) + if printOutput { + log.Println(e.String()) + } + e.Dir = dir + stdout, _ := e.StdoutPipe() + stderr, _ := e.StderrPipe() + e.Start() + scannerOut := bufio.NewScanner(stdout) + for scannerOut.Scan() { + m := scannerOut.Text() + if printOutput { + log.Println(m) + } + } + scannerErr := bufio.NewScanner(stderr) + for scannerErr.Scan() { + m := scannerErr.Text() + if printOutput { + log.Println(m) + } + } + e.Wait() + +} + +func folderExists(path string) bool { + _, err := os.Stat(path) + if err == nil { + return true + } + if os.IsNotExist(err) { + return false + } + return false +} + +func processError(err error) { + if err != nil { + log.Panic(err) + } +} + +func unzip(archivePath string, destinationFolderPath string) { + + openedArchive, err := zip.OpenReader(archivePath) + processError(err) + defer openedArchive.Close() + + // Permissions setup + os.MkdirAll(destinationFolderPath, 0755) + + // Closure required, so that Close() calls do not pile up when unzipping archives with a lot of files + extractAndWriteFile := func(f *zip.File) error { + rc, err := f.Open() + if err != nil { + return err + } + defer func() { + if err := rc.Close(); err != nil { + panic(err) + } + }() + + path := filepath.Join(destinationFolderPath, f.Name) + + // Check for ZipSlip (Directory traversal) + if !strings.HasPrefix(path, filepath.Clean(destinationFolderPath)+string(os.PathSeparator)) { + return fmt.Errorf("illegal file path: %s", path) + } + + if f.FileInfo().IsDir() { + os.MkdirAll(path, f.Mode()) + } else { + os.MkdirAll(filepath.Dir(path), f.Mode()) + f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) + processError(err) + defer f.Close() + _, err = io.Copy(f, rc) + processError(err) + } + return nil + } + + for _, f := range openedArchive.File { + err := extractAndWriteFile(f) + processError(err) + } +} diff --git a/apm-lambda-extension/e2e-testing/sam-java/sam-testing-java/build.gradle b/apm-lambda-extension/e2e-testing/sam-java/sam-testing-java/build.gradle new file mode 100644 index 00000000..e93549e0 --- /dev/null +++ b/apm-lambda-extension/e2e-testing/sam-java/sam-testing-java/build.gradle @@ -0,0 +1,14 @@ +plugins { + id 'java' +} + +repositories { + mavenCentral() +} + +dependencies { + implementation 'com.amazonaws:aws-lambda-java-core:1.2.1' + implementation 'com.amazonaws:aws-lambda-java-events:3.6.0' + testImplementation 'junit:junit:4.13.1' + compile "co.elastic.apm:apm-agent-api:1.28.4" +} diff --git a/apm-lambda-extension/e2e-testing/sam-java/sam-testing-java/gradlew b/apm-lambda-extension/e2e-testing/sam-java/sam-testing-java/gradlew new file mode 100755 index 00000000..af6708ff --- /dev/null +++ b/apm-lambda-extension/e2e-testing/sam-java/sam-testing-java/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/apm-lambda-extension/e2e-testing/sam-java/sam-testing-java/gradlew.bat b/apm-lambda-extension/e2e-testing/sam-java/sam-testing-java/gradlew.bat new file mode 100644 index 00000000..6d57edc7 --- /dev/null +++ b/apm-lambda-extension/e2e-testing/sam-java/sam-testing-java/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/apm-lambda-extension/e2e-testing/sam-java/sam-testing-java/src/main/java/samtestingjava/App.java b/apm-lambda-extension/e2e-testing/sam-java/sam-testing-java/src/main/java/samtestingjava/App.java new file mode 100644 index 00000000..5c70fb12 --- /dev/null +++ b/apm-lambda-extension/e2e-testing/sam-java/sam-testing-java/src/main/java/samtestingjava/App.java @@ -0,0 +1,55 @@ +package samtestingjava; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; + +import co.elastic.apm.api.ElasticApm; +import co.elastic.apm.api.Transaction; + +/** + * Handler for requests to Lambda function. + */ +public class App implements RequestHandler { + + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + Map headers = new HashMap<>(); + headers.put("Content-Type", "application/json"); + headers.put("X-Custom-Header", "application/json"); + + Transaction transaction = ElasticApm.startTransaction(); + transaction.setName(System.getenv("APM_AWS_EXTENSION_TEST_UUID")); + transaction.end(); + + APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent() + .withHeaders(headers); + try { + final String pageContents = this.getPageContents("https://checkip.amazonaws.com"); + String output = String.format("{ \"message\": \"hello world\", \"location\": \"%s\" }", pageContents); + + return response + .withStatusCode(200) + .withBody(output); + } catch (IOException e) { + return response + .withBody("{}") + .withStatusCode(500); + } + } + + private String getPageContents(String address) throws IOException{ + URL url = new URL(address); + try(BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()))) { + return br.lines().collect(Collectors.joining(System.lineSeparator())); + } + } +} diff --git a/apm-lambda-extension/e2e-testing/sam-java/sam-testing-java/src/test/java/helloworld/AppTest.java b/apm-lambda-extension/e2e-testing/sam-java/sam-testing-java/src/test/java/helloworld/AppTest.java new file mode 100644 index 00000000..8c1bfad4 --- /dev/null +++ b/apm-lambda-extension/e2e-testing/sam-java/sam-testing-java/src/test/java/helloworld/AppTest.java @@ -0,0 +1,22 @@ +package samtestingjava; + +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import org.junit.Test; + +public class AppTest { + @Test + public void successfulResponse() { + App app = new App(); + APIGatewayProxyResponseEvent result = app.handleRequest(null, null); + assertEquals(200, result.getStatusCode().intValue()); + assertEquals("application/json", result.getHeaders().get("Content-Type")); + String content = result.getBody(); + assertNotNull(content); + assertTrue(content.contains("\"message\"")); + assertTrue(content.contains("\"hello world\"")); + assertTrue(content.contains("\"location\"")); + } +} diff --git a/apm-lambda-extension/e2e-testing/sam-java/template.yml b/apm-lambda-extension/e2e-testing/sam-java/template.yml new file mode 100644 index 00000000..f3e799b9 --- /dev/null +++ b/apm-lambda-extension/e2e-testing/sam-java/template.yml @@ -0,0 +1,66 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + sam-java + + Sample SAM Template for sam-java + +# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst +Globals: + Function: + Timeout: 75 + +Parameters: + ApmServerURL: + Type: String + Description: APM server URL + ApmSecretToken: + Type: String + Description: APM server secret token + TestUUID: + Type: String + Description: The UUID used to verify the end-to-end test + +Resources: + ElasticAPMExtensionLayer: + Type: AWS::Serverless::LayerVersion + Properties: + ContentUri: ../../bin + LayerName: apm-lambda-extension + CompatibleArchitectures: + - x86_64 + ElasticAPMAgentLayer: + Type: AWS::Serverless::LayerVersion + Properties: + ContentUri: agent + LayerName: apm-java-agent + CompatibleArchitectures: + - x86_64 + SamTestingJava: + 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: sam-testing-java/ + Handler: samtestingjava.App::handleRequest + Runtime: java11 + Architectures: + - x86_64 + Layers: + - !Ref ElasticAPMExtensionLayer + - !Ref ElasticAPMAgentLayer + MemorySize: 512 + Events: + HelloWorld: + 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: /hello + Method: get + Environment: + Variables: + ELASTIC_APM_LAMBDA_APM_SERVER: !Ref ApmServerURL + ELASTIC_APM_SECRET_TOKEN: !Ref ApmSecretToken + ELASTIC_APM_CENTRAL_CONFIG: false + ELASTIC_APM_CLOUD_PROVIDER: none + ELASTIC_APM_SERVER_URL: http://localhost:8200 + AWS_LAMBDA_EXEC_WRAPPER: /opt/elastic-apm-handler + ELASTIC_APM_APPLICATION_PACKAGES: true + APM_AWS_EXTENSION_TEST_UUID: !Ref TestUUID diff --git a/apm-lambda-extension/e2e-testing/sam-node/sam-testing-node/.npmignore b/apm-lambda-extension/e2e-testing/sam-node/sam-testing-node/.npmignore new file mode 100644 index 00000000..e7e1fb04 --- /dev/null +++ b/apm-lambda-extension/e2e-testing/sam-node/sam-testing-node/.npmignore @@ -0,0 +1 @@ +tests/* diff --git a/apm-lambda-extension/e2e-testing/sam-node/sam-testing-node/app.js b/apm-lambda-extension/e2e-testing/sam-node/sam-testing-node/app.js new file mode 100644 index 00000000..23938d27 --- /dev/null +++ b/apm-lambda-extension/e2e-testing/sam-node/sam-testing-node/app.js @@ -0,0 +1,32 @@ +// const url = 'http://checkip.amazonaws.com/'; +const apm = require('elastic-apm-node').start({/*...*/}) +let response; + +/** + * + * Event doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format + * @param {Object} event - API Gateway Lambda Proxy Input Format + * + * Context doc: https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-context.html + * @param {Object} context + * + * Return doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html + * @returns {Object} object - API Gateway Lambda Proxy Output Format + * + */ +exports.lambdaHandler = apm.lambda(async (event, context) => { + apm.startTransaction(process.env.APM_AWS_EXTENSION_TEST_UUID) + try { + response = { + 'statusCode': 200, + 'body': JSON.stringify({ + message: 'hello world', + }) + } + } catch (err) { + console.log(err); + return err; + } + apm.endTransaction() + return response +}); diff --git a/apm-lambda-extension/e2e-testing/sam-node/sam-testing-node/package.json b/apm-lambda-extension/e2e-testing/sam-node/sam-testing-node/package.json new file mode 100644 index 00000000..ed8f4277 --- /dev/null +++ b/apm-lambda-extension/e2e-testing/sam-node/sam-testing-node/package.json @@ -0,0 +1,20 @@ +{ + "name": "sam_testing_node", + "version": "1.0.0", + "description": "hello world sample for NodeJS", + "main": "app.js", + "repository": "https://github.com/awslabs/aws-sam-cli/tree/develop/samcli/local/init/templates/cookiecutter-aws-sam-hello-nodejs", + "author": "SAM CLI", + "license": "MIT", + "dependencies": { + "axios": "^0.21.1", + "elastic-apm-node": "^3.26.0" + }, + "scripts": { + "test": "mocha tests/unit/" + }, + "devDependencies": { + "chai": "^4.2.0", + "mocha": "^8.2.1" + } +} diff --git a/apm-lambda-extension/e2e-testing/sam-node/sam-testing-node/tests/unit/test-handler.js b/apm-lambda-extension/e2e-testing/sam-node/sam-testing-node/tests/unit/test-handler.js new file mode 100644 index 00000000..ae94b9f2 --- /dev/null +++ b/apm-lambda-extension/e2e-testing/sam-node/sam-testing-node/tests/unit/test-handler.js @@ -0,0 +1,22 @@ +'use strict'; + +const app = require('../../app.js'); +const chai = require('chai'); +const expect = chai.expect; +var event, context; + +describe('Tests index', function () { + it('verifies successful response', async () => { + const result = await app.lambdaHandler(event, context) + + expect(result).to.be.an('object'); + expect(result.statusCode).to.equal(200); + expect(result.body).to.be.an('string'); + + let response = JSON.parse(result.body); + + expect(response).to.be.an('object'); + expect(response.message).to.be.equal("hello world"); + // expect(response.location).to.be.an("string"); + }); +}); diff --git a/apm-lambda-extension/e2e-testing/sam-node/template.yml b/apm-lambda-extension/e2e-testing/sam-node/template.yml new file mode 100644 index 00000000..fb0c115b --- /dev/null +++ b/apm-lambda-extension/e2e-testing/sam-node/template.yml @@ -0,0 +1,53 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + sam-node + +# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst +Globals: + Function: + Timeout: 20 + +Parameters: + ApmServerURL: + Type: String + Description: APM server URL + ApmSecretToken: + Type: String + Description: APM server secret token + TestUUID: + Type: String + Description: The UUID used to verify the end-to-end test + +Resources: + ElasticAPMExtensionLayer: + Type: AWS::Serverless::LayerVersion + Properties: + ContentUri: ../../bin + LayerName: apm-lambda-extension + CompatibleArchitectures: + - x86_64 + SamTestingNode: + 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: sam-testing-node/ + Handler: app.lambdaHandler + Runtime: nodejs14.x + Layers: + - !Ref ElasticAPMExtensionLayer + Architectures: + - x86_64 + Events: + SamTest: + 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: /hello + Method: get + Environment: + Variables: + ELASTIC_APM_LAMBDA_APM_SERVER: !Ref ApmServerURL + ELASTIC_APM_SECRET_TOKEN: !Ref ApmSecretToken + ELASTIC_APM_CENTRAL_CONFIG: false + ELASTIC_APM_CLOUD_PROVIDER: none + ELASTIC_APM_SERVER_URL: http://localhost:8200 + APM_AWS_EXTENSION_TEST_UUID: !Ref TestUUID diff --git a/apm-lambda-extension/e2e-testing/sam-python/sam-testing-python/__init__.py b/apm-lambda-extension/e2e-testing/sam-python/sam-testing-python/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apm-lambda-extension/e2e-testing/sam-python/sam-testing-python/app.py b/apm-lambda-extension/e2e-testing/sam-python/sam-testing-python/app.py new file mode 100644 index 00000000..7a6ba717 --- /dev/null +++ b/apm-lambda-extension/e2e-testing/sam-python/sam-testing-python/app.py @@ -0,0 +1,20 @@ +import json +import os + +# import requests +import elasticapm +from elasticapm import capture_serverless + +@capture_serverless() +def lambda_handler(event, context): + client = elasticapm.get_client() + client.begin_transaction('logging') + elasticapm.set_transaction_name(os.environ.get('APM_AWS_EXTENSION_TEST_UUID')) + client.end_transaction("A Python Lambda function sends you its regards") + return { + "statusCode": 200, + "body": json.dumps({ + "message": "hello world", + # "location": ip.text.replace("\n", "") + }), + } diff --git a/apm-lambda-extension/e2e-testing/sam-python/sam-testing-python/requirements.txt b/apm-lambda-extension/e2e-testing/sam-python/sam-testing-python/requirements.txt new file mode 100644 index 00000000..a234dac4 --- /dev/null +++ b/apm-lambda-extension/e2e-testing/sam-python/sam-testing-python/requirements.txt @@ -0,0 +1,2 @@ +requests +elastic-apm diff --git a/apm-lambda-extension/e2e-testing/sam-python/template.yml b/apm-lambda-extension/e2e-testing/sam-python/template.yml new file mode 100644 index 00000000..796c39d7 --- /dev/null +++ b/apm-lambda-extension/e2e-testing/sam-python/template.yml @@ -0,0 +1,55 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + sam-python + + Sample SAM Template for sam-python + +# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst +Globals: + Function: + Timeout: 20 + +Parameters: + ApmServerURL: + Type: String + Description: APM server URL + ApmSecretToken: + Type: String + Description: APM server secret token + TestUUID: + Type: String + Description: The UUID used to verify the end-to-end test + +Resources: + ElasticAPMExtensionLayer: + Type: AWS::Serverless::LayerVersion + Properties: + ContentUri: ../../bin + LayerName: apm-lambda-extension + CompatibleArchitectures: + - x86_64 + SamTestingPython: + 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: sam-testing-python/ + Handler: app.lambda_handler + Runtime: python3.9 + Architectures: + - x86_64 + Layers: + - !Ref ElasticAPMExtensionLayer + Events: + SamTest: + 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: /hello + Method: get + Environment: + Variables: + ELASTIC_APM_LAMBDA_APM_SERVER: !Ref ApmServerURL + ELASTIC_APM_SECRET_TOKEN: !Ref ApmSecretToken + ELASTIC_APM_CENTRAL_CONFIG: false + ELASTIC_APM_CLOUD_PROVIDER: none + ELASTIC_APM_SERVER_URL: http://localhost:8200 + APM_AWS_EXTENSION_TEST_UUID: !Ref TestUUID diff --git a/apm-lambda-extension/go.mod b/apm-lambda-extension/go.mod index 997209eb..9ed56eb8 100644 --- a/apm-lambda-extension/go.mod +++ b/apm-lambda-extension/go.mod @@ -3,7 +3,10 @@ module elastic/apm-lambda-extension go 1.14 require ( + github.com/elastic/go-elasticsearch/v8 v8.0.0-alpha // indirect + github.com/joho/godotenv v1.4.0 github.com/google/go-cmp v0.5.6 // indirect + github.com/google/uuid v1.3.0 // indirect github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.7.0 gotest.tools v2.2.0+incompatible diff --git a/apm-lambda-extension/go.sum b/apm-lambda-extension/go.sum index 14f1f2b5..5164d4b5 100644 --- a/apm-lambda-extension/go.sum +++ b/apm-lambda-extension/go.sum @@ -1,7 +1,15 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/elastic/elastic-transport-go/v8 v8.0.0-alpha h1:SW9xcMVxx4Nv9oRm5rQxzAMAatwiZV8xROP2a48y45Q= +github.com/elastic/elastic-transport-go/v8 v8.0.0-alpha/go.mod h1:87Tcz8IVNe6rVSLdBux1o/PEItLtyabHU3naC7IoqKI= +github.com/elastic/go-elasticsearch/v8 v8.0.0-alpha h1:lQmwwP38zQ9z4rGg8cNr1A4EMJ0wjxebi3j5EQYmR0Q= +github.com/elastic/go-elasticsearch/v8 v8.0.0-alpha/go.mod h1:8NCWP26meGbncX+R9sxo2JD8IqBjRTuS7yXMstHpd40= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= +github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= 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= From 3457f8033ca18c42bb3846bb1788895b366c5b32 Mon Sep 17 00:00:00 2001 From: Jean-Louis Voiseux <48380853+jlvoiseux@users.noreply.github.com> Date: Wed, 26 Jan 2022 14:29:54 +0100 Subject: [PATCH 02/20] Change current transaction name instead of creating a new one Also added an env variable to toggle output, and modified the test flow to make sure that the right permissions are always set for the java agent layer --- apm-lambda-extension/e2e-testing/e2e_test.go | 17 ++++++++++------- .../src/main/java/samtestingjava/App.java | 4 +--- .../sam-node/sam-testing-node/app.js | 3 +-- .../sam-python/sam-testing-python/app.py | 3 --- 4 files changed, 12 insertions(+), 15 deletions(-) diff --git a/apm-lambda-extension/e2e-testing/e2e_test.go b/apm-lambda-extension/e2e-testing/e2e_test.go index 2aecf012..27fe2527 100644 --- a/apm-lambda-extension/e2e-testing/e2e_test.go +++ b/apm-lambda-extension/e2e-testing/e2e_test.go @@ -50,19 +50,20 @@ func TestEndToEndExtensionBehavior(t *testing.T) { } buildExtensionBinaries() - - assert.True(t, runTest("sam-node", "SamTestingNode", forceBuildLambdasFlag)) - assert.True(t, runTest("sam-python", "SamTestingPython", forceBuildLambdasFlag)) if !folderExists(filepath.Join("sam-java", "agent")) { log.Println("Java agent not found ! Collecting archive from Github...") retrieveJavaAgent("sam-java") } + changeJavaAgentPermissions("sam-java") + + assert.True(t, runTest("sam-node", "SamTestingNode", forceBuildLambdasFlag)) + assert.True(t, runTest("sam-python", "SamTestingPython", forceBuildLambdasFlag)) assert.True(t, runTest("sam-java", "SamTestingJava", forceBuildLambdasFlag)) } func buildExtensionBinaries() { - runCommandInDir("make", []string{}, "..", false) + runCommandInDir("make", []string{}, "..", os.Getenv("DEBUG_OUTPUT") == "true") } func runTest(path string, serviceName string, buildFlag bool) bool { @@ -70,7 +71,7 @@ func runTest(path string, serviceName string, buildFlag bool) bool { if !folderExists(filepath.Join(path, ".aws-sam")) || buildFlag { log.Printf("Building the Lambda function %s", serviceName) - runCommandInDir("sam", []string{"build"}, path, false) + runCommandInDir("sam", []string{"build"}, path, os.Getenv("DEBUG_OUTPUT") == "true") } log.Printf("Invoking the Lambda function %s", serviceName) @@ -79,7 +80,7 @@ func runTest(path string, serviceName string, buildFlag bool) bool { fmt.Sprintf("ParameterKey=ApmServerURL,ParameterValue=%s", os.Getenv("ELASTIC_APM_SERVER_URL")), fmt.Sprintf("ParameterKey=ApmSecretToken,ParameterValue=%s", os.Getenv("ELASTIC_APM_SERVER_TOKEN")), fmt.Sprintf("ParameterKey=TestUUID,ParameterValue=%s", uuidWithHyphen)}, - path, false) + path, os.Getenv("DEBUG_OUTPUT") == "true") log.Printf("%s execution complete", serviceName) log.Printf("Querying Elasticsearch for transaction %s bound to %s...", uuidWithHyphen, serviceName) @@ -111,8 +112,10 @@ func retrieveJavaAgent(samJavaPath string) { unzip(agentArchivePath, agentFolderPath) err = os.Remove(agentArchivePath) processError(err) +} - // Change permissions +func changeJavaAgentPermissions(samJavaPath string) { + agentFolderPath := filepath.Join(samJavaPath, "agent") log.Println("Setting appropriate permissions for Java agent files...") agentFiles, err := ioutil.ReadDir(agentFolderPath) processError(err) diff --git a/apm-lambda-extension/e2e-testing/sam-java/sam-testing-java/src/main/java/samtestingjava/App.java b/apm-lambda-extension/e2e-testing/sam-java/sam-testing-java/src/main/java/samtestingjava/App.java index 5c70fb12..88cebf26 100644 --- a/apm-lambda-extension/e2e-testing/sam-java/sam-testing-java/src/main/java/samtestingjava/App.java +++ b/apm-lambda-extension/e2e-testing/sam-java/sam-testing-java/src/main/java/samtestingjava/App.java @@ -26,9 +26,7 @@ public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEv headers.put("Content-Type", "application/json"); headers.put("X-Custom-Header", "application/json"); - Transaction transaction = ElasticApm.startTransaction(); - transaction.setName(System.getenv("APM_AWS_EXTENSION_TEST_UUID")); - transaction.end(); + ElasticApm.currentTransaction().setName(System.getenv("APM_AWS_EXTENSION_TEST_UUID")); APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent() .withHeaders(headers); diff --git a/apm-lambda-extension/e2e-testing/sam-node/sam-testing-node/app.js b/apm-lambda-extension/e2e-testing/sam-node/sam-testing-node/app.js index 23938d27..67044976 100644 --- a/apm-lambda-extension/e2e-testing/sam-node/sam-testing-node/app.js +++ b/apm-lambda-extension/e2e-testing/sam-node/sam-testing-node/app.js @@ -15,7 +15,7 @@ let response; * */ exports.lambdaHandler = apm.lambda(async (event, context) => { - apm.startTransaction(process.env.APM_AWS_EXTENSION_TEST_UUID) + apm.setTransactionName(process.env.APM_AWS_EXTENSION_TEST_UUID) try { response = { 'statusCode': 200, @@ -27,6 +27,5 @@ exports.lambdaHandler = apm.lambda(async (event, context) => { console.log(err); return err; } - apm.endTransaction() return response }); diff --git a/apm-lambda-extension/e2e-testing/sam-python/sam-testing-python/app.py b/apm-lambda-extension/e2e-testing/sam-python/sam-testing-python/app.py index 7a6ba717..cf356f37 100644 --- a/apm-lambda-extension/e2e-testing/sam-python/sam-testing-python/app.py +++ b/apm-lambda-extension/e2e-testing/sam-python/sam-testing-python/app.py @@ -7,10 +7,7 @@ @capture_serverless() def lambda_handler(event, context): - client = elasticapm.get_client() - client.begin_transaction('logging') elasticapm.set_transaction_name(os.environ.get('APM_AWS_EXTENSION_TEST_UUID')) - client.end_transaction("A Python Lambda function sends you its regards") return { "statusCode": 200, "body": json.dumps({ From 8e955a7e3dff01dbceabbd2c294130761b9851de Mon Sep 17 00:00:00 2001 From: Jean-Louis Voiseux <48380853+jlvoiseux@users.noreply.github.com> Date: Wed, 26 Jan 2022 15:21:25 +0100 Subject: [PATCH 03/20] Parallelize Lambda execution --- apm-lambda-extension/e2e-testing/e2e_test.go | 30 ++++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/apm-lambda-extension/e2e-testing/e2e_test.go b/apm-lambda-extension/e2e-testing/e2e_test.go index 27fe2527..046c42c2 100644 --- a/apm-lambda-extension/e2e-testing/e2e_test.go +++ b/apm-lambda-extension/e2e-testing/e2e_test.go @@ -17,6 +17,7 @@ import ( "os/exec" "path/filepath" "strings" + "sync" "testing" ) @@ -56,9 +57,26 @@ func TestEndToEndExtensionBehavior(t *testing.T) { } changeJavaAgentPermissions("sam-java") - assert.True(t, runTest("sam-node", "SamTestingNode", forceBuildLambdasFlag)) - assert.True(t, runTest("sam-python", "SamTestingPython", forceBuildLambdasFlag)) - assert.True(t, runTest("sam-java", "SamTestingJava", forceBuildLambdasFlag)) + if os.Getenv("PARALLEL_EXECUTION") == "true" { + var waitGroup sync.WaitGroup + waitGroup.Add(3) + + cNode := make(chan bool) + go runTestAsync("sam-node", "SamTestingNode", forceBuildLambdasFlag, &waitGroup, cNode) + cPython := make(chan bool) + go runTestAsync("sam-python", "SamTestingPython", forceBuildLambdasFlag, &waitGroup, cPython) + cJava := make(chan bool) + go runTestAsync("sam-java", "SamTestingJava", forceBuildLambdasFlag, &waitGroup, cJava) + assert.True(t, <-cNode) + assert.True(t, <-cPython) + assert.True(t, <-cJava) + waitGroup.Wait() + + } else { + assert.True(t, runTest("sam-node", "SamTestingNode", forceBuildLambdasFlag)) + assert.True(t, runTest("sam-python", "SamTestingPython", forceBuildLambdasFlag)) + assert.True(t, runTest("sam-java", "SamTestingJava", forceBuildLambdasFlag)) + } } @@ -93,6 +111,12 @@ func runTest(path string, serviceName string, buildFlag bool) bool { return false } +func runTestAsync(path string, serviceName string, buildFlag bool, wg *sync.WaitGroup, c chan bool) { + defer wg.Done() + out := runTest(path, serviceName, buildFlag) + c <- out +} + func retrieveJavaAgent(samJavaPath string) { agentFolderPath := filepath.Join(samJavaPath, "agent") From cdffa11ecf16eb3d9aa2af16b7baac66546d3871 Mon Sep 17 00:00:00 2001 From: Jean-Louis Voiseux <48380853+jlvoiseux@users.noreply.github.com> Date: Wed, 26 Jan 2022 19:51:09 +0100 Subject: [PATCH 04/20] Replace Elasticsearch by a mock APM server --- apm-lambda-extension/e2e-testing/e2e_test.go | 145 +++++++----------- .../e2e-testing/sam-java/template.yml | 5 +- .../e2e-testing/sam-node/template.yml | 5 +- .../e2e-testing/sam-python/template.yml | 5 +- 4 files changed, 58 insertions(+), 102 deletions(-) diff --git a/apm-lambda-extension/e2e-testing/e2e_test.go b/apm-lambda-extension/e2e-testing/e2e_test.go index 046c42c2..4c730c58 100644 --- a/apm-lambda-extension/e2e-testing/e2e_test.go +++ b/apm-lambda-extension/e2e-testing/e2e_test.go @@ -3,9 +3,10 @@ package e2e_testing import ( "archive/zip" "bufio" - "encoding/json" + "bytes" + "compress/gzip" + "encoding/base64" "fmt" - "github.com/elastic/go-elasticsearch/v8" "github.com/google/uuid" "github.com/joho/godotenv" "github.com/stretchr/testify/assert" @@ -13,6 +14,7 @@ import ( "io/ioutil" "log" "net/http" + "net/http/httptest" "os" "os/exec" "path/filepath" @@ -29,27 +31,12 @@ func TestEndToEndExtensionBehavior(t *testing.T) { if os.Getenv("RUN_E2E_TESTS") != "true" { t.Skip("Skipping E2E tests. Please set the env. variable RUN_E2E_TESTS=true if you want to run them.") } - if os.Getenv("ELASTIC_CLOUD_ID") == "" { - log.Println("Please set the ELASTIC_CLOUD_ID environment variable") - os.Exit(1) - } - if os.Getenv("ELASTIC_CLOUD_API_KEY") == "" { - log.Println("Please set the ELASTIC_CLOUD_API_KEY environment variable") - os.Exit(1) - } - if os.Getenv("ELASTIC_APM_SERVER_URL") == "" { - log.Println("Please set the ELASTIC_APM_SERVER_URL environment variable") - os.Exit(1) - } - if os.Getenv("ELASTIC_APM_SERVER_TOKEN") == "" { - log.Println("Please set the ELASTIC_APM_SERVER_TOKEN environment variable") - os.Exit(1) - } forceBuildLambdasFlag := false if os.Getenv("FORCE_REBUILD_LOCAL_SAM_LAMBDAS") == "true" { forceBuildLambdasFlag = true } + // Build and download required binaries (extension and Java agent) buildExtensionBinaries() if !folderExists(filepath.Join("sam-java", "agent")) { log.Println("Java agent not found ! Collecting archive from Github...") @@ -57,34 +44,53 @@ func TestEndToEndExtensionBehavior(t *testing.T) { } changeJavaAgentPermissions("sam-java") + mockAPMServerLog := "" + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.RequestURI == "/intake/v2/events" { + mockAPMServerLog += decodeRequest(r) + } + })) + defer ts.Close() + if os.Getenv("PARALLEL_EXECUTION") == "true" { var waitGroup sync.WaitGroup waitGroup.Add(3) - cNode := make(chan bool) - go runTestAsync("sam-node", "SamTestingNode", forceBuildLambdasFlag, &waitGroup, cNode) - cPython := make(chan bool) - go runTestAsync("sam-python", "SamTestingPython", forceBuildLambdasFlag, &waitGroup, cPython) - cJava := make(chan bool) - go runTestAsync("sam-java", "SamTestingJava", forceBuildLambdasFlag, &waitGroup, cJava) - assert.True(t, <-cNode) - assert.True(t, <-cPython) - assert.True(t, <-cJava) + cNode := make(chan string) + go runTestAsync("sam-node", "SamTestingNode", ts.URL, forceBuildLambdasFlag, &waitGroup, cNode) + cPython := make(chan string) + go runTestAsync("sam-python", "SamTestingPython", ts.URL, forceBuildLambdasFlag, &waitGroup, cPython) + cJava := make(chan string) + go runTestAsync("sam-java", "SamTestingJava", ts.URL, forceBuildLambdasFlag, &waitGroup, cJava) + uuidNode := <-cNode + uuidPython := <-cPython + uuidJava := <-cJava + log.Printf("Querying the mock server for transaction %s bound to %s...", uuidNode, "SamTestingNode") + assert.True(t, strings.Contains(mockAPMServerLog, uuidNode)) + log.Printf("Querying the mock server for transaction %s bound to %s...", uuidPython, "SamTestingPython") + assert.True(t, strings.Contains(mockAPMServerLog, uuidPython)) + log.Printf("Querying the mock server for transaction %s bound to %s...", uuidJava, "SamTestingJava") + assert.True(t, strings.Contains(mockAPMServerLog, uuidJava)) waitGroup.Wait() } else { - assert.True(t, runTest("sam-node", "SamTestingNode", forceBuildLambdasFlag)) - assert.True(t, runTest("sam-python", "SamTestingPython", forceBuildLambdasFlag)) - assert.True(t, runTest("sam-java", "SamTestingJava", forceBuildLambdasFlag)) + uuidNode := runTest("sam-node", "SamTestingNode", ts.URL, forceBuildLambdasFlag) + uuidPython := runTest("sam-python", "SamTestingPython", ts.URL, forceBuildLambdasFlag) + uuidJava := runTest("sam-java", "SamTestingJava", ts.URL, forceBuildLambdasFlag) + log.Printf("Querying the mock server logs for transaction %s bound to %s...", uuidNode, "SamTestingNode") + assert.True(t, strings.Contains(mockAPMServerLog, uuidNode)) + log.Printf("Querying the mock server logs for transaction %s bound to %s...", uuidPython, "SamTestingPython") + assert.True(t, strings.Contains(mockAPMServerLog, uuidPython)) + log.Printf("Querying the mock server logs for transaction %s bound to %s...", uuidJava, "SamTestingJava") + assert.True(t, strings.Contains(mockAPMServerLog, uuidJava)) } - } func buildExtensionBinaries() { runCommandInDir("make", []string{}, "..", os.Getenv("DEBUG_OUTPUT") == "true") } -func runTest(path string, serviceName string, buildFlag bool) bool { +func runTest(path string, serviceName string, serverURL string, buildFlag bool) string { log.Printf("Starting to test %s", serviceName) if !folderExists(filepath.Join(path, ".aws-sam")) || buildFlag { @@ -94,26 +100,20 @@ func runTest(path string, serviceName string, buildFlag bool) bool { log.Printf("Invoking the Lambda function %s", serviceName) uuidWithHyphen := uuid.New().String() + urlSlice := strings.Split(serverURL, ":") + port := urlSlice[len(urlSlice)-1] runCommandInDir("sam", []string{"local", "invoke", "--parameter-overrides", - fmt.Sprintf("ParameterKey=ApmServerURL,ParameterValue=%s", os.Getenv("ELASTIC_APM_SERVER_URL")), - fmt.Sprintf("ParameterKey=ApmSecretToken,ParameterValue=%s", os.Getenv("ELASTIC_APM_SERVER_TOKEN")), + fmt.Sprintf("ParameterKey=ApmServerURL,ParameterValue=http://host.docker.internal:%s", port), fmt.Sprintf("ParameterKey=TestUUID,ParameterValue=%s", uuidWithHyphen)}, path, os.Getenv("DEBUG_OUTPUT") == "true") log.Printf("%s execution complete", serviceName) - log.Printf("Querying Elasticsearch for transaction %s bound to %s...", uuidWithHyphen, serviceName) - res := queryElasticsearch(serviceName, uuidWithHyphen) - if res { - log.Printf("SUCCESS : Transaction %s successfully sent to Elasticsearch by %s", uuidWithHyphen, serviceName) - return true - } - log.Printf("FAILURE : Transaction %s bound to %s not found in Elasticsearch", uuidWithHyphen, serviceName) - return false + return uuidWithHyphen } -func runTestAsync(path string, serviceName string, buildFlag bool, wg *sync.WaitGroup, c chan bool) { +func runTestAsync(path string, serviceName string, serverURL string, buildFlag bool, wg *sync.WaitGroup, c chan string) { defer wg.Done() - out := runTest(path, serviceName, buildFlag) + out := runTest(path, serviceName, serverURL, buildFlag) c <- out } @@ -148,52 +148,6 @@ func changeJavaAgentPermissions(samJavaPath string) { } } -func queryElasticsearch(serviceName string, transactionName string) bool { - - cfg := elasticsearch.Config{ - CloudID: os.Getenv("ELASTIC_CLOUD_ID"), - APIKey: os.Getenv("ELASTIC_CLOUD_API_KEY"), - } - - es, err := elasticsearch.NewClient(cfg) - - body := fmt.Sprintf(`{ - "query": { - "bool": { - "must": [ - { - "match": { - "service.name": "%s" - } - }, - { - "match": { - "transaction.name": "%s" - } - } - ] - } - } - }`, serviceName, transactionName) - - res, err := es.Count( - es.Count.WithIndex("apm-*-transaction-*"), - es.Count.WithBody(strings.NewReader(body)), - es.Count.WithPretty(), - ) - processError(err) - defer res.Body.Close() - - var r map[string]interface{} - err = json.NewDecoder(res.Body).Decode(&r) - processError(err) - hitsNum := int(r["count"].(float64)) - if hitsNum > 0 { - return true - } - return false -} - func runCommandInDir(command string, args []string, dir string, printOutput bool) { e := exec.Command(command, args...) if printOutput { @@ -284,3 +238,14 @@ func unzip(archivePath string, destinationFolderPath string) { processError(err) } } + +func decodeRequest(r *http.Request) string { + buf := new(bytes.Buffer) + buf.ReadFrom(r.Body) + str := base64.StdEncoding.EncodeToString(buf.Bytes()) + data, _ := base64.StdEncoding.DecodeString(str) + rdata := bytes.NewReader(data) + reader, _ := gzip.NewReader(rdata) + s, _ := ioutil.ReadAll(reader) + return string(s) +} diff --git a/apm-lambda-extension/e2e-testing/sam-java/template.yml b/apm-lambda-extension/e2e-testing/sam-java/template.yml index f3e799b9..379c5f41 100644 --- a/apm-lambda-extension/e2e-testing/sam-java/template.yml +++ b/apm-lambda-extension/e2e-testing/sam-java/template.yml @@ -14,9 +14,6 @@ Parameters: ApmServerURL: Type: String Description: APM server URL - ApmSecretToken: - Type: String - Description: APM server secret token TestUUID: Type: String Description: The UUID used to verify the end-to-end test @@ -57,7 +54,7 @@ Resources: Environment: Variables: ELASTIC_APM_LAMBDA_APM_SERVER: !Ref ApmServerURL - ELASTIC_APM_SECRET_TOKEN: !Ref ApmSecretToken + ELASTIC_APM_SECRET_TOKEN: none ELASTIC_APM_CENTRAL_CONFIG: false ELASTIC_APM_CLOUD_PROVIDER: none ELASTIC_APM_SERVER_URL: http://localhost:8200 diff --git a/apm-lambda-extension/e2e-testing/sam-node/template.yml b/apm-lambda-extension/e2e-testing/sam-node/template.yml index fb0c115b..a333ae48 100644 --- a/apm-lambda-extension/e2e-testing/sam-node/template.yml +++ b/apm-lambda-extension/e2e-testing/sam-node/template.yml @@ -12,9 +12,6 @@ Parameters: ApmServerURL: Type: String Description: APM server URL - ApmSecretToken: - Type: String - Description: APM server secret token TestUUID: Type: String Description: The UUID used to verify the end-to-end test @@ -46,7 +43,7 @@ Resources: Environment: Variables: ELASTIC_APM_LAMBDA_APM_SERVER: !Ref ApmServerURL - ELASTIC_APM_SECRET_TOKEN: !Ref ApmSecretToken + ELASTIC_APM_SECRET_TOKEN: none ELASTIC_APM_CENTRAL_CONFIG: false ELASTIC_APM_CLOUD_PROVIDER: none ELASTIC_APM_SERVER_URL: http://localhost:8200 diff --git a/apm-lambda-extension/e2e-testing/sam-python/template.yml b/apm-lambda-extension/e2e-testing/sam-python/template.yml index 796c39d7..af86f0be 100644 --- a/apm-lambda-extension/e2e-testing/sam-python/template.yml +++ b/apm-lambda-extension/e2e-testing/sam-python/template.yml @@ -14,9 +14,6 @@ Parameters: ApmServerURL: Type: String Description: APM server URL - ApmSecretToken: - Type: String - Description: APM server secret token TestUUID: Type: String Description: The UUID used to verify the end-to-end test @@ -48,7 +45,7 @@ Resources: Environment: Variables: ELASTIC_APM_LAMBDA_APM_SERVER: !Ref ApmServerURL - ELASTIC_APM_SECRET_TOKEN: !Ref ApmSecretToken + ELASTIC_APM_SECRET_TOKEN: none ELASTIC_APM_CENTRAL_CONFIG: false ELASTIC_APM_CLOUD_PROVIDER: none ELASTIC_APM_SERVER_URL: http://localhost:8200 From 6af16f5721b768b35d5b6515640a14c5c85e7d05 Mon Sep 17 00:00:00 2001 From: Jean-Louis Voiseux <48380853+jlvoiseux@users.noreply.github.com> Date: Thu, 27 Jan 2022 17:52:41 +0100 Subject: [PATCH 05/20] Cleanup env variable checks and go.mod --- apm-lambda-extension/e2e-testing/e2e_test.go | 11 ++++++----- apm-lambda-extension/go.mod | 5 ++--- apm-lambda-extension/go.sum | 4 ---- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/apm-lambda-extension/e2e-testing/e2e_test.go b/apm-lambda-extension/e2e-testing/e2e_test.go index 4c730c58..175253c3 100644 --- a/apm-lambda-extension/e2e-testing/e2e_test.go +++ b/apm-lambda-extension/e2e-testing/e2e_test.go @@ -25,16 +25,15 @@ import ( func TestEndToEndExtensionBehavior(t *testing.T) { + // Check the only mandatory environment variable if err := godotenv.Load(); err != nil { - t.Skip("No .env file found alongside e2e_test.go : Skipping end-to-end tests.") + log.Println("No additional .env file found") } if os.Getenv("RUN_E2E_TESTS") != "true" { t.Skip("Skipping E2E tests. Please set the env. variable RUN_E2E_TESTS=true if you want to run them.") } - forceBuildLambdasFlag := false - if os.Getenv("FORCE_REBUILD_LOCAL_SAM_LAMBDAS") == "true" { - forceBuildLambdasFlag = true - } + + forceBuildLambdasFlag := os.Getenv("FORCE_REBUILD_LOCAL_SAM_LAMBDAS") == "true" // Build and download required binaries (extension and Java agent) buildExtensionBinaries() @@ -44,6 +43,7 @@ func TestEndToEndExtensionBehavior(t *testing.T) { } changeJavaAgentPermissions("sam-java") + // Initialize Mock APM Server mockAPMServerLog := "" ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.RequestURI == "/intake/v2/events" { @@ -62,6 +62,7 @@ func TestEndToEndExtensionBehavior(t *testing.T) { go runTestAsync("sam-python", "SamTestingPython", ts.URL, forceBuildLambdasFlag, &waitGroup, cPython) cJava := make(chan string) go runTestAsync("sam-java", "SamTestingJava", ts.URL, forceBuildLambdasFlag, &waitGroup, cJava) + // Retrieve the UUID generated by the tests though dedicated channels uuidNode := <-cNode uuidPython := <-cPython uuidJava := <-cJava diff --git a/apm-lambda-extension/go.mod b/apm-lambda-extension/go.mod index 9ed56eb8..df316326 100644 --- a/apm-lambda-extension/go.mod +++ b/apm-lambda-extension/go.mod @@ -3,10 +3,9 @@ module elastic/apm-lambda-extension go 1.14 require ( - github.com/elastic/go-elasticsearch/v8 v8.0.0-alpha // indirect - github.com/joho/godotenv v1.4.0 github.com/google/go-cmp v0.5.6 // indirect - github.com/google/uuid v1.3.0 // indirect + github.com/google/uuid v1.3.0 + github.com/joho/godotenv v1.4.0 github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.7.0 gotest.tools v2.2.0+incompatible diff --git a/apm-lambda-extension/go.sum b/apm-lambda-extension/go.sum index 5164d4b5..5b3eec01 100644 --- a/apm-lambda-extension/go.sum +++ b/apm-lambda-extension/go.sum @@ -1,9 +1,5 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/elastic/elastic-transport-go/v8 v8.0.0-alpha h1:SW9xcMVxx4Nv9oRm5rQxzAMAatwiZV8xROP2a48y45Q= -github.com/elastic/elastic-transport-go/v8 v8.0.0-alpha/go.mod h1:87Tcz8IVNe6rVSLdBux1o/PEItLtyabHU3naC7IoqKI= -github.com/elastic/go-elasticsearch/v8 v8.0.0-alpha h1:lQmwwP38zQ9z4rGg8cNr1A4EMJ0wjxebi3j5EQYmR0Q= -github.com/elastic/go-elasticsearch/v8 v8.0.0-alpha/go.mod h1:8NCWP26meGbncX+R9sxo2JD8IqBjRTuS7yXMstHpd40= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= From 85e22f9159b40c8adf088934c7e8a21432bb1d10 Mon Sep 17 00:00:00 2001 From: Jean-Louis Voiseux <48380853+jlvoiseux@users.noreply.github.com> Date: Mon, 31 Jan 2022 15:50:06 +0100 Subject: [PATCH 06/20] Change test name and write Lambda paths as variables --- apm-lambda-extension/e2e-testing/e2e_test.go | 23 +++++++++++--------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/apm-lambda-extension/e2e-testing/e2e_test.go b/apm-lambda-extension/e2e-testing/e2e_test.go index 175253c3..ca5995c3 100644 --- a/apm-lambda-extension/e2e-testing/e2e_test.go +++ b/apm-lambda-extension/e2e-testing/e2e_test.go @@ -23,7 +23,7 @@ import ( "testing" ) -func TestEndToEndExtensionBehavior(t *testing.T) { +func TestEndToEnd(t *testing.T) { // Check the only mandatory environment variable if err := godotenv.Load(); err != nil { @@ -34,14 +34,17 @@ func TestEndToEndExtensionBehavior(t *testing.T) { } forceBuildLambdasFlag := os.Getenv("FORCE_REBUILD_LOCAL_SAM_LAMBDAS") == "true" + samNodePath := "sam-node" + samPythonPath := "sam-python" + samJavaPath := "sam-java" // Build and download required binaries (extension and Java agent) buildExtensionBinaries() - if !folderExists(filepath.Join("sam-java", "agent")) { + if !folderExists(filepath.Join(samJavaPath, "agent")) { log.Println("Java agent not found ! Collecting archive from Github...") - retrieveJavaAgent("sam-java") + retrieveJavaAgent(samJavaPath) } - changeJavaAgentPermissions("sam-java") + changeJavaAgentPermissions(samJavaPath) // Initialize Mock APM Server mockAPMServerLog := "" @@ -57,11 +60,11 @@ func TestEndToEndExtensionBehavior(t *testing.T) { waitGroup.Add(3) cNode := make(chan string) - go runTestAsync("sam-node", "SamTestingNode", ts.URL, forceBuildLambdasFlag, &waitGroup, cNode) + go runTestAsync(samNodePath, "SamTestingNode", ts.URL, forceBuildLambdasFlag, &waitGroup, cNode) cPython := make(chan string) - go runTestAsync("sam-python", "SamTestingPython", ts.URL, forceBuildLambdasFlag, &waitGroup, cPython) + go runTestAsync(samPythonPath, "SamTestingPython", ts.URL, forceBuildLambdasFlag, &waitGroup, cPython) cJava := make(chan string) - go runTestAsync("sam-java", "SamTestingJava", ts.URL, forceBuildLambdasFlag, &waitGroup, cJava) + go runTestAsync(samJavaPath, "SamTestingJava", ts.URL, forceBuildLambdasFlag, &waitGroup, cJava) // Retrieve the UUID generated by the tests though dedicated channels uuidNode := <-cNode uuidPython := <-cPython @@ -75,9 +78,9 @@ func TestEndToEndExtensionBehavior(t *testing.T) { waitGroup.Wait() } else { - uuidNode := runTest("sam-node", "SamTestingNode", ts.URL, forceBuildLambdasFlag) - uuidPython := runTest("sam-python", "SamTestingPython", ts.URL, forceBuildLambdasFlag) - uuidJava := runTest("sam-java", "SamTestingJava", ts.URL, forceBuildLambdasFlag) + uuidNode := runTest(samNodePath, "SamTestingNode", ts.URL, forceBuildLambdasFlag) + uuidPython := runTest(samPythonPath, "SamTestingPython", ts.URL, forceBuildLambdasFlag) + uuidJava := runTest(samJavaPath, "SamTestingJava", ts.URL, forceBuildLambdasFlag) log.Printf("Querying the mock server logs for transaction %s bound to %s...", uuidNode, "SamTestingNode") assert.True(t, strings.Contains(mockAPMServerLog, uuidNode)) log.Printf("Querying the mock server logs for transaction %s bound to %s...", uuidPython, "SamTestingPython") From 0495757554a313b3f3715dcdfb65bb4fcac3669f Mon Sep 17 00:00:00 2001 From: Jean-Louis Voiseux <48380853+jlvoiseux@users.noreply.github.com> Date: Mon, 31 Jan 2022 15:59:50 +0100 Subject: [PATCH 07/20] Make the Java APM Agent version an env. variable --- apm-lambda-extension/e2e-testing/e2e_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apm-lambda-extension/e2e-testing/e2e_test.go b/apm-lambda-extension/e2e-testing/e2e_test.go index ca5995c3..a212b1c3 100644 --- a/apm-lambda-extension/e2e-testing/e2e_test.go +++ b/apm-lambda-extension/e2e-testing/e2e_test.go @@ -130,7 +130,8 @@ func retrieveJavaAgent(samJavaPath string) { out, err := os.Create(agentArchivePath) processError(err) defer out.Close() - resp, err := http.Get("https://github.com/elastic/apm-agent-java/releases/download/v1.28.4/elastic-apm-java-aws-lambda-layer-1.28.4.zip") + resp, err := http.Get(fmt.Sprintf("https://github.com/elastic/apm-agent-java/releases/download/v%[1]s/elastic-apm-java-aws-lambda-layer-%[1]s.zip", + os.Getenv("APM_AGENT_JAVA_VERSION"))) processError(err) defer resp.Body.Close() io.Copy(out, resp.Body) From 680d8d3f6f0cb6376949dc56360203230bb0ed74 Mon Sep 17 00:00:00 2001 From: Jean-Louis Voiseux <48380853+jlvoiseux@users.noreply.github.com> Date: Mon, 31 Jan 2022 16:05:16 +0100 Subject: [PATCH 08/20] Remove test files and fix folder detection --- apm-lambda-extension/e2e-testing/e2e_test.go | 3 --- .../src/test/java/helloworld/AppTest.java | 22 ------------------- .../tests/unit/test-handler.js | 22 ------------------- 3 files changed, 47 deletions(-) delete mode 100644 apm-lambda-extension/e2e-testing/sam-java/sam-testing-java/src/test/java/helloworld/AppTest.java delete mode 100644 apm-lambda-extension/e2e-testing/sam-node/sam-testing-node/tests/unit/test-handler.js diff --git a/apm-lambda-extension/e2e-testing/e2e_test.go b/apm-lambda-extension/e2e-testing/e2e_test.go index a212b1c3..87d4f9c3 100644 --- a/apm-lambda-extension/e2e-testing/e2e_test.go +++ b/apm-lambda-extension/e2e-testing/e2e_test.go @@ -185,9 +185,6 @@ func folderExists(path string) bool { if err == nil { return true } - if os.IsNotExist(err) { - return false - } return false } diff --git a/apm-lambda-extension/e2e-testing/sam-java/sam-testing-java/src/test/java/helloworld/AppTest.java b/apm-lambda-extension/e2e-testing/sam-java/sam-testing-java/src/test/java/helloworld/AppTest.java deleted file mode 100644 index 8c1bfad4..00000000 --- a/apm-lambda-extension/e2e-testing/sam-java/sam-testing-java/src/test/java/helloworld/AppTest.java +++ /dev/null @@ -1,22 +0,0 @@ -package samtestingjava; - -import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import org.junit.Test; - -public class AppTest { - @Test - public void successfulResponse() { - App app = new App(); - APIGatewayProxyResponseEvent result = app.handleRequest(null, null); - assertEquals(200, result.getStatusCode().intValue()); - assertEquals("application/json", result.getHeaders().get("Content-Type")); - String content = result.getBody(); - assertNotNull(content); - assertTrue(content.contains("\"message\"")); - assertTrue(content.contains("\"hello world\"")); - assertTrue(content.contains("\"location\"")); - } -} diff --git a/apm-lambda-extension/e2e-testing/sam-node/sam-testing-node/tests/unit/test-handler.js b/apm-lambda-extension/e2e-testing/sam-node/sam-testing-node/tests/unit/test-handler.js deleted file mode 100644 index ae94b9f2..00000000 --- a/apm-lambda-extension/e2e-testing/sam-node/sam-testing-node/tests/unit/test-handler.js +++ /dev/null @@ -1,22 +0,0 @@ -'use strict'; - -const app = require('../../app.js'); -const chai = require('chai'); -const expect = chai.expect; -var event, context; - -describe('Tests index', function () { - it('verifies successful response', async () => { - const result = await app.lambdaHandler(event, context) - - expect(result).to.be.an('object'); - expect(result.statusCode).to.equal(200); - expect(result.body).to.be.an('string'); - - let response = JSON.parse(result.body); - - expect(response).to.be.an('object'); - expect(response.message).to.be.equal("hello world"); - // expect(response.location).to.be.an("string"); - }); -}); From 6f748fc6c2271d814c805b45465ef1a7bee77f2d Mon Sep 17 00:00:00 2001 From: Jean-Louis Voiseux <48380853+jlvoiseux@users.noreply.github.com> Date: Mon, 31 Jan 2022 18:40:30 +0100 Subject: [PATCH 09/20] Improve request response decoding (Based on PR #72) --- apm-lambda-extension/e2e-testing/e2e_test.go | 46 +++++++++++++++----- 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/apm-lambda-extension/e2e-testing/e2e_test.go b/apm-lambda-extension/e2e-testing/e2e_test.go index 87d4f9c3..fb4d9c7b 100644 --- a/apm-lambda-extension/e2e-testing/e2e_test.go +++ b/apm-lambda-extension/e2e-testing/e2e_test.go @@ -5,7 +5,7 @@ import ( "bufio" "bytes" "compress/gzip" - "encoding/base64" + "compress/zlib" "fmt" "github.com/google/uuid" "github.com/joho/godotenv" @@ -50,7 +50,8 @@ func TestEndToEnd(t *testing.T) { mockAPMServerLog := "" ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.RequestURI == "/intake/v2/events" { - mockAPMServerLog += decodeRequest(r) + bytesRes, _ := getDecompressedBytesFromRequest(r) + mockAPMServerLog += string(bytesRes) } })) defer ts.Close() @@ -241,13 +242,36 @@ func unzip(archivePath string, destinationFolderPath string) { } } -func decodeRequest(r *http.Request) string { - buf := new(bytes.Buffer) - buf.ReadFrom(r.Body) - str := base64.StdEncoding.EncodeToString(buf.Bytes()) - data, _ := base64.StdEncoding.DecodeString(str) - rdata := bytes.NewReader(data) - reader, _ := gzip.NewReader(rdata) - s, _ := ioutil.ReadAll(reader) - return string(s) +func getDecompressedBytesFromRequest(req *http.Request) ([]byte, error) { + var rawBytes []byte + if req.Body != nil { + rawBytes, _ = ioutil.ReadAll(req.Body) + } + + switch req.Header.Get("Content-Encoding") { + case "deflate": + reader := bytes.NewReader([]byte(rawBytes)) + zlibreader, err := zlib.NewReader(reader) + if err != nil { + return nil, fmt.Errorf("could not create zlib.NewReader: %v", err) + } + bodyBytes, err := ioutil.ReadAll(zlibreader) + if err != nil { + return nil, fmt.Errorf("could not read from zlib reader using ioutil.ReadAll: %v", err) + } + return bodyBytes, nil + case "gzip": + reader := bytes.NewReader([]byte(rawBytes)) + zlibreader, err := gzip.NewReader(reader) + if err != nil { + return nil, fmt.Errorf("could not create gzip.NewReader: %v", err) + } + bodyBytes, err := ioutil.ReadAll(zlibreader) + if err != nil { + return nil, fmt.Errorf("could not read from gzip reader using ioutil.ReadAll: %v", err) + } + return bodyBytes, nil + default: + return rawBytes, nil + } } From 523857f731aef78ce20b7e30c72d4fd578f36408 Mon Sep 17 00:00:00 2001 From: Jean-Louis Voiseux <48380853+jlvoiseux@users.noreply.github.com> Date: Tue, 1 Feb 2022 15:32:22 +0100 Subject: [PATCH 10/20] Refactor channel use to avoid test block by a single lambda --- apm-lambda-extension/e2e-testing/e2e_test.go | 150 ++++++++++++------ .../e2e-testing/sam-java/template.yml | 9 +- .../e2e-testing/sam-node/template.yml | 9 +- .../e2e-testing/sam-python/template.yml | 9 +- 4 files changed, 116 insertions(+), 61 deletions(-) diff --git a/apm-lambda-extension/e2e-testing/e2e_test.go b/apm-lambda-extension/e2e-testing/e2e_test.go index fb4d9c7b..52be2e83 100644 --- a/apm-lambda-extension/e2e-testing/e2e_test.go +++ b/apm-lambda-extension/e2e-testing/e2e_test.go @@ -18,9 +18,10 @@ import ( "os" "os/exec" "path/filepath" + "strconv" "strings" - "sync" "testing" + "time" ) func TestEndToEnd(t *testing.T) { @@ -29,17 +30,31 @@ func TestEndToEnd(t *testing.T) { if err := godotenv.Load(); err != nil { log.Println("No additional .env file found") } - if os.Getenv("RUN_E2E_TESTS") != "true" { + if getEnvVarValueOrSetDefault("RUN_E2E_TESTS", "false") != "true" { t.Skip("Skipping E2E tests. Please set the env. variable RUN_E2E_TESTS=true if you want to run them.") } - forceBuildLambdasFlag := os.Getenv("FORCE_REBUILD_LOCAL_SAM_LAMBDAS") == "true" + forceBuildLambdasFlag := getEnvVarValueOrSetDefault("FORCE_REBUILD_LOCAL_SAM_LAMBDAS", "false") == "true" samNodePath := "sam-node" + samNodeServiceName := "SamTestingNode" samPythonPath := "sam-python" + samPythonServiceName := "SamTestingPython" samJavaPath := "sam-java" + samJavaServiceName := "SamTestingJava" + + // Configure Timeouts and Tested Languages + timeoutNode, err := strconv.Atoi(getEnvVarValueOrSetDefault("TIMEOUT_NODE", "20")) + processError(err) + timeoutPython, err := strconv.Atoi(getEnvVarValueOrSetDefault("TIMEOUT_PYTHON", "20")) + processError(err) + timeoutJava, err := strconv.Atoi(getEnvVarValueOrSetDefault("TIMEOUT_JAVA", "75")) + processError(err) + timeoutMax := getMax([]int{timeoutNode, timeoutPython, timeoutJava}) // Build and download required binaries (extension and Java agent) buildExtensionBinaries() + + // Java agent processing if !folderExists(filepath.Join(samJavaPath, "agent")) { log.Println("Java agent not found ! Collecting archive from Github...") retrieveJavaAgent(samJavaPath) @@ -56,51 +71,81 @@ func TestEndToEnd(t *testing.T) { })) defer ts.Close() - if os.Getenv("PARALLEL_EXECUTION") == "true" { - var waitGroup sync.WaitGroup - waitGroup.Add(3) - - cNode := make(chan string) - go runTestAsync(samNodePath, "SamTestingNode", ts.URL, forceBuildLambdasFlag, &waitGroup, cNode) - cPython := make(chan string) - go runTestAsync(samPythonPath, "SamTestingPython", ts.URL, forceBuildLambdasFlag, &waitGroup, cPython) - cJava := make(chan string) - go runTestAsync(samJavaPath, "SamTestingJava", ts.URL, forceBuildLambdasFlag, &waitGroup, cJava) - // Retrieve the UUID generated by the tests though dedicated channels - uuidNode := <-cNode - uuidPython := <-cPython - uuidJava := <-cJava - log.Printf("Querying the mock server for transaction %s bound to %s...", uuidNode, "SamTestingNode") - assert.True(t, strings.Contains(mockAPMServerLog, uuidNode)) - log.Printf("Querying the mock server for transaction %s bound to %s...", uuidPython, "SamTestingPython") - assert.True(t, strings.Contains(mockAPMServerLog, uuidPython)) - log.Printf("Querying the mock server for transaction %s bound to %s...", uuidJava, "SamTestingJava") - assert.True(t, strings.Contains(mockAPMServerLog, uuidJava)) - waitGroup.Wait() + resultsChan := make(chan string, 3) + resultsMap := make(map[string]string) + + if getEnvVarValueOrSetDefault("PARALLEL_EXECUTION", "true") == "true" { + + timer := time.NewTimer(time.Duration(timeoutMax) * time.Second) + go runTest(samNodePath, samNodeServiceName, ts.URL, forceBuildLambdasFlag, timeoutNode, resultsChan) + go runTest(samPythonPath, samPythonServiceName, ts.URL, forceBuildLambdasFlag, timeoutPython, resultsChan) + go runTest(samJavaPath, samJavaServiceName, ts.URL, forceBuildLambdasFlag, timeoutJava, resultsChan) + + testLoop: + for i := 0; i < 3; i++ { + log.Println(i) + select { + case result := <-resultsChan: + resultSlice := strings.Split(result, ":") + switch resultSlice[0] { + case samNodeServiceName: + resultsMap["Node"] = resultSlice[1] + case samPythonServiceName: + resultsMap["Python"] = resultSlice[1] + case samJavaServiceName: + resultsMap["Java"] = resultSlice[1] + } + case <-timer.C: + break testLoop + } + } + + } else { + + runTestWithDedicatedTimer(samNodePath, samNodeServiceName, ts.URL, forceBuildLambdasFlag, timeoutNode, resultsMap, resultsChan) + runTestWithDedicatedTimer(samPythonPath, samPythonServiceName, ts.URL, forceBuildLambdasFlag, timeoutPython, resultsMap, resultsChan) + runTestWithDedicatedTimer(samJavaPath, samJavaServiceName, ts.URL, forceBuildLambdasFlag, timeoutJava, resultsMap, resultsChan) + + } + + checkTestResults(samNodeServiceName, mockAPMServerLog, resultsMap, t) + checkTestResults(samPythonServiceName, mockAPMServerLog, resultsMap, t) + checkTestResults(samJavaServiceName, mockAPMServerLog, resultsMap, t) +} +func checkTestResults(serviceName string, serverLog string, resultsMap map[string]string, t *testing.T) { + log.Printf("Querying the mock server for transaction bound to %s...", serviceName) + if uuidNode, exists := resultsMap["Node"]; exists { + assert.True(t, strings.Contains(serverLog, uuidNode)) } else { - uuidNode := runTest(samNodePath, "SamTestingNode", ts.URL, forceBuildLambdasFlag) - uuidPython := runTest(samPythonPath, "SamTestingPython", ts.URL, forceBuildLambdasFlag) - uuidJava := runTest(samJavaPath, "SamTestingJava", ts.URL, forceBuildLambdasFlag) - log.Printf("Querying the mock server logs for transaction %s bound to %s...", uuidNode, "SamTestingNode") - assert.True(t, strings.Contains(mockAPMServerLog, uuidNode)) - log.Printf("Querying the mock server logs for transaction %s bound to %s...", uuidPython, "SamTestingPython") - assert.True(t, strings.Contains(mockAPMServerLog, uuidPython)) - log.Printf("Querying the mock server logs for transaction %s bound to %s...", uuidJava, "SamTestingJava") - assert.True(t, strings.Contains(mockAPMServerLog, uuidJava)) + t.Fail() + log.Printf("FAILURE : Transaction %s bound to %s not found", uuidNode, serviceName) + } +} + +func runTestWithDedicatedTimer(path string, serviceName string, serverURL string, buildFlag bool, timeout int, resultsMap map[string]string, resultsChan chan string) { + timerNode := time.NewTimer(time.Duration(timeout) * time.Second) + runTest(path, serviceName, serverURL, buildFlag, timeout, resultsChan) + log.Println("Ready to Select") + select { + case result := <-resultsChan: + resultSlice := strings.Split(result, ":") + resultsMap["Node"] = resultSlice[1] + case <-timerNode.C: + break } } func buildExtensionBinaries() { - runCommandInDir("make", []string{}, "..", os.Getenv("DEBUG_OUTPUT") == "true") + runCommandInDir("make", []string{}, "..", getEnvVarValueOrSetDefault("DEBUG_OUTPUT", "false") == "true") } -func runTest(path string, serviceName string, serverURL string, buildFlag bool) string { +func runTest(path string, serviceName string, serverURL string, buildFlag bool, timeout int, resultsChan chan string) { log.Printf("Starting to test %s", serviceName) if !folderExists(filepath.Join(path, ".aws-sam")) || buildFlag { log.Printf("Building the Lambda function %s", serviceName) - runCommandInDir("sam", []string{"build"}, path, os.Getenv("DEBUG_OUTPUT") == "true") + runCommandInDir("sam", []string{"build"}, path, getEnvVarValueOrSetDefault("DEBUG_OUTPUT", "false") == "true") } log.Printf("Invoking the Lambda function %s", serviceName) @@ -109,17 +154,12 @@ func runTest(path string, serviceName string, serverURL string, buildFlag bool) port := urlSlice[len(urlSlice)-1] runCommandInDir("sam", []string{"local", "invoke", "--parameter-overrides", fmt.Sprintf("ParameterKey=ApmServerURL,ParameterValue=http://host.docker.internal:%s", port), - fmt.Sprintf("ParameterKey=TestUUID,ParameterValue=%s", uuidWithHyphen)}, - path, os.Getenv("DEBUG_OUTPUT") == "true") + fmt.Sprintf("ParameterKey=TestUUID,ParameterValue=%s", uuidWithHyphen), + fmt.Sprintf("ParameterKey=TimeoutParam,ParameterValue=%d", timeout)}, + path, getEnvVarValueOrSetDefault("DEBUG_OUTPUT", "false") == "true") log.Printf("%s execution complete", serviceName) - return uuidWithHyphen -} - -func runTestAsync(path string, serviceName string, serverURL string, buildFlag bool, wg *sync.WaitGroup, c chan string) { - defer wg.Done() - out := runTest(path, serviceName, serverURL, buildFlag) - c <- out + resultsChan <- fmt.Sprintf("%s:%s", serviceName, uuidWithHyphen) } func retrieveJavaAgent(samJavaPath string) { @@ -132,7 +172,7 @@ func retrieveJavaAgent(samJavaPath string) { processError(err) defer out.Close() resp, err := http.Get(fmt.Sprintf("https://github.com/elastic/apm-agent-java/releases/download/v%[1]s/elastic-apm-java-aws-lambda-layer-%[1]s.zip", - os.Getenv("APM_AGENT_JAVA_VERSION"))) + getEnvVarValueOrSetDefault("APM_AGENT_JAVA_VERSION", "1.28.4"))) processError(err) defer resp.Body.Close() io.Copy(out, resp.Body) @@ -154,6 +194,14 @@ func changeJavaAgentPermissions(samJavaPath string) { } } +func getEnvVarValueOrSetDefault(envVarName string, defaultVal string) string { + val := os.Getenv(envVarName) + if val == "" { + return defaultVal + } + return val +} + func runCommandInDir(command string, args []string, dir string, printOutput bool) { e := exec.Command(command, args...) if printOutput { @@ -275,3 +323,13 @@ func getDecompressedBytesFromRequest(req *http.Request) ([]byte, error) { return rawBytes, nil } } + +func getMax(args []int) int { + currMax := 0 + for _, el := range args { + if el > currMax { + currMax = el + } + } + return currMax +} diff --git a/apm-lambda-extension/e2e-testing/sam-java/template.yml b/apm-lambda-extension/e2e-testing/sam-java/template.yml index 379c5f41..0b9a4733 100644 --- a/apm-lambda-extension/e2e-testing/sam-java/template.yml +++ b/apm-lambda-extension/e2e-testing/sam-java/template.yml @@ -5,11 +5,6 @@ Description: > Sample SAM Template for sam-java -# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst -Globals: - Function: - Timeout: 75 - Parameters: ApmServerURL: Type: String @@ -17,6 +12,9 @@ Parameters: TestUUID: Type: String Description: The UUID used to verify the end-to-end test + TimeoutParam: + Type: Integer + Description: The Timeout for this lambda function Resources: ElasticAPMExtensionLayer: @@ -36,6 +34,7 @@ Resources: SamTestingJava: 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: + Timeout: !Ref TimeoutParam CodeUri: sam-testing-java/ Handler: samtestingjava.App::handleRequest Runtime: java11 diff --git a/apm-lambda-extension/e2e-testing/sam-node/template.yml b/apm-lambda-extension/e2e-testing/sam-node/template.yml index a333ae48..ceec5a31 100644 --- a/apm-lambda-extension/e2e-testing/sam-node/template.yml +++ b/apm-lambda-extension/e2e-testing/sam-node/template.yml @@ -3,11 +3,6 @@ Transform: AWS::Serverless-2016-10-31 Description: > sam-node -# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst -Globals: - Function: - Timeout: 20 - Parameters: ApmServerURL: Type: String @@ -15,6 +10,9 @@ Parameters: TestUUID: Type: String Description: The UUID used to verify the end-to-end test + TimeoutParam: + Type: Integer + Description: The Timeout for this lambda function Resources: ElasticAPMExtensionLayer: @@ -27,6 +25,7 @@ Resources: SamTestingNode: 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: + Timeout: !Ref TimeoutParam CodeUri: sam-testing-node/ Handler: app.lambdaHandler Runtime: nodejs14.x diff --git a/apm-lambda-extension/e2e-testing/sam-python/template.yml b/apm-lambda-extension/e2e-testing/sam-python/template.yml index af86f0be..e4c4682b 100644 --- a/apm-lambda-extension/e2e-testing/sam-python/template.yml +++ b/apm-lambda-extension/e2e-testing/sam-python/template.yml @@ -5,11 +5,6 @@ Description: > Sample SAM Template for sam-python -# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst -Globals: - Function: - Timeout: 20 - Parameters: ApmServerURL: Type: String @@ -17,6 +12,9 @@ Parameters: TestUUID: Type: String Description: The UUID used to verify the end-to-end test + TimeoutParam: + Type: Integer + Description: The Timeout for this lambda function Resources: ElasticAPMExtensionLayer: @@ -29,6 +27,7 @@ Resources: SamTestingPython: 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: + Timeout: !Ref TimeoutParam CodeUri: sam-testing-python/ Handler: app.lambda_handler Runtime: python3.9 From db89a9fe83c62dae5e2e4d63ac0d8ff8143d1c8b Mon Sep 17 00:00:00 2001 From: Jean-Louis Voiseux <48380853+jlvoiseux@users.noreply.github.com> Date: Thu, 3 Feb 2022 16:09:03 +0100 Subject: [PATCH 11/20] Make the tests single-language and fix Gradle --- .../e2e-testing/.e2e_test_config | 2 + apm-lambda-extension/e2e-testing/.gitignore | 1 + apm-lambda-extension/e2e-testing/README.md | 34 +++ apm-lambda-extension/e2e-testing/e2e_test.go | 126 +++----- .../sam-java/sam-testing-java/build.gradle | 2 +- .../sam-java/sam-testing-java/gradlew | 282 +++++++++++------- .../sam-java/sam-testing-java/gradlew.bat | 173 +++++------ 7 files changed, 339 insertions(+), 281 deletions(-) create mode 100644 apm-lambda-extension/e2e-testing/.e2e_test_config create mode 100644 apm-lambda-extension/e2e-testing/README.md diff --git a/apm-lambda-extension/e2e-testing/.e2e_test_config b/apm-lambda-extension/e2e-testing/.e2e_test_config new file mode 100644 index 00000000..0ec92687 --- /dev/null +++ b/apm-lambda-extension/e2e-testing/.e2e_test_config @@ -0,0 +1,2 @@ +RUN_E2E_TESTS=true +DEBUG_OUTPUT=true \ No newline at end of file diff --git a/apm-lambda-extension/e2e-testing/.gitignore b/apm-lambda-extension/e2e-testing/.gitignore index 7c8f1d6a..c11e7541 100644 --- a/apm-lambda-extension/e2e-testing/.gitignore +++ b/apm-lambda-extension/e2e-testing/.gitignore @@ -1,4 +1,5 @@ **/.aws-sam **/sam-java/agent +**/sam-java/*/.gradle **/sam-java/*/gradle **/.env \ No newline at end of file diff --git a/apm-lambda-extension/e2e-testing/README.md b/apm-lambda-extension/e2e-testing/README.md new file mode 100644 index 00000000..3bb582be --- /dev/null +++ b/apm-lambda-extension/e2e-testing/README.md @@ -0,0 +1,34 @@ +# End-to-End Testing + +The file `e2e_test.go` contains an end-to-end test of the Elastic APM Lambda Extension. This test is built on top of the AWS SAM CLI, which allows running Lambda functions and their associated layers locally. + +## Setup + +Since this test is sensibly longer than the other unit tests, it is disabled by default. To enable it, go to `.e2e_test_config` and set the environment variable `RUN_E2E_TESTS` to `true`. +In order to run the Lambda functions locally, the following dependencies must be installed : +- [Install](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) the SAM CLI. Creating an AWS account is actually not required. +- Install Docker +- Install a Go Runtime + +## Run + +```shell +cd apm-lambda-extension/e2e-testing +go test +``` + +### Command line arguments +The command line arguments are presented with their default value. +```shell +-rebuild=false # Rebuilds the Lambda function images +-lang=node # Selects the language of the Lambda function. node, java and python are supported. +-timer=20 # The timeout used to stop the execution of the Lambda function. + # Recommended values : Node : 20, Python : 30, Java : 40 +-java-agent-ver=1.28.4 # The version of the Java agent used when Java is selected. +``` + +Example : +```shell +go test -rebuild=false -lang=Java -timer=40 -java-agent-ver=1.28.4 +``` + diff --git a/apm-lambda-extension/e2e-testing/e2e_test.go b/apm-lambda-extension/e2e-testing/e2e_test.go index 52be2e83..6b713ade 100644 --- a/apm-lambda-extension/e2e-testing/e2e_test.go +++ b/apm-lambda-extension/e2e-testing/e2e_test.go @@ -6,6 +6,8 @@ import ( "bytes" "compress/gzip" "compress/zlib" + "errors" + "flag" "fmt" "github.com/google/uuid" "github.com/joho/godotenv" @@ -18,48 +20,45 @@ import ( "os" "os/exec" "path/filepath" - "strconv" "strings" "testing" "time" ) +var rebuildPtr = flag.Bool("rebuild", false, "rebuild lambda functions") +var langPtr = flag.String("lang", "java", "the language of the Lambda test function : Java, Node, or Python") +var timerPtr = flag.Int("timer", 40, "the timeout of the test lambda function") +var javaAgentVerPtr = flag.String("java-agent-ver", "1.28.4", "the version of the java APM agent") + func TestEndToEnd(t *testing.T) { // Check the only mandatory environment variable - if err := godotenv.Load(); err != nil { - log.Println("No additional .env file found") + if err := godotenv.Load(".e2e_test_config"); err != nil { + log.Println("No additional .e2e_test_config file found") } if getEnvVarValueOrSetDefault("RUN_E2E_TESTS", "false") != "true" { t.Skip("Skipping E2E tests. Please set the env. variable RUN_E2E_TESTS=true if you want to run them.") } - forceBuildLambdasFlag := getEnvVarValueOrSetDefault("FORCE_REBUILD_LOCAL_SAM_LAMBDAS", "false") == "true" - samNodePath := "sam-node" - samNodeServiceName := "SamTestingNode" - samPythonPath := "sam-python" - samPythonServiceName := "SamTestingPython" - samJavaPath := "sam-java" - samJavaServiceName := "SamTestingJava" + supportedLanguages := []string{"node", "python", "java"} + if !isStringInSlice(*langPtr, supportedLanguages) { + processError(errors.New("unsupported language")) + } - // Configure Timeouts and Tested Languages - timeoutNode, err := strconv.Atoi(getEnvVarValueOrSetDefault("TIMEOUT_NODE", "20")) - processError(err) - timeoutPython, err := strconv.Atoi(getEnvVarValueOrSetDefault("TIMEOUT_PYTHON", "20")) - processError(err) - timeoutJava, err := strconv.Atoi(getEnvVarValueOrSetDefault("TIMEOUT_JAVA", "75")) - processError(err) - timeoutMax := getMax([]int{timeoutNode, timeoutPython, timeoutJava}) + samPath := "sam-" + *langPtr + samServiceName := "sam-testing-" + *langPtr // Build and download required binaries (extension and Java agent) buildExtensionBinaries() // Java agent processing - if !folderExists(filepath.Join(samJavaPath, "agent")) { - log.Println("Java agent not found ! Collecting archive from Github...") - retrieveJavaAgent(samJavaPath) + if *langPtr == "java" { + if !folderExists(filepath.Join(samPath, "agent")) { + log.Println("Java agent not found ! Collecting archive from Github...") + retrieveJavaAgent(samPath, *javaAgentVerPtr) + } + changeJavaAgentPermissions(samPath) } - changeJavaAgentPermissions(samJavaPath) // Initialize Mock APM Server mockAPMServerLog := "" @@ -71,69 +70,26 @@ func TestEndToEnd(t *testing.T) { })) defer ts.Close() - resultsChan := make(chan string, 3) - resultsMap := make(map[string]string) - - if getEnvVarValueOrSetDefault("PARALLEL_EXECUTION", "true") == "true" { - - timer := time.NewTimer(time.Duration(timeoutMax) * time.Second) - go runTest(samNodePath, samNodeServiceName, ts.URL, forceBuildLambdasFlag, timeoutNode, resultsChan) - go runTest(samPythonPath, samPythonServiceName, ts.URL, forceBuildLambdasFlag, timeoutPython, resultsChan) - go runTest(samJavaPath, samJavaServiceName, ts.URL, forceBuildLambdasFlag, timeoutJava, resultsChan) - - testLoop: - for i := 0; i < 3; i++ { - log.Println(i) - select { - case result := <-resultsChan: - resultSlice := strings.Split(result, ":") - switch resultSlice[0] { - case samNodeServiceName: - resultsMap["Node"] = resultSlice[1] - case samPythonServiceName: - resultsMap["Python"] = resultSlice[1] - case samJavaServiceName: - resultsMap["Java"] = resultSlice[1] - } - case <-timer.C: - break testLoop - } - } + resultsChan := make(chan string, 1) - } else { - - runTestWithDedicatedTimer(samNodePath, samNodeServiceName, ts.URL, forceBuildLambdasFlag, timeoutNode, resultsMap, resultsChan) - runTestWithDedicatedTimer(samPythonPath, samPythonServiceName, ts.URL, forceBuildLambdasFlag, timeoutPython, resultsMap, resultsChan) - runTestWithDedicatedTimer(samJavaPath, samJavaServiceName, ts.URL, forceBuildLambdasFlag, timeoutJava, resultsMap, resultsChan) - - } - - checkTestResults(samNodeServiceName, mockAPMServerLog, resultsMap, t) - checkTestResults(samPythonServiceName, mockAPMServerLog, resultsMap, t) - checkTestResults(samJavaServiceName, mockAPMServerLog, resultsMap, t) -} - -func checkTestResults(serviceName string, serverLog string, resultsMap map[string]string, t *testing.T) { - log.Printf("Querying the mock server for transaction bound to %s...", serviceName) - if uuidNode, exists := resultsMap["Node"]; exists { - assert.True(t, strings.Contains(serverLog, uuidNode)) - } else { + uuid := runTestWithDedicatedTimer(samPath, samServiceName, ts.URL, *rebuildPtr, *timerPtr, resultsChan) + if uuid == "" { t.Fail() - log.Printf("FAILURE : Transaction %s bound to %s not found", uuidNode, serviceName) } + log.Printf("Querying the mock server for transaction bound to %s...", samServiceName) + assert.True(t, strings.Contains(mockAPMServerLog, uuid)) } -func runTestWithDedicatedTimer(path string, serviceName string, serverURL string, buildFlag bool, timeout int, resultsMap map[string]string, resultsChan chan string) { - timerNode := time.NewTimer(time.Duration(timeout) * time.Second) - runTest(path, serviceName, serverURL, buildFlag, timeout, resultsChan) - log.Println("Ready to Select") +func runTestWithDedicatedTimer(path string, serviceName string, serverURL string, buildFlag bool, timeout int, resultsChan chan string) string { + timerNode := time.NewTimer(time.Duration(timeout) * time.Second * 2) + go runTest(path, serviceName, serverURL, buildFlag, timeout, resultsChan) select { - case result := <-resultsChan: - resultSlice := strings.Split(result, ":") - resultsMap["Node"] = resultSlice[1] + case uuid := <-resultsChan: + return uuid case <-timerNode.C: break } + return "" } func buildExtensionBinaries() { @@ -159,10 +115,10 @@ func runTest(path string, serviceName string, serverURL string, buildFlag bool, path, getEnvVarValueOrSetDefault("DEBUG_OUTPUT", "false") == "true") log.Printf("%s execution complete", serviceName) - resultsChan <- fmt.Sprintf("%s:%s", serviceName, uuidWithHyphen) + resultsChan <- uuidWithHyphen } -func retrieveJavaAgent(samJavaPath string) { +func retrieveJavaAgent(samJavaPath string, version string) { agentFolderPath := filepath.Join(samJavaPath, "agent") agentArchivePath := filepath.Join(samJavaPath, "agent.zip") @@ -171,8 +127,7 @@ func retrieveJavaAgent(samJavaPath string) { out, err := os.Create(agentArchivePath) processError(err) defer out.Close() - resp, err := http.Get(fmt.Sprintf("https://github.com/elastic/apm-agent-java/releases/download/v%[1]s/elastic-apm-java-aws-lambda-layer-%[1]s.zip", - getEnvVarValueOrSetDefault("APM_AGENT_JAVA_VERSION", "1.28.4"))) + resp, err := http.Get(fmt.Sprintf("https://github.com/elastic/apm-agent-java/releases/download/v%[1]s/elastic-apm-java-aws-lambda-layer-%[1]s.zip", version)) processError(err) defer resp.Body.Close() io.Copy(out, resp.Body) @@ -324,12 +279,11 @@ func getDecompressedBytesFromRequest(req *http.Request) ([]byte, error) { } } -func getMax(args []int) int { - currMax := 0 - for _, el := range args { - if el > currMax { - currMax = el +func isStringInSlice(a string, list []string) bool { + for _, b := range list { + if b == a { + return true } } - return currMax + return false } diff --git a/apm-lambda-extension/e2e-testing/sam-java/sam-testing-java/build.gradle b/apm-lambda-extension/e2e-testing/sam-java/sam-testing-java/build.gradle index e93549e0..de954b5b 100644 --- a/apm-lambda-extension/e2e-testing/sam-java/sam-testing-java/build.gradle +++ b/apm-lambda-extension/e2e-testing/sam-java/sam-testing-java/build.gradle @@ -10,5 +10,5 @@ dependencies { implementation 'com.amazonaws:aws-lambda-java-core:1.2.1' implementation 'com.amazonaws:aws-lambda-java-events:3.6.0' testImplementation 'junit:junit:4.13.1' - compile "co.elastic.apm:apm-agent-api:1.28.4" + implementation "co.elastic.apm:apm-agent-api:1.28.4" } diff --git a/apm-lambda-extension/e2e-testing/sam-java/sam-testing-java/gradlew b/apm-lambda-extension/e2e-testing/sam-java/sam-testing-java/gradlew index af6708ff..1b6c7873 100755 --- a/apm-lambda-extension/e2e-testing/sam-java/sam-testing-java/gradlew +++ b/apm-lambda-extension/e2e-testing/sam-java/sam-testing-java/gradlew @@ -1,78 +1,129 @@ -#!/usr/bin/env sh +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` +APP_BASE_NAME=${0##*/} # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m"' +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -81,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" + JAVACMD=java which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the @@ -89,84 +140,95 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=$((i+1)) + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=$(save "$@") - -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" - -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/apm-lambda-extension/e2e-testing/sam-java/sam-testing-java/gradlew.bat b/apm-lambda-extension/e2e-testing/sam-java/sam-testing-java/gradlew.bat index 6d57edc7..ac1b06f9 100644 --- a/apm-lambda-extension/e2e-testing/sam-java/sam-testing-java/gradlew.bat +++ b/apm-lambda-extension/e2e-testing/sam-java/sam-testing-java/gradlew.bat @@ -1,84 +1,89 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega From 985658ca7006c8cfc6b9bbd2dbec287960b23eee Mon Sep 17 00:00:00 2001 From: Jean-Louis Voiseux <48380853+jlvoiseux@users.noreply.github.com> Date: Thu, 3 Feb 2022 16:14:30 +0100 Subject: [PATCH 12/20] Set the default config values --- apm-lambda-extension/e2e-testing/.e2e_test_config | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apm-lambda-extension/e2e-testing/.e2e_test_config b/apm-lambda-extension/e2e-testing/.e2e_test_config index 0ec92687..51f63da4 100644 --- a/apm-lambda-extension/e2e-testing/.e2e_test_config +++ b/apm-lambda-extension/e2e-testing/.e2e_test_config @@ -1,2 +1,2 @@ -RUN_E2E_TESTS=true -DEBUG_OUTPUT=true \ No newline at end of file +RUN_E2E_TESTS=false +DEBUG_OUTPUT=false \ No newline at end of file From abe1830d12f414f62ccdfe41696e1d1c311462bf Mon Sep 17 00:00:00 2001 From: Jean-Louis Voiseux <48380853+jlvoiseux@users.noreply.github.com> Date: Thu, 3 Feb 2022 17:26:23 +0100 Subject: [PATCH 13/20] Fix default values --- apm-lambda-extension/e2e-testing/e2e_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apm-lambda-extension/e2e-testing/e2e_test.go b/apm-lambda-extension/e2e-testing/e2e_test.go index 6b713ade..5a83114d 100644 --- a/apm-lambda-extension/e2e-testing/e2e_test.go +++ b/apm-lambda-extension/e2e-testing/e2e_test.go @@ -26,8 +26,8 @@ import ( ) var rebuildPtr = flag.Bool("rebuild", false, "rebuild lambda functions") -var langPtr = flag.String("lang", "java", "the language of the Lambda test function : Java, Node, or Python") -var timerPtr = flag.Int("timer", 40, "the timeout of the test lambda function") +var langPtr = flag.String("lang", "node", "the language of the Lambda test function : Java, Node, or Python") +var timerPtr = flag.Int("timer", 20, "the timeout of the test lambda function") var javaAgentVerPtr = flag.String("java-agent-ver", "1.28.4", "the version of the java APM agent") func TestEndToEnd(t *testing.T) { From 32289c3da3b617daf08eb48b6fe868e87b782a22 Mon Sep 17 00:00:00 2001 From: Jean-Louis Voiseux <48380853+jlvoiseux@users.noreply.github.com> Date: Fri, 4 Feb 2022 13:59:41 +0100 Subject: [PATCH 14/20] Add timer defer and add units to doc --- apm-lambda-extension/e2e-testing/README.md | 2 +- apm-lambda-extension/e2e-testing/e2e_test.go | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/apm-lambda-extension/e2e-testing/README.md b/apm-lambda-extension/e2e-testing/README.md index 3bb582be..880c9019 100644 --- a/apm-lambda-extension/e2e-testing/README.md +++ b/apm-lambda-extension/e2e-testing/README.md @@ -22,7 +22,7 @@ The command line arguments are presented with their default value. ```shell -rebuild=false # Rebuilds the Lambda function images -lang=node # Selects the language of the Lambda function. node, java and python are supported. --timer=20 # The timeout used to stop the execution of the Lambda function. +-timer=20 # The timeout (in seconds) used to stop the execution of the Lambda function. # Recommended values : Node : 20, Python : 30, Java : 40 -java-agent-ver=1.28.4 # The version of the Java agent used when Java is selected. ``` diff --git a/apm-lambda-extension/e2e-testing/e2e_test.go b/apm-lambda-extension/e2e-testing/e2e_test.go index 5a83114d..2add3556 100644 --- a/apm-lambda-extension/e2e-testing/e2e_test.go +++ b/apm-lambda-extension/e2e-testing/e2e_test.go @@ -81,12 +81,13 @@ func TestEndToEnd(t *testing.T) { } func runTestWithDedicatedTimer(path string, serviceName string, serverURL string, buildFlag bool, timeout int, resultsChan chan string) string { - timerNode := time.NewTimer(time.Duration(timeout) * time.Second * 2) + timer := time.NewTimer(time.Duration(timeout) * time.Second * 2) + defer timer.Stop() go runTest(path, serviceName, serverURL, buildFlag, timeout, resultsChan) select { case uuid := <-resultsChan: return uuid - case <-timerNode.C: + case <-timer.C: break } return "" From bce0bc4e3116005e4b1312c790c31509c1811501 Mon Sep 17 00:00:00 2001 From: Jean-Louis Voiseux <48380853+jlvoiseux@users.noreply.github.com> Date: Fri, 4 Feb 2022 14:05:40 +0100 Subject: [PATCH 15/20] Add tolerance for Uppercase, but set the documented language value to lowercase. --- apm-lambda-extension/e2e-testing/README.md | 2 +- apm-lambda-extension/e2e-testing/e2e_test.go | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/apm-lambda-extension/e2e-testing/README.md b/apm-lambda-extension/e2e-testing/README.md index 880c9019..d301e34e 100644 --- a/apm-lambda-extension/e2e-testing/README.md +++ b/apm-lambda-extension/e2e-testing/README.md @@ -29,6 +29,6 @@ The command line arguments are presented with their default value. Example : ```shell -go test -rebuild=false -lang=Java -timer=40 -java-agent-ver=1.28.4 +go test -rebuild=false -lang=java -timer=40 -java-agent-ver=1.28.4 ``` diff --git a/apm-lambda-extension/e2e-testing/e2e_test.go b/apm-lambda-extension/e2e-testing/e2e_test.go index 2add3556..80b858fb 100644 --- a/apm-lambda-extension/e2e-testing/e2e_test.go +++ b/apm-lambda-extension/e2e-testing/e2e_test.go @@ -40,19 +40,20 @@ func TestEndToEnd(t *testing.T) { t.Skip("Skipping E2E tests. Please set the env. variable RUN_E2E_TESTS=true if you want to run them.") } + languageName := strings.ToLower(*langPtr) supportedLanguages := []string{"node", "python", "java"} - if !isStringInSlice(*langPtr, supportedLanguages) { + if !isStringInSlice(languageName, supportedLanguages) { processError(errors.New("unsupported language")) } - samPath := "sam-" + *langPtr - samServiceName := "sam-testing-" + *langPtr + samPath := "sam-" + languageName + samServiceName := "sam-testing-" + languageName // Build and download required binaries (extension and Java agent) buildExtensionBinaries() // Java agent processing - if *langPtr == "java" { + if languageName == "java" { if !folderExists(filepath.Join(samPath, "agent")) { log.Println("Java agent not found ! Collecting archive from Github...") retrieveJavaAgent(samPath, *javaAgentVerPtr) From fc6f94b3f7b01e414cc0aa5950d7a8ac157a7d57 Mon Sep 17 00:00:00 2001 From: Jean-Louis Voiseux <48380853+jlvoiseux@users.noreply.github.com> Date: Fri, 4 Feb 2022 15:04:35 +0100 Subject: [PATCH 16/20] Add supported languages in Panic message --- apm-lambda-extension/e2e-testing/e2e_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apm-lambda-extension/e2e-testing/e2e_test.go b/apm-lambda-extension/e2e-testing/e2e_test.go index 80b858fb..9784d6a9 100644 --- a/apm-lambda-extension/e2e-testing/e2e_test.go +++ b/apm-lambda-extension/e2e-testing/e2e_test.go @@ -43,7 +43,7 @@ func TestEndToEnd(t *testing.T) { languageName := strings.ToLower(*langPtr) supportedLanguages := []string{"node", "python", "java"} if !isStringInSlice(languageName, supportedLanguages) { - processError(errors.New("unsupported language")) + processError(errors.New(fmt.Sprintf("Unsupported language %s ! Supported languages are %v", languageName, supportedLanguages))) } samPath := "sam-" + languageName From 5a583be66192a310f20fe29ad58b9912aa50fa7f Mon Sep 17 00:00:00 2001 From: Jean-Louis Voiseux <48380853+jlvoiseux@users.noreply.github.com> Date: Fri, 4 Feb 2022 15:30:05 +0100 Subject: [PATCH 17/20] Replace"node" by "nodejs" --- apm-lambda-extension/e2e-testing/README.md | 4 ++-- apm-lambda-extension/e2e-testing/e2e_test.go | 4 ++-- .../sam-testing-nodejs}/.npmignore | 0 .../sam-testing-node => sam-nodejs/sam-testing-nodejs}/app.js | 0 .../sam-testing-nodejs}/package.json | 0 .../e2e-testing/{sam-node => sam-nodejs}/template.yml | 2 +- 6 files changed, 5 insertions(+), 5 deletions(-) rename apm-lambda-extension/e2e-testing/{sam-node/sam-testing-node => sam-nodejs/sam-testing-nodejs}/.npmignore (100%) rename apm-lambda-extension/e2e-testing/{sam-node/sam-testing-node => sam-nodejs/sam-testing-nodejs}/app.js (100%) rename apm-lambda-extension/e2e-testing/{sam-node/sam-testing-node => sam-nodejs/sam-testing-nodejs}/package.json (100%) rename apm-lambda-extension/e2e-testing/{sam-node => sam-nodejs}/template.yml (97%) diff --git a/apm-lambda-extension/e2e-testing/README.md b/apm-lambda-extension/e2e-testing/README.md index d301e34e..ab43eda4 100644 --- a/apm-lambda-extension/e2e-testing/README.md +++ b/apm-lambda-extension/e2e-testing/README.md @@ -21,9 +21,9 @@ go test The command line arguments are presented with their default value. ```shell -rebuild=false # Rebuilds the Lambda function images --lang=node # Selects the language of the Lambda function. node, java and python are supported. +-lang=nodejs # Selects the language of the Lambda function. node, java and python are supported. -timer=20 # The timeout (in seconds) used to stop the execution of the Lambda function. - # Recommended values : Node : 20, Python : 30, Java : 40 + # Recommended values : NodeJS : 20, Python : 30, Java : 40 -java-agent-ver=1.28.4 # The version of the Java agent used when Java is selected. ``` diff --git a/apm-lambda-extension/e2e-testing/e2e_test.go b/apm-lambda-extension/e2e-testing/e2e_test.go index 9784d6a9..9905cff6 100644 --- a/apm-lambda-extension/e2e-testing/e2e_test.go +++ b/apm-lambda-extension/e2e-testing/e2e_test.go @@ -26,7 +26,7 @@ import ( ) var rebuildPtr = flag.Bool("rebuild", false, "rebuild lambda functions") -var langPtr = flag.String("lang", "node", "the language of the Lambda test function : Java, Node, or Python") +var langPtr = flag.String("lang", "nodejs", "the language of the Lambda test function : Java, Node, or Python") var timerPtr = flag.Int("timer", 20, "the timeout of the test lambda function") var javaAgentVerPtr = flag.String("java-agent-ver", "1.28.4", "the version of the java APM agent") @@ -41,7 +41,7 @@ func TestEndToEnd(t *testing.T) { } languageName := strings.ToLower(*langPtr) - supportedLanguages := []string{"node", "python", "java"} + supportedLanguages := []string{"nodejs", "python", "java"} if !isStringInSlice(languageName, supportedLanguages) { processError(errors.New(fmt.Sprintf("Unsupported language %s ! Supported languages are %v", languageName, supportedLanguages))) } diff --git a/apm-lambda-extension/e2e-testing/sam-node/sam-testing-node/.npmignore b/apm-lambda-extension/e2e-testing/sam-nodejs/sam-testing-nodejs/.npmignore similarity index 100% rename from apm-lambda-extension/e2e-testing/sam-node/sam-testing-node/.npmignore rename to apm-lambda-extension/e2e-testing/sam-nodejs/sam-testing-nodejs/.npmignore diff --git a/apm-lambda-extension/e2e-testing/sam-node/sam-testing-node/app.js b/apm-lambda-extension/e2e-testing/sam-nodejs/sam-testing-nodejs/app.js similarity index 100% rename from apm-lambda-extension/e2e-testing/sam-node/sam-testing-node/app.js rename to apm-lambda-extension/e2e-testing/sam-nodejs/sam-testing-nodejs/app.js diff --git a/apm-lambda-extension/e2e-testing/sam-node/sam-testing-node/package.json b/apm-lambda-extension/e2e-testing/sam-nodejs/sam-testing-nodejs/package.json similarity index 100% rename from apm-lambda-extension/e2e-testing/sam-node/sam-testing-node/package.json rename to apm-lambda-extension/e2e-testing/sam-nodejs/sam-testing-nodejs/package.json diff --git a/apm-lambda-extension/e2e-testing/sam-node/template.yml b/apm-lambda-extension/e2e-testing/sam-nodejs/template.yml similarity index 97% rename from apm-lambda-extension/e2e-testing/sam-node/template.yml rename to apm-lambda-extension/e2e-testing/sam-nodejs/template.yml index ceec5a31..23d5ddbe 100644 --- a/apm-lambda-extension/e2e-testing/sam-node/template.yml +++ b/apm-lambda-extension/e2e-testing/sam-nodejs/template.yml @@ -26,7 +26,7 @@ Resources: 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: Timeout: !Ref TimeoutParam - CodeUri: sam-testing-node/ + CodeUri: sam-testing-nodejs/ Handler: app.lambdaHandler Runtime: nodejs14.x Layers: From 78485cdc183d8d318a41868c23b93c9bc551a30d Mon Sep 17 00:00:00 2001 From: Jean-Louis Voiseux <48380853+jlvoiseux@users.noreply.github.com> Date: Fri, 4 Feb 2022 15:50:39 +0100 Subject: [PATCH 18/20] Print the UUID --- apm-lambda-extension/e2e-testing/e2e_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/apm-lambda-extension/e2e-testing/e2e_test.go b/apm-lambda-extension/e2e-testing/e2e_test.go index 9905cff6..355d5249 100644 --- a/apm-lambda-extension/e2e-testing/e2e_test.go +++ b/apm-lambda-extension/e2e-testing/e2e_test.go @@ -74,6 +74,7 @@ func TestEndToEnd(t *testing.T) { resultsChan := make(chan string, 1) uuid := runTestWithDedicatedTimer(samPath, samServiceName, ts.URL, *rebuildPtr, *timerPtr, resultsChan) + log.Printf("UUID generated during the test : %s", uuid) if uuid == "" { t.Fail() } From afb45a6bf1d5addec87c8fda42a39e921e885424 Mon Sep 17 00:00:00 2001 From: Jean-Louis Voiseux <48380853+jlvoiseux@users.noreply.github.com> Date: Fri, 4 Feb 2022 16:14:27 +0100 Subject: [PATCH 19/20] Variable/Function name refactor --- apm-lambda-extension/e2e-testing/e2e_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apm-lambda-extension/e2e-testing/e2e_test.go b/apm-lambda-extension/e2e-testing/e2e_test.go index 355d5249..f5e53650 100644 --- a/apm-lambda-extension/e2e-testing/e2e_test.go +++ b/apm-lambda-extension/e2e-testing/e2e_test.go @@ -73,7 +73,7 @@ func TestEndToEnd(t *testing.T) { resultsChan := make(chan string, 1) - uuid := runTestWithDedicatedTimer(samPath, samServiceName, ts.URL, *rebuildPtr, *timerPtr, resultsChan) + uuid := runTestWithTimer(samPath, samServiceName, ts.URL, *rebuildPtr, *timerPtr, resultsChan) log.Printf("UUID generated during the test : %s", uuid) if uuid == "" { t.Fail() @@ -82,10 +82,10 @@ func TestEndToEnd(t *testing.T) { assert.True(t, strings.Contains(mockAPMServerLog, uuid)) } -func runTestWithDedicatedTimer(path string, serviceName string, serverURL string, buildFlag bool, timeout int, resultsChan chan string) string { - timer := time.NewTimer(time.Duration(timeout) * time.Second * 2) +func runTestWithTimer(path string, serviceName string, serverURL string, buildFlag bool, lambdaFuncTimeout int, resultsChan chan string) string { + timer := time.NewTimer(time.Duration(lambdaFuncTimeout) * time.Second * 2) defer timer.Stop() - go runTest(path, serviceName, serverURL, buildFlag, timeout, resultsChan) + go runTest(path, serviceName, serverURL, buildFlag, lambdaFuncTimeout, resultsChan) select { case uuid := <-resultsChan: return uuid @@ -99,7 +99,7 @@ func buildExtensionBinaries() { runCommandInDir("make", []string{}, "..", getEnvVarValueOrSetDefault("DEBUG_OUTPUT", "false") == "true") } -func runTest(path string, serviceName string, serverURL string, buildFlag bool, timeout int, resultsChan chan string) { +func runTest(path string, serviceName string, serverURL string, buildFlag bool, lambdaFuncTimeout int, resultsChan chan string) { log.Printf("Starting to test %s", serviceName) if !folderExists(filepath.Join(path, ".aws-sam")) || buildFlag { @@ -114,7 +114,7 @@ func runTest(path string, serviceName string, serverURL string, buildFlag bool, runCommandInDir("sam", []string{"local", "invoke", "--parameter-overrides", fmt.Sprintf("ParameterKey=ApmServerURL,ParameterValue=http://host.docker.internal:%s", port), fmt.Sprintf("ParameterKey=TestUUID,ParameterValue=%s", uuidWithHyphen), - fmt.Sprintf("ParameterKey=TimeoutParam,ParameterValue=%d", timeout)}, + fmt.Sprintf("ParameterKey=TimeoutParam,ParameterValue=%d", lambdaFuncTimeout)}, path, getEnvVarValueOrSetDefault("DEBUG_OUTPUT", "false") == "true") log.Printf("%s execution complete", serviceName) From b508016befaf8fc8b4e1901f8339ed038cf111b1 Mon Sep 17 00:00:00 2001 From: Jean-Louis Voiseux <48380853+jlvoiseux@users.noreply.github.com> Date: Fri, 4 Feb 2022 16:33:42 +0100 Subject: [PATCH 20/20] Return empty string upon timeout and add new line to server log --- apm-lambda-extension/e2e-testing/e2e_test.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apm-lambda-extension/e2e-testing/e2e_test.go b/apm-lambda-extension/e2e-testing/e2e_test.go index f5e53650..0b066d5e 100644 --- a/apm-lambda-extension/e2e-testing/e2e_test.go +++ b/apm-lambda-extension/e2e-testing/e2e_test.go @@ -66,7 +66,7 @@ func TestEndToEnd(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.RequestURI == "/intake/v2/events" { bytesRes, _ := getDecompressedBytesFromRequest(r) - mockAPMServerLog += string(bytesRes) + mockAPMServerLog += fmt.Sprintf("%s\n", bytesRes) } })) defer ts.Close() @@ -90,9 +90,8 @@ func runTestWithTimer(path string, serviceName string, serverURL string, buildFl case uuid := <-resultsChan: return uuid case <-timer.C: - break + return "" } - return "" } func buildExtensionBinaries() {