sanford 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +19 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +164 -0
- data/Rakefile +8 -0
- data/bench/client.rb +30 -0
- data/bench/report.txt +10 -0
- data/bench/runner.rb +102 -0
- data/bench/services.rb +23 -0
- data/bench/tasks.rb +18 -0
- data/lib/sanford/config.rb +33 -0
- data/lib/sanford/connection.rb +70 -0
- data/lib/sanford/exception_handler.rb +43 -0
- data/lib/sanford/exceptions.rb +37 -0
- data/lib/sanford/host.rb +162 -0
- data/lib/sanford/manager.rb +58 -0
- data/lib/sanford/rake.rb +45 -0
- data/lib/sanford/server.rb +39 -0
- data/lib/sanford/service_handler.rb +93 -0
- data/lib/sanford/version.rb +3 -0
- data/lib/sanford.rb +32 -0
- data/log/.gitkeep +0 -0
- data/sanford.gemspec +27 -0
- data/test/helper.rb +20 -0
- data/test/support/helpers.rb +72 -0
- data/test/support/service_handlers.rb +115 -0
- data/test/support/services.rb +72 -0
- data/test/support/simple_client.rb +55 -0
- data/test/system/managing_test.rb +105 -0
- data/test/system/request_handling_test.rb +202 -0
- data/test/unit/config_test.rb +54 -0
- data/test/unit/connection_test.rb +23 -0
- data/test/unit/exception_handler_test.rb +69 -0
- data/test/unit/host/version_group_test.rb +39 -0
- data/test/unit/host_test.rb +142 -0
- data/test/unit/manager_test.rb +21 -0
- data/test/unit/server_test.rb +22 -0
- data/test/unit/service_handler_test.rb +170 -0
- metadata +209 -0
data/lib/sanford/host.rb
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
# Sanford's Host mixin is used to define service hosts. When mixed into a class
|
2
|
+
# it provides the interface for configuring the service host and for adding
|
3
|
+
# versioned services. It also contains the logic for routing a request to a
|
4
|
+
# a service handler.
|
5
|
+
#
|
6
|
+
# Options:
|
7
|
+
# * `name` - A string for naming this host. This can be used when specifying
|
8
|
+
# a host with the rake tasks and will be used to name the PID
|
9
|
+
# file. Defaults to the class's name.
|
10
|
+
# * `ip` - The string for the ip that the TCP Server should bind to. This
|
11
|
+
# defaults to '0.0.0.0'.
|
12
|
+
# * `port` - The integer for the port that the TCP Server should bind to.
|
13
|
+
# This isn't defaulted and must be provided.
|
14
|
+
# * `pid_dir` - The directory to write the PID file to. This is defaulted to
|
15
|
+
# Dir.pwd.
|
16
|
+
# * `logger` - The logger to use if the Sanford server logs messages. This is
|
17
|
+
# defaulted to an instance of Ruby's Logger.
|
18
|
+
#
|
19
|
+
require 'logger'
|
20
|
+
require 'ns-options'
|
21
|
+
require 'pathname'
|
22
|
+
|
23
|
+
require 'sanford/config'
|
24
|
+
require 'sanford/exception_handler'
|
25
|
+
require 'sanford/exceptions'
|
26
|
+
require 'sanford/service_handler'
|
27
|
+
|
28
|
+
module Sanford
|
29
|
+
module Host
|
30
|
+
|
31
|
+
# Notes:
|
32
|
+
# * When Host is included on a class, it needs to mixin NsOptions and define
|
33
|
+
# the options directly on the class (instead of on the Host module).
|
34
|
+
# Otherwise, NsOptions will not work correctly for the class.
|
35
|
+
def self.included(host_class)
|
36
|
+
host_class.class_eval do
|
37
|
+
include NsOptions
|
38
|
+
extend Sanford::Host::Interface
|
39
|
+
|
40
|
+
options :config do
|
41
|
+
option :name, String, :default => host_class.to_s
|
42
|
+
option :ip, String, :default => '0.0.0.0'
|
43
|
+
option :port, Integer
|
44
|
+
option :pid_dir, Pathname, :default => Dir.pwd
|
45
|
+
option :logger, :default => proc{ Sanford::NullLogger.new }
|
46
|
+
|
47
|
+
option :exception_handler, :default => Sanford::ExceptionHandler
|
48
|
+
option :versioned_services, Hash, :default => {}
|
49
|
+
end
|
50
|
+
end
|
51
|
+
Sanford.config.hosts.add(host_class)
|
52
|
+
end
|
53
|
+
|
54
|
+
INTERFACE_OPTIONS = [ :name, :ip, :port, :pid_dir, :logger, :exception_handler ]
|
55
|
+
|
56
|
+
# Notes:
|
57
|
+
# * The `initialize` takes the values configured on the class and merges
|
58
|
+
# the passed in options. This is used to set the individual instance's
|
59
|
+
# configuration (which allows overwriting options like the port).
|
60
|
+
def initialize(options = nil)
|
61
|
+
options = self.remove_nil_values(options)
|
62
|
+
config_options = self.class.config.to_hash.merge(options)
|
63
|
+
self.config.apply(config_options)
|
64
|
+
raise(Sanford::InvalidHostError.new(self.class)) if !self.port
|
65
|
+
end
|
66
|
+
|
67
|
+
INTERFACE_OPTIONS.each do |name|
|
68
|
+
|
69
|
+
define_method(name) do
|
70
|
+
self.config.send(name)
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
def run(request)
|
76
|
+
request_handler(request).run
|
77
|
+
end
|
78
|
+
|
79
|
+
def inspect
|
80
|
+
reference = '0x0%x' % (self.object_id << 1)
|
81
|
+
"#<#{self.class}:#{reference} ip=#{self.config.ip.inspect} " \
|
82
|
+
"port=#{self.config.port.inspect}>"
|
83
|
+
end
|
84
|
+
|
85
|
+
protected
|
86
|
+
|
87
|
+
def request_handler(request)
|
88
|
+
handler_class(get_handler_class_name(request)).new(self.logger, request)
|
89
|
+
end
|
90
|
+
|
91
|
+
def handler_class(class_name_str)
|
92
|
+
self.logger.info(" Handler: #{class_name_str.inspect}")
|
93
|
+
Sanford::ServiceHandler.constantize(class_name_str).tap do |handler_class|
|
94
|
+
raise Sanford::NoHandlerClassError.new(self, class_name_str) if !handler_class
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def get_handler_class_name(request)
|
99
|
+
services = self.config.versioned_services[request.version] || {}
|
100
|
+
services[request.name].tap do |name|
|
101
|
+
raise Sanford::NotFoundError if !name
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def remove_nil_values(options)
|
106
|
+
(options || {}).inject({}) do |hash, (k, v)|
|
107
|
+
hash.merge!({ k => v }) if !v.nil?
|
108
|
+
hash
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
module Interface
|
113
|
+
|
114
|
+
INTERFACE_OPTIONS.each do |name|
|
115
|
+
|
116
|
+
define_method(name) do |*args|
|
117
|
+
self.config.send("#{name}=", *args) if !args.empty?
|
118
|
+
self.config.send(name)
|
119
|
+
end
|
120
|
+
|
121
|
+
define_method("#{name}=") do |new_value|
|
122
|
+
self.config.send("#{name}=", new_value)
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
|
127
|
+
def version(name, &block)
|
128
|
+
version_group = Sanford::Host::VersionGroup.new(name, &block)
|
129
|
+
self.config.versioned_services.merge!(version_group.to_hash)
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
|
134
|
+
class VersionGroup
|
135
|
+
attr_reader :name, :services
|
136
|
+
|
137
|
+
def initialize(name, &definition_block)
|
138
|
+
@name = name
|
139
|
+
@services = {}
|
140
|
+
self.instance_eval(&definition_block)
|
141
|
+
end
|
142
|
+
|
143
|
+
def service_handler_ns(value = nil)
|
144
|
+
@service_handler_ns = value if value
|
145
|
+
@service_handler_ns
|
146
|
+
end
|
147
|
+
|
148
|
+
def service(service_name, handler_class_name)
|
149
|
+
if self.service_handler_ns && !(handler_class_name =~ /^::/)
|
150
|
+
handler_class_name = "#{self.service_handler_ns}::#{handler_class_name}"
|
151
|
+
end
|
152
|
+
@services[service_name] = handler_class_name
|
153
|
+
end
|
154
|
+
|
155
|
+
def to_hash
|
156
|
+
{ self.name => self.services }
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# The Manager class is responsible for managing sanford's server process. Given
|
2
|
+
# a host, it can start and stop the host's server process. This is done using
|
3
|
+
# the Daemons gem and `run_proc`. The class provides a convenience method on the
|
4
|
+
# class called `call`, which will find a host, build a new manager and call the
|
5
|
+
# relevant action (this is what the rake tasks use).
|
6
|
+
#
|
7
|
+
require 'daemons'
|
8
|
+
|
9
|
+
require 'sanford/config'
|
10
|
+
require 'sanford/exceptions'
|
11
|
+
require 'sanford/server'
|
12
|
+
|
13
|
+
module Sanford
|
14
|
+
|
15
|
+
class Manager
|
16
|
+
attr_reader :host, :process_name
|
17
|
+
|
18
|
+
def self.call(action, options = nil)
|
19
|
+
options ||= {}
|
20
|
+
options[:host] ||= ENV['SANFORD_HOST']
|
21
|
+
options[:ip] ||= ENV['SANFORD_IP']
|
22
|
+
options[:port] ||= ENV['SANFORD_PORT']
|
23
|
+
|
24
|
+
host_class = if (host_class_or_name = options.delete(:host))
|
25
|
+
Sanford.config.find_host(host_class_or_name)
|
26
|
+
else
|
27
|
+
Sanford.config.hosts.first
|
28
|
+
end
|
29
|
+
raise(Sanford::NoHostError.new(host_class_or_name)) if !host_class
|
30
|
+
self.new(host_class, options).call(action)
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize(host_class, options = {})
|
34
|
+
@host = host_class.new(options)
|
35
|
+
@process_name = [ self.host.ip, self.host.port, self.host.name ].join('_')
|
36
|
+
end
|
37
|
+
|
38
|
+
def call(action)
|
39
|
+
options = self.default_options.merge({ :ARGV => [ action.to_s ] })
|
40
|
+
FileUtils.mkdir_p(options[:dir])
|
41
|
+
::Daemons.run_proc(self.process_name, options) do
|
42
|
+
server = Sanford::Server.new(self.host)
|
43
|
+
server.start
|
44
|
+
server.join_thread
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
protected
|
49
|
+
|
50
|
+
def default_options
|
51
|
+
{ :dir_mode => :normal,
|
52
|
+
:dir => self.host.pid_dir
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
data/lib/sanford/rake.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
module Sanford::Rake
|
2
|
+
|
3
|
+
class Tasks
|
4
|
+
extend ::Rake::DSL
|
5
|
+
|
6
|
+
def self.load
|
7
|
+
namespace :sanford do
|
8
|
+
|
9
|
+
# Overwrite this to load your application's environment so that it can
|
10
|
+
# be used with Sanford
|
11
|
+
task :setup
|
12
|
+
|
13
|
+
task :load_manager => :setup do
|
14
|
+
require 'sanford'
|
15
|
+
Sanford.init
|
16
|
+
end
|
17
|
+
|
18
|
+
desc "Start a Sanford server and daemonize the process"
|
19
|
+
task :start => :load_manager do
|
20
|
+
Sanford::Manager.call :start
|
21
|
+
end
|
22
|
+
|
23
|
+
desc "Stop a daemonized Sanford server process"
|
24
|
+
task :stop => :load_manager do
|
25
|
+
Sanford::Manager.call :stop
|
26
|
+
end
|
27
|
+
|
28
|
+
desc "Restart a daemonized Sanford server process"
|
29
|
+
task :restart => :load_manager do
|
30
|
+
Sanford::Manager.call :restart
|
31
|
+
end
|
32
|
+
|
33
|
+
desc "Run a Sanford server (not daemonized)"
|
34
|
+
task :run => :load_manager do
|
35
|
+
Sanford::Manager.call :run
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
Sanford::Rake::Tasks.load
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# Sanford's server uses DatTCP for a TCP Server. When a client connects, the
|
2
|
+
# `serve` method is called. Sanford creates a new instance of a connection
|
3
|
+
# handler and hands it the service host and client socket. This is because the
|
4
|
+
# `serve` method can be accessed by multiple threads, so we essentially create a
|
5
|
+
# new connection handler per thread.
|
6
|
+
#
|
7
|
+
require 'dat-tcp'
|
8
|
+
|
9
|
+
require 'sanford/connection'
|
10
|
+
|
11
|
+
module Sanford
|
12
|
+
|
13
|
+
class Server
|
14
|
+
include DatTCP::Server
|
15
|
+
|
16
|
+
attr_reader :service_host
|
17
|
+
|
18
|
+
def initialize(service_host, options = {})
|
19
|
+
@service_host = service_host
|
20
|
+
super(self.service_host.ip, self.service_host.port, options)
|
21
|
+
end
|
22
|
+
|
23
|
+
def name
|
24
|
+
self.service_host.name
|
25
|
+
end
|
26
|
+
|
27
|
+
def serve(socket)
|
28
|
+
connection = Sanford::Connection.new(self.service_host, socket)
|
29
|
+
connection.process
|
30
|
+
end
|
31
|
+
|
32
|
+
def inspect
|
33
|
+
reference = '0x0%x' % (self.object_id << 1)
|
34
|
+
"#<#{self.class}:#{reference} @service_host=#{self.service_host.inspect}>"
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require 'sanford-protocol'
|
3
|
+
|
4
|
+
module Sanford
|
5
|
+
|
6
|
+
module ServiceHandler
|
7
|
+
|
8
|
+
def self.constantize(class_name)
|
9
|
+
names = class_name.to_s.split('::').reject{|name| name.empty? }
|
10
|
+
klass = names.inject(Object) do |constant, name|
|
11
|
+
constant.const_get(name)
|
12
|
+
end
|
13
|
+
klass == Object ? false : klass
|
14
|
+
rescue NameError
|
15
|
+
false
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_reader :logger, :request
|
19
|
+
|
20
|
+
def initialize(logger, request)
|
21
|
+
@logger = logger
|
22
|
+
@request = request
|
23
|
+
end
|
24
|
+
|
25
|
+
def init
|
26
|
+
self.init!
|
27
|
+
end
|
28
|
+
|
29
|
+
def init!
|
30
|
+
end
|
31
|
+
|
32
|
+
# This method has very specific handling when before/after callbacks halt.
|
33
|
+
# It should always return a response tuple: `[ status, data ]`
|
34
|
+
# * If `before_run` halts, then the handler is not 'run' (it's `init` and
|
35
|
+
# `run` methods are not called) and it's response tuple is returned.
|
36
|
+
# * If `after_run` halts, then it's response tuple is returned, even if
|
37
|
+
# calling `before_run` or 'running' the handler generated a response
|
38
|
+
# tuple.
|
39
|
+
# * If `before_run` and `after_run` do not halt, then the response tuple
|
40
|
+
# from 'running' is used.
|
41
|
+
def run
|
42
|
+
response_tuple = self.run_callback 'before_run'
|
43
|
+
response_tuple ||= catch(:halt) do
|
44
|
+
self.init
|
45
|
+
data = self.run!
|
46
|
+
[ 200, data ]
|
47
|
+
end
|
48
|
+
after_response_tuple = self.run_callback 'after_run'
|
49
|
+
(response_tuple = after_response_tuple) if after_response_tuple
|
50
|
+
response_tuple
|
51
|
+
end
|
52
|
+
|
53
|
+
def run!
|
54
|
+
raise NotImplementedError
|
55
|
+
end
|
56
|
+
|
57
|
+
def before_run
|
58
|
+
end
|
59
|
+
|
60
|
+
def after_run
|
61
|
+
end
|
62
|
+
|
63
|
+
def params
|
64
|
+
self.request.params
|
65
|
+
end
|
66
|
+
|
67
|
+
def inspect
|
68
|
+
reference = '0x0%x' % (self.object_id << 1)
|
69
|
+
"#<#{self.class}:#{reference} @request=#{self.request.inspect}>"
|
70
|
+
end
|
71
|
+
|
72
|
+
protected
|
73
|
+
|
74
|
+
def halt(status, options = nil)
|
75
|
+
options = OpenStruct.new(options || {})
|
76
|
+
response_status = [ status, options.message ]
|
77
|
+
throw(:halt, [ response_status, options.data ])
|
78
|
+
end
|
79
|
+
|
80
|
+
# Notes:
|
81
|
+
# * Callbacks need to catch :halt incase the halt method is called. They
|
82
|
+
# also need to be sure to return nil if nothing is thrown, so that it
|
83
|
+
# is not considered as a response.
|
84
|
+
def run_callback(name)
|
85
|
+
catch(:halt) do
|
86
|
+
self.send(name.to_s)
|
87
|
+
nil
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
data/lib/sanford.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
module Sanford; end
|
2
|
+
|
3
|
+
require 'sanford/config'
|
4
|
+
require 'sanford/manager'
|
5
|
+
require 'sanford/host'
|
6
|
+
require 'sanford/version'
|
7
|
+
|
8
|
+
module Sanford
|
9
|
+
|
10
|
+
def self.config
|
11
|
+
Sanford::Config
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.configure(&block)
|
15
|
+
self.config.define(&block)
|
16
|
+
self.config
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.init
|
20
|
+
require self.config.services_config
|
21
|
+
end
|
22
|
+
|
23
|
+
class NullLogger
|
24
|
+
require 'logger'
|
25
|
+
|
26
|
+
Logger::Severity.constants.each do |name|
|
27
|
+
define_method(name.downcase){|*args| } # no-op
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
data/log/.gitkeep
ADDED
File without changes
|
data/sanford.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'sanford/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "sanford"
|
8
|
+
gem.version = Sanford::VERSION
|
9
|
+
gem.authors = ["Collin Redding", "Kelly Redding"]
|
10
|
+
gem.email = ["collin.redding@me.com", "kelly@kellyredding.com"]
|
11
|
+
gem.description = "Simple hosts for Sanford services."
|
12
|
+
gem.summary = "Simple hosts for Sanford services."
|
13
|
+
gem.homepage = "https://github.com/redding/sanford"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
|
20
|
+
gem.add_dependency("daemons", ["~>1.1"])
|
21
|
+
gem.add_dependency("dat-tcp", ["~>0.1"])
|
22
|
+
gem.add_dependency("ns-options", ["~>1.0.0"])
|
23
|
+
gem.add_dependency("sanford-protocol", ["~>0.5"])
|
24
|
+
|
25
|
+
gem.add_development_dependency("assert", ["~>1.0"])
|
26
|
+
gem.add_development_dependency("assert-mocha", ["~>1.0"])
|
27
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
ENV['SANFORD_PROTOCOL_DEBUG'] = 'yes'
|
2
|
+
|
3
|
+
require 'ostruct'
|
4
|
+
|
5
|
+
ROOT = File.expand_path('../..', __FILE__)
|
6
|
+
|
7
|
+
require 'sanford'
|
8
|
+
|
9
|
+
Sanford.configure do |config|
|
10
|
+
config.services_config = File.join(ROOT, 'test/support/services')
|
11
|
+
end
|
12
|
+
Sanford.init
|
13
|
+
|
14
|
+
require 'test/support/service_handlers'
|
15
|
+
require 'test/support/simple_client'
|
16
|
+
require 'test/support/helpers'
|
17
|
+
|
18
|
+
if defined?(Assert)
|
19
|
+
require 'assert-mocha'
|
20
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Test
|
2
|
+
|
3
|
+
module Environment
|
4
|
+
|
5
|
+
def self.store_and_clear_hosts
|
6
|
+
@previous_hosts = Sanford.config.hosts.dup
|
7
|
+
Sanford.config.hosts.clear
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.restore_hosts
|
11
|
+
Sanford.config.hosts = @previous_hosts
|
12
|
+
@previous_hosts = nil
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
|
19
|
+
module ForkServerHelper
|
20
|
+
|
21
|
+
def start_server(server, &block)
|
22
|
+
begin
|
23
|
+
pid = fork do
|
24
|
+
trap("TERM"){ server.stop }
|
25
|
+
server.start
|
26
|
+
server.join_thread
|
27
|
+
end
|
28
|
+
sleep 0.3 # Give time for the socket to start listening.
|
29
|
+
yield
|
30
|
+
ensure
|
31
|
+
if pid
|
32
|
+
Process.kill("TERM", pid)
|
33
|
+
Process.wait(pid)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
|
42
|
+
module ForkManagerHelper
|
43
|
+
|
44
|
+
# start a Sanford server using Sanford's manager in a forked process
|
45
|
+
def call_sanford_manager(*args, &block)
|
46
|
+
pid = fork do
|
47
|
+
STDOUT.reopen('/dev/null')
|
48
|
+
trap("TERM"){ exit }
|
49
|
+
Sanford::Manager.call(*args)
|
50
|
+
end
|
51
|
+
sleep 1 # give time for the command to run
|
52
|
+
yield
|
53
|
+
ensure
|
54
|
+
if pid
|
55
|
+
Process.kill("TERM", pid)
|
56
|
+
Process.wait(pid)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def open_socket(host, port)
|
61
|
+
socket = TCPSocket.new(host, port)
|
62
|
+
ensure
|
63
|
+
socket.close rescue false
|
64
|
+
end
|
65
|
+
|
66
|
+
def expected_pid_file(host, ip, port)
|
67
|
+
host.config.pid_dir.join("#{ip}_#{port}_#{host}.pid")
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# A bunch of service handler examples. These are defined to implement certain
|
2
|
+
# edge cases and are for specific tests within the test suite.
|
3
|
+
#
|
4
|
+
|
5
|
+
class StaticServiceHandler
|
6
|
+
include Sanford::ServiceHandler
|
7
|
+
|
8
|
+
# builds with the same request and logger always, just for convenience
|
9
|
+
|
10
|
+
def initialize(logger = nil, request = nil)
|
11
|
+
request ||= Sanford::Protocol::Request.new('v1', 'name', {})
|
12
|
+
super(logger || Sanford::NullLogger.new, request)
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
class ManualThrowServiceHandler < StaticServiceHandler
|
18
|
+
|
19
|
+
def run!
|
20
|
+
throw(:halt, 'halted!')
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
class HaltWithServiceHandler < StaticServiceHandler
|
26
|
+
|
27
|
+
def initialize(halt_with)
|
28
|
+
request = Sanford::Protocol::Request.new('v1', 'name', {
|
29
|
+
'halt_with' => halt_with.dup
|
30
|
+
})
|
31
|
+
super(Sanford::NullLogger.new, request)
|
32
|
+
end
|
33
|
+
|
34
|
+
def run!
|
35
|
+
params['halt_with'].tap do |halt_with|
|
36
|
+
halt(halt_with.delete('code'), halt_with)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
class NoopServiceHandler < StaticServiceHandler
|
43
|
+
|
44
|
+
# simply overwrites the default `run!` so it doesn't error
|
45
|
+
|
46
|
+
def run!
|
47
|
+
# nothing!
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
class FlaggedServiceHandler < NoopServiceHandler
|
53
|
+
|
54
|
+
# flags a bunch of methods as they are run by setting instance variables
|
55
|
+
|
56
|
+
FLAGGED_METHODS = {
|
57
|
+
'init' => :init_called,
|
58
|
+
'init!' => :init_bang_called,
|
59
|
+
'run!' => :run_bang_called,
|
60
|
+
'before_run' => :before_run_called,
|
61
|
+
'after_run' => :after_run_called
|
62
|
+
}
|
63
|
+
FLAGS = FLAGGED_METHODS.values
|
64
|
+
|
65
|
+
attr_reader *FLAGS
|
66
|
+
|
67
|
+
def initialize(*passed)
|
68
|
+
super
|
69
|
+
FLAGS.each{|name| self.instance_variable_set("@#{name}", false) }
|
70
|
+
end
|
71
|
+
|
72
|
+
FLAGGED_METHODS.each do |method_name, instance_variable_name|
|
73
|
+
|
74
|
+
# def before_run
|
75
|
+
# super
|
76
|
+
# @before_run_called = true
|
77
|
+
# end
|
78
|
+
define_method(method_name) do
|
79
|
+
super
|
80
|
+
self.instance_variable_set("@#{instance_variable_name}", true)
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
class ConfigurableServiceHandler < FlaggedServiceHandler
|
88
|
+
|
89
|
+
def initialize(options = {})
|
90
|
+
@options = options
|
91
|
+
super
|
92
|
+
end
|
93
|
+
|
94
|
+
def before_run
|
95
|
+
super
|
96
|
+
if @options[:before_run]
|
97
|
+
self.instance_eval(&@options[:before_run])
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def run!
|
102
|
+
super
|
103
|
+
if @options[:run!]
|
104
|
+
self.instance_eval(&@options[:run!])
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def after_run
|
109
|
+
super
|
110
|
+
if @options[:after_run]
|
111
|
+
self.instance_eval(&@options[:after_run])
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|