pg_export 1.0.0.rc3 → 1.0.0.rc6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +19 -4
  3. data/CHANGELOG.md +7 -4
  4. data/README.md +46 -24
  5. data/bin/pg_export +55 -47
  6. data/lib/pg_export/configuration.rb +69 -21
  7. data/lib/pg_export/factory.rb +148 -0
  8. data/lib/pg_export/lib/pg_export/adapters/{bash_adapter.rb → shell_adapter.rb} +1 -1
  9. data/lib/pg_export/lib/pg_export/entities/dump.rb +22 -10
  10. data/lib/pg_export/lib/pg_export/factories/cipher_factory.rb +12 -11
  11. data/lib/pg_export/lib/pg_export/factories/dump_factory.rb +0 -1
  12. data/lib/pg_export/lib/pg_export/factories/ftp_gateway_factory.rb +7 -4
  13. data/lib/pg_export/lib/pg_export/factories/ssh_gateway_factory.rb +7 -4
  14. data/lib/pg_export/lib/pg_export/gateways/ftp.rb +1 -3
  15. data/lib/pg_export/lib/pg_export/gateways/ssh.rb +8 -11
  16. data/lib/pg_export/lib/pg_export/listeners/interactive/build_dump.rb +1 -1
  17. data/lib/pg_export/lib/pg_export/listeners/interactive/close_connection.rb +1 -1
  18. data/lib/pg_export/lib/pg_export/listeners/interactive/{download_dump_from_ftp.rb → download_dump.rb} +1 -1
  19. data/lib/pg_export/lib/pg_export/listeners/interactive/{fetch_dumps_from_ftp.rb → fetch_dumps.rb} +1 -1
  20. data/lib/pg_export/lib/pg_export/listeners/interactive/open_connection.rb +1 -1
  21. data/lib/pg_export/lib/pg_export/listeners/interactive/{remove_old_dumps_from_ftp.rb → remove_old_dumps.rb} +2 -2
  22. data/lib/pg_export/lib/pg_export/listeners/interactive/restore.rb +1 -1
  23. data/lib/pg_export/lib/pg_export/listeners/interactive/select_database.rb +12 -0
  24. data/lib/pg_export/lib/pg_export/listeners/interactive/select_dump.rb +12 -0
  25. data/lib/pg_export/lib/pg_export/listeners/interactive/{upload_dump_to_ftp.rb → upload_dump.rb} +2 -2
  26. data/lib/pg_export/lib/pg_export/listeners/interactive_listener.rb +4 -3
  27. data/lib/pg_export/lib/pg_export/listeners/plain/{download_dump_from_ftp.rb → download_dump.rb} +1 -1
  28. data/lib/pg_export/lib/pg_export/listeners/plain/{fetch_dumps_from_ftp.rb → fetch_dumps.rb} +1 -1
  29. data/lib/pg_export/lib/pg_export/listeners/plain/prepare_params.rb +15 -0
  30. data/lib/pg_export/lib/pg_export/listeners/plain/{remove_old_dumps_from_ftp.rb → remove_old_dumps.rb} +1 -1
  31. data/lib/pg_export/lib/pg_export/listeners/plain/{upload_dump_to_ftp.rb → upload_dump.rb} +1 -1
  32. data/lib/pg_export/lib/pg_export/listeners/plain_listener.rb +11 -5
  33. data/lib/pg_export/lib/pg_export/operations/decrypt_dump.rb +16 -6
  34. data/lib/pg_export/lib/pg_export/operations/encrypt_dump.rb +14 -5
  35. data/lib/pg_export/lib/pg_export/operations/open_connection.rb +14 -5
  36. data/lib/pg_export/lib/pg_export/operations/remove_old_dumps.rb +34 -0
  37. data/lib/pg_export/lib/pg_export/repositories/gateway_dump_file_repository.rb +0 -1
  38. data/lib/pg_export/lib/pg_export/repositories/gateway_dump_repository.rb +2 -7
  39. data/lib/pg_export/lib/pg_export/transactions/evaluator.rb +59 -0
  40. data/lib/pg_export/lib/pg_export/transactions/export_dump.rb +28 -22
  41. data/lib/pg_export/lib/pg_export/transactions/import_dump_interactively.rb +35 -33
  42. data/lib/pg_export/lib/pg_export/value_objects/dump_file.rb +2 -2
  43. data/lib/pg_export/lib/pg_export/value_objects/result.rb +49 -0
  44. data/lib/pg_export/version.rb +1 -1
  45. data/lib/pg_export.rb +12 -15
  46. data/pg_export.gemspec +2 -7
  47. metadata +27 -99
  48. data/lib/pg_export/build_logger.rb +0 -21
  49. data/lib/pg_export/container.rb +0 -57
  50. data/lib/pg_export/import.rb +0 -7
  51. data/lib/pg_export/lib/pg_export/operations/remove_old_dumps_from_ftp.rb +0 -26
  52. data/lib/pg_export/lib/pg_export/types.rb +0 -14
  53. data/lib/pg_export/system/boot/config.rb +0 -11
  54. data/lib/pg_export/system/boot/interactive.rb +0 -32
  55. data/lib/pg_export/system/boot/logger.rb +0 -15
  56. data/lib/pg_export/system/boot/plain.rb +0 -33
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 97c43da7e8d06b79d2a09c0d4f41f7a9ffcb7766ec3129f8fa7323147af23b28
4
- data.tar.gz: 4bcf143f440f6ffbcce3b456d6afa9e4d84f05d545aaa2199706931c757068c8
3
+ metadata.gz: ca033b9c62cc381584bd9696e9607b63d89f52310a5847b714908c7140e755dc
4
+ data.tar.gz: 755e0fa8760fa1ba6b2d3d8af65532da26b9a8978b5005853d9aaede8131d748
5
5
  SHA512:
6
- metadata.gz: 39acecf631b54e5216e65b3d8db6f13fbd0dc3042bf1a86ecc85d3edfd774a83fed62784d99f85859436661763ffab59ab1b94072fb51bad35a47ea581b77840
7
- data.tar.gz: f7d2bce1bdc045fad6841806c195ff1c7385f5fc516a9af3933e2260f62f6cc4efbbeca1321ac1a75b794ed45e57dc900a0967897672e5e00ec602705c5f39cb
6
+ metadata.gz: 01a50e23dff7a059c606aaff6ea744f3ee3da607ffcf5062514c447a0f9413386a7df378ae7467ff9e35745b65d5a78fa96c6e9abf93b6cff2d8dc4fa9be5e55
7
+ data.tar.gz: a32e7517a57dcc5eee39c40334ea5552709930d2e5bd402eaf55e67c96625b528aa40f99ef2ee5cdbacae8ad052250d2122f87eb83705ab85fa41599a755fff0
data/.rubocop.yml CHANGED
@@ -6,17 +6,32 @@ Metrics/BlockLength:
6
6
  - 'bin/pg_export'
7
7
  ExcludedMethods: ['describe', 'context']
8
8
 
9
- Metrics/LineLength:
10
- Max: 200
9
+ Layout/LineLength:
10
+ Max: 140
11
11
 
12
12
  Style/ParallelAssignment:
13
13
  Enabled: false
14
14
 
15
- Naming/UncommunicativeMethodParamName:
15
+ Naming/MethodParameterName:
16
16
  Enabled: false
17
17
 
18
- Documentation:
18
+ Style/Documentation:
19
19
  Enabled: false
20
20
 
21
21
  Lint/UnusedMethodArgument:
22
22
  Enabled: false
23
+
24
+ Metrics/MethodLength:
25
+ Max: 50
26
+
27
+ Metrics/CyclomaticComplexity:
28
+ Max: 30
29
+
30
+ Style/TrailingCommaInHashLiteral:
31
+ Enabled: false
32
+
33
+ Metrics/AbcSize:
34
+ Max: 30
35
+
36
+ Metrics/PerceivedComplexity:
37
+ Max: 30
data/CHANGELOG.md CHANGED
@@ -1,4 +1,4 @@
1
- ### 1.0.0 - 2021.03.20
1
+ ### 1.0.0 - 2022.05.17
2
2
  - Make it compatible with Ruby 3.0
3
3
  - Change configuration envs:
4
4
  - BACKUP_FTP_HOST -> PG_EXPORT_GATEWAY_HOST
@@ -7,14 +7,17 @@
7
7
  - DUMP_ENCRYPTION_KEY -> PG_EXPORT_ENCRYPTION_KEY
8
8
  - Drop Ruby 2.3 support
9
9
  - Add SSH option
10
+ - Add `encryption_algorithm` option to interface
11
+ - Remove dry libraries dependencies
12
+ - in case of failure bin/pg_export now returns with 1 exit value
10
13
 
11
14
  ### 0.7.7 - 2020.09.07
12
15
 
13
- - Upgrade dry-initializer
16
+ - Upgrade dry-initializer
14
17
 
15
18
  ### 0.7.6 - 2020.09.05
16
19
 
17
- - Upgrade dry-types, dry-struct dry-system
20
+ - Upgrade dry-types, dry-struct dry-system
18
21
 
19
22
  ### 0.7.0 - 2018.10.18
20
23
 
@@ -40,7 +43,7 @@
40
43
 
41
44
  ### 0.5.0 - 2017.03.11
42
45
 
43
- - Add restriction on DUMP_ENCRYPTION_KEY, to be exactly 16 characters length
46
+ - Add restriction on DUMP_ENCRYPTION_KEY, to be exactly 16 characters length
44
47
  - Make interactive mode more verbose by adding more messages
45
48
  - Fix concurrently opening ftp connection
46
49
  - Add closing ftp connection while importing dump in interactive mode
data/README.md CHANGED
@@ -11,7 +11,7 @@ Can be used for backups or synchronizing databases between production and develo
11
11
  Example:
12
12
 
13
13
  pg_export --database database_name --keep 5
14
-
14
+
15
15
  Above command will perform database dump, encrypt it, upload it to FTP and remove old dumps from FTP, keeping newest 5.
16
16
 
17
17
  FTP connection params and encryption key are configured by env variables.
@@ -50,20 +50,38 @@ Or install it yourself as:
50
50
 
51
51
  $ pg_export -h
52
52
 
53
- Usage: pg_export [options]
54
- -g, --gateway GATEWAY [Optional] ssh or ftp (default: ftp)
55
- -d, --database DATABASE [Required] Name of the database to export
56
- -k, --keep [KEEP] [Optional] Number of dump files to keep on FTP (default: 10)
57
- -t, --timestamped [Optional] Enables log messages with timestamps
58
- -m, --muted [Optional] Mutes log messages (overrides -t option)
59
- -i, --interactive [Optional] Interactive command line mode - for restoring dumps into databases
60
- -v, --version Show version
61
- -h, --help Show this message
62
-
63
- Setting can be verified by running following commands:
64
- -c, --configuration Prints the configuration
65
- -w, --welcome Tries connecting to the gateway (FTP or SSH) to verify the connection
66
-
53
+ SYNOPSIS
54
+ pg_export DATABASE [OPTION..]
55
+ pg_export --interactive DATABASE [OPTION..]
56
+
57
+ ARGUMENTS
58
+ DATABASE - database name to export (when default mode)
59
+ - phrase to filter database dumps by (when interactive mode)
60
+
61
+ OPTIONS
62
+ -g, --gateway GATEWAY Allowed values: ftp, ssh. Default: ftp. Credentials need to be set via ENVs
63
+ -s, --ssh Same as "--gateway ssh"
64
+ -f, --ftp Same as "--gateway ftp"
65
+ -d, --database DATABASE Alternative way of specifying name of the database to export or phrase to filter by
66
+ -e, --encryption_key KEY Dumps will be SSL encrypted using this key. Should have exactly 16 characters. Overwrites PG_EXPORT_ENCRYPTION_KEY env
67
+ -a, --algorithm ALGORITHM Encryption cipher algorithm (default: AES-128-CBC). Overwrites PG_EXPORT_ENCRYPTION_ALGORITHM env. For available option see `$ openssl list -cipher-algorithms`
68
+ -k, --keep KEEP Number of dump files to keep on FTP (default: 10). Overwrites KEEP_DUMPS env
69
+ -t, --timestamped Enables log messages with timestamps
70
+ -m, --muted Mutes log messages (overrides -t option)
71
+ -i, --interactive Interactive mode, for importing dumps
72
+ -v, --version Show version
73
+ -h, --help Show this message
74
+
75
+ ENV
76
+ PG_EXPORT_GATEWAY_HOST required
77
+ PG_EXPORT_GATEWAY_USER required
78
+ PG_EXPORT_GATEWAY_PASSWORD optional when eg. authorized key is added
79
+ PG_EXPORT_ENCRYPTION_KEY required or set by --encryption_key)
80
+ PG_EXPORT_ENCRYPTION_ALGORITHM required or set by --algorithm)
81
+
82
+ TEST RUN
83
+ -c, --configuration Print the configuration
84
+ -w, --welcome Try connecting to the gateway (FTP or SSH) to verify the connection
67
85
 
68
86
  ## How to start
69
87
 
@@ -73,28 +91,32 @@ __Step 1.__ Prepare ENV variables.
73
91
  PG_EXPORT_GATEWAY_HOST=yourftp.example.com
74
92
  PG_EXPORT_GATEWAY_USER=user
75
93
  PG_EXPORT_GATEWAY_PASSWORD=password
76
-
94
+
77
95
  /* Encryption key should have exactly 16 characters. */
78
96
  /* Dumps will be SSL(AES-128-CBC) encrypted using this key. */
79
97
  PG_EXPORT_ENCRYPTION_KEY=1234567890abcdef
80
-
98
+
81
99
  /* Dumps to be kept on FTP */
82
100
  /* Optional, defaults to 10 */
83
101
  KEEP_DUMPS=5
84
-
85
- Note, that variables cannot include `#` sign, [more info](http://serverfault.com/questions/539730/environment-variable-in-etc-environment-with-pound-hash-sign-in-the-value).
102
+
103
+ Note, that variables cannot include `#` sign, [more info](http://serverfault.com/questions/539730/environment-variable-in-etc-environment-with-pound-hash-sign-in-the-value).
86
104
 
87
105
  __Step 2.__ Print the configuration to verify if env variables has been loaded properly.
88
106
 
89
107
  $ pg_export --configuration
90
- => {:dump_encryption_key=>"k4***", :gateway_host=>"yourftp.example.com", :gateway_user=>"your_gateway_user",
91
- :gateway_password=>"pass***", :logger_format=>"plain", :keep_dumps=>2}
92
-
108
+ => encryption_key k4***
109
+ gateway_host yourftp.example.com
110
+ gateway_user your_gateway_user
111
+ gateway_password pass***
112
+ logger_format plain
113
+ keep_dumps 2
114
+
93
115
  __Step 3.__ Try connecting to FTP to verify the connection.
94
116
 
95
117
  $ pg_export --gateway ftp --welcome
96
118
  => 230 User your_ftp_user logged in
97
-
119
+
98
120
  __Step 4.__ Perform database export.
99
121
 
100
122
  $ pg_export -d your_database [-k 5]
@@ -103,7 +125,7 @@ __Step 4.__ Perform database export.
103
125
  Connect to yourftp.example.com
104
126
  Upload your_database_20181016_121314 (1.34MB) to yourftp.example.com
105
127
  Close FTP
106
-
128
+
107
129
  ## How to restore a dump?
108
130
 
109
131
  Run interactive mode and follow the instructions:
data/bin/pg_export CHANGED
@@ -1,36 +1,39 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- require 'optparse'
4
+ lib = File.expand_path('../lib', __dir__)
5
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
6
 
7
+ require 'optparse'
6
8
  require 'pg_export'
7
9
 
8
- ENV['KEEP_DUMPS'] = ENV['KEEP_DUMPS'] || '10'
9
- ENV['GATEWAY'] = 'ftp'
10
-
11
- interactive = false
12
10
  database = nil
13
-
11
+ x = nil
14
12
 
15
13
  option_parser = OptionParser.new do |opts|
16
14
  opts.banner = <<~TXT
17
- NAME
18
- pg_export - CLI for exporting/importing PostgreSQL dumps via FTP/SSH
15
+ NAME
16
+ pg_export - CLI for exporting/importing PostgreSQL dumps via FTP/SSH
19
17
 
20
- SYNOPSIS (default mode)
21
- pg_export DATABASE [options]
18
+ SYNOPSIS
19
+ pg_export DATABASE [OPTION..]
20
+ pg_export --interactive DATABASE [OPTION..]
22
21
 
23
- SYNOPSIS (interactive mode)
24
- pg_export --interactive [DATABASE] [options]
22
+ EXIT VALUES
23
+ 0 - Success
24
+ 1 - Error
25
25
 
26
- ARGUMENTS
27
- DATABASE - database name to export (when default mode)
28
- - phrase to filter database dumps by (when interactive mode)
26
+ ARGUMENTS
27
+ DATABASE - database name to export (when default mode)
28
+ - phrase to filter database dumps by (when interactive mode)
29
29
 
30
- OPTIONS
30
+ OPTIONS
31
31
  TXT
32
32
 
33
- opts.on('-g', '--gateway GATEWAY', String, 'ssh or ftp (default: ftp) - credentials need to be set via ENVs') do |g|
33
+ opts.program_name = 'pg_export'
34
+ opts.version = PgExport::VERSION
35
+
36
+ opts.on('-g', '--gateway GATEWAY', %w[ftp ssh], 'Allowed values: ftp, ssh. Default: ftp. Credentials need to be set via ENVs') do |g|
34
37
  ENV['GATEWAY'] = g
35
38
  end
36
39
 
@@ -46,11 +49,15 @@ OPTIONS
46
49
  database = d
47
50
  end
48
51
 
49
- opts.on('-e', '--encryption_key KEY', String, 'Dumps will be SSL(AES-128-CBC) encrypted using this key. Should have exactly 16 characters. Overwrites PG_EXPORT_ENCRYPTION_KEY env') do |key|
52
+ opts.on('-e', '--encryption_key KEY', String, 'Dumps will be SSL encrypted using this key. Should have exactly 16 characters. Overwrites PG_EXPORT_ENCRYPTION_KEY env') do |key|
50
53
  ENV['PG_EXPORT_ENCRYPTION_KEY'] = key
51
54
  end
52
55
 
53
- opts.on('-k', '--keep KEEP', String, "Number of dump files to keep on FTP (default: 10). Overwrites KEEP_DUMPS env") do |keep|
56
+ opts.on('-a', '--algorithm ALGORITHM', String, 'Encryption cipher algorithm (default: AES-128-CBC). Overwrites PG_EXPORT_ENCRYPTION_ALGORITHM env. For available option see `$ openssl list -cipher-algorithms`') do |key|
57
+ ENV['PG_EXPORT_ENCRYPTION_KEY'] = key
58
+ end
59
+
60
+ opts.on('-k', '--keep KEEP', String, 'Number of dump files to keep on FTP (default: 10). Overwrites KEEP_DUMPS env') do |keep|
54
61
  ENV['KEEP_DUMPS'] = keep
55
62
  end
56
63
 
@@ -63,7 +70,7 @@ OPTIONS
63
70
  end
64
71
 
65
72
  opts.on('-i', '--interactive', 'Interactive mode, for importing dumps') do
66
- interactive = true
73
+ ENV['PG_EXPORT_MODE'] = 'interactive'
67
74
  end
68
75
 
69
76
  opts.on('-v', '--version', 'Show version') do
@@ -78,28 +85,22 @@ OPTIONS
78
85
 
79
86
  opts.separator <<~TXT
80
87
 
81
- ENV
82
- PG_EXPORT_GATEWAY_HOST required
83
- PG_EXPORT_GATEWAY_USER required
84
- PG_EXPORT_GATEWAY_PASSWORD optional when eg. authorized key is added
85
- PG_EXPORT_ENCRYPTION_KEY required or set by --encryption_key)
88
+ ENV
89
+ PG_EXPORT_GATEWAY_HOST required
90
+ PG_EXPORT_GATEWAY_USER required
91
+ PG_EXPORT_GATEWAY_PASSWORD optional when eg. authorized key is added
92
+ PG_EXPORT_ENCRYPTION_KEY required or set by --encryption_key)
93
+ PG_EXPORT_ENCRYPTION_ALGORITHM required or set by --algorithm)
86
94
  TXT
87
95
 
88
96
  opts.separator "\nTEST RUN"
89
97
 
90
98
  opts.on('-c', '--configuration', 'Print the configuration') do
91
- require 'pg_export/container'
92
- PgExport::Container.start(:config)
93
- puts PgExport::Container['config'].to_h
94
- exit
99
+ x = -> { puts PgExport.new.config }
95
100
  end
96
101
 
97
102
  opts.on('-w', '--welcome', 'Try connecting to the gateway (FTP or SSH) to verify the connection') do
98
- require 'pg_export/container'
99
- PgExport::Container.start(ENV['GATEWAY'].to_sym)
100
- gateway = PgExport::Container['factories.gateway_factory'].gateway
101
- puts gateway.welcome
102
- exit
103
+ x = -> { puts PgExport.new.gateway_welcome }
103
104
  end
104
105
 
105
106
  if ARGV.empty?
@@ -110,29 +111,36 @@ end
110
111
 
111
112
  begin
112
113
  option_parser.parse!
114
+ if x
115
+ x.call
116
+ exit
117
+ end
113
118
  database = ARGV.first unless ARGV.empty?
114
119
  rescue OptionParser::ParseError => e
115
120
  warn e.message.capitalize
121
+ warn 'Details:'
122
+ warn option_parser.to_s.split("\n").grep(/ #{e.args.first}/).join("\n")
116
123
  warn 'Type "pg_export" for available options'
117
- exit
124
+ exit 1
118
125
  end
119
126
 
120
- require 'pg_export/container'
121
-
122
127
  begin
123
- pg_export =
124
- if interactive
125
- PgExport.interactive
126
- else
127
- PgExport.plain
128
- end
128
+ pg_export = PgExport.new
129
129
  rescue PgExport::InitializationError => e
130
130
  warn 'Unable to initialize PgExport due to invalid configuration. Check you ENVs.'
131
131
  warn "Detailed message: #{e.message}"
132
- exit
132
+ exit 1
133
133
  end
134
134
 
135
- pg_export.call(database) do |result|
136
- result.success { puts 'Success' }
137
- result.failure { |outcome| warn outcome[:message] }
135
+ begin
136
+ pg_export
137
+ .call(database)
138
+ .on_success { exit 0 }
139
+ .on_failure do |outcome|
140
+ warn outcome[:message]
141
+ exit 1
142
+ end
143
+
144
+ rescue Interrupt
145
+ puts
138
146
  end
@@ -1,38 +1,86 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry-types'
4
- require 'dry-struct'
5
-
6
3
  class PgExport
7
- class Configuration < Dry::Struct
8
- include Dry::Types()
4
+ class Configuration
5
+ ATTRS = %i[
6
+ encryption_key
7
+ encryption_algorithm
8
+ gateway_host
9
+ gateway_user
10
+ gateway_password
11
+ logger_format
12
+ keep_dumps
13
+ gateway mode
14
+ ].freeze
9
15
 
10
- attribute :dump_encryption_key, Strict::String.constrained(size: 16)
11
- attribute :gateway_host, Strict::String
12
- attribute :gateway_user, Strict::String
13
- attribute :gateway_password, Strict::String.optional
14
- attribute :logger_format, Coercible::String.enum('plain', 'timestamped', 'muted')
15
- attribute :keep_dumps, Coercible::Integer.constrained(gteq: 0)
16
+ attr_reader *ATTRS
16
17
 
17
18
  def self.build(env)
18
19
  new(
19
- dump_encryption_key: env['PG_EXPORT_ENCRYPTION_KEY'],
20
+ encryption_key: env['PG_EXPORT_ENCRYPTION_KEY'],
21
+ encryption_algorithm: env['PG_EXPORT_ENCRYPTION_ALGORITHM'],
20
22
  gateway_host: env['PG_EXPORT_GATEWAY_HOST'],
21
23
  gateway_user: env['PG_EXPORT_GATEWAY_USER'],
22
- gateway_password: env['PG_EXPORT_GATEWAY_PASSWORD'] == '' ? nil : env['PG_EXPORT_GATEWAY_PASSWORD'],
23
- logger_format: env['LOGGER_FORMAT'] || 'plain',
24
- keep_dumps: env['KEEP_DUMPS'] || 10
24
+ gateway_password: env['PG_EXPORT_GATEWAY_PASSWORD'],
25
+ logger_format: env['LOGGER_FORMAT'],
26
+ keep_dumps: env['KEEP_DUMPS'],
27
+ gateway: env['GATEWAY'],
28
+ mode: env['PG_EXPORT_MODE']
25
29
  )
26
- rescue Dry::Struct::Error => e
27
- raise PgExport::InitializationError, e.message.gsub('[PgExport::Configuration.new] ', '')
28
30
  end
29
31
 
30
- def gateway
31
- ENV['GATEWAY'].to_sym
32
+ def initialize(
33
+ encryption_key: nil,
34
+ encryption_algorithm: nil,
35
+ gateway_host: nil,
36
+ gateway_user: nil,
37
+ gateway_password: nil,
38
+ logger_format: nil,
39
+ keep_dumps: nil,
40
+ gateway: nil,
41
+ mode: nil
42
+ )
43
+
44
+ @encryption_key = String(encryption_key)
45
+ raise ArgumentError, 'Encryption key must be 16 chars long' if @encryption_key.length != 16
46
+
47
+ @encryption_algorithm = String(encryption_algorithm)
48
+ @encryption_algorithm = 'AES-128-CBC' if @encryption_algorithm.empty?
49
+
50
+ @gateway_host = String(gateway_host)
51
+ raise ArgumentError, 'Gatway host must not be empty' if @gateway_host.empty?
52
+
53
+ @gateway_user = String(gateway_user)
54
+ raise ArgumentError, 'Gatway user must not be empty' if @gateway_user.empty?
55
+
56
+ @gateway_password = nil if gateway_password.nil? || gateway_password.to_s.empty?
57
+
58
+ @logger_format = logger_format.to_s.to_sym
59
+ @logger_format = :plain if @logger_format.empty?
60
+ raise ArgumentError, 'Logger format must be one of: plain, timestamped, muted' unless %i[plain timestamped muted].include?(@logger_format)
61
+
62
+ @keep_dumps = Integer(keep_dumps || 10)
63
+ raise ArgumentError, 'Keep dumps must greater or equal to 1' unless @keep_dumps >= 1
64
+
65
+ @gateway = gateway.to_s.to_sym
66
+ @gateway = :ftp if @gateway.empty?
67
+ raise ArgumentError, 'Gateway must be one of: ftp, ssh' unless %i[ftp ssh].include?(@gateway)
68
+
69
+ @mode = mode.to_s.to_sym
70
+ @mode = :plain if @mode.empty?
71
+ raise ArgumentError, 'Mode must be one of: plain, interactive' unless %i[plain interactive].include?(@mode)
32
72
  end
33
73
 
34
- def logger_muted?
35
- logger_format == 'muted'
74
+ def to_s
75
+ ATTRS.map do |name|
76
+ value = public_send(name)
77
+
78
+ if %i[encryption_key gateway_password].include?(name)
79
+ " #{name} #{value.nil? ? '' : value[0..2] + '***'}"
80
+ else
81
+ " #{name} #{value}"
82
+ end
83
+ end.join("\n")
36
84
  end
37
85
  end
38
86
  end
@@ -0,0 +1,148 @@
1
+ # frozen_string_literal: true
2
+
3
+ class PgExport
4
+ class Factory
5
+ attr_reader :config
6
+
7
+ def initialize(config:)
8
+ @config = config
9
+ end
10
+
11
+ def gateway_factory
12
+ if config.gateway == :ftp
13
+ require 'pg_export/lib/pg_export/factories/ftp_gateway_factory'
14
+
15
+ Factories::FtpGatewayFactory.new(config: config)
16
+ elsif config.gateway == :ssh
17
+ require 'pg_export/lib/pg_export/factories/ssh_gateway_factory'
18
+
19
+ Factories::SshGatewayFactory.new(config: config)
20
+ else
21
+ raise ArgumentError, "Unknown gateway #{config.gateway}"
22
+ end
23
+ end
24
+
25
+ def transaction
26
+ require 'pg_export/lib/pg_export/operations/encrypt_dump'
27
+ require 'pg_export/lib/pg_export/operations/decrypt_dump'
28
+ require 'pg_export/lib/pg_export/operations/open_connection'
29
+ require 'pg_export/lib/pg_export/factories/cipher_factory'
30
+ require 'pg_export/lib/pg_export/factories/dump_factory'
31
+ require 'pg_export/lib/pg_export/adapters/shell_adapter'
32
+ require 'pg_export/lib/pg_export/repositories/gateway_dump_repository'
33
+ require 'pg_export/lib/pg_export/repositories/gateway_dump_file_repository'
34
+
35
+ cipher_factory = Factories::CipherFactory.new(
36
+ encryption_algorithm: config.encryption_algorithm,
37
+ encryption_key: config.encryption_key
38
+ )
39
+
40
+ gateway_dump_repository = Repositories::GatewayDumpRepository.new
41
+ gateway_dump_file_repository = Repositories::GatewayDumpFileRepository.new
42
+ open_connection = Operations::OpenConnection.new(gateway_factory: gateway_factory)
43
+ bash_adapter = Adapters::ShellAdapter.new
44
+
45
+ encrypt_dump = Operations::EncryptDump.new(cipher_factory: cipher_factory)
46
+
47
+ if config.mode == :plain
48
+ require 'pg_export/lib/pg_export/operations/remove_old_dumps'
49
+ require 'pg_export/lib/pg_export/transactions/export_dump'
50
+
51
+ remove_old_dumps = Operations::RemoveOldDumps.new(
52
+ gateway_dump_repository: gateway_dump_repository,
53
+ keep: config.keep_dumps
54
+ )
55
+
56
+ PgExport::Transactions::ExportDump.new(
57
+ dump_factory: Factories::DumpFactory.new,
58
+ bash_adapter: bash_adapter,
59
+ encrypt_dump: encrypt_dump,
60
+ open_connection: open_connection,
61
+ remove_old_dumps: remove_old_dumps,
62
+ listeners: plain_listeners
63
+ )
64
+ elsif config.mode == :interactive
65
+ require 'pg_export/lib/pg_export/transactions/import_dump_interactively'
66
+ require 'pg_export/lib/pg_export/ui/interactive/input'
67
+
68
+ decrypt_dump = Operations::DecryptDump.new(cipher_factory: cipher_factory)
69
+
70
+ PgExport::Transactions::ImportDumpInteractively.new(
71
+ input: Ui::Interactive::Input.new,
72
+ bash_adapter: bash_adapter,
73
+ gateway_dump_file_repository: gateway_dump_file_repository,
74
+ gateway_dump_repository: gateway_dump_repository,
75
+ open_connection: open_connection,
76
+ decrypt_dump: decrypt_dump,
77
+ listeners: interactive_listeners
78
+ )
79
+ else
80
+ raise ArgumentError, "Unknown mode #{config.mode}"
81
+ end
82
+ end
83
+
84
+ private
85
+
86
+ def plain_listeners
87
+ require 'pg_export/lib/pg_export/listeners/plain/prepare_params'
88
+ require 'pg_export/lib/pg_export/listeners/plain/build_dump'
89
+ require 'pg_export/lib/pg_export/listeners/plain/close_connection'
90
+ require 'pg_export/lib/pg_export/listeners/plain/decrypt_dump'
91
+ require 'pg_export/lib/pg_export/listeners/plain/download_dump'
92
+ require 'pg_export/lib/pg_export/listeners/plain/encrypt_dump'
93
+ require 'pg_export/lib/pg_export/listeners/plain/fetch_dumps'
94
+ require 'pg_export/lib/pg_export/listeners/plain/open_connection'
95
+ require 'pg_export/lib/pg_export/listeners/plain/remove_old_dumps'
96
+ require 'pg_export/lib/pg_export/listeners/plain/restore'
97
+ require 'pg_export/lib/pg_export/listeners/plain/upload_dump'
98
+
99
+ logger = build_logger(config.logger_format)
100
+
101
+ {
102
+ prepare_params: Listeners::Plain::PrepareParams.new(logger: logger),
103
+ build_dump: Listeners::Plain::BuildDump.new(logger: logger),
104
+ encrypt_dump: Listeners::Plain::EncryptDump.new(logger: logger),
105
+ open_connection: Listeners::Plain::OpenConnection.new(logger: logger),
106
+ upload_dump: Listeners::Plain::UploadDump.new(logger: logger),
107
+ remove_old_dumps: Listeners::Plain::RemoveOldDumps.new(logger: logger),
108
+ close_connection: Listeners::Plain::CloseConnection.new(logger: logger)
109
+ }
110
+ end
111
+
112
+ def interactive_listeners
113
+ require 'pg_export/lib/pg_export/listeners/interactive/open_connection'
114
+ require 'pg_export/lib/pg_export/listeners/interactive/fetch_dumps'
115
+ require 'pg_export/lib/pg_export/listeners/interactive/select_dump'
116
+ require 'pg_export/lib/pg_export/listeners/interactive/download_dump'
117
+ require 'pg_export/lib/pg_export/listeners/interactive/close_connection'
118
+ require 'pg_export/lib/pg_export/listeners/interactive/decrypt_dump'
119
+ require 'pg_export/lib/pg_export/listeners/interactive/select_database'
120
+ require 'pg_export/lib/pg_export/listeners/interactive/restore'
121
+
122
+ {
123
+ open_connection: Listeners::Interactive::OpenConnection.new,
124
+ fetch_dumps: Listeners::Interactive::FetchDumps.new,
125
+ select_dump: Listeners::Interactive::SelectDump.new,
126
+ download_dump: Listeners::Interactive::DownloadDump.new,
127
+ close_connection: Listeners::Interactive::CloseConnection.new,
128
+ decrypt_dump: Listeners::Interactive::DecryptDump.new,
129
+ select_database: Listeners::Interactive::SelectDatabase.new,
130
+ restore: Listeners::Interactive::Restore.new,
131
+ }
132
+ end
133
+
134
+ def build_logger(logger_format)
135
+ require 'logger'
136
+
137
+ formatters = {
138
+ plain: ->(_, _, _, message) { "#{message}\n" },
139
+ muted: ->(*) {},
140
+ timestamped: lambda do |severity, datetime, progname, message|
141
+ "#{datetime} #{Process.pid} TID-#{Thread.current.object_id.to_s(36)}#{progname} #{severity}: #{message}\n"
142
+ end
143
+ }
144
+
145
+ Logger.new($stdout, formatter: formatters.fetch(logger_format))
146
+ end
147
+ end
148
+ end
@@ -4,7 +4,7 @@ require 'open3'
4
4
 
5
5
  class PgExport
6
6
  module Adapters
7
- class BashAdapter
7
+ class ShellAdapter
8
8
  class PgRestoreError < StandardError; end
9
9
  class PgDumpError < StandardError; end
10
10