Skip to content

Commit 7fd66cd

Browse files
add localstack adaptations
1 parent 2ca3e4a commit 7fd66cd

File tree

15 files changed

+768
-2
lines changed

15 files changed

+768
-2
lines changed

.github/workflows/build.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: Build
2+
3+
on:
4+
push:
5+
branches: [ localstack-poc ]
6+
pull_request:
7+
branches: [ localstack-poc ]
8+
9+
jobs:
10+
11+
build:
12+
runs-on: ubuntu-latest
13+
steps:
14+
- uses: actions/checkout@v2
15+
16+
- name: Set up Go
17+
uses: actions/setup-go@v2
18+
with:
19+
go-version: 1.17
20+
21+
- name: Build
22+
run: make compile-lambda-linux-all
23+
24+
- uses: actions/upload-artifact@v2
25+
with:
26+
name: aws-lambda-rie
27+
path: bin/*
28+

Makefile

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
# LOCALSTACK CHANGES 2022-03-10: remove linker flags and add gc flags for delve debugger
2+
13
# RELEASE_BUILD_LINKER_FLAGS disables DWARF and symbol table generation to reduce binary size
2-
RELEASE_BUILD_LINKER_FLAGS=-s -w
4+
#RELEASE_BUILD_LINKER_FLAGS=-s -w
5+
36

47
BINARY_NAME=aws-lambda-rie
58
ARCH=x86_64
@@ -24,7 +27,7 @@ compile-with-docker:
2427
docker run --env GOPROXY=direct -v $(shell pwd):/LambdaRuntimeLocal -w /LambdaRuntimeLocal golang:1.17 make ARCH=${ARCH} compile-lambda-linux
2528

2629
compile-lambda-linux:
27-
CGO_ENABLED=0 GOOS=linux GOARCH=${GO_ARCH_${ARCH}} go build -ldflags "${RELEASE_BUILD_LINKER_FLAGS}" -o ${DESTINATION_${ARCH}} ./cmd/aws-lambda-rie
30+
CGO_ENABLED=0 GOOS=linux GOARCH=${GO_ARCH_${ARCH}} go build -ldflags "${RELEASE_BUILD_LINKER_FLAGS}" -gcflags="all=-N -l" -o ${DESTINATION_${ARCH}} ./cmd/aws-lambda-rie
2831

2932
tests:
3033
go test ./...

cmd/localstack/awsutil.go

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
// LOCALSTACK CHANGES 2022-03-10: modified/collected file from /cmd/aws-lambda-rie/* into this util
4+
// LOCALSTACK CHANGES 2022-03-10: minor refactoring of PrintEndReports
5+
6+
package main
7+
8+
import (
9+
"fmt"
10+
"github.com/jessevdk/go-flags"
11+
log "github.com/sirupsen/logrus"
12+
"go.amzn.com/lambda/interop"
13+
"go.amzn.com/lambda/rapidcore"
14+
"io"
15+
"math"
16+
"net/http"
17+
"os"
18+
"strings"
19+
"time"
20+
)
21+
22+
const (
23+
optBootstrap = "/opt/bootstrap"
24+
runtimeBootstrap = "/var/runtime/bootstrap"
25+
)
26+
27+
type options struct {
28+
LogLevel string `long:"log-level" default:"info" description:"log level"`
29+
InitCachingEnabled bool `long:"enable-init-caching" description:"Enable support for Init Caching"`
30+
}
31+
32+
func getCLIArgs() (options, []string) {
33+
var opts options
34+
parser := flags.NewParser(&opts, flags.IgnoreUnknown)
35+
args, err := parser.ParseArgs(os.Args)
36+
37+
if err != nil {
38+
log.WithError(err).Fatal("Failed to parse command line arguments:", os.Args)
39+
}
40+
41+
return opts, args
42+
}
43+
44+
func isBootstrapFileExist(filePath string) bool {
45+
file, err := os.Stat(filePath)
46+
return !os.IsNotExist(err) && !file.IsDir()
47+
}
48+
49+
func getBootstrap(args []string, opts options) (*rapidcore.Bootstrap, string) {
50+
var bootstrapLookupCmd []string
51+
var handler string
52+
currentWorkingDir := "/var/task" // default value
53+
54+
if len(args) <= 1 {
55+
// set default value to /var/task/bootstrap, but switch to the other options if it doesn't exist
56+
bootstrapLookupCmd = []string{
57+
fmt.Sprintf("%s/bootstrap", currentWorkingDir),
58+
}
59+
60+
if !isBootstrapFileExist(bootstrapLookupCmd[0]) {
61+
var bootstrapCmdCandidates = []string{
62+
optBootstrap,
63+
runtimeBootstrap,
64+
}
65+
66+
for i, bootstrapCandidate := range bootstrapCmdCandidates {
67+
if isBootstrapFileExist(bootstrapCandidate) {
68+
bootstrapLookupCmd = []string{bootstrapCmdCandidates[i]}
69+
break
70+
}
71+
}
72+
}
73+
74+
// handler is used later to set an env var for Lambda Image support
75+
handler = ""
76+
} else if len(args) > 1 {
77+
78+
bootstrapLookupCmd = args[1:]
79+
80+
if cwd, err := os.Getwd(); err == nil {
81+
currentWorkingDir = cwd
82+
}
83+
84+
if len(args) > 2 {
85+
// Assume last arg is the handler
86+
handler = args[len(args)-1]
87+
}
88+
89+
log.Infof("exec '%s' (cwd=%s, handler=%s)", args[1], currentWorkingDir, handler)
90+
91+
} else {
92+
log.Panic("insufficient arguments: bootstrap not provided")
93+
}
94+
95+
return rapidcore.NewBootstrapSingleCmd(bootstrapLookupCmd, currentWorkingDir), handler
96+
}
97+
98+
func PrintEndReports(invokeId string, initDuration string, memorySize string, invokeStart time.Time, timeoutDuration time.Duration, w io.Writer) {
99+
// Calculate invoke duration
100+
invokeDuration := math.Min(float64(time.Now().Sub(invokeStart).Nanoseconds()),
101+
float64(timeoutDuration.Nanoseconds())) / float64(time.Millisecond)
102+
103+
_, _ = fmt.Fprintln(w, "END RequestId: "+invokeId)
104+
// We set the Max Memory Used and Memory Size to be the same (whatever it is set to) since there is
105+
// not a clean way to get this information from rapidcore
106+
_, _ = fmt.Fprintf(w,
107+
"REPORT RequestId: %s\t"+
108+
initDuration+
109+
"Duration: %.2f ms\t"+
110+
"Billed Duration: %.f ms\t"+
111+
"Memory Size: %s MB\t"+
112+
"Max Memory Used: %s MB\t\n",
113+
invokeId, invokeDuration, math.Ceil(invokeDuration), memorySize, memorySize)
114+
}
115+
116+
type Sandbox interface {
117+
Init(i *interop.Init, invokeTimeoutMs int64)
118+
Invoke(responseWriter http.ResponseWriter, invoke *interop.Invoke) error
119+
}
120+
121+
func GetenvWithDefault(key string, defaultValue string) string {
122+
envValue := os.Getenv(key)
123+
124+
if envValue == "" {
125+
return defaultValue
126+
}
127+
128+
return envValue
129+
}
130+
131+
func InitHandler(sandbox Sandbox, functionVersion string, timeout int64) (time.Time, time.Time) {
132+
additionalFunctionEnvironmentVariables := map[string]string{}
133+
134+
// Add default Env Vars if they were not defined. This is a required otherwise 1p Python2.7, Python3.6, and
135+
// possibly others pre runtime API runtimes will fail. This will be overwritten if they are defined on the system.
136+
additionalFunctionEnvironmentVariables["AWS_LAMBDA_LOG_GROUP_NAME"] = "/aws/lambda/Functions"
137+
additionalFunctionEnvironmentVariables["AWS_LAMBDA_LOG_STREAM_NAME"] = "$LATEST"
138+
additionalFunctionEnvironmentVariables["AWS_LAMBDA_FUNCTION_VERSION"] = "$LATEST"
139+
additionalFunctionEnvironmentVariables["AWS_LAMBDA_FUNCTION_MEMORY_SIZE"] = "3008"
140+
additionalFunctionEnvironmentVariables["AWS_LAMBDA_FUNCTION_NAME"] = "test_function"
141+
142+
// Forward Env Vars from the running system (container) to what the function can view. Without this, Env Vars will
143+
// not be viewable when the function runs.
144+
for _, env := range os.Environ() {
145+
// Split the env into by the first "=". This will account for if the env var's value has a '=' in it
146+
envVar := strings.SplitN(env, "=", 2)
147+
additionalFunctionEnvironmentVariables[envVar[0]] = envVar[1]
148+
}
149+
150+
initStart := time.Now()
151+
// pass to rapid
152+
sandbox.Init(&interop.Init{
153+
Handler: GetenvWithDefault("AWS_LAMBDA_FUNCTION_HANDLER", os.Getenv("_HANDLER")),
154+
CorrelationID: "initCorrelationID",
155+
AwsKey: os.Getenv("AWS_ACCESS_KEY_ID"),
156+
AwsSecret: os.Getenv("AWS_SECRET_ACCESS_KEY"),
157+
AwsSession: os.Getenv("AWS_SESSION_TOKEN"),
158+
XRayDaemonAddress: "0.0.0.0:0", // TODO
159+
FunctionName: GetenvWithDefault("AWS_LAMBDA_FUNCTION_NAME", "test_function"),
160+
FunctionVersion: functionVersion,
161+
162+
CustomerEnvironmentVariables: additionalFunctionEnvironmentVariables,
163+
}, timeout*1000)
164+
initEnd := time.Now()
165+
return initStart, initEnd
166+
}

0 commit comments

Comments
 (0)