snapscatter 0.1.0
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 +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +95 -0
- data/Rakefile +1 -0
- data/bin/snapscatter +4 -0
- data/features/snapscatter.feature +19 -0
- data/features/support/setup.rb +5 -0
- data/lib/snapscatter/cli.rb +80 -0
- data/lib/snapscatter/locker.rb +60 -0
- data/lib/snapscatter/version.rb +3 -0
- data/lib/snapscatter.rb +70 -0
- data/snapscatter.gemspec +30 -0
- metadata +192 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Alex Escalante
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
# Snapscatter
|
2
|
+
|
3
|
+
This script creates snapshots from EBS volumes and copies them across regions for disaster recovery.
|
4
|
+
If your volume is used for database storage, this script can make sure your backup is consistent by
|
5
|
+
flushing and stopping writes until the snapshot is complete.
|
6
|
+
|
7
|
+
This script also purges old snapshots according to a simple retention policy specified by you, to keep
|
8
|
+
your Amazon AWS bills under control.
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
Just use the gem command to install:
|
13
|
+
|
14
|
+
$ gem install snapscatter
|
15
|
+
|
16
|
+
## Usage
|
17
|
+
|
18
|
+
You can have a look at the commands and options by just typing the name of the executable:
|
19
|
+
|
20
|
+
Commands:
|
21
|
+
snapscatter create # Create snapshots, optionally copying them to destination region
|
22
|
+
snapscatter help [COMMAND] # Describe available commands or one specific command
|
23
|
+
snapscatter list # Show available snapshots
|
24
|
+
snapscatter purge # purge snapshots older than the specified number of days
|
25
|
+
snapscatter targets # Show volumes tagged for backup
|
26
|
+
|
27
|
+
The best way to use this script is to make a shell wrapper for it that exports your AWS credentials
|
28
|
+
as environment variables and then put it under the control of the cron demon.
|
29
|
+
|
30
|
+
### Specifying volumes to backup
|
31
|
+
|
32
|
+
You should use the AWS console to mark the volumes you want to be backed up using the tag `Backup` with a
|
33
|
+
value of `true`. You can then check the list of these volumes using the command `targets`.
|
34
|
+
|
35
|
+
### Taking snapshots
|
36
|
+
|
37
|
+
Use the command `create` to take snapshots of all the tagged volumes. Snapshots will be taken to your default
|
38
|
+
AWS region, but you can optionally supply the '--destination' flag to create a copy onto another
|
39
|
+
region for disaster recovery.
|
40
|
+
|
41
|
+
Every snapshot taken will have the `PurgeAllow` tag set with the value of `true`. If for some reason you want
|
42
|
+
a snapshot not to be purged indefinitely, you can set this tag to any other value, or even remove the tag
|
43
|
+
altogether.
|
44
|
+
|
45
|
+
### Purging snapshots
|
46
|
+
|
47
|
+
You can call the `purge` command to delete any snapshots older than 30 days. This is the default retention policy
|
48
|
+
but you can change it by using the optional '-d' flag (for `--days).
|
49
|
+
|
50
|
+
You can also run this command with the `-n` (for `--noaction`) to only list the snapshots that would be purged
|
51
|
+
under the specified retention policy, no snapshot will be purged.
|
52
|
+
|
53
|
+
### Listing snapshots
|
54
|
+
|
55
|
+
The `list` command will show all the snapshots subject to be purged, that is, all snapshots with the `PurgAllow`
|
56
|
+
tag set to `true`. The `-f`option for this command gives more information on every snapshot: snapshot id, volume id
|
57
|
+
and date of creation.
|
58
|
+
|
59
|
+
### Consistent backups
|
60
|
+
|
61
|
+
If the volumes your attempting to snapshot are being used by a database, then you want to force a flush to disk and
|
62
|
+
stop writing so you can have a consistent backup. This script can do that for you (currently it only supports MongoDB).
|
63
|
+
|
64
|
+
To use this feature, you can tag the volume with the `Consistent` tag. The value of this tag contains the connection
|
65
|
+
information, as key-value pairs separated by commas, so that the script can have access to the database, flush and
|
66
|
+
stop writes until the snapshot has been taken. Here's an example:
|
67
|
+
|
68
|
+
strategy: mongo, host: 127.0.0.1, port: 27017, usr: admin, pwd: 12345
|
69
|
+
|
70
|
+
## Example
|
71
|
+
|
72
|
+
Create a shell file like the following and put it under cron's control:
|
73
|
+
|
74
|
+
#!/bin/sh
|
75
|
+
|
76
|
+
export AWS_ACCESS_KEY_ID="YOURACCESSKEY"
|
77
|
+
export AWS_SECRET_ACCESS_KEY="YOURSECRETACCESSKEY"
|
78
|
+
|
79
|
+
snapscatter purge -d 20
|
80
|
+
snapscatter create --destination="us-west-1"
|
81
|
+
|
82
|
+
You have to make two calls because the script won't purge and create snapshots on a single call.
|
83
|
+
|
84
|
+
## TODO
|
85
|
+
|
86
|
+
* Take consistency speficication out of the volume and put it in a configuration file
|
87
|
+
* More database connectors
|
88
|
+
|
89
|
+
## Contributing
|
90
|
+
|
91
|
+
1. Fork it
|
92
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
93
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
94
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
95
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/snapscatter
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
@announce-stdout
|
2
|
+
Feature: snapscatter
|
3
|
+
In order to create and distribute snapshots
|
4
|
+
As a CLI
|
5
|
+
I can execute several different commands
|
6
|
+
|
7
|
+
Scenario: show available commands and options
|
8
|
+
When I run `snapscatter`
|
9
|
+
Then the output should contain "targets"
|
10
|
+
And the output should contain "list"
|
11
|
+
And the output should contain "go"
|
12
|
+
|
13
|
+
Scenario: show all volumes available for snapshot
|
14
|
+
When I run `snapscatter targets`
|
15
|
+
Then the output should match /vol-[0-9a-f]+/
|
16
|
+
|
17
|
+
Scenario: list all current snapshots
|
18
|
+
When I run `snapscatter list`
|
19
|
+
Then the output should match /snap-[0-9a-f]+/
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require_relative '../snapscatter'
|
3
|
+
|
4
|
+
module Snapscatter
|
5
|
+
class CLI < Thor
|
6
|
+
|
7
|
+
desc 'targets', 'Show volumes tagged for backup'
|
8
|
+
method_option :keys, type: :hash, banner: 'AWS security keys'
|
9
|
+
def targets
|
10
|
+
targets = Snapscatter.targets create_ec2
|
11
|
+
targets.each { |target| puts target.id }
|
12
|
+
end
|
13
|
+
|
14
|
+
desc 'list', 'Show available snapshots'
|
15
|
+
method_option :keys, type: :hash, banner: 'AWS security keys'
|
16
|
+
method_option :full, type: :boolean, aliases: '-f', banner: 'Show useful info about snapshots'
|
17
|
+
def list
|
18
|
+
snapshots = Snapscatter.list create_ec2
|
19
|
+
snapshots.each do |snapshot|
|
20
|
+
output = [ snapshot.id ]
|
21
|
+
if options[:full]
|
22
|
+
output << snapshot.volume_id
|
23
|
+
output << snapshot.start_time.strftime("%Y-%m-%d")
|
24
|
+
end
|
25
|
+
puts output.join(" ")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
desc 'purge', 'purge snapshots older than the specified number of days'
|
30
|
+
method_option :keys, type: :hash, banner: 'AWS security keys'
|
31
|
+
method_option :days, type: :numeric, default: 30, aliases: '-d', banner: 'retention policy in days'
|
32
|
+
method_option :noaction, type: :boolean, default: false, aliases: '-n', banner: 'do not purge, just show'
|
33
|
+
def purge
|
34
|
+
purged = Snapscatter.purge create_ec2, options[:days], true # options[:noaction] # remove in production
|
35
|
+
purged.each { |snapshot| puts "#{snapshot.id}" }
|
36
|
+
end
|
37
|
+
|
38
|
+
desc 'create', 'Create snapshots, optionally copying them to destination region'
|
39
|
+
method_option :keys, type: :hash, banner: 'AWS security keys'
|
40
|
+
method_option :destination, type: :string, banner: 'region to copy snapshots to'
|
41
|
+
def create
|
42
|
+
source_ec2 = create_ec2
|
43
|
+
targets = Snapscatter.targets source_ec2
|
44
|
+
targets.each do |volume|
|
45
|
+
snapshot = nil
|
46
|
+
description = nil
|
47
|
+
|
48
|
+
Snapscatter.in_lock volume.tags['Consistent'] do
|
49
|
+
volume_name = volume.tags['Name']
|
50
|
+
date_as_string = Date.today.strftime("%Y-%m-%d")
|
51
|
+
description = "#{volume_name} #{date_as_string}"
|
52
|
+
|
53
|
+
snapshot = volume.create_snapshot description
|
54
|
+
snapshot.add_tag 'VolumeName', value: volume_name
|
55
|
+
snapshot.add_tag 'PurgeAllow', value: "true"
|
56
|
+
|
57
|
+
sleep 1 until [:completed, :error].include?(snapshot.status)
|
58
|
+
end
|
59
|
+
|
60
|
+
if snapshot.status == :completed
|
61
|
+
output = ["created", snapshot.id, description]
|
62
|
+
if options.has_key? 'destination'
|
63
|
+
destination_ec2 = create_ec2(region: options[:destination])
|
64
|
+
Snapscatter.copy destination_ec2, source_ec2.client.config.region, snapshot, description
|
65
|
+
output << "#{options[:destination]}"
|
66
|
+
end
|
67
|
+
puts output.join(" ")
|
68
|
+
else
|
69
|
+
puts "#{volume.id} (#{volume_name}): snapshot failed"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
def create_ec2 ec2_options={}
|
76
|
+
ec2_options.merge! options[:keys] if options.has_key? :keys
|
77
|
+
ec2 = AWS::EC2.new(ec2_options)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'mongo'
|
2
|
+
|
3
|
+
module Snapscatter
|
4
|
+
|
5
|
+
class Locker
|
6
|
+
|
7
|
+
def initialize spec
|
8
|
+
strategy = spec[:strategy] && spec.delete(:strategy)
|
9
|
+
case strategy
|
10
|
+
when 'mongo'
|
11
|
+
@strategy = MongoLocker.new spec
|
12
|
+
else
|
13
|
+
@strategy = NoOpLocker.new
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def lock
|
18
|
+
@strategy.lock
|
19
|
+
end
|
20
|
+
|
21
|
+
def unlock
|
22
|
+
@strategy.unlock
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class NoOpLocker
|
27
|
+
def method_missing sym
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class MongoLocker
|
32
|
+
def initialize spec
|
33
|
+
@host = spec[:host] && spec.delete(:host)
|
34
|
+
@port = spec[:port] && spec.delete(:port)
|
35
|
+
user = spec[:usr] && spec.delete(:usr)
|
36
|
+
password = spec[:pwd] && spec.delete(:pwd)
|
37
|
+
|
38
|
+
if @host
|
39
|
+
@client = Mongo::MongoClient.new @host, @port, spec # spec contains the options
|
40
|
+
else
|
41
|
+
@client = Mongo::MongoClient.new
|
42
|
+
end
|
43
|
+
|
44
|
+
if user
|
45
|
+
@client.add_auth 'admin', user, password, nil
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def lock
|
50
|
+
@client.lock!
|
51
|
+
puts "locked mongo instance at #{@host}"
|
52
|
+
end
|
53
|
+
|
54
|
+
def unlock
|
55
|
+
@client.unlock!
|
56
|
+
puts "unlocked mongo instance at #{@host}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
data/lib/snapscatter.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'snapscatter/version'
|
2
|
+
require 'snapscatter/locker'
|
3
|
+
require 'aws'
|
4
|
+
|
5
|
+
module Snapscatter
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def parse_spec str
|
10
|
+
str ||= ""
|
11
|
+
spec = {}
|
12
|
+
str.split(',').map do |i|
|
13
|
+
k, v = i.split(':').map { |i| i.strip }
|
14
|
+
spec[k.to_sym] = v
|
15
|
+
end
|
16
|
+
return spec
|
17
|
+
end
|
18
|
+
|
19
|
+
public
|
20
|
+
|
21
|
+
def targets ec2
|
22
|
+
ec2.volumes.tagged('Backup').tagged_values('true')
|
23
|
+
end
|
24
|
+
|
25
|
+
def list ec2
|
26
|
+
ec2.snapshots.tagged('PurgeAllow').tagged_values('true')
|
27
|
+
end
|
28
|
+
|
29
|
+
def copy ec2, region, snapshot, description
|
30
|
+
options = {
|
31
|
+
source_region: region,
|
32
|
+
source_snapshot_id: snapshot.id,
|
33
|
+
description: description
|
34
|
+
}
|
35
|
+
|
36
|
+
response = ec2.client.copy_snapshot options
|
37
|
+
copied_snapshot = ec2.snapshots[response.data[:snapshot_id]]
|
38
|
+
copied_snapshot.tags.set snapshot.tags
|
39
|
+
end
|
40
|
+
|
41
|
+
def purge ec2, purge_after_days, list_only
|
42
|
+
purged = []
|
43
|
+
snapshots = Snapscatter.list ec2
|
44
|
+
snapshots.each do |snapshot|
|
45
|
+
purge_date = snapshot.start_time.to_date + purge_after_days
|
46
|
+
# puts "#{Date.today} > #{purge_date} == #{Date.today > purge_date}"
|
47
|
+
if Date.today > purge_date
|
48
|
+
snapshot.delete if not list_only
|
49
|
+
purged << snapshot
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
return purged
|
54
|
+
end
|
55
|
+
|
56
|
+
# consistency spec should look like the following (all parameters but host, optional)
|
57
|
+
# strategy: mongo, host: 127.0.0.1, port: 27017, usr: admin, pwd: 12345
|
58
|
+
def in_lock consistency_spec
|
59
|
+
locker = Locker.new Snapscatter.parse_spec(consistency_spec)
|
60
|
+
locker.lock
|
61
|
+
begin
|
62
|
+
yield
|
63
|
+
ensure
|
64
|
+
locker.unlock
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
module_function :targets, :list, :copy, :purge, :in_lock, :parse_spec
|
69
|
+
|
70
|
+
end
|
data/snapscatter.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'snapscatter/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "snapscatter"
|
8
|
+
spec.version = Snapscatter::VERSION
|
9
|
+
spec.authors = ["Alex Escalante"]
|
10
|
+
spec.email = ["alex.escalante@gmail.com"]
|
11
|
+
spec.description = %q{Geographically distributed and consistent AWS snapshots}
|
12
|
+
spec.summary = %q{Creates consistent snapshots from EBS volumes and copies them across regions for disaster recovery}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "thor"
|
22
|
+
spec.add_dependency 'aws-sdk', '~> 1.0'
|
23
|
+
spec.add_dependency "mongo"
|
24
|
+
spec.add_dependency "bson_ext"
|
25
|
+
|
26
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
27
|
+
spec.add_development_dependency "rake"
|
28
|
+
spec.add_development_dependency "cucumber"
|
29
|
+
spec.add_development_dependency "aruba"
|
30
|
+
end
|
metadata
ADDED
@@ -0,0 +1,192 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: snapscatter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Alex Escalante
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-10-22 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: thor
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: aws-sdk
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '1.0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '1.0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: mongo
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: bson_ext
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: bundler
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ~>
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '1.3'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '1.3'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: rake
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: cucumber
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: aruba
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ! '>='
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
type: :development
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ! '>='
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
description: Geographically distributed and consistent AWS snapshots
|
143
|
+
email:
|
144
|
+
- alex.escalante@gmail.com
|
145
|
+
executables:
|
146
|
+
- snapscatter
|
147
|
+
extensions: []
|
148
|
+
extra_rdoc_files: []
|
149
|
+
files:
|
150
|
+
- .gitignore
|
151
|
+
- Gemfile
|
152
|
+
- LICENSE.txt
|
153
|
+
- README.md
|
154
|
+
- Rakefile
|
155
|
+
- bin/snapscatter
|
156
|
+
- features/snapscatter.feature
|
157
|
+
- features/support/setup.rb
|
158
|
+
- lib/snapscatter.rb
|
159
|
+
- lib/snapscatter/cli.rb
|
160
|
+
- lib/snapscatter/locker.rb
|
161
|
+
- lib/snapscatter/version.rb
|
162
|
+
- snapscatter.gemspec
|
163
|
+
homepage: ''
|
164
|
+
licenses:
|
165
|
+
- MIT
|
166
|
+
post_install_message:
|
167
|
+
rdoc_options: []
|
168
|
+
require_paths:
|
169
|
+
- lib
|
170
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
171
|
+
none: false
|
172
|
+
requirements:
|
173
|
+
- - ! '>='
|
174
|
+
- !ruby/object:Gem::Version
|
175
|
+
version: '0'
|
176
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
177
|
+
none: false
|
178
|
+
requirements:
|
179
|
+
- - ! '>='
|
180
|
+
- !ruby/object:Gem::Version
|
181
|
+
version: '0'
|
182
|
+
requirements: []
|
183
|
+
rubyforge_project:
|
184
|
+
rubygems_version: 1.8.23
|
185
|
+
signing_key:
|
186
|
+
specification_version: 3
|
187
|
+
summary: Creates consistent snapshots from EBS volumes and copies them across regions
|
188
|
+
for disaster recovery
|
189
|
+
test_files:
|
190
|
+
- features/snapscatter.feature
|
191
|
+
- features/support/setup.rb
|
192
|
+
has_rdoc:
|