smith 0.5.13.1 → 0.6.1
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 +7 -0
- data/bin/agency +19 -7
- data/bin/pry-smith +11 -0
- data/bin/smithctl +19 -21
- data/lib/smith/acl_compiler.rb +101 -56
- data/lib/smith/acl_parser.rb +75 -0
- data/lib/smith/agent.rb +28 -43
- data/lib/smith/agent_cache.rb +43 -17
- data/lib/smith/agent_monitoring.rb +1 -1
- data/lib/smith/agent_process.rb +148 -53
- data/lib/smith/application/agency.rb +44 -54
- data/lib/smith/bootstrap.rb +31 -17
- data/lib/smith/cache.rb +4 -0
- data/lib/smith/command.rb +1 -3
- data/lib/smith/commands/agency/kill.rb +22 -5
- data/lib/smith/commands/agency/list.rb +9 -4
- data/lib/smith/commands/agency/logger.rb +25 -12
- data/lib/smith/commands/agency/object_count.rb +19 -8
- data/lib/smith/commands/agency/start.rb +7 -10
- data/lib/smith/commands/agency/stop.rb +30 -12
- data/lib/smith/commands/common.rb +1 -1
- data/lib/smith/commands/smithctl/acl.rb +6 -3
- data/lib/smith/commands/smithctl/dump.rb +79 -0
- data/lib/smith/commands/smithctl/firehose.rb +2 -1
- data/lib/smith/commands/smithctl/push.rb +27 -12
- data/lib/smith/commands/smithctl/status.rb +27 -0
- data/lib/smith/config.rb +140 -28
- data/lib/smith/daemon.rb +16 -3
- data/lib/smith/exceptions.rb +6 -3
- data/lib/smith/logger.rb +12 -24
- data/lib/smith/messaging/acl/agent_keepalive.proto +2 -2
- data/lib/smith/messaging/acl/agent_lifecycle.proto +15 -9
- data/lib/smith/messaging/acl/agent_stats.proto +6 -5
- data/lib/smith/messaging/acl/default.rb +2 -7
- data/lib/smith/messaging/acl_type_cache.rb +77 -0
- data/lib/smith/messaging/factory.rb +29 -0
- data/lib/smith/messaging/queue.rb +12 -10
- data/lib/smith/messaging/queue_definition.rb +21 -4
- data/lib/smith/messaging/receiver.rb +55 -62
- data/lib/smith/messaging/requeue.rb +1 -5
- data/lib/smith/messaging/sender.rb +48 -43
- data/lib/smith/messaging/util.rb +0 -10
- data/lib/smith/queue_definitions.rb +7 -4
- data/lib/smith/version.rb +1 -1
- data/lib/smith.rb +57 -56
- metadata +77 -128
- data/lib/smith/messaging/payload.rb +0 -100
    
        checksums.yaml
    ADDED
    
    | @@ -0,0 +1,7 @@ | |
| 1 | 
            +
            ---
         | 
| 2 | 
            +
            SHA1:
         | 
| 3 | 
            +
              metadata.gz: 95209344632520fac5a790a8efa4cfd91a52ddb2
         | 
| 4 | 
            +
              data.tar.gz: 562bb8a1c8bd5371fdf3d74765f8c069b88f0fa0
         | 
| 5 | 
            +
            SHA512:
         | 
| 6 | 
            +
              metadata.gz: 2a8202bf141e34265c7254ba6dfef92d7798b8d6bd9ff7b0bc903060a27d11d86d38ba8f1b1be7aede80932f10eee31f1fd21cdb1d22655e1ffc212b039543c2
         | 
| 7 | 
            +
              data.tar.gz: a5e5d205a49c1fd10ada42bf317fe17bde5cf12d375746133b909ea87892ecd7b35462f6b6c9cb8e8267872503799a8ee99ab6acaa921cac287221369d383718
         | 
    
        data/bin/agency
    CHANGED
    
    | @@ -1,11 +1,13 @@ | |
| 1 1 | 
             
            #!/usr/bin/env ruby
         | 
| 2 2 | 
             
            # -*- encoding: utf-8 -*-
         | 
| 3 3 |  | 
| 4 | 
            -
             | 
| 4 | 
            +
            require 'pathname'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            $:.unshift(Pathname.new(__FILE__).dirname.parent.join('lib').expand_path)
         | 
| 5 7 |  | 
| 6 | 
            -
            require 'pp'
         | 
| 7 8 | 
             
            require 'smith'
         | 
| 8 9 | 
             
            require 'smith/daemon'
         | 
| 10 | 
            +
            require 'smith/agent_cache'
         | 
| 9 11 | 
             
            require 'smith/acl_compiler'
         | 
| 10 12 | 
             
            require 'smith/application/agency'
         | 
| 11 13 |  | 
| @@ -21,16 +23,19 @@ module Smith | |
| 21 23 | 
             
                  @options = opts
         | 
| 22 24 | 
             
                  options_check
         | 
| 23 25 |  | 
| 24 | 
            -
                  @daemon = Daemon.new(AGENCY_NAME, opts[:daemon], opts[:pid_dir])
         | 
| 25 | 
            -
                  @agency = Agency.new | 
| 26 | 
            +
                  @daemon = Daemon.new(add_vhost(AGENCY_NAME), opts[:daemon], opts[:pid_dir])
         | 
| 27 | 
            +
                  @agency = Agency.new
         | 
| 26 28 |  | 
| 27 29 | 
             
                  Smith.shutdown_hook do
         | 
| 28 | 
            -
                     | 
| 30 | 
            +
                    logger.info { "Shutting down" }
         | 
| 29 31 | 
             
                  end
         | 
| 30 32 |  | 
| 31 33 | 
             
                  # Setup signal handlers to clean up.
         | 
| 32 34 | 
             
                  %w{TERM INT QUIT}.each do |sig|
         | 
| 33 | 
            -
                    trap sig, proc { | 
| 35 | 
            +
                    trap sig, proc {
         | 
| 36 | 
            +
                      logger.info { "Shutting down" }
         | 
| 37 | 
            +
                      @agency.stop
         | 
| 38 | 
            +
                    }
         | 
| 34 39 | 
             
                  end
         | 
| 35 40 | 
             
                end
         | 
| 36 41 |  | 
| @@ -38,10 +43,12 @@ module Smith | |
| 38 43 | 
             
                  if @daemon.running?
         | 
| 39 44 | 
             
                    logger.fatal { "The agency is alredy running" }
         | 
| 40 45 | 
             
                  else
         | 
| 46 | 
            +
                    logger.info { "Using config file: #{Smith.config_path}" }
         | 
| 47 | 
            +
                    require 'smith/messaging/acl_type_cache'
         | 
| 48 | 
            +
             | 
| 41 49 | 
             
                    @daemon.daemonise
         | 
| 42 50 |  | 
| 43 51 | 
             
                    Smith.compile_acls
         | 
| 44 | 
            -
                    Smith.load_acls
         | 
| 45 52 |  | 
| 46 53 | 
             
                    Smith.start do
         | 
| 47 54 | 
             
                      # This block is here so the that the shutdown hook added in
         | 
| @@ -66,6 +73,11 @@ module Smith | |
| 66 73 | 
             
                    puts "Logger set to stdout and daemonise is true. Log messages will be sent to /dev/null."
         | 
| 67 74 | 
             
                  end
         | 
| 68 75 | 
             
                end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                def add_vhost(name)
         | 
| 78 | 
            +
                  suffix = Pathname.new(Smith.config.amqp.broker.vhost).basename
         | 
| 79 | 
            +
                  "%s.%s" % [name, (suffix.root?) ? 'root' : suffix.to_s]
         | 
| 80 | 
            +
                end
         | 
| 69 81 | 
             
              end
         | 
| 70 82 | 
             
            end
         | 
| 71 83 |  | 
    
        data/bin/pry-smith
    ADDED
    
    
    
        data/bin/smithctl
    CHANGED
    
    | @@ -5,20 +5,22 @@ | |
| 5 5 | 
             
              trap sig, proc { exit }
         | 
| 6 6 | 
             
            end
         | 
| 7 7 |  | 
| 8 | 
            -
             | 
| 9 | 
            -
            $:.unshift( | 
| 8 | 
            +
            require 'pathname'
         | 
| 9 | 
            +
            $:.unshift(Pathname.new(__FILE__).dirname.parent.join('lib').expand_path)
         | 
| 10 10 |  | 
| 11 11 | 
             
            require 'smith'
         | 
| 12 | 
            +
            require 'smith/command'
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            require 'trollop'
         | 
| 12 15 |  | 
| 13 16 | 
             
            include Smith::Logger
         | 
| 14 17 |  | 
| 15 18 | 
             
            module Smith
         | 
| 16 19 | 
             
              class SmithControl
         | 
| 17 20 |  | 
| 18 | 
            -
             | 
| 19 21 | 
             
                def initialize(options={})
         | 
| 20 | 
            -
                  log_level((options[:log_level_given]) ? options[:log_level].to_sym : : | 
| 21 | 
            -
                  @timeout = (options[:timeout_given]) ? options[:timeout] : Smith.config. | 
| 22 | 
            +
                  log_level((options[:log_level_given]) ? options[:log_level].to_sym : :warn)
         | 
| 23 | 
            +
                  @timeout = (options[:timeout_given]) ? options[:timeout] : Smith.config.smith.timeout
         | 
| 22 24 | 
             
                end
         | 
| 23 25 |  | 
| 24 26 | 
             
                def send_command(command, args, &blk)
         | 
| @@ -37,23 +39,19 @@ module Smith | |
| 37 39 |  | 
| 38 40 | 
             
                def agency_command(command, args, &blk)
         | 
| 39 41 | 
             
                  Messaging::Sender.new(QueueDefinitions::Agency_control) do |sender|
         | 
| 42 | 
            +
                    sender.on_timeout(5) { |message_id| blk.call("Timeout. Is the agency still running?") }
         | 
| 40 43 |  | 
| 41 | 
            -
                     | 
| 42 | 
            -
             | 
| 43 | 
            -
                    sender.on_reply(:timeout => timeout) do |reply_payload, r|
         | 
| 44 | 
            -
                      r.ack
         | 
| 44 | 
            +
                    sender.on_reply(:auto_ack => true) do |reply_payload, r|
         | 
| 45 45 | 
             
                      blk.call(reply_payload[:response])
         | 
| 46 46 | 
             
                    end
         | 
| 47 47 |  | 
| 48 | 
            -
                     | 
| 49 | 
            -
                       | 
| 50 | 
            -
             | 
| 51 | 
            -
             | 
| 52 | 
            -
             | 
| 53 | 
            -
             | 
| 54 | 
            -
                     | 
| 55 | 
            -
             | 
| 56 | 
            -
                    # sender.consumers?(callback, errback)
         | 
| 48 | 
            +
                    sender.consumer_count do |count|
         | 
| 49 | 
            +
                      if count > 0
         | 
| 50 | 
            +
                        sender.publish(Smith::ACL::AgencyCommand.new(:command => command, :args => args))
         | 
| 51 | 
            +
                      else
         | 
| 52 | 
            +
                        blk.call("Agency not running.")
         | 
| 53 | 
            +
                      end
         | 
| 54 | 
            +
                    end
         | 
| 57 55 | 
             
                  end
         | 
| 58 56 | 
             
                end
         | 
| 59 57 | 
             
              end
         | 
| @@ -84,14 +82,14 @@ Usage: | |
| 84 82 | 
             
                end
         | 
| 85 83 | 
             
              end
         | 
| 86 84 |  | 
| 87 | 
            -
              command = ARGV.shift
         | 
| 88 | 
            -
              args = ARGV
         | 
| 85 | 
            +
              command = ARGV.shift.dup
         | 
| 86 | 
            +
              args = ARGV.map {|a| a.dup}
         | 
| 89 87 |  | 
| 90 88 | 
             
              %w{TERM INT QUIT}.each do |sig|
         | 
| 91 89 | 
             
                trap sig, proc { (Smith.running?) ? Smith.stop(true) : exit}
         | 
| 92 90 | 
             
              end
         | 
| 93 91 |  | 
| 94 | 
            -
              Smith. | 
| 92 | 
            +
              Smith.compile_acls
         | 
| 95 93 |  | 
| 96 94 | 
             
              control = SmithControl.new(opts)
         | 
| 97 95 |  | 
    
        data/lib/smith/acl_compiler.rb
    CHANGED
    
    | @@ -1,89 +1,134 @@ | |
| 1 1 | 
             
            # -*- encoding: utf-8 -*-
         | 
| 2 | 
            -
            require ' | 
| 3 | 
            -
            require  | 
| 4 | 
            -
             | 
| 5 | 
            -
             | 
| 6 | 
            -
            # like this: log_writing unless silent but there is no way of setting silent!
         | 
| 7 | 
            -
            # So monkey patch it.
         | 
| 8 | 
            -
            class Protobuf::Visitor::Base
         | 
| 9 | 
            -
              def log_writing(filename, message="writing..."); end
         | 
| 10 | 
            -
            end
         | 
| 2 | 
            +
            require 'ffi'
         | 
| 3 | 
            +
            require "tempfile"
         | 
| 4 | 
            +
            require 'ruby_parser'
         | 
| 5 | 
            +
            require 'smith/messaging/acl_type_cache'
         | 
| 11 6 |  | 
| 12 7 | 
             
            module Smith
         | 
| 13 8 | 
             
              class ACLCompiler
         | 
| 14 9 |  | 
| 15 10 | 
             
                include Logger
         | 
| 11 | 
            +
                extend FFI::Library
         | 
| 16 12 |  | 
| 17 | 
            -
                def  | 
| 18 | 
            -
                   | 
| 19 | 
            -
             | 
| 13 | 
            +
                def self.find_so
         | 
| 14 | 
            +
                  $:.map{|p| Pathname.new(p).join("ruby_generator.so")}.detect{|so| so.exist? }
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                ffi_lib(find_so)
         | 
| 18 | 
            +
                attach_function(:_rprotoc_extern, [:int, :pointer], :int32)
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                def initialize
         | 
| 21 | 
            +
                  @acl_type_cache = AclTypeCache.instance
         | 
| 22 | 
            +
                  @acl_parser = ACLParser.new
         | 
| 20 23 | 
             
                end
         | 
| 21 24 |  | 
| 22 | 
            -
                # Compile any protocol buffer files. This checks the timestamp to see if
         | 
| 23 | 
            -
                # the file needs compiling. This is done in a subprocess to stop the agency
         | 
| 24 | 
            -
                # from dying. I think it's a problem with the class being loaded twice but
         | 
| 25 | 
            -
                # I don't actually know that so this could be considered a bit brute force.
         | 
| 26 25 | 
             
                def compile
         | 
| 27 | 
            -
                   | 
| 28 | 
            -
             | 
| 29 | 
            -
             | 
| 30 | 
            -
             | 
| 31 | 
            -
             | 
| 32 | 
            -
             | 
| 33 | 
            -
             | 
| 34 | 
            -
             | 
| 35 | 
            -
             | 
| 36 | 
            -
             | 
| 37 | 
            -
             | 
| 38 | 
            -
             | 
| 39 | 
            -
             | 
| 40 | 
            -
                       | 
| 26 | 
            +
                  $LOAD_PATH << Smith.acl_cache_path
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  Smith.acl_path.each do |path|
         | 
| 29 | 
            +
                    $LOAD_PATH << path
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                    acls_files = path_glob(path)
         | 
| 32 | 
            +
                    out_of_date_acls = path_glob(path).select { |p| should_compile?(p) }
         | 
| 33 | 
            +
                    if out_of_date_acls.size > 0
         | 
| 34 | 
            +
                      compile_on_path(path, acls_files, out_of_date_acls)
         | 
| 35 | 
            +
                    end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                    acls_files.each do |acl_file|
         | 
| 38 | 
            +
                      acl_class_path = acl_compiled_path(acl_file)
         | 
| 39 | 
            +
                      load_acl(acl_class_path)
         | 
| 40 | 
            +
                      add_to_type_cache(acl_class_path)
         | 
| 41 41 | 
             
                    end
         | 
| 42 42 | 
             
                  end
         | 
| 43 | 
            -
                  Process.wait
         | 
| 44 | 
            -
                  @cache_path
         | 
| 45 43 | 
             
                end
         | 
| 46 44 |  | 
| 47 | 
            -
                 | 
| 48 | 
            -
                  @cache_path.to_s
         | 
| 49 | 
            -
                end
         | 
| 45 | 
            +
                private
         | 
| 50 46 |  | 
| 51 | 
            -
                 | 
| 52 | 
            -
             | 
| 53 | 
            -
                # but if it's not specified and a temporary directory was created then
         | 
| 54 | 
            -
                # the directory is removed as well.
         | 
| 55 | 
            -
                def clear_cache
         | 
| 56 | 
            -
                  logger.info { "Clearing the Protocol Buffer cache: #{Smith.acl_cache_path}" }
         | 
| 47 | 
            +
                def compile_on_path(path, acls, out_of_date_acls)
         | 
| 48 | 
            +
                  out_of_date_acls.each { |acl| logger.debug("Compiling acl: #{path.join(acl)}") }
         | 
| 57 49 |  | 
| 58 | 
            -
                   | 
| 59 | 
            -
                    path | 
| 60 | 
            -
             | 
| 50 | 
            +
                  unless acls.empty?
         | 
| 51 | 
            +
                    Dir.chdir(path) do
         | 
| 52 | 
            +
                      begin
         | 
| 53 | 
            +
                        GC.disable
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                        args = ["rprotoc", "--ruby_out", Smith.acl_cache_path, "--proto_path", path].map {|a| ::FFI::MemoryPointer.from_string(a.to_s.dup) }
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                        ffi_acls = acls.map do |acl|
         | 
| 58 | 
            +
                          FFI::MemoryPointer.from_string(acl.to_s.dup)
         | 
| 59 | 
            +
                        end
         | 
| 60 | 
            +
                        ffi_acls << nil
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                        args += ffi_acls
         | 
| 63 | 
            +
                        argv = FFI::MemoryPointer.new(:pointer, args.size)
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                        args.each_with_index { |p, index| argv[index].put_pointer(0, p) }
         | 
| 61 66 |  | 
| 62 | 
            -
             | 
| 63 | 
            -
             | 
| 67 | 
            +
                        errors = capture_stderr do
         | 
| 68 | 
            +
                          self._rprotoc_extern(args.compact.size, argv)
         | 
| 69 | 
            +
                        end.split("\n")
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                        errors.each do |error|
         | 
| 72 | 
            +
                          logger.fatal { "Cannot compile ACLs: #{error}" }
         | 
| 73 | 
            +
                          raise RuntimeError, error
         | 
| 74 | 
            +
                        end
         | 
| 75 | 
            +
                      ensure
         | 
| 76 | 
            +
                        GC.enable
         | 
| 77 | 
            +
                      end
         | 
| 78 | 
            +
                    end
         | 
| 64 79 | 
             
                  end
         | 
| 65 80 | 
             
                end
         | 
| 66 81 |  | 
| 67 | 
            -
                private
         | 
| 68 | 
            -
             | 
| 69 82 | 
             
                # Returns true if the .proto file is newer that the .pb.rb file
         | 
| 70 83 | 
             
                def should_compile?(file)
         | 
| 71 | 
            -
                  cached_file =  | 
| 84 | 
            +
                  cached_file = Smith.acl_cache_path.join(file.basename).sub_ext(".pb.rb")
         | 
| 72 85 | 
             
                  if cached_file.exist?
         | 
| 73 | 
            -
                     | 
| 74 | 
            -
                      true
         | 
| 75 | 
            -
                    else
         | 
| 76 | 
            -
                      false
         | 
| 77 | 
            -
                    end
         | 
| 86 | 
            +
                    file.mtime > cached_file.mtime
         | 
| 78 87 | 
             
                  else
         | 
| 79 88 | 
             
                    true
         | 
| 80 89 | 
             
                  end
         | 
| 81 90 | 
             
                end
         | 
| 82 91 |  | 
| 83 92 | 
             
                def path_glob(path)
         | 
| 84 | 
            -
                  Pathname.glob( | 
| 85 | 
            -
             | 
| 93 | 
            +
                  Pathname.glob(path.join("*.proto")).map { |acl| acl.realpath }
         | 
| 94 | 
            +
                end
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                # This is not idea but I really don't know how else to do it. I cannot use
         | 
| 97 | 
            +
                # $stderr = StringIO.new as this only seems to capture STDERR if you
         | 
| 98 | 
            +
                # explicitly write to STDERR. In my case it's an ffi library call that's
         | 
| 99 | 
            +
                # writing to STDERR.
         | 
| 100 | 
            +
                def capture_stderr
         | 
| 101 | 
            +
                  org_stderr = STDERR.dup
         | 
| 102 | 
            +
                  begin
         | 
| 103 | 
            +
                    tmp = Tempfile.new('')
         | 
| 104 | 
            +
                    tmp.sync = true
         | 
| 105 | 
            +
                    STDERR.reopen(tmp)
         | 
| 106 | 
            +
                    yield
         | 
| 107 | 
            +
                    File.read(tmp.path)
         | 
| 108 | 
            +
                  ensure
         | 
| 109 | 
            +
                    STDERR.reopen(org_stderr)
         | 
| 86 110 | 
             
                  end
         | 
| 87 111 | 
             
                end
         | 
| 112 | 
            +
             | 
| 113 | 
            +
                def load_acl(path)
         | 
| 114 | 
            +
                  logger.verbose { "Loading ACL: #{path}" }
         | 
| 115 | 
            +
                  require path
         | 
| 116 | 
            +
                end
         | 
| 117 | 
            +
             | 
| 118 | 
            +
                def add_to_type_cache(path)
         | 
| 119 | 
            +
                  acl_class = File.read(path)
         | 
| 120 | 
            +
                  @acl_parser.go(acl_class)
         | 
| 121 | 
            +
                  @acl_parser.fully_qualified_classes.each do |clazz|
         | 
| 122 | 
            +
                    @acl_type_cache.add(to_class(clazz))
         | 
| 123 | 
            +
                  end
         | 
| 124 | 
            +
                end
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                def to_class(type_as_array)
         | 
| 127 | 
            +
                  type_as_array.inject(Kernel) { |acc, t| acc.const_get(t) }
         | 
| 128 | 
            +
                end
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                def acl_compiled_path(path)
         | 
| 131 | 
            +
                  "#{Smith.acl_cache_path.join(path.basename('.proto'))}.pb.rb"
         | 
| 132 | 
            +
                end
         | 
| 88 133 | 
             
              end
         | 
| 89 134 | 
             
            end
         | 
| @@ -0,0 +1,75 @@ | |
| 1 | 
            +
            # -*- encoding: utf-8 -*-
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'set'
         | 
| 4 | 
            +
            require 'sexp_processor'
         | 
| 5 | 
            +
            require 'ruby_parser'
         | 
| 6 | 
            +
            require 'optparse'
         | 
| 7 | 
            +
            require 'timeout'
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            module Smith
         | 
| 10 | 
            +
              class ACLParser < SexpProcessor
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                def initialize
         | 
| 13 | 
            +
                  super()
         | 
| 14 | 
            +
                  @class_stack         = []
         | 
| 15 | 
            +
                  self.auto_shift_type = true
         | 
| 16 | 
            +
                  self.reset
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                def go(ruby)
         | 
| 20 | 
            +
                  @parser = RubyParser.new
         | 
| 21 | 
            +
                  process(@parser.process(ruby))
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                # Adds name to the class stack, for the duration of the block
         | 
| 25 | 
            +
                def in_class(name)
         | 
| 26 | 
            +
                  @class_stack.unshift(name)
         | 
| 27 | 
            +
                  yield
         | 
| 28 | 
            +
                  @class_stack.shift
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                # Returns the first class in the list, or :main
         | 
| 32 | 
            +
                def class_name
         | 
| 33 | 
            +
                  if @class_stack.any?
         | 
| 34 | 
            +
                    @class_stack.reverse
         | 
| 35 | 
            +
                  else
         | 
| 36 | 
            +
                    :main
         | 
| 37 | 
            +
                  end
         | 
| 38 | 
            +
                end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                # Process each element of #exp in turn.
         | 
| 41 | 
            +
                def process_until_empty(exp)
         | 
| 42 | 
            +
                  process(exp.shift) until exp.empty?
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                def fully_qualified_classes
         | 
| 46 | 
            +
                  @classes.delete(:main)
         | 
| 47 | 
            +
                  @classes.inject([]) do |a, class_method|
         | 
| 48 | 
            +
                    a.tap do |acc|
         | 
| 49 | 
            +
                      acc << class_method
         | 
| 50 | 
            +
                    end
         | 
| 51 | 
            +
                  end
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                # Reset @classes data
         | 
| 55 | 
            +
                def reset
         | 
| 56 | 
            +
                  @classes = Set.new
         | 
| 57 | 
            +
                end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                # Process Class method
         | 
| 60 | 
            +
                def process_class(exp)
         | 
| 61 | 
            +
                  in_class(exp.shift) do
         | 
| 62 | 
            +
                    process_until_empty exp
         | 
| 63 | 
            +
                    @classes << class_name
         | 
| 64 | 
            +
                  end
         | 
| 65 | 
            +
                  s()
         | 
| 66 | 
            +
                end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                def process_module(exp)
         | 
| 69 | 
            +
                  in_class exp.shift do
         | 
| 70 | 
            +
                    process_until_empty exp
         | 
| 71 | 
            +
                  end
         | 
| 72 | 
            +
                  s()
         | 
| 73 | 
            +
                end
         | 
| 74 | 
            +
              end
         | 
| 75 | 
            +
            end
         | 
    
        data/lib/smith/agent.rb
    CHANGED
    
    | @@ -6,11 +6,12 @@ module Smith | |
| 6 6 | 
             
                include Logger
         | 
| 7 7 | 
             
                include Smith::ObjectCount
         | 
| 8 8 |  | 
| 9 | 
            -
                attr_reader : | 
| 9 | 
            +
                attr_reader :name, :pid, :uuid
         | 
| 10 10 |  | 
| 11 | 
            -
                def initialize( | 
| 11 | 
            +
                def initialize(uuid)
         | 
| 12 12 | 
             
                  @name = self.class.to_s
         | 
| 13 13 | 
             
                  @pid = $$
         | 
| 14 | 
            +
                  @uuid = uuid
         | 
| 14 15 |  | 
| 15 16 | 
             
                  @factory = QueueFactory.new
         | 
| 16 17 |  | 
| @@ -67,16 +68,9 @@ module Smith | |
| 67 68 | 
             
                  @on_running = blk
         | 
| 68 69 | 
             
                end
         | 
| 69 70 |  | 
| 70 | 
            -
                # Override this method to implement your own agent. | 
| 71 | 
            -
                # go away in the future. This method must not block.
         | 
| 71 | 
            +
                # Override this method to implement your own agent.
         | 
| 72 72 | 
             
                def run
         | 
| 73 | 
            -
                  raise ArgumentError, "You  | 
| 74 | 
            -
             | 
| 75 | 
            -
                  logger.debug { "Setting up default queue: #{default_queue_name}" }
         | 
| 76 | 
            -
             | 
| 77 | 
            -
                  subscribe(default_queue_name, :auto_delete => false) do |r|
         | 
| 78 | 
            -
                    @@task.call(r.payload)
         | 
| 79 | 
            -
                  end
         | 
| 73 | 
            +
                  raise ArgumentError, "You must override this method"
         | 
| 80 74 | 
             
                end
         | 
| 81 75 |  | 
| 82 76 | 
             
                def install_signal_handler(signal, position=:end, &blk)
         | 
| @@ -107,16 +101,14 @@ module Smith | |
| 107 101 | 
             
                end
         | 
| 108 102 |  | 
| 109 103 | 
             
                class << self
         | 
| 110 | 
            -
                  def task(opts={}, &blk)
         | 
| 111 | 
            -
                    @@task = blk
         | 
| 112 | 
            -
                  end
         | 
| 113 | 
            -
             | 
| 114 104 | 
             
                  # Options supported:
         | 
| 115 105 | 
             
                  # :monitor,   the agency will monitor the agent & if dies restart.
         | 
| 116 106 | 
             
                  # :singleton, only every have one agent. If this is set to false
         | 
| 117 107 | 
             
                  #             multiple agents are allowed.
         | 
| 118 108 | 
             
                  def options(opts)
         | 
| 119 | 
            -
                     | 
| 109 | 
            +
                    opts.each do |k, v|
         | 
| 110 | 
            +
                      Smith.config.agent.send("#{k}=", v)
         | 
| 111 | 
            +
                    end
         | 
| 120 112 | 
             
                  end
         | 
| 121 113 | 
             
                end
         | 
| 122 114 |  | 
| @@ -128,9 +120,9 @@ module Smith | |
| 128 120 | 
             
                end
         | 
| 129 121 |  | 
| 130 122 | 
             
                def setup_control_queue
         | 
| 131 | 
            -
                  logger.debug { "Setting up control queue: #{ | 
| 123 | 
            +
                  logger.debug { "Setting up control queue: #{control_queue_def.denormalise}" }
         | 
| 132 124 |  | 
| 133 | 
            -
                  Messaging::Receiver.new( | 
| 125 | 
            +
                  Messaging::Receiver.new(control_queue_def) do |receiver|
         | 
| 134 126 | 
             
                    receiver.subscribe do |payload|
         | 
| 135 127 | 
             
                      logger.debug { "Command received on agent control queue: #{payload.command} #{payload.options}" }
         | 
| 136 128 |  | 
| @@ -142,7 +134,7 @@ module Smith | |
| 142 134 | 
             
                      when 'log_level'
         | 
| 143 135 | 
             
                        begin
         | 
| 144 136 | 
             
                          level = payload.options.first
         | 
| 145 | 
            -
                          logger.info { "Setting log level to #{level} for: #{name}" }
         | 
| 137 | 
            +
                          logger.info { "Setting log level to #{level} for: #{name} (#{uuid})" }
         | 
| 146 138 | 
             
                          log_level(level)
         | 
| 147 139 | 
             
                        rescue ArgumentError => e
         | 
| 148 140 | 
             
                          logger.error { "Incorrect log level: #{level}" }
         | 
| @@ -155,20 +147,18 @@ module Smith | |
| 155 147 | 
             
                end
         | 
| 156 148 |  | 
| 157 149 | 
             
                def setup_stats_queue
         | 
| 158 | 
            -
                  puts "setting stats"
         | 
| 159 | 
            -
                  # instantiate this queue without using the factory so it doesn't show
         | 
| 160 | 
            -
                  # up in the stats.
         | 
| 161 150 | 
             
                  Messaging::Sender.new(QueueDefinitions::Agent_stats) do |stats_queue|
         | 
| 162 151 | 
             
                    EventMachine.add_periodic_timer(2) do
         | 
| 163 152 | 
             
                      stats_queue.number_of_consumers do |consumers|
         | 
| 164 153 | 
             
                        if consumers > 0
         | 
| 165 | 
            -
                          payload = ACL:: | 
| 154 | 
            +
                          payload = ACL::AgentStats.new.tap do |p|
         | 
| 155 | 
            +
                            p.uuid = uuid
         | 
| 166 156 | 
             
                            p.agent_name = self.name
         | 
| 167 157 | 
             
                            p.pid = self.pid
         | 
| 168 158 | 
             
                            p.rss = (File.read("/proc/#{pid}/statm").split[1].to_i * 4) / 1024 # This assumes the page size is 4K & is MB
         | 
| 169 159 | 
             
                            p.up_time = (Time.now - @start_time).to_i
         | 
| 170 | 
            -
                             | 
| 171 | 
            -
                              p.queues << ACL:: | 
| 160 | 
            +
                            queues.each_queue do |q|
         | 
| 161 | 
            +
                              p.queues << ACL::AgentStats::QueueStats.new(:name => q.name, :type => q.class.to_s, :length => q.counter)
         | 
| 172 162 | 
             
                            end
         | 
| 173 163 | 
             
                          end
         | 
| 174 164 |  | 
| @@ -180,24 +170,23 @@ module Smith | |
| 180 170 | 
             
                end
         | 
| 181 171 |  | 
| 182 172 | 
             
                def acknowledge_start(&blk)
         | 
| 183 | 
            -
                  Messaging::Sender.new(QueueDefinitions::Agent_lifecycle) do | | 
| 184 | 
            -
                    payload = ACL:: | 
| 185 | 
            -
                      p. | 
| 173 | 
            +
                  Messaging::Sender.new(QueueDefinitions::Agent_lifecycle) do |queue|
         | 
| 174 | 
            +
                    payload = ACL::AgentAcknowledgeStart.new.tap do |p|
         | 
| 175 | 
            +
                      p.uuid = uuid
         | 
| 186 176 | 
             
                      p.pid = $$
         | 
| 187 | 
            -
                      p.name = self.class.to_s
         | 
| 188 | 
            -
                      p.metadata = Smith.config.agent.metadata
         | 
| 189 | 
            -
                      p.monitor = Smith.config.agent.monitor
         | 
| 190 177 | 
             
                      p.singleton = Smith.config.agent.singleton
         | 
| 191 178 | 
             
                      p.started_at = Time.now.to_i
         | 
| 179 | 
            +
                      p.metadata = Smith.config.agent.metadata
         | 
| 180 | 
            +
                      p.monitor = Smith.config.agent.monitor
         | 
| 192 181 | 
             
                    end
         | 
| 193 | 
            -
                     | 
| 182 | 
            +
                    queue.publish(payload)
         | 
| 194 183 | 
             
                  end
         | 
| 195 184 | 
             
                end
         | 
| 196 185 |  | 
| 197 186 | 
             
                def acknowledge_stop(&blk)
         | 
| 198 | 
            -
                  Messaging::Sender.new(QueueDefinitions::Agent_lifecycle) do | | 
| 187 | 
            +
                  Messaging::Sender.new(QueueDefinitions::Agent_lifecycle) do |queue|
         | 
| 199 188 | 
             
                    message = {:state => 'acknowledge_stop', :pid => $$, :name => self.class.to_s}
         | 
| 200 | 
            -
                     | 
| 189 | 
            +
                    queue.publish(ACL::AgentAcknowledgeStop.new(:uuid => uuid), &blk)
         | 
| 201 190 | 
             
                  end
         | 
| 202 191 | 
             
                end
         | 
| 203 192 |  | 
| @@ -205,10 +194,10 @@ module Smith | |
| 205 194 | 
             
                  if Smith.config.agent.monitor
         | 
| 206 195 | 
             
                    EventMachine::add_periodic_timer(1) do
         | 
| 207 196 | 
             
                      Messaging::Sender.new(QueueDefinitions::Agent_keepalive) do |queue|
         | 
| 208 | 
            -
                        message = {:name => self.class.to_s, : | 
| 209 | 
            -
                         | 
| 197 | 
            +
                        message = {:name => self.class.to_s, :uuid => uuid, :time => Time.now.to_i}
         | 
| 198 | 
            +
                        queue.consumers do |consumers|
         | 
| 210 199 | 
             
                          if consumers > 0
         | 
| 211 | 
            -
                             | 
| 200 | 
            +
                            queue.publish(ACL::AgentKeepalive, message)
         | 
| 212 201 | 
             
                          end
         | 
| 213 202 | 
             
                        end
         | 
| 214 203 | 
             
                      end
         | 
| @@ -222,12 +211,8 @@ module Smith | |
| 222 211 | 
             
                  @factory
         | 
| 223 212 | 
             
                end
         | 
| 224 213 |  | 
| 225 | 
            -
                def  | 
| 226 | 
            -
                   | 
| 227 | 
            -
                end
         | 
| 228 | 
            -
             | 
| 229 | 
            -
                def default_queue_name
         | 
| 230 | 
            -
                  "agent.#{name.sub(/Agent$/, '').snake_case}"
         | 
| 214 | 
            +
                def control_queue_def
         | 
| 215 | 
            +
                  @control_queue_def ||= QueueDefinitions::Agent_control.call(uuid)
         | 
| 231 216 | 
             
                end
         | 
| 232 217 | 
             
              end
         | 
| 233 218 | 
             
            end
         | 
    
        data/lib/smith/agent_cache.rb
    CHANGED
    
    | @@ -1,40 +1,66 @@ | |
| 1 1 | 
             
            # -*- encoding: utf-8 -*-
         | 
| 2 | 
            +
            require 'tdb'
         | 
| 3 | 
            +
            require 'securerandom'
         | 
| 4 | 
            +
             | 
| 2 5 | 
             
            module Smith
         | 
| 3 | 
            -
              class AgentCache | 
| 6 | 
            +
              class AgentCache
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                include Enumerable
         | 
| 4 9 |  | 
| 5 10 | 
             
                attr_accessor :path
         | 
| 6 11 |  | 
| 7 12 | 
             
                def initialize(opts={})
         | 
| 8 | 
            -
                   | 
| 9 | 
            -
                  @ | 
| 13 | 
            +
                  # @db = LevelDB::DB.make(Smith.cache_path.join('agent_state').to_s, :error_if_exists => false, :create_if_missing => true)
         | 
| 14 | 
            +
                  @db = TDB.new(Smith.cache_path.join('agent_state.tdb').to_s)
         | 
| 15 | 
            +
                end
         | 
| 10 16 |  | 
| 11 | 
            -
             | 
| 17 | 
            +
                def create(name)
         | 
| 18 | 
            +
                  AgentProcess.new(@db, :name => name, :uuid => SecureRandom.uuid)
         | 
| 19 | 
            +
                end
         | 
| 12 20 |  | 
| 13 | 
            -
             | 
| 21 | 
            +
                def alive?(uuid)
         | 
| 22 | 
            +
                  (@db.include?(uuid)) ? instantiate(@db[uuid]).alive? : false
         | 
| 14 23 | 
             
                end
         | 
| 15 24 |  | 
| 16 | 
            -
                def  | 
| 17 | 
            -
                   | 
| 25 | 
            +
                def exist?(uuid)
         | 
| 26 | 
            +
                  @db.include?(uuid)
         | 
| 18 27 | 
             
                end
         | 
| 19 28 |  | 
| 29 | 
            +
                def find_by_name(*names)
         | 
| 30 | 
            +
                  inject([]) do |a, agent|
         | 
| 31 | 
            +
                    a.tap do |acc|
         | 
| 32 | 
            +
                      names.flatten.each do |name|
         | 
| 33 | 
            +
                        acc << agent if name == agent.name
         | 
| 34 | 
            +
                      end
         | 
| 35 | 
            +
                    end
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                  # select {|a| a.name == name.to_s }
         | 
| 40 | 
            +
                # end
         | 
| 41 | 
            +
             | 
| 20 42 | 
             
                def state(state)
         | 
| 21 43 | 
             
                  select {|a| a.state == state.to_s }
         | 
| 22 44 | 
             
                end
         | 
| 23 45 |  | 
| 24 | 
            -
                 | 
| 25 | 
            -
             | 
| 46 | 
            +
                def delete(uuid)
         | 
| 47 | 
            +
                  @db.delete(uuid)
         | 
| 48 | 
            +
                end
         | 
| 26 49 |  | 
| 27 | 
            -
                 | 
| 50 | 
            +
                def entry(uuid)
         | 
| 51 | 
            +
                  (uuid) ? instantiate(@db[uuid]) : nil
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                alias :[] :entry
         | 
| 28 55 |  | 
| 29 | 
            -
                 | 
| 30 | 
            -
             | 
| 31 | 
            -
                  AgentProcess.all.each { |a| update(a.name, a) }
         | 
| 56 | 
            +
                def each(&blk)
         | 
| 57 | 
            +
                  @db.each {|k,v| blk.call(instantiate(v)) }
         | 
| 32 58 | 
             
                end
         | 
| 33 59 |  | 
| 34 | 
            -
                 | 
| 35 | 
            -
             | 
| 36 | 
            -
             | 
| 37 | 
            -
                   | 
| 60 | 
            +
                private
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                def instantiate(state)
         | 
| 63 | 
            +
                  (state) ? AgentProcess.new(@db, state) : nil
         | 
| 38 64 | 
             
                end
         | 
| 39 65 | 
             
              end
         | 
| 40 66 | 
             
            end
         | 
| @@ -38,7 +38,7 @@ module Smith | |
| 38 38 | 
             
                          logger.info { "Restarting dead agent: #{agent_process.name}" }
         | 
| 39 39 | 
             
                          Messaging::Sender.new(QueueDefinitions::Agency_control) do |sender|
         | 
| 40 40 | 
             
                            sender.on_reply { |p, r| logger.debug { "Agent restart message acknowledged: #{agent_process.name}" } }
         | 
| 41 | 
            -
                            sender.publish(ACL:: | 
| 41 | 
            +
                            sender.publish(ACL::AgencyCommand.new(:command => 'start', :args => [agent_process.name]))
         | 
| 42 42 | 
             
                          end
         | 
| 43 43 | 
             
                        when 'unknown'
         | 
| 44 44 | 
             
                          logger.info { "Agent is in an unknown state: #{agent_process.name}" }
         |