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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a9cc2872dec9bd2ab10097d234b65e91dee35213
4
- data.tar.gz: a8284867a4c97126085bc9037f4a320bb5271ee3
3
+ metadata.gz: 3de5840cd79fe2fba85ac073fc88d540aa0358bc
4
+ data.tar.gz: 59be341801bd99d5fa3defe05b2fd6e7ad4d038b
5
5
  SHA512:
6
- metadata.gz: 2871346d59d5ff1e0c6aacdaf7178956e7fffca4c8baaa351adaa179622c4b3b5806089a0892ce270bcfce17dfdeaf665016fe05bb46e4ca108d1260ee4a91cc
7
- data.tar.gz: f01684df05e7a6f2df809cc1ebdb62fc015f25cbad3935ba88c05a67226d7e7c554fbce30b7e2928faa8c8a7cb9d8e32e1e0d9f5b128ed103f7f04acbda75c9c
6
+ metadata.gz: 0e794971af26b3e7596b749ed4e5959e395559c19c26af4e404d2df86022ece03f0ccbc9f74e386da9a2ebb677d1e93409effbb02df40b8de0b72258c6bc566f
7
+ data.tar.gz: 62a864b134b82d5888989869a9b43f9c93a43a96e03ba9c9fbba2986ea30ea954d657a95c0ea42f659dc68143faa8ca3d50bed0dfd2569a6bfe275695c8131a2
@@ -1,29 +1,27 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- fallout (0.0.2)
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.52.0)
12
- aws-sdk-v1 (= 1.52.0)
13
- aws-sdk-v1 (1.52.0)
14
- json (~> 1.4)
15
- nokogiri (>= 1.4.4)
16
- json (1.8.1)
17
- mini_portile (0.6.0)
18
- nokogiri (1.6.3.1)
19
- mini_portile (= 0.6.0)
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 (~> 1.5)
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) 2014 Valentin Vasilyev
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
- rehash, if you're on rbenv.
14
- You must have `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`
15
- environment variables set to authenticate with EC2.
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
- `expires_after` tag. When running the backup, `fallout` will search for
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 man want to delete the old, detached volume vol-72436837 and old volume snapshots
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. Then the process will boot the instance with the new volume and
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
- ### Licence
101
+ ### License
94
102
 
95
- This code is MIT licenced:
103
+ This code is MIT licensed:
96
104
 
97
- Copyright (c) 2014 Valentin Vasilyev
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
 
@@ -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
- f = Fallout::Backup.new(opts)
56
- expired_snapshots = f.delete_expired_snapshots
57
- if expired_snapshots.any?
58
- puts "Deleted snapshots #{expired_snapshots.map(&:id).join(', ')}"
59
- else
60
- puts "No expired snapshots found"
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.get_latest_snapshot_for_volume(r.get_volume)
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, shutting down your instance."
75
- instance = r.shutdown_instance
76
- puts "Shut down instance successfully: #{instance.id}, status: #{instance.status}"
77
- attachment = r.detach_volume(instance)
78
- puts "Detached volume #{attachment.volume.id} from instance #{instance.id} successfully"
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, instance)
88
+ r.attach_volume(new_volume)
82
89
  puts "Attached new volume to instance"
83
- instance = r.start_instance(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.dns_name}"
86
- puts "You man want to delete the old, detached volume #{attachment.volume.id} and old volume snapshots"
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
@@ -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", "~> 1.63"
20
+ spec.add_dependency "aws-sdk", "< 3"
21
21
  spec.add_dependency "trollop"
22
22
 
23
- spec.add_development_dependency "bundler", "~> 1.5"
23
+ spec.add_development_dependency "bundler"
24
24
  spec.add_development_dependency "rake"
25
25
  end
@@ -1,3 +1,6 @@
1
+ require 'aws-sdk'
2
+ require 'fallout/volume_utils'
3
+ require 'fallout/instance_utils'
1
4
  require 'fallout/version'
2
5
  require 'fallout/backup'
3
- require 'fallout/restore'
6
+ require 'fallout/restore'
@@ -1,20 +1,23 @@
1
- require 'aws'
2
1
  module Fallout
3
2
  class Backup
4
- EXPIRES_AFTER_KEY = 'expires_after'.freeze
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
- @expires_after = Date.today + @keep
9
- @ec2 = AWS::EC2.new
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 = @ec2.snapshots.filter('volume-id', @volume_id)
15
+ snapshots = @volume.snapshots
14
16
  snapshots = snapshots.map do |ss|
15
17
  begin
16
- da = Date.parse(ss.tags.to_h[EXPIRES_AFTER_KEY])
17
- if da < Date.today
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
- @volume = @ec2.volumes[@volume_id]
30
- raise "Volume does not exist: #{@volume_id}" if @volume.nil? || !@volume.exists?
31
- desc = "Snapshot for volume #{@volume_id}, will be deleted after #{@expires_after}"
32
- snapshot = @volume.create_snapshot(desc)
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
@@ -0,0 +1,11 @@
1
+ module Fallout
2
+ module InstanceUtils
3
+ def verify_instance_or_raise(instance)
4
+ if instance.nil? || !instance.exists?
5
+ raise "Instance does not exist: #{@instance_id}"
6
+ else
7
+ instance
8
+ end
9
+ end
10
+ end
11
+ end
@@ -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
- @ec2 = AWS::EC2.new
7
- @ec2_client = AWS::EC2::Client.new
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 get_volume
11
- @ec2.volumes[@volume_id]
19
+ def stop_instance
20
+ @instance.stop
21
+ @instance.wait_until_stopped
22
+ @instance
12
23
  end
13
24
 
14
- def shutdown_instance
15
- instance = @ec2.instances[@instance_id]
16
- raise "Instance does not exist: #{@instance_id}" if instance.nil? || !instance.exists?
17
- instance.stop
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(instance, device = '/dev/sda1')
33
- volume = @ec2.volumes[@volume_id]
34
- raise "Volume does not exist: #{volume_id}" if volume.nil? || !volume.exists?
35
- attachment = volume.detach_from(instance, device)
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, instance, device = '/dev/sda1')
43
- volume.attach_to(instance, device)
44
- while volume.status != :in_use
45
- sleep 1
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
- while volume.status != :deleted
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 get_latest_snapshot_for_volume(volume)
59
- snapshots = @ec2.snapshots.filter('volume-id', @volume_id)
60
- raise "No snapshots for volume #{volume.id} found, aborting restore process.\n
61
- Hint: have you created a snapshot for this volume at least once?" unless snapshots.any?
62
- snapshots.max_by{|ss| Date.parse(ss.tags.to_h[EXPIRES_AFTER_KEY]) rescue Date.new}
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, availability_zone = 'us-east-1a', volume_type: 'gp2')
66
- resp = @ec2_client.create_volume(
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 = @ec2.volumes[resp[:volume_id]]
71
- while volume.status != :available
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
@@ -1,3 +1,3 @@
1
1
  module Fallout
2
- VERSION = "0.0.3"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -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.3
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-03-06 00:00:00.000000000 Z
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: '1.63'
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: '1.63'
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: '1.5'
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: '1.5'
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: