easy_io 0.4.2 → 0.4.3
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.
- 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
|