ec2-snapshot 0.1
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.
- 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
|