Skip to content

Commit e7da832

Browse files
author
Adetokunbo Ige
committed
chore: update the pulumi stack name
Signed-off-by: Adetokunbo Ige <[email protected]>
1 parent a0caf11 commit e7da832

File tree

10 files changed

+123
-37
lines changed

10 files changed

+123
-37
lines changed

.github/workflows/docker-publish.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,6 @@ jobs:
8282
file: todo-app/lambda_function/Dockerfile
8383
push: true
8484
tags: ${{ env.ECR_REGISTRY }}/${{ env.REPO_NAME }}:${{ steps.sha_short.outputs.sha_short }}
85-
platforms: linux/amd64,linux/arm64
85+
platforms: linux/amd64
8686
provenance: false
8787
continue-on-error: false

.github/workflows/pulumi-deploy.yml

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ on:
1010
jobs:
1111
pulumi-deploy:
1212
runs-on: ubuntu-latest
13-
env:
14-
AWS_REGION: ${{ secrets.AWS_REGION }}
1513

1614
permissions:
1715
id-token: write
@@ -36,16 +34,25 @@ jobs:
3634
run: |
3735
pip install -r requirements.txt
3836
39-
- uses: pulumi/actions@v3
40-
with:
41-
command: preview
42-
stack-name: dev
37+
- name: Configure Pulumi
38+
working-directory: todo-app
39+
run: |
40+
pulumi stack select ExitoLab/todo-app/dev --non-interactive || pulumi stack init ExitoLab/todo-app/dev
4341
env:
4442
PULUMI_ACCESS_TOKEN: ${{ secrets.PULUMI_ACCESS_TOKEN }}
4543

46-
- uses: pulumi/actions@v3
47-
with:
48-
command: up
49-
stack-name: dev
44+
- name: Pulumi Preview
45+
working-directory: todo-app
46+
run: |
47+
pulumi stack select ExitoLab/todo-app/dev
48+
pulumi preview
49+
env:
50+
PULUMI_ACCESS_TOKEN: ${{ secrets.PULUMI_ACCESS_TOKEN }}
51+
52+
- name: Pulumi Up
53+
working-directory: todo-app
54+
run: |
55+
pulumi stack select ExitoLab/todo-app/dev
56+
pulumi up --yes
5057
env:
5158
PULUMI_ACCESS_TOKEN: ${{ secrets.PULUMI_ACCESS_TOKEN }}

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Building and Deploying a Serverless Todo App on AWS with Docker, Lambda, API Gateway, and GitHub Actions using Pulumi in Python
1+
# Building and Deploying a Serverless Todo App on AWS using Pulumi in Python
22

33
## Overview
44

todo-app/.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
11
*.pyc
22
venv/
3-

todo-app/Pulumi.dev.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
config:
2-
todo-app:location: eastus
2+
todo-app:docker_image: 289940214902.dkr.ecr.us-east-1.amazonaws.com/todo_pulumi_docker_aws_lambda_api_gateway:3f8cc06
3+
todo-app:environment: dev

todo-app/Pulumi.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ config:
99
pulumi:tags:
1010
value:
1111
pulumi:template: aws-python
12+

todo-app/__main__.py

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,13 @@
33
from pulumi_docker import Image, DockerBuild
44
import pulumi_docker as docker
55

6-
# Step 1: Create an ECR repository
7-
docker_image = "289940214902.dkr.ecr.us-east-1.amazonaws.com/todo-app:v1.1"
6+
from pulumi import Config
7+
8+
# Create a config object to access configuration values
9+
config = pulumi.Config()
10+
11+
docker_image = config.get("docker_image")
12+
environment = config.get("environment")
813

914
# Create an IAM Role for the Lambda function
1015
lambda_role = aws.iam.Role("lambdaExecutionRole",
@@ -29,6 +34,47 @@
2934
policy_arn="arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
3035
)
3136

37+
# Define an IAM policy that allows DynamoDB scan action
38+
dynamodb_scan_policy = aws.iam.Policy("dynamodb-policy",
39+
policy={
40+
"Version": "2012-10-17",
41+
"Statement": [
42+
{
43+
"Effect": "Allow",
44+
"Action": "dynamodb:Scan",
45+
"Resource": "arn:aws:dynamodb:us-east-1:289940214902:table/todo"
46+
}
47+
]
48+
}
49+
)
50+
51+
# Attach the policy to the Lambda execution role
52+
role_policy_attachment = aws.iam.RolePolicyAttachment("lambda-role-policy-attachment",
53+
role=lambda_role.name,
54+
policy_arn=dynamodb_scan_policy.arn
55+
)
56+
57+
# Define a DynamoDB table
58+
dynamodb_table = aws.dynamodb.Table(f"todo-{environment}",
59+
hash_key="id", # Partition key
60+
range_key="timestamp", # Sort key
61+
attributes=[
62+
aws.dynamodb.TableAttributeArgs(
63+
name="id",
64+
type="S" # S for string
65+
),
66+
aws.dynamodb.TableAttributeArgs(
67+
name="timestamp",
68+
type="N" # N for number
69+
),
70+
],
71+
billing_mode="PAY_PER_REQUEST", # Use on-demand mode (no provisioned throughput)
72+
tags={
73+
"Environment": "dev",
74+
"Created_By": "Pulumi"
75+
}
76+
)
77+
3278
# Create a Lambda function using the Docker image
3379
lambda_function = aws.lambda_.Function("my-serverless-function",
3480
name="my-serverless-function",
@@ -65,15 +111,13 @@
65111
type="AWS_PROXY",
66112
uri=lambda_function.invoke_arn) # Ensure lambda_function is defined
67113

68-
69114
lambda_permission = aws.lambda_.Permission("api-gateway-lambda-permission",
70115
action="lambda:InvokeFunction",
71116
function=lambda_function.name,
72117
principal="apigateway.amazonaws.com",
73118
source_arn=pulumi.Output.concat(api.execution_arn, "/*/*")
74119
)
75120

76-
77121
# Deployment of the API, explicitly depends on method and integration to avoid timing issues
78122
deployment = aws.apigateway.Deployment("api-deployment",
79123
rest_api=api.id,
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
README.md
22
invoke-function-policy.json
33
__pycache__/
4-
event.json
4+
event.json
5+

todo-app/lambda_function/lambda.py

Lines changed: 48 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
from fastapi.middleware.cors import CORSMiddleware
66
from mangum import Mangum
77

8+
from pydantic import BaseModel
9+
from typing import List, Dict
10+
811
logging.basicConfig(level=logging.DEBUG)
912

1013
app = FastAPI()
@@ -19,55 +22,83 @@
1922
)
2023

2124
# Connect to DynamoDB
22-
dynamodb = boto3.resource('dynamodb')
23-
table_name = 'todo'
24-
table = dynamodb.Table(table_name)
25+
dynamodb = boto3.resource('dynamodb', region_name='us-east-1')
26+
table = dynamodb.Table("todo")
2527

26-
@app.get("/todos")
28+
@app.get("/todos", response_model=List[dict])
2729
async def get_todos():
2830
try:
31+
# Perform a scan operation on the DynamoDB table
2932
response = table.scan()
33+
# Return the 'Items' from the scan result, which contains the table data
3034
return response.get('Items', [])
3135
except Exception as e:
3236
logging.error(f"Error getting todos: {e}")
3337
raise HTTPException(status_code=500, detail=str(e))
3438

39+
# POST request to create a new todo
3540
@app.post("/todos", status_code=201)
36-
async def create_todo(todo: dict):
41+
async def create_todo(todo: Dict):
3742
if not todo:
3843
raise HTTPException(status_code=400, detail="Invalid input")
44+
45+
# Ensure 'id' is present in the todo item, or else raise an error
46+
if 'id' not in todo:
47+
raise HTTPException(status_code=400, detail="Missing 'id' in request body")
48+
3949
try:
50+
# Add the todo item to DynamoDB
4051
table.put_item(Item=todo)
41-
return todo
52+
return todo # Return the created todo item
53+
4254
except Exception as e:
4355
logging.error(f"Error creating todo: {e}")
44-
raise HTTPException(status_code=500, detail=str(e))
56+
raise HTTPException(status_code=500, detail="Internal server error")
4557

58+
# PUT request to update an existing todo
4659
@app.put("/todos/{id}")
47-
async def update_todo(id: str, todo: dict):
60+
async def update_todo(id: str, todo: Dict):
61+
# Validate that 'text' is in the request body
4862
if 'text' not in todo:
49-
raise HTTPException(status_code=400, detail='Missing "text" in request body')
63+
raise HTTPException(status_code=400, detail="Missing 'text' in request body")
64+
5065
try:
51-
table.update_item(
66+
# Update the todo item in DynamoDB based on the 'id'
67+
response = table.update_item(
5268
Key={'id': id},
5369
UpdateExpression='SET #t = :t',
5470
ExpressionAttributeNames={'#t': 'text'},
55-
ExpressionAttributeValues={':t': todo['text']}
71+
ExpressionAttributeValues={':t': todo['text']},
72+
ReturnValues="ALL_NEW" # Return the updated item
5673
)
57-
return todo
74+
75+
# Check if the item was updated and return the updated todo
76+
updated_todo = response.get('Attributes')
77+
if not updated_todo:
78+
raise HTTPException(status_code=404, detail="Todo not found")
79+
80+
return updated_todo
81+
5882
except Exception as e:
5983
logging.error(f"Error updating todo: {e}")
60-
raise HTTPException(status_code=500, detail=str(e))
84+
raise HTTPException(status_code=500, detail="Internal server error")
6185

86+
# DELETE request to delete a todo
6287
@app.delete("/todos/{id}", status_code=204)
6388
async def delete_todo(id: str):
6489
try:
65-
table.delete_item(Key={'id': id})
66-
return {"detail": "Todo deleted"} # You can return a message or an empty response
90+
# Attempt to delete the todo item from DynamoDB
91+
response = table.delete_item(Key={'id': id})
92+
93+
# Check if the item exists based on the HTTP status code
94+
if response.get('ResponseMetadata', {}).get('HTTPStatusCode') != 200:
95+
raise HTTPException(status_code=404, detail="Todo not found")
96+
97+
return {"detail": "Todo deleted successfully"} # Confirm deletion
98+
6799
except Exception as e:
68100
logging.error(f"Error deleting todo: {e}")
69-
raise HTTPException(status_code=500, detail=str(e))
70-
101+
raise HTTPException(status_code=500, detail="Internal server error")
71102
@app.get("/health")
72103
async def health():
73104
try:

todo-app/requirements.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
pulumi>=3.0.0,<4.0.0
22
pulumi-aws>=6.0.2,<7.0.0
33
pulumi_docker==3.4.0
4-
setuptools
4+
setuptools
5+
6+

0 commit comments

Comments
 (0)