summaryrefslogtreecommitdiffstats
path: root/test/units/TEST-04-JOURNAL.journal-gatewayd.sh
diff options
context:
space:
mode:
Diffstat (limited to '')
-rwxr-xr-xtest/units/TEST-04-JOURNAL.journal-gatewayd.sh223
1 files changed, 223 insertions, 0 deletions
diff --git a/test/units/TEST-04-JOURNAL.journal-gatewayd.sh b/test/units/TEST-04-JOURNAL.journal-gatewayd.sh
new file mode 100755
index 0000000..9c7a3d0
--- /dev/null
+++ b/test/units/TEST-04-JOURNAL.journal-gatewayd.sh
@@ -0,0 +1,223 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+if [[ ! -x /usr/lib/systemd/systemd-journal-gatewayd ]]; then
+ echo "Built without systemd-journal-gatewayd support, skipping the test"
+ exit 0
+fi
+
+LOG_FILE="$(mktemp)"
+
+at_exit() {
+ if [[ $? -ne 0 ]]; then
+ # The $LOG_FILE is potentially huge (as it might be a full copy of the current journal), so let's
+ # dump it at debug level under a specific syslog tag, so it's clearly separated from the actual test
+ # journal; things get very confusing otherwise.
+ systemd-cat -t log-file-dump -p debug cat "$LOG_FILE"
+ fi
+
+ rm -f "$LOG_FILE"
+}
+
+trap at_exit EXIT
+
+TEST_MESSAGE="-= This is a test message $RANDOM =-"
+TEST_TAG="$(systemd-id128 new)"
+
+BEFORE_TIMESTAMP="$(date +%s)"
+echo "$TEST_MESSAGE" | systemd-cat -t "$TEST_TAG"
+sleep 1
+journalctl --sync
+TEST_CURSOR="$(journalctl -q -t "$TEST_TAG" -n 0 --show-cursor | awk '{ print $3; }')"
+BOOT_CURSOR="$(journalctl -q -b -n 0 --show-cursor | awk '{ print $3; }')"
+AFTER_TIMESTAMP="$(date +%s)"
+
+/usr/lib/systemd/systemd-journal-gatewayd --version
+/usr/lib/systemd/systemd-journal-gatewayd --help
+
+# Default configuration (HTTP, socket activated)
+systemctl start systemd-journal-gatewayd.socket
+
+# /browse
+# We should get redirected to /browse by default
+curl -LSfs http://localhost:19531 >"$LOG_FILE"
+grep -qF "<title>Journal</title>" "$LOG_FILE"
+curl -LSfs http://localhost:19531/browse >"$LOG_FILE"
+grep -qF "<title>Journal</title>" "$LOG_FILE"
+(! curl -LSfs http://localhost:19531/foo/bar/baz)
+(! curl -LSfs http://localhost:19531/foo/../../../bar/../baz)
+
+# /entries
+# Accept: text/plain should be the default
+curl -LSfs http://localhost:19531/entries >"$LOG_FILE"
+grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" "$LOG_FILE"
+curl -LSfs --header "Accept: text/plain" http://localhost:19531/entries >"$LOG_FILE"
+grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" "$LOG_FILE"
+curl -LSfs --header "Accept: application/json" http://localhost:19531/entries >"$LOG_FILE"
+jq -se ".[] | select(.MESSAGE == \"$TEST_MESSAGE\")" "$LOG_FILE"
+curl -LSfs --header "Accept: application/json" http://localhost:19531/entries?boot >"$LOG_FILE"
+jq -se ".[] | select(.MESSAGE == \"$TEST_MESSAGE\")" "$LOG_FILE"
+curl -LSfs --header "Accept: application/json" http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
+jq -se "length == 1 and select(.[].MESSAGE == \"$TEST_MESSAGE\")" "$LOG_FILE"
+# Show 10 entries starting from $BOOT_CURSOR, skip the first 5
+curl -LSfs \
+ --header "Accept: application/json" \
+ --header "Range: entries=$BOOT_CURSOR:5:10" \
+ http://localhost:19531/entries >"$LOG_FILE"
+jq -se "length == 10" "$LOG_FILE"
+# Check if the specified cursor refers to an existing entry and return just that entry
+curl -LSfs \
+ --header "Accept: application/json" \
+ --header "Range: entries=$TEST_CURSOR" \
+ http://localhost:19531/entries?discrete >"$LOG_FILE"
+jq -se "length == 1 and select(.[].MESSAGE == \"$TEST_MESSAGE\")" "$LOG_FILE"
+# Check entry is present (resp. absent) when filtering by timestamp
+curl -LSfs \
+ --header "Range: realtime=$BEFORE_TIMESTAMP:" \
+ http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
+grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" "$LOG_FILE"
+curl -LSfs \
+ --header "Range: realtime=:$AFTER_TIMESTAMP" \
+ http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
+grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" "$LOG_FILE"
+curl -LSfs \
+ --header "Accept: application/json" \
+ --header "Range: realtime=:$BEFORE_TIMESTAMP" \
+ http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
+jq -se "length == 0" "$LOG_FILE"
+curl -LSfs \
+ --header "Accept: application/json" \
+ --header "Range: realtime=$AFTER_TIMESTAMP:" \
+ http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
+jq -se "length == 0" "$LOG_FILE"
+# Check positive and negative skip when filtering by timestamp
+echo "-= This is a second test message =-" | systemd-cat -t "$TEST_TAG"
+journalctl --sync
+TEST2_CURSOR="$(journalctl -q -t "$TEST_TAG" -n 0 --show-cursor | awk '{ print $3; }')"
+echo "-= This is a third test message =-" | systemd-cat -t "$TEST_TAG"
+journalctl --sync
+sleep 1
+END_TIMESTAMP="$(date +%s)"
+curl -LSfs \
+ --header "Accept: application/json" \
+ --header "Range: realtime=$BEFORE_TIMESTAMP::1:1" \
+ http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
+jq -se "length == 1 and select(.[].__CURSOR == \"$TEST2_CURSOR\")" "$LOG_FILE"
+curl -LSfs \
+ --header "Accept: application/json" \
+ --header "Range: realtime=$END_TIMESTAMP::-1:1" \
+ http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
+jq -se "length == 1 and select(.[].__CURSOR == \"$TEST2_CURSOR\")" "$LOG_FILE"
+
+# No idea how to properly parse this (jq won't cut it), so let's at least do some sanity checks that every
+# line is either empty or begins with data:
+curl -LSfs --header "Accept: text/event-stream" http://localhost:19531/entries >"$LOG_FILE"
+awk '!/^(data: \{.+\}|)$/ { exit 1; }' "$LOG_FILE"
+# Same thing as journalctl --output=export
+mkdir /tmp/remote-journal
+curl -LSfs --header "Accept: application/vnd.fdo.journal" http://localhost:19531/entries >"$LOG_FILE"
+/usr/lib/systemd/systemd-journal-remote --output=/tmp/remote-journal/system.journal --split-mode=none "$LOG_FILE"
+journalctl --directory=/tmp/remote-journal -t "$TEST_TAG" --grep "$TEST_MESSAGE"
+rm -rf /tmp/remote-journal/*
+# Let's do the same thing again, but let systemd-journal-remote spawn curl itself
+/usr/lib/systemd/systemd-journal-remote --url=http://localhost:19531/entries \
+ --output=/tmp/remote-journal/system.journal \
+ --split-mode=none
+journalctl --directory=/tmp/remote-journal -t "$TEST_TAG" --grep "$TEST_MESSAGE"
+rm -rf /tmp/remote-journal
+
+# /machine
+curl -LSfs http://localhost:19531/machine >"$LOG_FILE"
+jq . "$LOG_FILE"
+
+# /fields
+curl -LSfs http://localhost:19531/fields/MESSAGE >"$LOG_FILE"
+grep -qE -- "$TEST_MESSAGE" "$LOG_FILE"
+curl -LSfs http://localhost:19531/fields/_TRANSPORT
+(! curl -LSfs http://localhost:19531/fields)
+(! curl -LSfs http://localhost:19531/fields/foo-bar-baz)
+
+systemctl stop systemd-journal-gatewayd.{socket,service}
+
+if ! command -v openssl >/dev/null; then
+ echo "openssl command not available, skipping the HTTPS tests"
+ exit 0
+fi
+
+# Generate a self-signed certificate for systemd-journal-gatewayd
+#
+# Note: older OpenSSL requires a config file with some extra options, unfortunately
+cat >/tmp/openssl.conf <<EOF
+[ req ]
+prompt = no
+distinguished_name = req_distinguished_name
+
+[ req_distinguished_name ]
+C = CZ
+L = Brno
+O = Foo
+OU = Bar
+CN = localhost
+EOF
+openssl req -x509 -nodes -newkey rsa:2048 -sha256 -days 7 \
+ -config /tmp/openssl.conf \
+ -keyout /tmp/key.pem -out /tmp/cert.pem
+# Start HTTPS version of gatewayd via the systemd-socket-activate tool to give it some coverage as well
+systemd-socket-activate --listen=19531 -- \
+ /usr/lib/systemd/systemd-journal-gatewayd \
+ --cert=/tmp/cert.pem \
+ --key=/tmp/key.pem \
+ --file="/var/log/journal/*/*.journal" &
+GATEWAYD_PID=$!
+sleep 1
+
+# Do a limited set of tests, since the underlying code should be the same past the HTTPS transport
+curl -LSfsk https://localhost:19531 >"$LOG_FILE"
+grep -qF "<title>Journal</title>" "$LOG_FILE"
+curl -LSfsk https://localhost:19531/entries >"$LOG_FILE"
+grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" "$LOG_FILE"
+curl -LSfsk --header "Accept: application/json" https://localhost:19531/entries >"$LOG_FILE"
+jq -se ".[] | select(.MESSAGE == \"$TEST_MESSAGE\")" "$LOG_FILE"
+curl -LSfsk https://localhost:19531/machine >"$LOG_FILE"
+jq . "$LOG_FILE"
+curl -LSfsk https://localhost:19531/fields/_TRANSPORT
+
+kill "$GATEWAYD_PID"
+
+# Test a couple of error scenarios
+GATEWAYD_FILE="$(mktemp /tmp/test-gatewayd-XXX.journal)"
+
+/usr/lib/systemd/systemd-journal-remote --output="$GATEWAYD_FILE" --getter="journalctl -n5 -o export"
+systemd-run --unit="test-gatewayd.service" --socket-property="ListenStream=19531" \
+ /usr/lib/systemd/systemd-journal-gatewayd --file="$GATEWAYD_FILE"
+
+# Call an unsupported endpoint together with some garbage data - gatewayd should not send garbage in return
+# See: https://github.com/systemd/systemd/issues/9858
+OUT="$(mktemp)"
+for _ in {0..4}; do
+ (! curl --fail-with-body -d "please process this🐱 $RANDOM" -L http://localhost:19531/upload | tee "$OUT")
+ (! grep '[^[:print:]]' "$OUT")
+done
+(! curl --fail-with-body --upload-file "$GATEWAYD_FILE" -L http://localhost:19531/upload | tee "$OUT")
+(! grep '[^[:print:]]' "$OUT")
+rm -rf "$OUT"
+
+curl -LSfs http://localhost:19531/browse >"$LOG_FILE"
+grep -qF "<title>Journal</title>" "$LOG_FILE"
+# Nuke the file behind the /browse endpoint
+mv /usr/share/systemd/gatewayd/browse.html /usr/share/systemd/gatewayd/browse.html.bak
+(! curl --fail-with-body -L http://localhost:19531/browse)
+mv /usr/share/systemd/gatewayd/browse.html.bak /usr/share/systemd/gatewayd/browse.html
+curl -LSfs http://localhost:19531/browse >"$LOG_FILE"
+grep -qF "<title>Journal</title>" "$LOG_FILE"
+
+# Nuke the journal file
+mv "$GATEWAYD_FILE" "$GATEWAYD_FILE.bak"
+(! curl --fail-with-body -L http://localhost:19531/fields/_PID)
+mv "$GATEWAYD_FILE.bak" "$GATEWAYD_FILE"
+curl -LSfs http://localhost:19531/fields/_PID
+
+systemctl stop test-gatewayd.{socket,service}
+rm -f "$GATEWAYD_FILE"