pg_export 1.0.0.rc5 → 1.0.0.rc6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +19 -4
- data/CHANGELOG.md +7 -5
- data/README.md +46 -24
- data/bin/pg_export +27 -29
- data/lib/pg_export/configuration.rb +70 -18
- data/lib/pg_export/factory.rb +148 -0
- data/lib/pg_export/lib/pg_export/adapters/{bash_adapter.rb → shell_adapter.rb} +1 -1
- data/lib/pg_export/lib/pg_export/entities/dump.rb +22 -10
- data/lib/pg_export/lib/pg_export/factories/cipher_factory.rb +8 -4
- data/lib/pg_export/lib/pg_export/factories/dump_factory.rb +0 -1
- data/lib/pg_export/lib/pg_export/factories/ftp_gateway_factory.rb +7 -4
- data/lib/pg_export/lib/pg_export/factories/ssh_gateway_factory.rb +7 -4
- data/lib/pg_export/lib/pg_export/gateways/ftp.rb +1 -3
- data/lib/pg_export/lib/pg_export/gateways/ssh.rb +2 -6
- data/lib/pg_export/lib/pg_export/listeners/interactive/build_dump.rb +1 -1
- data/lib/pg_export/lib/pg_export/listeners/interactive/close_connection.rb +1 -1
- data/lib/pg_export/lib/pg_export/listeners/interactive/remove_old_dumps.rb +1 -1
- data/lib/pg_export/lib/pg_export/listeners/interactive/restore.rb +1 -1
- data/lib/pg_export/lib/pg_export/listeners/interactive/upload_dump.rb +1 -1
- data/lib/pg_export/lib/pg_export/listeners/interactive_listener.rb +4 -3
- data/lib/pg_export/lib/pg_export/listeners/plain/prepare_params.rb +1 -1
- data/lib/pg_export/lib/pg_export/listeners/plain_listener.rb +11 -5
- data/lib/pg_export/lib/pg_export/operations/decrypt_dump.rb +16 -6
- data/lib/pg_export/lib/pg_export/operations/encrypt_dump.rb +14 -5
- data/lib/pg_export/lib/pg_export/operations/open_connection.rb +14 -5
- data/lib/pg_export/lib/pg_export/operations/remove_old_dumps.rb +14 -6
- data/lib/pg_export/lib/pg_export/repositories/gateway_dump_file_repository.rb +0 -1
- data/lib/pg_export/lib/pg_export/repositories/gateway_dump_repository.rb +2 -7
- data/lib/pg_export/lib/pg_export/transactions/evaluator.rb +59 -0
- data/lib/pg_export/lib/pg_export/transactions/export_dump.rb +27 -21
- data/lib/pg_export/lib/pg_export/transactions/import_dump_interactively.rb +31 -29
- data/lib/pg_export/lib/pg_export/value_objects/dump_file.rb +2 -2
- data/lib/pg_export/lib/pg_export/value_objects/result.rb +49 -0
- data/lib/pg_export/version.rb +1 -1
- data/lib/pg_export.rb +13 -8
- data/pg_export.gemspec +1 -6
- metadata +13 -89
- data/lib/pg_export/container.rb +0 -23
- data/lib/pg_export/import.rb +0 -7
- data/lib/pg_export/lib/pg_export/types.rb +0 -14
- data/lib/pg_export/system/boot/config.rb +0 -27
- data/lib/pg_export/system/boot/ftp.rb +0 -11
- data/lib/pg_export/system/boot/interactive.rb +0 -16
- data/lib/pg_export/system/boot/operations.rb +0 -17
- data/lib/pg_export/system/boot/plain.rb +0 -16
- data/lib/pg_export/system/boot/ssh.rb +0 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ca033b9c62cc381584bd9696e9607b63d89f52310a5847b714908c7140e755dc
|
4
|
+
data.tar.gz: 755e0fa8760fa1ba6b2d3d8af65532da26b9a8978b5005853d9aaede8131d748
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
10
|
-
Max:
|
9
|
+
Layout/LineLength:
|
10
|
+
Max: 140
|
11
11
|
|
12
12
|
Style/ParallelAssignment:
|
13
13
|
Enabled: false
|
14
14
|
|
15
|
-
Naming/
|
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 -
|
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,15 +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
|
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
|
11
13
|
|
12
14
|
### 0.7.7 - 2020.09.07
|
13
15
|
|
14
|
-
- Upgrade dry-initializer
|
16
|
+
- Upgrade dry-initializer
|
15
17
|
|
16
18
|
### 0.7.6 - 2020.09.05
|
17
19
|
|
18
|
-
- Upgrade dry-types, dry-struct dry-system
|
20
|
+
- Upgrade dry-types, dry-struct dry-system
|
19
21
|
|
20
22
|
### 0.7.0 - 2018.10.18
|
21
23
|
|
@@ -41,7 +43,7 @@
|
|
41
43
|
|
42
44
|
### 0.5.0 - 2017.03.11
|
43
45
|
|
44
|
-
- 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
|
45
47
|
- Make interactive mode more verbose by adding more messages
|
46
48
|
- Fix concurrently opening ftp connection
|
47
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
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
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
|
-
=>
|
91
|
-
|
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,28 +1,27 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
|
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
|
-
require 'pg_export/version'
|
8
|
-
|
9
|
-
ENV['KEEP_DUMPS'] = ENV['KEEP_DUMPS'] || '10'
|
10
|
-
ENV['GATEWAY'] = 'ftp'
|
11
|
-
ENV['PG_EXPORT_MODE'] = 'plain'
|
12
|
-
ENV['PG_EXPORT_ENCRYPTION_ALGORITHM'] = 'AES-128-CBC'
|
13
9
|
|
14
10
|
database = nil
|
11
|
+
x = nil
|
15
12
|
|
16
13
|
option_parser = OptionParser.new do |opts|
|
17
14
|
opts.banner = <<~TXT
|
18
15
|
NAME
|
19
16
|
pg_export - CLI for exporting/importing PostgreSQL dumps via FTP/SSH
|
20
17
|
|
21
|
-
SYNOPSIS
|
22
|
-
pg_export DATABASE [
|
18
|
+
SYNOPSIS
|
19
|
+
pg_export DATABASE [OPTION..]
|
20
|
+
pg_export --interactive DATABASE [OPTION..]
|
23
21
|
|
24
|
-
|
25
|
-
|
22
|
+
EXIT VALUES
|
23
|
+
0 - Success
|
24
|
+
1 - Error
|
26
25
|
|
27
26
|
ARGUMENTS
|
28
27
|
DATABASE - database name to export (when default mode)
|
@@ -97,18 +96,11 @@ option_parser = OptionParser.new do |opts|
|
|
97
96
|
opts.separator "\nTEST RUN"
|
98
97
|
|
99
98
|
opts.on('-c', '--configuration', 'Print the configuration') do
|
100
|
-
|
101
|
-
PgExport::Container.start(:config)
|
102
|
-
puts PgExport::Container['config'].to_h
|
103
|
-
exit
|
99
|
+
x = -> { puts PgExport.new.config }
|
104
100
|
end
|
105
101
|
|
106
102
|
opts.on('-w', '--welcome', 'Try connecting to the gateway (FTP or SSH) to verify the connection') do
|
107
|
-
|
108
|
-
PgExport::Container.start(ENV['GATEWAY'].to_sym)
|
109
|
-
gateway = PgExport::Container['factories.gateway_factory'].gateway
|
110
|
-
puts gateway.welcome
|
111
|
-
exit
|
103
|
+
x = -> { puts PgExport.new.gateway_welcome }
|
112
104
|
end
|
113
105
|
|
114
106
|
if ARGV.empty?
|
@@ -119,30 +111,36 @@ end
|
|
119
111
|
|
120
112
|
begin
|
121
113
|
option_parser.parse!
|
114
|
+
if x
|
115
|
+
x.call
|
116
|
+
exit
|
117
|
+
end
|
122
118
|
database = ARGV.first unless ARGV.empty?
|
123
119
|
rescue OptionParser::ParseError => e
|
124
120
|
warn e.message.capitalize
|
125
121
|
warn 'Details:'
|
126
122
|
warn option_parser.to_s.split("\n").grep(/ #{e.args.first}/).join("\n")
|
127
123
|
warn 'Type "pg_export" for available options'
|
128
|
-
exit
|
124
|
+
exit 1
|
129
125
|
end
|
130
126
|
|
131
|
-
require 'pg_export/container'
|
132
|
-
|
133
127
|
begin
|
134
|
-
pg_export = PgExport.
|
128
|
+
pg_export = PgExport.new
|
135
129
|
rescue PgExport::InitializationError => e
|
136
130
|
warn 'Unable to initialize PgExport due to invalid configuration. Check you ENVs.'
|
137
131
|
warn "Detailed message: #{e.message}"
|
138
|
-
exit
|
132
|
+
exit 1
|
139
133
|
end
|
140
134
|
|
141
135
|
begin
|
142
|
-
pg_export
|
143
|
-
|
144
|
-
|
145
|
-
|
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
|
+
|
146
144
|
rescue Interrupt
|
147
145
|
puts
|
148
146
|
end
|
@@ -1,19 +1,19 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'pg_export/lib/pg_export/types'
|
4
|
-
require 'dry-struct'
|
5
|
-
|
6
3
|
class PgExport
|
7
|
-
class Configuration
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
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
|
15
|
+
|
16
|
+
attr_reader *ATTRS
|
17
17
|
|
18
18
|
def self.build(env)
|
19
19
|
new(
|
@@ -21,14 +21,66 @@ class PgExport
|
|
21
21
|
encryption_algorithm: env['PG_EXPORT_ENCRYPTION_ALGORITHM'],
|
22
22
|
gateway_host: env['PG_EXPORT_GATEWAY_HOST'],
|
23
23
|
gateway_user: env['PG_EXPORT_GATEWAY_USER'],
|
24
|
-
gateway_password: env['PG_EXPORT_GATEWAY_PASSWORD']
|
25
|
-
logger_format: env['LOGGER_FORMAT']
|
26
|
-
keep_dumps: env['KEEP_DUMPS']
|
24
|
+
gateway_password: env['PG_EXPORT_GATEWAY_PASSWORD'],
|
25
|
+
logger_format: env['LOGGER_FORMAT'],
|
26
|
+
keep_dumps: env['KEEP_DUMPS'],
|
27
27
|
gateway: env['GATEWAY'],
|
28
28
|
mode: env['PG_EXPORT_MODE']
|
29
29
|
)
|
30
|
-
|
31
|
-
|
30
|
+
end
|
31
|
+
|
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)
|
72
|
+
end
|
73
|
+
|
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")
|
32
84
|
end
|
33
85
|
end
|
34
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
|
@@ -1,17 +1,25 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'dry-initializer'
|
4
|
-
require 'pg_export/lib/pg_export/types'
|
5
|
-
|
6
3
|
class PgExport
|
7
4
|
module Entities
|
8
5
|
class Dump
|
9
|
-
|
6
|
+
attr_reader :name, :type, :database, :file
|
7
|
+
|
8
|
+
def initialize(name: nil, type: nil, database: nil, file: nil)
|
9
|
+
@name = String(name)
|
10
|
+
raise ArgumentError, 'Dump name must not be empty' if @name.empty?
|
11
|
+
raise ArgumentError, 'Dump name does not match criteria' unless /.+_20[0-9]{6}_[0-9]{6}\Z/.match?(@name)
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
13
|
+
@type = String(type)
|
14
|
+
@type = 'plain' if @type.empty?
|
15
|
+
raise ArgumentError, 'Dump type must be one of: plain, encrypted' unless %w[plain encrypted].include?(@type)
|
16
|
+
|
17
|
+
@database = database
|
18
|
+
|
19
|
+
@file = file
|
20
|
+
@file = ValueObjects::DumpFile.new if @file.nil?
|
21
|
+
raise ArgumentError, "Invalid file type: #{@file.class}" unless @file.is_a?(ValueObjects::DumpFile)
|
22
|
+
end
|
15
23
|
|
16
24
|
def encrypt(cipher_factory:)
|
17
25
|
self.file = file.copy(cipher: cipher_factory.encryptor)
|
@@ -32,13 +40,17 @@ class PgExport
|
|
32
40
|
end
|
33
41
|
|
34
42
|
def file=(f)
|
35
|
-
@file =
|
43
|
+
@file = f
|
44
|
+
|
45
|
+
raise ArgumentError, "Invalid file type: '#{f}'" unless @file.is_a?(ValueObjects::DumpFile)
|
36
46
|
end
|
37
47
|
|
38
48
|
protected
|
39
49
|
|
40
50
|
def type=(t)
|
41
|
-
@type =
|
51
|
+
@type = t.to_s
|
52
|
+
|
53
|
+
raise ArgumentError, "Dump type '#{t}' must be one of: plain, encrypted" unless %w[plain encrypted].include?(@type)
|
42
54
|
end
|
43
55
|
end
|
44
56
|
end
|
@@ -1,12 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'openssl'
|
4
|
-
require 'pg_export/import'
|
5
4
|
|
6
5
|
class PgExport
|
7
6
|
module Factories
|
8
7
|
class CipherFactory
|
9
|
-
|
8
|
+
def initialize(encryption_algorithm:, encryption_key:)
|
9
|
+
@encryption_algorithm = encryption_algorithm
|
10
|
+
@encryption_key = encryption_key
|
11
|
+
end
|
10
12
|
|
11
13
|
def encryptor
|
12
14
|
build_cipher(:encrypt)
|
@@ -18,10 +20,12 @@ class PgExport
|
|
18
20
|
|
19
21
|
private
|
20
22
|
|
23
|
+
attr_reader :encryption_algorithm, :encryption_key
|
24
|
+
|
21
25
|
def build_cipher(type)
|
22
|
-
cipher = OpenSSL::Cipher.new(
|
26
|
+
cipher = OpenSSL::Cipher.new(encryption_algorithm)
|
23
27
|
cipher.public_send(type)
|
24
|
-
cipher.key =
|
28
|
+
cipher.key = encryption_key
|
25
29
|
cipher
|
26
30
|
end
|
27
31
|
end
|