From 2e38e5a576eba63f46f14bac427a72e2c2e6807d Mon Sep 17 00:00:00 2001 From: Jakub Kadlcik Date: Jan 10 2021 09:13:11 +0000 Subject: frontend, cli: allow excluding chroots when submitting builds Fix RHBZ 1399815 I am implementing the feature for excluding chroots, as described in the RHBZ 1399815, but without the pattern support. That to me is a different issue that should be dealt with at the same time as patterns for the normmal --chroot parameter. --- diff --git a/beaker-tests/Sanity/copr-cli-basic-operations/runtest-exclude-chroots.sh b/beaker-tests/Sanity/copr-cli-basic-operations/runtest-exclude-chroots.sh new file mode 100755 index 0000000..cfe234f --- /dev/null +++ b/beaker-tests/Sanity/copr-cli-basic-operations/runtest-exclude-chroots.sh @@ -0,0 +1,53 @@ +#! /bin/bash +# +# Copyright (c) 2021 Red Hat, Inc. +# +# This program is free software: you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation, either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied +# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +# PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see http://www.gnu.org/licenses/. + + +# Include Beaker environment +. /usr/bin/rhts-environment.sh || exit 1 +. /usr/share/beakerlib/beakerlib.sh || exit 1 + +# Load config settings +HERE=$(dirname "$(realpath "$0")") +source "$HERE/config" +source "$HERE/helpers" + + +rlJournalStart + rlPhaseStartSetup + rlAssertRpm "copr-cli" + rlAssertRpm "jq" + rlAssertExists ~/.config/copr + rlPhaseEnd + + rlPhaseStartTest + OUTPUT=`mktemp` + rlRun "copr-cli create --chroot fedora-$FEDORA_VERSION-i386 --chroot fedora-$FEDORA_VERSION-x86_64 --chroot fedora-$FEDORA_VERSION-aarch64 --chroot fedora-rawhide-i386 --chroot fedora-rawhide-x86_64 --chroot fedora-rawhide-aarch64 ${NAME_PREFIX}ExcludeChroots" + + rlRun -s "copr-cli build --nowait --exclude-chroot fedora-$FEDORA_VERSION-aarch64 --exclude-chroot fedora-rawhide-x86_64 ${NAME_PREFIX}ExcludeChroots ${HELLO}" + rlRun "parse_build_id" + rlRun "curl $FRONTEND_URL/api_3/build/$BUILD_ID |jq .chroots > $OUTPUT" + rlRun "cat $OUTPUT |grep fedora-32-x86_64" + rlRun "cat $OUTPUT |grep fedora-$FEDORA_VERSION-aarch64" 1 + rlAssertEquals "Make sure the correct number of chroots is enabled" "`jq length $OUTPUT`" 4 + rlPhaseEnd + + rlPhaseStartCleanup + rlRun "copr-cli cancel $BUILD_ID" + rlRun "copr-cli delete ${NAME_PREFIX}ExcludeChroots" + rlPhaseEnd +rlJournalPrintText +rlJournalEnd diff --git a/cli/copr_cli/main.py b/cli/copr_cli/main.py index c1badcc..fe98418 100644 --- a/cli/copr_cli/main.py +++ b/cli/copr_cli/main.py @@ -114,7 +114,7 @@ def buildopts_from_args(args, progress_callback): "background": args.background, "progress_callback": progress_callback, } - for opt in ["bootstrap", "after_build_id", "with_build_id", "isolation"]: + for opt in ["exclude_chroots", "bootstrap", "after_build_id", "with_build_id", "isolation"]: value = getattr(args, opt) if value is not None: buildopts[opt] = value @@ -1157,6 +1157,15 @@ def setup_parser(): help="Don't wait for build") parser_build_parent.add_argument("-r", "--chroot", dest="chroots", action="append", help="If you don't need this build for all the project's chroots. You can use it several times for each chroot you need.") + + parser_build_parent.add_argument( + "--exclude-chroot", + dest="exclude_chroots", + action="append", + help=("If you don't need this build for all the project's chroots." + "You can use it several times for each chroot you don't need.") + ) + parser_build_parent.add_argument("--background", dest="background", action="store_true", default=False, help="Mark the build as a background job. It will have lesser priority than regular builds.") parser_build_parent.add_argument("--isolation", choices=["simple", "nspawn", "default"], default="unchanged", diff --git a/cli/man/copr-cli.1.asciidoc b/cli/man/copr-cli.1.asciidoc index eb08eed..5ad0ad1 100644 --- a/cli/man/copr-cli.1.asciidoc +++ b/cli/man/copr-cli.1.asciidoc @@ -207,6 +207,10 @@ usage: copr-cli build [-h] [-r, --chroot CHROOTS] [--memory MEMORY] [--timeout T -r, --chroot:: If you don't need this build for all the project's chroots. You can use it several times for each chroot you need. +--exclude-chroot:: +If you don't need this build for all the project's chroots. You can use it +several times for each chroot you don't need. + --memory:: Override memory for this build. This is actually not used and it have no effect. diff --git a/frontend/coprs_frontend/coprs/forms.py b/frontend/coprs_frontend/coprs/forms.py index fb6f820..fabae1d 100644 --- a/frontend/coprs_frontend/coprs/forms.py +++ b/frontend/coprs_frontend/coprs/forms.py @@ -1096,7 +1096,13 @@ def _get_build_form(active_chroots, form, package=None): class F(form): @property def selected_chroots(self): - return self.chroots.data + chroots = self.chroots.data or [] + if self.exclude_chroots.data: + chroots = set(chroots or self.chroots_list) + chroots -= set(self.exclude_chroots.data) + return list(chroots) + return chroots + F.timeout = wtforms.IntegerField( "Timeout", @@ -1133,6 +1139,11 @@ def _get_build_form(active_chroots, form, package=None): choices=[(ch, ch) for ch in F.chroots_list], default=[ch for ch in F.chroots_list if ch in package_chroots]) + F.exclude_chroots = MultiCheckboxField( + "Exclude Chroots", + choices=[(ch, ch) for ch in F.chroots_list], + default=[]) + F.after_build_id = wtforms.IntegerField( "Batch-build after", description=( diff --git a/frontend/coprs_frontend/coprs/views/apiv3_ns/apiv3_builds.py b/frontend/coprs_frontend/coprs/views/apiv3_ns/apiv3_builds.py index 6f55127..8337c3a 100644 --- a/frontend/coprs_frontend/coprs/views/apiv3_ns/apiv3_builds.py +++ b/frontend/coprs_frontend/coprs/views/apiv3_ns/apiv3_builds.py @@ -128,7 +128,7 @@ def cancel_build(build_id): @api_login_required def create_from_url(): copr = get_copr() - data = get_form_compatible_data(preserve=["chroots"]) + data = get_form_compatible_data(preserve=["chroots", "exclude_chroots"]) form = forms.BuildFormUrlFactory(copr.active_chroots)(data, meta={'csrf': False}) def create_new_build(options): @@ -147,7 +147,7 @@ def create_from_url(): @file_upload() def create_from_upload(): copr = get_copr() - data = get_form_compatible_data(preserve=["chroots"]) + data = get_form_compatible_data(preserve=["chroots", "exclude_chroots"]) form = forms.BuildFormUploadFactory(copr.active_chroots)(data, meta={'csrf': False}) def create_new_build(options): @@ -164,7 +164,7 @@ def create_from_upload(): @api_login_required def create_from_scm(): copr = get_copr() - data = rename_fields(get_form_compatible_data(preserve=["chroots"])) + data = rename_fields(get_form_compatible_data(preserve=["chroots", "exclude_chroots"])) form = forms.BuildFormScmFactory(copr.active_chroots)(data, meta={'csrf': False}) def create_new_build(options): @@ -188,7 +188,7 @@ def create_from_distgit(): route for v3.proxies.create_from_distgit() call """ copr = get_copr() - data = rename_fields(get_form_compatible_data(preserve=["chroots"])) + data = rename_fields(get_form_compatible_data(preserve=["chroots", "exclude_chroots"])) # pylint: disable=not-callable form = forms.BuildFormDistGitSimpleFactory(copr.active_chroots)(data, meta={'csrf': False}) @@ -232,7 +232,7 @@ def create_from_pypi(): @api_login_required def create_from_rubygems(): copr = get_copr() - data = get_form_compatible_data(preserve=["chroots"]) + data = get_form_compatible_data(preserve=["chroots", "exclude_chroots"]) form = forms.BuildFormRubyGemsFactory(copr.active_chroots)(data, meta={'csrf': False}) def create_new_build(options): @@ -249,7 +249,7 @@ def create_from_rubygems(): @api_login_required def create_from_custom(): copr = get_copr() - data = get_form_compatible_data(preserve=["chroots"]) + data = get_form_compatible_data(preserve=["chroots", "exclude_chroots"]) form = forms.BuildFormCustomFactory(copr.active_chroots)(data, meta={'csrf': False}) def create_new_build(options): diff --git a/frontend/coprs_frontend/coprs/views/apiv3_ns/apiv3_packages.py b/frontend/coprs_frontend/coprs/views/apiv3_ns/apiv3_packages.py index 4dac67d..efed681 100644 --- a/frontend/coprs_frontend/coprs/views/apiv3_ns/apiv3_packages.py +++ b/frontend/coprs_frontend/coprs/views/apiv3_ns/apiv3_packages.py @@ -157,7 +157,8 @@ def package_reset(): @api_login_required def package_build(): copr = get_copr() - data = rename_fields(get_form_compatible_data(preserve=["python_versions"])) + data = rename_fields(get_form_compatible_data( + preserve=["python_versions", "chroots", "exclude_chroots"])) form = forms.RebuildPackageFactory.create_form_cls(copr.active_chroots)(data, meta={'csrf': False}) try: package = PackagesLogic.get(copr.main_dir.id, form.package_name.data)[0] diff --git a/frontend/coprs_frontend/tests/test_apiv3/test_builds.py b/frontend/coprs_frontend/tests/test_apiv3/test_builds.py index 04f5565..fea05f3 100644 --- a/frontend/coprs_frontend/tests/test_apiv3/test_builds.py +++ b/frontend/coprs_frontend/tests/test_apiv3/test_builds.py @@ -190,6 +190,28 @@ class TestAPIv3Builds(CoprsTestCase): assert response2.json["ownername"] == "user2" assert response2.json["projectname"] == "foocopr" + @pytest.mark.usefixtures("f_users", "f_users_api", "f_coprs", + "f_mock_chroots", "f_other_distgit", "f_db") + @pytest.mark.parametrize("case", CASES) + @pytest.mark.parametrize("exclude_chroots", [[], ["fedora-17-x86_64"]]) + def test_v3_builds_exclude_chroots(self, exclude_chroots, case): + source_type_text, data, source_json, _ = case + form_data = copy.deepcopy(data) + form_data.update({"exclude_chroots": exclude_chroots}) + + endpoint = "/api_3/build/create/distgit" + user = self.models.User.query.filter_by(username="user2").first() + r = self.post_api3_with_auth(endpoint, form_data, user) + assert r.status_code == 200 + build = self.models.Build.query.first() + + if not exclude_chroots: + assert not build.chroots + return + + expected = {ch.name for ch in build.copr.active_chroots} + expected -= set(exclude_chroots) + assert {ch.name for ch in build.chroots} == expected class TestWebUIBuilds(CoprsTestCase):