diff --git a/README.md b/README.md index 3599cc0..2eee917 100644 --- a/README.md +++ b/README.md @@ -172,6 +172,11 @@ The rest of these Environment Variables can be set to match AWS Lambda's environ * `AWS_LAMBDA_FUNCTION_NAME` * `AWS_LAMBDA_FUNCTION_MEMORY_SIZE` +By default `aws-lambda-rie` sets the value of the `AWS_LAMBDA_FUNCTION_NAME` environment variable to `test_function`, while the +function name in the endpoint URL is `function`. If you want the RIE to behave like AWS Lambda, where the function name in the +endpoint matches the value of the environment variable, set the value of the `AWS_LAMBDA_RIE_DYNAMIC_FUNCTION_URL` +environment variable to `"TRUE"`. + ## Level of support You can use the emulator to test if your function code is compatible with the Lambda environment, executes successfully diff --git a/cmd/aws-lambda-rie/handlers.go b/cmd/aws-lambda-rie/handlers.go index 2cca12d..9767ddb 100644 --- a/cmd/aws-lambda-rie/handlers.go +++ b/cmd/aws-lambda-rie/handlers.go @@ -55,6 +55,14 @@ func GetenvWithDefault(key string, defaultValue string) string { return envValue } +func GetFunctionName() string { + defaultValue := "function" + if GetenvWithDefault("AWS_LAMBDA_RIE_DYNAMIC_FUNCTION_URL", "FALSE") == "FALSE" { + defaultValue = "test_function" + } + return GetenvWithDefault("AWS_LAMBDA_FUNCTION_NAME", defaultValue) +} + func printEndReports(invokeId string, initDuration string, memorySize string, invokeStart time.Time, timeoutDuration time.Duration) { // Calcuation invoke duration invokeDuration := math.Min(float64(time.Now().Sub(invokeStart).Nanoseconds()), @@ -118,7 +126,7 @@ func InvokeHandler(w http.ResponseWriter, r *http.Request, sandbox Sandbox, bs i invokeStart := time.Now() invokePayload := &interop.Invoke{ ID: uuid.New().String(), - InvokedFunctionArn: fmt.Sprintf("arn:aws:lambda:us-east-1:012345678912:function:%s", GetenvWithDefault("AWS_LAMBDA_FUNCTION_NAME", "test_function")), + InvokedFunctionArn: fmt.Sprintf("arn:aws:lambda:us-east-1:012345678912:function:%s", GetFunctionName()), TraceID: r.Header.Get("X-Amzn-Trace-Id"), LambdaSegmentID: r.Header.Get("X-Amzn-Segment-Id"), Payload: bytes.NewReader(bodyBytes), @@ -198,7 +206,7 @@ func InitHandler(sandbox Sandbox, functionVersion string, timeout int64, bs inte additionalFunctionEnvironmentVariables["AWS_LAMBDA_LOG_STREAM_NAME"] = "$LATEST" additionalFunctionEnvironmentVariables["AWS_LAMBDA_FUNCTION_VERSION"] = "$LATEST" additionalFunctionEnvironmentVariables["AWS_LAMBDA_FUNCTION_MEMORY_SIZE"] = "3008" - additionalFunctionEnvironmentVariables["AWS_LAMBDA_FUNCTION_NAME"] = "test_function" + additionalFunctionEnvironmentVariables["AWS_LAMBDA_FUNCTION_NAME"] = GetFunctionName() // Forward Env Vars from the running system (container) to what the function can view. Without this, Env Vars will // not be viewable when the function runs. @@ -216,7 +224,7 @@ func InitHandler(sandbox Sandbox, functionVersion string, timeout int64, bs inte AwsSecret: os.Getenv("AWS_SECRET_ACCESS_KEY"), AwsSession: os.Getenv("AWS_SESSION_TOKEN"), XRayDaemonAddress: "0.0.0.0:0", // TODO - FunctionName: GetenvWithDefault("AWS_LAMBDA_FUNCTION_NAME", "test_function"), + FunctionName: GetFunctionName(), FunctionVersion: functionVersion, RuntimeInfo: interop.RuntimeInfo{ ImageJSON: "{}", diff --git a/cmd/aws-lambda-rie/http.go b/cmd/aws-lambda-rie/http.go index 88bd39b..5d6ad8b 100644 --- a/cmd/aws-lambda-rie/http.go +++ b/cmd/aws-lambda-rie/http.go @@ -11,15 +11,21 @@ import ( "go.amzn.com/lambda/rapidcore" ) -func startHTTPServer(ipport string, sandbox *rapidcore.SandboxBuilder, bs interop.Bootstrap) { +func startHTTPServer(ipport string, sandbox *rapidcore.SandboxBuilder, bs interop.Bootstrap, funcName string) { srv := &http.Server{ Addr: ipport, } - // Pass a channel - http.HandleFunc("/2015-03-31/functions/function/invocations", func(w http.ResponseWriter, r *http.Request) { - InvokeHandler(w, r, sandbox.LambdaInvokeAPI(), bs) - }) + var functions = []string{funcName} + if funcName != "function" { + functions = []string{"function", funcName} + } + for _, funcName := range functions { + // Pass a channel + http.HandleFunc("/2015-03-31/functions/"+funcName+"/invocations", func(w http.ResponseWriter, r *http.Request) { + InvokeHandler(w, r, sandbox.LambdaInvokeAPI(), bs) + }) + } // go routine (main thread waits) if err := srv.ListenAndServe(); err != nil { diff --git a/cmd/aws-lambda-rie/main.go b/cmd/aws-lambda-rie/main.go index bd15402..7ff7d7f 100644 --- a/cmd/aws-lambda-rie/main.go +++ b/cmd/aws-lambda-rie/main.go @@ -78,13 +78,15 @@ func main() { sandbox.SetRuntimeAPIAddress(opts.RuntimeAPIAddress) } + funcName := GetenvWithDefault("AWS_LAMBDA_FUNCTION_NAME", "function") + sandboxContext, internalStateFn := sandbox.Create() // Since we have not specified a custom interop server for standalone, we can // directly reference the default interop server, which is a concrete type sandbox.DefaultInteropServer().SetSandboxContext(sandboxContext) sandbox.DefaultInteropServer().SetInternalStateGetter(internalStateFn) - startHTTPServer(opts.RuntimeInterfaceEmulatorAddress, sandbox, bootstrap) + startHTTPServer(opts.RuntimeInterfaceEmulatorAddress, sandbox, bootstrap, funcName) } func getCLIArgs() (options, []string) { diff --git a/test/integration/local_lambda/test_end_to_end.py b/test/integration/local_lambda/test_end_to_end.py index 8e34b77..fd6a034 100644 --- a/test/integration/local_lambda/test_end_to_end.py +++ b/test/integration/local_lambda/test_end_to_end.py @@ -65,9 +65,9 @@ def run_command(self, cmd): def sleep_1s(self): time.sleep(SLEEP_TIME) - def invoke_function(self, json={}, headers={}): + def invoke_function(self, json={}, headers={}, function_name="function"): return requests.post( - f"http://localhost:{self.PORT}/2015-03-31/functions/function/invocations", + f"http://localhost:{self.PORT}/2015-03-31/functions/{function_name}/invocations", json=json, headers=headers, ) @@ -257,5 +257,25 @@ def test_custom_client_context(self): self.assertEqual(123, content["baz"]) + def test_function_name_is_overriden_consistent(self): + image, rie, image_name = self.tagged_name("assert_overwritten_consistent") + + params = f"--name {image} -d --env AWS_LAMBDA_FUNCTION_NAME=MyCoolName --env AWS_LAMBDA_RIE_DYNAMIC_FUNCTION_URL=TRUE -v {self.path_to_binary}:/local-lambda-runtime-server -p {self.PORT}:8080 --entrypoint /local-lambda-runtime-server/{rie} {image_name} {DEFAULT_1P_ENTRYPOINT} main.assert_env_var_is_overwritten" + + with self.create_container(params, image): + r = self.invoke_function(function_name="MyCoolName") + self.assertEqual(b'"My lambda ran succesfully"', r.content) + + + def test_lambda_function_arn_exists(self): + image, rie, image_name = self.tagged_name("arnexists_consistent") + + params = f"--name {image} -d --env AWS_LAMBDA_FUNCTION_NAME=MyCoolName --env AWS_LAMBDA_RIE_DYNAMIC_FUNCTION_URL=TRUE -v {self.path_to_binary}:/local-lambda-runtime-server -p {self.PORT}:8080 --entrypoint /local-lambda-runtime-server/{rie} {image_name} {DEFAULT_1P_ENTRYPOINT} main.assert_lambda_arn_in_context" + + with self.create_container(params, image): + r = self.invoke_function(function_name="MyCoolName") + self.assertEqual(b'"My lambda ran succesfully"', r.content) + + if __name__ == "__main__": - main() + main() \ No newline at end of file diff --git a/test/integration/testdata/main.py b/test/integration/testdata/main.py index 9757be8..2dd3a2f 100644 --- a/test/integration/testdata/main.py +++ b/test/integration/testdata/main.py @@ -25,13 +25,13 @@ def check_env_var_handler(event, context): def assert_env_var_is_overwritten(event, context): print(os.environ.get("AWS_LAMBDA_FUNCTION_NAME")) - if os.environ.get("AWS_LAMBDA_FUNCTION_NAME") == "test_function": + if os.environ.get("AWS_LAMBDA_FUNCTION_NAME") == "function": raise("Function name was not overwritten") else: return "My lambda ran succesfully" def assert_lambda_arn_in_context(event, context): - if context.invoked_function_arn == f"arn:aws:lambda:us-east-1:012345678912:function:{os.environ.get('AWS_LAMBDA_FUNCTION_NAME', 'test_function')}": + if context.invoked_function_arn == f"arn:aws:lambda:us-east-1:012345678912:function:{os.environ.get('AWS_LAMBDA_FUNCTION_NAME', 'function')}": return "My lambda ran succesfully" else: raise("Function Arn was not there")