Skip to content
cloudemu
Services

§ 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.

ProviderServiceSDK-compatDriver
AWSLambda (REST + JSON)✓ Liveaws.Lambda
AzureFunctions (Function Apps + /api/{name} invoke)✓ Liveazure.Functions
GCPCloud Functions v1 (Create LRO + :call)✓ Livegcp.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:4566

Because 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 endpoint

Call 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#

BehaviorWhat happens
Invoke runs your handlerA 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 errorsAn 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-runningCloud 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 extrasLayers 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:

ProviderCoverage
AWS LambdaFunction CRUD, versions, aliases, event source mappings, resource policy, tagging, sync invoke
Azure FunctionsFunction-app CRUD plus HTTP invoke
GCP Cloud FunctionsCreate/Delete LROs, get, list, synchronous call

See SDK-Compat for the full per-operation list.

On this page

On this page