fog-hetznercloud 0.0.1

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 (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,55 @@
1
+ module Fog
2
+ module Hetznercloud
3
+ class Client
4
+ def initialize(endpoint, token, connection_options)
5
+ @endpoint = endpoint
6
+ @token = token
7
+ @connection_options = connection_options
8
+ end
9
+
10
+ def request(params)
11
+ params[:headers] ||= {}
12
+ params[:headers]['Content-Type'] ||= 'application/json'
13
+ params[:headers]['Authorization'] ||= "Bearer #{@token}"
14
+
15
+ params[:body] = encode_body(params)
16
+
17
+ response = connection.request(params)
18
+
19
+ response.body = decode_body(response)
20
+
21
+ response
22
+ end
23
+
24
+ private
25
+
26
+ def connection
27
+ @connection ||= Fog::Core::Connection.new(@endpoint, false, @connection_options)
28
+ end
29
+
30
+ def encode_body(params)
31
+ body = params[:body]
32
+ content_type = params[:headers]['Content-Type']
33
+
34
+ if body.nil? || body.is_a?(String)
35
+ body
36
+ elsif content_type =~ %r{application/json.*}i
37
+ Fog::JSON.encode(body)
38
+ else
39
+ body.to_s
40
+ end
41
+ end
42
+
43
+ def decode_body(response)
44
+ body = response.body
45
+ content_type = response.headers['Content-Type']
46
+
47
+ if !body.nil? && !body.empty? && content_type =~ %r{application/json.*}i
48
+ Fog::JSON.decode(body)
49
+ else
50
+ body
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,146 @@
1
+ require 'fog/hetznercloud/client'
2
+ require 'fog/hetznercloud/errors'
3
+ require 'fog/hetznercloud/request_helper'
4
+
5
+ module Fog
6
+ module Hetznercloud
7
+ class Compute < Fog::Service
8
+ class UnauthorizedError < Error; end
9
+ class ForbiddenError < Error; end
10
+ class InvalidInputError < Error; end
11
+ class JsonErrorError < Error; end
12
+ class LockedError < Error; end
13
+ class NotFoundError < Error; end
14
+ class RateLimitExceededError < Error; end
15
+ class ResourceLimitExeceededError < Error; end
16
+ class ResourceUnavailableError < Error; end
17
+ class ServiceErrorError < Error; end
18
+ class UniquenessErrorError < Error; end
19
+ class UnknownResourceError < Error; end
20
+ class StateError < Error; end
21
+
22
+ requires :hetznercloud_token
23
+ recognizes :hetznercloud_datacenter
24
+ recognizes :hetznercloud_location
25
+ secrets :hetznercloud_token
26
+
27
+ model_path 'fog/hetznercloud/models/compute'
28
+
29
+ model :server
30
+ collection :servers
31
+ model :image
32
+ collection :images
33
+ model :server_type
34
+ collection :server_types
35
+ model :action
36
+ collection :actions
37
+ model :floating_ip
38
+ collection :floating_ips
39
+ model :location
40
+ collection :locations
41
+ model :datacenter
42
+ collection :datacenters
43
+ model :ssh_key
44
+ collection :ssh_keys
45
+
46
+ request_path 'fog/hetznercloud/requests/compute'
47
+
48
+ ## Servers
49
+ request :create_server
50
+ request :list_servers
51
+ request :get_server
52
+ request :update_server
53
+ request :delete_server
54
+ request :execute_server_action
55
+
56
+ # Server Types
57
+ request :list_server_types
58
+ request :get_server_type
59
+
60
+ # Images
61
+ request :list_images
62
+ request :get_image
63
+ request :delete_image
64
+ request :update_image
65
+
66
+ # Actions
67
+ request :list_actions
68
+ request :get_action
69
+
70
+ # Locations
71
+ request :list_locations
72
+ request :get_location
73
+
74
+ # Datacenters
75
+ request :list_datacenters
76
+ request :get_datacenter
77
+
78
+ # Datacenters
79
+ request :list_ssh_keys
80
+ request :get_ssh_key
81
+ request :create_ssh_key
82
+ request :delete_ssh_key
83
+ request :update_ssh_key
84
+
85
+ # Floating IP'S
86
+ request :list_floating_ips
87
+ request :get_floating_ip
88
+ request :create_floating_ip
89
+ request :delete_floating_ip
90
+ request :update_floating_ip
91
+ request :floating_ip_assign_to_server
92
+ request :floating_ip_unassign
93
+ request :floating_ip_update_dns_ptr
94
+
95
+ class Real
96
+ include Fog::Hetznercloud::RequestHelper
97
+
98
+ # FIXME: Make @location and @datacenter used in server creation
99
+ # as default
100
+ def initialize(options)
101
+ @token = options[:hetznercloud_token]
102
+ @location = options[:hetznercloud_location] || 'fsn1'
103
+ @datacenter = options[:hetznercloud_datacenter] || 'fsn1-dc8'
104
+ @connection_options = options[:connection_options] || {}
105
+ end
106
+
107
+ def request(params)
108
+ client.request(params)
109
+ rescue Excon::Errors::HTTPStatusError => error
110
+ decoded = Fog::Hetznercloud::Errors.decode_error(error)
111
+ raise if decoded.nil?
112
+
113
+ code = decoded[:code]
114
+ message = decoded[:message]
115
+
116
+ raise case code
117
+ when 'unauthorized', 'forbidden', 'invalid_input', 'json_error', 'locked', 'not_found', 'rate_limit_exceeded', 'resource_limit_exceeded', 'resource_unavailable', 'service_error', 'uniqueness_error'
118
+ Fog::Hetznercloud::Compute.const_get("#{camelize(code)}Error").slurp(error, message)
119
+ else
120
+ Fog::Hetznercloud::Compute::Error.slurp(error, message)
121
+ end
122
+ end
123
+
124
+ private
125
+
126
+ def client
127
+ @client ||= Fog::Hetznercloud::Client.new(endpoint, @token, @connection_options)
128
+ end
129
+
130
+ def endpoint
131
+ 'https://api.hetzner.cloud/v1'
132
+ end
133
+
134
+ def camelize(str)
135
+ str.split('_').collect(&:capitalize).join
136
+ end
137
+ end
138
+
139
+ class Mock
140
+ def request(*_args)
141
+ Fog::Mock.not_implemented
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,37 @@
1
+ module Fog
2
+ module Hetznercloud
3
+ module Errors
4
+ def self.decode_error(error)
5
+ body = begin
6
+ Fog::JSON.decode(error.response.body)
7
+ rescue Fog::JSON::DecodeError
8
+ nil
9
+ end
10
+
11
+ return if body.nil?
12
+
13
+ code = body['error']['code']
14
+ message = body['error']['message']
15
+ details = body['error']['details']
16
+
17
+ return if code.nil? || message.nil?
18
+
19
+ unless details.nil?
20
+ message << "\n"
21
+ message << format_details(details)
22
+ end
23
+
24
+ { code: code, message: message }
25
+ end
26
+
27
+ def self.format_details(details)
28
+ details.map { |field, msgs| format_field(field, msgs) }.join("\n")
29
+ end
30
+
31
+ def self.format_field(field, msgs)
32
+ msgs = msgs.map { |msg| "\t\t- #{msg}" }
33
+ "\t#{field}:\n#{msgs.join("\n")}"
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,31 @@
1
+ # https://docs.hetzner.cloud/#resources-actions-get
2
+ module Fog
3
+ module Hetznercloud
4
+ class Compute
5
+ class Action < Fog::Model
6
+ identity :id
7
+
8
+ attribute :command
9
+ attribute :status
10
+ attribute :progress
11
+ # attribute :extra_volumes
12
+ attribute :started
13
+ attribute :finished
14
+ attribute :resources
15
+ attribute :error
16
+
17
+ def running?
18
+ status == 'running'
19
+ end
20
+
21
+ def error?
22
+ status == 'error'
23
+ end
24
+
25
+ def success?
26
+ status == 'success'
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,22 @@
1
+ module Fog
2
+ module Hetznercloud
3
+ class Compute
4
+ class Actions < Fog::Collection
5
+ model Fog::Hetznercloud::Compute::Action
6
+
7
+ def all(filters = {})
8
+ actions = service.list_actions(filters).body['actions'] || []
9
+ load(actions)
10
+ end
11
+
12
+ def get(identity)
13
+ if (action = service.get_action(identity).body['action'])
14
+ new(action)
15
+ end
16
+ rescue Fog::Hetznercloud::Compute::UnknownResourceError
17
+ nil
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,15 @@
1
+ # https://docs.hetzner.cloud/#resources-actions-get
2
+ module Fog
3
+ module Hetznercloud
4
+ class Compute
5
+ class Datacenter < Fog::Model
6
+ identity :id
7
+
8
+ attribute :name
9
+ attribute :description
10
+ attribute :location
11
+ attribute :server_types
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,22 @@
1
+ module Fog
2
+ module Hetznercloud
3
+ class Compute
4
+ class Datacenters < Fog::Collection
5
+ model Fog::Hetznercloud::Compute::Datacenter
6
+
7
+ def all(filters = {})
8
+ datacenters = service.list_datacenters(filters).body['datacenters'] || []
9
+ load(datacenters)
10
+ end
11
+
12
+ def get(identity)
13
+ if (datacenter = service.get_datacenter(identity).body['datacenter'])
14
+ new(datacenter)
15
+ end
16
+ rescue Fog::Hetznercloud::Compute::UnknownResourceError
17
+ nil
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,154 @@
1
+ # https://docs.hetzner.cloud/#resources-actions-get
2
+ module Fog
3
+ module Hetznercloud
4
+ class Compute
5
+ class FloatingIp < Fog::Model
6
+ identity :id
7
+
8
+ attribute :description # String
9
+ attribute :ip # String
10
+ attribute :type # ipv4|ipv6
11
+ attribute :server # id of server
12
+ attribute :dns_ptr # array of string
13
+ attribute :home_location # location object
14
+ attribute :blocked # boolean
15
+
16
+ def type=(value)
17
+ valid = %w[ipv4 ipv6]
18
+ if !valid.include? value
19
+ raise Fog::Hetznercloud::Compute::InvalidInputError, "ERROR: floating_ip type must be one of #{valid}"
20
+ else
21
+ attributes[:type] = value
22
+ end
23
+ end
24
+
25
+ def description=(value)
26
+ @lastdescription = attributes[:description]
27
+ attributes[:description] = value
28
+ if @lastdescription && @lastdescription != attributes[:description]
29
+ @needsupdate = true
30
+ else
31
+ @needsupdate = false
32
+ end
33
+ end
34
+
35
+ def home_location=(value)
36
+ attributes[:home_location] = case value
37
+ when Hash
38
+ service.locations.new(value)
39
+ when String
40
+ service.locations.new(identity: value)
41
+ else
42
+ value
43
+ end
44
+ end
45
+
46
+ def server=(value)
47
+ attributes[:server] = case value
48
+ when Hash
49
+ service.servers.new(value)
50
+ when Integer
51
+ service.servers.get(value)
52
+ else
53
+ value
54
+ end
55
+ end
56
+
57
+ def destroy
58
+ requires :identity
59
+
60
+ service.delete_floating_ip(identity)
61
+ true
62
+ end
63
+
64
+ def save
65
+ if persisted?
66
+ reassign && unassign && update
67
+ else
68
+ create
69
+ end
70
+ end
71
+
72
+ def assign(serverid)
73
+ requires :identity
74
+ if serverid.nil?
75
+ body = {}
76
+
77
+ if (floating_ip = service.floating_ip_unassign(identity, body).body['floating_ip'])
78
+ merge_attributes(floating_ip)
79
+ true
80
+ else
81
+ false
82
+ end
83
+ else
84
+ body = {
85
+ server: serverid
86
+ }
87
+
88
+ if (floating_ip = service.floating_ip_assign_to_server(identity, body).body['floating_ip'])
89
+ merge_attributes(floating_ip)
90
+ true
91
+ else
92
+ false
93
+ end
94
+ end
95
+ end
96
+
97
+ def update_dns_ptr(newname)
98
+ requires :identity
99
+
100
+ body = {
101
+ ip: ip,
102
+ dns_ptr: newname
103
+ }
104
+
105
+ if (floating_ip = service.floating_ip_update_dns_ptr(identity, body).body['floating_ip'])
106
+ merge_attributes(floating_ip)
107
+ true
108
+ else
109
+ false
110
+ end
111
+ end
112
+
113
+ def unassign
114
+ assign(nil) unless server.nil?
115
+ end
116
+
117
+ private
118
+
119
+ def create
120
+ requires :type
121
+ requires_one :home_location, :server
122
+
123
+ options = {}
124
+ options[:description] = description unless description.nil?
125
+ options[:home_location] = home_location.identity unless home_location.nil?
126
+ options[:server] = server.identity unless server.nil?
127
+
128
+ if (floating_ip = service.create_floating_ip(type, options).body['floating_ip'])
129
+ merge_attributes(floating_ip)
130
+ true
131
+ else
132
+ false
133
+ end
134
+ end
135
+
136
+ def update
137
+ return true unless @needsupdate
138
+ requires :identity, :description
139
+
140
+ body = attributes.dup
141
+
142
+ body[:description] = description
143
+
144
+ if (floating_ip = service.update_floating_ip(identity, body).body['floating_ip'])
145
+ merge_attributes(floating_ip)
146
+ true
147
+ else
148
+ false
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
154
+ end