diff --git a/CHANGELOG.md b/CHANGELOG.md index 4bb28e388..2da70b6df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,11 +23,15 @@ - **Breaking Change:** rename `CreateFederatedIdentityProviderResponse` to `FederatedIdentityProvider` and update file path accordingly - **Breaking Change:** rename `CreateFederatedIdentityProviderResponseAssertionsInner` to `FederatedIdentityProviderAssertionsInner` and update file path accordingly - **Feature:** add `id` for `FederatedIdentityProvider` -- `ske`: [v1.7.0](services/ske/CHANGELOG.md#v170) - - **Feature:** New model `Access` - - **Feature:** New model `IDPKubeconfig` - - **Feature:** Add attribute `access` of type `Access` to model `Cluster` - - **Feature:** New API client methods: `get_idp_kubeconfig`, `get_idp_kubeconfig_with_http_info`, `get_idp_kubeconfig_without_preload_content`, `_get_idp_kubeconfig_serialize` +- `ske`: + - [v1.7.1](services/ske/CHANGELOG.md#v171) + - **Feature:** client now supports UUID and decimal types + - **Bugfix:** timeouts now passed to requests library + - [v1.7.0](services/ske/CHANGELOG.md#v170) + - **Feature:** New model `Access` + - **Feature:** New model `IDPKubeconfig` + - **Feature:** Add attribute `access` of type `Access` to model `Cluster` + - **Feature:** New API client methods: `get_idp_kubeconfig`, `get_idp_kubeconfig_with_http_info`, `get_idp_kubeconfig_without_preload_content`, `_get_idp_kubeconfig_serialize` - `kms`: - [v0.8.1](services/kms/CHANGELOG.md#v081) - **Feature:** client now supports UUID and decimal types diff --git a/services/ske/CHANGELOG.md b/services/ske/CHANGELOG.md index d252ec9e1..91b551d8d 100644 --- a/services/ske/CHANGELOG.md +++ b/services/ske/CHANGELOG.md @@ -1,3 +1,7 @@ +## v1.7.1 +- **Feature:** client now supports UUID and decimal types +- **Bugfix:** timeouts now passed to requests library + ## v1.7.0 - **Feature:** New model `Access` - **Feature:** New model `IDPKubeconfig` diff --git a/services/ske/oas_commit b/services/ske/oas_commit index f137f4d66..e3713dde3 100644 --- a/services/ske/oas_commit +++ b/services/ske/oas_commit @@ -1 +1 @@ -cb550f3c2129447568c2855337b1874968e033bb +0e64886dd0847341800d7191ed193b75413be998 diff --git a/services/ske/pyproject.toml b/services/ske/pyproject.toml index ba1bd4875..6e1eb24c2 100644 --- a/services/ske/pyproject.toml +++ b/services/ske/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "stackit-ske" -version = "v1.7.0" +version = "v1.7.1" description = "SKE-API" authors = [{ name = "STACKIT Developer Tools", email = "developer-tools@stackit.cloud" }] requires-python = ">=3.9,<4.0" diff --git a/services/ske/src/stackit/ske/api_client.py b/services/ske/src/stackit/ske/api_client.py index f21e96585..8f8cb852e 100644 --- a/services/ske/src/stackit/ske/api_client.py +++ b/services/ske/src/stackit/ske/api_client.py @@ -12,11 +12,13 @@ """ # noqa: E501 import datetime +import decimal import json import mimetypes import os import re import tempfile +import uuid from enum import Enum from typing import Dict, List, Optional, Tuple, Union from urllib.parse import quote @@ -63,8 +65,10 @@ class ApiClient: "bool": bool, "date": datetime.date, "datetime": datetime.datetime, + "decimal": decimal.Decimal, "object": object, } + _pool = None def __init__(self, configuration, header_name=None, header_value=None, cookie=None) -> None: self.config: Configuration = configuration @@ -267,7 +271,7 @@ def response_deserialize( return_data = self.__deserialize_file(response_data) elif response_type is not None: match = None - content_type = response_data.getheader("content-type") + content_type = response_data.headers.get("content-type") if content_type is not None: match = re.search(r"charset=([a-zA-Z\-\d]+)[\s;]?", content_type) encoding = match.group(1) if match else "utf-8" @@ -284,7 +288,7 @@ def response_deserialize( return ApiResponse( status_code=response_data.status, data=return_data, - headers=response_data.getheaders(), + headers=response_data.headers, raw_data=response_data.data, ) @@ -296,6 +300,7 @@ def sanitize_for_serialization(self, obj): If obj is str, int, long, float, bool, return directly. If obj is datetime.datetime, datetime.date convert to string in iso8601 format. + If obj is decimal.Decimal return string representation. If obj is list, sanitize each element in the list. If obj is dict, return the dict. If obj is OpenAPI model, return the properties dict. @@ -311,12 +316,16 @@ def sanitize_for_serialization(self, obj): return obj.get_secret_value() elif isinstance(obj, self.PRIMITIVE_TYPES): return obj + elif isinstance(obj, uuid.UUID): + return str(obj) elif isinstance(obj, list): return [self.sanitize_for_serialization(sub_obj) for sub_obj in obj] elif isinstance(obj, tuple): return tuple(self.sanitize_for_serialization(sub_obj) for sub_obj in obj) elif isinstance(obj, (datetime.datetime, datetime.date)): return obj.isoformat() + elif isinstance(obj, decimal.Decimal): + return str(obj) elif isinstance(obj, dict): obj_dict = obj @@ -326,7 +335,7 @@ def sanitize_for_serialization(self, obj): # and attributes which value is not None. # Convert attribute name to json key in # model definition for request. - if hasattr(obj, "to_dict") and callable(obj.to_dict): + if hasattr(obj, "to_dict") and callable(getattr(obj, "to_dict")): # noqa: B009 obj_dict = obj.to_dict() else: obj_dict = obj.__dict__ @@ -354,7 +363,7 @@ def deserialize(self, response_text: str, response_type: str, content_type: Opti data = json.loads(response_text) except ValueError: data = response_text - elif re.match(r"^application/(json|[\w!#$&.+-^_]+\+json)\s*(;|$)", content_type, re.IGNORECASE): + elif re.match(r"^application/(json|[\w!#$&.+\-^_]+\+json)\s*(;|$)", content_type, re.IGNORECASE): if response_text == "": data = "" else: @@ -400,12 +409,14 @@ def __deserialize(self, data, klass): if klass in self.PRIMITIVE_TYPES: return self.__deserialize_primitive(data, klass) - elif klass == object: + elif klass is object: return self.__deserialize_object(data) - elif klass == datetime.date: + elif klass is datetime.date: return self.__deserialize_date(data) - elif klass == datetime.datetime: + elif klass is datetime.datetime: return self.__deserialize_datetime(data) + elif klass is decimal.Decimal: + return decimal.Decimal(data) elif issubclass(klass, Enum): return self.__deserialize_enum(data, klass) else: @@ -553,12 +564,14 @@ def __deserialize_file(self, response): os.close(fd) os.remove(path) - content_disposition = response.getheader("Content-Disposition") + content_disposition = response.headers.get("Content-Disposition") if content_disposition: m = re.search(r'filename=[\'"]?([^\'"\s]+)[\'"]?', content_disposition) if m is None: raise ValueError("Unexpected 'content-disposition' header value") - filename = m.group(1) + filename = os.path.basename(m.group(1)) # Strip any directory traversal + if filename in ("", ".", ".."): # fall back to tmp filename + filename = os.path.basename(path) path = os.path.join(os.path.dirname(path), filename) with open(path, "wb") as f: diff --git a/services/ske/src/stackit/ske/exceptions.py b/services/ske/src/stackit/ske/exceptions.py index 0153ee5d2..7f4ae9acb 100644 --- a/services/ske/src/stackit/ske/exceptions.py +++ b/services/ske/src/stackit/ske/exceptions.py @@ -129,7 +129,7 @@ def __init__( self.body = http_resp.data.decode("utf-8") except Exception: # noqa: S110 pass - self.headers = http_resp.getheaders() + self.headers = http_resp.headers @classmethod def from_response( diff --git a/services/ske/src/stackit/ske/models/__init__.py b/services/ske/src/stackit/ske/models/__init__.py index 85d93b39d..52102c18d 100644 --- a/services/ske/src/stackit/ske/models/__init__.py +++ b/services/ske/src/stackit/ske/models/__init__.py @@ -12,7 +12,6 @@ Do not edit the class manually. """ # noqa: E501 - from stackit.ske.models.access import Access from stackit.ske.models.access_scope import AccessScope diff --git a/services/ske/src/stackit/ske/models/cluster.py b/services/ske/src/stackit/ske/models/cluster.py index db8860f64..84a32f94c 100644 --- a/services/ske/src/stackit/ske/models/cluster.py +++ b/services/ske/src/stackit/ske/models/cluster.py @@ -119,9 +119,9 @@ def to_dict(self) -> Dict[str, Any]: # override the default output from pydantic by calling `to_dict()` of each item in nodepools (list) _items = [] if self.nodepools: - for _item in self.nodepools: - if _item: - _items.append(_item.to_dict()) + for _item_nodepools in self.nodepools: + if _item_nodepools: + _items.append(_item_nodepools.to_dict()) _dict["nodepools"] = _items # override the default output from pydantic by calling `to_dict()` of status if self.status: diff --git a/services/ske/src/stackit/ske/models/cluster_status.py b/services/ske/src/stackit/ske/models/cluster_status.py index 08dd58ad4..508b36dbe 100644 --- a/services/ske/src/stackit/ske/models/cluster_status.py +++ b/services/ske/src/stackit/ske/models/cluster_status.py @@ -130,9 +130,9 @@ def to_dict(self) -> Dict[str, Any]: # override the default output from pydantic by calling `to_dict()` of each item in errors (list) _items = [] if self.errors: - for _item in self.errors: - if _item: - _items.append(_item.to_dict()) + for _item_errors in self.errors: + if _item_errors: + _items.append(_item_errors.to_dict()) _dict["errors"] = _items return _dict diff --git a/services/ske/src/stackit/ske/models/create_or_update_cluster_payload.py b/services/ske/src/stackit/ske/models/create_or_update_cluster_payload.py index 436748f19..6e3272251 100644 --- a/services/ske/src/stackit/ske/models/create_or_update_cluster_payload.py +++ b/services/ske/src/stackit/ske/models/create_or_update_cluster_payload.py @@ -112,9 +112,9 @@ def to_dict(self) -> Dict[str, Any]: # override the default output from pydantic by calling `to_dict()` of each item in nodepools (list) _items = [] if self.nodepools: - for _item in self.nodepools: - if _item: - _items.append(_item.to_dict()) + for _item_nodepools in self.nodepools: + if _item_nodepools: + _items.append(_item_nodepools.to_dict()) _dict["nodepools"] = _items # override the default output from pydantic by calling `to_dict()` of status if self.status: diff --git a/services/ske/src/stackit/ske/models/hibernation.py b/services/ske/src/stackit/ske/models/hibernation.py index c5e2ea0e5..8adfc480e 100644 --- a/services/ske/src/stackit/ske/models/hibernation.py +++ b/services/ske/src/stackit/ske/models/hibernation.py @@ -71,9 +71,9 @@ def to_dict(self) -> Dict[str, Any]: # override the default output from pydantic by calling `to_dict()` of each item in schedules (list) _items = [] if self.schedules: - for _item in self.schedules: - if _item: - _items.append(_item.to_dict()) + for _item_schedules in self.schedules: + if _item_schedules: + _items.append(_item_schedules.to_dict()) _dict["schedules"] = _items return _dict diff --git a/services/ske/src/stackit/ske/models/list_clusters_response.py b/services/ske/src/stackit/ske/models/list_clusters_response.py index 3fdd7ea60..428e1b0ed 100644 --- a/services/ske/src/stackit/ske/models/list_clusters_response.py +++ b/services/ske/src/stackit/ske/models/list_clusters_response.py @@ -71,9 +71,9 @@ def to_dict(self) -> Dict[str, Any]: # override the default output from pydantic by calling `to_dict()` of each item in items (list) _items = [] if self.items: - for _item in self.items: - if _item: - _items.append(_item.to_dict()) + for _item_items in self.items: + if _item_items: + _items.append(_item_items.to_dict()) _dict["items"] = _items return _dict diff --git a/services/ske/src/stackit/ske/models/machine_image.py b/services/ske/src/stackit/ske/models/machine_image.py index 1ce1ad7c4..082d9b4a9 100644 --- a/services/ske/src/stackit/ske/models/machine_image.py +++ b/services/ske/src/stackit/ske/models/machine_image.py @@ -72,9 +72,9 @@ def to_dict(self) -> Dict[str, Any]: # override the default output from pydantic by calling `to_dict()` of each item in versions (list) _items = [] if self.versions: - for _item in self.versions: - if _item: - _items.append(_item.to_dict()) + for _item_versions in self.versions: + if _item_versions: + _items.append(_item_versions.to_dict()) _dict["versions"] = _items return _dict diff --git a/services/ske/src/stackit/ske/models/machine_image_version.py b/services/ske/src/stackit/ske/models/machine_image_version.py index 862f7e3bf..489c28ab0 100644 --- a/services/ske/src/stackit/ske/models/machine_image_version.py +++ b/services/ske/src/stackit/ske/models/machine_image_version.py @@ -99,9 +99,9 @@ def to_dict(self) -> Dict[str, Any]: # override the default output from pydantic by calling `to_dict()` of each item in cri (list) _items = [] if self.cri: - for _item in self.cri: - if _item: - _items.append(_item.to_dict()) + for _item_cri in self.cri: + if _item_cri: + _items.append(_item_cri.to_dict()) _dict["cri"] = _items return _dict diff --git a/services/ske/src/stackit/ske/models/nodepool.py b/services/ske/src/stackit/ske/models/nodepool.py index 019ffb8f1..1ff8e7e54 100644 --- a/services/ske/src/stackit/ske/models/nodepool.py +++ b/services/ske/src/stackit/ske/models/nodepool.py @@ -123,9 +123,9 @@ def to_dict(self) -> Dict[str, Any]: # override the default output from pydantic by calling `to_dict()` of each item in taints (list) _items = [] if self.taints: - for _item in self.taints: - if _item: - _items.append(_item.to_dict()) + for _item_taints in self.taints: + if _item_taints: + _items.append(_item_taints.to_dict()) _dict["taints"] = _items # override the default output from pydantic by calling `to_dict()` of volume if self.volume: diff --git a/services/ske/src/stackit/ske/models/provider_options.py b/services/ske/src/stackit/ske/models/provider_options.py index 5e30509c4..125fdeafc 100644 --- a/services/ske/src/stackit/ske/models/provider_options.py +++ b/services/ske/src/stackit/ske/models/provider_options.py @@ -85,37 +85,37 @@ def to_dict(self) -> Dict[str, Any]: # override the default output from pydantic by calling `to_dict()` of each item in availability_zones (list) _items = [] if self.availability_zones: - for _item in self.availability_zones: - if _item: - _items.append(_item.to_dict()) + for _item_availability_zones in self.availability_zones: + if _item_availability_zones: + _items.append(_item_availability_zones.to_dict()) _dict["availabilityZones"] = _items # override the default output from pydantic by calling `to_dict()` of each item in kubernetes_versions (list) _items = [] if self.kubernetes_versions: - for _item in self.kubernetes_versions: - if _item: - _items.append(_item.to_dict()) + for _item_kubernetes_versions in self.kubernetes_versions: + if _item_kubernetes_versions: + _items.append(_item_kubernetes_versions.to_dict()) _dict["kubernetesVersions"] = _items # override the default output from pydantic by calling `to_dict()` of each item in machine_images (list) _items = [] if self.machine_images: - for _item in self.machine_images: - if _item: - _items.append(_item.to_dict()) + for _item_machine_images in self.machine_images: + if _item_machine_images: + _items.append(_item_machine_images.to_dict()) _dict["machineImages"] = _items # override the default output from pydantic by calling `to_dict()` of each item in machine_types (list) _items = [] if self.machine_types: - for _item in self.machine_types: - if _item: - _items.append(_item.to_dict()) + for _item_machine_types in self.machine_types: + if _item_machine_types: + _items.append(_item_machine_types.to_dict()) _dict["machineTypes"] = _items # override the default output from pydantic by calling `to_dict()` of each item in volume_types (list) _items = [] if self.volume_types: - for _item in self.volume_types: - if _item: - _items.append(_item.to_dict()) + for _item_volume_types in self.volume_types: + if _item_volume_types: + _items.append(_item_volume_types.to_dict()) _dict["volumeTypes"] = _items return _dict diff --git a/services/ske/src/stackit/ske/rest.py b/services/ske/src/stackit/ske/rest.py index 85936df6c..27074d05f 100644 --- a/services/ske/src/stackit/ske/rest.py +++ b/services/ske/src/stackit/ske/rest.py @@ -38,12 +38,17 @@ def read(self): self.data = self.response.content return self.data + @property + def headers(self): + """Returns a dictionary of response headers.""" + return self.response.headers + def getheaders(self): - """Returns a dictionary of the response headers.""" + """Returns a dictionary of the response headers; use ``headers`` instead.""" return self.response.headers def getheader(self, name, default=None): - """Returns a given response header.""" + """Returns a given response header; use ``headers.get()`` instead.""" return self.response.headers.get(name, default) @@ -93,6 +98,7 @@ def request(self, method, url, headers=None, body=None, post_params=None, _reque url, data=request_body, headers=headers, + timeout=_request_timeout, ) elif content_type == "application/x-www-form-urlencoded": r = self.session.request( @@ -100,6 +106,7 @@ def request(self, method, url, headers=None, body=None, post_params=None, _reque url, params=post_params, headers=headers, + timeout=_request_timeout, ) elif content_type == "multipart/form-data": # must del headers['Content-Type'], or the correct @@ -113,6 +120,7 @@ def request(self, method, url, headers=None, body=None, post_params=None, _reque url, files=post_params, headers=headers, + timeout=_request_timeout, ) # Pass a `string` parameter directly in the body to support # other content types than JSON when `body` argument is @@ -123,10 +131,17 @@ def request(self, method, url, headers=None, body=None, post_params=None, _reque url, data=body, headers=headers, + timeout=_request_timeout, ) elif headers["Content-Type"].startswith("text/") and isinstance(body, bool): request_body = "true" if body else "false" - r = self.session.request(method, url, data=request_body, headers=headers) + r = self.session.request( + method, + url, + data=request_body, + headers=headers, + timeout=_request_timeout, + ) else: # Cannot generate the request from given parameters msg = """Cannot prepare a request message for provided @@ -140,6 +155,7 @@ def request(self, method, url, headers=None, body=None, post_params=None, _reque url, params={}, headers=headers, + timeout=_request_timeout, ) except requests.exceptions.SSLError as e: msg = "\n".join([type(e).__name__, str(e)])