§ Documentation
Serverless
In-memory functions-as-a-service modelled on Lambda, Azure Functions, and Cloud Functions, driven with the real cloud SDKs
aws Lambdaazr Functionsgcp Cloud Functions
Emulates functions-as-a-service — the Lambda/Azure Functions/Cloud Functions control plane where you create a function and invoke it with a payload. Instead of deploying a zip, you register a plain Go function as the handler; an Invoke then runs it in-process and returns its response bytes. That keeps the create/invoke path fast and deterministic while still exercising your production wiring.
Reach for it when your code creates a function, invokes it synchronously, and reads the response — for example verifying an orchestration that fans out to Lambdas, or a caller that depends on a function's output shape — without deploying to a real cloud. You get the function's response back exactly as the SDK would deliver it.
| Provider | Service | SDK-compat | Driver |
|---|---|---|---|
| AWS | Lambda (REST + JSON) | ✓ Live | aws.Lambda |
| Azure | Functions (Function Apps + /api/{name} invoke) | ✓ Live | azure.Functions |
| GCP | Cloud Functions v1 (Create LRO + :call) | ✓ Live | gcp.CloudFunctions |
Drive it with the real SDK#
Run cloudemu as a server and point the real SDK at it. The stock binary serves the in-memory default with cloudemu serve (or docker run --rm -p 4566:4566 ghcr.io/stackshy/cloudemu) on http://localhost:4566:
cloudemu serve # AWS API on http://localhost:4566Because there's no real runtime, you give Invoke a Go handler to execute by embedding the provider in your own server and registering the handler on its Lambda driver. Then the stock Lambda client creates and calls the function against the endpoint, unchanged:
import (
"net/http"
"github.com/aws/aws-sdk-go-v2/service/lambda"
lambdatypes "github.com/aws/aws-sdk-go-v2/service/lambda/types"
"github.com/stackshy/cloudemu/v2"
awsserver "github.com/stackshy/cloudemu/v2/server/aws"
)
cloud := cloudemu.NewAWS()
// Register a Go handler so Invoke has something to run.
cloud.Lambda.RegisterHandler("my-handler", func(ctx context.Context, payload []byte) ([]byte, error) {
return []byte(`{"status":"ok"}`), nil
})
// Serve that provider on :4566.
go http.ListenAndServe(":4566", awsserver.NewFromProvider(cloud))
client := lambda.NewFromConfig(cfg, func(o *lambda.Options) {
o.BaseEndpoint = aws.String("http://localhost:4566")
})
client.CreateFunction(ctx, &lambda.CreateFunctionInput{
FunctionName: aws.String("my-handler"),
Runtime: lambdatypes.RuntimeGo1x,
Role: aws.String("arn:aws:iam::000000000000:role/test"),
Handler: aws.String("main"),
Code: &lambdatypes.FunctionCode{ZipFile: []byte("z")},
})
resp, _ := client.Invoke(ctx, &lambda.InvokeInput{
FunctionName: aws.String("my-handler"),
Payload: []byte(`{"key":"value"}`),
})
fmt.Println(string(resp.Payload)) // {"status":"ok"}Same shape with armappservice (Azure) and google.golang.org/api/cloudfunctions/v1 (GCP) — see SDK-Compat Server.
In-process (Go unit tests)#
For Go unit tests written inside cloudemu-aware code, stand the same wire server up in-process with httptest.NewServer and point the client at ts.URL. The Drivers you pass name exactly the drivers this service needs, and the same provider handle registers the Go handler:
import (
"github.com/stackshy/cloudemu/v2"
awsserver "github.com/stackshy/cloudemu/v2/server/aws"
)
cloud := cloudemu.NewAWS()
cloud.Lambda.RegisterHandler("my-handler", handler)
ts := httptest.NewServer(awsserver.New(awsserver.Drivers{Lambda: cloud.Lambda}))
defer ts.Close()
// point the same lambda client at ts.URL instead of the running endpointCall the driver directly#
For in-process setup you can skip the HTTP hop. The flow is the same three steps — create, register a handler, invoke — with the driver's own input structs:
import sdriver "github.com/stackshy/cloudemu/v2/services/serverless/driver"
aws.Lambda.CreateFunction(ctx, sdriver.FunctionConfig{
Name: "my-handler", Runtime: "go1.x", Handler: "main",
})
aws.Lambda.RegisterHandler("my-handler", func(_ context.Context, p []byte) ([]byte, error) {
return []byte(`{"ok":true}`), nil
})
out, _ := aws.Lambda.Invoke(ctx, sdriver.InvokeInput{
FunctionName: "my-handler", Payload: []byte(`{}`),
})Invoke returns an InvokeOutput carrying the status code, response payload, and any handler error string. UpdateFunction(ctx, name, config) patches an existing function's configuration in place and returns the updated FunctionInfo.
Behavior & fidelity#
| Behavior | What happens |
|---|---|
| Invoke runs your handler | A function with no handler registered via RegisterHandler has nothing to execute; the handler's returned bytes come straight back as the invocation payload. |
| Handler errors are invocation errors | An error from the handler is reported on the invocation result, not as a transport failure — the same way the real services separate a function error from a call failure. |
| GCP create/delete are long-running | Cloud Functions create and delete return an LRO you poll via Operations.Get, mirroring the real v1 API; :call is the synchronous invoke. |
| Portable-API-only extras | Layers and reserved/provisioned concurrency are not yet on the SDK-compat layer — reach for them through the Portable Go API. |
SDK-compat — Live#
Real lambda, armappservice, and cloudfunctions/v1 clients drive the emulator end-to-end:
| Provider | Coverage |
|---|---|
| AWS Lambda | Function CRUD, versions, aliases, event source mappings, resource policy, tagging, sync invoke |
| Azure Functions | Function-app CRUD plus HTTP invoke |
| GCP Cloud Functions | Create/Delete LROs, get, list, synchronous call |
See SDK-Compat for the full per-operation list.