jerbil 1.3.3 → 1.4.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,134 @@
1
+ #
2
+ # Author:: Robert Sharp
3
+ # Copyright:: Copyright (c) 2014 Robert Sharp
4
+ # License:: Open Software Licence v3.0
5
+ #
6
+ # This software is licensed for use under the Open Software Licence v. 3.0
7
+ # The terms of this licence can be found at http://www.opensource.org/licenses/osl-3.0.php
8
+ # and in the file copyright.txt. Under the terms of this licence, all derivative works
9
+ # must themselves be licensed under the Open Software Licence v. 3.0
10
+ #
11
+ #
12
+
13
+
14
+ module Jerbil
15
+ # Monitors the network for Jerbil servers and reports to the local server
16
+ #
17
+ # Provides a way to decouple the interactions between the jerbil server
18
+ # and other servers on the network to prevent race conditions or
19
+ # holding up start-up while other servers are discovered
20
+ #
21
+ # The Monitor object is intended to be used in a subprocess after creating
22
+ # the main jerbil server. It scans the LAN for other servers and keeps
23
+ # a record of known servers. When a new server is found, it registers it
24
+ # with the local server and also registers each of its services. If a
25
+ # known server disappears, then it also removes that server (and its services)
26
+ # from the network. If the server left in an orderly manner then it may
27
+ # have already done this, but the server can cope!
28
+ #
29
+ # The subprocess needs to be cleaned up afterwards - does not seem to be possible
30
+ # to either link it with the server's process or tidy up on kill.
31
+ class Monitor
32
+
33
+ # create the monitor object
34
+ #
35
+ # @params options [Hash] options hash as for the Jerbil Server
36
+ # @params jkey [String] jerbil servers private key
37
+ def initialize(options, jkey)
38
+ @options = options
39
+ @env = @options[:environment]
40
+ @jerbil_key = jkey
41
+ @local_server = Jerbil::Servers.create_local_server(@env, @jerbil_key)
42
+ @servers = Hash.new
43
+ @servers[@local_server.fqdn] = @local_server
44
+ @log_opts = Jellog::Logger.get_options(options)
45
+ log_opts = @log_opts.dup
46
+ @logger = Jellog::Logger.new('jerbil-monitor', log_opts)
47
+ @logger.system "Started the Jerbil Monitor"
48
+ self.monitor
49
+ end
50
+
51
+ # start the monitor loop to find and register existing servers
52
+ #
53
+ # Loops round to see what servers are up and adds any that are not
54
+ # already known about. It does this "check_count" times in case it misses
55
+ # a server (the TCP check may not have a long timeout). See
56
+ #
57
+ # It does one loop every :monitor_loop_time seconds (see {Jerbil::Config})
58
+ #
59
+ def monitor
60
+ # create a monitor thread
61
+ loop_time = @options[:loop_time]
62
+ # loop forever
63
+ ljerbil = @local_server.connect
64
+
65
+ check_count = @options[:check_count]
66
+
67
+ check_count.times do |c|
68
+ @logger.info "Starting jerbil server monitor loop: #{c}"
69
+ # set the time until the next loop
70
+ time_to_next_loop = Time.now + loop_time
71
+ # scan the LAN for other monitors
72
+ network_servers = Jerbil::Servers.find_servers(@env,
73
+ @options[:net_address],
74
+ @options[:net_mask],
75
+ @options[:scan_timeout])
76
+ scanned = Array.new
77
+
78
+ # for each discovered server
79
+ network_servers.each do |nserver|
80
+
81
+ scanned << nserver.fqdn
82
+ # skip if already known
83
+ next if @servers.has_key? nserver.fqdn
84
+
85
+ # its new so register with servers
86
+
87
+ rjerbil = nserver.connect
88
+ unless rjerbil.nil?
89
+ begin
90
+ # register local with remote
91
+ rkey = rjerbil.register_server(@local_server, @options[:secret], @env)
92
+ nserver.set_key(rkey)
93
+
94
+ # and register remote with local
95
+ ljerbil.register_server(nserver, @options[:secret], @env)
96
+
97
+ @logger.info "Found server: #{nserver.fqdn}"
98
+
99
+ # Add to local record - could use jerbil itself?
100
+ @servers[nserver.fqdn] = nserver
101
+
102
+
103
+ # now tell my server about these services
104
+
105
+ rjerbil.get_local_services(rkey).each do |rservice|
106
+ ljerbil.register_remote(@jerbil_key, rservice)
107
+ end
108
+
109
+ rescue Jerbil::JerbilAuthenticationError
110
+ @logger.fatal "Invalid Secret key registering with #{nserver.fqdn}"
111
+ @servers[nserver.fqdn] = nserver # save it anyway to stop repeated logging
112
+ end
113
+ end
114
+ end
115
+
116
+ if time_to_next_loop > Time.now then
117
+ @logger.debug "Taking a nap"
118
+ sleep(time_to_next_loop - Time.now)
119
+ end
120
+
121
+ end # loop
122
+
123
+
124
+ rescue => e
125
+ @logger.exception e
126
+ ensure
127
+ @logger.system "Jerbil Monitor complete and closing down"
128
+ @logger.close
129
+ Process.exit!
130
+ end
131
+
132
+
133
+ end
134
+ end
@@ -32,6 +32,10 @@ module Jerbil
32
32
  # Note that the callback parameters do not really need to be considered
33
33
  # if you are using {JerbilService::Base}
34
34
  #
35
+ # Warning - if your hostname is not fully qualified this may not work as expected
36
+ # if you DNS server does not provide expected reverse lookup. Consider using
37
+ # `hostname -f` although *nix dependent.
38
+ #
35
39
  # @param [Symbol] name identifying the service - needs to match /etc/services
36
40
  # or create fails with the exception InvalidService
37
41
  # @param [Symbol] env identify the service's environment. Allows multiple
@@ -42,7 +46,13 @@ module Jerbil
42
46
  # @return [ServiceRecord] of course
43
47
  # @raise [InvalidService] if the service is not registered through /etc/services
44
48
  def initialize(name, env, verify_callback=:verify_callback, stop_callback=nil)
49
+
50
+ # gethostname may npt provide the fqdn
45
51
  @host = Socket.gethostname
52
+ if @host.split('.').length == 1 then
53
+ # no domain name
54
+ @host = Socket.gethostbyname(@host).first
55
+ end
46
56
  @name = name
47
57
  begin
48
58
  @port = Socket.getservbyname(@name.to_s)
@@ -32,14 +32,14 @@ module Jerbil
32
32
  # @param [String] pid_dir path to directory where pid file is to be written
33
33
  # @return [String] the pid
34
34
  # @raise [Jerbil::ServiceConfigError] if the pid file cannot be written to
35
- def Support.write_pid_file(name, env, pid_dir)
36
- pid = Process.pid.to_s
35
+ def Support.write_pid_file(name, env, pid_dir, pid=Process.pid)
36
+ #pid = Process.pid.to_s
37
37
  pid_file = "#{pid_dir}/#{name.to_s}-#{env}.pid"
38
38
  FileUtils.rm_f(pid_file) if File.exists?(pid_file) # avoid permissions probs
39
39
  File.open(pid_file, "w") do |pfile|
40
- pfile.puts pid
40
+ pfile.puts pid.to_s
41
41
  end
42
- return pid
42
+ return pid.to_s
43
43
  rescue Errno::ENOENT
44
44
  # failed to write pid to file
45
45
  raise Jerbil::ServiceConfigError, "Cannot write pid file: #{pid_file}"
@@ -1,13 +1,14 @@
1
1
  # Created by Jevoom
2
2
  #
3
- # 03-Sep-2013
4
- # Ensure jerbil methods are called on the server and not the server record.
3
+ # 17-Oct-2014
4
+ # Add server version to 'jerbil remotes -V' and add system logging for start and
5
+ # end of monitor process.
5
6
 
6
7
  module Jerbil
7
- # version set to 1.3.3
8
- Version = '1.3.3'
9
- # date set to 03-Sep-2013
10
- Version_Date = '03-Sep-2013'
11
- #ident string set to: jerbil-1.3.3 03-Sep-2013
12
- Ident = 'jerbil-1.3.3 03-Sep-2013'
8
+ # version set to 1.4.5
9
+ Version = '1.4.5'
10
+ # date set to 17-Oct-2014
11
+ Version_Date = '17-Oct-2014'
12
+ #ident string set to: jerbil-1.4.5 17-Oct-2014
13
+ Ident = 'jerbil-1.4.5 17-Oct-2014'
13
14
  end
@@ -136,4 +136,15 @@ rescue DRb::DRbConnError
136
136
  end
137
137
 
138
138
  puts "Stopped Jerbil Server" if verbose
139
+
140
+ # puts "Stopping Monitor process" if verbose
141
+ #
142
+ # mpid = Jerbil::Support.get_pid_and_delete_file(:jmonitor, server_env, options[:pid_dir])
143
+ # if mpid then
144
+ # puts "Obtained a pid for monitor: #{mpid}" if verbose
145
+ # Process.kill "SIGKILL", mpid.to_i
146
+ # else
147
+ # puts "No mpid was found!" if verbose
148
+ # end
149
+
139
150
  exit 0
@@ -21,6 +21,7 @@ require 'jerbil/servers'
21
21
  require 'jerbil/config'
22
22
  require 'jerbil/version'
23
23
  require 'jerbil/support'
24
+ require 'jerbil/monitor'
24
25
 
25
26
  require 'jellog'
26
27
  require 'jeckyl'
@@ -157,9 +158,12 @@ end
157
158
 
158
159
 
159
160
 
160
- logger.puts "Logging started for Jerbil Daemon"
161
+ logger.puts "Logging started for Jerbil Daemon" if verbose
161
162
  logger.puts "Daemonized" if verbose && daemonize
162
163
 
164
+ # will only output messages to screen if not daemonised and therefore safe to
165
+ # assume verbose anyway.
166
+
163
167
  my_self = Jerbil::Servers.create_local_server(config[:environment], pkey)
164
168
 
165
169
  logger.puts "Created local server: #{my_self.ident}"
@@ -168,14 +172,31 @@ jerbild = Jerbil::Broker.new(config, pkey)
168
172
 
169
173
  logger.puts "Started Broker"
170
174
 
175
+ logger.puts "Starting Monitor"
176
+
177
+ # create a separate process to look for other servers
178
+ # and log them to this one
179
+ mon_pid = fork do
180
+
181
+ monitor = Jerbil::Monitor.new(config, pkey)
182
+
183
+ # exits automatically
184
+
185
+ end
186
+
171
187
  DRb.start_service(my_self.drb_address, jerbild)
172
188
 
173
189
  logger.puts "Started DRb"
174
190
 
175
- # now create the pid file
176
- Jerbil::Support.write_pid_file(:jerbil, config[:environment], config[:pid_dir])
177
191
 
178
- logger.puts "Written pid #{Process.pid} to pid file"
192
+ # now create the pid files
193
+ jer_pid = Jerbil::Support.write_pid_file(:jerbil, config[:environment], config[:pid_dir])
194
+
195
+ logger.puts "Written Jerbil pid #{jer_pid} to pid file"
196
+
197
+ # Jerbil::Support.write_pid_file(:jmonitor, config[:environment], config[:pid_dir], mon_pid)
198
+ #
199
+ # logger.puts "Written Monitor pid #{mon_pid} to pid file"
179
200
 
180
201
  $0 = "jerbild-#{config[:environment]}"
181
202
 
@@ -33,28 +33,28 @@ describe "Test Jerbil Client Interface" do
33
33
  it "should respond to the old connect method" do
34
34
  client_opts = {:local=>true, :config_file=>config_file, :quiet=>true, :jerbil_env=>:test}
35
35
  JerbilService::Client.connect(RubyTest, client_opts) do |rubytest|
36
- rubytest.action.should == 'Hello'
37
- rubytest.host.should == localhost
36
+ expect(rubytest.action).to eq('Hello')
37
+ expect(rubytest.host).to eq(localhost)
38
38
  end
39
39
  end
40
40
 
41
41
  it "should respond to the new find_services method" do
42
42
  JerbilService::Client.find_services(:first, RubyTest, @client_opts) do |rubytest|
43
- rubytest.action.should == 'Hello'
44
- rubytest.host.should == localhost
43
+ expect(rubytest.action).to eq('Hello')
44
+ expect(rubytest.host).to eq(localhost)
45
45
  end
46
46
  end
47
47
 
48
48
  it "should respond to the new find_services method with local service" do
49
49
  JerbilService::Client.find_services(:local, RubyTest, @client_opts) do |rubytest|
50
- rubytest.action.should == 'Hello'
51
- rubytest.host.should == localhost
50
+ expect(rubytest.action).to eq('Hello')
51
+ expect(rubytest.host).to eq(localhost)
52
52
  end
53
53
  end
54
54
 
55
55
  it "should work with multiple client call" do
56
56
  JerbilService::Client.find_services(:all, RubyTest, @client_opts) do |rubytest|
57
- rubytest.action.should == 'Hello'
57
+ expect(rubytest.action).to eq('Hello')
58
58
  end
59
59
  end
60
60
 
@@ -66,26 +66,26 @@ describe "Test Jerbil Client Interface" do
66
66
  options[:output] = log_file
67
67
  options[:quiet] = false
68
68
  JerbilService::Client.find_services(:local, RubyTest, options) do |rubytest|
69
- rubytest.action.should == 'Hello'
69
+ expect(rubytest.action).to eq('Hello')
70
70
  end
71
71
  log_file.close
72
72
  log = File.readlines(log_filename)
73
- log[0].should match(/^Welcome/)
73
+ expect(log[0]).to match(/^Welcome/)
74
74
  end
75
75
 
76
76
  it "should not respond to an unknown method" do
77
77
  JerbilService::Client.find_services(:first, RubyTest, @client_opts) do |rubytest|
78
- lambda{rubytest.unlikely_method}.should raise_error(NoMethodError)
78
+ expect {rubytest.unlikely_method}.to raise_error(NoMethodError)
79
79
  end
80
80
  end
81
81
 
82
82
  it "should not respond to an unknown search key" do
83
- lambda{JerbilService::Client.find_services(:last, RubyTest, @client_opts)}.should raise_error(ArgumentError)
83
+ expect {JerbilService::Client.find_services(:last, RubyTest, @client_opts)}.to raise_error(ArgumentError)
84
84
  end
85
85
 
86
86
  it "should not allow the stop_callback to be called" do
87
87
  JerbilService::Client.find_services(:first, RubyTest, @client_opts) do |rubytest|
88
- lambda{rubytest.stop_callback}.should raise_error(Jerbil::UnauthorizedMethod)
88
+ expect {rubytest.stop_callback}.to raise_error(Jerbil::UnauthorizedMethod)
89
89
  end
90
90
  end
91
91
 
@@ -97,18 +97,18 @@ describe "Jerbil Clients that do different things" do
97
97
  end
98
98
 
99
99
  it "should not find an invalid service" do
100
- lambda{JerbilService::Client.connect(Blabla)}.should raise_error(NameError)
100
+ expect {JerbilService::Client.connect(Blabla)}.to raise_error(NameError)
101
101
  end
102
102
 
103
103
  it "should find a local service" do
104
104
  JerbilService::Client.connect(RubyTest, @client_opts) do |client|
105
- client.action.should == 'Hello'
105
+ expect(client.action).to eq('Hello')
106
106
  end
107
107
  end
108
108
 
109
109
  it "should not find a local service if it thinks it is somewhere else" do
110
- Socket.stub(:gethostname).and_return('germanicus.osburn-sharp.ath.cx', 'lucius.osburn-sharp.ath.cx')
111
- lambda{JerbilService::Client.connect(RubyTest, @client_opts)}.should raise_error(Jerbil::ServiceNotFound)
110
+ allow(Socket).to receive_message_chain(:gethostname).and_return('germanicus.osburn-sharp.ath.cx', 'lucius.osburn-sharp.ath.cx')
111
+ expect {JerbilService::Client.connect(RubyTest, @client_opts)}.to raise_error(Jerbil::ServiceNotFound)
112
112
  end
113
113
 
114
114
  end
@@ -32,7 +32,6 @@ describe "A local Jerbil Session running under a daemon" do
32
32
  end
33
33
 
34
34
  before(:each) do
35
-
36
35
  my_conf = Jerbil::Config.new(conf_file)
37
36
  test_server = Jerbil::Servers.get_local_server(my_conf[:environment])
38
37
  @my_session = test_server.connect
@@ -44,21 +43,21 @@ describe "A local Jerbil Session running under a daemon" do
44
43
 
45
44
 
46
45
  it "should be easily created" do
47
- @my_session.started.should be_true
48
- @my_session.registrations.should == @registrations
49
- @my_session.service_count.should == @start_count
50
- @my_session.get_all.should == []
46
+ expect(@my_session.started).to be_kind_of(Time)
47
+ expect(@my_session.registrations).to eq(@registrations)
48
+ expect(@my_session.service_count).to eq(@start_count)
49
+ expect(@my_session.get_all).to eq([])
51
50
  end
52
51
 
53
52
  it "should be easy to add a service" do
54
53
  @my_session.register(@my_service)
55
- @my_session.registrations.should == @registrations + 1
56
- @my_session.service_count.should == @start_count + 1
54
+ expect(@my_session.registrations).to eq(@registrations + 1)
55
+ expect(@my_session.service_count).to eq(@start_count + 1)
57
56
  services = @my_session.get_all(:ignore_access => true)
58
- services[0].should == @my_service
57
+ expect(services[0]).to eq(@my_service)
59
58
  service = @my_session.get_local(:ignore_access => true)
60
- service.should == @my_service
61
- @my_session.find(:name=>'Another', :ignore_access => true).should == []
59
+ expect(service).to eq(@my_service)
60
+ expect(@my_session.find(:name=>'Another', :ignore_access => true)).to eq([])
62
61
  @my_session.remove(@my_service)
63
62
  end
64
63
 
@@ -67,13 +66,13 @@ describe "A local Jerbil Session running under a daemon" do
67
66
  it "should be easy to remove a service" do
68
67
  @my_session.register(@my_service)
69
68
  @my_session.remove(@my_service)
70
- @my_session.service_count.should == @start_count
69
+ expect(@my_session.service_count).to eq(@start_count)
71
70
  end
72
71
 
73
72
  it "should do nothing if you remove an unregistered service" do
74
73
  @my_session.register(@my_service)
75
74
  @my_session.remove(@a_service)
76
- @my_session.service_count.should == @start_count + 1
75
+ expect(@my_session.service_count).to eq(@start_count + 1)
77
76
  @my_session.remove(@my_service)
78
77
  end
79
78
 
@@ -13,7 +13,7 @@
13
13
  # The purpose of these tests is to check the local interface to a Jerbil Broker only
14
14
  #
15
15
  require 'rubygems'
16
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
16
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
17
17
  require 'jerbil/servers'
18
18
  require 'jerbil/service'
19
19
  require 'jerbil/config'
@@ -21,7 +21,7 @@ require 'jerbil'
21
21
  require 'jellog'
22
22
 
23
23
 
24
- conf_file = File.expand_path(File.dirname(__FILE__) + '/../test/conf.d/jerbil_local.rb')
24
+ conf_file = File.expand_path(File.join(File.dirname(File.dirname(__FILE__)), 'test', 'conf.d','jerbil_local.rb'))
25
25
 
26
26
 
27
27
  describe "A local Jerbil Session" do
@@ -45,45 +45,45 @@ describe "A local Jerbil Session" do
45
45
  end
46
46
 
47
47
  it "should be easily created" do
48
- @my_session.started.should be_true
49
- @my_session.registrations.should == 0
50
- @my_session.service_count.should == 0
51
- @my_session.local_service_count.should == 0
52
- @my_session.remote_service_count.should == 0
53
- @my_session.get_all.should == []
48
+ expect(@my_session.started).to be_kind_of(Time)
49
+ expect(@my_session.registrations).to eq(0)
50
+ expect(@my_session.service_count).to eq(0)
51
+ expect(@my_session.local_service_count).to eq(0)
52
+ expect(@my_session.remote_service_count).to eq(0)
53
+ expect(@my_session.get_all).to eq([])
54
54
  end
55
55
 
56
56
  it "should be easy to add a service" do
57
57
  @my_session.register(@my_service)
58
- @my_session.registrations.should == 1
59
- @my_session.service_count.should == 1
60
- @my_session.local_service_count.should == 1
61
- @my_session.remote_service_count.should == 0
58
+ expect(@my_session.registrations).to eq(1)
59
+ expect(@my_session.service_count).to eq(1)
60
+ expect(@my_session.local_service_count).to eq(1)
61
+ expect(@my_session.remote_service_count).to eq(0)
62
62
  services = @my_session.get_all(:ignore_access => true)
63
- services[0].should == @my_service
63
+ expect(services[0]).to eq(@my_service)
64
64
  service = @my_session.get_local(:ignore_access => true)
65
- service.should == @my_service
66
- @my_session.find(:name=>'Another', :ignore_access => true).should == []
65
+ expect(service).to eq(@my_service)
66
+ expect(@my_session.find(:name=>'Another', :ignore_access => true)).to eq([])
67
67
  @my_session.remove(@my_service)
68
68
  end
69
69
 
70
70
  it "should not be possible to register the same service twice" do
71
- @my_service.should_receive(:connect).and_return(true) # make it appear the service is live
71
+ allow(@my_service).to receive_messages(connect:true) # make it appear the service is live
72
72
  @my_session.register(@my_service)
73
- lambda{@my_session.register(@my_service)}.should raise_error{Jerbil::ServiceAlreadyRegistered}
73
+ expect {@my_session.register(@my_service)}.to raise_error{Jerbil::ServiceAlreadyRegistered}
74
74
  @my_session.remove(@my_service)
75
75
  end
76
76
 
77
77
  it "should be easy to remove a service" do
78
78
  @my_session.register(@my_service)
79
79
  @my_session.remove(@my_service)
80
- @my_session.service_count.should == 0
80
+ expect(@my_session.service_count).to eq(0)
81
81
  end
82
82
 
83
83
  it "should do nothing if you remove an unregistered service" do
84
84
  @my_session.register(@my_service)
85
85
  @my_session.remove(@a_service)
86
- @my_session.service_count.should == 1
86
+ expect(@my_session.service_count).to eq(1)
87
87
 
88
88
  end
89
89