1use super::Version;
7use crate::error::CoreError;
8use std::collections::{HashMap, VecDeque};
9
10use serde::{Deserialize, Serialize};
11
12#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct MigrationPlan {
15 pub from_version: Version,
17 pub toversion: Version,
19 pub steps: Vec<MigrationStep>,
21 pub estimated_effort: u32,
23 pub risk_level: RiskLevel,
25 pub rollback_plan: Option<RollbackPlan>,
27 pub prerequisites: Vec<String>,
29 pub validation_steps: Vec<String>,
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct MigrationStep {
36 pub id: String,
38 pub name: String,
40 pub description: String,
42 pub step_type: StepType,
44 pub estimated_effort: u32,
46 pub priority: StepPriority,
48 pub dependencies: Vec<String>,
50 pub automation_script: Option<String>,
52 pub manual_instructions: Option<String>,
54 pub validation_criteria: Vec<String>,
56 pub rollback_instructions: Option<String>,
58}
59
60#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
62pub enum StepType {
63 CodeChange,
65 ConfigurationUpdate,
67 DatabaseMigration,
69 DependencyUpdate,
71 FeatureRemoval,
73 ApiChange,
75 DataFormatChange,
77 Testing,
79 Documentation,
81}
82
83#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
85pub enum StepPriority {
86 Optional,
88 Low,
90 Medium,
92 High,
94 Critical,
96}
97
98#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
100pub enum RiskLevel {
101 Low,
103 Medium,
105 High,
107 Critical,
109}
110
111#[derive(Debug, Clone, Serialize, Deserialize)]
113pub struct RollbackPlan {
114 pub steps: Vec<RollbackStep>,
116 pub estimated_time: u32,
118 pub backup_requirements: Vec<String>,
120 pub recovery_validation: Vec<String>,
122}
123
124#[derive(Debug, Clone, Serialize, Deserialize)]
126pub struct RollbackStep {
127 pub id: String,
129 pub description: String,
131 pub instructions: String,
133 pub validation: Option<String>,
135}
136
137#[derive(Debug, Clone, Serialize, Deserialize)]
139pub struct MigrationExecution {
140 pub plan: MigrationPlan,
142 pub current_step: Option<String>,
144 pub completed_steps: Vec<String>,
146 pub failed_steps: Vec<String>,
148 pub status: ExecutionStatus,
150 pub start_time: chrono::DateTime<chrono::Utc>,
152 pub end_time: Option<chrono::DateTime<chrono::Utc>>,
154 pub executionlog: Vec<LogEntry>,
156}
157
158#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
160pub enum ExecutionStatus {
161 NotStarted,
163 InProgress,
165 Completed,
167 Failed,
169 Paused,
171 RolledBack,
173}
174
175#[derive(Debug, Clone, Serialize, Deserialize)]
177pub struct LogEntry {
178 pub timestamp: chrono::DateTime<chrono::Utc>,
180 pub level: LogLevel,
182 pub step_id: Option<String>,
184 pub message: String,
186 pub data: Option<String>,
188}
189
190#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
192pub enum LogLevel {
193 Debug,
195 Info,
197 Warning,
199 Error,
201}
202
203pub struct MigrationManager {
205 migration_templates: HashMap<(Version, Version), MigrationTemplate>,
207 active_migrations: HashMap<String, MigrationExecution>,
209 migration_history: Vec<MigrationExecution>,
211}
212
213impl MigrationManager {
214 pub fn new() -> Self {
216 Self {
217 migration_templates: HashMap::new(),
218 active_migrations: HashMap::new(),
219 migration_history: Vec::new(),
220 }
221 }
222
223 pub fn register_version(&mut self, _apiversion: &super::ApiVersion) -> Result<(), CoreError> {
225 Ok(())
227 }
228
229 pub fn create_migration_plan(
231 &self,
232 from_version: &Version,
233 toversion: &Version,
234 ) -> Result<MigrationPlan, CoreError> {
235 if let Some(template) = self
237 .migration_templates
238 .get(&(from_version.clone(), toversion.clone()))
239 {
240 return Ok(template.create_plan(from_version.clone(), toversion.clone()));
241 }
242
243 if let Some(path) = self.find_migration_path(from_version, toversion)? {
245 self.create_multi_step_plan(&path)
246 } else {
247 self.create_default_migration_plan(from_version, toversion)
248 }
249 }
250
251 pub fn has_migration_path(&self, from_version: &Version, toversion: &Version) -> bool {
253 if self
255 .migration_templates
256 .contains_key(&(from_version.clone(), toversion.clone()))
257 {
258 return true;
259 }
260
261 self.find_migration_path(from_version, toversion)
263 .unwrap_or(None)
264 .is_some()
265 }
266
267 fn find_migration_path(
269 &self,
270 from_version: &Version,
271 toversion: &Version,
272 ) -> Result<Option<Vec<Version>>, CoreError> {
273 let mut queue = VecDeque::new();
275 let mut visited = std::collections::HashSet::new();
276 let mut parent: HashMap<Version, Version> = HashMap::new();
277
278 queue.push_back(from_version.clone());
279 visited.insert(from_version.clone());
280
281 while let Some(current) = queue.pop_front() {
282 if current == *toversion {
283 let mut path = Vec::new();
285 let mut node = current;
286
287 while let Some(p) = parent.get(&node) {
288 path.push(node);
289 node = p.clone();
290 }
291 path.push(from_version.clone());
292 path.reverse();
293
294 return Ok(Some(path));
295 }
296
297 for (from, to) in self.migration_templates.keys() {
299 if *from == current && !visited.contains(to) {
300 visited.insert(to.clone());
301 parent.insert(to.clone(), current.clone());
302 queue.push_back(to.clone());
303 }
304 }
305 }
306
307 Ok(None)
308 }
309
310 fn create_multi_step_plan(&self, path: &[Version]) -> Result<MigrationPlan, CoreError> {
312 let mut all_steps = Vec::new();
313 let mut total_effort = 0;
314 let mut max_risk = RiskLevel::Low;
315
316 for window in path.windows(2) {
317 if let Some(template) = self
318 .migration_templates
319 .get(&(window[0].clone(), window[1].clone()))
320 {
321 let plan = template.create_plan(window[0].clone(), window[1].clone());
322 all_steps.extend(plan.steps);
323 total_effort += plan.estimated_effort;
324 max_risk = max_risk.max(plan.risk_level);
325 }
326 }
327
328 Ok(MigrationPlan {
329 from_version: path.first().unwrap().clone(),
330 toversion: path.last().unwrap().clone(),
331 steps: all_steps,
332 estimated_effort: total_effort,
333 risk_level: max_risk,
334 rollback_plan: None, prerequisites: Vec::new(),
336 validation_steps: Vec::new(),
337 })
338 }
339
340 fn create_default_migration_plan(
342 &self,
343 from_version: &Version,
344 toversion: &Version,
345 ) -> Result<MigrationPlan, CoreError> {
346 let mut steps = Vec::new();
347 let risk_level = if toversion.major() > from_version.major() {
348 RiskLevel::High
349 } else if toversion.minor() > from_version.minor() {
350 RiskLevel::Medium
351 } else {
352 RiskLevel::Low
353 };
354
355 if toversion.major() > from_version.major() {
357 steps.push(MigrationStep {
358 id: "major_version_review".to_string(),
359 name: "Major Version Review".to_string(),
360 description: "Review all breaking changes in major version upgrade".to_string(),
361 step_type: StepType::CodeChange,
362 estimated_effort: 40,
363 priority: StepPriority::Critical,
364 dependencies: Vec::new(),
365 automation_script: None,
366 manual_instructions: Some(
367 "Review changelog and identify all breaking changes".to_string(),
368 ),
369 validation_criteria: vec![
370 "All breaking changes identified".to_string(),
371 "Migration strategy defined".to_string(),
372 ],
373 rollback_instructions: Some("Revert to previous _version".to_string()),
374 });
375 }
376
377 if toversion.minor() > from_version.minor() {
378 steps.push(MigrationStep {
379 id: "minor_version_update".to_string(),
380 name: "Minor Version Update".to_string(),
381 description: "Update to new minor version with backward compatibility".to_string(),
382 step_type: StepType::DependencyUpdate,
383 estimated_effort: 8,
384 priority: StepPriority::Medium,
385 dependencies: Vec::new(),
386 automation_script: Some("update_dependencies.sh".to_string()),
387 manual_instructions: None,
388 validation_criteria: vec!["All tests pass".to_string()],
389 rollback_instructions: Some("Revert dependency versions".to_string()),
390 });
391 }
392
393 steps.push(MigrationStep {
395 id: "comprehensive_testing".to_string(),
396 name: "Comprehensive Testing".to_string(),
397 description: "Run full test suite and integration tests".to_string(),
398 step_type: StepType::Testing,
399 estimated_effort: 16,
400 priority: StepPriority::Critical,
401 dependencies: steps.iter().map(|s| s.id.clone()).collect(),
402 automation_script: Some("run_tests.sh".to_string()),
403 manual_instructions: Some("Verify all functionality works as expected".to_string()),
404 validation_criteria: vec![
405 "All unit tests pass".to_string(),
406 "All integration tests pass".to_string(),
407 "Performance benchmarks met".to_string(),
408 ],
409 rollback_instructions: None,
410 });
411
412 let estimated_effort = steps.iter().map(|s| s.estimated_effort).sum();
413
414 Ok(MigrationPlan {
415 from_version: from_version.clone(),
416 toversion: toversion.clone(),
417 steps,
418 estimated_effort,
419 risk_level,
420 rollback_plan: Some(self.create_default_rollback_plan()),
421 prerequisites: vec![
422 "Create backup of current system".to_string(),
423 "Ensure rollback plan is tested".to_string(),
424 ],
425 validation_steps: vec![
426 "Verify system functionality".to_string(),
427 "Check performance metrics".to_string(),
428 "Validate data integrity".to_string(),
429 ],
430 })
431 }
432
433 fn create_default_rollback_plan(&self) -> RollbackPlan {
435 RollbackPlan {
436 steps: vec![RollbackStep {
437 id: "restore_backup".to_string(),
438 description: "Restore from backup".to_string(),
439 instructions: "Restore system from pre-migration backup".to_string(),
440 validation: Some("Verify system is functioning".to_string()),
441 }],
442 estimated_time: 30, backup_requirements: vec![
444 "Full system backup".to_string(),
445 "Database backup".to_string(),
446 "Configuration backup".to_string(),
447 ],
448 recovery_validation: vec![
449 "System startup successful".to_string(),
450 "All services running".to_string(),
451 "Data integrity verified".to_string(),
452 ],
453 }
454 }
455
456 pub fn start_migration(
458 &mut self,
459 plan: MigrationPlan,
460 executionid: String,
461 ) -> Result<(), CoreError> {
462 let execution = MigrationExecution {
463 plan,
464 current_step: None,
465 completed_steps: Vec::new(),
466 failed_steps: Vec::new(),
467 status: ExecutionStatus::NotStarted,
468 start_time: chrono::Utc::now(),
469 end_time: None,
470 executionlog: Vec::new(),
471 };
472
473 self.active_migrations.insert(executionid, execution);
474 Ok(())
475 }
476
477 pub fn id_2(&self, executionid: &str) -> Option<&MigrationExecution> {
479 self.active_migrations.get(executionid)
480 }
481
482 pub fn cleanup_old_plans(&mut self) -> Result<usize, CoreError> {
484 let cutoff = chrono::Utc::now() - chrono::Duration::days(30);
485 let initial_count = self.migration_history.len();
486
487 self.migration_history
488 .retain(|execution| execution.start_time > cutoff);
489
490 Ok(initial_count - self.migration_history.len())
491 }
492}
493
494impl Default for MigrationManager {
495 fn default() -> Self {
496 Self::new()
497 }
498}
499
500struct MigrationTemplate {
502 steps: Vec<MigrationStepTemplate>,
504 base_effort: u32,
506 risk_level: RiskLevel,
508}
509
510impl MigrationTemplate {
511 fn create_plan(&self, from_version: Version, toversion: Version) -> MigrationPlan {
512 let steps = self
513 .steps
514 .iter()
515 .map(|template| template.create_step())
516 .collect();
517
518 MigrationPlan {
519 from_version,
520 toversion,
521 steps,
522 estimated_effort: self.base_effort,
523 risk_level: self.risk_level,
524 rollback_plan: None,
525 prerequisites: Vec::new(),
526 validation_steps: Vec::new(),
527 }
528 }
529}
530
531struct MigrationStepTemplate {
533 id: String,
534 name: String,
535 description: String,
536 step_type: StepType,
537 estimated_effort: u32,
538 priority: StepPriority,
539}
540
541impl MigrationStepTemplate {
542 fn create_step(&self) -> MigrationStep {
543 MigrationStep {
544 id: self.id.clone(),
545 name: self.name.clone(),
546 description: self.description.clone(),
547 step_type: self.step_type,
548 estimated_effort: self.estimated_effort,
549 priority: self.priority,
550 dependencies: Vec::new(),
551 automation_script: None,
552 manual_instructions: None,
553 validation_criteria: Vec::new(),
554 rollback_instructions: None,
555 }
556 }
557}
558
559#[cfg(test)]
560mod tests {
561 use super::*;
562
563 #[test]
564 fn test_migration_manager_creation() {
565 let manager = MigrationManager::new();
566 assert!(manager.migration_templates.is_empty());
567 assert!(manager.active_migrations.is_empty());
568 }
569
570 #[test]
571 fn test_default_migration_plan() {
572 let manager = MigrationManager::new();
573 let from_version = Version::new(1, 0, 0);
574 let toversion = Version::new(2, 0, 0);
575
576 let plan = manager
577 .create_migration_plan(&from_version, &toversion)
578 .unwrap();
579 assert_eq!(plan.from_version, from_version);
580 assert_eq!(plan.toversion, toversion);
581 assert!(!plan.steps.is_empty());
582 assert_eq!(plan.risk_level, RiskLevel::High); }
584
585 #[test]
586 fn test_minor_version_migration() {
587 let manager = MigrationManager::new();
588 let from_version = Version::new(1, 0, 0);
589 let toversion = Version::new(1, 1, 0);
590
591 let plan = manager
592 .create_migration_plan(&from_version, &toversion)
593 .unwrap();
594 assert_eq!(plan.risk_level, RiskLevel::Medium); }
596
597 #[test]
598 fn test_patch_version_migration() {
599 let manager = MigrationManager::new();
600 let from_version = Version::new(1, 0, 0);
601 let toversion = Version::new(1, 0, 1);
602
603 let plan = manager
604 .create_migration_plan(&from_version, &toversion)
605 .unwrap();
606 assert_eq!(plan.risk_level, RiskLevel::Low); }
608
609 #[test]
610 fn test_migration_execution() {
611 let mut manager = MigrationManager::new();
612 let plan = MigrationPlan {
613 from_version: Version::new(1, 0, 0),
614 toversion: Version::new(1, 1, 0),
615 steps: Vec::new(),
616 estimated_effort: 8,
617 risk_level: RiskLevel::Medium,
618 rollback_plan: None,
619 prerequisites: Vec::new(),
620 validation_steps: Vec::new(),
621 };
622
623 let executionid = "test_migration_123".to_string();
624 manager.start_migration(plan, executionid.clone()).unwrap();
625
626 }
630
631 #[test]
632 fn test_step_priority_ordering() {
633 assert!(StepPriority::Critical > StepPriority::High);
634 assert!(StepPriority::High > StepPriority::Medium);
635 assert!(StepPriority::Medium > StepPriority::Low);
636 assert!(StepPriority::Low > StepPriority::Optional);
637 }
638
639 #[test]
640 fn test_risk_level_ordering() {
641 assert!(RiskLevel::Critical > RiskLevel::High);
642 assert!(RiskLevel::High > RiskLevel::Medium);
643 assert!(RiskLevel::Medium > RiskLevel::Low);
644 }
645}