nagira 0.2.5
Sign up to get free protection for your applications and to get access to all the features.
- data/History.md +89 -0
- data/Rakefile +128 -0
- data/bin/nagira +11 -0
- data/bin/nagira-setup +6 -0
- data/config/defaults.rb +74 -0
- data/config/environment.rb +44 -0
- data/config/nagira.defaults +66 -0
- data/config/nagira.init_d +133 -0
- data/lib/app.rb +330 -0
- data/lib/app/routes/get/config.rb +22 -0
- data/lib/app/routes/get/objects.rb +71 -0
- data/lib/app/routes/get/status.rb +153 -0
- data/lib/app/routes/put.rb +52 -0
- data/lib/app/routes/put/status.rb +139 -0
- data/lib/nagira.rb +55 -0
- data/lib/nagira/background_parse.rb +28 -0
- data/lib/nagira/nagios.rb +20 -0
- data/lib/nagira/timed_parse.rb +77 -0
- data/spec/00_configuration_spec.rb +62 -0
- data/spec/01_nagira_response_spec.rb +122 -0
- data/spec/02_0_status_spec.rb +53 -0
- data/spec/02_nagira_data_spec.rb +101 -0
- data/spec/03_api_spec.rb +48 -0
- data/spec/spec_helper.rb +4 -0
- data/test/benchmark.rb +26 -0
- data/test/data/bad/README +1 -0
- data/test/data/bad/nagios.cfg +1305 -0
- data/test/data/bad/objects.cache +1868 -0
- data/test/data/bad/status.dat +1766 -0
- data/test/data/json/GET.txt +15 -0
- data/test/data/json/README.txt +9 -0
- data/test/data/json/host_check.json +4 -0
- data/test/data/json/host_check.sh +4 -0
- data/test/data/json/ping.json +5 -0
- data/test/data/json/ping_and_http.json +12 -0
- data/test/data/json/ping_and_http_check.sh +4 -0
- data/test/data/json/ping_check.sh +4 -0
- data/test/data/nagios.cfg +1305 -0
- data/test/data/objects.cache +1868 -0
- data/test/data/status.dat +1652 -0
- data/version.txt +1 -0
- metadata +384 -0
@@ -0,0 +1,52 @@
|
|
1
|
+
class Nagira < Sinatra::Base
|
2
|
+
|
3
|
+
# @method parse_input_data
|
4
|
+
# @overload before("Parse PUT request body")
|
5
|
+
#
|
6
|
+
# Process the data before on each HTTP request.
|
7
|
+
#
|
8
|
+
# @return [Array] @input Sets @input instance variable.
|
9
|
+
#
|
10
|
+
before do
|
11
|
+
if request.put?
|
12
|
+
data = request.body.read
|
13
|
+
@input = case @format
|
14
|
+
when :json then JSON.parse data
|
15
|
+
when :xml then Hash.from_xml data
|
16
|
+
when :yaml then YAML.load data
|
17
|
+
end
|
18
|
+
# Make sure we always return an Array
|
19
|
+
@input = [@input] if @input.is_a? Hash
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Define helpers for put methods
|
24
|
+
helpers do
|
25
|
+
|
26
|
+
# Helper to send PUT update to Nagios::ExternalCommands
|
27
|
+
#
|
28
|
+
# @param [Hash] params
|
29
|
+
# @param [Symbol] action Nagios external command name
|
30
|
+
#
|
31
|
+
# FIXME: This only accepts single service. Modify to use Arrays too
|
32
|
+
def put_update action, params
|
33
|
+
res = $nagios[:commands].write(params.merge({ :action => action }))
|
34
|
+
{ :status => res[:result], :object => res[:data]}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Small helper to submit data to ::Nagios::ExternalCommands
|
39
|
+
# object. For status updates sends external coond via
|
40
|
+
# ::Nagios::ExternalCommands.send method.
|
41
|
+
def update_service_status params
|
42
|
+
put_update :PROCESS_SERVICE_CHECK_RESULT, params
|
43
|
+
end
|
44
|
+
|
45
|
+
# Small helper to submit data to ::Nagios::ExternalCommands
|
46
|
+
# object. For host status updates sends external command via
|
47
|
+
# ::Nagios::ExternalCommands.write method.
|
48
|
+
def update_host_status params
|
49
|
+
put_update :PROCESS_HOST_CHECK_RESULT, params
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
class Nagira < Sinatra::Base
|
2
|
+
|
3
|
+
|
4
|
+
# @method put_status
|
5
|
+
# @overload put("/_status")
|
6
|
+
#
|
7
|
+
# Submit JSON Hash for multiple services, on multiple hosts.
|
8
|
+
put "/_status" do
|
9
|
+
"TODO: Not implemented"
|
10
|
+
end
|
11
|
+
|
12
|
+
# @method put_status_host_name
|
13
|
+
# @overload put("/_status/:host_name")
|
14
|
+
#
|
15
|
+
# Update hoststatus information only for the given host. URL
|
16
|
+
# hostname always override hostname given in the JSON file.
|
17
|
+
#
|
18
|
+
# == Example
|
19
|
+
#
|
20
|
+
# $ curl -i -H "Accept: application/json" -d @host.json -X
|
21
|
+
# PUT http://localhost:4567/_status/svaroh
|
22
|
+
#
|
23
|
+
# => {"status": true, "object": [{"data": {"host_name":"svaroh",
|
24
|
+
# "status_code": "0", "plugin_output": "ping OK", "action":
|
25
|
+
# "PROCESS_HOST_CHECK_RESULT"}, "result":true, "messages": []}]}
|
26
|
+
#
|
27
|
+
# == Example JSON
|
28
|
+
#
|
29
|
+
# {
|
30
|
+
# "status_code":"0",
|
31
|
+
# "plugin_output" : "ping OK"
|
32
|
+
# }
|
33
|
+
put "/_status/:host_name" do
|
34
|
+
@data = update_host_status @input.first.merge({
|
35
|
+
'host_name' => params['host_name']
|
36
|
+
})
|
37
|
+
nil
|
38
|
+
end
|
39
|
+
|
40
|
+
# Same as /_status/:host_name (Not implemented)
|
41
|
+
#
|
42
|
+
# @method put__host_status_host_name
|
43
|
+
# @overload put("/_host_status/:host_name")
|
44
|
+
#
|
45
|
+
put "/_host_status/:host_name" do
|
46
|
+
"Not implemented: TODO"
|
47
|
+
end
|
48
|
+
|
49
|
+
# @method put_status_host_name_services
|
50
|
+
# @overload put("/_status/:host_name/_services")
|
51
|
+
#
|
52
|
+
# Update multiple services on the same host.
|
53
|
+
#
|
54
|
+
# Hostname from URL always overrides host_name if it's is provided
|
55
|
+
# in the JSON data.
|
56
|
+
#
|
57
|
+
# == Example return JSON data
|
58
|
+
#
|
59
|
+
# $ curl -i -H "Accept: application/json" -d @dat_m.json -X PUT
|
60
|
+
# http://localhost:4567/_status/svaroh/_services
|
61
|
+
#
|
62
|
+
# [{"status":true, "object":[{"data":{"host_name":"svaroh",
|
63
|
+
# "service_description":"PING", "return_code":"0",
|
64
|
+
# "plugin_output":"OK",
|
65
|
+
# "action":"PROCESS_SERVICE_CHECK_RESULT"}, "result":true,
|
66
|
+
# "messages":[]}]}, {"status":true,
|
67
|
+
# "object":[{"data":{"host_name":"svaroh",
|
68
|
+
# "service_description":"Apache", "return_code":"r20",
|
69
|
+
# "plugin_output":"cwfailedOK",
|
70
|
+
# "action":"PROCESS_SERVICE_CHECK_RESULT"}, "result":true,
|
71
|
+
# "messages":[]}]}]%
|
72
|
+
#
|
73
|
+
# == Example JSON for submit
|
74
|
+
#
|
75
|
+
# All attributes provided in the example below are requried for host
|
76
|
+
# service status information:
|
77
|
+
# - host_name
|
78
|
+
# - service_description
|
79
|
+
# - return_code
|
80
|
+
# - plugin_output
|
81
|
+
#
|
82
|
+
#
|
83
|
+
# [{ "host_name":"viy",
|
84
|
+
# "service_description":"PING",
|
85
|
+
# "return_code":"0",
|
86
|
+
# "plugin_output":"64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.046 ms "
|
87
|
+
# },
|
88
|
+
#
|
89
|
+
# {"host_name":"svaroh",
|
90
|
+
# "service_description":"Apache",
|
91
|
+
# "return_code":"2",
|
92
|
+
# "plugin_output":"HTTP GEt failed"
|
93
|
+
# }
|
94
|
+
# ]
|
95
|
+
#
|
96
|
+
#
|
97
|
+
put "/_status/:host_name/_services" do
|
98
|
+
|
99
|
+
data = []
|
100
|
+
@input.each do |datum|
|
101
|
+
# FIXME - this calls update for each service. Should be batching them together
|
102
|
+
data << update_service_status(
|
103
|
+
datum.merge({
|
104
|
+
'host_name' => params['host_name']
|
105
|
+
})
|
106
|
+
)
|
107
|
+
end
|
108
|
+
@data = data
|
109
|
+
nil
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
# Update single service on a single host by JSON data.
|
114
|
+
put "/_status/:host_name/_services/:service_description" do
|
115
|
+
@data = update_service_status \
|
116
|
+
@input.first.merge({
|
117
|
+
'service_description' => params['service_description'],
|
118
|
+
'host_name' => params['host_name']
|
119
|
+
})
|
120
|
+
nil
|
121
|
+
end
|
122
|
+
|
123
|
+
# @method put_status_as_http_parms
|
124
|
+
# @overload put(/_status/:host_name/_services/:service_description/_return_code/:return_code/_plugin_output/:plugin_output)
|
125
|
+
#
|
126
|
+
# Update single service status on a single host. Use data provided
|
127
|
+
# in RESTful path as parameters.
|
128
|
+
#
|
129
|
+
# == Example
|
130
|
+
# curl -d "test data" \
|
131
|
+
# -X PUT http://localhost:4567/_status/viy/_services/PING/_return_code/0/_plugin_output/OK
|
132
|
+
# # => ok
|
133
|
+
put "/_status/:host_name/_services/:service_description/_return_code/:return_code/_plugin_output/:plugin_output" do
|
134
|
+
|
135
|
+
@data = update_service_status params
|
136
|
+
nil
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
data/lib/nagira.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'active_model/serialization'
|
2
|
+
require 'active_model/serializers/xml' # for Hash.to_xml
|
3
|
+
|
4
|
+
require 'active_support/inflector'
|
5
|
+
require 'active_support/inflector/inflections'
|
6
|
+
require 'active_support/core_ext/hash/slice' # for Hash.slice
|
7
|
+
|
8
|
+
require 'json'
|
9
|
+
require 'yaml'
|
10
|
+
require 'sinatra'
|
11
|
+
require 'sinatra/reloader'
|
12
|
+
|
13
|
+
$: << File.dirname(__FILE__) << File.dirname(File.dirname(__FILE__))
|
14
|
+
|
15
|
+
require 'config/defaults'
|
16
|
+
|
17
|
+
require "app/routes/get/config"
|
18
|
+
require "app/routes/get/objects"
|
19
|
+
require "app/routes/get/status"
|
20
|
+
|
21
|
+
require "app/routes/put"
|
22
|
+
require "app/routes/put/status"
|
23
|
+
|
24
|
+
|
25
|
+
#
|
26
|
+
# environment file must go after default, some settings override
|
27
|
+
# defaults.
|
28
|
+
#
|
29
|
+
require 'config/environment'
|
30
|
+
require 'nagira/nagios'
|
31
|
+
|
32
|
+
class Nagira < Sinatra::Base
|
33
|
+
|
34
|
+
VERSION = File.read(File.expand_path(File.dirname(__FILE__)) + '/../version.txt').strip
|
35
|
+
GITHUB = "http://dmytro.github.com/nagira/"
|
36
|
+
|
37
|
+
# Get all routes that Nagira provides.
|
38
|
+
def api
|
39
|
+
api = { }
|
40
|
+
|
41
|
+
param_regex = Regexp.new '\(\[\^\\\\\/\?\#\]\+\)'
|
42
|
+
Nagira.routes.keys.each do |method|
|
43
|
+
api[method] ||= []
|
44
|
+
Nagira.routes[method].each do |r|
|
45
|
+
path = r[0].inspect[3..-3]
|
46
|
+
r[1].each do |parm|
|
47
|
+
path.sub!(param_regex,":#{parm}")
|
48
|
+
end
|
49
|
+
path.gsub!('\\','')
|
50
|
+
api[method] << path unless path.empty?
|
51
|
+
end
|
52
|
+
end
|
53
|
+
api
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'nagios'
|
2
|
+
|
3
|
+
module Nagios
|
4
|
+
##
|
5
|
+
# Background parsing of status.dat file in separate thread. Runs on
|
6
|
+
# regular intervals slightly shorter than :ttl
|
7
|
+
#
|
8
|
+
class BackgroundParser
|
9
|
+
|
10
|
+
##
|
11
|
+
#
|
12
|
+
# If :ttl is not defined set to 0 and do not run
|
13
|
+
# background parsing.
|
14
|
+
#
|
15
|
+
def initialize
|
16
|
+
interval = [::DEFAULT[:ttl],1].max || nil
|
17
|
+
if interval && ::DEFAULT[:start_background_parser]
|
18
|
+
puts "[#{Time.now}] Starting background parser thread with interval #{interval} sec"
|
19
|
+
$bg = Thread.new {
|
20
|
+
loop {
|
21
|
+
$nagios[:status].parse
|
22
|
+
sleep interval
|
23
|
+
} #loop
|
24
|
+
} # thread
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Nagios
|
2
|
+
|
3
|
+
require 'nagira/timed_parse'
|
4
|
+
require 'nagira/background_parse'
|
5
|
+
|
6
|
+
# Extensions to Nagios::Status and Objects classes for use with
|
7
|
+
# Nagira: keep track of file modification times and parse only
|
8
|
+
# changed files.
|
9
|
+
class Config
|
10
|
+
include Nagios::TimedParse
|
11
|
+
end
|
12
|
+
|
13
|
+
class Status
|
14
|
+
include Nagios::TimedParse
|
15
|
+
end
|
16
|
+
|
17
|
+
class Nagios::Objects
|
18
|
+
include Nagios::TimedParse
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'nagios'
|
2
|
+
|
3
|
+
module Nagios
|
4
|
+
# Keep track of last parsed time and last changed time of the
|
5
|
+
# status/cache file to avoid parsing on each HTTP request.
|
6
|
+
module TimedParse
|
7
|
+
|
8
|
+
# Set some minimum interval for re-parsing of the status file:
|
9
|
+
# even if file changes often, we do not want to parse it more
|
10
|
+
# often, then this number of seconds.
|
11
|
+
|
12
|
+
TTL = ::DEFAULT[:ttl] || 0
|
13
|
+
|
14
|
+
# Override constructor and parse method from ::Nagios::Objects or
|
15
|
+
# ::Nagios::Status classes, add instance variables to handle
|
16
|
+
# modification and parseing times of status file.
|
17
|
+
# Original methods are aliased:
|
18
|
+
# - initialize -> constructor
|
19
|
+
# - parse -> parse!
|
20
|
+
# See also
|
21
|
+
# http://www.ruby-forum.com/topic/969161
|
22
|
+
def self.included(base)
|
23
|
+
base.class_eval do
|
24
|
+
alias_method :parse!, :parse
|
25
|
+
alias_method :constructor, :initialize
|
26
|
+
|
27
|
+
# @method initialize
|
28
|
+
# Extend current constructor with some additional data to
|
29
|
+
# track file change time
|
30
|
+
#
|
31
|
+
# @param [String] file Path to status file
|
32
|
+
# @param [Fixnum] parse_interval Number of seconds between
|
33
|
+
# re-parsing of the file
|
34
|
+
def initialize(file, parse_interval=TTL)
|
35
|
+
constructor(file)
|
36
|
+
|
37
|
+
# Time when status file was last time parsed, set it to 0 secs
|
38
|
+
# epoch to make sure it will be parsed
|
39
|
+
@last_parsed = Time.at(0)
|
40
|
+
|
41
|
+
# Last time file was changed
|
42
|
+
@last_changed = File.mtime(@path)
|
43
|
+
@parse_interval = parse_interval
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
# Extend original parse method: parse file only if it needs
|
48
|
+
# parsing and set time of parser run to current time.
|
49
|
+
def parse
|
50
|
+
if need_parsing?
|
51
|
+
parse!
|
52
|
+
@last_parsed = Time.now
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
attr_accessor :last_parsed, :parse_interval
|
59
|
+
|
60
|
+
def last_changed
|
61
|
+
@last_changed = File.mtime(@path)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Return true if file is changed since it was parsed last time
|
65
|
+
def changed?
|
66
|
+
self.last_changed > self.last_parsed
|
67
|
+
end
|
68
|
+
|
69
|
+
# Check if:
|
70
|
+
# - file changed?
|
71
|
+
# - was it parsed recently?
|
72
|
+
def need_parsing?
|
73
|
+
changed? && ((Time.now - self.last_parsed) > @parse_interval)
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Configuration" do
|
4
|
+
|
5
|
+
set :environment, ENV['RACK_ENV'] || :test
|
6
|
+
|
7
|
+
before {
|
8
|
+
@cfg = Nagios::Config.new(Nagira.settings.nagios_cfg)
|
9
|
+
}
|
10
|
+
|
11
|
+
context "nagios.cfg" do
|
12
|
+
|
13
|
+
it { File.should exist @cfg.path }
|
14
|
+
|
15
|
+
it "should be parseable" do
|
16
|
+
lambda { @cfg.parse }.should_not raise_error
|
17
|
+
end
|
18
|
+
|
19
|
+
context "parsing nagios.cfg file" do
|
20
|
+
|
21
|
+
before { @cfg.parse }
|
22
|
+
|
23
|
+
it "should have PATH to objects file" do
|
24
|
+
@cfg.object_cache_file.should be_a_kind_of String
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should have PATH to status file" do
|
28
|
+
@cfg.status_file.should be_a_kind_of String
|
29
|
+
end
|
30
|
+
|
31
|
+
end # parsing nagios.cfg file
|
32
|
+
end # nagios.cfg
|
33
|
+
|
34
|
+
context "data files" do
|
35
|
+
before { @cfg.parse }
|
36
|
+
|
37
|
+
context Nagios::Status do
|
38
|
+
|
39
|
+
subject { Nagios::Status.new( Nagira.settings.status_cfg || @cfg.status_file ) }
|
40
|
+
|
41
|
+
it { File.should exist( subject.path ) }
|
42
|
+
|
43
|
+
it "should be parseable" do
|
44
|
+
lambda { subject.parse }.should_not raise_error
|
45
|
+
end
|
46
|
+
end # Nagios::Status
|
47
|
+
|
48
|
+
|
49
|
+
context Nagios::Objects do
|
50
|
+
|
51
|
+
subject { Nagios::Objects.new( Nagira.settings.objects_cfg || @cfg.object_cache_file) }
|
52
|
+
|
53
|
+
it { File.should exist subject.path }
|
54
|
+
|
55
|
+
it "should be parseable" do
|
56
|
+
lambda { subject.parse }.should_not raise_error
|
57
|
+
end
|
58
|
+
end # Nagios::Objects
|
59
|
+
|
60
|
+
end # data files
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Nagira do
|
4
|
+
|
5
|
+
set :environment, ENV['RACK_ENV'] || :test
|
6
|
+
|
7
|
+
include Rack::Test::Methods
|
8
|
+
|
9
|
+
def app
|
10
|
+
@app ||= Nagira
|
11
|
+
end
|
12
|
+
|
13
|
+
TOP_PAGES = %w{ _config _objects _status _api }
|
14
|
+
FORMATS = %w{ xml yaml json}
|
15
|
+
DEFAULT_FORMAT = ::Nagira.settings.format
|
16
|
+
TYPES = %w{state list}
|
17
|
+
|
18
|
+
context "simple page load" do
|
19
|
+
TOP_PAGES.each do |page|
|
20
|
+
it "/#{page} should load" do
|
21
|
+
get "/#{page}"
|
22
|
+
last_response.should be_ok
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
# Check 3 different formats
|
27
|
+
FORMATS.each do |format|
|
28
|
+
|
29
|
+
it "should load '#{page}.#{format}' page" do
|
30
|
+
get "/#{page}.#{format}"
|
31
|
+
last_response.should be_ok
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context "data format check" do
|
39
|
+
|
40
|
+
TOP_PAGES.each do |page|
|
41
|
+
context page do
|
42
|
+
|
43
|
+
FORMATS.each do |format|
|
44
|
+
|
45
|
+
context format do
|
46
|
+
|
47
|
+
before do
|
48
|
+
get "/#{page}.#{format}"
|
49
|
+
@header = last_response.header
|
50
|
+
@body = last_response.body
|
51
|
+
end
|
52
|
+
|
53
|
+
it "Content-type: application/#{format}" do
|
54
|
+
@header['Content-Type'].should =~ /^application\/#{format}.*/
|
55
|
+
end
|
56
|
+
|
57
|
+
it "body should have content" do
|
58
|
+
@body.should_not be_empty
|
59
|
+
end
|
60
|
+
|
61
|
+
it "#{format} should be parseable" do
|
62
|
+
case format
|
63
|
+
when 'json'
|
64
|
+
lambda { JSON.parse @body }.should_not raise_error
|
65
|
+
when 'xml'
|
66
|
+
lambda { Hash.from_xml @body }.should_not raise_error
|
67
|
+
when 'yaml'
|
68
|
+
lambda { YAML.load @body }.should_not raise_error
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
context 'default format' do
|
76
|
+
it "/#{page}.#{Nagira.settings.format} response should be the same as /#{page}" do
|
77
|
+
get "/#{page}.#{Nagira.settings.format}"
|
78
|
+
a = last_response.body
|
79
|
+
get "/#{page}"
|
80
|
+
b = last_response.body
|
81
|
+
a.should eq b
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
#
|
89
|
+
# GET /config
|
90
|
+
# ----------------------------------------
|
91
|
+
context "/config" do
|
92
|
+
|
93
|
+
before do
|
94
|
+
get "/_config.json"
|
95
|
+
@data = JSON.parse last_response.body
|
96
|
+
end
|
97
|
+
|
98
|
+
context "important items in Nagios configuration" do
|
99
|
+
|
100
|
+
# Configuration strings
|
101
|
+
|
102
|
+
%w{ log_file object_cache_file resource_file status_file
|
103
|
+
nagios_user nagios_group }.each do |key|
|
104
|
+
|
105
|
+
it "attribute #{key} should be a String" do
|
106
|
+
@data[key].should be_a_kind_of String
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Congiration arrays
|
111
|
+
%w{cfg_file cfg_dir}.each do |key|
|
112
|
+
it "attribute #{key} should be an Array" do
|
113
|
+
@data[key].should be_a_kind_of Array
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
end # /config
|
119
|
+
|
120
|
+
|
121
|
+
end
|
122
|
+
end
|