#65 Send proper Y/Z metadata during builds
Merged a year ago by tdawson. Opened a year ago by tdawson.
centos/ tdawson/centpkg rhel-target  into  develop

file modified
+120
@@ -23,7 +23,11 @@ 

  from pyrpkg.cli import cliClient

  from pyrpkg import rpkgError

  from six.moves.urllib_parse import urlparse

+ import six.moves.configparser as ConfigParser

  

+ import json

+ import koji

+ import os

  

  _DEFAULT_API_BASE_URL = 'https://gitlab.com'

  
@@ -126,6 +130,122 @@ 

              msg = "Remote with name '{0}' already exists."

          self.log.info(msg.format(remote_name))

  

+     # Overloaded build

+     def register_build(self):

+         # Do all the work from the super class

+         super(centpkgClient, self).register_build()

+         build_parser = self.subparsers.choices['build']

+         build_parser.formatter_class = argparse.RawDescriptionHelpFormatter

+         build_parser.description = textwrap.dedent('''

+             {0}

+ 

+             centpkg now sets the rhel metadata with --rhel-target.

+              * exception - This will build for the current in-development Y-stream release.

+                It is equivalent to passing latest when not in the Blocker and Exception Phase.

+              * zstream - If pre-GA of a y-stream release, this will build for 0day.

+                If post-GA of a Y-stream release, this will build for the Z-stream of that release.

+              * latest - This will always build for the next Y-stream release

+ 

+         '''.format('\n'.join(textwrap.wrap(build_parser.description))))

+ 

+         # Now add our additional option

+         build_parser.add_argument(

+             '--rhel-target',

+             choices=['exception', 'zstream', 'latest'],

+             help='Set the rhel-target metadata')

+ 

+     # Overloaded _build

+     def _build(self, sets=None):

+ 

+         # Only run if we have internal configuraions, or scratch

+         internal_config_file = "/etc/rpkg/centpkg_internal.conf"

+         if os.path.exists(internal_config_file):

+             # Get our internal only variables

+             cfg = ConfigParser.SafeConfigParser()

+             cfg.read(internal_config_file)

+             pp_api_url = config_get_safely(cfg, "centpkg.internal", 'pp_api_url')

+             gitbz_query_url = config_get_safely(cfg, "centpkg.internal", 'gitbz_query_url')

+             rhel_dist_git = config_get_safely(cfg, "centpkg.internal", 'rhel_dist_git')

+             

+             # Find out divergent branch and stabalization

+             self.log.info("Checking rhel-target information:")

+             stream_version = self.cmd.target.split('-')[0]

+             rhel_version = centpkg.utils.stream_mapping(stream_version)

+             active_y, in_stabilization = centpkg.utils.determine_active_y_version(rhel_version, pp_api_url)

+             divergent_branch = centpkg.utils.does_divergent_branch_exist(

+                                     self.cmd.repo_name, 

+                                     rhel_version, 

+                                     rhel_dist_git, 

+                                     pp_api_url, 

+                                     "rpms")

+ 

+             # Good to know

+             if divergent_branch :

+                 self.log.info("    a divergent branch was found.")

+             else:

+                 self.log.info("    a divergent branch was not found.")

+             if in_stabilization :

+                 self.log.info("    we are in stabilization mode.")

+             else:

+                 self.log.info("    we are not in stabilization mode.")

+             

+             # Update args.custom_user_metadata

+             if hasattr(self.args, 'custom_user_metadata') and self.args.custom_user_metadata:

+                 try:

+                     temp_custom_user_metadata = json.loads(self.args.custom_user_metadata)

+                 # Use ValueError instead of json.JSONDecodeError for Python 2 and 3 compatibility

+                 except ValueError as e:

+                     self.parser.error("--custom-user-metadata is not valid JSON: %s" % e)

+                 if not isinstance(temp_custom_user_metadata, dict):

+                     self.parser.error("--custom-user-metadata must be a JSON object")

+                 if hasattr(self.args, 'rhel_target') and self.args.rhel_target:

+                     temp_custom_user_metadata["rhel-target"] = self.args.rhel_target

+                 else:

+                     if divergent_branch:

+                         temp_custom_user_metadata["rhel-target"] = "latest"

+                     elif not divergent_branch and not in_stabilization :

+                         temp_custom_user_metadata["rhel-target"] = "zstream"

+                     else:

+                         self.log.info("We are currently in Stabalization mode")

+                         self.log.info("You must either set the rhel-target (--rhel-target)")

+                         self.log.info("or branch for the previous version.")

+                         self.log.info("Exiting")

+                         raise SystemExit

+                 self.args.custom_user_metadata = json.dumps(temp_custom_user_metadata)

+             else:

+                 if hasattr(self.args, 'rhel_target') and self.args.rhel_target:

+                     temp_custom_user_metadata = {"rhel-target": self.args.rhel_target}

+                     self.args.custom_user_metadata = json.dumps(temp_custom_user_metadata)

+                 else:

+                     if divergent_branch:

+                         self.args.custom_user_metadata = '{"rhel-target": "latest"}'

+                     elif not divergent_branch and not in_stabilization :

+                         self.args.custom_user_metadata = '{"rhel-target": "zstream"}'

+                     else:

+                         self.log.info("We are currently in Stabalization mode")

+                         self.log.info("You must either set the rhel-target (--rhel-target)")

+                         self.log.info("or branch for the previous version.")

+                         self.log.info("Exiting")

+                         raise SystemExit

+                     

+ 

+             # Good to know, but take out for final cut

+             self.log.info("    rhel-target: " + json.loads(self.args.custom_user_metadata)['rhel-target'])

+ 

+             # Purposely fail during testing so we do not have so many builds

+             # self.args.custom_user_metadata = json.loads(self.args.custom_user_metadata)

+         

+         else:

+             if not self.args.scratch:

+                 self.log.info("NO SCRATCH BUILD")

+                 self.log.info("Only scratch builds are allowed without internal configurations")

+                 self.log.info("Exiting")

+                 raise SystemExit

+         

+         # Proceed with build

+         return super(centpkgClient, self)._build(sets)

+ 

+ 

      def register_request_gated_side_tag(self):

          """Register command line parser for subcommand request-gated-side-tag"""

          parser = self.subparsers.add_parser(

file modified
+180 -3
@@ -10,16 +10,22 @@ 

  # option) any later version.  See http://www.gnu.org/copyleft/gpl.html for

  # the full text of the license.

  

- import re

- import json

- 

  import git

+ import json

+ import logging

+ import os

+ import pytz

+ import re

  import requests

+ import sys

+ from datetime import date, datetime

  from pyrpkg import rpkgError

  from requests.exceptions import ConnectionError

  from six.moves.configparser import NoOptionError, NoSectionError

  from six.moves.urllib.parse import quote_plus, urlparse

  

+ import git as gitpython

+ 

  dist_git_config = None

  

  def do_fork(logger, base_url, token, repo_name, namespace, cli_name):
@@ -255,3 +261,174 @@ 

      repo_name = get_canonical_repo_name(dist_git_config, name)

  

      return '%s/%s' % (org, repo_name)

+ 

+ def stream_mapping(csname):

+     """

+     Given a CentOS Stream name, map it to the corresponding RHEL name.

+ 

+     Parameters

+     ----------

+     csname: str

+         The CentOS Stream name.

+ 

+     Returns

+     -------

+     str

+         Correspoinding RHEL name.

+     """

+     if csname == "c8s" or csname == "cs8" :

+         return "rhel-8"

+     if csname == "c9s" or csname == "cs9" :

+         return "rhel-9"

+     if csname == "c10s" or csname == "cs10" :

+         return "rhel-10"

+     if csname == "c11s" or csname == "cs11" :

+         return "rhel-11"

+     return None

+ 

+ def does_divergent_branch_exist(repo_name, rhel_version, rhel_dist_git, pp_api_url, namespace):

+     logger = logging.getLogger(__name__)

+ 

+     # Determine if the Y-1 branch exists for this repo

+ 

+     # Look up the Y-1 branch name

+     divergent_branch = determine_divergent_branch(

+         rhel_version,

+         pp_api_url,

+         namespace,

+     )

+     logger.debug("Divergent branch: {}".format(divergent_branch))

+     

+     g = gitpython.cmd.Git()

+     try:

+         g.ls_remote(

+             "--exit-code",

+             os.path.join(rhel_dist_git, namespace, repo_name),

+             divergent_branch,

+         )

+         branch_exists = True

+     except gitpython.GitCommandError as e:

+         t, v, tb = sys.exc_info()

+         # `git ls-remote --exit-code` returns "2" if it cannot find the ref

+         if e.status == 2:

+             branch_exists = False

+         else:

+             raise

+     return branch_exists

+ 

+ def determine_divergent_branch(rhel_version, pp_api_url, namespace):

+     logger = logging.getLogger(__name__)

+ 

+     # Query the "package pages" API for the current active Y-stream release

+     # Phase 230 is "Planning / Development / Testing" (AKA DeveTestDoc)

+     request_params = {

+         "phase": 230,

+         "product__shortname": "rhel",

+         "relgroup__shortname": rhel_version,

+         "format": "json",

+     }

+ 

+     res = requests.get(

+         os.path.join(pp_api_url, "latest", "releases"),

+         params=request_params,

+         timeout=60,

+     )

+     res.raise_for_status()

+     payload = json.loads(res.text)

+     logger.debug(

+         "Response from PP API: {}".format(json.dumps(payload, indent=2))

+     )

+     if len(payload) < 1:

+         raise RuntimeError("Received zero potential release matches)")

+ 

+     active_y_version = -1

+     for entry in payload:

+         shortname = entry["shortname"]

+ 

+         # The shortname is in the form rhel-9-1.0

+         # Extract the active Y-stream version

+         m = re.search("(?<={}-)\d+(?=\.0)".format(rhel_version), shortname)

+         if not m:

+             raise RuntimeError(

+                 "Could not determine active Y-stream version from shortname"

+             )

+         y_version = int(m.group(0))

+         if y_version > active_y_version:

+             active_y_version = y_version

+ 

+     # The divergent branch is Y-1

+     return "{}.{}.0".format(rhel_version, active_y_version - 1)

+ 

+ def _datesplit(isodate):

+     date_string_tuple = isodate.split('-')

+     return [ int(x) for x in date_string_tuple ]

+ 

+ 

+ def determine_active_y_version(rhel_version, pp_api_url):

+     """

+     Returns: A 2-tuple of the active Y-stream version(int) and whether we are

+     in the Exception Phase(bool)

+     """

+     logger = logging.getLogger(__name__)

+ 

+     # Query the "package pages" API for the current active Y-stream release

+     # Phase 230 is "Planning / Development / Testing" (AKA DeveTestDoc)

+     request_params = {

+         "phase": 230,

+         "product__shortname": "rhel",

+         "relgroup__shortname": rhel_version,

+         "format": "json",

+     }

+ 

+     res = requests.get(

+         os.path.join(pp_api_url, "latest", "releases"),

+         params=request_params,

+         timeout=60,

+     )

+     res.raise_for_status()

+     payload = json.loads(res.text)

+     logger.debug(

+         "Response from PP API: {}".format(json.dumps(payload, indent=2))

+     )

+     if len(payload) < 1:

+         raise RuntimeError("Received zero potential release matches)")

+ 

+     release_id = -1

+     active_y_version = -1

+     for entry in payload:

+         shortname = entry["shortname"]

+ 

+         # The shortname is in the form rhel-9-1.0

+         # Extract the active Y-stream version

+         m = re.search("(?<={}-)\d+(?=\.0)".format(rhel_version), shortname)

+         if not m:

+             raise RuntimeError(

+                 "Could not determine active Y-stream version from shortname"

+             )

+         y_version = int(m.group(0))

+         if y_version > active_y_version:

+             active_y_version = y_version

+             release_id = entry["id"]

+ 

+     # Now look up whether we are in the Exception Phase for this Y-stream release

+     request_params = {

+         "name__regex": "Exception Phase",

+         "format": "json",

+     }

+     res = requests.get(os.path.join(pp_api_url, "latest", "releases", str(release_id), "schedule-tasks"), params=request_params)

+     res.raise_for_status()

+     payload = json.loads(res.text)

+ 

+     # This lookup *must* return exactly one value or the Product Pages are

+     # wrong and must be fixed.

+     assert len(payload) == 1

+ 

+     # Determine if this Y-stream release is in the exception phase

+     today = datetime.now(tz=pytz.utc).date()

+     exception_start_date = date(*_datesplit(payload[0]["date_start"]))

+     in_exception_phase = today >= exception_start_date

+ 

+     logger.debug("Active Y-stream: {}, Enforcing: {}".format(active_y_version, in_exception_phase))

+ 

+     return active_y_version, in_exception_phase

+ 

For Y Z builds we need to have the proper metadata sent to koji when a build happens.
This adds the --rhel-target option, and makes sure the proper metadata get's passed along.

Signed-off-by: Troy Dawson tdawson@redhat.com

1 new commit added

  • Proper log output
a year ago

1 new commit added

  • Fix logic
a year ago

Although no comments have been posted here, people have looked at, and thought the final code was good. Merging.

Pull-Request has been merged by tdawson

a year ago