easy_io 0.4.2 → 0.4.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/lib/easy_io/disk.rb +30 -1
- data/lib/easy_io/run.rb +85 -8
- metadata +3 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 2b4ea31cfbbe102e2b9d4e14097c33da21c70e4de3b1a6b599c872503fd37741
|
4
|
+
data.tar.gz: d1df2063b2e92d513a37a9c14a0749c30586eb0f3fc7729100ddf7bd8add3b4b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 569f125fb4826b3db31d6d8e54e9e19ca8ab3d493a02d937197be114896ebb15314ef5e10a9459b0492dac904be8f2d45679d28eceae0df25482d38f584b5020
|
7
|
+
data.tar.gz: 8f5b5e0d8e5e28140c32a1f268cc3b8db9364fb51c33d90ed40ef2c54606967f15c55c037c1eb9fb9827c98cebce1ffd83ea0c3c55416bbfd6cce7c040e39ca3
|
data/lib/easy_io/disk.rb
CHANGED
@@ -70,7 +70,7 @@ module EasyIO
|
|
70
70
|
end
|
71
71
|
lock_success = file.flock(File::LOCK_EX | File::LOCK_NB)
|
72
72
|
if lock_success
|
73
|
-
yield(file)
|
73
|
+
yield(file) if block_given?
|
74
74
|
file.close
|
75
75
|
break
|
76
76
|
end
|
@@ -80,5 +80,34 @@ module EasyIO
|
|
80
80
|
end
|
81
81
|
end
|
82
82
|
end
|
83
|
+
|
84
|
+
# When credentials are nil, the current user will be used
|
85
|
+
def cross_domain_copy(source_folder, destination_folder, source_credentials: nil, destination_credentials: nil, file_filter: '*')
|
86
|
+
# require 'win32/process'
|
87
|
+
Dir.glob("#{source_folder}/#{file_filter}").each do |source_entry|
|
88
|
+
destination_entry = source_entry.sub(source_folder, destination_folder)
|
89
|
+
_cross_domain_entry_copy(source_entry, destination_entry, source_credentials: source_credentials, destination_credentials: destination_credentials)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def _cross_domain_entry_copy(source_entry, destination_entry, source_credentials: nil, destination_credentials: nil)
|
94
|
+
source_credentials ||= :current_user
|
95
|
+
destination_credentials ||= :current_user
|
96
|
+
|
97
|
+
# TODO: loop and read to async buffer and write as it goes
|
98
|
+
if source_credentials == :current_user
|
99
|
+
# TODO: read to memeory
|
100
|
+
else
|
101
|
+
Process.create(command_line: ?, domain: source_credentials['domain'], with_logon: source_credentials['user_name'], password: source_credentials['password'])
|
102
|
+
# TODO: read to memory
|
103
|
+
end
|
104
|
+
|
105
|
+
if destination_credentials == :current_user
|
106
|
+
# TODO: write from memory
|
107
|
+
else
|
108
|
+
Process.create(etc)
|
109
|
+
# TODO: write from memory
|
110
|
+
end
|
111
|
+
end
|
83
112
|
end
|
84
113
|
end
|
data/lib/easy_io/run.rb
CHANGED
@@ -4,6 +4,7 @@ module EasyIO
|
|
4
4
|
# execute a command with real-time output. Any stdout you want returned to the caller must come after the :output_separator which defaults to '#return_data#:'
|
5
5
|
# return_all_stdout: return all output to the caller instead after process completion
|
6
6
|
def execute_out(command, pid_logfile: nil, working_folder: Dir.pwd, regex_error_filters: [], info_on_exception: '', exception_exceptions: [], powershell: false, show_command_on_error: false, raise_on_first_error: true, return_all_stdout: false, output_separator: nil)
|
7
|
+
raise "Invalid argument to execute_out! working_folder (#{working_folder}) is not a valid directory!" unless ::File.directory?(working_folder)
|
7
8
|
output_separator ||= '#return_data#:'
|
8
9
|
if return_all_stdout
|
9
10
|
result = ''
|
@@ -22,6 +23,7 @@ module EasyIO
|
|
22
23
|
FileUtils.mkdir_p(::File.dirname(ps_script_file)) unless ::File.directory? ::File.dirname(ps_script_file)
|
23
24
|
::File.write(ps_script_file, command)
|
24
25
|
end
|
26
|
+
|
25
27
|
popen_arguments = powershell ? ['powershell.exe', ps_script_file] : [command]
|
26
28
|
Open3.popen3(*popen_arguments, chdir: working_folder) do |_stdin, stdout, stderr, wait_thread|
|
27
29
|
unless pid_logfile.nil? # Log pid in case job or script dies
|
@@ -75,7 +77,7 @@ module EasyIO
|
|
75
77
|
execute_out(ps_script, pid_logfile: pid_logfile, working_folder: working_folder, regex_error_filters: regex_error_filters, info_on_exception: info_on_exception, exception_exceptions: exception_exceptions, powershell: true, show_command_on_error: show_command_on_error, return_all_stdout: return_all_stdout, output_separator: output_separator)
|
76
78
|
end
|
77
79
|
|
78
|
-
def
|
80
|
+
def run_remote_winrm_command(remote_host, command, credentials, set_as_trusted_host: false)
|
79
81
|
add_as_winrm_trusted_host(remote_host) if set_as_trusted_host
|
80
82
|
|
81
83
|
remote_command = <<-EOS
|
@@ -96,9 +98,76 @@ module EasyIO
|
|
96
98
|
}
|
97
99
|
end
|
98
100
|
|
99
|
-
def
|
101
|
+
def run_remote_powershell_command(remote_host, command, credentials, set_as_trusted_host: false, transport: :ssh, output_stream: nil, output_file: nil, output_to_terminal: false)
|
102
|
+
return run_remote_winrm_command(remote_host, command, credentials, set_as_trusted_host: set_as_trusted_host) if transport == :winrm
|
103
|
+
command = 'powershell.exe ' unless command =~ /powershell/i
|
104
|
+
run_remote_command(remote_host, command, credentials, output_stream: output_stream, output_file: output_file, output_to_terminal: output_to_terminal)
|
105
|
+
end
|
106
|
+
|
107
|
+
def run_remote_command(remote_host, command, credentials, ssh_key: nil, output_stream: nil, output_file: nil, output_to_terminal: false, show_command_on_error: false)
|
108
|
+
require 'net/ssh'
|
109
|
+
start_params = if credentials['password'].nil?
|
110
|
+
{
|
111
|
+
host_key: 'ssh-rsa',
|
112
|
+
keys: [ssh_key],
|
113
|
+
verify_host_key: false,
|
114
|
+
}
|
115
|
+
else
|
116
|
+
{
|
117
|
+
password: credentials['password'],
|
118
|
+
}
|
119
|
+
end
|
120
|
+
|
121
|
+
retries ||= 3
|
122
|
+
return_code = nil
|
123
|
+
keep_stream_open = !output_stream.nil? # Leave the stream open if it was passed in
|
124
|
+
output_stream ||= ::File.open(output_file, ::File::RDWR | ::File::CREAT) unless output_file.nil?
|
125
|
+
stdout = ''
|
126
|
+
|
127
|
+
Net::SSH.start(remote_host, credentials['user'], **start_params) do |ssh|
|
128
|
+
command_thread = ssh.open_channel do |channel|
|
129
|
+
channel.exec(command) do |ch, success|
|
130
|
+
raise 'could not execute command' unless success
|
131
|
+
|
132
|
+
ch.on_data { |_c, data| stdout += data; process_command_output(data, output_stream, output_to_terminal) }
|
133
|
+
ch.on_extended_data { |_c, _type, data| stdout += data; process_command_output(data, output_stream, output_to_terminal) }
|
134
|
+
ch.on_request('exit-status') { |_ch, data| return_code = data.read_long }
|
135
|
+
end
|
136
|
+
end
|
137
|
+
command_thread.wait
|
138
|
+
end
|
139
|
+
{
|
140
|
+
'stdout' => stdout,
|
141
|
+
'exitcode' => return_code,
|
142
|
+
}
|
143
|
+
rescue Net::SSH::ConnectionTimeout => ex
|
144
|
+
EasyIO.logger.info 'Net::SSH::ConnectionTimeout - retrying...'
|
145
|
+
retry if (retries -= 1) >= 0
|
146
|
+
{
|
147
|
+
'exception' => ex,
|
148
|
+
'stderr' => (show_command_on_error ? "command: #{command}\n\n#{ex.message}" : ex.message) + "\n\n#{ex.backtrace}",
|
149
|
+
'exitcode' => return_code,
|
150
|
+
}
|
151
|
+
rescue => ex
|
152
|
+
{
|
153
|
+
'exception' => ex,
|
154
|
+
'stderr' => (show_command_on_error ? "command: #{command}\n\n#{ex.message}" : ex.message) + "\n\n#{ex.backtrace}",
|
155
|
+
'exitcode' => return_code,
|
156
|
+
}
|
157
|
+
ensure
|
158
|
+
output_stream.close unless keep_stream_open || !output_stream.respond_to?(:close)
|
159
|
+
end
|
160
|
+
|
161
|
+
def process_command_output(data, output_stream, output_to_terminal)
|
162
|
+
output_stream << data if output_stream
|
163
|
+
EasyIO.logger.info data if output_to_terminal
|
164
|
+
end
|
165
|
+
|
166
|
+
def run_command_on_remote_hosts(remote_hosts, command, credentials, command_message: nil, shell_type: nil, tail_count: nil, set_as_trusted_host: false, transport: :ssh)
|
100
167
|
tail_count ||= 1 # Return the last (1) line from each remote_host's log to the console
|
101
|
-
|
168
|
+
shell_type ||= OS.windows? ? :cmd : :bash
|
169
|
+
shell_type = shell_type.to_sym unless shell_type.is_a?(Symbol)
|
170
|
+
supported_shell_types = OS.windows? ? [:cmd, :powershell] : [:bash]
|
102
171
|
raise "Unsupported shell_type for running remote commands: '#{shell_type}'" unless supported_shell_types.include?(shell_type)
|
103
172
|
|
104
173
|
threads = {}
|
@@ -108,20 +177,28 @@ module EasyIO
|
|
108
177
|
EasyIO.logger.info "Output logs of processes run on the specified remote hosts will be placed in #{log_folder}..."
|
109
178
|
remote_hosts.each do |remote_host|
|
110
179
|
EasyIO.logger.info "Running `#{command_message || command}` on #{remote_host}..."
|
111
|
-
# threads_output[remote_host] = run_remote_powershell_command(remote_host, command, credentials, set_as_trusted_host: set_as_trusted_host)
|
112
180
|
threads[remote_host] = Thread.new do
|
113
|
-
|
181
|
+
case shell_type
|
182
|
+
when :powershell
|
183
|
+
threads_output[remote_host] = run_remote_powershell_command(remote_host, command, credentials, set_as_trusted_host: set_as_trusted_host, transport: transport)
|
184
|
+
when :cmd, :bash
|
185
|
+
threads_output[remote_host] = run_remote_command(remote_host, command, credentials)
|
186
|
+
end
|
114
187
|
end
|
115
188
|
end
|
116
189
|
threads.values.each(&:join) # Wait for all commands to complete
|
117
|
-
|
190
|
+
exceptions = []
|
118
191
|
threads_output.each do |remote_host, output|
|
119
192
|
::File.write("#{log_folder}/#{EasyFormat::File.windows_friendly_name(remote_host)}.#{::Time.now.strftime('%Y%m%d_%H%M%S')}.log", "#{output['stdout']}\n#{output['stderr']}")
|
120
193
|
tail_output = output['stdout'].nil? ? '--no standard output--' : output['stdout'].split("\n").last(tail_count).join("\n")
|
121
194
|
EasyIO.logger.info "[#{remote_host}]: #{tail_output}"
|
122
|
-
|
123
|
-
|
195
|
+
if output['exception']
|
196
|
+
exceptions.push "Failed to run command on #{remote_host}: #{output['stderr']}\n#{output['exception'].cause}\n#{output['exception'].message}"
|
197
|
+
next
|
198
|
+
end
|
199
|
+
exceptions.push "The script exited with exit code #{output['exitcode']}.\n\n#{output['stderr']}" unless output['exitcode'] == 0
|
124
200
|
end
|
201
|
+
raise exceptions.join("\n\n") unless exceptions.empty?
|
125
202
|
end
|
126
203
|
|
127
204
|
def add_as_winrm_trusted_host(remote_host)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: easy_io
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alex Munoz
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-07-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: easy_format
|
@@ -100,8 +100,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
100
100
|
- !ruby/object:Gem::Version
|
101
101
|
version: '0'
|
102
102
|
requirements: []
|
103
|
-
|
104
|
-
rubygems_version: 2.5.2.3
|
103
|
+
rubygems_version: 3.0.3
|
105
104
|
signing_key:
|
106
105
|
specification_version: 4
|
107
106
|
summary: Ruby library for ease of running commands with realtime output and return
|