docker-lambda
Advanced tools
| FROM lambci/lambda-base:build | ||
| ENV PATH=/var/lang/bin:/usr/local/bin:/usr/bin/:/bin \ | ||
| LD_LIBRARY_PATH=/var/lang/lib:/lib64:/usr/lib64:/var/runtime:/var/runtime/lib:/var/task:/var/task/lib \ | ||
| AWS_EXECUTION_ENV=AWS_Lambda_dotnetcore2.0 \ | ||
| DOTNET_SDK_VERSION=2.1.3 \ | ||
| DOTNET_CLI_TELEMETRY_OPTOUT=1 \ | ||
| NUGET_XMLDOC_MODE=skip | ||
| RUN rm -rf /var/runtime /var/lang && \ | ||
| curl https://lambci.s3.amazonaws.com/fs/dotnetcore2.0.tgz | tar -zx -C / && \ | ||
| yum install -y libunwind && \ | ||
| curl https://dot.net/v1/dotnet-install.sh | bash -s -- -v $DOTNET_SDK_VERSION -i /var/lang/bin && \ | ||
| mkdir /tmp/warmup && \ | ||
| cd /tmp/warmup && \ | ||
| dotnet new && \ | ||
| cd / && \ | ||
| rm -rf /tmp/warmup /tmp/NuGetScratch | ||
| CMD ["dotnet", "build"] |
| FROM microsoft/dotnet:2.0-sdk | ||
| WORKDIR /source | ||
| # cache restore result | ||
| COPY MockBootstraps/*.csproj . | ||
| RUN dotnet restore | ||
| # copy the rest of the code | ||
| COPY MockBootstraps/ . | ||
| RUN dotnet publish --output /app/ --configuration Release | ||
| FROM lambci/lambda-base | ||
| ENV PATH=/var/lang/bin:/usr/local/bin:/usr/bin/:/bin \ | ||
| LD_LIBRARY_PATH=/var/lang/lib:/lib64:/usr/lib64:/var/runtime:/var/runtime/lib:/var/task:/var/task/lib \ | ||
| AWS_EXECUTION_ENV=AWS_Lambda_dotnetcore2.0 | ||
| RUN rm -rf /var/runtime /var/lang && \ | ||
| curl https://lambci.s3.amazonaws.com/fs/dotnetcore2.0.tgz | tar -zx -C / | ||
| COPY --from=0 /app/MockBootstraps.* /var/runtime/ | ||
| ENTRYPOINT ["/var/lang/bin/dotnet", "/var/runtime/MockBootstraps.dll"] |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
| <Project Sdk="Microsoft.NET.Sdk"> | ||
| <PropertyGroup> | ||
| <OutputType>Exe</OutputType> | ||
| <TargetFramework>netcoreapp2.0</TargetFramework> | ||
| <RootNamespace>MockLambdaRuntime</RootNamespace> | ||
| </PropertyGroup> | ||
| <ItemGroup> | ||
| <Reference Include="Bootstrap"> | ||
| <HintPath>lib\Bootstrap.dll</HintPath> | ||
| </Reference> | ||
| <Reference Include="Amazon.Lambda.Core"> | ||
| <HintPath>lib\Amazon.Lambda.Core.dll</HintPath> | ||
| </Reference> | ||
| </ItemGroup> | ||
| </Project> |
Sorry, the diff of this file is not supported yet
| using System; | ||
| using System.Diagnostics; | ||
| using System.IO; | ||
| using System.Text; | ||
| namespace MockLambdaRuntime | ||
| { | ||
| public class MockLambdaContext | ||
| { | ||
| static Random random = new Random(); | ||
| /// Creates a mock context from a given Lambda handler and event | ||
| public MockLambdaContext(string handler, string eventBody) | ||
| { | ||
| RequestId = Guid.NewGuid().ToString(); | ||
| StartTime = DateTime.Now; | ||
| InputStream = new MemoryStream(); | ||
| OutputStream = new MemoryStream(); | ||
| var eventData = Encoding.UTF8.GetBytes(eventBody); | ||
| InputStream.Write(eventData, 0, eventData.Length); | ||
| InputStream.Position = 0; | ||
| Environment.SetEnvironmentVariable("AWS_LAMBDA_FUNCTION_NAME", FunctionName); | ||
| Environment.SetEnvironmentVariable("AWS_LAMBDA_FUNCTION_VERSION", FunctionVersion); | ||
| Environment.SetEnvironmentVariable("AWS_LAMBDA_FUNCTION_MEMORY_SIZE", MemorySize.ToString()); | ||
| Environment.SetEnvironmentVariable("AWS_LAMBDA_LOG_GROUP_NAME", LogGroup); | ||
| Environment.SetEnvironmentVariable("AWS_LAMBDA_LOG_STREAM_NAME", LogStream); | ||
| Environment.SetEnvironmentVariable("AWS_REGION", Region); | ||
| Environment.SetEnvironmentVariable("AWS_DEFAULT_REGION", Region); | ||
| Environment.SetEnvironmentVariable("_HANDLER", handler); | ||
| } | ||
| /// Calculates the remaining time using current time and timeout | ||
| public TimeSpan RemainingTime() | ||
| { | ||
| return StartTime + TimeSpan.FromSeconds(Timeout) - DateTime.Now; | ||
| } | ||
| public long Duration => (long)(DateTime.Now - StartTime).TotalMilliseconds; | ||
| public long BilledDuration => (long)(Math.Ceiling((DateTime.Now - StartTime).TotalMilliseconds / 100)) * 100; | ||
| public long MemoryUsed => Process.GetCurrentProcess().WorkingSet64; | ||
| public Stream InputStream { get; } | ||
| public Stream OutputStream { get; } | ||
| public string OutputText | ||
| { | ||
| get | ||
| { | ||
| OutputStream.Position = 0; | ||
| using (TextReader reader = new StreamReader(OutputStream)) | ||
| { | ||
| return reader.ReadToEnd(); | ||
| } | ||
| } | ||
| } | ||
| public string RequestId { get; } | ||
| public DateTime StartTime { get; } | ||
| public int Timeout => Convert.ToInt32(EnvHelper.GetOrDefault("AWS_LAMBDA_FUNCTION_TIMEOUT", "300")); | ||
| public int MemorySize => Convert.ToInt32(EnvHelper.GetOrDefault("AWS_LAMBDA_FUNCTION_MEMORY_SIZE", "1536")); | ||
| public string FunctionName => EnvHelper.GetOrDefault("AWS_LAMBDA_FUNCTION_NAME", "test"); | ||
| public string FunctionVersion => EnvHelper.GetOrDefault("AWS_LAMBDA_FUNCTION_VERSION", "$LATEST"); | ||
| public string LogGroup => EnvHelper.GetOrDefault("AWS_LAMBDA_LOG_GROUP_NAME", $"/aws/lambda/{FunctionName}"); | ||
| public string LogStream => EnvHelper.GetOrDefault("AWS_LAMBDA_LOG_STREAM_NAME", RandomLogStreamName); | ||
| public string Region => EnvHelper.GetOrDefault("AWS_REGION", EnvHelper.GetOrDefault("AWS_DEFAULT_REGION", "us-east-1")); | ||
| public string AccountId => EnvHelper.GetOrDefault("AWS_ACCOUNT_ID", "000000000000"); | ||
| public string Arn => $"arn:aws:lambda:{Region}:{AccountId}:function:{FunctionName}"; | ||
| string RandomLogStreamName => $"{DateTime.Now.ToString("yyyy/MM/dd")}/[{FunctionVersion}]{random.Next().ToString("x") + random.Next().ToString("x")}"; | ||
| } | ||
| } |
Sorry, the diff of this file is not supported yet
| <?xml version="1.0" encoding="utf-8" standalone="no"?> | ||
| <Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||
| <PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' "> | ||
| <RestoreSuccess Condition=" '$(RestoreSuccess)' == '' ">True</RestoreSuccess> | ||
| <RestoreTool Condition=" '$(RestoreTool)' == '' ">NuGet</RestoreTool> | ||
| <ProjectAssetsFile Condition=" '$(ProjectAssetsFile)' == '' ">/Users/michael/github/docker-lambda/dotnetcore2.0/run/MockBootstraps/obj/project.assets.json</ProjectAssetsFile> | ||
| <NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">/Users/michael/.nuget/packages/</NuGetPackageRoot> | ||
| <NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">/Users/michael/.nuget/packages/;/usr/local/share/dotnet/sdk/NuGetFallbackFolder</NuGetPackageFolders> | ||
| <NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle> | ||
| <NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">4.3.1</NuGetToolVersion> | ||
| </PropertyGroup> | ||
| <PropertyGroup> | ||
| <MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects> | ||
| </PropertyGroup> | ||
| <ImportGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' "> | ||
| <Import Project="$(NuGetPackageRoot)microsoft.netcore.app/2.0.0/build/netcoreapp2.0/Microsoft.NETCore.App.props" Condition="Exists('$(NuGetPackageRoot)microsoft.netcore.app/2.0.0/build/netcoreapp2.0/Microsoft.NETCore.App.props')" /> | ||
| </ImportGroup> | ||
| </Project> |
| <?xml version="1.0" encoding="utf-8" standalone="no"?> | ||
| <Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||
| <PropertyGroup> | ||
| <MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects> | ||
| </PropertyGroup> | ||
| <ImportGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' "> | ||
| <Import Project="$(NuGetPackageRoot)netstandard.library/2.0.0/build/netstandard2.0/NETStandard.Library.targets" Condition="Exists('$(NuGetPackageRoot)netstandard.library/2.0.0/build/netstandard2.0/NETStandard.Library.targets')" /> | ||
| <Import Project="$(NuGetPackageRoot)microsoft.netcore.app/2.0.0/build/netcoreapp2.0/Microsoft.NETCore.App.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.netcore.app/2.0.0/build/netcoreapp2.0/Microsoft.NETCore.App.targets')" /> | ||
| </ImportGroup> | ||
| </Project> |
Sorry, the diff of this file is too big to display
| using System; | ||
| using System.Collections.Generic; | ||
| using System.IO; | ||
| using System.Reflection; | ||
| using System.Runtime.Loader; | ||
| using AWSLambda.Internal.Bootstrap; | ||
| using AWSLambda.Internal.Bootstrap.Context; | ||
| namespace MockLambdaRuntime | ||
| { | ||
| class Program | ||
| { | ||
| /// Task root of lambda task | ||
| static string lambdaTaskRoot = EnvHelper.GetOrDefault("LAMBDA_TASK_ROOT", "/var/task"); | ||
| /// Program entry point | ||
| static void Main(string[] args) | ||
| { | ||
| AssemblyLoadContext.Default.Resolving += OnAssemblyResolving; | ||
| var handler = GetFunctionHandler(args); | ||
| var body = GetEventBody(args); | ||
| var lambdaContext = new MockLambdaContext(handler, body); | ||
| var userCodeLoader = new UserCodeLoader(handler, InternalLogger.NO_OP_LOGGER); | ||
| userCodeLoader.Init(Console.Error.WriteLine); | ||
| var lambdaContextInternal = new LambdaContextInternal(lambdaContext.RemainingTime, | ||
| LogAction, new Lazy<CognitoClientContextInternal>(), | ||
| lambdaContext.RequestId, | ||
| new Lazy<string>(lambdaContext.Arn), | ||
| new Lazy<string>(string.Empty), | ||
| new Lazy<string>(string.Empty), | ||
| Environment.GetEnvironmentVariables()); | ||
| Exception lambdaException = null; | ||
| LogRequestStart(lambdaContext); | ||
| try | ||
| { | ||
| userCodeLoader.Invoke(lambdaContext.InputStream, lambdaContext.OutputStream, lambdaContextInternal); | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| lambdaException = ex; | ||
| } | ||
| LogRequestEnd(lambdaContext); | ||
| if (lambdaException == null) | ||
| { | ||
| Console.WriteLine(lambdaContext.OutputText); | ||
| } | ||
| else | ||
| { | ||
| Console.Error.WriteLine(lambdaException); | ||
| } | ||
| } | ||
| /// Called when an assembly could not be resolved | ||
| private static Assembly OnAssemblyResolving(AssemblyLoadContext context, AssemblyName assembly) | ||
| { | ||
| return context.LoadFromAssemblyPath(Path.Combine(lambdaTaskRoot, $"{assembly.Name}.dll")); | ||
| } | ||
| /// Try to log everything to stderr except the function result | ||
| private static void LogAction(string text) | ||
| { | ||
| Console.Error.WriteLine(text); | ||
| } | ||
| static void LogRequestStart(MockLambdaContext context) | ||
| { | ||
| Console.Error.WriteLine($"START RequestId: {context.RequestId} Version: {context.FunctionVersion}"); | ||
| } | ||
| static void LogRequestEnd(MockLambdaContext context) | ||
| { | ||
| Console.Error.WriteLine($"END RequestId: {context.RequestId}"); | ||
| Console.Error.WriteLine($"REPORT RequestId {context.RequestId}\t" + | ||
| $"Duration: {context.Duration} ms\t" + | ||
| $"Billed Duration: {context.BilledDuration} ms\t" + | ||
| $"Memory Size {context.MemorySize} MB\t" + | ||
| $"Max Memory Used: {context.MemoryUsed / (1024 * 1024)} MB"); | ||
| } | ||
| /// Gets the function handler from arguments or environment | ||
| static string GetFunctionHandler(string[] args) | ||
| { | ||
| return args.Length > 0 ? args[0] : EnvHelper.GetOrDefault("AWS_LAMBDA_FUNCTION_HANDLER", string.Empty); | ||
| } | ||
| /// Gets the event body from arguments or environment | ||
| static string GetEventBody(string[] args) | ||
| { | ||
| return args.Length > 1 ? args[1] : (Environment.GetEnvironmentVariable("AWS_LAMBDA_EVENT_BODY") ?? | ||
| (Environment.GetEnvironmentVariable("DOCKER_LAMBDA_USE_STDIN") != null ? Console.In.ReadToEnd() : "{}")); | ||
| } | ||
| } | ||
| class EnvHelper | ||
| { | ||
| /// Gets the given environment variable with a fallback if it doesn't exist | ||
| public static string GetOrDefault(string name, string fallback) | ||
| { | ||
| return Environment.GetEnvironmentVariable(name) ?? fallback; | ||
| } | ||
| } | ||
| } |
| #!/bin/sh | ||
| curl https://lambci.s3.amazonaws.com/fs/dotnetcore2.0.tgz | \ | ||
| tar -xz var/runtime/Amazon.Lambda.Core.dll var/runtime/Bootstrap.dll | ||
| mv ./var/runtime/*.dll ./MockBootstraps/lib/ | ||
| rm -rf ./var |
| FROM lambci/lambda-base:build | ||
| ENV GOLANG_VERSION=1.10 \ | ||
| GOPATH=/go \ | ||
| PATH=/go/bin:/usr/local/go/bin:$PATH | ||
| WORKDIR /go/src/handler | ||
| RUN rm -rf /var/runtime /var/lang && \ | ||
| curl https://lambci.s3.amazonaws.com/fs/go1.x.tgz | tar -zx -C / && \ | ||
| curl https://dl.google.com/go/go${GOLANG_VERSION}.linux-amd64.tar.gz | tar -zx -C /usr/local && \ | ||
| go get github.com/golang/dep/cmd/dep && \ | ||
| go install github.com/golang/dep/cmd/dep | ||
| CMD ["dep", "ensure"] |
| package main | ||
| import ( | ||
| "bufio" | ||
| "bytes" | ||
| "encoding/hex" | ||
| "encoding/json" | ||
| "fmt" | ||
| "github.com/aws/aws-lambda-go/lambda/messages" | ||
| "io/ioutil" | ||
| "math" | ||
| "math/rand" | ||
| "net" | ||
| "net/rpc" | ||
| "os" | ||
| "os/exec" | ||
| "reflect" | ||
| "regexp" | ||
| "strconv" | ||
| "syscall" | ||
| "time" | ||
| ) | ||
| func main() { | ||
| rand.Seed(time.Now().UTC().UnixNano()) | ||
| var handler string | ||
| if len(os.Args) > 1 { | ||
| handler = os.Args[1] | ||
| } else { | ||
| handler = getEnv("AWS_LAMBDA_FUNCTION_HANDLER", getEnv("_HANDLER", "handler")) | ||
| } | ||
| var eventBody string | ||
| if len(os.Args) > 2 { | ||
| eventBody = os.Args[2] | ||
| } else { | ||
| eventBody = os.Getenv("AWS_LAMBDA_EVENT_BODY") | ||
| if eventBody == "" { | ||
| if os.Getenv("DOCKER_LAMBDA_USE_STDIN") != "" { | ||
| stdin, _ := ioutil.ReadAll(os.Stdin) | ||
| eventBody = string(stdin) | ||
| } else { | ||
| eventBody = "{}" | ||
| } | ||
| } | ||
| } | ||
| mockContext := &MockLambdaContext{ | ||
| RequestId: fakeGuid(), | ||
| EventBody: eventBody, | ||
| FnName: getEnv("AWS_LAMBDA_FUNCTION_NAME", "test"), | ||
| Version: getEnv("AWS_LAMBDA_FUNCTION_VERSION", "$LATEST"), | ||
| MemSize: getEnv("AWS_LAMBDA_FUNCTION_MEMORY_SIZE", "1536"), | ||
| Timeout: getEnv("AWS_LAMBDA_FUNCTION_TIMEOUT", "300"), | ||
| Region: getEnv("AWS_REGION", getEnv("AWS_DEFAULT_REGION", "us-east-1")), | ||
| AccountId: getEnv("AWS_ACCOUNT_ID", strconv.FormatInt(int64(rand.Int31()), 10)), | ||
| Start: time.Now(), | ||
| Pid: 1, | ||
| } | ||
| mockContext.ParseTimeout() | ||
| awsAccessKey := getEnv("AWS_ACCESS_KEY", getEnv("AWS_ACCESS_KEY_ID", "SOME_ACCESS_KEY_ID")) | ||
| awsSecretKey := getEnv("AWS_SECRET_KEY", getEnv("AWS_SECRET_ACCESS_KEY", "SOME_SECRET_ACCESS_KEY")) | ||
| awsSessionToken := getEnv("AWS_SESSION_TOKEN", os.Getenv("AWS_SECURITY_TOKEN")) | ||
| port := getEnv("_LAMBDA_SERVER_PORT", "54321") | ||
| os.Setenv("AWS_LAMBDA_FUNCTION_NAME", mockContext.FnName) | ||
| os.Setenv("AWS_LAMBDA_FUNCTION_VERSION", mockContext.Version) | ||
| os.Setenv("AWS_LAMBDA_FUNCTION_MEMORY_SIZE", mockContext.MemSize) | ||
| os.Setenv("AWS_LAMBDA_LOG_GROUP_NAME", "/aws/lambda/"+mockContext.FnName) | ||
| os.Setenv("AWS_LAMBDA_LOG_STREAM_NAME", logStreamName(mockContext.Version)) | ||
| os.Setenv("AWS_REGION", mockContext.Region) | ||
| os.Setenv("AWS_DEFAULT_REGION", mockContext.Region) | ||
| os.Setenv("_HANDLER", handler) | ||
| cmd := exec.Command("/var/task/" + handler) | ||
| cmd.Env = append(os.Environ(), | ||
| "_LAMBDA_SERVER_PORT="+port, | ||
| "AWS_ACCESS_KEY="+awsAccessKey, | ||
| "AWS_ACCESS_KEY_ID="+awsAccessKey, | ||
| "AWS_SECRET_KEY="+awsSecretKey, | ||
| "AWS_SECRET_ACCESS_KEY="+awsSecretKey, | ||
| ) | ||
| if len(awsSessionToken) > 0 { | ||
| cmd.Env = append(cmd.Env, | ||
| "AWS_SESSION_TOKEN="+awsSessionToken, | ||
| "AWS_SECURITY_TOKEN="+awsSessionToken, | ||
| ) | ||
| } | ||
| cmd.Stdout = os.Stderr | ||
| cmd.Stderr = os.Stderr | ||
| cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} | ||
| var err error | ||
| if err = cmd.Start(); err != nil { | ||
| defer abortRequest(mockContext, err) | ||
| return | ||
| } | ||
| mockContext.Pid = cmd.Process.Pid | ||
| defer syscall.Kill(-mockContext.Pid, syscall.SIGKILL) | ||
| var conn net.Conn | ||
| for { | ||
| conn, err = net.Dial("tcp", ":"+port) | ||
| if mockContext.HasExpired() { | ||
| defer abortRequest(mockContext, mockContext.TimeoutErr()) | ||
| return | ||
| } | ||
| if err == nil { | ||
| break | ||
| } | ||
| if oerr, ok := err.(*net.OpError); ok { | ||
| // Connection refused, try again | ||
| if oerr.Op == "dial" && oerr.Net == "tcp" { | ||
| time.Sleep(5 * time.Millisecond) | ||
| continue | ||
| } | ||
| } | ||
| defer abortRequest(mockContext, err) | ||
| return | ||
| } | ||
| client := rpc.NewClient(conn) | ||
| for { | ||
| err = client.Call("Function.Ping", messages.PingRequest{}, &messages.PingResponse{}) | ||
| if mockContext.HasExpired() { | ||
| defer abortRequest(mockContext, mockContext.TimeoutErr()) | ||
| return | ||
| } | ||
| if err == nil { | ||
| break | ||
| } | ||
| time.Sleep(5 * time.Millisecond) | ||
| } | ||
| // XXX: The Go runtime seems to amortize the startup time, reset it here | ||
| mockContext.Start = time.Now() | ||
| logStartRequest(mockContext) | ||
| err = client.Call("Function.Invoke", mockContext.Request(), &mockContext.Reply) | ||
| // We want the process killed before this, so defer it | ||
| defer logEndRequest(mockContext, err) | ||
| } | ||
| func abortRequest(mockContext *MockLambdaContext, err error) { | ||
| logStartRequest(mockContext) | ||
| logEndRequest(mockContext, err) | ||
| } | ||
| func logStartRequest(mockContext *MockLambdaContext) { | ||
| systemLog("START RequestId: " + mockContext.RequestId + " Version: " + mockContext.Version) | ||
| } | ||
| func logEndRequest(mockContext *MockLambdaContext, err error) { | ||
| curMem, _ := calculateMemoryInMb(mockContext.Pid) | ||
| diffMs := math.Min(float64(time.Now().Sub(mockContext.Start).Nanoseconds()), | ||
| float64(mockContext.TimeoutDuration.Nanoseconds())) / 1e6 | ||
| systemLog("END RequestId: " + mockContext.RequestId) | ||
| systemLog(fmt.Sprintf( | ||
| "REPORT RequestId: %s\t"+ | ||
| "Duration: %.2f ms\t"+ | ||
| "Billed Duration: %.f ms\t"+ | ||
| "Memory Size: %s MB\t"+ | ||
| "Max Memory Used: %d MB\t", | ||
| mockContext.RequestId, diffMs, math.Ceil(diffMs/100)*100, mockContext.MemSize, curMem)) | ||
| if err == nil && mockContext.HasExpired() { | ||
| err = mockContext.TimeoutErr() | ||
| } | ||
| if err != nil { | ||
| responseErr := messages.InvokeResponse_Error{ | ||
| Message: err.Error(), | ||
| Type: getErrorType(err), | ||
| } | ||
| if responseErr.Type == "errorString" { | ||
| responseErr.Type = "" | ||
| if responseErr.Message == "unexpected EOF" { | ||
| responseErr.Message = "RequestId: " + mockContext.RequestId + " Process exited before completing request" | ||
| } | ||
| } | ||
| systemErr(&responseErr) | ||
| os.Exit(1) | ||
| } | ||
| if mockContext.Reply.Error != nil { | ||
| systemErr(mockContext.Reply.Error) | ||
| os.Exit(1) | ||
| } | ||
| fmt.Println(string(mockContext.Reply.Payload)) | ||
| } | ||
| func getEnv(key, fallback string) string { | ||
| value := os.Getenv(key) | ||
| if value != "" { | ||
| return value | ||
| } | ||
| return fallback | ||
| } | ||
| func fakeGuid() string { | ||
| randBuf := make([]byte, 16) | ||
| rand.Read(randBuf) | ||
| hexBuf := make([]byte, hex.EncodedLen(len(randBuf))+4) | ||
| hex.Encode(hexBuf[0:8], randBuf[0:4]) | ||
| hexBuf[8] = '-' | ||
| hex.Encode(hexBuf[9:13], randBuf[4:6]) | ||
| hexBuf[13] = '-' | ||
| hex.Encode(hexBuf[14:18], randBuf[6:8]) | ||
| hexBuf[18] = '-' | ||
| hex.Encode(hexBuf[19:23], randBuf[8:10]) | ||
| hexBuf[23] = '-' | ||
| hex.Encode(hexBuf[24:], randBuf[10:]) | ||
| hexBuf[14] = '1' // Make it look like a v1 guid | ||
| return string(hexBuf) | ||
| } | ||
| func logStreamName(version string) string { | ||
| randBuf := make([]byte, 16) | ||
| rand.Read(randBuf) | ||
| hexBuf := make([]byte, hex.EncodedLen(len(randBuf))) | ||
| hex.Encode(hexBuf, randBuf) | ||
| return time.Now().Format("2006/01/02") + "/[" + version + "]" + string(hexBuf) | ||
| } | ||
| func arn(region string, accountId string, fnName string) string { | ||
| nonDigit := regexp.MustCompile(`[^\d]`) | ||
| return "arn:aws:lambda:" + region + ":" + nonDigit.ReplaceAllString(accountId, "") + ":function:" + fnName | ||
| } | ||
| // Thanks to https://stackoverflow.com/a/31881979 | ||
| func calculateMemoryInMb(pid int) (uint64, error) { | ||
| f, err := os.Open(fmt.Sprintf("/proc/%d/smaps", pid)) | ||
| if err != nil { | ||
| return 0, err | ||
| } | ||
| defer f.Close() | ||
| res := uint64(0) | ||
| pfx := []byte("Pss:") | ||
| r := bufio.NewScanner(f) | ||
| for r.Scan() { | ||
| line := r.Bytes() | ||
| if bytes.HasPrefix(line, pfx) { | ||
| var size uint64 | ||
| _, err := fmt.Sscanf(string(line[4:]), "%d", &size) | ||
| if err != nil { | ||
| return 0, err | ||
| } | ||
| res += size | ||
| } | ||
| } | ||
| if err := r.Err(); err != nil { | ||
| return 0, err | ||
| } | ||
| return res / 1024, nil | ||
| } | ||
| func getErrorType(err interface{}) string { | ||
| if errorType := reflect.TypeOf(err); errorType.Kind() == reflect.Ptr { | ||
| return errorType.Elem().Name() | ||
| } else { | ||
| return errorType.Name() | ||
| } | ||
| } | ||
| func systemLog(msg string) { | ||
| fmt.Fprintln(os.Stderr, "\033[32m"+msg+"\033[0m") | ||
| } | ||
| // Try to match the output of the Lambda web console | ||
| func systemErr(err *messages.InvokeResponse_Error) { | ||
| jsonBytes, _ := json.MarshalIndent(LambdaError{ | ||
| Message: err.Message, | ||
| Type: err.Type, | ||
| StackTrace: err.StackTrace, | ||
| }, "", " ") | ||
| fmt.Fprintln(os.Stderr, "\033[31m"+string(jsonBytes)+"\033[0m") | ||
| } | ||
| type LambdaError struct { | ||
| Message string `json:"errorMessage"` | ||
| Type string `json:"errorType,omitempty"` | ||
| StackTrace []*messages.InvokeResponse_Error_StackFrame `json:"stackTrace,omitempty"` | ||
| } | ||
| type MockLambdaContext struct { | ||
| RequestId string | ||
| EventBody string | ||
| FnName string | ||
| Version string | ||
| MemSize string | ||
| Timeout string | ||
| Region string | ||
| AccountId string | ||
| Start time.Time | ||
| TimeoutDuration time.Duration | ||
| Pid int | ||
| Reply *messages.InvokeResponse | ||
| } | ||
| func (mc *MockLambdaContext) ParseTimeout() { | ||
| timeoutDuration, err := time.ParseDuration(mc.Timeout + "s") | ||
| if err != nil { | ||
| panic(err) | ||
| } | ||
| mc.TimeoutDuration = timeoutDuration | ||
| } | ||
| func (mc *MockLambdaContext) Deadline() time.Time { | ||
| return mc.Start.Add(mc.TimeoutDuration) | ||
| } | ||
| func (mc *MockLambdaContext) HasExpired() bool { | ||
| return time.Now().After(mc.Deadline()) | ||
| } | ||
| func (mc *MockLambdaContext) Request() *messages.InvokeRequest { | ||
| return &messages.InvokeRequest{ | ||
| Payload: []byte(mc.EventBody), | ||
| RequestId: mc.RequestId, | ||
| XAmznTraceId: getEnv("_X_AMZN_TRACE_ID", ""), | ||
| InvokedFunctionArn: arn(mc.Region, mc.AccountId, mc.FnName), | ||
| Deadline: messages.InvokeRequest_Timestamp{ | ||
| Seconds: mc.Deadline().Unix(), | ||
| Nanos: int64(mc.Deadline().Nanosecond()), | ||
| }, | ||
| } | ||
| } | ||
| func (mc *MockLambdaContext) TimeoutErr() error { | ||
| return fmt.Errorf("%s %s Task timed out after %s.00 seconds", time.Now().Format("2006-01-02T15:04:05.999Z"), | ||
| mc.RequestId, mc.Timeout) | ||
| } |
| FROM golang:1 | ||
| WORKDIR /go/src/github.com/lambci/docker-lambda | ||
| RUN curl -sSL -o /usr/local/bin/dep https://github.com/golang/dep/releases/download/v0.3.2/dep-linux-amd64 && chmod +x /usr/local/bin/dep | ||
| COPY aws-lambda-mock.go Gopkg.toml Gopkg.lock ./ | ||
| RUN dep ensure | ||
| RUN GOARCH=amd64 GOOS=linux go build aws-lambda-mock.go | ||
| FROM lambci/lambda-base | ||
| RUN rm -rf /var/runtime /var/lang && \ | ||
| curl https://lambci.s3.amazonaws.com/fs/go1.x.tgz | tar -zx -C / | ||
| COPY --from=0 /go/src/github.com/lambci/docker-lambda/aws-lambda-mock /var/runtime/aws-lambda-go | ||
| USER sbx_user1051 | ||
| ENTRYPOINT ["/var/runtime/aws-lambda-go"] |
| # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. | ||
| [[projects]] | ||
| name = "github.com/aws/aws-lambda-go" | ||
| packages = ["lambda/messages"] | ||
| revision = "6e2e37798efbb1dfd8e9c6681702e683a6046517" | ||
| version = "v1.0.1" | ||
| [solve-meta] | ||
| analyzer-name = "dep" | ||
| analyzer-version = 1 | ||
| inputs-digest = "2c19b35c52e708394e6cc46c177e57a8379c3ab6e44aab67e2f9074218f861e9" | ||
| solver-name = "gps-cdcl" | ||
| solver-version = 1 |
| [[constraint]] | ||
| name = "github.com/aws/aws-lambda-go" | ||
| version = "1.0.1" |
| FROM lambci/lambda-base:build | ||
| ENV AWS_EXECUTION_ENV=AWS_Lambda_java8 | ||
| WORKDIR / | ||
| RUN rm -rf /var/runtime /var/lang && \ | ||
| curl https://lambci.s3.amazonaws.com/fs/java8.tgz | tar -zx -C / && \ | ||
| yum install -y java-1.8.0-openjdk-devel |
| FROM openjdk:8-alpine | ||
| WORKDIR /src | ||
| COPY ./lambda-runtime-mock /src | ||
| RUN apk add --no-cache curl && ./build.sh | ||
| FROM lambci/lambda-base | ||
| ENV AWS_EXECUTION_ENV=AWS_Lambda_java8 | ||
| RUN rm -rf /var/runtime /var/lang && \ | ||
| curl https://lambci.s3.amazonaws.com/fs/java8.tgz | tar -zx -C / | ||
| COPY --from=0 /src/LambdaSandboxJava-1.0.jar /var/runtime/lib/ | ||
| WORKDIR / | ||
| USER sbx_user1051 | ||
| ENTRYPOINT ["/usr/bin/java", "-XX:MaxHeapSize=1336935k", "-XX:MaxMetaspaceSize=157286k", "-XX:ReservedCodeCacheSize=78643k", \ | ||
| "-XX:+UseSerialGC", "-Xshare:on", "-XX:-TieredCompilation", "-jar", "/var/runtime/lib/LambdaJavaRTEntry-1.0.jar"] |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
| #!/bin/sh | ||
| cd $(dirname "$0") | ||
| mkdir -p ./target/classes | ||
| javac -target 1.8 -d ./target/classes ./src/main/java/lambdainternal/LambdaRuntime.java | ||
| curl -s https://lambci.s3.amazonaws.com/fs/java8.tgz | tar -zx -- var/runtime/lib/LambdaSandboxJava-1.0.jar | ||
| mv var/runtime/lib/LambdaSandboxJava-1.0.jar ./ | ||
| cp -R ./target/classes/lambdainternal ./ | ||
| jar uf LambdaSandboxJava-1.0.jar lambdainternal/LambdaRuntime*.class | ||
| rm -rf ./var ./lambdainternal |
| package lambdainternal; | ||
| import java.lang.reflect.Field; | ||
| import java.math.BigInteger; | ||
| import java.nio.charset.StandardCharsets; | ||
| import java.text.SimpleDateFormat; | ||
| import java.util.Collections; | ||
| import java.util.Date; | ||
| import java.util.Map; | ||
| import java.util.Random; | ||
| import java.util.Scanner; | ||
| import java.util.UUID; | ||
| import sun.misc.Unsafe; | ||
| public class LambdaRuntime { | ||
| private static Unsafe unsafe; | ||
| private static final String INVOKE_ID = UUID.randomUUID().toString(); | ||
| private static final String AWS_ACCESS_KEY_ID; | ||
| private static final String AWS_SECRET_ACCESS_KEY; | ||
| private static final String AWS_SESSION_TOKEN; | ||
| private static final String AWS_REGION; | ||
| private static final String HANDLER; | ||
| private static final String EVENT_BODY; | ||
| private static final int TIMEOUT; | ||
| private static final String X_AMZN_TRACE_ID; | ||
| private static final String CLIENT_CONTEXT = null; | ||
| private static final String COGNITO_IDENTITY_ID = ""; | ||
| private static final String COGNITO_IDENTITY_POOL_ID = ""; | ||
| private static final String TRACE_ID = ""; | ||
| private static final String PARENT_ID = ""; | ||
| private static final String FUNCTION_ARN; | ||
| private static final String ACCOUNT_ID; | ||
| private static boolean alreadyInvoked = false; | ||
| private static long invokeStart; | ||
| public static final int MEMORY_LIMIT; | ||
| public static final String LOG_GROUP_NAME; | ||
| public static final String LOG_STREAM_NAME; | ||
| public static final String FUNCTION_NAME; | ||
| public static final String FUNCTION_VERSION; | ||
| public static volatile boolean needsDebugLogs; | ||
| static { | ||
| try { | ||
| Field field = Unsafe.class.getDeclaredField("theUnsafe"); | ||
| field.setAccessible(true); | ||
| unsafe = (Unsafe) field.get(null); | ||
| } catch (Exception e) { | ||
| throw new RuntimeException(e); | ||
| } | ||
| TIMEOUT = Integer.parseInt(getEnvOrDefault("AWS_LAMBDA_FUNCTION_TIMEOUT", "300")); | ||
| MEMORY_LIMIT = Integer.parseInt(getEnvOrDefault("AWS_LAMBDA_FUNCTION_MEMORY_SIZE", "1536")); | ||
| FUNCTION_NAME = getEnvOrDefault("AWS_LAMBDA_FUNCTION_NAME", "test"); | ||
| FUNCTION_VERSION = getEnvOrDefault("AWS_LAMBDA_FUNCTION_VERSION", "$LATEST"); | ||
| LOG_GROUP_NAME = getEnvOrDefault("AWS_LAMBDA_LOG_GROUP_NAME", "/aws/lambda/" + FUNCTION_NAME); | ||
| LOG_STREAM_NAME = getEnvOrDefault("AWS_LAMBDA_LOG_STREAM_NAME", randomLogStreamName(FUNCTION_VERSION)); | ||
| AWS_ACCESS_KEY_ID = getEnvOrDefault("AWS_ACCESS_KEY_ID", "SOME_ACCESS_KEY_ID"); | ||
| AWS_SECRET_ACCESS_KEY = getEnvOrDefault("AWS_SECRET_ACCESS_KEY", "SOME_SECRET_ACCESS_KEY"); | ||
| AWS_SESSION_TOKEN = getEnv("AWS_SESSION_TOKEN"); | ||
| AWS_REGION = getEnvOrDefault("AWS_REGION", getEnvOrDefault("AWS_DEFAULT_REGION", "us-east-1")); | ||
| ACCOUNT_ID = getEnvOrDefault("AWS_ACCOUNT_ID", "000000000000"); | ||
| FUNCTION_ARN = "arn:aws:lambda:" + AWS_REGION + ":" + ACCOUNT_ID + ":function:" + FUNCTION_NAME; | ||
| X_AMZN_TRACE_ID = getEnvOrDefault("_X_AMZN_TRACE_ID", ""); | ||
| String[] args = getCmdLineArgs(); | ||
| HANDLER = args.length > 1 ? args[1] : getEnvOrDefault("AWS_LAMBDA_FUNCTION_HANDLER", getEnvOrDefault("_HANDLER", "index.Handler")); | ||
| EVENT_BODY = args.length > 2 ? args[2] : getEventBody(); | ||
| LambdaRuntime.needsDebugLogs = false; | ||
| setenv("AWS_LAMBDA_FUNCTION_NAME", FUNCTION_NAME, 1); | ||
| setenv("AWS_LAMBDA_FUNCTION_VERSION", FUNCTION_VERSION, 1); | ||
| setenv("AWS_LAMBDA_FUNCTION_MEMORY_SIZE", Integer.toString(MEMORY_LIMIT), 1); | ||
| setenv("AWS_LAMBDA_LOG_GROUP_NAME", LOG_GROUP_NAME, 1); | ||
| setenv("AWS_LAMBDA_LOG_STREAM_NAME", LOG_STREAM_NAME, 1); | ||
| setenv("AWS_REGION", AWS_REGION, 1); | ||
| setenv("AWS_DEFAULT_REGION", AWS_REGION, 1); | ||
| setenv("_HANDLER", HANDLER, 1); | ||
| } | ||
| private static String getEventBody() { | ||
| String eventBody = getEnv("AWS_LAMBDA_EVENT_BODY"); | ||
| if (eventBody == null) { | ||
| eventBody = getEnv("DOCKER_LAMBDA_USE_STDIN") != null ? | ||
| new Scanner(System.in).useDelimiter("\\A").next() : "{}"; | ||
| } | ||
| return eventBody; | ||
| } | ||
| private static String getEnvOrDefault(String key, String defaultVal) { | ||
| String envVal = getEnv(key); | ||
| return envVal != null ? envVal : defaultVal; | ||
| } | ||
| private static String randomLogStreamName(String functionVersion) { | ||
| byte[] randomBuf = new byte[16]; | ||
| new Random().nextBytes(randomBuf); | ||
| return String.format("%s/[%s]%016x", new SimpleDateFormat("yyyy/MM/dd").format(new Date()), functionVersion, | ||
| new BigInteger(1, randomBuf)); | ||
| } | ||
| private static void systemLog(String str) { | ||
| System.err.println("\033[32m" + str + "\033[0m"); | ||
| } | ||
| private static void systemErr(String str) { | ||
| System.err.println("\033[31m" + str + "\033[0m"); | ||
| } | ||
| public static String getEnv(final String envVariableName) { | ||
| return System.getenv(envVariableName); | ||
| } | ||
| public static void initRuntime() { | ||
| } | ||
| public static void reportRunning(final String p0) { | ||
| } | ||
| public static void reportDone(final String invokeid, final byte[] result, final int resultLength, final int p3) { | ||
| if (!alreadyInvoked) { | ||
| return; | ||
| } | ||
| double durationMs = (System.nanoTime() - invokeStart) / 1_000_000d; | ||
| long billedMs = Math.min(100 * ((long) Math.floor(durationMs / 100) + 1), TIMEOUT * 1000); | ||
| long maxMemory = Math.round((Runtime.getRuntime().totalMemory() - | ||
| Runtime.getRuntime().freeMemory()) / (1024 * 1024)); | ||
| systemLog("END RequestId: " + invokeid); | ||
| systemLog(String.join("\t", | ||
| "REPORT RequestId: " + invokeid, | ||
| "Duration: " + String.format("%.2f", durationMs) + " ms", | ||
| "Billed Duration: " + billedMs + " ms", | ||
| "Memory Size: " + MEMORY_LIMIT + " MB", | ||
| "Max Memory Used: " + maxMemory + " MB", | ||
| "")); | ||
| if (result != null) { | ||
| System.out.println("\n" + new String(result, 0, resultLength)); | ||
| } | ||
| } | ||
| public static void reportException(final String p0) { | ||
| } | ||
| public static void reportUserInitStart() { | ||
| } | ||
| public static void reportUserInitEnd() { | ||
| } | ||
| public static void reportUserInvokeStart() { | ||
| } | ||
| public static void reportUserInvokeEnd() { | ||
| } | ||
| public static void reportFault(final String invokeid, final String msg, final String exceptionClass, | ||
| final String stack) { | ||
| systemErr(stack); | ||
| } | ||
| public static void setenv(final String key, final String val, final int p2) { | ||
| getMutableEnv().put(key, val); | ||
| } | ||
| public static void unsetenv(final String key) { | ||
| getMutableEnv().remove(key); | ||
| } | ||
| public static WaitForStartResult waitForStart() { | ||
| return new WaitForStartResult(INVOKE_ID, HANDLER, "event", AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, | ||
| AWS_SESSION_TOKEN, false); | ||
| } | ||
| public static InvokeRequest waitForInvoke() { | ||
| if (alreadyInvoked) { | ||
| System.exit(0); | ||
| } | ||
| alreadyInvoked = true; | ||
| long address = 0; | ||
| byte[] eventBodyBytes = EVENT_BODY.getBytes(StandardCharsets.UTF_8); | ||
| try { | ||
| address = unsafe.allocateMemory(eventBodyBytes.length); | ||
| for (int i = 0; i < eventBodyBytes.length; i++) { | ||
| unsafe.putByte(address + i, eventBodyBytes[i]); | ||
| } | ||
| } catch (Exception e) { | ||
| // Not sure, could happen if memory is exhausted? | ||
| throw new RuntimeException(e); | ||
| } | ||
| invokeStart = System.nanoTime(); | ||
| systemLog("START RequestId: " + INVOKE_ID + " Version: " + FUNCTION_VERSION); | ||
| return new InvokeRequest(-1, INVOKE_ID, X_AMZN_TRACE_ID, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, | ||
| AWS_SESSION_TOKEN, CLIENT_CONTEXT, COGNITO_IDENTITY_ID, COGNITO_IDENTITY_POOL_ID, address, | ||
| eventBodyBytes.length, false, FUNCTION_ARN, TRACE_ID, false, PARENT_ID); | ||
| } | ||
| public static int getRemainingTime() { | ||
| return (int) ((TIMEOUT * 1000) - Math.round((System.nanoTime() - invokeStart) / 1_000_000d)); | ||
| } | ||
| public static void sendContextLogs(final byte[] msg, final int length) { | ||
| System.err.print(new String(msg, 0, length, StandardCharsets.UTF_8)); | ||
| } | ||
| public static synchronized void streamLogsToSlicer(final byte[] p0, final int p1, final int p2) { | ||
| } | ||
| private static String[] getCmdLineArgs() { | ||
| return System.getProperty("sun.java.command").split(" ", 3); | ||
| } | ||
| private static Map<String, String> getMutableEnv() { | ||
| Class[] classes = Collections.class.getDeclaredClasses(); | ||
| Map<String, String> env = System.getenv(); | ||
| for (Class cl : classes) { | ||
| if ("java.util.Collections$UnmodifiableMap".equals(cl.getName())) { | ||
| try { | ||
| Field field = cl.getDeclaredField("m"); | ||
| field.setAccessible(true); | ||
| Object obj = field.get(env); | ||
| return (Map<String, String>) obj; | ||
| } catch (Exception e) { | ||
| // Should never happen on Lambda | ||
| throw new RuntimeException(e); | ||
| } | ||
| } | ||
| } | ||
| // Should never happen on Lambda | ||
| throw new RuntimeException("Could not find java.util.Collections$UnmodifiableMap class"); | ||
| } | ||
| public static class AWSCredentials { | ||
| public final String key; | ||
| public final String secret; | ||
| public final String session; | ||
| public AWSCredentials(final String key, final String secret, final String session) { | ||
| this.key = key; | ||
| this.secret = secret; | ||
| this.session = session; | ||
| } | ||
| } | ||
| public static class InvokeRequest { | ||
| public final int sockfd; | ||
| public final String invokeid; | ||
| public final String xAmznTraceId; | ||
| public final AWSCredentials credentials; | ||
| public final String clientContext; | ||
| public final String cognitoIdentityId; | ||
| public final String cognitoPoolId; | ||
| public final long eventBodyAddr; | ||
| public final int eventBodyLen; | ||
| public final boolean needsDebugLogs; | ||
| public final String invokedFunctionArn; | ||
| public final String traceId; | ||
| public final boolean isSampled; | ||
| public final String parentId; | ||
| public InvokeRequest(final int sockfd, final String invokeid, final String xAmznTraceId, final String awskey, | ||
| final String awssecret, final String awssession, final String clientcontext, | ||
| final String cognitoidentityid, final String cognitopoolid, final long addr, final int len, | ||
| final boolean needsDebugLogs, final String invokedFunctionArn, final String traceId, | ||
| final boolean isSampled, final String parentId) { | ||
| this.sockfd = sockfd; | ||
| this.invokeid = invokeid; | ||
| this.xAmznTraceId = xAmznTraceId; | ||
| this.eventBodyAddr = addr; | ||
| this.eventBodyLen = len; | ||
| this.clientContext = clientcontext; | ||
| this.cognitoIdentityId = cognitoidentityid; | ||
| this.cognitoPoolId = cognitopoolid; | ||
| this.traceId = traceId; | ||
| this.isSampled = isSampled; | ||
| this.parentId = parentId; | ||
| this.credentials = new AWSCredentials(awskey, awssecret, awssession); | ||
| this.needsDebugLogs = needsDebugLogs; | ||
| this.invokedFunctionArn = invokedFunctionArn; | ||
| } | ||
| } | ||
| public static class WaitForStartResult { | ||
| public final String invokeid; | ||
| public final String handler; | ||
| public final String mode; | ||
| public final AWSCredentials credentials; | ||
| public final boolean suppressInit; | ||
| public WaitForStartResult(final String invokeid, final String handler, final String mode, final String awskey, | ||
| final String awssecret, final String awssession, final boolean suppressInit) { | ||
| this.invokeid = invokeid; | ||
| this.handler = handler; | ||
| this.mode = mode; | ||
| this.credentials = new AWSCredentials(awskey, awssecret, awssession); | ||
| this.suppressInit = suppressInit; | ||
| } | ||
| } | ||
| } |
| # Java 8 Build Instructions | ||
| As the Java 8 Lambda libraries are statically compiled into jars, it's not | ||
| possible to just swap in a mock interface source file, as it is in the other | ||
| dynamic runtimes, without compiling first. | ||
| The `Dockerfile` here will build using a patched `LambdaSandboxJava-1.0.jar`, | ||
| which is checked into git. This jar was built using the jar from the Lambda runtime, | ||
| `/var/runtime/lib/LambdaSandboxJava-1.0.jar`, with a single class | ||
| (`lambdainternal/LambdaRuntime.class`) updated to use local/mock methods | ||
| instead of native ones. | ||
| The build script to perform this patch/update is at | ||
| [./lambda-runtime-mock/build.sh](./lambda-runtime-mock/build.sh) |
+1
-0
@@ -16,2 +16,3 @@ var spawnSync = require('child_process').spawnSync | ||
| 'AWS_LAMBDA_EVENT_BODY', | ||
| 'DOCKER_LAMBDA_USE_STDIN', | ||
| ] | ||
@@ -18,0 +19,0 @@ var ENV_ARGS = [].concat.apply([], ENV_VARS.map(function(x) { return ['-e', x] })) |
+1
-1
| { | ||
| "name": "docker-lambda", | ||
| "version": "0.13.4", | ||
| "version": "0.15.0", | ||
| "description": "A Docker image and test runner that (very closely) mimics the live AWS Lambda environment", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
+61
-29
@@ -18,3 +18,3 @@ docker-lambda | ||
| This project consists of a set of Docker images for each of the supported Lambda runtimes | ||
| (Node.js 0.10, 4.3 and 6.10, Python 2.7 and 3.6, and Java 8\*). | ||
| (Node.js 0.10, 4.3 and 6.10, Python 2.7 and 3.6, Java 8, .NET Core 2.0, and Go 1.x). | ||
@@ -26,5 +26,2 @@ There are also a set of build images that include packages like gcc-c++, git, | ||
| \* NB: The Java 8 test runner is not yet complete, but the | ||
| language is installed in the images so can be manually tested | ||
| Prerequisites | ||
@@ -43,20 +40,43 @@ ------------- | ||
| # Test an index.handler function from the current directory on Node.js v4.3 | ||
| docker run -v "$PWD":/var/task lambci/lambda | ||
| docker run --rm -v "$PWD":/var/task lambci/lambda | ||
| # If using a function other than index.handler, with a custom event | ||
| docker run -v "$PWD":/var/task lambci/lambda index.myHandler '{"some": "event"}' | ||
| docker run --rm -v "$PWD":/var/task lambci/lambda index.myHandler '{"some": "event"}' | ||
| # Use the original Node.js v0.10 runtime | ||
| docker run -v "$PWD":/var/task lambci/lambda:nodejs | ||
| docker run --rm -v "$PWD":/var/task lambci/lambda:nodejs | ||
| # Use the Node.js v6.10 runtime | ||
| docker run -v "$PWD":/var/task lambci/lambda:nodejs6.10 | ||
| docker run --rm -v "$PWD":/var/task lambci/lambda:nodejs6.10 | ||
| # Test a lambda_function.lambda_handler function from the current directory on Python2.7 | ||
| docker run -v "$PWD":/var/task lambci/lambda:python2.7 | ||
| # Test a default function (lambda_function.lambda_handler) from the current directory on Python 2.7 | ||
| docker run --rm -v "$PWD":/var/task lambci/lambda:python2.7 | ||
| # Test on Python 3.6 with a custom file named my_module.py containing a my_handler function | ||
| docker run --rm -v "$PWD":/var/task lambci/lambda:python3.6 my_module.my_handler | ||
| # Test on Go 1.x with a compiled handler named my_handler and a custom event | ||
| docker run --rm -v "$PWD":/var/task lambci/lambda:go1.x my_handler '{"some": "event"}' | ||
| # Test a function from the current directory on Java 8 | ||
| # The directory must be laid out in the same way the Lambda zip file is, | ||
| # with top-level package source directories and a `lib` directory for third-party jars | ||
| # http://docs.aws.amazon.com/lambda/latest/dg/create-deployment-pkg-zip-java.html | ||
| # The default handler is "index.Handler", but you'll likely have your own package and class | ||
| docker run --rm -v "$PWD":/var/task lambci/lambda:java8 org.myorg.MyHandler | ||
| # Test on .NET Core 2.0 given a test.dll assembly in the current directory, | ||
| # a class named Function with a FunctionHandler method, and a custom event | ||
| docker run --rm -v "$PWD":/var/task lambci/lambda:dotnetcore2.0 test::test.Function::FunctionHandler '{"some": "event"}' | ||
| # Run custom commands on the default container | ||
| docker run --entrypoint node lambci/lambda -v | ||
| docker run --rm --entrypoint node lambci/lambda -v | ||
| # For large events you can pipe them into stdin if you set DOCKER_LAMBDA_USE_STDIN (on any runtime) | ||
| echo '{"some": "event"}' | docker run --rm -v "$PWD":/var/task -i -e DOCKER_LAMBDA_USE_STDIN=1 lambci/lambda | ||
| ``` | ||
| You can see more examples of how to build docker images and run different | ||
| runtimes in the [examples](./examples) directory. | ||
| To use the build images, for compilation, deployment, etc: | ||
@@ -66,12 +86,19 @@ | ||
| # To compile native deps in node_modules (runs `npm rebuild`) | ||
| docker run -v "$PWD":/var/task lambci/lambda:build | ||
| docker run --rm -v "$PWD":/var/task lambci/lambda:build | ||
| # To use a different runtime from the default Node.js v4.3 | ||
| docker run -v "$PWD":/var/task lambci/lambda:build-nodejs6.10 | ||
| docker run --rm -v "$PWD":/var/task lambci/lambda:build-nodejs6.10 | ||
| # Run custom commands on the build container | ||
| docker run lambci/lambda:build java -version | ||
| # To resolve dependencies on go1.x (working directory is /go/src/handler, will run `dep ensure`) | ||
| docker run --rm -v "$PWD":/go/src/handler lambci/lambda:build-go1.x | ||
| # To run an interactive session on the build container | ||
| docker run -it lambci/lambda:build bash | ||
| # For .NET Core 2.0, this will publish the compiled code to `./pub`, | ||
| # which you can then use to run with `-v "$PWD"/pub:/var/task` | ||
| docker run --rm -v "$PWD":/var/task lambci/lambda:build-dotnetcore2.0 dotnet publish -c Release -o pub | ||
| # Run custom commands on a build container | ||
| docker run --rm lambci/lambda:build aws --version | ||
| # To run an interactive session on a build container | ||
| docker run -it lambci/lambda:build-python3.6 bash | ||
| ``` | ||
@@ -101,3 +128,3 @@ | ||
| ADD . . | ||
| COPY . . | ||
@@ -110,3 +137,3 @@ RUN npm install | ||
| # docker build -t mylambda . | ||
| # docker run -e AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY mylambda | ||
| # docker run --rm -e AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY mylambda | ||
| ``` | ||
@@ -131,10 +158,10 @@ | ||
| By tarring the full filesystem in Lambda, uploading that to S3, and then | ||
| piping into Docker to create a new image from scratch – then creating | ||
| mock modules that will be required/included in place of the actual native | ||
| modules that communicate with the real Lambda coordinating services. Only the | ||
| native modules are mocked out – the actual parent JS/PY runner files are left | ||
| alone, so their behaviors don't need to be replicated (like the | ||
| overriding of `console.log`, and custom defined properties like | ||
| `callbackWaitsForEmptyEventLoop`) | ||
| By [tarring the full filesystem in Lambda, uploading that to S3](./base/dump-nodejs43.js), | ||
| and then [piping into Docker to create a new image from scratch](./base/create-base.sh) – | ||
| then [creating mock modules](./nodejs4.3/run/awslambda-mock.js) that will be | ||
| required/included in place of the actual native modules that communicate with | ||
| the real Lambda coordinating services. Only the native modules are mocked | ||
| out – the actual parent JS/PY/Java runner files are left alone, so their behaviors | ||
| don't need to be replicated (like the overriding of `console.log`, and custom | ||
| defined properties like `callbackWaitsForEmptyEventLoop`) | ||
@@ -162,4 +189,2 @@ * *What's missing from the images?* | ||
| TODO | ||
| Docker tags (follow the Lambda runtime names): | ||
@@ -171,2 +196,5 @@ - `latest` / `nodejs4.3` | ||
| - `python3.6` | ||
| - `java8` | ||
| - `go1.x` | ||
| - `dotnetcore2.0` | ||
| - `build` / `build-nodejs4.3` | ||
@@ -177,2 +205,5 @@ - `build-nodejs` | ||
| - `build-python3.6` | ||
| - `build-java8` | ||
| - `build-go1.x` | ||
| - `build-dotnetcore2.0` | ||
@@ -192,2 +223,3 @@ Env vars: | ||
| - `AWS_SESSION_TOKEN` | ||
| - `DOCKER_LAMBDA_USE_STDIN` | ||
@@ -194,0 +226,0 @@ Options to pass to `dockerLambda()`: |
+5
-0
@@ -84,2 +84,7 @@ var should = require('should') | ||
| // Should not fail if stdout contains extra newlines | ||
| resetMock({status: 0, stdout: 'Test\nResult\n\n{"success":true}\n\n'}) | ||
| result = dockerLambda() | ||
| result.should.eql({success: true}) | ||
| // Should return undefined if last stdout entry cannot be parsed | ||
@@ -86,0 +91,0 @@ resetMock({status: 0, stdout: 'Test\nResult\nsuccess'}) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
349330
1222.22%32
190.91%3072
1739.52%245
15.02%3
50%