|
|
@ -19,13 +19,15 @@ the provided arguments.
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
import argparse
|
|
|
|
import argparse
|
|
|
|
|
|
|
|
import json
|
|
|
|
import os
|
|
|
|
import os
|
|
|
|
import shutil
|
|
|
|
import shutil
|
|
|
|
import subprocess
|
|
|
|
import subprocess
|
|
|
|
import sys
|
|
|
|
import sys
|
|
|
|
|
|
|
|
from itertools import chain
|
|
|
|
from pathlib import Path
|
|
|
|
from pathlib import Path
|
|
|
|
from tempfile import TemporaryDirectory
|
|
|
|
from tempfile import TemporaryDirectory
|
|
|
|
from typing import Any, Dict, Optional, Sequence, Union
|
|
|
|
from typing import Any, Dict, Optional, Sequence, Tuple, Union
|
|
|
|
|
|
|
|
|
|
|
|
from yaml import CSafeLoader, load
|
|
|
|
from yaml import CSafeLoader, load
|
|
|
|
|
|
|
|
|
|
|
@ -64,6 +66,7 @@ class AnsibleCollection:
|
|
|
|
"ansible-galaxy",
|
|
|
|
"ansible-galaxy",
|
|
|
|
"collection",
|
|
|
|
"collection",
|
|
|
|
"install",
|
|
|
|
"install",
|
|
|
|
|
|
|
|
"--force",
|
|
|
|
"-n",
|
|
|
|
"-n",
|
|
|
|
"-p",
|
|
|
|
"-p",
|
|
|
|
str(destdir),
|
|
|
|
str(destdir),
|
|
|
@ -84,13 +87,24 @@ class AnsibleCollection:
|
|
|
|
with open(filelist, "w", encoding="utf-8") as file:
|
|
|
|
with open(filelist, "w", encoding="utf-8") as file:
|
|
|
|
file.write(contents)
|
|
|
|
file.write(contents)
|
|
|
|
|
|
|
|
|
|
|
|
def unit_test(self, extra_args: Sequence) -> None:
|
|
|
|
def unit_test(
|
|
|
|
with TemporaryDirectory() as temp:
|
|
|
|
self,
|
|
|
|
temppath = Path(temp) / "ansible_collections" / self.namespace / self.name
|
|
|
|
extra_args: Sequence[str],
|
|
|
|
|
|
|
|
extra_paths: Sequence[Path],
|
|
|
|
|
|
|
|
collections: Sequence[str],
|
|
|
|
|
|
|
|
) -> None:
|
|
|
|
|
|
|
|
with TemporaryDirectory() as _temp:
|
|
|
|
|
|
|
|
temp = Path(_temp)
|
|
|
|
|
|
|
|
temppath = temp / "ansible_collections" / self.namespace / self.name
|
|
|
|
shutil.copytree(
|
|
|
|
shutil.copytree(
|
|
|
|
self.collection_srcdir,
|
|
|
|
self.collection_srcdir,
|
|
|
|
temppath,
|
|
|
|
temppath,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
collection_paths = (
|
|
|
|
|
|
|
|
self._get_collection_path(collection) for collection in collections
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
for extra in chain(collection_paths, extra_paths):
|
|
|
|
|
|
|
|
self._handle_extra_path(temp, extra)
|
|
|
|
args = ("ansible-test", "units", *extra_args)
|
|
|
|
args = ("ansible-test", "units", *extra_args)
|
|
|
|
print(f"Running: {args}")
|
|
|
|
print(f"Running: {args}")
|
|
|
|
print()
|
|
|
|
print()
|
|
|
@ -104,10 +118,67 @@ class AnsibleCollection:
|
|
|
|
env={**os.environ, "ANSIBLE_GALAXY_COLLECTIONS_PATH_WARNING": "0"},
|
|
|
|
env={**os.environ, "ANSIBLE_GALAXY_COLLECTIONS_PATH_WARNING": "0"},
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _get_collection_path(self, collection: str) -> Path:
|
|
|
|
|
|
|
|
proc = subprocess.run(
|
|
|
|
|
|
|
|
["ansible-galaxy", "collection", "list", "--format=json", collection],
|
|
|
|
|
|
|
|
check=True,
|
|
|
|
|
|
|
|
universal_newlines=True,
|
|
|
|
|
|
|
|
stdout=subprocess.PIPE,
|
|
|
|
|
|
|
|
stderr=subprocess.PIPE,
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
# {
|
|
|
|
|
|
|
|
# "/usr/share/ansible/collections/ansible_collections": {
|
|
|
|
|
|
|
|
# "community.general": {
|
|
|
|
|
|
|
|
# "version": "8.2.0"
|
|
|
|
|
|
|
|
# }
|
|
|
|
|
|
|
|
# }
|
|
|
|
|
|
|
|
# }
|
|
|
|
|
|
|
|
data: Dict[str, Dict[str, Any]] = json.loads(proc.stdout)
|
|
|
|
|
|
|
|
for path, collection_part in data.items():
|
|
|
|
|
|
|
|
version = collection_part[collection]["version"]
|
|
|
|
|
|
|
|
print(f"Using locally-installed version {version} of {collection}")
|
|
|
|
|
|
|
|
return Path(path, *collection.split(".", 1))
|
|
|
|
|
|
|
|
raise CollectionError(f"Failed to add {collection} to the test tree")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _handle_extra_path(self, collection_tree: Path, extra_path: Path) -> None:
|
|
|
|
|
|
|
|
namespace_name = _get_namespace_name(extra_path)
|
|
|
|
|
|
|
|
if namespace_name == (self.namespace, self.name):
|
|
|
|
|
|
|
|
raise CollectionError(
|
|
|
|
|
|
|
|
f"{extra_path} is the same collection as {self.collection_srcdir}"
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
new_path = Path(collection_tree, "ansible_collections", *namespace_name)
|
|
|
|
|
|
|
|
if new_path.is_dir():
|
|
|
|
|
|
|
|
raise CollectionError(
|
|
|
|
|
|
|
|
f"Cannot copy {extra_path}."
|
|
|
|
|
|
|
|
f" Collection {namespace_name} was already added."
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
print(
|
|
|
|
|
|
|
|
f"Copying {extra_path} ({'.'.join(namespace_name)}) to the collection tree"
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
shutil.copytree(extra_path, new_path)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _get_namespace_name(extra_path: Path) -> Tuple[str, str]:
|
|
|
|
|
|
|
|
data_file = extra_path / "MANIFEST.json"
|
|
|
|
|
|
|
|
data_file2 = extra_path / "galaxy.yml"
|
|
|
|
|
|
|
|
if data_file.is_file():
|
|
|
|
|
|
|
|
with data_file.open("r", encoding="utf-8") as fp:
|
|
|
|
|
|
|
|
data = json.load(fp)["collection_info"]
|
|
|
|
|
|
|
|
elif data_file2.is_file():
|
|
|
|
|
|
|
|
data_file = data_file2
|
|
|
|
|
|
|
|
with data_file2.open("r", encoding="utf-8") as fp:
|
|
|
|
|
|
|
|
data = load(fp, Loader=CSafeLoader)
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
raise CollectionError(f"No metadata file found for collection in {extra_path}")
|
|
|
|
|
|
|
|
expected_keys = {"namespace", "name"}
|
|
|
|
|
|
|
|
if set(data) & expected_keys != expected_keys:
|
|
|
|
|
|
|
|
raise CollectionError(f"Invalid metadata file: {data_file}")
|
|
|
|
|
|
|
|
return data["namespace"], data["name"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def parseargs() -> argparse.Namespace:
|
|
|
|
def parseargs() -> argparse.Namespace:
|
|
|
|
parser = argparse.ArgumentParser(
|
|
|
|
parser = argparse.ArgumentParser(
|
|
|
|
"Install and test Ansible Collections in an rpmbuild environment"
|
|
|
|
description="Install and test Ansible Collections in an rpmbuild environment"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
subparsers = parser.add_subparsers(dest="action")
|
|
|
|
subparsers = parser.add_subparsers(dest="action")
|
|
|
|
install_parser = subparsers.add_parser(
|
|
|
|
install_parser = subparsers.add_parser(
|
|
|
@ -132,13 +203,33 @@ def parseargs() -> argparse.Namespace:
|
|
|
|
help="Run ansible-test unit after creating the necessary directory structure",
|
|
|
|
help="Run ansible-test unit after creating the necessary directory structure",
|
|
|
|
)
|
|
|
|
)
|
|
|
|
test_parser.add_argument(
|
|
|
|
test_parser.add_argument(
|
|
|
|
"extra_args", nargs="*", help="Extra arguments to pass to ansible-test"
|
|
|
|
"-p",
|
|
|
|
|
|
|
|
"--extra-path",
|
|
|
|
|
|
|
|
dest="extra_paths",
|
|
|
|
|
|
|
|
action="append",
|
|
|
|
|
|
|
|
help="Path to an extra collection include in the test ansible_collection tree",
|
|
|
|
|
|
|
|
type=Path,
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
test_parser.add_argument(
|
|
|
|
|
|
|
|
"-c",
|
|
|
|
|
|
|
|
"--collection",
|
|
|
|
|
|
|
|
action="append",
|
|
|
|
|
|
|
|
dest="collections",
|
|
|
|
|
|
|
|
help="Add a collection from the collection path to the test tree",
|
|
|
|
)
|
|
|
|
)
|
|
|
|
args = parser.parse_args()
|
|
|
|
test_parser.set_defaults(allow_extra_args=True)
|
|
|
|
|
|
|
|
args, extra_args = parser.parse_known_args()
|
|
|
|
# add_subparsers does not support required on Python 3.6
|
|
|
|
# add_subparsers does not support required on Python 3.6
|
|
|
|
if not args.action:
|
|
|
|
if not args.action:
|
|
|
|
parser.print_usage()
|
|
|
|
parser.print_usage()
|
|
|
|
sys.exit(2)
|
|
|
|
sys.exit(2)
|
|
|
|
|
|
|
|
if extra_args:
|
|
|
|
|
|
|
|
if not getattr(args, "allow_extra_args", False):
|
|
|
|
|
|
|
|
parser.error(f"unrecognized arguments: {' '.join(extra_args)}")
|
|
|
|
|
|
|
|
if extra_args and extra_args[0] == "--":
|
|
|
|
|
|
|
|
extra_args = extra_args[1:]
|
|
|
|
|
|
|
|
args.extra_args = extra_args
|
|
|
|
|
|
|
|
vars(args).pop("allow_extra_args", None)
|
|
|
|
return args
|
|
|
|
return args
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -149,11 +240,15 @@ def main():
|
|
|
|
collection.install(args.collections_dir)
|
|
|
|
collection.install(args.collections_dir)
|
|
|
|
collection.write_filelist(args.filelist)
|
|
|
|
collection.write_filelist(args.filelist)
|
|
|
|
elif args.action == "test":
|
|
|
|
elif args.action == "test":
|
|
|
|
collection.unit_test(args.extra_args)
|
|
|
|
collection.unit_test(
|
|
|
|
|
|
|
|
args.extra_args,
|
|
|
|
|
|
|
|
(args.extra_paths or ()),
|
|
|
|
|
|
|
|
(args.collections or ()),
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
if __name__ == "__main__":
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
main()
|
|
|
|
main()
|
|
|
|
except (CollectionError, subprocess.CalledProcessError) as err:
|
|
|
|
except (CollectionError, subprocess.CalledProcessError) as err:
|
|
|
|
sys.exit(err)
|
|
|
|
sys.exit(str(err))
|
|
|
|