-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Redo the whole thing and make it silly
- Loading branch information
1 parent
6b473d8
commit 8dbd049
Showing
17 changed files
with
597 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
name: CI | ||
on: [push] | ||
jobs: | ||
|
||
build: | ||
name: Build | ||
runs-on: ubuntu-latest | ||
steps: | ||
|
||
- uses: actions/checkout@v1 | ||
|
||
- uses: actions/setup-go@v1 | ||
with: | ||
go-version: 1.13 | ||
|
||
- run: go test -v ./... | ||
env: | ||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} | ||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | ||
AWS_REGION: ap-southeast-2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
name: Release | ||
on: | ||
release: | ||
types: [created] | ||
jobs: | ||
release: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v1 | ||
|
||
- uses: actions/setup-go@v1 | ||
with: | ||
go-version: 1.13 | ||
|
||
- uses: goreleaser/goreleaser-action@v1 | ||
with: | ||
version: latest | ||
args: release | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
builds: | ||
- env: [CGO_ENABLED=0] | ||
goos: [linux] | ||
goarch: [amd64] | ||
archives: | ||
- name_template: lambdahttp | ||
format: zip | ||
files: [none*] # https://github.com/goreleaser/goreleaser/issues/602 | ||
checksum: | ||
name_template: 'checksums.txt' | ||
snapshot: | ||
name_template: "{{ .Tag }}-next" | ||
changelog: | ||
sort: asc | ||
filters: | ||
exclude: | ||
- '^docs:' | ||
- '^test:' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# lambdahttp | ||
|
||
Do you yearn for the days of listening on port 80(80) for web traffic? Does this | ||
new serverless world scare you and make you wonder if you'll have to refactor | ||
your existing code? Or maybe you have a crusty old binary lying around, have | ||
lost the source code and are desperate to make it work on AWS Lambda? | ||
|
||
If so, `lambdahttp` is for **you**. | ||
|
||
## How do I use it? | ||
|
||
* Grab the latest zip from the [Releases][releases] | ||
tab on GitHub. | ||
* Add your app to the zip file. | ||
* Upload the zip to S3. | ||
* Create a Lambda function from that zip file. Configure it to use a "custom runtime" | ||
and specify a shell command as the "handler", e.g. `./myprogram listen --port 8080`. | ||
* Do the API Gateway / Application Load Balancer dance. | ||
* Swim in the savings of serverless. | ||
|
||
If you app _doesn't_ listen on port 8080, you can specify a different one using | ||
the `PORT` environment variable. | ||
|
||
Additionally, `lambdahttp` has no real way of knowing when your app is ready to | ||
start serving traffic. For this reason, it will continuously make requests to | ||
`/ping` until that endpoint returns a `200 OK` - this is how it knows you are | ||
good to go. If that path doesn't work for you, specify a different one in the | ||
`HEALTHCHECK_PATH` environment variable. | ||
|
||
## How does it work? | ||
|
||
Let me explain through the only kind of diagram I know: a sequence diagram. | ||
|
||
![seq-diag](/docs/seq-diag.png) | ||
|
||
`lambdahttp` communicates with the Lambda service using the [Lambda runtime interface][runtime] | ||
and converts these requests into regular HTTP over TCP. It converts the responses back to the | ||
format expected by Lambda and voilà. Nothing new you need to learn. | ||
|
||
[releases]: https://github.com/glassechidna/lambdahttp/releases | ||
[runtime]: https://docs.aws.amazon.com/lambda/latest/dg/runtimes-api.html |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
@startuml | ||
participant Browser | ||
participant "API Gateway" as apigw | ||
participant Lambda | ||
participant lambdahttp | ||
participant "Your app" as app | ||
|
||
Browser -> apigw: GET /index HTTP/1.1 | ||
apigw -> Lambda: {"spooky": "json"...} | ||
Lambda -> lambdahttp: (magic) | ||
lambdahttp -> app: GET /index HTTP/1.1 | ||
app -> lambdahttp: 200 OK ... | ||
lambdahttp -> Lambda: (magic) | ||
Lambda -> apigw: {"more": "aws json"...} | ||
apigw -> Browser: 200 OK ... | ||
@enduml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"github.com/glassechidna/lambdahttp/pkg/proxy" | ||
"net/http" | ||
"os" | ||
"os/exec" | ||
"strconv" | ||
"strings" | ||
"time" | ||
) | ||
|
||
func main() { | ||
port := port() | ||
|
||
ctx := context.Background() | ||
cmdch := make(chan error) | ||
go runCmd(ctx, cmdch) | ||
|
||
readych := make(chan error) | ||
go waitForHealthy(ctx, readych, port) | ||
|
||
select { | ||
case err := <-cmdch: | ||
panic(fmt.Sprintf("%+v", err)) | ||
case err := <-readych: | ||
if err != nil { | ||
panic(fmt.Sprintf("%+v", err)) | ||
} | ||
} | ||
|
||
go runProxy(ctx, port, cmdch) | ||
for { | ||
select { | ||
case <-ctx.Done(): | ||
panic("cancelled") | ||
case err := <-cmdch: | ||
panic(err) | ||
} | ||
} | ||
} | ||
|
||
func waitForHealthy(ctx context.Context, readych chan error, port int) { | ||
path := strings.TrimPrefix(os.Getenv("HEALTHCHECK_PATH"), "/") | ||
if path == "" { | ||
path = "ping" | ||
} | ||
|
||
url := fmt.Sprintf("http://127.0.0.1:%d/%s", port, path) | ||
|
||
waitUntil(ctx, readych, func() bool { | ||
resp, err := http.Get(url) | ||
return err == nil && resp != nil && resp.StatusCode == 200 | ||
}) | ||
} | ||
|
||
func runProxy(ctx context.Context, port int, cmdch chan error) { | ||
runtimeBaseUrl := os.ExpandEnv("http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01") | ||
|
||
proxy := proxy.New(runtimeBaseUrl, port, &http.Client{}, &http.Client{}) | ||
for { | ||
err := proxy.Next(ctx) | ||
if err != nil { | ||
cmdch <- err | ||
} | ||
} | ||
} | ||
|
||
func port() int { | ||
port, _ := strconv.Atoi(os.Getenv("PORT")) | ||
if port == 0 { | ||
port = 8080 | ||
os.Setenv("PORT", "8080") | ||
} | ||
return port | ||
} | ||
|
||
func runCmd(ctx context.Context, ch chan error) { | ||
fmt.Println(os.Getwd()) | ||
|
||
subcmd := os.Getenv("_HANDLER") | ||
cmd := exec.CommandContext(ctx, "/bin/sh", "-c", subcmd) | ||
cmd.Stdout = os.Stdout | ||
cmd.Stderr = os.Stderr | ||
ch <- cmd.Run() | ||
} | ||
|
||
func waitUntil(ctx context.Context, done chan error, condition func() bool) { | ||
ticker := time.NewTicker(100 * time.Millisecond) | ||
defer ticker.Stop() | ||
|
||
for { | ||
select { | ||
case <-ctx.Done(): | ||
done <- ctx.Err() | ||
case <-ticker.C: | ||
if condition() { | ||
done <- nil | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package proxy | ||
|
||
import ( | ||
"github.com/aws/aws-sdk-go/aws" | ||
"github.com/aws/aws-sdk-go/aws/session" | ||
"github.com/aws/aws-sdk-go/service/lambda" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
"io/ioutil" | ||
"os" | ||
"os/exec" | ||
"regexp" | ||
"strings" | ||
"testing" | ||
) | ||
|
||
func TestEndToEnd(t *testing.T) { | ||
if testing.Short() { | ||
t.SkipNow() | ||
} | ||
|
||
cmd := exec.Command("testdata/e2e.sh") | ||
cmd.Stdout = os.Stdout | ||
cmd.Stderr = os.Stderr | ||
err := cmd.Run() | ||
require.NoError(t, err) | ||
|
||
zip, err := ioutil.ReadFile("../../lambda.zip") | ||
require.NoError(t, err) | ||
|
||
sess, err := session.NewSession(aws.NewConfig().WithRegion("ap-southeast-2")) | ||
require.NoError(t, err) | ||
|
||
api := lambda.New(sess) | ||
updateResp, err := api.UpdateFunctionCode(&lambda.UpdateFunctionCodeInput{ | ||
FunctionName: aws.String("lambdahttptest"), | ||
Publish: aws.Bool(true), | ||
ZipFile: zip, | ||
}) | ||
require.NoError(t, err) | ||
|
||
payload, err := ioutil.ReadFile("testdata/alb_input.json") | ||
require.NoError(t, err) | ||
|
||
invokeResp, err := api.Invoke(&lambda.InvokeInput{ | ||
FunctionName: updateResp.FunctionArn, | ||
Payload: payload, | ||
}) | ||
require.NoError(t, err) | ||
|
||
expected, err := ioutil.ReadFile("testdata/alb_expected_output.json") | ||
assert.NoError(t, err) | ||
assert.JSONEq(t, string(expected), normalizeDateInResponse(invokeResp.Payload)) | ||
} | ||
|
||
func normalizeDateInResponse(payload []byte) string { | ||
response := string(payload) | ||
regex := regexp.MustCompile(`"Date":"([^"]+)"`) | ||
matches := regex.FindStringSubmatch(response) | ||
returnedDate := matches[1] | ||
return strings.ReplaceAll(response, returnedDate, "Sun, 06 Oct 2019 06:53:36 GMT") | ||
} |
Oops, something went wrong.