update example
parent
6535670cd3
commit
fee6fdec2b
15
Dockerfile
15
Dockerfile
|
|
@ -1,8 +1,11 @@
|
||||||
FROM python:3.11-slim
|
FROM python:3.11-slim
|
||||||
|
|
||||||
# Install gcc compiler for C code compilation
|
# Create a non-root user for security
|
||||||
|
RUN useradd --create-home --shell /bin/bash app
|
||||||
|
|
||||||
|
# Install gcc compiler for C code compilation and other production dependencies
|
||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
apt-get install -y gcc build-essential && \
|
apt-get install -y gcc build-essential curl && \
|
||||||
rm -rf /var/lib/apt/lists/*
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Set working directory
|
# Set working directory
|
||||||
|
|
@ -15,8 +18,12 @@ RUN pip install --no-cache-dir -r requirements.txt
|
||||||
# Copy application code
|
# Copy application code
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
|
# Change ownership to the app user
|
||||||
|
RUN chown -R app:app /app
|
||||||
|
USER app
|
||||||
|
|
||||||
# Expose port 5000
|
# Expose port 5000
|
||||||
EXPOSE 5000
|
EXPOSE 5000
|
||||||
|
|
||||||
# Run the application with Gunicorn
|
# Run the application with Gunicorn in production mode using config file
|
||||||
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "1", "app:app"]
|
CMD ["gunicorn", "--config", "gunicorn.conf.py", "app:app"]
|
||||||
185
README.md
185
README.md
|
|
@ -70,6 +70,38 @@ The system uses Markdown files for content management. There are two main types
|
||||||
|
|
||||||
The content directory is located at the parent directory level, outside of the `elemes/` directory. This allows for easy linking and sharing of content between different instances of the LMS.
|
The content directory is located at the parent directory level, outside of the `elemes/` directory. This allows for easy linking and sharing of content between different instances of the LMS.
|
||||||
|
|
||||||
|
### Environment Configuration
|
||||||
|
|
||||||
|
The application uses environment variables for configuration. Create a `.env` file at the parent directory level (same level as the `elemes/` directory) to configure the application. The `podman-compose.yml` file is configured to read environment variables from `../.env`.
|
||||||
|
|
||||||
|
Example `.env` file:
|
||||||
|
```
|
||||||
|
# Production Environment Configuration
|
||||||
|
|
||||||
|
# Flask Configuration
|
||||||
|
FLASK_ENV=production
|
||||||
|
FLASK_DEBUG=0
|
||||||
|
SECRET_KEY=your-super-secret-key-here-change-this-in-production
|
||||||
|
|
||||||
|
# Application Configuration
|
||||||
|
CONTENT_DIR=content
|
||||||
|
STATIC_DIR=static
|
||||||
|
TEMPLATES_DIR=templates
|
||||||
|
TOKENS_FILE=tokens.csv
|
||||||
|
|
||||||
|
# Server Configuration
|
||||||
|
HOST=0.0.0.0
|
||||||
|
PORT=5000
|
||||||
|
|
||||||
|
# Security Configuration
|
||||||
|
MAX_CONTENT_LENGTH=1024 * 1024 # 1MB max upload size
|
||||||
|
WTF_CSRF_ENABLED=true
|
||||||
|
|
||||||
|
# Logging Configuration
|
||||||
|
LOG_LEVEL=INFO
|
||||||
|
LOG_FILE=/var/log/lms-c/app.log
|
||||||
|
```
|
||||||
|
|
||||||
### Home Page (`home.md`)
|
### Home Page (`home.md`)
|
||||||
|
|
||||||
The home page serves as the main landing page and lesson directory. Here's the template structure:
|
The home page serves as the main landing page and lesson directory. Here's the template structure:
|
||||||
|
|
@ -392,13 +424,121 @@ This LMS can be used as a submodule in another repository. To set it up:
|
||||||
|
|
||||||
3. Add your lesson files to the content directory
|
3. Add your lesson files to the content directory
|
||||||
|
|
||||||
4. Run the LMS from the elemes directory:
|
4. Create an environment configuration file at the root level:
|
||||||
|
```bash
|
||||||
|
touch .env
|
||||||
|
```
|
||||||
|
|
||||||
|
5. Run the LMS from the elemes directory:
|
||||||
```bash
|
```bash
|
||||||
cd elemes
|
cd elemes
|
||||||
podman-compose -f podman-compose.yml up --build
|
podman-compose -f podman-compose.yml up --build
|
||||||
```
|
```
|
||||||
|
|
||||||
The content directory at the root level will be automatically mounted to the application container.
|
The content directory and environment configuration at the root level will be automatically mounted to the application container.
|
||||||
|
|
||||||
|
## Production Deployment
|
||||||
|
|
||||||
|
For production deployment, additional considerations and configurations are required to ensure security, performance, and reliability.
|
||||||
|
|
||||||
|
### Production Environment Setup
|
||||||
|
|
||||||
|
1. **Environment Variables**: Use the following environment variables for production:
|
||||||
|
- `FLASK_ENV=production` - Sets the environment to production
|
||||||
|
- `FLASK_DEBUG=0` - Disables debug mode
|
||||||
|
- `PORT=5000` - Specifies the port to run the application on
|
||||||
|
|
||||||
|
2. **Security Considerations**:
|
||||||
|
- Run the container as a non-root user (already configured in Dockerfile)
|
||||||
|
- Limit resource usage with podman/docker resource constraints
|
||||||
|
- Use a reverse proxy (like Nginx) in front of the application
|
||||||
|
- Implement proper HTTPS with SSL/TLS certificates
|
||||||
|
|
||||||
|
3. **Performance Optimizations**:
|
||||||
|
- Use the multi-worker Gunicorn configuration (already configured)
|
||||||
|
- Set appropriate worker processes based on your server's CPU cores
|
||||||
|
- Configure proper caching mechanisms if needed
|
||||||
|
|
||||||
|
### Production Deployment with Podman
|
||||||
|
|
||||||
|
To deploy in a production environment:
|
||||||
|
|
||||||
|
1. Build the production image:
|
||||||
|
```bash
|
||||||
|
podman build -t lms-c:prod .
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Run with production settings:
|
||||||
|
```bash
|
||||||
|
podman run -d \
|
||||||
|
--name lms-c-prod \
|
||||||
|
-p 80:5000 \
|
||||||
|
-v /path/to/production/content:/app/content \
|
||||||
|
-v /path/to/production/static:/app/static \
|
||||||
|
-v /path/to/production/tokens.csv:/app/tokens.csv \
|
||||||
|
--restart=unless-stopped \
|
||||||
|
--memory=512m \
|
||||||
|
--cpus=1 \
|
||||||
|
lms-c:prod
|
||||||
|
```
|
||||||
|
|
||||||
|
### Production Deployment with Podman Compose
|
||||||
|
|
||||||
|
Create a production-specific compose file (`podman-compose.prod.yml`):
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
lms-c:
|
||||||
|
build: .
|
||||||
|
ports:
|
||||||
|
- "80:5000"
|
||||||
|
volumes:
|
||||||
|
- /path/to/production/content:/app/content
|
||||||
|
- /path/to/production/static:/app/static
|
||||||
|
- /path/to/production/templates:/app/templates
|
||||||
|
- /path/to/production/tokens.csv:/app/tokens.csv
|
||||||
|
env_file:
|
||||||
|
- /path/to/production/.env
|
||||||
|
restart: unless-stopped
|
||||||
|
mem_limit: 512m
|
||||||
|
cpus: 1.0
|
||||||
|
```
|
||||||
|
|
||||||
|
Run with:
|
||||||
|
```bash
|
||||||
|
podman-compose -f podman-compose.prod.yml up -d --build
|
||||||
|
```
|
||||||
|
|
||||||
|
### Reverse Proxy Configuration
|
||||||
|
|
||||||
|
For production, it's recommended to put a reverse proxy like Nginx in front of the application:
|
||||||
|
|
||||||
|
Example Nginx configuration:
|
||||||
|
```
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name your-domain.com;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://localhost:5000;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### SSL/HTTPS Setup
|
||||||
|
|
||||||
|
For HTTPS, you can use Let's Encrypt with Certbot:
|
||||||
|
```bash
|
||||||
|
sudo certbot --nginx -d your-domain.com
|
||||||
|
```
|
||||||
|
|
||||||
|
This will automatically configure SSL certificates for your domain.
|
||||||
|
|
||||||
## Security Considerations
|
## Security Considerations
|
||||||
|
|
||||||
|
|
@ -416,8 +556,12 @@ The content directory at the root level will be automatically mounted to the app
|
||||||
- `tokens.csv`: Student progress tracking file
|
- `tokens.csv`: Student progress tracking file
|
||||||
- `generate_tokens.py`: Script to generate tokens CSV file
|
- `generate_tokens.py`: Script to generate tokens CSV file
|
||||||
- `Dockerfile`: Container configuration
|
- `Dockerfile`: Container configuration
|
||||||
- `podman-compose.yml`: Podman Compose configuration
|
- `podman-compose.yml`: Podman Compose configuration with env_file support
|
||||||
- `requirements.txt`: Python dependencies
|
- `requirements.txt`: Python dependencies
|
||||||
|
- `test/`: Directory containing test scripts and load testing tools
|
||||||
|
- `test/test_production.sh`: Production environment test script
|
||||||
|
- `test/locustfile.py`: Load testing script using Locust
|
||||||
|
- `test/podman-compose.locust.yml`: Podman Compose configuration for load testing
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
|
|
@ -427,12 +571,27 @@ To access the running container for development:
|
||||||
podman exec -it <container_name> /bin/bash
|
podman exec -it <container_name> /bin/bash
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
The project includes comprehensive test scripts:
|
||||||
|
|
||||||
|
1. **Production Environment Tests**: Run the production test script to verify all functionality:
|
||||||
|
```bash
|
||||||
|
./test/test_production.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Load Testing**: Use the included Locust configuration for load testing:
|
||||||
|
```bash
|
||||||
|
podman-compose -f test/podman-compose.locust.yml up --build
|
||||||
|
```
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
- If you get permission errors, make sure your user has access to Podman
|
- If you get permission errors, make sure your user has access to Podman
|
||||||
- If the application doesn't start, check that port 5000 is available
|
- If the application doesn't start, check that port 5000 is available
|
||||||
- If code compilation fails, verify that the gcc compiler is available in the container
|
- If code compilation fails, verify that the gcc compiler is available in the container
|
||||||
- If progress tracking isn't working, ensure the tokens.csv file is properly formatted
|
- If progress tracking isn't working, ensure the tokens.csv file is properly formatted
|
||||||
|
- If environment variables aren't being loaded, verify that the .env file is in the correct location and properly formatted
|
||||||
|
|
||||||
## Stopping the Application
|
## Stopping the Application
|
||||||
|
|
||||||
|
|
@ -562,11 +721,11 @@ The system includes support for load testing using Locust to simulate multiple c
|
||||||
4. **Alternative: Run Locust Directly**
|
4. **Alternative: Run Locust Directly**
|
||||||
- Install Locust on the load testing machine:
|
- Install Locust on the load testing machine:
|
||||||
```bash
|
```bash
|
||||||
pip install -r locust/requirements.txt
|
pip install -r test/requirements.txt
|
||||||
```
|
```
|
||||||
- Run Locust directly:
|
- Run Locust directly:
|
||||||
```bash
|
```bash
|
||||||
cd locust
|
cd test
|
||||||
locust -f locustfile.py --host http://<LMS_SERVER_IP>:5000
|
locust -f locustfile.py --host http://<LMS_SERVER_IP>:5000
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -587,3 +746,19 @@ Monitor the LMS server's resource usage (CPU, memory, disk I/O) during load test
|
||||||
- Compilation performance under load
|
- Compilation performance under load
|
||||||
- Database performance (if one is added in the future)
|
- Database performance (if one is added in the future)
|
||||||
- Container resource limits
|
- Container resource limits
|
||||||
|
|
||||||
|
The included `locustfile.py` simulates the following user behaviors:
|
||||||
|
- Browsing the home page
|
||||||
|
- Viewing lessons
|
||||||
|
- Compiling C code
|
||||||
|
- Validating student tokens
|
||||||
|
- Logging in with tokens
|
||||||
|
- Tracking student progress
|
||||||
|
|
||||||
|
### Monitoring Performance
|
||||||
|
|
||||||
|
Monitor the LMS server's resource usage (CPU, memory, disk I/O) during load testing to identify potential bottlenecks. Pay attention to:
|
||||||
|
- Response times for API requests
|
||||||
|
- Compilation performance under load
|
||||||
|
- Database performance (if one is added in the future)
|
||||||
|
- Container resource limits
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ else
|
||||||
podman exec -it lms-c-container /bin/bash
|
podman exec -it lms-c-container /bin/bash
|
||||||
else
|
else
|
||||||
echo "Building and starting container..."
|
echo "Building and starting container..."
|
||||||
podman build -t lms-c . && podman run -d -p 5000:5000 --name lms-c-container -v ../content:/app/content -v ./static:/app/static -v ./templates:/app/templates -v ./tokens.csv:/app/tokens.csv lms-c
|
podman build -t lms-c . && podman run -d -p 5000:5000 --name lms-c-container -v ../content:/app/content -v ./static:/app/static -v ./templates:/app/templates -v ./tokens.csv:/app/tokens.csv -e FLASK_ENV=production -e FLASK_DEBUG=0 lms-c
|
||||||
sleep 5 # Wait for the application to start
|
sleep 5 # Wait for the application to start
|
||||||
podman exec -it lms-c-container /bin/bash
|
podman exec -it lms-c-container /bin/bash
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
29
app.py
29
app.py
|
|
@ -9,6 +9,7 @@ import subprocess
|
||||||
import tempfile
|
import tempfile
|
||||||
import markdown
|
import markdown
|
||||||
from flask import Flask, render_template, request, jsonify, send_from_directory
|
from flask import Flask, render_template, request, jsonify, send_from_directory
|
||||||
|
from flask_talisman import Talisman
|
||||||
import glob
|
import glob
|
||||||
import csv
|
import csv
|
||||||
import uuid
|
import uuid
|
||||||
|
|
@ -16,11 +17,29 @@ from datetime import datetime
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
# Configuration
|
# Load configuration from environment variables with defaults
|
||||||
CONTENT_DIR = 'content'
|
CONTENT_DIR = os.environ.get('CONTENT_DIR', 'content')
|
||||||
STATIC_DIR = 'static'
|
STATIC_DIR = os.environ.get('STATIC_DIR', 'static')
|
||||||
TEMPLATES_DIR = 'templates'
|
TEMPLATES_DIR = os.environ.get('TEMPLATES_DIR', 'templates')
|
||||||
TOKENS_FILE = 'tokens.csv'
|
TOKENS_FILE = os.environ.get('TOKENS_FILE', 'tokens.csv')
|
||||||
|
|
||||||
|
# Security configuration using Talisman
|
||||||
|
Talisman(app,
|
||||||
|
force_https=False, # Set to True if using SSL
|
||||||
|
strict_transport_security=True,
|
||||||
|
strict_transport_security_max_age=31536000,
|
||||||
|
frame_options='DENY',
|
||||||
|
content_security_policy={
|
||||||
|
'default-src': "'self'",
|
||||||
|
'script-src': "'self' 'unsafe-inline' https://cdnjs.cloudflare.com",
|
||||||
|
'style-src': "'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com",
|
||||||
|
'img-src': "'self' data: https:",
|
||||||
|
'font-src': "'self' https://cdn.jsdelivr.net",
|
||||||
|
'connect-src': "'self'",
|
||||||
|
'frame-ancestors': "'none'",
|
||||||
|
},
|
||||||
|
referrer_policy='strict-origin-when-cross-origin'
|
||||||
|
)
|
||||||
|
|
||||||
def get_lessons():
|
def get_lessons():
|
||||||
"""Get all lesson files from the content directory"""
|
"""Get all lesson files from the content directory"""
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
# Production Environment Configuration
|
||||||
|
#
|
||||||
|
|
||||||
|
# tailscale Configuration
|
||||||
|
ELEMES_HOST=change-to-lms-topic
|
||||||
|
TS_AUTHKEY=your-super-secret-key-here-change-this-in-production
|
||||||
|
|
||||||
|
# Flask Configuration
|
||||||
|
FLASK_ENV=production
|
||||||
|
FLASK_DEBUG=0
|
||||||
|
SECRET_KEY=your-super-secret-key-here-change-this-in-production
|
||||||
|
|
||||||
|
# Application Configuration
|
||||||
|
CONTENT_DIR=content
|
||||||
|
STATIC_DIR=static
|
||||||
|
TEMPLATES_DIR=templates
|
||||||
|
TOKENS_FILE=tokens.csv
|
||||||
|
|
||||||
|
# Server Configuration
|
||||||
|
HOST=0.0.0.0
|
||||||
|
PORT=5000
|
||||||
|
|
||||||
|
# Security Configuration
|
||||||
|
MAX_CONTENT_LENGTH=1024 * 1024 # 1MB max upload size
|
||||||
|
WTF_CSRF_ENABLED=true
|
||||||
|
|
||||||
|
# Logging Configuration
|
||||||
|
LOG_LEVEL=INFO
|
||||||
|
LOG_FILE=/var/log/lms-c/app.log
|
||||||
|
|
||||||
|
# Database Configuration (if using one in future)
|
||||||
|
# DATABASE_URL=postgresql://user:password@localhost/dbname
|
||||||
|
|
||||||
|
# External Services (if applicable)
|
||||||
|
# SMTP_SERVER=smtp.example.com
|
||||||
|
# SMTP_PORT=587
|
||||||
|
# SMTP_USERNAME=user@example.com
|
||||||
|
# SMTP_PASSWORD=password
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"TCP": {
|
||||||
|
"443": {
|
||||||
|
"HTTPS": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Web": {
|
||||||
|
"${TS_CERT_DOMAIN}:443": {
|
||||||
|
"Handlers": {
|
||||||
|
"/": {
|
||||||
|
"Proxy": "http://elemes:5000"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AllowFunnel": {
|
||||||
|
"${TS_CERT_DOMAIN}:443": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
# Gunicorn configuration file for production
|
||||||
|
|
||||||
|
# Server socket
|
||||||
|
bind = "0.0.0.0:5000"
|
||||||
|
backlog = 2048
|
||||||
|
|
||||||
|
# Worker processes
|
||||||
|
workers = 4
|
||||||
|
worker_class = "sync"
|
||||||
|
worker_connections = 1000
|
||||||
|
timeout = 120
|
||||||
|
keepalive = 5
|
||||||
|
max_requests = 1000
|
||||||
|
max_requests_jitter = 100
|
||||||
|
preload_app = True
|
||||||
|
|
||||||
|
# Security
|
||||||
|
limit_request_line = 4094
|
||||||
|
limit_request_fields = 100
|
||||||
|
limit_request_field_size = 8190
|
||||||
|
|
||||||
|
# Logging
|
||||||
|
accesslog = "-"
|
||||||
|
errorlog = "-"
|
||||||
|
loglevel = "info"
|
||||||
|
access_log_format = '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s" %(D)s'
|
||||||
|
|
||||||
|
# Process naming
|
||||||
|
proc_name = 'lms-c'
|
||||||
|
|
||||||
|
# Server mechanics
|
||||||
|
user = 'app'
|
||||||
|
group = 'app'
|
||||||
|
tmp_upload_dir = None
|
||||||
|
|
||||||
|
# SSL (uncomment and configure if using SSL)
|
||||||
|
# keyfile = '/path/to/keyfile'
|
||||||
|
# certfile = '/path/to/certfile'
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
version: '3.8'
|
version: '3.8'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
lms-c:
|
elemes:
|
||||||
build: .
|
build: .
|
||||||
|
container_name: elemes
|
||||||
ports:
|
ports:
|
||||||
- "5000:5000"
|
- "5000:5000"
|
||||||
volumes:
|
volumes:
|
||||||
|
|
@ -10,6 +11,28 @@ services:
|
||||||
- ./static:/app/static
|
- ./static:/app/static
|
||||||
- ./templates:/app/templates
|
- ./templates:/app/templates
|
||||||
- ../tokens_siswa.csv:/app/tokens.csv
|
- ../tokens_siswa.csv:/app/tokens.csv
|
||||||
|
env_file:
|
||||||
|
- ../.env
|
||||||
|
command: gunicorn --config gunicorn.conf.py app:app
|
||||||
|
elemes-ts:
|
||||||
|
image: docker.io/tailscale/tailscale:latest
|
||||||
|
container_name: elemes-ts
|
||||||
|
hostname: ${ELEMES_HOST}
|
||||||
environment:
|
environment:
|
||||||
- FLASK_ENV=development
|
- TS_AUTHKEY=${TS_AUTHKEY}
|
||||||
command: python app.py
|
- TS_SERVE_CONFIG=/config/sinau-c-tail.json
|
||||||
|
- TS_STATE_DIR=/var/lib/tailscale
|
||||||
|
- TS_USERSPACE=true
|
||||||
|
volumes:
|
||||||
|
- ./state:/var/lib/tailscale
|
||||||
|
- ../config:/config
|
||||||
|
- /dev/net/tun:/dev/net/tun
|
||||||
|
cap_add:
|
||||||
|
- net_admin
|
||||||
|
- sys_module
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
networks:
|
||||||
|
main_network:
|
||||||
|
drive: bridge
|
||||||
|
network_mode: service:elemes-ts
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
Flask==2.3.3
|
Flask==2.3.3
|
||||||
markdown==3.5
|
markdown==3.5
|
||||||
gunicorn==21.2.0
|
gunicorn==21.2.0
|
||||||
|
flask-talisman==1.1.0
|
||||||
6
start.sh
6
start.sh
|
|
@ -5,15 +5,15 @@
|
||||||
echo "Starting C Programming Learning Management System..."
|
echo "Starting C Programming Learning Management System..."
|
||||||
|
|
||||||
# Check if container is already running
|
# Check if container is already running
|
||||||
if [ "$(podman ps -q -f name=lms-c-container)" ]; then
|
if [ "$(podman ps -q -f name=elemes-container)" ]; then
|
||||||
echo "Container is already running. Access the application at http://localhost:5000"
|
echo "Container is already running. Access the application at http://localhost:5000"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if container exists but is stopped
|
# Check if container exists but is stopped
|
||||||
if [ "$(podman ps -aq -f name=lms-c-container)" ]; then
|
if [ "$(podman ps -aq -f name=elemes-container)" ]; then
|
||||||
echo "Starting existing container..."
|
echo "Starting existing container..."
|
||||||
podman start lms-c-container
|
podman start elemes-container
|
||||||
else
|
else
|
||||||
# Build and run the container
|
# Build and run the container
|
||||||
echo "Building and starting container..."
|
echo "Building and starting container..."
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Test script for LMS-C Production Environment
|
||||||
|
|
||||||
|
echo "Starting LMS-C Production Environment Tests..."
|
||||||
|
|
||||||
|
# Start the application
|
||||||
|
echo "Starting the application..."
|
||||||
|
cd ..
|
||||||
|
./start.sh
|
||||||
|
cd test
|
||||||
|
|
||||||
|
# Wait for the application to start
|
||||||
|
echo "Waiting for application to start..."
|
||||||
|
sleep 10
|
||||||
|
|
||||||
|
# Test 1: Check if the home page loads and displays lessons
|
||||||
|
echo "Test 1: Checking home page and lesson display..."
|
||||||
|
HOME_PAGE_CHECK=$(curl -s http://localhost:5000/ | grep -i "Available Lessons" | wc -l)
|
||||||
|
if [ $HOME_PAGE_CHECK -gt 0 ]; then
|
||||||
|
echo "✓ Test 1 PASSED: Home page displays lessons correctly"
|
||||||
|
else
|
||||||
|
echo "✗ Test 1 FAILED: Home page does not display lessons"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 2: Check if specific lesson pages load
|
||||||
|
echo "Test 2: Checking lesson page loading..."
|
||||||
|
LESSON_PAGE_CHECK=$(curl -s http://localhost:5000/lesson/welcome.md | grep -i "C Programming Learning System" | wc -l)
|
||||||
|
if [ $LESSON_PAGE_CHECK -gt 0 ]; then
|
||||||
|
echo "✓ Test 2 PASSED: Lesson pages load correctly"
|
||||||
|
else
|
||||||
|
echo "✗ Test 2 FAILED: Lesson pages do not load"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 3: Test the code compilation API
|
||||||
|
echo "Test 3: Testing code compilation API..."
|
||||||
|
COMPILATION_TEST=$(curl -s -X POST http://localhost:5000/compile \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"code":"#include <stdio.h>\n\nint main() {\n printf(\"Hello, World!\\n\");\n return 0;\n}"}')
|
||||||
|
|
||||||
|
# Check if the response contains success: true and the expected output
|
||||||
|
if echo "$COMPILATION_TEST" | grep -q '"success":true' && echo "$COMPILATION_TEST" | grep -q "Hello, World!"; then
|
||||||
|
echo "✓ Test 3 PASSED: Code compilation API works correctly"
|
||||||
|
else
|
||||||
|
echo "✗ Test 3 FAILED: Code compilation API not working"
|
||||||
|
echo " Response: $COMPILATION_TEST"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 4: Test with a more complex C program
|
||||||
|
echo "Test 4: Testing complex code compilation..."
|
||||||
|
COMPLEX_CODE='{
|
||||||
|
"code": "#include <stdio.h>\nint main() {\n int i, sum = 0;\n for (i = 1; i <= 5; i++) {\n sum += i;\n }\n printf(\"Sum of 1 to 5 is: %d\\n\", sum);\n return 0;\n}"
|
||||||
|
}'
|
||||||
|
|
||||||
|
COMPLEX_TEST=$(curl -s -X POST http://localhost:5000/compile \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "$COMPLEX_CODE")
|
||||||
|
|
||||||
|
if echo "$COMPLEX_TEST" | grep -q '"success":true' && echo "$COMPLEX_TEST" | grep -q "Sum of 1 to 5"; then
|
||||||
|
echo "✓ Test 4 PASSED: Complex code compilation works correctly"
|
||||||
|
else
|
||||||
|
echo "✗ Test 4 FAILED: Complex code compilation not working"
|
||||||
|
echo " Response: $COMPLEX_TEST"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 5: Check if all expected lesson files are accessible
|
||||||
|
echo "Test 5: Checking lesson accessibility..."
|
||||||
|
LESSON_FILES=("welcome.md" "hello_world.md" "introduction_to_c.md" "arrays.md")
|
||||||
|
ALL_LESSONS_ACCESSIBLE=true
|
||||||
|
|
||||||
|
for lesson in "${LESSON_FILES[@]}"; do
|
||||||
|
lesson_check=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:5000/lesson/$lesson")
|
||||||
|
if [ "$lesson_check" -ne 200 ]; then
|
||||||
|
echo "✗ Lesson $lesson returned HTTP $lesson_check"
|
||||||
|
ALL_LESSONS_ACCESSIBLE=false
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$ALL_LESSONS_ACCESSIBLE" = true ]; then
|
||||||
|
echo "✓ Test 5 PASSED: All test lessons are accessible"
|
||||||
|
else
|
||||||
|
echo "✗ Test 5 FAILED: Some lessons are not accessible"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 6: Check for errors in container logs
|
||||||
|
echo "Test 6: Checking container logs for errors..."
|
||||||
|
LOG_ERRORS=$(podman logs elemes_lms-c_1 2>&1 | grep -i error | wc -l)
|
||||||
|
if [ $LOG_ERRORS -eq 0 ]; then
|
||||||
|
echo "✓ Test 6 PASSED: No errors found in container logs"
|
||||||
|
else
|
||||||
|
echo "✗ Test 6 FAILED: Errors found in container logs"
|
||||||
|
podman logs elemes_lms-c_1 | grep -i error
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Stop the application
|
||||||
|
echo "Stopping the application..."
|
||||||
|
cd ..
|
||||||
|
./stop.sh
|
||||||
|
cd test
|
||||||
|
|
||||||
|
echo "All tests completed!"
|
||||||
Loading…
Reference in New Issue