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

bci_repo_publish.py: Return 1 if the last published build is over a week old #2998

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all 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
44 changes: 36 additions & 8 deletions gocd/bci_repo_publish.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ def __init__(self):
ToolBase.ToolBase.__init__(self)
self.logger = logging.getLogger(__name__)
self.openqa = OpenQA_Client(server='https://openqa.suse.de')
self.last_publish_mtime = 0

def version_of_product(self, project, package, repo, arch):
"""Get the build version of the given product build, based on the binary name."""
Expand All @@ -43,7 +44,7 @@ def mtime_of_product(self, project, package, repo, arch):
url = makeurl(self.apiurl, ['build', project, repo, arch, package])
root = ET.parse(http_GET(url)).getroot()
mtime = root.xpath('/binarylist/binary[@filename = "_buildenv"]/@mtime')
return mtime[0]
return int(mtime[0])

def openqa_jobs_for_product(self, arch, version, build):
"""Query openQA for all relevant jobs"""
Expand Down Expand Up @@ -76,13 +77,13 @@ def is_repo_published(self, project, repo):

return True

def run(self, version, token=None):
def publish_if_possible(self, version, token=None):
"""If the OBS project is finished and the built binaries are newer than
the ones in the publish location, check openQA for results. If they pass,
use the given token to release the builds into the target project.
The mtime of the last published build is written to self.last_publish_mtime."""
build_prj = f'SUSE:SLE-{version}:Update:BCI'

if not self.is_repo_published(build_prj, 'images'):
self.logger.info(f'{build_prj}/images not successfully built')
return

# Build the list of packages with metainfo
packages = []
# As long as it's the same everywhere, hardcoding this list here
Expand All @@ -95,6 +96,13 @@ def run(self, version, token=None):
'publish_prj': f'SUSE:Products:SLE-BCI:{version}:{arch}'
})

if not self.is_repo_published(build_prj, 'images'):
self.logger.info(f'{build_prj}/images not successfully built')
pkg = packages[0]
self.last_publish_mtime = \
self.mtime_of_product(pkg['publish_prj'], pkg['name'], 'images', 'local')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and here? we're not actually publishing, we're aborting because it wasn't successfully built?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, which means we have to fetch the last published mtime from OBS to know how long ago that was

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as a nit pick, people usually avoid escaping newlines in python. you can write this in a more pythonic way:

     self.last_publish_mtime = self.mtime_of_product(
           pkg['publish_prj'], pkg['name'], 'images', 'local')

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO harder to read and flake8 didn't complain 🤷

return

# Fetch the build numbers of built products.
# After release, the BuildXXX part vanishes, so the mtime has to be
# used instead for comparing built and published binaries.
Expand All @@ -105,6 +113,7 @@ def run(self, version, token=None):
'images', 'local')
pkg['published_mtime'] = self.mtime_of_product(pkg['publish_prj'], pkg['name'],
'images', 'local')
self.last_publish_mtime = max(self.last_publish_mtime, pkg['published_mtime'])

# Verify that the builds for all archs are in sync
built_versions = {pkg['built_version'] for pkg in packages}
Expand All @@ -114,7 +123,7 @@ def run(self, version, token=None):
return

# Compare versions
newer_version_available = [int(pkg['built_mtime']) > int(pkg['published_mtime'])
newer_version_available = [pkg['built_mtime'] > pkg['published_mtime']
for pkg in packages]
if not any(newer_version_available):
self.logger.info('Current build already published, nothing to do.')
Expand Down Expand Up @@ -147,6 +156,7 @@ def run(self, version, token=None):
# Trigger publishing
if token is None:
self.logger.warning('Would publish now, but no token specified')
self.last_publish_mtime = packages[0]['built_mtime']
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are we setting last_publish_mtime in this error case?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not really an error case, more of a dry run case

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure, but there are plenty of other returns before hand that are not setting last_publish_mtime. so this is a bit inconsistent.

return

for pkg in packages:
Expand All @@ -162,12 +172,30 @@ def run(self, version, token=None):
if req.status_code != 200:
raise RuntimeError(f'Releasing failed: {req.text}')

# As sanity check fetch the mtime from the publish prj
pkg = packages[0]
self.last_publish_mtime = \
self.mtime_of_product(pkg['publish_prj'], pkg['name'], 'images', 'local')

self.logger.info('Waiting for publishing to finish')
for pkg in packages:
while not self.is_repo_published(pkg['publish_prj'], 'images'):
self.logger.debug(f'Waiting for {pkg["publish_prj"]}')
time.sleep(20)

def run(self, version, token=None):
"""Try a publish. Return failure if the last publish was >7d ago."""
self.publish_if_possible(version, token)

last_publish_diff_secs = time.time() - self.last_publish_mtime
last_publish_diff_days = last_publish_diff_secs / 60 / 60 / 24
self.logger.info(f'Published build {last_publish_diff_days:.2f} days old')
if last_publish_diff_days > 7:
self.logger.error('Last published build too old!')
return 1

return 0


class CommandLineInterface(ToolBase.CommandLineInterface):
def __init__(self, *args, **kwargs):
Expand All @@ -191,7 +219,7 @@ def do_run(self, subcmd, opts, project):
${cmd_option_list}
"""

self.tool.run(project, token=opts.token)
return self.tool.run(project, token=opts.token)


if __name__ == "__main__":
Expand Down
Loading