pg_export 0.7.4 → 1.0.0.rc2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +6 -3
  4. data/CHANGELOG.md +18 -0
  5. data/README.md +14 -11
  6. data/bin/pg_export +72 -16
  7. data/lib/pg_export/configuration.rb +12 -8
  8. data/lib/pg_export/container.rb +17 -8
  9. data/lib/pg_export/import.rb +1 -1
  10. data/lib/pg_export/lib/pg_export/factories/ftp_gateway_factory.rb +22 -0
  11. data/lib/pg_export/lib/pg_export/factories/ssh_gateway_factory.rb +22 -0
  12. data/lib/pg_export/lib/pg_export/{adapters/ftp_adapter.rb → gateways/ftp.rb} +11 -8
  13. data/lib/pg_export/lib/pg_export/gateways/ssh.rb +82 -0
  14. data/lib/pg_export/lib/pg_export/listeners/interactive/build_dump.rb +4 -4
  15. data/lib/pg_export/lib/pg_export/listeners/interactive/{close_ftp_connection.rb → close_connection.rb} +1 -1
  16. data/lib/pg_export/lib/pg_export/listeners/interactive/decrypt_dump.rb +2 -2
  17. data/lib/pg_export/lib/pg_export/listeners/interactive/download_dump_from_ftp.rb +2 -2
  18. data/lib/pg_export/lib/pg_export/listeners/interactive/encrypt_dump.rb +2 -2
  19. data/lib/pg_export/lib/pg_export/listeners/interactive/{open_ftp_connection.rb → open_connection.rb} +1 -1
  20. data/lib/pg_export/lib/pg_export/listeners/interactive/remove_old_dumps_from_ftp.rb +5 -5
  21. data/lib/pg_export/lib/pg_export/listeners/interactive/restore.rb +2 -2
  22. data/lib/pg_export/lib/pg_export/listeners/interactive/upload_dump_to_ftp.rb +2 -2
  23. data/lib/pg_export/lib/pg_export/listeners/interactive_listener.rb +2 -2
  24. data/lib/pg_export/lib/pg_export/listeners/plain/build_dump.rb +2 -2
  25. data/lib/pg_export/lib/pg_export/listeners/plain/{close_ftp_connection.rb → close_connection.rb} +2 -2
  26. data/lib/pg_export/lib/pg_export/listeners/plain/decrypt_dump.rb +2 -2
  27. data/lib/pg_export/lib/pg_export/listeners/plain/download_dump_from_ftp.rb +2 -2
  28. data/lib/pg_export/lib/pg_export/listeners/plain/encrypt_dump.rb +2 -2
  29. data/lib/pg_export/lib/pg_export/listeners/plain/fetch_dumps_from_ftp.rb +2 -2
  30. data/lib/pg_export/lib/pg_export/listeners/plain/open_connection.rb +15 -0
  31. data/lib/pg_export/lib/pg_export/listeners/plain/remove_old_dumps_from_ftp.rb +3 -3
  32. data/lib/pg_export/lib/pg_export/listeners/plain/restore.rb +1 -1
  33. data/lib/pg_export/lib/pg_export/listeners/plain/upload_dump_to_ftp.rb +2 -2
  34. data/lib/pg_export/lib/pg_export/listeners/plain_listener.rb +2 -2
  35. data/lib/pg_export/lib/pg_export/operations/{open_ftp_connection.rb → open_connection.rb} +5 -5
  36. data/lib/pg_export/lib/pg_export/operations/remove_old_dumps_from_ftp.rb +6 -6
  37. data/lib/pg_export/lib/pg_export/repositories/{ftp_dump_file_repository.rb → gateway_dump_file_repository.rb} +3 -3
  38. data/lib/pg_export/lib/pg_export/repositories/{ftp_dump_repository.rb → gateway_dump_repository.rb} +7 -7
  39. data/lib/pg_export/lib/pg_export/transactions/export_dump.rb +8 -8
  40. data/lib/pg_export/lib/pg_export/transactions/import_dump_interactively.rb +14 -14
  41. data/lib/pg_export/lib/pg_export/types.rb +1 -1
  42. data/lib/pg_export/system/boot/interactive.rb +1 -1
  43. data/lib/pg_export/system/boot/plain.rb +2 -2
  44. data/lib/pg_export/version.rb +1 -1
  45. data/pg_export.gemspec +13 -9
  46. metadata +88 -31
  47. data/lib/pg_export/lib/pg_export/factories/ftp_adapter_factory.rb +0 -22
  48. data/lib/pg_export/lib/pg_export/listeners/plain/open_ftp_connection.rb +0 -15
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ # auto_register: false
4
+
5
+ require 'ed25519'
6
+ require 'net/ssh'
7
+ require 'net/scp'
8
+
9
+ class PgExport
10
+ module Gateways
11
+ class Ssh
12
+ CHUNK_SIZE = (2**16).freeze
13
+
14
+ def initialize(host:, user:, password:)
15
+ @host, @user, @password, @logger = host, user, password
16
+ end
17
+
18
+ def open
19
+ if password.nil?
20
+ @ssh = Net::SSH.start(host, user)
21
+ else
22
+ @ssh = Net::SSH.start(host, user, password)
23
+ end
24
+ end
25
+
26
+ def welcome
27
+ open.exec!('hostname')
28
+ end
29
+
30
+ def close
31
+ @ssh&.close
32
+ end
33
+
34
+ def list(name)
35
+ grep =
36
+ if name.nil? || name.empty?
37
+ ''
38
+ else
39
+ " | grep #{name}"
40
+ end
41
+
42
+ ssh
43
+ .exec!("ls -l#{grep}")
44
+ .split("\n").map { |row| extract_meaningful_attributes(row) }
45
+ .reject { |item| item[:name].nil? }
46
+ .sort_by { |item| item[:name] }
47
+ .reverse
48
+ end
49
+
50
+ def delete(name)
51
+ # @TODO
52
+ end
53
+
54
+ def persist(file, name)
55
+ ssh.scp.upload(file.path, name).wait
56
+ end
57
+
58
+ def get(file, name)
59
+ ssh.scp.download(name, file.path).wait
60
+ end
61
+
62
+ def to_s
63
+ host
64
+ end
65
+
66
+ def ssh
67
+ @ssh ||= open
68
+ end
69
+
70
+ private
71
+
72
+ attr_reader :host, :user, :password
73
+
74
+ def extract_meaningful_attributes(item)
75
+ MEANINGFUL_KEYS.zip(item.split(' ').values_at(8, 4)).to_h
76
+ end
77
+
78
+ MEANINGFUL_KEYS = %i[name size].freeze
79
+ private_constant :MEANINGFUL_KEYS
80
+ end
81
+ end
82
+ end
@@ -6,12 +6,12 @@ class PgExport
6
6
  module Listeners
7
7
  class Interactive
8
8
  class BuildDump < InteractiveListener
9
- def on_step(step_name:, args:)
10
- @spinner = build_spinner("Dumping database #{args.first[:database_name]}")
9
+ def on_step(event)
10
+ @spinner = build_spinner("Dumping database #{event[:args].first[:database_name]}")
11
11
  end
12
12
 
13
- def on_step_succeeded(step_name:, args:, value:)
14
- @spinner.success([success, value[:dump]].join(' '))
13
+ def on_step_succeeded(event)
14
+ @spinner.success([success, event[:value][:dump]].join(' '))
15
15
  end
16
16
  end
17
17
  end
@@ -5,7 +5,7 @@ require_relative '../interactive_listener'
5
5
  class PgExport
6
6
  module Listeners
7
7
  class Interactive
8
- class CloseFtpConnection < InteractiveListener
8
+ class CloseConnection < InteractiveListener
9
9
  def on_step(*)
10
10
  @spinner = build_spinner('Closing FTP')
11
11
  end
@@ -10,8 +10,8 @@ class PgExport
10
10
  @spinner = build_spinner('Decrypting')
11
11
  end
12
12
 
13
- def on_step_succeeded(step_name:, args:, value:)
14
- @spinner.success([success, value[:dump]].join(' '))
13
+ def on_step_succeeded(event)
14
+ @spinner.success([success, event[:value][:dump]].join(' '))
15
15
  end
16
16
  end
17
17
  end
@@ -10,8 +10,8 @@ class PgExport
10
10
  @spinner = build_spinner('Downloading')
11
11
  end
12
12
 
13
- def on_step_succeeded(step_name:, args:, value:)
14
- @spinner.success([success, value[:dump]].join(' '))
13
+ def on_step_succeeded(event)
14
+ @spinner.success([success, event[:value][:dump]].join(' '))
15
15
  end
16
16
  end
17
17
  end
@@ -10,8 +10,8 @@ class PgExport
10
10
  @spinner = build_spinner('Encrypting')
11
11
  end
12
12
 
13
- def on_step_succeeded(step_name:, args:, value:)
14
- @spinner.success([success, value[:dump]].join(' '))
13
+ def on_step_succeeded(event)
14
+ @spinner.success([success, event[:value][:dump]].join(' '))
15
15
  end
16
16
  end
17
17
  end
@@ -5,7 +5,7 @@ require_relative '../interactive_listener'
5
5
  class PgExport
6
6
  module Listeners
7
7
  class Interactive
8
- class OpenFtpConnection < InteractiveListener
8
+ class OpenConnection < InteractiveListener
9
9
  def on_step(*)
10
10
  @spinner = build_spinner('Opening ftp connection')
11
11
  end
@@ -6,13 +6,13 @@ class PgExport
6
6
  module Listeners
7
7
  class Interactive
8
8
  class RemoveOldDumpsFromFtp < InteractiveListener
9
- def on_step(step_name:, args:)
10
- @spinner = build_spinner("Checking for old dumps on #{args.first[:ftp_adapter]}")
9
+ def on_step(event)
10
+ @spinner = build_spinner("Checking for old dumps on #{event[:args].first[:gateway]}")
11
11
  end
12
12
 
13
- def on_step_succeeded(step_name:, args:, value:)
14
- if value[:removed_dumps].any?
15
- @spinner.success([success, value[:removed_dumps].map { |filename| " #{filename} removed" }].join("\n"))
13
+ def on_step_succeeded(event)
14
+ if event[:value][:removed_dumps].any?
15
+ @spinner.success([success, event[:value][:removed_dumps].map { |filename| " #{filename} removed" }].join("\n"))
16
16
  else
17
17
  @spinner.success([success, 'nothing to remove'].join(' '))
18
18
  end
@@ -6,8 +6,8 @@ class PgExport
6
6
  module Listeners
7
7
  class Interactive
8
8
  class Restore < InteractiveListener
9
- def on_step(step_name:, args:)
10
- @spinner = build_spinner("Restoring dump to database #{args.first[:database]}")
9
+ def on_step(event)
10
+ @spinner = build_spinner("Restoring dump to database #{event[:args].first[:database]}")
11
11
  end
12
12
 
13
13
  def on_step_succeeded(*)
@@ -6,8 +6,8 @@ class PgExport
6
6
  module Listeners
7
7
  class Interactive
8
8
  class UploadDumpToFtp < InteractiveListener
9
- def on_step(step_name:, args:)
10
- @spinner = build_spinner("Uploading #{args.first[:dump]} to #{args.first[:ftp_adapter]}")
9
+ def on_step(event)
10
+ @spinner = build_spinner("Uploading #{event[:args].first[:dump]} to #{event[:args].first[:gateway]}")
11
11
  end
12
12
 
13
13
  def on_step_succeeded(*)
@@ -8,8 +8,8 @@ require 'tty-spinner'
8
8
  class PgExport
9
9
  module Listeners
10
10
  class InteractiveListener
11
- def on_step_failed(step_name:, args:, value:)
12
- @spinner.error([error, self.class.red(value[:message])].join("\n"))
11
+ def on_step_failed(event)
12
+ @spinner.error([error, self.class.red(event[:value][:message])].join("\n"))
13
13
  end
14
14
 
15
15
  class << self
@@ -6,8 +6,8 @@ class PgExport
6
6
  module Listeners
7
7
  class Plain
8
8
  class BuildDump < PlainListener
9
- def on_step_succeeded(step_name:, args:, value:)
10
- logger.info("Dump database #{value[:dump].database} to #{value[:dump]}")
9
+ def on_step_succeeded(event)
10
+ logger.info("Dump database #{event[:value][:dump].database} to #{event[:value][:dump]}")
11
11
  end
12
12
  end
13
13
  end
@@ -5,9 +5,9 @@ require_relative '../plain_listener'
5
5
  class PgExport
6
6
  module Listeners
7
7
  class Plain
8
- class CloseFtpConnection < PlainListener
8
+ class CloseConnection < PlainListener
9
9
  def on_step_succeeded(*)
10
- logger.info('Close FTP')
10
+ logger.info('Close connection')
11
11
  end
12
12
  end
13
13
  end
@@ -6,8 +6,8 @@ class PgExport
6
6
  module Listeners
7
7
  class Plain
8
8
  class DecryptDump < PlainListener
9
- def on_step_succeeded(step_name:, args:, value:)
10
- logger.info("Decrypt #{value[:dump]}")
9
+ def on_step_succeeded(event)
10
+ logger.info("Decrypt #{event[:value][:dump]}")
11
11
  end
12
12
  end
13
13
  end
@@ -6,8 +6,8 @@ class PgExport
6
6
  module Listeners
7
7
  class Plain
8
8
  class DownloadDumpFromFtp < PlainListener
9
- def on_step_succeeded(step_name:, args:, value:)
10
- logger.info("Download #{value[:dump]}")
9
+ def on_step_succeeded(event)
10
+ logger.info("Download #{event[:value][:dump]}")
11
11
  end
12
12
  end
13
13
  end
@@ -6,8 +6,8 @@ class PgExport
6
6
  module Listeners
7
7
  class Plain
8
8
  class EncryptDump < PlainListener
9
- def on_step_succeeded(step_name:, args:, value:)
10
- logger.info("Encrypt #{value[:dump]}")
9
+ def on_step_succeeded(event)
10
+ logger.info("Encrypt #{event[:value][:dump]}")
11
11
  end
12
12
  end
13
13
  end
@@ -6,8 +6,8 @@ class PgExport
6
6
  module Listeners
7
7
  class Plain
8
8
  class FetchDumpsFromFtp < PlainListener
9
- def on_step_succeeded(step_name:, args:, value:)
10
- logger.info("Fetch dumps (#{value[:dumps].count} items)")
9
+ def on_step_succeeded(event)
10
+ logger.info("Fetch dumps (#{event[:value][:dumps].count} items)")
11
11
  end
12
12
  end
13
13
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../plain_listener'
4
+
5
+ class PgExport
6
+ module Listeners
7
+ class Plain
8
+ class OpenConnection < PlainListener
9
+ def on_step_succeeded(event)
10
+ logger.info("Connect to #{event[:value][:gateway]}")
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -6,9 +6,9 @@ class PgExport
6
6
  module Listeners
7
7
  class Plain
8
8
  class RemoveOldDumpsFromFtp < PlainListener
9
- def on_step_succeeded(step_name:, args:, value:)
10
- value[:removed_dumps].each do |filename|
11
- logger.info("Remove #{filename} from #{value[:ftp_adapter]}")
9
+ def on_step_succeeded(event)
10
+ event[:value][:removed_dumps].each do |filename|
11
+ logger.info("Remove #{filename} from #{event[:value][:gateway]}")
12
12
  end
13
13
  end
14
14
  end
@@ -6,7 +6,7 @@ class PgExport
6
6
  module Listeners
7
7
  class Plain
8
8
  class Restore < PlainListener
9
- def on_step_succeeded(step_name:, args:, value:)
9
+ def on_step_succeeded(event)
10
10
  logger.info("Restore dump to database #{args.first[:database]}")
11
11
  end
12
12
  end
@@ -6,8 +6,8 @@ class PgExport
6
6
  module Listeners
7
7
  class Plain
8
8
  class UploadDumpToFtp < PlainListener
9
- def on_step_succeeded(step_name:, args:, value:)
10
- logger.info("Upload #{value[:dump]} to #{value[:ftp_adapter]}")
9
+ def on_step_succeeded(event)
10
+ logger.info("Upload #{event[:value][:dump]} to #{event[:value][:gateway]}")
11
11
  end
12
12
  end
13
13
  end
@@ -9,8 +9,8 @@ class PgExport
9
9
  class PlainListener
10
10
  include Import['logger']
11
11
 
12
- def on_step_failed(step_name:, args:, value:)
13
- logger.info("Error: #{value[:message]}")
12
+ def on_step_failed(event)
13
+ logger.info("Error: #{event[:value][:message]}")
14
14
  end
15
15
  end
16
16
  end
@@ -5,14 +5,14 @@ require 'pg_export/import'
5
5
 
6
6
  class PgExport
7
7
  module Operations
8
- class OpenFtpConnection
8
+ class OpenConnection
9
9
  include Dry::Transaction::Operation
10
- include Import['factories.ftp_adapter_factory']
10
+ include Import['factories.gateway_factory']
11
11
 
12
12
  def call(inputs)
13
- ftp_adapter = ftp_adapter_factory.ftp_adapter
14
- ftp_adapter.open_ftp
15
- Success(inputs.merge(ftp_adapter: ftp_adapter))
13
+ gateway = gateway_factory.gateway
14
+ gateway.open
15
+ Success(inputs.merge(gateway: gateway))
16
16
  end
17
17
  end
18
18
  end
@@ -7,19 +7,19 @@ class PgExport
7
7
  module Operations
8
8
  class RemoveOldDumpsFromFtp
9
9
  include Dry::Transaction::Operation
10
- include Import['repositories.ftp_dump_repository', 'config']
10
+ include Import['repositories.gateway_dump_repository', 'config']
11
11
 
12
- def call(dump:, ftp_adapter:)
13
- dumps = ftp_dump_repository.by_database_name(
12
+ def call(dump:, gateway:)
13
+ dumps = gateway_dump_repository.by_database_name(
14
14
  database_name: dump.database,
15
- ftp_adapter: ftp_adapter,
15
+ gateway: gateway,
16
16
  offset: config.keep_dumps
17
17
  )
18
18
  dumps.each do |d|
19
- ftp_adapter.delete(d.name)
19
+ gateway.delete(d.name)
20
20
  end
21
21
 
22
- Success(removed_dumps: dumps, ftp_adapter: ftp_adapter)
22
+ Success(removed_dumps: dumps, gateway: gateway)
23
23
  end
24
24
  end
25
25
  end
@@ -7,10 +7,10 @@ require 'pg_export/lib/pg_export/value_objects/dump_file'
7
7
 
8
8
  class PgExport
9
9
  module Repositories
10
- class FtpDumpFileRepository
11
- def by_name(name:, ftp_adapter:)
10
+ class GatewayDumpFileRepository
11
+ def by_name(name:, gateway:)
12
12
  file = ValueObjects::DumpFile.new
13
- ftp_adapter.get(file, name)
13
+ gateway.get(file, name)
14
14
 
15
15
  file
16
16
  end
@@ -7,21 +7,21 @@ require 'pg_export/lib/pg_export/value_objects/dump_file'
7
7
 
8
8
  class PgExport
9
9
  module Repositories
10
- class FtpDumpRepository
11
- def all(database_name:, ftp_adapter:)
12
- ftp_adapter.list([database_name, '*'].compact.join('_')).map do |name:, size:|
10
+ class GatewayDumpRepository
11
+ def all(database_name:, gateway:)
12
+ gateway.list(database_name).map do |item|
13
13
  begin
14
- dump(name, database_name, size)
14
+ dump(item[:name], database_name, item[:size])
15
15
  rescue Dry::Types::ConstraintError
16
16
  nil
17
17
  end
18
18
  end.compact
19
19
  end
20
20
 
21
- def by_database_name(database_name:, ftp_adapter:, offset:)
22
- ftp_adapter.list(database_name + '_*').drop(offset).map do |name:, size:|
21
+ def by_database_name(database_name:, gateway:, offset:)
22
+ gateway.list(database_name).drop(offset).map do |item|
23
23
  begin
24
- dump(name, database_name, size)
24
+ dump(item[:name], database_name, item[:size])
25
25
  rescue Dry::Types::ConstraintError
26
26
  nil
27
27
  end