mirror of
https://git.launchpad.net/~ubuntu-release/britney/+git/britney2-ubuntu
synced 2025-02-13 15:37:02 +00:00
feat(cloud): Add new cloud package set
The new cloud package set is a JSON file which has proper mapping of source packages that Britney has to the binary packages that cloud-test-framework expects.
This commit is contained in:
parent
b0a569d303
commit
04dc74a8cf
@ -48,7 +48,7 @@ If you have any questions about this email, please ask them in #ubuntu-release c
|
||||
Regards, Ubuntu Release Team.
|
||||
"""
|
||||
class CloudPolicy(BasePolicy):
|
||||
PACKAGE_SET_FILE = "cloud_package_set"
|
||||
PACKAGE_SET_FILE = "cloud_package_set.json"
|
||||
STATE_FILE = "cloud_state"
|
||||
DEFAULT_EMAILS = ["cpc@canonical.com"]
|
||||
TEST_LOG_FILE = "CTF.log"
|
||||
@ -89,16 +89,21 @@ class CloudPolicy(BasePolicy):
|
||||
self.failures = {}
|
||||
self.errors = {}
|
||||
self.email_needed = False
|
||||
self.package_set = {}
|
||||
|
||||
def initialise(self, britney):
|
||||
super().initialise(britney)
|
||||
|
||||
self.package_set = self._retrieve_cloud_package_set_for_series(self.options.series)
|
||||
self.package_set = self._retrieve_cloud_package_set_for_series(
|
||||
self.PACKAGE_SET_FILE,
|
||||
self.options.series
|
||||
)
|
||||
self._load_state()
|
||||
|
||||
def apply_src_policy_impl(self, policy_info, item, source_data_tdist, source_data_srcdist, excuse):
|
||||
self.logger.info("Cloud Policy: Looking at {}".format(item.package))
|
||||
if item.package not in self.package_set:
|
||||
clouds_to_test = self._check_if_tests_required(self.package_set, item.package)
|
||||
if len(clouds_to_test) == 0:
|
||||
verdict = PolicyVerdict.PASS
|
||||
excuse.add_verdict_info(
|
||||
verdict,
|
||||
@ -119,7 +124,7 @@ class CloudPolicy(BasePolicy):
|
||||
if self.reporting_enabled == "yes":
|
||||
self._report_test_start(item.package, source_data_srcdist.version, self.options.series)
|
||||
|
||||
self._run_cloud_tests(item.package, source_data_srcdist.version, self.options.series,
|
||||
self._run_cloud_tests(clouds_to_test, item.package, source_data_srcdist.version, self.options.series,
|
||||
self.sources, self.source_type)
|
||||
|
||||
if len(self.failures) > 0 or len(self.errors) > 0:
|
||||
@ -223,34 +228,55 @@ class CloudPolicy(BasePolicy):
|
||||
json.dump(self.state, data)
|
||||
self.logger.info("Saved cloud policy state file %s" % self.state_filename)
|
||||
|
||||
def _retrieve_cloud_package_set_for_series(self, series):
|
||||
def _retrieve_cloud_package_set_for_series(self, file_path, series):
|
||||
"""Retrieves a set of packages for the given series in which cloud
|
||||
tests should be run.
|
||||
|
||||
Temporarily a static list retrieved from file. Will be updated to
|
||||
retrieve from a database at a later date.
|
||||
|
||||
:param file_path The path to the package set file.
|
||||
:param series The Ubuntu codename for the series (e.g. jammy)
|
||||
"""
|
||||
package_set = set()
|
||||
package_set = {}
|
||||
|
||||
with open(self.PACKAGE_SET_FILE) as file:
|
||||
for line in file:
|
||||
package_set.add(line.strip())
|
||||
with open(file_path) as file:
|
||||
full_package_set = json.load(file)
|
||||
for cloud, cloud_set in full_package_set.items():
|
||||
for cloud_series, series_set in cloud_set.items():
|
||||
if cloud_series == series:
|
||||
package_set[cloud] = series_set
|
||||
|
||||
return package_set
|
||||
|
||||
def _run_cloud_tests(self, package, version, series, sources, source_type):
|
||||
def _check_if_tests_required(self, package_set, package):
|
||||
"""Checks the package set to see if the package is present and therefore tests are required.
|
||||
Returns a list of the clouds which contain the package.
|
||||
|
||||
:param package_set The cloud package set as a dictionary.
|
||||
:param package The name of the source package.
|
||||
:returns A list of clouds which require tests for this package.
|
||||
"""
|
||||
clouds_to_test = []
|
||||
for cloud, packages in package_set.items():
|
||||
if package in packages:
|
||||
clouds_to_test.append(cloud)
|
||||
|
||||
return clouds_to_test
|
||||
|
||||
def _run_cloud_tests(self, clouds, package, version, series, sources, source_type):
|
||||
"""Runs any cloud tests for the given package.
|
||||
Nothing is returned but test failures and errors are stored in instance variables.
|
||||
|
||||
:param clouds A list of clouds which need tests run
|
||||
:param package The name of the package to test
|
||||
:param version Version of the package
|
||||
:param series The Ubuntu codename for the series (e.g. jammy)
|
||||
:param sources List of sources where the package should be installed from (e.g. [proposed] or PPAs)
|
||||
:param source_type Either 'archive' or 'ppa'
|
||||
"""
|
||||
self._run_azure_tests(package, version, series, sources, source_type)
|
||||
for cloud in clouds:
|
||||
if cloud == "azure":
|
||||
self._run_azure_tests(package, version, series, sources, source_type)
|
||||
else:
|
||||
raise RuntimeError("Cloud Policy: Unexpected cloud to test: {}".format(cloud))
|
||||
|
||||
def _send_emails_if_needed(self, package, version, series):
|
||||
"""Sends email(s) if there are test failures and/or errors
|
||||
@ -290,13 +316,14 @@ class CloudPolicy(BasePolicy):
|
||||
return
|
||||
|
||||
urn = self._retrieve_urn(series)
|
||||
binaries = self._retrieve_binaries("azure", package)
|
||||
|
||||
self.logger.info("Cloud Policy: Running Azure tests for: {} in {}".format(package, series))
|
||||
params = [
|
||||
"/snap/bin/cloud-test-framework",
|
||||
"--instance-prefix", "britney-{}-{}".format(package, series)
|
||||
]
|
||||
params.extend(self._format_install_flags(package, sources, source_type))
|
||||
params.extend(self._format_install_flags(binaries, sources, source_type))
|
||||
params.extend(
|
||||
[
|
||||
"azure_gen2",
|
||||
@ -341,6 +368,14 @@ class CloudPolicy(BasePolicy):
|
||||
|
||||
return urn
|
||||
|
||||
def _retrieve_binaries(self, cloud, source_package):
|
||||
"""Given a source package name and cloud, retrieves the associated binary package names from the package set.
|
||||
|
||||
:param source_package The name of a source package.
|
||||
:returns The list of binary package names.
|
||||
"""
|
||||
return self.package_set[cloud][source_package]
|
||||
|
||||
def _find_results_files(self, file_regex):
|
||||
"""Find any test results files that match the given regex pattern.
|
||||
|
||||
@ -503,25 +538,28 @@ class CloudPolicy(BasePolicy):
|
||||
|
||||
return info
|
||||
|
||||
def _format_install_flags(self, package, sources, source_type):
|
||||
def _format_install_flags(self, binaries, sources, source_type):
|
||||
"""Determine the flags required to install the package from the given sources
|
||||
|
||||
:param package The name of the package to test
|
||||
:param binaries A list of binary package names
|
||||
:param sources List of sources where the package should be installed from (e.g. [proposed] or PPAs)
|
||||
:param source_type Either 'archive' or 'ppa'
|
||||
"""
|
||||
install_flags = []
|
||||
|
||||
for source in sources:
|
||||
flag = ""
|
||||
if source_type == "archive":
|
||||
install_flags.append("--install-archive-package")
|
||||
install_flags.append("{}/{}".format(package, source))
|
||||
flag = "--install-archive-package"
|
||||
elif source_type == "ppa":
|
||||
install_flags.append("--install-ppa-package")
|
||||
install_flags.append("{}/{}".format(package, source))
|
||||
flag = "--install-ppa-package"
|
||||
else:
|
||||
raise RuntimeError("Cloud Policy: Unexpected source type, {}".format(source_type))
|
||||
|
||||
for binary in binaries:
|
||||
install_flags.append(flag)
|
||||
install_flags.append("{}/{}".format(binary, source))
|
||||
|
||||
return install_flags
|
||||
|
||||
def _parse_ppas(self, ppas):
|
||||
|
5686
cloud_package_set.json
Normal file
5686
cloud_package_set.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -12,6 +12,7 @@ import sys
|
||||
from types import SimpleNamespace
|
||||
import unittest
|
||||
from unittest.mock import MagicMock, patch
|
||||
import tempfile
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
PROJECT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
@ -51,6 +52,7 @@ class T(unittest.TestCase):
|
||||
def test_run_cloud_tests_state_handling(self, mock_run, mock_xunit, mock_extra):
|
||||
"""Cloud tests should save state and not re-run tests for packages
|
||||
already tested."""
|
||||
self.policy.package_set = {"azure": {"chromium-browser": ["binary1"], "hello": ["binary2"]}}
|
||||
expected_state = {
|
||||
"azure": {
|
||||
"archive": {
|
||||
@ -71,7 +73,7 @@ class T(unittest.TestCase):
|
||||
# Package already tested, no tests should run
|
||||
self.policy.failures = {}
|
||||
self.policy.errors = {}
|
||||
self.policy._run_cloud_tests("chromium-browser", "55.0", "zazzy", ["proposed"], "archive")
|
||||
self.policy._run_cloud_tests(["azure"], "chromium-browser", "55.0", "zazzy", ["proposed"], "archive")
|
||||
self.assertDictEqual(expected_state, self.policy.state)
|
||||
mock_run.assert_not_called()
|
||||
self.assertEqual(len(self.policy.failures), 1)
|
||||
@ -85,7 +87,7 @@ class T(unittest.TestCase):
|
||||
}
|
||||
self.policy.failures = {}
|
||||
self.policy.errors = {}
|
||||
self.policy._run_cloud_tests("hello", "2.10", "zazzy", ["proposed"], "archive")
|
||||
self.policy._run_cloud_tests(["azure"], "hello", "2.10", "zazzy", ["proposed"], "archive")
|
||||
self.assertDictEqual(expected_state, self.policy.state)
|
||||
mock_run.assert_called()
|
||||
self.assertEqual(len(self.policy.failures), 0)
|
||||
@ -99,7 +101,7 @@ class T(unittest.TestCase):
|
||||
}
|
||||
self.policy.failures = {}
|
||||
self.policy.errors = {}
|
||||
self.policy._run_cloud_tests("chromium-browser", "55.1", "zazzy", ["proposed"], "archive")
|
||||
self.policy._run_cloud_tests(["azure"], "chromium-browser", "55.1", "zazzy", ["proposed"], "archive")
|
||||
self.assertDictEqual(expected_state, self.policy.state)
|
||||
self.assertEqual(mock_run.call_count, 2)
|
||||
self.assertEqual(len(self.policy.failures), 0)
|
||||
@ -115,6 +117,7 @@ class T(unittest.TestCase):
|
||||
def test_run_cloud_tests_state_handling_only_errors(self, mock_run, mock_xunit, mock_extra):
|
||||
"""Cloud tests should save state and not re-run tests for packages
|
||||
already tested."""
|
||||
self.policy.package_set = {"azure": {"chromium-browser": ["binary1"]}}
|
||||
start_state = {
|
||||
"azure": {
|
||||
"archive": {
|
||||
@ -133,14 +136,16 @@ class T(unittest.TestCase):
|
||||
self.policy._load_state()
|
||||
|
||||
# Package already tested, but only had errors - rerun
|
||||
self.policy._run_cloud_tests("chromium-browser", "55.0", "zazzy", ["proposed"], "archive")
|
||||
self.policy._run_cloud_tests(["azure"], "chromium-browser", "55.0", "zazzy", ["proposed"], "archive")
|
||||
mock_run.assert_called()
|
||||
|
||||
@patch("britney2.policies.cloud.CloudPolicy._run_cloud_tests")
|
||||
def test_run_cloud_tests_called_for_package_in_manifest(self, mock_run):
|
||||
"""Cloud tests should run for a package in the cloud package set.
|
||||
"""
|
||||
self.policy.package_set = set(["chromium-browser"])
|
||||
self.policy.package_set = {
|
||||
"acloud": {"chromium-browser": []}
|
||||
}
|
||||
self.policy.options.series = "jammy"
|
||||
|
||||
self.policy.apply_src_policy_impl(
|
||||
@ -148,15 +153,16 @@ class T(unittest.TestCase):
|
||||
)
|
||||
|
||||
mock_run.assert_called_once_with(
|
||||
"chromium-browser", "55.0", "jammy", ["proposed"], "archive"
|
||||
["acloud"], "chromium-browser", "55.0", "jammy", ["proposed"], "archive"
|
||||
)
|
||||
|
||||
@patch("britney2.policies.cloud.CloudPolicy._run_cloud_tests")
|
||||
def test_run_cloud_tests_not_called_for_package_not_in_manifest(self, mock_run):
|
||||
"""Cloud tests should not run for packages not in the cloud package set"""
|
||||
|
||||
self.policy.package_set = set(["vim"])
|
||||
self.policy.options.series = "jammy"
|
||||
self.policy.package_set = {
|
||||
"acloud": {"vim": []}
|
||||
}
|
||||
|
||||
verdict = self.policy.apply_src_policy_impl(
|
||||
None, FakeItem, None, FakeSourceData, MagicMock()
|
||||
@ -169,7 +175,9 @@ class T(unittest.TestCase):
|
||||
@patch("britney2.policies.cloud.CloudPolicy._run_cloud_tests")
|
||||
def test_no_tests_run_during_dry_run(self, mock_run, smtp):
|
||||
self.policy = CloudPolicy(self.fake_options, {}, dry_run=True)
|
||||
self.policy.package_set = set(["chromium-browser"])
|
||||
self.policy.package_set = {
|
||||
"acloud": {"chromium-browser": []}
|
||||
}
|
||||
self.policy.options.series = "jammy"
|
||||
self.policy.source = "jammy-proposed"
|
||||
|
||||
@ -187,7 +195,9 @@ class T(unittest.TestCase):
|
||||
self.fake_options.cloud_enable_reporting = "yes"
|
||||
policy = CloudPolicy(self.fake_options, {}, dry_run=False)
|
||||
|
||||
policy.package_set = set(["chromium-browser"])
|
||||
policy.package_set = {
|
||||
"acloud": {"chromium-browser": []}
|
||||
}
|
||||
policy.options.series = "jammy"
|
||||
|
||||
policy.apply_src_policy_impl(
|
||||
@ -204,7 +214,9 @@ class T(unittest.TestCase):
|
||||
self.fake_options.cloud_reporting_enabled = "no"
|
||||
policy = CloudPolicy(self.fake_options, {}, dry_run=False)
|
||||
|
||||
policy.package_set = set(["chromium-browser"])
|
||||
policy.package_set = {
|
||||
"acloud": {"chromium-browser": []}
|
||||
}
|
||||
policy.options.series = "jammy"
|
||||
|
||||
policy.apply_src_policy_impl(
|
||||
@ -302,10 +314,12 @@ class T(unittest.TestCase):
|
||||
"""Ensure the correct flags are returned with PPA sources"""
|
||||
expected_flags = [
|
||||
"--install-ppa-package", "tmux/ppa_url=fingerprint",
|
||||
"--install-ppa-package", "tmux/ppa_url2=fingerprint"
|
||||
"--install-ppa-package", "sed/ppa_url=fingerprint",
|
||||
"--install-ppa-package", "tmux/ppa_url2=fingerprint",
|
||||
"--install-ppa-package", "sed/ppa_url2=fingerprint",
|
||||
]
|
||||
install_flags = self.policy._format_install_flags(
|
||||
"tmux", ["ppa_url=fingerprint", "ppa_url2=fingerprint"], "ppa"
|
||||
["tmux", "sed"], ["ppa_url=fingerprint", "ppa_url2=fingerprint"], "ppa"
|
||||
)
|
||||
|
||||
self.assertListEqual(install_flags, expected_flags)
|
||||
@ -313,14 +327,14 @@ class T(unittest.TestCase):
|
||||
def test_format_install_flags_with_archive(self):
|
||||
"""Ensure the correct flags are returned with archive sources"""
|
||||
expected_flags = ["--install-archive-package", "tmux/proposed"]
|
||||
install_flags = self.policy._format_install_flags("tmux", ["proposed"], "archive")
|
||||
install_flags = self.policy._format_install_flags(["tmux"], ["proposed"], "archive")
|
||||
|
||||
self.assertListEqual(install_flags, expected_flags)
|
||||
|
||||
def test_format_install_flags_with_incorrect_type(self):
|
||||
"""Ensure errors are raised for unknown source types"""
|
||||
|
||||
self.assertRaises(RuntimeError, self.policy._format_install_flags, "tmux", ["a_source"], "something")
|
||||
self.assertRaises(RuntimeError, self.policy._format_install_flags, ["tmux"], ["a_source"], "something")
|
||||
|
||||
def test_parse_ppas(self):
|
||||
"""Ensure correct conversion from Britney format to cloud test format
|
||||
@ -392,6 +406,50 @@ class T(unittest.TestCase):
|
||||
self.policy.failures["FakeCloud"]["extra_info"]["install_source"], "source information"
|
||||
)
|
||||
|
||||
def test_retrieve_cloud_package_set_for_series(self):
|
||||
"""Tests that the package set is retrieved and only the given series is returned.
|
||||
"""
|
||||
with tempfile.NamedTemporaryFile(mode="w", delete=False) as f:
|
||||
raw_package_set = {
|
||||
"acloud": {
|
||||
"focal": {"grep": [], "tmux": []},
|
||||
"jammy": {"grep": [],},
|
||||
},
|
||||
"bcloud": {
|
||||
"focal": {"grep": [], "tmux": [],},
|
||||
"jammy": {"grep": [], "tmux": [],},
|
||||
"kinetic": {},
|
||||
}
|
||||
}
|
||||
json.dump(raw_package_set, f)
|
||||
|
||||
f.seek(0)
|
||||
package_set = self.policy._retrieve_cloud_package_set_for_series(f.name, "focal")
|
||||
expected_set = {
|
||||
"acloud": {"grep": [], "tmux": []},
|
||||
"bcloud": {"grep": [], "tmux": []},
|
||||
}
|
||||
self.assertDictEqual(package_set, expected_set)
|
||||
|
||||
def test_check_if_tests_required(self):
|
||||
"""Test that the package set is correctly parsed.
|
||||
Ensure that a package is found and the cloud is returned correctly.
|
||||
Ensure that only the correct series is checked.
|
||||
"""
|
||||
package_set = {
|
||||
"acloud": {"grep": []},
|
||||
"bcloud": {"grep": [], "tmux": []},
|
||||
}
|
||||
|
||||
clouds = self.policy._check_if_tests_required(package_set, "grep")
|
||||
self.assertListEqual(clouds, ["acloud", "bcloud"])
|
||||
|
||||
clouds = self.policy._check_if_tests_required(package_set, "tmux")
|
||||
self.assertListEqual(clouds, ["bcloud"])
|
||||
|
||||
clouds = self.policy._check_if_tests_required(package_set, "sed")
|
||||
self.assertListEqual(clouds, [])
|
||||
|
||||
def _create_fake_test_result_file(self, num_pass=1, num_err=0, num_fail=0):
|
||||
"""Helper function to generate an xunit test result file.
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user