raykit 0.0.467 → 0.0.468

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,343 +1,343 @@
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
+ 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