Skip to content
Open

Lab8 #2477

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
720bc30
first lab
KaramKhaddour Jan 30, 2025
a115c0b
second lab
KaramKhaddour Jan 30, 2025
67df90f
Merge branch 'lab2' into lab3
Kokai14 Feb 5, 2025
8d211c7
CI pipleline
Kokai14 Feb 6, 2025
a942194
CI pipleline
KaramKhaddour Feb 6, 2025
0300b93
CI pipleline
KaramKhaddour Feb 6, 2025
78b9530
Create CI.yml
KaramKhaddour Feb 6, 2025
566c80e
docker CI actions
KaramKhaddour Feb 6, 2025
3f8ec26
docker CI actions
KaramKhaddour Feb 6, 2025
2946c8b
docker CI actions
KaramKhaddour Feb 6, 2025
43c2cd2
docker CI actions
KaramKhaddour Feb 6, 2025
2e05437
correct the python files for the linter
KaramKhaddour Feb 6, 2025
3b5eeb6
put the correct docker names
KaramKhaddour Feb 6, 2025
8cb27c9
change the name of the docker image
KaramKhaddour Feb 6, 2025
92d38cf
change the name of the docker image
KaramKhaddour Feb 6, 2025
1a4d3a3
change the context for the docker image
KaramKhaddour Feb 6, 2025
d9c04f8
correct docker file name
KaramKhaddour Feb 6, 2025
68a5d59
Update README.md
KaramKhaddour Feb 6, 2025
8ab8630
Update README.md
KaramKhaddour Feb 6, 2025
2e6ddae
add CI badge
KaramKhaddour Feb 6, 2025
b5d25c6
utlize build cache
KaramKhaddour Feb 6, 2025
ad0b1a0
fix CI.yml
KaramKhaddour Feb 6, 2025
022b6db
fix CI.yml
KaramKhaddour Feb 6, 2025
95b955e
fix the working directories
KaramKhaddour Feb 6, 2025
cc07049
fix the requirements to have the linter
KaramKhaddour Feb 6, 2025
31eb22b
fix CI.yml
KaramKhaddour Feb 6, 2025
a88efce
fix CI.yml
KaramKhaddour Feb 6, 2025
644d70e
fix CI.yml
KaramKhaddour Feb 6, 2025
4414a07
SNYK
KaramKhaddour Feb 6, 2025
57705b6
add CI markdown
KaramKhaddour Feb 6, 2025
8935aed
lab4
Kokai14 Feb 12, 2025
0ca295c
Merge branch 'master' into lab4
Kokai14 Feb 12, 2025
7a288de
Merge branch 'master' into lab4
KaramKhaddour Mar 1, 2025
ac240ae
lab1
KaramKhaddour May 24, 2025
56899ac
lab4
KaramKhaddour May 25, 2025
0dede95
save work
KaramKhaddour Feb 14, 2026
b639b25
save work
KaramKhaddour Feb 14, 2026
643d264
create logging.md
KaramKhaddour Feb 15, 2026
76ea4e5
lab8
KaramKhaddour Feb 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
name: CI Pipeline

on: push

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
with:
python-version: "3.11"
- name: Install Dependencies
working-directory: app_python
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Lint Code
working-directory: app_python
run: flake8 .

test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
with:
python-version: "3.11"
- name: Install Dependencies
working-directory: app_python
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Run Tests
working-directory: app_python
run: pytest

snyk:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.11"
- name: Install Snyk CLI
run: |
npm install -g snyk
- name: Run Snyk Vulnerability Test
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
run: snyk test --all-projects

docker:
needs: [lint, test, snyk]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and Push Docker Image
uses: docker/build-push-action@v6
with:
context: app_python
push: true
tags: ${{ secrets.DOCKER_USERNAME }}/my-fastapi-app:latest
2 changes: 2 additions & 0 deletions app_python/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
__pycache__/
*.pyc
3 changes: 3 additions & 0 deletions app_python/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
*.pyc
__pycache__/
.pytest_cache/
13 changes: 13 additions & 0 deletions app_python/CI.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# CI Pipeline Best Practices

- **Job Separation**: Divided pipeline into linting, testing, security scanning, and Docker build jobs for clarity and maintainability.

- **`working-directory` Usage**: Ensures tasks run in the correct directory (`app_python`).

- **Efficient Dependency Management**: Installs dependencies from `requirements.txt` and upgrades `pip`.

- **Parallel Job Execution**: Runs linting, testing, and security scanning in parallel to speed up the pipeline.

- **Security with Snyk**: Uses **Snyk** for vulnerability scanning, with API token stored in GitHub Secrets.

- **Docker Integration**: Secures Docker Hub login and only builds and pushes Docker image after passing jobs.
20 changes: 20 additions & 0 deletions app_python/DOCKER.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Docker Best Practices

## Security

- The application **does not run as root**, ensuring better security.
- A dedicated **non-root user** (`appuser`) is created to execute the app.

## Efficiency

- **Using a minimal base image (`python:3.10-slim`)** to keep the image lightweight.
- **Avoiding unnecessary layers**: Commands are ordered logically to leverage Docker layer caching.
- **Using `.dockerignore`** to exclude unnecessary files (cache).

## Maintainability

- The `CMD` statement starts the FastAPI application using **Uvicorn**, keeping it lightweight.
- Dependencies are installed using `pip --no-cache-dir` to **avoid unnecessary bloat**.
- The FastAPI app files are **copied with ownership (`COPY --chown`)** to prevent permission issues.

---
19 changes: 19 additions & 0 deletions app_python/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
FROM python:3.10-slim

ENV PYTHONUNBUFFERED=1 \
PIP_NO_CACHE_DIR=1 \
PIP_DISABLE_PIP_VERSION_CHECK=1

RUN groupadd -r appgroup && useradd -r -g appgroup appuser
WORKDIR /app
COPY --chown=appuser:appgroup requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY --chown=appuser:appgroup main.py .
COPY --chown=appuser:appgroup templates/ templates/

USER appuser

EXPOSE 8000

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
31 changes: 31 additions & 0 deletions app_python/PYTHON.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Develop and Test Python Web Application

## Choosing a Framework

For this application, **FastAPI** was chosen due to its:

- **High performance** (asynchronous support with `uvicorn`)
- **Built-in support for type hints** and automatic API documentation
- **Ease of development and maintainability**

## Best Practices Applied

- **Separation of Concerns:** HTML templates are separate from Python logic using Jinja2.

- **Code Readability:** Good function names and code structure.
- **Modular Design:** Using FastAPI and template rendering makes the project scalable.

## Python Unit Tests

### Best Practices

- Naming of the tests describe the intent of the test clearly
- Testing only one concern with each testing fucntion
- Using the AAA pattern (Arange, Act, Assert)

### Unit Test Overview

The function `get_moscow_time()` is tested to verify:

- Correct dictionary structure.
- Valid time values.
84 changes: 84 additions & 0 deletions app_python/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
[![CI Pipeline](https://github.com/KaramKhaddour/S25-core-course-labs/actions/workflows/CI.yml/badge.svg)](https://github.com/KaramKhaddour/S25-core-course-labs/actions/workflows/CI.yml)

# Moscow Time Display Application (FastAPI)

## Overview

This application is built using FastAPI and provides the current time in Moscow. It is lightweight and fast.

## Prerequisites

Ensure you have Python installed on your system. This application requires Python 3.7 or later.

## Installation

1. Clone the repository :

```sh
git clone https://github.com/KaramKhaddour/S25-core-course-labs.git
cd S25-CORE-CORSE-LABS/app_python
```

2. Install the required dependencies:

```sh
pip install -r requirements.txt
```

## Running the Application

To start the FastAPI server, run the following command:

```sh
uvicorn main:app --reload
```

This will start the development server and enable automatic reloading for changes in the source code.

## Accessing the API

Once the application is running, you can access it via:

- **API Endpoint:** `http://127.0.0.1:8000` (or another specified host/port)
- **Interactive API Documentation:**
- Swagger UI: `http://127.0.0.1:8000/docs`

## Docker Instructions

This application can be built and run as a Docker container.

### Build the Docker Image

To build the Docker image, run:

```bash
docker build -t my-fastapi-app .
```

### Run the Docker Container

To run the container:

```bash
docker run -d -p 8000:8000 my-fastapi-app
```

### Pull the Docker Image

If you have pushed your Docker image to Docker Hub, pull it using:

```bash
docker pull karamkhaddourpro/my-fastapi-app
```

### Running the Pulled Image

```bash
docker run -d -p 8000:8000 karamkhaddourpro/my-fastapi-app
```

### Running tests

```bash
pytest
```
47 changes: 47 additions & 0 deletions app_python/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import datetime as dt
import pytz
import time
from fastapi import FastAPI, Response
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
from fastapi.requests import Request
from prometheus_client import Summary, Counter, generate_latest, CONTENT_TYPE_LATEST

# Initialize FastAPI app
app = FastAPI()

# Set up Jinja2 for rendering templates
templates = Jinja2Templates(directory="templates")


def get_moscow_time():
"""Returns the current time in Moscow as a dictionary."""
now = dt.datetime.now(pytz.timezone('Europe/Moscow'))
return {"hours": now.hour, "minutes": now.minute, "seconds": now.second}


@app.get("/", response_class=HTMLResponse)
async def show_moscow_time(request: Request):
"""Renders the Moscow time page."""
time_data = get_moscow_time()
return templates.TemplateResponse("index.html",
{"request": request, **time_data})


# Prometheus metrics definitions
REQUEST_TIME = Summary(
"request_processing_seconds",
"Time spent processing request"
)

REQUEST_COUNT = Counter(
"request_total",
"Total number of requests"
)

@app.get("/metrics")
def metrics():
return Response(
generate_latest(),
media_type=CONTENT_TYPE_LATEST
)
8 changes: 8 additions & 0 deletions app_python/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
fastapi==0.111.1
fastapi-cli==0.0.4
pytz==2022.1
uvicorn==0.30.3
jinja2==3.1.4
pytest==8.3.4
flake8==7.1.1
prometheus_client==0.20.0
Loading