vgh 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/.travis.yml CHANGED
@@ -3,4 +3,5 @@ rvm:
3
3
  - 1.9.3
4
4
  - 1.8.7
5
5
  - ree
6
- script: "rvmsudo bundle exec rake spec"
6
+ script: "bundle exec rake spec"
7
+ #script: "rvmsudo bundle exec rake spec"
data/CHANGELOG.rdoc CHANGED
@@ -1,3 +1,9 @@
1
+ === 0.2.0
2
+ - Add Checkpoint app
3
+ - Merged all configuration files to a single one
4
+ - Major code refactoring
5
+ - Improved tests
6
+
1
7
  === 0.1.2
2
8
  [Bugs]
3
9
  - Fix snapshot creation process (remove memoization)
data/README.rdoc CHANGED
@@ -8,12 +8,12 @@ A collection of custom scripts used on VladGh.com
8
8
  == Instalation
9
9
 
10
10
  Some system wide dependencies should be installed first:
11
- * ruby 1.8 or 1.9
12
- * rubygems (if ruby 1.8)
13
- * ruby-dev
14
- * build-essential
15
- * libxslt & libxslt-dev
16
- * libxml2 & libxml2-dev
11
+ * ruby 1.8 or 1.9
12
+ * rubygems (if ruby 1.8)
13
+ * ruby-dev
14
+ * build-essential
15
+ * libxslt & libxslt-dev
16
+ * libxml2 & libxml2-dev
17
17
 
18
18
 
19
19
  Add this line to your application's Gemfile:
@@ -31,26 +31,14 @@ Or install it yourself as:
31
31
 
32
32
  == Configuration
33
33
 
34
- This gem looks for the following configuration files inside "+/etc/vgh+" or the
35
- +--confdir+ specified in {VGH::CLI CLI Class}:
34
+ This gem looks for the configuration file inside the following folders
35
+ * +/etc/vgh+
36
+ * +~/.vgh+
37
+ * +--confdir+ (specified in the command line options)
36
38
 
37
- [+config.yml+]
38
- The main configuration file for this gem.
39
- This file should include the AWS specific configuration.
40
- Available values:
41
- - +:access_key_id+ - [String] Your AWS credentials
42
- - +:secret_access_key+ - [String] Your AWS credentials
43
- - +:region+ - optional [String] Your current instance's region.
44
- Ex: +'us-west-1'+
39
+ This gem checks if the file exists and if it is in a correct YAML format.
45
40
 
46
- [+#APP_NAME.config.yml+]
47
- The application specific configuration (see
48
- {file:README.rdoc#Applications Applications} below for a list of available
49
- apps and values).
50
-
51
- This gem checks if the files exists and if they are in a correct YAML format.
52
-
53
- All files should have the following format (where the keys are symbols):
41
+ It should have the following format (where the keys are symbols):
54
42
  # Comment
55
43
  :key: 'value'
56
44
  :string: 'string value'
@@ -63,11 +51,14 @@ All files should have the following format (where the keys are symbols):
63
51
  -
64
52
  :sub_key: 'sub value'
65
53
 
66
- Example configuration files can be found in this gem's directory. You can
67
- find were that is by specifying the +--gemdir+ command line option.
54
+ An example of the configuration file can be found in this gem's directory.
55
+ You can find were that is by specifying the +--gemdir+ command line option.
68
56
 
69
57
  Ex: <tt>vgh --gemdir</tt>
70
58
 
59
+ Read the config.yml.example for a list of configuration options.
60
+
61
+
71
62
  == Command line options
72
63
 
73
64
  The following command line options are available:
@@ -75,8 +66,8 @@ The following command line options are available:
75
66
  [+ApplicationName+] Specify the application you want to run. For a
76
67
  list of available apps see
77
68
  {file:README.rdoc#Applications Applications} below.
78
- [+-c+ <tt>--confdir '~/.vgh'</tt>] Specify the desired directory in which
79
- the configuration files reside.
69
+ [<tt>--confdir=~/.vgh</tt>] Specify the desired directory in which
70
+ the configuration files reside.
80
71
  [+-v+ <tt>--[no-]verbose</tt>] Specify whether to display messages on the
81
72
  screen or not.
82
73
  [+-l+ <tt>--[no-]logging</tt>] Specify whether to log messages or not.
@@ -116,28 +107,27 @@ The following command line options are available:
116
107
  be postponed for as long as the device is suspended.
117
108
 
118
109
 
119
- ==== EC2-Backup Configuration
120
- +ec2-backup.config.yml+
121
- - +:expiration+ -- required [Integer] Number of days to keep the snapshots.
122
- # Ex:
123
- :expiration: 7
124
- - +:mysql_user+ -- required [String] The MySQL user that can flush the tables.
125
- # Ex:
126
- :mysql_user: 'dbadmin'
127
- - +:mysql_pwd+ -- required [String] The password of the above MySQL user.
128
- # Ex:
129
- :mysql_pwd: 'MyStrongPassword'
130
- - +:instance+ -- optional [String] The id of the remote instance you want to backup.
131
- # Ex:
132
- :instance: 'i-12345678'
133
- - +:fqdn+ -- optional [String] The FQDN of the remote instance you want to backup.
134
- # Ex:
135
- :fqdn: 'MyServer.Example.com'
110
+ ==== Intended usage
111
+ # Cron Job:
112
+ 0 */6 * * * vgh ec2-backup -l
113
+
114
+ === Checkpoint
115
+
116
+ [/bin/vgh checkpoint]
117
+
118
+ ==== Description
119
+ This app looks for volumes tagged with the 'CHECKPOINT' key attached to the
120
+ current instance, and creates snapshots for them.
121
+ It is intended to be used as a cron job on an AWS instance running
122
+ Ubuntu.
123
+
124
+ MySQL flushing and LVM suspending specified above in the EC2-Backup app, work
125
+ the same.
136
126
 
137
127
 
138
128
  ==== Intended usage
139
129
  # Cron Job:
140
- 0 */6 * * * vgh ec2-backup -l
130
+ 0 */6 * * * vgh checkpoint -l
141
131
 
142
132
 
143
133
  == Contributing
data/Rakefile CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'bundler/gem_helper'
2
2
  require 'rspec/core/rake_task'
3
3
  require 'yard'
4
+ require 'reek/rake/task'
4
5
 
5
6
  $LOAD_PATH << File.join(File.dirname(__FILE__), 'tasks')
6
7
  Dir['tasks/**/*.rake'].each { |task| load task }
@@ -8,6 +9,12 @@ Dir['tasks/**/*.rake'].each { |task| load task }
8
9
  RSpec::Core::RakeTask.new
9
10
  Bundler::GemHelper.install_tasks
10
11
 
12
+ Reek::Rake::Task.new do |t|
13
+ t.fail_on_error = false
14
+ t.verbose = false
15
+ t.reek_opts = "--quiet"
16
+ end
17
+
11
18
  task :default do
12
19
  puts `rake -T`
13
20
  end
@@ -1,9 +1,30 @@
1
- # Main settings for VGH gem
1
+ # Configuration for the VGH gem
2
2
 
3
- # AWS Credentials:
4
- :access_key_id: 'xxxxxxxxxxxxxxxxxx'
5
- :secret_access_key: 'xxxxxxxxxxxxxxxxxx'
3
+ #####################
4
+ # AWS Configuration #
5
+ #####################
6
+ # (See http://docs.aws.amazon.com/AWSRubySDK/latest/AWS.html#config-class_method
7
+ # for a full list of configuration options):
8
+ # (NOTE: It's recommended to use IAM roles and not put your credentials here:
9
+ # http://docs.aws.amazon.com/AWSSdkDocsRuby/latest/DeveloperGuide/ruby-dg-roles.html)
10
+ #
11
+ #:access_key_id: '1234'
12
+ #:secret_access_key: '4321'
13
+ #:ec2_endpoint: 'ec2.us-west-2.amazonaws.com'
14
+ #:auto_scaling_endpoint: 'autoscaling.us-west-2.amazonaws.com'
6
15
 
7
- # AWS Region (optional). It defaults to 'us-east-1'
8
- #:region: 'us-west-1'
9
16
 
17
+ ###########################
18
+ # EC2-Backup app settings #
19
+ ###########################
20
+
21
+ # Expiration period (defaults to 7 days)
22
+ #:expiration: 7 # Number of days to keep the snapshots
23
+
24
+ # MySQL Settings
25
+ #:mysql_user: 'dbadmin' # The MySQL user that can flush the tables
26
+ #:mysql_password: 'MyStrongPassword' # The password of the above MySQL user
27
+
28
+ # Remote settings
29
+ #:instance: 'i-12345678' # The id of the remote instance you want to backup
30
+ #:fqdn: 'server.example.com' # The fully qualified domain name of the server
@@ -0,0 +1,55 @@
1
+ module VGH
2
+ module APPS
3
+
4
+ # == Description:
5
+ #
6
+ # See {file:README.rdoc#Checkpoint Checkpoint Section} in the README
7
+ # file.
8
+ #
9
+ # == Usage:
10
+ # checkpoint = APPS::Checkpoint.new
11
+ # checkpoint.run
12
+ #
13
+ class Checkpoint
14
+
15
+ # @return [Object] Volumes Class
16
+ attr_reader :volumes
17
+
18
+ # Initialize external classes
19
+ def initialize
20
+ @volumes ||= EC2::Volume.new
21
+ end
22
+
23
+ # Runs the checkpoint app logic
24
+ def run
25
+
26
+ System.lock
27
+
28
+ vols = volumes
29
+ vols.list_tagged('CHECKPOINT').map {|vid|
30
+ snap_and_tag(
31
+ vid,
32
+ "CHECKPOINT for #{vid}(#{vols.name_tag(vid)})",
33
+ {
34
+ 'Name' => fqdn,
35
+ 'CHECKPOINT' => "#{instance_id};#{vid}"
36
+ }
37
+ )
38
+ }
39
+
40
+ System.unlock
41
+
42
+ end
43
+
44
+ end # class EC2_Backup
45
+
46
+ end # module APPS
47
+ end # module VGH
48
+
49
+ require 'vgh/output'
50
+ require 'vgh/configuration'
51
+ require 'vgh/system/lvm'
52
+ require 'vgh/system/mysql'
53
+ require 'vgh/ec2/volume'
54
+ require 'vgh/ec2/snapshot'
55
+
@@ -12,58 +12,33 @@ module APPS
12
12
  #
13
13
  class EC2_Backup
14
14
 
15
- # Loads the necessary classes
15
+ # @return [Object] Volumes Class
16
+ attr_reader :volumes
17
+
18
+ # Initialize external classes
16
19
  def initialize
17
- # Check if this script is run with root credentials
18
- abort "ERROR: This app needs to be run as root!" unless is_root?
19
- @mysql = System::MySQL.new
20
- @lv = System::LV.new
21
- @volume = Extended_AWS::Extended_EC2::Volume.new
22
- @snapshot = Extended_AWS::Extended_EC2::Snapshot.new
20
+ @volumes ||= EC2::Volume.new
23
21
  end
24
22
 
25
23
  # Runs the ec2-backup app logic
26
24
  def run
27
- lock
28
-
29
- begin
30
- @volume.list.map {|id, info|
31
- @snapshot.create(id, info[:tag])
32
- }
33
- rescue
34
- message.fatal "Something went wrong in the snapshot creation process!"
35
- end
36
-
37
- unlock
38
25
 
39
- @snapshot.purge
40
- end
26
+ System.lock
41
27
 
42
- # Flushes MySQL tables and suspends Logical Volumes
43
- def lock
44
- unless remotely?
45
- @mysql.flush
46
- @lv.suspend
47
- end
48
- end
28
+ vols = volumes
29
+ vols.list.map {|vid|
30
+ snap_and_tag(
31
+ vid,
32
+ "Backup for #{vid}(#{vols.name_tag(vid)})",
33
+ {
34
+ 'Name' => fqdn,
35
+ 'BACKUP' => "#{instance_id};#{vid}"
36
+ }
37
+ )
38
+ }
49
39
 
50
- # Resumes MySQL and Logical Volumes
51
- def unlock
52
- unless remotely?
53
- @mysql.unlock
54
- @lv.resume
55
- end
56
- end
40
+ System.unlock
57
41
 
58
- # Checks if this script is run remotely.
59
- # @return [Boolean]
60
- def remotely?
61
- cfg = app_config
62
- if cfg[:instance] or cfg[:fqdn]
63
- @remotely = true
64
- else
65
- @remotely = false
66
- end
67
42
  end
68
43
 
69
44
  end # class EC2_Backup
@@ -72,8 +47,9 @@ end # module APPS
72
47
  end # module VGH
73
48
 
74
49
  require 'vgh/output'
50
+ require 'vgh/configuration'
75
51
  require 'vgh/system/lvm'
76
52
  require 'vgh/system/mysql'
77
- require 'vgh/extended_aws/extended_ec2/volume'
78
- require 'vgh/extended_aws/extended_ec2/snapshot'
53
+ require 'vgh/ec2/volume'
54
+ require 'vgh/ec2/snapshot'
79
55
 
data/lib/vgh/apps.rb CHANGED
@@ -13,7 +13,8 @@ module APPS
13
13
  # @return [Array]
14
14
  def self.list
15
15
  @apps ||= [
16
- 'ec2-backup'
16
+ 'ec2-backup',
17
+ 'checkpoint'
17
18
  ]
18
19
  end
19
20
 
data/lib/vgh/cli.rb CHANGED
@@ -14,12 +14,6 @@ module VGH
14
14
  $app ||= cli[:app]
15
15
  end
16
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
17
  # Returns verbosity
24
18
  # @return [Boolean]
25
19
  def verbose?
@@ -56,6 +50,7 @@ module VGH
56
50
  @options[:app] = false
57
51
  @options[:verbose] = false
58
52
  @options[:logging] = false
53
+ @options[:confdir] = nil
59
54
  end
60
55
 
61
56
  # Collect options
@@ -115,9 +110,14 @@ module VGH
115
110
 
116
111
  # Loads the configuration directory
117
112
  def confdir
118
- @optparse.on('-c', '--confdir \'~/.vgh\'', 'Configuration directory') do |conf|
119
- path = File.expand_path(conf)
120
- @options[:confdir] = path if File.directory?(path)
113
+ @optparse.on('--confdir=PATH', 'Configuration directory') do |config_dir|
114
+ path = File.expand_path(config_dir)
115
+ if File.directory?(path)
116
+ @options[:confdir] = path
117
+ else
118
+ puts "The configuration directory '#{config_dir}' does not exist!"
119
+ exit
120
+ end
121
121
  end
122
122
  end
123
123
 
@@ -5,40 +5,12 @@ require 'logger'
5
5
 
6
6
  module VGH
7
7
 
8
- # Initialize the configuration parser
9
- # @return [Hash]
10
- def parse_config
11
- $parse_config ||= Configuration.new
12
- end
13
-
14
- # Returns a hash containing the main settings
8
+ # Global config method
15
9
  # @return [Hash]
16
10
  def config
17
- $config ||= parse_config.main_config
18
- end
19
-
20
- # Returns a hash containing the app settings
21
- # @return [Hash]
22
- def app_config
23
- $app_config ||= parse_config.app_config
11
+ $config ||= Configuration.new.config
24
12
  end
25
13
 
26
- # Returns a single merged hash with all configurations
27
- # @return [Hash]
28
- def global_config
29
- $global_config ||= [config, app_config].inject(:merge)
30
- end
31
-
32
- # Creates a global ec2 method (passing the specified region).
33
- # The default region is us-east-1, so we overwrite it here.
34
- def ec2
35
- region = config[:region]
36
- if region
37
- $ec2 = AWS::EC2.new.regions[region]
38
- else
39
- $ec2 = AWS::EC2.new
40
- end
41
- end
42
14
 
43
15
  # == Description:
44
16
  #
@@ -48,81 +20,98 @@ module VGH
48
20
  #
49
21
  # == Usage:
50
22
  #
51
- # parse = Configuration.new
52
- # main_config = parse.main_config
53
- # app_config = parse.app_config
23
+ # config = Configuration.new.config
24
+ # mysetting = config[:mysetting]
54
25
  #
55
- # pp main_config
56
- # pp app_config
57
26
  #
58
27
  class Configuration
59
28
 
60
- # Main configuration
61
- attr_reader :main_config
29
+ # Global configuration
30
+ # @return [Hash] The configuration hash
31
+ attr_reader :config
62
32
 
63
- # App specific configuration
64
- attr_reader :app_config
65
-
66
- # Set defaults
33
+ # Parse the main configuration
34
+ # @return [Hash]
67
35
  def initialize
68
- @confdir = validate_config_dir('/etc/vgh')
69
- @main = "#{@confdir}/config.yml"
70
- @app = "#{@confdir}/#{app}.config.yml"
36
+ message.info "Loading configuration..."
37
+ @config ||= validate(config_file)
38
+ aws_config
39
+ end
40
+
41
+ # The global configuration directory
42
+ # @return [String]
43
+ def global_config_dir
44
+ '/etc/vgh'
45
+ end
46
+
47
+ # The user configuration directory
48
+ # @return [String]
49
+ def user_config_dir
50
+ File.expand_path('~/.vgh')
71
51
  end
72
52
 
73
53
  # IF specified, use the confdir specified in the command line options
74
- def validate_config_dir(default_confdir)
75
- cli = cli_confdir
76
- if cli.nil?
77
- return default_confdir
54
+ # @return [String]
55
+ def confdir
56
+ cli_confdir = cli[:confdir]
57
+ global = global_config_dir
58
+ if !cli_confdir.nil?
59
+ return cli_confdir
60
+ elsif File.directory?(global)
61
+ return global
78
62
  else
79
- return cli
63
+ return user_config_dir
80
64
  end
81
65
  end
82
66
 
67
+ # The main configuration file
68
+ # @return [String]
69
+ def config_file
70
+ "#{confdir}/config.yml"
71
+ end
72
+
83
73
  # Returns error if the configuration is not right
84
- def validate(cfg)
85
- unless config_exists?(cfg) and config_correct?(cfg)
86
- puts load_error(cfg)
74
+ # @return [Hash]
75
+ def validate(path)
76
+ if config_exists?(path) and config_correct?(path)
77
+ parse(path)
78
+ else
79
+ puts load_error(path)
87
80
  exit 1
88
81
  end
89
82
  end
90
83
 
91
84
  # Checks if the configuration file exists
85
+ # @return [Boolean]
92
86
  def config_exists?(path)
93
87
  File.exist?(path)
94
88
  end
95
89
 
96
90
  # Checks if the configuration is a valid YAML file
91
+ # @return [Boolean]
97
92
  def config_correct?(path)
98
93
  parse(path).kind_of?(Hash)
99
94
  end
100
95
 
101
96
  # Returns a parsed configuration
97
+ # @return [Hash]
102
98
  def parse(path)
103
- @parse = YAML.load(File.read(path))
99
+ YAML.load(File.read(path))
104
100
  end
105
101
 
106
- # Parse the main configuration
107
- def main_config
108
- message.info "Loading main configuration..."
109
- validate(@main)
110
- @main_config = parse(@main)
111
- AWS.config({
112
- :access_key_id => @main_config[:access_key_id],
113
- :secret_access_key => @main_config[:secret_access_key],
114
- :logger => log,
115
- :log_formatter => AWS::Core::LogFormatter.colored,
116
- :max_retries => 2
117
- })
118
- return @main_config
102
+ # Configures AWS
103
+ def aws_config
104
+ AWS.config(config)
105
+ AWS.config(aws_logging)
119
106
  end
120
107
 
121
- # Parse app specific configuration
122
- def app_config
123
- message.info "Loading #{app} configuration..."
124
- validate(@app)
125
- @app_config = parse(@app)
108
+ # Implements our own Logging class
109
+ # @return [Hash]
110
+ def aws_logging
111
+ aws_logging ||= {
112
+ :logger => log,
113
+ :log_formatter => AWS::Core::LogFormatter.colored
114
+ }
126
115
  end
127
116
 
128
117
  # Returns the error message in case the configuration os not right
@@ -6,16 +6,16 @@ module VGH
6
6
  # queries the API server for it.
7
7
  # @return [String]
8
8
  def instance_id
9
- remote_instance = app_config[:instance]
9
+ remote_instance = config[:instance]
10
10
  if remote_instance
11
11
  $instance_id ||= remote_instance
12
12
  else
13
- $instance_id ||= VGH::Extended_AWS::Extended_EC2::MetaData.new.instance_id
13
+ $instance_id ||= VGH::EC2::MetaData.new.instance_id
14
14
  end
15
+ return $instance_id
15
16
  end
16
17
 
17
- module Extended_AWS
18
- module Extended_EC2
18
+ module EC2
19
19
 
20
20
  # This class gathers metadata information about the current instance, used by
21
21
  # the applications in this gem.
@@ -50,8 +50,7 @@ class MetaData
50
50
 
51
51
  end
52
52
 
53
- end # module Extended_EC2
54
- end # module Extended_AWS
53
+ end # module EC2
55
54
  end # module VGH
56
55
 
57
56
  require 'vgh/output'