fog-hetznercloud 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE +201 -0
  4. data/README.md +139 -0
  5. data/Rakefile +24 -0
  6. data/bin/console +14 -0
  7. data/bin/setup +8 -0
  8. data/fog-hetznercloud.gemspec +33 -0
  9. data/lib/fog/hetznercloud.rb +13 -0
  10. data/lib/fog/hetznercloud/client.rb +55 -0
  11. data/lib/fog/hetznercloud/compute.rb +146 -0
  12. data/lib/fog/hetznercloud/errors.rb +37 -0
  13. data/lib/fog/hetznercloud/models/compute/action.rb +31 -0
  14. data/lib/fog/hetznercloud/models/compute/actions.rb +22 -0
  15. data/lib/fog/hetznercloud/models/compute/datacenter.rb +15 -0
  16. data/lib/fog/hetznercloud/models/compute/datacenters.rb +22 -0
  17. data/lib/fog/hetznercloud/models/compute/floating_ip.rb +154 -0
  18. data/lib/fog/hetznercloud/models/compute/floating_ips.rb +22 -0
  19. data/lib/fog/hetznercloud/models/compute/image.rb +78 -0
  20. data/lib/fog/hetznercloud/models/compute/images.rb +22 -0
  21. data/lib/fog/hetznercloud/models/compute/location.rb +18 -0
  22. data/lib/fog/hetznercloud/models/compute/locations.rb +22 -0
  23. data/lib/fog/hetznercloud/models/compute/server.rb +393 -0
  24. data/lib/fog/hetznercloud/models/compute/server_type.rb +17 -0
  25. data/lib/fog/hetznercloud/models/compute/server_types.rb +22 -0
  26. data/lib/fog/hetznercloud/models/compute/servers.rb +47 -0
  27. data/lib/fog/hetznercloud/models/compute/ssh_key.rb +65 -0
  28. data/lib/fog/hetznercloud/models/compute/ssh_keys.rb +22 -0
  29. data/lib/fog/hetznercloud/request_helper.rb +32 -0
  30. data/lib/fog/hetznercloud/requests/compute/create_floating_ip.rb +29 -0
  31. data/lib/fog/hetznercloud/requests/compute/create_server.rb +31 -0
  32. data/lib/fog/hetznercloud/requests/compute/create_ssh_key.rb +24 -0
  33. data/lib/fog/hetznercloud/requests/compute/delete_floating_ip.rb +17 -0
  34. data/lib/fog/hetznercloud/requests/compute/delete_image.rb +17 -0
  35. data/lib/fog/hetznercloud/requests/compute/delete_server.rb +17 -0
  36. data/lib/fog/hetznercloud/requests/compute/delete_ssh_key.rb +17 -0
  37. data/lib/fog/hetznercloud/requests/compute/execute_server_action.rb +17 -0
  38. data/lib/fog/hetznercloud/requests/compute/floating_ip_assign_to_server.rb +17 -0
  39. data/lib/fog/hetznercloud/requests/compute/floating_ip_unassign.rb +17 -0
  40. data/lib/fog/hetznercloud/requests/compute/floating_ip_update_dns_ptr.rb +17 -0
  41. data/lib/fog/hetznercloud/requests/compute/get_action.rb +17 -0
  42. data/lib/fog/hetznercloud/requests/compute/get_datacenter.rb +17 -0
  43. data/lib/fog/hetznercloud/requests/compute/get_floating_ip.rb +17 -0
  44. data/lib/fog/hetznercloud/requests/compute/get_image.rb +17 -0
  45. data/lib/fog/hetznercloud/requests/compute/get_location.rb +17 -0
  46. data/lib/fog/hetznercloud/requests/compute/get_server.rb +17 -0
  47. data/lib/fog/hetznercloud/requests/compute/get_server_type.rb +17 -0
  48. data/lib/fog/hetznercloud/requests/compute/get_ssh_key.rb +17 -0
  49. data/lib/fog/hetznercloud/requests/compute/list_actions.rb +17 -0
  50. data/lib/fog/hetznercloud/requests/compute/list_datacenters.rb +17 -0
  51. data/lib/fog/hetznercloud/requests/compute/list_floating_ips.rb +17 -0
  52. data/lib/fog/hetznercloud/requests/compute/list_images.rb +17 -0
  53. data/lib/fog/hetznercloud/requests/compute/list_locations.rb +17 -0
  54. data/lib/fog/hetznercloud/requests/compute/list_server_types.rb +17 -0
  55. data/lib/fog/hetznercloud/requests/compute/list_servers.rb +17 -0
  56. data/lib/fog/hetznercloud/requests/compute/list_ssh_keys.rb +17 -0
  57. data/lib/fog/hetznercloud/requests/compute/update_floating_ip.rb +17 -0
  58. data/lib/fog/hetznercloud/requests/compute/update_image.rb +17 -0
  59. data/lib/fog/hetznercloud/requests/compute/update_server.rb +77 -0
  60. data/lib/fog/hetznercloud/requests/compute/update_ssh_key.rb +17 -0
  61. data/lib/fog/hetznercloud/version.rb +5 -0
  62. metadata +244 -0
@@ -0,0 +1,22 @@
1
+ module Fog
2
+ module Hetznercloud
3
+ class Compute
4
+ class FloatingIps < Fog::Collection
5
+ model Fog::Hetznercloud::Compute::FloatingIp
6
+
7
+ def all(filters = {})
8
+ floating_ips = service.list_floating_ips(filters).body['floating_ips'] || []
9
+ load(floating_ips)
10
+ end
11
+
12
+ def get(identity)
13
+ if (floating_ip = service.get_floating_ip(identity).body['floating_ip'])
14
+ new(floating_ip)
15
+ end
16
+ rescue Fog::Hetznercloud::Compute::UnknownResourceError
17
+ nil
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,78 @@
1
+ module Fog
2
+ module Hetznercloud
3
+ class Compute
4
+ class Image < Fog::Model
5
+ identity :id
6
+
7
+ attribute :type
8
+ attribute :status
9
+ attribute :name
10
+ attribute :description
11
+ attribute :image_size
12
+ attribute :disk_size
13
+ attribute :created
14
+ attribute :created_from
15
+ attribute :bound_to
16
+ attribute :os_flavor
17
+ attribute :os_version
18
+ attribute :rapid_deploy
19
+
20
+ def created=(value)
21
+ attributes[:created] = Time.iso8601(value)
22
+ end
23
+
24
+ def created_from=(value)
25
+ attributes[:created_from] = case value
26
+ when Hash
27
+ service.servers.new(value)
28
+ when Integer
29
+ service.servers.new(identity: value)
30
+ else
31
+ value
32
+ end
33
+ end
34
+
35
+ def bound_to=(value)
36
+ attributes[:bound_to] = case value
37
+ when Hash
38
+ service.servers.new(value)
39
+ when Integer
40
+ service.servers.new(identity: value)
41
+ else
42
+ value
43
+ end
44
+ end
45
+
46
+ def save
47
+ update
48
+ end
49
+
50
+ def destroy
51
+ requires :identity
52
+
53
+ service.delete_image(identity)
54
+ true
55
+ end
56
+
57
+ private
58
+
59
+ def update
60
+ requires :identity
61
+ requires_one :description, :type
62
+
63
+ body = attributes.dup
64
+
65
+ body[:description] = description
66
+ body[:type] = type
67
+
68
+ if (image = service.update_image(identity, body).body['image'])
69
+ merge_attributes(image)
70
+ true
71
+ else
72
+ false
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,22 @@
1
+ module Fog
2
+ module Hetznercloud
3
+ class Compute
4
+ class Images < Fog::Collection
5
+ model Fog::Hetznercloud::Compute::Image
6
+
7
+ def all(filters = {})
8
+ images = service.list_images(filters).body['images'] || []
9
+ load(images)
10
+ end
11
+
12
+ def get(identity)
13
+ if (image = service.get_image(identity).body['image'])
14
+ new(image)
15
+ end
16
+ rescue Fog::Hetznercloud::Compute::UnknownResourceError
17
+ nil
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,18 @@
1
+ # https://docs.hetzner.cloud/#resources-actions-get
2
+ module Fog
3
+ module Hetznercloud
4
+ class Compute
5
+ class Location < Fog::Model
6
+ identity :id
7
+
8
+ attribute :name
9
+ attribute :description
10
+ attribute :country
11
+ # attribute :extra_volumes
12
+ attribute :city
13
+ attribute :latitude
14
+ attribute :longitude
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,22 @@
1
+ module Fog
2
+ module Hetznercloud
3
+ class Compute
4
+ class Locations < Fog::Collection
5
+ model Fog::Hetznercloud::Compute::Location
6
+
7
+ def all(filters = {})
8
+ locations = service.list_locations(filters).body['locations'] || []
9
+ load(locations)
10
+ end
11
+
12
+ def get(identity)
13
+ if (location = service.get_location(identity).body['location'])
14
+ new(location)
15
+ end
16
+ rescue Fog::Hetznercloud::Compute::UnknownResourceError
17
+ nil
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,393 @@
1
+ require 'fog/compute/models/server'
2
+
3
+ module Fog
4
+ module Hetznercloud
5
+ class Compute
6
+ class Server < Fog::Compute::Server
7
+ identity :id
8
+
9
+ # Required
10
+ attribute :name
11
+ attribute :image
12
+ attribute :server_type
13
+
14
+ attribute :status
15
+ attribute :created
16
+ attribute :user_data
17
+ attribute :ssh_keys
18
+ attribute :public_net
19
+ attribute :datacenter
20
+ attribute :location
21
+ attribute :iso
22
+ attribute :rescue_enabled
23
+ attribute :locked
24
+ attribute :backup_window
25
+ attribute :start_after_create
26
+ attribute :outgoing_traffic
27
+ attribute :incoming_traffic
28
+ attribute :included_traffic
29
+
30
+ def initialize(options)
31
+ @async = true
32
+ super(options)
33
+ end
34
+
35
+ def image=(value)
36
+ attributes[:image] = case value
37
+ when Hash
38
+ service.images.new(value)
39
+ when String
40
+ service.images.all(name: value).first
41
+ when Integer
42
+ service.images.get(value)
43
+ else
44
+ value
45
+ end
46
+ end
47
+
48
+ def ssh_keys=(value)
49
+ attributes[:ssh_keys] = []
50
+ # API does not return ssh_key
51
+ return true if value.nil?
52
+ value.each do |item|
53
+ thing = case item
54
+ when Hash
55
+ service.ssh_keys.new(item)
56
+ when String
57
+ service.ssh_keys.all(name: item).first
58
+ when Integer
59
+ service.ssh_keys.get(item)
60
+ else
61
+ value
62
+ end
63
+ attributes[:ssh_keys] << thing
64
+ end
65
+ end
66
+
67
+ def server_type=(value)
68
+ attributes[:server_type] = case value
69
+ when Hash
70
+ service.server_types.new(value)
71
+ when String
72
+ service.server_types.all(name: value).first
73
+ when Integer
74
+ service.server_types.get(value)
75
+ else
76
+ value
77
+ end
78
+ end
79
+
80
+ def location=(value)
81
+ attributes[:location] = case value
82
+ when Hash
83
+ service.locations.new(value)
84
+ when String
85
+ service.locations.all(name: value).first
86
+ when Integer
87
+ service.locations.get(value)
88
+ else
89
+ value
90
+ end
91
+ end
92
+
93
+ def datacenter=(value)
94
+ attributes[:datacenter] = case value
95
+ when Hash
96
+ service.datacenters.new(value)
97
+ attributes[:location] = service.locations.new(value['location'])
98
+ when String
99
+ service.datacenters.all(name: value).first
100
+ when Integer
101
+ service.datacenters.get(value)
102
+ else
103
+ value
104
+ end
105
+ end
106
+
107
+ def user_data=(value)
108
+ attributes[:user_data] = if value =~ /^\.?\/[^\/]+/
109
+ File.read(value)
110
+ else
111
+ value
112
+ end
113
+ end
114
+
115
+ def name=(value)
116
+ return true if name == value
117
+ @needsupdate = true
118
+ attributes[:name] = value
119
+ end
120
+
121
+ def public_dns_name
122
+ "static.#{public_net['ipv4']['ip']}.clients.your-server.de"
123
+ end
124
+
125
+ def reverse_dns_name
126
+ public_net['ipv4']['dns_ptr']
127
+ end
128
+
129
+ def ready?
130
+ status == 'running'
131
+ end
132
+
133
+ def migrating?
134
+ status == 'migrating'
135
+ end
136
+
137
+ def stopped?
138
+ status == 'off'
139
+ end
140
+
141
+ def save
142
+ if persisted?
143
+ update
144
+ else
145
+ create
146
+ end
147
+ end
148
+
149
+ def destroy
150
+ requires :identity
151
+
152
+ service.delete_server(identity)
153
+ true
154
+ end
155
+
156
+ def poweron(async: async_mode?)
157
+ requires :identity
158
+
159
+ execute_action('poweron', async)
160
+ end
161
+
162
+ def poweroff(async: async_mode?)
163
+ requires :identity
164
+
165
+ execute_action('poweroff', async)
166
+ end
167
+
168
+ def reboot(async: async_mode?)
169
+ requires :identity
170
+
171
+ execute_action('reboot', async)
172
+ end
173
+
174
+ def reset(async: async_mode?)
175
+ requires :identity
176
+
177
+ execute_action('reset', async)
178
+ end
179
+
180
+ def shutdown(async: async_mode?)
181
+ requires :identity
182
+
183
+ execute_action('shutdown', async)
184
+ end
185
+
186
+ def async=(value)
187
+ @async = !!value
188
+ end
189
+
190
+ def async?
191
+ @async
192
+ end
193
+
194
+ def async_mode?
195
+ async?
196
+ end
197
+
198
+ def sync=(value)
199
+ @async = !value
200
+ end
201
+
202
+ def sync?
203
+ !@async
204
+ end
205
+
206
+ def disable_rescue(async: async_mode?)
207
+ requires :identity
208
+
209
+ execute_action('disable_rescue', async)
210
+ end
211
+
212
+ def enable_rescue(type: 'linux64', ssh_keys: [], async: async_mode?)
213
+ requires :identity
214
+
215
+ # FIXME: Refactor with ssh_keys=(value) above to do DRY
216
+ sshkeys = []
217
+ ssh_keys.each do |item|
218
+ thing = case item
219
+ when Hash
220
+ service.ssh_keys.new(item)
221
+ when String
222
+ service.ssh_keys.all(name: item).first
223
+ when Integer
224
+ service.ssh_keys.get(item)
225
+ else
226
+ value
227
+ end
228
+ sshkeys << thing.identity
229
+ end
230
+
231
+ body = {
232
+ type: type,
233
+ ssh_keys: sshkeys
234
+ }
235
+
236
+ execute_action('enable_rescue', async, body)
237
+ end
238
+
239
+ # Returns action, imageObject
240
+ def create_backup(async: async_mode?, description: "Backup of #{attributes[:name]} created at #{Time.now}")
241
+ requires :identity
242
+
243
+ create_image(async: async, description: description, type: 'backup')
244
+ end
245
+
246
+ # Returns action, imageObject
247
+ def create_image(async: async_mode?, description: "image of #{attributes[:name]} created at #{Time.now}", type: 'snapshot')
248
+ requires :identity
249
+
250
+ body = {
251
+ description: description,
252
+ type: type
253
+ }
254
+
255
+ execute_action('create_image', async, body)
256
+ end
257
+
258
+ def rebuild_from_image(async: async_mode?, image:)
259
+ requires :identity
260
+
261
+ # FIXME: Lookup images by description. As API does not support
262
+ # this we need to filter our own. Probably needs pagination
263
+ # support.
264
+ image_id = case image
265
+ when Fog::Hetznercloud::Compute::Image
266
+ image.identity
267
+ when Integer
268
+ image
269
+ end
270
+
271
+ body = {
272
+ image: image_id
273
+ }
274
+
275
+ execute_action('rebuild', async, body)
276
+ end
277
+
278
+ def change_type(async: async_mode?, upgrade_disk: false, server_type:)
279
+ requires :identity
280
+
281
+ # FIXME: Should I shutdown and wait here as server needs to
282
+ # be stopped ?
283
+
284
+ body = {
285
+ upgrade_disk: upgrade_disk,
286
+ server_type: server_type
287
+ }
288
+
289
+ execute_action('change_type', async, body)
290
+ end
291
+
292
+ def enable_backup(async: async_mode?, backup_window: nil)
293
+ requires :identity
294
+
295
+ unless backup_window.nil?
296
+ body = {
297
+ backup_window: backup_window
298
+ }
299
+ end
300
+
301
+ execute_action('enable_backup', async, body)
302
+ end
303
+
304
+ def change_dns_ptr(dns_ptr, ip: public_ip_address, async: async_mode?)
305
+ requires :identity
306
+
307
+ body = {
308
+ ip: ip,
309
+ dns_ptr: dns_ptr
310
+ }
311
+
312
+ execute_action('change_dns_ptr', async, body)
313
+ end
314
+
315
+ def disable_backup(async: async_mode?)
316
+ requires :identity
317
+
318
+ execute_action('disable_backup', async)
319
+ end
320
+
321
+ # Returns action, password
322
+ def reset_root_password(async: async_mode?)
323
+ requires :identity
324
+ unless ready?
325
+ throw Fog::Hetznercloud::Error::StateError.new('ERROR: to reset the root password, the server must be running')
326
+ end
327
+
328
+ execute_action('reset_password', async)
329
+ end
330
+
331
+ def execute_action(action, async = true, body = {})
332
+ requires :identity
333
+
334
+ request_body = service.execute_server_action(identity, action, body).body
335
+ if (action = request_body['action'])
336
+ service.actions.new(action).tap do |a|
337
+ unless async
338
+ a.wait_for { a.success? }
339
+ reload
340
+ end
341
+ end
342
+ end
343
+ extra = service.images.new(request_body['image']) unless request_body['image'].nil?
344
+ extra = request_body['root_password'] unless request_body['root_password'].nil?
345
+ [action, extra]
346
+ end
347
+
348
+ def public_ip_address
349
+ public_net['ipv6']['ip'] if public_net.key?('ipv6')
350
+ public_net['ipv4']['ip'] if public_net.key?('ipv4')
351
+ end
352
+
353
+ private
354
+
355
+ def create
356
+ requires :name, :server_type, :image
357
+
358
+ options = {}
359
+ options[:ssh_keys] = ssh_keys.map(&:identity) unless ssh_keys.nil?
360
+ options[:user_data] = user_data unless user_data.nil?
361
+ options[:start_after_create] = start_after_create unless start_after_create.nil?
362
+ options[:location] = location.identity unless location.nil?
363
+ options[:datacenter] = datacenter.identity unless datacenter.nil?
364
+
365
+ if (server = service.create_server(name, image.identity, server_type.identity, options).body['server'])
366
+ merge_attributes(server)
367
+ true
368
+ else
369
+ false
370
+ end
371
+ end
372
+
373
+ def update
374
+ requires :identity, :name
375
+ return true unless @needsupdate
376
+
377
+ body = attributes.dup
378
+
379
+ body = {
380
+ name: name
381
+ }
382
+
383
+ if (server = service.update_server(identity, body).body['server'])
384
+ merge_attributes(server)
385
+ true
386
+ else
387
+ false
388
+ end
389
+ end
390
+ end
391
+ end
392
+ end
393
+ end