Add Helm chart, Docs, and Config conversion script
Some checks failed
Build / Code Quality Checks (push) Successful in 15m11s
Build / Build & Push Docker Images (worker) (push) Successful in 13m44s
Build / Build & Push Docker Images (frontend) (push) Successful in 5m8s
Build / Build & Push Docker Images (chat) (push) Failing after 30m7s
Build / Build & Push Docker Images (api) (push) Failing after 21m39s

This commit is contained in:
2025-10-22 14:35:21 +02:00
parent ba9900bd57
commit 2719cfff59
31 changed files with 4436 additions and 0 deletions

View File

@@ -148,6 +148,118 @@ After running the validation script, you'll find:
---
## 🔄 convert_config.py
**Configuration Format Converter**
### Description
Converts between `.env` and `values.yaml` configuration formats, making it easy to switch between Docker Compose and Helm deployments.
### Usage
#### Prerequisites
```bash
pip install pyyaml
```
#### Convert .env to values.yaml
```bash
./scripts/convert_config.py env-to-yaml .env values.yaml
```
#### Convert values.yaml to .env
```bash
./scripts/convert_config.py yaml-to-env values.yaml .env
```
### Examples
**Example 1: Create values.yaml from existing .env**
```bash
# You have an existing .env file from Docker development
./scripts/convert_config.py env-to-yaml .env my-values.yaml
# Use the generated values.yaml with Helm
helm install my-release deploy/helm/datacenter-docs -f my-values.yaml
```
**Example 2: Generate .env from values.yaml**
```bash
# You have a values.yaml from Kubernetes deployment
./scripts/convert_config.py yaml-to-env values.yaml .env
# Use the generated .env with Docker Compose
cd deploy/docker
docker-compose -f docker-compose.dev.yml up -d
```
**Example 3: Environment migration**
```bash
# Convert development .env to staging values.yaml
./scripts/convert_config.py env-to-yaml .env.development values-staging.yaml
# Manually adjust staging-specific settings
nano values-staging.yaml
# Deploy to staging Kubernetes cluster
helm install staging deploy/helm/datacenter-docs -f values-staging.yaml
```
### Supported Configuration
The script converts:
- **MongoDB**: Connection settings and authentication
- **Redis**: Connection and authentication
- **MCP Server**: URL and API key
- **Proxmox**: Host, authentication, SSL settings
- **LLM**: Provider settings (OpenAI, Anthropic, Ollama, etc.)
- **API**: Server configuration and workers
- **CORS**: Allowed origins
- **Application**: Logging and debug settings
- **Celery**: Broker and result backend
- **Vector Store**: ChromaDB and embedding model
### Output
```
Reading .env file: .env
Converting to values.yaml format...
Writing values.yaml: my-values.yaml
✓ Conversion completed successfully!
Output written to: my-values.yaml
```
### Limitations
- Converts common configuration options only
- Complex nested structures may require manual adjustment
- Helm-specific values (resource limits, replicas) not included in .env conversion
- Always review and test converted configuration
### Tips
1. **Review output**: Always check converted files for accuracy
2. **Test first**: Validate in development before production
3. **Keep secrets secure**: Use proper secret management tools
4. **Version control**: Track configuration changes
### See Also
- [CONFIGURATION.md](../CONFIGURATION.md) - Complete configuration guide
- [.env.example](../.env.example) - Environment variable template
- [values.yaml](../values.yaml) - YAML configuration template
---
## 🚀 Quick Start
```bash

298
scripts/convert_config.py Executable file
View File

@@ -0,0 +1,298 @@
#!/usr/bin/env python3
"""
Configuration Converter
Converts between .env and values.yaml formats
"""
import os
import sys
import argparse
from pathlib import Path
from typing import Dict, Any
import yaml
def parse_env_file(env_file: Path) -> Dict[str, str]:
"""Parse .env file and return dictionary of variables."""
env_vars = {}
with open(env_file, 'r') as f:
for line in f:
line = line.strip()
# Skip comments and empty lines
if not line or line.startswith('#'):
continue
# Parse KEY=VALUE
if '=' in line:
key, value = line.split('=', 1)
env_vars[key.strip()] = value.strip()
return env_vars
def env_to_values(env_vars: Dict[str, str]) -> Dict[str, Any]:
"""Convert environment variables to values.yaml structure."""
values = {
'mongodb': {
'auth': {
'rootUsername': env_vars.get('MONGO_ROOT_USER', 'admin'),
'rootPassword': env_vars.get('MONGO_ROOT_PASSWORD', 'changeme'),
'database': env_vars.get('MONGODB_DATABASE', 'datacenter_docs'),
},
'url': env_vars.get('MONGODB_URL', 'mongodb://admin:changeme@mongodb:27017'),
},
'redis': {
'auth': {
'password': env_vars.get('REDIS_PASSWORD', 'changeme'),
},
'url': env_vars.get('REDIS_URL', 'redis://redis:6379/0'),
},
'mcp': {
'server': {
'url': env_vars.get('MCP_SERVER_URL', 'https://mcp.company.local'),
'apiKey': env_vars.get('MCP_API_KEY', 'your_mcp_api_key_here'),
},
},
'proxmox': {
'host': env_vars.get('PROXMOX_HOST', 'proxmox.example.com'),
'port': int(env_vars.get('PROXMOX_PORT', '8006')),
'auth': {
'user': env_vars.get('PROXMOX_USER', 'root@pam'),
'password': env_vars.get('PROXMOX_PASSWORD', 'your-password-here'),
},
'ssl': {
'verify': env_vars.get('PROXMOX_VERIFY_SSL', 'false').lower() == 'true',
},
'timeout': int(env_vars.get('PROXMOX_TIMEOUT', '30')),
},
'llm': {
'baseUrl': env_vars.get('LLM_BASE_URL', 'https://api.openai.com/v1'),
'apiKey': env_vars.get('LLM_API_KEY', 'sk-your-openai-api-key-here'),
'model': env_vars.get('LLM_MODEL', 'gpt-4-turbo-preview'),
'generation': {
'temperature': float(env_vars.get('LLM_TEMPERATURE', '0.3')),
'maxTokens': int(env_vars.get('LLM_MAX_TOKENS', '4096')),
},
},
'api': {
'host': env_vars.get('API_HOST', '0.0.0.0'),
'port': int(env_vars.get('API_PORT', '8000')),
'workers': int(env_vars.get('WORKERS', '4')),
},
'cors': {
'origins': env_vars.get('CORS_ORIGINS', 'http://localhost:3000').split(','),
},
'application': {
'logging': {
'level': env_vars.get('LOG_LEVEL', 'INFO'),
},
'debug': env_vars.get('DEBUG', 'false').lower() == 'true',
},
'celery': {
'broker': {
'url': env_vars.get('CELERY_BROKER_URL', 'redis://redis:6379/0'),
},
'result': {
'backend': env_vars.get('CELERY_RESULT_BACKEND', 'redis://redis:6379/0'),
},
},
'vectorStore': {
'chroma': {
'path': env_vars.get('VECTOR_STORE_PATH', './data/chroma_db'),
},
'embedding': {
'model': env_vars.get('EMBEDDING_MODEL', 'sentence-transformers/all-MiniLM-L6-v2'),
},
},
}
return values
def values_to_env(values: Dict[str, Any]) -> Dict[str, str]:
"""Convert values.yaml structure to environment variables."""
env_vars = {}
# MongoDB
if 'mongodb' in values:
mongo = values['mongodb']
if 'auth' in mongo:
env_vars['MONGO_ROOT_USER'] = mongo['auth'].get('rootUsername', 'admin')
env_vars['MONGO_ROOT_PASSWORD'] = mongo['auth'].get('rootPassword', 'changeme')
env_vars['MONGODB_DATABASE'] = mongo['auth'].get('database', 'datacenter_docs')
env_vars['MONGODB_URL'] = mongo.get('url', 'mongodb://admin:changeme@mongodb:27017')
# Redis
if 'redis' in values:
redis = values['redis']
if 'auth' in redis:
env_vars['REDIS_PASSWORD'] = redis['auth'].get('password', 'changeme')
env_vars['REDIS_URL'] = redis.get('url', 'redis://redis:6379/0')
# MCP
if 'mcp' in values and 'server' in values['mcp']:
mcp = values['mcp']['server']
env_vars['MCP_SERVER_URL'] = mcp.get('url', 'https://mcp.company.local')
env_vars['MCP_API_KEY'] = mcp.get('apiKey', 'your_mcp_api_key_here')
# Proxmox
if 'proxmox' in values:
px = values['proxmox']
env_vars['PROXMOX_HOST'] = px.get('host', 'proxmox.example.com')
env_vars['PROXMOX_PORT'] = str(px.get('port', 8006))
if 'auth' in px:
env_vars['PROXMOX_USER'] = px['auth'].get('user', 'root@pam')
env_vars['PROXMOX_PASSWORD'] = px['auth'].get('password', 'your-password-here')
if 'ssl' in px:
env_vars['PROXMOX_VERIFY_SSL'] = str(px['ssl'].get('verify', False)).lower()
env_vars['PROXMOX_TIMEOUT'] = str(px.get('timeout', 30))
# LLM
if 'llm' in values:
llm = values['llm']
env_vars['LLM_BASE_URL'] = llm.get('baseUrl', 'https://api.openai.com/v1')
env_vars['LLM_API_KEY'] = llm.get('apiKey', 'sk-your-openai-api-key-here')
env_vars['LLM_MODEL'] = llm.get('model', 'gpt-4-turbo-preview')
if 'generation' in llm:
env_vars['LLM_TEMPERATURE'] = str(llm['generation'].get('temperature', 0.3))
env_vars['LLM_MAX_TOKENS'] = str(llm['generation'].get('maxTokens', 4096))
# API
if 'api' in values:
api = values['api']
env_vars['API_HOST'] = api.get('host', '0.0.0.0')
env_vars['API_PORT'] = str(api.get('port', 8000))
env_vars['WORKERS'] = str(api.get('workers', 4))
# CORS
if 'cors' in values:
origins = values['cors'].get('origins', ['http://localhost:3000'])
env_vars['CORS_ORIGINS'] = ','.join(origins)
# Application
if 'application' in values:
app = values['application']
if 'logging' in app:
env_vars['LOG_LEVEL'] = app['logging'].get('level', 'INFO')
env_vars['DEBUG'] = str(app.get('debug', False)).lower()
# Celery
if 'celery' in values:
celery = values['celery']
if 'broker' in celery:
env_vars['CELERY_BROKER_URL'] = celery['broker'].get('url', 'redis://redis:6379/0')
if 'result' in celery:
env_vars['CELERY_RESULT_BACKEND'] = celery['result'].get('backend', 'redis://redis:6379/0')
# Vector Store
if 'vectorStore' in values:
vs = values['vectorStore']
if 'chroma' in vs:
env_vars['VECTOR_STORE_PATH'] = vs['chroma'].get('path', './data/chroma_db')
if 'embedding' in vs:
env_vars['EMBEDDING_MODEL'] = vs['embedding'].get('model', 'sentence-transformers/all-MiniLM-L6-v2')
return env_vars
def write_env_file(env_vars: Dict[str, str], output_file: Path):
"""Write environment variables to .env file."""
with open(output_file, 'w') as f:
f.write("# =============================================================================\n")
f.write("# Datacenter Documentation System - Configuration\n")
f.write("# Generated from values.yaml\n")
f.write("# =============================================================================\n\n")
# Group by section
sections = {
'MongoDB': ['MONGO_ROOT_USER', 'MONGO_ROOT_PASSWORD', 'MONGODB_URL', 'MONGODB_DATABASE'],
'Redis': ['REDIS_PASSWORD', 'REDIS_URL'],
'MCP': ['MCP_SERVER_URL', 'MCP_API_KEY'],
'Proxmox': ['PROXMOX_HOST', 'PROXMOX_PORT', 'PROXMOX_USER', 'PROXMOX_PASSWORD',
'PROXMOX_VERIFY_SSL', 'PROXMOX_TIMEOUT'],
'LLM': ['LLM_BASE_URL', 'LLM_API_KEY', 'LLM_MODEL', 'LLM_TEMPERATURE', 'LLM_MAX_TOKENS'],
'API': ['API_HOST', 'API_PORT', 'WORKERS'],
'CORS': ['CORS_ORIGINS'],
'Application': ['LOG_LEVEL', 'DEBUG'],
'Celery': ['CELERY_BROKER_URL', 'CELERY_RESULT_BACKEND'],
'Vector Store': ['VECTOR_STORE_PATH', 'EMBEDDING_MODEL'],
}
for section, keys in sections.items():
f.write(f"# {section}\n")
for key in keys:
if key in env_vars:
f.write(f"{key}={env_vars[key]}\n")
f.write("\n")
def main():
parser = argparse.ArgumentParser(
description='Convert between .env and values.yaml configuration formats'
)
parser.add_argument(
'mode',
choices=['env-to-yaml', 'yaml-to-env'],
help='Conversion mode'
)
parser.add_argument(
'input',
type=Path,
help='Input file path'
)
parser.add_argument(
'output',
type=Path,
help='Output file path'
)
args = parser.parse_args()
# Check input file exists
if not args.input.exists():
print(f"Error: Input file not found: {args.input}", file=sys.stderr)
sys.exit(1)
try:
if args.mode == 'env-to-yaml':
# Convert .env to values.yaml
print(f"Reading .env file: {args.input}")
env_vars = parse_env_file(args.input)
print("Converting to values.yaml format...")
values = env_to_values(env_vars)
print(f"Writing values.yaml: {args.output}")
with open(args.output, 'w') as f:
yaml.dump(values, f, default_flow_style=False, sort_keys=False, indent=2)
print("✓ Conversion completed successfully!")
else: # yaml-to-env
# Convert values.yaml to .env
print(f"Reading values.yaml file: {args.input}")
with open(args.input, 'r') as f:
values = yaml.safe_load(f)
print("Converting to .env format...")
env_vars = values_to_env(values)
print(f"Writing .env file: {args.output}")
write_env_file(env_vars, args.output)
print("✓ Conversion completed successfully!")
print(f"\nOutput written to: {args.output}")
except Exception as e:
print(f"Error during conversion: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == '__main__':
main()