Skip to content

Commit 2c6a3b2

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

File tree

8 files changed

+112
-32
lines changed

8 files changed

+112
-32
lines changed

.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: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
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:fa6cb9a

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,12 @@
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")
812

913
# Create an IAM Role for the Lambda function
1014
lambda_role = aws.iam.Role("lambdaExecutionRole",
@@ -29,6 +33,48 @@
2933
policy_arn="arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
3034
)
3135

36+
# Define an IAM policy that allows DynamoDB scan action
37+
dynamodb_scan_policy = aws.iam.Policy("dynamodb-policy",
38+
policy={
39+
"Version": "2012-10-17",
40+
"Statement": [
41+
{
42+
"Effect": "Allow",
43+
"Action": "dynamodb:Scan",
44+
"Resource": "arn:aws:dynamodb:us-east-1:289940214902:table/DYNAMODB_TABLE"
45+
}
46+
]
47+
}
48+
)
49+
50+
# Attach the policy to the Lambda execution role
51+
role_policy_attachment = aws.iam.RolePolicyAttachment("lambda-role-policy-attachment",
52+
role=lambda_role.name,
53+
policy_arn=dynamodb_scan_policy.arn
54+
)
55+
56+
57+
# Define a DynamoDB table
58+
dynamodb_table = aws.dynamodb.Table("todo",
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,

todo-app/lambda_function/lambda.py

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,54 +19,81 @@
1919
)
2020

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

2625
@app.get("/todos")
2726
async def get_todos():
2827
try:
29-
response = table.scan()
28+
# Perform a scan operation on the DynamoDB table
29+
response = dynamodb.scan(TableName=table_name)
3030
return response.get('Items', [])
3131
except Exception as e:
3232
logging.error(f"Error getting todos: {e}")
3333
raise HTTPException(status_code=500, detail=str(e))
3434

35+
# POST request to create a new todo
3536
@app.post("/todos", status_code=201)
3637
async def create_todo(todo: dict):
3738
if not todo:
3839
raise HTTPException(status_code=400, detail="Invalid input")
40+
3941
try:
42+
# Ensure 'id' is present in the todo item, or else raise an error
43+
if 'id' not in todo:
44+
raise HTTPException(status_code=400, detail="Missing 'id' in request body")
45+
46+
# Add the todo item to DynamoDB
4047
table.put_item(Item=todo)
41-
return todo
48+
return todo # Return the created todo item
49+
4250
except Exception as e:
4351
logging.error(f"Error creating todo: {e}")
44-
raise HTTPException(status_code=500, detail=str(e))
52+
raise HTTPException(status_code=500, detail="Internal server error")
4553

54+
# PUT request to update an existing todo
4655
@app.put("/todos/{id}")
4756
async def update_todo(id: str, todo: dict):
4857
if 'text' not in todo:
49-
raise HTTPException(status_code=400, detail='Missing "text" in request body')
58+
raise HTTPException(status_code=400, detail="Missing 'text' in request body")
59+
5060
try:
51-
table.update_item(
61+
# Update the todo item in DynamoDB based on the 'id'
62+
response = table.update_item(
5263
Key={'id': id},
5364
UpdateExpression='SET #t = :t',
5465
ExpressionAttributeNames={'#t': 'text'},
55-
ExpressionAttributeValues={':t': todo['text']}
66+
ExpressionAttributeValues={':t': todo['text']},
67+
ReturnValues="ALL_NEW" # Return the updated item
5668
)
57-
return todo
69+
70+
# Check if the item was updated and return the updated todo
71+
updated_todo = response.get('Attributes')
72+
if not updated_todo:
73+
raise HTTPException(status_code=404, detail="Todo not found")
74+
75+
return updated_todo
76+
5877
except Exception as e:
5978
logging.error(f"Error updating todo: {e}")
60-
raise HTTPException(status_code=500, detail=str(e))
79+
raise HTTPException(status_code=500, detail="Internal server error")
6180

81+
# DELETE request to delete a todo
6282
@app.delete("/todos/{id}", status_code=204)
6383
async def delete_todo(id: str):
6484
try:
65-
table.delete_item(Key={'id': id})
66-
return {"detail": "Todo deleted"} # You can return a message or an empty response
85+
# Attempt to delete the todo item from DynamoDB
86+
response = table.delete_item(Key={'id': id})
87+
88+
# If the item does not exist, return a 404
89+
if response.get('ResponseMetadata', {}).get('HTTPStatusCode') != 200:
90+
raise HTTPException(status_code=404, detail="Todo not found")
91+
92+
return {"detail": "Todo deleted successfully"} # Confirm deletion
93+
6794
except Exception as e:
6895
logging.error(f"Error deleting todo: {e}")
69-
raise HTTPException(status_code=500, detail=str(e))
96+
raise HTTPException(status_code=500, detail="Internal server error")
7097

7198
@app.get("/health")
7299
async def health():

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)