From c801039963fb5a031bcf7df2da1e0b8e9a31b302 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20B=C3=A4r?= Date: Thu, 3 Sep 2020 09:12:09 +0200 Subject: [PATCH 1/2] #2: Removed `remotelog` source and added requirement to module instead. --- README.md | 7 +- doc/changes/changelog.md | 3 + doc/changes/changes_0.1.0.md | 22 +++ pom.xml | 2 +- src/main/lua/exasollog/log.lua | 221 ---------------------------- src/test/lua/exasollog/test_log.lua | 94 ------------ 6 files changed, 31 insertions(+), 318 deletions(-) create mode 100644 doc/changes/changelog.md create mode 100644 doc/changes/changes_0.1.0.md delete mode 100644 src/main/lua/exasollog/log.lua delete mode 100644 src/test/lua/exasollog/test_log.lua diff --git a/README.md b/README.md index 17cb13a..31c16b2 100644 --- a/README.md +++ b/README.md @@ -53,12 +53,15 @@ Running the RLS Lua Virtual Schema requires a Exasol with built-in Lua 5.1 or la | Dependency | Purpose | License | |------------------------------------------|--------------------------------------------------------|-------------------------------| | [Lua CJSON][luacjson] | JSON parsing and writing | MIT License | -| [LuaSocket][luasocket] | Socket communication | MIT License | +| [remotelog][remotelog] | Logging through a TCP socket | MIT License | -Note that Lua CSON and LuaSucket both are pre-installed on an Exasol database. For local unit testing you need to install them on the test machine though. +`remotelog` has a transitive dependency to [LuaSocket][luasocket] (MIT License). Note that Lua CSON and LuaSucket are pre-installed on an Exasol database. + +For local unit testing you need to install them on the test machine though. [luacjson]: https://www.kyne.com.au/~mark/software/lua-cjson.php [luasocket]: http://w3.impa.br/~diego/software/luasocket/ +[remotelog]: https://github.com/exasol/remotelog-lua ### Test Dependencies diff --git a/doc/changes/changelog.md b/doc/changes/changelog.md new file mode 100644 index 0000000..dc5b654 --- /dev/null +++ b/doc/changes/changelog.md @@ -0,0 +1,3 @@ +# Changes + +* [0.1.0](changes_0.1.0.md) \ No newline at end of file diff --git a/doc/changes/changes_0.1.0.md b/doc/changes/changes_0.1.0.md new file mode 100644 index 0000000..755770f --- /dev/null +++ b/doc/changes/changes_0.1.0.md @@ -0,0 +1,22 @@ +row-level-security-lua, released 2020-09-03 + +Code name: Prototype + +## Summary + +The prototype offers tenant-security, and has full unit test and integration test coverage. + +## Features / Enhancements + +* #1: Prototype + + +## Refactoring + +* #2: Removed `remotelog` sources and added the module as a LuaRocks dependency. + +## Dependency updates + +* Added `remotelog:1.0.0` + +Note that `row-level-security-lua` also has dependencies to `cjson` and `luasockets`, both of which are pre-installed on Exasol. \ No newline at end of file diff --git a/pom.xml b/pom.xml index f235f54..4a0fb85 100644 --- a/pom.xml +++ b/pom.xml @@ -178,7 +178,7 @@ exasolrls.adapter exasolrls.metadata_reader exasolrls.query_rewriter - exasollog.log + remotelog diff --git a/src/main/lua/exasollog/log.lua b/src/main/lua/exasollog/log.lua deleted file mode 100644 index c604059..0000000 --- a/src/main/lua/exasollog/log.lua +++ /dev/null @@ -1,221 +0,0 @@ -local levels = {NONE = 1, FATAL = 2, ERROR = 3, WARN = 4, INFO = 5, CONFIG = 6, DEBUG = 7, TRACE = 8} - ---- --- This module implements a remote log client with the ability to fall back to console logging in case no connection --- to a remote log receiver is established. ---

--- You can optionally use a high resolution timer for performance monitoring. Since Lua's os.date() --- function only has a resolution of seconds, that timer uses socket.gettime(). Note that the values --- you are getting are not the milliseconds of a second, but the milliseconds counted from when the module was first --- loaded — which is typically at the very beginning of the software using this module. ---

--- Use the init() method to set some global parameters for this module. ---

--- -local M = { - level = levels.INFO, - socket_client = nil, - connection_timeout = 0.1, -- seconds - log_client_name = nil, - timestamp_pattern = "%Y-%m-%d %H:%M:%S", - start_nanos = 0, - use_high_resolution_time = true, -} - -local socket = require("socket") - ---- --- Initialize the log module ---

--- This method allows you to set parameters that apply to all subsequent calls to logging methods. While it is possible --- to change these settings at runtime, the recommended way is to do this once only, before you use the log for the --- first time. ---

---

--- You can use a high resolution timer. Note that these are

not

the sub-second units of the timestamp! Lua --- timestamps only offer second resolution. Rather you get a time difference in milliseconds counted from the first time --- the log is opened. --- --- @param timestamp_pattern layout of timestamps displayed in the logs --- --- @param use_high_resolution_time switch high resolution time display on or off --- --- @return module loader --- -function M.init(timestamp_pattern, use_high_resolution_time) - M.timestamp_pattern = timestamp_pattern - if use_high_resolution_time ~= nil then - M.use_high_resolution_time = use_high_resolution_time - end - return M -end - ---- --- Set the log client name. ---

--- This is the name presented when the log is first opened. We recommend using the name of the application or script --- that uses the log and a version number. ---

--- -function M.set_client_name(log_client_name) - M.log_client_name = log_client_name -end - -local function start_high_resolution_timer() - if M.use_high_resolution_time then - M.start_nanos = socket.gettime() - end -end - -local function get_level_name(level) - for k, v in pairs(levels) do - if v == level then - return k - end - end - error("E-LOG-1: Unable to determine log level name for level number " .. level .. ".") -end - ---- --- Open a connection to a remote log receiver. ---

--- This method allows connecting the log to an external process listening on a TCP port. The process can be on a remote --- host. If the connection cannot be established, the logger falls back to console logging. ---

--- --- @param host remote host on which the logging process runs --- --- @param port TCP port on which the logging process listens --- -function M.connect(host, port) - local tcp_socket = socket.tcp() - tcp_socket:settimeout(M.connection_timeout) - local ok, err = tcp_socket:connect(host, port) - local log_client_prefix = M.log_client_name and (M.log_client_name .. ": ") or "" - if ok then - M.socket_client = tcp_socket - M.info("%sConnected to log receiver listening on %s:%d with log level %s. Timezone is UTC%s.", log_client_prefix, host, port, - get_level_name(M.level), os.date("%z")) - else - print(log_client_prefix .. "W-LOG-2: Unable to open socket connection to " .. host .. ":" .. port - .. "for sending log messages. Falling back to console logging with log level " .. get_level_name(M.level) - .. ". Timezone is UTC" .. os.date("%z") .. ". Caused by: " .. err) - end -end - ---- --- Close the connection to the remote log receiver. --- -function M.disconnect() - if(M.socket_client) then - M.socket_client:close() - end -end - ---- --- Set the log level. --- --- @param level_name name of the log level, one of: FATAL, ERROR, WARN, INFO, CONFIG, DEBUG, TRACE --- -function M.set_level(level_name) - local level = levels[level_name] - if level == nil then - M.warning('W-LOG-1: Attempt to set illegal log level "' .. level_name - .. ' Pick one of: NONE, FATAL, ERROR, WARN, INFO, CONFIG, DEBUG, TRACE. Falling back to level INFO.') - M.level = levels.INFO - else - M.level = level - end -end - ---- --- Write to a socket, print or discard the message. ---

--- If a socket connection is established, this method writes to that socket. Otherwise if the global print function --- exists (e.g. in a unit test) falls back to logging via print(). ---

--- Exasol removed print() in it's Lua implementation, so there is no fallback on a real Exasol instance. You either use --- remote logging or messages are discarded immediately. ---

--- --- @param level log level --- --- @param message log message; otherwise used as format string if any variadic parameters follow --- --- @param ... parameters to be inserted into formatted message (optional) --- -local function write(level, message, ...) - if not M.socket_client and print then - return - else - local entry - local formatted_message = (select('#', ...) > 0) and string.format(message, ...) or message - if M.use_high_resolution_time then - local current_millis = string.format("%07.3f", (socket.gettime() - M.start_nanos) * 1000) - entry = { - os.date(M.timestamp_pattern), - " (", current_millis, "ms) [", level , "]", - string.rep(" ", 7 - string.len(level)), formatted_message - } - else - entry = { - os.date(M.timestamp_pattern), - " [", level , "]", - string.rep(" ", 7 - string.len(level)), formatted_message - } - end - if M.socket_client then - entry[#entry + 1] = "\n" - M.socket_client:send(table.concat(entry)) - else - if(print) then - print(table.concat(entry)) - end - end - end -end - -function M.fatal(...) - if M.level >= levels.FATAL then - write("FATAL", ...) - end -end - -function M.error(...) - if M.level >= levels.ERROR then - write("ERROR", ...) - end -end - -function M.warn(...) - if M.level >= levels.WARN then - write("WARN", ...) - end -end - -function M.info(...) - if M.level >= levels.INFO then - write("INFO", ...) - end -end - -function M.config(...) - if M.level >= levels.CONFIG then - write("CONFIG", ...) - end -end - -function M.debug(...) - if M.level >= levels.DEBUG then - write("DEBUG", ...) - end -end - -function M.trace(...) - if M.level >= levels.FATAL then - write("TRACE", ...) - end -end - -start_high_resolution_timer() -return M diff --git a/src/test/lua/exasollog/test_log.lua b/src/test/lua/exasollog/test_log.lua deleted file mode 100644 index 163c796..0000000 --- a/src/test/lua/exasollog/test_log.lua +++ /dev/null @@ -1,94 +0,0 @@ -local luaunit = require("luaunit") -local mockagne = require("mockagne") - -local when, verify, any = mockagne.when, mockagne.verify, mockagne.any -local date_pattern = "%Y-%m-%d" - -test_log = {} - -function test_log:setUp() - self.today = os.date(date_pattern) - self.socket_mock = mockagne.getMock() - self.tcp_mock = mockagne.getMock() - self.client_mock = self.tcp_mock -- on connect(), the socket library promotes a TCP socket to a client socket - when(self.socket_mock.tcp()).thenAnswer(self.tcp_mock) - when(self.tcp_mock:connect(any(), any())).thenAnswer(1) - when(self.socket_mock.gettime()).thenAnswer(1000000) - package.preload["socket"] = function () return self.socket_mock end - self.log = require("exasollog.log").init(date_pattern, false) - self.log.set_client_name("Unit test") - self.log.connect("localhost", 3000) -end - -function test_log:tearDown() - self.log.disconnect() - package.loaded["socket"] = nil - package.loaded["exasollog.log"] = nil -end - -function test_log:assert_message(message) - verify(self.client_mock:send(message)) -end - -function test_log:assert_no_message(message) - luaunit.assertErrorMsgContains("no invocation made", function() self:assert_message(message) end) -end - -function test_log:test_startup_message() - local timezone = os.date("%z") - self:assert_message(self.today .. " [INFO] Unit test: Connected to log receiver listening on localhost:3000" - .. " with log level INFO. Timezone is UTC" .. timezone .. ".\n") -end - -function test_log:test_fatal() - self.log.fatal("Good by, cruel world!") - self:assert_message(self.today .. " [FATAL] Good by, cruel world!\n") -end - -function test_log:test_error() - self.log.error("Oops!") - self:assert_message(self.today .. " [ERROR] Oops!\n") -end - -function test_log:test_warn() - self.log.warn("This looks suspicious...") - self:assert_message(self.today .. " [WARN] This looks suspicious...\n") -end - -function test_log:test_info() - self.log.info("Good to know.") - self:assert_message(self.today .. " [INFO] Good to know.\n") -end - -function test_log:test_config() - self.log.set_level("CONFIG") - self.log.config("Life support enabled.") - self:assert_message(self.today .. " [CONFIG] Life support enabled.\n") -end - -function test_log:test_debug() - self.log.set_level("DEBUG") - self.log.debug("Look what we have here.") - self:assert_message(self.today .. " [DEBUG] Look what we have here.\n") -end - -function test_log:test_trace() - self.log.set_level("TRACE") - self.log.trace("foo(bar)") - self:assert_message(self.today .. " [TRACE] foo(bar)\n") -end - -function test_log:test_set_log_level() - self.log.set_level("WARN") - self.log.info("don't send") - self.log.warn("send") - self:assert_message(self.today .. " [WARN] send\n") - self:assert_no_message(self.today .. " [INFO] don't send\n") -end - -function test_log:test_logging_with_format_string() - self.log.info('%s says "Mount Everest is %d meters high."', "Simon", 8848) - self:assert_message(self.today .. ' [INFO] Simon says "Mount Everest is 8848 meters high."\n') -end - -os.exit(luaunit.LuaUnit.run()) From f0172975dbf2efb883ab6880a178044907bf8c77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20B=C3=A4r?= Date: Thu, 3 Sep 2020 11:59:20 +0200 Subject: [PATCH 2/2] #2: Turned `remotelog` into an external module. --- src/main/lua/exasolrls/metadata_reader.lua | 2 +- src/main/lua/exasolrls/query_rewriter.lua | 2 +- src/main/lua/exasolrls/table_protection_status.lua | 2 +- src/main/lua/exasolvs/request_dispatcher.lua | 2 +- src/test/lua/exasolvs/test_request_dispatcher.lua | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/lua/exasolrls/metadata_reader.lua b/src/main/lua/exasolrls/metadata_reader.lua index 6a3aa1a..423d3df 100644 --- a/src/main/lua/exasolrls/metadata_reader.lua +++ b/src/main/lua/exasolrls/metadata_reader.lua @@ -1,4 +1,4 @@ -local log = require("exasollog.log") +local log = require("remotelog") local cjson require("cjson") M = {} diff --git a/src/main/lua/exasolrls/query_rewriter.lua b/src/main/lua/exasolrls/query_rewriter.lua index 98c0573..4675a94 100644 --- a/src/main/lua/exasolrls/query_rewriter.lua +++ b/src/main/lua/exasolrls/query_rewriter.lua @@ -1,6 +1,6 @@ local renderer = require("exasolvs.query_renderer") local protection = require("exasolrls.table_protection_status") -local log = require("exasollog.log") +local log = require("remotelog") local M = {} diff --git a/src/main/lua/exasolrls/table_protection_status.lua b/src/main/lua/exasolrls/table_protection_status.lua index 2da285f..75d8af4 100644 --- a/src/main/lua/exasolrls/table_protection_status.lua +++ b/src/main/lua/exasolrls/table_protection_status.lua @@ -1,4 +1,4 @@ -local log = require("exasollog.log") +local log = require("remotelog") local M = {} diff --git a/src/main/lua/exasolvs/request_dispatcher.lua b/src/main/lua/exasolvs/request_dispatcher.lua index 1371bd8..9c46d68 100644 --- a/src/main/lua/exasolvs/request_dispatcher.lua +++ b/src/main/lua/exasolvs/request_dispatcher.lua @@ -1,4 +1,4 @@ -local log = require("exasollog.log") +local log = require("remotelog") local cjson = require("cjson") local adapter = require("exasolrls.adapter", "adapter") diff --git a/src/test/lua/exasolvs/test_request_dispatcher.lua b/src/test/lua/exasolvs/test_request_dispatcher.lua index ff35294..e716dfb 100644 --- a/src/test/lua/exasolvs/test_request_dispatcher.lua +++ b/src/test/lua/exasolvs/test_request_dispatcher.lua @@ -1,7 +1,7 @@ local luaunit = require("luaunit") local mockagne = require("mockagne") local log_mock = mockagne.getMock() -package.preload["exasollog.log"] = function () return log_mock end +package.preload["remotelog"] = function () return log_mock end local cjson = require("cjson") local dispatcher = require("exasolvs.request_dispatcher")