Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added support for memcached caching database #64

Open
wants to merge 8 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Gemfile_test
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ gem 'httpx'
gem 'typhoeus'
gem 'async-http'
gem 'ethon'
if RUBY_VERSION < '2.5'
gem 'dalli', '=2.7.11'
else
gem 'dalli'
end
if RUBY_ENGINE == 'jruby'
gem 'activerecord-jdbc-adapter'
gem 'jdbc-sqlite3'
Expand Down
2 changes: 2 additions & 0 deletions lib/newrelic_security/constants.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ module NewRelic::Security
HTTP_REQUEST = 'HTTP_REQUEST'
XPATH = 'XPATH'
LDAP = 'LDAP'
CACHING_DATA_STORE = 'CACHING_DATA_STORE'
MEMCACHED = 'MEMCACHED'
MONGO = 'MONGO'
SQLITE = 'SQLITE'
MYSQL = 'MYSQL'
Expand Down
25 changes: 25 additions & 0 deletions lib/newrelic_security/instrumentation-security/memcached/chain.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
module NewRelic::Security
module Instrumentation
module Dalli
module Client
module Chain

def self.instrument!
::Dalli::Client.class_eval do
include NewRelic::Security::Instrumentation::Dalli::Client

alias_method :perform_without_security, :perform

def perform(*all_args)
retval = nil
event = perform_on_enter(*all_args) { retval = perform_without_security(*all_args) }
perform_on_exit(event) { return retval }
end

end
end
end
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
require_relative 'prepend'
require_relative 'chain'

module NewRelic::Security
module Instrumentation
module Dalli::Client

READ_MODES = %i[get fetch].freeze
WRITE_MODES = %i[set].freeze
DELETE_MODES = %i[delete flush].freeze
UPDATE_MODES = %i[get append prepend incr decr touch replace].freeze

def perform_on_enter(*all_args)
event = nil
NewRelic::Security::Agent.logger.debug "OnEnter : #{self.class}.#{__method__}"
hash = {}
hash[:type] = all_args[0]
hash[:arguments] = all_args[1..-1]
if READ_MODES.include?(all_args[0])
hash[:mode] = :read
elsif WRITE_MODES.include?(all_args[0])
hash[:mode] = :write
elsif DELETE_MODES.include?(all_args[0])
hash[:mode] = :delete
elsif UPDATE_MODES.include?(all_args[0])
hash[:mode] = :update
end
event = NewRelic::Security::Agent::Control::Collector.collect(CACHING_DATA_STORE, [hash], MEMCACHED)
rescue => exception
NewRelic::Security::Agent.logger.error "Exception in hook in #{self.class}.#{__method__}, #{exception.inspect}, #{exception.backtrace}"
ensure
yield
return event
end

def perform_on_exit(event)
NewRelic::Security::Agent.logger.debug "OnExit : #{self.class}.#{__method__}"
NewRelic::Security::Agent::Utils.create_exit_event(event)
rescue => exception
NewRelic::Security::Agent.logger.error "Exception in hook in #{self.class}.#{__method__}, #{exception.inspect}, #{exception.backtrace}"
ensure
yield
end

end
end
end

NewRelic::Security::Instrumentation::InstrumentationLoader.install_instrumentation(:memcached, ::Dalli::Client, ::NewRelic::Security::Instrumentation::Dalli::Client)
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module NewRelic::Security
module Instrumentation
module Dalli
module Client
module Prepend
include NewRelic::Security::Instrumentation::Dalli::Client

def perform(*all_args)
retval = nil
event = perform_on_enter(*all_args) { retval = super }
perform_on_exit(event) { return retval }
end

end
end
end
end
end
34 changes: 33 additions & 1 deletion test/helpers/database_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ module Test
POSTGRESQL_USER = 'postgres'
POSTGRESQL_DATABASE = 'postgres'

MEMCACHED_HOST = 'localhost'
MEMCACHED_PORT = '11212'

module DatabaseHelper
extend self

Expand Down Expand Up @@ -110,6 +113,35 @@ def remove_postgresql_container
end
end

MEMCACHED_CONFIG = {
'Image' => 'memcached:latest',
'name' => 'memcached_test',
'HostConfig' => {
'PortBindings' => {
'11211/tcp' => [{ 'HostPort' => MEMCACHED_PORT }]
}
}
}

def create_memcached_container
image = Docker::Image.create('fromImage' => 'memcached:latest')
image.refresh!
begin
Docker::Container.get('memcached_test').remove(force: true)
rescue
end
container = Docker::Container.create(MEMCACHED_CONFIG)
container.start
sleep 10
end

def remove_memcached_container
begin
Docker::Container.get('memcached_test').remove(force: true)
rescue
end
end

end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
require 'dalli'
require_relative '../../../test_helper'
require 'newrelic_security/instrumentation-security/memcached/instrumentation'

module NewRelic::Security
module Test
module Instrumentation
class TestMemcached < Minitest::Test
@@before_all_flag = false

def setup
$event_list.clear()
NewRelic::Security::Agent::Control::HTTPContext.set_context({})
unless @@before_all_flag
NewRelic::Security::Test::DatabaseHelper.create_memcached_container
@@before_all_flag = true
end
end

def test_set_get_fetch_delete
cache = Dalli::Client.new("#{MEMCACHED_HOST}:#{MEMCACHED_PORT}")
$event_list.clear()
res = cache.set 'greet', 'hello', nil, :raw => true
args = [{:type=>:set, :arguments=>["greet", "hello", 0, 0, { :raw=>true }], :mode=>:write}]
expected_event = NewRelic::Security::Agent::Control::Event.new(CACHING_DATA_STORE, args, MEMCACHED)
assert_equal 1, NewRelic::Security::Agent::Control::Collector.get_event_count(CACHING_DATA_STORE)
assert_equal expected_event.caseType, $event_list[0].caseType
assert_equal expected_event.parameters, $event_list[0].parameters
assert_equal expected_event.eventCategory, $event_list[0].eventCategory
$event_list.clear()

res = cache.get 'greet'
args = [{:type=>:get, :arguments=>["greet", nil], :mode=>:read}]
expected_event = NewRelic::Security::Agent::Control::Event.new(CACHING_DATA_STORE, args, MEMCACHED)
assert_equal 'hello', res
assert_equal 1, NewRelic::Security::Agent::Control::Collector.get_event_count(CACHING_DATA_STORE)
assert_equal expected_event.caseType, $event_list[0].caseType
assert_equal expected_event.parameters, $event_list[0].parameters
assert_equal expected_event.eventCategory, $event_list[0].eventCategory
$event_list.clear()

res = cache.fetch 'greet'
args = [{:type=>:get, :arguments=>["greet", nil], :mode=>:read}]
expected_event = NewRelic::Security::Agent::Control::Event.new(CACHING_DATA_STORE, args, MEMCACHED)
assert_equal 'hello', res
assert_equal 1, NewRelic::Security::Agent::Control::Collector.get_event_count(CACHING_DATA_STORE)
assert_equal expected_event.caseType, $event_list[0].caseType
assert_equal expected_event.parameters, $event_list[0].parameters
assert_equal expected_event.eventCategory, $event_list[0].eventCategory
$event_list.clear()

res = cache.delete 'greet'
args = [{:type=>:delete, :arguments=>["greet", 0], :mode=>:delete}]
expected_event = NewRelic::Security::Agent::Control::Event.new(CACHING_DATA_STORE, args, MEMCACHED)
assert_equal 1, NewRelic::Security::Agent::Control::Collector.get_event_count(CACHING_DATA_STORE)
assert_equal true, res
assert_equal expected_event.caseType, $event_list[0].caseType
assert_equal expected_event.parameters, $event_list[0].parameters
assert_equal expected_event.eventCategory, $event_list[0].eventCategory
$event_list.clear()
end

def test_set_incr_get_flush
cache = Dalli::Client.new("#{MEMCACHED_HOST}:#{MEMCACHED_PORT}")
$event_list.clear()
res = cache.set 'counter', 0, nil, :raw => true
args = [{:type=>:set, :arguments=>["counter", 0, 0, 0, {:raw=>true}], :mode=>:write}]
expected_event = NewRelic::Security::Agent::Control::Event.new(CACHING_DATA_STORE, args, MEMCACHED)
assert_equal 1, NewRelic::Security::Agent::Control::Collector.get_event_count(CACHING_DATA_STORE)
assert_equal expected_event.caseType, $event_list[0].caseType
assert_equal expected_event.parameters, $event_list[0].parameters
assert_equal expected_event.eventCategory, $event_list[0].eventCategory
$event_list.clear()

res = cache.incr 'counter', 1
args = [{:type=>:incr, :arguments=>["counter", 1, 0, nil], :mode=>:update}]
expected_event = NewRelic::Security::Agent::Control::Event.new(CACHING_DATA_STORE, args, MEMCACHED)
assert_equal 1, res
assert_equal 1, NewRelic::Security::Agent::Control::Collector.get_event_count(CACHING_DATA_STORE)
assert_equal expected_event.caseType, $event_list[0].caseType
assert_equal expected_event.parameters, $event_list[0].parameters
assert_equal expected_event.eventCategory, $event_list[0].eventCategory
$event_list.clear()

res = cache.get 'counter'
args = [{:type=>:get, :arguments=>["counter", nil], :mode=>:read}]
expected_event = NewRelic::Security::Agent::Control::Event.new(CACHING_DATA_STORE, args, MEMCACHED)
assert_equal "1", res
assert_equal 1, NewRelic::Security::Agent::Control::Collector.get_event_count(CACHING_DATA_STORE)
assert_equal expected_event.caseType, $event_list[0].caseType
assert_equal expected_event.parameters, $event_list[0].parameters
assert_equal expected_event.eventCategory, $event_list[0].eventCategory
$event_list.clear()

res = cache.decr 'counter', 1
args = [{:type=>:decr, :arguments=>["counter", 1, 0, nil], :mode=>:update}]
expected_event = NewRelic::Security::Agent::Control::Event.new(CACHING_DATA_STORE, args, MEMCACHED)
assert_equal 0, res
assert_equal 1, NewRelic::Security::Agent::Control::Collector.get_event_count(CACHING_DATA_STORE)
assert_equal expected_event.caseType, $event_list[0].caseType
assert_equal expected_event.parameters, $event_list[0].parameters
assert_equal expected_event.eventCategory, $event_list[0].eventCategory
$event_list.clear()
end

def teardown
$event_list.clear()
NewRelic::Security::Agent::Control::HTTPContext.reset_context
end

Minitest.after_run do
NewRelic::Security::Test::DatabaseHelper.remove_memcached_container
end

end
end
end
end

Loading