From 82b5e56f73961bac0287dc3e4f8b4fdfed09c907 Mon Sep 17 00:00:00 2001 From: Felix Krasemann Date: Thu, 11 Aug 2022 21:29:19 +0200 Subject: [PATCH 1/8] added firewall support --- .../hetznercloud/HetznerCloudAPI.java | 196 +++++++++++++++++- .../objects/general/FWApplicationTarget.java | 38 ++++ .../objects/general/FWLabelSelector.java | 14 ++ .../objects/general/FWServerRef.java | 14 ++ .../objects/general/FWTargetedResource.java | 25 +++ .../objects/general/Firewall.java | 29 +++ .../objects/general/FirewallRule.java | 34 +++ .../request/CreateFirewallRequest.java | 40 ++++ .../request/UpdateFirewallRequest.java | 15 ++ .../objects/response/FirewallResponse.java | 15 ++ .../objects/response/FirewallsResponse.java | 19 ++ 11 files changed, 436 insertions(+), 3 deletions(-) create mode 100644 src/main/java/me/tomsdevsn/hetznercloud/objects/general/FWApplicationTarget.java create mode 100644 src/main/java/me/tomsdevsn/hetznercloud/objects/general/FWLabelSelector.java create mode 100644 src/main/java/me/tomsdevsn/hetznercloud/objects/general/FWServerRef.java create mode 100644 src/main/java/me/tomsdevsn/hetznercloud/objects/general/FWTargetedResource.java create mode 100644 src/main/java/me/tomsdevsn/hetznercloud/objects/general/Firewall.java create mode 100644 src/main/java/me/tomsdevsn/hetznercloud/objects/general/FirewallRule.java create mode 100644 src/main/java/me/tomsdevsn/hetznercloud/objects/request/CreateFirewallRequest.java create mode 100644 src/main/java/me/tomsdevsn/hetznercloud/objects/request/UpdateFirewallRequest.java create mode 100644 src/main/java/me/tomsdevsn/hetznercloud/objects/response/FirewallResponse.java create mode 100644 src/main/java/me/tomsdevsn/hetznercloud/objects/response/FirewallsResponse.java diff --git a/src/main/java/me/tomsdevsn/hetznercloud/HetznerCloudAPI.java b/src/main/java/me/tomsdevsn/hetznercloud/HetznerCloudAPI.java index 6296488b..8521a831 100644 --- a/src/main/java/me/tomsdevsn/hetznercloud/HetznerCloudAPI.java +++ b/src/main/java/me/tomsdevsn/hetznercloud/HetznerCloudAPI.java @@ -3,6 +3,9 @@ import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import me.tomsdevsn.hetznercloud.exception.InvalidParametersException; +import me.tomsdevsn.hetznercloud.objects.general.FWApplicationTarget; +import me.tomsdevsn.hetznercloud.objects.general.Firewall; +import me.tomsdevsn.hetznercloud.objects.general.FirewallRule; import me.tomsdevsn.hetznercloud.objects.general.PlacementGroupType; import me.tomsdevsn.hetznercloud.objects.pagination.PaginationParameters; import me.tomsdevsn.hetznercloud.objects.request.*; @@ -15,9 +18,7 @@ import java.io.IOException; import java.text.DateFormat; import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Optional; -import java.util.TimeZone; +import java.util.*; import java.util.concurrent.TimeUnit; public class HetznerCloudAPI { @@ -689,6 +690,195 @@ public DatacentersResponse getDatacenter(String name) { DatacentersResponse.class); } + /** + * Returns 25 Firewall objects. + * + * @return a FirewallsResponse containing all Firewalls of the requested page and paging metadata + * @see #getFirewalls(PaginationParameters) + */ + public FirewallsResponse getFirewalls() { + return getFirewalls(new PaginationParameters(null, null)); + } + + /** + * Returns all Firewall objects. + * + * @param paginationParameters + * @return a FirewallsResponse containing all Firewalls of the requested page and paging metadata + */ + public FirewallsResponse getFirewalls(PaginationParameters paginationParameters) { + return get( + UrlBuilder.from(API_URL + "/firewalls") + .queryParamIfPresent("page", Optional.ofNullable(paginationParameters.page)) + .queryParamIfPresent("per_page", Optional.ofNullable(paginationParameters.perPage)) + .toUri(), + FirewallsResponse.class); + } + + /** + * Creates a new Firewall. + * + * @param createFirewallRequest the config of the Firewall you want to create + * @return a FirewallResponse containing the created Firewall and taken Actions + */ + public FirewallResponse createFirewall(CreateFirewallRequest createFirewallRequest) { + return post( + API_URL + "/firewalls", + createFirewallRequest, + FirewallResponse.class); + } + + /** + * Deletes a Firewall. + * + * @param id + * @throws IllegalStateException if the Firewall is still attached to resources + */ + public void deleteFirewall(long id) throws IllegalStateException { + final Firewall firewall = getFirewallResponse(id).getFirewall(); + if (firewall.getAppliedTo().size() != 0) + throw new IllegalStateException("FW is attached to resources, this prevents deletion"); + + delete( + API_URL + "/firewalls/" + id, + String.class); + } + + /** + * Gets a specific Firewall. + * + * @param id + * @return the searched Firewall + */ + public Firewall getFirewall(long id) { + return getFirewallResponse(id) + .getFirewall(); + } + + /** + * Gets a specific Firewall. + * + * @param id + * @return the FirewallResponse containing the searched Firewall + */ + private FirewallResponse getFirewallResponse(long id) { + return get( + API_URL + "/firewalls/" + id, + FirewallResponse.class); + } + + /** + * Updates the Firewall. This replaces the current labels with the given + * + * @param id + * @param labels new labels of the firewall + * @param name new name of the firewall + * @return the FirewallResponse of the request, containing the new Firewall and Metadata + */ + public FirewallResponse updateFirewall(long id, Map labels, String name) { + return put( + API_URL + "/firewalls/" + id, + UpdateFirewallRequest.builder() + .labels(labels) + .name(name) + .build(), + FirewallResponse.class); + } + + /** + * Returns all Action objects for a Firewall. + * + * @param id + * @return an ActionsResponse with the executed actions + */ + public ActionsResponse getActionsOfFirewall(long id) { + return get( + String.format("%s/firewalls/%s/actions", API_URL, id), + ActionsResponse.class); + } + + /** + * Returns a specific Action for a Firewall. + * + * @param firewallId + * @param actionId + * @return an ActionsResponse with the executed actions + */ + public ActionResponse getActionOfFirewall(long firewallId, long actionId) { + return get( + String.format("%s/firewalls/%s/actions/%s", API_URL, firewallId, actionId), + ActionResponse.class); + } + + /** + * Applies one Firewall to multiple resources. + * + * @param id of the firewall you want to add to resources + * @param applicationTargets you want to add + * @return an ActionsResponse with the executed actions + * @throws RuntimeException if the removalTargets don't pass {@link FWApplicationTarget#isValidOrThrow()} + */ + public ActionsResponse applyFirewallToResources(long id, List applicationTargets) + throws RuntimeException { + for (var target: applicationTargets) + target.isValidOrThrow(); + + return post( + API_URL + "/firewalls/" + id + "/actions/apply_to_resources", + Map.of("apply_to", applicationTargets), + ActionsResponse.class); + } + + /** + * Removes one Firewall from multiple resources. + * + * @param id of the firewall you want to remove resources from + * @param removalTargets you want to remove + * @return an ActionsResponse with the executed actions + * @throws RuntimeException if the removalTargets don't pass {@link FWApplicationTarget#isValidOrThrow()} + */ + public ActionsResponse removeFirewallFromResources(long id, List removalTargets) + throws RuntimeException { + for (var target: removalTargets) + target.isValidOrThrow(); + + return post( + API_URL + "/firewalls/" + id + "/actions/remove_from_resources", + Map.of("remove_from", removalTargets), + ActionsResponse.class); + } + + /** + * Removes all rules of a Firewall. + * + * @param id the firewall you want to remove the rules from + * @return an ActionsResponse with the executed actions + * @see #setFirewallRules(long, List) + */ + public ActionsResponse removeAllRulesFromFirewall(long id) { + return setFirewallRules(id, Collections.EMPTY_LIST); + } + + /** + * Sets the rules of a Firewall. All existing rules will be overwritten. + * If the firewallRules are empty, all rules are deleted. + * + * @param id of the Firewall you want to set the Rules on. + * @param firewallRules you want to set. + * @return an ActionsResponse with the executed actions + * @throws IllegalArgumentException if the size of firewallRules is greater than 50 (size > 50) + */ + public ActionsResponse setFirewallRules(long id, List firewallRules) + throws IllegalArgumentException { + if (firewallRules.size() > 50) + throw new IllegalArgumentException("You can't add more than 50 rules"); + + return post( + API_URL + "/firewalls/" + id + "/actions/set_rules", + Map.of("rules", firewallRules), + ActionsResponse.class); + } + /** * Get all prices from the products * diff --git a/src/main/java/me/tomsdevsn/hetznercloud/objects/general/FWApplicationTarget.java b/src/main/java/me/tomsdevsn/hetznercloud/objects/general/FWApplicationTarget.java new file mode 100644 index 00000000..60ec2c68 --- /dev/null +++ b/src/main/java/me/tomsdevsn/hetznercloud/objects/general/FWApplicationTarget.java @@ -0,0 +1,38 @@ +package me.tomsdevsn.hetznercloud.objects.general; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@JsonInclude(JsonInclude.Include.NON_NULL) +public class FWApplicationTarget { + + @JsonProperty("label_selector") + private FWLabelSelector labelSelector; + private FWServerRef server; + private TargetType type; + + public void isValidOrThrow() throws RuntimeException { + final boolean isServerType = (getType() == TargetType.server); + final boolean isServerNull = (getServer() == null); + + final boolean isLabelSelectorType = (getType() == TargetType.label_selector); + final boolean isLabelSelectorNull = (getLabelSelector() == null); + + if (isServerType && (isServerNull || !isLabelSelectorNull)) + throw new RuntimeException( + "TargetType is \"server\", either \"server\" is null or \"labelSelector\" is not"); + + if (isLabelSelectorType && (isLabelSelectorNull || !isServerNull)) + throw new RuntimeException( + "TargetType is \"label_selector\", either \"labelSelector\" is null or \"server\" is not"); + } + +} diff --git a/src/main/java/me/tomsdevsn/hetznercloud/objects/general/FWLabelSelector.java b/src/main/java/me/tomsdevsn/hetznercloud/objects/general/FWLabelSelector.java new file mode 100644 index 00000000..98ff4c1a --- /dev/null +++ b/src/main/java/me/tomsdevsn/hetznercloud/objects/general/FWLabelSelector.java @@ -0,0 +1,14 @@ +package me.tomsdevsn.hetznercloud.objects.general; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class FWLabelSelector { + + private String selector; + +} diff --git a/src/main/java/me/tomsdevsn/hetznercloud/objects/general/FWServerRef.java b/src/main/java/me/tomsdevsn/hetznercloud/objects/general/FWServerRef.java new file mode 100644 index 00000000..4eb9b071 --- /dev/null +++ b/src/main/java/me/tomsdevsn/hetznercloud/objects/general/FWServerRef.java @@ -0,0 +1,14 @@ +package me.tomsdevsn.hetznercloud.objects.general; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class FWServerRef { + + private Long id; + +} diff --git a/src/main/java/me/tomsdevsn/hetznercloud/objects/general/FWTargetedResource.java b/src/main/java/me/tomsdevsn/hetznercloud/objects/general/FWTargetedResource.java new file mode 100644 index 00000000..f123cf52 --- /dev/null +++ b/src/main/java/me/tomsdevsn/hetznercloud/objects/general/FWTargetedResource.java @@ -0,0 +1,25 @@ +package me.tomsdevsn.hetznercloud.objects.general; + +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +public class FWTargetedResource { + + private FWServerRef server; + private TargetType type = TargetType.server; + + public FWTargetedResource(FWServerRef serverRef) { + this.server = serverRef; + } + + public FWTargetedResource(Long serverId) { + this.server = new FWServerRef(serverId); + } + + public FWTargetedResource(Server server) { + this.server = new FWServerRef(server.getId()); + } + +} diff --git a/src/main/java/me/tomsdevsn/hetznercloud/objects/general/Firewall.java b/src/main/java/me/tomsdevsn/hetznercloud/objects/general/Firewall.java new file mode 100644 index 00000000..a3833b3c --- /dev/null +++ b/src/main/java/me/tomsdevsn/hetznercloud/objects/general/Firewall.java @@ -0,0 +1,29 @@ +package me.tomsdevsn.hetznercloud.objects.general; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import me.tomsdevsn.hetznercloud.deserialize.DateDeserializer; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class Firewall { + + @JsonProperty("applied_to") + private List appliedTo; + @JsonDeserialize(contentUsing = DateDeserializer.class) + private Date created; + private Long id; + private Map labels; + private String name; + @JsonProperty("rules") + private List firewallRules; + +} diff --git a/src/main/java/me/tomsdevsn/hetznercloud/objects/general/FirewallRule.java b/src/main/java/me/tomsdevsn/hetznercloud/objects/general/FirewallRule.java new file mode 100644 index 00000000..a922e923 --- /dev/null +++ b/src/main/java/me/tomsdevsn/hetznercloud/objects/general/FirewallRule.java @@ -0,0 +1,34 @@ +package me.tomsdevsn.hetznercloud.objects.general; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.*; + +import java.util.List; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class FirewallRule { + + private String description; + @Singular("desinationIP") + @JsonProperty("destination_ips") + private List destinationIPs; + private Direction direction; + @Builder.Default + private String port = ""; + private Protocol protocol; + @Singular("sourceIP") + @JsonProperty("source_ips") + private List sourceIPs; + + public enum Direction { + in, out + } + + public enum Protocol { + tcp, udp, icmp, esp, gre + } + +} diff --git a/src/main/java/me/tomsdevsn/hetznercloud/objects/request/CreateFirewallRequest.java b/src/main/java/me/tomsdevsn/hetznercloud/objects/request/CreateFirewallRequest.java new file mode 100644 index 00000000..c83ed143 --- /dev/null +++ b/src/main/java/me/tomsdevsn/hetznercloud/objects/request/CreateFirewallRequest.java @@ -0,0 +1,40 @@ +package me.tomsdevsn.hetznercloud.objects.request; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Builder; +import lombok.Data; +import lombok.Singular; +import me.tomsdevsn.hetznercloud.objects.general.FWApplicationTarget; +import me.tomsdevsn.hetznercloud.objects.general.FirewallRule; + +import java.util.List; +import java.util.Map; + +@Data +@Builder +public class CreateFirewallRequest { + + @Singular("applicationTarget") + @JsonProperty("apply_to") + private List applyTo; + @Singular("label") + private Map labels; + private String name; + @Singular("firewallRule") + @JsonProperty("rules") + private List firewallRules; + + public CreateFirewallRequest(List applyTo, + Map labels, + String name, + List firewallRules) { + for (var target: applyTo) + target.isValidOrThrow(); + + this.applyTo = applyTo; + this.labels = labels; + this.name = name; + this.firewallRules = firewallRules; + } + +} diff --git a/src/main/java/me/tomsdevsn/hetznercloud/objects/request/UpdateFirewallRequest.java b/src/main/java/me/tomsdevsn/hetznercloud/objects/request/UpdateFirewallRequest.java new file mode 100644 index 00000000..c1a29bc3 --- /dev/null +++ b/src/main/java/me/tomsdevsn/hetznercloud/objects/request/UpdateFirewallRequest.java @@ -0,0 +1,15 @@ +package me.tomsdevsn.hetznercloud.objects.request; + +import lombok.Builder; +import lombok.Data; + +import java.util.Map; + +@Data +@Builder +public class UpdateFirewallRequest { + + private Map labels; + private String name; + +} diff --git a/src/main/java/me/tomsdevsn/hetznercloud/objects/response/FirewallResponse.java b/src/main/java/me/tomsdevsn/hetznercloud/objects/response/FirewallResponse.java new file mode 100644 index 00000000..9776b5dd --- /dev/null +++ b/src/main/java/me/tomsdevsn/hetznercloud/objects/response/FirewallResponse.java @@ -0,0 +1,15 @@ +package me.tomsdevsn.hetznercloud.objects.response; + +import lombok.Data; +import me.tomsdevsn.hetznercloud.objects.general.Action; +import me.tomsdevsn.hetznercloud.objects.general.Firewall; + +import java.util.List; + +@Data +public class FirewallResponse { + + private List actions; + private Firewall firewall; + +} diff --git a/src/main/java/me/tomsdevsn/hetznercloud/objects/response/FirewallsResponse.java b/src/main/java/me/tomsdevsn/hetznercloud/objects/response/FirewallsResponse.java new file mode 100644 index 00000000..b1c39a48 --- /dev/null +++ b/src/main/java/me/tomsdevsn/hetznercloud/objects/response/FirewallsResponse.java @@ -0,0 +1,19 @@ +package me.tomsdevsn.hetznercloud.objects.response; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import me.tomsdevsn.hetznercloud.objects.general.Firewall; +import me.tomsdevsn.hetznercloud.objects.general.Meta; + +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class FirewallsResponse { + + private List firewalls; + private Meta meta; + +} From a894cd308bbb4f9bc572892dc76e1e0286cb1e2b Mon Sep 17 00:00:00 2001 From: Felix Krasemann Date: Thu, 11 Aug 2022 21:32:20 +0200 Subject: [PATCH 2/8] added test stubs --- .../hetznercloud/HetznerCloudAPITest.java | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/src/test/java/me/tomsdevsn/hetznercloud/HetznerCloudAPITest.java b/src/test/java/me/tomsdevsn/hetznercloud/HetznerCloudAPITest.java index 396dc0e4..f6e40e3a 100644 --- a/src/test/java/me/tomsdevsn/hetznercloud/HetznerCloudAPITest.java +++ b/src/test/java/me/tomsdevsn/hetznercloud/HetznerCloudAPITest.java @@ -28,6 +28,8 @@ public class HetznerCloudAPITest { private final HetznerCloudAPI hetznerCloudAPI = new HetznerCloudAPI(System.getenv("HCLOUD_TOKEN")); + private Long CREATED_FW_ID; + @AfterAll public void cleanUpAfter() { cleanup(); @@ -270,6 +272,99 @@ void getDatacenter() { void getDatacenters() { } + @Test + void getFirewalls() { + final var firewalls = hetznerCloudAPI.getFirewalls(); + } + + @Test + void createFirewall() { + final var firewallRequest = CreateFirewallRequest.builder() + .applicationTarget(FWApplicationTarget.builder() + .labelSelector(new FWLabelSelector("env==dev")) + .type(TargetType.label_selector) + .build()) + //.applicationTarget(FWApplicationTarget.builder() + // .server(new FWServerRef(12345678L)) + // .type(TargetType.server) + // .build()) + .label("test", "firewall") + .name("test-firewall") + .firewallRule(FirewallRule.builder() + .description("test-fw-rule-http") + .desinationIP("0.0.0.0/0") + .port("80") + .direction(FirewallRule.Direction.out) + .protocol(FirewallRule.Protocol.tcp) + .build()) + .firewallRule(FirewallRule.builder() + .description("test-fw-rule-https") + .desinationIP("0.0.0.0/0") + .port("443") + .direction(FirewallRule.Direction.out) + .protocol(FirewallRule.Protocol.tcp) + .build()) + .firewallRule(FirewallRule.builder() + .description("test-fw-rule-icmp") + .desinationIP("0.0.0.0/0") + .direction(FirewallRule.Direction.out) + .protocol(FirewallRule.Protocol.icmp) + .build()) + .build(); + final var response = hetznerCloudAPI.createFirewall(firewallRequest); + CREATED_FW_ID = response.getFirewall().getId(); + } + + @Test + void deleteFirewall() { + final List firewalls = hetznerCloudAPI.getFirewalls().getFirewalls(); + assertThat(firewalls).hasSizeGreaterThanOrEqualTo(1); + + hetznerCloudAPI.deleteFirewall(CREATED_FW_ID); + assertThat(hetznerCloudAPI.getFirewalls().getFirewalls()) + .hasSize(firewalls.size() - 1); + } + + @Test + void getFirewall() { + + } + + @Test + void updateFirewall() { + + } + + @Test + void getActionsOfFirewall() { + + } + + @Test + void getActionOfFirewall() { + + } + + @Test + void applyFirewallToResources() { + + } + + @Test + void removeFirewallFromResources() { + + } + + @Test + void removeAllRulesFromFirewall() { + + } + + @Test + void setFirewallRules() { + + } + @Test void getPricing() { } From e8f2ff0557de088639e1949db57b7fd4000d933b Mon Sep 17 00:00:00 2001 From: Felix Krasemann Date: Fri, 12 Aug 2022 15:07:45 +0200 Subject: [PATCH 3/8] removed manual usage check as it's handled via the API --- .../java/me/tomsdevsn/hetznercloud/HetznerCloudAPI.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/main/java/me/tomsdevsn/hetznercloud/HetznerCloudAPI.java b/src/main/java/me/tomsdevsn/hetznercloud/HetznerCloudAPI.java index 8521a831..dc6c4980 100644 --- a/src/main/java/me/tomsdevsn/hetznercloud/HetznerCloudAPI.java +++ b/src/main/java/me/tomsdevsn/hetznercloud/HetznerCloudAPI.java @@ -732,13 +732,8 @@ public FirewallResponse createFirewall(CreateFirewallRequest createFirewallReque * Deletes a Firewall. * * @param id - * @throws IllegalStateException if the Firewall is still attached to resources */ - public void deleteFirewall(long id) throws IllegalStateException { - final Firewall firewall = getFirewallResponse(id).getFirewall(); - if (firewall.getAppliedTo().size() != 0) - throw new IllegalStateException("FW is attached to resources, this prevents deletion"); - + public void deleteFirewall(long id) { delete( API_URL + "/firewalls/" + id, String.class); From 2ab7635c22b05b3569d2bce6aac8ef6a0b1c072f Mon Sep 17 00:00:00 2001 From: Felix Krasemann Date: Fri, 12 Aug 2022 15:13:37 +0200 Subject: [PATCH 4/8] addressed consistency issue --- .../me/tomsdevsn/hetznercloud/HetznerCloudAPI.java | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/main/java/me/tomsdevsn/hetznercloud/HetznerCloudAPI.java b/src/main/java/me/tomsdevsn/hetznercloud/HetznerCloudAPI.java index dc6c4980..6b62cdc9 100644 --- a/src/main/java/me/tomsdevsn/hetznercloud/HetznerCloudAPI.java +++ b/src/main/java/me/tomsdevsn/hetznercloud/HetznerCloudAPI.java @@ -739,24 +739,13 @@ public void deleteFirewall(long id) { String.class); } - /** - * Gets a specific Firewall. - * - * @param id - * @return the searched Firewall - */ - public Firewall getFirewall(long id) { - return getFirewallResponse(id) - .getFirewall(); - } - /** * Gets a specific Firewall. * * @param id * @return the FirewallResponse containing the searched Firewall */ - private FirewallResponse getFirewallResponse(long id) { + private FirewallResponse getFirewall(long id) { return get( API_URL + "/firewalls/" + id, FirewallResponse.class); From 542df0f5e9a516dab7773b11ce621bcab1553a3f Mon Sep 17 00:00:00 2001 From: Felix Krasemann Date: Fri, 12 Aug 2022 15:15:52 +0200 Subject: [PATCH 5/8] addressed 2. consistency issue --- .../me/tomsdevsn/hetznercloud/HetznerCloudAPI.java | 11 +++-------- .../objects/request/UpdateFirewallRequest.java | 4 ++++ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/main/java/me/tomsdevsn/hetznercloud/HetznerCloudAPI.java b/src/main/java/me/tomsdevsn/hetznercloud/HetznerCloudAPI.java index 6b62cdc9..4b04f22b 100644 --- a/src/main/java/me/tomsdevsn/hetznercloud/HetznerCloudAPI.java +++ b/src/main/java/me/tomsdevsn/hetznercloud/HetznerCloudAPI.java @@ -4,7 +4,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import me.tomsdevsn.hetznercloud.exception.InvalidParametersException; import me.tomsdevsn.hetznercloud.objects.general.FWApplicationTarget; -import me.tomsdevsn.hetznercloud.objects.general.Firewall; import me.tomsdevsn.hetznercloud.objects.general.FirewallRule; import me.tomsdevsn.hetznercloud.objects.general.PlacementGroupType; import me.tomsdevsn.hetznercloud.objects.pagination.PaginationParameters; @@ -755,17 +754,13 @@ private FirewallResponse getFirewall(long id) { * Updates the Firewall. This replaces the current labels with the given * * @param id - * @param labels new labels of the firewall - * @param name new name of the firewall + * @param updateFirewallRequest the changes you want to perform * @return the FirewallResponse of the request, containing the new Firewall and Metadata */ - public FirewallResponse updateFirewall(long id, Map labels, String name) { + public FirewallResponse updateFirewall(long id, UpdateFirewallRequest updateFirewallRequest) { return put( API_URL + "/firewalls/" + id, - UpdateFirewallRequest.builder() - .labels(labels) - .name(name) - .build(), + updateFirewallRequest, FirewallResponse.class); } diff --git a/src/main/java/me/tomsdevsn/hetznercloud/objects/request/UpdateFirewallRequest.java b/src/main/java/me/tomsdevsn/hetznercloud/objects/request/UpdateFirewallRequest.java index c1a29bc3..30c0d163 100644 --- a/src/main/java/me/tomsdevsn/hetznercloud/objects/request/UpdateFirewallRequest.java +++ b/src/main/java/me/tomsdevsn/hetznercloud/objects/request/UpdateFirewallRequest.java @@ -1,14 +1,18 @@ package me.tomsdevsn.hetznercloud.objects.request; +import com.fasterxml.jackson.annotation.JsonInclude; import lombok.Builder; import lombok.Data; +import lombok.Singular; import java.util.Map; @Data @Builder +@JsonInclude(JsonInclude.Include.NON_NULL) public class UpdateFirewallRequest { + @Singular private Map labels; private String name; From 21e7e38a469b2b031c9dc2fea4517020b048db93 Mon Sep 17 00:00:00 2001 From: Felix Krasemann Date: Fri, 12 Aug 2022 15:16:41 +0200 Subject: [PATCH 6/8] removed validation checks --- .../hetznercloud/HetznerCloudAPI.java | 20 +++---------------- .../objects/general/FWApplicationTarget.java | 16 --------------- 2 files changed, 3 insertions(+), 33 deletions(-) diff --git a/src/main/java/me/tomsdevsn/hetznercloud/HetznerCloudAPI.java b/src/main/java/me/tomsdevsn/hetznercloud/HetznerCloudAPI.java index 4b04f22b..76525fb5 100644 --- a/src/main/java/me/tomsdevsn/hetznercloud/HetznerCloudAPI.java +++ b/src/main/java/me/tomsdevsn/hetznercloud/HetznerCloudAPI.java @@ -795,13 +795,8 @@ public ActionResponse getActionOfFirewall(long firewallId, long actionId) { * @param id of the firewall you want to add to resources * @param applicationTargets you want to add * @return an ActionsResponse with the executed actions - * @throws RuntimeException if the removalTargets don't pass {@link FWApplicationTarget#isValidOrThrow()} */ - public ActionsResponse applyFirewallToResources(long id, List applicationTargets) - throws RuntimeException { - for (var target: applicationTargets) - target.isValidOrThrow(); - + public ActionsResponse applyFirewallToResources(long id, List applicationTargets) { return post( API_URL + "/firewalls/" + id + "/actions/apply_to_resources", Map.of("apply_to", applicationTargets), @@ -814,13 +809,8 @@ public ActionsResponse applyFirewallToResources(long id, List removalTargets) - throws RuntimeException { - for (var target: removalTargets) - target.isValidOrThrow(); - + public ActionsResponse removeFirewallFromResources(long id, List removalTargets) { return post( API_URL + "/firewalls/" + id + "/actions/remove_from_resources", Map.of("remove_from", removalTargets), @@ -847,11 +837,7 @@ public ActionsResponse removeAllRulesFromFirewall(long id) { * @return an ActionsResponse with the executed actions * @throws IllegalArgumentException if the size of firewallRules is greater than 50 (size > 50) */ - public ActionsResponse setFirewallRules(long id, List firewallRules) - throws IllegalArgumentException { - if (firewallRules.size() > 50) - throw new IllegalArgumentException("You can't add more than 50 rules"); - + public ActionsResponse setFirewallRules(long id, List firewallRules) { return post( API_URL + "/firewalls/" + id + "/actions/set_rules", Map.of("rules", firewallRules), diff --git a/src/main/java/me/tomsdevsn/hetznercloud/objects/general/FWApplicationTarget.java b/src/main/java/me/tomsdevsn/hetznercloud/objects/general/FWApplicationTarget.java index 60ec2c68..642d2242 100644 --- a/src/main/java/me/tomsdevsn/hetznercloud/objects/general/FWApplicationTarget.java +++ b/src/main/java/me/tomsdevsn/hetznercloud/objects/general/FWApplicationTarget.java @@ -19,20 +19,4 @@ public class FWApplicationTarget { private FWServerRef server; private TargetType type; - public void isValidOrThrow() throws RuntimeException { - final boolean isServerType = (getType() == TargetType.server); - final boolean isServerNull = (getServer() == null); - - final boolean isLabelSelectorType = (getType() == TargetType.label_selector); - final boolean isLabelSelectorNull = (getLabelSelector() == null); - - if (isServerType && (isServerNull || !isLabelSelectorNull)) - throw new RuntimeException( - "TargetType is \"server\", either \"server\" is null or \"labelSelector\" is not"); - - if (isLabelSelectorType && (isLabelSelectorNull || !isServerNull)) - throw new RuntimeException( - "TargetType is \"label_selector\", either \"labelSelector\" is null or \"server\" is not"); - } - } From e68d75aeb6bff937567181b5c5d48992bc3a053f Mon Sep 17 00:00:00 2001 From: Felix Krasemann Date: Fri, 12 Aug 2022 15:23:43 +0200 Subject: [PATCH 7/8] missed validation step --- .../objects/request/CreateFirewallRequest.java | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/main/java/me/tomsdevsn/hetznercloud/objects/request/CreateFirewallRequest.java b/src/main/java/me/tomsdevsn/hetznercloud/objects/request/CreateFirewallRequest.java index c83ed143..0a0f20e8 100644 --- a/src/main/java/me/tomsdevsn/hetznercloud/objects/request/CreateFirewallRequest.java +++ b/src/main/java/me/tomsdevsn/hetznercloud/objects/request/CreateFirewallRequest.java @@ -1,6 +1,7 @@ package me.tomsdevsn.hetznercloud.objects.request; import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.Singular; @@ -12,6 +13,7 @@ @Data @Builder +@AllArgsConstructor public class CreateFirewallRequest { @Singular("applicationTarget") @@ -24,17 +26,4 @@ public class CreateFirewallRequest { @JsonProperty("rules") private List firewallRules; - public CreateFirewallRequest(List applyTo, - Map labels, - String name, - List firewallRules) { - for (var target: applyTo) - target.isValidOrThrow(); - - this.applyTo = applyTo; - this.labels = labels; - this.name = name; - this.firewallRules = firewallRules; - } - } From 58a124a1cd4854cd3b7279d5e0df13c68f81b290 Mon Sep 17 00:00:00 2001 From: Felix Krasemann Date: Fri, 12 Aug 2022 15:33:51 +0200 Subject: [PATCH 8/8] removed deprecated doc --- src/main/java/me/tomsdevsn/hetznercloud/HetznerCloudAPI.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/me/tomsdevsn/hetznercloud/HetznerCloudAPI.java b/src/main/java/me/tomsdevsn/hetznercloud/HetznerCloudAPI.java index 76525fb5..34c7711d 100644 --- a/src/main/java/me/tomsdevsn/hetznercloud/HetznerCloudAPI.java +++ b/src/main/java/me/tomsdevsn/hetznercloud/HetznerCloudAPI.java @@ -835,7 +835,6 @@ public ActionsResponse removeAllRulesFromFirewall(long id) { * @param id of the Firewall you want to set the Rules on. * @param firewallRules you want to set. * @return an ActionsResponse with the executed actions - * @throws IllegalArgumentException if the size of firewallRules is greater than 50 (size > 50) */ public ActionsResponse setFirewallRules(long id, List firewallRules) { return post(