raykit 0.0.471 → 0.0.472

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,343 +1,341 @@
1
- require "open3"
2
- require "timeout"
3
- require "json"
4
- require "yaml"
5
- require "logger"
6
- require "securerandom"
7
-
8
- BUFFER_SIZE = 1024 unless defined?(BUFFER_SIZE)
9
-
10
- module Raykit
11
- # Functionality to support executing and logging system commands
12
- class Command
13
- @@commands = []
14
- # The timeout in seconds, defaults to 0 if not specified
15
- attr_accessor :timeout
16
- # The working directory, defaults the current directory if not specified
17
- attr_accessor :directory
18
- # The start time
19
- attr_accessor :start_time
20
- # The execution time in seconds
21
- attr_accessor :elapsed
22
- attr_accessor :command, :output, :error, :exitstatus, :user, :machine, :logging_enabled, :success_log_level
23
-
24
- def init_defaults
25
- @timeout = 0
26
- @directory = Dir.pwd
27
- @output = +""
28
- @error = +""
29
- @exitstatus = 0
30
- @user = Environment.user
31
- @machine = Environment.machine
32
- @logging_enabled = true
33
- end
34
-
35
- def initialize(command)
36
- #def initialize(command, timeout = 0, success_log_level = Logger::DEBUG, logging_enabled = true)
37
- timeout = 0
38
- success_log_level = nil
39
- logging_enabled = false
40
- @@commands = []
41
- init_defaults
42
- @success_log_level = success_log_level
43
- @logging_enabled = logging_enabled
44
- @command = command
45
- @timeout = timeout
46
- #t = Time.now
47
- @elapsed = 0
48
- #run if @command.length.positive?
49
- self
50
- end
51
-
52
- def set_timeout(timeout)
53
- @timeout = timeout
54
- self
55
- end
56
-
57
- def run
58
- # puts '---running---'
59
- @start_time = Time.now
60
- @elapsed = 0
61
- timer = Timer.new
62
- if @timeout.zero?
63
- @output, @error, process_status = Open3.capture3(@command)
64
- @exitstatus = process_status.exitstatus
65
- else
66
- # =================================================
67
- #puts "@timeout is #{@timeout}"
68
- Open3.popen3(@command, chdir: @directory) do |_stdin, stdout, stderr, thread|
69
- tick = 1
70
- pid = thread.pid
71
- start = Time.now
72
- while ((Time.now - start) < @timeout) && thread.alive?
73
- Kernel.select([stdout, stderr], nil, nil, tick)
74
- begin
75
- @output << stdout.read_nonblock(BUFFER_SIZE)
76
- #@error << stderr.read_nonblock(BUFFER_SIZE)
77
- rescue IO::WaitReadable
78
- rescue EOFError
79
- #puts "rescue block entered"
80
- @exitstatus = thread.value.exitstatus
81
- until stdout.eof?
82
- @output << stdout.read_nonblock(BUFFER_SIZE)
83
- end
84
- until stderr.eof?
85
- @error << stderr.read_nonblock(BUFFER_SIZE)
86
- end
87
- break
88
- end
89
-
90
- #begin
91
- # @output << stdout.read_nonblock(BUFFER_SIZE)
92
- # @error << stderr.read_nonblock(BUFFER_SIZE)
93
- #rescue IO::WaitReadable
94
- #rescue EOFError
95
- # @exitstatus = thread.value.exitstatus
96
- # until stdout.eof?
97
- # @output << stdout.read_nonblock(BUFFER_SIZE)
98
- # end
99
- # until stderr.eof?
100
- # @error << stderr.read_nonblock(BUFFER_SIZE)
101
- # end
102
- # break
103
- #end
104
- end
105
- sleep 1
106
- if thread.alive?
107
- if Gem.win_platform?
108
- `taskkill /f /pid #{pid}`
109
- else
110
- Process.kill("TERM", pid)
111
- end
112
- @exitstatus = 5
113
- @error = +"timed out"
114
- else
115
- @exitstatus = thread.value.exitstatus
116
- end
117
- end
118
-
119
- # =================================================
120
- end
121
- @elapsed = timer.elapsed
122
- if @logging_enabled
123
- log
124
- if @exitstatus != 0
125
- to_log_event.to_seq
126
- else
127
- # puts '---logging---'
128
- unless @success_log_level.nil?
129
- e = to_log_event
130
- e["Level"] = "Verbose" if @success_log_level == "Verbose"
131
- e["Level"] = "Debug" if @success_log_level == Logger::DEBUG
132
- e["Level"] = "Information" if @success_log_level == Logger::INFO
133
- e["Level"] = "Warning" if @elapsed > 60 * 2
134
- e.to_seq
135
- end
136
- end
137
- end
138
- self
139
- end
140
-
141
- def to_log_event
142
- secrets = Secrets.new
143
- msg = secrets.hide(@command)
144
- level = "Verbose"
145
- level = "Warning" if @exitstatus != 0
146
- output = @output
147
- error = @error
148
- output = @output[-1000..-1] if @output.length > 1200
149
- error = @error[-1000..-1] if @error.length > 1200
150
- Raykit::LogEvent.new(level, msg, {
151
- "SourceContext" => "Raykit::Command",
152
- "Category" => "Command",
153
- "Timeout" => @timeout,
154
- "Directory" => @directory,
155
- "Output" => output,
156
- "Error" => error,
157
- "ExitStatus" => @exitstatus,
158
- "Elapsed" => elapsed_str,
159
- "ElapsedSeconds" => @elapsed,
160
- })
161
- end
162
-
163
- def log
164
- # --- Rolling File JSON -----
165
- log = Logger.new("#{Raykit::Environment.log_dir}/Raykit.Commands.txt", "daily")
166
- log.formatter = proc do |_severity, _datetime, _progname, msg|
167
- "#{msg}\n"
168
- end
169
- secrets = Secrets.new
170
- msg = secrets.hide(@command) # "?"# self.summary(false)
171
- level = "Verbose"
172
- level = "Warning" if @exitstatus != 0
173
- event = Raykit::LogEvent.new(level, msg, {
174
- "Timeout" => @timeout,
175
- "Directory" => @directory,
176
- "Output" => @output,
177
- "Error" => @error,
178
- "ExitStatus" => @exitstatus,
179
- "Elapsed" => elapsed_str,
180
- })
181
- log.info event.to_json
182
- # ---------------------------
183
- begin
184
- json = JSON.generate(to_hash)
185
- log_filename = "#{Environment.get_dev_dir("log")}/Raykit.Command/#{SecureRandom.uuid}.json"
186
- log_dir = File.dirname(log_filename)
187
- FileUtils.mkdir_p(log_dir) unless Dir.exist?(log_dir)
188
- File.open(log_filename, "w") { |f| f.write(json) }
189
- if !@exitstatus.nil? && @exitstatus.zero?
190
- LOG.log("Raykit.Command", Logger::Severity::INFO, json)
191
- else
192
- LOG.log("Raykit.Command", Logger::Severity::ERROR, json)
193
- end
194
- rescue JSON::GeneratorError => e
195
- puts to_hash.to_s
196
- puts e.to_s
197
- json = JSON.generate(to_hash)
198
- end
199
- end
200
-
201
- def elapsed_str
202
- if elapsed < 1.0
203
- end
204
- "#{format("%.0f", elapsed)}s"
205
- end
206
-
207
- def summary(show_directory = false)
208
- checkmark = "\u2713"
209
- # warning="\u26A0"
210
- error = "\u0058"
211
- symbol = Rainbow(checkmark.encode("utf-8")).green
212
- symbol = Rainbow(error.encode("utf-8")).red if @exitstatus != 0
213
- cmd = "#{Rainbow(SECRETS.hide(@command)).yellow}"
214
- if show_directory
215
- puts "#{symbol} #{cmd} " + Rainbow("#{elapsed_str}").cyan
216
- puts Rainbow(" #{@directory}").white + " "
217
- else
218
- puts "#{symbol} #{Rainbow(SECRETS.hide(@command)).yellow} " + Rainbow("#{elapsed_str}").cyan
219
- #puts "#{symbol} #{Rainbow(SECRETS.hide(@command)).yellow} (#{elapsed_str})"
220
- end
221
- self
222
- end
223
-
224
- def details_on_failure
225
- if @exitstatus != 0
226
- details
227
- end
228
- self
229
- end
230
-
231
- def details
232
- #puts " exit_code: " + @exitstatus.to_s
233
- summary
234
- if @output.length > 0
235
- @output.lines.each do |line|
236
- puts " " + line
237
- end
238
- end
239
- if @error.length > 0
240
- @error.lines.each do |line|
241
- puts " " + line
242
- end
243
- end
244
- self
245
- end
246
-
247
- def to_markdown
248
- checkmark = "\u2713"
249
- error = "\u0058"
250
- symbol = checkmark.encode("utf-8")
251
- symbol = error.encode("utf-8") if @exitstatus != 0
252
- cmd = "#{SECRETS.hide(@command)}"
253
- md = "#{symbol} #{SECRETS.hide(@command)} (#{elapsed_str})"
254
- md
255
- end
256
-
257
- def save
258
- filename = "#{Environment.get_dev_dir("log")}/Commands/#{SecureRandom.uuid}"
259
- log_dir = File.dirname(filename)
260
- FileUtils.mkdir_p(log_dir) unless Dir.exist?(log_dir)
261
-
262
- File.open(filename, "w") do |f|
263
- f.write(JSON.pretty_generate(to_hash))
264
- end
265
- self
266
- end
267
-
268
- def save_as(filename)
269
- File.open(filename, "w") do |f|
270
- f.write(JSON.pretty_generate(to_hash))
271
- end
272
- self
273
- end
274
-
275
- def log_to_file(filename)
276
- File.delete(filename) if File.exist?(filename)
277
- File.open(filename, "w") { |f|
278
- f.puts output
279
- f.puts error
280
- }
281
- self
282
- end
283
-
284
- def to_hash
285
- hash = {}
286
- hash[:command] = @command
287
- hash[:directory] = @directory
288
- hash[:timeout] = @timeout
289
- hash[:start_time] = @start_time
290
- hash[:elapsed] = @elapsed
291
- hash[:output] = @output.force_encoding("ISO-8859-1").encode("UTF-8")
292
- hash[:error] = @error.force_encoding("ISO-8859-1").encode("UTF-8")
293
- hash[:exitstatus] = @exitstatus
294
- hash[:user] = @user
295
- hash[:machine] = @machine
296
- hash[:context] = "Raykit.Command"
297
- hash
298
- end
299
-
300
- def from_hash(hash)
301
- @command = hash["command"]
302
- @directory = hash["directory"]
303
- @timeout = hash["timeout"]
304
- @start_time = hash["start_time"]
305
- @elapsed = hash["elapsed"]
306
- @output = hash["output"]
307
- @error = hash["error"]
308
- @exitstatus = hash["exitstatus"]
309
- @user = hash["user"] if hash.include?("user")
310
- @machine = hash["machine"] if hash.include?("machine")
311
- end
312
-
313
- def self.parse(json)
314
- cmd = Command.new("")
315
- cmd.from_hash(JSON.parse(json))
316
- cmd
317
- end
318
-
319
- def self.parse_yaml_commands(yaml)
320
- # commands=Array.new()
321
- data = YAML.safe_load(yaml)
322
- commands = get_script_commands(data)
323
- end
324
-
325
- def self.get_script_commands(hash)
326
- commands = []
327
- if hash.key?("script")
328
- hash["script"].each do |cmd|
329
- commands << cmd
330
- end
331
- end
332
- hash.each do |_k, v|
333
- next unless v.is_a?(Hash)
334
-
335
- subcommands = get_script_commands(v)
336
- subcommands.each do |c|
337
- commands << c
338
- end
339
- end
340
- commands
341
- end
342
- end
343
- end
1
+ require "open3"
2
+ require "timeout"
3
+ require "json"
4
+ require "yaml"
5
+ require "logger"
6
+ require "securerandom"
7
+
8
+ module Raykit
9
+ # Functionality to support executing and logging system commands
10
+ class Command
11
+ @@commands = []
12
+ # The timeout in seconds, defaults to 0 if not specified
13
+ attr_accessor :timeout
14
+ # The working directory, defaults the current directory if not specified
15
+ attr_accessor :directory
16
+ # The start time
17
+ attr_accessor :start_time
18
+ # The execution time in seconds
19
+ attr_accessor :elapsed
20
+ attr_accessor :command, :output, :error, :exitstatus, :user, :machine, :logging_enabled, :success_log_level
21
+
22
+ def init_defaults
23
+ @timeout = 0
24
+ @directory = Dir.pwd
25
+ @output = +""
26
+ @error = +""
27
+ @exitstatus = 0
28
+ @user = Environment.user
29
+ @machine = Environment.machine
30
+ @logging_enabled = true
31
+ end
32
+
33
+ def initialize(command)
34
+ #def initialize(command, timeout = 0, success_log_level = Logger::DEBUG, logging_enabled = true)
35
+ timeout = 0
36
+ success_log_level = nil
37
+ logging_enabled = false
38
+ @@commands = []
39
+ init_defaults
40
+ @success_log_level = success_log_level
41
+ @logging_enabled = logging_enabled
42
+ @command = command
43
+ @timeout = timeout
44
+ #t = Time.now
45
+ @elapsed = 0
46
+ #run if @command.length.positive?
47
+ self
48
+ end
49
+
50
+ def set_timeout(timeout)
51
+ @timeout = timeout
52
+ self
53
+ end
54
+
55
+ def run
56
+ # puts '---running---'
57
+ @start_time = Time.now
58
+ @elapsed = 0
59
+ timer = Timer.new
60
+ if @timeout.zero?
61
+ @output, @error, process_status = Open3.capture3(@command)
62
+ @exitstatus = process_status.exitstatus
63
+ else
64
+ # =================================================
65
+ #puts "@timeout is #{@timeout}"
66
+ Open3.popen3(@command, chdir: @directory) do |_stdin, stdout, stderr, thread|
67
+ tick = 1
68
+ pid = thread.pid
69
+ start = Time.now
70
+ while ((Time.now - start) < @timeout) && thread.alive?
71
+ Kernel.select([stdout, stderr], nil, nil, tick)
72
+ begin
73
+ @output << stdout.read_nonblock(BUFFER_SIZE)
74
+ #@error << stderr.read_nonblock(BUFFER_SIZE)
75
+ rescue IO::WaitReadable
76
+ rescue EOFError
77
+ #puts "rescue block entered"
78
+ @exitstatus = thread.value.exitstatus
79
+ until stdout.eof?
80
+ @output << stdout.read_nonblock(BUFFER_SIZE)
81
+ end
82
+ until stderr.eof?
83
+ @error << stderr.read_nonblock(BUFFER_SIZE)
84
+ end
85
+ break
86
+ end
87
+
88
+ #begin
89
+ # @output << stdout.read_nonblock(BUFFER_SIZE)
90
+ # @error << stderr.read_nonblock(BUFFER_SIZE)
91
+ #rescue IO::WaitReadable
92
+ #rescue EOFError
93
+ # @exitstatus = thread.value.exitstatus
94
+ # until stdout.eof?
95
+ # @output << stdout.read_nonblock(BUFFER_SIZE)
96
+ # end
97
+ # until stderr.eof?
98
+ # @error << stderr.read_nonblock(BUFFER_SIZE)
99
+ # end
100
+ # break
101
+ #end
102
+ end
103
+ sleep 1
104
+ if thread.alive?
105
+ if Gem.win_platform?
106
+ `taskkill /f /pid #{pid}`
107
+ else
108
+ Process.kill("TERM", pid)
109
+ end
110
+ @exitstatus = 5
111
+ @error = +"timed out"
112
+ else
113
+ @exitstatus = thread.value.exitstatus
114
+ end
115
+ end
116
+
117
+ # =================================================
118
+ end
119
+ @elapsed = timer.elapsed
120
+ if @logging_enabled
121
+ log
122
+ if @exitstatus != 0
123
+ to_log_event.to_seq
124
+ else
125
+ # puts '---logging---'
126
+ unless @success_log_level.nil?
127
+ e = to_log_event
128
+ e["Level"] = "Verbose" if @success_log_level == "Verbose"
129
+ e["Level"] = "Debug" if @success_log_level == Logger::DEBUG
130
+ e["Level"] = "Information" if @success_log_level == Logger::INFO
131
+ e["Level"] = "Warning" if @elapsed > 60 * 2
132
+ e.to_seq
133
+ end
134
+ end
135
+ end
136
+ self
137
+ end
138
+
139
+ def to_log_event
140
+ secrets = Secrets.new
141
+ msg = secrets.hide(@command)
142
+ level = "Verbose"
143
+ level = "Warning" if @exitstatus != 0
144
+ output = @output
145
+ error = @error
146
+ output = @output[-1000..-1] if @output.length > 1200
147
+ error = @error[-1000..-1] if @error.length > 1200
148
+ Raykit::LogEvent.new(level, msg, {
149
+ "SourceContext" => "Raykit::Command",
150
+ "Category" => "Command",
151
+ "Timeout" => @timeout,
152
+ "Directory" => @directory,
153
+ "Output" => output,
154
+ "Error" => error,
155
+ "ExitStatus" => @exitstatus,
156
+ "Elapsed" => elapsed_str,
157
+ "ElapsedSeconds" => @elapsed,
158
+ })
159
+ end
160
+
161
+ def log
162
+ # --- Rolling File JSON -----
163
+ log = Logger.new("#{Raykit::Environment.log_dir}/Raykit.Commands.txt", "daily")
164
+ log.formatter = proc do |_severity, _datetime, _progname, msg|
165
+ "#{msg}\n"
166
+ end
167
+ secrets = Secrets.new
168
+ msg = secrets.hide(@command) # "?"# self.summary(false)
169
+ level = "Verbose"
170
+ level = "Warning" if @exitstatus != 0
171
+ event = Raykit::LogEvent.new(level, msg, {
172
+ "Timeout" => @timeout,
173
+ "Directory" => @directory,
174
+ "Output" => @output,
175
+ "Error" => @error,
176
+ "ExitStatus" => @exitstatus,
177
+ "Elapsed" => elapsed_str,
178
+ })
179
+ log.info event.to_json
180
+ # ---------------------------
181
+ begin
182
+ json = JSON.generate(to_hash)
183
+ log_filename = "#{Environment.get_dev_dir("log")}/Raykit.Command/#{SecureRandom.uuid}.json"
184
+ log_dir = File.dirname(log_filename)
185
+ FileUtils.mkdir_p(log_dir) unless Dir.exist?(log_dir)
186
+ File.open(log_filename, "w") { |f| f.write(json) }
187
+ if !@exitstatus.nil? && @exitstatus.zero?
188
+ LOG.log("Raykit.Command", Logger::Severity::INFO, json)
189
+ else
190
+ LOG.log("Raykit.Command", Logger::Severity::ERROR, json)
191
+ end
192
+ rescue JSON::GeneratorError => e
193
+ puts to_hash.to_s
194
+ puts e.to_s
195
+ json = JSON.generate(to_hash)
196
+ end
197
+ end
198
+
199
+ def elapsed_str
200
+ if elapsed < 1.0
201
+ end
202
+ "#{format("%.0f", elapsed)}s"
203
+ end
204
+
205
+ def summary(show_directory = false)
206
+ checkmark = "\u2713"
207
+ # warning="\u26A0"
208
+ error = "\u0058"
209
+ symbol = Rainbow(checkmark.encode("utf-8")).green
210
+ symbol = Rainbow(error.encode("utf-8")).red if @exitstatus != 0
211
+ cmd = "#{Rainbow(SECRETS.hide(@command)).yellow}"
212
+ if show_directory
213
+ puts "#{symbol} #{cmd} " + Rainbow("#{elapsed_str}").cyan
214
+ puts Rainbow(" #{@directory}").white + " "
215
+ else
216
+ puts "#{symbol} #{Rainbow(SECRETS.hide(@command)).yellow} " + Rainbow("#{elapsed_str}").cyan
217
+ #puts "#{symbol} #{Rainbow(SECRETS.hide(@command)).yellow} (#{elapsed_str})"
218
+ end
219
+ self
220
+ end
221
+
222
+ def details_on_failure
223
+ if @exitstatus != 0
224
+ details
225
+ end
226
+ self
227
+ end
228
+
229
+ def details
230
+ #puts " exit_code: " + @exitstatus.to_s
231
+ summary
232
+ if @output.length > 0
233
+ @output.lines.each do |line|
234
+ puts " " + line
235
+ end
236
+ end
237
+ if @error.length > 0
238
+ @error.lines.each do |line|
239
+ puts " " + line
240
+ end
241
+ end
242
+ self
243
+ end
244
+
245
+ def to_markdown
246
+ checkmark = "\u2713"
247
+ error = "\u0058"
248
+ symbol = checkmark.encode("utf-8")
249
+ symbol = error.encode("utf-8") if @exitstatus != 0
250
+ cmd = "#{SECRETS.hide(@command)}"
251
+ md = "#{symbol} #{SECRETS.hide(@command)} (#{elapsed_str})"
252
+ md
253
+ end
254
+
255
+ def save
256
+ filename = "#{Environment.get_dev_dir("log")}/Commands/#{SecureRandom.uuid}"
257
+ log_dir = File.dirname(filename)
258
+ FileUtils.mkdir_p(log_dir) unless Dir.exist?(log_dir)
259
+
260
+ File.open(filename, "w") do |f|
261
+ f.write(JSON.pretty_generate(to_hash))
262
+ end
263
+ self
264
+ end
265
+
266
+ def save_as(filename)
267
+ File.open(filename, "w") do |f|
268
+ f.write(JSON.pretty_generate(to_hash))
269
+ end
270
+ self
271
+ end
272
+
273
+ def log_to_file(filename)
274
+ File.delete(filename) if File.exist?(filename)
275
+ File.open(filename, "w") { |f|
276
+ f.puts output
277
+ f.puts error
278
+ }
279
+ self
280
+ end
281
+
282
+ def to_hash
283
+ hash = {}
284
+ hash[:command] = @command
285
+ hash[:directory] = @directory
286
+ hash[:timeout] = @timeout
287
+ hash[:start_time] = @start_time
288
+ hash[:elapsed] = @elapsed
289
+ hash[:output] = @output.force_encoding("ISO-8859-1").encode("UTF-8")
290
+ hash[:error] = @error.force_encoding("ISO-8859-1").encode("UTF-8")
291
+ hash[:exitstatus] = @exitstatus
292
+ hash[:user] = @user
293
+ hash[:machine] = @machine
294
+ hash[:context] = "Raykit.Command"
295
+ hash
296
+ end
297
+
298
+ def from_hash(hash)
299
+ @command = hash["command"]
300
+ @directory = hash["directory"]
301
+ @timeout = hash["timeout"]
302
+ @start_time = hash["start_time"]
303
+ @elapsed = hash["elapsed"]
304
+ @output = hash["output"]
305
+ @error = hash["error"]
306
+ @exitstatus = hash["exitstatus"]
307
+ @user = hash["user"] if hash.include?("user")
308
+ @machine = hash["machine"] if hash.include?("machine")
309
+ end
310
+
311
+ def self.parse(json)
312
+ cmd = Command.new("")
313
+ cmd.from_hash(JSON.parse(json))
314
+ cmd
315
+ end
316
+
317
+ def self.parse_yaml_commands(yaml)
318
+ # commands=Array.new()
319
+ data = YAML.safe_load(yaml)
320
+ commands = get_script_commands(data)
321
+ end
322
+
323
+ def self.get_script_commands(hash)
324
+ commands = []
325
+ if hash.key?("script")
326
+ hash["script"].each do |cmd|
327
+ commands << cmd
328
+ end
329
+ end
330
+ hash.each do |_k, v|
331
+ next unless v.is_a?(Hash)
332
+
333
+ subcommands = get_script_commands(v)
334
+ subcommands.each do |c|
335
+ commands << c
336
+ end
337
+ end
338
+ commands
339
+ end
340
+ end
341
+ end