Skip to content

Commit f9bf927

Browse files
authored
feat: add support for Lambda Managed Instance (LMI) functions (#158)
1 parent ad5b46f commit f9bf927

File tree

305 files changed

+31823
-149
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

305 files changed

+31823
-149
lines changed

THIRD-PARTY-LICENSES.md

Lines changed: 519 additions & 98 deletions
Large diffs are not rendered by default.

cmd/aws-lambda-rie/main.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,17 @@
44
package main
55

66
import (
7+
"os"
8+
9+
"github.com/aws/aws-lambda-runtime-interface-emulator/internal/lambda-managed-instances/aws-lambda-rie/run"
10+
"github.com/aws/aws-lambda-runtime-interface-emulator/internal/lambda-managed-instances/rapidcore/env"
711
"github.com/aws/aws-lambda-runtime-interface-emulator/internal/lambda/rie"
812
)
913

1014
func main() {
15+
if _, ok := os.LookupEnv(env.AWS_LAMBDA_MAX_CONCURRENCY); ok {
16+
run.Run()
17+
return
18+
}
1119
rie.Run()
1220
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package agents
5+
6+
import (
7+
"log/slog"
8+
"path"
9+
"path/filepath"
10+
11+
"github.com/aws/aws-lambda-runtime-interface-emulator/internal/lambda-managed-instances/utils"
12+
)
13+
14+
const (
15+
ExtensionsDir = "/opt/extensions"
16+
)
17+
18+
func ListExternalAgentPaths(fileutils utils.FileUtil, dir string, root string) []string {
19+
var agentPaths []string
20+
if !isCanonical(dir) || !isCanonical(root) {
21+
slog.Warn("Agents base paths are not absolute and in canonical form", "dir", dir, "root", root)
22+
return agentPaths
23+
}
24+
fullDir := path.Join(root, dir)
25+
files, err := fileutils.ReadDirectory(fullDir)
26+
if err != nil {
27+
if fileutils.IsNotExist(err) {
28+
slog.Info("The extension's directory does not exist, assuming no extensions to be loaded", "fullDir", fullDir)
29+
} else {
30+
31+
slog.Error("Cannot list external agents", "err", err)
32+
}
33+
34+
return agentPaths
35+
}
36+
37+
for _, file := range files {
38+
if !file.IsDir() {
39+
40+
p := path.Join("/", dir, file.Name())
41+
agentPaths = append(agentPaths, p)
42+
}
43+
}
44+
return agentPaths
45+
}
46+
47+
func isCanonical(path string) bool {
48+
absPath, err := filepath.Abs(path)
49+
return err == nil && absPath == path
50+
}
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package agents
5+
6+
import (
7+
"os"
8+
"path"
9+
"testing"
10+
11+
"github.com/stretchr/testify/assert"
12+
"github.com/stretchr/testify/require"
13+
14+
"github.com/aws/aws-lambda-runtime-interface-emulator/internal/lambda-managed-instances/utils"
15+
)
16+
17+
type fileInfo struct {
18+
name string
19+
mode os.FileMode
20+
size int64
21+
target string
22+
}
23+
24+
func mkFile(name string, size int64, perm os.FileMode) fileInfo {
25+
return fileInfo{
26+
name: name,
27+
mode: perm,
28+
size: size,
29+
target: "",
30+
}
31+
}
32+
33+
func mkDir(name string, perm os.FileMode) fileInfo {
34+
return fileInfo{
35+
name: name,
36+
mode: perm | os.ModeDir,
37+
size: 0,
38+
target: "",
39+
}
40+
}
41+
42+
func mkLink(name, target string) fileInfo {
43+
return fileInfo{
44+
name: name,
45+
mode: os.ModeSymlink,
46+
size: 0,
47+
target: target,
48+
}
49+
}
50+
51+
func createFileTree(root string, fs []fileInfo) error {
52+
for _, info := range fs {
53+
filename := info.name
54+
dir := path.Join(root, path.Dir(filename))
55+
name := path.Base(filename)
56+
err := os.MkdirAll(dir, 0o775)
57+
if err != nil && !os.IsExist(err) {
58+
return err
59+
}
60+
switch {
61+
case os.ModeDir == info.mode&os.ModeDir:
62+
err := os.Mkdir(path.Join(dir, name), info.mode&os.ModePerm)
63+
if err != nil {
64+
return err
65+
}
66+
case os.ModeSymlink == info.mode&os.ModeSymlink:
67+
target := path.Join(root, info.target)
68+
_, err = os.Stat(target)
69+
if err != nil {
70+
return err
71+
}
72+
err := os.Symlink(target, path.Join(dir, name))
73+
if err != nil {
74+
return err
75+
}
76+
default:
77+
file, err := os.OpenFile(path.Join(dir, name), os.O_RDWR|os.O_CREATE, info.mode&os.ModePerm)
78+
if err != nil {
79+
return err
80+
}
81+
if err := file.Truncate(info.size); err != nil {
82+
return err
83+
}
84+
if err := file.Close(); err != nil {
85+
return err
86+
}
87+
}
88+
}
89+
90+
return nil
91+
}
92+
93+
func TestBaseEmpty(t *testing.T) {
94+
assert := assert.New(t)
95+
96+
fs := []fileInfo{
97+
mkDir("/opt/extensions", 0o777),
98+
}
99+
100+
tmpDir, err := os.MkdirTemp("", "ext-")
101+
require.NoError(t, err)
102+
103+
require.NoError(t, createFileTree(tmpDir, fs))
104+
defer func() { require.NoError(t, os.RemoveAll(tmpDir)) }()
105+
106+
fileUtils := utils.NewFileUtil()
107+
108+
agents := ListExternalAgentPaths(fileUtils, path.Join(tmpDir, "/opt/extensions"), "/")
109+
assert.Equal(0, len(agents))
110+
}
111+
112+
func TestBaseNotExist(t *testing.T) {
113+
assert := assert.New(t)
114+
115+
fileUtils := utils.NewFileUtil()
116+
117+
agents := ListExternalAgentPaths(fileUtils, "/path/which/does/not/exist", "/")
118+
assert.Equal(0, len(agents))
119+
}
120+
121+
func TestChrootNotExist(t *testing.T) {
122+
assert := assert.New(t)
123+
124+
fileUtils := utils.NewFileUtil()
125+
126+
agents := ListExternalAgentPaths(fileUtils, "/bin", "/does/not/exist")
127+
assert.Equal(0, len(agents))
128+
}
129+
130+
func TestBaseNotDir(t *testing.T) {
131+
assert := assert.New(t)
132+
133+
fs := []fileInfo{
134+
mkFile("/opt/extensions", 1, 0o777),
135+
}
136+
tmpDir, err := os.MkdirTemp("", "ext-")
137+
require.NoError(t, err)
138+
139+
require.NoError(t, createFileTree(tmpDir, fs))
140+
defer func() { require.NoError(t, os.RemoveAll(tmpDir)) }()
141+
142+
path := path.Join(tmpDir, "/opt/extensions")
143+
144+
fileUtils := utils.NewFileUtil()
145+
agents := ListExternalAgentPaths(fileUtils, path, "/")
146+
assert.Equal(0, len(agents))
147+
}
148+
149+
func TestFindAgentMixed(t *testing.T) {
150+
assert := assert.New(t)
151+
152+
listed := []fileInfo{
153+
mkFile("/opt/extensions/ok2", 1, 0o777),
154+
mkFile("/opt/extensions/ok1", 1, 0o777),
155+
mkFile("/opt/extensions/not_exec", 1, 0o666),
156+
mkFile("/opt/extensions/not_read", 1, 0o333),
157+
mkFile("/opt/extensions/empty_file", 0, 0o777),
158+
mkLink("/opt/extensions/link", "/opt/extensions/ok1"),
159+
}
160+
161+
unlisted := []fileInfo{
162+
mkDir("/opt/extensions/empty_dir", 0o777),
163+
mkDir("/opt/extensions/nonempty_dir", 0o777),
164+
mkFile("/opt/extensions/nonempty_dir/notok", 1, 0o777),
165+
}
166+
167+
fs := append([]fileInfo{}, listed...)
168+
fs = append(fs, unlisted...)
169+
170+
tmpDir, err := os.MkdirTemp("", "ext-")
171+
require.NoError(t, err)
172+
173+
require.NoError(t, createFileTree(tmpDir, fs))
174+
defer func() { require.NoError(t, os.RemoveAll(tmpDir)) }()
175+
176+
path := path.Join(tmpDir, "/opt/extensions")
177+
fileUtils := utils.NewFileUtil()
178+
agentPaths := ListExternalAgentPaths(fileUtils, path, "/")
179+
assert.Equal(len(listed), len(agentPaths))
180+
last := ""
181+
for index := range listed {
182+
if len(last) > 0 {
183+
assert.GreaterOrEqual(agentPaths[index], last)
184+
}
185+
last = agentPaths[index]
186+
}
187+
}
188+
189+
func TestFindAgentMixedInChroot(t *testing.T) {
190+
assert := assert.New(t)
191+
192+
listed := []fileInfo{
193+
mkFile("/opt/extensions/ok2", 1, 0o777),
194+
mkFile("/opt/extensions/ok1", 1, 0o777),
195+
mkFile("/opt/extensions/not_exec", 1, 0o666),
196+
mkFile("/opt/extensions/not_read", 1, 0o333),
197+
mkFile("/opt/extensions/empty_file", 0, 0o777),
198+
mkLink("/opt/extensions/link", "/opt/extensions/ok1"),
199+
}
200+
201+
unlisted := []fileInfo{
202+
mkDir("/opt/extensions/empty_dir", 0o777),
203+
mkDir("/opt/extensions/nonempty_dir", 0o777),
204+
mkFile("/opt/extensions/nonempty_dir/notok", 1, 0o777),
205+
}
206+
207+
fs := append([]fileInfo{}, listed...)
208+
fs = append(fs, unlisted...)
209+
210+
rootDir, err := os.MkdirTemp("", "rootfs")
211+
require.NoError(t, err)
212+
213+
require.NoError(t, createFileTree(rootDir, fs))
214+
defer func() { require.NoError(t, os.RemoveAll(rootDir)) }()
215+
fileUtils := utils.NewFileUtil()
216+
agentPaths := ListExternalAgentPaths(fileUtils, "/opt/extensions", rootDir)
217+
assert.Equal(len(listed), len(agentPaths))
218+
last := ""
219+
for index := range listed {
220+
if len(last) > 0 {
221+
assert.GreaterOrEqual(agentPaths[index], last)
222+
}
223+
last = agentPaths[index]
224+
}
225+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package appctx
5+
6+
import (
7+
"sync"
8+
)
9+
10+
type Key int
11+
12+
const (
13+
AppCtxInvokeErrorTraceDataKey Key = iota
14+
15+
AppCtxRuntimeReleaseKey
16+
17+
AppCtxInteropServerKey
18+
19+
AppCtxResponseSenderKey
20+
21+
AppCtxFirstFatalErrorKey
22+
)
23+
24+
type ApplicationContext interface {
25+
Store(key Key, value interface{})
26+
Load(key Key) (value interface{}, ok bool)
27+
Delete(key Key)
28+
GetOrDefault(key Key, defaultValue interface{}) interface{}
29+
StoreIfNotExists(key Key, value interface{}) interface{}
30+
}
31+
32+
type applicationContext struct {
33+
mux *sync.Mutex
34+
m map[Key]interface{}
35+
}
36+
37+
func (appCtx *applicationContext) Store(key Key, value interface{}) {
38+
appCtx.mux.Lock()
39+
defer appCtx.mux.Unlock()
40+
appCtx.m[key] = value
41+
}
42+
43+
func (appCtx *applicationContext) StoreIfNotExists(key Key, value interface{}) interface{} {
44+
appCtx.mux.Lock()
45+
defer appCtx.mux.Unlock()
46+
existing, found := appCtx.m[key]
47+
if found {
48+
return existing
49+
}
50+
appCtx.m[key] = value
51+
return nil
52+
}
53+
54+
func (appCtx *applicationContext) Load(key Key) (value interface{}, ok bool) {
55+
appCtx.mux.Lock()
56+
defer appCtx.mux.Unlock()
57+
value, ok = appCtx.m[key]
58+
return value, ok
59+
}
60+
61+
func (appCtx *applicationContext) Delete(key Key) {
62+
appCtx.mux.Lock()
63+
defer appCtx.mux.Unlock()
64+
delete(appCtx.m, key)
65+
}
66+
67+
func (appCtx *applicationContext) GetOrDefault(key Key, defaultValue interface{}) interface{} {
68+
if value, ok := appCtx.Load(key); ok {
69+
return value
70+
}
71+
return defaultValue
72+
}
73+
74+
func NewApplicationContext() ApplicationContext {
75+
return &applicationContext{
76+
mux: &sync.Mutex{},
77+
m: make(map[Key]interface{}),
78+
}
79+
}

0 commit comments

Comments
 (0)