Class: CommandJob
- Inherits:
-
Object
- Object
- CommandJob
- Includes:
- StandardModel
- Defined in:
- lib/app/models/command_job.rb
Overview
Base class for all jobs that will be run on builds
Constant Summary collapse
- STATE_NEW =
Constants
'new'- STATE_WIP =
'working'- STATE_RETRYING =
'retrying'- STATE_SUCCESS =
'success'- STATE_FAIL =
'failure'- STATE_CANCELLED =
'cancelled'- ALL_STATES =
[STATE_NEW, STATE_WIP, STATE_RETRYING, STATE_CANCELLED, STATE_SUCCESS, STATE_FAIL].freeze
Instance Method Summary collapse
-
#add_log(message) ⇒ Object
Add a job log message.
-
#cancelled? ⇒ Boolean
If we are cancelled.
-
#check_for_text(output, texts = [], inclusive = true) ⇒ Object
Check if any occurrences were found (or not found).
-
#completed? ⇒ Boolean
If we is finished, failed or success.
-
#copy_dir(dir, to_path) ⇒ Object
Copy a given directory to a new location and record the log.
-
#copy_file(from_path, to_path) ⇒ Object
Copy a given file to a new location and record the log.
-
#display_started_by ⇒ Object
Who started this job.
-
#download_file(file_url, file_path) ⇒ Object
Download a file to the given path.
-
#failed? ⇒ Boolean
True if in fail status.
- #failure_or_cancelled?(job = self) ⇒ Boolean
-
#incomplete? ⇒ Boolean
(also: #running?)
Job has not finished, failure or success.
- #job_cancelled?(job = self) ⇒ Boolean
-
#mask_keywords(output, keywords = []) ⇒ Object
Mask keywords if given in the command.
-
#mkdir(dir) ⇒ Object
(also: #make_dir)
Create a directory and record it.
-
#name ⇒ Object
Return the name of this job.
-
#new_job? ⇒ Boolean
True if in new status.
- #perform ⇒ Object
-
#remove_dir(dir_path) ⇒ Object
Remove the given file name.
-
#remove_file(file_path) ⇒ Object
Remove the given file name.
- #run ⇒ Object
-
#run! ⇒ Object
Determine the correct action to take and get it started.
-
#run_command(command, dir = '/tmp', options = {}) ⇒ Object
Run the command capturing the command output and any standard error to the log.
-
#sort_fields ⇒ Object
Which to sort by.
-
#status ⇒ Object
Return the job’s status and information in a hash that could be used to return to a calling api.
-
#succeeded? ⇒ Boolean
True if in success status.
-
#ttl ⇒ Object
Default time to keep a job before auto archiving it.
-
#unzip_file(file_path, to_dir) ⇒ Object
Unzip a given file.
-
#work_in_progress? ⇒ Boolean
True if in WIP status.
-
#write_file(path, contents) ⇒ Object
Write out the contents to the file.
Instance Method Details
#add_log(message) ⇒ Object
Add a job log message
304 305 306 |
# File 'lib/app/models/command_job.rb', line 304 def add_log() logs.create!(message: ) end |
#cancelled? ⇒ Boolean
If we are cancelled
108 109 110 |
# File 'lib/app/models/command_job.rb', line 108 def cancelled? STATE_CANCELLED.eql?(state) end |
#check_for_text(output, texts = [], inclusive = true) ⇒ Object
Check if any occurrences were found (or not found)
288 289 290 291 292 293 294 295 296 297 298 299 |
# File 'lib/app/models/command_job.rb', line 288 def check_for_text(output, texts = [], inclusive = true) return if texts.blank? texts = [texts] if texts.is_a?(String) texts.each do |text| if inclusive raise "Error: found text (#{text}) - #{output}" if output.match?(/#{text}/) else raise "Error: missing text (#{text}) - #{output}" unless output.match?(/#{text}/) end end end |
#completed? ⇒ Boolean
If we is finished, failed or success
92 93 94 |
# File 'lib/app/models/command_job.rb', line 92 def completed? [STATE_CANCELLED, STATE_FAIL, STATE_SUCCESS].include?(state) end |
#copy_dir(dir, to_path) ⇒ Object
Copy a given directory to a new location and record the log
211 212 213 214 |
# File 'lib/app/models/command_job.rb', line 211 def copy_dir(dir, to_path) FileUtils.cp_r dir, to_path add_log "Copy directory from: #{dir} to: #{to_path}" end |
#copy_file(from_path, to_path) ⇒ Object
Copy a given file to a new location and record the log
199 200 201 202 203 204 205 206 |
# File 'lib/app/models/command_job.rb', line 199 def copy_file(from_path, to_path) if File.exist? from_path FileUtils.cp(from_path, to_path) add_log "Copy file from: #{from_path} to: #{to_path}" else add_log "File not found: #{from_path}, copy not performed" end end |
#display_started_by ⇒ Object
Who started this job
43 44 45 |
# File 'lib/app/models/command_job.rb', line 43 def display_started_by started_by.present? ? started_by.display_name : 'System' end |
#download_file(file_url, file_path) ⇒ Object
Download a file to the given path
190 191 192 193 194 |
# File 'lib/app/models/command_job.rb', line 190 def download_file(file_url, file_path) download = URI.parse(file_url).open IO.copy_stream(download, file_path) add_log "Downloaded file: #{file_url} to #{file_path}" end |
#failed? ⇒ Boolean
True if in fail status
85 86 87 |
# File 'lib/app/models/command_job.rb', line 85 def failed? STATE_FAIL.eql?(state) end |
#failure_or_cancelled?(job = self) ⇒ Boolean
120 121 122 123 124 125 126 |
# File 'lib/app/models/command_job.rb', line 120 def failure_or_cancelled?(job = self) job.reload job.failed? || job.cancelled? rescue StandardError => error App47Logger.log_warn "Unable to check job cancelled #{job}", error true end |
#incomplete? ⇒ Boolean Also known as: running?
Job has not finished, failure or success
99 100 101 |
# File 'lib/app/models/command_job.rb', line 99 def incomplete? !completed? end |
#job_cancelled?(job = self) ⇒ Boolean
112 113 114 115 116 117 118 |
# File 'lib/app/models/command_job.rb', line 112 def job_cancelled?(job = self) job.reload job.cancelled? rescue StandardError => error App47Logger.log_warn "Unable to check job cancelled #{job}", error true end |
#mask_keywords(output, keywords = []) ⇒ Object
Mask keywords if given in the command
275 276 277 278 279 280 281 282 283 |
# File 'lib/app/models/command_job.rb', line 275 def mask_keywords(output, keywords = []) return output if keywords.blank? keywords = [keywords] if keywords.is_a?(String) keywords.each do |keyword| output = output.gsub(keyword, '***********') end output end |
#mkdir(dir) ⇒ Object Also known as: make_dir
Create a directory and record it
239 240 241 242 243 244 |
# File 'lib/app/models/command_job.rb', line 239 def mkdir(dir) return if File.exist?(dir) FileUtils.mkdir dir add_log "Created directory: #{dir}" end |
#name ⇒ Object
Return the name of this job
57 58 59 |
# File 'lib/app/models/command_job.rb', line 57 def name self.class.to_s.underscore.humanize end |
#new_job? ⇒ Boolean
True if in new status
64 65 66 |
# File 'lib/app/models/command_job.rb', line 64 def new_job? STATE_NEW.eql?(state) end |
#perform ⇒ Object
138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
# File 'lib/app/models/command_job.rb', line 138 def perform if failed? self.retries = 0 self. = 'Retrying' end begin save! run rescue StandardError => error log_error 'Unable to start job', error set state: STATE_FAIL, error_message: error. end end |
#remove_dir(dir_path) ⇒ Object
Remove the given file name
229 230 231 232 233 234 |
# File 'lib/app/models/command_job.rb', line 229 def remove_dir(dir_path) return unless File.exist?(dir_path) FileUtils.remove_dir dir_path add_log "Removing dir: #{dir_path}" end |
#remove_file(file_path) ⇒ Object
Remove the given file name
219 220 221 222 223 224 |
# File 'lib/app/models/command_job.rb', line 219 def remove_file(file_path) return unless File.exist?(file_path) FileUtils.remove_file file_path add_log "Removing file: #{file_path}" end |
#run ⇒ Object
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/app/models/command_job.rb', line 153 def run set state: STATE_WIP, started_at: Time.now.utc # Run the job run! set error_message: '', state: STATE_SUCCESS, finished_at: Time.now.utc rescue StandardError => error if retries >= max_retries log_error "Unable to run job id: #{id}, done retrying", error set state: STATE_FAIL, error_message: "Failed final attempt: #{error.}", finished_at: Time.now.utc else log_error "Unable to run job id: #{id}, retrying!!", error add_log "Unable to run job: #{error.}, retrying!!" set error_message: "Failed attempt # #{retries}: #{error.}", retries: retries + 1 delay(run_at: 10.seconds.from_now).run end end |
#run! ⇒ Object
Determine the correct action to take and get it started
175 176 177 |
# File 'lib/app/models/command_job.rb', line 175 def run! raise 'Incomplete class, concrete implementation should implement #run!' end |
#run_command(command, dir = '/tmp', options = {}) ⇒ Object
Run the command capturing the command output and any standard error to the log.
258 259 260 261 262 263 264 265 266 267 268 269 270 |
# File 'lib/app/models/command_job.rb', line 258 def run_command(command, dir = '/tmp', = {}) command = command.join(' ') if command.is_a?(Array) output = Tempfile.open('run-command-', '/tmp') do |f| Dir.chdir(dir) { `#{command} > #{f.path} 2>&1` } mask_keywords(f.open.read, [:mask_texts]) end output = 'Success' if output.blank? command = mask_keywords(command, [:mask_texts]) logs.create!(dir: dir, command: command, message: output) check_for_text(output, [:error_texts], true) check_for_text(output, [:required_texts], false) output end |
#sort_fields ⇒ Object
Which to sort by
311 312 313 |
# File 'lib/app/models/command_job.rb', line 311 def sort_fields %i[created_at] end |
#status ⇒ Object
Return the job’s status and information in a hash that could be used to return to a calling api
132 133 134 135 136 |
# File 'lib/app/models/command_job.rb', line 132 def status status = { state: state } status[:message] = if .present? status end |
#succeeded? ⇒ Boolean
True if in success status
78 79 80 |
# File 'lib/app/models/command_job.rb', line 78 def succeeded? STATE_SUCCESS.eql?(state) end |
#ttl ⇒ Object
Default time to keep a job before auto archiving it
50 51 52 |
# File 'lib/app/models/command_job.rb', line 50 def ttl 30 end |
#unzip_file(file_path, to_dir) ⇒ Object
Unzip a given file
251 252 253 |
# File 'lib/app/models/command_job.rb', line 251 def unzip_file(file_path, to_dir) run_command "unzip #{file_path}", to_dir, error_texts: 'unzip:' end |
#work_in_progress? ⇒ Boolean
True if in WIP status
71 72 73 |
# File 'lib/app/models/command_job.rb', line 71 def work_in_progress? [STATE_RETRYING, STATE_WIP].include?(state) end |
#write_file(path, contents) ⇒ Object
Write out the contents to the file
182 183 184 185 |
# File 'lib/app/models/command_job.rb', line 182 def write_file(path, contents) File.open(path, 'w') { |f| f.write(contents) } add_log "Saving:\n #{contents}\nto: #{path}" end |