Name component builds by states

This patch attemps to make code shorter and easier to read by naming
component builds for different states.

Signed-off-by: Chenxiong Qi <cqi@redhat.com>
This commit is contained in:
Chenxiong Qi
2019-09-11 17:27:31 +08:00
parent 442a855f0a
commit c36bd7ebac
8 changed files with 86 additions and 121 deletions

View File

@@ -29,6 +29,7 @@
import contextlib
import hashlib
import json
import koji
import re
from collections import OrderedDict, namedtuple
from datetime import datetime
@@ -1351,6 +1352,37 @@ class ComponentBuild(MBSBase):
self.state_reason,
)
@property
def is_completed(self):
return self.state == koji.BUILD_STATES["COMPLETE"]
@property
def is_building(self):
return self.state == koji.BUILD_STATES["BUILDING"]
@property
def is_failed(self):
return self.state == koji.BUILD_STATES["FAILED"]
@property
def is_waiting_for_build(self):
return self.state is None
@property
def is_unbuilt(self):
return self.is_waiting_for_build or self.is_building
@property
def is_tagged(self):
return self.tagged and (self.tagged_in_final or self.build_time_only)
@property
def is_unsuccessful(self):
return (
self.state == koji.BUILD_STATES["FAILED"]
or self.state == koji.BUILD_STATES["CANCELED"]
)
class ComponentBuildTrace(MBSBase):
__tablename__ = "component_builds_trace"

View File

@@ -79,22 +79,17 @@ def _finalize(config, db_session, msg, state):
further_work = []
parent_current_batch = parent.current_batch()
# If there are no other components still building in a batch,
# we can tag all successfully built components in the batch.
unbuilt_components_in_batch = [
c for c in parent.current_batch()
if c.state == koji.BUILD_STATES["BUILDING"] or not c.state
c for c in parent_current_batch
if c.is_waiting_for_build or c.is_building
]
if not unbuilt_components_in_batch:
failed_components_in_batch = [
c for c in parent.current_batch()
if (c.state in [koji.BUILD_STATES["FAILED"], koji.BUILD_STATES["CANCELED"]])
]
built_components_in_batch = [
c for c in parent.current_batch()
if c.state == koji.BUILD_STATES["COMPLETE"]
]
failed_components_in_batch = [c for c in parent_current_batch if c.is_unsuccessful]
built_components_in_batch = [c for c in parent_current_batch if c.is_completed]
builder = module_build_service.builder.GenericBuilder.create_from_module(
db_session, parent, config
@@ -151,7 +146,8 @@ def _finalize(config, db_session, msg, state):
builder.tag_artifacts(component_nvrs_to_tag_in_dest)
db_session.commit()
elif any([c.state != koji.BUILD_STATES["BUILDING"] for c in unbuilt_components_in_batch]):
elif any(not c.is_building for c in unbuilt_components_in_batch):
# We are not in the middle of the batch building and
# we have some unbuilt components in this batch. We might hit the
# concurrent builds threshold in previous call of continue_batch_build
@@ -162,6 +158,7 @@ def _finalize(config, db_session, msg, state):
db_session, parent, config)
further_work += module_build_service.utils.continue_batch_build(
config, parent, db_session, builder)
return further_work

View File

@@ -78,11 +78,6 @@ def failed(config, db_session, msg):
# This is ok.. it's a race condition we can ignore.
pass
unbuilt_components = [
c for c in build.component_builds
if (c.state != koji.BUILD_STATES["COMPLETE"] and c.state != koji.BUILD_STATES["FAILED"])
]
if build.koji_tag:
builder = module_build_service.builder.GenericBuilder.create_from_module(
db_session, build, config)
@@ -90,7 +85,7 @@ def failed(config, db_session, msg):
if build.new_repo_task_id:
builder.cancel_build(build.new_repo_task_id)
for component in unbuilt_components:
for component in (c for c in build.component_builds if c.is_unbuilt):
if component.task_id:
builder.cancel_build(component.task_id)
component.state = koji.BUILD_STATES["FAILED"]
@@ -442,7 +437,7 @@ def wait(config, db_session, msg):
component_build.state = state
component_build.reason = reason
component_build.nvr = nvr
elif component_build.state != koji.BUILD_STATES["COMPLETE"]:
elif not component_build.is_completed:
# It's possible that the build succeeded in the builder but some other step failed which
# caused module-build-macros to be marked as failed in MBS, so check to see if it exists
# first

View File

@@ -25,7 +25,6 @@
import module_build_service.builder
import logging
import koji
from datetime import datetime
from module_build_service import models, log
from module_build_service.utils import start_next_batch_build
@@ -54,41 +53,33 @@ def done(config, db_session, msg):
log.info("Ignoring repo regen for already failed %r" % module_build)
return
# If there are no components in this module build, then current_batch will be empty
if module_build.component_builds:
current_batch = module_build.current_batch()
else:
current_batch = []
# Get the list of untagged components in current/previous batches which
# have been built successfully
if config.system in ("koji", "test") and module_build.component_builds:
untagged_components = [
c for c in module_build.up_to_current_batch()
if (not c.tagged or (not c.tagged_in_final and not c.build_time_only))
and c.state == koji.BUILD_STATES["COMPLETE"]
]
if untagged_components:
if config.system in ("koji", "test") and current_batch:
if any(c.is_completed and not c.is_tagged for c in module_build.up_to_current_batch()):
log.info("Ignoring repo regen, because not all components are tagged.")
return
if all([c.state is None for c in module_build.current_batch()]):
if all(c.is_waiting_for_build for c in current_batch):
log.info("Ignoring repo regen because no components have started in the batch.")
return
# If there are no components in this module build, then current_batch will be empty
if not module_build.component_builds:
current_batch = []
else:
current_batch = module_build.current_batch()
# If any in the current batch are still running.. just wait.
running = [c.state == koji.BUILD_STATES["BUILDING"] for c in current_batch]
if any(running):
running = [c for c in current_batch if c.is_building]
if running:
log.info(
"%r has %r of %r components still "
"building in this batch (%r total)"
% (module_build, len(running), len(current_batch), len(module_build.component_builds))
"%r has %r of %r components still building in this batch (%r total)",
module_build, len(running), len(current_batch), len(module_build.component_builds)
)
return
# Assemble the list of all successful components in the batch.
good = [c for c in current_batch if c.state == koji.BUILD_STATES["COMPLETE"]]
failed_states = (koji.BUILD_STATES["FAILED"], koji.BUILD_STATES["CANCELED"])
good = [c for c in current_batch if c.is_completed]
# If *none* of the components completed for this batch, then obviously the
# module fails. However! We shouldn't reach this scenario. There is
@@ -97,7 +88,7 @@ def done(config, db_session, msg):
# valve.
if module_build.component_builds and not good:
state_reason = "Component(s) {} failed to build.".format(
", ".join(c.package for c in current_batch if c.state in failed_states))
", ".join(c.package for c in current_batch if c.is_unsuccessful))
module_build.transition(
db_session, config, models.BUILD_STATES["failed"], state_reason, failure_type="infra")
db_session.commit()
@@ -127,9 +118,8 @@ def done(config, db_session, msg):
# So now we can either start a new batch if there are still some to build
# or, if everything is built successfully, then we can bless the module as
# complete.
has_unbuilt_components = any(
c.state in [None, koji.BUILD_STATES["BUILDING"]] for c in module_build.component_builds)
has_failed_components = any(c.state in failed_states for c in module_build.component_builds)
has_unbuilt_components = any(c.is_unbuilt for c in module_build.component_builds)
has_failed_components = any(c.is_unsuccessful for c in module_build.component_builds)
further_work = []
if has_unbuilt_components and not has_failed_components:
@@ -148,7 +138,7 @@ def done(config, db_session, msg):
if has_failed_components:
state_reason = "Component(s) {} failed to build.".format(
", ".join(
c.package for c in module_build.component_builds if c.state in failed_states
c.package for c in module_build.component_builds if c.is_unsuccessful
)
)
module_build.transition(

View File

@@ -58,37 +58,21 @@ def tagged(config, db_session, msg):
component.tagged_in_final = True
db_session.commit()
unbuilt_components_in_batch = [
c for c in module_build.current_batch()
if c.state == koji.BUILD_STATES["BUILDING"] or not c.state
]
if unbuilt_components_in_batch:
if any(c.is_unbuilt for c in module_build.current_batch()):
log.info(
"Not regenerating repo for tag %s, there are still building components in a batch",
tag,
)
return []
# Get the list of untagged components in current/previous batches which
# have been built successfully.
untagged_components = [
c for c in module_build.up_to_current_batch()
if (not c.tagged or (not c.tagged_in_final and not c.build_time_only))
and c.state == koji.BUILD_STATES["COMPLETE"]
]
further_work = []
# If all components are tagged, start newRepo task.
if not untagged_components:
if not any(c.is_completed and not c.is_tagged for c in module_build.up_to_current_batch()):
builder = module_build_service.builder.GenericBuilder.create_from_module(
db_session, module_build, config)
unbuilt_components = [
c for c in module_build.component_builds
if c.state == koji.BUILD_STATES["BUILDING"] or not c.state
]
if unbuilt_components:
if any(c.is_unbuilt for c in module_build.component_builds):
if not _is_new_repo_generating(module_build, builder.koji_session):
repo_tag = builder.module_build_tag["name"]
log.info("All components in batch tagged, regenerating repo for tag %s", repo_tag)

View File

@@ -163,10 +163,7 @@ class MBSProducer(PollingProducer):
for module in stale_module_builds:
log.info("{0!r} is stale and is being cleaned up".format(module))
# Find completed artifacts in the stale build
artifacts = [
c for c in module.component_builds
if c.state == koji.BUILD_STATES["COMPLETE"]
]
artifacts = [c for c in module.component_builds if c.is_completed]
# If there are no completed artifacts, then there is nothing to tag
if artifacts:
# Set buildroot_connect=False so it doesn't recreate the Koji target and etc.

View File

@@ -93,7 +93,7 @@ def start_build_component(db_session, builder, c):
db_session.commit()
return
if not c.task_id and c.state == koji.BUILD_STATES["BUILDING"]:
if not c.task_id and c.is_building:
c.state = koji.BUILD_STATES["FAILED"]
c.state_reason = "Failed to build artifact %s: Builder did not return task ID" % (c.package)
with BUILD_COMPONENT_DB_SESSION_LOCK:
@@ -116,13 +116,8 @@ def continue_batch_build(config, module, db_session, builder, components=None):
# if none are provided then we just select everything that hasn't
# successfully built yet or isn't currently being built.
unbuilt_components = components or [
c for c in module.component_builds
if (
c.state != koji.BUILD_STATES["COMPLETE"]
and c.state != koji.BUILD_STATES["BUILDING"]
and c.state != koji.BUILD_STATES["FAILED"]
and c.batch == module.batch
)
c for c in module.current_batch()
if not c.is_completed and not c.is_building and not c.is_failed
]
if not unbuilt_components:
@@ -141,7 +136,7 @@ def continue_batch_build(config, module, db_session, builder, components=None):
# Check for builds that exist in the build system but MBS doesn't know about
for component in unbuilt_components:
# Only evaluate new components
if component.state is not None:
if not component.is_waiting_for_build:
continue
msgs = builder.recover_orphaned_artifact(component)
further_work += msgs
@@ -149,7 +144,7 @@ def continue_batch_build(config, module, db_session, builder, components=None):
for c in unbuilt_components:
# If a previous build of the component was found, then the state will be marked as
# COMPLETE so we should skip this
if c.state == koji.BUILD_STATES["COMPLETE"]:
if c.is_completed:
continue
# Check the concurrent build threshold.
if at_concurrent_component_threshold(config, db_session):
@@ -189,47 +184,23 @@ def start_next_batch_build(config, module, db_session, builder, components=None)
:return: a list of BaseMessage instances to be handled by the MBSConsumer.
"""
import koji # Placed here to avoid py2/py3 conflicts...
# Check the status of the module build and current batch so we can
# later decide if we can start new batch or not.
has_unbuilt_components = False
has_unbuilt_components_in_batch = False
has_building_components_in_batch = False
has_failed_components = False
# This is used to determine if it's worth checking if a component can be reused
# later on in the code
all_reused_in_prev_batch = True
for c in module.component_builds:
if c.state in [None, koji.BUILD_STATES["BUILDING"]]:
has_unbuilt_components = True
if c.batch == module.batch:
if not c.state:
has_unbuilt_components_in_batch = True
elif c.state == koji.BUILD_STATES["BUILDING"]:
has_building_components_in_batch = True
elif c.state in [koji.BUILD_STATES["FAILED"], koji.BUILD_STATES["CANCELED"]]:
has_failed_components = True
if c.batch == module.batch and not c.reused_component_id:
all_reused_in_prev_batch = False
# Do not start new batch if there are no components to build.
if not has_unbuilt_components:
if not any(c.is_unbuilt for c in module.component_builds):
log.debug(
"Not starting new batch, there is no component to build for module %s" % module)
return []
# Check that there is something to build in current batch before starting
current_batch = module.current_batch()
# Check that if there is something to build in current batch before starting
# the new one. If there is, continue building current batch.
if has_unbuilt_components_in_batch:
if any(c.is_unbuilt for c in current_batch):
log.info("Continuing building batch %d", module.batch)
return continue_batch_build(config, module, db_session, builder, components)
# Check that there are no components in BUILDING state in current batch.
# If there are, wait until they are built.
if has_building_components_in_batch:
if any(c.is_building for c in current_batch):
log.debug(
"Not starting new batch, there are still components in "
"BUILDING state in current batch for module %s",
@@ -239,7 +210,7 @@ def start_next_batch_build(config, module, db_session, builder, components=None)
# Check that there are no failed components in this batch. If there are,
# do not start the new batch.
if has_failed_components:
if any(c.is_unsuccessful for c in module.component_builds):
log.info("Not starting new batch, there are failed components for module %s", module)
return []
@@ -268,12 +239,17 @@ def start_next_batch_build(config, module, db_session, builder, components=None)
# Find out if there is repo regeneration in progress for this module.
# If there is, wait until the repo is regenerated before starting a new
# batch.
artifacts = [c.nvr for c in module.current_batch()]
artifacts = [c.nvr for c in current_batch]
if not builder.buildroot_ready(artifacts):
log.info(
"Not starting new batch, not all of %r are in the buildroot. Waiting." % artifacts)
return []
# This is used to determine if it's worth checking if a component can be
# reused later on in the code
all_reused_in_prev_batch = all(
c.reused_component_id is not None for c in module.component_builds)
# Although this variable isn't necessary, it is easier to read code later on with it
prev_batch = module.batch
module.batch += 1
@@ -282,13 +258,8 @@ def start_next_batch_build(config, module, db_session, builder, components=None)
# if none are provided then we just select everything that hasn't
# successfully built yet or isn't currently being built.
unbuilt_components = components or [
c for c in module.component_builds
if (
c.state != koji.BUILD_STATES["COMPLETE"]
and c.state != koji.BUILD_STATES["BUILDING"]
and c.state != koji.BUILD_STATES["FAILED"]
and c.batch == module.batch
)
c for c in module.current_batch()
if not c.is_completed and not c.is_building and not c.is_failed
]
# If there are no components to build, skip the batch and start building

View File

@@ -910,7 +910,6 @@ def submit_module_build(db_session, username, mmd, params):
:rtype: list with ModuleBuild
:return: List with submitted module builds.
"""
import koji # Placed here to avoid py2/py3 conflicts...
from .mse import generate_expanded_mmds
log.debug(
@@ -975,7 +974,7 @@ def submit_module_build(db_session, username, mmd, params):
log.debug("Resuming existing module build %r" % module)
# Reset all component builds that didn't complete
for component in module.component_builds:
if component.state is not None and component.state != koji.BUILD_STATES["COMPLETE"]:
if not component.is_waiting_for_build and not component.is_completed:
component.state = None
component.state_reason = None
db_session.add(component)