diff --git a/Dockerfile b/Dockerfile index 9900d28..c3f2a4b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -77,6 +77,10 @@ COPY files/tedge/software_update.toml /etc/tedge/operations/ COPY files/tedge/self_update.toml /etc/tedge/operations/ COPY files/tedge/self_update.sh /usr/bin/ COPY files/tedge/container_run.tpl /usr/share/tedge/ +# Container log_upload customer handler +COPY files/tedge/container-logs.sh /usr/bin/ +COPY files/tedge/log_upload.toml /etc/tedge/operations/ +COPY files/tedge/log_upload_container.toml /etc/tedge/operations/ ENV S6_BEHAVIOUR_IF_STAGE2_FAILS=2 diff --git a/files/tedge/container-logs.sh b/files/tedge/container-logs.sh new file mode 100755 index 0000000..57aab5a --- /dev/null +++ b/files/tedge/container-logs.sh @@ -0,0 +1,97 @@ +#!/bin/sh +set -e + +CONTAINER_NAME= +DATE_FROM=${DATE_FROM:-"24h"} +DATE_TO=${DATE_TO:-"0m"} +MAX_LINES=${MAX_LINES:-1000} +UPLOAD_URL= +TYPE=${TYPE:-container} + +while [ $# -gt 0 ]; do + case "$1" in + --container) + CONTAINER_NAME="$2" + shift + ;; + --type) + TYPE="$2" + shift + ;; + --since) + DATE_FROM="$2" + shift + ;; + --until) + DATE_TO="$2" + shift + ;; + --max-lines|-n) + MAX_LINES="$2" + shift + ;; + --url) + UPLOAD_URL="$2" + shift + ;; + --help|-h) + ;; + *) + ;; + esac + shift +done + +DOCKER_CMD=docker +if ! docker ps >/dev/null 2>&1; then + if command -V sudo >/dev/null 2>&1; then + DOCKER_CMD="sudo docker" + fi +fi + +CONTAINER_NAME=${CONTAINER_NAME:-} +if [ -z "$CONTAINER_NAME" ]; then + # Use the name of the container rather than the hostname as it human friendly + # and strip any leading slash (/) + CONTAINER_NAME=$($DOCKER_CMD inspect "$(hostname)" --format "{{.Name}}" | sed 's|^/||g') +fi + +TMP_LOG_DIR=$(mktemp -d) +# Ensure directory is always deleted afterwards +trap 'rm -rf -- "$TMP_LOG_DIR"' EXIT +TMP_FILE="${TMP_LOG_DIR}/${TYPE}_${CONTAINER_NAME}_$(date -Iseconds).log" + +# Add log header to give information about the contents +{ + echo "---------------- log parameters ----------------------" + echo "container: $CONTAINER_NAME" + echo "dateFrom: $DATE_FROM" + echo "dateTo: $DATE_TO" + echo "maxLines: $MAX_LINES" + echo "command: $DOCKER_CMD logs -n \"$MAX_LINES\" --since \"$DATE_FROM\" --until \"$DATE_TO\" \"$CONTAINER_NAME\"" + echo "------------------------------------------------------" + echo +} > "$TMP_FILE" + +# Write logs to file (stripping any ansci colour codes) +$DOCKER_CMD logs -n "$MAX_LINES" --since "$DATE_FROM" --until "$DATE_TO" "$CONTAINER_NAME" \ + | sed -e 's/\x1b\[[0-9;]*m//g' \ + | tee -a "$TMP_FILE" + +echo "Uploading log file to $UPLOAD_URL" >&2 + +# Use mtls if configured +if [ -f "$(tedge config get http.client.auth.key_file)" ] && [ -f "$(tedge config get http.client.auth.cert_file)" ]; then + # Upload using mtl + echo "Uploading log file using mtls" + curl -4 -sf \ + -XPUT \ + --data-binary "@$TMP_FILE" \ + --capath "$(tedge config get http.ca_path)" \ + --key "$(tedge config get http.client.auth.key_file)" \ + --cert "$(tedge config get http.client.auth.cert_file)" \ + "$UPLOAD_URL" +else + # Upload using default + curl -4 -sf -XPUT --data-binary "@$TMP_FILE" "$UPLOAD_URL" +fi diff --git a/files/tedge/log_upload.toml b/files/tedge/log_upload.toml new file mode 100644 index 0000000..9c878fa --- /dev/null +++ b/files/tedge/log_upload.toml @@ -0,0 +1,44 @@ +operation = "log_upload" + +[init] +action = "proceed" +on_success = "executing" + +[executing] +action = "proceed" +on_success = "check" + +[check] +script = "sh -c '[ ${.payload.type} = container ] && exit 0 || exit 1'" +on_exit.0 = "custom_log_handler" +on_exit.1 = "process" +on_exit._ = "failed" + +[custom_log_handler] +# Optional step where a self-update is performed +operation = "log_upload_${.payload.type}" +input.type = "${.payload.type}" +input.lines = "${.payload.lines}" +input.dateFrom = "${.payload.dateFrom}" +input.dateTo = "${.payload.dateTo}" +input.searchText = "${.payload.searchText}" +input.tedgeUrl = "${.payload.tedgeUrl}" +on_exec = "await-log-handler" + +[await-log-handler] +action = "await-operation-completion" +on_success = "successful" + +[process] +operation = "builtin:log_upload" +on_exec = "wait-for-builtin-log-handler" + +[wait-for-builtin-log-handler] +action = "await-operation-completion" +on_success = "successful" + +[successful] +action = "cleanup" + +[failed] +action = "cleanup" diff --git a/files/tedge/log_upload_container.toml b/files/tedge/log_upload_container.toml new file mode 100644 index 0000000..caf9c86 --- /dev/null +++ b/files/tedge/log_upload_container.toml @@ -0,0 +1,19 @@ +operation = "log_upload_container" + +[init] +action = "proceed" +on_success = "executing" + +[executing] +action = "proceed" +on_success = "process" + +[process] +script = "container-logs.sh --type ${.payload.type} -n ${.payload.lines} --since ${.payload.dateFrom} --until ${.payload.dateTo} --container ${.payload.searchText} --url ${.payload.tedgeUrl}" +on_success = "successful" + +[successful] +action = "cleanup" + +[failed] +action = "cleanup" diff --git a/files/tedge/plugins/tedge-log-plugin.toml b/files/tedge/plugins/tedge-log-plugin.toml index 30a8954..010be1d 100644 --- a/files/tedge/plugins/tedge-log-plugin.toml +++ b/files/tedge/plugins/tedge-log-plugin.toml @@ -1,4 +1,7 @@ files = [ { type = "software-management", path = "/data/tedge/logs/agent/workflow-software*" }, { type = "shell", path = "/data/tedge/logs/agent/c8y_Command-*" }, + + # Note: Custom log handler, log_upload_container and the path is ignored + { type = "container", path = "/tmp/container" }, ] \ No newline at end of file diff --git a/tests/main/operations.robot b/tests/main/operations.robot index 16940e8..dbc251b 100644 --- a/tests/main/operations.robot +++ b/tests/main/operations.robot @@ -43,6 +43,10 @@ Install application using docker compose ... {"name": "nodered-instance1", "version": "1.0.0", "softwareType": "container-group"} +Get Container Logs + ${operation}= Cumulocity.Get Log File container search_text=tedge maximum_lines=100 + Cumulocity.Operation Should Be SUCCESSFUL ${operation} + *** Keywords *** Get Configuration File [Arguments] ${typename}