log2mail 0.0.1.pre3 → 0.0.1.pre4
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.
- checksums.yaml +4 -4
- data/{INSTALL → INSTALL.md} +2 -0
- data/README.md +1 -1
- data/features/log2mail_configurations/config_1 +1 -1
- data/lib/ext/string.rb +6 -0
- data/lib/ext/symbol.rb +7 -0
- data/lib/log2mail.rb +1 -1
- data/lib/log2mail/config.rb +9 -210
- data/lib/log2mail/config/attribute.rb +21 -0
- data/lib/log2mail/config/config.rb +45 -0
- data/lib/log2mail/config/config_file_handler.rb +142 -0
- data/lib/log2mail/config/config_file_snippet.rb +17 -0
- data/lib/log2mail/config/old_config_file_handler.rb +220 -0
- data/lib/log2mail/config/parser.rb +144 -0
- data/lib/log2mail/config/section.rb +70 -0
- data/lib/log2mail/console/commands.rb +1 -1
- data/lib/log2mail/main.rb +2 -2
- data/lib/log2mail/version.rb +1 -1
- data/lib/log2mail/watcher.rb +1 -1
- data/log2mail.gemspec +1 -0
- data/man/log2mail.1 +1 -1
- data/man/log2mail.1.ronn +1 -1
- data/spec/factories.rb +126 -29
- data/spec/log2mail/{config_spec.rb → config/config_file_handler_spec.rb} +49 -3
- data/spec/log2mail/config/parser_spec.rb +467 -0
- data/spec/spec_helper.rb +1 -0
- metadata +29 -5
@@ -42,7 +42,7 @@ module Log2mail
|
|
42
42
|
desc "show configuration"
|
43
43
|
def config
|
44
44
|
config_path = ask('configuration path? ').chomp
|
45
|
-
config = Log2mail::Config.parse_config config_path
|
45
|
+
config = Log2mail::Config::ConfigFileHandler.parse_config config_path
|
46
46
|
puts "Defaults:"
|
47
47
|
puts config.defaults
|
48
48
|
puts "Settings:"
|
data/lib/log2mail/main.rb
CHANGED
@@ -81,7 +81,7 @@ Main {
|
|
81
81
|
option('maxbufsize') { argument_required; cast :int; default 65536 }
|
82
82
|
def run
|
83
83
|
fail "Not starting. Daemon running." if daemon_running? and !params['nofork'].value
|
84
|
-
config = Log2mail::Config.parse_config @config_path
|
84
|
+
config = Log2mail::Config::ConfigFileHandler.parse_config @config_path
|
85
85
|
unless params['nofork'].value
|
86
86
|
Process.daemon
|
87
87
|
$PROGRAM_NAME = Log2mail::PROGNAME
|
@@ -133,7 +133,7 @@ Main {
|
|
133
133
|
mode 'configtest' do
|
134
134
|
option('effective', 'e') { description 'show effective settings' }
|
135
135
|
def run
|
136
|
-
config = Log2mail::Config.parse_config @config_path
|
136
|
+
config = Log2mail::Config::ConfigFileHandler.parse_config @config_path
|
137
137
|
puts config.formatted(params['effective'].value)
|
138
138
|
end
|
139
139
|
end
|
data/lib/log2mail/version.rb
CHANGED
data/lib/log2mail/watcher.rb
CHANGED
@@ -9,7 +9,7 @@ module Log2mail
|
|
9
9
|
# end
|
10
10
|
|
11
11
|
def initialize( config, sleeptime )
|
12
|
-
fail Error, 'Invalid configuration.' unless config.instance_of?(
|
12
|
+
fail Error, 'Invalid configuration.' unless config.instance_of?(Config::ConfigFileHandler)
|
13
13
|
@file_patterns = config.file_patterns
|
14
14
|
@files = @file_patterns.keys.map {|f| Log2mail::File.new(f, @file_patterns[f] ) }
|
15
15
|
@sleeptime = sleeptime
|
data/log2mail.gemspec
CHANGED
data/man/log2mail.1
CHANGED
@@ -13,7 +13,7 @@
|
|
13
13
|
\fBlog2mail\.rb\fR helps having an eye on your systems\' log files\. It efficiently monitors multiple files and reports as soon as specified (regular expression) patterns match\.
|
14
14
|
.
|
15
15
|
.P
|
16
|
-
On startup, \fBlog2mail\.rb\fR opens all files on the \'watch list\' and seeks to EOF\. All new data are parsed about once a minute (see \fB\-\-sleeptime\fR)\.
|
16
|
+
On startup, \fBlog2mail\.rb\fR opens all files on the \'watch list\' and seeks to EOF\. All new data are parsed about once a minute (see \fB\-\-sleeptime\fR)\. Matched patterns are reported to the configured mail address(es) (see \fBmailto\fR configuration option)\.
|
17
17
|
.
|
18
18
|
.P
|
19
19
|
Log files are reopened automatically when rotated\.
|
data/man/log2mail.1.ronn
CHANGED
@@ -9,7 +9,7 @@ log2mail.rb(1) -- monitors (log) files for patterns and reports hits by mail
|
|
9
9
|
|
10
10
|
`log2mail.rb` helps having an eye on your systems' log files. It efficiently monitors multiple files and reports as soon as specified (regular expression) patterns match.
|
11
11
|
|
12
|
-
On startup, `log2mail.rb` opens all files on the 'watch list' and seeks to EOF. All new data are parsed about once a minute (see `--sleeptime`).
|
12
|
+
On startup, `log2mail.rb` opens all files on the 'watch list' and seeks to EOF. All new data are parsed about once a minute (see `--sleeptime`). Matched patterns are reported to the configured mail address(es) (see `mailto` configuration option).
|
13
13
|
|
14
14
|
Log files are reopened automatically when rotated.
|
15
15
|
|
data/spec/factories.rb
CHANGED
@@ -1,51 +1,101 @@
|
|
1
1
|
require 'tempfile'
|
2
2
|
|
3
3
|
FactoryGirl.define do
|
4
|
-
factory :config, class: Log2mail::Config do
|
5
4
|
|
5
|
+
factory :raw_config, class: String do
|
6
6
|
trait :valid do
|
7
|
+
initialize_with{ new <<-CONFIG }
|
8
|
+
# sample config file for log2mail
|
9
|
+
# comments start with '#'
|
10
|
+
# see source code doc/Configuration for additional information
|
11
|
+
|
12
|
+
defaults
|
13
|
+
sendtime = 20
|
14
|
+
resendtime = 50
|
15
|
+
maxlines = 7
|
16
|
+
template = /tmp/mail_template
|
17
|
+
fromaddr = log2mail
|
18
|
+
sendmail = /usr/sbin/sendmail -oi -t
|
19
|
+
mailto = global_default_recipient@example.org # new in log2mail.rb
|
20
|
+
file = test.log
|
21
|
+
pattern = /any/
|
22
|
+
pattern = string match
|
23
|
+
mailto = special@recipient
|
24
|
+
maxlines = 99
|
25
|
+
CONFIG
|
26
|
+
end
|
27
|
+
|
28
|
+
trait :valid_without_defaults do
|
29
|
+
initialize_with{ new <<-CONFIG }
|
30
|
+
file = test.log
|
31
|
+
pattern = /any/
|
32
|
+
pattern = string match
|
33
|
+
mailto = special@recipient
|
34
|
+
CONFIG
|
35
|
+
end
|
36
|
+
|
37
|
+
trait :valid_global_mailto_attributes do
|
38
|
+
initialize_with{ new <<-CONFIG }
|
39
|
+
defaults
|
40
|
+
mailto = recipient@test.itstrauss.eu
|
41
|
+
fromaddr = log2mail
|
42
|
+
file = test.log
|
43
|
+
pattern = string pattern
|
44
|
+
pattern = /regexp pattern/
|
45
|
+
CONFIG
|
46
|
+
end
|
47
|
+
|
48
|
+
trait :valid_global_pattern_attributes do
|
49
|
+
initialize_with{ new <<-CONFIG }
|
50
|
+
defaults
|
51
|
+
pattern = recipient@test.itstrauss.eu
|
52
|
+
mailto = special recipient for pattern
|
53
|
+
file = test.log
|
54
|
+
CONFIG
|
55
|
+
end
|
56
|
+
|
57
|
+
factory :valid_raw_config, traits: [:valid]
|
58
|
+
factory :valid_raw_config_without_defaults, traits: [:valid_without_defaults]
|
59
|
+
factory :valid_raw_config_with_defaults_with_global_mailto_attributes, traits: [:valid_global_mailto_attributes]
|
60
|
+
factory :valid_raw_config_with_defaults_with_global_pattern_attributes, traits: [:valid_global_pattern_attributes]
|
61
|
+
end
|
62
|
+
|
63
|
+
factory :config, class: Log2mail::Config::ConfigFileHandler do
|
64
|
+
|
65
|
+
factory :valid_config do
|
7
66
|
initialize_with do
|
8
67
|
tmp = Tempfile.new('valid_config')
|
9
|
-
tmp.write
|
10
|
-
# sample config file for log2mail
|
11
|
-
# comments start with '#'
|
12
|
-
# see source code doc/Configuration for additional information
|
13
|
-
|
14
|
-
defaults
|
15
|
-
sendtime = 20
|
16
|
-
resendtime = 50
|
17
|
-
maxlines = 7
|
18
|
-
template = /tmp/mail_template
|
19
|
-
fromaddr = log2mail
|
20
|
-
sendmail = /usr/sbin/sendmail -oi -t
|
21
|
-
mailto = global_default_recipient@example.org # new in log2mail.rb
|
22
|
-
file = test.log
|
23
|
-
pattern = /any/
|
24
|
-
pattern = string match
|
25
|
-
mailto = special@recipient
|
26
|
-
maxlines = 99
|
27
|
-
CONFIG
|
68
|
+
tmp.write build(:valid_raw_config)
|
28
69
|
tmp.close
|
29
70
|
new(tmp.path)
|
30
71
|
end
|
31
72
|
end
|
32
73
|
|
33
|
-
|
74
|
+
factory :valid_config_without_defaults do
|
75
|
+
initialize_with do
|
76
|
+
tmp = Tempfile.new('valid_config')
|
77
|
+
tmp.write build(:valid_raw_config_without_defaults)
|
78
|
+
tmp.close
|
79
|
+
new(tmp.path)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
factory :valid_config_with_defaults_with_global_mailto_attributes do
|
83
|
+
initialize_with do
|
84
|
+
tmp = Tempfile.new('valid_config')
|
85
|
+
tmp.write build(:valid_raw_config_with_defaults_with_global_mailto_attributes)
|
86
|
+
tmp.close
|
87
|
+
new(tmp.path)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
factory :valid_config_with_defaults_with_global_pattern_attributes do
|
34
91
|
initialize_with do
|
35
92
|
tmp = Tempfile.new('valid_config')
|
36
|
-
tmp.write
|
37
|
-
file = test.log
|
38
|
-
pattern = /any/
|
39
|
-
pattern = string match
|
40
|
-
mailto = special@recipient
|
41
|
-
CONFIG
|
93
|
+
tmp.write build(:valid_raw_config_with_defaults_with_global_pattern_attributes)
|
42
94
|
tmp.close
|
43
95
|
new(tmp.path)
|
44
96
|
end
|
45
97
|
end
|
46
98
|
|
47
|
-
factory :valid_config, traits: [:valid]
|
48
|
-
factory :valid_config_without_defaults, traits: [:valid_without_defaults]
|
49
99
|
end
|
50
100
|
|
51
101
|
factory :hit, class: Log2mail::Hit do
|
@@ -98,4 +148,51 @@ culpa qui officia deserunt mollit anim id est laborum.
|
|
98
148
|
end
|
99
149
|
end
|
100
150
|
|
151
|
+
sequence :snippet do |n|
|
152
|
+
<<-TEXT
|
153
|
+
# this is file file#{n}
|
154
|
+
file = file#{n}
|
155
|
+
pattern = for file#{n}
|
156
|
+
mailto = for pattern for file#{n}
|
157
|
+
TEXT
|
158
|
+
end
|
159
|
+
|
160
|
+
sequence :filename do |n|
|
161
|
+
"config file #{n}"
|
162
|
+
end
|
163
|
+
|
164
|
+
|
165
|
+
factory :defaults_snippet, class: Log2mail::Config::ConfigFileSnippet do
|
166
|
+
filename = '/config/defaults'
|
167
|
+
snippet = <<-TEXT
|
168
|
+
# comment
|
169
|
+
defaults
|
170
|
+
mailto = global_default_recipient@example.org
|
171
|
+
TEXT
|
172
|
+
initialize_with do
|
173
|
+
new(snippet, filename)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
factory :just_comments_snippet, class: Log2mail::Config::ConfigFileSnippet do
|
178
|
+
filename = '/config/comments'
|
179
|
+
snippet = <<-TEXT
|
180
|
+
# comment
|
181
|
+
# other comment
|
182
|
+
# even more important comment
|
183
|
+
TEXT
|
184
|
+
initialize_with do
|
185
|
+
new(snippet, filename)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
factory :config_file_snippet, class: Log2mail::Config::ConfigFileSnippet do
|
190
|
+
snippet
|
191
|
+
filename
|
192
|
+
initialize_with do
|
193
|
+
new(snippet, filename)
|
194
|
+
end
|
195
|
+
|
196
|
+
end
|
197
|
+
|
101
198
|
end
|
@@ -1,9 +1,9 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'tempfile'
|
3
3
|
|
4
|
-
module Log2mail
|
4
|
+
module Log2mail::Config
|
5
5
|
|
6
|
-
describe
|
6
|
+
describe ConfigFileHandler do
|
7
7
|
|
8
8
|
subject { build(:valid_config) }
|
9
9
|
|
@@ -24,7 +24,7 @@ module Log2mail
|
|
24
24
|
:template => "/tmp/mail_template",
|
25
25
|
:fromaddr => "log2mail",
|
26
26
|
:sendmail => "/usr/sbin/sendmail -oi -t",
|
27
|
-
:mailtos =>
|
27
|
+
:mailtos => {"global_default_recipient@example.org"=>{}},
|
28
28
|
})
|
29
29
|
expect(subject.patterns_for_file('test.log')).to eql(["/any/", "string match"])
|
30
30
|
expect(subject.mailtos_for_pattern('test.log', 'string match')).to eql(["special@recipient"])
|
@@ -47,6 +47,22 @@ module Log2mail
|
|
47
47
|
end
|
48
48
|
end
|
49
49
|
|
50
|
+
context 'with defaults with attributes after a global mailto' do
|
51
|
+
subject { build(:valid_config_with_defaults_with_global_mailto_attributes) }
|
52
|
+
it 'should log a warning' do
|
53
|
+
expect($logger).to receive(:warn).with(/Attributes for a global default recipient specified/)
|
54
|
+
subject
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'with defaults with attributes after a global pattern' do
|
59
|
+
subject { build(:valid_config_with_defaults_with_global_pattern_attributes) }
|
60
|
+
it 'should log a warning' do
|
61
|
+
expect($logger).to receive(:warn).with(/Attributes for a global default pattern specified/)
|
62
|
+
subject
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
50
66
|
# it { is_expected.to eql build(:config) }
|
51
67
|
end
|
52
68
|
|
@@ -98,6 +114,36 @@ module Log2mail
|
|
98
114
|
end
|
99
115
|
end
|
100
116
|
|
117
|
+
describe '#raw.join' do
|
118
|
+
it 'should return the original cat-together config files' do
|
119
|
+
expect(subject.raw.join).to eql(<<-EOF)
|
120
|
+
# sample config file for log2mail
|
121
|
+
# comments start with '#'
|
122
|
+
# see source code doc/Configuration for additional information
|
123
|
+
|
124
|
+
defaults
|
125
|
+
sendtime = 20
|
126
|
+
resendtime = 50
|
127
|
+
maxlines = 7
|
128
|
+
template = /tmp/mail_template
|
129
|
+
fromaddr = log2mail
|
130
|
+
sendmail = /usr/sbin/sendmail -oi -t
|
131
|
+
mailto = global_default_recipient@example.org # new in log2mail.rb
|
132
|
+
file = test.log
|
133
|
+
pattern = /any/
|
134
|
+
pattern = string match
|
135
|
+
mailto = special@recipient
|
136
|
+
maxlines = 99
|
137
|
+
EOF
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
describe '#formatted' do
|
142
|
+
it 'should return a formatted output of the configuration' do
|
143
|
+
expect( subject.formatted ).to be_instance_of(Terminal::Table)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
101
147
|
end
|
102
148
|
|
103
149
|
end
|
@@ -0,0 +1,467 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'log2mail/config/parser'
|
3
|
+
require 'parslet/rig/rspec'
|
4
|
+
|
5
|
+
module Log2mail::Config
|
6
|
+
|
7
|
+
describe Parser do
|
8
|
+
|
9
|
+
describe 'parse_snippets' do
|
10
|
+
it 'should merge the results from multiple files' do
|
11
|
+
expect( subject.parse_snippets([
|
12
|
+
build(:defaults_snippet),
|
13
|
+
build(:config_file_snippet),
|
14
|
+
build(:just_comments_snippet),
|
15
|
+
build(:config_file_snippet)
|
16
|
+
]).tree ).to eq({
|
17
|
+
:defaults=>{:mailtos=>{"global_default_recipient@example.org"=>{}}},
|
18
|
+
:files=>{
|
19
|
+
"file1"=>{:patterns=>{"for file1"=>{:mailtos=>{"for pattern for file1"=>{}}}}},
|
20
|
+
"file2"=>{:patterns=>{"for file2"=>{:mailtos=>{"for pattern for file2"=>{}}}}},
|
21
|
+
}
|
22
|
+
})
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should correctly merge configurations for THE SAME file', :focus do
|
26
|
+
expect( subject.parse_snippets([
|
27
|
+
ConfigFileSnippet.new(<<-TEXT, 'configfile1'),
|
28
|
+
file = /file/path
|
29
|
+
pattern = pattern1
|
30
|
+
mailto = for pattern1
|
31
|
+
TEXT
|
32
|
+
ConfigFileSnippet.new(<<-TEXT, 'configfile2'),
|
33
|
+
file = /file/path
|
34
|
+
pattern = pattern2
|
35
|
+
mailto = for pattern2
|
36
|
+
TEXT
|
37
|
+
build(:config_file_snippet)
|
38
|
+
]).tree ).to eq({
|
39
|
+
:files=>{
|
40
|
+
"/file/path"=>{:patterns=>{
|
41
|
+
"pattern1"=>{:mailtos=>{"for pattern1"=>{}}},
|
42
|
+
"pattern2"=>{:mailtos=>{"for pattern2"=>{}}} }},
|
43
|
+
"file1"=>{:patterns=>{"for file1"=>{:mailtos=>{"for pattern for file1"=>{}}}}},
|
44
|
+
}
|
45
|
+
})
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should correctly merge configurations for THE SAME file' do
|
49
|
+
expect( subject.parse_snippets([
|
50
|
+
ConfigFileSnippet.new(<<-TEXT, 'configfile'),
|
51
|
+
file = /file/path
|
52
|
+
pattern = pattern1
|
53
|
+
mailto = for pattern1
|
54
|
+
# file = /file/path
|
55
|
+
pattern = pattern2
|
56
|
+
mailto = for pattern2
|
57
|
+
TEXT
|
58
|
+
build(:config_file_snippet)
|
59
|
+
]).tree ).to eq({
|
60
|
+
:files=>{
|
61
|
+
"/file/path"=>{:patterns=>{
|
62
|
+
"pattern1"=>{:mailtos=>{"for pattern1"=>{}}},
|
63
|
+
"pattern2"=>{:mailtos=>{"for pattern2"=>{}}} }},
|
64
|
+
"file1"=>{:patterns=>{"for file1"=>{:mailtos=>{"for pattern for file1"=>{}}}}},
|
65
|
+
}
|
66
|
+
})
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
describe 'invalid configurations should fail' do
|
72
|
+
it{ expect{ subject.parse_and_transform("defaults") }.to raise_error(Parslet::ParseFailed) }
|
73
|
+
it{ expect{ subject.parse_and_transform("file\n") }.to raise_error(Parslet::ParseFailed)}
|
74
|
+
it{ expect{ subject.parse_and_transform("invalid_attribute = some value\n") }.to raise_error(Parslet::ParseFailed)}
|
75
|
+
it{ expect{ subject.parse_and_transform("defaults\n invalid_attribute = some value\n") }.to raise_error(Parslet::ParseFailed)}
|
76
|
+
it{ expect{ subject.parse_and_transform("pattern = string pattern\n") }.to raise_error(Parslet::ParseFailed)}
|
77
|
+
it{ expect{ subject.parse_and_transform("pattern = /regexp pattern/\n") }.to raise_error(Parslet::ParseFailed)}
|
78
|
+
end
|
79
|
+
|
80
|
+
describe '#tree should build hash trees' do
|
81
|
+
it{ expect( subject.parse_and_transform("").tree ).to eq({}) }
|
82
|
+
it{ expect( subject.parse_and_transform("defaults\n").tree ).to eq(defaults: {}) }
|
83
|
+
it{ expect( subject.parse_and_transform("file = /some/where/on/the/path\n").tree ).to eq(files:{'/some/where/on/the/path'=>{}}) }
|
84
|
+
it{ expect( subject.parse_and_transform("file = /some/where/on/the/path\npattern = test pattern\n").tree ).to eq \
|
85
|
+
files:{'/some/where/on/the/path' => {patterns:{'test pattern'=>{}}}} }
|
86
|
+
it{ expect( subject.parse_and_transform("defaults\n fromaddr = some value\n").tree ).to eq(defaults:{fromaddr: 'some value'}) }
|
87
|
+
it{ expect( subject.parse_and_transform("defaults\n pattern = string pattern\n").tree ).to eq(defaults:{patterns:{'string pattern'=>{}}}) }
|
88
|
+
it{ expect( subject.parse_and_transform("defaults\n pattern = string pattern\n pattern = another pattern\n").tree ).to eq(defaults:{patterns:{'string pattern'=>{}, 'another pattern'=>{}}}) }
|
89
|
+
it{ expect( subject.parse_and_transform("defaults\n pattern = \"quoted string pattern \" # with comment\n").tree ).to eq(defaults:{patterns:{'quoted string pattern '=>{}}}) }
|
90
|
+
it{ expect( subject.parse_and_transform("defaults\n pattern = \"multiline\n\nquoted string pattern\"\n").tree ).to eq(defaults:{patterns:{"multiline\n\nquoted string pattern"=>{}}}) }
|
91
|
+
it{ expect( subject.parse_and_transform(['defaults', "\n", 'pattern = "escaped \\"quoted\\" string pattern"', "\n"].join('') ).tree ).to eq \
|
92
|
+
defaults:{patterns:{'escaped "quoted" string pattern'=>{}}} }
|
93
|
+
it{ expect( subject.parse_and_transform("defaults\n pattern = /regexp pattern/\n").tree ).to eq(defaults:{patterns:{'/regexp pattern/'=>{}}}) }
|
94
|
+
it{ expect( subject.parse_and_transform("defaults\nfile = bla\n").tree ).to eq(defaults: {}, files:{'bla'=>{}})}
|
95
|
+
|
96
|
+
it{ expect( subject.parse_and_transform("file = file one\npattern = pattern for file one\npattern = second pattern for file one\nfile = file two\npattern = pattern for file two\n").tree ).to eq ({
|
97
|
+
files:{
|
98
|
+
'file one' => { patterns:{'pattern for file one'=>{}, 'second pattern for file one'=>{}} },
|
99
|
+
'file two' => { patterns:{'pattern for file two'=>{}} },
|
100
|
+
}
|
101
|
+
})}
|
102
|
+
|
103
|
+
it{ expect( subject.parse_and_transform(build(:valid_raw_config)).tree ).to eq({
|
104
|
+
defaults:{
|
105
|
+
sendtime: 20,
|
106
|
+
resendtime: 50,
|
107
|
+
maxlines: 7,
|
108
|
+
template: '/tmp/mail_template',
|
109
|
+
fromaddr: 'log2mail',
|
110
|
+
sendmail: '/usr/sbin/sendmail -oi -t',
|
111
|
+
mailtos: {'global_default_recipient@example.org'=>{}}},
|
112
|
+
files:{
|
113
|
+
'test.log' => {
|
114
|
+
patterns:{
|
115
|
+
'/any/'=>{},
|
116
|
+
'string match'=>{ mailtos:{'special@recipient'=>{ maxlines: 99} } }
|
117
|
+
}
|
118
|
+
}
|
119
|
+
}
|
120
|
+
}) }
|
121
|
+
|
122
|
+
it{ expect( subject.parse_and_transform(build(:valid_raw_config_without_defaults)).tree ).to \
|
123
|
+
eq( files:{'test.log'=>{ patterns:{'/any/'=>{}, 'string match'=>{mailtos:{'special@recipient'=>{}}}} }} )}
|
124
|
+
end
|
125
|
+
|
126
|
+
it{ expect( subject.parse_and_transform("") ).to eq(Config.new) }
|
127
|
+
|
128
|
+
it{ expect( subject.parse_and_transform("#\n") ).to eq(Config.new) }
|
129
|
+
|
130
|
+
it{ expect{ subject.parse_and_transform("file = a file\n pattern = a pattern\n \# a comment\n") }.not_to raise_error }
|
131
|
+
it{ expect{ subject.parse_and_transform("defaults\n pattern = a pattern\n \# a comment\n") }.not_to raise_error }
|
132
|
+
|
133
|
+
it 'disallows file -> mailto' do
|
134
|
+
expect{ subject.parse_and_transform("file = a file\nmailto = a recipient\n")}.to \
|
135
|
+
raise_error Parslet::ParseFailed, /Extra input after last repetition at line 2 char 1/
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'allows file -> pattern' do
|
139
|
+
expect{ subject.parse_and_transform("file = a file\npattern = a pattern\n")}.not_to raise_error
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'allows file -> pattern -> mailto' do
|
143
|
+
expect{ subject.parse_and_transform("file = a file\npattern = a pattern\nmailto = a mailto\n")}.not_to raise_error
|
144
|
+
end
|
145
|
+
|
146
|
+
it 'allows file -> #comment -> pattern -> mailto' do
|
147
|
+
expect{ subject.parse_and_transform("file = a file\n\# a comment\npattern = a pattern\nmailto = a mailto\n")}.not_to raise_error
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'allows file -> pattern -> #comment -> mailto' do
|
151
|
+
expect{ subject.parse_and_transform("file = a file\npattern = a pattern\n\# a comment\nmailto = a mailto\n")}.not_to raise_error
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'allows defaults -> mailto' do
|
155
|
+
expect{ subject.parse_and_transform("defaults\nmailto = a global mailto\n")}.not_to raise_error
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'allows defaults -> mailto' do
|
159
|
+
expect{ subject.parse_and_transform("defaults\nmailto = a global mailto\n")}.not_to raise_error
|
160
|
+
end
|
161
|
+
|
162
|
+
it 'disallows mailto' do
|
163
|
+
expect{ subject.parse_and_transform("mailto = a recipient\n")}.to \
|
164
|
+
raise_error Parslet::ParseFailed, /Extra input after last repetition at line 1 char 1/
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'allows defaults -> #command -> mailto' do
|
168
|
+
expect{ subject.parse_and_transform("defaults\n\# a comment\nmailto = a global mailto\n")}.not_to raise_error
|
169
|
+
end
|
170
|
+
|
171
|
+
|
172
|
+
describe 'defaults -> mailto -> attribute' do
|
173
|
+
it{ expect{ subject.parse_and_transform("defaults\nmailto = a global mailto\nsendtime = a mailto setting\n")}.not_to raise_error }
|
174
|
+
it 'assigns attribute to the global mailto' do
|
175
|
+
expect( subject.parse_and_transform("defaults\nmailto = a global mailto\nsendtime = a mailto setting\n").tree).to eq(
|
176
|
+
{:defaults=>{:mailtos=>{"a global mailto"=>{:sendtime=>"a mailto setting"}} }})
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
|
181
|
+
describe 'defaults -> pattern -> attribute' do
|
182
|
+
it{ expect{ subject.parse_and_transform("defaults\npattern = a global pattern\nsendtime = a pattern setting\n")}.not_to raise_error }
|
183
|
+
it 'assigns attribute to the global pattern' do
|
184
|
+
expect( subject.parse_and_transform("defaults\npattern = a global pattern\nsendtime = a pattern setting\n").tree).to eq(
|
185
|
+
{:defaults=>{:patterns=>{"a global pattern"=>{:sendtime=>"a pattern setting"}} }})
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
it{ expect( subject.parse_and_transform("\n") ).to eq(Config.new) }
|
190
|
+
|
191
|
+
it{ expect( subject.parse_and_transform("defaults\n") ).to eq(Config.new([Section.new(:defaults)])) }
|
192
|
+
|
193
|
+
it{ expect( subject.parse_and_transform(" defaults \n") ).to eq(Config.new([Section.new(:defaults)])) }
|
194
|
+
|
195
|
+
it{ expect( subject.parse_and_transform("\# comment\n") ).to eq(Config.new) }
|
196
|
+
|
197
|
+
it{ expect( subject.parse_and_transform("defaults \# with comment\n") ).to eq(Config.new([Section.new(:defaults)])) }
|
198
|
+
|
199
|
+
it{ expect( subject.parse_and_transform("file = /some/where/on/the/path\n") ).to eq \
|
200
|
+
Config.new [ Section.new(:file, '/some/where/on/the/path') ] }
|
201
|
+
|
202
|
+
it{ expect( subject.parse_and_transform("file = /some/where/on/the/path\npattern = test pattern\n") ).to eq \
|
203
|
+
Config.new [ Section.new(:file, '/some/where/on/the/path', [Section.new(:pattern,'test pattern')]) ] }
|
204
|
+
|
205
|
+
it{ expect( subject.parse_and_transform("defaults\n fromaddr = some value\n") ).to eq \
|
206
|
+
Config.new [ Section.new(:defaults, nil, [Attribute.new(:fromaddr,'some value')]) ] }
|
207
|
+
|
208
|
+
it 'should collapse multiple default sections' do
|
209
|
+
expect( subject.parse_and_transform("defaults\n mailto=recipient@anywhere.com\ndefaults\n") ).to eq \
|
210
|
+
Config.new [ Section.new(:defaults, nil, [Section.new(:mailto,'recipient@anywhere.com')]) ]
|
211
|
+
end
|
212
|
+
|
213
|
+
it{ expect( subject.parse_and_transform("defaults\nfile = bla\n") ).to eq \
|
214
|
+
Config.new [ Section.new(:defaults,nil), Section.new(:file, 'bla') ] }
|
215
|
+
|
216
|
+
end
|
217
|
+
|
218
|
+
describe Transform do
|
219
|
+
it 'transforms quoted strings' do
|
220
|
+
expect( subject.apply( {:quoted_string=>"multiline\n\nquoted string pattern"} ) ).to eq("multiline\n\nquoted string pattern")
|
221
|
+
end
|
222
|
+
|
223
|
+
it 'transforms unquoted strings' do
|
224
|
+
expect( subject.apply( {:unquoted_string=>"a string with spaces at the end "} ) ).to eq('a string with spaces at the end')
|
225
|
+
end
|
226
|
+
|
227
|
+
it 'transforms integers' do
|
228
|
+
expect( subject.apply( {:integer=>"123"} ) ).to eq(123)
|
229
|
+
end
|
230
|
+
|
231
|
+
it 'transforms attribute names' do
|
232
|
+
INT_OPTIONS.each do |opt|
|
233
|
+
rnd_int = rand(2**31)
|
234
|
+
expect( subject.apply( {:attribute_name=>opt, :attribute_value=>{:integer=>rnd_int} } ) ).to eq(Attribute.new(opt, rnd_int))
|
235
|
+
end
|
236
|
+
STR_OPTIONS.each do |opt|
|
237
|
+
rnd_string = rand(36**10).to_s(36)
|
238
|
+
expect( subject.apply( {:attribute_name=>opt, :attribute_value=>rnd_string } ) ).to eq(Attribute.new(opt, rnd_string))
|
239
|
+
end
|
240
|
+
PATH_OPTIONS.each do |opt|
|
241
|
+
expect( subject.apply( {:attribute_name=>opt, :attribute_value=>'/a/path/string' } ) ).to eq(Attribute.new(opt, '/a/path/string'))
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
it 'transforms section names' do
|
246
|
+
expect( subject.apply( {:section_name=>'file', :section_value=>'/a/file/path' } ) ).to eq(Section.new('file', '/a/file/path'))
|
247
|
+
expect( subject.apply( {:section_name=>'defaults' } ) ).to eq(Section.new('defaults'))
|
248
|
+
end
|
249
|
+
|
250
|
+
it 'transforms single file section without attributes' do
|
251
|
+
expect( subject.apply({
|
252
|
+
section:[
|
253
|
+
{section_name: 'file', section_value: '/file/path'},
|
254
|
+
]
|
255
|
+
} ) ).to eq(
|
256
|
+
Section.new(:file, '/file/path' )
|
257
|
+
)
|
258
|
+
end
|
259
|
+
|
260
|
+
it 'transforms single file section with attributes' do
|
261
|
+
expect( subject.apply({
|
262
|
+
section:[
|
263
|
+
{section_name: 'file', section_value: '/file/path'},
|
264
|
+
{attribute_name: 'sendtime', attribute_value: '123'},
|
265
|
+
{attribute_name: 'fromaddr', attribute_value: 'sender@address'},
|
266
|
+
]
|
267
|
+
} ) ).to eq(
|
268
|
+
Section.new(:file, '/file/path', [Attribute.new(:sendtime,'123'), Attribute.new(:fromaddr,'sender@address')] )
|
269
|
+
)
|
270
|
+
end
|
271
|
+
|
272
|
+
it 'transforms simple (non-array) sections' do
|
273
|
+
expect( subject.apply(
|
274
|
+
{:section=>
|
275
|
+
{:section_name=>"pattern",
|
276
|
+
:section_value=>{:unquoted_string=>"pattern for file one"}}}
|
277
|
+
) ).to eq(
|
278
|
+
Section.new(:pattern, 'pattern for file one' )
|
279
|
+
)
|
280
|
+
end
|
281
|
+
|
282
|
+
it 'transforms single file section with subsections' do
|
283
|
+
transform = subject.apply(
|
284
|
+
{:section=>
|
285
|
+
[{:section_name=>"file",
|
286
|
+
:section_value=>{:unquoted_string=>"file one"}},
|
287
|
+
{:section=>
|
288
|
+
{:section_name=>"pattern",
|
289
|
+
:section_value=>{:unquoted_string=>"pattern for file one"}}},
|
290
|
+
{:section=>
|
291
|
+
{:section_name=>"pattern",
|
292
|
+
:section_value=>
|
293
|
+
{:unquoted_string=>"second pattern for file one"}}}]},
|
294
|
+
)
|
295
|
+
expect(transform).to eq(
|
296
|
+
Section.new(:file, 'file one', [
|
297
|
+
Section.new(:pattern, 'pattern for file one'),
|
298
|
+
Section.new(:pattern, 'second pattern for file one')] )
|
299
|
+
)
|
300
|
+
end
|
301
|
+
|
302
|
+
### undecided case
|
303
|
+
# it 'transforms multiple file sections' do
|
304
|
+
# expect( subject.apply({
|
305
|
+
# section:[
|
306
|
+
# {section_name: 'file', section_value: '/file/path1'},
|
307
|
+
# {section_name: 'file', section_value: '/file/path2'},
|
308
|
+
# ]
|
309
|
+
# } ) ).to eq({
|
310
|
+
# files: ['/file/path1', '/file/path2']
|
311
|
+
# })
|
312
|
+
# end
|
313
|
+
#
|
314
|
+
|
315
|
+
it 'transforms single defaults section' do
|
316
|
+
expect( subject.apply({
|
317
|
+
section:[
|
318
|
+
{section_name: 'defaults'},
|
319
|
+
]
|
320
|
+
} ) ).to eq( Section.new(:defaults) )
|
321
|
+
end
|
322
|
+
|
323
|
+
it 'transforms defaults section with attributes' do
|
324
|
+
expect( subject.apply({
|
325
|
+
section:[
|
326
|
+
{section_name: 'defaults'},
|
327
|
+
{attribute_name: 'sendtime', attribute_value: '123'},
|
328
|
+
{attribute_name: 'fromaddr', attribute_value: 'sender@address'},
|
329
|
+
]
|
330
|
+
} ) ).to eq(
|
331
|
+
Section.new(:defaults, nil, [Attribute.new(:sendtime,'123'), Attribute.new(:fromaddr, 'sender@address')])
|
332
|
+
)
|
333
|
+
end
|
334
|
+
|
335
|
+
|
336
|
+
it 'transforms trivial config' do
|
337
|
+
expect( subject.apply({
|
338
|
+
config:[1,2,3]
|
339
|
+
} ) ).to eq(
|
340
|
+
Log2mail::Config::Config.new([1,2,3])
|
341
|
+
)
|
342
|
+
end
|
343
|
+
|
344
|
+
it 'transforms defaults only config' do
|
345
|
+
expect( subject.apply({
|
346
|
+
config:[
|
347
|
+
section:[
|
348
|
+
{section_name: 'defaults'},
|
349
|
+
{attribute_name: 'sendtime', attribute_value: '123'},
|
350
|
+
{attribute_name: 'fromaddr', attribute_value: 'sender@address'},
|
351
|
+
]
|
352
|
+
]
|
353
|
+
} ) ).to eq(Config.new([
|
354
|
+
Section.new(:defaults, nil, [Attribute.new(:sendtime,'123'), Attribute.new(:fromaddr, 'sender@address')])
|
355
|
+
]))
|
356
|
+
end
|
357
|
+
|
358
|
+
it 'transforms file only config' do
|
359
|
+
expect( subject.apply({
|
360
|
+
config:[
|
361
|
+
section:[
|
362
|
+
{section_name: 'file', section_value: '/file/path1'},
|
363
|
+
{attribute_name: 'sendtime', attribute_value: '888'},
|
364
|
+
{attribute_name: 'fromaddr', attribute_value: 'other@address'},
|
365
|
+
],
|
366
|
+
]
|
367
|
+
} ) ).to eq(Config.new([
|
368
|
+
Section.new(:file, '/file/path1', [Attribute.new(:sendtime,'888'), Attribute.new(:fromaddr,'other@address')]),
|
369
|
+
]))
|
370
|
+
end
|
371
|
+
|
372
|
+
it 'transforms combined config' do
|
373
|
+
expect( subject.apply(
|
374
|
+
{:config=>
|
375
|
+
[{:section=>
|
376
|
+
[{:section_name=>"defaults"},
|
377
|
+
{:attribute_name=>"sendtime",
|
378
|
+
:attribute_value=>{:integer=>"20"}},
|
379
|
+
{:attribute_name=>"resendtime",
|
380
|
+
:attribute_value=>{:integer=>"50"}},
|
381
|
+
{:attribute_name=>"maxlines", :attribute_value=>{:integer=>"7"}},
|
382
|
+
{:attribute_name=>"template",
|
383
|
+
:attribute_value=>{:unquoted_string=>"/tmp/mail_template"}},
|
384
|
+
{:attribute_name=>"fromaddr",
|
385
|
+
:attribute_value=>{:unquoted_string=>"log2mail"}},
|
386
|
+
{:attribute_name=>"sendmail",
|
387
|
+
:attribute_value=>{:unquoted_string=>"/usr/sbin/sendmail -oi -t"}},
|
388
|
+
{:attribute_name=>"mailto",
|
389
|
+
:attribute_value=>
|
390
|
+
{:unquoted_string=>"global_default_recipient@example.org "}}]},
|
391
|
+
{:section=>
|
392
|
+
[{:section_name=>"file",
|
393
|
+
:section_value=>{:unquoted_string=>"test.log"}},
|
394
|
+
{:section=>
|
395
|
+
{:section_name=>"pattern",
|
396
|
+
:section_value=>{:unquoted_string=>"/any/"}}},
|
397
|
+
{:section=>
|
398
|
+
[{:section_name=>"pattern",
|
399
|
+
:section_value=>{:unquoted_string=>"string match"}},
|
400
|
+
{:section=>
|
401
|
+
[{:section_name=>"mailto",
|
402
|
+
:section_value=>{:unquoted_string=>"special@recipient"}},
|
403
|
+
{:attribute_name=>"maxlines",
|
404
|
+
:attribute_value=>{:integer=>"99"}}]}]}]}]}
|
405
|
+
)).to eq(
|
406
|
+
Log2mail::Config::Config.new([
|
407
|
+
Section.new(:defaults, nil, [
|
408
|
+
Attribute.new(:sendtime,20),
|
409
|
+
Attribute.new(:resendtime, 50),
|
410
|
+
Attribute.new(:maxlines, 7),
|
411
|
+
Attribute.new(:template, '/tmp/mail_template'),
|
412
|
+
Attribute.new(:fromaddr, 'log2mail'),
|
413
|
+
Attribute.new(:sendmail, '/usr/sbin/sendmail -oi -t'),
|
414
|
+
Attribute.new(:mailto, 'global_default_recipient@example.org')]),
|
415
|
+
Section.new(:file, 'test.log', [
|
416
|
+
Section.new(:pattern,'/any/'),
|
417
|
+
Section.new(:pattern, 'string match', [Section.new(:mailto, 'special@recipient', [Attribute.new(:maxlines, 99)])])])
|
418
|
+
])
|
419
|
+
)
|
420
|
+
end
|
421
|
+
|
422
|
+
it 'transforms combined config' do
|
423
|
+
expect( subject.apply({
|
424
|
+
config:[
|
425
|
+
{section:[
|
426
|
+
{section_name: 'defaults'},
|
427
|
+
{attribute_name: 'sendtime', attribute_value: '123'},
|
428
|
+
{attribute_name: 'fromaddr', attribute_value: 'sender@address'},
|
429
|
+
]},
|
430
|
+
{section:[
|
431
|
+
{section_name: 'file', section_value: '/file/path1'},
|
432
|
+
{attribute_name: 'sendtime', attribute_value: '888'},
|
433
|
+
{attribute_name: 'fromaddr', attribute_value: 'other@address'},
|
434
|
+
]},
|
435
|
+
{section:[
|
436
|
+
{section_name: 'file', section_value: '/file/path2'},
|
437
|
+
]}
|
438
|
+
]
|
439
|
+
} ) ).to eq(
|
440
|
+
Log2mail::Config::Config.new([
|
441
|
+
Section.new(:defaults, nil, [Attribute.new(:sendtime,'123'), Attribute.new(:fromaddr, 'sender@address')]),
|
442
|
+
Section.new(:file, '/file/path1', [Attribute.new(:sendtime,'888'), Attribute.new(:fromaddr,'other@address')]),
|
443
|
+
Section.new(:file, '/file/path2' )
|
444
|
+
])
|
445
|
+
)
|
446
|
+
end
|
447
|
+
|
448
|
+
it 'transforms combined SAME FILE config' do
|
449
|
+
expect( subject.apply({
|
450
|
+
config:[
|
451
|
+
{section:[
|
452
|
+
{section_name: 'file', section_value: '/the/file/path'},
|
453
|
+
{attribute_name: 'sendtime', attribute_value: '888'},
|
454
|
+
{attribute_name: 'fromaddr', attribute_value: 'other@address'},
|
455
|
+
]},
|
456
|
+
{section:[
|
457
|
+
{section_name: 'file', section_value: '/the/file/path'},
|
458
|
+
{attribute_name: 'sendtime', attribute_value: '111'},
|
459
|
+
]},
|
460
|
+
]
|
461
|
+
} ) ).to eq( #{:files => {"/the/file/path"=>{:sendtime=>"111", :fromaddr=>'other@address'}}})
|
462
|
+
Config.new([Section.new(:file, '/the/file/path', [Attribute.new(:sendtime,'111'), Attribute.new(:fromaddr,'other@address')]) ]))
|
463
|
+
end
|
464
|
+
|
465
|
+
end
|
466
|
+
|
467
|
+
end
|