From 41d0a990c24088602f64088d4a4c1ce4bd966635 Mon Sep 17 00:00:00 2001 From: Meni Yakove Date: Wed, 15 May 2024 21:16:43 +0300 Subject: [PATCH 1/7] Add typing to resource.py --- ocp_resources/cluster_pool.py | 2 - ocp_resources/constants.py | 3 +- ocp_resources/resource.py | 240 ++++++++++++++++++---------------- 3 files changed, 132 insertions(+), 113 deletions(-) diff --git a/ocp_resources/cluster_pool.py b/ocp_resources/cluster_pool.py index c7a2e1427e..acbc555ad4 100644 --- a/ocp_resources/cluster_pool.py +++ b/ocp_resources/cluster_pool.py @@ -38,8 +38,6 @@ def __init__( self.size = size def to_dict(self) -> None: - self.res: Dict[str, Any] - super().to_dict() if not self.yaml_file: self.res["spec"] = {} diff --git a/ocp_resources/constants.py b/ocp_resources/constants.py index b08c8d07a1..c1c232e659 100644 --- a/ocp_resources/constants.py +++ b/ocp_resources/constants.py @@ -1,3 +1,4 @@ +from typing import Dict, List from kubernetes.dynamic.exceptions import ( ForbiddenError, InternalServerError, @@ -6,7 +7,7 @@ ) from urllib3.exceptions import MaxRetryError, ProtocolError -DEFAULT_CLUSTER_RETRY_EXCEPTIONS = { +DEFAULT_CLUSTER_RETRY_EXCEPTIONS: Dict[type[Exception], List[str]] = { MaxRetryError: [], ConnectionAbortedError: [], ConnectionResetError: [], diff --git a/ocp_resources/resource.py b/ocp_resources/resource.py index 50a0bdd5b1..7d6fc4d28f 100644 --- a/ocp_resources/resource.py +++ b/ocp_resources/resource.py @@ -1,4 +1,5 @@ from __future__ import annotations +from collections.abc import Callable, Generator import contextlib import copy import json @@ -7,9 +8,10 @@ import sys from io import StringIO from signal import SIGINT, signal +from typing import Optional import kubernetes -from kubernetes.dynamic import DynamicClient +from kubernetes.dynamic import DynamicClient, ResourceInstance import yaml from benedict import benedict from kubernetes.dynamic.exceptions import ( @@ -20,7 +22,7 @@ ) from kubernetes.dynamic.resource import ResourceField from packaging.version import Version -from simple_logger.logger import Any, Dict, get_logger +from simple_logger.logger import Any, Dict, List, get_logger, logging from urllib3.exceptions import MaxRetryError from ocp_resources.constants import ( @@ -51,7 +53,7 @@ def __repr__(self) -> str: return f"Missing required argument/s. Either provide yaml_file or pass {self.argument}" -def _find_supported_resource(dyn_client, api_group, kind): +def _find_supported_resource(dyn_client: DynamicClient, api_group: str, kind: str) -> Optional[ResourceField]: results = dyn_client.resources.search(group=api_group, kind=kind) sorted_results = sorted(results, key=lambda result: KubeAPIVersion(result.api_version), reverse=True) for result in sorted_results: @@ -59,7 +61,7 @@ def _find_supported_resource(dyn_client, api_group, kind): return result -def _get_api_version(dyn_client, api_group, kind): +def _get_api_version(dyn_client: DynamicClient, api_group: str, kind: str) -> str: # Returns api_group/api_version res = _find_supported_resource(dyn_client=dyn_client, api_group=api_group, kind=kind) if not res: @@ -71,7 +73,9 @@ def _get_api_version(dyn_client, api_group, kind): return res.group_version -def get_client(config_file=None, config_dict=None, context=None, **kwargs): +def get_client( + config_file: str | None = None, config_dict: Dict[str, Any] | None = None, context: str | None = None, **kwargs: Any +) -> DynamicClient: """ Get a kubernetes client. @@ -118,7 +122,7 @@ def get_client(config_file=None, config_dict=None, context=None, **kwargs): ) -def sub_resource_level(current_class, owner_class, parent_class): +def sub_resource_level(current_class: Any, owner_class: Any, parent_class: Any) -> Optional[str]: # return the name of the last class in MRO list that is not one of base # classes; otherwise return None for class_iterator in reversed([ @@ -137,12 +141,12 @@ class KubeAPIVersion(Version): component_re = re.compile(r"(\d+ | [a-z]+)", re.VERBOSE) - def __init__(self, vstring=None): + def __init__(self, vstring: str): self.vstring = vstring - self.version = None + self.version: List[str | Any] = [] super().__init__(version=vstring) - def parse(self, vstring): + def parse(self, vstring: str): components = [comp for comp in self.component_re.split(vstring) if comp] for idx, obj in enumerate(components): with contextlib.suppress(ValueError): @@ -183,10 +187,10 @@ def _cmp(self, other): class ClassProperty: - def __init__(self, func): + def __init__(self, func: Callable) -> None: self.func = func - def __get__(self, obj, owner): + def __get__(self, obj: Any, owner: Any) -> Any: return self.func(owner) @@ -201,10 +205,10 @@ class Resource: Base class for API resources """ - api_group = None - api_version = None - singular_name = None - timeout_seconds = TIMEOUT_1MINUTE + api_group: str = "" + api_version: str = "" + singular_name: str = "" + timeout_seconds: int = TIMEOUT_1MINUTE class Status: SUCCEEDED = "Succeeded" @@ -351,6 +355,7 @@ def __init__( node_selector: str = "", node_selector_labels: Dict[str, str] | None = None, config_file: str = "", + config_dict: Dict[str, Any] | None = None, context: str = "", label: Dict[str, str] | None = None, timeout_seconds: int = TIMEOUT_1MINUTE, @@ -381,7 +386,6 @@ def __init__( """ self.name = name - self.client = client self.teardown = teardown self.timeout = timeout self.privileged_client = privileged_client @@ -391,9 +395,11 @@ def __init__( self.node_selector = node_selector self.node_selector_labels = node_selector_labels self.config_file = config_file + self.config_dict = config_dict or {} self.context = context self.label = label self.timeout_seconds = timeout_seconds + self.client: DynamicClient = client or get_client(config_file=self.config_file, context=self.context) self.api_group: str = api_group or self.api_group self.hash_log_data = hash_log_data @@ -407,13 +413,13 @@ def __init__( self.resource_dict: Dict[str, Any] = {} # Filled in case yaml_file is not None self.node_selector_spec = self._prepare_node_selector_spec() self.res: Dict[str, Any] = {} - self.yaml_file_contents: Dict[Any, Any] | None = None + self.yaml_file_contents: str = "" self.initial_resource_version: str = "" self.logger = self._set_logger() # self._set_client_and_api_version() must be last init line self._set_client_and_api_version() - def _set_logger(self): + def _set_logger(self) -> logging.Logger: log_level = os.environ.get("OPENSHIFT_PYTHON_WRAPPER_LOG_LEVEL", "INFO") log_file = os.environ.get("OPENSHIFT_PYTHON_WRAPPER_LOG_FILE", "") return get_logger( @@ -431,16 +437,17 @@ def _prepare_node_selector_spec(self) -> Dict[str, str]: return {} @ClassProperty - def kind(cls): # noqa: N805 + def kind(cls) -> Optional[str]: # noqa: N805 return sub_resource_level(cls, NamespacedResource, Resource) - def _base_body(self): + def _base_body(self) -> None: """ Generate resource dict from yaml if self.yaml_file else return base resource dict. Returns: dict: Resource dict. """ + self.res: Dict[str, Any] = {} if self.yaml_file: if not self.yaml_file_contents: if isinstance(self.yaml_file, StringIO): @@ -461,25 +468,25 @@ def _base_body(self): if self.label: self.res.setdefault("metadata", {}).setdefault("labels", {}).update(self.label) - def to_dict(self): + def to_dict(self) -> None: """ Generate intended dict representation of the resource. """ self._base_body() - def __enter__(self): + def __enter__(self) -> "Resource": signal(SIGINT, self._sigint_handler) return self.deploy() - def __exit__(self, exception_type, exception_value, traceback): + def __exit__(self, exception_type: Any, exception_value: Any, traceback: Any) -> None: if self.teardown: self.clean_up() - def _sigint_handler(self, signal_received, frame): + def _sigint_handler(self, signal_received: int, frame: Any) -> None: self.__exit__(exception_type=None, exception_value=None, traceback=None) sys.exit(signal_received) - def deploy(self, wait=False): + def deploy(self, wait: bool = False) -> "Resource": """ For debug, export REUSE_IF_RESOURCE_EXISTS to skip resource create. Spaces are important in the export dict @@ -513,7 +520,7 @@ def deploy(self, wait=False): self.create(wait=wait) return self - def clean_up(self): + def clean_up(self) -> None: """ For debug, export SKIP_RESOURCE_TEARDOWN to skip resource teardown. Spaces are important in the export dict @@ -540,14 +547,16 @@ def clean_up(self): check_exists=False, ): self.logger.warning( - f"Skip resource {self.kind} {self.name} teardown. Got" f" {_export_str}={skip_resource_teardown}" + f"Skip resource {self.kind} {self.name} teardown. Got {_export_str}={skip_resource_teardown}" ) return self.delete(wait=True, timeout=self.delete_timeout) @classmethod - def _prepare_resources(cls, dyn_client, singular_name, *args, **kwargs): + def _prepare_resources( + cls, dyn_client: DynamicClient, singular_name: str, *args: Any, **kwargs: Any + ) -> ResourceInstance: if not cls.api_version: cls.api_version = _get_api_version(dyn_client=dyn_client, api_group=cls.api_group, kind=cls.kind) @@ -558,21 +567,21 @@ def _prepare_resources(cls, dyn_client, singular_name, *args, **kwargs): **get_kwargs, ).get(*args, **kwargs, timeout_seconds=cls.timeout_seconds) - def _prepare_singular_name_kwargs(self, **kwargs): + def _prepare_singular_name_kwargs(self, **kwargs: Any) -> Dict[str, Any]: kwargs = kwargs if kwargs else {} if self.singular_name: kwargs["singular_name"] = self.singular_name return kwargs - def _set_client_and_api_version(self): + def _set_client_and_api_version(self) -> None: if not self.client: self.client = get_client(config_file=self.config_file, context=self.context) if not self.api_version: self.api_version = _get_api_version(dyn_client=self.client, api_group=self.api_group, kind=self.kind) - def full_api(self, **kwargs): + def full_api(self, **kwargs: Any) -> ResourceInstance: """ Get resource API @@ -598,10 +607,10 @@ def full_api(self, **kwargs): return self.client.resources.get(api_version=self.api_version, kind=self.kind, **kwargs) @property - def api(self): + def api(self) -> ResourceInstance: return self.full_api() - def wait(self, timeout=TIMEOUT_4MINUTES, sleep=1): + def wait(self, timeout: int = TIMEOUT_4MINUTES, sleep: int = 1) -> None: """ Wait for resource @@ -627,7 +636,7 @@ def wait(self, timeout=TIMEOUT_4MINUTES, sleep=1): if sample: return - def wait_deleted(self, timeout=TIMEOUT_4MINUTES): + def wait_deleted(self, timeout: int = TIMEOUT_4MINUTES) -> None: """ Wait until resource is deleted @@ -641,7 +650,7 @@ def wait_deleted(self, timeout=TIMEOUT_4MINUTES): return self.client_wait_deleted(timeout=timeout) @property - def exists(self): + def exists(self) -> Optional[ResourceInstance]: """ Whether self exists on the server """ @@ -651,10 +660,10 @@ def exists(self): return None @property - def _kube_v1_api(self): + def _kube_v1_api(self) -> kubernetes.client.CoreV1Api: return kubernetes.client.CoreV1Api(api_client=self.client.client) - def client_wait_deleted(self, timeout): + def client_wait_deleted(self, timeout: int) -> None: """ client-side Wait until resource is deleted @@ -669,7 +678,9 @@ def client_wait_deleted(self, timeout): if not sample: return - def wait_for_status(self, status, timeout=TIMEOUT_4MINUTES, stop_status=None, sleep=1): + def wait_for_status( + self, status: str, timeout: int = TIMEOUT_4MINUTES, stop_status: str | None = None, sleep: int = 1 + ) -> None: """ Wait for resource to be in status @@ -717,7 +728,7 @@ def wait_for_status(self, status, timeout=TIMEOUT_4MINUTES, stop_status=None, sl self.logger.error(f"Status of {self.kind} {self.name} is {current_status}") raise - def create(self, wait=False): + def create(self, wait: bool = False) -> Optional[ResourceInstance]: """ Create resource. @@ -749,25 +760,26 @@ def create(self, wait=False): return self.wait() return resource_ - def delete(self, wait=False, timeout=TIMEOUT_4MINUTES, body=None): + def delete(self, wait: bool = False, timeout: int = TIMEOUT_4MINUTES, body: Dict[str, Any] | None = None) -> bool: self.logger.info(f"Delete {self.kind} {self.name}") + if self.exists: hashed_data = self.hash_resource_dict(resource_dict=self.instance.to_dict()) - self.logger.info(f"Deleting {hashed_data}") self.logger.debug(f"\n{yaml.dump(hashed_data)}") try: - res = self.api.delete(name=self.name, namespace=self.namespace, body=body) + self.api.delete(name=self.name, namespace=self.namespace, body=body) + if wait: + self.client_wait_deleted(timeout=timeout) + except NotFoundError: return False - if wait and res: - return self.wait_deleted(timeout=timeout) - return res + return True @property - def status(self): + def status(self) -> str: """ Get resource status @@ -779,7 +791,7 @@ def status(self): self.logger.info(f"Get {self.kind} {self.name} status") return self.instance.status.phase - def update(self, resource_dict): + def update(self, resource_dict: Dict[str, Any]) -> None: """ Update resource with resource dict @@ -795,7 +807,7 @@ def update(self, resource_dict): content_type="application/merge-patch+json", ) - def update_replace(self, resource_dict): + def update_replace(self, resource_dict: Dict[str, Any]) -> None: """ Replace resource metadata. Use this to remove existing field. (update() will only update existing fields) @@ -806,7 +818,9 @@ def update_replace(self, resource_dict): self.api.replace(body=resource_dict, name=self.name, namespace=self.namespace) @staticmethod - def retry_cluster_exceptions(func, exceptions_dict=DEFAULT_CLUSTER_RETRY_EXCEPTIONS, **kwargs): + def retry_cluster_exceptions( + func, exceptions_dict: Dict[type[Exception], List[str]] = DEFAULT_CLUSTER_RETRY_EXCEPTIONS, **kwargs: Any + ) -> Any: try: sampler = TimeoutSampler( wait_timeout=TIMEOUT_10SEC, @@ -828,14 +842,14 @@ def retry_cluster_exceptions(func, exceptions_dict=DEFAULT_CLUSTER_RETRY_EXCEPTI @classmethod def get( cls, - dyn_client=None, - config_file=None, - context=None, - singular_name=None, - exceptions_dict=DEFAULT_CLUSTER_RETRY_EXCEPTIONS, - *args, - **kwargs, - ): + config_file: str = "", + context: str = "", + singular_name: str = "", + exceptions_dict: Dict[type[Exception], List[str]] = DEFAULT_CLUSTER_RETRY_EXCEPTIONS, + dyn_client: DynamicClient | None = None, + *args: Any, + **kwargs: Any, + ) -> Generator[ResourceField, None, None]: """ Get resources @@ -852,7 +866,7 @@ def get( if not dyn_client: dyn_client = get_client(config_file=config_file, context=context) - def _get(): + def _get() -> Generator["Resource", None, None]: _resources = cls._prepare_resources(dyn_client=dyn_client, singular_name=singular_name, *args, **kwargs) try: for resource_field in _resources.items: @@ -863,7 +877,7 @@ def _get(): return Resource.retry_cluster_exceptions(func=_get, exceptions_dict=exceptions_dict) @property - def instance(self): + def instance(self) -> ResourceInstance: """ Get resource instance @@ -871,22 +885,22 @@ def instance(self): openshift.dynamic.client.ResourceInstance """ - def _instance(): + def _instance() -> Optional[ResourceInstance]: return self.api.get(name=self.name) return self.retry_cluster_exceptions(func=_instance) @property - def labels(self): + def labels(self) -> ResourceField: """ Method to get labels for this resource Returns: openshift.dynamic.resource.ResourceField: Representation of labels """ - return self.instance["metadata"]["labels"] + return self.instance.get("metadata", {})["labels"] - def watcher(self, timeout, resource_version=None): + def watcher(self, timeout: int, resource_version: str = "") -> Generator[Dict[str, Any], None, None]: """ Get resource for a given timeout. @@ -908,7 +922,7 @@ def watcher(self, timeout, resource_version=None): resource_version=resource_version or self.initial_resource_version, ) - def wait_for_condition(self, condition, status, timeout=300): + def wait_for_condition(self, condition: str, status: str, timeout: int = 300) -> None: """ Wait for Resource condition to be in desire status. @@ -941,7 +955,7 @@ def wait_for_condition(self, condition, status, timeout=300): if cond["type"] == condition and cond["status"] == status: return - def api_request(self, method, action, url, **params): + def api_request(self, method: str, action: str, url: str, **params: Any) -> Dict[str, Any]: """ Handle API requests to resource. @@ -954,11 +968,11 @@ def api_request(self, method, action, url, **params): data(dict): response data """ - client = self.privileged_client or self.client + client: DynamicClient = self.privileged_client or self.client response = client.client.request( method=method, url=f"{url}/{action}", - headers=self.client.configuration.api_key, + headers=client.client.configuration.api_key, **params, ) @@ -967,7 +981,7 @@ def api_request(self, method, action, url, **params): except json.decoder.JSONDecodeError: return response.data - def wait_for_conditions(self): + def wait_for_conditions(self) -> None: timeout_watcher = TimeoutWatch(timeout=30) for sample in TimeoutSampler( wait_timeout=30, @@ -988,11 +1002,11 @@ def wait_for_conditions(self): def events( self, - name=None, - label_selector=None, - field_selector=None, - resource_version=None, - timeout=None, + name: str = "", + label_selector: str = "", + field_selector: str = "", + resource_version: str = "", + timeout: int = TIMEOUT_4MINUTES, ): """ get - retrieves K8s events. @@ -1032,7 +1046,9 @@ def events( ) @staticmethod - def get_all_cluster_resources(config_file=None, config_dict=None, context=None, *args, **kwargs): + def get_all_cluster_resources( + config_file: str = "", context: str = "", config_dict: Dict[str, Any] | None = None, *args: Any, **kwargs: Any + ) -> Generator[ResourceField, None, None]: """ Get all cluster resources @@ -1060,7 +1076,7 @@ def get_all_cluster_resources(config_file=None, config_dict=None, context=None, except (NotFoundError, TypeError, MethodNotAllowedError): continue - def to_yaml(self): + def to_yaml(self) -> str: """ Get resource as YAML representation. @@ -1074,7 +1090,7 @@ def to_yaml(self): return resource_yaml @property - def keys_to_hash(self): + def keys_to_hash(self) -> List[str]: """ Resource attributes list to hash in the logs. @@ -1086,7 +1102,7 @@ def keys_to_hash(self): """ return [] - def hash_resource_dict(self, resource_dict): + def hash_resource_dict(self, resource_dict: Dict[str, Any]) -> Dict[str, Any]: if self.keys_to_hash and self.hash_log_data: resource_dict = copy.deepcopy(resource_dict) resource_dict = benedict(resource_dict, keypath_separator="..") @@ -1099,7 +1115,7 @@ def hash_resource_dict(self, resource_dict): return resource_dict - def get_condition_message(self, condition_type, condition_status=None): + def get_condition_message(self, condition_type: str, condition_status: str = "") -> str: """ Get condition message by condition type and condition status @@ -1134,15 +1150,15 @@ class NamespacedResource(Resource): def __init__( self, - name=None, - namespace=None, - client=None, - teardown=True, - timeout=TIMEOUT_4MINUTES, - privileged_client=None, - yaml_file=None, - delete_timeout=TIMEOUT_4MINUTES, - **kwargs, + name: str = "", + namespace: str = "", + teardown: bool = True, + timeout: int = TIMEOUT_4MINUTES, + yaml_file: str = "", + delete_timeout: int = TIMEOUT_4MINUTES, + client: DynamicClient | None = None, + privileged_client: DynamicClient | None = None, + **kwargs: Any, ): super().__init__( name=name, @@ -1161,14 +1177,14 @@ def __init__( @classmethod def get( cls, - dyn_client=None, - config_file=None, - context=None, - singular_name=None, - raw=False, - *args, - **kwargs, - ): + config_file: str = "", + context: str = "", + singular_name: str = "", + raw: bool = False, + dyn_client: DynamicClient | None = None, + *args: Any, + **kwargs: Any, + ) -> Generator[ResourceField, None, None]: """ Get resources @@ -1208,7 +1224,7 @@ def get( ) @property - def instance(self): + def instance(self) -> ResourceInstance: """ Get resource instance @@ -1221,7 +1237,7 @@ def _instance(): return self.retry_cluster_exceptions(func=_instance) - def _base_body(self): + def _base_body(self) -> None: if not self.res: super(NamespacedResource, self)._base_body() @@ -1234,12 +1250,14 @@ def _base_body(self): if not self.yaml_file: self.res["metadata"]["namespace"] = self.namespace - def to_dict(self): + def to_dict(self) -> None: self._base_body() class ResourceEditor: - def __init__(self, patches, action="update", user_backups=None): + def __init__( + self, patches: Dict[Any, Any], action: str = "update", user_backups: Dict[Any, Any] | None = None + ) -> None: """ Args: patches (dict): {: } @@ -1269,17 +1287,17 @@ def __init__(self, patches, action="update", user_backups=None): self._backups = {} @property - def backups(self): + def backups(self) -> Dict[Any, Any]: """Returns a dict {: } The backup dict kept for each resource edited""" return self._backups @property - def patches(self): + def patches(self) -> Dict[Any, Any]: """Returns the patches dict provided in the constructor""" return self._patches - def update(self, backup_resources=False): + def update(self, backup_resources: bool = False) -> None: """Prepares backup dicts (where necessary) and applies patches""" # prepare update dicts and backups resource_to_patch = [] @@ -1328,34 +1346,36 @@ def update(self, backup_resources=False): # apply changes self._apply_patches_sampler(patches=patches_to_apply, action_text="Updating", action=self.action) - def restore(self): + def restore(self) -> None: self._apply_patches_sampler(patches=self._backups, action_text="Restoring", action=self.action) - def __enter__(self): + def __enter__(self) -> "ResourceEditor": self.update(backup_resources=True) return self - def __exit__(self, exc_type, exc_val, exc_tb): + def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: # restore backups self.restore() @staticmethod - def _dictify_resourcefield(res): + def _dictify_resourcefield(res: Any) -> Any: """Recursively turns any ResourceField objects into dicts to avoid issues caused by appending lists, etc.""" if isinstance(res, ResourceField): return ResourceEditor._dictify_resourcefield(res=dict(res.items())) + elif isinstance(res, dict): return { ResourceEditor._dictify_resourcefield(res=key): ResourceEditor._dictify_resourcefield(res=value) for key, value in res.items() } + elif isinstance(res, list): return [ResourceEditor._dictify_resourcefield(res=x) for x in res] return res @staticmethod - def _create_backup(original, patch): + def _create_backup(original: Dict[Any, Any], patch: Dict[Any, Any]) -> Dict[Any, Any]: """ Args: original (dict*): source of values to back up if necessary @@ -1396,7 +1416,7 @@ def _create_backup(original, patch): return None @staticmethod - def _apply_patches(patches, action_text, action): + def _apply_patches(patches: Dict[Any, Any], action_text: str, action: str) -> None: """ Updates provided Resource objects with provided yaml patches @@ -1434,8 +1454,8 @@ def _apply_patches(patches, action_text, action): resource.update_replace(resource_dict=patch) # replace the resource metadata - def _apply_patches_sampler(self, patches, action_text, action): - exceptions_dict = {ConflictError: []} + def _apply_patches_sampler(self, patches: Dict[Any, Any], action_text: str, action: str) -> ResourceInstance: + exceptions_dict: Dict[type[Exception], List[str]] = {ConflictError: []} exceptions_dict.update(DEFAULT_CLUSTER_RETRY_EXCEPTIONS) return Resource.retry_cluster_exceptions( func=self._apply_patches, From 689b58f279934128a9500c10ddcf05ada721df80 Mon Sep 17 00:00:00 2001 From: Meni Yakove Date: Thu, 16 May 2024 16:40:28 +0300 Subject: [PATCH 2/7] Add typing in resource.py --- .pre-commit-config.yaml | 12 ++++++ ocp_resources/constants.py | 14 +++---- ocp_resources/resource.py | 75 +++++++++++++++++++++++--------------- pyproject.toml | 10 +++++ 4 files changed, 74 insertions(+), 37 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5491ba0d16..fa5f084d95 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -41,3 +41,15 @@ repos: rev: 37.358.0 hooks: - id: renovate-config-validator + + - repo: https://github.com/gitleaks/gitleaks + rev: v8.18.2 + hooks: + - id: gitleaks + + - repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.9.0 + hooks: + - id: mypy + exclude: ^(tests/|examples/|docs/) + additional_dependencies: [types-all] diff --git a/ocp_resources/constants.py b/ocp_resources/constants.py index c1c232e659..f3c539150c 100644 --- a/ocp_resources/constants.py +++ b/ocp_resources/constants.py @@ -20,11 +20,11 @@ ServerTimeoutError: [], ForbiddenError: ["context deadline exceeded"], } -PROTOCOL_ERROR_EXCEPTION_DICT = {ProtocolError: []} -NOT_FOUND_ERROR_EXCEPTION_DICT = {NotFoundError: []} +PROTOCOL_ERROR_EXCEPTION_DICT: Dict[type[Exception], List[str]] = {ProtocolError: []} +NOT_FOUND_ERROR_EXCEPTION_DICT: Dict[type[Exception], List[str]] = {NotFoundError: []} -TIMEOUT_10SEC = 10 -TIMEOUT_1MINUTE = 60 -TIMEOUT_2MINUTES = 2 * 60 -TIMEOUT_4MINUTES = 4 * 60 -TIMEOUT_10MINUTES = 10 * 60 +TIMEOUT_10SEC: int = 10 +TIMEOUT_1MINUTE: int = 60 +TIMEOUT_2MINUTES: int = 2 * 60 +TIMEOUT_4MINUTES: int = 4 * 60 +TIMEOUT_10MINUTES: int = 10 * 60 diff --git a/ocp_resources/resource.py b/ocp_resources/resource.py index 7d6fc4d28f..4824edcf3c 100644 --- a/ocp_resources/resource.py +++ b/ocp_resources/resource.py @@ -59,6 +59,7 @@ def _find_supported_resource(dyn_client: DynamicClient, api_group: str, kind: st for result in sorted_results: if KubeAPIVersion(result.api_version) <= KubeAPIVersion(MAX_SUPPORTED_API_VERSION): return result + return None def _get_api_version(dyn_client: DynamicClient, api_group: str, kind: str) -> str: @@ -132,6 +133,8 @@ def sub_resource_level(current_class: Any, owner_class: Any, parent_class: Any) ]): return class_iterator.__name__ + return None + class KubeAPIVersion(Version): """ @@ -447,7 +450,6 @@ def _base_body(self) -> None: Returns: dict: Resource dict. """ - self.res: Dict[str, Any] = {} if self.yaml_file: if not self.yaml_file_contents: if isinstance(self.yaml_file, StringIO): @@ -478,12 +480,12 @@ def __enter__(self) -> "Resource": signal(SIGINT, self._sigint_handler) return self.deploy() - def __exit__(self, exception_type: Any, exception_value: Any, traceback: Any) -> None: + def __exit__(self) -> None: if self.teardown: self.clean_up() def _sigint_handler(self, signal_received: int, frame: Any) -> None: - self.__exit__(exception_type=None, exception_value=None, traceback=None) + self.__exit__() sys.exit(signal_received) def deploy(self, wait: bool = False) -> "Resource": @@ -757,7 +759,7 @@ def create(self, wait: bool = False) -> Optional[ResourceInstance]: self.initial_resource_version = self.instance.metadata.resourceVersion if wait and resource_: - return self.wait() + self.wait() return resource_ def delete(self, wait: bool = False, timeout: int = TIMEOUT_4MINUTES, body: Dict[str, Any] | None = None) -> bool: @@ -846,10 +848,11 @@ def get( context: str = "", singular_name: str = "", exceptions_dict: Dict[type[Exception], List[str]] = DEFAULT_CLUSTER_RETRY_EXCEPTIONS, + raw: bool = False, dyn_client: DynamicClient | None = None, *args: Any, **kwargs: Any, - ) -> Generator[ResourceField, None, None]: + ) -> Generator["Resource|ResourceInstance", None, None]: """ Get resources @@ -858,6 +861,7 @@ def get( config_file (str): Path to config file for connecting to remote cluster. context (str): Context name for connecting to remote cluster. singular_name (str): Resource kind (in lowercase), in use where we have multiple matches for resource. + raw (bool): If True return raw object. exceptions_dict (dict): Exceptions dict for TimeoutSampler Returns: @@ -866,13 +870,20 @@ def get( if not dyn_client: dyn_client = get_client(config_file=config_file, context=context) - def _get() -> Generator["Resource", None, None]: - _resources = cls._prepare_resources(dyn_client=dyn_client, singular_name=singular_name, *args, **kwargs) + def _get() -> Generator["Resource|ResourceInstance", None, None]: + _resources = cls._prepare_resources(dyn_client=dyn_client, singular_name=singular_name, *args, **kwargs) # type: ignore[misc] try: for resource_field in _resources.items: - yield cls(client=dyn_client, name=resource_field.metadata.name) + if raw: + yield _resources + else: + yield cls(client=dyn_client, name=resource_field.metadata.name) + except TypeError: - yield cls(client=dyn_client, name=_resources.metadata.name) + if raw: + yield _resources + else: + yield cls(client=dyn_client, name=_resources.metadata.name) return Resource.retry_cluster_exceptions(func=_get, exceptions_dict=exceptions_dict) @@ -1102,7 +1113,7 @@ def keys_to_hash(self) -> List[str]: """ return [] - def hash_resource_dict(self, resource_dict: Dict[str, Any]) -> Dict[str, Any]: + def hash_resource_dict(self, resource_dict: Dict[Any, Any]) -> Dict[Any, Any]: if self.keys_to_hash and self.hash_log_data: resource_dict = copy.deepcopy(resource_dict) resource_dict = benedict(resource_dict, keypath_separator="..") @@ -1180,11 +1191,12 @@ def get( config_file: str = "", context: str = "", singular_name: str = "", + exceptions_dict: Dict[type[Exception], List[str]] = DEFAULT_CLUSTER_RETRY_EXCEPTIONS, raw: bool = False, dyn_client: DynamicClient | None = None, *args: Any, **kwargs: Any, - ) -> Generator[ResourceField, None, None]: + ) -> Generator["NamespacedResource|ResourceInstance", None, None]: """ Get resources @@ -1194,7 +1206,7 @@ def get( context (str): Context name for connecting to remote cluster. singular_name (str): Resource kind (in lowercase), in use where we have multiple matches for resource. raw (bool): If True return raw object. - + exceptions_dict (dict): Exceptions dict for TimeoutSampler Returns: generator: Generator of Resources of cls.kind @@ -1202,26 +1214,29 @@ def get( if not dyn_client: dyn_client = get_client(config_file=config_file, context=context) - _resources = cls._prepare_resources(dyn_client=dyn_client, singular_name=singular_name, *args, **kwargs) - try: - for resource_field in _resources.items: + def _get() -> Generator["NamespacedResource|ResourceInstance", None, None]: + _resources = cls._prepare_resources(dyn_client=dyn_client, singular_name=singular_name, *args, **kwargs) # type: ignore[misc] + try: + for resource_field in _resources.items: + if raw: + yield resource_field + else: + yield cls( + client=dyn_client, + name=resource_field.metadata.name, + namespace=resource_field.metadata.namespace, + ) + except TypeError: if raw: - yield resource_field + yield _resources else: yield cls( client=dyn_client, - name=resource_field.metadata.name, - namespace=resource_field.metadata.namespace, + name=_resources.metadata.name, + namespace=_resources.metadata.namespace, ) - except TypeError: - if raw: - yield _resources - else: - yield cls( - client=dyn_client, - name=_resources.metadata.name, - namespace=_resources.metadata.namespace, - ) + + return Resource.retry_cluster_exceptions(func=_get, exceptions_dict=exceptions_dict) @property def instance(self) -> ResourceInstance: @@ -1232,7 +1247,7 @@ def instance(self) -> ResourceInstance: openshift.dynamic.client.ResourceInstance """ - def _instance(): + def _instance() -> ResourceInstance: return self.api.get(name=self.name, namespace=self.namespace) return self.retry_cluster_exceptions(func=_instance) @@ -1284,7 +1299,7 @@ def __init__( self._patches = self._dictify_resourcefield(res=patches) self.action = action self.user_backups = user_backups - self._backups = {} + self._backups: Dict[Any, Any] = {} @property def backups(self) -> Dict[Any, Any]: @@ -1394,7 +1409,7 @@ def _create_backup(original: Dict[Any, Any], patch: Dict[Any, Any]) -> Dict[Any, # when both are dicts, get the diff (recursively if need be) if isinstance(original, dict) and isinstance(patch, dict): - diff_dict = {} + diff_dict: Dict[Any, Any] = {} for key, value in patch.items(): if key not in original: diff_dict[key] = None diff --git a/pyproject.toml b/pyproject.toml index 0c4d406e80..c9fe3161a0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,6 +7,16 @@ output-format = "grouped" [tool.ruff.format] exclude = [".git", ".venv", ".mypy_cache", ".tox", "__pycache__"] +[tool.mypy] +# TODO: enable all once we have typing in all the resources. +# check_untyped_defs = true +# disallow_any_generics = true +# disallow_incomplete_defs = true +# disallow_untyped_defs = true +no_implicit_optional = true +show_error_codes = true +warn_unused_ignores = true + [tool.poetry] name = "openshift-python-wrapper" version = "0.0.0" From b7e17a4c97f527a7cf042770f9ec2dfefa2d4a1c Mon Sep 17 00:00:00 2001 From: Meni Yakove Date: Thu, 16 May 2024 16:49:10 +0300 Subject: [PATCH 3/7] Add typing in resource.py --- ocp_resources/resource.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/ocp_resources/resource.py b/ocp_resources/resource.py index 4824edcf3c..fba0885317 100644 --- a/ocp_resources/resource.py +++ b/ocp_resources/resource.py @@ -8,6 +8,7 @@ import sys from io import StringIO from signal import SIGINT, signal +from types import TracebackType from typing import Optional import kubernetes @@ -480,7 +481,12 @@ def __enter__(self) -> "Resource": signal(SIGINT, self._sigint_handler) return self.deploy() - def __exit__(self) -> None: + def __exit__( + self, + exc_type: type[BaseException] | None = None, + exc_val: BaseException | None = None, + exc_tb: TracebackType | None = None, + ) -> None: if self.teardown: self.clean_up() @@ -1368,7 +1374,9 @@ def __enter__(self) -> "ResourceEditor": self.update(backup_resources=True) return self - def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: + def __exit__( + self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None + ) -> None: # restore backups self.restore() From 6aef9053cf0b5b2f1acbdb657a22da4a56e2b915 Mon Sep 17 00:00:00 2001 From: Meni Yakove Date: Thu, 16 May 2024 17:16:46 +0300 Subject: [PATCH 4/7] to_dict, added return type --- CONTRIBUTING.md | 41 ++++++++++++------- ocp_resources/backup.py | 2 +- ocp_resources/catalog_source.py | 2 +- ocp_resources/cluster_role.py | 2 +- ocp_resources/cluster_role_binding.py | 2 +- ocp_resources/configmap.py | 2 +- ocp_resources/controller_revision.py | 2 +- ocp_resources/cron_job.py | 2 +- ocp_resources/csi_storage_capacity.py | 2 +- ocp_resources/data_import_cron.py | 2 +- ocp_resources/data_source.py | 2 +- ocp_resources/datavolume.py | 2 +- ocp_resources/endpoint_slice.py | 2 +- ocp_resources/endpoints.py | 4 +- ocp_resources/group.py | 2 +- ocp_resources/hook.py | 2 +- ocp_resources/host.py | 2 +- ocp_resources/hostpath_provisioner.py | 2 +- ocp_resources/hyperconverged.py | 2 +- ocp_resources/image_content_source_policy.py | 2 +- ocp_resources/image_digest_mirror_set.py | 2 +- ocp_resources/image_stream.py | 2 +- ocp_resources/ip_address_pool.py | 2 +- ocp_resources/job.py | 2 +- ocp_resources/kube_descheduler.py | 2 +- ocp_resources/l2_advertisement.py | 2 +- ocp_resources/lease.py | 2 +- ocp_resources/limit_range.py | 2 +- ocp_resources/machine_config_pool.py | 2 +- ocp_resources/machine_health_check.py | 2 +- ocp_resources/machine_set.py | 2 +- ocp_resources/metallb.py | 2 +- ocp_resources/migration.py | 2 +- ocp_resources/migration_policy.py | 2 +- ocp_resources/multi_cluster_observability.py | 2 +- ocp_resources/multi_network_policy.py | 2 +- .../network_attachment_definition.py | 10 ++--- ocp_resources/network_map.py | 2 +- ocp_resources/node_health_check.py | 2 +- ocp_resources/node_maintenance.py | 2 +- .../node_network_configuration_policy.py | 2 +- ocp_resources/node_network_state.py | 2 +- ocp_resources/ocs_initialization.py | 2 +- ocp_resources/operator_group.py | 2 +- ocp_resources/operator_source.py | 2 +- ocp_resources/performance_profile.py | 2 +- ocp_resources/persistent_volume_claim.py | 2 +- ocp_resources/pipeline.py | 2 +- ocp_resources/pipelineruns.py | 2 +- ocp_resources/plan.py | 2 +- ocp_resources/pod_disruption_budget.py | 2 +- ocp_resources/priority_class.py | 2 +- ocp_resources/provider.py | 2 +- ocp_resources/resource_quota.py | 2 +- ocp_resources/restore.py | 2 +- ocp_resources/role.py | 2 +- ocp_resources/role_binding.py | 2 +- ocp_resources/route.py | 2 +- ocp_resources/sealed_secret.py | 2 +- ocp_resources/secret.py | 2 +- ocp_resources/service_account.py | 2 +- ocp_resources/sriov_network.py | 2 +- ocp_resources/sriov_network_node_policy.py | 2 +- ocp_resources/storage_class.py | 2 +- ocp_resources/storage_map.py | 2 +- ocp_resources/subscription.py | 2 +- ocp_resources/task_run.py | 2 +- ocp_resources/upload_token_request.py | 2 +- ocp_resources/virtual_machine.py | 2 +- ocp_resources/virtual_machine_clone.py | 2 +- ocp_resources/virtual_machine_export.py | 2 +- ocp_resources/virtual_machine_import.py | 4 +- .../virtual_machine_instance_migration.py | 2 +- ...irtual_machine_migration_resource_quota.py | 2 +- ocp_resources/virtual_machine_restore.py | 2 +- ocp_resources/virtual_machine_snapshot.py | 2 +- 76 files changed, 107 insertions(+), 96 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 17345521a6..067ff48684 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,40 +10,48 @@ To get an overview of the project, read the [README](README.md). ## Issues ### Create a new issue + If you find a problem with the code, [search if an issue already exists](https://github.com/RedHatQE/openshift-python-wrapper/issues). If you open a pull request to fix the problem, an issue will ba automatically created. If a related issue doesn't exist, you can open a new issue using a relevant [issue form](https://github.com/RedHatQE/openshift-python-wrapper/issues/new/choose). ## Pull requests + To contribute code to the project: + - Fork the project and work on your forked repository -- Before submitting a new pull request, make sure you have `pre-commit` installed +- Before submitting a new pull request, make sure you have `pre-commit` installed + ```bash pre-commit install ``` + - When submitting a pull request, make sure to fill all the required, relevant fields for your PR. -Make sure the title is descriptive and short. + Make sure the title is descriptive and short. - If the fix is needed in a released version, once your pull request is merged, cherry-pick it to the relevant branch(s). -Add `/cherry-pick ` to the PR comment. + Add `/cherry-pick ` to the PR comment. ## Adding a new module (resource) + To support working with a new (or missing) resource: + - Add a new file under `ocp_resources`, names as the resource. -If the resource name is composed of multiple words, separate them with an underscore. -For example: `ImageStream` filename is `image_stream.py` + If the resource name is composed of multiple words, separate them with an underscore. + For example: `ImageStream` filename is `image_stream.py` - Create a class named as the resource kind. -For example: `ImageStream` -> `class ImageStream` + For example: `ImageStream` -> `class ImageStream` - Inherit from the relevant class. -If the resource is cluster-scoped, inherit from `Resource` else from `NamespacedResource`. -For example: `class ImageStream(NamespacedResource):` + If the resource is cluster-scoped, inherit from `Resource` else from `NamespacedResource`. + For example: `class ImageStream(NamespacedResource):` - Add the resource's apiGroup (`apiVersion` prefix) as `api_group`. -For example: `image.openshift.io` + For example: `image.openshift.io` - Add a link to the resource's API reference. -For example: [ImageStream API reference](https://docs.openshift.com/container-platform/4.11/rest_api/image_apis/imagestream-image-openshift-io-v1.html#imagestream-image-openshift-io-v1) + For example: [ImageStream API reference](https://docs.openshift.com/container-platform/4.11/rest_api/image_apis/imagestream-image-openshift-io-v1.html#imagestream-image-openshift-io-v1) - Add an `__init__` function. -Define all the required arguments that are **required** to instantiate a new resource. -Optional parameters may be added as well. -For example: + Define all the required arguments that are **required** to instantiate a new resource. + Optional parameters may be added as well. + For example: + ``` def __init__( self, @@ -71,10 +79,12 @@ For example: self.tags = tags self.lookup_policy = lookup_policy ``` + - Use `to_dict` to set the values in the resource body. Make sure you call `super().to_dict()` first. -For example: + For example: + ``` - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: self.res.update( @@ -86,4 +96,5 @@ For example: } ) ``` + - Check [imageStreams](ocp_resources/image_stream.py) for reference. diff --git a/ocp_resources/backup.py b/ocp_resources/backup.py index 0e3508de0b..23ec4abf41 100644 --- a/ocp_resources/backup.py +++ b/ocp_resources/backup.py @@ -35,7 +35,7 @@ def __init__( self.snapshot_move_data = snapshot_move_data self.storage_location = storage_location - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: spec_dict = {} diff --git a/ocp_resources/catalog_source.py b/ocp_resources/catalog_source.py index 2b89118ee8..09e3af7f60 100644 --- a/ocp_resources/catalog_source.py +++ b/ocp_resources/catalog_source.py @@ -33,7 +33,7 @@ def __init__( self.publisher = publisher self.update_strategy_registry_poll_interval = update_strategy_registry_poll_interval - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: if not all([self.source_type, self.image, self.display_name, self.publisher]): diff --git a/ocp_resources/cluster_role.py b/ocp_resources/cluster_role.py index 42738a1312..cefae8f76b 100644 --- a/ocp_resources/cluster_role.py +++ b/ocp_resources/cluster_role.py @@ -20,7 +20,7 @@ def __init__(self, rules=None, **kwargs): super().__init__(**kwargs) self.rules = rules - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: if not self.rules: diff --git a/ocp_resources/cluster_role_binding.py b/ocp_resources/cluster_role_binding.py index 0640d3d52a..6e22611eb9 100644 --- a/ocp_resources/cluster_role_binding.py +++ b/ocp_resources/cluster_role_binding.py @@ -26,7 +26,7 @@ def __init__( self.cluster_role = cluster_role self.subjects = subjects - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: if not self.cluster_role: diff --git a/ocp_resources/configmap.py b/ocp_resources/configmap.py index d018106315..dc1b947384 100644 --- a/ocp_resources/configmap.py +++ b/ocp_resources/configmap.py @@ -20,7 +20,7 @@ def __init__( super().__init__(**kwargs) self.data = data - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file and self.data: self.res.setdefault("data", {}).update(self.data) diff --git a/ocp_resources/controller_revision.py b/ocp_resources/controller_revision.py index 5a55ebd63e..2a39d98a8d 100644 --- a/ocp_resources/controller_revision.py +++ b/ocp_resources/controller_revision.py @@ -26,7 +26,7 @@ def __init__( self.revision_object = revision_object self.revision = revision - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: if not self.revision: diff --git a/ocp_resources/cron_job.py b/ocp_resources/cron_job.py index 137b688041..4e5ec6d3da 100644 --- a/ocp_resources/cron_job.py +++ b/ocp_resources/cron_job.py @@ -44,7 +44,7 @@ def __init__( self.failed_jobs_history_limit = failed_jobs_history_limit self.starting_deadline_seconds = starting_deadline_seconds - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: if not (self.job_template and self.schedule): diff --git a/ocp_resources/csi_storage_capacity.py b/ocp_resources/csi_storage_capacity.py index ab5122649e..00cb5155fd 100644 --- a/ocp_resources/csi_storage_capacity.py +++ b/ocp_resources/csi_storage_capacity.py @@ -32,7 +32,7 @@ def __init__( self.storage_class_name = storage_class_name self.maximum_volume_size = maximum_volume_size - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: if not self.storage_class_name: diff --git a/ocp_resources/data_import_cron.py b/ocp_resources/data_import_cron.py index eeb5e84766..2305342fb0 100644 --- a/ocp_resources/data_import_cron.py +++ b/ocp_resources/data_import_cron.py @@ -53,7 +53,7 @@ def __init__( self.imports_to_keep = imports_to_keep self.bind_immediate_annotation = bind_immediate_annotation - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: if self.image_stream and self.url: diff --git a/ocp_resources/data_source.py b/ocp_resources/data_source.py index 2a71c4310d..9fd84a7057 100644 --- a/ocp_resources/data_source.py +++ b/ocp_resources/data_source.py @@ -22,7 +22,7 @@ def __init__(self, source=None, **kwargs): super().__init__(**kwargs) self._source = source - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: if not self._source: diff --git a/ocp_resources/datavolume.py b/ocp_resources/datavolume.py index b986fffad2..276fa932bb 100644 --- a/ocp_resources/datavolume.py +++ b/ocp_resources/datavolume.py @@ -153,7 +153,7 @@ def __init__( self.api_name = api_name self.delete_after_completion = delete_after_completion - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: self.res.update({ diff --git a/ocp_resources/endpoint_slice.py b/ocp_resources/endpoint_slice.py index 3a13957785..de6dc5b677 100644 --- a/ocp_resources/endpoint_slice.py +++ b/ocp_resources/endpoint_slice.py @@ -51,7 +51,7 @@ def __init__( self.endpoints = endpoints self.ports = ports - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: if not (self.address_type and self.endpoints): diff --git a/ocp_resources/endpoints.py b/ocp_resources/endpoints.py index a8f2b0b783..64130cf525 100644 --- a/ocp_resources/endpoints.py +++ b/ocp_resources/endpoints.py @@ -48,11 +48,11 @@ def __init__( self.addresses = addresses self.ports = ports - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: if not (self.addresses and self.ports): - raise MissingRequiredArgumentError(argumens="'addresses' and 'ports") + raise MissingRequiredArgumentError(argument="'addresses' and 'ports") self.res.update({ "subsets": { "addresses": self.addresses, diff --git a/ocp_resources/group.py b/ocp_resources/group.py index f875a52f8f..a514c2a1ab 100644 --- a/ocp_resources/group.py +++ b/ocp_resources/group.py @@ -12,7 +12,7 @@ def __init__( super().__init__(**kwargs) self.users = users - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if (not self.yaml_file) and self.users: self.res["users"] = self.users diff --git a/ocp_resources/hook.py b/ocp_resources/hook.py index 4636f4ebd4..d36590f250 100644 --- a/ocp_resources/hook.py +++ b/ocp_resources/hook.py @@ -39,7 +39,7 @@ def __init__( self.image = image self.playbook = playbook - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: self.res.update({ diff --git a/ocp_resources/host.py b/ocp_resources/host.py index f974f66c39..68f057979f 100644 --- a/ocp_resources/host.py +++ b/ocp_resources/host.py @@ -43,7 +43,7 @@ def __init__( self.secret_namespace = secret_namespace or self.namespace self.condition_message_ready = self.ConditionMessage.HOST_READY - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: self.res.update({ diff --git a/ocp_resources/hostpath_provisioner.py b/ocp_resources/hostpath_provisioner.py index f8c596f598..edbb531672 100644 --- a/ocp_resources/hostpath_provisioner.py +++ b/ocp_resources/hostpath_provisioner.py @@ -35,7 +35,7 @@ def __init__( self.path = path self.image_pull_policy = image_pull_policy - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: spec = self.res.setdefault("spec", {}) diff --git a/ocp_resources/hyperconverged.py b/ocp_resources/hyperconverged.py index e21771f597..e755b66b1f 100644 --- a/ocp_resources/hyperconverged.py +++ b/ocp_resources/hyperconverged.py @@ -29,7 +29,7 @@ def __init__( self.infra = infra self.workloads = workloads - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: if self.infra: diff --git a/ocp_resources/image_content_source_policy.py b/ocp_resources/image_content_source_policy.py index 6669930217..2a1e3b3ead 100644 --- a/ocp_resources/image_content_source_policy.py +++ b/ocp_resources/image_content_source_policy.py @@ -23,7 +23,7 @@ def __init__(self, repository_digest_mirrors=None, **kwargs): self.repository_digest_mirrors = repository_digest_mirrors super().__init__(**kwargs) - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: if not self.repository_digest_mirrors: diff --git a/ocp_resources/image_digest_mirror_set.py b/ocp_resources/image_digest_mirror_set.py index 805fc69e17..924bf24252 100644 --- a/ocp_resources/image_digest_mirror_set.py +++ b/ocp_resources/image_digest_mirror_set.py @@ -23,7 +23,7 @@ def __init__(self, image_digest_mirrors=None, **kwargs): self.image_digest_mirrors = image_digest_mirrors super().__init__(**kwargs) - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: if not self.image_digest_mirrors: diff --git a/ocp_resources/image_stream.py b/ocp_resources/image_stream.py index 2a054f5f70..3e81127c14 100644 --- a/ocp_resources/image_stream.py +++ b/ocp_resources/image_stream.py @@ -36,7 +36,7 @@ def __init__( self.tags = tags self.lookup_policy = lookup_policy - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: self.res.update({ diff --git a/ocp_resources/ip_address_pool.py b/ocp_resources/ip_address_pool.py index c45339f0ae..0505e61ae5 100644 --- a/ocp_resources/ip_address_pool.py +++ b/ocp_resources/ip_address_pool.py @@ -39,7 +39,7 @@ def __init__( self.auto_assign = auto_assign self.avoid_buggy_ips = avoid_buggy_ips - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: if not self.addresses: diff --git a/ocp_resources/job.py b/ocp_resources/job.py index 4e336f55a4..a319ac6485 100644 --- a/ocp_resources/job.py +++ b/ocp_resources/job.py @@ -63,7 +63,7 @@ def __init__( self.containers = containers self.background_propagation_policy = background_propagation_policy - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: self.res.setdefault("spec", {}) diff --git a/ocp_resources/kube_descheduler.py b/ocp_resources/kube_descheduler.py index 7e860fc0ef..dd48c5d787 100644 --- a/ocp_resources/kube_descheduler.py +++ b/ocp_resources/kube_descheduler.py @@ -48,7 +48,7 @@ def __init__( self.mode = mode self.operator_log_level = operator_log_level - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: self.res.update({ diff --git a/ocp_resources/l2_advertisement.py b/ocp_resources/l2_advertisement.py index 4eeb3fa697..3a9659e14a 100644 --- a/ocp_resources/l2_advertisement.py +++ b/ocp_resources/l2_advertisement.py @@ -40,7 +40,7 @@ def __init__( self.ip_address_pools = ip_address_pools self.ip_address_pools_selectors = ip_address_pools_selectors - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: self.res["spec"] = {} diff --git a/ocp_resources/lease.py b/ocp_resources/lease.py index 7a4b40f3fa..94ce73a180 100644 --- a/ocp_resources/lease.py +++ b/ocp_resources/lease.py @@ -57,7 +57,7 @@ def __init__( self.acquire_time = acquire_time self.renew_time = renew_time - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: if self.acquire_time: diff --git a/ocp_resources/limit_range.py b/ocp_resources/limit_range.py index 24b8ab0597..576ca7795b 100644 --- a/ocp_resources/limit_range.py +++ b/ocp_resources/limit_range.py @@ -21,7 +21,7 @@ def __init__( super().__init__(**kwargs) self.limits = limits - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: if not self.limits: diff --git a/ocp_resources/machine_config_pool.py b/ocp_resources/machine_config_pool.py index d1fe0fcb8d..8528fbf8fb 100644 --- a/ocp_resources/machine_config_pool.py +++ b/ocp_resources/machine_config_pool.py @@ -55,7 +55,7 @@ def __init__( self.max_unavailable = max_unavailable self.paused = paused - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: self.res.update( diff --git a/ocp_resources/machine_health_check.py b/ocp_resources/machine_health_check.py index 2b4deb4d0f..92d20472a7 100644 --- a/ocp_resources/machine_health_check.py +++ b/ocp_resources/machine_health_check.py @@ -45,7 +45,7 @@ def __init__( self.unhealthy_timeout = unhealthy_timeout self.reboot_strategy = reboot_strategy - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: if self.reboot_strategy: diff --git a/ocp_resources/machine_set.py b/ocp_resources/machine_set.py index c251200c29..860b22d0ec 100644 --- a/ocp_resources/machine_set.py +++ b/ocp_resources/machine_set.py @@ -77,7 +77,7 @@ def __init__( self.machine_type = machine_type self.provider_spec = provider_spec or {} - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: _spec, _metadata, _labels = ("spec", "metadata", "labels") diff --git a/ocp_resources/metallb.py b/ocp_resources/metallb.py index 336962b61f..588045dd7f 100644 --- a/ocp_resources/metallb.py +++ b/ocp_resources/metallb.py @@ -40,7 +40,7 @@ def __init__( self.speaker_config = speaker_config self.speaker_tolerations = speaker_tolerations - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: self.res["spec"] = {} diff --git a/ocp_resources/migration.py b/ocp_resources/migration.py index 70c4a3d3a1..3810a9d681 100644 --- a/ocp_resources/migration.py +++ b/ocp_resources/migration.py @@ -43,7 +43,7 @@ def __init__( self.cut_over = cut_over self.condition_message_succeeded = self.ConditionMessage.MIGRATION_SUCCEEDED - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: self.res.update({ diff --git a/ocp_resources/migration_policy.py b/ocp_resources/migration_policy.py index 92333ef489..5435a9b6e4 100644 --- a/ocp_resources/migration_policy.py +++ b/ocp_resources/migration_policy.py @@ -38,7 +38,7 @@ def __init__( self.namespace_selector = namespace_selector or {} self.vmi_selector = vmi_selector or {} - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: spec = self.res.setdefault("spec", {}) diff --git a/ocp_resources/multi_cluster_observability.py b/ocp_resources/multi_cluster_observability.py index 795200ab9e..db932c3b7a 100644 --- a/ocp_resources/multi_cluster_observability.py +++ b/ocp_resources/multi_cluster_observability.py @@ -37,7 +37,7 @@ def __init__( self.image_pull_policy = image_pull_policy self.image_pull_secret = image_pull_secret - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: if not self.metric_object_storage: diff --git a/ocp_resources/multi_network_policy.py b/ocp_resources/multi_network_policy.py index d4fcaa5bb8..9ea50a402f 100644 --- a/ocp_resources/multi_network_policy.py +++ b/ocp_resources/multi_network_policy.py @@ -37,7 +37,7 @@ def __init__( self.ingress = ingress self.egress = egress - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: if not self.network_name and self.pod_selector is None: diff --git a/ocp_resources/network_attachment_definition.py b/ocp_resources/network_attachment_definition.py index 0b8206c712..f504db4038 100644 --- a/ocp_resources/network_attachment_definition.py +++ b/ocp_resources/network_attachment_definition.py @@ -47,7 +47,7 @@ def __init__( self.cni_version = cni_version self.config = config - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: if self.resource_name is not None: @@ -97,7 +97,7 @@ def __init__( self.macspoofchk = macspoofchk self.add_resource_name = add_resource_name - def to_dict(self): + def to_dict(self) -> None: super().to_dict() spec_config = self.res["spec"]["config"] spec_config["name"] = self.bridge_name @@ -148,7 +148,7 @@ def __init__( ) self.tuning_type = tuning_type - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if self.tuning_type: self.old_nad_format = True @@ -188,7 +188,7 @@ def __init__( cni_version=cni_version, ) - def to_dict(self): + def to_dict(self) -> None: super().to_dict() self.res["spec"]["config"] = json.dumps(self.res["spec"]["config"]) @@ -222,7 +222,7 @@ def __init__( self.network_name = network_name self.topology = topology - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: if not self.network_name and not self.topology: diff --git a/ocp_resources/network_map.py b/ocp_resources/network_map.py index 6c2e9efa9a..7d929e96fb 100644 --- a/ocp_resources/network_map.py +++ b/ocp_resources/network_map.py @@ -58,7 +58,7 @@ def __init__( self.destination_provider_namespace = destination_provider_namespace self.condition_message_ready = self.ConditionMessage.NETWORK_MAP_READY - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: self.res.update(self.map_to_dict) diff --git a/ocp_resources/node_health_check.py b/ocp_resources/node_health_check.py index cc88e9cf96..0bed82c0af 100644 --- a/ocp_resources/node_health_check.py +++ b/ocp_resources/node_health_check.py @@ -48,7 +48,7 @@ def __init__( self.escalating_remediation = escalating_remediation self.remediation_template = remediation_template - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: if not (self.selector_match_expressions or self.selector_match_labels): diff --git a/ocp_resources/node_maintenance.py b/ocp_resources/node_maintenance.py index 21e3e260b1..961c5ae603 100644 --- a/ocp_resources/node_maintenance.py +++ b/ocp_resources/node_maintenance.py @@ -33,7 +33,7 @@ def __init__( self.node = node self.reason = reason - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: assert self.node, "node is mandatory for create" diff --git a/ocp_resources/node_network_configuration_policy.py b/ocp_resources/node_network_configuration_policy.py index 8168e08d64..530f7b79e5 100644 --- a/ocp_resources/node_network_configuration_policy.py +++ b/ocp_resources/node_network_configuration_policy.py @@ -129,7 +129,7 @@ def set_interface(self, interface): self.desired_state["interfaces"] = interfaces self.res.setdefault("spec", {}).setdefault("desiredState", {})["interfaces"] = self.desired_state["interfaces"] - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: if self.dns_resolver or self.routes or self.iface: diff --git a/ocp_resources/node_network_state.py b/ocp_resources/node_network_state.py index 4ac040ca83..a86eccdb3c 100644 --- a/ocp_resources/node_network_state.py +++ b/ocp_resources/node_network_state.py @@ -40,7 +40,7 @@ def set_interface(self, interface): interfaces.append(interface) self.desired_state["interfaces"] = interfaces - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: self.res.update({ diff --git a/ocp_resources/ocs_initialization.py b/ocp_resources/ocs_initialization.py index de75dd5b89..de64c8b79d 100644 --- a/ocp_resources/ocs_initialization.py +++ b/ocp_resources/ocs_initialization.py @@ -40,7 +40,7 @@ def __init__( ) self.enable_ceph_tools = enable_ceph_tools - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file and self.enable_ceph_tools is not None: self.res.update({ diff --git a/ocp_resources/operator_group.py b/ocp_resources/operator_group.py index ebe3024a0a..db42120bdc 100644 --- a/ocp_resources/operator_group.py +++ b/ocp_resources/operator_group.py @@ -31,7 +31,7 @@ def __init__( ) self.target_namespaces = target_namespaces - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: self.res.update({"spec": {"targetNamespaces": self.target_namespaces}}) diff --git a/ocp_resources/operator_source.py b/ocp_resources/operator_source.py index 1f3db6aeef..eb16e985a6 100644 --- a/ocp_resources/operator_source.py +++ b/ocp_resources/operator_source.py @@ -33,7 +33,7 @@ def __init__( self.publisher = publisher self.secret = secret - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: self.res.update({ diff --git a/ocp_resources/performance_profile.py b/ocp_resources/performance_profile.py index c3627eaed3..fd2fd1e89f 100644 --- a/ocp_resources/performance_profile.py +++ b/ocp_resources/performance_profile.py @@ -51,7 +51,7 @@ def __init__( self.real_time_kernel = real_time_kernel self.workload_hints = workload_hints - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: if not self.cpu and not self.node_selector: diff --git a/ocp_resources/persistent_volume_claim.py b/ocp_resources/persistent_volume_claim.py index d51f9e38a3..cc40e47224 100644 --- a/ocp_resources/persistent_volume_claim.py +++ b/ocp_resources/persistent_volume_claim.py @@ -63,7 +63,7 @@ def __init__( self.storage_class = storage_class self.pvlabel = pvlabel - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: self.res.update({ diff --git a/ocp_resources/pipeline.py b/ocp_resources/pipeline.py index 9b3d0f9ef6..dfc4ae2893 100644 --- a/ocp_resources/pipeline.py +++ b/ocp_resources/pipeline.py @@ -42,7 +42,7 @@ def __init__( self.params = params self.final_parallel_tasks = final_parallel_tasks - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: if not (self.tasks or self.params or self.final_parallel_tasks): diff --git a/ocp_resources/pipelineruns.py b/ocp_resources/pipelineruns.py index fe07e4d026..00debb217c 100644 --- a/ocp_resources/pipelineruns.py +++ b/ocp_resources/pipelineruns.py @@ -38,7 +38,7 @@ def __init__( self.params = params self.service_account_name = service_account_name - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: if not self.pipelineref: diff --git a/ocp_resources/plan.py b/ocp_resources/plan.py index 65dc847906..994f9945c2 100644 --- a/ocp_resources/plan.py +++ b/ocp_resources/plan.py @@ -109,7 +109,7 @@ def generate_hook_spec(hook_name, hook_namespace, hook_type): for vm in self.virtual_machines_list: vm["hooks"] = hooks_array - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: self.res.update({ diff --git a/ocp_resources/pod_disruption_budget.py b/ocp_resources/pod_disruption_budget.py index fde627032f..d5b320aa75 100644 --- a/ocp_resources/pod_disruption_budget.py +++ b/ocp_resources/pod_disruption_budget.py @@ -35,7 +35,7 @@ def __init__( self.max_unavailable = max_unavailable self.selector = selector - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: update_dict = { diff --git a/ocp_resources/priority_class.py b/ocp_resources/priority_class.py index 741c91490d..52dea4c152 100644 --- a/ocp_resources/priority_class.py +++ b/ocp_resources/priority_class.py @@ -35,7 +35,7 @@ def __init__( self.description = description self.preemption_policy = preemption_policy - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: if self.value: diff --git a/ocp_resources/provider.py b/ocp_resources/provider.py index 93676db73f..d737734a6b 100644 --- a/ocp_resources/provider.py +++ b/ocp_resources/provider.py @@ -41,7 +41,7 @@ def __init__( self.condition_message_ready = self.ConditionMessage.PROVIDER_READY self.vddk_init_image = vddk_init_image - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: self.res.update({ diff --git a/ocp_resources/resource_quota.py b/ocp_resources/resource_quota.py index af0c2c4889..5b483d797c 100644 --- a/ocp_resources/resource_quota.py +++ b/ocp_resources/resource_quota.py @@ -29,7 +29,7 @@ def __init__( self.scope_selector = scope_selector self.scopes = scopes - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: if not self.hard: diff --git a/ocp_resources/restore.py b/ocp_resources/restore.py index e04080a48d..11c400e182 100644 --- a/ocp_resources/restore.py +++ b/ocp_resources/restore.py @@ -35,7 +35,7 @@ def __init__( self.included_namespaces = included_namespaces self.backup_name = backup_name - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: diff --git a/ocp_resources/role.py b/ocp_resources/role.py index 45c85310ba..980aec89fe 100644 --- a/ocp_resources/role.py +++ b/ocp_resources/role.py @@ -47,7 +47,7 @@ def __init__( ) self.rules = rules - def to_dict(self): + def to_dict(self) -> None: if not self.rules and not self.yaml_file: raise MissingRequiredArgumentError(argument="rules") if not self.res: diff --git a/ocp_resources/role_binding.py b/ocp_resources/role_binding.py index 39e770ad3b..b99b10a724 100644 --- a/ocp_resources/role_binding.py +++ b/ocp_resources/role_binding.py @@ -42,7 +42,7 @@ def __init__( self.role_ref_kind = role_ref_kind self.role_ref_name = role_ref_name - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: subjects = {} diff --git a/ocp_resources/route.py b/ocp_resources/route.py index 28671b6245..b96ee03668 100644 --- a/ocp_resources/route.py +++ b/ocp_resources/route.py @@ -34,7 +34,7 @@ def __init__( self.service = service self.destination_ca_cert = destination_ca_cert - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: if self.service: diff --git a/ocp_resources/sealed_secret.py b/ocp_resources/sealed_secret.py index 2a430318d9..0ea72cd992 100644 --- a/ocp_resources/sealed_secret.py +++ b/ocp_resources/sealed_secret.py @@ -31,7 +31,7 @@ def __init__( self.template = template self.data = data - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: diff --git a/ocp_resources/secret.py b/ocp_resources/secret.py index 8f9344787e..67a5e2ef67 100644 --- a/ocp_resources/secret.py +++ b/ocp_resources/secret.py @@ -41,7 +41,7 @@ def __init__( self.string_data = string_data self.type = type - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: if self.accesskeyid: diff --git a/ocp_resources/service_account.py b/ocp_resources/service_account.py index 987e2a3c34..0031f01dc8 100644 --- a/ocp_resources/service_account.py +++ b/ocp_resources/service_account.py @@ -28,7 +28,7 @@ def __init__( self.image_pull_secrets = image_pull_secrets self.secrets = secrets - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: if self.automount_service_account_token: diff --git a/ocp_resources/sriov_network.py b/ocp_resources/sriov_network.py index 2663b2145d..de22586003 100644 --- a/ocp_resources/sriov_network.py +++ b/ocp_resources/sriov_network.py @@ -39,7 +39,7 @@ def __init__( self.ipam = ipam self.macspoofchk = macspoofchk - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: self.res["spec"] = { diff --git a/ocp_resources/sriov_network_node_policy.py b/ocp_resources/sriov_network_node_policy.py index 2d9f47ad51..0a43722516 100644 --- a/ocp_resources/sriov_network_node_policy.py +++ b/ocp_resources/sriov_network_node_policy.py @@ -43,7 +43,7 @@ def __init__( self.mtu = mtu self.node_selector = node_selector - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: self.res["spec"] = { diff --git a/ocp_resources/storage_class.py b/ocp_resources/storage_class.py index b468cd72de..ac11a8598f 100644 --- a/ocp_resources/storage_class.py +++ b/ocp_resources/storage_class.py @@ -89,7 +89,7 @@ def __init__( self.allowed_topologies = allowed_topologies self.mount_options = mount_options - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: if not self.provisioner: diff --git a/ocp_resources/storage_map.py b/ocp_resources/storage_map.py index 2fbdb4ebcc..6cd38df11e 100644 --- a/ocp_resources/storage_map.py +++ b/ocp_resources/storage_map.py @@ -61,7 +61,7 @@ def __init__( self.destination_provider_namespace = destination_provider_namespace self.condition_message_ready = self.ConditionMessage.STORAGE_MAP_READY - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: self.res.update(self.map_to_dict) diff --git a/ocp_resources/subscription.py b/ocp_resources/subscription.py index b83ea49d0e..3f9ff1bee1 100644 --- a/ocp_resources/subscription.py +++ b/ocp_resources/subscription.py @@ -41,7 +41,7 @@ def __init__( self.node_selector = node_selector self.tolerations = tolerations - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: self.res.update({ diff --git a/ocp_resources/task_run.py b/ocp_resources/task_run.py index ba3d09b6d6..d348aa995d 100644 --- a/ocp_resources/task_run.py +++ b/ocp_resources/task_run.py @@ -36,7 +36,7 @@ def __init__( self.service_account_name = service_account_name self.taskrun_timeout = taskrun_timeout - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: if not (self.task_ref or self.task_spec): diff --git a/ocp_resources/upload_token_request.py b/ocp_resources/upload_token_request.py index 19e37f9549..2a3d92fd01 100644 --- a/ocp_resources/upload_token_request.py +++ b/ocp_resources/upload_token_request.py @@ -33,7 +33,7 @@ def __init__( ) self.pvc_name = pvc_name - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: self.res.update({"spec": {"pvcName": self.pvc_name}}) diff --git a/ocp_resources/virtual_machine.py b/ocp_resources/virtual_machine.py index 0e5d70907a..5c2c5438f5 100644 --- a/ocp_resources/virtual_machine.py +++ b/ocp_resources/virtual_machine.py @@ -75,7 +75,7 @@ def _subresource_api_url(self): def api_request(self, method, action, **params): return super().api_request(method=method, action=action, url=self._subresource_api_url, **params) - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: body_spec = self.body.get("spec") if self.body else None diff --git a/ocp_resources/virtual_machine_clone.py b/ocp_resources/virtual_machine_clone.py index 54e3bdb52a..0a6d9967ab 100644 --- a/ocp_resources/virtual_machine_clone.py +++ b/ocp_resources/virtual_machine_clone.py @@ -39,7 +39,7 @@ def __init__( self.new_mac_addresses = new_mac_addresses self.new_smbios_serial = new_smbios_serial - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: if not self.source_name: diff --git a/ocp_resources/virtual_machine_export.py b/ocp_resources/virtual_machine_export.py index 9477ddd80a..77e99333cf 100644 --- a/ocp_resources/virtual_machine_export.py +++ b/ocp_resources/virtual_machine_export.py @@ -49,7 +49,7 @@ def __init__( self.source_kind = source_kind self.source_name = source_name - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: if not (self.source_kind and self.source_name): diff --git a/ocp_resources/virtual_machine_import.py b/ocp_resources/virtual_machine_import.py index f24195a295..f992ea2bd3 100644 --- a/ocp_resources/virtual_machine_import.py +++ b/ocp_resources/virtual_machine_import.py @@ -142,7 +142,7 @@ def vm(self): privileged_client=self.privileged_client or self.client, ) - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: spec = self.res.setdefault("spec", {}) @@ -261,7 +261,7 @@ def __init__( ) self.mapping = mapping - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: for provider, mapping in self.mapping.items(): diff --git a/ocp_resources/virtual_machine_instance_migration.py b/ocp_resources/virtual_machine_instance_migration.py index 4f498b942d..e2f22e715c 100644 --- a/ocp_resources/virtual_machine_instance_migration.py +++ b/ocp_resources/virtual_machine_instance_migration.py @@ -27,7 +27,7 @@ def __init__( ) self._vmi = vmi - def to_dict(self): + def to_dict(self) -> None: # When creating VirtualMachineInstanceMigration vmi is mandatory but when calling get() # we cannot pass vmi. super().to_dict() diff --git a/ocp_resources/virtual_machine_migration_resource_quota.py b/ocp_resources/virtual_machine_migration_resource_quota.py index ffa322d5b7..0b1722f491 100644 --- a/ocp_resources/virtual_machine_migration_resource_quota.py +++ b/ocp_resources/virtual_machine_migration_resource_quota.py @@ -31,7 +31,7 @@ def __init__( self.limits_cpu = limits_cpu self.limits_memory = limits_memory - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: additional_resources = self.res.setdefault("spec", {}).setdefault("additionalMigrationResources", {}) diff --git a/ocp_resources/virtual_machine_restore.py b/ocp_resources/virtual_machine_restore.py index b294e02d53..f6e0cff869 100644 --- a/ocp_resources/virtual_machine_restore.py +++ b/ocp_resources/virtual_machine_restore.py @@ -39,7 +39,7 @@ def __init__( self.vm_name = vm_name self.snapshot_name = snapshot_name - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: spec = self.res.setdefault("spec", {}) diff --git a/ocp_resources/virtual_machine_snapshot.py b/ocp_resources/virtual_machine_snapshot.py index 6566ae29f2..bef901d4c3 100644 --- a/ocp_resources/virtual_machine_snapshot.py +++ b/ocp_resources/virtual_machine_snapshot.py @@ -37,7 +37,7 @@ def __init__( ) self.vm_name = vm_name - def to_dict(self): + def to_dict(self) -> None: super().to_dict() if not self.yaml_file: spec = self.res.setdefault("spec", {}) From 15831edf4d6f9754f067b97b29d96306406018f4 Mon Sep 17 00:00:00 2001 From: Meni Yakove Date: Thu, 16 May 2024 17:16:58 +0300 Subject: [PATCH 5/7] to_dict, added return type --- ocp_resources/endpoints.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ocp_resources/endpoints.py b/ocp_resources/endpoints.py index 64130cf525..117e236672 100644 --- a/ocp_resources/endpoints.py +++ b/ocp_resources/endpoints.py @@ -53,6 +53,7 @@ def to_dict(self) -> None: if not self.yaml_file: if not (self.addresses and self.ports): raise MissingRequiredArgumentError(argument="'addresses' and 'ports") + self.res.update({ "subsets": { "addresses": self.addresses, From 40b248f84d3dfde699c455ddee9936ebbc910720 Mon Sep 17 00:00:00 2001 From: Meni Yakove Date: Thu, 16 May 2024 19:29:29 +0300 Subject: [PATCH 6/7] Fix get_client --- ocp_resources/resource.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ocp_resources/resource.py b/ocp_resources/resource.py index fba0885317..cc18e1e257 100644 --- a/ocp_resources/resource.py +++ b/ocp_resources/resource.py @@ -403,7 +403,9 @@ def __init__( self.context = context self.label = label self.timeout_seconds = timeout_seconds - self.client: DynamicClient = client or get_client(config_file=self.config_file, context=self.context) + self.client: DynamicClient = client or get_client( + config_file=self.config_file, context=self.context if self.context else None + ) self.api_group: str = api_group or self.api_group self.hash_log_data = hash_log_data From 4796e9d71665be0a18eeec2646aba7f1f17ed76a Mon Sep 17 00:00:00 2001 From: Meni Yakove Date: Thu, 16 May 2024 19:49:03 +0300 Subject: [PATCH 7/7] raise if config_file is not str --- ocp_resources/resource.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ocp_resources/resource.py b/ocp_resources/resource.py index cc18e1e257..1c19dce31d 100644 --- a/ocp_resources/resource.py +++ b/ocp_resources/resource.py @@ -399,6 +399,11 @@ def __init__( self.node_selector = node_selector self.node_selector_labels = node_selector_labels self.config_file = config_file + if not isinstance(config_file, str): + # If we pass config_file which isn't a string, get_client will fail and it will be very hard to know why. + # Better fail here and let the user know. + raise ValueError("config_file must be a string") + self.config_dict = config_dict or {} self.context = context self.label = label