vgh 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,28 @@
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
+ html/
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
19
+
20
+ # Mac Stuff
21
+ .DS_Store
22
+ .DS_Store?
23
+ ._*
24
+ .Spotlight-V100
25
+ .Trashes
26
+ Icon?
27
+ ehthumbs.db
28
+ Thumbs.db
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 1.9.2
5
+ - 1.8.7
6
+ - ree
7
+ script: "rvmsudo bundle exec rake spec"
data/.yardopts ADDED
@@ -0,0 +1,2 @@
1
+ --no-private --protected --readme=README.rdoc - LICENSE
2
+
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Vlad
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,121 @@
1
+ = VGH Scripts {<img src="https://secure.travis-ci.org/vladgh/vgh.png" alt="Build Status" />}[http://travis-ci.org/vladgh/vgh]
2
+
3
+ A collection of custom scripts used on VladGh.com
4
+
5
+
6
+ == Instalation
7
+
8
+ Some system wide dependencies should be installed first:
9
+
10
+ apt-get install libxslt-dev libxml2-dev
11
+
12
+ Add this line to your application's Gemfile:
13
+
14
+ gem 'vgh'
15
+
16
+ And then execute:
17
+
18
+ $ bundle
19
+
20
+ Or install it yourself as:
21
+
22
+ $ gem install vgh
23
+
24
+
25
+ == Configuration
26
+
27
+ This gem looks for the following configuration files inside "+/etc/vgh+" or the
28
+ +--confdir+ specified in {VGH::CLI CLI Class}:
29
+
30
+ [+config.yml+]
31
+ The main configuration file for this gem.
32
+ <em>At this moment nothing needs to be configured here.</em>
33
+
34
+ [+aws.config.yml+]
35
+ The AWS specific configuration.
36
+ Available values:
37
+ - +:access_key_id+ - [String] Your AWS credentials
38
+ - +:secret_access_key+ - [String] Your AWS credentials
39
+ - +:region+ - optional [String] Your current instance's region.
40
+ Ex: +'us-west-1'+
41
+
42
+ [+#APP_NAME.config.yml+]
43
+ The application specific configuration (see below a list of available apps and
44
+ values).
45
+
46
+ This gem checks if the files exists and if they are in a correct YAML format.
47
+
48
+ All files should have the following format (where the keys are symbols):
49
+ # Comment
50
+ :key: 'value'
51
+ :string: 'string value'
52
+ :integer: 123
53
+ :boolean: true/false
54
+ :array:
55
+ - '1st'
56
+ - '2nd'
57
+ :hash:
58
+ -
59
+ :sub_key: 'sub value'
60
+
61
+
62
+ == Command line options
63
+
64
+ The following command line options are available:
65
+
66
+ [+ApplicationName+] Specify the application you want to run. For a
67
+ list of available apps see
68
+ {file:README.rdoc#Applications Applications} below.
69
+ [+-c+ <tt>--confdir '~/.vgh'</tt>] Specify the desired directory in which
70
+ the configuration files reside.
71
+ [+-v+ <tt>--[no-]verbose</tt>] Specify whether to display messages on the
72
+ screen or not.
73
+ [+-l+ <tt>--[no-]logging</tt>] Specify whether to log messages or not.
74
+ [+-h+ +--help+] Displays this list.
75
+ [+-V+ +--version+] Displays the version number.
76
+
77
+
78
+ == Applications
79
+
80
+ === EC2-Backup
81
+
82
+ [/bin/vgh ec2-backup]
83
+
84
+ ==== Description
85
+ This app creates a snapshot of the attached EBS volumes. It also deletes
86
+ snapshots older than the expiration period specified in the configuration
87
+ file. It is intended to be used as a cron job on an AWS instance running
88
+ Ubuntu.
89
+ If a MySQL server is running, and the script has access to the administrator
90
+ credentials, then it closes all open tables and locks all tables for all
91
+ databases with a global read lock. Everything is unlocked at the end of the
92
+ script even if errors occur during the snapshot process. <em>You need to
93
+ provide the necessary mysql credentials in the +ec2-backup.config.yml+ file.
94
+ </em>
95
+ If Logical Volumes are found on the system then they are suspended for the
96
+ duration on the snapshot process.
97
+ Suspending a volume becomes useful if you want for example a consistent
98
+ EC2 snapshot of it. Any I/O that has already been mapped by the device but
99
+ has not yet completed will be flushed. Any further I/O to that device will
100
+ be postponed for as long as the device is suspended.
101
+
102
+
103
+ ==== EC2-Backup Configuration
104
+ +ec2-backup.config.yml+
105
+ - +:expiration+ -- [Integer] Number of days to keep the snapshots.
106
+ # Ex:
107
+ :expiration: 7
108
+
109
+
110
+ ==== Intended usage
111
+ # Cron Job:
112
+ 0 */6 * * * vgh ec2-backup -l
113
+
114
+
115
+ == Contributing
116
+
117
+ 1. Fork it
118
+ 2. Create your feature branch (<tt>git checkout -b my-new-feature</tt>)
119
+ 3. Commit your changes (<tt>git commit -am 'Add some feature'</tt>)
120
+ 4. Push to the branch (<tt>git push origin my-new-feature</tt>)
121
+ 5. Create new Pull Request (http://help.github.com/articles/using-pull-requests)
data/Rakefile ADDED
@@ -0,0 +1,25 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+ require "yard"
4
+
5
+ $LOAD_PATH << File.join(File.dirname(__FILE__), 'tasks')
6
+ Dir['tasks/**/*.rake'].each { |task| load task }
7
+
8
+ RSpec::Core::RakeTask.new
9
+
10
+ task :default do
11
+ `rake -T`
12
+ end
13
+
14
+ task :test => :spec
15
+
16
+ desc 'Look for the @todo tag'
17
+ task :todo do |task|
18
+ Rake::Task['yard:doc_info'].execute
19
+ YARD::Registry.load!.all.each do |o|
20
+ todo = o.tag(:todo)
21
+ puts "#{todo.object} - #{todo.text}" if todo
22
+ end
23
+ end
24
+
25
+
data/bin/vgh ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'vgh'
4
+
5
+ include VGH
6
+ run
7
+
@@ -0,0 +1,63 @@
1
+ module VGH
2
+ module APPS
3
+
4
+ # == Description:
5
+ #
6
+ # See {file:README.rdoc#EC2-Backup EC2-Backup Section} in the README
7
+ # file.
8
+ #
9
+ # == Usage:
10
+ # backup = APPS::EC2_Backup.new
11
+ # backup.run
12
+ #
13
+ class EC2_Backup
14
+
15
+ # Loads the necessary classes
16
+ def initialize
17
+ $cfg = parse_config.app_config
18
+ @mysql = System::MySQL.new
19
+ @lv = System::LV.new
20
+ @volume = Extended_AWS::Extended_EC2::Volume.new
21
+ @snapshot = Extended_AWS::Extended_EC2::Snapshot.new
22
+ end
23
+
24
+ # Runs the ec2-backup app logic
25
+ def run
26
+ lock
27
+
28
+ begin
29
+ @volume.list.map {|id, info|
30
+ @snapshot.create(id, info[:tag])
31
+ }
32
+ rescue
33
+ message.fatal "Something went wrong in the snapshot creation process!"
34
+ end
35
+
36
+ unlock
37
+
38
+ @snapshot.purge
39
+ end
40
+
41
+ # Flushes MySQL tables and suspends Logical Volumes
42
+ def lock
43
+ @mysql.flush
44
+ @lv.suspend
45
+ end
46
+
47
+ # Resumes MySQL and Logical Volumes
48
+ def unlock
49
+ @mysql.unlock
50
+ @lv.resume
51
+ end
52
+
53
+ end # class EC2_Backup
54
+
55
+ end # module APPS
56
+ end # module VGH
57
+
58
+ require 'vgh/output'
59
+ require 'vgh/system/lvm'
60
+ require 'vgh/system/mysql'
61
+ require 'vgh/extended_aws/extended_ec2/volume'
62
+ require 'vgh/extended_aws/extended_ec2/snapshot'
63
+
data/lib/vgh/apps.rb ADDED
@@ -0,0 +1,22 @@
1
+ module VGH
2
+
3
+ # See {file:README.rdoc#Applications Applications Section} in the README
4
+ # file.
5
+ #
6
+ #
7
+ # == Usage:
8
+ # APPS.list
9
+ #
10
+ module APPS
11
+
12
+ # Lists the available applications
13
+ # @return [Array]
14
+ def self.list
15
+ @apps ||= [
16
+ 'ec2-backup'
17
+ ]
18
+ end
19
+
20
+ end # module APPS
21
+
22
+ end # module VGH
data/lib/vgh/cli.rb ADDED
@@ -0,0 +1,185 @@
1
+ require 'optparse'
2
+
3
+ module VGH
4
+
5
+ # Returns a global collection of options.
6
+ # @return [Hash]
7
+ def cli
8
+ $cli ||= CLI.new.options
9
+ end
10
+
11
+ # Returns the app name specified in the command line
12
+ # @return [String]
13
+ def app
14
+ $app ||= cli[:app]
15
+ end
16
+
17
+ # Returns the configuration specified in the command line
18
+ # @return [String]
19
+ def cli_confdir
20
+ $cli_confdir ||= cli[:confdir]
21
+ end
22
+
23
+ # Returns verbosity
24
+ # @return [Boolean]
25
+ def verbose?
26
+ $verbose = cli[:verbose]
27
+ end
28
+
29
+ # Returns logging
30
+ # @return [Boolean]
31
+ def logging?
32
+ $logging = cli[:logging]
33
+ end
34
+
35
+ # == Description:
36
+ #
37
+ # Returns a structure describing the command line options and arguments.
38
+ # See {file:README.rdoc#Command+line+options Command line options} in
39
+ # the README file.
40
+ #
41
+ #
42
+ # == Usage:
43
+ # cli = CLI.new.options
44
+ # puts cli[:app]
45
+ # puts cli[:verbose]
46
+ # puts cli[:logging]
47
+ # puts cli[:confdir]
48
+ #
49
+ class CLI
50
+
51
+ # Command line options
52
+ attr_reader :options
53
+
54
+ # We set default values here.
55
+ def defaults
56
+ @options[:app] = false
57
+ @options[:verbose] = false
58
+ @options[:logging] = false
59
+ end
60
+
61
+ # Collect options
62
+ # @param [Array] apps A list of available apps
63
+ # @param [String] app The name of the app
64
+ # @param [Array] args The command line arguments
65
+ def initialize(apps = APPS.list, app = ARGV[0], args = ARGV)
66
+ @options = {}
67
+ @optparse = OptionParser.new()
68
+ defaults
69
+ banner(apps)
70
+ confdir
71
+ verbose
72
+ logging
73
+ help
74
+ version
75
+ validate(args)
76
+ check_app(apps, app)
77
+ end
78
+
79
+ # Checks if the provided app is in the available list
80
+ def check_app(apps, app)
81
+ if apps.include? app
82
+ @options[:app] = app
83
+ else
84
+ puts "ERROR: You need to specify one of the following apps: \
85
+ #{apps.join(', ')}"
86
+ exit
87
+ end
88
+ end
89
+
90
+ # Returns the banner
91
+ def banner(apps)
92
+ header
93
+ available_apps(apps)
94
+ global_options
95
+ end
96
+
97
+ # Returns the header of the banner
98
+ def header
99
+ @optparse.banner = 'Usage: vgh app [options]'
100
+ @optparse.separator '========================'
101
+ end
102
+
103
+ # Returns a list of available apps
104
+ def available_apps(apps)
105
+ @optparse.separator ''
106
+ @optparse.separator "Available apps: #{apps.join(', ')}"
107
+ end
108
+
109
+ # Returns the header of the global options section
110
+ def global_options
111
+ @optparse.separator ''
112
+ @optparse.separator 'Options:'
113
+ end
114
+
115
+ # Loads the configuration directory
116
+ def confdir
117
+ @optparse.on('-c', '--confdir \'~/.vgh\'', 'Configuration directory') do |conf|
118
+ path = File.expand_path(conf)
119
+ @options[:confdir] = path if File.directory?(path)
120
+ end
121
+ end
122
+
123
+ # Loads the verbosity argument
124
+ def verbose
125
+ @optparse.on('-v', '--[no-]verbose', 'Run verbosely') do |verbose|
126
+ @options[:verbose] = verbose
127
+ end
128
+ end
129
+
130
+ # Loads the logging argument
131
+ def logging
132
+ @optparse.on('-l', '--[no-]logging', 'Enable logging') do |logging|
133
+ @options[:logging] = logging
134
+ end
135
+ end
136
+
137
+ # Loads the help argument
138
+ def help
139
+ @optparse.on_tail('-h', '--help', 'Show this message') do
140
+ show_help
141
+ end
142
+ end
143
+
144
+ # Loads the version argument
145
+ def version
146
+ @optparse.on_tail('-V', '--version', 'Show version') do
147
+ show_version
148
+ end
149
+ end
150
+
151
+ # Parses the arguments
152
+ def validate(args)
153
+ begin
154
+ @optparse.parse!(args)
155
+ rescue OptionParser::ParseError,
156
+ OptionParser::InvalidArgument,
157
+ OptionParser::InvalidOption,
158
+ OptionParser::MissingArgument
159
+ puts $!.to_s
160
+ show_help
161
+ exit
162
+ end
163
+ end
164
+
165
+ # Returns the help
166
+ def show_help
167
+ puts @optparse
168
+ puts ''
169
+ show_version
170
+ exit
171
+ end
172
+
173
+ # Returns a message with the script's version
174
+ def show_version
175
+ puts "VGH Scripts: v#{VGH::VERSION}"
176
+ exit
177
+ end
178
+
179
+ end # Class CLI
180
+
181
+ end # module VGH
182
+
183
+ require 'vgh/apps'
184
+ require 'vgh/version'
185
+
@@ -0,0 +1,176 @@
1
+ require 'aws-sdk'
2
+ require 'yaml'
3
+
4
+ require 'logger'
5
+
6
+ module VGH
7
+
8
+ # Initialize the configuration parser
9
+ def parse_config
10
+ $config ||= Configuration.new
11
+ end
12
+
13
+ # Returns a hash containing the main settings
14
+ def main_config
15
+ $main_config ||= parse_config.main_config
16
+ end
17
+
18
+ # Returns a hash containing the AWS settings
19
+ def aws_config
20
+ $aws_config ||= parse_config.aws_config
21
+ end
22
+
23
+ # Returns a hash containing the app settings
24
+ def app_config
25
+ $app_config ||= parse_config.app_config
26
+ end
27
+
28
+ # Returns a single merged hash with all configurations
29
+ def global_config
30
+ $global_config ||= [main_config, aws_config, app_config].inject(:merge)
31
+ end
32
+
33
+ # Creates a global ec2 method (passing the specified region).
34
+ # The default region is us-east-1, so we overwrite it here.
35
+ def ec2
36
+ region = aws_config[:region]
37
+ if region
38
+ $ec2 = AWS::EC2.new.regions[region]
39
+ else
40
+ $ec2 = AWS::EC2.new
41
+ end
42
+ end
43
+
44
+ # == Description:
45
+ #
46
+ # See {file:README.rdoc#Configuration Configuration Section} in the
47
+ # README file.
48
+ #
49
+ #
50
+ # == Usage:
51
+ #
52
+ # cfg = Configuration.new
53
+ # main_config = cfg.main_config
54
+ # aws_config = cfg.aws_config
55
+ # app_config = cfg.app_config
56
+ #
57
+ # pp main_config
58
+ # pp aws_config
59
+ # pp app_config
60
+ #
61
+ class Configuration
62
+
63
+ # Main configuration
64
+ attr_reader :main_config
65
+
66
+ # AWS configuration
67
+ attr_reader :aws_config
68
+
69
+ # App specific configuration
70
+ attr_reader :app_config
71
+
72
+ # Set defaults
73
+ def initialize
74
+ @confdir = validate_config_dir('/etc/vgh')
75
+ @main = "#{@confdir}/config.yml"
76
+ @aws = "#{@confdir}/aws.config.yml"
77
+ @app = "#{@confdir}/#{app}.config.yml"
78
+ end
79
+
80
+ # IF specified, use the confdir specified in the command line options
81
+ def validate_config_dir(default_confdir, cli = cli_confdir)
82
+ if cli.nil?
83
+ return default_confdir
84
+ else
85
+ return cli
86
+ end
87
+ end
88
+
89
+ # Returns error if the configuration is not right
90
+ def validate(cfg)
91
+ unless config_exists?(cfg) and config_correct?(cfg)
92
+ puts load_error(cfg)
93
+ exit 1
94
+ end
95
+ end
96
+
97
+ # Returns a parsed configuration
98
+ def parse(path)
99
+ @parse = YAML.load(File.read(path))
100
+ end
101
+
102
+ # Checks if the configuration file exists
103
+ def config_exists?(path)
104
+ File.exist?(path)
105
+ end
106
+
107
+ # Checks if the configuration is a valid YAML file
108
+ def config_correct?(path)
109
+ parse(path).kind_of?(Hash)
110
+ end
111
+
112
+ # Parse the main configuration
113
+ def main_config
114
+ message.info "Loading main configuration..."
115
+ validate(@main)
116
+ parse(@main)
117
+ end
118
+
119
+ # Parse the AWS configuration
120
+ def aws_config
121
+ message.info "Loading AWS configuration..."
122
+ validate(@aws)
123
+ @aws_config = parse(@aws)
124
+ load_aws
125
+ return @aws_config
126
+ end
127
+
128
+ # Overwrite the AWS Core configuration
129
+ def load_aws
130
+ AWS.config(@aws_config)
131
+ AWS.config({
132
+ :logger => log,
133
+ :log_formatter => AWS::Core::LogFormatter.colored,
134
+ :max_retries => 2
135
+ })
136
+ end
137
+
138
+ # Parse app specific configuration
139
+ def app_config
140
+ message.info "Loading #{app} configuration..."
141
+ validate(@app)
142
+ @app_config = parse(@app)
143
+ end
144
+
145
+ # Returns the error message in case the configuration os not right
146
+ def load_error(path)
147
+ log.fatal("#{path} is either missing or formatted incorrectly!")
148
+ load_error = <<END
149
+ #{path} is either missing or formatted incorrectly!
150
+
151
+ To run this script, you need to create a file named
152
+ #{path} with your configuration in the following format:
153
+
154
+ # Comment
155
+ :key: 'value'
156
+ :string: 'string value'
157
+ :integer: 123
158
+ :boolean: true/false
159
+ :array:
160
+ - '1st'
161
+ - '2nd'
162
+ :hash:
163
+ -
164
+ :sub_key: 'sub value'
165
+
166
+ END
167
+ end
168
+
169
+ end # class Configuration
170
+
171
+ end # module VGH
172
+
173
+ require 'vgh/cli'
174
+ require 'vgh/output'
175
+ require 'vgh/logging'
176
+