Module: PgVerify::Core::CMDRunner

Defined in:
lib/pg-verify/core/cmd_runner.rb

Defined Under Namespace

Classes: CMDRunnerError

Class Method Summary collapse

Class Method Details

.command_exists?(command) ⇒ Boolean

Returns:

  • (Boolean)


130
131
132
# File 'lib/pg-verify/core/cmd_runner.rb', line 130

def self.command_exists?(command)
    !run_or_return("command -v '#{command}'", nil).nil?
end

.drop_into_shellObject



40
41
42
43
# File 'lib/pg-verify/core/cmd_runner.rb', line 40

def self.drop_into_shell()
    shell = run_cmd("echo ${SHELL}")
    system("#{shell}")
end

.run_and_follow(cmd, raise_on_fail: true, gulp_interrupt: false, timeout: nil, &blk) ⇒ Object

Runs the specified command and waits for it to complete. Calls the specified block for each line the process writes to stdout and err. Will return two things:

1. The complete output as a string
2. The result as a bool, where true means success.

If raise_on_fail is set to true it will raise directly on fail and not return the results. If gulp_interrupt is set to true will also gulp interrupts.



93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/pg-verify/core/cmd_runner.rb', line 93

def self.run_and_follow(cmd, raise_on_fail: true, gulp_interrupt: false, timeout: nil, &blk)
    start_time = Time.new
    output = ""
    success = nil
    
    Open3.popen3(cmd) do |stdin, stdout, stderr, wait_thread|
        begin
            # Listen to stdout & stderr in seperate threads.
            [stdout, stderr].each do |stream|
                Thread.new do
                    begin
                        until (line = stream.gets).nil? do
                            blk.call(line) unless blk.nil?
                            output += line
                        end
                    rescue IOError => e
                        # Expected when the stream is closed
                    end
                end
            end
            unless timeout.nil?
                require 'timeout'
                Timeout.timeout(timeout) { wait_thread.join }
            else
                wait_thread.join
            end
            delta_seconds = Time.new - start_time
            success = wait_thread.value.success?
            raise CMDRunnerError.new(cmd, output, "", delta_seconds) if raise_on_fail && !success
        rescue SignalException => e
            raise e unless gulp_interrupt
            success = false
        end
        return output, success
    end
end

.run_cmd(cmd, env_variables = {}, include_stderr: false) ⇒ Object

Runs the command and raises on error.

Raises:



8
9
10
11
12
13
14
15
# File 'lib/pg-verify/core/cmd_runner.rb', line 8

def self.run_cmd(cmd, env_variables = {}, include_stderr: false)
    start_time = Time.new
    output, err, status = Open3.capture3(env_variables, cmd)
    delta_seconds = (Time.new - start_time).to_i
    raise CMDRunnerError.new(cmd, output, err, delta_seconds) unless status.success?
    return output + err if include_stderr
    return output
end

.run_for_exit_code(cmd) ⇒ Object



35
36
37
38
# File 'lib/pg-verify/core/cmd_runner.rb', line 35

def self.run_for_exit_code(cmd)
    output, err, status = Open3.capture3(cmd)
    return status.exitstatus
end

.run_for_result(cmd) ⇒ Object

Runs the command and returns stdout, stderr and the status. Status will be true only if the command succeeded.



25
26
27
28
# File 'lib/pg-verify/core/cmd_runner.rb', line 25

def self.run_for_result(cmd)
    output, err, status = run_for_result_with_plain_status(cmd)
    return output, err, status.success?
end

.run_for_result_with_plain_status(cmd) ⇒ Object



30
31
32
33
# File 'lib/pg-verify/core/cmd_runner.rb', line 30

def self.run_for_result_with_plain_status(cmd)
    output, err, status = Open3.capture3(cmd)
    return output, err, status
end

.run_in_screen(cmd, session_name) ⇒ Object



45
46
47
# File 'lib/pg-verify/core/cmd_runner.rb', line 45

def self.run_in_screen(cmd, session_name)
    run_cmd("screen -S #{session_name} -dm bash -c '#{cmd}'")
end

.run_or_return(cmd, fail_output = nil) ⇒ Object

Runs the command and returns stdout on success. Returns the specified value otherwise.



18
19
20
21
# File 'lib/pg-verify/core/cmd_runner.rb', line 18

def self.run_or_return(cmd, fail_output = nil)
    output, err, status = Open3.capture3(cmd)
    return status.success? ? output : fail_output
end

.run_with_timeout(cmd) ⇒ Object



49
50
51
52
53
54
55
56
# File 'lib/pg-verify/core/cmd_runner.rb', line 49

def self.run_with_timeout(cmd)
    require 'timeout'
    Open3.popen3(cmd) do |stdin, stdout, stderr, wait_thread|
        Timeout.timeout(2) do
            wait_thread.join
        end
    end
end

.with_drop_on_fail(shell, intent: nil, &blk) ⇒ Object



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
# File 'lib/pg-verify/core/cmd_runner.rb', line 58

def self.with_drop_on_fail(shell, intent: nil, &blk)
    begin
        blk.call()
    rescue CMDRunnerError => e
        shell.info("Output:\n#{e.output}")
        shell.error("The action that was attempted did fail after #{e.delta_seconds} seconds!")
        shell.info("The exact command was: #{e.command.c_command}")
        shell.info("Intent was: #{intent}") unless intent.nil?
        shell.info("You can try to resolve this problem manually.")
        raise e unless shell.ask_confirm(nil, question: "Do you want to open a shell and try our luck?")
        drop_into_shell()
        shell.info("Welcome back :D")
    
        op1 = "Run the command again to see if it works."
        op2 = "Just continue and pretend the command did succeed.\n(This will return '' to the caller)"
        op3 = "Assume the command failed."
        sel = shell.select([op1, op2, op3], prompt: "How shall we continue?").first
        case sel
        when op1
            return with_drop_on_fail(shell, blk, intent: intent,)
        when op2
            return ""
        when op3
            raise e
        end
    end
end