Permalink
Browse files

Attach an EBS volume to an EC2 instance and refactored a bit.

  • Loading branch information...
alloy committed Jul 9, 2009
1 parent 8fe9bf3 commit a9e2506c0f8b1a1aa296f28a6d427afffd7a8650
Showing with 78 additions and 31 deletions.
  1. +13 −10 lib/backup.rb
  2. +20 −1 lib/ec2.rb
  3. +25 −16 test/backup_test.rb
  4. +16 −1 test/ec2_test.rb
  5. +2 −1 test/fixtures/config.yml
  6. +2 −2 test/fixtures/describe-volumes
View
@@ -11,29 +11,32 @@ class Backup
executable :mysql
executable :mysqldump
- attr_reader :paths, :tmp_path
- attr_reader :ami, :ec2, :ec2_instance_id
+ attr_reader :config
+ attr_reader :ec2, :ec2_instance_id
def initialize(config_file)
- config = YAML.load(File.read(config_file))
-
- @tmp_path = config['backup']['tmp']
- @paths = config['backup']['paths']
- @ami = config['ec2']['ami']
- @ec2 = Fingertips::EC2.new(config['ec2']['private_key_file'], config['ec2']['certificate_file'])
+ @config = YAML.load(File.read(config_file))
+ @ec2 = Fingertips::EC2.new(@config['ec2']['private_key_file'], @config['ec2']['certificate_file'])
+ end
+
+ def tmp_path
+ @config['backup']['tmp']
end
def bring_backup_volume_online!
- @ec2_instance_id = @ec2.run_instance(@ami)
+ @ec2_instance_id = @ec2.run_instance(@config['ec2']['ami'], :k => @config['ec2']['keypair_name'], :z => @config['ec2']['zone'])
sleep 2.5 until @ec2.running?(@ec2_instance_id)
+
+ @ec2.attach_volume(@config['ec2']['ebs'], @ec2_instance_id, :d => "/dev/sdh")
+ sleep 2.5 until @ec2.attached?(@config['ec2']['ebs'])
end
def mysql_databases
@mysql_databases ||= mysql('--batch --skip-column-names -e "show databases"').strip.split("\n")
end
def mysql_dump_dir
- File.join(@tmp_path, 'mysql')
+ File.join(tmp_path, 'mysql')
end
def create_mysql_dump
View
@@ -22,6 +22,8 @@ def env
{ 'EC2_HOME' => HOME, 'EC2_PRIVATE_KEY' => @private_key_file, 'EC2_CERT' => @certificate_file }
end
+ # EC2
+
def run_instance(ami, options = {})
ec2_run_instances("#{ami} #{concat_args(options)}", :env => env)[1][1]
end
@@ -35,19 +37,36 @@ def terminate_instance(instance_id)
end
def running?(instance_id)
- describe_instance(instance_id)[5] == "running"
+ describe_instance(instance_id)[5] == 'running'
end
def host_of_instance(instance_id)
describe_instance(instance_id)[3]
end
+ # EBS
+
+ def attach_volume(volume_id, instance_id, options = {})
+ ec2_attach_volume("#{volume_id} -i #{instance_id} #{concat_args(options)}", :env => env)
+ end
+
+ def describe_volume(volume_id)
+ ec2_describe_volumes(volume_id, :env => env).detect { |line| line[0] == 'ATTACHMENT' && line[1] == volume_id }
+ end
+
+ def attached?(volume_id)
+ describe_volume(volume_id)[4] == 'attached'
+ end
+
private
executable 'ec2-run-instances'
executable 'ec2-describe-instances'
executable 'ec2-terminate-instances'
+ executable 'ec2-attach-volume'
+ executable 'ec2-describe-volumes'
+
def parse(text)
text.strip.split("\n").map { |line| line.split("\t") }
end
View
@@ -6,12 +6,8 @@
@backup = Fingertips::Backup.new(fixture('config.yml'))
end
- it "should return the paths to backup" do
- @backup.paths.should == @config['backup']['paths']
- end
-
- it "should return the path to the tmp backup dirs" do
- @backup.tmp_path.should == '/tmp/ec2_test_backup'
+ it "should return the config" do
+ @backup.config.should == @config
end
it "should return a list of all MySQL databases" do
@@ -20,10 +16,6 @@
databases.should == `mysql --batch --skip-column-names -e "show databases"`.strip.split("\n")
end
- it "should return the AMI ID" do
- @backup.ami.should == 'ami-nonexistant'
- end
-
it "should return a configured Fingertips::EC2 instance" do
@backup.ec2.should.be.instance_of Fingertips::EC2
@backup.ec2.private_key_file.should == @config['ec2']['private_key_file']
@@ -70,15 +62,21 @@ def strip_comments(sql)
describe "Fingertips::Backup, concerning syncing with EBS" do
before do
@backup = Fingertips::Backup.new(fixture('config.yml'))
+ @ec2 = @backup.ec2
+
+ @ec2.stubs(:run_instance).returns("i-nonexistant")
+ @ec2.stubs(:running?).returns(true)
+
+ @ec2.stubs(:attach_volume)
+ @ec2.stubs(:attached?).returns(true)
end
it "should run an EC2 instance and wait till it's online" do
- ec2 = @backup.ec2
- ec2.expects(:run_instance).with(@backup.ami).returns("i-nonexistant")
+ @ec2.expects(:run_instance).with('ami-nonexistant', :k => 'fingertips', :z => 'eu-west-1a').returns("i-nonexistant")
- ec2.expects(:running?).with do |id|
+ @ec2.expects(:running?).with do |id|
# next time it's queried it will be running
- def ec2.running?(id)
+ def @ec2.running?(id)
true
end
@@ -89,7 +87,18 @@ def ec2.running?(id)
@backup.ec2_instance_id.should == "i-nonexistant"
end
- xit "should mount the configured EBS instance and wait till it's online" do
- @backup.ebs.expects(:mount_on).with("i-nonexistant")
+ it "should attach the existing EBS instance and wait till it's online" do
+ @ec2.expects(:attach_volume).with("vol-nonexistant", "i-nonexistant", :d => "/dev/sdh")
+
+ @ec2.expects(:attached?).with do |id|
+ # next time it's queried it will be attached
+ def @ec2.attached?(id)
+ true
+ end
+
+ id == "vol-nonexistant"
+ end.returns(false)
+
+ @backup.bring_backup_volume_online!
end
end
View
@@ -42,7 +42,7 @@
it "should terminate an instance" do
expect_call('terminate-instances', 'i-nonexistant')
- @instance.terminate_instance('i-nonexistant').should == "shutting-down"
+ @instance.terminate_instance('i-nonexistant').should == 'shutting-down'
end
it "should return if an instance is running" do
@@ -55,6 +55,21 @@
@instance.host_of_instance('i-nonexistant').should == 'ec2-174-129-88-205.compute-1.amazonaws.com'
end
+ it "should attach an EBS volume to an EC2 instance" do
+ expect_call('attach-volume', 'vol-nonexistant -i i-nonexistant -d /dev/sdh')
+ @instance.attach_volume('vol-nonexistant', 'i-nonexistant', :d => '/dev/sdh')
+ end
+
+ it "should return the status of a volume" do
+ expect_call('describe-volumes', 'vol-nonexistant')
+ @instance.describe_volume('vol-nonexistant').should == @instance.send(:parse, fixture_read('describe-volumes'))[1]
+ end
+
+ it "should return if a volume is attached" do
+ expect_call('describe-volumes', 'vol-nonexistant')
+ @instance.attached?('vol-nonexistant').should.be true
+ end
+
private
def expect_call(name, args)
View
@@ -1,9 +1,10 @@
ec2:
+ zone: eu-west-1a
ebs: vol-nonexistant
ami: ami-nonexistant
+ keypair_name: fingertips
private_key_file: /Volumes/Fingertips Confidential/aws/fingertips/pk-6LN7EWTYKIDRU25OJYMTY6P75S43WA45.pem
certificate_file: /Volumes/Fingertips Confidential/aws/fingertips/cert-6LN7EWTYKIDRU25OJYMTY6P75S43WA45.pem
- zone: eu-west-1a
backup:
tmp: /tmp/ec2_test_backup
paths:
@@ -1,2 +1,2 @@
-VOLUME vol-21e70e48 1 us-east-1d in-use 2009-07-09T13:41:40+0000
-ATTACHMENT vol-21e70e48 i-ad2f19c4 /dev/sdh attached 2009-07-09T13:46:45+0000
+VOLUME vol-nonexistant 1 us-east-1d in-use 2009-07-09T13:41:40+0000
+ATTACHMENT vol-nonexistant i-ad2f19c4 /dev/sdh attached 2009-07-09T13:46:45+0000

0 comments on commit a9e2506

Please sign in to comment.