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,197 @@
1
+ require 'fileutils'
2
+ require 'net/http'
3
+ require 'json'
4
+ require 'time'
5
+
6
+
7
+ module DigitalbitsCoreBackup
8
+ class Utils
9
+ include Contracts
10
+
11
+ Contract DigitalbitsCoreBackup::Config => Contracts::Any
12
+ def initialize(config)
13
+ @config = config
14
+ @working_dir = DigitalbitsCoreBackup::Utils.create_working_dir(@config.get('working_dir'))
15
+ @db_restore = DigitalbitsCoreBackup::Restore::Database.new(@config)
16
+ @fs_restore = DigitalbitsCoreBackup::Restore::Filesystem.new(@config)
17
+ end
18
+
19
+ Contract String => String
20
+ def self.create_working_dir(dir)
21
+ working_dir = dir + "/#{Process.pid}"
22
+ unless Dir.exists?(working_dir) then
23
+ Dir.mkdir working_dir
24
+ end
25
+ return working_dir
26
+ end
27
+
28
+ Contract String => nil
29
+ def self.remove_working_dir(working_dir)
30
+ if Dir.exists?(working_dir) then
31
+ Dir.rmdir working_dir + "/#{Process.pid}"
32
+ end
33
+ end
34
+
35
+ Contract String => String
36
+ def self.create_backup_dir(dir)
37
+ unless Dir.exists?(dir) then
38
+ Dir.mkdir dir
39
+ end
40
+ return dir
41
+ end
42
+
43
+ Contract String, String => String
44
+ def self.create_backup_tar(working_dir, backup_dir)
45
+ puts 'info: creating backup tarball'
46
+ tar_file = "#{backup_dir}/core-backup-#{Time.now.to_i}.tar"
47
+ Dir.chdir(working_dir)
48
+ # archive the working directory
49
+ DigitalbitsCoreBackup::Tar.pack(tar_file, '.')
50
+ return tar_file
51
+ end
52
+
53
+ Contract String => nil
54
+ def extract_backup(backup_archive)
55
+ # extract the backup archive into the working directory
56
+ DigitalbitsCoreBackup::Tar.unpack(backup_archive, @working_dir)
57
+ return
58
+ end
59
+
60
+ # Contract String => nil
61
+ # def restore(backup_archive)
62
+ # @fs_restore.restore(backup_archive)
63
+ # @db_restore.restore()
64
+ # end
65
+
66
+ Contract String => Bool
67
+ def self.cleanbucket(bucket_dir)
68
+ if FileUtils.remove(Dir.glob(bucket_dir+'/*')) then
69
+ puts 'info: cleaning up workspace'
70
+ return true
71
+ else
72
+ return false
73
+ end
74
+ end
75
+
76
+ Contract String => Bool
77
+ def self.cleanup(working_dir)
78
+ if FileUtils.remove_dir(working_dir) then
79
+ puts 'info: cleaning up workspace'
80
+ return true
81
+ else
82
+ return false
83
+ end
84
+ end
85
+
86
+ Contract String, String => Bool
87
+ def self.confirm_shasums_definitive(working_dir, backup_archive)
88
+
89
+ # create an array of filesunpacked into the working_dir
90
+ Dir.chdir(working_dir)
91
+ files_present=Dir.glob('./**/*')
92
+
93
+ # remove directories and shasum details from file array
94
+ files_present.delete('./'+File.basename(backup_archive))
95
+ files_present.delete('./core-db')
96
+ files_present.delete('./SHA256SUMS')
97
+ files_present.delete('./SHA256SUMS.sig')
98
+
99
+ # now delete the file names in the shasums file from the array
100
+ # we are expecting an array of zero length after this process
101
+ File.open("SHA256SUMS").each { |sha_file| files_present.delete(sha_file.split(' ')[1].chomp) }
102
+ if files_present.none? then
103
+ return true
104
+ else
105
+ return false
106
+ end
107
+ end
108
+
109
+ # check we have read permissions
110
+ Contract String => Bool
111
+ def self.readable?(file)
112
+ if File.readable?(file) then
113
+ puts "info: #{file} readable"
114
+ return true
115
+ else
116
+ puts "error: cannot read #{file}"
117
+ raise Errno::EACCES
118
+ end
119
+ end
120
+
121
+ # check we have write permissions
122
+ Contract String => Bool
123
+ def self.writable?(file)
124
+ if File.writable?(file) then
125
+ puts "info: #{file} writeable"
126
+ return true
127
+ else
128
+ puts "error: cannot write to #{file}"
129
+ raise Errno::EACCES
130
+ end
131
+ end
132
+
133
+ # return number of available cores
134
+ Contract nil => Integer
135
+ def self.num_cores?()
136
+ require 'etc'
137
+ return Etc.nprocessors
138
+ end
139
+
140
+ # check digitalbits-core status
141
+ Contract DigitalbitsCoreBackup::Config => Bool
142
+ def self.core_healthy?(config)
143
+ port = get_admin_port(config.get('core_config'))
144
+ url = "http://127.0.0.1:%s/info" % port
145
+ uri = URI(url)
146
+ begin
147
+ response = Net::HTTP.get(uri)
148
+ state = JSON.parse(response)['info']['state']
149
+ if state == 'Synced!' then
150
+ puts "info: digitalbits-core up and synced"
151
+ return true
152
+ else
153
+ puts "error: digitalbits-core status is: %s" % state
154
+ return false
155
+ end
156
+ rescue
157
+ puts "info: digitalbits-core down or not synced"
158
+ return false
159
+ end
160
+ end
161
+
162
+ private
163
+ Contract String => String
164
+ def self.get_admin_port(config)
165
+ File.open(config,'r') do |fd|
166
+ fd.each_line do |line|
167
+ if (line[/^HTTP_PORT=/]) then
168
+ port = /^HTTP_PORT=(.*)/.match(line).captures[0]
169
+ return port
170
+ end
171
+ end
172
+ end
173
+ end
174
+
175
+ # Publishes metric to pushgateway
176
+ # if no value provided current epoch timestamp will be used
177
+ Contract Any, String, Any => Bool
178
+ def self.push_metric(url, metric, value=nil)
179
+ if url.nil? then
180
+ # No url means pushgateway URL is not configured
181
+ return false
182
+ end
183
+
184
+ if value.nil? then
185
+ value = Time.now.to_i
186
+ end
187
+
188
+ uri = URI(url)
189
+ req = Net::HTTP::Post.new(uri.request_uri)
190
+ req.body = "%s %i\n" % [metric, value]
191
+ http = Net::HTTP.new(uri.host, uri.port)
192
+ http.request(req)
193
+ return true
194
+ end
195
+
196
+ end
197
+ end
@@ -0,0 +1,197 @@
1
+ require 'fileutils'
2
+ require 'net/http'
3
+ require 'json'
4
+ require 'time'
5
+
6
+
7
+ module DigitalbitsCoreBackup
8
+ class Utils
9
+ include Contracts
10
+
11
+ Contract DigitalbitsCoreBackup::Config => Contracts::Any
12
+ def initialize(config)
13
+ @config = config
14
+ @working_dir = DigitalbitsCoreBackup::Utils.create_working_dir(@config.get('working_dir'))
15
+ @db_restore = DigitalbitsCoreBackup::Restore::Database.new(@config)
16
+ @fs_restore = DigitalbitsCoreBackup::Restore::Filesystem.new(@config)
17
+ end
18
+
19
+ Contract String => String
20
+ def self.create_working_dir(dir)
21
+ working_dir = dir + "/#{Process.pid}"
22
+ unless Dir.exists?(working_dir) then
23
+ Dir.mkdir working_dir
24
+ end
25
+ return working_dir
26
+ end
27
+
28
+ Contract String => nil
29
+ def self.remove_working_dir(working_dir)
30
+ if Dir.exists?(working_dir) then
31
+ Dir.rmdir working_dir + "/#{Process.pid}"
32
+ end
33
+ end
34
+
35
+ Contract String => String
36
+ def self.create_backup_dir(dir)
37
+ unless Dir.exists?(dir) then
38
+ Dir.mkdir dir
39
+ end
40
+ return dir
41
+ end
42
+
43
+ Contract String, String => String
44
+ def self.create_backup_tar(working_dir, backup_dir)
45
+ puts 'info: creating backup tarball'
46
+ tar_file = "#{backup_dir}/core-backup-#{Time.now.to_i}.tar"
47
+ Dir.chdir(working_dir)
48
+ # archive the working directory
49
+ DigitalbitsCoreBackup::Tar.pack(tar_file, '.')
50
+ return tar_file
51
+ end
52
+
53
+ Contract String => nil
54
+ def extract_backup(backup_archive)
55
+ # extract the backup archive into the working directory
56
+ DigitalbitsCoreBackup::Tar.unpack(backup_archive, @working_dir)
57
+ return
58
+ end
59
+
60
+ # Contract String => nil
61
+ # def restore(backup_archive)
62
+ # @fs_restore.restore(backup_archive)
63
+ # @db_restore.restore()
64
+ # end
65
+
66
+ Contract String => Bool
67
+ def self.cleanbucket(bucket_dir)
68
+ if FileUtils.remove(Dir.glob(bucket_dir+'/*')) then
69
+ puts 'info: cleaning up workspace'
70
+ return true
71
+ else
72
+ return false
73
+ end
74
+ end
75
+
76
+ Contract String => Bool
77
+ def self.cleanup(working_dir)
78
+ if FileUtils.remove_dir(working_dir) then
79
+ puts 'info: cleaning up workspace'
80
+ return true
81
+ else
82
+ return false
83
+ end
84
+ end
85
+
86
+ Contract String, String => Bool
87
+ def self.confirm_shasums_definitive(working_dir, backup_archive)
88
+
89
+ # create an array of filesunpacked into the working_dir
90
+ Dir.chdir(working_dir)
91
+ files_present=Dir.glob('./**/*')
92
+
93
+ # remove directories and shasum details from file array
94
+ files_present.delete('./'+File.basename(backup_archive))
95
+ files_present.delete('./core-db')
96
+ files_present.delete('./SHA256SUMS')
97
+ files_present.delete('./SHA256SUMS.sig')
98
+
99
+ # now delete the file names in the shasums file from the array
100
+ # we are expecting an array of zero length after this process
101
+ File.open("SHA256SUMS").each { |sha_file| files_present.delete(sha_file.split(' ')[1].chomp) }
102
+ if files_present.none? then
103
+ return true
104
+ else
105
+ return false
106
+ end
107
+ end
108
+
109
+ # check we have read permissions
110
+ Contract String => Bool
111
+ def self.readable?(file)
112
+ if File.readable?(file) then
113
+ puts "info: #{file} readable"
114
+ return true
115
+ else
116
+ puts "error: cannot read #{file}"
117
+ raise Errno::EACCES
118
+ end
119
+ end
120
+
121
+ # check we have write permissions
122
+ Contract String => Bool
123
+ def self.writable?(file)
124
+ if File.writable?(file) then
125
+ puts "info: #{file} writeable"
126
+ return true
127
+ else
128
+ puts "error: cannot write to #{file}"
129
+ raise Errno::EACCES
130
+ end
131
+ end
132
+
133
+ # return number of available cores
134
+ Contract nil => Integer
135
+ def self.num_cores?()
136
+ require 'etc'
137
+ return Etc.nprocessors
138
+ end
139
+
140
+ # check digitalbits-core status
141
+ Contract DigitalbitsCoreBackup::Config => Bool
142
+ def self.core_healthy?(config)
143
+ port = get_admin_port(config.get('core_config'))
144
+ url = "http://127.0.0.1:%s/info" % port
145
+ uri = URI(url)
146
+ begin
147
+ response = Net::HTTP.get(uri)
148
+ state = JSON.parse(response)['info']['state']
149
+ if state == 'Synced!' then
150
+ puts "info: digitalbits-core up and synced"
151
+ return true
152
+ else
153
+ puts "error: digitalbits-core status is: %s" % state
154
+ return false
155
+ end
156
+ rescue
157
+ puts "info: digitalbits-core down or not synced"
158
+ return false
159
+ end
160
+ end
161
+
162
+ private
163
+ Contract String => String
164
+ def self.get_admin_port(config)
165
+ File.open(config,'r') do |fd|
166
+ fd.each_line do |line|
167
+ if (line[/^HTTP_PORT=/]) then
168
+ port = /^HTTP_PORT=(.*)/.match(line).captures[0]
169
+ return port
170
+ end
171
+ end
172
+ end
173
+ end
174
+
175
+ # Publishes metric to pushgateway
176
+ # if no value provided current epoch timestamp will be used
177
+ Contract Any, String, Any => Bool
178
+ def self.push_metric(url, metric, value=nil)
179
+ if url.nil? then
180
+ # No url means pushgateway URL is not configured
181
+ return false
182
+ end
183
+
184
+ if value.nil? then
185
+ value = Time.now.to_i
186
+ end
187
+
188
+ uri = URI(url)
189
+ req = Net::HTTP::Post.new(uri.request_uri)
190
+ req.body = "%s %i\n" % [metric, value]
191
+ http = Net::HTTP.new(uri.host, uri.port)
192
+ http.request(req)
193
+ return true
194
+ end
195
+
196
+ end
197
+ end
@@ -0,0 +1,3 @@
1
+ module DigitalbitsCoreBackup
2
+ VERSION = "0.0.7"
3
+ end
@@ -0,0 +1,3 @@
1
+ module DigitalbitsCoreBackup
2
+ VERSION = "0.0.7"
3
+ end
@@ -0,0 +1,21 @@
1
+ require "digitalbits-core-backup/version"
2
+ require "contracts"
3
+ require "fileutils"
4
+ require "pg"
5
+
6
+ module DigitalbitsCoreBackup
7
+ autoload :Cmd, "digitalbits-core-backup/cmd"
8
+ autoload :CmdResult, "digitalbits-core-backup/cmd_result"
9
+ autoload :Config, "digitalbits-core-backup/config"
10
+ autoload :Database, "digitalbits-core-backup/database"
11
+ autoload :Filesystem, "digitalbits-core-backup/filesystem"
12
+ autoload :Job, "digitalbits-core-backup/job"
13
+ autoload :S3, "digitalbits-core-backup/s3"
14
+ autoload :Tar, "digitalbits-core-backup/tar"
15
+ autoload :Utils, "digitalbits-core-backup/utils"
16
+
17
+ module Restore
18
+ autoload :Database, "digitalbits-core-backup/restore/database"
19
+ autoload :Filesystem, "digitalbits-core-backup/restore/filesystem"
20
+ end
21
+ end
metadata ADDED
@@ -0,0 +1,147 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: digitalbits-core-backup
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.7
5
+ platform: ruby
6
+ authors:
7
+ - Digitalbits
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-10-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: contracts
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.9'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.9'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pg
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.18.1
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.18.1
41
+ - !ruby/object:Gem::Dependency
42
+ name: aws-sdk-s3
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.7'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.7'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '10.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '10.0'
83
+ description:
84
+ email:
85
+ - support@digitalbits.io
86
+ executables:
87
+ - digitalbits-core-backup
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - CONTRIBUTING.md
92
+ - Gemfile
93
+ - LICENSE.txt
94
+ - README.md
95
+ - Rakefile
96
+ - bin/digitalbits-core-backup
97
+ - config/sample.yaml
98
+ - digitalbits-core-backup.gemspec
99
+ - lib/digitalbits-core-backup.rb
100
+ - lib/digitalbits-core-backup/cmd.rb
101
+ - lib/digitalbits-core-backup/cmd.rb-e
102
+ - lib/digitalbits-core-backup/cmd_result.rb
103
+ - lib/digitalbits-core-backup/cmd_result.rb-e
104
+ - lib/digitalbits-core-backup/config.rb
105
+ - lib/digitalbits-core-backup/config.rb-e
106
+ - lib/digitalbits-core-backup/database.rb
107
+ - lib/digitalbits-core-backup/database.rb-e
108
+ - lib/digitalbits-core-backup/filesystem.rb
109
+ - lib/digitalbits-core-backup/filesystem.rb-e
110
+ - lib/digitalbits-core-backup/job.rb
111
+ - lib/digitalbits-core-backup/job.rb-e
112
+ - lib/digitalbits-core-backup/restore/database.rb
113
+ - lib/digitalbits-core-backup/restore/database.rb-e
114
+ - lib/digitalbits-core-backup/restore/filesystem.rb
115
+ - lib/digitalbits-core-backup/restore/filesystem.rb-e
116
+ - lib/digitalbits-core-backup/s3.rb
117
+ - lib/digitalbits-core-backup/s3.rb-e
118
+ - lib/digitalbits-core-backup/tar.rb
119
+ - lib/digitalbits-core-backup/tar.rb-e
120
+ - lib/digitalbits-core-backup/utils.rb
121
+ - lib/digitalbits-core-backup/utils.rb-e
122
+ - lib/digitalbits-core-backup/version.rb
123
+ - lib/digitalbits-core-backup/version.rb-e
124
+ homepage: https://github.com/xdbfoundation/digitalbits-core-backup
125
+ licenses:
126
+ - Apache-2.0
127
+ metadata: {}
128
+ post_install_message:
129
+ rdoc_options: []
130
+ require_paths:
131
+ - lib
132
+ required_ruby_version: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
137
+ required_rubygems_version: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - ">="
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ requirements: []
143
+ rubygems_version: 3.0.3
144
+ signing_key:
145
+ specification_version: 4
146
+ summary: A helper script to backup a digitalbits-core node
147
+ test_files: []