ridley 0.12.4 → 1.0.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.
- data/Gemfile +1 -1
- data/lib/ridley.rb +3 -3
- data/lib/ridley/bootstrap_context.rb +100 -0
- data/lib/ridley/bootstrap_context/unix.rb +74 -0
- data/lib/ridley/bootstrap_context/windows.rb +120 -0
- data/lib/ridley/chef_objects/node_object.rb +8 -5
- data/lib/ridley/host_commander.rb +207 -0
- data/lib/ridley/host_connector.rb +49 -87
- data/lib/ridley/host_connector/ssh.rb +153 -39
- data/lib/ridley/host_connector/winrm.rb +164 -39
- data/lib/ridley/resources/node_resource.rb +52 -56
- data/lib/ridley/version.rb +1 -1
- data/ridley.gemspec +0 -2
- data/spec/spec_helper.rb +4 -4
- data/spec/support/chef_server.rb +9 -3
- data/spec/unit/ridley/{bootstrap_bindings/unix_template_binding_spec.rb → bootstrap_context/unix_spec.rb} +2 -2
- data/spec/unit/ridley/{bootstrap_bindings/windows_template_binding_spec.rb → bootstrap_context/windows_spec.rb} +2 -2
- data/spec/unit/ridley/{mixin/bootstrap_binding_spec.rb → bootstrap_context_spec.rb} +2 -6
- data/spec/unit/ridley/host_commander_spec.rb +208 -0
- data/spec/unit/ridley/host_connector/ssh_spec.rb +37 -31
- data/spec/unit/ridley/host_connector/winrm_spec.rb +124 -31
- data/spec/unit/ridley/host_connector_spec.rb +23 -147
- data/spec/unit/ridley/resources/node_resource_spec.rb +55 -115
- metadata +17 -66
- data/lib/ridley/bootstrap_bindings.rb +0 -3
- data/lib/ridley/bootstrap_bindings/unix_template_binding.rb +0 -108
- data/lib/ridley/bootstrap_bindings/windows_template_binding.rb +0 -163
- data/lib/ridley/bootstrapper.rb +0 -89
- data/lib/ridley/bootstrapper/context.rb +0 -81
- data/lib/ridley/host_connector/response_set.rb +0 -98
- data/lib/ridley/host_connector/ssh/worker.rb +0 -135
- data/lib/ridley/host_connector/winrm/worker.rb +0 -159
- data/lib/ridley/log.rb +0 -10
- data/lib/ridley/mixin/bootstrap_binding.rb +0 -77
- data/spec/unit/ridley/bootstrapper/context_spec.rb +0 -45
- data/spec/unit/ridley/bootstrapper_spec.rb +0 -96
- data/spec/unit/ridley/host_connector/response_set_spec.rb +0 -112
- data/spec/unit/ridley/host_connector/ssh/worker_spec.rb +0 -57
- data/spec/unit/ridley/host_connector/winrm/worker_spec.rb +0 -139
@@ -1,163 +0,0 @@
|
|
1
|
-
module Ridley
|
2
|
-
# Represents a binding that will be evaluated as an ERB template. When bootstrapping
|
3
|
-
# nodes, an instance of this class represents the customizable and necessary configurations
|
4
|
-
# needed by the Host in order to install and connect to Chef. By default, this class will be used
|
5
|
-
# when WinRM is the best way to connect to the node.
|
6
|
-
#
|
7
|
-
# @author Kyle Allan <kallan@riotgames.com>
|
8
|
-
#
|
9
|
-
# Windows Specific code written by Seth Chisamore (<schisamo@opscode.com>) in knife-windows
|
10
|
-
# https://github.com/opscode/knife-windows/blob/3b8886ddcfb928ca0958cd05b22f8c3d78bee86e/lib/chef/knife/bootstrap/windows-chef-client-msi.erb
|
11
|
-
# https://github.com/opscode/knife-windows/blob/78d38bbed358ac20107fc2b5b427f4b5e52e5cb2/lib/chef/knife/core/windows_bootstrap_context.rb
|
12
|
-
class WindowsTemplateBinding
|
13
|
-
include Ridley::BootstrapBinding
|
14
|
-
|
15
|
-
attr_reader :template_file
|
16
|
-
|
17
|
-
# @option options [String] :validator_client
|
18
|
-
# @option options [String] :validator_path
|
19
|
-
# filepath to the validator used to bootstrap the node (required)
|
20
|
-
# @option options [String] :bootstrap_proxy (nil)
|
21
|
-
# URL to a proxy server to bootstrap through
|
22
|
-
# @option options [String] :encrypted_data_bag_secret
|
23
|
-
# your organizations encrypted data bag secret
|
24
|
-
# @option options [Hash] :hints (Hash.new)
|
25
|
-
# a hash of Ohai hints to place on the bootstrapped node
|
26
|
-
# @option options [Hash] :attributes (Hash.new)
|
27
|
-
# a hash of attributes to use in the first Chef run
|
28
|
-
# @option options [Array] :run_list (Array.new)
|
29
|
-
# an initial run list to bootstrap with
|
30
|
-
# @option options [String] :chef_version (nil)
|
31
|
-
# version of Chef to install on the node
|
32
|
-
# @option options [String] :environment ('_default')
|
33
|
-
# environment to join the node to
|
34
|
-
# @option options [Boolean] :sudo (true)
|
35
|
-
# bootstrap with sudo (default: true)
|
36
|
-
# @option options [String] :template ('windows_omnibus')
|
37
|
-
# bootstrap template to use
|
38
|
-
def initialize(options)
|
39
|
-
options = self.class.default_options.merge(options)
|
40
|
-
options[:template] ||= default_template
|
41
|
-
self.class.validate_options(options)
|
42
|
-
|
43
|
-
@template_file = options[:template]
|
44
|
-
@bootstrap_proxy = options[:bootstrap_proxy]
|
45
|
-
@chef_version = options[:chef_version] ? options[:chef_version] : "latest"
|
46
|
-
@validator_path = options[:validator_path]
|
47
|
-
@encrypted_data_bag_secret = options[:encrypted_data_bag_secret]
|
48
|
-
@server_url = options[:server_url]
|
49
|
-
@validator_client = options[:validator_client]
|
50
|
-
@node_name = options[:node_name]
|
51
|
-
@attributes = options[:attributes]
|
52
|
-
@run_list = options[:run_list]
|
53
|
-
@environment = options[:environment]
|
54
|
-
end
|
55
|
-
|
56
|
-
# @return [String]
|
57
|
-
def boot_command
|
58
|
-
template.evaluate(self)
|
59
|
-
end
|
60
|
-
|
61
|
-
# @return [String]
|
62
|
-
def chef_config
|
63
|
-
body = <<-CONFIG
|
64
|
-
log_level :info
|
65
|
-
log_location STDOUT
|
66
|
-
chef_server_url "#{server_url}"
|
67
|
-
validation_client_name "#{validator_client}"
|
68
|
-
CONFIG
|
69
|
-
|
70
|
-
if node_name.present?
|
71
|
-
body << %Q{node_name "#{node_name}"\n}
|
72
|
-
else
|
73
|
-
body << "# Using default node name (fqdn)\n"
|
74
|
-
end
|
75
|
-
|
76
|
-
if bootstrap_proxy.present?
|
77
|
-
body << %Q{http_proxy "#{bootstrap_proxy}"\n}
|
78
|
-
body << %Q{https_proxy "#{bootstrap_proxy}"\n}
|
79
|
-
end
|
80
|
-
|
81
|
-
if encrypted_data_bag_secret.present?
|
82
|
-
body << %Q{encrypted_data_bag_secret '#{bootstrap_directory}\\encrypted_data_bag_secret'\n}
|
83
|
-
end
|
84
|
-
|
85
|
-
escape_and_echo(body)
|
86
|
-
end
|
87
|
-
|
88
|
-
# @return [String]
|
89
|
-
def bootstrap_directory
|
90
|
-
"C:\\chef"
|
91
|
-
end
|
92
|
-
|
93
|
-
# @return [String]
|
94
|
-
def validation_key
|
95
|
-
escape_and_echo(IO.read(File.expand_path(validator_path)).chomp)
|
96
|
-
rescue Errno::ENOENT
|
97
|
-
raise Errors::ValidatorNotFound, "Error bootstrapping: Validator not found at '#{validator_path}'"
|
98
|
-
end
|
99
|
-
|
100
|
-
# @return [String]
|
101
|
-
def chef_run
|
102
|
-
"chef-client -j #{bootstrap_directory}\\first-boot.json -E #{environment}"
|
103
|
-
end
|
104
|
-
|
105
|
-
# @return [String]
|
106
|
-
def default_template
|
107
|
-
templates_path.join('windows_omnibus.erb').to_s
|
108
|
-
end
|
109
|
-
|
110
|
-
# @return [String]
|
111
|
-
def encrypted_data_bag_secret
|
112
|
-
return unless @encrypted_data_bag_secret
|
113
|
-
|
114
|
-
escape_and_echo(@encrypted_data_bag_secret)
|
115
|
-
end
|
116
|
-
|
117
|
-
# Implements a Powershell script that attempts a simple
|
118
|
-
# 'wget' to download the Chef msi
|
119
|
-
#
|
120
|
-
# @return [String]
|
121
|
-
def windows_wget_powershell
|
122
|
-
win_wget_ps = <<-WGET_PS
|
123
|
-
param(
|
124
|
-
[String] $remoteUrl,
|
125
|
-
[String] $localPath
|
126
|
-
)
|
127
|
-
|
128
|
-
$webClient = new-object System.Net.WebClient;
|
129
|
-
|
130
|
-
$webClient.DownloadFile($remoteUrl, $localPath);
|
131
|
-
WGET_PS
|
132
|
-
|
133
|
-
escape_and_echo(win_wget_ps)
|
134
|
-
end
|
135
|
-
|
136
|
-
# @return [String]
|
137
|
-
def install_chef
|
138
|
-
'msiexec /qb /i "%LOCAL_DESTINATION_MSI_PATH%"'
|
139
|
-
end
|
140
|
-
|
141
|
-
# @return [String]
|
142
|
-
def first_boot
|
143
|
-
escape_and_echo(JSON.fast_generate(attributes.merge(run_list: run_list)))
|
144
|
-
end
|
145
|
-
|
146
|
-
# @return [String]
|
147
|
-
def set_path
|
148
|
-
"SET \"PATH=%PATH%;C:\\ruby\\bin;C:\\opscode\\chef\\bin;C:\\opscode\\chef\\embedded\\bin\"\n"
|
149
|
-
end
|
150
|
-
|
151
|
-
# @return [String]
|
152
|
-
def local_download_path
|
153
|
-
"%TEMP%\\chef-client-#{chef_version}.msi"
|
154
|
-
end
|
155
|
-
|
156
|
-
# escape WIN BATCH special chars
|
157
|
-
# and prefixes each line with an
|
158
|
-
# echo
|
159
|
-
def escape_and_echo(file_contents)
|
160
|
-
file_contents.gsub(/^(.*)$/, 'echo.\1').gsub(/([(<|>)^])/, '^\1')
|
161
|
-
end
|
162
|
-
end
|
163
|
-
end
|
data/lib/ridley/bootstrapper.rb
DELETED
@@ -1,89 +0,0 @@
|
|
1
|
-
require_relative 'bootstrapper/context'
|
2
|
-
require_relative 'logging'
|
3
|
-
|
4
|
-
module Ridley
|
5
|
-
# @author Jamie Winsor <reset@riotgames.com>
|
6
|
-
class Bootstrapper
|
7
|
-
include Celluloid
|
8
|
-
include Ridley::Logging
|
9
|
-
|
10
|
-
# @return [Array<String>]
|
11
|
-
attr_reader :hosts
|
12
|
-
|
13
|
-
# @return [Hash]
|
14
|
-
attr_reader :options
|
15
|
-
|
16
|
-
# @param [Array<#to_s>] hosts
|
17
|
-
# @option options [Hash] :ssh
|
18
|
-
# * :user (String) a shell user that will login to each node and perform the bootstrap command on (required)
|
19
|
-
# * :password (String) the password for the shell user that will perform the bootstrap
|
20
|
-
# * :keys (Array, String) an array of keys (or a single key) to authenticate the ssh user with instead of a password
|
21
|
-
# * :timeout (Float) [5.0] timeout value for SSH bootstrap
|
22
|
-
# @option options [Hash] :winrm
|
23
|
-
# * :user (String) a user that will login to each node and perform the bootstrap command on (required)
|
24
|
-
# * :password (String) the password for the user that will perform the bootstrap
|
25
|
-
# * :port (Fixnum) the winrm port to connect on the node the bootstrap will be performed on (5985)
|
26
|
-
# @option options [String] :validator_client
|
27
|
-
# @option options [String] :validator_path
|
28
|
-
# filepath to the validator used to bootstrap the node (required)
|
29
|
-
# @option options [String] :bootstrap_proxy (nil)
|
30
|
-
# URL to a proxy server to bootstrap through
|
31
|
-
# @option options [String] :encrypted_data_bag_secret
|
32
|
-
# your organizations encrypted data bag secret
|
33
|
-
# @option options [Hash] :hints (Hash.new)
|
34
|
-
# a hash of Ohai hints to place on the bootstrapped node
|
35
|
-
# @option options [Hash] :attributes (Hash.new)
|
36
|
-
# a hash of attributes to use in the first Chef run
|
37
|
-
# @option options [Array] :run_list (Array.new)
|
38
|
-
# an initial run list to bootstrap with
|
39
|
-
# @option options [String] :chef_version (nil)
|
40
|
-
# version of Chef to install on the node
|
41
|
-
# @option options [String] :environment ('_default')
|
42
|
-
# environment to join the node to
|
43
|
-
# @option options [Boolean] :sudo (true)
|
44
|
-
# bootstrap with sudo (default: true)
|
45
|
-
# @option options [String] :template
|
46
|
-
# bootstrap template to use
|
47
|
-
def initialize(hosts, options = {})
|
48
|
-
@hosts = Array(hosts).flatten.collect(&:to_s).uniq
|
49
|
-
@options = options.dup
|
50
|
-
@options[:ssh] ||= Hash.new
|
51
|
-
@options[:ssh] = {
|
52
|
-
timeout: 5.0,
|
53
|
-
sudo: true
|
54
|
-
}.merge(@options[:ssh])
|
55
|
-
|
56
|
-
@options[:sudo] = @options[:ssh][:sudo]
|
57
|
-
end
|
58
|
-
|
59
|
-
# @raise [Errors::HostConnectionError] if a node is unreachable
|
60
|
-
#
|
61
|
-
# @return [Array<Bootstrapper::Context>]
|
62
|
-
def contexts
|
63
|
-
@contexts ||= @hosts.collect { |host| Context.create(host, options) }
|
64
|
-
end
|
65
|
-
|
66
|
-
# @raise [Errors::HostConnectionError] if a node is unreachable
|
67
|
-
#
|
68
|
-
# @return [HostConnector::ResponseSet]
|
69
|
-
def run
|
70
|
-
workers = Array.new
|
71
|
-
futures = contexts.collect do |context|
|
72
|
-
log.info { "Running bootstrap command on #{context.host}" }
|
73
|
-
|
74
|
-
workers << worker = context.host_connector::Worker.new(context.host, self.options.freeze)
|
75
|
-
|
76
|
-
worker.future.run(context.template_binding.boot_command)
|
77
|
-
end
|
78
|
-
|
79
|
-
HostConnector::ResponseSet.new.tap do |response_set|
|
80
|
-
futures.each do |future|
|
81
|
-
status, response = future.value
|
82
|
-
response_set.add_response(response)
|
83
|
-
end
|
84
|
-
end
|
85
|
-
ensure
|
86
|
-
workers.map(&:terminate)
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
@@ -1,81 +0,0 @@
|
|
1
|
-
require 'erubis'
|
2
|
-
|
3
|
-
module Ridley
|
4
|
-
class Bootstrapper
|
5
|
-
# @author Jamie Winsor <reset@riotgames.com>
|
6
|
-
class Context
|
7
|
-
class << self
|
8
|
-
# @param [String] host
|
9
|
-
# @option options [Hash] :ssh
|
10
|
-
# * :user (String) a shell user that will login to each node and perform the bootstrap command on (required)
|
11
|
-
# * :password (String) the password for the shell user that will perform the bootstrap
|
12
|
-
# * :port (Fixnum) the ssh port to connect on the node the bootstrap will be performed on (22)
|
13
|
-
# * :keys (Array, String) an array of keys (or a single key) to authenticate the ssh user with instead of a password
|
14
|
-
# * :timeout (Float) [5.0] timeout value for SSH bootstrap
|
15
|
-
# @option options [Hash] :winrm
|
16
|
-
# * :user (String) a user that will login to each node and perform the bootstrap command on (required)
|
17
|
-
# * :password (String) the password for the user that will perform the bootstrap
|
18
|
-
# * :port (Fixnum) the winrm port to connect on the node the bootstrap will be performed on (5985)
|
19
|
-
# @option options [String] :validator_client
|
20
|
-
# @option options [String] :validator_path
|
21
|
-
# filepath to the validator used to bootstrap the node (required)
|
22
|
-
# @option options [String] :bootstrap_proxy (nil)
|
23
|
-
# URL to a proxy server to bootstrap through
|
24
|
-
# @option options [String] :encrypted_data_bag_secret
|
25
|
-
# your organizations encrypted data bag secret
|
26
|
-
# @option options [Hash] :hints (Hash.new)
|
27
|
-
# a hash of Ohai hints to place on the bootstrapped node
|
28
|
-
# @option options [Hash] :attributes (Hash.new)
|
29
|
-
# a hash of attributes to use in the first Chef run
|
30
|
-
# @option options [Array] :run_list (Array.new)
|
31
|
-
# an initial run list to bootstrap with
|
32
|
-
# @option options [String] :chef_version (nil)
|
33
|
-
# version of Chef to install on the node
|
34
|
-
# @option options [String] :environment ('_default')
|
35
|
-
# environment to join the node to
|
36
|
-
# @option options [Boolean] :sudo (true)
|
37
|
-
# bootstrap with sudo (default: true)
|
38
|
-
# @option options [String] :template ('omnibus')
|
39
|
-
# bootstrap template to use
|
40
|
-
#
|
41
|
-
# @raise [Errors::HostConnectionError] if a node is unreachable
|
42
|
-
def create(host, options = {})
|
43
|
-
host_connector = HostConnector.best_connector_for(host, options)
|
44
|
-
template_binding = case host_connector.to_s
|
45
|
-
when Ridley::HostConnector::SSH.to_s
|
46
|
-
Ridley::UnixTemplateBinding.new(options)
|
47
|
-
when Ridley::HostConnector::WinRM.to_s
|
48
|
-
Ridley::WindowsTemplateBinding.new(options)
|
49
|
-
else
|
50
|
-
raise Ridley::Errors::HostConnectionError, "Cannot find an appropriate Template Binding for an unknown connector."
|
51
|
-
end
|
52
|
-
new(host, host_connector, template_binding)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
# @return [String]
|
57
|
-
attr_reader :host
|
58
|
-
# @return [Ridley::HostConnector]
|
59
|
-
attr_reader :host_connector
|
60
|
-
# @return [Ridley::Binding]
|
61
|
-
attr_reader :template_binding
|
62
|
-
|
63
|
-
# @param [String] host
|
64
|
-
# name of the node as identified in Chef
|
65
|
-
# @param [Ridley::HostConnector] host_connector
|
66
|
-
# either the SSH or WinRM Connector class
|
67
|
-
# @param [Ridley::Binding] template_binding
|
68
|
-
# an instance of either the UnixTemplateBinding or WindowsTemplateBinding class
|
69
|
-
def initialize(host, host_connector, template_binding)
|
70
|
-
@host = host
|
71
|
-
@host_connector = host_connector
|
72
|
-
@template_binding = template_binding
|
73
|
-
end
|
74
|
-
|
75
|
-
# @return [String]
|
76
|
-
def clean_command
|
77
|
-
"rm /etc/chef/first-boot.json; rm /etc/chef/validation.pem"
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
@@ -1,98 +0,0 @@
|
|
1
|
-
module Ridley
|
2
|
-
module HostConnector
|
3
|
-
# @author Jamie Winsor <reset@riotgames.com>
|
4
|
-
class ResponseSet
|
5
|
-
class << self
|
6
|
-
# Merges the responses of the other ResponseSet with the target ResponseSet
|
7
|
-
# and returns the mutated target
|
8
|
-
#
|
9
|
-
# @param [HostConnector::ResponseSet] target
|
10
|
-
# @param [HostConnector::ResponseSet] other
|
11
|
-
#
|
12
|
-
# @return [HostConnector::ResponseSet]
|
13
|
-
def merge!(target, other)
|
14
|
-
if other.is_a?(self)
|
15
|
-
target.add_response(other.responses)
|
16
|
-
end
|
17
|
-
|
18
|
-
target
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
extend Forwardable
|
23
|
-
include Enumerable
|
24
|
-
|
25
|
-
attr_reader :failures
|
26
|
-
attr_reader :successes
|
27
|
-
|
28
|
-
def_delegator :responses, :each
|
29
|
-
|
30
|
-
def initialize(responses = Array.new)
|
31
|
-
@failures = Array.new
|
32
|
-
@successes = Array.new
|
33
|
-
|
34
|
-
add_response Array(responses)
|
35
|
-
end
|
36
|
-
|
37
|
-
# @param [HostConnector::Response, Array<HostConnector::Response>] response
|
38
|
-
#
|
39
|
-
# @return [Array<HostConnector::Response>]
|
40
|
-
def add_response(response)
|
41
|
-
if response.is_a?(Array)
|
42
|
-
until response.empty?
|
43
|
-
add_response(response.pop)
|
44
|
-
end
|
45
|
-
return responses
|
46
|
-
end
|
47
|
-
|
48
|
-
response.error? ? add_failure(response) : add_success(response)
|
49
|
-
responses
|
50
|
-
end
|
51
|
-
alias_method :<<, :add_response
|
52
|
-
|
53
|
-
def responses
|
54
|
-
successes + failures
|
55
|
-
end
|
56
|
-
|
57
|
-
# Return true if the response set contains any errors
|
58
|
-
#
|
59
|
-
# @return [Boolean]
|
60
|
-
def has_errors?
|
61
|
-
self.failures.any?
|
62
|
-
end
|
63
|
-
|
64
|
-
# Merges the responses of another ResponseSet with self and returns
|
65
|
-
# a new instance of ResponseSet
|
66
|
-
#
|
67
|
-
# @param [Ridley::HostConnector::ResponseSet] other
|
68
|
-
#
|
69
|
-
# @return [Ridley::HostConnector::ResponseSet]
|
70
|
-
def merge(other)
|
71
|
-
target = self.class.new(self.responses) # Why the fuck can't I use #dup here?
|
72
|
-
self.class.merge!(target, other)
|
73
|
-
end
|
74
|
-
|
75
|
-
# Merges the respones of another ResponseSet with self and returns
|
76
|
-
# mutated self
|
77
|
-
#
|
78
|
-
# @param [Ridley::HostConnector::ResponseSet] other
|
79
|
-
#
|
80
|
-
# @return [self]
|
81
|
-
def merge!(other)
|
82
|
-
self.class.merge!(self, other)
|
83
|
-
end
|
84
|
-
|
85
|
-
private
|
86
|
-
|
87
|
-
# @param [HostConnector::Response] response
|
88
|
-
def add_failure(response)
|
89
|
-
self.failures << response
|
90
|
-
end
|
91
|
-
|
92
|
-
# @param [HostConnector::Response] response
|
93
|
-
def add_success(response)
|
94
|
-
self.successes << response
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
@@ -1,135 +0,0 @@
|
|
1
|
-
module Ridley
|
2
|
-
module HostConnector
|
3
|
-
class SSH
|
4
|
-
# @author Jamie Winsor <reset@riotgames.com>
|
5
|
-
# @api private
|
6
|
-
class Worker
|
7
|
-
include Celluloid
|
8
|
-
include Celluloid::Logger
|
9
|
-
|
10
|
-
attr_reader :sudo
|
11
|
-
attr_reader :user
|
12
|
-
attr_reader :host
|
13
|
-
# @return [Hashie::Mash]
|
14
|
-
attr_reader :options
|
15
|
-
|
16
|
-
EMBEDDED_RUBY_PATH = '/opt/chef/embedded/bin/ruby'.freeze
|
17
|
-
|
18
|
-
# @param [Hash] options
|
19
|
-
def initialize(host, options = {})
|
20
|
-
options = options.deep_symbolize_keys
|
21
|
-
@options = options[:ssh] || Hash.new
|
22
|
-
@host = host
|
23
|
-
@sudo = @options[:sudo]
|
24
|
-
@user = @options[:user]
|
25
|
-
|
26
|
-
@options[:paranoid] = false
|
27
|
-
end
|
28
|
-
|
29
|
-
# @param [String] command
|
30
|
-
#
|
31
|
-
# @return [Array]
|
32
|
-
def run(command)
|
33
|
-
response = Ridley::HostConnector::Response.new(host)
|
34
|
-
debug "Running SSH command: '#{command}' on: '#{host}' as: '#{user}'"
|
35
|
-
|
36
|
-
channel_exec = ->(channel, command) do
|
37
|
-
channel.exec(command) do |ch, success|
|
38
|
-
unless success
|
39
|
-
raise "Channel execution failed while executing command #{command}"
|
40
|
-
end
|
41
|
-
|
42
|
-
channel.on_data do |ch, data|
|
43
|
-
response.stdout += data
|
44
|
-
info "NODE[#{host}] #{data}" if data.present? and data != "\r\n"
|
45
|
-
end
|
46
|
-
|
47
|
-
channel.on_extended_data do |ch, type, data|
|
48
|
-
response.stderr += data
|
49
|
-
info "NODE[#{host}] #{data}" if data.present? and data != "\r\n"
|
50
|
-
end
|
51
|
-
|
52
|
-
channel.on_request("exit-status") do |ch, data|
|
53
|
-
response.exit_code = data.read_long
|
54
|
-
end
|
55
|
-
|
56
|
-
channel.on_request("exit-signal") do |ch, data|
|
57
|
-
response.exit_signal = data.read_string
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
Net::SSH.start(host, user, options.slice(*Net::SSH::VALID_OPTIONS)) do |ssh|
|
63
|
-
ssh.open_channel do |channel|
|
64
|
-
if self.sudo
|
65
|
-
channel.request_pty do |channel, success|
|
66
|
-
raise "Could not aquire pty: A pty is required for running sudo commands." unless success
|
67
|
-
|
68
|
-
channel_exec.call(channel, command)
|
69
|
-
end
|
70
|
-
else
|
71
|
-
channel_exec.call(channel, command)
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
ssh.loop
|
76
|
-
end
|
77
|
-
|
78
|
-
case response.exit_code
|
79
|
-
when 0
|
80
|
-
debug "Successfully ran SSH command on: '#{host}' as: '#{user}'"
|
81
|
-
[ :ok, response ]
|
82
|
-
else
|
83
|
-
error "Successfully ran SSH command on: '#{host}' as: '#{user}', but it failed"
|
84
|
-
error response.stdout
|
85
|
-
[ :error, response ]
|
86
|
-
end
|
87
|
-
rescue => e
|
88
|
-
error "Failed to run SSH command on: '#{host}' as: '#{user}'"
|
89
|
-
error "#{e.class}: #{e.message}"
|
90
|
-
response.exit_code = -1
|
91
|
-
response.stderr = e.message
|
92
|
-
[ :error, response ]
|
93
|
-
end
|
94
|
-
|
95
|
-
# Executes a chef-client command on the nodes
|
96
|
-
#
|
97
|
-
# @return [#run]
|
98
|
-
def chef_client
|
99
|
-
command = "chef-client"
|
100
|
-
if sudo
|
101
|
-
command = "sudo #{command}"
|
102
|
-
end
|
103
|
-
|
104
|
-
run(command)
|
105
|
-
end
|
106
|
-
|
107
|
-
# Writes the given encrypted data bag secret to the node
|
108
|
-
#
|
109
|
-
# @param [String] secret
|
110
|
-
# your organization's encrypted data bag secret
|
111
|
-
#
|
112
|
-
# @return [#run]
|
113
|
-
def put_secret(secret)
|
114
|
-
command = "echo '#{secret}' > /etc/chef/encrypted_data_bag_secret; chmod 0600 /etc/chef/encrypted_data_bag_secret"
|
115
|
-
run(command)
|
116
|
-
end
|
117
|
-
|
118
|
-
# Executes a provided Ruby script in the embedded Ruby installation
|
119
|
-
#
|
120
|
-
# @param [Array<String>] command_lines
|
121
|
-
# An Array of lines of the command to be executed
|
122
|
-
#
|
123
|
-
# @return [#run]
|
124
|
-
def ruby_script(command_lines)
|
125
|
-
command = "#{EMBEDDED_RUBY_PATH} -e \"#{command_lines.join(';')}\""
|
126
|
-
run(command)
|
127
|
-
end
|
128
|
-
|
129
|
-
private
|
130
|
-
|
131
|
-
attr_reader :runner
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|
135
|
-
end
|