digitalbits-core-backup 0.0.7
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.
- checksums.yaml +7 -0
- data/CONTRIBUTING.md +58 -0
- data/Gemfile +2 -0
- data/LICENSE.txt +202 -0
- data/README.md +93 -0
- data/Rakefile +9 -0
- data/bin/digitalbits-core-backup +46 -0
- data/config/sample.yaml +8 -0
- data/digitalbits-core-backup.gemspec +26 -0
- data/lib/digitalbits-core-backup/cmd.rb +28 -0
- data/lib/digitalbits-core-backup/cmd.rb-e +28 -0
- data/lib/digitalbits-core-backup/cmd_result.rb +14 -0
- data/lib/digitalbits-core-backup/cmd_result.rb-e +14 -0
- data/lib/digitalbits-core-backup/config.rb +37 -0
- data/lib/digitalbits-core-backup/config.rb-e +37 -0
- data/lib/digitalbits-core-backup/database.rb +67 -0
- data/lib/digitalbits-core-backup/database.rb-e +67 -0
- data/lib/digitalbits-core-backup/filesystem.rb +62 -0
- data/lib/digitalbits-core-backup/filesystem.rb-e +62 -0
- data/lib/digitalbits-core-backup/job.rb +223 -0
- data/lib/digitalbits-core-backup/job.rb-e +223 -0
- data/lib/digitalbits-core-backup/restore/database.rb +36 -0
- data/lib/digitalbits-core-backup/restore/database.rb-e +36 -0
- data/lib/digitalbits-core-backup/restore/filesystem.rb +36 -0
- data/lib/digitalbits-core-backup/restore/filesystem.rb-e +36 -0
- data/lib/digitalbits-core-backup/s3.rb +64 -0
- data/lib/digitalbits-core-backup/s3.rb-e +64 -0
- data/lib/digitalbits-core-backup/tar.rb +37 -0
- data/lib/digitalbits-core-backup/tar.rb-e +37 -0
- data/lib/digitalbits-core-backup/utils.rb +197 -0
- data/lib/digitalbits-core-backup/utils.rb-e +197 -0
- data/lib/digitalbits-core-backup/version.rb +3 -0
- data/lib/digitalbits-core-backup/version.rb-e +3 -0
- data/lib/digitalbits-core-backup.rb +21 -0
- metadata +147 -0
@@ -0,0 +1,223 @@
|
|
1
|
+
module DigitalbitsCoreBackup
|
2
|
+
class Job
|
3
|
+
|
4
|
+
class NoConfig < StandardError ; end
|
5
|
+
|
6
|
+
def initialize(**args)
|
7
|
+
if args.has_key?(:config) then
|
8
|
+
@config = DigitalbitsCoreBackup::Config.new(args[:config])
|
9
|
+
else
|
10
|
+
puts "info: no config provided"
|
11
|
+
raise NoConfig
|
12
|
+
end
|
13
|
+
|
14
|
+
# Set run time options
|
15
|
+
@verify = args[:verify] if args.has_key?(:verify)
|
16
|
+
@clean = args[:clean] if args.has_key?(:clean)
|
17
|
+
@listlen = args[:listlen]
|
18
|
+
|
19
|
+
# Set common run time parameters
|
20
|
+
@job_type = args[:type]
|
21
|
+
@gpg_key = @config.get('gpg_key')
|
22
|
+
@working_dir = DigitalbitsCoreBackup::Utils.create_working_dir(@config.get('working_dir'))
|
23
|
+
@cmd = DigitalbitsCoreBackup::Cmd.new(@working_dir)
|
24
|
+
@select = args[:select] if args.has_key?(:select)
|
25
|
+
@s3 = DigitalbitsCoreBackup::S3.new(@config)
|
26
|
+
@pushgateway_url = @config.get('pushgateway_url')
|
27
|
+
|
28
|
+
# Set per operation type run time parameters
|
29
|
+
if args.has_key?(:type) then
|
30
|
+
case args[:type]
|
31
|
+
when 'backup'
|
32
|
+
puts 'info: backing up digitalbits-core'
|
33
|
+
DigitalbitsCoreBackup::Utils.push_metric(@pushgateway_url, 'digitalbits_core_backup_start_time')
|
34
|
+
@backup_dir = DigitalbitsCoreBackup::Utils.create_backup_dir(@config.get('backup_dir'))
|
35
|
+
@db = DigitalbitsCoreBackup::Database.new(@config)
|
36
|
+
@fs = DigitalbitsCoreBackup::Filesystem.new(@config)
|
37
|
+
when 'restore'
|
38
|
+
puts 'info: restoring digitalbits-core'
|
39
|
+
DigitalbitsCoreBackup::Utils.push_metric(@pushgateway_url, 'digitalbits_core_restore_start_time')
|
40
|
+
@db_restore = DigitalbitsCoreBackup::Restore::Database.new(@config)
|
41
|
+
@fs_restore = DigitalbitsCoreBackup::Restore::Filesystem.new(@config)
|
42
|
+
@utils = DigitalbitsCoreBackup::Utils.new(@config)
|
43
|
+
when 'getkey'
|
44
|
+
puts 'info: confirming public gpg key with key server'
|
45
|
+
when 'list'
|
46
|
+
puts "info: listing last #{@listlen} digitalbits-core backups"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def run()
|
52
|
+
case @job_type
|
53
|
+
when 'getkey'
|
54
|
+
begin
|
55
|
+
getkey = @cmd.run_and_capture('gpg', ['--keyserver', 'hkp://ipv4.pool.sks-keyservers.net', '--recv-key', @gpg_key, '2>&1'])
|
56
|
+
puts 'info: public gpg key installed'
|
57
|
+
if ! getkey.success then
|
58
|
+
puts "error: failed to get gpg key"
|
59
|
+
# dump the gpg output here for user level trouble shooting
|
60
|
+
puts "#{getkey.out}"
|
61
|
+
raise StandardError
|
62
|
+
end
|
63
|
+
rescue => e
|
64
|
+
puts e
|
65
|
+
end
|
66
|
+
when 'list'
|
67
|
+
begin
|
68
|
+
list=@s3.latest(@listlen)
|
69
|
+
puts "info: only #{list.length} backup files in bucket" if list.length < @listlen
|
70
|
+
puts list
|
71
|
+
rescue => e
|
72
|
+
puts e
|
73
|
+
end
|
74
|
+
when 'backup'
|
75
|
+
begin
|
76
|
+
if !DigitalbitsCoreBackup::Utils.core_healthy?(@config) then
|
77
|
+
puts "error: Can't back up unhealthy digitalbits-core"
|
78
|
+
raise StandardError
|
79
|
+
end
|
80
|
+
puts 'info: stopping digitalbits-core'
|
81
|
+
# using sudo, if running as non root uid then you will need to configure sudoers
|
82
|
+
stop_core = @cmd.run_and_capture('sudo', ['/bin/systemctl', 'stop', 'digitalbits-core'])
|
83
|
+
# only proceed if core is stopped
|
84
|
+
if stop_core.success then
|
85
|
+
DigitalbitsCoreBackup::Utils.push_metric(@pushgateway_url, 'digitalbits_core_backup_db_dump_start_time')
|
86
|
+
@db.backup
|
87
|
+
DigitalbitsCoreBackup::Utils.push_metric(@pushgateway_url, 'digitalbits_core_backup_db_dump_finish_time')
|
88
|
+
DigitalbitsCoreBackup::Utils.push_metric(@pushgateway_url, 'digitalbits_core_backup_fs_backup_start_time')
|
89
|
+
@fs.backup
|
90
|
+
DigitalbitsCoreBackup::Utils.push_metric(@pushgateway_url, 'digitalbits_core_backup_fs_backup_finish_time')
|
91
|
+
if @verify then
|
92
|
+
DigitalbitsCoreBackup::Utils.push_metric(@pushgateway_url, 'digitalbits_core_backup_verify_start_time')
|
93
|
+
create_hash_file = @cmd.run_and_capture('find', ['.', '-type', 'f', '!', '-name', 'SHA256SUMS', '|', 'xargs', 'sha256sum', '>', 'SHA256SUMS'])
|
94
|
+
if create_hash_file.success then
|
95
|
+
puts "info: sha sums file created"
|
96
|
+
else
|
97
|
+
puts 'error: error creating sha sums file'
|
98
|
+
raise StandardError
|
99
|
+
end
|
100
|
+
sign_hash_file = @cmd.run_and_capture('gpg', ['--local-user', @gpg_key, '--detach-sign', 'SHA256SUMS'])
|
101
|
+
if sign_hash_file.success then
|
102
|
+
puts "info: gpg signature created ok"
|
103
|
+
DigitalbitsCoreBackup::Utils.push_metric(@pushgateway_url, 'digitalbits_core_backup_verify_finish_time')
|
104
|
+
else
|
105
|
+
puts 'error: error signing sha256sum file'
|
106
|
+
raise StandardError
|
107
|
+
end
|
108
|
+
end
|
109
|
+
# create tar archive with fs, db backup files and if requested the file of shas256sums and corresponding gpg signature.
|
110
|
+
DigitalbitsCoreBackup::Utils.push_metric(@pushgateway_url, 'digitalbits_core_backup_tar_start_time')
|
111
|
+
@backup = DigitalbitsCoreBackup::Utils.create_backup_tar(@working_dir, @backup_dir)
|
112
|
+
DigitalbitsCoreBackup::Utils.push_metric(@pushgateway_url, 'digitalbits_core_backup_tar_finish_time')
|
113
|
+
DigitalbitsCoreBackup::Utils.push_metric(@pushgateway_url, 'digitalbits_core_backup_s3_push_start_time')
|
114
|
+
@s3.push(@backup)
|
115
|
+
DigitalbitsCoreBackup::Utils.push_metric(@pushgateway_url, 'digitalbits_core_backup_s3_push_finish_time')
|
116
|
+
else
|
117
|
+
puts 'error: can not stop digitalbits-core'
|
118
|
+
raise StandardError
|
119
|
+
end
|
120
|
+
|
121
|
+
# restart digitalbits-core post backup
|
122
|
+
puts 'info: starting digitalbits-core'
|
123
|
+
# using sudo, if running as non root uid then you will need to configure sudoers
|
124
|
+
start_core = @cmd.run_and_capture('sudo', ['/bin/systemctl', 'start', 'digitalbits-core'])
|
125
|
+
if start_core.success then
|
126
|
+
puts "info: digitalbits-core started"
|
127
|
+
else
|
128
|
+
puts 'error: can not start digitalbits-core'
|
129
|
+
raise StandardError
|
130
|
+
end
|
131
|
+
|
132
|
+
# clean up working_dir
|
133
|
+
DigitalbitsCoreBackup::Utils.cleanup(@working_dir)
|
134
|
+
rescue => e
|
135
|
+
puts e
|
136
|
+
# clean up working_dir
|
137
|
+
DigitalbitsCoreBackup::Utils.cleanup(@working_dir)
|
138
|
+
DigitalbitsCoreBackup::Utils.push_metric(@pushgateway_url, 'digitalbits_core_backup_fail_time')
|
139
|
+
else
|
140
|
+
DigitalbitsCoreBackup::Utils.push_metric(@pushgateway_url, 'digitalbits_core_backup_success_time')
|
141
|
+
end
|
142
|
+
when 'restore'
|
143
|
+
begin
|
144
|
+
# confirm the bucket directory is set to be cleaned or is empty
|
145
|
+
# the fs_restore.core_data_dir_empty? throws an exception if it's not empty
|
146
|
+
if ! @clean then
|
147
|
+
@fs_restore.core_data_dir_empty?()
|
148
|
+
end
|
149
|
+
# using sudo, if running as non root uid then you will need to configure sudoers
|
150
|
+
stop_core = @cmd.run_and_capture('sudo', ['/bin/systemctl', 'stop', 'digitalbits-core'])
|
151
|
+
# only proceed if core is stopped
|
152
|
+
if stop_core.success then
|
153
|
+
# if no manual selection has been made, use the latest as derived from the s3.latest method
|
154
|
+
# this method returns an array so set @select to the first and only element
|
155
|
+
@select=@s3.latest(1)[0] if ! @select
|
156
|
+
DigitalbitsCoreBackup::Utils.push_metric(@pushgateway_url, 'digitalbits_core_restore_s3_get_start_time')
|
157
|
+
@backup_archive = @s3.get(@select)
|
158
|
+
DigitalbitsCoreBackup::Utils.push_metric(@pushgateway_url, 'digitalbits_core_restore_s3_get_finish_time')
|
159
|
+
DigitalbitsCoreBackup::Utils.push_metric(@pushgateway_url, 'digitalbits_core_restore_untar_start_time')
|
160
|
+
@utils.extract_backup(@backup_archive)
|
161
|
+
DigitalbitsCoreBackup::Utils.push_metric(@pushgateway_url, 'digitalbits_core_restore_untar_finish_time')
|
162
|
+
if @verify then
|
163
|
+
DigitalbitsCoreBackup::Utils.push_metric(@pushgateway_url, 'digitalbits_core_restore_verify_start_time')
|
164
|
+
verify_hash_file = @cmd.run_and_capture('gpg', ['--local-user', @gpg_key, '--verify', 'SHA256SUMS.sig', 'SHA256SUMS', '2>&1'])
|
165
|
+
if verify_hash_file.success then
|
166
|
+
puts "info: gpg signature processed ok"
|
167
|
+
else
|
168
|
+
puts 'error: error verifying gpg signature'
|
169
|
+
raise StandardError
|
170
|
+
end
|
171
|
+
verify_sha_file_content = @cmd.run_and_capture('sha256sum', ['--status', '--strict', '-c', 'SHA256SUMS'])
|
172
|
+
if verify_sha_file_content.success then
|
173
|
+
puts "info: sha file sums match"
|
174
|
+
else
|
175
|
+
puts 'error: error processing sha256sum file'
|
176
|
+
raise StandardError
|
177
|
+
end
|
178
|
+
if DigitalbitsCoreBackup::Utils.confirm_shasums_definitive(@working_dir, @backup_archive) then
|
179
|
+
puts 'info: SHA256SUMS file list matches delivered archive'
|
180
|
+
DigitalbitsCoreBackup::Utils.push_metric(@pushgateway_url, 'digitalbits_core_restore_verify_finish_time')
|
181
|
+
else
|
182
|
+
puts 'error: unknown additional file(s) detected in archive'
|
183
|
+
raise StandardError
|
184
|
+
end
|
185
|
+
end
|
186
|
+
DigitalbitsCoreBackup::Utils.cleanbucket(@fs_restore.core_data_dir) if @clean
|
187
|
+
DigitalbitsCoreBackup::Utils.push_metric(@pushgateway_url, 'digitalbits_core_restore_fs_restore_start_time')
|
188
|
+
@fs_restore.restore(@backup_archive)
|
189
|
+
DigitalbitsCoreBackup::Utils.push_metric(@pushgateway_url, 'digitalbits_core_restore_fs_restore_finish_time')
|
190
|
+
DigitalbitsCoreBackup::Utils.push_metric(@pushgateway_url, 'digitalbits_core_restore_db_restore_start_time')
|
191
|
+
@db_restore.restore()
|
192
|
+
DigitalbitsCoreBackup::Utils.push_metric(@pushgateway_url, 'digitalbits_core_restore_db_restore_finish_time')
|
193
|
+
|
194
|
+
# restart digitalbits-core post restore
|
195
|
+
puts 'info: starting digitalbits-core'
|
196
|
+
# using sudo, if running as non root uid then you will need to configure sudoers
|
197
|
+
start_core = @cmd.run_and_capture('sudo', ['/bin/systemctl', 'start', 'digitalbits-core'])
|
198
|
+
if start_core.success then
|
199
|
+
puts "info: digitalbits-core started"
|
200
|
+
else
|
201
|
+
puts 'error: can not start digitalbits-core'
|
202
|
+
raise StandardError
|
203
|
+
end
|
204
|
+
else
|
205
|
+
puts 'error: can not stop digitalbits-core'
|
206
|
+
raise StandardError
|
207
|
+
end
|
208
|
+
|
209
|
+
# clean up working_dir
|
210
|
+
DigitalbitsCoreBackup::Utils.cleanup(@working_dir)
|
211
|
+
rescue => e
|
212
|
+
puts e
|
213
|
+
# clean up working_dir
|
214
|
+
DigitalbitsCoreBackup::Utils.cleanup(@working_dir)
|
215
|
+
DigitalbitsCoreBackup::Utils.push_metric(@pushgateway_url, 'digitalbits_core_restore_fail_time')
|
216
|
+
else
|
217
|
+
DigitalbitsCoreBackup::Utils.push_metric(@pushgateway_url, 'digitalbits_core_restore_success_time')
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
end
|
223
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'pg'
|
2
|
+
|
3
|
+
module DigitalbitsCoreBackup::Restore
|
4
|
+
class Database < DigitalbitsCoreBackup::Database
|
5
|
+
include Contracts
|
6
|
+
|
7
|
+
attr_reader :dbname
|
8
|
+
|
9
|
+
Contract DigitalbitsCoreBackup::Config => Contracts::Any
|
10
|
+
def initialize(config)
|
11
|
+
@config = config
|
12
|
+
@working_dir = DigitalbitsCoreBackup::Utils.create_working_dir(@config.get('working_dir'))
|
13
|
+
@dbname = check_db_connection
|
14
|
+
@cmd = DigitalbitsCoreBackup::Cmd.new(@working_dir)
|
15
|
+
end
|
16
|
+
|
17
|
+
public
|
18
|
+
Contract nil => nil
|
19
|
+
def restore()
|
20
|
+
puts "info: database restored" if pg_restore()
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
Contract nil => Bool
|
25
|
+
def pg_restore()
|
26
|
+
# we are restoring to public schema
|
27
|
+
pg_restore = @cmd.run('pg_restore', ['-n', 'public', '--jobs', "#{DigitalbitsCoreBackup::Utils.num_cores?()}", '-c', '-d', @dbname, 'core-db/'])
|
28
|
+
if pg_restore.success then
|
29
|
+
return true
|
30
|
+
else
|
31
|
+
return false
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'pg'
|
2
|
+
|
3
|
+
module DigitalbitsCoreBackup::Restore
|
4
|
+
class Database < DigitalbitsCoreBackup::Database
|
5
|
+
include Contracts
|
6
|
+
|
7
|
+
attr_reader :dbname
|
8
|
+
|
9
|
+
Contract DigitalbitsCoreBackup::Config => Contracts::Any
|
10
|
+
def initialize(config)
|
11
|
+
@config = config
|
12
|
+
@working_dir = DigitalbitsCoreBackup::Utils.create_working_dir(@config.get('working_dir'))
|
13
|
+
@dbname = check_db_connection
|
14
|
+
@cmd = DigitalbitsCoreBackup::Cmd.new(@working_dir)
|
15
|
+
end
|
16
|
+
|
17
|
+
public
|
18
|
+
Contract nil => nil
|
19
|
+
def restore()
|
20
|
+
puts "info: database restored" if pg_restore()
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
Contract nil => Bool
|
25
|
+
def pg_restore()
|
26
|
+
# we are restoring to public schema
|
27
|
+
pg_restore = @cmd.run('pg_restore', ['-n', 'public', '--jobs', "#{DigitalbitsCoreBackup::Utils.num_cores?()}", '-c', '-d', @dbname, 'core-db/'])
|
28
|
+
if pg_restore.success then
|
29
|
+
return true
|
30
|
+
else
|
31
|
+
return false
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module DigitalbitsCoreBackup::Restore
|
2
|
+
class Filesystem < ::DigitalbitsCoreBackup::Filesystem
|
3
|
+
include Contracts
|
4
|
+
|
5
|
+
class DataDirNotEmpty < StandardError ; end
|
6
|
+
|
7
|
+
attr_reader :core_data_dir
|
8
|
+
|
9
|
+
Contract DigitalbitsCoreBackup::Config => Contracts::Any
|
10
|
+
def initialize(config)
|
11
|
+
@config = config
|
12
|
+
@working_dir = DigitalbitsCoreBackup::Utils.create_working_dir(@config.get('working_dir'))
|
13
|
+
@core_data_dir = get_core_data_dir(@config.get('core_config'))
|
14
|
+
end
|
15
|
+
|
16
|
+
public
|
17
|
+
|
18
|
+
Contract String => nil
|
19
|
+
def restore(backup_archive)
|
20
|
+
# unpack the filesystem backup
|
21
|
+
puts "info: digitalbits-core buckets restored" if DigitalbitsCoreBackup::Tar.unpack("#{@working_dir}/core-fs.tar", @core_data_dir)
|
22
|
+
end
|
23
|
+
|
24
|
+
Contract nil => Bool
|
25
|
+
def core_data_dir_empty?()
|
26
|
+
# checks fs and asks user to remove fs manually if fs is already in place.
|
27
|
+
if (Dir.entries(@core_data_dir) - %w{ . .. }).empty? then
|
28
|
+
return true
|
29
|
+
else
|
30
|
+
puts "error: #{@core_data_dir} is not empty, you can only restore to an empty data directory"
|
31
|
+
raise DataDirNotEmpty
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module DigitalbitsCoreBackup::Restore
|
2
|
+
class Filesystem < ::DigitalbitsCoreBackup::Filesystem
|
3
|
+
include Contracts
|
4
|
+
|
5
|
+
class DataDirNotEmpty < StandardError ; end
|
6
|
+
|
7
|
+
attr_reader :core_data_dir
|
8
|
+
|
9
|
+
Contract DigitalbitsCoreBackup::Config => Contracts::Any
|
10
|
+
def initialize(config)
|
11
|
+
@config = config
|
12
|
+
@working_dir = DigitalbitsCoreBackup::Utils.create_working_dir(@config.get('working_dir'))
|
13
|
+
@core_data_dir = get_core_data_dir(@config.get('core_config'))
|
14
|
+
end
|
15
|
+
|
16
|
+
public
|
17
|
+
|
18
|
+
Contract String => nil
|
19
|
+
def restore(backup_archive)
|
20
|
+
# unpack the filesystem backup
|
21
|
+
puts "info: digitalbits-core buckets restored" if DigitalbitsCoreBackup::Tar.unpack("#{@working_dir}/core-fs.tar", @core_data_dir)
|
22
|
+
end
|
23
|
+
|
24
|
+
Contract nil => Bool
|
25
|
+
def core_data_dir_empty?()
|
26
|
+
# checks fs and asks user to remove fs manually if fs is already in place.
|
27
|
+
if (Dir.entries(@core_data_dir) - %w{ . .. }).empty? then
|
28
|
+
return true
|
29
|
+
else
|
30
|
+
puts "error: #{@core_data_dir} is not empty, you can only restore to an empty data directory"
|
31
|
+
raise DataDirNotEmpty
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'aws-sdk-s3'
|
2
|
+
|
3
|
+
module DigitalbitsCoreBackup
|
4
|
+
class S3
|
5
|
+
include Contracts
|
6
|
+
|
7
|
+
Contract DigitalbitsCoreBackup::Config => Contracts::Any
|
8
|
+
def initialize(config)
|
9
|
+
@config = config
|
10
|
+
@working_dir = DigitalbitsCoreBackup::Utils.create_working_dir(@config.get('working_dir'))
|
11
|
+
@s3_region = @config.get('s3_region')
|
12
|
+
@s3_bucket = @config.get('s3_bucket')
|
13
|
+
@s3_path = @config.get('s3_path')
|
14
|
+
begin
|
15
|
+
@s3_client = Aws::S3::Client.new(region: @s3_region)
|
16
|
+
@s3_resource = Aws::S3::Resource.new(client: @s3_client)
|
17
|
+
rescue Aws::S3::Errors::ServiceError => e
|
18
|
+
puts "info: error connecting to s3"
|
19
|
+
puts e
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def push(file)
|
24
|
+
begin
|
25
|
+
upload = @s3_resource.bucket(@s3_bucket).object("#{@s3_path}/#{File.basename(file)}")
|
26
|
+
if upload.upload_file(file) then
|
27
|
+
puts "info: pushed #{file} to s3 (#{@s3_bucket})"
|
28
|
+
else
|
29
|
+
puts "error: upload to s3 failed"
|
30
|
+
end
|
31
|
+
rescue Aws::S3::Errors::ServiceError => e
|
32
|
+
puts "info: error pushing #{file} to s3 (#{@s3_bucket})"
|
33
|
+
puts e
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# fetches a backup tar file from s3, places in working dir
|
38
|
+
def get(file)
|
39
|
+
local_copy = "#{@working_dir}/#{File.basename(file)}"
|
40
|
+
begin
|
41
|
+
download = @s3_resource.bucket(@s3_bucket).object(file)
|
42
|
+
if download.download_file(local_copy) then
|
43
|
+
puts "info: fetched #{file} from s3 (#{@s3_bucket})"
|
44
|
+
return local_copy
|
45
|
+
else
|
46
|
+
puts "error: download from s3 failed"
|
47
|
+
end
|
48
|
+
rescue Aws::S3::Errors::ServiceError => e
|
49
|
+
puts "info: error downloading #{file} from s3 (#{@s3_bucket})"
|
50
|
+
puts e
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def latest(listlen)
|
55
|
+
begin
|
56
|
+
@s3_client.list_objects_v2({bucket: @s3_bucket, prefix: @s3_path+'/core-backup-'}).contents.map{|o| o.key}.sort{|a,b| a.gsub(/(\d+)/,'\1') <=> b.gsub(/(\d+)/,'\1')}.last(listlen)
|
57
|
+
rescue Aws::S3::Errors::ServiceError => e
|
58
|
+
puts "info: error listing s3 (#{@s3_bucket})"
|
59
|
+
puts e
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'aws-sdk-s3'
|
2
|
+
|
3
|
+
module DigitalbitsCoreBackup
|
4
|
+
class S3
|
5
|
+
include Contracts
|
6
|
+
|
7
|
+
Contract DigitalbitsCoreBackup::Config => Contracts::Any
|
8
|
+
def initialize(config)
|
9
|
+
@config = config
|
10
|
+
@working_dir = DigitalbitsCoreBackup::Utils.create_working_dir(@config.get('working_dir'))
|
11
|
+
@s3_region = @config.get('s3_region')
|
12
|
+
@s3_bucket = @config.get('s3_bucket')
|
13
|
+
@s3_path = @config.get('s3_path')
|
14
|
+
begin
|
15
|
+
@s3_client = Aws::S3::Client.new(region: @s3_region)
|
16
|
+
@s3_resource = Aws::S3::Resource.new(client: @s3_client)
|
17
|
+
rescue Aws::S3::Errors::ServiceError => e
|
18
|
+
puts "info: error connecting to s3"
|
19
|
+
puts e
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def push(file)
|
24
|
+
begin
|
25
|
+
upload = @s3_resource.bucket(@s3_bucket).object("#{@s3_path}/#{File.basename(file)}")
|
26
|
+
if upload.upload_file(file) then
|
27
|
+
puts "info: pushed #{file} to s3 (#{@s3_bucket})"
|
28
|
+
else
|
29
|
+
puts "error: upload to s3 failed"
|
30
|
+
end
|
31
|
+
rescue Aws::S3::Errors::ServiceError => e
|
32
|
+
puts "info: error pushing #{file} to s3 (#{@s3_bucket})"
|
33
|
+
puts e
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# fetches a backup tar file from s3, places in working dir
|
38
|
+
def get(file)
|
39
|
+
local_copy = "#{@working_dir}/#{File.basename(file)}"
|
40
|
+
begin
|
41
|
+
download = @s3_resource.bucket(@s3_bucket).object(file)
|
42
|
+
if download.download_file(local_copy) then
|
43
|
+
puts "info: fetched #{file} from s3 (#{@s3_bucket})"
|
44
|
+
return local_copy
|
45
|
+
else
|
46
|
+
puts "error: download from s3 failed"
|
47
|
+
end
|
48
|
+
rescue Aws::S3::Errors::ServiceError => e
|
49
|
+
puts "info: error downloading #{file} from s3 (#{@s3_bucket})"
|
50
|
+
puts e
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def latest(listlen)
|
55
|
+
begin
|
56
|
+
@s3_client.list_objects_v2({bucket: @s3_bucket, prefix: @s3_path+'/core-backup-'}).contents.map{|o| o.key}.sort{|a,b| a.gsub(/(\d+)/,'\1') <=> b.gsub(/(\d+)/,'\1')}.last(listlen)
|
57
|
+
rescue Aws::S3::Errors::ServiceError => e
|
58
|
+
puts "info: error listing s3 (#{@s3_bucket})"
|
59
|
+
puts e
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module DigitalbitsCoreBackup
|
4
|
+
class Tar
|
5
|
+
include Contracts
|
6
|
+
|
7
|
+
Contract String, String => String
|
8
|
+
def self.pack(archive, directory)
|
9
|
+
if DigitalbitsCoreBackup::Utils.readable?(directory) then
|
10
|
+
# archive directory
|
11
|
+
puts "info: packing #{directory} in #{archive}"
|
12
|
+
%x{/bin/tar --create --file=#{archive} #{directory}}
|
13
|
+
if $?.exitstatus == 0 then
|
14
|
+
puts "info: #{archive} created"
|
15
|
+
return archive
|
16
|
+
else
|
17
|
+
raise StandardError
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
Contract String, String => String
|
23
|
+
def self.unpack(archive, destination)
|
24
|
+
if DigitalbitsCoreBackup::Utils.writable?(destination) then
|
25
|
+
# extract archive in destination directory
|
26
|
+
puts "info: unpacking #{archive} in #{destination}"
|
27
|
+
%x{/bin/tar --extract --file=#{archive} --directory=#{destination}}
|
28
|
+
if $?.exitstatus == 0 then
|
29
|
+
puts "info: #{archive} unpacked in #{destination}"
|
30
|
+
return destination
|
31
|
+
else
|
32
|
+
raise StandardError
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module DigitalbitsCoreBackup
|
4
|
+
class Tar
|
5
|
+
include Contracts
|
6
|
+
|
7
|
+
Contract String, String => String
|
8
|
+
def self.pack(archive, directory)
|
9
|
+
if DigitalbitsCoreBackup::Utils.readable?(directory) then
|
10
|
+
# archive directory
|
11
|
+
puts "info: packing #{directory} in #{archive}"
|
12
|
+
%x{/bin/tar --create --file=#{archive} #{directory}}
|
13
|
+
if $?.exitstatus == 0 then
|
14
|
+
puts "info: #{archive} created"
|
15
|
+
return archive
|
16
|
+
else
|
17
|
+
raise StandardError
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
Contract String, String => String
|
23
|
+
def self.unpack(archive, destination)
|
24
|
+
if DigitalbitsCoreBackup::Utils.writable?(destination) then
|
25
|
+
# extract archive in destination directory
|
26
|
+
puts "info: unpacking #{archive} in #{destination}"
|
27
|
+
%x{/bin/tar --extract --file=#{archive} --directory=#{destination}}
|
28
|
+
if $?.exitstatus == 0 then
|
29
|
+
puts "info: #{archive} unpacked in #{destination}"
|
30
|
+
return destination
|
31
|
+
else
|
32
|
+
raise StandardError
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|