diff --git a/prow/README.md b/prow/README.md index edd0e2f95a13..28314c4fc6bf 100644 --- a/prow/README.md +++ b/prow/README.md @@ -23,11 +23,13 @@ Options: --help Show this message and exit. Commands: - get_payloads Check the latest payload of each version. - get_results Return the Prow job executed info. - list List the jobs which support the API call. - run Run a job and save results to /tmp/prow-jobs.csv - run_required Run required jobs from a file + get_payloads Check the latest payload of each version. + get_results Return the Prow job executed info. + list List the jobs which support the API call. + run Run a job and save results to /tmp/prow-jobs.csv + run_image_consistency_check Run image consistency check Prow job. + run_required Run required jobs from a file + run_stage_testing Run stage testing Prow job for a given payload. ``` ### Run job @@ -75,6 +77,27 @@ Returned job id: 3ebe0a6e-ea5c-4c96-9ca4-295074f9eaa3 periodic-ci-openshift-openshift-tests-private-release-4.11-amd64-nightly-4.11-upgrade-from-stable-4.10-gcp-ipi-disconnected-private-p2-f14 None 3ebe0a6e-ea5c-4c96-9ca4-295074f9eaa3 2023-06-07T06:15:10Z https://qe-private-deck-ci.apps.ci.l2s4.p1.openshiftapps.com/view/gs/qe-private-deck/logs/periodic-ci-openshift-openshift-tests-private-release-4.11-amd64-nightly-4.11-upgrade-from-stable-4.10-gcp-ipi-disconnected-private-p2-f14/1666327924111839232 ``` +### Run stage testing + +Run the stage testing Prow job for a given release payload. The job name is version-specific and constructed automatically from the payload URL. + +```console +$ job run_stage_testing -p quay.io/openshift-release-dev/ocp-release:4.19.1-x86_64 +Stage testing job name: periodic-ci-openshift-openshift-tests-private-release-4.19-stage-testing-e2e-aws-ipi +Stage testing job id: a1b2c3d4-e5f6-7890-abcd-ef1234567890 +Stage testing job url: https://qe-private-deck-ci.apps.ci.l2s4.p1.openshiftapps.com/view/gs/qe-private-deck/logs/... +``` + +### Run image consistency check + +Run the image consistency check Prow job for a given release payload and shipment MR. + +```console +$ job run_image_consistency_check -p quay.io/openshift-release-dev/ocp-release:4.19.1-x86_64 -m 189 +Image consistency check job id: a1b2c3d4-e5f6-7890-abcd-ef1234567890 +Image consistency check job url: https://qe-private-deck-ci.apps.ci.l2s4.p1.openshiftapps.com/view/gs/qe-private-deck/logs/... +``` + ### Debug failure job - `Error code: 500, reason: Internal Server Error` This error indicates the input job name doesn't exist, you need to input an exist one. diff --git a/prow/job/job.py b/prow/job/job.py index 0a21ed3443b5..0884b6ddb625 100644 --- a/prow/job/job.py +++ b/prow/job/job.py @@ -26,6 +26,7 @@ class Jobs: POLL_INTERVAL_SECONDS = 5 IMAGE_CONSISTENCY_CHECK_JOB_NAME = "periodic-ci-openshift-release-tests-main-image-consistency-check" + STAGE_TESTING_JOB_NAME_TEMPLATE = "periodic-ci-openshift-openshift-tests-private-release-{minor_release}-stage-testing-e2e-aws-ipi" def __init__(self): self.run = False @@ -346,11 +347,14 @@ def _is_valid_payload_url(self, payload_url: str) -> bool: Returns: True if the payload URL is valid, False otherwise. """ - pattern = r"^quay\.io/openshift-release-dev/ocp-release:\d+\.\d+\.\d+.*-x86_64$" return re.match(pattern, payload_url) is not None - + def _get_minor_release_from_payload_url(self, payload_url): + """Extract minor release (e.g. '4.19') from a validated payload URL.""" + z_version = payload_url.split(":")[1].split("-")[0] + return ".".join(z_version.split(".")[:2]) + def run_image_consistency_check(self, payload_url: str, mr_id: int) -> dict: """ Run image consistency check Prow job. @@ -362,7 +366,6 @@ def run_image_consistency_check(self, payload_url: str, mr_id: int) -> dict: Returns: A dictionary containing the job info. """ - if not self._is_valid_payload_url(payload_url): raise Exception(f"Invalid payload URL: {payload_url}") @@ -399,9 +402,59 @@ def run_image_consistency_check(self, payload_url: str, mr_id: int) -> dict: return job_status + def run_stage_testing(self, payload_url: str) -> dict: + """ + Run stage testing Prow job. + + The Prow job name is version-specific: + periodic-ci-openshift-openshift-tests-private-release-{minor_release}-stage-testing-e2e-aws-ipi + + Args: + payload_url: The URL of the release payload to test + (e.g. quay.io/openshift-release-dev/ocp-release:4.19.1-x86_64) + + Returns: + A dictionary containing the job info. + """ + if not self._is_valid_payload_url(payload_url): + raise Exception(f"Invalid payload URL: {payload_url}") + + minor_release = self._get_minor_release_from_payload_url(payload_url) + + job_name = Jobs.STAGE_TESTING_JOB_NAME_TEMPLATE.format(minor_release=minor_release) + print(f"Stage testing job name: {job_name}") + + url = self.gangway_url + job_name + data = { + "job_execution_type": "1", + "pod_spec_options": { + "envs": { + "RELEASE_IMAGE_LATEST": payload_url, + } + } + } + + job_run_res = self._get_session().post( + url=url, + json=data, + headers=self.get_prow_headers() + ) + if job_run_res.status_code != 200: + raise Exception( + f"Failed to run stage testing job '{job_name}'. " + f"Error code: {job_run_res.status_code}, reason: {job_run_res.reason}" + ) + + job_id = json.loads(job_run_res.text)["id"] + print(f"Stage testing job id: {job_id}") + + job_status = self.get_job_results(job_id, poll=True) + print(f"Stage testing job url: {job_status['jobURL']}") + + return job_status + def run_job(self, job_name, payload, upgrade_from, upgrade_to): """Function run Prow job by calling the API""" - job_id = None if job_name is None: @@ -767,6 +820,18 @@ def run_image_consistency_check(payload_url: str, mr_id: int): """ JOB.run_image_consistency_check(payload_url, mr_id) + +@cli.command("run_stage_testing") +@click.option("-p", "--payload-url", type=str, required=True, help="Payload URL (e.g. quay.io/openshift-release-dev/ocp-release:4.19.1-x86_64)") +def run_stage_testing(payload_url: str): + """ + Run stage testing Prow job for a given payload. + + Args: + payload_url (str): The URL of the payload + """ + JOB.run_stage_testing(payload_url) + # no need this program entry since this file won't be imported as a module. # if __name__ == "__main__": # start = time.time()