Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Requires version constraints #517

Merged
merged 11 commits into from
Nov 22, 2024
23 changes: 11 additions & 12 deletions brewtils/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -502,25 +502,24 @@ def get_system_matching_version(self, require, **kwargs):
req = Requirement(require)
require_name = req.name
require_version = req.specifier
systems = self._ez_client.find_systems(name=require_name, **kwargs)
if require_version:
systems = self._ez_client.find_systems(name=require_name, **kwargs)
valid_versions = list(
require_version.filter(
[str(Version(system.version)) for system in systems]
)
)
if valid_versions:
system_candidates = [
system for system in systems if system.version in valid_versions
]
system = system_candidates[0]
for system_candidate in system_candidates:
if Version(system_candidate.version) > Version(system.version):
system = system_candidate
else:
system = self._ez_client.find_unique_system(
name=require_name, filter_latest=True, **kwargs
)
valid_versions = [str(Version(system.version)) for system in systems]

if valid_versions:
system_candidates = [
system for system in systems if system.version in valid_versions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should compare parsed versions to parsed versions:
system for system in systems if str(Version(system.version)) in valid_versions

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

complete

]
system = system_candidates[0]
for system_candidate in system_candidates:
if Version(system_candidate.version) > Version(system.version):
system = system_candidate

return system

Expand Down
10 changes: 10 additions & 0 deletions brewtils/test/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,16 @@ def bg_system_5(system_dict, bg_instance, bg_command, bg_command_2):
return System(**dict_copy)


@pytest.fixture
def bg_system_6(system_dict, bg_instance, bg_command, bg_command_2):
"""A system with a different version."""
dict_copy = copy.deepcopy(system_dict)
dict_copy["version"] = "3.0.0.dev0"
dict_copy["instances"] = [bg_instance]
dict_copy["commands"] = [bg_command, bg_command_2]
return System(**dict_copy)


@pytest.fixture
def child_request_dict(ts_epoch):
"""A child request represented as a dictionary."""
Expand Down
243 changes: 107 additions & 136 deletions test/plugin_test.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
# -*- coding: utf-8 -*-
import copy
import logging
import logging.config
import os
import warnings
from packaging.requirements import InvalidRequirement
from packaging.version import InvalidVersion

import pytest
from mock import ANY, MagicMock, Mock
Expand Down Expand Up @@ -369,6 +372,7 @@ def test_success(
return_value=(admin_processor, request_processor)
)
plugin._ez_client.find_unique_system = Mock(return_value=bg_system)
plugin._ez_client.find_systems = Mock(return_value=[bg_system])

plugin._startup()
assert admin_processor.startup.called is True
Expand All @@ -390,6 +394,7 @@ def test_success_no_ns(
return_value=(admin_processor, request_processor)
)
plugin._ez_client.find_unique_system = Mock(return_value=bg_system)
plugin._ez_client.find_systems = Mock(return_value=[bg_system])

plugin._startup()
assert admin_processor.startup.called is True
Expand Down Expand Up @@ -968,77 +973,77 @@ class TestDependencies(object):
# 2.1.0 bg_system_3
# 2.1.1 bg_system_4
# 3.0.0 bg_system_5
def test_no_specifier(
plugin, bg_system, bg_system_2, bg_system_3, bg_system_4, bg_system_5
):
p = Plugin(bg_host="localhost", system=bg_system)
p._ez_client.find_unique_system.return_value = bg_system_5
# Expect 3.0.0 as latest valid version
# Expect 3.0.0
assert p.get_system_dependency("system").version == bg_system_5.version

def test_equals(
plugin, bg_system, bg_system_2, bg_system_3, bg_system_4, bg_system_5
):
p = Plugin(bg_host="localhost", system=bg_system)
p._ez_client.find_systems.return_value = [
bg_system,
bg_system_2,
bg_system_3,
bg_system_4,
bg_system_5,
]
# Expect 2.1.1 valid
# Expect 2.1.1
assert p.get_system_dependency("system==2.1.0").version == bg_system_3.version

def test_compatible_release(
plugin, bg_system, bg_system_2, bg_system_3, bg_system_4, bg_system_5
):
p = Plugin(bg_host="localhost", system=bg_system)
p._ez_client.find_systems.return_value = [
bg_system,
bg_system_2,
bg_system_3,
bg_system_4,
bg_system_5,
]
# Expect 2.1.0, 2.1.1 valid
# Expect 2.1.1
assert p.get_system_dependency("system~=2.1.0").version == bg_system_4.version

def test_wildcard_minor(
plugin, bg_system, bg_system_2, bg_system_3, bg_system_4, bg_system_5
):
# 3.0.0.dev0 bg_system_6
@pytest.mark.parametrize(
"latest,versions",
[
("1.0.0", ["1.0.0"]),
("2.0.0", ["1.0.0", "2.0.0"]),
("1.2.0", ["1.0.0", "1.2.0"]),
("1.0.0", ["1.0.0", "0.2.1rc1"]),
("1.0.0rc1", ["1.0.0rc1", "0.2.1"]),
("1.0.0rc1", ["1.0.0rc1", "0.2.1rc1"]),
("1.0", ["1.0", "0.2.1"]),
("1.0.0", ["1.0.0rc1", "1.0.0"]),
],
)
def test_determine_latest(client, bg_system, versions, latest):
p = Plugin(bg_host="localhost", system=bg_system)
p._ez_client.find_systems.return_value = [
bg_system,
bg_system_2,
bg_system_3,
bg_system_4,
bg_system_5,
]
# Expect 1.0.0, 2.0.0, 2.1.1, 3.0.0 valid
# Expect 3.0.0
assert p.get_system_dependency("system==2.*").version == bg_system_4.version
system_versions = []
for version in versions:
s = copy.deepcopy(bg_system)
s.version = version
system_versions.append(s)
p._ez_client.find_systems.return_value = system_versions
assert p.get_system_dependency("system").version == latest

def test_wildcard_patch(
plugin, bg_system, bg_system_2, bg_system_3, bg_system_4, bg_system_5
):
@pytest.mark.parametrize(
"latest,versions",
[
("b", ["a", "b"]),
("1.0.0", ["a", "b", "1.0.0"]),
],
)
def test_determine_latest_failures(client, bg_system, versions, latest):
p = Plugin(bg_host="localhost", system=bg_system)
p._ez_client.find_systems.return_value = [
bg_system,
bg_system_2,
bg_system_3,
bg_system_4,
bg_system_5,
]
# Expect 1.0.0, 2.0.0, 2.1.1, 3.0.0 valid
# Expect 3.0.0
assert p.get_system_dependency("system==2.1.*").version == bg_system_4.version
system_versions = []
for version in versions:
s = copy.deepcopy(bg_system)
s.version = version
system_versions.append(s)
p._ez_client.find_systems.return_value = system_versions
with pytest.raises(InvalidVersion):
assert p.get_system_dependency("system").version == latest

def test_excludes(
plugin, bg_system, bg_system_2, bg_system_3, bg_system_4, bg_system_5
@pytest.mark.parametrize(
"version_spec,latest",
[
("system", "3.0.0"), # test no specifier ignores pre-release
("system==3.0.0.dev", "3.0.0.dev0"), # test version parsing
("system==2.1.0", "2.1.0"), # test equals
("system==3", "3.0.0"), # test equals no dev
("system~=2.1.0", "2.1.1"), # test compatible release
("system==2.*", "2.1.1"), # test minor wildcard
("system==2.1.*", "2.1.1"), # test patch wildcard
("system!=2.1.0", "3.0.0"), # test excludes
("system>2.1.0", "3.0.0"), # test greater than
("system>=2.1.0", "3.0.0"), # test greater than or equal
("system<2.1.0", "2.0.0"), # test less than
("system<=2.1.0", "2.1.0"), # test less than or equal
("system<2.0.0,>=1", "1.0.0"), # test range
("system==2.*,<2.1.1,!=2.1.0", "2.0.0"), # test combination
],
)
def test_version_specifier(
plugin,
bg_system,
bg_system_2,
bg_system_3,
bg_system_4,
bg_system_5,
bg_system_6,
version_spec,
latest,
):
p = Plugin(bg_host="localhost", system=bg_system)
p._ez_client.find_systems.return_value = [
Expand All @@ -1047,65 +1052,18 @@ def test_excludes(
bg_system_3,
bg_system_4,
bg_system_5,
bg_system_6,
]
# Expect 1.0.0, 2.0.0, 2.1.1, 3.0.0 valid
# Expect 3.0.0
assert p.get_system_dependency("system!=2.1.0").version == bg_system_5.version

def test_gt(plugin, bg_system, bg_system_2, bg_system_3, bg_system_4, bg_system_5):
p = Plugin(bg_host="localhost", system=bg_system)
p._ez_client.find_systems.return_value = [
bg_system,
bg_system_2,
bg_system_3,
bg_system_4,
bg_system_5,
]
# Expect 2.1.1, 3.0.0 valid
# Expect 3.0.0
assert p.get_system_dependency("system>2.1.0").version == bg_system_5.version

def test_gte(plugin, bg_system, bg_system_2, bg_system_3, bg_system_4, bg_system_5):
p = Plugin(bg_host="localhost", system=bg_system)
p._ez_client.find_systems.return_value = [
bg_system,
bg_system_2,
bg_system_3,
bg_system_4,
bg_system_5,
]
# Expect 2.1.0, 2.1.1, 3.0.0 valid
# Expect 3.0.0
assert p.get_system_dependency("system>=2.1.0").version == bg_system_5.version

def test_lt(plugin, bg_system, bg_system_2, bg_system_3, bg_system_4, bg_system_5):
p = Plugin(bg_host="localhost", system=bg_system)
p._ez_client.find_systems.return_value = [
bg_system,
bg_system_2,
bg_system_3,
bg_system_4,
bg_system_5,
]
# Expect 1.0.0, 2.0.0 valid
# Expect 2.0.0
assert p.get_system_dependency("system<2.1.0").version == bg_system_2.version

def test_lte(plugin, bg_system, bg_system_2, bg_system_3, bg_system_4, bg_system_5):
p = Plugin(bg_host="localhost", system=bg_system)
p._ez_client.find_systems.return_value = [
bg_system,
bg_system_2,
bg_system_3,
bg_system_4,
bg_system_5,
]
# Expect 1.0.0, 2.0.0, 2.1.0 valid
# Expect 2.1.0
assert p.get_system_dependency("system<=2.1.0").version == bg_system_3.version

def test_range(
plugin, bg_system, bg_system_2, bg_system_3, bg_system_4, bg_system_5
assert p.get_system_dependency(version_spec).version == latest

def test_no_match(
plugin,
bg_system,
bg_system_2,
bg_system_3,
bg_system_4,
bg_system_5,
bg_system_6,
):
p = Plugin(bg_host="localhost", system=bg_system)
p._ez_client.find_systems.return_value = [
Expand All @@ -1114,13 +1072,29 @@ def test_range(
bg_system_3,
bg_system_4,
bg_system_5,
bg_system_6,
]
# Expect 1.0.0 valid
# Expect 1.0.0
assert p.get_system_dependency("system<2.0.0,>=1").version == bg_system.version
p._wait = Mock(return_value=None)
with pytest.raises(PluginValidationError):
assert p.get_system_dependency("system==3.0.1.dev0").version

def test_combo(
plugin, bg_system, bg_system_2, bg_system_3, bg_system_4, bg_system_5
@pytest.mark.parametrize(
"version_spec",
[
"system==*",
"system==a",
"system$$3.0.0",
],
)
def test_invalid_requirement(
plugin,
bg_system,
bg_system_2,
bg_system_3,
bg_system_4,
bg_system_5,
bg_system_6,
version_spec,
):
p = Plugin(bg_host="localhost", system=bg_system)
p._ez_client.find_systems.return_value = [
Expand All @@ -1129,10 +1103,7 @@ def test_combo(
bg_system_3,
bg_system_4,
bg_system_5,
bg_system_6,
]
# Expect 1.0.0 valid
# Expect 1.0.0
assert (
p.get_system_dependency("system==2.*,<2.1.1,!=2.1.0").version
== bg_system_2.version
)
with pytest.raises(InvalidRequirement):
p.get_system_dependency(version_spec)
Loading