Skip to content
This repository has been archived by the owner on Oct 13, 2023. It is now read-only.

Commit

Permalink
[ART-5863] manage early kernel tracker completion
Browse files Browse the repository at this point in the history
* add logic both when searching for bugs to clone and when closing them
  out to also close, comment, and link advisories for related KMAINT trackers
* expand --comment flag to --update-tracker flag
* extract/expand more common logic to early_kernel.py
  • Loading branch information
sosiouxme committed Aug 10, 2023
1 parent 2ddef73 commit a8c0e93
Show file tree
Hide file tree
Showing 6 changed files with 331 additions and 176 deletions.
61 changes: 27 additions & 34 deletions elliottlib/cli/find_bugs_kernel_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ def _search_issues(jira_client, *args, **kwargs):

class FindBugsKernelCli:
def __init__(self, runtime: Runtime, trackers: Sequence[str],
clone: bool, reconcile: bool, comment: bool, dry_run: bool):
clone: bool, reconcile: bool, update_tracker: bool, dry_run: bool):
self._runtime = runtime
self._logger = runtime.logger
self.trackers = list(trackers)
self.clone = clone
self.reconcile = reconcile
self.comment = comment
self.update_tracker = update_tracker
self.dry_run = dry_run
self._id_bugs: Dict[int, Bug] = {} # cache for kernel bug; key is bug_id, value is Bug object
self._tracker_map: Dict[int, Issue] = {} # bug_id -> KMAINT jira mapping
Expand All @@ -57,17 +57,17 @@ async def run(self):
# Getting KMAINT trackers
trackers_keys = self.trackers
trackers: List[Issue] = []
if not trackers_keys:
logger.info("Searching for open trackers...")
trackers = self._find_kmaint_trackers(jira_client, config.tracker_jira.project, config.tracker_jira.labels)
trackers_keys = [t.key for t in trackers]
logger.info("Found %s tracker(s): %s", len(trackers_keys), trackers_keys)
else:
if trackers_keys:
logger.info("Find kernel bugs linked from KMAINT tracker(s): %s", trackers_keys)
for key in trackers_keys:
logger.info("Getting tracker JIRA %s...", key)
tracker = jira_client.issue(key)
trackers.append(tracker)
else:
logger.info("Searching for open trackers...")
trackers = self._find_kmaint_trackers(jira_client, config.tracker_jira.project, config.tracker_jira.labels)
trackers_keys = [t.key for t in trackers]
logger.info("Found %s tracker(s): %s", len(trackers_keys), trackers_keys)

# Get kernel bugs linked from KMAINT trackers
report: Dict[str, Any] = {"kernel_bugs": []}
Expand All @@ -86,9 +86,8 @@ async def run(self):
"summary": bug.summary,
"tracker": tracker,
})
if self.comment:
logger.info("Checking if making a comment on tracker %s is needed...", tracker.key)
self._comment_on_tracker(jira_client, tracker, koji_api, config.target_jira)
if self.update_tracker:
self._update_tracker(jira_client, tracker, koji_api, config.target_jira)

if self.clone and self._id_bugs:
# Clone kernel bugs into OCP Jira
Expand Down Expand Up @@ -175,7 +174,7 @@ def _clone_bugs(self, jira_client: JIRA, bugs: Sequence[Bug], conf: KernelBugSwe
jira_client.create_issue_link("Blocks", issue.key, kmaint_tracker)
result[bug_id] = [issue]
else:
logger.warning("[DRY RUN] Would have created Jira for bug %s", bug_id)
logger.info("[DRY RUN] Would have created Jira for bug %s", bug_id)
else: # this bug is already cloned into OCP Jira
logger.info("Bug %s is already cloned into OCP: %s", bug_id, [issue.key for issue in found_issues])
result[bug_id] = found_issues
Expand All @@ -190,7 +189,7 @@ def _clone_bugs(self, jira_client: JIRA, bugs: Sequence[Bug], conf: KernelBugSwe
if not self.dry_run:
issue.update(fields)
else:
logger.warning("[DRY RUN] Would have updated Jira %s to match bug %s", issue.key, bug_id)
logger.info("[DRY RUN] Would have updated Jira %s to match bug %s", issue.key, bug_id)

return result

Expand All @@ -204,30 +203,24 @@ def _print_report(report: Dict, out: TextIO):
text = f"{bug['tracker']}\t{bug['id']}\t{'N/A' if not cloned_issues else ','.join(cloned_issues)}\t{bug['status']}\t{bug['summary']}"
print_func(text, file=out)

def _comment_on_tracker(self, jira_client: JIRA, tracker: Issue, koji_api: koji.ClientSession,
conf: KernelBugSweepConfig.TargetJiraConfig):
def _update_tracker(self, jira_client: JIRA, tracker: Issue, koji_api: koji.ClientSession,
conf: KernelBugSweepConfig.TargetJiraConfig):
logger = self._runtime.logger
logger.info("Checking if an update to tracker %s is needed...", tracker.key)
# Determine which NVRs have the fix. e.g. ["kernel-5.14.0-284.14.1.el9_2"]
nvrs, candidate, shipped = early_kernel.get_tracker_builds_and_tags(logger, tracker, koji_api, conf)

tracker_message = None
if shipped:
tracker_message = f"Build(s) {nvrs} was/were already shipped and tagged into {shipped}."
early_kernel.process_shipped_tracker(logger, self.dry_run, jira_client, tracker, nvrs, shipped)
elif candidate:
tracker_message = f"Build(s) {nvrs} was/were already tagged into {candidate}."
if not tracker_message:
logger.info("No need to make a comment on %s", tracker.key)
return
comments = jira_client.comments(tracker.key)
if any(map(lambda comment: comment.body == tracker_message, comments)):
logger.info("A comment was already made on %s", tracker.key)
return
logger.info("Making a comment on tracker %s", tracker.key)
if not self.dry_run:
jira_client.add_comment(tracker.key, tracker_message)
logger.info("Left a comment on tracker %s", tracker.key)
early_kernel.comment_on_tracker(
logger, self.dry_run, jira_client, tracker,
[f"Build(s) {nvrs} was/were already tagged into {candidate}."]
# do not reword, see NOTE in method
)
else:
logger.warning("[DRY RUN] Would have left a comment on tracker %s", tracker.key)
logger.info("No need to update tracker %s", tracker.key)
return

@staticmethod
def _new_jira_fields_from_bug(bug: Bug, ocp_target_version: str, kmaint_tracker: Optional[str], conf: KernelBugSweepConfig.TargetJiraConfig):
Expand Down Expand Up @@ -294,10 +287,10 @@ def _new_jira_fields_from_bug(bug: Bug, ocp_target_version: str, kmaint_tracker:
is_flag=True,
default=False,
help="Update summary, description, etc for already cloned Jira bugs. Must be used with --clone")
@click.option("--comment",
@click.option("--update-tracker",
is_flag=True,
default=False,
help="Make comments on KMAINT trackers")
help="Update KMAINT trackers state, links, and comments")
@click.option("--dry-run",
is_flag=True,
default=False,
Expand All @@ -306,7 +299,7 @@ def _new_jira_fields_from_bug(bug: Bug, ocp_target_version: str, kmaint_tracker:
@click_coroutine
async def find_bugs_kernel_cli(
runtime: Runtime, trackers: Tuple[str, ...], clone: bool,
reconcile: bool, comment: bool, dry_run: bool):
reconcile: bool, update_tracker: bool, dry_run: bool):
"""Find kernel bugs in Bugzilla for weekly kernel release through OCP.
Example 1: Find kernel bugs and print them out
Expand All @@ -327,7 +320,7 @@ async def find_bugs_kernel_cli(
trackers=trackers,
clone=clone,
reconcile=reconcile,
comment=comment,
update_tracker=update_tracker,
dry_run=dry_run
)
await cli.run()
89 changes: 24 additions & 65 deletions elliottlib/cli/find_bugs_kernel_clones_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,19 @@

class FindBugsKernelClonesCli:
def __init__(self, runtime: Runtime, trackers: Sequence[str], bugs: Sequence[str],
move: bool, comment: bool, dry_run: bool):
move: bool, update_tracker: bool, dry_run: bool):
self._runtime = runtime
self._logger = runtime.logger
self.trackers = list(trackers)
self.bugs = list(bugs)
self.move = move
self.comment = comment
self.update_tracker = update_tracker
self.dry_run = dry_run

def run(self):
logger = self._logger
if self.comment and not self.move:
raise ElliottFatalError("--comment must be used with --move")
if self.update_tracker and not self.move:
raise ElliottFatalError("--update-tracker must be used with --move")
if self._runtime.assembly_type is not AssemblyTypes.STREAM:
raise ElliottFatalError("This command only supports stream assembly.")
group_config = self._runtime.group_config
Expand Down Expand Up @@ -140,46 +140,29 @@ def _find_trackers_for_bugs(self,
tracker_bugs.setdefault(tracker.key, []).append(bug)
return trackers, tracker_bugs

def _process_shipped_bugs(self, logger, bug_keys, bugs, jira_client: JIRA, nvrs, prod_brew_tag) -> str:
def _process_shipped_bugs(self, logger, bug_keys, bugs, jira_client: JIRA, nvrs, prod_brew_tag):
# when NVRs are shipped, ensure the associated bugs are closed with a comment
logger.info("Build(s) %s shipped (tagged into %s). Moving bug Jira(s) %s to CLOSED...", nvrs, prod_brew_tag, bug_keys)
for bug in bugs:
current_status: str = bug.fields.status.name
if current_status.lower() != "closed":
new_status = 'CLOSED'
message = f"Elliott changed bug status from {current_status} to {new_status} because {nvrs} was/were already shipped and tagged into {prod_brew_tag}."
self._move_jira(jira_client, bug, new_status, message)
early_kernel.move_jira(logger, self.dry_run, jira_client, bug, new_status, message)
else:
logger.info("No need to move %s because its status is %s", bug.key, current_status)
return f"Build(s) {nvrs} was/were already shipped and tagged into {prod_brew_tag}." # see wording NOTE

def _process_shipped_tracker(self, logger, tracker, jira_client: JIRA, nvrs) -> List[str]:
# when NVRs are shipped, ensure the associated tracker is closed with a comment
# and a link to any advisory that shipped them
logger.info("Build(s) %s shipped (tagged into %s). Looking for advisories...", nvrs)
tracker_messages = []

logger.info("Moving tracker Jira %s to CLOSED...")
current_status: str = tracker.fields.status.name
if current_status.lower() != "closed":
self._move_jira(jira_client, tracker, "CLOSED")
else:
logger.info("No need to move %s because its status is %s", tracker.key, current_status)

return tracker_messages

def _process_candidate_bugs(self, logger, bug_keys, bugs, jira_client: JIRA, nvrs, candidate_brew_tag) -> str:
def _process_candidate_bugs(self, logger, bug_keys, bugs, jira_client: JIRA, nvrs, candidate_brew_tag):
# when NVRs are tagged, ensure the associated bugs are modified with a comment
logger.info("Build(s) %s tagged into %s. Moving Jira(s) %s to MODIFIED...", nvrs, candidate_brew_tag, bug_keys)
for bug in bugs:
current_status: str = bug.fields.status.name
if current_status.lower() in {"new", "assigned", "post"}:
new_status = 'MODIFIED'
message = f"Elliott changed bug status from {current_status} to {new_status} because {nvrs} was/were already tagged into {candidate_brew_tag}."
self._move_jira(jira_client, bug, new_status, message)
early_kernel.move_jira(logger, self.dry_run, jira_client, bug, new_status, message)
else:
logger.info("No need to move %s because its status is %s", bug.key, current_status)
return f"Build(s) {nvrs} was/were already tagged into {candidate_brew_tag}." # see wording NOTE

def _update_jira_bugs(self, jira_client: JIRA, found_bugs: List[Issue], koji_api: koji.ClientSession, config: KernelBugSweepConfig):
logger = self._runtime.logger
Expand All @@ -189,42 +172,18 @@ def _update_jira_bugs(self, jira_client: JIRA, found_bugs: List[Issue], koji_api
nvrs, candidate, shipped = early_kernel.get_tracker_builds_and_tags(logger, tracker, koji_api, config.target_jira)
bugs = tracker_bugs[tracker_id]
bug_keys = [bug.key for bug in bugs]
tracker_message = []
if shipped:
tracker_message.append(self._process_shipped_bugs(logger, bug_keys, bugs, jira_client, nvrs, shipped))
tracker_message.extend(self._process_shipped_tracker(logger, tracker, jira_client, nvrs))
self._process_shipped_bugs(logger, bug_keys, bugs, jira_client, nvrs, shipped)
if self.update_tracker:
early_kernel.process_shipped_tracker(logger, self.dry_run, jira_client, tracker, nvrs, shipped)
elif candidate:
tracker_message.append(self._process_candidate_bugs(logger, bug_keys, bugs, jira_client, nvrs, candidate))

if self.comment and tracker_message:
logger.info("Checking if making a comment on tracker %s is needed", tracker.key)
# wording NOTE: this logic will re-comment on past bugs if the wording is not
# exactly as previously commented. think long and hard before changing the wording
# of any of these comments.
comments = jira_client.comments(tracker.key)
for message in tracker_message:
if any(map(lambda comment: comment.body == message, comments)):
logger.info("A comment was already made on %s", tracker.key)
continue
logger.info("Making a comment on tracker %s", tracker.key)
if self.dry_run:
logger.warning("[DRY RUN] Would have left a comment on tracker %s", tracker.key)
else:
jira_client.add_comment(tracker.key, message)
logger.info("Left a comment on tracker %s", tracker.key)

def _move_jira(self, jira_client: JIRA, issue: Issue, new_status: str, comment: Optional[str]):
logger = self._runtime.logger
current_status: str = issue.fields.status.name
logger.info("Moving %s from %s to %s", issue.key, current_status, new_status)
if self.dry_run:
logger.warning("[DRY RUN] Would have moved Jira %s from %s to %s", issue.key, current_status, new_status)
else:
jira_client.assign_issue(issue.key, jira_client.current_user())
jira_client.transition_issue(issue.key, new_status)
if comment:
jira_client.add_comment(issue.key, comment)
logger.info("Moved %s from %s to %s", issue.key, current_status, new_status)
self._process_candidate_bugs(logger, bug_keys, bugs, jira_client, nvrs, candidate)
if self.update_tracker:
early_kernel.comment_on_tracker(
logger, self.dry_run, jira_client, tracker,
[f"Build(s) {nvrs} was/were already tagged into {candidate}."]
# do not reword, see NOTE in method
)

@staticmethod
def _print_report(report: Dict, out: TextIO):
Expand All @@ -244,18 +203,18 @@ def _print_report(report: Dict, out: TextIO):
is_flag=True,
default=False,
help="Auto move Jira bugs to MODIFIED or CLOSED")
@click.option("--comment",
@click.option("--update-tracker",
is_flag=True,
default=False,
help="Make comments on KMAINT trackers")
help="Update KMAINT trackers state, links, and comments")
@click.option("--dry-run",
is_flag=True,
default=False,
help="Don't change anything")
@click.pass_obj
def find_bugs_kernel_clones_cli(
runtime: Runtime, trackers: Tuple[str, ...], issues: Tuple[str, ...],
move: bool, comment: bool, dry_run: bool):
move: bool, update_tracker: bool, dry_run: bool):
"""Find cloned kernel bugs in JIRA for weekly kernel release through OCP.
Example 1: List all bugs in JIRA
Expand All @@ -266,17 +225,17 @@ def find_bugs_kernel_clones_cli(
\b
$ elliott -g openshift-4.14 find-bugs:kernel-clones --move
Example 3: Move bugs and leave a comment on the KMAINT tracker
Example 3: Move bugs and update the KMAINT tracker
\b
$ elliott -g openshift-4.14 find-bugs:kernel-clones --move --comment
$ elliott -g openshift-4.14 find-bugs:kernel-clones --move --update-tracker
"""
runtime.initialize(mode="none")
cli = FindBugsKernelClonesCli(
runtime=runtime,
trackers=trackers,
bugs=issues,
move=move,
comment=comment,
update_tracker=update_tracker,
dry_run=dry_run
)
cli.run()
Loading

0 comments on commit a8c0e93

Please sign in to comment.