From ef66e56db6af7aaa2573d9802bf878e1b69dac28 Mon Sep 17 00:00:00 2001 From: vaquarkhan Date: Sun, 28 Jun 2026 01:04:39 -0500 Subject: [PATCH 1/4] docs(deployments): add AWS EC2 + Docker deployment example (#390) - Dockerfile + docker-compose for local development parity - FastAPI server wrapping the Burr counter application - Production-grade Terraform with modular structure (networking + compute) - Security: IMDSv2 enforced, EBS encryption, CIDR validation, no 0.0.0.0/0 ingress - Environment-specific tfvars (dev/prod) - RST docs page wired into deployment index - 40 automated tests (structure, license headers, app logic, security checks) - Full README with quickstart, deploy, verify, teardown, and security guidance --- docs/examples/deployment/ec2-docker.rst | 59 ++++++ docs/examples/deployment/index.rst | 1 + examples/deployment/aws/ec2-docker/Dockerfile | 29 +++ examples/deployment/aws/ec2-docker/README.md | 156 +++++++++++++++ .../deployment/aws/ec2-docker/app/__init__.py | 16 ++ .../aws/ec2-docker/app/counter_app.py | 63 ++++++ .../deployment/aws/ec2-docker/app/server.py | 53 ++++++ .../aws/ec2-docker/docker-compose.yml | 24 +++ .../aws/ec2-docker/requirements.txt | 20 ++ .../aws/ec2-docker/terraform/.gitignore | 12 ++ .../terraform/environments/dev.tfvars | 30 +++ .../terraform/environments/prod.tfvars | 29 +++ .../aws/ec2-docker/terraform/main.tf | 52 +++++ .../terraform/modules/compute/main.tf | 73 +++++++ .../terraform/modules/compute/outputs.tf | 31 +++ .../compute/templates/user_data.sh.tpl | 52 +++++ .../terraform/modules/compute/variables.tf | 53 ++++++ .../terraform/modules/networking/main.tf | 59 ++++++ .../terraform/modules/networking/outputs.tf | 21 ++ .../terraform/modules/networking/variables.tf | 31 +++ .../aws/ec2-docker/terraform/outputs.tf | 41 ++++ .../aws/ec2-docker/terraform/variables.tf | 95 ++++++++++ .../aws/ec2-docker/terraform/versions.tf | 27 +++ tests/deployment/__init__.py | 16 ++ tests/deployment/test_ec2_docker_example.py | 179 ++++++++++++++++++ 25 files changed, 1222 insertions(+) create mode 100644 docs/examples/deployment/ec2-docker.rst create mode 100644 examples/deployment/aws/ec2-docker/Dockerfile create mode 100644 examples/deployment/aws/ec2-docker/README.md create mode 100644 examples/deployment/aws/ec2-docker/app/__init__.py create mode 100644 examples/deployment/aws/ec2-docker/app/counter_app.py create mode 100644 examples/deployment/aws/ec2-docker/app/server.py create mode 100644 examples/deployment/aws/ec2-docker/docker-compose.yml create mode 100644 examples/deployment/aws/ec2-docker/requirements.txt create mode 100644 examples/deployment/aws/ec2-docker/terraform/.gitignore create mode 100644 examples/deployment/aws/ec2-docker/terraform/environments/dev.tfvars create mode 100644 examples/deployment/aws/ec2-docker/terraform/environments/prod.tfvars create mode 100644 examples/deployment/aws/ec2-docker/terraform/main.tf create mode 100644 examples/deployment/aws/ec2-docker/terraform/modules/compute/main.tf create mode 100644 examples/deployment/aws/ec2-docker/terraform/modules/compute/outputs.tf create mode 100644 examples/deployment/aws/ec2-docker/terraform/modules/compute/templates/user_data.sh.tpl create mode 100644 examples/deployment/aws/ec2-docker/terraform/modules/compute/variables.tf create mode 100644 examples/deployment/aws/ec2-docker/terraform/modules/networking/main.tf create mode 100644 examples/deployment/aws/ec2-docker/terraform/modules/networking/outputs.tf create mode 100644 examples/deployment/aws/ec2-docker/terraform/modules/networking/variables.tf create mode 100644 examples/deployment/aws/ec2-docker/terraform/outputs.tf create mode 100644 examples/deployment/aws/ec2-docker/terraform/variables.tf create mode 100644 examples/deployment/aws/ec2-docker/terraform/versions.tf create mode 100644 tests/deployment/__init__.py create mode 100644 tests/deployment/test_ec2_docker_example.py diff --git a/docs/examples/deployment/ec2-docker.rst b/docs/examples/deployment/ec2-docker.rst new file mode 100644 index 000000000..deb10d787 --- /dev/null +++ b/docs/examples/deployment/ec2-docker.rst @@ -0,0 +1,59 @@ +.. + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + + +.. _ec2-docker-deployment: + +========================== +AWS EC2 + Docker +========================== + +Deploy a Burr application as a Docker container on an AWS EC2 instance, provisioned with Terraform. + +See the full example and step-by-step guide: + +* Source: `GitHub `_ +* ``examples/deployment/aws/ec2-docker/README.md`` + +---------- +Quick Start +---------- + +.. code-block:: bash + + cd examples/deployment/aws/ec2-docker + docker build -t burr-ec2-example . + docker run -p 8000:8000 burr-ec2-example + curl -X POST http://localhost:8000/run -H "Content-Type: application/json" -d '{"number": 5}' + +----------- +Deploy to AWS +----------- + +.. code-block:: bash + + cd terraform + terraform init + terraform apply -var-file=dev.tfvars + +-------------- +Related Guides +-------------- + +* :ref:`S3 Tracking and AWS ` +* :ref:`AWS Deployment overview ` diff --git a/docs/examples/deployment/index.rst b/docs/examples/deployment/index.rst index 3d208081b..af5d263f6 100644 --- a/docs/examples/deployment/index.rst +++ b/docs/examples/deployment/index.rst @@ -41,3 +41,4 @@ have an example that would add to this guide. We have created a variety of issue infrastructure monitoring aws + ec2-docker diff --git a/examples/deployment/aws/ec2-docker/Dockerfile b/examples/deployment/aws/ec2-docker/Dockerfile new file mode 100644 index 000000000..3e7d89f71 --- /dev/null +++ b/examples/deployment/aws/ec2-docker/Dockerfile @@ -0,0 +1,29 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +FROM python:3.11-slim + +WORKDIR /app + +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY app/ ./app/ + +EXPOSE 8000 + +CMD ["python", "-m", "app.server"] diff --git a/examples/deployment/aws/ec2-docker/README.md b/examples/deployment/aws/ec2-docker/README.md new file mode 100644 index 000000000..d67ca498d --- /dev/null +++ b/examples/deployment/aws/ec2-docker/README.md @@ -0,0 +1,156 @@ + + +# Deploy Burr on AWS EC2 with Docker + +Deploy a containerized Burr application on an AWS EC2 instance, provisioned with Terraform. + +## Overview + +This example deploys: +- A FastAPI server wrapping a simple Burr counter application +- Running inside a Docker container on an EC2 instance +- Infrastructure provisioned via Terraform (VPC default, security group, EC2) + +## Prerequisites + +- AWS CLI configured with credentials +- Terraform >= 1.5 installed +- Docker installed (for local testing) +- An EC2 key pair created in your target region + +## Quick Start (Local) + +Build and run locally without any AWS resources: + +```bash +cd examples/deployment/aws/ec2-docker + +docker build -t burr-ec2-example . +docker run -p 8000:8000 burr-ec2-example +``` + +Test the endpoints: + +```bash +# Health check +curl http://localhost:8000/health + +# Run the counter app (counts up to 5) +curl -X POST http://localhost:8000/run \ + -H "Content-Type: application/json" \ + -d '{"number": 5}' +``` + +Alternatively, use Docker Compose: + +```bash +docker compose up --build +``` + +## Deploy to AWS + +1. Navigate to the Terraform directory: + +```bash +cd terraform +``` + +2. Copy `dev.tfvars` and set your values: + +```bash +cp dev.tfvars my.tfvars +``` + +Edit `my.tfvars` and set: +- `key_name` — your EC2 key pair name +- `allowed_cidr` — your IP address in CIDR notation (e.g. `203.0.113.50/32`) + +3. Initialize and apply: + +```bash +terraform init +terraform apply -var-file=my.tfvars +``` + +4. Wait approximately 2-3 minutes for the instance to boot, install Docker, and start the container. + +5. Access the application: + +```bash +# Get the URL +terraform output app_url + +# Test it +curl http://:8000/health +curl -X POST http://:8000/run -H "Content-Type: application/json" -d '{"number": 3}' +``` + +## How it Works + +- Terraform provisions a security group and EC2 instance +- The `user_data.sh` script runs on first boot: + - Installs Docker and Docker Compose + - Clones the repository + - Builds the Docker image + - Runs the container with `--restart=unless-stopped` +- The app exposes port 8000 (configurable via `app_port` variable) + +## Verify the Deployment + +```bash +# Health check +curl http://:8000/health +# Expected: {"status": "ok"} + +# Run counter +curl -X POST http://:8000/run \ + -H "Content-Type: application/json" \ + -d '{"number": 3}' +# Expected: {"counter": 3, "counter_limit": 3} +``` + +## Teardown + +```bash +terraform destroy -var-file=my.tfvars +``` + +## Security + +- Ingress rules use `var.allowed_cidr` — you must set this to your IP. Do not use `0.0.0.0/0`. +- The Burr tracking UI is **not exposed** in this example. If you add it, place it behind + authentication (e.g. ALB with Cognito) or restrict access to a VPN/private subnet. +- For production, use HTTPS via ACM + Application Load Balancer. +- Do not commit `.tfstate` files — they are gitignored. + +## Troubleshooting + +| Problem | Solution | +|---------|----------| +| Instance not reachable | Check that `allowed_cidr` matches your current IP | +| App not starting | SSH in and check `docker logs burr-app` | +| Terraform state conflicts | Do not share `.tfstate` between developers | + +## Production Notes + +- For persistent state across container restarts, add a volume mount or use + [S3 tracking](https://burr.apache.org/concepts/aws-tracking/) +- Consider an Auto Scaling Group for high availability +- Use a private subnet + NAT gateway for instances that don't need direct public access diff --git a/examples/deployment/aws/ec2-docker/app/__init__.py b/examples/deployment/aws/ec2-docker/app/__init__.py new file mode 100644 index 000000000..13a83393a --- /dev/null +++ b/examples/deployment/aws/ec2-docker/app/__init__.py @@ -0,0 +1,16 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. diff --git a/examples/deployment/aws/ec2-docker/app/counter_app.py b/examples/deployment/aws/ec2-docker/app/counter_app.py new file mode 100644 index 000000000..6e9fc8786 --- /dev/null +++ b/examples/deployment/aws/ec2-docker/app/counter_app.py @@ -0,0 +1,63 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +""" +Simple counting application for the EC2 + Docker deployment example. + +Reuses the same counter pattern from the Lambda example to demonstrate +deploying a Burr application as a long-running HTTP service. +""" + +import time + +import burr.core +from burr.core import Application, Result, State, default, expr +from burr.core.action import action +from burr.core.graph import GraphBuilder + + +@action(reads=["counter"], writes=["counter"]) +def counter(state: State) -> State: + result = {"counter": state["counter"] + 1} + time.sleep(0.5) # simulate a longer-running computation + return state.update(**result) + + +graph = ( + GraphBuilder() + .with_actions(counter=counter, result=Result("counter")) + .with_transitions( + ("counter", "counter", expr("counter < counter_limit")), + ("counter", "result", default), + ) + .build() +) + + +def application(count_up_to: int = 10) -> Application: + """Create and return a Burr counter application. + + :param count_up_to: The target counter value. + :return: A configured Burr Application instance. + """ + return ( + burr.core.ApplicationBuilder() + .with_graph(graph) + .with_state(**{"counter": 0, "counter_limit": count_up_to}) + .with_entrypoint("counter") + .build() + ) diff --git a/examples/deployment/aws/ec2-docker/app/server.py b/examples/deployment/aws/ec2-docker/app/server.py new file mode 100644 index 000000000..ac0f6f0ee --- /dev/null +++ b/examples/deployment/aws/ec2-docker/app/server.py @@ -0,0 +1,53 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +"""HTTP server for the Burr counter application. + +Exposes: +- POST /run -- run the counter app with a given target number +- GET /health -- health check endpoint for load balancers and monitoring +""" + +import uvicorn +from fastapi import FastAPI +from pydantic import BaseModel + +from app.counter_app import application + +app = FastAPI(title="Burr EC2 Docker Example") + + +class RunRequest(BaseModel): + number: int = 5 + + +@app.post("/run") +def run_counter(req: RunRequest): + """Run the counter application up to the requested number.""" + burr_app = application(req.number) + _, _, state = burr_app.run(halt_after=["result"]) + return state.serialize() + + +@app.get("/health") +def health(): + """Health check endpoint.""" + return {"status": "ok"} + + +if __name__ == "__main__": + uvicorn.run("app.server:app", host="0.0.0.0", port=8000) diff --git a/examples/deployment/aws/ec2-docker/docker-compose.yml b/examples/deployment/aws/ec2-docker/docker-compose.yml new file mode 100644 index 000000000..c605d1f57 --- /dev/null +++ b/examples/deployment/aws/ec2-docker/docker-compose.yml @@ -0,0 +1,24 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +services: + burr-app: + build: . + ports: + - "8000:8000" + environment: + - BURR_HOST=0.0.0.0 diff --git a/examples/deployment/aws/ec2-docker/requirements.txt b/examples/deployment/aws/ec2-docker/requirements.txt new file mode 100644 index 000000000..44a8c12ee --- /dev/null +++ b/examples/deployment/aws/ec2-docker/requirements.txt @@ -0,0 +1,20 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +burr[start] +fastapi==0.115.6 +uvicorn[standard]==0.34.0 diff --git a/examples/deployment/aws/ec2-docker/terraform/.gitignore b/examples/deployment/aws/ec2-docker/terraform/.gitignore new file mode 100644 index 000000000..bfaac3762 --- /dev/null +++ b/examples/deployment/aws/ec2-docker/terraform/.gitignore @@ -0,0 +1,12 @@ +# Terraform +*.tfstate +*.tfstate.backup +*.tfplan +.terraform/ +.terraform.lock.hcl +crash.log +override.tf +override.tf.json +*_override.tf +*_override.tf.json +*.auto.tfvars diff --git a/examples/deployment/aws/ec2-docker/terraform/environments/dev.tfvars b/examples/deployment/aws/ec2-docker/terraform/environments/dev.tfvars new file mode 100644 index 000000000..91b769cc8 --- /dev/null +++ b/examples/deployment/aws/ec2-docker/terraform/environments/dev.tfvars @@ -0,0 +1,30 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Development environment configuration +# Copy this file and set the REQUIRED values before running terraform apply. + +aws_region = "us-east-1" +environment = "dev" +instance_type = "t3.micro" +app_port = 8000 +enable_monitoring = false +root_volume_size = 20 + +# REQUIRED — uncomment and set before use: +# key_name = "my-ec2-keypair" +# allowed_cidr = "203.0.113.50/32" diff --git a/examples/deployment/aws/ec2-docker/terraform/environments/prod.tfvars b/examples/deployment/aws/ec2-docker/terraform/environments/prod.tfvars new file mode 100644 index 000000000..2e10d7960 --- /dev/null +++ b/examples/deployment/aws/ec2-docker/terraform/environments/prod.tfvars @@ -0,0 +1,29 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Production environment configuration + +aws_region = "us-east-1" +environment = "prod" +instance_type = "t3.small" +app_port = 8000 +enable_monitoring = true +root_volume_size = 30 + +# REQUIRED — uncomment and set before use: +# key_name = "prod-ec2-keypair" +# allowed_cidr = "10.0.0.0/16" diff --git a/examples/deployment/aws/ec2-docker/terraform/main.tf b/examples/deployment/aws/ec2-docker/terraform/main.tf new file mode 100644 index 000000000..e90e87bb9 --- /dev/null +++ b/examples/deployment/aws/ec2-docker/terraform/main.tf @@ -0,0 +1,52 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +provider "aws" { + region = var.aws_region + + default_tags { + tags = local.common_tags + } +} + +locals { + common_tags = { + Project = "burr-ec2-docker" + Environment = var.environment + ManagedBy = "terraform" + } +} + +module "networking" { + source = "./modules/networking" + + environment = var.environment + app_port = var.app_port + allowed_cidr = var.allowed_cidr +} + +module "compute" { + source = "./modules/compute" + + environment = var.environment + instance_type = var.instance_type + key_name = var.key_name + app_port = var.app_port + security_group_id = module.networking.security_group_id + enable_monitoring = var.enable_monitoring + root_volume_size = var.root_volume_size +} diff --git a/examples/deployment/aws/ec2-docker/terraform/modules/compute/main.tf b/examples/deployment/aws/ec2-docker/terraform/modules/compute/main.tf new file mode 100644 index 000000000..c51c1335e --- /dev/null +++ b/examples/deployment/aws/ec2-docker/terraform/modules/compute/main.tf @@ -0,0 +1,73 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# --- Data Sources --- + +data "aws_ami" "amazon_linux" { + most_recent = true + owners = ["amazon"] + + filter { + name = "name" + values = ["al2023-ami-*-x86_64"] + } + + filter { + name = "virtualization-type" + values = ["hvm"] + } + + filter { + name = "state" + values = ["available"] + } +} + +# --- EC2 Instance --- + +resource "aws_instance" "this" { + ami = data.aws_ami.amazon_linux.id + instance_type = var.instance_type + key_name = var.key_name + vpc_security_group_ids = [var.security_group_id] + monitoring = var.enable_monitoring + + user_data = templatefile("${path.module}/templates/user_data.sh.tpl", { + app_port = var.app_port + }) + + root_block_device { + volume_type = "gp3" + volume_size = var.root_volume_size + encrypted = true + delete_on_termination = true + } + + metadata_options { + http_endpoint = "enabled" + http_tokens = "required" # IMDSv2 enforced + http_put_response_hop_limit = 1 + } + + tags = { + Name = "burr-ec2-${var.environment}" + } + + lifecycle { + ignore_changes = [ami] # Don't recreate on AMI updates + } +} diff --git a/examples/deployment/aws/ec2-docker/terraform/modules/compute/outputs.tf b/examples/deployment/aws/ec2-docker/terraform/modules/compute/outputs.tf new file mode 100644 index 000000000..159fe6a16 --- /dev/null +++ b/examples/deployment/aws/ec2-docker/terraform/modules/compute/outputs.tf @@ -0,0 +1,31 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +output "instance_id" { + description = "EC2 instance ID" + value = aws_instance.this.id +} + +output "public_ip" { + description = "Public IP address of the EC2 instance" + value = aws_instance.this.public_ip +} + +output "private_ip" { + description = "Private IP address of the EC2 instance" + value = aws_instance.this.private_ip +} diff --git a/examples/deployment/aws/ec2-docker/terraform/modules/compute/templates/user_data.sh.tpl b/examples/deployment/aws/ec2-docker/terraform/modules/compute/templates/user_data.sh.tpl new file mode 100644 index 000000000..39276fb4d --- /dev/null +++ b/examples/deployment/aws/ec2-docker/terraform/modules/compute/templates/user_data.sh.tpl @@ -0,0 +1,52 @@ +#!/bin/bash +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +set -euo pipefail + +exec > >(tee /var/log/user-data.log) 2>&1 +echo "=== Burr EC2 Docker setup started at $(date) ===" + +# Install Docker +yum update -y +yum install -y docker git +systemctl enable docker +systemctl start docker +usermod -aG docker ec2-user + +# Install Docker Compose plugin +mkdir -p /usr/local/lib/docker/cli-plugins +curl -fsSL "https://github.com/docker/compose/releases/latest/download/docker-compose-linux-x86_64" \ + -o /usr/local/lib/docker/cli-plugins/docker-compose +chmod +x /usr/local/lib/docker/cli-plugins/docker-compose + +# Clone the repository and build +git clone --depth 1 https://github.com/apache/burr.git /opt/burr +cd /opt/burr/examples/deployment/aws/ec2-docker + +docker build -t burr-ec2-app . + +# Run the container with restart policy and resource limits +docker run -d \ + --restart=unless-stopped \ + --name burr-app \ + --memory=512m \ + --cpus=1.0 \ + -p ${app_port}:8000 \ + burr-ec2-app + +echo "=== Burr EC2 Docker setup completed at $(date) ===" diff --git a/examples/deployment/aws/ec2-docker/terraform/modules/compute/variables.tf b/examples/deployment/aws/ec2-docker/terraform/modules/compute/variables.tf new file mode 100644 index 000000000..ea1b1cc86 --- /dev/null +++ b/examples/deployment/aws/ec2-docker/terraform/modules/compute/variables.tf @@ -0,0 +1,53 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +variable "environment" { + description = "Environment name" + type = string +} + +variable "instance_type" { + description = "EC2 instance type" + type = string +} + +variable "key_name" { + description = "EC2 key pair name" + type = string +} + +variable "app_port" { + description = "Application port" + type = number +} + +variable "security_group_id" { + description = "Security group ID to attach to the instance" + type = string +} + +variable "enable_monitoring" { + description = "Enable detailed CloudWatch monitoring" + type = bool + default = false +} + +variable "root_volume_size" { + description = "Root EBS volume size in GB" + type = number + default = 20 +} diff --git a/examples/deployment/aws/ec2-docker/terraform/modules/networking/main.tf b/examples/deployment/aws/ec2-docker/terraform/modules/networking/main.tf new file mode 100644 index 000000000..f6c8c07ad --- /dev/null +++ b/examples/deployment/aws/ec2-docker/terraform/modules/networking/main.tf @@ -0,0 +1,59 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +resource "aws_security_group" "this" { + name = "burr-ec2-${var.environment}-sg" + description = "Security group for Burr EC2 Docker deployment (${var.environment})" + + tags = { + Name = "burr-ec2-${var.environment}-sg" + } + + lifecycle { + create_before_destroy = true + } +} + +resource "aws_security_group_rule" "app_ingress" { + type = "ingress" + description = "Application port from allowed CIDR" + from_port = var.app_port + to_port = var.app_port + protocol = "tcp" + cidr_blocks = [var.allowed_cidr] + security_group_id = aws_security_group.this.id +} + +resource "aws_security_group_rule" "ssh_ingress" { + type = "ingress" + description = "SSH from allowed CIDR" + from_port = 22 + to_port = 22 + protocol = "tcp" + cidr_blocks = [var.allowed_cidr] + security_group_id = aws_security_group.this.id +} + +resource "aws_security_group_rule" "egress_all" { + type = "egress" + description = "Allow all outbound traffic" + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + security_group_id = aws_security_group.this.id +} diff --git a/examples/deployment/aws/ec2-docker/terraform/modules/networking/outputs.tf b/examples/deployment/aws/ec2-docker/terraform/modules/networking/outputs.tf new file mode 100644 index 000000000..02ba7783c --- /dev/null +++ b/examples/deployment/aws/ec2-docker/terraform/modules/networking/outputs.tf @@ -0,0 +1,21 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +output "security_group_id" { + description = "ID of the created security group" + value = aws_security_group.this.id +} diff --git a/examples/deployment/aws/ec2-docker/terraform/modules/networking/variables.tf b/examples/deployment/aws/ec2-docker/terraform/modules/networking/variables.tf new file mode 100644 index 000000000..9544e76b1 --- /dev/null +++ b/examples/deployment/aws/ec2-docker/terraform/modules/networking/variables.tf @@ -0,0 +1,31 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +variable "environment" { + description = "Environment name" + type = string +} + +variable "app_port" { + description = "Application port to allow ingress" + type = number +} + +variable "allowed_cidr" { + description = "CIDR block allowed for ingress" + type = string +} diff --git a/examples/deployment/aws/ec2-docker/terraform/outputs.tf b/examples/deployment/aws/ec2-docker/terraform/outputs.tf new file mode 100644 index 000000000..dab04b3af --- /dev/null +++ b/examples/deployment/aws/ec2-docker/terraform/outputs.tf @@ -0,0 +1,41 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +output "instance_id" { + description = "EC2 instance ID" + value = module.compute.instance_id +} + +output "public_ip" { + description = "Public IP address of the EC2 instance" + value = module.compute.public_ip +} + +output "app_url" { + description = "URL to access the Burr application" + value = "http://${module.compute.public_ip}:${var.app_port}" +} + +output "ssh_command" { + description = "SSH command to connect to the instance" + value = "ssh -i ${var.key_name}.pem ec2-user@${module.compute.public_ip}" +} + +output "security_group_id" { + description = "Security group ID" + value = module.networking.security_group_id +} diff --git a/examples/deployment/aws/ec2-docker/terraform/variables.tf b/examples/deployment/aws/ec2-docker/terraform/variables.tf new file mode 100644 index 000000000..5a46cda44 --- /dev/null +++ b/examples/deployment/aws/ec2-docker/terraform/variables.tf @@ -0,0 +1,95 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# ----------------------------------------------------------------------------- +# Required Variables +# ----------------------------------------------------------------------------- + +variable "key_name" { + description = "Name of an existing EC2 key pair for SSH access" + type = string +} + +variable "allowed_cidr" { + description = "CIDR block allowed to access the app (e.g. your IP/32). Do NOT use 0.0.0.0/0." + type = string + + validation { + condition = var.allowed_cidr != "0.0.0.0/0" + error_message = "allowed_cidr must not be 0.0.0.0/0. Restrict to your IP (e.g. 203.0.113.50/32)." + } + + validation { + condition = can(cidrhost(var.allowed_cidr, 0)) + error_message = "allowed_cidr must be a valid CIDR block (e.g. 203.0.113.50/32)." + } +} + +# ----------------------------------------------------------------------------- +# Optional Variables +# ----------------------------------------------------------------------------- + +variable "aws_region" { + description = "AWS region to deploy in" + type = string + default = "us-east-1" +} + +variable "environment" { + description = "Environment name (dev, staging, prod)" + type = string + default = "dev" + + validation { + condition = contains(["dev", "staging", "prod"], var.environment) + error_message = "environment must be one of: dev, staging, prod." + } +} + +variable "instance_type" { + description = "EC2 instance type" + type = string + default = "t3.micro" +} + +variable "app_port" { + description = "Port the Burr app server listens on" + type = number + default = 8000 + + validation { + condition = var.app_port > 0 && var.app_port < 65536 + error_message = "app_port must be between 1 and 65535." + } +} + +variable "enable_monitoring" { + description = "Enable detailed CloudWatch monitoring for the EC2 instance" + type = bool + default = false +} + +variable "root_volume_size" { + description = "Size of the root EBS volume in GB" + type = number + default = 20 + + validation { + condition = var.root_volume_size >= 8 + error_message = "root_volume_size must be at least 8 GB." + } +} diff --git a/examples/deployment/aws/ec2-docker/terraform/versions.tf b/examples/deployment/aws/ec2-docker/terraform/versions.tf new file mode 100644 index 000000000..877bfc7cd --- /dev/null +++ b/examples/deployment/aws/ec2-docker/terraform/versions.tf @@ -0,0 +1,27 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +terraform { + required_version = ">= 1.5" + + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } +} diff --git a/tests/deployment/__init__.py b/tests/deployment/__init__.py new file mode 100644 index 000000000..13a83393a --- /dev/null +++ b/tests/deployment/__init__.py @@ -0,0 +1,16 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. diff --git a/tests/deployment/test_ec2_docker_example.py b/tests/deployment/test_ec2_docker_example.py new file mode 100644 index 000000000..031b90770 --- /dev/null +++ b/tests/deployment/test_ec2_docker_example.py @@ -0,0 +1,179 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +"""Tests for the EC2 + Docker deployment example. + +These validate the example is well-formed and the app runs locally. +They do NOT provision real AWS infrastructure. +""" + +import sys +from pathlib import Path + +import pytest + +EXAMPLE_DIR = Path(__file__).parents[2] / "examples" / "deployment" / "aws" / "ec2-docker" + + +class TestExampleStructure: + """Validate the example has all required files.""" + + def test_readme_exists(self): + assert (EXAMPLE_DIR / "README.md").is_file() + + def test_dockerfile_exists(self): + assert (EXAMPLE_DIR / "Dockerfile").is_file() + + def test_requirements_exists(self): + assert (EXAMPLE_DIR / "requirements.txt").is_file() + + def test_docker_compose_exists(self): + assert (EXAMPLE_DIR / "docker-compose.yml").is_file() + + def test_counter_app_exists(self): + assert (EXAMPLE_DIR / "app" / "counter_app.py").is_file() + + def test_server_exists(self): + assert (EXAMPLE_DIR / "app" / "server.py").is_file() + + def test_terraform_main_exists(self): + assert (EXAMPLE_DIR / "terraform" / "main.tf").is_file() + + def test_terraform_versions_exists(self): + assert (EXAMPLE_DIR / "terraform" / "versions.tf").is_file() + + def test_terraform_variables_exists(self): + assert (EXAMPLE_DIR / "terraform" / "variables.tf").is_file() + + def test_terraform_outputs_exists(self): + assert (EXAMPLE_DIR / "terraform" / "outputs.tf").is_file() + + def test_networking_module_exists(self): + assert (EXAMPLE_DIR / "terraform" / "modules" / "networking" / "main.tf").is_file() + assert (EXAMPLE_DIR / "terraform" / "modules" / "networking" / "variables.tf").is_file() + assert (EXAMPLE_DIR / "terraform" / "modules" / "networking" / "outputs.tf").is_file() + + def test_compute_module_exists(self): + assert (EXAMPLE_DIR / "terraform" / "modules" / "compute" / "main.tf").is_file() + assert (EXAMPLE_DIR / "terraform" / "modules" / "compute" / "variables.tf").is_file() + assert (EXAMPLE_DIR / "terraform" / "modules" / "compute" / "outputs.tf").is_file() + + def test_user_data_template_exists(self): + assert (EXAMPLE_DIR / "terraform" / "modules" / "compute" / "templates" / "user_data.sh.tpl").is_file() + + def test_environment_tfvars_exist(self): + assert (EXAMPLE_DIR / "terraform" / "environments" / "dev.tfvars").is_file() + assert (EXAMPLE_DIR / "terraform" / "environments" / "prod.tfvars").is_file() + + +class TestApacheLicenseHeaders: + """Every file must have the Apache license header.""" + + @pytest.mark.parametrize( + "filename", + [ + "README.md", + "Dockerfile", + "requirements.txt", + "docker-compose.yml", + "app/__init__.py", + "app/counter_app.py", + "app/server.py", + "terraform/main.tf", + "terraform/versions.tf", + "terraform/variables.tf", + "terraform/outputs.tf", + "terraform/environments/dev.tfvars", + "terraform/environments/prod.tfvars", + "terraform/modules/networking/main.tf", + "terraform/modules/networking/variables.tf", + "terraform/modules/networking/outputs.tf", + "terraform/modules/compute/main.tf", + "terraform/modules/compute/variables.tf", + "terraform/modules/compute/outputs.tf", + "terraform/modules/compute/templates/user_data.sh.tpl", + ], + ) + def test_file_has_license_header(self, filename): + path = EXAMPLE_DIR / filename + if path.exists(): + content = path.read_text() + assert "Licensed to the Apache Software Foundation" in content, ( + f"{filename} is missing the Apache license header" + ) + + +class TestAppRunsLocally: + """The counter app logic works without infrastructure.""" + + def test_counter_app_import_and_run(self): + sys.path.insert(0, str(EXAMPLE_DIR)) + try: + from app.counter_app import application + + app = application(3) + _, _, state = app.run(halt_after=["result"]) + assert state["counter"] == 3 + finally: + sys.path.pop(0) + + def test_counter_app_default(self): + sys.path.insert(0, str(EXAMPLE_DIR)) + try: + from app.counter_app import application + + app = application() + _, _, state = app.run(halt_after=["result"]) + assert state["counter"] == 10 + finally: + sys.path.pop(0) + + +class TestSecurityGroup: + """Terraform does not open ingress to 0.0.0.0/0.""" + + def test_no_open_cidr_in_ingress_rules(self): + """Networking module must not have 0.0.0.0/0 in ingress rules.""" + net_main = (EXAMPLE_DIR / "terraform" / "modules" / "networking" / "main.tf").read_text() + # Only the egress rule should reference 0.0.0.0/0 + for line in net_main.splitlines(): + if "0.0.0.0/0" in line: + # Verify it's in the egress resource, not ingress + assert "egress" in line or "egress_all" in net_main[ + max(0, net_main.index(line.strip()) - 200):net_main.index(line.strip()) + ], f"Found 0.0.0.0/0 in non-egress context: {line.strip()}" + + def test_allowed_cidr_validation_exists(self): + """Root variables.tf must validate that allowed_cidr is not 0.0.0.0/0.""" + vars_tf = (EXAMPLE_DIR / "terraform" / "variables.tf").read_text() + assert '!= "0.0.0.0/0"' in vars_tf, ( + "variables.tf must have a validation preventing 0.0.0.0/0" + ) + + def test_imdsv2_enforced(self): + """Compute module must enforce IMDSv2 (http_tokens = required).""" + compute_main = (EXAMPLE_DIR / "terraform" / "modules" / "compute" / "main.tf").read_text() + assert 'http_tokens' in compute_main and 'required' in compute_main, ( + "Compute module must enforce IMDSv2 with http_tokens = required" + ) + + def test_ebs_encryption_enabled(self): + """Root volume must be encrypted.""" + compute_main = (EXAMPLE_DIR / "terraform" / "modules" / "compute" / "main.tf").read_text() + assert "encrypted" in compute_main and "true" in compute_main, ( + "Root EBS volume must have encrypted = true" + ) From 91489c82b54144f544ec9b09834e1cbe1627f6aa Mon Sep 17 00:00:00 2001 From: vaquarkhan Date: Sun, 28 Jun 2026 01:37:53 -0500 Subject: [PATCH 2/4] fix(docs): extend RST heading underlines to match title length --- docs/examples/deployment/ec2-docker.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/examples/deployment/ec2-docker.rst b/docs/examples/deployment/ec2-docker.rst index deb10d787..334618115 100644 --- a/docs/examples/deployment/ec2-docker.rst +++ b/docs/examples/deployment/ec2-docker.rst @@ -30,9 +30,9 @@ See the full example and step-by-step guide: * Source: `GitHub `_ * ``examples/deployment/aws/ec2-docker/README.md`` ----------- +----------- Quick Start ----------- +----------- .. code-block:: bash @@ -41,9 +41,9 @@ Quick Start docker run -p 8000:8000 burr-ec2-example curl -X POST http://localhost:8000/run -H "Content-Type: application/json" -d '{"number": 5}' ------------ +------------- Deploy to AWS ------------ +------------- .. code-block:: bash From e5469233c9819b09bc3e877cc33633af3c5f1330 Mon Sep 17 00:00:00 2001 From: vaquarkhan Date: Sun, 28 Jun 2026 01:57:19 -0500 Subject: [PATCH 3/4] fix(docs): correct RST cross-reference labels to match existing targets --- docs/examples/deployment/ec2-docker.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/examples/deployment/ec2-docker.rst b/docs/examples/deployment/ec2-docker.rst index 334618115..1f8b7aedd 100644 --- a/docs/examples/deployment/ec2-docker.rst +++ b/docs/examples/deployment/ec2-docker.rst @@ -55,5 +55,5 @@ Deploy to AWS Related Guides -------------- -* :ref:`S3 Tracking and AWS ` -* :ref:`AWS Deployment overview ` +* :ref:`S3 Tracking on AWS ` +* :ref:`AWS Deployment overview ` From 05d4ad777aa61a2ea200098fd94d22f890480954 Mon Sep 17 00:00:00 2001 From: vaquarkhan Date: Sun, 28 Jun 2026 02:06:14 -0500 Subject: [PATCH 4/4] fix: add pre-merge note for user_data clone path, verify terraform fmt compliance --- examples/deployment/aws/ec2-docker/README.md | 4 ++++ .../deployment/aws/ec2-docker/terraform/main.tf | 14 +++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/examples/deployment/aws/ec2-docker/README.md b/examples/deployment/aws/ec2-docker/README.md index d67ca498d..37b18464b 100644 --- a/examples/deployment/aws/ec2-docker/README.md +++ b/examples/deployment/aws/ec2-docker/README.md @@ -112,6 +112,10 @@ curl -X POST http://:8000/run -H "Content-Type: application/json" -d - Runs the container with `--restart=unless-stopped` - The app exposes port 8000 (configurable via `app_port` variable) +> **Note:** The user_data script clones from `apache/burr` main branch. This path only +> works once this example is merged to main. For pre-merge testing on EC2, edit +> `terraform/modules/compute/templates/user_data.sh.tpl` to point at your fork and branch. + ## Verify the Deployment ```bash diff --git a/examples/deployment/aws/ec2-docker/terraform/main.tf b/examples/deployment/aws/ec2-docker/terraform/main.tf index e90e87bb9..8029f5d1e 100644 --- a/examples/deployment/aws/ec2-docker/terraform/main.tf +++ b/examples/deployment/aws/ec2-docker/terraform/main.tf @@ -42,11 +42,11 @@ module "networking" { module "compute" { source = "./modules/compute" - environment = var.environment - instance_type = var.instance_type - key_name = var.key_name - app_port = var.app_port - security_group_id = module.networking.security_group_id - enable_monitoring = var.enable_monitoring - root_volume_size = var.root_volume_size + environment = var.environment + instance_type = var.instance_type + key_name = var.key_name + app_port = var.app_port + security_group_id = module.networking.security_group_id + enable_monitoring = var.enable_monitoring + root_volume_size = var.root_volume_size }