Coverage for fake_kubernetes_client/dynamic_client.py: 60%
43 statements
« prev ^ index » next coverage.py v7.10.1, created at 2025-07-29 12:31 +0300
« prev ^ index » next coverage.py v7.10.1, created at 2025-07-29 12:31 +0300
1"""FakeDynamicClient implementation for fake Kubernetes client"""
3from typing import Any, Union
5from fake_kubernetes_client.exceptions import NotFoundError
6from fake_kubernetes_client.kubernetes_client import FakeKubernetesClient
7from fake_kubernetes_client.resource_field import FakeResourceField
8from fake_kubernetes_client.resource_manager import FakeResourceManager
9from fake_kubernetes_client.resource_registry import FakeResourceRegistry
10from fake_kubernetes_client.resource_storage import FakeResourceStorage
11from fake_kubernetes_client.resource_instance import FakeResourceInstance
14class FakeDynamicClient:
15 """Fake implementation of kubernetes.dynamic.DynamicClient"""
17 def __init__(self, client: Union[FakeKubernetesClient, None] = None) -> None:
18 # Distinguish between creating a new client vs using an existing one
19 if client is None:
20 # Create a new client with circular reference
21 self.client = FakeKubernetesClient(dynamic_client=self)
22 else:
23 # Use the provided client without modifying its dynamic_client reference
24 # This respects any intentional null or existing value
25 self.client = client
27 self.configuration = self.client.configuration
28 self.storage = FakeResourceStorage()
29 self.registry = FakeResourceRegistry()
30 self._resources_manager = FakeResourceManager(client=self)
32 @property
33 def resources(self) -> FakeResourceManager:
34 """Get the resource manager"""
35 return self._resources_manager
37 def get_openapi_spec(self) -> dict[str, Any]:
38 """Get OpenAPI spec (minimal fake implementation)"""
39 return {
40 "openapi": "3.0.0",
41 "info": {"title": "Kubernetes", "version": "v1.29.0"},
42 "paths": {},
43 }
45 def request(self, method: str, path: str, body: Any = None, **kwargs: Any) -> FakeResourceField:
46 """Make a raw request (fake implementation)"""
47 # Minimal implementation - just return empty response
48 return FakeResourceField(data={})
50 def version(self) -> dict[str, Any]:
51 """Get server version"""
52 return {
53 "major": "1",
54 "minor": "29",
55 "gitVersion": "v1.29.0",
56 "gitCommit": "fake",
57 "gitTreeState": "clean",
58 "buildDate": "2024-01-01T00:00:00Z",
59 "goVersion": "go1.21.0",
60 "compiler": "gc",
61 "platform": "linux/amd64",
62 }
64 def ensure_namespace(self, namespace: str) -> FakeResourceField:
65 """Ensure namespace exists (fake implementation)"""
66 # Get the namespace resource definition
67 ns_resource = self.resources.get(api_version="v1", kind="Namespace")
69 # Check if namespace exists
70 try:
71 return ns_resource.get(name=namespace)
72 except NotFoundError:
73 # Create namespace if it doesn't exist
74 body = {
75 "metadata": {"name": namespace},
76 "spec": {"finalizers": ["kubernetes"]},
77 }
78 return ns_resource.create(body=body)
80 def api_client(self) -> FakeKubernetesClient:
81 """Get the underlying API client"""
82 return self.client
84 def register_resources(self, resources: Union[dict[str, Any], list[dict[str, Any]]]) -> None:
85 """
86 Register custom resources dynamically.
88 This method allows you to add custom resource definitions to the fake client,
89 which is useful for testing with Custom Resource Definitions (CRDs) or
90 resources that are not included in the default schema.
92 Args:
93 resources: Either a single resource definition dict or a list of resource definitions.
94 Each resource definition should contain:
95 - kind: Resource kind (required)
96 - api_version: API version without group (required)
97 - group: API group (optional, empty string for core resources)
98 - namespaced: Whether resource is namespaced (optional, defaults to True)
99 - plural: Plural name (optional, will be generated if not provided)
100 - singular: Singular name (optional, defaults to lowercase kind)
102 Example:
103 # Register a single custom resource
104 client.register_resources({
105 "kind": "MyCustomResource",
106 "api_version": "v1alpha1",
107 "group": "example.com",
108 "namespaced": True
109 })
111 # Register multiple resources
112 client.register_resources([
113 {
114 "kind": "MyApp",
115 "api_version": "v1",
116 "group": "apps.example.com",
117 "namespaced": True,
118 "plural": "myapps"
119 },
120 {
121 "kind": "MyConfig",
122 "api_version": "v1beta1",
123 "group": "config.example.com",
124 "namespaced": False # cluster-scoped
125 }
126 ])
128 # After registration, use the resources normally
129 myapp_api = client.resources.get(
130 api_version="apps.example.com/v1",
131 kind="MyApp"
132 )
133 myapp_api.create(body={...}, namespace="default")
134 """
135 self.registry.register_resources(resources=resources)
137 def get(self, resource: Any, *args: Any, **kwargs: Any) -> Any:
138 """Get resources based on resource definition"""
139 # Extract resource definition from FakeResourceField if needed
140 if hasattr(resource, "data"):
141 resource_def = resource.data
142 else:
143 resource_def = resource
145 # Create a resource instance for this resource type
146 resource_instance = FakeResourceInstance(resource_def=resource_def, storage=self.storage, client=self)
148 # Call get on the resource instance to list resources
149 return resource_instance.get(*args, **kwargs)