vgh 0.1.2 → 0.2.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.
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'