Example #1
0
def test_build_artifact_from_path_no_version(collection_artifact, monkeypatch):
    mock_display = MagicMock()
    monkeypatch.setattr(Display, 'display', mock_display)

    # a collection artifact should always contain a valid version
    manifest_path = os.path.join(collection_artifact[0], b'MANIFEST.json')
    manifest_value = json.dumps({
        'collection_info': {
            'namespace': 'namespace',
            'name': 'name',
            'version': '',
            'dependencies': {}
        }
    })
    with open(manifest_path, 'wb') as manifest_obj:
        manifest_obj.write(to_bytes(manifest_value))

    tmp_path = os.path.join(os.path.split(collection_artifact[1])[0], b'temp')
    concrete_artifact_cm = collection.concrete_artifact_manager.ConcreteArtifactsManager(
        tmp_path, validate_certs=False)

    expected = (
        '^Collection metadata file `.*` at `.*` is expected to have a valid SemVer '
        'version value but got {empty_unicode_string!r}$'.format(
            empty_unicode_string=u''))
    with pytest.raises(AnsibleError, match=expected):
        Requirement.from_dir_path_as_unknown(collection_artifact[0],
                                             concrete_artifact_cm)
Example #2
0
def test_build_requirement_from_tar_no_manifest(tmp_path_factory):
    test_dir = to_bytes(
        tmp_path_factory.mktemp('test-ÅÑŚÌβŁÈ Collections Input'))

    json_data = to_bytes(json.dumps({
        'files': [],
        'format': 1,
    }))

    tar_path = os.path.join(test_dir, b'ansible-collections.tar.gz')
    with tarfile.open(tar_path, 'w:gz') as tfile:
        b_io = BytesIO(json_data)
        tar_info = tarfile.TarInfo('FILES.json')
        tar_info.size = len(json_data)
        tar_info.mode = 0o0644
        tfile.addfile(tarinfo=tar_info, fileobj=b_io)

    concrete_artifact_cm = collection.concrete_artifact_manager.ConcreteArtifactsManager(
        test_dir, validate_certs=False)

    expected = "Collection at '%s' does not contain the required file MANIFEST.json." % to_native(
        tar_path)
    with pytest.raises(AnsibleError, match=expected):
        Requirement.from_requirement_dict({'name': to_text(tar_path)},
                                          concrete_artifact_cm)
def collection_objects():
    collection_ham = Requirement('sandwiches.ham', '1.5.0', None, 'galaxy')

    collection_pbj = Requirement('sandwiches.pbj', '2.5', None, 'galaxy')

    collection_reuben = Requirement('sandwiches.reuben', '4', None, 'galaxy')

    return [collection_ham, collection_pbj, collection_reuben]
def test_build_requirement_from_tar_fail_not_tar(tmp_path_factory):
    test_dir = to_bytes(tmp_path_factory.mktemp('test-ÅÑŚÌβŁÈ Collections Input'))
    test_file = os.path.join(test_dir, b'fake.tar.gz')
    with open(test_file, 'wb') as test_obj:
        test_obj.write(b"\x00\x01\x02\x03")

    concrete_artifact_cm = collection.concrete_artifact_manager.ConcreteArtifactsManager(test_dir, validate_certs=False)

    expected = "Collection artifact at '%s' is not a valid tar file." % to_native(test_file)
    with pytest.raises(AnsibleError, match=expected):
        Requirement.from_requirement_dict({'name': to_text(test_file)}, concrete_artifact_cm)
def test_build_requirement_from_path_invalid_manifest(collection_artifact):
    manifest_path = os.path.join(collection_artifact[0], b'MANIFEST.json')
    with open(manifest_path, 'wb') as manifest_obj:
        manifest_obj.write(b"not json")

    tmp_path = os.path.join(os.path.split(collection_artifact[1])[0], b'temp')
    concrete_artifact_cm = collection.concrete_artifact_manager.ConcreteArtifactsManager(tmp_path, validate_certs=False)

    expected = "Collection tar file member MANIFEST.json does not contain a valid json string."
    with pytest.raises(AnsibleError, match=expected):
        Requirement.from_dir_path_as_unknown(collection_artifact[0], concrete_artifact_cm)
def mock_collection_objects(mocker):
    mocker.patch('ansible.cli.galaxy.GalaxyCLI._resolve_path',
                 side_effect=[
                     '/root/.ansible/collections',
                     '/usr/share/ansible/collections'
                 ])
    mocker.patch('ansible.cli.galaxy.validate_collection_path',
                 side_effect=[
                     '/root/.ansible/collections/ansible_collections',
                     '/usr/share/ansible/collections/ansible_collections'
                 ])

    collection_args_1 = (
        (
            'sandwiches.pbj',
            '1.5.0',
            None,
            'dir',
            None,
        ),
        (
            'sandwiches.reuben',
            '2.5.0',
            None,
            'dir',
            None,
        ),
    )

    collection_args_2 = (
        (
            'sandwiches.pbj',
            '1.0.0',
            None,
            'dir',
            None,
        ),
        (
            'sandwiches.ham',
            '1.0.0',
            None,
            'dir',
            None,
        ),
    )

    collections_path_1 = [Requirement(*cargs) for cargs in collection_args_1]
    collections_path_2 = [Requirement(*cargs) for cargs in collection_args_2]

    mocker.patch('ansible.cli.galaxy.find_existing_collections',
                 side_effect=[collections_path_1, collections_path_2])
def test_install_collection_with_circular_dependency(collection_artifact, monkeypatch):
    collection_path, collection_tar = collection_artifact
    temp_path = os.path.split(collection_tar)[0]
    shutil.rmtree(collection_path)

    mock_display = MagicMock()
    monkeypatch.setattr(Display, 'display', mock_display)

    concrete_artifact_cm = collection.concrete_artifact_manager.ConcreteArtifactsManager(temp_path, validate_certs=False)
    requirements = [Requirement('ansible_namespace.collection', '0.1.0', to_text(collection_tar), 'file', None)]
    collection.install_collections(requirements, to_text(temp_path), [], False, False, False, False, False, False, concrete_artifact_cm, True)

    assert os.path.isdir(collection_path)

    actual_files = os.listdir(collection_path)
    actual_files.sort()
    assert actual_files == [b'FILES.json', b'MANIFEST.json', b'README.md', b'docs', b'playbooks', b'plugins', b'roles',
                            b'runme.sh']

    with open(os.path.join(collection_path, b'MANIFEST.json'), 'rb') as manifest_obj:
        actual_manifest = json.loads(to_text(manifest_obj.read()))

    assert actual_manifest['collection_info']['namespace'] == 'ansible_namespace'
    assert actual_manifest['collection_info']['name'] == 'collection'
    assert actual_manifest['collection_info']['version'] == '0.1.0'

    # Filter out the progress cursor display calls.
    display_msgs = [m[1][0] for m in mock_display.mock_calls if 'newline' not in m[2] and len(m[1]) == 1]
    assert len(display_msgs) == 4
    assert display_msgs[0] == "Process install dependency map"
    assert display_msgs[1] == "Starting collection install process"
    assert display_msgs[2] == "Installing 'ansible_namespace.collection:0.1.0' to '%s'" % to_text(collection_path)
    assert display_msgs[3] == "ansible_namespace.collection:0.1.0 was installed successfully"
def test_install_collections_existing_without_force(collection_artifact, monkeypatch):
    collection_path, collection_tar = collection_artifact
    temp_path = os.path.split(collection_tar)[0]

    mock_display = MagicMock()
    monkeypatch.setattr(Display, 'display', mock_display)

    concrete_artifact_cm = collection.concrete_artifact_manager.ConcreteArtifactsManager(temp_path, validate_certs=False)

    assert os.path.isdir(collection_path)

    requirements = [Requirement('ansible_namespace.collection', '0.1.0', to_text(collection_tar), 'file', None)]
    collection.install_collections(requirements, to_text(temp_path), [], False, False, False, False, False, False, concrete_artifact_cm, True)

    assert os.path.isdir(collection_path)

    actual_files = os.listdir(collection_path)
    actual_files.sort()
    assert actual_files == [b'README.md', b'docs', b'galaxy.yml', b'playbooks', b'plugins', b'roles', b'runme.sh']

    # Filter out the progress cursor display calls.
    display_msgs = [m[1][0] for m in mock_display.mock_calls if 'newline' not in m[2] and len(m[1]) == 1]
    assert len(display_msgs) == 1

    assert display_msgs[0] == 'Nothing to do. All requested collections are already installed. If you want to reinstall them, consider using `--force`.'

    for msg in display_msgs:
        assert 'WARNING' not in msg
    def _from_path(collection_name='pbj'):
        collection_args = {
            'sandwiches.pbj': (
                (
                    'sandwiches.pbj',
                    '1.5.0',
                    None,
                    'dir',
                ),
                (
                    'sandwiches.pbj',
                    '1.0.0',
                    None,
                    'dir',
                ),
            ),
            'sandwiches.ham': ((
                'sandwiches.ham',
                '1.0.0',
                None,
                'dir',
            ), ),
        }

        from_path_objects = [
            Requirement(*args) for args in collection_args[collection_name]
        ]
        mocker.patch('ansible.cli.galaxy.Requirement.from_dir_path_as_unknown',
                     side_effect=from_path_objects)
Example #10
0
def test_get_collection_widths_single_collection(mocker):
    mocked_collection = Requirement('sandwiches.club', '3.0.0', None, 'galaxy',
                                    None)
    # Make this look like it is not iterable
    mocker.patch('ansible.cli.galaxy.is_iterable', return_value=False)

    assert _get_collection_widths(mocked_collection) == (15, 5)
Example #11
0
def test_build_requirement_from_path_no_version(collection_artifact,
                                                monkeypatch):
    mock_display = MagicMock()
    monkeypatch.setattr(Display, 'display', mock_display)

    # version may be falsey/arbitrary strings for collections in development
    manifest_path = os.path.join(collection_artifact[0], b'galaxy.yml')
    metadata = {
        'authors': ['Ansible'],
        'readme': 'README.md',
        'namespace': 'namespace',
        'name': 'name',
        'version': '',
        'dependencies': {},
    }
    with open(manifest_path, 'wb') as manifest_obj:
        manifest_obj.write(to_bytes(yaml.safe_dump(metadata)))

    tmp_path = os.path.join(os.path.split(collection_artifact[1])[0], b'temp')
    concrete_artifact_cm = collection.concrete_artifact_manager.ConcreteArtifactsManager(
        tmp_path, validate_certs=False)
    actual = Requirement.from_dir_path_as_unknown(collection_artifact[0],
                                                  concrete_artifact_cm)

    # While the folder name suggests a different collection, we treat MANIFEST.json as the source of truth.
    assert actual.namespace == u'namespace'
    assert actual.name == u'name'
    assert actual.src == collection_artifact[0]
    assert actual.ver == u'*'
Example #12
0
def test_build_requirement_from_path_with_manifest(version,
                                                   collection_artifact):
    manifest_path = os.path.join(collection_artifact[0], b'MANIFEST.json')
    manifest_value = json.dumps({
        'collection_info': {
            'namespace': 'namespace',
            'name': 'name',
            'version': version,
            'dependencies': {
                'ansible_namespace.collection': '*'
            }
        }
    })
    with open(manifest_path, 'wb') as manifest_obj:
        manifest_obj.write(to_bytes(manifest_value))

    tmp_path = os.path.join(os.path.split(collection_artifact[1])[0], b'temp')
    concrete_artifact_cm = collection.concrete_artifact_manager.ConcreteArtifactsManager(
        tmp_path, validate_certs=False)
    actual = Requirement.from_dir_path_as_unknown(collection_artifact[0],
                                                  concrete_artifact_cm)

    # While the folder name suggests a different collection, we treat MANIFEST.json as the source of truth.
    assert actual.namespace == u'namespace'
    assert actual.name == u'name'
    assert actual.src == collection_artifact[0]
    assert actual.ver == to_text(version)
Example #13
0
def test_install_missing_metadata_warning(collection_artifact, monkeypatch):
    collection_path, collection_tar = collection_artifact
    temp_path = os.path.split(collection_tar)[0]

    mock_display = MagicMock()
    monkeypatch.setattr(Display, 'display', mock_display)

    for file in [b'MANIFEST.json', b'galaxy.yml']:
        b_path = os.path.join(collection_path, file)
        if os.path.isfile(b_path):
            os.unlink(b_path)

    concrete_artifact_cm = collection.concrete_artifact_manager.ConcreteArtifactsManager(
        temp_path, validate_certs=False)
    requirements = [
        Requirement('ansible_namespace.collection', '0.1.0',
                    to_text(collection_tar), 'file')
    ]
    collection.install_collections(requirements, to_text(temp_path), [], False,
                                   False, False, False, False, False,
                                   concrete_artifact_cm)

    display_msgs = [
        m[1][0] for m in mock_display.mock_calls
        if 'newline' not in m[2] and len(m[1]) == 1
    ]

    assert 'WARNING' in display_msgs[0]
def test_build_requirement_from_tar_invalid_manifest(tmp_path_factory):
    test_dir = to_bytes(tmp_path_factory.mktemp('test-ÅÑŚÌβŁÈ Collections Input'))

    json_data = b"not a json"

    tar_path = os.path.join(test_dir, b'ansible-collections.tar.gz')
    with tarfile.open(tar_path, 'w:gz') as tfile:
        b_io = BytesIO(json_data)
        tar_info = tarfile.TarInfo('MANIFEST.json')
        tar_info.size = len(json_data)
        tar_info.mode = 0o0644
        tfile.addfile(tarinfo=tar_info, fileobj=b_io)

    concrete_artifact_cm = collection.concrete_artifact_manager.ConcreteArtifactsManager(test_dir, validate_certs=False)

    expected = "Collection tar file member MANIFEST.json does not contain a valid json string."
    with pytest.raises(AnsibleError, match=expected):
        Requirement.from_requirement_dict({'name': to_text(tar_path)}, concrete_artifact_cm)
def test_build_requirement_from_path(collection_artifact):
    tmp_path = os.path.join(os.path.split(collection_artifact[1])[0], b'temp')
    concrete_artifact_cm = collection.concrete_artifact_manager.ConcreteArtifactsManager(tmp_path, validate_certs=False)
    actual = Requirement.from_dir_path_as_unknown(collection_artifact[0], concrete_artifact_cm)

    assert actual.namespace == u'ansible_namespace'
    assert actual.name == u'collection'
    assert actual.src == collection_artifact[0]
    assert actual.ver == u'0.1.0'
def test_build_requirement_from_tar_no_files(tmp_path_factory):
    test_dir = to_bytes(tmp_path_factory.mktemp('test-ÅÑŚÌβŁÈ Collections Input'))

    json_data = to_bytes(json.dumps(
        {
            'collection_info': {},
        }
    ))

    tar_path = os.path.join(test_dir, b'ansible-collections.tar.gz')
    with tarfile.open(tar_path, 'w:gz') as tfile:
        b_io = BytesIO(json_data)
        tar_info = tarfile.TarInfo('MANIFEST.json')
        tar_info.size = len(json_data)
        tar_info.mode = 0o0644
        tfile.addfile(tarinfo=tar_info, fileobj=b_io)

    concrete_artifact_cm = collection.concrete_artifact_manager.ConcreteArtifactsManager(test_dir, validate_certs=False)
    with pytest.raises(KeyError, match='namespace'):
        Requirement.from_requirement_dict({'name': to_text(tar_path)}, concrete_artifact_cm)
Example #17
0
def test_install_collection_with_download(galaxy_server, collection_artifact,
                                          monkeypatch):
    collection_path, collection_tar = collection_artifact
    shutil.rmtree(collection_path)

    collections_dir = ('%s' % os.path.sep).join(
        to_text(collection_path).split('%s' % os.path.sep)[:-2])

    temp_path = os.path.join(os.path.split(collection_tar)[0], b'temp')
    os.makedirs(temp_path)

    mock_display = MagicMock()
    monkeypatch.setattr(Display, 'display', mock_display)

    concrete_artifact_cm = collection.concrete_artifact_manager.ConcreteArtifactsManager(
        temp_path, validate_certs=False)

    mock_download = MagicMock()
    mock_download.return_value = collection_tar
    monkeypatch.setattr(concrete_artifact_cm, 'get_galaxy_artifact_path',
                        mock_download)

    req = Requirement('ansible_namespace.collection', '0.1.0',
                      'https://downloadme.com', 'galaxy')
    collection.install(req, to_text(collections_dir), concrete_artifact_cm)

    actual_files = os.listdir(collection_path)
    actual_files.sort()
    assert actual_files == [
        b'FILES.json', b'MANIFEST.json', b'README.md', b'docs', b'playbooks',
        b'plugins', b'roles', b'runme.sh'
    ]

    assert mock_display.call_count == 2
    assert mock_display.mock_calls[0][1][0] == "Installing 'ansible_namespace.collection:0.1.0' to '%s'" \
        % to_text(collection_path)
    assert mock_display.mock_calls[1][1][
        0] == "ansible_namespace.collection:0.1.0 was installed successfully"

    assert mock_download.call_count == 1
    assert mock_download.mock_calls[0][1][0].src == 'https://downloadme.com'
    assert mock_download.mock_calls[0][1][0].type == 'galaxy'
Example #18
0
 def _cobj(fqcn='sandwiches.ham'):
     return Requirement(fqcn, '1.5.0', None, 'galaxy')