Permalink
Browse files

Initial commit of EM bindings for Warden

This provides both pure EventMachine bindings for Warden as well as
a Fiber aware client that provides a blocking api.

Test plan:
- Spec tests pass

Change-Id: I1ba12614b22f15630a9f884cb71015c8acfc6259
  • Loading branch information...
1 parent 35dcbcc commit 2d697ac1d131068a49a76dbcb5f2a7482146eb65 mpage committed Feb 15, 2012
@@ -0,0 +1,17 @@
+*.gem
+*.rbc
+.bundle
+.config
+.yardoc
+Gemfile.lock
+InstalledFiles
+_yardoc
+coverage
+doc/
+lib/bundler/man
+pkg
+rdoc
+spec/reports
+test/tmp
+test/version_tmp
+tmp
View
@@ -0,0 +1,4 @@
+source 'https://rubygems.org'
+
+# Specify your gem's dependencies in em-warden-client.gemspec
+gemspec
View
@@ -0,0 +1,22 @@
+Copyright (c) 2012 mpage
+
+MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
View
@@ -0,0 +1,29 @@
+# Em::Warden::Client
+
+TODO: Write a gem description
+
+## Installation
+
+Add this line to your application's Gemfile:
+
+ gem 'em-warden-client'
+
+And then execute:
+
+ $ bundle
+
+Or install it yourself as:
+
+ $ gem install em-warden-client
+
+## Usage
+
+TODO: Write usage instructions here
+
+## Contributing
+
+1. Fork it
+2. Create your feature branch (`git checkout -b my-new-feature`)
+3. Commit your changes (`git commit -am 'Added some feature'`)
+4. Push to the branch (`git push origin my-new-feature`)
+5. Create new Pull Request
@@ -0,0 +1,9 @@
+#!/usr/bin/env rake
+require "bundler/gem_tasks"
+require "rspec/core/rake_task"
+require "rspec/core/version"
+
+desc "Run all examples"
+RSpec::Core::RakeTask.new(:spec) do |t|
+ t.rspec_opts = %w[--color --format documentation]
+end
@@ -0,0 +1,20 @@
+require File.expand_path('../lib/em/warden/client/version', __FILE__)
+
+Gem::Specification.new do |gem|
+ gem.authors = ["mpage"]
+ gem.email = ["mpage@vmware.com"]
+ gem.description = "EM/Fiber compatible client for Warden"
+ gem.summary = "Provides EventMachine compatible code for talking with Warden"
+ gem.homepage = "http://www.cloudfoundry.com"
+
+ gem.files = Dir.glob("**/*")
+ gem.test_files = Dir.glob("spec/**/*")
+ gem.name = "em-warden-client"
+ gem.require_paths = ["lib"]
+ gem.version = EventMachine::Warden::Client::VERSION
+
+ gem.add_development_dependency('rake')
+ gem.add_development_dependency('rspec')
+ gem.add_dependency('eventmachine')
+ gem.add_dependency('yajl-ruby')
+end
@@ -0,0 +1,49 @@
+require 'eventmachine'
+require 'fiber'
+
+require 'em/warden/client/connection'
+require 'em/warden/client/error'
+
+module EventMachine
+ module Warden
+ end
+end
+
+class EventMachine::Warden::FiberAwareClient
+
+ attr_reader :socket_path
+
+ def initialize(socket_path)
+ @socket_path = socket_path
+ @connection = nil
+ end
+
+ def connect
+ return if @connection
+ @connection = EM.connect_unix_domain(@socket_path, EM::Warden::Client::Connection)
+ f = Fiber.current
+ @connection.on(:connected) { f.resume }
+ Fiber.yield
+ end
+
+ def connected?
+ @connection.connected?
+ end
+
+ def method_missing(method, *args, &blk)
+ raise EventMachine::Warden::Client::Error.new("Not connected") unless @connection.connected?
+
+ f = Fiber.current
+ @connection.call(method, *args) {|res| f.resume(res) }
+ result = Fiber.yield
+
+ result.get
+ end
+
+ def disconnect(close_after_writing=true)
+ @connection.close_connection(close_after_writing)
+ f = Fiber.current
+ @connection.on(:disconnected) { f.resume }
+ Fiber.yield
+ end
+end
@@ -0,0 +1,92 @@
+require 'eventmachine'
+require 'yajl'
+
+require 'em/warden/client/error'
+require 'em/warden/client/event_emitter'
+
+module EventMachine
+ module Warden
+ module Client
+ end
+ end
+end
+
+class EventMachine::Warden::Client::Connection < ::EM::Connection
+ include EM::Warden::Client::EventEmitter
+ include EM::Protocols::LineText2
+
+ class CommandResult
+ def initialize(value)
+ @value = value
+ end
+
+ def get
+ if @value.kind_of?(StandardError)
+ raise @value
+ else
+ @value
+ end
+ end
+ end
+
+ def post_init
+ @request_queue = []
+ @current_request = nil
+ @connected = false
+ end
+
+ def connected?
+ @connected
+ end
+
+ def connection_completed
+ @connected = true
+ emit(:connected)
+ end
+
+ def call(method, *args, &blk)
+ @request_queue << {:data => [method, *args], :callback => blk}
+ process_queue
+ end
+
+ def method_missing(method, *args, &blk)
+ call(method, *args, &blk)
+ end
+
+ def receive_line(line)
+ obj = Yajl::Parser.parse(line)
+
+ unless @current_request
+ # Should never happen
+ raise "Logic error! Received reply without a corresponding request"
+ end
+
+ if @current_request[:callback]
+ payload =
+ if obj['type'] == 'error'
+ EventMachine::Warden::Client::Error.new(obj['payload'])
+ else
+ obj['payload']
+ end
+ result = CommandResult.new(payload)
+ @current_request[:callback].call(result)
+ end
+
+ @current_request = nil
+
+ process_queue
+ end
+
+ def unbind
+ @connected = false
+ emit(:disconnected)
+ end
+
+ def process_queue
+ return if @current_request || @request_queue.empty?
+
+ @current_request = @request_queue.shift
+
+ send_data(Yajl::Encoder.encode(@current_request[:data]) + "\n")
+ end
+end
@@ -0,0 +1,8 @@
+module EventMachine
+ module Warden
+ module Client
+ class Error < StandardError
+ end
+ end
+ end
+end
@@ -0,0 +1,38 @@
+# Imported from the MIT licensed em-hiredis gem by Martyn Loughran.
+# https://github.com/mloughran/em-hiredis
+
+module EventMachine
+ module Warden
+ module Client
+ end
+ end
+end
+
+
+module EventMachine::Warden::Client::EventEmitter
+ def on(event, &listener)
+ _listeners[event] << listener
+ end
+
+ def emit(event, *args)
+ _listeners[event].each { |l| l.call(*args) }
+ end
+
+ def remove_listener(event, &listener)
+ _listeners[event].delete(listener)
+ end
+
+ def remove_all_listeners(event)
+ _listeners.delete(event)
+ end
+
+ def listeners(event)
+ _listeners[event]
+ end
+
+ private
+
+ def _listeners
+ @_listeners ||= Hash.new { |h,k| h[k] = [] }
+ end
+end
@@ -0,0 +1,7 @@
+module EventMachine
+ module Warden
+ module Client
+ VERSION = '0.0.1'
+ end
+ end
+end
@@ -0,0 +1,76 @@
+require 'spec_helper'
+require 'support/mock_warden_server'
+
+describe EventMachine::Warden::Client do
+ describe "events" do
+ it 'should emit the "connected" event upon connection completion' do
+ server = MockWardenServer.new(nil)
+ received_connected = false
+ em do
+ server.start
+ conn = server.create_connection
+ conn.on(:connected) { received_connected = true }
+ EM.stop
+ end
+ received_connected.should be_true
+ end
+
+ it 'should emit the "disconnected" event upon connection termination' do
+ server = MockWardenServer.new(nil)
+ received_disconnected =false
+ em do
+ server.start
+ conn = server.create_connection
+ conn.on(:disconnected) { received_disconnected = true }
+ EM.stop
+ end
+ received_disconnected.should be_true
+ end
+ end
+
+ describe "when connected" do
+ it 'should return non-error payloads' do
+ args = {"foo" => "bar"}
+ expected_result = ["test_result"]
+ handler = create_mock_handler(:test, :args => args, :result => expected_result)
+ server = MockWardenServer.new(handler)
+ result = nil
+ em do
+ server.start
+ conn = server.create_connection
+ conn.test(args) do |res|
+ result = res.get
+ EM.stop
+ end
+ end
+ result.should == expected_result
+ end
+
+ it 'should raise error payloads' do
+ handler = create_mock_handler(:test, :result => MockWardenServer::Error.new("test error"))
+ server = MockWardenServer.new(handler)
+ em do
+ server.start
+ conn = server.create_connection
+ conn.test do |res|
+ expect do
+ res.get
+ end.to raise_error(/test error/)
+ EM.stop
+ end
+ end
+ end
+
+ it 'should queue subsequent requests' do
+ handler = create_mock_handler(:test1)
+ handler.should_receive(:test2)
+ server = MockWardenServer.new(handler)
+ em do
+ server.start
+ conn = server.create_connection
+ conn.test1
+ conn.test2 {|res| EM.stop }
+ end
+ end
+ end
+end
Oops, something went wrong.

0 comments on commit 2d697ac

Please sign in to comment.