debian-forge-composer/internal/production/production_manager.go
robojerk 4eeaa43c39
Some checks failed
Tests / 🛃 Unit tests (push) Failing after 13s
Tests / 🗄 DB tests (push) Failing after 19s
Tests / 🐍 Lint python scripts (push) Failing after 1s
Tests / ⌨ Golang Lint (push) Failing after 1s
Tests / 📦 Packit config lint (push) Failing after 1s
Tests / 🔍 Check source preparation (push) Failing after 1s
Tests / 🔍 Check for valid snapshot urls (push) Failing after 1s
Tests / 🔍 Check for missing or unused runner repos (push) Failing after 1s
Tests / 🐚 Shellcheck (push) Failing after 1s
Tests / 📦 RPMlint (push) Failing after 1s
Tests / Gitlab CI trigger helper (push) Failing after 1s
Tests / 🎀 kube-linter (push) Failing after 1s
Tests / 🧹 cloud-cleaner-is-enabled (push) Successful in 3s
Tests / 🔍 Check spec file osbuild/images dependencies (push) Failing after 1s
did stuff
2025-08-26 10:34:42 -07:00

845 lines
27 KiB
Go

package production
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"sync"
"time"
"github.com/sirupsen/logrus"
)
// ProductionManager handles production deployment and readiness
type ProductionManager struct {
logger *logrus.Logger
config *ProductionConfig
performance *PerformanceOptimizer
deployment *DeploymentAutomation
support *ProductionSupport
mu sync.RWMutex
}
// ProductionConfig holds production configuration
type ProductionConfig struct {
Enabled bool `json:"enabled"`
Environment string `json:"environment"`
DeploymentPath string `json:"deployment_path"`
Performance bool `json:"performance"`
Automation bool `json:"automation"`
Support bool `json:"support"`
Metadata map[string]string `json:"metadata"`
}
// PerformanceOptimizer handles performance optimization and testing
type PerformanceOptimizer struct {
config *PerformanceConfig
loadTests map[string]LoadTest
scalability map[string]ScalabilityTest
benchmarks map[string]Benchmark
logger *logrus.Logger
}
// PerformanceConfig holds performance configuration
type PerformanceConfig struct {
Enabled bool `json:"enabled"`
LoadTesting bool `json:"load_testing"`
Scalability bool `json:"scalability"`
Benchmarking bool `json:"benchmarking"`
Thresholds map[string]int `json:"thresholds"`
Metadata map[string]string `json:"metadata"`
}
// LoadTest represents a load testing scenario
type LoadTest struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Type string `json:"type"`
Users int `json:"users"`
Duration time.Duration `json:"duration"`
RampUp time.Duration `json:"ramp_up"`
Script string `json:"script"`
Enabled bool `json:"enabled"`
Metadata map[string]interface{} `json:"metadata"`
}
// ScalabilityTest represents a scalability testing scenario
type ScalabilityTest struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Type string `json:"type"`
StartNodes int `json:"start_nodes"`
EndNodes int `json:"end_nodes"`
StepSize int `json:"step_size"`
Script string `json:"script"`
Enabled bool `json:"enabled"`
Metadata map[string]interface{} `json:"metadata"`
}
// Benchmark represents a performance benchmark
type Benchmark struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Type string `json:"type"`
Metric string `json:"metric"`
Target float64 `json:"target"`
Script string `json:"script"`
Enabled bool `json:"enabled"`
Metadata map[string]interface{} `json:"metadata"`
}
// DeploymentAutomation handles automated deployment
type DeploymentAutomation struct {
config *DeploymentConfig
scripts map[string]DeploymentScript
configs map[string]ConfigManagement
provisioning map[string]EnvironmentProvisioning
logger *logrus.Logger
}
// DeploymentConfig holds deployment configuration
type DeploymentConfig struct {
Enabled bool `json:"enabled"`
Scripts bool `json:"scripts"`
ConfigMgmt bool `json:"config_mgmt"`
Provisioning bool `json:"provisioning"`
Testing bool `json:"testing"`
Metadata map[string]string `json:"metadata"`
}
// DeploymentScript represents a deployment script
type DeploymentScript struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Type string `json:"type"`
ScriptPath string `json:"script_path"`
Parameters map[string]interface{} `json:"parameters"`
Timeout time.Duration `json:"timeout"`
Rollback string `json:"rollback"`
Enabled bool `json:"enabled"`
Metadata map[string]interface{} `json:"metadata"`
}
// ConfigManagement represents configuration management
type ConfigManagement struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Type string `json:"type"`
ConfigPath string `json:"config_path"`
Templates []string `json:"templates"`
Variables map[string]interface{} `json:"variables"`
Enabled bool `json:"enabled"`
Metadata map[string]interface{} `json:"metadata"`
}
// EnvironmentProvisioning represents environment provisioning
type EnvironmentProvisioning struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Type string `json:"type"`
Provider string `json:"provider"`
Resources map[string]interface{} `json:"resources"`
Script string `json:"script"`
Enabled bool `json:"enabled"`
Metadata map[string]interface{} `json:"metadata"`
}
// ProductionSupport handles support and maintenance
type ProductionSupport struct {
config *SupportConfig
documentation map[string]Documentation
maintenance map[string]MaintenanceProcedure
troubleshooting map[string]TroubleshootingGuide
training map[string]TrainingMaterial
logger *logrus.Logger
}
// SupportConfig holds support configuration
type SupportConfig struct {
Enabled bool `json:"enabled"`
Documentation bool `json:"documentation"`
Maintenance bool `json:"maintenance"`
Troubleshooting bool `json:"troubleshooting"`
Training bool `json:"training"`
Metadata map[string]string `json:"metadata"`
}
// Documentation represents support documentation
type Documentation struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Type string `json:"type"`
Path string `json:"path"`
Format string `json:"format"`
Version string `json:"version"`
Updated time.Time `json:"updated"`
Enabled bool `json:"enabled"`
Metadata map[string]interface{} `json:"metadata"`
}
// MaintenanceProcedure represents a maintenance procedure
type MaintenanceProcedure struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Type string `json:"type"`
Schedule string `json:"schedule"`
Duration time.Duration `json:"duration"`
Steps []MaintenanceStep `json:"steps"`
Enabled bool `json:"enabled"`
Metadata map[string]interface{} `json:"metadata"`
}
// MaintenanceStep represents a maintenance step
type MaintenanceStep struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Action string `json:"action"`
Command string `json:"command"`
Args []string `json:"args"`
Timeout time.Duration `json:"timeout"`
Order int `json:"order"`
Metadata map[string]interface{} `json:"metadata"`
}
// TroubleshootingGuide represents a troubleshooting guide
type TroubleshootingGuide struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Category string `json:"category"`
Problems []TroubleshootingProblem `json:"problems"`
Enabled bool `json:"enabled"`
Metadata map[string]interface{} `json:"metadata"`
}
// TroubleshootingProblem represents a troubleshooting problem
type TroubleshootingProblem struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Symptoms []string `json:"symptoms"`
Solutions []string `json:"solutions"`
Priority string `json:"priority"`
Metadata map[string]interface{} `json:"metadata"`
}
// TrainingMaterial represents training material
type TrainingMaterial struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Type string `json:"type"`
Path string `json:"path"`
Duration time.Duration `json:"duration"`
Prerequisites []string `json:"prerequisites"`
Enabled bool `json:"enabled"`
Metadata map[string]interface{} `json:"metadata"`
}
// NewProductionManager creates a new production manager
func NewProductionManager(config *ProductionConfig, logger *logrus.Logger) *ProductionManager {
manager := &ProductionManager{
logger: logger,
config: config,
performance: NewPerformanceOptimizer(logger),
deployment: NewDeploymentAutomation(logger),
support: NewProductionSupport(logger),
}
return manager
}
// NewPerformanceOptimizer creates a new performance optimizer
func NewPerformanceOptimizer(logger *logrus.Logger) *PerformanceOptimizer {
optimizer := &PerformanceOptimizer{
config: &PerformanceConfig{},
loadTests: make(map[string]LoadTest),
scalability: make(map[string]ScalabilityTest),
benchmarks: make(map[string]Benchmark),
logger: logger,
}
// Initialize performance testing scenarios
optimizer.initializeLoadTests()
optimizer.initializeScalabilityTests()
optimizer.initializeBenchmarks()
return optimizer
}
// NewDeploymentAutomation creates a new deployment automation manager
func NewDeploymentAutomation(logger *logrus.Logger) *DeploymentAutomation {
automation := &DeploymentAutomation{
config: &DeploymentConfig{},
scripts: make(map[string]DeploymentScript),
configs: make(map[string]ConfigManagement),
provisioning: make(map[string]EnvironmentProvisioning),
logger: logger,
}
// Initialize deployment automation
automation.initializeDeploymentScripts()
automation.initializeConfigManagement()
automation.initializeEnvironmentProvisioning()
return automation
}
// NewProductionSupport creates a new production support manager
func NewProductionSupport(logger *logrus.Logger) *ProductionSupport {
support := &ProductionSupport{
config: &SupportConfig{},
documentation: make(map[string]Documentation),
maintenance: make(map[string]MaintenanceProcedure),
troubleshooting: make(map[string]TroubleshootingGuide),
training: make(map[string]TrainingMaterial),
logger: logger,
}
// Initialize production support
support.initializeDocumentation()
support.initializeMaintenanceProcedures()
support.initializeTroubleshootingGuides()
support.initializeTrainingMaterials()
return support
}
// Initialize performance testing scenarios
func (po *PerformanceOptimizer) initializeLoadTests() {
// Basic load test
po.loadTests["basic"] = LoadTest{
ID: "basic",
Name: "Basic Load Test",
Description: "Basic load testing with moderate user load",
Type: "load",
Users: 100,
Duration: 10 * time.Minute,
RampUp: 2 * time.Minute,
Script: "scripts/load-test-basic.sh",
Enabled: true,
}
// Stress test
po.loadTests["stress"] = LoadTest{
ID: "stress",
Name: "Stress Test",
Description: "Stress testing with high user load",
Type: "stress",
Users: 500,
Duration: 15 * time.Minute,
RampUp: 5 * time.Minute,
Script: "scripts/load-test-stress.sh",
Enabled: true,
}
// Spike test
po.loadTests["spike"] = LoadTest{
ID: "spike",
Name: "Spike Test",
Description: "Spike testing with sudden user load increase",
Type: "spike",
Users: 1000,
Duration: 5 * time.Minute,
RampUp: 30 * time.Second,
Script: "scripts/load-test-spike.sh",
Enabled: true,
}
}
func (po *PerformanceOptimizer) initializeScalabilityTests() {
// Horizontal scaling test
po.scalability["horizontal"] = ScalabilityTest{
ID: "horizontal",
Name: "Horizontal Scaling Test",
Description: "Test horizontal scaling by adding nodes",
Type: "horizontal",
StartNodes: 1,
EndNodes: 10,
StepSize: 1,
Script: "scripts/scalability-horizontal.sh",
Enabled: true,
}
// Vertical scaling test
po.scalability["vertical"] = ScalabilityTest{
ID: "vertical",
Name: "Vertical Scaling Test",
Description: "Test vertical scaling by increasing resources",
Type: "vertical",
StartNodes: 1,
EndNodes: 1,
StepSize: 1,
Script: "scripts/scalability-vertical.sh",
Enabled: true,
}
}
func (po *PerformanceOptimizer) initializeBenchmarks() {
// Build performance benchmark
po.benchmarks["build"] = Benchmark{
ID: "build",
Name: "Build Performance Benchmark",
Description: "Benchmark build system performance",
Type: "build",
Metric: "build_time_seconds",
Target: 1800.0, // 30 minutes
Script: "scripts/benchmark-build.sh",
Enabled: true,
}
// API response time benchmark
po.benchmarks["api"] = Benchmark{
ID: "api",
Name: "API Response Time Benchmark",
Description: "Benchmark API response times",
Type: "api",
Metric: "response_time_ms",
Target: 2000.0, // 2 seconds
Script: "scripts/benchmark-api.sh",
Enabled: true,
}
}
// Initialize deployment automation
func (da *DeploymentAutomation) initializeDeploymentScripts() {
// Production deployment script
da.scripts["production"] = DeploymentScript{
ID: "production",
Name: "Production Deployment",
Description: "Deploy to production environment",
Type: "production",
ScriptPath: "scripts/deploy-production.sh",
Parameters: map[string]interface{}{"environment": "production"},
Timeout: 30 * time.Minute,
Rollback: "scripts/rollback-production.sh",
Enabled: true,
}
// Staging deployment script
da.scripts["staging"] = DeploymentScript{
ID: "staging",
Name: "Staging Deployment",
Description: "Deploy to staging environment",
Type: "staging",
ScriptPath: "scripts/deploy-staging.sh",
Parameters: map[string]interface{}{"environment": "staging"},
Timeout: 15 * time.Minute,
Rollback: "scripts/rollback-staging.sh",
Enabled: true,
}
}
func (da *DeploymentAutomation) initializeConfigManagement() {
// Production configuration
da.configs["production"] = ConfigManagement{
ID: "production",
Name: "Production Configuration",
Description: "Production environment configuration",
Type: "production",
ConfigPath: "config/production",
Templates: []string{"config/production/templates"},
Variables: map[string]interface{}{"environment": "production"},
Enabled: true,
}
// Staging configuration
da.configs["staging"] = ConfigManagement{
ID: "staging",
Name: "Staging Configuration",
Description: "Staging environment configuration",
Type: "staging",
ConfigPath: "config/staging",
Templates: []string{"config/staging/templates"},
Variables: map[string]interface{}{"environment": "staging"},
Enabled: true,
}
}
func (da *DeploymentAutomation) initializeEnvironmentProvisioning() {
// Production environment
da.provisioning["production"] = EnvironmentProvisioning{
ID: "production",
Name: "Production Environment",
Description: "Production environment provisioning",
Type: "production",
Provider: "kubernetes",
Resources: map[string]interface{}{
"nodes": 5,
"cpu": "8",
"memory": "32Gi",
},
Script: "scripts/provision-production.sh",
Enabled: true,
}
// Staging environment
da.provisioning["staging"] = EnvironmentProvisioning{
ID: "staging",
Name: "Staging Environment",
Description: "Staging environment provisioning",
Type: "staging",
Provider: "kubernetes",
Resources: map[string]interface{}{
"nodes": 2,
"cpu": "4",
"memory": "16Gi",
},
Script: "scripts/provision-staging.sh",
Enabled: true,
}
}
// Initialize production support
func (ps *ProductionSupport) initializeDocumentation() {
// User manual
ps.documentation["user-manual"] = Documentation{
ID: "user-manual",
Name: "User Manual",
Description: "User manual for Debian Forge",
Type: "manual",
Path: "docs/user-manual.md",
Format: "markdown",
Version: "1.0.0",
Updated: time.Now(),
Enabled: true,
}
// Admin guide
ps.documentation["admin-guide"] = Documentation{
ID: "admin-guide",
Name: "Administrator Guide",
Description: "Administrator guide for Debian Forge",
Type: "guide",
Path: "docs/admin-guide.md",
Format: "markdown",
Version: "1.0.0",
Updated: time.Now(),
Enabled: true,
}
}
func (ps *ProductionSupport) initializeMaintenanceProcedures() {
// Daily maintenance
ps.maintenance["daily"] = MaintenanceProcedure{
ID: "daily",
Name: "Daily Maintenance",
Description: "Daily maintenance procedures",
Type: "daily",
Schedule: "0 2 * * *", // 2 AM daily
Duration: 30 * time.Minute,
Steps: []MaintenanceStep{
{
ID: "backup",
Name: "Database Backup",
Description: "Create daily database backup",
Action: "backup_database",
Command: "pg_dump",
Args: []string{"--backup", "debian_forge"},
Timeout: 10 * time.Minute,
Order: 1,
},
{
ID: "cleanup",
Name: "Log Cleanup",
Description: "Clean up old log files",
Action: "cleanup_logs",
Command: "logrotate",
Args: []string{"-f", "/etc/logrotate.d/debian-forge"},
Timeout: 5 * time.Minute,
Order: 2,
},
},
Enabled: true,
}
// Weekly maintenance
ps.maintenance["weekly"] = MaintenanceProcedure{
ID: "weekly",
Name: "Weekly Maintenance",
Description: "Weekly maintenance procedures",
Type: "weekly",
Schedule: "0 3 * * 0", // 3 AM Sunday
Duration: 2 * time.Hour,
Steps: []MaintenanceStep{
{
ID: "updates",
Name: "System Updates",
Description: "Apply system updates",
Action: "system_updates",
Command: "apt",
Args: []string{"update", "&&", "apt", "upgrade", "-y"},
Timeout: 60 * time.Minute,
Order: 1,
},
},
Enabled: true,
}
}
func (ps *ProductionSupport) initializeTroubleshootingGuides() {
// Build failures
ps.troubleshooting["build-failures"] = TroubleshootingGuide{
ID: "build-failures",
Name: "Build Failures",
Description: "Troubleshooting guide for build failures",
Category: "builds",
Problems: []TroubleshootingProblem{
{
ID: "dependency-issues",
Name: "Dependency Issues",
Description: "Package dependency resolution problems",
Symptoms: []string{"Build fails with dependency errors", "Missing packages"},
Solutions: []string{"Update package lists", "Install missing dependencies", "Check repository configuration"},
Priority: "high",
},
{
ID: "resource-issues",
Name: "Resource Issues",
Description: "Insufficient system resources",
Symptoms: []string{"Build fails with out of memory", "Disk space errors"},
Solutions: []string{"Increase system memory", "Free up disk space", "Check resource limits"},
Priority: "medium",
},
},
Enabled: true,
}
}
func (ps *ProductionSupport) initializeTrainingMaterials() {
// Basic training
ps.training["basic"] = TrainingMaterial{
ID: "basic",
Name: "Basic Training",
Description: "Basic training for new users",
Type: "basic",
Path: "training/basic-training.md",
Duration: 2 * time.Hour,
Prerequisites: []string{"Linux basics", "Command line experience"},
Enabled: true,
}
// Advanced training
ps.training["advanced"] = TrainingMaterial{
ID: "advanced",
Name: "Advanced Training",
Description: "Advanced training for power users",
Type: "advanced",
Path: "training/advanced-training.md",
Duration: 4 * time.Hour,
Prerequisites: []string{"Basic training", "Go programming", "Container experience"},
Enabled: true,
}
}
// Performance optimization methods
func (po *PerformanceOptimizer) RunLoadTest(testID string) error {
test, exists := po.loadTests[testID]
if !exists {
return fmt.Errorf("load test not found: %s", testID)
}
if !test.Enabled {
return fmt.Errorf("load test is disabled: %s", testID)
}
po.logger.Infof("Running load test: %s", test.Name)
// Execute load test script
if err := po.executeTestScript(test.Script, test.Parameters); err != nil {
return fmt.Errorf("load test execution failed: %w", err)
}
po.logger.Infof("Load test completed successfully: %s", testID)
return nil
}
func (po *PerformanceOptimizer) RunScalabilityTest(testID string) error {
test, exists := po.scalability[testID]
if !exists {
return fmt.Errorf("scalability test not found: %s", testID)
}
if !test.Enabled {
return fmt.Errorf("scalability test is disabled: %s", testID)
}
po.logger.Infof("Running scalability test: %s", test.Name)
// Execute scalability test script
if err := po.executeTestScript(test.Script, test.Parameters); err != nil {
return fmt.Errorf("scalability test execution failed: %w", err)
}
po.logger.Infof("Scalability test completed successfully: %s", testID)
return nil
}
func (po *PerformanceOptimizer) RunBenchmark(benchmarkID string) error {
benchmark, exists := po.benchmarks[benchmarkID]
if !exists {
return fmt.Errorf("benchmark not found: %s", benchmarkID)
}
if !benchmark.Enabled {
return fmt.Errorf("benchmark is disabled: %s", benchmarkID)
}
po.logger.Infof("Running benchmark: %s", benchmark.Name)
// Execute benchmark script
if err := po.executeTestScript(benchmark.Script, benchmark.Parameters); err != nil {
return fmt.Errorf("benchmark execution failed: %w", err)
}
po.logger.Infof("Benchmark completed successfully: %s", benchmarkID)
return nil
}
func (po *PerformanceOptimizer) executeTestScript(scriptPath string, params map[string]interface{}) error {
// This is a placeholder for script execution
// In production, implement actual script execution logic
po.logger.Infof("Executing test script: %s", scriptPath)
// Simulate script execution
time.Sleep(2 * time.Second)
return nil
}
// Deployment automation methods
func (da *DeploymentAutomation) ExecuteDeployment(scriptID string) error {
script, exists := da.scripts[scriptID]
if !exists {
return fmt.Errorf("deployment script not found: %s", scriptID)
}
if !script.Enabled {
return fmt.Errorf("deployment script is disabled: %s", scriptID)
}
da.logger.Infof("Executing deployment: %s", script.Name)
// Execute deployment script
if err := da.executeDeploymentScript(script); err != nil {
return fmt.Errorf("deployment execution failed: %w", err)
}
da.logger.Infof("Deployment completed successfully: %s", scriptID)
return nil
}
func (da *DeploymentAutomation) executeDeploymentScript(script DeploymentScript) error {
da.logger.Infof("Executing deployment script: %s", script.ScriptPath)
// This is a placeholder for script execution
// In production, implement actual script execution logic
time.Sleep(5 * time.Second)
return nil
}
func (da *DeploymentAutomation) ProvisionEnvironment(envID string) error {
env, exists := da.provisioning[envID]
if !exists {
return fmt.Errorf("environment not found: %s", envID)
}
if !env.Enabled {
return fmt.Errorf("environment is disabled: %s", envID)
}
da.logger.Infof("Provisioning environment: %s", env.Name)
// Execute provisioning script
if err := da.executeProvisioningScript(env); err != nil {
return fmt.Errorf("environment provisioning failed: %w", err)
}
da.logger.Infof("Environment provisioned successfully: %s", envID)
return nil
}
func (da *DeploymentAutomation) executeProvisioningScript(env EnvironmentProvisioning) error {
da.logger.Infof("Executing provisioning script: %s", env.Script)
// This is a placeholder for script execution
// In production, implement actual script execution logic
time.Sleep(10 * time.Second)
return nil
}
// Production support methods
func (ps *ProductionSupport) GetDocumentation(docID string) (*Documentation, error) {
doc, exists := ps.documentation[docID]
if !exists {
return nil, fmt.Errorf("documentation not found: %s", docID)
}
return &doc, nil
}
func (ps *ProductionSupport) ExecuteMaintenance(procedureID string) error {
procedure, exists := ps.maintenance[procedureID]
if !exists {
return fmt.Errorf("maintenance procedure not found: %s", procedureID)
}
if !procedure.Enabled {
return fmt.Errorf("maintenance procedure is disabled: %s", procedureID)
}
ps.logger.Infof("Executing maintenance procedure: %s", procedure.Name)
// Execute maintenance steps in order
for _, step := range procedure.Steps {
if err := ps.executeMaintenanceStep(step); err != nil {
return fmt.Errorf("maintenance step failed: %s - %w", step.ID, err)
}
}
ps.logger.Infof("Maintenance procedure completed successfully: %s", procedureID)
return nil
}
func (ps *ProductionSupport) executeMaintenanceStep(step MaintenanceStep) error {
ps.logger.Infof("Executing maintenance step: %s", step.Name)
// This is a placeholder for step execution
// In production, implement actual step execution logic
ps.logger.Infof("Step %s completed: %s", step.ID, step.Description)
return nil
}
func (ps *ProductionSupport) GetTroubleshootingGuide(category string) (*TroubleshootingGuide, error) {
for _, guide := range ps.troubleshooting {
if guide.Category == category && guide.Enabled {
return &guide, nil
}
}
return nil, fmt.Errorf("troubleshooting guide not found for category: %s", category)
}
func (ps *ProductionSupport) GetTrainingMaterial(trainingID string) (*TrainingMaterial, error) {
training, exists := ps.training[trainingID]
if !exists {
return nil, fmt.Errorf("training material not found: %s", trainingID)
}
return &training, nil
}