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.
Files changed (35) hide show
  1. checksums.yaml +7 -0
  2. data/CONTRIBUTING.md +58 -0
  3. data/Gemfile +2 -0
  4. data/LICENSE.txt +202 -0
  5. data/README.md +93 -0
  6. data/Rakefile +9 -0
  7. data/bin/digitalbits-core-backup +46 -0
  8. data/config/sample.yaml +8 -0
  9. data/digitalbits-core-backup.gemspec +26 -0
  10. data/lib/digitalbits-core-backup/cmd.rb +28 -0
  11. data/lib/digitalbits-core-backup/cmd.rb-e +28 -0
  12. data/lib/digitalbits-core-backup/cmd_result.rb +14 -0
  13. data/lib/digitalbits-core-backup/cmd_result.rb-e +14 -0
  14. data/lib/digitalbits-core-backup/config.rb +37 -0
  15. data/lib/digitalbits-core-backup/config.rb-e +37 -0
  16. data/lib/digitalbits-core-backup/database.rb +67 -0
  17. data/lib/digitalbits-core-backup/database.rb-e +67 -0
  18. data/lib/digitalbits-core-backup/filesystem.rb +62 -0
  19. data/lib/digitalbits-core-backup/filesystem.rb-e +62 -0
  20. data/lib/digitalbits-core-backup/job.rb +223 -0
  21. data/lib/digitalbits-core-backup/job.rb-e +223 -0
  22. data/lib/digitalbits-core-backup/restore/database.rb +36 -0
  23. data/lib/digitalbits-core-backup/restore/database.rb-e +36 -0
  24. data/lib/digitalbits-core-backup/restore/filesystem.rb +36 -0
  25. data/lib/digitalbits-core-backup/restore/filesystem.rb-e +36 -0
  26. data/lib/digitalbits-core-backup/s3.rb +64 -0
  27. data/lib/digitalbits-core-backup/s3.rb-e +64 -0
  28. data/lib/digitalbits-core-backup/tar.rb +37 -0
  29. data/lib/digitalbits-core-backup/tar.rb-e +37 -0
  30. data/lib/digitalbits-core-backup/utils.rb +197 -0
  31. data/lib/digitalbits-core-backup/utils.rb-e +197 -0
  32. data/lib/digitalbits-core-backup/version.rb +3 -0
  33. data/lib/digitalbits-core-backup/version.rb-e +3 -0
  34. data/lib/digitalbits-core-backup.rb +21 -0
  35. 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