Class: Pipes::SystemPipe

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Defined in:
lib/system_pipe.rb

Direct Known Subclasses

FakePipe, SshPipe

Defined Under Namespace

Classes: ReturnCodeException

Constant Summary collapse

NUMBER_ONLY_REGEX =
/^(\d*(\.\d)?\d*)$/

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(initial_command) ⇒ SystemPipe

Returns a new instance of SystemPipe.



23
24
25
26
27
# File 'lib/system_pipe.rb', line 23

def initialize(initial_command)
  @initial_command = initial_command
  @abandoned_pipes_count = 0
  start_pipe
end

Instance Attribute Details

#abandoned_pipes_countObject (readonly)

Returns the value of attribute abandoned_pipes_count.



20
21
22
# File 'lib/system_pipe.rb', line 20

def abandoned_pipes_count
  @abandoned_pipes_count
end

#initial_commandObject (readonly)

Returns the value of attribute initial_command.



20
21
22
# File 'lib/system_pipe.rb', line 20

def initial_command
  @initial_command
end

#pipeObject (readonly)

Returns the value of attribute pipe.



20
21
22
# File 'lib/system_pipe.rb', line 20

def pipe
  @pipe
end

Instance Method Details

#backup_file(source) ⇒ Object



148
149
150
151
# File 'lib/system_pipe.rb', line 148

def backup_file(source)
  destination = "#{source}.bak_#{backup_timestamp}"
  cp(source, destination)
end

#backup_timestampObject



153
154
155
# File 'lib/system_pipe.rb', line 153

def backup_timestamp
  current_time.utc.strftime("%Y%m%d_%H%M%S")
end

#close_with_timeout(timeout = PIPE_CLOSE_TIMEOUT) ⇒ Object



44
45
46
47
48
49
50
51
52
53
54
# File 'lib/system_pipe.rb', line 44

def close_with_timeout(timeout = PIPE_CLOSE_TIMEOUT)
  begin
    Timeout::timeout(timeout) do
      close unless closed?
    end
  rescue Timeout::Error, Errno::EPIPE => exception
    @abandoned_pipes_count += 1
    Kernel.puts " Total Abandoned Pipes: #{abandoned_pipes_count}"
    Kernel.puts " Exception during unsafe_close: #{exception.message}"
  end
end

#cp(source, destination) ⇒ Object



143
144
145
146
# File 'lib/system_pipe.rb', line 143

def cp(source, destination)
  copy_file_command = "cp #{source} #{destination} 2>&1"
  run_command_and_ensure_return_code(copy_file_command)
end

#current_timeObject



157
158
159
# File 'lib/system_pipe.rb', line 157

def current_time
  Time.now
end

#ensure_startedObject



34
35
36
# File 'lib/system_pipe.rb', line 34

def ensure_started
  run_command_and_ensure_return_code("whoami")
end

#extract_number_from_string(response) ⇒ Object



63
64
65
66
# File 'lib/system_pipe.rb', line 63

def extract_number_from_string(response)
  match = response.match(NUMBER_ONLY_REGEX) unless response.empty?
  match ? match.captures.first.to_f : nil
end

#flush_until(expected) ⇒ Object



134
135
136
137
138
139
140
141
# File 'lib/system_pipe.rb', line 134

def flush_until(expected)
  output = ""
  until output.match(expected)
    output = readline
    yield output if block_given?
  end
  output.chomp
end

#follow_file(file_name) ⇒ Object



108
109
110
111
112
113
114
115
116
# File 'lib/system_pipe.rb', line 108

def follow_file(file_name)
  trigger = "FINISHED LINE"
  puts follow_file_command(file_name, trigger)
  while line = readline
    yield line
  end
ensure
  puts trigger
end

#follow_file_command(file_name, finished_trigger) ⇒ Object



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/system_pipe.rb', line 118

def follow_file_command(file_name, finished_trigger)
  "control ()
  {
      while read line; do
          if [[ \"$line\" == \"#{finished_trigger}\" ]]; then
              exit
          fi
      done
  }

  control <&0 &
  CONTROL_PID=$!
  tail --pid $CONTROL_PID -qF #{file_name} 2>&1
  "
end

#make_path(path) ⇒ Object



84
85
86
# File 'lib/system_pipe.rb', line 84

def make_path(path)
  run_command_and_ensure_return_code "mkdir -p #{path}"
end

#puts_command_read_number(command, timeout = PIPE_COMMAND_TIMEOUT) ⇒ Object



56
57
58
59
60
61
# File 'lib/system_pipe.rb', line 56

def puts_command_read_number(command, timeout = PIPE_COMMAND_TIMEOUT)
  retry_after_timeout(timeout) do
    puts_limit_one_line command
    extract_number_from_string(readline.strip)
  end
end

#puts_limit_one_line(command) ⇒ Object



104
105
106
# File 'lib/system_pipe.rb', line 104

def puts_limit_one_line(command)
  puts "#{command} | head -1"
end

#puts_with_output_to_dev_null(command) ⇒ Object



100
101
102
# File 'lib/system_pipe.rb', line 100

def puts_with_output_to_dev_null(command)
  puts "#{command} 2>&1 >/dev/null"
end

#retry_after_timeout(timeout = PIPE_COMMAND_TIMEOUT, &block) ⇒ Object



68
69
70
71
72
73
74
75
# File 'lib/system_pipe.rb', line 68

def retry_after_timeout(timeout = PIPE_COMMAND_TIMEOUT, &block)
  Timeout::timeout(timeout) do
    instance_eval &block
  end
rescue Timeout::Error, Errno::EPIPE, EOFError => exception
  retry_pipe
  raise PipeReset, "#{exception.class}: #{exception.message}"
end

#retry_pipeObject



38
39
40
41
# File 'lib/system_pipe.rb', line 38

def retry_pipe
  close_with_timeout
  start_pipe
end

#run_command_and_ensure_return_code(command) ⇒ Object



88
89
90
91
92
93
94
95
96
97
98
# File 'lib/system_pipe.rb', line 88

def run_command_and_ensure_return_code(command)
  output = []
  puts command
  puts "echo Return Code: $?"
  return_code_line = flush_until("Return Code:") do |line|
    output << line.chomp
  end
  output.pop
  raise ReturnCodeException, "Failed to run command:#{command} because #{return_code_line}.\n#{output.join("\n")}" unless return_code_line.match /Return Code: 0/
  output
end

#start_pipeObject



29
30
31
32
# File 'lib/system_pipe.rb', line 29

def start_pipe
  @pipe = open_pipe_for_writing(initial_command)
  ensure_started
end

#write_file(file_path, contents) ⇒ Object



77
78
79
80
81
82
# File 'lib/system_pipe.rb', line 77

def write_file(file_path, contents)
  escaped_contents = Escape.shell_single_word(contents)

  write_file_command = "echo -n #{escaped_contents} > #{file_path}"
  run_command_and_ensure_return_code(write_file_command)
end