langrove 0.0.2

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.
Files changed (62) hide show
  1. data/.rspec +1 -0
  2. data/.rvmrc +62 -0
  3. data/.watchr +27 -0
  4. data/Gemfile +16 -0
  5. data/Gemfile.lock +61 -0
  6. data/Rakefile +24 -0
  7. data/functional/.gitignore +1 -0
  8. data/functional/bin/datagram +6 -0
  9. data/functional/config/.gitignore +0 -0
  10. data/functional/config/boot.rb +64 -0
  11. data/functional/config/daemons.yml +12 -0
  12. data/functional/config/environment.rb +28 -0
  13. data/functional/config/environments/development.rb +0 -0
  14. data/functional/config/environments/production.rb +0 -0
  15. data/functional/config/environments/test.rb +0 -0
  16. data/functional/lib/daemon/datagram.rb +21 -0
  17. data/functional/lib/handler/socket_to_file.rb +36 -0
  18. data/functional/lib/protocol/socket_to_file.rb +55 -0
  19. data/functional/libexec/daemon.rb +68 -0
  20. data/functional/log/.gitignore +3 -0
  21. data/functional/tmp/README +1 -0
  22. data/lib/langrove/_base.rb +26 -0
  23. data/lib/langrove/adaptor/base.rb +3 -0
  24. data/lib/langrove/adaptor/datagram.rb +27 -0
  25. data/lib/langrove/adaptor_base.rb +89 -0
  26. data/lib/langrove/client/base.rb +2 -0
  27. data/lib/langrove/client/datagram.rb +25 -0
  28. data/lib/langrove/client_base.rb +114 -0
  29. data/lib/langrove/daemon/base.rb +2 -0
  30. data/lib/langrove/daemon_base.rb +175 -0
  31. data/lib/langrove/ext/class_loader.rb +148 -0
  32. data/lib/langrove/ext/config_item.rb +34 -0
  33. data/lib/langrove/ext/config_loader.rb +16 -0
  34. data/lib/langrove/ext/fake_logger.rb +8 -0
  35. data/lib/langrove/ext/persistable.rb +103 -0
  36. data/lib/langrove/ext/string.rb +35 -0
  37. data/lib/langrove/ext.rb +7 -0
  38. data/lib/langrove/handler/base.rb +2 -0
  39. data/lib/langrove/handler_base.rb +141 -0
  40. data/lib/langrove/protocol/base.rb +2 -0
  41. data/lib/langrove/protocol/syslog.rb +32 -0
  42. data/lib/langrove/protocol_base.rb +32 -0
  43. data/lib/langrove/version.rb +3 -0
  44. data/lib/langrove.rb +1 -0
  45. data/spec/functional/daemon/datagram_spec.rb +115 -0
  46. data/spec/langrove/adaptor/datagram_spec.rb +6 -0
  47. data/spec/langrove/adaptor_base_spec.rb +48 -0
  48. data/spec/langrove/client/datagram_spec.rb +1 -0
  49. data/spec/langrove/client_base_spec.rb +5 -0
  50. data/spec/langrove/daemon_base_spec.rb +101 -0
  51. data/spec/langrove/ext/class_loader_spec.rb +83 -0
  52. data/spec/langrove/ext/config_item_spec.rb +81 -0
  53. data/spec/langrove/ext/config_loader_spec.rb +5 -0
  54. data/spec/langrove/ext/fake_logger_spec.rb +0 -0
  55. data/spec/langrove/ext/persistable_spec.rb +117 -0
  56. data/spec/langrove/ext/string_spec.rb +16 -0
  57. data/spec/langrove/handler_base_spec.rb +57 -0
  58. data/spec/langrove/protocol/syslog_spec.rb +45 -0
  59. data/spec/langrove/protocol_base_spec.rb +6 -0
  60. data/spec/todo_spec.rb +12 -0
  61. data/tmp/README +2 -0
  62. metadata +200 -0
@@ -0,0 +1,141 @@
1
+ require 'langrove'
2
+
3
+ module LanGrove::Handler class Base
4
+
5
+ #
6
+ # One handler exists in the daemon with
7
+ # the primary purpose of housing through
8
+ # an addressable Hash the set of connected
9
+ # clients,
10
+ #
11
+ # In this:
12
+ #
13
+ # but, NOT YET
14
+ #
15
+ attr_writer :clients
16
+
17
+ #
18
+ # The protocol defininition per the config is
19
+ # loaded by this Handler, but not instanciated.
20
+ #
21
+ # This definition is then exposed,
22
+ #
23
+ # By this:
24
+ #
25
+ attr_reader :protocol
26
+
27
+ def daemon_start
28
+
29
+ #
30
+ # OVERRIDE
31
+ #
32
+ # To prepare the Handler in any necessary
33
+ # ways before the thread is handed over to
34
+ # the socket listener, (or thing),
35
+ #
36
+ # The daemon has just started.
37
+ #
38
+
39
+ end
40
+
41
+ def daemon_stop
42
+
43
+ #
44
+ # OVERRIDE
45
+ #
46
+ # To perfom any necessties before process
47
+ # termination,
48
+ #
49
+ # The daemon has received a signal to stop.
50
+ #
51
+
52
+ end
53
+
54
+ def periodic
55
+
56
+ #
57
+ # TODO: There shall not only be one
58
+ #
59
+ # Changes are pending.
60
+ #
61
+ # OVERRIDE
62
+ #
63
+ # To perform activities at the interval
64
+ # defined in the config.
65
+ #
66
+
67
+ end
68
+
69
+ def initialize( config_hash, daemon_name, logger )
70
+
71
+ @config = config_hash
72
+ @daemon_name = daemon_name
73
+ @logger = logger
74
+
75
+ @my_config = @config[ :daemons ][ @daemon_name ][ :handler ]
76
+
77
+ protocol = 'Base'
78
+
79
+ if @my_config.has_key? :protocol then
80
+
81
+ protocol = @my_config[ :protocol ]
82
+
83
+ end
84
+
85
+ #
86
+ # SIGNIFICANT DECISION
87
+ #
88
+ # - (currently) - There is one >>Instance<< of the protocol
89
+ #
90
+ # - It is an object ( has .new )
91
+ #
92
+ # - All attached socket traffic is directed through it for de/encode
93
+ # -
94
+ # - ?.. ie. multiple TcpClients all routing through the same object..?
95
+ # -
96
+ # - THIS MAY NEED TO CHANGE (thinks & thinks & thinks)
97
+ #
98
+ # - (Thinks) - It may move to the Client::Base so that each attached
99
+ # client is paired with its own instance of the protocol.
100
+ #
101
+ # - For now it wil be instanciated here and binded to each client at
102
+ # connect - to be shared for the decoding. That should be fine for
103
+ # as long as the protocol itself stores no state and is simply a
104
+ # code path (Still, thinks & ...
105
+ #
106
+
107
+ #
108
+ # initialize the protocol specified in config
109
+ #
110
+ # daemons:
111
+ #
112
+ # viking-invasion-early-warning-net_CoreHub_co_uk-D4RK1:
113
+ #
114
+ # adaptor:
115
+ # connection: TriFocalOcularRotisserie
116
+ # lightsource:
117
+ # type: Fire
118
+ # fuel: Peat
119
+ #
120
+ # handler:
121
+ # collection: ScottishCoastalVillages
122
+ # protocol: MedievalLanternMorse <----------
123
+ #
124
+ # Note: This is the application layer protocol
125
+ # and is therefore entirely agnostic of
126
+ # transport and disassociated from the
127
+ # adapter in use.
128
+ #
129
+
130
+ @logger.info "Load definition: Protocol::#{protocol}"
131
+
132
+ @protocol = LanGrove::ClassLoader.create( {
133
+
134
+ :module => 'Protocol',
135
+ :class => protocol
136
+
137
+ } )
138
+
139
+ end
140
+
141
+ end; end
@@ -0,0 +1,2 @@
1
+ module LanGrove::Protocol; end
2
+ require 'langrove/protocol_base'
@@ -0,0 +1,32 @@
1
+ require 'date'
2
+ require 'langrove'
3
+
4
+ module LanGrove::Protocol class Syslog
5
+
6
+ def decode( message )
7
+
8
+ #
9
+ # eg:
10
+ #
11
+ # <NN>Apr 4 11:00:06 fw.school1950.gp-online.net puppet[29206]: Autoloaded file blahblahblah
12
+ #
13
+
14
+ pass = message.split('>')[1].split()
15
+ time = DateTime.parse(pass[0..2].join(' '))
16
+ hostname = pass[3]
17
+ message = pass[4..-1].join(' ').split(': ',2)
18
+
19
+ hostname.gsub!( /vlan[\d]*\./, '')
20
+
21
+ @decoded = {
22
+ :timestamp => time, # DateTime object
23
+ :hostname => hostname, # without prepended vlanN
24
+ :tag => message[0], # includes[PID]
25
+ :event => message[1] # everything after the colon after the tag
26
+ }
27
+
28
+ return @decoded
29
+
30
+ end
31
+
32
+ end; end
@@ -0,0 +1,32 @@
1
+ require 'langrove'
2
+
3
+ module LanGrove::Protocol
4
+
5
+ class Base
6
+
7
+ def initialize( place_mark_protocol_config, logger )
8
+
9
+ @logger = logger
10
+
11
+ end
12
+
13
+ def decode( data )
14
+
15
+ #
16
+ # OVERRIDE
17
+ #
18
+ # To decode the data ahead of passing it
19
+ # into the <Handler>.receive() function.
20
+ #
21
+
22
+ {
23
+
24
+ :data => data
25
+
26
+ }
27
+
28
+ end
29
+
30
+ end
31
+
32
+ end
@@ -0,0 +1,3 @@
1
+ module LanGrove
2
+ Version = VERSION = '0.0.2'
3
+ end
data/lib/langrove.rb ADDED
@@ -0,0 +1 @@
1
+ require 'langrove/_base'
@@ -0,0 +1,115 @@
1
+ require 'langrove'
2
+
3
+ describe 'A daemon' do
4
+
5
+ #
6
+ # Use the demos in ./functional dir for integration tests
7
+ #
8
+ # Note: This uses the init.d style start|stop functionality
9
+ # as provided by DaemonKit
10
+ #
11
+ # Therefore, bugs that may exist in DaemonKit could be
12
+ # exposed here.
13
+ #
14
+ # I have not directly ovbserved any thus far.
15
+ #
16
+
17
+ before :all do
18
+
19
+ @daemon_name = 'datagram'
20
+
21
+ #
22
+ # cd - to get the rvm environment up
23
+ #
24
+ @daemon_root = File.expand_path("../../../../functional", __FILE__)
25
+ @daemon_stub = "./bin/#{@daemon_name}"
26
+
27
+ #
28
+ # Daemon is configured with Handler::SocketToFile
29
+ #
30
+ # The test sends a message to the daemon and then
31
+ # verifies the presence of the file.
32
+ #
33
+ @file = "#{@daemon_root}/tmp/datagram.txt"
34
+ @mesg = "Hello Datagram To File"
35
+
36
+ #
37
+ # remove the file ahead of testing
38
+ #
39
+ `rm -f #{@file}`
40
+
41
+ #
42
+ # call shell to start daemon
43
+ #
44
+
45
+ `cd #{@daemon_root} && #{@daemon_stub} start`
46
+
47
+ #
48
+ # give it a moment to start
49
+ #
50
+ sleep 1
51
+
52
+ end
53
+
54
+ after :all do
55
+
56
+ #
57
+ # call shell to stop daemon
58
+ #
59
+ `cd #{@daemon_root} && #{@daemon_stub} stop`
60
+
61
+ end
62
+
63
+ context 'running a server' do
64
+
65
+ it 'receives a message' do
66
+
67
+ require "eventmachine"
68
+
69
+ class Sender < EventMachine::Connection
70
+
71
+ def send( data )
72
+
73
+ send_datagram( data, '127.0.0.1', 12701 )
74
+
75
+ end
76
+
77
+ end
78
+
79
+ EM.run do
80
+
81
+ sender = nil
82
+
83
+ EM.add_periodic_timer(1) do
84
+
85
+ sender.send( "#{@file}|#{@mesg}" )
86
+ EM.stop
87
+
88
+ end
89
+
90
+ EM.open_datagram_socket "127.0.0.1", 0, Sender do |connected|
91
+
92
+ sender = connected
93
+
94
+ end
95
+
96
+ end
97
+
98
+ #
99
+ # Give the daemon a moment to write the file
100
+ #
101
+ sleep 1
102
+
103
+ File.file?( @file ).should == true
104
+
105
+ File.open( @file, 'r' ).each_line do |line|
106
+
107
+ line.should == @mesg
108
+
109
+ end
110
+
111
+ end
112
+
113
+ end
114
+
115
+ end
@@ -0,0 +1,6 @@
1
+ require 'langrove'
2
+
3
+ describe LanGrove::Adaptor::Datagram do
4
+
5
+
6
+ end
@@ -0,0 +1,48 @@
1
+ require 'langrove'
2
+
3
+ describe LanGrove::Adaptor::Base do
4
+
5
+ before :each do
6
+
7
+ @logger = LanGrove::FakeLogger.new
8
+
9
+ @extendhandler = 'ExtendedHandler'
10
+
11
+ @config = {
12
+
13
+ :iface => '111.111.111.111',
14
+
15
+ :port => 11111,
16
+
17
+ :connector => @extendhandler,
18
+
19
+ :client => {
20
+
21
+ :class => 'Base'
22
+
23
+ }
24
+ }
25
+
26
+ end
27
+
28
+ pending 'may need to override default connection handler' do
29
+
30
+ LanGrove::Adaptor.const_set( @extendhandler, Class.new )
31
+
32
+ subject = LanGrove::Adaptor::Base.new( @config, @logger )
33
+ connector = subject.instance_variable_get( :@handler )
34
+
35
+ connector.should be_a( LanGrove::Adaptor.const_get( @extendhandler ) )
36
+
37
+ end
38
+
39
+ pending 'assigns defaults' do
40
+
41
+ subject = LanGrove::Adaptor::Base.new( {}, @logger )
42
+ connector = subject.instance_variable_get( :@connector )
43
+
44
+ connector.should == LanGrove::Adaptor::SocketHandler
45
+
46
+ end
47
+
48
+ end
@@ -0,0 +1 @@
1
+ require 'langrove/client/datagram'
@@ -0,0 +1,5 @@
1
+ require 'langrove'
2
+
3
+ describe LanGrove::Client::Base do
4
+
5
+ end
@@ -0,0 +1,101 @@
1
+ require 'langrove'
2
+
3
+ describe LanGrove::Daemon::Base do
4
+
5
+ before :each do
6
+
7
+ @logger = LanGrove::FakeLogger.new :silent
8
+
9
+ @daemon_name = 'pretend_daemon'
10
+
11
+ @adaptor_class = 'Base'
12
+ @collection_class = 'Base'
13
+ @client_class = 'Base'
14
+
15
+ @config = {
16
+
17
+ :daemons => {
18
+
19
+ @daemon_name => {
20
+
21
+ :server => false,
22
+
23
+ :adaptor => {
24
+
25
+ :connection => @adaptor_class,
26
+
27
+ :client => {
28
+
29
+ :class => @client_class
30
+
31
+ }
32
+ },
33
+
34
+ :handler => {
35
+
36
+ :collection => @collection_class
37
+
38
+ }
39
+ }
40
+ }
41
+ }
42
+
43
+ end
44
+
45
+ subject do
46
+
47
+ LanGrove::Daemon::Base.new( @config, @daemon_name, @logger )
48
+
49
+ end
50
+
51
+ it 'requires a logger' do
52
+
53
+ expect {
54
+
55
+ LanGrove::Daemon::Base.new( @config, @daemon_name, nil )
56
+
57
+ }.to raise_error( LanGrove::DaemonConfigException, /Requires a logger/ )
58
+
59
+ end
60
+
61
+ it 'requires a configuration' do
62
+
63
+ expect {
64
+
65
+ LanGrove::Daemon::Base.new( {}, @daemon_name, @logger )
66
+
67
+ }.to raise_error( LanGrove::DaemonConfigException, /Missing config item/ )
68
+
69
+ end
70
+
71
+ it 'latebinds an adaptor by config' do
72
+
73
+ adaptor = subject.instance_variable_get( :@adaptor )
74
+
75
+ adaptor.should be_a( LanGrove::Adaptor.const_get( @adaptor_class ) )
76
+
77
+ end
78
+
79
+ it 'latebinds a handler by config' do
80
+
81
+ handler = subject.instance_variable_get( :@handler )
82
+
83
+ handler.should be_a( LanGrove::Handler.const_get( @collection_class ) )
84
+
85
+ end
86
+
87
+ it 'it schedules periodics'
88
+
89
+ it 'when run, it calls to listen if server is true' do
90
+
91
+ expect {
92
+
93
+ subject.instance_variable_set( :@server, true )
94
+
95
+ subject.run
96
+
97
+ }.to raise_error( LanGrove::DaemonConfigException, "NotYetExtended: undefined listen()" )
98
+
99
+ end
100
+
101
+ end
@@ -0,0 +1,83 @@
1
+ require 'langrove/ext/class_loader'
2
+ require 'langrove/ext/fake_logger'
3
+
4
+ describe LanGrove::ClassLoader do
5
+
6
+ before :all do
7
+
8
+ @logger = LanGrove::FakeLogger.new :silent
9
+
10
+ end
11
+
12
+ it 'raises on missing class definition file' do
13
+
14
+ expect {
15
+
16
+ LanGrove::ClassLoader.create( {
17
+
18
+ :module => 'Protocol',
19
+ :class => 'MedievalLanternMorse'
20
+
21
+ }, @logger )
22
+
23
+ }.to raise_error(
24
+
25
+ LanGrove::ClassLoaderException,
26
+
27
+ "no such file to load -- protocol/medieval_lantern_morse.rb"
28
+
29
+ )
30
+ end
31
+
32
+ it 'raises on missing ModuleName' do
33
+
34
+ expect {
35
+
36
+ LanGrove::ClassLoader.create( {
37
+
38
+ :class => 'MedievalLanternMorse'
39
+
40
+ }, @logger )
41
+
42
+ }.to raise_error(
43
+
44
+ LanGrove::ClassLoaderException, /:module/
45
+
46
+ )
47
+
48
+ end
49
+
50
+ it 'raises on missing ClassName' do
51
+
52
+ expect {
53
+
54
+ LanGrove::ClassLoader.create( {
55
+
56
+ :module => 'Protocol'
57
+
58
+ }, @logger )
59
+
60
+ }.to raise_error(
61
+
62
+ LanGrove::ClassLoaderException, /:class/
63
+
64
+ )
65
+
66
+ end
67
+
68
+ it 'returns a constantized class definition' do
69
+
70
+ test = LanGrove::ClassLoader.create( {
71
+
72
+ :module => 'Protocol',
73
+ :class => 'Base'
74
+
75
+ }, @logger ).new( nil, @logger )
76
+
77
+ test.should be_a( LanGrove::Protocol::Base )
78
+
79
+ end
80
+
81
+ it 'also works on local implementation'
82
+
83
+ end
@@ -0,0 +1,81 @@
1
+ require 'langrove/ext'
2
+
3
+ describe LanGrove::ConfigItem do
4
+
5
+ before :each do
6
+ @config_hash = {
7
+ 'root' => "3",
8
+ 'BLANK' => "",
9
+ :daemons => {
10
+ 'puppet_log' => {
11
+ :handler => {
12
+ :collection => 'SyslogStateMachines'
13
+ }
14
+ }
15
+ }
16
+ }
17
+ end
18
+
19
+ it 'accesses config items' do
20
+ LanGrove::ConfigItem.get(@config_hash, ['root']).should == '3'
21
+ end
22
+
23
+ it 'accesses nested config items' do
24
+ LanGrove::ConfigItem.get(@config_hash,
25
+
26
+ [:daemons , 'puppet_log', :handler, :collection]
27
+
28
+ ).should == 'SyslogStateMachines'
29
+ end
30
+
31
+ it 'raises a config exception if the key isnt present' do
32
+
33
+ expect {
34
+
35
+ LanGrove::ConfigItem.get(@config_hash, ['NOT'])
36
+
37
+ }.to raise_error( LanGrove::ConfigException, /Missing config item 'NOT'/ )
38
+
39
+ end
40
+
41
+ it 'raises a config exception if the key isnt present' do
42
+
43
+ expect {
44
+
45
+ LanGrove::ConfigItem.get(@config_hash, ['BLANK'])
46
+
47
+ }.to raise_error( LanGrove::ConfigException, /Missing config item 'BLANK'/ )
48
+
49
+ end
50
+
51
+ it 'raises a config exception if the value isnt present' do
52
+
53
+ expect {
54
+
55
+ LanGrove::ConfigItem.get(@config_hash, ['BLANK'], false)
56
+
57
+ }.to_not raise_error( LanGrove::ConfigException, /Missing config item 'BLANK'/ )
58
+
59
+ end
60
+
61
+ it 'raises a config exception if the nested item isnt present' do
62
+
63
+ expect {
64
+
65
+ LanGrove::ConfigItem.get(@config_hash, [ :daemons, 'NEITHER', 'this'])
66
+
67
+ }.to raise_error( LanGrove::ConfigException, /Missing config item 'NEITHER'/ )
68
+
69
+ end
70
+
71
+ it 'does not raise on not mandatory fields' do
72
+
73
+ expect {
74
+
75
+ LanGrove::ConfigItem.get(@config_hash, [ :daemons, 'NEITHER' ], false)
76
+
77
+ }.to_not raise_error( LanGrove::ConfigException )
78
+
79
+ end
80
+
81
+ end
@@ -0,0 +1,5 @@
1
+ require 'langrove/ext'
2
+
3
+ describe LanGrove::ConfigLoader do
4
+
5
+ end
File without changes