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
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:
@@ -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
298
scripts/convert_config.py
Executable 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()
|
||||
Reference in New Issue
Block a user