Skip to content

Commit e5478b9

Browse files
authored
Merge pull request #3 from zzhlogin/python_sample_application
Python sample application
2 parents 99c326f + a34ba4d commit e5478b9

File tree

32 files changed

+599
-0
lines changed

32 files changed

+599
-0
lines changed

sample-apps/python/README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Python Demo Sample App Updating Guide
2+
3+
## Introduction:
4+
5+
The python sample app is used to perform E2E testing on cloudwatch, cloudwatch operator and adot repository. If any changes need to be made on the demo sample app, the following steps should be taken.
6+
7+
## EKS Use Case: Uploading to ECR
8+
9+
### Build the sample app locally:
10+
run `docker-compose build` to generate two docker images: `pythonsampleapp/frontend-service` and `pythonsampleapp/remote-service`
11+
12+
### Steps to update image:
13+
1. Login to the testing account
14+
2. Create a new ECR repository if there's no existing one.
15+
3. Login to ECR Repository: `aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin {REPOSITORY}`.
16+
4. tag images and push to repository:
17+
```
18+
docker tag pythonsampleapp/frontend-service:latest ${REPOSITORY_PREFIX}/pythonsampleapp/frontend-service:latest
19+
docker push ${REPOSITORY_PREFIX}/pythonsampleapp/frontend-service:latest
20+
docker tag pythonsampleapp/remote-service:latest ${REPOSITORY_PREFIX}/pythonsampleapp/remote-service:latest
21+
docker push ${REPOSITORY_PREFIX}/pythonsampleapp/remote-service:latest
22+
```
23+
24+
25+
## EC2 Use Case: Building the JAR Files:
26+
1. Compress the folder with: `zip -r python-sample-app.zip .`
27+
2. Login to the testing account
28+
3. Create a new S3 bucket if there's no existing one.
29+
4. upload `python-sample-app.zip` to the bucket
30+
31+
32+
## APIs
33+
The following are the APIs supported:
34+
1. http://${ FRONTEND_SERVICE_IP }:8000/outgoing-http-call/
35+
2. http://${ FRONTEND_SERVICE_IP }:8000/aws-sdk-call/
36+
3. http://${ FRONTEND_SERVICE_IP }:8000/remote-service?ip=${ REMOTE_SERVICE_IP }/
37+
4. http://${ FRONTEND_SERVICE_IP }:8000/client-call/
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
static
2+
.idea
3+
.env
4+
db.sqlite3
5+
*.iml
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
## SPDX-License-Identifier: Apache-2.0
3+
4+
# Use an official Python runtime as a parent image
5+
FROM python:3.10
6+
7+
# Set environment variables
8+
ENV PYTHONDONTWRITEBYTECODE 1
9+
ENV PYTHONUNBUFFERED 1
10+
11+
# Set the working directory
12+
WORKDIR /django_frontend_app
13+
14+
# Install dependencies
15+
COPY django_frontend_service/requirements.txt /django_frontend_app/
16+
RUN pip install -r requirements.txt
17+
18+
# Copy the project code into the container
19+
COPY django_frontend_service/. /django_frontend_app/
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
## SPDX-License-Identifier: Apache-2.0
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
## SPDX-License-Identifier: Apache-2.0
3+
import os
4+
5+
from django.core.asgi import get_asgi_application
6+
7+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_frontend_service.settings")
8+
9+
application = get_asgi_application()
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
## SPDX-License-Identifier: Apache-2.0
3+
import os
4+
from pathlib import Path
5+
from dotenv import load_dotenv
6+
7+
load_dotenv()
8+
# Build paths inside the project like this: BASE_DIR / 'subdir'.
9+
BASE_DIR = Path(__file__).resolve().parent.parent
10+
11+
12+
# Quick-start development settings - unsuitable for production
13+
# See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/
14+
15+
# SECURITY WARNING: keep the secret key used in production secret!
16+
SECRET_KEY = "django-insecure-!=+@($z#-ylp@au_@ey=9nh701y-p@_n-^c23k_&ia!o8catit"
17+
18+
# SECURITY WARNING: don't run with debug turned on in production!
19+
DEBUG = True
20+
21+
ALLOWED_HOSTS = ["*"]
22+
23+
24+
# Application definition
25+
# settings.py
26+
INSTALLED_APPS = [
27+
"django.contrib.admin",
28+
"django.contrib.auth",
29+
"django.contrib.contenttypes",
30+
"django.contrib.sessions",
31+
"django.contrib.messages",
32+
"django.contrib.staticfiles",
33+
]
34+
35+
MIDDLEWARE = [
36+
"django.middleware.security.SecurityMiddleware",
37+
"django.contrib.sessions.middleware.SessionMiddleware",
38+
"django.middleware.common.CommonMiddleware",
39+
"django.middleware.csrf.CsrfViewMiddleware",
40+
"django.contrib.auth.middleware.AuthenticationMiddleware",
41+
"django.contrib.messages.middleware.MessageMiddleware",
42+
"django.middleware.clickjacking.XFrameOptionsMiddleware",
43+
]
44+
45+
ROOT_URLCONF = "django_frontend_service.urls"
46+
47+
TEMPLATES = [
48+
{
49+
"BACKEND": "django.template.backends.django.DjangoTemplates",
50+
"DIRS": [],
51+
"APP_DIRS": True,
52+
"OPTIONS": {
53+
"context_processors": [
54+
"django.template.context_processors.debug",
55+
"django.template.context_processors.request",
56+
"django.contrib.auth.context_processors.auth",
57+
"django.contrib.messages.context_processors.messages",
58+
],
59+
},
60+
},
61+
]
62+
63+
WSGI_APPLICATION = "django_frontend_service.wsgi.application"
64+
65+
66+
# Database
67+
# https://docs.djangoproject.com/en/5.0/ref/settings/#databases
68+
69+
DATABASES = {
70+
"default": {
71+
"ENGINE": "django.db.backends.sqlite3",
72+
"NAME": BASE_DIR / "db.sqlite3",
73+
}
74+
}
75+
76+
77+
# Password validation
78+
# https://docs.djangoproject.com/en/5.0/ref/settings/#auth-password-validators
79+
80+
AUTH_PASSWORD_VALIDATORS = [
81+
{
82+
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
83+
},
84+
{
85+
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
86+
},
87+
{
88+
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
89+
},
90+
{
91+
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
92+
},
93+
]
94+
95+
96+
# Internationalization
97+
# https://docs.djangoproject.com/en/5.0/topics/i18n/
98+
99+
LANGUAGE_CODE = "en-us"
100+
101+
TIME_ZONE = "UTC"
102+
103+
USE_I18N = True
104+
105+
USE_TZ = True
106+
107+
108+
# Static files (CSS, JavaScript, Images)
109+
# https://docs.djangoproject.com/en/5.0/howto/static-files/
110+
111+
STATIC_URL = "static/"
112+
STATIC_ROOT = os.path.join(BASE_DIR, "static")
113+
114+
# Default primary key field type
115+
# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field
116+
117+
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
## SPDX-License-Identifier: Apache-2.0
3+
from django.urls import path
4+
from frontend_service_app import views
5+
6+
urlpatterns = [
7+
path('', views.healthcheck),
8+
path('aws-sdk-call', views.aws_sdk_call),
9+
path('outgoing-http-call', views.http_call),
10+
path('remote-service', views.downstream_service),
11+
path('client-call', views.async_service),
12+
]
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
## SPDX-License-Identifier: Apache-2.0
3+
import os
4+
5+
from django.core.wsgi import get_wsgi_application
6+
7+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_frontend_service.settings")
8+
9+
application = get_wsgi_application()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
## SPDX-License-Identifier: Apache-2.0
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
## SPDX-License-Identifier: Apache-2.0
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
## SPDX-License-Identifier: Apache-2.0
3+
from django.apps import AppConfig
4+
5+
6+
class FrontendServiceAppConfig(AppConfig):
7+
default_auto_field = "django.db.models.BigAutoField"
8+
name = "frontend_service_app"
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
## SPDX-License-Identifier: Apache-2.0
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
## SPDX-License-Identifier: Apache-2.0
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
## SPDX-License-Identifier: Apache-2.0
3+
from django.http import HttpResponse, JsonResponse
4+
import boto3
5+
import logging
6+
import requests
7+
import schedule
8+
import time
9+
import threading
10+
from opentelemetry import trace
11+
from opentelemetry.trace.span import format_trace_id
12+
13+
logger = logging.getLogger(__name__)
14+
15+
should_send_local_root_client_call = False
16+
lock = threading.Lock()
17+
def run_local_root_client_call_recurring_service():
18+
def runnable_task():
19+
global should_send_local_root_client_call
20+
with lock:
21+
if should_send_local_root_client_call:
22+
should_send_local_root_client_call = False
23+
try:
24+
response = requests.get("http://local-root-client-call")
25+
# Handle the response if needed
26+
except Exception as e:
27+
# Handle exceptions
28+
pass
29+
30+
# Schedule the task to run every 1 second
31+
schedule.every(1).seconds.do(runnable_task)
32+
33+
# Run the scheduler in a separate thread
34+
def run_scheduler():
35+
while True:
36+
schedule.run_pending()
37+
time.sleep(0.1) # Sleep to prevent high CPU usage
38+
39+
thread = threading.Thread(target=run_scheduler)
40+
thread.daemon = True # Daemonize the thread so it exits when the main thread exits
41+
thread.start()
42+
43+
run_local_root_client_call_recurring_service()
44+
45+
def healthcheck(request):
46+
return HttpResponse("healthcheck")
47+
48+
def aws_sdk_call(request):
49+
bucket_name = "e2e-test-bucket-name"
50+
s3_client = boto3.client("s3")
51+
52+
try:
53+
s3_client.get_bucket_location(
54+
Bucket=bucket_name,
55+
)
56+
except Exception as e:
57+
logger.error("Could not retrieve http request:" + str(e))
58+
59+
return get_xray_trace_id()
60+
61+
def http_call(request):
62+
url = "https://www.amazon.com"
63+
try:
64+
response = requests.get(url)
65+
status_code = response.status_code
66+
logger.info("outgoing-http-call status code: " + str(status_code))
67+
except Exception as e:
68+
logger.error("Could not complete http request:" + str(e))
69+
return get_xray_trace_id()
70+
71+
def downstream_service(request):
72+
ip = request.GET.get('ip', '')
73+
ip = ip.replace("/", "")
74+
url = f"http://{ip}:8001/health-check"
75+
try:
76+
response = requests.get(url)
77+
status_code = response.status_code
78+
logger.info("Remote service call status code: " + str(status_code))
79+
return get_xray_trace_id()
80+
except Exception as e:
81+
logger.error("Could not complete http request to remote service:" + str(e))
82+
83+
return get_xray_trace_id()
84+
85+
def async_service(request):
86+
global should_send_local_root_client_call
87+
# Log the request
88+
logger.info("Client-call received")
89+
# Set the condition to trigger the recurring service
90+
with lock:
91+
should_send_local_root_client_call = True
92+
# Return a dummy traceId in the response
93+
return JsonResponse({'traceId': '1-00000000-000000000000000000000000'})
94+
95+
def get_xray_trace_id():
96+
span = trace.get_current_span()
97+
trace_id = format_trace_id(span.get_span_context().trace_id)
98+
xray_trace_id = f"1-{trace_id[:8]}-{trace_id[8:]}"
99+
100+
return JsonResponse({"traceId": xray_trace_id})
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#!/usr/bin/env python
2+
## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
## SPDX-License-Identifier: Apache-2.0
4+
import os
5+
import sys
6+
7+
8+
def main():
9+
"""Run administrative tasks."""
10+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_frontend_service.settings")
11+
try:
12+
from django.core.management import execute_from_command_line
13+
except ImportError as exc:
14+
raise ImportError(
15+
"Couldn't import Django. Are you sure it's installed and "
16+
"available on your PYTHONPATH environment variable? Did you "
17+
"forget to activate a virtual environment?"
18+
) from exc
19+
execute_from_command_line(sys.argv)
20+
21+
22+
if __name__ == "__main__":
23+
main()
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Django~=4.2.9
2+
requests~=2.31.0
3+
boto3~=1.34.3
4+
requests~=2.31.0
5+
schedule~=1.2.1
6+
python-dotenv~=1.0.0
7+
opentelemetry-api~=1.22.0
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
static
2+
.idea
3+
.env
4+
db.sqlite3
5+
*.iml

0 commit comments

Comments
 (0)