1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2026-04-22 19:59:07 +00:00

Prepare main for 13.0.0 (#11834)

* Bump version to 13.0.0.

* Remove deprecated modules and plugins.

* Remove deprecated module utils.

* Remove leftovers.

* Remove mode=compatibility.

* Change default of is_pre740 from true to false.

* Change default of force_defaults from true to false.

* Remove support for ubuntu_legacy mechanism.

* Remove cpanm compatibility tests.
This commit is contained in:
Felix Fontein 2026-04-20 12:35:43 +02:00 committed by GitHub
parent 7ce198f0e7
commit 72c13c85ad
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
62 changed files with 104 additions and 13052 deletions

View file

@ -1,51 +0,0 @@
# Copyright (c) Ansible project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
import random
import unittest
from ansible_collections.community.general.plugins.module_utils.cloud import _exponential_backoff, _full_jitter_backoff
class ExponentialBackoffStrategyTestCase(unittest.TestCase):
def test_no_retries(self):
strategy = _exponential_backoff(retries=0)
result = list(strategy())
self.assertEqual(result, [], "list should be empty")
def test_exponential_backoff(self):
strategy = _exponential_backoff(retries=5, delay=1, backoff=2)
result = list(strategy())
self.assertEqual(result, [1, 2, 4, 8, 16])
def test_max_delay(self):
strategy = _exponential_backoff(retries=7, delay=1, backoff=2, max_delay=60)
result = list(strategy())
self.assertEqual(result, [1, 2, 4, 8, 16, 32, 60])
def test_max_delay_none(self):
strategy = _exponential_backoff(retries=7, delay=1, backoff=2, max_delay=None)
result = list(strategy())
self.assertEqual(result, [1, 2, 4, 8, 16, 32, 64])
class FullJitterBackoffStrategyTestCase(unittest.TestCase):
def test_no_retries(self):
strategy = _full_jitter_backoff(retries=0)
result = list(strategy())
self.assertEqual(result, [], "list should be empty")
def test_full_jitter(self):
retries = 5
seed = 1
r = random.Random(seed)
expected = [r.randint(0, 2**i) for i in range(0, retries)]
strategy = _full_jitter_backoff(retries=retries, delay=1, _random=random.Random(seed))
result = list(strategy())
self.assertEqual(result, expected)

View file

@ -1,139 +0,0 @@
# Copyright (c) Ansible project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
import pytest
from ansible_collections.community.general.plugins.module_utils.database import (
SQLParseError,
is_input_dangerous,
pg_quote_identifier,
)
# These are all valid strings
# The results are based on interpreting the identifier as a table name
VALID = {
# User quoted
'"public.table"': '"public.table"',
'"public"."table"': '"public"."table"',
'"schema test"."table test"': '"schema test"."table test"',
# We quote part
"public.table": '"public"."table"',
'"public".table': '"public"."table"',
'public."table"': '"public"."table"',
"schema test.table test": '"schema test"."table test"',
'"schema test".table test': '"schema test"."table test"',
'schema test."table test"': '"schema test"."table test"',
# Embedded double quotes
'table "test"': '"table ""test"""',
'public."table ""test"""': '"public"."table ""test"""',
'public.table "test"': '"public"."table ""test"""',
'schema "test".table': '"schema ""test"""."table"',
'"schema ""test""".table': '"schema ""test"""."table"',
'"""wat"""."""test"""': '"""wat"""."""test"""',
# Sigh, handle these as well:
'"no end quote': '"""no end quote"',
'schema."table': '"schema"."""table"',
'"schema.table': '"""schema"."table"',
'schema."table.something': '"schema"."""table"."something"',
# Embedded dots
'"schema.test"."table.test"': '"schema.test"."table.test"',
'"schema.".table': '"schema."."table"',
'"schema."."table"': '"schema."."table"',
'schema.".table"': '"schema".".table"',
'"schema".".table"': '"schema".".table"',
'"schema.".".table"': '"schema.".".table"',
# These are valid but maybe not what the user intended
'."table"': '".""table"""',
"table.": '"table."',
}
INVALID = {
("test.too.many.dots", "table"): "PostgreSQL does not support table with more than 3 dots",
('"test.too".many.dots', "database"): "PostgreSQL does not support database with more than 1 dots",
('test.too."many.dots"', "database"): "PostgreSQL does not support database with more than 1 dots",
('"test"."too"."many"."dots"', "database"): "PostgreSQL does not support database with more than 1 dots",
('"test"."too"."many"."dots"', "schema"): "PostgreSQL does not support schema with more than 2 dots",
('"test"."too"."many"."dots"', "table"): "PostgreSQL does not support table with more than 3 dots",
('"test"."too"."many"."dots"."for"."column"', "column"): "PostgreSQL does not support column with more than 4 dots",
('"table "invalid" double quote"', "table"): "User escaped identifiers must escape extra quotes",
('"schema "invalid"""."table "invalid"', "table"): "User escaped identifiers must escape extra quotes",
('"schema."table"', "table"): "User escaped identifiers must escape extra quotes",
('"schema".', "table"): "Identifier name unspecified or unquoted trailing dot",
}
HOW_MANY_DOTS = (
("role", "role", '"role"', "PostgreSQL does not support role with more than 1 dots"),
("db", "database", '"db"', "PostgreSQL does not support database with more than 1 dots"),
("db.schema", "schema", '"db"."schema"', "PostgreSQL does not support schema with more than 2 dots"),
("db.schema.table", "table", '"db"."schema"."table"', "PostgreSQL does not support table with more than 3 dots"),
(
"db.schema.table.column",
"column",
'"db"."schema"."table"."column"',
"PostgreSQL does not support column with more than 4 dots",
),
)
VALID_QUOTES = ((test, VALID[test]) for test in sorted(VALID))
INVALID_QUOTES = ((test[0], test[1], INVALID[test]) for test in sorted(INVALID))
IS_STRINGS_DANGEROUS = (
("", False),
(" ", False),
("alternative database", False),
("backup of TRUNCATED table", False),
("bob.dropper", False),
("d'artagnan", False),
("user_with_select_update_truncate_right", False),
(";DROP DATABASE fluffy_pets_photos", True),
(";drop DATABASE fluffy_pets_photos", True),
("; TRUNCATE TABLE his_valuable_table", True),
("; truncate TABLE his_valuable_table", True),
("'--", True),
('"--', True),
("' union select username, password from admin_credentials", True),
("' UNION SELECT username, password from admin_credentials", True),
("' intersect select", True),
("' INTERSECT select", True),
("' except select", True),
("' EXCEPT select", True),
(";ALTER TABLE prices", True),
(";alter table prices", True),
("; UPDATE products SET price = '0'", True),
(";update products SET price = '0'", True),
("; DELETE FROM products", True),
("; delete FROM products", True),
("; SELECT * FROM products", True),
(" ; select * from products", True),
)
@pytest.mark.parametrize("identifier, quoted_identifier", VALID_QUOTES)
def test_valid_quotes(identifier, quoted_identifier):
assert pg_quote_identifier(identifier, "table") == quoted_identifier
@pytest.mark.parametrize("identifier, id_type, msg", INVALID_QUOTES)
def test_invalid_quotes(identifier, id_type, msg):
with pytest.raises(SQLParseError) as ex:
pg_quote_identifier(identifier, id_type)
ex.match(msg)
@pytest.mark.parametrize("identifier, id_type, quoted_identifier, msg", HOW_MANY_DOTS)
def test_how_many_dots(identifier, id_type, quoted_identifier, msg):
assert pg_quote_identifier(identifier, id_type) == quoted_identifier
with pytest.raises(SQLParseError) as ex:
pg_quote_identifier(f"{identifier}.more", id_type)
ex.match(msg)
@pytest.mark.parametrize("string, result", IS_STRINGS_DANGEROUS)
def test_is_input_dangerous(string, result):
assert is_input_dangerous(string) == result

View file

@ -1,120 +0,0 @@
# (c) 2015, Michael Scherer <mscherer@redhat.com>
# Copyright (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
import pytest
from ansible_collections.community.general.plugins.module_utils import known_hosts
URLS = {
"ssh://one.example.org/example.git": {
"is_ssh_url": True,
"get_fqdn": "one.example.org",
"add_host_key_cmd": " -t rsa one.example.org",
"port": None,
},
"ssh+git://two.example.org/example.git": {
"is_ssh_url": True,
"get_fqdn": "two.example.org",
"add_host_key_cmd": " -t rsa two.example.org",
"port": None,
},
"rsync://three.example.org/user/example.git": {
"is_ssh_url": False,
"get_fqdn": "three.example.org",
"add_host_key_cmd": None, # not called for non-ssh urls
"port": None,
},
"git@four.example.org:user/example.git": {
"is_ssh_url": True,
"get_fqdn": "four.example.org",
"add_host_key_cmd": " -t rsa four.example.org",
"port": None,
},
"git+ssh://five.example.org/example.git": {
"is_ssh_url": True,
"get_fqdn": "five.example.org",
"add_host_key_cmd": " -t rsa five.example.org",
"port": None,
},
"ssh://six.example.org:21/example.org": {
# ssh on FTP Port?
"is_ssh_url": True,
"get_fqdn": "six.example.org",
"add_host_key_cmd": " -t rsa -p 21 six.example.org",
"port": "21",
},
"ssh://[2001:DB8::abcd:abcd]/example.git": {
"is_ssh_url": True,
"get_fqdn": "[2001:DB8::abcd:abcd]",
"add_host_key_cmd": " -t rsa [2001:DB8::abcd:abcd]",
"port": None,
},
"ssh://[2001:DB8::abcd:abcd]:22/example.git": {
"is_ssh_url": True,
"get_fqdn": "[2001:DB8::abcd:abcd]",
"add_host_key_cmd": " -t rsa -p 22 [2001:DB8::abcd:abcd]",
"port": "22",
},
"username@[2001:DB8::abcd:abcd]/example.git": {
"is_ssh_url": True,
"get_fqdn": "[2001:DB8::abcd:abcd]",
"add_host_key_cmd": " -t rsa [2001:DB8::abcd:abcd]",
"port": None,
},
"username@[2001:DB8::abcd:abcd]:path/example.git": {
"is_ssh_url": True,
"get_fqdn": "[2001:DB8::abcd:abcd]",
"add_host_key_cmd": " -t rsa [2001:DB8::abcd:abcd]",
"port": None,
},
"ssh://internal.git.server:7999/repos/repo.git": {
"is_ssh_url": True,
"get_fqdn": "internal.git.server",
"add_host_key_cmd": " -t rsa -p 7999 internal.git.server",
"port": "7999",
},
}
@pytest.mark.parametrize("url, is_ssh_url", ((k, URLS[k]["is_ssh_url"]) for k in sorted(URLS)))
def test_is_ssh_url(url, is_ssh_url):
assert known_hosts.is_ssh_url(url) == is_ssh_url
@pytest.mark.parametrize("url, fqdn, port", ((k, URLS[k]["get_fqdn"], URLS[k]["port"]) for k in sorted(URLS)))
def test_get_fqdn_and_port(url, fqdn, port):
assert known_hosts.get_fqdn_and_port(url) == (fqdn, port)
@pytest.mark.parametrize(
"fqdn, port, add_host_key_cmd",
(
(URLS[k]["get_fqdn"], URLS[k]["port"], URLS[k]["add_host_key_cmd"])
for k in sorted(URLS)
if URLS[k]["is_ssh_url"]
),
)
def test_add_host_key(mocker, fqdn, port, add_host_key_cmd):
am = mocker.MagicMock()
get_bin_path = mocker.MagicMock()
get_bin_path.return_value = keyscan_cmd = "/custom/path/ssh-keyscan"
am.get_bin_path = get_bin_path
run_command = mocker.MagicMock()
run_command.return_value = (0, "Needs output, otherwise thinks ssh-keyscan timed out'", "")
am.run_command = run_command
append_to_file = mocker.MagicMock()
append_to_file.return_value = (None,)
am.append_to_file = append_to_file
mocker.patch("os.path.isdir", return_value=True)
mocker.patch("os.path.exists", return_value=True)
known_hosts.add_host_key(am, fqdn, port=port)
run_command.assert_called_with(keyscan_cmd + add_host_key_cmd, environ_update={"LANGUAGE": "C", "LC_ALL": "C"})

View file

@ -1,54 +0,0 @@
# Copyright (c) 2019, Andrey Tuzhilin <andrei.tuzhilin@gmail.com>
# Copyright (c) 2020, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
import pytest
from ansible_collections.community.general.plugins.module_utils.saslprep import saslprep
VALID = [
("", ""),
("\u00a0", " "),
("a", "a"),
("й", "й"),
("\u30de\u30c8\u30ea\u30c3\u30af\u30b9", "\u30de\u30c8\u30ea\u30c3\u30af\u30b9"),
("The\u00adM\u00aatr\u2168", "TheMatrIX"),
("I\u00adX", "IX"),
("user", "user"),
("USER", "USER"),
("\u00aa", "a"),
("\u2168", "IX"),
("\u05be\u00a0\u05be", "\u05be\u0020\u05be"),
]
INVALID = [
(None, TypeError),
(b"", TypeError),
("\u0221", ValueError),
("\u0007", ValueError),
("\u0627\u0031", ValueError),
("\ue0001", ValueError),
("\ue0020", ValueError),
("\ufff9", ValueError),
("\ufdd0", ValueError),
("\u0000", ValueError),
("\u06dd", ValueError),
("\uffffD", ValueError),
("\ud800", ValueError),
("\u200e", ValueError),
("\u05be\u00aa\u05be", ValueError),
]
@pytest.mark.parametrize("source,target", VALID)
def test_saslprep_conversions(source, target):
assert saslprep(source) == target
@pytest.mark.parametrize("source,exception", INVALID)
def test_saslprep_exceptions(source, exception):
with pytest.raises(exception):
saslprep(source)

View file

@ -7,52 +7,6 @@ anchors:
environ_true: &env-def-true {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
environ_false: &env-def-false {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
test_cases:
- id: install_dancer_compatibility
input:
name: Dancer
mode: compatibility
output:
changed: true
cpanm_version: '1.7047'
mocks:
run_command:
- command: [/testbin/cpanm, --version]
environ: *env-def-true
rc: 0
out: |
cpanm (App::cpanminus) version 1.7047 (/usr/local/bin/cpanm)
perl version 5.041005 (/usr/local/bin/perl)
err: ''
- command: [/testbin/perl, -le, use Dancer;]
environ: *env-def-false
rc: 2
out: ''
err: error, not installed
- command: [/testbin/cpanm, Dancer]
environ: *env-def-true
rc: 0
out: ''
err: ''
- id: install_dancer_already_installed_compatibility
input:
name: Dancer
mode: compatibility
output:
changed: false
mocks:
run_command:
- command: [/testbin/cpanm, --version]
environ: *env-def-true
rc: 0
out: |
cpanm (App::cpanminus) version 1.7047 (/usr/local/bin/cpanm)
perl version 5.041005 (/usr/local/bin/perl)
err: ''
- command: [/testbin/perl, -le, use Dancer;]
environ: *env-def-false
rc: 0
out: ''
err: ''
- id: install_dancer
input:
name: Dancer
@ -72,26 +26,6 @@ test_cases:
rc: 0
out: ''
err: ''
- id: install_distribution_file_compatibility
input:
name: MIYAGAWA/Plack-0.99_05.tar.gz
mode: compatibility
output:
changed: true
mocks:
run_command:
- command: [/testbin/cpanm, --version]
environ: *env-def-true
rc: 0
out: |
cpanm (App::cpanminus) version 1.7047 (/usr/local/bin/cpanm)
perl version 5.041005 (/usr/local/bin/perl)
err: ''
- command: [/testbin/cpanm, MIYAGAWA/Plack-0.99_05.tar.gz]
environ: *env-def-true
rc: 0
out: ''
err: ''
- id: install_distribution_file
input:
name: MIYAGAWA/Plack-0.99_05.tar.gz