pg_export 0.5.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/README.md +2 -8
  4. data/bin/pg_export +39 -19
  5. data/lib/pg_export/aes/base.rb +47 -0
  6. data/lib/pg_export/aes/decryptor.rb +13 -0
  7. data/lib/pg_export/aes/encryptor.rb +13 -0
  8. data/lib/pg_export/aes.rb +3 -0
  9. data/lib/pg_export/bash/adapter.rb +31 -0
  10. data/lib/pg_export/bash/factory.rb +23 -0
  11. data/lib/pg_export/bash/repository.rb +18 -0
  12. data/lib/pg_export/boot_container.rb +69 -0
  13. data/lib/pg_export/build_logger.rb +19 -0
  14. data/lib/pg_export/configuration.rb +11 -53
  15. data/lib/pg_export/{entities/dump.rb → dump.rb} +18 -4
  16. data/lib/pg_export/errors.rb +3 -3
  17. data/lib/pg_export/ftp/adapter.rb +41 -0
  18. data/lib/pg_export/ftp/connection.rb +40 -0
  19. data/lib/pg_export/ftp/repository.rb +40 -0
  20. data/lib/pg_export/roles/colourable_string.rb +19 -0
  21. data/lib/pg_export/roles/human_readable.rb +17 -0
  22. data/lib/pg_export/roles/interactive.rb +98 -0
  23. data/lib/pg_export/roles/validatable.rb +24 -0
  24. data/lib/pg_export/services/create_and_export_dump.rb +20 -0
  25. data/lib/pg_export/version.rb +1 -1
  26. data/lib/pg_export.rb +18 -56
  27. data/pg_export.gemspec +2 -1
  28. metadata +49 -31
  29. data/lib/pg_export/includable_modules/colourable_string.rb +0 -17
  30. data/lib/pg_export/includable_modules/dump/size_human.rb +0 -15
  31. data/lib/pg_export/includable_modules/interactive.rb +0 -97
  32. data/lib/pg_export/includable_modules/logging.rb +0 -31
  33. data/lib/pg_export/includable_modules/services_container.rb +0 -41
  34. data/lib/pg_export/services/aes/base.rb +0 -26
  35. data/lib/pg_export/services/aes/decryptor.rb +0 -12
  36. data/lib/pg_export/services/aes/encryptor.rb +0 -12
  37. data/lib/pg_export/services/aes.rb +0 -28
  38. data/lib/pg_export/services/bash_utils.rb +0 -32
  39. data/lib/pg_export/services/dump_storage.rb +0 -48
  40. data/lib/pg_export/services/ftp_adapter.rb +0 -38
  41. data/lib/pg_export/services/ftp_connection.rb +0 -28
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 763552e9063089d2137c2eff38b159caee96f457
4
- data.tar.gz: 445e0c662a841abf1c9ee1f3ba4fa05dc845e6e3
3
+ metadata.gz: 4e6bcca69be07806c87aa5e971292269c6e24ea3
4
+ data.tar.gz: 421af6b9f3430c9a5e3431da6f71d78b91463157
5
5
  SHA512:
6
- metadata.gz: 9f8e27408b4aa14d29783f3672c456d09521cce85195fee112fbd32b8fd3b987f039a82dc28e652553fc40f63be0d80509cf05eb1299494c9beaac57ff8d7f2f
7
- data.tar.gz: ab1c4938fbbb933735c35f806d6eca4745d240a7d749641180724ccebaba06e976b49a73762bc36eb4a7a577fdb6f2fe8e20ca41f4028cba29f8c235125c9204
6
+ metadata.gz: a2d7b152087be40ae99ae42a5e3acfb90b1aacef6780bfc6ec255883db9230c77455ae17065226e0aad91971ab74e89743badd74c4dd3c7f3571638fb8837ffe
7
+ data.tar.gz: 633e2d33af88ee049ee3ba5490ce228391ee445569533b8752b87ec5c29435bd1bdc0d518801b6c889e4a2898d08127d99a513d6239b1b54de3de1adea28b8f5
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ### 0.6.0 - 2017.08.18
2
+
3
+ - Improve internal architecture
4
+
1
5
  ### 0.5.1 - 2017.03.31
2
6
 
3
7
  - Simplify Dump entity design
data/README.md CHANGED
@@ -3,7 +3,6 @@
3
3
  [![Gem Version](https://badge.fury.io/rb/pg_export.svg)](https://badge.fury.io/rb/pg_export)
4
4
  [![Build Status](https://travis-ci.org/maicher/pg_export.svg?branch=master)](https://travis-ci.org/maicher/pg_export)
5
5
  [![Code Climate](https://codeclimate.com/github/maicher/pg_export/badges/gpa.svg)](https://codeclimate.com/github/maicher/pg_export)
6
- [![Test Coverage](https://codeclimate.com/github/maicher/cli_spinnable/badges/coverage.svg)](https://codeclimate.com/github/maicher/pg_export/coverage)
7
6
 
8
7
  CLI for creating and exporting PostgreSQL dumps to FTP.
9
8
 
@@ -86,18 +85,13 @@ __Step 3.__ Configure how many dumps should be kept in FTP (optional).
86
85
  __Step 4.__ Print the configuration to verify whether env variables has been loaded.
87
86
 
88
87
  $ pg_export --configuration
89
- => database:
90
- keep_dumps: 5
91
- dump_password: k40***
92
- ftp_host: yourftp.example.com
93
- ftp_user: user
94
- ftp_password: pass***
88
+ => {:database=>"undefined", :keep_dumps=>10, :dump_encryption_key=>"k4***", :ftp_host=>"yourftp.example.com",
89
+ :ftp_user=>"user", :ftp_password=>"pass***", :logger_format=>:plain}
95
90
 
96
91
  __Step 5.__ Try connecting to FTP to verify the connection.
97
92
 
98
93
  $ pg_export --ftp
99
94
  => Connect to yourftp.example.com
100
- Close FTP
101
95
 
102
96
  __Step 6.__ Perform database export.
103
97
 
data/bin/pg_export CHANGED
@@ -3,34 +3,38 @@
3
3
  require 'optparse'
4
4
  require 'ostruct'
5
5
 
6
- require 'cli_spinnable'
7
-
8
6
  require 'pg_export'
9
- require 'pg_export/includable_modules/colourable_string'
10
- require 'pg_export/includable_modules/interactive'
7
+ require 'pg_export/roles/colourable_string'
11
8
 
12
- config = PgExport::ServicesContainer.config
13
- pg_export = PgExport.new
9
+ config = {
10
+ database: 'undefined',
11
+ keep_dumps: ENV['KEEP_DUMPS'],
12
+ dump_encryption_key: ENV['DUMP_ENCRYPTION_KEY'],
13
+ ftp_host: ENV['BACKUP_FTP_HOST'],
14
+ ftp_user: ENV['BACKUP_FTP_USER'],
15
+ ftp_password: ENV['BACKUP_FTP_PASSWORD'],
16
+ logger_format: :plain,
17
+ interactive: false
18
+ }
14
19
 
15
20
  option_parser = OptionParser.new do |opts|
16
21
  opts.banner = 'Usage: pg_export [options]'
17
22
 
18
- opts.on('-d', '--database DATABASE', '[Required] Name of the database to export') do |database|
19
- config.database = database
23
+ opts.on('-d', '--database DATABASE', String, '[Required] Name of the database to export') do |database|
24
+ config[:database] = database
20
25
  end
21
26
 
22
- opts.on('-k', '--keep [KEEP]', Integer, "[Optional] Number of dump files to keep on FTP (default: #{config.keep_dumps})") do |keep|
23
- config.keep_dumps = keep
27
+ opts.on('-k', '--keep [KEEP]', Integer, "[Optional] Number of dump files to keep on FTP (default: #{config[:keep_dumps]})") do |keep|
28
+ config[:keep_dumps] = keep
24
29
  end
25
30
 
26
31
  opts.on('-t', '--timestamped', '[Optional] Enables log messages with timestamps') do
27
- PgExport::Logging.format_timestamped
32
+ config[:logger_format] = :timestamped
28
33
  end
29
34
 
30
35
  opts.on('-i', '--interactive', 'Interactive, command line mode, for restoring dumps into databases') do
31
- PgExport::Logging.mute
32
- config.database ||= 'undefined'
33
- pg_export.extend(PgExport::Interactive)
36
+ config[:logger_format] = :muted
37
+ config[:interactive] = true
34
38
  end
35
39
 
36
40
  opts.on('-h', '--help', 'Show this message') do
@@ -41,21 +45,37 @@ option_parser = OptionParser.new do |opts|
41
45
  opts.separator "\nSetting can be verified by running following commands:"
42
46
 
43
47
  opts.on('-c', '--configuration', 'Prints the configuration') do
44
- puts config.to_s
48
+ puts config
45
49
  exit
46
50
  end
47
51
 
48
52
  opts.on('-f', '--ftp', 'Tries connecting to FTP to verify the connection') do
49
- PgExport::ServicesContainer.ftp_connection.open
53
+ PgExport::Ftp::Connection.new(
54
+ host: config[:ftp_host],
55
+ user: config[:ftp_user],
56
+ password: config[:ftp_password],
57
+ logger: PgExport::BuildLogger.call(stream: $stdout, format: :plain)
58
+ ).open
50
59
  exit
51
60
  end
52
61
  end
53
62
 
54
63
  begin
55
64
  option_parser.parse!
56
- pg_export.call
57
- rescue OptionParser::MissingArgument, PgExport::InvalidConfigurationError => e
58
- using PgExport::ColourableString
65
+
66
+ PgExport.new(
67
+ dump_encryption_key: config[:dump_encryption_key],
68
+ ftp_host: config[:ftp_host],
69
+ ftp_user: config[:ftp_user],
70
+ ftp_password: config[:ftp_password],
71
+ logger_format: config[:logger_format],
72
+ interactive: config[:interactive]
73
+ ).call(
74
+ config[:database],
75
+ config[:keep_dumps]
76
+ )
77
+ rescue OptionParser::MissingArgument, PgExport::PgExportError, ArgumentError => e
78
+ using PgExport::Roles::ColourableString
59
79
  puts "Error: #{e}".red
60
80
  puts option_parser
61
81
  rescue PgExport::PgDumpError => e
@@ -0,0 +1,47 @@
1
+ require 'openssl'
2
+ require 'pg_export/dump'
3
+
4
+ class PgExport
5
+ module Aes
6
+ class Base
7
+ ALGORITHM = 'AES-128-CBC'.freeze
8
+
9
+ def initialize(key:, logger:)
10
+ @key, @logger = key, logger
11
+ end
12
+
13
+ def call(source_dump)
14
+ target_dump = Dump.new(name: target_dump_name, db_name: source_dump.db_name)
15
+ copy(from: source_dump, to: target_dump)
16
+ logger.info "Create #{target_dump}"
17
+ target_dump
18
+ end
19
+
20
+ private
21
+
22
+ attr_reader :key, :logger
23
+
24
+ def copy(from:, to:)
25
+ cipher.reset
26
+ to.open(:write) do |f|
27
+ from.each_chunk do |chunk|
28
+ f << cipher.update(chunk)
29
+ end
30
+ f << cipher.final
31
+ end
32
+ self
33
+ end
34
+
35
+ def cipher
36
+ @cipher ||= build_cipher
37
+ end
38
+
39
+ def build_cipher
40
+ OpenSSL::Cipher.new(ALGORITHM).tap do |cipher|
41
+ cipher.public_send(cipher_type)
42
+ cipher.key = key
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,13 @@
1
+ class PgExport
2
+ module Aes
3
+ class Decryptor < Base
4
+ def target_dump_name
5
+ 'Dump'.freeze
6
+ end
7
+
8
+ def cipher_type
9
+ :decrypt
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ class PgExport
2
+ module Aes
3
+ class Encryptor < Base
4
+ def target_dump_name
5
+ 'Encrypted Dump'.freeze
6
+ end
7
+
8
+ def cipher_type
9
+ :encrypt
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,3 @@
1
+ require_relative 'aes/base'
2
+ require_relative 'aes/encryptor'
3
+ require_relative 'aes/decryptor'
@@ -0,0 +1,31 @@
1
+ require 'open3'
2
+ require 'pg_export/errors'
3
+
4
+ class PgExport
5
+ module Bash
6
+ class Adapter
7
+ def get(path, db_name)
8
+ popen("pg_dump -Fc --file #{path} #{db_name}") do |errors|
9
+ raise PgDumpError, errors unless errors.empty?
10
+ end
11
+ end
12
+
13
+ def persist(path, db_name)
14
+ popen("pg_restore -c -d #{db_name} #{path}") do |errors|
15
+ raise PgRestoreError, errors if /FATAL/ =~ errors
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def popen(command)
22
+ Open3.popen3(command) do |_, _, err|
23
+ errors = err.read
24
+ yield errors
25
+ end
26
+
27
+ self
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,23 @@
1
+ require 'open3'
2
+ require 'pg_export/dump'
3
+
4
+ class PgExport
5
+ module Bash
6
+ class Factory
7
+ def initialize(adapter:, logger:)
8
+ @adapter, @logger = adapter, logger
9
+ end
10
+
11
+ def build_dump(db_name)
12
+ dump = Dump.new(name: 'Dump', db_name: db_name)
13
+ adapter.get(dump.path, dump.db_name)
14
+ logger.info "Create #{dump}"
15
+ dump
16
+ end
17
+
18
+ private
19
+
20
+ attr_reader :adapter, :logger
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,18 @@
1
+ class PgExport
2
+ module Bash
3
+ class Repository
4
+ def initialize(adapter:, logger:)
5
+ @adapter, @logger = adapter, logger
6
+ end
7
+
8
+ def persist(dump, db_name)
9
+ adapter.persist(dump.path, db_name)
10
+ logger.info "Persist #{dump} #{db_name} to #{adapter}"
11
+ end
12
+
13
+ private
14
+
15
+ attr_reader :adapter, :logger
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,69 @@
1
+ require_relative 'build_logger'
2
+ require_relative 'ftp/adapter'
3
+ require_relative 'ftp/connection'
4
+ require_relative 'ftp/repository'
5
+ require_relative 'bash/adapter'
6
+ require_relative 'bash/repository'
7
+ require_relative 'bash/factory'
8
+ require_relative 'aes'
9
+ require_relative 'services/create_and_export_dump'
10
+
11
+ class PgExport
12
+ class BootContainer
13
+ class << self
14
+ def call(config)
15
+ container = {}
16
+
17
+ boot_logger(container, config)
18
+ boot_aes(container, config)
19
+ boot_ftp(container, config)
20
+ boot_bash(container)
21
+ boot_services(container)
22
+
23
+ container
24
+ end
25
+
26
+ private
27
+
28
+ def boot_logger(container, config)
29
+ container[:logger] = BuildLogger.call(stream: $stdout, format: config[:logger_format])
30
+ end
31
+
32
+ def boot_aes(container, config)
33
+ container[:encryptor] = Aes::Encryptor.new(key: config[:dump_encryption_key], logger: container[:logger])
34
+ container[:decryptor] = Aes::Decryptor.new(key: config[:dump_encryption_key], logger: container[:logger])
35
+ end
36
+
37
+ def boot_ftp(container, config)
38
+ container[:ftp_connection] = Ftp::Connection.new(
39
+ host: config[:ftp_host],
40
+ user: config[:ftp_user],
41
+ password: config[:ftp_password],
42
+ logger: container[:logger]
43
+ )
44
+ container[:ftp_adapter] = Ftp::Adapter.new(connection: container[:ftp_connection])
45
+ container[:ftp_repository] = Ftp::Repository.new(adapter: container[:ftp_adapter], logger: container[:logger])
46
+ end
47
+
48
+ def boot_bash(container)
49
+ container[:bash_adapter] = Bash::Adapter.new
50
+ container[:bash_repository] = Bash::Repository.new(
51
+ adapter: container[:bash_adapter],
52
+ logger: container[:logger]
53
+ )
54
+ container[:bash_factory] = Bash::Factory.new(
55
+ adapter: container[:bash_adapter],
56
+ logger: container[:logger]
57
+ )
58
+ end
59
+
60
+ def boot_services(container)
61
+ container[:create_and_export_dump] = Services::CreateAndExportDump.new(
62
+ bash_factory: container[:bash_factory],
63
+ encryptor: container[:encryptor],
64
+ ftp_repository: container[:ftp_repository]
65
+ )
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,19 @@
1
+ require 'logger'
2
+
3
+ class PgExport
4
+ class BuildLogger
5
+ FORMATS = {
6
+ plain: ->(_, _, _, message) { "#{message}\n" },
7
+ muted: ->(_, _, _, _) {},
8
+ timestamped: lambda do |severity, datetime, progname, message|
9
+ "#{datetime} #{Process.pid} TID-#{Thread.current.object_id.to_s(36)}#{progname} #{severity}: #{message}\n"
10
+ end
11
+ }.freeze
12
+
13
+ def self.call(stream:, format:)
14
+ Logger.new(stream).tap do |logger|
15
+ logger.formatter = FORMATS.fetch(format)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -1,56 +1,14 @@
1
- class PgExport
2
- class Configuration
3
- FIELD_REQUIRED = 'Field %s is required'.freeze
4
- INVALID_ENCRYPTION_KEY_LENGTH = 'Dump encryption key should have exact 16 characters. Edit your DUMP_ENCRYPTION_KEY env variable.'.freeze
5
-
6
- DEFAULTS = {
7
- database: nil,
8
- keep_dumps: ENV['KEEP_DUMPS'] || 10,
9
- dump_encryption_key: ENV['DUMP_ENCRYPTION_KEY'],
10
- ftp_host: ENV['BACKUP_FTP_HOST'],
11
- ftp_user: ENV['BACKUP_FTP_USER'],
12
- ftp_password: ENV['BACKUP_FTP_PASSWORD']
13
- }.freeze
14
-
15
- attr_accessor *DEFAULTS.keys
16
-
17
- def initialize
18
- DEFAULTS.each_pair do |key, value|
19
- send("#{key}=", value)
20
- end
21
- end
22
-
23
- def validate
24
- DEFAULTS.keys.each do |field|
25
- raise InvalidConfigurationError, FIELD_REQUIRED % field if public_send(field).nil?
26
- end
27
- raise InvalidConfigurationError, INVALID_ENCRYPTION_KEY_LENGTH unless dump_encryption_key.length == 16
28
- end
1
+ require 'dry-struct'
29
2
 
30
- def ftp_params
31
- {
32
- host: ftp_host,
33
- user: ftp_user,
34
- password: ftp_password
35
- }
36
- end
37
-
38
- def to_s
39
- DEFAULTS.keys.map(&method(:print_attr))
40
- end
41
-
42
- private
43
-
44
- def print_attr(key)
45
- if %i(ftp_password dump_encryption_key).include?(key)
46
- if public_send(key)
47
- "#{key}: #{public_send(key)[0..2]}***\n"
48
- else
49
- "#{key}:\n"
50
- end
51
- else
52
- "#{key}: #{public_send(key)}\n"
53
- end
54
- end
3
+ class PgExport
4
+ class Configuration < Dry::Struct
5
+ include Dry::Types.module
6
+
7
+ attribute :dump_encryption_key, Strict::String.constrained(size: 16)
8
+ attribute :ftp_host, Strict::String
9
+ attribute :ftp_user, Strict::String
10
+ attribute :ftp_password, Strict::String
11
+ attribute :logger_format, Strict::Symbol.enum(:plain, :timestamped, :muted)
12
+ attribute :interactive, Strict::Bool
55
13
  end
56
14
  end
@@ -1,16 +1,24 @@
1
+ require 'forwardable'
2
+ require 'tempfile'
3
+
4
+ require_relative 'roles/human_readable'
5
+
1
6
  class PgExport
2
7
  class Dump
8
+ TIMESTAMP = '_%Y%m%d_%H%M%S'.freeze
9
+
3
10
  extend Forwardable
4
- include SizeHuman
11
+ include Roles::HumanReadable
5
12
 
6
13
  CHUNK_SIZE = (2**16).freeze
7
14
 
8
15
  def_delegators :file, :path, :read, :write, :<<, :rewind, :close, :size, :eof?
9
16
 
10
- attr_reader :name
17
+ attr_reader :name, :db_name
11
18
 
12
- def initialize(name)
13
- @name = name
19
+ def initialize(name:, db_name:)
20
+ @name, @db_name = name, db_name
21
+ @timestamp = Time.now.strftime(TIMESTAMP)
14
22
  end
15
23
 
16
24
  def ext
@@ -31,12 +39,18 @@ class PgExport
31
39
  end
32
40
  end
33
41
 
42
+ def timestamped_name
43
+ db_name + timestamp + ext
44
+ end
45
+
34
46
  def to_s
35
47
  "#{name} (#{size_human})"
36
48
  end
37
49
 
38
50
  private
39
51
 
52
+ attr_reader :timestamp
53
+
40
54
  def file
41
55
  @file ||= Tempfile.new(file_name)
42
56
  end
@@ -1,5 +1,5 @@
1
1
  class PgExport
2
- class InvalidConfigurationError < StandardError; end
3
- class PgDumpError < StandardError; end
4
- class PgRestoreError < StandardError; end
2
+ class PgExportError < StandardError; end
3
+ class PgRestoreError < PgExportError; end
4
+ class PgDumpError < PgExportError; end
5
5
  end
@@ -0,0 +1,41 @@
1
+ class PgExport
2
+ module Ftp
3
+ class Adapter
4
+ CHUNK_SIZE = (2**16).freeze
5
+
6
+ def initialize(connection:)
7
+ @connection = connection
8
+ @host = connection.host
9
+ ObjectSpace.define_finalizer(self, proc { connection.close })
10
+ end
11
+
12
+ def list(regex_string)
13
+ ftp.list(regex_string).map { |item| item.split(' ').last }.sort.reverse
14
+ end
15
+
16
+ def delete(filename)
17
+ ftp.delete(filename)
18
+ end
19
+
20
+ def persist(path, timestamped_name)
21
+ ftp.putbinaryfile(path, timestamped_name, CHUNK_SIZE)
22
+ end
23
+
24
+ def get(path, timestamped_name)
25
+ ftp.getbinaryfile(timestamped_name, path, CHUNK_SIZE)
26
+ end
27
+
28
+ def ftp
29
+ connection.ftp
30
+ end
31
+
32
+ def to_s
33
+ host
34
+ end
35
+
36
+ private
37
+
38
+ attr_reader :connection, :host
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,40 @@
1
+ require 'net/ftp'
2
+
3
+ class PgExport
4
+ module Ftp
5
+ class Connection
6
+ attr_reader :host
7
+
8
+ def initialize(host:, user:, password:, logger:)
9
+ @host, @user, @password, @logger = host, user, password, logger
10
+ open_ftp_thread
11
+ end
12
+
13
+ def ftp
14
+ open_ftp_thread.join
15
+ @ftp
16
+ end
17
+
18
+ def close
19
+ ftp.close
20
+ logger.info 'Close FTP'
21
+ self
22
+ end
23
+
24
+ private
25
+
26
+ attr_reader :user, :password, :logger
27
+
28
+ def open
29
+ @ftp = Net::FTP.new(host, user, password)
30
+ @ftp.passive = true
31
+ logger.info "Connect to #{host}"
32
+ self
33
+ end
34
+
35
+ def open_ftp_thread
36
+ @open_ftp_thread ||= Thread.new { open }
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,40 @@
1
+ class PgExport
2
+ module Ftp
3
+ class Repository
4
+ def initialize(adapter:, logger:)
5
+ @adapter, @logger = adapter, logger
6
+ end
7
+
8
+ def persist(dump)
9
+ adapter.persist(dump.path, dump.timestamped_name)
10
+ logger.info "Persist #{dump} #{dump.timestamped_name} to #{adapter}"
11
+ end
12
+
13
+ def get(db_name)
14
+ dump = Dump.new(name: 'Encrypted Dump', db_name: db_name)
15
+ adapter.get(dump.path, dump.db_name)
16
+ logger.info "Get #{dump} #{db_name} from #{adapter}"
17
+ dump
18
+ end
19
+
20
+ def remove_old(name, keep)
21
+ find_by_name(name).drop(keep).each do |filename|
22
+ adapter.delete(filename)
23
+ logger.info "Remove #{filename} from #{adapter}"
24
+ end
25
+ end
26
+
27
+ def find_by_name(name)
28
+ adapter.list(name + '_*')
29
+ end
30
+
31
+ def all
32
+ adapter.list('*')
33
+ end
34
+
35
+ private
36
+
37
+ attr_reader :adapter, :logger
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,19 @@
1
+ class PgExport
2
+ module Roles
3
+ module ColourableString
4
+ refine String do
5
+ def red
6
+ "\e[31m#{self}\e[0m"
7
+ end
8
+
9
+ def green
10
+ "\e[0;32;49m#{self}\e[0m"
11
+ end
12
+
13
+ def gray
14
+ "\e[37m#{self}\e[0m"
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,17 @@
1
+ class PgExport
2
+ module Roles
3
+ module HumanReadable
4
+ MAPPING = {
5
+ 'B' => 1024,
6
+ 'kB' => 1024 * 1024,
7
+ 'MB' => 1024 * 1024 * 1024,
8
+ 'GB' => 1024 * 1024 * 1024 * 1024,
9
+ 'TB' => 1024 * 1024 * 1024 * 1024 * 1024
10
+ }.freeze
11
+
12
+ def size_human
13
+ MAPPING.each_pair { |e, s| return "#{(size.to_f / (s / 1024)).round(2)}#{e}" if size < s }
14
+ end
15
+ end
16
+ end
17
+ end