shift-nanite 0.4.1.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 (63) hide show
  1. data/LICENSE +201 -0
  2. data/README.rdoc +430 -0
  3. data/Rakefile +76 -0
  4. data/TODO +24 -0
  5. data/bin/nanite-admin +65 -0
  6. data/bin/nanite-agent +79 -0
  7. data/bin/nanite-mapper +50 -0
  8. data/lib/nanite.rb +74 -0
  9. data/lib/nanite/actor.rb +71 -0
  10. data/lib/nanite/actor_registry.rb +26 -0
  11. data/lib/nanite/admin.rb +138 -0
  12. data/lib/nanite/agent.rb +264 -0
  13. data/lib/nanite/amqp.rb +58 -0
  14. data/lib/nanite/cluster.rb +250 -0
  15. data/lib/nanite/config.rb +112 -0
  16. data/lib/nanite/console.rb +39 -0
  17. data/lib/nanite/daemonize.rb +13 -0
  18. data/lib/nanite/identity.rb +16 -0
  19. data/lib/nanite/job.rb +104 -0
  20. data/lib/nanite/local_state.rb +38 -0
  21. data/lib/nanite/log.rb +66 -0
  22. data/lib/nanite/log/formatter.rb +39 -0
  23. data/lib/nanite/mapper.rb +309 -0
  24. data/lib/nanite/mapper_proxy.rb +67 -0
  25. data/lib/nanite/nanite_dispatcher.rb +92 -0
  26. data/lib/nanite/packets.rb +365 -0
  27. data/lib/nanite/pid_file.rb +52 -0
  28. data/lib/nanite/reaper.rb +39 -0
  29. data/lib/nanite/security/cached_certificate_store_proxy.rb +24 -0
  30. data/lib/nanite/security/certificate.rb +55 -0
  31. data/lib/nanite/security/certificate_cache.rb +66 -0
  32. data/lib/nanite/security/distinguished_name.rb +34 -0
  33. data/lib/nanite/security/encrypted_document.rb +46 -0
  34. data/lib/nanite/security/rsa_key_pair.rb +53 -0
  35. data/lib/nanite/security/secure_serializer.rb +68 -0
  36. data/lib/nanite/security/signature.rb +46 -0
  37. data/lib/nanite/security/static_certificate_store.rb +35 -0
  38. data/lib/nanite/security_provider.rb +47 -0
  39. data/lib/nanite/serializer.rb +52 -0
  40. data/lib/nanite/state.rb +168 -0
  41. data/lib/nanite/streaming.rb +125 -0
  42. data/lib/nanite/util.rb +58 -0
  43. data/spec/actor_registry_spec.rb +60 -0
  44. data/spec/actor_spec.rb +77 -0
  45. data/spec/agent_spec.rb +240 -0
  46. data/spec/cached_certificate_store_proxy_spec.rb +34 -0
  47. data/spec/certificate_cache_spec.rb +49 -0
  48. data/spec/certificate_spec.rb +27 -0
  49. data/spec/cluster_spec.rb +622 -0
  50. data/spec/distinguished_name_spec.rb +24 -0
  51. data/spec/encrypted_document_spec.rb +21 -0
  52. data/spec/job_spec.rb +251 -0
  53. data/spec/local_state_spec.rb +130 -0
  54. data/spec/nanite_dispatcher_spec.rb +136 -0
  55. data/spec/packet_spec.rb +220 -0
  56. data/spec/rsa_key_pair_spec.rb +33 -0
  57. data/spec/secure_serializer_spec.rb +41 -0
  58. data/spec/serializer_spec.rb +107 -0
  59. data/spec/signature_spec.rb +30 -0
  60. data/spec/spec_helper.rb +33 -0
  61. data/spec/static_certificate_store_spec.rb +30 -0
  62. data/spec/util_spec.rb +63 -0
  63. metadata +129 -0
@@ -0,0 +1,76 @@
1
+ require 'rubygems'
2
+ require 'rake/gempackagetask'
3
+ require "spec/rake/spectask"
4
+ begin; require 'rubygems'; rescue LoadError; end
5
+ begin
6
+ require 'hanna/rdoctask'
7
+ rescue LoadError
8
+ require 'rake/rdoctask'
9
+ end
10
+ require 'rake/clean'
11
+ require 'lib/nanite'
12
+
13
+ GEM = "nanite"
14
+ AUTHOR = "Ezra Zygmuntowicz"
15
+ EMAIL = "ezra@engineyard.com"
16
+ HOMEPAGE = "http://github.com/ezmobius/nanite"
17
+ SUMMARY = "self assembling fabric of ruby daemons"
18
+
19
+ Dir.glob('tasks/*.rake').each { |r| Rake.application.add_import r }
20
+
21
+ spec = Gem::Specification.new do |s|
22
+
23
+ s.name = GEM
24
+ s.version = Nanite::VERSION
25
+ s.platform = Gem::Platform::RUBY
26
+ s.has_rdoc = true
27
+ s.extra_rdoc_files = ["README.rdoc", "LICENSE", 'TODO']
28
+ s.summary = SUMMARY
29
+ s.description = s.summary
30
+ s.author = AUTHOR
31
+ s.email = EMAIL
32
+ s.homepage = HOMEPAGE
33
+
34
+ s.bindir = "bin"
35
+ s.executables = %w( nanite-agent nanite-mapper nanite-admin )
36
+
37
+ s.add_dependency('amqp', '>= 0.6.0')
38
+
39
+ s.require_path = 'lib'
40
+ s.files = %w(LICENSE README.rdoc Rakefile TODO) + Dir.glob("{lib,bin,specs}/**/*")
41
+ end
42
+
43
+ Rake::GemPackageTask.new(spec) do |pkg|
44
+ pkg.gem_spec = spec
45
+ end
46
+
47
+ task :default => :spec
48
+
49
+ task :install => [:package] do
50
+ sh %{sudo gem install pkg/#{GEM}-#{Nanite::VERSION}}
51
+ end
52
+
53
+ desc "Run unit specs"
54
+ Spec::Rake::SpecTask.new do |t|
55
+ t.spec_opts = ["--format", "specdoc", "--colour"]
56
+ t.spec_files = FileList["spec/**/*_spec.rb"]
57
+ end
58
+
59
+ desc 'Generate RDoc documentation'
60
+ Rake::RDocTask.new do |rd|
61
+ rd.title = spec.name
62
+ rd.rdoc_dir = 'rdoc'
63
+ rd.main = "README.rdoc"
64
+ rd.rdoc_files.include("lib/**/*.rb", *spec.extra_rdoc_files)
65
+ end
66
+ CLOBBER.include(:clobber_rdoc)
67
+
68
+ desc 'Generate and open documentation'
69
+ task :docs => :rdoc do
70
+ case RUBY_PLATFORM
71
+ when /darwin/ ; sh 'open rdoc/index.html'
72
+ when /mswin|mingw/ ; sh 'start rdoc\index.html'
73
+ else
74
+ sh 'firefox rdoc/index.html'
75
+ end
76
+ end
data/TODO ADDED
@@ -0,0 +1,24 @@
1
+ TODO:
2
+
3
+ - The examples/crew.rb file is pointing towards a hard coded user dir. Needs to be
4
+ documented as part of a working example.
5
+ - examples/async_rack_front/async_rack_front.ru needs to be documented and verified working.
6
+
7
+ Ian:
8
+ - Sync Mapper/Agent#start with nanite-mapper/agent
9
+ - Update docs for Agent#start and Mapper#start
10
+ - Update docs in nanite-agent and nanite-mapper
11
+ - Ensure file transfer works
12
+ - Check secure stuff still works
13
+ - Check custom status_proc works
14
+ - Check documentation, only document public methods
15
+ - ensure the removal of threaded_actors option doesn't cause shit to block
16
+
17
+ - Look into using EM deferables for actors dispatch.
18
+ - Integration specs that spawn a small cluster of nanites
19
+ - Rename Ping to Status
20
+ - request/push should take *args for payload?
21
+
22
+ Maybe:
23
+ - Make mapper queue durable and Results respect :persistent flag on the request
24
+ - Add a global result received callback
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # To work without being installed as a gem:
4
+ libdir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
5
+ $:.unshift libdir unless $:.include? libdir
6
+
7
+ require 'nanite'
8
+ require 'nanite/admin'
9
+ require 'eventmachine'
10
+ require 'thin'
11
+ # IMPORTANT!
12
+ # You need raggi's patched async version of Thin at the moment to use
13
+ # the nanite-admin tool.
14
+ #
15
+ # raggi's Git repo contains a branch called 'async_for_rack' which contains the
16
+ # version of Thin you want to install. raggi has apparently removed the 'master'
17
+ # branch of his Git repo so you may see a warning like that shown below.
18
+ #
19
+ # git clone git://github.com/raggi/thin.git thin-raggi-async
20
+ # ...
21
+ # warning: remote HEAD refers to nonexistent ref, unable to checkout. <<== IGNORE THIS
22
+ #
23
+ # cd thin-raggi-async/
24
+ # git checkout --track -b async_for_rack origin/async_for_rack
25
+ # warning: You appear to be on a branch yet to be born. <<== IGNORE THIS
26
+ # warning: Forcing checkout of origin/async_for_rack. <<== IGNORE THIS
27
+ # Branch async_for_rack set up to track remote branch refs/remotes/origin/async_for_rack.
28
+ # Switched to a new branch "async_for_rack"
29
+
30
+ # run : 'rake install' to build and install the Thin gem
31
+ # cd <NANITE>
32
+ # ./bin/nanite-admin
33
+
34
+ # When you need to update this Thin install you should be able to do a 'git pull' on the
35
+ # "async_for_rack" branch.
36
+
37
+ require File.dirname(__FILE__) + '/../lib/nanite'
38
+ require 'yaml'
39
+ require "optparse"
40
+
41
+ include Nanite::CommonConfig
42
+
43
+ options = {}
44
+
45
+ opts = OptionParser.new do |opts|
46
+ opts.banner = "Usage: nanite-admin [-flags] [argument]"
47
+ opts.define_head "Nanite Admin: a basic control interface for your nanite cluster."
48
+ opts.separator '*'*80
49
+
50
+ setup_mapper_options(opts, options)
51
+
52
+ opts.on("--thin-debug", "Set the equivalent of the '--debug' flag on the Thin webserver.") do
53
+ options[:thin_debug] = true
54
+ end
55
+ end
56
+
57
+ opts.parse!
58
+
59
+ EM.run do
60
+ Nanite.start_mapper(options)
61
+ Nanite::Log.info "starting nanite-admin"
62
+ Rack::Handler::Thin.run(Nanite::Admin.new(Nanite.mapper), :Port => 4000) do
63
+ Thin::Logging.debug = options[:thin_debug]
64
+ end
65
+ end
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.dirname(__FILE__) + '/../lib/nanite'
4
+ require 'optparse'
5
+
6
+ include Nanite::CommonConfig
7
+
8
+ options = {}
9
+
10
+ opts = OptionParser.new do |opts|
11
+ opts.banner = "Usage: nanite-agent [-flag] [argument]"
12
+ opts.define_head "Nanite Agent: ruby process that acts upon messages passed to it by a mapper."
13
+ opts.separator '*'*80
14
+
15
+ setup_common_options(opts, options, 'agent')
16
+
17
+ opts.on("-n", "--nanite NANITE_ROOT", "Specify the root of your nanite agent project.") do |nanite|
18
+ options[:root] = nanite
19
+ end
20
+
21
+ opts.on("--ping-time PINGTIME", "Specify how often the agents contacts the mapper") do |ping|
22
+ options[:ping_time] = ping
23
+ end
24
+
25
+ opts.on("--actors-dir DIR", "Path to directory containing actors (NANITE_ROOT/actors by default)") do |dir|
26
+ options[:actors_dir] = dir
27
+ end
28
+
29
+ opts.on("--actors ACTORS", "Comma separated list of actors to load (all ruby files in actors directory by default)") do |a|
30
+ options[:actors] = a.split(',')
31
+ end
32
+
33
+ opts.on("--initrb FILE", "Path to agent initialization file (NANITE_ROOT/init.rb by default)") do |initrb|
34
+ options[:initrb] = initrb
35
+ end
36
+
37
+ opts.on("--single-threaded", "Run all operations in one thread") do
38
+ options[:single_threaded] = true
39
+ end
40
+
41
+ opts.on("--threadpool COUNT", Integer, "Number of threads to run all operations in") do |tps|
42
+ options[:threadpool_size] = tps
43
+ end
44
+
45
+ end
46
+
47
+ opts.parse!
48
+
49
+ if ARGV[0] == 'stop' || ARGV[0] == 'status'
50
+ agent = Nanite::Agent.new(options)
51
+ pid_file = Nanite::PidFile.new(agent.identity, agent.options)
52
+ unless pid = pid_file.read_pid
53
+ puts "#{pid_file} not found"
54
+ exit
55
+ end
56
+ if ARGV[0] == 'stop'
57
+ puts "Stopping nanite agent #{agent.identity} (pid #{pid})"
58
+ begin
59
+ Process.kill('TERM', pid)
60
+ rescue Errno::ESRCH
61
+ puts "Process does not exist (pid #{pid})"
62
+ exit
63
+ end
64
+ puts 'Done.'
65
+ else
66
+ if Process.getpgid(pid) != -1
67
+ psdata = `ps up #{pid}`.split("\n").last.split
68
+ memory = (psdata[5].to_i / 1024)
69
+ puts "The agent is alive, using #{memory}MB of memory"
70
+ else
71
+ puts "The agent is not running but has a stale pid file at #{pid_file}"
72
+ end
73
+ end
74
+ exit
75
+ end
76
+
77
+ EM.run do
78
+ Nanite.start_agent(options)
79
+ end
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.dirname(__FILE__) + '/../lib/nanite'
4
+ require 'optparse'
5
+
6
+ include Nanite::CommonConfig
7
+
8
+ options = {}
9
+
10
+ opts = OptionParser.new do |opts|
11
+ opts.banner = "Usage: nanite-mapper [-flags] [argument]"
12
+ opts.define_head "Nanite Mapper: clustered head unit for self assembling cluster of ruby processes."
13
+ opts.separator '*'*80
14
+
15
+ setup_mapper_options(opts, options)
16
+ end
17
+
18
+ opts.parse!
19
+
20
+ if ARGV[0] == 'stop' || ARGV[0] == 'status'
21
+ mapper = Nanite::Mapper.new(options)
22
+ pid_file = Nanite::PidFile.new(mapper.identity, mapper.options)
23
+ unless pid = pid_file.read_pid
24
+ puts "#{pid_file} not found"
25
+ exit
26
+ end
27
+ if ARGV[0] == 'stop'
28
+ puts "Stopping nanite mapper #{mapper.identity} (pid #{pid})"
29
+ begin
30
+ Process.kill('TERM', pid)
31
+ rescue Errno::ESRCH
32
+ puts "Process does not exist (pid #{pid})"
33
+ exit
34
+ end
35
+ puts 'Done.'
36
+ else
37
+ if Process.getpgid(pid) != -1
38
+ psdata = `ps up #{pid}`.split("\n").last.split
39
+ memory = (psdata[5].to_i / 1024)
40
+ puts "The mapper is alive, using #{memory}MB of memory"
41
+ else
42
+ puts "The mapper is not running but has a stale pid file at #{pid_file}"
43
+ end
44
+ end
45
+ exit
46
+ end
47
+
48
+ EM.run do
49
+ Nanite.start_mapper(options)
50
+ end
@@ -0,0 +1,74 @@
1
+ require 'rubygems'
2
+ require 'amqp'
3
+ require 'mq'
4
+ require 'json'
5
+ require 'logger'
6
+ require 'yaml'
7
+ require 'openssl'
8
+
9
+ $:.unshift File.dirname(__FILE__)
10
+ require 'nanite/amqp'
11
+ require 'nanite/util'
12
+ require 'nanite/config'
13
+ require 'nanite/packets'
14
+ require 'nanite/identity'
15
+ require 'nanite/console'
16
+ require 'nanite/daemonize'
17
+ require 'nanite/pid_file'
18
+ require 'nanite/job'
19
+ require 'nanite/mapper'
20
+ require 'nanite/actor'
21
+ require 'nanite/actor_registry'
22
+ require 'nanite/streaming'
23
+ require 'nanite/nanite_dispatcher'
24
+ require 'nanite/agent'
25
+ require 'nanite/cluster'
26
+ require 'nanite/reaper'
27
+ require 'nanite/log'
28
+ require 'nanite/mapper_proxy'
29
+ require 'nanite/security_provider'
30
+ require 'nanite/security/cached_certificate_store_proxy'
31
+ require 'nanite/security/certificate'
32
+ require 'nanite/security/certificate_cache'
33
+ require 'nanite/security/distinguished_name'
34
+ require 'nanite/security/encrypted_document'
35
+ require 'nanite/security/rsa_key_pair'
36
+ require 'nanite/security/secure_serializer'
37
+ require 'nanite/security/signature'
38
+ require 'nanite/security/static_certificate_store'
39
+ require 'nanite/serializer'
40
+
41
+ module Nanite
42
+ VERSION = '0.4.1.2' unless defined?(Nanite::VERSION)
43
+
44
+ class MapperNotRunning < StandardError; end
45
+
46
+ class << self
47
+ attr_reader :mapper, :agent
48
+
49
+ def start_agent(options = {})
50
+ @agent = Nanite::Agent.start(options)
51
+ end
52
+
53
+ def start_mapper(options = {})
54
+ @mapper = Nanite::Mapper.start(options)
55
+ end
56
+
57
+ def request(*args, &blk)
58
+ ensure_mapper
59
+ @mapper.request(*args, &blk)
60
+ end
61
+
62
+ def push(*args)
63
+ ensure_mapper
64
+ @mapper.push(*args)
65
+ end
66
+
67
+ def ensure_mapper
68
+ @mapper ||= MapperProxy.instance
69
+ unless @mapper
70
+ raise MapperNotRunning.new('A mapper needs to be started via Nanite.start_mapper')
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,71 @@
1
+ module Nanite
2
+ # This mixin provides Nanite actor functionality.
3
+ #
4
+ # To use it simply include it your class containing the functionality to be exposed:
5
+ #
6
+ # class Foo
7
+ # include Nanite::Actor
8
+ # expose :bar
9
+ #
10
+ # def bar(payload)
11
+ # # ...
12
+ # end
13
+ #
14
+ # end
15
+ module Actor
16
+
17
+ def self.included(base)
18
+ base.class_eval do
19
+ include Nanite::Actor::InstanceMethods
20
+ extend Nanite::Actor::ClassMethods
21
+ end # base.class_eval
22
+ end # self.included
23
+
24
+ module ClassMethods
25
+ def default_prefix
26
+ to_s.to_const_path
27
+ end
28
+
29
+ def expose(*meths)
30
+ @exposed ||= []
31
+ meths.each do |meth|
32
+ @exposed << meth unless @exposed.include?(meth)
33
+ end
34
+ end
35
+
36
+ def provides_for(prefix)
37
+ return [] unless @exposed
38
+ @exposed.select do |meth|
39
+ if instance_methods.include?(meth.to_s) or instance_methods.include?(meth.to_sym)
40
+ true
41
+ else
42
+ Nanite::Log.warn("Exposing non-existing method #{meth} in actor #{name}")
43
+ false
44
+ end
45
+ end.map {|meth| "/#{prefix}/#{meth}".squeeze('/')}
46
+ end
47
+
48
+ def on_exception(proc = nil, &blk)
49
+ raise 'No callback provided for on_exception' unless proc || blk
50
+ @exception_callback = proc || blk
51
+ end
52
+
53
+ def exception_callback
54
+ @exception_callback
55
+ end
56
+
57
+ end # ClassMethods
58
+
59
+ module InstanceMethods
60
+ # send nanite request to another agent (through the mapper)
61
+ def request(*args, &blk)
62
+ MapperProxy.instance.request(*args, &blk)
63
+ end
64
+
65
+ def push(*args)
66
+ MapperProxy.instance.push(*args)
67
+ end
68
+ end # InstanceMethods
69
+
70
+ end # Actor
71
+ end # Nanite
@@ -0,0 +1,26 @@
1
+ module Nanite
2
+ class ActorRegistry
3
+ attr_reader :actors
4
+
5
+ def initialize
6
+ @actors = {}
7
+ end
8
+
9
+ def register(actor, prefix)
10
+ raise ArgumentError, "#{actor.inspect} is not a Nanite::Actor subclass instance" unless Nanite::Actor === actor
11
+ log_msg = "[actor] #{actor.class.to_s}"
12
+ log_msg += ", prefix #{prefix}" if prefix && !prefix.empty?
13
+ Nanite::Log.info(log_msg)
14
+ prefix ||= actor.class.default_prefix
15
+ actors[prefix.to_s] = actor
16
+ end
17
+
18
+ def services
19
+ actors.map {|prefix, actor| actor.class.provides_for(prefix) }.flatten.uniq
20
+ end
21
+
22
+ def actor_for(prefix)
23
+ actor = actors[prefix]
24
+ end
25
+ end # ActorRegistry
26
+ end # Nanite