fallout 0.0.3 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +12 -14
- data/LICENSE +2 -2
- data/README.md +25 -17
- data/bin/fallout +26 -19
- data/fallout.gemspec +2 -2
- data/lib/fallout.rb +4 -1
- data/lib/fallout/backup.rb +14 -13
- data/lib/fallout/instance_utils.rb +11 -0
- data/lib/fallout/restore.rb +42 -48
- data/lib/fallout/version.rb +1 -1
- data/lib/fallout/volume_utils.rb +11 -0
- metadata +12 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3de5840cd79fe2fba85ac073fc88d540aa0358bc
|
4
|
+
data.tar.gz: 59be341801bd99d5fa3defe05b2fd6e7ad4d038b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0e794971af26b3e7596b749ed4e5959e395559c19c26af4e404d2df86022ece03f0ccbc9f74e386da9a2ebb677d1e93409effbb02df40b8de0b72258c6bc566f
|
7
|
+
data.tar.gz: 62a864b134b82d5888989869a9b43f9c93a43a96e03ba9c9fbba2986ea30ea954d657a95c0ea42f659dc68143faa8ca3d50bed0dfd2569a6bfe275695c8131a2
|
data/Gemfile.lock
CHANGED
@@ -1,29 +1,27 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
fallout (0.0
|
5
|
-
aws-sdk
|
4
|
+
fallout (0.1.0)
|
5
|
+
aws-sdk (< 3)
|
6
6
|
trollop
|
7
7
|
|
8
8
|
GEM
|
9
9
|
remote: https://rubygems.org/
|
10
10
|
specs:
|
11
|
-
aws-sdk (1.
|
12
|
-
aws-sdk-
|
13
|
-
aws-sdk-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
rake (10.3.2)
|
21
|
-
trollop (2.0)
|
11
|
+
aws-sdk (2.1.35)
|
12
|
+
aws-sdk-resources (= 2.1.35)
|
13
|
+
aws-sdk-core (2.1.35)
|
14
|
+
jmespath (~> 1.0)
|
15
|
+
aws-sdk-resources (2.1.35)
|
16
|
+
aws-sdk-core (= 2.1.35)
|
17
|
+
jmespath (1.1.3)
|
18
|
+
rake (10.4.2)
|
19
|
+
trollop (2.1.2)
|
22
20
|
|
23
21
|
PLATFORMS
|
24
22
|
ruby
|
25
23
|
|
26
24
|
DEPENDENCIES
|
27
|
-
bundler
|
25
|
+
bundler
|
28
26
|
fallout!
|
29
27
|
rake
|
data/LICENSE
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
The MIT License (MIT)
|
2
2
|
|
3
|
-
Copyright (c)
|
3
|
+
Copyright (c) 2015 Valentin Vasilyev
|
4
4
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
18
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
19
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
20
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
-
SOFTWARE.
|
21
|
+
SOFTWARE.
|
data/README.md
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
# Fallout
|
2
2
|
|
3
3
|
Really easy Amazon EC2 backup/restore solution.
|
4
|
+
Works by making volume snapshots periodically (with expiration)
|
5
|
+
and taking the last snapshot when restoring.
|
4
6
|
|
7
|
+
Fallout is really easy, I suggest you try it.
|
5
8
|
|
6
9
|
## Installation
|
7
10
|
|
@@ -9,10 +12,21 @@ Really easy Amazon EC2 backup/restore solution.
|
|
9
12
|
|
10
13
|
## Usage
|
11
14
|
|
12
|
-
This gem installs `fallout` executable. Don't forget to run `rbenv`
|
13
|
-
|
14
|
-
|
15
|
-
|
15
|
+
This gem installs `fallout` executable. Don't forget to run `rbenv` rehash, if you're on rbenv.
|
16
|
+
|
17
|
+
### Required environment variables:
|
18
|
+
|
19
|
+
`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY` and `AWS_REGION`
|
20
|
+
|
21
|
+
`AWS_REGION` is required to be able to connect with AWS SDK. Specify the region of the EC2 instance you want to back up/restore.
|
22
|
+
|
23
|
+
#### Optional environment variables - used in restore command only:
|
24
|
+
|
25
|
+
`AWS_AVAILABILITY_ZONE` - availability zone where to create the new volume for your instance in.
|
26
|
+
|
27
|
+
If you don't set it, it will default to your AWS_REGION + 'a', e.g. `eu-central-1a`.
|
28
|
+
|
29
|
+
`AWS_ATTACH_VOLUME_AS_DEVICE` - will use `/dev/sda1` as default.
|
16
30
|
|
17
31
|
Fallout supports 2 commands:
|
18
32
|
|
@@ -38,7 +52,7 @@ specified volume._
|
|
38
52
|
#### How does expiration work?
|
39
53
|
|
40
54
|
When creating a snapshot, `fallout` will tag it with a special
|
41
|
-
`
|
55
|
+
`expires_on` tag. When running the backup, `fallout` will search for
|
42
56
|
any expired snapshots and remove them. This way you can have `fallout`
|
43
57
|
running every day, it will keep N most fresh snapshots automatically.
|
44
58
|
|
@@ -62,7 +76,7 @@ Created new volume from the latest snapshot (vol-3942697c)
|
|
62
76
|
Attached new volume to instance
|
63
77
|
Successfully restored and started the instance with the new volume
|
64
78
|
Instance public hostname: ec2-54-165-20-157.compute-1.amazonaws.com
|
65
|
-
You
|
79
|
+
You may want to delete the old, detached volume vol-72436837 and old volume snapshots
|
66
80
|
IMPORTANT: You must update your backup command to use new volume_id: vol-3942697c
|
67
81
|
```
|
68
82
|
|
@@ -71,16 +85,10 @@ IMPORTANT: You must update your backup command to use new volume_id: vol-3942697
|
|
71
85
|
Restoring with fallout requires you to have at least 1 snapshot for the
|
72
86
|
volume. The process will shutdown the instance, detach the root volume,
|
73
87
|
create new volume from the latest snapshot and attach it as `/dev/sda1`
|
74
|
-
device
|
88
|
+
device(can be overridden with AWS_ATTACH_VOLUME_AS_DEVICE env var).
|
89
|
+
Then the process will boot the instance with the new volume and
|
75
90
|
display its public hostname.
|
76
91
|
|
77
|
-
##### IMPORTANT:
|
78
|
-
|
79
|
-
Currently the volume will be attached as `/dev/sda1` device and created
|
80
|
-
in `us-east-1a` zone.
|
81
|
-
If you need this stuff to be configurable, let me know, I'll implement
|
82
|
-
it.
|
83
|
-
|
84
92
|
#### General considerations:
|
85
93
|
|
86
94
|
I created this gem purely for my own purposes, we only have a handful of
|
@@ -90,11 +98,11 @@ I run `fallout backup` daily with cron.
|
|
90
98
|
|
91
99
|
Contributions, tips/advices and pull requests are welcome.
|
92
100
|
|
93
|
-
###
|
101
|
+
### License
|
94
102
|
|
95
|
-
This code is MIT
|
103
|
+
This code is MIT licensed:
|
96
104
|
|
97
|
-
Copyright (c)
|
105
|
+
Copyright (c) 2015 Valentin Vasilyev
|
98
106
|
|
99
107
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
100
108
|
|
data/bin/fallout
CHANGED
@@ -52,15 +52,19 @@ when 'backup'
|
|
52
52
|
if(opts[:volume].nil? || opts[:keep].nil?)
|
53
53
|
puts '-v and -k are required for backup'
|
54
54
|
else
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
55
|
+
begin
|
56
|
+
f = Fallout::Backup.new(opts)
|
57
|
+
expired_snapshots = f.delete_expired_snapshots
|
58
|
+
if expired_snapshots.any?
|
59
|
+
puts "Deleted snapshots #{expired_snapshots.map(&:id).join(', ')}"
|
60
|
+
else
|
61
|
+
puts "No expired snapshots found"
|
62
|
+
end
|
63
|
+
new_snapshot, expires_on = f.run
|
64
|
+
puts "Created new snapshot #{new_snapshot.id}, expires on #{expires_on}"
|
65
|
+
rescue Exception => e
|
66
|
+
puts e.message
|
61
67
|
end
|
62
|
-
new_snapshot, expires_after = f.run
|
63
|
-
puts "Created new snapshot #{new_snapshot.id}, expires after #{expires_after}"
|
64
68
|
end
|
65
69
|
when 'restore'
|
66
70
|
if(opts[:instance].nil? || opts[:volume].nil?)
|
@@ -68,23 +72,26 @@ when 'restore'
|
|
68
72
|
else
|
69
73
|
begin
|
70
74
|
r = Fallout::Restore.new(opts)
|
71
|
-
snapshot = r.
|
75
|
+
snapshot = r.get_latest_snapshot
|
76
|
+
unless snapshot
|
77
|
+
raise "You do not have any snapshots to restore your instance from.\n Did you run fallout backup at least once?"
|
78
|
+
end
|
72
79
|
puts "Initiating omega protocol, retina scan required."
|
73
80
|
sleep 3
|
74
|
-
puts "Ok, let's do it without retina,
|
75
|
-
instance = r.
|
76
|
-
puts "
|
77
|
-
|
78
|
-
puts "Detached volume #{
|
81
|
+
puts "Ok, let's do it without retina, stopping your instance."
|
82
|
+
instance = r.stop_instance
|
83
|
+
puts "Stopped instance successfully: #{instance.id}, status: #{instance.state.name}"
|
84
|
+
volume_attachment = r.detach_volume
|
85
|
+
puts "Detached volume #{volume_attachment.volume_id} from instance #{volume_attachment.instance_id} successfully"
|
79
86
|
new_volume = r.create_volume(snapshot)
|
80
87
|
puts "Created new volume from the latest snapshot (#{new_volume.id})"
|
81
|
-
r.attach_volume(new_volume
|
88
|
+
r.attach_volume(new_volume)
|
82
89
|
puts "Attached new volume to instance"
|
83
|
-
instance = r.start_instance
|
90
|
+
instance = r.start_instance
|
84
91
|
puts "Successfully restored and started the instance with the new volume"
|
85
|
-
puts "Instance public hostname: #{instance.
|
86
|
-
puts "You
|
87
|
-
puts "IMPORTANT: You must update your backup command to use new volume_id: #{new_volume.id}"
|
92
|
+
puts "Instance public hostname: #{Aws::EC2::Instance.new(instance.id).public_dns_name}"
|
93
|
+
puts "You may want to delete the old, detached volume #{volume_attachment.volume_id} and old volume snapshots"
|
94
|
+
puts "IMPORTANT: You must update your backup command to use the new volume_id: #{new_volume.id}"
|
88
95
|
rescue Exception => e
|
89
96
|
puts e.message
|
90
97
|
end
|
data/fallout.gemspec
CHANGED
@@ -17,9 +17,9 @@ Gem::Specification.new do |spec|
|
|
17
17
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
18
|
spec.require_paths = ["lib"]
|
19
19
|
|
20
|
-
spec.add_dependency "aws-sdk", "
|
20
|
+
spec.add_dependency "aws-sdk", "< 3"
|
21
21
|
spec.add_dependency "trollop"
|
22
22
|
|
23
|
-
spec.add_development_dependency "bundler"
|
23
|
+
spec.add_development_dependency "bundler"
|
24
24
|
spec.add_development_dependency "rake"
|
25
25
|
end
|
data/lib/fallout.rb
CHANGED
data/lib/fallout/backup.rb
CHANGED
@@ -1,20 +1,23 @@
|
|
1
|
-
require 'aws'
|
2
1
|
module Fallout
|
3
2
|
class Backup
|
4
|
-
|
3
|
+
EXPIRES_ON_KEY = 'expires_on'.freeze
|
4
|
+
|
5
|
+
include VolumeUtils
|
6
|
+
|
5
7
|
def initialize(options)
|
6
8
|
@volume_id = options[:volume]
|
7
9
|
@keep = options[:keep].to_i
|
8
|
-
@
|
9
|
-
@
|
10
|
+
@expires_on = Date.today + @keep
|
11
|
+
@volume = verify_volume_or_raise(Aws::EC2::Volume.new(@volume_id))
|
10
12
|
end
|
11
13
|
|
12
14
|
def delete_expired_snapshots
|
13
|
-
snapshots = @
|
15
|
+
snapshots = @volume.snapshots
|
14
16
|
snapshots = snapshots.map do |ss|
|
15
17
|
begin
|
16
|
-
|
17
|
-
|
18
|
+
tags_hash = Hash[ss.tags.map{|t| [t.key, t.value]}]
|
19
|
+
expires_on = Date.parse(tags_hash[EXPIRES_ON_KEY])
|
20
|
+
if expires_on < Date.today
|
18
21
|
ss.delete
|
19
22
|
ss
|
20
23
|
end
|
@@ -26,12 +29,10 @@ module Fallout
|
|
26
29
|
end
|
27
30
|
|
28
31
|
def run
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
snapshot
|
33
|
-
snapshot.add_tag(EXPIRES_AFTER_KEY, value: @expires_after.to_s)
|
34
|
-
[snapshot, @expires_after]
|
32
|
+
description = "Snapshot for volume #{@volume_id}, will be deleted on #{@expires_on}"
|
33
|
+
snapshot = @volume.create_snapshot(description: description)
|
34
|
+
snapshot.create_tags(tags: [{key: EXPIRES_ON_KEY, value: @expires_on.to_s}])
|
35
|
+
[snapshot, @expires_on]
|
35
36
|
end
|
36
37
|
end
|
37
38
|
end
|
data/lib/fallout/restore.rb
CHANGED
@@ -1,76 +1,70 @@
|
|
1
1
|
module Fallout
|
2
2
|
class Restore
|
3
|
+
include VolumeUtils, InstanceUtils
|
4
|
+
attr_reader :volume
|
3
5
|
def initialize(options)
|
4
6
|
@instance_id = options[:instance]
|
7
|
+
@instance = verify_instance_or_raise(Aws::EC2::Instance.new(@instance_id))
|
5
8
|
@volume_id = options[:volume]
|
6
|
-
@
|
7
|
-
@
|
9
|
+
@volume = verify_volume_or_raise(Aws::EC2::Volume.new(@volume_id))
|
10
|
+
@ec2 = Aws::EC2::Client.new
|
11
|
+
if ENV['AWS_REGION']
|
12
|
+
@availability_zone = ENV['AWS_AVAILABILITY_ZONE'] || "#{ENV['AWS_REGION']}a"
|
13
|
+
else
|
14
|
+
raise 'AWS_REGION is a required env variable. Please see the list of available regions at: http://goo.gl/0b2VOE'
|
15
|
+
end
|
16
|
+
@attach_as_device = ENV['AWS_ATTACH_VOLUME_AS_DEVICE'] || '/dev/sda1'
|
8
17
|
end
|
9
18
|
|
10
|
-
def
|
11
|
-
@
|
19
|
+
def stop_instance
|
20
|
+
@instance.stop
|
21
|
+
@instance.wait_until_stopped
|
22
|
+
@instance
|
12
23
|
end
|
13
24
|
|
14
|
-
def
|
15
|
-
instance
|
16
|
-
|
17
|
-
instance
|
18
|
-
while instance.status != :stopped
|
19
|
-
sleep 1
|
20
|
-
end
|
21
|
-
instance
|
22
|
-
end
|
23
|
-
|
24
|
-
def start_instance(instance)
|
25
|
-
instance.start
|
26
|
-
while instance.status != :running
|
27
|
-
sleep 1
|
28
|
-
end
|
29
|
-
instance
|
25
|
+
def start_instance
|
26
|
+
@instance.start
|
27
|
+
@instance.wait_until_running
|
28
|
+
@instance
|
30
29
|
end
|
31
30
|
|
32
|
-
def detach_volume
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
while volume.status != :available
|
37
|
-
sleep 1
|
38
|
-
end
|
39
|
-
attachment
|
31
|
+
def detach_volume
|
32
|
+
volume_attachment = volume.detach_from_instance(instance_id: @instance_id, device: @attach_as_device)
|
33
|
+
@ec2.wait_until(:volume_available, volume_ids: [volume.id])
|
34
|
+
volume_attachment
|
40
35
|
end
|
41
36
|
|
42
|
-
def attach_volume(volume
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
end
|
37
|
+
def attach_volume(volume)
|
38
|
+
attach_options = {instance_id: @instance_id, device: @attach_as_device}
|
39
|
+
volume.attach_to_instance(attach_options)
|
40
|
+
@ec2.wait_until(:volume_in_use, volume_ids: [volume.id])
|
47
41
|
volume
|
48
42
|
end
|
49
43
|
|
50
44
|
def delete_volume(volume)
|
51
45
|
volume.delete
|
52
|
-
|
53
|
-
sleep 1
|
54
|
-
end
|
46
|
+
@ec2.wait_until(:volume_deleted, volume_ids: [volume.id])
|
55
47
|
volume
|
56
48
|
end
|
57
49
|
|
58
|
-
def
|
59
|
-
snapshots = @
|
60
|
-
|
61
|
-
|
62
|
-
|
50
|
+
def get_latest_snapshot
|
51
|
+
snapshots = @volume.snapshots
|
52
|
+
unless snapshots.any?
|
53
|
+
raise "No snapshots for volume #{@volume.id} found, aborting restore process.\nHint: have you created a snapshot for this volume at least once?"
|
54
|
+
end
|
55
|
+
snapshots.max_by{|ss|
|
56
|
+
tags_hash = Hash[ss.tags.map{|t| [t.key, t.value]}]
|
57
|
+
Date.parse(tags_hash[EXPIRES_ON_KEY]) rescue Date.new
|
58
|
+
}
|
63
59
|
end
|
64
60
|
|
65
|
-
def create_volume(snapshot,
|
66
|
-
resp = @
|
61
|
+
def create_volume(snapshot, volume_type: 'gp2')
|
62
|
+
resp = @ec2.create_volume(
|
67
63
|
snapshot_id: snapshot.id,
|
68
|
-
availability_zone: availability_zone,
|
64
|
+
availability_zone: @availability_zone,
|
69
65
|
volume_type: volume_type)
|
70
|
-
volume =
|
71
|
-
|
72
|
-
sleep 1
|
73
|
-
end
|
66
|
+
volume = Aws::EC2::Volume.new(resp.volume_id)
|
67
|
+
@ec2.wait_until(:volume_available, volume_ids: [resp.volume_id])
|
74
68
|
volume
|
75
69
|
end
|
76
70
|
end
|
data/lib/fallout/version.rb
CHANGED
@@ -0,0 +1,11 @@
|
|
1
|
+
module Fallout
|
2
|
+
module VolumeUtils
|
3
|
+
def verify_volume_or_raise(volume)
|
4
|
+
unless %w(in-use available).include?(volume.state)
|
5
|
+
raise "Volume #{volume.id} does not exist or not in a valid state. Valid states are 'in-use' or 'available'"
|
6
|
+
else
|
7
|
+
volume
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
metadata
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fallout
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Valentin Vasilyev
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-11-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aws-sdk
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - "<"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '3'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - "<"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '3'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: trollop
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -42,16 +42,16 @@ dependencies:
|
|
42
42
|
name: bundler
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - "
|
45
|
+
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
47
|
+
version: '0'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- - "
|
52
|
+
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
54
|
+
version: '0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rake
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -84,8 +84,10 @@ files:
|
|
84
84
|
- fallout.gemspec
|
85
85
|
- lib/fallout.rb
|
86
86
|
- lib/fallout/backup.rb
|
87
|
+
- lib/fallout/instance_utils.rb
|
87
88
|
- lib/fallout/restore.rb
|
88
89
|
- lib/fallout/version.rb
|
90
|
+
- lib/fallout/volume_utils.rb
|
89
91
|
homepage: https://github.com/Valve/fallout
|
90
92
|
licenses:
|
91
93
|
- MIT
|
@@ -111,4 +113,3 @@ signing_key:
|
|
111
113
|
specification_version: 4
|
112
114
|
summary: A simple ruby script that can backup and restore Amazon EC2 instances
|
113
115
|
test_files: []
|
114
|
-
has_rdoc:
|