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,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