Class: CC::Analyzer::Container
- Inherits:
-
Object
- Object
- CC::Analyzer::Container
- Defined in:
- lib/cc/analyzer/container.rb,
lib/cc/analyzer/container/result.rb
Overview
Running an abstract docker container
Input:
- image
- name
- command (Optional)
Output:
- Result
- exit_status
- timed_out?
- duration
- maximum_output_exceeded?
- output_byte_count
- stderr
Never raises (unless broken)
Defined Under Namespace
Classes: Result
Constant Summary collapse
- DEFAULT_TIMEOUT =
15m
15 * 60
- DEFAULT_MAXIMUM_OUTPUT_BYTES =
500_000_000
Instance Method Summary collapse
-
#initialize(image:, name:, command: nil) ⇒ Container
constructor
A new instance of Container.
- #on_output(delimeter = "\n", &block) ⇒ Object
- #run(options = []) ⇒ Object
- #stop(message = nil) ⇒ Object
Constructor Details
#initialize(image:, name:, command: nil) ⇒ Container
Returns a new instance of Container.
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
# File 'lib/cc/analyzer/container.rb', line 31 def initialize(image:, name:, command: nil) @image = image @name = name @command = command @timed_out = false @maximum_output_exceeded = false @stdout_io = StringIO.new @stderr_io = StringIO.new @output_byte_count = 0 @counter_mutex = Mutex.new # By default accumulate and include stdout in result @output_delimeter = "\n" @on_output = ->(output) { @stdout_io.puts(output) } end |
Instance Method Details
#on_output(delimeter = "\n", &block) ⇒ Object
47 48 49 50 |
# File 'lib/cc/analyzer/container.rb', line 47 def on_output(delimeter = "\n", &block) @output_delimeter = delimeter @on_output = block end |
#run(options = []) ⇒ Object
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/cc/analyzer/container.rb', line 52 def run( = []) started = Time.now command = docker_run_command() Analyzer.logger.debug("docker run: #{command.inspect}") pid, _, out, err = POSIX::Spawn.popen4(*command) @t_out = read_stdout(out) @t_err = read_stderr(err) t_timeout = timeout_thread # blocks until the engine stops. this is put in a thread so that we can # explicitly abort it as part of #stop. otherwise a run-away container # could still block here forever if the docker-kill/wait is not # successful. there may still be stdout in flight if it was being # produced more quickly than consumed. @t_wait = Thread.new { _, @status = Process.waitpid2(pid) } @t_wait.join # blocks until all readers are done. they're still governed by the # timeout thread at this point. if we hit the timeout while processing # output, the threads will be Thread#killed as part of #stop and this # will unblock with the correct value in @timed_out [@t_out, @t_err].each(&:join) duration = if @timed_out timeout * 1000 else ((Time.now - started) * 1000).round end Result.new( container_name: @name, duration: duration, exit_status: @status&.exitstatus, maximum_output_exceeded: @maximum_output_exceeded, output_byte_count: output_byte_count, stderr: @stderr_io.string, stdout: @stdout_io.string, timed_out: @timed_out, ) ensure kill_reader_threads t_timeout&.kill end |
#stop(message = nil) ⇒ Object
99 100 101 102 103 |
# File 'lib/cc/analyzer/container.rb', line 99 def stop( = nil) reap_running_container() kill_reader_threads kill_wait_thread end |