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 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