ey_cloud_server 1.4.47 → 1.4.49
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -7
- data/features/support/env.rb +3 -3
- data/lib/ey-flex.rb +2 -3
- data/lib/ey-flex/snapshot_minder.rb +1 -1
- data/lib/ey_backup.rb +8 -2
- data/lib/ey_backup/backend.rb +1 -1
- data/lib/ey_backup/base.rb +1 -1
- data/lib/ey_backup/cli.rb +47 -19
- data/lib/ey_backup/database.rb +15 -1
- data/lib/ey_backup/dumper.rb +31 -3
- data/lib/ey_backup/engine.rb +3 -3
- data/lib/ey_backup/engines/mysql_engine.rb +49 -7
- data/lib/ey_backup/engines/postgresql_engine.rb +69 -21
- data/lib/ey_backup/logger.rb +54 -4
- data/lib/ey_backup/spawner.rb +31 -5
- data/lib/ey_cloud_server.rb +0 -1
- data/lib/ey_cloud_server/version.rb +1 -1
- data/spec/config.yml +11 -0
- data/spec/ey_backup/mysql_backups_spec.rb +11 -10
- data/spec/ey_backup/postgres_backups_spec.rb +9 -9
- data/spec/helpers.rb +16 -10
- data/spec/spec_helper.rb +1 -2
- metadata +126 -141
- data/bin/binary_log_purge +0 -263
- data/bin/clear_binlogs_from_slave +0 -7
- data/bin/ey-agent +0 -5
- data/bin/mysql_start +0 -6
- data/lib/ey-flex/big-brother.rb +0 -80
- data/lib/ey_cloud_server/mysql_start.rb +0 -155
- data/spec/big-brother_spec.rb +0 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
5
|
-
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b322ebd431ff9f4bd27a8a908d145ad4e70f01e9
|
4
|
+
data.tar.gz: 4a67d4081f42712cd36e8d683f1b00f767196d7a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 25bf1195e76d0042b379f7bc3aa9fb21e7ff30b5ab8b0b93cabc698c54032ec8dc15a20cd3b163e3d314fda03404310ca4e437fd2612d4d87e0714469b791499
|
7
|
+
data.tar.gz: 1211cea6558f7b4d1639bc686d24c015522fd35a69f55733b6cc7091a2a7e66ae2b920e597e48b009d99734701c19301d5188a306989f15074fe0b02744bb0d7
|
data/features/support/env.rb
CHANGED
@@ -3,15 +3,15 @@ require File.dirname(__FILE__) + "/../../lib/ey_backup"
|
|
3
3
|
require 'fakeweb'
|
4
4
|
require 'fakefs/safe'
|
5
5
|
require 'randexp'
|
6
|
+
require 'fog/aws'
|
7
|
+
|
8
|
+
Fog.mock!
|
6
9
|
|
7
10
|
require File.dirname(__FILE__) + '/../../spec/fakefs_hax'
|
8
11
|
require File.dirname(__FILE__) + '/../../spec/helpers'
|
9
12
|
|
10
13
|
FakeWeb.allow_net_connect = false # if it's not here, it doesn't exist
|
11
14
|
Randexp::Dictionary.words
|
12
|
-
Fog.mock!
|
13
|
-
|
14
|
-
Fog.mock!
|
15
15
|
|
16
16
|
World(Helpers)
|
17
17
|
|
data/lib/ey-flex.rb
CHANGED
@@ -6,7 +6,6 @@ require 'fileutils'
|
|
6
6
|
require 'json/ext'
|
7
7
|
require 'open-uri'
|
8
8
|
require 'rest_client'
|
9
|
-
require 'dbi'
|
10
9
|
require 'zlib'
|
11
10
|
require 'stringio'
|
12
11
|
require 'yaml'
|
@@ -29,7 +28,8 @@ module EY
|
|
29
28
|
@enzyme_api ||= EY::Enzyme::API.new(
|
30
29
|
enzyme_config[:api],
|
31
30
|
enzyme_config[:instance_id],
|
32
|
-
enzyme_config[:token]
|
31
|
+
enzyme_config[:token],
|
32
|
+
EY::Backup.logger
|
33
33
|
)
|
34
34
|
end
|
35
35
|
|
@@ -43,7 +43,6 @@ module EY
|
|
43
43
|
module CloudServer; end
|
44
44
|
end
|
45
45
|
|
46
|
-
require lib_dir + '/big-brother'
|
47
46
|
require lib_dir + '/bucket_minder'
|
48
47
|
require lib_dir + '/ey-api'
|
49
48
|
require lib_dir + '/snapshot_minder'
|
data/lib/ey_backup.rb
CHANGED
@@ -6,7 +6,8 @@ require 'open4'
|
|
6
6
|
require 'zlib'
|
7
7
|
require 'ostruct'
|
8
8
|
require 'fileutils'
|
9
|
-
require 'fog'
|
9
|
+
require 'fog/aws'
|
10
|
+
require 'timeout'
|
10
11
|
|
11
12
|
module Fog
|
12
13
|
module AWS
|
@@ -72,6 +73,7 @@ module EY
|
|
72
73
|
|
73
74
|
def restore
|
74
75
|
databases.each do |database|
|
76
|
+
@engine.check_if_replica
|
75
77
|
Loader.run(database, @options[:index])
|
76
78
|
end
|
77
79
|
end
|
@@ -110,6 +112,10 @@ module EY
|
|
110
112
|
EY::Backup.logger ||= Logger.quiet
|
111
113
|
else
|
112
114
|
EY::Backup.logger ||= Logger.new
|
115
|
+
|
116
|
+
if @options[:verbose]
|
117
|
+
EY::Backup.logger.set_verbose
|
118
|
+
end
|
113
119
|
end
|
114
120
|
end
|
115
121
|
|
@@ -130,7 +136,7 @@ module EY
|
|
130
136
|
if ! @options.key?(:dbhost) or @options[:dbhost] == nil or @options[:dbhost] == ""
|
131
137
|
@options[:dbhost] = 'localhost'
|
132
138
|
end
|
133
|
-
@engine = engine_class.new(@options[:dbuser], @options[:dbpass], @options[:dbhost], @options[:key_id])
|
139
|
+
@engine = engine_class.new(@options[:dbuser], @options[:dbpass], @options[:dbhost], @options[:key_id], @options[:force])
|
134
140
|
end
|
135
141
|
|
136
142
|
def dispatch
|
data/lib/ey_backup/backend.rb
CHANGED
data/lib/ey_backup/base.rb
CHANGED
data/lib/ey_backup/cli.rb
CHANGED
@@ -5,6 +5,7 @@ module EY
|
|
5
5
|
|
6
6
|
def run(argv)
|
7
7
|
options = default_options.merge(opt_parse(argv))
|
8
|
+
verify_restore if options[:command] == :restore and !options[:force]
|
8
9
|
|
9
10
|
config_path = options[:config] || "/etc/.#{options[:engine]}.backups.yml"
|
10
11
|
|
@@ -16,8 +17,18 @@ module EY
|
|
16
17
|
:command => :new_backup,
|
17
18
|
:format => "gzip",
|
18
19
|
:engine => 'mysql',
|
20
|
+
:verbose => false,
|
19
21
|
}
|
20
22
|
end
|
23
|
+
|
24
|
+
def verify_restore
|
25
|
+
res = ''
|
26
|
+
puts "This will overwrite your database, are you sure you would like to proceed (Y/n)?"
|
27
|
+
Timeout::timeout(30){
|
28
|
+
res = gets.strip
|
29
|
+
}
|
30
|
+
abort("You indicated '#{res}', exiting!") unless res.upcase == 'Y'
|
31
|
+
end
|
21
32
|
|
22
33
|
def opt_parse(argv)
|
23
34
|
# Build a parser for the command line arguments
|
@@ -25,12 +36,13 @@ module EY
|
|
25
36
|
|
26
37
|
opts = OptionParser.new do |opts|
|
27
38
|
opts.version = EY::CloudServer::VERSION
|
39
|
+
puts '' # add a blank line
|
28
40
|
|
29
41
|
opts.banner = "Usage: eybackup [-flag] [argument]"
|
30
|
-
opts.define_head "eybackup: manage dump (mysqldump/pg_dump) style backups of your database."
|
42
|
+
opts.define_head " eybackup: manage dump (mysqldump/pg_dump) style backups of your database."
|
31
43
|
opts.separator '*'*80
|
32
44
|
|
33
|
-
opts.on("-l", "--list-backup DATABASE", "List
|
45
|
+
opts.on("-l", "--list-backup DATABASE", "List backups for DATABASE") do |db|
|
34
46
|
if db == "all"
|
35
47
|
db = nil
|
36
48
|
end
|
@@ -38,7 +50,10 @@ module EY
|
|
38
50
|
options[:command] = :list
|
39
51
|
end
|
40
52
|
|
41
|
-
opts.on("-
|
53
|
+
opts.on("-e", "--engine DATABASE_ENGINE", "The database engine. ex: mysql, postgresql.") do |engine|
|
54
|
+
options[:engine] = engine
|
55
|
+
end
|
56
|
+
opts.on("-n", "--new-backup", "Create a new backup (default)") do
|
42
57
|
options[:command] = :new_backup
|
43
58
|
end
|
44
59
|
|
@@ -53,36 +68,49 @@ module EY
|
|
53
68
|
opts.on("-t", "--tmp_dir TMPDIR", "Use the given directory for temporary storage.") do |tmp_dir|
|
54
69
|
options[:tmp_dir] = tmp_dir
|
55
70
|
end
|
71
|
+
|
72
|
+
opts.on("-k", "--key KEYID", "Public key ID to use for the backup operation") do |key_id|
|
73
|
+
options[:key_id] = key_id
|
74
|
+
end
|
75
|
+
|
76
|
+
opts.on("-q", "--quiet", "Suppress output to STDOUT") do
|
77
|
+
options[:quiet] = true
|
78
|
+
end
|
79
|
+
|
80
|
+
opts.on("-s", "--split_size INTEGER", "Maximum size of a single backup file before splitting.") do |split_size|
|
81
|
+
options[:split_size] = split_size.to_i
|
82
|
+
end
|
56
83
|
|
57
|
-
|
84
|
+
opts.on("-v", "--verbose", "Show verbose output") do
|
85
|
+
options[:verbose] = true
|
86
|
+
end
|
87
|
+
|
88
|
+
opts.on("-d", "--download BACKUP_INDEX",
|
89
|
+
"Download the backup specified by index.",
|
90
|
+
' Run `eybackup -l #{db_name}` to get the index.',
|
91
|
+
' BACKUP_INDEX uses the format #{index_number}:#{db_name}') do |index_and_db|
|
58
92
|
options[:command] = :download
|
59
93
|
db, index = split_index(index_and_db)
|
60
94
|
options[:index] = index
|
61
95
|
options[:db] = db
|
62
96
|
end
|
63
97
|
|
64
|
-
opts.on("-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
opts.on("-r", "--restore BACKUP_INDEX", "Download and apply the backup specified by index WARNING! will overwrite the current db with the backup. Run eybackup -l to get the index.") do |index_and_db|
|
98
|
+
opts.on("-r", "--restore BACKUP_INDEX", "Download and apply the backup specified by index.",
|
99
|
+
" **WARNING!** will overwrite the current db with the backup.",
|
100
|
+
' Run `eybackup -l #{db_name}` to get the index.',
|
101
|
+
' BACKUP_INDEX uses the format #{index_number}:#{db_name}') do |index_and_db|
|
69
102
|
options[:command] = :restore
|
70
103
|
db, index = split_index(index_and_db)
|
71
104
|
options[:index] = index
|
72
105
|
options[:db] = db
|
73
106
|
end
|
74
|
-
|
75
|
-
|
76
|
-
|
107
|
+
|
108
|
+
options[:force] = false
|
109
|
+
opts.on("-f", "--force", "Force backup restore, bypass confirmation prompts.",
|
110
|
+
" For use with automated restore operations (e.g. Staging).") do
|
111
|
+
options[:force] = true
|
77
112
|
end
|
78
113
|
|
79
|
-
opts.on("-q", "--quiet", "Supress output to STDOUT") do
|
80
|
-
options[:quiet] = true
|
81
|
-
end
|
82
|
-
|
83
|
-
opts.on("-s", "--split_size INTEGER", "Maximum size of a single backup file before splitting.") do |split_size|
|
84
|
-
options[:split_size] = split_size.to_i
|
85
|
-
end
|
86
114
|
end
|
87
115
|
|
88
116
|
opts.parse!(argv)
|
data/lib/ey_backup/database.rb
CHANGED
@@ -11,8 +11,9 @@ module EY
|
|
11
11
|
@backend = backend
|
12
12
|
@environment = environment
|
13
13
|
@name = name
|
14
|
+
@backup_size = nil
|
14
15
|
end
|
15
|
-
attr_reader :name, :engine, :environment, :keep, :base_path
|
16
|
+
attr_reader :name, :engine, :environment, :keep, :base_path, :backup_size
|
16
17
|
|
17
18
|
def bucket_minder
|
18
19
|
@backend.bucket_minder
|
@@ -28,6 +29,7 @@ module EY
|
|
28
29
|
|
29
30
|
basename = generate_basename
|
30
31
|
file = @engine.dump(@name, basename)
|
32
|
+
@backup_size = number_to_human_size(File.size(file))
|
31
33
|
|
32
34
|
BackupSet.from(self, basename, file)
|
33
35
|
end
|
@@ -43,6 +45,18 @@ module EY
|
|
43
45
|
def timestamp
|
44
46
|
Time.now.strftime("%Y-%m-%dT%H-%M-%S")
|
45
47
|
end
|
48
|
+
|
49
|
+
def number_to_human_size(size)
|
50
|
+
if size < 1024
|
51
|
+
"#{size} bytes"
|
52
|
+
elsif size < 1024.0 * 1024.0
|
53
|
+
"%.01f KB" % (size / 1024.0)
|
54
|
+
elsif size < 1024.0 * 1024.0 * 1024.0
|
55
|
+
"%.01f MB" % (size / 1024.0 / 1024.0)
|
56
|
+
else
|
57
|
+
"%.01f GB" % (size / 1024.0 / 1024.0 / 1024.0)
|
58
|
+
end
|
59
|
+
end
|
46
60
|
end
|
47
61
|
end
|
48
62
|
end
|
data/lib/ey_backup/dumper.rb
CHANGED
@@ -1,14 +1,40 @@
|
|
1
1
|
module EY
|
2
2
|
module Backup
|
3
3
|
class Dumper < Base
|
4
|
+
require 'shellwords'
|
4
5
|
include Logging
|
5
6
|
|
6
7
|
attr_reader :database
|
7
8
|
|
8
9
|
def self.run(databases, split_size)
|
10
|
+
exceptions = []
|
11
|
+
completed = []
|
9
12
|
databases.each do |database|
|
10
|
-
|
13
|
+
begin
|
14
|
+
backup_size = new(database).run(split_size)
|
15
|
+
completed << "#{database.name} (#{backup_size})"
|
16
|
+
rescue => e
|
17
|
+
puts e
|
18
|
+
exceptions << database.name
|
19
|
+
next
|
20
|
+
end
|
11
21
|
end
|
22
|
+
message = ''
|
23
|
+
message = message + "Failures: #{exceptions}" if ! exceptions.empty?
|
24
|
+
message = message + ", " if ! exceptions.empty? and ! completed.empty?
|
25
|
+
message = message + "Completed successfully: #{completed}" if ! completed.empty?
|
26
|
+
|
27
|
+
alert_level=exceptions.empty? ? 'OKAY' : 'FAILURE'
|
28
|
+
|
29
|
+
message.gsub!("\n", '\n')
|
30
|
+
full_txt= "Severity: #{alert_level}\n" \
|
31
|
+
+ "Time: #{Time.now.to_i}\n" \
|
32
|
+
+ "Type: process-dbbackup summary\n" \
|
33
|
+
+ "Plugin: exec\n" \
|
34
|
+
+ "raw_message: '#{message}'"
|
35
|
+
full_txt = Shellwords.escape(full_txt)
|
36
|
+
alert_command = %Q(echo #{full_txt} | /engineyard/bin/ey-alert.rb 2>&1)
|
37
|
+
system(alert_command)
|
12
38
|
end
|
13
39
|
|
14
40
|
def initialize(database)
|
@@ -16,16 +42,18 @@ module EY
|
|
16
42
|
end
|
17
43
|
|
18
44
|
def run(split_size)
|
19
|
-
info
|
45
|
+
info("Doing database: #{@database.name}")
|
20
46
|
|
21
47
|
backup_set = @database.dump
|
22
48
|
backup_set.split!(split_size)
|
23
49
|
backup_set.upload!
|
24
50
|
|
25
|
-
|
51
|
+
okay("Successful backup: #{@database.name} (#{@database.backup_size})", @database.name)
|
26
52
|
backup_set.cleanup
|
27
53
|
backup_set.rm!
|
54
|
+
@database.backup_size
|
28
55
|
end
|
56
|
+
|
29
57
|
end
|
30
58
|
end
|
31
59
|
end
|
data/lib/ey_backup/engine.rb
CHANGED
@@ -3,7 +3,7 @@ module EY
|
|
3
3
|
class Engine < Base
|
4
4
|
include Spawner
|
5
5
|
|
6
|
-
attr_reader :username, :password, :host, :key_id
|
6
|
+
attr_reader :username, :password, :host, :key_id, :force
|
7
7
|
|
8
8
|
def self.label
|
9
9
|
@label
|
@@ -26,8 +26,8 @@ module EY
|
|
26
26
|
EY::Backup.logger.fatal("Unknown database engine: #{label}")
|
27
27
|
end
|
28
28
|
|
29
|
-
def initialize(username, password, host, key_id)
|
30
|
-
@username, @password, @host, @key_id = username, password, host, key_id
|
29
|
+
def initialize(username, password, host, key_id, force)
|
30
|
+
@username, @password, @host, @key_id, @force = username, password, host, key_id, force
|
31
31
|
end
|
32
32
|
|
33
33
|
def gpg?
|
@@ -6,7 +6,7 @@ module EY
|
|
6
6
|
def dump(database_name, basename)
|
7
7
|
file = basename + '.sql'
|
8
8
|
|
9
|
-
command = "mysqldump #{username_option} #{host_option} #{
|
9
|
+
command = "mysqldump #{username_option} #{host_option} #{single_transaction_option(database_name)} #{routines_option} #{master_data_option} #{databases_option(database_name)} 2> /tmp/eybackup.$$.dumperr"
|
10
10
|
|
11
11
|
if gpg?
|
12
12
|
command << " | " << GPGEncryptor.command_for(key_id)
|
@@ -18,12 +18,13 @@ module EY
|
|
18
18
|
|
19
19
|
command << " > #{file}"
|
20
20
|
|
21
|
-
run(command)
|
21
|
+
run(command, database_name)
|
22
22
|
|
23
23
|
file
|
24
24
|
end
|
25
25
|
|
26
26
|
def load(database_name, file)
|
27
|
+
cycle_database(database_name)
|
27
28
|
command = "cat #{file}"
|
28
29
|
|
29
30
|
if gpg?
|
@@ -32,17 +33,30 @@ module EY
|
|
32
33
|
command << " | " << GZipper.gunzip
|
33
34
|
end
|
34
35
|
|
35
|
-
command << " | mysql #{username_option} #{host_option} #{
|
36
|
+
command << " | mysql #{username_option} #{host_option} #{database_name}"
|
36
37
|
|
37
38
|
run(command)
|
38
39
|
end
|
40
|
+
|
41
|
+
def check_if_replica
|
42
|
+
err_msg="ERROR: Target host: '#{host}' is currently: "
|
43
|
+
err_msg=err_msg + "a replica based on 'show slave status', " if db_replicating?
|
44
|
+
err_msg=err_msg + "in read_only mode, " if read_only_on?
|
45
|
+
err_msg=err_msg + "restores should be processed against the master."
|
46
|
+
|
47
|
+
EY::Backup.logger.fatal(%Q{#{err_msg}}) if db_replicating? or read_only_on?
|
48
|
+
end
|
39
49
|
|
40
50
|
def routines_option
|
41
51
|
"--routines"
|
42
52
|
end
|
43
|
-
|
44
|
-
def
|
45
|
-
"-
|
53
|
+
|
54
|
+
def master_data_option
|
55
|
+
"--master-data=2" if log_bin_on?
|
56
|
+
end
|
57
|
+
|
58
|
+
def databases_option(db)
|
59
|
+
"--add-drop-database --databases #{db}"
|
46
60
|
end
|
47
61
|
|
48
62
|
def host_option
|
@@ -59,9 +73,37 @@ module EY
|
|
59
73
|
|
60
74
|
def db_has_myisam?(database_name)
|
61
75
|
query, stdout = "SELECT 1 FROM information_schema.tables WHERE table_schema='#{database_name}' AND engine='MyISAM' LIMIT 1;", StringIO.new
|
62
|
-
spawn(%Q{mysql #{username_option} #{
|
76
|
+
spawn(%Q{mysql #{username_option} #{host_option} -N -e"#{query}"}, stdout)
|
63
77
|
stdout.string.strip == '1'
|
64
78
|
end
|
79
|
+
|
80
|
+
def db_replicating?
|
81
|
+
stdout = StringIO.new
|
82
|
+
spawn(%Q{mysql #{username_option} #{host_option} -BN -e"show slave status"|wc -l}, stdout)
|
83
|
+
stdout.string.to_i > 0
|
84
|
+
end
|
85
|
+
|
86
|
+
def read_only_on?
|
87
|
+
stdout = StringIO.new
|
88
|
+
spawn(%Q{mysql #{username_option} #{host_option} -BN -e"select @@global.read_only"}, stdout)
|
89
|
+
stdout.string.to_i == 1
|
90
|
+
end
|
91
|
+
|
92
|
+
def log_bin_on?
|
93
|
+
stdout = StringIO.new
|
94
|
+
spawn(%Q{mysql #{username_option} #{host_option} -BN -e"select @@global.log_bin"}, stdout)
|
95
|
+
stdout.string.to_i == 1
|
96
|
+
end
|
97
|
+
|
98
|
+
def cycle_database(database_name)
|
99
|
+
query = "show create database #{database_name} \\G"
|
100
|
+
create_cmd = %x(mysql #{username_option} #{host_option} -NB -e "#{query}"|tail -n 1)
|
101
|
+
create_cmd="Create database #{database_name}" if create_cmd==""
|
102
|
+
|
103
|
+
%x(mysql #{username_option} #{host_option} -e 'DROP DATABASE IF EXISTS #{database_name}')
|
104
|
+
%x(mysql #{username_option} #{host_option} -e '#{create_cmd}')
|
105
|
+
|
106
|
+
end
|
65
107
|
|
66
108
|
def suffix
|
67
109
|
/sql\.(gz|gpz)$/
|