Skip to content

Commit ccb5f47

Browse files
authored
Add support for Resources on FirewallClient.create (#134)
Signed-off-by: Lukas Kämmerling <lukas.kaemmerling@hetzner-cloud.de> Signed-off-by: Lukas Kämmerling <lukas.kaemmerling@hetzner-cloud.de>
1 parent 2b1c72e commit ccb5f47

4 files changed

Lines changed: 142 additions & 9 deletions

File tree

hcloud/firewalls/client.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@ def create(self,
238238
name, # type: str
239239
rules=None, # type: Optional[List[FirewallRule]]
240240
labels=None, # type: Optional[str]
241+
resources=None, # type: Optional[List[FirewallResource]]
241242
):
242243
# type: (...) -> CreateFirewallResponse
243244
"""Creates a new Firewall.
@@ -247,6 +248,7 @@ def create(self,
247248
:param rules: List[:class:`FirewallRule <hcloud.firewalls.domain.FirewallRule>`] (optional)
248249
:param labels: Dict[str, str] (optional)
249250
User-defined labels (key-value pairs)
251+
:param resources: List[:class:`FirewallResource <hcloud.firewalls.domain.FirewallResource>`] (optional)
250252
:return: :class:`CreateFirewallResponse <hcloud.firewalls.domain.CreateFirewallResponse>`
251253
"""
252254

@@ -260,16 +262,19 @@ def create(self,
260262
data.update({"rules": []})
261263
for rule in rules:
262264
data['rules'].append(rule.to_payload())
263-
265+
if resources is not None:
266+
data.update({"apply_to": []})
267+
for resource in resources:
268+
data['apply_to'].append(resource.to_payload())
264269
response = self._client.request(url="/firewalls", json=data, method="POST")
265270

266-
action = None
267-
if response.get('action') is not None:
268-
action = BoundAction(self._client.actions, response['action'])
271+
actions = []
272+
if response.get('actions') is not None:
273+
actions = [BoundAction(self._client.actions, _) for _ in response['actions']]
269274

270275
result = CreateFirewallResponse(
271276
firewall=BoundFirewall(self, response['firewall']),
272-
action=action
277+
actions=actions
273278
)
274279
return result
275280

hcloud/firewalls/domain.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -150,18 +150,18 @@ class CreateFirewallResponse(BaseDomain):
150150
151151
:param firewall: :class:`BoundFirewall <hcloud.firewalls.client.BoundFirewall>`
152152
The Firewall which was created
153-
:param action: :class:`BoundAction <hcloud.actions.client.BoundAction>`
153+
:param actions: List[:class:`BoundAction <hcloud.actions.client.BoundAction>`]
154154
The Action which shows the progress of the Firewall Creation
155155
"""
156156
__slots__ = (
157157
"firewall",
158-
"action"
158+
"actions"
159159
)
160160

161161
def __init__(
162162
self,
163163
firewall, # type: BoundFirewall
164-
action, # type: BoundAction
164+
actions, # type: BoundAction
165165
):
166166
self.firewall = firewall
167-
self.action = action
167+
self.actions = actions

tests/unit/firewalls/conftest.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,92 @@
11
import pytest
22

33

4+
@pytest.fixture()
5+
def response_create_firewall():
6+
return {
7+
"firewall": {
8+
"id": 38,
9+
"name": "Corporate Intranet Protection",
10+
"labels": {},
11+
"created": "2016-01-30T23:50:00+00:00",
12+
"rules": [
13+
{
14+
"direction": "in",
15+
"source_ips": [
16+
"28.239.13.1/32",
17+
"28.239.14.0/24",
18+
"ff21:1eac:9a3b:ee58:5ca:990c:8bc9:c03b/128"
19+
],
20+
"destination_ips": [],
21+
"protocol": "tcp",
22+
"port": "80"
23+
},
24+
{
25+
"direction": "out",
26+
"source_ips": [],
27+
"destination_ips": [
28+
"28.239.13.1/32",
29+
"28.239.14.0/24",
30+
"ff21:1eac:9a3b:ee58:5ca:990c:8bc9:c03b/128"
31+
],
32+
"protocol": "tcp",
33+
"port": "80"
34+
}
35+
],
36+
"applied_to": [
37+
{
38+
"server": {
39+
"id": 42
40+
},
41+
"type": "server"
42+
}
43+
]
44+
},
45+
"actions": [
46+
{
47+
"command": "set_firewall_rules",
48+
"error": {
49+
"code": "action_failed",
50+
"message": "Action failed"
51+
},
52+
"finished": "2016-01-30T23:56:00+00:00",
53+
"id": 13,
54+
"progress": 100,
55+
"resources": [
56+
{
57+
"id": 38,
58+
"type": "firewall"
59+
}
60+
],
61+
"started": "2016-01-30T23:55:00+00:00",
62+
"status": "success"
63+
},
64+
{
65+
"command": "apply_firewall",
66+
"error": {
67+
"code": "action_failed",
68+
"message": "Action failed"
69+
},
70+
"finished": "2016-01-30T23:56:00+00:00",
71+
"id": 14,
72+
"progress": 100,
73+
"resources": [
74+
{
75+
"id": 42,
76+
"type": "server"
77+
},
78+
{
79+
"id": 38,
80+
"type": "firewall"
81+
}
82+
],
83+
"started": "2016-01-30T23:55:00+00:00",
84+
"status": "success"
85+
}
86+
]
87+
}
88+
89+
490
@pytest.fixture()
591
def firewall_response():
692
return {

tests/unit/firewalls/test_client.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,48 @@ def test_get_actions_list(self, firewalls_client, firewall, response_get_actions
270270
assert actions[0].id == 13
271271
assert actions[0].command == "set_firewall_rules"
272272

273+
def test_create(self, firewalls_client, response_create_firewall):
274+
firewalls_client._client.request.return_value = response_create_firewall
275+
response = firewalls_client.create(
276+
"Corporate Intranet Protection",
277+
rules=[FirewallRule(direction=FirewallRule.DIRECTION_IN, protocol=FirewallRule.PROTOCOL_ICMP,
278+
source_ips=["0.0.0.0/0"])],
279+
resources=[FirewallResource(type=FirewallResource.TYPE_SERVER, server=Server(id=4711))]
280+
)
281+
firewalls_client._client.request.assert_called_with(
282+
url="/firewalls",
283+
method="POST",
284+
json={
285+
'name': "Corporate Intranet Protection",
286+
'rules': [
287+
{
288+
"direction": "in",
289+
"protocol": "icmp",
290+
"source_ips": [
291+
"0.0.0.0/0"
292+
]
293+
}
294+
],
295+
"apply_to": [
296+
{
297+
"type": "server",
298+
"server": {
299+
"id": 4711
300+
}
301+
}
302+
],
303+
}
304+
)
305+
306+
bound_firewall = response.firewall
307+
actions = response.actions
308+
309+
assert bound_firewall._client is firewalls_client
310+
assert bound_firewall.id == 38
311+
assert bound_firewall.name == "Corporate Intranet Protection"
312+
313+
assert len(actions) == 2
314+
273315
@pytest.mark.parametrize("firewall", [Firewall(id=38), BoundFirewall(mock.MagicMock(), dict(id=38))])
274316
def test_update(self, firewalls_client, firewall, response_update_firewall):
275317
firewalls_client._client.request.return_value = response_update_firewall

0 commit comments

Comments
 (0)