1253 lines
32 KiB
Markdown
1253 lines
32 KiB
Markdown
# Ferrero Automation - Production Cutover Plan
|
|
|
|
## Overview
|
|
|
|
This document outlines the complete cutover process from development/staging to production environment for the Ferrero DAM Content Scaling automation system.
|
|
|
|
**Current State:** Running on staging/dev environment
|
|
**Target State:** Full production deployment with live DAM integration
|
|
|
|
---
|
|
|
|
## Pre-Cutover Checklist
|
|
|
|
### 1. Verify Current System Health
|
|
|
|
**Development Environment:**
|
|
```bash
|
|
# Check all workflows are working
|
|
python scripts/a1_to_a2_download.py
|
|
python scripts/a5_to_a6_download.py
|
|
python scripts/b1_to_b2_download.py
|
|
python scripts/a2_to_a3_upload_polling.py
|
|
python scripts/creativex_scoring_storing.py
|
|
|
|
# Verify database connectivity
|
|
python scripts/test_connection.py
|
|
|
|
# Check recent logs for errors
|
|
grep -i error logs/*.log | tail -50
|
|
```
|
|
|
|
**Expected Results:**
|
|
- ✅ All scripts execute without errors
|
|
- ✅ Database connections successful
|
|
- ✅ Box authentication working
|
|
- ✅ DAM OAuth2 authentication working
|
|
- ✅ Email notifications sending
|
|
|
|
### 2. Backup Current Configuration
|
|
|
|
```bash
|
|
# Backup current .env file
|
|
cp .env .env.backup.$(date +%Y%m%d)
|
|
|
|
# Backup database
|
|
./database/backup.sh --daily
|
|
|
|
# Backup Box-config.json
|
|
cp ../Box-config.json ../Box-config.json.backup.$(date +%Y%m%d)
|
|
|
|
# Backup current cron jobs
|
|
crontab -l > cron_backup_$(date +%Y%m%d).txt
|
|
```
|
|
|
|
### 3. Document Current URLs and IDs
|
|
|
|
Create reference document with current settings:
|
|
```bash
|
|
# Extract current settings
|
|
grep "^DAM_BASE_URL" .env
|
|
grep "^BOX_ROOT_FOLDER" .env
|
|
grep "WEBHOOK" .env
|
|
```
|
|
|
|
---
|
|
|
|
## Production Credentials & Configuration
|
|
|
|
### Required Production Values
|
|
|
|
#### 1. DAM Production Endpoints
|
|
|
|
**Current (Dev/Staging):**
|
|
```bash
|
|
DAM_BASE_URL=https://ppr.dam.ferrero.com/otmmapi
|
|
DAM_AUTH_URL=https://ppr.dam.ferrero.com/otdsws/oauth2/token
|
|
```
|
|
|
|
**Production:**
|
|
```bash
|
|
DAM_BASE_URL=https://dam.ferrero.com/otmmapi # VERIFY actual production URL
|
|
DAM_AUTH_URL=https://dam.ferrero.com/otdsws/oauth2/token # VERIFY
|
|
```
|
|
|
|
**ACTION REQUIRED:**
|
|
- [ ] Confirm production DAM URLs with Ferrero IT team
|
|
- [ ] Obtain production OAuth2 credentials
|
|
- [ ] Test authentication against production endpoint
|
|
|
|
#### 2. DAM OAuth2 Credentials
|
|
|
|
**Current (Dev):**
|
|
```bash
|
|
DAM_CLIENT_ID=otds-OLV
|
|
DAM_CLIENT_SECRET=hs28LZ9ZzQ5I9rlW3P7Wwyw85oOatlC1
|
|
```
|
|
|
|
**Production:**
|
|
```bash
|
|
DAM_CLIENT_ID=<production_client_id>
|
|
DAM_CLIENT_SECRET=<production_client_secret>
|
|
```
|
|
|
|
**ACTION REQUIRED:**
|
|
- [ ] Request production OAuth2 client credentials from Ferrero
|
|
- [ ] Test credentials against production DAM
|
|
- [ ] Verify token refresh works
|
|
|
|
#### 3. Box Folder IDs
|
|
|
|
**Current (Dev/Staging):**
|
|
```bash
|
|
BOX_ROOT_FOLDER_A1_A2=348304357505 # Local master downloads
|
|
BOX_ROOT_FOLDER_A2_A3=348526703108 # Agency upload processing
|
|
BOX_ROOT_FOLDER_B1_B2=349261192115 # Global master downloads
|
|
BOX_ROOT_FOLDER_CREATIVEX=350605024645 # CreativeX PDF scoring
|
|
```
|
|
|
|
**Production:**
|
|
```bash
|
|
BOX_ROOT_FOLDER_A1_A2=<production_folder_id>
|
|
BOX_ROOT_FOLDER_A2_A3=<production_folder_id>
|
|
BOX_ROOT_FOLDER_B1_B2=<production_folder_id>
|
|
BOX_ROOT_FOLDER_CREATIVEX=<production_folder_id>
|
|
```
|
|
|
|
**ACTION REQUIRED:**
|
|
- [ ] Create production Box folders with correct structure
|
|
- [ ] Document folder IDs
|
|
- [ ] Set proper permissions for Box service account
|
|
- [ ] Test upload/download to each folder
|
|
|
|
#### 4. Box Authentication
|
|
|
|
**Current (Dev):**
|
|
```bash
|
|
BOX_CLIENT_ID=l2atwxxq4xna7phcjr2uifm4mbah69qp
|
|
BOX_CLIENT_SECRET=6XcuCQ6akpk9daE0UHaGSv3mSxWaER4l
|
|
BOX_JWT_KEY_ID=n1izyn3l
|
|
BOX_PASSPHRASE=971585f5fd6171428c14a7c8899af5ab
|
|
BOX_ENTERPRISE_ID=43984435
|
|
```
|
|
|
|
**Production:**
|
|
```bash
|
|
BOX_CLIENT_ID=<production_value>
|
|
BOX_CLIENT_SECRET=<production_value>
|
|
BOX_JWT_KEY_ID=<production_value>
|
|
BOX_PASSPHRASE=<production_value>
|
|
BOX_ENTERPRISE_ID=<production_value>
|
|
```
|
|
|
|
**File:** `../Box-config.json` (one level up from Python-Version)
|
|
|
|
**ACTION REQUIRED:**
|
|
- [ ] Request production Box JWT credentials
|
|
- [ ] Generate new Box-config.json for production
|
|
- [ ] Place at correct location (../Box-config.json)
|
|
- [ ] Set permissions: `chmod 600 ../Box-config.json`
|
|
- [ ] Test Box authentication
|
|
|
|
#### 5. Database
|
|
|
|
**Current (Dev):**
|
|
```bash
|
|
DB_HOST=localhost
|
|
DB_PORT=5437
|
|
DB_USER=ferrero_user
|
|
DB_PASSWORD=ferrero_pass_2025
|
|
```
|
|
|
|
**Production Options:**
|
|
|
|
**Option A: Keep local PostgreSQL (recommended for start)**
|
|
```bash
|
|
DB_HOST=localhost
|
|
DB_PORT=5437
|
|
DB_USER=ferrero_user
|
|
DB_PASSWORD=<new_strong_production_password>
|
|
```
|
|
|
|
**Option B: Use managed database service**
|
|
```bash
|
|
DB_HOST=<rds_or_managed_db_hostname>
|
|
DB_PORT=5432
|
|
DB_USER=ferrero_prod_user
|
|
DB_PASSWORD=<production_password>
|
|
```
|
|
|
|
**ACTION REQUIRED:**
|
|
- [ ] Decide: Local PostgreSQL or managed service
|
|
- [ ] Change default password (ferrero_pass_2025)
|
|
- [ ] Update .env with production credentials
|
|
- [ ] Initialize production database with init.sql
|
|
- [ ] Set up automated backups (cron jobs)
|
|
|
|
#### 6. Email Notifications
|
|
|
|
**Current (Dev):**
|
|
```bash
|
|
SMTP_SERVER=smtp.mailgun.org
|
|
SMTP_USER=twist@mail.dev.oliver.solutions
|
|
SENDER_EMAIL=TWIST-UK-SERVER@oliver.agency
|
|
ERROR_EMAIL=daveporter@oliver.agency
|
|
REPORT_EMAILS=daveporter@oliver.agency
|
|
```
|
|
|
|
**Production:**
|
|
```bash
|
|
SMTP_SERVER=smtp.mailgun.org
|
|
SMTP_USER=<production_mailgun_user>
|
|
SENDER_EMAIL=ferrero-automation@ferrero.com # Or oliver.agency
|
|
ERROR_EMAIL=ferrero-it@ferrero.com,daveporter@oliver.agency
|
|
REPORT_EMAILS=ferrero-team@ferrero.com,operations@oliver.agency
|
|
```
|
|
|
|
**ACTION REQUIRED:**
|
|
- [ ] Set up production email domain
|
|
- [ ] Update recipient lists for production team
|
|
- [ ] Test email delivery
|
|
- [ ] Verify spam filters don't block automated emails
|
|
|
|
#### 7. Webhooks
|
|
|
|
**Current (Dev):**
|
|
```bash
|
|
CAMPAIGN_STATUS_WEBHOOK_URL=https://hook.us1.make.celonis.com/3f9ztwl8qnljufo0l65utfv5wvvnt9m5
|
|
```
|
|
|
|
**Production:**
|
|
```bash
|
|
CAMPAIGN_STATUS_WEBHOOK_URL=<production_make_scenario_url>
|
|
```
|
|
|
|
**ACTION REQUIRED:**
|
|
- [ ] Create production Make.com scenario
|
|
- [ ] Update webhook URL
|
|
- [ ] Test webhook delivery
|
|
- [ ] Verify webhook signature validation (if enabled)
|
|
|
|
#### 8. CreativeX / LlamaCloud
|
|
|
|
**Current (Dev):**
|
|
```bash
|
|
LLAMA_CLOUD_API_KEY=llx-EDmfh0ZReUbXUbaa5i5275TAP2LznNDqc3skJRL3HY4RUDcf
|
|
CREATIVEX_AGENT_NAME=Creativex-Extract
|
|
```
|
|
|
|
**Production:**
|
|
```bash
|
|
LLAMA_CLOUD_API_KEY=<production_api_key>
|
|
CREATIVEX_AGENT_NAME=Creativex-Extract # Same agent or production-specific?
|
|
```
|
|
|
|
**ACTION REQUIRED:**
|
|
- [ ] Confirm same LlamaCloud API key for production or get new one
|
|
- [ ] Verify agent "Creativex-Extract" exists and works in production
|
|
- [ ] Test extraction with production data
|
|
|
|
#### 9. mTLS Certificate (Optional)
|
|
|
|
**Current (Dev):**
|
|
```bash
|
|
DAM_MTLS_BASE_URL=https://dev-auth.app-api.ferrero.com/00003/mm
|
|
DAM_MTLS_CERT_PATH=config/certificates/dam-mtls-dev.pfx
|
|
DAM_MTLS_CERT_PASSWORD=fnJ8xrnh!54NE&2HR62=2P3YEy+hy9RajZ7v5&=y
|
|
```
|
|
|
|
**Production:**
|
|
```bash
|
|
DAM_MTLS_BASE_URL=https://prod-auth.app-api.ferrero.com/00003/mm # VERIFY
|
|
DAM_MTLS_CERT_PATH=config/certificates/dam-mtls-prod.pfx
|
|
DAM_MTLS_CERT_PASSWORD=<production_cert_password>
|
|
```
|
|
|
|
**ACTION REQUIRED:**
|
|
- [ ] Obtain production mTLS certificate
|
|
- [ ] Place in config/certificates/
|
|
- [ ] Set permissions: `chmod 600 config/certificates/*.pfx`
|
|
- [ ] Whitelist production server IP
|
|
- [ ] Test mTLS authentication: `python scripts/test_connection.py --auth-pfx`
|
|
|
|
---
|
|
|
|
## Cutover Steps
|
|
|
|
### Phase 1: Preparation (1-2 days before)
|
|
|
|
#### Step 1.1: Update Production Server Code
|
|
|
|
```bash
|
|
# SSH to production server
|
|
ssh user@production-server
|
|
|
|
# Navigate to deployment directory
|
|
cd /opt/ferrero-automation/Python-Version
|
|
|
|
# Pull latest code from Bitbucket
|
|
git pull origin main
|
|
|
|
# Verify all new files present
|
|
ls -la scripts/creativex_scoring_storing.py
|
|
ls -la database/backup.sh
|
|
ls -la DATABASE_BACKUP_GUIDE.md
|
|
```
|
|
|
|
#### Step 1.2: Install Dependencies
|
|
|
|
```bash
|
|
# Activate virtual environment
|
|
source venv/bin/activate
|
|
|
|
# Install new dependencies (llama-cloud-services)
|
|
sudo venv/bin/pip install llama-cloud-services
|
|
|
|
# Verify installation
|
|
pip show llama-cloud-services
|
|
|
|
# Or reinstall all dependencies
|
|
sudo venv/bin/pip install -r requirements.txt
|
|
```
|
|
|
|
#### Step 1.3: Database Updates
|
|
|
|
```bash
|
|
# Create creativex_scores table
|
|
PGPASSWORD=ferrero_pass_2025 psql -h localhost -p 5437 -U ferrero_user -d ferrero_tracking -f database/init.sql
|
|
|
|
# Or just add the new table
|
|
PGPASSWORD=ferrero_pass_2025 psql -h localhost -p 5437 -U ferrero_user -d ferrero_tracking -c "
|
|
CREATE TABLE IF NOT EXISTS creativex_scores (
|
|
id SERIAL PRIMARY KEY,
|
|
filename VARCHAR(500) NOT NULL,
|
|
box_file_id VARCHAR(255),
|
|
creativex_id VARCHAR(255),
|
|
creativex_url TEXT,
|
|
quality_score VARCHAR(50),
|
|
full_extraction_data JSONB,
|
|
extracted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
status VARCHAR(50) DEFAULT 'active',
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_creativex_filename ON creativex_scores(filename);
|
|
CREATE INDEX IF NOT EXISTS idx_creativex_box_file ON creativex_scores(box_file_id);
|
|
CREATE INDEX IF NOT EXISTS idx_creativex_status ON creativex_scores(status);
|
|
"
|
|
|
|
# Verify table created
|
|
PGPASSWORD=ferrero_pass_2025 psql -h localhost -p 5437 -U ferrero_user -d ferrero_tracking -c "\d creativex_scores"
|
|
```
|
|
|
|
#### Step 1.4: Update .env with Production Credentials
|
|
|
|
```bash
|
|
# Backup current .env
|
|
cp .env .env.staging.backup
|
|
|
|
# Edit .env with production values
|
|
nano .env
|
|
```
|
|
|
|
**Update these sections:**
|
|
|
|
```bash
|
|
# DAM Production URLs
|
|
DAM_BASE_URL=<production_dam_url>
|
|
DAM_AUTH_URL=<production_auth_url>
|
|
DAM_CLIENT_ID=<production_client_id>
|
|
DAM_CLIENT_SECRET=<production_client_secret>
|
|
|
|
# Box Production Folder IDs
|
|
BOX_ROOT_FOLDER_A1_A2=<production_folder>
|
|
BOX_ROOT_FOLDER_A2_A3=<production_folder>
|
|
BOX_ROOT_FOLDER_B1_B2=<production_folder>
|
|
BOX_ROOT_FOLDER_CREATIVEX=<production_folder>
|
|
|
|
# Database Production Password
|
|
DB_PASSWORD=<new_strong_password>
|
|
|
|
# Email Production Recipients
|
|
ERROR_EMAIL=ferrero-it@ferrero.com,operations@oliver.agency
|
|
REPORT_EMAILS=ferrero-team@ferrero.com,operations@oliver.agency
|
|
|
|
# Webhooks Production URL
|
|
CAMPAIGN_STATUS_WEBHOOK_URL=<production_webhook>
|
|
|
|
# CreativeX Production
|
|
LLAMA_CLOUD_API_KEY=<production_key>
|
|
```
|
|
|
|
Save and exit.
|
|
|
|
#### Step 1.5: Update Box-config.json
|
|
|
|
```bash
|
|
# Backup current Box config
|
|
cp ../Box-config.json ../Box-config.json.staging.backup
|
|
|
|
# Replace with production Box JWT config
|
|
nano ../Box-config.json
|
|
```
|
|
|
|
Paste production Box JWT credentials (JSON format from Box Developer Console).
|
|
|
|
```bash
|
|
# Set secure permissions
|
|
chmod 600 ../Box-config.json
|
|
```
|
|
|
|
#### Step 1.6: Add mTLS Production Certificate (If Using)
|
|
|
|
```bash
|
|
# Copy production certificate
|
|
cp /path/to/dam-mtls-prod.pfx config/certificates/
|
|
|
|
# Set permissions
|
|
chmod 600 config/certificates/dam-mtls-prod.pfx
|
|
|
|
# Update .env
|
|
nano .env
|
|
# Update: DAM_MTLS_CERT_PATH=config/certificates/dam-mtls-prod.pfx
|
|
# Update: DAM_MTLS_CERT_PASSWORD=<production_password>
|
|
```
|
|
|
|
### Phase 2: Testing (Before Cutover)
|
|
|
|
#### Step 2.1: Test Database Connection
|
|
|
|
```bash
|
|
# Test PostgreSQL
|
|
PGPASSWORD=<new_password> psql -h localhost -p 5437 -U ferrero_user -d ferrero_tracking -c "SELECT COUNT(*) FROM master_assets;"
|
|
|
|
# Test via Python
|
|
python scripts/test_connection.py
|
|
```
|
|
|
|
**Expected:** Connection successful, tables accessible
|
|
|
|
#### Step 2.2: Test DAM Authentication
|
|
|
|
```bash
|
|
# Test OAuth2 (production credentials)
|
|
python scripts/test_connection.py
|
|
|
|
# Test mTLS (if using)
|
|
python scripts/test_connection.py --auth-pfx
|
|
```
|
|
|
|
**Expected:** DAM connection successful, can retrieve campaigns
|
|
|
|
#### Step 2.3: Test Box Authentication
|
|
|
|
```bash
|
|
# Run Box connection test
|
|
python -c "
|
|
from shared.config_loader import load_config
|
|
from shared.box_client import BoxClient
|
|
config = load_config('config/config.yaml')
|
|
box = BoxClient(config, root_folder_id=config['box']['root_folder_a1_a2'])
|
|
print('Box test:', box.test_connection())
|
|
"
|
|
```
|
|
|
|
**Expected:** Box connection OK
|
|
|
|
#### Step 2.4: Test Email Notifications
|
|
|
|
```bash
|
|
# Send test email
|
|
python -c "
|
|
from shared.config_loader import load_config
|
|
from shared.notifier import Notifier
|
|
config = load_config('config/config.yaml')
|
|
notifier = Notifier(config)
|
|
notifier.send_email(
|
|
template_name='a1_to_a2_no_assets',
|
|
recipients=config['notifications']['recipients']['success'],
|
|
data={
|
|
'campaign_name': 'TEST CAMPAIGN',
|
|
'campaign_id': 'TEST123',
|
|
'campaign_number': 'C000000999'
|
|
}
|
|
)
|
|
print('Test email sent')
|
|
"
|
|
```
|
|
|
|
**Expected:** Email received by production recipients
|
|
|
|
#### Step 2.5: Test Webhook
|
|
|
|
```bash
|
|
# Test webhook delivery
|
|
python -c "
|
|
from shared.config_loader import load_config
|
|
from shared.notifier import Notifier
|
|
config = load_config('config/config.yaml')
|
|
notifier = Notifier(config)
|
|
result = notifier.send_webhook(
|
|
url=config['webhooks']['campaign_status_update']['url'],
|
|
payload={'test': 'cutover_test', 'campaign': 'TEST'}
|
|
)
|
|
print('Webhook test:', result)
|
|
"
|
|
```
|
|
|
|
**Expected:** Webhook received in production Make.com scenario
|
|
|
|
#### Step 2.6: Test CreativeX Extraction
|
|
|
|
```bash
|
|
# Upload test PDF to production CreativeX folder
|
|
# Then run extraction
|
|
python scripts/creativex_scoring_storing.py
|
|
|
|
# Verify in database
|
|
PGPASSWORD=<new_password> psql -h localhost -p 5437 -U ferrero_user -d ferrero_tracking -c "
|
|
SELECT filename, quality_score FROM creativex_scores WHERE status = 'active' LIMIT 5;
|
|
"
|
|
```
|
|
|
|
**Expected:** Scores extracted and stored successfully
|
|
|
|
#### Step 2.7: Dry Run Workflows (Production Data, No Status Updates)
|
|
|
|
**Important:** Test with real production campaigns but DON'T update statuses yet
|
|
|
|
```bash
|
|
# Test A1→A2 download (will process but not update status without all assets)
|
|
# Manually select a small test campaign first
|
|
python scripts/a1_to_a2_download.py
|
|
|
|
# Check logs
|
|
tail -100 logs/a1_to_a2.log
|
|
|
|
# Verify files in Box production folder
|
|
# Verify records in database
|
|
|
|
# Test A2→A3 upload with test file
|
|
# Upload one file with tracking ID to A2→A3 folder
|
|
python scripts/a2_to_a3_upload_polling.py
|
|
|
|
# Verify upload to DAM
|
|
# Check email notification
|
|
```
|
|
|
|
**CRITICAL:** During dry run testing:
|
|
- ✅ Use real production endpoints
|
|
- ✅ Use real production Box folders
|
|
- ⚠️ Process SMALL test campaigns only
|
|
- ⚠️ Verify you're comfortable with status updates before going live
|
|
- ⚠️ Have rollback plan ready
|
|
|
|
### Phase 3: Go-Live Decision Point
|
|
|
|
#### Go/No-Go Criteria
|
|
|
|
✅ **GO if all true:**
|
|
- [ ] All connection tests passed
|
|
- [ ] Email notifications working
|
|
- [ ] Webhooks delivering successfully
|
|
- [ ] Test campaign processed without errors
|
|
- [ ] Box uploads/downloads working
|
|
- [ ] DAM uploads working with correct metadata
|
|
- [ ] CreativeX scores extracting and integrating
|
|
- [ ] Database backups configured and tested
|
|
- [ ] Team trained on monitoring/support
|
|
- [ ] Rollback plan documented
|
|
|
|
⛔ **NO-GO if any true:**
|
|
- [ ] Authentication failing
|
|
- [ ] Production credentials incomplete
|
|
- [ ] Email delivery issues
|
|
- [ ] Webhook not reaching Make.com
|
|
- [ ] Test uploads corrupted or failed
|
|
- [ ] Metadata mapping incorrect
|
|
- [ ] No backup system in place
|
|
|
|
---
|
|
|
|
## Go-Live Execution
|
|
|
|
### Phase 4: Cutover Day
|
|
|
|
#### Step 4.1: Final Preparation (Morning)
|
|
|
|
```bash
|
|
# 1. Stop all cron jobs temporarily
|
|
crontab -e
|
|
# Comment out all Ferrero automation lines (add # at start)
|
|
|
|
# 2. Create final backup of staging data
|
|
./database/backup.sh --daily
|
|
|
|
# 3. Verify no workflows are running
|
|
ps aux | grep python | grep scripts
|
|
|
|
# 4. Clear any pending Box files
|
|
# Manually verify Box folders are empty or in known state
|
|
```
|
|
|
|
#### Step 4.2: Update Environment Variable
|
|
|
|
```bash
|
|
# Update environment setting
|
|
nano .env
|
|
|
|
# Change:
|
|
ENV=staging
|
|
# To:
|
|
ENV=production
|
|
|
|
# Save and exit
|
|
```
|
|
|
|
#### Step 4.3: Enable Production Cron Jobs
|
|
|
|
```bash
|
|
crontab -e
|
|
```
|
|
|
|
**Production Cron Schedule:**
|
|
```cron
|
|
# A1→A2: Download master assets from DAM (every 5 minutes)
|
|
*/5 * * * * cd /opt/ferrero-automation/Python-Version && venv/bin/python scripts/a1_to_a2_download.py >> logs/cron_a1_a2.log 2>&1
|
|
|
|
# A5→A6: Download rejected assets for rework (every 5 minutes)
|
|
*/5 * * * * cd /opt/ferrero-automation/Python-Version && venv/bin/python scripts/a5_to_a6_download.py >> logs/cron_a5_a6.log 2>&1
|
|
|
|
# B1→B2: Download global master campaigns (every 5 minutes)
|
|
*/5 * * * * cd /opt/ferrero-automation/Python-Version && venv/bin/python scripts/b1_to_b2_download.py >> logs/cron_b1_b2.log 2>&1
|
|
|
|
# A2→A3: Process agency uploads from Box (every 5 minutes)
|
|
*/5 * * * * cd /opt/ferrero-automation/Python-Version && venv/bin/python scripts/a2_to_a3_upload_polling.py >> logs/cron_a2_a3.log 2>&1
|
|
|
|
# Daily Report: Send summary email (7:00 PM daily)
|
|
0 19 * * * cd /opt/ferrero-automation/Python-Version && venv/bin/python scripts/daily_report.py >> logs/daily_report.log 2>&1
|
|
|
|
# Database Backup: Daily at 2:00 AM
|
|
0 2 * * * cd /opt/ferrero-automation/Python-Version && ./database/backup.sh --daily >> logs/backup.log 2>&1
|
|
|
|
# Database Backup: Weekly on Sundays at 3:00 AM
|
|
0 3 * * 0 cd /opt/ferrero-automation/Python-Version && ./database/backup.sh --weekly >> logs/backup.log 2>&1
|
|
|
|
# Backup Health Check: Daily at 8:00 AM
|
|
0 8 * * * cd /opt/ferrero-automation/Python-Version && ./database/check_backups.sh --quiet >> logs/backup_check.log 2>&1
|
|
```
|
|
|
|
Save and exit.
|
|
|
|
#### Step 4.4: Monitor Initial Runs
|
|
|
|
```bash
|
|
# Watch logs in real-time
|
|
tail -f logs/cron_a1_a2.log
|
|
|
|
# In another terminal, watch all workflow logs
|
|
tail -f logs/*.log
|
|
|
|
# Monitor for 1-2 hours
|
|
# Look for:
|
|
# - ✅ "No A1 campaigns found" (normal if no campaigns ready)
|
|
# - ✅ Successful processing messages
|
|
# - ⚠️ Any errors or authentication failures
|
|
```
|
|
|
|
#### Step 4.5: Verify First Campaign Processing
|
|
|
|
**When first A1 campaign appears:**
|
|
|
|
```bash
|
|
# Monitor A1→A2 processing
|
|
tail -f logs/a1_to_a2.log
|
|
|
|
# Expected flow:
|
|
# - Campaign detected
|
|
# - Assets downloaded from DAM
|
|
# - Uploaded to Box with tracking IDs
|
|
# - Stored in database
|
|
# - Status updated A1→A2
|
|
# - Email sent
|
|
|
|
# Verify in database
|
|
PGPASSWORD=<prod_password> psql -h localhost -p 5437 -U ferrero_user -d ferrero_tracking -c "
|
|
SELECT tracking_id, original_filename, created_at FROM master_assets ORDER BY created_at DESC LIMIT 10;
|
|
"
|
|
|
|
# Verify in Box folder
|
|
# Check production Box folder contains files with tracking IDs
|
|
```
|
|
|
|
---
|
|
|
|
## Post-Cutover Monitoring
|
|
|
|
### First 24 Hours
|
|
|
|
#### Hourly Checks:
|
|
|
|
```bash
|
|
# Check cron execution
|
|
tail -100 logs/cron_*.log
|
|
|
|
# Check for errors
|
|
grep -i error logs/*.log | grep "$(date +%Y-%m-%d)"
|
|
|
|
# Check database growth
|
|
PGPASSWORD=<prod_password> psql -h localhost -p 5437 -U ferrero_user -d ferrero_tracking -c "
|
|
SELECT
|
|
(SELECT COUNT(*) FROM master_assets) as masters,
|
|
(SELECT COUNT(*) FROM derivative_assets) as derivatives,
|
|
(SELECT COUNT(*) FROM creativex_scores) as creativex,
|
|
(SELECT COUNT(*) FROM campaign_status) as campaigns;
|
|
"
|
|
|
|
# Check Box folders
|
|
# Verify files are being created/deleted as expected
|
|
```
|
|
|
|
#### Email Monitoring:
|
|
|
|
- ✅ Success emails arriving
|
|
- ⚠️ No error emails (or expected errors only)
|
|
- 📊 Daily report at 7 PM contains production data
|
|
|
|
#### Workflow Health:
|
|
|
|
```bash
|
|
# Check all workflows completed recent runs
|
|
ls -lt logs/*.log | head -10
|
|
|
|
# Verify campaigns progressing through stages
|
|
PGPASSWORD=<prod_password> psql -h localhost -p 5437 -U ferrero_user -d ferrero_tracking -c "
|
|
SELECT campaign_number, status, webhook_sent, updated_at
|
|
FROM campaign_status
|
|
ORDER BY updated_at DESC
|
|
LIMIT 10;
|
|
"
|
|
```
|
|
|
|
### First Week
|
|
|
|
#### Daily Checks:
|
|
|
|
1. **Review daily report email** (7 PM)
|
|
- Verify statistics are accurate
|
|
- Check success rates
|
|
- Review any errors
|
|
|
|
2. **Check backup health**
|
|
```bash
|
|
./database/check_backups.sh
|
|
```
|
|
|
|
3. **Monitor disk space**
|
|
```bash
|
|
df -h /opt/ferrero-automation
|
|
du -sh Python-Version/backups/
|
|
du -sh Python-Version/logs/
|
|
du -sh Python-Version/temp/
|
|
```
|
|
|
|
4. **Check cron logs**
|
|
```bash
|
|
# Verify cron is executing
|
|
grep CRON /var/log/syslog | grep ferrero | tail -20
|
|
```
|
|
|
|
#### Weekly Tasks:
|
|
|
|
- [ ] Review workflow success rates
|
|
- [ ] Check for any recurring errors
|
|
- [ ] Verify webhook delivery to Make.com
|
|
- [ ] Test restore from backup (monthly, but good to test early)
|
|
- [ ] Review with Ferrero team
|
|
|
|
---
|
|
|
|
## Rollback Plan
|
|
|
|
### If Critical Issues Found
|
|
|
|
#### Quick Rollback (Revert to Staging)
|
|
|
|
```bash
|
|
# 1. Stop production cron jobs
|
|
crontab -e
|
|
# Comment out all lines
|
|
|
|
# 2. Restore staging .env
|
|
cp .env.staging.backup .env
|
|
|
|
# 3. Restore staging Box-config.json
|
|
cp ../Box-config.json.staging.backup ../Box-config.json
|
|
|
|
# 4. Update environment
|
|
nano .env
|
|
# Change: ENV=production → ENV=staging
|
|
|
|
# 5. Restart staging cron jobs
|
|
crontab -e
|
|
# Restore original cron jobs
|
|
```
|
|
|
|
#### Database Rollback
|
|
|
|
```bash
|
|
# If database needs rollback
|
|
./database/restore.sh backups/dumps/<pre_cutover_backup>.sql.gz
|
|
```
|
|
|
|
#### Code Rollback
|
|
|
|
```bash
|
|
# If code changes need rollback
|
|
git log --oneline | head -10 # Find commit before cutover
|
|
git revert <commit_hash>
|
|
git push origin main
|
|
```
|
|
|
|
---
|
|
|
|
## Known Production Differences
|
|
|
|
### Development vs Production
|
|
|
|
| Component | Development | Production | Notes |
|
|
|-----------|-------------|------------|-------|
|
|
| **DAM URL** | ppr.dam.ferrero.com | dam.ferrero.com (TBD) | Confirm with Ferrero IT |
|
|
| **Box Folders** | Dev folder IDs | Prod folder IDs | Create new folders |
|
|
| **OAuth2 Creds** | Dev credentials | Prod credentials | Request from Ferrero |
|
|
| **Email Recipients** | daveporter@oliver.agency | Ferrero team + ops | Update recipient lists |
|
|
| **Webhook URL** | Dev Make scenario | Prod Make scenario | Create new scenario |
|
|
| **DB Password** | ferrero_pass_2025 | Strong production password | Change for security |
|
|
| **mTLS Cert** | dev-mtls-dev.pfx | dam-mtls-prod.pfx | Get prod certificate |
|
|
| **Server IP** | Dev IP | Prod IP | Whitelist for mTLS |
|
|
|
|
---
|
|
|
|
## Cutover Communication Plan
|
|
|
|
### Stakeholders to Notify
|
|
|
|
**Before Cutover (48 hours):**
|
|
- [ ] Ferrero DAM team
|
|
- [ ] Ferrero IT team
|
|
- [ ] Oliver Agency operations
|
|
- [ ] Content team users
|
|
|
|
**Email Template:**
|
|
```
|
|
Subject: Ferrero DAM Automation - Production Cutover [Date]
|
|
|
|
The automated content scaling system will be going live on [DATE] at [TIME].
|
|
|
|
What's changing:
|
|
- Automation moves from staging to production DAM environment
|
|
- Campaigns will automatically progress through workflow stages
|
|
- Email notifications will come from production system
|
|
|
|
What to expect:
|
|
- A1→A2: Master assets auto-downloaded to Box
|
|
- A2→A3: Localized assets auto-uploaded to DAM
|
|
- A5→A6: Rejected assets flagged for rework
|
|
- CreativeX scores automatically attached to assets
|
|
|
|
Support during cutover:
|
|
- [Contact person]: [Email/Phone]
|
|
- Monitoring: First 24 hours continuous
|
|
- Rollback available if issues found
|
|
|
|
Please report any issues immediately to [ERROR_EMAIL]
|
|
```
|
|
|
|
**During Cutover:**
|
|
- [ ] Status updates every 2 hours
|
|
- [ ] Available for immediate response
|
|
|
|
**After Cutover (24 hours):**
|
|
- [ ] Summary email with statistics
|
|
- [ ] Known issues (if any)
|
|
- [ ] Next steps
|
|
|
|
---
|
|
|
|
## Post-Cutover Validation
|
|
|
|
### Validation Checklist (First 24 Hours)
|
|
|
|
#### Workflow Validations:
|
|
|
|
**A1→A2 (Master Download):**
|
|
- [ ] Campaign detected with status A1
|
|
- [ ] All assets downloaded from DAM
|
|
- [ ] Uploaded to Box with tracking IDs
|
|
- [ ] Stored in database with full metadata
|
|
- [ ] Status updated to A2
|
|
- [ ] Success email sent
|
|
- [ ] Tracking IDs are unique
|
|
- [ ] Subfolder structure preserved
|
|
|
|
**A5→A6 (Rejections/Rework):**
|
|
- [ ] Only "NOT APPROVED" assets downloaded
|
|
- [ ] Rejection comments extracted
|
|
- [ ] Tracking IDs reused correctly
|
|
- [ ] Email lists rejection reasons
|
|
- [ ] Approved assets skipped
|
|
|
|
**B1→B2 (Global Masters):**
|
|
- [ ] Only "Global comm" campaigns processed
|
|
- [ ] Uploaded to separate Box folder
|
|
- [ ] MASTERS_ prefix in folder names
|
|
- [ ] Email sent (no webhook)
|
|
|
|
**A2→A3 (Agency Uploads):**
|
|
- [ ] Files detected in Box A2→A3 folder
|
|
- [ ] Tracking IDs parsed from filenames
|
|
- [ ] Master metadata loaded from database
|
|
- [ ] CreativeX scores looked up from database
|
|
- [ ] Default values used when scores missing
|
|
- [ ] Uploaded to correct DAM folder
|
|
- [ ] Files deleted from Box after success
|
|
- [ ] Email shows CreativeX status
|
|
|
|
**CreativeX Scoring:**
|
|
- [ ] PDFs processed from Box folder 350605024645
|
|
- [ ] Scores extracted via LlamaExtract
|
|
- [ ] Stored in database with version tracking
|
|
- [ ] Decimals removed from IDs and scores
|
|
- [ ] PDFs deleted after successful extraction
|
|
- [ ] Email sent with version badges
|
|
|
|
#### Database Validations:
|
|
|
|
```bash
|
|
# Check all tables have data
|
|
PGPASSWORD=<prod_password> psql -h localhost -p 5437 -U ferrero_user -d ferrero_tracking -c "
|
|
SELECT
|
|
'master_assets' as table_name, COUNT(*) as records FROM master_assets
|
|
UNION ALL SELECT
|
|
'derivative_assets', COUNT(*) FROM derivative_assets
|
|
UNION ALL SELECT
|
|
'creativex_scores', COUNT(*) FROM creativex_scores
|
|
UNION ALL SELECT
|
|
'campaign_status', COUNT(*) FROM campaign_status
|
|
UNION ALL SELECT
|
|
'asset_events', COUNT(*) FROM asset_events;
|
|
"
|
|
|
|
# Check for any errors in logs
|
|
grep -i error logs/*.log | grep "$(date +%Y-%m-%d)"
|
|
```
|
|
|
|
#### Backup Validations:
|
|
|
|
```bash
|
|
# Verify first production backup
|
|
./database/check_backups.sh
|
|
|
|
# Expected:
|
|
# - Backup created at 2:00 AM
|
|
# - Backup file size reasonable
|
|
# - No errors in backup.log
|
|
```
|
|
|
|
---
|
|
|
|
## Production Support Procedures
|
|
|
|
### Monitoring Dashboard Commands
|
|
|
|
**Create monitoring script:** `scripts/production_status.sh`
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
echo "========================================="
|
|
echo "Ferrero Automation Production Status"
|
|
echo "========================================="
|
|
echo ""
|
|
|
|
# Database stats
|
|
echo "Database Records:"
|
|
PGPASSWORD=<prod_password> psql -h localhost -p 5437 -U ferrero_user -d ferrero_tracking -c "
|
|
SELECT
|
|
(SELECT COUNT(*) FROM master_assets WHERE status = 'active') as active_masters,
|
|
(SELECT COUNT(*) FROM creativex_scores WHERE status = 'active') as active_scores,
|
|
(SELECT COUNT(*) FROM campaign_status WHERE webhook_sent = true) as completed_campaigns;
|
|
"
|
|
|
|
echo ""
|
|
echo "Recent Activity (Last 24 hours):"
|
|
PGPASSWORD=<prod_password> psql -h localhost -p 5437 -U ferrero_user -d ferrero_tracking -c "
|
|
SELECT COUNT(*) as new_assets_today
|
|
FROM master_assets
|
|
WHERE created_at > NOW() - INTERVAL '24 hours';
|
|
"
|
|
|
|
echo ""
|
|
echo "Backup Status:"
|
|
./database/check_backups.sh --quiet
|
|
|
|
echo ""
|
|
echo "Recent Errors:"
|
|
grep -i error logs/*.log | grep "$(date +%Y-%m-%d)" | tail -5
|
|
```
|
|
|
|
### Emergency Contacts
|
|
|
|
**Escalation Path:**
|
|
1. **Level 1:** Operations team (monitors daily)
|
|
2. **Level 2:** Technical lead (Dave Porter)
|
|
3. **Level 3:** Ferrero IT team
|
|
|
|
**Contact Information:**
|
|
- Operations: operations@oliver.agency
|
|
- Technical Lead: daveporter@oliver.agency
|
|
- Ferrero IT: [TO BE PROVIDED]
|
|
|
|
---
|
|
|
|
## Configuration Checklist Summary
|
|
|
|
### Files to Update:
|
|
|
|
- [ ] `Python-Version/.env` - All production credentials
|
|
- [ ] `../Box-config.json` - Production Box JWT
|
|
- [ ] `config/certificates/dam-mtls-prod.pfx` - Production mTLS cert (if using)
|
|
- [ ] Crontab - Production schedule
|
|
- [ ] `/etc/hosts` - Production hostname if needed
|
|
|
|
### Credentials to Obtain:
|
|
|
|
- [ ] Production DAM OAuth2 client ID and secret
|
|
- [ ] Production DAM base URL and auth URL
|
|
- [ ] Production Box folder IDs (4 folders)
|
|
- [ ] Production Box JWT credentials
|
|
- [ ] Production mTLS certificate (if using)
|
|
- [ ] Production webhook URL (Make.com)
|
|
- [ ] Production email recipients list
|
|
- [ ] Production LlamaCloud API key (or confirm dev key works)
|
|
|
|
### Testing to Complete:
|
|
|
|
- [ ] DAM connection test
|
|
- [ ] Box connection test
|
|
- [ ] Database connection test
|
|
- [ ] Email delivery test
|
|
- [ ] Webhook delivery test
|
|
- [ ] CreativeX extraction test
|
|
- [ ] End-to-end workflow test with small campaign
|
|
- [ ] Backup and restore test
|
|
|
|
---
|
|
|
|
## Success Metrics
|
|
|
|
### Week 1 Targets:
|
|
|
|
- **Uptime:** > 99% (workflows running without crashes)
|
|
- **Success Rate:** > 95% (assets processed successfully)
|
|
- **Email Delivery:** 100% (all notifications received)
|
|
- **Backup Success:** 7/7 daily backups completed
|
|
- **Response Time:** < 10 minutes (cron cycle)
|
|
|
|
### Monitor These KPIs:
|
|
|
|
```bash
|
|
# Check success rate from daily report emails
|
|
# Or query database:
|
|
PGPASSWORD=<prod_password> psql -h localhost -p 5437 -U ferrero_user -d ferrero_tracking -c "
|
|
SELECT
|
|
COUNT(*) FILTER (WHERE webhook_sent = true) as successful_campaigns,
|
|
COUNT(*) as total_campaigns,
|
|
ROUND(100.0 * COUNT(*) FILTER (WHERE webhook_sent = true) / NULLIF(COUNT(*), 0), 1) as success_rate
|
|
FROM campaign_status
|
|
WHERE created_at > NOW() - INTERVAL '7 days';
|
|
"
|
|
```
|
|
|
|
---
|
|
|
|
## Troubleshooting Production Issues
|
|
|
|
### Issue: Workflows Not Running
|
|
|
|
```bash
|
|
# Check cron service
|
|
systemctl status cron
|
|
|
|
# Check cron logs
|
|
grep CRON /var/log/syslog | tail -50
|
|
|
|
# Manually run workflow
|
|
cd /opt/ferrero-automation/Python-Version
|
|
source venv/bin/activate
|
|
python scripts/a1_to_a2_download.py
|
|
```
|
|
|
|
### Issue: Authentication Failing
|
|
|
|
```bash
|
|
# Test OAuth2
|
|
python scripts/test_connection.py
|
|
|
|
# Test mTLS
|
|
python scripts/test_connection.py --auth-pfx
|
|
|
|
# Check credentials in .env
|
|
grep "DAM_" .env | grep -v PASSWORD
|
|
```
|
|
|
|
### Issue: Database Not Accessible
|
|
|
|
```bash
|
|
# Check PostgreSQL container
|
|
docker ps | grep ferrero-tracking-db
|
|
|
|
# Check logs
|
|
docker logs ferrero-tracking-db | tail -50
|
|
|
|
# Test connection
|
|
PGPASSWORD=<prod_password> psql -h localhost -p 5437 -U ferrero_user -d ferrero_tracking -c "SELECT 1;"
|
|
```
|
|
|
|
### Issue: Box Upload/Download Failing
|
|
|
|
```bash
|
|
# Test Box connection
|
|
python -c "
|
|
from shared.config_loader import load_config
|
|
from shared.box_client import BoxClient
|
|
config = load_config('config/config.yaml')
|
|
box = BoxClient(config)
|
|
print(box.test_connection())
|
|
"
|
|
|
|
# Check Box-config.json location
|
|
ls -la ../Box-config.json
|
|
|
|
# Verify Box folder IDs
|
|
grep "BOX_ROOT_FOLDER" .env
|
|
```
|
|
|
|
### Issue: Emails Not Sending
|
|
|
|
```bash
|
|
# Check SMTP settings
|
|
grep "SMTP" .env
|
|
|
|
# Test email manually
|
|
python -c "
|
|
from shared.config_loader import load_config
|
|
from shared.notifier import Notifier
|
|
config = load_config('config/config.yaml')
|
|
notifier = Notifier(config)
|
|
# Send test email...
|
|
"
|
|
|
|
# Check Mailgun logs (if using Mailgun)
|
|
```
|
|
|
|
---
|
|
|
|
## Appendix: Production Readiness Checklist
|
|
|
|
### Infrastructure:
|
|
|
|
- [ ] Production server provisioned and accessible
|
|
- [ ] Docker installed and running
|
|
- [ ] PostgreSQL container running on port 5437
|
|
- [ ] Sufficient disk space (minimum 10 GB)
|
|
- [ ] Network access to DAM, Box, email servers
|
|
- [ ] Firewall rules configured (if using mTLS)
|
|
- [ ] SSL certificates valid (if applicable)
|
|
|
|
### Code Deployment:
|
|
|
|
- [ ] Latest code pulled from Bitbucket
|
|
- [ ] Virtual environment created (Python 3.10+)
|
|
- [ ] All dependencies installed (requirements.txt)
|
|
- [ ] llama-cloud-services installed
|
|
- [ ] Scripts executable (chmod +x)
|
|
- [ ] Database schema initialized (init.sql)
|
|
- [ ] creativex_scores table created
|
|
|
|
### Configuration:
|
|
|
|
- [ ] .env file configured with production values
|
|
- [ ] Box-config.json in correct location (../Box-config.json)
|
|
- [ ] mTLS certificate in place (if using)
|
|
- [ ] All folder IDs updated
|
|
- [ ] Email recipients updated
|
|
- [ ] Webhook URL updated
|
|
|
|
### Testing:
|
|
|
|
- [ ] All connection tests passed
|
|
- [ ] Test campaign processed successfully
|
|
- [ ] Email notifications received
|
|
- [ ] Webhook delivery confirmed
|
|
- [ ] CreativeX extraction working
|
|
- [ ] Backup system tested
|
|
|
|
### Automation:
|
|
|
|
- [ ] Cron jobs configured
|
|
- [ ] Cron service running
|
|
- [ ] Log rotation configured
|
|
- [ ] Backup schedule active
|
|
- [ ] Monitoring scripts in place
|
|
|
|
### Documentation:
|
|
|
|
- [ ] Team trained on system
|
|
- [ ] Support procedures documented
|
|
- [ ] Rollback plan understood
|
|
- [ ] Escalation path defined
|
|
- [ ] Emergency contacts listed
|
|
|
|
### Sign-off:
|
|
|
|
- [ ] Technical lead approval: _________________ Date: _______
|
|
- [ ] Operations approval: _________________ Date: _______
|
|
- [ ] Ferrero stakeholder approval: _________________ Date: _______
|
|
|
|
---
|
|
|
|
## Post-Cutover: Continuous Improvement
|
|
|
|
### Month 1 Review:
|
|
|
|
- Analyze success rates
|
|
- Identify recurring errors
|
|
- Optimize cron schedule if needed
|
|
- Tune email notifications
|
|
- Adjust backup retention if needed
|
|
|
|
### Potential Enhancements:
|
|
|
|
- [ ] Add Grafana/Prometheus monitoring
|
|
- [ ] Implement alerting thresholds
|
|
- [ ] Add Slack notifications
|
|
- [ ] Remote backup to S3/cloud storage
|
|
- [ ] Point-in-time recovery (WAL archiving)
|
|
- [ ] High availability setup (if needed)
|
|
- [ ] Load balancing (if volume increases)
|
|
|
|
---
|
|
|
|
**Document Version:** 1.0
|
|
**Last Updated:** November 11, 2025
|
|
**Owner:** Dave Porter (daveporter@oliver.agency)
|
|
**Review Date:** [After cutover + 1 week]
|