hetzner-bootstrap-coreos 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d07dc6e9373f36a16bc6da8782dee0d1c9b958d2
4
+ data.tar.gz: 0fc042ce7cfc437391037d4c6bc3e1b66ffb587e
5
+ SHA512:
6
+ metadata.gz: e4a4e45ee787118c7094771f40c7a9bc4b5897d7e621161b6891a022f817be95b9ac99e20a34c659a66ce9e4426f08c4cac55fb90d8a1804c60448f28a5f843a
7
+ data.tar.gz: 812962458371ed6c2d600120e8e38303a3b7379de10a5d6244773fd5c03cf9b1db9b6ae36af5524a79fb722180f8c8b41bd02812d22c21bd401a4036e8c5dca5
data/.gitignore ADDED
@@ -0,0 +1,7 @@
1
+ .idea/*
2
+ bin/*
3
+ *.gem
4
+ .bundle
5
+ Gemfile.lock
6
+ pkg/*
7
+ .rbenv-vars
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in hetzner-bootstrap-coreos.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Moriz GmbH, Roland Moriz, http://moriz.de/
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,93 @@
1
+ # hetzner-bootstrap-coreos
2
+
3
+ This gem allows you to bootstrap CoreOS on a Hetzner root server.
4
+
5
+ [![Gem Version](https://badge.fury.io/rb/hetzner-bootstrap-coreos.png)](http://badge.fury.io/rb/hetzner-bootstrap-coreos)
6
+
7
+ ## What it does:
8
+ /3.
9
+ In our case we are running a large [CoreOS](https://coreos.com) cluster and which uses bare metal servers hosted at Hetzner. This Ruby gem helps to fully automate the provisioning of CoreOS on a Hetzner root server.
10
+
11
+ Warning: All existing data on the system will be lost!
12
+
13
+ ## Requirements:
14
+
15
+ First of all retrieve your API login credentials from the Hetzner admin interface at [https://robot.your-server.de](https://robot.your-server.de). Additionally you need the IP address of the shipped system.
16
+
17
+ ## Implemented steps:
18
+
19
+ 1. Enable Rescue Mode (using Hetzner's webservice)
20
+ 2. Resetting the System to boot into rescue mode (using Hetzner's webservice)
21
+ 3. Log into the rescue system, write your cloud config file and launch the installation
22
+ 4. Reboot
23
+ 5. Verify installation (very basic check but can be overwritten)
24
+ 6. Copy your local ssh public-key into root's .authorized_keys
25
+ 7. Adds the generated server key into your .know_hosts file
26
+ 8. Execute post_install hooks (optional)
27
+
28
+ ## Example:
29
+
30
+ **see example.rb file for usage!**
31
+
32
+ Warning: All existing data on the system will be lost!
33
+
34
+ ```ruby
35
+
36
+ #!/usr/bin/env ruby
37
+ require "rubygems"
38
+ require "hetzner-bootstrap-coreos"
39
+
40
+ # Retrieve your API login credentials from the Hetzner admin interface
41
+ # at https://robot.your-server.de and assign the appropriate environment
42
+ # variables ENV['ROBOT_USER'] and ENV['ROBOT_PASSWORD']
43
+
44
+ bs = Hetzner::Bootstrap::CoreOS.new(
45
+ :api => Hetzner::API.new(ENV['ROBOT_USER'], ENV['ROBOT_PASSWORD'])
46
+ )
47
+
48
+ # Main configuration (cloud-config)
49
+ cloud_config = <<EOT
50
+ hostname: <%= hostname %>
51
+ ssh_authorized_keys:
52
+ - <%= public_keys %>
53
+ EOT
54
+
55
+ # The post_install hook is the right place to launch further tasks (e.g.
56
+ # software installation, system provisioning etc.)
57
+ post_install = <<EOT
58
+ # TODO
59
+ EOT
60
+
61
+ bs << { :ip => "1.2.3.4",
62
+ :cloud_config => cloud_config,
63
+ :hostname => 'artemis.massive-insights.com',
64
+ :public_keys => "~/.ssh/id_dsa.pub",
65
+ :post_install => post_install
66
+ }
67
+
68
+ bs.bootstrap!
69
+
70
+ ```
71
+
72
+ Installation:
73
+ -------------
74
+
75
+ **gem install hetzner-bootstrap-coreos**
76
+
77
+ Warnings:
78
+ ---------
79
+
80
+ * All existing data on the system will be wiped on bootstrap!
81
+ * This is not an official Hetzner AG project.
82
+ * The gem and the author are not related to Hetzner AG!
83
+
84
+ **Use at your very own risk. Satisfaction is NOT guaranteed.**
85
+
86
+ ## Thank you greeting
87
+
88
+ This Ruby gem is inspired by the [hetzner-bootstrap](https://github.com/rmoriz/hetzner-bootstrap) gem and requires the underlying wrapper for the Hetzner server management API [hetzner-api](https://github.com/rmoriz/hetzner-api). Thus I want to thank [Roland Moriz](https://roland.io/developer) a lot for his great work!
89
+
90
+ Copyright
91
+ ---------
92
+
93
+ Copyright © 2014 Christoph Pilka ([Asconix Systems AS](https://www.asconix.com)
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
data/example.rb ADDED
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env ruby
2
+ require "rubygems"
3
+ require "hetzner-bootstrap-coreos"
4
+
5
+ # Retrieve your API login credentials from the Hetzner admin interface
6
+ # at https://robot.your-server.de and assign the appropriate environment
7
+ # variables ENV['ROBOT_USER'] and ENV['ROBOT_PASSWORD']
8
+
9
+ bs = Hetzner::Bootstrap::CoreOS.new(
10
+ :api => Hetzner::API.new(ENV['ROBOT_USER'], ENV['ROBOT_PASSWORD'])
11
+ )
12
+
13
+ # Main configuration (cloud-config)
14
+ cloud_config = <<EOT
15
+ hostname: <%= hostname %>
16
+ ssh_authorized_keys:
17
+ - <%= public_keys %>
18
+ EOT
19
+
20
+ # The post_install hook is the right place to launch further tasks (e.g.
21
+ # software installation, system provisioning etc.)
22
+ post_install = <<EOT
23
+ # TODO
24
+ EOT
25
+
26
+ bs << { :ip => "1.2.3.4",
27
+ :cloud_config => cloud_config,
28
+ :hostname => 'artemis.massive-insights.com',
29
+ :public_keys => "~/.ssh/id_dsa.pub",
30
+ :post_install => post_install
31
+ }
32
+
33
+ bs.bootstrap!
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "hetzner/bootstrap/coreos/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "hetzner-bootstrap-coreos"
7
+ s.version = Hetzner::Bootstrap::CoreOS::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Christoph Pilka"]
10
+ s.email = ["c.pilka@asconix.com"]
11
+ s.homepage = "http://www.asconix.com"
12
+ s.summary = %q{Bootstrapping of the root servers at Hetzner with CoreOS}
13
+ s.description = %q{Bootstrapping of the root servers at Hetzner with CoreOS}
14
+
15
+ s.add_dependency 'hetzner-api', '>= 1.1.0'
16
+ s.add_dependency 'net-ssh', '>= 2.6.0'
17
+ s.add_dependency 'erubis', '>= 2.7.0'
18
+
19
+ s.add_development_dependency "rspec", ">= 2.13.0"
20
+ s.add_development_dependency "rake"
21
+
22
+ s.files = `git ls-files`.split("\n")
23
+ 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"]
26
+ end
@@ -0,0 +1,21 @@
1
+ module Hetzner
2
+ class Bootstrap
3
+ class CoreOS
4
+ class CloudConfig
5
+ attr_accessor :raw_cloud_config
6
+
7
+ def initialize(param)
8
+ if param.is_a? Hetzner::Bootstrap::CoreOS::CloudConfig
9
+ return param
10
+ elsif param.is_a? String
11
+ @raw_cloud_config = param
12
+ end
13
+ end
14
+
15
+ def to_s
16
+ @raw_clooud_config
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,241 @@
1
+ require 'erubis'
2
+ require 'net/ssh'
3
+ require 'socket'
4
+ require 'timeout'
5
+
6
+ module Hetzner
7
+ class Bootstrap
8
+ class CoreOS
9
+ class Target
10
+ attr_accessor :ip
11
+ attr_accessor :login
12
+ attr_accessor :password
13
+ attr_accessor :cloud_config
14
+ attr_accessor :rescue_os
15
+ attr_accessor :rescue_os_bit
16
+ attr_accessor :actions
17
+ attr_accessor :hostname
18
+ attr_accessor :post_install
19
+ attr_accessor :post_install_remote
20
+ attr_accessor :public_keys
21
+ attr_accessor :bootstrap_cmd
22
+ attr_accessor :logger
23
+
24
+ def initialize(options = {})
25
+ @rescue_os = 'linux'
26
+ @rescue_os_bit = '64'
27
+ @retries = 0
28
+ @bootstrap_cmd = 'export TERM=xterm; /tmp/coreos-install -d /dev/sda -C stable -c /tmp/cloud-config.yaml'
29
+ @login = 'root'
30
+
31
+ if cc = options.delete(:cloud_config)
32
+ @cloud_config = CloudConfig.new cc
33
+ else
34
+ raise NoCloudConfigProvidedError.new 'No cloud config file provided.'
35
+ end
36
+
37
+ options.each_pair do |k,v|
38
+ self.send("#{k}=", v)
39
+ end
40
+ end
41
+
42
+ def enable_rescue_mode(options = {})
43
+ result = @api.enable_rescue! @ip, @rescue_os, @rescue_os_bit
44
+
45
+ if result.success? && result['rescue']
46
+ @password = result['rescue']['password']
47
+ reset_retries
48
+ logger.info "IP: #{ip} => password: #{@password}"
49
+ elsif @retries > 3
50
+ logger.error "Rescue system could not be activated"
51
+ raise CantActivateRescueSystemError, result
52
+ else
53
+ @retries += 1
54
+
55
+ logger.warn "Problem while trying to activate rescue system (retries: #{@retries})"
56
+ @api.disable_rescue! @ip
57
+
58
+ rolling_sleep
59
+ enable_rescue_mode options
60
+ end
61
+ end
62
+
63
+ def reset(options = {})
64
+ result = @api.reset! @ip, :hw
65
+
66
+ if result.success?
67
+ reset_retries
68
+ elsif @retries > 3
69
+ logger.error "Resetting through web service failed."
70
+ raise CantResetSystemError, result
71
+ else
72
+ @retries += 1
73
+ logger.warn "Problem while trying to reset/reboot system (retries: #{@retries})"
74
+ rolling_sleep
75
+ reset options
76
+ end
77
+ end
78
+
79
+ def port_open? ip, port
80
+ ssh_port_probe = TCPSocket.new ip, port
81
+ IO.select([ssh_port_probe], nil, nil, 2)
82
+ ssh_port_probe.close
83
+ true
84
+ end
85
+
86
+ def wait_for_ssh_down(options = {})
87
+ loop do
88
+ sleep 2
89
+ Timeout::timeout(4) do
90
+ if port_open? @ip, 22
91
+ logger.debug "SSH UP"
92
+ else
93
+ raise Errno::ECONNREFUSED
94
+ end
95
+ end
96
+ end
97
+ rescue Timeout::Error, Errno::ECONNREFUSED
98
+ logger.debug "SSH down"
99
+ end
100
+
101
+ def wait_for_ssh_up(options = {})
102
+ loop do
103
+ Timeout::timeout(4) do
104
+ if port_open? @ip, 22
105
+ logger.debug "SSH up"
106
+ return true
107
+ else
108
+ raise Errno::ECONNREFUSED
109
+ end
110
+ end
111
+ end
112
+ rescue Errno::ECONNREFUSED, Timeout::Error
113
+ logger.debug "SSH down"
114
+ sleep 2
115
+ retry
116
+ end
117
+
118
+ def installimage(options = {})
119
+ cloud_config = render_cloud_config
120
+
121
+ remote do |ssh|
122
+ ssh.exec! "echo \"#{cloud_config}\" > /tmp/cloud-config.yaml"
123
+ logger.info "Remote executing: #{@bootstrap_cmd}"
124
+ output = ssh.exec!(@bootstrap_cmd)
125
+ logger.info output
126
+ end
127
+ end
128
+
129
+ def reboot(options = {})
130
+ remote do |ssh|
131
+ ssh.exec!("reboot")
132
+ end
133
+ end
134
+
135
+ def verify_installation(options = {})
136
+ remote do |ssh|
137
+ working_hostname = ssh.exec!("cat /etc/hostname")
138
+ unless @hostname == working_hostname.chomp
139
+ raise InstallationError, "Hostnames do not match: assumed #{@hostname} but received #{working_hostname}"
140
+ end
141
+ end
142
+ end
143
+
144
+ def update_local_known_hosts(options = {})
145
+ remote(:paranoid => true) do |ssh|
146
+ # dummy
147
+ end
148
+ rescue Net::SSH::HostKeyMismatch => e
149
+ e.remember_host!
150
+ logger.info "Remote host key added to local ~/.ssh/known_hosts file."
151
+ end
152
+
153
+ def post_install(options = {})
154
+ return unless @post_install
155
+
156
+ post_install = render_post_install
157
+ logger.info "Executing post_install:\n #{post_install}"
158
+
159
+ output = local do
160
+ `#{post_install}`
161
+ end
162
+
163
+ logger.info output
164
+ end
165
+
166
+ def post_install_remote(options = {})
167
+ remote do |ssh|
168
+ @post_install_remote.split("\n").each do |cmd|
169
+ cmd.chomp!
170
+ logger.info "executing #{cmd}"
171
+ ssh.exec!(cmd)
172
+ end
173
+ end
174
+ end
175
+
176
+ def render_cloud_config
177
+ eruby = Erubis::Eruby.new @cloud_config.to_s
178
+
179
+ params = {}
180
+ params[:hostname] = @hostname
181
+ params[:ip] = @ip
182
+
183
+ return eruby.result(params)
184
+ end
185
+
186
+ def render_post_install
187
+ eruby = Erubis::Eruby.new @post_install.to_s
188
+
189
+ params = {}
190
+ params[:hostname] = @hostname
191
+ params[:ip] = @ip
192
+ params[:login] = @login
193
+ params[:password] = @password
194
+
195
+ return eruby.result(params)
196
+ end
197
+
198
+ def use_api(api_obj)
199
+ @api = api_obj
200
+ end
201
+
202
+ def use_logger(logger_obj)
203
+ @logger = logger_obj
204
+ @logger.formatter = default_log_formatter
205
+ end
206
+
207
+ def remote(options = {}, &block)
208
+ default = { :paranoid => false, :password => @password }
209
+ default.merge! options
210
+ Net::SSH.start(@ip, @login, default) do |ssh|
211
+ block.call ssh
212
+ end
213
+ end
214
+
215
+ def local(&block)
216
+ block.call
217
+ end
218
+
219
+ def reset_retries
220
+ @retries = 0
221
+ end
222
+
223
+ def rolling_sleep
224
+ sleep @retries * @retries * 3 + 1 # => 1, 4, 13, 28, 49, 76, 109, 148, 193, 244, 301, 364 ... seconds
225
+ end
226
+
227
+ def default_log_formatter
228
+ proc do |severity, datetime, progname, msg|
229
+ caller[4]=~/`(.*?)'/
230
+ "[#{datetime.strftime "%H:%M:%S"}][#{sprintf "%-15s", ip}][#{$1}] #{msg}\n"
231
+ end
232
+ end
233
+
234
+ class NoCloudConfigProvidedError < ArgumentError; end
235
+ class CantActivateRescueSystemError < StandardError; end
236
+ class CantResetSystemError < StandardError; end
237
+ class InstallationError < StandardError; end
238
+ end
239
+ end
240
+ end
241
+ end
@@ -0,0 +1,7 @@
1
+ module Hetzner
2
+ class Bootstrap
3
+ class CoreOS
4
+ VERSION = '0.0.1'
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,71 @@
1
+ require 'benchmark'
2
+ require 'logger'
3
+
4
+ require 'hetzner-api'
5
+ require 'hetzner/bootstrap/coreos/version'
6
+ require 'hetzner/bootstrap/coreos/target'
7
+ require 'hetzner/bootstrap/coreos/cloud_config'
8
+
9
+ module Hetzner
10
+ class Bootstrap
11
+ class CoreOS
12
+ attr_accessor :targets
13
+ attr_accessor :api
14
+ attr_accessor :actions
15
+ attr_accessor :logger
16
+
17
+ def initialize(options = {})
18
+ @targets = []
19
+ @actions = %w(enable_rescue_mode
20
+ reset
21
+ wait_for_ssh_down
22
+ wait_for_ssh_up
23
+ installimage
24
+ reboot
25
+ wait_for_ssh_down
26
+ wait_for_ssh_up
27
+ verify_installation
28
+ update_local_known_hosts
29
+ post_install
30
+ post_install_remote)
31
+ @api = options[:api]
32
+ @logger = options[:logger] || Logger.new(STDOUT)
33
+ end
34
+
35
+ def add_target(param)
36
+ if param.is_a? Hetzner::Bootstrap::CoreOS::Target
37
+ @targets << param
38
+ else
39
+ @targets << (Hetzner::Bootstrap::CoreOS::Target.new param)
40
+ end
41
+ end
42
+
43
+ def <<(param)
44
+ add_target param
45
+ end
46
+
47
+ def bootstrap!(options = {})
48
+ @targets.each do |target|
49
+ target.use_api @api
50
+ target.use_logger @logger
51
+ bootstrap_one_target! target
52
+ end
53
+ end
54
+
55
+ def bootstrap_one_target!(target)
56
+ actions = (target.actions || @actions)
57
+ actions.each_with_index do |action, index|
58
+ loghack = "\b" * 24 # remove: "[bootstrap_one_target!] ".length
59
+ target.logger.info "#{loghack}[#{action}] #{sprintf "%-20s", "START"}"
60
+ d = Benchmark.realtime do
61
+ target.send action
62
+ end
63
+ target.logger.info "#{loghack}[#{action}] FINISHED in #{sprintf "%.5f",d} seconds"
64
+ end
65
+ rescue => e
66
+ puts "Something bad happened unexpectedly: #{e.class} => #{e.message}"
67
+ end
68
+ end
69
+ end
70
+ end
71
+
@@ -0,0 +1,49 @@
1
+ require 'hetzner-api'
2
+ require 'spec_helper'
3
+
4
+ describe "Bootstrap" do
5
+ before(:all) do
6
+ @api = Hetzner::API.new API_USERNAME, API_PASSWORD
7
+ @bootstrap = Hetzner::Bootstrap::CoreOS.new :api => @api
8
+ end
9
+
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::CoreOS::Target
16
+ end
17
+
18
+ it "should have the default cloud config file if none is specified" do
19
+ @bootstrap.add_target proper_target
20
+ @bootstrap.targets.first.cloud_config.should be_instance_of Hetzner::Bootstrap::CoreOS::CloudConfig
21
+ end
22
+
23
+ it "should raise an NoCloudConfigProvidedError when no cloud config option provided" do
24
+ lambda {
25
+ @bootstrap.add_target improper_target_without_cloud_condfig
26
+ }.should raise_error(Hetzner::Bootstrap::CoreOS::Target::NoCloudConfigProvidedError)
27
+ end
28
+
29
+ end
30
+
31
+ def proper_target
32
+ return {
33
+ :ip => "1.2.3.4",
34
+ :login => "root",
35
+ :password => "verysecret",
36
+ :rescue_os => "linux",
37
+ :rescue_os_bit => "64",
38
+ :cloud_config => default_cloud_config
39
+ }
40
+ end
41
+
42
+ def improper_target_without_cloud_config
43
+ proper_target.select { |k,v| k != :cloud_config }
44
+ end
45
+
46
+ def default_cloud_config
47
+ "foobar"
48
+ end
49
+ end
metadata ADDED
@@ -0,0 +1,126 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hetzner-bootstrap-coreos
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Christoph Pilka
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-10-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: hetzner-api
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 1.1.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 1.1.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: net-ssh
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 2.6.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 2.6.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: erubis
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 2.7.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 2.7.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 2.13.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 2.13.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Bootstrapping of the root servers at Hetzner with CoreOS
84
+ email:
85
+ - c.pilka@asconix.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - Gemfile
92
+ - LICENSE
93
+ - README.md
94
+ - Rakefile
95
+ - example.rb
96
+ - hetzner-bootstrap-coreos.gemspec
97
+ - lib/hetzner-bootstrap-coreos.rb
98
+ - lib/hetzner/bootstrap/coreos/cloud_config.rb
99
+ - lib/hetzner/bootstrap/coreos/target.rb
100
+ - lib/hetzner/bootstrap/coreos/version.rb
101
+ - spec/hetzner_bootstrap_coreos_spec.rb
102
+ homepage: http://www.asconix.com
103
+ licenses: []
104
+ metadata: {}
105
+ post_install_message:
106
+ rdoc_options: []
107
+ require_paths:
108
+ - lib
109
+ required_ruby_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ required_rubygems_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ requirements: []
120
+ rubyforge_project:
121
+ rubygems_version: 2.2.2
122
+ signing_key:
123
+ specification_version: 4
124
+ summary: Bootstrapping of the root servers at Hetzner with CoreOS
125
+ test_files:
126
+ - spec/hetzner_bootstrap_coreos_spec.rb