class Backupsss::Backup

A class for delivering a tar to S3

Constants

MAX_FILE_SIZE

Attributes

client[R]
config[R]
filename[R]

Public Class Methods

new(config, client) click to toggle source
# File lib/backupsss/backup.rb, line 10
def initialize(config, client)
  @config   = config
  @client   = client
  @filename = config[:filename]
end

Public Instance Methods

put_file(file) click to toggle source
# File lib/backupsss/backup.rb, line 16
def put_file(file)
  large_file(file) ? multi_upload(file) : single_upload(file)
end

Private Instance Methods

abort_multipart_message(error, upload_id) click to toggle source
# File lib/backupsss/backup.rb, line 55
def abort_multipart_message(error, upload_id)
  "#{error}\nAborting multipart upload : #{upload_id}"
end
bail_multipart_on_fail(upload_id) { || ... } click to toggle source
# File lib/backupsss/backup.rb, line 87
def bail_multipart_on_fail(upload_id)
  yield
rescue StandardError => e
  $stdout.puts abort_multipart_message(e, upload_id)
  client.abort_multipart_upload(bucket_opts.merge(upload_id: upload_id))
end
bail_upload_part_on_fail(part, upload_id) { || ... } click to toggle source
# File lib/backupsss/backup.rb, line 94
def bail_upload_part_on_fail(part, upload_id)
  yield
rescue StandardError => e
  output = ["Failed to upload part number #{part} : #{upload_id}"]
  output << "because of #{e.message}"
  output << "Aborting remaining parts : #{upload_id}"
  raise output.join("\n")
end
bucket_opts() click to toggle source
# File lib/backupsss/backup.rb, line 121
def bucket_opts
  { bucket: config[:s3_bucket],
    key:    "#{config[:s3_bucket_prefix]}/#{filename}" }
end
complete_multipart_upload_request(upload_id, parts) click to toggle source
# File lib/backupsss/backup.rb, line 37
def complete_multipart_upload_request(upload_id, parts)
  bucket_opts.merge(
    upload_id: upload_id, multipart_upload: { parts: parts }
  )
end
create_multipart_upload() click to toggle source
# File lib/backupsss/backup.rb, line 22
def create_multipart_upload
  $stdout.puts 'Creating a multipart upload'

  client.create_multipart_upload(bucket_opts)
end
large_file(file) click to toggle source
# File lib/backupsss/backup.rb, line 28
def large_file(file)
  $stdout.puts 'Checking backup size ...'
  is_lg  = file.size > MAX_FILE_SIZE
  status = is_lg ? 'greater than' : 'less than or equal to'
  $stdout.puts "Size of backup is #{status} 100MB"

  is_lg
end
multi_upload(file) click to toggle source
# File lib/backupsss/backup.rb, line 59
def multi_upload(file)
  upload_id = create_multipart_upload.upload_id

  bail_multipart_on_fail(upload_id) do
    timed_multipart_upload do
      parts = upload_parts(file, upload_id).sort do |a, b|
        a[:part_number] <=> b[:part_number]
      end

      req = complete_multipart_upload_request(upload_id, parts)
      client.complete_multipart_upload(req)
    end
  end
end
part_count(file) click to toggle source
# File lib/backupsss/backup.rb, line 110
def part_count(file)
  c = (file.size.to_f / MAX_FILE_SIZE.to_f).ceil
  $stdout.puts "Uploading backup as #{c} parts"

  c
end
single_upload(file) click to toggle source
# File lib/backupsss/backup.rb, line 117
def single_upload(file)
  client.put_object(bucket_opts.merge(body: file.read))
end
timed_multipart_upload() { || ... } click to toggle source
# File lib/backupsss/backup.rb, line 43
def timed_multipart_upload
  s = Time.now
  $stdout.puts "Starting multipart upload at #{s}"
  yield
  e = Time.now
  duration = ((e - s) / 60).round(2)
  output   = ["Completed multipart upload at #{e}"]
  output << "Completed in #{duration} minutes."

  $stdout.puts output.join("\n")
end
upload_part_params(file, part, upload_id) click to toggle source
# File lib/backupsss/backup.rb, line 103
def upload_part_params(file, part, upload_id)
  start = (part - 1) * MAX_FILE_SIZE
  body  = IO.read(file.path, MAX_FILE_SIZE, start)

  bucket_opts.merge(part_number: part, body: body, upload_id: upload_id)
end
upload_parts(file, upload_id) click to toggle source
# File lib/backupsss/backup.rb, line 74
def upload_parts(file, upload_id)
  Parallel.map(1..part_count(file), in_threads: 10) do |part|
    bail_upload_part_on_fail(part, upload_id) do
      $stdout.puts "Uploading part number #{part} : #{upload_id}\n"
      r = client.upload_part(upload_part_params(file, part, upload_id))
      success_msg = "Completed uploading part number #{part} : #{upload_id}"
      r.on_success { $stdout.puts success_msg }

      { etag: r.etag, part_number: part }
    end
  end
end