hetznercloud 6.1.0 → 7.0.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 (35) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +55 -0
  3. data/README.md +0 -1
  4. data/config/inflections.rb +1 -0
  5. data/lib/hcloud/entities/assignee.rb +8 -0
  6. data/lib/hcloud/entities/health_status.rb +3 -0
  7. data/lib/hcloud/entities/load_balancer_public_net.rb +9 -2
  8. data/lib/hcloud/entities/load_balancer_public_net_ip.rb +13 -0
  9. data/lib/hcloud/entities/network_member.rb +23 -0
  10. data/lib/hcloud/entities/service_http.rb +2 -0
  11. data/lib/hcloud/errors.rb +17 -0
  12. data/lib/hcloud/http.rb +8 -8
  13. data/lib/hcloud/resource_type.rb +3 -4
  14. data/lib/hcloud/resources/floating_ip.rb +1 -1
  15. data/lib/hcloud/resources/image.rb +4 -5
  16. data/lib/hcloud/resources/load_balancer.rb +8 -2
  17. data/lib/hcloud/resources/load_balancer_type.rb +0 -2
  18. data/lib/hcloud/resources/network.rb +38 -0
  19. data/lib/hcloud/resources/primary_ip.rb +5 -5
  20. data/lib/hcloud/resources/server.rb +1 -4
  21. data/lib/hcloud/resources/server_type.rb +0 -4
  22. data/lib/hcloud/resources/volume.rb +7 -5
  23. data/lib/hcloud/version.rb +2 -2
  24. data/lib/http/features/compression/block_io.rb +21 -0
  25. data/lib/http/features/{request → compression}/brotli_body.rb +1 -1
  26. data/lib/http/features/{response → compression}/brotli_inflater.rb +7 -4
  27. data/lib/http/features/{request → compression}/compressed_body.rb +4 -2
  28. data/lib/http/features/{response → compression}/gzip_inflater.rb +1 -1
  29. data/lib/http/features/{request → compression}/gzipped_body.rb +1 -1
  30. data/lib/http/features/compression.rb +7 -7
  31. data/lib/http/rate_limiter.rb +8 -6
  32. metadata +15 -14
  33. data/lib/hcloud/entities/datacenter_server_type.rb +0 -20
  34. data/lib/hcloud/resources/datacenter.rb +0 -55
  35. data/lib/http/features/block_io.rb +0 -18
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c97f9307e8cae23c94b06c853eab8432a5d571fc90b13c527a0fa8a029038143
4
- data.tar.gz: da9e9aecda35dbdca6a6b1e1047d4ff957ca63a649f1a3a52b25d31c42302f48
3
+ metadata.gz: 5abeeb66cec383f59fad1ed6047f9c630da9f47261125db792fa247e0701a485
4
+ data.tar.gz: b63435fca21c3c6bbc8cffea309687b9f35fd33e154a7f7db68a49ec133932c5
5
5
  SHA512:
6
- metadata.gz: feb065fa72661720caeefff45ea650374bdb71919a7e901102cfcfe79f386b80b9fde9e1700112052041ec5f4e7cc7e25eec3c6ced0e5dd15f2bbf5eb4872e37
7
- data.tar.gz: 61fe9582cdc7681c567c9da110c1028a0c713f3cd3fc1f8a59ff00a0495a1e82e82070e74e3514f738eacc7949513dd68e2c35d761883cbb464015dc63b9d3ba
6
+ metadata.gz: 196c3b0d592ec53e80341db861f40f9ade8a06a98589c666fa8e4b80812e8955bfc88518a56dbbdfcd22b41ed174806272cf410047e6a02b35490a08617ef900
7
+ data.tar.gz: 8971d904b0a003f2cc11b8273a747d5c5a335b7f19d1f132189e3dba89a7d41fcf3111823937b6e6de7a32e98732bf0a984b61b78965da2b1e09a60429054f74
data/CHANGELOG.md CHANGED
@@ -10,6 +10,61 @@
10
10
 
11
11
  ### Fixed
12
12
 
13
+ ## HCloud v7.0.0 (2026-11-02)
14
+
15
+ ### Upgrading
16
+
17
+ This release contains breaking changes, following changes in the Hetzner Cloud API.
18
+
19
+ - The `http` dependency now requires version 6.0 or higher.
20
+ - `HCloud::Datacenter` and `HCloud::DatacenterServerType` have been removed, because the API endpoints were removed. Use `HCloud::Location` instead.
21
+ - `HCloud::Server#datacenter` and `HCloud::PrimaryIP#datacenter` have been removed. Use `#location` instead, and pass `location:` instead of `datacenter:` when creating primary IPs.
22
+ - `HCloud::Image#deprecated` and `HCloud::LoadBalancerType#deprecated` have been removed. Use `#deprecation` instead. `HCloud::Image#deprecated?` is still available.
23
+ - `HCloud::ServerType#deprecated` and `HCloud::ServerType#deprecated?` have been removed. Use `#deprecation` on each of the server type's `locations` instead.
24
+ - `HCloud::Image#build_id` has been removed, because it is not present in the API.
25
+ - `HCloud::Image#image_size` is now a float instead of an integer.
26
+ - `HCloud::LoadBalancer#delete` now returns an `HCloud::Action` instead of the load balancer itself.
27
+ - `HCloud::PrimaryIP#assignee_type` no longer defaults to `"server"`. Pass `assignee_type: "server"` explicitly when creating a primary IP assigned to a server.
28
+ - `HCloud::Volume#server` is deprecated and will be removed when the API removes it (27 October 2027). Use `#assignee` instead, and pass `assignee: { id: 1, type: "server" }` instead of `server: 1` when creating or attaching volumes.
29
+
30
+ ### Added
31
+
32
+ - Add `location` to `HCloud::PrimaryIP`
33
+ - Add `placement_group` to `HCloud::Server` creatable attributes
34
+ - Add `description` to `HCloud::FloatingIP` updatable attributes
35
+ - Add `timeout_idle` to `HCloud::ServiceHTTP`
36
+ - Add `detail` and `http_status_code` to `HCloud::HealthStatus`
37
+ - Add `deprecation` to `HCloud::Image`
38
+ - Add `assignee` to `HCloud::Volume`
39
+ - Add `public_net` to `HCloud::LoadBalancer` creatable attributes (assign existing primary IPs)
40
+ - Add `id` and `blocked` to `HCloud::LoadBalancer#public_net` IPs
41
+ - Add `PrimaryIPBlocked` and `PrimaryIPLocationMismatch` error classes
42
+ - Add `HCloud::Network#members`
43
+
44
+ ### Changed
45
+
46
+ - Change `HCloud::Image#image_size` type from integer to float
47
+ - Change `HCloud::LoadBalancer#targets` type to support label selector and IP targets
48
+ - Upgrade `http` dependency to 6.0
49
+ - Set `HCloud::Errors::ServerError` message to the HTTP status line
50
+ - Remove default value of `HCloud::PrimaryIP#assignee_type` (API defaults to `unassigned`)
51
+ - Deprecate `HCloud::Volume#server` (use `assignee` instead)
52
+ - `HCloud::LoadBalancer#delete` now returns an `HCloud::Action`
53
+
54
+ ### Removed
55
+
56
+ - Remove `datacenter` from `HCloud::Server` and `HCloud::PrimaryIP`
57
+ - Remove `build_id` from `HCloud::Image` (not present in API)
58
+ - Remove `deprecated` from `HCloud::Image` (use `deprecation` instead)
59
+ - Remove `deprecated` from `HCloud::ServerType` (use `locations[].deprecation` instead)
60
+ - Remove `deprecated` from `HCloud::LoadBalancerType` (use `deprecation` instead)
61
+ - Remove `HCloud::Datacenter` (endpoints removed from API)
62
+
63
+ ### Fixed
64
+
65
+ - Fix error class lookup for error codes containing `ip`, `ipv4` or `ipv6` (e.g. `HCloud::Errors::PrimaryIPAssigned`)
66
+ - Fix error class lookup for the `json_error` error code (`HCloud::Errors::JSONError`)
67
+
13
68
  ## HCloud v6.1.0 (2026-07-10)
14
69
 
15
70
  ### Added
data/README.md CHANGED
@@ -74,7 +74,6 @@ The following table lists the Hetzner Cloud API endpoints that are currently imp
74
74
  | [Actions](lib/hcloud/resources/action.rb) | Implemented |
75
75
  | [Certificates](lib/hcloud/resources/certificate.rb) | Implemented |
76
76
  | [Certificate Actions](lib/hcloud/resources/certificate.rb) | Implemented |
77
- | [Datacenters](lib/hcloud/resources/datacenter.rb) | Implemented |
78
77
  | [Firewalls](lib/hcloud/resources/firewall.rb) | Implemented |
79
78
  | [Firewall Actions](lib/hcloud/resources/firewall.rb) | Implemented |
80
79
  | [Floating IPs](lib/hcloud/resources/floating_ip.rb) | Implemented |
@@ -15,6 +15,7 @@ HCloud.loader.inflector.inflect(
15
15
  "ipv6" => "IPv6",
16
16
  "iso" => "ISO",
17
17
  "iso_type" => "ISOType",
18
+ "load_balancer_public_net_ip" => "LoadBalancerPublicNetIP",
18
19
  "primary_ip" => "PrimaryIP",
19
20
  "primary_ip_prices" => "PrimaryIPPrices",
20
21
  "rrset" => "RRSet",
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HCloud
4
+ class Assignee < Entity
5
+ attribute :id, :integer
6
+ attribute :type
7
+ end
8
+ end
@@ -4,5 +4,8 @@ module HCloud
4
4
  class HealthStatus < Entity
5
5
  attribute :listen_port, :integer
6
6
  attribute :status
7
+
8
+ attribute :detail
9
+ attribute :http_status_code, :integer
7
10
  end
8
11
  end
@@ -3,9 +3,16 @@
3
3
  module HCloud
4
4
  class LoadBalancerPublicNet < Entity
5
5
  attribute :enabled, :boolean
6
- attribute :ipv4, :dns_pointer
7
- attribute :ipv6, :dns_pointer
6
+ attribute :ipv4, :load_balancer_public_net_ip
7
+ attribute :ipv6, :load_balancer_public_net_ip
8
8
 
9
9
  alias enabled? enabled
10
+
11
+ def to_h
12
+ {
13
+ ipv4: ipv4&.id,
14
+ ipv6: ipv6&.id,
15
+ }.compact
16
+ end
10
17
  end
11
18
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HCloud
4
+ class LoadBalancerPublicNetIP < Entity
5
+ attribute :id, :integer
6
+ attribute :ip
7
+ attribute :dns_ptr
8
+
9
+ attribute :blocked, :boolean
10
+
11
+ alias blocked? blocked
12
+ end
13
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HCloud
4
+ class NetworkMember < Entity
5
+ attribute :type
6
+ attribute :id, :integer
7
+
8
+ attribute :ip
9
+ attribute :alias_ips, array: true, default: -> { [] }
10
+ attribute :subnet
11
+
12
+ attribute :status
13
+
14
+ ##
15
+ # The attached resource (e.g. HCloud::Server or HCloud::LoadBalancer)
16
+ #
17
+ def resource
18
+ ActiveModel::Type
19
+ .lookup(type.to_sym)
20
+ .cast(id)
21
+ end
22
+ end
23
+ end
@@ -7,6 +7,8 @@ module HCloud
7
7
  attribute :cookie_lifetime, :integer
8
8
  attribute :cookie_name
9
9
 
10
+ attribute :timeout_idle, :integer
11
+
10
12
  attribute :redirect_http, :boolean
11
13
  attribute :sticky_sessions, :boolean
12
14
 
data/lib/hcloud/errors.rb CHANGED
@@ -2,6 +2,21 @@
2
2
 
3
3
  module HCloud
4
4
  module Errors
5
+ # Acronyms used in error class names
6
+ ACRONYMS = {
7
+ "ip" => "IP",
8
+ "ipv4" => "IPv4",
9
+ "ipv6" => "IPv6",
10
+ "json" => "JSON",
11
+ }.freeze
12
+
13
+ ##
14
+ # Look up the error class for an API error code (e.g. "primary_ip_assigned" => PrimaryIPAssigned)
15
+ #
16
+ def self.for(code)
17
+ const_get(code.to_s.split("_").map { |word| ACRONYMS.fetch(word) { word.capitalize } }.join)
18
+ end
19
+
5
20
  class Error < StandardError
6
21
  attr_reader :data
7
22
 
@@ -50,7 +65,9 @@ module HCloud
50
65
  class PlacementError < Error; end
51
66
  class PrimaryIPAssigned < Error; end
52
67
  class PrimaryIPAlreadyAssigned < Error; end
68
+ class PrimaryIPBlocked < Error; end
53
69
  class PrimaryIPDatacenterMismatch < Error; end
70
+ class PrimaryIPLocationMismatch < Error; end
54
71
  class PrimaryIPVersionMismatch < Error; end
55
72
  class Protected < Error; end
56
73
  class RateLimitExceeded < Error; end
data/lib/hcloud/http.rb CHANGED
@@ -28,7 +28,7 @@ module HCloud
28
28
  response = http
29
29
  .get(url_for(path), params: transform_params(params))
30
30
 
31
- raise Errors::ServerError, response if response.status.server_error?
31
+ raise Errors::ServerError, { message: response.status.to_s } if response.status.server_error?
32
32
 
33
33
  data = response
34
34
  .parse(:json)
@@ -36,14 +36,14 @@ module HCloud
36
36
 
37
37
  return data if response.status.success?
38
38
 
39
- raise Errors.const_get(data.dig(:error, :code).camelize), data[:error]
39
+ raise Errors.for(data.dig(:error, :code)), data[:error]
40
40
  end
41
41
 
42
42
  def put(path, body = {})
43
43
  response = http
44
44
  .put(url_for(path), json: body)
45
45
 
46
- raise Errors::ServerError, response if response.status.server_error?
46
+ raise Errors::ServerError, { message: response.status.to_s } if response.status.server_error?
47
47
 
48
48
  data = response
49
49
  .parse(:json)
@@ -51,14 +51,14 @@ module HCloud
51
51
 
52
52
  return data if response.status.success?
53
53
 
54
- raise Errors.const_get(data.dig(:error, :code).camelize), data[:error]
54
+ raise Errors.for(data.dig(:error, :code)), data[:error]
55
55
  end
56
56
 
57
57
  def post(path, body = {})
58
58
  response = http
59
59
  .post(url_for(path), json: body)
60
60
 
61
- raise Errors::ServerError, response if response.status.server_error?
61
+ raise Errors::ServerError, { message: response.status.to_s } if response.status.server_error?
62
62
 
63
63
  data = response
64
64
  .parse(:json)
@@ -66,14 +66,14 @@ module HCloud
66
66
 
67
67
  return data if response.status.success?
68
68
 
69
- raise Errors.const_get(data.dig(:error, :code).camelize), data[:error]
69
+ raise Errors.for(data.dig(:error, :code)), data[:error]
70
70
  end
71
71
 
72
72
  def delete(path)
73
73
  response = http
74
74
  .delete(url_for(path))
75
75
 
76
- raise Errors::ServerError, response if response.status.server_error?
76
+ raise Errors::ServerError, { message: response.status.to_s } if response.status.server_error?
77
77
 
78
78
  return if response.status.success? && response.body.empty?
79
79
 
@@ -83,7 +83,7 @@ module HCloud
83
83
 
84
84
  return data if response.status.success?
85
85
 
86
- raise Errors.const_get(data.dig(:error, :code).camelize), data[:error]
86
+ raise Errors.for(data.dig(:error, :code)), data[:error]
87
87
  end
88
88
 
89
89
  private
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # rubocop:disable Metrics/CyclomaticComplexity
3
+ # rubocop:disable-next Metrics/CyclomaticComplexity
4
4
  module HCloud
5
5
  # @!visibility private
6
6
  class ResourceType
@@ -84,7 +84,6 @@ module HCloud
84
84
  end
85
85
  end
86
86
  end
87
- # rubocop:enable Metrics/CyclomaticComplexity
88
87
 
89
88
  ActiveModel::Type.register(:action, HCloud::ResourceType.Type("HCloud::Action"))
90
89
  ActiveModel::Type.register(:algorithm, HCloud::ResourceType.Type("HCloud::Algorithm"))
@@ -92,12 +91,11 @@ ActiveModel::Type.register(:amount, HCloud::ResourceType.Type("HCloud::Amount"))
92
91
  ActiveModel::Type.register(:applied_to, HCloud::ResourceType.Type("HCloud::AppliedTo"))
93
92
  ActiveModel::Type.register(:applied_to_resource, HCloud::ResourceType.Type("HCloud::AppliedToResource"))
94
93
  ActiveModel::Type.register(:apply_to, HCloud::ResourceType.Type("HCloud::ApplyTo"))
94
+ ActiveModel::Type.register(:assignee, HCloud::ResourceType.Type("HCloud::Assignee"))
95
95
  ActiveModel::Type.register(:authoritative_nameservers, HCloud::ResourceType.Type("HCloud::AuthoritativeNameservers"))
96
96
  ActiveModel::Type.register(:certificate, HCloud::ResourceType.Type("HCloud::Certificate"))
97
97
  ActiveModel::Type.register(:certificate_status, HCloud::ResourceType.Type("HCloud::CertificateStatus"))
98
98
  ActiveModel::Type.register(:change_protection, HCloud::ResourceType.Type("HCloud::ChangeProtection"))
99
- ActiveModel::Type.register(:datacenter, HCloud::ResourceType.Type("HCloud::Datacenter"))
100
- ActiveModel::Type.register(:datacenter_server_type, HCloud::ResourceType.Type("HCloud::DatacenterServerType"))
101
99
  ActiveModel::Type.register(:deprecation, HCloud::ResourceType.Type("HCloud::Deprecation"))
102
100
  ActiveModel::Type.register(:dns_pointer, HCloud::ResourceType.Type("HCloud::DNSPointer"))
103
101
  ActiveModel::Type.register(:error, HCloud::ResourceType.Type("HCloud::Error"))
@@ -117,6 +115,7 @@ ActiveModel::Type.register(:label_selector, HCloud::ResourceType.Type("HCloud::L
117
115
  ActiveModel::Type.register(:load_balancer, HCloud::ResourceType.Type("HCloud::LoadBalancer"))
118
116
  ActiveModel::Type.register(:load_balancer_private_net, HCloud::ResourceType.Type("HCloud::LoadBalancerPrivateNet"))
119
117
  ActiveModel::Type.register(:load_balancer_public_net, HCloud::ResourceType.Type("HCloud::LoadBalancerPublicNet"))
118
+ ActiveModel::Type.register(:load_balancer_public_net_ip, HCloud::ResourceType.Type("HCloud::LoadBalancerPublicNetIP"))
120
119
  ActiveModel::Type.register(:load_balancer_type, HCloud::ResourceType.Type("HCloud::LoadBalancerType"))
121
120
  ActiveModel::Type.register(:load_balancer_type_price, HCloud::ResourceType.Type("HCloud::LoadBalancerTypePrice"))
122
121
  ActiveModel::Type.register(:location, HCloud::ResourceType.Type("HCloud::Location"))
@@ -139,7 +139,7 @@ module HCloud
139
139
  end
140
140
 
141
141
  def updatable_attributes
142
- [:name, :labels]
142
+ [:name, :description, :labels]
143
143
  end
144
144
  end
145
145
  end
@@ -95,10 +95,8 @@ module HCloud
95
95
  attribute :type
96
96
  attribute :status
97
97
 
98
- attribute :build_id
99
-
100
98
  attribute :disk_size, :integer
101
- attribute :image_size, :integer
99
+ attribute :image_size, :float
102
100
 
103
101
  attribute :os_flavor
104
102
  attribute :os_version
@@ -111,7 +109,8 @@ module HCloud
111
109
  # Explicitly specify created attribute, because resource is not Creatable
112
110
  attribute :created, :datetime
113
111
  attribute :deleted, :datetime
114
- attribute :deprecated, :datetime
112
+
113
+ attribute :deprecation, :deprecation
115
114
 
116
115
  attribute :rapid_deploy
117
116
 
@@ -126,7 +125,7 @@ module HCloud
126
125
  end
127
126
 
128
127
  def deprecated?
129
- deprecated.present?
128
+ deprecation.present?
130
129
  end
131
130
 
132
131
  def rapid_deploy?
@@ -40,6 +40,11 @@ module HCloud
40
40
  # load_balancer = HCloud::LoadBalancer.create(name: "my_load_balancer", algorithm: { type: "round_robin" }, load_balancer_type: "lb11", location: "nbg1")
41
41
  # # => #<HCloud::LoadBalancer id: 1, ...>
42
42
  #
43
+ # == Create load balancer with existing primary IPs
44
+ #
45
+ # load_balancer = HCloud::LoadBalancer.create(name: "my_load_balancer", load_balancer_type: "lb11", location: "nbg1", public_net: { ipv4: 1, ipv6: 2 })
46
+ # # => #<HCloud::LoadBalancer id: 1, ...>
47
+ #
43
48
  # == Update load balancer
44
49
  #
45
50
  # load_balancer = HCloud::LoadBalancer.find(1)
@@ -50,6 +55,7 @@ module HCloud
50
55
  #
51
56
  # load_balancer = HCloud::LoadBalancer.find(1)
52
57
  # load_balancer.delete
58
+ # # => #<HCloud::Action id: 1, command: "delete_load_balancer", ...>
53
59
  # load_balancer.deleted?
54
60
  # # => true
55
61
  #
@@ -188,7 +194,7 @@ module HCloud
188
194
  attribute :public_net, :load_balancer_public_net
189
195
 
190
196
  attribute :services, :service, array: true, default: -> { [] }
191
- attribute :targets, :target, array: true, default: -> { [] }
197
+ attribute :targets, :targets, array: true, default: -> { [] }
192
198
 
193
199
  attribute :protection, :protection
194
200
 
@@ -212,7 +218,7 @@ module HCloud
212
218
  action :disable_public_interface
213
219
 
214
220
  def creatable_attributes
215
- [:name, :labels, :algorithm, :network_zone, :public_interface, :services, :targets, load_balancer_type: [:id, :name], location: [:id, :name], network: [:id, :name]]
221
+ [:name, :labels, :algorithm, :network_zone, :public_interface, :public_net, :services, :targets, load_balancer_type: [:id, :name], location: [:id, :name], network: [:id, :name]]
216
222
  end
217
223
 
218
224
  def updatable_attributes
@@ -34,7 +34,5 @@ module HCloud
34
34
  attribute :prices, :price, array: true, default: -> { [] }
35
35
 
36
36
  attribute :deprecation, :deprecation
37
-
38
- attribute :deprecated, :datetime, deprecated: true
39
37
  end
40
38
  end
@@ -45,6 +45,26 @@ module HCloud
45
45
  # network.deleted?
46
46
  # # => true
47
47
  #
48
+ # == List network members
49
+ #
50
+ # HCloud::Network.find(1).members
51
+ # # => [#<HCloud::NetworkMember type: "server", id: 1, ...>, ...]
52
+ #
53
+ # == Sort network members
54
+ #
55
+ # HCloud::Network.find(1).members.sort(ip: :asc)
56
+ # # => [#<HCloud::NetworkMember type: "server", id: 1, ...>, ...]
57
+ #
58
+ # == Search network members
59
+ #
60
+ # HCloud::Network.find(1).members.where(type: "load_balancer", status: "ok")
61
+ # # => [#<HCloud::NetworkMember type: "load_balancer", id: 1, ...>, ...]
62
+ #
63
+ # == Get the attached resource of a network member
64
+ #
65
+ # HCloud::Network.find(1).members.first.resource
66
+ # # => #<HCloud::Server id: 1, ...>
67
+ #
48
68
  # = Actions
49
69
  # == List actions
50
70
  #
@@ -135,6 +155,24 @@ module HCloud
135
155
  action :delete_route
136
156
  action :delete_subnet
137
157
 
158
+ def members
159
+ raise Errors::MissingIDError unless id
160
+
161
+ Collection.new do |params|
162
+ response = client
163
+ .get("#{resource_path}/#{id}/members", params)
164
+
165
+ data = response
166
+ .fetch(:members)
167
+ .map { |attrs| NetworkMember.new attrs }
168
+
169
+ meta = response
170
+ .fetch(:meta)
171
+
172
+ [data, meta]
173
+ end
174
+ end
175
+
138
176
  def creatable_attributes
139
177
  [:name, :labels, :ip_range, :expose_routes_to_vswitch, :routes, :subnets]
140
178
  end
@@ -35,7 +35,7 @@ module HCloud
35
35
  #
36
36
  # == Create primary IP
37
37
  #
38
- # primary IP = HCloud::PrimaryIP.new(name: "my_primary_ip", type: "ipv4", datacenter: "fsn1-dc8")
38
+ # primary IP = HCloud::PrimaryIP.new(name: "my_primary_ip", type: "ipv4", location: "fsn1")
39
39
  # primary IP.create
40
40
  # primary IP.created?
41
41
  # # => true
@@ -120,13 +120,13 @@ module HCloud
120
120
  attribute :dns_ptr, :dns_pointer, array: true, default: -> { [] }
121
121
 
122
122
  attribute :assignee_id
123
- attribute :assignee_type, default: "server"
123
+ attribute :assignee_type
124
+
125
+ attribute :location, :location
124
126
 
125
127
  attribute :auto_delete, :boolean
126
128
  attribute :blocked, :boolean
127
129
 
128
- attribute :datacenter, :datacenter
129
-
130
130
  attribute :protection, :protection
131
131
 
132
132
  alias auto_delete? auto_delete
@@ -140,7 +140,7 @@ module HCloud
140
140
  action :change_protection
141
141
 
142
142
  def creatable_attributes
143
- [:name, :type, :assignee_id, :assignee_type, :auto_delete, :labels, datacenter: [:id, :name]]
143
+ [:name, :type, :assignee_id, :assignee_type, :auto_delete, :labels, location: [:id, :name]]
144
144
  end
145
145
 
146
146
  def updatable_attributes
@@ -92,7 +92,6 @@ module HCloud
92
92
  # TODO: use only for creation
93
93
  attribute :user_data
94
94
 
95
- # TODO: use only for creation
96
95
  attribute :location, :location
97
96
 
98
97
  # TODO: use only for creation
@@ -106,8 +105,6 @@ module HCloud
106
105
 
107
106
  attribute :root_password
108
107
 
109
- attribute :datacenter, :datacenter
110
-
111
108
  attribute :included_traffic, :integer
112
109
  attribute :ingoing_traffic, :integer
113
110
  attribute :outgoing_traffic, :integer
@@ -171,7 +168,7 @@ module HCloud
171
168
  action :request_console
172
169
 
173
170
  def creatable_attributes
174
- [:name, :automount, :start_after_create, :user_data, :labels, :public_net, datacenter: [:id, :name], image: [:id, :name], location: [:id, :name], server_type: [:id, :name], ssh_keys: [:id, :name], firewalls: :id, networks: :id, volumes: :id]
171
+ [:name, :automount, :start_after_create, :user_data, :labels, :public_net, image: [:id, :name], location: [:id, :name], server_type: [:id, :name], ssh_keys: [:id, :name], firewalls: :id, networks: :id, volumes: :id, placement_group: :id]
175
172
  end
176
173
 
177
174
  def updatable_attributes
@@ -40,9 +40,5 @@ module HCloud
40
40
  attribute :locations, :server_type_location, array: true, default: -> { [] }
41
41
 
42
42
  attribute :deprecation, :deprecation, deprecated: true
43
-
44
- attribute :deprecated, :boolean, deprecated: true
45
-
46
- alias deprecated? deprecated
47
43
  end
48
44
  end
@@ -84,12 +84,12 @@ module HCloud
84
84
  # # => #<HCloud::Action id: 1, ...>
85
85
  #
86
86
  # = Resource-specific actions
87
- # == Attach a volume to a server
87
+ # == Attach a volume
88
88
  #
89
- # HCloud::Volume.find(1).attach(server: 1, automount: false)
89
+ # HCloud::Volume.find(1).attach(assignee: { id: 1, type: "server" }, automount: false)
90
90
  # # => #<HCloud::Action id: 1, ...>
91
91
  #
92
- # == Detach a volume from a server
92
+ # == Detach a volume
93
93
  #
94
94
  # HCloud::Volume.find(1).detach
95
95
  # # => #<HCloud::Action id: 1, ...>
@@ -126,7 +126,9 @@ module HCloud
126
126
  attribute :location, :location
127
127
  attribute :protection, :protection
128
128
 
129
- attribute :server, :server
129
+ attribute :assignee, :assignee
130
+
131
+ attribute :server, :server, deprecated: true
130
132
 
131
133
  alias automount? automount
132
134
 
@@ -138,7 +140,7 @@ module HCloud
138
140
  action :change_protection
139
141
 
140
142
  def creatable_attributes
141
- [:name, :format, :size, :server, :automount, :labels, location: [:id, :name]]
143
+ [:name, :format, :size, :assignee, :server, :automount, :labels, location: [:id, :name]]
142
144
  end
143
145
 
144
146
  def updatable_attributes
@@ -3,8 +3,8 @@
3
3
  module HCloud
4
4
  # @!visibility private
5
5
  module Version
6
- MAJOR = 6
7
- MINOR = 1
6
+ MAJOR = 7
7
+ MINOR = 0
8
8
  PATCH = 0
9
9
  PRE = nil
10
10
 
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @!visibility private
4
+ module HTTP
5
+ # @!visibility private
6
+ module Features
7
+ # @!visibility private
8
+ class Compression < Feature
9
+ # @!visibility private
10
+ class BlockIO
11
+ def initialize(block)
12
+ @block = block
13
+ end
14
+
15
+ def write(data)
16
+ @block.call(data)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -14,7 +14,7 @@ module HTTP
14
14
  # @!visibility private
15
15
  module Features
16
16
  # @!visibility private
17
- module Request
17
+ class Compression < Feature
18
18
  # @!visibility private
19
19
  class BrotliBody < CompressedBody
20
20
  def compress(&block)
@@ -14,17 +14,20 @@ module HTTP
14
14
  # @!visibility private
15
15
  module Features
16
16
  # @!visibility private
17
- module Response
17
+ class Compression < Feature
18
18
  # @!visibility private
19
19
  class BrotliInflater < HTTP::Response::Inflater
20
20
  def readpartial(*)
21
21
  chunks = []
22
22
 
23
- while (chunk = @connection.readpartial(*))
24
- chunks << chunk
23
+ loop do
24
+ chunks << @connection.readpartial(*)
25
+ rescue EOFError
26
+ break
25
27
  end
26
28
 
27
- return if chunks.empty?
29
+ # Signal end of stream when no more data is left
30
+ raise EOFError if chunks.empty?
28
31
 
29
32
  Brotli.inflate(chunks.join)
30
33
  end
@@ -5,10 +5,12 @@ module HTTP
5
5
  # @!visibility private
6
6
  module Features
7
7
  # @!visibility private
8
- module Request
8
+ class Compression < Feature
9
9
  # @!visibility private
10
10
  class CompressedBody < HTTP::Request::Body
11
- def initialize(uncompressed_body) # rubocop:disable Lint/MissingSuper
11
+ def initialize(uncompressed_body)
12
+ super(nil)
13
+
12
14
  @body = uncompressed_body
13
15
  @compressed = nil
14
16
  end
@@ -7,7 +7,7 @@ module HTTP
7
7
  # @!visibility private
8
8
  module Features
9
9
  # @!visibility private
10
- module Response
10
+ class Compression < Feature
11
11
  # @!visibility private
12
12
  class GzipInflater < HTTP::Response::Inflater
13
13
  end
@@ -7,7 +7,7 @@ module HTTP
7
7
  # @!visibility private
8
8
  module Features
9
9
  # @!visibility private
10
- module Request
10
+ class Compression < Feature
11
11
  # @!visibility private
12
12
  class GzippedBody < CompressedBody
13
13
  def compress(&block)
@@ -15,10 +15,10 @@ module HTTP
15
15
 
16
16
  attr_reader :method
17
17
 
18
- def initialize(**)
19
- super
18
+ def initialize(method: "gzip")
19
+ super()
20
20
 
21
- @method = @opts.fetch(:method, "gzip").to_s
21
+ @method = method.to_s
22
22
 
23
23
  raise Error, "Only gzip and brotli methods are supported" unless SUPPORTED_ENCODING.key?(method)
24
24
  end
@@ -67,18 +67,18 @@ module HTTP
67
67
  def compress(body)
68
68
  case method
69
69
  when "gzip"
70
- Request::GzippedBody.new(body)
70
+ GzippedBody.new(body)
71
71
  when "brotli"
72
- Request::BrotliBody.new(body)
72
+ BrotliBody.new(body)
73
73
  end
74
74
  end
75
75
 
76
76
  def decompress(connection)
77
77
  case method
78
78
  when "gzip"
79
- HTTP::Response::Body.new(Response::GzipInflater.new(connection))
79
+ HTTP::Response::Body.new(GzipInflater.new(connection))
80
80
  when "brotli"
81
- HTTP::Response::Body.new(Response::BrotliInflater.new(connection))
81
+ HTTP::Response::Body.new(BrotliInflater.new(connection))
82
82
  end
83
83
  end
84
84
  end
@@ -6,7 +6,7 @@ module HTTP
6
6
  class RateLimiter < Feature
7
7
  attr_reader :limit, :remaining, :reset, :at, :rate
8
8
 
9
- def initialize(...)
9
+ def initialize
10
10
  super
11
11
 
12
12
  @limit = Float::INFINITY
@@ -23,15 +23,17 @@ module HTTP
23
23
  end
24
24
 
25
25
  def wrap_response(response)
26
- return response if response["RateLimit-Limit"].nil? || response["RateLimit-Remaining"].nil? || response["RateLimit-Reset"].nil?
26
+ headers = response.headers
27
+
28
+ return response if headers["RateLimit-Limit"].nil? || headers["RateLimit-Remaining"].nil? || headers["RateLimit-Reset"].nil?
27
29
 
28
30
  # Extract rate limits
29
- @limit = response["RateLimit-Limit"].to_i
30
- @remaining = response["RateLimit-Remaining"].to_i
31
- @reset = Time.at(response["RateLimit-Reset"].to_i)
31
+ @limit = headers["RateLimit-Limit"].to_i
32
+ @remaining = headers["RateLimit-Remaining"].to_i
33
+ @reset = Time.at(headers["RateLimit-Reset"].to_i)
32
34
 
33
35
  # Extract request date
34
- @at = Time.parse(response.headers["Date"])
36
+ @at = Time.parse(headers["Date"])
35
37
 
36
38
  # Calculate remaining increment rate
37
39
  @rate = 3600 / limit
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hetznercloud
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.1.0
4
+ version: 7.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Florian Dejonckheere
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-07-10 00:00:00.000000000 Z
11
+ date: 2026-11-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -42,16 +42,16 @@ dependencies:
42
42
  name: http
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ">="
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '0'
47
+ version: '6.0'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ">="
52
+ - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '0'
54
+ version: '6.0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: openssl
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -116,10 +116,10 @@ files:
116
116
  - lib/hcloud/entities/applied_to.rb
117
117
  - lib/hcloud/entities/applied_to_resource.rb
118
118
  - lib/hcloud/entities/apply_to.rb
119
+ - lib/hcloud/entities/assignee.rb
119
120
  - lib/hcloud/entities/authoritative_nameservers.rb
120
121
  - lib/hcloud/entities/certificate_status.rb
121
122
  - lib/hcloud/entities/change_protection.rb
122
- - lib/hcloud/entities/datacenter_server_type.rb
123
123
  - lib/hcloud/entities/deprecation.rb
124
124
  - lib/hcloud/entities/dns_pointer.rb
125
125
  - lib/hcloud/entities/error.rb
@@ -134,9 +134,11 @@ files:
134
134
  - lib/hcloud/entities/label_selector.rb
135
135
  - lib/hcloud/entities/load_balancer_private_net.rb
136
136
  - lib/hcloud/entities/load_balancer_public_net.rb
137
+ - lib/hcloud/entities/load_balancer_public_net_ip.rb
137
138
  - lib/hcloud/entities/load_balancer_type_price.rb
138
139
  - lib/hcloud/entities/metrics.rb
139
140
  - lib/hcloud/entities/nameserver.rb
141
+ - lib/hcloud/entities/network_member.rb
140
142
  - lib/hcloud/entities/price.rb
141
143
  - lib/hcloud/entities/primary_ip_prices.rb
142
144
  - lib/hcloud/entities/private_net.rb
@@ -175,7 +177,6 @@ files:
175
177
  - lib/hcloud/resource_type.rb
176
178
  - lib/hcloud/resources/action.rb
177
179
  - lib/hcloud/resources/certificate.rb
178
- - lib/hcloud/resources/datacenter.rb
179
180
  - lib/hcloud/resources/firewall.rb
180
181
  - lib/hcloud/resources/floating_ip.rb
181
182
  - lib/hcloud/resources/image.rb
@@ -200,13 +201,13 @@ files:
200
201
  - lib/hcloud/resources/zone.rb
201
202
  - lib/hcloud/sub_collection.rb
202
203
  - lib/hcloud/version.rb
203
- - lib/http/features/block_io.rb
204
204
  - lib/http/features/compression.rb
205
- - lib/http/features/request/brotli_body.rb
206
- - lib/http/features/request/compressed_body.rb
207
- - lib/http/features/request/gzipped_body.rb
208
- - lib/http/features/response/brotli_inflater.rb
209
- - lib/http/features/response/gzip_inflater.rb
205
+ - lib/http/features/compression/block_io.rb
206
+ - lib/http/features/compression/brotli_body.rb
207
+ - lib/http/features/compression/brotli_inflater.rb
208
+ - lib/http/features/compression/compressed_body.rb
209
+ - lib/http/features/compression/gzip_inflater.rb
210
+ - lib/http/features/compression/gzipped_body.rb
210
211
  - lib/http/mime_type/yaml.rb
211
212
  - lib/http/rate_limiter.rb
212
213
  homepage: https://github.com/floriandejonckheere/hcloud
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module HCloud
4
- class DatacenterServerType < Entity
5
- attribute :available,
6
- array: true,
7
- default: -> { [] },
8
- deprecated: true
9
-
10
- attribute :available_for_migration,
11
- array: true,
12
- default: -> { [] },
13
- deprecated: true
14
-
15
- attribute :supported,
16
- array: true,
17
- default: -> { [] },
18
- deprecated: true
19
- end
20
- end
@@ -1,55 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module HCloud
4
- ##
5
- # Represents a datacenter
6
- #
7
- # == List all datacenters
8
- #
9
- # HCloud::Datacenter.all
10
- # # => [#<HCloud::Datacenter id: 2, ...>, ...]
11
- #
12
- # == Sort datacenters
13
- #
14
- # HCloud::Datacenter.sort(name: :desc)
15
- # # => [#<HCloud::Datacenter id: 1, ...>, ...]
16
- #
17
- # HCloud::Datacenter.sort(:id, name: :asc)
18
- # # => [#<HCloud::Datacenter id: 1, ...>, ...]
19
- #
20
- # == Search datacenters
21
- #
22
- # HCloud::Datacenter.where(name: "fsn1-dc8")
23
- # # => #<HCloud::Datacenter id: 2, ...>
24
- #
25
- # == Find datacenter by ID
26
- #
27
- # HCloud::Datacenter.find(2)
28
- # # => #<HCloud::Datacenter id: 2, ...>
29
- #
30
- # == Get datacenter recommendation
31
- #
32
- # HCloud::Datacenter.recommendation
33
- # # => #<HCloud::Datacenter id: 2, ...>
34
- #
35
- class Datacenter < Resource
36
- deprecated since: "2026-10-01"
37
-
38
- queryable
39
-
40
- attribute :id, :integer
41
- attribute :name
42
- attribute :description
43
-
44
- attribute :location, :location
45
- attribute :server_types, :datacenter_server_type
46
-
47
- def self.recommendation
48
- warn "[DEPRECATION] Field \"recommendation\" on #{self.class.name} is deprecated."
49
-
50
- find client
51
- .get(resource_path)
52
- .fetch(:recommendation)
53
- end
54
- end
55
- end
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # @!visibility private
4
- module HTTP
5
- # @!visibility private
6
- module Features
7
- # @!visibility private
8
- class BlockIO
9
- def initialize(block)
10
- @block = block
11
- end
12
-
13
- def write(data)
14
- @block.call(data)
15
- end
16
- end
17
- end
18
- end