Skip to content
Open
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
4 changes: 2 additions & 2 deletions copier.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ copier__auth:

copier__dynamo_db:
type: bool
help: "Do you want to enable DynamoDB for your project? (y/n) [default: No]"
default: false
help: "Do you want to enable DynamoDB for your project? (y/n) [default: Yes]"
default: true

copier__aws_region:
type: str
Expand Down
11 changes: 6 additions & 5 deletions tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def run_setup():
print("Error: AWS SAM CLI is not installed. Please install it and try again.")
exit(1)

print("Running AWS SAM build and validate...")
print("Running AWS SAM validate and build...")
subprocess.run(shlex.split("make validate"))
subprocess.run(shlex.split("make build"))
print("AWS Lambda template build and setup complete.")
Expand Down Expand Up @@ -52,10 +52,11 @@ def log_next_steps():
HINT
+ "Next steps:"
+ "\n1. Run 'cd {{ copier__project_name }}' to enter the project directory"
+ "\n2. Update the .envrc file with your AWS credentials"
+ "\n3. Run 'make setup' to run dynamodb locally and create the database"
+ "\n4. Then run 'make build' to build the lambda function"
+ "\n5. Then run 'make deploy' to deploy the lambda function"
+ "\n2. Run 'make setup' to run dynamodb locally and create the database"
+ "\n3. Then run 'make build' to build the lambda function"
+ "\n4. To run lambda function locally, run 'make local' to invoke the lambda function"
+ "\n5. To deploy, run 'make deploy' to deploy the lambda function"
+ "\n6. To remove the local setup, run 'make down'"
+ "\nRefer to README.md for more details."
+ TERMINATOR
)
Expand Down
3 changes: 0 additions & 3 deletions {{copier__project_name}}/.envrc

This file was deleted.

146 changes: 98 additions & 48 deletions {{copier__project_name}}/Makefile
Original file line number Diff line number Diff line change
@@ -1,60 +1,61 @@
.PHONY: build deploy local clean test setup{% if copier__dynamo_db %} dynamo-up dynamo-down create-table{% endif %}
.PHONY: build deploy local clean test setup{% if copier__dynamo_db %} dynamo-up dynamo-down wait-for-dynamodb create-table{% endif %}

# Configuration
STACK_NAME ?= {{ copier__stack_name }}
REGION ?= {{copier__aws_region}}
TEMPLATE_FILE ?= template.yaml
BUILD_DIR ?= .aws-sam/build
AWS_PROFILE ?= {{copier__aws_profile}}
{% if copier__dynamo_db %}
DYNAMO_ENDPOINT ?= http://localhost:8000
TABLE_NAME ?= RequestsTable-{{copier__stack_name}}
CONTAINER_NAME ?= {{copier__stack_name}}-dynamodb-local
NETWORK_NAME ?= {{copier__stack_name}}-network
{% endif %}

# Default target
all: build
## Build & Deploy

build:
sam build {% if copier__package_type != "image" %}--use-container{% endif %} > /dev/null 2>&1
build: ## Build the AWS SAM project
@sam build > /dev/null 2>build_error.log && echo "Build succeeded!" || (cat build_error.log && exit 1)
@rm -f build_error.log

validate:
validate: ## Validate the SAM template with linting
sam validate --lint

deploy:
sam deploy --guided --profile {{copier__aws_profile}}
deploy: ## Deploy the stack using interactive guided mode
sam deploy --guided --profile $(AWS_PROFILE) \
{% if copier__package_type == "image" %}--resolve-image-repos{% endif %}

deploy-sandbox:
sam deploy --stack-name $(STACK_NAME)-sandbox \
--capabilities CAPABILITY_IAM \
--parameter-overrides StageName=sandbox \
{% if copier__package_type != "image" %}--resolve-image-repos{% endif %}
## Local Development

local:
sam local start-api {% if copier__dynamo_db %}--env-vars env_example.json --docker-network lambda-local{% endif %}
local: ## Start the local API using SAM
sam local start-api {% if copier__dynamo_db %}--env-vars env_example.json --docker-network $(NETWORK_NAME){% endif %}

invoke:
sam local invoke HelloWorldFunction --event events/event.json{% if copier__dynamo_db %} --env-vars env_example.json --docker-network lambda-local{% endif %}
invoke: ## Invoke a Lambda function locally using an event payload
sam local invoke HelloWorldFunction --event events/event.json{% if copier__dynamo_db %} --env-vars env_example.json --docker-network $(NETWORK_NAME){% endif %}

{% if copier__dynamo_db %}
dynamo-up:
@if [ -z "$$(docker network ls --filter name=^lambda-local$$ -q)" ]; then \
echo "Creating Docker network 'lambda-local'..."; \
docker network create lambda-local; \
## DynamoDB (Local) Setup

dynamo-up: ## Start a local DynamoDB instance in a Docker container
@if [ -z "$$(docker network ls --filter name=^$(NETWORK_NAME)$$ -q)" ]; then \
echo "Creating Docker network '$(NETWORK_NAME)'..."; \
docker network create $(NETWORK_NAME); \
else \
echo "Docker network 'lambda-local' already exists."; \
echo "Docker network '$(NETWORK_NAME)' already exists."; \
fi; \
if [ -f .dynamodb-container-id ] && [ -n "$$(docker ps -q -f id=$$(cat .dynamodb-container-id 2>/dev/null))" ]; then \
echo "DynamoDB local container is already running with ID: $$(cat .dynamodb-container-id)"; \
if [ -n "$$(docker ps -q -f name=^/$(CONTAINER_NAME)$$)" ]; then \
echo "DynamoDB local container '$(CONTAINER_NAME)' is already running."; \
else \
echo "Starting DynamoDB local container..."; \
rm -f .dynamodb-container-id 2>/dev/null || true; \
docker run -d --rm --network lambda-local -p 8000:8000 --name {{copier__stack_name}}-dynamodb-local amazon/dynamodb-local:latest -jar DynamoDBLocal.jar -sharedDb > .dynamodb-container-id; \
echo "DynamoDB local container started with ID: $$(cat .dynamodb-container-id)"; \
docker run -d --rm --network $(NETWORK_NAME) --name $(CONTAINER_NAME) amazon/dynamodb-local:latest -jar DynamoDBLocal.jar -sharedDb; \
echo "DynamoDB local container '$(CONTAINER_NAME)' started."; \
fi

wait-for-dynamodb:
@echo "Waiting for DynamoDB Local to be ready..."
wait-for-dynamodb: ## Wait until DynamoDB Local is ready inside the container
@echo "Waiting for DynamoDB Local to be ready (inside container)..."
@attempts=0; \
until curl -s http://localhost:8000 >/dev/null; do \
until docker exec $(CONTAINER_NAME) curl -s http://localhost:8000 >/dev/null 2>&1; do \
if [ $$attempts -ge 10 ]; then \
echo "DynamoDB Local did not become ready in time"; \
exit 1; \
Expand All @@ -65,9 +66,13 @@ wait-for-dynamodb:
done; \
echo "DynamoDB Local is ready."

create-table:
@# Store the AWS command output in a variable
$(eval AWS_OUTPUT := $(shell aws dynamodb create-table \
create-table: ## Create the DynamoDB table in the local container
@echo "Creating DynamoDB table '$(TABLE_NAME)' in container '$(CONTAINER_NAME)'..."
@docker run --rm --network $(NETWORK_NAME) \
-e AWS_ACCESS_KEY_ID=dummy \
-e AWS_SECRET_ACCESS_KEY=dummy \
amazon/aws-cli \
dynamodb create-table \
--table-name $(TABLE_NAME) \
--attribute-definitions \
AttributeName=ip_address,AttributeType=S \
Expand All @@ -76,25 +81,70 @@ create-table:
AttributeName=ip_address,KeyType=HASH \
AttributeName=timestamp,KeyType=RANGE \
--billing-mode PAY_PER_REQUEST \
--endpoint-url $(DYNAMO_ENDPOINT)))
--endpoint-url http://$(CONTAINER_NAME):8000 \
--region $(REGION)

list-records: ## List all records in the DynamoDB table
@echo "Listing records from DynamoDB table '$(TABLE_NAME)'..."
@docker run --rm --network $(NETWORK_NAME) \
-e AWS_ACCESS_KEY_ID=dummy \
-e AWS_SECRET_ACCESS_KEY=dummy \
amazon/aws-cli \
dynamodb scan \
--table-name $(TABLE_NAME) \
--endpoint-url http://$(CONTAINER_NAME):8000 \
--region $(REGION)

count-records: ## Count the number of records in the DynamoDB table
@echo "Counting records in DynamoDB table '$(TABLE_NAME)'..."
@docker run --rm --network $(NETWORK_NAME) \
-e AWS_ACCESS_KEY_ID=dummy \
-e AWS_SECRET_ACCESS_KEY=dummy \
amazon/aws-cli \
dynamodb scan \
--table-name $(TABLE_NAME) \
--select "COUNT" \
--endpoint-url http://$(CONTAINER_NAME):8000 \
--region $(REGION)

@# Check if jq exists and use it if available
@if command -v jq > /dev/null 2>&1; then \
echo '$(AWS_OUTPUT)' | jq; \
else \
echo '$(AWS_OUTPUT)'; \
echo "\033[33mNote: Install jq for colorized JSON output\033[0m"; \
fi
setup: dynamo-up wait-for-dynamodb create-table ## Set up local DynamoDB environment{% else %}
setup: ## Install Python dependencies
pip3 install -r src/requirements.txt{% endif %}

setup: dynamo-up wait-for-dynamodb create-table{% else %}
setup:
pip install -r requirements.txt{% endif %}
## Testing

test:
test: ## Run Python unit tests using uv and pytest
AWS_SAM_STACK_NAME=$(STACK_NAME) PYTHONPATH=. uv run --with pytest,boto3,requests,pytest-mock pytest -v --disable-warnings --tb=short tests

delete-sandbox:
sam delete --stack-name $(STACK_NAME)-sandbox
## Cleanup

delete:
down: ## Stop and remove local DynamoDB container and network
@if [ -n "$$(docker ps -q -f name=^/$(CONTAINER_NAME)$$)" ]; then \
echo "Stopping DynamoDB local container..."; \
docker stop $(CONTAINER_NAME); \
echo "DynamoDB local container '$(CONTAINER_NAME)' stopped."; \
else \
echo "DynamoDB local container '$(CONTAINER_NAME)' is not running."; \
fi; \
if [ -n "$$(docker network ls --filter name=^$(NETWORK_NAME)$$ -q)" ]; then \
echo "Removing Docker network '$(NETWORK_NAME)'..."; \
docker network rm $(NETWORK_NAME); \
echo "Docker network '$(NETWORK_NAME)' removed."; \
else \
echo "Docker network '$(NETWORK_NAME)' does not exist."; \
fi

delete: ## Delete the deployed SAM stack
sam delete --stack-name $(STACK_NAME)

## Help

help: ## Show the list of all the commands and their help text
@awk 'BEGIN { FS = ":.*##"; target="";printf "\nUsage:\n make \033[36m<target>\033[33m\n\nTargets:\033[0m\n" } \
/^[a-zA-Z_-]+:.*?##/ { if(target=="")print ""; target=$$1; printf " \033[36m%-10s\033[0m %s\n\n", $$1, $$2 } \
/^([a-zA-Z_-]+):/ {if(target=="")print "";match($$0, "(.*):"); target=substr($$0,RSTART,RLENGTH) } \
/^\t## (.*)/ { match($$0, "[^\t#:\\\\]+"); txt=substr($$0,RSTART,RLENGTH);printf " \033[36m%-10s\033[0m", target; printf " %s\n", txt ; target=""} \
/^## .*/ {match($$0, "## (.+)$$"); txt=substr($$0,4,RLENGTH);printf "\n\033[33m%s\033[0m\n", txt ; target=""} \
' $(MAKEFILE_LIST)

.DEFAULT_GOAL := help
6 changes: 6 additions & 0 deletions {{copier__project_name}}/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,12 @@ make test

## Cleanup

To delete the local setup (i.e container and local network) run the following command:

```bash
make down
```

To delete the sample application that you created, use the AWS CLI. Assuming you used your project name for the stack name, you can run the following:

```bash
Expand Down
2 changes: 1 addition & 1 deletion {{copier__project_name}}/template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ Resources:
Runtime: python{{ copier__runtime }}
{% endif %}
Architectures:
- {{copier__architectures}}
- "{{copier__architectures}}"
{% if copier__dynamo_db %}
Environment:
Variables:
Expand Down