deps: update dependency hcloud to v1.29.0 (#334)

[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [hcloud](https://togithub.com/hetznercloud/hcloud-python)
([changelog](https://togithub.com/hetznercloud/hcloud-python/blob/main/CHANGELOG.md))
| `1.28.0` -> `1.29.0` |
[![age](https://developer.mend.io/api/mc/badges/age/pypi/hcloud/1.29.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/pypi/hcloud/1.29.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/pypi/hcloud/1.28.0/1.29.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/hcloud/1.28.0/1.29.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>hetznercloud/hcloud-python (hcloud)</summary>

###
[`v1.29.0`](https://togithub.com/hetznercloud/hcloud-python/blob/HEAD/CHANGELOG.md#1290-2023-09-25)

[Compare
Source](https://togithub.com/hetznercloud/hcloud-python/compare/v1.28.0...v1.29.0)

##### Features

- add domain attribute type hints to bound models
([#&#8203;300](https://togithub.com/hetznercloud/hcloud-python/issues/300))
([6d46d06](6d46d06c42))
- **firewalls:** add `applied_to_resources` to `FirewallResource`
([#&#8203;297](https://togithub.com/hetznercloud/hcloud-python/issues/297))
([55d2b20](55d2b2043e))

##### Bug Fixes

- missing BaseDomain base class inheritance
([#&#8203;303](https://togithub.com/hetznercloud/hcloud-python/issues/303))
([0ee7598](0ee759856c))

##### Dependencies

- update actions/checkout action to v4
([#&#8203;295](https://togithub.com/hetznercloud/hcloud-python/issues/295))
([c02b446](c02b4468f0))
- update dependency sphinx to >=7.2.2,<7.3
([#&#8203;291](https://togithub.com/hetznercloud/hcloud-python/issues/291))
([10234ea](10234ea7bf))
- update dependency sphinx to v7
([#&#8203;211](https://togithub.com/hetznercloud/hcloud-python/issues/211))
([f635c94](f635c94c23))
- update pre-commit hook asottile/pyupgrade to v3.11.0
([#&#8203;298](https://togithub.com/hetznercloud/hcloud-python/issues/298))
([4bbd0cc](4bbd0ccb0f))
- update pre-commit hook asottile/pyupgrade to v3.11.1
([#&#8203;299](https://togithub.com/hetznercloud/hcloud-python/issues/299))
([2f9fcd7](2f9fcd7bb8))
- update pre-commit hook asottile/pyupgrade to v3.13.0
([#&#8203;301](https://togithub.com/hetznercloud/hcloud-python/issues/301))
([951dbf3](951dbf3e3b))
- update pre-commit hook pre-commit/mirrors-prettier to v3.0.3
([#&#8203;294](https://togithub.com/hetznercloud/hcloud-python/issues/294))
([381e336](381e336ff1))
- update pre-commit hook psf/black to v23.9.1
([#&#8203;296](https://togithub.com/hetznercloud/hcloud-python/issues/296))
([4374a7b](4374a7be9f))

##### Documentation

- load token from env in examples scripts
([#&#8203;302](https://togithub.com/hetznercloud/hcloud-python/issues/302))
([f18c9a6](f18c9a60e0))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Mend
Renovate](https://www.mend.io/free-developer-tools/renovate/). View
repository job log
[here](https://developer.mend.io/github/ansible-collections/hetzner.hcloud).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNi45Ny4xIiwidXBkYXRlZEluVmVyIjoiMzYuOTcuMSIsInRhcmdldEJyYW5jaCI6Im1haW4ifQ==-->

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: jo <ljonas@riseup.net>
This commit is contained in:
renovate[bot] 2023-09-25 13:34:49 +02:00 committed by GitHub
parent 576721987e
commit 4ae557c287
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 136 additions and 47 deletions

View file

@ -45,7 +45,7 @@ class Client:
poll_interval: int = 1,
timeout: float | tuple[float, float] | None = None,
):
"""Create an new Client instance
"""Create a new Client instance
:param token: Hetzner Cloud API token
:param api_endpoint: Hetzner Cloud API endpoint

View file

@ -1,3 +1,3 @@
from __future__ import annotations
VERSION = "1.28.0" # x-release-please-version
VERSION = "1.29.0" # x-release-please-version

View file

@ -11,7 +11,7 @@ if TYPE_CHECKING:
from .._client import Client
class BoundAction(BoundModelBase):
class BoundAction(BoundModelBase, Action):
_client: ActionsClient
model = Action
@ -27,6 +27,7 @@ class BoundAction(BoundModelBase):
while self.status == Action.STATUS_RUNNING:
if max_retries > 0:
self.reload()
# pylint: disable=protected-access
time.sleep(self._client._client.poll_interval)
max_retries = max_retries - 1
else:

View file

@ -15,7 +15,7 @@ if TYPE_CHECKING:
from .._client import Client
class BoundCertificate(BoundModelBase):
class BoundCertificate(BoundModelBase, Certificate):
_client: CertificatesClient
model = Certificate

View file

@ -45,6 +45,7 @@ class ClientEntityBase:
def _get_first_by(self, **kwargs): # type: ignore[no-untyped-def]
assert hasattr(self, "get_list")
# pylint: disable=no-member
entities, _ = self.get_list(**kwargs)
return entities[0] if entities else None

View file

@ -6,6 +6,9 @@ class BaseDomain:
@classmethod
def from_dict(cls, data: dict): # type: ignore[no-untyped-def]
"""
Build the domain object from the data dict.
"""
supported_data = {k: v for k, v in data.items() if k in cls.__slots__}
return cls(**supported_data)
@ -22,12 +25,14 @@ class DomainIdentityMixin:
@property
def id_or_name(self) -> int | str:
"""
Return the first defined value, and fails if none is defined.
"""
if self.id is not None:
return self.id
elif self.name is not None:
if self.name is not None:
return self.name
else:
raise ValueError("id or name must be set")
raise ValueError("id or name must be set")
class Pagination(BaseDomain):
@ -65,6 +70,9 @@ class Meta(BaseDomain):
@classmethod
def parse_meta(cls, response: dict) -> Meta | None:
"""
If present, extract the meta details from the response and return a meta object.
"""
meta = None
if response and "meta" in response:
meta = cls()

View file

@ -11,7 +11,7 @@ if TYPE_CHECKING:
from .._client import Client
class BoundDatacenter(BoundModelBase):
class BoundDatacenter(BoundModelBase, Datacenter):
_client: DatacentersClient
model = Datacenter

View file

@ -36,7 +36,7 @@ class Datacenter(BaseDomain, DomainIdentityMixin):
self.server_types = server_types
class DatacenterServerTypes:
class DatacenterServerTypes(BaseDomain):
"""DatacenterServerTypes Domain
:param available: List[:class:`BoundServerTypes <hcloud.server_types.client.BoundServerTypes>`]

View file

@ -5,6 +5,7 @@ from .domain import ( # noqa: F401
CreateFirewallResponse,
Firewall,
FirewallResource,
FirewallResourceAppliedToResources,
FirewallResourceLabelSelector,
FirewallRule,
)

View file

@ -8,6 +8,7 @@ from .domain import (
CreateFirewallResponse,
Firewall,
FirewallResource,
FirewallResourceAppliedToResources,
FirewallResourceLabelSelector,
FirewallRule,
)
@ -16,7 +17,7 @@ if TYPE_CHECKING:
from .._client import Client
class BoundFirewall(BoundModelBase):
class BoundFirewall(BoundModelBase, Firewall):
_client: FirewallsClient
model = Firewall
@ -39,29 +40,53 @@ class BoundFirewall(BoundModelBase):
applied_to = data.get("applied_to", [])
if applied_to:
# pylint: disable=import-outside-toplevel
from ..servers import BoundServer
ats = []
for a in applied_to:
if a["type"] == FirewallResource.TYPE_SERVER:
ats.append(
data_applied_to = []
for firewall_resource in applied_to:
applied_to_resources = None
if firewall_resource.get("applied_to_resources"):
applied_to_resources = [
FirewallResourceAppliedToResources(
type=resource["type"],
server=(
BoundServer(
client._client.servers,
resource.get("server"),
complete=False,
)
if resource.get("server") is not None
else None
),
)
for resource in firewall_resource.get("applied_to_resources")
]
if firewall_resource["type"] == FirewallResource.TYPE_SERVER:
data_applied_to.append(
FirewallResource(
type=a["type"],
type=firewall_resource["type"],
server=BoundServer(
client._client.servers, a["server"], complete=False
client._client.servers,
firewall_resource["server"],
complete=False,
),
applied_to_resources=applied_to_resources,
)
)
elif a["type"] == FirewallResource.TYPE_LABEL_SELECTOR:
ats.append(
elif firewall_resource["type"] == FirewallResource.TYPE_LABEL_SELECTOR:
data_applied_to.append(
FirewallResource(
type=a["type"],
type=firewall_resource["type"],
label_selector=FirewallResourceLabelSelector(
selector=a["label_selector"]["selector"]
selector=firewall_resource["label_selector"]["selector"]
),
applied_to_resources=applied_to_resources,
)
)
data["applied_to"] = ats
data["applied_to"] = data_applied_to
super().__init__(client, data, complete)

View file

@ -51,7 +51,7 @@ class Firewall(BaseDomain):
self.created = isoparse(created) if created else None
class FirewallRule:
class FirewallRule(BaseDomain):
"""Firewall Rule Domain
:param direction: str
@ -111,6 +111,9 @@ class FirewallRule:
self.description = description
def to_payload(self) -> dict[str, Any]:
"""
Generates the request payload from this domain object.
"""
payload: dict[str, Any] = {
"direction": self.direction,
"protocol": self.protocol,
@ -125,7 +128,7 @@ class FirewallRule:
return payload
class FirewallResource:
class FirewallResource(BaseDomain):
"""Firewall Used By Domain
:param type: str
@ -134,9 +137,11 @@ class FirewallResource:
Server the Firewall is applied to
:param label_selector: Optional[FirewallResourceLabelSelector]
Label Selector for Servers the Firewall should be applied to
:param applied_to_resources: (read-only) List of effective resources the firewall is
applied to.
"""
__slots__ = ("type", "server", "label_selector")
__slots__ = ("type", "server", "label_selector", "applied_to_resources")
TYPE_SERVER = "server"
"""Firewall Used By Type Server"""
@ -148,12 +153,17 @@ class FirewallResource:
type: str,
server: Server | BoundServer | None = None,
label_selector: FirewallResourceLabelSelector | None = None,
applied_to_resources: list[FirewallResourceAppliedToResources] | None = None,
):
self.type = type
self.server = server
self.label_selector = label_selector
self.applied_to_resources = applied_to_resources
def to_payload(self) -> dict[str, Any]:
"""
Generates the request payload from this domain object.
"""
payload: dict[str, Any] = {"type": self.type}
if self.server is not None:
payload["server"] = {"id": self.server.id}
@ -163,6 +173,24 @@ class FirewallResource:
return payload
class FirewallResourceAppliedToResources(BaseDomain):
"""Firewall Resource applied to Domain
:param type: Type of resource referenced
:param server: Server the Firewall is applied to
"""
__slots__ = ("type", "server")
def __init__(
self,
type: str,
server: BoundServer | None = None,
):
self.type = type
self.server = server
class FirewallResourceLabelSelector(BaseDomain):
"""FirewallResourceLabelSelector Domain

View file

@ -13,12 +13,13 @@ if TYPE_CHECKING:
from ..servers import BoundServer, Server
class BoundFloatingIP(BoundModelBase):
class BoundFloatingIP(BoundModelBase, FloatingIP):
_client: FloatingIPsClient
model = FloatingIP
def __init__(self, client: FloatingIPsClient, data: dict, complete: bool = True):
# pylint: disable=import-outside-toplevel
from ..servers import BoundServer
server = data.get("server")

View file

@ -8,4 +8,5 @@ warnings.warn(
stacklevel=2,
)
# pylint: disable=wildcard-import,wrong-import-position,unused-wildcard-import
from ._client import * # noqa

View file

@ -18,10 +18,10 @@ class LabelValidator:
:return: bool
"""
for k, v in labels.items():
if LabelValidator.KEY_REGEX.match(k) is None:
for key, value in labels.items():
if LabelValidator.KEY_REGEX.match(key) is None:
return False
if LabelValidator.VALUE_REGEX.match(v) is None:
if LabelValidator.VALUE_REGEX.match(value) is None:
return False
return True
@ -32,9 +32,15 @@ class LabelValidator:
:return: bool, str
"""
for k, v in labels.items():
if LabelValidator.KEY_REGEX.match(k) is None:
return False, f"label key {k} is not correctly formatted"
if LabelValidator.VALUE_REGEX.match(v) is None:
return False, f"label value {v} (key: {k}) is not correctly formatted"
for key, value in labels.items():
if LabelValidator.KEY_REGEX.match(key) is None:
return (
False,
f"label key {key} is not correctly formatted",
)
if LabelValidator.VALUE_REGEX.match(value) is None:
return (
False,
f"label value {value} (key: {key}) is not correctly formatted",
)
return True, ""

View file

@ -10,12 +10,13 @@ if TYPE_CHECKING:
from .._client import Client
class BoundImage(BoundModelBase):
class BoundImage(BoundModelBase, Image):
_client: ImagesClient
model = Image
def __init__(self, client: ImagesClient, data: dict):
# pylint: disable=import-outside-toplevel
from ..servers import BoundServer
created_from = data.get("created_from")

View file

@ -74,6 +74,7 @@ class Image(BaseDomain, DomainIdentityMixin):
"deprecated",
)
# pylint: disable=too-many-locals
def __init__(
self,
id: int | None = None,

View file

@ -10,7 +10,7 @@ if TYPE_CHECKING:
from .._client import Client
class BoundIso(BoundModelBase):
class BoundIso(BoundModelBase, Iso):
_client: IsosClient
model = Iso

View file

@ -9,7 +9,7 @@ if TYPE_CHECKING:
from .._client import Client
class BoundLoadBalancerType(BoundModelBase):
class BoundLoadBalancerType(BoundModelBase, LoadBalancerType):
_client: LoadBalancerTypesClient
model = LoadBalancerType

View file

@ -34,11 +34,12 @@ if TYPE_CHECKING:
from ..networks import Network
class BoundLoadBalancer(BoundModelBase):
class BoundLoadBalancer(BoundModelBase, LoadBalancer):
_client: LoadBalancersClient
model = LoadBalancer
# pylint: disable=too-many-branches,too-many-locals
def __init__(self, client: LoadBalancersClient, data: dict, complete: bool = True):
algorithm = data.get("algorithm")
if algorithm:

View file

@ -72,6 +72,7 @@ class LoadBalancer(BaseDomain):
"included_traffic",
)
# pylint: disable=too-many-locals
def __init__(
self,
id: int,
@ -140,7 +141,11 @@ class LoadBalancerService(BaseDomain):
self.health_check = health_check
self.http = http
# pylint: disable=too-many-branches
def to_payload(self) -> dict[str, Any]:
"""
Generates the request payload from this domain object.
"""
payload: dict[str, Any] = {}
if self.protocol is not None:
@ -337,6 +342,9 @@ class LoadBalancerTarget(BaseDomain):
self.health_status = health_status
def to_payload(self) -> dict[str, Any]:
"""
Generates the request payload from this domain object.
"""
payload: dict[str, Any] = {
"type": self.type,
}

View file

@ -9,7 +9,7 @@ if TYPE_CHECKING:
from .._client import Client
class BoundLocation(BoundModelBase):
class BoundLocation(BoundModelBase, Location):
_client: LocationsClient
model = Location

View file

@ -10,7 +10,7 @@ if TYPE_CHECKING:
from .._client import Client
class BoundNetwork(BoundModelBase):
class BoundNetwork(BoundModelBase, Network):
_client: NetworksClient
model = Network
@ -26,6 +26,7 @@ class BoundNetwork(BoundModelBase):
routes = [NetworkRoute.from_dict(route) for route in routes]
data["routes"] = routes
# pylint: disable=import-outside-toplevel
from ..servers import BoundServer
servers = data.get("servers", [])

View file

@ -10,7 +10,7 @@ if TYPE_CHECKING:
from .._client import Client
class BoundPlacementGroup(BoundModelBase):
class BoundPlacementGroup(BoundModelBase, PlacementGroup):
_client: PlacementGroupsClient
model = PlacementGroup

View file

@ -11,12 +11,13 @@ if TYPE_CHECKING:
from ..datacenters import BoundDatacenter, Datacenter
class BoundPrimaryIP(BoundModelBase):
class BoundPrimaryIP(BoundModelBase, PrimaryIP):
_client: PrimaryIPsClient
model = PrimaryIP
def __init__(self, client: PrimaryIPsClient, data: dict, complete: bool = True):
# pylint: disable=import-outside-toplevel
from ..datacenters import BoundDatacenter
datacenter = data.get("datacenter", {})

View file

@ -9,7 +9,7 @@ if TYPE_CHECKING:
from .._client import Client
class BoundServerType(BoundModelBase):
class BoundServerType(BoundModelBase, ServerType):
_client: ServerTypesClient
model = ServerType

View file

@ -42,11 +42,12 @@ if TYPE_CHECKING:
from .domain import ServerCreatePublicNetwork
class BoundServer(BoundModelBase):
class BoundServer(BoundModelBase, Server):
_client: ServersClient
model = Server
# pylint: disable=too-many-locals
def __init__(self, client: ServersClient, data: dict, complete: bool = True):
datacenter = data.get("datacenter")
if datacenter is not None:
@ -540,6 +541,7 @@ class ServersClient(ClientEntityBase):
"""
return self._get_first_by(name=name)
# pylint: disable=too-many-branches,too-many-locals
def create(
self,
name: str,

View file

@ -107,6 +107,7 @@ class Server(BaseDomain):
"placement_group",
)
# pylint: disable=too-many-locals
def __init__(
self,
id: int,

View file

@ -9,7 +9,7 @@ if TYPE_CHECKING:
from .._client import Client
class BoundSSHKey(BoundModelBase):
class BoundSSHKey(BoundModelBase, SSHKey):
_client: SSHKeysClient
model = SSHKey

View file

@ -13,7 +13,7 @@ if TYPE_CHECKING:
from ..servers import BoundServer, Server
class BoundVolume(BoundModelBase):
class BoundVolume(BoundModelBase, Volume):
_client: VolumesClient
model = Volume
@ -23,6 +23,7 @@ class BoundVolume(BoundModelBase):
if location is not None:
data["location"] = BoundLocation(client._client.locations, location)
# pylint: disable=import-outside-toplevel
from ..servers import BoundServer
server = data.get("server")
@ -254,7 +255,7 @@ class VolumesClient(ClientEntityBase):
if size <= 0:
raise ValueError("size must be greater than 0")
if not (bool(location) ^ bool(server)):
if not bool(location) ^ bool(server):
raise ValueError("only one of server or location must be provided")
data: dict[str, Any] = {"name": name, "size": size}

View file

@ -19,7 +19,7 @@ from textwrap import dedent
logger = logging.getLogger("vendor")
HCLOUD_SOURCE_URL = "https://github.com/hetznercloud/hcloud-python"
HCLOUD_VERSION = "v1.28.0"
HCLOUD_VERSION = "v1.29.0"
HCLOUD_VENDOR_PATH = "plugins/module_utils/vendor/hcloud"