ey_cloud_server 1.4.47 → 1.4.49
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 -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)$/
|