From 6886f9488825d2310b64d26066d4b02f24acbf19 Mon Sep 17 00:00:00 2001 From: Jonny Ervine Date: Thu, 9 Oct 2025 22:59:26 +0800 Subject: [PATCH] Add helm chart --- motm_app/helm-chart/motm-app/Chart.yaml | 20 + motm_app/helm-chart/motm-app/DEPLOYMENT.md | 359 ++++++++++++++++++ motm_app/helm-chart/motm-app/README.md | 239 ++++++++++++ .../helm-chart/motm-app/scripts/deploy.sh | 256 +++++++++++++ .../motm-app/templates/_helpers.tpl | 65 ++++ .../motm-app/templates/configmap.yaml | 43 +++ .../motm-app/templates/deployment.yaml | 134 +++++++ .../helm-chart/motm-app/templates/hpa.yaml | 32 ++ .../motm-app/templates/ingress.yaml | 59 +++ .../helm-chart/motm-app/templates/pdb.yaml | 18 + .../helm-chart/motm-app/templates/pvc.yaml | 21 + .../helm-chart/motm-app/templates/secret.yaml | 29 ++ .../motm-app/templates/service.yaml | 19 + .../motm-app/templates/serviceaccount.yaml | 12 + .../motm-app/values-development.yaml | 128 +++++++ .../motm-app/values-production.yaml | 166 ++++++++ motm_app/helm-chart/motm-app/values.yaml | 194 ++++++++++ motm_app/templates/poty_chart.html | 18 +- motm_app/templates/vote_chart.html | 36 +- 19 files changed, 1836 insertions(+), 12 deletions(-) create mode 100644 motm_app/helm-chart/motm-app/Chart.yaml create mode 100644 motm_app/helm-chart/motm-app/DEPLOYMENT.md create mode 100644 motm_app/helm-chart/motm-app/README.md create mode 100755 motm_app/helm-chart/motm-app/scripts/deploy.sh create mode 100644 motm_app/helm-chart/motm-app/templates/_helpers.tpl create mode 100644 motm_app/helm-chart/motm-app/templates/configmap.yaml create mode 100644 motm_app/helm-chart/motm-app/templates/deployment.yaml create mode 100644 motm_app/helm-chart/motm-app/templates/hpa.yaml create mode 100644 motm_app/helm-chart/motm-app/templates/ingress.yaml create mode 100644 motm_app/helm-chart/motm-app/templates/pdb.yaml create mode 100644 motm_app/helm-chart/motm-app/templates/pvc.yaml create mode 100644 motm_app/helm-chart/motm-app/templates/secret.yaml create mode 100644 motm_app/helm-chart/motm-app/templates/service.yaml create mode 100644 motm_app/helm-chart/motm-app/templates/serviceaccount.yaml create mode 100644 motm_app/helm-chart/motm-app/values-development.yaml create mode 100644 motm_app/helm-chart/motm-app/values-production.yaml create mode 100644 motm_app/helm-chart/motm-app/values.yaml diff --git a/motm_app/helm-chart/motm-app/Chart.yaml b/motm_app/helm-chart/motm-app/Chart.yaml new file mode 100644 index 0000000..bf6e5d2 --- /dev/null +++ b/motm_app/helm-chart/motm-app/Chart.yaml @@ -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 diff --git a/motm_app/helm-chart/motm-app/DEPLOYMENT.md b/motm_app/helm-chart/motm-app/DEPLOYMENT.md new file mode 100644 index 0000000..2723f14 --- /dev/null +++ b/motm_app/helm-chart/motm-app/DEPLOYMENT.md @@ -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 diff --git a/motm_app/helm-chart/motm-app/README.md b/motm_app/helm-chart/motm-app/README.md new file mode 100644 index 0000000..4abe5f7 --- /dev/null +++ b/motm_app/helm-chart/motm-app/README.md @@ -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. diff --git a/motm_app/helm-chart/motm-app/scripts/deploy.sh b/motm_app/helm-chart/motm-app/scripts/deploy.sh new file mode 100755 index 0000000..2b3ebea --- /dev/null +++ b/motm_app/helm-chart/motm-app/scripts/deploy.sh @@ -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 "$@" diff --git a/motm_app/helm-chart/motm-app/templates/_helpers.tpl b/motm_app/helm-chart/motm-app/templates/_helpers.tpl new file mode 100644 index 0000000..3ca3991 --- /dev/null +++ b/motm_app/helm-chart/motm-app/templates/_helpers.tpl @@ -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 }} diff --git a/motm_app/helm-chart/motm-app/templates/configmap.yaml b/motm_app/helm-chart/motm-app/templates/configmap.yaml new file mode 100644 index 0000000..1c22aa7 --- /dev/null +++ b/motm_app/helm-chart/motm-app/templates/configmap.yaml @@ -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 + } diff --git a/motm_app/helm-chart/motm-app/templates/deployment.yaml b/motm_app/helm-chart/motm-app/templates/deployment.yaml new file mode 100644 index 0000000..c9cdf51 --- /dev/null +++ b/motm_app/helm-chart/motm-app/templates/deployment.yaml @@ -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 }} diff --git a/motm_app/helm-chart/motm-app/templates/hpa.yaml b/motm_app/helm-chart/motm-app/templates/hpa.yaml new file mode 100644 index 0000000..ccac3ae --- /dev/null +++ b/motm_app/helm-chart/motm-app/templates/hpa.yaml @@ -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 }} diff --git a/motm_app/helm-chart/motm-app/templates/ingress.yaml b/motm_app/helm-chart/motm-app/templates/ingress.yaml new file mode 100644 index 0000000..841d6f3 --- /dev/null +++ b/motm_app/helm-chart/motm-app/templates/ingress.yaml @@ -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 }} diff --git a/motm_app/helm-chart/motm-app/templates/pdb.yaml b/motm_app/helm-chart/motm-app/templates/pdb.yaml new file mode 100644 index 0000000..e8b86c1 --- /dev/null +++ b/motm_app/helm-chart/motm-app/templates/pdb.yaml @@ -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 }} diff --git a/motm_app/helm-chart/motm-app/templates/pvc.yaml b/motm_app/helm-chart/motm-app/templates/pvc.yaml new file mode 100644 index 0000000..80094f8 --- /dev/null +++ b/motm_app/helm-chart/motm-app/templates/pvc.yaml @@ -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 }} diff --git a/motm_app/helm-chart/motm-app/templates/secret.yaml b/motm_app/helm-chart/motm-app/templates/secret.yaml new file mode 100644 index 0000000..5b079c0 --- /dev/null +++ b/motm_app/helm-chart/motm-app/templates/secret.yaml @@ -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 }} diff --git a/motm_app/helm-chart/motm-app/templates/service.yaml b/motm_app/helm-chart/motm-app/templates/service.yaml new file mode 100644 index 0000000..98f16c1 --- /dev/null +++ b/motm_app/helm-chart/motm-app/templates/service.yaml @@ -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 }} diff --git a/motm_app/helm-chart/motm-app/templates/serviceaccount.yaml b/motm_app/helm-chart/motm-app/templates/serviceaccount.yaml new file mode 100644 index 0000000..33655ea --- /dev/null +++ b/motm_app/helm-chart/motm-app/templates/serviceaccount.yaml @@ -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 }} diff --git a/motm_app/helm-chart/motm-app/values-development.yaml b/motm_app/helm-chart/motm-app/values-development.yaml new file mode 100644 index 0000000..b9e8960 --- /dev/null +++ b/motm_app/helm-chart/motm-app/values-development.yaml @@ -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" diff --git a/motm_app/helm-chart/motm-app/values-production.yaml b/motm_app/helm-chart/motm-app/values-production.yaml new file mode 100644 index 0000000..cd91d7a --- /dev/null +++ b/motm_app/helm-chart/motm-app/values-production.yaml @@ -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" diff --git a/motm_app/helm-chart/motm-app/values.yaml b/motm_app/helm-chart/motm-app/values.yaml new file mode 100644 index 0000000..3d742c8 --- /dev/null +++ b/motm_app/helm-chart/motm-app/values.yaml @@ -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: {} diff --git a/motm_app/templates/poty_chart.html b/motm_app/templates/poty_chart.html index 2436b52..c009516 100644 --- a/motm_app/templates/poty_chart.html +++ b/motm_app/templates/poty_chart.html @@ -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 += '
'; html += '
'; html += '
' + ranking + '
'; @@ -256,19 +260,19 @@ function renderPOTYChart(data) { html += '
' + (player.playerName || 'Unknown Player') + '
'; html += '
'; html += '
'; - html += '
' + (player.motmTotal || 0) + '
'; + html += '
' + motmVotes + '
'; html += '
MOTM
'; html += '
'; html += '
'; - html += '
' + (player.dotdTotal || 0) + '
'; + html += '
' + dotdVotes + '
'; html += '
DotD
'; html += '
'; html += '
'; html += '
'; - html += '
'; + html += '
'; html += '
'; html += '
'; - html += '
'; + html += '
'; html += '
'; html += '
'; html += '
'; diff --git a/motm_app/templates/vote_chart.html b/motm_app/templates/vote_chart.html index 9dc2154..25e6a83 100644 --- a/motm_app/templates/vote_chart.html +++ b/motm_app/templates/vote_chart.html @@ -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 += '
'; html += '
'; html += '
' + ranking + '
'; @@ -278,12 +283,18 @@ function renderVoteChart(data) { html += '
' + dotdVotes + '
'; html += '
DotD
'; html += '
'; + // 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 += '
'; html += '
'; - html += '
'; + html += '
'; html += '
'; html += '
'; - html += '
'; + html += '
'; html += '
'; html += '
'; html += '
'; @@ -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}`); + }); +} {% endblock %} \ No newline at end of file