diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f6054ec3..1cd880c7 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 @@ -34,10 +35,10 @@ jobs: - name: Run lints run: ./scripts/lint - upload: - if: github.repository == 'stainless-sdks/gcore-python' + 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 @@ -45,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 @@ -62,6 +77,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 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/.stats.yml b/.stats.yml index b42d4f2b..f6858de3 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 -config_hash: 62229a7a78ca1c79cb7cf61752a8df7e +configured_endpoints: 309 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-a3eb777ed002489583f1ec680d7ac5638c4bf54a843d381a1c1ad5d1c9f5e967.yml +openapi_spec_hash: 3e4993495b21ba44b2969cc41cdb1221 +config_hash: 708e4dbf0025978d06dedb02b94647a3 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/README.md b/README.md index 9a23b61d..b571c246 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, @@ -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: @@ -252,7 +286,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 diff --git a/api.md b/api.md index 82d3b4fb..040eb35c 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 @@ -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 @@ -667,17 +666,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 @@ -816,8 +820,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 @@ -928,18 +930,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: @@ -1166,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/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[@]} diff --git a/examples/cloud/file_shares.py b/examples/cloud/file_shares.py index 964c1da9..908af045 100644 --- a/examples/cloud/file_shares.py +++ b/examples/cloud/file_shares.py @@ -4,6 +4,39 @@ from gcore.types.cloud.file_share_create_params import CreateStandardFileShareSerializerNetwork +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, + # 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) + 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 +132,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..04fd38c3 100644 --- a/examples/cloud/file_shares_async.py +++ b/examples/cloud/file_shares_async.py @@ -5,6 +5,39 @@ from gcore.types.cloud.file_share_create_params import CreateStandardFileShareSerializerNetwork +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, + # 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) + 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 +135,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/floating_ips.py b/examples/cloud/floating_ips.py new file mode 100644 index 00000000..2dac2967 --- /dev/null +++ b/examples/cloud/floating_ips.py @@ -0,0 +1,92 @@ +from __future__ import annotations + +import os + +from gcore import Gcore + + +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( + # 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) + 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..0c08c747 --- /dev/null +++ b/examples/cloud/floating_ips_async.py @@ -0,0 +1,92 @@ +from __future__ import annotations + +import os +import asyncio + +from gcore import AsyncGcore + + +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( + # 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) + 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()) diff --git a/examples/cloud/instances.py b/examples/cloud/instances.py new file mode 100644 index 00000000..065e722e --- /dev/null +++ b/examples/cloud/instances.py @@ -0,0 +1,350 @@ +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 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, + # 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) + + 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) + 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) + + # 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: + 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) + + +def create_instance(*, client: Gcore, image_id: str) -> str: + print("\n=== CREATE INSTANCE ===") + + instance = client.cloud.instances.create_and_poll( + 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, + boot_index=0, + ), + ], + password="Gcore123!", + tags={"name": "gcore-go-example"}, + ) + 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 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: + 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") + + +if __name__ == "__main__": + main() diff --git a/examples/cloud/instances_async.py b/examples/cloud/instances_async.py new file mode 100644 index 00000000..20b1ab25 --- /dev/null +++ b/examples/cloud/instances_async.py @@ -0,0 +1,357 @@ +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 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, + # 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) + + 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) + 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) + + # 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: + 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, image_id: str) -> str: + print("\n=== CREATE INSTANCE ===") + + instance = await client.cloud.instances.create_and_poll( + 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, + boot_index=0, + ), + ], + password="Gcore123!", + tags={"name": "gcore-go-example"}, + ) + 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 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: + 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") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/cloud/ip_ranges.py b/examples/cloud/ip_ranges.py index 1604d51e..8130711b 100644 --- a/examples/cloud/ip_ranges.py +++ b/examples/cloud/ip_ranges.py @@ -1,15 +1,22 @@ -import os - from gcore import Gcore from gcore.types.cloud import IPRanges -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 main() -> None: + # TODO set API key before running + # api_key = os.environ["GCORE_API_KEY"] + + 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(*, 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("===========================") @@ -17,4 +24,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..aab3d5f2 100644 --- a/examples/cloud/ip_ranges_async.py +++ b/examples/cloud/ip_ranges_async.py @@ -1,27 +1,29 @@ -import os import asyncio from gcore import AsyncGcore from gcore.types.cloud import IPRanges -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 main() -> None: + # TODO set API key before running + # api_key = os.environ["GCORE_API_KEY"] + + 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(*, 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 -async def main() -> None: - await list_all_ip_ranges() - - if __name__ == "__main__": asyncio.run(main()) diff --git a/examples/cloud/load_balancers.py b/examples/cloud/load_balancers.py new file mode 100644 index 00000000..dee3ece5 --- /dev/null +++ b/examples/cloud/load_balancers.py @@ -0,0 +1,123 @@ +from gcore import Gcore + + +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, + # 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) + 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}") + 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 if lb.flavor else "Unknown" + 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..dea488d5 --- /dev/null +++ b/examples/cloud/load_balancers_async.py @@ -0,0 +1,129 @@ +from __future__ import annotations + +import asyncio + +from gcore import AsyncGcore + + +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, + # 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) + 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 if lb.flavor else "Unknown" + 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()) diff --git a/examples/cloud/networks.py b/examples/cloud/networks.py new file mode 100644 index 00000000..36dde8c5 --- /dev/null +++ b/examples/cloud/networks.py @@ -0,0 +1,194 @@ +from gcore import Gcore + + +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( + # 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) + 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) + + # 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) + + +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 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) + 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) + 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..669a55ef --- /dev/null +++ b/examples/cloud/networks_async.py @@ -0,0 +1,202 @@ +import asyncio + +from gcore import AsyncGcore + + +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( + # 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) + 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) + + # 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) + + +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 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) + 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) + 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()) diff --git a/examples/cloud/projects.py b/examples/cloud/projects.py index ff30a6ca..0fe79699 100644 --- a/examples/cloud/projects.py +++ b/examples/cloud/projects.py @@ -1,71 +1,63 @@ -import os - from gcore import Gcore from gcore.pagination import SyncOffsetPage from gcore.types.cloud import Project -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) +def main() -> None: + # TODO set API key before running + # api_key = os.environ["GCORE_API_KEY"] - print("\n=== GET PROJECT BY ID ===") - print(f"Project ID: {project.id}, Name: {project.name}, Created: {project.created_at}") - print("==========================") - return project + gcore = Gcore( + # No need to explicitly pass to Gcore constructor if using environment variables + # api_key=api_key, + ) + 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) + + +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("=======================") 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..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 @@ -6,73 +5,63 @@ from gcore.types.cloud import Project -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) +async def main() -> None: + # TODO set API key before running + # api_key = os.environ["GCORE_API_KEY"] - print("\n=== GET PROJECT BY ID ===") - print(f"Project ID: {project.id}, Name: {project.name}, Created: {project.created_at}") - print("==========================") - return project + 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 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 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(*, 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("=======================") -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..ea058b83 100644 --- a/examples/cloud/quotas.py +++ b/examples/cloud/quotas.py @@ -7,6 +7,28 @@ from gcore.types.cloud.quota_get_by_region_response import QuotaGetByRegionResponse +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( + # 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) + 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 +96,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..5be4ee9e 100644 --- a/examples/cloud/quotas_async.py +++ b/examples/cloud/quotas_async.py @@ -8,6 +8,28 @@ from gcore.types.cloud.quota_get_by_region_response import QuotaGetByRegionResponse +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( + # 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) + 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 +96,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..46ae32ad 100644 --- a/examples/cloud/regions.py +++ b/examples/cloud/regions.py @@ -5,41 +5,47 @@ from gcore.types.cloud import Region -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 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)) + 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(*, 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 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..0703ba49 100644 --- a/examples/cloud/regions_async.py +++ b/examples/cloud/regions_async.py @@ -6,49 +6,51 @@ from gcore.types.cloud import Region -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 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)) + + 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(*, 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 -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 new file mode 100644 index 00000000..1233b661 --- /dev/null +++ b/examples/cloud/reserved_fixed_ips.py @@ -0,0 +1,103 @@ +from gcore import Gcore +from gcore.pagination import SyncOffsetPage +from gcore.types.cloud import ReservedFixedIP + + +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( + # 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, + ) + + 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( + 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__": + main() diff --git a/examples/cloud/reserved_fixed_ips_async.py b/examples/cloud/reserved_fixed_ips_async.py new file mode 100644 index 00000000..3542a229 --- /dev/null +++ b/examples/cloud/reserved_fixed_ips_async.py @@ -0,0 +1,107 @@ +import asyncio + +from gcore import AsyncGcore +from gcore.pagination import AsyncOffsetPage +from gcore.types.cloud import ReservedFixedIP + + +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( + # 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, + ) + + 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( + 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("========================") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/cloud/secrets.py b/examples/cloud/secrets.py index bd67260f..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 @@ -6,62 +5,64 @@ from gcore.types.cloud.secret_upload_tls_certificate_params import Payload -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 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"] + 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_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__": - 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..fe7f16ca 100644 --- a/examples/cloud/secrets_async.py +++ b/examples/cloud/secrets_async.py @@ -1,83 +1,67 @@ -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 -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 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"] + + 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_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("=====================") - - -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) + print("========================") if __name__ == "__main__": diff --git a/examples/cloud/security_groups.py b/examples/cloud/security_groups.py new file mode 100644 index 00000000..2620e283 --- /dev/null +++ b/examples/cloud/security_groups.py @@ -0,0 +1,118 @@ +from gcore import Gcore +from gcore.types.cloud.security_group_create_params import SecurityGroup + + +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( + # 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) + 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..dacb1cfc --- /dev/null +++ b/examples/cloud/security_groups_async.py @@ -0,0 +1,122 @@ +import asyncio + +from gcore import AsyncGcore +from gcore.types.cloud.security_group_create_params import SecurityGroup + + +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( + # 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) + 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("========================") + + +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()) diff --git a/examples/cloud/ssh_keys.py b/examples/cloud/ssh_keys.py index 72c90667..231111d0 100644 --- a/examples/cloud/ssh_keys.py +++ b/examples/cloud/ssh_keys.py @@ -1,67 +1,69 @@ -import os - from gcore import Gcore from gcore.pagination import SyncOffsetPage from gcore.types.cloud import SSHKey, SSHKeyCreated -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("==========================") +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( + # 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__": - # 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..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 @@ -6,69 +5,68 @@ from gcore.types.cloud import SSHKey, SSHKeyCreated -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("==========================") +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( + # 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 - - -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("========================") + return ssh_keys - 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("=======================") - - -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) + 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 667a11bc..e47b047e 100644 --- a/examples/cloud/task.py +++ b/examples/cloud/task.py @@ -1,86 +1,62 @@ 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 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") +def main() -> None: + # TODO set API key before running + # api_key = os.environ["GCORE_API_KEY"] + + # TODO set cloud network ID before running + cloud_task_id = os.environ["GCORE_CLOUD_TASK_ID"] + 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 + 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("========================") - 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 +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__": - get_task_by_id() - list_tasks() + main() diff --git a/examples/cloud/task_async.py b/examples/cloud/task_async.py index ec0e8fd8..eebb0293 100644 --- a/examples/cloud/task_async.py +++ b/examples/cloud/task_async.py @@ -1,90 +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 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") +async def main() -> None: + # TODO set API key before running + # api_key = os.environ["GCORE_API_KEY"] + + # TODO set cloud network ID before running + cloud_task_id = os.environ["GCORE_CLOUD_TASK_ID"] + 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 + 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) - 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 +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 main() -> None: - await get_task_by_id() - await list_tasks() +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__": diff --git a/examples/cloud/volumes.py b/examples/cloud/volumes.py new file mode 100644 index 00000000..b23f0523 --- /dev/null +++ b/examples/cloud/volumes.py @@ -0,0 +1,120 @@ +import os + +from gcore import Gcore + + +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( + # 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) + 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..522e0ff9 --- /dev/null +++ b/examples/cloud/volumes_async.py @@ -0,0 +1,122 @@ +import os +import asyncio + +from gcore import AsyncGcore + + +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( + # 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) + 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( + 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("========================") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/pyproject.toml b/pyproject.toml index 887aaeee..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" @@ -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/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 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/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/_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 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/cloud/baremetal/flavors.py b/src/gcore/resources/cloud/baremetal/flavors.py index 83f7e0ea..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"] @@ -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 @@ -113,64 +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 suitalbe flavors for bare metal server creation - - 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 @@ -210,11 +153,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 @@ -264,64 +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 suitalbe flavors for bare metal server creation - - 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: @@ -330,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: @@ -342,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: @@ -354,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: @@ -366,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/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 a38c9e1d..eeee848b 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. @@ -73,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) @@ -217,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 @@ -263,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 @@ -346,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 @@ -399,7 +400,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, @@ -483,7 +484,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, @@ -501,7 +502,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: @@ -537,7 +538,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. @@ -547,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) @@ -691,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 @@ -737,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 @@ -820,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 @@ -873,7 +876,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, @@ -907,8 +910,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, @@ -957,7 +960,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, @@ -973,7 +976,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/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/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..3bbb8930 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, @@ -141,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 @@ -322,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 @@ -375,7 +372,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, @@ -462,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 @@ -643,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 d28d0379..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 @@ -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. @@ -123,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 @@ -440,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 @@ -496,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 @@ -515,7 +520,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( @@ -539,7 +544,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. @@ -735,7 +740,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. @@ -745,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 @@ -1062,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 @@ -1118,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 @@ -1137,7 +1148,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( @@ -1161,7 +1172,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 0b4ac0e9..4d1aa992 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"] @@ -101,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 @@ -182,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 @@ -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, @@ -317,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. @@ -444,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 @@ -525,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 @@ -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, @@ -660,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/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/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/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..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"] @@ -59,11 +57,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 @@ -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 suitable flavors for instance creation - - 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 @@ -241,11 +141,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 @@ -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 suitable flavors for instance creation - - 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/resources/cloud/instances/images.py b/src/gcore/resources/cloud/instances/images.py index 3cbb21e5..91f82de4 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 @@ -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 @@ -284,7 +285,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, @@ -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 @@ -369,7 +372,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. @@ -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 @@ -477,7 +480,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, @@ -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 @@ -571,7 +576,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. @@ -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 @@ -871,7 +877,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, @@ -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 @@ -956,7 +964,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. @@ -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 @@ -1064,7 +1072,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, @@ -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 @@ -1158,7 +1168,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 f4155a0e..e505ab11 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. @@ -135,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) @@ -270,7 +270,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, @@ -429,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 @@ -485,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 @@ -787,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. @@ -1008,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: @@ -1107,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 @@ -1367,7 +1374,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. @@ -1377,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) @@ -1512,7 +1520,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, @@ -1671,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 @@ -1727,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 @@ -2029,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. @@ -2250,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: @@ -2349,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/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( 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..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 @@ -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], @@ -694,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 @@ -875,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 @@ -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 8fdcb349..b54df5d7 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,26 @@ 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. 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 extra_query: Add additional query parameters to the request @@ -280,6 +303,7 @@ def update( "logging": logging, "name": name, "preferred_connectivity": preferred_connectivity, + "tags": tags, }, load_balancer_update_params.LoadBalancerUpdateParams, ), @@ -334,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 @@ -433,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. @@ -531,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. @@ -571,7 +593,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, @@ -611,7 +633,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], @@ -798,7 +824,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 +923,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 +932,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 +945,26 @@ 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. 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 extra_query: Add additional query parameters to the request @@ -937,6 +986,7 @@ async def update( "logging": logging, "name": name, "preferred_connectivity": preferred_connectivity, + "tags": tags, }, load_balancer_update_params.LoadBalancerUpdateParams, ), @@ -991,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 @@ -1090,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. @@ -1190,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. @@ -1230,7 +1278,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, @@ -1270,7 +1318,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], 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/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/resources/cloud/networks/networks.py b/src/gcore/resources/cloud/networks/networks.py index d980ebfa..13f2084d 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,26 @@ 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. 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 extra_query: Add additional query parameters to the request @@ -178,7 +202,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 +221,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 +243,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 @@ -220,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 @@ -247,6 +278,7 @@ def list( query=maybe_transform( { "limit": limit, + "name": name, "offset": offset, "order_by": order_by, "tag_key": tag_key, @@ -384,7 +416,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 +480,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 +489,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 +504,26 @@ 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. 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 extra_query: Add additional query parameters to the request @@ -484,7 +540,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 +559,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 +581,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 @@ -526,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 @@ -553,6 +616,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/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 b65a97e0..1f61c9b2 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,26 @@ 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. 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 extra_query: Add additional query parameters to the request @@ -212,6 +233,7 @@ def update( "gateway_ip": gateway_ip, "host_routes": host_routes, "name": name, + "tags": tags, }, subnet_update_params.SubnetUpdateParams, ), @@ -274,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 @@ -440,7 +460,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 +555,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 +564,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 +586,26 @@ 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. 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 extra_query: Add additional query parameters to the request @@ -588,6 +629,7 @@ async def update( "gateway_ip": gateway_ip, "host_routes": host_routes, "name": name, + "tags": tags, }, subnet_update_params.SubnetUpdateParams, ), @@ -650,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 51a470d1..034bbe2b 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"] @@ -76,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 @@ -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,33 @@ def update( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SecurityGroup: """ - Change security group + Update the configuration of an existing 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. 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 extra_query: Add additional query parameters to the request @@ -153,6 +175,7 @@ def update( { "changed_rules": changed_rules, "name": name, + "tags": tags, }, security_group_update_params.SecurityGroupUpdateParams, ), @@ -179,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 @@ -239,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 @@ -278,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. @@ -299,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( @@ -323,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 @@ -362,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 @@ -427,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 @@ -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,33 @@ async def update( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SecurityGroup: """ - Change security group + Update the configuration of an existing 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. 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 extra_query: Add additional query parameters to the request @@ -504,6 +545,7 @@ async def update( { "changed_rules": changed_rules, "name": name, + "tags": tags, }, security_group_update_params.SecurityGroupUpdateParams, ), @@ -530,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 @@ -590,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 @@ -629,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. @@ -650,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( @@ -674,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 @@ -713,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..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 @@ -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 @@ -311,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, @@ -320,7 +330,7 @@ def update( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Volume: """ - Rename volume + Rename a volume or update tags Args: project_id: Project ID @@ -329,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 @@ -347,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 ), @@ -376,8 +412,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 +443,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 +498,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 +612,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 +714,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 +759,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 +809,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 +888,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 +957,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 +1026,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 @@ -1075,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, @@ -1084,7 +1139,7 @@ async def update( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Volume: """ - Rename volume + Rename a volume or update tags Args: project_id: Project ID @@ -1093,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 @@ -1111,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 ), @@ -1140,8 +1221,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 +1252,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 +1307,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 +1421,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 +1525,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 +1570,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 +1620,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/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/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/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/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_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/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/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/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/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/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/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/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/__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/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_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/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..5ce1b76a 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 @@ -175,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_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 9b7653fe..f4b01786 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,29 @@ 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. 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. + """ + class Logging(TypedDict, total=False): destination_region_id: Optional[int] 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/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..fd80aecd 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. @@ -34,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 8319b067..8aa509d3 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,28 @@ 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. 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/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_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_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 7fb218da..966b3ccf 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,29 @@ 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. 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. + """ + class HostRoute(TypedDict, total=False): destination: Required[str] 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 d2118275..a63fad11 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,29 @@ 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. 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. + """ + 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/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/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/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/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/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/cloud/baremetal/test_flavors.py b/tests/api_resources/cloud/baremetal/test_flavors.py index 322c22e2..18da226a 100644 --- a/tests/api_resources/cloud/baremetal/test_flavors.py +++ b/tests/api_resources/cloud/baremetal/test_flavors.py @@ -65,54 +65,11 @@ 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("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: @@ -161,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 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 347210d3..db10a5ea 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", ) @@ -221,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: @@ -250,7 +251,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 +260,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/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 30e17761..e9f6c63f 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"]) @@ -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: @@ -355,7 +357,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/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..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", @@ -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: @@ -833,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/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..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,124 +63,11 @@ 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("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: @@ -229,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 diff --git a/tests/api_resources/cloud/instances/test_images.py b/tests/api_resources/cloud/instances/test_images.py index d796aec9..c30fde90 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"]) @@ -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: @@ -529,7 +531,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 +647,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/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..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, @@ -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: @@ -169,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_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..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, @@ -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: @@ -387,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, @@ -397,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, @@ -482,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/load_balancers/test_statuses.py b/tests/api_resources/cloud/load_balancers/test_statuses.py index 413375f0..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,8 +98,11 @@ 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], 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 88244d9c..0680b1f7 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"]) @@ -292,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: @@ -325,7 +328,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 +389,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/quotas/test_requests.py b/tests/api_resources/cloud/quotas/test_requests.py index ad9a144f..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" @@ -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: @@ -383,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 @@ -401,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" @@ -421,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 @@ -439,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" 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 30ea6d26..1167b206 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,13 +301,15 @@ 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 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: @@ -317,7 +320,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 +333,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 +348,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 +363,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 +562,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 +576,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 +590,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/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 4c547362..fca472e6 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"]) @@ -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: @@ -421,7 +423,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 +483,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..a0772131 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"]) @@ -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: @@ -331,7 +333,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..7c495e40 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", ) @@ -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: @@ -506,7 +508,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_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 c517c82b..9dfe0883 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", ) @@ -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: @@ -922,7 +924,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 +936,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_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 a219b32d..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, @@ -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"]) @@ -482,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: @@ -533,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, @@ -543,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, @@ -585,7 +588,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 +645,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..dbd1f4d0 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"], @@ -261,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: @@ -279,7 +289,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 +324,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 +348,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 +361,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 +377,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 +393,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_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 20630068..24ed373f 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"]) @@ -253,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: @@ -267,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: @@ -281,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 @@ -389,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: @@ -484,6 +487,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"]) @@ -624,7 +628,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: @@ -638,7 +642,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: @@ -652,7 +656,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 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..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 @@ -666,7 +673,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: @@ -856,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"]) @@ -866,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 @@ -880,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" @@ -897,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 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/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 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..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, }, } @@ -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: @@ -529,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": { @@ -537,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": { @@ -564,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": { @@ -598,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, }, } @@ -678,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": { @@ -686,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": { @@ -713,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": { @@ -747,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, }, } 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_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 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 diff --git a/tests/test_client.py b/tests/test_client.py index c6b0df66..53499b05 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 @@ -194,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") @@ -735,32 +733,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]) @@ -1027,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") @@ -1582,32 +1570,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])