Skip to content

Commit bfa7ac5

Browse files
authored
Feat: improve proof-of-concept app (#116)
2 parents b10916e + 3578cfd commit bfa7ac5

File tree

32 files changed

+12599
-804
lines changed

32 files changed

+12599
-804
lines changed

.devcontainer/Dockerfile

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,10 @@ RUN pip install --no-cache-dir -r docs/requirements.txt
1111
# install streamlit requirements
1212
RUN pip install --no-cache-dir -r streamlit_app/requirements.txt
1313

14-
# install AWS CLI and Copilot CLI (requires root permissions)
14+
# install AWS Copilot CLI (requires root permissions)
1515
USER root
16-
# download AWS CLIs to /tmp to avoid write error (23) from curl command
16+
# download AWS Copilot CLI to /tmp to avoid write error (23) from curl command
1717
WORKDIR /tmp
18-
RUN HOST_ARCH=$(uname -m) && \
19-
case "$HOST_ARCH" in \
20-
x86_64) HOST_ARCH="amd64" ;; \
21-
aarch64) HOST_ARCH="arm64" ;; \
22-
esac \
23-
&& if [ "$HOST_ARCH" = "amd64" ]; then \
24-
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"; \
25-
elif [ "$HOST_ARCH" = "arm64" ]; then \
26-
curl "https://awscli.amazonaws.com/awscli-exe-linux-aarch64.zip" -o "awscliv2.zip"; \
27-
fi \
28-
&& unzip awscliv2.zip \
29-
&& ./aws/install \
30-
&& rm -rf aws awscliv2.zip
3118

3219
RUN curl -Lo copilot https://github.com/aws/copilot-cli/releases/latest/download/copilot-linux \
3320
&& chmod +x copilot \

appcontainer/Dockerfile

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,23 @@ RUN useradd --create-home --shell /bin/bash $USER && \
5252
apt-get install -qq --no-install-recommends build-essential nginx gettext && \
5353
python -m pip install --upgrade pip
5454

55+
# install AWS CLI (requires root permissions)
56+
# download AWS CLI to /tmp to avoid write error (23) from curl command
57+
WORKDIR /tmp
58+
RUN HOST_ARCH=$(uname -m) && \
59+
case "$HOST_ARCH" in \
60+
x86_64) HOST_ARCH="amd64" ;; \
61+
aarch64) HOST_ARCH="arm64" ;; \
62+
esac \
63+
&& if [ "$HOST_ARCH" = "amd64" ]; then \
64+
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"; \
65+
elif [ "$HOST_ARCH" = "arm64" ]; then \
66+
curl "https://awscli.amazonaws.com/awscli-exe-linux-aarch64.zip" -o "awscliv2.zip"; \
67+
fi \
68+
&& unzip awscliv2.zip \
69+
&& ./aws/install \
70+
&& rm -rf aws awscliv2.zip
71+
5572
# enter source directory
5673
WORKDIR /$USER
5774

bin/start_aws.sh

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#!/usr/bin/env bash
2+
set -eu
3+
4+
#
5+
# S3 bucket name is injected by Copilot as an environment variable
6+
# since it was created via copilot storage init --name pems-db, the variable is 'PEMSDB_NAME'
7+
S3_BUCKET_NAME="$PEMSDB_NAME"
8+
S3_FIXTURE_PATH="fixtures.json"
9+
LOCAL_FIXTURE_PATH="fixtures.json"
10+
11+
echo "Downloading $S3_FIXTURE_PATH from bucket $S3_BUCKET_NAME"
12+
aws s3 cp "s3://${S3_BUCKET_NAME}/${S3_FIXTURE_PATH}" "${LOCAL_FIXTURE_PATH}"
13+
echo "Download complete"
14+
15+
# initialize Django
16+
17+
bin/init.sh
18+
19+
# effectively reset database by loading downloaded fixtures into the database
20+
echo "Loading data from ${LOCAL_FIXTURE_PATH}"
21+
python manage.py loaddata "${LOCAL_FIXTURE_PATH}"
22+
echo "Data loading complete"
23+
24+
# start the web server
25+
26+
nginx
27+
28+
# start the application server
29+
30+
python -m gunicorn -c $GUNICORN_CONF pems.wsgi
File renamed without changes.

copilot/streamlit/manifest.yml renamed to infra/copilot/streamlit/manifest.yml

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,21 @@
44

55
# Your service name will be used in naming your resources like log groups, ECS services, etc.
66
name: streamlit
7-
type: Backend Service
7+
type: Load Balanced Web Service
88

9-
# Your service is reachable at "http://streamlit.${COPILOT_SERVICE_DISCOVERY_ENDPOINT}:8501" but is not public.
9+
# Distribute traffic to your service.
10+
http:
11+
# Requests to this path will be forwarded to your service.
12+
# To match all requests you can use the "/" path.
13+
path: "/streamlit"
14+
healthcheck: "/streamlit/_stcore/health"
1015

1116
# Configuration for your containers and service.
1217
image:
1318
# Docker build arguments. For additional overrides: https://aws.github.io/copilot-cli/docs/manifest/backend-service/#image-build
1419
build:
15-
dockerfile: streamlit_app/Dockerfile
16-
context: .
20+
dockerfile: ../streamlit_app/Dockerfile
21+
context: ../
1722
# Port exposed through your container to route traffic to it.
1823
port: 8501
1924

@@ -30,9 +35,8 @@ network:
3035

3136
# Optional fields for more advanced use-cases.
3237
#
33-
#variables: # Pass environment variables as key value pairs.
34-
# LOG_LEVEL: info
35-
38+
variables: # Pass environment variables as key value pairs.
39+
STREAMLIT_BASE_URL: /streamlit
3640
#secrets: # Pass secrets from AWS Systems Manager (SSM) Parameter Store.
3741
# GITHUB_TOKEN: GITHUB_TOKEN # The key is the name of the environment variable, the value is the name of the SSM parameter.
3842

infra/copilot/web/addons/pems-db.yml

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
Parameters:
2+
App:
3+
Type: String
4+
Description: Your application's name.
5+
Env:
6+
Type: String
7+
Description: The environment name your service, job, or workflow is being deployed to.
8+
Name:
9+
Type: String
10+
Description: Your workload's name.
11+
Resources:
12+
pemsdbBucket:
13+
Metadata:
14+
"aws:copilot:description": "An Amazon S3 bucket to store and retrieve objects for pems-db"
15+
Type: AWS::S3::Bucket
16+
Properties:
17+
VersioningConfiguration:
18+
Status: Enabled
19+
AccessControl: Private
20+
BucketEncryption:
21+
ServerSideEncryptionConfiguration:
22+
- ServerSideEncryptionByDefault:
23+
SSEAlgorithm: AES256
24+
PublicAccessBlockConfiguration:
25+
BlockPublicAcls: true
26+
BlockPublicPolicy: true
27+
IgnorePublicAcls: true
28+
RestrictPublicBuckets: true
29+
OwnershipControls:
30+
Rules:
31+
- ObjectOwnership: BucketOwnerEnforced
32+
LifecycleConfiguration:
33+
Rules:
34+
- Id: ExpireNonCurrentObjects
35+
Status: Enabled
36+
NoncurrentVersionExpirationInDays: 30
37+
AbortIncompleteMultipartUpload:
38+
DaysAfterInitiation: 1
39+
40+
pemsdbBucketPolicy:
41+
Metadata:
42+
"aws:copilot:description": "A bucket policy to deny unencrypted access to the bucket and its contents"
43+
Type: AWS::S3::BucketPolicy
44+
DeletionPolicy: Retain
45+
Properties:
46+
PolicyDocument:
47+
Version: "2012-10-17"
48+
Statement:
49+
- Sid: ForceHTTPS
50+
Effect: Deny
51+
Principal: "*"
52+
Action: "s3:*"
53+
Resource:
54+
- !Sub ${ pemsdbBucket.Arn}/*
55+
- !Sub ${ pemsdbBucket.Arn}
56+
Condition:
57+
Bool:
58+
"aws:SecureTransport": false
59+
Bucket: !Ref pemsdbBucket
60+
61+
pemsdbAccessPolicy:
62+
Metadata:
63+
"aws:copilot:description": "An IAM ManagedPolicy for your service to access the pems-db bucket"
64+
Type: AWS::IAM::ManagedPolicy
65+
Properties:
66+
Description: !Sub
67+
- Grants CRUD access to the S3 bucket ${Bucket}
68+
- { Bucket: !Ref pemsdbBucket }
69+
PolicyDocument:
70+
Version: "2012-10-17"
71+
Statement:
72+
- Sid: S3ObjectActions
73+
Effect: Allow
74+
Action:
75+
- s3:GetObject
76+
- s3:PutObject
77+
- s3:PutObjectACL
78+
- s3:PutObjectTagging
79+
- s3:DeleteObject
80+
- s3:RestoreObject
81+
Resource: !Sub ${ pemsdbBucket.Arn}/*
82+
- Sid: S3ListAction
83+
Effect: Allow
84+
Action: s3:ListBucket
85+
Resource: !Sub ${ pemsdbBucket.Arn}
86+
87+
Outputs:
88+
pemsdbName:
89+
Description: "The name of a user-defined bucket."
90+
Value: !Ref pemsdbBucket
91+
pemsdbAccessPolicy:
92+
Description: "The IAM::ManagedPolicy to attach to the task role"
93+
Value: !Ref pemsdbAccessPolicy

copilot/web/manifest.yml renamed to infra/copilot/web/manifest.yml

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,37 +10,38 @@ type: Load Balanced Web Service
1010
http:
1111
# Requests to this path will be forwarded to your service.
1212
# To match all requests you can use the "/" path.
13-
path: '/'
13+
path: "/"
1414
# You can specify a custom health check path. The default is "/".
15-
healthcheck: '/healthcheck'
15+
healthcheck: "/healthcheck"
1616

1717
# Configuration for your containers and service.
1818
image:
1919
# Docker build arguments. For additional overrides: https://aws.github.io/copilot-cli/docs/manifest/lb-web-service/#image-build
2020
build:
21-
dockerfile: appcontainer/Dockerfile
22-
context: .
21+
dockerfile: ../appcontainer/Dockerfile
22+
context: ../
2323
# Port exposed through your container to route traffic to it.
2424
port: 8000
2525

26-
cpu: 256 # Number of CPU units for the task.
27-
memory: 512 # Amount of memory in MiB used by the task.
28-
platform: linux/x86_64 # See https://aws.github.io/copilot-cli/docs/manifest/lb-web-service/#platform
29-
count: 1 # Number of tasks that should be running in your service.
30-
exec: true # Enable running commands in your container.
26+
cpu: 256 # Number of CPU units for the task.
27+
memory: 512 # Amount of memory in MiB used by the task.
28+
platform: linux/x86_64 # See https://aws.github.io/copilot-cli/docs/manifest/lb-web-service/#platform
29+
count: 1 # Number of tasks that should be running in your service.
30+
exec: true # Enable running commands in your container.
31+
command: bin/start_aws.sh
3132
network:
3233
connect: true # Enable Service Connect for intra-environment traffic between services.
3334

3435
# storage:
35-
# readonly_fs: true # Limit to read-only access to mounted root filesystems.
36+
# readonly_fs: true # Limit to read-only access to mounted root filesystems.
3637

3738
# Optional fields for more advanced use-cases.
3839
#
39-
#variables: # Pass environment variables as key value pairs.
40-
# LOG_LEVEL: info
40+
variables: # Pass environment variables as key value pairs.
41+
STREAMLIT_URL: /streamlit
4142

42-
secrets: # Pass secrets from AWS Systems Manager (SSM) Parameter Store.
43-
DJANGO_ALLOWED_HOSTS: /pems/web/DJANGO_ALLOWED_HOSTS # The key is the name of the environment variable, the value is the name of the SSM parameter.
43+
secrets: # Pass secrets from AWS Systems Manager (SSM) Parameter Store.
44+
DJANGO_ALLOWED_HOSTS: /pems/web/DJANGO_ALLOWED_HOSTS # The key is the name of the environment variable, the value is the name of the SSM parameter.
4445

4546
# You can override any of the values defined above by environment.
4647
#environments:

pems/core/context_processors.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,16 @@
33
"""
44

55
from pems import __version__
6+
from django.conf import settings
67

78

89
def pems_version(request):
910
"""Context processor adds information about the PeMS application's version."""
1011

1112
return {"pems_version": __version__}
13+
14+
15+
def streamlit(request):
16+
"""Context processor adds Streamlit-related information."""
17+
18+
return {"streamlit": {"url": settings.STREAMLIT_URL}}

pems/districts/templates/districts/district.html

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,11 @@
99
<div class="col-lg-4 border">
1010
<h2>Form</h2>
1111
</div>
12-
<div class="col-lg-8 border">
13-
<h2>Chart</h2>
14-
</div>
1512
</div>
16-
<div class="row">
17-
<div class="col-lg-4 border">
18-
<h2>Details for {{ current_district.name }}</h2>
19-
</div>
20-
<div class="col-lg-8 border">
21-
<h2>Map</h2>
13+
<div class="row" style="min-height: 450px;">
14+
<div class="col-lg-12 border">
15+
<iframe class="w-100 h-100" src="{{ streamlit.url }}/stations--stations?embed=true&district_number={{ current_district.number }}">
16+
</iframe>
2217
</div>
2318
</div>
2419
{% endblock districts-content %}

pems/districts/templates/districts/index.html

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
{% endblock sidebar %}
1414
</div>
1515
<div class="col-lg-10 pb-lg-5">
16-
1716
{% block districts-content %}
1817
<div class="row">
1918
<div class="col-lg-4 border">
@@ -23,16 +22,13 @@ <h2>Form</h2>
2322
<h2>Chart</h2>
2423
</div>
2524
</div>
26-
<div class="row">
27-
<div class="col-lg-4 border">
28-
<h2>Details</h2>
29-
</div>
30-
<div class="col-lg-8 border">
31-
<h2>Map</h2>
25+
<div class="row" style="min-height: 450px;">
26+
<div class="col-lg-12 border">
27+
<iframe class="w-100 h-100" src="{{ streamlit.url }}/stations--stations?embed=true">
28+
</iframe>
3229
</div>
3330
</div>
3431
{% endblock districts-content %}
35-
3632
</div>
3733
</div>
3834
</div>

pems/settings.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ def _filter_empty(ls):
3333
"django.contrib.staticfiles",
3434
"pems.core",
3535
"pems.districts",
36-
"pems.streamlit_sample",
3736
]
3837

3938
MIDDLEWARE = [
@@ -61,6 +60,7 @@ def _filter_empty(ls):
6160
"django.contrib.auth.context_processors.auth",
6261
"django.contrib.messages.context_processors.messages",
6362
"pems.core.context_processors.pems_version",
63+
"pems.core.context_processors.streamlit",
6464
],
6565
},
6666
},
@@ -138,3 +138,6 @@ def _filter_empty(ls):
138138
# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field
139139

140140
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
141+
142+
# Streamlit settings
143+
STREAMLIT_URL = os.environ.get("STREAMLIT_URL", "http://localhost:8501")

pems/streamlit_sample/apps.py

Lines changed: 0 additions & 7 deletions
This file was deleted.

pems/streamlit_sample/templates/streamlit_sample/index.html

Lines changed: 0 additions & 6 deletions
This file was deleted.

pems/streamlit_sample/urls.py

Lines changed: 0 additions & 6 deletions
This file was deleted.

pems/urls.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,4 @@
55
path("", include("pems.core.urls")),
66
path("admin/", admin.site.urls),
77
path("districts/", include("pems.districts.urls")),
8-
path("streamlit/", include("pems.streamlit_sample.urls")),
98
]

streamlit_app/Dockerfile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,10 @@ ENV PYTHONPATH="$PYTHONPATH:/$USER/app"
3333

3434
EXPOSE 8501
3535

36+
COPY .streamlit .streamlit
37+
3638
COPY streamlit_app streamlit_app
3739

3840
RUN pip install -r streamlit_app/requirements.txt
3941

40-
ENTRYPOINT ["streamlit", "run", "streamlit_app/main.py", "--server.port=8501", "--server.address=0.0.0.0"]
42+
ENTRYPOINT ["./streamlit_app/entrypoint.sh"]

streamlit_app/apps/sample/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)