Skip to content

Commit 2fa20c3

Browse files
authored
Make defang environment dynamic based on stack input
0 parents  commit 2fa20c3

6 files changed

Lines changed: 289 additions & 0 deletions

File tree

README.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Python & Flask & AWS S3
2+
3+
[![1-click-deploy](https://raw.githubusercontent.com/DefangLabs/defang-assets/main/Logos/Buttons/SVG/deploy-with-defang.svg)](https://portal.defang.dev/redirect?url=https%3A%2F%2Fgithub.com%2Fnew%3Ftemplate_name%3Dsample-python-s3-template%26template_owner%3DDefangSamples)
4+
5+
This sample requires an API key to access AWS S3. The name of the config values is referenced in the compose.yaml file.
6+
To provide a value for it, you can use the Defang CLI like this:
7+
8+
```
9+
defang config set --name AWS_ACCESS_KEY
10+
defang config set --name AWS_SECRET_KEY
11+
```
12+
13+
and then enter the value when prompted.
14+
15+
## Testing
16+
17+
```bash
18+
curl -X POST -H 'Content-Type: application/json' -d '{ "first_name" : "jane", "last_name" : "doe" }' https://xxxxxx/upload
19+
curl https://xxxxxx/download
20+
```
21+
22+
---
23+
24+
Title: Python & Flask & AWS S3
25+
26+
Short Description: An app that demonstrates how to upload and download files from AWS S3 using Python and Flask.
27+
28+
Tags: Python, Flask, AWS, S3
29+
30+
Languages: python

app/.gitignore

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
2+
# --- Python ---
3+
# Byte-compiled / optimized / DLL files
4+
__pycache__/
5+
*.py[cod]
6+
*$py.class
7+
8+
# C extensions
9+
*.so
10+
11+
# Distribution / packaging
12+
.Python
13+
build/
14+
develop-eggs/
15+
dist/
16+
downloads/
17+
eggs/
18+
.eggs/
19+
lib/
20+
lib64/
21+
parts/
22+
sdist/
23+
var/
24+
wheels/
25+
share/python-wheels/
26+
*.egg-info/
27+
.installed.cfg
28+
*.egg
29+
MANIFEST
30+
31+
# PyInstaller
32+
*.manifest
33+
*.spec
34+
35+
# Installer logs
36+
pip-log.txt
37+
pip-delete-this-directory.txt
38+
39+
# Unit test / coverage reports
40+
htmlcov/
41+
.tox/
42+
.nox/
43+
.coverage
44+
.coverage.*
45+
.cache
46+
nosetests.xml
47+
coverage.xml
48+
*.cover
49+
*.py,cover
50+
.hypothesis/
51+
.pytest_cache/
52+
cover/
53+
54+
# Translations
55+
*.mo
56+
*.pot
57+
58+
# Django stuff:
59+
*.log
60+
local_settings.py
61+
db.sqlite3
62+
db.sqlite3-journal
63+
64+
# Flask stuff:
65+
instance/
66+
.webassets-cache
67+
68+
# Scrapy stuff:
69+
.scrapy
70+
71+
# Sphinx documentation
72+
docs/_build/
73+
74+
# PyBuilder
75+
.pybuilder/
76+
target/
77+
78+
# Jupyter Notebook
79+
.ipynb_checkpoints
80+
81+
# IPython
82+
profile_default/
83+
ipython_config.py
84+
85+
# pdm
86+
.pdm.toml
87+
.pdm-python
88+
.pdm-build/
89+
90+
# PEP 582
91+
__pypackages__/
92+
93+
# Celery stuff
94+
celerybeat-schedule
95+
celerybeat.pid
96+
97+
# SageMath parsed files
98+
*.sage.py
99+
100+
# Environments
101+
.env
102+
.venv
103+
env/
104+
venv/
105+
ENV/
106+
env.bak/
107+
venv.bak/
108+
109+
# Spyder project settings
110+
.spyderproject
111+
.spyproject
112+
113+
# Rope project settings
114+
.ropeproject
115+
116+
# mkdocs documentation
117+
/site
118+
119+
# mypy
120+
.mypy_cache/
121+
.dmypy.json
122+
dmypy.json
123+
124+
# Pyre type checker
125+
.pyre/
126+
127+
# pytype static type analyzer
128+
.pytype/
129+
130+
# Cython debug symbols
131+
cython_debug/
132+
133+
# PyCharm
134+
.idea/

app/Dockerfile

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Use an official Python runtime as a parent image
2+
FROM python:3.11-slim
3+
4+
# Set the working directory to /app
5+
WORKDIR /app
6+
7+
# Install required C++11 libraries and ca-certificates
8+
RUN apt-get update -qq \
9+
&& apt-get install -y \
10+
build-essential \
11+
python3-dev \
12+
ca-certificates \
13+
curl \
14+
&& apt-get clean \
15+
&& rm -rf /var/lib/apt/lists/*
16+
17+
# Install any needed packages specified in requirements.txt
18+
COPY requirements.txt /app/
19+
RUN pip install --no-cache-dir -r requirements.txt
20+
21+
# Copy the current directory contents into the container at /app
22+
COPY . /app
23+
24+
# Make port 5000 available to the world outside this container
25+
EXPOSE 5000
26+
27+
# Run main when the container launches
28+
ENTRYPOINT ["uwsgi", "--http", "0.0.0.0:5000", "--master", "-p", "2", "-w", "main:app"]
29+
USER nobody

app/main.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import os
2+
import json
3+
import boto3
4+
from botocore.exceptions import ClientError
5+
6+
from flask import Flask
7+
from flask import request
8+
9+
10+
app = Flask(__name__)
11+
12+
13+
REGION_NAME = 'us-west-2'
14+
BUCKET_NAME = 'my-sample-bucket'
15+
FILE_NAME = 'file1.json'
16+
17+
18+
@app.route("/")
19+
def index():
20+
return { 'status' : 'ok' }, 200
21+
22+
23+
@app.route("/upload", methods=['POST'])
24+
def upload():
25+
# Check that the post body is valid JSON
26+
if not request.is_json:
27+
return { 'error': 'Invalid JSON' }, 400
28+
29+
# Parse the JSON into a Python dictionary
30+
data = request.get_json()
31+
32+
AWS_ACCESS_KEY=os.environ['AWS_ACCESS_KEY']
33+
AWS_SECRET_KEY=os.environ['AWS_SECRET_KEY']
34+
35+
# Write the JSON to S3
36+
s3 = boto3.client('s3', region_name=REGION_NAME, aws_access_key_id=AWS_ACCESS_KEY, aws_secret_access_key=AWS_SECRET_KEY)
37+
38+
with open(FILE_NAME, 'w') as f:
39+
json.dump(data, f)
40+
41+
s3.upload_file(FILE_NAME, BUCKET_NAME, FILE_NAME)
42+
43+
return { 'status': 'ok' }, 200
44+
45+
46+
@app.route("/download", methods=['GET'])
47+
def download():
48+
AWS_ACCESS_KEY=os.environ['AWS_ACCESS_KEY']
49+
AWS_SECRET_KEY=os.environ['AWS_SECRET_KEY']
50+
51+
# Write the JSON to S3
52+
s3 = boto3.client('s3', region_name=REGION_NAME, aws_access_key_id=AWS_ACCESS_KEY, aws_secret_access_key=AWS_SECRET_KEY)
53+
54+
try:
55+
file_object = s3.get_object(Bucket=BUCKET_NAME, Key=FILE_NAME)
56+
# Read the Body stream of the S3 object and load the JSON
57+
file_content = json.load(file_object['Body'])
58+
59+
return file_content, 200
60+
except ClientError as e:
61+
error_code = int(e.response['Error']['Code'])
62+
if error_code == 404:
63+
return { 'error': 'File not found in S3 bucket' }, 404
64+
else:
65+
return { 'error': 'Unknown error' }, 500
66+
67+
68+
if __name__ == '__main__':
69+
app.run(debug=True, host='0.0.0.0')
70+

app/requirements.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
uwsgi
2+
flask
3+
boto3

compose.yaml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
name: python-s3
2+
services:
3+
app:
4+
restart: unless-stopped
5+
build:
6+
context: ./app
7+
dockerfile: Dockerfile
8+
ports:
9+
- mode: ingress
10+
target: 5000
11+
environment:
12+
- AWS_ACCESS_KEY
13+
- AWS_SECRET_KEY
14+
deploy:
15+
resources:
16+
reservations:
17+
memory: 256M
18+
healthcheck:
19+
test:
20+
- CMD
21+
- curl
22+
- -f
23+
- http://localhost:5000/

0 commit comments

Comments
 (0)