digitalbits-core-backup 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
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