From a521c3353743109b7cd099416b3a2817b7444558 Mon Sep 17 00:00:00 2001 From: "James O. D. Hunt" Date: Thu, 22 Mar 2018 12:58:32 +0000 Subject: [PATCH 1/2] scripts: Add data collection script Add a data collection script that can be run by users and its output pasted directly into a github issue. The script is designed to make diagnosing issues as easy as possible and its output provides a summary of a Kata Containers system including: - Versions of all components. - Details of container managers. - Errors found in the system journal. Fixes #80. Signed-off-by: James O. D. Hunt --- .gitignore | 1 + Makefile | 11 +- data/kata-collect-data.sh.in | 561 +++++++++++++++++++++++++++++++++++ 3 files changed, 570 insertions(+), 3 deletions(-) create mode 100644 data/kata-collect-data.sh.in diff --git a/.gitignore b/.gitignore index ff952d4d8a..1603ad3540 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /build-runv /kata-runtime +/data/kata-collect-data.sh diff --git a/Makefile b/Makefile index a2834bfc29..1863a4eaa7 100644 --- a/Makefile +++ b/Makefile @@ -64,6 +64,13 @@ LIBEXECDIR := $(PREFIX)/libexec SHAREDIR := $(PREFIX)/share DEFAULTSDIR := $(SHAREDIR)/defaults +COLLECT_SCRIPT = data/kata-collect-data.sh +COLLECT_SCRIPT_SRC = $(COLLECT_SCRIPT).in + +GENERATED_FILES += $(COLLECT_SCRIPT) +SCRIPTS += $(COLLECT_SCRIPT) +SCRIPTS_DIR := $(BINDIR) + PKGDATADIR := $(SHAREDIR)/$(PROJECT_DIR) PKGLIBDIR := $(LOCALSTATEDIR)/lib/$(PROJECT_DIR) PKGRUNDIR := $(LOCALSTATEDIR)/run/$(PROJECT_DIR) @@ -124,8 +131,6 @@ DESTSYSCONFIG := $(abspath $(DESTSYSCONFDIR)/$(CONFIG_FILE)) DESTSHAREDIR := $(DESTDIR)/$(SHAREDIR) -SCRIPTS_DIR := $(BINDIR) - # list of variables the user may wish to override USER_VARS += ARCH USER_VARS += BINDIR @@ -380,7 +385,7 @@ install-scripts: $(foreach f,$(SCRIPTS),$(call INSTALL_EXEC,$f,$(SCRIPTS_DIR))) clean: - $(QUIET_CLEAN)rm -f $(TARGET) $(CONFIG) $(GENERATED_GO_FILES) $(GENERATED_FILES) + $(QUIET_CLEAN)rm -f $(TARGET) $(CONFIG) $(GENERATED_GO_FILES) $(GENERATED_FILES) $(COLLECT_SCRIPT) show-usage: show-header @printf "• Overview:\n" diff --git a/data/kata-collect-data.sh.in b/data/kata-collect-data.sh.in new file mode 100644 index 0000000000..b26044c837 --- /dev/null +++ b/data/kata-collect-data.sh.in @@ -0,0 +1,561 @@ +#!/bin/bash +# +# Copyright (c) 2017-2018 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 +# + +script_name=${0##*/} +runtime_name="@RUNTIME_NAME@" +runtime=$(command -v "$runtime_name" 2>/dev/null) +issue_url="@PROJECT_BUG_URL@" +script_version="@VERSION@ (commit @COMMIT@)" + +typeset -r unknown="unknown" + +# Maximum number of errors to show for a single system component +# (such as runtime or proxy). +PROBLEM_LIMIT=${PROBLEM_LIMIT:-50} + +# List of patterns used to detect problems in logfiles. +problem_pattern="(" +problem_pattern+="\&2 "ERROR: $script_name: $msg" + exit 1 +} + +msg() +{ + local msg="$*" + echo "$msg" +} + +heading() +{ + local name="$*" + echo -e "\n# $name\n" +} + +subheading() +{ + local name="$*" + echo -e "\n## $name\n" +} + +separator() +{ + echo -e '\n---\n' +} + +have_cmd() +{ + local cmd="$1" + + command -v "$cmd" &>/dev/null + local ret=$? + + if [ $ret -eq 0 ]; then + msg "Have \`$cmd\`" + else + msg "No \`$cmd\`" + fi + + [ $ret -eq 0 ] +} + +show_quoted_text() +{ + local text="$*" + + echo "\`\`\`" + echo "$text" + echo "\`\`\`" +} + +run_cmd_and_show_quoted_output() +{ + local cmd="$*" + + local output + + msg "Output of \"\`$cmd\`\":" + output=$(eval "$cmd" 2>&1) + show_quoted_text "$output" +} + +show_runtime_configs() +{ + local configs config + + heading "Runtime config files" + + configs=$($runtime --@PROJECT_TYPE@-show-default-config-paths) + if [ $? -ne 0 ]; then + version=$($runtime --version|tr '\n' ' ') + die "failed to check config files - runtime is probably too old ($version)" + fi + + subheading "Runtime default config files" + + show_quoted_text "$configs" + + # add in the standard defaults for good measure "just in case" + configs+=" /etc/@PROJECT_TAG@/configuration.toml" + configs+=" /usr/share/defaults/@PROJECT_TAG@/configuration.toml" + configs+=" @DESTCONFIG@" + configs+=" @DESTSYSCONFIG@" + + # create a unique list of config files + configs=$(echo $configs|tr ' ' '\n'|sort -u) + + subheading "Runtime config file contents" + + for config in $configs; do + if [ -e "$config" ]; then + run_cmd_and_show_quoted_output "cat \"$config\"" + else + msg "Config file \`$config\` not found" + fi + done + + separator +} + +show_log_details() +{ + heading "Logfiles" + + show_runtime_log_details + show_proxy_log_details + show_shim_log_details + + separator +} + +show_runtime_log_details() +{ + subheading "Runtime logs" + + find_system_journal_problems "runtime" "@RUNTIME_NAME@" "" +} + +find_system_journal_problems() +{ + local name="$1" + local program="$2" + local unit="$3" + + # select by identifier + local selector="-t" + + local data_source="system journal" + + local problems=$(journalctl -q -o cat -a "$selector" "$program" |\ + grep "time=" |\ + egrep -i "$problem_pattern" |\ + egrep -iv "$problem_exclude_pattern" |\ + tail -n ${PROBLEM_LIMIT}) + + if [ -n "$problems" ]; then + msg "Recent $name problems found in $data_source:" + show_quoted_text "$problems" + else + msg "No recent $name problems found in $data_source." + fi +} + +show_proxy_log_details() +{ + subheading "Proxy logs" + + find_system_journal_problems "proxy" "@PROJECT_TYPE@-proxy" "" +} + +show_shim_log_details() +{ + subheading "Shim logs" + + find_system_journal_problems "shim" "@PROJECT_TYPE@-shim" "" +} + +show_package_versions() +{ + heading "Packages" + + local pattern="(" + local project + + # CC 2.x runtime. This shouldn't be installed but let's check anyway + pattern+="cc-oci-runtime" + + # core components + for project in @PROJECT_TYPE@ + do + pattern+="|${project}-proxy" + pattern+="|${project}-runtime" + pattern+="|${project}-shim" + done + + # assets + pattern+="|clear-containers-image" + pattern+="|linux-container" + + # optimised hypervisor + pattern+="|qemu-lite" + + # default distro hypervisor + pattern+="|qemu-system-x86" + + pattern+=")" + + if have_cmd "dpkg"; then + run_cmd_and_show_quoted_output "dpkg -l|egrep \"$pattern\"" + fi + + if have_cmd "rpm"; then + run_cmd_and_show_quoted_output "rpm -qa|egrep \"$pattern\"" + fi + + separator +} + +show_container_mgr_details() +{ + heading "Container manager details" + + if have_cmd "docker"; then + subheading "Docker" + run_cmd_and_show_quoted_output "docker version" + run_cmd_and_show_quoted_output "docker info" + run_cmd_and_show_quoted_output "systemctl show docker" + fi + + if have_cmd "kubectl"; then + subheading "Kubernetes" + run_cmd_and_show_quoted_output "kubectl version" + run_cmd_and_show_quoted_output "kubectl config view" + run_cmd_and_show_quoted_output "systemctl show kubelet" + + if have_cmd "crio"; then + run_cmd_and_show_quoted_output "crio --version" + run_cmd_and_show_quoted_output "systemctl show crio" + fi + fi + + separator +} + +show_meta() +{ + local date + + heading "Meta details" + + date=$(date '+%Y-%m-%d.%H:%M:%S.%N%z') + msg "Running \`$script_name\` version \`$script_version\` at \`$date\`." + + separator +} + +show_runtime() +{ + local cmd + + msg "Runtime is \`$runtime\`." + + cmd="@PROJECT_TYPE@-env" + heading "\`$cmd\`" + run_cmd_and_show_quoted_output "$runtime $cmd" + + separator +} + +# Parameter 1: Path to disk image file. +# Returns: Agent version string, or "$unknown" on error. +get_agent_version() +{ + local img="$1" + + [ -z "$img" ] && { echo "$unknown"; return;} + [ -e "$img" ] || { echo "$unknown"; return;} + + local loop_device + local partition_path + local partitions + local partition + local count + local mountpoint + local version + local expected + + loop_device=$(loopmount_image "$img") + if [ -z "$loop_device" ]; then + echo "$unknown" + return + fi + + partitions=$(get_partitions "$loop_device") + count=$(echo "$partitions"|wc -l) + + expected=1 + + if [ "$count" -ne "$expected" ]; then + release_device "$loop_device" + echo "$unknown" + return + fi + + partition="$partitions" + + partition_path="/dev/${partition}" + if [ ! -e "$partition_path" ]; then + release_device "$loop_device" + echo "$unknown" + return + fi + + mountpoint=$(mount_partition "$partition_path") + + agent="/bin/@PROJECT_TYPE@-agent" + + version=$(chroot "$mountpoint" "$agent" --version 2>/dev/null) + + unmount_partition "$mountpoint" + release_device "$loop_device" + + [ -z "$version" ] && version="$unknown" + + echo "$version" +} + +# Returns: Full path to the image file. +get_image_file() +{ + local cmd="@PROJECT_TYPE@-env" + local cmdline="$runtime $cmd" + + image=$(eval "$cmdline" 2>/dev/null |\ + grep -A 1 '^\[Image\]' |\ + egrep "\ =" |\ + awk '{print $3}' |\ + tr -d '"') + + echo "$image" +} + +# Parameter 1: Path to disk image file. +# Returns: Path to loop device. +loopmount_image() +{ + local img="$1" + [ -n "$img" ] || die "need image file" + + local device_path + + losetup -fP "$img" + + device_path=$(losetup -j "$img" |\ + cut -d: -f1 |\ + sort -k1,1 |\ + tail -1) + + echo "$device_path" +} + +# Parameter 1: Path to loop device. +# Returns: Partition names. +get_partitions() +{ + local device_path="$1" + [ -n "$device_path" ] || die "need device path" + + local device + local partitions + + device=${device_path/\/dev\//} + + partitions=$(lsblk -nli -o NAME "${device_path}" |\ + grep -v "^${device}$") + + echo "$partitions" +} + +# Parameter 1: Path to disk partition device. +# Returns: Mountpoint. +mount_partition() +{ + local partition="$1" + [ -n "$partition" ] || die "need partition" + [ -e "$partition" ] || die "partition does not exist: $partition" + + local mountpoint + + mountpoint=$(mktemp -d) + + mount -oro,noload "$partition" "$mountpoint" + + echo "$mountpoint" +} + +# Parameter 1: Mountpoint. +unmount_partition() +{ + local mountpoint="$1" + [ -n "$mountpoint" ] || die "need mountpoint" + [ -n "$mountpoint" ] || die "mountpoint does not exist: $mountpoint" + + umount "$mountpoint" +} + +# Parameter 1: Loop device path. +release_device() +{ + local device="$1" + [ -n "$device" ] || die "need device" + [ -e "$device" ] || die "device does not exist: $device" + + losetup -d "$device" +} + +show_agent_version() +{ + local image + local version + + image=$(get_image_file) + version=$(get_agent_version "$image") + + heading "Agent" + + msg "version:" + + show_quoted_text "$version" + + separator +} + +main() +{ + args=$(getopt \ + -n "$script_name" \ + -a \ + --options="dhv" \ + --longoptions="debug help version" \ + -- "$@") + + eval set -- "$args" + [ $? -ne 0 ] && { usage && exit 1; } + [ $# -eq 0 ] && { usage && exit 0; } + + while [ $# -gt 1 ] + do + case "$1" in + -d|--debug) + set -x + ;; + + -h|--help) + usage && exit 0 + ;; + + -v|--version) + version && exit 0 + ;; + + --) + shift + break + ;; + esac + shift + done + + [ $(id -u) -eq 0 ] || die "Need to run as root" + [ -n "$runtime" ] || die "cannot find runtime '$runtime_name'" + + show_meta + show_runtime + show_runtime_configs + show_agent_version + show_log_details + show_container_mgr_details + show_package_versions +} + +main "$@" From f93b6d2dd1f1d1d2117f33236a819c4a6a771b38 Mon Sep 17 00:00:00 2001 From: "James O. D. Hunt" Date: Thu, 22 Mar 2018 13:04:05 +0000 Subject: [PATCH 2/2] github: Add issue template Add a template that will offer guidance for when users visit the following URL: - https://github.com/clearcontainers/runtime/issues/new Crucially, the template asks the users to run the `kata-collect-data.sh` script and paste the output direct into the issue. Signed-off-by: James O. D. Hunt --- .github/ISSUE_TEMPLATE.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE.md diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000000..e6bde08576 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,17 @@ +# Description of problem + +(replace this text with the list of steps you followed) + +# Expected result + +(replace this text with an explanation of what you thought would happen) + +# Actual result + +(replace this text with details of what actually happened) + +--- + +(replace this text with the output of the `kata-collect-data.sh` script, after +you have reviewed its content to ensure it does not contain any private +information).