diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..897f371 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +FROM registry.access.redhat.com/ubi8/ubi:8.1 + +WORKDIR /tests + +COPY main.sh . +COPY testcases/smoke_test.sh ./testcases/ + +RUN yum -y install wget +RUN yum -y install jq + +RUN wget https://github.com/mingrammer/flog/releases/download/v0.4.3/flog_0.4.3_linux_amd64.tar.gz \ + && tar -xvf flog_0.4.3_linux_amd64.tar.gz \ + && cp flog /usr/local/bin + +ENTRYPOINT ["./main.sh"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..d1fccee --- /dev/null +++ b/README.md @@ -0,0 +1,17 @@ +## Quest + +This repository contains integration tests and load generation tests for Parseable server. Tests are written in shell script and bundled in a container. + +### Build test container locally + +``` +docker build . -t parseable/test:edge +``` + +### Running tests + +Use the below format to run tests against a Parseable server. The first argument is the test name, the second argument is the server URL, the third argument is the username and the fourth argument is the password. + +``` +docker run parseable/test:edge smoke http://demo.parseable.io parseable parseable +``` diff --git a/main.sh b/main.sh new file mode 100644 index 0000000..c535903 --- /dev/null +++ b/main.sh @@ -0,0 +1,33 @@ +#!/bin/sh +# +# Parseable Server (C) 2023 Cloudnatively Pvt. Ltd. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +mode=$1 +endpoint=$2 +username=$3 +password=$4 + +run_smoke_test () { + stream_name=$(head /dev/urandom | tr -dc a-z | head -c10) + ./testcases/smoke_test.sh "$endpoint" "$stream_name" "$username" "$password" + return $? +} + +case "$mode" in + "smoke") run_smoke_test + ;; +esac diff --git a/testcases/smoke-test.sh b/testcases/smoke-test.sh new file mode 100644 index 0000000..bbbffb7 --- /dev/null +++ b/testcases/smoke-test.sh @@ -0,0 +1,300 @@ +#!/bin/sh +# +# Parseable Server (C) 2023 Cloudnatively Pvt. Ltd. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +parseable_url=$1 +stream_name=$2 +username=$3 +password=$4 + +events=50 +input_file=$PWD/input.json + +curl_std_opts=( -sS --header 'Content-Type: application/json' -w '\n\n%{http_code}' -u "$username":"$password" ) + +alert_body='{"alerts":[{"name":"server-fail-alert1","message":"server reported error status","rule":{"field":"http_status","contains":"500","repeats":5,"within":"10m"},"target":[{"name":"slack-target","server_url":"http://mailgun.com","api_key":"xxxx"}]}]}' + +schema_body='{"fields":[{"name":"host","data_type":"Utf8","nullable":true,"dict_id":0,"dict_is_ordered":false},{"name":"user-identifier","data_type":"Utf8","nullable":true,"dict_id":0,"dict_is_ordered":false},{"name":"datetime","data_type":"Utf8","nullable":true,"dict_id":0,"dict_is_ordered":false},{"name":"method","data_type":"Utf8","nullable":true,"dict_id":0,"dict_is_ordered":false},{"name":"request","data_type":"Utf8","nullable":true,"dict_id":0,"dict_is_ordered":false},{"name":"protocol","data_type":"Utf8","nullable":true,"dict_id":0,"dict_is_ordered":false},{"name":"status","data_type":"Int64","nullable":true,"dict_id":0,"dict_is_ordered":false},{"name":"bytes","data_type":"Int64","nullable":true,"dict_id":0,"dict_is_ordered":false},{"name":"referer","data_type":"Utf8","nullable":true,"dict_id":0,"dict_is_ordered":false},{"name":"labels","data_type":"Utf8","nullable":true,"dict_id":0,"dict_is_ordered":false}]}' + +# Generate events using flog (https://github.com/mingrammer/flog) and store it in input.json file +create_input_file () { + flog -f json -n "$events" -t log -o "$input_file" + sleep 2 + sed -i '1s/^/[/;$!s/$/,/;$s/$/]/' "$input_file" + return $? +} + +# Create stream +create_stream () { + response=$(curl "${curl_std_opts[@]}" --request PUT "$parseable_url"/api/v1/logstream/"$stream_name") + + if [ $? -ne 0 ]; then + printf "Failed to create log stream %s with exit code: %s\n" "$stream_name" "$?" + printf "Test create_stream: failed\n" + exit 1 + fi + + http_code=$(tail -n1 <<< "$response") + if [ "$http_code" -ne 200 ]; then + printf "Failed to create log stream %s with http code: %s and response: %s\n" "$stream_name" "$http_code" "$content" + printf "Test create_stream: failed\n" + exit 1 + fi + + content=$(sed '$ d' <<< "$response") + if [ "$content" != "Created log stream $stream_name" ]; then + printf "Failed to create log stream $stream_name with response: %s\n" "$content" + printf "Test create_stream: failed\n" + exit 1 + fi + + printf "Test create_stream: successful\n" + return 0 +} + +# Post log data to the stream +post_event_data () { + create_input_file + if [ $? -ne 0 ]; then + printf "Failed to create log data to be posted to %s with exit code: %s\n" "$stream_name" "$?" + printf "Test post_event_data: failed\n" + exit 1 + fi + + content=$(cat "$input_file") + response=$(curl "${curl_std_opts[@]}" --request POST "$parseable_url"/api/v1/logstream/"$stream_name" --data-raw "$content") + if [ $? -ne 0 ]; then + printf "Failed to post log data to %s with exit code: %s\n" "$stream_name" "$?" + printf "Test post_event_data: failed\n" + exit 1 + fi + + http_code=$(tail -n1 <<< "$response") + if [ "$http_code" -ne 200 ]; then + printf "Failed to create log stream %s with http code: %s and response: %s\n" "$stream_name" "$http_code" "$content" + printf "Test post_event_data: failed\n" + exit 1 + fi + + content=$(sed '$ d' <<< "$response") + if [ "$content" != "Successfully posted $events events" ]; then + printf "Failed to post log data to %s with response: %s\n" "$stream_name" "$content" + printf "Test post_event_data: failed\n" + exit 1 + fi + + printf "Test post_event_data: successful\n" + return 0 +} + +# List all log stream and [TODO] verify if the stream is created +list_log_streams () { + response=$(curl "${curl_std_opts[@]}" --request GET "$parseable_url"/api/v1/logstream) + if [ $? -ne 0 ]; then + printf "Failed to list log streams with exit code: %s\n" "$?" + printf "Test list_log_streams: failed\n" + exit 1 + fi + + http_code=$(tail -n1 <<< "$response") + if [ "$http_code" -ne 200 ]; then + printf "Failed to list all log streams with http code: %s and response: %s" "$http_code" "$content" + printf "Test list_log_streams: failed\n" + exit 1 + fi + + content=$(sed '$ d' <<< "$response") + echo "$content" > "$PWD/log_streams.json" + + if [ "$(jq < $PWD/log_streams.json '[.[].name | select(. == "'"$stream_name"'")] | length')" -ne 1 ]; then + printf "Failed to find new log stream %s in list stream result: %s\n" "$stream_name" "$content" + printf "Test list_log_streams: failed\n" + exit 1 + fi + + printf "Test list_log_streams: successful\n" + return 0 +} + +# Get Stream's schema and [TODO] validate its schema +get_streams_schema () { + response=$(curl "${curl_std_opts[@]}" --request GET "$parseable_url"/api/v1/logstream/"$stream_name"/schema) + if [ $? -ne 0 ]; then + printf "Failed to fetch stream schema with exit code: %s\n" "$?" + printf "Test get_streams_schema: failed\n" + exit 1 + fi + + http_code=$(tail -n1 <<< "$response") + if [ "$http_code" -ne 200 ]; then + printf "Failed to get schema for stream %s with http code: %s and response: %s" "$stream_name" "$http_code" "$content" + printf "Test get_streams_schema: failed\n" + exit 1 + fi + + content=$(sed '$ d' <<< "$response") + if [ "$content" != "$schema_body" ]; then + printf "Get schema response doesn't match with expected schema.\n" + printf "Schema expected: %s\n" "$schema_body" + printf "Schema returned: %s\n" "$content" + printf "Test get_streams_schema: failed\n" + exit 1 + fi + + printf "Test get_streams_schema: successful\n" + return 0 +} + +# Query the log stream and verify if count of events is equal to the number of events posted +query_log_stream() { + # Query last two minutes of data only + end_time=$(date "+%Y-%m-%dT%H:%M:%S%:z") + start_time=$(date --date="@$(($(date +%s)-120))" "+%Y-%m-%dT%H:%M:%S%:z") + + response=$(curl "${curl_std_opts[@]}" --request POST "$parseable_url"/api/v1/query --data-raw '{ + "query": "select count(*) from '$stream_name'", + "startTime": "'$start_time'", + "endTime": "'$end_time'" + }') + if [ $? -ne 0 ]; then + printf "Failed to query log data from %s with exit code: %s\n" "$stream_name" "$?" + printf "Test query_log_stream: failed\n" + exit 1 + fi + + http_code=$(tail -n1 <<< "$response") + if [ "$http_code" -ne 200 ]; then + printf "Failed to query stream %s with http code: %s and response: %s" "$stream_name" "$http_code" "$content" + printf "Test query_log_stream: failed\n" + exit 1 + fi + + content=$(sed '$ d' <<< "$response") + queryResult=$(echo "$content" | cut -d ':' -f2 | cut -d '}' -f1) + if [ "$queryResult" != $events ]; then + printf "Validation failed. Count of events returned from query does not match with the ones posted.\n" + printf "Test query_log_stream: failed\n" + exit 1 + fi + printf "Test query_log_stream: successful\n" + return 0 +} + +# Set Alert +set_alert () { + response=$(curl "${curl_std_opts[@]}" --request PUT "$parseable_url"/api/v1/logstream/"$stream_name"/alert --data-raw "$alert_body") + if [ $? -ne 0 ]; then + printf "Failed to set alert for %s with exit code: %s\n" "$stream_name" "$?" + printf "Test set_alert: failed\n" + exit 1 + fi + + http_code=$(tail -n1 <<< "$response") + if [ "$http_code" -ne 200 ]; then + printf "Failed to set alert for %s with http code: %s and response: %s\n" "$stream_name" "$http_code" "$content" + printf "Test set_alert: failed\n" + exit 1 + fi + + content=$(sed '$ d' <<< "$response") + if [ "$content" != "Set alert configuration for log stream $stream_name" ]; then + printf "Failed to set alert on log stream %s with response: %s\n" "$stream_name" "$content" + printf "Test set_alert: failed\n" + exit 1 + fi + + printf "Test set_alert: successful\n" + return 0 +} + +# Get Alert +get_alert () { + response=$(curl "${curl_std_opts[@]}" --request GET "$parseable_url"/api/v1/logstream/"$stream_name"/alert) + if [ $? -ne 0 ]; then + printf "Failed to get alert for %s with exit code: %s\n" "$stream_name" "$?" + printf "Test get_alert: failed\n" + exit 1 + fi + + http_code=$(tail -n1 <<< "$response") + if [ "$http_code" -ne 200 ]; then + printf "Failed to get alert for %s with http code: %s and response: %s" "$stream_name" "$http_code" "$content" + printf "Test get_alert: failed\n" + exit 1 + fi + + content=$(sed '$ d' <<< "$response") + if [ "$content" != "$alert_body" ]; then + printf "Get alert response doesn't match with Alert config returned.\n" + printf "Alert set: %s\n" "$alert_body" + printf "Alert returned: %s\n" "$content" + printf "Test get_alert: failed\n" + exit 1 + fi + + printf "Test get_alert: successful\n" + return 0 +} + +# Delete stream +delete_stream () { + response=$(curl "${curl_std_opts[@]}" --request DELETE "$parseable_url"/api/v1/logstream/"$stream_name") + + if [ $? -ne 0 ]; then + printf "Failed to delete stream for %s with exit code: %s\n" "$stream_name" "$?" + printf "Test delete_stream: failed\n" + exit 1 + fi + + http_code=$(tail -n1 <<< "$response") + if [ "$http_code" -ne 200 ]; then + printf "Failed to delete log stream %s with http code: %s and response: %s\n" "$stream_name" "$http_code" "$content" + printf "Test delete_stream: failed\n" + exit 1 + fi + + content=$(sed '$ d' <<< "$response") + if [ "$content" != "log stream $stream_name deleted" ]; then + printf "Failed to delete log stream %s with response: %s" "$stream_name" "$content" + printf "Test delete_stream: failed\n" + exit 1 + fi + + printf "Test delete_stream: successful\n" + return 0 +} + +cleanup () { + rm -rf "$input_file" + rm -rf "$PWD/logstream_test.json" + return $? +} + +printf "======= Starting smoke tests =======\n" +printf "** Log stream name: %s **\n" "$stream_name" +printf "** Event count: %s **\n" "$events" +printf "====================================\n" +create_stream +post_event_data +list_log_streams +get_streams_schema +query_log_stream +set_alert +get_alert +delete_stream +cleanup +printf "======= Smoke tests completed ======\n"