diff options
Diffstat (limited to 'test/integration/targets/ansible-galaxy-collection')
12 files changed, 1060 insertions, 0 deletions
diff --git a/test/integration/targets/ansible-galaxy-collection/aliases b/test/integration/targets/ansible-galaxy-collection/aliases new file mode 100644 index 00000000..4b3ebea3 --- /dev/null +++ b/test/integration/targets/ansible-galaxy-collection/aliases @@ -0,0 +1,3 @@ +shippable/fallaxy/group1 +shippable/fallaxy/smoketest +cloud/fallaxy diff --git a/test/integration/targets/ansible-galaxy-collection/files/build_bad_tar.py b/test/integration/targets/ansible-galaxy-collection/files/build_bad_tar.py new file mode 100644 index 00000000..6182e865 --- /dev/null +++ b/test/integration/targets/ansible-galaxy-collection/files/build_bad_tar.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python + +# Copyright: (c) 2020, Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import hashlib +import io +import json +import os +import sys +import tarfile + +manifest = { + 'collection_info': { + 'namespace': 'suspicious', + 'name': 'test', + 'version': '1.0.0', + 'dependencies': {}, + }, + 'file_manifest_file': { + 'name': 'FILES.json', + 'ftype': 'file', + 'chksum_type': 'sha256', + 'chksum_sha256': None, + 'format': 1 + }, + 'format': 1, +} + +files = { + 'files': [ + { + 'name': '.', + 'ftype': 'dir', + 'chksum_type': None, + 'chksum_sha256': None, + 'format': 1, + }, + ], + 'format': 1, +} + + +def add_file(tar_file, filename, b_content, update_files=True): + tar_info = tarfile.TarInfo(filename) + tar_info.size = len(b_content) + tar_info.mode = 0o0755 + tar_file.addfile(tarinfo=tar_info, fileobj=io.BytesIO(b_content)) + + if update_files: + sha256 = hashlib.sha256() + sha256.update(b_content) + + files['files'].append({ + 'name': filename, + 'ftype': 'file', + 'chksum_type': 'sha256', + 'chksum_sha256': sha256.hexdigest(), + 'format': 1 + }) + + +collection_tar = os.path.join(sys.argv[1], 'suspicious-test-1.0.0.tar.gz') +with tarfile.open(collection_tar, mode='w:gz') as tar_file: + add_file(tar_file, '../../outside.sh', b"#!/usr/bin/env bash\necho \"you got pwned\"") + + b_files = json.dumps(files).encode('utf-8') + b_files_hash = hashlib.sha256() + b_files_hash.update(b_files) + manifest['file_manifest_file']['chksum_sha256'] = b_files_hash.hexdigest() + add_file(tar_file, 'FILES.json', b_files) + add_file(tar_file, 'MANIFEST.json', json.dumps(manifest).encode('utf-8')) + + b_manifest = json.dumps(manifest).encode('utf-8') + + for name, b in [('MANIFEST.json', b_manifest), ('FILES.json', b_files)]: + b_io = io.BytesIO(b) + tar_info = tarfile.TarInfo(name) + tar_info.size = len(b) + tar_info.mode = 0o0644 + tar_file.addfile(tarinfo=tar_info, fileobj=b_io) diff --git a/test/integration/targets/ansible-galaxy-collection/library/setup_collections.py b/test/integration/targets/ansible-galaxy-collection/library/setup_collections.py new file mode 100644 index 00000000..b876a65f --- /dev/null +++ b/test/integration/targets/ansible-galaxy-collection/library/setup_collections.py @@ -0,0 +1,169 @@ +#!/usr/bin/python + +# Copyright: (c) 2020, Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +ANSIBLE_METADATA = { + 'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community' +} + +DOCUMENTATION = ''' +--- +module: setup_collections +short_description: Set up test collections based on the input +description: +- Builds and publishes a whole bunch of collections used for testing in bulk. +options: + server: + description: + - The Galaxy server to upload the collections to. + required: yes + type: str + token: + description: + - The token used to authenticate with the Galaxy server. + required: yes + type: str + collections: + description: + - A list of collection details to use for the build. + required: yes + type: list + elements: dict + options: + namespace: + description: + - The namespace of the collection. + required: yes + type: str + name: + description: + - The name of the collection. + required: yes + type: str + version: + description: + - The version of the collection. + type: str + default: '1.0.0' + dependencies: + description: + - The dependencies of the collection. + type: dict + default: '{}' +author: +- Jordan Borean (@jborean93) +''' + +EXAMPLES = ''' +- name: Build test collections + setup_collections: + path: ~/ansible/collections/ansible_collections + collections: + - namespace: namespace1 + name: name1 + version: 0.0.1 + - namespace: namespace1 + name: name1 + version: 0.0.2 +''' + +RETURN = ''' +# +''' + +import os +import tempfile +import yaml + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils._text import to_bytes + + +def run_module(): + module_args = dict( + server=dict(type='str', required=True), + token=dict(type='str', required=True), + collections=dict( + type='list', + elements='dict', + required=True, + options=dict( + namespace=dict(type='str', required=True), + name=dict(type='str', required=True), + version=dict(type='str', default='1.0.0'), + dependencies=dict(type='dict', default={}), + use_symlink=dict(type='bool', default=False), + ), + ), + ) + + module = AnsibleModule( + argument_spec=module_args, + supports_check_mode=False + ) + + result = dict(changed=True) + + for idx, collection in enumerate(module.params['collections']): + collection_dir = os.path.join(module.tmpdir, "%s-%s-%s" % (collection['namespace'], collection['name'], + collection['version'])) + b_collection_dir = to_bytes(collection_dir, errors='surrogate_or_strict') + os.mkdir(b_collection_dir) + + with open(os.path.join(b_collection_dir, b'README.md'), mode='wb') as fd: + fd.write(b"Collection readme") + + galaxy_meta = { + 'namespace': collection['namespace'], + 'name': collection['name'], + 'version': collection['version'], + 'readme': 'README.md', + 'authors': ['Collection author <name@email.com'], + 'dependencies': collection['dependencies'], + } + with open(os.path.join(b_collection_dir, b'galaxy.yml'), mode='wb') as fd: + fd.write(to_bytes(yaml.safe_dump(galaxy_meta), errors='surrogate_or_strict')) + + with tempfile.NamedTemporaryFile(mode='wb') as temp_fd: + temp_fd.write(b"data") + + if collection['use_symlink']: + os.mkdir(os.path.join(b_collection_dir, b'docs')) + os.mkdir(os.path.join(b_collection_dir, b'plugins')) + b_target_file = b'RE\xc3\x85DM\xc3\x88.md' + with open(os.path.join(b_collection_dir, b_target_file), mode='wb') as fd: + fd.write(b'data') + + os.symlink(b_target_file, os.path.join(b_collection_dir, b_target_file + b'-link')) + os.symlink(temp_fd.name, os.path.join(b_collection_dir, b_target_file + b'-outside-link')) + os.symlink(os.path.join(b'..', b_target_file), os.path.join(b_collection_dir, b'docs', b_target_file)) + os.symlink(os.path.join(b_collection_dir, b_target_file), + os.path.join(b_collection_dir, b'plugins', b_target_file)) + os.symlink(b'docs', os.path.join(b_collection_dir, b'docs-link')) + + release_filename = '%s-%s-%s.tar.gz' % (collection['namespace'], collection['name'], collection['version']) + collection_path = os.path.join(collection_dir, release_filename) + module.run_command(['ansible-galaxy', 'collection', 'build'], cwd=collection_dir) + + # To save on time, skip the import wait until the last collection is being uploaded. + publish_args = ['ansible-galaxy', 'collection', 'publish', collection_path, '--server', + module.params['server'], '--token', module.params['token']] + if idx != (len(module.params['collections']) - 1): + publish_args.append('--no-wait') + module.run_command(publish_args) + + module.exit_json(**result) + + +def main(): + run_module() + + +if __name__ == '__main__': + main() diff --git a/test/integration/targets/ansible-galaxy-collection/meta/main.yml b/test/integration/targets/ansible-galaxy-collection/meta/main.yml new file mode 100644 index 00000000..e3dd5fb1 --- /dev/null +++ b/test/integration/targets/ansible-galaxy-collection/meta/main.yml @@ -0,0 +1,3 @@ +--- +dependencies: +- setup_remote_tmp_dir diff --git a/test/integration/targets/ansible-galaxy-collection/tasks/build.yml b/test/integration/targets/ansible-galaxy-collection/tasks/build.yml new file mode 100644 index 00000000..a5ba1d47 --- /dev/null +++ b/test/integration/targets/ansible-galaxy-collection/tasks/build.yml @@ -0,0 +1,53 @@ +--- +- name: build basic collection based on current directory + command: ansible-galaxy collection build {{ galaxy_verbosity }} + args: + chdir: '{{ galaxy_dir }}/scratch/ansible_test/my_collection' + register: build_current_dir + +- name: get result of build basic collection on current directory + stat: + path: '{{ galaxy_dir }}/scratch/ansible_test/my_collection/ansible_test-my_collection-1.0.0.tar.gz' + register: build_current_dir_actual + +- name: assert build basic collection based on current directory + assert: + that: + - '"Created collection for ansible_test.my_collection" in build_current_dir.stdout' + - build_current_dir_actual.stat.exists + +- name: build basic collection based on relative dir + command: ansible-galaxy collection build scratch/ansible_test/my_collection {{ galaxy_verbosity }} + args: + chdir: '{{ galaxy_dir }}' + register: build_relative_dir + +- name: get result of build basic collection based on relative dir + stat: + path: '{{ galaxy_dir }}/ansible_test-my_collection-1.0.0.tar.gz' + register: build_relative_dir_actual + +- name: assert build basic collection based on relative dir + assert: + that: + - '"Created collection for ansible_test.my_collection" in build_relative_dir.stdout' + - build_relative_dir_actual.stat.exists + +- name: fail to build existing collection without force + command: ansible-galaxy collection build scratch/ansible_test/my_collection {{ galaxy_verbosity }} + args: + chdir: '{{ galaxy_dir }}' + ignore_errors: yes + register: build_existing_no_force + +- name: build existing collection with force + command: ansible-galaxy collection build scratch/ansible_test/my_collection --force {{ galaxy_verbosity }} + args: + chdir: '{{ galaxy_dir }}' + register: build_existing_force + +- name: assert build existing collection + assert: + that: + - '"use --force to re-create the collection artifact" in build_existing_no_force.stderr' + - '"Created collection for ansible_test.my_collection" in build_existing_force.stdout' diff --git a/test/integration/targets/ansible-galaxy-collection/tasks/download.yml b/test/integration/targets/ansible-galaxy-collection/tasks/download.yml new file mode 100644 index 00000000..bdd743b2 --- /dev/null +++ b/test/integration/targets/ansible-galaxy-collection/tasks/download.yml @@ -0,0 +1,142 @@ +--- +- name: create test download dir + file: + path: '{{ galaxy_dir }}/download' + state: directory + +- name: download collection with multiple dependencies + command: ansible-galaxy collection download parent_dep.parent_collection -s {{ fallaxy_galaxy_server }} {{ galaxy_verbosity }} + register: download_collection + args: + chdir: '{{ galaxy_dir }}/download' + +- name: get result of download collection with multiple dependencies + find: + path: '{{ galaxy_dir }}/download/collections' + file_type: file + register: download_collection_actual + +- name: assert download collection with multiple dependencies + assert: + that: + - '"Downloading collection ''parent_dep.parent_collection'' to" in download_collection.stdout' + - '"Downloading collection ''child_dep.child_collection'' to" in download_collection.stdout' + - '"Downloading collection ''child_dep.child_dep2'' to" in download_collection.stdout' + - download_collection_actual.examined == 4 + - download_collection_actual.matched == 4 + - (download_collection_actual.files[0].path | basename) in ['requirements.yml', 'child_dep-child_dep2-1.2.2.tar.gz', 'child_dep-child_collection-0.9.9.tar.gz', 'parent_dep-parent_collection-1.0.0.tar.gz'] + - (download_collection_actual.files[1].path | basename) in ['requirements.yml', 'child_dep-child_dep2-1.2.2.tar.gz', 'child_dep-child_collection-0.9.9.tar.gz', 'parent_dep-parent_collection-1.0.0.tar.gz'] + - (download_collection_actual.files[2].path | basename) in ['requirements.yml', 'child_dep-child_dep2-1.2.2.tar.gz', 'child_dep-child_collection-0.9.9.tar.gz', 'parent_dep-parent_collection-1.0.0.tar.gz'] + - (download_collection_actual.files[3].path | basename) in ['requirements.yml', 'child_dep-child_dep2-1.2.2.tar.gz', 'child_dep-child_collection-0.9.9.tar.gz', 'parent_dep-parent_collection-1.0.0.tar.gz'] + +- name: test install of download requirements file + command: ansible-galaxy collection install -r requirements.yml -p '{{ galaxy_dir }}/download' {{ galaxy_verbosity }} + args: + chdir: '{{ galaxy_dir }}/download/collections' + register: install_download + +- name: get result of test install of download requirements file + slurp: + path: '{{ galaxy_dir }}/download/ansible_collections/{{ collection.namespace }}/{{ collection.name }}/MANIFEST.json' + register: install_download_actual + loop_control: + loop_var: collection + loop: + - namespace: parent_dep + name: parent_collection + - namespace: child_dep + name: child_collection + - namespace: child_dep + name: child_dep2 + +- name: assert test install of download requirements file + assert: + that: + - '"Installing ''parent_dep.parent_collection:1.0.0'' to" in install_download.stdout' + - '"Installing ''child_dep.child_collection:0.9.9'' to" in install_download.stdout' + - '"Installing ''child_dep.child_dep2:1.2.2'' to" in install_download.stdout' + - (install_download_actual.results[0].content | b64decode | from_json).collection_info.version == '1.0.0' + - (install_download_actual.results[1].content | b64decode | from_json).collection_info.version == '0.9.9' + - (install_download_actual.results[2].content | b64decode | from_json).collection_info.version == '1.2.2' + +- name: create test requirements file for download + copy: + content: | + collections: + - name: namespace1.name1 + version: 1.1.0-beta.1 + + dest: '{{ galaxy_dir }}/download/download.yml' + +- name: download collection with req to custom dir + command: ansible-galaxy collection download -r '{{ galaxy_dir }}/download/download.yml' -s {{ fallaxy_ah_server }} -p '{{ galaxy_dir }}/download/collections-custom' {{ galaxy_verbosity }} + register: download_req_custom_path + +- name: get result of download collection with req to custom dir + find: + path: '{{ galaxy_dir }}/download/collections-custom' + file_type: file + register: download_req_custom_path_actual + +- name: assert download collection with multiple dependencies + assert: + that: + - '"Downloading collection ''namespace1.name1'' to" in download_req_custom_path.stdout' + - download_req_custom_path_actual.examined == 2 + - download_req_custom_path_actual.matched == 2 + - (download_req_custom_path_actual.files[0].path | basename) in ['requirements.yml', 'namespace1-name1-1.1.0-beta.1.tar.gz'] + - (download_req_custom_path_actual.files[1].path | basename) in ['requirements.yml', 'namespace1-name1-1.1.0-beta.1.tar.gz'] + +# https://github.com/ansible/ansible/issues/68186 +- name: create test requirements file without roles and collections + copy: + content: | + collections: + roles: + + dest: '{{ galaxy_dir }}/download/no_roles_no_collections.yml' + +- name: install collection with requirements + command: ansible-galaxy collection install -r '{{ galaxy_dir }}/download/no_roles_no_collections.yml' {{ galaxy_verbosity }} + register: install_no_requirements + +- name: assert install collection with no roles and no collections in requirements + assert: + that: + - '"Skipping install, no requirements found" in install_no_requirements.stdout' + +- name: Test downloading a tar.gz collection artifact + block: + + - name: get result of build basic collection on current directory + stat: + path: '{{ galaxy_dir }}/scratch/ansible_test/my_collection/ansible_test-my_collection-1.0.0.tar.gz' + register: result + + - name: create default skeleton + command: ansible-galaxy collection init ansible_test.my_collection {{ galaxy_verbosity }} + args: + chdir: '{{ galaxy_dir }}/scratch' + when: not result.stat.exists + + - name: build the tar.gz + command: ansible-galaxy collection build {{ galaxy_verbosity }} + args: + chdir: '{{ galaxy_dir }}/scratch/ansible_test/my_collection' + when: not result.stat.exists + + - name: download a tar.gz file + command: ansible-galaxy collection download '{{ galaxy_dir }}/scratch/ansible_test/my_collection/ansible_test-my_collection-1.0.0.tar.gz' + args: + chdir: '{{ galaxy_dir }}/download' + register: download_collection + + - name: get result of downloaded tar.gz + stat: + path: '{{ galaxy_dir }}/download/collections/ansible_test-my_collection-1.0.0.tar.gz' + register: download_collection_actual + + - assert: + that: + - '"Downloading collection ''ansible_test.my_collection'' to" in download_collection.stdout' + - download_collection_actual.stat.exists diff --git a/test/integration/targets/ansible-galaxy-collection/tasks/init.yml b/test/integration/targets/ansible-galaxy-collection/tasks/init.yml new file mode 100644 index 00000000..15ec5eab --- /dev/null +++ b/test/integration/targets/ansible-galaxy-collection/tasks/init.yml @@ -0,0 +1,44 @@ +--- +- name: create default skeleton + command: ansible-galaxy collection init ansible_test.my_collection {{ galaxy_verbosity }} + args: + chdir: '{{ galaxy_dir }}/scratch' + register: init_relative + +- name: get result of create default skeleton + find: + path: '{{ galaxy_dir }}/scratch/ansible_test/my_collection' + recurse: yes + file_type: directory + register: init_relative_actual + +- debug: + var: init_relative_actual.files | map(attribute='path') | list + +- name: assert create default skeleton + assert: + that: + - '"Collection ansible_test.my_collection was created successfully" in init_relative.stdout' + - init_relative_actual.files | length == 3 + - (init_relative_actual.files | map(attribute='path') | list)[0] | basename in ['docs', 'plugins', 'roles'] + - (init_relative_actual.files | map(attribute='path') | list)[1] | basename in ['docs', 'plugins', 'roles'] + - (init_relative_actual.files | map(attribute='path') | list)[2] | basename in ['docs', 'plugins', 'roles'] + +- name: create collection with custom init path + command: ansible-galaxy collection init ansible_test2.my_collection --init-path "{{ galaxy_dir }}/scratch/custom-init-dir" {{ galaxy_verbosity }} + register: init_custom_path + +- name: get result of create default skeleton + find: + path: '{{ galaxy_dir }}/scratch/custom-init-dir/ansible_test2/my_collection' + file_type: directory + register: init_custom_path_actual + +- name: assert create collection with custom init path + assert: + that: + - '"Collection ansible_test2.my_collection was created successfully" in init_custom_path.stdout' + - init_custom_path_actual.files | length == 3 + - (init_custom_path_actual.files | map(attribute='path') | list)[0] | basename in ['docs', 'plugins', 'roles'] + - (init_custom_path_actual.files | map(attribute='path') | list)[1] | basename in ['docs', 'plugins', 'roles'] + - (init_custom_path_actual.files | map(attribute='path') | list)[2] | basename in ['docs', 'plugins', 'roles'] diff --git a/test/integration/targets/ansible-galaxy-collection/tasks/install.yml b/test/integration/targets/ansible-galaxy-collection/tasks/install.yml new file mode 100644 index 00000000..11ce1c01 --- /dev/null +++ b/test/integration/targets/ansible-galaxy-collection/tasks/install.yml @@ -0,0 +1,330 @@ +--- +- name: create test collection install directory - {{ test_name }} + file: + path: '{{ galaxy_dir }}/ansible_collections' + state: directory + +- name: install simple collection with implicit path - {{ test_name }} + command: ansible-galaxy collection install namespace1.name1 -s '{{ test_server }}' {{ galaxy_verbosity }} + environment: + ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}/ansible_collections' + register: install_normal + +- name: get installed files of install simple collection with implicit path - {{ test_name }} + find: + path: '{{ galaxy_dir }}/ansible_collections/namespace1/name1' + file_type: file + register: install_normal_files + +- name: get the manifest of install simple collection with implicit path - {{ test_name }} + slurp: + path: '{{ galaxy_dir }}/ansible_collections/namespace1/name1/MANIFEST.json' + register: install_normal_manifest + +- name: assert install simple collection with implicit path - {{ test_name }} + assert: + that: + - '"Installing ''namespace1.name1:1.0.9'' to" in install_normal.stdout' + - install_normal_files.files | length == 3 + - install_normal_files.files[0].path | basename in ['MANIFEST.json', 'FILES.json', 'README.md'] + - install_normal_files.files[1].path | basename in ['MANIFEST.json', 'FILES.json', 'README.md'] + - install_normal_files.files[2].path | basename in ['MANIFEST.json', 'FILES.json', 'README.md'] + - (install_normal_manifest.content | b64decode | from_json).collection_info.version == '1.0.9' + +- name: install existing without --force - {{ test_name }} + command: ansible-galaxy collection install namespace1.name1 -s '{{ test_server }}' {{ galaxy_verbosity }} + environment: + ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}/ansible_collections' + register: install_existing_no_force + +- name: assert install existing without --force - {{ test_name }} + assert: + that: + - '"Skipping ''namespace1.name1'' as it is already installed" in install_existing_no_force.stdout' + +- name: install existing with --force - {{ test_name }} + command: ansible-galaxy collection install namespace1.name1 -s '{{ test_server }}' --force {{ galaxy_verbosity }} + environment: + ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}/ansible_collections' + register: install_existing_force + +- name: assert install existing with --force - {{ test_name }} + assert: + that: + - '"Installing ''namespace1.name1:1.0.9'' to" in install_existing_force.stdout' + +- name: remove test installed collection - {{ test_name }} + file: + path: '{{ galaxy_dir }}/ansible_collections/namespace1' + state: absent + +- name: install pre-release as explicit version to custom dir - {{ test_name }} + command: ansible-galaxy collection install 'namespace1.name1:1.1.0-beta.1' -s '{{ test_server }}' -p '{{ galaxy_dir }}/ansible_collections' {{ galaxy_verbosity }} + register: install_prerelease + +- name: get result of install pre-release as explicit version to custom dir - {{ test_name }} + slurp: + path: '{{ galaxy_dir }}/ansible_collections/namespace1/name1/MANIFEST.json' + register: install_prerelease_actual + +- name: assert install pre-release as explicit version to custom dir - {{ test_name }} + assert: + that: + - '"Installing ''namespace1.name1:1.1.0-beta.1'' to" in install_prerelease.stdout' + - (install_prerelease_actual.content | b64decode | from_json).collection_info.version == '1.1.0-beta.1' + +- name: Remove beta + file: + path: '{{ galaxy_dir }}/ansible_collections/namespace1/name1' + state: absent + +- name: install pre-release version with --pre to custom dir - {{ test_name }} + command: ansible-galaxy collection install --pre 'namespace1.name1' -s '{{ test_server }}' -p '{{ galaxy_dir }}/ansible_collections' {{ galaxy_verbosity }} + register: install_prerelease + +- name: get result of install pre-release version with --pre to custom dir - {{ test_name }} + slurp: + path: '{{ galaxy_dir }}/ansible_collections/namespace1/name1/MANIFEST.json' + register: install_prerelease_actual + +- name: assert install pre-release version with --pre to custom dir - {{ test_name }} + assert: + that: + - '"Installing ''namespace1.name1:1.1.0-beta.1'' to" in install_prerelease.stdout' + - (install_prerelease_actual.content | b64decode | from_json).collection_info.version == '1.1.0-beta.1' + +- name: install multiple collections with dependencies - {{ test_name }} + command: ansible-galaxy collection install parent_dep.parent_collection namespace2.name -s {{ test_name }} {{ galaxy_verbosity }} + args: + chdir: '{{ galaxy_dir }}/ansible_collections' + environment: + ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}/ansible_collections' + ANSIBLE_CONFIG: '{{ galaxy_dir }}/ansible.cfg' + register: install_multiple_with_dep + +- name: get result of install multiple collections with dependencies - {{ test_name }} + slurp: + path: '{{ galaxy_dir }}/ansible_collections/{{ collection.namespace }}/{{ collection.name }}/MANIFEST.json' + register: install_multiple_with_dep_actual + loop_control: + loop_var: collection + loop: + - namespace: namespace2 + name: name + - namespace: parent_dep + name: parent_collection + - namespace: child_dep + name: child_collection + - namespace: child_dep + name: child_dep2 + +- name: assert install multiple collections with dependencies - {{ test_name }} + assert: + that: + - (install_multiple_with_dep_actual.results[0].content | b64decode | from_json).collection_info.version == '1.0.0' + - (install_multiple_with_dep_actual.results[1].content | b64decode | from_json).collection_info.version == '1.0.0' + - (install_multiple_with_dep_actual.results[2].content | b64decode | from_json).collection_info.version == '0.9.9' + - (install_multiple_with_dep_actual.results[3].content | b64decode | from_json).collection_info.version == '1.2.2' + +- name: expect failure with dep resolution failure + command: ansible-galaxy collection install fail_namespace.fail_collection -s {{ test_server }} {{ galaxy_verbosity }} + register: fail_dep_mismatch + failed_when: '"Cannot meet dependency requirement ''fail_dep2.name:<0.0.5'' for collection fail_namespace.fail_collection" not in fail_dep_mismatch.stderr' + +- name: download a collection for an offline install - {{ test_name }} + get_url: + url: '{{ test_server }}custom/collections/namespace3-name-1.0.0.tar.gz' + dest: '{{ galaxy_dir }}/namespace3.tar.gz' + +- name: install a collection from a tarball - {{ test_name }} + command: ansible-galaxy collection install '{{ galaxy_dir }}/namespace3.tar.gz' {{ galaxy_verbosity }} + register: install_tarball + environment: + ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}/ansible_collections' + +- name: get result of install collection from a tarball - {{ test_name }} + slurp: + path: '{{ galaxy_dir }}/ansible_collections/namespace3/name/MANIFEST.json' + register: install_tarball_actual + +- name: assert install a collection from a tarball - {{ test_name }} + assert: + that: + - '"Installing ''namespace3.name:1.0.0'' to" in install_tarball.stdout' + - (install_tarball_actual.content | b64decode | from_json).collection_info.version == '1.0.0' + +- name: setup bad tarball - {{ test_name }} + script: build_bad_tar.py {{ galaxy_dir | quote }} + +- name: fail to install a collection from a bad tarball - {{ test_name }} + command: ansible-galaxy collection install '{{ galaxy_dir }}/suspicious-test-1.0.0.tar.gz' {{ galaxy_verbosity }} + register: fail_bad_tar + failed_when: fail_bad_tar.rc != 1 and "Cannot extract tar entry '../../outside.sh' as it will be placed outside the collection directory" not in fail_bad_tar.stderr + environment: + ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}/ansible_collections' + +- name: get result of failed collection install - {{ test_name }} + stat: + path: '{{ galaxy_dir }}/ansible_collections\suspicious' + register: fail_bad_tar_actual + +- name: assert result of failed collection install - {{ test_name }} + assert: + that: + - not fail_bad_tar_actual.stat.exists + +- name: install a collection from a URI - {{ test_name }} + command: ansible-galaxy collection install '{{ test_server }}custom/collections/namespace4-name-1.0.0.tar.gz' {{ galaxy_verbosity }} + register: install_uri + environment: + ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}/ansible_collections' + +- name: get result of install collection from a URI - {{ test_name }} + slurp: + path: '{{ galaxy_dir }}/ansible_collections/namespace4/name/MANIFEST.json' + register: install_uri_actual + +- name: assert install a collection from a URI - {{ test_name }} + assert: + that: + - '"Installing ''namespace4.name:1.0.0'' to" in install_uri.stdout' + - (install_uri_actual.content | b64decode | from_json).collection_info.version == '1.0.0' + +- name: fail to install a collection with an undefined URL - {{ test_name }} + command: ansible-galaxy collection install namespace5.name {{ galaxy_verbosity }} + register: fail_undefined_server + failed_when: '"No setting was provided for required configuration plugin_type: galaxy_server plugin: undefined" not in fail_undefined_server.stderr' + environment: + ANSIBLE_GALAXY_SERVER_LIST: undefined + +- name: install a collection with an empty server list - {{ test_name }} + command: ansible-galaxy collection install namespace5.name -s '{{ test_server }}' {{ galaxy_verbosity }} + register: install_empty_server_list + environment: + ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}/ansible_collections' + ANSIBLE_GALAXY_SERVER_LIST: '' + +- name: get result of a collection with an empty server list - {{ test_name }} + slurp: + path: '{{ galaxy_dir }}/ansible_collections/namespace5/name/MANIFEST.json' + register: install_empty_server_list_actual + +- name: assert install a collection with an empty server list - {{ test_name }} + assert: + that: + - '"Installing ''namespace5.name:1.0.0'' to" in install_empty_server_list.stdout' + - (install_empty_server_list_actual.content | b64decode | from_json).collection_info.version == '1.0.0' + +- name: create test requirements file with both roles and collections - {{ test_name }} + copy: + content: | + collections: + - namespace6.name + - name: namespace7.name + roles: + - skip.me + dest: '{{ galaxy_dir }}/ansible_collections/requirements-with-role.yml' + +# Need to run with -vvv to validate the roles will be skipped msg +- name: install collections only with requirements-with-role.yml - {{ test_name }} + command: ansible-galaxy collection install -r '{{ galaxy_dir }}/ansible_collections/requirements-with-role.yml' -s '{{ test_server }}' -vvv + register: install_req_collection + environment: + ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}/ansible_collections' + +- name: get result of install collections only with requirements-with-roles.yml - {{ test_name }} + slurp: + path: '{{ galaxy_dir }}/ansible_collections/{{ collection }}/name/MANIFEST.json' + register: install_req_collection_actual + loop_control: + loop_var: collection + loop: + - namespace6 + - namespace7 + +- name: assert install collections only with requirements-with-role.yml - {{ test_name }} + assert: + that: + - '"contains roles which will be ignored" in install_req_collection.stdout' + - '"Installing ''namespace6.name:1.0.0'' to" in install_req_collection.stdout' + - '"Installing ''namespace7.name:1.0.0'' to" in install_req_collection.stdout' + - (install_req_collection_actual.results[0].content | b64decode | from_json).collection_info.version == '1.0.0' + - (install_req_collection_actual.results[1].content | b64decode | from_json).collection_info.version == '1.0.0' + +- name: create test requirements file with just collections - {{ test_name }} + copy: + content: | + collections: + - namespace8.name + - name: namespace9.name + dest: '{{ galaxy_dir }}/ansible_collections/requirements.yaml' + +- name: install collections with ansible-galaxy install - {{ test_name }} + command: ansible-galaxy install -r '{{ galaxy_dir }}/ansible_collections/requirements.yaml' -s '{{ test_server }}' + register: install_req + environment: + ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}/ansible_collections' + +- name: get result of install collections with ansible-galaxy install - {{ test_name }} + slurp: + path: '{{ galaxy_dir }}/ansible_collections/{{ collection }}/name/MANIFEST.json' + register: install_req_actual + loop_control: + loop_var: collection + loop: + - namespace8 + - namespace9 + +- name: assert install collections with ansible-galaxy install - {{ test_name }} + assert: + that: + - '"Installing ''namespace8.name:1.0.0'' to" in install_req.stdout' + - '"Installing ''namespace9.name:1.0.0'' to" in install_req.stdout' + - (install_req_actual.results[0].content | b64decode | from_json).collection_info.version == '1.0.0' + - (install_req_actual.results[1].content | b64decode | from_json).collection_info.version == '1.0.0' + +- name: remove test collection install directory - {{ test_name }} + file: + path: '{{ galaxy_dir }}/ansible_collections' + state: absent + +- name: install collection with symlink - {{ test_name }} + command: ansible-galaxy collection install symlink.symlink -s '{{ test_server }}' {{ galaxy_verbosity }} + environment: + ANSIBLE_COLLECTIONS_PATHS: '{{ galaxy_dir }}/ansible_collections' + register: install_symlink + +- find: + paths: '{{ galaxy_dir }}/ansible_collections/symlink/symlink' + recurse: yes + file_type: any + +- name: get result of install collection with symlink - {{ test_name }} + stat: + path: '{{ galaxy_dir }}/ansible_collections/symlink/symlink/{{ path }}' + register: install_symlink_actual + loop_control: + loop_var: path + loop: + - REÅDMÈ.md-link + - docs/REÅDMÈ.md + - plugins/REÅDMÈ.md + - REÅDMÈ.md-outside-link + - docs-link + - docs-link/REÅDMÈ.md + +- name: assert install collection with symlink - {{ test_name }} + assert: + that: + - '"Installing ''symlink.symlink:1.0.0'' to" in install_symlink.stdout' + - install_symlink_actual.results[0].stat.islnk + - install_symlink_actual.results[0].stat.lnk_target == 'REÅDMÈ.md' + - install_symlink_actual.results[1].stat.islnk + - install_symlink_actual.results[1].stat.lnk_target == '../REÅDMÈ.md' + - install_symlink_actual.results[2].stat.islnk + - install_symlink_actual.results[2].stat.lnk_target == '../REÅDMÈ.md' + - install_symlink_actual.results[3].stat.isreg + - install_symlink_actual.results[4].stat.islnk + - install_symlink_actual.results[4].stat.lnk_target == 'docs' + - install_symlink_actual.results[5].stat.islnk + - install_symlink_actual.results[5].stat.lnk_target == '../REÅDMÈ.md' diff --git a/test/integration/targets/ansible-galaxy-collection/tasks/main.yml b/test/integration/targets/ansible-galaxy-collection/tasks/main.yml new file mode 100644 index 00000000..c4cc9edb --- /dev/null +++ b/test/integration/targets/ansible-galaxy-collection/tasks/main.yml @@ -0,0 +1,175 @@ +--- +- name: set some facts for tests + set_fact: + galaxy_dir: "{{ remote_tmp_dir }}/galaxy" + +- name: create scratch dir used for testing + file: + path: '{{ galaxy_dir }}/scratch' + state: directory + +- name: run ansible-galaxy collection init tests + import_tasks: init.yml + +- name: run ansible-galaxy collection build tests + import_tasks: build.yml + +- name: create test ansible.cfg that contains the Galaxy server list + template: + src: ansible.cfg.j2 + dest: '{{ galaxy_dir }}/ansible.cfg' + +- name: run ansible-galaxy collection publish tests for {{ test_name }} + include_tasks: publish.yml + vars: + test_name: '{{ item.name }}' + test_server: '{{ item.server }}' + with_items: + - name: galaxy + server: '{{ fallaxy_galaxy_server }}' + - name: automation_hub + server: '{{ fallaxy_ah_server }}' + +# We use a module for this so we can speed up the test time. +- name: setup test collections for install and download test + setup_collections: + server: '{{ fallaxy_galaxy_server }}' + token: '{{ fallaxy_token }}' + collections: + # Scenario to test out pre-release being ignored unless explicitly set and version pagination. + - namespace: namespace1 + name: name1 + version: 0.0.1 + - namespace: namespace1 + name: name1 + version: 0.0.2 + - namespace: namespace1 + name: name1 + version: 0.0.3 + - namespace: namespace1 + name: name1 + version: 0.0.4 + - namespace: namespace1 + name: name1 + version: 0.0.5 + - namespace: namespace1 + name: name1 + version: 0.0.6 + - namespace: namespace1 + name: name1 + version: 0.0.7 + - namespace: namespace1 + name: name1 + version: 0.0.8 + - namespace: namespace1 + name: name1 + version: 0.0.9 + - namespace: namespace1 + name: name1 + version: 0.0.10 + - namespace: namespace1 + name: name1 + version: 0.1.0 + - namespace: namespace1 + name: name1 + version: 1.0.0 + - namespace: namespace1 + name: name1 + version: 1.0.9 + - namespace: namespace1 + name: name1 + version: 1.1.0-beta.1 + + # Pad out number of namespaces for pagination testing + - namespace: namespace2 + name: name + - namespace: namespace3 + name: name + - namespace: namespace4 + name: name + - namespace: namespace5 + name: name + - namespace: namespace6 + name: name + - namespace: namespace7 + name: name + - namespace: namespace8 + name: name + - namespace: namespace9 + name: name + + # Complex dependency resolution + - namespace: parent_dep + name: parent_collection + dependencies: + child_dep.child_collection: '>=0.5.0,<1.0.0' + - namespace: child_dep + name: child_collection + version: 0.4.0 + - namespace: child_dep + name: child_collection + version: 0.5.0 + - namespace: child_dep + name: child_collection + version: 0.9.9 + dependencies: + child_dep.child_dep2: '!=1.2.3' + - namespace: child_dep + name: child_collection + - namespace: child_dep + name: child_dep2 + version: 1.2.2 + - namespace: child_dep + name: child_dep2 + version: 1.2.3 + + # Dep resolution failure + - namespace: fail_namespace + name: fail_collection + version: 2.1.2 + dependencies: + fail_dep.name: '0.0.5' + fail_dep2.name: '<0.0.5' + - namespace: fail_dep + name: name + version: '0.0.5' + dependencies: + fail_dep2.name: '>0.0.5' + - namespace: fail_dep2 + name: name + + # Symlink tests + - namespace: symlink + name: symlink + use_symlink: yes + +- name: run ansible-galaxy collection install tests for {{ test_name }} + include_tasks: install.yml + vars: + test_name: '{{ item.name }}' + test_server: '{{ item.server }}' + with_items: + - name: galaxy + server: '{{ fallaxy_galaxy_server }}' + - name: automation_hub + server: '{{ fallaxy_ah_server }}' + +# fake.fake does not exist but we check the output to ensure it checked all 3 +# servers defined in the config. We hardcode to -vvv as that's what level the +# message is shown +- name: test install fallback on server list + command: ansible-galaxy collection install fake.fake -vvv + ignore_errors: yes + environment: + ANSIBLE_CONFIG: '{{ galaxy_dir }}/ansible.cfg' + register: missing_fallback + +- name: assert test install fallback on server list + assert: + that: + - missing_fallback.rc == 1 + - '"Collection ''fake.fake'' is not available from server galaxy" in missing_fallback.stdout' + - '"Collection ''fake.fake'' is not available from server automation_hub" in missing_fallback.stdout' + +- name: run ansible-galaxy collection download tests + include_tasks: download.yml diff --git a/test/integration/targets/ansible-galaxy-collection/tasks/publish.yml b/test/integration/targets/ansible-galaxy-collection/tasks/publish.yml new file mode 100644 index 00000000..aa137304 --- /dev/null +++ b/test/integration/targets/ansible-galaxy-collection/tasks/publish.yml @@ -0,0 +1,46 @@ +--- +- name: fail to publish with no token - {{ test_name }} + command: ansible-galaxy collection publish ansible_test-my_collection-1.0.0.tar.gz -s {{ test_server }} {{ galaxy_verbosity }} + args: + chdir: '{{ galaxy_dir }}' + register: fail_no_token + failed_when: '"HTTP Code: 401" not in fail_no_token.stderr' + +- name: fail to publish with invalid token - {{ test_name }} + command: ansible-galaxy collection publish ansible_test-my_collection-1.0.0.tar.gz -s {{ test_server }} --token fail {{ galaxy_verbosity }} + args: + chdir: '{{ galaxy_dir }}' + register: fail_invalid_token + failed_when: '"HTTP Code: 401" not in fail_invalid_token.stderr' + +- name: publish collection - {{ test_name }} + command: ansible-galaxy collection publish ansible_test-my_collection-1.0.0.tar.gz -s {{ test_server }} --token {{ fallaxy_token }} {{ galaxy_verbosity }} + args: + chdir: '{{ galaxy_dir }}' + register: publish_collection + +- name: get result of publish collection - {{ test_name }} + uri: + url: '{{ test_server }}v2/collections/ansible_test/my_collection/versions/1.0.0/' + return_content: yes + register: publish_collection_actual + +- name: assert publish collection - {{ test_name }} + assert: + that: + - '"Collection has been successfully published and imported to the Galaxy server" in publish_collection.stdout' + - publish_collection_actual.json.metadata.name == 'my_collection' + - publish_collection_actual.json.metadata.namespace == 'ansible_test' + - publish_collection_actual.json.metadata.version == '1.0.0' + +- name: fail to publish existing collection version - {{ test_name }} + command: ansible-galaxy collection publish ansible_test-my_collection-1.0.0.tar.gz -s {{ test_server }} --token {{ fallaxy_token }} {{ galaxy_verbosity }} + args: + chdir: '{{ galaxy_dir }}' + register: fail_publish_existing + failed_when: '"Artifact already exists" not in fail_publish_existing.stderr' + +- name: reset published collections - {{ test_name }} + uri: + url: '{{ test_server }}custom/reset/' + method: POST diff --git a/test/integration/targets/ansible-galaxy-collection/templates/ansible.cfg.j2 b/test/integration/targets/ansible-galaxy-collection/templates/ansible.cfg.j2 new file mode 100644 index 00000000..74d36aac --- /dev/null +++ b/test/integration/targets/ansible-galaxy-collection/templates/ansible.cfg.j2 @@ -0,0 +1,10 @@ +[galaxy] +server_list=galaxy,automation_hub + +[galaxy_server.galaxy] +url={{ fallaxy_galaxy_server }} +token={{ fallaxy_token }} + +[galaxy_server.automation_hub] +url={{ fallaxy_ah_server }} +token={{ fallaxy_token }} diff --git a/test/integration/targets/ansible-galaxy-collection/vars/main.yml b/test/integration/targets/ansible-galaxy-collection/vars/main.yml new file mode 100644 index 00000000..bc006ca5 --- /dev/null +++ b/test/integration/targets/ansible-galaxy-collection/vars/main.yml @@ -0,0 +1 @@ +galaxy_verbosity: "{{ '' if not ansible_verbosity else '-' ~ ('v' * ansible_verbosity) }}" |