summaryrefslogtreecommitdiff
path: root/qa/qa/service/shellout.rb
blob: 9b93297f6ffcf2cadefe143d698f7ad963a27be2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
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
# frozen_string_literal: true

require 'open3'

module QA
  module Service
    module Shellout
      using Rainbow

      CommandError = Class.new(StandardError)

      module_function

      def shell(command, stdin_data: nil, fail_on_exception: true, stream_progress: true, mask_secrets: []) # rubocop:disable Metrics/CyclomaticComplexity
        cmd_string = Array(command).join(' ')

        QA::Runtime::Logger.info("Executing: `#{mask_secrets_on_string(cmd_string, mask_secrets).cyan}`")

        Open3.popen2e(*command) do |stdin, out, wait|
          stdin.puts(stdin_data) if stdin_data
          stdin.close if stdin_data

          print_progress_dots = stream_progress && !Runtime::Env.running_in_ci?
          cmd_output = ''

          out.each do |line|
            line = mask_secrets_on_string(line, mask_secrets)

            cmd_output += line
            yield line if block_given?

            # indicate progress for local run by printing dots
            print "." if print_progress_dots
          end

          # add newline after progress dots
          puts if print_progress_dots && !cmd_output.empty?

          if wait.value.exited? && wait.value.exitstatus.nonzero? && fail_on_exception
            Runtime::Logger.error("Command output:\n#{cmd_output.strip}") unless cmd_output.empty?
            raise CommandError, "Command: `#{mask_secrets_on_string(cmd_string, mask_secrets)}` failed! ✘"
          end

          Runtime::Logger.debug("Command output:\n#{cmd_output.strip}") unless cmd_output.empty?
        end
      end

      def sql_to_docker_exec_cmd(sql, username, password, database, host, container)
        <<~CMD
          docker exec --env PGPASSWORD=#{password} #{container} \
            bash -c "psql -U #{username} -d #{database} -h #{host} -c \\"#{sql}\\""
        CMD
      end

      def wait_until_shell_command(cmd, **kwargs)
        sleep_interval = kwargs.delete(:sleep_interval) || 1
        stream_progress = kwargs.delete(:stream_progress).then { |arg| arg.nil? ? true : false }

        Support::Waiter.wait_until(sleep_interval: sleep_interval, **kwargs) do
          shell(cmd, stream_progress: stream_progress) do |line|
            break true if yield line
          end
        end
      end

      def wait_until_shell_command_matches(cmd, regex, **kwargs)
        wait_until_shell_command(cmd, stream_progress: false, **kwargs) do |line|
          line =~ regex
        end
      end

      def mask_secrets_on_string(str, secrets)
        secrets.reduce(str) { |s, secret| s.gsub(secret, '****') }
      end
    end
  end
end