ridley 0.9.1 → 0.10.0.rc1

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.
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