mongolly 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
@@ -0,0 +1,24 @@
1
+ Copyright (c) 2012 Michael Saffitz
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+ * Redistributions of source code must retain the above copyright
7
+ notice, this list of conditions and the following disclaimer.
8
+ * Redistributions in binary form must reproduce the above copyright
9
+ notice, this list of conditions and the following disclaimer in the
10
+ documentation and/or other materials provided with the distribution.
11
+ * Neither the name of the <organization> nor the
12
+ names of its contributors may be used to endorse or promote products
13
+ derived from this software without specific prior written permission.
14
+
15
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18
+ DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
19
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,36 @@
1
+ # Mongolly
2
+
3
+ **Easy backups for EBS-based MongoDB Databases**
4
+
5
+ More details coming soon.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'mongolly'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install mongolly
20
+
21
+ ## Usage
22
+
23
+ `mongolly help usage`
24
+
25
+ ## Contributing
26
+
27
+ 1. Fork it
28
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
29
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
30
+ 4. Push to the branch (`git push origin my-new-feature`)
31
+ 5. Create new Pull Request
32
+
33
+ ### Copyright
34
+
35
+ Copyright (c) 2012 Michael Saffitz. See LICENSE.txt for
36
+ further details.
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,120 @@
1
+ #!/usr/bin/env ruby
2
+ require 'thor'
3
+ require 'yaml'
4
+ require 'time'
5
+ require 'mongo'
6
+ require './lib/mongolly'
7
+
8
+ module Mongolly
9
+ class Runner < Thor
10
+
11
+ def initialize(*args)
12
+ super
13
+ @config = read_config
14
+ @db_name, @db_config = database_config
15
+ exit unless valid_config?
16
+ end
17
+
18
+ CONFIG_PATH = File.expand_path '~/.mongolly'
19
+
20
+ desc "snapshot", "takes an EBS snapshot of the given volumes"
21
+ method_option :database, aliases: '-d'
22
+ def snapshot
23
+ db = Mongo::Connection.new( @db_config["host"], @db_config["port"] )
24
+ db['admin'].authenticate( @db_config["user"], @db_config["pass"] )
25
+
26
+ SnapshotManager.take_snapshots( db, @config["aws_access_key_id"], @config["aws_secret_asccess_key"], @db_config["volumes"] )
27
+ end
28
+
29
+ desc "clean", "removes snapshots older than the given data"
30
+ method_option :maximum_age, aliases: '-a', required: true
31
+ method_option :database, aliases: '-d'
32
+ def clean
33
+ max_age = Time.parse(options[:maximum_age])
34
+
35
+ puts " ** Cleaning snapshots older than #{max_age}"
36
+
37
+ ec2 = AWS::EC2.new(access_key_id: @config["aws_access_key_id"], secret_access_key: @config["aws_secret_asccess_key"])
38
+ ec2.snapshots.with_owner(:self).each do |snapshot|
39
+ unless snapshot.tags[:created_at].nil? || snapshot.tags[:backup_key].nil?
40
+ if Time.parse(snapshot.tags[:created_at]) < max_age
41
+ puts " ** Deleting #{snapshot.id} created on #{snapshot.tags[:created_at]} with key #{snapshot.tags[:backup_key]}"
42
+ snapshot.delete
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ private
49
+ def seed_config
50
+ return true if File.exists? CONFIG_PATH
51
+
52
+ empty_config = {
53
+ databases: {
54
+ dbname: {
55
+ host: nil,
56
+ port: nil,
57
+ user: nil,
58
+ pass: nil,
59
+ volumes: [nil]
60
+ }
61
+ },
62
+ aws_access_key_id: nil,
63
+ aws_secret_asccess_key: nil,
64
+ }
65
+
66
+ File.open( CONFIG_PATH, "w" ) do |f|
67
+ f.write( empty_config.to_yaml )
68
+ end
69
+
70
+ puts " ** An empty configuration file has been written to #{CONFIG_PATH}."
71
+ puts " ** you must now edit this configuration file with your AWS Credentials,"
72
+ puts " ** MongoDB Connection Details, and the array of volume IDs that you wish"
73
+ puts " ** to snapshot"
74
+
75
+ return false
76
+ end
77
+
78
+ def read_config
79
+ return unless seed_config
80
+ begin
81
+ return YAML::load( File.read( CONFIG_PATH ) )
82
+ rescue e
83
+ puts " ** Unable to read config at #{CONFIG_PATH}"
84
+ raise e
85
+ end
86
+ end
87
+
88
+ def database_config
89
+ if options[:database].to_s.strip.empty? && @config["databases"].size > 1
90
+ raise ArgumentError.new("Database name not provided and more than database specified in the config file")
91
+ elsif ! @config["databases"].keys.include? options[:database].to_s.strip
92
+ raise ArgumentError.new("Database #{options[:database]} not defined in config")
93
+ end
94
+
95
+ db_name = options[:database].to_s.strip.empty? ? @config["databases"].keys.first : options[:database]
96
+ db_config = @config["databases"][db_name]
97
+
98
+ %w(host port user pass region).each do |arg|
99
+ if db_config[arg].to_s.strip.empty?
100
+ raise ArgumentError.new( "#{arg} for database #{db_name} cannot be empty" )
101
+ end
102
+ end
103
+ if db_config["volumes"].empty? or db_config["volumes"].map { |v| v.to_s.strip.empty? }.include?(true)
104
+ raise ArgumentError.new("volumes cannot be empty or include an empty string for database #{db_name}")
105
+ end
106
+
107
+ return db_name, db_config
108
+ end
109
+
110
+ def valid_config?
111
+ %w(aws_access_key_id aws_secret_asccess_key).each do |arg|
112
+ raise ArgumentError.new("#{arg} cannot be empty") if @config[arg].to_s.strip.empty?
113
+ end
114
+ return true
115
+ end
116
+
117
+ end
118
+ end
119
+
120
+ Mongolly::Runner.start
@@ -0,0 +1,6 @@
1
+ require "mongolly/version"
2
+ require "mongolly/snapshot_manager"
3
+
4
+ module Mongolly
5
+ # Your code goes here...
6
+ end
@@ -0,0 +1,35 @@
1
+ require 'aws-sdk'
2
+
3
+ module Mongolly
4
+ class SnapshotManager
5
+
6
+ def self.take_snapshots(db, aws_key_id, aws_secret_key, volume_ids)
7
+ unless db.locked?
8
+ puts " ** Locking Database"
9
+ db.lock!
10
+ end
11
+
12
+ begin
13
+ ec2 = AWS::EC2.new(access_key_id: aws_key_id, secret_access_key: aws_secret_key)
14
+ backup_key = (0...8).map{65.+(rand(25)).chr}.join
15
+
16
+ puts " ** Starting Snapshot with key #{backup_key}"
17
+
18
+ volume_ids.map{ |v| v.to_s.strip }.each do |volume_id|
19
+ puts " ** Taking snapshot of volume #{volume_id}"
20
+ volume = ec2.volumes[volume_id]
21
+ raise RuntimeError.new("Volume #{volume_id} does not exist") unless volume.exists?
22
+
23
+ snapshot = volume.create_snapshot("#{backup_key} #{Time.now} mongo backup")
24
+ snapshot.add_tag('created_at', value: Time.now)
25
+ snapshot.add_tag('backup_key', value: backup_key)
26
+ end
27
+ ensure
28
+ if db.locked?
29
+ puts " ** Unlocking Database"
30
+ db.unlock!
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,3 @@
1
+ module Mongolly
2
+ VERSION = "0.0.2"
3
+ end
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'mongolly/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "mongolly"
8
+ gem.version = Mongolly::VERSION
9
+ gem.authors = ["Michael Saffitz"]
10
+ gem.email = ["m@saffitz.com"]
11
+ gem.description = %q{Easy backups for EBS-based MongoDB Databases}
12
+ gem.summary = %q{Easy backups for EBS-based MongoDB Databases}
13
+ gem.homepage = "http://www.github.com/msaffitz/mongolly"
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("thor", ["~> 0.15.4"])
21
+ gem.add_dependency("mongo", ["~> 1.6.4"])
22
+ gem.add_dependency("bson_ext", ["~> 1.6.4"])
23
+ gem.add_dependency("aws-sdk", ["~> 1.5.8"])
24
+
25
+ end
metadata ADDED
@@ -0,0 +1,126 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mongolly
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Michael Saffitz
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-07-27 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.15.4
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.15.4
30
+ - !ruby/object:Gem::Dependency
31
+ name: mongo
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 1.6.4
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.6.4
46
+ - !ruby/object:Gem::Dependency
47
+ name: bson_ext
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 1.6.4
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: 1.6.4
62
+ - !ruby/object:Gem::Dependency
63
+ name: aws-sdk
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: 1.5.8
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: 1.5.8
78
+ description: Easy backups for EBS-based MongoDB Databases
79
+ email:
80
+ - m@saffitz.com
81
+ executables:
82
+ - mongolly
83
+ extensions: []
84
+ extra_rdoc_files: []
85
+ files:
86
+ - .gitignore
87
+ - Gemfile
88
+ - LICENSE.txt
89
+ - README.md
90
+ - Rakefile
91
+ - bin/mongolly
92
+ - lib/mongolly.rb
93
+ - lib/mongolly/snapshot_manager.rb
94
+ - lib/mongolly/version.rb
95
+ - mongolly.gemspec
96
+ homepage: http://www.github.com/msaffitz/mongolly
97
+ licenses: []
98
+ post_install_message:
99
+ rdoc_options: []
100
+ require_paths:
101
+ - lib
102
+ required_ruby_version: !ruby/object:Gem::Requirement
103
+ none: false
104
+ requirements:
105
+ - - ! '>='
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ segments:
109
+ - 0
110
+ hash: -4173052183562029100
111
+ required_rubygems_version: !ruby/object:Gem::Requirement
112
+ none: false
113
+ requirements:
114
+ - - ! '>='
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ segments:
118
+ - 0
119
+ hash: -4173052183562029100
120
+ requirements: []
121
+ rubyforge_project:
122
+ rubygems_version: 1.8.24
123
+ signing_key:
124
+ specification_version: 3
125
+ summary: Easy backups for EBS-based MongoDB Databases
126
+ test_files: []