Skip to content

feat: waiting functionality for DNS service #40

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 12 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions examples/wait/wait_example.py
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is a generic example to demonstrate the wait functionality, I think it would be enough to have a single standard wait case (e.g. the wait_for_create_zone(client, project_id, zone_id)) and then add some examples to configure timeout, sleep before wait, etc

Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import os
from http import HTTPStatus

from stackit.core.configuration import Configuration
from stackit.core.wait import WaitConfig
from stackit.dns.api.default_api import DefaultApi
from stackit.dns.models.create_zone_payload import CreateZonePayload
from stackit.dns.wait import wait_for_create_zone

project_id = os.getenv("PROJECT_ID")

# Create a new API client, that uses default authentication and configuration
config = Configuration()
client = DefaultApi(config)


# Create a new DNS zone
create_zone_response = client.create_zone(
create_zone_payload=CreateZonePayload(name="myZone", dnsName="testZone.com"), project_id=project_id
)
zone1_id = create_zone_response.zone.id
zone2_id = client.create_zone(
create_zone_payload=CreateZonePayload(name="myZone2", dnsName="testZone2.com"), project_id=project_id
).zone.id
zone3_id = client.create_zone(
create_zone_payload=CreateZonePayload(name="myZone3", dnsName="testZone3.com"), project_id=project_id
).zone.id

# Wait for the zone to be fully created.
wait_for_zone_response = wait_for_create_zone(client, project_id, zone1_id)

# Optionally the wait configuration can be adjusted. It's possible to adjust just one of the parameters....
wait_for_zone_response = wait_for_create_zone(client, project_id, zone2_id, WaitConfig(sleep_before_wait=5))

# ... or all of them
wait_for_zone_response = wait_for_create_zone(
client,
project_id,
zone3_id,
WaitConfig(
sleep_before_wait=6,
throttle=10,
timeout=10,
temp_error_retry_limit=10,
retry_http_error_status_codes=[HTTPStatus.BAD_GATEWAY, HTTPStatus.GATEWAY_TIMEOUT],
),
)

# Delete all of the zones again
delete_zone_message = client.delete_zone(project_id, zone1_id)
delete_zone_message = client.delete_zone(project_id, zone2_id)
delete_zone_message = client.delete_zone(project_id, zone3_id)
2 changes: 1 addition & 1 deletion services/dns/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ packages = [

[tool.poetry.dependencies]
python = ">=3.8,<4.0"
stackit-core = "^0.0.1a"
stackit-core = "0.0.1a1"
requests = "^2.32.3"
pydantic = "^2.9.2"
python-dateutil = "^2.9.0.post0"
Expand Down
219 changes: 219 additions & 0 deletions services/dns/src/stackit/dns/wait.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
from enum import Enum
from typing import Any, Tuple, Union

from stackit.core.wait import Wait, WaitConfig

from stackit.dns.api.default_api import DefaultApi
from stackit.dns.exceptions import ApiException
from stackit.dns.models.record_set_response import RecordSetResponse
from stackit.dns.models.zone_response import ZoneResponse


class _States(str, Enum):
CreateSuccess = "CREATE_SUCCEEDED"
CreateFail = "CREATE_FAILED"
UpdateSuccess = "UPDATE_SUCCEEDED"
UpdateFail = "UPDATE_FAILED"
DeleteSuccess = "DELETE_SUCCEEDED"
DeleteFail = "DELETE_FAILED"


def wait_for_create_zone(
api_client: DefaultApi,
project_id: str,
zone_id: str,
wait_config: Union[WaitConfig, None] = None,
) -> ZoneResponse:

def get_zone_execute_state() -> Tuple[bool, Union[Exception, None], Union[int, None], Any]:

nonlocal api_client, project_id, zone_id

try:
response = api_client.get_zone(project_id, zone_id)
if response.zone.id != zone_id:
return False, ValueError("ID of zone in return not equal to ID of requested zone."), None, None
elif response.zone.state == _States.CreateSuccess:
return True, None, None, response
elif response.zone.state == _States.CreateFail:
return True, Exception("Create failed for zone with ID %s" % zone_id), None, response
else:
return False, None, None, None
except ApiException as e:
return False, e, e.status, None
except Exception as e:
return False, e, None, None

wait = Wait(
get_zone_execute_state,
config=wait_config,
)
return wait.wait()


def wait_for_partial_update_zone(
api_client: DefaultApi,
project_id: str,
zone_id: str,
wait_config: Union[WaitConfig, None] = None,
) -> ZoneResponse:

def get_zone_execute_state() -> Tuple[bool, Union[Exception, None], Union[int, None], Any]:

nonlocal api_client, project_id, zone_id

try:
response = api_client.get_zone(project_id, zone_id)
if response.zone.id != zone_id:
return False, ValueError("ID of zone in return not equal to ID of requested zone."), None, None
elif response.zone.state == _States.UpdateSuccess:
return True, None, None, response
elif response.zone.state == _States.UpdateFail:
return True, Exception("Update failed for zone with ID %s" % zone_id), None, response
else:
return False, None, None, None
except ApiException as e:
return False, e, e.status, None
except Exception as e:
return False, e, None, None

wait = Wait(
get_zone_execute_state,
config=wait_config,
)
return wait.wait()


def wait_for_delete_zone(
api_client: DefaultApi,
project_id: str,
zone_id: str,
wait_config: Union[WaitConfig, None] = None,
) -> ZoneResponse:

def get_zone_execute_state() -> Tuple[bool, Union[Exception, None], Union[int, None], Any]:

nonlocal api_client, project_id, zone_id

try:
response = api_client.get_zone(project_id, zone_id)
if response.zone.id != zone_id:
return False, ValueError("ID of zone in return not equal to ID of requested zone."), None, None
elif response.zone.state == _States.DeleteSuccess:
return True, None, None, response
elif response.zone.state == _States.DeleteFail:
return True, Exception("Delete failed for zone with ID %s" % zone_id), None, response
else:
return False, None, None, None
except ApiException as e:
return False, e, e.status, None
except Exception as e:
return False, e, None, None

wait = Wait(
get_zone_execute_state,
config=wait_config,
)
return wait.wait()


def wait_for_create_recordset(
api_client: DefaultApi,
project_id: str,
zone_id: str,
rr_set_id: str,
wait_config: Union[WaitConfig, None] = None,
) -> RecordSetResponse:

def get_rr_set_execute_state() -> Tuple[bool, Union[Exception, None], Union[int, None], Any]:

nonlocal api_client, project_id, zone_id, rr_set_id

try:
response = api_client.get_record_set(project_id, zone_id, rr_set_id)
if response.rrset.id != rr_set_id:
return False, ValueError("ID of rrset in return not equal to ID of requested rrset."), None, None
elif response.rrset.state == _States.CreateSuccess:
return True, None, None, response
elif response.rrset.state == _States.CreateFail:
return True, Exception("Create failed for rrset with ID %s" % rr_set_id), None, response
else:
return False, None, None, None
except ApiException as e:
return False, e, e.status, None
except Exception as e:
return False, e, None, None

wait = Wait(
get_rr_set_execute_state,
config=wait_config,
)
return wait.wait()


def wait_for_partial_update_recordset(
api_client: DefaultApi,
project_id: str,
zone_id: str,
rr_set_id: str,
wait_config: Union[WaitConfig, None] = None,
) -> RecordSetResponse:

def get_rr_set_execute_state() -> Tuple[bool, Union[Exception, None], Union[int, None], Any]:

nonlocal api_client, project_id, zone_id, rr_set_id

try:
response = api_client.get_record_set(project_id, zone_id, rr_set_id)
if response.rrset.id != rr_set_id:
return False, ValueError("ID of rrset in return not equal to ID of requested rrset."), None, None
elif response.rrset.state == _States.UpdateSuccess:
return True, None, None, response
elif response.rrset.state == _States.UpdateFail:
return True, Exception("Update failed for rrset with ID %s" % rr_set_id), None, response
else:
return False, None, None, None
except ApiException as e:
return False, e, e.status, None
except Exception as e:
return False, e, None, None

wait = Wait(
get_rr_set_execute_state,
config=wait_config,
)
return wait.wait()


def wait_for_delete_recordset(
api_client: DefaultApi,
project_id: str,
zone_id: str,
rr_set_id: str,
wait_config: Union[WaitConfig, None] = None,
) -> RecordSetResponse:

def get_rr_set_execute_state() -> Tuple[bool, Union[Exception, None], Union[int, None], Any]:

nonlocal api_client, project_id, zone_id, rr_set_id

try:
response = api_client.get_record_set(project_id, zone_id, rr_set_id)
if response.rrset.id != rr_set_id:
return False, ValueError("ID of rrset in return not equal to ID of requested rrset."), None, None
elif response.rrset.state == _States.DeleteSuccess:
return True, None, None, response
elif response.rrset.state == _States.DeleteFail:
return True, Exception("Delete failed for rrset with ID %s" % rr_set_id), None, response
else:
return False, None, None, None
except ApiException as e:
return False, e, e.status, None
except Exception as e:
return False, e, None, None

wait = Wait(
get_rr_set_execute_state,
config=wait_config,
)
return wait.wait()
Loading
Loading