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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: f4995afc12ae9df92f39bab54a14a07745c319b1
4
- data.tar.gz: ee3b92492666e222aa9895eaace81c96c238acb1
2
+ SHA256:
3
+ metadata.gz: 2b4ea31cfbbe102e2b9d4e14097c33da21c70e4de3b1a6b599c872503fd37741
4
+ data.tar.gz: d1df2063b2e92d513a37a9c14a0749c30586eb0f3fc7729100ddf7bd8add3b4b
5
5
  SHA512:
6
- metadata.gz: 52189de578634351266752e2b67fa3c70ab71ff0713afa9ee9d4385fadc0542444e7d317b9ba2562e9ba583b5d0e64a32ffd4f13a6ec530e18df99a0c6789d4f
7
- data.tar.gz: 5d5087dfbbc6c7e64019c38cce980d9f09011ac8fd99fbee01866c995d2b0be0626d37ecd89fbc866274209495894f65599eb41bd1cc84c0499fb704e14566b1
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 run_remote_powershell_command(remote_host, command, credentials, set_as_trusted_host: false)
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 run_command_on_remote_hosts(remote_hosts, command, credentials, command_message: nil, shell_type: :cmd, tail_count: nil, set_as_trusted_host: false)
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
- supported_shell_types = [:cmd, :powershell] # TODO: implement shell_type :bash
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
- threads_output[remote_host] = run_remote_powershell_command(remote_host, command, credentials, set_as_trusted_host: set_as_trusted_host)
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
- # threads.each { |remote_host, thread| pp thread }
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
- raise "Failed to run command on #{remote_host}: #{output['stderr']}\n#{output['exception'].cause}\n#{output['exception'].message}" if output['exception']
123
- raise "The script exited with exit code #{output['exitcode']}.\n\n#{output['stderr']}" unless output['exitcode'] == 0
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.2
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: 2020-05-27 00:00:00.000000000 Z
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
- rubyforge_project:
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