Add helm chart
This commit is contained in:
parent
09d5c79e8d
commit
6886f94888
20
motm_app/helm-chart/motm-app/Chart.yaml
Normal file
20
motm_app/helm-chart/motm-app/Chart.yaml
Normal file
@ -0,0 +1,20 @@
|
||||
apiVersion: v2
|
||||
name: motm-app
|
||||
description: A Helm chart for MOTM (Man of the Match) Hockey Voting Application
|
||||
type: application
|
||||
version: 1.0.0
|
||||
appVersion: "1.0.0"
|
||||
home: https://github.com/your-org/motm-app
|
||||
sources:
|
||||
- https://github.com/your-org/motm-app
|
||||
maintainers:
|
||||
- name: Your Name
|
||||
email: your.email@example.com
|
||||
keywords:
|
||||
- flask
|
||||
- hockey
|
||||
- voting
|
||||
- web-application
|
||||
annotations:
|
||||
category: Sports
|
||||
licenses: MIT
|
||||
359
motm_app/helm-chart/motm-app/DEPLOYMENT.md
Normal file
359
motm_app/helm-chart/motm-app/DEPLOYMENT.md
Normal file
@ -0,0 +1,359 @@
|
||||
# MOTM App Kubernetes Deployment Guide
|
||||
|
||||
This guide provides step-by-step instructions for deploying the MOTM (Man of the Match) Hockey Voting Application to a Kubernetes cluster using Helm.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### Required Tools
|
||||
- **Kubernetes Cluster** (version 1.19+)
|
||||
- **Helm** (version 3.0+)
|
||||
- **kubectl** (configured for your cluster)
|
||||
- **Docker** (for building images)
|
||||
|
||||
### Required Services
|
||||
- **PostgreSQL Database** (or MySQL/SQLite)
|
||||
- **S3-compatible Storage** (optional, for asset management)
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Build and Push Docker Image
|
||||
|
||||
```bash
|
||||
# Navigate to the application directory
|
||||
cd /home/jonny/Projects/gcp-hockey-results/motm_app
|
||||
|
||||
# Build the Docker image
|
||||
docker build -t your-registry/motm-app:latest .
|
||||
|
||||
# Push to your container registry
|
||||
docker push your-registry/motm-app:latest
|
||||
```
|
||||
|
||||
### 2. Deploy to Development
|
||||
|
||||
```bash
|
||||
# Navigate to the helm chart directory
|
||||
cd helm-chart/motm-app
|
||||
|
||||
# Update the image repository in values-development.yaml
|
||||
sed -i 's/your-registry\/motm-app/your-actual-registry\/motm-app/g' values-development.yaml
|
||||
|
||||
# Deploy using the deployment script
|
||||
./scripts/deploy.sh development install
|
||||
```
|
||||
|
||||
### 3. Deploy to Production
|
||||
|
||||
```bash
|
||||
# Update production values
|
||||
cp values-production.yaml my-production-values.yaml
|
||||
# Edit my-production-values.yaml with your production settings
|
||||
|
||||
# Deploy to production
|
||||
./scripts/deploy.sh production install
|
||||
```
|
||||
|
||||
## Manual Deployment
|
||||
|
||||
### 1. Customize Values
|
||||
|
||||
Edit the appropriate values file:
|
||||
|
||||
```bash
|
||||
# For development
|
||||
vim values-development.yaml
|
||||
|
||||
# For production
|
||||
vim values-production.yaml
|
||||
```
|
||||
|
||||
Key values to update:
|
||||
- `image.repository`: Your container registry
|
||||
- `database.host`: Database service name
|
||||
- `ingress.hosts[0].host`: Your domain name
|
||||
- `secrets.*`: Database and S3 credentials
|
||||
|
||||
### 2. Install with Helm
|
||||
|
||||
```bash
|
||||
# Development
|
||||
helm install motm-app ./motm-app \
|
||||
--namespace motm-app \
|
||||
--values values-development.yaml \
|
||||
--create-namespace
|
||||
|
||||
# Production
|
||||
helm install motm-app ./motm-app \
|
||||
--namespace motm-app \
|
||||
--values values-production.yaml \
|
||||
--create-namespace
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### Database Setup
|
||||
|
||||
The application supports multiple database types:
|
||||
|
||||
#### PostgreSQL (Recommended)
|
||||
```yaml
|
||||
database:
|
||||
type: "postgresql"
|
||||
host: "postgresql-service"
|
||||
port: 5432
|
||||
name: "motm"
|
||||
username: "motm_user"
|
||||
```
|
||||
|
||||
#### MySQL
|
||||
```yaml
|
||||
database:
|
||||
type: "mysql"
|
||||
host: "mysql-service"
|
||||
port: 3306
|
||||
name: "motm"
|
||||
username: "motm_user"
|
||||
```
|
||||
|
||||
#### SQLite (Development only)
|
||||
```yaml
|
||||
database:
|
||||
type: "sqlite"
|
||||
# Other fields ignored for SQLite
|
||||
```
|
||||
|
||||
### S3 Configuration
|
||||
|
||||
For asset management (logos, images):
|
||||
|
||||
```yaml
|
||||
s3:
|
||||
enabled: true
|
||||
endpoint: "https://s3.amazonaws.com"
|
||||
region: "us-east-1"
|
||||
bucket: "motm-assets"
|
||||
# Credentials set via secrets
|
||||
```
|
||||
|
||||
### Security Configuration
|
||||
|
||||
#### Secrets Management
|
||||
|
||||
Set secrets via Helm values or external secret management:
|
||||
|
||||
```yaml
|
||||
secrets:
|
||||
dbPassword: "your-database-password"
|
||||
s3AccessKey: "your-s3-access-key"
|
||||
s3SecretKey: "your-s3-secret-key"
|
||||
```
|
||||
|
||||
Or use external secret management:
|
||||
```bash
|
||||
# Create secrets manually
|
||||
kubectl create secret generic motm-app-secrets \
|
||||
--from-literal=db-password=your-password \
|
||||
--from-literal=s3-access-key=your-key \
|
||||
--from-literal=s3-secret-key=your-secret \
|
||||
--namespace motm-app
|
||||
```
|
||||
|
||||
#### Network Policies
|
||||
|
||||
For enhanced security, create network policies:
|
||||
|
||||
```yaml
|
||||
# network-policy.yaml
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: motm-app-netpol
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: motm-app
|
||||
policyTypes:
|
||||
- Ingress
|
||||
- Egress
|
||||
ingress:
|
||||
- from:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
name: ingress-nginx
|
||||
egress:
|
||||
- to:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
name: postgresql
|
||||
```
|
||||
|
||||
## Monitoring and Observability
|
||||
|
||||
### Health Checks
|
||||
|
||||
The application includes built-in health checks:
|
||||
- **Liveness Probe**: Checks if the application is running
|
||||
- **Readiness Probe**: Checks if the application is ready to serve traffic
|
||||
|
||||
### Logging
|
||||
|
||||
Configure log levels in values:
|
||||
|
||||
```yaml
|
||||
logging:
|
||||
level: "INFO" # DEBUG, INFO, WARNING, ERROR
|
||||
format: "json" # json, text
|
||||
```
|
||||
|
||||
### Metrics (Optional)
|
||||
|
||||
Add Prometheus metrics endpoint:
|
||||
|
||||
```yaml
|
||||
monitoring:
|
||||
enabled: true
|
||||
serviceMonitor:
|
||||
enabled: true
|
||||
interval: 30s
|
||||
```
|
||||
|
||||
## Scaling
|
||||
|
||||
### Horizontal Pod Autoscaling
|
||||
|
||||
Enable HPA for production:
|
||||
|
||||
```yaml
|
||||
autoscaling:
|
||||
enabled: true
|
||||
minReplicas: 2
|
||||
maxReplicas: 10
|
||||
targetCPUUtilizationPercentage: 70
|
||||
targetMemoryUtilizationPercentage: 80
|
||||
```
|
||||
|
||||
### Resource Limits
|
||||
|
||||
Adjust based on your cluster capacity:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
limits:
|
||||
cpu: 1000m
|
||||
memory: 1Gi
|
||||
requests:
|
||||
cpu: 200m
|
||||
memory: 512Mi
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
#### 1. Pod Not Starting
|
||||
```bash
|
||||
# Check pod status
|
||||
kubectl get pods -n motm-app -l app.kubernetes.io/name=motm-app
|
||||
|
||||
# Check pod logs
|
||||
kubectl logs -n motm-app -l app.kubernetes.io/name=motm-app
|
||||
```
|
||||
|
||||
#### 2. Database Connection Issues
|
||||
```bash
|
||||
# Test database connectivity
|
||||
kubectl exec -n motm-app -it deployment/motm-app -- python -c "
|
||||
from database import sql_read_static
|
||||
from sqlalchemy import text
|
||||
try:
|
||||
result = sql_read_static(text('SELECT 1'))
|
||||
print('Database connection successful')
|
||||
except Exception as e:
|
||||
print(f'Database connection failed: {e}')
|
||||
"
|
||||
```
|
||||
|
||||
#### 3. S3 Connection Issues
|
||||
```bash
|
||||
# Check S3 configuration
|
||||
kubectl exec -n motm-app -it deployment/motm-app -- cat /app/s3_config.json
|
||||
```
|
||||
|
||||
#### 4. Ingress Issues
|
||||
```bash
|
||||
# Check ingress status
|
||||
kubectl get ingress -n motm-app
|
||||
|
||||
# Check ingress controller logs
|
||||
kubectl logs -n ingress-nginx -l app.kubernetes.io/name=ingress-nginx
|
||||
```
|
||||
|
||||
### Debugging Commands
|
||||
|
||||
```bash
|
||||
# Get all resources
|
||||
kubectl get all -n motm-app
|
||||
|
||||
# Describe deployment
|
||||
kubectl describe deployment -n motm-app motm-app
|
||||
|
||||
# Check events
|
||||
kubectl get events -n motm-app --sort-by='.lastTimestamp'
|
||||
|
||||
# Port forward for local testing
|
||||
kubectl port-forward -n motm-app svc/motm-app 8080:80
|
||||
```
|
||||
|
||||
## Maintenance
|
||||
|
||||
### Updates
|
||||
|
||||
```bash
|
||||
# Update application
|
||||
helm upgrade motm-app ./motm-app \
|
||||
--namespace motm-app \
|
||||
--values values-production.yaml
|
||||
|
||||
# Rollback if needed
|
||||
helm rollback motm-app 1 --namespace motm-app
|
||||
```
|
||||
|
||||
### Backup
|
||||
|
||||
```bash
|
||||
# Backup database (PostgreSQL)
|
||||
kubectl exec -n postgresql postgresql-0 -- pg_dump -U motm_user motm > backup.sql
|
||||
|
||||
# Backup application data
|
||||
kubectl exec -n motm-app deployment/motm-app -- tar -czf /tmp/data-backup.tar.gz /app/data
|
||||
kubectl cp motm-app/deployment/motm-app:/tmp/data-backup.tar.gz ./data-backup.tar.gz
|
||||
```
|
||||
|
||||
### Cleanup
|
||||
|
||||
```bash
|
||||
# Uninstall application
|
||||
helm uninstall motm-app --namespace motm-app
|
||||
|
||||
# Delete namespace (if no other resources)
|
||||
kubectl delete namespace motm-app
|
||||
```
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
1. **Use Non-Root Containers**: The application runs as non-root user (UID 1000)
|
||||
2. **Read-Only Root Filesystem**: Enable in production values
|
||||
3. **Network Policies**: Implement to restrict pod-to-pod communication
|
||||
4. **RBAC**: Use dedicated service accounts with minimal permissions
|
||||
5. **Secret Management**: Use external secret management solutions
|
||||
6. **Image Security**: Scan images for vulnerabilities
|
||||
7. **TLS**: Enable TLS for all ingress traffic
|
||||
8. **Resource Limits**: Set appropriate CPU and memory limits
|
||||
|
||||
## Support
|
||||
|
||||
For issues and questions:
|
||||
1. Check the application logs
|
||||
2. Review Kubernetes events
|
||||
3. Consult the Helm chart documentation
|
||||
4. Create an issue in the repository
|
||||
239
motm_app/helm-chart/motm-app/README.md
Normal file
239
motm_app/helm-chart/motm-app/README.md
Normal file
@ -0,0 +1,239 @@
|
||||
# MOTM App Helm Chart
|
||||
|
||||
This Helm chart deploys the MOTM (Man of the Match) Hockey Voting Application to a Kubernetes cluster.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Kubernetes 1.19+
|
||||
- Helm 3.0+
|
||||
- PostgreSQL database (or MySQL/SQLite)
|
||||
- S3-compatible storage (optional)
|
||||
|
||||
## Installation
|
||||
|
||||
### 1. Build and Push Docker Image
|
||||
|
||||
First, build and push your Docker image to a registry:
|
||||
|
||||
```bash
|
||||
# Build the image
|
||||
docker build -t your-registry/motm-app:latest .
|
||||
|
||||
# Push to registry
|
||||
docker push your-registry/motm-app:latest
|
||||
```
|
||||
|
||||
### 2. Configure Values
|
||||
|
||||
Copy the default values file and customize it:
|
||||
|
||||
```bash
|
||||
cp values.yaml my-values.yaml
|
||||
```
|
||||
|
||||
Key values to update in `my-values.yaml`:
|
||||
|
||||
```yaml
|
||||
# Image configuration
|
||||
image:
|
||||
repository: your-registry/motm-app
|
||||
tag: "latest"
|
||||
|
||||
# Database configuration
|
||||
database:
|
||||
host: "your-postgresql-service"
|
||||
name: "motm"
|
||||
username: "motm_user"
|
||||
|
||||
# S3 configuration (if using S3)
|
||||
s3:
|
||||
enabled: true
|
||||
endpoint: "https://s3.amazonaws.com"
|
||||
bucket: "your-bucket-name"
|
||||
|
||||
# Ingress configuration
|
||||
ingress:
|
||||
enabled: true
|
||||
hosts:
|
||||
- host: motm.yourdomain.com
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
tls:
|
||||
- secretName: motm-app-tls
|
||||
hosts:
|
||||
- motm.yourdomain.com
|
||||
|
||||
# Secrets (set these via --set or separate secret management)
|
||||
secrets:
|
||||
dbPassword: "your-db-password"
|
||||
s3AccessKey: "your-s3-access-key"
|
||||
s3SecretKey: "your-s3-secret-key"
|
||||
```
|
||||
|
||||
### 3. Deploy with Helm
|
||||
|
||||
#### Option A: Using values file
|
||||
|
||||
```bash
|
||||
helm install motm-app ./motm-app -f my-values.yaml
|
||||
```
|
||||
|
||||
#### Option B: Using command line parameters
|
||||
|
||||
```bash
|
||||
helm install motm-app ./motm-app \
|
||||
--set image.repository=your-registry/motm-app \
|
||||
--set database.host=your-postgresql-service \
|
||||
--set ingress.hosts[0].host=motm.yourdomain.com \
|
||||
--set secrets.dbPassword=your-db-password
|
||||
```
|
||||
|
||||
#### Option C: Using external secret management
|
||||
|
||||
If using external secret management (e.g., Sealed Secrets, External Secrets Operator), create the secrets separately and set:
|
||||
|
||||
```yaml
|
||||
secrets:
|
||||
dbPassword: "" # Will be managed externally
|
||||
s3AccessKey: "" # Will be managed externally
|
||||
s3SecretKey: "" # Will be managed externally
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### Database Setup
|
||||
|
||||
The application supports PostgreSQL, MySQL, and SQLite. Configure your database connection in the values file:
|
||||
|
||||
```yaml
|
||||
database:
|
||||
type: "postgresql" # postgresql, mysql, or sqlite
|
||||
host: "postgresql-service"
|
||||
port: 5432
|
||||
name: "motm"
|
||||
username: "motm_user"
|
||||
```
|
||||
|
||||
### S3 Configuration
|
||||
|
||||
Configure S3-compatible storage for asset management:
|
||||
|
||||
```yaml
|
||||
s3:
|
||||
enabled: true
|
||||
endpoint: "https://s3.amazonaws.com"
|
||||
region: "us-east-1"
|
||||
bucket: "motm-assets"
|
||||
```
|
||||
|
||||
### Resource Limits
|
||||
|
||||
Adjust resource limits based on your cluster capacity:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 256Mi
|
||||
```
|
||||
|
||||
### Autoscaling
|
||||
|
||||
Enable horizontal pod autoscaling:
|
||||
|
||||
```yaml
|
||||
autoscaling:
|
||||
enabled: true
|
||||
minReplicas: 2
|
||||
maxReplicas: 10
|
||||
targetCPUUtilizationPercentage: 80
|
||||
```
|
||||
|
||||
## Upgrading
|
||||
|
||||
To upgrade the application:
|
||||
|
||||
```bash
|
||||
helm upgrade motm-app ./motm-app -f my-values.yaml
|
||||
```
|
||||
|
||||
## Uninstalling
|
||||
|
||||
To uninstall the application:
|
||||
|
||||
```bash
|
||||
helm uninstall motm-app
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Check Pod Status
|
||||
|
||||
```bash
|
||||
kubectl get pods -l app.kubernetes.io/name=motm-app
|
||||
```
|
||||
|
||||
### View Logs
|
||||
|
||||
```bash
|
||||
kubectl logs -l app.kubernetes.io/name=motm-app
|
||||
```
|
||||
|
||||
### Check Service
|
||||
|
||||
```bash
|
||||
kubectl get svc -l app.kubernetes.io/name=motm-app
|
||||
```
|
||||
|
||||
### Debug Database Connection
|
||||
|
||||
```bash
|
||||
kubectl exec -it deployment/motm-app -- python -c "
|
||||
from database import sql_read_static
|
||||
from sqlalchemy import text
|
||||
try:
|
||||
result = sql_read_static(text('SELECT 1'))
|
||||
print('Database connection successful')
|
||||
except Exception as e:
|
||||
print(f'Database connection failed: {e}')
|
||||
"
|
||||
```
|
||||
|
||||
## Values Reference
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `image.repository` | string | `"your-registry/motm-app"` | Image repository |
|
||||
| `image.tag` | string | `"latest"` | Image tag |
|
||||
| `service.type` | string | `"ClusterIP"` | Service type |
|
||||
| `ingress.enabled` | bool | `true` | Enable ingress |
|
||||
| `database.type` | string | `"postgresql"` | Database type |
|
||||
| `database.host` | string | `"postgresql-service"` | Database host |
|
||||
| `s3.enabled` | bool | `true` | Enable S3 storage |
|
||||
| `resources.limits.cpu` | string | `"500m"` | CPU limit |
|
||||
| `resources.limits.memory` | string | `"512Mi"` | Memory limit |
|
||||
|
||||
## Security Considerations
|
||||
|
||||
1. **Secrets Management**: Use proper secret management solutions (e.g., Sealed Secrets, External Secrets Operator)
|
||||
2. **Network Policies**: Implement network policies to restrict pod-to-pod communication
|
||||
3. **RBAC**: Configure proper RBAC for service accounts
|
||||
4. **Image Security**: Use non-root containers and scan images for vulnerabilities
|
||||
5. **TLS**: Enable TLS for ingress and internal communication
|
||||
|
||||
## Monitoring
|
||||
|
||||
The chart includes basic health checks. For production deployments, consider adding:
|
||||
|
||||
- Prometheus metrics endpoint
|
||||
- ServiceMonitor for Prometheus Operator
|
||||
- Grafana dashboards
|
||||
- Alerting rules
|
||||
|
||||
## Support
|
||||
|
||||
For issues and questions, please refer to the application documentation or create an issue in the repository.
|
||||
256
motm_app/helm-chart/motm-app/scripts/deploy.sh
Executable file
256
motm_app/helm-chart/motm-app/scripts/deploy.sh
Executable file
@ -0,0 +1,256 @@
|
||||
#!/bin/bash
|
||||
|
||||
# MOTM App Helm Deployment Script
|
||||
# Usage: ./deploy.sh [environment] [action]
|
||||
# Environment: development, staging, production
|
||||
# Action: install, upgrade, uninstall, template
|
||||
|
||||
set -e
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Default values
|
||||
ENVIRONMENT=${1:-development}
|
||||
ACTION=${2:-install}
|
||||
RELEASE_NAME="motm-app"
|
||||
CHART_PATH="./motm-app"
|
||||
NAMESPACE="motm-app"
|
||||
|
||||
# Function to print colored output
|
||||
print_status() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
print_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# Function to check prerequisites
|
||||
check_prerequisites() {
|
||||
print_status "Checking prerequisites..."
|
||||
|
||||
# Check if helm is installed
|
||||
if ! command -v helm &> /dev/null; then
|
||||
print_error "Helm is not installed. Please install Helm 3.0+"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if kubectl is installed
|
||||
if ! command -v kubectl &> /dev/null; then
|
||||
print_error "kubectl is not installed. Please install kubectl"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if we can connect to Kubernetes cluster
|
||||
if ! kubectl cluster-info &> /dev/null; then
|
||||
print_error "Cannot connect to Kubernetes cluster"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
print_success "Prerequisites check passed"
|
||||
}
|
||||
|
||||
# Function to create namespace
|
||||
create_namespace() {
|
||||
print_status "Creating namespace: $NAMESPACE"
|
||||
kubectl create namespace $NAMESPACE --dry-run=client -o yaml | kubectl apply -f -
|
||||
print_success "Namespace created/verified"
|
||||
}
|
||||
|
||||
# Function to validate values file
|
||||
validate_values() {
|
||||
local values_file="values-${ENVIRONMENT}.yaml"
|
||||
|
||||
if [[ ! -f "$values_file" ]]; then
|
||||
print_error "Values file $values_file not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
print_status "Validating values file: $values_file"
|
||||
|
||||
# Check for required values
|
||||
if ! grep -q "repository:" "$values_file"; then
|
||||
print_error "Image repository not specified in $values_file"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! grep -q "host:" "$values_file"; then
|
||||
print_error "Database host not specified in $values_file"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
print_success "Values file validation passed"
|
||||
}
|
||||
|
||||
# Function to template the chart
|
||||
template_chart() {
|
||||
local values_file="values-${ENVIRONMENT}.yaml"
|
||||
|
||||
print_status "Generating Kubernetes manifests..."
|
||||
helm template $RELEASE_NAME $CHART_PATH \
|
||||
--namespace $NAMESPACE \
|
||||
--values $values_file \
|
||||
--output-dir ./generated-manifests
|
||||
|
||||
print_success "Manifests generated in ./generated-manifests/"
|
||||
}
|
||||
|
||||
# Function to install the chart
|
||||
install_chart() {
|
||||
local values_file="values-${ENVIRONMENT}.yaml"
|
||||
|
||||
print_status "Installing MOTM App in $ENVIRONMENT environment..."
|
||||
|
||||
helm install $RELEASE_NAME $CHART_PATH \
|
||||
--namespace $NAMESPACE \
|
||||
--values $values_file \
|
||||
--create-namespace \
|
||||
--wait \
|
||||
--timeout 10m
|
||||
|
||||
print_success "MOTM App installed successfully"
|
||||
|
||||
# Show status
|
||||
kubectl get pods -n $NAMESPACE -l app.kubernetes.io/name=motm-app
|
||||
}
|
||||
|
||||
# Function to upgrade the chart
|
||||
upgrade_chart() {
|
||||
local values_file="values-${ENVIRONMENT}.yaml"
|
||||
|
||||
print_status "Upgrading MOTM App in $ENVIRONMENT environment..."
|
||||
|
||||
helm upgrade $RELEASE_NAME $CHART_PATH \
|
||||
--namespace $NAMESPACE \
|
||||
--values $values_file \
|
||||
--wait \
|
||||
--timeout 10m
|
||||
|
||||
print_success "MOTM App upgraded successfully"
|
||||
|
||||
# Show status
|
||||
kubectl get pods -n $NAMESPACE -l app.kubernetes.io/name=motm-app
|
||||
}
|
||||
|
||||
# Function to uninstall the chart
|
||||
uninstall_chart() {
|
||||
print_warning "Uninstalling MOTM App from $ENVIRONMENT environment..."
|
||||
|
||||
read -p "Are you sure you want to uninstall? (y/N): " -n 1 -r
|
||||
echo
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
helm uninstall $RELEASE_NAME --namespace $NAMESPACE
|
||||
print_success "MOTM App uninstalled successfully"
|
||||
else
|
||||
print_status "Uninstall cancelled"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to show status
|
||||
show_status() {
|
||||
print_status "Showing MOTM App status..."
|
||||
|
||||
echo "=== Helm Release Status ==="
|
||||
helm status $RELEASE_NAME --namespace $NAMESPACE
|
||||
|
||||
echo -e "\n=== Pods Status ==="
|
||||
kubectl get pods -n $NAMESPACE -l app.kubernetes.io/name=motm-app
|
||||
|
||||
echo -e "\n=== Services ==="
|
||||
kubectl get svc -n $NAMESPACE -l app.kubernetes.io/name=motm-app
|
||||
|
||||
echo -e "\n=== Ingress ==="
|
||||
kubectl get ingress -n $NAMESPACE -l app.kubernetes.io/name=motm-app
|
||||
}
|
||||
|
||||
# Function to show logs
|
||||
show_logs() {
|
||||
print_status "Showing MOTM App logs..."
|
||||
kubectl logs -n $NAMESPACE -l app.kubernetes.io/name=motm-app --tail=100 -f
|
||||
}
|
||||
|
||||
# Function to show help
|
||||
show_help() {
|
||||
echo "MOTM App Helm Deployment Script"
|
||||
echo ""
|
||||
echo "Usage: $0 [environment] [action]"
|
||||
echo ""
|
||||
echo "Environments:"
|
||||
echo " development Deploy to development environment"
|
||||
echo " staging Deploy to staging environment"
|
||||
echo " production Deploy to production environment"
|
||||
echo ""
|
||||
echo "Actions:"
|
||||
echo " install Install the application (default)"
|
||||
echo " upgrade Upgrade the application"
|
||||
echo " uninstall Uninstall the application"
|
||||
echo " template Generate Kubernetes manifests"
|
||||
echo " status Show application status"
|
||||
echo " logs Show application logs"
|
||||
echo " help Show this help message"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " $0 development install"
|
||||
echo " $0 production upgrade"
|
||||
echo " $0 staging template"
|
||||
echo " $0 development status"
|
||||
}
|
||||
|
||||
# Main execution
|
||||
main() {
|
||||
case $ACTION in
|
||||
install)
|
||||
check_prerequisites
|
||||
create_namespace
|
||||
validate_values
|
||||
install_chart
|
||||
;;
|
||||
upgrade)
|
||||
check_prerequisites
|
||||
create_namespace
|
||||
validate_values
|
||||
upgrade_chart
|
||||
;;
|
||||
uninstall)
|
||||
check_prerequisites
|
||||
uninstall_chart
|
||||
;;
|
||||
template)
|
||||
check_prerequisites
|
||||
validate_values
|
||||
template_chart
|
||||
;;
|
||||
status)
|
||||
check_prerequisites
|
||||
show_status
|
||||
;;
|
||||
logs)
|
||||
check_prerequisites
|
||||
show_logs
|
||||
;;
|
||||
help|--help|-h)
|
||||
show_help
|
||||
;;
|
||||
*)
|
||||
print_error "Unknown action: $ACTION"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main "$@"
|
||||
65
motm_app/helm-chart/motm-app/templates/_helpers.tpl
Normal file
65
motm_app/helm-chart/motm-app/templates/_helpers.tpl
Normal file
@ -0,0 +1,65 @@
|
||||
{{/*
|
||||
Expand the name of the chart.
|
||||
*/}}
|
||||
{{- define "motm-app.name" -}}
|
||||
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create a default fully qualified app name.
|
||||
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
|
||||
If release name contains chart name it will be used as a full name.
|
||||
*/}}
|
||||
{{- define "motm-app.fullname" -}}
|
||||
{{- if .Values.fullnameOverride }}
|
||||
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- $name := default .Chart.Name .Values.nameOverride }}
|
||||
{{- if contains $name .Release.Name }}
|
||||
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create chart name and version as used by the chart label.
|
||||
*/}}
|
||||
{{- define "motm-app.chart" -}}
|
||||
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Common labels
|
||||
*/}}
|
||||
{{- define "motm-app.labels" -}}
|
||||
helm.sh/chart: {{ include "motm-app.chart" . }}
|
||||
{{ include "motm-app.selectorLabels" . }}
|
||||
{{- if .Chart.AppVersion }}
|
||||
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
||||
{{- end }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
{{- with .Values.labels }}
|
||||
{{- toYaml . | nindent 0 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Selector labels
|
||||
*/}}
|
||||
{{- define "motm-app.selectorLabels" -}}
|
||||
app.kubernetes.io/name: {{ include "motm-app.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create the name of the service account to use
|
||||
*/}}
|
||||
{{- define "motm-app.serviceAccountName" -}}
|
||||
{{- if .Values.serviceAccount.create }}
|
||||
{{- default (include "motm-app.fullname" .) .Values.serviceAccount.name }}
|
||||
{{- else }}
|
||||
{{- default "default" .Values.serviceAccount.name }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
43
motm_app/helm-chart/motm-app/templates/configmap.yaml
Normal file
43
motm_app/helm-chart/motm-app/templates/configmap.yaml
Normal file
@ -0,0 +1,43 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: {{ include "motm-app.fullname" . }}-config
|
||||
labels:
|
||||
{{- include "motm-app.labels" . | nindent 4 }}
|
||||
data:
|
||||
database_config.ini: |
|
||||
[DATABASE]
|
||||
type = {{ .Values.database.type }}
|
||||
sqlite_database_path = hockey_results.db
|
||||
|
||||
[MYSQL]
|
||||
host = {{ .Values.database.host }}
|
||||
port = {{ .Values.database.port }}
|
||||
database = {{ .Values.database.name }}
|
||||
username = {{ .Values.database.username }}
|
||||
charset = utf8mb4
|
||||
|
||||
[POSTGRESQL]
|
||||
host = {{ .Values.database.host }}
|
||||
port = {{ .Values.database.port }}
|
||||
database = {{ .Values.database.name }}
|
||||
username = {{ .Values.database.username }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: {{ include "motm-app.fullname" . }}-s3-config
|
||||
labels:
|
||||
{{- include "motm-app.labels" . | nindent 4 }}
|
||||
data:
|
||||
s3_config.json: |
|
||||
{
|
||||
"enable_s3": {{ .Values.s3.enabled }},
|
||||
"aws_access_key_id": "",
|
||||
"aws_secret_access_key": "",
|
||||
"aws_region": "{{ .Values.s3.region }}",
|
||||
"bucket_name": "{{ .Values.s3.bucket }}",
|
||||
"endpoint_url": "{{ .Values.s3.endpoint }}",
|
||||
"use_ssl": true,
|
||||
"verify_ssl": true
|
||||
}
|
||||
134
motm_app/helm-chart/motm-app/templates/deployment.yaml
Normal file
134
motm_app/helm-chart/motm-app/templates/deployment.yaml
Normal file
@ -0,0 +1,134 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "motm-app.fullname" . }}
|
||||
labels:
|
||||
{{- include "motm-app.labels" . | nindent 4 }}
|
||||
spec:
|
||||
{{- if not .Values.autoscaling.enabled }}
|
||||
replicas: {{ .Values.replicaCount | default 1 }}
|
||||
{{- end }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "motm-app.selectorLabels" . | nindent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
{{- with .Values.podAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
labels:
|
||||
{{- include "motm-app.selectorLabels" . | nindent 8 }}
|
||||
{{- with .Values.podLabels }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- with .Values.imagePullSecrets }}
|
||||
imagePullSecrets:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
serviceAccountName: {{ include "motm-app.serviceAccountName" . }}
|
||||
securityContext:
|
||||
{{- toYaml .Values.podSecurityContext | nindent 8 }}
|
||||
containers:
|
||||
- name: {{ .Chart.Name }}
|
||||
securityContext:
|
||||
{{- toYaml .Values.securityContext | nindent 12 }}
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 5000
|
||||
protocol: TCP
|
||||
env:
|
||||
# Application environment variables
|
||||
{{- range $key, $value := .Values.env }}
|
||||
- name: {{ $key }}
|
||||
value: {{ $value | quote }}
|
||||
{{- end }}
|
||||
# Database configuration
|
||||
- name: DB_HOST
|
||||
value: {{ .Values.database.host | quote }}
|
||||
- name: DB_PORT
|
||||
value: {{ .Values.database.port | quote }}
|
||||
- name: DB_NAME
|
||||
value: {{ .Values.database.name | quote }}
|
||||
- name: DB_USER
|
||||
value: {{ .Values.database.username | quote }}
|
||||
- name: DB_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ include "motm-app.fullname" . }}-secrets
|
||||
key: db-password
|
||||
# S3 configuration
|
||||
{{- if .Values.s3.enabled }}
|
||||
- name: S3_ENDPOINT
|
||||
value: {{ .Values.s3.endpoint | quote }}
|
||||
- name: S3_REGION
|
||||
value: {{ .Values.s3.region | quote }}
|
||||
- name: S3_BUCKET
|
||||
value: {{ .Values.s3.bucket | quote }}
|
||||
- name: S3_ACCESS_KEY_ID
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ include "motm-app.fullname" . }}-secrets
|
||||
key: s3-access-key
|
||||
- name: S3_SECRET_ACCESS_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ include "motm-app.fullname" . }}-secrets
|
||||
key: s3-secret-key
|
||||
{{- end }}
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: {{ .Values.healthCheck.path }}
|
||||
port: http
|
||||
initialDelaySeconds: {{ .Values.healthCheck.initialDelaySeconds }}
|
||||
periodSeconds: {{ .Values.healthCheck.periodSeconds }}
|
||||
timeoutSeconds: {{ .Values.healthCheck.timeoutSeconds }}
|
||||
failureThreshold: {{ .Values.healthCheck.failureThreshold }}
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: {{ .Values.healthCheck.path }}
|
||||
port: http
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 3
|
||||
resources:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
volumeMounts:
|
||||
- name: config-volume
|
||||
mountPath: /app/database_config.ini
|
||||
subPath: database_config.ini
|
||||
- name: s3-config-volume
|
||||
mountPath: /app/s3_config.json
|
||||
subPath: s3_config.json
|
||||
{{- if .Values.persistence.enabled }}
|
||||
- name: data-volume
|
||||
mountPath: /app/data
|
||||
{{- end }}
|
||||
volumes:
|
||||
- name: config-volume
|
||||
configMap:
|
||||
name: {{ include "motm-app.fullname" . }}-config
|
||||
- name: s3-config-volume
|
||||
configMap:
|
||||
name: {{ include "motm-app.fullname" . }}-s3-config
|
||||
{{- if .Values.persistence.enabled }}
|
||||
- name: data-volume
|
||||
persistentVolumeClaim:
|
||||
claimName: {{ include "motm-app.fullname" . }}-pvc
|
||||
{{- end }}
|
||||
{{- with .Values.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.affinity }}
|
||||
affinity:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.tolerations }}
|
||||
tolerations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
32
motm_app/helm-chart/motm-app/templates/hpa.yaml
Normal file
32
motm_app/helm-chart/motm-app/templates/hpa.yaml
Normal file
@ -0,0 +1,32 @@
|
||||
{{- if .Values.autoscaling.enabled }}
|
||||
apiVersion: autoscaling/v2
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: {{ include "motm-app.fullname" . }}
|
||||
labels:
|
||||
{{- include "motm-app.labels" . | nindent 4 }}
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: {{ include "motm-app.fullname" . }}
|
||||
minReplicas: {{ .Values.autoscaling.minReplicas }}
|
||||
maxReplicas: {{ .Values.autoscaling.maxReplicas }}
|
||||
metrics:
|
||||
{{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
|
||||
{{- end }}
|
||||
{{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
|
||||
- type: Resource
|
||||
resource:
|
||||
name: memory
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
59
motm_app/helm-chart/motm-app/templates/ingress.yaml
Normal file
59
motm_app/helm-chart/motm-app/templates/ingress.yaml
Normal file
@ -0,0 +1,59 @@
|
||||
{{- if .Values.ingress.enabled -}}
|
||||
{{- $fullName := include "motm-app.fullname" . -}}
|
||||
{{- $svcPort := .Values.service.port -}}
|
||||
{{- if and .Values.ingress.className (not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class")) }}
|
||||
{{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}}
|
||||
{{- end }}
|
||||
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
|
||||
apiVersion: networking.k8s.io/v1
|
||||
{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
|
||||
apiVersion: networking.k8s.io/v1beta1
|
||||
{{- else -}}
|
||||
apiVersion: extensions/v1beta1
|
||||
{{- end }}
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: {{ $fullName }}
|
||||
labels:
|
||||
{{- include "motm-app.labels" . | nindent 4 }}
|
||||
{{- with .Values.ingress.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }}
|
||||
ingressClassName: {{ .Values.ingress.className }}
|
||||
{{- end }}
|
||||
{{- if .Values.ingress.tls }}
|
||||
tls:
|
||||
{{- range .Values.ingress.tls }}
|
||||
- hosts:
|
||||
{{- range .hosts }}
|
||||
- {{ . | quote }}
|
||||
{{- end }}
|
||||
secretName: {{ .secretName }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
rules:
|
||||
{{- range .Values.ingress.hosts }}
|
||||
- host: {{ .host | quote }}
|
||||
http:
|
||||
paths:
|
||||
{{- range .paths }}
|
||||
- path: {{ .path }}
|
||||
{{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }}
|
||||
pathType: {{ .pathType }}
|
||||
{{- end }}
|
||||
backend:
|
||||
{{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }}
|
||||
service:
|
||||
name: {{ $fullName }}
|
||||
port:
|
||||
number: {{ $svcPort }}
|
||||
{{- else }}
|
||||
serviceName: {{ $fullName }}
|
||||
servicePort: {{ $svcPort }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
18
motm_app/helm-chart/motm-app/templates/pdb.yaml
Normal file
18
motm_app/helm-chart/motm-app/templates/pdb.yaml
Normal file
@ -0,0 +1,18 @@
|
||||
{{- if .Values.podDisruptionBudget.enabled }}
|
||||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
name: {{ include "motm-app.fullname" . }}
|
||||
labels:
|
||||
{{- include "motm-app.labels" . | nindent 4 }}
|
||||
spec:
|
||||
{{- if .Values.podDisruptionBudget.minAvailable }}
|
||||
minAvailable: {{ .Values.podDisruptionBudget.minAvailable }}
|
||||
{{- end }}
|
||||
{{- if .Values.podDisruptionBudget.maxUnavailable }}
|
||||
maxUnavailable: {{ .Values.podDisruptionBudget.maxUnavailable }}
|
||||
{{- end }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "motm-app.selectorLabels" . | nindent 6 }}
|
||||
{{- end }}
|
||||
21
motm_app/helm-chart/motm-app/templates/pvc.yaml
Normal file
21
motm_app/helm-chart/motm-app/templates/pvc.yaml
Normal file
@ -0,0 +1,21 @@
|
||||
{{- if .Values.persistence.enabled }}
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: {{ include "motm-app.fullname" . }}-pvc
|
||||
labels:
|
||||
{{- include "motm-app.labels" . | nindent 4 }}
|
||||
spec:
|
||||
accessModes:
|
||||
- {{ .Values.persistence.accessMode }}
|
||||
resources:
|
||||
requests:
|
||||
storage: {{ .Values.persistence.size }}
|
||||
{{- if .Values.persistence.storageClass }}
|
||||
{{- if (eq "-" .Values.persistence.storageClass) }}
|
||||
storageClassName: ""
|
||||
{{- else }}
|
||||
storageClassName: "{{ .Values.persistence.storageClass }}"
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
29
motm_app/helm-chart/motm-app/templates/secret.yaml
Normal file
29
motm_app/helm-chart/motm-app/templates/secret.yaml
Normal file
@ -0,0 +1,29 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: {{ include "motm-app.fullname" . }}-secrets
|
||||
labels:
|
||||
{{- include "motm-app.labels" . | nindent 4 }}
|
||||
type: Opaque
|
||||
data:
|
||||
# Database password
|
||||
{{- if .Values.secrets.dbPassword }}
|
||||
db-password: {{ .Values.secrets.dbPassword | b64enc | quote }}
|
||||
{{- else }}
|
||||
db-password: {{ "changeme" | b64enc | quote }}
|
||||
{{- end }}
|
||||
|
||||
{{- if .Values.s3.enabled }}
|
||||
# S3 credentials
|
||||
{{- if .Values.secrets.s3AccessKey }}
|
||||
s3-access-key: {{ .Values.secrets.s3AccessKey | b64enc | quote }}
|
||||
{{- else }}
|
||||
s3-access-key: {{ "changeme" | b64enc | quote }}
|
||||
{{- end }}
|
||||
|
||||
{{- if .Values.secrets.s3SecretKey }}
|
||||
s3-secret-key: {{ .Values.secrets.s3SecretKey | b64enc | quote }}
|
||||
{{- else }}
|
||||
s3-secret-key: {{ "changeme" | b64enc | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
19
motm_app/helm-chart/motm-app/templates/service.yaml
Normal file
19
motm_app/helm-chart/motm-app/templates/service.yaml
Normal file
@ -0,0 +1,19 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ include "motm-app.fullname" . }}
|
||||
labels:
|
||||
{{- include "motm-app.labels" . | nindent 4 }}
|
||||
{{- with .Values.service.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
type: {{ .Values.service.type }}
|
||||
ports:
|
||||
- port: {{ .Values.service.port }}
|
||||
targetPort: {{ .Values.service.targetPort }}
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
{{- include "motm-app.selectorLabels" . | nindent 4 }}
|
||||
12
motm_app/helm-chart/motm-app/templates/serviceaccount.yaml
Normal file
12
motm_app/helm-chart/motm-app/templates/serviceaccount.yaml
Normal file
@ -0,0 +1,12 @@
|
||||
{{- if .Values.serviceAccount.create -}}
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: {{ include "motm-app.serviceAccountName" . }}
|
||||
labels:
|
||||
{{- include "motm-app.labels" . | nindent 4 }}
|
||||
{{- with .Values.serviceAccount.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
128
motm_app/helm-chart/motm-app/values-development.yaml
Normal file
128
motm_app/helm-chart/motm-app/values-development.yaml
Normal file
@ -0,0 +1,128 @@
|
||||
# Development values for MOTM App
|
||||
# Use this file for development/staging environments
|
||||
|
||||
# Application Configuration
|
||||
app:
|
||||
name: motm-app-dev
|
||||
version: "dev"
|
||||
|
||||
# Image Configuration
|
||||
image:
|
||||
repository: your-registry/motm-app
|
||||
tag: "dev" # Use dev tag for development
|
||||
pullPolicy: Always # Always pull latest dev image
|
||||
|
||||
# Resource Limits for Development (lighter)
|
||||
resources:
|
||||
limits:
|
||||
cpu: 200m
|
||||
memory: 256Mi
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 128Mi
|
||||
|
||||
# No autoscaling for development
|
||||
autoscaling:
|
||||
enabled: false
|
||||
|
||||
# Pod Disruption Budget
|
||||
podDisruptionBudget:
|
||||
enabled: false
|
||||
|
||||
# Security Context (more permissive for development)
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
readOnlyRootFilesystem: false # Allow writing for development
|
||||
runAsNonRoot: true
|
||||
runAsUser: 1000
|
||||
|
||||
# Service Configuration
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 80
|
||||
targetPort: 5000
|
||||
|
||||
# Ingress Configuration for Development
|
||||
ingress:
|
||||
enabled: true
|
||||
className: "nginx"
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/rewrite-target: /
|
||||
nginx.ingress.kubernetes.io/ssl-redirect: "false" # No SSL for dev
|
||||
cert-manager.io/cluster-issuer: "letsencrypt-staging"
|
||||
hosts:
|
||||
- host: motm-dev.yourdomain.com
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
tls:
|
||||
- secretName: motm-app-dev-tls
|
||||
hosts:
|
||||
- motm-dev.yourdomain.com
|
||||
|
||||
# Database Configuration for Development
|
||||
database:
|
||||
type: "postgresql"
|
||||
host: "postgresql-dev-service"
|
||||
port: 5432
|
||||
name: "motm_dev"
|
||||
username: "motm_user"
|
||||
|
||||
# S3 Configuration for Development
|
||||
s3:
|
||||
enabled: true
|
||||
endpoint: "https://s3.amazonaws.com"
|
||||
region: "us-east-1"
|
||||
bucket: "motm-assets-dev"
|
||||
|
||||
# Environment Variables for Development
|
||||
env:
|
||||
FLASK_ENV: "development"
|
||||
FLASK_APP: "main.py"
|
||||
FLASK_RUN_HOST: "0.0.0.0"
|
||||
FLASK_RUN_PORT: "5000"
|
||||
PYTHONUNBUFFERED: "1"
|
||||
PYTHONDONTWRITEBYTECODE: "1"
|
||||
FLASK_DEBUG: "1" # Enable debug mode for development
|
||||
|
||||
# Health Checks
|
||||
healthCheck:
|
||||
enabled: true
|
||||
path: "/"
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 30
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 5
|
||||
|
||||
# Persistence for Development
|
||||
persistence:
|
||||
enabled: true
|
||||
storageClass: "standard" # Use standard storage class
|
||||
accessMode: ReadWriteOnce
|
||||
size: 1Gi
|
||||
|
||||
# Monitoring (disabled for development)
|
||||
monitoring:
|
||||
enabled: false
|
||||
|
||||
# Logging
|
||||
logging:
|
||||
level: "DEBUG"
|
||||
format: "text"
|
||||
|
||||
# Labels and Annotations
|
||||
labels:
|
||||
environment: "development"
|
||||
team: "development"
|
||||
|
||||
annotations:
|
||||
deployment.kubernetes.io/revision: "dev"
|
||||
|
||||
podLabels:
|
||||
environment: "development"
|
||||
|
||||
podAnnotations:
|
||||
debug: "true"
|
||||
166
motm_app/helm-chart/motm-app/values-production.yaml
Normal file
166
motm_app/helm-chart/motm-app/values-production.yaml
Normal file
@ -0,0 +1,166 @@
|
||||
# Production values for MOTM App
|
||||
# Use this file as a template for production deployment
|
||||
|
||||
# Application Configuration
|
||||
app:
|
||||
name: motm-app
|
||||
version: "1.0.0"
|
||||
|
||||
# Image Configuration
|
||||
image:
|
||||
repository: your-registry/motm-app
|
||||
tag: "v1.0.0" # Use specific version tags in production
|
||||
pullPolicy: IfNotPresent
|
||||
|
||||
# Resource Limits for Production
|
||||
resources:
|
||||
limits:
|
||||
cpu: 1000m
|
||||
memory: 1Gi
|
||||
requests:
|
||||
cpu: 200m
|
||||
memory: 512Mi
|
||||
|
||||
# Autoscaling for Production
|
||||
autoscaling:
|
||||
enabled: true
|
||||
minReplicas: 2
|
||||
maxReplicas: 10
|
||||
targetCPUUtilizationPercentage: 70
|
||||
targetMemoryUtilizationPercentage: 80
|
||||
|
||||
# Pod Disruption Budget
|
||||
podDisruptionBudget:
|
||||
enabled: true
|
||||
minAvailable: 1
|
||||
|
||||
# Security Context
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
readOnlyRootFilesystem: true
|
||||
runAsNonRoot: true
|
||||
runAsUser: 1000
|
||||
|
||||
# Service Configuration
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 80
|
||||
targetPort: 5000
|
||||
|
||||
# Ingress Configuration for Production
|
||||
ingress:
|
||||
enabled: true
|
||||
className: "nginx"
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/rewrite-target: /
|
||||
nginx.ingress.kubernetes.io/ssl-redirect: "true"
|
||||
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
|
||||
cert-manager.io/cluster-issuer: "letsencrypt-prod"
|
||||
nginx.ingress.kubernetes.io/rate-limit: "100"
|
||||
nginx.ingress.kubernetes.io/rate-limit-window: "1m"
|
||||
hosts:
|
||||
- host: motm.yourdomain.com
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
tls:
|
||||
- secretName: motm-app-tls
|
||||
hosts:
|
||||
- motm.yourdomain.com
|
||||
|
||||
# Database Configuration
|
||||
database:
|
||||
type: "postgresql"
|
||||
host: "postgresql-primary-service"
|
||||
port: 5432
|
||||
name: "motm_prod"
|
||||
username: "motm_user"
|
||||
|
||||
# S3 Configuration for Production
|
||||
s3:
|
||||
enabled: true
|
||||
endpoint: "https://s3.amazonaws.com"
|
||||
region: "us-east-1"
|
||||
bucket: "motm-assets-prod"
|
||||
|
||||
# Environment Variables
|
||||
env:
|
||||
FLASK_ENV: "production"
|
||||
FLASK_APP: "main.py"
|
||||
FLASK_RUN_HOST: "0.0.0.0"
|
||||
FLASK_RUN_PORT: "5000"
|
||||
PYTHONUNBUFFERED: "1"
|
||||
PYTHONDONTWRITEBYTECODE: "1"
|
||||
|
||||
# Health Checks
|
||||
healthCheck:
|
||||
enabled: true
|
||||
path: "/"
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 3
|
||||
|
||||
# Persistence for Production
|
||||
persistence:
|
||||
enabled: true
|
||||
storageClass: "fast-ssd" # Use fast storage class
|
||||
accessMode: ReadWriteOnce
|
||||
size: 10Gi
|
||||
|
||||
# Monitoring
|
||||
monitoring:
|
||||
enabled: true
|
||||
serviceMonitor:
|
||||
enabled: true
|
||||
interval: 30s
|
||||
scrapeTimeout: 10s
|
||||
|
||||
# Logging
|
||||
logging:
|
||||
level: "INFO"
|
||||
format: "json"
|
||||
|
||||
# Node Selector for Production
|
||||
nodeSelector:
|
||||
node-type: "production"
|
||||
|
||||
# Tolerations
|
||||
tolerations:
|
||||
- key: "production"
|
||||
operator: "Equal"
|
||||
value: "true"
|
||||
effect: "NoSchedule"
|
||||
|
||||
# Affinity Rules
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- weight: 100
|
||||
podAffinityTerm:
|
||||
labelSelector:
|
||||
matchExpressions:
|
||||
- key: app.kubernetes.io/name
|
||||
operator: In
|
||||
values:
|
||||
- motm-app
|
||||
topologyKey: kubernetes.io/hostname
|
||||
|
||||
# Labels and Annotations
|
||||
labels:
|
||||
environment: "production"
|
||||
team: "platform"
|
||||
|
||||
annotations:
|
||||
deployment.kubernetes.io/revision: "1"
|
||||
|
||||
podLabels:
|
||||
environment: "production"
|
||||
|
||||
podAnnotations:
|
||||
prometheus.io/scrape: "true"
|
||||
prometheus.io/port: "5000"
|
||||
prometheus.io/path: "/metrics"
|
||||
194
motm_app/helm-chart/motm-app/values.yaml
Normal file
194
motm_app/helm-chart/motm-app/values.yaml
Normal file
@ -0,0 +1,194 @@
|
||||
# Default values for motm-app
|
||||
# This is a YAML-formatted file.
|
||||
# Declare variables to be passed into your templates.
|
||||
|
||||
# Application Configuration
|
||||
app:
|
||||
name: motm-app
|
||||
version: "1.0.0"
|
||||
description: "MOTM Hockey Voting Application"
|
||||
|
||||
# Image Configuration
|
||||
image:
|
||||
repository: your-registry/motm-app
|
||||
tag: "latest"
|
||||
pullPolicy: IfNotPresent
|
||||
# Overrides the image tag whose default is the chart appVersion.
|
||||
# tag: ""
|
||||
|
||||
# Image pull secrets
|
||||
imagePullSecrets: []
|
||||
nameOverride: ""
|
||||
fullnameOverride: ""
|
||||
|
||||
# Service Account
|
||||
serviceAccount:
|
||||
# Specifies whether a service account should be created
|
||||
create: true
|
||||
# Annotations to add to the service account
|
||||
annotations: {}
|
||||
# The name of the service account to use.
|
||||
# If not set and create is true, a name is generated using the fullname template
|
||||
name: ""
|
||||
|
||||
# Pod Security Context
|
||||
podSecurityContext:
|
||||
fsGroup: 1000
|
||||
runAsNonRoot: true
|
||||
runAsUser: 1000
|
||||
|
||||
# Container Security Context
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
readOnlyRootFilesystem: false
|
||||
runAsNonRoot: true
|
||||
runAsUser: 1000
|
||||
|
||||
# Service Configuration
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 80
|
||||
targetPort: 5000
|
||||
annotations: {}
|
||||
|
||||
# Ingress Configuration
|
||||
ingress:
|
||||
enabled: true
|
||||
className: ""
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/rewrite-target: /
|
||||
nginx.ingress.kubernetes.io/ssl-redirect: "true"
|
||||
cert-manager.io/cluster-issuer: "letsencrypt-prod"
|
||||
hosts:
|
||||
- host: motm.yourdomain.com
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
tls:
|
||||
- secretName: motm-app-tls
|
||||
hosts:
|
||||
- motm.yourdomain.com
|
||||
|
||||
# Resource Limits and Requests
|
||||
resources:
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 256Mi
|
||||
|
||||
# Autoscaling
|
||||
autoscaling:
|
||||
enabled: false
|
||||
minReplicas: 1
|
||||
maxReplicas: 10
|
||||
targetCPUUtilizationPercentage: 80
|
||||
targetMemoryUtilizationPercentage: 80
|
||||
|
||||
# Node Selector
|
||||
nodeSelector: {}
|
||||
|
||||
# Tolerations
|
||||
tolerations: []
|
||||
|
||||
# Affinity
|
||||
affinity: {}
|
||||
|
||||
# Pod Disruption Budget
|
||||
podDisruptionBudget:
|
||||
enabled: false
|
||||
minAvailable: 1
|
||||
|
||||
# Database Configuration
|
||||
database:
|
||||
type: "postgresql" # postgresql, mysql, sqlite
|
||||
host: "postgresql-service"
|
||||
port: 5432
|
||||
name: "motm"
|
||||
username: "motm_user"
|
||||
# Password should be set via secret
|
||||
# password: ""
|
||||
|
||||
# S3 Configuration
|
||||
s3:
|
||||
enabled: true
|
||||
endpoint: "https://s3.amazonaws.com"
|
||||
region: "us-east-1"
|
||||
bucket: "motm-assets"
|
||||
accessKeyId: ""
|
||||
secretAccessKey: ""
|
||||
# These should be set via secret
|
||||
|
||||
# Environment Variables
|
||||
env:
|
||||
FLASK_ENV: "production"
|
||||
FLASK_APP: "main.py"
|
||||
FLASK_RUN_HOST: "0.0.0.0"
|
||||
FLASK_RUN_PORT: "5000"
|
||||
PYTHONUNBUFFERED: "1"
|
||||
PYTHONDONTWRITEBYTECODE: "1"
|
||||
|
||||
# ConfigMap for application configuration
|
||||
configMap:
|
||||
databaseConfig: |
|
||||
[DATABASE]
|
||||
type = {{ .Values.database.type }}
|
||||
|
||||
[MYSQL]
|
||||
host = {{ .Values.database.host }}
|
||||
port = {{ .Values.database.port }}
|
||||
database = {{ .Values.database.name }}
|
||||
username = {{ .Values.database.username }}
|
||||
|
||||
[POSTGRESQL]
|
||||
host = {{ .Values.database.host }}
|
||||
port = {{ .Values.database.port }}
|
||||
database = {{ .Values.database.name }}
|
||||
username = {{ .Values.database.username }}
|
||||
|
||||
# Secrets
|
||||
secrets:
|
||||
# Database password
|
||||
dbPassword: ""
|
||||
# S3 credentials
|
||||
s3AccessKey: ""
|
||||
s3SecretKey: ""
|
||||
|
||||
# Health Checks
|
||||
healthCheck:
|
||||
enabled: true
|
||||
path: "/"
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 3
|
||||
|
||||
# Persistence
|
||||
persistence:
|
||||
enabled: false
|
||||
# storageClass: ""
|
||||
accessMode: ReadWriteOnce
|
||||
size: 1Gi
|
||||
|
||||
# Monitoring
|
||||
monitoring:
|
||||
enabled: false
|
||||
serviceMonitor:
|
||||
enabled: false
|
||||
interval: 30s
|
||||
scrapeTimeout: 10s
|
||||
|
||||
# Logging
|
||||
logging:
|
||||
level: "INFO"
|
||||
format: "json"
|
||||
|
||||
# Labels and Annotations
|
||||
labels: {}
|
||||
annotations: {}
|
||||
podLabels: {}
|
||||
podAnnotations: {}
|
||||
@ -136,14 +136,15 @@
|
||||
margin-bottom: 4px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background-color: #e9ecef; /* Light gray background for empty bars */
|
||||
}
|
||||
|
||||
.poty-bar-motm {
|
||||
background: linear-gradient(90deg, #28a745, #20c997);
|
||||
background-color: rgba(40, 167, 69, 0.1); /* Very light green background */
|
||||
}
|
||||
|
||||
.poty-bar-dotd {
|
||||
background: linear-gradient(90deg, #dc3545, #fd7e14);
|
||||
background-color: rgba(220, 53, 69, 0.1); /* Very light red background */
|
||||
}
|
||||
|
||||
.poty-bar-fill {
|
||||
@ -238,7 +239,7 @@ function renderPOTYChart(data) {
|
||||
return a.dotdTotal - b.dotdTotal;
|
||||
});
|
||||
|
||||
// Find max values for scaling - each bar scales independently
|
||||
// Find max values for independent scaling - each bar type scales to its own maximum
|
||||
const maxMotm = Math.max(...data.map(p => p.motmTotal || 0));
|
||||
const maxDotd = Math.max(...data.map(p => p.dotdTotal || 0));
|
||||
|
||||
@ -249,6 +250,9 @@ function renderPOTYChart(data) {
|
||||
const ranking = index + 1;
|
||||
const rankingClass = ranking === 1 ? 'gold' : ranking === 2 ? 'silver' : ranking === 3 ? 'bronze' : '';
|
||||
|
||||
const motmVotes = player.motmTotal || 0;
|
||||
const dotdVotes = player.dotdTotal || 0;
|
||||
|
||||
html += '<div class="poty-player-card">';
|
||||
html += '<div class="d-flex align-items-center">';
|
||||
html += '<div class="poty-ranking ' + rankingClass + '">' + ranking + '</div>';
|
||||
@ -256,19 +260,19 @@ function renderPOTYChart(data) {
|
||||
html += '<div class="poty-player-name">' + (player.playerName || 'Unknown Player') + '</div>';
|
||||
html += '<div class="poty-stats">';
|
||||
html += '<div class="poty-stat">';
|
||||
html += '<div class="poty-stat-value text-success">' + (player.motmTotal || 0) + '</div>';
|
||||
html += '<div class="poty-stat-value text-success">' + motmVotes + '</div>';
|
||||
html += '<div class="poty-stat-label">MOTM</div>';
|
||||
html += '</div>';
|
||||
html += '<div class="poty-stat">';
|
||||
html += '<div class="poty-stat-value text-danger">' + (player.dotdTotal || 0) + '</div>';
|
||||
html += '<div class="poty-stat-value text-danger">' + dotdVotes + '</div>';
|
||||
html += '<div class="poty-stat-label">DotD</div>';
|
||||
html += '</div>';
|
||||
html += '<div class="poty-bar-container">';
|
||||
html += '<div class="poty-bar poty-bar-motm">';
|
||||
html += '<div class="poty-bar-fill" style="width: ' + (maxMotm > 0 ? ((player.motmTotal || 0) / maxMotm * 100) : 0) + '%"></div>';
|
||||
html += '<div class="poty-bar-fill" style="width: ' + (maxMotm > 0 ? (motmVotes / maxMotm * 100) : 0) + '%"></div>';
|
||||
html += '</div>';
|
||||
html += '<div class="poty-bar poty-bar-dotd">';
|
||||
html += '<div class="poty-bar-fill" style="width: ' + (maxDotd > 0 ? ((player.dotdTotal || 0) / maxDotd * 100) : 0) + '%"></div>';
|
||||
html += '<div class="poty-bar-fill" style="width: ' + (maxDotd > 0 ? (dotdVotes / maxDotd * 100) : 0) + '%"></div>';
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
|
||||
@ -136,14 +136,15 @@
|
||||
margin-bottom: 4px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background-color: #e9ecef; /* Light gray background for empty bars */
|
||||
}
|
||||
|
||||
.vote-bar-motm {
|
||||
background: linear-gradient(90deg, #28a745, #20c997);
|
||||
background-color: rgba(40, 167, 69, 0.1); /* Very light green background */
|
||||
}
|
||||
|
||||
.vote-bar-dotd {
|
||||
background: linear-gradient(90deg, #dc3545, #fd7e14);
|
||||
background-color: rgba(220, 53, 69, 0.1); /* Very light red background */
|
||||
}
|
||||
|
||||
.vote-bar-fill {
|
||||
@ -250,12 +251,14 @@ function renderVoteChart(data) {
|
||||
return aDotd - bDotd;
|
||||
});
|
||||
|
||||
// Find max values for scaling - each bar scales independently
|
||||
// Find max values for independent scaling - each bar type scales to its own maximum
|
||||
const maxMotm = Math.max(...data.map(p => p[motmKey] || 0));
|
||||
const maxDotd = Math.max(...data.map(p => p[dotdKey] || 0));
|
||||
|
||||
let html = '';
|
||||
|
||||
console.log('Max values calculated:', {maxMotm, maxDotd});
|
||||
|
||||
data.forEach((player, index) => {
|
||||
console.log('Processing vote player:', player);
|
||||
const ranking = index + 1;
|
||||
@ -264,6 +267,8 @@ function renderVoteChart(data) {
|
||||
const motmVotes = player[motmKey] || 0;
|
||||
const dotdVotes = player[dotdKey] || 0;
|
||||
|
||||
console.log(`${player.playerName}: MOTM=${motmVotes}, DotD=${dotdVotes}`);
|
||||
|
||||
html += '<div class="vote-player-card">';
|
||||
html += '<div class="d-flex align-items-center">';
|
||||
html += '<div class="vote-ranking ' + rankingClass + '">' + ranking + '</div>';
|
||||
@ -278,12 +283,18 @@ function renderVoteChart(data) {
|
||||
html += '<div class="vote-stat-value text-danger">' + dotdVotes + '</div>';
|
||||
html += '<div class="vote-stat-label">DotD</div>';
|
||||
html += '</div>';
|
||||
// Calculate percentages with debugging
|
||||
const motmPercent = maxMotm > 0 ? (motmVotes / maxMotm * 100) : 0;
|
||||
const dotdPercent = maxDotd > 0 ? (dotdVotes / maxDotd * 100) : 0;
|
||||
|
||||
console.log(`${player.playerName} percentages: MOTM=${motmPercent.toFixed(1)}%, DotD=${dotdPercent.toFixed(1)}%`);
|
||||
|
||||
html += '<div class="vote-bar-container">';
|
||||
html += '<div class="vote-bar vote-bar-motm">';
|
||||
html += '<div class="vote-bar-fill" style="width: ' + (maxMotm > 0 ? (motmVotes / maxMotm * 100) : 0) + '%"></div>';
|
||||
html += '<div class="vote-bar-fill" style="width: ' + motmPercent + '%"></div>';
|
||||
html += '</div>';
|
||||
html += '<div class="vote-bar vote-bar-dotd">';
|
||||
html += '<div class="vote-bar-fill" style="width: ' + (maxDotd > 0 ? (dotdVotes / maxDotd * 100) : 0) + '%"></div>';
|
||||
html += '<div class="vote-bar-fill" style="width: ' + dotdPercent + '%"></div>';
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
@ -294,6 +305,11 @@ function renderVoteChart(data) {
|
||||
|
||||
container.innerHTML = html;
|
||||
|
||||
// Verify bar widths are set correctly
|
||||
setTimeout(() => {
|
||||
verifyBarWidths();
|
||||
}, 100);
|
||||
|
||||
// Animate bars after a short delay
|
||||
setTimeout(() => {
|
||||
const bars = container.querySelectorAll('.vote-bar-fill');
|
||||
@ -326,5 +342,15 @@ function testVoteChart() {
|
||||
console.log('Testing with sample vote data:', testData);
|
||||
renderVoteChart(testData);
|
||||
}
|
||||
|
||||
// Add a simple test to verify bar widths are being set
|
||||
function verifyBarWidths() {
|
||||
const bars = document.querySelectorAll('.vote-bar-fill');
|
||||
console.log('Found bars:', bars.length);
|
||||
bars.forEach((bar, index) => {
|
||||
const width = bar.style.width;
|
||||
console.log(`Bar ${index}: width = ${width}`);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
Loading…
Reference in New Issue
Block a user