hcloud 0.1.2 → 1.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.
- checksums.yaml +5 -5
- data/.gitignore +0 -1
- data/.rubocop.yml +24 -9
- data/.rubocop_todo.yml +18 -121
- data/.travis.yml +3 -3
- data/CHANGELOG.md +13 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +143 -0
- data/README.md +34 -1
- data/Rakefile +2 -0
- data/bin/console +1 -0
- data/hcloud.gemspec +5 -3
- data/lib/hcloud.rb +31 -5
- data/lib/hcloud/abstract_resource.rb +162 -55
- data/lib/hcloud/action.rb +8 -10
- data/lib/hcloud/action_resource.rb +3 -29
- data/lib/hcloud/client.rb +66 -28
- data/lib/hcloud/datacenter.rb +7 -7
- data/lib/hcloud/datacenter_resource.rb +6 -31
- data/lib/hcloud/entry_loader.rb +181 -20
- data/lib/hcloud/errors.rb +2 -0
- data/lib/hcloud/floating_ip.rb +18 -29
- data/lib/hcloud/floating_ip_resource.rb +15 -30
- data/lib/hcloud/image.rb +12 -32
- data/lib/hcloud/image_resource.rb +7 -38
- data/lib/hcloud/iso.rb +4 -1
- data/lib/hcloud/iso_resource.rb +7 -28
- data/lib/hcloud/location.rb +3 -9
- data/lib/hcloud/location_resource.rb +6 -27
- data/lib/hcloud/network.rb +33 -0
- data/lib/hcloud/network_resource.rb +25 -0
- data/lib/hcloud/pagination.rb +2 -9
- data/lib/hcloud/server.rb +37 -70
- data/lib/hcloud/server_resource.rb +16 -36
- data/lib/hcloud/server_type.rb +3 -10
- data/lib/hcloud/server_type_resource.rb +6 -28
- data/lib/hcloud/ssh_key.rb +6 -17
- data/lib/hcloud/ssh_key_resource.rb +13 -32
- data/lib/hcloud/typhoeus_ext.rb +112 -0
- data/lib/hcloud/version.rb +3 -1
- data/lib/hcloud/volume.rb +32 -0
- data/lib/hcloud/volume_resource.rb +29 -0
- metadata +31 -13
- data/lib/hcloud/multi_reply.rb +0 -21
data/lib/hcloud/datacenter.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Hcloud
|
2
4
|
class Datacenter
|
3
|
-
|
4
|
-
id: nil,
|
5
|
-
name: nil,
|
6
|
-
description: nil,
|
7
|
-
location: Location,
|
8
|
-
server_types: nil
|
9
|
-
}.freeze
|
5
|
+
require 'hcloud/datacenter_resource'
|
10
6
|
|
11
7
|
include EntryLoader
|
8
|
+
|
9
|
+
schema(
|
10
|
+
location: Location
|
11
|
+
)
|
12
12
|
end
|
13
13
|
end
|
@@ -1,44 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Hcloud
|
2
4
|
class DatacenterResource < AbstractResource
|
3
|
-
|
5
|
+
filter_attributes :name
|
4
6
|
|
5
|
-
|
6
|
-
j = Oj.load(request('datacenters').run.body)
|
7
|
-
dc = j['datacenters'].map { |x| Datacenter.new(x, self, client) }
|
8
|
-
dc.reject { |x| x.id == j['recommendation'] }.unshift(
|
9
|
-
dc.find { |x| x.id == j['recommendation'] }
|
10
|
-
)
|
11
|
-
end
|
7
|
+
bind_to Datacenter
|
12
8
|
|
13
9
|
def recommended
|
14
10
|
all.first
|
15
11
|
end
|
16
12
|
|
17
|
-
def find(id)
|
18
|
-
Datacenter.new(
|
19
|
-
Oj.load(request("datacenters/#{id}").run.body)['datacenter'],
|
20
|
-
self,
|
21
|
-
client
|
22
|
-
)
|
23
|
-
end
|
24
|
-
|
25
|
-
def find_by(name:)
|
26
|
-
x = Oj.load(request('datacenters', q: { name: name }).run.body)['datacenters']
|
27
|
-
return nil if x.none?
|
28
|
-
x.each do |s|
|
29
|
-
return Datacenter.new(s, self, client)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
13
|
def [](arg)
|
34
14
|
case arg
|
35
|
-
when Integer
|
36
|
-
|
37
|
-
find(arg)
|
38
|
-
rescue Error::NotFound
|
39
|
-
end
|
40
|
-
when String
|
41
|
-
find_by(name: arg)
|
15
|
+
when Integer then find_by(id: arg)
|
16
|
+
when String then find_by(name: arg)
|
42
17
|
end
|
43
18
|
end
|
44
19
|
end
|
data/lib/hcloud/entry_loader.rb
CHANGED
@@ -1,36 +1,197 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'active_support/concern'
|
4
|
+
require 'active_model'
|
5
|
+
|
2
6
|
module Hcloud
|
3
|
-
module EntryLoader
|
7
|
+
module EntryLoader # rubocop:disable Metrics/ModuleLength
|
4
8
|
extend ActiveSupport::Concern
|
5
9
|
|
10
|
+
module Collection
|
11
|
+
attr_accessor :response
|
12
|
+
end
|
13
|
+
|
6
14
|
included do |klass|
|
7
|
-
klass.send(:
|
8
|
-
klass.
|
9
|
-
klass.send(:attr_accessor, attribute)
|
10
|
-
end
|
15
|
+
klass.send(:attr_writer, :client)
|
16
|
+
klass.include(ActiveModel::Dirty)
|
11
17
|
end
|
12
18
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
19
|
+
class_methods do # rubocop:disable Metrics/BlockLength
|
20
|
+
attr_accessor :resource_url
|
21
|
+
|
22
|
+
def _callbacks
|
23
|
+
@_callbacks ||= { before: [], after: [] }
|
24
|
+
end
|
25
|
+
|
26
|
+
%i[before after].each do |method|
|
27
|
+
define_method(method) { |&block| _callbacks[method] << block }
|
28
|
+
end
|
29
|
+
|
30
|
+
def schema(**kwargs)
|
31
|
+
@schema ||= {}.with_indifferent_access
|
32
|
+
return @schema if kwargs.empty?
|
33
|
+
|
34
|
+
@schema.merge!(kwargs)
|
35
|
+
end
|
36
|
+
|
37
|
+
def updatable(*args)
|
38
|
+
define_attribute_methods(*args)
|
39
|
+
args.each do |updateable_attribute|
|
40
|
+
define_method(updateable_attribute) { _attributes[updateable_attribute] }
|
41
|
+
define_method("#{updateable_attribute}=") do |value|
|
42
|
+
if _attributes[updateable_attribute] != value
|
43
|
+
public_send("#{updateable_attribute}_will_change!")
|
44
|
+
end
|
45
|
+
_update_attribute(updateable_attribute, value)
|
23
46
|
end
|
24
|
-
|
25
|
-
|
26
|
-
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def destructible
|
51
|
+
define_method(:destroy) { prepare_request(method: :delete) }
|
52
|
+
end
|
53
|
+
|
54
|
+
def protectable(*args)
|
55
|
+
define_method(:change_protection) do |**kwargs|
|
56
|
+
kwargs.keys.each do |key|
|
57
|
+
next if args.map(&:to_s).include? key.to_s
|
58
|
+
|
59
|
+
raise ArgumentError, "#{key} not an allowed protection mode (allowed: #{args})"
|
27
60
|
end
|
61
|
+
prepare_request('actions/change_protection', j: kwargs)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def has_actions # rubocop:disable Naming/PredicateName
|
66
|
+
define_method(:actions) do
|
67
|
+
ActionResource.new(client: client, base_path: resource_url)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def resource_class
|
72
|
+
ancestors.reverse.find { |const| const.include?(Hcloud::EntryLoader) }
|
73
|
+
end
|
74
|
+
|
75
|
+
def from_response(response, autoload_action: nil)
|
76
|
+
attributes = response.resource_attributes
|
77
|
+
action = response.parsed_json[:action] if autoload_action
|
78
|
+
client = response.context.client
|
79
|
+
if attributes.is_a?(Array)
|
80
|
+
results = attributes.map { |item| new(item).tap { |entity| entity.response = response } }
|
81
|
+
results.tap { |ary| ary.extend(Collection) }.response = response
|
82
|
+
return results
|
83
|
+
end
|
84
|
+
|
85
|
+
return Action.new(client, action) if attributes.nil? && action
|
86
|
+
return new(attributes).tap { |entity| entity.response = response } if action.nil?
|
87
|
+
|
88
|
+
[
|
89
|
+
Action.new(client, action),
|
90
|
+
new(attributes).tap { |entity| entity.response = response }
|
91
|
+
]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
attr_accessor :response
|
96
|
+
|
97
|
+
def initialize(client = nil, **kwargs)
|
98
|
+
@client = client
|
99
|
+
_load(kwargs)
|
100
|
+
end
|
101
|
+
|
102
|
+
def inspect
|
103
|
+
"#<#{self.class.name}:0x#{__id__.to_s(16)} #{_attributes.inspect}>"
|
104
|
+
end
|
105
|
+
|
106
|
+
def client
|
107
|
+
@client || response&.context&.client
|
108
|
+
end
|
109
|
+
|
110
|
+
def resource_url
|
111
|
+
if block = self.class.resource_url
|
112
|
+
return instance_exec(&block)
|
113
|
+
end
|
114
|
+
|
115
|
+
[self.class.resource_class.name.demodulize.tableize, id].compact.join('/')
|
116
|
+
end
|
117
|
+
|
118
|
+
def resource_path
|
119
|
+
self.class.resource_class.name.demodulize.underscore
|
120
|
+
end
|
121
|
+
|
122
|
+
def prepare_request(url_suffix = nil, **kwargs, &block)
|
123
|
+
kwargs[:resource_path] ||= resource_path
|
124
|
+
kwargs[:resource_class] ||= self.class
|
125
|
+
kwargs[:autoload_action] = true unless kwargs.key?(:autoload_action)
|
126
|
+
|
127
|
+
client.prepare_request(
|
128
|
+
[resource_url, url_suffix].compact.join('/'),
|
129
|
+
**kwargs,
|
130
|
+
&block
|
131
|
+
)
|
132
|
+
end
|
133
|
+
|
134
|
+
def _attributes
|
135
|
+
@_attributes ||= {}.with_indifferent_access
|
136
|
+
end
|
137
|
+
|
138
|
+
def method_missing(method, *args, &block)
|
139
|
+
_attributes.key?(method) ? _attributes[method] : super
|
140
|
+
end
|
141
|
+
|
142
|
+
def respond_to_missing?(method, *args, &block)
|
143
|
+
_attributes.key?(method) || super
|
144
|
+
end
|
145
|
+
|
146
|
+
def update(**kwargs)
|
147
|
+
context = self
|
148
|
+
_run_callbacks(:before)
|
149
|
+
prepare_request(j: kwargs, method: :put) do |response|
|
150
|
+
response.resource_class.from_response(
|
151
|
+
response,
|
152
|
+
autoload_action: response.autoload_action
|
153
|
+
).tap do |*_args|
|
154
|
+
_run_callbacks(:after)
|
155
|
+
context.send(:changes_applied)
|
28
156
|
end
|
29
157
|
end
|
30
158
|
end
|
31
159
|
|
32
|
-
def
|
33
|
-
|
160
|
+
def save
|
161
|
+
update(changes.map { |key, _value| [key.to_sym, _attributes[key]] }.to_h)
|
162
|
+
end
|
163
|
+
|
164
|
+
def rollback
|
165
|
+
restore_attributes
|
166
|
+
end
|
167
|
+
|
168
|
+
def _run_callbacks(order)
|
169
|
+
self.class._callbacks[order].each { |block| instance_exec(&block) }
|
170
|
+
end
|
171
|
+
|
172
|
+
def _update_attribute(key, value)
|
173
|
+
_attributes[key] = value
|
174
|
+
instance_variable_set("@#{key}", value)
|
175
|
+
end
|
176
|
+
|
177
|
+
def _load(resource)
|
178
|
+
@_attributes = {}.with_indifferent_access
|
179
|
+
|
180
|
+
resource.each do |key, value|
|
181
|
+
definition = self.class.schema[key]
|
182
|
+
|
183
|
+
if definition == :time
|
184
|
+
_update_attribute(key, value ? Time.parse(value) : nil)
|
185
|
+
next
|
186
|
+
end
|
187
|
+
|
188
|
+
if definition.is_a?(Class) && definition.include?(EntryLoader)
|
189
|
+
_update_attribute(key, value ? definition.new(client, value) : nil)
|
190
|
+
next
|
191
|
+
end
|
192
|
+
|
193
|
+
_update_attribute(key, value.is_a?(Hash) ? value.with_indifferent_access : value)
|
194
|
+
end
|
34
195
|
end
|
35
196
|
end
|
36
197
|
end
|
data/lib/hcloud/errors.rb
CHANGED
data/lib/hcloud/floating_ip.rb
CHANGED
@@ -1,43 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Hcloud
|
2
4
|
class FloatingIP
|
3
|
-
|
4
|
-
|
5
|
-
description: nil,
|
6
|
-
ip: nil,
|
7
|
-
type: nil,
|
8
|
-
dns_ptr: nil,
|
9
|
-
server: nil,
|
10
|
-
home_location: Location,
|
11
|
-
blocked: nil
|
12
|
-
}.freeze
|
5
|
+
require 'hcloud/floating_ip_resource'
|
6
|
+
|
13
7
|
include EntryLoader
|
14
8
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
9
|
+
schema(
|
10
|
+
home_location: Location,
|
11
|
+
created: :time
|
12
|
+
)
|
13
|
+
|
14
|
+
protectable :delete
|
15
|
+
updatable :description
|
16
|
+
destructible
|
17
|
+
|
18
|
+
has_actions
|
21
19
|
|
22
20
|
def assign(server:)
|
23
|
-
|
24
|
-
j: { server: server }).run.body)
|
25
|
-
Action.new(j['action'], self, client)
|
21
|
+
prepare_request('actions/assign', j: COLLECT_ARGS.call(__method__, binding))
|
26
22
|
end
|
27
23
|
|
28
24
|
def unassign
|
29
|
-
|
30
|
-
method: :post).run.body)
|
31
|
-
Action.new(j['action'], self, client)
|
32
|
-
end
|
33
|
-
|
34
|
-
def actions
|
35
|
-
ActionResource.new(client: client, parent: self, base_path: "floating_ips/#{id.to_i}")
|
25
|
+
prepare_request('actions/unassign', method: :post)
|
36
26
|
end
|
37
27
|
|
38
|
-
def
|
39
|
-
|
40
|
-
true
|
28
|
+
def change_dns_ptr(ip:, dns_ptr:)
|
29
|
+
prepare_request('actions/change_dns_ptr', j: COLLECT_ARGS.call(__method__, binding))
|
41
30
|
end
|
42
31
|
end
|
43
32
|
end
|
@@ -1,40 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Hcloud
|
2
4
|
class FloatingIPResource < AbstractResource
|
3
|
-
|
5
|
+
filter_attributes :name
|
4
6
|
|
5
|
-
|
6
|
-
mj('floating_ips') do |j|
|
7
|
-
j.flat_map { |x| x['floating_ips'].map { |x| FloatingIP.new(x, self, client) } }
|
8
|
-
end
|
9
|
-
end
|
7
|
+
bind_to FloatingIP
|
10
8
|
|
11
|
-
def
|
12
|
-
|
13
|
-
|
14
|
-
|
9
|
+
def [](arg)
|
10
|
+
case arg
|
11
|
+
when Integer then find_by(id: arg)
|
12
|
+
when String then find_by(name: arg)
|
15
13
|
end
|
16
|
-
j = Oj.load(request('floating_ips', j: query, code: 200).run.body)
|
17
|
-
[
|
18
|
-
j.key?('action') ? Action.new(j['action'], self, client) : nil,
|
19
|
-
FloatingIP.new(j['floating_ip'], self, client)
|
20
|
-
]
|
21
14
|
end
|
22
15
|
|
23
|
-
def
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
def [](arg)
|
32
|
-
case arg
|
33
|
-
when Integer
|
34
|
-
begin
|
35
|
-
find(arg)
|
36
|
-
rescue Error::NotFound
|
37
|
-
end
|
16
|
+
def create(type:, server: nil, home_location: nil, description: nil)
|
17
|
+
prepare_request(
|
18
|
+
'floating_ips', j: COLLECT_ARGS.call(__method__, binding),
|
19
|
+
expected_code: 201
|
20
|
+
) do |response|
|
21
|
+
action = Action.new(client, response[:action]) if response[:action]
|
22
|
+
[action, FloatingIP.new(client, response[:floating_ip])]
|
38
23
|
end
|
39
24
|
end
|
40
25
|
end
|
data/lib/hcloud/image.rb
CHANGED
@@ -1,42 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Hcloud
|
2
4
|
class Image
|
3
|
-
|
4
|
-
id: nil,
|
5
|
-
type: nil,
|
6
|
-
status: nil,
|
7
|
-
name: nil,
|
8
|
-
description: nil,
|
9
|
-
image_size: nil,
|
10
|
-
disk_size: nil,
|
11
|
-
created: :time,
|
12
|
-
created_from: nil,
|
13
|
-
bound_to: nil,
|
14
|
-
os_flavor: nil,
|
15
|
-
os_version: nil,
|
16
|
-
rapid_deploy: nil
|
17
|
-
}.freeze
|
5
|
+
require 'hcloud/image_resource'
|
18
6
|
|
19
7
|
include EntryLoader
|
20
8
|
|
21
|
-
|
22
|
-
|
23
|
-
|
9
|
+
schema(
|
10
|
+
created: :time,
|
11
|
+
deprecated: :time
|
12
|
+
)
|
24
13
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
(var = eval(x.last.to_s)).nil? ? r : r.merge!(x.last => var)
|
29
|
-
end
|
30
|
-
Image.new(
|
31
|
-
Oj.load(request("images/#{id.to_i}", j: query, method: :put).run.body)['image'],
|
32
|
-
parent,
|
33
|
-
client
|
34
|
-
)
|
35
|
-
end
|
14
|
+
protectable :delete
|
15
|
+
updatable :description, :type
|
16
|
+
destructible
|
36
17
|
|
37
|
-
def
|
38
|
-
|
39
|
-
true
|
18
|
+
def to_snapshot
|
19
|
+
update(type: 'snapshot')
|
40
20
|
end
|
41
21
|
end
|
42
22
|
end
|