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

Commit

Permalink
Open JIRA issues when reconciliation PR is open
Browse files Browse the repository at this point in the history
  • Loading branch information
jupierce committed Jan 6, 2023
1 parent f83cc41 commit a5c5058
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 4 deletions.
85 changes: 83 additions & 2 deletions doozerlib/cli/images_streams.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,18 @@
import time
import datetime
import random
from typing import Dict

from github import Github, UnknownObjectException, GithubException

from jira import JIRA, Issue

from dockerfile_parse import DockerfileParser
from doozerlib.model import Missing
from doozerlib.pushd import Dir
from doozerlib.cli import cli, pass_runtime
from doozerlib import brew, state, exectools, model, constants
from doozerlib.image import ImageMetadata
from doozerlib.util import get_docker_config_json, convert_remote_git_to_ssh, \
split_git_url, remove_prefix, green_print,\
yellow_print, red_print, convert_remote_git_to_https, \
Expand Down Expand Up @@ -603,6 +607,82 @@ def images_upstreampulls(runtime):
print(yaml.dump(retdata, default_flow_style=False, width=10000))


def reconcile_jira_issues(runtime, pr_links: Dict[str, str], dry_run: bool):
"""
Ensures there is a Jira issue open for reconciliation PRs.
Args:
runtime: The doozer runtime
pr_links: a map of distgit_keys->pr_url to open reconciliation PRs
dry_run: If true, new desired jira issues would only be printed to the console.
"""
major, minor = runtime.get_major_minor_fields()
if (major == 4 and minor < 12) or major < 4:
# Only enabled for 4.12 and beyond at the moment.
return

new_issues: Dict[str, Issue] = dict()
existing_issues: Dict[str, Issue] = dict()

release_version = f'{major}.{minor}'
jira_client: JIRA = runtime.build_jira_client()
for distgit_key, pr_url in pr_links.items():
image_meta: ImageMetadata = runtime.image_map[distgit_key]
project, component = image_meta.get_jira_info()
summary = f"Update {release_version} {image_meta.name} image to be consistent with ART"

query = f'project={project} AND summary ~ "{summary}" AND statusCategory in ("To Do", "In Progress")'
open_issues = jira_client.search_issues(query)

if open_issues:
print(f'A JIRA issue is already open for {pr_url}: {open_issues[0]}')
existing_issues[distgit_key] = open_issues[0]
continue

description = f'''
Please review the following PR: {pr_url}
The PR has been automatically opened by ART (#aos-art) team automation and indicates
that the image(s) being used downstream for production builds are not consistent
with the images referenced in this component's github repository.
Differences in upstream and downstream builds impact the fidelity of your CI signal.
If you disagree with the content of this PR, please contact @release-artists
in #aos-art to discuss the discrepancy.
Closing this issue without addressing the difference will cause the issue to
be reopened automatically.
'''
fields = {
'project': {'key': project},
'issuetype': {'name': 'Bug'},
'versions': [{'name': release_version}], # Affects Version/s
'components': [{'name': component}],
'summary': summary,
'description': description
}

if not dry_run:
issue = jira_client.create_issue(
fields
)
new_issues[distgit_key] = issue
print(f'A JIRA issue has been opened for {pr_url}: {issue}')
else:
new_issues[distgit_key] = 'NEW!'
print(f'Would have created JIRA issue for {distgit_key} / {pr_url}:\n{fields}\n')

if new_issues:
print('Newly opened JIRA issues:')
for key, issue in new_issues.items():
print(f'{key}: {issue}')

if existing_issues:
print('Previously existing JIRA issues:')
for key, issue in existing_issues.items():
print(f'{key}: {issue}')


@prs.command('open', short_help='Open PRs against upstream component repos that have a FROM that differs from ART metadata.')
@click.option('--github-access-token', metavar='TOKEN', required=True, help='Github access token for user.')
@click.option('--bug', metavar='BZ#', required=False, default=None, help='Title with Bug #: prefix')
Expand All @@ -626,7 +706,7 @@ def images_streams_prs(runtime, github_access_token, bug, interstitial, ignore_c

master_major, master_minor = extract_version_fields(what_is_in_master(), at_least=2)
if not ignore_ci_master and (major > master_major or minor > master_minor):
# ART building a release before is is in master. Too early to open PRs.
# ART building a release before it is in master. Too early to open PRs.
runtime.logger.warning(f'Target {major}.{minor} has not been in master yet (it is tracking {master_major}.{master_minor}); skipping PRs')
exit(0)

Expand Down Expand Up @@ -972,7 +1052,7 @@ def check_if_upstream_image_exists(upstream_image):
__TLDR__:
Component owners, please ensure that this PR merges as it impacts the fidelity
of your CI signal. Patch-manager / leads, this PR is a no-op from a product
perspective -- feel free to manually apply any labels (e.g. bugzilla/valid-bug) to help the
perspective -- feel free to manually apply any labels (e.g. jira/valid-bug) to help the
PR merge as long as tests are passing. If the PR is labeled "needs-ok-to-test", this is
to limit costs for re-testing these PRs while they wait for review. Issue /ok-to-test
to remove this tag and help the PR to merge.
Expand Down Expand Up @@ -1134,6 +1214,7 @@ def check_if_upstream_image_exists(upstream_image):
if pr_links:
print('Currently open PRs:')
print(yaml.safe_dump(pr_links))
reconcile_jira_issues(runtime, pr_links, moist_run)

if skipping_dgks:
print('Some PRs were skipped; Exiting with return code 25 to indicate this')
Expand Down
5 changes: 5 additions & 0 deletions doozerlib/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,11 @@ def get_jira_info(self) -> Tuple[str, str]:
prodsec_mapping = self.runtime.get_openshift4_component_mapping()
prodsec_jira_component = prodsec_mapping.get(self.get_component_name(), 'Unknown')

if self.distgit_key == 'openshift-enterprise-base':
# This is a special case image that is represented by upstream but
# no one release owns. ART should handle merges here.
prodsec_jira_component = 'Release'

return maintainer.get('project', 'OCPBUGS'), maintainer.get('component', prodsec_jira_component)

def extract_kube_env_vars(self) -> Dict[str, str]:
Expand Down
32 changes: 30 additions & 2 deletions doozerlib/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
import time
import re

from jira import JIRA, Issue

from doozerlib import gitdata
from . import logutil
from . import assertion
Expand Down Expand Up @@ -729,6 +731,22 @@ def initialize_logging(self):
debug_log_handler.setLevel(logging.DEBUG)
self.logger.addHandler(debug_log_handler)

def build_jira_client(self) -> JIRA:
"""
:return: Returns a JIRA client setup for the server in bug.yaml
"""
major, minor = self.get_major_minor_fields()
if major == 4 and minor < 6:
raise ValueError("ocp-build-data/bug.yml is not expected to be available for 4.X versions < 4.6")
bug_config = Model(self.gitdata.load_data(key='bug').data)
server = bug_config.jira_config.server or 'issues.redhat.com'

token_auth = os.environ.get("JIRA_TOKEN")
if not token_auth:
raise ValueError(f"Jira activity requires login credentials for {server}. Set a JIRA_TOKEN env var")
client = JIRA(server, token_auth=token_auth)
return client

def build_retrying_koji_client(self):
"""
:return: Returns a new koji client instance that will automatically retry
Expand Down Expand Up @@ -1589,10 +1607,20 @@ def get_candidate_brew_tags(self):
# adjust as needed (and just imagine rhel 9)!
return {tag, tag.replace('-rhel-7', '-rhel-8')} if tag else set()

def get_minor_version(self):
# only applicable if appropriate vars are defined in group config
def get_minor_version(self) -> str:
"""
Returns: "<MAJOR>.<MINOR>" if the vars are defined in the group config.
"""
return '.'.join(str(self.group_config.vars[v]) for v in ('MAJOR', 'MINOR'))

def get_major_minor_fields(self) -> Tuple[int, int]:
"""
Returns: (int(MAJOR), int(MINOR)) if the vars are defined in the group config.
"""
major = int(self.group_config.vars['MAJOR'])
minor = int(self.group_config.vars['MINOR'])
return major, minor

def scan_for_upstream_changes(self) -> List[Tuple[Metadata, RebuildHint]]:
"""
Determines if the current upstream source commit hash has a downstream
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ python-dateutil >= 2.8.1
openshift-client >= 1.0.12
setuptools-scm
aiohttp
jira==3.4.1

0 comments on commit a5c5058

Please sign in to comment.