44package lambda
55
66import (
7+ "fmt"
8+ "io"
79 "io/ioutil" //nolint: staticcheck
10+ "net"
811 "net/http"
912 "os"
1013 "os/exec"
11- "path"
12- "strconv"
14+ "path/filepath"
1315 "strings"
1416 "testing"
1517 "time"
@@ -19,19 +21,22 @@ import (
1921)
2022
2123func TestEnableSigterm (t * testing.T ) {
22- if _ , err := exec .LookPath ("aws-lambda-rie" ); err != nil {
23- t .Skipf ("%v - install from https://github.com/aws/aws-lambda-runtime-interface-emulator/" , err )
24+ containerCmd := ""
25+ if _ , err := exec .LookPath ("finch" ); err == nil {
26+ containerCmd = "finch"
27+ } else if _ , err := exec .LookPath ("docker" ); err == nil {
28+ containerCmd = "docker"
29+ } else {
30+ t .Skip ("finch or docker required" )
2431 }
2532
2633 testDir := t .TempDir ()
2734
28- // compile our handler, it'll always run to timeout ensuring the SIGTERM is triggered by aws-lambda-rie
29- handlerBuild := exec .Command ("go" , "build" , "-o" , path .Join (testDir , "sigterm.handler" ), "./testdata/sigterm.go" )
30- handlerBuild .Stderr = os .Stderr
31- handlerBuild .Stdout = os .Stderr
35+ // compile our handler, it'll always run to timeout ensuring the SIGTERM is triggered
36+ handlerBuild := exec .Command ("go" , "build" , "-o" , filepath .Join (testDir , "bootstrap" ), "./testdata/sigterm.go" )
37+ handlerBuild .Env = append (os .Environ (), "GOOS=linux" )
3238 require .NoError (t , handlerBuild .Run ())
3339
34- portI := 0
3540 for name , opts := range map [string ]struct {
3641 envVars []string
3742 assertLogs func (t * testing.T , logs string )
@@ -51,43 +56,53 @@ func TestEnableSigterm(t *testing.T) {
5156 },
5257 } {
5358 t .Run (name , func (t * testing.T ) {
54- portI += 1
55- addr1 := "localhost:" + strconv .Itoa (8000 + portI )
56- addr2 := "localhost:" + strconv .Itoa (9000 + portI )
57- rieInvokeAPI := "http://" + addr1 + "/2015-03-31/functions/function/invocations"
58- // run the runtime interface emulator, capture the logs for assertion
59- cmd := exec .Command ("aws-lambda-rie" , "--runtime-interface-emulator-address" , addr1 , "--runtime-api-address" , addr2 , "sigterm.handler" )
60- cmd .Env = append ([]string {
61- "PATH=" + testDir ,
62- "AWS_LAMBDA_FUNCTION_TIMEOUT=2" ,
63- }, opts .envVars ... )
64- cmd .Stderr = os .Stderr
59+ // Find an available port
60+ listener , err := net .Listen ("tcp" , "127.0.0.1:0" )
61+ require .NoError (t , err )
62+ port := listener .Addr ().(* net.TCPAddr ).Port
63+ listener .Close ()
64+
65+ cmdArgs := []string {"run" , "--rm" ,
66+ "-v" , testDir + ":/var/runtime:ro,delegated" ,
67+ "-p" , fmt .Sprintf ("%d:8080" , port ),
68+ "-e" , "AWS_LAMBDA_FUNCTION_TIMEOUT=2" }
69+ for _ , env := range opts .envVars {
70+ cmdArgs = append (cmdArgs , "-e" , env )
71+ }
72+ cmdArgs = append (cmdArgs , "public.ecr.aws/lambda/provided:al2023" , "bootstrap" )
73+
74+ cmd := exec .Command (containerCmd , cmdArgs ... )
6575 stdout , err := cmd .StdoutPipe ()
6676 require .NoError (t , err )
67- var logs string
68- done := make (chan interface {}) // closed on completion of log flush
77+ stderr , err := cmd .StderrPipe ()
78+ require .NoError (t , err )
79+
80+ var logBuf strings.Builder
81+ logDone := make (chan struct {})
6982 go func () {
70- logBytes , err := ioutil .ReadAll (stdout )
71- require .NoError (t , err )
72- logs = string (logBytes )
73- close (done )
83+ _ , _ = io .Copy (io .MultiWriter (os .Stderr , & logBuf ), io .MultiReader (stdout , stderr ))
84+ close (logDone )
7485 }()
86+
7587 require .NoError (t , cmd .Start ())
7688 t .Cleanup (func () { _ = cmd .Process .Kill () })
7789
78- // give a moment for the port to bind
79- time .Sleep (500 * time .Millisecond )
90+ time .Sleep (5 * time .Second ) // Wait for container to start
8091
81- client := & http.Client {Timeout : 5 * time .Second } // http client timeout to prevent case from hanging on aws-lambda-rie
82- resp , err := client .Post (rieInvokeAPI , "application/json" , strings .NewReader ("{}" ))
92+ client := & http.Client {Timeout : 5 * time .Second }
93+ invokeURL := fmt .Sprintf ("http://127.0.0.1:%d/2015-03-31/functions/function/invocations" , port )
94+ resp , err := client .Post (invokeURL , "application/json" , strings .NewReader ("{}" ))
8395 require .NoError (t , err )
8496 defer resp .Body .Close ()
8597 body , err := ioutil .ReadAll (resp .Body )
8698 assert .NoError (t , err )
87- assert .Equal (t , string (body ), "Task timed out after 2.00 seconds" )
99+ assert .Equal (t , "Task timed out after 2.00 seconds" , string (body ))
100+
101+ _ = cmd .Process .Kill ()
102+ _ = cmd .Wait ()
103+ <- logDone
88104
89- require .NoError (t , cmd .Process .Kill ()) // now ensure the logs are drained
90- <- done
105+ logs := logBuf .String ()
91106 t .Logf ("stdout:\n %s" , logs )
92107 opts .assertLogs (t , logs )
93108 })
0 commit comments