summaryrefslogtreecommitdiffstats
path: root/src/fluent-bit/tests/runtime_shell
diff options
context:
space:
mode:
Diffstat (limited to 'src/fluent-bit/tests/runtime_shell')
-rw-r--r--src/fluent-bit/tests/runtime_shell/.gitignore2
-rw-r--r--src/fluent-bit/tests/runtime_shell/CMakeLists.txt29
-rw-r--r--src/fluent-bit/tests/runtime_shell/common.sh26
-rw-r--r--src/fluent-bit/tests/runtime_shell/conf/in_dummy_expect.conf25
-rw-r--r--src/fluent-bit/tests/runtime_shell/conf/in_expect_base.conf28
-rw-r--r--src/fluent-bit/tests/runtime_shell/conf/in_http_tls_expect.conf13
-rw-r--r--src/fluent-bit/tests/runtime_shell/conf/in_syslog_tcp_plaintext_expect.conf8
-rw-r--r--src/fluent-bit/tests/runtime_shell/conf/in_syslog_tcp_tls_expect.conf14
-rw-r--r--src/fluent-bit/tests/runtime_shell/conf/in_syslog_udp_plaintext_expect.conf8
-rw-r--r--src/fluent-bit/tests/runtime_shell/conf/in_syslog_uds_dgram_plaintext_expect.conf7
-rw-r--r--src/fluent-bit/tests/runtime_shell/conf/in_syslog_uds_stream_plaintext_expect.conf7
-rw-r--r--src/fluent-bit/tests/runtime_shell/conf/in_tail_expect.conf38
-rwxr-xr-xsrc/fluent-bit/tests/runtime_shell/in_dummy_expect.sh8
-rwxr-xr-xsrc/fluent-bit/tests/runtime_shell/in_http_tls_expect.sh29
-rwxr-xr-xsrc/fluent-bit/tests/runtime_shell/in_syslog_tcp_plaintext_expect.sh26
-rwxr-xr-xsrc/fluent-bit/tests/runtime_shell/in_syslog_tcp_tls_expect.sh27
-rwxr-xr-xsrc/fluent-bit/tests/runtime_shell/in_syslog_udp_plaintext_expect.sh26
-rwxr-xr-xsrc/fluent-bit/tests/runtime_shell/in_syslog_uds_dgram_plaintext_expect.sh29
-rwxr-xr-xsrc/fluent-bit/tests/runtime_shell/in_syslog_uds_stream_plaintext_expect.sh24
-rw-r--r--src/fluent-bit/tests/runtime_shell/in_tail/README.md126
-rw-r--r--src/fluent-bit/tests/runtime_shell/in_tail/conf/multiline_rotation.conf60
-rw-r--r--src/fluent-bit/tests/runtime_shell/in_tail/conf/normal_rotation.conf55
-rw-r--r--src/fluent-bit/tests/runtime_shell/in_tail/conf/rotate_link.conf20
-rw-r--r--src/fluent-bit/tests/runtime_shell/in_tail/conf/single_static_rotation.conf19
-rw-r--r--src/fluent-bit/tests/runtime_shell/in_tail/conf/truncate_link.conf19
-rw-r--r--src/fluent-bit/tests/runtime_shell/in_tail/conf/truncate_rotation.conf19
-rwxr-xr-xsrc/fluent-bit/tests/runtime_shell/in_tail/logger_file.py72
-rwxr-xr-xsrc/fluent-bit/tests/runtime_shell/in_tail/run_tests.sh530
-rw-r--r--src/fluent-bit/tests/runtime_shell/in_tail/test_rotation.sh542
-rwxr-xr-xsrc/fluent-bit/tests/runtime_shell/in_tail_expect.sh16
-rw-r--r--src/fluent-bit/tests/runtime_shell/runtime_shell.env.in5
-rw-r--r--src/fluent-bit/tests/runtime_shell/tls/certificate.pem22
-rw-r--r--src/fluent-bit/tests/runtime_shell/tls/private_key.pem28
33 files changed, 1907 insertions, 0 deletions
diff --git a/src/fluent-bit/tests/runtime_shell/.gitignore b/src/fluent-bit/tests/runtime_shell/.gitignore
new file mode 100644
index 000000000..bf8c81f4d
--- /dev/null
+++ b/src/fluent-bit/tests/runtime_shell/.gitignore
@@ -0,0 +1,2 @@
+runtime_shell.env
+
diff --git a/src/fluent-bit/tests/runtime_shell/CMakeLists.txt b/src/fluent-bit/tests/runtime_shell/CMakeLists.txt
new file mode 100644
index 000000000..79c96de5f
--- /dev/null
+++ b/src/fluent-bit/tests/runtime_shell/CMakeLists.txt
@@ -0,0 +1,29 @@
+configure_file(
+ "${CMAKE_CURRENT_SOURCE_DIR}/runtime_shell.env.in"
+ "${CMAKE_CURRENT_SOURCE_DIR}/runtime_shell.env"
+ )
+
+set(UNIT_TESTS_SH
+ in_dummy_expect.sh
+ in_tail_expect.sh
+ in_http_tls_expect.sh
+ in_syslog_tcp_tls_expect.sh
+ in_syslog_tcp_plaintext_expect.sh
+ in_syslog_udp_plaintext_expect.sh
+ in_syslog_uds_dgram_plaintext_expect.sh
+ in_syslog_uds_stream_plaintext_expect.sh
+ )
+
+# Prepare list of unit tests
+foreach(script ${UNIT_TESTS_SH})
+ add_test(NAME ${script}
+ COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/${script}
+ )
+
+ set_tests_properties(${script} PROPERTIES ENVIRONMENT
+ "FLB_ROOT=${PROJECT_SOURCE_DIR};\
+FLB_RUNTIME_SHELL_PATH=${CMAKE_CURRENT_SOURCE_DIR};\
+FLB_RUNTIME_SHELL_CONF=${CMAKE_CURRENT_SOURCE_DIR}/conf;\
+FLB_BIN=${CMAKE_BINARY_DIR}/bin/fluent-bit"
+ )
+endforeach()
diff --git a/src/fluent-bit/tests/runtime_shell/common.sh b/src/fluent-bit/tests/runtime_shell/common.sh
new file mode 100644
index 000000000..1f4ec663a
--- /dev/null
+++ b/src/fluent-bit/tests/runtime_shell/common.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+# This function is meant to be used alongside with
+# in_expect_base.conf or a similar setup where
+# fluent-bit is set up to create a file as soon
+# as it starts.
+#
+# I would rather use pidof but it's not viable.
+#
+wait_for_fluent_bit() {
+ result=1
+
+ for retry in `seq 10`
+ do
+ if test -f $1
+ then
+ sleep 1
+ result=0
+ break
+ fi
+
+ sleep 1
+ done
+
+ echo "$result"
+}
diff --git a/src/fluent-bit/tests/runtime_shell/conf/in_dummy_expect.conf b/src/fluent-bit/tests/runtime_shell/conf/in_dummy_expect.conf
new file mode 100644
index 000000000..1b72d64e8
--- /dev/null
+++ b/src/fluent-bit/tests/runtime_shell/conf/in_dummy_expect.conf
@@ -0,0 +1,25 @@
+[SERVICE]
+ Flush 1
+ Grace 2
+ Log_Level info
+
+[INPUT]
+ Name dummy
+ Dummy {"r1": "someval", "s1": {"s2": null}, "r2": {"x": 0}}
+ Samples 1
+
+[FILTER]
+ Name expect
+ Match *
+ Log_Level debug
+ # Rules
+ key_exists $r1
+ key_not_exists $r0
+ key_val_is_null $s1['s2']
+ key_val_is_not_null $r2['x']
+ key_val_eq $r1 someval
+ action exit
+
+[OUTPUT]
+ Name exit
+ Match *
diff --git a/src/fluent-bit/tests/runtime_shell/conf/in_expect_base.conf b/src/fluent-bit/tests/runtime_shell/conf/in_expect_base.conf
new file mode 100644
index 000000000..760b47d53
--- /dev/null
+++ b/src/fluent-bit/tests/runtime_shell/conf/in_expect_base.conf
@@ -0,0 +1,28 @@
+[SERVICE]
+ Flush 1
+ Grace 1
+ Log_Level error
+ Parsers_File ${FLB_ROOT}/conf/parsers.conf
+
+[INPUT]
+ name dummy
+ samples 1
+
+[OUTPUT]
+ name file
+ match dummy.*
+ file ${SIGNAL_FILE_PATH}
+ mkdir on
+
+[FILTER]
+ Name expect
+ Match target_input
+ Log_Level debug
+ # Rules
+ key_exists $message
+ key_val_eq $message Hello!
+ action exit
+
+[OUTPUT]
+ Name exit
+ Match target_input
diff --git a/src/fluent-bit/tests/runtime_shell/conf/in_http_tls_expect.conf b/src/fluent-bit/tests/runtime_shell/conf/in_http_tls_expect.conf
new file mode 100644
index 000000000..2b66dff87
--- /dev/null
+++ b/src/fluent-bit/tests/runtime_shell/conf/in_http_tls_expect.conf
@@ -0,0 +1,13 @@
+@INCLUDE in_expect_base.conf
+
+[INPUT]
+ name http
+ tag target_input
+ listen ${LISTENER_HOST}
+ port ${LISTENER_PORT}
+ tls.verify no
+ tls on
+ tls.vhost ${LISTENER_VHOST}
+ tls.debug 4
+ tls.crt_file ${FLB_RUNTIME_SHELL_PATH}/tls/certificate.pem
+ tls.key_file ${FLB_RUNTIME_SHELL_PATH}/tls/private_key.pem \ No newline at end of file
diff --git a/src/fluent-bit/tests/runtime_shell/conf/in_syslog_tcp_plaintext_expect.conf b/src/fluent-bit/tests/runtime_shell/conf/in_syslog_tcp_plaintext_expect.conf
new file mode 100644
index 000000000..d47f6e335
--- /dev/null
+++ b/src/fluent-bit/tests/runtime_shell/conf/in_syslog_tcp_plaintext_expect.conf
@@ -0,0 +1,8 @@
+@INCLUDE in_expect_base.conf
+
+[INPUT]
+ name syslog
+ tag target_input
+ listen ${LISTENER_HOST}
+ port ${LISTENER_PORT}
+ mode tcp \ No newline at end of file
diff --git a/src/fluent-bit/tests/runtime_shell/conf/in_syslog_tcp_tls_expect.conf b/src/fluent-bit/tests/runtime_shell/conf/in_syslog_tcp_tls_expect.conf
new file mode 100644
index 000000000..afa28e6f5
--- /dev/null
+++ b/src/fluent-bit/tests/runtime_shell/conf/in_syslog_tcp_tls_expect.conf
@@ -0,0 +1,14 @@
+@INCLUDE in_expect_base.conf
+
+[INPUT]
+ name syslog
+ tag target_input
+ listen ${LISTENER_HOST}
+ port ${LISTENER_PORT}
+ mode tcp
+ tls.verify no
+ tls on
+ tls.vhost ${LISTENER_VHOST}
+ tls.debug 4
+ tls.crt_file ${FLB_RUNTIME_SHELL_PATH}/tls/certificate.pem
+ tls.key_file ${FLB_RUNTIME_SHELL_PATH}/tls/private_key.pem \ No newline at end of file
diff --git a/src/fluent-bit/tests/runtime_shell/conf/in_syslog_udp_plaintext_expect.conf b/src/fluent-bit/tests/runtime_shell/conf/in_syslog_udp_plaintext_expect.conf
new file mode 100644
index 000000000..ae92b39e5
--- /dev/null
+++ b/src/fluent-bit/tests/runtime_shell/conf/in_syslog_udp_plaintext_expect.conf
@@ -0,0 +1,8 @@
+@INCLUDE in_expect_base.conf
+
+[INPUT]
+ name syslog
+ tag target_input
+ listen ${LISTENER_HOST}
+ port ${LISTENER_PORT}
+ mode udp \ No newline at end of file
diff --git a/src/fluent-bit/tests/runtime_shell/conf/in_syslog_uds_dgram_plaintext_expect.conf b/src/fluent-bit/tests/runtime_shell/conf/in_syslog_uds_dgram_plaintext_expect.conf
new file mode 100644
index 000000000..48b6c375a
--- /dev/null
+++ b/src/fluent-bit/tests/runtime_shell/conf/in_syslog_uds_dgram_plaintext_expect.conf
@@ -0,0 +1,7 @@
+@INCLUDE in_expect_base.conf
+
+[INPUT]
+ name syslog
+ tag target_input
+ path ${SOCKET_PATH}
+ mode unix_udp \ No newline at end of file
diff --git a/src/fluent-bit/tests/runtime_shell/conf/in_syslog_uds_stream_plaintext_expect.conf b/src/fluent-bit/tests/runtime_shell/conf/in_syslog_uds_stream_plaintext_expect.conf
new file mode 100644
index 000000000..cfebdd24d
--- /dev/null
+++ b/src/fluent-bit/tests/runtime_shell/conf/in_syslog_uds_stream_plaintext_expect.conf
@@ -0,0 +1,7 @@
+@INCLUDE in_expect_base.conf
+
+[INPUT]
+ name syslog
+ tag target_input
+ path ${SOCKET_PATH}
+ mode unix_tcp \ No newline at end of file
diff --git a/src/fluent-bit/tests/runtime_shell/conf/in_tail_expect.conf b/src/fluent-bit/tests/runtime_shell/conf/in_tail_expect.conf
new file mode 100644
index 000000000..7348bacc0
--- /dev/null
+++ b/src/fluent-bit/tests/runtime_shell/conf/in_tail_expect.conf
@@ -0,0 +1,38 @@
+[SERVICE]
+ Flush 1
+ Grace 2
+ Log_Level info
+ Parsers_File ${FLB_ROOT}/conf/parsers.conf
+
+[INPUT]
+ name tail
+ path /tmp/flb_tail_expect*.log
+ exclude_path /tmp/flb_*2.log
+ read_from_head true
+ parser json
+ refresh_interval 10
+ rotate_wait 1
+ docker_mode false
+ docker_mode_flush 4
+ path_key path_key
+ ignore_older 0
+ buffer_chunk_size 32k
+ buffer_max_size 32k
+ skip_long_lines false
+ exit_on_eof false
+ db /tmp/flb_tail_expect.db
+ db.sync full
+
+[FILTER]
+ Name expect
+ Match *
+ Log_Level debug
+ # Rules
+ key_exists $path_key
+ key_val_eq $path_key /tmp/flb_tail_expect_1.log
+ key_not_exists $nokey
+ action exit
+
+[OUTPUT]
+ Name exit
+ Match *
diff --git a/src/fluent-bit/tests/runtime_shell/in_dummy_expect.sh b/src/fluent-bit/tests/runtime_shell/in_dummy_expect.sh
new file mode 100755
index 000000000..b75477634
--- /dev/null
+++ b/src/fluent-bit/tests/runtime_shell/in_dummy_expect.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+test_in_dummy_filter_expect() {
+ $FLB_BIN -c $FLB_RUNTIME_SHELL_CONF/in_dummy_expect.conf
+}
+
+# The following command launch the unit test
+. $FLB_RUNTIME_SHELL_PATH/runtime_shell.env
diff --git a/src/fluent-bit/tests/runtime_shell/in_http_tls_expect.sh b/src/fluent-bit/tests/runtime_shell/in_http_tls_expect.sh
new file mode 100755
index 000000000..fb47e2ae2
--- /dev/null
+++ b/src/fluent-bit/tests/runtime_shell/in_http_tls_expect.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+. ${FLB_RUNTIME_SHELL_PATH}/common.sh
+
+input_generator() {
+ result=$(wait_for_fluent_bit)
+
+ if test "$result" -eq "0"
+ then
+ curl -s -k \
+ -H 'content-type: application/json' \
+ -d '{"message": "Hello!"}' \
+ "https://${LISTENER_HOST}:${LISTENER_PORT}"
+ fi
+}
+
+test_in_http_tls_filter_expect() {
+ export SIGNAL_FILE_PATH="/tmp/fb_signal_$$"
+ export LISTENER_VHOST=leo.vcap.me
+ export LISTENER_HOST=127.0.0.1
+ export LISTENER_PORT=50000
+
+ input_generator &
+
+ $FLB_BIN -c $FLB_RUNTIME_SHELL_CONF/in_http_tls_expect.conf
+}
+
+# The following command launch the unit test
+. $FLB_RUNTIME_SHELL_PATH/runtime_shell.env
diff --git a/src/fluent-bit/tests/runtime_shell/in_syslog_tcp_plaintext_expect.sh b/src/fluent-bit/tests/runtime_shell/in_syslog_tcp_plaintext_expect.sh
new file mode 100755
index 000000000..414d40698
--- /dev/null
+++ b/src/fluent-bit/tests/runtime_shell/in_syslog_tcp_plaintext_expect.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+. ${FLB_RUNTIME_SHELL_PATH}/common.sh
+
+input_generator() {
+ result=$(wait_for_fluent_bit ${SIGNAL_FILE_PATH})
+
+ if test "$result" -eq "0"
+ then
+ echo '<13>1 1970-01-01T00:00:00.000000+00:00 testhost testuser - - [] Hello!' | \
+ nc -w 1 $LISTENER_HOST $LISTENER_PORT
+ fi
+}
+
+test_in_syslog_tcp_plaintext_filter_expect() {
+ export SIGNAL_FILE_PATH="/tmp/fb_signal_$$"
+ export LISTENER_HOST=127.0.0.1
+ export LISTENER_PORT=50001
+
+ input_generator &
+
+ $FLB_BIN -c $FLB_RUNTIME_SHELL_CONF/in_syslog_tcp_plaintext_expect.conf
+}
+
+# The following command launch the unit test
+. $FLB_RUNTIME_SHELL_PATH/runtime_shell.env
diff --git a/src/fluent-bit/tests/runtime_shell/in_syslog_tcp_tls_expect.sh b/src/fluent-bit/tests/runtime_shell/in_syslog_tcp_tls_expect.sh
new file mode 100755
index 000000000..d76c0e3c0
--- /dev/null
+++ b/src/fluent-bit/tests/runtime_shell/in_syslog_tcp_tls_expect.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+. ${FLB_RUNTIME_SHELL_PATH}/common.sh
+
+input_generator() {
+ result=$(wait_for_fluent_bit ${SIGNAL_FILE_PATH})
+
+ if test "$result" -eq "0"
+ then
+ echo '<13>1 1970-01-01T00:00:00.000000+00:00 testhost testuser - - [] Hello!' | \
+ openssl s_client -connect $LISTENER_HOST:$LISTENER_PORT 2>&1 >/dev/null
+ fi
+}
+
+test_in_syslog_tcp_plaintext_filter_expect() {
+ export SIGNAL_FILE_PATH="/tmp/fb_signal_$$"
+ export LISTENER_VHOST=leo.vcap.me
+ export LISTENER_HOST=127.0.0.1
+ export LISTENER_PORT=50002
+
+ input_generator &
+
+ $FLB_BIN -c $FLB_RUNTIME_SHELL_CONF/in_syslog_tcp_tls_expect.conf
+}
+
+# The following command launch the unit test
+. $FLB_RUNTIME_SHELL_PATH/runtime_shell.env
diff --git a/src/fluent-bit/tests/runtime_shell/in_syslog_udp_plaintext_expect.sh b/src/fluent-bit/tests/runtime_shell/in_syslog_udp_plaintext_expect.sh
new file mode 100755
index 000000000..23970a134
--- /dev/null
+++ b/src/fluent-bit/tests/runtime_shell/in_syslog_udp_plaintext_expect.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+. ${FLB_RUNTIME_SHELL_PATH}/common.sh
+
+input_generator() {
+ result=$(wait_for_fluent_bit ${SIGNAL_FILE_PATH})
+
+ if test "$result" -eq "0"
+ then
+ echo '<13>1 1970-01-01T00:00:00.000000+00:00 testhost testuser - - [] Hello!' | \
+ nc -w 1 -u $LISTENER_HOST $LISTENER_PORT
+ fi
+}
+
+test_in_syslog_tcp_plaintext_filter_expect() {
+ export SIGNAL_FILE_PATH="/tmp/fb_signal_$$"
+ export LISTENER_HOST=127.0.0.1
+ export LISTENER_PORT=50003
+
+ input_generator &
+
+ $FLB_BIN -c $FLB_RUNTIME_SHELL_CONF/in_syslog_udp_plaintext_expect.conf
+}
+
+# The following command launch the unit test
+. $FLB_RUNTIME_SHELL_PATH/runtime_shell.env
diff --git a/src/fluent-bit/tests/runtime_shell/in_syslog_uds_dgram_plaintext_expect.sh b/src/fluent-bit/tests/runtime_shell/in_syslog_uds_dgram_plaintext_expect.sh
new file mode 100755
index 000000000..2ab7703b2
--- /dev/null
+++ b/src/fluent-bit/tests/runtime_shell/in_syslog_uds_dgram_plaintext_expect.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+. ${FLB_RUNTIME_SHELL_PATH}/common.sh
+
+input_generator() {
+ result=$(wait_for_fluent_bit ${SIGNAL_FILE_PATH})
+
+ if test "$result" -eq "0"
+ then
+ echo '<13>Jan 1 00:00:00 testuser: Hello!' | nc -w 1 -U -u $SOCKET_PATH
+ fi
+}
+
+test_in_syslog_uds_stream_plaintext_filter_expect() {
+ platform=$(uname)
+
+ if test "$platform" != "Darwin"
+ then
+ export SIGNAL_FILE_PATH="/tmp/fb_signal_$$"
+ export SOCKET_PATH=/tmp/fluent_bit_syslog_uds_dgram.sock
+
+ input_generator &
+
+ $FLB_BIN -c $FLB_RUNTIME_SHELL_CONF/in_syslog_uds_dgram_plaintext_expect.conf
+ fi
+}
+
+# The following command launch the unit test
+. $FLB_RUNTIME_SHELL_PATH/runtime_shell.env
diff --git a/src/fluent-bit/tests/runtime_shell/in_syslog_uds_stream_plaintext_expect.sh b/src/fluent-bit/tests/runtime_shell/in_syslog_uds_stream_plaintext_expect.sh
new file mode 100755
index 000000000..b3a38cc22
--- /dev/null
+++ b/src/fluent-bit/tests/runtime_shell/in_syslog_uds_stream_plaintext_expect.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+. ${FLB_RUNTIME_SHELL_PATH}/common.sh
+
+input_generator() {
+ result=$(wait_for_fluent_bit ${SIGNAL_FILE_PATH})
+
+ if test "$result" -eq "0"
+ then
+ echo '<13>Jan 1 00:00:00 testuser: Hello!' | nc -w 1 -U $SOCKET_PATH
+ fi
+}
+
+test_in_syslog_uds_stream_plaintext_filter_expect() {
+ export SIGNAL_FILE_PATH="/tmp/fb_signal_$$"
+ export SOCKET_PATH=/tmp/fluent_bit_syslog_uds_stream.sock
+
+ input_generator &
+
+ $FLB_BIN -c $FLB_RUNTIME_SHELL_CONF/in_syslog_uds_stream_plaintext_expect.conf
+}
+
+# The following command launch the unit test
+. $FLB_RUNTIME_SHELL_PATH/runtime_shell.env
diff --git a/src/fluent-bit/tests/runtime_shell/in_tail/README.md b/src/fluent-bit/tests/runtime_shell/in_tail/README.md
new file mode 100644
index 000000000..78c77525f
--- /dev/null
+++ b/src/fluent-bit/tests/runtime_shell/in_tail/README.md
@@ -0,0 +1,126 @@
+# Fluent Bit Tail Input Plugin Tests
+
+The following directory contains tests for Tail input plugin behaviors.
+
+## run_tests.sh
+
+This script provide validations for offsets, database file entries and rotation under different scenarios. The following tests are available in the script
+
+- test_normal_rotation
+- test_single_static_rotation
+- test_truncate
+- test_rotate_link
+- test_truncate_link
+
+Running the script ```test_rotation.sh``` will run every test listed above, to run a single test just append it name, e.g:
+
+```
+./test_rotation.sh -- test_truncate
+```
+
+### 1. Normal Rotation
+
+**Unit**
+
+```test_normal_rotation```
+
+**Description**
+
+Run the logger tool that creates 5 different files, write 100000 messages to each one while rotating at 256KB.
+
+This test enable the database backend for Tail so it also helps to validate expected entries into the 'in_tail_files' table.
+
+**Configuration File**
+
+```conf/normal_rotation.conf```
+
+### 2. Single Static Rotation
+
+**Unit**
+
+```test_single_static_rotation```
+
+**Description**
+
+Run the logger tool that creates 1 big file and let Fluent Bit process it in the static mode, before to promote it to 'events' and it gets rotated.
+
+**Configuration File**
+
+```conf/single_static_rotation.conf```
+
+### 3. Truncate
+
+**Unit**
+
+```test_truncate```
+
+**Description**
+
+ Some environments still rely on truncation mode or well known as copytruncate,
+ this is the definition by logrotate(8):
+
+> Truncate the original log file to zero size in place after creating a copy,
+> instead of moving the old log file and optionally creating a new one. It
+> can be used when some program cannot be told to close its logfile and
+> thus might continue writing (appending) to the previous log file forever.
+>
+> Note that there is a very small time slice between copying the file and
+> truncating it, so some logging data might be lost. When this option is
+> used, the create option will have no effect, as the old log file stays in
+> place.
+
+This test checks that after a truncation the new lines added are properly
+processed.
+
+**Configuration File**
+
+```conf/truncate_rotation.conf```
+
+### 4. Rotate Link
+
+**Unit**
+
+```test_rotate_link```
+
+**Description**
+
+This test checks that a monitored link, upon rotation, keeps the proper offset and database status for the real file.
+
+ Example:
+
+ - file with data: data.log
+ - monitored link: test.log
+
+ Check the behavior upon the following rotation: test.log -> test.log.1
+
+**Configuration File**
+
+```conf/rotate_link.conf```
+
+### 5. Truncate Link
+
+**Unit**
+
+```test_truncate_link```
+
+**Description**
+
+Test a link that gets a truncation and Fluent Bit properly use the new offset
+
+**Configuration File**
+
+```conf/truncate_link.conf```
+
+### 6. Multiline Rotation
+
+**Unit**
+
+```test_multiline_rotation```
+
+**Description**
+
+Test a multiline rotation for issue 4190.
+
+**Configuration File**
+
+```conf/multiline_rotation.conf```
diff --git a/src/fluent-bit/tests/runtime_shell/in_tail/conf/multiline_rotation.conf b/src/fluent-bit/tests/runtime_shell/in_tail/conf/multiline_rotation.conf
new file mode 100644
index 000000000..3695014a5
--- /dev/null
+++ b/src/fluent-bit/tests/runtime_shell/in_tail/conf/multiline_rotation.conf
@@ -0,0 +1,60 @@
+[SERVICE]
+ flush 1
+ daemon off
+ log_level debug
+ log_file ${TEST_DIR}/out.log
+
+[INPUT]
+ name tail
+ tag a
+ path ${TEST_DIR}/a.log
+ db ${TEST_DIR}/a.db
+ db.sync full
+ multiline.parser cri
+ rotate_wait 5
+ refresh_interval 2
+
+[INPUT]
+ name tail
+ tag b
+ path ${TEST_DIR}/b.log
+ db ${TEST_DIR}/b.db
+ db.sync full
+ multiline.parser cri
+ rotate_wait 5
+ refresh_interval 2
+
+[INPUT]
+ name tail
+ tag c
+ path ${TEST_DIR}/c.log
+ db ${TEST_DIR}/c.db
+ db.sync full
+ multiline.parser cri
+ rotate_wait 5
+ refresh_interval 2
+
+[INPUT]
+ name tail
+ tag d
+ path ${TEST_DIR}/d.log
+ db ${TEST_DIR}/d.db
+ db.sync full
+ multiline.parser cri
+ rotate_wait 5
+ refresh_interval 2
+
+[INPUT]
+ name tail
+ tag e
+ path ${TEST_DIR}/e.log
+ db ${TEST_DIR}/e.db
+ db.sync full
+ multiline.parser cri
+ rotate_wait 5
+ refresh_interval 2
+
+[OUTPUT]
+ name file
+ match *
+ path ${TEST_DIR}
diff --git a/src/fluent-bit/tests/runtime_shell/in_tail/conf/normal_rotation.conf b/src/fluent-bit/tests/runtime_shell/in_tail/conf/normal_rotation.conf
new file mode 100644
index 000000000..0265df5d1
--- /dev/null
+++ b/src/fluent-bit/tests/runtime_shell/in_tail/conf/normal_rotation.conf
@@ -0,0 +1,55 @@
+[SERVICE]
+ flush 1
+ daemon off
+ log_level debug
+ log_file ${TEST_DIR}/out.log
+
+[INPUT]
+ name tail
+ tag a
+ path ${TEST_DIR}/a.log
+ db ${TEST_DIR}/a.db
+ db.sync full
+ rotate_wait 5
+ refresh_interval 2
+
+[INPUT]
+ name tail
+ tag b
+ path ${TEST_DIR}/b.log
+ db ${TEST_DIR}/b.db
+ db.sync full
+ rotate_wait 5
+ refresh_interval 2
+
+[INPUT]
+ name tail
+ tag c
+ path ${TEST_DIR}/c.log
+ db ${TEST_DIR}/c.db
+ db.sync full
+ rotate_wait 5
+ refresh_interval 2
+
+[INPUT]
+ name tail
+ tag d
+ path ${TEST_DIR}/d.log
+ db ${TEST_DIR}/d.db
+ db.sync full
+ rotate_wait 5
+ refresh_interval 2
+
+[INPUT]
+ name tail
+ tag e
+ path ${TEST_DIR}/e.log
+ db ${TEST_DIR}/e.db
+ db.sync full
+ rotate_wait 5
+ refresh_interval 2
+
+[OUTPUT]
+ name file
+ match *
+ path ${TEST_DIR}
diff --git a/src/fluent-bit/tests/runtime_shell/in_tail/conf/rotate_link.conf b/src/fluent-bit/tests/runtime_shell/in_tail/conf/rotate_link.conf
new file mode 100644
index 000000000..f0b017d44
--- /dev/null
+++ b/src/fluent-bit/tests/runtime_shell/in_tail/conf/rotate_link.conf
@@ -0,0 +1,20 @@
+[SERVICE]
+ flush 1
+ daemon off
+ log_level debug
+ log_file ${TEST_DIR}/out.log
+
+[INPUT]
+ name tail
+ tag a
+ path ${TEST_DIR}/a.log
+ db ${TEST_DIR}/a.db
+ db.sync full
+ rotate_wait 5
+ watcher_interval 1
+ refresh_interval 2
+
+[OUTPUT]
+ name file
+ match *
+ path ${TEST_DIR}
diff --git a/src/fluent-bit/tests/runtime_shell/in_tail/conf/single_static_rotation.conf b/src/fluent-bit/tests/runtime_shell/in_tail/conf/single_static_rotation.conf
new file mode 100644
index 000000000..ac883c957
--- /dev/null
+++ b/src/fluent-bit/tests/runtime_shell/in_tail/conf/single_static_rotation.conf
@@ -0,0 +1,19 @@
+[SERVICE]
+ flush 1
+ daemon off
+ log_level debug
+ log_file ${TEST_DIR}/out.log
+
+[INPUT]
+ name tail
+ tag a
+ path ${TEST_DIR}/a.log
+ db ${TEST_DIR}/a.db
+ db.sync full
+ rotate_wait 5
+ refresh_interval 2
+
+[OUTPUT]
+ name file
+ match *
+ path ${TEST_DIR}
diff --git a/src/fluent-bit/tests/runtime_shell/in_tail/conf/truncate_link.conf b/src/fluent-bit/tests/runtime_shell/in_tail/conf/truncate_link.conf
new file mode 100644
index 000000000..6cfa5410c
--- /dev/null
+++ b/src/fluent-bit/tests/runtime_shell/in_tail/conf/truncate_link.conf
@@ -0,0 +1,19 @@
+[SERVICE]
+ flush 1
+ daemon off
+ log_level debug
+ log_file ${TEST_DIR}/out.log
+
+[INPUT]
+ name tail
+ tag a
+ path ${TEST_DIR}/a.log
+ db ${TEST_DIR}/a.db
+ db.sync full
+ rotate_wait 5
+ refresh_interval 1
+
+[OUTPUT]
+ name file
+ match *
+ path ${TEST_DIR}
diff --git a/src/fluent-bit/tests/runtime_shell/in_tail/conf/truncate_rotation.conf b/src/fluent-bit/tests/runtime_shell/in_tail/conf/truncate_rotation.conf
new file mode 100644
index 000000000..ac883c957
--- /dev/null
+++ b/src/fluent-bit/tests/runtime_shell/in_tail/conf/truncate_rotation.conf
@@ -0,0 +1,19 @@
+[SERVICE]
+ flush 1
+ daemon off
+ log_level debug
+ log_file ${TEST_DIR}/out.log
+
+[INPUT]
+ name tail
+ tag a
+ path ${TEST_DIR}/a.log
+ db ${TEST_DIR}/a.db
+ db.sync full
+ rotate_wait 5
+ refresh_interval 2
+
+[OUTPUT]
+ name file
+ match *
+ path ${TEST_DIR}
diff --git a/src/fluent-bit/tests/runtime_shell/in_tail/logger_file.py b/src/fluent-bit/tests/runtime_shell/in_tail/logger_file.py
new file mode 100755
index 000000000..a8e2a1b39
--- /dev/null
+++ b/src/fluent-bit/tests/runtime_shell/in_tail/logger_file.py
@@ -0,0 +1,72 @@
+#!/bin/env python
+
+import sys
+import uuid
+import time
+import signal
+import logging
+from argparse import ArgumentParser
+from logging.handlers import RotatingFileHandler
+from threading import Thread
+
+class LoggerManager:
+ def __init__(self, args):
+ # KB to bytes
+ self.max_bytes = (args.size * 1000)
+ self.backup = args.backup
+ self.lines = args.lines
+ self.delay = args.delay
+ self.threads = []
+
+ # Create a thread for every writer
+ for f in args.filenames:
+ thread = Thread(target = self.single_logger_thread, args = (f,))
+ if thread is None:
+ print("error creating thread")
+ sys.exit(1)
+ self.threads.append(thread)
+ thread.start()
+ print("Logger thread for '" + f + "' has started")
+
+ for th in self.threads:
+ th.join()
+ print("Logger thread finished")
+
+ def single_logger_thread(self, name):
+ logger = logging.getLogger(name)
+ logger.setLevel(logging.DEBUG)
+ handler = RotatingFileHandler(name, maxBytes = self.max_bytes,
+ backupCount = self.backup)
+ logger.addHandler(handler)
+ rnd = uuid.uuid4()
+
+ i = 0
+ while i < self.lines:
+ logger.debug(rnd)
+ if self.delay > 0.0:
+ time.sleep(self.delay / 1000.0)
+ i = i + 1
+
+def signal_handler(sig, frame):
+ print("stopping logger")
+ sys.exit(0)
+
+if __name__ == '__main__':
+ signal.signal(signal.SIGINT, signal_handler)
+
+ # Define arguments
+ parser = ArgumentParser()
+ parser.add_argument("-b", "--backup", dest="backup", default=50, type=int)
+ parser.add_argument("-d", "--delay", dest="delay", default=0.1, type=float,
+ help="milliseconds delay between line writes")
+ parser.add_argument("-l", "--lines", dest="lines", default=1000, type=int)
+ parser.add_argument("-f", "--file", dest="filenames", action='append', required=True,
+ help="write logs to FILE", metavar="FILE")
+ parser.add_argument("-s", "--size", dest="size", type=int,
+ help="maximum log file size in KB before rotation",
+ default=256)
+ # Read arguments
+ args = parser.parse_args()
+
+ # Start the Logger
+ lm = LoggerManager(args)
diff --git a/src/fluent-bit/tests/runtime_shell/in_tail/run_tests.sh b/src/fluent-bit/tests/runtime_shell/in_tail/run_tests.sh
new file mode 100755
index 000000000..09bb9c438
--- /dev/null
+++ b/src/fluent-bit/tests/runtime_shell/in_tail/run_tests.sh
@@ -0,0 +1,530 @@
+#!/bin/sh
+
+# Environment variables
+FLB_BIN=`realpath ../../../build/bin/fluent-bit`
+FLB_RUNTIME_SHELL_PATH=`realpath $(pwd)/../`
+FLB_RUN_TEST=`realpath $FLB_RUNTIME_SHELL_PATH/../lib/shunit2/shunit2`
+
+# Colorize shunit2
+bold=$(tput bold)
+normal=$(tput sgr0)
+SHUNIT_TEST_PREFIX="$bold==========> UNIT TEST: $normal"
+
+# 1. Normal Rotation
+# ------------------
+# Run the logger tool that creates 5 different files, write 100000 messages to each one
+# while rotating at 256KB.
+#
+# This test enable the database backend for Tail so it also helps to validate expected
+# entries into the 'in_tail_files' table.
+#
+# Configuration file used: conf/normal_rotation.conf
+
+test_normal_rotation() {
+ # Helper function to check monitored files
+ sqlite_check()
+ {
+ # Incoming parameters:
+ # $1: temporal directory to store data
+ # $2: database file name
+ # $3: Fluent Bit PID
+ #
+ # This function store the remaining monitored files listed in the database,
+ # we send the output to an .inodes for troubleshooting purposes if required
+ sqlite3 $1/$2 -batch \
+ ".headers off" ".width 20" "SELECT inode FROM in_tail_files" > \
+ $1/$2.inodes
+
+ rows=`cat $1/$2.inodes | wc -l | tr -d -C '[0-9]'`
+ if [ $rows != "1" ]; then
+ echo "> database file $1/$2 contains $rows rows, inodes:"
+ cat $1/$2.inodes
+ echo "> open files"
+ ls -l /proc/$3/fd/ | grep \\.log
+ else
+ echo "> database file $1/$2 is OK"
+ fi
+ ${_ASSERT_EQUALS_} "1" $rows
+ }
+
+ # Prepare test directory
+ export TEST_DIR=tmp_test
+ rm -rf $TEST_DIR
+ mkdir $TEST_DIR
+
+ # Create empty files so Fluent Bit will enqueue them on start
+ for logfile in a b c d e ; do
+ touch $TEST_DIR/$logfile.log
+ done
+
+ # Run Fluent Bit
+ $FLB_BIN -c conf/normal_rotation.conf &
+ FLB_PID=$!
+ echo "Fluent Bit started, pid=$FLB_PID"
+
+ # Start the Logger: 5 files = 500000 log lines in total
+ python logger_file.py -l 100000 -s 256 -b 100 -d 0.1 \
+ -f $TEST_DIR/a.log \
+ -f $TEST_DIR/b.log \
+ -f $TEST_DIR/c.log \
+ -f $TEST_DIR/d.log \
+ -f $TEST_DIR/e.log
+
+ echo "Logger finished...wait 10 seconds"
+ sleep 10
+
+ # Count number of processed lines
+ write_lines=`cat $TEST_DIR/[abcdefghij].log* | wc -l`
+ read_lines=`cat $TEST_DIR/[abcdefghij] | wc -l`
+
+ echo "> write lines: $write_lines"
+ echo "> read lines : $read_lines"
+
+ # Check we processed same number of records
+ ${_ASSERT_EQUALS_} $write_lines $read_lines
+
+ # Validate our database files has only one remaining entry per database file
+ for logfile in a b c d e; do
+ sqlite_check $TEST_DIR "$logfile.db" $FLB_PID
+ done
+
+ # Stop Fluent Bit (SIGTERM)
+ kill -15 $FLB_PID
+}
+
+# 2. Single Static Rotation (static process mode + rotation)
+# ----------------------------------------------------------
+# Run the logger tool that creates 1 big file and let Fluent Bit process it in
+# the static mode, before to promote it to 'events' and it gets rotated.
+#
+# Configuration file used: conf/single_static_rotation.conf
+
+test_single_static_rotation() {
+ # Write a log file of 200000 lines
+
+ # Prepare test directory
+ export TEST_DIR=tmp_test
+ rm -rf $TEST_DIR
+ mkdir $TEST_DIR
+
+ # Create empty files so Fluent Bit will enqueue them on start
+ touch $TEST_DIR/a.log
+
+ # Start the Logger: 1 file with 400000 lines, we use a big size (-s) to
+ # avoid rotation
+ python logger_file.py -l 400000 -s 200000 -b 100 -d 0 \
+ -f $TEST_DIR/a.log
+ lines=`cat $TEST_DIR/a.log | wc -l`
+ echo "Logger done, written lines "$lines
+
+ # Run Fluent Bit
+ $FLB_BIN -c conf/single_static_rotation.conf &
+ FLB_PID=$!
+ echo "Fluent Bit started, pid=$FLB_PID"
+
+ # Wait 3 seconds before rotation
+ sleep 2
+ mv $TEST_DIR/a.log $TEST_DIR/a.log.1
+
+ lines=`cat $TEST_DIR/a | wc -l`
+ echo "file Rotated, mid-check: processed lines $lines"
+ sleep 30
+
+ # Count number of processed lines
+ write_lines=`cat $TEST_DIR/a.log.1 | wc -l`
+ read_lines=`cat $TEST_DIR/a | wc -l`
+
+ echo "> write lines: $write_lines"
+ echo "> read lines : $read_lines"
+
+ # Check we processed same number of records
+ ${_ASSERT_EQUALS_} $write_lines $read_lines
+
+ # Validate our database files has only one remaining entry per database file
+ #sqlite_check $TEST_DIR "$logfile.db" $FLB_PID
+
+ # Stop Fluent Bit (SIGTERM)
+ kill -15 $FLB_PID
+}
+
+# 3. Truncate
+# -----------
+# Some environments still rely on truncation mode or well known as copytruncate,
+# this is the definition by logrotate(8):
+#
+# "Truncate the original log file to zero size in place after creating a copy,
+# instead of moving the old log file and optionally creating a new one. It
+# can be used when some program cannot be told to close its logfile and
+# thus might continue writing (appending) to the previous log file forever.
+#
+# Note that there is a very small time slice between copying the file and
+# truncating it, so some logging data might be lost. When this option is
+# used, the create option will have no effect, as the old log file stays in
+# place."
+#
+# This test checks that after a truncation the new lines added are properly
+# processed.
+#
+# Configuration file used: conf/truncate_rotation.conf
+
+test_truncate() {
+ # Helper function to check monitored files
+ sqlite_check()
+ {
+ # Incoming parameters:
+ # $1: temporal directory to store data
+ # $2: database file name
+ # $3: Fluent Bit PID
+ #
+ # This function store the remaining monitored files listed in the database,
+ # we send the output to an .inodes for troubleshooting purposes if required
+
+ # Get the last size of the 'a.log' file and check we have the same value
+ # in the database
+ offset=`wc -c < $TEST_DIR/a.log`
+
+ sqlite3 $1/$2 -batch \
+ ".headers off" "SELECT inode FROM in_tail_files WHERE offset=$offset" > \
+ $1/$2.offset
+
+ rows=`cat $1/$2.offset | wc -l | tr -d -C '[0-9]'`
+ if [ $rows != "1" ]; then
+ echo "> invalid database content:"
+ cat $1/$2.offset
+ echo "> open files"
+ ls -l /proc/$3/fd/ | grep \\.log
+ else
+ echo "> database file $1/$2 is OK"
+ fi
+ ${_ASSERT_EQUALS_} "1" $rows
+ }
+
+ # Prepare test directory
+ export TEST_DIR=tmp_test
+ rm -rf $TEST_DIR
+ mkdir $TEST_DIR
+
+ # Create empty files so Fluent Bit will enqueue them on start
+ touch $TEST_DIR/a.log
+
+ # Start the Logger: 1 file with 200 lines, we use a big size limit (-s) to
+ # avoid rotation
+ python logger_file.py -l 200 -s 200000 -b 100 -d 0 -f $TEST_DIR/a.log
+ lines=`cat $TEST_DIR/a.log | wc -l`
+ echo "Logger done, written lines "$lines
+
+ # Run Fluent Bit
+ $FLB_BIN -c conf/truncate_rotation.conf &
+ FLB_PID=$!
+ echo "Fluent Bit started, pid=$FLB_PID"
+
+ # Wait 2 seconds before truncation
+ sleep 2
+ pre_lines=`cat $TEST_DIR/a.log | wc -l`
+ truncate -s 0 $TEST_DIR/a.log
+
+ lines=`cat $TEST_DIR/a | wc -l`
+ echo "file truncated, mid-check: processed lines $lines"
+
+ # Append 100 more lines
+ python logger_file.py -l 100 -s 200000 -b 100 -d 0 -f $TEST_DIR/a.log
+
+ sleep 3
+
+ # Count number of processed lines
+ write_lines=300
+ read_lines=`cat $TEST_DIR/a | wc -l`
+
+ echo "> write lines: $write_lines"
+ echo "> read lines : $read_lines"
+
+ # Check we processed same number of records
+ ${_ASSERT_EQUALS_} $write_lines $read_lines
+
+ sqlite_check $TEST_DIR a.db $FLB_PID
+
+ # Stop Fluent Bit (SIGTERM)
+ kill -15 $FLB_PID
+}
+
+# 4. Rotate Link
+# --------------
+# This case checks that a monitored link, upon rotation, keeps the proper offset
+# and database status for the real file.
+#
+# Example:
+#
+# - file with data: data.log
+# - monitored link: test.log
+#
+# Check the behavior upon test.log -> test.log.1 behavior
+#
+# Configuration file used: conf/rotate_link.conf
+
+test_rotate_link() {
+ # Helper function to check monitored files
+ sqlite_check()
+ {
+ # Incoming parameters:
+ # $1: temporal directory to store data
+ # $2: database file name
+ # $3: Fluent Bit PID
+ #
+ # This function store the remaining monitored files listed in the database,
+ # we send the output to an .inodes for troubleshooting purposes if required
+
+ # Get the last size of the file pointed by 'a.log.1' and check we have the
+ # same value in the database
+ offset=`wc -c < $TEST_DIR/a.log.1`
+
+ sqlite3 $1/$2 -batch \
+ ".headers off" "SELECT inode FROM in_tail_files WHERE offset=$offset \
+ AND rotated=1" > $1/$2.offset
+
+ rows=`cat $1/$2.offset | wc -l | tr -d -C '[0-9]'`
+ if [ $rows != "1" ]; then
+ echo "> invalid database content:"
+ cat $1/$2.offset
+ echo "> open files"
+ ls -l /proc/$3/fd/ | grep \\.log
+ else
+ echo "> offset database check $1/$2 is OK"
+ fi
+ ${_ASSERT_EQUALS_} "1" $rows
+
+ # After rotate_wait (5 secs + watcher) we expect an empty database
+ sleep 6
+ sqlite3 $1/$2 -batch \
+ ".headers off" "SELECT inode FROM in_tail_files WHERE offset=$offset \
+ AND rotated=1" > $1/$2.offset
+
+ rows=`cat $1/$2.offset | wc -l | tr -d -C '[0-9]'`
+ if [ $rows != "0" ]; then
+ echo "> invalid database content:"
+ cat $1/$2.offset
+ echo "> open files"
+ ls -l /proc/$3/fd/ | grep \\.log
+ else
+ echo "> empty database check $1/$2 is OK"
+ fi
+ ${_ASSERT_EQUALS_} "0" $rows
+ }
+
+ # Prepare test directory
+ export TEST_DIR=tmp_test
+ rm -rf $TEST_DIR
+ mkdir $TEST_DIR
+
+ # Create empty files so Fluent Bit will enqueue them on start
+ touch $TEST_DIR/data.log
+
+ # Start the Logger: 1 file with 100 lines, we use a big size limit (-s) to
+ # avoid rotation
+ python logger_file.py -l 100 -s 200000 -b 100 -d 0 -f $TEST_DIR/data.log
+ lines=`cat $TEST_DIR/data.log | wc -l`
+ ln -s data.log $TEST_DIR/a.log
+ echo "Logger done, written lines "$lines
+
+ # Run Fluent Bit
+ $FLB_BIN -c conf/rotate_link.conf &
+ FLB_PID=$!
+ echo "Fluent Bit started, pid=$FLB_PID"
+
+ # Wait 2 seconds and rotate file
+ sleep 2
+ pre_lines=`cat $TEST_DIR/a.log | wc -l`
+ mv $TEST_DIR/a.log $TEST_DIR/a.log.1
+
+ lines=`cat $TEST_DIR/a | wc -l`
+ echo "file rotated, mid-check: processed lines $lines"
+
+ # Append 200 more lines to the rotated link
+ python logger_file.py -l 200 -s 200000 -b 100 -d 0 -f $TEST_DIR/a.log.1
+
+ # Count number of processed lines
+ sleep 3
+ write_lines=300
+ read_lines=`cat $TEST_DIR/a | wc -l`
+
+ echo "> write lines: $write_lines"
+ echo "> read lines : $read_lines"
+
+ # Check we processed same number of records
+ ${_ASSERT_EQUALS_} $write_lines $read_lines
+
+ # Check that database file have the right offset and mark the file as rotated
+ sqlite_check $TEST_DIR a.db $FLB_PID
+
+ # Stop Fluent Bit (SIGTERM)
+ kill -15 $FLB_PID
+}
+
+# 5. Truncate Link
+#
+# Test a link that gets a truncation and Fluent Bit properly use the new offset
+#
+# Configuration file used: conf/truncate_link.conf
+
+test_truncate_link() {
+ # Helper function to check monitored files
+ sqlite_check()
+ {
+ # Incoming parameters:
+ # $1: temporal directory to store data
+ # $2: database file name
+ # $3: Fluent Bit PID
+ #
+ # This function store the remaining monitored files listed in the database,
+ # we send the output to an .inodes for troubleshooting purposes if required
+
+ # Get the last size of the 'a.log' file and check we have the same value
+ # in the database
+ offset=`wc -c < $TEST_DIR/a.log`
+
+ sqlite3 $1/$2 -batch \
+ ".headers off" "SELECT inode FROM in_tail_files WHERE offset=$offset" > \
+ $1/$2.offset
+
+ rows=`cat $1/$2.offset | wc -l | tr -d -C '[0-9]'`
+ if [ $rows != "1" ]; then
+ echo "> invalid database content:"
+ cat $1/$2.offset
+ echo "> open files"
+ ls -l /proc/$3/fd/ | grep \\.log
+ else
+ echo "> database file $1/$2 is OK"
+ fi
+ ${_ASSERT_EQUALS_} "1" $rows
+ }
+
+ # Prepare test directory
+ export TEST_DIR=tmp_test
+ rm -rf $TEST_DIR
+ mkdir $TEST_DIR
+
+ # Create empty files so Fluent Bit will enqueue them on start
+ touch $TEST_DIR/data.log
+
+ # Start the Logger: 1 file with 100 lines, we use a big size limit (-s) to
+ # avoid rotation
+ python logger_file.py -l 100 -s 200000 -b 100 -d 0 -f $TEST_DIR/data.log
+ lines=`cat $TEST_DIR/data.log | wc -l`
+ ln -s data.log $TEST_DIR/a.log
+ echo "Logger done, written lines "$lines
+
+ # Run Fluent Bit
+ $FLB_BIN -c conf/truncate_link.conf &
+ FLB_PID=$!
+ echo "Fluent Bit started, pid=$FLB_PID"
+
+ # Wait 1 second before truncation
+ sleep 1
+ pre_lines=`cat $TEST_DIR/a.log | wc -l`
+ truncate -s 0 $TEST_DIR/a.log
+
+ sleep 2
+ lines=`cat $TEST_DIR/a | wc -l`
+ echo "file truncated, mid-check: processed lines $lines"
+
+ # Append 200 more lines
+ python logger_file.py -l 200 -s 200000 -b 100 -d 0 -f $TEST_DIR/a.log
+
+ sleep 4
+
+ # Count number of processed lines
+ write_lines=300
+ read_lines=`cat $TEST_DIR/a | wc -l`
+
+ echo "> write lines: $write_lines"
+ echo "> read lines : $read_lines"
+
+ # Check we processed same number of records
+ ${_ASSERT_EQUALS_} $write_lines $read_lines
+
+ # Stop Fluent Bit (SIGTERM)
+ kill -15 $FLB_PID
+}
+
+# 6. Multiline + rotation
+# ------------------
+# Run the logger tool that creates 5 different files, write 100000 messages to each one
+# while rotating at 256KB.
+#
+# This test for issue 4190
+#
+# Configuration file used: conf/multiline_rotation.conf
+
+test_multiline_rotation() {
+ # Helper function to check monitored files
+ sqlite_check()
+ {
+ # Incoming parameters:
+ # $1: temporal directory to store data
+ # $2: database file name
+ # $3: Fluent Bit PID
+ #
+ # This function store the remaining monitored files listed in the database,
+ # we send the output to an .inodes for troubleshooting purposes if required
+ sqlite3 $1/$2 -batch \
+ ".headers off" ".width 20" "SELECT inode FROM in_tail_files" > \
+ $1/$2.inodes
+
+ rows=`cat $1/$2.inodes | wc -l | tr -d -C '[0-9]'`
+ if [ $rows != "1" ]; then
+ echo "> database file $1/$2 contains $rows rows, inodes:"
+ cat $1/$2.inodes
+ echo "> open files"
+ ls -l /proc/$3/fd/ | grep \\.log
+ else
+ echo "> database file $1/$2 is OK"
+ fi
+ ${_ASSERT_EQUALS_} "1" $rows
+ }
+
+ # Prepare test directory
+ export TEST_DIR=tmp_test
+ rm -rf $TEST_DIR
+ mkdir $TEST_DIR
+
+ # Create empty files so Fluent Bit will enqueue them on start
+ for logfile in a b c d e ; do
+ touch $TEST_DIR/$logfile.log
+ done
+
+ # Run Fluent Bit
+ $FLB_BIN -c conf/multiline_rotation.conf &
+ FLB_PID=$!
+ echo "Fluent Bit started, pid=$FLB_PID"
+
+ # Start the Logger: 5 files = 500000 log lines in total
+ python logger_file.py -l 100000 -s 256 -b 100 -d 0.1 \
+ -f $TEST_DIR/a.log \
+ -f $TEST_DIR/b.log \
+ -f $TEST_DIR/c.log \
+ -f $TEST_DIR/d.log \
+ -f $TEST_DIR/e.log
+
+ echo "Logger finished...wait 10 seconds"
+ sleep 10
+
+ # Count number of processed lines
+ write_lines=`cat $TEST_DIR/[abcdefghij].log* | wc -l`
+ read_lines=`cat $TEST_DIR/[abcdefghij] | wc -l`
+
+ echo "> write lines: $write_lines"
+ echo "> read lines : $read_lines"
+
+ # Check we processed same number of records
+ ${_ASSERT_EQUALS_} $write_lines $read_lines
+
+ # Validate our database files has only one remaining entry per database file
+ for logfile in a b c d e; do
+ sqlite_check $TEST_DIR "$logfile.db" $FLB_PID
+ done
+
+ # Stop Fluent Bit (SIGTERM)
+ kill -15 $FLB_PID
+}
+
+# Launch the tests
+. $FLB_RUN_TEST
diff --git a/src/fluent-bit/tests/runtime_shell/in_tail/test_rotation.sh b/src/fluent-bit/tests/runtime_shell/in_tail/test_rotation.sh
new file mode 100644
index 000000000..889fa7b06
--- /dev/null
+++ b/src/fluent-bit/tests/runtime_shell/in_tail/test_rotation.sh
@@ -0,0 +1,542 @@
+#!/bin/sh
+
+# Environment variables
+FLB_BIN=`realpath ../../../build/bin/fluent-bit`
+FLB_RUNTIME_SHELL_PATH=`realpath $(pwd)/../`
+FLB_RUN_TEST=`realpath $FLB_RUNTIME_SHELL_PATH/../lib/shunit2/shunit2`
+
+# Colorize shunit2
+bold=$(tput bold)
+normal=$(tput sgr0)
+SHUNIT_TEST_PREFIX="$bold==========> UNIT TEST: $normal"
+
+# 1. Normal Rotation
+# ------------------
+# Run the logger tool that creates 5 different files, write 100000 messages to each one
+# while rotating at 256KB.
+#
+# This test enable the database backend for Tail so it also helps to validate expected
+# entries into the 'in_tail_files' table.
+#
+# Configuration file used: conf/normal_rotation.conf
+
+test_normal_rotation() {
+ # Helper function to check monitored files
+ sqlite_check()
+ {
+ # Incoming parameters:
+ # $1: temporal directory to store data
+ # $2: database file name
+ # $3: Fluent Bit PID
+ #
+ # This function store the remaining monitored files listed in the database,
+ # we send the output to an .inodes for troubleshooting purposes if required
+ sqlite3 $1/$2 -batch \
+ ".headers off" ".width 20" "SELECT inode FROM in_tail_files" > \
+ $1/$2.inodes
+
+ rows=`cat $1/$2.inodes | wc -l | tr -d -C '[0-9]'`
+ if [ $rows != "1" ]; then
+ echo "> database file $1/$2 contains $rows rows, inodes:"
+ cat $1/$2.inodes
+ echo "> open files"
+ ls -l /proc/$3/fd/ | grep \\.log
+ else
+ echo "> database file $1/$2 is OK"
+ fi
+ ${_ASSERT_EQUALS_} "1" $rows
+ }
+
+ # Prepare test directory
+ export TEST_DIR=tmp_test
+ rm -rf $TEST_DIR
+ mkdir $TEST_DIR
+
+ # Create empty files so Fluent Bit will enqueue them on start
+ for logfile in a b c d e ; do
+ touch $TEST_DIR/$logfile.log
+ done
+
+ # Run Fluent Bit
+ $FLB_BIN -c conf/normal_rotation.conf &
+ FLB_PID=$!
+ echo "Fluent Bit started, pid=$FLB_PID"
+
+ # Start the Logger: 5 files = 500000 log lines in total
+ python logger_file.py -l 100000 -s 256 -b 100 -d 0.1 \
+ -f $TEST_DIR/a.log \
+ -f $TEST_DIR/b.log \
+ -f $TEST_DIR/c.log \
+ -f $TEST_DIR/d.log \
+ -f $TEST_DIR/e.log
+
+ echo "Logger finished...wait 10 seconds"
+ sleep 10
+
+ # Count number of processed lines
+ write_lines=`cat $TEST_DIR/[abcdefghij].log* | wc -l`
+ read_lines=`cat $TEST_DIR/[abcdefghij] | wc -l`
+
+ echo "> write lines: $write_lines"
+ echo "> read lines : $read_lines"
+
+ # Check we processed same number of records
+ ${_ASSERT_EQUALS_} $write_lines $read_lines
+
+ # Validate our database files has only one remaining entry per database file
+ for logfile in a b c d e; do
+ sqlite_check $TEST_DIR "$logfile.db" $FLB_PID
+ done
+
+ # Stop Fluent Bit (SIGTERM)
+ kill -15 $FLB_PID
+}
+
+# 2. Single Static Rotation (static process mode + rotation)
+# ----------------------------------------------------------
+# Run the logger tool that creates 1 big file and let Fluent Bit process it in
+# the static mode, before to promote it to 'events' and it gets rotated.
+#
+# Configuration file used: conf/single_static_rotation.conf
+
+test_single_static_rotation() {
+ # Write a log file of 200000 lines
+
+ # Prepare test directory
+ export TEST_DIR=tmp_test
+ rm -rf $TEST_DIR
+ mkdir $TEST_DIR
+
+ # Create empty files so Fluent Bit will enqueue them on start
+ touch $TEST_DIR/a.log
+
+ # Start the Logger: 1 file with 400000 lines, we use a big size (-s) to
+ # avoid rotation
+ python logger_file.py -l 400000 -s 200000 -b 100 -d 0 \
+ -f $TEST_DIR/a.log
+ lines=`cat $TEST_DIR/a.log | wc -l`
+ echo "Logger done, written lines "$lines
+
+ # Run Fluent Bit
+ $FLB_BIN -c conf/single_static_rotation.conf &
+ FLB_PID=$!
+ echo "Fluent Bit started, pid=$FLB_PID"
+
+ # Wait 3 seconds before rotation
+ sleep 2
+ mv $TEST_DIR/a.log $TEST_DIR/a.log.1
+
+ lines=`cat $TEST_DIR/a | wc -l`
+ echo "file Rotated, mid-check: processed lines $lines"
+ sleep 30
+
+ # Count number of processed lines
+ write_lines=`cat $TEST_DIR/a.log.1 | wc -l`
+ read_lines=`cat $TEST_DIR/a | wc -l`
+
+ echo "> write lines: $write_lines"
+ echo "> read lines : $read_lines"
+
+ # Check we processed same number of records
+ ${_ASSERT_EQUALS_} $write_lines $read_lines
+
+ # Validate our database files has only one remaining entry per database file
+ #sqlite_check $TEST_DIR "$logfile.db" $FLB_PID
+
+ # Stop Fluent Bit (SIGTERM)
+ kill -15 $FLB_PID
+}
+
+# 3. Truncate
+# -----------
+# Some environments still rely on truncation mode or well known as copytruncate,
+# this is the definition by logrotate(8):
+#
+# "Truncate the original log file to zero size in place after creating a copy,
+# instead of moving the old log file and optionally creating a new one. It
+# can be used when some program cannot be told to close its logfile and
+# thus might continue writing (appending) to the previous log file forever.
+#
+# Note that there is a very small time slice between copying the file and
+# truncating it, so some logging data might be lost. When this option is
+# used, the create option will have no effect, as the old log file stays in
+# place."
+#
+# This test checks that after a truncation the new lines added are properly
+# processed.
+#
+# Configuration file used: conf/truncate_rotation.conf
+
+test_truncate() {
+ # Helper function to check monitored files
+ sqlite_check()
+ {
+ # Incoming parameters:
+ # $1: temporal directory to store data
+ # $2: database file name
+ # $3: Fluent Bit PID
+ #
+ # This function store the remaining monitored files listed in the database,
+ # we send the output to an .inodes for troubleshooting purposes if required
+
+ # Get the last size of the 'a.log' file and check we have the same value
+ # in the database
+ offset=`wc -c < $TEST_DIR/a.log`
+
+ sqlite3 $1/$2 -batch \
+ ".headers off" "SELECT inode FROM in_tail_files WHERE offset=$offset" > \
+ $1/$2.offset
+
+ rows=`cat $1/$2.offset | wc -l | tr -d -C '[0-9]'`
+ if [ $rows != "1" ]; then
+ echo "> invalid database content:"
+ cat $1/$2.offset
+ echo "> open files"
+ ls -l /proc/$3/fd/ | grep \\.log
+ else
+ echo "> database file $1/$2 is OK"
+ fi
+ ${_ASSERT_EQUALS_} "1" $rows
+ }
+
+ # Prepare test directory
+ export TEST_DIR=tmp_test
+ rm -rf $TEST_DIR
+ mkdir $TEST_DIR
+
+ # Create empty files so Fluent Bit will enqueue them on start
+ touch $TEST_DIR/a.log
+
+ # Start the Logger: 1 file with 200 lines, we use a big size limit (-s) to
+ # avoid rotation
+ python logger_file.py -l 200 -s 200000 -b 100 -d 0 -f $TEST_DIR/a.log
+ lines=`cat $TEST_DIR/a.log | wc -l`
+ echo "Logger done, written lines "$lines
+
+ # Run Fluent Bit
+ $FLB_BIN -c conf/truncate_rotation.conf &
+ FLB_PID=$!
+ echo "Fluent Bit started, pid=$FLB_PID"
+
+ # Wait 2 seconds before truncation
+ sleep 2
+ pre_lines=`cat $TEST_DIR/a.log | wc -l`
+ truncate -s 0 $TEST_DIR/a.log
+
+ lines=`cat $TEST_DIR/a | wc -l`
+ echo "file truncated, mid-check: processed lines $lines"
+
+ # Append 100 more lines
+ python logger_file.py -l 100 -s 200000 -b 100 -d 0 -f $TEST_DIR/a.log
+
+ sleep 3
+
+ # Count number of processed lines
+ write_lines=300
+ read_lines=`cat $TEST_DIR/a | wc -l`
+
+ echo "> write lines: $write_lines"
+ echo "> read lines : $read_lines"
+
+ # Check we processed same number of records
+ ${_ASSERT_EQUALS_} $write_lines $read_lines
+
+ sqlite_check $TEST_DIR a.db $FLB_PID
+
+ # Stop Fluent Bit (SIGTERM)
+ kill -15 $FLB_PID
+}
+
+# 4. Rotate Link
+# --------------
+# This case checks that a monitored link, upon rotation, keeps the proper offset
+# and database status for the real file.
+#
+# Example:
+#
+# - file with data: data.log
+# - monitored link: test.log
+#
+# Check the behavior upon test.log -> test.log.1 behavior
+#
+# Configuration file used: conf/rotate_link.conf
+
+test_rotate_link() {
+ # Helper function to check monitored files
+ sqlite_check()
+ {
+ # Incoming parameters:
+ # $1: temporal directory to store data
+ # $2: database file name
+ # $3: Fluent Bit PID
+ #
+ # This function store the remaining monitored files listed in the database,
+ # we send the output to an .inodes for troubleshooting purposes if required
+
+ # Get the last size of the file pointed by 'a.log.1' and check we have the
+ # same value in the database
+ offset=`wc -c < $TEST_DIR/a.log.1`
+
+ sqlite3 $1/$2 -batch \
+ ".headers off" "SELECT inode FROM in_tail_files WHERE offset=$offset \
+ AND rotated=1" > $1/$2.offset
+
+ rows=`cat $1/$2.offset | wc -l | tr -d -C '[0-9]'`
+ if [ $rows != "1" ]; then
+ echo "> invalid database content:"
+ cat $1/$2.offset
+ echo "> open files"
+ ls -l /proc/$3/fd/ | grep \\.log
+ else
+ echo "> offset database check $1/$2 is OK"
+ fi
+ ${_ASSERT_EQUALS_} "1" $rows
+
+ # After rotate_wait (5 secs + watcher) we expect an empty database
+ sleep 6
+ sqlite3 $1/$2 -batch \
+ ".headers off" "SELECT inode FROM in_tail_files WHERE offset=$offset \
+ AND rotated=1" > $1/$2.offset
+
+ rows=`cat $1/$2.offset | wc -l | tr -d -C '[0-9]'`
+ if [ $rows != "0" ]; then
+ echo "> invalid database content:"
+ cat $1/$2.offset
+ echo "> open files"
+ ls -l /proc/$3/fd/ | grep \\.log
+ else
+ echo "> empty database check $1/$2 is OK"
+ fi
+ ${_ASSERT_EQUALS_} "0" $rows
+ }
+
+ # Prepare test directory
+ export TEST_DIR=tmp_test
+ rm -rf $TEST_DIR
+ mkdir $TEST_DIR
+
+ # Create empty files so Fluent Bit will enqueue them on start
+ touch $TEST_DIR/data.log
+
+ # Start the Logger: 1 file with 100 lines, we use a big size limit (-s) to
+ # avoid rotation
+ python logger_file.py -l 100 -s 200000 -b 100 -d 0 -f $TEST_DIR/data.log
+ lines=`cat $TEST_DIR/data.log | wc -l`
+ ln -s data.log $TEST_DIR/a.log
+ echo "Logger done, written lines "$lines
+
+ # Run Fluent Bit
+ $FLB_BIN -c conf/rotate_link.conf &
+ FLB_PID=$!
+ echo "Fluent Bit started, pid=$FLB_PID"
+
+ # Wait 2 seconds and rotate file
+ sleep 2
+ pre_lines=`cat $TEST_DIR/a.log | wc -l`
+ mv $TEST_DIR/a.log $TEST_DIR/a.log.1
+
+ lines=`cat $TEST_DIR/a | wc -l`
+ echo "file rotated, mid-check: processed lines $lines"
+
+ # Append 200 more lines to the rotated link
+ python logger_file.py -l 200 -s 200000 -b 100 -d 0 -f $TEST_DIR/a.log.1
+
+ # Count number of processed lines
+ sleep 3
+ write_lines=300
+ read_lines=`cat $TEST_DIR/a | wc -l`
+
+ echo "> write lines: $write_lines"
+ echo "> read lines : $read_lines"
+
+ # Check we processed same number of records
+ ${_ASSERT_EQUALS_} $write_lines $read_lines
+
+ # Check that database file have the right offset and mark the file as rotated
+ sqlite_check $TEST_DIR a.db $FLB_PID
+
+ # Stop Fluent Bit (SIGTERM)
+ kill -15 $FLB_PID
+}
+
+# 5. Truncate Link
+#
+# Test a link that gets a truncation and Fluent Bit properly use the new offset
+#
+# Configuration file used: conf/truncate_link.conf
+
+test_truncate_link() {
+ # Helper function to check monitored files
+ sqlite_check()
+ {
+ # Incoming parameters:
+ # $1: temporal directory to store data
+ # $2: database file name
+ # $3: Fluent Bit PID
+ #
+ # This function store the remaining monitored files listed in the database,
+ # we send the output to an .inodes for troubleshooting purposes if required
+
+ # Get the last size of the 'a.log' file and check we have the same value
+ # in the database
+ offset=`wc -c < $TEST_DIR/a.log`
+
+ sqlite3 $1/$2 -batch \
+ ".headers off" "SELECT inode FROM in_tail_files WHERE offset=$offset" > \
+ $1/$2.offset
+
+ rows=`cat $1/$2.offset | wc -l | tr -d -C '[0-9]'`
+ if [ $rows != "1" ]; then
+ echo "> invalid database content:"
+ cat $1/$2.offset
+ echo "> open files"
+ ls -l /proc/$3/fd/ | grep \\.log
+ else
+ echo "> database file $1/$2 is OK"
+ fi
+ ${_ASSERT_EQUALS_} "1" $rows
+ }
+
+ # Prepare test directory
+ export TEST_DIR=tmp_test
+ rm -rf $TEST_DIR
+ mkdir $TEST_DIR
+
+ # Create empty files so Fluent Bit will enqueue them on start
+ touch $TEST_DIR/data.log
+
+ # Start the Logger: 1 file with 100 lines, we use a big size limit (-s) to
+ # avoid rotation
+ python logger_file.py -l 100 -s 200000 -b 100 -d 0 -f $TEST_DIR/data.log
+ lines=`cat $TEST_DIR/data.log | wc -l`
+ ln -s data.log $TEST_DIR/a.log
+ echo "Logger done, written lines "$lines
+
+ # Run Fluent Bit
+ $FLB_BIN -c conf/truncate_link.conf &
+ FLB_PID=$!
+ echo "Fluent Bit started, pid=$FLB_PID"
+
+ # Wait 1 second before truncation
+ sleep 1
+ pre_lines=`cat $TEST_DIR/a.log | wc -l`
+ truncate -s 0 $TEST_DIR/a.log
+
+ sleep 2
+ lines=`cat $TEST_DIR/a | wc -l`
+ echo "file truncated, mid-check: processed lines $lines"
+
+ # Append 200 more lines
+ python logger_file.py -l 200 -s 200000 -b 100 -d 0 -f $TEST_DIR/a.log
+
+ sleep 4
+
+ # Count number of processed lines
+ write_lines=300
+ read_lines=`cat $TEST_DIR/a | wc -l`
+
+ echo "> write lines: $write_lines"
+ echo "> read lines : $read_lines"
+
+ # Check we processed same number of records
+ ${_ASSERT_EQUALS_} $write_lines $read_lines
+
+ # Stop Fluent Bit (SIGTERM)
+ kill -15 $FLB_PID
+}
+
+# 6. Database Resume
+#
+# Tests that if Fluent Bit stops, it can resume reading files from the last position
+#
+# Configuration file used: conf/database_resume.conf
+
+test_database_resume() {
+ # Helper function to check monitored files
+ sqlite_check()
+ {
+ # Incoming parameters:
+ # $1: temporal directory to store data
+ # $2: database file name
+ # $3: Fluent Bit PID
+ #
+ # This function store the remaining monitored files listed in the database,
+ # we send the output to an .inodes for troubleshooting purposes if required
+
+ # Get the last size of the 'a.log' file and check we have the same value
+ # in the database
+ offset=`stat -c %s $TEST_DIR/a.log`
+
+ sqlite3 $1/$2 -batch \
+ ".headers off" "SELECT inode FROM in_tail_files WHERE offset=$offset" > \
+ $1/$2.offset
+
+ rows=`cat $1/$2.offset | wc -l`
+ if [ $rows != "1" ]; then
+ echo "> invalid database content:"
+ cat $1/$2.offset
+ echo "> open files"
+ ls -l /proc/$3/fd/ | grep \\.log
+ else
+ echo "> database file $1/$2 is OK"
+ fi
+ ${_ASSERT_EQUALS_} "1" $rows
+ }
+
+ # Prepare test directory
+ export TEST_DIR=tmp_test
+ rm -rf $TEST_DIR
+ mkdir $TEST_DIR
+
+ # Start the Logger: 5 files with 10000 lines each, we use a big size limit (-s) to
+ # avoid rotation
+ python logger_file.py -l 10000 -s 200000 -b 100 -d 0 \
+ -f $TEST_DIR/a.log \
+ -f $TEST_DIR/b.log \
+ -f $TEST_DIR/c.log \
+ -f $TEST_DIR/d.log \
+ -f $TEST_DIR/e.log
+
+ lines=`cat $TEST_DIR/[abcde].log | wc -l`
+ echo "Logger done, written lines "$lines
+
+ # Run Fluent Bit
+ $FLB_BIN -c conf/database_resume.conf &
+ FLB_PID=$!
+ echo "Fluent Bit started, pid=$FLB_PID"
+
+ # Wait 1 second before stop Fluent Bit
+ sleep 1
+
+ # Stop Fluent Bit (SIGTERM)
+ kill -15 $FLB_PID
+
+ sqlite3 $TEST_DIR/logs.db "SELECT * FROM in_tail_files"
+ exit 0
+
+ sleep 2
+ lines=`cat $TEST_DIR/a | wc -l`
+ echo "file truncated, mid-check: processed lines $lines"
+
+ # Append 200 more lines
+ python logger_file.py -l 200 -s 200000 -b 100 -d 0 -f $TEST_DIR/a.log
+
+ sleep 4
+
+ # Count number of processed lines
+ write_lines=300
+ read_lines=`cat $TEST_DIR/a | wc -l`
+
+ echo "> write lines: $write_lines"
+ echo "> read lines : $read_lines"
+
+ # Check we processed same number of records
+ ${_ASSERT_EQUALS_} $write_lines $read_lines
+
+ # Stop Fluent Bit (SIGTERM)
+ kill -15 $FLB_PID
+}
+
+
+# Launch the tests
+. $FLB_RUN_TEST
diff --git a/src/fluent-bit/tests/runtime_shell/in_tail_expect.sh b/src/fluent-bit/tests/runtime_shell/in_tail_expect.sh
new file mode 100755
index 000000000..c847d65f4
--- /dev/null
+++ b/src/fluent-bit/tests/runtime_shell/in_tail_expect.sh
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+test_in_tail_filter_expect() {
+ rm -rf /tmp/flb_*
+
+ # Monitor this file
+ echo "{\"key\": \"val\"}" > /tmp/flb_tail_expect_1.log
+
+ # Excluded file
+ echo "{\"nokey\": \"\"}" > /tmp/flb_tail_expect_2.log
+
+ $FLB_BIN -c $FLB_RUNTIME_SHELL_CONF/in_tail_expect.conf
+}
+
+# The following command launch the unit test
+. $FLB_RUNTIME_SHELL_PATH/runtime_shell.env
diff --git a/src/fluent-bit/tests/runtime_shell/runtime_shell.env.in b/src/fluent-bit/tests/runtime_shell/runtime_shell.env.in
new file mode 100644
index 000000000..6a40c513f
--- /dev/null
+++ b/src/fluent-bit/tests/runtime_shell/runtime_shell.env.in
@@ -0,0 +1,5 @@
+flb_runtime_shell_test_run() {
+ . $FLB_RUNTIME_SHELL_PATH/../lib/shunit2/shunit2
+}
+
+flb_runtime_shell_test_run
diff --git a/src/fluent-bit/tests/runtime_shell/tls/certificate.pem b/src/fluent-bit/tests/runtime_shell/tls/certificate.pem
new file mode 100644
index 000000000..b79719873
--- /dev/null
+++ b/src/fluent-bit/tests/runtime_shell/tls/certificate.pem
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDlzCCAn+gAwIBAgIUCl24aoQh3HuyObboivsozKKOb3cwDQYJKoZIhvcNAQEL
+BQAwWzELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
+GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEUMBIGA1UEAwwLbGVvLnZjYXAubWUw
+HhcNMjIwODE3MTM0OTAyWhcNMjMwODE3MTM0OTAyWjBbMQswCQYDVQQGEwJBVTET
+MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ
+dHkgTHRkMRQwEgYDVQQDDAtsZW8udmNhcC5tZTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBAJ2cbLtGhSeGPAXKitw+PN7U8CkaSGL7BssPFSuqtuVkJ1AH
+puluHq62FHm4UgTyJB/Iundf1kBxiOehGFr9E1fGp6kUety6iBZK3mmmX0NdXY2A
+ObqfvxTgPNVPEj+5Qp0CJW6fEBRhHrFgpf+OdKzoKnjSCbl1EyJ1wFZeSNDTx98I
+h951xEOCobcXj1qXHDd0snRW5QNKTdSnj+3amqrNqV5DrkCyyb5Rq/VQX9LIyGV8
+tOyq50mUKeK+vOu/oNwfkZqgZ5wlY/AUyPMNvd8UKitnWeQMiMmr3ImQRZLkFnoE
+2mi2Xr6ZQhmsweWbTmmUYxr+ej7L5WvBVQEkbd0CAwEAAaNTMFEwHQYDVR0OBBYE
+FGliHqbvEsRN36C2w1cCq5M/gGCRMB8GA1UdIwQYMBaAFGliHqbvEsRN36C2w1cC
+q5M/gGCRMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBACWCNohz
+YbnnDzGXMhGlfV5PywH6+gguqTrtzJlfJB/FGHDMybBvEp9gda5ssPZfPdv6aAvo
+hz2XC+uwJqYn76ZcX+LOixsqhoXJ367pT0ggMSUB/UNvGkzMFWuGerxcB5Q7xvsh
+0Ui37Ojok7Ozj9Ofk/zroJB1WG3kPDtwoo+GwNmJaDtXhOutRPxCqp5Dw+dC0N0c
+TlPCBRB17hx49Zjw+h9sv1VmOUtQcplNZgRL9lguv7eXB6y0sSdpDyDLlTBbRMGl
+uVnQ20uKnqqALRfpXDWFL8g01xPNEIfV3KctpJfu1inLWtPuklerl0yeyUh9M1f0
+zCAi0URsGEHMFA0=
+-----END CERTIFICATE-----
diff --git a/src/fluent-bit/tests/runtime_shell/tls/private_key.pem b/src/fluent-bit/tests/runtime_shell/tls/private_key.pem
new file mode 100644
index 000000000..b6a705395
--- /dev/null
+++ b/src/fluent-bit/tests/runtime_shell/tls/private_key.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCdnGy7RoUnhjwF
+yorcPjze1PApGkhi+wbLDxUrqrblZCdQB6bpbh6uthR5uFIE8iQfyLp3X9ZAcYjn
+oRha/RNXxqepFHrcuogWSt5ppl9DXV2NgDm6n78U4DzVTxI/uUKdAiVunxAUYR6x
+YKX/jnSs6Cp40gm5dRMidcBWXkjQ08ffCIfedcRDgqG3F49alxw3dLJ0VuUDSk3U
+p4/t2pqqzaleQ65Assm+Uav1UF/SyMhlfLTsqudJlCnivrzrv6DcH5GaoGecJWPw
+FMjzDb3fFCorZ1nkDIjJq9yJkEWS5BZ6BNpotl6+mUIZrMHlm05plGMa/no+y+Vr
+wVUBJG3dAgMBAAECggEANXQ/AEkLkfsZ0lD+RXIqTNzlUttiH4fJpwbHhFbSzvvn
+xWHC/zpk15ZTXXDhCGJjVBBNBX2QeazH5N8jFoDslYF/jX2vqbrturnLswNFHeDF
+gN8zNRNGyDrBBwtZQhl/+SYoMdtqpa7GrRv9UK4s7hOTjASYXbjSM4bCI8i4Y3JX
+5iKYnejTJ4GNXm1S5g4H8QpgXxrxYU7HY1UlMBGni1BSDPb5SxANnFvgw0oypaDG
+4a0KpnUmziBtZo8wIQ7+UeStS4xikIeaT2SQM/ilplPNx0HgPHbTMeTBHQylG1JU
+ztEE04Y1F9UF7cqZnxqQLlKmIqsOepLeDOkRXjDtNQKBgQDQtWYbewCmGuo1+AR7
+wStOXpfhqdu6NxAwX+S06ZdAV7f4duMFb//6uJ/c997qwRlNvRvpPlJBdcT3qziS
+zjvR8uk2YN5N9D+ZN0Yi11vltUYVPeGpoAk4L7+Ruj5RTVqk5iZEZ1RbAv5cRIm0
+D24H4W0Cj/aldDED2pwTb9ukEwKBgQDBUv/s7gUNM3LCkNLc1EDg2adSzoUopxHU
+jIQCitXt5iFpY2Lv2tpaIz4ux4HQGwhmao2MiUYOMit0++/pFaKur66tgRQRsr7F
+uS1fpjqtS51/5uFuc1xcWjVquidZXH9u9iChrYvim6SBkxzFsbu4kXNOj4I562cm
+E2VWH5GETwKBgQCo5ePX4VbJFYbsXeXi8JRHO63V5Uv4Co+DVlcTQOYyH8q1vCBE
+Sjrxf29/tugjOllr29o2i0StzMy1UU7bHyKx6M5qP0In+71sFJshnv6ziltI3Wc9
+ilFrstho6jt8OAle4RGe0bAmZunJaX22xbXZkshRBognpTv1Tnh4ElHBGQKBgHIW
+nVohjXGg7xTLiuUvjaokSI6hugunrOoWksE9VcqziPw83uJV8Y5IRiYtLvq1OVvX
+ffl1+ZXfHa5ID+kqD3uvyhIynrljFxpwkcpkuzQR77zPcDJSeis2QVfey+H8qGe/
+cLp5RJhS6d5eBxjULshZbgbqwhuURKc/wwn0T1gZAoGAXu2wqwBE8ikhpaDVFYZR
+EZe0G1iq03CtabFL0oPiddytV9jJy4Mg457tkCj58uCVkxw/8qjXEnm5OoJAzSlJ
+Goc4wh1N1rngYRbSGG+LxD0DeI/Hj3T5tWezMBLCE7J+hieUuVjOB9ELKip7aTj3
+KLNVudOATWYGXyvzux6ciu0=
+-----END PRIVATE KEY-----