ridley 0.9.1 → 0.10.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/.gitignore +1 -0
  2. data/.ruby-version +1 -0
  3. data/Guardfile +1 -1
  4. data/README.md +1 -1
  5. data/bootstrappers/{omnibus.erb → unix_omnibus.erb} +20 -5
  6. data/bootstrappers/windows_omnibus.erb +133 -0
  7. data/lib/ridley.rb +4 -1
  8. data/lib/ridley/bootstrap_bindings.rb +5 -0
  9. data/lib/ridley/bootstrap_bindings/unix_template_binding.rb +112 -0
  10. data/lib/ridley/bootstrap_bindings/windows_template_binding.rb +221 -0
  11. data/lib/ridley/bootstrapper.rb +14 -22
  12. data/lib/ridley/bootstrapper/context.rb +53 -162
  13. data/lib/ridley/chef.rb +0 -1
  14. data/lib/ridley/chef/cookbook.rb +0 -9
  15. data/lib/ridley/client.rb +12 -1
  16. data/lib/ridley/errors.rb +1 -0
  17. data/lib/ridley/host_connector.rb +76 -0
  18. data/lib/ridley/{ssh → host_connector}/response.rb +1 -1
  19. data/lib/ridley/{ssh → host_connector}/response_set.rb +11 -11
  20. data/lib/ridley/host_connector/ssh.rb +58 -0
  21. data/lib/ridley/host_connector/ssh/worker.rb +99 -0
  22. data/lib/ridley/host_connector/winrm.rb +55 -0
  23. data/lib/ridley/host_connector/winrm/worker.rb +126 -0
  24. data/lib/ridley/mixin/bootstrap_binding.rb +88 -0
  25. data/lib/ridley/resource.rb +1 -1
  26. data/lib/ridley/resources/client_resource.rb +7 -0
  27. data/lib/ridley/resources/node_resource.rb +9 -4
  28. data/lib/ridley/sandbox_uploader.rb +1 -7
  29. data/lib/ridley/version.rb +1 -1
  30. data/ridley.gemspec +1 -0
  31. data/spec/unit/ridley/bootstrap_bindings/unix_template_binding_spec.rb +102 -0
  32. data/spec/unit/ridley/bootstrap_bindings/windows_template_binding_spec.rb +118 -0
  33. data/spec/unit/ridley/bootstrapper/context_spec.rb +19 -106
  34. data/spec/unit/ridley/bootstrapper_spec.rb +2 -12
  35. data/spec/unit/ridley/client_spec.rb +20 -2
  36. data/spec/unit/ridley/{ssh → host_connector}/response_set_spec.rb +17 -17
  37. data/spec/unit/ridley/host_connector/ssh/worker_spec.rb +15 -0
  38. data/spec/unit/ridley/{ssh_spec.rb → host_connector/ssh_spec.rb} +5 -5
  39. data/spec/unit/ridley/host_connector/winrm/worker_spec.rb +41 -0
  40. data/spec/unit/ridley/host_connector/winrm_spec.rb +47 -0
  41. data/spec/unit/ridley/host_connector_spec.rb +102 -0
  42. data/spec/unit/ridley/mixin/bootstrap_binding_spec.rb +56 -0
  43. data/spec/unit/ridley/sandbox_uploader_spec.rb +1 -28
  44. metadata +53 -25
  45. data/.rbenv-version +0 -1
  46. data/lib/ridley/chef/chefignore.rb +0 -76
  47. data/lib/ridley/ssh.rb +0 -56
  48. data/lib/ridley/ssh/worker.rb +0 -87
  49. data/spec/fixtures/chefignore +0 -8
  50. data/spec/unit/ridley/chef/chefignore_spec.rb +0 -40
  51. data/spec/unit/ridley/ssh/worker_spec.rb +0 -13
@@ -3,22 +3,10 @@ module Ridley
3
3
  class Bootstrapper
4
4
  autoload :Context, 'ridley/bootstrapper/context'
5
5
 
6
- class << self
7
- # @return [Pathname]
8
- def templates_path
9
- Ridley.root.join('bootstrappers')
10
- end
11
-
12
- # @return [String]
13
- def default_template
14
- templates_path.join('omnibus.erb').to_s
15
- end
16
- end
17
-
18
6
  include Celluloid
19
7
  include Celluloid::Logger
20
-
21
- # @return [Array<String>]
8
+
9
+ # @return [Array<String>]
22
10
  attr_reader :hosts
23
11
 
24
12
  # @return [Array<Bootstrapper::Context>]
@@ -33,6 +21,10 @@ module Ridley
33
21
  # * :password (String) the password for the shell user that will perform the bootstrap
34
22
  # * :keys (Array, String) an array of keys (or a single key) to authenticate the ssh user with instead of a password
35
23
  # * :timeout (Float) [5.0] timeout value for SSH bootstrap
24
+ # @option options [Hash] :winrm
25
+ # * :user (String) a user that will login to each node and perform the bootstrap command on (required)
26
+ # * :password (String) the password for the user that will perform the bootstrap
27
+ # * :port (Fixnum) the winrm port to connect on the node the bootstrap will be performed on (5985)
36
28
  # @option options [String] :validator_client
37
29
  # @option options [String] :validator_path
38
30
  # filepath to the validator used to bootstrap the node (required)
@@ -46,13 +38,13 @@ module Ridley
46
38
  # a hash of attributes to use in the first Chef run
47
39
  # @option options [Array] :run_list (Array.new)
48
40
  # an initial run list to bootstrap with
49
- # @option options [String] :chef_version (Ridley::CHEF_VERSION)
41
+ # @option options [String] :chef_version (nil)
50
42
  # version of Chef to install on the node
51
43
  # @option options [String] :environment ('_default')
52
44
  # environment to join the node to
53
45
  # @option options [Boolean] :sudo (true)
54
46
  # bootstrap with sudo (default: true)
55
- # @option options [String] :template ('omnibus')
47
+ # @option options [String] :template
56
48
  # bootstrap template to use
57
49
  def initialize(hosts, options = {})
58
50
  @hosts = Array(hosts).collect(&:to_s).uniq
@@ -64,23 +56,23 @@ module Ridley
64
56
  }.merge(@options[:ssh])
65
57
 
66
58
  @options[:sudo] = @options[:ssh][:sudo]
67
-
68
59
  @contexts = @hosts.collect do |host|
69
- Context.new(host, options)
60
+ Context.create(host, options)
70
61
  end
71
62
  end
72
63
 
73
- # @return [SSH::ResponseSet]
64
+ # @return [HostConnector::ResponseSet]
74
65
  def run
75
66
  workers = Array.new
76
67
  futures = contexts.collect do |context|
77
68
  info "Running bootstrap command on #{context.host}"
78
69
 
79
- workers << worker = SSH::Worker.new_link(self.options[:ssh].freeze)
80
- worker.future.run(context.host, context.boot_command)
70
+ workers << worker = context.host_connector::Worker.new(context.host, self.options.freeze)
71
+
72
+ worker.future.run(context.template_binding.boot_command)
81
73
  end
82
74
 
83
- SSH::ResponseSet.new.tap do |response_set|
75
+ HostConnector::ResponseSet.new.tap do |response_set|
84
76
  futures.each do |future|
85
77
  status, response = future.value
86
78
  response_set.add_response(response)
@@ -5,185 +5,76 @@ module Ridley
5
5
  # @author Jamie Winsor <reset@riotgames.com>
6
6
  class Context
7
7
  class << self
8
- def validate_options(options = {})
9
- if options[:server_url].nil?
10
- raise Errors::ArgumentError, "A server_url is required for bootstrapping"
11
- end
12
8
 
13
- if options[:validator_path].nil?
14
- raise Errors::ArgumentError, "A path to a validator is required for bootstrapping"
9
+ # @param [String] host
10
+ # @option options [Hash] :ssh
11
+ # * :user (String) a shell user that will login to each node and perform the bootstrap command on (required)
12
+ # * :password (String) the password for the shell user that will perform the bootstrap
13
+ # * :port (Fixnum) the ssh port to connect on the node the bootstrap will be performed on (22)
14
+ # * :keys (Array, String) an array of keys (or a single key) to authenticate the ssh user with instead of a password
15
+ # * :timeout (Float) [5.0] timeout value for SSH bootstrap
16
+ # @option options [Hash] :winrm
17
+ # * :user (String) a user that will login to each node and perform the bootstrap command on (required)
18
+ # * :password (String) the password for the user that will perform the bootstrap
19
+ # * :port (Fixnum) the winrm port to connect on the node the bootstrap will be performed on (5985)
20
+ # @option options [String] :validator_client
21
+ # @option options [String] :validator_path
22
+ # filepath to the validator used to bootstrap the node (required)
23
+ # @option options [String] :bootstrap_proxy (nil)
24
+ # URL to a proxy server to bootstrap through
25
+ # @option options [String] :encrypted_data_bag_secret_path (nil)
26
+ # filepath on your host machine to your organizations encrypted data bag secret
27
+ # @option options [Hash] :hints (Hash.new)
28
+ # a hash of Ohai hints to place on the bootstrapped node
29
+ # @option options [Hash] :attributes (Hash.new)
30
+ # a hash of attributes to use in the first Chef run
31
+ # @option options [Array] :run_list (Array.new)
32
+ # an initial run list to bootstrap with
33
+ # @option options [String] :chef_version (nil)
34
+ # version of Chef to install on the node
35
+ # @option options [String] :environment ('_default')
36
+ # environment to join the node to
37
+ # @option options [Boolean] :sudo (true)
38
+ # bootstrap with sudo (default: true)
39
+ # @option options [String] :template ('omnibus')
40
+ # bootstrap template to use
41
+ def create(host, options = {})
42
+ host_connector = HostConnector.best_connector_for(host, options)
43
+ template_binding = case host_connector.to_s
44
+ when Ridley::HostConnector::SSH.to_s
45
+ Ridley::UnixTemplateBinding.new(options)
46
+ when Ridley::HostConnector::WinRM.to_s
47
+ Ridley::WindowsTemplateBinding.new(options)
48
+ else
49
+ raise Ridley::Errors::HostConnectionError, "Cannot find an appropriate Template Binding for an unknown connector."
15
50
  end
16
- end
17
-
18
- # A hash of default options to be used in the Context initializer
19
- #
20
- # @return [Hash]
21
- def default_options
22
- @default_options ||= {
23
- validator_client: "chef-validator",
24
- hints: Hash.new,
25
- attributes: Hash.new,
26
- run_list: Array.new,
27
- chef_version: Ridley::CHEF_VERSION,
28
- environment: "_default",
29
- sudo: true,
30
- template: Bootstrapper.default_template
31
- }
51
+ new(host, host_connector, template_binding)
32
52
  end
33
53
  end
34
54
 
35
55
  # @return [String]
36
56
  attr_reader :host
37
- # @return [String]
38
- attr_reader :node_name
39
- # @return [String]
40
- attr_reader :server_url
41
- # @return [String]
42
- attr_reader :validator_client
43
- # @return [String]
44
- attr_reader :validator_path
45
- # @return [String]
46
- attr_reader :bootstrap_proxy
47
- # @return [Hash]
48
- attr_reader :hints
49
- # @return [String]
50
- attr_reader :chef_version
51
- # @return [String]
52
- attr_reader :environment
57
+ # @return [Ridley::HostConnector]
58
+ attr_reader :host_connector
59
+ # @return [Ridley::Binding]
60
+ attr_reader :template_binding
53
61
 
54
62
  # @param [String] host
55
63
  # name of the node as identified in Chef
56
- # @option options [String] :validator_path
57
- # filepath to the validator used to bootstrap the node (required)
58
- # @option options [String] :node_name
59
- # @option options [String] :server_url
60
- # @option options [String] :validator_client
61
- # @option options [String] :bootstrap_proxy
62
- # URL to a proxy server to bootstrap through (default: nil)
63
- # @option options [String] :encrypted_data_bag_secret_path
64
- # filepath on your host machine to your organizations encrypted data bag secret (default: nil)
65
- # @option options [Hash] :hints
66
- # a hash of Ohai hints to place on the bootstrapped node (default: Hash.new)
67
- # @option options [Hash] :attributes
68
- # a hash of attributes to use in the first Chef run (default: Hash.new)
69
- # @option options [Array] :run_list
70
- # an initial run list to bootstrap with (default: Array.new)
71
- # @option options [String] :chef_version
72
- # version of Chef to install on the node (default: {Ridley::CHEF_VERSION})
73
- # @option options [String] :environment
74
- # environment to join the node to (default: '_default')
75
- # @option options [Boolean] :sudo
76
- # bootstrap with sudo (default: true)
77
- # @option options [String] :template
78
- # bootstrap template to use (default: omnibus)
79
- def initialize(host, options = {})
80
- options = self.class.default_options.merge(options)
81
- self.class.validate_options(options)
82
-
64
+ # @param [Ridley::HostConnector] host_connector
65
+ # either the SSH or WinRM Connector class
66
+ # @param [Ridley::Binding] template_binding
67
+ # an instance of either the UnixTemplateBinding or WindowsTemplateBinding class
68
+ def initialize(host, host_connector, template_binding)
83
69
  @host = host
84
- @server_url = options[:server_url]
85
- @validator_path = options[:validator_path]
86
- @node_name = options[:node_name]
87
- @validator_client = options[:validator_client]
88
- @bootstrap_proxy = options[:bootstrap_proxy]
89
- @encrypted_data_bag_secret_path = options[:encrypted_data_bag_secret_path]
90
- @hints = options[:hints]
91
- @attributes = options[:attributes]
92
- @run_list = options[:run_list]
93
- @chef_version = options[:chef_version]
94
- @environment = options[:environment]
95
- @sudo = options[:sudo]
96
- @template_file = options[:template]
97
- end
98
-
99
- # @return [String]
100
- def boot_command
101
- cmd = template.evaluate(self)
102
-
103
- if sudo
104
- cmd = "sudo #{cmd}"
105
- end
106
-
107
- cmd
70
+ @host_connector = host_connector
71
+ @template_binding = template_binding
108
72
  end
109
73
 
110
74
  # @return [String]
111
75
  def clean_command
112
76
  "rm /etc/chef/first-boot.json; rm /etc/chef/validation.pem"
113
77
  end
114
-
115
- # @return [String]
116
- def chef_run
117
- "chef-client -j /etc/chef/first-boot.json -E #{environment}"
118
- end
119
-
120
- # @return [String]
121
- def chef_config
122
- body = <<-CONFIG
123
- log_level :info
124
- log_location STDOUT
125
- chef_server_url "#{server_url}"
126
- validation_client_name "#{validator_client}"
127
- CONFIG
128
-
129
- if node_name.present?
130
- body << %Q{node_name "#{node_name}"\n}
131
- else
132
- body << "# Using default node name (fqdn)\n"
133
- end
134
-
135
- if bootstrap_proxy.present?
136
- body << %Q{http_proxy "#{bootstrap_proxy}"\n}
137
- body << %Q{https_proxy "#{bootstrap_proxy}"\n}
138
- end
139
-
140
- if encrypted_data_bag_secret.present?
141
- body << %Q{encrypted_data_bag_secret "/etc/chef/encrypted_data_bag_secret"\n}
142
- end
143
-
144
- body
145
- end
146
-
147
- # @return [String]
148
- def first_boot
149
- MultiJson.encode attributes.merge(run_list: run_list)
150
- end
151
-
152
- # The validation key to create a new client for the node
153
- #
154
- # @raise [Ridley::Errors::ValidatorNotFound]
155
- #
156
- # @return [String]
157
- def validation_key
158
- IO.read(File.expand_path(validator_path)).chomp
159
- rescue Errno::ENOENT
160
- raise Errors::ValidatorNotFound, "Error bootstrapping: Validator not found at '#{validator_path}'"
161
- end
162
-
163
- # @raise [Ridley::Errors::EncryptedDataBagSecretNotFound]
164
- #
165
- # @return [String, nil]
166
- def encrypted_data_bag_secret
167
- return nil if encrypted_data_bag_secret_path.nil?
168
-
169
- IO.read(encrypted_data_bag_secret_path).chomp
170
- rescue Errno::ENOENT => encrypted_data_bag_secret
171
- raise Errors::EncryptedDataBagSecretNotFound, "Error bootstrapping: Encrypted data bag secret provided but not found at '#{encrypted_data_bag_secret_path}'"
172
- end
173
-
174
- private
175
-
176
- attr_reader :sudo
177
- attr_reader :template_file
178
- attr_reader :encrypted_data_bag_secret_path
179
- attr_reader :validator_path
180
- attr_reader :run_list
181
- attr_reader :attributes
182
-
183
- # @return [Erubis::Eruby]
184
- def template
185
- Erubis::Eruby.new(IO.read(template_file).chomp)
186
- end
187
78
  end
188
79
  end
189
80
  end
data/lib/ridley/chef.rb CHANGED
@@ -5,7 +5,6 @@ module Ridley
5
5
  # site, and Chef Cookbooks
6
6
  module Chef
7
7
  autoload :Cookbook, 'ridley/chef/cookbook'
8
- autoload :Chefignore, 'ridley/chef/chefignore'
9
8
  autoload :Digester, 'ridley/chef/digester'
10
9
  end
11
10
  end
@@ -96,7 +96,6 @@ module Ridley::Chef
96
96
  root_files: Array.new
97
97
  )
98
98
  @frozen = false
99
- @chefignore = Ridley::Chef::Chefignore.new(@path)
100
99
 
101
100
  load_files
102
101
  end
@@ -208,12 +207,7 @@ module Ridley::Chef
208
207
 
209
208
  private
210
209
 
211
- # @return [Array]
212
210
  attr_reader :files
213
- # @return [Ridley::Chef::Chefignore]
214
- attr_reader :chefignore
215
-
216
- def_delegator :chefignore, :ignored?
217
211
 
218
212
  def load_files
219
213
  load_shallow(:recipes, 'recipes', '*.rb')
@@ -231,7 +225,6 @@ module Ridley::Chef
231
225
  [].tap do |files|
232
226
  Dir.glob(path.join('*'), File::FNM_DOTMATCH).each do |file|
233
227
  next if File.directory?(file)
234
- next if ignored?(file)
235
228
  @files << file
236
229
  @manifest[:root_files] << file_metadata(:root_files, file)
237
230
  end
@@ -243,7 +236,6 @@ module Ridley::Chef
243
236
  file_spec = path.join(category_dir, '**', glob)
244
237
  Dir.glob(file_spec, File::FNM_DOTMATCH).each do |file|
245
238
  next if File.directory?(file)
246
- next if ignored?(file)
247
239
  @files << file
248
240
  @manifest[category] << file_metadata(category, file)
249
241
  end
@@ -253,7 +245,6 @@ module Ridley::Chef
253
245
  def load_shallow(category, *path_glob)
254
246
  [].tap do |files|
255
247
  Dir[path.join(*path_glob)].each do |file|
256
- next if ignored?(file)
257
248
  @files << file
258
249
  @manifest[category] << file_metadata(category, file)
259
250
  end
data/lib/ridley/client.rb CHANGED
@@ -77,6 +77,8 @@ module Ridley
77
77
  attr_accessor :validator_path
78
78
  attr_accessor :encrypted_data_bag_secret_path
79
79
  attr_accessor :ssh
80
+ attr_accessor :winrm
81
+ attr_accessor :chef_version
80
82
 
81
83
  # @option options [String] :server_url
82
84
  # URL to the Chef API
@@ -93,6 +95,12 @@ module Ridley
93
95
  # * :keys (Array, String) an array of keys (or a single key) to authenticate the ssh user with instead of a password
94
96
  # * :timeout (Float) [5.0] timeout value for SSH bootstrap
95
97
  # * :sudo (Boolean) [true] bootstrap with sudo
98
+ # @option options [Hash] :winrm (Hash.new)
99
+ # * :user (String) a user that will login to each node and perform the bootstrap command on (required)
100
+ # * :password (String) the password for the user that will perform the bootstrap
101
+ # * :port (Fixnum) the winrm port to connect on the node the bootstrap will be performed on (5985)
102
+ # @option options [String] :chef_version
103
+ # the version of Chef to use when bootstrapping
96
104
  # @option options [Hash] :params
97
105
  # URI query unencoded key/value pairs
98
106
  # @option options [Hash] :headers
@@ -111,11 +119,14 @@ module Ridley
111
119
  super()
112
120
 
113
121
  @options = options.reverse_merge(
114
- ssh: Hash.new
122
+ ssh: Hash.new,
123
+ winrm: Hash.new
115
124
  ).deep_symbolize_keys
116
125
  self.class.validate_options(@options)
117
126
 
118
127
  @ssh = @options[:ssh]
128
+ @winrm = @options[:winrm]
129
+ @chef_version = @options[:chef_version]
119
130
  @validator_client = @options[:validator_client]
120
131
 
121
132
  @options[:client_key] = File.expand_path(@options[:client_key])
data/lib/ridley/errors.rb CHANGED
@@ -38,6 +38,7 @@ module Ridley
38
38
  class BootstrapError < RidleyError; end
39
39
  class ClientKeyFileNotFound < BootstrapError; end
40
40
  class EncryptedDataBagSecretNotFound < BootstrapError; end
41
+ class HostConnectionError < BootstrapError; end
41
42
 
42
43
  # Exception thrown when the maximum amount of requests is exceeded.
43
44
  class RedirectLimitReached < RidleyError
@@ -0,0 +1,76 @@
1
+ require 'socket'
2
+ require 'timeout'
3
+
4
+ module Ridley
5
+ # @author Kyle Allan <kallan@riotgames.com>
6
+ module HostConnector
7
+ autoload :Response, 'ridley/host_connector/response'
8
+ autoload :ResponseSet, 'ridley/host_connector/response_set'
9
+ autoload :SSH, 'ridley/host_connector/ssh'
10
+ autoload :WinRM, 'ridley/host_connector/winrm'
11
+
12
+ DEFAULT_SSH_PORT = 22.freeze
13
+ DEFAULT_WINRM_PORT = 5985.freeze
14
+
15
+ class << self
16
+ # Finds and returns the best HostConnector for a given host
17
+ #
18
+ # @param host [String]
19
+ # the host to attempt to connect to
20
+ # @option options [Hash] :ssh
21
+ # * :port (Fixnum) the ssh port to connect on the node the bootstrap will be performed on (22)
22
+ # @option options [Hash] :winrm
23
+ # * :port (Fixnum) the winrm port to connect on the node the bootstrap will be performed on (5985)
24
+ #
25
+ # @return [Ridley::HostConnector] a class under Ridley::HostConnector
26
+ def best_connector_for(host, options = {})
27
+ ssh_port, winrm_port = parse_port_options(options)
28
+ if connector_port_open?(host, ssh_port)
29
+ Ridley::HostConnector::SSH
30
+ elsif connector_port_open?(host, winrm_port)
31
+ Ridley::HostConnector::WinRM
32
+ else
33
+ raise Ridley::Errors::HostConnectionError, "No available connection method available on #{host}."
34
+ end
35
+ end
36
+
37
+ # Checks to see if the given port is open for TCP connections
38
+ # on the given host.
39
+ #
40
+ # @param host [String]
41
+ # the host to attempt to connect to
42
+ # @param port [Fixnum]
43
+ # the port to attempt to connect on
44
+ #
45
+ # @return [Boolean]
46
+ def connector_port_open?(host, port)
47
+ Timeout::timeout(1) do
48
+ begin
49
+ socket = TCPSocket.new(host, port)
50
+ socket.close
51
+ true
52
+ rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
53
+ false
54
+ end
55
+ end
56
+ rescue Timeout::Error
57
+ false
58
+ end
59
+
60
+ # Parses the options Hash and returns an array of
61
+ # [SSH_PORT, WINRM_PORT] used to attempt to connect to.
62
+ #
63
+ # @option options [Hash] :ssh
64
+ # * :port (Fixnum) the ssh port to connect on the node the bootstrap will be performed on (22)
65
+ # @option options [Hash] :winrm
66
+ # * :port (Fixnum) the winrm port to connect on the node the bootstrap will be performed on (5985)
67
+ #
68
+ # @return [Array]
69
+ def parse_port_options(options)
70
+ ssh_port = options[:ssh][:port] if options[:ssh]
71
+ winrm_port = options[:winrm][:port] if options[:winrm]
72
+ [ssh_port || DEFAULT_SSH_PORT, winrm_port || DEFAULT_WINRM_PORT]
73
+ end
74
+ end
75
+ end
76
+ end