From 26b81bdeb1d84c2bc72a34e046db8fb1b1e5fcd1 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 17 Jun 2025 10:10:43 +0000 Subject: [PATCH 01/50] feat(api): aggregated API specs update --- .stats.yml | 4 +- .../resources/cloud/baremetal/servers.py | 7 +- .../cloud/file_shares/file_shares.py | 15 ++-- src/gcore/resources/cloud/floating_ips.py | 7 +- .../gpu_baremetal_clusters.py | 7 +- .../cloud/gpu_baremetal_clusters/images.py | 7 +- src/gcore/resources/cloud/instances/images.py | 10 +-- .../resources/cloud/instances/instances.py | 7 +- .../cloud/load_balancers/load_balancers.py | 56 +++++++++++-- .../resources/cloud/networks/networks.py | 84 ++++++++++++++++--- src/gcore/resources/cloud/networks/subnets.py | 48 +++++++++-- .../cloud/security_groups/security_groups.py | 45 +++++++++- .../cloud/baremetal/server_create_params.py | 12 +-- src/gcore/types/cloud/ddos_profile.py | 12 ++- .../types/cloud/file_share_create_params.py | 8 +- .../types/cloud/floating_ip_create_params.py | 6 +- .../gpu_baremetal_cluster_create_params.py | 5 +- .../image_upload_params.py | 6 +- .../types/cloud/instance_create_params.py | 15 ++-- .../image_create_from_volume_params.py | 6 +- .../cloud/instances/image_upload_params.py | 6 +- .../cloud/load_balancer_create_params.py | 5 +- .../cloud/load_balancer_update_params.py | 21 +++++ .../types/cloud/network_create_params.py | 5 +- src/gcore/types/cloud/network_list_params.py | 3 + .../types/cloud/network_update_params.py | 27 +++++- .../cloud/networks/subnet_create_params.py | 5 +- .../cloud/networks/subnet_update_params.py | 22 +++++ .../cloud/security_group_update_params.py | 22 +++++ src/gcore/types/cloud/tag_update_map_param.py | 4 +- .../cloud/baremetal/test_servers.py | 10 +-- .../gpu_baremetal_clusters/test_images.py | 4 +- .../cloud/instances/test_images.py | 8 +- .../cloud/networks/test_subnets.py | 6 +- tests/api_resources/cloud/test_file_shares.py | 8 +- .../api_resources/cloud/test_floating_ips.py | 4 +- .../cloud/test_gpu_baremetal_clusters.py | 4 +- tests/api_resources/cloud/test_instances.py | 8 +- .../cloud/test_load_balancers.py | 6 +- tests/api_resources/cloud/test_networks.py | 32 +++++-- .../cloud/test_security_groups.py | 2 + 41 files changed, 426 insertions(+), 153 deletions(-) diff --git a/.stats.yml b/.stats.yml index b42d4f2b..194bd758 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 302 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-b5883594159ef5544fde3856674f72adc1000c03aaf48ec8b7ba7ca9b128c66d.yml -openapi_spec_hash: beacedf1517348ed0158b7d9f530fc62 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-ac4eb65ad579c94b23a363fbf0d27e49daf2fdd4c122b2f096297ff039d52365.yml +openapi_spec_hash: c1286da9cda9769c850ac41dd3216f72 config_hash: 62229a7a78ca1c79cb7cf61752a8df7e diff --git a/src/gcore/resources/cloud/baremetal/servers.py b/src/gcore/resources/cloud/baremetal/servers.py index a38c9e1d..8c2ebea7 100644 --- a/src/gcore/resources/cloud/baremetal/servers.py +++ b/src/gcore/resources/cloud/baremetal/servers.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import List, Union, Iterable, Optional +from typing import Dict, List, Union, Iterable, Optional from datetime import datetime from typing_extensions import Literal @@ -22,7 +22,6 @@ from ...._base_client import AsyncPaginator, make_request_options from ....types.cloud.baremetal import server_list_params, server_create_params, server_rebuild_params from ....types.cloud.task_id_list import TaskIDList -from ....types.cloud.tag_update_map_param import TagUpdateMapParam from ....types.cloud.baremetal.baremetal_server import BaremetalServer __all__ = ["ServersResource", "AsyncServersResource"] @@ -63,7 +62,7 @@ def create( name_template: str | NotGiven = NOT_GIVEN, password: str | NotGiven = NOT_GIVEN, ssh_key_name: Optional[str] | NotGiven = NOT_GIVEN, - tags: TagUpdateMapParam | NotGiven = NOT_GIVEN, + tags: Dict[str, str] | NotGiven = NOT_GIVEN, user_data: str | NotGiven = NOT_GIVEN, username: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -537,7 +536,7 @@ async def create( name_template: str | NotGiven = NOT_GIVEN, password: str | NotGiven = NOT_GIVEN, ssh_key_name: Optional[str] | NotGiven = NOT_GIVEN, - tags: TagUpdateMapParam | NotGiven = NOT_GIVEN, + tags: Dict[str, str] | NotGiven = NOT_GIVEN, user_data: str | NotGiven = NOT_GIVEN, username: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. diff --git a/src/gcore/resources/cloud/file_shares/file_shares.py b/src/gcore/resources/cloud/file_shares/file_shares.py index 9a9ad4bb..dea00eab 100644 --- a/src/gcore/resources/cloud/file_shares/file_shares.py +++ b/src/gcore/resources/cloud/file_shares/file_shares.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Iterable +from typing import Dict, Iterable from typing_extensions import Literal, overload import httpx @@ -35,7 +35,6 @@ from ...._base_client import AsyncPaginator, make_request_options from ....types.cloud.file_share import FileShare from ....types.cloud.task_id_list import TaskIDList -from ....types.cloud.tag_update_map_param import TagUpdateMapParam __all__ = ["FileSharesResource", "AsyncFileSharesResource"] @@ -75,7 +74,7 @@ def create( protocol: Literal["NFS"], size: int, access: Iterable[file_share_create_params.CreateStandardFileShareSerializerAccess] | NotGiven = NOT_GIVEN, - tags: TagUpdateMapParam | NotGiven = NOT_GIVEN, + tags: Dict[str, str] | NotGiven = NOT_GIVEN, volume_type: Literal["default_share_type"] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -130,7 +129,7 @@ def create( protocol: Literal["NFS"], size: int, volume_type: Literal["vast_share_type"], - tags: TagUpdateMapParam | NotGiven = NOT_GIVEN, + tags: Dict[str, str] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -181,7 +180,7 @@ def create( protocol: Literal["NFS"], size: int, access: Iterable[file_share_create_params.CreateStandardFileShareSerializerAccess] | NotGiven = NOT_GIVEN, - tags: TagUpdateMapParam | NotGiven = NOT_GIVEN, + tags: Dict[str, str] | NotGiven = NOT_GIVEN, volume_type: Literal["default_share_type"] | Literal["vast_share_type"] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -504,7 +503,7 @@ async def create( protocol: Literal["NFS"], size: int, access: Iterable[file_share_create_params.CreateStandardFileShareSerializerAccess] | NotGiven = NOT_GIVEN, - tags: TagUpdateMapParam | NotGiven = NOT_GIVEN, + tags: Dict[str, str] | NotGiven = NOT_GIVEN, volume_type: Literal["default_share_type"] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -559,7 +558,7 @@ async def create( protocol: Literal["NFS"], size: int, volume_type: Literal["vast_share_type"], - tags: TagUpdateMapParam | NotGiven = NOT_GIVEN, + tags: Dict[str, str] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -610,7 +609,7 @@ async def create( protocol: Literal["NFS"], size: int, access: Iterable[file_share_create_params.CreateStandardFileShareSerializerAccess] | NotGiven = NOT_GIVEN, - tags: TagUpdateMapParam | NotGiven = NOT_GIVEN, + tags: Dict[str, str] | NotGiven = NOT_GIVEN, volume_type: Literal["default_share_type"] | Literal["vast_share_type"] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. diff --git a/src/gcore/resources/cloud/floating_ips.py b/src/gcore/resources/cloud/floating_ips.py index ff122671..6fab4e6c 100644 --- a/src/gcore/resources/cloud/floating_ips.py +++ b/src/gcore/resources/cloud/floating_ips.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import List, Optional +from typing import Dict, List, Optional import httpx @@ -22,7 +22,6 @@ from ...types.cloud.floating_ip import FloatingIP from ...types.cloud.task_id_list import TaskIDList from ...types.cloud.floating_ip_detailed import FloatingIPDetailed -from ...types.cloud.tag_update_map_param import TagUpdateMapParam __all__ = ["FloatingIPsResource", "AsyncFloatingIPsResource"] @@ -54,7 +53,7 @@ def create( region_id: int | None = None, fixed_ip_address: Optional[str] | NotGiven = NOT_GIVEN, port_id: Optional[str] | NotGiven = NOT_GIVEN, - tags: TagUpdateMapParam | NotGiven = NOT_GIVEN, + tags: Dict[str, str] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -375,7 +374,7 @@ async def create( region_id: int | None = None, fixed_ip_address: Optional[str] | NotGiven = NOT_GIVEN, port_id: Optional[str] | NotGiven = NOT_GIVEN, - tags: TagUpdateMapParam | NotGiven = NOT_GIVEN, + tags: Dict[str, str] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, diff --git a/src/gcore/resources/cloud/gpu_baremetal_clusters/gpu_baremetal_clusters.py b/src/gcore/resources/cloud/gpu_baremetal_clusters/gpu_baremetal_clusters.py index d28d0379..e37c8d0c 100644 --- a/src/gcore/resources/cloud/gpu_baremetal_clusters/gpu_baremetal_clusters.py +++ b/src/gcore/resources/cloud/gpu_baremetal_clusters/gpu_baremetal_clusters.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import List, Iterable, Optional +from typing import Dict, List, Iterable, Optional import httpx @@ -58,7 +58,6 @@ ) from ...._base_client import AsyncPaginator, make_request_options from ....types.cloud.task_id_list import TaskIDList -from ....types.cloud.tag_update_map_param import TagUpdateMapParam from ....types.cloud.gpu_baremetal_cluster import GPUBaremetalCluster from ....types.cloud.gpu_baremetal_cluster_server_list import GPUBaremetalClusterServerList @@ -113,7 +112,7 @@ def create( instances_count: int | NotGiven = NOT_GIVEN, password: str | NotGiven = NOT_GIVEN, ssh_key_name: str | NotGiven = NOT_GIVEN, - tags: TagUpdateMapParam | NotGiven = NOT_GIVEN, + tags: Dict[str, str] | NotGiven = NOT_GIVEN, user_data: str | NotGiven = NOT_GIVEN, username: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -735,7 +734,7 @@ async def create( instances_count: int | NotGiven = NOT_GIVEN, password: str | NotGiven = NOT_GIVEN, ssh_key_name: str | NotGiven = NOT_GIVEN, - tags: TagUpdateMapParam | NotGiven = NOT_GIVEN, + tags: Dict[str, str] | NotGiven = NOT_GIVEN, user_data: str | NotGiven = NOT_GIVEN, username: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. diff --git a/src/gcore/resources/cloud/gpu_baremetal_clusters/images.py b/src/gcore/resources/cloud/gpu_baremetal_clusters/images.py index 0b4ac0e9..f1524d32 100644 --- a/src/gcore/resources/cloud/gpu_baremetal_clusters/images.py +++ b/src/gcore/resources/cloud/gpu_baremetal_clusters/images.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Optional +from typing import Dict, Optional from typing_extensions import Literal import httpx @@ -21,7 +21,6 @@ from ....types.cloud.gpu_image import GPUImage from ....types.cloud.task_id_list import TaskIDList from ....types.cloud.gpu_image_list import GPUImageList -from ....types.cloud.tag_update_map_param import TagUpdateMapParam from ....types.cloud.gpu_baremetal_clusters import image_upload_params __all__ = ["ImagesResource", "AsyncImagesResource"] @@ -227,7 +226,7 @@ def upload( os_type: Optional[Literal["linux", "windows"]] | NotGiven = NOT_GIVEN, os_version: Optional[str] | NotGiven = NOT_GIVEN, ssh_key: Literal["allow", "deny", "required"] | NotGiven = NOT_GIVEN, - tags: TagUpdateMapParam | NotGiven = NOT_GIVEN, + tags: Dict[str, str] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -570,7 +569,7 @@ async def upload( os_type: Optional[Literal["linux", "windows"]] | NotGiven = NOT_GIVEN, os_version: Optional[str] | NotGiven = NOT_GIVEN, ssh_key: Literal["allow", "deny", "required"] | NotGiven = NOT_GIVEN, - tags: TagUpdateMapParam | NotGiven = NOT_GIVEN, + tags: Dict[str, str] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, diff --git a/src/gcore/resources/cloud/instances/images.py b/src/gcore/resources/cloud/instances/images.py index 3cbb21e5..c99d01a7 100644 --- a/src/gcore/resources/cloud/instances/images.py +++ b/src/gcore/resources/cloud/instances/images.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import List, Optional +from typing import Dict, List, Optional from typing_extensions import Literal import httpx @@ -284,7 +284,7 @@ def create_from_volume( os_type: Literal["linux", "windows"] | NotGiven = NOT_GIVEN, source: Literal["volume"] | NotGiven = NOT_GIVEN, ssh_key: Literal["allow", "deny", "required"] | NotGiven = NOT_GIVEN, - tags: TagUpdateMapParam | NotGiven = NOT_GIVEN, + tags: Dict[str, str] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -477,7 +477,7 @@ def upload( os_type: Literal["linux", "windows"] | NotGiven = NOT_GIVEN, os_version: Optional[str] | NotGiven = NOT_GIVEN, ssh_key: Literal["allow", "deny", "required"] | NotGiven = NOT_GIVEN, - tags: TagUpdateMapParam | NotGiven = NOT_GIVEN, + tags: Dict[str, str] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -871,7 +871,7 @@ async def create_from_volume( os_type: Literal["linux", "windows"] | NotGiven = NOT_GIVEN, source: Literal["volume"] | NotGiven = NOT_GIVEN, ssh_key: Literal["allow", "deny", "required"] | NotGiven = NOT_GIVEN, - tags: TagUpdateMapParam | NotGiven = NOT_GIVEN, + tags: Dict[str, str] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1064,7 +1064,7 @@ async def upload( os_type: Literal["linux", "windows"] | NotGiven = NOT_GIVEN, os_version: Optional[str] | NotGiven = NOT_GIVEN, ssh_key: Literal["allow", "deny", "required"] | NotGiven = NOT_GIVEN, - tags: TagUpdateMapParam | NotGiven = NOT_GIVEN, + tags: Dict[str, str] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, diff --git a/src/gcore/resources/cloud/instances/instances.py b/src/gcore/resources/cloud/instances/instances.py index f4155a0e..14c319f4 100644 --- a/src/gcore/resources/cloud/instances/instances.py +++ b/src/gcore/resources/cloud/instances/instances.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import List, Union, Iterable, Optional +from typing import Dict, List, Union, Iterable, Optional from datetime import datetime from typing_extensions import Literal, overload @@ -68,7 +68,6 @@ from ....types.cloud.instance import Instance from ....types.cloud.task_id_list import TaskIDList from ....types.cloud.instance_interface import InstanceInterface -from ....types.cloud.tag_update_map_param import TagUpdateMapParam __all__ = ["InstancesResource", "AsyncInstancesResource"] @@ -125,7 +124,7 @@ def create( security_groups: Iterable[instance_create_params.SecurityGroup] | NotGiven = NOT_GIVEN, servergroup_id: str | NotGiven = NOT_GIVEN, ssh_key_name: Optional[str] | NotGiven = NOT_GIVEN, - tags: TagUpdateMapParam | NotGiven = NOT_GIVEN, + tags: Dict[str, str] | NotGiven = NOT_GIVEN, user_data: str | NotGiven = NOT_GIVEN, username: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -1367,7 +1366,7 @@ async def create( security_groups: Iterable[instance_create_params.SecurityGroup] | NotGiven = NOT_GIVEN, servergroup_id: str | NotGiven = NOT_GIVEN, ssh_key_name: Optional[str] | NotGiven = NOT_GIVEN, - tags: TagUpdateMapParam | NotGiven = NOT_GIVEN, + tags: Dict[str, str] | NotGiven = NOT_GIVEN, user_data: str | NotGiven = NOT_GIVEN, username: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. diff --git a/src/gcore/resources/cloud/load_balancers/load_balancers.py b/src/gcore/resources/cloud/load_balancers/load_balancers.py index 8fdcb349..5e02feaa 100644 --- a/src/gcore/resources/cloud/load_balancers/load_balancers.py +++ b/src/gcore/resources/cloud/load_balancers/load_balancers.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import List, Iterable +from typing import Dict, List, Iterable, Optional import httpx @@ -141,7 +141,7 @@ def create( name: str | NotGiven = NOT_GIVEN, name_template: str | NotGiven = NOT_GIVEN, preferred_connectivity: LoadBalancerMemberConnectivity | NotGiven = NOT_GIVEN, - tags: TagUpdateMapParam | NotGiven = NOT_GIVEN, + tags: Dict[str, str] | NotGiven = NOT_GIVEN, vip_ip_family: InterfaceIPFamily | NotGiven = NOT_GIVEN, vip_network_id: str | NotGiven = NOT_GIVEN, vip_port_id: str | NotGiven = NOT_GIVEN, @@ -240,6 +240,7 @@ def update( logging: load_balancer_update_params.Logging | NotGiven = NOT_GIVEN, name: str | NotGiven = NOT_GIVEN, preferred_connectivity: LoadBalancerMemberConnectivity | NotGiven = NOT_GIVEN, + tags: Optional[TagUpdateMapParam] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -248,8 +249,10 @@ def update( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> LoadBalancer: """ - Rename load balancer, activate/deactivate logs or update preferred connectivity - for load balancer + Rename load balancer, activate/deactivate logging, update preferred connectivity + type and/or modify load balancer tags. The request will only process the fields + that are provided in the request body. Any fields that are not included will + remain unchanged. Args: logging: Logging configuration @@ -259,6 +262,23 @@ def update( preferred_connectivity: Preferred option to establish connectivity between load balancer and its pools members + tags: Update key-value tags using JSON Merge Patch semantics (RFC 7386). Provide + key-value pairs to add or update tags. Set tag values to `null` to remove tags. + Unspecified tags remain unchanged. **Examples:** + + - **Add/update tags:** + `{'tags': {'environment': 'production', 'team': 'backend'}}` adds new tags or + updates existing ones. + - **Delete tags:** `{'tags': {'`old_tag`': null}}` removes specific tags. + - **Partial update:** `{'tags': {'environment': 'staging'}}` only updates + specified tags. + - **Mixed operations:** + `{'tags': {'environment': 'production', '`cost_center`': 'engineering', '`deprecated_tag`': null}}` + adds/updates 'environment' and '`cost_center`' while removing + '`deprecated_tag`', preserving other existing tags. + - **Replace all:** first delete existing tags with null values, then add new + ones in the same request. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -280,6 +300,7 @@ def update( "logging": logging, "name": name, "preferred_connectivity": preferred_connectivity, + "tags": tags, }, load_balancer_update_params.LoadBalancerUpdateParams, ), @@ -798,7 +819,7 @@ async def create( name: str | NotGiven = NOT_GIVEN, name_template: str | NotGiven = NOT_GIVEN, preferred_connectivity: LoadBalancerMemberConnectivity | NotGiven = NOT_GIVEN, - tags: TagUpdateMapParam | NotGiven = NOT_GIVEN, + tags: Dict[str, str] | NotGiven = NOT_GIVEN, vip_ip_family: InterfaceIPFamily | NotGiven = NOT_GIVEN, vip_network_id: str | NotGiven = NOT_GIVEN, vip_port_id: str | NotGiven = NOT_GIVEN, @@ -897,6 +918,7 @@ async def update( logging: load_balancer_update_params.Logging | NotGiven = NOT_GIVEN, name: str | NotGiven = NOT_GIVEN, preferred_connectivity: LoadBalancerMemberConnectivity | NotGiven = NOT_GIVEN, + tags: Optional[TagUpdateMapParam] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -905,8 +927,10 @@ async def update( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> LoadBalancer: """ - Rename load balancer, activate/deactivate logs or update preferred connectivity - for load balancer + Rename load balancer, activate/deactivate logging, update preferred connectivity + type and/or modify load balancer tags. The request will only process the fields + that are provided in the request body. Any fields that are not included will + remain unchanged. Args: logging: Logging configuration @@ -916,6 +940,23 @@ async def update( preferred_connectivity: Preferred option to establish connectivity between load balancer and its pools members + tags: Update key-value tags using JSON Merge Patch semantics (RFC 7386). Provide + key-value pairs to add or update tags. Set tag values to `null` to remove tags. + Unspecified tags remain unchanged. **Examples:** + + - **Add/update tags:** + `{'tags': {'environment': 'production', 'team': 'backend'}}` adds new tags or + updates existing ones. + - **Delete tags:** `{'tags': {'`old_tag`': null}}` removes specific tags. + - **Partial update:** `{'tags': {'environment': 'staging'}}` only updates + specified tags. + - **Mixed operations:** + `{'tags': {'environment': 'production', '`cost_center`': 'engineering', '`deprecated_tag`': null}}` + adds/updates 'environment' and '`cost_center`' while removing + '`deprecated_tag`', preserving other existing tags. + - **Replace all:** first delete existing tags with null values, then add new + ones in the same request. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -937,6 +978,7 @@ async def update( "logging": logging, "name": name, "preferred_connectivity": preferred_connectivity, + "tags": tags, }, load_balancer_update_params.LoadBalancerUpdateParams, ), diff --git a/src/gcore/resources/cloud/networks/networks.py b/src/gcore/resources/cloud/networks/networks.py index d980ebfa..9647db97 100644 --- a/src/gcore/resources/cloud/networks/networks.py +++ b/src/gcore/resources/cloud/networks/networks.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import List +from typing import Dict, List, Optional from typing_extensions import Literal import httpx @@ -78,7 +78,7 @@ def create( region_id: int | None = None, name: str, create_router: bool | NotGiven = NOT_GIVEN, - tags: TagUpdateMapParam | NotGiven = NOT_GIVEN, + tags: Dict[str, str] | NotGiven = NOT_GIVEN, type: Literal["vlan", "vxlan"] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -142,7 +142,8 @@ def update( *, project_id: int | None = None, region_id: int | None = None, - name: str, + name: str | NotGiven = NOT_GIVEN, + tags: Optional[TagUpdateMapParam] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -150,8 +151,11 @@ def update( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Network: - """ - Change network name + """Rename network and/or update network tags. + + The request will only process the + fields that are provided in the request body. Any fields that are not included + will remain unchanged. Args: project_id: Project ID @@ -162,6 +166,23 @@ def update( name: Name. + tags: Update key-value tags using JSON Merge Patch semantics (RFC 7386). Provide + key-value pairs to add or update tags. Set tag values to `null` to remove tags. + Unspecified tags remain unchanged. **Examples:** + + - **Add/update tags:** + `{'tags': {'environment': 'production', 'team': 'backend'}}` adds new tags or + updates existing ones. + - **Delete tags:** `{'tags': {'`old_tag`': null}}` removes specific tags. + - **Partial update:** `{'tags': {'environment': 'staging'}}` only updates + specified tags. + - **Mixed operations:** + `{'tags': {'environment': 'production', '`cost_center`': 'engineering', '`deprecated_tag`': null}}` + adds/updates 'environment' and '`cost_center`' while removing + '`deprecated_tag`', preserving other existing tags. + - **Replace all:** first delete existing tags with null values, then add new + ones in the same request. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -178,7 +199,13 @@ def update( raise ValueError(f"Expected a non-empty value for `network_id` but received {network_id!r}") return self._patch( f"/cloud/v1/networks/{project_id}/{region_id}/{network_id}", - body=maybe_transform({"name": name}, network_update_params.NetworkUpdateParams), + body=maybe_transform( + { + "name": name, + "tags": tags, + }, + network_update_params.NetworkUpdateParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -191,6 +218,7 @@ def list( project_id: int | None = None, region_id: int | None = None, limit: int | NotGiven = NOT_GIVEN, + name: str | NotGiven = NOT_GIVEN, offset: int | NotGiven = NOT_GIVEN, order_by: Literal["created_at.asc", "created_at.desc", "name.asc", "name.desc"] | NotGiven = NOT_GIVEN, tag_key: List[str] | NotGiven = NOT_GIVEN, @@ -212,6 +240,8 @@ def list( limit: Optional. Limit the number of returned items + name: Filter networks by name + offset: Optional. Offset value is used to exclude the first set of records from the result @@ -247,6 +277,7 @@ def list( query=maybe_transform( { "limit": limit, + "name": name, "offset": offset, "order_by": order_by, "tag_key": tag_key, @@ -384,7 +415,7 @@ async def create( region_id: int | None = None, name: str, create_router: bool | NotGiven = NOT_GIVEN, - tags: TagUpdateMapParam | NotGiven = NOT_GIVEN, + tags: Dict[str, str] | NotGiven = NOT_GIVEN, type: Literal["vlan", "vxlan"] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -448,7 +479,8 @@ async def update( *, project_id: int | None = None, region_id: int | None = None, - name: str, + name: str | NotGiven = NOT_GIVEN, + tags: Optional[TagUpdateMapParam] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -456,8 +488,11 @@ async def update( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Network: - """ - Change network name + """Rename network and/or update network tags. + + The request will only process the + fields that are provided in the request body. Any fields that are not included + will remain unchanged. Args: project_id: Project ID @@ -468,6 +503,23 @@ async def update( name: Name. + tags: Update key-value tags using JSON Merge Patch semantics (RFC 7386). Provide + key-value pairs to add or update tags. Set tag values to `null` to remove tags. + Unspecified tags remain unchanged. **Examples:** + + - **Add/update tags:** + `{'tags': {'environment': 'production', 'team': 'backend'}}` adds new tags or + updates existing ones. + - **Delete tags:** `{'tags': {'`old_tag`': null}}` removes specific tags. + - **Partial update:** `{'tags': {'environment': 'staging'}}` only updates + specified tags. + - **Mixed operations:** + `{'tags': {'environment': 'production', '`cost_center`': 'engineering', '`deprecated_tag`': null}}` + adds/updates 'environment' and '`cost_center`' while removing + '`deprecated_tag`', preserving other existing tags. + - **Replace all:** first delete existing tags with null values, then add new + ones in the same request. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -484,7 +536,13 @@ async def update( raise ValueError(f"Expected a non-empty value for `network_id` but received {network_id!r}") return await self._patch( f"/cloud/v1/networks/{project_id}/{region_id}/{network_id}", - body=await async_maybe_transform({"name": name}, network_update_params.NetworkUpdateParams), + body=await async_maybe_transform( + { + "name": name, + "tags": tags, + }, + network_update_params.NetworkUpdateParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -497,6 +555,7 @@ def list( project_id: int | None = None, region_id: int | None = None, limit: int | NotGiven = NOT_GIVEN, + name: str | NotGiven = NOT_GIVEN, offset: int | NotGiven = NOT_GIVEN, order_by: Literal["created_at.asc", "created_at.desc", "name.asc", "name.desc"] | NotGiven = NOT_GIVEN, tag_key: List[str] | NotGiven = NOT_GIVEN, @@ -518,6 +577,8 @@ def list( limit: Optional. Limit the number of returned items + name: Filter networks by name + offset: Optional. Offset value is used to exclude the first set of records from the result @@ -553,6 +614,7 @@ def list( query=maybe_transform( { "limit": limit, + "name": name, "offset": offset, "order_by": order_by, "tag_key": tag_key, diff --git a/src/gcore/resources/cloud/networks/subnets.py b/src/gcore/resources/cloud/networks/subnets.py index b65a97e0..311bd170 100644 --- a/src/gcore/resources/cloud/networks/subnets.py +++ b/src/gcore/resources/cloud/networks/subnets.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import List, Iterable, Optional +from typing import Dict, List, Iterable, Optional from typing_extensions import Literal import httpx @@ -64,7 +64,7 @@ def create( host_routes: Optional[Iterable[subnet_create_params.HostRoute]] | NotGiven = NOT_GIVEN, ip_version: IPVersion | NotGiven = NOT_GIVEN, router_id_to_connect: Optional[str] | NotGiven = NOT_GIVEN, - tags: TagUpdateMapParam | NotGiven = NOT_GIVEN, + tags: Dict[str, str] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -159,6 +159,7 @@ def update( gateway_ip: Optional[str] | NotGiven = NOT_GIVEN, host_routes: Optional[Iterable[subnet_update_params.HostRoute]] | NotGiven = NOT_GIVEN, name: Optional[str] | NotGiven = NOT_GIVEN, + tags: Optional[TagUpdateMapParam] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -167,7 +168,7 @@ def update( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Subnet: """ - Change subnet properties + Update subnet Args: project_id: Project ID @@ -189,6 +190,23 @@ def update( name: Name + tags: Update key-value tags using JSON Merge Patch semantics (RFC 7386). Provide + key-value pairs to add or update tags. Set tag values to `null` to remove tags. + Unspecified tags remain unchanged. **Examples:** + + - **Add/update tags:** + `{'tags': {'environment': 'production', 'team': 'backend'}}` adds new tags or + updates existing ones. + - **Delete tags:** `{'tags': {'`old_tag`': null}}` removes specific tags. + - **Partial update:** `{'tags': {'environment': 'staging'}}` only updates + specified tags. + - **Mixed operations:** + `{'tags': {'environment': 'production', '`cost_center`': 'engineering', '`deprecated_tag`': null}}` + adds/updates 'environment' and '`cost_center`' while removing + '`deprecated_tag`', preserving other existing tags. + - **Replace all:** first delete existing tags with null values, then add new + ones in the same request. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -212,6 +230,7 @@ def update( "gateway_ip": gateway_ip, "host_routes": host_routes, "name": name, + "tags": tags, }, subnet_update_params.SubnetUpdateParams, ), @@ -440,7 +459,7 @@ async def create( host_routes: Optional[Iterable[subnet_create_params.HostRoute]] | NotGiven = NOT_GIVEN, ip_version: IPVersion | NotGiven = NOT_GIVEN, router_id_to_connect: Optional[str] | NotGiven = NOT_GIVEN, - tags: TagUpdateMapParam | NotGiven = NOT_GIVEN, + tags: Dict[str, str] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -535,6 +554,7 @@ async def update( gateway_ip: Optional[str] | NotGiven = NOT_GIVEN, host_routes: Optional[Iterable[subnet_update_params.HostRoute]] | NotGiven = NOT_GIVEN, name: Optional[str] | NotGiven = NOT_GIVEN, + tags: Optional[TagUpdateMapParam] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -543,7 +563,7 @@ async def update( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Subnet: """ - Change subnet properties + Update subnet Args: project_id: Project ID @@ -565,6 +585,23 @@ async def update( name: Name + tags: Update key-value tags using JSON Merge Patch semantics (RFC 7386). Provide + key-value pairs to add or update tags. Set tag values to `null` to remove tags. + Unspecified tags remain unchanged. **Examples:** + + - **Add/update tags:** + `{'tags': {'environment': 'production', 'team': 'backend'}}` adds new tags or + updates existing ones. + - **Delete tags:** `{'tags': {'`old_tag`': null}}` removes specific tags. + - **Partial update:** `{'tags': {'environment': 'staging'}}` only updates + specified tags. + - **Mixed operations:** + `{'tags': {'environment': 'production', '`cost_center`': 'engineering', '`deprecated_tag`': null}}` + adds/updates 'environment' and '`cost_center`' while removing + '`deprecated_tag`', preserving other existing tags. + - **Replace all:** first delete existing tags with null values, then add new + ones in the same request. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -588,6 +625,7 @@ async def update( "gateway_ip": gateway_ip, "host_routes": host_routes, "name": name, + "tags": tags, }, subnet_update_params.SubnetUpdateParams, ), diff --git a/src/gcore/resources/cloud/security_groups/security_groups.py b/src/gcore/resources/cloud/security_groups/security_groups.py index 51a470d1..cddbd7a2 100644 --- a/src/gcore/resources/cloud/security_groups/security_groups.py +++ b/src/gcore/resources/cloud/security_groups/security_groups.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import List, Iterable +from typing import List, Iterable, Optional import httpx @@ -33,6 +33,7 @@ ) from ...._base_client import AsyncPaginator, make_request_options from ....types.cloud.security_group import SecurityGroup +from ....types.cloud.tag_update_map_param import TagUpdateMapParam __all__ = ["SecurityGroupsResource", "AsyncSecurityGroupsResource"] @@ -118,6 +119,7 @@ def update( region_id: int | None = None, changed_rules: Iterable[security_group_update_params.ChangedRule] | NotGiven = NOT_GIVEN, name: str | NotGiven = NOT_GIVEN, + tags: Optional[TagUpdateMapParam] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -126,13 +128,30 @@ def update( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SecurityGroup: """ - Change security group + Update security group Args: changed_rules: List of rules to create or delete name: Name + tags: Update key-value tags using JSON Merge Patch semantics (RFC 7386). Provide + key-value pairs to add or update tags. Set tag values to `null` to remove tags. + Unspecified tags remain unchanged. **Examples:** + + - **Add/update tags:** + `{'tags': {'environment': 'production', 'team': 'backend'}}` adds new tags or + updates existing ones. + - **Delete tags:** `{'tags': {'`old_tag`': null}}` removes specific tags. + - **Partial update:** `{'tags': {'environment': 'staging'}}` only updates + specified tags. + - **Mixed operations:** + `{'tags': {'environment': 'production', '`cost_center`': 'engineering', '`deprecated_tag`': null}}` + adds/updates 'environment' and '`cost_center`' while removing + '`deprecated_tag`', preserving other existing tags. + - **Replace all:** first delete existing tags with null values, then add new + ones in the same request. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -153,6 +172,7 @@ def update( { "changed_rules": changed_rules, "name": name, + "tags": tags, }, security_group_update_params.SecurityGroupUpdateParams, ), @@ -469,6 +489,7 @@ async def update( region_id: int | None = None, changed_rules: Iterable[security_group_update_params.ChangedRule] | NotGiven = NOT_GIVEN, name: str | NotGiven = NOT_GIVEN, + tags: Optional[TagUpdateMapParam] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -477,13 +498,30 @@ async def update( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SecurityGroup: """ - Change security group + Update security group Args: changed_rules: List of rules to create or delete name: Name + tags: Update key-value tags using JSON Merge Patch semantics (RFC 7386). Provide + key-value pairs to add or update tags. Set tag values to `null` to remove tags. + Unspecified tags remain unchanged. **Examples:** + + - **Add/update tags:** + `{'tags': {'environment': 'production', 'team': 'backend'}}` adds new tags or + updates existing ones. + - **Delete tags:** `{'tags': {'`old_tag`': null}}` removes specific tags. + - **Partial update:** `{'tags': {'environment': 'staging'}}` only updates + specified tags. + - **Mixed operations:** + `{'tags': {'environment': 'production', '`cost_center`': 'engineering', '`deprecated_tag`': null}}` + adds/updates 'environment' and '`cost_center`' while removing + '`deprecated_tag`', preserving other existing tags. + - **Replace all:** first delete existing tags with null values, then add new + ones in the same request. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -504,6 +542,7 @@ async def update( { "changed_rules": changed_rules, "name": name, + "tags": tags, }, security_group_update_params.SecurityGroupUpdateParams, ), diff --git a/src/gcore/types/cloud/baremetal/server_create_params.py b/src/gcore/types/cloud/baremetal/server_create_params.py index 9c6dc5d1..3b9981b3 100644 --- a/src/gcore/types/cloud/baremetal/server_create_params.py +++ b/src/gcore/types/cloud/baremetal/server_create_params.py @@ -2,11 +2,10 @@ from __future__ import annotations -from typing import Union, Iterable, Optional +from typing import Dict, Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from ..interface_ip_family import InterfaceIPFamily -from ..tag_update_map_param import TagUpdateMapParam __all__ = [ "ServerCreateParams", @@ -88,7 +87,7 @@ class ServerCreateParams(TypedDict, total=False): [/v1/`ssh_keys` endpoint](/docs/api-reference/ssh-keys/add-or-generate-ssh-key). """ - tags: TagUpdateMapParam + tags: Dict[str, str] """Key-value tags to associate with the resource. A tag is a key-value pair that can be associated with a resource, enabling @@ -361,10 +360,7 @@ class DDOSProfileField(TypedDict, total=False): class DDOSProfile(TypedDict, total=False): profile_template: Required[int] - """DDoS profile template ID""" + """Advanced DDoS template ID""" - fields: Optional[Iterable[DDOSProfileField]] + fields: Iterable[DDOSProfileField] """DDoS profile parameters""" - - profile_template_name: Optional[str] - """DDoS profile template name""" diff --git a/src/gcore/types/cloud/ddos_profile.py b/src/gcore/types/cloud/ddos_profile.py index f84c69cd..bc242300 100644 --- a/src/gcore/types/cloud/ddos_profile.py +++ b/src/gcore/types/cloud/ddos_profile.py @@ -8,14 +8,20 @@ from .ddos_profile_template import DDOSProfileTemplate from .ddos_profile_option_list import DDOSProfileOptionList -__all__ = ["DDOSProfile"] +__all__ = ["DDOSProfile", "Protocol"] + + +class Protocol(BaseModel): + port: str + + protocols: List[str] class DDOSProfile(BaseModel): id: int """DDoS protection profile ID""" - profile_template: DDOSProfileTemplate + profile_template: Optional[DDOSProfileTemplate] = None """Template data""" fields: Optional[List[DDOSProfileField]] = None @@ -25,7 +31,7 @@ class DDOSProfile(BaseModel): profile_template_description: Optional[str] = None """DDoS profile template description""" - protocols: Optional[List[object]] = None + protocols: Optional[List[Protocol]] = None """List of protocols""" site: Optional[str] = None diff --git a/src/gcore/types/cloud/file_share_create_params.py b/src/gcore/types/cloud/file_share_create_params.py index 02fe341b..7958fe69 100644 --- a/src/gcore/types/cloud/file_share_create_params.py +++ b/src/gcore/types/cloud/file_share_create_params.py @@ -2,11 +2,9 @@ from __future__ import annotations -from typing import Union, Iterable +from typing import Dict, Union, Iterable from typing_extensions import Literal, Required, TypeAlias, TypedDict -from .tag_update_map_param import TagUpdateMapParam - __all__ = [ "FileShareCreateParams", "CreateStandardFileShareSerializer", @@ -38,7 +36,7 @@ class CreateStandardFileShareSerializer(TypedDict, total=False): access: Iterable[CreateStandardFileShareSerializerAccess] """Access Rules""" - tags: TagUpdateMapParam + tags: Dict[str, str] """Key-value tags to associate with the resource. A tag is a key-value pair that can be associated with a resource, enabling @@ -90,7 +88,7 @@ class CreateVastFileShareSerializer(TypedDict, total=False): volume_type: Required[Literal["vast_share_type"]] """File share volume type""" - tags: TagUpdateMapParam + tags: Dict[str, str] """Key-value tags to associate with the resource. A tag is a key-value pair that can be associated with a resource, enabling diff --git a/src/gcore/types/cloud/floating_ip_create_params.py b/src/gcore/types/cloud/floating_ip_create_params.py index 06936bd7..01d7f41b 100644 --- a/src/gcore/types/cloud/floating_ip_create_params.py +++ b/src/gcore/types/cloud/floating_ip_create_params.py @@ -2,11 +2,9 @@ from __future__ import annotations -from typing import Optional +from typing import Dict, Optional from typing_extensions import TypedDict -from .tag_update_map_param import TagUpdateMapParam - __all__ = ["FloatingIPCreateParams"] @@ -29,7 +27,7 @@ class FloatingIPCreateParams(TypedDict, total=False): If provided, the floating IP will be immediately attached to the specified port. """ - tags: TagUpdateMapParam + tags: Dict[str, str] """Key-value tags to associate with the resource. A tag is a key-value pair that can be associated with a resource, enabling diff --git a/src/gcore/types/cloud/gpu_baremetal_cluster_create_params.py b/src/gcore/types/cloud/gpu_baremetal_cluster_create_params.py index 210346b4..7a82c03b 100644 --- a/src/gcore/types/cloud/gpu_baremetal_cluster_create_params.py +++ b/src/gcore/types/cloud/gpu_baremetal_cluster_create_params.py @@ -2,11 +2,10 @@ from __future__ import annotations -from typing import Union, Iterable, Optional +from typing import Dict, Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from .interface_ip_family import InterfaceIPFamily -from .tag_update_map_param import TagUpdateMapParam __all__ = [ "GPUBaremetalClusterCreateParams", @@ -55,7 +54,7 @@ class GPUBaremetalClusterCreateParams(TypedDict, total=False): [/v1/`ssh_keys` endpoint](/docs/api-reference/ssh-keys/add-or-generate-ssh-key). """ - tags: TagUpdateMapParam + tags: Dict[str, str] """Key-value tags to associate with the resource. A tag is a key-value pair that can be associated with a resource, enabling diff --git a/src/gcore/types/cloud/gpu_baremetal_clusters/image_upload_params.py b/src/gcore/types/cloud/gpu_baremetal_clusters/image_upload_params.py index eff9ad1d..c83f5687 100644 --- a/src/gcore/types/cloud/gpu_baremetal_clusters/image_upload_params.py +++ b/src/gcore/types/cloud/gpu_baremetal_clusters/image_upload_params.py @@ -2,11 +2,9 @@ from __future__ import annotations -from typing import Optional +from typing import Dict, Optional from typing_extensions import Literal, Required, TypedDict -from ..tag_update_map_param import TagUpdateMapParam - __all__ = ["ImageUploadParams"] @@ -47,7 +45,7 @@ class ImageUploadParams(TypedDict, total=False): ssh_key: Literal["allow", "deny", "required"] """Permission to use a ssh key in instances""" - tags: TagUpdateMapParam + tags: Dict[str, str] """Key-value tags to associate with the resource. A tag is a key-value pair that can be associated with a resource, enabling diff --git a/src/gcore/types/cloud/instance_create_params.py b/src/gcore/types/cloud/instance_create_params.py index 3f6762bc..0f16aa79 100644 --- a/src/gcore/types/cloud/instance_create_params.py +++ b/src/gcore/types/cloud/instance_create_params.py @@ -2,11 +2,10 @@ from __future__ import annotations -from typing import Union, Iterable, Optional +from typing import Dict, Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from .interface_ip_family import InterfaceIPFamily -from .tag_update_map_param import TagUpdateMapParam __all__ = [ "InstanceCreateParams", @@ -113,7 +112,7 @@ class InstanceCreateParams(TypedDict, total=False): [/v1/`ssh_keys` endpoint](/docs/api-reference/ssh-keys/add-or-generate-ssh-key). """ - tags: TagUpdateMapParam + tags: Dict[str, str] """Key-value tags to associate with the resource. A tag is a key-value pair that can be associated with a resource, enabling @@ -389,7 +388,7 @@ class VolumeCreateInstanceCreateNewVolumeSerializer(TypedDict, total=False): If not specified, a name will be generated automatically. """ - tags: TagUpdateMapParam + tags: Dict[str, str] """Key-value tags to associate with the resource. A tag is a key-value pair that can be associated with a resource, enabling @@ -450,7 +449,7 @@ class VolumeCreateInstanceCreateVolumeFromImageSerializer(TypedDict, total=False - For basic VMs: the size is set automatically based on the flavor. """ - tags: TagUpdateMapParam + tags: Dict[str, str] """Key-value tags to associate with the resource. A tag is a key-value pair that can be associated with a resource, enabling @@ -504,7 +503,7 @@ class VolumeCreateInstanceCreateVolumeFromSnapshotSerializer(TypedDict, total=Fa If not specified, a name will be generated automatically. """ - tags: TagUpdateMapParam + tags: Dict[str, str] """Key-value tags to associate with the resource. A tag is a key-value pair that can be associated with a resource, enabling @@ -550,7 +549,7 @@ class VolumeCreateInstanceCreateVolumeFromApptemplateSerializer(TypedDict, total size: int """Volume size in GiB.""" - tags: TagUpdateMapParam + tags: Dict[str, str] """Key-value tags to associate with the resource. A tag is a key-value pair that can be associated with a resource, enabling @@ -595,7 +594,7 @@ class VolumeCreateInstanceExistingVolumeSerializer(TypedDict, total=False): delete_on_termination: bool """Set to `true` to automatically delete the volume when the instance is deleted.""" - tags: TagUpdateMapParam + tags: Dict[str, str] """Key-value tags to associate with the resource. A tag is a key-value pair that can be associated with a resource, enabling diff --git a/src/gcore/types/cloud/instances/image_create_from_volume_params.py b/src/gcore/types/cloud/instances/image_create_from_volume_params.py index 358a4a7d..70dd351f 100644 --- a/src/gcore/types/cloud/instances/image_create_from_volume_params.py +++ b/src/gcore/types/cloud/instances/image_create_from_volume_params.py @@ -2,11 +2,9 @@ from __future__ import annotations -from typing import Optional +from typing import Dict, Optional from typing_extensions import Literal, Required, TypedDict -from ..tag_update_map_param import TagUpdateMapParam - __all__ = ["ImageCreateFromVolumeParams"] @@ -42,7 +40,7 @@ class ImageCreateFromVolumeParams(TypedDict, total=False): ssh_key: Literal["allow", "deny", "required"] """Whether the image supports SSH key or not""" - tags: TagUpdateMapParam + tags: Dict[str, str] """Key-value tags to associate with the resource. A tag is a key-value pair that can be associated with a resource, enabling diff --git a/src/gcore/types/cloud/instances/image_upload_params.py b/src/gcore/types/cloud/instances/image_upload_params.py index 7a278c91..08cfe96f 100644 --- a/src/gcore/types/cloud/instances/image_upload_params.py +++ b/src/gcore/types/cloud/instances/image_upload_params.py @@ -2,11 +2,9 @@ from __future__ import annotations -from typing import Optional +from typing import Dict, Optional from typing_extensions import Literal, Required, TypedDict -from ..tag_update_map_param import TagUpdateMapParam - __all__ = ["ImageUploadParams"] @@ -51,7 +49,7 @@ class ImageUploadParams(TypedDict, total=False): ssh_key: Literal["allow", "deny", "required"] """Whether the image supports SSH key or not""" - tags: TagUpdateMapParam + tags: Dict[str, str] """Key-value tags to associate with the resource. A tag is a key-value pair that can be associated with a resource, enabling diff --git a/src/gcore/types/cloud/load_balancer_create_params.py b/src/gcore/types/cloud/load_balancer_create_params.py index c208e7fa..6d1739de 100644 --- a/src/gcore/types/cloud/load_balancer_create_params.py +++ b/src/gcore/types/cloud/load_balancer_create_params.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import List, Union, Iterable, Optional +from typing import Dict, List, Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from .http_method import HTTPMethod @@ -10,7 +10,6 @@ from .lb_pool_protocol import LbPoolProtocol from .interface_ip_family import InterfaceIPFamily from .lb_listener_protocol import LbListenerProtocol -from .tag_update_map_param import TagUpdateMapParam from .lb_health_monitor_type import LbHealthMonitorType from .lb_session_persistence_type import LbSessionPersistenceType from .laas_index_retention_policy_param import LaasIndexRetentionPolicyParam @@ -66,7 +65,7 @@ class LoadBalancerCreateParams(TypedDict, total=False): specification. """ - tags: TagUpdateMapParam + tags: Dict[str, str] """Key-value tags to associate with the resource. A tag is a key-value pair that can be associated with a resource, enabling diff --git a/src/gcore/types/cloud/load_balancer_update_params.py b/src/gcore/types/cloud/load_balancer_update_params.py index 9b7653fe..4747c719 100644 --- a/src/gcore/types/cloud/load_balancer_update_params.py +++ b/src/gcore/types/cloud/load_balancer_update_params.py @@ -5,6 +5,7 @@ from typing import Optional from typing_extensions import TypedDict +from .tag_update_map_param import TagUpdateMapParam from .laas_index_retention_policy_param import LaasIndexRetentionPolicyParam from .load_balancer_member_connectivity import LoadBalancerMemberConnectivity @@ -28,6 +29,26 @@ class LoadBalancerUpdateParams(TypedDict, total=False): members """ + tags: Optional[TagUpdateMapParam] + """Update key-value tags using JSON Merge Patch semantics (RFC 7386). + + Provide key-value pairs to add or update tags. Set tag values to `null` to + remove tags. Unspecified tags remain unchanged. **Examples:** + + - **Add/update tags:** + `{'tags': {'environment': 'production', 'team': 'backend'}}` adds new tags or + updates existing ones. + - **Delete tags:** `{'tags': {'`old_tag`': null}}` removes specific tags. + - **Partial update:** `{'tags': {'environment': 'staging'}}` only updates + specified tags. + - **Mixed operations:** + `{'tags': {'environment': 'production', '`cost_center`': 'engineering', '`deprecated_tag`': null}}` + adds/updates 'environment' and '`cost_center`' while removing + '`deprecated_tag`', preserving other existing tags. + - **Replace all:** first delete existing tags with null values, then add new + ones in the same request. + """ + class Logging(TypedDict, total=False): destination_region_id: Optional[int] diff --git a/src/gcore/types/cloud/network_create_params.py b/src/gcore/types/cloud/network_create_params.py index a91d5bec..da45b25c 100644 --- a/src/gcore/types/cloud/network_create_params.py +++ b/src/gcore/types/cloud/network_create_params.py @@ -2,10 +2,9 @@ from __future__ import annotations +from typing import Dict from typing_extensions import Literal, Required, TypedDict -from .tag_update_map_param import TagUpdateMapParam - __all__ = ["NetworkCreateParams"] @@ -22,7 +21,7 @@ class NetworkCreateParams(TypedDict, total=False): create_router: bool """Defaults to True""" - tags: TagUpdateMapParam + tags: Dict[str, str] """Key-value tags to associate with the resource. A tag is a key-value pair that can be associated with a resource, enabling diff --git a/src/gcore/types/cloud/network_list_params.py b/src/gcore/types/cloud/network_list_params.py index 95d7c10f..2aaa17b2 100644 --- a/src/gcore/types/cloud/network_list_params.py +++ b/src/gcore/types/cloud/network_list_params.py @@ -18,6 +18,9 @@ class NetworkListParams(TypedDict, total=False): limit: int """Optional. Limit the number of returned items""" + name: str + """Filter networks by name""" + offset: int """Optional. diff --git a/src/gcore/types/cloud/network_update_params.py b/src/gcore/types/cloud/network_update_params.py index 8319b067..828b283c 100644 --- a/src/gcore/types/cloud/network_update_params.py +++ b/src/gcore/types/cloud/network_update_params.py @@ -2,7 +2,10 @@ from __future__ import annotations -from typing_extensions import Required, TypedDict +from typing import Optional +from typing_extensions import TypedDict + +from .tag_update_map_param import TagUpdateMapParam __all__ = ["NetworkUpdateParams"] @@ -14,5 +17,25 @@ class NetworkUpdateParams(TypedDict, total=False): region_id: int """Region ID""" - name: Required[str] + name: str """Name.""" + + tags: Optional[TagUpdateMapParam] + """Update key-value tags using JSON Merge Patch semantics (RFC 7386). + + Provide key-value pairs to add or update tags. Set tag values to `null` to + remove tags. Unspecified tags remain unchanged. **Examples:** + + - **Add/update tags:** + `{'tags': {'environment': 'production', 'team': 'backend'}}` adds new tags or + updates existing ones. + - **Delete tags:** `{'tags': {'`old_tag`': null}}` removes specific tags. + - **Partial update:** `{'tags': {'environment': 'staging'}}` only updates + specified tags. + - **Mixed operations:** + `{'tags': {'environment': 'production', '`cost_center`': 'engineering', '`deprecated_tag`': null}}` + adds/updates 'environment' and '`cost_center`' while removing + '`deprecated_tag`', preserving other existing tags. + - **Replace all:** first delete existing tags with null values, then add new + ones in the same request. + """ diff --git a/src/gcore/types/cloud/networks/subnet_create_params.py b/src/gcore/types/cloud/networks/subnet_create_params.py index c9371c7a..9fd4a938 100644 --- a/src/gcore/types/cloud/networks/subnet_create_params.py +++ b/src/gcore/types/cloud/networks/subnet_create_params.py @@ -2,11 +2,10 @@ from __future__ import annotations -from typing import List, Iterable, Optional +from typing import Dict, List, Iterable, Optional from typing_extensions import Required, TypedDict from ..ip_version import IPVersion -from ..tag_update_map_param import TagUpdateMapParam __all__ = ["SubnetCreateParams", "HostRoute"] @@ -60,7 +59,7 @@ class SubnetCreateParams(TypedDict, total=False): find a router created during network creation. """ - tags: TagUpdateMapParam + tags: Dict[str, str] """Key-value tags to associate with the resource. A tag is a key-value pair that can be associated with a resource, enabling diff --git a/src/gcore/types/cloud/networks/subnet_update_params.py b/src/gcore/types/cloud/networks/subnet_update_params.py index 7fb218da..2ade80c6 100644 --- a/src/gcore/types/cloud/networks/subnet_update_params.py +++ b/src/gcore/types/cloud/networks/subnet_update_params.py @@ -5,6 +5,8 @@ from typing import List, Iterable, Optional from typing_extensions import Required, TypedDict +from ..tag_update_map_param import TagUpdateMapParam + __all__ = ["SubnetUpdateParams", "HostRoute"] @@ -35,6 +37,26 @@ class SubnetUpdateParams(TypedDict, total=False): name: Optional[str] """Name""" + tags: Optional[TagUpdateMapParam] + """Update key-value tags using JSON Merge Patch semantics (RFC 7386). + + Provide key-value pairs to add or update tags. Set tag values to `null` to + remove tags. Unspecified tags remain unchanged. **Examples:** + + - **Add/update tags:** + `{'tags': {'environment': 'production', 'team': 'backend'}}` adds new tags or + updates existing ones. + - **Delete tags:** `{'tags': {'`old_tag`': null}}` removes specific tags. + - **Partial update:** `{'tags': {'environment': 'staging'}}` only updates + specified tags. + - **Mixed operations:** + `{'tags': {'environment': 'production', '`cost_center`': 'engineering', '`deprecated_tag`': null}}` + adds/updates 'environment' and '`cost_center`' while removing + '`deprecated_tag`', preserving other existing tags. + - **Replace all:** first delete existing tags with null values, then add new + ones in the same request. + """ + class HostRoute(TypedDict, total=False): destination: Required[str] diff --git a/src/gcore/types/cloud/security_group_update_params.py b/src/gcore/types/cloud/security_group_update_params.py index d2118275..f94acd2b 100644 --- a/src/gcore/types/cloud/security_group_update_params.py +++ b/src/gcore/types/cloud/security_group_update_params.py @@ -5,6 +5,8 @@ from typing import Iterable, Optional from typing_extensions import Literal, Required, TypedDict +from .tag_update_map_param import TagUpdateMapParam + __all__ = ["SecurityGroupUpdateParams", "ChangedRule"] @@ -19,6 +21,26 @@ class SecurityGroupUpdateParams(TypedDict, total=False): name: str """Name""" + tags: Optional[TagUpdateMapParam] + """Update key-value tags using JSON Merge Patch semantics (RFC 7386). + + Provide key-value pairs to add or update tags. Set tag values to `null` to + remove tags. Unspecified tags remain unchanged. **Examples:** + + - **Add/update tags:** + `{'tags': {'environment': 'production', 'team': 'backend'}}` adds new tags or + updates existing ones. + - **Delete tags:** `{'tags': {'`old_tag`': null}}` removes specific tags. + - **Partial update:** `{'tags': {'environment': 'staging'}}` only updates + specified tags. + - **Mixed operations:** + `{'tags': {'environment': 'production', '`cost_center`': 'engineering', '`deprecated_tag`': null}}` + adds/updates 'environment' and '`cost_center`' while removing + '`deprecated_tag`', preserving other existing tags. + - **Replace all:** first delete existing tags with null values, then add new + ones in the same request. + """ + class ChangedRule(TypedDict, total=False): action: Required[Literal["create", "delete"]] diff --git a/src/gcore/types/cloud/tag_update_map_param.py b/src/gcore/types/cloud/tag_update_map_param.py index a1eff214..6726649c 100644 --- a/src/gcore/types/cloud/tag_update_map_param.py +++ b/src/gcore/types/cloud/tag_update_map_param.py @@ -2,9 +2,9 @@ from __future__ import annotations -from typing import Dict +from typing import Dict, Optional from typing_extensions import TypeAlias __all__ = ["TagUpdateMapParam"] -TagUpdateMapParam: TypeAlias = Dict[str, str] +TagUpdateMapParam: TypeAlias = Dict[str, Optional[str]] diff --git a/tests/api_resources/cloud/baremetal/test_servers.py b/tests/api_resources/cloud/baremetal/test_servers.py index 347210d3..72ff981b 100644 --- a/tests/api_resources/cloud/baremetal/test_servers.py +++ b/tests/api_resources/cloud/baremetal/test_servers.py @@ -47,7 +47,7 @@ def test_method_create_with_all_params(self, client: Gcore) -> None: app_config={}, apptemplate_id="apptemplate_id", ddos_profile={ - "profile_template": 1, + "profile_template": 123, "fields": [ { "base_field": 10, @@ -56,14 +56,13 @@ def test_method_create_with_all_params(self, client: Gcore) -> None: "value": "value", } ], - "profile_template_name": "profile_template_name", }, image_id="image_id", name="my-bare-metal", name_template="name_template", password="password", ssh_key_name="my-ssh-key", - tags={"foo": "my-tag-value"}, + tags={"my-tag": "my-tag-value"}, user_data="user_data", username="username", ) @@ -250,7 +249,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncGcore) -> app_config={}, apptemplate_id="apptemplate_id", ddos_profile={ - "profile_template": 1, + "profile_template": 123, "fields": [ { "base_field": 10, @@ -259,14 +258,13 @@ async def test_method_create_with_all_params(self, async_client: AsyncGcore) -> "value": "value", } ], - "profile_template_name": "profile_template_name", }, image_id="image_id", name="my-bare-metal", name_template="name_template", password="password", ssh_key_name="my-ssh-key", - tags={"foo": "my-tag-value"}, + tags={"my-tag": "my-tag-value"}, user_data="user_data", username="username", ) diff --git a/tests/api_resources/cloud/gpu_baremetal_clusters/test_images.py b/tests/api_resources/cloud/gpu_baremetal_clusters/test_images.py index 30e17761..c2b357cb 100644 --- a/tests/api_resources/cloud/gpu_baremetal_clusters/test_images.py +++ b/tests/api_resources/cloud/gpu_baremetal_clusters/test_images.py @@ -167,7 +167,7 @@ def test_method_upload_with_all_params(self, client: Gcore) -> None: os_type="linux", os_version="19.04", ssh_key="allow", - tags={"foo": "my-tag-value"}, + tags={"my-tag": "my-tag-value"}, ) assert_matches_type(TaskIDList, image, path=["response"]) @@ -355,7 +355,7 @@ async def test_method_upload_with_all_params(self, async_client: AsyncGcore) -> os_type="linux", os_version="19.04", ssh_key="allow", - tags={"foo": "my-tag-value"}, + tags={"my-tag": "my-tag-value"}, ) assert_matches_type(TaskIDList, image, path=["response"]) diff --git a/tests/api_resources/cloud/instances/test_images.py b/tests/api_resources/cloud/instances/test_images.py index d796aec9..80a58a38 100644 --- a/tests/api_resources/cloud/instances/test_images.py +++ b/tests/api_resources/cloud/instances/test_images.py @@ -196,7 +196,7 @@ def test_method_create_from_volume_with_all_params(self, client: Gcore) -> None: os_type="linux", source="volume", ssh_key="allow", - tags={"foo": "my-tag-value"}, + tags={"my-tag": "my-tag-value"}, ) assert_matches_type(TaskIDList, image, path=["response"]) @@ -312,7 +312,7 @@ def test_method_upload_with_all_params(self, client: Gcore) -> None: os_type="linux", os_version="22.04", ssh_key="allow", - tags={"foo": "my-tag-value"}, + tags={"my-tag": "my-tag-value"}, ) assert_matches_type(TaskIDList, image, path=["response"]) @@ -529,7 +529,7 @@ async def test_method_create_from_volume_with_all_params(self, async_client: Asy os_type="linux", source="volume", ssh_key="allow", - tags={"foo": "my-tag-value"}, + tags={"my-tag": "my-tag-value"}, ) assert_matches_type(TaskIDList, image, path=["response"]) @@ -645,7 +645,7 @@ async def test_method_upload_with_all_params(self, async_client: AsyncGcore) -> os_type="linux", os_version="22.04", ssh_key="allow", - tags={"foo": "my-tag-value"}, + tags={"my-tag": "my-tag-value"}, ) assert_matches_type(TaskIDList, image, path=["response"]) diff --git a/tests/api_resources/cloud/networks/test_subnets.py b/tests/api_resources/cloud/networks/test_subnets.py index 88244d9c..f399473c 100644 --- a/tests/api_resources/cloud/networks/test_subnets.py +++ b/tests/api_resources/cloud/networks/test_subnets.py @@ -49,7 +49,7 @@ def test_method_create_with_all_params(self, client: Gcore) -> None: ], ip_version=4, router_id_to_connect="00000000-0000-4000-8000-000000000000", - tags={"foo": "my-tag-value"}, + tags={"my-tag": "my-tag-value"}, ) assert_matches_type(TaskIDList, subnet, path=["response"]) @@ -110,6 +110,7 @@ def test_method_update_with_all_params(self, client: Gcore) -> None: } ], name="some_name", + tags={"foo": "my-tag-value"}, ) assert_matches_type(Subnet, subnet, path=["response"]) @@ -325,7 +326,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncGcore) -> ], ip_version=4, router_id_to_connect="00000000-0000-4000-8000-000000000000", - tags={"foo": "my-tag-value"}, + tags={"my-tag": "my-tag-value"}, ) assert_matches_type(TaskIDList, subnet, path=["response"]) @@ -386,6 +387,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncGcore) -> } ], name="some_name", + tags={"foo": "my-tag-value"}, ) assert_matches_type(Subnet, subnet, path=["response"]) diff --git a/tests/api_resources/cloud/test_file_shares.py b/tests/api_resources/cloud/test_file_shares.py index 4c547362..62cb137a 100644 --- a/tests/api_resources/cloud/test_file_shares.py +++ b/tests/api_resources/cloud/test_file_shares.py @@ -51,7 +51,7 @@ def test_method_create_with_all_params_overload_1(self, client: Gcore) -> None: "ip_address": "10.0.0.1", } ], - tags={"foo": "my-tag-value"}, + tags={"my-tag": "my-tag-value"}, volume_type="default_share_type", ) assert_matches_type(TaskIDList, file_share, path=["response"]) @@ -111,7 +111,7 @@ def test_method_create_with_all_params_overload_2(self, client: Gcore) -> None: protocol="NFS", size=5, volume_type="vast_share_type", - tags={"foo": "my-tag-value"}, + tags={"my-tag": "my-tag-value"}, ) assert_matches_type(TaskIDList, file_share, path=["response"]) @@ -421,7 +421,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn "ip_address": "10.0.0.1", } ], - tags={"foo": "my-tag-value"}, + tags={"my-tag": "my-tag-value"}, volume_type="default_share_type", ) assert_matches_type(TaskIDList, file_share, path=["response"]) @@ -481,7 +481,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn protocol="NFS", size=5, volume_type="vast_share_type", - tags={"foo": "my-tag-value"}, + tags={"my-tag": "my-tag-value"}, ) assert_matches_type(TaskIDList, file_share, path=["response"]) diff --git a/tests/api_resources/cloud/test_floating_ips.py b/tests/api_resources/cloud/test_floating_ips.py index 38d101e5..52a54438 100644 --- a/tests/api_resources/cloud/test_floating_ips.py +++ b/tests/api_resources/cloud/test_floating_ips.py @@ -37,7 +37,7 @@ def test_method_create_with_all_params(self, client: Gcore) -> None: region_id=1, fixed_ip_address="192.168.10.15", port_id="ee2402d0-f0cd-4503-9b75-69be1d11c5f1", - tags={"foo": "my-tag-value"}, + tags={"my-tag": "my-tag-value"}, ) assert_matches_type(TaskIDList, floating_ip, path=["response"]) @@ -331,7 +331,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncGcore) -> region_id=1, fixed_ip_address="192.168.10.15", port_id="ee2402d0-f0cd-4503-9b75-69be1d11c5f1", - tags={"foo": "my-tag-value"}, + tags={"my-tag": "my-tag-value"}, ) assert_matches_type(TaskIDList, floating_ip, path=["response"]) diff --git a/tests/api_resources/cloud/test_gpu_baremetal_clusters.py b/tests/api_resources/cloud/test_gpu_baremetal_clusters.py index 5b455c80..bbb0f9ab 100644 --- a/tests/api_resources/cloud/test_gpu_baremetal_clusters.py +++ b/tests/api_resources/cloud/test_gpu_baremetal_clusters.py @@ -60,7 +60,7 @@ def test_method_create_with_all_params(self, client: Gcore) -> None: instances_count=1, password="password", ssh_key_name="my-ssh-key", - tags={"foo": "my-tag-value"}, + tags={"my-tag": "my-tag-value"}, user_data="user_data", username="username", ) @@ -506,7 +506,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncGcore) -> instances_count=1, password="password", ssh_key_name="my-ssh-key", - tags={"foo": "my-tag-value"}, + tags={"my-tag": "my-tag-value"}, user_data="user_data", username="username", ) diff --git a/tests/api_resources/cloud/test_instances.py b/tests/api_resources/cloud/test_instances.py index c517c82b..6012d388 100644 --- a/tests/api_resources/cloud/test_instances.py +++ b/tests/api_resources/cloud/test_instances.py @@ -61,7 +61,7 @@ def test_method_create_with_all_params(self, client: Gcore) -> None: "attachment_tag": "boot", "delete_on_termination": False, "name": "boot-volume", - "tags": {"foo": "my-tag-value"}, + "tags": {"my-tag": "my-tag-value"}, "type_name": "ssd_hiiops", } ], @@ -73,7 +73,7 @@ def test_method_create_with_all_params(self, client: Gcore) -> None: security_groups=[{"id": "ae74714c-c380-48b4-87f8-758d656cdad6"}], servergroup_id="servergroup_id", ssh_key_name="my-ssh-key", - tags={"foo": "my-tag-value"}, + tags={"my-tag": "my-tag-value"}, user_data="user_data", username="username", ) @@ -922,7 +922,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncGcore) -> "attachment_tag": "boot", "delete_on_termination": False, "name": "boot-volume", - "tags": {"foo": "my-tag-value"}, + "tags": {"my-tag": "my-tag-value"}, "type_name": "ssd_hiiops", } ], @@ -934,7 +934,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncGcore) -> security_groups=[{"id": "ae74714c-c380-48b4-87f8-758d656cdad6"}], servergroup_id="servergroup_id", ssh_key_name="my-ssh-key", - tags={"foo": "my-tag-value"}, + tags={"my-tag": "my-tag-value"}, user_data="user_data", username="username", ) diff --git a/tests/api_resources/cloud/test_load_balancers.py b/tests/api_resources/cloud/test_load_balancers.py index a219b32d..42cc2367 100644 --- a/tests/api_resources/cloud/test_load_balancers.py +++ b/tests/api_resources/cloud/test_load_balancers.py @@ -122,7 +122,7 @@ def test_method_create_with_all_params(self, client: Gcore) -> None: name="new_load_balancer", name_template="lb_name_template", preferred_connectivity="L2", - tags={"foo": "my-tag-value"}, + tags={"my-tag": "my-tag-value"}, vip_ip_family="dual", vip_network_id="ac307687-31a4-4a11-a949-6bea1b2878f5", vip_port_id="ff83e13a-b256-4be2-ba5d-028d3f0ab450", @@ -179,6 +179,7 @@ def test_method_update_with_all_params(self, client: Gcore) -> None: }, name="some_name", preferred_connectivity="L2", + tags={"foo": "my-tag-value"}, ) assert_matches_type(LoadBalancer, load_balancer, path=["response"]) @@ -585,7 +586,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncGcore) -> name="new_load_balancer", name_template="lb_name_template", preferred_connectivity="L2", - tags={"foo": "my-tag-value"}, + tags={"my-tag": "my-tag-value"}, vip_ip_family="dual", vip_network_id="ac307687-31a4-4a11-a949-6bea1b2878f5", vip_port_id="ff83e13a-b256-4be2-ba5d-028d3f0ab450", @@ -642,6 +643,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncGcore) -> }, name="some_name", preferred_connectivity="L2", + tags={"foo": "my-tag-value"}, ) assert_matches_type(LoadBalancer, load_balancer, path=["response"]) diff --git a/tests/api_resources/cloud/test_networks.py b/tests/api_resources/cloud/test_networks.py index 7380d239..18fd0fdf 100644 --- a/tests/api_resources/cloud/test_networks.py +++ b/tests/api_resources/cloud/test_networks.py @@ -37,7 +37,7 @@ def test_method_create_with_all_params(self, client: Gcore) -> None: region_id=1, name="my network", create_router=True, - tags={"foo": "my-tag-value"}, + tags={"my-tag": "my-tag-value"}, type="vxlan", ) assert_matches_type(TaskIDList, network, path=["response"]) @@ -72,11 +72,21 @@ def test_streaming_response_create(self, client: Gcore) -> None: @parametrize def test_method_update(self, client: Gcore) -> None: + network = client.cloud.networks.update( + network_id="b39792c3-3160-4356-912e-ba396c95cdcf", + project_id=1, + region_id=1, + ) + assert_matches_type(Network, network, path=["response"]) + + @parametrize + def test_method_update_with_all_params(self, client: Gcore) -> None: network = client.cloud.networks.update( network_id="b39792c3-3160-4356-912e-ba396c95cdcf", project_id=1, region_id=1, name="some_name", + tags={"foo": "my-tag-value"}, ) assert_matches_type(Network, network, path=["response"]) @@ -86,7 +96,6 @@ def test_raw_response_update(self, client: Gcore) -> None: network_id="b39792c3-3160-4356-912e-ba396c95cdcf", project_id=1, region_id=1, - name="some_name", ) assert response.is_closed is True @@ -100,7 +109,6 @@ def test_streaming_response_update(self, client: Gcore) -> None: network_id="b39792c3-3160-4356-912e-ba396c95cdcf", project_id=1, region_id=1, - name="some_name", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -117,7 +125,6 @@ def test_path_params_update(self, client: Gcore) -> None: network_id="", project_id=1, region_id=1, - name="some_name", ) @parametrize @@ -134,6 +141,7 @@ def test_method_list_with_all_params(self, client: Gcore) -> None: project_id=1, region_id=1, limit=1000, + name="my-network", offset=0, order_by="created_at.desc", tag_key=["key1", "key2"], @@ -279,7 +287,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncGcore) -> region_id=1, name="my network", create_router=True, - tags={"foo": "my-tag-value"}, + tags={"my-tag": "my-tag-value"}, type="vxlan", ) assert_matches_type(TaskIDList, network, path=["response"]) @@ -314,11 +322,21 @@ async def test_streaming_response_create(self, async_client: AsyncGcore) -> None @parametrize async def test_method_update(self, async_client: AsyncGcore) -> None: + network = await async_client.cloud.networks.update( + network_id="b39792c3-3160-4356-912e-ba396c95cdcf", + project_id=1, + region_id=1, + ) + assert_matches_type(Network, network, path=["response"]) + + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncGcore) -> None: network = await async_client.cloud.networks.update( network_id="b39792c3-3160-4356-912e-ba396c95cdcf", project_id=1, region_id=1, name="some_name", + tags={"foo": "my-tag-value"}, ) assert_matches_type(Network, network, path=["response"]) @@ -328,7 +346,6 @@ async def test_raw_response_update(self, async_client: AsyncGcore) -> None: network_id="b39792c3-3160-4356-912e-ba396c95cdcf", project_id=1, region_id=1, - name="some_name", ) assert response.is_closed is True @@ -342,7 +359,6 @@ async def test_streaming_response_update(self, async_client: AsyncGcore) -> None network_id="b39792c3-3160-4356-912e-ba396c95cdcf", project_id=1, region_id=1, - name="some_name", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -359,7 +375,6 @@ async def test_path_params_update(self, async_client: AsyncGcore) -> None: network_id="", project_id=1, region_id=1, - name="some_name", ) @parametrize @@ -376,6 +391,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncGcore) -> No project_id=1, region_id=1, limit=1000, + name="my-network", offset=0, order_by="created_at.desc", tag_key=["key1", "key2"], diff --git a/tests/api_resources/cloud/test_security_groups.py b/tests/api_resources/cloud/test_security_groups.py index 20630068..d8b88654 100644 --- a/tests/api_resources/cloud/test_security_groups.py +++ b/tests/api_resources/cloud/test_security_groups.py @@ -113,6 +113,7 @@ def test_method_update_with_all_params(self, client: Gcore) -> None: } ], name="some_name", + tags={"foo": "my-tag-value"}, ) assert_matches_type(SecurityGroup, security_group, path=["response"]) @@ -484,6 +485,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncGcore) -> } ], name="some_name", + tags={"foo": "my-tag-value"}, ) assert_matches_type(SecurityGroup, security_group, path=["response"]) From f60c2f48f715d64704a85c46a12c6ad99a636ea1 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 17 Jun 2025 12:15:24 +0000 Subject: [PATCH 02/50] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 194bd758..72a98f04 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 302 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-ac4eb65ad579c94b23a363fbf0d27e49daf2fdd4c122b2f096297ff039d52365.yml -openapi_spec_hash: c1286da9cda9769c850ac41dd3216f72 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-c69353c2638a896685622a7bc91746002ce45c684c09f0cd59f90d193a711bfd.yml +openapi_spec_hash: 288f2041f8a716abffdbdf64ae886c63 config_hash: 62229a7a78ca1c79cb7cf61752a8df7e From fbe3508482950455f4ccd02b6941bb0273b2fdf6 Mon Sep 17 00:00:00 2001 From: Danil Krox Date: Tue, 17 Jun 2025 15:17:30 +0200 Subject: [PATCH 03/50] chore: format --- src/gcore/resources/cloud/baremetal/servers.py | 12 ++++++------ .../gpu_baremetal_clusters.py | 4 ++-- .../cloud/inference/deployments/deployments.py | 12 ++++++++++-- .../load_balancers/l7_policies/l7_policies.py | 12 ++++++++++-- .../resources/cloud/load_balancers/listeners.py | 17 +++++++++++++---- .../cloud/load_balancers/load_balancers.py | 12 ++++++++++-- 6 files changed, 51 insertions(+), 18 deletions(-) diff --git a/src/gcore/resources/cloud/baremetal/servers.py b/src/gcore/resources/cloud/baremetal/servers.py index 8c2ebea7..3b031c2a 100644 --- a/src/gcore/resources/cloud/baremetal/servers.py +++ b/src/gcore/resources/cloud/baremetal/servers.py @@ -482,7 +482,7 @@ def rebuild_and_poll( extra_body=extra_body, ) if not response.tasks: - raise ValueError("Expected at least one task to be created") + raise ValueError("Expected at least one task to be created") self._client.cloud.tasks.poll( response.tasks[0], extra_headers=extra_headers, @@ -500,7 +500,7 @@ def rebuild_and_poll( raise ValueError(f"Server {server_id} not found") return servers.results[0] - + class AsyncServersResource(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncServersResourceWithRawResponse: @@ -906,8 +906,8 @@ async def create_and_poll( extra_body=extra_body, ) if not response.tasks or len(response.tasks) != 1: - raise ValueError(f"Expected exactly one task to be created") - task =await self._client.cloud.tasks.poll( + raise ValueError(f"Expected exactly one task to be created") + task = await self._client.cloud.tasks.poll( response.tasks[0], extra_headers=extra_headers, polling_interval_seconds=polling_interval_seconds, @@ -956,7 +956,7 @@ async def rebuild_and_poll( extra_body=extra_body, ) if not response.tasks: - raise ValueError("Expected at least one task to be created") + raise ValueError("Expected at least one task to be created") await self._client.cloud.tasks.poll( response.tasks[0], extra_headers=extra_headers, @@ -972,7 +972,7 @@ async def rebuild_and_poll( ) if len(servers.results) != 1: raise ValueError(f"Server {server_id} not found") - return servers.results[0] + return servers.results[0] class ServersResourceWithRawResponse: diff --git a/src/gcore/resources/cloud/gpu_baremetal_clusters/gpu_baremetal_clusters.py b/src/gcore/resources/cloud/gpu_baremetal_clusters/gpu_baremetal_clusters.py index e37c8d0c..2087a7ba 100644 --- a/src/gcore/resources/cloud/gpu_baremetal_clusters/gpu_baremetal_clusters.py +++ b/src/gcore/resources/cloud/gpu_baremetal_clusters/gpu_baremetal_clusters.py @@ -514,7 +514,7 @@ def resize( if region_id is None: region_id = self._client._get_cloud_region_id_path_param() if not cluster_id: - raise ValueError(f"Expected a non-empty value for `cluster_id` but received {cluster_id!r}") + raise ValueError(f"Expected a non-empty value for `cluster_id` but received {cluster_id!r}") return self._post( f"/cloud/v1/ai/clusters/gpu/{project_id}/{region_id}/{cluster_id}/resize", body=maybe_transform( @@ -1136,7 +1136,7 @@ async def resize( if region_id is None: region_id = self._client._get_cloud_region_id_path_param() if not cluster_id: - raise ValueError(f"Expected a non-empty value for `cluster_id` but received {cluster_id!r}") + raise ValueError(f"Expected a non-empty value for `cluster_id` but received {cluster_id!r}") return await self._post( f"/cloud/v1/ai/clusters/gpu/{project_id}/{region_id}/{cluster_id}/resize", body=await async_maybe_transform( diff --git a/src/gcore/resources/cloud/inference/deployments/deployments.py b/src/gcore/resources/cloud/inference/deployments/deployments.py index 2165e39b..5d660ccb 100644 --- a/src/gcore/resources/cloud/inference/deployments/deployments.py +++ b/src/gcore/resources/cloud/inference/deployments/deployments.py @@ -596,7 +596,11 @@ def create_and_poll( extra_headers=extra_headers, polling_interval_seconds=polling_interval_seconds, ) - if not task.created_resources or not task.created_resources.inference_instances or len(task.created_resources.inference_instances) != 1: + if ( + not task.created_resources + or not task.created_resources.inference_instances + or len(task.created_resources.inference_instances) != 1 + ): raise ValueError(f"Expected exactly one resource to be created in a task") return self.get( deployment_name=task.created_resources.inference_instances[0], @@ -1263,7 +1267,11 @@ async def create_and_poll( extra_headers=extra_headers, polling_interval_seconds=polling_interval_seconds, ) - if not task.created_resources or not task.created_resources.inference_instances or len(task.created_resources.inference_instances) != 1: + if ( + not task.created_resources + or not task.created_resources.inference_instances + or len(task.created_resources.inference_instances) != 1 + ): raise ValueError(f"Expected exactly one resource to be created in a task") return await self.get( deployment_name=task.created_resources.inference_instances[0], diff --git a/src/gcore/resources/cloud/load_balancers/l7_policies/l7_policies.py b/src/gcore/resources/cloud/load_balancers/l7_policies/l7_policies.py index d3966051..ba9f4b28 100644 --- a/src/gcore/resources/cloud/load_balancers/l7_policies/l7_policies.py +++ b/src/gcore/resources/cloud/load_balancers/l7_policies/l7_policies.py @@ -381,7 +381,11 @@ def create_and_poll( extra_headers=extra_headers, polling_interval_seconds=polling_interval_seconds, ) - if not task.created_resources or not task.created_resources.l7polices or len(task.created_resources.l7polices) != 1: + if ( + not task.created_resources + or not task.created_resources.l7polices + or len(task.created_resources.l7polices) != 1 + ): raise ValueError(f"Expected exactly one resource to be created in a task") return self.get( l7policy_id=task.created_resources.l7polices[0], @@ -824,7 +828,11 @@ async def create_and_poll( extra_headers=extra_headers, polling_interval_seconds=polling_interval_seconds, ) - if not task.created_resources or not task.created_resources.l7polices or len(task.created_resources.l7polices) != 1: + if ( + not task.created_resources + or not task.created_resources.l7polices + or len(task.created_resources.l7polices) != 1 + ): raise ValueError(f"Expected exactly one resource to be created in a task") return await self.get( l7policy_id=task.created_resources.l7polices[0], diff --git a/src/gcore/resources/cloud/load_balancers/listeners.py b/src/gcore/resources/cloud/load_balancers/listeners.py index a59db9a8..bdf3c75a 100644 --- a/src/gcore/resources/cloud/load_balancers/listeners.py +++ b/src/gcore/resources/cloud/load_balancers/listeners.py @@ -447,7 +447,11 @@ def create_and_poll( extra_headers=extra_headers, polling_interval_seconds=polling_interval_seconds, ) - if not task.created_resources or not task.created_resources.listeners or len(task.created_resources.listeners) != 1: + if ( + not task.created_resources + or not task.created_resources.listeners + or len(task.created_resources.listeners) != 1 + ): raise ValueError(f"Expected exactly one resource to be created in a task") return self.get( listener_id=task.created_resources.listeners[0], @@ -966,7 +970,11 @@ async def create_and_poll( extra_headers=extra_headers, polling_interval_seconds=polling_interval_seconds, ) - if not task.created_resources or not task.created_resources.listeners or len(task.created_resources.listeners) != 1: + if ( + not task.created_resources + or not task.created_resources.listeners + or len(task.created_resources.listeners) != 1 + ): raise ValueError(f"Expected exactly one resource to be created in a task") return await self.get( listener_id=task.created_resources.listeners[0], @@ -975,7 +983,7 @@ async def create_and_poll( extra_headers=extra_headers, timeout=timeout, ) - + async def delete_and_poll( self, listener_id: str, @@ -1008,7 +1016,7 @@ async def delete_and_poll( task_id=response.tasks[0], extra_headers=extra_headers, polling_interval_seconds=polling_interval_seconds, - ) + ) async def update_and_poll( self, @@ -1069,6 +1077,7 @@ async def update_and_poll( timeout=timeout, ) + class ListenersResourceWithRawResponse: def __init__(self, listeners: ListenersResource) -> None: self._listeners = listeners diff --git a/src/gcore/resources/cloud/load_balancers/load_balancers.py b/src/gcore/resources/cloud/load_balancers/load_balancers.py index 5e02feaa..89b8e06f 100644 --- a/src/gcore/resources/cloud/load_balancers/load_balancers.py +++ b/src/gcore/resources/cloud/load_balancers/load_balancers.py @@ -632,7 +632,11 @@ def create_and_poll( extra_headers=extra_headers, polling_interval_seconds=polling_interval_seconds, ) - if not task.created_resources or not task.created_resources.loadbalancers or len(task.created_resources.loadbalancers) != 1: + if ( + not task.created_resources + or not task.created_resources.loadbalancers + or len(task.created_resources.loadbalancers) != 1 + ): raise ValueError(f"Expected exactly one resource to be created in a task") return self.get( loadbalancer_id=task.created_resources.loadbalancers[0], @@ -1312,7 +1316,11 @@ async def create_and_poll( extra_headers=extra_headers, polling_interval_seconds=polling_interval_seconds, ) - if not task.created_resources or not task.created_resources.loadbalancers or len(task.created_resources.loadbalancers) != 1: + if ( + not task.created_resources + or not task.created_resources.loadbalancers + or len(task.created_resources.loadbalancers) != 1 + ): raise ValueError(f"Expected exactly one resource to be created in a task") return await self.get( loadbalancer_id=task.created_resources.loadbalancers[0], From a42b974dde0d61edd9efa8f5929c0cbd967eebf8 Mon Sep 17 00:00:00 2001 From: Algis Dumbris <151737200+algis-dumbris@users.noreply.github.com> Date: Tue, 17 Jun 2025 16:24:04 +0300 Subject: [PATCH 04/50] feat(cloud): add reserved fixed ips examples Refs GCLOUD2-18673 --- examples/cloud/reserved_fixed_ips.py | 95 ++++++++++++++++++ examples/cloud/reserved_fixed_ips_async.py | 108 +++++++++++++++++++++ 2 files changed, 203 insertions(+) create mode 100644 examples/cloud/reserved_fixed_ips.py create mode 100644 examples/cloud/reserved_fixed_ips_async.py diff --git a/examples/cloud/reserved_fixed_ips.py b/examples/cloud/reserved_fixed_ips.py new file mode 100644 index 00000000..5d18ce92 --- /dev/null +++ b/examples/cloud/reserved_fixed_ips.py @@ -0,0 +1,95 @@ +from gcore import Gcore +from gcore.pagination import SyncOffsetPage +from gcore.types.cloud import ReservedFixedIP + + +def create_reserved_fixed_ip(*, client: Gcore) -> ReservedFixedIP: + print("\n=== CREATE RESERVED FIXED IP ===") + task_list = client.cloud.reserved_fixed_ips.create( + type="external", + ip_family="ipv4", + is_vip=False, + ) + task = client.cloud.tasks.poll(task_list.tasks[0]) + if not task.created_resources or not task.created_resources.ports or len(task.created_resources.ports) != 1: + raise RuntimeError("Expected exactly one port created in the task result") + port = client.cloud.reserved_fixed_ips.get(task.created_resources.ports[0]) + print(f"Created reserved fixed IP: ID={port.port_id}, name={port.name}, IP={port.fixed_ip_address}") + print("========================") + return port + + +def list_reserved_fixed_ips(*, client: Gcore) -> SyncOffsetPage[ReservedFixedIP]: + print("\n=== LIST RESERVED FIXED IPS ===") + reserved_ips = client.cloud.reserved_fixed_ips.list() + for count, ip in enumerate(reserved_ips, 1): + print(f"{count}. Reserved fixed IP: ID={ip.port_id}, name={ip.name}, status={ip.status}") + print("========================") + return reserved_ips + + +def get_reserved_fixed_ip(*, client: Gcore, port_id: str) -> ReservedFixedIP: + print("\n=== GET RESERVED FIXED IP ===") + reserved_ip = client.cloud.reserved_fixed_ips.get(port_id) + print(f"Reserved fixed IP: ID={reserved_ip.port_id}, name={reserved_ip.name}, status={reserved_ip.status}") + print("========================") + return reserved_ip + + +def toggle_reserved_fixed_ip_vip(*, client: Gcore, port_id: str, is_vip: bool) -> ReservedFixedIP: + print("\n=== TOGGLE RESERVED FIXED IP VIP ===") + reserved_ip = client.cloud.reserved_fixed_ips.vip.toggle(port_id, is_vip=is_vip) + print( + f"Toggled reserved fixed IP VIP: ID={reserved_ip.port_id}, name={reserved_ip.name}, is_vip={reserved_ip.is_vip}" + ) + print("========================") + return reserved_ip + + +def list_candidate_ports(*, client: Gcore, port_id: str) -> None: + print("\n=== LIST CANDIDATE PORTS ===") + candidate_ports = client.cloud.reserved_fixed_ips.vip.list_candidate_ports(port_id) + for count, port in enumerate(candidate_ports.results, 1): + print(f"{count}. Candidate port: ID={port.port_id}, instance name={port.instance_name}") + print("========================") + + +def list_connected_ports(*, client: Gcore, port_id: str) -> None: + print("\n=== LIST CONNECTED PORTS ===") + connected_ports = client.cloud.reserved_fixed_ips.vip.list_connected_ports(port_id) + for count, port in enumerate(connected_ports.results, 1): + print(f"{count}. Connected port: ID={port.port_id}, instance name={port.instance_name}") + print("========================") + + +def delete_reserved_fixed_ip(*, client: Gcore, port_id: str) -> None: + print("\n=== DELETE RESERVED FIXED IP ===") + task_list = client.cloud.reserved_fixed_ips.delete(port_id) + client.cloud.tasks.poll(task_list.tasks[0]) + print(f"Deleted reserved fixed IP: ID={port_id}") + print("========================") + + +if __name__ == "__main__": + # No need to pass the API key explicitly — it will automatically be read from the GCORE_API_KEY environment variable if omitted + # api_key = os.environ.get("GCORE_API_KEY") + # Will use Production API URL if omitted + # base_url = os.environ.get("GCORE_BASE_URL") + + gcore = Gcore( + # api_key=api_key, + # base_url=base_url, + ) + + fixed_ip = create_reserved_fixed_ip(client=gcore) + list_reserved_fixed_ips(client=gcore) + get_reserved_fixed_ip(client=gcore, port_id=fixed_ip.port_id) + + # VIP + toggle_reserved_fixed_ip_vip(client=gcore, port_id=fixed_ip.port_id, is_vip=True) + list_candidate_ports(client=gcore, port_id=fixed_ip.port_id) + list_connected_ports(client=gcore, port_id=fixed_ip.port_id) + # is_vip needs to be false to delete the reserved fixed IP + toggle_reserved_fixed_ip_vip(client=gcore, port_id=fixed_ip.port_id, is_vip=False) + + delete_reserved_fixed_ip(client=gcore, port_id=fixed_ip.port_id) diff --git a/examples/cloud/reserved_fixed_ips_async.py b/examples/cloud/reserved_fixed_ips_async.py new file mode 100644 index 00000000..28e9e96f --- /dev/null +++ b/examples/cloud/reserved_fixed_ips_async.py @@ -0,0 +1,108 @@ +""" +Example demonstrating full lifecycle of Reserved Fixed IPs resource using async client. +Includes create, get, list, update (i.e. toggle VIP), and delete operations. +""" + +import asyncio + +from gcore import AsyncGcore +from gcore.pagination import AsyncOffsetPage +from gcore.types.cloud import ReservedFixedIP + + +async def create_reserved_fixed_ip(*, client: AsyncGcore) -> ReservedFixedIP: + print("\n=== CREATE RESERVED FIXED IP ===") + task_list = await client.cloud.reserved_fixed_ips.create( + type="external", + ip_family="ipv4", + is_vip=False, + ) + task = await client.cloud.tasks.poll(task_list.tasks[0]) + if not task.created_resources or not task.created_resources.ports or len(task.created_resources.ports) != 1: + raise RuntimeError("Expected exactly one port created in the task result") + port = await client.cloud.reserved_fixed_ips.get(task.created_resources.ports[0]) + print(f"Created reserved fixed IP: ID={port.port_id}, name={port.name}, IP={port.fixed_ip_address}") + print("========================") + return port + + +async def list_reserved_fixed_ips(*, client: AsyncGcore) -> AsyncOffsetPage[ReservedFixedIP]: + print("\n=== LIST RESERVED FIXED IPS ===") + reserved_ips = await client.cloud.reserved_fixed_ips.list() + count = 1 + async for ip in reserved_ips: + print(f"{count}. Reserved fixed IP: ID={ip.port_id}, name={ip.name}, status={ip.status}") + count += 1 + print("========================") + return reserved_ips + + +async def get_reserved_fixed_ip(*, client: AsyncGcore, port_id: str) -> ReservedFixedIP: + print("\n=== GET RESERVED FIXED IP ===") + reserved_ip = await client.cloud.reserved_fixed_ips.get(port_id) + print(f"Reserved fixed IP: ID={reserved_ip.port_id}, name={reserved_ip.name}, status={reserved_ip.status}") + print("========================") + return reserved_ip + + +async def toggle_reserved_fixed_ip_vip(*, client: AsyncGcore, port_id: str, is_vip: bool) -> ReservedFixedIP: + print("\n=== TOGGLE RESERVED FIXED IP VIP ===") + reserved_ip = await client.cloud.reserved_fixed_ips.vip.toggle(port_id, is_vip=is_vip) + print( + f"Toggled reserved fixed IP VIP: ID={reserved_ip.port_id}, name={reserved_ip.name}, is_vip={reserved_ip.is_vip}" + ) + print("========================") + return reserved_ip + + +async def list_candidate_ports(*, client: AsyncGcore, port_id: str) -> None: + print("\n=== LIST CANDIDATE PORTS ===") + candidate_ports = await client.cloud.reserved_fixed_ips.vip.list_candidate_ports(port_id) + for count, port in enumerate(candidate_ports.results, 1): + print(f"{count}. Candidate port: ID={port.port_id}, instance name={port.instance_name}") + print("========================") + + +async def list_connected_ports(*, client: AsyncGcore, port_id: str) -> None: + print("\n=== LIST CONNECTED PORTS ===") + connected_ports = await client.cloud.reserved_fixed_ips.vip.list_connected_ports(port_id) + for count, port in enumerate(connected_ports.results, 1): + print(f"{count}. Connected port: ID={port.port_id}, instance name={port.instance_name}") + print("========================") + + +async def delete_reserved_fixed_ip(*, client: AsyncGcore, port_id: str) -> None: + print("\n=== DELETE RESERVED FIXED IP ===") + task_list = await client.cloud.reserved_fixed_ips.delete(port_id) + await client.cloud.tasks.poll(task_list.tasks[0]) + print(f"Deleted reserved fixed IP: ID={port_id}") + print("========================") + + +async def main() -> None: + # No need to pass the API key explicitly — it will automatically be read from the GCORE_API_KEY environment variable if omitted + # api_key = os.environ.get("GCORE_API_KEY") + # Will use Production API URL if omitted + # base_url = os.environ.get("GCORE_BASE_URL") + + gcore = AsyncGcore( + # api_key=api_key, + # base_url=base_url, + ) + + fixed_ip = await create_reserved_fixed_ip(client=gcore) + await list_reserved_fixed_ips(client=gcore) + await get_reserved_fixed_ip(client=gcore, port_id=fixed_ip.port_id) + + # VIP + await toggle_reserved_fixed_ip_vip(client=gcore, port_id=fixed_ip.port_id, is_vip=True) + await list_candidate_ports(client=gcore, port_id=fixed_ip.port_id) + await list_connected_ports(client=gcore, port_id=fixed_ip.port_id) + # is_vip needs to be false to delete the reserved fixed IP + await toggle_reserved_fixed_ip_vip(client=gcore, port_id=fixed_ip.port_id, is_vip=False) + + await delete_reserved_fixed_ip(client=gcore, port_id=fixed_ip.port_id) + + +if __name__ == "__main__": + asyncio.run(main()) From 90101344d7fdf1ba0c8f55bc290ffe7839cb6af0 Mon Sep 17 00:00:00 2001 From: Algis Dumbris <151737200+algis-dumbris@users.noreply.github.com> Date: Tue, 17 Jun 2025 16:57:47 +0300 Subject: [PATCH 05/50] feat(cloud): add floating IPs examples Refs GCLOUD2-18674 --- examples/cloud/floating_ips.py | 80 ++++++++++++++++++++++++++++ examples/cloud/floating_ips_async.py | 80 ++++++++++++++++++++++++++++ 2 files changed, 160 insertions(+) create mode 100644 examples/cloud/floating_ips.py create mode 100644 examples/cloud/floating_ips_async.py diff --git a/examples/cloud/floating_ips.py b/examples/cloud/floating_ips.py new file mode 100644 index 00000000..180f3746 --- /dev/null +++ b/examples/cloud/floating_ips.py @@ -0,0 +1,80 @@ +from __future__ import annotations + +import os + +from gcore import Gcore + + +def main() -> None: + # TODO set cloud port ID before running + cloud_port_id = os.environ["GCORE_CLOUD_PORT_ID"] + + gcore = Gcore() + + floating_ip_id = create_floating_ip(client=gcore) + list_floating_ips(client=gcore) + get_floating_ip(client=gcore, floating_ip_id=floating_ip_id) + assign_floating_ip(client=gcore, floating_ip_id=floating_ip_id, port_id=cloud_port_id) + unassign_floating_ip(client=gcore, floating_ip_id=floating_ip_id) + delete_floating_ip(client=gcore, floating_ip_id=floating_ip_id) + + +def create_floating_ip(*, client: Gcore) -> str: + print("\n=== CREATE FLOATING IP ===") + response = client.cloud.floating_ips.create(tags={"name": "gcore-go-example"}) + task = client.cloud.tasks.poll(task_id=response.tasks[0]) + if task.created_resources is None or task.created_resources.floatingips is None: + raise RuntimeError("Task completed but created_resources or floatingips is missing") + floating_ip_id: str = task.created_resources.floatingips[0] + print(f"Created Floating IP: ID={floating_ip_id}") + print("========================") + return floating_ip_id + + +def list_floating_ips(*, client: Gcore) -> None: + print("\n=== LIST FLOATING IPS ===") + floating_ips = client.cloud.floating_ips.list() + for count, ip in enumerate(floating_ips, 1): + print(f"{count}. Floating IP: ID={ip.id}, status={ip.status}, floating IP address={ip.floating_ip_address}") + if not floating_ips: + print("No floating IPs found.") + print("========================") + + +def get_floating_ip(*, client: Gcore, floating_ip_id: str) -> None: + print("\n=== GET FLOATING IP ===") + floating_ip = client.cloud.floating_ips.get(floating_ip_id=floating_ip_id) + print( + f"Floating IP: ID={floating_ip.id}, status={floating_ip.status}, floating IP address={floating_ip.floating_ip_address}" + ) + print("========================") + + +def assign_floating_ip(*, client: Gcore, floating_ip_id: str, port_id: str) -> None: + print("\n=== ASSIGN FLOATING IP ===") + floating_ip = client.cloud.floating_ips.assign( + floating_ip_id=floating_ip_id, + port_id=port_id, + ) + print(f"Assigned floating IP: ID={floating_ip.id}, port ID={floating_ip.port_id}") + print("========================") + + +def unassign_floating_ip(*, client: Gcore, floating_ip_id: str) -> None: + print("\n=== UNASSIGN FLOATING IP ===") + floating_ip = client.cloud.floating_ips.unassign(floating_ip_id=floating_ip_id) + print(f"Unassigned floating IP: ID={floating_ip.id}") + print("========================") + + +def delete_floating_ip(*, client: Gcore, floating_ip_id: str) -> None: + print("\n=== DELETE FLOATING IP ===") + response = client.cloud.floating_ips.delete(floating_ip_id=floating_ip_id) + task_id = response.tasks[0] + client.cloud.tasks.poll(task_id=task_id) + print(f"Deleted floating IP: ID={floating_ip_id}") + print("========================") + + +if __name__ == "__main__": + main() diff --git a/examples/cloud/floating_ips_async.py b/examples/cloud/floating_ips_async.py new file mode 100644 index 00000000..bd1c9a95 --- /dev/null +++ b/examples/cloud/floating_ips_async.py @@ -0,0 +1,80 @@ +from __future__ import annotations + +import os +import asyncio + +from gcore import AsyncGcore + + +async def main() -> None: + # TODO set cloud port ID before running + cloud_port_id = os.environ["GCORE_CLOUD_PORT_ID"] + + gcore = AsyncGcore() + + floating_ip_id = await create_floating_ip(client=gcore) + await list_floating_ips(client=gcore) + await get_floating_ip(client=gcore, floating_ip_id=floating_ip_id) + await assign_floating_ip(client=gcore, floating_ip_id=floating_ip_id, port_id=cloud_port_id) + await unassign_floating_ip(client=gcore, floating_ip_id=floating_ip_id) + await delete_floating_ip(client=gcore, floating_ip_id=floating_ip_id) + + +async def create_floating_ip(*, client: AsyncGcore) -> str: + print("\n=== CREATE FLOATING IP ===") + response = await client.cloud.floating_ips.create(tags={"name": "gcore-go-example"}) + task = await client.cloud.tasks.poll(task_id=response.tasks[0]) + if task.created_resources is None or task.created_resources.floatingips is None: + raise RuntimeError("Task completed but created_resources or floatingips is missing") + floating_ip_id: str = task.created_resources.floatingips[0] + print(f"Created floating IP: ID={floating_ip_id}") + print("========================") + return floating_ip_id + + +async def list_floating_ips(*, client: AsyncGcore) -> None: + print("\n=== LIST FLOATING IPS ===") + count = 0 + async for ip in client.cloud.floating_ips.list(): + count += 1 + print(f"{count}. Floating IP: ID={ip.id}, status={ip.status}, floating IP address={ip.floating_ip_address}") + print("========================") + + +async def get_floating_ip(*, client: AsyncGcore, floating_ip_id: str) -> None: + print("\n=== GET FLOATING IP ===") + floating_ip = await client.cloud.floating_ips.get(floating_ip_id=floating_ip_id) + print( + f"Floating IP: ID={floating_ip.id}, status={floating_ip.status}, floating IP address={floating_ip.floating_ip_address}" + ) + print("========================") + + +async def assign_floating_ip(*, client: AsyncGcore, floating_ip_id: str, port_id: str) -> None: + print("\n=== ASSIGN FLOATING IP ===") + floating_ip = await client.cloud.floating_ips.assign( + floating_ip_id=floating_ip_id, + port_id=port_id, + ) + print(f"Assigned floating IP: ID={floating_ip.id}, port ID={floating_ip.port_id}") + print("========================") + + +async def unassign_floating_ip(*, client: AsyncGcore, floating_ip_id: str) -> None: + print("\n=== UNASSIGN FLOATING IP ===") + floating_ip = await client.cloud.floating_ips.unassign(floating_ip_id=floating_ip_id) + print(f"Unassigned floating IP: ID={floating_ip.id}") + print("========================") + + +async def delete_floating_ip(*, client: AsyncGcore, floating_ip_id: str) -> None: + print("\n=== DELETE FLOATING IP ===") + response = await client.cloud.floating_ips.delete(floating_ip_id=floating_ip_id) + task_id = response.tasks[0] + await client.cloud.tasks.poll(task_id=task_id) + print(f"Deleted floating IP: ID={floating_ip_id}") + print("========================") + + +if __name__ == "__main__": + asyncio.run(main()) From 5c4f2a57603a5940c13220770e363c1e597ec48d Mon Sep 17 00:00:00 2001 From: Algis Dumbris <151737200+algis-dumbris@users.noreply.github.com> Date: Tue, 17 Jun 2025 17:39:40 +0300 Subject: [PATCH 06/50] feat(cloud): add security groups examples Refs GCLOUD2-18775 --- examples/cloud/security_groups.py | 106 ++++++++++++++++++++++ examples/cloud/security_groups_async.py | 114 ++++++++++++++++++++++++ 2 files changed, 220 insertions(+) create mode 100644 examples/cloud/security_groups.py create mode 100644 examples/cloud/security_groups_async.py diff --git a/examples/cloud/security_groups.py b/examples/cloud/security_groups.py new file mode 100644 index 00000000..f2456b25 --- /dev/null +++ b/examples/cloud/security_groups.py @@ -0,0 +1,106 @@ +from gcore import Gcore +from gcore.types.cloud.security_group_create_params import SecurityGroup + + +def main() -> None: + gcore = Gcore() + + security_group_id = create_security_group(client=gcore) + list_security_groups(client=gcore) + get_security_group(client=gcore, security_group_id=security_group_id) + update_security_group(client=gcore, security_group_id=security_group_id) + + # Rules + rule_id = create_security_group_rule(client=gcore, security_group_id=security_group_id) + rule_id = replace_security_group_rule(client=gcore, rule_id=rule_id, security_group_id=security_group_id) + delete_security_group_rule(client=gcore, rule_id=rule_id) + + delete_security_group(client=gcore, security_group_id=security_group_id) + + +def create_security_group(*, client: Gcore) -> str: + print("\n=== CREATE SECURITY GROUP ===") + security_group = client.cloud.security_groups.create(security_group=SecurityGroup(name="gcore-go-example")) + print(f"Created security group: ID={security_group.id}, name={security_group.name}") + print("========================") + return security_group.id + + +def list_security_groups(*, client: Gcore) -> None: + print("\n=== LIST SECURITY GROUPS ===") + security_groups = client.cloud.security_groups.list() + for count, security_group in enumerate(security_groups, 1): + print(f"{count}. Security group: ID={security_group.id}, name={security_group.name}") + print("========================") + + +def get_security_group(*, client: Gcore, security_group_id: str) -> None: + print("\n=== GET SECURITY GROUP ===") + security_group = client.cloud.security_groups.get(group_id=security_group_id) + print( + f"Security group: ID={security_group.id}, name={security_group.name}, description={security_group.description}" + ) + print("========================") + + +def update_security_group(*, client: Gcore, security_group_id: str) -> None: + print("\n=== UPDATE SECURITY GROUP ===") + security_group = client.cloud.security_groups.update( + group_id=security_group_id, + name="gcore-go-example-updated", + ) + print(f"Updated security group: ID={security_group.id}, name={security_group.name}") + print("========================") + + +def delete_security_group(*, client: Gcore, security_group_id: str) -> None: + print("\n=== DELETE SECURITY GROUP ===") + client.cloud.security_groups.delete(group_id=security_group_id) + print(f"Deleted security group: ID={security_group_id}") + print("========================") + + +def create_security_group_rule(*, client: Gcore, security_group_id: str) -> str: + print("\n=== CREATE SECURITY GROUP RULE ===") + rule = client.cloud.security_groups.rules.create( + group_id=security_group_id, + direction="ingress", + protocol="tcp", + ethertype="IPv4", + port_range_min=80, + port_range_max=80, + remote_ip_prefix="0.0.0.0/0", + description="Allow HTTP traffic", + ) + print(f"Created security group rule: ID={rule.id}, protocol={rule.protocol}, port={rule.port_range_min}") + print("========================") + return rule.id + + +def replace_security_group_rule(*, client: Gcore, rule_id: str, security_group_id: str) -> str: + print("\n=== REPLACE SECURITY GROUP RULE ===") + rule = client.cloud.security_groups.rules.replace( + rule_id=rule_id, + direction="ingress", + security_group_id=security_group_id, + protocol="tcp", + ethertype="IPv4", + port_range_min=443, + port_range_max=443, + remote_ip_prefix="0.0.0.0/0", + description="Allow HTTPS traffic", + ) + print(f"Replaced security group rule: ID={rule.id}, protocol={rule.protocol}, port={rule.port_range_min}") + print("========================") + return rule.id + + +def delete_security_group_rule(*, client: Gcore, rule_id: str) -> None: + print("\n=== DELETE SECURITY GROUP RULE ===") + client.cloud.security_groups.rules.delete(rule_id=rule_id) + print(f"Deleted security group rule: ID={rule_id}") + print("========================") + + +if __name__ == "__main__": + main() diff --git a/examples/cloud/security_groups_async.py b/examples/cloud/security_groups_async.py new file mode 100644 index 00000000..ff5388e7 --- /dev/null +++ b/examples/cloud/security_groups_async.py @@ -0,0 +1,114 @@ +import asyncio + +from gcore import AsyncGcore +from gcore.types.cloud.security_group_create_params import SecurityGroup + + +async def main() -> None: + gcore = AsyncGcore() + + security_group_id = await create_security_group(client=gcore) + await list_security_groups(client=gcore) + await get_security_group(client=gcore, security_group_id=security_group_id) + await update_security_group(client=gcore, security_group_id=security_group_id) + + # Rules + rule_id = await create_security_group_rule(client=gcore, security_group_id=security_group_id) + rule_id = await replace_security_group_rule(client=gcore, rule_id=rule_id, security_group_id=security_group_id) + await delete_security_group_rule(client=gcore, rule_id=rule_id) + + await delete_security_group(client=gcore, security_group_id=security_group_id) + + +async def create_security_group(client: AsyncGcore) -> str: + print("\n=== CREATE SECURITY GROUP ===") + security_group = await client.cloud.security_groups.create( + security_group=SecurityGroup(name="gcore-go-example") + ) + print(f"Created security group: ID={security_group.id}, name={security_group.name}") + print("========================") + return security_group.id + + +async def list_security_groups(*, client: AsyncGcore) -> None: + print("\n=== LIST SECURITY GROUPS ===") + security_groups = await client.cloud.security_groups.list() + count = 0 + async for security_group in security_groups: + count += 1 + print(f"{count}. Security group: ID={security_group.id}, name={security_group.name}") + print("========================") + + +async def get_security_group(*, client: AsyncGcore, security_group_id: str) -> None: + print("\n=== GET SECURITY GROUP ===") + security_group = await client.cloud.security_groups.get(group_id=security_group_id) + print( + f"Security group: ID={security_group.id}, name={security_group.name}, description={security_group.description}" + ) + print("========================") + + +async def update_security_group(*, client: AsyncGcore, security_group_id: str) -> None: + print("\n=== UPDATE SECURITY GROUP ===") + security_group = await client.cloud.security_groups.update( + group_id=security_group_id, + name="gcore-go-example-updated", + ) + print(f"Updated security group: ID={security_group.id}, name={security_group.name}") + print("========================") + + + +async def delete_security_group(*, client: AsyncGcore, security_group_id: str) -> None: + print("\n=== DELETE SECURITY GROUP ===") + await client.cloud.security_groups.delete(group_id=security_group_id) + print(f"Deleted security group: ID={security_group_id}") + print("========================") +# Security Group Rules functions + + +async def create_security_group_rule(*, client: AsyncGcore, security_group_id: str) -> str: + print("\n=== CREATE SECURITY GROUP RULE ===") + rule = await client.cloud.security_groups.rules.create( + group_id=security_group_id, + direction="ingress", + protocol="tcp", + ethertype="IPv4", + port_range_min=80, + port_range_max=80, + remote_ip_prefix="0.0.0.0/0", + description="Allow HTTP traffic", + ) + print(f"Created security group rule: ID={rule.id}, protocol={rule.protocol}, port={rule.port_range_min}") + print("========================") + return rule.id + + +async def replace_security_group_rule(*, client: AsyncGcore, rule_id: str, security_group_id: str) -> str: + print("\n=== REPLACE SECURITY GROUP RULE ===") + rule = await client.cloud.security_groups.rules.replace( + rule_id=rule_id, + direction="ingress", + security_group_id=security_group_id, + protocol="tcp", + ethertype="IPv4", + port_range_min=443, + port_range_max=443, + remote_ip_prefix="0.0.0.0/0", + description="Allow HTTPS traffic", + ) + print(f"Replaced security group rule: ID={rule.id}, protocol={rule.protocol}, port={rule.port_range_min}") + print("========================") + return rule.id + + +async def delete_security_group_rule(*, client: AsyncGcore, rule_id: str) -> None: + print("\n=== DELETE SECURITY GROUP RULE ===") + await client.cloud.security_groups.rules.delete(rule_id=rule_id) + print(f"Deleted security group rule: ID={rule_id}") + print("========================") + + +if __name__ == "__main__": + asyncio.run(main()) From 85766ca7a1a3848cf6e252a55d7cc7ef384a4f94 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 17 Jun 2025 16:34:55 +0000 Subject: [PATCH 07/50] fix(waap): remove duplicate method for acct overview --- .stats.yml | 2 +- api.md | 12 -- src/gcore/resources/waap/__init__.py | 14 --- src/gcore/resources/waap/clients.py | 135 --------------------- src/gcore/resources/waap/waap.py | 32 ----- src/gcore/types/waap/__init__.py | 1 - src/gcore/types/waap/client_me_response.py | 34 ------ tests/api_resources/waap/test_clients.py | 72 ----------- 8 files changed, 1 insertion(+), 301 deletions(-) delete mode 100644 src/gcore/resources/waap/clients.py delete mode 100644 src/gcore/types/waap/client_me_response.py delete mode 100644 tests/api_resources/waap/test_clients.py diff --git a/.stats.yml b/.stats.yml index 72a98f04..0cc0367f 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 302 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-c69353c2638a896685622a7bc91746002ce45c684c09f0cd59f90d193a711bfd.yml openapi_spec_hash: 288f2041f8a716abffdbdf64ae886c63 -config_hash: 62229a7a78ca1c79cb7cf61752a8df7e +config_hash: 851d2f0c1f925aee3b53478d4fe311ba diff --git a/api.md b/api.md index 82d3b4fb..307d30c4 100644 --- a/api.md +++ b/api.md @@ -928,18 +928,6 @@ Methods: - client.waap.get_account_overview() -> WaapGetAccountOverviewResponse -## Clients - -Types: - -```python -from gcore.types.waap import ClientMeResponse -``` - -Methods: - -- client.waap.clients.me() -> ClientMeResponse - ## Statistics Methods: diff --git a/src/gcore/resources/waap/__init__.py b/src/gcore/resources/waap/__init__.py index 69655c51..f5020fdf 100644 --- a/src/gcore/resources/waap/__init__.py +++ b/src/gcore/resources/waap/__init__.py @@ -16,14 +16,6 @@ WaapResourceWithStreamingResponse, AsyncWaapResourceWithStreamingResponse, ) -from .clients import ( - ClientsResource, - AsyncClientsResource, - ClientsResourceWithRawResponse, - AsyncClientsResourceWithRawResponse, - ClientsResourceWithStreamingResponse, - AsyncClientsResourceWithStreamingResponse, -) from .domains import ( DomainsResource, AsyncDomainsResource, @@ -74,12 +66,6 @@ ) __all__ = [ - "ClientsResource", - "AsyncClientsResource", - "ClientsResourceWithRawResponse", - "AsyncClientsResourceWithRawResponse", - "ClientsResourceWithStreamingResponse", - "AsyncClientsResourceWithStreamingResponse", "StatisticsResource", "AsyncStatisticsResource", "StatisticsResourceWithRawResponse", diff --git a/src/gcore/resources/waap/clients.py b/src/gcore/resources/waap/clients.py deleted file mode 100644 index 0cee8933..00000000 --- a/src/gcore/resources/waap/clients.py +++ /dev/null @@ -1,135 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import httpx - -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ..._base_client import make_request_options -from ...types.waap.client_me_response import ClientMeResponse - -__all__ = ["ClientsResource", "AsyncClientsResource"] - - -class ClientsResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> ClientsResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/G-Core/gcore-python#accessing-raw-response-data-eg-headers - """ - return ClientsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> ClientsResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/G-Core/gcore-python#with_streaming_response - """ - return ClientsResourceWithStreamingResponse(self) - - def me( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> ClientMeResponse: - """Get information about WAAP service for the client""" - return self._get( - "/waap/v1/clients/me", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ClientMeResponse, - ) - - -class AsyncClientsResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncClientsResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/G-Core/gcore-python#accessing-raw-response-data-eg-headers - """ - return AsyncClientsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncClientsResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/G-Core/gcore-python#with_streaming_response - """ - return AsyncClientsResourceWithStreamingResponse(self) - - async def me( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> ClientMeResponse: - """Get information about WAAP service for the client""" - return await self._get( - "/waap/v1/clients/me", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ClientMeResponse, - ) - - -class ClientsResourceWithRawResponse: - def __init__(self, clients: ClientsResource) -> None: - self._clients = clients - - self.me = to_raw_response_wrapper( - clients.me, - ) - - -class AsyncClientsResourceWithRawResponse: - def __init__(self, clients: AsyncClientsResource) -> None: - self._clients = clients - - self.me = async_to_raw_response_wrapper( - clients.me, - ) - - -class ClientsResourceWithStreamingResponse: - def __init__(self, clients: ClientsResource) -> None: - self._clients = clients - - self.me = to_streamed_response_wrapper( - clients.me, - ) - - -class AsyncClientsResourceWithStreamingResponse: - def __init__(self, clients: AsyncClientsResource) -> None: - self._clients = clients - - self.me = async_to_streamed_response_wrapper( - clients.me, - ) diff --git a/src/gcore/resources/waap/waap.py b/src/gcore/resources/waap/waap.py index dc9181ac..cebf40de 100644 --- a/src/gcore/resources/waap/waap.py +++ b/src/gcore/resources/waap/waap.py @@ -12,14 +12,6 @@ TagsResourceWithStreamingResponse, AsyncTagsResourceWithStreamingResponse, ) -from .clients import ( - ClientsResource, - AsyncClientsResource, - ClientsResourceWithRawResponse, - AsyncClientsResourceWithRawResponse, - ClientsResourceWithStreamingResponse, - AsyncClientsResourceWithStreamingResponse, -) from .ip_info import ( IPInfoResource, AsyncIPInfoResource, @@ -84,10 +76,6 @@ class WaapResource(SyncAPIResource): - @cached_property - def clients(self) -> ClientsResource: - return ClientsResource(self._client) - @cached_property def statistics(self) -> StatisticsResource: return StatisticsResource(self._client) @@ -156,10 +144,6 @@ def get_account_overview( class AsyncWaapResource(AsyncAPIResource): - @cached_property - def clients(self) -> AsyncClientsResource: - return AsyncClientsResource(self._client) - @cached_property def statistics(self) -> AsyncStatisticsResource: return AsyncStatisticsResource(self._client) @@ -235,10 +219,6 @@ def __init__(self, waap: WaapResource) -> None: waap.get_account_overview, ) - @cached_property - def clients(self) -> ClientsResourceWithRawResponse: - return ClientsResourceWithRawResponse(self._waap.clients) - @cached_property def statistics(self) -> StatisticsResourceWithRawResponse: return StatisticsResourceWithRawResponse(self._waap.statistics) @@ -276,10 +256,6 @@ def __init__(self, waap: AsyncWaapResource) -> None: waap.get_account_overview, ) - @cached_property - def clients(self) -> AsyncClientsResourceWithRawResponse: - return AsyncClientsResourceWithRawResponse(self._waap.clients) - @cached_property def statistics(self) -> AsyncStatisticsResourceWithRawResponse: return AsyncStatisticsResourceWithRawResponse(self._waap.statistics) @@ -317,10 +293,6 @@ def __init__(self, waap: WaapResource) -> None: waap.get_account_overview, ) - @cached_property - def clients(self) -> ClientsResourceWithStreamingResponse: - return ClientsResourceWithStreamingResponse(self._waap.clients) - @cached_property def statistics(self) -> StatisticsResourceWithStreamingResponse: return StatisticsResourceWithStreamingResponse(self._waap.statistics) @@ -358,10 +330,6 @@ def __init__(self, waap: AsyncWaapResource) -> None: waap.get_account_overview, ) - @cached_property - def clients(self) -> AsyncClientsResourceWithStreamingResponse: - return AsyncClientsResourceWithStreamingResponse(self._waap.clients) - @cached_property def statistics(self) -> AsyncStatisticsResourceWithStreamingResponse: return AsyncStatisticsResourceWithStreamingResponse(self._waap.statistics) diff --git a/src/gcore/types/waap/__init__.py b/src/gcore/types/waap/__init__.py index f74ffde6..88cc7228 100644 --- a/src/gcore/types/waap/__init__.py +++ b/src/gcore/types/waap/__init__.py @@ -18,7 +18,6 @@ from .waap_top_session import WaapTopSession as WaapTopSession from .waap_organization import WaapOrganization as WaapOrganization from .waap_traffic_type import WaapTrafficType as WaapTrafficType -from .client_me_response import ClientMeResponse as ClientMeResponse from .domain_list_params import DomainListParams as DomainListParams from .ip_info_get_params import IPInfoGetParams as IPInfoGetParams from .waap_advanced_rule import WaapAdvancedRule as WaapAdvancedRule diff --git a/src/gcore/types/waap/client_me_response.py b/src/gcore/types/waap/client_me_response.py deleted file mode 100644 index 15310d16..00000000 --- a/src/gcore/types/waap/client_me_response.py +++ /dev/null @@ -1,34 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Dict, List, Optional - -from ..._models import BaseModel - -__all__ = ["ClientMeResponse", "Quotas", "Service"] - - -class Quotas(BaseModel): - allowed: int - """The maximum allowed number of this resource""" - - current: int - """The current number of this resource""" - - -class Service(BaseModel): - enabled: bool - """Whether the service is enabled""" - - -class ClientMeResponse(BaseModel): - id: Optional[int] = None - """The client ID""" - - features: List[str] - """List of enabled features""" - - quotas: Dict[str, Quotas] - """Quotas for the client""" - - service: Service - """Information about the WAAP service status""" diff --git a/tests/api_resources/waap/test_clients.py b/tests/api_resources/waap/test_clients.py deleted file mode 100644 index d7d83c47..00000000 --- a/tests/api_resources/waap/test_clients.py +++ /dev/null @@ -1,72 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import os -from typing import Any, cast - -import pytest - -from gcore import Gcore, AsyncGcore -from tests.utils import assert_matches_type -from gcore.types.waap import ClientMeResponse - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestClients: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @parametrize - def test_method_me(self, client: Gcore) -> None: - client_ = client.waap.clients.me() - assert_matches_type(ClientMeResponse, client_, path=["response"]) - - @parametrize - def test_raw_response_me(self, client: Gcore) -> None: - response = client.waap.clients.with_raw_response.me() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - client_ = response.parse() - assert_matches_type(ClientMeResponse, client_, path=["response"]) - - @parametrize - def test_streaming_response_me(self, client: Gcore) -> None: - with client.waap.clients.with_streaming_response.me() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - client_ = response.parse() - assert_matches_type(ClientMeResponse, client_, path=["response"]) - - assert cast(Any, response.is_closed) is True - - -class TestAsyncClients: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) - - @parametrize - async def test_method_me(self, async_client: AsyncGcore) -> None: - client = await async_client.waap.clients.me() - assert_matches_type(ClientMeResponse, client, path=["response"]) - - @parametrize - async def test_raw_response_me(self, async_client: AsyncGcore) -> None: - response = await async_client.waap.clients.with_raw_response.me() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - client = await response.parse() - assert_matches_type(ClientMeResponse, client, path=["response"]) - - @parametrize - async def test_streaming_response_me(self, async_client: AsyncGcore) -> None: - async with async_client.waap.clients.with_streaming_response.me() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - client = await response.parse() - assert_matches_type(ClientMeResponse, client, path=["response"]) - - assert cast(Any, response.is_closed) is True From 57ddcba83e1664b765c1c16e9ee314f6af74c38b Mon Sep 17 00:00:00 2001 From: Algis Dumbris <151737200+algis-dumbris@users.noreply.github.com> Date: Tue, 17 Jun 2025 19:44:41 +0300 Subject: [PATCH 08/50] feat(cloud): add volumes examples Refs GCLOUD2-18668 --- examples/cloud/volumes.py | 108 +++++++++++++++++++++++++++++++ examples/cloud/volumes_async.py | 109 ++++++++++++++++++++++++++++++++ 2 files changed, 217 insertions(+) create mode 100644 examples/cloud/volumes.py create mode 100644 examples/cloud/volumes_async.py diff --git a/examples/cloud/volumes.py b/examples/cloud/volumes.py new file mode 100644 index 00000000..767d4320 --- /dev/null +++ b/examples/cloud/volumes.py @@ -0,0 +1,108 @@ +import os + +from gcore import Gcore + + +def main() -> None: + # TODO set instance ID before running attach/detach operations + instance_id = os.environ["GCORE_CLOUD_INSTANCE_ID"] + + gcore = Gcore() + + volume_id = create_volume(client=gcore) + list_volumes(client=gcore) + get_volume(client=gcore, volume_id=volume_id) + update_volume(client=gcore, volume_id=volume_id) + attach_to_instance(client=gcore, volume_id=volume_id, instance_id=instance_id) + detach_from_instance(client=gcore, volume_id=volume_id, instance_id=instance_id) + change_type(client=gcore, volume_id=volume_id) + resize(client=gcore, volume_id=volume_id) + delete_volume(client=gcore, volume_id=volume_id) + + +def create_volume(*, client: Gcore) -> str: + print("\n=== CREATE VOLUME ===") + response = client.cloud.volumes.create( + name="gcore-go-example", + size=1, + source="new-volume", + ) + task = client.cloud.tasks.poll(task_id=response.tasks[0]) + if task.created_resources is None or task.created_resources.volumes is None: + raise RuntimeError("Task completed but created_resources or volumes is missing") + volume_id: str = task.created_resources.volumes[0] + print(f"Created volume: ID={volume_id}") + print("========================") + return volume_id + + +def list_volumes(*, client: Gcore) -> None: + print("\n=== LIST VOLUMES ===") + volumes = client.cloud.volumes.list() + for count, volume in enumerate(volumes): + print(f"{count}. Volume: ID={volume.id}, name={volume.name}, size={volume.size} GiB") + print("========================") + + +def get_volume(*, client: Gcore, volume_id: str) -> None: + print("\n=== GET VOLUME ===") + volume = client.cloud.volumes.get(volume_id=volume_id) + print(f"Volume: ID={volume.id}, name={volume.name}, size={volume.size} GiB") + print("========================") + + +def update_volume(*, client: Gcore, volume_id: str) -> None: + print("\n=== UPDATE VOLUME ===") + volume = client.cloud.volumes.update( + volume_id=volume_id, + name="gcore-go-example-updated", + ) + print(f"Updated volume: ID={volume.id}, name={volume.name}") + print("========================") + + +def attach_to_instance(*, client: Gcore, volume_id: str, instance_id: str) -> None: + print("\n=== ATTACH TO INSTANCE ===") + response = client.cloud.volumes.attach_to_instance(volume_id=volume_id, instance_id=instance_id) + task_id = response.tasks[0] + client.cloud.tasks.poll(task_id=task_id) + print(f"Attached volume to instance: volume_id={volume_id}, instance_id={instance_id}") + print("========================") + + +def detach_from_instance(*, client: Gcore, volume_id: str, instance_id: str) -> None: + print("\n=== DETACH FROM INSTANCE ===") + response = client.cloud.volumes.detach_from_instance(volume_id=volume_id, instance_id=instance_id) + task_id = response.tasks[0] + client.cloud.tasks.poll(task_id=task_id) + print(f"Detached volume from instance: volume_id={volume_id}, instance_id={instance_id}") + print("========================") + + +def change_type(*, client: Gcore, volume_id: str) -> None: + print("\n=== CHANGE TYPE ===") + volume = client.cloud.volumes.change_type(volume_id=volume_id, volume_type="ssd_hiiops") + print(f"Changed volume type: ID={volume.id}, type=ssd_hiiops") + print("========================") + + +def resize(*, client: Gcore, volume_id: str) -> None: + print("\n=== RESIZE ===") + response = client.cloud.volumes.resize(volume_id=volume_id, size=2) + task_id = response.tasks[0] + client.cloud.tasks.poll(task_id=task_id) + print(f"Resized volume: ID={volume_id}, size=2 GiB") + print("========================") + + +def delete_volume(*, client: Gcore, volume_id: str) -> None: + print("\n=== DELETE VOLUME ===") + response = client.cloud.volumes.delete(volume_id=volume_id) + task_id = response.tasks[0] + client.cloud.tasks.poll(task_id=task_id) + print(f"Deleted volume: ID={volume_id}") + print("========================") + + +if __name__ == "__main__": + main() diff --git a/examples/cloud/volumes_async.py b/examples/cloud/volumes_async.py new file mode 100644 index 00000000..664cd0d3 --- /dev/null +++ b/examples/cloud/volumes_async.py @@ -0,0 +1,109 @@ +import os +import asyncio + +from gcore import AsyncGcore + + +async def create_volume(*, client: AsyncGcore) -> str: + print("\n=== CREATE VOLUME ===") + response = await client.cloud.volumes.create( + name="gcore-go-example", + size=1, + source="new-volume", + ) + task = await client.cloud.tasks.poll(task_id=response.tasks[0]) + if task.created_resources is None or task.created_resources.volumes is None: + raise RuntimeError("Task completed but created_resources or volumes is missing") + volume_id: str = task.created_resources.volumes[0] + print(f"Created volume: ID={volume_id}") + print("========================") + return volume_id + + +async def list_volumes(*, client: AsyncGcore) -> None: + print("\n=== LIST VOLUMES ===") + count = 0 + async for volume in client.cloud.volumes.list(): + print(f"{count}. Volume: ID={volume.id}, name={volume.name}, size={volume.size} GiB") + count += 1 + print("========================") + + +async def get_volume(*, client: AsyncGcore, volume_id: str) -> None: + print("\n=== GET VOLUME ===") + volume = await client.cloud.volumes.get(volume_id=volume_id) + print(f"Volume: ID={volume.id}, name={volume.name}, size={volume.size} GiB") + print("========================") + + +async def update_volume(*, client: AsyncGcore, volume_id: str) -> None: + print("\n=== UPDATE VOLUME ===") + volume = await client.cloud.volumes.update( + volume_id=volume_id, + name="gcore-go-example-updated", + ) + print(f"Updated volume: ID={volume.id}, name={volume.name}") + print("========================") + + +async def attach_to_instance(*, client: AsyncGcore, volume_id: str, instance_id: str) -> None: + print("\n=== ATTACH TO INSTANCE ===") + response = await client.cloud.volumes.attach_to_instance(volume_id=volume_id, instance_id=instance_id) + task_id = response.tasks[0] + await client.cloud.tasks.poll(task_id=task_id) + print(f"Attached volume to instance: volume_id={volume_id}, instance_id={instance_id}") + print("========================") + + +async def detach_from_instance(*, client: AsyncGcore, volume_id: str, instance_id: str) -> None: + print("\n=== DETACH FROM INSTANCE ===") + response = await client.cloud.volumes.detach_from_instance(volume_id=volume_id, instance_id=instance_id) + task_id = response.tasks[0] + await client.cloud.tasks.poll(task_id=task_id) + print(f"Detached volume from instance: volume_id={volume_id}, instance_id={instance_id}") + print("========================") + + +async def change_type(*, client: AsyncGcore, volume_id: str) -> None: + print("\n=== CHANGE TYPE ===") + volume = await client.cloud.volumes.change_type(volume_id=volume_id, volume_type="ssd_hiiops") + print(f"Changed volume type: ID={volume.id}, type=ssd_hiiops") + print("========================") + + +async def resize(*, client: AsyncGcore, volume_id: str) -> None: + print("\n=== RESIZE ===") + response = await client.cloud.volumes.resize(volume_id=volume_id, size=2) + task_id = response.tasks[0] + await client.cloud.tasks.poll(task_id=task_id) + print(f"Resized volume: ID={volume_id}, size=2 GiB") + print("========================") + + +async def delete_volume(*, client: AsyncGcore, volume_id: str) -> None: + print("\n=== DELETE VOLUME ===") + response = await client.cloud.volumes.delete(volume_id=volume_id) + task_id = response.tasks[0] + await client.cloud.tasks.poll(task_id=task_id) + print(f"Deleted volume: ID={volume_id}") + print("========================") + + +async def main() -> None: + instance_id = os.environ["GCORE_CLOUD_INSTANCE_ID"] + + gcore = AsyncGcore() + + volume_id = await create_volume(client=gcore) + await list_volumes(client=gcore) + await get_volume(client=gcore, volume_id=volume_id) + await update_volume(client=gcore, volume_id=volume_id) + await attach_to_instance(client=gcore, volume_id=volume_id, instance_id=instance_id) + await detach_from_instance(client=gcore, volume_id=volume_id, instance_id=instance_id) + await change_type(client=gcore, volume_id=volume_id) + await resize(client=gcore, volume_id=volume_id) + await delete_volume(client=gcore, volume_id=volume_id) + + +if __name__ == "__main__": + asyncio.run(main()) From 6634a74d3106f25476bd6bce98d12249f07dd8a6 Mon Sep 17 00:00:00 2001 From: Pedro Oliveira <8281907+pedrodeoliveira@users.noreply.github.com> Date: Tue, 17 Jun 2025 17:46:22 +0100 Subject: [PATCH 09/50] fix(cloud): update tags type for gpu baremetal clusters and images, instances, load balancers --- src/gcore/resources/cloud/baremetal/servers.py | 4 ++-- .../gpu_baremetal_clusters/gpu_baremetal_clusters.py | 4 ++-- .../resources/cloud/gpu_baremetal_clusters/images.py | 4 ++-- src/gcore/resources/cloud/instances/images.py | 8 ++++---- src/gcore/resources/cloud/instances/instances.py | 4 ++-- .../resources/cloud/load_balancers/load_balancers.py | 4 ++-- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/gcore/resources/cloud/baremetal/servers.py b/src/gcore/resources/cloud/baremetal/servers.py index 3b031c2a..d3f04458 100644 --- a/src/gcore/resources/cloud/baremetal/servers.py +++ b/src/gcore/resources/cloud/baremetal/servers.py @@ -398,7 +398,7 @@ def create_and_poll( name_template: str | NotGiven = NOT_GIVEN, password: str | NotGiven = NOT_GIVEN, ssh_key_name: Optional[str] | NotGiven = NOT_GIVEN, - tags: TagUpdateMapParam | NotGiven = NOT_GIVEN, + tags: Dict[str, str] | NotGiven = NOT_GIVEN, user_data: str | NotGiven = NOT_GIVEN, username: str | NotGiven = NOT_GIVEN, polling_interval_seconds: int | NotGiven = NOT_GIVEN, @@ -872,7 +872,7 @@ async def create_and_poll( name_template: str | NotGiven = NOT_GIVEN, password: str | NotGiven = NOT_GIVEN, ssh_key_name: Optional[str] | NotGiven = NOT_GIVEN, - tags: TagUpdateMapParam | NotGiven = NOT_GIVEN, + tags: Dict[str, str] | NotGiven = NOT_GIVEN, user_data: str | NotGiven = NOT_GIVEN, username: str | NotGiven = NOT_GIVEN, polling_interval_seconds: int | NotGiven = NOT_GIVEN, diff --git a/src/gcore/resources/cloud/gpu_baremetal_clusters/gpu_baremetal_clusters.py b/src/gcore/resources/cloud/gpu_baremetal_clusters/gpu_baremetal_clusters.py index 2087a7ba..25968fbe 100644 --- a/src/gcore/resources/cloud/gpu_baremetal_clusters/gpu_baremetal_clusters.py +++ b/src/gcore/resources/cloud/gpu_baremetal_clusters/gpu_baremetal_clusters.py @@ -538,7 +538,7 @@ def create_and_poll( name: str, instances_count: int | NotGiven = NOT_GIVEN, ssh_key_name: str | NotGiven = NOT_GIVEN, - tags: TagUpdateMapParam | NotGiven = NOT_GIVEN, + tags: Dict[str, str] | NotGiven = NOT_GIVEN, polling_interval_seconds: int | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -1160,7 +1160,7 @@ async def create_and_poll( name: str, instances_count: int | NotGiven = NOT_GIVEN, ssh_key_name: str | NotGiven = NOT_GIVEN, - tags: TagUpdateMapParam | NotGiven = NOT_GIVEN, + tags: Dict[str, str] | NotGiven = NOT_GIVEN, polling_interval_seconds: int | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. diff --git a/src/gcore/resources/cloud/gpu_baremetal_clusters/images.py b/src/gcore/resources/cloud/gpu_baremetal_clusters/images.py index f1524d32..cda7ff9d 100644 --- a/src/gcore/resources/cloud/gpu_baremetal_clusters/images.py +++ b/src/gcore/resources/cloud/gpu_baremetal_clusters/images.py @@ -316,7 +316,7 @@ def upload_and_poll( os_type: Optional[Literal["linux", "windows"]] | NotGiven = NOT_GIVEN, os_version: Optional[str] | NotGiven = NOT_GIVEN, ssh_key: Literal["allow", "deny", "required"] | NotGiven = NOT_GIVEN, - tags: TagUpdateMapParam | NotGiven = NOT_GIVEN, + tags: Dict[str, str] | NotGiven = NOT_GIVEN, polling_interval_seconds: int | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -659,7 +659,7 @@ async def upload_and_poll( os_type: Optional[Literal["linux", "windows"]] | NotGiven = NOT_GIVEN, os_version: Optional[str] | NotGiven = NOT_GIVEN, ssh_key: Literal["allow", "deny", "required"] | NotGiven = NOT_GIVEN, - tags: TagUpdateMapParam | NotGiven = NOT_GIVEN, + tags: Dict[str, str] | NotGiven = NOT_GIVEN, polling_interval_seconds: int | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. diff --git a/src/gcore/resources/cloud/instances/images.py b/src/gcore/resources/cloud/instances/images.py index c99d01a7..11b3db09 100644 --- a/src/gcore/resources/cloud/instances/images.py +++ b/src/gcore/resources/cloud/instances/images.py @@ -369,7 +369,7 @@ def create_from_volume_and_poll( os_type: Literal["linux", "windows"] | NotGiven = NOT_GIVEN, source: Literal["volume"] | NotGiven = NOT_GIVEN, ssh_key: Literal["allow", "deny", "required"] | NotGiven = NOT_GIVEN, - tags: TagUpdateMapParam | NotGiven = NOT_GIVEN, + tags: Dict[str, str] | NotGiven = NOT_GIVEN, polling_interval_seconds: int | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -571,7 +571,7 @@ def upload_and_poll( os_type: Literal["linux", "windows"] | NotGiven = NOT_GIVEN, os_version: Optional[str] | NotGiven = NOT_GIVEN, ssh_key: Literal["allow", "deny", "required"] | NotGiven = NOT_GIVEN, - tags: TagUpdateMapParam | NotGiven = NOT_GIVEN, + tags: Dict[str, str] | NotGiven = NOT_GIVEN, polling_interval_seconds: int | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -956,7 +956,7 @@ async def create_from_volume_and_poll( os_type: Literal["linux", "windows"] | NotGiven = NOT_GIVEN, source: Literal["volume"] | NotGiven = NOT_GIVEN, ssh_key: Literal["allow", "deny", "required"] | NotGiven = NOT_GIVEN, - tags: TagUpdateMapParam | NotGiven = NOT_GIVEN, + tags: Dict[str, str] | NotGiven = NOT_GIVEN, polling_interval_seconds: int | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -1158,7 +1158,7 @@ async def upload_and_poll( os_type: Literal["linux", "windows"] | NotGiven = NOT_GIVEN, os_version: Optional[str] | NotGiven = NOT_GIVEN, ssh_key: Literal["allow", "deny", "required"] | NotGiven = NOT_GIVEN, - tags: TagUpdateMapParam | NotGiven = NOT_GIVEN, + tags: Dict[str, str] | NotGiven = NOT_GIVEN, polling_interval_seconds: int | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. diff --git a/src/gcore/resources/cloud/instances/instances.py b/src/gcore/resources/cloud/instances/instances.py index 14c319f4..57bacd94 100644 --- a/src/gcore/resources/cloud/instances/instances.py +++ b/src/gcore/resources/cloud/instances/instances.py @@ -269,7 +269,7 @@ def create_and_poll( security_groups: Iterable[instance_create_params.SecurityGroup] | NotGiven = NOT_GIVEN, servergroup_id: str | NotGiven = NOT_GIVEN, ssh_key_name: Optional[str] | NotGiven = NOT_GIVEN, - tags: TagUpdateMapParam | NotGiven = NOT_GIVEN, + tags: Dict[str, str] | NotGiven = NOT_GIVEN, user_data: str | NotGiven = NOT_GIVEN, username: str | NotGiven = NOT_GIVEN, polling_interval_seconds: int | NotGiven = NOT_GIVEN, @@ -1511,7 +1511,7 @@ async def create_and_poll( security_groups: Iterable[instance_create_params.SecurityGroup] | NotGiven = NOT_GIVEN, servergroup_id: str | NotGiven = NOT_GIVEN, ssh_key_name: Optional[str] | NotGiven = NOT_GIVEN, - tags: TagUpdateMapParam | NotGiven = NOT_GIVEN, + tags: Dict[str, str] | NotGiven = NOT_GIVEN, user_data: str | NotGiven = NOT_GIVEN, username: str | NotGiven = NOT_GIVEN, polling_interval_seconds: int | NotGiven = NOT_GIVEN, diff --git a/src/gcore/resources/cloud/load_balancers/load_balancers.py b/src/gcore/resources/cloud/load_balancers/load_balancers.py index 89b8e06f..80d430fa 100644 --- a/src/gcore/resources/cloud/load_balancers/load_balancers.py +++ b/src/gcore/resources/cloud/load_balancers/load_balancers.py @@ -592,7 +592,7 @@ def create_and_poll( name: str | NotGiven = NOT_GIVEN, name_template: str | NotGiven = NOT_GIVEN, preferred_connectivity: LoadBalancerMemberConnectivity | NotGiven = NOT_GIVEN, - tags: TagUpdateMapParam | NotGiven = NOT_GIVEN, + tags: Dict[str, str] | NotGiven = NOT_GIVEN, vip_ip_family: InterfaceIPFamily | NotGiven = NOT_GIVEN, vip_network_id: str | NotGiven = NOT_GIVEN, vip_port_id: str | NotGiven = NOT_GIVEN, @@ -1276,7 +1276,7 @@ async def create_and_poll( name: str | NotGiven = NOT_GIVEN, name_template: str | NotGiven = NOT_GIVEN, preferred_connectivity: LoadBalancerMemberConnectivity | NotGiven = NOT_GIVEN, - tags: TagUpdateMapParam | NotGiven = NOT_GIVEN, + tags: Dict[str, str] | NotGiven = NOT_GIVEN, vip_ip_family: InterfaceIPFamily | NotGiven = NOT_GIVEN, vip_network_id: str | NotGiven = NOT_GIVEN, vip_port_id: str | NotGiven = NOT_GIVEN, From c73f5d1b84dbd2c96c9383100a93c89c8cf5e498 Mon Sep 17 00:00:00 2001 From: Algis Dumbris <151737200+algis-dumbris@users.noreply.github.com> Date: Tue, 17 Jun 2025 20:26:17 +0300 Subject: [PATCH 10/50] feat(cloud): add load balancers examples (#50) Refs GCLOUD2-18675 --- examples/cloud/load_balancers.py | 117 ++++++++++++++++++++++++ examples/cloud/load_balancers_async.py | 121 +++++++++++++++++++++++++ 2 files changed, 238 insertions(+) create mode 100644 examples/cloud/load_balancers.py create mode 100644 examples/cloud/load_balancers_async.py diff --git a/examples/cloud/load_balancers.py b/examples/cloud/load_balancers.py new file mode 100644 index 00000000..26de74ce --- /dev/null +++ b/examples/cloud/load_balancers.py @@ -0,0 +1,117 @@ +from gcore import Gcore + + +def main() -> None: + # TODO set cloud project ID before running + # cloud_project_id = os.environ["GCORE_CLOUD_PROJECT_ID"] + # TODO set cloud region ID before running + # cloud_region_id = os.environ["GCORE_CLOUD_REGION_ID"] + + gcore = Gcore(timeout=180.0) + + lb_id = create_load_balancer(client=gcore) + list_load_balancers(client=gcore) + get_load_balancer(client=gcore, loadbalancer_id=lb_id) + update_load_balancer(client=gcore, loadbalancer_id=lb_id) + resize_load_balancer(client=gcore, loadbalancer_id=lb_id) + failover_load_balancer(client=gcore, loadbalancer_id=lb_id) + + # Statuses + list_load_balancer_statuses(client=gcore) + get_load_balancer_status(client=gcore, loadbalancer_id=lb_id) + + # Metrics + get_load_balancer_metrics(client=gcore, loadbalancer_id=lb_id) + + delete_load_balancer(client=gcore, loadbalancer_id=lb_id) + + +def create_load_balancer(*, client: Gcore) -> str: + print("\n=== CREATE LOAD BALANCER ===") + lb = client.cloud.load_balancers.create_and_poll(flavor="lb1-1-2", name="gcore-go-example") + print(f"Created load balancer: ID={lb.id}, name={lb.name}, status={lb.provisioning_status}") + print("========================") + return lb.id + + +def list_load_balancers(*, client: Gcore) -> None: + print("\n=== LIST LOAD BALANCERS ===") + load_balancers = client.cloud.load_balancers.list() + for count, lb in enumerate(load_balancers, 1): + print(f"{count}. Load balancer: ID={lb.id}, name={lb.name}, status={lb.provisioning_status}") + if not load_balancers.results: + print("No load balancers found.") + print("========================") + + +def get_load_balancer(*, client: Gcore, loadbalancer_id: str) -> None: + print("\n=== GET LOAD BALANCER ===") + lb = client.cloud.load_balancers.get(loadbalancer_id=loadbalancer_id) + flavor_name = lb.flavor.flavor_name + print(f"Load balancer: ID={lb.id}, name={lb.name}, status={lb.provisioning_status}, flavor={flavor_name}") + print("========================") + + +def update_load_balancer(*, client: Gcore, loadbalancer_id: str) -> None: + print("\n=== UPDATE LOAD BALANCER ===") + lb = client.cloud.load_balancers.update(loadbalancer_id=loadbalancer_id, name="gcore-go-example-updated") + print(f"Updated load balancer: ID={lb.id}, name={lb.name}") + print("========================") + + +def resize_load_balancer(*, client: Gcore, loadbalancer_id: str) -> None: + print("\n=== RESIZE LOAD BALANCER ===") + lb = client.cloud.load_balancers.resize_and_poll(loadbalancer_id=loadbalancer_id, flavor="lb1-2-4") + print(f"Resized load balancer: ID={lb.id}, flavor=lb1-2-4") + print("========================") + + +def failover_load_balancer(*, client: Gcore, loadbalancer_id: str) -> None: + print("\n=== FAILOVER LOAD BALANCER ===") + lb = client.cloud.load_balancers.failover_and_poll(loadbalancer_id=loadbalancer_id) + print(f"Failed over load balancer: ID={lb.id}") + print("========================") + + +def list_load_balancer_statuses(*, client: Gcore) -> None: + print("\n=== LIST LOAD BALANCER STATUSES ===") + statuses = client.cloud.load_balancers.statuses.list() + for count, status in enumerate(statuses.results, 1): + print( + f"{count}. Load balancer status: ID={status.id}, operating status={status.operating_status}, provisioning status={status.provisioning_status}" + ) + print("========================") + + +def get_load_balancer_status(*, client: Gcore, loadbalancer_id: str) -> None: + print("\n=== GET LOAD BALANCER STATUS ===") + status = client.cloud.load_balancers.statuses.get(loadbalancer_id=loadbalancer_id) + print( + f"Load balancer status: ID={status.id}, operating status={status.operating_status}, provisioning status={status.provisioning_status}" + ) + print("========================") + + +def get_load_balancer_metrics(*, client: Gcore, loadbalancer_id: str) -> None: + print("\n=== GET LOAD BALANCER METRICS ===") + metrics = client.cloud.load_balancers.metrics.list( + loadbalancer_id=loadbalancer_id, + time_interval=1, + time_unit="hour", + ) + print(f"Load balancer metrics: ID={loadbalancer_id}") + if metrics.results: + metric = metrics.results[0] + print(f"CPU: {metric.cpu_util}%, memory: {metric.memory_util}%, time: {metric.time}") + print("========================") + + +def delete_load_balancer(*, client: Gcore, loadbalancer_id: str) -> None: + print("\n=== DELETE LOAD BALANCER ===") + client.cloud.load_balancers.delete_and_poll(loadbalancer_id=loadbalancer_id) + print(f"Deleted load balancer: ID={loadbalancer_id}") + print("========================") + + +if __name__ == "__main__": + main() diff --git a/examples/cloud/load_balancers_async.py b/examples/cloud/load_balancers_async.py new file mode 100644 index 00000000..d11c8320 --- /dev/null +++ b/examples/cloud/load_balancers_async.py @@ -0,0 +1,121 @@ +from __future__ import annotations + +import asyncio + +from gcore import AsyncGcore + + +async def main() -> None: + # TODO set cloud project ID before running + # cloud_project_id = os.environ["GCORE_CLOUD_PROJECT_ID"] + # TODO set cloud region ID before running + # cloud_region_id = os.environ["GCORE_CLOUD_REGION_ID"] + + gcore = AsyncGcore(timeout=180.0) + + lb_id = await create_load_balancer(client=gcore) + await list_load_balancers(client=gcore) + await get_load_balancer(client=gcore, loadbalancer_id=lb_id) + await update_load_balancer(client=gcore, loadbalancer_id=lb_id) + await resize_load_balancer(client=gcore, loadbalancer_id=lb_id) + await failover_load_balancer(client=gcore, loadbalancer_id=lb_id) + + # Statuses + await list_load_balancer_statuses(client=gcore) + await get_load_balancer_status(client=gcore, loadbalancer_id=lb_id) + + # Metrics + await get_load_balancer_metrics(client=gcore, loadbalancer_id=lb_id) + + await delete_load_balancer(client=gcore, loadbalancer_id=lb_id) + + +async def create_load_balancer(*, client: AsyncGcore) -> str: + print("\n=== CREATE LOAD BALANCER ===") + lb = await client.cloud.load_balancers.create_and_poll(flavor="lb1-1-2", name="gcore-go-example") + print(f"Created load balancer: ID={lb.id}, name={lb.name}, status={lb.provisioning_status}") + print("========================") + return lb.id + + +async def list_load_balancers(*, client: AsyncGcore) -> None: + print("\n=== LIST LOAD BALANCERS ===") + load_balancers = await client.cloud.load_balancers.list() + count = 1 + async for lb in load_balancers: + print(f"{count}. Load balancer: ID={lb.id}, name={lb.name}, status={lb.provisioning_status}") + count += 1 + print("========================") + + +async def get_load_balancer(*, client: AsyncGcore, loadbalancer_id: str) -> None: + print("\n=== GET LOAD BALANCER ===") + lb = await client.cloud.load_balancers.get(loadbalancer_id=loadbalancer_id) + flavor_name = lb.flavor.flavor_name + print(f"Load balancer: ID={lb.id}, name={lb.name}, status={lb.provisioning_status}, flavor={flavor_name}") + print("========================") + + +async def update_load_balancer(*, client: AsyncGcore, loadbalancer_id: str) -> None: + print("\n=== UPDATE LOAD BALANCER ===") + lb = await client.cloud.load_balancers.update(loadbalancer_id=loadbalancer_id, name="gcore-go-example-updated") + print(f"Updated load balancer: ID={lb.id}, name={lb.name}") + print("========================") + + +async def resize_load_balancer(*, client: AsyncGcore, loadbalancer_id: str) -> None: + print("\n=== RESIZE LOAD BALANCER ===") + lb = await client.cloud.load_balancers.resize_and_poll(loadbalancer_id=loadbalancer_id, flavor="lb1-2-4") + print(f"Resized load balancer: ID={lb.id}, flavor=lb1-2-4") + print("========================") + + +async def failover_load_balancer(*, client: AsyncGcore, loadbalancer_id: str) -> None: + print("\n=== FAILOVER LOAD BALANCER ===") + lb = await client.cloud.load_balancers.failover_and_poll(loadbalancer_id=loadbalancer_id) + print(f"Failed over load balancer: ID={lb.id}") + print("========================") + + +async def list_load_balancer_statuses(*, client: AsyncGcore) -> None: + print("\n=== LIST LOAD BALANCER STATUSES ===") + statuses = await client.cloud.load_balancers.statuses.list() + for count, status in enumerate(statuses.results, 1): + print( + f"{count}. Load balancer status: ID={status.id}, operating status={status.operating_status}, provisioning status={status.provisioning_status}" + ) + print("========================") + + +async def get_load_balancer_status(*, client: AsyncGcore, loadbalancer_id: str) -> None: + print("\n=== GET LOAD BALANCER STATUS ===") + status = await client.cloud.load_balancers.statuses.get(loadbalancer_id=loadbalancer_id) + print( + f"Load balancer status: ID={status.id}, operating status={status.operating_status}, provisioning status={status.provisioning_status}" + ) + print("========================") + + +async def get_load_balancer_metrics(*, client: AsyncGcore, loadbalancer_id: str) -> None: + print("\n=== GET LOAD BALANCER METRICS ===") + metrics = await client.cloud.load_balancers.metrics.list( + loadbalancer_id=loadbalancer_id, + time_interval=1, + time_unit="hour", + ) + print(f"Load balancer metrics: ID={loadbalancer_id}") + if metrics.results: + metric = metrics.results[0] + print(f"CPU: {metric.cpu_util}%, memory: {metric.memory_util}%, time: {metric.time}") + print("========================") + + +async def delete_load_balancer(*, client: AsyncGcore, loadbalancer_id: str) -> None: + print("\n=== DELETE LOAD BALANCER ===") + await client.cloud.load_balancers.delete_and_poll(loadbalancer_id=loadbalancer_id) + print(f"Deleted load balancer: ID={loadbalancer_id}") + print("========================") + + +if __name__ == "__main__": + asyncio.run(main()) From 7c3a5683c8482ca7398e6863c77fdae350b3e711 Mon Sep 17 00:00:00 2001 From: Danil Krox Date: Tue, 17 Jun 2025 19:35:36 +0200 Subject: [PATCH 11/50] chore(cloud): reorder example functions --- examples/cloud/file_shares.py | 38 ++++++++-------- examples/cloud/file_shares_async.py | 40 ++++++++--------- examples/cloud/ip_ranges.py | 6 ++- examples/cloud/ip_ranges_async.py | 8 ++-- examples/cloud/projects.py | 15 ++++--- examples/cloud/projects_async.py | 17 ++++---- examples/cloud/quotas.py | 18 +++++--- examples/cloud/quotas_async.py | 20 ++++----- examples/cloud/regions.py | 10 +++-- examples/cloud/regions_async.py | 12 +++--- examples/cloud/reserved_fixed_ips.py | 6 ++- examples/cloud/reserved_fixed_ips_async.py | 50 +++++++++++----------- examples/cloud/secrets.py | 12 ++++-- examples/cloud/secrets_async.py | 14 +++--- examples/cloud/security_groups_async.py | 6 +-- examples/cloud/ssh_keys.py | 16 ++++--- examples/cloud/ssh_keys_async.py | 18 ++++---- examples/cloud/task.py | 8 +++- examples/cloud/task_async.py | 10 ++--- examples/cloud/volumes_async.py | 32 +++++++------- 20 files changed, 193 insertions(+), 163 deletions(-) diff --git a/examples/cloud/file_shares.py b/examples/cloud/file_shares.py index 964c1da9..9e3e4f0f 100644 --- a/examples/cloud/file_shares.py +++ b/examples/cloud/file_shares.py @@ -4,6 +4,26 @@ from gcore.types.cloud.file_share_create_params import CreateStandardFileShareSerializerNetwork +def main() -> None: + # TODO set cloud network ID before running + cloud_network_id = os.environ["GCORE_CLOUD_NETWORK_ID"] + + gcore = Gcore(timeout=180.0) + + fs_id = create_file_share(client=gcore, network_id=cloud_network_id) + list_file_shares(client=gcore) + get_file_share(client=gcore, file_share_id=fs_id) + update_file_share(client=gcore, file_share_id=fs_id) + resize_file_share(client=gcore, file_share_id=fs_id) + + # Access rules + access_rule_id = create_file_share_access_rule(client=gcore, file_share_id=fs_id) + list_file_share_access_rules(client=gcore, file_share_id=fs_id) + delete_file_share_access_rule(client=gcore, file_share_id=fs_id, access_rule_id=access_rule_id) + + delete_file_share(client=gcore, file_share_id=fs_id) + + def create_file_share(*, client: Gcore, network_id: str) -> str: print("\n=== CREATE FILE SHARE ===") response = client.cloud.file_shares.create( @@ -99,20 +119,4 @@ def delete_file_share_access_rule(*, client: Gcore, file_share_id: str, access_r if __name__ == "__main__": - # TODO set cloud network ID before running - cloud_network_id = os.environ["GCORE_CLOUD_NETWORK_ID"] - - gcore = Gcore(timeout=180.0) - - fs_id = create_file_share(client=gcore, network_id=cloud_network_id) - list_file_shares(client=gcore) - get_file_share(client=gcore, file_share_id=fs_id) - update_file_share(client=gcore, file_share_id=fs_id) - resize_file_share(client=gcore, file_share_id=fs_id) - - # Access rules - access_rule_id = create_file_share_access_rule(client=gcore, file_share_id=fs_id) - list_file_share_access_rules(client=gcore, file_share_id=fs_id) - delete_file_share_access_rule(client=gcore, file_share_id=fs_id, access_rule_id=access_rule_id) - - delete_file_share(client=gcore, file_share_id=fs_id) + main() diff --git a/examples/cloud/file_shares_async.py b/examples/cloud/file_shares_async.py index 9adc3202..f8459294 100644 --- a/examples/cloud/file_shares_async.py +++ b/examples/cloud/file_shares_async.py @@ -5,6 +5,26 @@ from gcore.types.cloud.file_share_create_params import CreateStandardFileShareSerializerNetwork +async def main() -> None: + # TODO set cloud network ID before running + cloud_network_id = os.environ["GCORE_CLOUD_NETWORK_ID"] + + gcore = AsyncGcore(timeout=180.0) + + fs_id = await create_file_share(client=gcore, network_id=cloud_network_id) + await list_file_shares(client=gcore) + await get_file_share(client=gcore, file_share_id=fs_id) + await update_file_share(client=gcore, file_share_id=fs_id) + await resize_file_share(client=gcore, file_share_id=fs_id) + + # Access rules + access_rule_id = await create_file_share_access_rule(client=gcore, file_share_id=fs_id) + await list_file_share_access_rules(client=gcore, file_share_id=fs_id) + await delete_file_share_access_rule(client=gcore, file_share_id=fs_id, access_rule_id=access_rule_id) + + await delete_file_share(client=gcore, file_share_id=fs_id) + + async def create_file_share(client: AsyncGcore, *, network_id: str) -> str: print("\n=== CREATE FILE SHARE ===") network = CreateStandardFileShareSerializerNetwork(network_id=network_id) @@ -102,25 +122,5 @@ async def delete_file_share(*, client: AsyncGcore, file_share_id: str) -> None: print("========================") -async def main() -> None: - # TODO set cloud network ID before running - cloud_network_id = os.environ["GCORE_CLOUD_NETWORK_ID"] - - gcore = AsyncGcore(timeout=180.0) - - fs_id = await create_file_share(client=gcore, network_id=cloud_network_id) - await list_file_shares(client=gcore) - await get_file_share(client=gcore, file_share_id=fs_id) - await update_file_share(client=gcore, file_share_id=fs_id) - await resize_file_share(client=gcore, file_share_id=fs_id) - - # Access rules - access_rule_id = await create_file_share_access_rule(client=gcore, file_share_id=fs_id) - await list_file_share_access_rules(client=gcore, file_share_id=fs_id) - await delete_file_share_access_rule(client=gcore, file_share_id=fs_id, access_rule_id=access_rule_id) - - await delete_file_share(client=gcore, file_share_id=fs_id) - - if __name__ == "__main__": asyncio.run(main()) diff --git a/examples/cloud/ip_ranges.py b/examples/cloud/ip_ranges.py index 1604d51e..e592c23d 100644 --- a/examples/cloud/ip_ranges.py +++ b/examples/cloud/ip_ranges.py @@ -4,6 +4,10 @@ from gcore.types.cloud import IPRanges +def main() -> None: + list_all_ip_ranges() + + def list_all_ip_ranges() -> IPRanges: # No need to pass the API key explicitly — it will automatically be read from the GCORE_API_KEY environment variable if omitted gcore = Gcore(api_key=os.environ.get("GCORE_API_KEY"), base_url=os.environ.get("GCORE_API_URL")) @@ -17,4 +21,4 @@ def list_all_ip_ranges() -> IPRanges: if __name__ == "__main__": - list_all_ip_ranges() + main() diff --git a/examples/cloud/ip_ranges_async.py b/examples/cloud/ip_ranges_async.py index 72b5e929..f5108d8a 100644 --- a/examples/cloud/ip_ranges_async.py +++ b/examples/cloud/ip_ranges_async.py @@ -5,6 +5,10 @@ from gcore.types.cloud import IPRanges +async def main() -> None: + await list_all_ip_ranges() + + async def list_all_ip_ranges() -> IPRanges: # No need to pass the API key explicitly — it will automatically be read from the GCORE_API_KEY environment variable if omitted gcore = AsyncGcore(api_key=os.environ.get("GCORE_API_KEY"), base_url=os.environ.get("GCORE_API_URL")) @@ -19,9 +23,5 @@ async def list_all_ip_ranges() -> IPRanges: return all_ip_ranges -async def main() -> None: - await list_all_ip_ranges() - - if __name__ == "__main__": asyncio.run(main()) diff --git a/examples/cloud/projects.py b/examples/cloud/projects.py index ff30a6ca..99170215 100644 --- a/examples/cloud/projects.py +++ b/examples/cloud/projects.py @@ -5,6 +5,14 @@ from gcore.types.cloud import Project +def main() -> None: + new_project = create_new_project() + list_all_projects() + get_project_by_id() + update_project(project_id=new_project.id) + delete_project(project_id=new_project.id) + + def get_project_by_id() -> Project: # No need to pass the API key explicitly — it will automatically be read from the GCORE_API_KEY environment variable if omitted gcore = Gcore(api_key=os.environ.get("GCORE_API_KEY"), base_url=os.environ.get("GCORE_API_URL")) @@ -63,9 +71,4 @@ def delete_project(project_id: int) -> None: if __name__ == "__main__": - # Follow the order: create, list, get, delete - new_project = create_new_project() - list_all_projects() - get_project_by_id() - update_project(project_id=new_project.id) - delete_project(project_id=new_project.id) + main() diff --git a/examples/cloud/projects_async.py b/examples/cloud/projects_async.py index 4588d7fa..b1cd431b 100644 --- a/examples/cloud/projects_async.py +++ b/examples/cloud/projects_async.py @@ -6,6 +6,14 @@ from gcore.types.cloud import Project +async def main() -> None: + new_project = await create_new_project() + await list_all_projects() + await get_project_by_id() + await update_project(project_id=new_project.id) + await delete_project(project_id=new_project.id) + + async def get_project_by_id() -> Project: # No need to pass the API key explicitly — it will automatically be read from the GCORE_API_KEY environment variable if omitted gcore = AsyncGcore(api_key=os.environ.get("GCORE_API_KEY"), base_url=os.environ.get("GCORE_API_URL")) @@ -65,14 +73,5 @@ async def delete_project(project_id: int) -> None: print("=======================") -async def main() -> None: - # Follow the order: create, list, get, update, delete - new_project = await create_new_project() - await list_all_projects() - await get_project_by_id() - await update_project(project_id=new_project.id) - await delete_project(project_id=new_project.id) - - if __name__ == "__main__": asyncio.run(main()) diff --git a/examples/cloud/quotas.py b/examples/cloud/quotas.py index 5f988949..15d8add3 100644 --- a/examples/cloud/quotas.py +++ b/examples/cloud/quotas.py @@ -7,6 +7,16 @@ from gcore.types.cloud.quota_get_by_region_response import QuotaGetByRegionResponse +def main() -> None: + gcore_client_id = int(os.environ["GCORE_CLIENT_ID"]) + + gcore = Gcore() + + get_all_quotas(client=gcore) + get_regional_quotas(client=gcore, client_id=gcore_client_id) + get_global_quotas(client=gcore, client_id=gcore_client_id) + + def get_all_quotas(*, client: Gcore) -> None: print("\n=== GET ALL QUOTAS ===") all_quotas = client.cloud.quotas.get_all() @@ -74,10 +84,4 @@ def _print_truncated_fields(fields: "list[tuple[str, Any]]", *, display_limit: i if __name__ == "__main__": - gcore_client_id = int(os.environ["GCORE_CLIENT_ID"]) - - gcore = Gcore() - - get_all_quotas(client=gcore) - get_regional_quotas(client=gcore, client_id=gcore_client_id) - get_global_quotas(client=gcore, client_id=gcore_client_id) + main() diff --git a/examples/cloud/quotas_async.py b/examples/cloud/quotas_async.py index e0f9348f..5d969ce1 100644 --- a/examples/cloud/quotas_async.py +++ b/examples/cloud/quotas_async.py @@ -8,6 +8,16 @@ from gcore.types.cloud.quota_get_by_region_response import QuotaGetByRegionResponse +async def main() -> None: + gcore_client_id = int(os.environ["GCORE_CLIENT_ID"]) + + gcore = AsyncGcore() + + await get_all_quotas(client=gcore) + await get_regional_quotas(client=gcore, client_id=gcore_client_id) + await get_global_quotas(client=gcore, client_id=gcore_client_id) + + async def get_all_quotas(*, client: AsyncGcore) -> None: print("\n=== GET ALL QUOTAS ===") all_quotas = await client.cloud.quotas.get_all() @@ -74,15 +84,5 @@ def _print_truncated_fields(fields: "list[tuple[str, Any]]", *, display_limit: i print(f"{indent}... and {len(fields) - display_count} more quota fields") -async def main() -> None: - gcore_client_id = int(os.environ["GCORE_CLIENT_ID"]) - - gcore = AsyncGcore() - - await get_all_quotas(client=gcore) - await get_regional_quotas(client=gcore, client_id=gcore_client_id) - await get_global_quotas(client=gcore, client_id=gcore_client_id) - - if __name__ == "__main__": asyncio.run(main()) diff --git a/examples/cloud/regions.py b/examples/cloud/regions.py index 22c7ed88..d2c9d243 100644 --- a/examples/cloud/regions.py +++ b/examples/cloud/regions.py @@ -5,6 +5,12 @@ from gcore.types.cloud import Region +def main() -> None: + get_region_by_id() + list_all_regions() + list_regions_with_filters() + + def get_region_by_id() -> Region: # No need to pass the API key explicitly — it will automatically be read from the GCORE_API_KEY environment variable if omitted gcore = Gcore(api_key=os.environ.get("GCORE_API_KEY")) @@ -40,6 +46,4 @@ def list_regions_with_filters() -> SyncOffsetPage[Region]: if __name__ == "__main__": - get_region_by_id() - list_all_regions() - list_regions_with_filters() + main() diff --git a/examples/cloud/regions_async.py b/examples/cloud/regions_async.py index cf5fce3d..f5069d99 100644 --- a/examples/cloud/regions_async.py +++ b/examples/cloud/regions_async.py @@ -6,6 +6,12 @@ from gcore.types.cloud import Region +async def main() -> None: + await get_region_by_id() + await list_all_regions() + await list_regions_with_filters() + + async def get_region_by_id() -> Region: # No need to pass the API key explicitly — it will automatically be read from the GCORE_API_KEY environment variable if omitted gcore = AsyncGcore(api_key=os.environ.get("GCORE_API_KEY")) @@ -44,11 +50,5 @@ async def list_regions_with_filters() -> AsyncOffsetPage[Region]: return filtered_regions -async def main() -> None: - await get_region_by_id() - await list_all_regions() - await list_regions_with_filters() - - if __name__ == "__main__": asyncio.run(main()) diff --git a/examples/cloud/reserved_fixed_ips.py b/examples/cloud/reserved_fixed_ips.py index 5d18ce92..d6691d66 100644 --- a/examples/cloud/reserved_fixed_ips.py +++ b/examples/cloud/reserved_fixed_ips.py @@ -70,7 +70,7 @@ def delete_reserved_fixed_ip(*, client: Gcore, port_id: str) -> None: print("========================") -if __name__ == "__main__": +def main() -> None: # No need to pass the API key explicitly — it will automatically be read from the GCORE_API_KEY environment variable if omitted # api_key = os.environ.get("GCORE_API_KEY") # Will use Production API URL if omitted @@ -93,3 +93,7 @@ def delete_reserved_fixed_ip(*, client: Gcore, port_id: str) -> None: toggle_reserved_fixed_ip_vip(client=gcore, port_id=fixed_ip.port_id, is_vip=False) delete_reserved_fixed_ip(client=gcore, port_id=fixed_ip.port_id) + + +if __name__ == "__main__": + main() diff --git a/examples/cloud/reserved_fixed_ips_async.py b/examples/cloud/reserved_fixed_ips_async.py index 28e9e96f..f3a1d9f7 100644 --- a/examples/cloud/reserved_fixed_ips_async.py +++ b/examples/cloud/reserved_fixed_ips_async.py @@ -10,6 +10,31 @@ from gcore.types.cloud import ReservedFixedIP +async def main() -> None: + # No need to pass the API key explicitly — it will automatically be read from the GCORE_API_KEY environment variable if omitted + # api_key = os.environ.get("GCORE_API_KEY") + # Will use Production API URL if omitted + # base_url = os.environ.get("GCORE_BASE_URL") + + gcore = AsyncGcore( + # api_key=api_key, + # base_url=base_url, + ) + + fixed_ip = await create_reserved_fixed_ip(client=gcore) + await list_reserved_fixed_ips(client=gcore) + await get_reserved_fixed_ip(client=gcore, port_id=fixed_ip.port_id) + + # VIP + await toggle_reserved_fixed_ip_vip(client=gcore, port_id=fixed_ip.port_id, is_vip=True) + await list_candidate_ports(client=gcore, port_id=fixed_ip.port_id) + await list_connected_ports(client=gcore, port_id=fixed_ip.port_id) + # is_vip needs to be false to delete the reserved fixed IP + await toggle_reserved_fixed_ip_vip(client=gcore, port_id=fixed_ip.port_id, is_vip=False) + + await delete_reserved_fixed_ip(client=gcore, port_id=fixed_ip.port_id) + + async def create_reserved_fixed_ip(*, client: AsyncGcore) -> ReservedFixedIP: print("\n=== CREATE RESERVED FIXED IP ===") task_list = await client.cloud.reserved_fixed_ips.create( @@ -79,30 +104,5 @@ async def delete_reserved_fixed_ip(*, client: AsyncGcore, port_id: str) -> None: print("========================") -async def main() -> None: - # No need to pass the API key explicitly — it will automatically be read from the GCORE_API_KEY environment variable if omitted - # api_key = os.environ.get("GCORE_API_KEY") - # Will use Production API URL if omitted - # base_url = os.environ.get("GCORE_BASE_URL") - - gcore = AsyncGcore( - # api_key=api_key, - # base_url=base_url, - ) - - fixed_ip = await create_reserved_fixed_ip(client=gcore) - await list_reserved_fixed_ips(client=gcore) - await get_reserved_fixed_ip(client=gcore, port_id=fixed_ip.port_id) - - # VIP - await toggle_reserved_fixed_ip_vip(client=gcore, port_id=fixed_ip.port_id, is_vip=True) - await list_candidate_ports(client=gcore, port_id=fixed_ip.port_id) - await list_connected_ports(client=gcore, port_id=fixed_ip.port_id) - # is_vip needs to be false to delete the reserved fixed IP - await toggle_reserved_fixed_ip_vip(client=gcore, port_id=fixed_ip.port_id, is_vip=False) - - await delete_reserved_fixed_ip(client=gcore, port_id=fixed_ip.port_id) - - if __name__ == "__main__": asyncio.run(main()) diff --git a/examples/cloud/secrets.py b/examples/cloud/secrets.py index bd67260f..ac1d33e0 100644 --- a/examples/cloud/secrets.py +++ b/examples/cloud/secrets.py @@ -6,6 +6,13 @@ from gcore.types.cloud.secret_upload_tls_certificate_params import Payload +def main() -> None: + cert = upload_tls_cert() + get_secret_by_id(cert.id) + list_all_secrets() + delete_secret(cert.id) + + def upload_tls_cert() -> Secret: # No need to pass the API key explicitly — it will automatically be read from the GCORE_API_KEY environment variable if omitted gcore = Gcore(api_key=os.environ.get("GCORE_API_KEY")) @@ -61,7 +68,4 @@ def delete_secret(secret_id: str) -> None: if __name__ == "__main__": - cert = upload_tls_cert() - get_secret_by_id(cert.id) - list_all_secrets() - delete_secret(cert.id) + main() diff --git a/examples/cloud/secrets_async.py b/examples/cloud/secrets_async.py index 53ca7724..61c074f1 100644 --- a/examples/cloud/secrets_async.py +++ b/examples/cloud/secrets_async.py @@ -9,6 +9,13 @@ from gcore.types.cloud.secret_upload_tls_certificate_params import Payload +async def main() -> None: + cert = await upload_tls_cert() + await get_secret_by_id(cert.id) + await list_all_secrets() + await delete_secret(cert.id) + + async def upload_tls_cert() -> Secret: # No need to pass the API key explicitly — it will automatically be read from the GCORE_API_KEY environment variable if omitted gcore = AsyncGcore(api_key=os.environ.get("GCORE_API_KEY")) @@ -73,12 +80,5 @@ async def delete_secret(secret_id: str) -> None: print("=====================") -async def main() -> None: - cert = await upload_tls_cert() - await get_secret_by_id(cert.id) - await list_all_secrets() - await delete_secret(cert.id) - - if __name__ == "__main__": asyncio.run(main()) diff --git a/examples/cloud/security_groups_async.py b/examples/cloud/security_groups_async.py index ff5388e7..13123835 100644 --- a/examples/cloud/security_groups_async.py +++ b/examples/cloud/security_groups_async.py @@ -22,9 +22,7 @@ async def main() -> None: async def create_security_group(client: AsyncGcore) -> str: print("\n=== CREATE SECURITY GROUP ===") - security_group = await client.cloud.security_groups.create( - security_group=SecurityGroup(name="gcore-go-example") - ) + security_group = await client.cloud.security_groups.create(security_group=SecurityGroup(name="gcore-go-example")) print(f"Created security group: ID={security_group.id}, name={security_group.name}") print("========================") return security_group.id @@ -59,13 +57,11 @@ async def update_security_group(*, client: AsyncGcore, security_group_id: str) - print("========================") - async def delete_security_group(*, client: AsyncGcore, security_group_id: str) -> None: print("\n=== DELETE SECURITY GROUP ===") await client.cloud.security_groups.delete(group_id=security_group_id) print(f"Deleted security group: ID={security_group_id}") print("========================") -# Security Group Rules functions async def create_security_group_rule(*, client: AsyncGcore, security_group_id: str) -> str: diff --git a/examples/cloud/ssh_keys.py b/examples/cloud/ssh_keys.py index 72c90667..ad73f8e3 100644 --- a/examples/cloud/ssh_keys.py +++ b/examples/cloud/ssh_keys.py @@ -5,6 +5,15 @@ from gcore.types.cloud import SSHKey, SSHKeyCreated +def main() -> None: + # Follow the order: create, list, get, update, delete + new_ssh_key = create_new_ssh_key() + list_all_ssh_keys() + get_ssh_key_by_id(new_ssh_key.id) + update_ssh_key(ssh_key_id=new_ssh_key.id) + delete_ssh_key(ssh_key_id=new_ssh_key.id) + + def get_ssh_key_by_id(ssh_key_id: str) -> SSHKey: # No need to pass the API key explicitly — it will automatically be read from the GCORE_API_KEY environment variable if omitted gcore = Gcore(api_key=os.environ.get("GCORE_API_KEY"), base_url=os.environ.get("GCORE_API_URL")) @@ -59,9 +68,4 @@ def delete_ssh_key(ssh_key_id: str) -> None: if __name__ == "__main__": - # Follow the order: create, list, get, update, delete - new_ssh_key = create_new_ssh_key() - list_all_ssh_keys() - get_ssh_key_by_id(new_ssh_key.id) - update_ssh_key(ssh_key_id=new_ssh_key.id) - delete_ssh_key(ssh_key_id=new_ssh_key.id) + main() diff --git a/examples/cloud/ssh_keys_async.py b/examples/cloud/ssh_keys_async.py index 91aeedb7..2ed7740f 100644 --- a/examples/cloud/ssh_keys_async.py +++ b/examples/cloud/ssh_keys_async.py @@ -6,6 +6,15 @@ from gcore.types.cloud import SSHKey, SSHKeyCreated +async def main() -> None: + # Follow the order: create, list, get, update, delete + new_ssh_key = await create_new_ssh_key() + await list_all_ssh_keys() + await get_ssh_key_by_id(new_ssh_key.id) + await update_ssh_key(ssh_key_id=new_ssh_key.id) + await delete_ssh_key(ssh_key_id=new_ssh_key.id) + + async def get_ssh_key_by_id(ssh_key_id: str) -> SSHKey: # No need to pass the API key explicitly — it will automatically be read from the GCORE_API_KEY environment variable if omitted gcore = AsyncGcore(api_key=os.environ.get("GCORE_API_KEY"), base_url=os.environ.get("GCORE_API_URL")) @@ -62,14 +71,5 @@ async def delete_ssh_key(ssh_key_id: str) -> None: print("=======================") -async def main() -> None: - # Follow the order: create, list, get, update, delete - new_ssh_key = await create_new_ssh_key() - await list_all_ssh_keys() - await get_ssh_key_by_id(new_ssh_key.id) - await update_ssh_key(ssh_key_id=new_ssh_key.id) - await delete_ssh_key(ssh_key_id=new_ssh_key.id) - - if __name__ == "__main__": asyncio.run(main()) diff --git a/examples/cloud/task.py b/examples/cloud/task.py index 667a11bc..cc8ebba2 100644 --- a/examples/cloud/task.py +++ b/examples/cloud/task.py @@ -8,6 +8,11 @@ from gcore.types.cloud import Task +def main() -> None: + get_task_by_id() + list_tasks() + + def list_tasks() -> Optional[SyncOffsetPage[Task]]: """Demonstrates listing all tasks for a project and region.""" # The API key is read automatically from the GCORE_API_KEY environment variable if omitted @@ -82,5 +87,4 @@ def get_task_by_id() -> Optional[Task]: if __name__ == "__main__": - get_task_by_id() - list_tasks() + main() diff --git a/examples/cloud/task_async.py b/examples/cloud/task_async.py index ec0e8fd8..1793fe8f 100644 --- a/examples/cloud/task_async.py +++ b/examples/cloud/task_async.py @@ -9,6 +9,11 @@ from gcore.types.cloud import Task +async def main() -> None: + await get_task_by_id() + await list_tasks() + + async def list_tasks() -> Optional[AsyncOffsetPage[Task]]: """Demonstrates listing all tasks for a project and region asynchronously.""" # The API key is read automatically from the GCORE_API_KEY environment variable if omitted @@ -82,10 +87,5 @@ async def get_task_by_id() -> Optional[Task]: return task -async def main() -> None: - await get_task_by_id() - await list_tasks() - - if __name__ == "__main__": asyncio.run(main()) diff --git a/examples/cloud/volumes_async.py b/examples/cloud/volumes_async.py index 664cd0d3..e9c1ed64 100644 --- a/examples/cloud/volumes_async.py +++ b/examples/cloud/volumes_async.py @@ -4,6 +4,22 @@ from gcore import AsyncGcore +async def main() -> None: + instance_id = os.environ["GCORE_CLOUD_INSTANCE_ID"] + + gcore = AsyncGcore() + + volume_id = await create_volume(client=gcore) + await list_volumes(client=gcore) + await get_volume(client=gcore, volume_id=volume_id) + await update_volume(client=gcore, volume_id=volume_id) + await attach_to_instance(client=gcore, volume_id=volume_id, instance_id=instance_id) + await detach_from_instance(client=gcore, volume_id=volume_id, instance_id=instance_id) + await change_type(client=gcore, volume_id=volume_id) + await resize(client=gcore, volume_id=volume_id) + await delete_volume(client=gcore, volume_id=volume_id) + + async def create_volume(*, client: AsyncGcore) -> str: print("\n=== CREATE VOLUME ===") response = await client.cloud.volumes.create( @@ -89,21 +105,5 @@ async def delete_volume(*, client: AsyncGcore, volume_id: str) -> None: print("========================") -async def main() -> None: - instance_id = os.environ["GCORE_CLOUD_INSTANCE_ID"] - - gcore = AsyncGcore() - - volume_id = await create_volume(client=gcore) - await list_volumes(client=gcore) - await get_volume(client=gcore, volume_id=volume_id) - await update_volume(client=gcore, volume_id=volume_id) - await attach_to_instance(client=gcore, volume_id=volume_id, instance_id=instance_id) - await detach_from_instance(client=gcore, volume_id=volume_id, instance_id=instance_id) - await change_type(client=gcore, volume_id=volume_id) - await resize(client=gcore, volume_id=volume_id) - await delete_volume(client=gcore, volume_id=volume_id) - - if __name__ == "__main__": asyncio.run(main()) From 6ba343d802d353c4df42e4e7f6cc4f693fe9734a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 18 Jun 2025 02:21:43 +0000 Subject: [PATCH 12/50] chore(readme): update badges --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9a23b61d..44ec1713 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Gcore Python API library -[![PyPI version](https://img.shields.io/pypi/v/gcore.svg)](https://pypi.org/project/gcore/) +[![PyPI version]()](https://pypi.org/project/gcore/) The Gcore Python library provides convenient access to the Gcore REST API from any Python 3.8+ application. The library includes type definitions for all request params and response fields, From 3d88ae5a7df41b7e9e88d0520a9586c1240da54c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 18 Jun 2025 06:02:43 +0000 Subject: [PATCH 13/50] fix(tests): fix: tests which call HTTP endpoints directly with the example parameters --- tests/test_client.py | 41 ++++++++--------------------------------- 1 file changed, 8 insertions(+), 33 deletions(-) diff --git a/tests/test_client.py b/tests/test_client.py index c6b0df66..bef5cc73 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -23,9 +23,7 @@ from gcore import Gcore, AsyncGcore, APIResponseValidationError from gcore._types import Omit -from gcore._utils import maybe_transform from gcore._models import BaseModel, FinalRequestOptions -from gcore._constants import RAW_RESPONSE_HEADER from gcore._exceptions import GcoreError, APIStatusError, APITimeoutError, APIResponseValidationError from gcore._base_client import ( DEFAULT_TIMEOUT, @@ -35,7 +33,6 @@ DefaultAsyncHttpxClient, make_request_options, ) -from gcore.types.cloud.project_create_params import ProjectCreateParams from .utils import update_env @@ -735,32 +732,21 @@ def test_parse_retry_after_header(self, remaining_retries: int, retry_after: str @mock.patch("gcore._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) - def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter) -> None: + def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, client: Gcore) -> None: respx_mock.post("/cloud/v1/projects").mock(side_effect=httpx.TimeoutException("Test timeout error")) with pytest.raises(APITimeoutError): - self.client.post( - "/cloud/v1/projects", - body=cast(object, maybe_transform(dict(name="New Project"), ProjectCreateParams)), - cast_to=httpx.Response, - options={"headers": {RAW_RESPONSE_HEADER: "stream"}}, - ) + client.cloud.projects.with_streaming_response.create(name="New Project").__enter__() assert _get_open_connections(self.client) == 0 @mock.patch("gcore._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) - def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter) -> None: + def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, client: Gcore) -> None: respx_mock.post("/cloud/v1/projects").mock(return_value=httpx.Response(500)) with pytest.raises(APIStatusError): - self.client.post( - "/cloud/v1/projects", - body=cast(object, maybe_transform(dict(name="New Project"), ProjectCreateParams)), - cast_to=httpx.Response, - options={"headers": {RAW_RESPONSE_HEADER: "stream"}}, - ) - + client.cloud.projects.with_streaming_response.create(name="New Project").__enter__() assert _get_open_connections(self.client) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @@ -1582,32 +1568,21 @@ async def test_parse_retry_after_header(self, remaining_retries: int, retry_afte @mock.patch("gcore._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) - async def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter) -> None: + async def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, async_client: AsyncGcore) -> None: respx_mock.post("/cloud/v1/projects").mock(side_effect=httpx.TimeoutException("Test timeout error")) with pytest.raises(APITimeoutError): - await self.client.post( - "/cloud/v1/projects", - body=cast(object, maybe_transform(dict(name="New Project"), ProjectCreateParams)), - cast_to=httpx.Response, - options={"headers": {RAW_RESPONSE_HEADER: "stream"}}, - ) + await async_client.cloud.projects.with_streaming_response.create(name="New Project").__aenter__() assert _get_open_connections(self.client) == 0 @mock.patch("gcore._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) - async def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter) -> None: + async def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, async_client: AsyncGcore) -> None: respx_mock.post("/cloud/v1/projects").mock(return_value=httpx.Response(500)) with pytest.raises(APIStatusError): - await self.client.post( - "/cloud/v1/projects", - body=cast(object, maybe_transform(dict(name="New Project"), ProjectCreateParams)), - cast_to=httpx.Response, - options={"headers": {RAW_RESPONSE_HEADER: "stream"}}, - ) - + await async_client.cloud.projects.with_streaming_response.create(name="New Project").__aenter__() assert _get_open_connections(self.client) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) From 5f32d6f75aec5a7ff33729c65984f8b171910f8b Mon Sep 17 00:00:00 2001 From: Algis Dumbris <151737200+algis-dumbris@users.noreply.github.com> Date: Wed, 18 Jun 2025 10:56:46 +0300 Subject: [PATCH 14/50] feat(cloud): add networks examples Refs GCLOUD2-18670 --- examples/cloud/networks.py | 121 +++++++++++++++++++++++++++++ examples/cloud/networks_async.py | 127 +++++++++++++++++++++++++++++++ 2 files changed, 248 insertions(+) create mode 100644 examples/cloud/networks.py create mode 100644 examples/cloud/networks_async.py diff --git a/examples/cloud/networks.py b/examples/cloud/networks.py new file mode 100644 index 00000000..ba360164 --- /dev/null +++ b/examples/cloud/networks.py @@ -0,0 +1,121 @@ +from gcore import Gcore + + +def main() -> None: + # TODO set cloud project ID before running + # cloud_project_id = os.environ["GCORE_CLOUD_PROJECT_ID"] + # TODO set cloud region ID before running + # cloud_region_id = os.environ["GCORE_CLOUD_REGION_ID"] + + gcore = Gcore() + + network_id = create_network(client=gcore) + list_networks(client=gcore) + get_network(client=gcore, network_id=network_id) + update_network(client=gcore, network_id=network_id) + + # Subnets + subnet_id = create_subnet(client=gcore, network_id=network_id) + list_subnets(client=gcore, network_id=network_id) + get_subnet(client=gcore, subnet_id=subnet_id) + update_subnet(client=gcore, subnet_id=subnet_id) + delete_subnet(client=gcore, subnet_id=subnet_id) + + delete_network(client=gcore, network_id=network_id) + + +def create_network(*, client: Gcore) -> str: + print("\n=== CREATE NETWORK ===") + response = client.cloud.networks.create(name="gcore-go-example", create_router=True, type="vxlan") + task_id = response.tasks[0] + task = client.cloud.tasks.poll(task_id=task_id) + if task.created_resources is None or task.created_resources.networks is None: + raise RuntimeError("Task completed but created_resources or networks is missing") + network_id: str = task.created_resources.networks[0] + print(f"Created network: ID={network_id}") + print("========================") + return network_id + + +def list_networks(*, client: Gcore) -> None: + print("\n=== LIST NETWORKS ===") + networks = client.cloud.networks.list() + for count, network in enumerate(networks, 1): + print(f"{count}. Network: ID={network.id}, name={network.name}, type={network.type}") + print("========================") + + +def get_network(*, client: Gcore, network_id: str) -> None: + print("\n=== GET NETWORK ===") + network = client.cloud.networks.get(network_id=network_id) + print(f"Network: ID={network.id}, name={network.name}, type={network.type}") + print("========================") + + +def update_network(*, client: Gcore, network_id: str) -> None: + print("\n=== UPDATE NETWORK ===") + network = client.cloud.networks.update(network_id=network_id, name="gcore-go-example-updated") + print(f"Updated network: ID={network.id}, name={network.name}") + print("========================") + + +def create_subnet(*, client: Gcore, network_id: str) -> str: + print("\n=== CREATE SUBNET ===") + response = client.cloud.networks.subnets.create( + network_id=network_id, + cidr="192.168.1.0/24", + name="gcore-go-example", + enable_dhcp=True, + ip_version=4, + ) + task_id = response.tasks[0] + task = client.cloud.tasks.poll(task_id=task_id) + if task.created_resources is None or task.created_resources.subnets is None: + raise RuntimeError("Task completed but created_resources or subnets is missing") + subnet_id: str = task.created_resources.subnets[0] + print(f"Created subnet: ID={subnet_id}") + print("========================") + return subnet_id + + +def list_subnets(*, client: Gcore, network_id: str) -> None: + print("\n=== LIST SUBNETS ===") + subnets = client.cloud.networks.subnets.list(network_id=network_id) + for count, subnet in enumerate(subnets, 1): + print(f"{count}. Subnet: ID={subnet.id}, CIDR={subnet.cidr}, name={subnet.name}") + print("========================") + + +def get_subnet(*, client: Gcore, subnet_id: str) -> None: + print("\n=== GET SUBNET ===") + subnet = client.cloud.networks.subnets.get(subnet_id=subnet_id) + print(f"Subnet: ID={subnet.id}, CIDR={subnet.cidr}, name={subnet.name}") + print("========================") + + +def update_subnet(*, client: Gcore, subnet_id: str) -> None: + print("\n=== UPDATE SUBNET ===") + subnet = client.cloud.networks.subnets.update(subnet_id=subnet_id, name="gcore-go-example-updated") + print(f"Updated subnet: ID={subnet.id}, name={subnet.name}") + print("========================") + + +def delete_subnet(*, client: Gcore, subnet_id: str) -> None: + print("\n=== DELETE SUBNET ===") + client.cloud.networks.subnets.delete(subnet_id=subnet_id) + print(f"Deleted subnet: ID={subnet_id}") + print("========================") + + +def delete_network(*, client: Gcore, network_id: str) -> None: + print("\n=== DELETE NETWORK ===") + response = client.cloud.networks.delete(network_id=network_id) + if response.tasks: + task_id = response.tasks[0] + client.cloud.tasks.poll(task_id=task_id) + print(f"Deleted network: ID={network_id}") + print("========================") + + +if __name__ == "__main__": + main() diff --git a/examples/cloud/networks_async.py b/examples/cloud/networks_async.py new file mode 100644 index 00000000..1eea1a5f --- /dev/null +++ b/examples/cloud/networks_async.py @@ -0,0 +1,127 @@ +import asyncio + +from gcore import AsyncGcore + + +async def main() -> None: + # TODO set cloud project ID before running + # cloud_project_id = os.environ["GCORE_CLOUD_PROJECT_ID"] + # TODO set cloud region ID before running + # cloud_region_id = os.environ["GCORE_CLOUD_REGION_ID"] + + gcore = AsyncGcore() + + network_id = await create_network(client=gcore) + await list_networks(client=gcore) + await get_network(client=gcore, network_id=network_id) + await update_network(client=gcore, network_id=network_id) + + # Subnets + subnet_id = await create_subnet(client=gcore, network_id=network_id) + await list_subnets(client=gcore, network_id=network_id) + await get_subnet(client=gcore, subnet_id=subnet_id) + await update_subnet(client=gcore, subnet_id=subnet_id) + await delete_subnet(client=gcore, subnet_id=subnet_id) + + await delete_network(client=gcore, network_id=network_id) + + +async def create_network(*, client: AsyncGcore) -> str: + print("\n=== CREATE NETWORK ===") + response = await client.cloud.networks.create(name="gcore-go-example", create_router=True, type="vxlan") + task_id = response.tasks[0] + task = await client.cloud.tasks.poll(task_id=task_id) + if task.created_resources is None or task.created_resources.networks is None: + raise RuntimeError("Task completed but created_resources or networks is missing") + network_id: str = task.created_resources.networks[0] + print(f"Created network: ID={network_id}") + print("========================") + return network_id + + +async def list_networks(*, client: AsyncGcore) -> None: + print("\n=== LIST NETWORKS ===") + networks = await client.cloud.networks.list() + count = 0 + async for network in networks: + count += 1 + print(f"{count}. Network: ID={network.id}, name={network.name}, type={network.type}") + print("========================") + + +async def get_network(*, client: AsyncGcore, network_id: str) -> None: + print("\n=== GET NETWORK ===") + network = await client.cloud.networks.get(network_id=network_id) + print(f"Network: ID={network.id}, name={network.name}, type={network.type}") + print("========================") + + +async def update_network(*, client: AsyncGcore, network_id: str) -> None: + print("\n=== UPDATE NETWORK ===") + network = await client.cloud.networks.update(network_id=network_id, name="gcore-go-example-updated") + print(f"Updated network: ID={network.id}, name={network.name}") + print("========================") + + +async def create_subnet(*, client: AsyncGcore, network_id: str) -> str: + print("\n=== CREATE SUBNET ===") + response = await client.cloud.networks.subnets.create( + network_id=network_id, + cidr="192.168.1.0/24", + name="gcore-go-example", + enable_dhcp=True, + ip_version=4, + ) + task_id = response.tasks[0] + task = await client.cloud.tasks.poll(task_id=task_id) + if task.created_resources is None or task.created_resources.subnets is None: + raise RuntimeError("Task completed but created_resources or subnets is missing") + subnet_id: str = task.created_resources.subnets[0] + print(f"Created subnet: ID={subnet_id}") + print("========================") + return subnet_id + + +async def list_subnets(*, client: AsyncGcore, network_id: str) -> None: + print("\n=== LIST SUBNETS ===") + subnets = await client.cloud.networks.subnets.list(network_id=network_id) + count = 0 + async for subnet in subnets: + count += 1 + print(f"{count}. Subnet: ID={subnet.id}, CIDR={subnet.cidr}, name={subnet.name}") + print("========================") + + +async def get_subnet(*, client: AsyncGcore, subnet_id: str) -> None: + print("\n=== GET SUBNET ===") + subnet = await client.cloud.networks.subnets.get(subnet_id=subnet_id) + print(f"Subnet: ID={subnet.id}, CIDR={subnet.cidr}, name={subnet.name}") + print("========================") + + +async def update_subnet(*, client: AsyncGcore, subnet_id: str) -> None: + print("\n=== UPDATE SUBNET ===") + subnet = await client.cloud.networks.subnets.update(subnet_id=subnet_id, name="gcore-go-example-updated") + print(f"Updated subnet: ID={subnet.id}, name={subnet.name}") + print("========================") + + +async def delete_subnet(*, client: AsyncGcore, subnet_id: str) -> None: + print("\n=== DELETE SUBNET ===") + await client.cloud.networks.subnets.delete(subnet_id=subnet_id) + print(f"Deleted subnet: ID={subnet_id}") + print("========================") + + +async def delete_network(*, client: AsyncGcore, network_id: str) -> None: + print("\n=== DELETE NETWORK ===") + response = await client.cloud.networks.delete(network_id=network_id) + if response.tasks: + task_id = response.tasks[0] + await client.cloud.tasks.poll(task_id=task_id) + print(f"Deleted network: ID={network_id}") + print("========================") + + +if __name__ == "__main__": + asyncio.run(main()) From 3534beaed71a9fb5fe61df6a8b9aa76b9c3913c8 Mon Sep 17 00:00:00 2001 From: Pedro Oliveira <8281907+pedrodeoliveira@users.noreply.github.com> Date: Wed, 18 Jun 2025 10:55:41 +0100 Subject: [PATCH 15/50] fix(cloud): linting on load balancer examples --- examples/cloud/load_balancers.py | 2 +- examples/cloud/load_balancers_async.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/cloud/load_balancers.py b/examples/cloud/load_balancers.py index 26de74ce..d9f94003 100644 --- a/examples/cloud/load_balancers.py +++ b/examples/cloud/load_balancers.py @@ -47,7 +47,7 @@ def list_load_balancers(*, client: Gcore) -> None: def get_load_balancer(*, client: Gcore, loadbalancer_id: str) -> None: print("\n=== GET LOAD BALANCER ===") lb = client.cloud.load_balancers.get(loadbalancer_id=loadbalancer_id) - flavor_name = lb.flavor.flavor_name + flavor_name = lb.flavor.flavor_name if lb.flavor else "Unknown" print(f"Load balancer: ID={lb.id}, name={lb.name}, status={lb.provisioning_status}, flavor={flavor_name}") print("========================") diff --git a/examples/cloud/load_balancers_async.py b/examples/cloud/load_balancers_async.py index d11c8320..725a8955 100644 --- a/examples/cloud/load_balancers_async.py +++ b/examples/cloud/load_balancers_async.py @@ -51,7 +51,7 @@ async def list_load_balancers(*, client: AsyncGcore) -> None: async def get_load_balancer(*, client: AsyncGcore, loadbalancer_id: str) -> None: print("\n=== GET LOAD BALANCER ===") lb = await client.cloud.load_balancers.get(loadbalancer_id=loadbalancer_id) - flavor_name = lb.flavor.flavor_name + flavor_name = lb.flavor.flavor_name if lb.flavor else "Unknown" print(f"Load balancer: ID={lb.id}, name={lb.name}, status={lb.provisioning_status}, flavor={flavor_name}") print("========================") From 1f4c28f252df42a703a60ed2901391167296ecc7 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 19 Jun 2025 03:01:01 +0000 Subject: [PATCH 16/50] docs(client): fix httpx.Timeout documentation reference --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 44ec1713..ed806261 100644 --- a/README.md +++ b/README.md @@ -252,7 +252,7 @@ client.with_options(max_retries=5).cloud.projects.create( ### Timeouts By default requests time out after 1 minute. You can configure this with a `timeout` option, -which accepts a float or an [`httpx.Timeout`](https://www.python-httpx.org/advanced/#fine-tuning-the-configuration) object: +which accepts a float or an [`httpx.Timeout`](https://www.python-httpx.org/advanced/timeouts/#fine-tuning-the-configuration) object: ```python from gcore import Gcore From c86820e1ca68185902d8e5e3cb911fca6d4dc10b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 19 Jun 2025 14:10:04 +0000 Subject: [PATCH 17/50] feat(api): aggregated API specs update --- .stats.yml | 4 +- api.md | 13 +- .../resources/cloud/baremetal/flavors.py | 24 ++-- src/gcore/resources/cloud/baremetal/images.py | 22 ++-- .../resources/cloud/baremetal/servers.py | 36 +++--- .../resources/cloud/billing_reservations.py | 4 +- src/gcore/resources/cloud/floating_ips.py | 12 +- .../gpu_baremetal_clusters.py | 36 ++++-- .../cloud/gpu_baremetal_clusters/images.py | 8 +- .../gpu_baremetal_clusters/interfaces.py | 4 +- .../cloud/gpu_baremetal_clusters/servers.py | 12 +- .../cloud/inference/registry_credentials.py | 4 +- .../resources/cloud/inference/secrets.py | 16 +-- .../resources/cloud/instances/flavors.py | 32 ++--- src/gcore/resources/cloud/instances/images.py | 66 ++++++---- .../resources/cloud/instances/instances.py | 68 ++++++---- .../resources/cloud/instances/interfaces.py | 4 +- .../cloud/load_balancers/listeners.py | 8 +- .../cloud/load_balancers/load_balancers.py | 26 ++-- .../resources/cloud/load_balancers/metrics.py | 4 +- .../load_balancers/pools/health_monitors.py | 4 +- .../resources/cloud/networks/networks.py | 18 +-- src/gcore/resources/cloud/networks/routers.py | 36 +++--- src/gcore/resources/cloud/networks/subnets.py | 18 +-- src/gcore/resources/cloud/projects.py | 62 +++++---- src/gcore/resources/cloud/quotas/quotas.py | 12 +- src/gcore/resources/cloud/quotas/requests.py | 16 +-- .../resources/cloud/registries/artifacts.py | 8 +- .../resources/cloud/registries/registries.py | 30 ++--- .../cloud/registries/repositories.py | 8 +- src/gcore/resources/cloud/registries/tags.py | 4 +- src/gcore/resources/cloud/registries/users.py | 43 +++---- .../reserved_fixed_ips/reserved_fixed_ips.py | 40 +++--- .../resources/cloud/reserved_fixed_ips/vip.py | 20 +-- .../resources/cloud/security_groups/rules.py | 12 +- .../cloud/security_groups/security_groups.py | 64 +++++----- src/gcore/resources/cloud/tasks.py | 8 +- .../resources/cloud/users/role_assignments.py | 26 ++-- src/gcore/resources/cloud/volumes.py | 120 ++++++++++++------ .../cloud/baremetal/image_list_params.py | 6 +- .../cloud/baremetal/server_list_params.py | 6 +- .../types/cloud/floating_ip_list_params.py | 6 +- src/gcore/types/cloud/instance_list_params.py | 6 +- .../cloud/instances/image_list_params.py | 6 +- .../types/cloud/load_balancer_list_params.py | 6 +- .../cloud/load_balancer_update_params.py | 5 +- src/gcore/types/cloud/network_list_params.py | 6 +- .../types/cloud/network_update_params.py | 5 +- .../cloud/networks/router_list_params.py | 4 +- .../cloud/networks/subnet_list_params.py | 6 +- .../cloud/networks/subnet_update_params.py | 5 +- src/gcore/types/cloud/registries/__init__.py | 1 + .../user_refresh_secret_response.py | 31 +++++ .../cloud/reserved_fixed_ip_list_params.py | 4 +- .../types/cloud/security_group_list_params.py | 10 +- .../cloud/security_group_update_params.py | 5 +- src/gcore/types/cloud/volume_list_params.py | 6 +- .../cloud/registries/test_users.py | 29 +++-- .../cloud/test_security_groups.py | 12 +- 59 files changed, 611 insertions(+), 506 deletions(-) create mode 100644 src/gcore/types/cloud/registries/user_refresh_secret_response.py diff --git a/.stats.yml b/.stats.yml index 0cc0367f..11475e47 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 302 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-c69353c2638a896685622a7bc91746002ce45c684c09f0cd59f90d193a711bfd.yml -openapi_spec_hash: 288f2041f8a716abffdbdf64ae886c63 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-6335dff48838208c2b58720ea26dda6b46ee4e8f0cbed4a3e7839e2a85c53de4.yml +openapi_spec_hash: d9954e09f207068566e8b8f3052ac498 config_hash: 851d2f0c1f925aee3b53478d4fe311ba diff --git a/api.md b/api.md index 307d30c4..238e7d83 100644 --- a/api.md +++ b/api.md @@ -418,7 +418,7 @@ Methods: - client.cloud.security_groups.update(group_id, \*, project_id, region_id, \*\*params) -> SecurityGroup - client.cloud.security_groups.list(\*, project_id, region_id, \*\*params) -> SyncOffsetPage[SecurityGroup] - client.cloud.security_groups.delete(group_id, \*, project_id, region_id) -> None -- client.cloud.security_groups.copy(group_id, \*, project_id, region_id, \*\*params) -> None +- client.cloud.security_groups.copy(group_id, \*, project_id, region_id, \*\*params) -> SecurityGroup - client.cloud.security_groups.get(group_id, \*, project_id, region_id) -> SecurityGroup - client.cloud.security_groups.revert_to_default(group_id, \*, project_id, region_id) -> SecurityGroup @@ -667,17 +667,22 @@ Methods: Types: ```python -from gcore.types.cloud.registries import RegistryUser, RegistryUserCreated, RegistryUserList +from gcore.types.cloud.registries import ( + RegistryUser, + RegistryUserCreated, + RegistryUserList, + UserRefreshSecretResponse, +) ``` Methods: -- client.cloud.registries.users.create(registry_id, \*, project_id, region_id, \*\*params) -> RegistryUser +- client.cloud.registries.users.create(registry_id, \*, project_id, region_id, \*\*params) -> RegistryUserCreated - client.cloud.registries.users.update(user_id, \*, project_id, region_id, registry_id, \*\*params) -> RegistryUser - client.cloud.registries.users.list(registry_id, \*, project_id, region_id) -> RegistryUserList - client.cloud.registries.users.delete(user_id, \*, project_id, region_id, registry_id) -> None - client.cloud.registries.users.create_multiple(registry_id, \*, project_id, region_id, \*\*params) -> RegistryUserCreated -- client.cloud.registries.users.refresh_secret(user_id, \*, project_id, region_id, registry_id) -> None +- client.cloud.registries.users.refresh_secret(user_id, \*, project_id, region_id, registry_id) -> UserRefreshSecretResponse ## FileShares diff --git a/src/gcore/resources/cloud/baremetal/flavors.py b/src/gcore/resources/cloud/baremetal/flavors.py index 83f7e0ea..5199c40c 100644 --- a/src/gcore/resources/cloud/baremetal/flavors.py +++ b/src/gcore/resources/cloud/baremetal/flavors.py @@ -59,11 +59,12 @@ def list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> BaremetalFlavorList: - """Retrieve a list of flavors. + """List all available bare metal flavors in the specified project and region. - When the `include_prices` query parameter is - specified, the list shows prices. A client in trial mode gets all price values - as 0. If you get Pricing Error contact the support + When + `include_prices` is specified, the list includes pricing information. A client + in trial mode gets all price values as 0. If you get Pricing Error contact the + support. Args: disabled: Flag for filtering disabled flavors in the region. Defaults to true @@ -129,7 +130,8 @@ def list_suitable( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> BaremetalFlavorList: """ - List suitalbe flavors for bare metal server creation + List all flavors that are suitable for creating a bare metal server with the + specified image. Args: include_prices: Set to true if flavor listing should include flavor prices @@ -210,11 +212,12 @@ async def list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> BaremetalFlavorList: - """Retrieve a list of flavors. + """List all available bare metal flavors in the specified project and region. - When the `include_prices` query parameter is - specified, the list shows prices. A client in trial mode gets all price values - as 0. If you get Pricing Error contact the support + When + `include_prices` is specified, the list includes pricing information. A client + in trial mode gets all price values as 0. If you get Pricing Error contact the + support. Args: disabled: Flag for filtering disabled flavors in the region. Defaults to true @@ -280,7 +283,8 @@ async def list_suitable( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> BaremetalFlavorList: """ - List suitalbe flavors for bare metal server creation + List all flavors that are suitable for creating a bare metal server with the + specified image. Args: include_prices: Set to true if flavor listing should include flavor prices diff --git a/src/gcore/resources/cloud/baremetal/images.py b/src/gcore/resources/cloud/baremetal/images.py index d49c23a6..147ebf85 100644 --- a/src/gcore/resources/cloud/baremetal/images.py +++ b/src/gcore/resources/cloud/baremetal/images.py @@ -61,10 +61,11 @@ def list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ImageList: - """Retrieve the available images list for bare metal servers. + """Retrieve a list of available images for bare metal servers. - Returned entities may - or may not be owned by the project + The list can be + filtered by visibility, tags, and other parameters. Returned entities may or may + not be owned by the project. Args: include_prices: Show price @@ -73,9 +74,7 @@ def list( tag_key: Filter by tag keys. - tag_key_value: Filter by tag key-value pairs. Must be a valid JSON string. 'curl -G - --data-urlencode '`tag_key_value`={"key": "value"}' --url - 'http://localhost:1111/v1/images/1/1'" + tag_key_value: Filter by tag key-value pairs. Must be a valid JSON string. visibility: Image visibility. Globally visible images are public @@ -150,10 +149,11 @@ async def list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ImageList: - """Retrieve the available images list for bare metal servers. + """Retrieve a list of available images for bare metal servers. - Returned entities may - or may not be owned by the project + The list can be + filtered by visibility, tags, and other parameters. Returned entities may or may + not be owned by the project. Args: include_prices: Show price @@ -162,9 +162,7 @@ async def list( tag_key: Filter by tag keys. - tag_key_value: Filter by tag key-value pairs. Must be a valid JSON string. 'curl -G - --data-urlencode '`tag_key_value`={"key": "value"}' --url - 'http://localhost:1111/v1/images/1/1'" + tag_key_value: Filter by tag key-value pairs. Must be a valid JSON string. visibility: Image visibility. Globally visible images are public diff --git a/src/gcore/resources/cloud/baremetal/servers.py b/src/gcore/resources/cloud/baremetal/servers.py index d3f04458..eeee848b 100644 --- a/src/gcore/resources/cloud/baremetal/servers.py +++ b/src/gcore/resources/cloud/baremetal/servers.py @@ -72,8 +72,10 @@ def create( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: - """ - For Linux, + """Create a new bare metal server with the specified configuration. + + How to get + access: For Linux, - Use the `user_data` field to provide a [cloud-init script](https://cloudinit.readthedocs.io/en/latest/reference/examples.html) @@ -216,8 +218,10 @@ def list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SyncOffsetPage[BaremetalServer]: - """ - List bare metal servers + """List all bare metal servers in the specified project and region. + + Results can be + filtered by various parameters like name, status, and IP address. Args: project_id: Project ID @@ -262,9 +266,7 @@ def list( status: Filters instances by a server status, as a string. - tag_key_value: Optional. Filter by tag key-value pairs. curl -G --data-urlencode - "`tag_key_value`={"key": "value"}" --url - "https://example.com/cloud/v1/resource/1/1" + tag_key_value: Optional. Filter by tag key-value pairs. tag_value: Optional. Filter by tag values. ?`tag_value`=value1&`tag_value`=value2 @@ -345,7 +347,7 @@ def rebuild( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: """ - Rebuild bare metal server + Rebuild a bare metal server with a new image while preserving its configuration. Args: image_id: Image ID @@ -546,8 +548,10 @@ async def create( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: - """ - For Linux, + """Create a new bare metal server with the specified configuration. + + How to get + access: For Linux, - Use the `user_data` field to provide a [cloud-init script](https://cloudinit.readthedocs.io/en/latest/reference/examples.html) @@ -690,8 +694,10 @@ def list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AsyncPaginator[BaremetalServer, AsyncOffsetPage[BaremetalServer]]: - """ - List bare metal servers + """List all bare metal servers in the specified project and region. + + Results can be + filtered by various parameters like name, status, and IP address. Args: project_id: Project ID @@ -736,9 +742,7 @@ def list( status: Filters instances by a server status, as a string. - tag_key_value: Optional. Filter by tag key-value pairs. curl -G --data-urlencode - "`tag_key_value`={"key": "value"}" --url - "https://example.com/cloud/v1/resource/1/1" + tag_key_value: Optional. Filter by tag key-value pairs. tag_value: Optional. Filter by tag values. ?`tag_value`=value1&`tag_value`=value2 @@ -819,7 +823,7 @@ async def rebuild( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: """ - Rebuild bare metal server + Rebuild a bare metal server with a new image while preserving its configuration. Args: image_id: Image ID diff --git a/src/gcore/resources/cloud/billing_reservations.py b/src/gcore/resources/cloud/billing_reservations.py index 31855aea..0d9df747 100644 --- a/src/gcore/resources/cloud/billing_reservations.py +++ b/src/gcore/resources/cloud/billing_reservations.py @@ -160,7 +160,7 @@ def get( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> BillingReservation: """ - Get specific reservation + Get reservation Args: reservation_id: ID of the reservation @@ -316,7 +316,7 @@ async def get( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> BillingReservation: """ - Get specific reservation + Get reservation Args: reservation_id: ID of the reservation diff --git a/src/gcore/resources/cloud/floating_ips.py b/src/gcore/resources/cloud/floating_ips.py index 6fab4e6c..3bbb8930 100644 --- a/src/gcore/resources/cloud/floating_ips.py +++ b/src/gcore/resources/cloud/floating_ips.py @@ -140,9 +140,7 @@ def list( tag_key: Optional. Filter by tag keys. ?`tag_key`=key1&`tag_key`=key2 - tag_key_value: Optional. Filter by tag key-value pairs. curl -G --data-urlencode - "`tag_key_value`={"key": "value"}" --url - "https://example.com/cloud/v1/resource/1/1" + tag_key_value: Optional. Filter by tag key-value pairs. extra_headers: Send extra headers @@ -321,7 +319,7 @@ def unassign( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> FloatingIP: """ - Unassign floating IP from the instance + Unassign floating IP Args: extra_headers: Send extra headers @@ -461,9 +459,7 @@ def list( tag_key: Optional. Filter by tag keys. ?`tag_key`=key1&`tag_key`=key2 - tag_key_value: Optional. Filter by tag key-value pairs. curl -G --data-urlencode - "`tag_key_value`={"key": "value"}" --url - "https://example.com/cloud/v1/resource/1/1" + tag_key_value: Optional. Filter by tag key-value pairs. extra_headers: Send extra headers @@ -642,7 +638,7 @@ async def unassign( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> FloatingIP: """ - Unassign floating IP from the instance + Unassign floating IP Args: extra_headers: Send extra headers diff --git a/src/gcore/resources/cloud/gpu_baremetal_clusters/gpu_baremetal_clusters.py b/src/gcore/resources/cloud/gpu_baremetal_clusters/gpu_baremetal_clusters.py index 25968fbe..0da7e432 100644 --- a/src/gcore/resources/cloud/gpu_baremetal_clusters/gpu_baremetal_clusters.py +++ b/src/gcore/resources/cloud/gpu_baremetal_clusters/gpu_baremetal_clusters.py @@ -122,8 +122,10 @@ def create( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: - """ - Create bare metal GPU cluster + """Create a new GPU cluster with specified configuration. + + The cluster can be + created with one or more nodes. Args: flavor: Flavor name @@ -439,8 +441,10 @@ def rebuild( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: - """ - All cluster nodes must be specified to update the cluster image. + """Rebuild one or more nodes in a GPU cluster. + + All cluster nodes must be specified + to update the cluster image. Args: nodes: List of nodes uuids to be rebuild @@ -495,8 +499,10 @@ def resize( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: - """ - Resize bare metal GPU cluster + """Change the number of nodes in a GPU cluster. + + The cluster can be scaled up or + down. Args: instances_count: Resized (total) number of instances @@ -744,8 +750,10 @@ async def create( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: - """ - Create bare metal GPU cluster + """Create a new GPU cluster with specified configuration. + + The cluster can be + created with one or more nodes. Args: flavor: Flavor name @@ -1061,8 +1069,10 @@ async def rebuild( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: - """ - All cluster nodes must be specified to update the cluster image. + """Rebuild one or more nodes in a GPU cluster. + + All cluster nodes must be specified + to update the cluster image. Args: nodes: List of nodes uuids to be rebuild @@ -1117,8 +1127,10 @@ async def resize( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: - """ - Resize bare metal GPU cluster + """Change the number of nodes in a GPU cluster. + + The cluster can be scaled up or + down. Args: instances_count: Resized (total) number of instances diff --git a/src/gcore/resources/cloud/gpu_baremetal_clusters/images.py b/src/gcore/resources/cloud/gpu_baremetal_clusters/images.py index cda7ff9d..4d1aa992 100644 --- a/src/gcore/resources/cloud/gpu_baremetal_clusters/images.py +++ b/src/gcore/resources/cloud/gpu_baremetal_clusters/images.py @@ -100,7 +100,7 @@ def delete( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: """ - Delete bare metal GPU image by ID + Delete bare metal GPU image Args: project_id: Project ID @@ -181,7 +181,7 @@ def get( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> GPUImage: """ - Get bare metal GPU image by ID + Get bare metal GPU image Args: project_id: Project ID @@ -443,7 +443,7 @@ async def delete( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: """ - Delete bare metal GPU image by ID + Delete bare metal GPU image Args: project_id: Project ID @@ -524,7 +524,7 @@ async def get( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> GPUImage: """ - Get bare metal GPU image by ID + Get bare metal GPU image Args: project_id: Project ID diff --git a/src/gcore/resources/cloud/gpu_baremetal_clusters/interfaces.py b/src/gcore/resources/cloud/gpu_baremetal_clusters/interfaces.py index dc9e5249..863ffde8 100644 --- a/src/gcore/resources/cloud/gpu_baremetal_clusters/interfaces.py +++ b/src/gcore/resources/cloud/gpu_baremetal_clusters/interfaces.py @@ -53,7 +53,7 @@ def list( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> NetworkInterfaceList: """ - Returns the network interfaces attached to the servers in the cluster. + Retrieve a list of network interfaces attached to the GPU cluster servers. Args: extra_headers: Send extra headers @@ -113,7 +113,7 @@ async def list( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> NetworkInterfaceList: """ - Returns the network interfaces attached to the servers in the cluster. + Retrieve a list of network interfaces attached to the GPU cluster servers. Args: extra_headers: Send extra headers diff --git a/src/gcore/resources/cloud/gpu_baremetal_clusters/servers.py b/src/gcore/resources/cloud/gpu_baremetal_clusters/servers.py index 8251387f..cf5c2ef9 100644 --- a/src/gcore/resources/cloud/gpu_baremetal_clusters/servers.py +++ b/src/gcore/resources/cloud/gpu_baremetal_clusters/servers.py @@ -65,8 +65,10 @@ def delete( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: - """ - Delete bare metal GPU server from cluster + """Delete a specific node from a GPU cluster. + + The node must be in a state that + allows deletion. Args: delete_floatings: Set False if you do not want to delete assigned floating IPs. By default, it's @@ -590,8 +592,10 @@ async def delete( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: - """ - Delete bare metal GPU server from cluster + """Delete a specific node from a GPU cluster. + + The node must be in a state that + allows deletion. Args: delete_floatings: Set False if you do not want to delete assigned floating IPs. By default, it's diff --git a/src/gcore/resources/cloud/inference/registry_credentials.py b/src/gcore/resources/cloud/inference/registry_credentials.py index 70c0cbf6..e77bb5ea 100644 --- a/src/gcore/resources/cloud/inference/registry_credentials.py +++ b/src/gcore/resources/cloud/inference/registry_credentials.py @@ -253,7 +253,7 @@ def replace( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> None: """ - Update inference registry credential + Replace inference registry credential Args: project_id: Project ID @@ -522,7 +522,7 @@ async def replace( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> None: """ - Update inference registry credential + Replace inference registry credential Args: project_id: Project ID diff --git a/src/gcore/resources/cloud/inference/secrets.py b/src/gcore/resources/cloud/inference/secrets.py index c21ef45c..80ceff3b 100644 --- a/src/gcore/resources/cloud/inference/secrets.py +++ b/src/gcore/resources/cloud/inference/secrets.py @@ -58,7 +58,7 @@ def create( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> InferenceSecret: """ - Create Inference Secret + Create inference secret Args: project_id: Project ID @@ -108,7 +108,7 @@ def list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SyncOffsetPage[InferenceSecret]: - """List Secrets for Inference + """List inference secrets Args: project_id: Project ID @@ -203,7 +203,7 @@ def get( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> InferenceSecret: """ - Get Inference Secret + Get inference secret Args: project_id: Project ID @@ -245,7 +245,7 @@ def replace( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> InferenceSecret: """ - Update Inference Secret + Replace inference secret Args: project_id: Project ID @@ -319,7 +319,7 @@ async def create( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> InferenceSecret: """ - Create Inference Secret + Create inference secret Args: project_id: Project ID @@ -369,7 +369,7 @@ def list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AsyncPaginator[InferenceSecret, AsyncOffsetPage[InferenceSecret]]: - """List Secrets for Inference + """List inference secrets Args: project_id: Project ID @@ -464,7 +464,7 @@ async def get( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> InferenceSecret: """ - Get Inference Secret + Get inference secret Args: project_id: Project ID @@ -506,7 +506,7 @@ async def replace( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> InferenceSecret: """ - Update Inference Secret + Replace inference secret Args: project_id: Project ID diff --git a/src/gcore/resources/cloud/instances/flavors.py b/src/gcore/resources/cloud/instances/flavors.py index 4dd3898e..f27760f8 100644 --- a/src/gcore/resources/cloud/instances/flavors.py +++ b/src/gcore/resources/cloud/instances/flavors.py @@ -59,11 +59,11 @@ def list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> InstanceFlavorList: - """Retrieve a list of flavors. + """Retrieve a list of available instance flavors in the project and region. - When the `include_prices` query parameter is - specified, the list shows prices. A client in trial mode gets all price values - as 0. If you get Pricing Error contact the support + When + `include_prices` is specified, the list includes pricing information. Trial mode + clients see all prices as 0. Contact support for pricing errors. Args: disabled: Flag for filtering disabled flavors in the region. Defaults to true @@ -168,12 +168,12 @@ def list_suitable( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> InstanceFlavorList: - """List suitable flavors for instance creation + """ + List all flavors that are suitable for instance creation based on volume + requirements. Args: - volumes: Volumes details. - - Non-important info such as names may be omitted. + volumes: Volumes details. Non-important info such as names may be omitted. include_prices: Set to true if flavor listing should include flavor prices @@ -241,11 +241,11 @@ async def list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> InstanceFlavorList: - """Retrieve a list of flavors. + """Retrieve a list of available instance flavors in the project and region. - When the `include_prices` query parameter is - specified, the list shows prices. A client in trial mode gets all price values - as 0. If you get Pricing Error contact the support + When + `include_prices` is specified, the list includes pricing information. Trial mode + clients see all prices as 0. Contact support for pricing errors. Args: disabled: Flag for filtering disabled flavors in the region. Defaults to true @@ -350,12 +350,12 @@ async def list_suitable( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> InstanceFlavorList: - """List suitable flavors for instance creation + """ + List all flavors that are suitable for instance creation based on volume + requirements. Args: - volumes: Volumes details. - - Non-important info such as names may be omitted. + volumes: Volumes details. Non-important info such as names may be omitted. include_prices: Set to true if flavor listing should include flavor prices diff --git a/src/gcore/resources/cloud/instances/images.py b/src/gcore/resources/cloud/instances/images.py index 11b3db09..91f82de4 100644 --- a/src/gcore/resources/cloud/instances/images.py +++ b/src/gcore/resources/cloud/instances/images.py @@ -74,7 +74,7 @@ def update( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Image: """ - Update image fields + Update image properties and tags. Args: hw_firmware_type: Specifies the type of firmware with which to boot the guest. @@ -146,10 +146,11 @@ def list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ImageList: - """Retrieve an available images list. + """Retrieve a list of available images in the project and region. - Returned entities owned by the project and - public OR shared with the client + The list can be + filtered by visibility, tags, and other parameters. Returned entities are owned + by the project or are public/shared with the client. Args: include_prices: Show price @@ -158,9 +159,7 @@ def list( tag_key: Filter by tag keys. - tag_key_value: Filter by tag key-value pairs. Must be a valid JSON string. 'curl -G - --data-urlencode '`tag_key_value`={"key": "value"}' --url - 'http://localhost:1111/v1/images/1/1'" + tag_key_value: Filter by tag key-value pairs. Must be a valid JSON string. visibility: Image visibility. Globally visible images are public @@ -210,8 +209,10 @@ def delete( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: - """ - Delete the image + """Delete a specific image. + + The image cannot be deleted if it is used by protected + snapshots. Args: extra_headers: Send extra headers @@ -292,8 +293,10 @@ def create_from_volume( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: - """ - Create image from volume + """Create a new image from a bootable volume. + + The volume must be bootable to create + an image from it. Args: name: Image name @@ -430,7 +433,7 @@ def get( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Image: """ - Get image + Retrieve detailed information about a specific image. Args: include_prices: Show price @@ -485,8 +488,10 @@ def upload( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: - """ - Upload image + """Upload an image from a URL. + + The image can be configured with various properties + like OS type, architecture, and tags. Args: name: Image name @@ -661,7 +666,7 @@ async def update( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Image: """ - Update image fields + Update image properties and tags. Args: hw_firmware_type: Specifies the type of firmware with which to boot the guest. @@ -733,10 +738,11 @@ async def list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ImageList: - """Retrieve an available images list. + """Retrieve a list of available images in the project and region. - Returned entities owned by the project and - public OR shared with the client + The list can be + filtered by visibility, tags, and other parameters. Returned entities are owned + by the project or are public/shared with the client. Args: include_prices: Show price @@ -745,9 +751,7 @@ async def list( tag_key: Filter by tag keys. - tag_key_value: Filter by tag key-value pairs. Must be a valid JSON string. 'curl -G - --data-urlencode '`tag_key_value`={"key": "value"}' --url - 'http://localhost:1111/v1/images/1/1'" + tag_key_value: Filter by tag key-value pairs. Must be a valid JSON string. visibility: Image visibility. Globally visible images are public @@ -797,8 +801,10 @@ async def delete( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: - """ - Delete the image + """Delete a specific image. + + The image cannot be deleted if it is used by protected + snapshots. Args: extra_headers: Send extra headers @@ -879,8 +885,10 @@ async def create_from_volume( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: - """ - Create image from volume + """Create a new image from a bootable volume. + + The volume must be bootable to create + an image from it. Args: name: Image name @@ -1017,7 +1025,7 @@ async def get( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Image: """ - Get image + Retrieve detailed information about a specific image. Args: include_prices: Show price @@ -1072,8 +1080,10 @@ async def upload( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: - """ - Upload image + """Upload an image from a URL. + + The image can be configured with various properties + like OS type, architecture, and tags. Args: name: Image name diff --git a/src/gcore/resources/cloud/instances/instances.py b/src/gcore/resources/cloud/instances/instances.py index 57bacd94..e505ab11 100644 --- a/src/gcore/resources/cloud/instances/instances.py +++ b/src/gcore/resources/cloud/instances/instances.py @@ -134,8 +134,9 @@ def create( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: - """ - For Linux, + """Create an instance with specified configuration. + + How to get access: For Linux, - Use the `user_data` field to provide a [cloud-init script](https://cloudinit.readthedocs.io/en/latest/reference/examples.html) @@ -428,8 +429,10 @@ def list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SyncOffsetPage[Instance]: - """ - List instances + """List all instances in the specified project and region. + + Results can be filtered + by various parameters like name, status, and IP address. Args: project_id: Project ID @@ -484,9 +487,7 @@ def list( status: Filters instances by status. - tag_key_value: Optional. Filter by tag key-value pairs. curl -G --data-urlencode - "`tag_key_value`={"key": "value"}" --url - "https://example.com/cloud/v1/resource/1/1" + tag_key_value: Optional. Filter by tag key-value pairs. tag_value: Optional. Filter by tag values. ?`tag_value`=value1&`tag_value`=value2 @@ -786,8 +787,10 @@ def add_to_placement_group( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: - """ - Put instance into the server group + """Add an instance to a server group. + + The instance must not already be in a server + group. Bare metal servers do not support server groups. Args: servergroup_id: Anti-affinity or affinity or soft-anti-affinity server group ID. @@ -1007,8 +1010,11 @@ def get( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Instance: - """ - **Cookie Parameters**: + """Retrieve detailed information about a specific instance. + + The response content + language for `ddos_profile` can be controlled via the 'language' cookie + parameter. **Cookie Parameters**: - `language` (str, optional): Language for the response content. Affects the `ddos_profile` field. Supported values: @@ -1106,8 +1112,10 @@ def remove_from_placement_group( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: - """ - Remove instance from the server group + """Remove an instance from its current server group. + + The instance must be in a + server group to be removed. Bare metal servers do not support server groups. Args: extra_headers: Send extra headers @@ -1376,8 +1384,9 @@ async def create( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: - """ - For Linux, + """Create an instance with specified configuration. + + How to get access: For Linux, - Use the `user_data` field to provide a [cloud-init script](https://cloudinit.readthedocs.io/en/latest/reference/examples.html) @@ -1670,8 +1679,10 @@ def list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AsyncPaginator[Instance, AsyncOffsetPage[Instance]]: - """ - List instances + """List all instances in the specified project and region. + + Results can be filtered + by various parameters like name, status, and IP address. Args: project_id: Project ID @@ -1726,9 +1737,7 @@ def list( status: Filters instances by status. - tag_key_value: Optional. Filter by tag key-value pairs. curl -G --data-urlencode - "`tag_key_value`={"key": "value"}" --url - "https://example.com/cloud/v1/resource/1/1" + tag_key_value: Optional. Filter by tag key-value pairs. tag_value: Optional. Filter by tag values. ?`tag_value`=value1&`tag_value`=value2 @@ -2028,8 +2037,10 @@ async def add_to_placement_group( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: - """ - Put instance into the server group + """Add an instance to a server group. + + The instance must not already be in a server + group. Bare metal servers do not support server groups. Args: servergroup_id: Anti-affinity or affinity or soft-anti-affinity server group ID. @@ -2249,8 +2260,11 @@ async def get( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Instance: - """ - **Cookie Parameters**: + """Retrieve detailed information about a specific instance. + + The response content + language for `ddos_profile` can be controlled via the 'language' cookie + parameter. **Cookie Parameters**: - `language` (str, optional): Language for the response content. Affects the `ddos_profile` field. Supported values: @@ -2348,8 +2362,10 @@ async def remove_from_placement_group( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: - """ - Remove instance from the server group + """Remove an instance from its current server group. + + The instance must be in a + server group to be removed. Bare metal servers do not support server groups. Args: extra_headers: Send extra headers diff --git a/src/gcore/resources/cloud/instances/interfaces.py b/src/gcore/resources/cloud/instances/interfaces.py index 733ded71..9cc4b04f 100644 --- a/src/gcore/resources/cloud/instances/interfaces.py +++ b/src/gcore/resources/cloud/instances/interfaces.py @@ -59,7 +59,7 @@ def list( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> NetworkInterfaceList: """ - List network interfaces attached to the instance + List all network interfaces attached to the specified instance. Args: extra_headers: Send extra headers @@ -415,7 +415,7 @@ async def list( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> NetworkInterfaceList: """ - List network interfaces attached to the instance + List all network interfaces attached to the specified instance. Args: extra_headers: Send extra headers diff --git a/src/gcore/resources/cloud/load_balancers/listeners.py b/src/gcore/resources/cloud/load_balancers/listeners.py index bdf3c75a..ec138c0c 100644 --- a/src/gcore/resources/cloud/load_balancers/listeners.py +++ b/src/gcore/resources/cloud/load_balancers/listeners.py @@ -175,7 +175,7 @@ def update( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: """ - Update listener + Update load balancer listener Args: project_id: Project ID @@ -356,7 +356,7 @@ def get( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> LoadBalancerListenerDetail: """ - Get listener + Get load balancer listener Args: project_id: Project ID @@ -698,7 +698,7 @@ async def update( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: """ - Update listener + Update load balancer listener Args: project_id: Project ID @@ -879,7 +879,7 @@ async def get( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> LoadBalancerListenerDetail: """ - Get listener + Get load balancer listener Args: project_id: Project ID diff --git a/src/gcore/resources/cloud/load_balancers/load_balancers.py b/src/gcore/resources/cloud/load_balancers/load_balancers.py index 80d430fa..b54df5d7 100644 --- a/src/gcore/resources/cloud/load_balancers/load_balancers.py +++ b/src/gcore/resources/cloud/load_balancers/load_balancers.py @@ -264,12 +264,15 @@ def update( tags: Update key-value tags using JSON Merge Patch semantics (RFC 7386). Provide key-value pairs to add or update tags. Set tag values to `null` to remove tags. - Unspecified tags remain unchanged. **Examples:** + Unspecified tags remain unchanged. Read-only tags are always preserved and + cannot be modified. **Examples:** - **Add/update tags:** `{'tags': {'environment': 'production', 'team': 'backend'}}` adds new tags or updates existing ones. - **Delete tags:** `{'tags': {'`old_tag`': null}}` removes specific tags. + - **Remove all tags:** `{'tags': null}` removes all user-managed tags (read-only + tags are preserved). - **Partial update:** `{'tags': {'environment': 'staging'}}` only updates specified tags. - **Mixed operations:** @@ -355,9 +358,7 @@ def list( tag_key: Filter by tag keys. - tag_key_value: Filter by tag key-value pairs. Must be a valid JSON string. curl -G - --data-urlencode "`tag_key_value`={"key": "value"}" --url - "http://localhost:1111/v1/loadbalancers/1/1" + tag_key_value: Filter by tag key-value pairs. Must be a valid JSON string. with_ddos: Show Advanced DDoS protection profile, if exists @@ -454,7 +455,7 @@ def failover( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: """ - Failover loadbalancer + Failover load balancer Args: force: Validate current load balancer status before failover or not. @@ -552,7 +553,7 @@ def resize( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: """ - Resize loadbalancer + Resize load balancer Args: flavor: Name of the desired flavor to resize to. @@ -946,12 +947,15 @@ async def update( tags: Update key-value tags using JSON Merge Patch semantics (RFC 7386). Provide key-value pairs to add or update tags. Set tag values to `null` to remove tags. - Unspecified tags remain unchanged. **Examples:** + Unspecified tags remain unchanged. Read-only tags are always preserved and + cannot be modified. **Examples:** - **Add/update tags:** `{'tags': {'environment': 'production', 'team': 'backend'}}` adds new tags or updates existing ones. - **Delete tags:** `{'tags': {'`old_tag`': null}}` removes specific tags. + - **Remove all tags:** `{'tags': null}` removes all user-managed tags (read-only + tags are preserved). - **Partial update:** `{'tags': {'environment': 'staging'}}` only updates specified tags. - **Mixed operations:** @@ -1037,9 +1041,7 @@ def list( tag_key: Filter by tag keys. - tag_key_value: Filter by tag key-value pairs. Must be a valid JSON string. curl -G - --data-urlencode "`tag_key_value`={"key": "value"}" --url - "http://localhost:1111/v1/loadbalancers/1/1" + tag_key_value: Filter by tag key-value pairs. Must be a valid JSON string. with_ddos: Show Advanced DDoS protection profile, if exists @@ -1136,7 +1138,7 @@ async def failover( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: """ - Failover loadbalancer + Failover load balancer Args: force: Validate current load balancer status before failover or not. @@ -1236,7 +1238,7 @@ async def resize( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: """ - Resize loadbalancer + Resize load balancer Args: flavor: Name of the desired flavor to resize to. diff --git a/src/gcore/resources/cloud/load_balancers/metrics.py b/src/gcore/resources/cloud/load_balancers/metrics.py index 7cfa283c..59df8038 100644 --- a/src/gcore/resources/cloud/load_balancers/metrics.py +++ b/src/gcore/resources/cloud/load_balancers/metrics.py @@ -59,7 +59,7 @@ def list( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> LoadBalancerMetricsList: """ - Get loadbalancer metrics, including cpu, memory and network + Get load balancer metrics, including cpu, memory and network Args: time_interval: Time interval @@ -132,7 +132,7 @@ async def list( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> LoadBalancerMetricsList: """ - Get loadbalancer metrics, including cpu, memory and network + Get load balancer metrics, including cpu, memory and network Args: time_interval: Time interval diff --git a/src/gcore/resources/cloud/load_balancers/pools/health_monitors.py b/src/gcore/resources/cloud/load_balancers/pools/health_monitors.py index 0a63989b..3712deec 100644 --- a/src/gcore/resources/cloud/load_balancers/pools/health_monitors.py +++ b/src/gcore/resources/cloud/load_balancers/pools/health_monitors.py @@ -68,7 +68,7 @@ def create( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: """ - Create Load Balancer Pool Health Monitor + Create load balancer pool health monitor Args: project_id: Project ID @@ -219,7 +219,7 @@ async def create( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: """ - Create Load Balancer Pool Health Monitor + Create load balancer pool health monitor Args: project_id: Project ID diff --git a/src/gcore/resources/cloud/networks/networks.py b/src/gcore/resources/cloud/networks/networks.py index 9647db97..13f2084d 100644 --- a/src/gcore/resources/cloud/networks/networks.py +++ b/src/gcore/resources/cloud/networks/networks.py @@ -168,12 +168,15 @@ def update( tags: Update key-value tags using JSON Merge Patch semantics (RFC 7386). Provide key-value pairs to add or update tags. Set tag values to `null` to remove tags. - Unspecified tags remain unchanged. **Examples:** + Unspecified tags remain unchanged. Read-only tags are always preserved and + cannot be modified. **Examples:** - **Add/update tags:** `{'tags': {'environment': 'production', 'team': 'backend'}}` adds new tags or updates existing ones. - **Delete tags:** `{'tags': {'`old_tag`': null}}` removes specific tags. + - **Remove all tags:** `{'tags': null}` removes all user-managed tags (read-only + tags are preserved). - **Partial update:** `{'tags': {'environment': 'staging'}}` only updates specified tags. - **Mixed operations:** @@ -250,9 +253,7 @@ def list( tag_key: Optional. Filter by tag keys. ?`tag_key`=key1&`tag_key`=key2 - tag_key_value: Optional. Filter by tag key-value pairs. curl -G --data-urlencode - "`tag_key_value`={"key": "value"}" --url - "https://example.com/cloud/v1/resource/1/1" + tag_key_value: Optional. Filter by tag key-value pairs. extra_headers: Send extra headers @@ -505,12 +506,15 @@ async def update( tags: Update key-value tags using JSON Merge Patch semantics (RFC 7386). Provide key-value pairs to add or update tags. Set tag values to `null` to remove tags. - Unspecified tags remain unchanged. **Examples:** + Unspecified tags remain unchanged. Read-only tags are always preserved and + cannot be modified. **Examples:** - **Add/update tags:** `{'tags': {'environment': 'production', 'team': 'backend'}}` adds new tags or updates existing ones. - **Delete tags:** `{'tags': {'`old_tag`': null}}` removes specific tags. + - **Remove all tags:** `{'tags': null}` removes all user-managed tags (read-only + tags are preserved). - **Partial update:** `{'tags': {'environment': 'staging'}}` only updates specified tags. - **Mixed operations:** @@ -587,9 +591,7 @@ def list( tag_key: Optional. Filter by tag keys. ?`tag_key`=key1&`tag_key`=key2 - tag_key_value: Optional. Filter by tag key-value pairs. curl -G --data-urlencode - "`tag_key_value`={"key": "value"}" --url - "https://example.com/cloud/v1/resource/1/1" + tag_key_value: Optional. Filter by tag key-value pairs. extra_headers: Send extra headers diff --git a/src/gcore/resources/cloud/networks/routers.py b/src/gcore/resources/cloud/networks/routers.py index 97c942f6..657982bd 100644 --- a/src/gcore/resources/cloud/networks/routers.py +++ b/src/gcore/resources/cloud/networks/routers.py @@ -68,7 +68,7 @@ def create( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: """ - Create router + Create a new router with the specified configuration. Args: name: name of router @@ -125,7 +125,7 @@ def update( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Router: """ - Update router + Update the configuration of an existing router. Args: external_gateway_info: New external gateway. @@ -179,12 +179,12 @@ def list( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SyncOffsetPage[Router]: """ - List routers + List all routers in the specified project and region. Args: - limit: Limit the number of returned limit request entities. + limit: Limit the number of returned routers - offset: Offset value is used to exclude the first set of records from the result. + offset: Offset value is used to exclude the first set of records from the result extra_headers: Send extra headers @@ -231,7 +231,7 @@ def delete( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: """ - Delete router + Delete a specific router and all its associated resources. Args: extra_headers: Send extra headers @@ -272,7 +272,7 @@ def attach_subnet( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Router: """ - Attach subnet to router + Attach a subnet to an existing router. Args: project_id: Project ID @@ -330,7 +330,7 @@ def detach_subnet( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Router: """ - Detach subnet from router + Detach a subnet from an existing router. Args: subnet_id: Target IP is identified by it's subnet @@ -372,7 +372,7 @@ def get( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Router: """ - Get specific router + Get detailed information about a specific router. Args: extra_headers: Send extra headers @@ -435,7 +435,7 @@ async def create( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: """ - Create router + Create a new router with the specified configuration. Args: name: name of router @@ -492,7 +492,7 @@ async def update( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Router: """ - Update router + Update the configuration of an existing router. Args: external_gateway_info: New external gateway. @@ -546,12 +546,12 @@ def list( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AsyncPaginator[Router, AsyncOffsetPage[Router]]: """ - List routers + List all routers in the specified project and region. Args: - limit: Limit the number of returned limit request entities. + limit: Limit the number of returned routers - offset: Offset value is used to exclude the first set of records from the result. + offset: Offset value is used to exclude the first set of records from the result extra_headers: Send extra headers @@ -598,7 +598,7 @@ async def delete( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: """ - Delete router + Delete a specific router and all its associated resources. Args: extra_headers: Send extra headers @@ -639,7 +639,7 @@ async def attach_subnet( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Router: """ - Attach subnet to router + Attach a subnet to an existing router. Args: project_id: Project ID @@ -697,7 +697,7 @@ async def detach_subnet( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Router: """ - Detach subnet from router + Detach a subnet from an existing router. Args: subnet_id: Target IP is identified by it's subnet @@ -741,7 +741,7 @@ async def get( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Router: """ - Get specific router + Get detailed information about a specific router. Args: extra_headers: Send extra headers diff --git a/src/gcore/resources/cloud/networks/subnets.py b/src/gcore/resources/cloud/networks/subnets.py index 311bd170..1f61c9b2 100644 --- a/src/gcore/resources/cloud/networks/subnets.py +++ b/src/gcore/resources/cloud/networks/subnets.py @@ -192,12 +192,15 @@ def update( tags: Update key-value tags using JSON Merge Patch semantics (RFC 7386). Provide key-value pairs to add or update tags. Set tag values to `null` to remove tags. - Unspecified tags remain unchanged. **Examples:** + Unspecified tags remain unchanged. Read-only tags are always preserved and + cannot be modified. **Examples:** - **Add/update tags:** `{'tags': {'environment': 'production', 'team': 'backend'}}` adds new tags or updates existing ones. - **Delete tags:** `{'tags': {'`old_tag`': null}}` removes specific tags. + - **Remove all tags:** `{'tags': null}` removes all user-managed tags (read-only + tags are preserved). - **Partial update:** `{'tags': {'environment': 'staging'}}` only updates specified tags. - **Mixed operations:** @@ -293,9 +296,7 @@ def list( tag_key: Optional. Filter by tag keys. ?`tag_key`=key1&`tag_key`=key2 - tag_key_value: Optional. Filter by tag key-value pairs. curl -G --data-urlencode - "`tag_key_value`={"key": "value"}" --url - "https://example.com/cloud/v1/resource/1/1" + tag_key_value: Optional. Filter by tag key-value pairs. extra_headers: Send extra headers @@ -587,12 +588,15 @@ async def update( tags: Update key-value tags using JSON Merge Patch semantics (RFC 7386). Provide key-value pairs to add or update tags. Set tag values to `null` to remove tags. - Unspecified tags remain unchanged. **Examples:** + Unspecified tags remain unchanged. Read-only tags are always preserved and + cannot be modified. **Examples:** - **Add/update tags:** `{'tags': {'environment': 'production', 'team': 'backend'}}` adds new tags or updates existing ones. - **Delete tags:** `{'tags': {'`old_tag`': null}}` removes specific tags. + - **Remove all tags:** `{'tags': null}` removes all user-managed tags (read-only + tags are preserved). - **Partial update:** `{'tags': {'environment': 'staging'}}` only updates specified tags. - **Mixed operations:** @@ -688,9 +692,7 @@ def list( tag_key: Optional. Filter by tag keys. ?`tag_key`=key1&`tag_key`=key2 - tag_key_value: Optional. Filter by tag key-value pairs. curl -G --data-urlencode - "`tag_key_value`={"key": "value"}" --url - "https://example.com/cloud/v1/resource/1/1" + tag_key_value: Optional. Filter by tag key-value pairs. extra_headers: Send extra headers diff --git a/src/gcore/resources/cloud/projects.py b/src/gcore/resources/cloud/projects.py index 1412cae0..383f3c3b 100644 --- a/src/gcore/resources/cloud/projects.py +++ b/src/gcore/resources/cloud/projects.py @@ -60,12 +60,13 @@ def create( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Project: - """Create project + """Create a new project for a client. - Args: - name: Unique project name for a client. + Project management must be enabled to perform + this operation. - Each client always has one "default" project. + Args: + name: Unique project name for a client. Each client always has one "default" project. client_id: ID associated with the client. @@ -114,8 +115,10 @@ def list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SyncOffsetPage[Project]: - """ - List projects + """Retrieve a list of projects for a client. + + Results can be filtered by name and + ordered by various fields. Args: client_id: Client ID filter for administrators. @@ -172,9 +175,11 @@ def delete( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: - """ - All cloud resources in all regions that belong to the project will be deleted - and will not be recoverable + """Delete a project and all its associated cloud resources across all regions. + + This + operation is irreversible and cannot be undone. Default projects cannot be + deleted. Args: extra_headers: Send extra headers @@ -207,7 +212,7 @@ def get( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Project: """ - Get Project + Retrieve detailed information about a specific project. Args: extra_headers: Send extra headers @@ -241,8 +246,10 @@ def replace( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Project: - """ - Update Project + """Update project name and description. + + Project management must be enabled to + perform this operation. Args: name: Name of the entity, following a specific format. @@ -309,12 +316,13 @@ async def create( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Project: - """Create project + """Create a new project for a client. - Args: - name: Unique project name for a client. + Project management must be enabled to perform + this operation. - Each client always has one "default" project. + Args: + name: Unique project name for a client. Each client always has one "default" project. client_id: ID associated with the client. @@ -363,8 +371,10 @@ def list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AsyncPaginator[Project, AsyncOffsetPage[Project]]: - """ - List projects + """Retrieve a list of projects for a client. + + Results can be filtered by name and + ordered by various fields. Args: client_id: Client ID filter for administrators. @@ -421,9 +431,11 @@ async def delete( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: - """ - All cloud resources in all regions that belong to the project will be deleted - and will not be recoverable + """Delete a project and all its associated cloud resources across all regions. + + This + operation is irreversible and cannot be undone. Default projects cannot be + deleted. Args: extra_headers: Send extra headers @@ -456,7 +468,7 @@ async def get( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Project: """ - Get Project + Retrieve detailed information about a specific project. Args: extra_headers: Send extra headers @@ -490,8 +502,10 @@ async def replace( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Project: - """ - Update Project + """Update project name and description. + + Project management must be enabled to + perform this operation. Args: name: Name of the entity, following a specific format. diff --git a/src/gcore/resources/cloud/quotas/quotas.py b/src/gcore/resources/cloud/quotas/quotas.py index 46f5ca02..9a9f9c82 100644 --- a/src/gcore/resources/cloud/quotas/quotas.py +++ b/src/gcore/resources/cloud/quotas/quotas.py @@ -63,7 +63,7 @@ def get_all( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> QuotaGetAllResponse: - """Get combined client quotas, regional and global.""" + """Get combined client quotas, including both regional and global quotas.""" return self._get( "/cloud/v2/client_quotas", options=make_request_options( @@ -85,7 +85,7 @@ def get_by_region( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> QuotaGetByRegionResponse: """ - Get a quota by region + Get quotas for a specific region and client. Args: client_id: Client ID @@ -122,7 +122,7 @@ def get_global( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> QuotaGetGlobalResponse: """ - Get global quota + Get global quotas for a specific client. Args: client_id: Client ID @@ -178,7 +178,7 @@ async def get_all( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> QuotaGetAllResponse: - """Get combined client quotas, regional and global.""" + """Get combined client quotas, including both regional and global quotas.""" return await self._get( "/cloud/v2/client_quotas", options=make_request_options( @@ -200,7 +200,7 @@ async def get_by_region( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> QuotaGetByRegionResponse: """ - Get a quota by region + Get quotas for a specific region and client. Args: client_id: Client ID @@ -237,7 +237,7 @@ async def get_global( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> QuotaGetGlobalResponse: """ - Get global quota + Get global quotas for a specific client. Args: client_id: Client ID diff --git a/src/gcore/resources/cloud/quotas/requests.py b/src/gcore/resources/cloud/quotas/requests.py index 39611360..d6ba0a82 100644 --- a/src/gcore/resources/cloud/quotas/requests.py +++ b/src/gcore/resources/cloud/quotas/requests.py @@ -60,7 +60,7 @@ def create( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> None: """ - Create request to change quotas + Create a request to change current quotas. Args: description: Describe the reason, in general terms. @@ -108,7 +108,7 @@ def list( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SyncOffsetPage[RequestListResponse]: """ - Returns a list of sent requests to change current quotas and their statuses + Get a list of sent requests to change current quotas and their statuses. Args: limit: Optional. Limit the number of returned items @@ -158,7 +158,7 @@ def delete( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> None: """ - Delete request to change quotas + Delete a specific quota limit request. Args: request_id: LimitRequest ID @@ -194,7 +194,7 @@ def get( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> RequestGetResponse: """ - Get request to change quota limits. + Get detailed information about a specific quota limit request. Args: request_id: LimitRequest ID @@ -252,7 +252,7 @@ async def create( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> None: """ - Create request to change quotas + Create a request to change current quotas. Args: description: Describe the reason, in general terms. @@ -300,7 +300,7 @@ def list( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AsyncPaginator[RequestListResponse, AsyncOffsetPage[RequestListResponse]]: """ - Returns a list of sent requests to change current quotas and their statuses + Get a list of sent requests to change current quotas and their statuses. Args: limit: Optional. Limit the number of returned items @@ -350,7 +350,7 @@ async def delete( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> None: """ - Delete request to change quotas + Delete a specific quota limit request. Args: request_id: LimitRequest ID @@ -386,7 +386,7 @@ async def get( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> RequestGetResponse: """ - Get request to change quota limits. + Get detailed information about a specific quota limit request. Args: request_id: LimitRequest ID diff --git a/src/gcore/resources/cloud/registries/artifacts.py b/src/gcore/resources/cloud/registries/artifacts.py index f336141b..0b579ba5 100644 --- a/src/gcore/resources/cloud/registries/artifacts.py +++ b/src/gcore/resources/cloud/registries/artifacts.py @@ -54,7 +54,7 @@ def list( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> RegistryArtifactList: """ - List artifacts + List all artifacts in a specific repository. Args: extra_headers: Send extra headers @@ -95,7 +95,7 @@ def delete( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> None: """ - Delete an artifact + Delete a specific artifact from a repository. Args: extra_headers: Send extra headers @@ -159,7 +159,7 @@ async def list( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> RegistryArtifactList: """ - List artifacts + List all artifacts in a specific repository. Args: extra_headers: Send extra headers @@ -200,7 +200,7 @@ async def delete( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> None: """ - Delete an artifact + Delete a specific artifact from a repository. Args: extra_headers: Send extra headers diff --git a/src/gcore/resources/cloud/registries/registries.py b/src/gcore/resources/cloud/registries/registries.py index 2d9580ce..8889d473 100644 --- a/src/gcore/resources/cloud/registries/registries.py +++ b/src/gcore/resources/cloud/registries/registries.py @@ -104,12 +104,11 @@ def create( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Registry: - """Create a registry + """ + Create a new container registry with the specified configuration. Args: - name: A name for the container registry. - - Should be in lowercase, consisting only of + name: A name for the container registry. Should be in lowercase, consisting only of numbers, letters and -, with maximum length of 24 characters storage_limit: Registry storage limit, GiB @@ -154,7 +153,7 @@ def list( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> RegistryList: """ - Get registry list + List all container registries in the specified project and region. Args: extra_headers: Send extra headers @@ -191,7 +190,7 @@ def delete( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> None: """ - Delete a registry + Delete a specific container registry and all its associated resources. Args: extra_headers: Send extra headers @@ -229,7 +228,7 @@ def get( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Registry: """ - Get a registry + Get detailed information about a specific container registry. Args: extra_headers: Send extra headers @@ -267,7 +266,7 @@ def resize( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Registry: """ - Resize a registry + Update the size of a container registry. Args: storage_limit: Registry storage limit, GiB @@ -344,12 +343,11 @@ async def create( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Registry: - """Create a registry + """ + Create a new container registry with the specified configuration. Args: - name: A name for the container registry. - - Should be in lowercase, consisting only of + name: A name for the container registry. Should be in lowercase, consisting only of numbers, letters and -, with maximum length of 24 characters storage_limit: Registry storage limit, GiB @@ -394,7 +392,7 @@ async def list( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> RegistryList: """ - Get registry list + List all container registries in the specified project and region. Args: extra_headers: Send extra headers @@ -431,7 +429,7 @@ async def delete( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> None: """ - Delete a registry + Delete a specific container registry and all its associated resources. Args: extra_headers: Send extra headers @@ -469,7 +467,7 @@ async def get( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Registry: """ - Get a registry + Get detailed information about a specific container registry. Args: extra_headers: Send extra headers @@ -507,7 +505,7 @@ async def resize( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Registry: """ - Resize a registry + Update the size of a container registry. Args: storage_limit: Registry storage limit, GiB diff --git a/src/gcore/resources/cloud/registries/repositories.py b/src/gcore/resources/cloud/registries/repositories.py index b2d9c600..74377052 100644 --- a/src/gcore/resources/cloud/registries/repositories.py +++ b/src/gcore/resources/cloud/registries/repositories.py @@ -53,7 +53,7 @@ def list( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> RegistryRepositoryList: """ - List repositories + List all repositories in the container registry. Args: extra_headers: Send extra headers @@ -91,7 +91,7 @@ def delete( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> None: """ - Delete a repository + Delete a specific repository from the container registry. Args: extra_headers: Send extra headers @@ -152,7 +152,7 @@ async def list( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> RegistryRepositoryList: """ - List repositories + List all repositories in the container registry. Args: extra_headers: Send extra headers @@ -190,7 +190,7 @@ async def delete( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> None: """ - Delete a repository + Delete a specific repository from the container registry. Args: extra_headers: Send extra headers diff --git a/src/gcore/resources/cloud/registries/tags.py b/src/gcore/resources/cloud/registries/tags.py index 3a8d39ea..ab35828c 100644 --- a/src/gcore/resources/cloud/registries/tags.py +++ b/src/gcore/resources/cloud/registries/tags.py @@ -55,7 +55,7 @@ def delete( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> None: """ - Delete a tag + Delete a specific tag from an artifact. Args: extra_headers: Send extra headers @@ -123,7 +123,7 @@ async def delete( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> None: """ - Delete a tag + Delete a specific tag from an artifact. Args: extra_headers: Send extra headers diff --git a/src/gcore/resources/cloud/registries/users.py b/src/gcore/resources/cloud/registries/users.py index c9e1b821..f535c7dd 100644 --- a/src/gcore/resources/cloud/registries/users.py +++ b/src/gcore/resources/cloud/registries/users.py @@ -21,6 +21,7 @@ from ....types.cloud.registries.registry_user import RegistryUser from ....types.cloud.registries.registry_user_list import RegistryUserList from ....types.cloud.registries.registry_user_created import RegistryUserCreated +from ....types.cloud.registries.user_refresh_secret_response import UserRefreshSecretResponse __all__ = ["UsersResource", "AsyncUsersResource"] @@ -61,9 +62,9 @@ def create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> RegistryUser: + ) -> RegistryUserCreated: """ - Create a user + Create a new user for accessing the container registry. Args: duration: User account operating time, days @@ -101,7 +102,7 @@ def create( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=RegistryUser, + cast_to=RegistryUserCreated, ) def update( @@ -121,7 +122,7 @@ def update( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> RegistryUser: """ - Update a user + Update the configuration of a specific registry user. Args: duration: User account operating time, days @@ -169,7 +170,7 @@ def list( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> RegistryUserList: """ - Get user list + List all users with access to the container registry. Args: extra_headers: Send extra headers @@ -207,7 +208,7 @@ def delete( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> None: """ - Delete a user + Delete a specific user from the container registry. Args: extra_headers: Send extra headers @@ -246,7 +247,7 @@ def create_multiple( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> RegistryUserCreated: """ - Batch create users + Create multiple users for accessing the container registry in a single request. Args: users: Set of users @@ -285,9 +286,9 @@ def refresh_secret( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> None: + ) -> UserRefreshSecretResponse: """ - Refresh a secret + Generate a new secret for a specific registry user. Args: extra_headers: Send extra headers @@ -302,13 +303,12 @@ def refresh_secret( project_id = self._client._get_cloud_project_id_path_param() if region_id is None: region_id = self._client._get_cloud_region_id_path_param() - extra_headers = {"Accept": "*/*", **(extra_headers or {})} return self._post( f"/cloud/v1/registries/{project_id}/{region_id}/{registry_id}/users/{user_id}/refresh_secret", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=NoneType, + cast_to=UserRefreshSecretResponse, ) @@ -348,9 +348,9 @@ async def create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> RegistryUser: + ) -> RegistryUserCreated: """ - Create a user + Create a new user for accessing the container registry. Args: duration: User account operating time, days @@ -388,7 +388,7 @@ async def create( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=RegistryUser, + cast_to=RegistryUserCreated, ) async def update( @@ -408,7 +408,7 @@ async def update( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> RegistryUser: """ - Update a user + Update the configuration of a specific registry user. Args: duration: User account operating time, days @@ -456,7 +456,7 @@ async def list( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> RegistryUserList: """ - Get user list + List all users with access to the container registry. Args: extra_headers: Send extra headers @@ -494,7 +494,7 @@ async def delete( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> None: """ - Delete a user + Delete a specific user from the container registry. Args: extra_headers: Send extra headers @@ -533,7 +533,7 @@ async def create_multiple( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> RegistryUserCreated: """ - Batch create users + Create multiple users for accessing the container registry in a single request. Args: users: Set of users @@ -572,9 +572,9 @@ async def refresh_secret( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> None: + ) -> UserRefreshSecretResponse: """ - Refresh a secret + Generate a new secret for a specific registry user. Args: extra_headers: Send extra headers @@ -589,13 +589,12 @@ async def refresh_secret( project_id = self._client._get_cloud_project_id_path_param() if region_id is None: region_id = self._client._get_cloud_region_id_path_param() - extra_headers = {"Accept": "*/*", **(extra_headers or {})} return await self._post( f"/cloud/v1/registries/{project_id}/{region_id}/{registry_id}/users/{user_id}/refresh_secret", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=NoneType, + cast_to=UserRefreshSecretResponse, ) diff --git a/src/gcore/resources/cloud/reserved_fixed_ips/reserved_fixed_ips.py b/src/gcore/resources/cloud/reserved_fixed_ips/reserved_fixed_ips.py index 6afd2869..d5b0e030 100644 --- a/src/gcore/resources/cloud/reserved_fixed_ips/reserved_fixed_ips.py +++ b/src/gcore/resources/cloud/reserved_fixed_ips/reserved_fixed_ips.py @@ -76,7 +76,7 @@ def create( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: """ - Create reserved fixed IP + Create a new reserved fixed IP with the specified configuration. Args: type: Must be 'external' @@ -112,7 +112,7 @@ def create( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: """ - Create reserved fixed IP + Create a new reserved fixed IP with the specified configuration. Args: subnet_id: Reserved fixed IP will be allocated in this subnet @@ -149,7 +149,7 @@ def create( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: """ - Create reserved fixed IP + Create a new reserved fixed IP with the specified configuration. Args: network_id: Reserved fixed IP will be allocated in a subnet of this network @@ -188,7 +188,7 @@ def create( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: """ - Create reserved fixed IP + Create a new reserved fixed IP with the specified configuration. Args: ip_address: Reserved fixed IP will be allocated the given IP address @@ -225,7 +225,7 @@ def create( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: """ - Create reserved fixed IP + Create a new reserved fixed IP with the specified configuration. Args: port_id: Port ID to make a reserved fixed IP (for example, `vip_port_id` of the Load @@ -315,7 +315,7 @@ def list( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SyncOffsetPage[ReservedFixedIP]: """ - List reserved fixed IPs + List all reserved fixed IPs in the specified project and region. Args: available_only: Set to true if the response should only list IP addresses that are not attached @@ -334,8 +334,8 @@ def list( offset: Offset value is used to exclude the first set of records from the result order_by: Ordering reserved fixed IP list result by name, status, `updated_at`, - `created_at` or `fixed_ip_address` fields of the reserved fixed IP and - directions (status.asc), default is "`fixed_ip_address`.asc" + `created_at` or `fixed_ip_address` fields and directions (status.asc), default + is "`fixed_ip_address`.asc" vip_only: Set to true if the response should only list VIPs @@ -391,7 +391,7 @@ def delete( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: """ - Delete reserved fixed ip + Delete a specific reserved fixed IP and all its associated resources. Args: extra_headers: Send extra headers @@ -430,7 +430,7 @@ def get( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ReservedFixedIP: """ - Get reserved fixed IP + Get detailed information about a specific reserved fixed IP. Args: extra_headers: Send extra headers @@ -497,7 +497,7 @@ async def create( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: """ - Create reserved fixed IP + Create a new reserved fixed IP with the specified configuration. Args: type: Must be 'external' @@ -533,7 +533,7 @@ async def create( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: """ - Create reserved fixed IP + Create a new reserved fixed IP with the specified configuration. Args: subnet_id: Reserved fixed IP will be allocated in this subnet @@ -570,7 +570,7 @@ async def create( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: """ - Create reserved fixed IP + Create a new reserved fixed IP with the specified configuration. Args: network_id: Reserved fixed IP will be allocated in a subnet of this network @@ -609,7 +609,7 @@ async def create( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: """ - Create reserved fixed IP + Create a new reserved fixed IP with the specified configuration. Args: ip_address: Reserved fixed IP will be allocated the given IP address @@ -646,7 +646,7 @@ async def create( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: """ - Create reserved fixed IP + Create a new reserved fixed IP with the specified configuration. Args: port_id: Port ID to make a reserved fixed IP (for example, `vip_port_id` of the Load @@ -736,7 +736,7 @@ def list( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AsyncPaginator[ReservedFixedIP, AsyncOffsetPage[ReservedFixedIP]]: """ - List reserved fixed IPs + List all reserved fixed IPs in the specified project and region. Args: available_only: Set to true if the response should only list IP addresses that are not attached @@ -755,8 +755,8 @@ def list( offset: Offset value is used to exclude the first set of records from the result order_by: Ordering reserved fixed IP list result by name, status, `updated_at`, - `created_at` or `fixed_ip_address` fields of the reserved fixed IP and - directions (status.asc), default is "`fixed_ip_address`.asc" + `created_at` or `fixed_ip_address` fields and directions (status.asc), default + is "`fixed_ip_address`.asc" vip_only: Set to true if the response should only list VIPs @@ -812,7 +812,7 @@ async def delete( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: """ - Delete reserved fixed ip + Delete a specific reserved fixed IP and all its associated resources. Args: extra_headers: Send extra headers @@ -851,7 +851,7 @@ async def get( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ReservedFixedIP: """ - Get reserved fixed IP + Get detailed information about a specific reserved fixed IP. Args: extra_headers: Send extra headers diff --git a/src/gcore/resources/cloud/reserved_fixed_ips/vip.py b/src/gcore/resources/cloud/reserved_fixed_ips/vip.py index 5bfbd5c9..6d3c91f3 100644 --- a/src/gcore/resources/cloud/reserved_fixed_ips/vip.py +++ b/src/gcore/resources/cloud/reserved_fixed_ips/vip.py @@ -63,7 +63,7 @@ def list_candidate_ports( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> CandidatePortList: """ - List instance ports that are available for connecting to VIP + List all instance ports that are available for connecting to a VIP. Args: extra_headers: Send extra headers @@ -102,7 +102,7 @@ def list_connected_ports( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ConnectedPortList: """ - List instance ports that share VIP + List all instance ports that share a VIP. Args: extra_headers: Send extra headers @@ -142,7 +142,7 @@ def replace_connected_ports( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ConnectedPortList: """ - Replace ports that share VIP + Replace the list of instance ports that share a VIP. Args: port_ids: List of port IDs that will share one VIP @@ -187,7 +187,7 @@ def toggle( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ReservedFixedIP: """ - Switch VIP status of reserved fixed IP + Update the VIP status of a reserved fixed IP. Args: is_vip: If reserved fixed IP should be a VIP @@ -230,7 +230,7 @@ def update_connected_ports( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ConnectedPortList: """ - Add ports that share VIP + Add instance ports to share a VIP. Args: port_ids: List of port IDs that will share one VIP @@ -295,7 +295,7 @@ async def list_candidate_ports( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> CandidatePortList: """ - List instance ports that are available for connecting to VIP + List all instance ports that are available for connecting to a VIP. Args: extra_headers: Send extra headers @@ -334,7 +334,7 @@ async def list_connected_ports( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ConnectedPortList: """ - List instance ports that share VIP + List all instance ports that share a VIP. Args: extra_headers: Send extra headers @@ -374,7 +374,7 @@ async def replace_connected_ports( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ConnectedPortList: """ - Replace ports that share VIP + Replace the list of instance ports that share a VIP. Args: port_ids: List of port IDs that will share one VIP @@ -419,7 +419,7 @@ async def toggle( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ReservedFixedIP: """ - Switch VIP status of reserved fixed IP + Update the VIP status of a reserved fixed IP. Args: is_vip: If reserved fixed IP should be a VIP @@ -462,7 +462,7 @@ async def update_connected_ports( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ConnectedPortList: """ - Add ports that share VIP + Add instance ports to share a VIP. Args: port_ids: List of port IDs that will share one VIP diff --git a/src/gcore/resources/cloud/security_groups/rules.py b/src/gcore/resources/cloud/security_groups/rules.py index 105615d6..89b27a23 100644 --- a/src/gcore/resources/cloud/security_groups/rules.py +++ b/src/gcore/resources/cloud/security_groups/rules.py @@ -92,7 +92,7 @@ def create( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SecurityGroupRule: """ - Add new rule to security group + Add a new rule to an existing security group. Args: description: Rule description @@ -160,7 +160,7 @@ def delete( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> None: """ - Delete security group rule + Delete a specific rule from a security group. Args: extra_headers: Send extra headers @@ -235,7 +235,7 @@ def replace( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SecurityGroupRule: """ - Edit the security group rule: delete old and create new rule + Update the configuration of an existing security group rule. Args: direction: Ingress or egress, which is the direction in which the security group rule is @@ -363,7 +363,7 @@ async def create( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SecurityGroupRule: """ - Add new rule to security group + Add a new rule to an existing security group. Args: description: Rule description @@ -431,7 +431,7 @@ async def delete( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> None: """ - Delete security group rule + Delete a specific rule from a security group. Args: extra_headers: Send extra headers @@ -506,7 +506,7 @@ async def replace( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SecurityGroupRule: """ - Edit the security group rule: delete old and create new rule + Update the configuration of an existing security group rule. Args: direction: Ingress or egress, which is the direction in which the security group rule is diff --git a/src/gcore/resources/cloud/security_groups/security_groups.py b/src/gcore/resources/cloud/security_groups/security_groups.py index cddbd7a2..034bbe2b 100644 --- a/src/gcore/resources/cloud/security_groups/security_groups.py +++ b/src/gcore/resources/cloud/security_groups/security_groups.py @@ -77,7 +77,7 @@ def create( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SecurityGroup: """ - Create security group + Create a new security group with the specified configuration. Args: security_group: Security group @@ -128,7 +128,7 @@ def update( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SecurityGroup: """ - Update security group + Update the configuration of an existing security group. Args: changed_rules: List of rules to create or delete @@ -137,12 +137,15 @@ def update( tags: Update key-value tags using JSON Merge Patch semantics (RFC 7386). Provide key-value pairs to add or update tags. Set tag values to `null` to remove tags. - Unspecified tags remain unchanged. **Examples:** + Unspecified tags remain unchanged. Read-only tags are always preserved and + cannot be modified. **Examples:** - **Add/update tags:** `{'tags': {'environment': 'production', 'team': 'backend'}}` adds new tags or updates existing ones. - **Delete tags:** `{'tags': {'`old_tag`': null}}` removes specific tags. + - **Remove all tags:** `{'tags': null}` removes all user-managed tags (read-only + tags are preserved). - **Partial update:** `{'tags': {'environment': 'staging'}}` only updates specified tags. - **Mixed operations:** @@ -199,18 +202,16 @@ def list( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SyncOffsetPage[SecurityGroup]: """ - Get security groups + List all security groups in the specified project and region. Args: - limit: Limit the number of returned limit request entities. + limit: Limit the number of returned security groups - offset: Offset value is used to exclude the first set of records from the result. + offset: Offset value is used to exclude the first set of records from the result tag_key: Filter by tag keys. - tag_key_value: Filter by tag key-value pairs. Must be a valid JSON string. curl -G - --data-urlencode "`tag_key_value`={"key": "value"}" --url - "http://localhost:1111/v1/securitygroups/1/1" + tag_key_value: Filter by tag key-value pairs. Must be a valid JSON string. extra_headers: Send extra headers @@ -259,7 +260,7 @@ def delete( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> None: """ - Delete security group + Delete a specific security group and all its associated rules. Args: extra_headers: Send extra headers @@ -298,9 +299,9 @@ def copy( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> None: + ) -> SecurityGroup: """ - Create a deep copy of security group + Create a deep copy of an existing security group. Args: name: Name. @@ -319,14 +320,13 @@ def copy( region_id = self._client._get_cloud_region_id_path_param() if not group_id: raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") - extra_headers = {"Accept": "*/*", **(extra_headers or {})} return self._post( f"/cloud/v1/securitygroups/{project_id}/{region_id}/{group_id}/copy", body=maybe_transform({"name": name}, security_group_copy_params.SecurityGroupCopyParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=NoneType, + cast_to=SecurityGroup, ) def get( @@ -343,7 +343,7 @@ def get( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SecurityGroup: """ - Get security group + Get detailed information about a specific security group. Args: extra_headers: Send extra headers @@ -382,7 +382,7 @@ def revert_to_default( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SecurityGroup: """ - Revert security group + Revert a security group to its previous state. Args: extra_headers: Send extra headers @@ -447,7 +447,7 @@ async def create( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SecurityGroup: """ - Create security group + Create a new security group with the specified configuration. Args: security_group: Security group @@ -498,7 +498,7 @@ async def update( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SecurityGroup: """ - Update security group + Update the configuration of an existing security group. Args: changed_rules: List of rules to create or delete @@ -507,12 +507,15 @@ async def update( tags: Update key-value tags using JSON Merge Patch semantics (RFC 7386). Provide key-value pairs to add or update tags. Set tag values to `null` to remove tags. - Unspecified tags remain unchanged. **Examples:** + Unspecified tags remain unchanged. Read-only tags are always preserved and + cannot be modified. **Examples:** - **Add/update tags:** `{'tags': {'environment': 'production', 'team': 'backend'}}` adds new tags or updates existing ones. - **Delete tags:** `{'tags': {'`old_tag`': null}}` removes specific tags. + - **Remove all tags:** `{'tags': null}` removes all user-managed tags (read-only + tags are preserved). - **Partial update:** `{'tags': {'environment': 'staging'}}` only updates specified tags. - **Mixed operations:** @@ -569,18 +572,16 @@ def list( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AsyncPaginator[SecurityGroup, AsyncOffsetPage[SecurityGroup]]: """ - Get security groups + List all security groups in the specified project and region. Args: - limit: Limit the number of returned limit request entities. + limit: Limit the number of returned security groups - offset: Offset value is used to exclude the first set of records from the result. + offset: Offset value is used to exclude the first set of records from the result tag_key: Filter by tag keys. - tag_key_value: Filter by tag key-value pairs. Must be a valid JSON string. curl -G - --data-urlencode "`tag_key_value`={"key": "value"}" --url - "http://localhost:1111/v1/securitygroups/1/1" + tag_key_value: Filter by tag key-value pairs. Must be a valid JSON string. extra_headers: Send extra headers @@ -629,7 +630,7 @@ async def delete( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> None: """ - Delete security group + Delete a specific security group and all its associated rules. Args: extra_headers: Send extra headers @@ -668,9 +669,9 @@ async def copy( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> None: + ) -> SecurityGroup: """ - Create a deep copy of security group + Create a deep copy of an existing security group. Args: name: Name. @@ -689,14 +690,13 @@ async def copy( region_id = self._client._get_cloud_region_id_path_param() if not group_id: raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") - extra_headers = {"Accept": "*/*", **(extra_headers or {})} return await self._post( f"/cloud/v1/securitygroups/{project_id}/{region_id}/{group_id}/copy", body=await async_maybe_transform({"name": name}, security_group_copy_params.SecurityGroupCopyParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=NoneType, + cast_to=SecurityGroup, ) async def get( @@ -713,7 +713,7 @@ async def get( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SecurityGroup: """ - Get security group + Get detailed information about a specific security group. Args: extra_headers: Send extra headers @@ -752,7 +752,7 @@ async def revert_to_default( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SecurityGroup: """ - Revert security group + Revert a security group to its previous state. Args: extra_headers: Send extra headers diff --git a/src/gcore/resources/cloud/tasks.py b/src/gcore/resources/cloud/tasks.py index 011f7d3b..bb48ed9e 100644 --- a/src/gcore/resources/cloud/tasks.py +++ b/src/gcore/resources/cloud/tasks.py @@ -233,7 +233,7 @@ def acknowledge_all( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> None: """ - Acknowledge all client tasks in project or region + Acknowledge all tasks Args: project_id: Project ID @@ -279,7 +279,7 @@ def acknowledge_one( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Task: """ - Acknowledge one task on project scope + Acknowledge one task Args: task_id: Task ID @@ -541,7 +541,7 @@ async def acknowledge_all( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> None: """ - Acknowledge all client tasks in project or region + Acknowledge all tasks Args: project_id: Project ID @@ -587,7 +587,7 @@ async def acknowledge_one( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Task: """ - Acknowledge one task on project scope + Acknowledge one task Args: task_id: Task ID diff --git a/src/gcore/resources/cloud/users/role_assignments.py b/src/gcore/resources/cloud/users/role_assignments.py index 4bb855df..1b09de11 100644 --- a/src/gcore/resources/cloud/users/role_assignments.py +++ b/src/gcore/resources/cloud/users/role_assignments.py @@ -64,7 +64,7 @@ def create( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> RoleAssignment: """ - Assign role to existing user + Assign a role to an existing user in the specified scope. Args: role: User role @@ -116,7 +116,7 @@ def update( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> RoleAssignmentUpdateDelete: """ - Modify role assignment to existing user + Modify an existing role assignment for a user. Args: assignment_id: Assignment ID @@ -168,12 +168,11 @@ def list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SyncOffsetPage[RoleAssignment]: - """List assignments + """ + List all role assignments in the specified scope. Args: - limit: Limit the number of returned items. - - Falls back to default of 1000 if not + limit: Limit the number of returned items. Falls back to default of 1000 if not specified. Limited by max limit value of 1000 offset: Offset value is used to exclude the first set of records from the result @@ -223,7 +222,7 @@ def delete( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> RoleAssignmentUpdateDelete: """ - Delete role assignment + Delete an existing role assignment. Args: assignment_id: Assignment ID @@ -280,7 +279,7 @@ async def create( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> RoleAssignment: """ - Assign role to existing user + Assign a role to an existing user in the specified scope. Args: role: User role @@ -332,7 +331,7 @@ async def update( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> RoleAssignmentUpdateDelete: """ - Modify role assignment to existing user + Modify an existing role assignment for a user. Args: assignment_id: Assignment ID @@ -384,12 +383,11 @@ def list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AsyncPaginator[RoleAssignment, AsyncOffsetPage[RoleAssignment]]: - """List assignments + """ + List all role assignments in the specified scope. Args: - limit: Limit the number of returned items. - - Falls back to default of 1000 if not + limit: Limit the number of returned items. Falls back to default of 1000 if not specified. Limited by max limit value of 1000 offset: Offset value is used to exclude the first set of records from the result @@ -439,7 +437,7 @@ async def delete( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> RoleAssignmentUpdateDelete: """ - Delete role assignment + Delete an existing role assignment. Args: assignment_id: Assignment ID diff --git a/src/gcore/resources/cloud/volumes.py b/src/gcore/resources/cloud/volumes.py index dc35bbba..7b86a7e9 100644 --- a/src/gcore/resources/cloud/volumes.py +++ b/src/gcore/resources/cloud/volumes.py @@ -79,8 +79,11 @@ def create( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: - """ - Create volume + """Create a new volume in the project and region. + + The volume can be created from + scratch, from an image, or from a snapshot. Optionally attach the volume to an + instance during creation. Args: project_id: Project ID @@ -145,8 +148,11 @@ def create( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: - """ - Create volume + """Create a new volume in the project and region. + + The volume can be created from + scratch, from an image, or from a snapshot. Optionally attach the volume to an + instance during creation. Args: project_id: Project ID @@ -211,8 +217,11 @@ def create( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: - """ - Create volume + """Create a new volume in the project and region. + + The volume can be created from + scratch, from an image, or from a snapshot. Optionally attach the volume to an + instance during creation. Args: project_id: Project ID @@ -320,7 +329,7 @@ def update( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Volume: """ - Rename volume + Rename a volume. Args: project_id: Project ID @@ -376,8 +385,11 @@ def list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SyncOffsetPage[Volume]: - """ - List volumes + """Retrieve a list of volumes in the project and region. + + The list can be filtered + by various parameters like bootable status, metadata/tags, attachments, instance + ID, name, and ID. Args: project_id: Project ID @@ -404,9 +416,7 @@ def list( tag_key: Optional. Filter by tag keys. ?`tag_key`=key1&`tag_key`=key2 - tag_key_value: Optional. Filter by tag key-value pairs. curl -G --data-urlencode - "`tag_key_value`={"key": "value"}" --url - "https://example.com/cloud/v1/resource/1/1" + tag_key_value: Optional. Filter by tag key-value pairs. extra_headers: Send extra headers @@ -461,8 +471,10 @@ def delete( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: - """ - Delete volume + """Delete a volume and all its snapshots. + + The volume must be in an available state + to be deleted. Args: project_id: Project ID @@ -573,8 +585,10 @@ def change_type( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Volume: - """ - Change volume type + """Change the type of a volume. + + The volume must not have any snapshots to change + its type. Args: project_id: Project ID @@ -673,7 +687,7 @@ def get( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Volume: """ - Get volume + Retrieve detailed information about a specific volume. Args: project_id: Project ID @@ -718,8 +732,10 @@ def resize( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: - """ - Extend volume + """Increase the size of a volume. + + The new size must be greater than the current + size. Args: project_id: Project ID @@ -766,8 +782,10 @@ def revert_to_last_snapshot( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> None: - """ - Revert volume to it's last snapshot + """Revert a volume to its last snapshot. + + The volume must be in an available state + to be reverted. Args: project_id: Project ID @@ -843,8 +861,11 @@ async def create( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: - """ - Create volume + """Create a new volume in the project and region. + + The volume can be created from + scratch, from an image, or from a snapshot. Optionally attach the volume to an + instance during creation. Args: project_id: Project ID @@ -909,8 +930,11 @@ async def create( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: - """ - Create volume + """Create a new volume in the project and region. + + The volume can be created from + scratch, from an image, or from a snapshot. Optionally attach the volume to an + instance during creation. Args: project_id: Project ID @@ -975,8 +999,11 @@ async def create( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: - """ - Create volume + """Create a new volume in the project and region. + + The volume can be created from + scratch, from an image, or from a snapshot. Optionally attach the volume to an + instance during creation. Args: project_id: Project ID @@ -1084,7 +1111,7 @@ async def update( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Volume: """ - Rename volume + Rename a volume. Args: project_id: Project ID @@ -1140,8 +1167,11 @@ def list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AsyncPaginator[Volume, AsyncOffsetPage[Volume]]: - """ - List volumes + """Retrieve a list of volumes in the project and region. + + The list can be filtered + by various parameters like bootable status, metadata/tags, attachments, instance + ID, name, and ID. Args: project_id: Project ID @@ -1168,9 +1198,7 @@ def list( tag_key: Optional. Filter by tag keys. ?`tag_key`=key1&`tag_key`=key2 - tag_key_value: Optional. Filter by tag key-value pairs. curl -G --data-urlencode - "`tag_key_value`={"key": "value"}" --url - "https://example.com/cloud/v1/resource/1/1" + tag_key_value: Optional. Filter by tag key-value pairs. extra_headers: Send extra headers @@ -1225,8 +1253,10 @@ async def delete( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: - """ - Delete volume + """Delete a volume and all its snapshots. + + The volume must be in an available state + to be deleted. Args: project_id: Project ID @@ -1337,8 +1367,10 @@ async def change_type( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Volume: - """ - Change volume type + """Change the type of a volume. + + The volume must not have any snapshots to change + its type. Args: project_id: Project ID @@ -1439,7 +1471,7 @@ async def get( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Volume: """ - Get volume + Retrieve detailed information about a specific volume. Args: project_id: Project ID @@ -1484,8 +1516,10 @@ async def resize( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TaskIDList: - """ - Extend volume + """Increase the size of a volume. + + The new size must be greater than the current + size. Args: project_id: Project ID @@ -1532,8 +1566,10 @@ async def revert_to_last_snapshot( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> None: - """ - Revert volume to it's last snapshot + """Revert a volume to its last snapshot. + + The volume must be in an available state + to be reverted. Args: project_id: Project ID diff --git a/src/gcore/types/cloud/baremetal/image_list_params.py b/src/gcore/types/cloud/baremetal/image_list_params.py index 07949b72..075fe52e 100644 --- a/src/gcore/types/cloud/baremetal/image_list_params.py +++ b/src/gcore/types/cloud/baremetal/image_list_params.py @@ -23,11 +23,7 @@ class ImageListParams(TypedDict, total=False): """Filter by tag keys.""" tag_key_value: str - """Filter by tag key-value pairs. - - Must be a valid JSON string. 'curl -G --data-urlencode '`tag_key_value`={"key": - "value"}' --url 'http://localhost:1111/v1/images/1/1'" - """ + """Filter by tag key-value pairs. Must be a valid JSON string.""" visibility: Literal["private", "public", "shared"] """Image visibility. Globally visible images are public""" diff --git a/src/gcore/types/cloud/baremetal/server_list_params.py b/src/gcore/types/cloud/baremetal/server_list_params.py index e7a0b35b..1ddb1c48 100644 --- a/src/gcore/types/cloud/baremetal/server_list_params.py +++ b/src/gcore/types/cloud/baremetal/server_list_params.py @@ -85,11 +85,7 @@ class ServerListParams(TypedDict, total=False): """Filters instances by a server status, as a string.""" tag_key_value: str - """Optional. - - Filter by tag key-value pairs. curl -G --data-urlencode "`tag_key_value`={"key": - "value"}" --url "https://example.com/cloud/v1/resource/1/1" - """ + """Optional. Filter by tag key-value pairs.""" tag_value: List[str] """Optional. Filter by tag values. ?`tag_value`=value1&`tag_value`=value2""" diff --git a/src/gcore/types/cloud/floating_ip_list_params.py b/src/gcore/types/cloud/floating_ip_list_params.py index 6f0341d0..825b2de1 100644 --- a/src/gcore/types/cloud/floating_ip_list_params.py +++ b/src/gcore/types/cloud/floating_ip_list_params.py @@ -28,8 +28,4 @@ class FloatingIPListParams(TypedDict, total=False): """Optional. Filter by tag keys. ?`tag_key`=key1&`tag_key`=key2""" tag_key_value: str - """Optional. - - Filter by tag key-value pairs. curl -G --data-urlencode "`tag_key_value`={"key": - "value"}" --url "https://example.com/cloud/v1/resource/1/1" - """ + """Optional. Filter by tag key-value pairs.""" diff --git a/src/gcore/types/cloud/instance_list_params.py b/src/gcore/types/cloud/instance_list_params.py index dbbbb038..54ca4270 100644 --- a/src/gcore/types/cloud/instance_list_params.py +++ b/src/gcore/types/cloud/instance_list_params.py @@ -118,11 +118,7 @@ class InstanceListParams(TypedDict, total=False): """Filters instances by status.""" tag_key_value: str - """Optional. - - Filter by tag key-value pairs. curl -G --data-urlencode "`tag_key_value`={"key": - "value"}" --url "https://example.com/cloud/v1/resource/1/1" - """ + """Optional. Filter by tag key-value pairs.""" tag_value: List[str] """Optional. Filter by tag values. ?`tag_value`=value1&`tag_value`=value2""" diff --git a/src/gcore/types/cloud/instances/image_list_params.py b/src/gcore/types/cloud/instances/image_list_params.py index 07949b72..075fe52e 100644 --- a/src/gcore/types/cloud/instances/image_list_params.py +++ b/src/gcore/types/cloud/instances/image_list_params.py @@ -23,11 +23,7 @@ class ImageListParams(TypedDict, total=False): """Filter by tag keys.""" tag_key_value: str - """Filter by tag key-value pairs. - - Must be a valid JSON string. 'curl -G --data-urlencode '`tag_key_value`={"key": - "value"}' --url 'http://localhost:1111/v1/images/1/1'" - """ + """Filter by tag key-value pairs. Must be a valid JSON string.""" visibility: Literal["private", "public", "shared"] """Image visibility. Globally visible images are public""" diff --git a/src/gcore/types/cloud/load_balancer_list_params.py b/src/gcore/types/cloud/load_balancer_list_params.py index eecfdab7..704c2eb3 100644 --- a/src/gcore/types/cloud/load_balancer_list_params.py +++ b/src/gcore/types/cloud/load_balancer_list_params.py @@ -43,11 +43,7 @@ class LoadBalancerListParams(TypedDict, total=False): """Filter by tag keys.""" tag_key_value: str - """Filter by tag key-value pairs. - - Must be a valid JSON string. curl -G --data-urlencode "`tag_key_value`={"key": - "value"}" --url "http://localhost:1111/v1/loadbalancers/1/1" - """ + """Filter by tag key-value pairs. Must be a valid JSON string.""" with_ddos: bool """Show Advanced DDoS protection profile, if exists""" diff --git a/src/gcore/types/cloud/load_balancer_update_params.py b/src/gcore/types/cloud/load_balancer_update_params.py index 4747c719..f4b01786 100644 --- a/src/gcore/types/cloud/load_balancer_update_params.py +++ b/src/gcore/types/cloud/load_balancer_update_params.py @@ -33,12 +33,15 @@ class LoadBalancerUpdateParams(TypedDict, total=False): """Update key-value tags using JSON Merge Patch semantics (RFC 7386). Provide key-value pairs to add or update tags. Set tag values to `null` to - remove tags. Unspecified tags remain unchanged. **Examples:** + remove tags. Unspecified tags remain unchanged. Read-only tags are always + preserved and cannot be modified. **Examples:** - **Add/update tags:** `{'tags': {'environment': 'production', 'team': 'backend'}}` adds new tags or updates existing ones. - **Delete tags:** `{'tags': {'`old_tag`': null}}` removes specific tags. + - **Remove all tags:** `{'tags': null}` removes all user-managed tags (read-only + tags are preserved). - **Partial update:** `{'tags': {'environment': 'staging'}}` only updates specified tags. - **Mixed operations:** diff --git a/src/gcore/types/cloud/network_list_params.py b/src/gcore/types/cloud/network_list_params.py index 2aaa17b2..fd80aecd 100644 --- a/src/gcore/types/cloud/network_list_params.py +++ b/src/gcore/types/cloud/network_list_params.py @@ -37,8 +37,4 @@ class NetworkListParams(TypedDict, total=False): """Optional. Filter by tag keys. ?`tag_key`=key1&`tag_key`=key2""" tag_key_value: str - """Optional. - - Filter by tag key-value pairs. curl -G --data-urlencode "`tag_key_value`={"key": - "value"}" --url "https://example.com/cloud/v1/resource/1/1" - """ + """Optional. Filter by tag key-value pairs.""" diff --git a/src/gcore/types/cloud/network_update_params.py b/src/gcore/types/cloud/network_update_params.py index 828b283c..8aa509d3 100644 --- a/src/gcore/types/cloud/network_update_params.py +++ b/src/gcore/types/cloud/network_update_params.py @@ -24,12 +24,15 @@ class NetworkUpdateParams(TypedDict, total=False): """Update key-value tags using JSON Merge Patch semantics (RFC 7386). Provide key-value pairs to add or update tags. Set tag values to `null` to - remove tags. Unspecified tags remain unchanged. **Examples:** + remove tags. Unspecified tags remain unchanged. Read-only tags are always + preserved and cannot be modified. **Examples:** - **Add/update tags:** `{'tags': {'environment': 'production', 'team': 'backend'}}` adds new tags or updates existing ones. - **Delete tags:** `{'tags': {'`old_tag`': null}}` removes specific tags. + - **Remove all tags:** `{'tags': null}` removes all user-managed tags (read-only + tags are preserved). - **Partial update:** `{'tags': {'environment': 'staging'}}` only updates specified tags. - **Mixed operations:** diff --git a/src/gcore/types/cloud/networks/router_list_params.py b/src/gcore/types/cloud/networks/router_list_params.py index 5ff04e35..4842c311 100644 --- a/src/gcore/types/cloud/networks/router_list_params.py +++ b/src/gcore/types/cloud/networks/router_list_params.py @@ -13,7 +13,7 @@ class RouterListParams(TypedDict, total=False): region_id: int limit: int - """Limit the number of returned limit request entities.""" + """Limit the number of returned routers""" offset: int - """Offset value is used to exclude the first set of records from the result.""" + """Offset value is used to exclude the first set of records from the result""" diff --git a/src/gcore/types/cloud/networks/subnet_list_params.py b/src/gcore/types/cloud/networks/subnet_list_params.py index 8865e006..ecae30b7 100644 --- a/src/gcore/types/cloud/networks/subnet_list_params.py +++ b/src/gcore/types/cloud/networks/subnet_list_params.py @@ -51,8 +51,4 @@ class SubnetListParams(TypedDict, total=False): """Optional. Filter by tag keys. ?`tag_key`=key1&`tag_key`=key2""" tag_key_value: str - """Optional. - - Filter by tag key-value pairs. curl -G --data-urlencode "`tag_key_value`={"key": - "value"}" --url "https://example.com/cloud/v1/resource/1/1" - """ + """Optional. Filter by tag key-value pairs.""" diff --git a/src/gcore/types/cloud/networks/subnet_update_params.py b/src/gcore/types/cloud/networks/subnet_update_params.py index 2ade80c6..966b3ccf 100644 --- a/src/gcore/types/cloud/networks/subnet_update_params.py +++ b/src/gcore/types/cloud/networks/subnet_update_params.py @@ -41,12 +41,15 @@ class SubnetUpdateParams(TypedDict, total=False): """Update key-value tags using JSON Merge Patch semantics (RFC 7386). Provide key-value pairs to add or update tags. Set tag values to `null` to - remove tags. Unspecified tags remain unchanged. **Examples:** + remove tags. Unspecified tags remain unchanged. Read-only tags are always + preserved and cannot be modified. **Examples:** - **Add/update tags:** `{'tags': {'environment': 'production', 'team': 'backend'}}` adds new tags or updates existing ones. - **Delete tags:** `{'tags': {'`old_tag`': null}}` removes specific tags. + - **Remove all tags:** `{'tags': null}` removes all user-managed tags (read-only + tags are preserved). - **Partial update:** `{'tags': {'environment': 'staging'}}` only updates specified tags. - **Mixed operations:** diff --git a/src/gcore/types/cloud/registries/__init__.py b/src/gcore/types/cloud/registries/__init__.py index 9b7535c7..a4e5934f 100644 --- a/src/gcore/types/cloud/registries/__init__.py +++ b/src/gcore/types/cloud/registries/__init__.py @@ -12,3 +12,4 @@ from .registry_artifact_list import RegistryArtifactList as RegistryArtifactList from .registry_repository_list import RegistryRepositoryList as RegistryRepositoryList from .user_create_multiple_params import UserCreateMultipleParams as UserCreateMultipleParams +from .user_refresh_secret_response import UserRefreshSecretResponse as UserRefreshSecretResponse diff --git a/src/gcore/types/cloud/registries/user_refresh_secret_response.py b/src/gcore/types/cloud/registries/user_refresh_secret_response.py new file mode 100644 index 00000000..17fb1144 --- /dev/null +++ b/src/gcore/types/cloud/registries/user_refresh_secret_response.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime + +from ...._models import BaseModel + +__all__ = ["UserRefreshSecretResponse"] + + +class UserRefreshSecretResponse(BaseModel): + id: int + """User ID""" + + created_at: datetime + """User creation date-time""" + + duration: int + """User account operating time, days""" + + expires_at: datetime + """User operation end date-time""" + + name: str + """User name""" + + read_only: Optional[bool] = None + """Read-only user""" + + secret: Optional[str] = None + """User secret""" diff --git a/src/gcore/types/cloud/reserved_fixed_ip_list_params.py b/src/gcore/types/cloud/reserved_fixed_ip_list_params.py index df83a8eb..655ef02a 100644 --- a/src/gcore/types/cloud/reserved_fixed_ip_list_params.py +++ b/src/gcore/types/cloud/reserved_fixed_ip_list_params.py @@ -39,8 +39,8 @@ class ReservedFixedIPListParams(TypedDict, total=False): order_by: str """ Ordering reserved fixed IP list result by name, status, `updated_at`, - `created_at` or `fixed_ip_address` fields of the reserved fixed IP and - directions (status.asc), default is "`fixed_ip_address`.asc" + `created_at` or `fixed_ip_address` fields and directions (status.asc), default + is "`fixed_ip_address`.asc" """ vip_only: bool diff --git a/src/gcore/types/cloud/security_group_list_params.py b/src/gcore/types/cloud/security_group_list_params.py index a6a7500e..8ba036cc 100644 --- a/src/gcore/types/cloud/security_group_list_params.py +++ b/src/gcore/types/cloud/security_group_list_params.py @@ -14,17 +14,13 @@ class SecurityGroupListParams(TypedDict, total=False): region_id: int limit: int - """Limit the number of returned limit request entities.""" + """Limit the number of returned security groups""" offset: int - """Offset value is used to exclude the first set of records from the result.""" + """Offset value is used to exclude the first set of records from the result""" tag_key: List[str] """Filter by tag keys.""" tag_key_value: str - """Filter by tag key-value pairs. - - Must be a valid JSON string. curl -G --data-urlencode "`tag_key_value`={"key": - "value"}" --url "http://localhost:1111/v1/securitygroups/1/1" - """ + """Filter by tag key-value pairs. Must be a valid JSON string.""" diff --git a/src/gcore/types/cloud/security_group_update_params.py b/src/gcore/types/cloud/security_group_update_params.py index f94acd2b..a63fad11 100644 --- a/src/gcore/types/cloud/security_group_update_params.py +++ b/src/gcore/types/cloud/security_group_update_params.py @@ -25,12 +25,15 @@ class SecurityGroupUpdateParams(TypedDict, total=False): """Update key-value tags using JSON Merge Patch semantics (RFC 7386). Provide key-value pairs to add or update tags. Set tag values to `null` to - remove tags. Unspecified tags remain unchanged. **Examples:** + remove tags. Unspecified tags remain unchanged. Read-only tags are always + preserved and cannot be modified. **Examples:** - **Add/update tags:** `{'tags': {'environment': 'production', 'team': 'backend'}}` adds new tags or updates existing ones. - **Delete tags:** `{'tags': {'`old_tag`': null}}` removes specific tags. + - **Remove all tags:** `{'tags': null}` removes all user-managed tags (read-only + tags are preserved). - **Partial update:** `{'tags': {'environment': 'staging'}}` only updates specified tags. - **Mixed operations:** diff --git a/src/gcore/types/cloud/volume_list_params.py b/src/gcore/types/cloud/volume_list_params.py index 20730cc7..18573ee7 100644 --- a/src/gcore/types/cloud/volume_list_params.py +++ b/src/gcore/types/cloud/volume_list_params.py @@ -49,8 +49,4 @@ class VolumeListParams(TypedDict, total=False): """Optional. Filter by tag keys. ?`tag_key`=key1&`tag_key`=key2""" tag_key_value: str - """Optional. - - Filter by tag key-value pairs. curl -G --data-urlencode "`tag_key_value`={"key": - "value"}" --url "https://example.com/cloud/v1/resource/1/1" - """ + """Optional. Filter by tag key-value pairs.""" diff --git a/tests/api_resources/cloud/registries/test_users.py b/tests/api_resources/cloud/registries/test_users.py index 30ea6d26..e0d56803 100644 --- a/tests/api_resources/cloud/registries/test_users.py +++ b/tests/api_resources/cloud/registries/test_users.py @@ -13,6 +13,7 @@ RegistryUser, RegistryUserList, RegistryUserCreated, + UserRefreshSecretResponse, ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -30,7 +31,7 @@ def test_method_create(self, client: Gcore) -> None: duration=14, name="user1", ) - assert_matches_type(RegistryUser, user, path=["response"]) + assert_matches_type(RegistryUserCreated, user, path=["response"]) @parametrize def test_method_create_with_all_params(self, client: Gcore) -> None: @@ -43,7 +44,7 @@ def test_method_create_with_all_params(self, client: Gcore) -> None: read_only=False, secret="secret", ) - assert_matches_type(RegistryUser, user, path=["response"]) + assert_matches_type(RegistryUserCreated, user, path=["response"]) @parametrize def test_raw_response_create(self, client: Gcore) -> None: @@ -58,7 +59,7 @@ def test_raw_response_create(self, client: Gcore) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" user = response.parse() - assert_matches_type(RegistryUser, user, path=["response"]) + assert_matches_type(RegistryUserCreated, user, path=["response"]) @parametrize def test_streaming_response_create(self, client: Gcore) -> None: @@ -73,7 +74,7 @@ def test_streaming_response_create(self, client: Gcore) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" user = response.parse() - assert_matches_type(RegistryUser, user, path=["response"]) + assert_matches_type(RegistryUserCreated, user, path=["response"]) assert cast(Any, response.is_closed) is True @@ -272,7 +273,7 @@ def test_method_refresh_secret(self, client: Gcore) -> None: region_id=0, registry_id=0, ) - assert user is None + assert_matches_type(UserRefreshSecretResponse, user, path=["response"]) @parametrize def test_raw_response_refresh_secret(self, client: Gcore) -> None: @@ -286,7 +287,7 @@ def test_raw_response_refresh_secret(self, client: Gcore) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" user = response.parse() - assert user is None + assert_matches_type(UserRefreshSecretResponse, user, path=["response"]) @parametrize def test_streaming_response_refresh_secret(self, client: Gcore) -> None: @@ -300,7 +301,7 @@ def test_streaming_response_refresh_secret(self, client: Gcore) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" user = response.parse() - assert user is None + assert_matches_type(UserRefreshSecretResponse, user, path=["response"]) assert cast(Any, response.is_closed) is True @@ -317,7 +318,7 @@ async def test_method_create(self, async_client: AsyncGcore) -> None: duration=14, name="user1", ) - assert_matches_type(RegistryUser, user, path=["response"]) + assert_matches_type(RegistryUserCreated, user, path=["response"]) @parametrize async def test_method_create_with_all_params(self, async_client: AsyncGcore) -> None: @@ -330,7 +331,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncGcore) -> read_only=False, secret="secret", ) - assert_matches_type(RegistryUser, user, path=["response"]) + assert_matches_type(RegistryUserCreated, user, path=["response"]) @parametrize async def test_raw_response_create(self, async_client: AsyncGcore) -> None: @@ -345,7 +346,7 @@ async def test_raw_response_create(self, async_client: AsyncGcore) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" user = await response.parse() - assert_matches_type(RegistryUser, user, path=["response"]) + assert_matches_type(RegistryUserCreated, user, path=["response"]) @parametrize async def test_streaming_response_create(self, async_client: AsyncGcore) -> None: @@ -360,7 +361,7 @@ async def test_streaming_response_create(self, async_client: AsyncGcore) -> None assert response.http_request.headers.get("X-Stainless-Lang") == "python" user = await response.parse() - assert_matches_type(RegistryUser, user, path=["response"]) + assert_matches_type(RegistryUserCreated, user, path=["response"]) assert cast(Any, response.is_closed) is True @@ -559,7 +560,7 @@ async def test_method_refresh_secret(self, async_client: AsyncGcore) -> None: region_id=0, registry_id=0, ) - assert user is None + assert_matches_type(UserRefreshSecretResponse, user, path=["response"]) @parametrize async def test_raw_response_refresh_secret(self, async_client: AsyncGcore) -> None: @@ -573,7 +574,7 @@ async def test_raw_response_refresh_secret(self, async_client: AsyncGcore) -> No assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" user = await response.parse() - assert user is None + assert_matches_type(UserRefreshSecretResponse, user, path=["response"]) @parametrize async def test_streaming_response_refresh_secret(self, async_client: AsyncGcore) -> None: @@ -587,6 +588,6 @@ async def test_streaming_response_refresh_secret(self, async_client: AsyncGcore) assert response.http_request.headers.get("X-Stainless-Lang") == "python" user = await response.parse() - assert user is None + assert_matches_type(UserRefreshSecretResponse, user, path=["response"]) assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/cloud/test_security_groups.py b/tests/api_resources/cloud/test_security_groups.py index d8b88654..5190081d 100644 --- a/tests/api_resources/cloud/test_security_groups.py +++ b/tests/api_resources/cloud/test_security_groups.py @@ -254,7 +254,7 @@ def test_method_copy(self, client: Gcore) -> None: region_id=0, name="some_name", ) - assert security_group is None + assert_matches_type(SecurityGroup, security_group, path=["response"]) @parametrize def test_raw_response_copy(self, client: Gcore) -> None: @@ -268,7 +268,7 @@ def test_raw_response_copy(self, client: Gcore) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" security_group = response.parse() - assert security_group is None + assert_matches_type(SecurityGroup, security_group, path=["response"]) @parametrize def test_streaming_response_copy(self, client: Gcore) -> None: @@ -282,7 +282,7 @@ def test_streaming_response_copy(self, client: Gcore) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" security_group = response.parse() - assert security_group is None + assert_matches_type(SecurityGroup, security_group, path=["response"]) assert cast(Any, response.is_closed) is True @@ -626,7 +626,7 @@ async def test_method_copy(self, async_client: AsyncGcore) -> None: region_id=0, name="some_name", ) - assert security_group is None + assert_matches_type(SecurityGroup, security_group, path=["response"]) @parametrize async def test_raw_response_copy(self, async_client: AsyncGcore) -> None: @@ -640,7 +640,7 @@ async def test_raw_response_copy(self, async_client: AsyncGcore) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" security_group = await response.parse() - assert security_group is None + assert_matches_type(SecurityGroup, security_group, path=["response"]) @parametrize async def test_streaming_response_copy(self, async_client: AsyncGcore) -> None: @@ -654,7 +654,7 @@ async def test_streaming_response_copy(self, async_client: AsyncGcore) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" security_group = await response.parse() - assert security_group is None + assert_matches_type(SecurityGroup, security_group, path=["response"]) assert cast(Any, response.is_closed) is True From 8b5d094f732e0c5f236f8bfc32fe069a6fd412ed Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 20 Jun 2025 16:24:57 +0000 Subject: [PATCH 18/50] feat(api): aggregated API specs update --- .stats.yml | 4 +-- src/gcore/resources/cloud/ip_ranges.py | 36 ++++++++++++++++++++++++-- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/.stats.yml b/.stats.yml index 11475e47..0bae0f63 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 302 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-6335dff48838208c2b58720ea26dda6b46ee4e8f0cbed4a3e7839e2a85c53de4.yml -openapi_spec_hash: d9954e09f207068566e8b8f3052ac498 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-8e761e093cec076307af9cd395249c974076de7c2bb8a672f919bfc2f0264fdb.yml +openapi_spec_hash: 5be7d0cc67dba31a269661f2b620b1f5 config_hash: 851d2f0c1f925aee3b53478d4fe311ba diff --git a/src/gcore/resources/cloud/ip_ranges.py b/src/gcore/resources/cloud/ip_ranges.py index 739e19cf..12024d04 100644 --- a/src/gcore/resources/cloud/ip_ranges.py +++ b/src/gcore/resources/cloud/ip_ranges.py @@ -49,7 +49,23 @@ def list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> IPRanges: - """List of all Edge Cloud Egress Public IPs.""" + """ + Returns the complete list of IPv4 and IPv6 address ranges that Cloud uses for + outbound (egress) traffic. Typical reasons to call this endpoint: + + - Host-file delivery workflows – You upload images or other assets to the Cloud + and share a download link that points to your own infrastructure. Add these + egress prefixes to your firewall or object-storage allow-list so our clients + can fetch the files without being blocked. + - Push integrations / webhooks – You subscribe to the user-actions event log and + Cloud pushes events to your listener endpoint. Whitelisting the egress IP + ranges lets you accept only traffic that originates from us. + - General security controls, audit tooling, or SIEM rules that need to verify + that traffic truly comes from the Cloud. The list is global (covers all + regions) and refreshed automatically whenever Gcore allocates new egress IP + space. The response is an array of CIDR blocks; duplicate prefixes are not + returned. + """ return self._get( "/cloud/public/v1/ipranges/egress", options=make_request_options( @@ -89,7 +105,23 @@ async def list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> IPRanges: - """List of all Edge Cloud Egress Public IPs.""" + """ + Returns the complete list of IPv4 and IPv6 address ranges that Cloud uses for + outbound (egress) traffic. Typical reasons to call this endpoint: + + - Host-file delivery workflows – You upload images or other assets to the Cloud + and share a download link that points to your own infrastructure. Add these + egress prefixes to your firewall or object-storage allow-list so our clients + can fetch the files without being blocked. + - Push integrations / webhooks – You subscribe to the user-actions event log and + Cloud pushes events to your listener endpoint. Whitelisting the egress IP + ranges lets you accept only traffic that originates from us. + - General security controls, audit tooling, or SIEM rules that need to verify + that traffic truly comes from the Cloud. The list is global (covers all + regions) and refreshed automatically whenever Gcore allocates new egress IP + space. The response is an array of CIDR blocks; duplicate prefixes are not + returned. + """ return await self._get( "/cloud/public/v1/ipranges/egress", options=make_request_options( From a983aee8dd4ddf7d111f4798c89cfd3f341568d4 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 20 Jun 2025 23:00:07 +0000 Subject: [PATCH 19/50] feat(client): add support for aiohttp --- README.md | 34 +++++++++++++++ pyproject.toml | 2 + requirements-dev.lock | 27 ++++++++++++ requirements.lock | 27 ++++++++++++ src/gcore/__init__.py | 3 +- src/gcore/_base_client.py | 22 ++++++++++ .../cloud/baremetal/test_flavors.py | 4 +- .../cloud/baremetal/test_images.py | 4 +- .../cloud/baremetal/test_servers.py | 4 +- .../cloud/file_shares/test_access_rules.py | 4 +- .../gpu_baremetal_clusters/test_flavors.py | 4 +- .../gpu_baremetal_clusters/test_images.py | 4 +- .../gpu_baremetal_clusters/test_interfaces.py | 4 +- .../gpu_baremetal_clusters/test_servers.py | 4 +- .../cloud/inference/deployments/test_logs.py | 4 +- .../cloud/inference/test_deployments.py | 4 +- .../cloud/inference/test_flavors.py | 4 +- .../cloud/inference/test_models.py | 4 +- .../inference/test_registry_credentials.py | 4 +- .../cloud/inference/test_secrets.py | 4 +- .../cloud/instances/test_flavors.py | 4 +- .../cloud/instances/test_images.py | 4 +- .../cloud/instances/test_interfaces.py | 4 +- .../cloud/instances/test_metrics.py | 4 +- .../load_balancers/l7_policies/test_rules.py | 4 +- .../pools/test_health_monitors.py | 4 +- .../load_balancers/pools/test_members.py | 4 +- .../cloud/load_balancers/test_flavors.py | 4 +- .../cloud/load_balancers/test_l7_policies.py | 4 +- .../cloud/load_balancers/test_listeners.py | 4 +- .../cloud/load_balancers/test_metrics.py | 4 +- .../cloud/load_balancers/test_pools.py | 4 +- .../cloud/load_balancers/test_statuses.py | 4 +- .../cloud/networks/test_routers.py | 4 +- .../cloud/networks/test_subnets.py | 4 +- .../cloud/quotas/test_requests.py | 4 +- .../cloud/registries/test_artifacts.py | 4 +- .../cloud/registries/test_repositories.py | 4 +- .../cloud/registries/test_tags.py | 4 +- .../cloud/registries/test_users.py | 4 +- .../cloud/reserved_fixed_ips/test_vip.py | 4 +- .../cloud/security_groups/test_rules.py | 4 +- .../cloud/test_billing_reservations.py | 4 +- tests/api_resources/cloud/test_file_shares.py | 4 +- .../api_resources/cloud/test_floating_ips.py | 4 +- .../cloud/test_gpu_baremetal_clusters.py | 4 +- tests/api_resources/cloud/test_inference.py | 4 +- tests/api_resources/cloud/test_instances.py | 4 +- tests/api_resources/cloud/test_ip_ranges.py | 4 +- .../cloud/test_load_balancers.py | 4 +- tests/api_resources/cloud/test_networks.py | 4 +- .../cloud/test_placement_groups.py | 4 +- tests/api_resources/cloud/test_projects.py | 4 +- tests/api_resources/cloud/test_quotas.py | 4 +- tests/api_resources/cloud/test_regions.py | 4 +- tests/api_resources/cloud/test_registries.py | 4 +- .../cloud/test_reserved_fixed_ips.py | 4 +- tests/api_resources/cloud/test_secrets.py | 4 +- .../cloud/test_security_groups.py | 4 +- tests/api_resources/cloud/test_ssh_keys.py | 4 +- tests/api_resources/cloud/test_tasks.py | 4 +- tests/api_resources/cloud/test_volumes.py | 4 +- .../cloud/users/test_role_assignments.py | 4 +- tests/api_resources/test_waap.py | 4 +- .../waap/domains/analytics/test_requests.py | 4 +- .../api_discovery/test_scan_results.py | 4 +- .../waap/domains/test_advanced_rules.py | 4 +- .../waap/domains/test_analytics.py | 4 +- .../waap/domains/test_api_discovery.py | 4 +- .../waap/domains/test_api_path_groups.py | 4 +- .../waap/domains/test_api_paths.py | 4 +- .../waap/domains/test_custom_rules.py | 4 +- .../waap/domains/test_firewall_rules.py | 4 +- .../waap/domains/test_insight_silences.py | 4 +- .../waap/domains/test_insights.py | 4 +- .../waap/domains/test_policies.py | 4 +- .../waap/domains/test_settings.py | 4 +- .../api_resources/waap/test_advanced_rules.py | 4 +- .../waap/test_custom_page_sets.py | 4 +- tests/api_resources/waap/test_domains.py | 4 +- tests/api_resources/waap/test_ip_info.py | 4 +- .../api_resources/waap/test_organizations.py | 4 +- tests/api_resources/waap/test_statistics.py | 4 +- tests/api_resources/waap/test_tags.py | 4 +- tests/conftest.py | 43 ++++++++++++++++--- 85 files changed, 385 insertions(+), 85 deletions(-) diff --git a/README.md b/README.md index ed806261..b571c246 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,40 @@ asyncio.run(main()) Functionality between the synchronous and asynchronous clients is otherwise identical. +### With aiohttp + +By default, the async client uses `httpx` for HTTP requests. However, for improved concurrency performance you may also use `aiohttp` as the HTTP backend. + +You can enable this by installing `aiohttp`: + +```sh +# install from PyPI +pip install gcore[aiohttp] +``` + +Then you can enable it by instantiating the client with `http_client=DefaultAioHttpClient()`: + +```python +import os +import asyncio +from gcore import DefaultAioHttpClient +from gcore import AsyncGcore + + +async def main() -> None: + async with AsyncGcore( + api_key=os.environ.get("GCORE_API_KEY"), # This is the default and can be omitted + http_client=DefaultAioHttpClient(), + ) as client: + project = await client.cloud.projects.create( + name="New Project", + ) + print(project.id) + + +asyncio.run(main()) +``` + ## Using types Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typing.html#typing.TypedDict). Responses are [Pydantic models](https://docs.pydantic.dev) which also provide helper methods for things like: diff --git a/pyproject.toml b/pyproject.toml index 887aaeee..930acded 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,6 +37,8 @@ classifiers = [ Homepage = "https://github.com/G-Core/gcore-python" Repository = "https://github.com/G-Core/gcore-python" +[project.optional-dependencies] +aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.6"] [tool.rye] managed = true diff --git a/requirements-dev.lock b/requirements-dev.lock index 1db72836..69ce07e9 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -10,6 +10,13 @@ # universal: false -e file:. +aiohappyeyeballs==2.6.1 + # via aiohttp +aiohttp==3.12.8 + # via gcore + # via httpx-aiohttp +aiosignal==1.3.2 + # via aiohttp annotated-types==0.6.0 # via pydantic anyio==4.4.0 @@ -17,6 +24,10 @@ anyio==4.4.0 # via httpx argcomplete==3.1.2 # via nox +async-timeout==5.0.1 + # via aiohttp +attrs==25.3.0 + # via aiohttp certifi==2023.7.22 # via httpcore # via httpx @@ -34,16 +45,23 @@ execnet==2.1.1 # via pytest-xdist filelock==3.12.4 # via virtualenv +frozenlist==1.6.2 + # via aiohttp + # via aiosignal h11==0.14.0 # via httpcore httpcore==1.0.2 # via httpx httpx==0.28.1 # via gcore + # via httpx-aiohttp # via respx +httpx-aiohttp==0.1.6 + # via gcore idna==3.4 # via anyio # via httpx + # via yarl importlib-metadata==7.0.0 iniconfig==2.0.0 # via pytest @@ -51,6 +69,9 @@ markdown-it-py==3.0.0 # via rich mdurl==0.1.2 # via markdown-it-py +multidict==6.4.4 + # via aiohttp + # via yarl mypy==1.14.1 mypy-extensions==1.0.0 # via mypy @@ -65,6 +86,9 @@ platformdirs==3.11.0 # via virtualenv pluggy==1.5.0 # via pytest +propcache==0.3.1 + # via aiohttp + # via yarl pydantic==2.10.3 # via gcore pydantic-core==2.27.1 @@ -98,11 +122,14 @@ tomli==2.0.2 typing-extensions==4.12.2 # via anyio # via gcore + # via multidict # via mypy # via pydantic # via pydantic-core # via pyright virtualenv==20.24.5 # via nox +yarl==1.20.0 + # via aiohttp zipp==3.17.0 # via importlib-metadata diff --git a/requirements.lock b/requirements.lock index 97422010..b755417d 100644 --- a/requirements.lock +++ b/requirements.lock @@ -10,11 +10,22 @@ # universal: false -e file:. +aiohappyeyeballs==2.6.1 + # via aiohttp +aiohttp==3.12.8 + # via gcore + # via httpx-aiohttp +aiosignal==1.3.2 + # via aiohttp annotated-types==0.6.0 # via pydantic anyio==4.4.0 # via gcore # via httpx +async-timeout==5.0.1 + # via aiohttp +attrs==25.3.0 + # via aiohttp certifi==2023.7.22 # via httpcore # via httpx @@ -22,15 +33,28 @@ distro==1.8.0 # via gcore exceptiongroup==1.2.2 # via anyio +frozenlist==1.6.2 + # via aiohttp + # via aiosignal h11==0.14.0 # via httpcore httpcore==1.0.2 # via httpx httpx==0.28.1 # via gcore + # via httpx-aiohttp +httpx-aiohttp==0.1.6 + # via gcore idna==3.4 # via anyio # via httpx + # via yarl +multidict==6.4.4 + # via aiohttp + # via yarl +propcache==0.3.1 + # via aiohttp + # via yarl pydantic==2.10.3 # via gcore pydantic-core==2.27.1 @@ -41,5 +65,8 @@ sniffio==1.3.0 typing-extensions==4.12.2 # via anyio # via gcore + # via multidict # via pydantic # via pydantic-core +yarl==1.20.0 + # via aiohttp diff --git a/src/gcore/__init__.py b/src/gcore/__init__.py index a3a9f6eb..83786fb8 100644 --- a/src/gcore/__init__.py +++ b/src/gcore/__init__.py @@ -26,7 +26,7 @@ UnprocessableEntityError, APIResponseValidationError, ) -from ._base_client import DefaultHttpxClient, DefaultAsyncHttpxClient +from ._base_client import DefaultHttpxClient, DefaultAioHttpClient, DefaultAsyncHttpxClient from ._utils._logs import setup_logging as _setup_logging __all__ = [ @@ -68,6 +68,7 @@ "DEFAULT_CONNECTION_LIMITS", "DefaultHttpxClient", "DefaultAsyncHttpxClient", + "DefaultAioHttpClient", ] if not _t.TYPE_CHECKING: diff --git a/src/gcore/_base_client.py b/src/gcore/_base_client.py index 41e09d0e..d531e5f0 100644 --- a/src/gcore/_base_client.py +++ b/src/gcore/_base_client.py @@ -1289,6 +1289,24 @@ def __init__(self, **kwargs: Any) -> None: super().__init__(**kwargs) +try: + import httpx_aiohttp +except ImportError: + + class _DefaultAioHttpClient(httpx.AsyncClient): + def __init__(self, **_kwargs: Any) -> None: + raise RuntimeError("To use the aiohttp client you must have installed the package with the `aiohttp` extra") +else: + + class _DefaultAioHttpClient(httpx_aiohttp.HttpxAiohttpClient): # type: ignore + def __init__(self, **kwargs: Any) -> None: + kwargs.setdefault("timeout", DEFAULT_TIMEOUT) + kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS) + kwargs.setdefault("follow_redirects", True) + + super().__init__(**kwargs) + + if TYPE_CHECKING: DefaultAsyncHttpxClient = httpx.AsyncClient """An alias to `httpx.AsyncClient` that provides the same defaults that this SDK @@ -1297,8 +1315,12 @@ def __init__(self, **kwargs: Any) -> None: This is useful because overriding the `http_client` with your own instance of `httpx.AsyncClient` will result in httpx's defaults being used, not ours. """ + + DefaultAioHttpClient = httpx.AsyncClient + """An alias to `httpx.AsyncClient` that changes the default HTTP transport to `aiohttp`.""" else: DefaultAsyncHttpxClient = _DefaultAsyncHttpxClient + DefaultAioHttpClient = _DefaultAioHttpClient class AsyncHttpxClientWrapper(DefaultAsyncHttpxClient): diff --git a/tests/api_resources/cloud/baremetal/test_flavors.py b/tests/api_resources/cloud/baremetal/test_flavors.py index 322c22e2..1778e4f4 100644 --- a/tests/api_resources/cloud/baremetal/test_flavors.py +++ b/tests/api_resources/cloud/baremetal/test_flavors.py @@ -112,7 +112,9 @@ def test_streaming_response_list_suitable(self, client: Gcore) -> None: class TestAsyncFlavors: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_list(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/baremetal/test_images.py b/tests/api_resources/cloud/baremetal/test_images.py index bff7624d..235f970b 100644 --- a/tests/api_resources/cloud/baremetal/test_images.py +++ b/tests/api_resources/cloud/baremetal/test_images.py @@ -66,7 +66,9 @@ def test_streaming_response_list(self, client: Gcore) -> None: class TestAsyncImages: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_list(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/baremetal/test_servers.py b/tests/api_resources/cloud/baremetal/test_servers.py index 72ff981b..db10a5ea 100644 --- a/tests/api_resources/cloud/baremetal/test_servers.py +++ b/tests/api_resources/cloud/baremetal/test_servers.py @@ -220,7 +220,9 @@ def test_path_params_rebuild(self, client: Gcore) -> None: class TestAsyncServers: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_create(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/file_shares/test_access_rules.py b/tests/api_resources/cloud/file_shares/test_access_rules.py index 59ad1e15..e85ffcd5 100644 --- a/tests/api_resources/cloud/file_shares/test_access_rules.py +++ b/tests/api_resources/cloud/file_shares/test_access_rules.py @@ -177,7 +177,9 @@ def test_path_params_delete(self, client: Gcore) -> None: class TestAsyncAccessRules: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_create(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/gpu_baremetal_clusters/test_flavors.py b/tests/api_resources/cloud/gpu_baremetal_clusters/test_flavors.py index 05585a1a..6b7e5324 100644 --- a/tests/api_resources/cloud/gpu_baremetal_clusters/test_flavors.py +++ b/tests/api_resources/cloud/gpu_baremetal_clusters/test_flavors.py @@ -63,7 +63,9 @@ def test_streaming_response_list(self, client: Gcore) -> None: class TestAsyncFlavors: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_list(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/gpu_baremetal_clusters/test_images.py b/tests/api_resources/cloud/gpu_baremetal_clusters/test_images.py index c2b357cb..e9f6c63f 100644 --- a/tests/api_resources/cloud/gpu_baremetal_clusters/test_images.py +++ b/tests/api_resources/cloud/gpu_baremetal_clusters/test_images.py @@ -203,7 +203,9 @@ def test_streaming_response_upload(self, client: Gcore) -> None: class TestAsyncImages: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_list(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/gpu_baremetal_clusters/test_interfaces.py b/tests/api_resources/cloud/gpu_baremetal_clusters/test_interfaces.py index bc0fc507..ec77e011 100644 --- a/tests/api_resources/cloud/gpu_baremetal_clusters/test_interfaces.py +++ b/tests/api_resources/cloud/gpu_baremetal_clusters/test_interfaces.py @@ -65,7 +65,9 @@ def test_path_params_list(self, client: Gcore) -> None: class TestAsyncInterfaces: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_list(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/gpu_baremetal_clusters/test_servers.py b/tests/api_resources/cloud/gpu_baremetal_clusters/test_servers.py index 2063267b..993a16f9 100644 --- a/tests/api_resources/cloud/gpu_baremetal_clusters/test_servers.py +++ b/tests/api_resources/cloud/gpu_baremetal_clusters/test_servers.py @@ -593,7 +593,9 @@ def test_path_params_reboot(self, client: Gcore) -> None: class TestAsyncServers: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_delete(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/inference/deployments/test_logs.py b/tests/api_resources/cloud/inference/deployments/test_logs.py index b23ecfa9..19fdaf11 100644 --- a/tests/api_resources/cloud/inference/deployments/test_logs.py +++ b/tests/api_resources/cloud/inference/deployments/test_logs.py @@ -74,7 +74,9 @@ def test_path_params_list(self, client: Gcore) -> None: class TestAsyncLogs: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_list(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/inference/test_deployments.py b/tests/api_resources/cloud/inference/test_deployments.py index bbce91ba..d7b21e45 100644 --- a/tests/api_resources/cloud/inference/test_deployments.py +++ b/tests/api_resources/cloud/inference/test_deployments.py @@ -620,7 +620,9 @@ def test_path_params_stop(self, client: Gcore) -> None: class TestAsyncDeployments: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_create(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/inference/test_flavors.py b/tests/api_resources/cloud/inference/test_flavors.py index cc6433e5..2de7d73e 100644 --- a/tests/api_resources/cloud/inference/test_flavors.py +++ b/tests/api_resources/cloud/inference/test_flavors.py @@ -91,7 +91,9 @@ def test_path_params_get(self, client: Gcore) -> None: class TestAsyncFlavors: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_list(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/inference/test_models.py b/tests/api_resources/cloud/inference/test_models.py index 23048376..d0f08a50 100644 --- a/tests/api_resources/cloud/inference/test_models.py +++ b/tests/api_resources/cloud/inference/test_models.py @@ -92,7 +92,9 @@ def test_path_params_get(self, client: Gcore) -> None: class TestAsyncModels: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_list(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/inference/test_registry_credentials.py b/tests/api_resources/cloud/inference/test_registry_credentials.py index 76ddb01f..e7f4b0d5 100644 --- a/tests/api_resources/cloud/inference/test_registry_credentials.py +++ b/tests/api_resources/cloud/inference/test_registry_credentials.py @@ -244,7 +244,9 @@ def test_path_params_replace(self, client: Gcore) -> None: class TestAsyncRegistryCredentials: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_create(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/inference/test_secrets.py b/tests/api_resources/cloud/inference/test_secrets.py index 1dcff8b1..62ad7207 100644 --- a/tests/api_resources/cloud/inference/test_secrets.py +++ b/tests/api_resources/cloud/inference/test_secrets.py @@ -255,7 +255,9 @@ def test_path_params_replace(self, client: Gcore) -> None: class TestAsyncSecrets: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_create(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/instances/test_flavors.py b/tests/api_resources/cloud/instances/test_flavors.py index d9766976..8efdf80d 100644 --- a/tests/api_resources/cloud/instances/test_flavors.py +++ b/tests/api_resources/cloud/instances/test_flavors.py @@ -182,7 +182,9 @@ def test_streaming_response_list_suitable(self, client: Gcore) -> None: class TestAsyncFlavors: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_list(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/instances/test_images.py b/tests/api_resources/cloud/instances/test_images.py index 80a58a38..c30fde90 100644 --- a/tests/api_resources/cloud/instances/test_images.py +++ b/tests/api_resources/cloud/instances/test_images.py @@ -348,7 +348,9 @@ def test_streaming_response_upload(self, client: Gcore) -> None: class TestAsyncImages: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_update(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/instances/test_interfaces.py b/tests/api_resources/cloud/instances/test_interfaces.py index a3b34d4c..348e946d 100644 --- a/tests/api_resources/cloud/instances/test_interfaces.py +++ b/tests/api_resources/cloud/instances/test_interfaces.py @@ -432,7 +432,9 @@ def test_path_params_detach(self, client: Gcore) -> None: class TestAsyncInterfaces: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_list(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/instances/test_metrics.py b/tests/api_resources/cloud/instances/test_metrics.py index cf5ce302..afa61520 100644 --- a/tests/api_resources/cloud/instances/test_metrics.py +++ b/tests/api_resources/cloud/instances/test_metrics.py @@ -73,7 +73,9 @@ def test_path_params_list(self, client: Gcore) -> None: class TestAsyncMetrics: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_list(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/load_balancers/l7_policies/test_rules.py b/tests/api_resources/cloud/load_balancers/l7_policies/test_rules.py index 794e32f1..3d55c8a0 100644 --- a/tests/api_resources/cloud/load_balancers/l7_policies/test_rules.py +++ b/tests/api_resources/cloud/load_balancers/l7_policies/test_rules.py @@ -328,7 +328,9 @@ def test_path_params_replace(self, client: Gcore) -> None: class TestAsyncRules: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_create(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/load_balancers/pools/test_health_monitors.py b/tests/api_resources/cloud/load_balancers/pools/test_health_monitors.py index d5df2874..0fecec7c 100644 --- a/tests/api_resources/cloud/load_balancers/pools/test_health_monitors.py +++ b/tests/api_resources/cloud/load_balancers/pools/test_health_monitors.py @@ -144,7 +144,9 @@ def test_path_params_delete(self, client: Gcore) -> None: class TestAsyncHealthMonitors: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_create(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/load_balancers/pools/test_members.py b/tests/api_resources/cloud/load_balancers/pools/test_members.py index 3bace5b7..3b32b30c 100644 --- a/tests/api_resources/cloud/load_balancers/pools/test_members.py +++ b/tests/api_resources/cloud/load_balancers/pools/test_members.py @@ -148,7 +148,9 @@ def test_path_params_remove(self, client: Gcore) -> None: class TestAsyncMembers: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_add(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/load_balancers/test_flavors.py b/tests/api_resources/cloud/load_balancers/test_flavors.py index 33869b8d..51b2776e 100644 --- a/tests/api_resources/cloud/load_balancers/test_flavors.py +++ b/tests/api_resources/cloud/load_balancers/test_flavors.py @@ -62,7 +62,9 @@ def test_streaming_response_list(self, client: Gcore) -> None: class TestAsyncFlavors: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_list(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/load_balancers/test_l7_policies.py b/tests/api_resources/cloud/load_balancers/test_l7_policies.py index cfeb59dc..769aa5f8 100644 --- a/tests/api_resources/cloud/load_balancers/test_l7_policies.py +++ b/tests/api_resources/cloud/load_balancers/test_l7_policies.py @@ -269,7 +269,9 @@ def test_path_params_replace(self, client: Gcore) -> None: class TestAsyncL7Policies: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_create(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/load_balancers/test_listeners.py b/tests/api_resources/cloud/load_balancers/test_listeners.py index e1ccbeeb..ac026380 100644 --- a/tests/api_resources/cloud/load_balancers/test_listeners.py +++ b/tests/api_resources/cloud/load_balancers/test_listeners.py @@ -306,7 +306,9 @@ def test_path_params_get(self, client: Gcore) -> None: class TestAsyncListeners: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_create(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/load_balancers/test_metrics.py b/tests/api_resources/cloud/load_balancers/test_metrics.py index 50b10030..39bdefed 100644 --- a/tests/api_resources/cloud/load_balancers/test_metrics.py +++ b/tests/api_resources/cloud/load_balancers/test_metrics.py @@ -73,7 +73,9 @@ def test_path_params_list(self, client: Gcore) -> None: class TestAsyncMetrics: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_list(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/load_balancers/test_pools.py b/tests/api_resources/cloud/load_balancers/test_pools.py index b53c75c2..004e5dd3 100644 --- a/tests/api_resources/cloud/load_balancers/test_pools.py +++ b/tests/api_resources/cloud/load_balancers/test_pools.py @@ -348,7 +348,9 @@ def test_path_params_get(self, client: Gcore) -> None: class TestAsyncPools: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_create(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/load_balancers/test_statuses.py b/tests/api_resources/cloud/load_balancers/test_statuses.py index 413375f0..cf38797d 100644 --- a/tests/api_resources/cloud/load_balancers/test_statuses.py +++ b/tests/api_resources/cloud/load_balancers/test_statuses.py @@ -99,7 +99,9 @@ def test_path_params_get(self, client: Gcore) -> None: class TestAsyncStatuses: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_list(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/networks/test_routers.py b/tests/api_resources/cloud/networks/test_routers.py index b8414ea9..9dfe1a80 100644 --- a/tests/api_resources/cloud/networks/test_routers.py +++ b/tests/api_resources/cloud/networks/test_routers.py @@ -399,7 +399,9 @@ def test_path_params_get(self, client: Gcore) -> None: class TestAsyncRouters: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_create(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/networks/test_subnets.py b/tests/api_resources/cloud/networks/test_subnets.py index f399473c..0680b1f7 100644 --- a/tests/api_resources/cloud/networks/test_subnets.py +++ b/tests/api_resources/cloud/networks/test_subnets.py @@ -293,7 +293,9 @@ def test_path_params_get(self, client: Gcore) -> None: class TestAsyncSubnets: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_create(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/quotas/test_requests.py b/tests/api_resources/cloud/quotas/test_requests.py index ad9a144f..ccc87ba5 100644 --- a/tests/api_resources/cloud/quotas/test_requests.py +++ b/tests/api_resources/cloud/quotas/test_requests.py @@ -237,7 +237,9 @@ def test_path_params_get(self, client: Gcore) -> None: class TestAsyncRequests: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_create(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/registries/test_artifacts.py b/tests/api_resources/cloud/registries/test_artifacts.py index ab68f23d..2d602314 100644 --- a/tests/api_resources/cloud/registries/test_artifacts.py +++ b/tests/api_resources/cloud/registries/test_artifacts.py @@ -132,7 +132,9 @@ def test_path_params_delete(self, client: Gcore) -> None: class TestAsyncArtifacts: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_list(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/registries/test_repositories.py b/tests/api_resources/cloud/registries/test_repositories.py index 6cb3d0c6..897e48d7 100644 --- a/tests/api_resources/cloud/registries/test_repositories.py +++ b/tests/api_resources/cloud/registries/test_repositories.py @@ -106,7 +106,9 @@ def test_path_params_delete(self, client: Gcore) -> None: class TestAsyncRepositories: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_list(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/registries/test_tags.py b/tests/api_resources/cloud/registries/test_tags.py index 0999b260..18010d41 100644 --- a/tests/api_resources/cloud/registries/test_tags.py +++ b/tests/api_resources/cloud/registries/test_tags.py @@ -95,7 +95,9 @@ def test_path_params_delete(self, client: Gcore) -> None: class TestAsyncTags: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_delete(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/registries/test_users.py b/tests/api_resources/cloud/registries/test_users.py index e0d56803..1167b206 100644 --- a/tests/api_resources/cloud/registries/test_users.py +++ b/tests/api_resources/cloud/registries/test_users.py @@ -307,7 +307,9 @@ def test_streaming_response_refresh_secret(self, client: Gcore) -> None: class TestAsyncUsers: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_create(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/reserved_fixed_ips/test_vip.py b/tests/api_resources/cloud/reserved_fixed_ips/test_vip.py index 5970928c..65011df5 100644 --- a/tests/api_resources/cloud/reserved_fixed_ips/test_vip.py +++ b/tests/api_resources/cloud/reserved_fixed_ips/test_vip.py @@ -277,7 +277,9 @@ def test_path_params_update_connected_ports(self, client: Gcore) -> None: class TestAsyncVip: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_list_candidate_ports(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/security_groups/test_rules.py b/tests/api_resources/cloud/security_groups/test_rules.py index b6ac50fb..6f84f09a 100644 --- a/tests/api_resources/cloud/security_groups/test_rules.py +++ b/tests/api_resources/cloud/security_groups/test_rules.py @@ -200,7 +200,9 @@ def test_path_params_replace(self, client: Gcore) -> None: class TestAsyncRules: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_create(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/test_billing_reservations.py b/tests/api_resources/cloud/test_billing_reservations.py index 7463ebd1..9053b82d 100644 --- a/tests/api_resources/cloud/test_billing_reservations.py +++ b/tests/api_resources/cloud/test_billing_reservations.py @@ -95,7 +95,9 @@ def test_streaming_response_get(self, client: Gcore) -> None: class TestAsyncBillingReservations: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_list(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/test_file_shares.py b/tests/api_resources/cloud/test_file_shares.py index 62cb137a..fca472e6 100644 --- a/tests/api_resources/cloud/test_file_shares.py +++ b/tests/api_resources/cloud/test_file_shares.py @@ -389,7 +389,9 @@ def test_path_params_resize(self, client: Gcore) -> None: class TestAsyncFileShares: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_create_overload_1(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/test_floating_ips.py b/tests/api_resources/cloud/test_floating_ips.py index 52a54438..a0772131 100644 --- a/tests/api_resources/cloud/test_floating_ips.py +++ b/tests/api_resources/cloud/test_floating_ips.py @@ -314,7 +314,9 @@ def test_path_params_unassign(self, client: Gcore) -> None: class TestAsyncFloatingIPs: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_create(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/test_gpu_baremetal_clusters.py b/tests/api_resources/cloud/test_gpu_baremetal_clusters.py index bbb0f9ab..7c495e40 100644 --- a/tests/api_resources/cloud/test_gpu_baremetal_clusters.py +++ b/tests/api_resources/cloud/test_gpu_baremetal_clusters.py @@ -466,7 +466,9 @@ def test_path_params_resize(self, client: Gcore) -> None: class TestAsyncGPUBaremetalClusters: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_create(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/test_inference.py b/tests/api_resources/cloud/test_inference.py index b30c062e..b16c0068 100644 --- a/tests/api_resources/cloud/test_inference.py +++ b/tests/api_resources/cloud/test_inference.py @@ -44,7 +44,9 @@ def test_streaming_response_get_capacity_by_region(self, client: Gcore) -> None: class TestAsyncInference: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_get_capacity_by_region(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/test_instances.py b/tests/api_resources/cloud/test_instances.py index 6012d388..9dfe0883 100644 --- a/tests/api_resources/cloud/test_instances.py +++ b/tests/api_resources/cloud/test_instances.py @@ -883,7 +883,9 @@ def test_path_params_unassign_security_group(self, client: Gcore) -> None: class TestAsyncInstances: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_create(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/test_ip_ranges.py b/tests/api_resources/cloud/test_ip_ranges.py index c0545598..e7dde3b1 100644 --- a/tests/api_resources/cloud/test_ip_ranges.py +++ b/tests/api_resources/cloud/test_ip_ranges.py @@ -44,7 +44,9 @@ def test_streaming_response_list(self, client: Gcore) -> None: class TestAsyncIPRanges: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_list(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/test_load_balancers.py b/tests/api_resources/cloud/test_load_balancers.py index 42cc2367..3a61a657 100644 --- a/tests/api_resources/cloud/test_load_balancers.py +++ b/tests/api_resources/cloud/test_load_balancers.py @@ -483,7 +483,9 @@ def test_path_params_resize(self, client: Gcore) -> None: class TestAsyncLoadBalancers: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_create(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/test_networks.py b/tests/api_resources/cloud/test_networks.py index 18fd0fdf..dbd1f4d0 100644 --- a/tests/api_resources/cloud/test_networks.py +++ b/tests/api_resources/cloud/test_networks.py @@ -269,7 +269,9 @@ def test_path_params_get(self, client: Gcore) -> None: class TestAsyncNetworks: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_create(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/test_placement_groups.py b/tests/api_resources/cloud/test_placement_groups.py index a6962d8d..84783508 100644 --- a/tests/api_resources/cloud/test_placement_groups.py +++ b/tests/api_resources/cloud/test_placement_groups.py @@ -185,7 +185,9 @@ def test_path_params_get(self, client: Gcore) -> None: class TestAsyncPlacementGroups: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_create(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/test_projects.py b/tests/api_resources/cloud/test_projects.py index 9bbc7710..b01e5c29 100644 --- a/tests/api_resources/cloud/test_projects.py +++ b/tests/api_resources/cloud/test_projects.py @@ -203,7 +203,9 @@ def test_streaming_response_replace(self, client: Gcore) -> None: class TestAsyncProjects: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_create(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/test_quotas.py b/tests/api_resources/cloud/test_quotas.py index 788423b7..b027378e 100644 --- a/tests/api_resources/cloud/test_quotas.py +++ b/tests/api_resources/cloud/test_quotas.py @@ -109,7 +109,9 @@ def test_streaming_response_get_global(self, client: Gcore) -> None: class TestAsyncQuotas: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_get_all(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/test_regions.py b/tests/api_resources/cloud/test_regions.py index 0d00b673..9f31384c 100644 --- a/tests/api_resources/cloud/test_regions.py +++ b/tests/api_resources/cloud/test_regions.py @@ -95,7 +95,9 @@ def test_streaming_response_get(self, client: Gcore) -> None: class TestAsyncRegions: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_list(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/test_registries.py b/tests/api_resources/cloud/test_registries.py index 4fe48383..8d2be038 100644 --- a/tests/api_resources/cloud/test_registries.py +++ b/tests/api_resources/cloud/test_registries.py @@ -221,7 +221,9 @@ def test_streaming_response_resize(self, client: Gcore) -> None: class TestAsyncRegistries: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_create(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/test_reserved_fixed_ips.py b/tests/api_resources/cloud/test_reserved_fixed_ips.py index b4bf37d3..3f321ace 100644 --- a/tests/api_resources/cloud/test_reserved_fixed_ips.py +++ b/tests/api_resources/cloud/test_reserved_fixed_ips.py @@ -412,7 +412,9 @@ def test_path_params_get(self, client: Gcore) -> None: class TestAsyncReservedFixedIPs: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_create_overload_1(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/test_secrets.py b/tests/api_resources/cloud/test_secrets.py index f49b1f3a..61efb25b 100644 --- a/tests/api_resources/cloud/test_secrets.py +++ b/tests/api_resources/cloud/test_secrets.py @@ -301,7 +301,9 @@ def test_streaming_response_upload_tls_certificate(self, client: Gcore) -> None: class TestAsyncSecrets: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_create(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/test_security_groups.py b/tests/api_resources/cloud/test_security_groups.py index 5190081d..24ed373f 100644 --- a/tests/api_resources/cloud/test_security_groups.py +++ b/tests/api_resources/cloud/test_security_groups.py @@ -390,7 +390,9 @@ def test_path_params_revert_to_default(self, client: Gcore) -> None: class TestAsyncSecurityGroups: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_create(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/test_ssh_keys.py b/tests/api_resources/cloud/test_ssh_keys.py index 606308f7..e1049f06 100644 --- a/tests/api_resources/cloud/test_ssh_keys.py +++ b/tests/api_resources/cloud/test_ssh_keys.py @@ -235,7 +235,9 @@ def test_path_params_get(self, client: Gcore) -> None: class TestAsyncSSHKeys: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_create(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/test_tasks.py b/tests/api_resources/cloud/test_tasks.py index e28f3fde..eeb37cb3 100644 --- a/tests/api_resources/cloud/test_tasks.py +++ b/tests/api_resources/cloud/test_tasks.py @@ -172,7 +172,9 @@ def test_path_params_get(self, client: Gcore) -> None: class TestAsyncTasks: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_list(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/test_volumes.py b/tests/api_resources/cloud/test_volumes.py index 0140c029..2a263914 100644 --- a/tests/api_resources/cloud/test_volumes.py +++ b/tests/api_resources/cloud/test_volumes.py @@ -666,7 +666,9 @@ def test_path_params_revert_to_last_snapshot(self, client: Gcore) -> None: class TestAsyncVolumes: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_create_overload_1(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/users/test_role_assignments.py b/tests/api_resources/cloud/users/test_role_assignments.py index f41e6762..2c74e087 100644 --- a/tests/api_resources/cloud/users/test_role_assignments.py +++ b/tests/api_resources/cloud/users/test_role_assignments.py @@ -181,7 +181,9 @@ def test_streaming_response_delete(self, client: Gcore) -> None: class TestAsyncRoleAssignments: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_create(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/test_waap.py b/tests/api_resources/test_waap.py index 2bb0cf25..f006a612 100644 --- a/tests/api_resources/test_waap.py +++ b/tests/api_resources/test_waap.py @@ -44,7 +44,9 @@ def test_streaming_response_get_account_overview(self, client: Gcore) -> None: class TestAsyncWaap: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_get_account_overview(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/waap/domains/analytics/test_requests.py b/tests/api_resources/waap/domains/analytics/test_requests.py index 920e5527..a02cc8f8 100644 --- a/tests/api_resources/waap/domains/analytics/test_requests.py +++ b/tests/api_resources/waap/domains/analytics/test_requests.py @@ -116,7 +116,9 @@ def test_path_params_get(self, client: Gcore) -> None: class TestAsyncRequests: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_list(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/waap/domains/api_discovery/test_scan_results.py b/tests/api_resources/waap/domains/api_discovery/test_scan_results.py index 57c11b9f..3778f693 100644 --- a/tests/api_resources/waap/domains/api_discovery/test_scan_results.py +++ b/tests/api_resources/waap/domains/api_discovery/test_scan_results.py @@ -109,7 +109,9 @@ def test_path_params_get(self, client: Gcore) -> None: class TestAsyncScanResults: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_list(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/waap/domains/test_advanced_rules.py b/tests/api_resources/waap/domains/test_advanced_rules.py index 17e72abe..d4eadeeb 100644 --- a/tests/api_resources/waap/domains/test_advanced_rules.py +++ b/tests/api_resources/waap/domains/test_advanced_rules.py @@ -295,7 +295,9 @@ def test_streaming_response_toggle(self, client: Gcore) -> None: class TestAsyncAdvancedRules: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_create(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/waap/domains/test_analytics.py b/tests/api_resources/waap/domains/test_analytics.py index a1e5067d..698ecf8c 100644 --- a/tests/api_resources/waap/domains/test_analytics.py +++ b/tests/api_resources/waap/domains/test_analytics.py @@ -210,7 +210,9 @@ def test_streaming_response_list_event_traffic(self, client: Gcore) -> None: class TestAsyncAnalytics: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_get_event_statistics(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/waap/domains/test_api_discovery.py b/tests/api_resources/waap/domains/test_api_discovery.py index 0cc77d1f..9f4abcc0 100644 --- a/tests/api_resources/waap/domains/test_api_discovery.py +++ b/tests/api_resources/waap/domains/test_api_discovery.py @@ -166,7 +166,9 @@ def test_streaming_response_upload_openapi(self, client: Gcore) -> None: class TestAsyncAPIDiscovery: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_get_settings(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/waap/domains/test_api_path_groups.py b/tests/api_resources/waap/domains/test_api_path_groups.py index 39767b29..1987bf0a 100644 --- a/tests/api_resources/waap/domains/test_api_path_groups.py +++ b/tests/api_resources/waap/domains/test_api_path_groups.py @@ -50,7 +50,9 @@ def test_streaming_response_list(self, client: Gcore) -> None: class TestAsyncAPIPathGroups: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_list(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/waap/domains/test_api_paths.py b/tests/api_resources/waap/domains/test_api_paths.py index 76b67fdd..23f497f1 100644 --- a/tests/api_resources/waap/domains/test_api_paths.py +++ b/tests/api_resources/waap/domains/test_api_paths.py @@ -264,7 +264,9 @@ def test_path_params_get(self, client: Gcore) -> None: class TestAsyncAPIPaths: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_create(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/waap/domains/test_custom_rules.py b/tests/api_resources/waap/domains/test_custom_rules.py index 181f5843..fa8f46dc 100644 --- a/tests/api_resources/waap/domains/test_custom_rules.py +++ b/tests/api_resources/waap/domains/test_custom_rules.py @@ -498,7 +498,9 @@ def test_streaming_response_toggle(self, client: Gcore) -> None: class TestAsyncCustomRules: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_create(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/waap/domains/test_firewall_rules.py b/tests/api_resources/waap/domains/test_firewall_rules.py index 5aeb5fd6..0d652808 100644 --- a/tests/api_resources/waap/domains/test_firewall_rules.py +++ b/tests/api_resources/waap/domains/test_firewall_rules.py @@ -342,7 +342,9 @@ def test_streaming_response_toggle(self, client: Gcore) -> None: class TestAsyncFirewallRules: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_create(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/waap/domains/test_insight_silences.py b/tests/api_resources/waap/domains/test_insight_silences.py index 9d90ce31..5b97be86 100644 --- a/tests/api_resources/waap/domains/test_insight_silences.py +++ b/tests/api_resources/waap/domains/test_insight_silences.py @@ -271,7 +271,9 @@ def test_path_params_get(self, client: Gcore) -> None: class TestAsyncInsightSilences: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_create(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/waap/domains/test_insights.py b/tests/api_resources/waap/domains/test_insights.py index b9c262cc..1f94b7d7 100644 --- a/tests/api_resources/waap/domains/test_insights.py +++ b/tests/api_resources/waap/domains/test_insights.py @@ -153,7 +153,9 @@ def test_path_params_replace(self, client: Gcore) -> None: class TestAsyncInsights: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_list(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/waap/domains/test_policies.py b/tests/api_resources/waap/domains/test_policies.py index 8686f7f4..bb043c17 100644 --- a/tests/api_resources/waap/domains/test_policies.py +++ b/tests/api_resources/waap/domains/test_policies.py @@ -61,7 +61,9 @@ def test_path_params_toggle(self, client: Gcore) -> None: class TestAsyncPolicies: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_toggle(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/waap/domains/test_settings.py b/tests/api_resources/waap/domains/test_settings.py index 55ebebf3..158273e1 100644 --- a/tests/api_resources/waap/domains/test_settings.py +++ b/tests/api_resources/waap/domains/test_settings.py @@ -96,7 +96,9 @@ def test_streaming_response_get(self, client: Gcore) -> None: class TestAsyncSettings: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_update(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/waap/test_advanced_rules.py b/tests/api_resources/waap/test_advanced_rules.py index 14744da2..d61c91af 100644 --- a/tests/api_resources/waap/test_advanced_rules.py +++ b/tests/api_resources/waap/test_advanced_rules.py @@ -44,7 +44,9 @@ def test_streaming_response_list(self, client: Gcore) -> None: class TestAsyncAdvancedRules: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_list(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/waap/test_custom_page_sets.py b/tests/api_resources/waap/test_custom_page_sets.py index 68fda447..bb2ad1ee 100644 --- a/tests/api_resources/waap/test_custom_page_sets.py +++ b/tests/api_resources/waap/test_custom_page_sets.py @@ -319,7 +319,9 @@ def test_streaming_response_preview(self, client: Gcore) -> None: class TestAsyncCustomPageSets: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_create(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/waap/test_domains.py b/tests/api_resources/waap/test_domains.py index 196db9f7..5d380b41 100644 --- a/tests/api_resources/waap/test_domains.py +++ b/tests/api_resources/waap/test_domains.py @@ -193,7 +193,9 @@ def test_streaming_response_list_rule_sets(self, client: Gcore) -> None: class TestAsyncDomains: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_update(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/waap/test_ip_info.py b/tests/api_resources/waap/test_ip_info.py index 3d8e495d..641feb39 100644 --- a/tests/api_resources/waap/test_ip_info.py +++ b/tests/api_resources/waap/test_ip_info.py @@ -328,7 +328,9 @@ def test_streaming_response_list_attacked_countries(self, client: Gcore) -> None class TestAsyncIPInfo: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_get(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/waap/test_organizations.py b/tests/api_resources/waap/test_organizations.py index d70ad3bb..d4d2c516 100644 --- a/tests/api_resources/waap/test_organizations.py +++ b/tests/api_resources/waap/test_organizations.py @@ -55,7 +55,9 @@ def test_streaming_response_list(self, client: Gcore) -> None: class TestAsyncOrganizations: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_list(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/waap/test_statistics.py b/tests/api_resources/waap/test_statistics.py index 5eaa022b..513e819d 100644 --- a/tests/api_resources/waap/test_statistics.py +++ b/tests/api_resources/waap/test_statistics.py @@ -60,7 +60,9 @@ def test_streaming_response_get_usage_series(self, client: Gcore) -> None: class TestAsyncStatistics: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_get_usage_series(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/waap/test_tags.py b/tests/api_resources/waap/test_tags.py index 588a1b44..00753147 100644 --- a/tests/api_resources/waap/test_tags.py +++ b/tests/api_resources/waap/test_tags.py @@ -57,7 +57,9 @@ def test_streaming_response_list(self, client: Gcore) -> None: class TestAsyncTags: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @parametrize async def test_method_list(self, async_client: AsyncGcore) -> None: diff --git a/tests/conftest.py b/tests/conftest.py index 38916e21..36a0402c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -6,10 +6,12 @@ import logging from typing import TYPE_CHECKING, Iterator, AsyncIterator +import httpx import pytest from pytest_asyncio import is_async_test -from gcore import Gcore, AsyncGcore +from gcore import Gcore, AsyncGcore, DefaultAioHttpClient +from gcore._utils import is_dict if TYPE_CHECKING: from _pytest.fixtures import FixtureRequest # pyright: ignore[reportPrivateImportUsage] @@ -27,6 +29,19 @@ def pytest_collection_modifyitems(items: list[pytest.Function]) -> None: for async_test in pytest_asyncio_tests: async_test.add_marker(session_scope_marker, append=False) + # We skip tests that use both the aiohttp client and respx_mock as respx_mock + # doesn't support custom transports. + for item in items: + if "async_client" not in item.fixturenames or "respx_mock" not in item.fixturenames: + continue + + if not hasattr(item, "callspec"): + continue + + async_client_param = item.callspec.params.get("async_client") + if is_dict(async_client_param) and async_client_param.get("http_client") == "aiohttp": + item.add_marker(pytest.mark.skip(reason="aiohttp client is not compatible with respx_mock")) + base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -45,9 +60,25 @@ def client(request: FixtureRequest) -> Iterator[Gcore]: @pytest.fixture(scope="session") async def async_client(request: FixtureRequest) -> AsyncIterator[AsyncGcore]: - strict = getattr(request, "param", True) - if not isinstance(strict, bool): - raise TypeError(f"Unexpected fixture parameter type {type(strict)}, expected {bool}") - - async with AsyncGcore(base_url=base_url, api_key=api_key, _strict_response_validation=strict) as client: + param = getattr(request, "param", True) + + # defaults + strict = True + http_client: None | httpx.AsyncClient = None + + if isinstance(param, bool): + strict = param + elif is_dict(param): + strict = param.get("strict", True) + assert isinstance(strict, bool) + + http_client_type = param.get("http_client", "httpx") + if http_client_type == "aiohttp": + http_client = DefaultAioHttpClient() + else: + raise TypeError(f"Unexpected fixture parameter type {type(param)}, expected bool or dict") + + async with AsyncGcore( + base_url=base_url, api_key=api_key, _strict_response_validation=strict, http_client=http_client + ) as client: yield client From 2f728bc5aa2abd76973c7f24f6cdb0af35248475 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 23 Jun 2025 12:15:12 +0000 Subject: [PATCH 20/50] codegen metadata --- .stats.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.stats.yml b/.stats.yml index 0bae0f63..84861e08 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 302 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-8e761e093cec076307af9cd395249c974076de7c2bb8a672f919bfc2f0264fdb.yml -openapi_spec_hash: 5be7d0cc67dba31a269661f2b620b1f5 -config_hash: 851d2f0c1f925aee3b53478d4fe311ba +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-c499a9575c28f870c13772a0db8d0c62489ddf9f5f64931034c240be1eefacdc.yml +openapi_spec_hash: 90621c3e75827e66e860b01442ac94b6 +config_hash: ea790cbbc6c75b298783df8f2313d56b From 1da4aa370b1e633b002e1be99b7c5f3cf785ebc7 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 23 Jun 2025 12:36:22 +0000 Subject: [PATCH 21/50] feat(api): update via SDK Studio --- .stats.yml | 4 +-- src/gcore/resources/cloud/ip_ranges.py | 36 ++------------------------ 2 files changed, 4 insertions(+), 36 deletions(-) diff --git a/.stats.yml b/.stats.yml index 84861e08..608c1376 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 302 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-c499a9575c28f870c13772a0db8d0c62489ddf9f5f64931034c240be1eefacdc.yml -openapi_spec_hash: 90621c3e75827e66e860b01442ac94b6 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-6335dff48838208c2b58720ea26dda6b46ee4e8f0cbed4a3e7839e2a85c53de4.yml +openapi_spec_hash: d9954e09f207068566e8b8f3052ac498 config_hash: ea790cbbc6c75b298783df8f2313d56b diff --git a/src/gcore/resources/cloud/ip_ranges.py b/src/gcore/resources/cloud/ip_ranges.py index 12024d04..739e19cf 100644 --- a/src/gcore/resources/cloud/ip_ranges.py +++ b/src/gcore/resources/cloud/ip_ranges.py @@ -49,23 +49,7 @@ def list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> IPRanges: - """ - Returns the complete list of IPv4 and IPv6 address ranges that Cloud uses for - outbound (egress) traffic. Typical reasons to call this endpoint: - - - Host-file delivery workflows – You upload images or other assets to the Cloud - and share a download link that points to your own infrastructure. Add these - egress prefixes to your firewall or object-storage allow-list so our clients - can fetch the files without being blocked. - - Push integrations / webhooks – You subscribe to the user-actions event log and - Cloud pushes events to your listener endpoint. Whitelisting the egress IP - ranges lets you accept only traffic that originates from us. - - General security controls, audit tooling, or SIEM rules that need to verify - that traffic truly comes from the Cloud. The list is global (covers all - regions) and refreshed automatically whenever Gcore allocates new egress IP - space. The response is an array of CIDR blocks; duplicate prefixes are not - returned. - """ + """List of all Edge Cloud Egress Public IPs.""" return self._get( "/cloud/public/v1/ipranges/egress", options=make_request_options( @@ -105,23 +89,7 @@ async def list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> IPRanges: - """ - Returns the complete list of IPv4 and IPv6 address ranges that Cloud uses for - outbound (egress) traffic. Typical reasons to call this endpoint: - - - Host-file delivery workflows – You upload images or other assets to the Cloud - and share a download link that points to your own infrastructure. Add these - egress prefixes to your firewall or object-storage allow-list so our clients - can fetch the files without being blocked. - - Push integrations / webhooks – You subscribe to the user-actions event log and - Cloud pushes events to your listener endpoint. Whitelisting the egress IP - ranges lets you accept only traffic that originates from us. - - General security controls, audit tooling, or SIEM rules that need to verify - that traffic truly comes from the Cloud. The list is global (covers all - regions) and refreshed automatically whenever Gcore allocates new egress IP - space. The response is an array of CIDR blocks; duplicate prefixes are not - returned. - """ + """List of all Edge Cloud Egress Public IPs.""" return await self._get( "/cloud/public/v1/ipranges/egress", options=make_request_options( From a9428867b76afd4e3e0adb5892c007c3b8922fca Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 23 Jun 2025 14:16:12 +0000 Subject: [PATCH 22/50] feat(api): aggregated API specs update --- .stats.yml | 4 +- .../waap/domains/custom_rule_create_params.py | 10 ++- .../waap/domains/custom_rule_update_params.py | 10 ++- src/gcore/types/waap/waap_custom_rule.py | 10 ++- .../waap/domains/test_custom_rules.py | 64 +++++++++---------- 5 files changed, 55 insertions(+), 43 deletions(-) diff --git a/.stats.yml b/.stats.yml index 608c1376..525f59ca 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 302 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-6335dff48838208c2b58720ea26dda6b46ee4e8f0cbed4a3e7839e2a85c53de4.yml -openapi_spec_hash: d9954e09f207068566e8b8f3052ac498 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-4701f855ef372199144e38f9e834b76f3ce548793d6eb01d67fbe64662126b75.yml +openapi_spec_hash: fa885a37ceec15242fb34b5ad0d4558e config_hash: ea790cbbc6c75b298783df8f2313d56b diff --git a/src/gcore/types/waap/domains/custom_rule_create_params.py b/src/gcore/types/waap/domains/custom_rule_create_params.py index da009154..61e7618c 100644 --- a/src/gcore/types/waap/domains/custom_rule_create_params.py +++ b/src/gcore/types/waap/domains/custom_rule_create_params.py @@ -274,10 +274,14 @@ class ConditionTags(TypedDict, total=False): class ConditionURL(TypedDict, total=False): url: Required[str] - """The pattern to match against the request URL. + """ + The pattern to match against the request URL. Constraints depend on + `match_type`: - If `match_type` is `Regex` the value must be a valid regular expression that - does not use lookahead or lookbehind constructs + - **Exact/Contains**: plain text matching (e.g., `/admin`). + - **Regex**: a valid regular expression (must comply with + `^[\\ww!\\$$~:#\\[[\\]]@\\((\\))\\*\\++,=\\//\\--\\..\\%%]+$`). Lookahead/lookbehind constructs are + forbidden. """ match_type: Literal["Exact", "Contains", "Regex"] diff --git a/src/gcore/types/waap/domains/custom_rule_update_params.py b/src/gcore/types/waap/domains/custom_rule_update_params.py index 8a223135..7e5d36b0 100644 --- a/src/gcore/types/waap/domains/custom_rule_update_params.py +++ b/src/gcore/types/waap/domains/custom_rule_update_params.py @@ -277,10 +277,14 @@ class ConditionTags(TypedDict, total=False): class ConditionURL(TypedDict, total=False): url: Required[str] - """The pattern to match against the request URL. + """ + The pattern to match against the request URL. Constraints depend on + `match_type`: - If `match_type` is `Regex` the value must be a valid regular expression that - does not use lookahead or lookbehind constructs + - **Exact/Contains**: plain text matching (e.g., `/admin`). + - **Regex**: a valid regular expression (must comply with + `^[\\ww!\\$$~:#\\[[\\]]@\\((\\))\\*\\++,=\\//\\--\\..\\%%]+$`). Lookahead/lookbehind constructs are + forbidden. """ match_type: Literal["Exact", "Contains", "Regex"] diff --git a/src/gcore/types/waap/waap_custom_rule.py b/src/gcore/types/waap/waap_custom_rule.py index 20e1c445..f273efcf 100644 --- a/src/gcore/types/waap/waap_custom_rule.py +++ b/src/gcore/types/waap/waap_custom_rule.py @@ -255,10 +255,14 @@ class ConditionTags(BaseModel): class ConditionURL(BaseModel): url: str - """The pattern to match against the request URL. + """ + The pattern to match against the request URL. Constraints depend on + `match_type`: - If `match_type` is `Regex` the value must be a valid regular expression that - does not use lookahead or lookbehind constructs + - **Exact/Contains**: plain text matching (e.g., `/admin`). + - **Regex**: a valid regular expression (must comply with + `^[\\ww!\\$$~:#\\[[\\]]@\\((\\))\\*\\++,=\\//\\--\\..\\%%]+$`). Lookahead/lookbehind constructs are + forbidden. """ match_type: Optional[Literal["Exact", "Contains", "Regex"]] = None diff --git a/tests/api_resources/waap/domains/test_custom_rules.py b/tests/api_resources/waap/domains/test_custom_rules.py index fa8f46dc..ca4081bf 100644 --- a/tests/api_resources/waap/domains/test_custom_rules.py +++ b/tests/api_resources/waap/domains/test_custom_rules.py @@ -47,7 +47,7 @@ def test_method_create_with_all_params(self, client: Gcore) -> None: conditions=[ { "content_type": { - "content_type": ["string"], + "content_type": ["application/xml"], "negation": True, }, "country": { @@ -55,17 +55,17 @@ def test_method_create_with_all_params(self, client: Gcore) -> None: "negation": True, }, "file_extension": { - "file_extension": ["string"], + "file_extension": ["pdf"], "negation": True, }, "header": { - "header": "header", + "header": "Origin", "value": "value", "match_type": "Exact", "negation": True, }, "header_exists": { - "header": "header", + "header": "Origin", "negation": True, }, "http_method": { @@ -82,7 +82,7 @@ def test_method_create_with_all_params(self, client: Gcore) -> None: "negation": True, }, "organization": { - "organization": "organization", + "organization": "UptimeRobot s.r.o", "negation": True, }, "owner_types": { @@ -116,17 +116,17 @@ def test_method_create_with_all_params(self, client: Gcore) -> None: "negation": True, }, "url": { - "url": "x", + "url": "/wp-admin/", "match_type": "Exact", "negation": True, }, "user_agent": { - "user_agent": "user_agent", + "user_agent": "curl/", "match_type": "Exact", "negation": True, }, "user_defined_tags": { - "tags": ["string"], + "tags": ["SQfNklznVLBBpr"], "negation": True, }, } @@ -196,7 +196,7 @@ def test_method_update_with_all_params(self, client: Gcore) -> None: conditions=[ { "content_type": { - "content_type": ["string"], + "content_type": ["application/xml"], "negation": True, }, "country": { @@ -204,17 +204,17 @@ def test_method_update_with_all_params(self, client: Gcore) -> None: "negation": True, }, "file_extension": { - "file_extension": ["string"], + "file_extension": ["pdf"], "negation": True, }, "header": { - "header": "header", + "header": "Origin", "value": "value", "match_type": "Exact", "negation": True, }, "header_exists": { - "header": "header", + "header": "Origin", "negation": True, }, "http_method": { @@ -231,7 +231,7 @@ def test_method_update_with_all_params(self, client: Gcore) -> None: "negation": True, }, "organization": { - "organization": "organization", + "organization": "UptimeRobot s.r.o", "negation": True, }, "owner_types": { @@ -265,17 +265,17 @@ def test_method_update_with_all_params(self, client: Gcore) -> None: "negation": True, }, "url": { - "url": "x", + "url": "/wp-admin/", "match_type": "Exact", "negation": True, }, "user_agent": { - "user_agent": "user_agent", + "user_agent": "curl/", "match_type": "Exact", "negation": True, }, "user_defined_tags": { - "tags": ["string"], + "tags": ["SQfNklznVLBBpr"], "negation": True, }, } @@ -531,7 +531,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncGcore) -> conditions=[ { "content_type": { - "content_type": ["string"], + "content_type": ["application/xml"], "negation": True, }, "country": { @@ -539,17 +539,17 @@ async def test_method_create_with_all_params(self, async_client: AsyncGcore) -> "negation": True, }, "file_extension": { - "file_extension": ["string"], + "file_extension": ["pdf"], "negation": True, }, "header": { - "header": "header", + "header": "Origin", "value": "value", "match_type": "Exact", "negation": True, }, "header_exists": { - "header": "header", + "header": "Origin", "negation": True, }, "http_method": { @@ -566,7 +566,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncGcore) -> "negation": True, }, "organization": { - "organization": "organization", + "organization": "UptimeRobot s.r.o", "negation": True, }, "owner_types": { @@ -600,17 +600,17 @@ async def test_method_create_with_all_params(self, async_client: AsyncGcore) -> "negation": True, }, "url": { - "url": "x", + "url": "/wp-admin/", "match_type": "Exact", "negation": True, }, "user_agent": { - "user_agent": "user_agent", + "user_agent": "curl/", "match_type": "Exact", "negation": True, }, "user_defined_tags": { - "tags": ["string"], + "tags": ["SQfNklznVLBBpr"], "negation": True, }, } @@ -680,7 +680,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncGcore) -> conditions=[ { "content_type": { - "content_type": ["string"], + "content_type": ["application/xml"], "negation": True, }, "country": { @@ -688,17 +688,17 @@ async def test_method_update_with_all_params(self, async_client: AsyncGcore) -> "negation": True, }, "file_extension": { - "file_extension": ["string"], + "file_extension": ["pdf"], "negation": True, }, "header": { - "header": "header", + "header": "Origin", "value": "value", "match_type": "Exact", "negation": True, }, "header_exists": { - "header": "header", + "header": "Origin", "negation": True, }, "http_method": { @@ -715,7 +715,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncGcore) -> "negation": True, }, "organization": { - "organization": "organization", + "organization": "UptimeRobot s.r.o", "negation": True, }, "owner_types": { @@ -749,17 +749,17 @@ async def test_method_update_with_all_params(self, async_client: AsyncGcore) -> "negation": True, }, "url": { - "url": "x", + "url": "/wp-admin/", "match_type": "Exact", "negation": True, }, "user_agent": { - "user_agent": "user_agent", + "user_agent": "curl/", "match_type": "Exact", "negation": True, }, "user_defined_tags": { - "tags": ["string"], + "tags": ["SQfNklznVLBBpr"], "negation": True, }, } From 272ce51ed26d568e181dee26205448d35184e015 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 24 Jun 2025 04:44:22 +0000 Subject: [PATCH 23/50] chore(tests): skip some failing tests on the latest python versions --- tests/test_client.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_client.py b/tests/test_client.py index bef5cc73..53499b05 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -191,6 +191,7 @@ def test_copy_signature(self) -> None: copy_param = copy_signature.parameters.get(name) assert copy_param is not None, f"copy() signature is missing the {name} param" + @pytest.mark.skipif(sys.version_info >= (3, 10), reason="fails because of a memory leak that started from 3.12") def test_copy_build_request(self) -> None: options = FinalRequestOptions(method="get", url="/foo") @@ -1013,6 +1014,7 @@ def test_copy_signature(self) -> None: copy_param = copy_signature.parameters.get(name) assert copy_param is not None, f"copy() signature is missing the {name} param" + @pytest.mark.skipif(sys.version_info >= (3, 10), reason="fails because of a memory leak that started from 3.12") def test_copy_build_request(self) -> None: options = FinalRequestOptions(method="get", url="/foo") From 96a27dd845f5fe9128b111171feb6c065f0c2916 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 24 Jun 2025 15:30:53 +0000 Subject: [PATCH 24/50] feat(api): update via SDK Studio --- src/gcore/resources/cloud/ip_ranges.py | 36 ++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/src/gcore/resources/cloud/ip_ranges.py b/src/gcore/resources/cloud/ip_ranges.py index 739e19cf..12024d04 100644 --- a/src/gcore/resources/cloud/ip_ranges.py +++ b/src/gcore/resources/cloud/ip_ranges.py @@ -49,7 +49,23 @@ def list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> IPRanges: - """List of all Edge Cloud Egress Public IPs.""" + """ + Returns the complete list of IPv4 and IPv6 address ranges that Cloud uses for + outbound (egress) traffic. Typical reasons to call this endpoint: + + - Host-file delivery workflows – You upload images or other assets to the Cloud + and share a download link that points to your own infrastructure. Add these + egress prefixes to your firewall or object-storage allow-list so our clients + can fetch the files without being blocked. + - Push integrations / webhooks – You subscribe to the user-actions event log and + Cloud pushes events to your listener endpoint. Whitelisting the egress IP + ranges lets you accept only traffic that originates from us. + - General security controls, audit tooling, or SIEM rules that need to verify + that traffic truly comes from the Cloud. The list is global (covers all + regions) and refreshed automatically whenever Gcore allocates new egress IP + space. The response is an array of CIDR blocks; duplicate prefixes are not + returned. + """ return self._get( "/cloud/public/v1/ipranges/egress", options=make_request_options( @@ -89,7 +105,23 @@ async def list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> IPRanges: - """List of all Edge Cloud Egress Public IPs.""" + """ + Returns the complete list of IPv4 and IPv6 address ranges that Cloud uses for + outbound (egress) traffic. Typical reasons to call this endpoint: + + - Host-file delivery workflows – You upload images or other assets to the Cloud + and share a download link that points to your own infrastructure. Add these + egress prefixes to your firewall or object-storage allow-list so our clients + can fetch the files without being blocked. + - Push integrations / webhooks – You subscribe to the user-actions event log and + Cloud pushes events to your listener endpoint. Whitelisting the egress IP + ranges lets you accept only traffic that originates from us. + - General security controls, audit tooling, or SIEM rules that need to verify + that traffic truly comes from the Cloud. The list is global (covers all + regions) and refreshed automatically whenever Gcore allocates new egress IP + space. The response is an array of CIDR blocks; duplicate prefixes are not + returned. + """ return await self._get( "/cloud/public/v1/ipranges/egress", options=make_request_options( From 054c374d0b02ec85d6702ecfded1362e827cf655 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 24 Jun 2025 15:57:04 +0000 Subject: [PATCH 25/50] chore(internal): updates --- .stats.yml | 4 ++-- .../cloud/inference/test_deployments.py | 8 +++---- .../cloud/quotas/test_requests.py | 24 +++++++++---------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/.stats.yml b/.stats.yml index 525f59ca..824afbdd 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 302 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-4701f855ef372199144e38f9e834b76f3ce548793d6eb01d67fbe64662126b75.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-1cf10e154912e670b97c12bdf6887f3ee959e4d53d6987b92e0e5d980a085f29.yml openapi_spec_hash: fa885a37ceec15242fb34b5ad0d4558e -config_hash: ea790cbbc6c75b298783df8f2313d56b +config_hash: 13858022450ab96760041d6619620b32 diff --git a/tests/api_resources/cloud/inference/test_deployments.py b/tests/api_resources/cloud/inference/test_deployments.py index d7b21e45..ad310970 100644 --- a/tests/api_resources/cloud/inference/test_deployments.py +++ b/tests/api_resources/cloud/inference/test_deployments.py @@ -233,14 +233,14 @@ def test_method_update_with_all_params(self, client: Gcore) -> None: "cooldown_period": 60, "polling_interval": 30, "triggers": { - "cpu": {"threshold": 80}, + "cpu": {"threshold": 75}, "gpu_memory": {"threshold": 80}, "gpu_utilization": {"threshold": 80}, "http": { "rate": 1, "window": 60, }, - "memory": {"threshold": 70}, + "memory": {"threshold": 80}, "sqs": { "activation_queue_length": 1, "aws_region": "us-east-1", @@ -835,14 +835,14 @@ async def test_method_update_with_all_params(self, async_client: AsyncGcore) -> "cooldown_period": 60, "polling_interval": 30, "triggers": { - "cpu": {"threshold": 80}, + "cpu": {"threshold": 75}, "gpu_memory": {"threshold": 80}, "gpu_utilization": {"threshold": 80}, "http": { "rate": 1, "window": 60, }, - "memory": {"threshold": 70}, + "memory": {"threshold": 80}, "sqs": { "activation_queue_length": 1, "aws_region": "us-east-1", diff --git a/tests/api_resources/cloud/quotas/test_requests.py b/tests/api_resources/cloud/quotas/test_requests.py index ccc87ba5..e9d3aae4 100644 --- a/tests/api_resources/cloud/quotas/test_requests.py +++ b/tests/api_resources/cloud/quotas/test_requests.py @@ -162,14 +162,14 @@ def test_streaming_response_list(self, client: Gcore) -> None: @parametrize def test_method_delete(self, client: Gcore) -> None: request = client.cloud.quotas.requests.delete( - "request_id", + "3", ) assert request is None @parametrize def test_raw_response_delete(self, client: Gcore) -> None: response = client.cloud.quotas.requests.with_raw_response.delete( - "request_id", + "3", ) assert response.is_closed is True @@ -180,7 +180,7 @@ def test_raw_response_delete(self, client: Gcore) -> None: @parametrize def test_streaming_response_delete(self, client: Gcore) -> None: with client.cloud.quotas.requests.with_streaming_response.delete( - "request_id", + "3", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -200,14 +200,14 @@ def test_path_params_delete(self, client: Gcore) -> None: @parametrize def test_method_get(self, client: Gcore) -> None: request = client.cloud.quotas.requests.get( - "request_id", + "3", ) assert_matches_type(RequestGetResponse, request, path=["response"]) @parametrize def test_raw_response_get(self, client: Gcore) -> None: response = client.cloud.quotas.requests.with_raw_response.get( - "request_id", + "3", ) assert response.is_closed is True @@ -218,7 +218,7 @@ def test_raw_response_get(self, client: Gcore) -> None: @parametrize def test_streaming_response_get(self, client: Gcore) -> None: with client.cloud.quotas.requests.with_streaming_response.get( - "request_id", + "3", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -385,14 +385,14 @@ async def test_streaming_response_list(self, async_client: AsyncGcore) -> None: @parametrize async def test_method_delete(self, async_client: AsyncGcore) -> None: request = await async_client.cloud.quotas.requests.delete( - "request_id", + "3", ) assert request is None @parametrize async def test_raw_response_delete(self, async_client: AsyncGcore) -> None: response = await async_client.cloud.quotas.requests.with_raw_response.delete( - "request_id", + "3", ) assert response.is_closed is True @@ -403,7 +403,7 @@ async def test_raw_response_delete(self, async_client: AsyncGcore) -> None: @parametrize async def test_streaming_response_delete(self, async_client: AsyncGcore) -> None: async with async_client.cloud.quotas.requests.with_streaming_response.delete( - "request_id", + "3", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -423,14 +423,14 @@ async def test_path_params_delete(self, async_client: AsyncGcore) -> None: @parametrize async def test_method_get(self, async_client: AsyncGcore) -> None: request = await async_client.cloud.quotas.requests.get( - "request_id", + "3", ) assert_matches_type(RequestGetResponse, request, path=["response"]) @parametrize async def test_raw_response_get(self, async_client: AsyncGcore) -> None: response = await async_client.cloud.quotas.requests.with_raw_response.get( - "request_id", + "3", ) assert response.is_closed is True @@ -441,7 +441,7 @@ async def test_raw_response_get(self, async_client: AsyncGcore) -> None: @parametrize async def test_streaming_response_get(self, async_client: AsyncGcore) -> None: async with async_client.cloud.quotas.requests.with_streaming_response.get( - "request_id", + "3", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" From d4b4f221489c1047b8e92752a1da014f1af59af9 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 23 Jun 2025 14:10:45 +0000 Subject: [PATCH 26/50] feat(api): aggregated API specs update --- .stats.yml | 4 +- .../waap/domains/custom_rule_create_params.py | 10 ++- .../waap/domains/custom_rule_update_params.py | 10 ++- src/gcore/types/waap/waap_custom_rule.py | 10 ++- .../waap/domains/test_custom_rules.py | 64 +++++++++---------- 5 files changed, 55 insertions(+), 43 deletions(-) diff --git a/.stats.yml b/.stats.yml index 84861e08..525f59ca 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 302 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-c499a9575c28f870c13772a0db8d0c62489ddf9f5f64931034c240be1eefacdc.yml -openapi_spec_hash: 90621c3e75827e66e860b01442ac94b6 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-4701f855ef372199144e38f9e834b76f3ce548793d6eb01d67fbe64662126b75.yml +openapi_spec_hash: fa885a37ceec15242fb34b5ad0d4558e config_hash: ea790cbbc6c75b298783df8f2313d56b diff --git a/src/gcore/types/waap/domains/custom_rule_create_params.py b/src/gcore/types/waap/domains/custom_rule_create_params.py index da009154..61e7618c 100644 --- a/src/gcore/types/waap/domains/custom_rule_create_params.py +++ b/src/gcore/types/waap/domains/custom_rule_create_params.py @@ -274,10 +274,14 @@ class ConditionTags(TypedDict, total=False): class ConditionURL(TypedDict, total=False): url: Required[str] - """The pattern to match against the request URL. + """ + The pattern to match against the request URL. Constraints depend on + `match_type`: - If `match_type` is `Regex` the value must be a valid regular expression that - does not use lookahead or lookbehind constructs + - **Exact/Contains**: plain text matching (e.g., `/admin`). + - **Regex**: a valid regular expression (must comply with + `^[\\ww!\\$$~:#\\[[\\]]@\\((\\))\\*\\++,=\\//\\--\\..\\%%]+$`). Lookahead/lookbehind constructs are + forbidden. """ match_type: Literal["Exact", "Contains", "Regex"] diff --git a/src/gcore/types/waap/domains/custom_rule_update_params.py b/src/gcore/types/waap/domains/custom_rule_update_params.py index 8a223135..7e5d36b0 100644 --- a/src/gcore/types/waap/domains/custom_rule_update_params.py +++ b/src/gcore/types/waap/domains/custom_rule_update_params.py @@ -277,10 +277,14 @@ class ConditionTags(TypedDict, total=False): class ConditionURL(TypedDict, total=False): url: Required[str] - """The pattern to match against the request URL. + """ + The pattern to match against the request URL. Constraints depend on + `match_type`: - If `match_type` is `Regex` the value must be a valid regular expression that - does not use lookahead or lookbehind constructs + - **Exact/Contains**: plain text matching (e.g., `/admin`). + - **Regex**: a valid regular expression (must comply with + `^[\\ww!\\$$~:#\\[[\\]]@\\((\\))\\*\\++,=\\//\\--\\..\\%%]+$`). Lookahead/lookbehind constructs are + forbidden. """ match_type: Literal["Exact", "Contains", "Regex"] diff --git a/src/gcore/types/waap/waap_custom_rule.py b/src/gcore/types/waap/waap_custom_rule.py index 20e1c445..f273efcf 100644 --- a/src/gcore/types/waap/waap_custom_rule.py +++ b/src/gcore/types/waap/waap_custom_rule.py @@ -255,10 +255,14 @@ class ConditionTags(BaseModel): class ConditionURL(BaseModel): url: str - """The pattern to match against the request URL. + """ + The pattern to match against the request URL. Constraints depend on + `match_type`: - If `match_type` is `Regex` the value must be a valid regular expression that - does not use lookahead or lookbehind constructs + - **Exact/Contains**: plain text matching (e.g., `/admin`). + - **Regex**: a valid regular expression (must comply with + `^[\\ww!\\$$~:#\\[[\\]]@\\((\\))\\*\\++,=\\//\\--\\..\\%%]+$`). Lookahead/lookbehind constructs are + forbidden. """ match_type: Optional[Literal["Exact", "Contains", "Regex"]] = None diff --git a/tests/api_resources/waap/domains/test_custom_rules.py b/tests/api_resources/waap/domains/test_custom_rules.py index fa8f46dc..ca4081bf 100644 --- a/tests/api_resources/waap/domains/test_custom_rules.py +++ b/tests/api_resources/waap/domains/test_custom_rules.py @@ -47,7 +47,7 @@ def test_method_create_with_all_params(self, client: Gcore) -> None: conditions=[ { "content_type": { - "content_type": ["string"], + "content_type": ["application/xml"], "negation": True, }, "country": { @@ -55,17 +55,17 @@ def test_method_create_with_all_params(self, client: Gcore) -> None: "negation": True, }, "file_extension": { - "file_extension": ["string"], + "file_extension": ["pdf"], "negation": True, }, "header": { - "header": "header", + "header": "Origin", "value": "value", "match_type": "Exact", "negation": True, }, "header_exists": { - "header": "header", + "header": "Origin", "negation": True, }, "http_method": { @@ -82,7 +82,7 @@ def test_method_create_with_all_params(self, client: Gcore) -> None: "negation": True, }, "organization": { - "organization": "organization", + "organization": "UptimeRobot s.r.o", "negation": True, }, "owner_types": { @@ -116,17 +116,17 @@ def test_method_create_with_all_params(self, client: Gcore) -> None: "negation": True, }, "url": { - "url": "x", + "url": "/wp-admin/", "match_type": "Exact", "negation": True, }, "user_agent": { - "user_agent": "user_agent", + "user_agent": "curl/", "match_type": "Exact", "negation": True, }, "user_defined_tags": { - "tags": ["string"], + "tags": ["SQfNklznVLBBpr"], "negation": True, }, } @@ -196,7 +196,7 @@ def test_method_update_with_all_params(self, client: Gcore) -> None: conditions=[ { "content_type": { - "content_type": ["string"], + "content_type": ["application/xml"], "negation": True, }, "country": { @@ -204,17 +204,17 @@ def test_method_update_with_all_params(self, client: Gcore) -> None: "negation": True, }, "file_extension": { - "file_extension": ["string"], + "file_extension": ["pdf"], "negation": True, }, "header": { - "header": "header", + "header": "Origin", "value": "value", "match_type": "Exact", "negation": True, }, "header_exists": { - "header": "header", + "header": "Origin", "negation": True, }, "http_method": { @@ -231,7 +231,7 @@ def test_method_update_with_all_params(self, client: Gcore) -> None: "negation": True, }, "organization": { - "organization": "organization", + "organization": "UptimeRobot s.r.o", "negation": True, }, "owner_types": { @@ -265,17 +265,17 @@ def test_method_update_with_all_params(self, client: Gcore) -> None: "negation": True, }, "url": { - "url": "x", + "url": "/wp-admin/", "match_type": "Exact", "negation": True, }, "user_agent": { - "user_agent": "user_agent", + "user_agent": "curl/", "match_type": "Exact", "negation": True, }, "user_defined_tags": { - "tags": ["string"], + "tags": ["SQfNklznVLBBpr"], "negation": True, }, } @@ -531,7 +531,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncGcore) -> conditions=[ { "content_type": { - "content_type": ["string"], + "content_type": ["application/xml"], "negation": True, }, "country": { @@ -539,17 +539,17 @@ async def test_method_create_with_all_params(self, async_client: AsyncGcore) -> "negation": True, }, "file_extension": { - "file_extension": ["string"], + "file_extension": ["pdf"], "negation": True, }, "header": { - "header": "header", + "header": "Origin", "value": "value", "match_type": "Exact", "negation": True, }, "header_exists": { - "header": "header", + "header": "Origin", "negation": True, }, "http_method": { @@ -566,7 +566,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncGcore) -> "negation": True, }, "organization": { - "organization": "organization", + "organization": "UptimeRobot s.r.o", "negation": True, }, "owner_types": { @@ -600,17 +600,17 @@ async def test_method_create_with_all_params(self, async_client: AsyncGcore) -> "negation": True, }, "url": { - "url": "x", + "url": "/wp-admin/", "match_type": "Exact", "negation": True, }, "user_agent": { - "user_agent": "user_agent", + "user_agent": "curl/", "match_type": "Exact", "negation": True, }, "user_defined_tags": { - "tags": ["string"], + "tags": ["SQfNklznVLBBpr"], "negation": True, }, } @@ -680,7 +680,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncGcore) -> conditions=[ { "content_type": { - "content_type": ["string"], + "content_type": ["application/xml"], "negation": True, }, "country": { @@ -688,17 +688,17 @@ async def test_method_update_with_all_params(self, async_client: AsyncGcore) -> "negation": True, }, "file_extension": { - "file_extension": ["string"], + "file_extension": ["pdf"], "negation": True, }, "header": { - "header": "header", + "header": "Origin", "value": "value", "match_type": "Exact", "negation": True, }, "header_exists": { - "header": "header", + "header": "Origin", "negation": True, }, "http_method": { @@ -715,7 +715,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncGcore) -> "negation": True, }, "organization": { - "organization": "organization", + "organization": "UptimeRobot s.r.o", "negation": True, }, "owner_types": { @@ -749,17 +749,17 @@ async def test_method_update_with_all_params(self, async_client: AsyncGcore) -> "negation": True, }, "url": { - "url": "x", + "url": "/wp-admin/", "match_type": "Exact", "negation": True, }, "user_agent": { - "user_agent": "user_agent", + "user_agent": "curl/", "match_type": "Exact", "negation": True, }, "user_defined_tags": { - "tags": ["string"], + "tags": ["SQfNklznVLBBpr"], "negation": True, }, } From 4b45142d7fe138a5f0a030bdd27f9de26df533b9 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 24 Jun 2025 04:44:22 +0000 Subject: [PATCH 27/50] chore(tests): skip some failing tests on the latest python versions --- tests/test_client.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_client.py b/tests/test_client.py index bef5cc73..53499b05 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -191,6 +191,7 @@ def test_copy_signature(self) -> None: copy_param = copy_signature.parameters.get(name) assert copy_param is not None, f"copy() signature is missing the {name} param" + @pytest.mark.skipif(sys.version_info >= (3, 10), reason="fails because of a memory leak that started from 3.12") def test_copy_build_request(self) -> None: options = FinalRequestOptions(method="get", url="/foo") @@ -1013,6 +1014,7 @@ def test_copy_signature(self) -> None: copy_param = copy_signature.parameters.get(name) assert copy_param is not None, f"copy() signature is missing the {name} param" + @pytest.mark.skipif(sys.version_info >= (3, 10), reason="fails because of a memory leak that started from 3.12") def test_copy_build_request(self) -> None: options = FinalRequestOptions(method="get", url="/foo") From 237758929ed119a1780c5a0cf9d60c72f6c70b8a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 24 Jun 2025 15:57:04 +0000 Subject: [PATCH 28/50] chore(internal): updates --- .stats.yml | 4 ++-- .../cloud/inference/test_deployments.py | 8 +++---- .../cloud/quotas/test_requests.py | 24 +++++++++---------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/.stats.yml b/.stats.yml index 525f59ca..824afbdd 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 302 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-4701f855ef372199144e38f9e834b76f3ce548793d6eb01d67fbe64662126b75.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-1cf10e154912e670b97c12bdf6887f3ee959e4d53d6987b92e0e5d980a085f29.yml openapi_spec_hash: fa885a37ceec15242fb34b5ad0d4558e -config_hash: ea790cbbc6c75b298783df8f2313d56b +config_hash: 13858022450ab96760041d6619620b32 diff --git a/tests/api_resources/cloud/inference/test_deployments.py b/tests/api_resources/cloud/inference/test_deployments.py index d7b21e45..ad310970 100644 --- a/tests/api_resources/cloud/inference/test_deployments.py +++ b/tests/api_resources/cloud/inference/test_deployments.py @@ -233,14 +233,14 @@ def test_method_update_with_all_params(self, client: Gcore) -> None: "cooldown_period": 60, "polling_interval": 30, "triggers": { - "cpu": {"threshold": 80}, + "cpu": {"threshold": 75}, "gpu_memory": {"threshold": 80}, "gpu_utilization": {"threshold": 80}, "http": { "rate": 1, "window": 60, }, - "memory": {"threshold": 70}, + "memory": {"threshold": 80}, "sqs": { "activation_queue_length": 1, "aws_region": "us-east-1", @@ -835,14 +835,14 @@ async def test_method_update_with_all_params(self, async_client: AsyncGcore) -> "cooldown_period": 60, "polling_interval": 30, "triggers": { - "cpu": {"threshold": 80}, + "cpu": {"threshold": 75}, "gpu_memory": {"threshold": 80}, "gpu_utilization": {"threshold": 80}, "http": { "rate": 1, "window": 60, }, - "memory": {"threshold": 70}, + "memory": {"threshold": 80}, "sqs": { "activation_queue_length": 1, "aws_region": "us-east-1", diff --git a/tests/api_resources/cloud/quotas/test_requests.py b/tests/api_resources/cloud/quotas/test_requests.py index ccc87ba5..e9d3aae4 100644 --- a/tests/api_resources/cloud/quotas/test_requests.py +++ b/tests/api_resources/cloud/quotas/test_requests.py @@ -162,14 +162,14 @@ def test_streaming_response_list(self, client: Gcore) -> None: @parametrize def test_method_delete(self, client: Gcore) -> None: request = client.cloud.quotas.requests.delete( - "request_id", + "3", ) assert request is None @parametrize def test_raw_response_delete(self, client: Gcore) -> None: response = client.cloud.quotas.requests.with_raw_response.delete( - "request_id", + "3", ) assert response.is_closed is True @@ -180,7 +180,7 @@ def test_raw_response_delete(self, client: Gcore) -> None: @parametrize def test_streaming_response_delete(self, client: Gcore) -> None: with client.cloud.quotas.requests.with_streaming_response.delete( - "request_id", + "3", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -200,14 +200,14 @@ def test_path_params_delete(self, client: Gcore) -> None: @parametrize def test_method_get(self, client: Gcore) -> None: request = client.cloud.quotas.requests.get( - "request_id", + "3", ) assert_matches_type(RequestGetResponse, request, path=["response"]) @parametrize def test_raw_response_get(self, client: Gcore) -> None: response = client.cloud.quotas.requests.with_raw_response.get( - "request_id", + "3", ) assert response.is_closed is True @@ -218,7 +218,7 @@ def test_raw_response_get(self, client: Gcore) -> None: @parametrize def test_streaming_response_get(self, client: Gcore) -> None: with client.cloud.quotas.requests.with_streaming_response.get( - "request_id", + "3", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -385,14 +385,14 @@ async def test_streaming_response_list(self, async_client: AsyncGcore) -> None: @parametrize async def test_method_delete(self, async_client: AsyncGcore) -> None: request = await async_client.cloud.quotas.requests.delete( - "request_id", + "3", ) assert request is None @parametrize async def test_raw_response_delete(self, async_client: AsyncGcore) -> None: response = await async_client.cloud.quotas.requests.with_raw_response.delete( - "request_id", + "3", ) assert response.is_closed is True @@ -403,7 +403,7 @@ async def test_raw_response_delete(self, async_client: AsyncGcore) -> None: @parametrize async def test_streaming_response_delete(self, async_client: AsyncGcore) -> None: async with async_client.cloud.quotas.requests.with_streaming_response.delete( - "request_id", + "3", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -423,14 +423,14 @@ async def test_path_params_delete(self, async_client: AsyncGcore) -> None: @parametrize async def test_method_get(self, async_client: AsyncGcore) -> None: request = await async_client.cloud.quotas.requests.get( - "request_id", + "3", ) assert_matches_type(RequestGetResponse, request, path=["response"]) @parametrize async def test_raw_response_get(self, async_client: AsyncGcore) -> None: response = await async_client.cloud.quotas.requests.with_raw_response.get( - "request_id", + "3", ) assert response.is_closed is True @@ -441,7 +441,7 @@ async def test_raw_response_get(self, async_client: AsyncGcore) -> None: @parametrize async def test_streaming_response_get(self, async_client: AsyncGcore) -> None: async with async_client.cloud.quotas.requests.with_streaming_response.get( - "request_id", + "3", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" From dd87a630497b9dd478330bb190920da41fc6b6da Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 25 Jun 2025 08:13:18 +0000 Subject: [PATCH 29/50] feat(api): aggregated API specs update --- .stats.yml | 4 +- src/gcore/resources/cloud/volumes.py | 72 ++++++++++++++++--- src/gcore/types/cloud/volume_update_params.py | 32 ++++++++- tests/api_resources/cloud/test_volumes.py | 30 +++++--- 4 files changed, 116 insertions(+), 22 deletions(-) diff --git a/.stats.yml b/.stats.yml index 824afbdd..f22045b8 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 302 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-1cf10e154912e670b97c12bdf6887f3ee959e4d53d6987b92e0e5d980a085f29.yml -openapi_spec_hash: fa885a37ceec15242fb34b5ad0d4558e +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-381b496c557322d1e84bf8c9aadceab8af0eca97afaaab7143863736fb7dba08.yml +openapi_spec_hash: 9bba0add4c34eb48fde29469643d9aff config_hash: 13858022450ab96760041d6619620b32 diff --git a/src/gcore/resources/cloud/volumes.py b/src/gcore/resources/cloud/volumes.py index 7b86a7e9..06159e25 100644 --- a/src/gcore/resources/cloud/volumes.py +++ b/src/gcore/resources/cloud/volumes.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import List, Iterable +from typing import List, Iterable, Optional from typing_extensions import Literal, overload import httpx @@ -320,7 +320,8 @@ def update( *, project_id: int | None = None, region_id: int | None = None, - name: str, + name: str | NotGiven = NOT_GIVEN, + tags: Optional[TagUpdateMapParam] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -329,7 +330,7 @@ def update( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Volume: """ - Rename a volume. + Rename a volume or update tags Args: project_id: Project ID @@ -338,7 +339,27 @@ def update( volume_id: Volume ID - name: Name. + name: Name + + tags: Update key-value tags using JSON Merge Patch semantics (RFC 7386). Provide + key-value pairs to add or update tags. Set tag values to `null` to remove tags. + Unspecified tags remain unchanged. Read-only tags are always preserved and + cannot be modified. **Examples:** + + - **Add/update tags:** + `{'tags': {'environment': 'production', 'team': 'backend'}}` adds new tags or + updates existing ones. + - **Delete tags:** `{'tags': {'`old_tag`': null}}` removes specific tags. + - **Remove all tags:** `{'tags': null}` removes all user-managed tags (read-only + tags are preserved). + - **Partial update:** `{'tags': {'environment': 'staging'}}` only updates + specified tags. + - **Mixed operations:** + `{'tags': {'environment': 'production', '`cost_center`': 'engineering', '`deprecated_tag`': null}}` + adds/updates 'environment' and '`cost_center`' while removing + '`deprecated_tag`', preserving other existing tags. + - **Replace all:** first delete existing tags with null values, then add new + ones in the same request. extra_headers: Send extra headers @@ -356,7 +377,13 @@ def update( raise ValueError(f"Expected a non-empty value for `volume_id` but received {volume_id!r}") return self._patch( f"/cloud/v1/volumes/{project_id}/{region_id}/{volume_id}", - body=maybe_transform({"name": name}, volume_update_params.VolumeUpdateParams), + body=maybe_transform( + { + "name": name, + "tags": tags, + }, + volume_update_params.VolumeUpdateParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -1102,7 +1129,8 @@ async def update( *, project_id: int | None = None, region_id: int | None = None, - name: str, + name: str | NotGiven = NOT_GIVEN, + tags: Optional[TagUpdateMapParam] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1111,7 +1139,7 @@ async def update( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Volume: """ - Rename a volume. + Rename a volume or update tags Args: project_id: Project ID @@ -1120,7 +1148,27 @@ async def update( volume_id: Volume ID - name: Name. + name: Name + + tags: Update key-value tags using JSON Merge Patch semantics (RFC 7386). Provide + key-value pairs to add or update tags. Set tag values to `null` to remove tags. + Unspecified tags remain unchanged. Read-only tags are always preserved and + cannot be modified. **Examples:** + + - **Add/update tags:** + `{'tags': {'environment': 'production', 'team': 'backend'}}` adds new tags or + updates existing ones. + - **Delete tags:** `{'tags': {'`old_tag`': null}}` removes specific tags. + - **Remove all tags:** `{'tags': null}` removes all user-managed tags (read-only + tags are preserved). + - **Partial update:** `{'tags': {'environment': 'staging'}}` only updates + specified tags. + - **Mixed operations:** + `{'tags': {'environment': 'production', '`cost_center`': 'engineering', '`deprecated_tag`': null}}` + adds/updates 'environment' and '`cost_center`' while removing + '`deprecated_tag`', preserving other existing tags. + - **Replace all:** first delete existing tags with null values, then add new + ones in the same request. extra_headers: Send extra headers @@ -1138,7 +1186,13 @@ async def update( raise ValueError(f"Expected a non-empty value for `volume_id` but received {volume_id!r}") return await self._patch( f"/cloud/v1/volumes/{project_id}/{region_id}/{volume_id}", - body=await async_maybe_transform({"name": name}, volume_update_params.VolumeUpdateParams), + body=await async_maybe_transform( + { + "name": name, + "tags": tags, + }, + volume_update_params.VolumeUpdateParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/gcore/types/cloud/volume_update_params.py b/src/gcore/types/cloud/volume_update_params.py index 58439323..455e6210 100644 --- a/src/gcore/types/cloud/volume_update_params.py +++ b/src/gcore/types/cloud/volume_update_params.py @@ -2,7 +2,10 @@ from __future__ import annotations -from typing_extensions import Required, TypedDict +from typing import Optional +from typing_extensions import TypedDict + +from .tag_update_map_param import TagUpdateMapParam __all__ = ["VolumeUpdateParams"] @@ -14,5 +17,28 @@ class VolumeUpdateParams(TypedDict, total=False): region_id: int """Region ID""" - name: Required[str] - """Name.""" + name: str + """Name""" + + tags: Optional[TagUpdateMapParam] + """Update key-value tags using JSON Merge Patch semantics (RFC 7386). + + Provide key-value pairs to add or update tags. Set tag values to `null` to + remove tags. Unspecified tags remain unchanged. Read-only tags are always + preserved and cannot be modified. **Examples:** + + - **Add/update tags:** + `{'tags': {'environment': 'production', 'team': 'backend'}}` adds new tags or + updates existing ones. + - **Delete tags:** `{'tags': {'`old_tag`': null}}` removes specific tags. + - **Remove all tags:** `{'tags': null}` removes all user-managed tags (read-only + tags are preserved). + - **Partial update:** `{'tags': {'environment': 'staging'}}` only updates + specified tags. + - **Mixed operations:** + `{'tags': {'environment': 'production', '`cost_center`': 'engineering', '`deprecated_tag`': null}}` + adds/updates 'environment' and '`cost_center`' while removing + '`deprecated_tag`', preserving other existing tags. + - **Replace all:** first delete existing tags with null values, then add new + ones in the same request. + """ diff --git a/tests/api_resources/cloud/test_volumes.py b/tests/api_resources/cloud/test_volumes.py index 2a263914..95d43b9f 100644 --- a/tests/api_resources/cloud/test_volumes.py +++ b/tests/api_resources/cloud/test_volumes.py @@ -209,7 +209,17 @@ def test_method_update(self, client: Gcore) -> None: volume_id="726ecfcc-7fd0-4e30-a86e-7892524aa483", project_id=1, region_id=1, - name="my-resource", + ) + assert_matches_type(Volume, volume, path=["response"]) + + @parametrize + def test_method_update_with_all_params(self, client: Gcore) -> None: + volume = client.cloud.volumes.update( + volume_id="726ecfcc-7fd0-4e30-a86e-7892524aa483", + project_id=1, + region_id=1, + name="some_name", + tags={"foo": "my-tag-value"}, ) assert_matches_type(Volume, volume, path=["response"]) @@ -219,7 +229,6 @@ def test_raw_response_update(self, client: Gcore) -> None: volume_id="726ecfcc-7fd0-4e30-a86e-7892524aa483", project_id=1, region_id=1, - name="my-resource", ) assert response.is_closed is True @@ -233,7 +242,6 @@ def test_streaming_response_update(self, client: Gcore) -> None: volume_id="726ecfcc-7fd0-4e30-a86e-7892524aa483", project_id=1, region_id=1, - name="my-resource", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -250,7 +258,6 @@ def test_path_params_update(self, client: Gcore) -> None: volume_id="", project_id=1, region_id=1, - name="my-resource", ) @parametrize @@ -858,7 +865,17 @@ async def test_method_update(self, async_client: AsyncGcore) -> None: volume_id="726ecfcc-7fd0-4e30-a86e-7892524aa483", project_id=1, region_id=1, - name="my-resource", + ) + assert_matches_type(Volume, volume, path=["response"]) + + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncGcore) -> None: + volume = await async_client.cloud.volumes.update( + volume_id="726ecfcc-7fd0-4e30-a86e-7892524aa483", + project_id=1, + region_id=1, + name="some_name", + tags={"foo": "my-tag-value"}, ) assert_matches_type(Volume, volume, path=["response"]) @@ -868,7 +885,6 @@ async def test_raw_response_update(self, async_client: AsyncGcore) -> None: volume_id="726ecfcc-7fd0-4e30-a86e-7892524aa483", project_id=1, region_id=1, - name="my-resource", ) assert response.is_closed is True @@ -882,7 +898,6 @@ async def test_streaming_response_update(self, async_client: AsyncGcore) -> None volume_id="726ecfcc-7fd0-4e30-a86e-7892524aa483", project_id=1, region_id=1, - name="my-resource", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -899,7 +914,6 @@ async def test_path_params_update(self, async_client: AsyncGcore) -> None: volume_id="", project_id=1, region_id=1, - name="my-resource", ) @parametrize From 5d70914f55e7a26d4b9d3ac2bea92618a0b4c381 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 25 Jun 2025 10:10:55 +0000 Subject: [PATCH 30/50] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index f22045b8..b3da9053 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 302 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-381b496c557322d1e84bf8c9aadceab8af0eca97afaaab7143863736fb7dba08.yml -openapi_spec_hash: 9bba0add4c34eb48fde29469643d9aff +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-9bb10402e0f2bbd452c7eb2e738726c1da02e2f4c340a84d9a22b5c20adac014.yml +openapi_spec_hash: d8225066dbe62a49cad42dd4f7710794 config_hash: 13858022450ab96760041d6619620b32 From aba1f6343d40a6c6181929923b80a31d0bae332c Mon Sep 17 00:00:00 2001 From: Algis Dumbris <151737200+algis-dumbris@users.noreply.github.com> Date: Wed, 25 Jun 2025 19:20:42 +0300 Subject: [PATCH 31/50] feat(cloud): add routers examples --- examples/cloud/networks.py | 74 +++++++++++++++++++++++++++++-- examples/cloud/networks_async.py | 76 ++++++++++++++++++++++++++++++-- 2 files changed, 142 insertions(+), 8 deletions(-) diff --git a/examples/cloud/networks.py b/examples/cloud/networks.py index ba360164..0df76cb5 100644 --- a/examples/cloud/networks.py +++ b/examples/cloud/networks.py @@ -19,8 +19,17 @@ def main() -> None: list_subnets(client=gcore, network_id=network_id) get_subnet(client=gcore, subnet_id=subnet_id) update_subnet(client=gcore, subnet_id=subnet_id) - delete_subnet(client=gcore, subnet_id=subnet_id) + # Routers + router_id = create_router(client=gcore) + list_routers(client=gcore) + get_router(client=gcore, router_id=router_id) + update_router(client=gcore, router_id=router_id) + attach_subnet_to_router(client=gcore, router_id=router_id, subnet_id=subnet_id) + detach_subnet_from_router(client=gcore, router_id=router_id, subnet_id=subnet_id) + + delete_router(client=gcore, router_id=router_id) + delete_subnet(client=gcore, subnet_id=subnet_id) delete_network(client=gcore, network_id=network_id) @@ -100,6 +109,64 @@ def update_subnet(*, client: Gcore, subnet_id: str) -> None: print("========================") +def create_router(*, client: Gcore) -> str: + print("\n=== CREATE ROUTER ===") + response = client.cloud.networks.routers.create(name="gcore-go-example") + task_id = response.tasks[0] + task = client.cloud.tasks.poll(task_id=task_id) + if task.created_resources is None or task.created_resources.routers is None: + raise RuntimeError("Task completed but created_resources or routers is missing") + router_id: str = task.created_resources.routers[0] + print(f"Created router: ID={router_id}") + print("========================") + return router_id + + +def list_routers(*, client: Gcore) -> None: + print("\n=== LIST ROUTERS ===") + routers = client.cloud.networks.routers.list() + for count, router in enumerate(routers, 1): + print(f"{count}. Router: ID={router.id}, name={router.name}, status={router.status}") + print("========================") + + +def get_router(*, client: Gcore, router_id: str) -> None: + print("\n=== GET ROUTER ===") + router = client.cloud.networks.routers.get(router_id=router_id) + print(f"Router: ID={router.id}, name={router.name}, status={router.status}") + print("========================") + + +def update_router(*, client: Gcore, router_id: str) -> None: + print("\n=== UPDATE ROUTER ===") + router = client.cloud.networks.routers.update(router_id=router_id, name="gcore-go-example-updated") + print(f"Updated router: ID={router.id}, name={router.name}") + print("========================") + + +def attach_subnet_to_router(*, client: Gcore, router_id: str, subnet_id: str) -> None: + print("\n=== ATTACH SUBNET TO ROUTER ===") + router = client.cloud.networks.routers.attach_subnet(router_id=router_id, subnet_id=subnet_id) + print(f"Attached subnet {subnet_id} to router: ID={router.id}") + print("========================") + + +def detach_subnet_from_router(*, client: Gcore, router_id: str, subnet_id: str) -> None: + print("\n=== DETACH SUBNET FROM ROUTER ===") + router = client.cloud.networks.routers.detach_subnet(router_id=router_id, subnet_id=subnet_id) + print(f"Detached subnet {subnet_id} from router: ID={router.id}") + print("========================") + + +def delete_router(*, client: Gcore, router_id: str) -> None: + print("\n=== DELETE ROUTER ===") + response = client.cloud.networks.routers.delete(router_id=router_id) + task_id = response.tasks[0] + client.cloud.tasks.poll(task_id=task_id) + print(f"Deleted router: ID={router_id}") + print("========================") + + def delete_subnet(*, client: Gcore, subnet_id: str) -> None: print("\n=== DELETE SUBNET ===") client.cloud.networks.subnets.delete(subnet_id=subnet_id) @@ -110,9 +177,8 @@ def delete_subnet(*, client: Gcore, subnet_id: str) -> None: def delete_network(*, client: Gcore, network_id: str) -> None: print("\n=== DELETE NETWORK ===") response = client.cloud.networks.delete(network_id=network_id) - if response.tasks: - task_id = response.tasks[0] - client.cloud.tasks.poll(task_id=task_id) + task_id = response.tasks[0] + client.cloud.tasks.poll(task_id=task_id) print(f"Deleted network: ID={network_id}") print("========================") diff --git a/examples/cloud/networks_async.py b/examples/cloud/networks_async.py index 1eea1a5f..664d2f5b 100644 --- a/examples/cloud/networks_async.py +++ b/examples/cloud/networks_async.py @@ -21,8 +21,17 @@ async def main() -> None: await list_subnets(client=gcore, network_id=network_id) await get_subnet(client=gcore, subnet_id=subnet_id) await update_subnet(client=gcore, subnet_id=subnet_id) - await delete_subnet(client=gcore, subnet_id=subnet_id) + # Routers + router_id = await create_router(client=gcore) + await list_routers(client=gcore) + await get_router(client=gcore, router_id=router_id) + await update_router(client=gcore, router_id=router_id) + await attach_subnet_to_router(client=gcore, router_id=router_id, subnet_id=subnet_id) + await detach_subnet_from_router(client=gcore, router_id=router_id, subnet_id=subnet_id) + + await delete_router(client=gcore, router_id=router_id) + await delete_subnet(client=gcore, subnet_id=subnet_id) await delete_network(client=gcore, network_id=network_id) @@ -106,6 +115,66 @@ async def update_subnet(*, client: AsyncGcore, subnet_id: str) -> None: print("========================") +async def create_router(*, client: AsyncGcore) -> str: + print("\n=== CREATE ROUTER ===") + response = await client.cloud.networks.routers.create(name="gcore-go-example") + task_id = response.tasks[0] + task = await client.cloud.tasks.poll(task_id=task_id) + if task.created_resources is None or task.created_resources.routers is None: + raise RuntimeError("Task completed but created_resources or routers is missing") + router_id: str = task.created_resources.routers[0] + print(f"Created router: ID={router_id}") + print("========================") + return router_id + + +async def list_routers(*, client: AsyncGcore) -> None: + print("\n=== LIST ROUTERS ===") + routers = await client.cloud.networks.routers.list() + count = 0 + async for router in routers: + count += 1 + print(f"{count}. Router: ID={router.id}, name={router.name}, status={router.status}") + print("========================") + + +async def get_router(*, client: AsyncGcore, router_id: str) -> None: + print("\n=== GET ROUTER ===") + router = await client.cloud.networks.routers.get(router_id=router_id) + print(f"Router: ID={router.id}, name={router.name}, status={router.status}") + print("========================") + + +async def update_router(*, client: AsyncGcore, router_id: str) -> None: + print("\n=== UPDATE ROUTER ===") + router = await client.cloud.networks.routers.update(router_id=router_id, name="gcore-go-example-updated") + print(f"Updated router: ID={router.id}, name={router.name}") + print("========================") + + +async def attach_subnet_to_router(*, client: AsyncGcore, router_id: str, subnet_id: str) -> None: + print("\n=== ATTACH SUBNET TO ROUTER ===") + router = await client.cloud.networks.routers.attach_subnet(router_id=router_id, subnet_id=subnet_id) + print(f"Attached subnet {subnet_id} to router: ID={router.id}") + print("========================") + + +async def detach_subnet_from_router(*, client: AsyncGcore, router_id: str, subnet_id: str) -> None: + print("\n=== DETACH SUBNET FROM ROUTER ===") + router = await client.cloud.networks.routers.detach_subnet(router_id=router_id, subnet_id=subnet_id) + print(f"Detached subnet {subnet_id} from router: ID={router.id}") + print("========================") + + +async def delete_router(*, client: AsyncGcore, router_id: str) -> None: + print("\n=== DELETE ROUTER ===") + response = await client.cloud.networks.routers.delete(router_id=router_id) + task_id = response.tasks[0] + await client.cloud.tasks.poll(task_id=task_id) + print(f"Deleted router: ID={router_id}") + print("========================") + + async def delete_subnet(*, client: AsyncGcore, subnet_id: str) -> None: print("\n=== DELETE SUBNET ===") await client.cloud.networks.subnets.delete(subnet_id=subnet_id) @@ -116,9 +185,8 @@ async def delete_subnet(*, client: AsyncGcore, subnet_id: str) -> None: async def delete_network(*, client: AsyncGcore, network_id: str) -> None: print("\n=== DELETE NETWORK ===") response = await client.cloud.networks.delete(network_id=network_id) - if response.tasks: - task_id = response.tasks[0] - await client.cloud.tasks.poll(task_id=task_id) + task_id = response.tasks[0] + await client.cloud.tasks.poll(task_id=task_id) print(f"Deleted network: ID={network_id}") print("========================") From 93742610ca3186d6343947f6d8222263e0618b15 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 26 Jun 2025 12:15:30 +0000 Subject: [PATCH 32/50] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index b3da9053..a57f9830 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 302 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-9bb10402e0f2bbd452c7eb2e738726c1da02e2f4c340a84d9a22b5c20adac014.yml -openapi_spec_hash: d8225066dbe62a49cad42dd4f7710794 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-7051bc15d5829927c5221604afb0511f7a1a8f57d929917d13c08ef5c974b5f4.yml +openapi_spec_hash: e971ef590ded216d1a7c11a56ca78067 config_hash: 13858022450ab96760041d6619620b32 From a38f10024534ab8c65ff72e96f49100bfaeb17a1 Mon Sep 17 00:00:00 2001 From: Algis Dumbris <151737200+algis-dumbris@users.noreply.github.com> Date: Thu, 26 Jun 2025 15:53:10 +0300 Subject: [PATCH 33/50] feat(cloud): add instances examples --- examples/cloud/instances.py | 256 +++++++++++++++++++++++++++++ examples/cloud/instances_async.py | 263 ++++++++++++++++++++++++++++++ 2 files changed, 519 insertions(+) create mode 100644 examples/cloud/instances.py create mode 100644 examples/cloud/instances_async.py diff --git a/examples/cloud/instances.py b/examples/cloud/instances.py new file mode 100644 index 00000000..91f887a7 --- /dev/null +++ b/examples/cloud/instances.py @@ -0,0 +1,256 @@ +import os +from typing import List + +from gcore import Gcore +from gcore.types.cloud.instance import Volume, Instance +from gcore.types.cloud.network_interface import NetworkInterface +from gcore.types.cloud.instance_create_params import ( + InterfaceNewInterfaceExternalSerializerPydantic, + VolumeCreateInstanceCreateVolumeFromImageSerializer, +) +from gcore.types.cloud.instances.instance_flavor import InstanceFlavor + + +def main() -> None: + # TODO set placement group ID before running + placement_group_id = os.environ.get("GCORE_CLOUD_PLACEMENT_GROUP_ID") + + gcore = Gcore(timeout=180.0) + + instance_id = create_instance(client=gcore) + instance = get_instance(client=gcore, instance_id=instance_id) + get_console(client=gcore, instance_id=instance_id) + list_instances(client=gcore) + update_instance(client=gcore, instance_id=instance_id) + reboot_instance(client=gcore, instance_id=instance_id) + resize_instance(client=gcore, instance_id=instance_id) + + # Flavors + list_flavors(client=gcore) + + # Interfaces + interfaces = list_interfaces(client=gcore, instance_id=instance_id) + if interfaces: + ip_address = interfaces[0].ip_assignments[0].ip_address + port_id = interfaces[0].port_id + network_id = interfaces[0].network_id + detach_interface(client=gcore, instance_id=instance_id, ip_address=ip_address, port_id=port_id) + attach_interface(client=gcore, instance_id=instance_id, network_id=network_id) + + # Metrics + list_metrics(client=gcore, instance_id=instance_id) + + # Placement groups + if placement_group_id: + add_to_placement_group(client=gcore, instance_id=instance_id, placement_group_id=placement_group_id) + remove_from_placement_group(client=gcore, instance_id=instance_id) + + # Security groups + unassign_security_group(client=gcore, instance_id=instance_id) + assign_security_group(client=gcore, instance_id=instance_id) + + delete_instance(client=gcore, instance_id=instance_id, volumes=instance.volumes if instance else []) + + +def create_instance(*, client: Gcore) -> str: + print("\n=== CREATE INSTANCE ===") + image_id, vsize = _find_image(client=client) + + instance = client.cloud.instances.create_and_poll( + name_template="gcore-go-example-{ip_octets}", + flavor="g1-standard-1-2", + interfaces=[ + InterfaceNewInterfaceExternalSerializerPydantic(type="external"), + ], + volumes=[ + VolumeCreateInstanceCreateVolumeFromImageSerializer( + source="image", + image_id=image_id, + size=vsize, + boot_index=0, + ), + ], + password="Gcore123!", + ) + print(f"Created instance: ID={instance.id}, name={instance.name}, status={instance.status}") + print("========================") + return instance.id + + +def get_instance(*, client: Gcore, instance_id: str) -> Instance: + print("\n=== GET INSTANCE ===") + instance = client.cloud.instances.get(instance_id=instance_id) + print(f"Instance: ID={instance.id}, name={instance.name}, status={instance.status}") + print("========================") + return instance + + +def get_console(*, client: Gcore, instance_id: str) -> None: + print("\n=== GET CONSOLE ===") + console = client.cloud.instances.get_console(instance_id=instance_id) + print( + f"Console: protocol={console.remote_console.protocol}, type={console.remote_console.type}, url={console.remote_console.url}" + ) + print("========================") + + +def list_instances(*, client: Gcore) -> None: + print("\n=== LIST INSTANCES ===") + instances = client.cloud.instances.list() + for count, instance in enumerate(instances, 1): + print(f" {count}. Instance: ID={instance.id}, name={instance.name}, status={instance.status}") + print("========================") + + +def update_instance(*, client: Gcore, instance_id: str) -> None: + print("\n=== UPDATE INSTANCE ===") + instance = client.cloud.instances.update(instance_id=instance_id, name="gcore-go-example-updated") + print(f"Instance updated: ID={instance.id}, name changed to {instance.name}") + print("========================") + + +def reboot_instance(*, client: Gcore, instance_id: str) -> None: + print("\n=== REBOOT INSTANCE ===") + response = client.cloud.instances.action(instance_id=instance_id, action="reboot") + client.cloud.tasks.poll(task_id=response.tasks[0]) + print(f"Rebooted instance: {instance_id}") + print("========================") + + +def resize_instance(*, client: Gcore, instance_id: str) -> None: + print("\n=== RESIZE INSTANCE ===") + instance = client.cloud.instances.resize_and_poll(instance_id=instance_id, flavor_id="g1-standard-2-4") + print(f"Instance resized: ID={instance.id}, new flavor=g1-standard-2-4") + print("========================") + + +def list_flavors(*, client: Gcore) -> None: + print("\n=== LIST FLAVORS ===") + flavors = client.cloud.instances.flavors.list() + _print_flavor_details(flavors.results) + print("========================") + + +def list_interfaces(*, client: Gcore, instance_id: str) -> List[NetworkInterface]: + print("\n=== LIST INTERFACES ===") + interfaces = client.cloud.instances.interfaces.list(instance_id=instance_id) + for count, interface in enumerate(interfaces.results, 1): + print(f" {count}. Interface: PortID={interface.port_id}, NetworkID={interface.network_id}") + print("========================") + return interfaces.results + + +def list_metrics(*, client: Gcore, instance_id: str) -> None: + print("\n=== LIST METRICS ===") + metrics = client.cloud.instances.metrics.list(instance_id=instance_id, time_interval=1, time_unit="hour") + print(f"Metrics for instance: {len(metrics.results)} entries") + + # Display first few metrics + display_count = min(3, len(metrics.results)) + + for i in range(display_count): + metric = metrics.results[i] + cpu = getattr(metric, "cpu_util", "N/A") + memory = getattr(metric, "memory_util", "N/A") + timestamp = getattr(metric, "timestamp", "N/A") + + print(f" {i + 1}. Metric: CPU={cpu}%, Memory={memory}%, Time={timestamp}") + + if len(metrics.results) > display_count: + print(f" ... and {len(metrics.results) - display_count} more metrics") + print("========================") + + +def assign_security_group(*, client: Gcore, instance_id: str) -> None: + print("\n=== ASSIGN SECURITY GROUP ===") + client.cloud.instances.assign_security_group(instance_id=instance_id, name="default") + print("Assigned security group: default") + print("========================") + + +def unassign_security_group(*, client: Gcore, instance_id: str) -> None: + print("\n=== UNASSIGN SECURITY GROUP ===") + client.cloud.instances.unassign_security_group(instance_id=instance_id, name="default") + print("Unassigned security group: default") + print("========================") + + +def add_to_placement_group(*, client: Gcore, instance_id: str, placement_group_id: str) -> None: + print("\n=== ADD TO PLACEMENT GROUP ===") + client.cloud.instances.add_to_placement_group_and_poll(instance_id=instance_id, servergroup_id=placement_group_id) + print(f"Added instance {instance_id} to placement group: {placement_group_id}") + print("========================") + + +def remove_from_placement_group(*, client: Gcore, instance_id: str) -> None: + print("\n=== REMOVE FROM PLACEMENT GROUP ===") + client.cloud.instances.remove_from_placement_group_and_poll(instance_id=instance_id) + print(f"Removed instance {instance_id} from placement group") + print("========================") + + +def detach_interface(*, client: Gcore, instance_id: str, ip_address: str, port_id: str) -> None: + print("\n=== DETACH INTERFACE ===") + response = client.cloud.instances.interfaces.detach(instance_id=instance_id, ip_address=ip_address, port_id=port_id) + client.cloud.tasks.poll(task_id=response.tasks[0]) + + print(f"Detached interface (IP: {ip_address}, Port: {port_id}) from instance: {instance_id}") + print("========================") + + +def attach_interface(*, client: Gcore, instance_id: str, network_id: str) -> None: + print("\n=== ATTACH INTERFACE ===") + response = client.cloud.instances.interfaces.attach( + instance_id=instance_id, type="any_subnet", network_id=network_id + ) + client.cloud.tasks.poll(task_id=response.tasks[0]) + + print(f"Attached interface to any available subnet in network {network_id} (instance: {instance_id})") + print("========================") + + +def delete_instance(*, client: Gcore, instance_id: str, volumes: List[Volume]) -> None: + print("\n=== DELETE INSTANCE ===") + volumes_str = "" + if volumes: + volumes_str = ",".join([vol.id for vol in volumes]) + + client.cloud.instances.delete_and_poll( + instance_id=instance_id, + delete_floatings=True, + volumes=volumes_str, + ) + print(f"Deleted instance and related resources: ID={instance_id}, Volumes={volumes_str}") + print("========================") + + +def _print_flavor_details(flavors: List[InstanceFlavor]) -> None: + display_count = 3 + if len(flavors) < display_count: + display_count = len(flavors) + + for i in range(display_count): + flavor = flavors[i] + print(f" {i + 1}. Flavor: ID={flavor.flavor_id}, name={flavor.flavor_name}") + print(f" RAM: {flavor.ram} MB, VCPUs: {flavor.vcpus}") + status = "AVAILABLE" + if flavor.disabled: + status = "DISABLED" + print(f" Status: {status}") + print() + + if len(flavors) > display_count: + print(f" ... and {len(flavors) - display_count} more flavors") + + +def _find_image(*, client: Gcore) -> "tuple[str, int]": + images = client.cloud.instances.images.list() + if not images.results: + raise RuntimeError("No available images") + image = images.results[0] + vsize = getattr(image, "min_disk", 40) or 40 + return image.id, vsize + + +if __name__ == "__main__": + main() diff --git a/examples/cloud/instances_async.py b/examples/cloud/instances_async.py new file mode 100644 index 00000000..77622030 --- /dev/null +++ b/examples/cloud/instances_async.py @@ -0,0 +1,263 @@ +import os +import asyncio +from typing import List + +from gcore import AsyncGcore +from gcore.types.cloud.instance import Volume, Instance +from gcore.types.cloud.network_interface import NetworkInterface +from gcore.types.cloud.instance_create_params import ( + InterfaceNewInterfaceExternalSerializerPydantic, + VolumeCreateInstanceCreateVolumeFromImageSerializer, +) +from gcore.types.cloud.instances.instance_flavor import InstanceFlavor + + +async def main() -> None: + # TODO set placement group ID before running + placement_group_id = os.environ.get("GCORE_CLOUD_PLACEMENT_GROUP_ID") + + gcore = AsyncGcore(timeout=180.0) + + instance_id = await create_instance(client=gcore) + instance = await get_instance(client=gcore, instance_id=instance_id) + await get_console(client=gcore, instance_id=instance_id) + await list_instances(client=gcore) + await update_instance(client=gcore, instance_id=instance_id) + await reboot_instance(client=gcore, instance_id=instance_id) + await resize_instance(client=gcore, instance_id=instance_id) + + # Flavors + await list_flavors(client=gcore) + + # Interfaces + interfaces = await list_interfaces(client=gcore, instance_id=instance_id) + if interfaces: + ip_address = interfaces[0].ip_assignments[0].ip_address + port_id = interfaces[0].port_id + network_id = interfaces[0].network_id + await detach_interface(client=gcore, instance_id=instance_id, ip_address=ip_address, port_id=port_id) + await attach_interface(client=gcore, instance_id=instance_id, network_id=network_id) + + # Metrics + await list_metrics(client=gcore, instance_id=instance_id) + + # Placement groups + if placement_group_id: + await add_to_placement_group(client=gcore, instance_id=instance_id, placement_group_id=placement_group_id) + await remove_from_placement_group(client=gcore, instance_id=instance_id) + + # Security groups + await unassign_security_group(client=gcore, instance_id=instance_id) + await assign_security_group(client=gcore, instance_id=instance_id) + + await delete_instance(client=gcore, instance_id=instance_id, volumes=instance.volumes) + + +async def create_instance(*, client: AsyncGcore) -> str: + print("\n=== CREATE INSTANCE ===") + image_id, vsize = await _find_image(client=client) + + instance = await client.cloud.instances.create_and_poll( + name_template="gcore-go-example-{ip_octets}", + flavor="g1-standard-1-2", + interfaces=[ + InterfaceNewInterfaceExternalSerializerPydantic(type="external"), + ], + volumes=[ + VolumeCreateInstanceCreateVolumeFromImageSerializer( + source="image", + image_id=image_id, + size=int(vsize), + boot_index=0, + ), + ], + password="Gcore123!", + ) + print(f"Created instance: ID={instance.id}, name={instance.name}, status={instance.status}") + print("========================") + return instance.id + + +async def get_instance(*, client: AsyncGcore, instance_id: str) -> Instance: + print("\n=== GET INSTANCE ===") + instance = await client.cloud.instances.get(instance_id=instance_id) + print(f"Instance: ID={instance.id}, name={instance.name}, status={instance.status}") + print("========================") + return instance + + +async def get_console(*, client: AsyncGcore, instance_id: str) -> None: + print("\n=== GET CONSOLE ===") + console = await client.cloud.instances.get_console(instance_id=instance_id) + print( + f"Console: protocol={console.remote_console.protocol}, type={console.remote_console.type}, url={console.remote_console.url}" + ) + print("========================") + + +async def list_instances(*, client: AsyncGcore) -> None: + print("\n=== LIST INSTANCES ===") + instances = await client.cloud.instances.list() + count = 0 + async for instance in instances: + count += 1 + print(f" {count}. Instance: ID={instance.id}, name={instance.name}, status={instance.status}") + print("========================") + + +async def update_instance(*, client: AsyncGcore, instance_id: str) -> None: + print("\n=== UPDATE INSTANCE ===") + instance = await client.cloud.instances.update(instance_id=instance_id, name="gcore-go-example-updated") + print(f"Instance updated: ID={instance.id}, name changed to {instance.name}") + print("========================") + + +async def reboot_instance(*, client: AsyncGcore, instance_id: str) -> None: + print("\n=== REBOOT INSTANCE ===") + response = await client.cloud.instances.action(instance_id=instance_id, action="reboot") + await client.cloud.tasks.poll(task_id=response.tasks[0]) + print(f"Rebooted instance: {instance_id}") + print("========================") + + +async def resize_instance(*, client: AsyncGcore, instance_id: str) -> None: + print("\n=== RESIZE INSTANCE ===") + instance = await client.cloud.instances.resize_and_poll(instance_id=instance_id, flavor_id="g1-standard-2-4") + print(f"Instance resized: ID={instance.id}, new flavor=g1-standard-2-4") + print("========================") + + +async def list_flavors(*, client: AsyncGcore) -> None: + print("\n=== LIST FLAVORS ===") + flavors = await client.cloud.instances.flavors.list() + await _print_flavor_details(flavors.results) + print("========================") + + +async def list_interfaces(*, client: AsyncGcore, instance_id: str) -> List[NetworkInterface]: + print("\n=== LIST INTERFACES ===") + interfaces = await client.cloud.instances.interfaces.list(instance_id=instance_id) + for count, interface in enumerate(interfaces.results, 1): + print(f" {count}. Interface: PortID={interface.port_id}, NetworkID={interface.network_id}") + print("========================") + return interfaces.results + + +async def list_metrics(*, client: AsyncGcore, instance_id: str) -> None: + print("\n=== LIST METRICS ===") + metrics = await client.cloud.instances.metrics.list(instance_id=instance_id, time_interval=1, time_unit="hour") + print(f"Metrics for instance: {len(metrics.results)} entries") + + # Display first few metrics + display_count = min(3, len(metrics.results)) + + for i in range(display_count): + metric = metrics.results[i] + cpu = getattr(metric, "cpu_util", "N/A") + memory = getattr(metric, "memory_util", "N/A") + timestamp = getattr(metric, "timestamp", "N/A") + + print(f" {i + 1}. Metric: CPU={cpu}%, Memory={memory}%, Time={timestamp}") + + if len(metrics.results) > display_count: + print(f" ... and {len(metrics.results) - display_count} more metrics") + print("========================") + + +async def assign_security_group(*, client: AsyncGcore, instance_id: str) -> None: + print("\n=== ASSIGN SECURITY GROUP ===") + await client.cloud.instances.assign_security_group(instance_id=instance_id, name="default") + print("Assigned security group: default") + print("========================") + + +async def unassign_security_group(*, client: AsyncGcore, instance_id: str) -> None: + print("\n=== UNASSIGN SECURITY GROUP ===") + await client.cloud.instances.unassign_security_group(instance_id=instance_id, name="default") + print("Unassigned security group: default") + print("========================") + + +async def add_to_placement_group(*, client: AsyncGcore, instance_id: str, placement_group_id: str) -> None: + print("\n=== ADD TO PLACEMENT GROUP ===") + await client.cloud.instances.add_to_placement_group_and_poll( + instance_id=instance_id, servergroup_id=placement_group_id + ) + print(f"Added instance {instance_id} to placement group: {placement_group_id}") + print("========================") + + +async def remove_from_placement_group(*, client: AsyncGcore, instance_id: str) -> None: + print("\n=== REMOVE FROM PLACEMENT GROUP ===") + await client.cloud.instances.remove_from_placement_group_and_poll(instance_id=instance_id) + print(f"Removed instance {instance_id} from placement group") + print("========================") + + +async def detach_interface(*, client: AsyncGcore, instance_id: str, ip_address: str, port_id: str) -> None: + print("\n=== DETACH INTERFACE ===") + response = await client.cloud.instances.interfaces.detach( + instance_id=instance_id, ip_address=ip_address, port_id=port_id + ) + await client.cloud.tasks.poll(task_id=response.tasks[0]) + + print(f"Detached interface (IP: {ip_address}, Port: {port_id}) from instance: {instance_id}") + print("========================") + + +async def attach_interface(*, client: AsyncGcore, instance_id: str, network_id: str) -> None: + print("\n=== ATTACH INTERFACE ===") + response = await client.cloud.instances.interfaces.attach( + instance_id=instance_id, type="any_subnet", network_id=network_id + ) + await client.cloud.tasks.poll(task_id=response.tasks[0]) + + print(f"Attached interface to any available subnet in network {network_id} (instance: {instance_id})") + print("========================") + + +async def delete_instance(*, client: AsyncGcore, instance_id: str, volumes: List[Volume]) -> None: + print("\n=== DELETE INSTANCE ===") + volumes_str = "" + if volumes: + volumes_str = ",".join([vol.id for vol in volumes]) + + await client.cloud.instances.delete_and_poll( + instance_id=instance_id, + delete_floatings=True, + volumes=volumes_str, + ) + print(f"Deleted instance and related resources: ID={instance_id}, Volumes={volumes_str}") + print("========================") + + +async def _print_flavor_details(flavors: List[InstanceFlavor]) -> None: + display_count = 3 + if len(flavors) < display_count: + display_count = len(flavors) + + for i in range(display_count): + flavor = flavors[i] + print(f" {i + 1}. Flavor: ID={flavor.flavor_id}, name={flavor.flavor_name}") + print(f" RAM: {flavor.ram} MB, VCPUs: {flavor.vcpus}") + status = "AVAILABLE" + if flavor.disabled: + status = "DISABLED" + print(f" Status: {status}") + print() + + if len(flavors) > display_count: + print(f" ... and {len(flavors) - display_count} more flavors") + + +async def _find_image(*, client: AsyncGcore) -> "tuple[str, int]": + images = await client.cloud.instances.images.list() + if not images.results: + raise RuntimeError("No available images") + image = images.results[0] + vsize = getattr(image, "min_disk", 40) or 40 + return image.id, vsize + + +if __name__ == "__main__": + asyncio.run(main()) From ecc8d91c94df8c09ab4aeb5f89c966f03924caed Mon Sep 17 00:00:00 2001 From: Algis Dumbris <151737200+algis-dumbris@users.noreply.github.com> Date: Thu, 26 Jun 2025 16:25:36 +0300 Subject: [PATCH 34/50] feat(images): add instance images examples --- examples/cloud/instances.py | 111 ++++++++++++++++++++++++++---- examples/cloud/instances_async.py | 109 +++++++++++++++++++++++++---- 2 files changed, 191 insertions(+), 29 deletions(-) diff --git a/examples/cloud/instances.py b/examples/cloud/instances.py index 91f887a7..59ed8b73 100644 --- a/examples/cloud/instances.py +++ b/examples/cloud/instances.py @@ -17,7 +17,9 @@ def main() -> None: gcore = Gcore(timeout=180.0) - instance_id = create_instance(client=gcore) + uploaded_image_id = upload_image(client=gcore) + + instance_id = create_instance(client=gcore, image_id=uploaded_image_id) instance = get_instance(client=gcore, instance_id=instance_id) get_console(client=gcore, instance_id=instance_id) list_instances(client=gcore) @@ -28,6 +30,14 @@ def main() -> None: # Flavors list_flavors(client=gcore) + # Images + list_images(client=gcore) + get_image(client=gcore, image_id=uploaded_image_id) + update_image(client=gcore, image_id=uploaded_image_id) + delete_image(client=gcore, image_id=uploaded_image_id) + # volume_image_id = create_image_from_volume(client=gcore, volume_id=instance.volumes[0].id) + # delete_image(client=gcore, image_id=volume_image_id) + # Interfaces interfaces = list_interfaces(client=gcore, instance_id=instance_id) if interfaces: @@ -49,28 +59,30 @@ def main() -> None: unassign_security_group(client=gcore, instance_id=instance_id) assign_security_group(client=gcore, instance_id=instance_id) - delete_instance(client=gcore, instance_id=instance_id, volumes=instance.volumes if instance else []) + delete_instance(client=gcore, instance_id=instance_id, volumes=instance.volumes) -def create_instance(*, client: Gcore) -> str: +def create_instance(*, client: Gcore, image_id: str) -> str: print("\n=== CREATE INSTANCE ===") - image_id, vsize = _find_image(client=client) instance = client.cloud.instances.create_and_poll( - name_template="gcore-go-example-{ip_octets}", + name="gcore-go-example-instance", flavor="g1-standard-1-2", interfaces=[ InterfaceNewInterfaceExternalSerializerPydantic(type="external"), ], volumes=[ VolumeCreateInstanceCreateVolumeFromImageSerializer( + name="gcore-go-example-volume", + size=10, + type_name="standard", source="image", image_id=image_id, - size=vsize, boot_index=0, ), ], password="Gcore123!", + tags={"name": "gcore-go-example"}, ) print(f"Created instance: ID={instance.id}, name={instance.name}, status={instance.status}") print("========================") @@ -224,6 +236,84 @@ def delete_instance(*, client: Gcore, instance_id: str, volumes: List[Volume]) - print("========================") +def upload_image(*, client: Gcore) -> str: + print("\n=== UPLOAD IMAGE ===") + + image = client.cloud.instances.images.upload_and_poll( + name="gcore-go-example-uploaded", + url="https://cloud-images.ubuntu.com/releases/24.04/release/ubuntu-24.04-server-cloudimg-amd64.img", + os_type="linux", + architecture="x86_64", + ssh_key="allow", + os_distro="Ubuntu", + os_version="24.04", + ) + + print( + f"Uploaded image: ID={image.id}, name={image.name}, OS type={image.os_type}, arch={image.architecture}, status={image.status}, size={image.size}" + ) + print("========================") + return image.id + + +def create_image_from_volume(*, client: Gcore, volume_id: str) -> str: + print("\n=== CREATE IMAGE FROM VOLUME ===") + + image = client.cloud.instances.images.create_from_volume_and_poll( + volume_id=volume_id, name="gcore-go-example", os_type="linux" + ) + + print(f"Created image ID: {image.id}") + print("========================") + return image.id + + +def list_images(*, client: Gcore) -> None: + print("\n=== LIST ALL IMAGES ===") + + images = client.cloud.instances.images.list() + + display_count = 3 + if len(images.results) < display_count: + display_count = len(images.results) + + for i in range(display_count): + img = images.results[i] + print(f" {i + 1}. Image ID: {img.id}, name: {img.name}, OS type: {img.os_type}, status: {img.status}") + + if len(images.results) > display_count: + print(f" ... and {len(images.results) - display_count} more images") + + print("========================") + + +def get_image(*, client: Gcore, image_id: str) -> None: + print("\n=== GET IMAGE BY ID ===") + + image = client.cloud.instances.images.get(image_id=image_id) + + print(f"Image ID: {image.id}, name: {image.name}, OS type: {image.os_type}, status: {image.status}") + print("========================") + + +def update_image(*, client: Gcore, image_id: str) -> None: + print("\n=== UPDATE IMAGE ===") + + updated_image = client.cloud.instances.images.update(image_id=image_id, name="gcore-go-example-updated") + + print(f"Updated image ID: {updated_image.id}, name: {updated_image.name}") + print("========================") + + +def delete_image(*, client: Gcore, image_id: str) -> None: + print("\n=== DELETE IMAGE ===") + + client.cloud.instances.images.delete_and_poll(image_id=image_id) + + print(f"Image with ID {image_id} successfully deleted") + print("========================") + + def _print_flavor_details(flavors: List[InstanceFlavor]) -> None: display_count = 3 if len(flavors) < display_count: @@ -243,14 +333,5 @@ def _print_flavor_details(flavors: List[InstanceFlavor]) -> None: print(f" ... and {len(flavors) - display_count} more flavors") -def _find_image(*, client: Gcore) -> "tuple[str, int]": - images = client.cloud.instances.images.list() - if not images.results: - raise RuntimeError("No available images") - image = images.results[0] - vsize = getattr(image, "min_disk", 40) or 40 - return image.id, vsize - - if __name__ == "__main__": main() diff --git a/examples/cloud/instances_async.py b/examples/cloud/instances_async.py index 77622030..e3ab6d9b 100644 --- a/examples/cloud/instances_async.py +++ b/examples/cloud/instances_async.py @@ -18,7 +18,9 @@ async def main() -> None: gcore = AsyncGcore(timeout=180.0) - instance_id = await create_instance(client=gcore) + uploaded_image_id = await upload_image(client=gcore) + + instance_id = await create_instance(client=gcore, image_id=uploaded_image_id) instance = await get_instance(client=gcore, instance_id=instance_id) await get_console(client=gcore, instance_id=instance_id) await list_instances(client=gcore) @@ -29,6 +31,14 @@ async def main() -> None: # Flavors await list_flavors(client=gcore) + # Images + await list_images(client=gcore) + await get_image(client=gcore, image_id=uploaded_image_id) + await update_image(client=gcore, image_id=uploaded_image_id) + await delete_image(client=gcore, image_id=uploaded_image_id) + # volume_image_id = await create_image_from_volume(client=gcore, volume_id=instance.volumes[0].id) + # await delete_image(client=gcore, image_id=volume_image_id) + # Interfaces interfaces = await list_interfaces(client=gcore, instance_id=instance_id) if interfaces: @@ -53,25 +63,27 @@ async def main() -> None: await delete_instance(client=gcore, instance_id=instance_id, volumes=instance.volumes) -async def create_instance(*, client: AsyncGcore) -> str: +async def create_instance(*, client: AsyncGcore, image_id: str) -> str: print("\n=== CREATE INSTANCE ===") - image_id, vsize = await _find_image(client=client) instance = await client.cloud.instances.create_and_poll( - name_template="gcore-go-example-{ip_octets}", + name="gcore-go-example-instance", flavor="g1-standard-1-2", interfaces=[ InterfaceNewInterfaceExternalSerializerPydantic(type="external"), ], volumes=[ VolumeCreateInstanceCreateVolumeFromImageSerializer( + name="gcore-go-example-volume", + size=10, + type_name="standard", source="image", image_id=image_id, - size=int(vsize), boot_index=0, ), ], password="Gcore123!", + tags={"name": "gcore-go-example"}, ) print(f"Created instance: ID={instance.id}, name={instance.name}, status={instance.status}") print("========================") @@ -231,6 +243,84 @@ async def delete_instance(*, client: AsyncGcore, instance_id: str, volumes: List print("========================") +async def upload_image(*, client: AsyncGcore) -> str: + print("\n=== UPLOAD IMAGE ===") + + image = await client.cloud.instances.images.upload_and_poll( + name="gcore-go-example-uploaded", + url="https://cloud-images.ubuntu.com/releases/24.04/release/ubuntu-24.04-server-cloudimg-amd64.img", + os_type="linux", + architecture="x86_64", + ssh_key="allow", + os_distro="Ubuntu", + os_version="24.04", + ) + + print( + f"Uploaded image: ID={image.id}, name={image.name}, OS type={image.os_type}, arch={image.architecture}, status={image.status}, size={image.size}" + ) + print("========================") + return image.id + + +async def create_image_from_volume(*, client: AsyncGcore, volume_id: str) -> str: + print("\n=== CREATE IMAGE FROM VOLUME ===") + + image = await client.cloud.instances.images.create_from_volume_and_poll( + volume_id=volume_id, name="gcore-go-example", os_type="linux" + ) + + print(f"Created image ID: {image.id}") + print("========================") + return image.id + + +async def list_images(*, client: AsyncGcore) -> None: + print("\n=== LIST ALL IMAGES ===") + + images = await client.cloud.instances.images.list() + + display_count = 3 + if len(images.results) < display_count: + display_count = len(images.results) + + for i in range(display_count): + img = images.results[i] + print(f" {i + 1}. Image ID: {img.id}, name: {img.name}, OS type: {img.os_type}, status: {img.status}") + + if len(images.results) > display_count: + print(f" ... and {len(images.results) - display_count} more images") + + print("========================") + + +async def get_image(*, client: AsyncGcore, image_id: str) -> None: + print("\n=== GET IMAGE BY ID ===") + + image = await client.cloud.instances.images.get(image_id=image_id) + + print(f"Image ID: {image.id}, name: {image.name}, OS type: {image.os_type}, status: {image.status}") + print("========================") + + +async def update_image(*, client: AsyncGcore, image_id: str) -> None: + print("\n=== UPDATE IMAGE ===") + + updated_image = await client.cloud.instances.images.update(image_id=image_id, name="gcore-go-example-updated") + + print(f"Updated image ID: {updated_image.id}, name: {updated_image.name}") + print("========================") + + +async def delete_image(*, client: AsyncGcore, image_id: str) -> None: + print("\n=== DELETE IMAGE ===") + + await client.cloud.instances.images.delete_and_poll(image_id=image_id) + + print(f"Image with ID {image_id} successfully deleted") + print("========================") + + async def _print_flavor_details(flavors: List[InstanceFlavor]) -> None: display_count = 3 if len(flavors) < display_count: @@ -250,14 +340,5 @@ async def _print_flavor_details(flavors: List[InstanceFlavor]) -> None: print(f" ... and {len(flavors) - display_count} more flavors") -async def _find_image(*, client: AsyncGcore) -> "tuple[str, int]": - images = await client.cloud.instances.images.list() - if not images.results: - raise RuntimeError("No available images") - image = images.results[0] - vsize = getattr(image, "min_disk", 40) or 40 - return image.id, vsize - - if __name__ == "__main__": asyncio.run(main()) From 0bf3b1850e85aa8d9b635dcee9aeaf416f35df6c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 27 Jun 2025 02:43:22 +0000 Subject: [PATCH 35/50] =?UTF-8?q?fix(ci):=20release-doctor=20=E2=80=94=20r?= =?UTF-8?q?eport=20correct=20token=20name?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bin/check-release-environment | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/check-release-environment b/bin/check-release-environment index 4df2a0a0..b845b0f4 100644 --- a/bin/check-release-environment +++ b/bin/check-release-environment @@ -3,7 +3,7 @@ errors=() if [ -z "${PYPI_TOKEN}" ]; then - errors+=("The GCORE_PYPI_TOKEN secret has not been set. Please set it in either this repository's secrets or your organization secrets.") + errors+=("The PYPI_TOKEN secret has not been set. Please set it in either this repository's secrets or your organization secrets.") fi lenErrors=${#errors[@]} From 7df32eca9f58a777487a2d00e2bfd41ccdd6a518 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 28 Jun 2025 08:48:01 +0000 Subject: [PATCH 36/50] chore(ci): only run for pushes and fork pull requests --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f6054ec3..7eb50a15 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,6 +17,7 @@ jobs: timeout-minutes: 10 name: lint runs-on: ${{ github.repository == 'stainless-sdks/gcore-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + if: github.event_name == 'push' || github.event.pull_request.head.repo.fork steps: - uses: actions/checkout@v4 @@ -42,6 +43,7 @@ jobs: contents: read id-token: write runs-on: depot-ubuntu-24.04 + if: github.event_name == 'push' || github.event.pull_request.head.repo.fork steps: - uses: actions/checkout@v4 @@ -62,6 +64,7 @@ jobs: timeout-minutes: 10 name: test runs-on: ${{ github.repository == 'stainless-sdks/gcore-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + if: github.event_name == 'push' || github.event.pull_request.head.repo.fork steps: - uses: actions/checkout@v4 From b8d8b9275a3c0d0864cfbd0d396a68cfd07c0b7b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 30 Jun 2025 02:38:28 +0000 Subject: [PATCH 37/50] fix(ci): correct conditional --- .github/workflows/ci.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7eb50a15..b642078b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,14 +36,13 @@ jobs: run: ./scripts/lint upload: - if: github.repository == 'stainless-sdks/gcore-python' + if: github.repository == 'stainless-sdks/gcore-python' && (github.event_name == 'push' || github.event.pull_request.head.repo.fork) timeout-minutes: 10 name: upload permissions: contents: read id-token: write runs-on: depot-ubuntu-24.04 - if: github.event_name == 'push' || github.event.pull_request.head.repo.fork steps: - uses: actions/checkout@v4 From 656485a2a433aac60eac0193b58d24534c10cccb Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 30 Jun 2025 10:11:12 +0000 Subject: [PATCH 38/50] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index a57f9830..af340d1a 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 302 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-7051bc15d5829927c5221604afb0511f7a1a8f57d929917d13c08ef5c974b5f4.yml -openapi_spec_hash: e971ef590ded216d1a7c11a56ca78067 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-739daccdbf486da0806ddb7c892d037d991e743a6a536ffe0ebb1ff6ac6cd817.yml +openapi_spec_hash: 04456598c665817e9bb233d70b118f07 config_hash: 13858022450ab96760041d6619620b32 From 0441c52c6407e2ae427b70fdd22881bde8ea8191 Mon Sep 17 00:00:00 2001 From: Danil Krox Date: Mon, 30 Jun 2025 17:18:05 +0200 Subject: [PATCH 39/50] chore(cloud): reorder --- examples/cloud/reserved_fixed_ips.py | 50 +++++++++++----------- examples/cloud/reserved_fixed_ips_async.py | 5 --- 2 files changed, 25 insertions(+), 30 deletions(-) diff --git a/examples/cloud/reserved_fixed_ips.py b/examples/cloud/reserved_fixed_ips.py index d6691d66..7be747a5 100644 --- a/examples/cloud/reserved_fixed_ips.py +++ b/examples/cloud/reserved_fixed_ips.py @@ -3,6 +3,31 @@ from gcore.types.cloud import ReservedFixedIP +def main() -> None: + # No need to pass the API key explicitly — it will automatically be read from the GCORE_API_KEY environment variable if omitted + # api_key = os.environ.get("GCORE_API_KEY") + # Will use Production API URL if omitted + # base_url = os.environ.get("GCORE_BASE_URL") + + gcore = Gcore( + # api_key=api_key, + # base_url=base_url, + ) + + fixed_ip = create_reserved_fixed_ip(client=gcore) + list_reserved_fixed_ips(client=gcore) + get_reserved_fixed_ip(client=gcore, port_id=fixed_ip.port_id) + + # VIP + toggle_reserved_fixed_ip_vip(client=gcore, port_id=fixed_ip.port_id, is_vip=True) + list_candidate_ports(client=gcore, port_id=fixed_ip.port_id) + list_connected_ports(client=gcore, port_id=fixed_ip.port_id) + # is_vip needs to be false to delete the reserved fixed IP + toggle_reserved_fixed_ip_vip(client=gcore, port_id=fixed_ip.port_id, is_vip=False) + + delete_reserved_fixed_ip(client=gcore, port_id=fixed_ip.port_id) + + def create_reserved_fixed_ip(*, client: Gcore) -> ReservedFixedIP: print("\n=== CREATE RESERVED FIXED IP ===") task_list = client.cloud.reserved_fixed_ips.create( @@ -70,30 +95,5 @@ def delete_reserved_fixed_ip(*, client: Gcore, port_id: str) -> None: print("========================") -def main() -> None: - # No need to pass the API key explicitly — it will automatically be read from the GCORE_API_KEY environment variable if omitted - # api_key = os.environ.get("GCORE_API_KEY") - # Will use Production API URL if omitted - # base_url = os.environ.get("GCORE_BASE_URL") - - gcore = Gcore( - # api_key=api_key, - # base_url=base_url, - ) - - fixed_ip = create_reserved_fixed_ip(client=gcore) - list_reserved_fixed_ips(client=gcore) - get_reserved_fixed_ip(client=gcore, port_id=fixed_ip.port_id) - - # VIP - toggle_reserved_fixed_ip_vip(client=gcore, port_id=fixed_ip.port_id, is_vip=True) - list_candidate_ports(client=gcore, port_id=fixed_ip.port_id) - list_connected_ports(client=gcore, port_id=fixed_ip.port_id) - # is_vip needs to be false to delete the reserved fixed IP - toggle_reserved_fixed_ip_vip(client=gcore, port_id=fixed_ip.port_id, is_vip=False) - - delete_reserved_fixed_ip(client=gcore, port_id=fixed_ip.port_id) - - if __name__ == "__main__": main() diff --git a/examples/cloud/reserved_fixed_ips_async.py b/examples/cloud/reserved_fixed_ips_async.py index f3a1d9f7..0e97c4ea 100644 --- a/examples/cloud/reserved_fixed_ips_async.py +++ b/examples/cloud/reserved_fixed_ips_async.py @@ -1,8 +1,3 @@ -""" -Example demonstrating full lifecycle of Reserved Fixed IPs resource using async client. -Includes create, get, list, update (i.e. toggle VIP), and delete operations. -""" - import asyncio from gcore import AsyncGcore From e26746fd50a2fee2157c88244b737352f07dd55c Mon Sep 17 00:00:00 2001 From: Danil Krox Date: Mon, 30 Jun 2025 18:20:17 +0200 Subject: [PATCH 40/50] chore(cloud): streamline envs in examples --- examples/cloud/file_shares.py | 15 ++++++++++++++- examples/cloud/file_shares_async.py | 15 ++++++++++++++- examples/cloud/floating_ips.py | 14 +++++++++++++- examples/cloud/floating_ips_async.py | 14 +++++++++++++- examples/cloud/instances.py | 15 ++++++++++++++- examples/cloud/instances_async.py | 15 ++++++++++++++- examples/cloud/ip_ranges.py | 7 +++++++ examples/cloud/ip_ranges_async.py | 7 +++++++ examples/cloud/load_balancers.py | 12 +++++++++--- examples/cloud/load_balancers_async.py | 10 +++++++++- examples/cloud/networks.py | 9 ++++++++- examples/cloud/networks_async.py | 9 ++++++++- examples/cloud/projects.py | 5 +++++ examples/cloud/projects_async.py | 5 +++++ examples/cloud/quotas.py | 14 +++++++++++++- examples/cloud/quotas_async.py | 14 +++++++++++++- examples/cloud/regions.py | 3 +++ examples/cloud/regions_async.py | 3 +++ examples/cloud/reserved_fixed_ips.py | 14 +++++++++----- examples/cloud/reserved_fixed_ips_async.py | 14 +++++++++----- examples/cloud/secrets.py | 7 +++++++ examples/cloud/secrets_async.py | 7 +++++++ examples/cloud/security_groups.py | 14 +++++++++++++- examples/cloud/security_groups_async.py | 14 +++++++++++++- examples/cloud/ssh_keys.py | 7 +++++++ examples/cloud/ssh_keys_async.py | 7 +++++++ examples/cloud/task.py | 7 +++++++ examples/cloud/task_async.py | 7 +++++++ examples/cloud/volumes.py | 14 +++++++++++++- examples/cloud/volumes_async.py | 15 ++++++++++++++- 30 files changed, 285 insertions(+), 28 deletions(-) diff --git a/examples/cloud/file_shares.py b/examples/cloud/file_shares.py index 9e3e4f0f..908af045 100644 --- a/examples/cloud/file_shares.py +++ b/examples/cloud/file_shares.py @@ -5,10 +5,23 @@ def main() -> None: + # TODO set API key before running + # api_key = os.environ["GCORE_API_KEY"] + # TODO set cloud project ID before running + # cloud_project_id = os.environ["GCORE_CLOUD_PROJECT_ID"] + # TODO set cloud region ID before running + # cloud_region_id = os.environ["GCORE_CLOUD_REGION_ID"] + # TODO set cloud network ID before running cloud_network_id = os.environ["GCORE_CLOUD_NETWORK_ID"] - gcore = Gcore(timeout=180.0) + gcore = Gcore( + timeout=180.0, + # No need to explicitly pass to Gcore constructor if using environment variables + # api_key=api_key, + # cloud_project_id=cloud_project_id, + # cloud_region_id=cloud_region_id, + ) fs_id = create_file_share(client=gcore, network_id=cloud_network_id) list_file_shares(client=gcore) diff --git a/examples/cloud/file_shares_async.py b/examples/cloud/file_shares_async.py index f8459294..04fd38c3 100644 --- a/examples/cloud/file_shares_async.py +++ b/examples/cloud/file_shares_async.py @@ -6,10 +6,23 @@ async def main() -> None: + # TODO set API key before running + # api_key = os.environ["GCORE_API_KEY"] + # TODO set cloud project ID before running + # cloud_project_id = os.environ["GCORE_CLOUD_PROJECT_ID"] + # TODO set cloud region ID before running + # cloud_region_id = os.environ["GCORE_CLOUD_REGION_ID"] + # TODO set cloud network ID before running cloud_network_id = os.environ["GCORE_CLOUD_NETWORK_ID"] - gcore = AsyncGcore(timeout=180.0) + gcore = AsyncGcore( + timeout=180.0, + # No need to explicitly pass to AsyncGcore constructor if using environment variables + # api_key=api_key, + # cloud_project_id=cloud_project_id, + # cloud_region_id=cloud_region_id, + ) fs_id = await create_file_share(client=gcore, network_id=cloud_network_id) await list_file_shares(client=gcore) diff --git a/examples/cloud/floating_ips.py b/examples/cloud/floating_ips.py index 180f3746..2dac2967 100644 --- a/examples/cloud/floating_ips.py +++ b/examples/cloud/floating_ips.py @@ -6,10 +6,22 @@ def main() -> None: + # TODO set API key before running + # api_key = os.environ["GCORE_API_KEY"] + # TODO set cloud project ID before running + # cloud_project_id = os.environ["GCORE_CLOUD_PROJECT_ID"] + # TODO set cloud region ID before running + # cloud_region_id = os.environ["GCORE_CLOUD_REGION_ID"] + # TODO set cloud port ID before running cloud_port_id = os.environ["GCORE_CLOUD_PORT_ID"] - gcore = Gcore() + gcore = Gcore( + # No need to explicitly pass to Gcore constructor if using environment variables + # api_key=api_key, + # cloud_project_id=cloud_project_id, + # cloud_region_id=cloud_region_id, + ) floating_ip_id = create_floating_ip(client=gcore) list_floating_ips(client=gcore) diff --git a/examples/cloud/floating_ips_async.py b/examples/cloud/floating_ips_async.py index bd1c9a95..0c08c747 100644 --- a/examples/cloud/floating_ips_async.py +++ b/examples/cloud/floating_ips_async.py @@ -7,10 +7,22 @@ async def main() -> None: + # TODO set API key before running + # api_key = os.environ["GCORE_API_KEY"] + # TODO set cloud project ID before running + # cloud_project_id = os.environ["GCORE_CLOUD_PROJECT_ID"] + # TODO set cloud region ID before running + # cloud_region_id = os.environ["GCORE_CLOUD_REGION_ID"] + # TODO set cloud port ID before running cloud_port_id = os.environ["GCORE_CLOUD_PORT_ID"] - gcore = AsyncGcore() + gcore = AsyncGcore( + # No need to explicitly pass to AsyncGcore constructor if using environment variables + # api_key=api_key, + # cloud_project_id=cloud_project_id, + # cloud_region_id=cloud_region_id, + ) floating_ip_id = await create_floating_ip(client=gcore) await list_floating_ips(client=gcore) diff --git a/examples/cloud/instances.py b/examples/cloud/instances.py index 59ed8b73..065e722e 100644 --- a/examples/cloud/instances.py +++ b/examples/cloud/instances.py @@ -12,10 +12,23 @@ def main() -> None: + # TODO set API key before running + # api_key = os.environ["GCORE_API_KEY"] + # TODO set cloud project ID before running + # cloud_project_id = os.environ["GCORE_CLOUD_PROJECT_ID"] + # TODO set cloud region ID before running + # cloud_region_id = os.environ["GCORE_CLOUD_REGION_ID"] + # TODO set placement group ID before running placement_group_id = os.environ.get("GCORE_CLOUD_PLACEMENT_GROUP_ID") - gcore = Gcore(timeout=180.0) + gcore = Gcore( + timeout=180.0, + # No need to explicitly pass to Gcore constructor if using environment variables + # api_key=api_key, + # cloud_project_id=cloud_project_id, + # cloud_region_id=cloud_region_id, + ) uploaded_image_id = upload_image(client=gcore) diff --git a/examples/cloud/instances_async.py b/examples/cloud/instances_async.py index e3ab6d9b..20b1ab25 100644 --- a/examples/cloud/instances_async.py +++ b/examples/cloud/instances_async.py @@ -13,10 +13,23 @@ async def main() -> None: + # TODO set API key before running + # api_key = os.environ["GCORE_API_KEY"] + # TODO set cloud project ID before running + # cloud_project_id = os.environ["GCORE_CLOUD_PROJECT_ID"] + # TODO set cloud region ID before running + # cloud_region_id = os.environ["GCORE_CLOUD_REGION_ID"] + # TODO set placement group ID before running placement_group_id = os.environ.get("GCORE_CLOUD_PLACEMENT_GROUP_ID") - gcore = AsyncGcore(timeout=180.0) + gcore = AsyncGcore( + timeout=180.0, + # No need to explicitly pass to AsyncGcore constructor if using environment variables + # api_key=api_key, + # cloud_project_id=cloud_project_id, + # cloud_region_id=cloud_region_id, + ) uploaded_image_id = await upload_image(client=gcore) diff --git a/examples/cloud/ip_ranges.py b/examples/cloud/ip_ranges.py index e592c23d..221de980 100644 --- a/examples/cloud/ip_ranges.py +++ b/examples/cloud/ip_ranges.py @@ -5,6 +5,13 @@ def main() -> None: + # TODO set API key before running + # api_key = os.environ["GCORE_API_KEY"] + # TODO set cloud project ID before running + # cloud_project_id = os.environ["GCORE_CLOUD_PROJECT_ID"] + # TODO set cloud region ID before running + # cloud_region_id = os.environ["GCORE_CLOUD_REGION_ID"] + list_all_ip_ranges() diff --git a/examples/cloud/ip_ranges_async.py b/examples/cloud/ip_ranges_async.py index f5108d8a..3c8b95e8 100644 --- a/examples/cloud/ip_ranges_async.py +++ b/examples/cloud/ip_ranges_async.py @@ -6,6 +6,13 @@ async def main() -> None: + # TODO set API key before running + # api_key = os.environ["GCORE_API_KEY"] + # TODO set cloud project ID before running + # cloud_project_id = os.environ["GCORE_CLOUD_PROJECT_ID"] + # TODO set cloud region ID before running + # cloud_region_id = os.environ["GCORE_CLOUD_REGION_ID"] + await list_all_ip_ranges() diff --git a/examples/cloud/load_balancers.py b/examples/cloud/load_balancers.py index d9f94003..dee3ece5 100644 --- a/examples/cloud/load_balancers.py +++ b/examples/cloud/load_balancers.py @@ -2,12 +2,20 @@ def main() -> None: + # TODO set API key before running + # api_key = os.environ["GCORE_API_KEY"] # TODO set cloud project ID before running # cloud_project_id = os.environ["GCORE_CLOUD_PROJECT_ID"] # TODO set cloud region ID before running # cloud_region_id = os.environ["GCORE_CLOUD_REGION_ID"] - gcore = Gcore(timeout=180.0) + gcore = Gcore( + timeout=180.0, + # No need to explicitly pass to Gcore constructor if using environment variables + # api_key=api_key, + # cloud_project_id=cloud_project_id, + # cloud_region_id=cloud_region_id, + ) lb_id = create_load_balancer(client=gcore) list_load_balancers(client=gcore) @@ -39,8 +47,6 @@ def list_load_balancers(*, client: Gcore) -> None: load_balancers = client.cloud.load_balancers.list() for count, lb in enumerate(load_balancers, 1): print(f"{count}. Load balancer: ID={lb.id}, name={lb.name}, status={lb.provisioning_status}") - if not load_balancers.results: - print("No load balancers found.") print("========================") diff --git a/examples/cloud/load_balancers_async.py b/examples/cloud/load_balancers_async.py index 725a8955..dea488d5 100644 --- a/examples/cloud/load_balancers_async.py +++ b/examples/cloud/load_balancers_async.py @@ -6,12 +6,20 @@ async def main() -> None: + # TODO set API key before running + # api_key = os.environ["GCORE_API_KEY"] # TODO set cloud project ID before running # cloud_project_id = os.environ["GCORE_CLOUD_PROJECT_ID"] # TODO set cloud region ID before running # cloud_region_id = os.environ["GCORE_CLOUD_REGION_ID"] - gcore = AsyncGcore(timeout=180.0) + gcore = AsyncGcore( + timeout=180.0, + # No need to explicitly pass to AsyncGcore constructor if using environment variables + # api_key=api_key, + # cloud_project_id=cloud_project_id, + # cloud_region_id=cloud_region_id, + ) lb_id = await create_load_balancer(client=gcore) await list_load_balancers(client=gcore) diff --git a/examples/cloud/networks.py b/examples/cloud/networks.py index 0df76cb5..36dde8c5 100644 --- a/examples/cloud/networks.py +++ b/examples/cloud/networks.py @@ -2,12 +2,19 @@ def main() -> None: + # TODO set API key before running + # api_key = os.environ["GCORE_API_KEY"] # TODO set cloud project ID before running # cloud_project_id = os.environ["GCORE_CLOUD_PROJECT_ID"] # TODO set cloud region ID before running # cloud_region_id = os.environ["GCORE_CLOUD_REGION_ID"] - gcore = Gcore() + gcore = Gcore( + # No need to explicitly pass to Gcore constructor if using environment variables + # api_key=api_key, + # cloud_project_id=cloud_project_id, + # cloud_region_id=cloud_region_id, + ) network_id = create_network(client=gcore) list_networks(client=gcore) diff --git a/examples/cloud/networks_async.py b/examples/cloud/networks_async.py index 664d2f5b..669a55ef 100644 --- a/examples/cloud/networks_async.py +++ b/examples/cloud/networks_async.py @@ -4,12 +4,19 @@ async def main() -> None: + # TODO set API key before running + # api_key = os.environ["GCORE_API_KEY"] # TODO set cloud project ID before running # cloud_project_id = os.environ["GCORE_CLOUD_PROJECT_ID"] # TODO set cloud region ID before running # cloud_region_id = os.environ["GCORE_CLOUD_REGION_ID"] - gcore = AsyncGcore() + gcore = AsyncGcore( + # No need to explicitly pass to AsyncGcore constructor if using environment variables + # api_key=api_key, + # cloud_project_id=cloud_project_id, + # cloud_region_id=cloud_region_id, + ) network_id = await create_network(client=gcore) await list_networks(client=gcore) diff --git a/examples/cloud/projects.py b/examples/cloud/projects.py index 99170215..6a6c67e8 100644 --- a/examples/cloud/projects.py +++ b/examples/cloud/projects.py @@ -6,6 +6,11 @@ def main() -> None: + # TODO set API key before running + # api_key = os.environ["GCORE_API_KEY"] + # TODO set cloud project ID before running + # cloud_project_id = os.environ["GCORE_CLOUD_PROJECT_ID"] + new_project = create_new_project() list_all_projects() get_project_by_id() diff --git a/examples/cloud/projects_async.py b/examples/cloud/projects_async.py index b1cd431b..16f672f6 100644 --- a/examples/cloud/projects_async.py +++ b/examples/cloud/projects_async.py @@ -7,6 +7,11 @@ async def main() -> None: + # TODO set API key before running + # api_key = os.environ["GCORE_API_KEY"] + # TODO set cloud project ID before running + # cloud_project_id = os.environ["GCORE_CLOUD_PROJECT_ID"] + new_project = await create_new_project() await list_all_projects() await get_project_by_id() diff --git a/examples/cloud/quotas.py b/examples/cloud/quotas.py index 15d8add3..ea058b83 100644 --- a/examples/cloud/quotas.py +++ b/examples/cloud/quotas.py @@ -8,9 +8,21 @@ def main() -> None: + # TODO set API key before running + # api_key = os.environ["GCORE_API_KEY"] + # TODO set cloud project ID before running + # cloud_project_id = os.environ["GCORE_CLOUD_PROJECT_ID"] + # TODO set cloud region ID before running + # cloud_region_id = os.environ["GCORE_CLOUD_REGION_ID"] + gcore_client_id = int(os.environ["GCORE_CLIENT_ID"]) - gcore = Gcore() + gcore = Gcore( + # No need to explicitly pass to Gcore constructor if using environment variables + # api_key=api_key, + # cloud_project_id=cloud_project_id, + # cloud_region_id=cloud_region_id, + ) get_all_quotas(client=gcore) get_regional_quotas(client=gcore, client_id=gcore_client_id) diff --git a/examples/cloud/quotas_async.py b/examples/cloud/quotas_async.py index 5d969ce1..5be4ee9e 100644 --- a/examples/cloud/quotas_async.py +++ b/examples/cloud/quotas_async.py @@ -9,9 +9,21 @@ async def main() -> None: + # TODO set API key before running + # api_key = os.environ["GCORE_API_KEY"] + # TODO set cloud project ID before running + # cloud_project_id = os.environ["GCORE_CLOUD_PROJECT_ID"] + # TODO set cloud region ID before running + # cloud_region_id = os.environ["GCORE_CLOUD_REGION_ID"] + gcore_client_id = int(os.environ["GCORE_CLIENT_ID"]) - gcore = AsyncGcore() + gcore = AsyncGcore( + # No need to explicitly pass to AsyncGcore constructor if using environment variables + # api_key=api_key, + # cloud_project_id=cloud_project_id, + # cloud_region_id=cloud_region_id, + ) await get_all_quotas(client=gcore) await get_regional_quotas(client=gcore, client_id=gcore_client_id) diff --git a/examples/cloud/regions.py b/examples/cloud/regions.py index d2c9d243..cf6d4afa 100644 --- a/examples/cloud/regions.py +++ b/examples/cloud/regions.py @@ -6,6 +6,9 @@ def main() -> None: + # TODO set API key before running + # api_key = os.environ["GCORE_API_KEY"] + get_region_by_id() list_all_regions() list_regions_with_filters() diff --git a/examples/cloud/regions_async.py b/examples/cloud/regions_async.py index f5069d99..911653b4 100644 --- a/examples/cloud/regions_async.py +++ b/examples/cloud/regions_async.py @@ -7,6 +7,9 @@ async def main() -> None: + # TODO set API key before running + # api_key = os.environ["GCORE_API_KEY"] + await get_region_by_id() await list_all_regions() await list_regions_with_filters() diff --git a/examples/cloud/reserved_fixed_ips.py b/examples/cloud/reserved_fixed_ips.py index 7be747a5..1233b661 100644 --- a/examples/cloud/reserved_fixed_ips.py +++ b/examples/cloud/reserved_fixed_ips.py @@ -4,14 +4,18 @@ def main() -> None: - # No need to pass the API key explicitly — it will automatically be read from the GCORE_API_KEY environment variable if omitted - # api_key = os.environ.get("GCORE_API_KEY") - # Will use Production API URL if omitted - # base_url = os.environ.get("GCORE_BASE_URL") + # TODO set API key before running + # api_key = os.environ["GCORE_API_KEY"] + # TODO set cloud project ID before running + # cloud_project_id = os.environ["GCORE_CLOUD_PROJECT_ID"] + # TODO set cloud region ID before running + # cloud_region_id = os.environ["GCORE_CLOUD_REGION_ID"] gcore = Gcore( + # No need to explicitly pass to Gcore constructor if using environment variables # api_key=api_key, - # base_url=base_url, + # cloud_project_id=cloud_project_id, + # cloud_region_id=cloud_region_id, ) fixed_ip = create_reserved_fixed_ip(client=gcore) diff --git a/examples/cloud/reserved_fixed_ips_async.py b/examples/cloud/reserved_fixed_ips_async.py index 0e97c4ea..3542a229 100644 --- a/examples/cloud/reserved_fixed_ips_async.py +++ b/examples/cloud/reserved_fixed_ips_async.py @@ -6,14 +6,18 @@ async def main() -> None: - # No need to pass the API key explicitly — it will automatically be read from the GCORE_API_KEY environment variable if omitted - # api_key = os.environ.get("GCORE_API_KEY") - # Will use Production API URL if omitted - # base_url = os.environ.get("GCORE_BASE_URL") + # TODO set API key before running + # api_key = os.environ["GCORE_API_KEY"] + # TODO set cloud project ID before running + # cloud_project_id = os.environ["GCORE_CLOUD_PROJECT_ID"] + # TODO set cloud region ID before running + # cloud_region_id = os.environ["GCORE_CLOUD_REGION_ID"] gcore = AsyncGcore( + # No need to explicitly pass to AsyncGcore constructor if using environment variables # api_key=api_key, - # base_url=base_url, + # cloud_project_id=cloud_project_id, + # cloud_region_id=cloud_region_id, ) fixed_ip = await create_reserved_fixed_ip(client=gcore) diff --git a/examples/cloud/secrets.py b/examples/cloud/secrets.py index ac1d33e0..449d1198 100644 --- a/examples/cloud/secrets.py +++ b/examples/cloud/secrets.py @@ -7,6 +7,13 @@ def main() -> None: + # TODO set API key before running + # api_key = os.environ["GCORE_API_KEY"] + # TODO set cloud project ID before running + # cloud_project_id = os.environ["GCORE_CLOUD_PROJECT_ID"] + # TODO set cloud region ID before running + # cloud_region_id = os.environ["GCORE_CLOUD_REGION_ID"] + cert = upload_tls_cert() get_secret_by_id(cert.id) list_all_secrets() diff --git a/examples/cloud/secrets_async.py b/examples/cloud/secrets_async.py index 61c074f1..9f6451ec 100644 --- a/examples/cloud/secrets_async.py +++ b/examples/cloud/secrets_async.py @@ -10,6 +10,13 @@ async def main() -> None: + # TODO set API key before running + # api_key = os.environ["GCORE_API_KEY"] + # TODO set cloud project ID before running + # cloud_project_id = os.environ["GCORE_CLOUD_PROJECT_ID"] + # TODO set cloud region ID before running + # cloud_region_id = os.environ["GCORE_CLOUD_REGION_ID"] + cert = await upload_tls_cert() await get_secret_by_id(cert.id) await list_all_secrets() diff --git a/examples/cloud/security_groups.py b/examples/cloud/security_groups.py index f2456b25..2620e283 100644 --- a/examples/cloud/security_groups.py +++ b/examples/cloud/security_groups.py @@ -3,7 +3,19 @@ def main() -> None: - gcore = Gcore() + # TODO set API key before running + # api_key = os.environ["GCORE_API_KEY"] + # TODO set cloud project ID before running + # cloud_project_id = os.environ["GCORE_CLOUD_PROJECT_ID"] + # TODO set cloud region ID before running + # cloud_region_id = os.environ["GCORE_CLOUD_REGION_ID"] + + gcore = Gcore( + # No need to explicitly pass to Gcore constructor if using environment variables + # api_key=api_key, + # cloud_project_id=cloud_project_id, + # cloud_region_id=cloud_region_id, + ) security_group_id = create_security_group(client=gcore) list_security_groups(client=gcore) diff --git a/examples/cloud/security_groups_async.py b/examples/cloud/security_groups_async.py index 13123835..dacb1cfc 100644 --- a/examples/cloud/security_groups_async.py +++ b/examples/cloud/security_groups_async.py @@ -5,7 +5,19 @@ async def main() -> None: - gcore = AsyncGcore() + # TODO set API key before running + # api_key = os.environ["GCORE_API_KEY"] + # TODO set cloud project ID before running + # cloud_project_id = os.environ["GCORE_CLOUD_PROJECT_ID"] + # TODO set cloud region ID before running + # cloud_region_id = os.environ["GCORE_CLOUD_REGION_ID"] + + gcore = AsyncGcore( + # No need to explicitly pass to AsyncGcore constructor if using environment variables + # api_key=api_key, + # cloud_project_id=cloud_project_id, + # cloud_region_id=cloud_region_id, + ) security_group_id = await create_security_group(client=gcore) await list_security_groups(client=gcore) diff --git a/examples/cloud/ssh_keys.py b/examples/cloud/ssh_keys.py index ad73f8e3..230c976a 100644 --- a/examples/cloud/ssh_keys.py +++ b/examples/cloud/ssh_keys.py @@ -6,6 +6,13 @@ def main() -> None: + # TODO set API key before running + # api_key = os.environ["GCORE_API_KEY"] + # TODO set cloud project ID before running + # cloud_project_id = os.environ["GCORE_CLOUD_PROJECT_ID"] + # TODO set cloud region ID before running + # cloud_region_id = os.environ["GCORE_CLOUD_REGION_ID"] + # Follow the order: create, list, get, update, delete new_ssh_key = create_new_ssh_key() list_all_ssh_keys() diff --git a/examples/cloud/ssh_keys_async.py b/examples/cloud/ssh_keys_async.py index 2ed7740f..6866b3c5 100644 --- a/examples/cloud/ssh_keys_async.py +++ b/examples/cloud/ssh_keys_async.py @@ -7,6 +7,13 @@ async def main() -> None: + # TODO set API key before running + # api_key = os.environ["GCORE_API_KEY"] + # TODO set cloud project ID before running + # cloud_project_id = os.environ["GCORE_CLOUD_PROJECT_ID"] + # TODO set cloud region ID before running + # cloud_region_id = os.environ["GCORE_CLOUD_REGION_ID"] + # Follow the order: create, list, get, update, delete new_ssh_key = await create_new_ssh_key() await list_all_ssh_keys() diff --git a/examples/cloud/task.py b/examples/cloud/task.py index cc8ebba2..c451c503 100644 --- a/examples/cloud/task.py +++ b/examples/cloud/task.py @@ -9,6 +9,13 @@ def main() -> None: + # TODO set API key before running + # api_key = os.environ["GCORE_API_KEY"] + # TODO set cloud project ID before running + # cloud_project_id = os.environ["GCORE_CLOUD_PROJECT_ID"] + # TODO set cloud region ID before running + # cloud_region_id = os.environ["GCORE_CLOUD_REGION_ID"] + get_task_by_id() list_tasks() diff --git a/examples/cloud/task_async.py b/examples/cloud/task_async.py index 1793fe8f..c28baeaf 100644 --- a/examples/cloud/task_async.py +++ b/examples/cloud/task_async.py @@ -10,6 +10,13 @@ async def main() -> None: + # TODO set API key before running + # api_key = os.environ["GCORE_API_KEY"] + # TODO set cloud project ID before running + # cloud_project_id = os.environ["GCORE_CLOUD_PROJECT_ID"] + # TODO set cloud region ID before running + # cloud_region_id = os.environ["GCORE_CLOUD_REGION_ID"] + await get_task_by_id() await list_tasks() diff --git a/examples/cloud/volumes.py b/examples/cloud/volumes.py index 767d4320..b23f0523 100644 --- a/examples/cloud/volumes.py +++ b/examples/cloud/volumes.py @@ -4,10 +4,22 @@ def main() -> None: + # TODO set API key before running + # api_key = os.environ["GCORE_API_KEY"] + # TODO set cloud project ID before running + # cloud_project_id = os.environ["GCORE_CLOUD_PROJECT_ID"] + # TODO set cloud region ID before running + # cloud_region_id = os.environ["GCORE_CLOUD_REGION_ID"] + # TODO set instance ID before running attach/detach operations instance_id = os.environ["GCORE_CLOUD_INSTANCE_ID"] - gcore = Gcore() + gcore = Gcore( + # No need to explicitly pass to Gcore constructor if using environment variables + # api_key=api_key, + # cloud_project_id=cloud_project_id, + # cloud_region_id=cloud_region_id, + ) volume_id = create_volume(client=gcore) list_volumes(client=gcore) diff --git a/examples/cloud/volumes_async.py b/examples/cloud/volumes_async.py index e9c1ed64..522e0ff9 100644 --- a/examples/cloud/volumes_async.py +++ b/examples/cloud/volumes_async.py @@ -5,9 +5,22 @@ async def main() -> None: + # TODO set API key before running + # api_key = os.environ["GCORE_API_KEY"] + # TODO set cloud project ID before running + # cloud_project_id = os.environ["GCORE_CLOUD_PROJECT_ID"] + # TODO set cloud region ID before running + # cloud_region_id = os.environ["GCORE_CLOUD_REGION_ID"] + + # TODO set instance ID before running instance_id = os.environ["GCORE_CLOUD_INSTANCE_ID"] - gcore = AsyncGcore() + gcore = AsyncGcore( + # No need to explicitly pass to AsyncGcore constructor if using environment variables + # api_key=api_key, + # cloud_project_id=cloud_project_id, + # cloud_region_id=cloud_region_id, + ) volume_id = await create_volume(client=gcore) await list_volumes(client=gcore) From dc8eca44e4ac8fb013aa182e8066a4cad19cd821 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 1 Jul 2025 06:15:21 +0000 Subject: [PATCH 41/50] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index af340d1a..757399c4 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 302 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-739daccdbf486da0806ddb7c892d037d991e743a6a536ffe0ebb1ff6ac6cd817.yml -openapi_spec_hash: 04456598c665817e9bb233d70b118f07 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-3bd81b64cc2ef4de71b6d709296074d203d109fbe6549331480f09fd4412edd4.yml +openapi_spec_hash: 4bcd0c3053fcb13e42cf6b0d8244499f config_hash: 13858022450ab96760041d6619620b32 From 32446c4e9bb385f68b0a24637f731ff93ee0eb5a Mon Sep 17 00:00:00 2001 From: Danil Krox Date: Tue, 1 Jul 2025 13:11:23 +0200 Subject: [PATCH 42/50] chore(cloud): unify examples format --- examples/cloud/ip_ranges.py | 18 ++--- examples/cloud/ip_ranges_async.py | 21 ++--- examples/cloud/projects.py | 76 +++++++----------- examples/cloud/projects_async.py | 75 +++++++----------- examples/cloud/regions.py | 37 +++++---- examples/cloud/regions_async.py | 37 +++++---- examples/cloud/secrets.py | 58 ++++++-------- examples/cloud/secrets_async.py | 75 ++++++------------ examples/cloud/ssh_keys.py | 89 ++++++++++----------- examples/cloud/ssh_keys_async.py | 89 ++++++++++----------- examples/cloud/task.py | 125 +++++++++++------------------ examples/cloud/task_async.py | 127 +++++++++++------------------- 12 files changed, 333 insertions(+), 494 deletions(-) diff --git a/examples/cloud/ip_ranges.py b/examples/cloud/ip_ranges.py index 221de980..8130711b 100644 --- a/examples/cloud/ip_ranges.py +++ b/examples/cloud/ip_ranges.py @@ -1,5 +1,3 @@ -import os - from gcore import Gcore from gcore.types.cloud import IPRanges @@ -7,20 +5,18 @@ def main() -> None: # TODO set API key before running # api_key = os.environ["GCORE_API_KEY"] - # TODO set cloud project ID before running - # cloud_project_id = os.environ["GCORE_CLOUD_PROJECT_ID"] - # TODO set cloud region ID before running - # cloud_region_id = os.environ["GCORE_CLOUD_REGION_ID"] - list_all_ip_ranges() + gcore = Gcore( + # No need to explicitly pass to Gcore constructor if using environment variables + # api_key=api_key, + ) + list_all_ip_ranges(client=gcore) -def list_all_ip_ranges() -> IPRanges: - # No need to pass the API key explicitly — it will automatically be read from the GCORE_API_KEY environment variable if omitted - gcore = Gcore(api_key=os.environ.get("GCORE_API_KEY"), base_url=os.environ.get("GCORE_API_URL")) - all_ip_ranges = gcore.cloud.ip_ranges.list() +def list_all_ip_ranges(*, client: Gcore) -> IPRanges: print("\n=== LIST ALL IP RANGES ===") + all_ip_ranges = client.cloud.ip_ranges.list() for count, iprange in enumerate(all_ip_ranges.ranges, 1): print(f" {count}. IP Range: {iprange}") print("===========================") diff --git a/examples/cloud/ip_ranges_async.py b/examples/cloud/ip_ranges_async.py index 3c8b95e8..aab3d5f2 100644 --- a/examples/cloud/ip_ranges_async.py +++ b/examples/cloud/ip_ranges_async.py @@ -1,4 +1,3 @@ -import os import asyncio from gcore import AsyncGcore @@ -8,24 +7,20 @@ async def main() -> None: # TODO set API key before running # api_key = os.environ["GCORE_API_KEY"] - # TODO set cloud project ID before running - # cloud_project_id = os.environ["GCORE_CLOUD_PROJECT_ID"] - # TODO set cloud region ID before running - # cloud_region_id = os.environ["GCORE_CLOUD_REGION_ID"] - await list_all_ip_ranges() + gcore = AsyncGcore( + # No need to explicitly pass to AsyncGcore constructor if using environment variables + # api_key=api_key, + ) + await list_all_ip_ranges(client=gcore) -async def list_all_ip_ranges() -> IPRanges: - # No need to pass the API key explicitly — it will automatically be read from the GCORE_API_KEY environment variable if omitted - gcore = AsyncGcore(api_key=os.environ.get("GCORE_API_KEY"), base_url=os.environ.get("GCORE_API_URL")) - all_ip_ranges = await gcore.cloud.ip_ranges.list() +async def list_all_ip_ranges(*, client: AsyncGcore) -> IPRanges: print("\n=== LIST ALL IP RANGES ===") - count = 1 - for iprange in all_ip_ranges.ranges: + all_ip_ranges = await client.cloud.ip_ranges.list() + for count, iprange in enumerate(all_ip_ranges.ranges, 1): print(f" {count}. IP Range: {iprange}") - count += 1 print("===========================") return all_ip_ranges diff --git a/examples/cloud/projects.py b/examples/cloud/projects.py index 6a6c67e8..0fe79699 100644 --- a/examples/cloud/projects.py +++ b/examples/cloud/projects.py @@ -1,5 +1,3 @@ -import os - from gcore import Gcore from gcore.pagination import SyncOffsetPage from gcore.types.cloud import Project @@ -8,70 +6,56 @@ def main() -> None: # TODO set API key before running # api_key = os.environ["GCORE_API_KEY"] - # TODO set cloud project ID before running - # cloud_project_id = os.environ["GCORE_CLOUD_PROJECT_ID"] - - new_project = create_new_project() - list_all_projects() - get_project_by_id() - update_project(project_id=new_project.id) - delete_project(project_id=new_project.id) + gcore = Gcore( + # No need to explicitly pass to Gcore constructor if using environment variables + # api_key=api_key, + ) -def get_project_by_id() -> Project: - # No need to pass the API key explicitly — it will automatically be read from the GCORE_API_KEY environment variable if omitted - gcore = Gcore(api_key=os.environ.get("GCORE_API_KEY"), base_url=os.environ.get("GCORE_API_URL")) - # Project ID can be specified via environment variable or default to 100 - project_id = int(os.environ.get("GCORE_CLOUD_PROJECT_ID", "100")) - project = gcore.cloud.projects.get(project_id=project_id) + project = create_project(client=gcore) + list_all_projects(client=gcore) + get_project_by_id(client=gcore, project_id=project.id) + update_project(client=gcore, project_id=project.id) + delete_project(client=gcore, project_id=project.id) - print("\n=== GET PROJECT BY ID ===") - print(f"Project ID: {project.id}, Name: {project.name}, Created: {project.created_at}") - print("==========================") - return project +def create_project(*, client: Gcore) -> Project: + print("\n=== CREATE PROJECT ===") + new_project = client.cloud.projects.create(name="gcore-go-example") + print(f"Project ID: {new_project.id}, name: {new_project.name}") + print("===========================") + return new_project -def list_all_projects() -> SyncOffsetPage[Project]: - gcore = Gcore(api_key=os.environ.get("GCORE_API_KEY"), base_url=os.environ.get("GCORE_API_URL")) - all_projects = gcore.cloud.projects.list() +def list_all_projects(*, client: Gcore) -> SyncOffsetPage[Project]: print("\n=== LIST ALL PROJECTS ===") + all_projects = client.cloud.projects.list() for count, project in enumerate(all_projects, 1): - print(f" {count}. Project ID: {project.id}, Name: {project.name}") + print(f" {count}. Project ID: {project.id}, name: {project.name}") print("==========================") return all_projects -def create_new_project() -> Project: - gcore = Gcore(api_key=os.environ.get("GCORE_API_KEY"), base_url=os.environ.get("GCORE_API_URL")) - # Project name can be specified via environment variable or default - project_name = "Example Project" - new_project = gcore.cloud.projects.create(name=project_name) - - print("\n=== CREATE NEW PROJECT ===") - print(f"Project ID: {new_project.id}, Name: {new_project.name}") - print("===========================") - return new_project - +def get_project_by_id(*, client: Gcore, project_id: int) -> Project: + print("\n=== GET PROJECT BY ID ===") + project = client.cloud.projects.get(project_id=project_id) + print(f"Project ID: {project.id}, name: {project.name}, created: {project.created_at}") + print("==========================") + return project -def update_project(project_id: int) -> Project: - gcore = Gcore(api_key=os.environ.get("GCORE_API_KEY"), base_url=os.environ.get("GCORE_API_URL")) - # Updated project name can be specified via environment variable or default - updated_name = "Updated Example Project" - updated_project = gcore.cloud.projects.replace(project_id=project_id, name=updated_name) +def update_project(*, client: Gcore, project_id: int) -> Project: print("\n=== UPDATE PROJECT ===") - print(f"Project ID: {updated_project.id}, Updated Name: {updated_project.name}") + updated_project = client.cloud.projects.replace(project_id=project_id, name="gcore-go-example-updated") + print(f"Project ID: {updated_project.id}, name: {updated_project.name}") print("=======================") return updated_project -def delete_project(project_id: int) -> None: - gcore = Gcore(api_key=os.environ.get("GCORE_API_KEY"), base_url=os.environ.get("GCORE_API_URL")) - gcore.cloud.projects.delete(project_id=project_id) - +def delete_project(*, client: Gcore, project_id: int) -> None: print("\n=== DELETE PROJECT ===") - print(f"Project ID: {project_id} has been deleted") + client.cloud.projects.delete(project_id=project_id) + print(f"Deleted project: ID={project_id}") print("=======================") diff --git a/examples/cloud/projects_async.py b/examples/cloud/projects_async.py index 16f672f6..62bff7e1 100644 --- a/examples/cloud/projects_async.py +++ b/examples/cloud/projects_async.py @@ -1,4 +1,3 @@ -import os import asyncio from gcore import AsyncGcore @@ -9,72 +8,58 @@ async def main() -> None: # TODO set API key before running # api_key = os.environ["GCORE_API_KEY"] - # TODO set cloud project ID before running - # cloud_project_id = os.environ["GCORE_CLOUD_PROJECT_ID"] - new_project = await create_new_project() - await list_all_projects() - await get_project_by_id() - await update_project(project_id=new_project.id) - await delete_project(project_id=new_project.id) + gcore = AsyncGcore( + # No need to explicitly pass to AsyncGcore constructor if using environment variables + # api_key=api_key, + ) + project = await create_project(client=gcore) + await list_all_projects(client=gcore) + await get_project_by_id(client=gcore, project_id=project.id) + await update_project(client=gcore, project_id=project.id) + await delete_project(client=gcore, project_id=project.id) -async def get_project_by_id() -> Project: - # No need to pass the API key explicitly — it will automatically be read from the GCORE_API_KEY environment variable if omitted - gcore = AsyncGcore(api_key=os.environ.get("GCORE_API_KEY"), base_url=os.environ.get("GCORE_API_URL")) - # Project ID can be specified via environment variable or default to 100 - project_id = int(os.environ.get("GCORE_CLOUD_PROJECT_ID", "100")) - project = await gcore.cloud.projects.get(project_id=project_id) - - print("\n=== GET PROJECT BY ID ===") - print(f"Project ID: {project.id}, Name: {project.name}, Created: {project.created_at}") - print("==========================") - return project +async def create_project(*, client: AsyncGcore) -> Project: + print("\n=== CREATE PROJECT ===") + new_project = await client.cloud.projects.create(name="gcore-go-example") + print(f"Project ID: {new_project.id}, name: {new_project.name}") + print("===========================") + return new_project -async def list_all_projects() -> AsyncOffsetPage[Project]: - gcore = AsyncGcore(api_key=os.environ.get("GCORE_API_KEY"), base_url=os.environ.get("GCORE_API_URL")) - all_projects = await gcore.cloud.projects.list() +async def list_all_projects(*, client: AsyncGcore) -> AsyncOffsetPage[Project]: print("\n=== LIST ALL PROJECTS ===") + all_projects = await client.cloud.projects.list() count = 1 async for project in all_projects: - print(f" {count}. Project ID: {project.id}, Name: {project.name}") + print(f" {count}. Project ID: {project.id}, name: {project.name}") count += 1 print("==========================") return all_projects -async def create_new_project() -> Project: - gcore = AsyncGcore(api_key=os.environ.get("GCORE_API_KEY"), base_url=os.environ.get("GCORE_API_URL")) - # Project name can be specified via environment variable or default - project_name = "Example Project" - new_project = await gcore.cloud.projects.create(name=project_name) - - print("\n=== CREATE NEW PROJECT ===") - print(f"Project ID: {new_project.id}, Name: {new_project.name}") - print("===========================") - return new_project - +async def get_project_by_id(*, client: AsyncGcore, project_id: int) -> Project: + print("\n=== GET PROJECT BY ID ===") + project = await client.cloud.projects.get(project_id=project_id) + print(f"Project ID: {project.id}, name: {project.name}, created: {project.created_at}") + print("==========================") + return project -async def update_project(project_id: int) -> Project: - gcore = AsyncGcore(api_key=os.environ.get("GCORE_API_KEY"), base_url=os.environ.get("GCORE_API_URL")) - # Updated project name can be specified via environment variable or default - updated_name = "Updated Example Project" - updated_project = await gcore.cloud.projects.replace(project_id=project_id, name=updated_name) +async def update_project(*, client: AsyncGcore, project_id: int) -> Project: print("\n=== UPDATE PROJECT ===") - print(f"Project ID: {updated_project.id}, Updated Name: {updated_project.name}") + updated_project = await client.cloud.projects.replace(project_id=project_id, name="gcore-go-example-updated") + print(f"Project ID: {updated_project.id}, name: {updated_project.name}") print("=======================") return updated_project -async def delete_project(project_id: int) -> None: - gcore = AsyncGcore(api_key=os.environ.get("GCORE_API_KEY"), base_url=os.environ.get("GCORE_API_URL")) - await gcore.cloud.projects.delete(project_id=project_id) - +async def delete_project(*, client: AsyncGcore, project_id: int) -> None: print("\n=== DELETE PROJECT ===") - print(f"Project ID: {project_id} has been deleted") + await client.cloud.projects.delete(project_id=project_id) + print(f"Deleted project: ID={project_id}") print("=======================") diff --git a/examples/cloud/regions.py b/examples/cloud/regions.py index cf6d4afa..46ae32ad 100644 --- a/examples/cloud/regions.py +++ b/examples/cloud/regions.py @@ -8,42 +8,41 @@ def main() -> None: # TODO set API key before running # api_key = os.environ["GCORE_API_KEY"] + # TODO set cloud region ID before running + cloud_region_id = int(os.environ.get("GCORE_CLOUD_REGION_ID", 76)) - get_region_by_id() - list_all_regions() - list_regions_with_filters() + gcore = Gcore( + # No need to explicitly pass to Gcore constructor if using environment variables + # api_key=api_key, + ) + get_region_by_id(client=gcore, region_id=cloud_region_id) + list_all_regions(client=gcore) + list_regions_with_filters(client=gcore) -def get_region_by_id() -> Region: - # No need to pass the API key explicitly — it will automatically be read from the GCORE_API_KEY environment variable if omitted - gcore = Gcore(api_key=os.environ.get("GCORE_API_KEY")) - # Region ID can also be omitted — it defaults to the GCORE_CLOUD_REGION_ID environment variable - region = gcore.cloud.regions.get(region_id=int(os.environ.get("GCORE_CLOUD_REGION_ID", "76"))) +def get_region_by_id(*, client: Gcore, region_id: int) -> Region: print("\n=== GET REGION BY ID ===") - print(f"Region ID: {region.id}, Display Name: {region.display_name}") + region = client.cloud.regions.get(region_id=region_id) + print(f"Region ID: {region.id}, display name: {region.display_name}") print("========================") return region -def list_all_regions() -> SyncOffsetPage[Region]: - gcore = Gcore(api_key=os.environ.get("GCORE_API_KEY")) - all_regions = gcore.cloud.regions.list() - +def list_all_regions(*, client: Gcore) -> SyncOffsetPage[Region]: print("\n=== LIST ALL REGIONS ===") + all_regions = client.cloud.regions.list() for count, region in enumerate(all_regions, 1): - print(f" {count}. Region ID: {region.id}, Display Name: {region.display_name}") + print(f" {count}. Region ID: {region.id}, display name: {region.display_name}") print("========================") return all_regions -def list_regions_with_filters() -> SyncOffsetPage[Region]: - gcore = Gcore(api_key=os.environ.get("GCORE_API_KEY")) - filtered_regions = gcore.cloud.regions.list(product="containers") - +def list_regions_with_filters(*, client: Gcore) -> SyncOffsetPage[Region]: print("\n=== LIST REGIONS WITH FILTERS ===") + filtered_regions = client.cloud.regions.list(product="inference") for count, region in enumerate(filtered_regions, 1): - print(f" {count}. Region ID: {region.id}, Display Name: {region.display_name}") + print(f" {count}. Region ID: {region.id}, display name: {region.display_name}") print("=================================") return filtered_regions diff --git a/examples/cloud/regions_async.py b/examples/cloud/regions_async.py index 911653b4..0703ba49 100644 --- a/examples/cloud/regions_async.py +++ b/examples/cloud/regions_async.py @@ -9,45 +9,44 @@ async def main() -> None: # TODO set API key before running # api_key = os.environ["GCORE_API_KEY"] + # TODO set cloud region ID before running + cloud_region_id = int(os.environ.get("GCORE_CLOUD_REGION_ID", 76)) - await get_region_by_id() - await list_all_regions() - await list_regions_with_filters() + gcore = AsyncGcore( + # No need to explicitly pass to AsyncGcore constructor if using environment variables + # api_key=api_key, + ) + await get_region_by_id(client=gcore, region_id=cloud_region_id) + await list_all_regions(client=gcore) + await list_regions_with_filters(client=gcore) -async def get_region_by_id() -> Region: - # No need to pass the API key explicitly — it will automatically be read from the GCORE_API_KEY environment variable if omitted - gcore = AsyncGcore(api_key=os.environ.get("GCORE_API_KEY")) - # Region ID can also be omitted — it defaults to the GCORE_CLOUD_REGION_ID environment variable - region = await gcore.cloud.regions.get(region_id=int(os.environ.get("GCORE_CLOUD_REGION_ID", "76"))) +async def get_region_by_id(*, client: AsyncGcore, region_id: int) -> Region: print("\n=== GET REGION BY ID ===") - print(f"Region ID: {region.id}, Display Name: {region.display_name}") + region = await client.cloud.regions.get(region_id=region_id) + print(f"Region ID: {region.id}, display name: {region.display_name}") print("========================") return region -async def list_all_regions() -> AsyncOffsetPage[Region]: - gcore = AsyncGcore(api_key=os.environ.get("GCORE_API_KEY")) - all_regions = await gcore.cloud.regions.list() - +async def list_all_regions(*, client: AsyncGcore) -> AsyncOffsetPage[Region]: print("\n=== LIST ALL REGIONS ===") + all_regions = await client.cloud.regions.list() count = 1 async for region in all_regions: - print(f" {count}. Region ID: {region.id}, Display Name: {region.display_name}") + print(f" {count}. Region ID: {region.id}, display name: {region.display_name}") count += 1 print("========================") return all_regions -async def list_regions_with_filters() -> AsyncOffsetPage[Region]: - gcore = AsyncGcore(api_key=os.environ.get("GCORE_API_KEY")) - filtered_regions = await gcore.cloud.regions.list(product="containers") - +async def list_regions_with_filters(*, client: AsyncGcore) -> AsyncOffsetPage[Region]: print("\n=== LIST REGIONS WITH FILTERS ===") + filtered_regions = await client.cloud.regions.list(product="inference") count = 1 async for region in filtered_regions: - print(f" {count}. Region ID: {region.id}, Display Name: {region.display_name}") + print(f" {count}. Region ID: {region.id}, display name: {region.display_name}") count += 1 print("=================================") return filtered_regions diff --git a/examples/cloud/secrets.py b/examples/cloud/secrets.py index 449d1198..449a380d 100644 --- a/examples/cloud/secrets.py +++ b/examples/cloud/secrets.py @@ -1,4 +1,3 @@ -import os from typing import List from gcore import Gcore @@ -14,64 +13,55 @@ def main() -> None: # TODO set cloud region ID before running # cloud_region_id = os.environ["GCORE_CLOUD_REGION_ID"] - cert = upload_tls_cert() - get_secret_by_id(cert.id) - list_all_secrets() - delete_secret(cert.id) + client = Gcore( + # No need to explicitly pass to Gcore constructor if using environment variables + # api_key=api_key, + # cloud_project_id=cloud_project_id, + # cloud_region_id=cloud_region_id, + ) + cert = upload_tls_certificate_and_poll(client=client) + get_secret_by_id(client=client, secret_id=cert.id) + list_all_secrets(client=client) + delete_secret(client=client, secret_id=cert.id) -def upload_tls_cert() -> Secret: - # No need to pass the API key explicitly — it will automatically be read from the GCORE_API_KEY environment variable if omitted - gcore = Gcore(api_key=os.environ.get("GCORE_API_KEY")) +def upload_tls_certificate_and_poll(*, client: Gcore) -> Secret: + print("\n=== UPLOAD TLS CERTIFICATE ===") payload = Payload( certificate="-----BEGIN CERTIFICATE-----\nMIIFkDCCA3igAwIBAgIUPreVqGwsi0hPOrf8tMK3QMLrRxIwDQYJKoZIhvcNAQEL\nBQAwVTEQMA4GA1UECwwHVW5rbm93bjEQMA4GA1UECgwHVW5rbm93bjEQMA4GA1UE\nBwwHVW5rbm93bjEQMA4GA1UECAwHdW5rbm93bjELMAkGA1UEBhMCQVUwHhcNMjUw\nNDA5MTI1OTQ2WhcNMjUwNTA5MTI1OTQ2WjBrMRQwEgYDVQQDDAtleGFtcGxlLmNv\nbTEQMA4GA1UECwwHVW5rbm93bjEQMA4GA1UECgwHVW5rbm93bjEQMA4GA1UEBwwH\nVW5rbm93bjEQMA4GA1UECAwHdW5rbm93bjELMAkGA1UEBhMCQVUwggIiMA0GCSqG\nSIb3DQEBAQUAA4ICDwAwggIKAoICAQDeVaY8IK93hdmwz7i3eXpY9uN57OPfG0ew\nlsL/rsmwYF+TA45l8E0TnvMgXDRW30D58NUIF+gpLhAXit2M7g0BUDeLVVz8TMcP\ntV+bokyemcEW1ju8oVWi1iW3n+qXjCy9tqyjXZrAbKwdZFvv6fcRQreZlsqbMHlD\nLbcACgtU+HzWJKGzU0rIVOMxj0DQBeDTgN8U70ElhA3ZNqgMTXTwwQtZpZX+Oz9g\nuY+WizNYXHNLD70MEcUtHwg+2tgyBs4mHIQmHb7Dp5OfNM/CwYL+udQuRJjxdG5D\nZor1dXEbVRJdNcas7TfeLHqDOnVF7GUW7OCY6evXTVZFIpu8PFDr48/p0XS71yAu\n4G03S4lQqF2P4H+KplfkGTmdRXccRwaKDsBKljSyrBxi2eiuijmerd4V+qCI6zx8\nUDbxsSm3bFeNtikdVR8Kuhegb3lqT5/nP7FSEoDmxh++i4CcNs35czxTKmPnarov\nD4EnoEH7oDZv2YWoJgDfq6MdO1NMJDUyl6SSeF5MxQY6MyardDKHaXu41USMzrtQ\nN03H3opR+jd7h0IvQAuw6hTjcB5kqAFNT8tpG2wuU1iaJtLHcLICxl6ZU7UANYqn\nQaR53YceKILUCza2rsWriCeC6IVXJbmLo/1dlXYKO2u0gPVGT1rEzMlD8zpkkZFH\nPlhCE+1V9wIDAQABo0IwQDAdBgNVHQ4EFgQUZi0hvSfVSiaiZ958WLgioO0v5jww\nHwYDVR0jBBgwFoAUv5ciSGmtNm2gRbj/E84bSvZwjR4wDQYJKoZIhvcNAQELBQAD\nggIBABd/Idv9TBnQMTGuOZQw1uHvoVpjdsbqYuEEtugmOWeo9wzR6wFVsCg0NJ/e\ncj8fC0PQ3pbZjrRGvBXQLFqzxR2ppSkfiG8aRZiUVxS38b2a9Q2YdvneGdqE5g8C\niZF14c07Bl8gLvHb7BJdUfc8cke/A/KtBQHGuaK6Kj+Ub/XgWut50r8wY9afGg2V\n73VAZOlDAinJo8friikvHIOte1NGCwzapUn8Z0x7ZaNEY5zo1DBZxZgL8XTIcLou\nHVI6Sx5PZhNUR9/QlxtVRM+G29pXSj08TRd6C+ZfWqEIHVWoYBbyWlQy14gQ0c75\nBy2FReHQpwVYAeVh5FFHlFVFO7VEP+cT9/eV3JNin+d5FAn9dXaiuFZLPwqXoQG/\nQoe28un8YXhhzKmpXyU8WORAJuIfR99cZ8EBZ9H/O0tW6tAKyMBXia3f58yHnLCg\nCenmp/T8J2B5V0swffLB3dld+hBfPoQgD9liM+iTWCwelJVKlk0V2q7JoJs+CXzt\nPPCrTwYwj0xuBiZtGkP6LKm+zyhwfMQEpka7N7VrZ6Br+sAzxTyjuu01GedjeWIp\nsLsfRughBJtnkG/Yxf8RMh3akwpoMJjxDF0jlamRrKjilwBcoFmsQZahPMEs67fk\nCf0SW+Q+zKez2y1jhBDvLdm97pvjp5IutcSQB69RAtdUpouO\n-----END CERTIFICATE-----\n", certificate_chain="-----BEGIN CERTIFICATE-----\nMIIFizCCA3OgAwIBAgIUGY2OqxQWBvQkvezHv6JjibqgoNEwDQYJKoZIhvcNAQEL\nBQAwVTEQMA4GA1UECwwHVW5rbm93bjEQMA4GA1UECgwHVW5rbm93bjEQMA4GA1UE\nBwwHVW5rbm93bjEQMA4GA1UECAwHdW5rbm93bjELMAkGA1UEBhMCQVUwHhcNMjUw\nNDA5MTI1OTM1WhcNMjUwNTA5MTI1OTM1WjBVMRAwDgYDVQQLDAdVbmtub3duMRAw\nDgYDVQQKDAdVbmtub3duMRAwDgYDVQQHDAdVbmtub3duMRAwDgYDVQQIDAd1bmtu\nb3duMQswCQYDVQQGEwJBVTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB\nAKPOutT9V6/OFjzEU1Q8+U1qTd4ntWCy1OHsopveGyKrPahtsrLY48xvPJklQJJs\naMi5QZje0mNA+Mowuru9WYe8scv0syeNGLyXbmqXhtHNUwh5MfhhOhxkCDjnVHdT\nBVb5grICMWdJzWtSEEAcgAeFwC5UNb99W6pwwIF6N3eaMTbLSm3HCRO8i8kWKw1p\nZcqaNlxyxu7vyFq85t7vyt8m6CkvtEZUKWz2yKZau+A180kVOEViTZBGboGyXWsV\nIopcLOMxWZTbkI69XqMaIQUVr3UTEd5FwR5Rc/GK5ukkfGgpZji+Hj6PGAnCl3Sj\nojRzfpPeJnYA9fepi+JAk+n2tG8/FTp/Hdg1RZ/+NfhRNRi9vzjjbcS/lc164KGg\nqRhk/pmOGTlC1dNYLvJ5O5qrIhz/rhzo1I5PxXaTqTIx/H9RkdnSjkIRu2v6qgZA\nMASayZijLQ+G/dTeXPcU5NQsZyKXQcrVHKviVTxRmJV+UzG+07Uka3IztKFImK9m\nKpW5qcBsVsGgruQyXeg7HVjDgDab6UmDadECpw9eUyNxorIwiRmnrtNLSuWMNznW\n2YgMDXtyByBO9YZrTbmtdbBzFdUIgMhaoEF1P1lrkeSRhP51RdbzndMLuZ/q5uYQ\nzHBXk70O+wmfEwsnEPjuaXFCJPuiYaRFk+GMIBI2liqvAgMBAAGjUzBRMB0GA1Ud\nDgQWBBS/lyJIaa02baBFuP8TzhtK9nCNHjAfBgNVHSMEGDAWgBS/lyJIaa02baBF\nuP8TzhtK9nCNHjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQBG\nYxIeKr1VwkhYh3eDOUsUXx1NNCwNmpOM6LNsRIKO1Z4CKLrjsuYNgSNuPlUietN7\nS60l+j1b9i/oQvKN0YkZAm6Pe/zUJ8NMg11Ussd09ysDbjG2jjc6GXM08neK8TcO\nKYEbml2OVVlE8q+R2EYocdH2GnLwLwjheLU17Hh2tqrlG5QnUTUZQsB55MXMKldP\nAiiPI1sWo4p7XOoCQvyWo0Q1FVQM8XhxzxkvDYilBGGy1Sq3auZ3aOFR9BZeCEIk\n0EgXV94v1PhHZGV4JpzkOwEOTQ3EUAwS1/01jdYwXaxu5vI08dwZMTZg31aMXyfp\n33T5wqZVyqkGmX8jrqmPFCjGJxb/0ZI7uWG8RtjS1XcBPdjq6JPYwVMT0HFhxnOQ\nq5hBeq1dcunFBWPAjXl//qdRMVCuHKfEGBWUoFmpF5C0cBVeR+JSRzn2WSm3Nqet\n/4P2ICu95MIXDidmigdJCkF8vZ9MVwMU6Zk6Bx0Gd/qPwkbEVe89gMsbIKVUzZPV\nTP8iDwdzb4DXsZSZyQaJcYPhLddY9WIInf524/GjpMJSdsIRdKuqISJySvsCID9E\ns+0EYjsjWCc+KLqvQB+AXgZqDtrPFwTQDOLyhaps9dkrBt0CFNN6HP3CTvACiPpm\n4GH/L4MCmbUR6m6oFnd6SXNFJwUETYv3N6iCl/chiw==\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIFizCCA3OgAwIBAgIUGY2OqxQWBvQkvezHv6JjibqgoNEwDQYJKoZIhvcNAQEL\nBQAwVTEQMA4GA1UECwwHVW5rbm93bjEQMA4GA1UECgwHVW5rbm93bjEQMA4GA1UE\nBwwHVW5rbm93bjEQMA4GA1UECAwHdW5rbm93bjELMAkGA1UEBhMCQVUwHhcNMjUw\nNDA5MTI1OTM1WhcNMjUwNTA5MTI1OTM1WjBVMRAwDgYDVQQLDAdVbmtub3duMRAw\nDgYDVQQKDAdVbmtub3duMRAwDgYDVQQHDAdVbmtub3duMRAwDgYDVQQIDAd1bmtu\nb3duMQswCQYDVQQGEwJBVTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB\nAKPOutT9V6/OFjzEU1Q8+U1qTd4ntWCy1OHsopveGyKrPahtsrLY48xvPJklQJJs\naMi5QZje0mNA+Mowuru9WYe8scv0syeNGLyXbmqXhtHNUwh5MfhhOhxkCDjnVHdT\nBVb5grICMWdJzWtSEEAcgAeFwC5UNb99W6pwwIF6N3eaMTbLSm3HCRO8i8kWKw1p\nZcqaNlxyxu7vyFq85t7vyt8m6CkvtEZUKWz2yKZau+A180kVOEViTZBGboGyXWsV\nIopcLOMxWZTbkI69XqMaIQUVr3UTEd5FwR5Rc/GK5ukkfGgpZji+Hj6PGAnCl3Sj\nojRzfpPeJnYA9fepi+JAk+n2tG8/FTp/Hdg1RZ/+NfhRNRi9vzjjbcS/lc164KGg\nqRhk/pmOGTlC1dNYLvJ5O5qrIhz/rhzo1I5PxXaTqTIx/H9RkdnSjkIRu2v6qgZA\nMASayZijLQ+G/dTeXPcU5NQsZyKXQcrVHKviVTxRmJV+UzG+07Uka3IztKFImK9m\nKpW5qcBsVsGgruQyXeg7HVjDgDab6UmDadECpw9eUyNxorIwiRmnrtNLSuWMNznW\n2YgMDXtyByBO9YZrTbmtdbBzFdUIgMhaoEF1P1lrkeSRhP51RdbzndMLuZ/q5uYQ\nzHBXk70O+wmfEwsnEPjuaXFCJPuiYaRFk+GMIBI2liqvAgMBAAGjUzBRMB0GA1Ud\nDgQWBBS/lyJIaa02baBFuP8TzhtK9nCNHjAfBgNVHSMEGDAWgBS/lyJIaa02baBF\nuP8TzhtK9nCNHjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQBG\nYxIeKr1VwkhYh3eDOUsUXx1NNCwNmpOM6LNsRIKO1Z4CKLrjsuYNgSNuPlUietN7\nS60l+j1b9i/oQvKN0YkZAm6Pe/zUJ8NMg11Ussd09ysDbjG2jjc6GXM08neK8TcO\nKYEbml2OVVlE8q+R2EYocdH2GnLwLwjheLU17Hh2tqrlG5QnUTUZQsB55MXMKldP\nAiiPI1sWo4p7XOoCQvyWo0Q1FVQM8XhxzxkvDYilBGGy1Sq3auZ3aOFR9BZeCEIk\n0EgXV94v1PhHZGV4JpzkOwEOTQ3EUAwS1/01jdYwXaxu5vI08dwZMTZg31aMXyfp\n33T5wqZVyqkGmX8jrqmPFCjGJxb/0ZI7uWG8RtjS1XcBPdjq6JPYwVMT0HFhxnOQ\nq5hBeq1dcunFBWPAjXl//qdRMVCuHKfEGBWUoFmpF5C0cBVeR+JSRzn2WSm3Nqet\n/4P2ICu95MIXDidmigdJCkF8vZ9MVwMU6Zk6Bx0Gd/qPwkbEVe89gMsbIKVUzZPV\nTP8iDwdzb4DXsZSZyQaJcYPhLddY9WIInf524/GjpMJSdsIRdKuqISJySvsCID9E\ns+0EYjsjWCc+KLqvQB+AXgZqDtrPFwTQDOLyhaps9dkrBt0CFNN6HP3CTvACiPpm\n4GH/L4MCmbUR6m6oFnd6SXNFJwUETYv3N6iCl/chiw==\n-----END CERTIFICATE-----\n", private_key="-----BEGIN PRIVATE KEY-----\nMIIJQQIBADANBgkqhkiG9w0BAQEFAASCCSswggknAgEAAoICAQDeVaY8IK93hdmw\nz7i3eXpY9uN57OPfG0ewlsL/rsmwYF+TA45l8E0TnvMgXDRW30D58NUIF+gpLhAX\nit2M7g0BUDeLVVz8TMcPtV+bokyemcEW1ju8oVWi1iW3n+qXjCy9tqyjXZrAbKwd\nZFvv6fcRQreZlsqbMHlDLbcACgtU+HzWJKGzU0rIVOMxj0DQBeDTgN8U70ElhA3Z\nNqgMTXTwwQtZpZX+Oz9guY+WizNYXHNLD70MEcUtHwg+2tgyBs4mHIQmHb7Dp5Of\nNM/CwYL+udQuRJjxdG5DZor1dXEbVRJdNcas7TfeLHqDOnVF7GUW7OCY6evXTVZF\nIpu8PFDr48/p0XS71yAu4G03S4lQqF2P4H+KplfkGTmdRXccRwaKDsBKljSyrBxi\n2eiuijmerd4V+qCI6zx8UDbxsSm3bFeNtikdVR8Kuhegb3lqT5/nP7FSEoDmxh++\ni4CcNs35czxTKmPnarovD4EnoEH7oDZv2YWoJgDfq6MdO1NMJDUyl6SSeF5MxQY6\nMyardDKHaXu41USMzrtQN03H3opR+jd7h0IvQAuw6hTjcB5kqAFNT8tpG2wuU1ia\nJtLHcLICxl6ZU7UANYqnQaR53YceKILUCza2rsWriCeC6IVXJbmLo/1dlXYKO2u0\ngPVGT1rEzMlD8zpkkZFHPlhCE+1V9wIDAQABAoICABQhtj/V3SgvQa24XqtCVSPk\nFbyPCM0at8GTd8xWA840KRid5vwaUlMB/qrB8+f1Js7a5zc95ELXoxO2pRFN5ss6\ne7pikYeziLIb7rB1dYbw43fRZdmn1CIT+O3+YsFegG3x3Kzy3Ksqy+TiFvm2JNh2\nbZB7LmpMl9ZPjTVJiOAkmzfQBlI/VaimbbypkilOjEkqb0itJ1LMeRfffT+WmI2f\niosPVXG5Of0PSvighGwuUYQ3mcW+Ktfx6jm4GtZgy7QjZeCGVfp6XIFlyKAuYdGg\nevLGOau3VVqbWN7D8mi9VGswjjD5+SflaCHPc8SlPzwLA9qPZ9BNdjp5WUJQmypY\nRZ+aa2WueJuH6KaL0LjzeOPYjNMpViFzxiV9lCVqpcH1snQT9YRqvvhcJSEmLOIY\nJSNggYsRf3DpPDOTsDu/hsBXgYlY2mWcCU53v4tETKRetKa9fAgyNy18umVQenOx\n9WAR6FOLGfHSxhItnxIni2dA78YfVU0wHl7Yro0zvhF5EtQ7mDBsu1AoUXE48F5l\nFcrjamvFMpbKmHVHMPhP1oPbVnYl4MVsTBIBfZRbXC1xhcXBSCQsHHZ5Gc+8vQnE\n4tPupPnuc3487GF/r9ZRaEWg+ZSJNoP6EEDpFQwIboqSPWBAprVvHnooRv86ylSQ\n5ZplALcDmBJPIxKidsatAoIBAQD364WZ0faDCvtlJrsj8Waez3HjimqSPL97CY9K\nMuWABB9mIGmHSctj8vK3n/7l5IopmsFLsZ46DEUAvtNX5siskFxW24iBhoiVsYVq\nYCv7lHtMSmvevLnG40gInoR+Tb399OSciYPB2wIUN4EmKpdJWWmLamRwNdn3A3d7\noh/FKakX1xgxR5QWKp2A5AUOuQuqyrCUjH0WBTyErVi9JfDDjBunvlI88uTRxd8+\n4yak4iMpBstZBfXfPcO61vnjb/TeT0QF2l/L1jN8n/y1dJgvIVIux+wttW75CyQ+\n2dUlMbnj4lQ2tNKbBaX9F/4zSe8WcGHedmZyY230BJPoqQ+lAoIBAQDllKjk9T4y\ns5jEMhiYn37PxqVFRGZ7tzm7XEiFHU1IputTYjSwX0WTF/9pSveOu3CTgpoTM4ca\nBn09boyw8cvSwn01YE95/29SVuJWuedFdcTVPS2QdmL0WUk9aUb4vUDlytXIfB25\nwvj7NLq+kpSqX2yL8HeRJh5ilUuc2LdViRCzeoGWBiIdCqO1Rp37ZYJw1ll4IiRQ\n1g78zz8YQ7VPFGtRmXhLVpqYlfRJEAXEdZqovVP3TswnbpjzpYtWP/RbQtfRdrfg\nhAbPCQd14eeCpgTslGbF/AvJLK2FyVdjSgkYF4kQOivHFa6gHUtA7vnh8KifCfq6\nqpehIwkT6txrAoIBAF0+pwQglS/aTI1R0OcG30rxyOsE32pbEMWs5cjJdcn9QvX9\nUNOCbM4NzT5FHfDHUTOuse7PQiyg+r76BrEz6twEe8ZrUV8uA2cR2pUU0NYRlYIv\nJ30hzFnCmBAt1rVOdhvzJJy/l9+siI6kBZ4ePMJor6qw/E+74VvnYOQOKRbVwXRn\nAQFf45Gmu5CDsmdEL+Av1dQ5Nr62f3mDXTHe+DwEEU645WNpE6jWXE3hz2IKb6D5\nnjfAZyZsq5Y1Ts09CYMnmvT8mjUnPjwsTDbPQIHRbYCMzwauC6v9hcdh1Knllv3f\n3T6qKeAGctTLVl9h+ludLyIltAhn3y39HshN9cUCggEAfaJUqrbqSqStvPANNbhS\nlTGHz9gWnS0vkrB1nyLh4Bg4P3FGlB4O3OgNBXnY72rzuEWIO2m/TSav8qZEp7Aq\ncjOsgUErPP/j05NoWT1yqjhAdtD71kpy7HTP96NdC1HF6fqN8yC4w6dGyXGZoCBm\n6rU9mXcGd4/8oMZCkpql+VEAqrcnownIMUxZOiJi4egy8bzbSTql1PbPTNm9FXI4\nDgaGlCkAA3ppL4cgH7t87H3PHPg+st+UKSAE45B8J77n4ek6YY4uIdceQr4WLxRo\ntL5Vg4HSnBXJ/VVNwCDmiZdCUsTOZOrwegoLfeOKAwbECDjCjgXQB8bDI5MgrJ2h\n8QKCAQAZyS7bzeBfh10fq3jLEuNcqFhc6NhKm9bZeFs73r8rB3amnqDa/qlZcPcu\nq29g21f5G64ujODUeKvp/LeeGC/NOu0zsceyfRM/RAZZi5xU2J+CHZd3IoX2juqU\nLzSBu6NsrS3XsCXmbKYozqslNBuLDLNFVFFLziz43CF6kH12KLSZbHJP2tt7Ccw8\n55q9V7xR/Pn1gqM6xCXjtV7o88NzOOoiBmnoWBJB7RpIcGb56CYpLF0ed4rxWeNX\nIBAGuZq54ms8OU+OjfgdLqttRbXnj1EtN1u6/zs5s5gE+SgdfmwyYtgBjpka/YmA\nkf90Czon6WsRXM2o++kWrtXIa8IR\n-----END PRIVATE KEY-----\n", ) - secret = gcore.cloud.secrets.upload_tls_certificate_and_poll( - name="sdk-example-secret", - payload=payload, - # Project ID and Region ID are also read from environment variables if not provided explicitly - project_id=int(os.environ.get("GCORE_CLOUD_PROJECT_ID", "100")), - region_id=int(os.environ.get("GCORE_CLOUD_REGION_ID", "76")), - ) - - print("\n=== CREATE SECRET ===") - print(f"Secret ID: {secret.id}, Name: {secret.name}") - print("=====================") + secret = client.cloud.secrets.upload_tls_certificate_and_poll(name="gcore-python-example", payload=payload) + print(f"Secret ID: {secret.id}, name: {secret.name}") + print("========================") return secret -def get_secret_by_id(secret_id: str) -> Secret: - gcore = Gcore() - secret = gcore.cloud.secrets.get(secret_id=secret_id) - +def get_secret_by_id(*, client: Gcore, secret_id: str) -> Secret: print("\n=== GET SECRET BY ID ===") - print(f"Secret ID: {secret.id}, Name: {secret.name}") + secret = client.cloud.secrets.get(secret_id=secret_id) + print(f"Secret ID: {secret.id}, name: {secret.name}") print("========================") return secret -def list_all_secrets() -> List[Secret]: - gcore = Gcore() - all_secrets = gcore.cloud.secrets.list() - +def list_all_secrets(*, client: Gcore) -> List[Secret]: print("\n=== LIST ALL SECRETS ===") + all_secrets = client.cloud.secrets.list() # TODO remove .results after pagination for count, secret in enumerate(all_secrets.results, 1): - print(f" {count}. Secret ID: {secret.id}, Name: {secret.name}") + print(f" {count}. Secret ID: {secret.id}, name: {secret.name}") print("========================") return all_secrets.results -def delete_secret(secret_id: str) -> None: - gcore = Gcore() - gcore.cloud.secrets.delete(secret_id=secret_id) - +def delete_secret(*, client: Gcore, secret_id: str) -> None: print("\n=== DELETE SECRET ===") + client.cloud.secrets.delete(secret_id=secret_id) print(f"Secret deleted successfully: {secret_id}") - print("=====================") + print("========================") if __name__ == "__main__": diff --git a/examples/cloud/secrets_async.py b/examples/cloud/secrets_async.py index 9f6451ec..fe7f16ca 100644 --- a/examples/cloud/secrets_async.py +++ b/examples/cloud/secrets_async.py @@ -1,11 +1,8 @@ -import os import asyncio from typing import List from gcore import AsyncGcore -from gcore.types.cloud import ( - Secret, -) +from gcore.types.cloud import Secret from gcore.types.cloud.secret_upload_tls_certificate_params import Payload @@ -17,74 +14,54 @@ async def main() -> None: # TODO set cloud region ID before running # cloud_region_id = os.environ["GCORE_CLOUD_REGION_ID"] - cert = await upload_tls_cert() - await get_secret_by_id(cert.id) - await list_all_secrets() - await delete_secret(cert.id) + client = AsyncGcore( + # No need to explicitly pass to AsyncGcore constructor if using environment variables + # api_key=api_key, + # cloud_project_id=cloud_project_id, + # cloud_region_id=cloud_region_id, + ) + cert = await upload_tls_certificate_and_poll(client=client) + await get_secret_by_id(client=client, secret_id=cert.id) + await list_all_secrets(client=client) + await delete_secret(client=client, secret_id=cert.id) -async def upload_tls_cert() -> Secret: - # No need to pass the API key explicitly — it will automatically be read from the GCORE_API_KEY environment variable if omitted - gcore = AsyncGcore(api_key=os.environ.get("GCORE_API_KEY")) +async def upload_tls_certificate_and_poll(*, client: AsyncGcore) -> Secret: + print("\n=== UPLOAD TLS CERTIFICATE ===") payload = Payload( certificate="-----BEGIN CERTIFICATE-----\nMIIFkDCCA3igAwIBAgIUPreVqGwsi0hPOrf8tMK3QMLrRxIwDQYJKoZIhvcNAQEL\nBQAwVTEQMA4GA1UECwwHVW5rbm93bjEQMA4GA1UECgwHVW5rbm93bjEQMA4GA1UE\nBwwHVW5rbm93bjEQMA4GA1UECAwHdW5rbm93bjELMAkGA1UEBhMCQVUwHhcNMjUw\nNDA5MTI1OTQ2WhcNMjUwNTA5MTI1OTQ2WjBrMRQwEgYDVQQDDAtleGFtcGxlLmNv\nbTEQMA4GA1UECwwHVW5rbm93bjEQMA4GA1UECgwHVW5rbm93bjEQMA4GA1UEBwwH\nVW5rbm93bjEQMA4GA1UECAwHdW5rbm93bjELMAkGA1UEBhMCQVUwggIiMA0GCSqG\nSIb3DQEBAQUAA4ICDwAwggIKAoICAQDeVaY8IK93hdmwz7i3eXpY9uN57OPfG0ew\nlsL/rsmwYF+TA45l8E0TnvMgXDRW30D58NUIF+gpLhAXit2M7g0BUDeLVVz8TMcP\ntV+bokyemcEW1ju8oVWi1iW3n+qXjCy9tqyjXZrAbKwdZFvv6fcRQreZlsqbMHlD\nLbcACgtU+HzWJKGzU0rIVOMxj0DQBeDTgN8U70ElhA3ZNqgMTXTwwQtZpZX+Oz9g\nuY+WizNYXHNLD70MEcUtHwg+2tgyBs4mHIQmHb7Dp5OfNM/CwYL+udQuRJjxdG5D\nZor1dXEbVRJdNcas7TfeLHqDOnVF7GUW7OCY6evXTVZFIpu8PFDr48/p0XS71yAu\n4G03S4lQqF2P4H+KplfkGTmdRXccRwaKDsBKljSyrBxi2eiuijmerd4V+qCI6zx8\nUDbxsSm3bFeNtikdVR8Kuhegb3lqT5/nP7FSEoDmxh++i4CcNs35czxTKmPnarov\nD4EnoEH7oDZv2YWoJgDfq6MdO1NMJDUyl6SSeF5MxQY6MyardDKHaXu41USMzrtQ\nN03H3opR+jd7h0IvQAuw6hTjcB5kqAFNT8tpG2wuU1iaJtLHcLICxl6ZU7UANYqn\nQaR53YceKILUCza2rsWriCeC6IVXJbmLo/1dlXYKO2u0gPVGT1rEzMlD8zpkkZFH\nPlhCE+1V9wIDAQABo0IwQDAdBgNVHQ4EFgQUZi0hvSfVSiaiZ958WLgioO0v5jww\nHwYDVR0jBBgwFoAUv5ciSGmtNm2gRbj/E84bSvZwjR4wDQYJKoZIhvcNAQELBQAD\nggIBABd/Idv9TBnQMTGuOZQw1uHvoVpjdsbqYuEEtugmOWeo9wzR6wFVsCg0NJ/e\ncj8fC0PQ3pbZjrRGvBXQLFqzxR2ppSkfiG8aRZiUVxS38b2a9Q2YdvneGdqE5g8C\niZF14c07Bl8gLvHb7BJdUfc8cke/A/KtBQHGuaK6Kj+Ub/XgWut50r8wY9afGg2V\n73VAZOlDAinJo8friikvHIOte1NGCwzapUn8Z0x7ZaNEY5zo1DBZxZgL8XTIcLou\nHVI6Sx5PZhNUR9/QlxtVRM+G29pXSj08TRd6C+ZfWqEIHVWoYBbyWlQy14gQ0c75\nBy2FReHQpwVYAeVh5FFHlFVFO7VEP+cT9/eV3JNin+d5FAn9dXaiuFZLPwqXoQG/\nQoe28un8YXhhzKmpXyU8WORAJuIfR99cZ8EBZ9H/O0tW6tAKyMBXia3f58yHnLCg\nCenmp/T8J2B5V0swffLB3dld+hBfPoQgD9liM+iTWCwelJVKlk0V2q7JoJs+CXzt\nPPCrTwYwj0xuBiZtGkP6LKm+zyhwfMQEpka7N7VrZ6Br+sAzxTyjuu01GedjeWIp\nsLsfRughBJtnkG/Yxf8RMh3akwpoMJjxDF0jlamRrKjilwBcoFmsQZahPMEs67fk\nCf0SW+Q+zKez2y1jhBDvLdm97pvjp5IutcSQB69RAtdUpouO\n-----END CERTIFICATE-----\n", certificate_chain="-----BEGIN CERTIFICATE-----\nMIIFizCCA3OgAwIBAgIUGY2OqxQWBvQkvezHv6JjibqgoNEwDQYJKoZIhvcNAQEL\nBQAwVTEQMA4GA1UECwwHVW5rbm93bjEQMA4GA1UECgwHVW5rbm93bjEQMA4GA1UE\nBwwHVW5rbm93bjEQMA4GA1UECAwHdW5rbm93bjELMAkGA1UEBhMCQVUwHhcNMjUw\nNDA5MTI1OTM1WhcNMjUwNTA5MTI1OTM1WjBVMRAwDgYDVQQLDAdVbmtub3duMRAw\nDgYDVQQKDAdVbmtub3duMRAwDgYDVQQHDAdVbmtub3duMRAwDgYDVQQIDAd1bmtu\nb3duMQswCQYDVQQGEwJBVTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB\nAKPOutT9V6/OFjzEU1Q8+U1qTd4ntWCy1OHsopveGyKrPahtsrLY48xvPJklQJJs\naMi5QZje0mNA+Mowuru9WYe8scv0syeNGLyXbmqXhtHNUwh5MfhhOhxkCDjnVHdT\nBVb5grICMWdJzWtSEEAcgAeFwC5UNb99W6pwwIF6N3eaMTbLSm3HCRO8i8kWKw1p\nZcqaNlxyxu7vyFq85t7vyt8m6CkvtEZUKWz2yKZau+A180kVOEViTZBGboGyXWsV\nIopcLOMxWZTbkI69XqMaIQUVr3UTEd5FwR5Rc/GK5ukkfGgpZji+Hj6PGAnCl3Sj\nojRzfpPeJnYA9fepi+JAk+n2tG8/FTp/Hdg1RZ/+NfhRNRi9vzjjbcS/lc164KGg\nqRhk/pmOGTlC1dNYLvJ5O5qrIhz/rhzo1I5PxXaTqTIx/H9RkdnSjkIRu2v6qgZA\nMASayZijLQ+G/dTeXPcU5NQsZyKXQcrVHKviVTxRmJV+UzG+07Uka3IztKFImK9m\nKpW5qcBsVsGgruQyXeg7HVjDgDab6UmDadECpw9eUyNxorIwiRmnrtNLSuWMNznW\n2YgMDXtyByBO9YZrTbmtdbBzFdUIgMhaoEF1P1lrkeSRhP51RdbzndMLuZ/q5uYQ\nzHBXk70O+wmfEwsnEPjuaXFCJPuiYaRFk+GMIBI2liqvAgMBAAGjUzBRMB0GA1Ud\nDgQWBBS/lyJIaa02baBFuP8TzhtK9nCNHjAfBgNVHSMEGDAWgBS/lyJIaa02baBF\nuP8TzhtK9nCNHjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQBG\nYxIeKr1VwkhYh3eDOUsUXx1NNCwNmpOM6LNsRIKO1Z4CKLrjsuYNgSNuPlUietN7\nS60l+j1b9i/oQvKN0YkZAm6Pe/zUJ8NMg11Ussd09ysDbjG2jjc6GXM08neK8TcO\nKYEbml2OVVlE8q+R2EYocdH2GnLwLwjheLU17Hh2tqrlG5QnUTUZQsB55MXMKldP\nAiiPI1sWo4p7XOoCQvyWo0Q1FVQM8XhxzxkvDYilBGGy1Sq3auZ3aOFR9BZeCEIk\n0EgXV94v1PhHZGV4JpzkOwEOTQ3EUAwS1/01jdYwXaxu5vI08dwZMTZg31aMXyfp\n33T5wqZVyqkGmX8jrqmPFCjGJxb/0ZI7uWG8RtjS1XcBPdjq6JPYwVMT0HFhxnOQ\nq5hBeq1dcunFBWPAjXl//qdRMVCuHKfEGBWUoFmpF5C0cBVeR+JSRzn2WSm3Nqet\n/4P2ICu95MIXDidmigdJCkF8vZ9MVwMU6Zk6Bx0Gd/qPwkbEVe89gMsbIKVUzZPV\nTP8iDwdzb4DXsZSZyQaJcYPhLddY9WIInf524/GjpMJSdsIRdKuqISJySvsCID9E\ns+0EYjsjWCc+KLqvQB+AXgZqDtrPFwTQDOLyhaps9dkrBt0CFNN6HP3CTvACiPpm\n4GH/L4MCmbUR6m6oFnd6SXNFJwUETYv3N6iCl/chiw==\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIFizCCA3OgAwIBAgIUGY2OqxQWBvQkvezHv6JjibqgoNEwDQYJKoZIhvcNAQEL\nBQAwVTEQMA4GA1UECwwHVW5rbm93bjEQMA4GA1UECgwHVW5rbm93bjEQMA4GA1UE\nBwwHVW5rbm93bjEQMA4GA1UECAwHdW5rbm93bjELMAkGA1UEBhMCQVUwHhcNMjUw\nNDA5MTI1OTM1WhcNMjUwNTA5MTI1OTM1WjBVMRAwDgYDVQQLDAdVbmtub3duMRAw\nDgYDVQQKDAdVbmtub3duMRAwDgYDVQQHDAdVbmtub3duMRAwDgYDVQQIDAd1bmtu\nb3duMQswCQYDVQQGEwJBVTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB\nAKPOutT9V6/OFjzEU1Q8+U1qTd4ntWCy1OHsopveGyKrPahtsrLY48xvPJklQJJs\naMi5QZje0mNA+Mowuru9WYe8scv0syeNGLyXbmqXhtHNUwh5MfhhOhxkCDjnVHdT\nBVb5grICMWdJzWtSEEAcgAeFwC5UNb99W6pwwIF6N3eaMTbLSm3HCRO8i8kWKw1p\nZcqaNlxyxu7vyFq85t7vyt8m6CkvtEZUKWz2yKZau+A180kVOEViTZBGboGyXWsV\nIopcLOMxWZTbkI69XqMaIQUVr3UTEd5FwR5Rc/GK5ukkfGgpZji+Hj6PGAnCl3Sj\nojRzfpPeJnYA9fepi+JAk+n2tG8/FTp/Hdg1RZ/+NfhRNRi9vzjjbcS/lc164KGg\nqRhk/pmOGTlC1dNYLvJ5O5qrIhz/rhzo1I5PxXaTqTIx/H9RkdnSjkIRu2v6qgZA\nMASayZijLQ+G/dTeXPcU5NQsZyKXQcrVHKviVTxRmJV+UzG+07Uka3IztKFImK9m\nKpW5qcBsVsGgruQyXeg7HVjDgDab6UmDadECpw9eUyNxorIwiRmnrtNLSuWMNznW\n2YgMDXtyByBO9YZrTbmtdbBzFdUIgMhaoEF1P1lrkeSRhP51RdbzndMLuZ/q5uYQ\nzHBXk70O+wmfEwsnEPjuaXFCJPuiYaRFk+GMIBI2liqvAgMBAAGjUzBRMB0GA1Ud\nDgQWBBS/lyJIaa02baBFuP8TzhtK9nCNHjAfBgNVHSMEGDAWgBS/lyJIaa02baBF\nuP8TzhtK9nCNHjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQBG\nYxIeKr1VwkhYh3eDOUsUXx1NNCwNmpOM6LNsRIKO1Z4CKLrjsuYNgSNuPlUietN7\nS60l+j1b9i/oQvKN0YkZAm6Pe/zUJ8NMg11Ussd09ysDbjG2jjc6GXM08neK8TcO\nKYEbml2OVVlE8q+R2EYocdH2GnLwLwjheLU17Hh2tqrlG5QnUTUZQsB55MXMKldP\nAiiPI1sWo4p7XOoCQvyWo0Q1FVQM8XhxzxkvDYilBGGy1Sq3auZ3aOFR9BZeCEIk\n0EgXV94v1PhHZGV4JpzkOwEOTQ3EUAwS1/01jdYwXaxu5vI08dwZMTZg31aMXyfp\n33T5wqZVyqkGmX8jrqmPFCjGJxb/0ZI7uWG8RtjS1XcBPdjq6JPYwVMT0HFhxnOQ\nq5hBeq1dcunFBWPAjXl//qdRMVCuHKfEGBWUoFmpF5C0cBVeR+JSRzn2WSm3Nqet\n/4P2ICu95MIXDidmigdJCkF8vZ9MVwMU6Zk6Bx0Gd/qPwkbEVe89gMsbIKVUzZPV\nTP8iDwdzb4DXsZSZyQaJcYPhLddY9WIInf524/GjpMJSdsIRdKuqISJySvsCID9E\ns+0EYjsjWCc+KLqvQB+AXgZqDtrPFwTQDOLyhaps9dkrBt0CFNN6HP3CTvACiPpm\n4GH/L4MCmbUR6m6oFnd6SXNFJwUETYv3N6iCl/chiw==\n-----END CERTIFICATE-----\n", private_key="-----BEGIN PRIVATE KEY-----\nMIIJQQIBADANBgkqhkiG9w0BAQEFAASCCSswggknAgEAAoICAQDeVaY8IK93hdmw\nz7i3eXpY9uN57OPfG0ewlsL/rsmwYF+TA45l8E0TnvMgXDRW30D58NUIF+gpLhAX\nit2M7g0BUDeLVVz8TMcPtV+bokyemcEW1ju8oVWi1iW3n+qXjCy9tqyjXZrAbKwd\nZFvv6fcRQreZlsqbMHlDLbcACgtU+HzWJKGzU0rIVOMxj0DQBeDTgN8U70ElhA3Z\nNqgMTXTwwQtZpZX+Oz9guY+WizNYXHNLD70MEcUtHwg+2tgyBs4mHIQmHb7Dp5Of\nNM/CwYL+udQuRJjxdG5DZor1dXEbVRJdNcas7TfeLHqDOnVF7GUW7OCY6evXTVZF\nIpu8PFDr48/p0XS71yAu4G03S4lQqF2P4H+KplfkGTmdRXccRwaKDsBKljSyrBxi\n2eiuijmerd4V+qCI6zx8UDbxsSm3bFeNtikdVR8Kuhegb3lqT5/nP7FSEoDmxh++\ni4CcNs35czxTKmPnarovD4EnoEH7oDZv2YWoJgDfq6MdO1NMJDUyl6SSeF5MxQY6\nMyardDKHaXu41USMzrtQN03H3opR+jd7h0IvQAuw6hTjcB5kqAFNT8tpG2wuU1ia\nJtLHcLICxl6ZU7UANYqnQaR53YceKILUCza2rsWriCeC6IVXJbmLo/1dlXYKO2u0\ngPVGT1rEzMlD8zpkkZFHPlhCE+1V9wIDAQABAoICABQhtj/V3SgvQa24XqtCVSPk\nFbyPCM0at8GTd8xWA840KRid5vwaUlMB/qrB8+f1Js7a5zc95ELXoxO2pRFN5ss6\ne7pikYeziLIb7rB1dYbw43fRZdmn1CIT+O3+YsFegG3x3Kzy3Ksqy+TiFvm2JNh2\nbZB7LmpMl9ZPjTVJiOAkmzfQBlI/VaimbbypkilOjEkqb0itJ1LMeRfffT+WmI2f\niosPVXG5Of0PSvighGwuUYQ3mcW+Ktfx6jm4GtZgy7QjZeCGVfp6XIFlyKAuYdGg\nevLGOau3VVqbWN7D8mi9VGswjjD5+SflaCHPc8SlPzwLA9qPZ9BNdjp5WUJQmypY\nRZ+aa2WueJuH6KaL0LjzeOPYjNMpViFzxiV9lCVqpcH1snQT9YRqvvhcJSEmLOIY\nJSNggYsRf3DpPDOTsDu/hsBXgYlY2mWcCU53v4tETKRetKa9fAgyNy18umVQenOx\n9WAR6FOLGfHSxhItnxIni2dA78YfVU0wHl7Yro0zvhF5EtQ7mDBsu1AoUXE48F5l\nFcrjamvFMpbKmHVHMPhP1oPbVnYl4MVsTBIBfZRbXC1xhcXBSCQsHHZ5Gc+8vQnE\n4tPupPnuc3487GF/r9ZRaEWg+ZSJNoP6EEDpFQwIboqSPWBAprVvHnooRv86ylSQ\n5ZplALcDmBJPIxKidsatAoIBAQD364WZ0faDCvtlJrsj8Waez3HjimqSPL97CY9K\nMuWABB9mIGmHSctj8vK3n/7l5IopmsFLsZ46DEUAvtNX5siskFxW24iBhoiVsYVq\nYCv7lHtMSmvevLnG40gInoR+Tb399OSciYPB2wIUN4EmKpdJWWmLamRwNdn3A3d7\noh/FKakX1xgxR5QWKp2A5AUOuQuqyrCUjH0WBTyErVi9JfDDjBunvlI88uTRxd8+\n4yak4iMpBstZBfXfPcO61vnjb/TeT0QF2l/L1jN8n/y1dJgvIVIux+wttW75CyQ+\n2dUlMbnj4lQ2tNKbBaX9F/4zSe8WcGHedmZyY230BJPoqQ+lAoIBAQDllKjk9T4y\ns5jEMhiYn37PxqVFRGZ7tzm7XEiFHU1IputTYjSwX0WTF/9pSveOu3CTgpoTM4ca\nBn09boyw8cvSwn01YE95/29SVuJWuedFdcTVPS2QdmL0WUk9aUb4vUDlytXIfB25\nwvj7NLq+kpSqX2yL8HeRJh5ilUuc2LdViRCzeoGWBiIdCqO1Rp37ZYJw1ll4IiRQ\n1g78zz8YQ7VPFGtRmXhLVpqYlfRJEAXEdZqovVP3TswnbpjzpYtWP/RbQtfRdrfg\nhAbPCQd14eeCpgTslGbF/AvJLK2FyVdjSgkYF4kQOivHFa6gHUtA7vnh8KifCfq6\nqpehIwkT6txrAoIBAF0+pwQglS/aTI1R0OcG30rxyOsE32pbEMWs5cjJdcn9QvX9\nUNOCbM4NzT5FHfDHUTOuse7PQiyg+r76BrEz6twEe8ZrUV8uA2cR2pUU0NYRlYIv\nJ30hzFnCmBAt1rVOdhvzJJy/l9+siI6kBZ4ePMJor6qw/E+74VvnYOQOKRbVwXRn\nAQFf45Gmu5CDsmdEL+Av1dQ5Nr62f3mDXTHe+DwEEU645WNpE6jWXE3hz2IKb6D5\nnjfAZyZsq5Y1Ts09CYMnmvT8mjUnPjwsTDbPQIHRbYCMzwauC6v9hcdh1Knllv3f\n3T6qKeAGctTLVl9h+ludLyIltAhn3y39HshN9cUCggEAfaJUqrbqSqStvPANNbhS\nlTGHz9gWnS0vkrB1nyLh4Bg4P3FGlB4O3OgNBXnY72rzuEWIO2m/TSav8qZEp7Aq\ncjOsgUErPP/j05NoWT1yqjhAdtD71kpy7HTP96NdC1HF6fqN8yC4w6dGyXGZoCBm\n6rU9mXcGd4/8oMZCkpql+VEAqrcnownIMUxZOiJi4egy8bzbSTql1PbPTNm9FXI4\nDgaGlCkAA3ppL4cgH7t87H3PHPg+st+UKSAE45B8J77n4ek6YY4uIdceQr4WLxRo\ntL5Vg4HSnBXJ/VVNwCDmiZdCUsTOZOrwegoLfeOKAwbECDjCjgXQB8bDI5MgrJ2h\n8QKCAQAZyS7bzeBfh10fq3jLEuNcqFhc6NhKm9bZeFs73r8rB3amnqDa/qlZcPcu\nq29g21f5G64ujODUeKvp/LeeGC/NOu0zsceyfRM/RAZZi5xU2J+CHZd3IoX2juqU\nLzSBu6NsrS3XsCXmbKYozqslNBuLDLNFVFFLziz43CF6kH12KLSZbHJP2tt7Ccw8\n55q9V7xR/Pn1gqM6xCXjtV7o88NzOOoiBmnoWBJB7RpIcGb56CYpLF0ed4rxWeNX\nIBAGuZq54ms8OU+OjfgdLqttRbXnj1EtN1u6/zs5s5gE+SgdfmwyYtgBjpka/YmA\nkf90Czon6WsRXM2o++kWrtXIa8IR\n-----END PRIVATE KEY-----\n", ) - secret = await gcore.cloud.secrets.upload_tls_certificate_and_poll( - name="sdk-example-secret", - payload=payload, - # Project ID and Region ID are also read from environment variables if not provided explicitly - project_id=int(os.environ.get("GCORE_CLOUD_PROJECT_ID", "100")), - region_id=int(os.environ.get("GCORE_CLOUD_REGION_ID", "76")), - ) - - print("\n=== CREATE SECRET ===") - print(f"Secret ID: {secret.id}, Name: {secret.name}") - print("=====================") + secret = await client.cloud.secrets.upload_tls_certificate_and_poll(name="gcore-python-example", payload=payload) + print(f"Secret ID: {secret.id}, name: {secret.name}") + print("========================") return secret -async def get_secret_by_id(secret_id: str) -> Secret: - gcore = AsyncGcore() - secret = await gcore.cloud.secrets.get( - secret_id=secret_id, - project_id=int(os.environ.get("GCORE_CLOUD_PROJECT_ID", "100")), - region_id=int(os.environ.get("GCORE_CLOUD_REGION_ID", "76")), - ) - +async def get_secret_by_id(*, client: AsyncGcore, secret_id: str) -> Secret: print("\n=== GET SECRET BY ID ===") - print(f"Secret ID: {secret.id}, Name: {secret.name}") + secret = await client.cloud.secrets.get(secret_id=secret_id) + print(f"Secret ID: {secret.id}, name: {secret.name}") print("========================") return secret -async def list_all_secrets() -> List[Secret]: - gcore = AsyncGcore() - all_secrets = await gcore.cloud.secrets.list() - +async def list_all_secrets(*, client: AsyncGcore) -> List[Secret]: print("\n=== LIST ALL SECRETS ===") - count = 1 - # TODO async for and remove .results after pagination - for secret in all_secrets.results: - print(f" {count}. Secret ID: {secret.id}, Name: {secret.name}") - count += 1 + all_secrets = await client.cloud.secrets.list() + for count, secret in enumerate(all_secrets.results, 1): + print(f" {count}. Secret ID: {secret.id}, name: {secret.name}") print("========================") return all_secrets.results -async def delete_secret(secret_id: str) -> None: - gcore = AsyncGcore() - await gcore.cloud.secrets.delete( - secret_id=secret_id, - project_id=int(os.environ.get("GCORE_CLOUD_PROJECT_ID", "100")), - region_id=int(os.environ.get("GCORE_CLOUD_REGION_ID", "76")), - ) - +async def delete_secret(*, client: AsyncGcore, secret_id: str) -> None: print("\n=== DELETE SECRET ===") + await client.cloud.secrets.delete(secret_id=secret_id) print(f"Secret deleted successfully: {secret_id}") - print("=====================") + print("========================") if __name__ == "__main__": diff --git a/examples/cloud/ssh_keys.py b/examples/cloud/ssh_keys.py index 230c976a..231111d0 100644 --- a/examples/cloud/ssh_keys.py +++ b/examples/cloud/ssh_keys.py @@ -1,5 +1,3 @@ -import os - from gcore import Gcore from gcore.pagination import SyncOffsetPage from gcore.types.cloud import SSHKey, SSHKeyCreated @@ -13,65 +11,58 @@ def main() -> None: # TODO set cloud region ID before running # cloud_region_id = os.environ["GCORE_CLOUD_REGION_ID"] - # Follow the order: create, list, get, update, delete - new_ssh_key = create_new_ssh_key() - list_all_ssh_keys() - get_ssh_key_by_id(new_ssh_key.id) - update_ssh_key(ssh_key_id=new_ssh_key.id) - delete_ssh_key(ssh_key_id=new_ssh_key.id) - - -def get_ssh_key_by_id(ssh_key_id: str) -> SSHKey: - # No need to pass the API key explicitly — it will automatically be read from the GCORE_API_KEY environment variable if omitted - gcore = Gcore(api_key=os.environ.get("GCORE_API_KEY"), base_url=os.environ.get("GCORE_API_URL")) - ssh_key = gcore.cloud.ssh_keys.get(ssh_key_id=ssh_key_id) - - print("\n=== GET SSH KEY BY ID ===") - print(f"SSH Key ID: {ssh_key.id}, Name: {ssh_key.name}, Fingerprint: {ssh_key.fingerprint}") - print("==========================") + gcore = Gcore( + # No need to explicitly pass to Gcore constructor if using environment variables + # api_key=api_key, + # cloud_project_id=cloud_project_id, + # cloud_region_id=cloud_region_id, + ) + + ssh_key = create_ssh_key(client=gcore) + list_ssh_keys(client=gcore) + get_ssh_key(client=gcore, ssh_key_id=ssh_key.id) + update_ssh_key(client=gcore, ssh_key_id=ssh_key.id) + delete_ssh_key(client=gcore, ssh_key_id=ssh_key.id) + + +def create_ssh_key(*, client: Gcore) -> SSHKeyCreated: + print("\n=== CREATE SSH KEY ===") + ssh_key = client.cloud.ssh_keys.create(name="gcore-go-example") + print(f"Created SSH key: ID={ssh_key.id}, name={ssh_key.name}") + print("========================") return ssh_key -def list_all_ssh_keys() -> SyncOffsetPage[SSHKey]: - gcore = Gcore(api_key=os.environ.get("GCORE_API_KEY"), base_url=os.environ.get("GCORE_API_URL")) - all_ssh_keys = gcore.cloud.ssh_keys.list() - - print("\n=== LIST ALL SSH KEYS ===") - for count, ssh_key in enumerate(all_ssh_keys, 1): - print(f" {count}. SSH Key ID: {ssh_key.id}, Name: {ssh_key.name}") - print("==========================") - return all_ssh_keys +def list_ssh_keys(*, client: Gcore) -> SyncOffsetPage[SSHKey]: + print("\n=== LIST SSH KEYS ===") + ssh_keys = client.cloud.ssh_keys.list() + for count, ssh_key in enumerate(ssh_keys, 1): + print(f" {count}. SSH key: ID={ssh_key.id}, name={ssh_key.name}") + print("========================") + return ssh_keys -def create_new_ssh_key() -> SSHKeyCreated: - gcore = Gcore(api_key=os.environ.get("GCORE_API_KEY"), base_url=os.environ.get("GCORE_API_URL")) - # Sample SSH key values - ssh_key_name = "Example SSH Key" - new_ssh_key = gcore.cloud.ssh_keys.create(name=ssh_key_name) - - print("\n=== CREATE NEW SSH KEY ===") - print(f"SSH Key ID: {new_ssh_key.id}, Name: {new_ssh_key.name}") - print("===========================") - return new_ssh_key - +def get_ssh_key(*, client: Gcore, ssh_key_id: str) -> SSHKey: + print("\n=== GET SSH KEY ===") + ssh_key = client.cloud.ssh_keys.get(ssh_key_id=ssh_key_id) + print(f"SSH key: ID={ssh_key.id}, name={ssh_key.name}, fingerprint={ssh_key.fingerprint}") + print("========================") + return ssh_key -def update_ssh_key(ssh_key_id: str) -> SSHKey: - gcore = Gcore(api_key=os.environ.get("GCORE_API_KEY"), base_url=os.environ.get("GCORE_API_URL")) - updated_ssh_key = gcore.cloud.ssh_keys.update(ssh_key_id=ssh_key_id, shared_in_project=True) +def update_ssh_key(*, client: Gcore, ssh_key_id: str) -> SSHKey: print("\n=== UPDATE SSH KEY ===") - print(f"SSH Key ID: {updated_ssh_key.id}, Updated Name: {updated_ssh_key.name}") - print("=======================") + updated_ssh_key = client.cloud.ssh_keys.update(ssh_key_id=ssh_key_id, shared_in_project=True) + print(f"Updated SSH key: ID={updated_ssh_key.id}, name={updated_ssh_key.name}") + print("========================") return updated_ssh_key -def delete_ssh_key(ssh_key_id: str) -> None: - gcore = Gcore(api_key=os.environ.get("GCORE_API_KEY"), base_url=os.environ.get("GCORE_API_URL")) - gcore.cloud.ssh_keys.delete(ssh_key_id=ssh_key_id) - +def delete_ssh_key(*, client: Gcore, ssh_key_id: str) -> None: print("\n=== DELETE SSH KEY ===") - print(f"SSH Key ID: {ssh_key_id} has been deleted") - print("=======================") + client.cloud.ssh_keys.delete(ssh_key_id=ssh_key_id) + print(f"Deleted SSH key: ID={ssh_key_id}") + print("========================") if __name__ == "__main__": diff --git a/examples/cloud/ssh_keys_async.py b/examples/cloud/ssh_keys_async.py index 6866b3c5..bf34abab 100644 --- a/examples/cloud/ssh_keys_async.py +++ b/examples/cloud/ssh_keys_async.py @@ -1,4 +1,3 @@ -import os import asyncio from gcore import AsyncGcore @@ -14,68 +13,60 @@ async def main() -> None: # TODO set cloud region ID before running # cloud_region_id = os.environ["GCORE_CLOUD_REGION_ID"] - # Follow the order: create, list, get, update, delete - new_ssh_key = await create_new_ssh_key() - await list_all_ssh_keys() - await get_ssh_key_by_id(new_ssh_key.id) - await update_ssh_key(ssh_key_id=new_ssh_key.id) - await delete_ssh_key(ssh_key_id=new_ssh_key.id) - - -async def get_ssh_key_by_id(ssh_key_id: str) -> SSHKey: - # No need to pass the API key explicitly — it will automatically be read from the GCORE_API_KEY environment variable if omitted - gcore = AsyncGcore(api_key=os.environ.get("GCORE_API_KEY"), base_url=os.environ.get("GCORE_API_URL")) - ssh_key = await gcore.cloud.ssh_keys.get(ssh_key_id=ssh_key_id) - - print("\n=== GET SSH KEY BY ID ===") - print(f"SSH Key ID: {ssh_key.id}, Name: {ssh_key.name}, Fingerprint: {ssh_key.fingerprint}") - print("==========================") + gcore = AsyncGcore( + # No need to explicitly pass to AsyncGcore constructor if using environment variables + # api_key=api_key, + # cloud_project_id=cloud_project_id, + # cloud_region_id=cloud_region_id, + ) + + ssh_key = await create_ssh_key(client=gcore) + await list_ssh_keys(client=gcore) + await get_ssh_key(client=gcore, ssh_key_id=ssh_key.id) + await update_ssh_key(client=gcore, ssh_key_id=ssh_key.id) + await delete_ssh_key(client=gcore, ssh_key_id=ssh_key.id) + + +async def create_ssh_key(*, client: AsyncGcore) -> SSHKeyCreated: + print("\n=== CREATE SSH KEY ===") + ssh_key = await client.cloud.ssh_keys.create(name="gcore-go-example") + print(f"Created SSH key: ID={ssh_key.id}, name={ssh_key.name}") + print("========================") return ssh_key -async def list_all_ssh_keys() -> AsyncOffsetPage[SSHKey]: - gcore = AsyncGcore(api_key=os.environ.get("GCORE_API_KEY"), base_url=os.environ.get("GCORE_API_URL")) - all_ssh_keys = await gcore.cloud.ssh_keys.list() - - print("\n=== LIST ALL SSH KEYS ===") +async def list_ssh_keys(*, client: AsyncGcore) -> AsyncOffsetPage[SSHKey]: + print("\n=== LIST SSH KEYS ===") + ssh_keys = await client.cloud.ssh_keys.list() count = 1 - async for ssh_key in all_ssh_keys: - print(f" {count}. SSH Key ID: {ssh_key.id}, Name: {ssh_key.name}") + async for ssh_key in ssh_keys: + print(f" {count}. SSH key: ID={ssh_key.id}, name={ssh_key.name}") count += 1 - print("==========================") - return all_ssh_keys - + print("========================") + return ssh_keys -async def create_new_ssh_key() -> SSHKeyCreated: - gcore = AsyncGcore(api_key=os.environ.get("GCORE_API_KEY"), base_url=os.environ.get("GCORE_API_URL")) - # Sample SSH key values - ssh_key_name = "Example SSH Key" - - new_ssh_key = await gcore.cloud.ssh_keys.create(name=ssh_key_name) - - print("\n=== CREATE NEW SSH KEY ===") - print(f"SSH Key ID: {new_ssh_key.id}, Name: {new_ssh_key.name}") - print("===========================") - return new_ssh_key +async def get_ssh_key(*, client: AsyncGcore, ssh_key_id: str) -> SSHKey: + print("\n=== GET SSH KEY ===") + ssh_key = await client.cloud.ssh_keys.get(ssh_key_id=ssh_key_id) + print(f"SSH key: ID={ssh_key.id}, name={ssh_key.name}, fingerprint={ssh_key.fingerprint}") + print("========================") + return ssh_key -async def update_ssh_key(ssh_key_id: str) -> SSHKey: - gcore = AsyncGcore(api_key=os.environ.get("GCORE_API_KEY"), base_url=os.environ.get("GCORE_API_URL")) - updated_ssh_key = await gcore.cloud.ssh_keys.update(ssh_key_id=ssh_key_id, shared_in_project=True) +async def update_ssh_key(*, client: AsyncGcore, ssh_key_id: str) -> SSHKey: print("\n=== UPDATE SSH KEY ===") - print(f"SSH Key ID: {updated_ssh_key.id}, SharedInProject: {updated_ssh_key.shared_in_project}") - print("=======================") + updated_ssh_key = await client.cloud.ssh_keys.update(ssh_key_id=ssh_key_id, shared_in_project=True) + print(f"Updated SSH key: ID={updated_ssh_key.id}, name={updated_ssh_key.name}") + print("========================") return updated_ssh_key -async def delete_ssh_key(ssh_key_id: str) -> None: - gcore = AsyncGcore(api_key=os.environ.get("GCORE_API_KEY"), base_url=os.environ.get("GCORE_API_URL")) - await gcore.cloud.ssh_keys.delete(ssh_key_id=ssh_key_id) - +async def delete_ssh_key(*, client: AsyncGcore, ssh_key_id: str) -> None: print("\n=== DELETE SSH KEY ===") - print(f"SSH Key ID: {ssh_key_id} has been deleted") - print("=======================") + await client.cloud.ssh_keys.delete(ssh_key_id=ssh_key_id) + print(f"Deleted SSH key: ID={ssh_key_id}") + print("========================") if __name__ == "__main__": diff --git a/examples/cloud/task.py b/examples/cloud/task.py index c451c503..e47b047e 100644 --- a/examples/cloud/task.py +++ b/examples/cloud/task.py @@ -1,96 +1,61 @@ import os -from typing import Optional - -from httpx import HTTPStatusError from gcore import Gcore -from gcore.pagination import SyncOffsetPage -from gcore.types.cloud import Task def main() -> None: # TODO set API key before running # api_key = os.environ["GCORE_API_KEY"] - # TODO set cloud project ID before running - # cloud_project_id = os.environ["GCORE_CLOUD_PROJECT_ID"] - # TODO set cloud region ID before running - # cloud_region_id = os.environ["GCORE_CLOUD_REGION_ID"] - - get_task_by_id() - list_tasks() + # TODO set cloud network ID before running + cloud_task_id = os.environ["GCORE_CLOUD_TASK_ID"] -def list_tasks() -> Optional[SyncOffsetPage[Task]]: - """Demonstrates listing all tasks for a project and region.""" - # The API key is read automatically from the GCORE_API_KEY environment variable if omitted - # Read base URL from environment variable, if set - base_url = os.environ.get("GCORE_API_URL") - gcore = Gcore( - api_key=os.environ.get("GCORE_API_KEY"), - base_url=base_url if base_url else None, # Pass base_url only if it's set - ) - # Get project ID from environment variable GCORE_CLOUD_PROJECT_ID, defaulting to 1 if not set - project_id = int(os.environ.get("GCORE_CLOUD_PROJECT_ID", "1")) - # Get region ID from environment variable GCORE_CLOUD_REGION_ID, defaulting to 1 if not set - region_id = int(os.environ.get("GCORE_CLOUD_REGION_ID", "1")) - - print(f"=== LIST ALL TASKS (Project: {project_id}, Region: {region_id}) ===") - tasks: Optional[SyncOffsetPage[Task]] = None - try: - # Pass project_id and region_id as lists, as expected by the API - tasks = gcore.cloud.tasks.list(project_id=[project_id], region_id=[region_id]) - - count = 0 - print("Results:") - # Check if tasks_page is not None before iterating - if tasks: - for task in tasks: - print(f"- Task ID: {task.id}, Type: {task.task_type}, State: {task.state}, Created: {task.created_on}") - count += 1 - - if count == 0: - print("No tasks found.") - else: - print("Could not retrieve tasks.") # Handle case where tasks_page might remain None - - except Exception as e: - print(f"Error listing tasks: {e}") # Basic error logging - - print("=================================================") - # Note: The SDK handles pagination implicitly for simple iteration. - # If you need manual control, use the methods on the SyncOffsetPage object. - return tasks - - -def get_task_by_id() -> Optional[Task]: - """Demonstrates retrieving a specific task by its ID.""" - # Initialize client (same as list_tasks) - base_url = os.environ.get("GCORE_API_URL") gcore = Gcore( - api_key=os.environ.get("GCORE_API_KEY"), - base_url=base_url if base_url else None, + # No need to explicitly pass to Gcore constructor if using environment variables + # api_key=api_key, ) - # Replace with a valid task ID from your account. - # You might need to run list_tasks first to find one. - task_id_to_get = "your-task-id-here" # TODO: Replace with an actual task ID - - print(f"=== GET TASK BY ID ({task_id_to_get}) ===") - task: Optional[Task] = None - try: - task = gcore.cloud.tasks.get(task_id=task_id_to_get) - print(f"- Task ID: {task.id}, Type: {task.task_type}, State: {task.state}, Created: {task.created_on}") - except HTTPStatusError as e: - # Handle cases like 404 Not Found gracefully - if e.response.status_code == 404: - print(f"Info: Task '{task_id_to_get}' not found.") - else: - print(f"Error retrieving task '{task_id_to_get}': {e}") - except Exception as e: - print(f"An unexpected error occurred: {e}") - - print("============================") - return task + list_tasks(client=gcore) + get_task(client=gcore, task_id=cloud_task_id) + poll_task(client=gcore, task_id=cloud_task_id) + acknowledge_task(client=gcore, task_id=cloud_task_id) + acknowledge_all_tasks(client=gcore) + + +def list_tasks(*, client: Gcore) -> None: + print("\n=== LIST TASKS ===") + tasks = client.cloud.tasks.list(state=["RUNNING"]) + for count, task in enumerate(tasks, 1): + print(f"{count}. Task: ID={task.id}, type={task.task_type}, state={task.state}") + print("========================") + + +def get_task(*, client: Gcore, task_id: str) -> None: + print("\n=== GET TASK ===") + task = client.cloud.tasks.get(task_id=task_id) + print(f"Task: ID={task.id}, type={task.task_type}, state={task.state}") + print("========================") + + +def poll_task(*, client: Gcore, task_id: str) -> None: + print("\n=== POLL TASK ===") + task = client.cloud.tasks.poll(task_id=task_id) + print(f"Polled task: ID={task.id}, type={task.task_type}, state={task.state}") + print("========================") + + +def acknowledge_task(*, client: Gcore, task_id: str) -> None: + print("\n=== ACKNOWLEDGE TASK ===") + client.cloud.tasks.acknowledge_one(task_id=task_id) + print(f"Acknowledged task: ID={task_id}") + print("========================") + + +def acknowledge_all_tasks(*, client: Gcore) -> None: + print("\n=== ACKNOWLEDGE ALL TASKS ===") + client.cloud.tasks.acknowledge_all() + print("Acknowledged all tasks") + print("========================") if __name__ == "__main__": diff --git a/examples/cloud/task_async.py b/examples/cloud/task_async.py index c28baeaf..eebb0293 100644 --- a/examples/cloud/task_async.py +++ b/examples/cloud/task_async.py @@ -1,97 +1,64 @@ import os import asyncio -from typing import Optional - -from httpx import HTTPStatusError from gcore import AsyncGcore -from gcore.pagination import AsyncOffsetPage -from gcore.types.cloud import Task async def main() -> None: # TODO set API key before running # api_key = os.environ["GCORE_API_KEY"] - # TODO set cloud project ID before running - # cloud_project_id = os.environ["GCORE_CLOUD_PROJECT_ID"] - # TODO set cloud region ID before running - # cloud_region_id = os.environ["GCORE_CLOUD_REGION_ID"] - - await get_task_by_id() - await list_tasks() + # TODO set cloud network ID before running + cloud_task_id = os.environ["GCORE_CLOUD_TASK_ID"] -async def list_tasks() -> Optional[AsyncOffsetPage[Task]]: - """Demonstrates listing all tasks for a project and region asynchronously.""" - # The API key is read automatically from the GCORE_API_KEY environment variable if omitted - # Read base URL from environment variable, if set - base_url = os.environ.get("GCORE_API_URL") - gcore = AsyncGcore( - api_key=os.environ.get("GCORE_API_KEY"), - base_url=base_url if base_url else None, # Pass base_url only if it's set - ) - # Get project ID from environment variable GCORE_CLOUD_PROJECT_ID, defaulting to 1 if not set - project_id = int(os.environ.get("GCORE_CLOUD_PROJECT_ID", "1")) - # Get region ID from environment variable GCORE_CLOUD_REGION_ID, defaulting to 1 if not set - region_id = int(os.environ.get("GCORE_CLOUD_REGION_ID", "1")) - - print(f"\n=== LIST ALL TASKS (Project: {project_id}, Region: {region_id}) ===") - tasks: Optional[AsyncOffsetPage[Task]] = None - try: - # Pass project_id and region_id as lists, as expected by the API - tasks = await gcore.cloud.tasks.list(project_id=[project_id], region_id=[region_id]) - - count = 0 - print("Results:") - # Check if tasks_page is not None before iterating - if tasks: - async for task in tasks: - print(f"- Task ID: {task.id}, Type: {task.task_type}, State: {task.state}, Created: {task.created_on}") - count += 1 - - if count == 0: - print("No tasks found.") - else: - print("Could not retrieve tasks.") # Handle case where tasks_page might remain None - - except Exception as e: - print(f"Error listing tasks: {e}") # Basic error logging - - print("=================================================") - # Note: The SDK handles pagination implicitly for simple iteration. - # If you need manual control, use the methods on the AsyncOffsetPage object. - return tasks # Returning the page object for potential further use - - -async def get_task_by_id() -> Optional[Task]: - """Demonstrates retrieving a specific task by its ID asynchronously.""" - # Initialize client (same as list_tasks) - base_url = os.environ.get("GCORE_API_URL") gcore = AsyncGcore( - api_key=os.environ.get("GCORE_API_KEY"), - base_url=base_url if base_url else None, + # No need to explicitly pass to AsyncGcore constructor if using environment variables + # api_key=api_key, ) - # Replace with a valid task ID from your account. - # You might need to run list_tasks first to find one. - task_id_to_get = "your-task-id-here" # TODO: Replace with an actual task ID - - print(f"\n=== GET TASK BY ID ASYNC ({task_id_to_get}) ===") - task: Optional[Task] = None - try: - task = await gcore.cloud.tasks.get(task_id=task_id_to_get) - print(f"- Task ID: {task.id}, Type: {task.task_type}, State: {task.state}, Created: {task.created_on}") - except HTTPStatusError as e: - # Handle cases like 404 Not Found gracefully - if e.response.status_code == 404: - print(f"Info: Task '{task_id_to_get}' not found.") - else: - print(f"Error retrieving task '{task_id_to_get}': {e}") - except Exception as e: - print(f"An unexpected error occurred: {e}") - - print("=======================================") - return task + await list_tasks(client=gcore) + await get_task(client=gcore, task_id=cloud_task_id) + await poll_task(client=gcore, task_id=cloud_task_id) + await acknowledge_task(client=gcore, task_id=cloud_task_id) + await acknowledge_all_tasks(client=gcore) + + +async def list_tasks(*, client: AsyncGcore) -> None: + print("\n=== LIST ALL TASKS ===") + all_tasks = await client.cloud.tasks.list(state=["RUNNING"]) + count = 1 + async for task in all_tasks: + print(f" {count}. Task {task.id} ({task.task_type}): {task.state}") + count += 1 + print("========================") + + +async def get_task(*, client: AsyncGcore, task_id: str) -> None: + print("\n=== GET TASK BY ID ===") + task = await client.cloud.tasks.get(task_id=task_id) + print(f"Task {task.id} ({task.task_type}): {task.state}") + print("======================") + + +async def poll_task(*, client: AsyncGcore, task_id: str) -> None: + print("\n=== POLL TASK ===") + task = await client.cloud.tasks.poll(task_id=task_id) + print(f"Task {task.id} ({task.task_type}): {task.state}") + print("==================") + + +async def acknowledge_task(*, client: AsyncGcore, task_id: str) -> None: + print("\n=== ACKNOWLEDGE TASK ===") + await client.cloud.tasks.acknowledge_one(task_id=task_id) + print(f"Acknowledged task: ID={task_id}") + print("========================") + + +async def acknowledge_all_tasks(*, client: AsyncGcore) -> None: + print("\n=== ACKNOWLEDGE ALL TASKS ===") + await client.cloud.tasks.acknowledge_all() + print("Acknowledged all tasks") + print("=============================") if __name__ == "__main__": From 24b00fec390f141457c98334b302dab5a8b1d480 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 1 Jul 2025 11:53:41 +0000 Subject: [PATCH 43/50] refactor!: remove list suitable and list for resize from instance flavors --- .stats.yml | 4 +- api.md | 2 - .../resources/cloud/instances/flavors.py | 226 +---------------- src/gcore/types/cloud/instances/__init__.py | 2 - .../flavor_list_for_resize_params.py | 16 -- .../instances/flavor_list_suitable_params.py | 59 ----- .../cloud/instances/test_flavors.py | 234 +----------------- 7 files changed, 4 insertions(+), 539 deletions(-) delete mode 100644 src/gcore/types/cloud/instances/flavor_list_for_resize_params.py delete mode 100644 src/gcore/types/cloud/instances/flavor_list_suitable_params.py diff --git a/.stats.yml b/.stats.yml index 757399c4..56851965 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 302 +configured_endpoints: 300 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-3bd81b64cc2ef4de71b6d709296074d203d109fbe6549331480f09fd4412edd4.yml openapi_spec_hash: 4bcd0c3053fcb13e42cf6b0d8244499f -config_hash: 13858022450ab96760041d6619620b32 +config_hash: 82e3bee0a1a3a3c07cd95fe80e8e2930 diff --git a/api.md b/api.md index 238e7d83..2a19927f 100644 --- a/api.md +++ b/api.md @@ -821,8 +821,6 @@ from gcore.types.cloud.instances import InstanceFlavor, InstanceFlavorList Methods: - client.cloud.instances.flavors.list(\*, project_id, region_id, \*\*params) -> InstanceFlavorList -- client.cloud.instances.flavors.list_for_resize(instance_id, \*, project_id, region_id, \*\*params) -> InstanceFlavorList -- client.cloud.instances.flavors.list_suitable(\*, project_id, region_id, \*\*params) -> InstanceFlavorList ### Interfaces diff --git a/src/gcore/resources/cloud/instances/flavors.py b/src/gcore/resources/cloud/instances/flavors.py index f27760f8..9bd7f38f 100644 --- a/src/gcore/resources/cloud/instances/flavors.py +++ b/src/gcore/resources/cloud/instances/flavors.py @@ -2,8 +2,6 @@ from __future__ import annotations -from typing import Iterable - import httpx from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven @@ -17,7 +15,7 @@ async_to_streamed_response_wrapper, ) from ...._base_client import make_request_options -from ....types.cloud.instances import flavor_list_params, flavor_list_suitable_params, flavor_list_for_resize_params +from ....types.cloud.instances import flavor_list_params from ....types.cloud.instances.instance_flavor_list import InstanceFlavorList __all__ = ["FlavorsResource", "AsyncFlavorsResource"] @@ -106,104 +104,6 @@ def list( cast_to=InstanceFlavorList, ) - def list_for_resize( - self, - instance_id: str, - *, - project_id: int | None = None, - region_id: int | None = None, - include_prices: bool | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> InstanceFlavorList: - """ - List suitable flavors for instance resize - - Args: - include_prices: Set to true if flavor listing should include flavor prices - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if project_id is None: - project_id = self._client._get_cloud_project_id_path_param() - if region_id is None: - region_id = self._client._get_cloud_region_id_path_param() - if not instance_id: - raise ValueError(f"Expected a non-empty value for `instance_id` but received {instance_id!r}") - return self._get( - f"/cloud/v1/instances/{project_id}/{region_id}/{instance_id}/available_flavors", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - {"include_prices": include_prices}, flavor_list_for_resize_params.FlavorListForResizeParams - ), - ), - cast_to=InstanceFlavorList, - ) - - def list_suitable( - self, - *, - project_id: int | None = None, - region_id: int | None = None, - volumes: Iterable[flavor_list_suitable_params.Volume], - include_prices: bool | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> InstanceFlavorList: - """ - List all flavors that are suitable for instance creation based on volume - requirements. - - Args: - volumes: Volumes details. Non-important info such as names may be omitted. - - include_prices: Set to true if flavor listing should include flavor prices - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if project_id is None: - project_id = self._client._get_cloud_project_id_path_param() - if region_id is None: - region_id = self._client._get_cloud_region_id_path_param() - return self._post( - f"/cloud/v1/instances/{project_id}/{region_id}/available_flavors", - body=maybe_transform({"volumes": volumes}, flavor_list_suitable_params.FlavorListSuitableParams), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - {"include_prices": include_prices}, flavor_list_suitable_params.FlavorListSuitableParams - ), - ), - cast_to=InstanceFlavorList, - ) - class AsyncFlavorsResource(AsyncAPIResource): @cached_property @@ -288,106 +188,6 @@ async def list( cast_to=InstanceFlavorList, ) - async def list_for_resize( - self, - instance_id: str, - *, - project_id: int | None = None, - region_id: int | None = None, - include_prices: bool | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> InstanceFlavorList: - """ - List suitable flavors for instance resize - - Args: - include_prices: Set to true if flavor listing should include flavor prices - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if project_id is None: - project_id = self._client._get_cloud_project_id_path_param() - if region_id is None: - region_id = self._client._get_cloud_region_id_path_param() - if not instance_id: - raise ValueError(f"Expected a non-empty value for `instance_id` but received {instance_id!r}") - return await self._get( - f"/cloud/v1/instances/{project_id}/{region_id}/{instance_id}/available_flavors", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"include_prices": include_prices}, flavor_list_for_resize_params.FlavorListForResizeParams - ), - ), - cast_to=InstanceFlavorList, - ) - - async def list_suitable( - self, - *, - project_id: int | None = None, - region_id: int | None = None, - volumes: Iterable[flavor_list_suitable_params.Volume], - include_prices: bool | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> InstanceFlavorList: - """ - List all flavors that are suitable for instance creation based on volume - requirements. - - Args: - volumes: Volumes details. Non-important info such as names may be omitted. - - include_prices: Set to true if flavor listing should include flavor prices - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if project_id is None: - project_id = self._client._get_cloud_project_id_path_param() - if region_id is None: - region_id = self._client._get_cloud_region_id_path_param() - return await self._post( - f"/cloud/v1/instances/{project_id}/{region_id}/available_flavors", - body=await async_maybe_transform( - {"volumes": volumes}, flavor_list_suitable_params.FlavorListSuitableParams - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"include_prices": include_prices}, flavor_list_suitable_params.FlavorListSuitableParams - ), - ), - cast_to=InstanceFlavorList, - ) - class FlavorsResourceWithRawResponse: def __init__(self, flavors: FlavorsResource) -> None: @@ -396,12 +196,6 @@ def __init__(self, flavors: FlavorsResource) -> None: self.list = to_raw_response_wrapper( flavors.list, ) - self.list_for_resize = to_raw_response_wrapper( - flavors.list_for_resize, - ) - self.list_suitable = to_raw_response_wrapper( - flavors.list_suitable, - ) class AsyncFlavorsResourceWithRawResponse: @@ -411,12 +205,6 @@ def __init__(self, flavors: AsyncFlavorsResource) -> None: self.list = async_to_raw_response_wrapper( flavors.list, ) - self.list_for_resize = async_to_raw_response_wrapper( - flavors.list_for_resize, - ) - self.list_suitable = async_to_raw_response_wrapper( - flavors.list_suitable, - ) class FlavorsResourceWithStreamingResponse: @@ -426,12 +214,6 @@ def __init__(self, flavors: FlavorsResource) -> None: self.list = to_streamed_response_wrapper( flavors.list, ) - self.list_for_resize = to_streamed_response_wrapper( - flavors.list_for_resize, - ) - self.list_suitable = to_streamed_response_wrapper( - flavors.list_suitable, - ) class AsyncFlavorsResourceWithStreamingResponse: @@ -441,9 +223,3 @@ def __init__(self, flavors: AsyncFlavorsResource) -> None: self.list = async_to_streamed_response_wrapper( flavors.list, ) - self.list_for_resize = async_to_streamed_response_wrapper( - flavors.list_for_resize, - ) - self.list_suitable = async_to_streamed_response_wrapper( - flavors.list_suitable, - ) diff --git a/src/gcore/types/cloud/instances/__init__.py b/src/gcore/types/cloud/instances/__init__.py index f275af0a..c2f77cfe 100644 --- a/src/gcore/types/cloud/instances/__init__.py +++ b/src/gcore/types/cloud/instances/__init__.py @@ -14,6 +14,4 @@ from .instance_flavor_list import InstanceFlavorList as InstanceFlavorList from .interface_attach_params import InterfaceAttachParams as InterfaceAttachParams from .interface_detach_params import InterfaceDetachParams as InterfaceDetachParams -from .flavor_list_suitable_params import FlavorListSuitableParams as FlavorListSuitableParams -from .flavor_list_for_resize_params import FlavorListForResizeParams as FlavorListForResizeParams from .image_create_from_volume_params import ImageCreateFromVolumeParams as ImageCreateFromVolumeParams diff --git a/src/gcore/types/cloud/instances/flavor_list_for_resize_params.py b/src/gcore/types/cloud/instances/flavor_list_for_resize_params.py deleted file mode 100644 index b1eb6ab1..00000000 --- a/src/gcore/types/cloud/instances/flavor_list_for_resize_params.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import TypedDict - -__all__ = ["FlavorListForResizeParams"] - - -class FlavorListForResizeParams(TypedDict, total=False): - project_id: int - - region_id: int - - include_prices: bool - """Set to true if flavor listing should include flavor prices""" diff --git a/src/gcore/types/cloud/instances/flavor_list_suitable_params.py b/src/gcore/types/cloud/instances/flavor_list_suitable_params.py deleted file mode 100644 index 3508d066..00000000 --- a/src/gcore/types/cloud/instances/flavor_list_suitable_params.py +++ /dev/null @@ -1,59 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Iterable, Optional -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["FlavorListSuitableParams", "Volume"] - - -class FlavorListSuitableParams(TypedDict, total=False): - project_id: int - - region_id: int - - volumes: Required[Iterable[Volume]] - """Volumes details. Non-important info such as names may be omitted.""" - - include_prices: bool - """Set to true if flavor listing should include flavor prices""" - - -class Volume(TypedDict, total=False): - source: Required[Literal["apptemplate", "existing-volume", "image", "new-volume", "snapshot"]] - """Volume source""" - - apptemplate_id: str - """App template ID. Mandatory if volume is created from marketplace template""" - - boot_index: int - """ - 0 should be set for primary boot device Unique positive values for other - bootable devices.Negative - boot prohibited - """ - - image_id: str - """Image ID. Mandatory if volume is created from image""" - - name: Optional[str] - - size: int - """Volume size. - - Must be specified when source is 'new-volume' or 'image'. If specified for - source 'snapshot' or 'existing-volume', value must be equal to respective - snapshot or volume size - """ - - snapshot_id: str - """Volume snapshot ID. Mandatory if volume is created from a snapshot""" - - type_name: Literal["cold", "ssd_hiiops", "ssd_local", "ssd_lowlatency", "standard", "ultra"] - """ - One of 'standard', '`ssd_hiiops`', '`ssd_local`', '`ssd_lowlatency`', 'cold', - 'ultra' - """ - - volume_id: str - """Volume ID. Mandatory if volume is pre-existing volume""" diff --git a/tests/api_resources/cloud/instances/test_flavors.py b/tests/api_resources/cloud/instances/test_flavors.py index 8efdf80d..51ff8119 100644 --- a/tests/api_resources/cloud/instances/test_flavors.py +++ b/tests/api_resources/cloud/instances/test_flavors.py @@ -9,9 +9,7 @@ from gcore import Gcore, AsyncGcore from tests.utils import assert_matches_type -from gcore.types.cloud.instances import ( - InstanceFlavorList, -) +from gcore.types.cloud.instances import InstanceFlavorList base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -65,121 +63,6 @@ def test_streaming_response_list(self, client: Gcore) -> None: assert cast(Any, response.is_closed) is True - @parametrize - def test_method_list_for_resize(self, client: Gcore) -> None: - flavor = client.cloud.instances.flavors.list_for_resize( - instance_id="instance_id", - project_id=0, - region_id=0, - ) - assert_matches_type(InstanceFlavorList, flavor, path=["response"]) - - @parametrize - def test_method_list_for_resize_with_all_params(self, client: Gcore) -> None: - flavor = client.cloud.instances.flavors.list_for_resize( - instance_id="instance_id", - project_id=0, - region_id=0, - include_prices=True, - ) - assert_matches_type(InstanceFlavorList, flavor, path=["response"]) - - @parametrize - def test_raw_response_list_for_resize(self, client: Gcore) -> None: - response = client.cloud.instances.flavors.with_raw_response.list_for_resize( - instance_id="instance_id", - project_id=0, - region_id=0, - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - flavor = response.parse() - assert_matches_type(InstanceFlavorList, flavor, path=["response"]) - - @parametrize - def test_streaming_response_list_for_resize(self, client: Gcore) -> None: - with client.cloud.instances.flavors.with_streaming_response.list_for_resize( - instance_id="instance_id", - project_id=0, - region_id=0, - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - flavor = response.parse() - assert_matches_type(InstanceFlavorList, flavor, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_list_for_resize(self, client: Gcore) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `instance_id` but received ''"): - client.cloud.instances.flavors.with_raw_response.list_for_resize( - instance_id="", - project_id=0, - region_id=0, - ) - - @parametrize - def test_method_list_suitable(self, client: Gcore) -> None: - flavor = client.cloud.instances.flavors.list_suitable( - project_id=0, - region_id=0, - volumes=[{"source": "image"}], - ) - assert_matches_type(InstanceFlavorList, flavor, path=["response"]) - - @parametrize - def test_method_list_suitable_with_all_params(self, client: Gcore) -> None: - flavor = client.cloud.instances.flavors.list_suitable( - project_id=0, - region_id=0, - volumes=[ - { - "source": "image", - "apptemplate_id": "apptemplate_id", - "boot_index": 0, - "image_id": "f01fd9a0-9548-48ba-82dc-a8c8b2d6f2f1", - "name": "TestVM5 Ubuntu boot image", - "size": 10, - "snapshot_id": "snapshot_id", - "type_name": "ssd_hiiops", - "volume_id": "volume_id", - } - ], - include_prices=True, - ) - assert_matches_type(InstanceFlavorList, flavor, path=["response"]) - - @parametrize - def test_raw_response_list_suitable(self, client: Gcore) -> None: - response = client.cloud.instances.flavors.with_raw_response.list_suitable( - project_id=0, - region_id=0, - volumes=[{"source": "image"}], - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - flavor = response.parse() - assert_matches_type(InstanceFlavorList, flavor, path=["response"]) - - @parametrize - def test_streaming_response_list_suitable(self, client: Gcore) -> None: - with client.cloud.instances.flavors.with_streaming_response.list_suitable( - project_id=0, - region_id=0, - volumes=[{"source": "image"}], - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - flavor = response.parse() - assert_matches_type(InstanceFlavorList, flavor, path=["response"]) - - assert cast(Any, response.is_closed) is True - class TestAsyncFlavors: parametrize = pytest.mark.parametrize( @@ -231,118 +114,3 @@ async def test_streaming_response_list(self, async_client: AsyncGcore) -> None: assert_matches_type(InstanceFlavorList, flavor, path=["response"]) assert cast(Any, response.is_closed) is True - - @parametrize - async def test_method_list_for_resize(self, async_client: AsyncGcore) -> None: - flavor = await async_client.cloud.instances.flavors.list_for_resize( - instance_id="instance_id", - project_id=0, - region_id=0, - ) - assert_matches_type(InstanceFlavorList, flavor, path=["response"]) - - @parametrize - async def test_method_list_for_resize_with_all_params(self, async_client: AsyncGcore) -> None: - flavor = await async_client.cloud.instances.flavors.list_for_resize( - instance_id="instance_id", - project_id=0, - region_id=0, - include_prices=True, - ) - assert_matches_type(InstanceFlavorList, flavor, path=["response"]) - - @parametrize - async def test_raw_response_list_for_resize(self, async_client: AsyncGcore) -> None: - response = await async_client.cloud.instances.flavors.with_raw_response.list_for_resize( - instance_id="instance_id", - project_id=0, - region_id=0, - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - flavor = await response.parse() - assert_matches_type(InstanceFlavorList, flavor, path=["response"]) - - @parametrize - async def test_streaming_response_list_for_resize(self, async_client: AsyncGcore) -> None: - async with async_client.cloud.instances.flavors.with_streaming_response.list_for_resize( - instance_id="instance_id", - project_id=0, - region_id=0, - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - flavor = await response.parse() - assert_matches_type(InstanceFlavorList, flavor, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_list_for_resize(self, async_client: AsyncGcore) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `instance_id` but received ''"): - await async_client.cloud.instances.flavors.with_raw_response.list_for_resize( - instance_id="", - project_id=0, - region_id=0, - ) - - @parametrize - async def test_method_list_suitable(self, async_client: AsyncGcore) -> None: - flavor = await async_client.cloud.instances.flavors.list_suitable( - project_id=0, - region_id=0, - volumes=[{"source": "image"}], - ) - assert_matches_type(InstanceFlavorList, flavor, path=["response"]) - - @parametrize - async def test_method_list_suitable_with_all_params(self, async_client: AsyncGcore) -> None: - flavor = await async_client.cloud.instances.flavors.list_suitable( - project_id=0, - region_id=0, - volumes=[ - { - "source": "image", - "apptemplate_id": "apptemplate_id", - "boot_index": 0, - "image_id": "f01fd9a0-9548-48ba-82dc-a8c8b2d6f2f1", - "name": "TestVM5 Ubuntu boot image", - "size": 10, - "snapshot_id": "snapshot_id", - "type_name": "ssd_hiiops", - "volume_id": "volume_id", - } - ], - include_prices=True, - ) - assert_matches_type(InstanceFlavorList, flavor, path=["response"]) - - @parametrize - async def test_raw_response_list_suitable(self, async_client: AsyncGcore) -> None: - response = await async_client.cloud.instances.flavors.with_raw_response.list_suitable( - project_id=0, - region_id=0, - volumes=[{"source": "image"}], - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - flavor = await response.parse() - assert_matches_type(InstanceFlavorList, flavor, path=["response"]) - - @parametrize - async def test_streaming_response_list_suitable(self, async_client: AsyncGcore) -> None: - async with async_client.cloud.instances.flavors.with_streaming_response.list_suitable( - project_id=0, - region_id=0, - volumes=[{"source": "image"}], - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - flavor = await response.parse() - assert_matches_type(InstanceFlavorList, flavor, path=["response"]) - - assert cast(Any, response.is_closed) is True From 262693876592601fa5a07dd088033037c7eae9b6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 1 Jul 2025 12:11:11 +0000 Subject: [PATCH 44/50] refactor(cloud)!: remove list suitable from bm flavors --- .stats.yml | 4 +- api.md | 1 - .../resources/cloud/baremetal/flavors.py | 132 +----------------- src/gcore/types/cloud/baremetal/__init__.py | 1 - .../baremetal/flavor_list_suitable_params.py | 22 --- .../cloud/baremetal/test_flavors.py | 90 ------------ 6 files changed, 3 insertions(+), 247 deletions(-) delete mode 100644 src/gcore/types/cloud/baremetal/flavor_list_suitable_params.py diff --git a/.stats.yml b/.stats.yml index 56851965..9651bace 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 300 +configured_endpoints: 299 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-3bd81b64cc2ef4de71b6d709296074d203d109fbe6549331480f09fd4412edd4.yml openapi_spec_hash: 4bcd0c3053fcb13e42cf6b0d8244499f -config_hash: 82e3bee0a1a3a3c07cd95fe80e8e2930 +config_hash: 9e417d14176a0e60806f859c2a25ddd7 diff --git a/api.md b/api.md index 2a19927f..a21f7d28 100644 --- a/api.md +++ b/api.md @@ -594,7 +594,6 @@ Methods: Methods: - client.cloud.baremetal.flavors.list(\*, project_id, region_id, \*\*params) -> BaremetalFlavorList -- client.cloud.baremetal.flavors.list_suitable(\*, project_id, region_id, \*\*params) -> BaremetalFlavorList ### Servers diff --git a/src/gcore/resources/cloud/baremetal/flavors.py b/src/gcore/resources/cloud/baremetal/flavors.py index 5199c40c..1645b30b 100644 --- a/src/gcore/resources/cloud/baremetal/flavors.py +++ b/src/gcore/resources/cloud/baremetal/flavors.py @@ -15,7 +15,7 @@ async_to_streamed_response_wrapper, ) from ...._base_client import make_request_options -from ....types.cloud.baremetal import flavor_list_params, flavor_list_suitable_params +from ....types.cloud.baremetal import flavor_list_params from ....types.cloud.baremetal_flavor_list import BaremetalFlavorList __all__ = ["FlavorsResource", "AsyncFlavorsResource"] @@ -114,65 +114,6 @@ def list( cast_to=BaremetalFlavorList, ) - def list_suitable( - self, - *, - project_id: int | None = None, - region_id: int | None = None, - include_prices: bool | NotGiven = NOT_GIVEN, - apptemplate_id: str | NotGiven = NOT_GIVEN, - image_id: str | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> BaremetalFlavorList: - """ - List all flavors that are suitable for creating a bare metal server with the - specified image. - - Args: - include_prices: Set to true if flavor listing should include flavor prices - - apptemplate_id: Apptemplate ID - - image_id: Image ID - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if project_id is None: - project_id = self._client._get_cloud_project_id_path_param() - if region_id is None: - region_id = self._client._get_cloud_region_id_path_param() - return self._post( - f"/cloud/v1/bminstances/{project_id}/{region_id}/available_flavors", - body=maybe_transform( - { - "apptemplate_id": apptemplate_id, - "image_id": image_id, - }, - flavor_list_suitable_params.FlavorListSuitableParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - {"include_prices": include_prices}, flavor_list_suitable_params.FlavorListSuitableParams - ), - ), - cast_to=BaremetalFlavorList, - ) - class AsyncFlavorsResource(AsyncAPIResource): @cached_property @@ -267,65 +208,6 @@ async def list( cast_to=BaremetalFlavorList, ) - async def list_suitable( - self, - *, - project_id: int | None = None, - region_id: int | None = None, - include_prices: bool | NotGiven = NOT_GIVEN, - apptemplate_id: str | NotGiven = NOT_GIVEN, - image_id: str | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> BaremetalFlavorList: - """ - List all flavors that are suitable for creating a bare metal server with the - specified image. - - Args: - include_prices: Set to true if flavor listing should include flavor prices - - apptemplate_id: Apptemplate ID - - image_id: Image ID - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if project_id is None: - project_id = self._client._get_cloud_project_id_path_param() - if region_id is None: - region_id = self._client._get_cloud_region_id_path_param() - return await self._post( - f"/cloud/v1/bminstances/{project_id}/{region_id}/available_flavors", - body=await async_maybe_transform( - { - "apptemplate_id": apptemplate_id, - "image_id": image_id, - }, - flavor_list_suitable_params.FlavorListSuitableParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"include_prices": include_prices}, flavor_list_suitable_params.FlavorListSuitableParams - ), - ), - cast_to=BaremetalFlavorList, - ) - class FlavorsResourceWithRawResponse: def __init__(self, flavors: FlavorsResource) -> None: @@ -334,9 +216,6 @@ def __init__(self, flavors: FlavorsResource) -> None: self.list = to_raw_response_wrapper( flavors.list, ) - self.list_suitable = to_raw_response_wrapper( - flavors.list_suitable, - ) class AsyncFlavorsResourceWithRawResponse: @@ -346,9 +225,6 @@ def __init__(self, flavors: AsyncFlavorsResource) -> None: self.list = async_to_raw_response_wrapper( flavors.list, ) - self.list_suitable = async_to_raw_response_wrapper( - flavors.list_suitable, - ) class FlavorsResourceWithStreamingResponse: @@ -358,9 +234,6 @@ def __init__(self, flavors: FlavorsResource) -> None: self.list = to_streamed_response_wrapper( flavors.list, ) - self.list_suitable = to_streamed_response_wrapper( - flavors.list_suitable, - ) class AsyncFlavorsResourceWithStreamingResponse: @@ -370,6 +243,3 @@ def __init__(self, flavors: AsyncFlavorsResource) -> None: self.list = async_to_streamed_response_wrapper( flavors.list, ) - self.list_suitable = async_to_streamed_response_wrapper( - flavors.list_suitable, - ) diff --git a/src/gcore/types/cloud/baremetal/__init__.py b/src/gcore/types/cloud/baremetal/__init__.py index 7cdfa810..5c1e9aea 100644 --- a/src/gcore/types/cloud/baremetal/__init__.py +++ b/src/gcore/types/cloud/baremetal/__init__.py @@ -10,4 +10,3 @@ from .server_rebuild_params import ServerRebuildParams as ServerRebuildParams from .baremetal_fixed_address import BaremetalFixedAddress as BaremetalFixedAddress from .baremetal_floating_address import BaremetalFloatingAddress as BaremetalFloatingAddress -from .flavor_list_suitable_params import FlavorListSuitableParams as FlavorListSuitableParams diff --git a/src/gcore/types/cloud/baremetal/flavor_list_suitable_params.py b/src/gcore/types/cloud/baremetal/flavor_list_suitable_params.py deleted file mode 100644 index ec5beb24..00000000 --- a/src/gcore/types/cloud/baremetal/flavor_list_suitable_params.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import TypedDict - -__all__ = ["FlavorListSuitableParams"] - - -class FlavorListSuitableParams(TypedDict, total=False): - project_id: int - - region_id: int - - include_prices: bool - """Set to true if flavor listing should include flavor prices""" - - apptemplate_id: str - """Apptemplate ID""" - - image_id: str - """Image ID""" diff --git a/tests/api_resources/cloud/baremetal/test_flavors.py b/tests/api_resources/cloud/baremetal/test_flavors.py index 1778e4f4..18da226a 100644 --- a/tests/api_resources/cloud/baremetal/test_flavors.py +++ b/tests/api_resources/cloud/baremetal/test_flavors.py @@ -65,51 +65,6 @@ def test_streaming_response_list(self, client: Gcore) -> None: assert cast(Any, response.is_closed) is True - @parametrize - def test_method_list_suitable(self, client: Gcore) -> None: - flavor = client.cloud.baremetal.flavors.list_suitable( - project_id=0, - region_id=0, - ) - assert_matches_type(BaremetalFlavorList, flavor, path=["response"]) - - @parametrize - def test_method_list_suitable_with_all_params(self, client: Gcore) -> None: - flavor = client.cloud.baremetal.flavors.list_suitable( - project_id=0, - region_id=0, - include_prices=True, - apptemplate_id="apptemplate_id", - image_id="b5b4d65d-945f-4b98-ab6f-332319c724ef", - ) - assert_matches_type(BaremetalFlavorList, flavor, path=["response"]) - - @parametrize - def test_raw_response_list_suitable(self, client: Gcore) -> None: - response = client.cloud.baremetal.flavors.with_raw_response.list_suitable( - project_id=0, - region_id=0, - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - flavor = response.parse() - assert_matches_type(BaremetalFlavorList, flavor, path=["response"]) - - @parametrize - def test_streaming_response_list_suitable(self, client: Gcore) -> None: - with client.cloud.baremetal.flavors.with_streaming_response.list_suitable( - project_id=0, - region_id=0, - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - flavor = response.parse() - assert_matches_type(BaremetalFlavorList, flavor, path=["response"]) - - assert cast(Any, response.is_closed) is True - class TestAsyncFlavors: parametrize = pytest.mark.parametrize( @@ -163,48 +118,3 @@ async def test_streaming_response_list(self, async_client: AsyncGcore) -> None: assert_matches_type(BaremetalFlavorList, flavor, path=["response"]) assert cast(Any, response.is_closed) is True - - @parametrize - async def test_method_list_suitable(self, async_client: AsyncGcore) -> None: - flavor = await async_client.cloud.baremetal.flavors.list_suitable( - project_id=0, - region_id=0, - ) - assert_matches_type(BaremetalFlavorList, flavor, path=["response"]) - - @parametrize - async def test_method_list_suitable_with_all_params(self, async_client: AsyncGcore) -> None: - flavor = await async_client.cloud.baremetal.flavors.list_suitable( - project_id=0, - region_id=0, - include_prices=True, - apptemplate_id="apptemplate_id", - image_id="b5b4d65d-945f-4b98-ab6f-332319c724ef", - ) - assert_matches_type(BaremetalFlavorList, flavor, path=["response"]) - - @parametrize - async def test_raw_response_list_suitable(self, async_client: AsyncGcore) -> None: - response = await async_client.cloud.baremetal.flavors.with_raw_response.list_suitable( - project_id=0, - region_id=0, - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - flavor = await response.parse() - assert_matches_type(BaremetalFlavorList, flavor, path=["response"]) - - @parametrize - async def test_streaming_response_list_suitable(self, async_client: AsyncGcore) -> None: - async with async_client.cloud.baremetal.flavors.with_streaming_response.list_suitable( - project_id=0, - region_id=0, - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - flavor = await response.parse() - assert_matches_type(BaremetalFlavorList, flavor, path=["response"]) - - assert cast(Any, response.is_closed) is True From 960a44ba2f255ad2bf8f571de381b06e5f1fffbd Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 2 Jul 2025 05:28:08 +0000 Subject: [PATCH 45/50] chore(ci): change upload type --- .github/workflows/ci.yml | 18 ++++++++++++++++-- scripts/utils/upload-artifact.sh | 12 +++++++----- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b642078b..1cd880c7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,10 +35,10 @@ jobs: - name: Run lints run: ./scripts/lint - upload: + build: if: github.repository == 'stainless-sdks/gcore-python' && (github.event_name == 'push' || github.event.pull_request.head.repo.fork) timeout-minutes: 10 - name: upload + name: build permissions: contents: read id-token: write @@ -46,6 +46,20 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Install Rye + run: | + curl -sSf https://rye.astral.sh/get | bash + echo "$HOME/.rye/shims" >> $GITHUB_PATH + env: + RYE_VERSION: '0.44.0' + RYE_INSTALL_OPTION: '--yes' + + - name: Install dependencies + run: rye sync --all-features + + - name: Run build + run: rye build + - name: Get GitHub OIDC Token id: github-oidc uses: actions/github-script@v6 diff --git a/scripts/utils/upload-artifact.sh b/scripts/utils/upload-artifact.sh index 6b8632b6..dcb0ac9e 100755 --- a/scripts/utils/upload-artifact.sh +++ b/scripts/utils/upload-artifact.sh @@ -1,7 +1,9 @@ #!/usr/bin/env bash set -exuo pipefail -RESPONSE=$(curl -X POST "$URL" \ +FILENAME=$(basename dist/*.whl) + +RESPONSE=$(curl -X POST "$URL?filename=$FILENAME" \ -H "Authorization: Bearer $AUTH" \ -H "Content-Type: application/json") @@ -12,13 +14,13 @@ if [[ "$SIGNED_URL" == "null" ]]; then exit 1 fi -UPLOAD_RESPONSE=$(tar -cz . | curl -v -X PUT \ - -H "Content-Type: application/gzip" \ - --data-binary @- "$SIGNED_URL" 2>&1) +UPLOAD_RESPONSE=$(curl -v -X PUT \ + -H "Content-Type: binary/octet-stream" \ + --data-binary "@dist/$FILENAME" "$SIGNED_URL" 2>&1) if echo "$UPLOAD_RESPONSE" | grep -q "HTTP/[0-9.]* 200"; then echo -e "\033[32mUploaded build to Stainless storage.\033[0m" - echo -e "\033[32mInstallation: pip install 'https://pkg.stainless.com/s/gcore-python/$SHA'\033[0m" + echo -e "\033[32mInstallation: pip install 'https://pkg.stainless.com/s/gcore-python/$SHA/$FILENAME'\033[0m" else echo -e "\033[31mFailed to upload artifact.\033[0m" exit 1 From e781ff5f25ced4842b4e67253161d8a8d7464ef0 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 2 Jul 2025 08:12:50 +0000 Subject: [PATCH 46/50] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 9651bace..d2d27950 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 299 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-3bd81b64cc2ef4de71b6d709296074d203d109fbe6549331480f09fd4412edd4.yml -openapi_spec_hash: 4bcd0c3053fcb13e42cf6b0d8244499f +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-6de27f3b3373ff39c76710f9e653246118ced81d2a1d0b96a9e78c4a540d9f12.yml +openapi_spec_hash: 9f5702a77532ce5f742965d7d5d15bfe config_hash: 9e417d14176a0e60806f859c2a25ddd7 From 7395880c1291632db977714b43de1ab7061c23ed Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 2 Jul 2025 14:10:21 +0000 Subject: [PATCH 47/50] feat(api): aggregated API specs update --- .stats.yml | 4 ++-- .../resources/cloud/load_balancers/pools/members.py | 8 ++++++-- src/gcore/types/cloud/health_monitor.py | 7 ++++++- src/gcore/types/cloud/load_balancer_create_params.py | 7 ++++++- .../types/cloud/load_balancers/pool_create_params.py | 7 ++++++- .../types/cloud/load_balancers/pool_update_params.py | 7 ++++++- .../cloud/load_balancers/pools/member_add_params.py | 7 ++++++- src/gcore/types/cloud/member.py | 7 ++++++- .../cloud/load_balancers/pools/test_members.py | 4 ++-- .../api_resources/cloud/load_balancers/test_pools.py | 12 ++++++------ tests/api_resources/cloud/test_load_balancers.py | 8 ++++---- 11 files changed, 56 insertions(+), 22 deletions(-) diff --git a/.stats.yml b/.stats.yml index d2d27950..5d3a588f 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 299 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-6de27f3b3373ff39c76710f9e653246118ced81d2a1d0b96a9e78c4a540d9f12.yml -openapi_spec_hash: 9f5702a77532ce5f742965d7d5d15bfe +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-a3eb777ed002489583f1ec680d7ac5638c4bf54a843d381a1c1ad5d1c9f5e967.yml +openapi_spec_hash: 3e4993495b21ba44b2969cc41cdb1221 config_hash: 9e417d14176a0e60806f859c2a25ddd7 diff --git a/src/gcore/resources/cloud/load_balancers/pools/members.py b/src/gcore/resources/cloud/load_balancers/pools/members.py index 5e05941f..4f5f236a 100644 --- a/src/gcore/resources/cloud/load_balancers/pools/members.py +++ b/src/gcore/resources/cloud/load_balancers/pools/members.py @@ -78,7 +78,9 @@ def add( protocol_port: Member IP port - admin_state_up: true if enabled. Defaults to true + admin_state_up: Administrative state of the resource. When set to true, the resource is enabled + and operational. When set to false, the resource is disabled and will not + process traffic. When null is passed, the value is skipped and defaults to true. instance_id: Either `subnet_id` or `instance_id` should be provided @@ -233,7 +235,9 @@ async def add( protocol_port: Member IP port - admin_state_up: true if enabled. Defaults to true + admin_state_up: Administrative state of the resource. When set to true, the resource is enabled + and operational. When set to false, the resource is disabled and will not + process traffic. When null is passed, the value is skipped and defaults to true. instance_id: Either `subnet_id` or `instance_id` should be provided diff --git a/src/gcore/types/cloud/health_monitor.py b/src/gcore/types/cloud/health_monitor.py index 25303572..85674aa9 100644 --- a/src/gcore/types/cloud/health_monitor.py +++ b/src/gcore/types/cloud/health_monitor.py @@ -16,7 +16,12 @@ class HealthMonitor(BaseModel): """Health monitor ID""" admin_state_up: bool - """true if enabled. Defaults to true""" + """Administrative state of the resource. + + When set to true, the resource is enabled and operational. When set to false, + the resource is disabled and will not process traffic. When null is passed, the + value is skipped and defaults to true. + """ delay: int """The time, in seconds, between sending probes to members""" diff --git a/src/gcore/types/cloud/load_balancer_create_params.py b/src/gcore/types/cloud/load_balancer_create_params.py index 6d1739de..5ce1b76a 100644 --- a/src/gcore/types/cloud/load_balancer_create_params.py +++ b/src/gcore/types/cloud/load_balancer_create_params.py @@ -174,7 +174,12 @@ class ListenerPoolMember(TypedDict, total=False): """Member IP port""" admin_state_up: Optional[bool] - """true if enabled. Defaults to true""" + """Administrative state of the resource. + + When set to true, the resource is enabled and operational. When set to false, + the resource is disabled and will not process traffic. When null is passed, the + value is skipped and defaults to true. + """ instance_id: Optional[str] """Either `subnet_id` or `instance_id` should be provided""" diff --git a/src/gcore/types/cloud/load_balancers/pool_create_params.py b/src/gcore/types/cloud/load_balancers/pool_create_params.py index 200c0f1d..4cf1b7c2 100644 --- a/src/gcore/types/cloud/load_balancers/pool_create_params.py +++ b/src/gcore/types/cloud/load_balancers/pool_create_params.py @@ -105,7 +105,12 @@ class Member(TypedDict, total=False): """Member IP port""" admin_state_up: Optional[bool] - """true if enabled. Defaults to true""" + """Administrative state of the resource. + + When set to true, the resource is enabled and operational. When set to false, + the resource is disabled and will not process traffic. When null is passed, the + value is skipped and defaults to true. + """ instance_id: Optional[str] """Either `subnet_id` or `instance_id` should be provided""" diff --git a/src/gcore/types/cloud/load_balancers/pool_update_params.py b/src/gcore/types/cloud/load_balancers/pool_update_params.py index d02fa244..7d851e8f 100644 --- a/src/gcore/types/cloud/load_balancers/pool_update_params.py +++ b/src/gcore/types/cloud/load_balancers/pool_update_params.py @@ -103,7 +103,12 @@ class Member(TypedDict, total=False): """Member IP port""" admin_state_up: Optional[bool] - """true if enabled. Defaults to true""" + """Administrative state of the resource. + + When set to true, the resource is enabled and operational. When set to false, + the resource is disabled and will not process traffic. When null is passed, the + value is skipped and defaults to true. + """ instance_id: Optional[str] """Either `subnet_id` or `instance_id` should be provided""" diff --git a/src/gcore/types/cloud/load_balancers/pools/member_add_params.py b/src/gcore/types/cloud/load_balancers/pools/member_add_params.py index 341128b5..e14dca71 100644 --- a/src/gcore/types/cloud/load_balancers/pools/member_add_params.py +++ b/src/gcore/types/cloud/load_balancers/pools/member_add_params.py @@ -22,7 +22,12 @@ class MemberAddParams(TypedDict, total=False): """Member IP port""" admin_state_up: Optional[bool] - """true if enabled. Defaults to true""" + """Administrative state of the resource. + + When set to true, the resource is enabled and operational. When set to false, + the resource is disabled and will not process traffic. When null is passed, the + value is skipped and defaults to true. + """ instance_id: Optional[str] """Either `subnet_id` or `instance_id` should be provided""" diff --git a/src/gcore/types/cloud/member.py b/src/gcore/types/cloud/member.py index edcda79f..144a0eab 100644 --- a/src/gcore/types/cloud/member.py +++ b/src/gcore/types/cloud/member.py @@ -17,7 +17,12 @@ class Member(BaseModel): """Member IP address""" admin_state_up: bool - """true if enabled. Defaults to true""" + """Administrative state of the resource. + + When set to true, the resource is enabled and operational. When set to false, + the resource is disabled and will not process traffic. When null is passed, the + value is skipped and defaults to true. + """ operating_status: LoadBalancerOperatingStatus """Member operating status of the entity""" diff --git a/tests/api_resources/cloud/load_balancers/pools/test_members.py b/tests/api_resources/cloud/load_balancers/pools/test_members.py index 3b32b30c..151b9304 100644 --- a/tests/api_resources/cloud/load_balancers/pools/test_members.py +++ b/tests/api_resources/cloud/load_balancers/pools/test_members.py @@ -36,7 +36,7 @@ def test_method_add_with_all_params(self, client: Gcore) -> None: region_id=1, address="192.168.40.33", protocol_port=80, - admin_state_up=False, + admin_state_up=True, instance_id="a7e7e8d6-0bf7-4ac9-8170-831b47ee2ba9", monitor_address="monitor_address", monitor_port=0, @@ -171,7 +171,7 @@ async def test_method_add_with_all_params(self, async_client: AsyncGcore) -> Non region_id=1, address="192.168.40.33", protocol_port=80, - admin_state_up=False, + admin_state_up=True, instance_id="a7e7e8d6-0bf7-4ac9-8170-831b47ee2ba9", monitor_address="monitor_address", monitor_port=0, diff --git a/tests/api_resources/cloud/load_balancers/test_pools.py b/tests/api_resources/cloud/load_balancers/test_pools.py index 004e5dd3..edd9b1be 100644 --- a/tests/api_resources/cloud/load_balancers/test_pools.py +++ b/tests/api_resources/cloud/load_balancers/test_pools.py @@ -54,7 +54,7 @@ def test_method_create_with_all_params(self, client: Gcore) -> None: { "address": "192.168.1.101", "protocol_port": 8000, - "admin_state_up": False, + "admin_state_up": True, "instance_id": "a7e7e8d6-0bf7-4ac9-8170-831b47ee2ba9", "monitor_address": "monitor_address", "monitor_port": 0, @@ -64,7 +64,7 @@ def test_method_create_with_all_params(self, client: Gcore) -> None: { "address": "192.168.1.102", "protocol_port": 8000, - "admin_state_up": False, + "admin_state_up": True, "instance_id": "169942e0-9b53-42df-95ef-1a8b6525c2bd", "monitor_address": "monitor_address", "monitor_port": 0, @@ -149,7 +149,7 @@ def test_method_update_with_all_params(self, client: Gcore) -> None: { "address": "192.168.40.33", "protocol_port": 80, - "admin_state_up": False, + "admin_state_up": True, "instance_id": "a7e7e8d6-0bf7-4ac9-8170-831b47ee2ba9", "monitor_address": "monitor_address", "monitor_port": 0, @@ -389,7 +389,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncGcore) -> { "address": "192.168.1.101", "protocol_port": 8000, - "admin_state_up": False, + "admin_state_up": True, "instance_id": "a7e7e8d6-0bf7-4ac9-8170-831b47ee2ba9", "monitor_address": "monitor_address", "monitor_port": 0, @@ -399,7 +399,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncGcore) -> { "address": "192.168.1.102", "protocol_port": 8000, - "admin_state_up": False, + "admin_state_up": True, "instance_id": "169942e0-9b53-42df-95ef-1a8b6525c2bd", "monitor_address": "monitor_address", "monitor_port": 0, @@ -484,7 +484,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncGcore) -> { "address": "192.168.40.33", "protocol_port": 80, - "admin_state_up": False, + "admin_state_up": True, "instance_id": "a7e7e8d6-0bf7-4ac9-8170-831b47ee2ba9", "monitor_address": "monitor_address", "monitor_port": 0, diff --git a/tests/api_resources/cloud/test_load_balancers.py b/tests/api_resources/cloud/test_load_balancers.py index 3a61a657..fa8ddb3d 100644 --- a/tests/api_resources/cloud/test_load_balancers.py +++ b/tests/api_resources/cloud/test_load_balancers.py @@ -70,7 +70,7 @@ def test_method_create_with_all_params(self, client: Gcore) -> None: { "address": "192.168.1.101", "protocol_port": 8000, - "admin_state_up": False, + "admin_state_up": True, "instance_id": "a7e7e8d6-0bf7-4ac9-8170-831b47ee2ba9", "monitor_address": "monitor_address", "monitor_port": 0, @@ -80,7 +80,7 @@ def test_method_create_with_all_params(self, client: Gcore) -> None: { "address": "192.168.1.102", "protocol_port": 8000, - "admin_state_up": False, + "admin_state_up": True, "instance_id": "169942e0-9b53-42df-95ef-1a8b6525c2bd", "monitor_address": "monitor_address", "monitor_port": 0, @@ -536,7 +536,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncGcore) -> { "address": "192.168.1.101", "protocol_port": 8000, - "admin_state_up": False, + "admin_state_up": True, "instance_id": "a7e7e8d6-0bf7-4ac9-8170-831b47ee2ba9", "monitor_address": "monitor_address", "monitor_port": 0, @@ -546,7 +546,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncGcore) -> { "address": "192.168.1.102", "protocol_port": 8000, - "admin_state_up": False, + "admin_state_up": True, "instance_id": "169942e0-9b53-42df-95ef-1a8b6525c2bd", "monitor_address": "monitor_address", "monitor_port": 0, From 8dd7ccb677a41455d0c3ee6407e21271485669aa Mon Sep 17 00:00:00 2001 From: Pedro Oliveira <8281907+pedrodeoliveira@users.noreply.github.com> Date: Thu, 3 Jul 2025 15:24:07 +0100 Subject: [PATCH 48/50] chore(cloud): skip load balancer test statuses * chore(cloud): skip load balancer test statuses * chore(cloud): improve skip reason message --- tests/api_resources/cloud/load_balancers/test_statuses.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/api_resources/cloud/load_balancers/test_statuses.py b/tests/api_resources/cloud/load_balancers/test_statuses.py index cf38797d..775d027e 100644 --- a/tests/api_resources/cloud/load_balancers/test_statuses.py +++ b/tests/api_resources/cloud/load_balancers/test_statuses.py @@ -13,7 +13,7 @@ base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - +@pytest.mark.skip(reason="Skipping tests due to Prism routing request to the wrong path") class TestStatuses: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @@ -98,6 +98,7 @@ def test_path_params_get(self, client: Gcore) -> None: ) +@pytest.mark.skip(reason="Skipping tests due to Prism routing request to the wrong path") class TestAsyncStatuses: parametrize = pytest.mark.parametrize( "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] From 1507ac37f5d588a999880cf5f51c94293f65b039 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 4 Jul 2025 09:23:18 +0000 Subject: [PATCH 49/50] feat(iam): add IAM --- .stats.yml | 4 +- api.md | 43 ++ src/gcore/_client.py | 9 + src/gcore/pagination.py | 62 +- src/gcore/resources/__init__.py | 14 + src/gcore/resources/iam/__init__.py | 47 ++ src/gcore/resources/iam/api_tokens.py | 521 ++++++++++++++ src/gcore/resources/iam/iam.py | 199 ++++++ src/gcore/resources/iam/users.py | 642 ++++++++++++++++++ src/gcore/types/iam/__init__.py | 17 + src/gcore/types/iam/account_overview.py | 488 +++++++++++++ src/gcore/types/iam/api_token.py | 78 +++ src/gcore/types/iam/api_token_create.py | 15 + .../types/iam/api_token_create_params.py | 42 ++ src/gcore/types/iam/api_token_list.py | 81 +++ src/gcore/types/iam/api_token_list_params.py | 41 ++ src/gcore/types/iam/user.py | 86 +++ src/gcore/types/iam/user_detailed.py | 104 +++ src/gcore/types/iam/user_invite.py | 15 + src/gcore/types/iam/user_invite_params.py | 37 + src/gcore/types/iam/user_list_params.py | 15 + src/gcore/types/iam/user_update.py | 104 +++ src/gcore/types/iam/user_update_params.py | 52 ++ tests/api_resources/iam/__init__.py | 1 + tests/api_resources/iam/test_api_tokens.py | 356 ++++++++++ tests/api_resources/iam/test_users.py | 428 ++++++++++++ tests/api_resources/test_iam.py | 74 ++ 27 files changed, 3572 insertions(+), 3 deletions(-) create mode 100644 src/gcore/resources/iam/__init__.py create mode 100644 src/gcore/resources/iam/api_tokens.py create mode 100644 src/gcore/resources/iam/iam.py create mode 100644 src/gcore/resources/iam/users.py create mode 100644 src/gcore/types/iam/__init__.py create mode 100644 src/gcore/types/iam/account_overview.py create mode 100644 src/gcore/types/iam/api_token.py create mode 100644 src/gcore/types/iam/api_token_create.py create mode 100644 src/gcore/types/iam/api_token_create_params.py create mode 100644 src/gcore/types/iam/api_token_list.py create mode 100644 src/gcore/types/iam/api_token_list_params.py create mode 100644 src/gcore/types/iam/user.py create mode 100644 src/gcore/types/iam/user_detailed.py create mode 100644 src/gcore/types/iam/user_invite.py create mode 100644 src/gcore/types/iam/user_invite_params.py create mode 100644 src/gcore/types/iam/user_list_params.py create mode 100644 src/gcore/types/iam/user_update.py create mode 100644 src/gcore/types/iam/user_update_params.py create mode 100644 tests/api_resources/iam/__init__.py create mode 100644 tests/api_resources/iam/test_api_tokens.py create mode 100644 tests/api_resources/iam/test_users.py create mode 100644 tests/api_resources/test_iam.py diff --git a/.stats.yml b/.stats.yml index 5d3a588f..f6858de3 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 299 +configured_endpoints: 309 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-a3eb777ed002489583f1ec680d7ac5638c4bf54a843d381a1c1ad5d1c9f5e967.yml openapi_spec_hash: 3e4993495b21ba44b2969cc41cdb1221 -config_hash: 9e417d14176a0e60806f859c2a25ddd7 +config_hash: 708e4dbf0025978d06dedb02b94647a3 diff --git a/api.md b/api.md index a21f7d28..040eb35c 100644 --- a/api.md +++ b/api.md @@ -1156,3 +1156,46 @@ Methods: - client.waap.ip_info.get_top_urls(\*\*params) -> IPInfoGetTopURLsResponse - client.waap.ip_info.get_top_user_agents(\*\*params) -> IPInfoGetTopUserAgentsResponse - client.waap.ip_info.list_attacked_countries(\*\*params) -> IPInfoListAttackedCountriesResponse + +# Iam + +Types: + +```python +from gcore.types.iam import AccountOverview +``` + +Methods: + +- client.iam.get_account_overview() -> AccountOverview + +## APITokens + +Types: + +```python +from gcore.types.iam import APIToken, APITokenCreate, APITokenList +``` + +Methods: + +- client.iam.api_tokens.create(client_id, \*\*params) -> APITokenCreate +- client.iam.api_tokens.list(client_id, \*\*params) -> APITokenList +- client.iam.api_tokens.delete(token_id, \*, client_id) -> None +- client.iam.api_tokens.get(token_id, \*, client_id) -> APIToken + +## Users + +Types: + +```python +from gcore.types.iam import User, UserDetailed, UserInvite, UserUpdate +``` + +Methods: + +- client.iam.users.update(user_id, \*\*params) -> UserUpdate +- client.iam.users.list(\*\*params) -> SyncOffsetPageIam[User] +- client.iam.users.delete(user_id, \*, client_id) -> None +- client.iam.users.get(user_id) -> UserDetailed +- client.iam.users.invite(\*\*params) -> UserInvite diff --git a/src/gcore/_client.py b/src/gcore/_client.py index 009df272..05954d6f 100644 --- a/src/gcore/_client.py +++ b/src/gcore/_client.py @@ -28,6 +28,7 @@ SyncAPIClient, AsyncAPIClient, ) +from .resources.iam import iam from .resources.waap import waap from .resources.cloud import cloud @@ -37,6 +38,7 @@ class Gcore(SyncAPIClient): cloud: cloud.CloudResource waap: waap.WaapResource + iam: iam.IamResource with_raw_response: GcoreWithRawResponse with_streaming_response: GcoreWithStreamedResponse @@ -117,6 +119,7 @@ def __init__( self.cloud = cloud.CloudResource(self) self.waap = waap.WaapResource(self) + self.iam = iam.IamResource(self) self.with_raw_response = GcoreWithRawResponse(self) self.with_streaming_response = GcoreWithStreamedResponse(self) @@ -252,6 +255,7 @@ def _make_status_error( class AsyncGcore(AsyncAPIClient): cloud: cloud.AsyncCloudResource waap: waap.AsyncWaapResource + iam: iam.AsyncIamResource with_raw_response: AsyncGcoreWithRawResponse with_streaming_response: AsyncGcoreWithStreamedResponse @@ -332,6 +336,7 @@ def __init__( self.cloud = cloud.AsyncCloudResource(self) self.waap = waap.AsyncWaapResource(self) + self.iam = iam.AsyncIamResource(self) self.with_raw_response = AsyncGcoreWithRawResponse(self) self.with_streaming_response = AsyncGcoreWithStreamedResponse(self) @@ -468,24 +473,28 @@ class GcoreWithRawResponse: def __init__(self, client: Gcore) -> None: self.cloud = cloud.CloudResourceWithRawResponse(client.cloud) self.waap = waap.WaapResourceWithRawResponse(client.waap) + self.iam = iam.IamResourceWithRawResponse(client.iam) class AsyncGcoreWithRawResponse: def __init__(self, client: AsyncGcore) -> None: self.cloud = cloud.AsyncCloudResourceWithRawResponse(client.cloud) self.waap = waap.AsyncWaapResourceWithRawResponse(client.waap) + self.iam = iam.AsyncIamResourceWithRawResponse(client.iam) class GcoreWithStreamedResponse: def __init__(self, client: Gcore) -> None: self.cloud = cloud.CloudResourceWithStreamingResponse(client.cloud) self.waap = waap.WaapResourceWithStreamingResponse(client.waap) + self.iam = iam.IamResourceWithStreamingResponse(client.iam) class AsyncGcoreWithStreamedResponse: def __init__(self, client: AsyncGcore) -> None: self.cloud = cloud.AsyncCloudResourceWithStreamingResponse(client.cloud) self.waap = waap.AsyncWaapResourceWithStreamingResponse(client.waap) + self.iam = iam.AsyncIamResourceWithStreamingResponse(client.iam) Client = Gcore diff --git a/src/gcore/pagination.py b/src/gcore/pagination.py index cf967212..870a0d3a 100644 --- a/src/gcore/pagination.py +++ b/src/gcore/pagination.py @@ -5,7 +5,7 @@ from ._base_client import BasePage, PageInfo, BaseSyncPage, BaseAsyncPage -__all__ = ["SyncOffsetPage", "AsyncOffsetPage"] +__all__ = ["SyncOffsetPage", "AsyncOffsetPage", "SyncOffsetPageIam", "AsyncOffsetPageIam"] _T = TypeVar("_T") @@ -68,3 +68,63 @@ def next_page_info(self) -> Optional[PageInfo]: return PageInfo(params={"offset": current_count}) return None + + +class SyncOffsetPageIam(BaseSyncPage[_T], BasePage[_T], Generic[_T]): + result: List[_T] + count: Optional[int] = None + + @override + def _get_page_items(self) -> List[_T]: + result = self.result + if not result: + return [] + return result + + @override + def next_page_info(self) -> Optional[PageInfo]: + offset = self._options.params.get("offset") or 0 + if not isinstance(offset, int): + raise ValueError(f'Expected "offset" param to be an integer but got {offset}') + + length = len(self._get_page_items()) + current_count = offset + length + + count = self.count + if count is None: + return None + + if current_count < count: + return PageInfo(params={"offset": current_count}) + + return None + + +class AsyncOffsetPageIam(BaseAsyncPage[_T], BasePage[_T], Generic[_T]): + result: List[_T] + count: Optional[int] = None + + @override + def _get_page_items(self) -> List[_T]: + result = self.result + if not result: + return [] + return result + + @override + def next_page_info(self) -> Optional[PageInfo]: + offset = self._options.params.get("offset") or 0 + if not isinstance(offset, int): + raise ValueError(f'Expected "offset" param to be an integer but got {offset}') + + length = len(self._get_page_items()) + current_count = offset + length + + count = self.count + if count is None: + return None + + if current_count < count: + return PageInfo(params={"offset": current_count}) + + return None diff --git a/src/gcore/resources/__init__.py b/src/gcore/resources/__init__.py index 502dad77..ed8fc7da 100644 --- a/src/gcore/resources/__init__.py +++ b/src/gcore/resources/__init__.py @@ -1,5 +1,13 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from .iam import ( + IamResource, + AsyncIamResource, + IamResourceWithRawResponse, + AsyncIamResourceWithRawResponse, + IamResourceWithStreamingResponse, + AsyncIamResourceWithStreamingResponse, +) from .waap import ( WaapResource, AsyncWaapResource, @@ -30,4 +38,10 @@ "AsyncWaapResourceWithRawResponse", "WaapResourceWithStreamingResponse", "AsyncWaapResourceWithStreamingResponse", + "IamResource", + "AsyncIamResource", + "IamResourceWithRawResponse", + "AsyncIamResourceWithRawResponse", + "IamResourceWithStreamingResponse", + "AsyncIamResourceWithStreamingResponse", ] diff --git a/src/gcore/resources/iam/__init__.py b/src/gcore/resources/iam/__init__.py new file mode 100644 index 00000000..e8a7c852 --- /dev/null +++ b/src/gcore/resources/iam/__init__.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .iam import ( + IamResource, + AsyncIamResource, + IamResourceWithRawResponse, + AsyncIamResourceWithRawResponse, + IamResourceWithStreamingResponse, + AsyncIamResourceWithStreamingResponse, +) +from .users import ( + UsersResource, + AsyncUsersResource, + UsersResourceWithRawResponse, + AsyncUsersResourceWithRawResponse, + UsersResourceWithStreamingResponse, + AsyncUsersResourceWithStreamingResponse, +) +from .api_tokens import ( + APITokensResource, + AsyncAPITokensResource, + APITokensResourceWithRawResponse, + AsyncAPITokensResourceWithRawResponse, + APITokensResourceWithStreamingResponse, + AsyncAPITokensResourceWithStreamingResponse, +) + +__all__ = [ + "APITokensResource", + "AsyncAPITokensResource", + "APITokensResourceWithRawResponse", + "AsyncAPITokensResourceWithRawResponse", + "APITokensResourceWithStreamingResponse", + "AsyncAPITokensResourceWithStreamingResponse", + "UsersResource", + "AsyncUsersResource", + "UsersResourceWithRawResponse", + "AsyncUsersResourceWithRawResponse", + "UsersResourceWithStreamingResponse", + "AsyncUsersResourceWithStreamingResponse", + "IamResource", + "AsyncIamResource", + "IamResourceWithRawResponse", + "AsyncIamResourceWithRawResponse", + "IamResourceWithStreamingResponse", + "AsyncIamResourceWithStreamingResponse", +] diff --git a/src/gcore/resources/iam/api_tokens.py b/src/gcore/resources/iam/api_tokens.py new file mode 100644 index 00000000..ecd59cb6 --- /dev/null +++ b/src/gcore/resources/iam/api_tokens.py @@ -0,0 +1,521 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..._types import NOT_GIVEN, Body, Query, Headers, NoneType, NotGiven +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...types.iam import api_token_list_params, api_token_create_params +from ..._base_client import make_request_options +from ...types.iam.api_token import APIToken +from ...types.iam.api_token_list import APITokenList +from ...types.iam.api_token_create import APITokenCreate + +__all__ = ["APITokensResource", "AsyncAPITokensResource"] + + +class APITokensResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> APITokensResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/G-Core/gcore-python#accessing-raw-response-data-eg-headers + """ + return APITokensResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> APITokensResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/G-Core/gcore-python#with_streaming_response + """ + return APITokensResourceWithStreamingResponse(self) + + def create( + self, + client_id: int, + *, + client_user: api_token_create_params.ClientUser, + exp_date: str, + name: str, + description: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> APITokenCreate: + """ + Create an API token in the current account. + + Args: + client_user: API token role. + + exp_date: Date when the API token becomes expired (ISO 8086/RFC 3339 format), UTC. If + null, then the API token will never expire. + + name: API token name. + + description: API token description. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + f"/iam/clients/{client_id}/tokens", + body=maybe_transform( + { + "client_user": client_user, + "exp_date": exp_date, + "name": name, + "description": description, + }, + api_token_create_params.APITokenCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=APITokenCreate, + ) + + def list( + self, + client_id: int, + *, + deleted: bool | NotGiven = NOT_GIVEN, + issued_by: int | NotGiven = NOT_GIVEN, + not_issued_by: int | NotGiven = NOT_GIVEN, + role: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> APITokenList: + """Get information about your permanent API tokens in the account. + + A user with the + Administrators role gets information about all API tokens in the account. + + Args: + deleted: The state of API tokens included in the response. + Two possible values: + + - True - API token was not deleted.\\** False - API token was deleted. + + Example, _&deleted=True_ + + issued_by: User's ID. Use to get API tokens issued by a particular user. + Example, _&`issued_by`=1234_ + + not_issued_by: User's ID. Use to get API tokens issued by anyone except a particular user. + Example, _¬_issued_by=1234_ + + role: + Group's ID. Possible values are: + + - 1 - Administrators* 2 - Users* 5 - Engineers* 3009 - Purge and Prefetch only + (API+Web)* 3022 - Purge and Prefetch only (API) + + Example, _&role=Engineers_ + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + f"/iam/clients/{client_id}/tokens", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "deleted": deleted, + "issued_by": issued_by, + "not_issued_by": not_issued_by, + "role": role, + }, + api_token_list_params.APITokenListParams, + ), + ), + cast_to=APITokenList, + ) + + def delete( + self, + token_id: int, + *, + client_id: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> None: + """Delete API token from current account. + + Ensure that the API token is not being + used by an active application. After deleting the token, all applications that + use this token will not be able to get access to your account via API. The + action cannot be reversed. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._delete( + f"/iam/clients/{client_id}/tokens/{token_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + def get( + self, + token_id: int, + *, + client_id: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> APIToken: + """ + Get API Token + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + f"/iam/clients/{client_id}/tokens/{token_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=APIToken, + ) + + +class AsyncAPITokensResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncAPITokensResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/G-Core/gcore-python#accessing-raw-response-data-eg-headers + """ + return AsyncAPITokensResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncAPITokensResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/G-Core/gcore-python#with_streaming_response + """ + return AsyncAPITokensResourceWithStreamingResponse(self) + + async def create( + self, + client_id: int, + *, + client_user: api_token_create_params.ClientUser, + exp_date: str, + name: str, + description: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> APITokenCreate: + """ + Create an API token in the current account. + + Args: + client_user: API token role. + + exp_date: Date when the API token becomes expired (ISO 8086/RFC 3339 format), UTC. If + null, then the API token will never expire. + + name: API token name. + + description: API token description. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + f"/iam/clients/{client_id}/tokens", + body=await async_maybe_transform( + { + "client_user": client_user, + "exp_date": exp_date, + "name": name, + "description": description, + }, + api_token_create_params.APITokenCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=APITokenCreate, + ) + + async def list( + self, + client_id: int, + *, + deleted: bool | NotGiven = NOT_GIVEN, + issued_by: int | NotGiven = NOT_GIVEN, + not_issued_by: int | NotGiven = NOT_GIVEN, + role: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> APITokenList: + """Get information about your permanent API tokens in the account. + + A user with the + Administrators role gets information about all API tokens in the account. + + Args: + deleted: The state of API tokens included in the response. + Two possible values: + + - True - API token was not deleted.\\** False - API token was deleted. + + Example, _&deleted=True_ + + issued_by: User's ID. Use to get API tokens issued by a particular user. + Example, _&`issued_by`=1234_ + + not_issued_by: User's ID. Use to get API tokens issued by anyone except a particular user. + Example, _¬_issued_by=1234_ + + role: + Group's ID. Possible values are: + + - 1 - Administrators* 2 - Users* 5 - Engineers* 3009 - Purge and Prefetch only + (API+Web)* 3022 - Purge and Prefetch only (API) + + Example, _&role=Engineers_ + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + f"/iam/clients/{client_id}/tokens", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "deleted": deleted, + "issued_by": issued_by, + "not_issued_by": not_issued_by, + "role": role, + }, + api_token_list_params.APITokenListParams, + ), + ), + cast_to=APITokenList, + ) + + async def delete( + self, + token_id: int, + *, + client_id: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> None: + """Delete API token from current account. + + Ensure that the API token is not being + used by an active application. After deleting the token, all applications that + use this token will not be able to get access to your account via API. The + action cannot be reversed. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._delete( + f"/iam/clients/{client_id}/tokens/{token_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + async def get( + self, + token_id: int, + *, + client_id: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> APIToken: + """ + Get API Token + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + f"/iam/clients/{client_id}/tokens/{token_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=APIToken, + ) + + +class APITokensResourceWithRawResponse: + def __init__(self, api_tokens: APITokensResource) -> None: + self._api_tokens = api_tokens + + self.create = to_raw_response_wrapper( + api_tokens.create, + ) + self.list = to_raw_response_wrapper( + api_tokens.list, + ) + self.delete = to_raw_response_wrapper( + api_tokens.delete, + ) + self.get = to_raw_response_wrapper( + api_tokens.get, + ) + + +class AsyncAPITokensResourceWithRawResponse: + def __init__(self, api_tokens: AsyncAPITokensResource) -> None: + self._api_tokens = api_tokens + + self.create = async_to_raw_response_wrapper( + api_tokens.create, + ) + self.list = async_to_raw_response_wrapper( + api_tokens.list, + ) + self.delete = async_to_raw_response_wrapper( + api_tokens.delete, + ) + self.get = async_to_raw_response_wrapper( + api_tokens.get, + ) + + +class APITokensResourceWithStreamingResponse: + def __init__(self, api_tokens: APITokensResource) -> None: + self._api_tokens = api_tokens + + self.create = to_streamed_response_wrapper( + api_tokens.create, + ) + self.list = to_streamed_response_wrapper( + api_tokens.list, + ) + self.delete = to_streamed_response_wrapper( + api_tokens.delete, + ) + self.get = to_streamed_response_wrapper( + api_tokens.get, + ) + + +class AsyncAPITokensResourceWithStreamingResponse: + def __init__(self, api_tokens: AsyncAPITokensResource) -> None: + self._api_tokens = api_tokens + + self.create = async_to_streamed_response_wrapper( + api_tokens.create, + ) + self.list = async_to_streamed_response_wrapper( + api_tokens.list, + ) + self.delete = async_to_streamed_response_wrapper( + api_tokens.delete, + ) + self.get = async_to_streamed_response_wrapper( + api_tokens.get, + ) diff --git a/src/gcore/resources/iam/iam.py b/src/gcore/resources/iam/iam.py new file mode 100644 index 00000000..54b4653f --- /dev/null +++ b/src/gcore/resources/iam/iam.py @@ -0,0 +1,199 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from .users import ( + UsersResource, + AsyncUsersResource, + UsersResourceWithRawResponse, + AsyncUsersResourceWithRawResponse, + UsersResourceWithStreamingResponse, + AsyncUsersResourceWithStreamingResponse, +) +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._compat import cached_property +from .api_tokens import ( + APITokensResource, + AsyncAPITokensResource, + APITokensResourceWithRawResponse, + AsyncAPITokensResourceWithRawResponse, + APITokensResourceWithStreamingResponse, + AsyncAPITokensResourceWithStreamingResponse, +) +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.iam.account_overview import AccountOverview + +__all__ = ["IamResource", "AsyncIamResource"] + + +class IamResource(SyncAPIResource): + @cached_property + def api_tokens(self) -> APITokensResource: + return APITokensResource(self._client) + + @cached_property + def users(self) -> UsersResource: + return UsersResource(self._client) + + @cached_property + def with_raw_response(self) -> IamResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/G-Core/gcore-python#accessing-raw-response-data-eg-headers + """ + return IamResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> IamResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/G-Core/gcore-python#with_streaming_response + """ + return IamResourceWithStreamingResponse(self) + + def get_account_overview( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AccountOverview: + """Get information about your profile, users and other account details.""" + return self._get( + "/iam/clients/me", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AccountOverview, + ) + + +class AsyncIamResource(AsyncAPIResource): + @cached_property + def api_tokens(self) -> AsyncAPITokensResource: + return AsyncAPITokensResource(self._client) + + @cached_property + def users(self) -> AsyncUsersResource: + return AsyncUsersResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncIamResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/G-Core/gcore-python#accessing-raw-response-data-eg-headers + """ + return AsyncIamResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncIamResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/G-Core/gcore-python#with_streaming_response + """ + return AsyncIamResourceWithStreamingResponse(self) + + async def get_account_overview( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AccountOverview: + """Get information about your profile, users and other account details.""" + return await self._get( + "/iam/clients/me", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AccountOverview, + ) + + +class IamResourceWithRawResponse: + def __init__(self, iam: IamResource) -> None: + self._iam = iam + + self.get_account_overview = to_raw_response_wrapper( + iam.get_account_overview, + ) + + @cached_property + def api_tokens(self) -> APITokensResourceWithRawResponse: + return APITokensResourceWithRawResponse(self._iam.api_tokens) + + @cached_property + def users(self) -> UsersResourceWithRawResponse: + return UsersResourceWithRawResponse(self._iam.users) + + +class AsyncIamResourceWithRawResponse: + def __init__(self, iam: AsyncIamResource) -> None: + self._iam = iam + + self.get_account_overview = async_to_raw_response_wrapper( + iam.get_account_overview, + ) + + @cached_property + def api_tokens(self) -> AsyncAPITokensResourceWithRawResponse: + return AsyncAPITokensResourceWithRawResponse(self._iam.api_tokens) + + @cached_property + def users(self) -> AsyncUsersResourceWithRawResponse: + return AsyncUsersResourceWithRawResponse(self._iam.users) + + +class IamResourceWithStreamingResponse: + def __init__(self, iam: IamResource) -> None: + self._iam = iam + + self.get_account_overview = to_streamed_response_wrapper( + iam.get_account_overview, + ) + + @cached_property + def api_tokens(self) -> APITokensResourceWithStreamingResponse: + return APITokensResourceWithStreamingResponse(self._iam.api_tokens) + + @cached_property + def users(self) -> UsersResourceWithStreamingResponse: + return UsersResourceWithStreamingResponse(self._iam.users) + + +class AsyncIamResourceWithStreamingResponse: + def __init__(self, iam: AsyncIamResource) -> None: + self._iam = iam + + self.get_account_overview = async_to_streamed_response_wrapper( + iam.get_account_overview, + ) + + @cached_property + def api_tokens(self) -> AsyncAPITokensResourceWithStreamingResponse: + return AsyncAPITokensResourceWithStreamingResponse(self._iam.api_tokens) + + @cached_property + def users(self) -> AsyncUsersResourceWithStreamingResponse: + return AsyncUsersResourceWithStreamingResponse(self._iam.users) diff --git a/src/gcore/resources/iam/users.py b/src/gcore/resources/iam/users.py new file mode 100644 index 00000000..8ba9d6de --- /dev/null +++ b/src/gcore/resources/iam/users.py @@ -0,0 +1,642 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Iterable, Optional +from typing_extensions import Literal + +import httpx + +from ..._types import NOT_GIVEN, Body, Query, Headers, NoneType, NotGiven +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...types.iam import user_list_params, user_invite_params, user_update_params +from ...pagination import SyncOffsetPageIam, AsyncOffsetPageIam +from ..._base_client import AsyncPaginator, make_request_options +from ...types.iam.user import User +from ...types.iam.user_invite import UserInvite +from ...types.iam.user_update import UserUpdate +from ...types.iam.user_detailed import UserDetailed + +__all__ = ["UsersResource", "AsyncUsersResource"] + + +class UsersResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> UsersResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/G-Core/gcore-python#accessing-raw-response-data-eg-headers + """ + return UsersResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> UsersResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/G-Core/gcore-python#with_streaming_response + """ + return UsersResourceWithStreamingResponse(self) + + def update( + self, + user_id: int, + *, + auth_types: List[Literal["password", "sso", "github", "google-oauth2"]] | NotGiven = NOT_GIVEN, + company: str | NotGiven = NOT_GIVEN, + email: str | NotGiven = NOT_GIVEN, + groups: Iterable[user_update_params.Group] | NotGiven = NOT_GIVEN, + lang: Literal["de", "en", "ru", "zh", "az"] | NotGiven = NOT_GIVEN, + name: Optional[str] | NotGiven = NOT_GIVEN, + phone: Optional[str] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> UserUpdate: + """This method updates user's details. + + Args: + auth_types: System field. + + List of auth types available for the account. + + company: User's company. + + email: User's email address. + + groups: + User's group in the current account. IAM supports 5 groups: + + - Users + - Administrators + - Engineers + - Purge and Prefetch only (API) + - Purge and Prefetch only (API+Web) + + lang: User's language. Defines language of the control panel and email messages. + + name: User's name. + + phone: User's phone. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._patch( + f"/iam/users/{user_id}", + body=maybe_transform( + { + "auth_types": auth_types, + "company": company, + "email": email, + "groups": groups, + "lang": lang, + "name": name, + "phone": phone, + }, + user_update_params.UserUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=UserUpdate, + ) + + def list( + self, + *, + limit: int | NotGiven = NOT_GIVEN, + offset: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SyncOffsetPageIam[User]: + """Get a list of users. + + Pass a value for the `limit` parameter in your request if + you want retrieve a paginated result. Otherwise API returns a list with all + users without pagination. + + Args: + limit: The maximum number of items. + + offset: Offset relative to the beginning of list. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/iam/users", + page=SyncOffsetPageIam[User], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "offset": offset, + }, + user_list_params.UserListParams, + ), + ), + model=User, + ) + + def delete( + self, + user_id: int, + *, + client_id: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> None: + """Revokes user's access to the specified account. + + If the specified user doesn't + have access to multiple accounts, the user is deleted. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._delete( + f"/iam/clients/{client_id}/client-users/{user_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + def get( + self, + user_id: int, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> UserDetailed: + """ + Get user's details + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + f"/iam/users/{user_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=UserDetailed, + ) + + def invite( + self, + *, + client_id: int, + email: str, + user_role: user_invite_params.UserRole, + lang: Literal["de", "en", "ru", "zh", "az"] | NotGiven = NOT_GIVEN, + name: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> UserInvite: + """Invite a user to the account. + + User will receive an email. The new user will + receive an invitation email with a link to create an account password, the + existing user will be notified about the invitation to the account. + + Args: + client_id: ID of account. + + email: User email. + + lang: User's language. Defines language of the control panel and email messages. + + name: User name. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/iam/clients/invite_user", + body=maybe_transform( + { + "client_id": client_id, + "email": email, + "user_role": user_role, + "lang": lang, + "name": name, + }, + user_invite_params.UserInviteParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=UserInvite, + ) + + +class AsyncUsersResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncUsersResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/G-Core/gcore-python#accessing-raw-response-data-eg-headers + """ + return AsyncUsersResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncUsersResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/G-Core/gcore-python#with_streaming_response + """ + return AsyncUsersResourceWithStreamingResponse(self) + + async def update( + self, + user_id: int, + *, + auth_types: List[Literal["password", "sso", "github", "google-oauth2"]] | NotGiven = NOT_GIVEN, + company: str | NotGiven = NOT_GIVEN, + email: str | NotGiven = NOT_GIVEN, + groups: Iterable[user_update_params.Group] | NotGiven = NOT_GIVEN, + lang: Literal["de", "en", "ru", "zh", "az"] | NotGiven = NOT_GIVEN, + name: Optional[str] | NotGiven = NOT_GIVEN, + phone: Optional[str] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> UserUpdate: + """This method updates user's details. + + Args: + auth_types: System field. + + List of auth types available for the account. + + company: User's company. + + email: User's email address. + + groups: + User's group in the current account. IAM supports 5 groups: + + - Users + - Administrators + - Engineers + - Purge and Prefetch only (API) + - Purge and Prefetch only (API+Web) + + lang: User's language. Defines language of the control panel and email messages. + + name: User's name. + + phone: User's phone. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._patch( + f"/iam/users/{user_id}", + body=await async_maybe_transform( + { + "auth_types": auth_types, + "company": company, + "email": email, + "groups": groups, + "lang": lang, + "name": name, + "phone": phone, + }, + user_update_params.UserUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=UserUpdate, + ) + + def list( + self, + *, + limit: int | NotGiven = NOT_GIVEN, + offset: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[User, AsyncOffsetPageIam[User]]: + """Get a list of users. + + Pass a value for the `limit` parameter in your request if + you want retrieve a paginated result. Otherwise API returns a list with all + users without pagination. + + Args: + limit: The maximum number of items. + + offset: Offset relative to the beginning of list. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/iam/users", + page=AsyncOffsetPageIam[User], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "offset": offset, + }, + user_list_params.UserListParams, + ), + ), + model=User, + ) + + async def delete( + self, + user_id: int, + *, + client_id: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> None: + """Revokes user's access to the specified account. + + If the specified user doesn't + have access to multiple accounts, the user is deleted. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._delete( + f"/iam/clients/{client_id}/client-users/{user_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + async def get( + self, + user_id: int, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> UserDetailed: + """ + Get user's details + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + f"/iam/users/{user_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=UserDetailed, + ) + + async def invite( + self, + *, + client_id: int, + email: str, + user_role: user_invite_params.UserRole, + lang: Literal["de", "en", "ru", "zh", "az"] | NotGiven = NOT_GIVEN, + name: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> UserInvite: + """Invite a user to the account. + + User will receive an email. The new user will + receive an invitation email with a link to create an account password, the + existing user will be notified about the invitation to the account. + + Args: + client_id: ID of account. + + email: User email. + + lang: User's language. Defines language of the control panel and email messages. + + name: User name. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/iam/clients/invite_user", + body=await async_maybe_transform( + { + "client_id": client_id, + "email": email, + "user_role": user_role, + "lang": lang, + "name": name, + }, + user_invite_params.UserInviteParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=UserInvite, + ) + + +class UsersResourceWithRawResponse: + def __init__(self, users: UsersResource) -> None: + self._users = users + + self.update = to_raw_response_wrapper( + users.update, + ) + self.list = to_raw_response_wrapper( + users.list, + ) + self.delete = to_raw_response_wrapper( + users.delete, + ) + self.get = to_raw_response_wrapper( + users.get, + ) + self.invite = to_raw_response_wrapper( + users.invite, + ) + + +class AsyncUsersResourceWithRawResponse: + def __init__(self, users: AsyncUsersResource) -> None: + self._users = users + + self.update = async_to_raw_response_wrapper( + users.update, + ) + self.list = async_to_raw_response_wrapper( + users.list, + ) + self.delete = async_to_raw_response_wrapper( + users.delete, + ) + self.get = async_to_raw_response_wrapper( + users.get, + ) + self.invite = async_to_raw_response_wrapper( + users.invite, + ) + + +class UsersResourceWithStreamingResponse: + def __init__(self, users: UsersResource) -> None: + self._users = users + + self.update = to_streamed_response_wrapper( + users.update, + ) + self.list = to_streamed_response_wrapper( + users.list, + ) + self.delete = to_streamed_response_wrapper( + users.delete, + ) + self.get = to_streamed_response_wrapper( + users.get, + ) + self.invite = to_streamed_response_wrapper( + users.invite, + ) + + +class AsyncUsersResourceWithStreamingResponse: + def __init__(self, users: AsyncUsersResource) -> None: + self._users = users + + self.update = async_to_streamed_response_wrapper( + users.update, + ) + self.list = async_to_streamed_response_wrapper( + users.list, + ) + self.delete = async_to_streamed_response_wrapper( + users.delete, + ) + self.get = async_to_streamed_response_wrapper( + users.get, + ) + self.invite = async_to_streamed_response_wrapper( + users.invite, + ) diff --git a/src/gcore/types/iam/__init__.py b/src/gcore/types/iam/__init__.py new file mode 100644 index 00000000..ad8745f0 --- /dev/null +++ b/src/gcore/types/iam/__init__.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .user import User as User +from .api_token import APIToken as APIToken +from .user_invite import UserInvite as UserInvite +from .user_update import UserUpdate as UserUpdate +from .user_detailed import UserDetailed as UserDetailed +from .api_token_list import APITokenList as APITokenList +from .account_overview import AccountOverview as AccountOverview +from .api_token_create import APITokenCreate as APITokenCreate +from .user_list_params import UserListParams as UserListParams +from .user_invite_params import UserInviteParams as UserInviteParams +from .user_update_params import UserUpdateParams as UserUpdateParams +from .api_token_list_params import APITokenListParams as APITokenListParams +from .api_token_create_params import APITokenCreateParams as APITokenCreateParams diff --git a/src/gcore/types/iam/account_overview.py b/src/gcore/types/iam/account_overview.py new file mode 100644 index 00000000..0f30b80b --- /dev/null +++ b/src/gcore/types/iam/account_overview.py @@ -0,0 +1,488 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = [ + "AccountOverview", + "FreeFeatures", + "FreeFeaturesCdn", + "FreeFeaturesCloud", + "FreeFeaturesDDOS", + "FreeFeaturesDNS", + "FreeFeaturesStorage", + "FreeFeaturesStreaming", + "PaidFeatures", + "PaidFeaturesCdn", + "PaidFeaturesCloud", + "PaidFeaturesDDOS", + "PaidFeaturesDNS", + "PaidFeaturesStorage", + "PaidFeaturesStreaming", + "ServiceStatuses", + "ServiceStatusesCdn", + "ServiceStatusesCloud", + "ServiceStatusesDDOS", + "ServiceStatusesDNS", + "ServiceStatusesStorage", + "ServiceStatusesStreaming", + "User", + "UserGroup", +] + + +class FreeFeaturesCdn(BaseModel): + create_date: Optional[str] = None + """Date and time when the feature was activated (ISO 8086/RFC 3339 format).""" + + feature_id: Optional[int] = None + """Feature ID.""" + + free_feature_id: Optional[int] = None + """Internal feature activation ID.""" + + name: Optional[str] = None + """Name of the feature.""" + + service: Optional[Literal["CDN", "STORAGE", "STREAMING", "DNS", "DDOS", "CLOUD"]] = None + """Service's name.""" + + +class FreeFeaturesCloud(BaseModel): + create_date: Optional[str] = None + """Date and time when the feature was activated (ISO 8086/RFC 3339 format).""" + + feature_id: Optional[int] = None + """Feature ID.""" + + free_feature_id: Optional[int] = None + """Internal feature activation ID.""" + + name: Optional[str] = None + """Name of the feature.""" + + service: Optional[Literal["CDN", "STORAGE", "STREAMING", "DNS", "DDOS", "CLOUD"]] = None + """Service's name.""" + + +class FreeFeaturesDDOS(BaseModel): + create_date: Optional[str] = None + """Date and time when the feature was activated (ISO 8086/RFC 3339 format).""" + + feature_id: Optional[int] = None + """Feature ID.""" + + free_feature_id: Optional[int] = None + """Internal feature activation ID.""" + + name: Optional[str] = None + """Name of the feature.""" + + service: Optional[Literal["CDN", "STORAGE", "STREAMING", "DNS", "DDOS", "CLOUD"]] = None + """Service's name.""" + + +class FreeFeaturesDNS(BaseModel): + create_date: Optional[str] = None + """Date and time when the feature was activated (ISO 8086/RFC 3339 format).""" + + feature_id: Optional[int] = None + """Feature ID.""" + + free_feature_id: Optional[int] = None + """Internal feature activation ID.""" + + name: Optional[str] = None + """Name of the feature.""" + + service: Optional[Literal["CDN", "STORAGE", "STREAMING", "DNS", "DDOS", "CLOUD"]] = None + """Service's name.""" + + +class FreeFeaturesStorage(BaseModel): + create_date: Optional[str] = None + """Date and time when the feature was activated (ISO 8086/RFC 3339 format).""" + + feature_id: Optional[int] = None + """Feature ID.""" + + free_feature_id: Optional[int] = None + """Internal feature activation ID.""" + + name: Optional[str] = None + """Name of the feature.""" + + service: Optional[Literal["CDN", "STORAGE", "STREAMING", "DNS", "DDOS", "CLOUD"]] = None + """Service's name.""" + + +class FreeFeaturesStreaming(BaseModel): + create_date: Optional[str] = None + """Date and time when the feature was activated (ISO 8086/RFC 3339 format).""" + + feature_id: Optional[int] = None + """Feature ID.""" + + free_feature_id: Optional[int] = None + """Internal feature activation ID.""" + + name: Optional[str] = None + """Name of the feature.""" + + service: Optional[Literal["CDN", "STORAGE", "STREAMING", "DNS", "DDOS", "CLOUD"]] = None + """Service's name.""" + + +class FreeFeatures(BaseModel): + cdn: Optional[List[FreeFeaturesCdn]] = FieldInfo(alias="CDN", default=None) + + cloud: Optional[List[FreeFeaturesCloud]] = FieldInfo(alias="CLOUD", default=None) + + ddos: Optional[List[FreeFeaturesDDOS]] = FieldInfo(alias="DDOS", default=None) + + dns: Optional[List[FreeFeaturesDNS]] = FieldInfo(alias="DNS", default=None) + + storage: Optional[List[FreeFeaturesStorage]] = FieldInfo(alias="STORAGE", default=None) + + streaming: Optional[List[FreeFeaturesStreaming]] = FieldInfo(alias="STREAMING", default=None) + + +class PaidFeaturesCdn(BaseModel): + create_date: Optional[str] = None + """Date and time when the feature was activated (ISO 8086/RFC 3339 format).""" + + feature_id: Optional[int] = None + """Feature ID.""" + + name: Optional[str] = None + """Name of the feature.""" + + paid_feature_id: Optional[int] = None + """Internal feature activation ID.""" + + service: Optional[Literal["CDN", "STORAGE", "STREAMING", "DNS", "DDOS", "CLOUD"]] = None + """Service's name.""" + + +class PaidFeaturesCloud(BaseModel): + create_date: Optional[str] = None + """Date and time when the feature was activated (ISO 8086/RFC 3339 format).""" + + feature_id: Optional[int] = None + """Feature ID.""" + + name: Optional[str] = None + """Name of the feature.""" + + paid_feature_id: Optional[int] = None + """Internal feature activation ID.""" + + service: Optional[Literal["CDN", "STORAGE", "STREAMING", "DNS", "DDOS", "CLOUD"]] = None + """Service's name.""" + + +class PaidFeaturesDDOS(BaseModel): + create_date: Optional[str] = None + """Date and time when the feature was activated (ISO 8086/RFC 3339 format).""" + + feature_id: Optional[int] = None + """Feature ID.""" + + name: Optional[str] = None + """Name of the feature.""" + + paid_feature_id: Optional[int] = None + """Internal feature activation ID.""" + + service: Optional[Literal["CDN", "STORAGE", "STREAMING", "DNS", "DDOS", "CLOUD"]] = None + """Service's name.""" + + +class PaidFeaturesDNS(BaseModel): + create_date: Optional[str] = None + """Date and time when the feature was activated (ISO 8086/RFC 3339 format).""" + + feature_id: Optional[int] = None + """Feature ID.""" + + name: Optional[str] = None + """Name of the feature.""" + + paid_feature_id: Optional[int] = None + """Internal feature activation ID.""" + + service: Optional[Literal["CDN", "STORAGE", "STREAMING", "DNS", "DDOS", "CLOUD"]] = None + """Service's name.""" + + +class PaidFeaturesStorage(BaseModel): + create_date: Optional[str] = None + """Date and time when the feature was activated (ISO 8086/RFC 3339 format).""" + + feature_id: Optional[int] = None + """Feature ID.""" + + name: Optional[str] = None + """Name of the feature.""" + + paid_feature_id: Optional[int] = None + """Internal feature activation ID.""" + + service: Optional[Literal["CDN", "STORAGE", "STREAMING", "DNS", "DDOS", "CLOUD"]] = None + """Service's name.""" + + +class PaidFeaturesStreaming(BaseModel): + create_date: Optional[str] = None + """Date and time when the feature was activated (ISO 8086/RFC 3339 format).""" + + feature_id: Optional[int] = None + """Feature ID.""" + + name: Optional[str] = None + """Name of the feature.""" + + paid_feature_id: Optional[int] = None + """Internal feature activation ID.""" + + service: Optional[Literal["CDN", "STORAGE", "STREAMING", "DNS", "DDOS", "CLOUD"]] = None + """Service's name.""" + + +class PaidFeatures(BaseModel): + cdn: Optional[List[PaidFeaturesCdn]] = FieldInfo(alias="CDN", default=None) + + cloud: Optional[List[PaidFeaturesCloud]] = FieldInfo(alias="CLOUD", default=None) + + ddos: Optional[List[PaidFeaturesDDOS]] = FieldInfo(alias="DDOS", default=None) + + dns: Optional[List[PaidFeaturesDNS]] = FieldInfo(alias="DNS", default=None) + + storage: Optional[List[PaidFeaturesStorage]] = FieldInfo(alias="STORAGE", default=None) + + streaming: Optional[List[PaidFeaturesStreaming]] = FieldInfo(alias="STREAMING", default=None) + + +class ServiceStatusesCdn(BaseModel): + enabled: Optional[bool] = None + """`true` - service is available in the Control Panel.""" + + status: Optional[Literal["new", "trial", "trialend", "active", "paused", "activating", "deleted"]] = None + """Status of the service.""" + + +class ServiceStatusesCloud(BaseModel): + enabled: Optional[bool] = None + """`true` - service is available in the Control Panel.""" + + status: Optional[Literal["new", "trial", "trialend", "active", "paused", "activating", "deleted"]] = None + """Status of the service.""" + + +class ServiceStatusesDDOS(BaseModel): + enabled: Optional[bool] = None + """`true` - service is available in the Control Panel.""" + + status: Optional[Literal["new", "trial", "trialend", "active", "paused", "activating", "deleted"]] = None + """Status of the service.""" + + +class ServiceStatusesDNS(BaseModel): + enabled: Optional[bool] = None + """`true` - service is available in the Control Panel.""" + + status: Optional[Literal["new", "trial", "trialend", "active", "paused", "activating", "deleted"]] = None + """Status of the service.""" + + +class ServiceStatusesStorage(BaseModel): + enabled: Optional[bool] = None + """`true` - service is available in the Control Panel.""" + + status: Optional[Literal["new", "trial", "trialend", "active", "paused", "activating", "deleted"]] = None + """Status of the service.""" + + +class ServiceStatusesStreaming(BaseModel): + enabled: Optional[bool] = None + """`true` - service is available in the Control Panel.""" + + status: Optional[Literal["new", "trial", "trialend", "active", "paused", "activating", "deleted"]] = None + """Status of the service.""" + + +class ServiceStatuses(BaseModel): + cdn: Optional[ServiceStatusesCdn] = FieldInfo(alias="CDN", default=None) + + cloud: Optional[ServiceStatusesCloud] = FieldInfo(alias="CLOUD", default=None) + + ddos: Optional[ServiceStatusesDDOS] = FieldInfo(alias="DDOS", default=None) + + dns: Optional[ServiceStatusesDNS] = FieldInfo(alias="DNS", default=None) + + storage: Optional[ServiceStatusesStorage] = FieldInfo(alias="STORAGE", default=None) + + streaming: Optional[ServiceStatusesStreaming] = FieldInfo(alias="STREAMING", default=None) + + +class UserGroup(BaseModel): + id: Optional[int] = None + """Group's ID: Possible values are: + + - 1 - Administrators* 2 - Users* 5 - Engineers* 3009 - Purge and Prefetch only + (API+Web)* 3022 - Purge and Prefetch only (API) + """ + + name: Optional[ + Literal[ + "Users", "Administrators", "Engineers", "Purge and Prefetch only (API)", "Purge and Prefetch only (API+Web)" + ] + ] = None + """Group's name.""" + + +class User(BaseModel): + id: Optional[int] = None + """User's ID.""" + + activated: Optional[bool] = None + """Email confirmation: + + - `true` – user confirmed the email; + - `false` – user did not confirm the email. + """ + + auth_types: Optional[List[Literal["password", "sso", "github", "google-oauth2"]]] = None + """System field. List of auth types available for the account.""" + + client: Optional[float] = None + """User's account ID.""" + + company: Optional[str] = None + """User's company.""" + + deleted: Optional[bool] = None + """Deletion flag. If `true` then user was deleted.""" + + email: Optional[str] = None + """User's email address.""" + + groups: Optional[List[UserGroup]] = None + """User's group in the current account. IAM supports 5 groups: + + - Users + - Administrators + - Engineers + - Purge and Prefetch only (API) + - Purge and Prefetch only (API+Web) + """ + + lang: Optional[Literal["de", "en", "ru", "zh", "az"]] = None + """User's language. Defines language of the control panel and email messages.""" + + name: Optional[str] = None + """User's name.""" + + phone: Optional[str] = None + """User's phone.""" + + reseller: Optional[int] = None + """Services provider ID.""" + + sso_auth: Optional[bool] = None + """SSO authentication flag. If `true` then user can login via SAML SSO.""" + + two_fa: Optional[bool] = None + """Two-step verification: + + - `true` – user enabled two-step verification; + - `false` – user disabled two-step verification. + """ + + +class AccountOverview(BaseModel): + id: Optional[int] = None + """The account ID.""" + + bill_type: Optional[str] = None + """System field. Billing type of the account.""" + + capabilities: Optional[List[Literal["CDN", "STORAGE", "STREAMING", "DNS", "DDOS", "CLOUD"]]] = None + """System field. List of services available for the account.""" + + company_name: Optional[str] = FieldInfo(alias="companyName", default=None) + """The company name.""" + + country_code: Optional[str] = None + """System field. The company country (ISO 3166-1 alpha-2 format).""" + + current_user: Optional[int] = FieldInfo(alias="currentUser", default=None) + """ID of the current user.""" + + custom_id: Optional[str] = None + """The account custom ID.""" + + deleted: Optional[bool] = None + """The field shows the status of the account: + + - `true` – the account has been deleted + - `false` – the account is not deleted + """ + + email: Optional[str] = None + """The account email.""" + + entry_base_domain: Optional[str] = FieldInfo(alias="entryBaseDomain", default=None) + """System field. Control panel domain.""" + + free_features: Optional[FreeFeatures] = FieldInfo(alias="freeFeatures", default=None) + """ + An object of arrays which contains information about free features available for + the requested account. + """ + + has_active_admin: Optional[bool] = None + """System field.""" + + is_test: Optional[bool] = None + """System field: + + - `true` — a test account; + - `false` — a production account. + """ + + name: Optional[str] = None + """Name of a user who registered the requested account.""" + + paid_features: Optional[PaidFeatures] = FieldInfo(alias="paidFeatures", default=None) + """ + An object of arrays which contains information about paid features available for + the requested account. + """ + + phone: Optional[str] = None + """Phone of a user who registered the requested account.""" + + service_statuses: Optional[ServiceStatuses] = FieldInfo(alias="serviceStatuses", default=None) + """ + An object of arrays which contains information about all services available for + the requested account. + """ + + signup_process: Optional[Literal["sign_up_full", "sign_up_simple"]] = None + """System field. Type of the account registration process.""" + + status: Optional[Literal["new", "trial", "trialend", "active", "integration", "paused", "preparation", "ready"]] = ( + None + ) + """Status of the account.""" + + users: Optional[List[User]] = None + """List of account users.""" + + website: Optional[str] = None + """The company website.""" diff --git a/src/gcore/types/iam/api_token.py b/src/gcore/types/iam/api_token.py new file mode 100644 index 00000000..c896a522 --- /dev/null +++ b/src/gcore/types/iam/api_token.py @@ -0,0 +1,78 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["APIToken", "ClientUser", "ClientUserRole"] + + +class ClientUserRole(BaseModel): + id: Optional[int] = None + """Group's ID: Possible values are: + + - 1 - Administrators* 2 - Users* 5 - Engineers* 3009 - Purge and Prefetch only + (API+Web)* 3022 - Purge and Prefetch only (API) + """ + + name: Optional[ + Literal[ + "Users", "Administrators", "Engineers", "Purge and Prefetch only (API)", "Purge and Prefetch only (API+Web)" + ] + ] = None + """Group's name.""" + + +class ClientUser(BaseModel): + client_id: Optional[int] = None + """Account's ID.""" + + deleted: Optional[bool] = None + """Deletion flag. If true, then the API token was deleted.""" + + role: Optional[ClientUserRole] = None + + user_email: Optional[str] = None + """User's email who issued the API token.""" + + user_id: Optional[int] = None + """User's ID who issued the API token.""" + + user_name: Optional[str] = None + """User's name who issued the API token.""" + + +class APIToken(BaseModel): + client_user: ClientUser + + exp_date: str + """ + Date when the API token becomes expired (ISO 8086/RFC 3339 format), UTC. If + null, then the API token will never expire. + """ + + name: str + """API token name.""" + + id: Optional[int] = None + """API token ID.""" + + created: Optional[str] = None + """Date when the API token was issued (ISO 8086/RFC 3339 format), UTC.""" + + deleted: Optional[bool] = None + """Deletion flag. If true, then the API token was deleted.""" + + description: Optional[str] = None + """API token description.""" + + expired: Optional[bool] = None + """Expiration flag. + + If true, then the API token has expired. When an API token expires it will be + automatically deleted. + """ + + last_usage: Optional[str] = None + """Date when the API token was last used (ISO 8086/RFC 3339 format), UTC.""" diff --git a/src/gcore/types/iam/api_token_create.py b/src/gcore/types/iam/api_token_create.py new file mode 100644 index 00000000..5cfc280a --- /dev/null +++ b/src/gcore/types/iam/api_token_create.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["APITokenCreate"] + + +class APITokenCreate(BaseModel): + token: Optional[str] = None + """ + API token. Copy it, because you will not be able to get it again. We do not + store tokens. All responsibility for token storage and usage is on the issuer. + """ diff --git a/src/gcore/types/iam/api_token_create_params.py b/src/gcore/types/iam/api_token_create_params.py new file mode 100644 index 00000000..dcb3f8f3 --- /dev/null +++ b/src/gcore/types/iam/api_token_create_params.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["APITokenCreateParams", "ClientUser", "ClientUserRole"] + + +class APITokenCreateParams(TypedDict, total=False): + client_user: Required[ClientUser] + """API token role.""" + + exp_date: Required[str] + """ + Date when the API token becomes expired (ISO 8086/RFC 3339 format), UTC. If + null, then the API token will never expire. + """ + + name: Required[str] + """API token name.""" + + description: str + """API token description.""" + + +class ClientUserRole(TypedDict, total=False): + id: int + """Group's ID: Possible values are: + + - 1 - Administrators* 2 - Users* 5 - Engineers* 3009 - Purge and Prefetch only + (API+Web)* 3022 - Purge and Prefetch only (API) + """ + + name: Literal[ + "Users", "Administrators", "Engineers", "Purge and Prefetch only (API)", "Purge and Prefetch only (API+Web)" + ] + """Group's name.""" + + +class ClientUser(TypedDict, total=False): + role: ClientUserRole diff --git a/src/gcore/types/iam/api_token_list.py b/src/gcore/types/iam/api_token_list.py new file mode 100644 index 00000000..7a68232a --- /dev/null +++ b/src/gcore/types/iam/api_token_list.py @@ -0,0 +1,81 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal, TypeAlias + +from ..._models import BaseModel + +__all__ = ["APITokenList", "APITokenListItem", "APITokenListItemClientUser", "APITokenListItemClientUserRole"] + + +class APITokenListItemClientUserRole(BaseModel): + id: Optional[int] = None + """Group's ID: Possible values are: + + - 1 - Administrators* 2 - Users* 5 - Engineers* 3009 - Purge and Prefetch only + (API+Web)* 3022 - Purge and Prefetch only (API) + """ + + name: Optional[ + Literal[ + "Users", "Administrators", "Engineers", "Purge and Prefetch only (API)", "Purge and Prefetch only (API+Web)" + ] + ] = None + """Group's name.""" + + +class APITokenListItemClientUser(BaseModel): + client_id: Optional[int] = None + """Account's ID.""" + + deleted: Optional[bool] = None + """Deletion flag. If true, then the API token was deleted.""" + + role: Optional[APITokenListItemClientUserRole] = None + + user_email: Optional[str] = None + """User's email who issued the API token.""" + + user_id: Optional[int] = None + """User's ID who issued the API token.""" + + user_name: Optional[str] = None + """User's name who issued the API token.""" + + +class APITokenListItem(BaseModel): + client_user: APITokenListItemClientUser + + exp_date: str + """ + Date when the API token becomes expired (ISO 8086/RFC 3339 format), UTC. If + null, then the API token will never expire. + """ + + name: str + """API token name.""" + + id: Optional[int] = None + """API token ID.""" + + created: Optional[str] = None + """Date when the API token was issued (ISO 8086/RFC 3339 format), UTC.""" + + deleted: Optional[bool] = None + """Deletion flag. If true, then the API token was deleted.""" + + description: Optional[str] = None + """API token description.""" + + expired: Optional[bool] = None + """Expiration flag. + + If true, then the API token has expired. When an API token expires it will be + automatically deleted. + """ + + last_usage: Optional[str] = None + """Date when the API token was last used (ISO 8086/RFC 3339 format), UTC.""" + + +APITokenList: TypeAlias = List[APITokenListItem] diff --git a/src/gcore/types/iam/api_token_list_params.py b/src/gcore/types/iam/api_token_list_params.py new file mode 100644 index 00000000..b185e501 --- /dev/null +++ b/src/gcore/types/iam/api_token_list_params.py @@ -0,0 +1,41 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["APITokenListParams"] + + +class APITokenListParams(TypedDict, total=False): + deleted: bool + """The state of API tokens included in the response. + Two possible values: + + - True - API token was not deleted.\\** False - API token was deleted. + + Example, _&deleted=True_ + """ + + issued_by: int + """User's ID. + + Use to get API tokens issued by a particular user. + Example, _&`issued_by`=1234_ + """ + + not_issued_by: int + """User's ID. + + Use to get API tokens issued by anyone except a particular user. + Example, _¬_issued_by=1234_ + """ + + role: str + """Group's ID. Possible values are: + + - 1 - Administrators* 2 - Users* 5 - Engineers* 3009 - Purge and Prefetch only + (API+Web)* 3022 - Purge and Prefetch only (API) + + Example, _&role=Engineers_ + """ diff --git a/src/gcore/types/iam/user.py b/src/gcore/types/iam/user.py new file mode 100644 index 00000000..15424ee4 --- /dev/null +++ b/src/gcore/types/iam/user.py @@ -0,0 +1,86 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["User", "Group"] + + +class Group(BaseModel): + id: Optional[int] = None + """Group's ID: Possible values are: + + - 1 - Administrators* 2 - Users* 5 - Engineers* 3009 - Purge and Prefetch only + (API+Web)* 3022 - Purge and Prefetch only (API) + """ + + name: Optional[ + Literal[ + "Users", "Administrators", "Engineers", "Purge and Prefetch only (API)", "Purge and Prefetch only (API+Web)" + ] + ] = None + """Group's name.""" + + +class User(BaseModel): + id: Optional[int] = None + """User's ID.""" + + activated: Optional[bool] = None + """Email confirmation: + + - `true` – user confirmed the email; + - `false` – user did not confirm the email. + """ + + auth_types: Optional[List[Literal["password", "sso", "github", "google-oauth2"]]] = None + """System field. List of auth types available for the account.""" + + client: Optional[float] = None + """User's account ID.""" + + company: Optional[str] = None + """User's company.""" + + deleted: Optional[bool] = None + """Deletion flag. If `true` then user was deleted.""" + + email: Optional[str] = None + """User's email address.""" + + groups: Optional[List[Group]] = None + """User's group in the current account. IAM supports 5 groups: + + - Users + - Administrators + - Engineers + - Purge and Prefetch only (API) + - Purge and Prefetch only (API+Web) + """ + + lang: Optional[Literal["de", "en", "ru", "zh", "az"]] = None + """User's language. Defines language of the control panel and email messages.""" + + name: Optional[str] = None + """User's name.""" + + phone: Optional[str] = None + """User's phone.""" + + reseller: Optional[int] = None + """Services provider ID.""" + + sso_auth: Optional[bool] = None + """SSO authentication flag. If `true` then user can login via SAML SSO.""" + + two_fa: Optional[bool] = None + """Two-step verification: + + - `true` – user enabled two-step verification; + - `false` – user disabled two-step verification. + """ + + user_type: Optional[Literal["common"]] = None + """User's type.""" diff --git a/src/gcore/types/iam/user_detailed.py b/src/gcore/types/iam/user_detailed.py new file mode 100644 index 00000000..b82809a2 --- /dev/null +++ b/src/gcore/types/iam/user_detailed.py @@ -0,0 +1,104 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["UserDetailed", "ClientAndRole", "Group"] + + +class ClientAndRole(BaseModel): + client_company_name: str + + client_id: int + + user_id: int + """User's ID.""" + + user_roles: List[str] + """User role in this client.""" + + +class Group(BaseModel): + id: Optional[int] = None + """Group's ID: Possible values are: + + - 1 - Administrators* 2 - Users* 5 - Engineers* 3009 - Purge and Prefetch only + (API+Web)* 3022 - Purge and Prefetch only (API) + """ + + name: Optional[ + Literal[ + "Users", "Administrators", "Engineers", "Purge and Prefetch only (API)", "Purge and Prefetch only (API+Web)" + ] + ] = None + """Group's name.""" + + +class UserDetailed(BaseModel): + id: Optional[int] = None + """User's ID.""" + + activated: Optional[bool] = None + """Email confirmation: + + - `true` – user confirmed the email; + - `false` – user did not confirm the email. + """ + + auth_types: Optional[List[Literal["password", "sso", "github", "google-oauth2"]]] = None + """System field. List of auth types available for the account.""" + + client: Optional[float] = None + """User's account ID.""" + + client_and_roles: Optional[List[ClientAndRole]] = None + """List of user's clients. User can access to one or more clients.""" + + company: Optional[str] = None + """User's company.""" + + deleted: Optional[bool] = None + """Deletion flag. If `true` then user was deleted.""" + + email: Optional[str] = None + """User's email address.""" + + groups: Optional[List[Group]] = None + """User's group in the current account. IAM supports 5 groups: + + - Users + - Administrators + - Engineers + - Purge and Prefetch only (API) + - Purge and Prefetch only (API+Web) + """ + + is_active: Optional[bool] = None + """User activity flag.""" + + lang: Optional[Literal["de", "en", "ru", "zh", "az"]] = None + """User's language. Defines language of the control panel and email messages.""" + + name: Optional[str] = None + """User's name.""" + + phone: Optional[str] = None + """User's phone.""" + + reseller: Optional[int] = None + """Services provider ID.""" + + sso_auth: Optional[bool] = None + """SSO authentication flag. If `true` then user can login via SAML SSO.""" + + two_fa: Optional[bool] = None + """Two-step verification: + + - `true` – user enabled two-step verification; + - `false` – user disabled two-step verification. + """ + + user_type: Optional[Literal["common", "reseller", "seller"]] = None + """User's type.""" diff --git a/src/gcore/types/iam/user_invite.py b/src/gcore/types/iam/user_invite.py new file mode 100644 index 00000000..86eea011 --- /dev/null +++ b/src/gcore/types/iam/user_invite.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["UserInvite"] + + +class UserInvite(BaseModel): + status: Optional[str] = None + """Status of the invitation.""" + + user_id: Optional[int] = None + """Invited user ID.""" diff --git a/src/gcore/types/iam/user_invite_params.py b/src/gcore/types/iam/user_invite_params.py new file mode 100644 index 00000000..8f17f566 --- /dev/null +++ b/src/gcore/types/iam/user_invite_params.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["UserInviteParams", "UserRole"] + + +class UserInviteParams(TypedDict, total=False): + client_id: Required[int] + """ID of account.""" + + email: Required[str] + """User email.""" + + user_role: Required[UserRole] + + lang: Literal["de", "en", "ru", "zh", "az"] + """User's language. Defines language of the control panel and email messages.""" + + name: str + """User name.""" + + +class UserRole(TypedDict, total=False): + id: int + """Group's ID: Possible values are: + + - 1 - Administrators* 2 - Users* 5 - Engineers* 3009 - Purge and Prefetch only + (API+Web)* 3022 - Purge and Prefetch only (API) + """ + + name: Literal[ + "Users", "Administrators", "Engineers", "Purge and Prefetch only (API)", "Purge and Prefetch only (API+Web)" + ] + """Group's name.""" diff --git a/src/gcore/types/iam/user_list_params.py b/src/gcore/types/iam/user_list_params.py new file mode 100644 index 00000000..497bb66d --- /dev/null +++ b/src/gcore/types/iam/user_list_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["UserListParams"] + + +class UserListParams(TypedDict, total=False): + limit: int + """The maximum number of items.""" + + offset: int + """Offset relative to the beginning of list.""" diff --git a/src/gcore/types/iam/user_update.py b/src/gcore/types/iam/user_update.py new file mode 100644 index 00000000..78b75ac0 --- /dev/null +++ b/src/gcore/types/iam/user_update.py @@ -0,0 +1,104 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["UserUpdate", "ClientAndRole", "Group"] + + +class ClientAndRole(BaseModel): + client_company_name: str + + client_id: int + + user_id: int + """User's ID.""" + + user_roles: List[str] + """User role in this client.""" + + +class Group(BaseModel): + id: Optional[int] = None + """Group's ID: Possible values are: + + - 1 - Administrators* 2 - Users* 5 - Engineers* 3009 - Purge and Prefetch only + (API+Web)* 3022 - Purge and Prefetch only (API) + """ + + name: Optional[ + Literal[ + "Users", "Administrators", "Engineers", "Purge and Prefetch only (API)", "Purge and Prefetch only (API+Web)" + ] + ] = None + """Group's name.""" + + +class UserUpdate(BaseModel): + id: Optional[int] = None + """User's ID.""" + + activated: Optional[bool] = None + """Email confirmation: + + - `true` – user confirmed the email; + - `false` – user did not confirm the email. + """ + + auth_types: Optional[List[Literal["password", "sso", "github", "google-oauth2"]]] = None + """System field. List of auth types available for the account.""" + + client: Optional[float] = None + """User's account ID.""" + + client_and_roles: Optional[List[ClientAndRole]] = None + """List of user's clients. User can access to one or more clients.""" + + company: Optional[str] = None + """User's company.""" + + deleted: Optional[bool] = None + """Deletion flag. If `true` then user was deleted.""" + + email: Optional[str] = None + """User's email address.""" + + groups: Optional[List[Group]] = None + """User's group in the current account. IAM supports 5 groups: + + - Users + - Administrators + - Engineers + - Purge and Prefetch only (API) + - Purge and Prefetch only (API+Web) + """ + + is_active: Optional[bool] = None + """User activity flag.""" + + lang: Optional[Literal["de", "en", "ru", "zh", "az"]] = None + """User's language. Defines language of the control panel and email messages.""" + + name: Optional[str] = None + """User's name.""" + + phone: Optional[str] = None + """User's phone.""" + + reseller: Optional[int] = None + """Services provider ID.""" + + sso_auth: Optional[bool] = None + """SSO authentication flag. If `true` then user can login via SAML SSO.""" + + two_fa: Optional[bool] = None + """Two-step verification: + + - `true` – user enabled two-step verification; + - `false` – user disabled two-step verification. + """ + + user_type: Optional[Literal["common", "reseller", "seller"]] = None + """User's type.""" diff --git a/src/gcore/types/iam/user_update_params.py b/src/gcore/types/iam/user_update_params.py new file mode 100644 index 00000000..6353a943 --- /dev/null +++ b/src/gcore/types/iam/user_update_params.py @@ -0,0 +1,52 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Iterable, Optional +from typing_extensions import Literal, TypedDict + +__all__ = ["UserUpdateParams", "Group"] + + +class UserUpdateParams(TypedDict, total=False): + auth_types: List[Literal["password", "sso", "github", "google-oauth2"]] + """System field. List of auth types available for the account.""" + + company: str + """User's company.""" + + email: str + """User's email address.""" + + groups: Iterable[Group] + """User's group in the current account. IAM supports 5 groups: + + - Users + - Administrators + - Engineers + - Purge and Prefetch only (API) + - Purge and Prefetch only (API+Web) + """ + + lang: Literal["de", "en", "ru", "zh", "az"] + """User's language. Defines language of the control panel and email messages.""" + + name: Optional[str] + """User's name.""" + + phone: Optional[str] + """User's phone.""" + + +class Group(TypedDict, total=False): + id: int + """Group's ID: Possible values are: + + - 1 - Administrators* 2 - Users* 5 - Engineers* 3009 - Purge and Prefetch only + (API+Web)* 3022 - Purge and Prefetch only (API) + """ + + name: Literal[ + "Users", "Administrators", "Engineers", "Purge and Prefetch only (API)", "Purge and Prefetch only (API+Web)" + ] + """Group's name.""" diff --git a/tests/api_resources/iam/__init__.py b/tests/api_resources/iam/__init__.py new file mode 100644 index 00000000..fd8019a9 --- /dev/null +++ b/tests/api_resources/iam/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/iam/test_api_tokens.py b/tests/api_resources/iam/test_api_tokens.py new file mode 100644 index 00000000..4991518d --- /dev/null +++ b/tests/api_resources/iam/test_api_tokens.py @@ -0,0 +1,356 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gcore import Gcore, AsyncGcore +from tests.utils import assert_matches_type +from gcore.types.iam import APIToken, APITokenList, APITokenCreate + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestAPITokens: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: Gcore) -> None: + api_token = client.iam.api_tokens.create( + client_id=0, + client_user={}, + exp_date="2021-01-01 12:00:00+00:00", + name="My token", + ) + assert_matches_type(APITokenCreate, api_token, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: Gcore) -> None: + api_token = client.iam.api_tokens.create( + client_id=0, + client_user={ + "role": { + "id": 1, + "name": "Administrators", + } + }, + exp_date="2021-01-01 12:00:00+00:00", + name="My token", + description="It's my token", + ) + assert_matches_type(APITokenCreate, api_token, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: Gcore) -> None: + response = client.iam.api_tokens.with_raw_response.create( + client_id=0, + client_user={}, + exp_date="2021-01-01 12:00:00+00:00", + name="My token", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + api_token = response.parse() + assert_matches_type(APITokenCreate, api_token, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: Gcore) -> None: + with client.iam.api_tokens.with_streaming_response.create( + client_id=0, + client_user={}, + exp_date="2021-01-01 12:00:00+00:00", + name="My token", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + api_token = response.parse() + assert_matches_type(APITokenCreate, api_token, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_list(self, client: Gcore) -> None: + api_token = client.iam.api_tokens.list( + client_id=0, + ) + assert_matches_type(APITokenList, api_token, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: Gcore) -> None: + api_token = client.iam.api_tokens.list( + client_id=0, + deleted=True, + issued_by=0, + not_issued_by=0, + role="role", + ) + assert_matches_type(APITokenList, api_token, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: Gcore) -> None: + response = client.iam.api_tokens.with_raw_response.list( + client_id=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + api_token = response.parse() + assert_matches_type(APITokenList, api_token, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: Gcore) -> None: + with client.iam.api_tokens.with_streaming_response.list( + client_id=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + api_token = response.parse() + assert_matches_type(APITokenList, api_token, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_delete(self, client: Gcore) -> None: + api_token = client.iam.api_tokens.delete( + token_id=0, + client_id=0, + ) + assert api_token is None + + @parametrize + def test_raw_response_delete(self, client: Gcore) -> None: + response = client.iam.api_tokens.with_raw_response.delete( + token_id=0, + client_id=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + api_token = response.parse() + assert api_token is None + + @parametrize + def test_streaming_response_delete(self, client: Gcore) -> None: + with client.iam.api_tokens.with_streaming_response.delete( + token_id=0, + client_id=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + api_token = response.parse() + assert api_token is None + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_get(self, client: Gcore) -> None: + api_token = client.iam.api_tokens.get( + token_id=0, + client_id=0, + ) + assert_matches_type(APIToken, api_token, path=["response"]) + + @parametrize + def test_raw_response_get(self, client: Gcore) -> None: + response = client.iam.api_tokens.with_raw_response.get( + token_id=0, + client_id=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + api_token = response.parse() + assert_matches_type(APIToken, api_token, path=["response"]) + + @parametrize + def test_streaming_response_get(self, client: Gcore) -> None: + with client.iam.api_tokens.with_streaming_response.get( + token_id=0, + client_id=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + api_token = response.parse() + assert_matches_type(APIToken, api_token, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncAPITokens: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncGcore) -> None: + api_token = await async_client.iam.api_tokens.create( + client_id=0, + client_user={}, + exp_date="2021-01-01 12:00:00+00:00", + name="My token", + ) + assert_matches_type(APITokenCreate, api_token, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncGcore) -> None: + api_token = await async_client.iam.api_tokens.create( + client_id=0, + client_user={ + "role": { + "id": 1, + "name": "Administrators", + } + }, + exp_date="2021-01-01 12:00:00+00:00", + name="My token", + description="It's my token", + ) + assert_matches_type(APITokenCreate, api_token, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncGcore) -> None: + response = await async_client.iam.api_tokens.with_raw_response.create( + client_id=0, + client_user={}, + exp_date="2021-01-01 12:00:00+00:00", + name="My token", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + api_token = await response.parse() + assert_matches_type(APITokenCreate, api_token, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncGcore) -> None: + async with async_client.iam.api_tokens.with_streaming_response.create( + client_id=0, + client_user={}, + exp_date="2021-01-01 12:00:00+00:00", + name="My token", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + api_token = await response.parse() + assert_matches_type(APITokenCreate, api_token, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_list(self, async_client: AsyncGcore) -> None: + api_token = await async_client.iam.api_tokens.list( + client_id=0, + ) + assert_matches_type(APITokenList, api_token, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncGcore) -> None: + api_token = await async_client.iam.api_tokens.list( + client_id=0, + deleted=True, + issued_by=0, + not_issued_by=0, + role="role", + ) + assert_matches_type(APITokenList, api_token, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncGcore) -> None: + response = await async_client.iam.api_tokens.with_raw_response.list( + client_id=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + api_token = await response.parse() + assert_matches_type(APITokenList, api_token, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGcore) -> None: + async with async_client.iam.api_tokens.with_streaming_response.list( + client_id=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + api_token = await response.parse() + assert_matches_type(APITokenList, api_token, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_delete(self, async_client: AsyncGcore) -> None: + api_token = await async_client.iam.api_tokens.delete( + token_id=0, + client_id=0, + ) + assert api_token is None + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncGcore) -> None: + response = await async_client.iam.api_tokens.with_raw_response.delete( + token_id=0, + client_id=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + api_token = await response.parse() + assert api_token is None + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncGcore) -> None: + async with async_client.iam.api_tokens.with_streaming_response.delete( + token_id=0, + client_id=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + api_token = await response.parse() + assert api_token is None + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_get(self, async_client: AsyncGcore) -> None: + api_token = await async_client.iam.api_tokens.get( + token_id=0, + client_id=0, + ) + assert_matches_type(APIToken, api_token, path=["response"]) + + @parametrize + async def test_raw_response_get(self, async_client: AsyncGcore) -> None: + response = await async_client.iam.api_tokens.with_raw_response.get( + token_id=0, + client_id=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + api_token = await response.parse() + assert_matches_type(APIToken, api_token, path=["response"]) + + @parametrize + async def test_streaming_response_get(self, async_client: AsyncGcore) -> None: + async with async_client.iam.api_tokens.with_streaming_response.get( + token_id=0, + client_id=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + api_token = await response.parse() + assert_matches_type(APIToken, api_token, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/iam/test_users.py b/tests/api_resources/iam/test_users.py new file mode 100644 index 00000000..de972b4d --- /dev/null +++ b/tests/api_resources/iam/test_users.py @@ -0,0 +1,428 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gcore import Gcore, AsyncGcore +from tests.utils import assert_matches_type +from gcore.types.iam import ( + User, + UserInvite, + UserUpdate, + UserDetailed, +) +from gcore.pagination import SyncOffsetPageIam, AsyncOffsetPageIam + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestUsers: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_update(self, client: Gcore) -> None: + user = client.iam.users.update( + user_id=0, + ) + assert_matches_type(UserUpdate, user, path=["response"]) + + @parametrize + def test_method_update_with_all_params(self, client: Gcore) -> None: + user = client.iam.users.update( + user_id=0, + auth_types=["password"], + company="company", + email="dev@stainless.com", + groups=[ + { + "id": 1, + "name": "Administrators", + } + ], + lang="de", + name="name", + phone="phone", + ) + assert_matches_type(UserUpdate, user, path=["response"]) + + @parametrize + def test_raw_response_update(self, client: Gcore) -> None: + response = client.iam.users.with_raw_response.update( + user_id=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + user = response.parse() + assert_matches_type(UserUpdate, user, path=["response"]) + + @parametrize + def test_streaming_response_update(self, client: Gcore) -> None: + with client.iam.users.with_streaming_response.update( + user_id=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + user = response.parse() + assert_matches_type(UserUpdate, user, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_list(self, client: Gcore) -> None: + user = client.iam.users.list() + assert_matches_type(SyncOffsetPageIam[User], user, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: Gcore) -> None: + user = client.iam.users.list( + limit=0, + offset=0, + ) + assert_matches_type(SyncOffsetPageIam[User], user, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: Gcore) -> None: + response = client.iam.users.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + user = response.parse() + assert_matches_type(SyncOffsetPageIam[User], user, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: Gcore) -> None: + with client.iam.users.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + user = response.parse() + assert_matches_type(SyncOffsetPageIam[User], user, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_delete(self, client: Gcore) -> None: + user = client.iam.users.delete( + user_id=0, + client_id=0, + ) + assert user is None + + @parametrize + def test_raw_response_delete(self, client: Gcore) -> None: + response = client.iam.users.with_raw_response.delete( + user_id=0, + client_id=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + user = response.parse() + assert user is None + + @parametrize + def test_streaming_response_delete(self, client: Gcore) -> None: + with client.iam.users.with_streaming_response.delete( + user_id=0, + client_id=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + user = response.parse() + assert user is None + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_get(self, client: Gcore) -> None: + user = client.iam.users.get( + 0, + ) + assert_matches_type(UserDetailed, user, path=["response"]) + + @parametrize + def test_raw_response_get(self, client: Gcore) -> None: + response = client.iam.users.with_raw_response.get( + 0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + user = response.parse() + assert_matches_type(UserDetailed, user, path=["response"]) + + @parametrize + def test_streaming_response_get(self, client: Gcore) -> None: + with client.iam.users.with_streaming_response.get( + 0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + user = response.parse() + assert_matches_type(UserDetailed, user, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_invite(self, client: Gcore) -> None: + user = client.iam.users.invite( + client_id=0, + email="dev@stainless.com", + user_role={}, + ) + assert_matches_type(UserInvite, user, path=["response"]) + + @parametrize + def test_method_invite_with_all_params(self, client: Gcore) -> None: + user = client.iam.users.invite( + client_id=0, + email="dev@stainless.com", + user_role={ + "id": 1, + "name": "Administrators", + }, + lang="de", + name="name", + ) + assert_matches_type(UserInvite, user, path=["response"]) + + @parametrize + def test_raw_response_invite(self, client: Gcore) -> None: + response = client.iam.users.with_raw_response.invite( + client_id=0, + email="dev@stainless.com", + user_role={}, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + user = response.parse() + assert_matches_type(UserInvite, user, path=["response"]) + + @parametrize + def test_streaming_response_invite(self, client: Gcore) -> None: + with client.iam.users.with_streaming_response.invite( + client_id=0, + email="dev@stainless.com", + user_role={}, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + user = response.parse() + assert_matches_type(UserInvite, user, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncUsers: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_update(self, async_client: AsyncGcore) -> None: + user = await async_client.iam.users.update( + user_id=0, + ) + assert_matches_type(UserUpdate, user, path=["response"]) + + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncGcore) -> None: + user = await async_client.iam.users.update( + user_id=0, + auth_types=["password"], + company="company", + email="dev@stainless.com", + groups=[ + { + "id": 1, + "name": "Administrators", + } + ], + lang="de", + name="name", + phone="phone", + ) + assert_matches_type(UserUpdate, user, path=["response"]) + + @parametrize + async def test_raw_response_update(self, async_client: AsyncGcore) -> None: + response = await async_client.iam.users.with_raw_response.update( + user_id=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + user = await response.parse() + assert_matches_type(UserUpdate, user, path=["response"]) + + @parametrize + async def test_streaming_response_update(self, async_client: AsyncGcore) -> None: + async with async_client.iam.users.with_streaming_response.update( + user_id=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + user = await response.parse() + assert_matches_type(UserUpdate, user, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_list(self, async_client: AsyncGcore) -> None: + user = await async_client.iam.users.list() + assert_matches_type(AsyncOffsetPageIam[User], user, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncGcore) -> None: + user = await async_client.iam.users.list( + limit=0, + offset=0, + ) + assert_matches_type(AsyncOffsetPageIam[User], user, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncGcore) -> None: + response = await async_client.iam.users.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + user = await response.parse() + assert_matches_type(AsyncOffsetPageIam[User], user, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGcore) -> None: + async with async_client.iam.users.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + user = await response.parse() + assert_matches_type(AsyncOffsetPageIam[User], user, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_delete(self, async_client: AsyncGcore) -> None: + user = await async_client.iam.users.delete( + user_id=0, + client_id=0, + ) + assert user is None + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncGcore) -> None: + response = await async_client.iam.users.with_raw_response.delete( + user_id=0, + client_id=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + user = await response.parse() + assert user is None + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncGcore) -> None: + async with async_client.iam.users.with_streaming_response.delete( + user_id=0, + client_id=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + user = await response.parse() + assert user is None + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_get(self, async_client: AsyncGcore) -> None: + user = await async_client.iam.users.get( + 0, + ) + assert_matches_type(UserDetailed, user, path=["response"]) + + @parametrize + async def test_raw_response_get(self, async_client: AsyncGcore) -> None: + response = await async_client.iam.users.with_raw_response.get( + 0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + user = await response.parse() + assert_matches_type(UserDetailed, user, path=["response"]) + + @parametrize + async def test_streaming_response_get(self, async_client: AsyncGcore) -> None: + async with async_client.iam.users.with_streaming_response.get( + 0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + user = await response.parse() + assert_matches_type(UserDetailed, user, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_invite(self, async_client: AsyncGcore) -> None: + user = await async_client.iam.users.invite( + client_id=0, + email="dev@stainless.com", + user_role={}, + ) + assert_matches_type(UserInvite, user, path=["response"]) + + @parametrize + async def test_method_invite_with_all_params(self, async_client: AsyncGcore) -> None: + user = await async_client.iam.users.invite( + client_id=0, + email="dev@stainless.com", + user_role={ + "id": 1, + "name": "Administrators", + }, + lang="de", + name="name", + ) + assert_matches_type(UserInvite, user, path=["response"]) + + @parametrize + async def test_raw_response_invite(self, async_client: AsyncGcore) -> None: + response = await async_client.iam.users.with_raw_response.invite( + client_id=0, + email="dev@stainless.com", + user_role={}, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + user = await response.parse() + assert_matches_type(UserInvite, user, path=["response"]) + + @parametrize + async def test_streaming_response_invite(self, async_client: AsyncGcore) -> None: + async with async_client.iam.users.with_streaming_response.invite( + client_id=0, + email="dev@stainless.com", + user_role={}, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + user = await response.parse() + assert_matches_type(UserInvite, user, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_iam.py b/tests/api_resources/test_iam.py new file mode 100644 index 00000000..58a9d590 --- /dev/null +++ b/tests/api_resources/test_iam.py @@ -0,0 +1,74 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gcore import Gcore, AsyncGcore +from tests.utils import assert_matches_type +from gcore.types.iam import AccountOverview + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestIam: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_get_account_overview(self, client: Gcore) -> None: + iam = client.iam.get_account_overview() + assert_matches_type(AccountOverview, iam, path=["response"]) + + @parametrize + def test_raw_response_get_account_overview(self, client: Gcore) -> None: + response = client.iam.with_raw_response.get_account_overview() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + iam = response.parse() + assert_matches_type(AccountOverview, iam, path=["response"]) + + @parametrize + def test_streaming_response_get_account_overview(self, client: Gcore) -> None: + with client.iam.with_streaming_response.get_account_overview() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + iam = response.parse() + assert_matches_type(AccountOverview, iam, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncIam: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_get_account_overview(self, async_client: AsyncGcore) -> None: + iam = await async_client.iam.get_account_overview() + assert_matches_type(AccountOverview, iam, path=["response"]) + + @parametrize + async def test_raw_response_get_account_overview(self, async_client: AsyncGcore) -> None: + response = await async_client.iam.with_raw_response.get_account_overview() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + iam = await response.parse() + assert_matches_type(AccountOverview, iam, path=["response"]) + + @parametrize + async def test_streaming_response_get_account_overview(self, async_client: AsyncGcore) -> None: + async with async_client.iam.with_streaming_response.get_account_overview() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + iam = await response.parse() + assert_matches_type(AccountOverview, iam, path=["response"]) + + assert cast(Any, response.is_closed) is True From daf66fc3fc664d4ca689246eb2b0f1708746bb3e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 4 Jul 2025 09:23:51 +0000 Subject: [PATCH 50/50] release: 0.4.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 70 +++++++++++++++++++++++++++++++++++ pyproject.toml | 2 +- src/gcore/_version.py | 2 +- 4 files changed, 73 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 6b7b74c5..da59f99e 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.3.0" + ".": "0.4.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index fdcbc808..6a7fe6a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,75 @@ # Changelog +## 0.4.0 (2025-07-04) + +Full Changelog: [v0.3.0...v0.4.0](https://github.com/G-Core/gcore-python/compare/v0.3.0...v0.4.0) + +### ⚠ BREAKING CHANGES + +* **cloud:** remove list suitable from bm flavors +* remove list suitable and list for resize from instance flavors + +### Features + +* **api:** aggregated API specs update ([7395880](https://github.com/G-Core/gcore-python/commit/7395880c1291632db977714b43de1ab7061c23ed)) +* **api:** aggregated API specs update ([dd87a63](https://github.com/G-Core/gcore-python/commit/dd87a630497b9dd478330bb190920da41fc6b6da)) +* **api:** aggregated API specs update ([d4b4f22](https://github.com/G-Core/gcore-python/commit/d4b4f221489c1047b8e92752a1da014f1af59af9)) +* **api:** aggregated API specs update ([a942886](https://github.com/G-Core/gcore-python/commit/a9428867b76afd4e3e0adb5892c007c3b8922fca)) +* **api:** aggregated API specs update ([8b5d094](https://github.com/G-Core/gcore-python/commit/8b5d094f732e0c5f236f8bfc32fe069a6fd412ed)) +* **api:** aggregated API specs update ([c86820e](https://github.com/G-Core/gcore-python/commit/c86820e1ca68185902d8e5e3cb911fca6d4dc10b)) +* **api:** aggregated API specs update ([26b81bd](https://github.com/G-Core/gcore-python/commit/26b81bdeb1d84c2bc72a34e046db8fb1b1e5fcd1)) +* **api:** update via SDK Studio ([96a27dd](https://github.com/G-Core/gcore-python/commit/96a27dd845f5fe9128b111171feb6c065f0c2916)) +* **api:** update via SDK Studio ([1da4aa3](https://github.com/G-Core/gcore-python/commit/1da4aa370b1e633b002e1be99b7c5f3cf785ebc7)) +* **client:** add support for aiohttp ([a983aee](https://github.com/G-Core/gcore-python/commit/a983aee8dd4ddf7d111f4798c89cfd3f341568d4)) +* **cloud:** add floating IPs examples ([9010134](https://github.com/G-Core/gcore-python/commit/90101344d7fdf1ba0c8f55bc290ffe7839cb6af0)) +* **cloud:** add instances examples ([a38f100](https://github.com/G-Core/gcore-python/commit/a38f10024534ab8c65ff72e96f49100bfaeb17a1)) +* **cloud:** add load balancers examples ([#50](https://github.com/G-Core/gcore-python/issues/50)) ([c73f5d1](https://github.com/G-Core/gcore-python/commit/c73f5d1b84dbd2c96c9383100a93c89c8cf5e498)) +* **cloud:** add networks examples ([5f32d6f](https://github.com/G-Core/gcore-python/commit/5f32d6f75aec5a7ff33729c65984f8b171910f8b)) +* **cloud:** add reserved fixed ips examples ([a42b974](https://github.com/G-Core/gcore-python/commit/a42b974dde0d61edd9efa8f5929c0cbd967eebf8)) +* **cloud:** add routers examples ([aba1f63](https://github.com/G-Core/gcore-python/commit/aba1f6343d40a6c6181929923b80a31d0bae332c)) +* **cloud:** add security groups examples ([5c4f2a5](https://github.com/G-Core/gcore-python/commit/5c4f2a57603a5940c13220770e363c1e597ec48d)) +* **cloud:** add volumes examples ([57ddcba](https://github.com/G-Core/gcore-python/commit/57ddcba83e1664b765c1c16e9ee314f6af74c38b)) +* **iam:** add IAM ([1507ac3](https://github.com/G-Core/gcore-python/commit/1507ac37f5d588a999880cf5f51c94293f65b039)) +* **images:** add instance images examples ([ecc8d91](https://github.com/G-Core/gcore-python/commit/ecc8d91c94df8c09ab4aeb5f89c966f03924caed)) + + +### Bug Fixes + +* **ci:** correct conditional ([b8d8b92](https://github.com/G-Core/gcore-python/commit/b8d8b9275a3c0d0864cfbd0d396a68cfd07c0b7b)) +* **ci:** release-doctor — report correct token name ([0bf3b18](https://github.com/G-Core/gcore-python/commit/0bf3b1850e85aa8d9b635dcee9aeaf416f35df6c)) +* **cloud:** linting on load balancer examples ([3534bea](https://github.com/G-Core/gcore-python/commit/3534beaed71a9fb5fe61df6a8b9aa76b9c3913c8)) +* **cloud:** update tags type for gpu baremetal clusters and images, instances, load balancers ([6634a74](https://github.com/G-Core/gcore-python/commit/6634a74d3106f25476bd6bce98d12249f07dd8a6)) +* **tests:** fix: tests which call HTTP endpoints directly with the example parameters ([3d88ae5](https://github.com/G-Core/gcore-python/commit/3d88ae5a7df41b7e9e88d0520a9586c1240da54c)) +* **waap:** remove duplicate method for acct overview ([85766ca](https://github.com/G-Core/gcore-python/commit/85766ca7a1a3848cf6e252a55d7cc7ef384a4f94)) + + +### Chores + +* **ci:** change upload type ([960a44b](https://github.com/G-Core/gcore-python/commit/960a44ba2f255ad2bf8f571de381b06e5f1fffbd)) +* **ci:** only run for pushes and fork pull requests ([7df32ec](https://github.com/G-Core/gcore-python/commit/7df32eca9f58a777487a2d00e2bfd41ccdd6a518)) +* **cloud:** reorder ([0441c52](https://github.com/G-Core/gcore-python/commit/0441c52c6407e2ae427b70fdd22881bde8ea8191)) +* **cloud:** reorder example functions ([7c3a568](https://github.com/G-Core/gcore-python/commit/7c3a5683c8482ca7398e6863c77fdae350b3e711)) +* **cloud:** skip load balancer test statuses ([8dd7ccb](https://github.com/G-Core/gcore-python/commit/8dd7ccb677a41455d0c3ee6407e21271485669aa)) +* **cloud:** streamline envs in examples ([e26746f](https://github.com/G-Core/gcore-python/commit/e26746fd50a2fee2157c88244b737352f07dd55c)) +* **cloud:** unify examples format ([32446c4](https://github.com/G-Core/gcore-python/commit/32446c4e9bb385f68b0a24637f731ff93ee0eb5a)) +* format ([fbe3508](https://github.com/G-Core/gcore-python/commit/fbe3508482950455f4ccd02b6941bb0273b2fdf6)) +* **internal:** updates ([2377589](https://github.com/G-Core/gcore-python/commit/237758929ed119a1780c5a0cf9d60c72f6c70b8a)) +* **internal:** updates ([054c374](https://github.com/G-Core/gcore-python/commit/054c374d0b02ec85d6702ecfded1362e827cf655)) +* **readme:** update badges ([6ba343d](https://github.com/G-Core/gcore-python/commit/6ba343d802d353c4df42e4e7f6cc4f693fe9734a)) +* **tests:** skip some failing tests on the latest python versions ([4b45142](https://github.com/G-Core/gcore-python/commit/4b45142d7fe138a5f0a030bdd27f9de26df533b9)) +* **tests:** skip some failing tests on the latest python versions ([272ce51](https://github.com/G-Core/gcore-python/commit/272ce51ed26d568e181dee26205448d35184e015)) + + +### Documentation + +* **client:** fix httpx.Timeout documentation reference ([1f4c28f](https://github.com/G-Core/gcore-python/commit/1f4c28f252df42a703a60ed2901391167296ecc7)) + + +### Refactors + +* **cloud:** remove list suitable from bm flavors ([2626938](https://github.com/G-Core/gcore-python/commit/262693876592601fa5a07dd088033037c7eae9b6)) +* remove list suitable and list for resize from instance flavors ([24b00fe](https://github.com/G-Core/gcore-python/commit/24b00fec390f141457c98334b302dab5a8b1d480)) + ## 0.3.0 (2025-06-17) Full Changelog: [v0.2.0...v0.3.0](https://github.com/G-Core/gcore-python/compare/v0.2.0...v0.3.0) diff --git a/pyproject.toml b/pyproject.toml index 930acded..0bd483e5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "gcore" -version = "0.3.0" +version = "0.4.0" description = "The official Python library for the gcore API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/gcore/_version.py b/src/gcore/_version.py index be1b14df..bab6ec7d 100644 --- a/src/gcore/_version.py +++ b/src/gcore/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "gcore" -__version__ = "0.3.0" # x-release-please-version +__version__ = "0.4.0" # x-release-please-version