1
0
Fork 0
mirror of https://github.com/ansible-collections/hetzner.hcloud.git synced 2026-02-04 08:01:49 +00:00
hetzner.hcloud/tests/unit/module_utils/test_deprecation.py
Jonas L. 826e6a5309
feat: per location server types (#692)
[Server Types](https://docs.hetzner.cloud/reference/cloud#server-types)
now depend on
[Locations](https://docs.hetzner.cloud/reference/cloud#locations).

- We added a new `locations` property to the [Server
Types](https://docs.hetzner.cloud/reference/cloud#server-types)
resource. The new property defines a list of supported
[Locations](https://docs.hetzner.cloud/reference/cloud#locations) and
additional per
[Locations](https://docs.hetzner.cloud/reference/cloud#locations)
details such as deprecations information.

- We deprecated the `deprecation` property from the [Server
Types](https://docs.hetzner.cloud/reference/cloud#server-types)
resource. The property will gradually be phased out as per
[Locations](https://docs.hetzner.cloud/reference/cloud#locations)
deprecations are being announced. Please use the new per
[Locations](https://docs.hetzner.cloud/reference/cloud#locations)
deprecation information instead.

See our
[changelog](https://docs.hetzner.cloud/changelog#2025-09-24-per-location-server-types)
for more details.

**Upgrading**

```yaml
---
- name: Validate server type
  hosts: localhost
  connection: local
  tasks:
    - name: Fetch server type info
      hetzner.hcloud.server_type_info:
        name: cx22
      register: server_type

    - name: Ensure server type exists
      ansible.builtin.assert:
        fail_msg: server type does not exists
        that:
          - server_type.hcloud_server_type_info | count == 1

    - name: Ensure server type is not deprecated
      ansible.builtin.assert:
        fail_msg: server type is deprecated
        that:
          - server_type.hcloud_server_type_info[0].deprecation is none
```

```yaml
---
- name: Validate server type
  hosts: localhost
  connection: local
  tasks:
    - name: Fetch location info
      hetzner.hcloud.location_info:
        name: fsn1
      register: location

    - name: Fetch server type info
      hetzner.hcloud.server_type_info:
        name: cx22
      register: server_type

    - name: Ensure server type exists
      ansible.builtin.assert:
        fail_msg: server type does not exists
        that:
          - server_type.hcloud_server_type_info | count == 1

    - name: Extract server type location info
      ansible.builtin.set_fact:
        server_type_location: >
          {{
            server_type.hcloud_server_type_info[0].locations
            | selectattr("name", "eq", location.hcloud_location_info[0].name)
            | first
          }}

    - name: Ensure server type is not deprecated
      ansible.builtin.assert:
        fail_msg: server type is deprecated in location
        that:
          - server_type_location.deprecation is none
```
2025-09-26 11:50:05 +02:00

254 lines
8.1 KiB
Python

from __future__ import annotations
from datetime import datetime, timedelta, timezone
from unittest import mock
import pytest
from ansible_collections.hetzner.hcloud.plugins.module_utils.deprecation import (
deprecated_server_type_warning,
)
from ansible_collections.hetzner.hcloud.plugins.module_utils.vendor.hcloud.locations import (
BoundLocation,
)
from ansible_collections.hetzner.hcloud.plugins.module_utils.vendor.hcloud.server_types import (
BoundServerType,
)
PAST = datetime.now(timezone.utc) - timedelta(days=14)
FUTURE = datetime.now(timezone.utc) + timedelta(days=14)
LOCATION_FSN = {
"id": 1,
"name": "fsn1",
}
LOCATION_NBG = {
"id": 2,
"name": "nbg1",
}
DEPRECATION_NONE = {
"deprecation": None,
}
DEPRECATION_DEPRECATED = {
"deprecation": {
"announced": PAST.isoformat(),
"unavailable_after": FUTURE.isoformat(),
},
}
DEPRECATION_UNAVAILABLE = {
"deprecation": {
"announced": PAST.isoformat(),
"unavailable_after": PAST.isoformat(),
},
}
@pytest.mark.parametrize(
("server_type", "location", "calls"),
[
(
BoundServerType(
mock.Mock(),
{"name": "cx22", "locations": []},
),
BoundLocation(mock.Mock(), LOCATION_FSN),
[],
),
# - Deprecated (backward compatible)
(
BoundServerType(
mock.Mock(),
{"name": "cx22", **DEPRECATION_DEPRECATED},
),
None,
[
mock.call(
"Server type cx22 is deprecated in all locations and will no longer "
f"be available for order as of {FUTURE.strftime('%Y-%m-%d')}. "
"Existing servers of that type will continue to work as before and "
"no action is required on your part."
)
],
),
# - Unavailable (backward compatible)
(
BoundServerType(
mock.Mock(),
{"name": "cx22", **DEPRECATION_UNAVAILABLE},
),
None,
[
mock.call(
"Server type cx22 is unavailable in all locations and can no longer "
"be ordered. Existing servers of that type will continue to work as "
"before and no action is required on your part."
)
],
),
# - SOME locations are deprecated
# - Given location is NOT deprecated
(
BoundServerType(
mock.Mock(),
{
"name": "cx22",
"locations": [
{**LOCATION_FSN, **DEPRECATION_NONE},
{**LOCATION_NBG, **DEPRECATION_DEPRECATED},
],
},
),
BoundLocation(mock.Mock(), LOCATION_FSN),
[],
),
# - SOME locations are deprecated
# - Given location is deprecated
(
BoundServerType(
mock.Mock(),
{
"name": "cx22",
"locations": [
{**LOCATION_FSN, **DEPRECATION_NONE},
{**LOCATION_NBG, **DEPRECATION_DEPRECATED},
],
},
),
BoundLocation(mock.Mock(), LOCATION_NBG),
[
mock.call(
"Server type cx22 is deprecated in nbg1 and will no longer be available "
f"for order as of {FUTURE.strftime('%Y-%m-%d')}. Existing servers of "
"that type will continue to work as before and no action is required "
"on your part."
)
],
),
# - SOME locations are unavailable
# - Given location is unavailable
(
BoundServerType(
mock.Mock(),
{
"name": "cx22",
"locations": [
{**LOCATION_FSN, **DEPRECATION_NONE},
{**LOCATION_NBG, **DEPRECATION_UNAVAILABLE},
],
},
),
BoundLocation(mock.Mock(), LOCATION_NBG),
[
mock.call(
"Server type cx22 is unavailable in nbg1 and can no longer be ordered. "
"Existing servers of that type will continue to work as before and no "
"action is required on your part."
)
],
),
# - SOME locations are deprecated
# - Location is not given
(
BoundServerType(
mock.Mock(),
{
"name": "cx22",
"locations": [
{**LOCATION_FSN, **DEPRECATION_NONE},
{**LOCATION_NBG, **DEPRECATION_DEPRECATED},
],
},
),
None,
[],
),
# - SOME locations are unavailable
# - Location is not given
(
BoundServerType(
mock.Mock(),
{
"name": "cx22",
"locations": [
{**LOCATION_FSN, **DEPRECATION_NONE},
{**LOCATION_NBG, **DEPRECATION_UNAVAILABLE},
],
},
),
None,
[],
),
# - SOME locations are deprecated
# - SOME locations are unavailable
# - Location is not given
(
BoundServerType(
mock.Mock(),
{
"name": "cx22",
"locations": [
{**LOCATION_FSN, **DEPRECATION_DEPRECATED},
{**LOCATION_NBG, **DEPRECATION_UNAVAILABLE},
],
},
),
None,
[
mock.call(
"Server type cx22 is deprecated in all locations (fsn1,nbg1) and "
"can no longer be ordered in some locations (nbg1). Existing servers"
" of that type will continue to work as before and no action is "
"required on your part."
)
],
),
# - ALL locations are deprecated
# - Location is not given
(
BoundServerType(
mock.Mock(),
{
"name": "cx22",
"locations": [
{**LOCATION_FSN, **DEPRECATION_DEPRECATED},
{**LOCATION_NBG, **DEPRECATION_DEPRECATED},
],
},
),
None,
[
mock.call(
"Server type cx22 is deprecated in all locations (fsn1,nbg1) and "
"will no longer be available for order. Existing servers of that "
"type will continue to work as before and no action is required on "
"your part."
)
],
),
# - ALL locations are unavailable
# - Location is not given
(
BoundServerType(
mock.Mock(),
{
"name": "cx22",
"locations": [
{**LOCATION_FSN, **DEPRECATION_UNAVAILABLE},
{**LOCATION_NBG, **DEPRECATION_UNAVAILABLE},
],
},
),
None,
[
mock.call(
"Server type cx22 is unavailable in all locations (fsn1,nbg1) and "
"can no longer be ordered. Existing servers of that type will "
"continue to work as before and no action is required on your part."
)
],
),
],
)
def test_deprecated_server_type_warning(server_type, location, calls):
m = mock.Mock()
deprecated_server_type_warning(m, server_type, location)
m.warn.assert_has_calls(calls)