From 609a23511d537fc254825afae8da89200e801235 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hunor=20Csomort=C3=A1ni?= Date: Mon, 11 Mar 2019 12:10:51 +0100 Subject: [PATCH] Test fetching from Koji by NVR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Hunor Csomortáni --- tests/integration/README.md | 6 + tests/integration/constants.py | 2 + tests/integration/push_nvr/push_nvr_test.py | 173 ++++++++++++++++++++ tests/integration/utils.py | 33 ++++ 4 files changed, 214 insertions(+) create mode 100644 tests/integration/push_nvr/push_nvr_test.py diff --git a/tests/integration/README.md b/tests/integration/README.md index 614e898..ff989b5 100644 --- a/tests/integration/README.md +++ b/tests/integration/README.md @@ -25,6 +25,12 @@ For example: `https://quay.io/api/v1`. `OMPS_INT_TEST_QUAY_PASSWD`: Password to authenticate with Quay. +`OMPS_INT_TEST_KOJI_NVRS`: A comma-separated list of Koji build NVRs, in the +following order: a build with valid operator artifacts, a build with invalid +operator artifacts, a build which is not an operator container build. Used to +test fetching operator artifacts from Koji. +For example: 'valid-operator-container-1.0.0-1,invalid-operator-container-1.0.0-1,etcd-container-1.0.0-1' + All the variables above could be set using [direnv](https://direnv.net/), when one navigates to the OMPS repo. See `.envrc.example` for a starting point. diff --git a/tests/integration/constants.py b/tests/integration/constants.py index f462bfd..8393c41 100644 --- a/tests/integration/constants.py +++ b/tests/integration/constants.py @@ -8,3 +8,5 @@ TEST_NAMESPACE = os.getenv('OMPS_INT_TEST_OMPS_ORG') TEST_PACKAGE = os.getenv('OMPS_INT_TEST_PACKAGE', default='int-test') +TEST_VALID_NVR, TEST_INVALID_NVR, TEST_NOT_AN_OPERATOR = os.getenv( + 'OMPS_INT_TEST_KOJI_NVRS', '').split(',') diff --git a/tests/integration/push_nvr/push_nvr_test.py b/tests/integration/push_nvr/push_nvr_test.py new file mode 100644 index 0000000..f28f8b0 --- /dev/null +++ b/tests/integration/push_nvr/push_nvr_test.py @@ -0,0 +1,173 @@ +# +# Copyright (C) 2019 Red Hat, Inc +# see the LICENSE file for license +# + +import shutil +import requests +from tests.integration.constants import ( + TEST_NAMESPACE, + TEST_PACKAGE, + TEST_VALID_NVR, + TEST_INVALID_NVR, + TEST_NOT_AN_OPERATOR) + + +def test_invalid_zip(omps, quay, tmp_path): + """ + When fetching an NVR from Koji, + and the archive attached to the build has an invalid bundle, + then fetching the NVR fails. + """ + nvr = TEST_INVALID_NVR + response = omps.fetch_nvr(organization=TEST_NAMESPACE, + repo=TEST_PACKAGE, nvr=nvr) + + assert response.status_code == requests.codes.internal_server_error + assert response.json()['error'] == 'QuayCourierError' + assert 'bundle is invalid' in response.json()['message'] + + +def test_not_an_operator(omps, quay, tmp_path): + """ + When fetching an NVR from Koji, + and the container image referenced by the NVR is not an operator, + then fetching the NVR fails. + """ + nvr = TEST_NOT_AN_OPERATOR + response = omps.fetch_nvr(organization=TEST_NAMESPACE, + repo=TEST_PACKAGE, nvr=nvr) + + assert response.status_code == requests.codes.bad_request + assert response.json()['error'] == 'KojiNotAnOperatorImage' + assert 'Not an operator image' in response.json()['message'] + + +def test_nvr_not_found(omps, quay, tmp_path): + """ + When fetching an NVR from Koji, + and no build exists for that NVR in Koji, + then fetching the NVR fails. + """ + nvr = 'no-such-operator-container-image-1.0.0-111' + response = omps.fetch_nvr(organization=TEST_NAMESPACE, + repo=TEST_PACKAGE, nvr=nvr) + + assert response.status_code == requests.codes.not_found + assert response.json()['error'] == 'KojiNVRBuildNotFound' + assert 'NVR not found' in response.json()['message'] + + +def test_valid_zip_default_version(omps, quay, tmp_path): + """ + When fetching an NVR from Koji, + and it's going to be the first release in the package, + and there is no version specified, + then the release gets the default version number. + """ + nvr = TEST_VALID_NVR + quay.clean_up_package(TEST_NAMESPACE, TEST_PACKAGE) + + response = omps.fetch_nvr(organization=TEST_NAMESPACE, + repo=TEST_PACKAGE, nvr=nvr) + + assert response.status_code == requests.codes.ok + assert response.json() == { + 'extracted_files': [ + 'crd.yaml', + 'csv.yaml', + 'packages.yaml' + ], + 'nvr': TEST_VALID_NVR, + 'organization': TEST_NAMESPACE, + 'repo': TEST_PACKAGE, + 'version': '1.0.0', + } + assert quay.get_release(TEST_NAMESPACE, TEST_PACKAGE, '1.0.0') + + +def test_valid_zip_defined_version(omps, quay, tmp_path): + """ + When fetching an NVR from Koji, + and there is a version specified, + then the release gets the version number specified. + """ + nvr = TEST_VALID_NVR + version = '6.5.4' + response = omps.fetch_nvr(organization=TEST_NAMESPACE, + repo=TEST_PACKAGE, nvr=nvr, version=version) + + assert response.status_code == requests.codes.ok + assert response.json() == { + 'extracted_files': [ + 'crd.yaml', + 'csv.yaml', + 'packages.yaml' + ], + 'nvr': nvr, + 'organization': TEST_NAMESPACE, + 'repo': TEST_PACKAGE, + 'version': version, + } + assert quay.get_release(TEST_NAMESPACE, TEST_PACKAGE, version) + + +def test_version_exists(omps, quay, tmp_path): + """ + When fetching an NVR from Koji, + and the request specifies a version, + and a release with the same version already exists, + then fetching the NVR fails. + """ + nvr = TEST_VALID_NVR + version = '8.0.1' + + archive = shutil.make_archive(tmp_path / 'archive', 'zip', + 'tests/integration/push_archive/artifacts/') + + if not quay.get_release(TEST_NAMESPACE, TEST_PACKAGE, version): + omps.upload(organization=TEST_NAMESPACE, + repo=TEST_PACKAGE, version=version, archive=archive) + + response = omps.fetch_nvr(organization=TEST_NAMESPACE, + repo=TEST_PACKAGE, nvr=nvr, version=version) + + assert response.status_code == requests.codes.server_error + assert response.json()['error'] == 'QuayCourierError' + assert 'Failed to push' in response.json()['message'] + + +def test_increment_version(omps, quay, tmp_path): + """ + When fetching an NVR from Koji, + and the request specifies no version for the release to be created, + and there are already some releases for the package, + then the major bit of the semantically highest version is incremented, + and used as the version of the new release. + """ + nvr = TEST_VALID_NVR + version = '7.6.1' + next_version = '8.0.0' + + quay.clean_up_package(TEST_NAMESPACE, TEST_PACKAGE) + archive = shutil.make_archive(tmp_path / 'archive', 'zip', + 'tests/integration/push_archive/artifacts/') + omps.upload(organization=TEST_NAMESPACE, + repo=TEST_PACKAGE, version=version, archive=archive) + + response = omps.fetch_nvr(organization=TEST_NAMESPACE, + repo=TEST_PACKAGE, nvr=nvr) + + assert response.status_code == requests.codes.ok + assert response.json() == { + 'extracted_files': [ + 'crd.yaml', + 'csv.yaml', + 'packages.yaml' + ], + 'nvr': nvr, + 'organization': TEST_NAMESPACE, + 'repo': TEST_PACKAGE, + 'version': next_version, + } + assert quay.get_release(TEST_NAMESPACE, TEST_PACKAGE, next_version) diff --git a/tests/integration/utils.py b/tests/integration/utils.py index 0794d20..3ee3dab 100644 --- a/tests/integration/utils.py +++ b/tests/integration/utils.py @@ -68,6 +68,24 @@ def delete(self, organization, repo, version=None): return requests.delete(url, headers=self._headers) + def fetch_nvr(self, organization, repo, nvr, version=None): + """ + + Args: + + Returns: + + Raises: + """ + url = '{api}/{org}/{repo}/koji/{nvr}{version}'.format( + api=self._api_url, + org=organization, + repo=repo, + nvr=nvr, + version='' if not version else '/' + version) + + return requests.post(url, headers=self._headers) + class QuayAppRegistry(object): """Quay App Registry. @@ -255,3 +273,18 @@ def clean_up(self, namespace, package_prefix): package_prefix=package_prefix) if package['name'].startswith(name_prefix): self.delete_releases(package['name'], package['releases']) + + def clean_up_package(self, namespace, package): + """Delete all versions of a package + + Args: + namespace: Namespace. + package: Package in the namespace. + + Returns: None + + Raises: None. + """ + releases = [release['release'] for release in + self.get_releases(namespace, package)] + self.delete_releases('/'.join([namespace, package]), releases)