feat: Add typing and split fake client code#2438
Conversation
WalkthroughThis change refactors the fake Kubernetes client by splitting its previous monolithic implementation into multiple focused modules, reorganizes public exports, updates documentation, and makes minor configuration adjustments. It introduces new classes and files for configuration, dynamic client, resource management, storage, exceptions, and status generation, while removing the old single-file implementation. Changes
Possibly related PRs
Suggested labels
Suggested reviewers
📜 Recent review detailsConfiguration used: .coderabbit.yaml 📒 Files selected for processing (12)
✅ Files skipped from review due to trivial changes (1)
🚧 Files skipped from review as they are similar to previous changes (4)
🧰 Additional context used🧠 Learnings (3)tests/test_resource.py (2)fake_kubernetes_client/status_templates.py (3)fake_kubernetes_client/resource_instance.py (1)🧬 Code Graph Analysis (3)tests/test_resource.py (1)
fake_kubernetes_client/resource_manager.py (6)
fake_kubernetes_client/status_schema_parser.py (3)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
🔇 Additional comments (19)
✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
Documentation and Community
|
|
Report bugs in Issues Welcome! 🎉This pull request will be automatically processed with the following features: 🔄 Automatic Actions
📋 Available CommandsPR Status Management
Review & Approval
Testing & Validation
Container Operations
Cherry-pick Operations
Label Management
✅ Merge RequirementsThis PR will be automatically approved when the following conditions are met:
📊 Review ProcessApprovers and ReviewersApprovers:
Reviewers:
Available Labels
💡 Tips
For more information, please refer to the project documentation or contact the maintainers. |
There was a problem hiding this comment.
Actionable comments posted: 11
🧹 Nitpick comments (13)
fake_kubernetes_client/types.py (1)
1-2: Placeholder file adds no value yet—consider fleshing it out or omitting it until needed.Right now the module contains only a docstring. If no shared type aliases or protocols are actually consumed by the new sub-modules, shipping an empty file clutters the package surface and invites dead-code drift. Either:
- Populate it with the concrete
TypedDict,Protocol, orLiteraldefinitions the other modules will import, or- Delete it for now and re-add once you have real content.
Also, if you decide to keep it, end the docstring with a period to satisfy common docstring-lint rules.
class_generator/class_generator.py (1)
244-250: Consider logging a warning when copy fails.While the silent failure approach prevents breaking the entire schema update process, it could lead to the fake client operating with stale resource mappings. Consider adding a warning log when the copy fails to help with debugging potential issues.
# Copy the resources mapping file to fake_kubernetes_client for the fake client to use fake_client_mappings = Path("fake_kubernetes_client/__resources-mappings.json") try: shutil.copy2(RESOURCES_MAPPING_FILE, fake_client_mappings) -except (OSError, IOError): +except (OSError, IOError) as exp: # Don't fail the entire process if copy fails - pass + LOGGER.warning(f"Failed to copy resources mapping file to fake client: {exp}")fake_kubernetes_client/resource_storage.py (1)
98-115: Consider documenting the limited label selector support.The current implementation only supports equality (
key=value), inequality (key!=value), and key presence checks. It doesn't support Kubernetes set-based selectors likein,notin,exists, ordoesnotexist.Consider adding a docstring note about these limitations or expanding support in the future.
Would you like me to generate an implementation that supports the full Kubernetes label selector syntax?
fake_kubernetes_client/README.md (2)
201-217: Use proper markdown heading instead of emphasis.-**1. Testing CRDs (Custom Resource Definitions)** +### 1. Testing CRDs (Custom Resource Definitions)
219-239: Use proper markdown heading instead of emphasis.-**2. Multi-Version Resources** +### 2. Multi-Version Resourcesfake_kubernetes_client/kubernetes_client.py (1)
17-18: Use pipe syntax for union types.Since the codebase uses modern Python typing, consider using the pipe syntax for better readability.
Apply this diff to use the newer pipe syntax:
- configuration: Union[FakeConfiguration, None] = None, - dynamic_client: Union["FakeDynamicClient", None] = None, + configuration: FakeConfiguration | None = None, + dynamic_client: "FakeDynamicClient" | None = None,fake_kubernetes_client/resource_field.py (1)
36-72: Reduce code duplication between__getattr__and__getitem__.The value wrapping logic is duplicated between
__getattr__(lines 52-60) and__getitem__(lines 63-71). Consider extracting this into a helper method.Apply this diff to reduce duplication:
+ def _wrap_value(self, value: Any) -> Any: + """Wrap value based on its type""" + if value is None: + return FakeResourceField(data={}) + elif isinstance(value, dict): + return FakeResourceField(data=value) + elif isinstance(value, list): + return [FakeResourceField(data=item) if isinstance(item, dict) else item for item in value] + else: + return value + def __getattr__(self, name: str) -> Any: # This is called ONLY when __getattribute__ raises AttributeError # Handle all dynamic attribute access here # Direct access to _data without using hasattr (avoids recursion) try: data = object.__getattribute__(self, "_data") except AttributeError: data = {} # For resource definition access, return simple values for common attributes # This ensures compatibility with ocp_resources code that expects strings if name in ["api_version", "group_version", "kind", "plural", "singular", "group", "version"]: return data.get(name, "") # Handle general data access value = data.get(name) - if value is None: - return FakeResourceField(data={}) - elif isinstance(value, dict): - return FakeResourceField(data=value) - elif isinstance(value, list): - return [FakeResourceField(data=item) if isinstance(item, dict) else item for item in value] - else: - return value + return self._wrap_value(value) def __getitem__(self, key: str) -> Any: value = self._data.get(key) - if value is None: - return FakeResourceField(data={}) - elif isinstance(value, dict): - return FakeResourceField(data=value) - elif isinstance(value, list): - return [FakeResourceField(data=item) if isinstance(item, dict) else item for item in value] - else: - return value + return self._wrap_value(value)fake_kubernetes_client/status_schema_parser.py (1)
233-233: Consider making random IP generation deterministic for tests.Random IP addresses could cause test flakiness if tests expect consistent values. Consider adding an option for deterministic generation.
You could add a seed parameter or use a deterministic approach:
elif field_name.endswith("IP"): - return f"10.0.0.{random.randint(1, 254)}" + # Use a hash of the field name for deterministic IPs + import hashlib + hash_val = int(hashlib.md5(field_name.encode()).hexdigest()[:2], 16) % 254 + 1 + return f"10.0.0.{hash_val}"fake_kubernetes_client/resource_manager.py (1)
50-60: Enhance preferred version selection logic.The current implementation only checks for "v1" specifically. Consider supporting other stable versions (v2, v3, etc.) and using a more robust version comparison.
- if preferred: - # Simple logic: prefer stable versions (v1 > v1beta1 > v1alpha1) - for def_ in definitions: - if def_["version"] == "v1": - resource_def = def_ - break - if not resource_def: - resource_def = definitions[0] + if preferred: + # Prefer stable versions: v1, v2, etc. > v1beta1 > v1alpha1 + stable_versions = [] + beta_versions = [] + alpha_versions = [] + + for def_ in definitions: + version = def_["version"] + if version.startswith("v") and version[1:].isdigit(): + stable_versions.append((int(version[1:]), def_)) + elif "beta" in version: + beta_versions.append(def_) + elif "alpha" in version: + alpha_versions.append(def_) + + if stable_versions: + # Sort by version number and pick the highest + resource_def = max(stable_versions, key=lambda x: x[0])[1] + elif beta_versions: + resource_def = beta_versions[0] + elif alpha_versions: + resource_def = alpha_versions[0] + else: + resource_def = definitions[0]fake_kubernetes_client/dynamic_client.py (1)
130-142: Consider improving type annotations for better type safety.The method uses
Anytypes extensively, which reduces type safety. Consider using more specific types or Union types.- def get(self, resource: Any, *args: Any, **kwargs: Any) -> Any: + def get( + self, + resource: Union[FakeResourceField, dict[str, Any]], + *args: Any, + **kwargs: Any + ) -> FakeResourceField: """Get resources based on resource definition""" # Extract resource definition from FakeResourceField if needed if hasattr(resource, "data"): resource_def = resource.data else: resource_def = resource # Create a resource instance for this resource type resource_instance = FakeResourceInstance(resource_def=resource_def, storage=self.storage, client=self) # Call get on the resource instance to list resources return resource_instance.get(*args, **kwargs)fake_kubernetes_client/resource_instance.py (3)
93-95: Consider making default namespace configurable.The default namespace is hardcoded to "default". Consider making this configurable or obtaining it from the client configuration.
resource_namespace = ( - body["metadata"].get("namespace", "default") if self.resource_def.get("namespaced") else None + body["metadata"].get("namespace") or self._get_default_namespace() if self.resource_def.get("namespaced") else None )Add a helper method:
def _get_default_namespace(self) -> str: """Get default namespace from configuration or return 'default'""" if self.client and hasattr(self.client, 'configuration'): return getattr(self.client.configuration, 'default_namespace', 'default') return 'default'
70-150: Extract helper methods to improve readability.The
createmethod is quite long (80 lines). Consider extracting some logic into helper methods for better maintainability.Extract metadata generation into a helper method:
def _generate_create_metadata(self, body: dict[str, Any]) -> None: """Generate metadata fields for resource creation""" body["metadata"].update({ "uid": str(uuid.uuid4()), "resourceVersion": self._generate_resource_version(), "creationTimestamp": self._generate_timestamp(), "generation": 1, "labels": body["metadata"].get("labels", {}), "annotations": body["metadata"].get("annotations", {}), })Then use it in the create method:
- # Add generated metadata - body["metadata"].update({ - "uid": str(uuid.uuid4()), - "resourceVersion": self._generate_resource_version(), - "creationTimestamp": self._generate_timestamp(), - "generation": 1, - "labels": body["metadata"].get("labels", {}), - "annotations": body["metadata"].get("annotations", {}), - }) + # Add generated metadata + self._generate_create_metadata(body)
397-399: Preserve API version and kind from body if provided.The method unconditionally sets
apiVersionandkind, potentially overriding values provided in the body. Consider preserving user-provided values.# Set API version and kind - body["apiVersion"] = self.resource_def["api_version"] - body["kind"] = self.resource_def["kind"] + # Set API version and kind if not provided + body.setdefault("apiVersion", self.resource_def["api_version"]) + body.setdefault("kind", self.resource_def["kind"])
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (75)
class_generator/schema/__not-kind.txtis excluded by!class_generator/schema/**class_generator/schema/__resources-mappings.jsonis excluded by!class_generator/schema/**class_generator/schema/_definitions.jsonis excluded by!class_generator/schema/**class_generator/schema/alertmanager.jsonis excluded by!class_generator/schema/**class_generator/schema/all.jsonis excluded by!class_generator/schema/**class_generator/schema/apiresource.jsonis excluded by!class_generator/schema/**class_generator/schema/apiresourcelist.jsonis excluded by!class_generator/schema/**class_generator/schema/backup.jsonis excluded by!class_generator/schema/**class_generator/schema/backuplist.jsonis excluded by!class_generator/schema/**class_generator/schema/cephblockpool.jsonis excluded by!class_generator/schema/**class_generator/schema/cephbuckettopic.jsonis excluded by!class_generator/schema/**class_generator/schema/cephconnection.jsonis excluded by!class_generator/schema/**class_generator/schema/cephobjectstoreuser.jsonis excluded by!class_generator/schema/**class_generator/schema/clientprofile.jsonis excluded by!class_generator/schema/**class_generator/schema/clientprofilemapping.jsonis excluded by!class_generator/schema/**class_generator/schema/clusteruserdefinednetwork.jsonis excluded by!class_generator/schema/**class_generator/schema/console.jsonis excluded by!class_generator/schema/**class_generator/schema/consoleplugin.jsonis excluded by!class_generator/schema/**class_generator/schema/consolepluginlist.jsonis excluded by!class_generator/schema/**class_generator/schema/controlplanemachineset.jsonis excluded by!class_generator/schema/**class_generator/schema/database.jsonis excluded by!class_generator/schema/**class_generator/schema/databaselist.jsonis excluded by!class_generator/schema/**class_generator/schema/driver.jsonis excluded by!class_generator/schema/**class_generator/schema/gateway.jsonis excluded by!class_generator/schema/**class_generator/schema/gatewayclass.jsonis excluded by!class_generator/schema/**class_generator/schema/gatewayclasslist.jsonis excluded by!class_generator/schema/**class_generator/schema/gatewaylist.jsonis excluded by!class_generator/schema/**class_generator/schema/grpcroute.jsonis excluded by!class_generator/schema/**class_generator/schema/grpcroutelist.jsonis excluded by!class_generator/schema/**class_generator/schema/helmchartrepository.jsonis excluded by!class_generator/schema/**class_generator/schema/host.jsonis excluded by!class_generator/schema/**class_generator/schema/httproute.jsonis excluded by!class_generator/schema/**class_generator/schema/httproutelist.jsonis excluded by!class_generator/schema/**class_generator/schema/hyperconverged.jsonis excluded by!class_generator/schema/**class_generator/schema/localvolume.jsonis excluded by!class_generator/schema/**class_generator/schema/localvolumediscovery.jsonis excluded by!class_generator/schema/**class_generator/schema/localvolumeset.jsonis excluded by!class_generator/schema/**class_generator/schema/machineosbuild.jsonis excluded by!class_generator/schema/**class_generator/schema/machineosbuildlist.jsonis excluded by!class_generator/schema/**class_generator/schema/machineosconfig.jsonis excluded by!class_generator/schema/**class_generator/schema/machineosconfiglist.jsonis excluded by!class_generator/schema/**class_generator/schema/migration.jsonis excluded by!class_generator/schema/**class_generator/schema/networkfence.jsonis excluded by!class_generator/schema/**class_generator/schema/networkmap.jsonis excluded by!class_generator/schema/**class_generator/schema/noobaa.jsonis excluded by!class_generator/schema/**class_generator/schema/operatorconfig.jsonis excluded by!class_generator/schema/**class_generator/schema/persistentvolumeclaimvolumesource.jsonis excluded by!class_generator/schema/**class_generator/schema/plan.jsonis excluded by!class_generator/schema/**class_generator/schema/projecthelmchartrepository.jsonis excluded by!class_generator/schema/**class_generator/schema/prometheus.jsonis excluded by!class_generator/schema/**class_generator/schema/provider.jsonis excluded by!class_generator/schema/**class_generator/schema/referencegrant.jsonis excluded by!class_generator/schema/**class_generator/schema/referencegrantlist.jsonis excluded by!class_generator/schema/**class_generator/schema/route.jsonis excluded by!class_generator/schema/**class_generator/schema/routelist.jsonis excluded by!class_generator/schema/**class_generator/schema/storageautoscaler.jsonis excluded by!class_generator/schema/**class_generator/schema/storageautoscalerlist.jsonis excluded by!class_generator/schema/**class_generator/schema/storagecluster.jsonis excluded by!class_generator/schema/**class_generator/schema/storageconsumer.jsonis excluded by!class_generator/schema/**class_generator/schema/storagemap.jsonis excluded by!class_generator/schema/**class_generator/schema/subscription.jsonis excluded by!class_generator/schema/**class_generator/schema/subscriptionlist.jsonis excluded by!class_generator/schema/**class_generator/schema/thanosruler.jsonis excluded by!class_generator/schema/**class_generator/schema/user.jsonis excluded by!class_generator/schema/**class_generator/schema/userlist.jsonis excluded by!class_generator/schema/**class_generator/schema/virtualmachine.jsonis excluded by!class_generator/schema/**class_generator/schema/virtualmachineclusterpreference.jsonis excluded by!class_generator/schema/**class_generator/schema/virtualmachineinstance.jsonis excluded by!class_generator/schema/**class_generator/schema/virtualmachineinstancepreset.jsonis excluded by!class_generator/schema/**class_generator/schema/virtualmachineinstancereplicaset.jsonis excluded by!class_generator/schema/**class_generator/schema/virtualmachinepool.jsonis excluded by!class_generator/schema/**class_generator/schema/virtualmachinepreference.jsonis excluded by!class_generator/schema/**class_generator/schema/virtualmachinesnapshotcontent.jsonis excluded by!class_generator/schema/**class_generator/schema/vspherexcopyvolumepopulator.jsonis excluded by!class_generator/schema/**class_generator/schema/vspherexcopyvolumepopulatorlist.jsonis excluded by!class_generator/schema/**
📒 Files selected for processing (20)
.flake8(1 hunks).pre-commit-config.yaml(1 hunks)README.md(1 hunks)class_generator/__cluster_version__.txt(1 hunks)class_generator/class_generator.py(1 hunks)fake_kubernetes_client/README.md(5 hunks)fake_kubernetes_client/__init__.py(1 hunks)fake_kubernetes_client/configuration.py(1 hunks)fake_kubernetes_client/dynamic_client.py(1 hunks)fake_kubernetes_client/exceptions.py(1 hunks)fake_kubernetes_client/fake_dynamic_client.py(0 hunks)fake_kubernetes_client/kubernetes_client.py(1 hunks)fake_kubernetes_client/resource_field.py(1 hunks)fake_kubernetes_client/resource_instance.py(1 hunks)fake_kubernetes_client/resource_manager.py(1 hunks)fake_kubernetes_client/resource_registry.py(1 hunks)fake_kubernetes_client/resource_storage.py(1 hunks)fake_kubernetes_client/status_schema_parser.py(1 hunks)fake_kubernetes_client/status_templates.py(1 hunks)fake_kubernetes_client/types.py(1 hunks)
💤 Files with no reviewable changes (1)
- fake_kubernetes_client/fake_dynamic_client.py
🧰 Additional context used
🧠 Learnings (4)
README.md (1)
Learnt from: EdDev
PR: RedHatQE/openshift-python-wrapper#2291
File: ocp_resources/resource.py:126-126
Timestamp: 2025-01-28T07:25:19.573Z
Learning: The environment variable `OPENSHIFT_PYTHON_WRAPPER_CLIENT_USE_PROXY` must be set and not empty to override the `use_proxy` parameter in the `get_client` function.
class_generator/__cluster_version__.txt (2)
Learnt from: sbahar619
PR: RedHatQE/openshift-python-wrapper#2126
File: ocp_resources/user_defined_network.py:19-19
Timestamp: 2024-10-08T23:43:22.342Z
Learning: When reviewing code generated by the class generator, avoid suggesting changes that would alter its automatic version.
Learnt from: sbahar619
PR: RedHatQE/openshift-python-wrapper#2126
File: ocp_resources/user_defined_network.py:19-19
Timestamp: 2024-09-29T12:43:09.926Z
Learning: When reviewing code generated by the class generator, avoid suggesting changes that would alter its automatic version.
class_generator/class_generator.py (5)
Learnt from: myakove
PR: RedHatQE/openshift-python-wrapper#2181
File: class_generator/class_generator.py:226-229
Timestamp: 2024-10-27T13:29:34.775Z
Learning: In the `update_kind_schema()` function in `class_generator.py`, the `tmp_schema_dir` directory contains only JSON files without subdirectories, so preserving the directory structure when copying files is unnecessary.
Learnt from: myakove
PR: RedHatQE/openshift-python-wrapper#2181
File: class_generator/schema/manualapprovalgatelist.json:12-18
Timestamp: 2024-10-27T14:07:35.575Z
Learning: All files under `class_generator/schema/` are auto-generated.
Learnt from: myakove
PR: RedHatQE/openshift-python-wrapper#2209
File: ocp_resources/volume_snapshot_class.py:0-0
Timestamp: 2024-12-03T08:02:11.880Z
Learning: In generated code files like `ocp_resources/volume_snapshot_class.py`, avoid suggesting adding validation checks or modifications, as generated code should not be manually altered.
Learnt from: myakove
PR: RedHatQE/openshift-python-wrapper#2152
File: class_generator/tests/manifests/ServingRuntime/serving_runtime.py:96-160
Timestamp: 2024-10-09T14:25:49.618Z
Learning: The code in the `class_generator/tests/manifests/ServingRuntime/serving_runtime.py` file is auto-generated. Avoid suggesting refactoring changes to auto-generated code in this path.
Learnt from: myakove
PR: RedHatQE/openshift-python-wrapper#2154
File: ocp_resources/serving_runtime.py:110-160
Timestamp: 2024-10-09T18:44:29.284Z
Learning: In `ocp_resources/serving_runtime.py`, the code is auto-generated and should not be manually modified.
fake_kubernetes_client/README.md (1)
Learnt from: myakove
PR: RedHatQE/openshift-python-wrapper#2232
File: tests/test_resources.py:21-23
Timestamp: 2024-12-15T08:13:40.715Z
Learning: In the `pod` fixture in `tests/test_resources.py`, it is expected that there will always be at least one pod available, and the test should fail if there are none.
🧬 Code Graph Analysis (4)
fake_kubernetes_client/status_schema_parser.py (1)
fake_kubernetes_client/resource_instance.py (2)
get(236-278)replace(351-418)
fake_kubernetes_client/resource_storage.py (4)
fake_kubernetes_client/dynamic_client.py (2)
resources(28-30)get(130-142)fake_kubernetes_client/resource_instance.py (1)
get(236-278)fake_kubernetes_client/resource_field.py (1)
get(85-89)fake_kubernetes_client/resource_manager.py (1)
get(21-65)
fake_kubernetes_client/status_templates.py (2)
fake_kubernetes_client/status_schema_parser.py (3)
StatusSchemaParser(8-339)get_status_schema_for_resource(15-39)generate_status_from_schema(151-164)fake_kubernetes_client/resource_instance.py (1)
get(236-278)
fake_kubernetes_client/resource_field.py (3)
fake_kubernetes_client/dynamic_client.py (1)
get(130-142)fake_kubernetes_client/resource_instance.py (1)
get(236-278)fake_kubernetes_client/resource_manager.py (1)
get(21-65)
🪛 markdownlint-cli2 (0.17.2)
fake_kubernetes_client/README.md
202-202: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
219-219: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: can-be-merged
- GitHub Check: conventional-title
- GitHub Check: tox
- GitHub Check: python-module-install
🔇 Additional comments (9)
class_generator/__cluster_version__.txt (1)
1-1: Version update acknowledged.The version has been updated from v1.32.3 to v1.32.5 as part of the class generator process.
.flake8 (1)
70-70: LGTM - Appropriate flake8 exclusion.Adding
shutilto the exclusion list is correct and aligns with the new usage ofshutil.copy2in the class generator code.README.md (1)
44-46: Excellent documentation addition.The new section clearly introduces the fake Kubernetes client feature and provides appropriate reference to detailed documentation. This will help users discover and understand the testing capabilities.
.pre-commit-config.yaml (1)
45-45: Appropriate exclusion for generated content.Adding the exclusion for
fake_kubernetes_client/__resources-mappings.jsonis correct since this is generated/copied content that shouldn't be scanned for secrets.fake_kubernetes_client/configuration.py (1)
1-23: LGTM!The
FakeConfigurationclass provides a clean mock implementation with appropriate default values for testing scenarios.fake_kubernetes_client/__init__.py (1)
1-30: Well-structured module exports!The refactored imports and exports properly expose the new modular architecture, providing a clean public API for the fake Kubernetes client.
fake_kubernetes_client/README.md (1)
141-199: Excellent documentation for the new resource registration API!The examples and parameter table provide clear guidance for users migrating to the new
register_resources()method. The comprehensive parameter documentation is particularly helpful.fake_kubernetes_client/dynamic_client.py (1)
77-128: Well-documented resource registration method.The
register_resourcesmethod is excellently documented with clear examples and parameter descriptions. The delegation pattern to the registry is appropriate.fake_kubernetes_client/resource_instance.py (1)
448-455: Clean implementation of merge patch logic.The recursive merge patch implementation correctly handles nested dictionaries and follows the JSON merge patch semantics.
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (5)
fake_kubernetes_client/resource_storage.py (1)
129-146: String conversion in field selector matching may cause issues.Converting field values to strings (lines 138, 144) might not work correctly for all data types:
- Booleans: Python's
Truebecomes"True"instead of"true"- None/null values:
Nonebecomes"None"instead of being handled as missing- Numbers: Potential precision issues with floats
Consider implementing type-aware comparison:
def _matches_field_selector(self, resource: dict[str, Any], selector: str) -> bool: """Check if resource matches field selector""" # Handle simple field selectors (field.path=value or field.path==value) parts = selector.split(",") for part in parts: if "==" in part: # Handle double equals field_path, value = part.split("==", 1) field_value = self._get_field_value(resource, field_path.strip()) - if str(field_value) != value.strip(): + if not self._compare_field_value(field_value, value.strip()): return False elif "=" in part: # Handle single equals field_path, value = part.split("=", 1) field_value = self._get_field_value(resource, field_path.strip()) - if str(field_value) != value.strip(): + if not self._compare_field_value(field_value, value.strip()): return False return True + +def _compare_field_value(self, field_value: Any, expected: str) -> bool: + """Compare field value with expected string value""" + if field_value is None: + return expected.lower() in ("", "null", "none") + elif isinstance(field_value, bool): + return str(field_value).lower() == expected.lower() + else: + return str(field_value) == expectedfake_kubernetes_client/status_schema_parser.py (2)
41-52: Complete the reference resolution implementation.The comment indicates this is a placeholder implementation. Without proper reference resolution, the schema parser may not work correctly for resources with
$refin their status definitions.Would you like me to help implement proper
$refresolution that follows JSON Schema references?
256-259: Remove unreachable return statement.Line 258 contains an unreachable return statement after the else block already returns.
Apply this diff to remove the dead code:
else: return 1 - return 1fake_kubernetes_client/resource_instance.py (2)
333-334: Replace assert with proper error handling.Using
assertfor type narrowing is not ideal as assertions can be disabled with Python's-Oflag. Consider explicit checking.- # Type assertion - at this point existing cannot be None - assert existing is not None + # Existing cannot be None at this point due to the check above + if existing is None: + # This should never happen, but handle it gracefully + self._create_not_found_error(name) + return FakeResourceField(data={}) # unreachable, but satisfies type checker
381-382: Replace assert with proper error handling in replace method.Same issue as in the patch method - assertions can be disabled.
- # Type assertion - at this point existing cannot be None - assert existing is not None + # Existing cannot be None at this point due to the check above + if existing is None: + # This should never happen, but handle it gracefully + self._create_not_found_error(name) + return FakeResourceField(data={}) # unreachable, but satisfies type checker
🧹 Nitpick comments (3)
fake_kubernetes_client/resource_storage.py (1)
102-119: Consider documenting label selector limitations.The current implementation only supports simple equality (
key=value), inequality (key!=value), and key existence checks. It doesn't support Kubernetes set-based selectors likekey in (value1,value2),key notin (value1,value2), or the!keysyntax for non-existence.Consider adding a docstring note about supported selector syntax:
def _matches_label_selector(self, labels: dict[str, str], selector: str) -> bool: - """Check if labels match selector (simplified implementation)""" + """Check if labels match selector (simplified implementation). + + Supports: + - Equality: key=value + - Inequality: key!=value + - Existence: key + + Does not support: + - Set-based: key in (value1,value2) + - Non-existence: !key + """fake_kubernetes_client/resource_manager.py (1)
51-58: Enhance version preference logic for better stability ordering.The current logic only checks for "v1" and doesn't handle other stable versions like "v2" or properly order beta/alpha versions.
Consider a more comprehensive version ordering:
if preferred: # Simple logic: prefer stable versions (v1 > v1beta1 > v1alpha1) - for def_ in definitions: - if def_["version"] == "v1": - resource_def = def_ - break - if not resource_def: - resource_def = definitions[0] + # Sort by version stability + def version_priority(def_: dict[str, Any]) -> tuple[int, str]: + version = def_["version"] + if version.startswith("v") and version[1:].isdigit(): + return (0, version) # Stable versions first + elif "beta" in version: + return (1, version) + elif "alpha" in version: + return (2, version) + else: + return (3, version) + + resource_def = min(definitions, key=version_priority)fake_kubernetes_client/resource_instance.py (1)
62-64: Consider adding uniqueness to resource version generation.The current timestamp-based approach could theoretically produce collisions with rapid operations.
Consider adding a counter or random component:
+ def __init__(self, ...): + # ... existing init code ... + self._version_counter = 0 + def _generate_resource_version(self) -> str: """Generate unique resource version""" - return str(int(time.time() * 1000)) + self._version_counter += 1 + return f"{int(time.time() * 1000)}-{self._version_counter}"
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
fake_kubernetes_client/dynamic_client.py(1 hunks)fake_kubernetes_client/exceptions.py(1 hunks)fake_kubernetes_client/kubernetes_client.py(1 hunks)fake_kubernetes_client/resource_field.py(1 hunks)fake_kubernetes_client/resource_instance.py(1 hunks)fake_kubernetes_client/resource_manager.py(1 hunks)fake_kubernetes_client/resource_registry.py(1 hunks)fake_kubernetes_client/resource_storage.py(1 hunks)fake_kubernetes_client/status_schema_parser.py(1 hunks)fake_kubernetes_client/status_templates.py(1 hunks)
✅ Files skipped from review due to trivial changes (2)
- fake_kubernetes_client/resource_registry.py
- fake_kubernetes_client/dynamic_client.py
🚧 Files skipped from review as they are similar to previous changes (4)
- fake_kubernetes_client/exceptions.py
- fake_kubernetes_client/kubernetes_client.py
- fake_kubernetes_client/status_templates.py
- fake_kubernetes_client/resource_field.py
🧰 Additional context used
🧠 Learnings (2)
fake_kubernetes_client/resource_storage.py (1)
Learnt from: tarukumar
PR: RedHatQE/openshift-python-wrapper#2151
File: ocp_resources/serving_runtime.py:130-134
Timestamp: 2024-10-09T13:40:48.030Z
Learning: In the `ServingRuntime` class in `ocp_resources/serving_runtime.py`, the `nodeSelector` key is correctly used in camelCase within the `_spec` dictionary. Avoid flagging this as an issue in future reviews.
fake_kubernetes_client/resource_instance.py (1)
Learnt from: myakove
PR: RedHatQE/openshift-python-wrapper#2209
File: ocp_resources/volume_snapshot_class.py:0-0
Timestamp: 2024-12-03T08:02:11.880Z
Learning: In generated code files like `ocp_resources/volume_snapshot_class.py`, avoid suggesting adding validation checks or modifications, as generated code should not be manually altered.
🧬 Code Graph Analysis (2)
fake_kubernetes_client/status_schema_parser.py (4)
fake_kubernetes_client/resource_field.py (2)
get(85-89)items(103-112)fake_kubernetes_client/dynamic_client.py (1)
get(130-142)fake_kubernetes_client/resource_manager.py (1)
get(21-65)fake_kubernetes_client/resource_instance.py (1)
get(236-278)
fake_kubernetes_client/resource_storage.py (5)
fake_kubernetes_client/dynamic_client.py (2)
resources(28-30)get(130-142)ocp_resources/resource.py (2)
kind(671-672)labels(1158-1165)fake_kubernetes_client/resource_field.py (1)
get(85-89)fake_kubernetes_client/resource_manager.py (1)
get(21-65)fake_kubernetes_client/resource_instance.py (1)
get(236-278)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: can-be-merged
- GitHub Check: can-be-merged
- GitHub Check: can-be-merged
- GitHub Check: conventional-title
- GitHub Check: tox
- GitHub Check: python-module-install
🔇 Additional comments (2)
fake_kubernetes_client/resource_manager.py (1)
36-37: API version construction handles empty groups correctly.The current implementation correctly handles empty group strings for core resources. When
groupis an empty string, it evaluates toFalseand returns just theversion, which is the expected behavior for core Kubernetes resources.fake_kubernetes_client/resource_instance.py (1)
129-136: Good implementation of ProjectRequest semantics.The code correctly implements OpenShift's ProjectRequest behavior where it's an ephemeral resource that triggers Project creation without being persisted itself. This matches real cluster behavior.
|
/verified |
|
/lgtm |
* feat: Add typing and split fake client code * feat: Add typing and split fake client code * feat: Add typing and split fake client code * feat: Add typing and split fake client code * Use Union for type * Use Union for type * improve the client code
FakeClient:
Summary by CodeRabbit
New Features
Bug Fixes
Documentation
Chores
Refactor
Revert