From ad5a45b25973feca77782200e2f1cac05b7f42b3 Mon Sep 17 00:00:00 2001 From: vaquarkhan Date: Mon, 29 Jun 2026 22:41:45 -0500 Subject: [PATCH 1/2] docs(deployments): add Burr UI on AWS deployment example (#391) Deploy the Burr tracking UI server in a private VPC on AWS: - Single EC2 instance in private subnet (single-tenant, per issue requirements) - SSM Session Manager for private access (no public IP, no open ports) - Parameterizable on S3 bucket (bring-your-own-bucket) - Read-only IAM scoped to the single bucket ARN - VPC with NAT gateway + SSM VPC endpoints - IMDSv2 enforced, encrypted gp3 EBS - Systemd service for automatic restart - 45 automated tests (structure, license, security, terraform fmt/validate) - Full README with architecture diagram, SSM access, troubleshooting - RST docs page wired into deployment toctree Addresses #391 --- docs/examples/deployment/burr-ui-aws.rst | 62 ++++++ docs/examples/deployment/index.rst | 1 + examples/deployment/aws/burr-ui/Dockerfile | 28 +++ examples/deployment/aws/burr-ui/README.md | 147 ++++++++++++++ .../deployment/aws/burr-ui/requirements.txt | 18 ++ .../aws/burr-ui/terraform/.gitignore | 11 + .../burr-ui/terraform/environments/dev.tfvars | 24 +++ .../terraform/environments/prod.tfvars | 24 +++ .../deployment/aws/burr-ui/terraform/main.tf | 59 ++++++ .../burr-ui/terraform/modules/compute/main.tf | 69 +++++++ .../terraform/modules/compute/outputs.tf | 26 +++ .../compute/templates/user_data.sh.tpl | 53 +++++ .../terraform/modules/compute/variables.tf | 51 +++++ .../aws/burr-ui/terraform/modules/iam/main.tf | 70 +++++++ .../burr-ui/terraform/modules/iam/outputs.tf | 26 +++ .../terraform/modules/iam/variables.tf | 26 +++ .../terraform/modules/networking/main.tf | 191 ++++++++++++++++++ .../terraform/modules/networking/outputs.tf | 31 +++ .../terraform/modules/networking/variables.tf | 31 +++ .../aws/burr-ui/terraform/outputs.tf | 41 ++++ .../aws/burr-ui/terraform/variables.tf | 69 +++++++ .../aws/burr-ui/terraform/versions.tf | 27 +++ tests/deployment/test_burr_ui_example.py | 163 +++++++++++++++ 23 files changed, 1248 insertions(+) create mode 100644 docs/examples/deployment/burr-ui-aws.rst create mode 100644 examples/deployment/aws/burr-ui/Dockerfile create mode 100644 examples/deployment/aws/burr-ui/README.md create mode 100644 examples/deployment/aws/burr-ui/requirements.txt create mode 100644 examples/deployment/aws/burr-ui/terraform/.gitignore create mode 100644 examples/deployment/aws/burr-ui/terraform/environments/dev.tfvars create mode 100644 examples/deployment/aws/burr-ui/terraform/environments/prod.tfvars create mode 100644 examples/deployment/aws/burr-ui/terraform/main.tf create mode 100644 examples/deployment/aws/burr-ui/terraform/modules/compute/main.tf create mode 100644 examples/deployment/aws/burr-ui/terraform/modules/compute/outputs.tf create mode 100644 examples/deployment/aws/burr-ui/terraform/modules/compute/templates/user_data.sh.tpl create mode 100644 examples/deployment/aws/burr-ui/terraform/modules/compute/variables.tf create mode 100644 examples/deployment/aws/burr-ui/terraform/modules/iam/main.tf create mode 100644 examples/deployment/aws/burr-ui/terraform/modules/iam/outputs.tf create mode 100644 examples/deployment/aws/burr-ui/terraform/modules/iam/variables.tf create mode 100644 examples/deployment/aws/burr-ui/terraform/modules/networking/main.tf create mode 100644 examples/deployment/aws/burr-ui/terraform/modules/networking/outputs.tf create mode 100644 examples/deployment/aws/burr-ui/terraform/modules/networking/variables.tf create mode 100644 examples/deployment/aws/burr-ui/terraform/outputs.tf create mode 100644 examples/deployment/aws/burr-ui/terraform/variables.tf create mode 100644 examples/deployment/aws/burr-ui/terraform/versions.tf create mode 100644 tests/deployment/test_burr_ui_example.py diff --git a/docs/examples/deployment/burr-ui-aws.rst b/docs/examples/deployment/burr-ui-aws.rst new file mode 100644 index 000000000..eb2ce79a8 --- /dev/null +++ b/docs/examples/deployment/burr-ui-aws.rst @@ -0,0 +1,62 @@ +.. + 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-ui-aws-deployment: + +============================== +Burr UI on AWS (Private VPC) +============================== + +Deploy the Burr tracking UI server in a private VPC, reading from an existing S3 tracking bucket. +Access is via AWS SSM Session Manager port forwarding. + +See the full example: + +* Source: `GitHub `_ +* ``examples/deployment/aws/burr-ui/README.md`` + +----------- +Quick Start +----------- + +.. code-block:: bash + + cd examples/deployment/aws/burr-ui/terraform + terraform init + terraform apply -var="s3_bucket_name=my-tracking-bucket" + +-------------------------- +Access the Private UI +-------------------------- + +.. code-block:: bash + + INSTANCE_ID=$(terraform output -raw instance_id) + aws ssm start-session --target $INSTANCE_ID \ + --document-name AWS-StartPortForwardingSession \ + --parameters '{"portNumber":["7241"],"localPortNumber":["7241"]}' + +Then open http://localhost:7241. + +-------------- +Related Guides +-------------- + +* :ref:`S3 Tracking on AWS ` +* :ref:`AWS Deployment overview ` diff --git a/docs/examples/deployment/index.rst b/docs/examples/deployment/index.rst index 3d208081b..700e4828c 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 + burr-ui-aws diff --git a/examples/deployment/aws/burr-ui/Dockerfile b/examples/deployment/aws/burr-ui/Dockerfile new file mode 100644 index 000000000..2b7ff91f7 --- /dev/null +++ b/examples/deployment/aws/burr-ui/Dockerfile @@ -0,0 +1,28 @@ +# 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 + +EXPOSE 7241 + +# The S3 bucket is configured via environment variable at runtime +CMD ["burr", "--no-open", "--host", "0.0.0.0"] diff --git a/examples/deployment/aws/burr-ui/README.md b/examples/deployment/aws/burr-ui/README.md new file mode 100644 index 000000000..52a9f37aa --- /dev/null +++ b/examples/deployment/aws/burr-ui/README.md @@ -0,0 +1,147 @@ + + +# Deploy Burr UI on AWS (Private VPC, S3 Backend) + +Deploy the Burr tracking UI server in a private VPC on AWS, reading tracking data from +an existing S3 bucket. Access is via AWS SSM Session Manager port forwarding — no public +IP, no open inbound ports. + +## Overview + +This deploys a single-tenant Burr UI server that: + +- Runs in a private subnet inside a dedicated VPC +- Reads and indexes tracking logs from your existing S3 bucket +- Is accessible only via SSM port forwarding (no public internet exposure) +- Uses a single EC2 instance (not horizontally scaled — by design) + +``` +┌─────────────────────────────────────────────────────────┐ +│ VPC (10.0.0.0/16) │ +│ │ +│ ┌─────────────────────────────────────────────────┐ │ +│ │ Private Subnet │ │ +│ │ │ │ +│ │ ┌──────────────────────────┐ │ │ +│ │ │ EC2 (t3.small) │ │ │ +│ │ │ - burr --no-open │ │ │ +│ │ │ - port 7241 │ │ │ +│ │ │ - BURR_S3_BUCKET=... │──── reads ──── S3 │ │ +│ │ └──────────────────────────┘ │ │ +│ └─────────────────────────────────────────────────┘ │ +│ │ +│ SSM VPC Endpoints (ssm, ssmmessages, ec2messages) │ +│ NAT Gateway (outbound for pip install) │ +└─────────────────────────────────────────────────────────┘ + │ + │ SSM port-forward (your laptop) + ▼ + http://localhost:7241 +``` + +## Prerequisites + +- AWS CLI v2 configured with credentials +- [AWS Session Manager plugin](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html) installed +- Terraform >= 1.5 +- An existing S3 bucket with Burr tracking data (written by `S3TrackingClient`) + +## Configure + +```bash +cd examples/deployment/aws/burr-ui/terraform +cp environments/dev.tfvars my.tfvars +``` + +Edit `my.tfvars` and set: +- `s3_bucket_name` — your existing Burr tracking bucket name (required) +- `aws_region` — the region where your bucket lives (default: us-east-1) + +## Deploy + +```bash +terraform init +terraform apply -var-file=my.tfvars +``` + +Wait 2-3 minutes for the instance to boot, install Burr, and start indexing. + +## Access the Private UI + +Use SSM port forwarding (no SSH key needed, no open ports): + +```bash +# Get the instance ID from Terraform output +INSTANCE_ID=$(terraform output -raw instance_id) + +# Start the port forward +aws ssm start-session --target $INSTANCE_ID \ + --document-name AWS-StartPortForwardingSession \ + --parameters '{"portNumber":["7241"],"localPortNumber":["7241"]}' +``` + +Then open http://localhost:7241 in your browser. + +## Verify + +- The UI loads and shows a list of projects from your S3 bucket +- Navigate into a project to see tracked application runs +- If the bucket is empty, the UI shows an empty project list (no errors) + +## Teardown + +```bash +terraform destroy -var-file=my.tfvars +``` + +## Security + +- **No public IP.** The instance is in a private subnet with no inbound security group rules. +- **No SSH.** Access is exclusively via SSM Session Manager (port forwarding). +- **No open ports.** The security group allows egress only (for pip install and S3 access). +- **IMDSv2 enforced.** Instance metadata requires session tokens. +- **Encrypted EBS.** Root volume uses gp3 with encryption enabled. +- **Least-privilege IAM.** The instance role has read-only access to the single specified bucket. +- **VPC endpoints for SSM.** SSM control plane traffic stays within the VPC. + +## Architecture Decisions + +| Decision | Rationale | +|----------|-----------| +| Single EC2 instance | Issue #391 requests single-tenant. The UI is read-heavy and not write-bound. | +| SSM over bastion/ALB | Simplest private access. No key management, no extra instances, no public endpoints. | +| NAT Gateway | Required for pip install on boot. Could be replaced with VPC endpoint for S3 + pre-baked AMI. | +| No bucket creation | Issue says "parameterizable on the S3 bucket" — assumes an existing one. | + +## Troubleshooting + +| Problem | Solution | +|---------|----------| +| SSM can't connect | Ensure the SSM plugin is installed and the instance has `AmazonSSMManagedInstanceCore` policy | +| UI loads but no projects | Check that `s3_bucket_name` matches your tracking bucket and the region is correct | +| Server not starting | SSM into the instance: `sudo journalctl -u burr-ui` | +| Indexing errors | Check `/var/log/burr-ui-setup.log` on the instance | + +## Production Notes + +- For team access, place an internal ALB in front and connect via VPN +- For HTTPS, add an ACM certificate and ALB listener +- For persistent index across restarts, enable S3 snapshot (set `BURR_LOAD_SNAPSHOT_ON_START=true`) +- See [S3 Tracking on AWS](https://burr.apache.org/concepts/aws-tracking/) for all configuration options diff --git a/examples/deployment/aws/burr-ui/requirements.txt b/examples/deployment/aws/burr-ui/requirements.txt new file mode 100644 index 000000000..6c5f621df --- /dev/null +++ b/examples/deployment/aws/burr-ui/requirements.txt @@ -0,0 +1,18 @@ +# 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. + +apache-burr[tracking-server-s3,cli] diff --git a/examples/deployment/aws/burr-ui/terraform/.gitignore b/examples/deployment/aws/burr-ui/terraform/.gitignore new file mode 100644 index 000000000..1775b100d --- /dev/null +++ b/examples/deployment/aws/burr-ui/terraform/.gitignore @@ -0,0 +1,11 @@ +*.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/burr-ui/terraform/environments/dev.tfvars b/examples/deployment/aws/burr-ui/terraform/environments/dev.tfvars new file mode 100644 index 000000000..c642a0722 --- /dev/null +++ b/examples/deployment/aws/burr-ui/terraform/environments/dev.tfvars @@ -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. + +aws_region = "us-east-1" +environment = "dev" +instance_type = "t3.small" +root_volume_size = 20 + +# REQUIRED — set to your existing Burr tracking bucket: +# s3_bucket_name = "my-burr-tracking-logs" diff --git a/examples/deployment/aws/burr-ui/terraform/environments/prod.tfvars b/examples/deployment/aws/burr-ui/terraform/environments/prod.tfvars new file mode 100644 index 000000000..1cfc79bab --- /dev/null +++ b/examples/deployment/aws/burr-ui/terraform/environments/prod.tfvars @@ -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. + +aws_region = "us-east-1" +environment = "prod" +instance_type = "t3.small" +root_volume_size = 30 + +# REQUIRED — set to your existing Burr tracking bucket: +# s3_bucket_name = "my-burr-tracking-logs" diff --git a/examples/deployment/aws/burr-ui/terraform/main.tf b/examples/deployment/aws/burr-ui/terraform/main.tf new file mode 100644 index 000000000..993229c15 --- /dev/null +++ b/examples/deployment/aws/burr-ui/terraform/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. + +provider "aws" { + region = var.aws_region + + default_tags { + tags = local.common_tags + } +} + +locals { + common_tags = { + Project = "burr-ui-server" + Environment = var.environment + ManagedBy = "terraform" + } +} + +module "networking" { + source = "./modules/networking" + + environment = var.environment + vpc_cidr = var.vpc_cidr + aws_region = var.aws_region +} + +module "iam" { + source = "./modules/iam" + + environment = var.environment + s3_bucket_name = var.s3_bucket_name +} + +module "compute" { + source = "./modules/compute" + + environment = var.environment + instance_type = var.instance_type + root_volume_size = var.root_volume_size + s3_bucket_name = var.s3_bucket_name + subnet_id = module.networking.private_subnet_id + security_group_id = module.networking.security_group_id + instance_profile_arn = module.iam.instance_profile_arn +} diff --git a/examples/deployment/aws/burr-ui/terraform/modules/compute/main.tf b/examples/deployment/aws/burr-ui/terraform/modules/compute/main.tf new file mode 100644 index 000000000..feb419767 --- /dev/null +++ b/examples/deployment/aws/burr-ui/terraform/modules/compute/main.tf @@ -0,0 +1,69 @@ +# 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 "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"] + } +} + +resource "aws_instance" "this" { + ami = data.aws_ami.amazon_linux.id + instance_type = var.instance_type + subnet_id = var.subnet_id + vpc_security_group_ids = [var.security_group_id] + iam_instance_profile = var.instance_profile_arn + + user_data = templatefile("${path.module}/templates/user_data.sh.tpl", { + s3_bucket_name = var.s3_bucket_name + }) + + 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" + http_put_response_hop_limit = 1 + } + + tags = { + Name = "burr-ui-${var.environment}" + } + + lifecycle { + ignore_changes = [ami] + } +} diff --git a/examples/deployment/aws/burr-ui/terraform/modules/compute/outputs.tf b/examples/deployment/aws/burr-ui/terraform/modules/compute/outputs.tf new file mode 100644 index 000000000..084e752de --- /dev/null +++ b/examples/deployment/aws/burr-ui/terraform/modules/compute/outputs.tf @@ -0,0 +1,26 @@ +# 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 "private_ip" { + description = "Private IP address of the EC2 instance" + value = aws_instance.this.private_ip +} diff --git a/examples/deployment/aws/burr-ui/terraform/modules/compute/templates/user_data.sh.tpl b/examples/deployment/aws/burr-ui/terraform/modules/compute/templates/user_data.sh.tpl new file mode 100644 index 000000000..624b43f63 --- /dev/null +++ b/examples/deployment/aws/burr-ui/terraform/modules/compute/templates/user_data.sh.tpl @@ -0,0 +1,53 @@ +#!/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/burr-ui-setup.log) 2>&1 +echo "=== Burr UI server setup started at $(date) ===" + +# Install Python and pip +yum update -y +yum install -y python3.11 python3.11-pip + +# Install the Burr tracking server with S3 backend +python3.11 -m pip install "apache-burr[tracking-server-s3,cli]" + +# Create a systemd service for the Burr UI +cat > /etc/systemd/system/burr-ui.service <= 8 + error_message = "root_volume_size must be at least 8 GB." + } +} diff --git a/examples/deployment/aws/burr-ui/terraform/versions.tf b/examples/deployment/aws/burr-ui/terraform/versions.tf new file mode 100644 index 000000000..877bfc7cd --- /dev/null +++ b/examples/deployment/aws/burr-ui/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/test_burr_ui_example.py b/tests/deployment/test_burr_ui_example.py new file mode 100644 index 000000000..c6345158f --- /dev/null +++ b/tests/deployment/test_burr_ui_example.py @@ -0,0 +1,163 @@ +# 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 Burr UI on AWS deployment example. + +Validate structure and security without provisioning real infrastructure. +""" + +import subprocess +from pathlib import Path + +import pytest + +EXAMPLE_DIR = Path(__file__).parents[2] / "examples" / "deployment" / "aws" / "burr-ui" + + +class TestExampleStructure: + @pytest.mark.parametrize( + "rel", + [ + "README.md", + "Dockerfile", + "requirements.txt", + "terraform/main.tf", + "terraform/variables.tf", + "terraform/outputs.tf", + "terraform/versions.tf", + "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", + "terraform/modules/iam/main.tf", + "terraform/modules/iam/variables.tf", + "terraform/modules/iam/outputs.tf", + ], + ) + def test_required_file_exists(self, rel): + assert (EXAMPLE_DIR / rel).is_file(), f"missing {rel}" + + +class TestApacheLicenseHeaders: + @pytest.mark.parametrize( + "rel", + [ + "README.md", + "Dockerfile", + "requirements.txt", + "terraform/main.tf", + "terraform/variables.tf", + "terraform/outputs.tf", + "terraform/versions.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", + "terraform/modules/iam/main.tf", + "terraform/modules/iam/variables.tf", + "terraform/modules/iam/outputs.tf", + ], + ) + def test_has_header(self, rel): + p = EXAMPLE_DIR / rel + if p.exists(): + assert "Licensed to the Apache Software Foundation" in p.read_text(encoding="utf-8"), ( + f"{rel} is missing the Apache license header" + ) + + +class TestBucketParameterization: + def test_s3_bucket_variable_declared(self): + v = (EXAMPLE_DIR / "terraform" / "variables.tf").read_text() + assert "s3_bucket_name" in v + + def test_user_data_sets_bucket_env(self): + ud = ( + EXAMPLE_DIR / "terraform" / "modules" / "compute" / "templates" / "user_data.sh.tpl" + ).read_text() + assert "BURR_S3_BUCKET" in ud + assert "burr" in ud + + +class TestSecurity: + def test_imdsv2_enforced(self): + c = (EXAMPLE_DIR / "terraform" / "modules" / "compute" / "main.tf").read_text() + assert "http_tokens" in c and "required" in c + + def test_ebs_encrypted(self): + c = (EXAMPLE_DIR / "terraform" / "modules" / "compute" / "main.tf").read_text() + assert "encrypted" in c and "true" in c + + def test_no_ingress_rules(self): + """With SSM access, the security group should have NO ingress rules.""" + net_main = ( + EXAMPLE_DIR / "terraform" / "modules" / "networking" / "main.tf" + ).read_text() + # There should be no aws_security_group_rule with type = "ingress" + assert 'type = "ingress"' not in net_main, ( + "Security group should have no ingress rules (SSM access only)" + ) + + def test_iam_read_only(self): + """IAM policy should only have read actions, no write/delete.""" + iam_main = (EXAMPLE_DIR / "terraform" / "modules" / "iam" / "main.tf").read_text() + assert "s3:GetObject" in iam_main + assert "s3:PutObject" not in iam_main + assert "s3:DeleteObject" not in iam_main + + def test_ssm_core_policy_attached(self): + """Instance must have AmazonSSMManagedInstanceCore for SSM access.""" + iam_main = (EXAMPLE_DIR / "terraform" / "modules" / "iam" / "main.tf").read_text() + assert "AmazonSSMManagedInstanceCore" in iam_main + + +class TestTerraformValidate: + @pytest.fixture(autouse=True) + def _skip_if_no_terraform(self): + if subprocess.run(["terraform", "version"], capture_output=True).returncode != 0: + pytest.skip("terraform not installed") + + def test_fmt(self): + r = subprocess.run( + ["terraform", "fmt", "-check", "-recursive"], + cwd=EXAMPLE_DIR / "terraform", + capture_output=True, + text=True, + ) + assert r.returncode == 0, f"terraform fmt failed:\n{r.stdout}" + + def test_validate(self): + subprocess.run( + ["terraform", "init", "-backend=false"], + cwd=EXAMPLE_DIR / "terraform", + capture_output=True, + ) + r = subprocess.run( + ["terraform", "validate"], + cwd=EXAMPLE_DIR / "terraform", + capture_output=True, + text=True, + ) + assert r.returncode == 0, f"terraform validate failed:\n{r.stderr}" From 0d9b8b2978c5bf62f7c0ba8c5fb0329657f8e78f Mon Sep 17 00:00:00 2001 From: vaquarkhan Date: Mon, 29 Jun 2026 23:48:26 -0500 Subject: [PATCH 2/2] docs(deployments): use mermaid diagram for Burr UI architecture (#391) --- examples/deployment/aws/burr-ui/README.md | 39 ++++++++++------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/examples/deployment/aws/burr-ui/README.md b/examples/deployment/aws/burr-ui/README.md index 52a9f37aa..a246974fa 100644 --- a/examples/deployment/aws/burr-ui/README.md +++ b/examples/deployment/aws/burr-ui/README.md @@ -32,28 +32,23 @@ This deploys a single-tenant Burr UI server that: - Is accessible only via SSM port forwarding (no public internet exposure) - Uses a single EC2 instance (not horizontally scaled — by design) -``` -┌─────────────────────────────────────────────────────────┐ -│ VPC (10.0.0.0/16) │ -│ │ -│ ┌─────────────────────────────────────────────────┐ │ -│ │ Private Subnet │ │ -│ │ │ │ -│ │ ┌──────────────────────────┐ │ │ -│ │ │ EC2 (t3.small) │ │ │ -│ │ │ - burr --no-open │ │ │ -│ │ │ - port 7241 │ │ │ -│ │ │ - BURR_S3_BUCKET=... │──── reads ──── S3 │ │ -│ │ └──────────────────────────┘ │ │ -│ └─────────────────────────────────────────────────┘ │ -│ │ -│ SSM VPC Endpoints (ssm, ssmmessages, ec2messages) │ -│ NAT Gateway (outbound for pip install) │ -└─────────────────────────────────────────────────────────┘ - │ - │ SSM port-forward (your laptop) - ▼ - http://localhost:7241 +```mermaid +graph TB + subgraph VPC["VPC (10.0.0.0/16)"] + subgraph Private["Private Subnet"] + EC2["EC2 (t3.small)
burr --no-open
port 7241
BURR_S3_BUCKET=..."] + end + NAT["NAT Gateway"] + SSM_EP["SSM VPC Endpoints"] + end + + S3["S3 Bucket
(existing tracking data)"] + User["Your Laptop
http://localhost:7241"] + + EC2 -->|"reads tracking logs"| S3 + EC2 -->|"outbound via NAT"| NAT + EC2 --- SSM_EP + User -->|"SSM port-forward 7241"| SSM_EP ``` ## Prerequisites