Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ module github.com/sil-org/rest-data-archiver
go 1.25

require (
github.com/aws/aws-lambda-go v1.49.0
github.com/aws/aws-sdk-go-v2/config v1.31.12
github.com/aws/aws-sdk-go-v2/credentials v1.18.16
github.com/aws/aws-sdk-go-v2/service/s3 v1.88.3
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
github.com/aws/aws-lambda-go v1.49.0 h1:z4VhTqkFZPM3xpEtTqWqRqsRH4TZBMJqTkRiBPYLqIQ=
github.com/aws/aws-lambda-go v1.49.0/go.mod h1:dpMpZgvWx5vuQJfBt0zqBha60q7Dd7RfgJv23DymV8A=
github.com/aws/aws-sdk-go-v2 v1.39.2 h1:EJLg8IdbzgeD7xgvZ+I8M1e0fL0ptn/M47lianzth0I=
github.com/aws/aws-sdk-go-v2 v1.39.2/go.mod h1:sDioUELIUO9Znk23YVmIk86/9DOpkbyyVb1i/gUNFXY=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.1 h1:i8p8P4diljCr60PpJp6qZXNlgX4m2yQFpYk+9ZT+J4E=
Expand Down
3 changes: 1 addition & 2 deletions lambda-example/.dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@
*

# Whitelist required files
!.env.encrypted
!build.sh
!cdk*.json
!config.json
!go.*
!*.go
!serverless.yml
1 change: 1 addition & 0 deletions lambda-example/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_REGION=us-east-1
STAGE=dev
57 changes: 57 additions & 0 deletions lambda-example/.github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: Continuous Integration

on:
push:
branches: ["**"]

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: false

jobs:
test:
name: Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5

- uses: actions/setup-go@v6
with:
go-version-file: "src/go.mod"
cache-dependency-path: |
src/go.sum
cdk/go.sum
- name: Unit tests
run: go test -C src -v

deploy:
name: Deploy
runs-on: ubuntu-latest
if: github.ref_name == 'main'
needs: test
steps:
- name: Checkout
uses: actions/checkout@v5

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "22"

- name: Install AWS CDK
run: npm install -g aws-cdk

- uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ vars.AWS_REGION }}

- name: Build
run: ./build.sh

- name: Deploy
env:
STAGE: production
run: cd cdk && cdk deploy --require-approval never
31 changes: 23 additions & 8 deletions lambda-example/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,10 +1,25 @@
FROM golang:1.17
FROM node:22

RUN curl -o- -L https://slss.io/install | VERSION=3.7.1 bash && \
mv $HOME/.serverless/bin/serverless /usr/local/bin && \
ln -s /usr/local/bin/serverless /usr/local/bin/sls
ENV GO_VERSION=1.25.0

# Copy in source and install deps
WORKDIR /src
COPY ./ .
RUN go get ./...
ADD https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip .
ADD https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz .

RUN <<EOF
unzip awscli-exe-linux-x86_64.zip
rm awscli-exe-linux-x86_64.zip
./aws/install
rm -rf ./aws

tar -C /usr/local -xzf go${GO_VERSION}.linux-amd64.tar.gz
rm go${GO_VERSION}.linux-amd64.tar.gz
ln -s /usr/local/go/bin/go /usr/local/bin/go

npm install --ignore-scripts --global aws-cdk

adduser user
EOF

USER user

WORKDIR /cdk
15 changes: 6 additions & 9 deletions lambda-example/Makefile
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
build:
docker-compose up -d app
docker compose run --rm app

bash:
docker-compose run --rm app bash
docker compose run --rm app bash

deploy:
docker-compose run --rm app sls deploy --verbose
deploy: build
docker compose run --rm app cdk deploy --require-approval never

info:
docker-compose run --rm app sls info

remove:
docker-compose run --rm app sls remove
destroy:
docker compose run --rm app cdk destroy lambda-example
19 changes: 8 additions & 11 deletions lambda-example/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,20 @@ secrets. Be sure not to store this file in a public repository.

The contents of this `lambda-example` directory are intended to be copied to
your own filesystem and repository. In this directory are a couple files for use
with [Codeship](https://codeship.com). If you don't use Codeship you can remove
them and replace them with whatever is appropriate for your CI/CD provider.
with Github Actions. If you don't use Github Actions you can remove them and
replace them with whatever is appropriate for your CI/CD provider.

After downloading the files, copy `config.example.json` to `config.json` and
edit as needed. Also copy `.env.example` to `.env` and insert AWS credentials
for Serverless to use to deploy the Lambda function.
for AWS CDK to use to deploy the Lambda function.

Due to dependencies on Go to build the application and Serverless to interact
Due to dependencies on Go to build the application and AWS CDK to interact
with AWS to create and deploy the Lambda the easiest way to do all this is using
the included `Dockerfile` and `docker-compose`.
the included `Dockerfile` and `docker compose`.

Run `make deploy`. This will build the Docker image, run `go get` inside the
container, build the Go binary, and use Serverless to deploy the Lambda function
to the default Serverless stage `dev`, but you can update the command in the
Makefile to change the stage as desired.
container, build the Go binary, and use CDK to deploy the Lambda function
to `dev`, but you can update the `.env` file to change the stage as desired.

If you want to automate deployment with Codeship, encrypt the `.env` file to
`.env.encrypted` and connect your private project repo with Codeship. By
default, it is configured to only deploy _production_ when changes are pushed to
By default, it is configured to only deploy _production_ when changes are pushed to
the main branch.
8 changes: 3 additions & 5 deletions lambda-example/build.sh
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
#!/usr/bin/env bash

# Exit script with error if any step fails.
set -e

# Echo out all commands for monitoring progress
set -x

# Build all the things
go build -ldflags="-s -w" -o bin/rda rda.go
# When using the provided.al2 runtime, the binary must be named "bootstrap" and be in the root directory
CGO_ENABLED=0 go build -C src -tags lambda.norpc -ldflags="-s -w" -o bin/bootstrap && \
cp src/config.json src/bin/config.json
18 changes: 18 additions & 0 deletions lambda-example/cdk/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# go.sum should be committed
!go.sum

# CDK asset staging directory
.cdk.staging
cdk.out
3 changes: 3 additions & 0 deletions lambda-example/cdk/cdk.context.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"cli-telemetry": false
}
105 changes: 105 additions & 0 deletions lambda-example/cdk/cdk.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package main

import (
"os"

"github.com/aws/aws-cdk-go/awscdk/v2"
"github.com/aws/aws-cdk-go/awscdk/v2/awsevents"
"github.com/aws/aws-cdk-go/awscdk/v2/awseventstargets"
"github.com/aws/aws-cdk-go/awscdk/v2/awsiam"
"github.com/aws/aws-cdk-go/awscdk/v2/awslambda"
"github.com/aws/aws-cdk-go/awscdk/v2/awslogs"
"github.com/aws/constructs-go/constructs/v10"
"github.com/aws/jsii-runtime-go"
)

type CdkStackProps struct {
awscdk.StackProps
}

func NewCdkStack(scope constructs.Construct, id, stage string, props *CdkStackProps) awscdk.Stack {
var sprops awscdk.StackProps
if props != nil {
sprops = props.StackProps
}
stack := awscdk.NewStack(scope, &id, &sprops)

logGroup := awslogs.NewLogGroup(stack, jsii.String("LambdaLogGroup"), &awslogs.LogGroupProps{
LogGroupName: jsii.Sprintf("/aws/lambda/%s-cdk", id),
Retention: awslogs.RetentionDays_TWO_MONTHS,
RemovalPolicy: awscdk.RemovalPolicy_DESTROY, // Remove logs when stack is deleted
})

function := awslambda.NewFunction(stack, jsii.Sprintf("%s-%s", id, stage), &awslambda.FunctionProps{
Code: awslambda.Code_FromAsset(jsii.String("../src/bin"), nil),
FunctionName: jsii.Sprintf("%s-%s", id, stage),
Handler: jsii.String("bootstrap"),
LoggingFormat: awslambda.LoggingFormat_JSON,
LogGroup: logGroup,
Runtime: awslambda.Runtime_PROVIDED_AL2023(),
Timeout: awscdk.Duration_Seconds(jsii.Number(300)),
})

function.AddToRolePolicy(awsiam.NewPolicyStatement(&awsiam.PolicyStatementProps{
Actions: jsii.Strings(
"ses:SendEmail",
),
Resources: &[]*string{function.FunctionArn()},
}))

schedule := awsevents.NewRule(stack, jsii.Sprintf("%s-schedule-%s", id, stage), &awsevents.RuleProps{
Schedule: awsevents.Schedule_Cron(&awsevents.CronOptions{
Minute: jsii.String("10"),
Hour: jsii.String("9"),
}),
})
schedule.AddTarget(awseventstargets.NewLambdaFunction(function, nil))

return stack
}

func main() {
defer jsii.Close()

const name = "lambda-example"
stage := getOptionalEnv("STAGE", "production")
short := shortenStageName(stage)

app := awscdk.NewApp(nil)
NewCdkStack(app, name, short, &CdkStackProps{
awscdk.StackProps{
Env: &awscdk.Environment{
Region: jsii.String(os.Getenv("AWS_REGION")),
},
Tags: &map[string]*string{
"managed_by": jsii.String("cdk"),
"itse_app_name": jsii.String(name),
"itse_app_customer": jsii.String("gtis"),
"itse_app_env": jsii.String(stage),
},
},
})

app.Synth(nil)
}

func getOptionalEnv(name, fallback string) string {
v := os.Getenv(name)
if v == "" {
v = fallback
}
return v
}

func shortenStageName(stage string) string {
m := map[string]string{
"production": "prod",
"prod": "prod",
"develop": "dev",
"dev": "dev",
"staging": "stg",
"stg": "stg",
}

return m[stage]
}
Loading