ec2-backup 1.0.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.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/LICENSE +20 -0
- data/README.md +33 -0
- data/bin/ec2-backup +6 -0
- data/ec2-backup.example.yml +19 -0
- data/ec2-backup.gemspec +24 -0
- data/lib/ec2-backup.rb +5 -0
- data/lib/ec2-backup/ec2-backup.rb +246 -0
- metadata +83 -0
checksums.yaml
ADDED
@@ -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
|
data/.gitignore
ADDED
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.
|
data/README.md
ADDED
@@ -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.
|
data/bin/ec2-backup
ADDED
@@ -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'
|
data/ec2-backup.gemspec
ADDED
@@ -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
|
data/lib/ec2-backup.rb
ADDED
@@ -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:
|