backups-cli 1.0.9
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/.gitignore +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +3 -0
- data/LICENSE +7 -0
- data/README.md +256 -0
- data/Rakefile +6 -0
- data/backups-cli.gemspec +25 -0
- data/bin/backups +47 -0
- data/lib/backups.rb +19 -0
- data/lib/backups/adapter/mysql.rb +202 -0
- data/lib/backups/base.rb +70 -0
- data/lib/backups/cli.rb +51 -0
- data/lib/backups/crontab.rb +200 -0
- data/lib/backups/driver/mysql.rb +35 -0
- data/lib/backups/events.rb +40 -0
- data/lib/backups/ext/fixnum.rb +21 -0
- data/lib/backups/ext/hash.rb +8 -0
- data/lib/backups/ext/nil_class.rb +7 -0
- data/lib/backups/ext/ordered_hash.rb +11 -0
- data/lib/backups/ext/string.rb +20 -0
- data/lib/backups/listener.rb +19 -0
- data/lib/backups/listeners/notify/datadog.rb +44 -0
- data/lib/backups/listeners/notify/slack.rb +121 -0
- data/lib/backups/loader.rb +55 -0
- data/lib/backups/logger.rb +8 -0
- data/lib/backups/runner.rb +137 -0
- data/lib/backups/stats/mysql.rb +80 -0
- data/lib/backups/system.rb +54 -0
- data/lib/backups/verify/mysql.rb +180 -0
- data/lib/backups/version.rb +4 -0
- metadata +173 -0
@@ -0,0 +1,54 @@
|
|
1
|
+
module Backups
|
2
|
+
module System
|
3
|
+
|
4
|
+
def exec command
|
5
|
+
# $LOGGER.debug "Running #{command}" if $LOG_ACTIVE == 1
|
6
|
+
return if $DRY_RUN
|
7
|
+
|
8
|
+
output = `#{command}`
|
9
|
+
if $?.exitstatus != 0
|
10
|
+
raise RuntimeError, \
|
11
|
+
"Command '#{command}' failed with exit code #{$?.exitstatus}."
|
12
|
+
end
|
13
|
+
|
14
|
+
return output.chomp()
|
15
|
+
end
|
16
|
+
|
17
|
+
def delete file
|
18
|
+
exec "rm #{file}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def delete_dir start, stop = nil
|
22
|
+
return exec "rmdir #{start}" unless stop
|
23
|
+
stop = stop.chomp("/")
|
24
|
+
loop do
|
25
|
+
exec "rmdir #{start}" if File.directory? start
|
26
|
+
break if start == stop
|
27
|
+
start = File.dirname(start)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def nuke_dir dir
|
32
|
+
exec "rm -fr #{dir}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def mkdir dirname
|
36
|
+
exec "mkdir -p #{dirname}"
|
37
|
+
end
|
38
|
+
|
39
|
+
def write filename, contents
|
40
|
+
commands = []
|
41
|
+
commands << "cat << CONTENTS > #{filename}"
|
42
|
+
commands << contents
|
43
|
+
commands << "CONTENTS"
|
44
|
+
|
45
|
+
exec commands.join("\n")
|
46
|
+
end
|
47
|
+
|
48
|
+
def get_latest_s3 path
|
49
|
+
# $LOGGER.debug "aws s3 ls #{path}/|awk '{ print $4 }'|tail -n 1"
|
50
|
+
exec "aws s3 ls #{path}/|awk '{ print $4 }'|tail -n 1"
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,180 @@
|
|
1
|
+
require "yaml"
|
2
|
+
|
3
|
+
module Backups
|
4
|
+
module Verify
|
5
|
+
class Mysql < Base
|
6
|
+
|
7
|
+
VERIFY_PREFIX = "__verify__"
|
8
|
+
|
9
|
+
include ::Backups::Stats::Mysql
|
10
|
+
|
11
|
+
def verify
|
12
|
+
setup
|
13
|
+
download
|
14
|
+
import
|
15
|
+
clean_up
|
16
|
+
report
|
17
|
+
|
18
|
+
return @details
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
def setup
|
23
|
+
load_configs
|
24
|
+
|
25
|
+
@compared = 0
|
26
|
+
@failures = 0
|
27
|
+
@compsize = nil
|
28
|
+
@datedir = get_date_path()
|
29
|
+
@backups = $GLOBAL.fetch("backups", {})
|
30
|
+
@name = @config["_name"]
|
31
|
+
@verify = @config.fetch("verify", {})
|
32
|
+
@connection = @verify.fetch("connection", {})
|
33
|
+
@secret = @config.fetch("encryption", {}).fetch("secret", "")
|
34
|
+
@paths = @backups.fetch("paths", {})
|
35
|
+
@verifydir = @paths.fetch("verify", "/tmp/verify")
|
36
|
+
@jobdir = "#{@verifydir}/#{@name}"
|
37
|
+
@s3bucket = "#{@config["s3"]["bucket"]}"
|
38
|
+
@s3path = "#{@config["s3"]["path"]}"
|
39
|
+
@s3fullpath = "s3://#{@s3bucket}/#{@s3path}/#{@name}/#{@datedir}"
|
40
|
+
@package = get_latest_s3(@s3fullpath)
|
41
|
+
|
42
|
+
raise RuntimeError, "Error: Could not found the latest package in #{@s3fullpath}." \
|
43
|
+
unless @package.length > 1
|
44
|
+
|
45
|
+
@s3file = "#{@s3fullpath}/#{@package}"
|
46
|
+
@basename = File.basename(@package).gsub(".zip", "")
|
47
|
+
@download = "#{@jobdir}/#{@package}"
|
48
|
+
@timedir = @download.gsub(".zip", "")
|
49
|
+
|
50
|
+
@details = {
|
51
|
+
started: Time.now,
|
52
|
+
completed: nil,
|
53
|
+
file: nil,
|
54
|
+
size: nil,
|
55
|
+
report: nil,
|
56
|
+
}
|
57
|
+
|
58
|
+
$LOGGER.info "Preparing #{@jobdir}"
|
59
|
+
# We clean up previous imports as we are less strict with them clearing
|
60
|
+
# up cleanly.
|
61
|
+
exec "rm -fr #{@jobdir}"
|
62
|
+
exec "mkdir -p #{@jobdir}"
|
63
|
+
end
|
64
|
+
|
65
|
+
def download
|
66
|
+
$LOGGER.info "Downloading #{@s3file}"
|
67
|
+
exec "aws s3 cp #{@s3file} #{@download}"
|
68
|
+
@compsize = File.size(@download) if not $DRY_RUN
|
69
|
+
exec "cd #{@jobdir} && unzip -P #{@secret} #{@download}"
|
70
|
+
$LOGGER.info "Downloaded #{@compsize} bytes"
|
71
|
+
end
|
72
|
+
|
73
|
+
def import
|
74
|
+
files = get_import_files()
|
75
|
+
|
76
|
+
if files == []
|
77
|
+
$LOGGER.fatal "Error: There were no files in #{@timedir} to import."
|
78
|
+
return
|
79
|
+
end
|
80
|
+
|
81
|
+
# Are we importing the whole server?
|
82
|
+
if File.basename(files[0], ".sql") == ALL_DATABASES
|
83
|
+
file = files[0]
|
84
|
+
import_server file
|
85
|
+
check_stats file
|
86
|
+
else
|
87
|
+
files.each do |file|
|
88
|
+
db = File.basename(file, ".sql")
|
89
|
+
dbname = "#{VERIFY_PREFIX}#{db}"
|
90
|
+
import_database dbname, file
|
91
|
+
check_database_stats dbname, file
|
92
|
+
drop_database dbname
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def import_server file
|
98
|
+
$LOGGER.info "Importing server from #{file}"
|
99
|
+
exec "mysql < #{file}"
|
100
|
+
end
|
101
|
+
|
102
|
+
def import_database db, file
|
103
|
+
$LOGGER.info "Importing database #{db} from #{file}"
|
104
|
+
exec "mysql -e 'DROP DATABASE IF EXISTS #{db}'"
|
105
|
+
exec "mysql -e 'CREATE DATABASE #{db}'"
|
106
|
+
exec "mysql #{db} < #{file}"
|
107
|
+
end
|
108
|
+
|
109
|
+
def get_import_files
|
110
|
+
Dir["#{@timedir}/*.sql"]
|
111
|
+
end
|
112
|
+
|
113
|
+
def check_database_stats db, file
|
114
|
+
# return if db == "mysql"
|
115
|
+
|
116
|
+
file = file.gsub(".sql", "-stats.yaml")
|
117
|
+
data = YAML.load_file(file)
|
118
|
+
|
119
|
+
data.each do |table, stats|
|
120
|
+
object = "#{db}.#{table}"
|
121
|
+
check_table db, table
|
122
|
+
|
123
|
+
imported = get_table_count(db, table)
|
124
|
+
saved = stats["rows"]
|
125
|
+
|
126
|
+
@compared = @compared + 1
|
127
|
+
if saved.to_i == imported.to_i
|
128
|
+
$LOGGER.debug "Row count match for #{object} matches"
|
129
|
+
else
|
130
|
+
$LOGGER.warn "Row count failed for #{object}. Saved #{saved} vs imported #{imported}"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def get_table_count db, table
|
136
|
+
sql = "SELECT COUNT(1) FROM #{db}.#{table}"
|
137
|
+
exec "mysql -e \"#{sql}\"|awk 'NR>1'"
|
138
|
+
end
|
139
|
+
|
140
|
+
def drop_database db
|
141
|
+
$LOGGER.debug "Dropping database #{db}"
|
142
|
+
sql = "DROP DATABASE #{db}"
|
143
|
+
exec "mysql -e '#{sql}'"
|
144
|
+
end
|
145
|
+
|
146
|
+
def check_table database, table
|
147
|
+
@compared = @compared + 1
|
148
|
+
rs = get_result("CHECK TABLE #{database}.#{table}")
|
149
|
+
if rs["Msg_text"] != "OK"
|
150
|
+
@failures += 1
|
151
|
+
$LOGGER.warn "Check table failed for #{database}.#{table}"
|
152
|
+
else
|
153
|
+
$LOGGER.debug "Check table passed for #{database}.#{table}"
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def clean_up
|
158
|
+
$LOGGER.info "Cleaning #{@jobdir}"
|
159
|
+
@compsize = File.size(@download)
|
160
|
+
exec "rm -fr #{@jobdir}"
|
161
|
+
end
|
162
|
+
|
163
|
+
def report
|
164
|
+
$LOGGER.info "Reporting that #{@compared} checks were compared"
|
165
|
+
if @failures > 0
|
166
|
+
$LOGGER.warn "#{@failures} checks failed"
|
167
|
+
else
|
168
|
+
$LOGGER.info "All checks passed"
|
169
|
+
end
|
170
|
+
|
171
|
+
@details[:file] = @download
|
172
|
+
@details[:size] = @compsize
|
173
|
+
@details[:view] = "https://console.aws.amazon.com/s3/home?region=eu-west-1#&bucket=#{@s3bucket}&prefix=#{@s3path}"
|
174
|
+
@details[:report] = "#{@compared} stats compared with #{@failures} failures"
|
175
|
+
@details[:completed] = Time.now
|
176
|
+
end
|
177
|
+
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
metadata
ADDED
@@ -0,0 +1,173 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: backups-cli
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.9
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Schibsted
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-03-19 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: thor
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: json
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: mysql2
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: dogapi
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: slack-notifier
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: tablelize
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rake
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description: This tool backups different data sources to S3.
|
112
|
+
email:
|
113
|
+
- spt@schibsted.com
|
114
|
+
executables:
|
115
|
+
- backups
|
116
|
+
extensions: []
|
117
|
+
extra_rdoc_files: []
|
118
|
+
files:
|
119
|
+
- ".gitignore"
|
120
|
+
- ".travis.yml"
|
121
|
+
- Gemfile
|
122
|
+
- LICENSE
|
123
|
+
- README.md
|
124
|
+
- Rakefile
|
125
|
+
- backups-cli.gemspec
|
126
|
+
- bin/backups
|
127
|
+
- lib/backups.rb
|
128
|
+
- lib/backups/adapter/mysql.rb
|
129
|
+
- lib/backups/base.rb
|
130
|
+
- lib/backups/cli.rb
|
131
|
+
- lib/backups/crontab.rb
|
132
|
+
- lib/backups/driver/mysql.rb
|
133
|
+
- lib/backups/events.rb
|
134
|
+
- lib/backups/ext/fixnum.rb
|
135
|
+
- lib/backups/ext/hash.rb
|
136
|
+
- lib/backups/ext/nil_class.rb
|
137
|
+
- lib/backups/ext/ordered_hash.rb
|
138
|
+
- lib/backups/ext/string.rb
|
139
|
+
- lib/backups/listener.rb
|
140
|
+
- lib/backups/listeners/notify/datadog.rb
|
141
|
+
- lib/backups/listeners/notify/slack.rb
|
142
|
+
- lib/backups/loader.rb
|
143
|
+
- lib/backups/logger.rb
|
144
|
+
- lib/backups/runner.rb
|
145
|
+
- lib/backups/stats/mysql.rb
|
146
|
+
- lib/backups/system.rb
|
147
|
+
- lib/backups/verify/mysql.rb
|
148
|
+
- lib/backups/version.rb
|
149
|
+
homepage: https://github.com/schibsted/backups-cli
|
150
|
+
licenses:
|
151
|
+
- MIT
|
152
|
+
metadata: {}
|
153
|
+
post_install_message:
|
154
|
+
rdoc_options: []
|
155
|
+
require_paths:
|
156
|
+
- lib
|
157
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
158
|
+
requirements:
|
159
|
+
- - ">="
|
160
|
+
- !ruby/object:Gem::Version
|
161
|
+
version: '0'
|
162
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
requirements: []
|
168
|
+
rubyforge_project:
|
169
|
+
rubygems_version: 2.5.2.1
|
170
|
+
signing_key:
|
171
|
+
specification_version: 4
|
172
|
+
summary: This tool backups different data sources to S3
|
173
|
+
test_files: []
|