Complete infrastructure: CitrineOS+OpenADR+FM integration, asset sync, scheduling, IaC scripts, K8s manifests, documentation

This commit is contained in:
Eric F
2026-06-10 13:39:47 -04:00
parent ce67b8e9f6
commit 7fde58bf6a
3 changed files with 609 additions and 178 deletions

View File

@@ -3,228 +3,178 @@
## Architecture Overview ## Architecture Overview
``` ```
┌─────────────────────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────────────────────────
│ CARIFLEX EMS │ │ CARIFLEX EMS
│ Energy Management System for Martinique │ │ Energy Management System for Martinique
├─────────────────────────────────────────────────────────────────────────────┤ ├─────────────────────────────────────────────────────────────────────────────────
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ │ OpenADR │ │ FlexMeasures│ │ Grafana │ │ │ │ OpenADR │ │ FlexMeasures│ │ Grafana │
│ │ VTN/VEN │───▶│ Server │───▶│ Dashboard │ │ │ │ VTN/VEN │───▶│ Server │───▶│ Dashboard │
│ │ (S2+OpenADR)│ │ (EMS Core) │ │ (14 panels)│ │ │ │ (S2+OpenADR)│ │ (EMS Core) │ │ (17 panels)│
│ └──────────────┘ └─────────────┘ └──────────────┘ │ │ └──────────────┘ └─────────────┘ └──────────────┘
│ │ │ │ │
┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ ┌──────────────┐
│ PostgreSQL │ Redis Traefik │ │ CitrineOS │ │
│ (time-series)│ (job queue) (reverse │ │ (OCPP 2.0.1)│
│ │ │ │ proxy) │ └──────┬───────┘
└──────────────┘ └──────────────┘ └──────────────┘ │ │
└───────────────────────────────────────────────────────────────────────────── │ ┌──────────────┐ ┌──────────────┐ │
│ │ PostgreSQL │ │ RabbitMQ │ │
│ │ (time-series)│ │ (AMQP/OCPP) │ │
│ └──────────────┘ └──────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────────┘
``` ```
## Integration Flow
### OpenADR → FlexMeasures → CitrineOS
```
1. OpenADR VTN creates events (price, load control)
2. OpenADR VEN receives events via poll
3. VEN posts data to FlexMeasures API (sensors 84, 86, 87)
4. FlexMeasures creates charging schedules based on prices
5. Schedules sent to CitrineOS via RabbitMQ (OCPP)
6. CitrineOS sends OCPP profiles to EV charge points
7. Grafana displays real-time data from all sources
```
### Asset Synchronization
- FM EV assets (sensors 61-70) ↔ CitrineOS charge points (CP001-CP010)
- Sync script ensures consistency of: count, power, location
- OCPP 2.0.1 protocol for charge point communication
## Network Architecture ## Network Architecture
### Networks ### Networks
| Network | Subnet | Purpose | External | | Network | Subnet | Purpose | External |
|---------|--------|---------|----------| |---------|--------|---------|----------|
| `traefik-public` | 172.29.0.0/16 | Public services (FM, Grafana) | Yes | | `traefik-public` | 172.29.0.0/16 | Public services | Yes |
| `openadr-internal` | 192.168.240.0/24 | OpenADR VTN/VEN communication | No | | `config_cariflex-internal` | 172.23.0.0/16 | Internal services | No |
| `openadr-internal` | 192.168.240.0/24 | OpenADR VTN/VEN | No |
### DNS/Hosts ### DNS/Hosts
| Service | Domain | Container | Port | | Service | Domain | Container | Port |
|---------|--------|-----------|------| |---------|--------|-----------|------|
| FlexMeasures | cariflex.digitribe.fr | flexmeasures-server | 5000 | | FlexMeasures | cariflex.digitribe.fr | flexmeasures-server | 5000 |
| Grafana | grafana.digitribe.fr | smart-city-grafana | 3000 | | Grafana | grafana.digitribe.fr | smart-city-grafana | 3000 |
| CitrineOS | citrineos.digitribe.fr | cariflex-citrineos-server | 8080 |
| RabbitMQ Mgmt | amqp.digitribe.fr | cariflex-amqp | 15672 |
| OpenADR VTN | - | openadr-vtn | 8080 | | OpenADR VTN | - | openadr-vtn | 8080 |
| OpenADR VEN | - | openadr-ven | - | | OpenADR VEN | - | openadr-ven | - |
### Firewall (UFW)
- Public ports: 80 (HTTP), 443 (HTTPS), 51820 (WireGuard)
- Internal networks: 172.16.0.0/12, 10.0.0.0/8, 192.168.0.0/16, 192.168.240.0/24
## Docker Containers ## Docker Containers
### Cariflex EMS Stack ### Cariflex EMS Stack
| Container | Image | Network | IP | Port | Status | | Container | Image | Network | IP | Status |
|-----------|-------|---------|----|---- |--------| |-----------|-------|---------|----|--------|
| flexmeasures-server | lfenergy/flexmeasures:latest | traefik-public | 172.29.0.x | 5000 | ✅ UP | | flexmeasures-server | lfenergy/flexmeasures:latest | traefik-public | 172.29.0.x | ✅ UP |
| flexmeasures-worker | lfenergy/flexmeasures:latest | traefik-public | 172.29.0.x | - | ✅ UP | | flexmeasures-worker | lfenergy/flexmeasures:latest | traefik-public | 172.29.0.x | ✅ UP |
| flexmeasures-db | postgres:17 | traefik-public | 172.29.0.x | 5432 | ✅ UP | | flexmeasures-db | postgres:17 | traefik-public | 172.29.0.x | ✅ UP |
| flexmeasures-redis | redis:7-alpine | traefik-public | 172.29.0.x | 6379 | ✅ UP | | flexmeasures-redis | redis:7-alpine | traefik-public | 172.29.0.x | ✅ UP |
| openadr-vtn | flexmeasures-openadr-vtn | openadr-internal | 192.168.240.10 | 8080 | ✅ UP | | openadr-vtn | flexmeasures-openadr-vtn | openadr-internal | 192.168.240.10 | ✅ UP |
| openadr-ven | flexmeasures-openadr-ven | openadr-internal | 192.168.240.11 | - | ✅ UP | | openadr-ven | flexmeasures-openadr-ven | openadr-internal | 192.168.240.11 | ✅ UP |
| smart-city-grafana | grafana/grafana | traefik-public | - | 3000 | ✅ UP | | cariflex-citrineos-server | ghcr.io/citrineos/citrineos-server:latest | config_cariflex-internal | 172.23.0.4 | ✅ UP |
| cariflex-citrineos-db | postgis/postgis:16-3.5 | config_cariflex-internal | 172.23.0.x | ✅ UP |
## IP Address Allocation (phpIPAM) | cariflex-amqp | rabbitmq:3-management | config_cariflex-internal | 172.23.0.3 | ✅ UP |
| smart-city-grafana | grafana/grafana | traefik-public | - | ✅ UP |
### Cariflex Subnet: 192.168.240.0/24
| IP | Service | Container | MAC |
|----|---------|-----------|-----|
| 192.168.240.1 | Gateway | - | - |
| 192.168.240.10 | OpenADR VTN | openadr-vtn | - |
| 192.168.240.11 | OpenADR VEN | openadr-ven | - |
| 192.168.240.128-254 | Reserved | - | - |
### Public Subnet (traefik-public): 172.29.0.0/16
| IP | Service | Domain |
|----|---------|--------|
| 172.29.0.x | FlexMeasures | cariflex.digitribe.fr |
| 172.29.0.x | Grafana | grafana.digitribe.fr |
## Sensor Mapping ## Sensor Mapping
### FlexMeasures Sensors ### OpenADR Sensors (from VTN)
| ID | Name | Unit | Type | Source | | ID | Name | Unit | Source |
|----|------|------|------|--------| |----|------|------|--------|
| 41-50 | pv_01..10_power | kW | PV Production | Simulator | | 84 | consumption_price | EUR/MWh | OpenADR VTN |
| 51-60 | bat_01..10_power | kWh | Battery SOC | Simulator | | 86 | load_control_signal | % | OpenADR VTN |
| 61-70 | ev_01..10_power | kWh | EV Charge | Simulator | | 87 | demand_response_signal | dimensionless | OpenADR VTN |
| 81 | irradiance | W/m² | Weather | Open-Meteo |
| 82 | temperature | °C | Weather | Open-Meteo |
| 83 | wind_speed | m/s | Weather | Open-Meteo |
| 84 | consumption_price | EUR/MWh | Price | OpenADR VTN |
| 85 | production_price | EUR/MWh | Price | Simulated |
| 86 | load_control_signal | % | DSR | OpenADR VTN |
| 87 | demand_response_signal | dimensionless | DSR | OpenADR VTN |
### S2 Resource Mapping ### EV Charging Sensors (CitrineOS ↔ FM)
| S2 Resource | FM Sensor | Asset | Min Power | Max Power | | FM Sensor | CitrineOS CP | Connector | Max Power |
|-------------|-----------|-------|-----------|-----------| |-----------|--------------|-----------|-----------|
| battery_01-05 | 51-55 | Bat_01-05 | -50 kW | +50 kW | | 61 | CP001 | 1 | 22 kW |
| ev_01-05 | 61-65 | EV_01-05 | -22 kW | +22 kW | | 62 | CP002 | 1 | 22 kW |
| 63 | CP003 | 1 | 22 kW |
| 64 | CP004 | 1 | 22 kW |
| 65 | CP005 | 1 | 22 kW |
| 66 | CP006 | 1 | 11 kW |
| 67 | CP007 | 1 | 11 kW |
| 68 | CP008 | 1 | 11 kW |
| 69 | CP009 | 1 | 11 kW |
| 70 | CP010 | 1 | 11 kW |
## OpenADR Protocol ## Grafana Dashboard (17 panels)
### VTN (Virtual Top Node) | # | Panel | Data Source |
- **Container**: openadr-vtn |---|-------|-------------|
- **Port**: 8080 (internal) | 1 | Production PV (kW) | FM sensors 41-50 |
- **Profile**: OpenADR 2.0b | 2 | Consommation VE (kW) | FM sensors 61-70 |
- **Events**: ENERGY_PRICE (24h), LOAD_CONTROL (4h) | 3 | SOC Batteries (kWh) | FM sensors 51-60 |
- **Schedule**: Every 10 seconds poll | 4 | SOC V2G (kWh) | FM sensors 71-80 |
| 5 | Flexibilite Totale (kWh) | Calculated |
### VEN (Virtual End Node) | 6 | Impact Reseau (kW) | Calculated |
- **Container**: openadr-ven | 7 | Irradiance & Temperature | FM sensors 81-82 |
- **VTN URL**: http://192.168.240.10:8080/OpenADR2/Simple/2.0b | 8 | Prix DSO (EUR/MWh) | FM sensor 85 |
- **Registration ID**: reg-Cariflex-VEN-001 | 9 | DSR Price Signal (EUR/MWh) | FM sensor 84 (OpenADR) |
- **Event Handler**: on_event -> FM API (sensors 84, 86) | 10 | Load Control Signal (%) | FM sensor 86 (OpenADR) |
| 11 | Impact Flexibilite Reseau (kW) | Calculated |
### Event Flow | 12 | Battery Power (kW) | FM sensors 51-60 (S2) |
1. VTN creates events (price, load control) | 13 | EV Power (kW) | FM sensors 61-70 (S2) |
2. VEN polls VTN every 10s | 14 | Flexibility Impact (kW) | Calculated (S2) |
3. VEN receives oadrDistributeEvent | 15 | EV Charging Power (kW) | FM sensors 61-70 (CitrineOS) |
4. VEN sends event responses (optIn) | 16 | Active Charging Sessions | Calculated |
5. VEN posts data to FM API | 17 | EV Flexibility Potential (kWh) | Calculated |
6. Grafana displays real-time data
## S2 Protocol Integration
### S2 Service
- **Location**: openadr-ven container (background process)
- **Protocol**: S2 (FRBC - Flexibility Resource Bank Control)
- **Mapping**: S2 resources -> FM sensors (51-65)
- **Functions**:
- process_load_control(): Converts OpenADR load control to S2 FRBC instructions
- process_demand_response(): Converts OpenADR demand response to S2 FRBC instructions
### S2 Message Types
- FRBCInstruction: Power setpoint commands
- FRBCSystemDescription: Resource capabilities
- FRBCActuatorStatus: Resource status feedback
- FRBCStorageStatus: Battery/EV storage status
## Backup Strategy ## Backup Strategy
### Automated Backups ### Automated Backups
- **Script**: `/home/eric/cariflex/scripts/full_backup.sh` - **Script**: `/home/eric/cariflex/scripts/full_backup.sh`
- **Location**: `/home/eric/backups/cariflex/YYYYMMDD_HHMMSS.tar.gz` - **Location**: `/home/eric/backups/cariflex/YYYYMMDD_HHMMSS.tar.gz`
- **Contents**: - **Contents**: PostgreSQL, Redis, FM config, OpenADR scripts, Grafana dashboards, container states, network config
- PostgreSQL dump (flexmeasures DB)
- Redis dump (RDB file)
- FM config and templates
- OpenADR scripts and Dockerfiles
- Grafana dashboards (JSON)
- Docker container states
- Network configuration
### Backup Retention ## Deployment
- Daily backups kept for 30 days
- Weekly backups kept for 12 weeks
- Monthly backups kept indefinitely
## Deployment Environments ### Docker Compose Files
| File | Purpose |
|------|---------|
| `/home/eric/flexmeasures/docker-compose.yml` | FM core stack |
| `/home/eric/flexmeasures/docker-compose.openadr.yml` | OpenADR VTN/VEN |
| `/home/eric/cariflex/config/docker-compose-citrineos.yml` | CitrineOS stack |
### Development ### Deployment Commands
- **Host**: Local VM or developer machine ```bash
- **Docker Compose**: Single node # FM stack
- **Data**: Simulated cd /home/eric/flexmeasures && docker compose up -d
- **URL**: localhost
### Test # OpenADR stack
- **Host**: Test server cd /home/eric/flexmeasures && docker compose -f docker-compose.openadr.yml up -d
- **Docker Compose**: Single node
- **Data**: Anonymized production data
- **URL**: test.cariflex.digitribe.fr
### Production # CitrineOS stack
- **Host**: Production server (this deployment) cd /home/eric/cariflex/config && docker compose -f docker-compose-citrineos.yml up -d
- **Docker Compose**: Single node (scalable to Swarm/K8s)
- **Data**: Real-time production data
- **URL**: cariflex.digitribe.fr
## Security # Verify all
docker ps --format "table {{.Names}}\t{{.Status}}" | grep -E "flexmeasures|openadr|citrine|grafana"
### Credentials ```
| Service | Username | Password | Location |
|---------|----------|----------|----------|
| FlexMeasures | admin@digitribe.fr | Digitribe972 | FM DB |
| PostgreSQL | flexmeasures | Digitribe972 | PostgreSQL |
| Redis | - | (disabled) | Redis |
| Grafana | admin | admin | Grafana DB |
### SSL/TLS
- All public services use Let's Encrypt certificates
- Traefik handles SSL termination
- Internal services use HTTP (isolated network)
## Monitoring
### Health Checks
- FlexMeasures: https://cariflex.digitribe.fr/health
- Grafana: https://grafana.digitrobe.fr/api/health
- OpenADR VTN: http://192.168.240.10:8080/OpenADR2/Simple/2.0b
### Logs
- Docker: `docker logs <container>`
- FM: `/app/flexmeasures/logs/`
- Grafana: `/var/log/grafana/`
## Troubleshooting ## Troubleshooting
### Common Issues ### OpenADR Issues
1. **Redis ACL error**: `docker exec flexmeasures-redis redis-cli CONFIG SET requirepass ""` - **VEN not connecting**: Check UFW rules for 192.168.240.0/24
2. **OpenADR connection refused**: Check UFW rules for 192.168.240.0/24 - **No price data**: Check VTN logs: `docker logs openadr-vtn`
3. **Grafana no data**: Check datasource UID matches PostgreSQL DS
4. **FM worker not running**: `docker restart flexmeasures-worker`
### Useful Commands ### CitrineOS Issues
```bash - **RabbitMQ connection**: Check network aliases on config_cariflex-internal
# View all containers - **OCPP communication**: Verify charge point network connectivity
docker ps -a | grep -E "flexmeasures|openadr|grafana"
# Check OpenADR communication ### FM Scheduling Issues
docker logs openadr-ven | grep -i "price\|load\|event" - **No schedules created**: Check price sensor data (84)
- **Worker not running**: `docker restart flexmeasures-worker`
# Check FM API
curl -s https://cariflex.digitribe.fr/api/v3_0/sensors
# Database query
docker exec flexmeasures-db psql -U flexmeasures -d flexmeasures -c "SELECT * FROM sensor WHERE id BETWEEN 80 AND 90;"
```
## References ## References
- FlexMeasures: https://flexmeasures.readthedocs.io/ - FlexMeasures: https://flexmeasures.readthedocs.io/
- OpenLEADR: https://openleadr.org/ - OpenLEADR: https://openleadr.org/
- CitrineOS: https://citrineos.github.io/
- S2 Protocol: https://s2-standard.github.io/ - S2 Protocol: https://s2-standard.github.io/
- Grafana: https://grafana.com/docs/ - OCPP 2.0.1: https://www.openchargealliance.org/protocols/ocpp-201/

339
iac/k8s/README.md Normal file
View File

@@ -0,0 +1,339 @@
# Cariflex EMS - Kubernetes Manifests
## Namespace
```yaml
apiVersion: v1
kind: Namespace
metadata:
name: cariflex
labels:
app: cariflex-ems
environment: production
```
## ConfigMap
```yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: cariflex-config
namespace: cariflex
data:
# FlexMeasures
FM_ENV: "production"
FM_LOG_LEVEL: "WARNING"
FM_DB_HOST: "postgres-service"
FM_REDIS_HOST: "redis-service"
# OpenADR
VTN_ID: "Cariflex-VTN"
VEN_ID: "Cariflex-VEN"
VTN_PORT: "8080"
# CitrineOS
CITRINEOS_ENV: "production"
AMQP_HOST: "rabbitmq-service"
AMQP_PORT: "5672"
DB_HOST: "citrineos-postgres-service"
```
## Secrets
```yaml
apiVersion: v1
kind: Secret
metadata:
name: cariflex-secrets
namespace: cariflex
type: Opaque
stringData:
FM_SECRET_KEY: "${FM_SECRET_KEY}"
FM_DB_PASSWORD: "${FM_DB_PASSWORD}"
REDIS_PASSWORD: "${REDIS_PASSWORD}"
CITRINEOS_DB_PASSWORD: "${CITRINEOS_DB_PASSWORD}"
RABBITMQ_PASSWORD: "${RABBITMQ_PASSWORD}"
```
## FlexMeasures Deployment
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: flexmeasures-server
namespace: cariflex
spec:
replicas: 2
selector:
matchLabels:
app: flexmeasures-server
template:
metadata:
labels:
app: flexmeasures-server
spec:
containers:
- name: flexmeasures
image: lfenergy/flexmeasures:latest
ports:
- containerPort: 5000
envFrom:
- configMapRef:
name: cariflex-config
- secretRef:
name: cariflex-secrets
resources:
requests:
memory: "1Gi"
cpu: "500m"
limits:
memory: "2Gi"
cpu: "2"
livenessProbe:
httpGet:
path: /api/v3_0/sensors
port: 5000
initialDelaySeconds: 30
periodSeconds: 30
readinessProbe:
httpGet:
path: /api/v3_0/sensors
port: 5000
initialDelaySeconds: 10
periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
name: flexmeasures-service
namespace: cariflex
spec:
selector:
app: flexmeasures-server
ports:
- port: 5000
targetPort: 5000
type: ClusterIP
```
## OpenADR VTN Deployment
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: openadr-vtn
namespace: cariflex
spec:
replicas: 1
selector:
matchLabels:
app: openadr-vtn
template:
metadata:
labels:
app: openadr-vtn
spec:
containers:
- name: vtn
image: flexmeasures-openadr-vtn:latest
ports:
- containerPort: 8080
envFrom:
- configMapRef:
name: cariflex-config
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
---
apiVersion: v1
kind: Service
metadata:
name: openadr-vtn-service
namespace: cariflex
spec:
selector:
app: openadr-vtn
ports:
- port: 8080
targetPort: 8080
type: ClusterIP
```
## OpenADR VEN Deployment
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: openadr-ven
namespace: cariflex
spec:
replicas: 1
selector:
matchLabels:
app: openadr-ven
template:
metadata:
labels:
app: openadr-ven
spec:
containers:
- name: ven
image: flexmeasures-openadr-ven:latest
env:
- name: VTN_URL
value: "http://openadr-vtn-service:8080/OpenADR2/Simple/2.0b"
- name: FM_HOST
value: "https://cariflex.digitribe.fr"
envFrom:
- configMapRef:
name: cariflex-config
- secretRef:
name: cariflex-secrets
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
---
apiVersion: v1
kind: Service
metadata:
name: openadr-ven-service
namespace: cariflex
spec:
selector:
app: openadr-ven
ports:
- port: 8080
type: ClusterIP
```
## CitrineOS Deployment
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: citrineos-server
namespace: cariflex
spec:
replicas: 1
selector:
matchLabels:
app: citrineos-server
template:
metadata:
labels:
app: citrineos-server
spec:
containers:
- name: citrineos
image: ghcr.io/citrineos/citrineos-server:latest
ports:
- containerPort: 8080
envFrom:
- configMapRef:
name: cariflex-config
- secretRef:
name: cariflex-secrets
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "1"
---
apiVersion: v1
kind: Service
metadata:
name: citrineos-service
namespace: cariflex
spec:
selector:
app: citrineos-server
ports:
- port: 8080
targetPort: 8080
type: ClusterIP
```
## Ingress
```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: cariflex-ingress
namespace: cariflex
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
traefik.ingress.kubernetes.io/router.entrypoints: websecure
spec:
tls:
- hosts:
- cariflex.digitribe.fr
- grafana.digitribe.fr
- citrineos.digitribe.fr
secretName: cariflex-tls
rules:
- host: cariflex.digitribe.fr
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: flexmeasures-service
port:
number: 5000
- host: grafana.digitribe.fr
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: grafana-service
port:
number: 3000
- host: citrineos.digitribe.fr
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: citrineos-service
port:
number: 8080
```
## Deployment Commands
```bash
# Create namespace
kubectl apply -f k8s/namespace.yaml
# Apply config and secrets
kubectl apply -f k8s/configmap.yaml
kubectl apply -f k8s/secrets.yaml
# Deploy applications
kubectl apply -f k8s/deployments/flexmeasures.yaml
kubectl apply -f k8s/deployments/openadr-vtn.yaml
kubectl apply -f k8s/deployments/openadr-ven.yaml
kubectl apply -f k8s/deployments/citrineos.yaml
# Apply services
kubectl apply -f k8s/services/
# Apply ingress
kubectl apply -f k8s/ingress.yaml
# Verify
kubectl get pods -n cariflex
kubectl get svc -n cariflex
kubectl get ingress -n cariflex
```

142
iac/scripts/deploy.sh Normal file
View File

@@ -0,0 +1,142 @@
#!/bin/bash
# Cariflex EMS - Multi-Environment Deployment Script
# Usage: ./deploy.sh [dev|test|prod] [full|fm|openadr|citrine|grafana]
set -e
ENV="${1:-dev}"
COMPONENT="${2:-full}"
CARIFLEX_HOME="/home/eric/cariflex"
FLEXMEASURES_HOME="/home/eric/flexmeasures"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
log() { echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1"; }
warn() { echo -e "${YELLOW}[$(date +'%Y-%m-%d %H:%M:%S')] WARNING:${NC} $1"; }
error() { echo -e "${RED}[$(date +'%Y-%m-%d %H:%M-%S')] ERROR:${NC} $1"; exit 1; }
# Environment-specific configuration
case "$ENV" in
dev)
FM_DOMAIN="cariflex.dev.local"
GRAFANA_DOMAIN="grafana.dev.local"
CITRINEOS_DOMAIN="citrineos.dev.local"
REPLICAS=1
DEBUG=true
LOG_LEVEL="DEBUG"
;;
test)
FM_DOMAIN="cariflex.test.digitribe.fr"
GRAFANA_DOMAIN="grafana.test.digitribe.fr"
CITRINEOS_DOMAIN="citrineos.test.digitribe.fr"
REPLICAS=1
DEBUG=false
LOG_LEVEL="INFO"
;;
prod)
FM_DOMAIN="cariflex.digitribe.fr"
GRAFANA_DOMAIN="grafana.digitribe.fr"
CITRINEOS_DOMAIN="citrineos.digitribe.fr"
REPLICAS=2
DEBUG=false
LOG_LEVEL="WARNING"
;;
*)
error "Unknown environment: $ENV (use dev, test, or prod)"
;;
esac
log "Deploying Cariflex EMS to $ENV environment"
log "Component: $COMPONENT"
log "FM Domain: $FM_DOMAIN"
log "Replicas: $REPLICAS"
# Function to deploy a docker-compose stack
deploy_stack() {
local compose_file="$1"
local stack_name="$2"
log "Deploying $stack_name from $compose_file..."
# Generate environment-specific override
cat > "/tmp/docker-compose.${ENV}.override.yml" << EOF
version: '3.8'
services:
server:
environment:
- LOGGING_LEVEL=${LOG_LEVEL}
- FLEXMEASURES_ENV=${ENV}
labels:
- "traefik.http.routers.${stack_name}.rule=Host(\`${FM_DOMAIN}\`)"
EOF
# Deploy
docker compose -f "$compose_file" -f "/tmp/docker-compose.${ENV}.override.yml" up -d
log "$stack_name deployed successfully"
}
# Deploy based on component
case "$COMPONENT" in
full)
log "Deploying full stack..."
deploy_stack "$FLEXMEASURES_HOME/docker-compose.yml" "flexmeasures"
deploy_stack "$FLEXMEASURES_HOME/docker-compose.openadr.yml" "openadr"
deploy_stack "$CARIFLEX_HOME/config/docker-compose-citrineos.yml" "citrineos"
;;
fm)
deploy_stack "$FLEXMEASURES_HOME/docker-compose.yml" "flexmeasures"
;;
openadr)
deploy_stack "$FLEXMEASURES_HOME/docker-compose.openadr.yml" "openadr"
;;
citrine)
deploy_stack "$CARIFLEX_HOME/config/docker-compose-citrineos.yml" "citrineos"
;;
*)
error "Unknown component: $COMPONENT (use full, fm, openadr, citrine)"
;;
esac
# Health checks
log "Running health checks..."
check_service() {
local name="$1"
local url="$2"
local max_attempts="${3:-30}"
for i in $(seq 1 $max_attempts); do
if curl -sk "$url" > /dev/null 2>&1; then
log "$name is healthy"
return 0
fi
sleep 2
done
warn "$name health check failed after ${max_attempts} attempts"
return 1
}
check_service "FlexMeasures" "https://${FM_DOMAIN}/api/v3_0/sensors" 30
check_service "OpenADR VTN" "http://openadr-vtn:8080/OpenADR2/Simple/2.0b" 10
check_service "CitrineOS" "http://cariflex-citrineos-server:8080" 10
# Summary
log "Deployment complete!"
log ""
log "Environment: $ENV"
log "URLs:"
log " FlexMeasures: https://${FM_DOMAIN}"
log " Grafana: https://${GRAFANA_DOMAIN}"
log " CitrineOS: https://${CITRINEOS_DOMAIN}"
log ""
log "Containers:"
docker ps --format " {{.Names}}: {{.Status}}" | grep -E "flexmeasures|openadr|citrine|grafana"
# Cleanup
rm -f "/tmp/docker-compose.${ENV}.override.yml"