Coverage for mcpgateway / tools / builder / factory.py: 100%
31 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-02-11 07:10 +0000
« prev ^ index » next coverage.py v7.13.1, created at 2026-02-11 07:10 +0000
1# -*- coding: utf-8 -*-
2"""Location: ./mcpgateway/tools/builder/factory.py
3Copyright 2025
4SPDX-License-Identifier: Apache-2.0
5Authors: Teryl Taylor
7Factory for creating MCP Stack deployment implementations.
9This module provides a factory pattern for creating the appropriate deployment
10implementation (Dagger or Plain Python) based on availability and user preference.
12The factory handles graceful fallback from Dagger to Python if dependencies are
13unavailable, ensuring the deployment system works in various environments.
15Example:
16 >>> deployer, mode = DeployFactory.create_deployer("dagger", verbose=False)
17 ⚠ Dagger not installed. Using plain python.
18 >>> # Validate configuration (output varies by config)
19 >>> # deployer.validate("mcp-stack.yaml")
20"""
22# Standard
23from enum import Enum
25# Third-Party
26from rich.console import Console
28# First-Party
29from mcpgateway.tools.builder.pipeline import CICDModule
32class CICDTypes(str, Enum):
33 """Deployment implementation types.
35 Attributes:
36 DAGGER: Dagger-based implementation (optimal performance)
37 PYTHON: Plain Python implementation (fallback, no dependencies)
39 Examples:
40 >>> # Test enum values
41 >>> CICDTypes.DAGGER.value
42 'dagger'
43 >>> CICDTypes.PYTHON.value
44 'python'
46 >>> # Test enum comparison
47 >>> CICDTypes.DAGGER == "dagger"
48 True
49 >>> CICDTypes.PYTHON == "python"
50 True
52 >>> # Test enum membership
53 >>> "dagger" in [t.value for t in CICDTypes]
54 True
55 >>> "python" in [t.value for t in CICDTypes]
56 True
58 >>> # Test enum iteration
59 >>> types = list(CICDTypes)
60 >>> len(types)
61 2
62 >>> CICDTypes.DAGGER in types
63 True
64 """
66 DAGGER = "dagger"
67 PYTHON = "python"
70console = Console()
73class DeployFactory:
74 """Factory for creating MCP Stack deployment implementations.
76 This factory implements the Strategy pattern, allowing dynamic selection
77 between Dagger and Python implementations based on availability.
78 """
80 @staticmethod
81 def create_deployer(deployer: str, verbose: bool = False) -> tuple[CICDModule, CICDTypes]:
82 """Create a deployment implementation instance.
84 Attempts to load the requested deployer type with automatic fallback
85 to Python implementation if dependencies are missing.
87 Args:
88 deployer: Deployment type to create ("dagger" or "python")
89 verbose: Enable verbose logging during creation
91 Returns:
92 tuple: (deployer_instance, actual_type)
93 - deployer_instance: Instance of MCPStackDagger or MCPStackPython
94 - actual_type: CICDTypes enum indicating which implementation was loaded
96 Raises:
97 RuntimeError: If no implementation can be loaded (critical failure)
99 Example:
100 >>> # Try to load Dagger, fall back to Python if unavailable
101 >>> deployer, mode = DeployFactory.create_deployer("dagger", verbose=False)
102 ⚠ Dagger not installed. Using plain python.
103 >>> if mode == CICDTypes.DAGGER:
104 ... print("Using optimized Dagger implementation")
105 ... else:
106 ... print("Using fallback Python implementation")
107 Using fallback Python implementation
108 """
109 # Attempt to load Dagger implementation first if requested
110 if deployer == "dagger":
111 try:
112 # First-Party
113 from mcpgateway.tools.builder.dagger_deploy import DAGGER_AVAILABLE, MCPStackDagger
115 # Check if dagger is actually available (not just the module)
116 if not DAGGER_AVAILABLE:
117 raise ImportError("Dagger SDK not installed")
119 if verbose:
120 console.print("[green]✓ Dagger module loaded[/green]")
122 return (MCPStackDagger(verbose), CICDTypes.DAGGER)
124 except ImportError:
125 # Dagger dependencies not available, fall back to Python
126 console.print("[yellow]⚠ Dagger not installed. Using plain python.[/yellow]")
128 # Load plain Python implementation (fallback or explicitly requested)
129 try:
130 # First-Party
131 from mcpgateway.tools.builder.python_deploy import MCPStackPython
133 if verbose and deployer != "dagger":
134 console.print("[blue]Using plain Python implementation[/blue]")
136 return (MCPStackPython(verbose), CICDTypes.PYTHON)
138 except ImportError as e:
139 # Critical failure - neither implementation can be loaded
140 console.print("[red]✗ ERROR: Cannot import deployment modules[/red]")
141 console.print(f"[red] Details: {e}[/red]")
142 console.print("[yellow] Make sure you're running from the project root[/yellow]")
143 console.print("[yellow] and PYTHONPATH is set correctly[/yellow]")
145 # This should never be reached if PYTHONPATH is set correctly
146 raise RuntimeError(f"Unable to load deployer of type '{deployer}'. ")