ec2-backup 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ff55011774526499c64260a0d43975fde63d1e59
4
+ data.tar.gz: 4215bc6ffd251eab34fae1112bdcabb3abb10153
5
+ SHA512:
6
+ metadata.gz: eabadc478c110797af49223fda452c3bddcd78959f2744fd917dfbca5fe55617cba16a4bc6a7060952d969710c4702fd3d7ccd7846bf8cf26dd372f03b54ef3d
7
+ data.tar.gz: ef47001adb02aab9aaae01e84601be1f806f34c9bdec98e6a6e248f04208ab21fdb61c658442467b39d8125e67c77dde939010cfe40a2b0d16e6d289911f76f8
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ coverage
6
+ InstalledFiles
7
+ lib/bundler/man
8
+ pkg
9
+ rdoc
10
+ spec/reports
11
+ test/tmp
12
+ test/version_tmp
13
+ tmp
14
+
15
+ # YARD artifacts
16
+ .yardoc
17
+ _yardoc
18
+ doc/
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2013 Alfred Moreno
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,33 @@
1
+ ec2-backup
2
+ ==========
3
+
4
+ Automate backups of your infrastructure dynamically via AWS EC2 Tagging and Snapshots
5
+
6
+ Installation
7
+ ==========
8
+
9
+ `gem install ec2-backup`
10
+
11
+ Configuration
12
+ ==========
13
+
14
+ * accounts - An array of accounts you wish to configure backups for
15
+
16
+ Each account has a key for the name of the account followed by the
17
+ `access_key_id` and `secret_access_key` for the account
18
+
19
+ * hourly_snapshots - The amount of hourly snapshots to retain
20
+ * daily_snapshots - The amount of daily snapshots to retain
21
+ * weekly_snapshots - The amount of weekly snapshots to retain
22
+ * monthly_snapshots - The amount of monthly snapshots to retain
23
+
24
+ * tags - The AWS EC2 Tags used for finding instances to be snapshotted.
25
+
26
+ Usage
27
+ ==========
28
+
29
+ Create a `ec2-backup.yml` as shown in the example file in the repository
30
+ and place it in your home directory as `.ec2-backup.yml`
31
+
32
+ When you're ready to start backing up your instances, execute the
33
+ `ec2-backup` command from your terminal.
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'ec2-backup'
4
+
5
+ backup = Ec2Backup.new
6
+ backup.start
@@ -0,0 +1,19 @@
1
+
2
+ accounts:
3
+ production:
4
+ access_key_id: ''
5
+ secret_access_key: ''
6
+ corporate:
7
+ access_key_id: ''
8
+ secret_access_key: ''
9
+ testing:
10
+ access_key_id: ''
11
+ secret_access_key: ''
12
+
13
+ hourly_snapshots: 24
14
+ daily_snapshots: 30
15
+ weekly_snapshots: 4
16
+ monthly_snapshots: 12
17
+
18
+ tags:
19
+ backup: 'true'
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'ec2-backup/ec2-backup'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "ec2-backup"
8
+ gem.version = '1.0.0'
9
+ gem.authors = ["Alfred Moreno"]
10
+ gem.email = ["kryptek@gmail.com"]
11
+ gem.description = %q{Automate backups of your infrastructure dynamically via AWS EC2 Tagging and Snapshots}
12
+ gem.summary = %q{A configurable backup gem for ec2 volumes. See the github page for more}
13
+ gem.homepage = "https://github.com/kryptek/ec2-backup"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_dependency 'active_support'
21
+ gem.add_dependency 'fog'
22
+
23
+ gem.license = 'MIT'
24
+ end
@@ -0,0 +1,5 @@
1
+ __LIB_DIR__ = File.expand_path(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift __LIB_DIR__ unless $LOAD_PATH.include?(__LIB_DIR__)
3
+
4
+ require 'ec2-backup/ec2-backup'
5
+
@@ -0,0 +1,246 @@
1
+ require 'active_support/all'
2
+ require 'fog'
3
+
4
+ class Ec2Backup
5
+ def initialize
6
+
7
+ @settings = YAML.load_file("#{ENV['HOME']}/.ec2-backup.yml")
8
+
9
+ @hourly_snapshots = @settings['hourly_snapshots']
10
+ @daily_snapshots = @settings['daily_snapshots']
11
+ @weekly_snapshots = @settings['weekly_snapshots']
12
+ @monthly_snapshots = @settings['monthly_snapshots']
13
+ @tags = @settings['tags']
14
+
15
+ end
16
+
17
+ ###############################################################################
18
+ # def log
19
+ #
20
+ # Purpose: Neatly logs events to the screen
21
+ # Parameters:
22
+ # text<~String>: The text to log to the screen
23
+ # Returns:
24
+ # <~String> - Full line of text
25
+ ###############################################################################
26
+ def log(text)
27
+ puts "[#{Time.now}] \e[0;30mCaller: #{caller[0][/`(.*)'/,1]} \e[0m| #{text}"
28
+ end
29
+
30
+ ###############################################################################
31
+ # def ec2
32
+ #
33
+ # Purpose: Connects to the Amazon API
34
+ # Parameters: None
35
+ # Returns: Fog::Compute::AWS
36
+ ###############################################################################
37
+ def ec2
38
+ Fog::Compute::AWS.new(aws_access_key_id: @aws_access_key_id, aws_secret_access_key: @aws_secret_access_key)
39
+ end
40
+
41
+ ###############################################################################
42
+ # def volume_snapshots
43
+ #
44
+ # Purpose: Returns all snapshots associated with an EBS volume id
45
+ # Parameters:
46
+ # volume_id<~String>: The volume id of the EBS volume
47
+ # Returns: <~Array>
48
+ # Fog::AWS::Snapshot
49
+ ###############################################################################
50
+ def volume_snapshots(volume_id)
51
+ ec2.snapshots.select { |snapshot| snapshot.volume_id == volume_id }
52
+ end
53
+
54
+ ###############################################################################
55
+ # def find_instances
56
+ #
57
+ # Purpose: Returns all servers with matching key-value tags
58
+ # Parameters:
59
+ # tags<~Hash>: key-value pairs of tags to match against EC2 instances
60
+ # Returns: <~Array>
61
+ # Fog::Compute::AWS::Server
62
+ #
63
+ ###############################################################################
64
+ def find_instances(tags)
65
+ attempts = 0
66
+ begin
67
+ ec2.servers.select { |server| tags.reject { |k,v| server.tags[k] == tags[k] }.empty? }
68
+ rescue Excon::Errors::ServiceUnavailable
69
+ sleep 5
70
+ attempts += 1
71
+ return [] if attempts == 5
72
+ retry
73
+ end
74
+ end
75
+
76
+ ###############################################################################
77
+ # def create_snapshot
78
+ #
79
+ # Purpose: Creates an EBS snapshot
80
+ # Parameters:
81
+ # options<~Hash>
82
+ # volume_id<~String>: The volume id to snapshot
83
+ # description<~String>: The description of the snapshot
84
+ # snapshot_type<~String>: The type of snapshot being created (hourly, etc)
85
+ # tags<~Hash>: Key-value pairs of tags to apply to the snapshot
86
+ # Returns: nil
87
+ ###############################################################################
88
+ def create_snapshot(options)
89
+ snapshot = ec2.snapshots.new
90
+ snapshot.volume_id = options['volume_id']
91
+ snapshot.description = options['description']
92
+
93
+ attempts = 0
94
+
95
+ begin
96
+ snapshot.save
97
+ snapshot.reload
98
+ rescue Fog::Compute::AWS::Error
99
+ sleep 5
100
+ attempts += 1
101
+ if attempts == 5
102
+ log "Error communicating with API; Unable to save volume `#{options['volume_id']}` (Desc: #{options['description']})"
103
+ end
104
+ return unless attempts == 5
105
+ end
106
+
107
+ options['tags'].each do |k,v|
108
+ begin
109
+ ec2.tags.create({resource_id: snapshot.id, key: k, value: v})
110
+ rescue Errno::EINPROGRESS , Errno::EISCONN
111
+ log "API Connection Error"
112
+ sleep 1
113
+ retry
114
+ rescue Fog::Compute::AWS::Error
115
+ log "Failed attaching tag `'#{k}' => #{v}` to #{options['snapshot_type']} snapshot #{snapshot.id}"
116
+ sleep 1
117
+ retry
118
+ end
119
+ end
120
+
121
+ end
122
+
123
+ ###############################################################################
124
+ # def delete_snapshot
125
+ #
126
+ # Purpose: Delete an EBS snapshot from Amazon EC2
127
+ # Parameters:
128
+ # snapshot_id<~String>: The id of the snapshot to be deleted
129
+ # Returns: nil
130
+ ###############################################################################
131
+ def delete_snapshot(snapshot_id)
132
+ log "\e[0;31m:: Deleting snapshot:\e[0m #{snapshot_id}"
133
+
134
+ begin
135
+ ec2.delete_snapshot(snapshot_id)
136
+ sleep 0.2
137
+ rescue Fog::Compute::AWS::NotFound
138
+ log "Failed to delete snapshot: #{snapshot_id}; setting { 'protected' => true }"
139
+ ec2.tags.create({resource_id: snapshot_id, key: 'protected', value: 'true'})
140
+ rescue Fog::Compute::AWS::Error
141
+ log "API Error"
142
+ end
143
+
144
+ end
145
+
146
+ ###############################################################################
147
+ # def too_soon?
148
+ #
149
+ # Purpose: Determines if enough time has passed between taking snapshots
150
+ # Parameters:
151
+ # history<~Array>
152
+ # Fog::Compute::AWS::Snapshot: Volume snapshot
153
+ # snapshot_type<~String>: The type of snapshot (hourly, etc)
154
+ # Returns: Boolean
155
+ ###############################################################################
156
+ def too_soon?(history,snapshot_type)
157
+
158
+ # If the backup history size is zero,
159
+ # the server doesn't have any backups yet.
160
+ return false if history.size == 0
161
+
162
+ elapsed = Time.now - history.last.created_at
163
+
164
+ case snapshot_type
165
+ when 'hourly'
166
+ elapsed < 1.hour
167
+ when 'daily'
168
+ elapsed < 1.day
169
+ when 'weekly'
170
+ elapsed < 1.week
171
+ when 'monthly'
172
+ elapsed < 1.month
173
+ end
174
+
175
+ end
176
+
177
+ ###############################################################################
178
+ # def start
179
+ #
180
+ # Purpose: Start the backup process
181
+ # Parameters: none
182
+ # Returns: nil
183
+ ###############################################################################
184
+ def start
185
+
186
+ @settings['accounts'].each do |account,keys|
187
+
188
+ puts "Account: #{account}"
189
+ @aws_access_key_id = keys['access_key_id']
190
+ @aws_secret_access_key = keys['secret_access_key']
191
+
192
+ # Find all servers with tags matching the supplied Hash
193
+ find_instances(@tags).each do |server|
194
+
195
+ # Begin snapshotting each volume attached to the server
196
+ #
197
+ server.block_device_mapping.each do |block_device|
198
+
199
+ log "\e[0;32m Searching for matching snapshots \e[0m(#{server.id}:#{block_device}).."
200
+ snapshots = volume_snapshots(block_device['volumeId'])
201
+
202
+ # Create each type of backup we'll be using
203
+ #
204
+ %w(hourly daily weekly monthly).each do |snapshot_type|
205
+
206
+ # Build snapshot history for the working volume and return all snapshots
207
+ # matching our particular snapshot type
208
+ history = snapshots.select do |snapshot|
209
+ snapshot.tags['snapshot_type'] == snapshot_type &&
210
+ snapshot.tags['volume_id'] == block_device['volumeId'] &&
211
+ snapshot.tags['protected'] == 'false'
212
+ end
213
+
214
+ history.sort_by! { |snapshot| snapshot.created_at }
215
+
216
+ unless too_soon?(history,snapshot_type) || instance_variable_get("@#{snapshot_type}_snapshots") == 0
217
+
218
+ # Check against threshold limits for backup history and delete as needed
219
+ #
220
+ while history.size >= instance_variable_get("@#{snapshot_type}_snapshots")
221
+ delete_snapshot(history.first.id)
222
+ history.delete(history.first)
223
+ end
224
+
225
+ log "Creating #{snapshot_type} for #{block_device['volumeId']}.."
226
+ create_snapshot({
227
+ 'volume_id' => block_device['volumeId'],
228
+ 'snapshot_type' => snapshot_type,
229
+ 'description' => "Snapshot::#{snapshot_type.capitalize}> Server: #{server.id}",
230
+ 'tags' => {
231
+ 'snapshot_time' => "#{Time.now}",
232
+ 'snapshot_type' => snapshot_type,
233
+ 'instance_id' => server.id,
234
+ 'volume_id' => block_device['volumeId'],
235
+ 'deviceName' => block_device['deviceName'],
236
+ 'protected' => 'false'
237
+ }
238
+ })
239
+ end
240
+ end
241
+ end
242
+ end
243
+ end
244
+
245
+ end
246
+ end
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ec2-backup
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Alfred Moreno
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-11-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: active_support
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: fog
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: Automate backups of your infrastructure dynamically via AWS EC2 Tagging
42
+ and Snapshots
43
+ email:
44
+ - kryptek@gmail.com
45
+ executables:
46
+ - ec2-backup
47
+ extensions: []
48
+ extra_rdoc_files: []
49
+ files:
50
+ - ".gitignore"
51
+ - LICENSE
52
+ - README.md
53
+ - bin/ec2-backup
54
+ - ec2-backup.example.yml
55
+ - ec2-backup.gemspec
56
+ - lib/ec2-backup.rb
57
+ - lib/ec2-backup/ec2-backup.rb
58
+ homepage: https://github.com/kryptek/ec2-backup
59
+ licenses:
60
+ - MIT
61
+ metadata: {}
62
+ post_install_message:
63
+ rdoc_options: []
64
+ require_paths:
65
+ - lib
66
+ required_ruby_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ requirements: []
77
+ rubyforge_project:
78
+ rubygems_version: 2.0.2
79
+ signing_key:
80
+ specification_version: 4
81
+ summary: A configurable backup gem for ec2 volumes. See the github page for more
82
+ test_files: []
83
+ has_rdoc: