visor-common 0.0.1

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/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: