debian-koji-osbuild/test/unit/test_cli.py
Christian Kellner 591a55aad5 plugins: add support for customizations
The Cloud API supports passing in a variety of image customizations,
like e.g. extra packages or pre-defining users.

Add a new command line option to the client `--customizations` which
takes a path to a JSON file which contains the customziations; they
will be passed via the existing `opts` argument to the hub.

Add support for `customizations` to the `opts`/`options` arguments
to the hub plugin. No validation to the object is done. Instead we
rely in Composer for the validation of the content.

Add support for `customizations` the image `ComposeRequest` in the
builder plugin. All specified values are just passed through to
composer as-is.

Add tests for the respective plugins.
2022-05-03 12:28:05 +02:00

345 lines
10 KiB
Python

#
# koji command line interface plugin unit tests
#
import contextlib
import io
import json
import os
import tempfile
import koji
import koji_cli.lib as kl
from flexmock import flexmock
from plugintest import PluginTest
@PluginTest.load_plugin("cli")
class TestCliPlugin(PluginTest):
@staticmethod
def mock_koji_lib(*, bg=False, task_result=0):
kojilib = flexmock(OptionParser=kl.OptionParser)
kojilib.should_receive("get_usage_str").and_return("usage")
kojilib.should_receive("activate_session").once()
kojilib.should_receive("_running_in_bg").and_return(bg)
kojilib.should_receive("watch_tasks").and_return(task_result)
return kojilib
@staticmethod
def mock_options(*, quiet=False):
options = flexmock(
quiet=quiet,
weburl="http://osbuild.org/"
)
return options
@staticmethod
def mock_session_add_valid_tag(session):
build_target = {
"build_tag": 23,
"build_tag_name": "target-build",
"dest_tag": 42,
"dest_tag_name": "target-dest"
}
tag_info = {
"id": build_target["dest_tag"],
"name": build_target["dest_tag_name"],
"locked": False
}
session.should_receive("getBuildTarget") \
.with_args("target") \
.and_return(build_target) \
.once()
session.should_receive("getTag") \
.with_args(build_target["dest_tag"]) \
.and_return(tag_info) \
.once()
return session
def test_basic_invocation(self):
# check we get the right amount of arguments
# i.e. we are missing the architecture here
argv = ["name", "version", "distro", "target"]
f = io.StringIO()
with self.assertRaises(SystemExit) as cm, \
contextlib.redirect_stderr(f):
self.plugin.handle_osbuild_image(None, None, argv)
self.assertEqual(cm.exception.code, 2)
self.assertIn("osbuild-image", f.getvalue())
f.close()
# check one successful invocation
argv = [
# the required positional arguments
"name", "version", "distro", "target", "arch1",
# optional keyword arguments
"--repo", "https://first.repo",
"--repo", "https://second.repo",
"--release", "20200202.n2",
"--skip-tag"
]
expected_args = ["name", "version", "distro",
['guest-image'], # the default image type
"target",
['arch1']]
expected_opts = {
"release": "20200202.n2",
"repo": ["https://first.repo", "https://second.repo"],
"skip_tag": True
}
task_result = {"compose_id": "42", "build_id": 23}
task_id = 1
koji_lib = self.mock_koji_lib()
options = self.mock_options()
session = flexmock()
self.mock_session_add_valid_tag(session)
session.should_receive("osbuildImage") \
.with_args(*expected_args, opts=expected_opts) \
.and_return(task_id) \
.once()
session.should_receive("logout") \
.with_args() \
.once()
session.should_receive("getTaskResult") \
.with_args(task_id) \
.and_return(task_result) \
.once()
setattr(self.plugin, "kl", koji_lib)
r = self.plugin.handle_osbuild_image(options, session, argv)
self.assertEqual(r, 0)
def test_customizations_options(self):
with tempfile.TemporaryDirectory() as tmpdir:
customizations = {
"packages": [
"emacs"
]
}
path = os.path.join(tmpdir, "customizations.json")
with open(path, "w", encoding="utf-8") as f:
json.dump(customizations, f)
argv = [
# the required positional arguments
"name", "version", "distro", "target", "arch1",
# optional keyword arguments
"--repo", "https://first.repo",
"--repo", "https://second.repo",
"--release", "20200202.n2",
"--customizations", path
]
expected_args = ["name", "version", "distro",
['guest-image'], # the default image type
"target",
['arch1']]
expected_opts = {
"release": "20200202.n2",
"repo": ["https://first.repo", "https://second.repo"],
"customizations": customizations
}
task_result = {"compose_id": "42", "build_id": 23}
task_id = 1
koji_lib = self.mock_koji_lib()
options = self.mock_options()
session = flexmock()
self.mock_session_add_valid_tag(session)
session.should_receive("osbuildImage") \
.with_args(*expected_args, opts=expected_opts) \
.and_return(task_id) \
.once()
session.should_receive("logout") \
.with_args() \
.once()
session.should_receive("getTaskResult") \
.with_args(task_id) \
.and_return(task_result) \
.once()
setattr(self.plugin, "kl", koji_lib)
r = self.plugin.handle_osbuild_image(options, session, argv)
self.assertEqual(r, 0)
def test_ostree_options(self):
# Check we properly handle ostree specific options
argv = [
# the required positional arguments
"name", "version", "distro", "target", "arch1",
# optional keyword arguments
"--repo", "https://first.repo",
"--repo", "https://second.repo",
"--release", "20200202.n2",
"--ostree-parent", "ostree/$arch/staging",
"--ostree-ref", "ostree/$arch/production",
"--ostree-url", "https://osbuild.org/repo",
]
expected_args = ["name", "version", "distro",
['guest-image'], # the default image type
"target",
['arch1']]
expected_opts = {
"release": "20200202.n2",
"repo": ["https://first.repo", "https://second.repo"],
"ostree": {
"parent": "ostree/$arch/staging",
"ref": "ostree/$arch/production",
"url": "https://osbuild.org/repo",
}
}
task_result = {"compose_id": "42", "build_id": 23}
task_id = 1
koji_lib = self.mock_koji_lib()
options = self.mock_options()
session = flexmock()
self.mock_session_add_valid_tag(session)
session.should_receive("osbuildImage") \
.with_args(*expected_args, opts=expected_opts) \
.and_return(task_id) \
.once()
session.should_receive("logout") \
.with_args() \
.once()
session.should_receive("getTaskResult") \
.with_args(task_id) \
.and_return(task_result) \
.once()
setattr(self.plugin, "kl", koji_lib)
r = self.plugin.handle_osbuild_image(options, session, argv)
self.assertEqual(r, 0)
def test_repo_package_sets(self):
# Check we properly handle ostree specific options
argv = [
# the required positional arguments
"name", "version", "distro", "target", "arch1",
# optional keyword arguments
"--repo", "https://first.repo",
"--repo-package-sets", "a; b; c",
"--repo-package-sets", "d",
"--repo", "https://second.repo",
"--repo-package-sets", "alpha",
"--repo", "https://third.repo", # NB: no `--repo-package-set`
"--release", "20200202.n2",
]
expected_args = ["name", "version", "distro",
['guest-image'], # the default image type
"target",
['arch1']]
expected_opts = {
"release": "20200202.n2",
"repo": [
{"baseurl": "https://first.repo",
"package_sets": ["a", "b", "c", "d"]},
{"baseurl": "https://second.repo",
"package_sets": ["alpha"]},
{"baseurl": "https://third.repo"}
],
}
task_result = {"compose_id": "42", "build_id": 23}
task_id = 1
koji_lib = self.mock_koji_lib()
options = self.mock_options()
session = flexmock()
self.mock_session_add_valid_tag(session)
session.should_receive("osbuildImage") \
.with_args(*expected_args, opts=expected_opts) \
.and_return(task_id) \
.once()
session.should_receive("logout") \
.with_args() \
.once()
session.should_receive("getTaskResult") \
.with_args(task_id) \
.and_return(task_result) \
.once()
setattr(self.plugin, "kl", koji_lib)
r = self.plugin.handle_osbuild_image(options, session, argv)
self.assertEqual(r, 0)
def test_target_check(self):
# unknown build target
session = flexmock()
session.should_receive("getBuildTarget") \
.with_args("target") \
.and_return(None) \
.once()
argv = ["name", "version", "distro", "target", "arch1"]
with self.assertRaises(koji.GenericError):
self.plugin.handle_osbuild_image(None, session, argv)
# unknown destination tag
build_target = {
"build_tag": 23,
"build_tag_name": "target-build",
"dest_tag": 42,
"dest_tag_name": "target-dest" # missing!
}
session = flexmock()
session.should_receive("getBuildTarget") \
.with_args("target") \
.and_return(build_target) \
.once()
session.should_receive("getTag") \
.with_args(build_target["dest_tag"]) \
.and_return(None) \
.once()
argv = ["name", "version", "distro", "target", "arch1"]
with self.assertRaises(koji.GenericError):
self.plugin.handle_osbuild_image(None, session, argv)