pangea-hcloud 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +6 -0
  3. data/.rspec_status +35 -0
  4. data/Gemfile +5 -0
  5. data/Gemfile.lock +88 -0
  6. data/LICENSE +201 -0
  7. data/Rakefile +8 -0
  8. data/flake.lock +835 -0
  9. data/flake.nix +60 -0
  10. data/gemset.nix +272 -0
  11. data/lib/pangea/resources/hcloud.rb +64 -0
  12. data/lib/pangea/resources/hcloud_certificate/resource.rb +50 -0
  13. data/lib/pangea/resources/hcloud_certificate/types.rb +26 -0
  14. data/lib/pangea/resources/hcloud_firewall/resource.rb +57 -0
  15. data/lib/pangea/resources/hcloud_firewall/types.rb +25 -0
  16. data/lib/pangea/resources/hcloud_firewall_attachment/resource.rb +40 -0
  17. data/lib/pangea/resources/hcloud_firewall_attachment/types.rb +22 -0
  18. data/lib/pangea/resources/hcloud_floating_ip/resource.rb +52 -0
  19. data/lib/pangea/resources/hcloud_floating_ip/types.rb +28 -0
  20. data/lib/pangea/resources/hcloud_floating_ip_assignment/resource.rb +40 -0
  21. data/lib/pangea/resources/hcloud_floating_ip_assignment/types.rb +22 -0
  22. data/lib/pangea/resources/hcloud_load_balancer/resource.rb +57 -0
  23. data/lib/pangea/resources/hcloud_load_balancer/types.rb +28 -0
  24. data/lib/pangea/resources/hcloud_load_balancer_network/resource.rb +43 -0
  25. data/lib/pangea/resources/hcloud_load_balancer_network/types.rb +26 -0
  26. data/lib/pangea/resources/hcloud_load_balancer_service/resource.rb +70 -0
  27. data/lib/pangea/resources/hcloud_load_balancer_service/types.rb +29 -0
  28. data/lib/pangea/resources/hcloud_load_balancer_target/resource.rb +45 -0
  29. data/lib/pangea/resources/hcloud_load_balancer_target/types.rb +28 -0
  30. data/lib/pangea/resources/hcloud_managed_certificate/resource.rb +49 -0
  31. data/lib/pangea/resources/hcloud_managed_certificate/types.rb +25 -0
  32. data/lib/pangea/resources/hcloud_network/resource.rb +48 -0
  33. data/lib/pangea/resources/hcloud_network/types.rb +25 -0
  34. data/lib/pangea/resources/hcloud_network_route/resource.rb +42 -0
  35. data/lib/pangea/resources/hcloud_network_route/types.rb +23 -0
  36. data/lib/pangea/resources/hcloud_network_subnet/resource.rb +44 -0
  37. data/lib/pangea/resources/hcloud_network_subnet/types.rb +27 -0
  38. data/lib/pangea/resources/hcloud_placement_group/resource.rb +48 -0
  39. data/lib/pangea/resources/hcloud_placement_group/types.rb +25 -0
  40. data/lib/pangea/resources/hcloud_primary_ip/resource.rb +52 -0
  41. data/lib/pangea/resources/hcloud_primary_ip/types.rb +29 -0
  42. data/lib/pangea/resources/hcloud_rdns/resource.rb +42 -0
  43. data/lib/pangea/resources/hcloud_rdns/types.rb +23 -0
  44. data/lib/pangea/resources/hcloud_server/resource.rb +91 -0
  45. data/lib/pangea/resources/hcloud_server/types.rb +58 -0
  46. data/lib/pangea/resources/hcloud_server_network/resource.rb +43 -0
  47. data/lib/pangea/resources/hcloud_server_network/types.rb +26 -0
  48. data/lib/pangea/resources/hcloud_snapshot/resource.rb +41 -0
  49. data/lib/pangea/resources/hcloud_snapshot/types.rb +25 -0
  50. data/lib/pangea/resources/hcloud_ssh_key/resource.rb +73 -0
  51. data/lib/pangea/resources/hcloud_ssh_key/types.rb +38 -0
  52. data/lib/pangea/resources/hcloud_uploaded_certificate/resource.rb +44 -0
  53. data/lib/pangea/resources/hcloud_uploaded_certificate/types.rb +26 -0
  54. data/lib/pangea/resources/hcloud_volume/resource.rb +54 -0
  55. data/lib/pangea/resources/hcloud_volume/types.rb +28 -0
  56. data/lib/pangea/resources/hcloud_volume_attachment/resource.rb +41 -0
  57. data/lib/pangea/resources/hcloud_volume_attachment/types.rb +25 -0
  58. data/lib/pangea/resources/hcloud_zone/resource.rb +40 -0
  59. data/lib/pangea/resources/hcloud_zone/types.rb +24 -0
  60. data/lib/pangea/resources/hcloud_zone_rrset/resource.rb +44 -0
  61. data/lib/pangea/resources/hcloud_zone_rrset/types.rb +27 -0
  62. data/lib/pangea/resources/types/hetzner/core.rb +138 -0
  63. data/lib/pangea-hcloud/version.rb +5 -0
  64. data/lib/pangea-hcloud.rb +34 -0
  65. data/pangea-hcloud.gemspec +33 -0
  66. metadata +207 -0
data/flake.nix ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ description = "Pangea Hetzner Cloud provider — typed Terraform resource functions";
3
+
4
+ inputs = {
5
+ nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
6
+ ruby-nix.url = "github:inscapist/ruby-nix";
7
+ flake-utils.url = "github:numtide/flake-utils";
8
+ substrate = {
9
+ url = "github:pleme-io/substrate";
10
+ inputs.nixpkgs.follows = "nixpkgs";
11
+ };
12
+ forge = {
13
+ url = "github:pleme-io/forge";
14
+ inputs.nixpkgs.follows = "nixpkgs";
15
+ inputs.substrate.follows = "substrate";
16
+ };
17
+ };
18
+
19
+ outputs = {
20
+ self,
21
+ nixpkgs,
22
+ ruby-nix,
23
+ flake-utils,
24
+ substrate,
25
+ forge,
26
+ ...
27
+ }:
28
+ flake-utils.lib.eachSystem ["x86_64-linux" "aarch64-linux" "aarch64-darwin"] (system: let
29
+ pkgs = import nixpkgs {
30
+ inherit system;
31
+ overlays = [ruby-nix.overlays.ruby];
32
+ };
33
+ rnix = ruby-nix.lib pkgs;
34
+ rnix-env = rnix {
35
+ name = "pangea-hcloud";
36
+ gemset = ./gemset.nix;
37
+ };
38
+ env = rnix-env.env;
39
+ ruby = rnix-env.ruby;
40
+
41
+ rubyBuild = import "${substrate}/lib/ruby-build.nix" {
42
+ inherit pkgs;
43
+ forgeCmd = "${forge.packages.${system}.default}/bin/forge";
44
+ defaultGhcrToken = "";
45
+ };
46
+ in {
47
+ devShells.default = pkgs.mkShell {
48
+ buildInputs = [env ruby];
49
+ shellHook = ''
50
+ export RUBYLIB=$PWD/lib:$RUBYLIB
51
+ export DRY_TYPES_WARNINGS=false
52
+ '';
53
+ };
54
+
55
+ apps = rubyBuild.mkRubyGemApps {
56
+ srcDir = self;
57
+ name = "pangea-hcloud";
58
+ };
59
+ });
60
+ }
data/gemset.nix ADDED
@@ -0,0 +1,272 @@
1
+ {
2
+ abstract-synthesizer = {
3
+ groups = ["default"];
4
+ platforms = [];
5
+ source = {
6
+ remotes = ["https://rubygems.org"];
7
+ sha256 = "1724yzbklcmiiahb7s3y1ir1i0n03b9c3arlib35g85d8hf0h75d";
8
+ type = "gem";
9
+ };
10
+ version = "0.0.15";
11
+ };
12
+ base64 = {
13
+ groups = ["default"];
14
+ platforms = [];
15
+ source = {
16
+ remotes = ["https://rubygems.org"];
17
+ sha256 = "0yx9yn47a8lkfcjmigk79fykxvr80r4m1i35q82sxzynpbm7lcr7";
18
+ type = "gem";
19
+ };
20
+ version = "0.3.0";
21
+ };
22
+ bigdecimal = {
23
+ groups = ["default"];
24
+ platforms = [];
25
+ source = {
26
+ remotes = ["https://rubygems.org"];
27
+ sha256 = "19y406nx17arzsbc515mjmr6k5p59afprspa1k423yd9cp8d61wb";
28
+ type = "gem";
29
+ };
30
+ version = "4.0.1";
31
+ };
32
+ concurrent-ruby = {
33
+ groups = ["default"];
34
+ platforms = [];
35
+ source = {
36
+ remotes = ["https://rubygems.org"];
37
+ sha256 = "1aymcakhzl83k77g2f2krz07bg1cbafbcd2ghvwr4lky3rz86mkb";
38
+ type = "gem";
39
+ };
40
+ version = "1.3.6";
41
+ };
42
+ diff-lcs = {
43
+ groups = ["default" "development"];
44
+ platforms = [];
45
+ source = {
46
+ remotes = ["https://rubygems.org"];
47
+ sha256 = "0qlrj2qyysc9avzlr4zs1py3x684hqm61n4czrsk1pyllz5x5q4s";
48
+ type = "gem";
49
+ };
50
+ version = "1.6.2";
51
+ };
52
+ docile = {
53
+ groups = ["default" "development"];
54
+ platforms = [];
55
+ source = {
56
+ remotes = ["https://rubygems.org"];
57
+ sha256 = "07pj4z3h8wk4fgdn6s62vw1lwvhj0ac0x10vfbdkr9xzk7krn5cn";
58
+ type = "gem";
59
+ };
60
+ version = "1.4.1";
61
+ };
62
+ dry-core = {
63
+ dependencies = ["concurrent-ruby" "logger" "zeitwerk"];
64
+ groups = ["default"];
65
+ platforms = [];
66
+ source = {
67
+ remotes = ["https://rubygems.org"];
68
+ sha256 = "18cn9s2p7cbgacy0z41h3sf9jvl75vjfmvj774apyffzi3dagi8c";
69
+ type = "gem";
70
+ };
71
+ version = "1.2.0";
72
+ };
73
+ dry-inflector = {
74
+ groups = ["default"];
75
+ platforms = [];
76
+ source = {
77
+ remotes = ["https://rubygems.org"];
78
+ sha256 = "1k1dd35sqqqg2abd2g2w78m94pa3mcwvmrsjbkr3hxpn0jxw5c3z";
79
+ type = "gem";
80
+ };
81
+ version = "1.3.1";
82
+ };
83
+ dry-logic = {
84
+ dependencies = ["bigdecimal" "concurrent-ruby" "dry-core" "zeitwerk"];
85
+ groups = ["default"];
86
+ platforms = [];
87
+ source = {
88
+ remotes = ["https://rubygems.org"];
89
+ sha256 = "18nf8mbnhgvkw34drj7nmvpx2afmyl2nyzncn3wl3z4h1yyfsvys";
90
+ type = "gem";
91
+ };
92
+ version = "1.6.0";
93
+ };
94
+ dry-struct = {
95
+ dependencies = ["dry-core" "dry-types" "ice_nine" "zeitwerk"];
96
+ groups = ["default"];
97
+ platforms = [];
98
+ source = {
99
+ remotes = ["https://rubygems.org"];
100
+ sha256 = "0ri9iqxknxvvhpbshf6jn7bq581k8l67iv23mii69yr4k5aqphvl";
101
+ type = "gem";
102
+ };
103
+ version = "1.8.0";
104
+ };
105
+ dry-types = {
106
+ dependencies = ["bigdecimal" "concurrent-ruby" "dry-core" "dry-inflector" "dry-logic" "zeitwerk"];
107
+ groups = ["default"];
108
+ platforms = [];
109
+ source = {
110
+ remotes = ["https://rubygems.org"];
111
+ sha256 = "0y7icwaa26ycikz6h97gwd1hji3r280n4yr2kmn5sfgqp76yxsxs";
112
+ type = "gem";
113
+ };
114
+ version = "1.9.1";
115
+ };
116
+ ice_nine = {
117
+ groups = ["default"];
118
+ platforms = [];
119
+ source = {
120
+ remotes = ["https://rubygems.org"];
121
+ sha256 = "1nv35qg1rps9fsis28hz2cq2fx1i96795f91q4nmkm934xynll2x";
122
+ type = "gem";
123
+ };
124
+ version = "0.11.2";
125
+ };
126
+ logger = {
127
+ groups = ["default"];
128
+ platforms = [];
129
+ source = {
130
+ remotes = ["https://rubygems.org"];
131
+ sha256 = "00q2zznygpbls8asz5knjvvj2brr3ghmqxgr83xnrdj4rk3xwvhr";
132
+ type = "gem";
133
+ };
134
+ version = "1.7.0";
135
+ };
136
+ pangea-core = {
137
+ dependencies = ["base64" "dry-struct" "dry-types" "terraform-synthesizer"];
138
+ groups = ["default"];
139
+ platforms = [];
140
+ source = {
141
+ path = ../pangea-core;
142
+ type = "path";
143
+ };
144
+ version = "0.1.0";
145
+ };
146
+ pangea-hcloud = {
147
+ dependencies = ["dry-struct" "dry-types" "pangea-core" "terraform-synthesizer"];
148
+ groups = ["default"];
149
+ platforms = [];
150
+ source = {
151
+ path = ./.;
152
+ type = "path";
153
+ };
154
+ version = "0.1.0";
155
+ };
156
+ rake = {
157
+ groups = ["development"];
158
+ platforms = [];
159
+ source = {
160
+ remotes = ["https://rubygems.org"];
161
+ sha256 = "175iisqb211n0qbfyqd8jz2g01q6xj038zjf4q0nm8k6kz88k7lc";
162
+ type = "gem";
163
+ };
164
+ version = "13.3.1";
165
+ };
166
+ rspec = {
167
+ dependencies = ["rspec-core" "rspec-expectations" "rspec-mocks"];
168
+ groups = ["development"];
169
+ platforms = [];
170
+ source = {
171
+ remotes = ["https://rubygems.org"];
172
+ sha256 = "11q5hagj6vr694innqj4r45jrm8qcwvkxjnphqgyd66piah88qi0";
173
+ type = "gem";
174
+ };
175
+ version = "3.13.2";
176
+ };
177
+ rspec-core = {
178
+ dependencies = ["rspec-support"];
179
+ groups = ["default" "development"];
180
+ platforms = [];
181
+ source = {
182
+ remotes = ["https://rubygems.org"];
183
+ sha256 = "0bcbh9yv6cs6pv299zs4bvalr8yxa51kcdd1pjl60yv625j3r0m8";
184
+ type = "gem";
185
+ };
186
+ version = "3.13.6";
187
+ };
188
+ rspec-expectations = {
189
+ dependencies = ["diff-lcs" "rspec-support"];
190
+ groups = ["default" "development"];
191
+ platforms = [];
192
+ source = {
193
+ remotes = ["https://rubygems.org"];
194
+ sha256 = "0dl8npj0jfpy31bxi6syc7jymyd861q277sfr6jawq2hv6hx791k";
195
+ type = "gem";
196
+ };
197
+ version = "3.13.5";
198
+ };
199
+ rspec-mocks = {
200
+ dependencies = ["diff-lcs" "rspec-support"];
201
+ groups = ["default" "development"];
202
+ platforms = [];
203
+ source = {
204
+ remotes = ["https://rubygems.org"];
205
+ sha256 = "071bqrk2rblk3zq3jk1xxx0dr92y0szi5pxdm8waimxici706y89";
206
+ type = "gem";
207
+ };
208
+ version = "3.13.7";
209
+ };
210
+ rspec-support = {
211
+ groups = ["default" "development"];
212
+ platforms = [];
213
+ source = {
214
+ remotes = ["https://rubygems.org"];
215
+ sha256 = "0z64h5rznm2zv21vjdjshz4v0h7bxvg02yc6g7yzxakj11byah06";
216
+ type = "gem";
217
+ };
218
+ version = "3.13.7";
219
+ };
220
+ simplecov = {
221
+ dependencies = ["docile" "simplecov-html" "simplecov_json_formatter"];
222
+ groups = ["development"];
223
+ platforms = [];
224
+ source = {
225
+ remotes = ["https://rubygems.org"];
226
+ sha256 = "198kcbrjxhhzca19yrdcd6jjj9sb51aaic3b0sc3pwjghg3j49py";
227
+ type = "gem";
228
+ };
229
+ version = "0.22.0";
230
+ };
231
+ simplecov-html = {
232
+ groups = ["default" "development"];
233
+ platforms = [];
234
+ source = {
235
+ remotes = ["https://rubygems.org"];
236
+ sha256 = "0ikjfwydgs08nm3xzc4cn4b6z6rmcrj2imp84xcnimy2wxa8w2xx";
237
+ type = "gem";
238
+ };
239
+ version = "0.13.2";
240
+ };
241
+ simplecov_json_formatter = {
242
+ groups = ["default" "development"];
243
+ platforms = [];
244
+ source = {
245
+ remotes = ["https://rubygems.org"];
246
+ sha256 = "0a5l0733hj7sk51j81ykfmlk2vd5vaijlq9d5fn165yyx3xii52j";
247
+ type = "gem";
248
+ };
249
+ version = "0.1.4";
250
+ };
251
+ terraform-synthesizer = {
252
+ dependencies = ["abstract-synthesizer"];
253
+ groups = ["default"];
254
+ platforms = [];
255
+ source = {
256
+ remotes = ["https://rubygems.org"];
257
+ sha256 = "01yl1s6xnxn3qh42ybqanxdgcfpppg2cvjk8pka7xcf5hxz9qxda";
258
+ type = "gem";
259
+ };
260
+ version = "0.0.28";
261
+ };
262
+ zeitwerk = {
263
+ groups = ["default"];
264
+ platforms = [];
265
+ source = {
266
+ remotes = ["https://rubygems.org"];
267
+ sha256 = "1pbkiwwla5gldgb3saamn91058nl1sq1344l5k36xsh9ih995nnq";
268
+ type = "gem";
269
+ };
270
+ version = "2.7.5";
271
+ };
272
+ }
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pangea/resources/types'
4
+ require 'pangea/resources/base'
5
+
6
+ # Require all hcloud resources
7
+ require_relative 'hcloud_certificate/resource'
8
+ require_relative 'hcloud_firewall/resource'
9
+ require_relative 'hcloud_firewall_attachment/resource'
10
+ require_relative 'hcloud_floating_ip/resource'
11
+ require_relative 'hcloud_floating_ip_assignment/resource'
12
+ require_relative 'hcloud_load_balancer/resource'
13
+ require_relative 'hcloud_load_balancer_network/resource'
14
+ require_relative 'hcloud_load_balancer_service/resource'
15
+ require_relative 'hcloud_load_balancer_target/resource'
16
+ require_relative 'hcloud_managed_certificate/resource'
17
+ require_relative 'hcloud_network/resource'
18
+ require_relative 'hcloud_network_route/resource'
19
+ require_relative 'hcloud_network_subnet/resource'
20
+ require_relative 'hcloud_placement_group/resource'
21
+ require_relative 'hcloud_primary_ip/resource'
22
+ require_relative 'hcloud_rdns/resource'
23
+ require_relative 'hcloud_server/resource'
24
+ require_relative 'hcloud_server_network/resource'
25
+ require_relative 'hcloud_snapshot/resource'
26
+ require_relative 'hcloud_ssh_key/resource'
27
+ require_relative 'hcloud_uploaded_certificate/resource'
28
+ require_relative 'hcloud_volume/resource'
29
+ require_relative 'hcloud_volume_attachment/resource'
30
+ require_relative 'hcloud_zone/resource'
31
+ require_relative 'hcloud_zone_rrset/resource'
32
+
33
+ module Pangea
34
+ module Resources
35
+ module Hcloud
36
+ include Base
37
+ include HcloudCertificate
38
+ include HcloudFirewall
39
+ include HcloudFirewallAttachment
40
+ include HcloudFloatingIp
41
+ include HcloudFloatingIpAssignment
42
+ include HcloudLoadBalancer
43
+ include HcloudLoadBalancerNetwork
44
+ include HcloudLoadBalancerService
45
+ include HcloudLoadBalancerTarget
46
+ include HcloudManagedCertificate
47
+ include HcloudNetwork
48
+ include HcloudNetworkRoute
49
+ include HcloudNetworkSubnet
50
+ include HcloudPlacementGroup
51
+ include HcloudPrimaryIp
52
+ include HcloudRdns
53
+ include HcloudServer
54
+ include HcloudServerNetwork
55
+ include HcloudSnapshot
56
+ include HcloudSshKey
57
+ include HcloudUploadedCertificate
58
+ include HcloudVolume
59
+ include HcloudVolumeAttachment
60
+ include HcloudZone
61
+ include HcloudZoneRrset
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+ # Copyright 2025 The Pangea Authors
3
+
4
+ require 'pangea/resources/base'
5
+ require 'pangea/resources/reference'
6
+ require 'pangea/resources/hcloud_certificate/types'
7
+ require 'pangea/resource_registry'
8
+
9
+ module Pangea
10
+ module Resources
11
+ module HcloudCertificate
12
+ # Create Certificate
13
+ def hcloud_certificate(name, attributes = {})
14
+ cert_attrs = Hetzner::Types::CertificateAttributes.new(attributes)
15
+
16
+ resource(:hcloud_certificate, name) do
17
+ name cert_attrs.name
18
+ certificate cert_attrs.certificate if cert_attrs.certificate
19
+ private_key cert_attrs.private_key if cert_attrs.private_key
20
+
21
+ if cert_attrs.labels.any?
22
+ labels do
23
+ cert_attrs.labels.each do |key, value|
24
+ public_send(key, value)
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ ResourceReference.new(
31
+ type: 'hcloud_certificate',
32
+ name: name,
33
+ resource_attributes: cert_attrs.to_h,
34
+ outputs: {
35
+ id: "${hcloud_certificate.#{name}.id}",
36
+ name: "${hcloud_certificate.#{name}.name}",
37
+ not_valid_before: "${hcloud_certificate.#{name}.not_valid_before}",
38
+ not_valid_after: "${hcloud_certificate.#{name}.not_valid_after}"
39
+ }
40
+ )
41
+ end
42
+ end
43
+
44
+ module Hetzner
45
+ include HcloudCertificate
46
+ end
47
+ end
48
+ end
49
+
50
+ Pangea::ResourceRegistry.register_module(Pangea::Resources::Hetzner)
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+ # Copyright 2025 The Pangea Authors
3
+
4
+ require 'dry-struct'
5
+ require 'pangea/resources/types'
6
+
7
+ module Pangea
8
+ module Resources
9
+ module Hetzner
10
+ module Types
11
+ # Hetzner Certificate attributes
12
+ class CertificateAttributes < Dry::Struct
13
+ transform_keys(&:to_sym)
14
+
15
+ # Required attributes
16
+ attribute :name, Resources::Types::String
17
+
18
+ # Optional attributes (depends on type)
19
+ attribute :certificate, Resources::Types::String.optional.default(nil)
20
+ attribute :private_key, Resources::Types::String.optional.default(nil)
21
+ attribute :labels, Resources::Types::HetznerLabels.default({}.freeze)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+ # Copyright 2025 The Pangea Authors
3
+
4
+ require 'pangea/resources/base'
5
+ require 'pangea/resources/reference'
6
+ require 'pangea/resources/hcloud_firewall/types'
7
+ require 'pangea/resource_registry'
8
+
9
+ module Pangea
10
+ module Resources
11
+ module HcloudFirewall
12
+ # Create a Hetzner Cloud Firewall with type-safe attributes
13
+ def hcloud_firewall(name, attributes = {})
14
+ firewall_attrs = Hetzner::Types::FirewallAttributes.new(attributes)
15
+
16
+ resource(:hcloud_firewall, name) do
17
+ name firewall_attrs.name
18
+
19
+ # Add firewall rules
20
+ firewall_attrs.rules.each do |rule|
21
+ rule do
22
+ direction rule[:direction]
23
+ protocol rule[:protocol]
24
+ port rule[:port] if rule[:port]
25
+ source_ips rule[:source_ips] if rule[:source_ips]
26
+ destination_ips rule[:destination_ips] if rule[:destination_ips]
27
+ end
28
+ end
29
+
30
+ if firewall_attrs.labels.any?
31
+ labels do
32
+ firewall_attrs.labels.each do |key, value|
33
+ public_send(key, value)
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ ResourceReference.new(
40
+ type: 'hcloud_firewall',
41
+ name: name,
42
+ resource_attributes: firewall_attrs.to_h,
43
+ outputs: {
44
+ id: "${hcloud_firewall.#{name}.id}",
45
+ name: "${hcloud_firewall.#{name}.name}"
46
+ }
47
+ )
48
+ end
49
+ end
50
+
51
+ module Hetzner
52
+ include HcloudFirewall
53
+ end
54
+ end
55
+ end
56
+
57
+ Pangea::ResourceRegistry.register_module(Pangea::Resources::Hetzner)
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+ # Copyright 2025 The Pangea Authors
3
+
4
+ require 'dry-struct'
5
+ require 'pangea/resources/types'
6
+
7
+ module Pangea
8
+ module Resources
9
+ module Hetzner
10
+ module Types
11
+ # Hetzner Firewall resource attributes with validation
12
+ class FirewallAttributes < Dry::Struct
13
+ transform_keys(&:to_sym)
14
+
15
+ # Required attributes
16
+ attribute :name, Resources::Types::String
17
+
18
+ # Optional attributes
19
+ attribute :rules, Resources::Types::Array.of(Resources::Types::HetznerFirewallRule).default([].freeze)
20
+ attribute :labels, Resources::Types::HetznerLabels.default({}.freeze)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+ # Copyright 2025 The Pangea Authors
3
+
4
+ require 'pangea/resources/base'
5
+ require 'pangea/resources/reference'
6
+ require 'pangea/resources/hcloud_firewall_attachment/types'
7
+ require 'pangea/resource_registry'
8
+
9
+ module Pangea
10
+ module Resources
11
+ module HcloudFirewallAttachment
12
+ # Attach Firewall to Servers
13
+ def hcloud_firewall_attachment(name, attributes = {})
14
+ fa_attrs = Hetzner::Types::FirewallAttachmentAttributes.new(attributes)
15
+
16
+ resource(:hcloud_firewall_attachment, name) do
17
+ firewall_id fa_attrs.firewall_id
18
+ server_ids fa_attrs.server_ids if fa_attrs.server_ids.any?
19
+ end
20
+
21
+ ResourceReference.new(
22
+ type: 'hcloud_firewall_attachment',
23
+ name: name,
24
+ resource_attributes: fa_attrs.to_h,
25
+ outputs: {
26
+ id: "${hcloud_firewall_attachment.#{name}.id}",
27
+ firewall_id: "${hcloud_firewall_attachment.#{name}.firewall_id}",
28
+ server_ids: "${hcloud_firewall_attachment.#{name}.server_ids}"
29
+ }
30
+ )
31
+ end
32
+ end
33
+
34
+ module Hetzner
35
+ include HcloudFirewallAttachment
36
+ end
37
+ end
38
+ end
39
+
40
+ Pangea::ResourceRegistry.register_module(Pangea::Resources::Hetzner)
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+ # Copyright 2025 The Pangea Authors
3
+
4
+ require 'dry-struct'
5
+ require 'pangea/resources/types'
6
+
7
+ module Pangea
8
+ module Resources
9
+ module Hetzner
10
+ module Types
11
+ # Hetzner Firewall Attachment attributes
12
+ class FirewallAttachmentAttributes < Dry::Struct
13
+ transform_keys(&:to_sym)
14
+
15
+ # Required attributes
16
+ attribute :firewall_id, Resources::Types::String
17
+ attribute :server_ids, Resources::Types::Array.of(Resources::Types::String).default([].freeze)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+ # Copyright 2025 The Pangea Authors
3
+
4
+ require 'pangea/resources/base'
5
+ require 'pangea/resources/reference'
6
+ require 'pangea/resources/hcloud_floating_ip/types'
7
+ require 'pangea/resource_registry'
8
+
9
+ module Pangea
10
+ module Resources
11
+ module HcloudFloatingIp
12
+ # Create a Hetzner Cloud Floating IP
13
+ def hcloud_floating_ip(name, attributes = {})
14
+ fip_attrs = Hetzner::Types::FloatingIpAttributes.new(attributes)
15
+
16
+ resource(:hcloud_floating_ip, name) do
17
+ type fip_attrs.type
18
+ home_location fip_attrs.home_location if fip_attrs.home_location
19
+ server_id fip_attrs.server_id if fip_attrs.server_id
20
+ description fip_attrs.description if fip_attrs.description
21
+ name fip_attrs.name if fip_attrs.name
22
+
23
+ if fip_attrs.labels.any?
24
+ labels do
25
+ fip_attrs.labels.each do |key, value|
26
+ public_send(key, value)
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ ResourceReference.new(
33
+ type: 'hcloud_floating_ip',
34
+ name: name,
35
+ resource_attributes: fip_attrs.to_h,
36
+ outputs: {
37
+ id: "${hcloud_floating_ip.#{name}.id}",
38
+ ip_address: "${hcloud_floating_ip.#{name}.ip_address}",
39
+ name: "${hcloud_floating_ip.#{name}.name}",
40
+ home_location: "${hcloud_floating_ip.#{name}.home_location}"
41
+ }
42
+ )
43
+ end
44
+ end
45
+
46
+ module Hetzner
47
+ include HcloudFloatingIp
48
+ end
49
+ end
50
+ end
51
+
52
+ Pangea::ResourceRegistry.register_module(Pangea::Resources::Hetzner)