Skip to content

Commit

Permalink
Redo the whole thing and make it silly
Browse files Browse the repository at this point in the history
  • Loading branch information
aidansteele committed Nov 28, 2019
1 parent 6b473d8 commit 8dbd049
Show file tree
Hide file tree
Showing 17 changed files with 597 additions and 1 deletion.
20 changes: 20 additions & 0 deletions .github/workflows/push.yml
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
20 changes: 20 additions & 0 deletions .github/workflows/release.yml
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 }}
18 changes: 18 additions & 0 deletions .goreleaser.yml
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:'
41 changes: 41 additions & 0 deletions docs/README.md
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
Binary file added docs/seq-diag.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 16 additions & 0 deletions docs/seq-diag.txt
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
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ go 1.13

require (
github.com/aws/aws-lambda-go v1.13.2
github.com/aws/aws-sdk-go v1.25.6
github.com/jarcoal/httpmock v1.0.4
github.com/pkg/errors v0.8.1
github.com/stretchr/testify v1.4.0
)
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/aws/aws-lambda-go v1.13.2 h1:8lYuRVn6rESoUNZXdbCmtGB4bBk4vcVYojiHjE4mMrM=
github.com/aws/aws-lambda-go v1.13.2/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
github.com/aws/aws-sdk-go v1.25.6 h1:Rmg2pgKXoCfNe0KQb4LNSNmHqMdcgBjpMeXK9IjHWq8=
github.com/aws/aws-sdk-go v1.25.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/jarcoal/httpmock v1.0.4 h1:jp+dy/+nonJE4g4xbVtl9QdrUNbn6/3hDT5R4nDIZnA=
github.com/jarcoal/httpmock v1.0.4/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
Expand Down
104 changes: 104 additions & 0 deletions main.go
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
}
}
}
}
6 changes: 5 additions & 1 deletion pkg/gowrap/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,11 @@ func urlForRequest(request events.ALBTargetGroupRequest) *url.URL {
query[k] = append(query[k], v)
}

u, _ := url.Parse(fmt.Sprintf("%s://%s%s?%s", proto, host, path, query.Encode()))
u, err := url.Parse(fmt.Sprintf("%s://%s%s?%s", proto, host, path, query.Encode()))
if err != nil {
panic(err)
}

return u
}

Expand Down
62 changes: 62 additions & 0 deletions pkg/proxy/e2e_test.go
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")
}
Loading

0 comments on commit 8dbd049

Please sign in to comment.