From 961183ed706aa87a4e5b4e8401db8fc1ca74a4ee Mon Sep 17 00:00:00 2001 From: Pierre Equoy Date: Thu, 11 Apr 2024 14:57:36 +0800 Subject: [PATCH] Make sure the device reboots after snap refresh/revert tests (BugFix) (#1124) * Provide more information in Snapd._poll_change() In order to get more data in the job logs, Snapd._poll_change() is amended to do the following: - Outuput info about the task when it's in the "Wait" status (and return, as it likely means the snap is waiting for a reboot) - Outuput info about the task when it's in the "Error" status - If an ongoing task (status = "Doing") has a progress label assigned, print the current progress in % (similar to what's done in the snap command itself). This is useful to investigate if tasks like snap downloads have frozen at some point. Unit tests for the _poll_change() method are also added. * Return the response when calling Snapd.install() and remove() In order to be more consistent with other methods, these methods now return the response. Unit tests are added for these methods as well. * Add timeout argument to snap_update_test.py and output more info Script can now be called with the --timeout argument (defaulting to 300 seconds). This is passed to the Snapd() instance, along with the verbosity flag that enables Snapd._info() to output useful information for the job logs. * Replace logging with print statements in snap_update_test.py Print statements make more sense because any information captured during the snap commands should be made available to the Checkbox results later on. * Modify snap-refresh-revert jobs - Reboot commands are moved inside the snapd/snap-[refresh|revert]-* jobs. This is to make sure the device is rebooted right after the command is issued (with previous version, some other jobs might be run between the refresh/revert command and the reboot command, making the test results unreliable) - As a result, the snapd/snap-[refresh|revert]-* are flagged as noreturn - Because Checkbox does not capture the outputs of noreturn jobs, their outputs are stored in the $PLAINBOX_SESSION_SHARE directory using `tee` - Reboot jobs are replaced with attachment jobs to upload the outputs along with the submissions * Replace reboot jobs with log attachment jobs in snap-refresh-revert test plan Fix CHECKBOX-1264 * Fix snapd unit tests * Add unit test to improve coverage * Fix unit test * Add template-id to log-attach jobs Following the work done in commit b8befd8ffff1f2dd6aee0485cf8a1c24a374f2c0, add template-id to the new templates that don't have them set yet. --- .../checkbox_support/snap_utils/snapd.py | 47 ++++-- .../snap_utils/tests/test_snapd.py | 150 ++++++++++++++++++ providers/base/bin/snap_update_test.py | 114 +++++++------ providers/base/tests/test_snap_update_test.py | 18 ++- providers/base/units/snapd/jobs.pxu | 109 +++++++------ providers/base/units/snapd/test-plan.pxu | 24 +-- 6 files changed, 327 insertions(+), 135 deletions(-) create mode 100644 checkbox-support/checkbox_support/snap_utils/tests/test_snapd.py diff --git a/checkbox-support/checkbox_support/snap_utils/snapd.py b/checkbox-support/checkbox_support/snap_utils/snapd.py index 3abcc47b2..4b60c44a7 100644 --- a/checkbox-support/checkbox_support/snap_utils/snapd.py +++ b/checkbox-support/checkbox_support/snap_utils/snapd.py @@ -89,8 +89,27 @@ def _poll_change(self, change_id): abort_result = self._abort_change(change_id) raise AsyncException(status, abort_result) for task in self.tasks(change_id): - if task['status'] == 'Doing': - self._info(task['summary']) + if task["status"] == "Doing": + if task["progress"]["label"]: + done = task["progress"]["done"] + total = task["progress"]["total"] + total_progress = done / total * 100 + message = "({}) {} ({:.1f}%)".format( + task["status"], task["summary"], total_progress + ) + else: + message = "({}) {}".format( + task["status"], task["summary"] + ) + self._info(message) + elif task["status"] == "Wait": + message = "({}) {}".format(task["status"], task["summary"]) + self._info(message) + return + elif task["status"] == "Error": + message = "({}) {}".format(task["status"], task["summary"]) + self._info(message) + raise AsyncException(task.get("log")) time.sleep(self._poll_interval) def _abort_change(self, change_id): @@ -110,23 +129,25 @@ def list(self, snap=None): return None raise - def install(self, snap, channel='stable', revision=None): - path = self._snaps + '/' + snap - data = {'action': 'install', 'channel': channel} + def install(self, snap, channel="stable", revision=None): + path = self._snaps + "/" + snap + data = {"action": "install", "channel": channel} if revision is not None: - data['revision'] = revision + data["revision"] = revision r = self._post(path, json.dumps(data)) - if r['type'] == 'async' and r['status'] == 'Accepted': - self._poll_change(r['change']) + if r["type"] == "async" and r["status"] == "Accepted": + self._poll_change(r["change"]) + return r def remove(self, snap, revision=None): - path = self._snaps + '/' + snap - data = {'action': 'remove'} + path = self._snaps + "/" + snap + data = {"action": "remove"} if revision is not None: - data['revision'] = revision + data["revision"] = revision r = self._post(path, json.dumps(data)) - if r['type'] == 'async' and r['status'] == 'Accepted': - self._poll_change(r['change']) + if r["type"] == "async" and r["status"] == "Accepted": + self._poll_change(r["change"]) + return r def find(self, search, exact=False): if exact: diff --git a/checkbox-support/checkbox_support/snap_utils/tests/test_snapd.py b/checkbox-support/checkbox_support/snap_utils/tests/test_snapd.py new file mode 100644 index 000000000..55fa0c262 --- /dev/null +++ b/checkbox-support/checkbox_support/snap_utils/tests/test_snapd.py @@ -0,0 +1,150 @@ +import json + +from unittest import TestCase +from unittest.mock import patch, MagicMock, ANY + +from checkbox_support.snap_utils.snapd import AsyncException, Snapd + + +class TestSnapd(TestCase): + @patch("checkbox_support.snap_utils.snapd.time.sleep") + @patch("checkbox_support.snap_utils.snapd.time.time") + def test_poll_change_done(self, mock_time, mock_sleep): + mock_self = MagicMock() + mock_self.change.return_value = "Done" + self.assertTrue(Snapd._poll_change(mock_self, 0)) + + @patch("checkbox_support.snap_utils.snapd.time.sleep") + @patch("checkbox_support.snap_utils.snapd.time.time") + def test_poll_change_timeout(self, mock_time, mock_sleep): + mock_time.side_effect = [0, 1] + mock_self = MagicMock() + mock_self._task_timeout = 0 + with self.assertRaises(AsyncException): + Snapd._poll_change(mock_self, 0) + + @patch("checkbox_support.snap_utils.snapd.time.sleep") + @patch("checkbox_support.snap_utils.snapd.time.time") + def test_poll_change_doing(self, mock_time, mock_sleep): + mock_time.return_value = 0 + mock_self = MagicMock() + mock_self.change.side_effect = ["Doing", "Done"] + mock_self._task_timeout = 0 + mock_self.tasks.return_value = [ + { + "summary": "Test", + "status": "Doing", + "progress": {"label": "", "done": 1, "total": 1}, + }, + ] + Snapd._poll_change(mock_self, 0) + message = "(Doing) Test" + mock_self._info.assert_called_with(message) + mock_self.change.side_effect = ["Doing", "Done"] + mock_self.tasks.return_value = [ + { + "summary": "Test", + "status": "Doing", + "progress": {"label": "Downloading", "done": 1, "total": 2}, + }, + ] + Snapd._poll_change(mock_self, 0) + message = "(Doing) Test (50.0%)" + mock_self._info.assert_called_with(message) + + @patch("checkbox_support.snap_utils.snapd.time.sleep") + @patch("checkbox_support.snap_utils.snapd.time.time") + def test_poll_change_wait(self, mock_time, mock_sleep): + mock_time.return_value = 0 + mock_self = MagicMock() + mock_self.change.return_value = "Wait" + mock_self._task_timeout = 0 + mock_self.tasks.return_value = [ + { + "summary": "Test", + "status": "Wait", + "progress": {"label": "", "done": 1, "total": 1}, + }, + ] + Snapd._poll_change(mock_self, 0) + message = "(Wait) Test" + mock_self._info.assert_called_with(message) + + @patch("checkbox_support.snap_utils.snapd.time.sleep") + @patch("checkbox_support.snap_utils.snapd.time.time") + def test_poll_change_error(self, mock_time, mock_sleep): + mock_time.return_value = 0 + mock_self = MagicMock() + mock_self.change.return_value = "Error" + mock_self._task_timeout = 0 + mock_self.tasks.return_value = [ + { + "summary": "Test", + "status": "Error", + "progress": {"label": "", "done": 1, "total": 1}, + }, + ] + message = "(Error) Test" + with self.assertRaises(AsyncException): + Snapd._poll_change(mock_self, 0) + mock_self._info.assert_called_with(message) + + def test_install_accepted(self): + mock_self = MagicMock() + mock_self._poll_change = MagicMock() + mock_self._post.return_value = { + "type": "async", + "status": "Accepted", + "change": "1", + } + response = Snapd.install(mock_self, "test") + self.assertEqual(response, mock_self._post.return_value) + mock_self._poll_change.assert_called_with("1") + + def test_install_other(self): + mock_self = MagicMock() + mock_self._poll_change = MagicMock() + mock_self._post.return_value = { + "type": "async", + "status": "Other", + "change": "1", + } + response = Snapd.install(mock_self, "test") + self.assertEqual(response, mock_self._post.return_value) + mock_self._poll_change.assert_not_called() + + def test_install_revision(self): + mock_self = MagicMock() + Snapd.install(mock_self, "test", revision="1") + test_data = {"action": "install", "channel": "stable", "revision": "1"} + mock_self._post.assert_called_with(ANY, json.dumps(test_data)) + + def test_remove_accepted(self): + mock_self = MagicMock() + mock_self._poll_change = MagicMock() + mock_self._post.return_value = { + "type": "async", + "status": "Accepted", + "change": "1", + } + response = Snapd.remove(mock_self, "test") + self.assertEqual(response, mock_self._post.return_value) + mock_self._poll_change.assert_called_with("1") + + def test_remove_other(self): + mock_self = MagicMock() + mock_self._poll_change = MagicMock() + mock_self._post.return_value = { + "type": "async", + "status": "Other", + "change": "1", + } + response = Snapd.remove(mock_self, "test") + self.assertEqual(response, mock_self._post.return_value) + mock_self._poll_change.assert_not_called() + + def test_remove_revision(self): + mock_self = MagicMock() + Snapd.remove(mock_self, "test", revision="1") + test_data = {"action": "remove", "revision": "1"} + mock_self._post.assert_called_with(ANY, json.dumps(test_data)) diff --git a/providers/base/bin/snap_update_test.py b/providers/base/bin/snap_update_test.py index b3f9b548b..5585299dd 100755 --- a/providers/base/bin/snap_update_test.py +++ b/providers/base/bin/snap_update_test.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright 2023 Canonical Ltd. +# Copyright 2023-2024 Canonical Ltd. # All rights reserved. # # Written by: @@ -21,19 +21,11 @@ import argparse from pathlib import Path import json -import logging import sys import time from checkbox_support.snap_utils.snapd import Snapd -logger = logging.getLogger(__name__) -logging.basicConfig( - level=logging.INFO, - format="%(asctime)s %(levelname)-8s %(message)s", - datefmt="%Y-%m-%d %H:%M:%S", -) - def guess_snaps() -> list: """ @@ -128,15 +120,15 @@ def load_change_info(path): with open(path, "r") as file: data = json.load(file) except FileNotFoundError: - logger.error("File not found: %s", path) - logger.error("Did the previous job run as expected?") - raise SystemExit(1) + error_msg = "File not found: {}. Did the previous job run as expected?" + raise SystemExit(error_msg.format(path)) return data class SnapRefreshRevert: - def __init__(self, name, revision, info_path): - self.snapd = Snapd() + def __init__(self, name, revision, info_path, timeout): + self.timeout = timeout + self.snapd = Snapd(task_timeout=self.timeout, verbose=True) self.snap_info = SnapInfo(name) self.path = info_path self.revision = revision @@ -146,88 +138,84 @@ def snap_refresh(self): data = {} original_revision = self.snap_info.installed_revision if original_revision == self.revision: - logger.error( - "Trying to refresh to the same revision (%s)!", self.revision - ) - raise SystemExit(1) + error_msg = "Trying to refresh to the same revision ({})!" + raise SystemExit(error_msg.format(self.revision)) data["name"] = self.name data["original_revision"] = original_revision data["destination_revision"] = self.revision - logger.info( - "Refreshing %s snap from revision %s to revision %s", - self.name, - original_revision, - self.revision, + print( + "Refreshing snap {} from revision {} to {}".format( + self.name, original_revision, self.revision + ) ) response = self.snapd.refresh( self.name, channel=self.snap_info.tracking_channel, revision=self.revision, - reboot=True, - ) - logger.info( - "Refreshing requested (channel %s, revision %s)", - self.snap_info.tracking_channel, - self.revision, ) data["change_id"] = response["change"] + print( + "Snap operation finished. " + "See `snap change {}` for more info".format(response["change"]) + ) save_change_info(self.path, data) - logger.info("Waiting for reboot...") + print("Waiting for reboot...") def snap_revert(self): data = load_change_info(self.path) original_rev = data["original_revision"] destination_rev = data["destination_revision"] - logger.info( - "Reverting %s snap (from revision %s to revision %s)", - self.name, - destination_rev, - original_rev, + print( + "Reverting snap {} from revision {} to {}".format( + self.name, destination_rev, original_rev + ) ) - response = self.snapd.revert(self.name, reboot=True) - logger.info("Reverting requested") + response = self.snapd.revert(self.name) data["change_id"] = response["change"] + print( + "Snap operation finished. " + "See `snap change {}` for more info".format(response["change"]) + ) save_change_info(self.path, data) - logger.info("Waiting for reboot...") + print("Waiting for reboot...") - def wait_for_snap_change(self, change_id, type, timeout=300): + def wait_for_snap_change(self, change_id, type): start_time = time.time() while True: result = self.snapd.change(str(change_id)) if result == "Done": - logger.info("%s snap %s complete", self.name, type) + print("{} snap {} complete".format(self.name, type)) return elif result == "Error": tasks = self.snapd.tasks(str(change_id)) for task in tasks: - logger.error( - "%s | %s | %s", - task["id"], - task["status"], - task["summary"], + print( + "{} | {} | {}".format( + task["id"], task["status"], task["summary"] + ) ) if task.get("log"): for log in task["log"]: - logger.error("\t %s", log) + print("\t {}".format(log)) raise SystemExit( "Error during snap {} {}.".format(self.name, type) ) current_time = time.time() - if current_time - start_time >= timeout: + if current_time - start_time >= self.timeout: raise SystemExit( "{} snap {} did not complete within {} seconds".format( - self.name, type, timeout + self.name, type, self.timeout ) ) - logger.info( - "Waiting for %s snap %s to be done...", self.name, type + print( + "Waiting for {} snap {} to be done...".format(self.name, type) ) - logger.info("Trying again in 10 seconds...") + print("Trying again in 10 seconds...") time.sleep(10) - def verify(self, type, timeout=300): - logger.info("Beginning verify...") + def verify(self, type): + print("Beginning verify...") if type not in ("refresh", "revert"): msg = ( "'{}' verification unknown. Can be either 'refresh' " @@ -236,8 +224,8 @@ def verify(self, type, timeout=300): raise SystemExit(msg) data = load_change_info(self.path) id = data["change_id"] - self.wait_for_snap_change(id, type, timeout) - logger.info("Checking %s status for snap %s...", type, self.name) + self.wait_for_snap_change(id, type) + print("Checking {} status for snap {}...".format(type, self.name)) current_rev = self.snapd.list(self.name)["revision"] if type == "refresh": @@ -251,9 +239,9 @@ def verify(self, type, timeout=300): ).format(current_rev, tested_rev) raise SystemExit(msg) else: - logger.info( - "PASS: current revision (%s) matches the expected revision", - current_rev, + print( + "PASS: current revision ({}) matches the expected " + "revision".format(current_rev) ) @@ -295,6 +283,11 @@ def main(args): "--revision", help="Revision to refresh to", ) + parser.add_argument( + "--timeout", + default=300, + help="Timeout for each task, in seconds (default: %(default)s))", + ) args = parser.parse_args(args) @@ -302,7 +295,10 @@ def main(args): print_resource_info() else: test = SnapRefreshRevert( - name=args.name, info_path=args.info_path, revision=args.revision + name=args.name, + info_path=args.info_path, + revision=args.revision, + timeout=args.timeout, ) if args.refresh: test.snap_refresh() diff --git a/providers/base/tests/test_snap_update_test.py b/providers/base/tests/test_snap_update_test.py index 8ca6ecc47..c20aa3e1b 100644 --- a/providers/base/tests/test_snap_update_test.py +++ b/providers/base/tests/test_snap_update_test.py @@ -146,6 +146,20 @@ def setUpClass(cls): def tearDownClass(cls): logging.disable(logging.NOTSET) + @patch("snap_update_test.SnapInfo") + @patch("snap_update_test.Snapd") + def test_init(self, mock_snapd, mock_snapinfo): + srr = snap_update_test.SnapRefreshRevert( + name="test", + revision="1", + info_path="/test", + timeout="10", + ) + self.assertEqual(srr.name, "test") + self.assertEqual(srr.path, "/test") + self.assertEqual(srr.revision, "1") + self.assertEqual(srr.timeout, "10") + def test_snap_refresh_same_revision(self): mock_self = MagicMock() mock_self.revision = "1" @@ -242,15 +256,17 @@ def test_wait_for_snap_change_error(self): def test_wait_for_snap_change_timeout(self): mock_self = MagicMock() + mock_self.timeout = -1 with self.assertRaises(SystemExit): snap_update_test.SnapRefreshRevert.wait_for_snap_change( - mock_self, change_id=1, type="refresh", timeout=-1 + mock_self, change_id=1, type="refresh" ) @patch("snap_update_test.time.time") @patch("snap_update_test.time.sleep") def test_wait_for_snap_change_ongoing(self, mock_sleep, mock_time): mock_self = MagicMock() + mock_self.timeout = 300 mock_self.snapd.change.side_effect = ["Doing", "Done"] mock_time.return_value = 1 snap_update_test.SnapRefreshRevert.wait_for_snap_change( diff --git a/providers/base/units/snapd/jobs.pxu b/providers/base/units/snapd/jobs.pxu index a1feafea9..88ed45eca 100644 --- a/providers/base/units/snapd/jobs.pxu +++ b/providers/base/units/snapd/jobs.pxu @@ -22,6 +22,7 @@ plugin: shell estimated_duration: 1m category_id: snapd user: root +flags: noreturn imports: from com.canonical.certification import snap_revision_info from com.canonical.plainbox import manifest @@ -30,24 +31,25 @@ requires: manifest.need_{type}_snap_update_test == "True" command: path="$PLAINBOX_SESSION_SHARE/{name}_snap_revision_info" - snap_update_test.py --refresh --revision {stable_rev} --info-path "$path" {name} + logpath="$PLAINBOX_SESSION_SHARE/snap-refresh-{type}-{name}-to-stable-rev.log" + snap_update_test.py --refresh --revision {stable_rev} --info-path "$path" {name} | tee "$logpath" + reboot unit: template template-resource: snap_revision_info template-unit: job -id: snapd/reboot-after-snap-refresh-{type}-{name}-to-stable-rev -template-id: snapd/reboot-after-snap-refresh-type-name-to-stable-rev -_summary: Reboot after {name} snap refresh to latest revision in stable channel -plugin: shell -flags: noreturn autorestart -estimated_duration: 3m +id: snapd/log-attach-after-snap-refresh-{type}-{name}-to-stable-rev +template-id: snapd/log-attach-after-snap-refresh-type-name-to-stable-rev +_summary: Attach logs after refreshing {name} snap to latest revision in stable channel +plugin: attachment +estimated_duration: 1s category_id: snapd -user: root -depends: snapd/snap-refresh-{type}-{name}-to-stable-rev +after: snapd/snap-refresh-{type}-{name}-to-stable-rev command: - echo "Waiting 90s for any snap operation to finish before rebooting..." - sleep 90 - reboot + logpath="$PLAINBOX_SESSION_SHARE/snap-refresh-{type}-{name}-to-stable-rev.log" + if [ -f "$logpath" ]; then + cat "$logpath" + fi unit: template template-resource: snap_revision_info @@ -71,30 +73,32 @@ id: snapd/snap-revert-{type}-{name}-from-stable-rev template-id: snapd/snap-revert-type-name-from-stable-rev _summary: Revert {name} snap to original revision from stable channel plugin: shell +flags: noreturn estimated_duration: 3m category_id: snapd user: root depends: snapd/snap-verify-after-refresh-{type}-{name}-to-stable-rev command: path="$PLAINBOX_SESSION_SHARE/{name}_snap_revision_info" - snap_update_test.py --revert --info-path "$path" {name} + logpath="$PLAINBOX_SESSION_SHARE/snap-revert-{type}-{name}-from-stable-rev.log" + snap_update_test.py --revert --info-path "$path" {name} | tee "$logpath" + reboot unit: template template-resource: snap_revision_info template-unit: job -id: snapd/reboot-after-snap-revert-{type}-{name}-from-stable-rev -template-id: snapd/reboot-after-snap-revert-type-name-from-stable-rev -_summary: Reboot after {name} snap reverting to latest revision in stable channel -plugin: shell -flags: noreturn autorestart -estimated_duration: 3m +id: snapd/log-attach-after-snap-revert-{type}-{name}-from-stable-rev +template-id: snapd/log-attach-after-snap-revert-type-name-from-stable-rev +_summary: Attach logs after reverting {name} snap to latest revision in stable channel +plugin: attachment +estimated_duration: 1s category_id: snapd -user: root -depends: snapd/snap-revert-{type}-{name}-from-stable-rev +after: snapd/snap-revert-{type}-{name}-from-stable-rev command: - echo "Waiting 90s for any snap operation to finish before rebooting..." - sleep 90 - reboot + logpath="$PLAINBOX_SESSION_SHARE/snap-revert-{type}-{name}-from-stable-rev.log" + if [ -f "$logpath" ]; then + cat "$logpath" + fi unit: template template-resource: snap_revision_info @@ -106,7 +110,7 @@ plugin: shell estimated_duration: 3s category_id: snapd user: root -depends: snapd/reboot-after-snap-revert-{type}-{name}-from-stable-rev +depends: snapd/snap-revert-{type}-{name}-from-stable-rev command: path="$PLAINBOX_SESSION_SHARE/{name}_snap_revision_info" snap_update_test.py --verify-revert --info-path "$path" {name} @@ -129,6 +133,7 @@ plugin: shell estimated_duration: 3m category_id: snapd user: root +flags: noreturn imports: from com.canonical.certification import snap_revision_info from com.canonical.plainbox import manifest @@ -137,24 +142,25 @@ requires: manifest.need_{type}_snap_update_test == "True" command: path="$PLAINBOX_SESSION_SHARE/{name}_snap_revision_info" - snap_update_test.py --refresh --revision {base_rev} --info-path "$path" {name} + logpath="$PLAINBOX_SESSION_SHARE/snap-refresh-{type}-{name}-to-base-rev.log" + snap_update_test.py --refresh --revision {base_rev} --info-path "$path" {name} | tee "$logpath" + reboot unit: template template-resource: snap_revision_info template-unit: job -id: snapd/reboot-after-snap-refresh-{type}-{name}-to-base-rev -template-id: snapd/reboot-after-snap-refresh-type-name-to-base-rev -_summary: Reboot after {name} snap refresh to base revision -plugin: shell -flags: noreturn autorestart -estimated_duration: 3m +id: snapd/log-attach-after-snap-refresh-{type}-{name}-to-base-rev +template-id: snapd/log-attach-after-snap-refresh-type-name-to-base-rev +_summary: Attach logs after refreshing {name} snap to base revision +plugin: attachment +estimated_duration: 1s category_id: snapd -user: root -depends: snapd/snap-refresh-{type}-{name}-to-base-rev +after: snapd/snap-refresh-{type}-{name}-to-base-rev command: - echo "Waiting 90s for any snap operation to finish before rebooting..." - sleep 90 - reboot + logpath="$PLAINBOX_SESSION_SHARE/snap-refresh-{type}-{name}-to-base-rev.log" + if [ -f "$logpath" ]; then + cat "$logpath" + fi unit: template template-resource: snap_revision_info @@ -166,7 +172,7 @@ plugin: shell estimated_duration: 30s category_id: snapd user: root -depends: snapd/reboot-after-snap-refresh-{type}-{name}-to-base-rev +depends: snapd/snap-refresh-{type}-{name}-to-base-rev command: path="$PLAINBOX_SESSION_SHARE/{name}_snap_revision_info" snap_update_test.py --verify-refresh --info-path "$path" {name} @@ -178,30 +184,33 @@ id: snapd/snap-revert-{type}-{name}-from-base-rev template-id: snapd/snap-revert-type-name-from-base-rev _summary: Revert {name} snap from base revision to original revision plugin: shell +flags: noreturn estimated_duration: 3m category_id: snapd user: root depends: snapd/snap-verify-after-refresh-{type}-{name}-to-base-rev command: path="$PLAINBOX_SESSION_SHARE/{name}_snap_revision_info" - snap_update_test.py --revert --info-path "$path" {name} + logpath="$PLAINBOX_SESSION_SHARE/snap-revert-{type}-{name}-from-base-rev.log" + snap_update_test.py --revert --info-path "$path" {name} | tee "$logpath" + reboot unit: template template-resource: snap_revision_info template-unit: job -id: snapd/reboot-after-snap-revert-{type}-{name}-from-base-rev -template-id: snapd/reboot-after-snap-revert-type-name-from-base-rev -_summary: Reboot after {name} snap revert to base revision -plugin: shell -flags: noreturn autorestart -estimated_duration: 3m +id: snapd/log-attach-after-snap-revert-{type}-{name}-from-base-rev +template-id: snapd/log-attach-after-snap-revert-type-name-from-base-rev +_summary: Attach logs after reverting {name} snap to base revision +plugin: attachment +estimated_duration: 1s category_id: snapd user: root -depends: snapd/snap-revert-{type}-{name}-from-base-rev +after: snapd/snap-revert-{type}-{name}-from-base-rev command: - echo "Waiting 90s for any snap operation to finish before rebooting..." - sleep 90 - reboot + logpath="$PLAINBOX_SESSION_SHARE/snap-revert-{type}-{name}-from-base-rev.log" + if [ -f "$logpath" ]; then + cat "$logpath" + fi unit: template template-resource: snap_revision_info @@ -213,7 +222,7 @@ plugin: shell estimated_duration: 3s category_id: snapd user: root -depends: snapd/reboot-after-snap-revert-{type}-{name}-from-base-rev +depends: snapd/snap-revert-{type}-{name}-from-base-rev command: path="$PLAINBOX_SESSION_SHARE/{name}_snap_revision_info" snap_update_test.py --verify-revert --info-path "$path" {name} diff --git a/providers/base/units/snapd/test-plan.pxu b/providers/base/units/snapd/test-plan.pxu index 318b6a1b8..255ff8579 100644 --- a/providers/base/units/snapd/test-plan.pxu +++ b/providers/base/units/snapd/test-plan.pxu @@ -110,44 +110,44 @@ include: # Gadget-related tests ###################### snapd/snap-refresh-gadget-.*-base-rev - snapd/reboot-after-snap-refresh-gadget-.*-base-rev + snapd/log-attach-after-snap-refresh-gadget-.*-base-rev snapd/snap-verify-after-refresh-gadget-.*-base-rev snapd/snap-revert-gadget-.*-base-rev - snapd/reboot-after-snap-revert-gadget-.*-base-rev + snapd/log-attach-after-snap-revert-gadget-.*-base-rev snapd/snap-verify-after-revert-gadget-.*-base-rev snapd/snap-refresh-gadget-.*-stable-rev - snapd/reboot-after-snap-refresh-gadget-.*-stable-rev + snapd/log-attach-after-snap-refresh-gadget-.*-stable-rev snapd/snap-verify-after-refresh-gadget-.*-stable-rev snapd/snap-revert-gadget-.*-stable-rev - snapd/reboot-after-snap-revert-gadget-.*-stable-rev + snapd/log-attach-after-snap-revert-gadget-.*-stable-rev snapd/snap-verify-after-revert-gadget-.*-stable-rev ##################### # Snapd-related tests ##################### snapd/snap-refresh-snapd-.*-base-rev - snapd/reboot-after-snap-refresh-snapd-.*-base-rev + snapd/log-attach-after-snap-refresh-snapd-.*-base-rev snapd/snap-verify-after-refresh-snapd-.*-base-rev snapd/snap-revert-snapd-.*-base-rev - snapd/reboot-after-snap-revert-snapd-.*-base-rev + snapd/log-attach-after-snap-revert-snapd-.*-base-rev snapd/snap-verify-after-revert-snapd-.*-base-rev snapd/snap-refresh-snapd-.*-stable-rev - snapd/reboot-after-snap-refresh-snapd-.*-stable-rev + snapd/log-attach-after-snap-refresh-snapd-.*-stable-rev snapd/snap-verify-after-refresh-snapd-.*-stable-rev snapd/snap-revert-snapd-.*-stable-rev - snapd/reboot-after-snap-revert-snapd-.*-stable-rev + snapd/log-attach-after-snap-revert-snapd-.*-stable-rev snapd/snap-verify-after-revert-snapd-.*-stable-rev ###################### # Kernel-related tests ###################### snapd/snap-refresh-kernel-.*-base-rev - snapd/reboot-after-snap-refresh-kernel-.*-base-rev + snapd/log-attach-after-snap-refresh-kernel-.*-base-rev snapd/snap-verify-after-refresh-kernel-.*-base-rev snapd/snap-revert-kernel-.*-base-rev - snapd/reboot-after-snap-revert-kernel-.*-base-rev + snapd/log-attach-after-snap-revert-kernel-.*-base-rev snapd/snap-verify-after-revert-kernel-.*-base-rev snapd/snap-refresh-kernel-.*-stable-rev - snapd/reboot-after-snap-refresh-kernel-.*-stable-rev + snapd/log-attach-after-snap-refresh-kernel-.*-stable-rev snapd/snap-verify-after-refresh-kernel-.*-stable-rev snapd/snap-revert-kernel-.*-stable-rev - snapd/reboot-after-snap-revert-kernel-.*-stable-rev + snapd/log-attach-after-snap-revert-kernel-.*-stable-rev snapd/snap-verify-after-revert-kernel-.*-stable-rev