From f1f6809a2d87ea5f3c9ed4c0a86bf2411e6c5837 Mon Sep 17 00:00:00 2001 From: Laure-di Date: Wed, 8 Oct 2025 09:51:31 +0200 Subject: [PATCH 1/4] doc(instance): guide how to create an instance --- scaleway/scaleway/instance/v1/custom_api.py | 84 +++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/scaleway/scaleway/instance/v1/custom_api.py b/scaleway/scaleway/instance/v1/custom_api.py index ede8a8242..0845ae566 100644 --- a/scaleway/scaleway/instance/v1/custom_api.py +++ b/scaleway/scaleway/instance/v1/custom_api.py @@ -146,3 +146,87 @@ def wait_instance_server(self, server_id: str, zone: ScwZone) -> GetServerRespon f"Server {server_id} in zone {zone} did not reach a stable state " f"after {max_retry} retries." ) + + def create_instance_server( + self, + *, + zone: Optional[ScwZone] = None, + commercial_type: str, + name: Optional[str] = None, + dynamic_ip_required: Optional[bool] = None, + routed_ip_enabled: Optional[bool] = None, + image: Optional[str] = None, + volumes: Optional[dict[str, VolumeServerTemplate]] = None, + enable_ipv6: Optional[bool] = None, + protected: bool = False, + public_ip: Optional[str] = None, + public_ips: Optional[list[str]] = None, + boot_type: Optional[BootType] = None, + organization: Optional[str] = None, + project: Optional[str] = None, + tags: Optional[list[str]] = None, + security_group: Optional[str] = None, + placement_group: Optional[str] = None, + admin_password_encryption_ssh_key_id: Optional[str] = None, + ) -> CreateServerResponse: + """ + Create an Instance. + Create a new Instance of the specified commercial type in the specified zone. Pay attention to the volumes parameter, which takes an object which can be used in different ways to achieve different behaviors. + Get more information in the [Technical Information](#technical-information) section of the introduction. + :param zone: Zone to target. If none is passed will use default zone from the config. + :param commercial_type: Define the Instance commercial type (i.e. GP1-S). + :param name: Instance name. + :param dynamic_ip_required: By default, `dynamic_ip_required` is true, a dynamic ip is attached to the instance (if no flexible ip is already attached). + :param routed_ip_enabled: If true, configure the Instance so it uses the new routed IP mode. + :param image: Instance image ID or label. + :param volumes: Volumes attached to the server. + :param enable_ipv6: True if IPv6 is enabled on the server (deprecated and always `False` when `routed_ip_enabled` is `True`). + :param protected: True to activate server protection option. + :param public_ip: ID of the reserved IP to attach to the Instance. + :param public_ips: A list of reserved IP IDs to attach to the Instance. + :param boot_type: Boot type to use. + :param organization: Instance Organization ID. + One-Of ('project_identifier'): at most one of 'project', 'organization' could be set. + :param project: Instance Project ID. + One-Of ('project_identifier'): at most one of 'project', 'organization' could be set. + :param tags: Instance tags. + :param security_group: Security group ID. + :param placement_group: Placement group ID if Instance must be part of a placement group. + :param admin_password_encryption_ssh_key_id: The public_key value of this key is used to encrypt the admin password. + :return: :class:`CreateServerResponse ` + + Usage: + :: + + result = api.create_instance_server( + commercial_type="example", + protected=False, + ) + """ + + payload = { + "zone": zone, + "commercial_type": commercial_type, + "name": name, + "dynamic_ip_required": dynamic_ip_required, + "routed_ip_enabled": routed_ip_enabled, + "image": image, + "volumes": volumes, + "enable_ipv6": enable_ipv6, + "protected": protected, + "public_ip": public_ip, + "public_ips": public_ips, + "boot_type": boot_type, + "organization": organization, + "project": project, + "tags": tags, + "security_group": security_group, + "placement_group": placement_group, + "admin_password_encryption_ssh_key_id": admin_password_encryption_ssh_key_id, + } + + clean_payload = {k: v for k, v in payload.items() if v is not None} + + response = self._create_server(**clean_payload) + + return response From d6c1fadefd3f4adced6c0e7d901c2e11a814096c Mon Sep 17 00:00:00 2001 From: Laure-di Date: Wed, 8 Oct 2025 09:52:56 +0200 Subject: [PATCH 2/4] doc(instance): guide how to create an instance --- scaleway/scaleway/instance/v1/tests/test_instance_sdk.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scaleway/scaleway/instance/v1/tests/test_instance_sdk.py b/scaleway/scaleway/instance/v1/tests/test_instance_sdk.py index 55556cccb..03712319c 100644 --- a/scaleway/scaleway/instance/v1/tests/test_instance_sdk.py +++ b/scaleway/scaleway/instance/v1/tests/test_instance_sdk.py @@ -51,7 +51,7 @@ def instance_volume( ) } - instance = instance_api._create_server( + instance = instance_api.create_instance_server( commercial_type=commercial_type, zone=zone, name=server_name, @@ -131,7 +131,7 @@ def test_create_new_server( instance_block_api: tuple[InstanceUtilsV1API, BlockV1Alpha1API], ) -> None: instance_api, _ = instance_block_api - server_instance = instance_api._create_server( + server_instance = instance_api.create_instance_server( commercial_type=commercial_type, zone=zone, name=server_name_extra, From ea38221732e3a238224566f023961f9f3e802702 Mon Sep 17 00:00:00 2001 From: Laure-di Date: Wed, 8 Oct 2025 11:31:58 +0200 Subject: [PATCH 3/4] fix(instance): update tests and add update-goldens rule --- Makefile | 3 + scaleway/scaleway/instance/v1/custom_api.py | 115 +++++++++--------- .../instance/v1/tests/test_custom_api.py | 3 +- 3 files changed, 64 insertions(+), 57 deletions(-) diff --git a/Makefile b/Makefile index 5054d08d1..86543ac83 100644 --- a/Makefile +++ b/Makefile @@ -58,6 +58,9 @@ test: poetry run pytest -v; \ done +update-goldens: + PYTHON_UPDATE_CASSETTES=true $(MAKE) test + publish: install-dependencies for lib in $(LIBRARIES); do \ cd ${WORKDIR}/$$lib && \ diff --git a/scaleway/scaleway/instance/v1/custom_api.py b/scaleway/scaleway/instance/v1/custom_api.py index 0845ae566..37c2f2162 100644 --- a/scaleway/scaleway/instance/v1/custom_api.py +++ b/scaleway/scaleway/instance/v1/custom_api.py @@ -9,6 +9,11 @@ from .api import InstanceV1API from .custom_marshalling import marshal_GetServerUserDataRequest from .custom_types import GetServerUserDataRequest, GetAllServerUserDataResponse +from .types import ( + VolumeServerTemplate, + BootType, + CreateServerResponse, +) max_retry = 10 interval = 0.01 @@ -148,61 +153,61 @@ def wait_instance_server(self, server_id: str, zone: ScwZone) -> GetServerRespon ) def create_instance_server( - self, - *, - zone: Optional[ScwZone] = None, - commercial_type: str, - name: Optional[str] = None, - dynamic_ip_required: Optional[bool] = None, - routed_ip_enabled: Optional[bool] = None, - image: Optional[str] = None, - volumes: Optional[dict[str, VolumeServerTemplate]] = None, - enable_ipv6: Optional[bool] = None, - protected: bool = False, - public_ip: Optional[str] = None, - public_ips: Optional[list[str]] = None, - boot_type: Optional[BootType] = None, - organization: Optional[str] = None, - project: Optional[str] = None, - tags: Optional[list[str]] = None, - security_group: Optional[str] = None, - placement_group: Optional[str] = None, - admin_password_encryption_ssh_key_id: Optional[str] = None, + self, + *, + zone: Optional[ScwZone] = None, + commercial_type: str, + name: Optional[str] = None, + dynamic_ip_required: Optional[bool] = None, + routed_ip_enabled: Optional[bool] = None, + image: Optional[str] = None, + volumes: Optional[dict[str, VolumeServerTemplate]] = None, + enable_ipv6: Optional[bool] = None, + protected: bool = False, + public_ip: Optional[str] = None, + public_ips: Optional[list[str]] = None, + boot_type: Optional[BootType] = None, + organization: Optional[str] = None, + project: Optional[str] = None, + tags: Optional[list[str]] = None, + security_group: Optional[str] = None, + placement_group: Optional[str] = None, + admin_password_encryption_ssh_key_id: Optional[str] = None, ) -> CreateServerResponse: """ - Create an Instance. - Create a new Instance of the specified commercial type in the specified zone. Pay attention to the volumes parameter, which takes an object which can be used in different ways to achieve different behaviors. - Get more information in the [Technical Information](#technical-information) section of the introduction. - :param zone: Zone to target. If none is passed will use default zone from the config. - :param commercial_type: Define the Instance commercial type (i.e. GP1-S). - :param name: Instance name. - :param dynamic_ip_required: By default, `dynamic_ip_required` is true, a dynamic ip is attached to the instance (if no flexible ip is already attached). - :param routed_ip_enabled: If true, configure the Instance so it uses the new routed IP mode. - :param image: Instance image ID or label. - :param volumes: Volumes attached to the server. - :param enable_ipv6: True if IPv6 is enabled on the server (deprecated and always `False` when `routed_ip_enabled` is `True`). - :param protected: True to activate server protection option. - :param public_ip: ID of the reserved IP to attach to the Instance. - :param public_ips: A list of reserved IP IDs to attach to the Instance. - :param boot_type: Boot type to use. - :param organization: Instance Organization ID. - One-Of ('project_identifier'): at most one of 'project', 'organization' could be set. - :param project: Instance Project ID. - One-Of ('project_identifier'): at most one of 'project', 'organization' could be set. - :param tags: Instance tags. - :param security_group: Security group ID. - :param placement_group: Placement group ID if Instance must be part of a placement group. - :param admin_password_encryption_ssh_key_id: The public_key value of this key is used to encrypt the admin password. - :return: :class:`CreateServerResponse ` - - Usage: - :: - - result = api.create_instance_server( - commercial_type="example", - protected=False, - ) - """ + Create an Instance. + Create a new Instance of the specified commercial type in the specified zone. Pay attention to the volumes parameter, which takes an object which can be used in different ways to achieve different behaviors. + Get more information in the [Technical Information](#technical-information) section of the introduction. + :param zone: Zone to target. If none is passed will use default zone from the config. + :param commercial_type: Define the Instance commercial type (i.e. GP1-S). + :param name: Instance name. + :param dynamic_ip_required: By default, `dynamic_ip_required` is true, a dynamic ip is attached to the instance (if no flexible ip is already attached). + :param routed_ip_enabled: If true, configure the Instance so it uses the new routed IP mode. + :param image: Instance image ID or label. + :param volumes: Volumes attached to the server. + :param enable_ipv6: True if IPv6 is enabled on the server (deprecated and always `False` when `routed_ip_enabled` is `True`). + :param protected: True to activate server protection option. + :param public_ip: ID of the reserved IP to attach to the Instance. + :param public_ips: A list of reserved IP IDs to attach to the Instance. + :param boot_type: Boot type to use. + :param organization: Instance Organization ID. + One-Of ('project_identifier'): at most one of 'project', 'organization' could be set. + :param project: Instance Project ID. + One-Of ('project_identifier'): at most one of 'project', 'organization' could be set. + :param tags: Instance tags. + :param security_group: Security group ID. + :param placement_group: Placement group ID if Instance must be part of a placement group. + :param admin_password_encryption_ssh_key_id: The public_key value of this key is used to encrypt the admin password. + :return: :class:`CreateServerResponse ` + + Usage: + :: + + result = api.create_instance_server( + commercial_type="example", + protected=False, + ) + """ payload = { "zone": zone, @@ -227,6 +232,4 @@ def create_instance_server( clean_payload = {k: v for k, v in payload.items() if v is not None} - response = self._create_server(**clean_payload) - - return response + return self._create_server(**clean_payload) # type: ignore[arg-type] diff --git a/scaleway/scaleway/instance/v1/tests/test_custom_api.py b/scaleway/scaleway/instance/v1/tests/test_custom_api.py index 2bbd86795..4e48eefa6 100644 --- a/scaleway/scaleway/instance/v1/tests/test_custom_api.py +++ b/scaleway/scaleway/instance/v1/tests/test_custom_api.py @@ -7,6 +7,7 @@ from tests.utils import initialize_client_test from vcr_config import scw_vcr + server_name = "test-sdk-python-fixture" server_name_extra = "test-sdk-python-extra" max_retry = 10 @@ -26,7 +27,7 @@ def instance_api() -> InstanceUtilsV1API: @pytest.fixture(scope="module") @scw_vcr.use_cassette def server(instance_api: InstanceUtilsV1API) -> Generator[Server, Any, None]: - instance = instance_api._create_server( + instance = instance_api.create_instance_server( commercial_type=commercial_type, zone=zone, name=server_name, From 31e23efbf6e16571993569bfefbee691e4ffc8ea Mon Sep 17 00:00:00 2001 From: Laure-di Date: Wed, 8 Oct 2025 17:09:02 +0200 Subject: [PATCH 4/4] rename function --- scaleway/scaleway/instance/v1/custom_api.py | 2 +- scaleway/scaleway/instance/v1/tests/test_custom_api.py | 3 +-- scaleway/scaleway/instance/v1/tests/test_instance_sdk.py | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/scaleway/scaleway/instance/v1/custom_api.py b/scaleway/scaleway/instance/v1/custom_api.py index 37c2f2162..853dcc7af 100644 --- a/scaleway/scaleway/instance/v1/custom_api.py +++ b/scaleway/scaleway/instance/v1/custom_api.py @@ -152,7 +152,7 @@ def wait_instance_server(self, server_id: str, zone: ScwZone) -> GetServerRespon f"after {max_retry} retries." ) - def create_instance_server( + def create_server( self, *, zone: Optional[ScwZone] = None, diff --git a/scaleway/scaleway/instance/v1/tests/test_custom_api.py b/scaleway/scaleway/instance/v1/tests/test_custom_api.py index 4e48eefa6..c6b34d83e 100644 --- a/scaleway/scaleway/instance/v1/tests/test_custom_api.py +++ b/scaleway/scaleway/instance/v1/tests/test_custom_api.py @@ -7,7 +7,6 @@ from tests.utils import initialize_client_test from vcr_config import scw_vcr - server_name = "test-sdk-python-fixture" server_name_extra = "test-sdk-python-extra" max_retry = 10 @@ -27,7 +26,7 @@ def instance_api() -> InstanceUtilsV1API: @pytest.fixture(scope="module") @scw_vcr.use_cassette def server(instance_api: InstanceUtilsV1API) -> Generator[Server, Any, None]: - instance = instance_api.create_instance_server( + instance = instance_api.create_server( commercial_type=commercial_type, zone=zone, name=server_name, diff --git a/scaleway/scaleway/instance/v1/tests/test_instance_sdk.py b/scaleway/scaleway/instance/v1/tests/test_instance_sdk.py index 03712319c..992f623d5 100644 --- a/scaleway/scaleway/instance/v1/tests/test_instance_sdk.py +++ b/scaleway/scaleway/instance/v1/tests/test_instance_sdk.py @@ -51,7 +51,7 @@ def instance_volume( ) } - instance = instance_api.create_instance_server( + instance = instance_api.create_server( commercial_type=commercial_type, zone=zone, name=server_name, @@ -131,7 +131,7 @@ def test_create_new_server( instance_block_api: tuple[InstanceUtilsV1API, BlockV1Alpha1API], ) -> None: instance_api, _ = instance_block_api - server_instance = instance_api.create_instance_server( + server_instance = instance_api.create_server( commercial_type=commercial_type, zone=zone, name=server_name_extra,