diff options
-rw-r--r-- | .travis.yml | 13 | ||||
-rw-r--r-- | Gemfile.lock | 6 | ||||
-rw-r--r-- | Rakefile | 2 | ||||
-rwxr-xr-x | bin/tapicero | 13 | ||||
-rw-r--r-- | lib/tapicero.rb | 26 | ||||
-rw-r--r-- | lib/tapicero/user_database.rb | 53 | ||||
-rw-r--r-- | lib/tapicero/user_event_handler.rb | 44 | ||||
-rw-r--r-- | lib/tapicero/version.rb | 3 | ||||
-rw-r--r-- | lib/tapicero_daemon.rb | 26 | ||||
-rw-r--r-- | test/config.yaml | 19 | ||||
-rw-r--r-- | test/config/config.yaml | 5 | ||||
-rw-r--r-- | test/integration/tapicero_test.rb | 37 | ||||
-rw-r--r-- | test/integration/test_setup_test.rb | 15 | ||||
-rw-r--r-- | test/setup_couch.sh | 13 | ||||
-rw-r--r-- | test/support/integration_test.rb | 39 | ||||
-rw-r--r-- | test/test_helper.rb | 15 | ||||
-rw-r--r-- | test/unit/couch_changes_test.rb | 32 | ||||
-rw-r--r-- | test/unit/couch_stream_test.rb | 29 |
18 files changed, 249 insertions, 141 deletions
diff --git a/.travis.yml b/.travis.yml index 984e24a..3fd27a7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,2 +1,15 @@ +rvm: + - "1.9.3" services: - couchdb +notifications: + email: false +before_script: + - "/bin/bash test/setup_couch.sh" + - "touch /tmp/tapicero_test.log" + - "bundle exec bin/tapicero start test/config.yaml" + - "bundle exec bin/tapicero status" + - "cat /tmp/tapicero_test.log" +after_script: + - "cat /tmp/tapicero_test.log" + - "cat /tmp/tapicero_test.seq" diff --git a/Gemfile.lock b/Gemfile.lock index afbb265..917a4f7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,9 +1,9 @@ PATH remote: . specs: - tapicero (0.3.1) + tapicero (0.3.2) couchrest (~> 1.1.3) - couchrest_changes (~> 0.0.4) + couchrest_changes (~> 0.0.5) daemons syslog_logger (~> 2.0.0) yajl-ruby (~> 1.1.0) @@ -15,7 +15,7 @@ GEM mime-types (~> 1.15) multi_json (~> 1.0) rest-client (~> 1.6.1) - couchrest_changes (0.0.4) + couchrest_changes (0.0.5) couchrest (~> 1.1.3) syslog_logger (~> 2.0.0) yajl-ruby @@ -76,7 +76,7 @@ end ## Rake::TestTask.new do |t| - t.pattern = "test/unit/*_test.rb" + t.pattern = "test/*/*_test.rb" end task :default => :test diff --git a/bin/tapicero b/bin/tapicero index c20a114..72f974b 100755 --- a/bin/tapicero +++ b/bin/tapicero @@ -1,10 +1,10 @@ -#!/usr/bin/ruby +#!/usr/bin/env ruby -require 'pathname' # # Tapicero Daemon # +require 'pathname' BASE_DIR = Pathname.new(__FILE__).realpath + '../..' begin @@ -46,7 +46,8 @@ end # --overwrite-security overwrite existing couch security settings # TODO: not implemented yet: # --overwrite-designs overwrite existing design documents -Tapicero::FLAGS.concat ARGV.grep(/--.*/) +Tapicero::FLAGS.concat ARGV.grep(/--.*/) +Tapicero::CONFIGS.concat ARGV.grep(/\.ya?ml$/) # if flags have been set but an action is missing we assume # tapicero should run in foreground. @@ -59,7 +60,6 @@ end # # Start the daemon # -require 'tapicero' # so we can use Tapicero.logger below. require 'daemons' if ENV["USER"] == "root" options = {:app_name => 'tapicero', :dir_mode => :system} # this will put the pid file in /var/run @@ -70,7 +70,6 @@ begin Daemons.run("#{BASE_DIR}/lib/tapicero_daemon.rb", options) rescue SystemExit rescue Exception => exc - Tapicero.logger.error "Uncaught exception. Daemon will die." - Tapicero.logger.error exc.class.name + ": " + exc.to_s - Tapicero.logger.error exc.backtrace.join("\n") + puts exc.class.name + exc.to_s + puts exc.backtrace.join("\n") end diff --git a/lib/tapicero.rb b/lib/tapicero.rb index e2a8f38..a098287 100644 --- a/lib/tapicero.rb +++ b/lib/tapicero.rb @@ -1,9 +1,6 @@ unless defined? BASE_DIR BASE_DIR = Pathname.new(__FILE__) + '../..' end -unless defined? TAPICERO_CONFIG - TAPICERO_CONFIG = '/etc/leap/tapicero.yaml' -end module Tapicero class <<self @@ -11,27 +8,24 @@ module Tapicero attr_accessor :config end - + # reraise exceptions instead of retrying + # used in tests + unless defined? RERAISE + RERAISE = false + end # # Load Config # this must come first, because CouchRest needs the connection # defined before the models are defined. # require 'couchrest/changes' - configs = ['config/default.yaml', TAPICERO_CONFIG, ARGV.grep(/\.ya?ml$/).first] - self.config = CouchRest::Changes::Config.load(BASE_DIR, *configs) + self.config = CouchRest::Changes::Config.load(BASE_DIR, *CONFIGS) self.logger = CouchRest::Changes::Config.logger # hand flags over to CouchRest::Changes - config.flags = FLAGS - puts "flags: #{FLAGS}" if FLAGS.any? - - # - # Load Tapicero Parts - # - require 'tapicero/user_database' - - def self.user_database(id) - UserDatabase.new(config.couch_host, config.options[:db_prefix] + id) + if defined? FLAGS + config.flags = FLAGS + puts "flags: #{FLAGS}" if FLAGS.any? end + end diff --git a/lib/tapicero/user_database.rb b/lib/tapicero/user_database.rb index 3cfdd70..26d013d 100644 --- a/lib/tapicero/user_database.rb +++ b/lib/tapicero/user_database.rb @@ -4,16 +4,8 @@ require 'json' module Tapicero class UserDatabase - def initialize(host, name) - @host = host - @name = name - end - - def prepare(config) - create - secure(config.options[:security]) - add_design_docs - Tapicero.logger.info "Prepared storage " + name + def initialize(user_id) + @db = couch.database(config.options[:db_prefix] + user_id) end def create @@ -22,7 +14,8 @@ module Tapicero end end - def secure(security) + def secure(security = nil) + security ||= config.options[:security] # let's not overwrite if we have a security doc already return if secured? && !Tapicero::FLAGS.include?('--overwrite-security') retry_request_once "Writing security to" do @@ -42,9 +35,9 @@ module Tapicero end def upload_design_doc(file) - url = design_url(file.basename('.json')) - CouchRest.put url, JSON.parse(file.read) - rescue RestClient::Conflict + old = CouchRest.get design_url(file.basename('.json')) + rescue RestClient::ResourceNotFound + CouchRest.put design_url(file.basename('.json')), JSON.parse(file.read) end @@ -54,28 +47,33 @@ module Tapicero end end + def name + db.name + end + protected def create_db - CouchRest.new(host).create_db(name) - rescue RestClient::PreconditionFailed # database already existed + db.info # test if db exists + rescue RestClient::ResourceNotFound + couch.create_db(db.name) end def delete_db - db = CouchRest.new(host).database(name) db.delete! if db rescue RestClient::ResourceNotFound # no database found end def retry_request_once(action) second_try ||= false - Tapicero.logger.debug "#{action} #{name}" + Tapicero.logger.debug "#{action} #{db.name}" yield rescue RestClient::Exception => exc + raise exc if Tapicero::RERAISE if second_try - log_error "#{action} #{name} failed twice due to:", exc + log_error "#{action} #{db.name} failed twice due to:", exc else - log_error "#{action} #{name} failed due to:", exc + log_error "#{action} #{db.name} failed due to:", exc sleep 5 second_try = true retry @@ -95,13 +93,22 @@ module Tapicero end def security_url - "#{host}/#{name}/_security" + db.root + "/_security" end def design_url(doc_name) - "#{host}/#{name}/_design/#{doc_name}" + db.root + "/_design/#{doc_name}" + end + + attr_reader :db + + def couch + @couch ||= CouchRest.new(config.couch_host) + end + + def config + Tapicero.config end - attr_reader :host, :name end end diff --git a/lib/tapicero/user_event_handler.rb b/lib/tapicero/user_event_handler.rb new file mode 100644 index 0000000..38cf8f8 --- /dev/null +++ b/lib/tapicero/user_event_handler.rb @@ -0,0 +1,44 @@ +require 'tapicero/user_database' +module Tapicero + class UserEventHandler + def initialize(users) + users.created do |hash| + prepare_db(hash['id']) + end + + # Sometimes changes log starts with rev 2. So the + # detection of is currently not working properly + # Working around this until a new version of + # couchrest changes takes this into account. + users.updated do |hash| + prepare_db(hash['id']) + end + + users.deleted do |hash| + destroy_db(hash['id']) + end + end + + protected + + def prepare_db(id) + db = user_database(id) + db.create + db.secure + db.add_design_docs + logger.info "Prepared storage " + db.name + end + + def destroy_db(id) + user_database(id).destroy + end + + def logger + Tapicero.logger + end + + def user_database(id) + UserDatabase.new(id) + end + end +end diff --git a/lib/tapicero/version.rb b/lib/tapicero/version.rb index 1d372e5..8b7a6b0 100644 --- a/lib/tapicero/version.rb +++ b/lib/tapicero/version.rb @@ -1,5 +1,6 @@ module Tapicero - VERSION = "0.3.2" + VERSION = "0.4.0" REQUIRE_PATHS = ['lib'] FLAGS = [] + CONFIGS = ['config/default.yaml', '/etc/leap/tapicero.yaml'] end diff --git a/lib/tapicero_daemon.rb b/lib/tapicero_daemon.rb index 23248e3..89566de 100644 --- a/lib/tapicero_daemon.rb +++ b/lib/tapicero_daemon.rb @@ -4,30 +4,14 @@ # # Daemons.run('tapicero_daemon.rb') # - require 'tapicero' module Tapicero - users = CouchRest::Changes.new('users') - - users.created do |hash| - logger.debug "Created user " + hash['id'] - user_database(hash['id']).prepare(config) - end + module Daemon + require 'tapicero/user_event_handler' + users = CouchRest::Changes.new('users') + UserEventHandler.new(users) + users.listen - # Sometimes changes log starts with rev 2. So the - # detection of is currently not working properly - # Working around this until a new version of - # couchrest changes takes this into account. - users.updated do |hash| - logger.debug "Updated user " + hash['id'] - user_database(hash['id']).prepare(config) end - - users.deleted do |hash| - logger.debug "Deleted user " + hash['id'] - user_database(hash['id']).destroy - end - - users.listen end diff --git a/test/config.yaml b/test/config.yaml new file mode 100644 index 0000000..e59b4de --- /dev/null +++ b/test/config.yaml @@ -0,0 +1,19 @@ +# +# Default configuration options for Tapicero +# + +# couch connection configuration +connection: + protocol: "http" + host: "localhost" + port: 5984 + username: anna + password: secret + prefix: "tapicero_test" + suffix: "" + +# file to store the last processed user record in so we can resume after +# a restart: +seq_file: "/tmp/tapicero_test.seq" +log_file: "/tmp/tapicero_test.log" +log_level: debug diff --git a/test/config/config.yaml b/test/config/config.yaml deleted file mode 100644 index ee10fe6..0000000 --- a/test/config/config.yaml +++ /dev/null @@ -1,5 +0,0 @@ -# -# testing configuration options -# - -db_prefix: "tapicero_test-" diff --git a/test/integration/tapicero_test.rb b/test/integration/tapicero_test.rb new file mode 100644 index 0000000..88e3715 --- /dev/null +++ b/test/integration/tapicero_test.rb @@ -0,0 +1,37 @@ +require_relative '../test_helper.rb' + +class TapiceroTest < Tapicero::IntegrationTest + + def setup + create_user + end + + def teardown + delete_user(true) + end + + def test_creates_user_db + assert user_database + assert user_database.name.start_with?(config.options[:db_prefix]) + assert user_database.info # ensure db exists in couch. + end + + def test_configures_security + assert_equal config.options[:security], user_database.get('_security') + end + + def test_stores_design_docs + assert_equal ['_design/docs', '_design/syncs', '_design/transactions'], + design_docs(user_database).map{|doc| doc["id"]}.sort + end + + def test_cleares_user_db + assert user_database.info # ensure db exists in couch. + delete_user + assert !host.databases.include?(user_database.name) + end + + def design_docs(db) + db.documents(startkey: '_design', endkey: '_design'.succ)["rows"] + end +end diff --git a/test/integration/test_setup_test.rb b/test/integration/test_setup_test.rb new file mode 100644 index 0000000..525c14d --- /dev/null +++ b/test/integration/test_setup_test.rb @@ -0,0 +1,15 @@ +require_relative '../test_helper.rb' + +class TestSetupTest < Tapicero::IntegrationTest + + def test_loads_config + assert_equal "tapicero_test", config.connection[:prefix] + assert_equal "debug", config.send(:log_level) + end + + def test_database_exists + assert database + assert_equal "tapicero_test_users", database.name + end + +end diff --git a/test/setup_couch.sh b/test/setup_couch.sh new file mode 100644 index 0000000..c11993d --- /dev/null +++ b/test/setup_couch.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +HOST="http://localhost:5984" +echo "couch version :" +curl -X GET $HOST +echo "creating unprivileged user :" +curl -HContent-Type:application/json -XPUT $HOST/_users/org.couchdb.user:me --data-binary '{"_id": "org.couchdb.user:me","name": "me","roles": [],"type": "user","password": "pwd"}' +echo "creating database to watch:" +curl -X PUT $HOST/tapicero_test_users +echo "restricting database access :" +curl -X PUT $HOST/tapicero_test_users/_security -Hcontent-type:application/json --data-binary '{"admins":{"names":[],"roles":[]},"members":{"names":["me"],"roles":[]}}' +echo "adding admin :" +curl -X PUT $HOST/_config/admins/anna -d '"secret"' diff --git a/test/support/integration_test.rb b/test/support/integration_test.rb new file mode 100644 index 0000000..b28c0e1 --- /dev/null +++ b/test/support/integration_test.rb @@ -0,0 +1,39 @@ +module Tapicero + class IntegrationTest < MiniTest::Unit::TestCase + + def create_user(fast = false) + result = database.save_doc :some => :content + raise RuntimeError.new(result.inspect) unless result['ok'] + sleep 1 unless fast # allow tapicero to do its job + @user = {'_id' => result["id"], '_rev' => result["rev"]} + end + + def delete_user(fast = false) + return if @user.nil? or @user['_deleted'] + result = database.delete_doc @user + raise RuntimeError.new(result.inspect) unless result['ok'] + @user['_deleted'] = true + sleep 1 unless fast # allow tapicero to do its job + end + + def user_database + host.database(config.options[:db_prefix] + @user['_id']) + end + + def database + @database ||= host.database(database_name) + end + + def database_name + config.complete_db_name('users') + end + + def host + @host ||= CouchRest.new(config.couch_host) + end + + def config + Tapicero.config + end + end +end diff --git a/test/test_helper.rb b/test/test_helper.rb index 3857e2c..5a3b509 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,10 +1,19 @@ require 'rubygems' require 'minitest/autorun' +require 'pathname' -BASE_DIR = File.expand_path('../..', __FILE__) -$:.unshift File.expand_path('lib', BASE_DIR) +unless defined? BASE_DIR + BASE_DIR = Pathname.new(__FILE__) + '../..' +end + +$:.unshift BASE_DIR + 'lib' require 'mocha/setup' -TAPICERO_CONFIG = "test/config/config.yaml" +require 'tapicero/version' +Tapicero::CONFIGS << "test/config.yaml" +Tapicero::RERAISE = true require 'tapicero' + + +require_relative 'support/integration_test' diff --git a/test/unit/couch_changes_test.rb b/test/unit/couch_changes_test.rb deleted file mode 100644 index 043caf1..0000000 --- a/test/unit/couch_changes_test.rb +++ /dev/null @@ -1,32 +0,0 @@ -require File.expand_path('../../test_helper.rb', __FILE__) -require 'tapicero/couch_changes' - -class CouchChangesTest < MiniTest::Unit::TestCase - - LAST_SEQ = 12 - - def setup - @stream = mock() - @changes = Tapicero::CouchChanges.new(@stream) - end - - def test_last_seq - @stream.expects(:get). - with('_changes', {:limit => 1, :descending => true}). - yields(:last_seq => LAST_SEQ) - assert_equal LAST_SEQ, @changes.last_seq - end - - def test_follow - stub_entry = {:new => :result} - @stream.expects(:get). - with('_changes', {:limit => 1, :descending => true}). - yields(:last_seq => LAST_SEQ) - @stream.expects(:get). - with('_changes', {:feed => :continuous, :since => LAST_SEQ}). - yields(stub_entry) - @changes.follow do |hash| - assert_equal stub_entry, hash - end - end -end diff --git a/test/unit/couch_stream_test.rb b/test/unit/couch_stream_test.rb deleted file mode 100644 index a9de21f..0000000 --- a/test/unit/couch_stream_test.rb +++ /dev/null @@ -1,29 +0,0 @@ -require File.expand_path('../../test_helper.rb', __FILE__) -require 'tapicero/couch_stream' - -class CouchStreamTest < MiniTest::Unit::TestCase - - def setup - @root = "http://server/database" - @stream = Tapicero::CouchStream.new(@root) - @url = @root + "/_changes?a=b&c=d" - @path = "_changes" - @options = {:a => :b, :c => :d} - end - - def test_get - Tapicero::JsonStream.expects(:get). - with(@url, :symbolize_keys => true). - yields(stub_hash = stub) - @stream.get(@path, @options) do |hash| - assert_equal stub_hash, hash - end - end - - # internal - def test_url_creation - assert_equal "http://server/database/", @stream.send(:url_for, "") - assert_equal @url, @stream.send(:url_for, @path, @options) - end - -end |