-
Notifications
You must be signed in to change notification settings - Fork 649
tests: tag features collected from debug logs #15091
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
b969424
218f9ec
cb05d2f
c18b194
5017469
975119e
7f6d860
b87ce8e
bc414fc
cc7de48
08af4ac
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| #!/bin/bash | ||
|
|
||
| after_non_nested_task() { | ||
| # Write to the directory specified in the spread.yaml file for artifacts | ||
| local write_dir="${SPREAD_PATH}/feature-tags" | ||
| local task_dir="${write_dir}/${SPREAD_JOB//\//--}" | ||
| mkdir -p "$write_dir" | ||
| mkdir -p "$task_dir" | ||
| "$TESTSTOOLS"/journal-state get-log --no-pager --output cat | grep '"TRACE"' > "$task_dir"/journal.txt | ||
| cp /var/lib/snapd/state.json "$task_dir" | ||
| } | ||
|
|
||
| after_nested_task() { | ||
| local write_dir="${SPREAD_PATH}/feature-tags" | ||
| local task_dir="${write_dir}/${SPREAD_JOB//\//--}" | ||
| mkdir -p "$write_dir" | ||
| mkdir -p "$task_dir" | ||
|
|
||
| "$TESTSTOOLS"/remote.exec "sudo journalctl --no-pager --output cat | grep '\"TRACE\"'" > "$task_dir"/journal.txt | ||
| "$TESTSTOOLS"/remote.exec "sudo chmod 777 /var/lib/snapd/state.json" | ||
| "$TESTSTOOLS"/remote.pull "/var/lib/snapd/state.json" "$task_dir" | ||
| } | ||
|
|
||
|
|
||
| case "$1" in | ||
| --after-non-nested-task) | ||
| if [ -n "$TAG_FEATURES" ]; then | ||
| after_non_nested_task | ||
| fi | ||
| ;; | ||
| --after-nested-task) | ||
| if [ -n "$TAG_FEATURES" ]; then | ||
| after_nested_task | ||
| fi | ||
| ;; | ||
| *) | ||
| echo "unsupported argument: $1" | ||
| exit 1 | ||
| ;; | ||
| esac | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -648,6 +648,10 @@ prepare_suite() { | |
| prepare_classic | ||
| fi | ||
|
|
||
| if [ -n "$TAG_FEATURES" ]; then | ||
| snap set system journal.persistent=true | ||
| fi | ||
|
|
||
| # Make sure the suite starts with a clean environment and with the snapd state restored | ||
| # shellcheck source=tests/lib/reset.sh | ||
| "$TESTSLIB"/reset.sh --reuse-core | ||
|
|
@@ -733,6 +737,9 @@ prepare_suite_each() { | |
| } | ||
|
|
||
| restore_suite_each() { | ||
| if not tests.nested is-nested; then | ||
| "$TESTSLIB"/collect-features.sh --after-non-nested-task | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we should do this just when SPREAD_TAG_FEATURES is set
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The collect-features.sh script checks if TAG_FEATURES is set before actually calling the function. I thought that was easier than checking in all the places the script is called. If you think the check should be outside, I can move it outside |
||
| fi | ||
| local variant="$1" | ||
|
|
||
| rm -f "$RUNTIME_STATE_PATH/audit-stamp" | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| #!/usr/bin/env python3 | ||
|
|
||
| import argparse | ||
| from collections import defaultdict | ||
| import json | ||
| from typing import Any, TextIO | ||
|
|
||
|
|
||
| # This will be removed | ||
| class AllFeature: | ||
| name = "all" | ||
| parent = "all" | ||
|
|
||
| @staticmethod | ||
| def maybe_add_feature(feature_dict: dict[str, list[Any]], json_entry: dict[str, Any], state_json: dict[str, Any]): | ||
| feature_dict[AllFeature.parent].append({AllFeature.name: json_entry}) | ||
|
|
||
|
|
||
| FEATURE_LIST = [AllFeature] | ||
|
|
||
|
|
||
| def get_feature_dictionary(log_file: TextIO, feature_list: list[str], state_json: dict[str, Any]): | ||
| ''' | ||
| Extracts features from the journal entries and places them in a dictionary. | ||
|
|
||
| :param log_file: iterator of journal entries | ||
| :param feature_list: list of feature names to extract | ||
| :param state_json: dictionary of a state.json | ||
| :return: dictionary of features | ||
| :raises: ValueError if an invalid feature name is provided | ||
| :raises: RuntimeError if a line could not be parsed as json | ||
| ''' | ||
|
|
||
| feature_dict = defaultdict(list) | ||
| feature_classes = [cls for cls in FEATURE_LIST | ||
| if cls.name in feature_list] | ||
| if len(feature_classes) != len(feature_list): | ||
| raise ValueError( | ||
| "Error: Invalid feature name in feature list {}".format(feature_list)) | ||
|
|
||
| for line in log_file: | ||
| try: | ||
| line_json = json.loads(line) | ||
| for feature_class in feature_classes: | ||
| feature_class.maybe_add_feature(feature_dict, line_json, state_json) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess later on new classes with add feature depending on contents of the log entry?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes exactly. They would check keys and values and see if the log entry is relevant to them. If it's relevant, then they would grab the information they need and, if necessary, query the state.json for any extra info. |
||
| except json.JSONDecodeError: | ||
| raise RuntimeError("Could not parse line as json: {}".format(line)) | ||
| return feature_dict | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| parser = argparse.ArgumentParser( | ||
| description="""Given a set of features with journal entries, each in json format, and a | ||
| state.json, this script will search the text file and extract the features. Those | ||
| features will be saved in a dictionary and written to the indicated file in output.""") | ||
| parser.add_argument('-o', '--output', help='Output file', required=True) | ||
| parser.add_argument( | ||
| '-f', '--feature', help='Features to extract from journal {all}; can be repeated multiple times', nargs='+') | ||
| parser.add_argument( | ||
| '-j', '--journal', help='Text file containing journal entries', required=True, type=argparse.FileType('r')) | ||
| parser.add_argument( | ||
| '-s', '--state', help='state.json', required=True, type=argparse.FileType('r')) | ||
| args = parser.parse_args() | ||
|
|
||
| try: | ||
| state_json = json.load(args.state) | ||
| feature_dictionary = get_feature_dictionary(args.journal, args.features, state_json) | ||
| json.dump(feature_dictionary, open(args.output, "w")) | ||
| except json.JSONDecodeError: | ||
| raise RuntimeError("The state.json is not valid json") | ||
Uh oh!
There was an error while loading. Please reload this page.