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,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: []