Skip to content
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

Restore all jobs in the job state map (bugfix) #1605

Merged
merged 8 commits into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion checkbox-ng/plainbox/impl/session/assistant.py
Original file line number Diff line number Diff line change
Expand Up @@ -828,12 +828,19 @@ def bootstrap(self):
] = "to run bootstrapping job"
rb = self.run_job(job.id, "silent", False)
self.use_job_result(job.id, rb.get_result())
# we may have a list of rejected jobs if this session is a resumed
# session
already_rejected = [
JobIdQualifier(job_id, None, inclusive=False)
for job_id in self._context.state.metadata.rejected_jobs
]
# Perform initial selection -- we want to run everything that is
# described by the test plan that was selected earlier.
desired_job_list = select_units(
self._context.state.job_list,
[plan.get_qualifier() for plan in self._manager.test_plans]
+ self._exclude_qualifiers,
+ self._exclude_qualifiers
+ already_rejected,
)
self._context.state.update_desired_job_list(desired_job_list)
# Set subsequent usage expectations i.e. all of the runtime parts are
Expand Down
4 changes: 4 additions & 0 deletions checkbox-ng/plainbox/impl/session/resume.py
Original file line number Diff line number Diff line change
Expand Up @@ -779,6 +779,10 @@ def _restore_SessionState_jobs_and_results(self, session, session_repr):
self._process_job(session, jobs_repr, results_repr, job_id)
except KeyError:
leftover_jobs.append(job_id)

leftover_jobs += session_repr.get("metadata", {}).get(
"rejected_jobs", []
)
# Process leftovers. For each iteration the leftover_jobs list should
# shrink or we're not making any progress. If that happens we've got
# undefined jobs (in general the session is corrupted)
Expand Down
6 changes: 5 additions & 1 deletion checkbox-ng/plainbox/impl/session/suspend.py
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,7 @@ def _repr_SessionState(self, obj, session_dir):
state object.
"""
id_run_list = frozenset([job.id for job in obj.run_list])

return {
"jobs": {
state.job.id: state.job.checksum
Expand Down Expand Up @@ -631,7 +632,10 @@ def _repr_SessionState(self, obj, session_dir):
The representation of meta-data associated with the session
state object.
"""
id_run_list = frozenset([job.id for job in obj.run_list])
id_run_list = frozenset(
[job.id for job in obj.run_list] + obj.metadata.rejected_jobs
)

return {
"jobs": {
state.job.id: state.job.checksum
Expand Down
35 changes: 35 additions & 0 deletions checkbox-ng/plainbox/impl/session/test_resume.py
Original file line number Diff line number Diff line change
Expand Up @@ -2031,6 +2031,41 @@ def test_simple_session(self):
# as specific tests for restoring results are written elsewhere
self.assertEqual(session.job_state_map[job.id].result.outcome, "pass")

def test_simple_session_with_rejected(self):
"""
verify that _restore_SessionState_jobs_and_results() works when
faced with a representation of a simple session (no generated jobs
or anything "exotic").
Hook25 marked this conversation as resolved.
Show resolved Hide resolved
"""
job = make_job(id="job")
job1 = make_job(id="job1")
session_repr = {
"jobs": {job.id: job.checksum, job1.id: job1.checksum},
"results": {
job.id: [
{
"outcome": "pass",
"comments": None,
"execution_duration": None,
"return_code": None,
"io_log": [],
}
]
},
"metadata": {"rejected_jobs": [job1.id]},
}
helper = self.parameters.resume_cls([], None, None)
session = SessionState([job, job1])
helper._restore_SessionState_jobs_and_results(session, session_repr)
# Session still has one job in it
self.assertEqual(session.job_list, [job, job1])
# Resources don't have anything (no resource jobs)
self.assertEqual(session.resource_map, {})
# The result was restored correctly. This is just a smoke test
# as specific tests for restoring results are written elsewhere
self.assertEqual(session.job_state_map[job.id].result.outcome, "pass")
self.assertIn(job1.id, session.job_state_map)
Hook25 marked this conversation as resolved.
Show resolved Hide resolved

def test_unknown_jobs_get_reported(self):
"""
verify that _restore_SessionState_jobs_and_results() reports
Expand Down
6 changes: 5 additions & 1 deletion metabox/metabox/core/lxd_execute.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import threading
import time

from contextlib import suppress

from loguru import logger
import metabox.core.keys as keys
from metabox.core.utils import ExecuteResult
Expand Down Expand Up @@ -113,7 +115,9 @@ def expect(self, pattern, timeout=0):
return found

def expect_not(self, data, timeout=0):
return not self.expect(data, timeout)
with suppress(TimeoutError):
return not self.expect(data, timeout)
return True

def select_test_plan(self, data, timeout=0):
if not self._lookup_by_id:
Expand Down
29 changes: 29 additions & 0 deletions metabox/metabox/scenarios/restart/agent_respawn.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
Expect,
Start,
Signal,
ExpectNot,
)
from metabox.core.scenario import Scenario
from metabox.core.utils import tag
Expand Down Expand Up @@ -161,3 +162,31 @@ class ResumeAfterFinishPreserveOutputRemote(Scenario):
Expect("job passed"),
Expect("job failed"),
]


@tag("resume", "manual", "regression")
class ResumePreservesRejectedJobsStateMap(Scenario):
launcher = "# no launcher"
steps = [
Start(),
Expect("Select test plan"),
SelectTestPlan("2021.com.canonical.certification::pass-and-crash"),
Send(keys.KEY_ENTER),
Expect("Press (T) to start"),
Send(keys.KEY_ENTER),
Expect("basic-shell-crashing"),
Send(keys.KEY_DOWN + keys.KEY_SPACE),
Expect("[ ]"),
Send("T"),
Expect("Do you want to submit 'upload to certification' report?"),
Signal(keys.SIGINT),
Start(),
Expect("Reports will be saved to"),
# Part of the regression, fixing the job state map would make the
# re-bootstrapping of the session include the excluded job
ExpectNot("basic-shell-crashing", timeout=0.1),
# Here the session will try to re-generate the submission.json but it
# will fail if the info about the session is not complete in the job
# state map (as it was prior to this regression)
Expect("Do you want to submit 'upload to certification' report?"),
]
Loading