Skip to content
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

feat: allow returning root_password in servers rebuild #276

Merged
merged 1 commit into from
Nov 17, 2023
Merged
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
42 changes: 33 additions & 9 deletions hcloud/servers/client.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import warnings
from typing import TYPE_CHECKING, Any, NamedTuple

from ..actions import ActionsPageResult, BoundAction, ResourceActionsClient
Expand All @@ -21,6 +22,7 @@
PrivateNet,
PublicNetwork,
PublicNetworkFirewall,
RebuildResponse,
RequestConsoleResponse,
ResetPasswordResponse,
Server,
Expand Down Expand Up @@ -299,13 +301,18 @@ def create_image(
"""
return self._client.create_image(self, description, type, labels)

def rebuild(self, image: Image | BoundImage) -> BoundAction:
def rebuild(
self,
image: Image | BoundImage,
*,
return_response: bool = False,
) -> RebuildResponse | BoundAction:
"""Rebuilds a server overwriting its disk with the content of an image, thereby destroying all data on the target server.

:param image: :class:`BoundImage <hcloud.images.client.BoundImage>` or :class:`Image <hcloud.servers.domain.Image>`
:return: :class:`BoundAction <hcloud.actions.client.BoundAction>`
:param image: Image to use for the rebuilt server
:param return_response: Whether to return the full response or only the action.
"""
return self._client.rebuild(self, image)
return self._client.rebuild(self, image, return_response=return_response)

def change_type(
self,
Expand Down Expand Up @@ -930,20 +937,37 @@ def rebuild(
self,
server: Server | BoundServer,
image: Image | BoundImage,
) -> BoundAction:
*,
return_response: bool = False,
) -> RebuildResponse | BoundAction:
"""Rebuilds a server overwriting its disk with the content of an image, thereby destroying all data on the target server.

:param server: :class:`BoundServer <hcloud.servers.client.BoundServer>` or :class:`Server <hcloud.servers.domain.Server>`
:param image: :class:`BoundImage <hcloud.images.client.BoundImage>` or :class:`Image <hcloud.servers.domain.Image>`
:return: :class:`BoundAction <hcloud.actions.client.BoundAction>`
:param server: Server to rebuild
:param image: Image to use for the rebuilt server
:param return_response: Whether to return the full response or only the action.
"""
data: dict[str, Any] = {"image": image.id_or_name}
response = self._client.request(
url=f"/servers/{server.id}/actions/rebuild",
method="POST",
json=data,
)
return BoundAction(self._client.actions, response["action"])

rebuild_response = RebuildResponse(
action=BoundAction(self._client.actions, response["action"]),
root_password=response.get("root_password"),
)

if not return_response:
warnings.warn(
"Returning only the 'action' is deprecated, please set the "
"'return_response' keyword argument to 'True' to return the full "
"rebuild response and update your code accordingly.",
DeprecationWarning,
stacklevel=2,
)
return rebuild_response.action
return rebuild_response

def enable_backup(self, server: Server | BoundServer) -> BoundAction:
"""Enables and configures the automatic daily backup option for the server. Enabling automatic backups will increase the price of the server by 20%.
Expand Down
18 changes: 18 additions & 0 deletions hcloud/servers/domain.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,24 @@ def __init__(
self.password = password


class RebuildResponse(BaseDomain):
"""Rebuild Response Domain

:param action: Shows the progress of the server rebuild action
:param root_password: The root password of the server when not using SSH keys
"""

__slots__ = ("action", "root_password")

def __init__(
self,
action: BoundAction,
root_password: str | None,
):
self.action = action
self.root_password = root_password


class PublicNetwork(BaseDomain):
"""Public Network Domain

Expand Down
26 changes: 20 additions & 6 deletions tests/unit/servers/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -307,15 +307,19 @@ def test_create_image(

def test_rebuild(self, hetzner_client, bound_server, generic_action):
hetzner_client.request.return_value = generic_action
action = bound_server.rebuild(Image(name="ubuntu-20.04"))
response = bound_server.rebuild(
Image(name="ubuntu-20.04"),
return_response=True,
)
hetzner_client.request.assert_called_with(
url="/servers/14/actions/rebuild",
method="POST",
json={"image": "ubuntu-20.04"},
)

assert action.id == 1
assert action.progress == 0
assert response.action.id == 1
assert response.action.progress == 0
assert response.root_password is None or isinstance(response.root_password, str)

def test_enable_backup(self, hetzner_client, bound_server, generic_action):
hetzner_client.request.return_value = generic_action
Expand Down Expand Up @@ -1040,15 +1044,25 @@ def test_create_image(self, servers_client, server, response_server_create_image
)
def test_rebuild(self, servers_client, server, generic_action):
servers_client._client.request.return_value = generic_action
action = servers_client.rebuild(server, Image(name="ubuntu-20.04"))
response = servers_client.rebuild(
server,
Image(name="ubuntu-20.04"),
return_response=True,
)
servers_client._client.request.assert_called_with(
url="/servers/1/actions/rebuild",
method="POST",
json={"image": "ubuntu-20.04"},
)

assert action.id == 1
assert action.progress == 0
assert response.action.id == 1
assert response.action.progress == 0
assert response.root_password is None or isinstance(response.root_password, str)

def test_rebuild_return_response_deprecation(self, servers_client, generic_action):
servers_client._client.request.return_value = generic_action
with pytest.deprecated_call():
servers_client.rebuild(Server(id=1), Image(name="ubuntu-20.04"))

@pytest.mark.parametrize(
"server", [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))]
Expand Down