fog-hetznercloud 0.0.1 → 0.0.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 624b520019fef559e39234bfaeaefe1c84fdccb3
4
- data.tar.gz: a3cc609598f1ecb0353256e2a94e9fd90638a5d8
3
+ metadata.gz: f2513d94d86a7673b99f59b766e1a0eda8dc0106
4
+ data.tar.gz: 818b34b6423ee7a51c25dcfc3b163883cdb52814
5
5
  SHA512:
6
- metadata.gz: badcbf8b5728b9d9af029ce70b1dd8cb8d9329200305fc8f8b56f33d483939e73eb667a4a91a21e48d20f1dd8990002abc93ebd39fded9cc19cea7f84186b936
7
- data.tar.gz: 6b09eac7483499e5e6a8962ac45b696f42fdfd217c984df0cb4001b24a79c01c719889b0887ef9aa0a2b7c41ff3defdb0d75bc9c74f54d57e13ea4199d87839c
6
+ metadata.gz: cef2459d83661bc34425320e27cc348224bbacd0257d3db921ba5eb9193856522bfc2742e051660c59e803140edf6b682f23289afdeb01503bf264fd29c61eca
7
+ data.tar.gz: 10d33c8176c6b505f91d299d1a27c17f8332c6f85098e972b7db22fd382930e37755e03b50ef1dce62c4921b39deb3af4445dcc36fd2f792d37d132a9ce5139f
data/README.md CHANGED
@@ -1,11 +1,12 @@
1
1
 
2
+
2
3
  # Rubygem fog-hetznercloud
3
4
 
4
5
  Fog provider gem to support [Hetzner Cloud](https://cloud.hetzner.com/).
5
6
 
6
7
  ## Features and development
7
8
 
8
- This gem is currently a MVP prototype that is missing some features:
9
+ This gem is currently a MVP prototype that is **missing** some features like:
9
10
 
10
11
  API:
11
12
  * [Pagination Support](https://docs.hetzner.cloud/#header-pagination-1)
@@ -53,7 +54,9 @@ default:
53
54
 
54
55
  ## Example
55
56
 
56
- ### Gemfile
57
+ **See [examples/integrationtest.rb](examples/integrationtest.rb) for a reasonable example**
58
+
59
+ ### Ge mfile
57
60
 
58
61
  ```ruby
59
62
  source "https://rubygems.org"
data/Rakefile CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'bundler/gem_tasks'
2
2
  require 'rake/testtask'
3
3
  require 'rubocop/rake_task'
4
+ require 'bump/tasks'
4
5
 
5
6
  Rake::TestTask.new('test:units') do |t|
6
7
  t.libs << 'test'
data/examples/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ #gem 'fog-hetznercloud', '>= 0.0.1'
6
+ gem 'fog-hetznercloud', :path => '../'
7
+
8
+ # gem "rails"
@@ -0,0 +1,166 @@
1
+ #!/usr/bin/env ruby2.0
2
+ #
3
+ # This is a simple integraiton test performing some usefull server actions.
4
+ #
5
+ # This is also intended as some kind of documentation on how to use the library
6
+ #
7
+ #
8
+ require 'fog/hetznercloud'
9
+ require 'pp'
10
+
11
+ # Connecto to Hetznercloud
12
+ connection = Fog::Compute[:hetznercloud]
13
+
14
+ # Some variables
15
+ ssh_public_keyfile = "./keys/testkey.pub" # needs to be generated first with 'ssh-keygen -t rsa testkey'
16
+ ssh_private_keyfile = "./keys/testkey" # needs to be generated first with 'ssh-keygen -t rsa testkey'
17
+ ssh_keyname = "testkey" # Name of the ssh key
18
+ servername = "testserver" # Name of the server
19
+
20
+ # resources created
21
+ image_snapshot = nil
22
+ image_backup = nil
23
+ server = nil
24
+ floating_ip = nil
25
+ ssh_key = nil
26
+
27
+ raise "Error: Fild #{ssh_private_keyfile} missing (create with 'ssh-keygen -t rsa -N \"\" -f #{ssh_private_keyfile}')" unless File.file?(ssh_private_keyfile)
28
+
29
+ begin
30
+
31
+ # Lookups, see api doc https://docs.hetzner.cloud for filters
32
+ connection.datacenters.each { |x| puts "Datacenter #{x.name} located in #{x.location.name}" }
33
+ connection.locations.each { |x| puts "Location #{x.name} located in #{x.city}" }
34
+ connection.locations.all(:name => 'fsn1').each { |x| puts "FSN Found" }
35
+ connection.locations.all(:name => 'nbg1').each { |x| puts "NBG Found" }
36
+ connection.server_types.each { |x| puts "Server Type #{x.name} (#{x.cores}/#{x.memory}/#{x.disk})" }
37
+ connection.server_types.all(:name => 'cx11').each { |x| puts "Server cx11 found" }
38
+
39
+ ## create or select ssh key ...
40
+ ssh_key = connection.ssh_keys.all(:name => ssh_keyname).first
41
+ if !ssh_key
42
+ puts "Creating SSH Key ..."
43
+ ssh_key = connection.ssh_keys.create(:name => ssh_keyname, :public_key => ssh_public_keyfile)
44
+ else
45
+ puts "Using existing SSH Key ..."
46
+ end
47
+
48
+ puts "Creating Floating IP"
49
+ floating_ip = connection.floating_ips.create(:type => 'ipv4', :home_location => 'fsn1' )
50
+ puts "Using existing VIP #{floating_ip.ip} #{floating_ip.server.nil? ? 'unbound' : 'assigned to' + floating_ip.server.name}"
51
+
52
+ # lookup existing server by name or create new server, works with most resources
53
+ server = connection.servers.all(:name => servername).first
54
+ if !server
55
+ puts "Bootstrapping Server (waiting until ssh is ready)..."
56
+ server = connection.servers.bootstrap(
57
+ :name => servername,
58
+ :image => 'centos-7',
59
+ :server_type => 'cx11',
60
+ :location => 'nbg1',
61
+ :user_data => "./userdata.txt",
62
+ :private_key_path => ssh_private_keyfile,
63
+ :ssh_keys => [ ssh_key.identity ],
64
+ :bootstap_timeout => 120,
65
+ )
66
+ else
67
+ puts "Using existing Server ... "
68
+ end
69
+ puts "Server public_ip_address:#{server.public_ip_address} (#{server.public_dns_name}/#{server.reverse_dns_name})"
70
+
71
+ puts "Assign VIP #{floating_ip.ip} to #{server.name}"
72
+ floating_ip.assign(server)
73
+
74
+ # now wait for the server to boot
75
+ puts "Waiting for Server SSH ..."
76
+ server.wait_for { server.sshable? }
77
+
78
+ puts "Adding VIP to server"
79
+ server.ssh("/sbin/ip addr add dev eth0 #{floating_ip.ip}")
80
+
81
+ puts "Changing Root Password"
82
+ action,password = server.reset_root_password
83
+ puts "New Root password #{password}"
84
+
85
+ puts "Unassigning VIP"
86
+ floating_ip.unassign()
87
+
88
+ puts "SSH in server ..."
89
+ puts server.ssh('cat /tmp/test').first.stdout # => "test1\r\n"
90
+
91
+ puts "Setting API to syncronous mode (wait for action to complete)"
92
+ server.sync=true
93
+
94
+ puts "Create Image (Sync) ..."
95
+ action, image_snapshot = server.create_image()
96
+
97
+ puts "Enable Backup ..."
98
+ server.enable_backup
99
+
100
+ puts "Create Backup (ASync) ..."
101
+ action, image_backup = server.create_backup()
102
+
103
+ puts "Disable Backup ..."
104
+ server.enable_backup
105
+
106
+ # Boot Server in rescue system with SSH Keys
107
+ puts "Booting into rescue mode"
108
+ server.enable_rescue(:ssh_keys => [ ssh_keyname ])
109
+ server.reboot()
110
+ server.wait_for { server.sshable? }
111
+
112
+ puts "SSH in Rescue System ..."
113
+ puts server.ssh('hostname').first.stdout # => "test1\r\n"
114
+
115
+ # Reboot again
116
+ puts "Booting into normal mode"
117
+ server.disable_rescue()
118
+ server.reset()
119
+ server.wait_for { server.sshable? }
120
+
121
+ # Change Server Type
122
+ puts "Changing Server Type to cx21"
123
+ server.shutdown()
124
+ # FIXME: Handle in GEM, sometimes API error ?
125
+ server.wait_for { server.stopped? }
126
+ server.change_type(:upgrade_disk => false, :server_type => 'cx21')
127
+ server.poweron()
128
+ server.wait_for { server.sshable? }
129
+
130
+ puts server.ssh('hostname').first.stdout # => "test1\r\n"
131
+
132
+ # Change PTR
133
+ puts "Changing PTR"
134
+ server.change_dns_ptr('www.elconas.de')
135
+ server.reload
136
+
137
+ puts "PowerOff Server"
138
+ server.poweroff
139
+
140
+ puts "Setting API to asynchronous mode"
141
+ server.async=true
142
+
143
+ puts "Destroy Server ..."
144
+ server.destroy
145
+ rescue Exception => e
146
+ server.destroy unless server.nil?
147
+ image_snapshot.destroy unless image_snapshot.nil?
148
+ image_backup.destroy unless image_backup.nil?
149
+ floating_ip.destroy unless floating_ip.nil?
150
+ ssh_key.destroy unless ssh_key.nil?
151
+
152
+ ## List Accounts
153
+ connection.servers.each { |x|
154
+ puts "Server #{x.id}: #{x.name} #{x.public_ip_address}"
155
+ }
156
+ connection.floating_ips.each { |x|
157
+ puts "Floating IP #{x.id}: #{x.ip}"
158
+ }
159
+ connection.images.all(:type => 'snapshot').each { |x|
160
+ puts "Snapshot Image #{x.id}: #{x.description}"
161
+ }
162
+ connection.images.all(:type => 'backup').each { |x|
163
+ puts "Backup Image #{x.id}: #{x.description}"
164
+ }
165
+ raise e
166
+ end
File without changes
@@ -0,0 +1,3 @@
1
+ #cloud-config
2
+ runcmd:
3
+ - hostname > /tmp/test
@@ -14,7 +14,6 @@ Gem::Specification.new do |spec|
14
14
  spec.license = 'APACHE-2.0'
15
15
 
16
16
  spec.files = `git ls-files -z`.split("\x0").reject { |f|
17
- puts "Checking file #{f}"
18
17
  f.match(%r{^(test|spec|features)/}) || f.match(%r{^\.})
19
18
  }
20
19
  spec.require_paths = ['lib']
@@ -22,12 +21,13 @@ Gem::Specification.new do |spec|
22
21
  spec.add_development_dependency 'bundler'
23
22
  spec.add_development_dependency 'coveralls'
24
23
  spec.add_development_dependency 'minitest'
25
- spec.add_development_dependency 'net-ssh'
26
24
  spec.add_development_dependency 'pry'
27
25
  spec.add_development_dependency 'rake'
28
26
  spec.add_development_dependency 'rubocop'
29
27
  spec.add_development_dependency 'simplecov'
28
+ spec.add_development_dependency 'bump'
30
29
 
31
30
  spec.add_dependency 'fog-core', '>= 1.45.0'
32
31
  spec.add_dependency 'fog-json', '>= 1.0.2'
32
+ spec.add_dependency 'net-ssh'
33
33
  end
@@ -75,7 +75,7 @@ module Fog
75
75
  request :list_datacenters
76
76
  request :get_datacenter
77
77
 
78
- # Datacenters
78
+ # Ssh Keys
79
79
  request :list_ssh_keys
80
80
  request :get_ssh_key
81
81
  request :create_ssh_key
@@ -128,7 +128,7 @@ module Fog
128
128
  end
129
129
 
130
130
  def endpoint
131
- 'https://api.hetzner.cloud/v1'
131
+ 'https://api.hetzner.cloud/'
132
132
  end
133
133
 
134
134
  def camelize(str)
@@ -8,7 +8,6 @@ module Fog
8
8
  attribute :command
9
9
  attribute :status
10
10
  attribute :progress
11
- # attribute :extra_volumes
12
11
  attribute :started
13
12
  attribute :finished
14
13
  attribute :resources
@@ -9,6 +9,19 @@ module Fog
9
9
  attribute :description
10
10
  attribute :location
11
11
  attribute :server_types
12
+
13
+ def location=(value)
14
+ attributes[:location] = case value
15
+ when Hash
16
+ service.locations.new(value)
17
+ when String
18
+ service.locations.all(name: value).first
19
+ when Integer
20
+ service.locations.get(value)
21
+ else
22
+ value
23
+ end
24
+ end
12
25
  end
13
26
  end
14
27
  end
@@ -81,8 +81,19 @@ module Fog
81
81
  false
82
82
  end
83
83
  else
84
+ server_id = case serverid
85
+ when Fog::Hetznercloud::Compute::Server
86
+ serverid.identity
87
+ when String
88
+ service.servers.all(name: serverid).first.identity
89
+ when Integer
90
+ serverid
91
+ else
92
+ raise Fog::Hetznercloud::Error::InvalidInputError, 'ERROR: Need Fog::Hetznercloud::Compute::Server|String|Integer'
93
+ end
94
+
84
95
  body = {
85
- server: serverid
96
+ server: server_id
86
97
  }
87
98
 
88
99
  if (floating_ip = service.floating_ip_assign_to_server(identity, body).body['floating_ip'])
@@ -8,7 +8,6 @@ module Fog
8
8
  attribute :name
9
9
  attribute :description
10
10
  attribute :country
11
- # attribute :extra_volumes
12
11
  attribute :city
13
12
  attribute :latitude
14
13
  attribute :longitude
@@ -105,7 +105,7 @@ module Fog
105
105
  end
106
106
 
107
107
  def user_data=(value)
108
- attributes[:user_data] = if value =~ /^\.?\/[^\/]+/
108
+ attributes[:user_data] = if value =~ /^(\.|~)?\/[^\/]+/
109
109
  File.read(value)
110
110
  else
111
111
  value
@@ -17,22 +17,47 @@ module Fog
17
17
  nil
18
18
  end
19
19
 
20
- def bootstrap(bootstap_timeout: 120, new_attributes: {}, ssh_keys:)
20
+ # Sets the proc used to determine the IP Address used for ssh/scp interactions.
21
+ # @example
22
+ # service.servers.bootstrap :name => "bootstrap-server",
23
+ # :image => service.flavors.first.id,
24
+ # :server_type => service.server_types.all(:name => 'cx11').identity,
25
+ # :private_key_path => "~/.ssh/fog_rsa",
26
+ # :user_data => "./file",
27
+ # :location => 'nbg1',
28
+ # :ssh_keys => [ 'keyname' ],
29
+ # :bootstap_timeout => 120
30
+ #
31
+ # @note
32
+ # When booting without ssh_keys set, bootstrapping will wait
33
+ # until the server is read and not until you can ssh into the server
34
+ # Bootstrapping waits until bootstap_timeout seconds and then destroy
35
+ # the server. Default timeout is 120 seconds, which should be ok
36
+ # unless you have a lot of user-data scripts.
37
+ def bootstrap(new_attributes = {})
21
38
  require 'securerandom'
22
39
 
23
40
  defaults = {
24
41
  name: "hc-#{SecureRandom.hex(3)}",
25
42
  image: 'centos-7',
26
- server_type: 'cx11',
27
- ssh_keys: ssh_keys
43
+ server_type: 'cx11'
28
44
  }
29
45
 
46
+ bootstap_timeout = new_attributes[:bootstap_timeout].nil? ? 120 : new_attributes[:bootstap_timeout]
47
+
30
48
  server = create(defaults.merge(new_attributes))
31
49
 
50
+ # if ssh keys are provided wait until we can SSH the server
51
+ # othwise just wait till the server is ready as we will never
52
+ # be able to login
32
53
  status = false
33
54
  begin
34
55
  status = Timeout.timeout(bootstap_timeout) do
35
- server.wait_for { sshable?(auth_methods: %w[publickey]) }
56
+ if new_attributes[:ssh_keys].nil?
57
+ server.wait_for { ready? }
58
+ else
59
+ server.wait_for { sshable?(auth_methods: %w[publickey]) }
60
+ end
36
61
  end
37
62
  rescue Timeout::Error => e
38
63
  server.destroy
@@ -8,68 +8,8 @@ module Fog
8
8
  end
9
9
 
10
10
  class Mock
11
- def update_server(server_id, body)
12
- body = jsonify(body)
13
-
14
- server = lookup(:servers, server_id)
15
-
16
- bootscript = if body['bootscript'].is_a?(Hash)
17
- lookup(:bootscripts, body['bootscript']['id'])
18
- elsif body['bootscript'].is_a?(String)
19
- lookup(:bootscripts, body['bootscript'])
20
- end
21
-
22
- _, product_server = lookup_product_server(server['commercial_type'])
23
-
24
- if body['enable_ipv6'] && !product_server['network']['ipv6_support']
25
- raise_invalid_request_error("Cannot enable ipv6 on #{commercial_type}")
26
- end
27
-
28
- volumes = {}
29
- body['volumes'].each do |index, volume|
30
- volume = lookup(:volumes, volume['id'])
31
-
32
- if volume['server'] && volume['server']['id'] != server['id']
33
- message = "volume #{volume['id']} is already attached to a server"
34
- raise_invalid_request_error(message)
35
- end
36
-
37
- volumes[index] = volume
38
- end
39
-
40
- server['bootscript'] = bootscript if bootscript
41
- server['dynamic_ip_required'] = body['dynamic_ip_required']
42
- server['enable_ipv6'] = body['enable_ipv6']
43
- server['hostname'] = body['name']
44
- server['modification_date'] = now
45
- server['name'] = body['name']
46
- server['tags'] = body['tags']
47
- server['volumes'] = volumes
48
-
49
- if server['dynamic_ip_required'] && server['state'] == 'running'
50
- server['public_ip'] ||= create_dynamic_ip
51
- elsif !server['dynamic_ip_required'] && server['public_ip'] && server['public_ip']['dynamic']
52
- server['public_ip'] = nil
53
- end
54
-
55
- if server['enable_ipv6'] && server['state'] == 'running'
56
- server['ipv6'] ||= create_ipv6
57
- elsif !server['enable_ipv6']
58
- server['ipv6'] = nil
59
- end
60
-
61
- data[:volumes].each do |id, volume|
62
- if server['volumes'].any? { |_i, v| v['id'] == id }
63
- volume['server'] = {
64
- 'id' => server['id'],
65
- 'name' => server['name']
66
- }
67
- elsif volume['server'] && volume['server']['id'] == server['id']
68
- volume['server'] = nil
69
- end
70
- end
71
-
72
- response(status: 200, body: { 'server' => server })
11
+ def update_server(_server_id, _body)
12
+ Fog::Mock.not_implemented
73
13
  end
74
14
  end
75
15
  end
@@ -1,5 +1,5 @@
1
1
  module Fog
2
2
  module Hetznercloud
3
- VERSION = '0.0.1'.freeze
3
+ VERSION = '0.0.2'.freeze
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fog-hetznercloud
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Heinzmann
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-03-06 00:00:00.000000000 Z
11
+ date: 2018-03-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -53,7 +53,7 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: net-ssh
56
+ name: pry
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ">="
@@ -67,7 +67,7 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: pry
70
+ name: rake
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - ">="
@@ -81,7 +81,7 @@ dependencies:
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
- name: rake
84
+ name: rubocop
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - ">="
@@ -95,7 +95,7 @@ dependencies:
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
97
  - !ruby/object:Gem::Dependency
98
- name: rubocop
98
+ name: simplecov
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - ">="
@@ -109,7 +109,7 @@ dependencies:
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
111
  - !ruby/object:Gem::Dependency
112
- name: simplecov
112
+ name: bump
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
115
  - - ">="
@@ -150,6 +150,20 @@ dependencies:
150
150
  - - ">="
151
151
  - !ruby/object:Gem::Version
152
152
  version: 1.0.2
153
+ - !ruby/object:Gem::Dependency
154
+ name: net-ssh
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :runtime
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
153
167
  description: Fog provider gem to support the Hetzner Cloud.
154
168
  email:
155
169
  - reg@elconas.de
@@ -163,6 +177,10 @@ files:
163
177
  - Rakefile
164
178
  - bin/console
165
179
  - bin/setup
180
+ - examples/Gemfile
181
+ - examples/integrationtest.rb
182
+ - examples/keys/.dummy
183
+ - examples/userdata.txt
166
184
  - fog-hetznercloud.gemspec
167
185
  - lib/fog/hetznercloud.rb
168
186
  - lib/fog/hetznercloud/client.rb