hetznercloud 1.3.1 → 1.5.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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +27 -6
  3. data/Gemfile +13 -0
  4. data/LICENSE.md +1 -1
  5. data/README.md +76 -30
  6. data/config/inflections.rb +6 -0
  7. data/lib/hcloud/client.rb +4 -3
  8. data/lib/hcloud/collection.rb +4 -2
  9. data/lib/hcloud/concerns/concerns.rb +4 -0
  10. data/lib/hcloud/concerns/creatable.rb +2 -0
  11. data/lib/hcloud/concerns/deletable.rb +2 -0
  12. data/lib/hcloud/concerns/labelable.rb +11 -0
  13. data/lib/hcloud/concerns/queryable.rb +2 -0
  14. data/lib/hcloud/concerns/updatable.rb +5 -1
  15. data/lib/hcloud/entities/primary_ip_prices.rb +8 -0
  16. data/lib/hcloud/entities/private_network.rb +15 -0
  17. data/lib/hcloud/entities/protection.rb +0 -2
  18. data/lib/hcloud/entities/server_protection.rb +11 -0
  19. data/lib/hcloud/errors.rb +8 -0
  20. data/lib/hcloud/http.rb +4 -2
  21. data/lib/hcloud/resource_type.rb +33 -2
  22. data/lib/hcloud/resources/action.rb +5 -6
  23. data/lib/hcloud/resources/certificate.rb +8 -6
  24. data/lib/hcloud/resources/datacenter.rb +1 -1
  25. data/lib/hcloud/resources/firewall.rb +7 -5
  26. data/lib/hcloud/resources/floating_ip.rb +7 -5
  27. data/lib/hcloud/resources/image.rb +6 -7
  28. data/lib/hcloud/resources/iso.rb +1 -1
  29. data/lib/hcloud/resources/load_balancer.rb +7 -5
  30. data/lib/hcloud/resources/load_balancer_type.rb +1 -1
  31. data/lib/hcloud/resources/location.rb +1 -1
  32. data/lib/hcloud/resources/metadata.rb +33 -0
  33. data/lib/hcloud/resources/network.rb +5 -3
  34. data/lib/hcloud/resources/placement_group.rb +7 -5
  35. data/lib/hcloud/resources/pricing.rb +2 -0
  36. data/lib/hcloud/resources/primary_ip.rb +150 -0
  37. data/lib/hcloud/resources/server.rb +9 -7
  38. data/lib/hcloud/resources/server_type.rb +1 -1
  39. data/lib/hcloud/resources/ssh_key.rb +8 -6
  40. data/lib/hcloud/resources/volume.rb +8 -6
  41. data/lib/hcloud/version.rb +2 -2
  42. data/lib/hcloud.rb +1 -1
  43. data/lib/http/mime_type/yaml.rb +23 -0
  44. data/lib/http/rate_limiter.rb +40 -0
  45. metadata +11 -129
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 78ef10de0c462ae7e1c5bf47c747d25a110551f817800b70b2aef7f5a045db5c
4
- data.tar.gz: d9833600b525f01a5879888809a401df07bb7120f2d42db2e43220e0897cbfb5
3
+ metadata.gz: 0a4ca2b02a62b66c5fc5024fcfc7d1c0614f29b7008fdb7b7d92ce9839b542c3
4
+ data.tar.gz: 26a182b4a53db2a04731130c5b74ffa6f92eaadf17ae0d69080c64b3a0a71c50
5
5
  SHA512:
6
- metadata.gz: 20a46b53f9caa94a730c512208817c3ec7eb871983ade5bdd8acf2c19f77089084a31648c7b10d4d25d7cf3004f440d6732729398836e437838315fb01ec1985
7
- data.tar.gz: 6cd6709f99a23259d4e82a93285aa415358cdc51dff6489911df5c35a06c6d85005c65eee5a42021bd69a665f33b620196d3657675122bd1c78da0d2d374c869
6
+ metadata.gz: e57fbe462b9805f2f6615f9ddef3ef9eb1b4498620d54537dc0d73a2000e34df78ed2b8493e12032fdb842cb632887fe1e7df826cffb87f8b630d99fa002c35e
7
+ data.tar.gz: 2ee09e22a6c9d4dafb745ab98f204bd2af649b26eaab35f2a67370250a47fd7ff6c1060e79fe3e785c736c7c9ed5d48725479856dfc1d879147c82a0f32de79d
data/CHANGELOG.md CHANGED
@@ -1,15 +1,36 @@
1
1
  # Changelog
2
2
 
3
- ## HCloud v1.3.1
3
+ ## HCloud v1.5.0 (2023-02-01)
4
4
 
5
- - Add missing Server#user_data
5
+ - Add pricing for Primary IPs
6
+ - Add server Metadata resource
7
+ - Add support for rate limits
8
+ - `Action#resources` now returns a list of resources instead of a list of hashes
9
+ - Separate server protection entity from other protection entity (only server protection includes `rebuild`)
10
+ - Add shorthand methods for `.first`, `.last`, `.count`, `.where`, `.sort`, `.each`, and `.empty?` on resource
11
+ - Add `label_selector:` argument to `HCloud::Collection`
6
12
 
7
- ## HCloud v1.3.0
13
+ ## HCloud v1.4.0 (2023-01-22)
14
+
15
+ - Implemented Primary IPs
16
+ - Implemented Primary IP Actions
17
+ - Allow calling `#update` with additional attributes to be updated
18
+ - Return the resource when calling `#create`, `#update` or `#delete`
19
+ - Add additional primary IP mismatch errors:
20
+ - `PrimaryIPAssigned`
21
+ - `PrimaryIPDatacenterMismatch`
22
+ - `PrimaryIPVersionMismatch`
23
+
24
+ ## HCloud v1.3.1 (2022-02-06)
25
+
26
+ - Add missing `Server#user_data`
27
+
28
+ ## HCloud v1.3.0 (2022-02-04)
8
29
 
9
30
  - Implemented Load Balancers
10
31
  - Implemented Load Balancer Types
11
32
 
12
- ## HCloud v1.2.0
33
+ ## HCloud v1.2.0 (2022-02-01)
13
34
 
14
35
  - Implemented Networks
15
36
  - Implemented Network Actions
@@ -17,10 +38,10 @@
17
38
  - Implemented Certificates
18
39
  - Implemented Certificate Actions
19
40
 
20
- ## HCloud v1.1.0
41
+ ## HCloud v1.1.0 (2022-01-30)
21
42
 
22
43
  - Implemented Firewall Actions
23
44
 
24
- ## HCloud v1.0.0
45
+ ## HCloud v1.0.0 (2022-01-16)
25
46
 
26
47
  Initial release
data/Gemfile CHANGED
@@ -4,3 +4,16 @@ source "https://rubygems.org"
4
4
 
5
5
  # Specify your gem's dependencies in hcloud.gemspec
6
6
  gemspec
7
+
8
+ group :development, :test do
9
+ gem "byebug", require: false
10
+ gem "dotenv", require: false
11
+ gem "ffaker", require: false
12
+ gem "rake", require: false
13
+ gem "rspec", require: false
14
+ gem "rubocop", require: false
15
+ gem "rubocop-performance", require: false
16
+ gem "rubocop-rspec", require: false
17
+ gem "timecop", require: false
18
+ gem "webmock", require: false
19
+ end
data/LICENSE.md CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2021 Florian Dejonckheere
3
+ Copyright (c) 2023 Florian Dejonckheere
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -69,35 +69,82 @@ It expects you to use it in a sane way.
69
69
 
70
70
  ## Features
71
71
 
72
- Not all Hetzner Cloud API endpoints have been implemented yet.
73
-
74
- | Resource | State |
75
- |-----------------------|-----------------------|
76
- | Actions | Implemented |
77
- | Certificates | Implemented |
78
- | Certificate Actions | Implemented |
79
- | Datacenters | Implemented |
80
- | Firewalls | Implemented |
81
- | Firewall Actions | Implemented |
82
- | Floating IPs | Implemented |
83
- | Floating IP Actions | Implemented |
84
- | Images | Implemented |
85
- | Image Actions | Implemented |
86
- | ISOs | Implemented |
87
- | Load Balancers | Implemented |
88
- | Load Balancer Actions | Not implemented |
89
- | Load Balancer Types | Implemented |
90
- | Locations | Implemented |
91
- | Networks | Implemented |
92
- | Network Actions | Implemented |
93
- | Placement Groups | Implemented |
94
- | Pricing | Implemented |
95
- | Servers | Partially implemented |
96
- | Server Actions | Not implemented |
97
- | Server Types | Implemented |
98
- | SSH Keys | Implemented |
99
- | Volumes | Implemented |
100
- | Volume Actions | Implemented |
72
+ The following table lists the Hetzner Cloud API endpoints that are currently implemented.
73
+
74
+ | Resource | State |
75
+ |----------------------------------------------------------------|-----------------------|
76
+ | [Actions](lib/hcloud/resources/action.rb) | Implemented |
77
+ | [Certificates](lib/hcloud/resources/certificate.rb) | Implemented |
78
+ | [Certificate Actions](lib/hcloud/resources/certificate.rb) | Implemented |
79
+ | [Datacenters](lib/hcloud/resources/datacenter.rb) | Implemented |
80
+ | [Firewalls](lib/hcloud/resources/firewall.rb) | Implemented |
81
+ | [Firewall Actions](lib/hcloud/resources/firewall.rb) | Implemented |
82
+ | [Floating IPs](lib/hcloud/resources/floating_ip.rb) | Implemented |
83
+ | [Floating IP Actions](lib/hcloud/resources/floating_ip.rb) | Implemented |
84
+ | [Images](lib/hcloud/resources/image.rb) | Implemented |
85
+ | [Image Actions](lib/hcloud/resources/image.rb) | Implemented |
86
+ | [ISOs](lib/hcloud/resources/iso.rb) | Implemented |
87
+ | [Load Balancers](lib/hcloud/resources/load_balancer.rb) | Implemented |
88
+ | [Load Balancer Actions](lib/hcloud/resources/load_balancer.rb) | Not implemented |
89
+ | [Load Balancer Types](lib/hcloud/resources/load_balancer.rb) | Implemented |
90
+ | [Locations](lib/hcloud/resources/location.rb) | Implemented |
91
+ | [Primary IPs](lib/hcloud/resources/primary_ip.rb) | Implemented |
92
+ | [Primary IP Actions](lib/hcloud/resources/primary_ip.rb) | Implemented |
93
+ | [Networks](lib/hcloud/resources/network.rb) | Implemented |
94
+ | [Network Actions](lib/hcloud/resources/network.rb) | Implemented |
95
+ | [Placement Groups](lib/hcloud/resources/placement_group.rb) | Implemented |
96
+ | [Pricing](lib/hcloud/resources/pricing.rb) | Implemented |
97
+ | [Servers](lib/hcloud/resources/server.rb) | Partially implemented |
98
+ | [Server Actions](lib/hcloud/resources/server.rb) | Not implemented |
99
+ | [Server Types](lib/hcloud/resources/server_type.rb) | Implemented |
100
+ | [SSH Keys](lib/hcloud/resources/ssh_key.rb) | Implemented |
101
+ | [Volumes](lib/hcloud/resources/volume.rb) | Implemented |
102
+ | [Volume Actions](lib/hcloud/resources/volume.rb) | Implemented |
103
+ | | |
104
+ | [Metadata](lib/hcloud/resources/metadata.rb) | Implemented |
105
+
106
+ ### Pagination
107
+
108
+ Paginated resources are wrapped in a `HCloud::Collection` that automatically fetches the next page when needed.
109
+ The collection acts as a (lazy) enumerator.
110
+ Call `to_a` to fetch all pages and parse all resources.
111
+
112
+ ### Rate limiting
113
+
114
+ From the [documentation](https://docs.hetzner.cloud/#rate-limiting):
115
+
116
+ > The default limit is 3600 requests per hour and per Project.
117
+ > The number of remaining requests increases gradually.
118
+ > For example, when your limit is 3600 requests per hour, the number of remaining requests will increase by 1 every second.
119
+
120
+ The client is able to handle the rate limiting by delaying the requests if necessary and executing them whenever possible.
121
+ To enable this behaviour, pass `rate_limit: true` as argument to `HCloud::Client.new`.
122
+ Client calls will block until possible to execute and then return.
123
+
124
+ ```ruby
125
+ client = HCloud::Client.new(access_token: "my_token", rate_limit: true)
126
+
127
+ # At least one request has to be made to enable the rate limiter
128
+ client.rate_limiter.limit # => nil
129
+ client.rate_limiter.remaining # => nil
130
+ client.rate_limiter.reset # => nil
131
+
132
+ HCloud::Server.create(...)
133
+
134
+ client.rate_limiter.limit # => 3600
135
+ client.rate_limiter.remaining # => 3599
136
+ client.rate_limiter.reset # => 2023-01-01 00:00:00 +0100
137
+
138
+ # Make a bunch of requests
139
+
140
+ client.rate_limiter.remaining # => 0
141
+
142
+ servers = HCloud::Server.all # Will block until remaining requests have regenerated (1 second by default) and then execute
143
+ ssh_keys = HCloud::SSHKey.all # Will block until remaining requests have regenerated (1 second by default) and then execute
144
+ ```
145
+
146
+ Since rate limits are per hour and per project, using multiple clients at the same time will interfere with the rate limiting mechanism.
147
+ To prevent this, wrap client calls in a loop that retries the call after it fails with a `HCloud::RateLimitExceeded` error.
101
148
 
102
149
  ## Testing
103
150
 
@@ -128,7 +175,6 @@ When executing the test suite, set `LOG_LEVEL` environment variable to `debug` i
128
175
 
129
176
  `HCloud::Client` also accepts an alternate endpoint:
130
177
 
131
-
132
178
  ```ruby
133
179
  client = HCloud::Client.new(access_token: "my_access_token", endpoint: "https://myproxy/v1")
134
180
  ```
@@ -14,15 +14,21 @@ HCloud.loader.inflector.inflect(
14
14
  "ipv6" => "IPv6",
15
15
  "iso" => "ISO",
16
16
  "iso_type" => "ISOType",
17
+ "primary_ip" => "PrimaryIP",
18
+ "primary_ip_prices" => "PrimaryIPPrices",
17
19
  "service_http" => "ServiceHTTP",
18
20
  "ssh_key" => "SSHKey",
19
21
  "target_ip" => "TargetIP",
22
+ "yaml" => "YAML",
20
23
  )
21
24
 
22
25
  ActiveSupport::Inflector.inflections(:en) do |inflect|
23
26
  inflect.acronym "DNS"
24
27
  inflect.acronym "HTTP"
25
28
  inflect.acronym "IP"
29
+ inflect.acronym "IPv4"
30
+ inflect.acronym "IPv6"
26
31
  inflect.acronym "ISO"
27
32
  inflect.acronym "JSON"
33
+ inflect.acronym "YAML"
28
34
  end
data/lib/hcloud/client.rb CHANGED
@@ -19,12 +19,13 @@ module HCloud
19
19
 
20
20
  self.connection = NilConnection.new
21
21
 
22
- attr_reader :access_token, :endpoint, :logger
22
+ attr_reader :access_token, :endpoint, :logger, :rate_limit
23
23
 
24
- def initialize(access_token:, endpoint: "https://api.hetzner.cloud/v1", logger: Logger.new("/dev/null"))
24
+ def initialize(access_token:, endpoint: "https://api.hetzner.cloud/v1", logger: Logger.new("/dev/null"), rate_limit: false)
25
25
  @access_token = access_token
26
26
  @endpoint = endpoint
27
27
  @logger = logger
28
+ @rate_limit = rate_limit
28
29
  end
29
30
 
30
31
  delegate :get, :put, :post, :delete, to: :http
@@ -32,7 +33,7 @@ module HCloud
32
33
  private
33
34
 
34
35
  def http
35
- @http ||= HTTP.new(access_token, endpoint, logger)
36
+ @http ||= HTTP.new(access_token, endpoint, logger, rate_limit)
36
37
  end
37
38
  end
38
39
  end
@@ -6,7 +6,7 @@ module HCloud
6
6
 
7
7
  attr_accessor :page, :previous_page, :next_page, :last_page, :per_page, :total_entries, :proc
8
8
 
9
- attr_reader :sort_by, :filter_by
9
+ attr_reader :sort_by, :filter_by, :label_selector
10
10
 
11
11
  def initialize(&block)
12
12
  @proc = block
@@ -24,8 +24,9 @@ module HCloud
24
24
  self
25
25
  end
26
26
 
27
- def where(**filter_by)
27
+ def where(label_selector: nil, **filter_by)
28
28
  @filter_by = filter_by
29
+ @label_selector = label_selector
29
30
 
30
31
  self
31
32
  end
@@ -70,6 +71,7 @@ module HCloud
70
71
  page: page,
71
72
  per_page: per_page,
72
73
  sort: sort_by,
74
+ label_selector: label_selector&.map { |k, v| "#{k}=#{v}" }&.join(","),
73
75
  **filter_by,
74
76
  }
75
77
  end
@@ -32,6 +32,10 @@ module HCloud
32
32
  def singleton
33
33
  include Singleton
34
34
  end
35
+
36
+ def labelable
37
+ include Labelable
38
+ end
35
39
  end
36
40
  end
37
41
  end
@@ -12,6 +12,8 @@ module HCloud
12
12
  .post("/#{resource_name.pluralize}", creatable_params)
13
13
  .tap { |r| r[resource_name.to_sym].merge!(r.slice(:root_password)) }
14
14
  .fetch(resource_name.to_sym)
15
+
16
+ self
15
17
  end
16
18
 
17
19
  def created?
@@ -12,6 +12,8 @@ module HCloud
12
12
  .delete("/#{resource_name.pluralize}/#{id}")
13
13
 
14
14
  @deleted = true
15
+
16
+ self
15
17
  end
16
18
 
17
19
  def deleted?
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HCloud
4
+ module Labelable
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ attribute :labels, default: -> { {} }
9
+ end
10
+ end
11
+ end
@@ -17,6 +17,8 @@ module HCloud
17
17
  end
18
18
 
19
19
  class_methods do
20
+ delegate :first, :last, :count, :where, :sort, :each, :empty?, :any?, to: :all
21
+
20
22
  def find(id)
21
23
  raise Errors::MissingIDError unless id
22
24
 
@@ -5,12 +5,16 @@ module HCloud
5
5
  extend ActiveSupport::Concern
6
6
 
7
7
  included do
8
- def update
8
+ def update(**attributes)
9
9
  raise Errors::MissingIDError unless id
10
10
 
11
+ assign_attributes attributes
12
+
11
13
  assign_attributes client
12
14
  .put("/#{resource_name.pluralize}/#{id}", updatable_params)
13
15
  .fetch(resource_name.to_sym)
16
+
17
+ self
14
18
  end
15
19
 
16
20
  def updatable_attributes
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HCloud
4
+ class PrimaryIPPrices < Entity
5
+ attribute :type
6
+ attribute :prices, :price
7
+ end
8
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HCloud
4
+ class PrivateNetwork < Entity
5
+ attribute :ip
6
+ attribute :alias_ips, array: true, default: -> { [] }
7
+ attribute :interface_num, :integer
8
+ attribute :mac_address
9
+ attribute :network_id, :integer
10
+ attribute :network_name
11
+ attribute :network
12
+ attribute :subnet
13
+ attribute :gateway
14
+ end
15
+ end
@@ -3,9 +3,7 @@
3
3
  module HCloud
4
4
  class Protection < Entity
5
5
  attribute :delete, :boolean
6
- attribute :rebuild, :boolean
7
6
 
8
7
  alias delete? delete
9
- alias rebuild? rebuild
10
8
  end
11
9
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HCloud
4
+ class ServerProtection < Entity
5
+ attribute :delete, :boolean
6
+ attribute :rebuild, :boolean
7
+
8
+ alias delete? delete
9
+ alias rebuild? rebuild
10
+ end
11
+ end
data/lib/hcloud/errors.rb CHANGED
@@ -43,6 +43,10 @@ module HCloud
43
43
  class NotFound < Error; end
44
44
  class PlacementError < Error; end
45
45
  class PlacementError < Error; end
46
+ class PrimaryIPAssigned < Error; end
47
+ class PrimaryIPAlreadyAssigned < Error; end
48
+ class PrimaryIPDatacenterMismatch < Error; end
49
+ class PrimaryIPVersionMismatch < Error; end
46
50
  class Protected < Error; end
47
51
  class RateLimitExceeded < Error; end
48
52
  class ResourceInUse < Error; end
@@ -52,7 +56,11 @@ module HCloud
52
56
  class ServerAlreadyAdded < Error; end
53
57
  class ServerAlreadyAttached < Error; end
54
58
  class ServerError < Error; end
59
+ class ServerHasIPv4 < Error; end
60
+ class ServerHasIPv6 < Error; end
61
+ class ServerIsLoadBalancerTarget < Error; end
55
62
  class ServerNotAttachedToNetwork < Error; end
63
+ class ServerNotStopped < Error; end
56
64
  class ServiceError < Error; end
57
65
  class SourcePortAlreadyUsed < Error; end
58
66
  class TargetAlreadyDefined < Error; end
data/lib/hcloud/http.rb CHANGED
@@ -4,12 +4,13 @@ require "http"
4
4
 
5
5
  module HCloud
6
6
  class HTTP
7
- attr_reader :access_token, :endpoint, :logger, :timeout
7
+ attr_reader :access_token, :endpoint, :logger, :rate_limit, :timeout
8
8
 
9
- def initialize(access_token, endpoint, logger, timeout = 10)
9
+ def initialize(access_token, endpoint, logger, rate_limit = false, timeout = 10)
10
10
  @access_token = access_token
11
11
  @endpoint = endpoint
12
12
  @logger = logger
13
+ @rate_limit = rate_limit
13
14
  @timeout = timeout
14
15
  end
15
16
 
@@ -80,6 +81,7 @@ module HCloud
80
81
  .headers(accept: "application/json", user_agent: "#{HCloud::NAME}/#{HCloud::VERSION}")
81
82
  .timeout(timeout)
82
83
  .use(logging: { logger: logger })
84
+ .then { |h| rate_limit ? h.use(:rate_limiter) : h }
83
85
  .encoding("utf-8")
84
86
  .auth("Bearer #{access_token}")
85
87
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # rubocop:disable Metrics/CyclomaticComplexity,Metrics/AbcSize
3
4
  module HCloud
4
5
  class ResourceType
5
6
  class_attribute :resource_class_name
@@ -10,7 +11,6 @@ module HCloud
10
11
  @array = array
11
12
  end
12
13
 
13
- # rubocop:disable Metrics/CyclomaticComplexity
14
14
  def cast(value)
15
15
  case value
16
16
  when nil, []
@@ -28,12 +28,13 @@ module HCloud
28
28
  when Hash # Attribute hash
29
29
  resource_class.new(value)
30
30
  when Array # List
31
+ raise ArgumentError, "cannot cast value: #{value} for type #{resource_class_name}" unless array?
32
+
31
33
  value.map { |v| cast(v) }
32
34
  else
33
35
  raise ArgumentError, "cannot cast value: #{value} for type #{resource_class_name}"
34
36
  end
35
37
  end
36
- # rubocop:enable Metrics/CyclomaticComplexity
37
38
 
38
39
  def resource_class
39
40
  @resource_class ||= resource_class_name.constantize
@@ -52,8 +53,32 @@ module HCloud
52
53
  .tap { |klass| HCloud.const_set(:"#{class_name.demodulize}ResourceType", klass) }
53
54
  end
54
55
  # rubocop:enable Naming/MethodName
56
+
57
+ class GenericType < ResourceType
58
+ def cast(value)
59
+ case value
60
+ when nil, []
61
+ array? ? [] : nil
62
+ when HCloud::Resource # Class
63
+ value
64
+ when Hash # Attribute hash
65
+ ActiveModel::Type
66
+ .lookup(value.fetch(:type).to_sym)
67
+ .cast(value.except(:type))
68
+ when Array # List
69
+ raise ArgumentError, "cannot cast value: #{value} for generic type" unless array?
70
+
71
+ value.map { |v| cast(v) }
72
+ else
73
+ raise ArgumentError, "cannot cast value: #{value} for generic type"
74
+ end
75
+ rescue KeyError => e
76
+ raise ArgumentError, "cannot cast value: #{value} for generic type: #{e.message}"
77
+ end
78
+ end
55
79
  end
56
80
  end
81
+ # rubocop:enable Metrics/CyclomaticComplexity,Metrics/AbcSize
57
82
 
58
83
  ActiveModel::Type.register(:action, HCloud::ResourceType.Type("HCloud::Action"))
59
84
  ActiveModel::Type.register(:algorithm, HCloud::ResourceType.Type("HCloud::Algorithm"))
@@ -86,17 +111,23 @@ ActiveModel::Type.register(:load_balancer_public_net, HCloud::ResourceType.Type(
86
111
  ActiveModel::Type.register(:load_balancer_type, HCloud::ResourceType.Type("HCloud::LoadBalancerType"))
87
112
  ActiveModel::Type.register(:load_balancer_type_price, HCloud::ResourceType.Type("HCloud::LoadBalancerTypePrice"))
88
113
  ActiveModel::Type.register(:location, HCloud::ResourceType.Type("HCloud::Location"))
114
+ ActiveModel::Type.register(:metadata, HCloud::ResourceType.Type("HCloud::Metadata"))
89
115
  ActiveModel::Type.register(:network, HCloud::ResourceType.Type("HCloud::Network"))
90
116
  ActiveModel::Type.register(:placement_group, HCloud::ResourceType.Type("HCloud::PlacementGroup"))
91
117
  ActiveModel::Type.register(:price, HCloud::ResourceType.Type("HCloud::Price"))
92
118
  ActiveModel::Type.register(:pricing, HCloud::ResourceType.Type("HCloud::Pricing"))
119
+ ActiveModel::Type.register(:primary_ip, HCloud::ResourceType.Type("HCloud::PrimaryIP"))
120
+ ActiveModel::Type.register(:primary_ip_prices, HCloud::ResourceType.Type("HCloud::PrimaryIPPrices"))
93
121
  ActiveModel::Type.register(:private_net, HCloud::ResourceType.Type("HCloud::PrivateNet"))
122
+ ActiveModel::Type.register(:private_network, HCloud::ResourceType.Type("HCloud::PrivateNetwork"))
94
123
  ActiveModel::Type.register(:protection, HCloud::ResourceType.Type("HCloud::Protection"))
95
124
  ActiveModel::Type.register(:public_net, HCloud::ResourceType.Type("HCloud::PublicNet"))
125
+ ActiveModel::Type.register(:resource, HCloud::ResourceType::GenericType)
96
126
  ActiveModel::Type.register(:route, HCloud::ResourceType.Type("HCloud::Route"))
97
127
  ActiveModel::Type.register(:rule, HCloud::ResourceType.Type("HCloud::Rule"))
98
128
  ActiveModel::Type.register(:server, HCloud::ResourceType.Type("HCloud::Server"))
99
129
  ActiveModel::Type.register(:server_backup_price, HCloud::ResourceType.Type("HCloud::ServerBackupPrice"))
130
+ ActiveModel::Type.register(:server_protection, HCloud::ResourceType.Type("HCloud::ServerProtection"))
100
131
  ActiveModel::Type.register(:server_type, HCloud::ResourceType.Type("HCloud::ServerType"))
101
132
  ActiveModel::Type.register(:server_type_price, HCloud::ResourceType.Type("HCloud::ServerTypePrice"))
102
133
  ActiveModel::Type.register(:service, HCloud::ResourceType.Type("HCloud::Service"))
@@ -11,18 +11,18 @@ module HCloud
11
11
  #
12
12
  # == Sort actions
13
13
  #
14
- # HCloud::Action.all.sort(command: :desc)
14
+ # HCloud::Action.sort(command: :desc)
15
15
  # # => [#<HCloud::Action id: 1, ...>, ...]
16
16
  #
17
- # HCloud::Action.all.sort(:id, command: :asc)
17
+ # HCloud::Action.sort(:id, command: :asc)
18
18
  # # => [#<HCloud::Action id: 1, ...>, ...]
19
19
  #
20
20
  # == Search actions
21
21
  #
22
- # HCloud::Action.all.where(command: "my_action")
22
+ # HCloud::Action.where(command: "my_action")
23
23
  # # => #<HCloud::Action id: 1, ...>
24
24
  #
25
- # HCloud::Action.all.where(status: "success")
25
+ # HCloud::Action.where(status: "success")
26
26
  # # => #<HCloud::Action id: 1, ...>
27
27
  #
28
28
  # == Find action by ID
@@ -43,7 +43,6 @@ module HCloud
43
43
  attribute :status
44
44
  attribute :error, :error
45
45
 
46
- # TODO: return array of resources
47
- attribute :resources, array: true, default: -> { [] }
46
+ attribute :resources, :resource, array: true, default: -> { [] }
48
47
  end
49
48
  end
@@ -11,18 +11,21 @@ module HCloud
11
11
  #
12
12
  # == Sort certificates
13
13
  #
14
- # HCloud::Certificate.all.sort(name: :desc)
14
+ # HCloud::Certificate.sort(name: :desc)
15
15
  # # => [#<HCloud::Certificate id: 1, ...>, ...]
16
16
  #
17
- # HCloud::Certificate.all.sort(:id, name: :asc)
17
+ # HCloud::Certificate.sort(:id, name: :asc)
18
18
  # # => [#<HCloud::Certificate id: 1, ...>, ...]
19
19
  #
20
20
  # == Search certificates
21
21
  #
22
- # HCloud::Certificate.all.where(name: "my_certificate")
22
+ # HCloud::Certificate.where(name: "my_certificate")
23
23
  # # => #<HCloud::Certificate id: 1, ...>
24
24
  #
25
- # HCloud::Certificate.all.where(type: "uploaded")
25
+ # HCloud::Certificate.where(type: "uploaded")
26
+ # # => #<HCloud::Certificate id: 1, ...>
27
+ #
28
+ # HCloud::Certificate.where(label_selector: { environment: "production" })
26
29
  # # => #<HCloud::Certificate id: 1, ...>
27
30
  #
28
31
  # == Find certificate by ID
@@ -94,6 +97,7 @@ module HCloud
94
97
  creatable
95
98
  updatable
96
99
  deletable
100
+ labelable
97
101
 
98
102
  attribute :id, :integer
99
103
  attribute :name
@@ -116,8 +120,6 @@ module HCloud
116
120
  # TODO: dynamically return resource classes
117
121
  attribute :used_by, :used_by, array: true, default: -> { [] }
118
122
 
119
- attribute :labels, default: -> { {} }
120
-
121
123
  action :retry
122
124
 
123
125
  def creatable_attributes
@@ -11,7 +11,7 @@ module HCloud
11
11
  #
12
12
  # == Search datacenters
13
13
  #
14
- # HCloud::Datacenter.all.where(name: "fsn1-dc8")
14
+ # HCloud::Datacenter.where(name: "fsn1-dc8")
15
15
  # # => #<HCloud::Datacenter id: 2, ...>
16
16
  #
17
17
  # == Find datacenter by ID