ridley 1.7.1 → 2.0.0

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.
@@ -1,218 +0,0 @@
1
- # Silencing warnings because not all versions of GSSAPI support all of the GSSAPI methods
2
- # the gssapi gem attempts to attach to and these warnings are dumped to STDERR.
3
- silence_warnings do
4
- require 'winrm'
5
- end
6
-
7
- module Ridley
8
- module HostConnector
9
- class WinRM < HostConnector::Base
10
- require_relative 'winrm/command_uploader'
11
-
12
- DEFAULT_PORT = 5985
13
- EMBEDDED_RUBY_PATH = 'C:\opscode\chef\embedded\bin\ruby'.freeze
14
- SESSION_TYPE_COMMAND_METHODS = {
15
- powershell: :run_powershell_script,
16
- cmd: :run_cmd
17
- }.freeze
18
-
19
- # Execute a shell command on a node
20
- #
21
- # @param [String] host
22
- # the host to perform the action on
23
- # @param [String] command
24
- #
25
- # @option options [Symbol] :session_type (:cmd)
26
- # * :powershell - run the given command in a powershell session
27
- # * :cmd - run the given command in a cmd session
28
- # @option options [Hash] :winrm
29
- # * :user (String) a user that will login to each node and perform the bootstrap command on
30
- # * :password (String) the password for the user that will perform the bootstrap (required)
31
- # * :port (Fixnum) the winrm port to connect on the node the bootstrap will be performed on (5985)
32
- #
33
- # @return [HostConnector::Response]
34
- def run(host, command, options = {})
35
- options = options.reverse_merge(winrm: Hash.new, session_type: :cmd)
36
- options[:winrm].reverse_merge!(port: DEFAULT_PORT)
37
-
38
- command_uploaders = Array.new
39
- user = options[:winrm][:user]
40
- password = options[:winrm][:password]
41
- port = options[:winrm][:port]
42
- connection = winrm(host, port, options[:winrm].slice(:user, :password))
43
-
44
- unless command_method = SESSION_TYPE_COMMAND_METHODS[options[:session_type]]
45
- raise RuntimeError, "unknown session type: #{options[:session_type]}. Known session types " +
46
- "are: #{SESSION_TYPE_COMMAND_METHODS.keys}"
47
- end
48
-
49
- HostConnector::Response.new(host).tap do |response|
50
- begin
51
- command_uploaders << command_uploader = CommandUploader.new(connection)
52
- command = get_command(command, command_uploader)
53
-
54
- log.info "Running WinRM Command: '#{command}' on: '#{host}' as: '#{user}'"
55
-
56
- defer {
57
- output = connection.send(command_method, command) do |stdout, stderr|
58
- if stdout && stdout.present?
59
- response.stdout += stdout
60
- log.info "[#{host}](WinRM) #{stdout}"
61
- end
62
-
63
- if stderr && stderr.present?
64
- response.stderr += stderr
65
- log.info "[#{host}](WinRM) #{stderr}"
66
- end
67
- end
68
- response.exit_code = output[:exitcode]
69
- }
70
- rescue ::WinRM::WinRMHTTPTransportError => ex
71
- response.exit_code = :transport_error
72
- response.stderr = ex.message
73
- end
74
-
75
- case response.exit_code
76
- when 0
77
- log.info "Successfully ran WinRM command on: '#{host}' as: '#{user}'"
78
- when :transport_error
79
- log.info "A transport error occured while attempting to run a WinRM command on: '#{host}' as: '#{user}'"
80
- else
81
- log.info "Successfully ran WinRM command on: '#{host}' as: '#{user}', but it failed"
82
- end
83
- end
84
- ensure
85
- begin
86
- command_uploaders.map(&:cleanup)
87
- rescue ::WinRM::WinRMHTTPTransportError => ex
88
- log.info "Error cleaning up leftover Powershell scripts on some hosts"
89
- end
90
- end
91
-
92
- # Returns the command if it does not break the WinRM command length
93
- # limit. Otherwise, we return an execution of the command as a batch file.
94
- #
95
- # @param command [String]
96
- #
97
- # @return [String]
98
- def get_command(command, command_uploader)
99
- if command.length < CommandUploader::CHUNK_LIMIT
100
- command
101
- else
102
- log.debug "Detected a command that was longer than #{CommandUploader::CHUNK_LIMIT} characters. " +
103
- "Uploading command as a file to the host."
104
- command_uploader.upload(command)
105
- command_uploader.command
106
- end
107
- end
108
-
109
- # Bootstrap a node
110
- #
111
- # @param [String] host
112
- # the host to perform the action on
113
- #
114
- # @option options [Hash] :winrm
115
- # * :user (String) a user that will login to each node and perform the bootstrap command on
116
- # * :password (String) the password for the user that will perform the bootstrap (required)
117
- # * :port (Fixnum) the winrm port to connect on the node the bootstrap will be performed on (5985)
118
- #
119
- # @return [HostConnector::Response]
120
- def bootstrap(host, options = {})
121
- context = BootstrapContext::Windows.new(options)
122
-
123
- log.info "Bootstrapping host: #{host}"
124
- run(host, context.boot_command, options)
125
- end
126
-
127
- # Perform a chef client run on a node
128
- #
129
- # @param [String] host
130
- # the host to perform the action on
131
- #
132
- # @option options [Hash] :winrm
133
- # * :user (String) a user that will login to each node and perform the bootstrap command on
134
- # * :password (String) the password for the user that will perform the bootstrap (required)
135
- # * :port (Fixnum) the winrm port to connect on the node the bootstrap will be performed on (5985)
136
- #
137
- # @return [HostConnector::Response]
138
- def chef_client(host, options = {})
139
- run(host, "chef-client", options)
140
- end
141
-
142
- # Write your encrypted data bag secret on a node
143
- #
144
- # @param [String] host
145
- # the host to perform the action on
146
- # @param [String] secret
147
- # your organization's encrypted data bag secret
148
- #
149
- # @option options [Hash] :winrm
150
- # * :user (String) a user that will login to each node and perform the bootstrap command on
151
- # * :password (String) the password for the user that will perform the bootstrap (required)
152
- # * :port (Fixnum) the winrm port to connect on the node the bootstrap will be performed on (5985)
153
- #
154
- # @return [HostConnector::Response]
155
- def put_secret(host, secret, options = {})
156
- command = "echo #{secret} > C:\\chef\\encrypted_data_bag_secret"
157
- run(host, command, options)
158
- end
159
-
160
- # Execute line(s) of Ruby code on a node using Chef's embedded Ruby
161
- #
162
- # @param [String] host
163
- # the host to perform the action on
164
- # @param [Array<String>] command_lines
165
- # An Array of lines of the command to be executed
166
- #
167
- # @option options [Hash] :winrm
168
- # * :user (String) a user that will login to each node and perform the bootstrap command on
169
- # * :password (String) the password for the user that will perform the bootstrap (required)
170
- # * :port (Fixnum) the winrm port to connect on the node the bootstrap will be performed on (5985)
171
- #
172
- # @return [HostConnector::Response]
173
- def ruby_script(host, command_lines, options = {})
174
- command = "#{EMBEDDED_RUBY_PATH} -e \"#{command_lines.join(';')}\""
175
- run(host, command, options)
176
- end
177
-
178
- # Uninstall Chef from a node
179
- #
180
- # @param [String] host
181
- # the host to perform the action on
182
- #
183
- # @option options [Boolena] :skip_chef (false)
184
- # skip removal of the Chef package and the contents of the installation
185
- # directory. Setting this to true will only remove any data and configurations
186
- # generated by running Chef client.
187
- # @option options [Hash] :winrm
188
- # * :user (String) a user that will login to each node and perform the bootstrap command on
189
- # * :password (String) the password for the user that will perform the bootstrap (required)
190
- # * :port (Fixnum) the winrm port to connect on the node the bootstrap will be performed on (5985)
191
- #
192
- # @return [HostConnector::Response]
193
- def uninstall_chef(host, options = {})
194
- options[:session_type] = :powershell
195
- log.info "Uninstalling Chef from host: #{host}"
196
- run(host, CommandContext::WindowsUninstall.command(options), options)
197
- end
198
-
199
- private
200
-
201
- # @param [String] host
202
- # @param [Integer] port
203
- #
204
- # @option options [String] :user
205
- # @option options [String] :password
206
- #
207
- # @return [WinRM::WinRMWebService]
208
- def winrm(host, port, options = {})
209
- winrm_opts = { disable_sspi: true, basic_auth_only: true }
210
- winrm_opts[:user] = options[:user]
211
- winrm_opts[:pass] = options[:password]
212
- client = ::WinRM::WinRMWebService.new("http://#{host}:#{port}/wsman", :plaintext, winrm_opts)
213
- client.set_timeout(6000)
214
- client
215
- end
216
- end
217
- end
218
- end
@@ -1,87 +0,0 @@
1
- module Ridley
2
- module HostConnector
3
- class WinRM
4
- # @example
5
- # command_uploader = CommandUploader.new(long_command, winrm)
6
- # command_uploader.upload
7
- # command_uploader.command
8
- #
9
- # This class is used by WinRM Workers when the worker is told to execute a command that
10
- # might be too long for WinRM to handle.
11
- #
12
- # After an instance of this class is created, the upload method will upload the long command
13
- # to the node being worked on in chunks. Once on the node, some Powershell code will decode
14
- # the long_command into a batch file. The command method will return a String representing the
15
- # command the worker will use to execute the uploaded batch script.
16
- class CommandUploader
17
- CHUNK_LIMIT = 1024
18
-
19
- # @return [WinRM::WinRMWebService]
20
- attr_reader :winrm
21
- # @return [String]
22
- attr_reader :base64_file_name
23
- # @return [String]
24
- attr_reader :command_file_name
25
-
26
- # @param [WinRM::WinRMWebService] winrm
27
- def initialize(winrm)
28
- @winrm = winrm
29
- @base64_file_name = get_file_path("winrm-upload-base64-#{unique_string}")
30
- @command_file_name = get_file_path("winrm-upload-#{unique_string}.bat")
31
- end
32
-
33
- # Uploads the command encoded as base64 to a file on the host
34
- # and then uses Powershell to transform the base64 file into the
35
- # command that was originally passed through.
36
- #
37
- # @param [String] command_string
38
- def upload(command_string)
39
- upload_command(command_string)
40
- convert_command
41
- end
42
-
43
- # @return [String] the command to execute the uploaded file
44
- def command
45
- "cmd.exe /C #{command_file_name}"
46
- end
47
-
48
- # Runs a delete command on the files generated by #base64_file_name
49
- # and #command_file_name
50
- def cleanup
51
- winrm.run_cmd( "del #{base64_file_name} /F /Q" )
52
- winrm.run_cmd( "del #{command_file_name} /F /Q" )
53
- end
54
-
55
- private
56
-
57
- def upload_command(command_string)
58
- command_string_chars(command_string).each_slice(CHUNK_LIMIT) do |chunk|
59
- winrm.run_cmd( "echo #{chunk.join} >> \"#{base64_file_name}\"" )
60
- end
61
- end
62
-
63
- def command_string_chars(command_string)
64
- Base64.encode64(command_string).gsub("\n", '').chars.to_a
65
- end
66
-
67
- def convert_command
68
- winrm.powershell <<-POWERSHELL
69
- $base64_string = Get-Content \"#{base64_file_name}\"
70
- $bytes = [System.Convert]::FromBase64String($base64_string)
71
- $new_file = [System.IO.Path]::GetFullPath(\"#{command_file_name}\")
72
- [System.IO.File]::WriteAllBytes($new_file,$bytes)
73
- POWERSHELL
74
- end
75
-
76
- def unique_string
77
- @unique_string ||= "#{Process.pid}-#{Time.now.to_i}"
78
- end
79
-
80
- # @return [String]
81
- def get_file_path(file)
82
- (winrm.run_cmd("echo %TEMP%\\#{file}"))[:data][0][:stdout].chomp
83
- end
84
- end
85
- end
86
- end
87
- end
@@ -1,173 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Ridley::HostCommander do
4
- subject { described_class.new }
5
-
6
- describe "#run" do
7
- let(:host) { "reset.riotgames.com" }
8
- let(:command) { "ls" }
9
- let(:options) do
10
- { ssh: { port: 22 }, winrm: { port: 5985 } }
11
- end
12
-
13
- context "when communicating to a unix node" do
14
- before do
15
- subject.stub(:connector_port_open?).with(host, options[:winrm][:port]).and_return(false)
16
- subject.stub(:connector_port_open?).with(host, options[:ssh][:port], anything).and_return(true)
17
- end
18
-
19
- it "sends a #run message to the ssh host connector" do
20
- subject.send(:ssh).should_receive(:run).with(host, command, options)
21
-
22
- subject.run(host, command, options)
23
- end
24
- end
25
-
26
- context "when communicating to a windows node" do
27
- before do
28
- subject.stub(:connector_port_open?).with(host, options[:winrm][:port]).and_return(true)
29
- subject.stub(:connector_port_open?).with(host, options[:ssh][:port], anything).and_return(false)
30
- end
31
-
32
- it "sends a #run message to the ssh host connector" do
33
- subject.send(:winrm).should_receive(:run).with(host, command, options)
34
-
35
- subject.run(host, command, options)
36
- end
37
- end
38
- end
39
-
40
- describe "#bootstrap" do
41
- let(:host) { "reset.riotgames.com" }
42
- let(:options) do
43
- { ssh: { port: 22 }, winrm: { port: 5985 } }
44
- end
45
-
46
- context "when communicating to a unix node" do
47
- before do
48
- subject.stub(:connector_port_open?).with(host, options[:winrm][:port]).and_return(false)
49
- subject.stub(:connector_port_open?).with(host, options[:ssh][:port], anything).and_return(true)
50
- end
51
-
52
- it "sends a #bootstrap message to the ssh host connector" do
53
- subject.send(:ssh).should_receive(:bootstrap).with(host, options)
54
-
55
- subject.bootstrap(host, options)
56
- end
57
- end
58
-
59
- context "when communicating to a windows node" do
60
- before do
61
- subject.stub(:connector_port_open?).with(host, options[:winrm][:port]).and_return(true)
62
- subject.stub(:connector_port_open?).with(host, options[:ssh][:port], anything).and_return(false)
63
- end
64
-
65
- it "sends a #bootstrap message to the ssh host connector" do
66
- subject.send(:winrm).should_receive(:bootstrap).with(host, options)
67
-
68
- subject.bootstrap(host, options)
69
- end
70
- end
71
- end
72
-
73
- describe "#chef_client" do
74
- let(:host) { "reset.riotgames.com" }
75
- let(:options) do
76
- { ssh: { port: 22 }, winrm: { port: 5985 } }
77
- end
78
-
79
- context "when communicating to a unix node" do
80
- before do
81
- subject.stub(:connector_port_open?).with(host, options[:winrm][:port]).and_return(false)
82
- subject.stub(:connector_port_open?).with(host, options[:ssh][:port], anything).and_return(true)
83
- end
84
-
85
- it "sends a #chef_client message to the ssh host connector" do
86
- subject.send(:ssh).should_receive(:chef_client).with(host, options)
87
-
88
- subject.chef_client(host, options)
89
- end
90
- end
91
-
92
- context "when communicating to a windows node" do
93
- before do
94
- subject.stub(:connector_port_open?).with(host, options[:winrm][:port]).and_return(true)
95
- subject.stub(:connector_port_open?).with(host, options[:ssh][:port], anything).and_return(false)
96
- end
97
-
98
- it "sends a #chef_client message to the ssh host connector" do
99
- subject.send(:winrm).should_receive(:chef_client).with(host, options)
100
-
101
- subject.chef_client(host, options)
102
- end
103
- end
104
- end
105
-
106
- describe "#put_secret" do
107
- let(:host) { "reset.riotgames.com" }
108
- let(:secret) { "something_secret" }
109
- let(:options) do
110
- { ssh: { port: 22 }, winrm: { port: 5985 } }
111
- end
112
-
113
- context "when communicating to a unix node" do
114
- before do
115
- subject.stub(:connector_port_open?).with(host, options[:winrm][:port]).and_return(false)
116
- subject.stub(:connector_port_open?).with(host, options[:ssh][:port], anything).and_return(true)
117
- end
118
-
119
- it "sends a #put_secret message to the ssh host connector" do
120
- subject.send(:ssh).should_receive(:put_secret).with(host, secret, options)
121
-
122
- subject.put_secret(host, secret, options)
123
- end
124
- end
125
-
126
- context "when communicating to a windows node" do
127
- before do
128
- subject.stub(:connector_port_open?).with(host, options[:winrm][:port]).and_return(true)
129
- subject.stub(:connector_port_open?).with(host, options[:ssh][:port], anything).and_return(false)
130
- end
131
-
132
- it "sends a #put_secret message to the ssh host connector" do
133
- subject.send(:winrm).should_receive(:put_secret).with(host, secret, options)
134
-
135
- subject.put_secret(host, secret, options)
136
- end
137
- end
138
- end
139
-
140
- describe "#ruby_script" do
141
- let(:host) { "reset.riotgames.com" }
142
- let(:command_lines) { ["line one"] }
143
- let(:options) do
144
- { ssh: { port: 22 }, winrm: { port: 5985 } }
145
- end
146
-
147
- context "when communicating to a unix node" do
148
- before do
149
- subject.stub(:connector_port_open?).with(host, options[:winrm][:port]).and_return(false)
150
- subject.stub(:connector_port_open?).with(host, options[:ssh][:port], anything).and_return(true)
151
- end
152
-
153
- it "sends a #ruby_script message to the ssh host connector" do
154
- subject.send(:ssh).should_receive(:ruby_script).with(host, command_lines, options)
155
-
156
- subject.ruby_script(host, command_lines, options)
157
- end
158
- end
159
-
160
- context "when communicating to a windows node" do
161
- before do
162
- subject.stub(:connector_port_open?).with(host, options[:winrm][:port]).and_return(true)
163
- subject.stub(:connector_port_open?).with(host, options[:ssh][:port], anything).and_return(false)
164
- end
165
-
166
- it "sends a #ruby_script message to the ssh host connector" do
167
- subject.send(:winrm).should_receive(:ruby_script).with(host, command_lines, options)
168
-
169
- subject.ruby_script(host, command_lines, options)
170
- end
171
- end
172
- end
173
- end