Module: EY::Backup::Spawner

Extended by:
Spawner, Forwardable
Included in:
Engine, GZipper, Spawner, Splitter
Defined in:
lib/ey_backup/spawner.rb

Constant Summary collapse

CHUNK_SIZE =
4096

Instance Method Summary collapse

Instance Method Details

#ioify(stdout, stdin, stderr, &block) ⇒ Object



68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/ey_backup/spawner.rb', line 68

def ioify(stdout, stdin, stderr, &block)
  if String === stdin
    File.open(stdin, 'r') do |f|
      ioify(stdout, f, stderr, &block)
    end
  elsif String === stdout
    File.open(stdout, 'w') do |f|
      ioify(f, stdin, stderr, &block)
    end
  else
    yield stdout, stdin, stderr
  end
end

#run(command, db = nil) ⇒ Object



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

def run(command, db = nil)
  unless runs?(command, db)
    raise "Failed to run backup command."
  end
end

#runs?(command, db) ⇒ Boolean

Returns:

  • (Boolean)


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
# File 'lib/ey_backup/spawner.rb', line 29

def runs?(command, db)
  # This is to detect failures anywhere in the pipeline.
  wrapper = <<-EOT
    status=0
    #{command}
    status=$?

    if [ $status -gt 0 ]; then exit 1; fi
  EOT

  escaped_command = Shellwords.escape(wrapper)

  verbose "Running command: #{command}" 
  pid, stdin, stdout, stderr = Open4.popen4("bash -o pipefail -c #{escaped_command}")
  pid, status = Process::waitpid2(pid)
  
  verbose "stdout: #{stdout.read}"
  verbose "stderr: #{stderr.read}"
  verbose "status: #{status}"
  
  if ! status.success?
    dumperr = File.exists?("/tmp/eybackup.#{pid}.dumperr") ? File.read("/tmp/eybackup.#{pid}.dumperr") : status
    err_msg = "DB dump failed. The error returned was: #{dumperr}"
    verbose "#{db} backup failed: #{err_msg}"
    error(err_msg, db)
  end

  # Clean up:
  # This whole error output thing is hideous.
  # We need the errors, but as we're dealing with a pipeline we
  # need to capture them separately rather than send them as
  # input to the next process. We construct the backup command
  # in the backup engine but only know the pid of the resulting
  # backup here. Ugh.
  system("rm /tmp/eybackup.#{pid}.dumperr") if File.exists?("/tmp/eybackup.#{pid}.dumperr")

  status.success?
end

#spawn(command, stdout = nil, stdin = nil, stderr = logger.stderr) ⇒ Object



13
14
15
16
17
18
19
20
21
# File 'lib/ey_backup/spawner.rb', line 13

def spawn(command, stdout = nil, stdin = nil, stderr = logger.stderr)
  ioify(stdout, stdin, stderr) do |o, i, e|
    ios = {:stderr => e}
    ios[:stdout] = o if o
    ios[:stdin] = i if i
    result = Open4.spawn([command], ios)

  end
end