Skip to content

Commit

Permalink
Merge pull request #21 from dokku/master
Browse files Browse the repository at this point in the history
Release 0.2.0
  • Loading branch information
josegonzalez authored Apr 18, 2022
2 parents 11b34e9 + 994e871 commit ae752ee
Show file tree
Hide file tree
Showing 8 changed files with 162 additions and 21 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ MAINTAINER_NAME = Jose Diaz-Gonzalez
REPOSITORY = lambda-builder
HARDWARE = $(shell uname -m)
SYSTEM_NAME = $(shell uname -s | tr '[:upper:]' '[:lower:]')
BASE_VERSION ?= 0.2.0
BASE_VERSION ?= 0.3.0
IMAGE_NAME ?= $(MAINTAINER)/$(REPOSITORY)
PACKAGECLOUD_REPOSITORY ?= dokku/dokku-betafish

Expand Down
33 changes: 30 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ I don't want to go through the motions of figuring out the correct way to build

```shell
# substitute the version number as desired
go build -ldflags "-X main.Version=0.2.0
go build -ldflags "-X main.Version=0.3.0
```
## Usage
Expand All @@ -28,7 +28,7 @@ Available commands are:
version Return the version of the binary
```
### Building an image
### Building an app
To build an app:
Expand All @@ -53,6 +53,12 @@ Custom environment variables can be supplied for the build environment by specif
lambda-builder build --build-env KEY=VALUE --build-env ANOTHER_KEY=some-value
```
A `builder` can be chosen by a flag. Note that while a `builder` may be selected, the detection for that builder must still pass in order for the build to succeed.
```shell
lambda-builder build --generate-image --builder dotnet
````
#### Building an image
A docker image can be produced from the generated artifact by specifying the `--generate-image` flag. This also allows for multiple `--label` flags as well as specifying a single image tag via either `-t` or `--tag`:
Expand Down Expand Up @@ -84,6 +90,27 @@ Custom environment variables can be supplied for the built image by specifying o
lambda-builder build --generate-image --image-env KEY=VALUE --image-env ANOTHER_KEY=some-value
```
The `build-image` and `run-image` can also be specified as flags:
```shell
lambda-builder build --generate-image --build-image "mlupin/docker-lambda:dotnetcore3.1-build" --run-image "mlupin/docker-lambda:dotnetcore3.1"
````
A generated image can be run locally with the following line:
```shell
# run the container and ensure it stays open
# replace `$APP` with your folder name
docker run --rm -it -e DOCKER_LAMBDA_STAY_OPEN=1 -p 9001:9001 "lambda-builder:$APP:latest"
# invoke it using the awscli (v2)
# note that the function name in this example is `function.handler`
aws lambda invoke --endpoint http://localhost:9001 --no-sign-request --function-name function.handler --payload '{}' --cli-binary-format raw-in-base64-out output.json
# invoke it via curl
curl -d '{}' http://localhost:9001/2015-03-31/functions/function.handler/invocations
```
#### Generating a Procfile
A `Procfile` can be written to the working directory by specifying the `--write-procfile` flag. This file will not be written if one already exists in the working directory. If an image is being built, the detected handler will also be injected into the build context and used as the default `CMD` for the image. The contents of the `Procfile` are a `web` process type and a detected handler.
Expand Down Expand Up @@ -112,7 +139,7 @@ Internally, `lambda-builder` detects a given language and builds the app accordi
- dotnetcore3.1
- `go`
- default build image: `lambci/lambda:build-go1.x`
- requirement: `go.mod`
- requirement: `go.mod` or `main.go`
- runtimes:
- provided.al2
- `nodejs`
Expand Down
15 changes: 12 additions & 3 deletions builders/go.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@ func NewGoBuilder(config Config) (GoBuilder, error) {
}

func (b GoBuilder) Detect() bool {
if io.FileExistsInDirectory(b.Config.WorkingDirectory, "go.sum") {
if io.FileExistsInDirectory(b.Config.WorkingDirectory, "go.mod") {
return true
}

if io.FileExistsInDirectory(b.Config.WorkingDirectory, "main.go") {
return true
}

Expand Down Expand Up @@ -76,8 +80,13 @@ puts-step() {
}
install-gomod() {
puts-step "Downloading dependencies via go mod"
go mod download 2>&1 | indent
if [[ -f "go.mod" ]]; then
puts-step "Downloading dependencies via go mod"
go mod download 2>&1 | indent
else
puts-step "Missing go.mod, downloading dependencies via go get"
go get
fi
puts-step "Compiling via go build"
go build -o bootstrap main.go 2>&1 | indent
Expand Down
5 changes: 3 additions & 2 deletions builders/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ type Builder interface {

type Config struct {
BuildEnv []string
GenerateImage bool
Builder string
BuilderBuildImage string
BuilderRunImage string
GenerateImage bool
Handler string
HandlerMap map[string]string
Identifier string
Expand All @@ -37,8 +38,8 @@ type Config struct {
ImageTag string
Port int
RunQuiet bool
WriteProcfile bool
WorkingDirectory string
WriteProcfile bool
}

func (c Config) GetImageTag() string {
Expand Down
40 changes: 28 additions & 12 deletions commands/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,16 @@ type BuildCommand struct {
command.Meta

buildEnv []string
builder string
buildImage string
generateImage bool
handler string
imageEnv []string
imageTag string
labels []string
port int
quiet bool
runImage string
workingDirectory string
writeProcfile bool
}
Expand Down Expand Up @@ -74,9 +77,12 @@ func (c *BuildCommand) FlagSet() *flag.FlagSet {
f.BoolVar(&c.quiet, "quiet", false, "run builder in quiet mode")
f.BoolVar(&c.writeProcfile, "write-procfile", false, "writes a Procfile if a handler is specified or detected")
f.IntVar(&c.port, "port", -1, "set the default port for the lambda to listen on")
f.StringVar(&c.builder, "builder", "", "set the builder to use")
f.StringVar(&c.buildImage, "build-image", "", "set the build-image to use")
f.StringVar(&c.handler, "handler", "", "handler override to specify as the default command to run in a built image")
f.StringVarP(&c.imageTag, "tag", "t", "", "name and optionally a tag in the 'name:tag' format")
f.StringVar(&c.runImage, "run-image", "", "set the run-image to use")
f.StringVar(&c.workingDirectory, "working-directory", workingDirectory, "working directory")
f.StringVarP(&c.imageTag, "tag", "t", "", "name and optionally a tag in the 'name:tag' format")
f.StringArrayVar(&c.buildEnv, "build-env", []string{}, "environment variables to be set for the build context")
f.StringArrayVar(&c.imageEnv, "image-env", []string{}, "environment variables to be committed to a built image")
f.StringArrayVar(&c.labels, "label", []string{}, "set metadata for an image")
Expand All @@ -88,12 +94,15 @@ func (c *BuildCommand) AutocompleteFlags() complete.Flags {
c.Meta.AutocompleteFlags(command.FlagSetClient),
complete.Flags{
"--build-env": complete.PredictAnything,
"--build-image": complete.PredictAnything,
"--builder": complete.PredictSet("dotnet", "go", "nodejs", "python", "ruby"),
"--generate-image": complete.PredictNothing,
"--handler": complete.PredictAnything,
"--image-env": complete.PredictAnything,
"--label": complete.PredictAnything,
"--port": complete.PredictAnything,
"--quiet": complete.PredictNothing,
"--run-image": complete.PredictAnything,
"-t": complete.PredictAnything,
"--tag": complete.PredictAnything,
"--working-directory": complete.PredictAnything,
Expand Down Expand Up @@ -136,16 +145,19 @@ func (c *BuildCommand) Run(args []string) int {

identifier := uuid.New().String()
config := builders.Config{
BuildEnv: c.buildEnv,
GenerateImage: c.generateImage,
Identifier: identifier,
ImageEnv: c.imageEnv,
ImageLabels: c.labels,
ImageTag: c.imageTag,
Port: c.port,
RunQuiet: c.quiet,
WriteProcfile: c.writeProcfile,
WorkingDirectory: c.workingDirectory,
BuildEnv: c.buildEnv,
Builder: c.builder,
BuilderBuildImage: c.buildImage,
BuilderRunImage: c.runImage,
GenerateImage: c.generateImage,
Identifier: identifier,
ImageEnv: c.imageEnv,
ImageLabels: c.labels,
ImageTag: c.imageTag,
Port: c.port,
RunQuiet: c.quiet,
WorkingDirectory: c.workingDirectory,
WriteProcfile: c.writeProcfile,
}

logger.LogHeader1("Detecting builder")
Expand Down Expand Up @@ -223,8 +235,12 @@ func detectBuilder(config builders.Config) (builders.Builder, error) {
}
bs = append(bs, builder)

selectedImage := lambdaYML.Builder
if config.Builder != "" {
selectedImage = config.Builder
}
for _, builder := range bs {
if lambdaYML.Builder != "" && lambdaYML.Builder != builder.Name() {
if selectedImage != "" && selectedImage != builder.Name() {
continue
}

Expand Down
7 changes: 7 additions & 0 deletions test.bats
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ teardown_file() {
[[ "$status" -eq 0 ]]
}

@test "[build] go without go modules" {
run $LAMBDA_BUILDER_BIN build --working-directory tests/go-nomod
echo "output: $output"
echo "status: $status"
[[ "$status" -eq 0 ]]
}

@test "[build] hooks" {
run $LAMBDA_BUILDER_BIN build --working-directory tests/hooks
echo "output: $output"
Expand Down
20 changes: 20 additions & 0 deletions tests/go-nomod/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package main

import (
"context"
"fmt"

"github.com/aws/aws-lambda-go/lambda"
)

type MyEvent struct {
Name string `json:"name"`
}

func HandleRequest(ctx context.Context, name MyEvent) (string, error) {
return fmt.Sprintf("Hello %s!", name.Name), nil
}

func main() {
lambda.Start(HandleRequest)
}
61 changes: 61 additions & 0 deletions tests/go-nomod/test.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#!/usr/bin/env bats

export LAMBDA_ROLE="arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
export AWS_ACCOUNT_ID="$(aws sts get-caller-identity | jq -r ".Account")"
export LAMBDA_FUNCTION_NAME=lambda-go1x
export LAMBDA_RUNTIME=provided.al2
export LAMBDA_HANDLER=bootstrap

setup() {
aws lambda delete-function --function-name "$LAMBDA_FUNCTION_NAME" 2>/dev/null || true
aws iam detach-role-policy --role-name "$LAMBDA_FUNCTION_NAME" --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole 2>/dev/null || true
aws iam delete-role --role-name "$LAMBDA_FUNCTION_NAME" 2>/dev/null || true
}

teardown() {
aws lambda delete-function --function-name "$LAMBDA_FUNCTION_NAME" 2>/dev/null || true
aws iam detach-role-policy --role-name "$LAMBDA_FUNCTION_NAME" --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole 2>/dev/null || true
aws iam delete-role --role-name "$LAMBDA_FUNCTION_NAME" 2>/dev/null || true
}

@test "aws test" {
run /bin/bash -c "lambda-builder build"
echo "output: $output"
echo "status: $status"
[[ "$status" -eq 0 ]]

run /bin/bash -c "aws iam create-role --role-name '$LAMBDA_FUNCTION_NAME' --tags 'Key=app,Value=lambda-builder' --tags 'Key=com.dokku.lambda-builder/runtime,Value=$LAMBDA_RUNTIME' --assume-role-policy-document '{\"Version\": \"2012-10-17\", \"Statement\": [{ \"Effect\": \"Allow\", \"Principal\": {\"Service\": \"lambda.amazonaws.com\"}, \"Action\": \"sts:AssumeRole\"}]}'"
echo "output: $output"
echo "status: $status"
[[ "$status" -eq 0 ]]

run /bin/bash -c "aws iam attach-role-policy --role-name '$LAMBDA_FUNCTION_NAME' --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
echo "output: $output"
echo "status: $status"
[[ "$status" -eq 0 ]]

run /bin/bash -c "sleep 10"
echo "output: $output"
echo "status: $status"
[[ "$status" -eq 0 ]]

run /bin/bash -c "aws lambda create-function --function-name '$LAMBDA_FUNCTION_NAME' --package-type Zip --tags 'app=lambda-builder,com.dokku.lambda-builder/runtime=$LAMBDA_RUNTIME' --role 'arn:aws:iam::${AWS_ACCOUNT_ID}:role/$LAMBDA_FUNCTION_NAME' --zip-file fileb://lambda.zip --runtime '$LAMBDA_RUNTIME' --handler '$LAMBDA_HANDLER'"
echo "output: $output"
echo "status: $status"
[[ "$status" -eq 0 ]]

run /bin/bash -c "sleep 10"
echo "output: $output"
echo "status: $status"
[[ "$status" -eq 0 ]]

run /bin/bash -c "aws lambda get-function --function-name '$LAMBDA_FUNCTION_NAME'"
echo "output: $output"
echo "status: $status"
[[ "$status" -eq 0 ]]

run /bin/bash -c "aws lambda invoke --cli-binary-format raw-in-base64-out --function-name '$LAMBDA_FUNCTION_NAME' --payload '{\"name\": \"World\"}' response.json"
echo "output: $output"
echo "status: $status"
[[ "$status" -eq 0 ]]
}

0 comments on commit ae752ee

Please sign in to comment.