hetzner-bootstrap 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4ba4fde8b05ff3b0e9f54e0c2f8f5d29227853ce
4
+ data.tar.gz: a56548e44c4e384393e536dc1ff161e45994de88
5
+ SHA512:
6
+ metadata.gz: 59f476a35614db959c5dbd7fe2a7a09e380a2476566e32a7ce5975f0813b190b4e9923d209d00f4c4efe4fe8a702d96441f424b2b2d1a357b92c22d89a4e0ccd
7
+ data.tar.gz: 59cd2b96ec2b22fc68878b2cc5b7fdbbd2a1767eaf492894a265c181d0c168ff3f27dc1f80dfc3373795427902c7adb3569fa55cacd50de10807ced6a81ad9b8
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,17 @@
1
+ Metrics/LineLength:
2
+ Enabled: false
3
+
4
+ Metrics/MethodLength:
5
+ Enabled: false
6
+
7
+ Style/FileName:
8
+ Enabled: false
9
+
10
+ Style/Documentation:
11
+ Enabled: false
12
+
13
+ Metrics/ClassLength:
14
+ Enabled: false
15
+
16
+ Metrics/AbcSize:
17
+ Enabled: false
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
- source "https://rubygems.org"
1
+ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in hetzner.gemspec
4
4
  gemspec
data/README.md CHANGED
@@ -3,6 +3,8 @@ hetzner-bootstrap
3
3
 
4
4
  hetzner-bootstrap allows you to bootstrap a provisioned EQ Server from hetzner.de
5
5
 
6
+ [![Gem Version](https://badge.fury.io/rb/hetzner-bootstrap.png)](http://badge.fury.io/rb/hetzner-bootstrap)
7
+
6
8
  What it does:
7
9
  -------------
8
10
 
@@ -38,6 +40,116 @@ Example:
38
40
 
39
41
  Warning: All existing data on the system will be lost!
40
42
 
43
+
44
+
45
+ ```ruby
46
+
47
+ #!/usr/bin/env ruby
48
+ require 'hetzner-bootstrap'
49
+
50
+ bs = Hetzner::Bootstrap.new(api: Hetzner::API.new(ENV['ROBOT_USER'], ENV['ROBOT_PASSWORD']))
51
+
52
+ template = <<EOT
53
+ DRIVE1 /dev/sda
54
+ DRIVE2 /dev/sdb
55
+
56
+ ## activate software RAID? < 0 | 1 >
57
+ SWRAID 1
58
+
59
+ ## Choose the level for the software RAID < 0 | 1 >
60
+ SWRAIDLEVEL 1
61
+
62
+ ## which bootloader should be used? < lilo | grub >
63
+ BOOTLOADER grub
64
+
65
+ HOSTNAME <%= hostname %>
66
+
67
+ ## PART <mountpoint/lvm> <filesystem/VG> <size in MB>
68
+ ##
69
+ ## * <mountpoint/lvm> mountpoint for this filesystem *OR* keyword 'lvm'
70
+ ## to use this PART as volume group (VG) for LVM
71
+ ## * <filesystem/VG> can be ext2, ext3, reiserfs, xfs, swap *OR* name
72
+ ## of the LVM volume group (VG), if this PART is a VG
73
+ ## * <size> you can use the keyword 'all' to assign all the
74
+ ## remaining space of the drive to the *last* partition.
75
+ ## you can use M/G/T for unit specification in MIB/GIB/TIB
76
+ ##
77
+ ## notes:
78
+ ## - extended partitions are created automatically
79
+ ## - '/boot' cannot be on a xfs filesystem!
80
+ ## - '/boot' cannot be on LVM!
81
+ ## - when using software RAID 0, you need a '/boot' partition
82
+
83
+ PART /boot ext3 1G
84
+ PART lvm host 75G
85
+ PART lvm guest all
86
+
87
+ #LV <VG> <name> <mount> <filesystem> <size>
88
+ LV host root / ext4 50G
89
+ LV host swap swap swap 5G
90
+
91
+
92
+ ## ========================
93
+ ## OPERATING SYSTEM IMAGE:
94
+ ## ========================
95
+
96
+ ## full path to the operating system image
97
+ ## supported image sources: local dir, ftp, http, nfs
98
+ ## supported image types: tar, tar.gz, tar.bz, tar.bz2, tgz, tbz
99
+ ## examples:
100
+ #
101
+ # local: /path/to/image/filename.tar.gz
102
+ # ftp: ftp://<user>:<password>@hostname/path/to/image/filename.tar.bz2
103
+ # http: http://<user>:<password>@hostname/path/to/image/filename.tbz
104
+ # https: https://<user>:<password>@hostname/path/to/image/filename.tbz
105
+ # nfs: hostname:/path/to/image/filename.tgz
106
+
107
+ # Default images provided by hetzner as of October 2014:
108
+ # Archlinux-2014-64-minmal.tar.gz
109
+ # CentOS-65-32-minimal.tar.gz
110
+ # CentOS-65-64-cpanel.tar.gz
111
+ # CentOS-65-64-minimal.tar.gz
112
+ # CentOS-70-64-minimal.tar.gz
113
+ # Debian-76-wheezy-32-minimal.tar.gz
114
+ # Debian-76-wheezy-64-LAMP.tar.gz
115
+ # Debian-76-wheezy-64-minimal.tar.gz
116
+ # openSUSE-131-64-minimal.tar.gz
117
+ # Ubuntu-1204-precise-64-minimal.tar.gz
118
+ # Ubuntu-1404-trusty-64-minimal.tar.gz
119
+
120
+
121
+ IMAGE /root/images/Ubuntu-1404-trusty-64-minimal.tar.gz
122
+
123
+ EOT
124
+
125
+ # the post_install hook is a great place to setup further software/system provisioning
126
+ #
127
+ post_install = <<EOT
128
+ # knife bootstrap <%= ip %> -N <%= hostname %> "role[base],role[kvm_host]"
129
+ EOT
130
+
131
+ bs << {
132
+ ip: '1.2.3.4',
133
+ template: template, # string will be parsed by erubis
134
+ hostname: 'server100.example.com', # will be used for setting the systems' hostname
135
+ public_keys: "~/.ssh/id_dsa.pub", # will be copied over to the freshly bootstrapped system
136
+ post_install: post_install # will be called locally at the end and can be used e.g. to run a chef bootstrap
137
+ }
138
+
139
+ bs << {
140
+ ip: '1.2.3.5',
141
+ template: template, # string will be parsed by erubis
142
+ hostname: 'server101.example.com', # will be used for setting the systems' hostname
143
+ public_keys: "~/.ssh/id_dsa.pub", # will be copied over to the freshly bootstrapped system
144
+ post_install: post_install # will be called locally at the end and can be used e.g. to run a chef bootstrap
145
+ }
146
+
147
+
148
+ bs.bootstrap!
149
+
150
+
151
+ ```
152
+
41
153
  Installation:
42
154
  -------------
43
155
 
@@ -63,4 +175,7 @@ Commercial Support available through:
63
175
  Copyright
64
176
  ---------
65
177
 
66
- Copyright (c) 2011 Moriz GmbH, Roland Moriz. See LICENSE file for details.
178
+ Copyright © 2013 [Roland Moriz](https://roland.io), [Moriz GmbH](https://moriz.de/)
179
+
180
+ [![LinkedIn](http://www.linkedin.com/img/webpromo/btn_viewmy_160x25.png)](http://www.linkedin.com/in/rmoriz)
181
+ [![Twitter](http://i.imgur.com/1kYFHlu.png)](https://twitter.com/rmoriz)
data/example.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
- require "rubygems"
3
- require "hetzner-bootstrap"
2
+ require 'rubygems'
3
+ require 'hetzner-bootstrap'
4
4
 
5
5
  # get your API login from Hetzner's customer panel at: https://robot.your-server.de/
6
6
  # assign env variables:
@@ -10,7 +10,10 @@ require "hetzner-bootstrap"
10
10
  # rbenv-tip: checkout rbenv-vars, it's awesome!
11
11
  # https://github.com/sstephenson/rbenv-vars/
12
12
 
13
- bs = Hetzner::Bootstrap.new :api => Hetzner::API.new(ENV['ROBOT_USER'], ENV['ROBOT_PASSWORD'])
13
+ bs = Hetzner::Bootstrap.new(api: Hetzner::API.new(
14
+ ENV['ROBOT_USER'],
15
+ ENV['ROBOT_PASSWORD']
16
+ ))
14
17
 
15
18
  # 2 disks, software raid 1, etc.
16
19
  template = <<EOT
@@ -73,42 +76,36 @@ LV host swap swap swap 5G
73
76
  # https: https://<user>:<password>@hostname/path/to/image/filename.tbz
74
77
  # nfs: hostname:/path/to/image/filename.tgz
75
78
 
76
- # Default images provided by hetzner as of August 2012:
77
- # CentOS-58-32-minimal.tar.gz
78
- # CentOS-58-64-minimal.tar.gz
79
- # CentOS-62-32-minimal.tar.gz
80
- # CentOS-62-64-cpanel.tar.gz
81
- # CentOS-62-64-minimal.tar.gz
82
- # CentOS-63-32-minimal.tar.gz
83
- # CentOS-63-64-cpanel.tar.gz
84
- # CentOS-63-64-minimal.tar.gz
85
- # Debian-60-squeeze-32-minimal.tar.gz
86
- # Debian-60-squeeze-64-LAMP.tar.gz
87
- # Debian-60-squeeze-64-minimal.tar.gz
88
- # openSUSE-121-32-minimal.tar.gz
89
- # openSUSE-121-64-minimal.tar.gz
90
- # Ubuntu-1004-lucid-32-minimal.tar.gz
91
- # Ubuntu-1004-lucid-64-minimal.tar.gz
92
- # Ubuntu-1204-precise-32-minimal.tar.gz
79
+ # Default images provided by hetzner as of October 2014:
80
+ # Archlinux-2014-64-minmal.tar.gz
81
+ # CentOS-65-32-minimal.tar.gz
82
+ # CentOS-65-64-cpanel.tar.gz
83
+ # CentOS-65-64-minimal.tar.gz
84
+ # CentOS-70-64-minimal.tar.gz
85
+ # Debian-76-wheezy-32-minimal.tar.gz
86
+ # Debian-76-wheezy-64-LAMP.tar.gz
87
+ # Debian-76-wheezy-64-minimal.tar.gz
88
+ # openSUSE-131-64-minimal.tar.gz
93
89
  # Ubuntu-1204-precise-64-minimal.tar.gz
90
+ # Ubuntu-1404-trusty-64-minimal.tar.gz
94
91
 
95
92
 
96
93
  IMAGE /root/images/Ubuntu-1204-precise-64-minimal.tar.gz
97
94
 
98
95
  EOT
99
96
 
100
- # the post_install hook is a great place to setup further software/system provisioning
97
+ # the post_install hook is a great place to setup further
98
+ # software/system provisioning
101
99
  #
102
100
  post_install = <<EOT
103
101
  # knife bootstrap <%= ip %> -N <%= hostname %> "role[base],role[kvm_host]"
104
102
  EOT
105
103
 
106
104
  # duplicate entry for each system
107
- bs << { :ip => "1.2.3.4",
108
- :template => template, # string will be parsed by erubis
109
- :hostname => 'server100.example.com', # will be used for setting the systems' hostname
110
- :public_keys => "~/.ssh/id_dsa.pub", # will be copied over to the freshly bootstrapped system
111
- :post_install => post_install } # will be called locally at the end and can be used e.g. to run a chef bootstrap
105
+ bs << { ip: '1.2.3.4',
106
+ template: template, # string will be parsed by erubis
107
+ hostname: 'server100', # sets hostname
108
+ public_keys: '~/.ssh/id_dsa.pub', # will be copied to your system
109
+ post_install: post_install } # will be executed *locally* at the end
112
110
 
113
111
  bs.bootstrap!
114
-
@@ -1,26 +1,27 @@
1
1
  # -*- encoding: utf-8 -*-
2
- $:.push File.expand_path("../lib", __FILE__)
3
- require "hetzner/bootstrap/version"
2
+ $LOAD_PATH.push File.expand_path('../lib', __FILE__)
3
+ require 'hetzner/bootstrap/version'
4
4
 
5
5
  Gem::Specification.new do |s|
6
- s.name = "hetzner-bootstrap"
6
+ s.name = 'hetzner-bootstrap'
7
7
  s.version = Hetzner::Bootstrap::VERSION
8
8
  s.platform = Gem::Platform::RUBY
9
- s.authors = ["Roland Moriz"]
10
- s.email = ["roland@moriz.de"]
11
- s.homepage = "http://moriz.de/opensource/hetzner-api"
12
- s.summary = %q{Easy bootstrapping of hetzner.de rootservers using hetzner-api}
13
- s.description = %q{Easy bootstrapping of hetzner.de rootservers using hetzner-api}
9
+ s.authors = ['Roland Moriz']
10
+ s.email = ['roland@moriz.de']
11
+ s.homepage = 'http://moriz.de/opensource/hetzner-api'
12
+ s.summary = 'Easy bootstrapping of hetzner.de rootservers using hetzner-api'
13
+ s.description = 'Easy bootstrapping of hetzner.de rootservers using hetzner-api'
14
14
 
15
15
  s.add_dependency 'hetzner-api', '>= 1.1.0'
16
16
  s.add_dependency 'net-ssh', '>= 2.6.0'
17
17
  s.add_dependency 'erubis', '>= 2.7.0'
18
18
 
19
- s.add_development_dependency "rspec", ">= 2.13.0"
20
- s.add_development_dependency "rake"
19
+ s.add_development_dependency 'rspec', '~> 3.4.0'
20
+ s.add_development_dependency 'rake'
21
+ s.add_development_dependency 'rubocop', '~> 0.36.0'
21
22
 
22
23
  s.files = `git ls-files`.split("\n")
23
24
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
24
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
25
- s.require_paths = ["lib"]
25
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
26
+ s.require_paths = ['lib']
26
27
  end
@@ -33,42 +33,40 @@ module Hetzner
33
33
  end
34
34
 
35
35
  def add_target(param)
36
- if param.is_a? Hetzner::Bootstrap::Target
37
- @targets << param
38
- else
39
- @targets << (Hetzner::Bootstrap::Target.new param)
40
- end
36
+ @targets << if param.is_a? Hetzner::Bootstrap::Target
37
+ param
38
+ else
39
+ Hetzner::Bootstrap::Target.new(param)
40
+ end
41
41
  end
42
42
 
43
43
  def <<(param)
44
44
  add_target param
45
45
  end
46
46
 
47
- def bootstrap!(options = {})
47
+ def bootstrap!(_options = {})
48
48
  @targets.each do |target|
49
- #fork do
50
- target.use_api @api
51
- target.use_logger @logger
52
- bootstrap_one_target! target
53
- #end
49
+ # fork do
50
+ target.use_api @api
51
+ target.use_logger @logger
52
+ bootstrap_one_target! target
53
+ # end
54
54
  end
55
- #Process.waitall
55
+ # Process.waitall
56
56
  end
57
57
 
58
58
  def bootstrap_one_target!(target)
59
59
  actions = (target.actions || @actions)
60
- actions.each_with_index do |action, index|
61
-
60
+ actions.each_with_index do |action, _index|
62
61
  loghack = "\b" * 24 # remove: "[bootstrap_one_target!] ".length
63
- target.logger.info "#{loghack}[#{action}] #{sprintf "%-20s", "START"}"
62
+ target.logger.info "#{loghack}[#{action}] #{format '%-20s', 'START'}"
64
63
  d = Benchmark.realtime do
65
64
  target.send action
66
65
  end
67
- target.logger.info "#{loghack}[#{action}] FINISHED in #{sprintf "%.5f",d} seconds"
66
+ target.logger.info "#{loghack}[#{action}] FINISHED in #{format '%.5f', d} seconds"
68
67
  end
69
68
  rescue => e
70
69
  puts "something bad happened unexpectedly: #{e.class} => #{e.message}"
71
70
  end
72
71
  end
73
72
  end
74
-
@@ -21,20 +21,19 @@ module Hetzner
21
21
  attr_accessor :logger
22
22
 
23
23
  def initialize(options = {})
24
- @rescue_os = 'linux'
25
- @rescue_os_bit = '64'
26
- @retries = 0
27
- @bootstrap_cmd = 'export TERM=xterm; /root/.oldroot/nfs/install/installimage -a -c /tmp/template'
28
- @login = 'root'
29
-
30
- if tmpl = options.delete(:template)
31
- @template = Template.new tmpl
32
- else
33
- raise NoTemplateProvidedError.new 'No imageinstall template provided.'
34
- end
24
+ @rescue_os = 'linux'
25
+ @rescue_os_bit = '64'
26
+ @retries = 0
27
+ @bootstrap_cmd = 'export TERM=xterm; /root/.oldroot/nfs/install/installimage -a -c /tmp/template'
28
+ @login = 'root'
29
+ @post_install_remote = ''
30
+
31
+ @template = Template.new options.delete(:template)
35
32
 
36
- options.each_pair do |k,v|
37
- self.send("#{k}=", v)
33
+ fail NoTemplateProvidedError 'No imageinstall template provided.' unless @template
34
+
35
+ options.each_pair do |k, v|
36
+ send("#{k}=", v)
38
37
  end
39
38
  end
40
39
 
@@ -46,8 +45,8 @@ module Hetzner
46
45
  reset_retries
47
46
  logger.info "IP: #{ip} => password: #{@password}"
48
47
  elsif @retries > 3
49
- logger.error "rescue system could not be activated"
50
- raise CantActivateRescueSystemError, result
48
+ logger.error 'rescue system could not be activated'
49
+ fail CantActivateRescueSystemError, result
51
50
  else
52
51
  @retries += 1
53
52
 
@@ -65,8 +64,8 @@ module Hetzner
65
64
  if result.success?
66
65
  reset_retries
67
66
  elsif @retries > 3
68
- logger.error "resetting through webservice failed."
69
- raise CantResetSystemError, result
67
+ logger.error 'resetting through webservice failed.'
68
+ fail CantResetSystemError, result
70
69
  else
71
70
  @retries += 1
72
71
  logger.warn "problem while trying to reset/reboot system (retries: #{@retries})"
@@ -75,75 +74,75 @@ module Hetzner
75
74
  end
76
75
  end
77
76
 
78
- def port_open? ip, port
77
+ def port_open?(ip, port)
79
78
  ssh_port_probe = TCPSocket.new ip, port
80
79
  IO.select([ssh_port_probe], nil, nil, 2)
81
80
  ssh_port_probe.close
82
81
  true
83
82
  end
84
83
 
85
- def wait_for_ssh_down(options = {})
84
+ def wait_for_ssh_down
86
85
  loop do
87
86
  sleep 2
88
- Timeout::timeout(4) do
89
- if port_open? @ip, 22
90
- logger.debug "SSH UP"
91
- else
92
- raise Errno::ECONNREFUSED
93
- end
87
+ Timeout.timeout(4) do
88
+ fail Errno::ECONNREFUSED unless port_open? @ip, 22
89
+ logger.debug 'SSH UP'
94
90
  end
95
91
  end
96
92
  rescue Timeout::Error, Errno::ECONNREFUSED
97
- logger.debug "SSH DOWN"
93
+ logger.debug 'SSH DOWN'
98
94
  end
99
95
 
100
- def wait_for_ssh_up(options = {})
96
+ def wait_for_ssh_up
101
97
  loop do
102
- Timeout::timeout(4) do
103
- if port_open? @ip, 22
104
- logger.debug "SSH UP"
105
- return true
106
- else
107
- raise Errno::ECONNREFUSED
108
- end
98
+ Timeout.timeout(4) do
99
+ fail Errno::ECONNREFUSED unless port_open? @ip, 22
100
+
101
+ logger.debug 'SSH UP'
102
+ return true
109
103
  end
110
104
  end
111
105
  rescue Errno::ECONNREFUSED, Timeout::Error
112
- logger.debug "SSH DOWN"
106
+ logger.debug 'SSH DOWN'
113
107
  sleep 2
114
108
  retry
115
109
  end
116
110
 
117
- def installimage(options = {})
111
+ def installimage
118
112
  template = render_template
119
113
 
120
114
  remote do |ssh|
121
115
  ssh.exec! "echo \"#{template}\" > /tmp/template"
122
116
  logger.info "remote executing: #{@bootstrap_cmd}"
123
117
  output = ssh.exec!(@bootstrap_cmd)
124
- logger.info output
118
+ logger.info output.gsub(`clear`, '')
125
119
  end
120
+
121
+ rescue Net::SSH::Disconnect
122
+ puts 'SSH connection was closed.'
126
123
  end
127
124
 
128
- def reboot(options = {})
125
+ def reboot
129
126
  remote do |ssh|
130
- ssh.exec!("reboot")
127
+ ssh.exec!('reboot')
131
128
  end
129
+ rescue Net::SSH::Disconnect
130
+ puts 'SSH connection was closed.'
132
131
  end
133
132
 
134
- def verify_installation(options = {})
133
+ def verify_installation
135
134
  remote do |ssh|
136
- working_hostname = ssh.exec!("cat /etc/hostname")
135
+ working_hostname = ssh.exec!('cat /etc/hostname')
137
136
  unless @hostname == working_hostname.chomp
138
- raise InstallationError, "hostnames do not match: assumed #{@hostname} but received #{working_hostname}"
137
+ logger.debug "hostnames do not match: assumed #{@hostname} but received #{working_hostname}"
139
138
  end
140
139
  end
141
140
  end
142
141
 
143
- def copy_ssh_keys(options = {})
142
+ def copy_ssh_keys
144
143
  if @public_keys
145
144
  remote do |ssh|
146
- ssh.exec!("mkdir /root/.ssh")
145
+ ssh.exec!('mkdir /root/.ssh')
147
146
  Array(@public_keys).each do |key|
148
147
  pub = File.read(File.expand_path(key))
149
148
  ssh.exec!("echo \"#{pub}\" >> /root/.ssh/authorized_keys")
@@ -152,16 +151,16 @@ module Hetzner
152
151
  end
153
152
  end
154
153
 
155
- def update_local_known_hosts(options = {})
156
- remote(:paranoid => true) do |ssh|
154
+ def update_local_known_hosts
155
+ remote(paranoid: true) do |ssh|
157
156
  # dummy
158
157
  end
159
158
  rescue Net::SSH::HostKeyMismatch => e
160
159
  e.remember_host!
161
- logger.info "remote host key added to local ~/.ssh/known_hosts file."
160
+ logger.info 'remote host key added to local ~/.ssh/known_hosts file.'
162
161
  end
163
162
 
164
- def post_install(options = {})
163
+ def post_install
165
164
  return unless @post_install
166
165
 
167
166
  post_install = render_post_install
@@ -174,7 +173,7 @@ module Hetzner
174
173
  logger.info output
175
174
  end
176
175
 
177
- def post_install_remote(options = {})
176
+ def post_install_remote
178
177
  remote do |ssh|
179
178
  @post_install_remote.split("\n").each do |cmd|
180
179
  cmd.chomp!
@@ -191,7 +190,7 @@ module Hetzner
191
190
  params[:hostname] = @hostname
192
191
  params[:ip] = @ip
193
192
 
194
- return eruby.result(params)
193
+ eruby.result(params)
195
194
  end
196
195
 
197
196
  def render_post_install
@@ -203,7 +202,7 @@ module Hetzner
203
202
  params[:login] = @login
204
203
  params[:password] = @password
205
204
 
206
- return eruby.result(params)
205
+ eruby.result(params)
207
206
  end
208
207
 
209
208
  def use_api(api_obj)
@@ -215,18 +214,17 @@ module Hetzner
215
214
  @logger.formatter = default_log_formatter
216
215
  end
217
216
 
218
- def remote(options = {}, &block)
219
-
220
- default = { :paranoid => false, :password => @password }
217
+ def remote(options = {})
218
+ default = { paranoid: false, password: @password }
221
219
  default.merge! options
222
220
 
223
221
  Net::SSH.start(@ip, @login, default) do |ssh|
224
- block.call ssh
222
+ yield ssh
225
223
  end
226
224
  end
227
225
 
228
- def local(&block)
229
- block.call
226
+ def local
227
+ yield
230
228
  end
231
229
 
232
230
  def reset_retries
@@ -234,14 +232,16 @@ module Hetzner
234
232
  end
235
233
 
236
234
  def rolling_sleep
237
- sleep @retries * @retries * 3 + 1 # => 1, 4, 13, 28, 49, 76, 109, 148, 193, 244, 301, 364 ... seconds
235
+ # => 1, 4, 13, 28, 49, 76, 109, 148, 193, 244, 301, 364 ... seconds
236
+ sleep @retries * @retries * 3 + 1
238
237
  end
239
238
 
240
239
  def default_log_formatter
241
- proc do |severity, datetime, progname, msg|
242
- caller[4]=~/`(.*?)'/
243
- "[#{datetime.strftime "%H:%M:%S"}][#{sprintf "%-15s", ip}][#{$1}] #{msg}\n"
244
- end
240
+ proc do |_severity, datetime, _progname, msg|
241
+ caller[4] =~ /`(.*?)'/
242
+ "[#{datetime.strftime '%H:%M:%S'}][#{format '%-15s', ip}]" \
243
+ "[#{Regexp.last_match(1)}] #{msg}\n"
244
+ end
245
245
  end
246
246
 
247
247
  class NoTemplateProvidedError < ArgumentError; end
@@ -24,4 +24,4 @@ module Hetzner
24
24
  end
25
25
  end
26
26
  end
27
- end
27
+ end
@@ -1,5 +1,6 @@
1
+ # frozen_string_literal: true
1
2
  module Hetzner
2
3
  class Bootstrap
3
- VERSION = '1.1.0'
4
+ VERSION = '1.2.0'.freeze
4
5
  end
5
6
  end
@@ -1,51 +1,42 @@
1
1
  require 'hetzner-api'
2
- require 'spec_helper'
2
+ require 'hetzner-bootstrap'
3
3
 
4
- describe "Bootstrap" do
5
- before(:all) do
6
- @api = Hetzner::API.new API_USERNAME, API_PASSWORD
7
- @bootstrap = Hetzner::Bootstrap.new :api => @api
4
+ describe 'Bootstrap' do
5
+ let(:bs) do
6
+ Hetzner::Bootstrap.new(api: Hetzner::API.new(API_USERNAME, API_PASSWORD))
8
7
  end
9
8
 
10
- context "add target" do
11
-
12
- it "should be able to add a server to operate on" do
13
- @bootstrap.add_target proper_target
14
- @bootstrap.targets.should have(1).target
15
- @bootstrap.targets.first.should be_instance_of Hetzner::Bootstrap::Target
16
- end
9
+ context 'add target' do
10
+ it 'should be able to add a server to operate on' do
11
+ bs.add_target proper_target
17
12
 
18
- it "should have the default template if none is specified" do
19
- @bootstrap.add_target proper_target
20
- @bootstrap.targets.first.template.should be_instance_of Hetzner::Bootstrap::Template
13
+ expect(bs.targets.size).to be_eql(1)
14
+ expect(bs.targets.first).to be_instance_of(Hetzner::Bootstrap::Target)
21
15
  end
22
16
 
23
- it "should raise an NoTemplateProvidedError when no template option provided" do
24
- lambda {
25
- @bootstrap.add_target improper_target_without_template
26
- }.should raise_error(Hetzner::Bootstrap::Target::NoTemplateProvidedError)
17
+ it 'should have the default template if none is specified' do
18
+ bs.add_target proper_target
19
+
20
+ expect(bs.targets.first.template).to be_instance_of Hetzner::Bootstrap::Template
27
21
  end
28
-
29
22
  end
30
23
 
31
24
  def proper_target
32
- return {
33
- :ip => "1.2.3.4",
34
- :login => "root",
35
- # :password => "halloMartin!",
36
- :rescue_os => "linux",
37
- :rescue_os_bit => "64",
38
- :template => default_template
25
+ {
26
+ ip: '1.2.3.4',
27
+ login: 'root',
28
+ # :password => "halloMartin!",
29
+ rescue_os: 'linux',
30
+ rescue_os_bit: '64',
31
+ template: default_template
39
32
  }
40
33
  end
41
34
 
42
35
  def improper_target_without_template
43
- proper_target.select { |k,v| k != :template }
36
+ proper_target.select { |k, _v| k != :template }
44
37
  end
45
38
 
46
39
  def default_template
47
- "bla"
40
+ 'string'
48
41
  end
49
42
  end
50
-
51
-
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+ # This file was generated by the `rspec --init` command. Conventionally, all
3
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
4
+ # The generated `.rspec` file contains `--require spec_helper` which will cause
5
+ # this file to always be loaded, without a need to explicitly require it in any
6
+ # files.
7
+ #
8
+ # Given that it is always loaded, you are encouraged to keep this file as
9
+ # light-weight as possible. Requiring heavyweight dependencies from this file
10
+ # will add to the boot time of your test suite on EVERY test run, even for an
11
+ # individual file that may not need all of that loaded. Instead, consider making
12
+ # a separate helper file that requires the additional dependencies and performs
13
+ # the additional setup, and require it from the spec files that actually need
14
+ # it.
15
+ #
16
+ # The `.rspec` file also contains a few flags that are not defaults but that
17
+ # users commonly want.
18
+ #
19
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
20
+ RSpec.configure do |config|
21
+ # rspec-expectations config goes here. You can use an alternate
22
+ # assertion/expectation library such as wrong or the stdlib/minitest
23
+ # assertions if you prefer.
24
+ config.expect_with :rspec do |expectations|
25
+ # This option will default to `true` in RSpec 4. It makes the `description`
26
+ # and `failure_message` of custom matchers include text for helper methods
27
+ # defined using `chain`, e.g.:
28
+ # be_bigger_than(2).and_smaller_than(4).description
29
+ # # => "be bigger than 2 and smaller than 4"
30
+ # ...rather than:
31
+ # # => "be bigger than 2"
32
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
33
+ end
34
+
35
+ # rspec-mocks config goes here. You can use an alternate test double
36
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
37
+ config.mock_with :rspec do |mocks|
38
+ # Prevents you from mocking or stubbing a method that does not exist on
39
+ # a real object. This is generally recommended, and will default to
40
+ # `true` in RSpec 4.
41
+ mocks.verify_partial_doubles = true
42
+ end
43
+
44
+ # The settings below are suggested to provide a good initial experience
45
+ # with RSpec, but feel free to customize to your heart's content.
46
+ # # These two settings work together to allow you to limit a spec run
47
+ # # to individual examples or groups you care about by tagging them with
48
+ # # `:focus` metadata. When nothing is tagged with `:focus`, all examples
49
+ # # get run.
50
+ # config.filter_run :focus
51
+ # config.run_all_when_everything_filtered = true
52
+ #
53
+ # # Allows RSpec to persist some state between runs in order to support
54
+ # # the `--only-failures` and `--next-failure` CLI options. We recommend
55
+ # # you configure your source control system to ignore this file.
56
+ # config.example_status_persistence_file_path = "spec/examples.txt"
57
+ #
58
+ # # Limits the available syntax to the non-monkey patched syntax that is
59
+ # # recommended. For more details, see:
60
+ # # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
61
+ # # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
62
+ # # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
63
+ # config.disable_monkey_patching!
64
+ #
65
+ # # This setting enables warnings. It's recommended, but in some cases may
66
+ # # be too noisy due to issues in dependencies.
67
+ # config.warnings = true
68
+ #
69
+ # # Many RSpec users commonly either run the entire suite or an individual
70
+ # # file, and it's useful to allow more verbose output when running an
71
+ # # individual spec file.
72
+ # if config.files_to_run.one?
73
+ # # Use the documentation formatter for detailed output,
74
+ # # unless a formatter has already been configured
75
+ # # (e.g. via a command-line flag).
76
+ # config.default_formatter = 'doc'
77
+ # end
78
+ #
79
+ # # Print the 10 slowest examples and example groups at the
80
+ # # end of the spec run, to help surface which specs are running
81
+ # # particularly slow.
82
+ # config.profile_examples = 10
83
+ #
84
+ # # Run specs in random order to surface order dependencies. If you find an
85
+ # # order dependency and want to debug it, you can fix the order by providing
86
+ # # the seed, which is printed after each run.
87
+ # # --seed 1234
88
+ # config.order = :random
89
+ #
90
+ # # Seed global randomization in this process using the `--seed` CLI option.
91
+ # # Setting this allows you to use `--seed` to deterministically reproduce
92
+ # # test failures related to randomization by passing the same `--seed` value
93
+ # # as the one that triggered the failure.
94
+ # Kernel.srand config.seed
95
+ end
96
+
97
+ API_USERNAME = 'api-login'.freeze
98
+ API_PASSWORD = 'password'.freeze
metadata CHANGED
@@ -1,96 +1,99 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hetzner-bootstrap
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
5
- prerelease:
4
+ version: 1.2.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Roland Moriz
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-03-26 00:00:00.000000000 Z
11
+ date: 2016-01-26 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: hetzner-api
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>='
17
+ - - ">="
20
18
  - !ruby/object:Gem::Version
21
19
  version: 1.1.0
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ! '>='
24
+ - - ">="
28
25
  - !ruby/object:Gem::Version
29
26
  version: 1.1.0
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: net-ssh
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
- - - ! '>='
31
+ - - ">="
36
32
  - !ruby/object:Gem::Version
37
33
  version: 2.6.0
38
34
  type: :runtime
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
- - - ! '>='
38
+ - - ">="
44
39
  - !ruby/object:Gem::Version
45
40
  version: 2.6.0
46
41
  - !ruby/object:Gem::Dependency
47
42
  name: erubis
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
- - - ! '>='
45
+ - - ">="
52
46
  - !ruby/object:Gem::Version
53
47
  version: 2.7.0
54
48
  type: :runtime
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
- - - ! '>='
52
+ - - ">="
60
53
  - !ruby/object:Gem::Version
61
54
  version: 2.7.0
62
55
  - !ruby/object:Gem::Dependency
63
56
  name: rspec
64
57
  requirement: !ruby/object:Gem::Requirement
65
- none: false
66
58
  requirements:
67
- - - ! '>='
59
+ - - "~>"
68
60
  - !ruby/object:Gem::Version
69
- version: 2.13.0
61
+ version: 3.4.0
70
62
  type: :development
71
63
  prerelease: false
72
64
  version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
65
  requirements:
75
- - - ! '>='
66
+ - - "~>"
76
67
  - !ruby/object:Gem::Version
77
- version: 2.13.0
68
+ version: 3.4.0
78
69
  - !ruby/object:Gem::Dependency
79
70
  name: rake
80
71
  requirement: !ruby/object:Gem::Requirement
81
- none: false
82
72
  requirements:
83
- - - ! '>='
73
+ - - ">="
84
74
  - !ruby/object:Gem::Version
85
75
  version: '0'
86
76
  type: :development
87
77
  prerelease: false
88
78
  version_requirements: !ruby/object:Gem::Requirement
89
- none: false
90
79
  requirements:
91
- - - ! '>='
80
+ - - ">="
92
81
  - !ruby/object:Gem::Version
93
82
  version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.36.0
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.36.0
94
97
  description: Easy bootstrapping of hetzner.de rootservers using hetzner-api
95
98
  email:
96
99
  - roland@moriz.de
@@ -98,7 +101,9 @@ executables: []
98
101
  extensions: []
99
102
  extra_rdoc_files: []
100
103
  files:
101
- - .gitignore
104
+ - ".gitignore"
105
+ - ".rspec"
106
+ - ".rubocop.yml"
102
107
  - Gemfile
103
108
  - LICENSE
104
109
  - README.md
@@ -110,30 +115,30 @@ files:
110
115
  - lib/hetzner/bootstrap/template.rb
111
116
  - lib/hetzner/bootstrap/version.rb
112
117
  - spec/hetzner_bootstrap_spec.rb
118
+ - spec/spec_helper.rb
113
119
  homepage: http://moriz.de/opensource/hetzner-api
114
120
  licenses: []
121
+ metadata: {}
115
122
  post_install_message:
116
123
  rdoc_options: []
117
124
  require_paths:
118
125
  - lib
119
126
  required_ruby_version: !ruby/object:Gem::Requirement
120
- none: false
121
127
  requirements:
122
- - - ! '>='
128
+ - - ">="
123
129
  - !ruby/object:Gem::Version
124
130
  version: '0'
125
131
  required_rubygems_version: !ruby/object:Gem::Requirement
126
- none: false
127
132
  requirements:
128
- - - ! '>='
133
+ - - ">="
129
134
  - !ruby/object:Gem::Version
130
135
  version: '0'
131
136
  requirements: []
132
137
  rubyforge_project:
133
- rubygems_version: 1.8.23
138
+ rubygems_version: 2.5.1
134
139
  signing_key:
135
- specification_version: 3
140
+ specification_version: 4
136
141
  summary: Easy bootstrapping of hetzner.de rootservers using hetzner-api
137
142
  test_files:
138
143
  - spec/hetzner_bootstrap_spec.rb
139
- has_rdoc:
144
+ - spec/spec_helper.rb