ey_cloud_server 1.4.5 → 1.4.26

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 (42) hide show
  1. data/bin/binary_log_purge +10 -2
  2. data/bin/ey-snapshots +7 -1
  3. data/bin/eybackup +13 -1
  4. data/lib/ey-flex.rb +18 -7
  5. data/lib/ey-flex/big-brother.rb +2 -2
  6. data/lib/ey-flex/bucket_minder.rb +46 -160
  7. data/lib/ey-flex/ec2.rb +17 -0
  8. data/lib/ey-flex/snapshot_minder.rb +93 -171
  9. data/lib/ey_backup.rb +84 -48
  10. data/lib/ey_backup/backend.rb +34 -0
  11. data/lib/ey_backup/backup_set.rb +70 -63
  12. data/lib/ey_backup/base.rb +0 -5
  13. data/lib/ey_backup/cli.rb +26 -6
  14. data/lib/ey_backup/database.rb +48 -0
  15. data/lib/ey_backup/dumper.rb +15 -31
  16. data/lib/ey_backup/engine.rb +7 -17
  17. data/lib/ey_backup/engines/mysql_engine.rb +24 -16
  18. data/lib/ey_backup/engines/postgresql_engine.rb +26 -20
  19. data/lib/ey_backup/loader.rb +13 -33
  20. data/lib/ey_backup/processors/gpg_encryptor.rb +3 -20
  21. data/lib/ey_backup/processors/gzipper.rb +0 -29
  22. data/lib/ey_backup/processors/splitter.rb +22 -34
  23. data/lib/ey_backup/spawner.rb +7 -13
  24. data/lib/ey_cloud_server.rb +1 -1
  25. data/lib/{ey-flex → ey_cloud_server}/version.rb +1 -1
  26. data/spec/big-brother_spec.rb +12 -0
  27. data/spec/bucket_minder_spec.rb +113 -0
  28. data/spec/config-example.yml +11 -0
  29. data/spec/ey_api_spec.rb +63 -0
  30. data/spec/ey_backup/backend_spec.rb +12 -0
  31. data/spec/ey_backup/backup_spec.rb +54 -0
  32. data/spec/ey_backup/cli_spec.rb +35 -0
  33. data/spec/ey_backup/mysql_backups_spec.rb +208 -0
  34. data/spec/ey_backup/postgres_backups_spec.rb +106 -0
  35. data/spec/ey_backup/spec_helper.rb +5 -0
  36. data/spec/fakefs_hax.rb +50 -0
  37. data/spec/gpg.public +0 -0
  38. data/spec/gpg.sekrit +0 -0
  39. data/spec/helpers.rb +270 -0
  40. data/spec/snapshot_minder_spec.rb +68 -0
  41. data/spec/spec_helper.rb +31 -0
  42. metadata +286 -53
@@ -1,39 +1,35 @@
1
1
  module EY
2
2
  module Backup
3
- module Splitter
4
- extend Spawner
5
- extend Logging
6
- extend self
3
+ class Splitter
4
+ include Spawner
5
+ include Logging
7
6
 
8
7
  CHUNK_SIZE = 1024 * 64
9
8
  MAX_FILE_SIZE = (4.5*1024*1024*1024).to_i #4.5GB
10
9
 
11
- def load(backup)
12
- if backup.files.size > 1
13
- backup.files = join(backup.files)
14
- end
10
+ def self.dump(file, split_size)
11
+ split_size ||= MAX_FILE_SIZE
12
+ new.dump(file, split_size)
13
+ end
15
14
 
16
- backup
15
+ def self.load(files)
16
+ new.load(files)
17
17
  end
18
18
 
19
- def dump(command, files)
20
- if files.size != 1
21
- fatal "Can't split multiple files."
19
+ def load(files)
20
+ if files.size > 1
21
+ join(files)
22
+ else
23
+ files.first
22
24
  end
23
-
24
25
  end
25
26
 
26
- def dump(backup)
27
- output_files = backup.files.map do |file|
28
- if File.size(file) >= split_size
29
- split(file)
30
- else
31
- file
32
- end
33
- end.flatten
34
-
35
- backup.files = output_files
36
- backup
27
+ def dump(file, split_size)
28
+ if File.size(file) >= split_size
29
+ split(file, split_size)
30
+ else
31
+ [file]
32
+ end
37
33
  end
38
34
 
39
35
  def join(input_files)
@@ -46,12 +42,10 @@ module EY
46
42
  output << chunk
47
43
  end
48
44
  end
49
-
50
- FileUtils.rm(input)
51
45
  end
52
46
  end
53
47
 
54
- [filename]
48
+ filename
55
49
  end
56
50
 
57
51
  def sort(input_files)
@@ -62,7 +56,7 @@ module EY
62
56
  file[/\.part(\d+)/, 1].to_i
63
57
  end
64
58
 
65
- def split(file)
59
+ def split(file, split_size)
66
60
  total_size, part = 0, 0
67
61
  files = []
68
62
  File.open(file, 'r') do |i|
@@ -83,14 +77,8 @@ module EY
83
77
  end
84
78
  end
85
79
 
86
- FileUtils.rm(file)
87
-
88
80
  files
89
81
  end
90
-
91
- def split_size
92
- EY::Backup.config.split_size || MAX_FILE_SIZE
93
- end
94
82
  end
95
83
  end
96
84
  end
@@ -19,21 +19,15 @@ module EY
19
19
  end
20
20
 
21
21
  def run(command)
22
- pid, *_ = Open4.popen4(command)
23
- Process::waitpid2(pid)
22
+ unless runs?(command)
23
+ raise "Failed to run #{command.inspect}"
24
+ end
24
25
  end
25
26
 
26
- def popen(command, stdout, stdin = nil, stderr = logger.stderr)
27
- ioify(stdout, stdin, stderr) do |o, i, e|
28
- Open4.popen4(command) do |pid, stdin, stdout, stderr|
29
- while chunk = i.read(CHUNK_SIZE)
30
- stdin << chunk
31
- end
32
-
33
- stdin.flush
34
- stdin.close
35
- end
36
- end
27
+ def runs?(command)
28
+ pid, *_ = Open4.popen4(command)
29
+ pid, status = Process::waitpid2(pid)
30
+ status.success?
37
31
  end
38
32
 
39
33
  def ioify(stdout, stdin, stderr, &block)
@@ -5,5 +5,5 @@ module EY
5
5
  end
6
6
  end
7
7
 
8
- require 'ey-flex/version'
8
+ require 'ey_cloud_server/version'
9
9
  require 'ey_cloud_server/mysql_start'
@@ -1,3 +1,3 @@
1
1
  module EY::CloudServer
2
- VERSION = '1.4.5'
2
+ VERSION = '1.4.26'
3
3
  end
@@ -0,0 +1,12 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe "BigBrother" do
4
+ it "works" do
5
+ setup_dna({})
6
+
7
+ response = EY::BigBrother.check(tmp_dir + "/dna.json")
8
+ response.should == "{}"
9
+
10
+ teardown_dna({})
11
+ end
12
+ end
@@ -0,0 +1,113 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe "BucketMinder" do
4
+ def setup_s3(secret_id, secret_key, region)
5
+ @raw_s3 = Fog::Storage.new(:provider => 'AWS', :region => region, :aws_access_key_id => secret_id, :aws_secret_access_key => secret_key)
6
+ end
7
+
8
+ def setup_bucket(secret_id, secret_key, bucket_name, region = nil)
9
+ @bucket_minder = EY::BucketMinder.new(secret_id, secret_key, bucket_name, region)
10
+ end
11
+
12
+ describe "in us-east-1" do
13
+ before do
14
+ @config = spec_config
15
+
16
+ @bucket_name = "ey-backup-#{Digest::SHA1.hexdigest(@config['aws_secret_id'])[0..11]}"
17
+ @region = 'us-east-1'
18
+
19
+ setup_s3(@config['aws_secret_id'], @config['aws_secret_key'], @region)
20
+ end
21
+
22
+ it "sets up the bucket" do
23
+ setup_bucket(@config['aws_secret_id'], @config['aws_secret_key'], @bucket_name)
24
+
25
+ real_bucket = @raw_s3.directories.get(@bucket_name)
26
+ real_bucket.key.should == @bucket_name
27
+ end
28
+
29
+ it "uses a default name when the bucket name is not provided" do
30
+ setup_bucket(@config['aws_secret_id'], @config['aws_secret_key'], nil)
31
+ @bucket_minder.bucket.key.should == @bucket_name
32
+ end
33
+
34
+ describe "with a working bucket" do
35
+ before :each do
36
+ setup_bucket(@config['aws_secret_id'], @config['aws_secret_key'], @bucket_name)
37
+
38
+ @test_filename = 'somefile.tst'
39
+ @file_contents = 'i am file contents'
40
+ lambda { @bucket_minder.remove_object(@test_filename) }
41
+ end
42
+
43
+ it "can put, get and remove a file's contents to and from bucket" do
44
+ @bucket_minder.list(@test_filename).should be_empty
45
+ @bucket_minder.put(@test_filename, @file_contents)
46
+
47
+ remote_file = @bucket_minder.list(@test_filename).first
48
+ remote_file[:name].should == @test_filename
49
+
50
+ @bucket_minder.stream(@test_filename) do |chunk, remaining_size, total_size|
51
+ chunk.should == @file_contents
52
+ end
53
+
54
+ @bucket_minder.remove_object(@test_filename)
55
+ @bucket_minder.list(@test_filename).should be_empty
56
+ end
57
+
58
+ describe "when deleting backups" do
59
+ it "does not load the backup files into memory" do
60
+ @bucket_minder.files.should_not_receive(:get)
61
+
62
+ @bucket_minder.put('test_bucket_minder_file_1', 'foo')
63
+ @bucket_minder.remove_object('test_bucket_minder_file_1')
64
+
65
+ @bucket_minder.list('test_bucket_minder_file_1').should be_empty
66
+ end
67
+ end
68
+
69
+ describe "bucket minder merge operations" do
70
+ it "returns the number of files in a bucket" do
71
+ @bucket_minder.put('test_bucket_minder_file_1', 'foo')
72
+ @bucket_minder.put('test_bucket_minder_file_2', 'foo')
73
+
74
+ bucketfiles = @bucket_minder.list('test_bucket_minder_file')
75
+ bucketfiles.length.should == 2
76
+ bucketfiles.detect { |file| file[:name] == 'test_bucket_minder_file_1' }.should_not be_empty
77
+ bucketfiles.detect { |file| file[:name] == 'test_bucket_minder_file_2' }.should_not be_empty
78
+ end
79
+
80
+ it "collects file parts" do
81
+ @bucket_minder.put('test_bucket_minder_file_1', 'foo')
82
+ @bucket_minder.put('test_bucket_minder_file_2', 'foo')
83
+ @bucket_minder.put('test_bucket_minder_multi_file.part1', 'foo')
84
+ @bucket_minder.put('test_bucket_minder_multi_file.part2', 'foo')
85
+
86
+ bucketfiles = @bucket_minder.list('test_bucket_minder')
87
+ bucketfiles.length.should == 3
88
+ bucketfiles.detect { |file| file[:name] == 'test_bucket_minder_file_1' }.should_not be_empty
89
+ bucketfiles.detect { |file| file[:name] == 'test_bucket_minder_file_2' }.should_not be_empty
90
+
91
+ multi_part_file = bucketfiles.detect { |file| file[:name] == 'test_bucket_minder_multi_file' }
92
+ multi_part_file.should_not be_empty
93
+ multi_part_file[:keys].should == ["test_bucket_minder_multi_file.part1", "test_bucket_minder_multi_file.part2"]
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+ describe "outside of us-east-1" do
100
+ before do
101
+ @config = spec_config
102
+ @bucket_name = "ey-backup-non-us-#{Digest::SHA1.hexdigest(@config['aws_secret_id'])[0..11]}"
103
+ @region = 'ap-northeast-1'
104
+
105
+ setup_s3(@config['aws_secret_id'], @config['aws_secret_key'], @region)
106
+ end
107
+
108
+ it "creates the bucket in the proper region" do
109
+ setup_bucket(@config['aws_secret_id'], @config['aws_secret_key'], @bucket_name, @region)
110
+ @bucket_minder.bucket.location.should == @region
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,11 @@
1
+ ---
2
+ tmp_dir:
3
+ data_json:
4
+ mysql_user: root
5
+ mysql_password:
6
+ mysql_host: localhost
7
+ postgresql_user: root
8
+ postgresql_password:
9
+ postgresql_host: localhost
10
+ aws_secret_id: 'abcd123.id'
11
+ aws_secret_key: 'abcd123.key'
@@ -0,0 +1,63 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe "EyApi" do
4
+ describe "#get_json" do
5
+ before(:each) do
6
+ @api = Object.new
7
+ @api.instance_variable_set("@rest", RestClient::Resource.new('http://example.org'))
8
+ @api.instance_variable_set("@env", @mock_environment_name)
9
+ @api.instance_variable_set("@keys", {})
10
+ @api.extend(EyApi)
11
+
12
+ @api.stub!(:sleep)
13
+ end
14
+
15
+ after(:each) do
16
+ FakeWeb.clean_registry
17
+ end
18
+
19
+ describe "when the JSON is available" do
20
+ before(:each) do
21
+ FakeWeb.register_uri(:post, "http://example.org/api/json_for_instance", :body => JSON.generate({:valid => "json"}))
22
+ end
23
+
24
+ it "should pass through exceptions that it receives" do
25
+ JSON.stub!(:parse).and_raise(Exception)
26
+ lambda {
27
+ @api.get_json("i-decafbad")
28
+ }.should raise_error
29
+ end
30
+
31
+ it "should return a valid JSON structure" do
32
+ presumably_valid_json = @api.get_json("i-feedface")
33
+ lambda {
34
+ JSON[presumably_valid_json]
35
+ }.should_not raise_error
36
+ end
37
+ end
38
+
39
+ describe "when the JSON is not yet available" do
40
+
41
+ it "should retry until the 503 goes away" do
42
+ FakeWeb.register_uri(:post, "http://example.org/api/json_for_instance", [{
43
+ :status => ["503", "Service Unavailable"],
44
+ :body => "{}",
45
+ }, {
46
+ :status => ["503", "Service Unavailable"],
47
+ :body => "{}",
48
+ }, {
49
+ :status => ["200", "OK"],
50
+ :body => JSON.generate({:valid => 'json'}),
51
+ }]
52
+ )
53
+
54
+ lambda {
55
+ JSON[@api.get_json("i-robot")]
56
+ }.should_not raise_error
57
+ end
58
+
59
+ end
60
+ end
61
+ end
62
+
63
+
@@ -0,0 +1,12 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe EY::Backup do
4
+ before do
5
+ @backend = EY::Backup::Backend.new( spec_config['aws_secret_id'], spec_config['aws_secret_key'], 'ap-northeast-1', 'test-backup-bucket')
6
+ end
7
+
8
+ it "uses correct region" do
9
+ @backend.bucket_minder.bucket.location.should == 'ap-northeast-1'
10
+ @backend.bucket_minder.bucket_name == "test-backup-bucket"
11
+ end
12
+ end
@@ -0,0 +1,54 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe EY::Backup do
4
+ before(:each) do
5
+ @db_name = create_mysql_database('first')
6
+ end
7
+
8
+ describe "#list" do
9
+
10
+ it 'prints the database to be listed first' do
11
+ EY::Backup.run([ "-c", backup_config_file ])
12
+
13
+ reset_logger
14
+
15
+ EY::Backup.run(["-c", backup_config_file, "-l", @db_name])
16
+
17
+ stdout.should =~ /\AListing database backups for #{@db_name}$/
18
+ end
19
+
20
+ it 'conforms to the /^\d:#{db_name} #{db_name}\.#{timestamp}\.#{ext}$/ line output' do
21
+ EY::Backup.run([ "-c", backup_config_file ])
22
+
23
+ reset_logger
24
+
25
+ EY::Backup.run(["-c", backup_config_file, "-l", @db_name])
26
+
27
+ stdout.should =~ /^\d+:[a-zA-Z0-9_\-]+\s+[a-zA-Z0-9_\-]+\.[T0-9\-]+\.[.a-zA-Z0-9]+$/
28
+ end
29
+ end
30
+
31
+ describe "#list_all" do
32
+ it 'prints all the database names' do
33
+ @db_name2 = create_mysql_database('second')
34
+
35
+ EY::Backup.run([ "-c", backup_config_file ])
36
+
37
+ reset_logger
38
+
39
+ EY::Backup.run(["-c", backup_config_file, '-l', ''])
40
+
41
+ stdout.should =~ /^\d+:#{@db_name}/
42
+ stdout.should =~ /^\d+:#{@db_name2}/
43
+ end
44
+ end
45
+
46
+ describe "cleanup" do
47
+ it "removes the last backup" do
48
+ set_keep 5
49
+ 6.times do
50
+ EY::Backup.run([ "-c", backup_config_file ])
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,35 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe "MySQL Backups" do
4
+ before(:each) do
5
+ @db_name = create_mysql_database('first')
6
+ end
7
+
8
+ describe "--quiet" do
9
+ it "does not print anything to STDOUT" do
10
+ capturing_stdio do
11
+ reset_logger
12
+ EY::Backup.run(["-c", backup_config_file, '--quiet'])
13
+ end
14
+
15
+ last_stdout.should be_empty
16
+ end
17
+
18
+ it 'does print to STDOUT without the flag' do
19
+ EY::Backup.logger = nil
20
+
21
+ result = capturing_stdio do
22
+ EY::Backup.run(["-c", backup_config_file ])
23
+ end
24
+
25
+ last_stdout.should_not be_empty
26
+ EY::Backup.logger = nil
27
+
28
+ result = capturing_stdio do
29
+ EY::Backup.run(["-c", backup_config_file ])
30
+ end
31
+
32
+ last_stdout.should_not be_empty
33
+ end
34
+ end
35
+ end