mirror of
https://github.com/ansible/ansible.git
synced 2026-07-28 16:15:12 +02:00
Switch to new galaxy-ng container setup (#86623)
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
minor_changes:
|
||||
- ansible-test - update galaxy_ng container to current version deployed to galaxy.ansible.com
|
||||
@@ -1,233 +0,0 @@
|
||||
#!/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 annotations
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: reset_pulp
|
||||
short_description: Resets pulp back to the initial state
|
||||
description:
|
||||
- See short_description
|
||||
options:
|
||||
pulp_api:
|
||||
description:
|
||||
- The Pulp API endpoint.
|
||||
required: yes
|
||||
type: str
|
||||
galaxy_ng_server:
|
||||
description:
|
||||
- The Galaxy NG API endpoint.
|
||||
required: yes
|
||||
type: str
|
||||
url_username:
|
||||
description:
|
||||
- The username to use when authenticating against Pulp.
|
||||
required: yes
|
||||
type: str
|
||||
url_password:
|
||||
description:
|
||||
- The password to use when authenticating against Pulp.
|
||||
required: yes
|
||||
type: str
|
||||
repositories:
|
||||
description:
|
||||
- A list of pulp repositories to create.
|
||||
- Galaxy NG expects a repository that matches C(GALAXY_API_DEFAULT_DISTRIBUTION_BASE_PATH) in
|
||||
C(/etc/pulp/settings.py) or the default of C(published).
|
||||
required: yes
|
||||
type: list
|
||||
elements: str
|
||||
namespaces:
|
||||
description:
|
||||
- A list of namespaces to create for Galaxy NG.
|
||||
required: yes
|
||||
type: list
|
||||
elements: str
|
||||
author:
|
||||
- Jordan Borean (@jborean93)
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
- name: reset pulp content
|
||||
reset_pulp:
|
||||
pulp_api: http://galaxy:24817
|
||||
galaxy_ng_server: http://galaxy/api/galaxy/
|
||||
url_username: username
|
||||
url_password: password
|
||||
repository: published
|
||||
namespaces:
|
||||
- namespace1
|
||||
- namespace2
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
#
|
||||
"""
|
||||
|
||||
import json
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.urls import fetch_url
|
||||
from ansible.module_utils.common.text.converters import to_text
|
||||
|
||||
|
||||
def invoke_api(module, url, method='GET', data=None, status_codes=None):
|
||||
status_codes = status_codes or [200]
|
||||
headers = {}
|
||||
if data:
|
||||
headers['Content-Type'] = 'application/json'
|
||||
data = json.dumps(data)
|
||||
|
||||
resp, info = fetch_url(module, url, method=method, data=data, headers=headers)
|
||||
if info['status'] not in status_codes:
|
||||
info['url'] = url
|
||||
module.fail_json(**info)
|
||||
|
||||
data = to_text(resp.read())
|
||||
if data:
|
||||
return json.loads(data)
|
||||
|
||||
|
||||
def delete_galaxy_namespace(namespace, module):
|
||||
""" Deletes the galaxy ng namespace specified. """
|
||||
ns_uri = '%sv3/namespaces/%s/' % (module.params['galaxy_ng_server'], namespace)
|
||||
invoke_api(module, ns_uri, method='DELETE', status_codes=[204])
|
||||
|
||||
|
||||
def delete_pulp_distribution(distribution, module):
|
||||
""" Deletes the pulp distribution at the URI specified. """
|
||||
task_info = invoke_api(module, distribution, method='DELETE', status_codes=[202])
|
||||
wait_pulp_task(task_info['task'], module)
|
||||
|
||||
|
||||
def delete_pulp_orphans(module):
|
||||
""" Deletes any orphaned pulp objects. """
|
||||
orphan_uri = module.params['galaxy_ng_server'] + 'pulp/api/v3/orphans/'
|
||||
task_info = invoke_api(module, orphan_uri, method='DELETE', status_codes=[202])
|
||||
wait_pulp_task(task_info['task'], module)
|
||||
|
||||
|
||||
def delete_pulp_repository(repository, module):
|
||||
""" Deletes the pulp repository at the URI specified. """
|
||||
task_info = invoke_api(module, repository, method='DELETE', status_codes=[202])
|
||||
wait_pulp_task(task_info['task'], module)
|
||||
|
||||
|
||||
def get_galaxy_namespaces(module):
|
||||
""" Gets a list of galaxy namespaces. """
|
||||
# No pagination has been implemented, shouldn't need unless we ever exceed 100 namespaces.
|
||||
namespace_uri = module.params['galaxy_ng_server'] + 'v3/namespaces/?limit=100&offset=0'
|
||||
ns_info = invoke_api(module, namespace_uri)
|
||||
|
||||
return [n['name'] for n in ns_info['data']]
|
||||
|
||||
|
||||
def get_pulp_distributions(module, distribution):
|
||||
""" Gets a list of all the pulp distributions. """
|
||||
distro_uri = module.params['galaxy_ng_server'] + 'pulp/api/v3/distributions/ansible/ansible/'
|
||||
distro_info = invoke_api(module, distro_uri + '?name=' + distribution)
|
||||
return [module.params['pulp_api'] + r['pulp_href'] for r in distro_info['results']]
|
||||
|
||||
|
||||
def get_pulp_repositories(module, repository):
|
||||
""" Gets a list of all the pulp repositories. """
|
||||
repo_uri = module.params['galaxy_ng_server'] + 'pulp/api/v3/repositories/ansible/ansible/'
|
||||
repo_info = invoke_api(module, repo_uri + '?name=' + repository)
|
||||
return [module.params['pulp_api'] + r['pulp_href'] for r in repo_info['results']]
|
||||
|
||||
|
||||
def get_repo_collections(repository, module):
|
||||
collections_uri = module.params['galaxy_ng_server'] + 'v3/plugin/ansible/content/' + repository + '/collections/index/'
|
||||
# status code 500 isn't really expected, an unhandled exception is causing this instead of a 404
|
||||
# See https://issues.redhat.com/browse/AAH-2329
|
||||
info = invoke_api(module, collections_uri + '?limit=100&offset=0', status_codes=[200, 500])
|
||||
if not info:
|
||||
return []
|
||||
return [module.params['pulp_api'] + c['href'] for c in info['data']]
|
||||
|
||||
|
||||
def delete_repo_collection(collection, module):
|
||||
task_info = invoke_api(module, collection, method='DELETE', status_codes=[202])
|
||||
wait_pulp_task(task_info['task'], module)
|
||||
|
||||
|
||||
def new_galaxy_namespace(name, module):
|
||||
""" Creates a new namespace in Galaxy NG. """
|
||||
ns_uri = module.params['galaxy_ng_server'] + 'v3/namespaces/ '
|
||||
data = {'name': name, 'groups': []}
|
||||
ns_info = invoke_api(module, ns_uri, method='POST', data=data, status_codes=[201])
|
||||
|
||||
return ns_info['id']
|
||||
|
||||
|
||||
def new_pulp_repository(name, module):
|
||||
""" Creates a new pulp repository. """
|
||||
repo_uri = module.params['galaxy_ng_server'] + 'pulp/api/v3/repositories/ansible/ansible/'
|
||||
# retain_repo_versions to work around https://issues.redhat.com/browse/AAH-2332
|
||||
data = {'name': name, 'retain_repo_versions': '1024'}
|
||||
repo_info = invoke_api(module, repo_uri, method='POST', data=data, status_codes=[201])
|
||||
|
||||
return repo_info['pulp_href']
|
||||
|
||||
|
||||
def new_pulp_distribution(name, base_path, repository, module):
|
||||
""" Creates a new pulp distribution for a repository. """
|
||||
distro_uri = module.params['galaxy_ng_server'] + 'pulp/api/v3/distributions/ansible/ansible/'
|
||||
data = {'name': name, 'base_path': base_path, 'repository': repository}
|
||||
task_info = invoke_api(module, distro_uri, method='POST', data=data, status_codes=[202])
|
||||
task_info = wait_pulp_task(task_info['task'], module)
|
||||
|
||||
return module.params['pulp_api'] + task_info['created_resources'][0]
|
||||
|
||||
|
||||
def wait_pulp_task(task, module):
|
||||
""" Waits for a pulp import task to finish. """
|
||||
while True:
|
||||
task_info = invoke_api(module, module.params['pulp_api'] + task)
|
||||
if task_info['finished_at'] is not None:
|
||||
break
|
||||
|
||||
return task_info
|
||||
|
||||
|
||||
def main():
|
||||
module_args = dict(
|
||||
pulp_api=dict(type='str', required=True),
|
||||
galaxy_ng_server=dict(type='str', required=True),
|
||||
url_username=dict(type='str', required=True),
|
||||
url_password=dict(type='str', required=True, no_log=True),
|
||||
repositories=dict(type='list', elements='str', required=True),
|
||||
namespaces=dict(type='list', elements='str', required=True),
|
||||
)
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=module_args,
|
||||
supports_check_mode=False
|
||||
)
|
||||
module.params['force_basic_auth'] = True
|
||||
|
||||
# It may be due to the process of cleaning up orphans, but we cannot delete the namespace
|
||||
# while a collection still exists, so this is just a new safety to nuke all collections
|
||||
# first
|
||||
for repository in module.params['repositories']:
|
||||
[delete_repo_collection(c, module) for c in get_repo_collections(repository, module)]
|
||||
|
||||
for repository in module.params['repositories']:
|
||||
[delete_pulp_distribution(d, module) for d in get_pulp_distributions(module, repository)]
|
||||
[delete_pulp_repository(r, module) for r in get_pulp_repositories(module, repository)]
|
||||
delete_pulp_orphans(module)
|
||||
[delete_galaxy_namespace(n, module) for n in get_galaxy_namespaces(module)]
|
||||
|
||||
for repository in module.params['repositories']:
|
||||
repo_href = new_pulp_repository(repository, module)
|
||||
new_pulp_distribution(repository, repository, repo_href, module)
|
||||
[new_galaxy_namespace(n, module) for n in module.params['namespaces']]
|
||||
|
||||
module.exit_json(changed=True)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,291 +0,0 @@
|
||||
#!/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 annotations
|
||||
|
||||
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: '{}'
|
||||
use_symlink:
|
||||
description:
|
||||
- Create a symlink in the collection.
|
||||
type: bool
|
||||
default: False
|
||||
runtime:
|
||||
description:
|
||||
- File content for meta/runtime.yml.
|
||||
type: str
|
||||
default: 'requires_ansible: ">=2.9"'
|
||||
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 datetime
|
||||
import os
|
||||
import subprocess
|
||||
import tarfile
|
||||
import tempfile
|
||||
import yaml
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.common.text.converters import to_bytes
|
||||
from functools import partial
|
||||
from multiprocessing import dummy as threading
|
||||
from multiprocessing import TimeoutError, Lock
|
||||
|
||||
|
||||
COLLECTIONS_BUILD_AND_PUBLISH_TIMEOUT = 300
|
||||
LOCK = Lock()
|
||||
|
||||
|
||||
def publish_collection(module, collection):
|
||||
namespace = collection['namespace']
|
||||
name = collection['name']
|
||||
version = collection['version']
|
||||
dependencies = collection['dependencies']
|
||||
use_symlink = collection['use_symlink']
|
||||
runtime = collection['runtime']
|
||||
|
||||
result = {}
|
||||
collection_dir = os.path.join(module.tmpdir, "%s-%s-%s" % (namespace, name, version))
|
||||
b_collection_dir = to_bytes(collection_dir, errors='surrogate_or_strict')
|
||||
os.mkdir(b_collection_dir)
|
||||
os.mkdir(os.path.join(b_collection_dir, b'meta'))
|
||||
|
||||
with open(os.path.join(b_collection_dir, b'README.md'), mode='wb') as fd:
|
||||
fd.write(b"Collection readme")
|
||||
|
||||
galaxy_meta = {
|
||||
'namespace': namespace,
|
||||
'name': name,
|
||||
'version': version,
|
||||
'readme': 'README.md',
|
||||
'authors': ['Collection author <name@email.com'],
|
||||
'dependencies': dependencies,
|
||||
'license': ['GPL-3.0-or-later'],
|
||||
'repository': 'https://ansible.com/',
|
||||
}
|
||||
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 open(os.path.join(b_collection_dir, b'meta/runtime.yml'), mode='wb') as fd:
|
||||
fd.write(to_bytes(runtime))
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode='wb') as temp_fd:
|
||||
temp_fd.write(b"data")
|
||||
|
||||
if 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' % (namespace, name, version)
|
||||
collection_path = os.path.join(collection_dir, release_filename)
|
||||
rc, stdout, stderr = module.run_command(['ansible-galaxy', 'collection', 'build'], cwd=collection_dir)
|
||||
result['build'] = {
|
||||
'rc': rc,
|
||||
'stdout': stdout,
|
||||
'stderr': stderr,
|
||||
}
|
||||
|
||||
if module.params['signature_dir'] is not None:
|
||||
# To test user-provided signatures, we need to sign the MANIFEST.json before publishing
|
||||
|
||||
# Extract the tarfile to sign the MANIFEST.json
|
||||
with tarfile.open(collection_path, mode='r') as collection_tar:
|
||||
collection_tar.extractall(path=os.path.join(collection_dir, '%s-%s-%s' % (namespace, name, version)), filter='data')
|
||||
|
||||
manifest_path = os.path.join(collection_dir, '%s-%s-%s' % (namespace, name, version), 'MANIFEST.json')
|
||||
signature_path = os.path.join(module.params['signature_dir'], '%s-%s-%s-MANIFEST.json.asc' % (namespace, name, version))
|
||||
sign_manifest(signature_path, manifest_path, module, result)
|
||||
|
||||
# Create the tarfile containing the signed MANIFEST.json
|
||||
with tarfile.open(collection_path, "w:gz") as tar:
|
||||
tar.add(os.path.join(collection_dir, '%s-%s-%s' % (namespace, name, version)), arcname=os.path.sep)
|
||||
|
||||
publish_args = ['ansible-galaxy', 'collection', 'publish', collection_path, '--server', module.params['server']]
|
||||
if module.params['token']:
|
||||
publish_args.extend(['--token', module.params['token']])
|
||||
|
||||
with LOCK:
|
||||
# lock publish operations since Galaxy publish DB key generation is not mutex'd
|
||||
rc, stdout, stderr = module.run_command(publish_args)
|
||||
|
||||
result['publish'] = {
|
||||
'rc': rc,
|
||||
'stdout': stdout,
|
||||
'stderr': stderr,
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def sign_manifest(signature_path, manifest_path, module, collection_setup_result):
|
||||
collection_setup_result['gpg_detach_sign'] = {'signature_path': signature_path}
|
||||
|
||||
status_fd_read, status_fd_write = os.pipe()
|
||||
gpg_cmd = [
|
||||
"gpg",
|
||||
"--batch",
|
||||
"--pinentry-mode",
|
||||
"loopback",
|
||||
"--yes",
|
||||
"--homedir",
|
||||
module.params['signature_dir'],
|
||||
"--detach-sign",
|
||||
"--armor",
|
||||
"--output",
|
||||
signature_path,
|
||||
manifest_path,
|
||||
]
|
||||
try:
|
||||
p = subprocess.Popen(
|
||||
gpg_cmd,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
pass_fds=(status_fd_write,),
|
||||
encoding='utf8',
|
||||
)
|
||||
except (FileNotFoundError, subprocess.SubprocessError) as err:
|
||||
collection_setup_result['gpg_detach_sign']['error'] = "Failed during GnuPG verification with command '{gpg_cmd}': {err}".format(
|
||||
gpg_cmd=gpg_cmd, err=err
|
||||
)
|
||||
else:
|
||||
stdout, stderr = p.communicate()
|
||||
collection_setup_result['gpg_detach_sign']['stdout'] = stdout
|
||||
if stderr:
|
||||
error = "Failed during GnuPG verification with command '{gpg_cmd}':\n{stderr}".format(gpg_cmd=gpg_cmd, stderr=stderr)
|
||||
collection_setup_result['gpg_detach_sign']['error'] = error
|
||||
finally:
|
||||
os.close(status_fd_write)
|
||||
|
||||
|
||||
def run_module():
|
||||
module_args = dict(
|
||||
server=dict(type='str', required=True),
|
||||
token=dict(type='str'),
|
||||
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),
|
||||
runtime=dict(type='str', default='requires_ansible: ">=2.9"'),
|
||||
),
|
||||
),
|
||||
signature_dir=dict(type='path', default=None),
|
||||
)
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=module_args,
|
||||
supports_check_mode=False
|
||||
)
|
||||
|
||||
start = datetime.datetime.now()
|
||||
result = dict(changed=True, results=[], start=str(start))
|
||||
|
||||
pool = threading.Pool(1)
|
||||
publish_func = partial(publish_collection, module)
|
||||
try:
|
||||
result['results'] = pool.map_async(
|
||||
publish_func, module.params['collections'],
|
||||
).get(timeout=COLLECTIONS_BUILD_AND_PUBLISH_TIMEOUT)
|
||||
except TimeoutError as timeout_err:
|
||||
module.fail_json(
|
||||
'Timed out waiting for collections to be provisioned.',
|
||||
)
|
||||
|
||||
failed = bool(sum(
|
||||
r['build']['rc'] + r['publish']['rc'] for r in result['results']
|
||||
))
|
||||
|
||||
end = datetime.datetime.now()
|
||||
delta = end - start
|
||||
module.exit_json(failed=failed, end=str(end), delta=str(delta), **result)
|
||||
|
||||
|
||||
def main():
|
||||
run_module()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -4,6 +4,16 @@
|
||||
path: '{{ galaxy_dir }}/ansible_collections'
|
||||
state: directory
|
||||
|
||||
- name: install simple collection from specific server
|
||||
command: ansible-galaxy collection install namespace1.name1 -s galaxy_ng_token -vvvv
|
||||
environment:
|
||||
ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}/ansible_collections'
|
||||
|
||||
- name: remove collection
|
||||
file:
|
||||
path: '{{ galaxy_dir }}/ansible_collections/namespace1/name1'
|
||||
state: absent
|
||||
|
||||
- name: install simple collection from first accessible server
|
||||
command: ansible-galaxy collection install namespace1.name1 -vvvv
|
||||
environment:
|
||||
@@ -30,7 +40,6 @@
|
||||
- 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'
|
||||
- 'from_first_good_server.stdout|regex_findall("has not signed namespace1\.name1")|length == 1'
|
||||
- "info_dir is is_dir"
|
||||
vars:
|
||||
info_dir: "{{ galaxy_dir }}/ansible_collections/namespace1.name1-1.0.9.info"
|
||||
@@ -178,9 +187,9 @@
|
||||
|
||||
- name: Find artifact url for namespace3.name
|
||||
uri:
|
||||
url: '{{ test_api_server }}v3/plugin/ansible/content/primary/collections/index/namespace3/name/versions/1.0.0/'
|
||||
user: '{{ pulp_user }}'
|
||||
password: '{{ pulp_password }}'
|
||||
url: '{{ test_api_server }}/v3/plugin/ansible/content/published/collections/index/namespace3/name/versions/1.0.0/'
|
||||
user: '{{ galaxy_user }}'
|
||||
password: '{{ galaxy_password }}'
|
||||
force_basic_auth: true
|
||||
register: artifact_url_response
|
||||
|
||||
@@ -188,6 +197,9 @@
|
||||
get_url:
|
||||
url: '{{ artifact_url_response.json.download_url }}'
|
||||
dest: '{{ galaxy_dir }}/namespace3.tar.gz'
|
||||
url_username: '{{ galaxy_user }}'
|
||||
url_password: '{{ galaxy_password }}'
|
||||
force_basic_auth: true
|
||||
|
||||
- name: install a collection from a tarball - {{ test_id }}
|
||||
command: ansible-galaxy collection install '{{ galaxy_dir }}/namespace3.tar.gz' {{ galaxy_verbosity }}
|
||||
@@ -305,16 +317,8 @@
|
||||
that:
|
||||
- not fail_bad_tar_actual.stat.exists
|
||||
|
||||
- name: Find artifact url for namespace4.name
|
||||
uri:
|
||||
url: '{{ test_api_server }}v3/plugin/ansible/content/primary/collections/index/namespace4/name/versions/1.0.0/'
|
||||
user: '{{ pulp_user }}'
|
||||
password: '{{ pulp_password }}'
|
||||
force_basic_auth: true
|
||||
register: artifact_url_response
|
||||
|
||||
- name: install a collection from a URI - {{ test_id }}
|
||||
command: ansible-galaxy collection install {{ artifact_url_response.json.download_url}} {{ galaxy_verbosity }}
|
||||
command: ansible-galaxy collection install {{ amanda }}/artifacts/namespace4-name-1.0.0.tar.gz {{ galaxy_verbosity }}
|
||||
register: install_uri
|
||||
environment:
|
||||
ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}/ansible_collections'
|
||||
@@ -438,6 +442,30 @@
|
||||
- namespace8
|
||||
- namespace9
|
||||
|
||||
- name: store signatures
|
||||
copy:
|
||||
dest: "{{ gpg_homedir }}/{{ item.namespace }}-{{ item.name }}-{{ item.version }}-MANIFEST.json.asc"
|
||||
content: "{{ version_data.signatures.0.signature }}"
|
||||
loop:
|
||||
- namespace: namespace1
|
||||
name: name1
|
||||
version: "1.0.0"
|
||||
- namespace: namespace1
|
||||
name: name1
|
||||
version: "1.0.9"
|
||||
- namespace: namespace2
|
||||
name: name
|
||||
version: "1.0.0"
|
||||
- namespace: namespace7
|
||||
name: name
|
||||
version: "1.0.0"
|
||||
- namespace: namespace9
|
||||
name: name
|
||||
version: "1.0.0"
|
||||
vars:
|
||||
version_url: '{{ galaxy_ng_server }}/v3/collections/{{ item.namespace }}/{{ item.name }}/versions/{{ item.version }}/'
|
||||
version_data: "{{ lookup('url', version_url, username=galaxy_user, password=galaxy_password, force_basic_auth=true)|from_json }}"
|
||||
|
||||
- name: rewrite requirements file with collections and signatures
|
||||
copy:
|
||||
content: |
|
||||
@@ -751,13 +779,36 @@
|
||||
- set_fact:
|
||||
cache_version_build: '{{ (cache_version_build | int) + 1 }}'
|
||||
|
||||
- name: publish update for cache.cache test
|
||||
setup_collections:
|
||||
server: galaxy_ng
|
||||
collections:
|
||||
- namespace: cache
|
||||
name: cache
|
||||
version: 1.0.{{ cache_version_build }}
|
||||
- name: copy cache collection to tmpdir
|
||||
copy:
|
||||
src: '{{ galaxy_dir }}/ansible_collections/cache/cache/'
|
||||
dest: '{{ galaxy_dir }}/cache-src'
|
||||
remote_src: true
|
||||
|
||||
- name: write galaxy.yml file
|
||||
copy:
|
||||
dest: '{{ galaxy_dir }}/cache-src/galaxy.yml'
|
||||
content: |
|
||||
{{ (lookup('file', manifest)|from_json).collection_info|to_nice_yaml }}
|
||||
vars:
|
||||
manifest: '{{ galaxy_dir }}/cache-src/MANIFEST.json'
|
||||
|
||||
- name: update cache.cache src version
|
||||
replace:
|
||||
path: '{{ galaxy_dir }}/cache-src/galaxy.yml'
|
||||
regexp: '^version:.*$'
|
||||
replace: 'version: 1.0.{{ cache_version_build }}'
|
||||
|
||||
- name: build updated cache.cache
|
||||
command:
|
||||
cmd: ansible-galaxy collection build
|
||||
chdir: '{{ galaxy_dir }}/cache-src'
|
||||
register: cache_cache_build
|
||||
|
||||
- name: publish updated cache.cache
|
||||
command:
|
||||
cmd: ansible-galaxy collection publish -s galaxy_ng "{{ cache_cache_build.stdout_lines|last|split|last }}"
|
||||
chdir: '{{ galaxy_dir }}/cache-src'
|
||||
|
||||
- name: make sure the cache version list is ignored on a collection version change - {{ test_id }}
|
||||
command: ansible-galaxy collection install cache.cache -s '{{ test_name }}' --force -vvv
|
||||
@@ -1017,6 +1068,7 @@
|
||||
- name: install simple collection with invalid detached signature
|
||||
command: ansible-galaxy collection install namespace1.name1 -vvvv {{ signature_options }}
|
||||
environment:
|
||||
ANSIBLE_GALAXY_REQUIRED_VALID_SIGNATURE_COUNT: "all"
|
||||
ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}/ansible_collections'
|
||||
ANSIBLE_NOCOLOR: True
|
||||
ANSIBLE_FORCE_COLOR: False
|
||||
@@ -1067,6 +1119,7 @@
|
||||
- name: use lenient signature verification (default) without providing signatures
|
||||
command: ansible-galaxy collection install namespace1.name1:1.0.0 -vvvv --keyring {{ gpg_homedir }}/pubring.kbx --force
|
||||
environment:
|
||||
ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}/ansible_collections'
|
||||
ANSIBLE_GALAXY_REQUIRED_VALID_SIGNATURE_COUNT: "all"
|
||||
register: missing_signature
|
||||
|
||||
@@ -1077,8 +1130,9 @@
|
||||
- '"namespace1.name1:1.0.0 was installed successfully" in missing_signature.stdout'
|
||||
- '"Signature verification failed for ''namespace1.name1'': no successful signatures" not in missing_signature.stdout'
|
||||
|
||||
# These steps use secondary (amanda) as it has no signatures provided
|
||||
- name: use strict signature verification without providing signatures
|
||||
command: ansible-galaxy collection install namespace1.name1:1.0.0 -vvvv --keyring {{ gpg_homedir }}/pubring.kbx --force
|
||||
command: ansible-galaxy collection install -s secondary namespace1.name1:1.0.0 -vvvv --keyring {{ gpg_homedir }}/pubring.kbx --force
|
||||
environment:
|
||||
ANSIBLE_GALAXY_REQUIRED_VALID_SIGNATURE_COUNT: "+1"
|
||||
ignore_errors: yes
|
||||
|
||||
@@ -14,34 +14,28 @@
|
||||
- name: run ansible-galaxy collection build tests
|
||||
import_tasks: build.yml
|
||||
|
||||
# The pulp container has a long start up time
|
||||
# The first task to interact with pulp needs to wait until it responds appropriately
|
||||
- name: list pulp distributions
|
||||
- name: get pulp repository list
|
||||
uri:
|
||||
url: '{{ pulp_api }}/pulp/api/v3/distributions/ansible/ansible/'
|
||||
status_code:
|
||||
- 200
|
||||
user: '{{ pulp_user }}'
|
||||
password: '{{ pulp_password }}'
|
||||
url: '{{ galaxy_ng_server }}/pulp/api/v3/repositories/ansible/ansible/'
|
||||
user: '{{ galaxy_user }}'
|
||||
password: '{{ galaxy_password }}'
|
||||
force_basic_auth: true
|
||||
register: pulp_distributions
|
||||
until: pulp_distributions is successful
|
||||
delay: 1
|
||||
retries: 60
|
||||
register: pulp_repo_list
|
||||
|
||||
- name: configure pulp
|
||||
include_tasks: pulp.yml
|
||||
- set_fact:
|
||||
pulp_published_repo: '{{ pulp_repo_list.json.results|selectattr("name", "eq", "published")|first }}'
|
||||
pulp_rejected_repo: '{{ pulp_repo_list.json.results|selectattr("name", "eq", "rejected")|first }}'
|
||||
|
||||
- name: Get galaxy_ng token
|
||||
uri:
|
||||
url: '{{ galaxy_ng_server }}v3/auth/token/'
|
||||
url: '{{ galaxy_ng_server }}/v3/auth/token/'
|
||||
method: POST
|
||||
body_format: json
|
||||
body: {}
|
||||
status_code:
|
||||
- 200
|
||||
user: '{{ pulp_user }}'
|
||||
password: '{{ pulp_password }}'
|
||||
user: '{{ galaxy_user }}'
|
||||
password: '{{ galaxy_password }}'
|
||||
force_basic_auth: true
|
||||
register: galaxy_ng_token
|
||||
|
||||
@@ -79,19 +73,6 @@
|
||||
|
||||
- include_tasks: setup_gpg.yml
|
||||
|
||||
# We use a module for this so we can speed up the test time.
|
||||
# For pulp interactions, we only upload to galaxy_ng which shares
|
||||
# the same repo and distribution with pulp_ansible
|
||||
# However, we use galaxy_ng only, since collections are unique across
|
||||
# pulp repositories, and galaxy_ng maintains a 2nd list of published collections
|
||||
- name: setup test collections for install and download test
|
||||
setup_collections:
|
||||
server: galaxy_ng
|
||||
collections: '{{ collection_list }}'
|
||||
signature_dir: '{{ gpg_homedir }}'
|
||||
environment:
|
||||
ANSIBLE_CONFIG: '{{ galaxy_dir }}/ansible.cfg'
|
||||
|
||||
# Stores the cached test version number index as we run install many times
|
||||
- set_fact:
|
||||
cache_version_build: 0
|
||||
@@ -134,18 +115,6 @@
|
||||
# In this test suite because the bug relies on the dep having versions on a Galaxy server
|
||||
include_tasks: virtual_direct_requests.yml
|
||||
|
||||
- name: publish collection with a dep on another server
|
||||
setup_collections:
|
||||
server: secondary
|
||||
collections:
|
||||
- namespace: secondary
|
||||
name: name
|
||||
# parent_dep.parent_collection does not exist on the secondary server
|
||||
dependencies:
|
||||
parent_dep.parent_collection: '1.0.0'
|
||||
environment:
|
||||
ANSIBLE_CONFIG: '{{ galaxy_dir }}/ansible.cfg'
|
||||
|
||||
- name: install collection with dep on another server
|
||||
command: ansible-galaxy collection install secondary.name -vvv # 3 -v's will show the source in the stdout
|
||||
register: install_cross_dep
|
||||
@@ -204,7 +173,7 @@
|
||||
ANSIBLE_CONFIG: '{{ galaxy_dir }}/ansible.cfg'
|
||||
vars:
|
||||
test_api_fallback: 'secondary'
|
||||
test_api_fallback_versions: 'v3, pulp-v3, v1'
|
||||
test_api_fallback_versions: 'v3'
|
||||
test_name: 'galaxy_ng'
|
||||
|
||||
- name: run ansible-galaxy collection list tests
|
||||
|
||||
@@ -10,10 +10,10 @@
|
||||
|
||||
- name: get result of publish collection - {{ test_name }}
|
||||
uri:
|
||||
url: '{{ test_api_server }}v3/plugin/ansible/content/primary/collections/index/ansible_test/my_collection/versions/1.0.0/'
|
||||
url: '{{ test_api_server }}/v3/plugin/ansible/content/published/collections/index/ansible_test/my_collection/versions/1.0.0/'
|
||||
return_content: yes
|
||||
user: '{{ pulp_user }}'
|
||||
password: '{{ pulp_password }}'
|
||||
user: '{{ galaxy_user }}'
|
||||
password: '{{ galaxy_password }}'
|
||||
force_basic_auth: true
|
||||
register: publish_collection_actual
|
||||
|
||||
@@ -24,13 +24,3 @@
|
||||
- publish_collection_actual.json.collection.name == 'my_collection'
|
||||
- publish_collection_actual.json.namespace.name == 'ansible_test'
|
||||
- publish_collection_actual.json.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_name }} {{ galaxy_verbosity }}
|
||||
args:
|
||||
chdir: '{{ galaxy_dir }}'
|
||||
register: fail_publish_existing
|
||||
failed_when: fail_publish_existing is not failed
|
||||
|
||||
- name: reset published collections - {{ test_name }}
|
||||
include_tasks: pulp.yml
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
# These tasks configure pulp/pulp_ansible so that we can use the container
|
||||
# This will also reset pulp between iterations
|
||||
# A module is used to make the tests run quicker as this will send lots of API requests.
|
||||
- name: reset pulp content
|
||||
reset_pulp:
|
||||
pulp_api: '{{ pulp_api }}'
|
||||
galaxy_ng_server: '{{ galaxy_ng_server }}'
|
||||
url_username: '{{ pulp_user }}'
|
||||
url_password: '{{ pulp_password }}'
|
||||
repositories: '{{ pulp_repositories }}'
|
||||
namespaces: '{{ collection_list|map(attribute="namespace")|unique + publish_namespaces }}'
|
||||
@@ -1,14 +1,10 @@
|
||||
- name: generate revocation certificate
|
||||
expect:
|
||||
command: "gpg --homedir {{ gpg_homedir }} --pinentry-mode loopback --output {{ gpg_homedir }}/revoke.asc --gen-revoke {{ fingerprint }}"
|
||||
responses:
|
||||
"Create a revocation certificate for this key": "y"
|
||||
"Please select the reason for the revocation": "0"
|
||||
"Enter an optional description": ""
|
||||
"Is this okay": "y"
|
||||
- name: list keys for debugging
|
||||
command: "gpg --no-tty --homedir {{ gpg_homedir }} --list-keys"
|
||||
|
||||
- name: revoke key
|
||||
command: "gpg --no-tty --homedir {{ gpg_homedir }} --import {{ gpg_homedir }}/revoke.asc"
|
||||
command:
|
||||
cmd: "gpg --no-tty --homedir {{ gpg_homedir }} --import"
|
||||
stdin: '{{ pulp_rejected_repo.gpgkey }}'
|
||||
|
||||
- name: list keys for debugging
|
||||
command: "gpg --no-tty --homedir {{ gpg_homedir }} --list-keys {{ gpg_user }}"
|
||||
command: "gpg --no-tty --homedir {{ gpg_homedir }} --list-keys"
|
||||
|
||||
@@ -32,14 +32,18 @@
|
||||
command: whoami
|
||||
register: user
|
||||
|
||||
- name: generate key for user with gpg
|
||||
command: "gpg --no-tty --homedir {{ gpg_homedir }} --passphrase '' --pinentry-mode loopback --quick-gen-key {{ user.stdout }} default default"
|
||||
- name: import public key
|
||||
command:
|
||||
cmd: gpg --no-tty --homedir {{ gpg_homedir }} --import
|
||||
stdin: '{{ pulp_published_repo.gpgkey }}'
|
||||
register: gpg_import
|
||||
|
||||
- name: list gpg keys for user
|
||||
command: "gpg --no-tty --homedir {{ gpg_homedir }} --list-keys {{ user.stdout }}"
|
||||
- command: gpg --no-tty --homedir {{ gpg_homedir }} --list-keys {{ keyid }}
|
||||
vars:
|
||||
keyid: "{{ (gpg_import.stderr_lines | select('match', 'gpg: key ') | first | split).2 | replace(':', '') }}"
|
||||
register: gpg_list_keys
|
||||
|
||||
- name: save gpg user and fingerprint of new key
|
||||
set_fact:
|
||||
gpg_user: "{{ user.stdout }}"
|
||||
gpg_user: ansible-test
|
||||
fingerprint: "{{ gpg_list_keys.stdout_lines[1] | trim }}"
|
||||
|
||||
@@ -293,8 +293,9 @@
|
||||
|
||||
# TODO: add a test for offline Galaxy signature metadata
|
||||
|
||||
# These steps use secondary (amanda) as it has no signatures provided
|
||||
- name: install a collection that was signed by setup_collections
|
||||
command: ansible-galaxy collection install namespace1.name1:1.0.0
|
||||
command: ansible-galaxy collection install -s secondary namespace1.name1:1.0.0
|
||||
|
||||
- name: verify the installed collection with a detached signature
|
||||
command: ansible-galaxy collection verify namespace1.name1:1.0.0 {{ galaxy_verbosity }} {{ signature_options }}
|
||||
@@ -320,6 +321,7 @@
|
||||
environment:
|
||||
ANSIBLE_NOCOLOR: True
|
||||
ANSIBLE_FORCE_COLOR: False
|
||||
ANSIBLE_GALAXY_REQUIRED_VALID_SIGNATURE_COUNT: all
|
||||
|
||||
- assert:
|
||||
that:
|
||||
@@ -346,6 +348,7 @@
|
||||
environment:
|
||||
ANSIBLE_NOCOLOR: True
|
||||
ANSIBLE_FORCE_COLOR: False
|
||||
ANSIBLE_GALAXY_REQUIRED_VALID_SIGNATURE_COUNT: all
|
||||
|
||||
- assert:
|
||||
that:
|
||||
@@ -450,7 +453,7 @@
|
||||
verify_stdout: "{{ verify.stdout | regex_replace('\"') | regex_replace('\\n') | regex_replace(' ', ' ') }}"
|
||||
|
||||
- name: use lenient signature verification (default) without providing signatures
|
||||
command: ansible-galaxy collection verify namespace1.name1:1.0.0 -vvvv --keyring {{ gpg_homedir }}/pubring.kbx
|
||||
command: ansible-galaxy collection verify namespace1.name1:1.0.0 -s secondary -vvvv --keyring {{ gpg_homedir }}/pubring.kbx
|
||||
environment:
|
||||
ANSIBLE_GALAXY_REQUIRED_VALID_SIGNATURE_COUNT: "1"
|
||||
register: verify
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
[galaxy]
|
||||
# Ensures subsequent unstable reruns don't use the cached information causing another failure
|
||||
cache_dir={{ remote_tmp_dir }}/galaxy_cache
|
||||
server_list=offline,galaxy_ng,secondary
|
||||
server_list=offline,galaxy_ng,secondary,galaxy_ng_token
|
||||
|
||||
[galaxy_server.offline]
|
||||
url={{ offline_server }}
|
||||
|
||||
[galaxy_server.galaxy_ng]
|
||||
url={{ galaxy_ng_server }}content/primary/
|
||||
token={{ galaxy_ng_token.json.token }}
|
||||
url={{ galaxy_ng_server }}
|
||||
username={{ galaxy_user }}
|
||||
password={{ galaxy_password }}
|
||||
|
||||
[galaxy_server.secondary]
|
||||
url={{ galaxy_ng_server }}content/secondary/
|
||||
username={{ pulp_user }}
|
||||
password={{ pulp_password }}
|
||||
url={{ amanda }}
|
||||
|
||||
[galaxy_server.galaxy_ng_token]
|
||||
# This server is last to prevent it from being used by tests that don't ask for it
|
||||
url={{ galaxy_ng_server }}
|
||||
token={{ galaxy_ng_token.json.token }}
|
||||
|
||||
@@ -13,231 +13,3 @@ supported_resolvelib_versions:
|
||||
unsupported_resolvelib_versions:
|
||||
- "0.2.0" # Fails on import
|
||||
- "0.5.3"
|
||||
|
||||
pulp_repositories:
|
||||
- primary
|
||||
- secondary
|
||||
|
||||
publish_namespaces:
|
||||
- ansible_test
|
||||
- secondary
|
||||
|
||||
collection_list:
|
||||
# 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
|
||||
version: 0.0.1
|
||||
dependencies:
|
||||
child_dep.child_collection: '<0.5.0'
|
||||
- namespace: parent_dep
|
||||
name: parent_collection
|
||||
version: 1.0.0
|
||||
dependencies:
|
||||
child_dep.child_collection: '>=0.5.0,<1.0.0'
|
||||
- namespace: parent_dep
|
||||
name: parent_collection
|
||||
version: 1.1.0
|
||||
dependencies:
|
||||
child_dep.child_collection: '>=0.9.9,<=1.0.0'
|
||||
- namespace: parent_dep
|
||||
name: parent_collection
|
||||
version: 2.0.0
|
||||
dependencies:
|
||||
child_dep.child_collection: '>=1.0.0'
|
||||
- namespace: parent_dep2
|
||||
name: parent_collection
|
||||
dependencies:
|
||||
child_dep.child_collection: '0.5.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
|
||||
version: 1.0.0
|
||||
dependencies:
|
||||
child_dep.child_dep2: '!=1.2.3'
|
||||
- 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
|
||||
|
||||
# Caching update tests
|
||||
- namespace: cache
|
||||
name: cache
|
||||
version: 1.0.0
|
||||
|
||||
# Dep with beta version
|
||||
- namespace: dep_with_beta
|
||||
name: parent
|
||||
dependencies:
|
||||
namespace1.name1: '*'
|
||||
|
||||
# non-prerelease is published to test that installing
|
||||
# the pre-release from SCM doesn't accidentally prefer indirect
|
||||
# dependencies from Galaxy
|
||||
- namespace: test_prereleases
|
||||
name: collection2
|
||||
version: 1.0.0
|
||||
|
||||
- namespace: dev_and_stables_ns
|
||||
name: dev_and_stables_name
|
||||
version: 1.2.3-dev0
|
||||
- namespace: dev_and_stables_ns
|
||||
name: dev_and_stables_name
|
||||
version: 1.2.4
|
||||
|
||||
- namespace: ns_with_wildcard_dep
|
||||
name: name_with_wildcard_dep
|
||||
version: 5.6.7-beta.3
|
||||
dependencies:
|
||||
dev_and_stables_ns.dev_and_stables_name: >-
|
||||
*
|
||||
- namespace: ns_with_dev_dep
|
||||
name: name_with_dev_dep
|
||||
version: 6.7.8
|
||||
dependencies:
|
||||
dev_and_stables_ns.dev_and_stables_name: 1.2.3-dev0
|
||||
|
||||
- namespace: rc_meta_ns_with_transitive_dev_dep
|
||||
name: rc_meta_name_with_transitive_dev_dep
|
||||
version: 2.4.5-rc5
|
||||
dependencies:
|
||||
ns_with_dev_dep.name_with_dev_dep: >-
|
||||
*
|
||||
- namespace: meta_ns_with_transitive_wildcard_dep
|
||||
name: meta_name_with_transitive_wildcard_dep
|
||||
version: 4.5.6
|
||||
dependencies:
|
||||
ns_with_wildcard_dep.name_with_wildcard_dep: 5.6.7-beta.3
|
||||
|
||||
- namespace: ns_requires_ansible
|
||||
name: name_requires_ansible
|
||||
version: 1.0.0
|
||||
runtime: |-
|
||||
requires_ansible: "<1.1"
|
||||
|
||||
- namespace: ns_requires_ansible
|
||||
name: dependency
|
||||
version: 1.0.0
|
||||
|
||||
- namespace: ns_requires_ansible
|
||||
name: dependency
|
||||
version: 2.0.0
|
||||
dependencies:
|
||||
ns_requires_ansible.name_requires_ansible: 1.0.0
|
||||
|
||||
- namespace: ns_requires_ansible
|
||||
name: dependency_incompat
|
||||
version: 1.0.0
|
||||
dependencies:
|
||||
ns_requires_ansible.name_requires_ansible: 1.0.0
|
||||
|
||||
- namespace: ns_requires_ansible
|
||||
name: dependency_incompat
|
||||
version: 2.0.0
|
||||
dependencies:
|
||||
ns_requires_ansible.dependency: 2.0.0 # pinned incompatible version
|
||||
|
||||
- namespace: ns_requires_ansible
|
||||
name: dependency_incompat
|
||||
version: 3.0.0
|
||||
dependencies:
|
||||
ns_requires_ansible.name_requires_ansible: 1.0.0
|
||||
|
||||
- namespace: ns_requires_ansible
|
||||
name: backtracking_error
|
||||
version: 1.0.0
|
||||
dependencies:
|
||||
ns_requires_ansible.dependency_incompat: "*"
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
# The first task to interact with pulp needs to wait until it responds appropriately.
|
||||
- name: Wait for Pulp API
|
||||
uri:
|
||||
url: '{{ pulp_api }}/pulp/api/v3/distributions/ansible/ansible/'
|
||||
user: '{{ pulp_user }}'
|
||||
password: '{{ pulp_password }}'
|
||||
url: '{{ galaxy_ng_server }}/pulp/api/v3/repositories/ansible/ansible/'
|
||||
user: '{{ galaxy_user }}'
|
||||
password: '{{ galaxy_password }}'
|
||||
force_basic_auth: true
|
||||
register: this
|
||||
until: this is successful
|
||||
@@ -13,13 +13,9 @@
|
||||
|
||||
- name: Verify Galaxy NG server
|
||||
uri:
|
||||
url: "{{ galaxy_ng_server }}"
|
||||
user: '{{ pulp_user }}'
|
||||
password: '{{ pulp_password }}'
|
||||
url: "{{ amanda }}/api/"
|
||||
force_basic_auth: true
|
||||
|
||||
- name: Verify Pulp server
|
||||
uri:
|
||||
url: "{{ pulp_server }}"
|
||||
status_code:
|
||||
- 404 # endpoint responds without authentication
|
||||
url: "{{ galaxy_ng_server }}"
|
||||
|
||||
@@ -10,6 +10,7 @@ from ....config import (
|
||||
)
|
||||
|
||||
from ....docker_util import (
|
||||
docker_cp_from,
|
||||
docker_cp_to,
|
||||
docker_exec,
|
||||
)
|
||||
@@ -33,43 +34,6 @@ from . import (
|
||||
)
|
||||
|
||||
|
||||
GALAXY_HOST_NAME = 'galaxy-pulp'
|
||||
SETTINGS = {
|
||||
'PULP_CONTENT_ORIGIN': f'http://{GALAXY_HOST_NAME}',
|
||||
'PULP_ANSIBLE_API_HOSTNAME': f'http://{GALAXY_HOST_NAME}',
|
||||
'PULP_GALAXY_API_PATH_PREFIX': '/api/galaxy/',
|
||||
# These paths are unique to the container image which has an nginx location for /pulp/content to route
|
||||
# requests to the content backend
|
||||
'PULP_ANSIBLE_CONTENT_HOSTNAME': f'http://{GALAXY_HOST_NAME}/pulp/content/api/galaxy/v3/artifacts/collections/',
|
||||
'PULP_CONTENT_PATH_PREFIX': '/pulp/content/api/galaxy/v3/artifacts/collections/',
|
||||
'PULP_GALAXY_AUTHENTICATION_CLASSES': [
|
||||
'rest_framework.authentication.SessionAuthentication',
|
||||
'rest_framework.authentication.TokenAuthentication',
|
||||
'rest_framework.authentication.BasicAuthentication',
|
||||
'django.contrib.auth.backends.ModelBackend',
|
||||
],
|
||||
# This should probably be false see https://issues.redhat.com/browse/AAH-2328
|
||||
'PULP_GALAXY_REQUIRE_CONTENT_APPROVAL': 'true',
|
||||
'PULP_GALAXY_DEPLOYMENT_MODE': 'standalone',
|
||||
'PULP_GALAXY_AUTO_SIGN_COLLECTIONS': 'false',
|
||||
'PULP_GALAXY_COLLECTION_SIGNING_SERVICE': 'ansible-default',
|
||||
'PULP_RH_ENTITLEMENT_REQUIRED': 'insights',
|
||||
'PULP_TOKEN_AUTH_DISABLED': 'false',
|
||||
'PULP_TOKEN_SERVER': f'http://{GALAXY_HOST_NAME}/token/',
|
||||
'PULP_TOKEN_SIGNATURE_ALGORITHM': 'ES256',
|
||||
'PULP_PUBLIC_KEY_PATH': '/src/galaxy_ng/dev/common/container_auth_public_key.pem',
|
||||
'PULP_PRIVATE_KEY_PATH': '/src/galaxy_ng/dev/common/container_auth_private_key.pem',
|
||||
'PULP_ANALYTICS': 'false',
|
||||
'PULP_GALAXY_ENABLE_UNAUTHENTICATED_COLLECTION_ACCESS': 'true',
|
||||
'PULP_GALAXY_ENABLE_UNAUTHENTICATED_COLLECTION_DOWNLOAD': 'true',
|
||||
'PULP_GALAXY_ENABLE_LEGACY_ROLES': 'true',
|
||||
'PULP_GALAXY_FEATURE_FLAGS__execution_environments': 'false',
|
||||
'PULP_SOCIAL_AUTH_LOGIN_REDIRECT_URL': '/',
|
||||
'PULP_GALAXY_FEATURE_FLAGS__ai_deny_index': 'true',
|
||||
'PULP_DEFAULT_ADMIN_PASSWORD': 'password'
|
||||
}
|
||||
|
||||
|
||||
GALAXY_IMPORTER = b"""
|
||||
[galaxy-importer]
|
||||
ansible_local_tmp=~/.ansible/tmp
|
||||
@@ -90,49 +54,86 @@ run_flake8=false
|
||||
|
||||
class GalaxyProvider(CloudProvider):
|
||||
"""
|
||||
Galaxy plugin. Sets up pulp (ansible-galaxy) servers for tests.
|
||||
The pulp source itself resides at: https://github.com/pulp/pulp-oci-images
|
||||
Galaxy plugin. Sets up ansible-galaxy servers for tests.
|
||||
"""
|
||||
|
||||
def __init__(self, args: IntegrationConfig) -> None:
|
||||
super().__init__(args)
|
||||
|
||||
self.image = os.environ.get(
|
||||
'ANSIBLE_PULP_CONTAINER',
|
||||
'quay.io/pulp/galaxy:4.7.1'
|
||||
)
|
||||
|
||||
self.uses_docker = True
|
||||
self.galaxy_image = os.getenv(
|
||||
'ANSIBLE_GALAXY_CONTAINER',
|
||||
'ghcr.io/ansible/galaxy-ng-test-container:26.03.0'
|
||||
)
|
||||
self.postgres_image = os.getenv(
|
||||
'ANSIBLE_POSTGRES_CONTAINER',
|
||||
'public.ecr.aws/docker/library/postgres:13'
|
||||
)
|
||||
self.amanda_image = os.getenv(
|
||||
'ANSIBLE_AMANDA_CONTAINER',
|
||||
'ghcr.io/sivel/amanda@sha256:f704fe6f062b8ada59ae6553a70d2175295d068d56f544875980581b7df9c16d'
|
||||
)
|
||||
|
||||
def setup(self) -> None:
|
||||
"""Setup cloud resource before delegation and reg cleanup callback."""
|
||||
super().setup()
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode='w+') as env_fd:
|
||||
settings = '\n'.join(
|
||||
f'{key}={value}' for key, value in SETTINGS.items()
|
||||
)
|
||||
env_fd.write(settings)
|
||||
env_fd.flush()
|
||||
display.info(f'>>> galaxy_ng Configuration\n{settings}', verbosity=3)
|
||||
descriptor = run_support_container(
|
||||
# This container is created separately from the actual galaxy container due to
|
||||
# needing it created for postgres, but the galaxy container has a dependency on knowing the postgres
|
||||
# container id
|
||||
gdata = run_support_container(self.args, self.platform, self.galaxy_image, 'galaxy-data', [0], start=False)
|
||||
if not gdata:
|
||||
return
|
||||
|
||||
amanda = run_support_container(
|
||||
self.args,
|
||||
self.platform,
|
||||
self.amanda_image,
|
||||
'amanda',
|
||||
[8001],
|
||||
aliases=['amanda'],
|
||||
options=[
|
||||
'--volumes-from', gdata.container_id,
|
||||
],
|
||||
cmd=['-port', '8001', '-publish'],
|
||||
)
|
||||
if not amanda:
|
||||
return
|
||||
|
||||
postgres = run_support_container(
|
||||
self.args,
|
||||
self.platform,
|
||||
self.postgres_image,
|
||||
'galaxy-postgres',
|
||||
[5432],
|
||||
aliases=['postgres'],
|
||||
options=[
|
||||
'--volumes-from', gdata.container_id,
|
||||
],
|
||||
)
|
||||
if not postgres:
|
||||
return
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
docker_cp_from(self.args, gdata.container_id, '/galaxy_ng.env', tmpdir)
|
||||
galaxy_ng = run_support_container(
|
||||
self.args,
|
||||
self.platform,
|
||||
self.image,
|
||||
GALAXY_HOST_NAME,
|
||||
[
|
||||
80,
|
||||
],
|
||||
aliases=[
|
||||
GALAXY_HOST_NAME,
|
||||
],
|
||||
self.galaxy_image,
|
||||
'galaxy_ng',
|
||||
[8000, 24816],
|
||||
aliases=['galaxy'],
|
||||
start=True,
|
||||
options=[
|
||||
'--env-file', env_fd.name,
|
||||
'--env-file', os.path.join(tmpdir, 'galaxy_ng.env'),
|
||||
'--add-host', f'postgres:{postgres.details.container_ip}',
|
||||
],
|
||||
cmd=[
|
||||
'/bin/sh', '-c',
|
||||
'(start-api &); (start-content-app &); start-worker;'
|
||||
],
|
||||
)
|
||||
|
||||
if not descriptor:
|
||||
if not galaxy_ng:
|
||||
return
|
||||
|
||||
injected_files = [
|
||||
@@ -143,13 +144,14 @@ class GalaxyProvider(CloudProvider):
|
||||
temp_fd.write(content)
|
||||
temp_fd.flush()
|
||||
display.info(f'>>> {friendly_name} Configuration\n{to_text(content)}', verbosity=3)
|
||||
docker_exec(self.args, descriptor.container_id, ['mkdir', '-p', os.path.dirname(path)], True)
|
||||
docker_cp_to(self.args, descriptor.container_id, temp_fd.name, path)
|
||||
docker_exec(self.args, descriptor.container_id, ['chown', 'pulp:pulp', path], True)
|
||||
docker_exec(self.args, galaxy_ng.container_id, ['mkdir', '-p', os.path.dirname(path)], True, options=['-u', 'root'])
|
||||
docker_cp_to(self.args, galaxy_ng.container_id, temp_fd.name, path)
|
||||
docker_exec(self.args, galaxy_ng.container_id, ['chown', 'galaxy:galaxy', path], True, options=['-u', 'root'])
|
||||
|
||||
self._set_cloud_config('PULP_HOST', GALAXY_HOST_NAME)
|
||||
self._set_cloud_config('PULP_USER', 'admin')
|
||||
self._set_cloud_config('PULP_PASSWORD', 'password')
|
||||
self._set_cloud_config('GALAXY_HOST', 'galaxy')
|
||||
self._set_cloud_config('GALAXY_USER', 'admin')
|
||||
self._set_cloud_config('GALAXY_PASSWORD', 'admin')
|
||||
self._set_cloud_config('AMANDA_HOST', 'amanda')
|
||||
|
||||
|
||||
class GalaxyEnvironment(CloudEnvironment):
|
||||
@@ -157,22 +159,22 @@ class GalaxyEnvironment(CloudEnvironment):
|
||||
|
||||
def get_environment_config(self) -> CloudEnvironmentConfig:
|
||||
"""Return environment configuration for use in the test environment after delegation."""
|
||||
pulp_user = str(self._get_cloud_config('PULP_USER'))
|
||||
pulp_password = str(self._get_cloud_config('PULP_PASSWORD'))
|
||||
pulp_host = self._get_cloud_config('PULP_HOST')
|
||||
galaxy_user = str(self._get_cloud_config('GALAXY_USER'))
|
||||
galaxy_password = str(self._get_cloud_config('GALAXY_PASSWORD'))
|
||||
galaxy_host = self._get_cloud_config('GALAXY_HOST')
|
||||
amanda_host = self._get_cloud_config('AMANDA_HOST')
|
||||
|
||||
return CloudEnvironmentConfig(
|
||||
ansible_vars=dict(
|
||||
pulp_user=pulp_user,
|
||||
pulp_password=pulp_password,
|
||||
pulp_api=f'http://{pulp_host}',
|
||||
pulp_server=f'http://{pulp_host}/pulp_ansible/galaxy/',
|
||||
galaxy_ng_server=f'http://{pulp_host}/api/galaxy/',
|
||||
galaxy_user=galaxy_user,
|
||||
galaxy_password=galaxy_password,
|
||||
galaxy_ng_server=f'http://{galaxy_host}:8000/api/galaxy',
|
||||
amanda=f'http://{amanda_host}:8001',
|
||||
),
|
||||
env_vars=dict(
|
||||
PULP_USER=pulp_user,
|
||||
PULP_PASSWORD=pulp_password,
|
||||
PULP_SERVER=f'http://{pulp_host}/pulp_ansible/galaxy/api/',
|
||||
GALAXY_NG_SERVER=f'http://{pulp_host}/api/galaxy/',
|
||||
GALAXY_USER=galaxy_user,
|
||||
GALAXY_PASSWORD=galaxy_password,
|
||||
GALAXY_NG_SERVER=f'http://{galaxy_host}:8000/api/galaxy',
|
||||
AMANDA=f'http://{amanda_host}:8001',
|
||||
),
|
||||
)
|
||||
|
||||
@@ -451,6 +451,9 @@ def create_container_database(args: EnvironmentConfig) -> ContainerDatabase:
|
||||
managed: dict[str, dict[str, ContainerAccess]] = {}
|
||||
|
||||
for name, container in support_containers.items():
|
||||
if container.details is None:
|
||||
# data containers will not be started, and will be missing details
|
||||
continue
|
||||
if container.details.published_ports:
|
||||
if require_docker().command == 'podman':
|
||||
host_ip_func = get_podman_host_ip
|
||||
|
||||
@@ -658,15 +658,15 @@ def __docker_pull(args: CommonConfig, image: str) -> None:
|
||||
"""Internal implementation for docker_pull. Do not call directly."""
|
||||
if '@' not in image and ':' not in image:
|
||||
display.info('Skipping pull of image without tag or digest: %s' % image, verbosity=2)
|
||||
inspect = docker_image_inspect(args, image)
|
||||
elif inspect := docker_image_inspect(args, image, always=True):
|
||||
docker_image_inspect(args, image)
|
||||
elif docker_image_inspect(args, image, always=True):
|
||||
display.info('Skipping pull of existing image: %s' % image, verbosity=2)
|
||||
else:
|
||||
for _iteration in range(1, 10):
|
||||
try:
|
||||
docker_command(args, ['pull', image], capture=False)
|
||||
|
||||
if (inspect := docker_image_inspect(args, image)) or args.explain:
|
||||
if docker_image_inspect(args, image) or args.explain:
|
||||
break
|
||||
|
||||
display.warning(f'Image "{image}" not found after pull completed. Waiting a few seconds before trying again.')
|
||||
@@ -676,18 +676,17 @@ def __docker_pull(args: CommonConfig, image: str) -> None:
|
||||
else:
|
||||
raise ApplicationError(f'Failed to pull container image "{image}".')
|
||||
|
||||
if inspect and inspect.volumes:
|
||||
display.warning(f'Image "{image}" contains {len(inspect.volumes)} volume(s): {", ".join(sorted(inspect.volumes))}\n'
|
||||
'This may result in leaking anonymous volumes. It may also prevent the image from working on some hosts or container engines.\n'
|
||||
'The image should be rebuilt without the use of the VOLUME instruction.',
|
||||
unique=True)
|
||||
|
||||
|
||||
def docker_cp_to(args: CommonConfig, container_id: str, src: str, dst: str) -> None:
|
||||
"""Copy a file to the specified container."""
|
||||
docker_command(args, ['cp', src, '%s:%s' % (container_id, dst)], capture=True)
|
||||
|
||||
|
||||
def docker_cp_from(args: CommonConfig, container_id: str, src: str, dst: str) -> None:
|
||||
"""Copy a file from the specified container."""
|
||||
docker_command(args, ['cp', '%s:%s' % (container_id, src), dst], capture=True)
|
||||
|
||||
|
||||
def docker_create(
|
||||
args: CommonConfig,
|
||||
image: str,
|
||||
@@ -726,7 +725,7 @@ def docker_rm(args: CommonConfig, container_id: str) -> None:
|
||||
# Podman supports `--time` for stop. The `--timeout` option was deprecated in 1.9.0.
|
||||
# Both Docker and Podman support the `-t` option for stop.
|
||||
docker_command(args, ['stop', '-t', '0', container_id], capture=True)
|
||||
docker_command(args, ['rm', container_id], capture=True)
|
||||
docker_command(args, ['rm', '-v', container_id], capture=True)
|
||||
except SubprocessError as ex:
|
||||
# Both Podman and Docker report an error if the container does not exist.
|
||||
# The error messages contain the same "no such container" string, differing only in capitalization.
|
||||
|
||||
Reference in New Issue
Block a user