Skip to content

Commit 6baa2ed

Browse files
committed
initial commit
1 parent f8ba29b commit 6baa2ed

File tree

5 files changed

+207
-2
lines changed

5 files changed

+207
-2
lines changed

.gitignore

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
2+
# Binaries for programs and plugins
3+
*.exe
4+
*.exe~
5+
*.dll
6+
*.so
7+
*.dylib
8+
bin
9+
10+
# Test binary, built with `go test -c`
11+
*.test
12+
13+
# Output of the go coverage tool, specifically when used with LiteIDE
14+
*.out
15+
16+
# IDE
17+
.vscode
18+
.vscode/*
19+
.history/*
20+
*.code-workspace
21+
22+
# Raw .json
23+
*.json

README.md

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,67 @@
1-
# aws-lambda-rpc-client
2-
A simple RPC client that can be used to call AWS lambda functions written in Go to test them.
1+
# AWS Lambda RPC Client
2+
A simple RPC client that can be used to debug AWS lambda functions locally that are written in Go. This is made possible by the fact that the AWS SDK for Golang implements lambda functions as RPC servers, making them very easy to invoke in a multitude of different environments.
3+
4+
### Compile From Source (default build with flag for removing the debug symbols from the binary)
5+
6+
```powershell
7+
go build -ldflags=-w -o "lrpc.exe"
8+
```
9+
10+
### Prerequisites
11+
12+
* Must have a lambda to test against
13+
* Must have the `_LAMBDA_SERVER_PORT` environment variable set to a value of `9988` in either your local configuration or in your lambda source code before `lambda.Start(handler)` is invoked (i.e. `os.Setenv("_LAMBDA_SERVER_PORT", "9988")`).
14+
15+
### Usage
16+
17+
First, you'll need to run your lambda function by invoking the `lambda.Start(handler)` method (i.e. `go run <module-name>`), this will start the RPC server for that particular lambda function and it will begin to run as a server.
18+
19+
### Example Output (Terminal)
20+
21+
```
22+
./lrpc.exe -e "./events/apigateway-authorizer.json" -o "resp.json"
23+
LAMBDA RESPONSE:
24+
{
25+
"policyDocument": {
26+
"Statement": [
27+
{
28+
"Action": [
29+
"execute-api:Invoke"
30+
],
31+
"Effect": "Deny",
32+
"Resource": [
33+
"*"
34+
]
35+
}
36+
],
37+
"Version": "2012-10-17"
38+
},
39+
"principalId": "apigateway.amazonaws.com"
40+
}
41+
```
42+
43+
Event used in the above example (stored locally in `"./events/apigateway-authorizer.json"`)
44+
```
45+
{
46+
"type": "TOKEN",
47+
"authorizationToken": "incoming-client-token",
48+
"methodArn": "arn:aws:execute-api:us-east-1:123456789012:example/prod/POST/{proxy+}"
49+
}
50+
```
51+
52+
### AWS SDK References
53+
54+
- [_LAMBDA_SERVER_PORT Environment Variable](https://github.com/aws/aws-lambda-go/blob/bc1ec47cb1670c0d5eaca47c10d89789d8507c3d/lambda/rpc.go#L16)
55+
- [InvokeRequest/InvokeResponse](https://github.com/aws/aws-lambda-go/blob/bc1ec47cb1670c0d5eaca47c10d89789d8507c3d/lambda/messages/messages.go#L20)
56+
57+
### Help
58+
59+
The following arguments can be passed to the CLI to invoke the help documentation:
60+
61+
* -h
62+
63+
* --h
64+
65+
* -help
66+
67+
* --help

go.mod

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module github.com/JS10X/aws-lambda-rpc-client
2+
3+
go 1.17
4+
5+
require github.com/aws/aws-lambda-go v1.39.1

go.sum

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
github.com/aws/aws-lambda-go v1.39.1 h1:UcuX9O3JqhQyP/rxPJEpTUUSehzqkNpwKKRFa9N+ozk=
2+
github.com/aws/aws-lambda-go v1.39.1/go.mod h1:jwFe2KmMsHmffA1X2R09hH6lFzJQxzI8qK17ewzbQMM=
3+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
5+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
6+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
7+
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
8+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
9+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

main.go

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package main
2+
3+
import (
4+
"encoding/base64"
5+
"encoding/json"
6+
"flag"
7+
"fmt"
8+
"io/ioutil"
9+
"log"
10+
"net/rpc"
11+
"os"
12+
"strconv"
13+
"strings"
14+
15+
"github.com/aws/aws-lambda-go/lambda/messages"
16+
)
17+
18+
const (
19+
LAMBDA_SERVER_PORT = "9988"
20+
LAMBDA_INVOKE_PROC_ID = "Function.Invoke"
21+
)
22+
23+
func main() {
24+
25+
var eventfile, outputfile string
26+
flag.StringVar(&eventfile, "e", "", "The AWS event template [request] you want to send to the Lamba (must be .json file).")
27+
flag.StringVar(&outputfile, "o", "", "Print the [response] sent from the AWS Lambda server (local function running).")
28+
flag.Parse()
29+
if len(strings.TrimSpace(eventfile)) <= 0 {
30+
log.Fatalln("You must specify an event [request] to send to the LAMBDA")
31+
}
32+
33+
// Dial up to the function over localhost:9988 (reserved port used for Lambas in the AWS SDK)
34+
client, err := rpc.Dial("tcp", fmt.Sprintf("localhost:%v", LAMBDA_SERVER_PORT))
35+
if err != nil {
36+
log.Fatalln(err)
37+
}
38+
defer client.Close()
39+
40+
// Translate the event file to raw bytes to send in the request payload to the Lambda
41+
openEventFile, err := os.Open(eventfile)
42+
if err != nil {
43+
log.Fatalln(err)
44+
}
45+
defer openEventFile.Close()
46+
eventFileBytes, err := ioutil.ReadAll(openEventFile)
47+
if err != nil {
48+
log.Fatalln(err)
49+
}
50+
51+
// Send the request payload to the Lambda over RPC
52+
lambdaRequest := &messages.InvokeRequest{Payload: eventFileBytes}
53+
var lambdaResponse messages.InvokeResponse
54+
err = client.Call(LAMBDA_INVOKE_PROC_ID, lambdaRequest, &lambdaResponse)
55+
if err != nil {
56+
log.Fatalln(err)
57+
}
58+
if lambdaResponse.Error != nil {
59+
log.Fatalln(lambdaResponse.Error)
60+
}
61+
62+
decodedResponse, err := decodeResponse(lambdaResponse.Payload)
63+
if err != nil {
64+
log.Fatalln(err)
65+
}
66+
printResponse(decodedResponse, outputfile)
67+
}
68+
69+
func printResponse(responseString string, outputfile string) {
70+
if len(strings.TrimSpace(responseString)) <= 0 {
71+
return
72+
}
73+
74+
log.Println("LAMBDA RESPONSE:\n", responseString)
75+
if len(strings.TrimSpace(outputfile)) != 0 {
76+
file, err := os.Create(outputfile)
77+
if err != nil {
78+
log.Printf("Error occurred when creating the output file provided: [%v]\n", err)
79+
} else {
80+
file.WriteString(responseString)
81+
}
82+
file.Close()
83+
}
84+
}
85+
86+
func decodeResponse(input []byte) (string, error) {
87+
88+
marshalledInput, err := json.Marshal(input)
89+
if err != nil {
90+
log.Fatalf("could not marshal the lambda reponse payload [%v]\n", err.Error())
91+
}
92+
93+
unquotedString, err := strconv.Unquote(string(marshalledInput))
94+
if err != nil {
95+
log.Fatalf("could not remove escaped quotes from the lambda reponse [%v]\n", err.Error())
96+
}
97+
98+
decoded, err := base64.StdEncoding.DecodeString(unquotedString)
99+
if err != nil {
100+
log.Fatalf("could not base64 decode the lambda response string [%v]\n", err.Error())
101+
}
102+
return string(decoded), nil
103+
}

0 commit comments

Comments
 (0)