Add Quantum LocalStack OpenTofu project
This commit is contained in:
+13
@@ -0,0 +1,13 @@
|
|||||||
|
.terraform/
|
||||||
|
.terraform.lock.hcl
|
||||||
|
terraform.tfstate
|
||||||
|
terraform.tfstate.*
|
||||||
|
*.tfplan
|
||||||
|
*.zip
|
||||||
|
crash.log
|
||||||
|
crash.*.log
|
||||||
|
override.tf
|
||||||
|
override.tf.json
|
||||||
|
*_override.tf
|
||||||
|
*_override.tf.json
|
||||||
|
.env
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
# aws-localstack
|
||||||
|
|
||||||
|
Projeto OpenTofu para provisionar recursos AWS simulados no LocalStack da aplicacao ficticia Quantum.
|
||||||
|
|
||||||
|
Endpoint LocalStack:
|
||||||
|
|
||||||
|
```text
|
||||||
|
https://localstack.paulononato.com.br
|
||||||
|
```
|
||||||
|
|
||||||
|
## Recursos
|
||||||
|
|
||||||
|
- S3 bucket para artefatos da aplicacao Quantum.
|
||||||
|
- SQS fila principal e DLQ.
|
||||||
|
- Lambda Python para processar eventos.
|
||||||
|
- IAM role e policies ficticias para a Lambda.
|
||||||
|
- CloudWatch Log Group.
|
||||||
|
- Secrets Manager com credenciais simuladas.
|
||||||
|
- Event source mapping SQS -> Lambda.
|
||||||
|
|
||||||
|
## Pre-requisitos
|
||||||
|
|
||||||
|
- OpenTofu instalado.
|
||||||
|
- AWS CLI opcional para testes.
|
||||||
|
- Acesso ao endpoint LocalStack.
|
||||||
|
|
||||||
|
Credenciais usadas pelo LocalStack:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export AWS_ACCESS_KEY_ID=test
|
||||||
|
export AWS_SECRET_ACCESS_KEY=test
|
||||||
|
export AWS_DEFAULT_REGION=us-east-1
|
||||||
|
```
|
||||||
|
|
||||||
|
No PowerShell:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
$env:AWS_ACCESS_KEY_ID="test"
|
||||||
|
$env:AWS_SECRET_ACCESS_KEY="test"
|
||||||
|
$env:AWS_DEFAULT_REGION="us-east-1"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Como usar
|
||||||
|
|
||||||
|
Inicializar:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
tofu init
|
||||||
|
```
|
||||||
|
|
||||||
|
Planejar:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
tofu plan
|
||||||
|
```
|
||||||
|
|
||||||
|
Aplicar:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
tofu apply
|
||||||
|
```
|
||||||
|
|
||||||
|
Destruir:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
tofu destroy
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testes rapidos
|
||||||
|
|
||||||
|
Listar buckets:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
aws --endpoint-url https://localstack.paulononato.com.br s3 ls
|
||||||
|
```
|
||||||
|
|
||||||
|
Enviar mensagem para a fila Quantum:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
aws --endpoint-url https://localstack.paulononato.com.br sqs send-message \
|
||||||
|
--queue-url "$(tofu output -raw quantum_queue_url)" \
|
||||||
|
--message-body '{"event":"quantum.order.created","orderId":"QTM-1001"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
Ver segredo:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
aws --endpoint-url https://localstack.paulononato.com.br secretsmanager get-secret-value \
|
||||||
|
--secret-id "$(tofu output -raw quantum_secret_name)"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Observacao sobre RDS
|
||||||
|
|
||||||
|
RDS nao esta incluido na edicao Community do LocalStack provisionada no servidor. Este projeto evita RDS e usa apenas os servicos disponiveis na stack atual.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"event": "quantum.order.created",
|
||||||
|
"orderId": "QTM-1001",
|
||||||
|
"customer": {
|
||||||
|
"id": "CUS-9001",
|
||||||
|
"name": "Ada Lovelace"
|
||||||
|
},
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"sku": "QBIT-001",
|
||||||
|
"quantity": 2
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
def lambda_handler(event, context):
|
||||||
|
app_name = os.environ.get("APP_NAME", "Quantum")
|
||||||
|
bucket_name = os.environ.get("BUCKET_NAME", "unknown")
|
||||||
|
secret_name = os.environ.get("SECRET_NAME", "unknown")
|
||||||
|
|
||||||
|
records = event.get("Records", [])
|
||||||
|
|
||||||
|
processed = []
|
||||||
|
for record in records:
|
||||||
|
body = record.get("body", "{}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
payload = json.loads(body)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
payload = {"raw": body}
|
||||||
|
|
||||||
|
processed.append(
|
||||||
|
{
|
||||||
|
"messageId": record.get("messageId"),
|
||||||
|
"payload": payload,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
print(
|
||||||
|
json.dumps(
|
||||||
|
{
|
||||||
|
"app": app_name,
|
||||||
|
"bucket": bucket_name,
|
||||||
|
"secret": secret_name,
|
||||||
|
"processedCount": len(processed),
|
||||||
|
"records": processed,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"statusCode": 200,
|
||||||
|
"body": json.dumps(
|
||||||
|
{
|
||||||
|
"message": "Quantum event batch processed",
|
||||||
|
"processedCount": len(processed),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
}
|
||||||
@@ -0,0 +1,207 @@
|
|||||||
|
locals {
|
||||||
|
name_prefix = "${var.project_name}-${var.environment}"
|
||||||
|
|
||||||
|
common_tags = {
|
||||||
|
Application = "Quantum"
|
||||||
|
Environment = var.environment
|
||||||
|
ManagedBy = "OpenTofu"
|
||||||
|
Runtime = "LocalStack"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data "archive_file" "quantum_lambda" {
|
||||||
|
type = "zip"
|
||||||
|
source_file = "${path.module}/lambda/handler.py"
|
||||||
|
output_path = "${path.module}/build/quantum_lambda.zip"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_s3_bucket" "quantum_artifacts" {
|
||||||
|
bucket = "${local.name_prefix}-artifacts"
|
||||||
|
force_destroy = true
|
||||||
|
|
||||||
|
tags = merge(local.common_tags, {
|
||||||
|
Name = "${local.name_prefix}-artifacts"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_s3_bucket_versioning" "quantum_artifacts" {
|
||||||
|
bucket = aws_s3_bucket.quantum_artifacts.id
|
||||||
|
|
||||||
|
versioning_configuration {
|
||||||
|
status = "Enabled"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_s3_object" "sample_config" {
|
||||||
|
bucket = aws_s3_bucket.quantum_artifacts.id
|
||||||
|
key = "config/quantum-dev.json"
|
||||||
|
content_type = "application/json"
|
||||||
|
|
||||||
|
content = jsonencode({
|
||||||
|
appName = "Quantum"
|
||||||
|
environment = var.environment
|
||||||
|
featureToggle = "simulated-localstack"
|
||||||
|
owner = "platform-team"
|
||||||
|
})
|
||||||
|
|
||||||
|
tags = local.common_tags
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_sqs_queue" "quantum_dlq" {
|
||||||
|
name = "${local.name_prefix}-events-dlq"
|
||||||
|
message_retention_seconds = 1209600
|
||||||
|
|
||||||
|
tags = merge(local.common_tags, {
|
||||||
|
Name = "${local.name_prefix}-events-dlq"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_sqs_queue" "quantum_events" {
|
||||||
|
name = "${local.name_prefix}-events"
|
||||||
|
visibility_timeout_seconds = 45
|
||||||
|
message_retention_seconds = 345600
|
||||||
|
|
||||||
|
redrive_policy = jsonencode({
|
||||||
|
deadLetterTargetArn = aws_sqs_queue.quantum_dlq.arn
|
||||||
|
maxReceiveCount = 3
|
||||||
|
})
|
||||||
|
|
||||||
|
tags = merge(local.common_tags, {
|
||||||
|
Name = "${local.name_prefix}-events"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_secretsmanager_secret" "quantum_app" {
|
||||||
|
name = "${local.name_prefix}/app"
|
||||||
|
recovery_window_in_days = 0
|
||||||
|
|
||||||
|
tags = merge(local.common_tags, {
|
||||||
|
Name = "${local.name_prefix}-app-secret"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_secretsmanager_secret_version" "quantum_app" {
|
||||||
|
secret_id = aws_secretsmanager_secret.quantum_app.id
|
||||||
|
|
||||||
|
secret_string = jsonencode({
|
||||||
|
databaseUrl = "postgres://quantum_user:fake_password@quantum-db.local:5432/quantum"
|
||||||
|
apiKey = "qtm_dev_fake_123456"
|
||||||
|
jwtSecret = "localstack-only-secret"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_iam_role" "quantum_lambda" {
|
||||||
|
name = "${local.name_prefix}-lambda-role"
|
||||||
|
|
||||||
|
assume_role_policy = jsonencode({
|
||||||
|
Version = "2012-10-17"
|
||||||
|
Statement = [
|
||||||
|
{
|
||||||
|
Effect = "Allow"
|
||||||
|
Principal = {
|
||||||
|
Service = "lambda.amazonaws.com"
|
||||||
|
}
|
||||||
|
Action = "sts:AssumeRole"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
tags = local.common_tags
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_iam_policy" "quantum_lambda" {
|
||||||
|
name = "${local.name_prefix}-lambda-policy"
|
||||||
|
description = "Permissoes simuladas da Lambda Quantum no LocalStack."
|
||||||
|
|
||||||
|
policy = jsonencode({
|
||||||
|
Version = "2012-10-17"
|
||||||
|
Statement = [
|
||||||
|
{
|
||||||
|
Effect = "Allow"
|
||||||
|
Action = [
|
||||||
|
"logs:CreateLogGroup",
|
||||||
|
"logs:CreateLogStream",
|
||||||
|
"logs:PutLogEvents"
|
||||||
|
]
|
||||||
|
Resource = "*"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Effect = "Allow"
|
||||||
|
Action = [
|
||||||
|
"sqs:ReceiveMessage",
|
||||||
|
"sqs:DeleteMessage",
|
||||||
|
"sqs:GetQueueAttributes"
|
||||||
|
]
|
||||||
|
Resource = aws_sqs_queue.quantum_events.arn
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Effect = "Allow"
|
||||||
|
Action = [
|
||||||
|
"secretsmanager:GetSecretValue"
|
||||||
|
]
|
||||||
|
Resource = aws_secretsmanager_secret.quantum_app.arn
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Effect = "Allow"
|
||||||
|
Action = [
|
||||||
|
"s3:GetObject",
|
||||||
|
"s3:PutObject",
|
||||||
|
"s3:ListBucket"
|
||||||
|
]
|
||||||
|
Resource = [
|
||||||
|
aws_s3_bucket.quantum_artifacts.arn,
|
||||||
|
"${aws_s3_bucket.quantum_artifacts.arn}/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
tags = local.common_tags
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_iam_role_policy_attachment" "quantum_lambda" {
|
||||||
|
role = aws_iam_role.quantum_lambda.name
|
||||||
|
policy_arn = aws_iam_policy.quantum_lambda.arn
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_cloudwatch_log_group" "quantum_lambda" {
|
||||||
|
name = "/aws/lambda/${local.name_prefix}-processor"
|
||||||
|
retention_in_days = 14
|
||||||
|
|
||||||
|
tags = local.common_tags
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_lambda_function" "quantum_processor" {
|
||||||
|
function_name = "${local.name_prefix}-processor"
|
||||||
|
description = "Processador ficticio de eventos da aplicacao Quantum."
|
||||||
|
role = aws_iam_role.quantum_lambda.arn
|
||||||
|
runtime = "python3.11"
|
||||||
|
handler = "handler.lambda_handler"
|
||||||
|
filename = data.archive_file.quantum_lambda.output_path
|
||||||
|
source_code_hash = data.archive_file.quantum_lambda.output_base64sha256
|
||||||
|
timeout = 30
|
||||||
|
memory_size = 256
|
||||||
|
|
||||||
|
environment {
|
||||||
|
variables = {
|
||||||
|
APP_NAME = "Quantum"
|
||||||
|
BUCKET_NAME = aws_s3_bucket.quantum_artifacts.bucket
|
||||||
|
SECRET_NAME = aws_secretsmanager_secret.quantum_app.name
|
||||||
|
QUEUE_URL = aws_sqs_queue.quantum_events.url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
depends_on = [
|
||||||
|
aws_cloudwatch_log_group.quantum_lambda,
|
||||||
|
aws_iam_role_policy_attachment.quantum_lambda
|
||||||
|
]
|
||||||
|
|
||||||
|
tags = local.common_tags
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_lambda_event_source_mapping" "quantum_events" {
|
||||||
|
event_source_arn = aws_sqs_queue.quantum_events.arn
|
||||||
|
function_name = aws_lambda_function.quantum_processor.arn
|
||||||
|
batch_size = 5
|
||||||
|
enabled = true
|
||||||
|
}
|
||||||
+34
@@ -0,0 +1,34 @@
|
|||||||
|
output "localstack_endpoint" {
|
||||||
|
description = "Endpoint LocalStack usado pelo provider."
|
||||||
|
value = var.localstack_endpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
output "quantum_bucket_name" {
|
||||||
|
description = "Bucket S3 da aplicacao Quantum."
|
||||||
|
value = aws_s3_bucket.quantum_artifacts.bucket
|
||||||
|
}
|
||||||
|
|
||||||
|
output "quantum_queue_url" {
|
||||||
|
description = "URL da fila SQS principal."
|
||||||
|
value = aws_sqs_queue.quantum_events.url
|
||||||
|
}
|
||||||
|
|
||||||
|
output "quantum_dlq_url" {
|
||||||
|
description = "URL da fila DLQ."
|
||||||
|
value = aws_sqs_queue.quantum_dlq.url
|
||||||
|
}
|
||||||
|
|
||||||
|
output "quantum_lambda_name" {
|
||||||
|
description = "Nome da Lambda processadora."
|
||||||
|
value = aws_lambda_function.quantum_processor.function_name
|
||||||
|
}
|
||||||
|
|
||||||
|
output "quantum_log_group_name" {
|
||||||
|
description = "Log Group CloudWatch da Lambda."
|
||||||
|
value = aws_cloudwatch_log_group.quantum_lambda.name
|
||||||
|
}
|
||||||
|
|
||||||
|
output "quantum_secret_name" {
|
||||||
|
description = "Nome do segredo no Secrets Manager."
|
||||||
|
value = aws_secretsmanager_secret.quantum_app.name
|
||||||
|
}
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
provider "aws" {
|
||||||
|
region = var.aws_region
|
||||||
|
access_key = "test"
|
||||||
|
secret_key = "test"
|
||||||
|
s3_use_path_style = true
|
||||||
|
skip_credentials_validation = true
|
||||||
|
skip_metadata_api_check = true
|
||||||
|
skip_requesting_account_id = true
|
||||||
|
|
||||||
|
endpoints {
|
||||||
|
apigateway = var.localstack_endpoint
|
||||||
|
cloudformation = var.localstack_endpoint
|
||||||
|
cloudwatch = var.localstack_endpoint
|
||||||
|
iam = var.localstack_endpoint
|
||||||
|
lambda = var.localstack_endpoint
|
||||||
|
logs = var.localstack_endpoint
|
||||||
|
s3 = var.localstack_endpoint
|
||||||
|
secretsmanager = var.localstack_endpoint
|
||||||
|
sqs = var.localstack_endpoint
|
||||||
|
sts = var.localstack_endpoint
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
variable "aws_region" {
|
||||||
|
description = "Regiao AWS simulada no LocalStack."
|
||||||
|
type = string
|
||||||
|
default = "us-east-1"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "localstack_endpoint" {
|
||||||
|
description = "Endpoint HTTPS do LocalStack."
|
||||||
|
type = string
|
||||||
|
default = "https://localstack.paulononato.com.br"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "project_name" {
|
||||||
|
description = "Nome curto do projeto ficticio."
|
||||||
|
type = string
|
||||||
|
default = "quantum"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "environment" {
|
||||||
|
description = "Ambiente ficticio da aplicacao."
|
||||||
|
type = string
|
||||||
|
default = "dev"
|
||||||
|
}
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
terraform {
|
||||||
|
required_version = ">= 1.6.0"
|
||||||
|
|
||||||
|
required_providers {
|
||||||
|
aws = {
|
||||||
|
source = "hashicorp/aws"
|
||||||
|
version = "~> 5.0"
|
||||||
|
}
|
||||||
|
archive = {
|
||||||
|
source = "hashicorp/archive"
|
||||||
|
version = "~> 2.4"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user