sts-sponsors team mailing list archive
-
sts-sponsors team
-
Mailing list archive
-
Message #03553
[Merge] ~alexsander-souza/maas/+git/maas-release-tools:add_jenkins into ~maas-committers/maas/+git/maas-release-tools:main
Alexsander de Souza has proposed merging ~alexsander-souza/maas/+git/maas-release-tools:add_jenkins into ~maas-committers/maas/+git/maas-release-tools:main.
Commit message:
add checks for MAAS system integration tests
Requested reviews:
MAAS Committers (maas-committers)
For more details, see:
https://code.launchpad.net/~alexsander-souza/maas/+git/maas-release-tools/+merge/433614
--
Your team MAAS Committers is requested to review the proposed merge of ~alexsander-souza/maas/+git/maas-release-tools:add_jenkins into ~maas-committers/maas/+git/maas-release-tools:main.
diff --git a/maas_release_tools/maasci.py b/maas_release_tools/maasci.py
new file mode 100644
index 0000000..099df77
--- /dev/null
+++ b/maas_release_tools/maasci.py
@@ -0,0 +1,53 @@
+"""Interact with Jenkins API"""
+import configparser
+from functools import cached_property
+import logging
+from os.path import expanduser
+from pathlib import Path
+from typing import Optional, Tuple
+
+from jenkins import Jenkins
+
+JJB_CONFIG = Path("~/.config/jenkins_jobs/jenkins_jobs.ini")
+JJB_SECTION = "maas-integration-ci"
+
+
+class JenkinsActions:
+ def __init__(
+ self,
+ server_section: Optional[str] = None,
+ jenkins_config: Optional[Path] = None,
+ dry_run: bool = False,
+ ):
+ self._jenkins = self._get_client(
+ server_section=server_section, jenkins_config=jenkins_config
+ )
+ self.logger = logging.getLogger("jenkins")
+ self.dry_run = dry_run
+
+ def _get_client(
+ self,
+ server_section: Optional[str] = None,
+ jenkins_config: Optional[Path] = None,
+ ) -> Jenkins:
+ """Return a Jenkins API client."""
+ jenkins_config = jenkins_config or Path(expanduser(JJB_CONFIG))
+ server_section = server_section or JJB_SECTION
+ config = configparser.ConfigParser()
+ config.read(jenkins_config)
+ url = config[server_section]["url"]
+ kwargs = {
+ "username": config[server_section]["user"],
+ "password": config[server_section]["password"],
+ }
+ return Jenkins(url, **kwargs)
+
+ @cached_property
+ def me(self) -> str:
+ return self._jenkins.get_whoami()["id"]
+
+ def get_last_build_result(self, job_name: str) -> Tuple[str, str]:
+ job = self._jenkins.get_job_info(job_name)
+ last_build = job["lastCompletedBuild"]["number"]
+ build_info = self._jenkins.get_build_info(job_name, last_build)
+ return str(build_info["result"]), str(build_info["url"])
diff --git a/maas_release_tools/scripts/release_status.py b/maas_release_tools/scripts/release_status.py
index bcd7a75..ba2a9b0 100644
--- a/maas_release_tools/scripts/release_status.py
+++ b/maas_release_tools/scripts/release_status.py
@@ -23,6 +23,7 @@ import sys
from typing import Iterable, Optional
from debian.changelog import Changelog
+from jenkins import JenkinsException
from lazr.restfulclient.errors import NotFound
from pymacaroons import Macaroon
import requests
@@ -30,6 +31,7 @@ import requests
from . import convert_file_descriptors_to_path
from ..git import Git
from ..launchpad import DONE_BUGS, LaunchpadActions, UnknownLaunchpadEntry
+from ..maasci import JenkinsActions
from ..version import get_branch_setup_version, ReleaseVersion
MAAS_SNAP_ID = "shY22YTZ3RhJJDOj0MfmShTNZTEb1Jiq"
@@ -92,8 +94,10 @@ class ReleasePreparer:
version: ReleaseVersion,
snapstore_auth,
launchpad: LaunchpadActions,
+ jenkins: JenkinsActions,
):
self.launchpad = launchpad
+ self.jenkins = jenkins
self.version = version
self.snapstore_auth = snapstore_auth
self.git_short_rev = Git().get_short_rev("HEAD")
@@ -416,7 +420,9 @@ class PackagesCopiedToReleasePPA(MAASPPA):
return f"Packages copied to ppa:{self.ppa_path}"
def skip(self):
- return self.preparer.version.grade == "beta" and self.ppa_type == "stable"
+ return (
+ self.preparer.version.grade == "beta" and self.ppa_type == "stable"
+ )
def check(self):
sources = {
@@ -717,15 +723,64 @@ class DebianChangelogUpdated(ReleaseStep):
)
+class SystemIntegrationTests(ReleaseStep):
+ def __init__(
+ self,
+ preparer: ReleasePreparer,
+ job_name: str,
+ ):
+ super().__init__(preparer)
+ self._job = job_name
+
+ @property
+ def title(self):
+ return f"System Integration '{self._job}' result"
+
+ def check(self):
+ try:
+ result, url = self.preparer.jenkins.get_last_build_result(
+ self._job
+ )
+ if result == "FAILURE":
+ return (
+ False,
+ f"Last build has failed, check {url}",
+ )
+ except JenkinsException:
+ return (
+ False,
+ "Failed to communicate with Jenkins, check your credentials",
+ )
+ else:
+ return True, None
+
+
def parse_args():
parser = ArgumentParser(description=__doc__)
parser.add_argument("version", help="The version of MAAS to be released")
parser.add_argument(
+ "--dry-run",
+ action="store_true",
+ dest="dry_run",
+ help="Don't execute actions",
+ )
+ parser.add_argument(
"--launchpad-credentials",
default=None,
type=FileType(),
help="Launchpad credentials file",
)
+ parser.add_argument(
+ "--jenkins-config",
+ default=None,
+ type=FileType(),
+ help="Jenkins configuration file",
+ )
+ parser.add_argument(
+ "--jenkins-section",
+ default=None,
+ help="Jenkins server section name",
+ )
ns = parser.parse_args()
convert_file_descriptors_to_path(ns)
@@ -744,13 +799,21 @@ def main():
return 1
launchpad = LaunchpadActions(
- "maas", credentials_file=args.launchpad_credentials
+ "maas",
+ credentials_file=args.launchpad_credentials,
+ dry_run=args.dry_run,
+ )
+ jenkins = JenkinsActions(
+ dry_run=args.dry_run,
+ jenkins_config=args.jenkins_config,
+ server_section=args.jenkins_section,
)
release_version = ReleaseVersion(args.version)
preparer = ReleasePreparer(
release_version,
macaroon_auth(macaroons),
launchpad=launchpad,
+ jenkins=jenkins,
)
preparer.steps = [
MAASVersion(preparer),
@@ -779,6 +842,8 @@ def main():
preparer,
release_version.snap_channels[0] + "/release-prep",
),
+ SystemIntegrationTests(preparer, "maas-system-tests"),
+ SystemIntegrationTests(preparer, "maas-system-tests-snap"),
PackagesCopiedToReleasePPA(preparer, "candidate"),
*[
SnapsInChannel(preparer, snap_channel)
Follow ups