Skip to content

Commit a5efb48

Browse files
committed
✨ Add minimal support for firewalls
This change adds support to get firewall by ID or to list firewalls by selector Downstream issue: jenkinsci/hetzner-cloud-plugin#97 Signed-off-by: Richard Kosegi <[email protected]>
1 parent a4563ef commit a5efb48

File tree

6 files changed

+364
-1
lines changed

6 files changed

+364
-1
lines changed

src/main/java/cloud/dnation/hetznerclient/HetznerApi.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,26 @@ Call<GetServersBySelectorResponse> getServersBySelector(@Query("label_selector")
182182
@GET("/v1/networks/{id}")
183183
Call<GetNetworkByIdResponse> getNetworkById(@Path("id") long id);
184184

185+
/**
186+
* Get all firewalls matching given label selector.
187+
*
188+
* @param selector label selector used to match firewalls
189+
* @return list of firewalls
190+
* see <a href="https://docs.hetzner.cloud/reference/cloud#firewalls-list-firewalls">API reference</a>
191+
*/
192+
@GET("/v1/firewalls")
193+
Call<GetFirewallsBySelectorResponse> getFirewallsBySelector(@Query("label_selector") String selector);
194+
195+
/**
196+
* Get single firewall by ID.
197+
*
198+
* @param id firewall ID
199+
* @return firewall detail
200+
* see <a href="https://docs.hetzner.cloud/reference/cloud#firewalls-get-a-firewall">API reference</a>
201+
*/
202+
@GET("/v1/firewalls/{id}")
203+
Call<GetFirewallByIdResponse> getFirewallById(@Path("id") long id);
204+
185205
/**
186206
* Get placement groups, optionally filtered using label expression.
187207
*

src/main/resources/api.yaml

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,16 @@ components:
127127
$ref: "#/components/schemas/NetworkDetail"
128128
type: object
129129

130+
GetFirewallsBySelectorResponse:
131+
allOf:
132+
- $ref: "#/components/schemas/AbstractSearchResponse"
133+
properties:
134+
firewalls:
135+
type: array
136+
items:
137+
$ref: "#/components/schemas/FirewallDetail"
138+
type: object
139+
130140
GetServersBySelectorResponse:
131141
allOf:
132142
- $ref: "#/components/schemas/AbstractSearchResponse"
@@ -205,6 +215,12 @@ components:
205215
$ref: "#/components/schemas/NetworkDetail"
206216
type: object
207217

218+
GetFirewallByIdResponse:
219+
properties:
220+
firewall:
221+
$ref: "#/components/schemas/FirewallDetail"
222+
type: object
223+
208224
GetPlacementGroupByIdResponse:
209225
properties:
210226
placement_group:
@@ -236,7 +252,10 @@ components:
236252
ssh_key:
237253
$ref: "#/components/schemas/SshKeyDetail"
238254
type: object
239-
255+
CreateServerFirewallsRequest:
256+
properties:
257+
firewall:
258+
$ref: "#/components/schemas/Identifier"
240259
CreateServerRequest:
241260
properties:
242261
automount:
@@ -247,6 +266,11 @@ components:
247266
not be used together with location)
248267
example: nbg1-dc3
249268
type: string
269+
firewalls:
270+
description: Firewalls which should be applied on the Server's public network interface at creation time.
271+
type: array
272+
items:
273+
$ref: "#/components/schemas/CreateServerFirewallsRequest"
250274
image:
251275
description: ID or name of the Image the Server is created from
252276
example: ubuntu-20.04
@@ -685,6 +709,58 @@ components:
685709
example: false
686710
type: boolean
687711
type: object
712+
FirewallRule:
713+
properties:
714+
description:
715+
description: Description of the rule.
716+
type: string
717+
direction:
718+
description: Traffic direction in which the rule should be applied to.
719+
type: string
720+
protocol:
721+
description: Network protocol to apply the rule for.
722+
type: string
723+
port:
724+
description: Port or port range to apply the rule for.
725+
type: string
726+
destination_ips:
727+
description: List of permitted IPv4/IPv6 addresses for outgoing traffic.
728+
type: array
729+
items:
730+
type: string
731+
source_ips:
732+
description: List of permitted IPv4/IPv6 addresses for incoming traffic.
733+
type: array
734+
items:
735+
type: string
736+
FirewallAppliedToDetail:
737+
properties:
738+
type:
739+
type: string
740+
FirewallDetail:
741+
allOf:
742+
- $ref: "#/components/schemas/IdentifiableResource"
743+
properties:
744+
created:
745+
description: Point in time when the Resource was created (in
746+
ISO-8601 format)
747+
example: '2016-01-30T23:55:00+00:00'
748+
type: string
749+
name:
750+
type: string
751+
description: Name of the Firewall. Must be unique per Project.
752+
rules:
753+
type: array
754+
items:
755+
$ref: "#/components/schemas/FirewallRule"
756+
applied_to:
757+
type: array
758+
items:
759+
$ref: "#/components/schemas/FirewallAppliedToDetail"
760+
labels:
761+
description: User-defined labels (key-value pairs)
762+
$ref: "#/components/schemas/Labeled"
763+
688764
VolumeDetail:
689765
allOf:
690766
- $ref: "#/components/schemas/IdentifiableResource"

src/test/java/cloud/dnation/hetznerclient/BasicTest.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,4 +102,38 @@ public void testGetPrimaryIpsBySelector() throws IOException {
102102
assertEquals("1.2.3.4", result.getPrimaryIps().get(0).getIp());
103103
assertNull(result.getPrimaryIps().get(0).getAssigneeId());
104104
}
105+
106+
@Test
107+
public void testGetFirewallBySelector() throws IOException {
108+
ws.enqueue(new MockResponse().setBody(resourceAsString("get-firewalls-by-selector.json")));
109+
final Call<GetFirewallsBySelectorResponse> call = api.getFirewallsBySelector("any");
110+
final GetFirewallsBySelectorResponse result = call.execute().body();
111+
assertEquals(3029857349L, (long) result.getFirewalls().get(0).getId());
112+
assertEquals("::/0", result.getFirewalls().get(0).getRules().get(1).getSourceIps().get(1));
113+
assertEquals("in", result.getFirewalls().get(0).getRules().get(1).getDirection());
114+
assertEquals("22", result.getFirewalls().get(0).getRules().get(0).getPort());
115+
}
116+
117+
@Test
118+
public void testGetFirewallById() throws IOException {
119+
ws.enqueue(new MockResponse().setBody(resourceAsString("get-firewall-by-id.json")));
120+
final Call<GetFirewallByIdResponse> call = api.getFirewallById(345676L);
121+
final GetFirewallByIdResponse result = call.execute().body();
122+
assertEquals(345676, (long) result.getFirewall().getId());
123+
assertEquals("::/0", result.getFirewall().getRules().get(1).getSourceIps().get(1));
124+
assertEquals("in", result.getFirewall().getRules().get(1).getDirection());
125+
assertEquals("22", result.getFirewall().getRules().get(0).getPort());
126+
}
127+
128+
129+
@Test
130+
public void testGetFirewallByIdInvalid() throws IOException {
131+
ws.enqueue(new MockResponse()
132+
.setBody(resourceAsString("get-firewall-by-id-invalid.json"))
133+
.setResponseCode(404)
134+
);
135+
Call<GetNetworkByIdResponse> call = api.getNetworkById(11);
136+
Response<GetNetworkByIdResponse> response = call.execute();
137+
assertEquals(404, response.code());
138+
}
105139
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"error": {
3+
"message": "firewall with ID XYZ not found",
4+
"code": "not_found",
5+
"details": null
6+
}
7+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
{
2+
"firewall": {
3+
"id": 345676,
4+
"name": "firewall-worker",
5+
"labels": {},
6+
"created": "2022-11-23T12:36:58+00:00",
7+
"rules": [
8+
{
9+
"direction": "in",
10+
"protocol": "tcp",
11+
"port": "22",
12+
"source_ips": [
13+
"0.0.0.0/0",
14+
"::/0"
15+
],
16+
"destination_ips": [],
17+
"description": null
18+
},
19+
{
20+
"direction": "in",
21+
"protocol": "icmp",
22+
"port": null,
23+
"source_ips": [
24+
"0.0.0.0/0",
25+
"::/0"
26+
],
27+
"destination_ips": [],
28+
"description": null
29+
},
30+
{
31+
"direction": "in",
32+
"protocol": "tcp",
33+
"port": "80",
34+
"source_ips": [
35+
"0.0.0.0/0",
36+
"::/0"
37+
],
38+
"destination_ips": [],
39+
"description": null
40+
},
41+
{
42+
"direction": "in",
43+
"protocol": "tcp",
44+
"port": "9090",
45+
"source_ips": [
46+
"0.0.0.0/0",
47+
"::/0"
48+
],
49+
"destination_ips": [],
50+
"description": null
51+
},
52+
{
53+
"direction": "in",
54+
"protocol": "tcp",
55+
"port": "443",
56+
"source_ips": [
57+
"0.0.0.0/0",
58+
"::/0"
59+
],
60+
"destination_ips": [],
61+
"description": null
62+
},
63+
{
64+
"direction": "in",
65+
"protocol": "tcp",
66+
"port": "any",
67+
"source_ips": [
68+
"10.1.0.0/16"
69+
],
70+
"destination_ips": [],
71+
"description": null
72+
},
73+
{
74+
"direction": "in",
75+
"protocol": "udp",
76+
"port": "any",
77+
"source_ips": [
78+
"10.1.0.0/16"
79+
],
80+
"destination_ips": [],
81+
"description": null
82+
}
83+
],
84+
"applied_to": []
85+
}
86+
}

0 commit comments

Comments
 (0)