hetzner-bootstrap 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,32 @@
1
+ = hetzner-bootstrap
2
+
3
+ hetzner-bootstrap allows you to bootstrap a provisioned EQ Server from hetzner.de
4
+
5
+ Requirements
6
+
7
+ - get a webservice login from your customer panel (https://robot.your-server.de/)
8
+ - the ip address of the shipped system(s)
9
+
10
+ == Installation
11
+
12
+ <tt> gem install hetzner-bootstrap</tt>
13
+
14
+ == Example
15
+
16
+ <b>see example.rb for usage!</b>
17
+
18
+ == WARNING!
19
+
20
+ This is not an official Hetzner AG project.
21
+
22
+ The gem and the author are not related to Hetzner AG!
23
+
24
+ <b>Use at your very own risk! Satisfaction NOT guaranteed!</b>
25
+
26
+ == Copyright
27
+
28
+ Copyright (c) 2011 Moriz GmbH, Roland Moriz. See LICENSE for details.
29
+
30
+ {Ruby on Rails Entwicklung}[http://moriz.de/] -> Moriz GmbH
31
+
32
+ {Ruby on Rails Hosting}[http://rails.io/] -> Rails.io
data/example.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ require "rubygems"
2
3
  require "hetzner-bootstrap"
3
4
 
4
5
  API_USERNAME="xxx"
@@ -1,4 +1,5 @@
1
1
  require 'benchmark'
2
+ require 'logger'
2
3
 
3
4
  require 'hetzner-api'
4
5
  require 'hetzner/bootstrap/version'
@@ -9,21 +10,25 @@ module Hetzner
9
10
  class Bootstrap
10
11
  attr_accessor :targets
11
12
  attr_accessor :api
12
- attr_accessor :use_threads
13
13
  attr_accessor :actions
14
+ attr_accessor :logger
14
15
 
15
16
  def initialize(options = {})
16
17
  @targets = []
17
18
  @actions = %w(enable_rescue_mode
18
19
  reset
19
- wait_for_ssh
20
+ wait_for_ssh_down
21
+ wait_for_ssh_up
20
22
  installimage
21
- wait_for_ssh
23
+ reboot
24
+ wait_for_ssh_down
25
+ wait_for_ssh_up
22
26
  verify_installation
23
27
  copy_ssh_keys
24
28
  post_install)
29
+ #@actions = %w(wait_for_ssh_down)
25
30
  @api = options[:api]
26
- @use_threads = options[:use_threads] || true
31
+ @logger = options[:logger] || Logger.new(STDOUT)
27
32
  end
28
33
 
29
34
  def add_target(param)
@@ -37,50 +42,31 @@ module Hetzner
37
42
  def <<(param)
38
43
  add_target param
39
44
  end
40
-
41
- def bootstrap!(options = {})
42
- threads = []
43
45
 
46
+ def bootstrap!(options = {})
44
47
  @targets.each do |target|
45
- target.use_api @api
46
-
47
- if uses_threads?
48
- threads << Thread.new do
49
- bootstrap_one_target! target
50
- end
51
- else
48
+ #fork do
49
+ target.use_api @api
50
+ target.use_logger @logger
52
51
  bootstrap_one_target! target
53
- end
52
+ #end
54
53
  end
55
-
56
- finalize_threads(threads) if uses_threads?
54
+ #Process.waitall
57
55
  end
58
56
 
59
57
  def bootstrap_one_target!(target)
60
58
  actions = (target.actions || @actions)
61
59
  actions.each_with_index do |action, index|
62
-
63
- log target.ip, action, index, 'START'
60
+
61
+ loghack = "\b" * 24 # remove: "[bootstrap_one_target!] ".length
62
+ target.logger.info "#{loghack}[#{action}] #{sprintf "%-20s", "START"}"
64
63
  d = Benchmark.realtime do
65
64
  target.send action
66
65
  end
67
-
68
- log target.ip, action, index, "FINISHED in #{sprintf "%.5f",d} seconds"
66
+ target.logger.info "#{loghack}[#{action}] FINISHED in #{sprintf "%.5f",d} seconds"
69
67
  end
70
68
  rescue => e
71
- puts "something bad happend: #{e.class} #{e.message}"
72
- end
73
-
74
- def uses_threads?
75
- @use_threads
76
- end
77
-
78
- def finalize_threads(threads)
79
- threads.each { |t| t.join }
80
- end
81
-
82
- def log(where, what, index, message)
83
- puts "[#{where}] #{what} #{' ' * (index * 4)}#{message}"
69
+ puts "something bad happened unexpectedly: #{e.class} => #{e.message}"
84
70
  end
85
71
  end
86
72
  end
@@ -1,6 +1,7 @@
1
1
  require 'erubis'
2
2
  require 'net/ssh'
3
3
  require 'socket'
4
+ require 'timeout'
4
5
 
5
6
  module Hetzner
6
7
  class Bootstrap
@@ -16,13 +17,15 @@ module Hetzner
16
17
  attr_accessor :post_install
17
18
  attr_accessor :public_keys
18
19
  attr_accessor :bootstrap_cmd
20
+ attr_accessor :logger
19
21
 
20
22
  def initialize(options = {})
21
23
  @rescue_os = 'linux'
22
24
  @rescue_os_bit = '64'
23
25
  @retries = 0
24
26
  @bootstrap_cmd = '/root/.oldroot/nfs/install/installimage -a -c /tmp/template'
25
-
27
+ @login = 'root'
28
+
26
29
  if tmpl = options.delete(:template)
27
30
  @template = Template.new tmpl
28
31
  else
@@ -38,19 +41,19 @@ module Hetzner
38
41
  result = @api.enable_rescue! @ip, @rescue_os, @rescue_os_bit
39
42
 
40
43
  if result.success? && result['rescue']
41
- @login = 'root'
42
44
  @password = result['rescue']['password']
43
45
  reset_retries
44
- puts "IP: #{ip} => password: #{@password}"
46
+ logger.info "IP: #{ip} => password: #{@password}"
45
47
  elsif @retries > 3
48
+ logger.error "rescue system could not be activated"
46
49
  raise CantActivateRescueSystemError, result
47
50
  else
48
51
  @retries += 1
49
52
 
50
- puts "problem while trying to activate rescue system (retries: #{@retries})"
53
+ logger.warn "problem while trying to activate rescue system (retries: #{@retries})"
51
54
  @api.disable_rescue! @ip
52
55
 
53
- sleep @retries * 5 # => 5, 10, 15s
56
+ rolling_sleep
54
57
  enable_rescue_mode options
55
58
  end
56
59
  end
@@ -60,37 +63,58 @@ module Hetzner
60
63
 
61
64
  if result.success?
62
65
  reset_retries
63
- sleep 15
64
66
  elsif @retries > 3
67
+ logger.error "resetting through webservice failed."
65
68
  raise CantResetSystemError, result
66
69
  else
67
70
  @retries += 1
71
+ logger.warn "problem while trying to reset/reboot system (retries: #{@retries})"
68
72
  rolling_sleep
69
- puts "problem while trying to reset/reboot system (retries: #{@retries})"
70
73
  reset options
71
74
  end
72
75
  end
73
76
 
74
- def wait_for_ssh(options = {})
75
- ssh_port_probe = TCPSocket.new @ip, 22
76
- return if IO.select([ssh_port_probe], nil, nil, 5)
77
+ def port_open? ip, port
78
+ ssh_port_probe = TCPSocket.new ip, port
79
+ IO.select([ssh_port_probe], nil, nil, 2)
80
+ ssh_port_probe.close
81
+ true
82
+ end
77
83
 
84
+ def wait_for_ssh_down(options = {})
85
+ loop do
86
+ Timeout::timeout(3) do
87
+ if port_open? @ip, 22
88
+ logger.debug "SSH UP"
89
+ sleep 10
90
+ else
91
+ raise Errno::ECONNREFUSED
92
+ end
93
+ end
94
+ sleep 2
95
+ end
96
+ rescue Timeout::Error
97
+ sleep 2
98
+ retry
78
99
  rescue Errno::ECONNREFUSED
79
- @retries += 1
80
- print "."
81
- STDOUT.flush
100
+ logger.debug "SHH DOWN"
101
+ end
82
102
 
83
- if @retries > 20
84
- raise CantSshAfterResetError
85
- else
86
- rolling_sleep
87
- wait_for_ssh options
103
+ def wait_for_ssh_up(options = {})
104
+ loop do
105
+ Timeout::timeout(4) do
106
+ if port_open? @ip, 22
107
+ logger.debug "SSH UP"
108
+ return true
109
+ else
110
+ raise Errno::ECONNREFUSED
111
+ end
112
+ end
88
113
  end
89
- rescue => e
90
- puts "Exception: #{e.class} #{e.message}"
91
- ensure
92
- puts ""
93
- ssh_port_probe && ssh_port_probe.close
114
+ rescue Errno::ECONNREFUSED, Timeout::Error
115
+ logger.debug "SSH DOWN"
116
+ sleep 2
117
+ retry
94
118
  end
95
119
 
96
120
  def installimage(options = {})
@@ -98,11 +122,18 @@ module Hetzner
98
122
 
99
123
  Net::SSH.start(@ip, @login, :password => @password) do |ssh|
100
124
  ssh.exec!("echo \"#{template}\" > /tmp/template")
101
- puts "remote executing: #{@bootstrap_cmd}"
125
+ logger.info "remote executing: #{@bootstrap_cmd}"
102
126
  output = ssh.exec!(@bootstrap_cmd)
103
- puts output
127
+ logger.info output
128
+ end
129
+ rescue Net::SSH::HostKeyMismatch => e
130
+ e.remember_host!
131
+ retry
132
+ end
133
+
134
+ def reboot(options = {})
135
+ Net::SSH.start(@ip, @login, :password => @password) do |ssh|
104
136
  ssh.exec!("reboot")
105
- sleep 4
106
137
  end
107
138
  rescue Net::SSH::HostKeyMismatch => e
108
139
  e.remember_host!
@@ -139,8 +170,8 @@ module Hetzner
139
170
  def post_install(options = {})
140
171
  return unless @post_install
141
172
  post_install = render_post_install
142
- puts "executing:\n #{post_install}"
143
- puts `#{post_install}`
173
+ logger.info "executing post_install:\n #{post_install}"
174
+ logger.info `#{post_install}`
144
175
  end
145
176
 
146
177
  def render_template
@@ -165,8 +196,13 @@ module Hetzner
165
196
  return eruby.result(params)
166
197
  end
167
198
 
168
- def use_api(api)
169
- @api = api
199
+ def use_api(api_obj)
200
+ @api = api_obj
201
+ end
202
+
203
+ def use_logger(logger_obj)
204
+ @logger = logger_obj
205
+ @logger.formatter = default_log_formatter
170
206
  end
171
207
 
172
208
  def reset_retries
@@ -177,6 +213,13 @@ module Hetzner
177
213
  sleep @retries * @retries * 3 + 1 # => 1, 4, 13, 28, 49, 76, 109, 148, 193, 244, 301, 364 ... seconds
178
214
  end
179
215
 
216
+ def default_log_formatter
217
+ proc do |severity, datetime, progname, msg|
218
+ caller[4]=~/`(.*?)'/
219
+ "[#{datetime.strftime "%H:%M:%S"}][#{sprintf "%-15s", ip}][#{$1}] #{msg}\n"
220
+ end
221
+ end
222
+
180
223
  class NoTemplateProvidedError < ArgumentError; end
181
224
  class CantActivateRescueSystemError < StandardError; end
182
225
  class CantResetSystemError < StandardError; end
@@ -1,5 +1,5 @@
1
1
  module Hetzner
2
2
  class Bootstrap
3
- VERSION = '0.0.1'
3
+ VERSION = '0.0.2'
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hetzner-bootstrap
3
3
  version: !ruby/object:Gem::Version
4
- hash: 29
4
+ hash: 27
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 1
10
- version: 0.0.1
9
+ - 2
10
+ version: 0.0.2
11
11
  platform: ruby
12
12
  authors:
13
13
  - Roland Moriz
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-02-06 00:00:00 +01:00
18
+ date: 2011-02-11 00:00:00 +01:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -89,7 +89,7 @@ files:
89
89
  - .gitignore
90
90
  - Gemfile
91
91
  - LICENSE
92
- - README
92
+ - README.rdoc
93
93
  - Rakefile
94
94
  - example.rb
95
95
  - hetzner-bootstrap.gemspec
data/README DELETED
@@ -1,9 +0,0 @@
1
- hetzner-bootstrap allows you to bootstrap a provisioned EQ Server from hetzner.de
2
-
3
- Requirements
4
-
5
- - get a webservice login (robots.your-server.de)
6
- - the ip address of the shipped systems
7
-
8
- see example.rb for usage
9
-