ec2-snapshot 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Gemfile +3 -0
- data/LICENSE +19 -0
- data/README.md +122 -0
- data/Rakefile +4 -0
- data/bin/ec2-snapshot +64 -0
- data/ec2-snapshot.gemspec +21 -0
- data/lib/ec2-snapshot.rb +1 -0
- data/lib/ec2_snapshot.rb +2 -0
- data/lib/ec2_snapshot/instance.rb +88 -0
- data/lib/ec2_snapshot/version.rb +3 -0
- data/lib/ec2_snapshot/volume.rb +63 -0
- data/test/helper.rb +3 -0
- data/test/test_instance.rb +118 -0
- data/test/test_volume.rb +76 -0
- metadata +103 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2011 Voormedia B.V.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
EC2-snapshot - create snapshots from all mounted EBS volumes
|
2
|
+
============================================================
|
3
|
+
|
4
|
+
EC2-snapshot is gem that allows you to easily create snapshots for mounted EBS volumes on
|
5
|
+
an EC2 instance.
|
6
|
+
|
7
|
+
The idea originated from the wish of automating snapshot creation for EC2 instances and making
|
8
|
+
it easy to integrate it into Chef.
|
9
|
+
|
10
|
+
EC2-snapshot was written with the idea of it running only on the EC2 instance for which snapshots
|
11
|
+
need to be created. Because we wanted to use it with Chef, we needed to keep configuration to a minimum.
|
12
|
+
There is no need to provide an instance id or volume ids, as that will already be retrieved on the
|
13
|
+
instance itself. The only necessities are the AWS credentials, region and an option to set which types
|
14
|
+
of volumes need to be snapshotted.
|
15
|
+
|
16
|
+
Therefore, the current implementation doesn't support the case of using a snapshotting server that creates snapshots
|
17
|
+
for all volumes. It is meant to run on all servers that require snapshots to be created of its volumes.
|
18
|
+
|
19
|
+
|
20
|
+
Features
|
21
|
+
--------
|
22
|
+
|
23
|
+
* No need to specify instance id and volume ids while using the gem
|
24
|
+
* Recognizes XFS filesystems, and looks up mount points automatically
|
25
|
+
* Freezes XFS filesystems while creating the snapshot, resulting in a consistent snapshot
|
26
|
+
* Easy integration within Chef, requiring a very simple recipe that only requires (globally defined) AWS credentials and a region
|
27
|
+
* Easy integration within your own scripts with either the executable or by instantiating the `Ec2Snapshot::Instance` class yourself
|
28
|
+
* Custom actions that need to be executed before and/or after the snapshot is created can be easily configured
|
29
|
+
|
30
|
+
|
31
|
+
Requirements
|
32
|
+
------------
|
33
|
+
|
34
|
+
There are some requirements for using this gem:
|
35
|
+
|
36
|
+
* The gem only works on Linux, as it has dependencies on files such as `/etc/hostname` and `/proc/mounts`
|
37
|
+
* `wget` needs to be installed. This is required to automatically retrieve the current instance id
|
38
|
+
* `xfs_freeze` (included in xfsprogs package) needs to be installed in order to be able to freeze a XFS filesystem to get a consistent snapshot
|
39
|
+
|
40
|
+
|
41
|
+
Getting started
|
42
|
+
---------------
|
43
|
+
|
44
|
+
Installing the Gem is pretty straightforward:
|
45
|
+
|
46
|
+
gem install ec2-snapshot
|
47
|
+
|
48
|
+
Note that `/etc/hostname` is used to get the name of the current instance.
|
49
|
+
Also `/proc/mounts` is used to retrieve information on the filesystems to be snapshotted.
|
50
|
+
|
51
|
+
|
52
|
+
Using the executable
|
53
|
+
--------------------
|
54
|
+
|
55
|
+
An executable has been provided to easily use the gem within your own scripts.
|
56
|
+
|
57
|
+
The executable requires a few mandatory details to be able to use your AWS account. These are:
|
58
|
+
|
59
|
+
* `AWS Access Key`: The access key defaults to `ENV["AWS_ACCESS_KEY_ID"]`.
|
60
|
+
If the environment variable is not set the value should be provided as an option while using the executable,
|
61
|
+
ie. `--aws-access-key KEY`
|
62
|
+
* `AWS Secret Access Key`: The secret access key defaults to `ENV["AWS_SECRET_ACCESS_KEY"]`.
|
63
|
+
If the environment variable is not set the value should be provided as an option while using the executable,
|
64
|
+
ie. `--aws-secret-access-key KEY`
|
65
|
+
* `AWS Region`: The region on which the volumes have been created. Needs to be provided as an option,
|
66
|
+
ie. `--aws-region eu-west-1`
|
67
|
+
|
68
|
+
The gem makes a distinction between root volumes and data volumes. The root volume is the volume on which the OS
|
69
|
+
is installed, while the data volumes are other volumes mounted on the same instance that could for example be used to store
|
70
|
+
application specific data.
|
71
|
+
|
72
|
+
By default, EC2-snapshot will attempt to create snapshots of all volumes mounted on the
|
73
|
+
current instance. In case you only need snapshots of the data volumes, which could be a valid case when using Chef,
|
74
|
+
you can easily specify that by using the `--volume-type` option:
|
75
|
+
|
76
|
+
ec2-snapshot --aws-access-key ACCESS_KEY --aws-secret-access-key KEY --aws-region us-west-1 --volume-type data
|
77
|
+
|
78
|
+
For a complete list of supported options, please execute
|
79
|
+
|
80
|
+
ec2-snapshot -h
|
81
|
+
|
82
|
+
Take note that `xfs_freeze` requires the process to run with root privileges (sudo), otherwise the XFS filesystem
|
83
|
+
cannot be frozen.
|
84
|
+
|
85
|
+
|
86
|
+
Security
|
87
|
+
--------
|
88
|
+
|
89
|
+
The most secure way of using EC2-snapshot is to create a new user with AWS IAM and allow that user to only execute a
|
90
|
+
specific set of actions.
|
91
|
+
|
92
|
+
The following actions are required:
|
93
|
+
|
94
|
+
* ec2:CreateSnapshot
|
95
|
+
* ec2:CreateTags
|
96
|
+
* ec2:DeleteSnapshot
|
97
|
+
* ec2:DescribeInstances
|
98
|
+
* ec2:DescribeSnapshots
|
99
|
+
|
100
|
+
By restricting the user to only these actions, you can make sure that the access keys required for EC2-instance cannot
|
101
|
+
be misused to manipulate the instances themselves.
|
102
|
+
|
103
|
+
|
104
|
+
Inspiration
|
105
|
+
-----------
|
106
|
+
|
107
|
+
EC2-snapshot was inspired by [ec2-consistent-snapshot](https://launchpad.net/ec2-consistent-snapshot) and
|
108
|
+
its Ruby port [ec2-consistent-snapshot-rb](http://rubygems.org/gems/ec2-consistent-snapshot-rb).
|
109
|
+
|
110
|
+
|
111
|
+
About EC2-snapshot
|
112
|
+
------------------
|
113
|
+
|
114
|
+
EC2-snapshot was created by Mattijs van Druenen (m.vandruenen *at* voormedia.com)
|
115
|
+
|
116
|
+
Copyright 2011 Voormedia - [www.voormedia.com](http://www.voormedia.com/)
|
117
|
+
|
118
|
+
|
119
|
+
License
|
120
|
+
-------
|
121
|
+
|
122
|
+
EC2-snapshot is released under the MIT license.
|
data/Rakefile
ADDED
data/bin/ec2-snapshot
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "ec2_snapshot"
|
4
|
+
require "optparse"
|
5
|
+
require "date"
|
6
|
+
|
7
|
+
options = Hash.new
|
8
|
+
|
9
|
+
optparse = OptionParser.new do |opts|
|
10
|
+
opts.banner = "Usage: ec2-snapshot [options]"
|
11
|
+
|
12
|
+
options[:volume_type] = :all
|
13
|
+
opts.on("--volume-type TYPE", [:all, :root, :data], "Volume types that need to be snapshotted (all/root/data). Defaults to all.") do |volume_type|
|
14
|
+
options[:volume_type] = volume_type
|
15
|
+
end
|
16
|
+
|
17
|
+
options[:aws_access_key] = ENV["AWS_ACCESS_KEY_ID"]
|
18
|
+
opts.on("--aws-access-key KEY", "AWS Access Key.Either set ENV[\"AWS_ACCESS_KEY_ID\"] or provide it as an option.") do |access_key|
|
19
|
+
options[:aws_access_key] = access_key
|
20
|
+
end
|
21
|
+
|
22
|
+
options[:aws_secret_access_key] = ENV["AWS_SECRET_ACCESS_KEY"]
|
23
|
+
opts.on("--aws-secret-access-key KEY", "AWS Secret Access Key. Either set ENV[\"AWS_SECRET_ACCESS_KEY\"] or provide it as an option.") do |secret_access_key|
|
24
|
+
options[:aws_secret_access_key] = secret_access_key
|
25
|
+
end
|
26
|
+
|
27
|
+
opts.on("--aws-region REGION", "AWS Region. Mandatory. Example regions are eu-west-1 and us-west-1") do |region|
|
28
|
+
options[:aws_region] = region
|
29
|
+
end
|
30
|
+
|
31
|
+
opts.on("--before BEFORE", "Shell command to run before volume is snapshotted. Optional.") do |before|
|
32
|
+
options[:before] = before
|
33
|
+
end
|
34
|
+
|
35
|
+
opts.on("--after AFTER", "Shell command to run after volume is snapshotted. Optional.") do |after|
|
36
|
+
options[:after] = after
|
37
|
+
end
|
38
|
+
|
39
|
+
opts.on("--delete-older-than MONTHS", "Delete snapshots for this instance that are older than # months. Optional.") do |months|
|
40
|
+
options[:delete_older_than] = months.to_i
|
41
|
+
end
|
42
|
+
|
43
|
+
options[:verbose] = false
|
44
|
+
opts.on("-v", "--verbose", "Display progress output.") do |verbose|
|
45
|
+
options[:verbose] = verbose
|
46
|
+
end
|
47
|
+
|
48
|
+
opts.on("-h", "--help", "Display this screen.") do
|
49
|
+
puts opts
|
50
|
+
exit
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
optparse.parse!
|
55
|
+
|
56
|
+
if options[:aws_access_key].nil? or options[:aws_secret_access_key].nil? or options[:aws_region].nil?
|
57
|
+
Kernel.abort("Not all mandatory options have been provided. Exiting.")
|
58
|
+
end
|
59
|
+
|
60
|
+
instance = Ec2Snapshot::Instance.new(options)
|
61
|
+
instance.enable_rootvol_snapshot if [:all, :root].include?(options[:volume_type])
|
62
|
+
instance.enable_datavol_snapshot if [:all, :data].include?(options[:volume_type])
|
63
|
+
instance.create_snapshots
|
64
|
+
instance.delete_snapshots(Date.today << options[:delete_older_than]) if options.include?(:delete_older_than)
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path("../lib/ec2_snapshot/version", __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.name = "ec2-snapshot"
|
6
|
+
gem.authors = ["mattijsvandruenen"]
|
7
|
+
gem.email = ["m.vandruenen@voormedia.com"]
|
8
|
+
gem.description = %q{EC2 EBS Volume Snapshotting}
|
9
|
+
gem.summary = %q{Create consistent snapshots of EBS volumes on Amazon EC2 instances.}
|
10
|
+
gem.homepage = "https://github.com/voormedia/ec2-snapshot"
|
11
|
+
|
12
|
+
gem.add_runtime_dependency "right_aws"
|
13
|
+
gem.add_development_dependency "minitest", "~> 2.8.0"
|
14
|
+
gem.add_development_dependency "mocha"
|
15
|
+
|
16
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
|
+
gem.files = `git ls-files`.split("\n")
|
18
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
gem.require_paths = ["lib"]
|
20
|
+
gem.version = Ec2Snapshot::VERSION
|
21
|
+
end
|
data/lib/ec2-snapshot.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'ec2_snapshot'
|
data/lib/ec2_snapshot.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
require "date"
|
2
|
+
require "right_aws"
|
3
|
+
|
4
|
+
module Ec2Snapshot
|
5
|
+
class Instance
|
6
|
+
attr_accessor :ec2, :ec2_info, :create_rootvol_snapshot, :create_datavol_snapshot, :before, :after, :verbose
|
7
|
+
|
8
|
+
def initialize(options = {})
|
9
|
+
@ec2 = RightAws::Ec2.new(options[:aws_access_key], options[:aws_secret_access_key], :region => options[:aws_region], :logger => Logger.new('/dev/null'))
|
10
|
+
@ec2_info = @ec2.describe_instances(instance_id).first
|
11
|
+
@create_rootvol_snapshot = false
|
12
|
+
@create_datavol_snapshot = false
|
13
|
+
@before = options[:before]
|
14
|
+
@after = options[:after]
|
15
|
+
@verbose = options[:verbose] ? options[:verbose] : false
|
16
|
+
end
|
17
|
+
|
18
|
+
def enable_rootvol_snapshot
|
19
|
+
@create_rootvol_snapshot = true
|
20
|
+
end
|
21
|
+
|
22
|
+
def enable_datavol_snapshot
|
23
|
+
@create_datavol_snapshot = true
|
24
|
+
end
|
25
|
+
|
26
|
+
def create_snapshots
|
27
|
+
volumes.each do |volume|
|
28
|
+
next if not volume.requires_snapshot
|
29
|
+
|
30
|
+
puts "preparing snapshot for volume #{volume.volume_id} (#{volume.device_name})" if @verbose
|
31
|
+
custom_actions do
|
32
|
+
volume.freeze_filesystem do
|
33
|
+
volume.create_snapshot
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def delete_snapshots(cut_off_date = Date.today << 3)
|
40
|
+
# This function requires a tag called Hostname to be set for each snapshot of the current instance
|
41
|
+
@ec2.describe_snapshots(:filters => {'tag:Hostname' => hostname}).each do |snapshot|
|
42
|
+
if Date.parse(snapshot[:aws_started_at]) <= cut_off_date
|
43
|
+
puts "deleting snapshot #{snapshot[:aws_id]}" if @verbose
|
44
|
+
@ec2.delete_snapshot(snapshot[:aws_id])
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def custom_actions(&block)
|
50
|
+
if @before
|
51
|
+
puts "executing before command" if @verbose
|
52
|
+
Kernel.system(@before)
|
53
|
+
end
|
54
|
+
begin
|
55
|
+
yield
|
56
|
+
rescue Exception => ex
|
57
|
+
puts "exception thrown during snapshot creation: #{ex}" if @verbose
|
58
|
+
ensure
|
59
|
+
if @after
|
60
|
+
puts "executing after command" if @verbose
|
61
|
+
Kernel.system(@after)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def instance_id
|
67
|
+
# See http://docs.amazonwebservices.com/AWSEC2/latest/UserGuide/index.html?AESDG-chapter-instancedata.html
|
68
|
+
output = %x[wget -T 5 -t 1 -q -O - http://169.254.169.254/latest/meta-data/instance-id]
|
69
|
+
raise if output.empty?
|
70
|
+
output
|
71
|
+
rescue
|
72
|
+
# catch both empty output and missing wget
|
73
|
+
raise "Failed to retrieve the current instance id"
|
74
|
+
end
|
75
|
+
|
76
|
+
def hostname
|
77
|
+
%x[cat /etc/hostname].chomp
|
78
|
+
end
|
79
|
+
|
80
|
+
def root_device_name
|
81
|
+
@ec2_info[:root_device_name]
|
82
|
+
end
|
83
|
+
|
84
|
+
def volumes
|
85
|
+
@ec2_info[:block_device_mappings].collect{ |v| Ec2Snapshot::Volume.new(self, v[:ebs_volume_id], v[:device_name]) }
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Ec2Snapshot
|
2
|
+
class Volume
|
3
|
+
attr_accessor :instance, :volume_id, :device_name, :mount_point, :file_system
|
4
|
+
|
5
|
+
def initialize(instance, volume_id, device_name)
|
6
|
+
@instance = instance
|
7
|
+
@volume_id = volume_id
|
8
|
+
@device_name = device_name
|
9
|
+
init_mount_info
|
10
|
+
end
|
11
|
+
|
12
|
+
def requires_snapshot
|
13
|
+
root_device = @instance.root_device_name
|
14
|
+
raise Exception, "No root device could be found" unless root_device
|
15
|
+
(root_device == @device_name and @instance.create_rootvol_snapshot) or (root_device != @device_name and @instance.create_datavol_snapshot)
|
16
|
+
end
|
17
|
+
|
18
|
+
def freeze_filesystem(&block)
|
19
|
+
if @mount_point and @file_system == "xfs"
|
20
|
+
puts "freezing XFS filesystem" if @instance.verbose
|
21
|
+
Kernel.system("xfs_freeze -f #{@mount_point}")
|
22
|
+
end
|
23
|
+
|
24
|
+
begin
|
25
|
+
yield
|
26
|
+
rescue Exception => ex
|
27
|
+
puts "exception thrown during snapshot creation: #{ex}" if @verbose
|
28
|
+
ensure
|
29
|
+
if @mount_point and @file_system == "xfs"
|
30
|
+
puts "unfreezing XFS filesystem" if @instance.verbose
|
31
|
+
Kernel.system("xfs_freeze -u #{@mount_point}")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def create_snapshot
|
37
|
+
puts "creating and tagging snapshot" if @instance.verbose
|
38
|
+
snapshot = @instance.ec2.create_snapshot(@volume_id, "#{@instance.hostname}: automated snapshot #{@device_name} (#{@volume_id})")
|
39
|
+
# The only way to set the name of a snapshot is by creating a name tag for the snapshot
|
40
|
+
@instance.ec2.create_tags(snapshot[:aws_id], { "Name" => "#{@instance.hostname} (#{@device_name})", "Hostname" => @instance.hostname })
|
41
|
+
end
|
42
|
+
|
43
|
+
def xfs_device_name
|
44
|
+
# Required for Ubuntu Natty Narwhal and up, which uses xvdX, while Amazon uses sdX
|
45
|
+
@device_name.gsub("/sd", "/xvd")
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def init_mount_info
|
51
|
+
mounts = %x[cat /proc/mounts].split("\n")
|
52
|
+
mounts.each do |mount|
|
53
|
+
parts = mount.split(" ")
|
54
|
+
# Make sure that both Amazons volume naming and Ubuntu naming is matched
|
55
|
+
if [@device_name, xfs_device_name].include?(parts.first)
|
56
|
+
@mount_point = parts[1]
|
57
|
+
@file_system = parts[2]
|
58
|
+
break
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'ec2_snapshot/instance'
|
2
|
+
require 'ec2_snapshot/volume'
|
3
|
+
require File.expand_path("../helper", __FILE__)
|
4
|
+
|
5
|
+
class InstanceTest < MiniTest::Unit::TestCase
|
6
|
+
def setup
|
7
|
+
# Mock system calls
|
8
|
+
Ec2Snapshot::Instance.any_instance.stubs(:`).returns("output")
|
9
|
+
Ec2Snapshot::Volume.any_instance.stubs(:`).returns("/dev/xvdf /srv xfs rw,noatime,attr2,delaylog,noquota 0 0")
|
10
|
+
# Mock right_aws init, return mock object and mock methods on that object
|
11
|
+
m = MiniTest::Mock.new
|
12
|
+
RightAws::Ec2.stubs(:new).returns(m)
|
13
|
+
m.stubs(:describe_instances).returns([{:root_device_type=>"ebs", :root_device_name=>"/dev/sda1", :block_device_mappings=>[{:device_name=>"/dev/sda1", :ebs_volume_id=>"vol-id1", :ebs_status=>"attached", :ebs_attach_time=>"2011-12-06T14:48:27.000Z", :ebs_delete_on_termination=>true}, {:device_name=>"/dev/sdf", :ebs_volume_id=>"vol-id2", :ebs_status=>"attached", :ebs_attach_time=>"2011-12-06T14:48:27.000Z", :ebs_delete_on_termination=>false}]}])
|
14
|
+
|
15
|
+
# Init Ec2Snapshot::Instance to work with
|
16
|
+
@instance = Ec2Snapshot::Instance.new({ :access_key => "accesskey", :secret_access_key => "secretkey", :region => "region" })
|
17
|
+
end
|
18
|
+
|
19
|
+
# new
|
20
|
+
def test_create_new_instance_should_fail_if_required_ec2_credentials_are_unavailable
|
21
|
+
assert_raises NoMethodError do
|
22
|
+
Ec2Snapshot::Instance.new(nil)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_create_new_instance_should_not_fail_if_required_ec2_credentials_are_available
|
27
|
+
assert_instance_of Ec2Snapshot::Instance, @instance
|
28
|
+
end
|
29
|
+
|
30
|
+
# custom_actions
|
31
|
+
def test_custom_actions_should_execute_before_action_if_set
|
32
|
+
Kernel.expects(:system).with("test")
|
33
|
+
@instance.before = "test"
|
34
|
+
@instance.custom_actions do
|
35
|
+
true
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_custom_actions_should_execute_after_action_if_set
|
40
|
+
Kernel.expects(:system).with("test")
|
41
|
+
@instance.after = "test"
|
42
|
+
@instance.custom_actions do
|
43
|
+
true
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_custom_actions_should_execute_after_action_even_if_block_throws_an_exception
|
48
|
+
Kernel.expects(:system).with("test")
|
49
|
+
@instance.after = "test"
|
50
|
+
@instance.custom_actions do
|
51
|
+
raise Exception
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# enable_rootvol_snapshot
|
56
|
+
def test_enable_rootvol_snapshot_should_return_true
|
57
|
+
assert @instance.enable_rootvol_snapshot
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_enable_rootvol_snapshot_should_set_instance_variable
|
61
|
+
assert !@instance.create_rootvol_snapshot
|
62
|
+
@instance.enable_rootvol_snapshot
|
63
|
+
assert @instance.create_rootvol_snapshot
|
64
|
+
end
|
65
|
+
|
66
|
+
# enable_datavol_snapshot
|
67
|
+
def test_enable_datavol_snapshot_should_return_true
|
68
|
+
assert @instance.enable_datavol_snapshot
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_enable_datavol_snapshot_should_set_instance_variable
|
72
|
+
assert !@instance.create_datavol_snapshot
|
73
|
+
@instance.enable_datavol_snapshot
|
74
|
+
assert @instance.create_datavol_snapshot
|
75
|
+
end
|
76
|
+
|
77
|
+
# instance_id
|
78
|
+
def test_instance_id_should_return_a_string
|
79
|
+
assert_instance_of String, @instance.instance_id
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_instance_id_should_return_output_of_wget_call
|
83
|
+
assert_equal "output", @instance.instance_id
|
84
|
+
end
|
85
|
+
|
86
|
+
# hostname
|
87
|
+
def test_hostname_should_return_a_string
|
88
|
+
assert_instance_of String, @instance.hostname
|
89
|
+
end
|
90
|
+
|
91
|
+
def test_hostname_should_return_hostname
|
92
|
+
assert_equal "output", @instance.instance_id
|
93
|
+
end
|
94
|
+
|
95
|
+
# root_device_name
|
96
|
+
def test_root_device_name_should_return_a_string_if_device_name_is_found
|
97
|
+
assert_instance_of String, @instance.root_device_name
|
98
|
+
end
|
99
|
+
|
100
|
+
def test_root_device_name_should_return_correct_metadata
|
101
|
+
assert_equal "/dev/sda1", @instance.root_device_name
|
102
|
+
end
|
103
|
+
|
104
|
+
# volumes
|
105
|
+
def test_volumes_should_return_an_array
|
106
|
+
assert_instance_of Array, @instance.volumes
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_volumes_should_return_volume_objects
|
110
|
+
@instance.volumes.each do |vol|
|
111
|
+
assert_instance_of Ec2Snapshot::Volume, vol
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def test_volumes_should_return_correct_number_of_volumes
|
116
|
+
assert_equal 2, @instance.volumes.count
|
117
|
+
end
|
118
|
+
end
|
data/test/test_volume.rb
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'ec2_snapshot/volume'
|
2
|
+
require File.expand_path("../helper", __FILE__)
|
3
|
+
|
4
|
+
class VolumeTest < MiniTest::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
# Mock system calls
|
7
|
+
Ec2Snapshot::Instance.any_instance.stubs(:`).returns("output")
|
8
|
+
Ec2Snapshot::Volume.any_instance.stubs(:`).returns("/dev/xvdf /srv xfs rw,noatime,attr2,delaylog,noquota 0 0")
|
9
|
+
# Mock right_aws init, return mock object and mock methods on that object
|
10
|
+
m = MiniTest::Mock.new
|
11
|
+
RightAws::Ec2.stubs(:new).returns(m)
|
12
|
+
m.stubs(:describe_instances).returns([{:root_device_type=>"ebs", :root_device_name=>"/dev/sda1", :block_device_mappings=>[{:device_name=>"/dev/sda1", :ebs_volume_id=>"vol-id1", :ebs_status=>"attached", :ebs_attach_time=>"2011-12-06T14:48:27.000Z", :ebs_delete_on_termination=>true}, {:device_name=>"/dev/sdf", :ebs_volume_id=>"vol-id2", :ebs_status=>"attached", :ebs_attach_time=>"2011-12-06T14:48:27.000Z", :ebs_delete_on_termination=>false}]}])
|
13
|
+
# Init Ec2Snapshot::Instance to work with
|
14
|
+
@instance = Ec2Snapshot::Instance.new({ :access_key => "accesskey", :secret_access_key => "secretkey", :region => "region" })
|
15
|
+
end
|
16
|
+
|
17
|
+
# freeze_filesystem
|
18
|
+
def test_freeze_filesystem_should_freeze_and_unfreeze_filesystem_if_filesystem_is_xfs
|
19
|
+
vol = Ec2Snapshot::Volume.new(@instance, "volume-id", "/dev/sdf")
|
20
|
+
vol.file_system = "xfs"
|
21
|
+
Kernel.expects(:system).with("xfs_freeze -f /srv")
|
22
|
+
Kernel.expects(:system).with("xfs_freeze -u /srv")
|
23
|
+
vol.freeze_filesystem
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_freeze_filesystem_shouldnt_freeze_and_unfreeze_filesystem_if_filesystem_is_not_xfs
|
27
|
+
vol = Ec2Snapshot::Volume.new(@instance, "volume-id", "/dev/sdf")
|
28
|
+
vol.file_system = "fat32"
|
29
|
+
Kernel.expects(:system).never
|
30
|
+
vol.freeze_filesystem
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_freeze_filesystem_should_freeze_and_unfreeze_filesystem_if_filesystem_is_xfs_and_block_throws_an_exception
|
34
|
+
vol = Ec2Snapshot::Volume.new(@instance, "volume-id", "/dev/sdf")
|
35
|
+
vol.file_system = "xfs"
|
36
|
+
Kernel.expects(:system).with("xfs_freeze -f /srv")
|
37
|
+
Kernel.expects(:system).with("xfs_freeze -u /srv")
|
38
|
+
vol.freeze_filesystem do
|
39
|
+
raise Exception
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# xfs_device_name
|
44
|
+
def test_xfs_device_name_should_return_a_string
|
45
|
+
vol = Ec2Snapshot::Volume.new(@instance, "volume-id", "/dev/sdf")
|
46
|
+
assert_instance_of String, vol.xfs_device_name
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_xfs_device_name_should_replace_device_name
|
50
|
+
vol = Ec2Snapshot::Volume.new(@instance, "volume-id", "/dev/sdf")
|
51
|
+
assert_equal "/dev/xvdf", vol.xfs_device_name
|
52
|
+
end
|
53
|
+
|
54
|
+
# requires_snapshot
|
55
|
+
def test_requires_snapshot_should_return_true_if_volume_is_rootvol_and_rootvol_should_be_snapshotted
|
56
|
+
vol = Ec2Snapshot::Volume.new(@instance, "volume-id", "/dev/sda1")
|
57
|
+
@instance.enable_rootvol_snapshot
|
58
|
+
assert vol.requires_snapshot
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_requires_snapshot_should_return_false_if_volume_is_rootvol_and_rootvol_shouldnt_be_snapshotted
|
62
|
+
vol = Ec2Snapshot::Volume.new(@instance, "volume-id", "/dev/sda1")
|
63
|
+
assert !vol.requires_snapshot
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_requires_snapshot_should_return_true_if_volume_is_datavol_and_datavol_should_be_snapshotted
|
67
|
+
vol = Ec2Snapshot::Volume.new(@instance, "volume-id", "/dev/sdf")
|
68
|
+
@instance.enable_datavol_snapshot
|
69
|
+
assert vol.requires_snapshot
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_requires_snapshot_should_return_false_if_volume_is_datavol_and_datavol_shouldnt_be_snapshotted
|
73
|
+
vol = Ec2Snapshot::Volume.new(@instance, "volume-id", "/dev/sdf")
|
74
|
+
assert !vol.requires_snapshot
|
75
|
+
end
|
76
|
+
end
|
metadata
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ec2-snapshot
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: "0.1"
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- mattijsvandruenen
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-12-22 00:00:00 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: right_aws
|
17
|
+
prerelease: false
|
18
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
19
|
+
none: false
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
type: :runtime
|
25
|
+
version_requirements: *id001
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: minitest
|
28
|
+
prerelease: false
|
29
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
30
|
+
none: false
|
31
|
+
requirements:
|
32
|
+
- - ~>
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 2.8.0
|
35
|
+
type: :development
|
36
|
+
version_requirements: *id002
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: mocha
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: "0"
|
46
|
+
type: :development
|
47
|
+
version_requirements: *id003
|
48
|
+
description: EC2 EBS Volume Snapshotting
|
49
|
+
email:
|
50
|
+
- m.vandruenen@voormedia.com
|
51
|
+
executables:
|
52
|
+
- ec2-snapshot
|
53
|
+
extensions: []
|
54
|
+
|
55
|
+
extra_rdoc_files: []
|
56
|
+
|
57
|
+
files:
|
58
|
+
- .gitignore
|
59
|
+
- Gemfile
|
60
|
+
- LICENSE
|
61
|
+
- README.md
|
62
|
+
- Rakefile
|
63
|
+
- bin/ec2-snapshot
|
64
|
+
- ec2-snapshot.gemspec
|
65
|
+
- lib/ec2-snapshot.rb
|
66
|
+
- lib/ec2_snapshot.rb
|
67
|
+
- lib/ec2_snapshot/instance.rb
|
68
|
+
- lib/ec2_snapshot/version.rb
|
69
|
+
- lib/ec2_snapshot/volume.rb
|
70
|
+
- test/helper.rb
|
71
|
+
- test/test_instance.rb
|
72
|
+
- test/test_volume.rb
|
73
|
+
homepage: https://github.com/voormedia/ec2-snapshot
|
74
|
+
licenses: []
|
75
|
+
|
76
|
+
post_install_message:
|
77
|
+
rdoc_options: []
|
78
|
+
|
79
|
+
require_paths:
|
80
|
+
- lib
|
81
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
82
|
+
none: false
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: "0"
|
87
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
88
|
+
none: false
|
89
|
+
requirements:
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: "0"
|
93
|
+
requirements: []
|
94
|
+
|
95
|
+
rubyforge_project:
|
96
|
+
rubygems_version: 1.8.8
|
97
|
+
signing_key:
|
98
|
+
specification_version: 3
|
99
|
+
summary: Create consistent snapshots of EBS volumes on Amazon EC2 instances.
|
100
|
+
test_files:
|
101
|
+
- test/helper.rb
|
102
|
+
- test/test_instance.rb
|
103
|
+
- test/test_volume.rb
|