visor-common 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/bin/visor-config ADDED
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # VISoR configuration command line interface script.
4
+ # Run <visor-config -h> to get more usage help.
5
+
6
+ require File.expand_path('../../lib/visor-common', __FILE__)
7
+
8
+ unless ARGV.empty?
9
+ puts %q[Usage: visor-config
10
+
11
+ This script will generate the following VISOR configuration and logging directories and files:
12
+
13
+ - ~/.visor The VISOR default configuration directory
14
+ - ~/.visor/visor-config.yml The VISOR configuration file template
15
+ - ~/.visor/logs The VISOR default logging directory
16
+
17
+ Run it without any arguments to generate the above directories and files.
18
+
19
+ ]
20
+ exit
21
+ end
22
+
23
+ dir = "#{ENV['HOME']}/.visor"
24
+
25
+ if Dir.exists? dir
26
+ puts "Directory #{dir} already exists, do you want to override it and its files? (y/n)"
27
+ if gets.chomp.downcase == "n"
28
+ puts "Aborting configurations override."
29
+ exit
30
+ end
31
+ end
32
+
33
+ puts "\nGenerating VISOR configuration directories and files:\n\n"
34
+ sleep (0.5)
35
+ print "creating #{dir}..."
36
+ system "mkdir #{dir}"
37
+ puts " [DONE]"
38
+ print "creating #{dir}/logs..."
39
+ system "mkdir #{dir}/logs"
40
+ puts " [DONE]"
41
+ print "creating #{dir}/visor-config.yml..."
42
+ system "echo '#{Visor::Common::Config::CONFIG_TEMPLATE}' > #{dir}/visor-config.yml"
43
+ puts " [DONE]"
44
+ puts "\nAll configurations were successful. Now open and customize the VISOR configuration file at #{dir}/visor-config.yml\n\n"
@@ -0,0 +1,257 @@
1
+ require 'yaml'
2
+ require 'logger'
3
+
4
+ module Visor
5
+ module Common
6
+
7
+ # The Config module provides a set of utility functions to manipulate configuration
8
+ # files and Logging.
9
+ #
10
+ module Config
11
+
12
+ include Visor::Common::Exception
13
+
14
+ # Default possible configuration file directories
15
+ DEFAULT_CONFIG_DIRS = %w{. ~/.visor /etc/visor}
16
+ # Default configuration file name
17
+ DEFAULT_CONFIG_FILE = 'visor-config.yml'
18
+ # Default logs path
19
+ DEFAULT_LOG_PATH = '~/.visor/logs'
20
+ # Default log datetime format
21
+ DEFAULT_LOG_DATETIME = "%Y-%m-%d %H:%M:%S"
22
+ # Default log level
23
+ DEFAULT_LOG_LEVEL = Logger::INFO
24
+ # Configuration file template
25
+ CONFIG_TEMPLATE = %q[
26
+ # ========== Default always loaded configuration throughout VISOR sub-systems =====
27
+ #
28
+ default: &default
29
+ # Set the default log date time format
30
+ log_datetime_format: "%Y-%m-%d %H:%M:%S"
31
+ # Set the default log path
32
+ log_path: ~/.visor/logs
33
+ # VISoR access and secret key credentials, grab yours from $ visor-admin
34
+ access_key: XXXXXX
35
+ secret_key: XXXXXX
36
+
37
+ # ================================ VISoR Auth ==================================
38
+ #
39
+ visor_auth:
40
+ # Merge default configuration
41
+ <<: *default
42
+ # Address to bind the meta server
43
+ bind_host: 0.0.0.0
44
+ # Port to bind the meta server
45
+ bind_port: 4566
46
+ # Backend connection string (syntax: name://user:pass@host:port/database)
47
+ backend: mongodb://:@127.0.0.1:27017/visor
48
+ # backend: mysql://visor:passwd@127.0.0.1:3306/visor
49
+ # Log file name (available: filename or empty for STDOUT)
50
+ log_file: visor-auth-server.log
51
+ # Log level to start logging above events (available: DEBUG, INFO)
52
+ log_level: INFO
53
+
54
+ # ================================ VISoR Meta =====================================
55
+ #
56
+ visor_meta:
57
+ # Merge default configuration
58
+ <<: *default
59
+ # Address to bind the meta server
60
+ bind_host: 0.0.0.0
61
+ # Port to bind the meta server
62
+ bind_port: 4567
63
+ # Backend connection string (syntax: name://user:pass@host:port/database)
64
+ backend: mongodb://:@127.0.0.1:27017/visor
65
+ # backend: mysql://visor:passwd@127.0.0.1:3306/visor
66
+ # Log file name (available: filename or empty for STDOUT)
67
+ log_file: visor-meta-server.log
68
+ # Log level to start logging above events (available: DEBUG, INFO)
69
+ log_level: INFO
70
+
71
+ # ================================ VISoR Image ====================================
72
+ #
73
+ visor_image:
74
+ # Merge default configuration
75
+ <<: *default
76
+ # Address to bind the meta server
77
+ bind_host: 0.0.0.0
78
+ # Port to bind the meta server
79
+ bind_port: 4568
80
+ # Log file name (available: filename or empty for STDOUT)
81
+ log_file: visor-api-server.log
82
+ # Log level to start logging equal and above events (available: DEBUG, INFO)
83
+ log_level: INFO
84
+
85
+ # ============================== VISoR Image Backends =============================
86
+ #
87
+ visor_store:
88
+ # Default store backend (available: s3, cumulus, walrus, lunacloud, hdfs, file)
89
+ default: file
90
+ #
91
+ # FileSystem store backend settings
92
+ #
93
+ file:
94
+ # Default directory to store image files in
95
+ directory: ~/VMs/
96
+ #
97
+ # Amazon Simple Storage (S3) store backend settings
98
+ #
99
+ s3:
100
+ # The bucket to store images in, make sure you provide an already existing bucket
101
+ bucket: XXXXXX
102
+ # Access and secret key credentials, grab yours on your AWS account settings page
103
+ access_key: XXXXXX
104
+ secret_key: XXXXXX
105
+ #
106
+ # Nimbus Cumulus (Cumulus) store backend settings
107
+ #
108
+ cumulus:
109
+ # The Cumulus host address
110
+ host: XXXXXX
111
+ # The Cumulus port number
112
+ port: XXXXXX
113
+ # The bucket to store images in, make sure you provide an already existing bucket
114
+ bucket: XXXXXX
115
+ # Access and secret key credentials, grab yours with Nimbus cloud
116
+ access_key: XXXXXX
117
+ secret_key: XXXXXX
118
+ #
119
+ # Eucalyptus Walrus (Walrus) store backend settings
120
+ #
121
+ walrus:
122
+ # The Walrus host address
123
+ host: XXXXXX
124
+ # The Walrus port number
125
+ port: XXXXXX
126
+ # A
127
+ # The bucket to store images in, make sure you provide an already existing bucket
128
+ bucket: XXXXXX
129
+ # Access and secret key credentials, grab yours with Eucalyptus cloud
130
+ access_key: XXXXXX
131
+ secret_key: XXXXXX
132
+ #
133
+ # Lunacloud (Lunacloud) store backend settings
134
+ #
135
+ lunacloud:
136
+ # The Lunacloud host address
137
+ host: betalcs.lunacloud.com
138
+ # The Lunacloud port number
139
+ port: 80
140
+ # The bucket to store images in, make sure you provide an already existing bucket
141
+ bucket: XXXXXX
142
+ # Access and secret key credentials, grab yours with Lunacloud cloud
143
+ access_key: XXXXXX
144
+ secret_key: XXXXXX
145
+ #
146
+ # Apache Hadoop HDFS (HDFS) store backend settings
147
+ #
148
+ hdfs:
149
+ # The HDFS host address
150
+ host: XXXXXX
151
+ # The HDFS port number
152
+ port: XXXXXX
153
+ # The bucket to store images in
154
+ bucket: XXXXXX
155
+ # Access credentials
156
+ username: XXXXXX
157
+ ]
158
+
159
+ # Ordered search for a VISoR configuration file on default locations and return the first matched.
160
+ #
161
+ # @param other_file [String] Other file to use instead of default config files.
162
+ #
163
+ # @return [nil, String] Returns nil if no valid config file was found or a string with the
164
+ # absolute path to the found configuration file.
165
+ #
166
+ def self.find_config_file(other_file = nil)
167
+ if other_file
168
+ file = File.expand_path(other_file)
169
+ File.exists?(file) ? file : nil
170
+ else
171
+ DEFAULT_CONFIG_DIRS.each do |dir|
172
+ file = File.join(File.expand_path(dir), DEFAULT_CONFIG_FILE)
173
+ return file if File.exists?(file)
174
+ end
175
+ end
176
+ end
177
+
178
+ # Looks for a VISoR configuration file througth {#self.find_config_file} and returns a hash with
179
+ # all configuration settings or just a sub-system scoped settings.
180
+ #
181
+ # @param scope [String] Used to return just the settings about a specific sub-system.
182
+ # @param other_file [String] Other file to use instead of default config files.
183
+ #
184
+ # @return [Hash] Global or scoped settings.
185
+ #
186
+ # @raise [RuntimeError] If there is no configuration files or if errors occur during parsing.
187
+ #
188
+ #TODO: YAML.load_openstruct(File.read(file))
189
+ def self.load_config(scope = nil, other_file = nil)
190
+ file = find_config_file(other_file)
191
+ raise ConfigError, "Could not found any configuration file." unless file
192
+ begin
193
+ content = YAML.load_file(file).symbolize_keys
194
+ rescue => e
195
+ raise ConfigError, "Error while parsing the configuration file: #{e.message}."
196
+ end
197
+ config = scope ? content[scope] : content
198
+ config.merge(file: file)
199
+ end
200
+
201
+ # Build and return a Logger instance for a given VISoR sub-system, based on configuration
202
+ # file options, which are validated througth {#self.validate_logger}.
203
+ #
204
+ # @param app_name [Symbol] The VISoR sub-system app name to build a log for.
205
+ # @option configs [Hash] Optional configuration options to override config file ones.
206
+ #
207
+ # @return [Logger] A logger instance already properly configured.
208
+ #
209
+ def self.build_logger(app_name, configs = nil)
210
+ conf = configs || load_config
211
+
212
+ raise ConfigError, "Cannot locate 'default' configuration group." unless conf[:default]
213
+ raise ConfigError, "Cannot locate '#{app_name}' configuration group." unless conf[app_name]
214
+
215
+ log_path = File.expand_path(conf[:default][:log_path] || DEFAULT_LOG_PATH)
216
+ log_datetime = conf[:default][:log_datetime_format] || DEFAULT_LOG_DATETIME
217
+ log_file = conf[app_name][:log_file] || STDOUT
218
+ log_level = conf[app_name][:log_level] || DEFAULT_LOG_LEVEL
219
+
220
+ begin
221
+ Dir.mkdir(log_path) unless Dir.exists?(log_path)
222
+ rescue => e
223
+ raise ConfigError, "Cannot create the 'default/log_path' directory: #{e.message}."
224
+ end
225
+
226
+ begin
227
+ output = log_file==STDOUT ? log_file : File.join(log_path, log_file)
228
+ log = Logger.new(output, 5, 1024*1024)
229
+ rescue => e
230
+ raise ConfigError, "Error while create the logger for #{output}: #{e.message}."
231
+ end
232
+
233
+ begin
234
+ log.datetime_format = log_datetime
235
+ log.level = get_log_level(log_level)
236
+ rescue => e
237
+ raise ConfigError, "Error while setting logger properties: #{e.message}."
238
+ end
239
+ log
240
+ end
241
+
242
+ private
243
+
244
+ def self.get_log_level(level)
245
+ case level
246
+ when 'DEBUG' then
247
+ Logger::DEBUG
248
+ when 'INFO' then
249
+ Logger::INFO
250
+ else
251
+ DEFAULT_LOG_LEVEL
252
+ end
253
+ end
254
+
255
+ end
256
+ end
257
+ end
@@ -0,0 +1,34 @@
1
+ module Visor
2
+ module Common
3
+
4
+ # The Exceptions module introduces a set of custom exceptions used along
5
+ # all VISoR sub-systems.
6
+ #
7
+ module Exception
8
+
9
+ # Raise if invalid data is provided within new metadata
10
+ class Invalid < ArgumentError; end
11
+
12
+ # Raise if no image meta or file path is not found
13
+ class NotFound < StandardError; end
14
+
15
+ # Raise on a configuration error
16
+ class ConfigError < RuntimeError; end
17
+
18
+ # Raise if provided store backend is not supported
19
+ class UnsupportedStore < RuntimeError; end
20
+
21
+ # Raise if a record or file is already stored
22
+ class Duplicated < RuntimeError; end
23
+
24
+ # Raise on an internal server error
25
+ class InternalError < RuntimeError; end
26
+
27
+ # Raise on error trying to manipulate image files
28
+ class Forbidden < RuntimeError; end
29
+
30
+ # Raise on error trying to update image files/meta
31
+ class ConflictError < RuntimeError; end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,27 @@
1
+ module Visor
2
+ module Common
3
+
4
+ # The Module Extensions provides a set of functions to extend the Standard Core Libraries
5
+ # with custom usefull methods used allong all VISoR sub-systems.
6
+ #
7
+ module Extensions
8
+ #
9
+ # Extending Array class
10
+ #
11
+ module Array
12
+
13
+ # Convert each array element to an OpenStruct.
14
+ # Used for Hash.to_openstruct
15
+ #
16
+ # @return [Array] The array with elements converted to OpenStruct.
17
+ #
18
+ def to_openstruct
19
+ map { |el| el.to_openstruct }
20
+ end
21
+
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ Array.send :include, Visor::Common::Extensions::Array
@@ -0,0 +1,121 @@
1
+ require 'ostruct'
2
+
3
+ module Visor
4
+ module Common
5
+
6
+ # The Module Extensions provides a set of functions to extend the Standard Core Libraries
7
+ # with custom usefull methods used allong all VISoR sub-systems.
8
+ #
9
+ module Extensions
10
+ #
11
+ # Extending Hash class
12
+ #
13
+ module Hash
14
+
15
+ # Return a new hash with all keys converted to strings.
16
+ #
17
+ def stringify_keys
18
+ inject({}) do |acc, (k, v)|
19
+ key = Symbol === k ? k.to_s : k
20
+ value = Hash === v ? v.stringify_keys : v
21
+ acc[key] = value
22
+ acc
23
+ end
24
+ end
25
+
26
+ # Destructively convert all keys to strings.
27
+ #
28
+ def stringify_keys!
29
+ self.replace(self.stringify_keys)
30
+ end
31
+
32
+ # Return a new hash with all keys converted to symbols.
33
+ #
34
+ def symbolize_keys
35
+ inject({}) do |acc, (k, v)|
36
+ key = String === k ? k.to_sym : k
37
+ value = Hash === v ? v.symbolize_keys : v
38
+ acc[key] = value
39
+ acc
40
+ end
41
+ end
42
+
43
+ # Destructively convert all keys to symbols.
44
+ #
45
+ def symbolize_keys!
46
+ self.replace(self.symbolize_keys)
47
+ end
48
+
49
+ # Validate all keys in a hash match *valid keys.
50
+ #
51
+ # @param [Array] valid_keys Valid keys.
52
+ #
53
+ # @raise [ArgumentError] On a mismatch.
54
+ #
55
+ def assert_valid_keys(*valid_keys)
56
+ unknown_keys = keys - valid_keys.flatten
57
+ raise ArgumentError, "Unknown fields: #{unknown_keys.join(", ")}" unless unknown_keys.empty?
58
+ end
59
+
60
+ # Validate inclusions of some keys in a hash.
61
+ #
62
+ # @param [Array] inclusion_keys Keys that must be included in the hash.
63
+ #
64
+ # @raise [ArgumentError] If some key is not included.
65
+ #
66
+ def assert_inclusion_keys(*inclusion_keys)
67
+ inc = inclusion_keys.flatten - keys
68
+ raise ArgumentError, "These fields are required: #{inc.join(', ')}" unless inc.empty?
69
+ end
70
+
71
+ # Validate non-inclusion of some keys in a hash.
72
+ #
73
+ # @param [Array] exclude_keys Keys that must be not be included in the hash.
74
+ #
75
+ # @raise [ArgumentError] If some key is included.
76
+ #
77
+ def assert_exclusion_keys(*exclude_keys)
78
+ exc = exclude_keys.flatten & keys
79
+ raise ArgumentError, "These fields are read-only: #{exc.join(', ')}" unless exc.empty?
80
+ end
81
+
82
+ # Validate value of some key in a hash.
83
+ #
84
+ # @param [Array] valid_values Values to assert against the given key.
85
+ # @param [Array] key The key to assert its value.
86
+ #
87
+ # @raise [ArgumentError] If some key is included.
88
+ #
89
+ def assert_valid_values_for(key, *valid_values)
90
+ unless valid_values.flatten.include?(self[key.to_sym]) || self[key].nil?
91
+ raise ArgumentError, "Invalid #{key.to_s} '#{self[key]}', available options: #{valid_values.join(', ')}"
92
+ end
93
+ end
94
+
95
+ # Set some keys in a hash to a given value, possibly ignoring some keys.
96
+ #
97
+ # @param [Array] keys_to_set Keys to set its value.
98
+ # @param [Array] keys_to_ignore Keys to be ignored.
99
+ # @param [Array] to_value Value to set the wanted keys.
100
+ #
101
+ # @raise [ArgumentError] If some key is included.
102
+ #
103
+ def set_blank_keys_value_to(keys_to_set, keys_to_ignore, to_value)
104
+ keys_to_set.each { |k| self.merge!(k => to_value) unless self[k] || keys_to_ignore.include?(k) }
105
+ end
106
+
107
+ # Convert a Hash to a OpenStruct, so it can be accessed as options like: h[key] => h.key
108
+ #
109
+ # @return [OpenStruct] The resulting OpenStruct object.
110
+ #
111
+ def to_openstruct
112
+ mapped = {}
113
+ each { |key, value| mapped[key] = value.to_openstruct }
114
+ OpenStruct.new(mapped)
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
120
+
121
+ Hash.send :include, Visor::Common::Extensions::Hash
@@ -0,0 +1,9 @@
1
+ #
2
+ # Extends the Logger Class
3
+ #
4
+ # Make Rack::CommonLogger accept a Logger instance
5
+ # without raising undefined method `write' for #<Logger:0x007fc12db61778>
6
+ #
7
+ class Logger
8
+ alias write <<
9
+ end
@@ -0,0 +1,27 @@
1
+ module Visor
2
+ module Common
3
+
4
+ # The Module Extensions provides a set of functions to extend the Standard Core Libraries
5
+ # with custom usefull methods used allong all VISoR sub-systems.
6
+ #
7
+ module Extensions
8
+ #
9
+ # Extending Object class
10
+ #
11
+ module Object
12
+
13
+ # Pass from Hash.to_openstruct
14
+ #
15
+ # @return [self] The object itself.
16
+ #
17
+ def to_openstruct
18
+ self
19
+ end
20
+
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ Object.send :include, Visor::Common::Extensions::Object
27
+
@@ -0,0 +1,29 @@
1
+ module Visor
2
+ module Common
3
+
4
+ # The Module Extensions provides a set of functions to extend the Standard Core Libraries
5
+ # with custom usefull methods used allong all VISoR sub-systems.
6
+ #
7
+ module Extensions
8
+ #
9
+ # Extending YAML library
10
+ #
11
+ module YAML
12
+
13
+ # Load a YAML source to to an OpenStruct object.
14
+ # Used for Hash.to_openstruct
15
+ #
16
+ # @param source [YAML] A file or a parsed YAML object.
17
+ #
18
+ # @return [OpenStruct] YAML file parsed to an OpenStruct object.
19
+ #
20
+ def self.load_openstruct(source)
21
+ self.load(source).to_openstruct
22
+ end
23
+
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ YAML.send :include, Visor::Common::Extensions::YAML
@@ -0,0 +1,103 @@
1
+ require 'time'
2
+ require 'openssl'
3
+ require 'base64'
4
+
5
+ module Visor
6
+ module Common
7
+
8
+ # The Util module provides a set of utility functions used along all VISoR sub-systems.
9
+ #
10
+ module Util
11
+ extend self
12
+
13
+ # Push a hash containing image metadata into an HTTP header.
14
+ # Each key value pair is pushed as a string of the form 'x-image-meta-<key>'.
15
+ #
16
+ # @param meta [Hash] The image metadata
17
+ # @param header [Hash] (nil) The HTTP headers hash
18
+ #
19
+ # @return [Hash] The header containing the metadata headers
20
+ #
21
+ def push_meta_into_headers(meta, headers = {})
22
+ meta.each { |k, v| headers["x-image-meta-#{k.to_s.downcase}"] = v.to_s }
23
+ headers
24
+ end
25
+
26
+ def pull_meta_from_headers(headers)
27
+ meta = {}
28
+ headers.each do |k, v|
29
+ if key = k.split(/x[_-]image[_-]meta[_-]/i)[1]
30
+ value = parse_value v
31
+ meta[key.downcase.to_sym] = value
32
+ end
33
+ end
34
+ meta
35
+ end
36
+
37
+ def parse_value(string)
38
+ if is_integer?(string) then
39
+ Integer(string)
40
+ elsif is_float?(string) then
41
+ Float(object)
42
+ elsif is_date?(string) then
43
+ Time.parse(string)
44
+ else
45
+ string
46
+ end
47
+ end
48
+
49
+ def is_integer?(object)
50
+ true if Integer(object) rescue false
51
+ end
52
+
53
+ def is_float?(object)
54
+ true if Float(object) rescue false
55
+ end
56
+
57
+ def is_date?(object)
58
+ regexp = /\d{4}[-\/]\d{1,2}[-\/]\d{1,2}\s\d{2}:\d{2}:\d{2}\s\W\d{4}/
59
+ object.match(regexp) ? true : false
60
+ end
61
+
62
+ def sign_request(access_key, secret_key, method, path, headers={})
63
+ headers['Date'] ||= Time.now.utc.httpdate
64
+ desc = canonical_description(method, path, headers)
65
+ signature = Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha1'), secret_key, desc)).strip
66
+
67
+ headers['Authorization'] = "VISOR #{access_key}:#{signature}"
68
+ end
69
+
70
+ def canonical_description(method, path, headers={})
71
+ attributes = {}
72
+ headers.each do |key, value|
73
+ key = key.downcase
74
+ attributes[key] = value.to_s.strip if key.match(/^x-image-meta-|^content-md5$|^content-type$|^date$/o)
75
+ end
76
+
77
+ attributes['content-type'] ||= ''
78
+ attributes['content-md5'] ||= ''
79
+
80
+ desc = "#{method}\n"
81
+ attributes.sort { |a, b| a[0] <=> b[0] }.each do |key, value|
82
+ desc << (key.match(/^x-image-meta-/o) ? "#{key}:#{value}\n" : "#{value}\n")
83
+ end
84
+ desc << path.gsub(/\?.*$/, '')
85
+ end
86
+
87
+ def authorize(env, vas)
88
+ auth = env['headers']['Authorization']
89
+ raise Visor::Common::Exception::Forbidden, "Authorization not provided." unless auth
90
+ access_key = auth.scan(/\ (\w+):/).flatten.first
91
+ raise Visor::Common::Exception::Forbidden, "No access key found in Authorization." unless access_key
92
+ user = vas.get_user(access_key) rescue nil
93
+ raise Visor::Common::Exception::Forbidden, "No user found with access key '#{access_key}'." unless user
94
+ sign = sign_request(user[:access_key], user[:secret_key], env['REQUEST_METHOD'], env['REQUEST_PATH'], env['headers'])
95
+ raise Visor::Common::Exception::Forbidden, "Invalid authorization, signatures do not match." unless auth == sign
96
+ access_key
97
+ end
98
+
99
+ end
100
+ end
101
+ end
102
+
103
+ #sign_request('key', 'secret', 'GET', '/users/joaodrp', {'x-image-meta-name' => 'hi'})
@@ -0,0 +1,5 @@
1
+ module Visor
2
+ module Common
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,9 @@
1
+ $:.unshift File.expand_path('../../lib', __FILE__)
2
+ require 'common/exception'
3
+ require 'common/config'
4
+ require 'common/util'
5
+ require 'common/extensions/object'
6
+ require 'common/extensions/array'
7
+ require 'common/extensions/yaml'
8
+ require 'common/extensions/hash'
9
+ require 'common/extensions/logger'
@@ -0,0 +1,102 @@
1
+ require 'spec_helper'
2
+
3
+ module Visor::Common
4
+ describe Config do
5
+
6
+ let(:file) { File.join(File.expand_path('~/.visor'), Config::DEFAULT_CONFIG_FILE) }
7
+ let(:some_file) { File.expand_path('~/some_file') }
8
+ let(:invalid_file) { '~/some_invalid_file' }
9
+
10
+ let(:sample_conf) { {default:
11
+ {log_datetime_format: "%Y-%m-%d %H:%M:%S",
12
+ log_path: "~/.visor/logs"},
13
+ visor_meta:
14
+ {bind_host: "0.0.0.0", bind_port: 4567,
15
+ backend: "mongodb://:@127.0.0.1:27017/visor",
16
+ log: {file: "visor-meta.log", level: "DEBUG"}}} }
17
+
18
+ before(:all) do
19
+ unless File.exists?(file)
20
+ File.open(file, 'w') do |f|
21
+ f.write(sample_conf.stringify_keys.to_yaml)
22
+ end
23
+ end
24
+
25
+ File.open(some_file, 'w') do |f|
26
+ f.write(sample_conf.stringify_keys.to_yaml)
27
+ end
28
+
29
+ File.open(File.expand_path(invalid_file), 'w') do |f|
30
+ f.write('this will break YAML parsing')
31
+ end
32
+ end
33
+
34
+ after(:all) do
35
+ #File.delete(some_file, File.expand_path(invalid_file))
36
+ end
37
+
38
+ describe "#find_config_file" do
39
+ it "should find a configuration file in default dirs if it exists" do
40
+ Config.find_config_file.should == file
41
+ end
42
+
43
+ it "should get an existing config file as option" do
44
+ Config.find_config_file(some_file).should == some_file
45
+ end
46
+
47
+ it "should return nil if no configuration file found" do
48
+ Config.find_config_file('fake_file').should be_nil
49
+ end
50
+ end
51
+
52
+ describe "#load_config" do
53
+ it "should raise if there is no configuration files" do
54
+ lambda { Config.load_config(nil, invalid_file) }.should raise_exception
55
+ end
56
+
57
+ it "should return a valid non-empty hash" do
58
+ conf = Config.load_config
59
+ conf.should be_a Hash
60
+ conf.should_not be_empty
61
+ end
62
+
63
+ it "should append the configuration file full path to hash" do
64
+ conf = Config.load_config
65
+ conf[:file].should_not be_nil
66
+ File.exists?(conf[:file]).should be_true
67
+ end
68
+
69
+ it "should return scoped configuration" do
70
+ conf = Config.load_config :default
71
+ conf.keys.should == sample_conf[:default].keys << :file
72
+ end
73
+
74
+ it "should raise an exception if an error occurs during parsing" do
75
+ lambda { Config.load_config(nil, invalid_file) }.should raise_exception
76
+ end
77
+ end
78
+
79
+ describe "#build_logger" do
80
+ it "should return a Logger for a specific app" do
81
+ log = Config.build_logger :visor_meta
82
+ log.should be_a Logger
83
+ end
84
+
85
+ it "should raise an exception if an error occurs loading the config file" do
86
+ lambda { Config.build_logger :fake_scope }.should raise_exception
87
+ end
88
+
89
+ it "should set the log level if provided" do
90
+ log = Config.build_logger :visor_meta, sample_conf
91
+ log.level.should == Logger::DEBUG
92
+ end
93
+
94
+ it "should set the log level to the default if not provided" do
95
+ sample_conf[:visor_meta][:log].delete(:level)
96
+ log = Config.build_logger :visor_meta, sample_conf
97
+ log.level.should == Config::DEFAULT_LOG_LEVEL
98
+ end
99
+ end
100
+
101
+ end
102
+ end
@@ -0,0 +1,138 @@
1
+ require 'spec_helper'
2
+
3
+ module Visor::Common::Extensions
4
+ describe Hash do
5
+
6
+ let(:h_symbols) { {a: 1, b: 2, c: {d: 4, e: 5}} }
7
+ let(:h_strings) { {'a' => 1, 'b' => 2, 'c' => {'d' => 4, 'e' => 5}} }
8
+
9
+ let(:simple_hash) { {a: 1, b: 2, c: 3, d: 4, e: 5} }
10
+ let(:valid_keys) { [:a, :b, :c, :d, :e] }
11
+ let(:inclusion_keys) { [:a, :b, :c, :d, :e] }
12
+ let(:exclusion_keys) { [:f, :g] }
13
+
14
+ describe "#stringify_keys" do
15
+ it "should return a new hash" do
16
+ h_symbols.stringify_keys.should be_a Hash
17
+ end
18
+
19
+ it "should return the hash with all keys stringified" do
20
+ h_symbols.stringify_keys.keys.each { |k| k.should be_a String }
21
+ end
22
+
23
+ it "should succed at any hash depth" do
24
+ new = h_symbols.stringify_keys
25
+ new['c'].keys.each { |k| k.should be_a String }
26
+ end
27
+ end
28
+
29
+ describe "#stringify_keys!" do
30
+ it "should replace the hash with all keys stringified" do
31
+ h_symbols.stringify_keys!
32
+ h_symbols.keys.each { |k| k.should be_a String }
33
+ end
34
+
35
+ it "should succed at any hash depth" do
36
+ h_symbols.stringify_keys!
37
+ h_symbols['c'].keys.each { |k| k.should be_a String }
38
+ end
39
+ end
40
+
41
+ describe "#symbolize_keys" do
42
+ it "should return a new hash" do
43
+ h_strings.symbolize_keys.should be_a Hash
44
+ end
45
+
46
+ it "should return the hash with all keys symbolized" do
47
+ h_strings.symbolize_keys.keys.each { |k| k.should be_a Symbol }
48
+ end
49
+
50
+ it "should succed at any hash depth" do
51
+ new = h_strings.symbolize_keys
52
+ new[:c].keys.each { |k| k.should be_a Symbol }
53
+ end
54
+ end
55
+
56
+ describe "#symbolize_keys!" do
57
+ it "should replace the hash with all keys symbolized" do
58
+ h_strings.symbolize_keys!
59
+ h_strings.keys.each { |k| k.should be_a Symbol }
60
+ end
61
+
62
+ it "should succed at any hash depth" do
63
+ h_strings.symbolize_keys!
64
+ h_strings[:c].keys.each { |k| k.should be_a Symbol }
65
+ end
66
+ end
67
+
68
+ describe "#assert_valid_keys" do
69
+ it "should validate that all keys are valid" do
70
+ l = lambda { simple_hash.assert_valid_keys(simple_hash.keys) }
71
+ l.should_not raise_exception
72
+ end
73
+
74
+ it "should raise if any key is not valid" do
75
+ l = lambda { simple_hash.assert_valid_keys([:x, :y, :z]) }
76
+ l.should raise_exception ArgumentError
77
+ end
78
+ end
79
+
80
+ describe "#assert_inclusion_keys" do
81
+ it "should validate that all mandatory keys are present" do
82
+ l = lambda { simple_hash.assert_inclusion_keys(inclusion_keys) }
83
+ l.should_not raise_exception
84
+ end
85
+
86
+ it "should raise if any mandatory keys are not present" do
87
+ l = lambda { simple_hash.assert_inclusion_keys(inclusion_keys << :new_one) }
88
+ l.should raise_exception ArgumentError
89
+ end
90
+ end
91
+
92
+ describe "#assert_exclusion_keys" do
93
+ it "should validate that all exclusion keys are not present" do
94
+ l = lambda { simple_hash.assert_exclusion_keys(exclusion_keys) }
95
+ l.should_not raise_exception
96
+ end
97
+
98
+ it "should raise if any exclusion keys are present" do
99
+ l = lambda { simple_hash.assert_exclusion_keys(exclusion_keys << :a) }
100
+ l.should raise_exception ArgumentError
101
+ end
102
+ end
103
+
104
+ describe "#assert_valid_values_for" do
105
+ it "should validate that a given key has a given possible value" do
106
+ l = lambda { simple_hash.assert_valid_values_for(:a, [1, 2, 3]) }
107
+ l.should_not raise_exception
108
+ end
109
+
110
+ it "should raise if any exclusion keys are present" do
111
+ l = lambda { simple_hash.assert_valid_values_for(:a, [4, 5, 6]) }
112
+ l.should raise_exception ArgumentError
113
+ end
114
+ end
115
+
116
+ describe "#set_blank_keys_value_to" do
117
+ it "should fill blank/non existing keys to a given value" do
118
+ simple_hash.set_blank_keys_value_to([:x, :y, :z], [], 1)
119
+ [:x, :y, :z].each { |el| simple_hash[el].should == 1 }
120
+ end
121
+
122
+ it "should ignore some fields if asked to" do
123
+ simple_hash.set_blank_keys_value_to([:x, :y, :z], [:z], 1)
124
+ [:x, :y].each { |el| simple_hash[el].should == 1 }
125
+ simple_hash[:z].should be_nil
126
+ end
127
+ end
128
+
129
+ describe "#to_openstruct" do
130
+ it "should return an OpenStruct object from a hash one" do
131
+ os = simple_hash.to_openstruct
132
+ os.should be_a OpenStruct
133
+ os.a.should == simple_hash[:a]
134
+ end
135
+ end
136
+
137
+ end
138
+ end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+
3
+ module Visor::Common
4
+ describe Util do
5
+
6
+ let(:date) { Time.now }
7
+ let(:meta) { {name: 'util_spec', architecture: 'i386', access: 'public', created_at: date} }
8
+
9
+ let(:headers) { {'X_IMAGE_META_NAME' => 'util_spec',
10
+ 'X_IMAGE_META_ARCHITECTURE' => 'i386',
11
+ 'X_IMAGE_META_ACCESS' => 'public',
12
+ 'X_IMAGE_META_CREATED_AT' => date.to_s} }
13
+
14
+ describe "#push_meta_into_headers" do
15
+ it "should receive an hash and push it into another as HTTP headers" do
16
+ headers = Visor::Common::Util.push_meta_into_headers(meta)
17
+ headers.should be_a Hash
18
+ i = 0
19
+ headers.each do |k, v|
20
+ orig_key = meta.keys[i]
21
+ k.should == "x-image-meta-#{orig_key}"
22
+ v.should == meta[orig_key.to_sym].to_s
23
+ i+=1
24
+ end
25
+ end
26
+ end
27
+
28
+ describe "#push_meta_into_headers" do
29
+ it "should receive an hash and pull HTTP headers to a new hash" do
30
+ hash = Visor::Common::Util.pull_meta_from_headers(headers)
31
+ hash.should be_a Hash
32
+ hash.keys.should == meta.keys
33
+ end
34
+
35
+ it "should ignore non image meta headers" do
36
+ hash = Visor::Common::Util.pull_meta_from_headers(headers.merge('X_EXTRA' => 'value'))
37
+ hash.should_not include(:X_EXTRA)
38
+ end
39
+ end
40
+
41
+ end
42
+ end
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: visor-common
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - João Pereira
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-06-10 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: yard
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description: The VISOR Common System, a set of utility methods.
47
+ email: joaodrp@gmail.com
48
+ executables:
49
+ - visor-config
50
+ extensions: []
51
+ extra_rdoc_files: []
52
+ files:
53
+ - lib/common/config.rb
54
+ - lib/common/exception.rb
55
+ - lib/common/extensions/array.rb
56
+ - lib/common/extensions/hash.rb
57
+ - lib/common/extensions/logger.rb
58
+ - lib/common/extensions/object.rb
59
+ - lib/common/extensions/yaml.rb
60
+ - lib/common/util.rb
61
+ - lib/common/version.rb
62
+ - lib/visor-common.rb
63
+ - spec/lib/config_spec.rb
64
+ - spec/lib/extensions/hash_spec.rb
65
+ - spec/lib/util_spec.rb
66
+ - bin/visor-config
67
+ homepage: http://cvisor.org
68
+ licenses: []
69
+ post_install_message:
70
+ rdoc_options: []
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ! '>='
77
+ - !ruby/object:Gem::Version
78
+ version: 1.9.2
79
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
+ none: false
81
+ requirements:
82
+ - - ! '>='
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ requirements: []
86
+ rubyforge_project:
87
+ rubygems_version: 1.8.24
88
+ signing_key:
89
+ specification_version: 3
90
+ summary: ! 'VISOR: Virtual Images Management Service for Cloud Infrastructures'
91
+ test_files:
92
+ - spec/lib/config_spec.rb
93
+ - spec/lib/extensions/hash_spec.rb
94
+ - spec/lib/util_spec.rb
95
+ has_rdoc: